xinput: Make device hotplugging thread-safe.
Signed-off-by: Andrew Eikum <aeikum@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
c59e9d302b
commit
4d3db6d2d7
|
@ -47,15 +47,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(xinput);
|
||||||
|
|
||||||
#define XINPUT_GAMEPAD_GUIDE 0x0400
|
#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
|
struct axis_info
|
||||||
{
|
{
|
||||||
LONG min;
|
LONG min;
|
||||||
|
@ -69,8 +60,6 @@ struct hid_platform_private {
|
||||||
WCHAR *device_path;
|
WCHAR *device_path;
|
||||||
BOOL enabled;
|
BOOL enabled;
|
||||||
|
|
||||||
CRITICAL_SECTION crit;
|
|
||||||
|
|
||||||
DWORD report_length;
|
DWORD report_length;
|
||||||
BYTE current_report;
|
BYTE current_report;
|
||||||
CHAR *reports[2];
|
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);
|
private->device_path = HeapAlloc(GetProcessHeap(), 0, size);
|
||||||
memcpy(private->device_path, path, size);
|
memcpy(private->device_path, path, size);
|
||||||
private->enabled = TRUE;
|
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)
|
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;
|
SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
|
||||||
PHIDP_PREPARSED_DATA ppd;
|
PHIDP_PREPARSED_DATA ppd;
|
||||||
DWORD detail_size = MAX_PATH * sizeof(WCHAR);
|
DWORD detail_size = MAX_PATH * sizeof(WCHAR);
|
||||||
HANDLE device = INVALID_HANDLE_VALUE;
|
HANDLE device;
|
||||||
HIDP_CAPS Caps;
|
HIDP_CAPS Caps;
|
||||||
DWORD idx,didx;
|
DWORD idx;
|
||||||
int i;
|
int i, open_device_idx;
|
||||||
|
|
||||||
idx = GetTickCount();
|
idx = GetTickCount();
|
||||||
if ((idx - last_check) < 2000)
|
if ((idx - last_check) < 2000)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
EnterCriticalSection(&xinput_crit);
|
||||||
|
|
||||||
|
if ((idx - last_check) < 2000)
|
||||||
|
{
|
||||||
|
LeaveCriticalSection(&xinput_crit);
|
||||||
|
return;
|
||||||
|
}
|
||||||
last_check = idx;
|
last_check = idx;
|
||||||
|
|
||||||
HidD_GetHidGuid(&hid_guid);
|
HidD_GetHidGuid(&hid_guid);
|
||||||
|
|
||||||
EnterCriticalSection(&hid_xinput_crit);
|
|
||||||
|
|
||||||
device_info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
|
device_info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
|
||||||
|
|
||||||
data = HeapAlloc(GetProcessHeap(), 0 , sizeof(*data) + detail_size);
|
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));
|
ZeroMemory(&interface_data, sizeof(interface_data));
|
||||||
interface_data.cbSize = sizeof(interface_data);
|
interface_data.cbSize = sizeof(interface_data);
|
||||||
|
|
||||||
idx = didx = 0;
|
idx = 0;
|
||||||
while (SetupDiEnumDeviceInterfaces(device_info_set, NULL, &hid_guid, idx++,
|
while (SetupDiEnumDeviceInterfaces(device_info_set, NULL, &hid_guid, idx++,
|
||||||
&interface_data) && didx < XUSER_MAX_COUNT)
|
&interface_data))
|
||||||
{
|
{
|
||||||
static const WCHAR ig[] = {'I','G','_',0};
|
static const WCHAR ig[] = {'I','G','_',0};
|
||||||
if (!SetupDiGetDeviceInterfaceDetailW(device_info_set,
|
if (!SetupDiGetDeviceInterfaceDetailW(device_info_set,
|
||||||
|
@ -242,14 +234,24 @@ void HID_find_gamepads(xinput_controller *devices)
|
||||||
if (!wcsstr(data->DevicePath, ig))
|
if (!wcsstr(data->DevicePath, ig))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
open_device_idx = -1;
|
||||||
for (i = 0; i < XUSER_MAX_COUNT; i++)
|
for (i = 0; i < XUSER_MAX_COUNT; i++)
|
||||||
{
|
{
|
||||||
struct hid_platform_private *private = devices[i].platform_private;
|
struct hid_platform_private *private = devices[i].platform_private;
|
||||||
if (devices[i].connected && !wcscmp(data->DevicePath, private->device_path))
|
if (devices[i].connected)
|
||||||
break;
|
{
|
||||||
|
if (!wcscmp(data->DevicePath, private->device_path))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if(open_device_idx < 0)
|
||||||
|
open_device_idx = i;
|
||||||
}
|
}
|
||||||
if (i != XUSER_MAX_COUNT)
|
if (i != XUSER_MAX_COUNT)
|
||||||
|
/* this device is already opened */
|
||||||
continue;
|
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 );
|
device = CreateFileW(data->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
|
||||||
if (device == INVALID_HANDLE_VALUE)
|
if (device == INVALID_HANDLE_VALUE)
|
||||||
continue;
|
continue;
|
||||||
|
@ -262,13 +264,12 @@ void HID_find_gamepads(xinput_controller *devices)
|
||||||
Caps.Usage == 0x8 /* Multi-axis Controller */))
|
Caps.Usage == 0x8 /* Multi-axis Controller */))
|
||||||
{
|
{
|
||||||
struct hid_platform_private *private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct hid_platform_private));
|
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));
|
TRACE("Found gamepad %i %s\n", open_device_idx, debugstr_w(data->DevicePath));
|
||||||
devices[didx].connected = TRUE;
|
|
||||||
build_private(private, ppd, &Caps, device, data->DevicePath);
|
build_private(private, ppd, &Caps, device, data->DevicePath);
|
||||||
devices[didx].platform_private = private;
|
devices[open_device_idx].platform_private = private;
|
||||||
didx++;
|
devices[open_device_idx].connected = TRUE;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -282,41 +283,39 @@ void HID_find_gamepads(xinput_controller *devices)
|
||||||
CloseHandle(device);
|
CloseHandle(device);
|
||||||
HidD_FreePreparsedData(ppd);
|
HidD_FreePreparsedData(ppd);
|
||||||
}
|
}
|
||||||
device = INVALID_HANDLE_VALUE;
|
|
||||||
}
|
}
|
||||||
HeapFree(GetProcessHeap(), 0, data);
|
HeapFree(GetProcessHeap(), 0, data);
|
||||||
SetupDiDestroyDeviceInfoList(device_info_set);
|
SetupDiDestroyDeviceInfoList(device_info_set);
|
||||||
LeaveCriticalSection(&hid_xinput_crit);
|
LeaveCriticalSection(&xinput_crit);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void remove_gamepad(xinput_controller *device)
|
static void remove_gamepad(xinput_controller *device)
|
||||||
{
|
{
|
||||||
|
EnterCriticalSection(&device->crit);
|
||||||
|
|
||||||
if (device->connected)
|
if (device->connected)
|
||||||
{
|
{
|
||||||
struct hid_platform_private *private = device->platform_private;
|
struct hid_platform_private *private = device->platform_private;
|
||||||
|
|
||||||
EnterCriticalSection(&private->crit);
|
device->connected = FALSE;
|
||||||
|
|
||||||
CloseHandle(private->device);
|
CloseHandle(private->device);
|
||||||
HeapFree(GetProcessHeap(), 0, private->reports[0]);
|
HeapFree(GetProcessHeap(), 0, private->reports[0]);
|
||||||
HeapFree(GetProcessHeap(), 0, private->reports[1]);
|
HeapFree(GetProcessHeap(), 0, private->reports[1]);
|
||||||
HeapFree(GetProcessHeap(), 0, private->device_path);
|
HeapFree(GetProcessHeap(), 0, private->device_path);
|
||||||
HidD_FreePreparsedData(private->ppd);
|
HidD_FreePreparsedData(private->ppd);
|
||||||
device->platform_private = NULL;
|
device->platform_private = NULL;
|
||||||
device->connected = FALSE;
|
|
||||||
LeaveCriticalSection(&private->crit);
|
|
||||||
DeleteCriticalSection(&private->crit);
|
|
||||||
HeapFree(GetProcessHeap(), 0, private);
|
HeapFree(GetProcessHeap(), 0, private);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LeaveCriticalSection(&device->crit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HID_destroy_gamepads(xinput_controller *devices)
|
void HID_destroy_gamepads(xinput_controller *devices)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
EnterCriticalSection(&hid_xinput_crit);
|
|
||||||
for (i = 0; i < XUSER_MAX_COUNT; i++)
|
for (i = 0; i < XUSER_MAX_COUNT; i++)
|
||||||
remove_gamepad(&devices[i]);
|
remove_gamepad(&devices[i]);
|
||||||
LeaveCriticalSection(&hid_xinput_crit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static SHORT scale_short(LONG value, const struct axis_info *axis)
|
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;
|
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;
|
struct hid_platform_private *private = device->platform_private;
|
||||||
int i;
|
int i;
|
||||||
|
@ -343,107 +342,107 @@ void HID_update_state(xinput_controller* device)
|
||||||
if (!private->enabled)
|
if (!private->enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
EnterCriticalSection(&private->crit);
|
|
||||||
if (!HidD_GetInputReport(private->device, target_report, private->report_length))
|
if (!HidD_GetInputReport(private->device, target_report, private->report_length))
|
||||||
{
|
{
|
||||||
if (GetLastError() == ERROR_ACCESS_DENIED || GetLastError() == ERROR_INVALID_HANDLE)
|
if (GetLastError() == ERROR_ACCESS_DENIED || GetLastError() == ERROR_INVALID_HANDLE)
|
||||||
|
{
|
||||||
|
EnterCriticalSection(&xinput_crit);
|
||||||
remove_gamepad(device);
|
remove_gamepad(device);
|
||||||
|
LeaveCriticalSection(&xinput_crit);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ERR("Failed to get Input Report (%x)\n", GetLastError());
|
ERR("Failed to get Input Report (%x)\n", GetLastError());
|
||||||
LeaveCriticalSection(&private->crit);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (memcmp(report, target_report, private->report_length) == 0)
|
if (memcmp(report, target_report, private->report_length) != 0)
|
||||||
{
|
{
|
||||||
LeaveCriticalSection(&private->crit);
|
private->current_report = (private->current_report+1)%2;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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++;
|
device->state.Gamepad.wButtons = 0;
|
||||||
button_length = ARRAY_SIZE(buttons);
|
for (i = 0; i < button_length; i++)
|
||||||
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])
|
|
||||||
{
|
{
|
||||||
case 1: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_A; break;
|
switch (buttons[i])
|
||||||
case 2: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_B; break;
|
{
|
||||||
case 3: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_X; break;
|
case 1: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_A; break;
|
||||||
case 4: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_Y; break;
|
case 2: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_B; break;
|
||||||
case 5: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_SHOULDER; break;
|
case 3: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_X; break;
|
||||||
case 6: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_SHOULDER; break;
|
case 4: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_Y; break;
|
||||||
case 7: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_BACK; break;
|
case 5: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_SHOULDER; break;
|
||||||
case 8: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_START; break;
|
case 6: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_SHOULDER; break;
|
||||||
case 9: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; break;
|
case 7: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_BACK; break;
|
||||||
case 10: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB; break;
|
case 8: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_START; break;
|
||||||
case 11: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; 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_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_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_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_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_HATSWITCH, &hat_value,
|
memcpy(state, &device->state, sizeof(*state));
|
||||||
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_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_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_Z, &value,
|
|
||||||
private->ppd, target_report, private->report_length) == HIDP_STATUS_SUCCESS)
|
|
||||||
device->state.Gamepad.bLeftTrigger = scale_byte(value, &private->ltrigger);
|
|
||||||
LeaveCriticalSection(&private->crit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD HID_set_state(xinput_controller* device, XINPUT_VIBRATION* 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);
|
report.right = (BYTE)(state->wRightMotorSpeed / 256);
|
||||||
memset(&report.pad2, 0, sizeof(report.pad2));
|
memset(&report.pad2, 0, sizeof(report.pad2));
|
||||||
|
|
||||||
EnterCriticalSection(&private->crit);
|
|
||||||
rc = HidD_SetOutputReport(private->device, &report, sizeof(report));
|
rc = HidD_SetOutputReport(private->device, &report, sizeof(report));
|
||||||
LeaveCriticalSection(&private->crit);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
return GetLastError();
|
return GetLastError();
|
||||||
|
@ -492,7 +489,6 @@ void HID_enable(xinput_controller* device, BOOL enable)
|
||||||
|
|
||||||
if (device->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED)
|
if (device->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED)
|
||||||
{
|
{
|
||||||
EnterCriticalSection(&private->crit);
|
|
||||||
if (private->enabled && !enable)
|
if (private->enabled && !enable)
|
||||||
{
|
{
|
||||||
XINPUT_VIBRATION state;
|
XINPUT_VIBRATION state;
|
||||||
|
@ -504,7 +500,6 @@ void HID_enable(xinput_controller* device, BOOL enable)
|
||||||
{
|
{
|
||||||
HID_set_state(device, &device->vibration);
|
HID_set_state(device, &device->vibration);
|
||||||
}
|
}
|
||||||
LeaveCriticalSection(&private->crit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private->enabled = enable;
|
private->enabled = enable;
|
||||||
|
|
|
@ -34,7 +34,66 @@
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(xinput);
|
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)
|
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 ++)
|
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);
|
HID_enable(&controllers[index], enable);
|
||||||
|
unlock_device(&controllers[index]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD WINAPI DECLSPEC_HOTPATCH XInputSetState(DWORD index, XINPUT_VIBRATION* vibration)
|
DWORD WINAPI DECLSPEC_HOTPATCH XInputSetState(DWORD index, XINPUT_VIBRATION* vibration)
|
||||||
{
|
{
|
||||||
|
DWORD ret;
|
||||||
|
|
||||||
TRACE("(index %u, vibration %p)\n", index, vibration);
|
TRACE("(index %u, vibration %p)\n", index, vibration);
|
||||||
|
|
||||||
HID_find_gamepads(controllers);
|
HID_find_gamepads(controllers);
|
||||||
|
|
||||||
if (index >= XUSER_MAX_COUNT)
|
if (index >= XUSER_MAX_COUNT)
|
||||||
return ERROR_BAD_ARGUMENTS;
|
return ERROR_BAD_ARGUMENTS;
|
||||||
if (!controllers[index].connected)
|
if (!verify_and_lock_device(&controllers[index]))
|
||||||
return ERROR_DEVICE_NOT_CONNECTED;
|
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
|
/* 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)
|
if (index >= XUSER_MAX_COUNT)
|
||||||
return ERROR_BAD_ARGUMENTS;
|
return ERROR_BAD_ARGUMENTS;
|
||||||
if (!controllers[index].connected)
|
if (!verify_and_lock_device(&controllers[index]))
|
||||||
return ERROR_DEVICE_NOT_CONNECTED;
|
return ERROR_DEVICE_NOT_CONNECTED;
|
||||||
|
|
||||||
HID_update_state(&controllers[index]);
|
HID_update_state(&controllers[index], state);
|
||||||
memcpy(state, &controllers[index].state, sizeof(XINPUT_STATE));
|
|
||||||
|
if (!controllers[index].connected)
|
||||||
|
{
|
||||||
|
/* update_state may have disconnected the controller */
|
||||||
|
unlock_device(&controllers[index]);
|
||||||
|
return ERROR_DEVICE_NOT_CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_device(&controllers[index]);
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -151,13 +225,20 @@ DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilities(DWORD index, DWORD flags, X
|
||||||
|
|
||||||
if (index >= XUSER_MAX_COUNT)
|
if (index >= XUSER_MAX_COUNT)
|
||||||
return ERROR_BAD_ARGUMENTS;
|
return ERROR_BAD_ARGUMENTS;
|
||||||
if (!controllers[index].connected)
|
|
||||||
return ERROR_DEVICE_NOT_CONNECTED;
|
if (!verify_and_lock_device(&controllers[index]))
|
||||||
if (flags & XINPUT_FLAG_GAMEPAD && controllers[index].caps.SubType != XINPUT_DEVSUBTYPE_GAMEPAD)
|
|
||||||
return ERROR_DEVICE_NOT_CONNECTED;
|
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));
|
memcpy(capabilities, &controllers[index].caps, sizeof(*capabilities));
|
||||||
|
|
||||||
|
unlock_device(&controllers[index]);
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,16 +19,19 @@
|
||||||
|
|
||||||
typedef struct _xinput_controller
|
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;
|
XINPUT_CAPABILITIES caps;
|
||||||
void *platform_private;
|
void *platform_private;
|
||||||
XINPUT_STATE state;
|
XINPUT_STATE state;
|
||||||
XINPUT_VIBRATION vibration;
|
XINPUT_VIBRATION vibration;
|
||||||
} xinput_controller;
|
} xinput_controller;
|
||||||
|
|
||||||
|
CRITICAL_SECTION xinput_crit;
|
||||||
|
xinput_controller controllers[XUSER_MAX_COUNT];
|
||||||
|
|
||||||
void HID_find_gamepads(xinput_controller *devices) DECLSPEC_HIDDEN;
|
void HID_find_gamepads(xinput_controller *devices) DECLSPEC_HIDDEN;
|
||||||
void HID_destroy_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;
|
DWORD HID_set_state(xinput_controller* device, XINPUT_VIBRATION* state) DECLSPEC_HIDDEN;
|
||||||
void HID_enable(xinput_controller* device, BOOL enable) DECLSPEC_HIDDEN;
|
void HID_enable(xinput_controller* device, BOOL enable) DECLSPEC_HIDDEN;
|
||||||
|
|
Loading…
Reference in New Issue