Otherwise we may later write the cancelled status to invalid memory.
Also use a manual-reset event, as it should be for overlapped I/O, so all waiters are waken up on cancel.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com ---
Supersedes: 216572-216573
dlls/xinput1_3/main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index 81d11e5d38f..1f9d4881751 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -354,6 +354,7 @@ static void controller_disable(struct xinput_controller *controller) controller->enabled = FALSE;
CancelIoEx(controller->device, &controller->hid.read_ovl); + WaitForSingleObject(controller->hid.read_ovl.hEvent, INFINITE); SetEvent(update_event); }
@@ -365,7 +366,7 @@ static BOOL controller_init(struct xinput_controller *controller, PHIDP_PREPARSE controller->hid.caps = *caps; if (!(controller->hid.feature_report_buf = calloc(1, controller->hid.caps.FeatureReportByteLength))) goto failed; if (!controller_check_caps(controller, device, preparsed)) goto failed; - if (!(event = CreateEventA(NULL, FALSE, FALSE, NULL))) goto failed; + if (!(event = CreateEventW(NULL, TRUE, FALSE, NULL))) goto failed;
TRACE("Found gamepad %s\n", debugstr_w(device_path));
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/xinput1_3/main.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index 1f9d4881751..36c02acb855 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -331,9 +331,14 @@ static DWORD HID_set_state(struct xinput_controller *controller, XINPUT_VIBRATIO return ERROR_SUCCESS; }
+static void controller_destroy(struct xinput_controller *controller, BOOL already_removed); + static void controller_enable(struct xinput_controller *controller) { + ULONG report_len = controller->hid.caps.InputReportByteLength; + char *report_buf = controller->hid.input_report_buf; XINPUT_VIBRATION state = controller->vibration; + BOOL ret;
if (controller->enabled) return; if (controller->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED) HID_set_state(controller, &state); @@ -341,8 +346,9 @@ static void controller_enable(struct xinput_controller *controller)
memset(&controller->hid.read_ovl, 0, sizeof(controller->hid.read_ovl)); controller->hid.read_ovl.hEvent = controller->hid.read_event; - ReadFile(controller->device, controller->hid.input_report_buf, controller->hid.caps.InputReportByteLength, NULL, &controller->hid.read_ovl); - SetEvent(update_event); + ret = ReadFile(controller->device, report_buf, report_len, NULL, &controller->hid.read_ovl); + if (!ret && GetLastError() != ERROR_IO_PENDING) controller_destroy(controller, TRUE); + else SetEvent(update_event); }
static void controller_disable(struct xinput_controller *controller) @@ -498,13 +504,13 @@ static void update_controller_list(void) SetupDiDestroyDeviceInfoList(set); }
-static void controller_destroy(struct xinput_controller *controller) +static void controller_destroy(struct xinput_controller *controller, BOOL already_removed) { EnterCriticalSection(&controller->crit);
if (controller->device) { - controller_disable(controller); + if (!already_removed) controller_disable(controller); CloseHandle(controller->device); controller->device = NULL;
@@ -529,7 +535,7 @@ static void stop_update_thread(void) CloseHandle(done_event); CloseHandle(update_event);
- for (i = 0; i < XUSER_MAX_COUNT; i++) controller_destroy(&controllers[i]); + for (i = 0; i < XUSER_MAX_COUNT; i++) controller_destroy(&controllers[i], FALSE); }
static LONG sign_extend(ULONG value, const HIDP_VALUE_CAPS *caps) @@ -555,11 +561,12 @@ static void read_controller_state(struct xinput_controller *controller) NTSTATUS status; USAGE buttons[11]; ULONG i, button_length, value; + BOOL ret;
if (!GetOverlappedResult(controller->device, &controller->hid.read_ovl, &read_len, TRUE)) { if (GetLastError() == ERROR_OPERATION_ABORTED) return; - if (GetLastError() == ERROR_ACCESS_DENIED || GetLastError() == ERROR_INVALID_HANDLE) controller_destroy(controller); + if (GetLastError() == ERROR_ACCESS_DENIED || GetLastError() == ERROR_INVALID_HANDLE) controller_destroy(controller, TRUE); else ERR("Failed to read input report, GetOverlappedResult failed with error %u\n", GetLastError()); return; } @@ -636,7 +643,8 @@ static void read_controller_state(struct xinput_controller *controller) controller->state = state; memset(&controller->hid.read_ovl, 0, sizeof(controller->hid.read_ovl)); controller->hid.read_ovl.hEvent = controller->hid.read_event; - ReadFile(controller->device, controller->hid.input_report_buf, controller->hid.caps.InputReportByteLength, NULL, &controller->hid.read_ovl); + ret = ReadFile(controller->device, report_buf, report_len, NULL, &controller->hid.read_ovl); + if (!ret && GetLastError() != ERROR_IO_PENDING) controller_destroy(controller, TRUE); } LeaveCriticalSection(&controller->crit); }
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/xinput1_1/Makefile.in | 2 +- dlls/xinput1_2/Makefile.in | 2 +- dlls/xinput1_3/Makefile.in | 2 +- dlls/xinput1_4/Makefile.in | 2 +- dlls/xinput9_1_0/Makefile.in | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/dlls/xinput1_1/Makefile.in b/dlls/xinput1_1/Makefile.in index 328435a110f..864b976f7e0 100644 --- a/dlls/xinput1_1/Makefile.in +++ b/dlls/xinput1_1/Makefile.in @@ -1,6 +1,6 @@ MODULE = xinput1_1.dll +IMPORTS = hid setupapi advapi32 PARENTSRC = ../xinput1_3 -DELAYIMPORTS = hid setupapi advapi32
C_SRCS = \ main.c diff --git a/dlls/xinput1_2/Makefile.in b/dlls/xinput1_2/Makefile.in index e66b6e67261..4dfcab040ee 100644 --- a/dlls/xinput1_2/Makefile.in +++ b/dlls/xinput1_2/Makefile.in @@ -1,6 +1,6 @@ MODULE = xinput1_2.dll +IMPORTS = hid setupapi advapi32 PARENTSRC = ../xinput1_3 -DELAYIMPORTS = hid setupapi advapi32
C_SRCS = \ main.c diff --git a/dlls/xinput1_3/Makefile.in b/dlls/xinput1_3/Makefile.in index 15ce3a691dd..105d29f3300 100644 --- a/dlls/xinput1_3/Makefile.in +++ b/dlls/xinput1_3/Makefile.in @@ -1,6 +1,6 @@ MODULE = xinput1_3.dll IMPORTLIB = xinput -DELAYIMPORTS = hid setupapi advapi32 +IMPORTS = hid setupapi advapi32
C_SRCS = \ main.c diff --git a/dlls/xinput1_4/Makefile.in b/dlls/xinput1_4/Makefile.in index b21a3d3ce53..2e243d8b268 100644 --- a/dlls/xinput1_4/Makefile.in +++ b/dlls/xinput1_4/Makefile.in @@ -1,6 +1,6 @@ MODULE = xinput1_4.dll +IMPORTS = hid setupapi advapi32 PARENTSRC = ../xinput1_3 -DELAYIMPORTS = hid setupapi advapi32
C_SRCS = \ main.c diff --git a/dlls/xinput9_1_0/Makefile.in b/dlls/xinput9_1_0/Makefile.in index f014e67dea3..dc97909914d 100644 --- a/dlls/xinput9_1_0/Makefile.in +++ b/dlls/xinput9_1_0/Makefile.in @@ -1,6 +1,6 @@ MODULE = xinput9_1_0.dll +IMPORTS = hid setupapi advapi32 PARENTSRC = ../xinput1_3 -DELAYIMPORTS = hid setupapi advapi32
C_SRCS = \ main.c
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/xinput1_3/main.c | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-)
diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index 36c02acb855..f49e74dc4b1 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -85,16 +85,6 @@ struct xinput_controller } hid; };
-/* xinput_crit guards controllers array */ -static CRITICAL_SECTION xinput_crit; -static CRITICAL_SECTION_DEBUG xinput_critsect_debug = -{ - 0, 0, &xinput_crit, - { &xinput_critsect_debug.ProcessLocksList, &xinput_critsect_debug.ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": xinput_crit") } -}; -static CRITICAL_SECTION xinput_crit = { &xinput_critsect_debug, -1, 0, 0, 0, 0 }; - static struct xinput_controller controllers[XUSER_MAX_COUNT]; static CRITICAL_SECTION_DEBUG controller_critsect_debug[XUSER_MAX_COUNT] = { @@ -128,6 +118,7 @@ static struct xinput_controller controllers[XUSER_MAX_COUNT] = {{ &controller_critsect_debug[3], -1, 0, 0, 0, 0 }}, };
+static HANDLE start_event; static HANDLE stop_event; static HANDLE done_event; static HANDLE update_event; @@ -531,6 +522,7 @@ static void stop_update_thread(void) SetEvent(stop_event); WaitForSingleObject(done_event, INFINITE);
+ CloseHandle(start_event); CloseHandle(stop_event); CloseHandle(done_event); CloseHandle(update_event); @@ -655,9 +647,11 @@ static DWORD WINAPI hid_update_thread_proc(void *param) HANDLE events[XUSER_MAX_COUNT + 2]; DWORD i, count = 2, ret = WAIT_TIMEOUT;
+ update_controller_list(); + SetEvent(start_event); + do { - EnterCriticalSection(&xinput_crit); if (ret == WAIT_TIMEOUT) update_controller_list(); if (ret < count - 2) read_controller_state(devices[ret]);
@@ -676,7 +670,6 @@ static DWORD WINAPI hid_update_thread_proc(void *param) } events[count++] = update_event; events[count++] = stop_event; - LeaveCriticalSection(&xinput_crit); } while ((ret = WaitForMultipleObjectsEx( count, events, FALSE, 2000, TRUE )) < count - 1 || ret == WAIT_TIMEOUT);
@@ -689,11 +682,14 @@ static BOOL WINAPI start_update_thread_once( INIT_ONCE *once, void *param, void { HANDLE thread;
+ start_event = CreateEventA(NULL, FALSE, FALSE, NULL); + if (!start_event) ERR("failed to create start event, error %u\n", GetLastError()); + stop_event = CreateEventA(NULL, FALSE, FALSE, NULL); if (!stop_event) ERR("failed to create stop event, error %u\n", GetLastError());
done_event = CreateEventA(NULL, FALSE, FALSE, NULL); - if (!done_event) ERR("failed to create stop event, error %u\n", GetLastError()); + if (!done_event) ERR("failed to create done event, error %u\n", GetLastError());
update_event = CreateEventA(NULL, FALSE, FALSE, NULL); if (!update_event) ERR("failed to create update event, error %u\n", GetLastError()); @@ -702,11 +698,7 @@ static BOOL WINAPI start_update_thread_once( INIT_ONCE *once, void *param, void if (!thread) ERR("failed to create update thread, error %u\n", GetLastError()); CloseHandle(thread);
- /* do it once now, to resolve delayed imports and populate the initial list */ - EnterCriticalSection(&xinput_crit); - update_controller_list(); - LeaveCriticalSection(&xinput_crit); - + WaitForSingleObject(start_event, INFINITE); return TRUE; }
Based on a patch from Andrew Eikum aeikum@codeweavers.com.
Dark Souls Remasters checks for xinput devices when it receives a WM_DEVICECHANGE message. We would only poll for new devices if it had been at least 2 seconds since the last check. So often, DS would receive the message, but we would refuse to poll for devices, so the game would think no controller was present.
This commit fixes that by also subscribing to event notifications and triggering a poll.
Signed-off-by: Rémi Bernon rbernon@codeweavers.com --- dlls/xinput1_1/Makefile.in | 2 +- dlls/xinput1_2/Makefile.in | 2 +- dlls/xinput1_3/Makefile.in | 2 +- dlls/xinput1_3/main.c | 38 +++++++++++++++++++++++++++++++++++- dlls/xinput1_4/Makefile.in | 2 +- dlls/xinput9_1_0/Makefile.in | 2 +- 6 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/dlls/xinput1_1/Makefile.in b/dlls/xinput1_1/Makefile.in index 864b976f7e0..cc2825d0d19 100644 --- a/dlls/xinput1_1/Makefile.in +++ b/dlls/xinput1_1/Makefile.in @@ -1,5 +1,5 @@ MODULE = xinput1_1.dll -IMPORTS = hid setupapi advapi32 +IMPORTS = hid setupapi advapi32 user32 PARENTSRC = ../xinput1_3
C_SRCS = \ diff --git a/dlls/xinput1_2/Makefile.in b/dlls/xinput1_2/Makefile.in index 4dfcab040ee..1c4b48cdbda 100644 --- a/dlls/xinput1_2/Makefile.in +++ b/dlls/xinput1_2/Makefile.in @@ -1,5 +1,5 @@ MODULE = xinput1_2.dll -IMPORTS = hid setupapi advapi32 +IMPORTS = hid setupapi advapi32 user32 PARENTSRC = ../xinput1_3
C_SRCS = \ diff --git a/dlls/xinput1_3/Makefile.in b/dlls/xinput1_3/Makefile.in index 105d29f3300..a1c58a7452a 100644 --- a/dlls/xinput1_3/Makefile.in +++ b/dlls/xinput1_3/Makefile.in @@ -1,6 +1,6 @@ MODULE = xinput1_3.dll IMPORTLIB = xinput -IMPORTS = hid setupapi advapi32 +IMPORTS = hid setupapi advapi32 user32
C_SRCS = \ main.c diff --git a/dlls/xinput1_3/main.c b/dlls/xinput1_3/main.c index f49e74dc4b1..506802d142a 100644 --- a/dlls/xinput1_3/main.c +++ b/dlls/xinput1_3/main.c @@ -33,6 +33,7 @@ #include "winnls.h" #include "winternl.h"
+#include "dbt.h" #include "setupapi.h" #include "devpkey.h" #include "hidusage.h" @@ -118,6 +119,7 @@ static struct xinput_controller controllers[XUSER_MAX_COUNT] = {{ &controller_critsect_debug[3], -1, 0, 0, 0, 0 }}, };
+static HMODULE xinput_instance; static HANDLE start_event; static HANDLE stop_event; static HANDLE done_event; @@ -641,17 +643,45 @@ static void read_controller_state(struct xinput_controller *controller) LeaveCriticalSection(&controller->crit); }
+static LRESULT CALLBACK xinput_devnotify_wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) +{ + if (msg == WM_DEVICECHANGE && wparam == DBT_DEVICEARRIVAL) update_controller_list(); + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + static DWORD WINAPI hid_update_thread_proc(void *param) { struct xinput_controller *devices[XUSER_MAX_COUNT + 2]; HANDLE events[XUSER_MAX_COUNT + 2]; DWORD i, count = 2, ret = WAIT_TIMEOUT; + DEV_BROADCAST_DEVICEINTERFACE_W filter = + { + .dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE_W), + .dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE, + .dbcc_classguid = GUID_DEVINTERFACE_WINEXINPUT, + }; + WNDCLASSEXW cls = + { + .cbSize = sizeof(WNDCLASSEXW), + .hInstance = xinput_instance, + .lpszClassName = L"__wine_xinput_devnotify", + .lpfnWndProc = xinput_devnotify_wndproc, + }; + HDEVNOTIFY notif; + HWND hwnd; + MSG msg; + + RegisterClassExW(&cls); + hwnd = CreateWindowExW(0, cls.lpszClassName, NULL, 0, 0, 0, 0, 0, + HWND_MESSAGE, NULL, NULL, NULL); + notif = RegisterDeviceNotificationW(hwnd, &filter, DEVICE_NOTIFY_WINDOW_HANDLE);
update_controller_list(); SetEvent(start_event);
do { + if (ret == count) while (PeekMessageW(&msg, hwnd, 0, 0, PM_REMOVE)) DispatchMessageW(&msg); if (ret == WAIT_TIMEOUT) update_controller_list(); if (ret < count - 2) read_controller_state(devices[ret]);
@@ -671,7 +701,12 @@ static DWORD WINAPI hid_update_thread_proc(void *param) events[count++] = update_event; events[count++] = stop_event; } - while ((ret = WaitForMultipleObjectsEx( count, events, FALSE, 2000, TRUE )) < count - 1 || ret == WAIT_TIMEOUT); + while ((ret = MsgWaitForMultipleObjectsEx(count, events, 2000, QS_ALLINPUT, MWMO_ALERTABLE)) < count - 1 || + ret == count || ret == WAIT_TIMEOUT); + + UnregisterDeviceNotification(notif); + DestroyWindow(hwnd); + UnregisterClassW(cls.lpszClassName, xinput_instance);
if (ret != count - 1) ERR("update thread exited unexpectedly, ret %u\n", ret); SetEvent(done_event); @@ -733,6 +768,7 @@ BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) switch (reason) { case DLL_PROCESS_ATTACH: + xinput_instance = inst; DisableThreadLibraryCalls(inst); break; case DLL_PROCESS_DETACH: diff --git a/dlls/xinput1_4/Makefile.in b/dlls/xinput1_4/Makefile.in index 2e243d8b268..393a416af9e 100644 --- a/dlls/xinput1_4/Makefile.in +++ b/dlls/xinput1_4/Makefile.in @@ -1,5 +1,5 @@ MODULE = xinput1_4.dll -IMPORTS = hid setupapi advapi32 +IMPORTS = hid setupapi advapi32 user32 PARENTSRC = ../xinput1_3
C_SRCS = \ diff --git a/dlls/xinput9_1_0/Makefile.in b/dlls/xinput9_1_0/Makefile.in index dc97909914d..55ea44ba666 100644 --- a/dlls/xinput9_1_0/Makefile.in +++ b/dlls/xinput9_1_0/Makefile.in @@ -1,5 +1,5 @@ MODULE = xinput9_1_0.dll -IMPORTS = hid setupapi advapi32 +IMPORTS = hid setupapi advapi32 user32 PARENTSRC = ../xinput1_3
C_SRCS = \