[PATCH 1/4] xinput: Make device hotplugging thread-safe

Andrew Eikum aeikum at codeweavers.com
Tue Oct 22 15:00:26 CDT 2019


Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>
---
 dlls/xinput1_3/hid.c            | 239 ++++++++++++++++----------------
 dlls/xinput1_3/xinput_main.c    |  95 ++++++++++++-
 dlls/xinput1_3/xinput_private.h |   7 +-
 3 files changed, 210 insertions(+), 131 deletions(-)

diff --git a/dlls/xinput1_3/hid.c b/dlls/xinput1_3/hid.c
index 38c93beb80f..477d5304cac 100644
--- a/dlls/xinput1_3/hid.c
+++ b/dlls/xinput1_3/hid.c
@@ -47,15 +47,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(xinput);
 
 #define XINPUT_GAMEPAD_GUIDE 0x0400
 
-static CRITICAL_SECTION hid_xinput_crit;
-static CRITICAL_SECTION_DEBUG hid_critsect_debug =
-{
-    0, 0, &hid_xinput_crit,
-    { &hid_critsect_debug.ProcessLocksList, &hid_critsect_debug.ProcessLocksList },
-      0, 0, { (DWORD_PTR)(__FILE__ ": hid_xinput_crit") }
-};
-static CRITICAL_SECTION hid_xinput_crit = { &hid_critsect_debug, -1, 0, 0, 0, 0 };
-
 struct axis_info
 {
     LONG min;
@@ -69,8 +60,6 @@ struct hid_platform_private {
     WCHAR *device_path;
     BOOL enabled;
 
-    CRITICAL_SECTION crit;
-
     DWORD report_length;
     BYTE current_report;
     CHAR *reports[2];
@@ -195,9 +184,6 @@ static void build_private(struct hid_platform_private *private, PHIDP_PREPARSED_
     private->device_path = HeapAlloc(GetProcessHeap(), 0, size);
     memcpy(private->device_path, path, size);
     private->enabled = TRUE;
-
-    InitializeCriticalSection(&private->crit);
-    private->crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
 }
 
 void HID_find_gamepads(xinput_controller *devices)
@@ -208,20 +194,26 @@ void HID_find_gamepads(xinput_controller *devices)
     SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
     PHIDP_PREPARSED_DATA ppd;
     DWORD detail_size = MAX_PATH * sizeof(WCHAR);
-    HANDLE device = INVALID_HANDLE_VALUE;
+    HANDLE device;
     HIDP_CAPS Caps;
-    DWORD idx,didx;
-    int i;
+    DWORD idx;
+    int i, open_device_idx;
 
     idx = GetTickCount();
     if ((idx - last_check) < 2000)
         return;
+
+    EnterCriticalSection(&xinput_crit);
+
+    if ((idx - last_check) < 2000)
+    {
+        LeaveCriticalSection(&xinput_crit);
+        return;
+    }
     last_check = idx;
 
     HidD_GetHidGuid(&hid_guid);
 
-    EnterCriticalSection(&hid_xinput_crit);
-
     device_info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
 
     data = HeapAlloc(GetProcessHeap(), 0 , sizeof(*data) + detail_size);
@@ -230,9 +222,9 @@ void HID_find_gamepads(xinput_controller *devices)
     ZeroMemory(&interface_data, sizeof(interface_data));
     interface_data.cbSize = sizeof(interface_data);
 
-    idx = didx = 0;
+    idx = 0;
     while (SetupDiEnumDeviceInterfaces(device_info_set, NULL, &hid_guid, idx++,
-           &interface_data) && didx < XUSER_MAX_COUNT)
+           &interface_data))
     {
         static const WCHAR ig[] = {'I','G','_',0};
         if (!SetupDiGetDeviceInterfaceDetailW(device_info_set,
@@ -242,14 +234,24 @@ void HID_find_gamepads(xinput_controller *devices)
         if (!wcsstr(data->DevicePath, ig))
             continue;
 
+        open_device_idx = -1;
         for (i = 0; i < XUSER_MAX_COUNT; i++)
         {
             struct hid_platform_private *private = devices[i].platform_private;
-            if (devices[i].connected && !wcscmp(data->DevicePath, private->device_path))
-                break;
+            if (devices[i].connected)
+            {
+                if (!wcscmp(data->DevicePath, private->device_path))
+                    break;
+            }
+            else if(open_device_idx < 0)
+                open_device_idx = i;
         }
         if (i != XUSER_MAX_COUNT)
+            /* this device is already opened */
             continue;
+        if (open_device_idx < 0)
+            /* no open device slots */
+            break;
         device = CreateFileW(data->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
         if (device == INVALID_HANDLE_VALUE)
             continue;
@@ -262,13 +264,12 @@ void HID_find_gamepads(xinput_controller *devices)
              Caps.Usage == 0x8 /* Multi-axis Controller */))
         {
             struct hid_platform_private *private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct hid_platform_private));
-            if (VerifyGamepad(ppd, &(devices[didx].caps), private, &Caps))
+            if (VerifyGamepad(ppd, &devices[open_device_idx].caps, private, &Caps))
             {
-                TRACE("Found gamepad %i %s\n",didx, debugstr_w(data->DevicePath));
-                devices[didx].connected = TRUE;
+                TRACE("Found gamepad %i %s\n", open_device_idx, debugstr_w(data->DevicePath));
                 build_private(private, ppd, &Caps, device, data->DevicePath);
-                devices[didx].platform_private = private;
-                didx++;
+                devices[open_device_idx].platform_private = private;
+                devices[open_device_idx].connected = TRUE;
             }
             else
             {
@@ -282,41 +283,39 @@ void HID_find_gamepads(xinput_controller *devices)
             CloseHandle(device);
             HidD_FreePreparsedData(ppd);
         }
-        device = INVALID_HANDLE_VALUE;
     }
     HeapFree(GetProcessHeap(), 0, data);
     SetupDiDestroyDeviceInfoList(device_info_set);
-    LeaveCriticalSection(&hid_xinput_crit);
-    return;
+    LeaveCriticalSection(&xinput_crit);
 }
 
 static void remove_gamepad(xinput_controller *device)
 {
+    EnterCriticalSection(&device->crit);
+
     if (device->connected)
     {
         struct hid_platform_private *private = device->platform_private;
 
-        EnterCriticalSection(&private->crit);
+        device->connected = FALSE;
+
         CloseHandle(private->device);
         HeapFree(GetProcessHeap(), 0, private->reports[0]);
         HeapFree(GetProcessHeap(), 0, private->reports[1]);
         HeapFree(GetProcessHeap(), 0, private->device_path);
         HidD_FreePreparsedData(private->ppd);
         device->platform_private = NULL;
-        device->connected = FALSE;
-        LeaveCriticalSection(&private->crit);
-        DeleteCriticalSection(&private->crit);
         HeapFree(GetProcessHeap(), 0, private);
     }
+
+    LeaveCriticalSection(&device->crit);
 }
 
 void HID_destroy_gamepads(xinput_controller *devices)
 {
     int i;
-    EnterCriticalSection(&hid_xinput_crit);
     for (i = 0; i < XUSER_MAX_COUNT; i++)
         remove_gamepad(&devices[i]);
-    LeaveCriticalSection(&hid_xinput_crit);
 }
 
 static SHORT scale_short(LONG value, const struct axis_info *axis)
@@ -329,7 +328,7 @@ static BYTE scale_byte(LONG value, const struct axis_info *axis)
     return (((ULONGLONG)(value - axis->min)) * 0xff) / axis->range;
 }
 
-void HID_update_state(xinput_controller* device)
+void HID_update_state(xinput_controller *device, XINPUT_STATE *state)
 {
     struct hid_platform_private *private = device->platform_private;
     int i;
@@ -343,107 +342,107 @@ void HID_update_state(xinput_controller* device)
     if (!private->enabled)
         return;
 
-    EnterCriticalSection(&private->crit);
     if (!HidD_GetInputReport(private->device, target_report, private->report_length))
     {
         if (GetLastError() == ERROR_ACCESS_DENIED || GetLastError() == ERROR_INVALID_HANDLE)
+        {
+            EnterCriticalSection(&xinput_crit);
             remove_gamepad(device);
+            LeaveCriticalSection(&xinput_crit);
+        }
         else
             ERR("Failed to get Input Report (%x)\n", GetLastError());
-        LeaveCriticalSection(&private->crit);
         return;
     }
-    if (memcmp(report, target_report, private->report_length) == 0)
+    if (memcmp(report, target_report, private->report_length) != 0)
     {
-        LeaveCriticalSection(&private->crit);
-        return;
-    }
-
-    private->current_report = (private->current_report+1)%2;
+        private->current_report = (private->current_report+1)%2;
 
-    device->state.dwPacketNumber++;
-    button_length = ARRAY_SIZE(buttons);
-    HidP_GetUsages(HidP_Input, HID_USAGE_PAGE_BUTTON, 0, buttons, &button_length, private->ppd, target_report, private->report_length);
+        device->state.dwPacketNumber++;
+        button_length = ARRAY_SIZE(buttons);
+        HidP_GetUsages(HidP_Input, HID_USAGE_PAGE_BUTTON, 0, buttons, &button_length, private->ppd, target_report, private->report_length);
 
-    device->state.Gamepad.wButtons = 0;
-    for (i = 0; i < button_length; i++)
-    {
-        switch (buttons[i])
+        device->state.Gamepad.wButtons = 0;
+        for (i = 0; i < button_length; i++)
         {
-            case 1: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_A; break;
-            case 2: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_B; break;
-            case 3: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_X; break;
-            case 4: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_Y; break;
-            case 5: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_SHOULDER; break;
-            case 6: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_SHOULDER; break;
-            case 7: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_BACK; break;
-            case 8: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_START; break;
-            case 9: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; break;
-            case 10: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB; break;
-            case 11: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; break;
+            switch (buttons[i])
+            {
+                case 1: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_A; break;
+                case 2: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_B; break;
+                case 3: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_X; break;
+                case 4: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_Y; break;
+                case 5: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_SHOULDER; break;
+                case 6: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_SHOULDER; break;
+                case 7: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_BACK; break;
+                case 8: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_START; break;
+                case 9: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; break;
+                case 10: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB; break;
+                case 11: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; break;
+            }
         }
-    }
 
-    if(HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_HATSWITCH, &hat_value,
-                              private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
-    {
-        switch(hat_value){
-            /* 8 1 2
-             * 7 0 3
-             * 6 5 4 */
-            case 0:
-                break;
-            case 1:
-                device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP;
-                break;
-            case 2:
-                device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_DPAD_RIGHT;
-                break;
-            case 3:
-                device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT;
-                break;
-            case 4:
-                device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_DPAD_DOWN;
-                break;
-            case 5:
-                device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN;
-                break;
-            case 6:
-                device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_DPAD_LEFT;
-                break;
-            case 7:
-                device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT;
-                break;
-            case 8:
-                device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_DPAD_UP;
-                break;
+        if(HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_HATSWITCH, &hat_value,
+                                  private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
+        {
+            switch(hat_value){
+                /* 8 1 2
+                 * 7 0 3
+                 * 6 5 4 */
+                case 0:
+                    break;
+                case 1:
+                    device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP;
+                    break;
+                case 2:
+                    device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_DPAD_RIGHT;
+                    break;
+                case 3:
+                    device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT;
+                    break;
+                case 4:
+                    device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_DPAD_DOWN;
+                    break;
+                case 5:
+                    device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN;
+                    break;
+                case 6:
+                    device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_DPAD_LEFT;
+                    break;
+                case 7:
+                    device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT;
+                    break;
+                case 8:
+                    device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_DPAD_UP;
+                    break;
+            }
         }
-    }
 
-    if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_X, &value,
-                                    private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
-        device->state.Gamepad.sThumbLX = scale_short(value, &private->lx);
+        if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_X, &value,
+                                        private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
+            device->state.Gamepad.sThumbLX = scale_short(value, &private->lx);
 
-    if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Y, &value,
-                                    private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
-        device->state.Gamepad.sThumbLY = -scale_short(value, &private->ly) - 1;
+        if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Y, &value,
+                                        private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
+            device->state.Gamepad.sThumbLY = -scale_short(value, &private->ly) - 1;
 
-    if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RX, &value,
-                                    private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
-        device->state.Gamepad.sThumbRX = scale_short(value, &private->rx);
+        if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RX, &value,
+                                        private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
+            device->state.Gamepad.sThumbRX = scale_short(value, &private->rx);
 
-    if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RY, &value,
-                                    private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
-        device->state.Gamepad.sThumbRY = -scale_short(value, &private->ry) - 1;
+        if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RY, &value,
+                                        private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
+            device->state.Gamepad.sThumbRY = -scale_short(value, &private->ry) - 1;
 
-    if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RZ, &value,
-                                    private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
-        device->state.Gamepad.bRightTrigger = scale_byte(value, &private->rtrigger);
+        if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RZ, &value,
+                                        private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
+            device->state.Gamepad.bRightTrigger = scale_byte(value, &private->rtrigger);
+
+        if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z, &value,
+                                        private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
+            device->state.Gamepad.bLeftTrigger = scale_byte(value, &private->ltrigger);
+    }
 
-    if(HidP_GetScaledUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z, &value,
-                                    private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
-        device->state.Gamepad.bLeftTrigger = scale_byte(value, &private->ltrigger);
-    LeaveCriticalSection(&private->crit);
+    memcpy(state, &device->state, sizeof(*state));
 }
 
 DWORD HID_set_state(xinput_controller* device, XINPUT_VIBRATION* state)
@@ -474,9 +473,7 @@ DWORD HID_set_state(xinput_controller* device, XINPUT_VIBRATION* state)
             report.right = (BYTE)(state->wRightMotorSpeed / 256);
             memset(&report.pad2, 0, sizeof(report.pad2));
 
-            EnterCriticalSection(&private->crit);
             rc = HidD_SetOutputReport(private->device, &report, sizeof(report));
-            LeaveCriticalSection(&private->crit);
             if (rc)
                 return ERROR_SUCCESS;
             return GetLastError();
@@ -492,7 +489,6 @@ void HID_enable(xinput_controller* device, BOOL enable)
 
     if (device->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED)
     {
-        EnterCriticalSection(&private->crit);
         if (private->enabled && !enable)
         {
             XINPUT_VIBRATION state;
@@ -504,7 +500,6 @@ void HID_enable(xinput_controller* device, BOOL enable)
         {
             HID_set_state(device, &device->vibration);
         }
-        LeaveCriticalSection(&private->crit);
     }
 
     private->enabled = enable;
diff --git a/dlls/xinput1_3/xinput_main.c b/dlls/xinput1_3/xinput_main.c
index a417fec7ad1..5c83e87fb13 100644
--- a/dlls/xinput1_3/xinput_main.c
+++ b/dlls/xinput1_3/xinput_main.c
@@ -34,7 +34,66 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(xinput);
 
-xinput_controller controllers[XUSER_MAX_COUNT];
+/* xinput_crit guards controllers array */
+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") }
+};
+CRITICAL_SECTION xinput_crit = { &xinput_critsect_debug, -1, 0, 0, 0, 0 };
+
+static CRITICAL_SECTION_DEBUG controller_critsect_debug[XUSER_MAX_COUNT] =
+{
+    {
+        0, 0, &controllers[0].crit,
+        { &controller_critsect_debug[0].ProcessLocksList, &controller_critsect_debug[0].ProcessLocksList },
+          0, 0, { (DWORD_PTR)(__FILE__ ": controllers[0].crit") }
+    },
+    {
+        0, 0, &controllers[1].crit,
+        { &controller_critsect_debug[1].ProcessLocksList, &controller_critsect_debug[1].ProcessLocksList },
+          0, 0, { (DWORD_PTR)(__FILE__ ": controllers[1].crit") }
+    },
+    {
+        0, 0, &controllers[2].crit,
+        { &controller_critsect_debug[2].ProcessLocksList, &controller_critsect_debug[2].ProcessLocksList },
+          0, 0, { (DWORD_PTR)(__FILE__ ": controllers[2].crit") }
+    },
+    {
+        0, 0, &controllers[3].crit,
+        { &controller_critsect_debug[3].ProcessLocksList, &controller_critsect_debug[3].ProcessLocksList },
+          0, 0, { (DWORD_PTR)(__FILE__ ": controllers[3].crit") }
+    },
+};
+
+xinput_controller controllers[XUSER_MAX_COUNT] = {
+    {{ &controller_critsect_debug[0], -1, 0, 0, 0, 0 }},
+    {{ &controller_critsect_debug[1], -1, 0, 0, 0, 0 }},
+    {{ &controller_critsect_debug[2], -1, 0, 0, 0, 0 }},
+    {{ &controller_critsect_debug[3], -1, 0, 0, 0, 0 }},
+};
+
+static BOOL verify_and_lock_device(xinput_controller *device)
+{
+    if (!device->connected)
+        return FALSE;
+
+    EnterCriticalSection(&device->crit);
+
+    if (!device->connected)
+    {
+        LeaveCriticalSection(&device->crit);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void unlock_device(xinput_controller *device)
+{
+    LeaveCriticalSection(&device->crit);
+}
 
 BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved)
 {
@@ -65,23 +124,30 @@ void WINAPI DECLSPEC_HOTPATCH XInputEnable(BOOL enable)
 
     for (index = 0; index < XUSER_MAX_COUNT; index ++)
     {
-        if (!controllers[index].connected) continue;
+        if (!verify_and_lock_device(&controllers[index])) continue;
         HID_enable(&controllers[index], enable);
+        unlock_device(&controllers[index]);
     }
 }
 
 DWORD WINAPI DECLSPEC_HOTPATCH XInputSetState(DWORD index, XINPUT_VIBRATION* vibration)
 {
+    DWORD ret;
+
     TRACE("(index %u, vibration %p)\n", index, vibration);
 
     HID_find_gamepads(controllers);
 
     if (index >= XUSER_MAX_COUNT)
         return ERROR_BAD_ARGUMENTS;
-    if (!controllers[index].connected)
+    if (!verify_and_lock_device(&controllers[index]))
         return ERROR_DEVICE_NOT_CONNECTED;
 
-    return HID_set_state(&controllers[index], vibration);
+    ret = HID_set_state(&controllers[index], vibration);
+
+    unlock_device(&controllers[index]);
+
+    return ret;
 }
 
 /* Some versions of SteamOverlayRenderer hot-patch XInputGetStateEx() and call
@@ -95,11 +161,19 @@ static DWORD xinput_get_state(DWORD index, XINPUT_STATE *state)
 
     if (index >= XUSER_MAX_COUNT)
         return ERROR_BAD_ARGUMENTS;
+    if (!verify_and_lock_device(&controllers[index]))
+        return ERROR_DEVICE_NOT_CONNECTED;
+
+    HID_update_state(&controllers[index], state);
+
     if (!controllers[index].connected)
+    {
+        /* update_state may have disconnected the controller */
+        unlock_device(&controllers[index]);
         return ERROR_DEVICE_NOT_CONNECTED;
+    }
 
-    HID_update_state(&controllers[index]);
-    memcpy(state, &controllers[index].state, sizeof(XINPUT_STATE));
+    unlock_device(&controllers[index]);
 
     return ERROR_SUCCESS;
 }
@@ -151,13 +225,20 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilities(DWORD index, DWORD flags, X
 
     if (index >= XUSER_MAX_COUNT)
         return ERROR_BAD_ARGUMENTS;
-    if (!controllers[index].connected)
+
+    if (!verify_and_lock_device(&controllers[index]))
         return ERROR_DEVICE_NOT_CONNECTED;
+
     if (flags & XINPUT_FLAG_GAMEPAD && controllers[index].caps.SubType != XINPUT_DEVSUBTYPE_GAMEPAD)
+    {
+        unlock_device(&controllers[index]);
         return ERROR_DEVICE_NOT_CONNECTED;
+    }
 
     memcpy(capabilities, &controllers[index].caps, sizeof(*capabilities));
 
+    unlock_device(&controllers[index]);
+
     return ERROR_SUCCESS;
 }
 
diff --git a/dlls/xinput1_3/xinput_private.h b/dlls/xinput1_3/xinput_private.h
index d6e0e9614ee..74cf318a6fb 100644
--- a/dlls/xinput1_3/xinput_private.h
+++ b/dlls/xinput1_3/xinput_private.h
@@ -19,16 +19,19 @@
 
 typedef struct _xinput_controller
 {
-    BOOL connected;
+    CRITICAL_SECTION crit;
+    BOOL connected; /* only TRUE when device is valid; may be used without holding crit */
     XINPUT_CAPABILITIES caps;
     void *platform_private;
     XINPUT_STATE state;
     XINPUT_VIBRATION vibration;
 } xinput_controller;
 
+CRITICAL_SECTION xinput_crit;
+xinput_controller controllers[XUSER_MAX_COUNT];
 
 void HID_find_gamepads(xinput_controller *devices) DECLSPEC_HIDDEN;
 void HID_destroy_gamepads(xinput_controller *devices) DECLSPEC_HIDDEN;
-void HID_update_state(xinput_controller* device) DECLSPEC_HIDDEN;
+void HID_update_state(xinput_controller* device, XINPUT_STATE *state) DECLSPEC_HIDDEN;
 DWORD HID_set_state(xinput_controller* device, XINPUT_VIBRATION* state) DECLSPEC_HIDDEN;
 void HID_enable(xinput_controller* device, BOOL enable) DECLSPEC_HIDDEN;
-- 
2.23.0





More information about the wine-devel mailing list