1
- From 98744d243b9ef173ab0c82a9b3e13e675175589b Mon Sep 17 00:00:00 2001
2
- From: GloriousEggroll <
[email protected] >
3
- Date: Sat, 27 Jul 2024 18:59:05 -0600
4
- Subject: [PATCH] dai_xinput_fix
1
+ From 4fd6c1a286dd2babd098decf045522be1dff5656 Mon Sep 17 00:00:00 2001
2
+ From: Cameron Moore <
[email protected] >
3
+ Date: Sat, 3 Feb 2024 21:21:50 -0500
4
+ Subject: [PATCH] user32: [GAMEFIX] Fix xinput support for Dragon Age
5
+ Inquisition
5
6
7
+ user32: [GAMEFIX] 1/2 Dragon Age Inquisition: fix xinput support
8
+
9
+ https://bugs.winehq.org/show_bug.cgi?id=47070
10
+
11
+ I'm not completely sure on the specifics of why the bug happens,
12
+ but there are two main threads involved. Thread A owns the window
13
+ object for the game. Thread B makes all the calls to XInputGetState.
14
+ However, Thread B checks if the window has focus to receive keyboard
15
+ inputs every frame. Thread B calls AttachThreadInput to attach its
16
+ message queue to the Thread A's window a few times before the bug
17
+ starts occurring along with one separate time at the start where
18
+ Thread A attaches Thread B's message queue to the window as well.
19
+ The message queue is detached one last time and then Thread B no
20
+ longer has access to the window.
21
+
22
+ My workaround checks GetFocus for when it starts receiving Null
23
+ for a response (meaning it does not have access to a window anymore).
24
+ Prior to this, AttachThreadInput stores the IDs of the two threads
25
+ which call the function the very first time (hopefully this works
26
+ with the EA App as I've been debugging this with Origin which doesn't
27
+ seem to call either of the two WINAPI functions as long as you load
28
+ straight into the game from the Origin library menu). When GetFocus
29
+ realizes it does not have a window for the thread, it calls
30
+ AttachThreadInput with the two thread IDs. This allows Thread B to
31
+ access the window, which let's GetFocus return the proper window
32
+ handle, which then let's the program access XInputGetState meaning
33
+ the controller finally works.
34
+
35
+ Anyway, as far as the final cause of the bug: it might be because
36
+ of thread prioritization causing Thread B to AttachThreadInput to
37
+ be called by it last as the only time Thread A calls it, it does
38
+ not detach the message queue afterwards. This leads me to believe
39
+ that the bug might just be caused by incomplete wine
40
+ functionality / stubs. Although, this part is speculation.
41
+
42
+ user32: [GAMEFIX] 2/2 Dragon Age Inquisition: fix xinput support
43
+
44
+ Wrap hack with a SteamGameId check and cache it.
6
45
---
7
- dlls/user32/input.c | 15 ++++++++++++++-
8
- dlls/win32u/input.c | 32 ++++++++++++++++++++++++++++++++
9
- 2 files changed, 46 insertions(+), 1 deletion(-)
46
+ dlls/user32/input.c | 22 +++++++ ++++++++++++++-
47
+ dlls/win32u/input.c | 37 +++++ ++++++++++++++++++++++++++++++++
48
+ 2 files changed, 58 insertions(+), 1 deletion(-)
10
49
11
50
diff --git a/dlls/user32/input.c b/dlls/user32/input.c
12
- index f91b65a5f56..02241110eae 100644
51
+ index f91b65a5f56..dda952262ac 100644
13
52
--- a/dlls/user32/input.c
14
53
+++ b/dlls/user32/input.c
15
- @@ -814,8 +814,21 @@ HWND WINAPI GetActiveWindow(void)
54
+ @@ -814,8 +814,28 @@ HWND WINAPI GetActiveWindow(void)
16
55
HWND WINAPI GetFocus(void)
17
56
{
18
57
GUITHREADINFO info;
19
58
+ HWND retValueWindow;
20
59
+ static HWND prev = 0;
21
60
+ const char *sgi;
61
+ + static int is_DragonAgeInquis = -1;
62
+ +
22
63
info.cbSize = sizeof(info);
23
64
- return NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndFocus : 0;
24
65
+
25
66
+ retValueWindow = NtUserGetGUIThreadInfo( GetCurrentThreadId(), &info ) ? info.hwndFocus : 0;
26
67
+
27
- + if ((sgi = getenv("SteamGameId")) && !strcmp(sgi, "1222690")) {
28
- + if (retValueWindow == 0 && prev != 0)
29
- + NtUserAttachThreadInput(0, 0, 1);
30
- + else
31
- + prev = retValueWindow;
68
+ + if (is_DragonAgeInquis)
69
+ + {
70
+ + if ((is_DragonAgeInquis == 1) ||
71
+ + (is_DragonAgeInquis = ((sgi = getenv("SteamGameId")) && !strcmp(sgi, "1222690"))))
72
+ + {
73
+ + if (retValueWindow == 0 && prev != 0)
74
+ + NtUserAttachThreadInput(0, 0, 1);
75
+ + else
76
+ + prev = retValueWindow;
77
+ + }
32
78
+ }
33
79
+
34
80
+ return retValueWindow;
35
81
}
36
82
37
83
38
84
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c
39
- index d67f462359c..dee6c5acf57 100644
85
+ index 6268c978dab..1dbb6690c03 100644
40
86
--- a/dlls/win32u/input.c
41
87
+++ b/dlls/win32u/input.c
42
88
@@ -30,6 +30,8 @@
@@ -48,7 +94,7 @@ index d67f462359c..dee6c5acf57 100644
48
94
#include "ntstatus.h"
49
95
#define WIN32_NO_STATUS
50
96
#include "win32u_private.h"
51
- @@ -596,6 +598,38 @@ HWND get_focus(void)
97
+ @@ -596,6 +598,41 @@ HWND get_focus(void)
52
98
BOOL WINAPI NtUserAttachThreadInput( DWORD from, DWORD to, BOOL attach )
53
99
{
54
100
BOOL ret;
@@ -58,35 +104,38 @@ index d67f462359c..dee6c5acf57 100644
58
104
+ static char processNameForHack[16];
59
105
+ static const char* DAIprocessName = "DragonAgeInquis";
60
106
+ static const char* DAIGameLoopName = "GameLoop";
107
+ + static int is_DragonAgeInquis = -1;
61
108
+ const char *sgi;
62
109
+
63
- + if ((sgi = getenv("SteamGameId")) && !strcmp(sgi, "1222690") )
110
+ + if (is_DragonAgeInquis )
64
111
+ {
65
- + prctl(PR_GET_NAME, processNameForHack);
66
- +
67
- + TRACE("Process Name: %s\n", processNameForHack);
68
- +
69
- + if (strncmp(DAIprocessName, processNameForHack, 15) == 0 || strncmp(DAIGameLoopName, processNameForHack, 8) == 0)
112
+ + if ((is_DragonAgeInquis == 1) ||
113
+ + (is_DragonAgeInquis = ((sgi = getenv("SteamGameId")) && !strcmp(sgi, "1222690"))))
70
114
+ {
71
- + if (!visited)
115
+ + prctl(PR_GET_NAME, processNameForHack);
116
+ + TRACE("Process Name: %s\n", processNameForHack);
117
+ + if (strncmp(DAIprocessName, processNameForHack, 15) == 0 || strncmp(DAIGameLoopName, processNameForHack, 8) == 0)
72
118
+ {
73
- + TRACE("First Visit Process Name: %s\n", processNameForHack);
74
- + fromThreadForHack = from;
75
- + toThreadForHack = to;
76
- + visited = 1;
77
- + }
119
+ + if (!visited)
120
+ + {
121
+ + TRACE("First Visit Process Name: %s\n", processNameForHack);
122
+ + fromThreadForHack = from;
123
+ + toThreadForHack = to;
124
+ + visited = 1;
125
+ + }
78
126
+
79
- + if (from == 0 && to == 0 && visited)
80
- + {
81
- + TRACE("00 Process Name: %s\n", processNameForHack);
82
- + from = fromThreadForHack;
83
- + to = toThreadForHack;
127
+ + if (from == 0 && to == 0 && visited)
128
+ + {
129
+ + TRACE("00 Process Name: %s\n", processNameForHack);
130
+ + from = fromThreadForHack;
131
+ + to = toThreadForHack;
132
+ + }
84
133
+ }
85
134
+ }
86
135
+ }
87
136
88
137
SERVER_START_REQ( attach_thread_input )
89
138
{
90
139
- -
91
- 2.45.2
140
+ 2.48.1
92
141
0 commit comments