2 * The Wine project - Xinput Joystick HID interface
3 * Copyright 2018 Aric Stewart
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "wine/debug.h"
38 #include "ddk/hidsdi.h"
43 #include "xinput_private.h"
46 WINE_DEFAULT_DEBUG_CHANNEL(xinput
);
48 #define XINPUT_GAMEPAD_GUIDE 0x0400
57 struct hid_platform_private
{
58 PHIDP_PREPARSED_DATA ppd
;
67 struct axis_info lx
, ly
, ltrigger
, rx
, ry
, rtrigger
;
70 static DWORD last_check
= 0;
72 static void MarkUsage(struct hid_platform_private
*private, WORD usage
, LONG min
, LONG max
, USHORT bits
)
74 struct axis_info info
= {min
, max
-min
, bits
};
78 case HID_USAGE_GENERIC_X
: private->lx
= info
; break;
79 case HID_USAGE_GENERIC_Y
: private->ly
= info
; break;
80 case HID_USAGE_GENERIC_Z
: private->ltrigger
= info
; break;
81 case HID_USAGE_GENERIC_RX
: private->rx
= info
; break;
82 case HID_USAGE_GENERIC_RY
: private->ry
= info
; break;
83 case HID_USAGE_GENERIC_RZ
: private->rtrigger
= info
; break;
87 static BOOL
VerifyGamepad(PHIDP_PREPARSED_DATA ppd
, XINPUT_CAPABILITIES
*xinput_caps
, struct hid_platform_private
*private, HIDP_CAPS
*caps
)
89 HIDP_BUTTON_CAPS
*button_caps
;
90 HIDP_VALUE_CAPS
*value_caps
;
94 USHORT button_caps_count
= 0;
95 USHORT value_caps_count
= 0;
98 memset(xinput_caps
, 0, sizeof(XINPUT_CAPABILITIES
));
100 button_caps_count
= caps
->NumberInputButtonCaps
;
101 button_caps
= HeapAlloc(GetProcessHeap(), 0, sizeof(*button_caps
) * button_caps_count
);
102 HidP_GetButtonCaps(HidP_Input
, button_caps
, &button_caps_count
, ppd
);
103 for (i
= 0; i
< button_caps_count
; i
++)
105 if (button_caps
[i
].UsagePage
!= HID_USAGE_PAGE_BUTTON
)
107 if (button_caps
[i
].IsRange
)
108 button_count
= max(button_count
, button_caps
[i
].Range
.UsageMax
);
110 button_count
= max(button_count
, button_caps
[i
].NotRange
.Usage
);
112 HeapFree(GetProcessHeap(), 0, button_caps
);
113 if (button_count
< 11)
114 WARN("Too few buttons, continuing anyway\n");
115 xinput_caps
->Gamepad
.wButtons
= 0xffff;
117 value_caps_count
= caps
->NumberInputValueCaps
;
118 value_caps
= HeapAlloc(GetProcessHeap(), 0, sizeof(*value_caps
) * value_caps_count
);
119 HidP_GetValueCaps(HidP_Input
, value_caps
, &value_caps_count
, ppd
);
120 for (i
= 0; i
< value_caps_count
; i
++)
122 if (value_caps
[i
].UsagePage
!= HID_USAGE_PAGE_GENERIC
)
124 if (value_caps
[i
].IsRange
)
127 for (u
= value_caps
[i
].Range
.UsageMin
; u
<=value_caps
[i
].Range
.UsageMax
; u
++)
128 MarkUsage(private, u
, value_caps
[i
].LogicalMin
, value_caps
[i
].LogicalMax
, value_caps
[i
].BitSize
);
131 MarkUsage(private, value_caps
[i
].NotRange
.Usage
, value_caps
[i
].LogicalMin
, value_caps
[i
].LogicalMax
, value_caps
[i
].BitSize
);
133 HeapFree(GetProcessHeap(), 0, value_caps
);
135 if (private->ltrigger
.bits
)
136 xinput_caps
->Gamepad
.bLeftTrigger
= (1u << (sizeof(xinput_caps
->Gamepad
.bLeftTrigger
) + 1)) - 1;
138 WARN("Missing axis LeftTrigger\n");
139 if (private->rtrigger
.bits
)
140 xinput_caps
->Gamepad
.bRightTrigger
= (1u << (sizeof(xinput_caps
->Gamepad
.bRightTrigger
) + 1)) - 1;
142 WARN("Missing axis RightTrigger\n");
143 if (private->lx
.bits
)
144 xinput_caps
->Gamepad
.sThumbLX
= (1u << (sizeof(xinput_caps
->Gamepad
.sThumbLX
) + 1)) - 1;
146 WARN("Missing axis ThumbLX\n");
147 if (private->ly
.bits
)
148 xinput_caps
->Gamepad
.sThumbLY
= (1u << (sizeof(xinput_caps
->Gamepad
.sThumbLY
) + 1)) - 1;
150 WARN("Missing axis ThumbLY\n");
151 if (private->rx
.bits
)
152 xinput_caps
->Gamepad
.sThumbRX
= (1u << (sizeof(xinput_caps
->Gamepad
.sThumbRX
) + 1)) - 1;
154 WARN("Missing axis ThumbRX\n");
155 if (private->ry
.bits
)
156 xinput_caps
->Gamepad
.sThumbRY
= (1u << (sizeof(xinput_caps
->Gamepad
.sThumbRY
) + 1)) - 1;
158 WARN("Missing axis ThumbRY\n");
160 xinput_caps
->Type
= XINPUT_DEVTYPE_GAMEPAD
;
161 xinput_caps
->SubType
= XINPUT_DEVSUBTYPE_GAMEPAD
;
163 value_caps_count
= caps
->NumberOutputValueCaps
;
164 if (value_caps_count
> 0)
166 xinput_caps
->Flags
|= XINPUT_CAPS_FFB_SUPPORTED
;
167 xinput_caps
->Vibration
.wLeftMotorSpeed
= 255;
168 xinput_caps
->Vibration
.wRightMotorSpeed
= 255;
174 static BOOL
init_controller(xinput_controller
*controller
, PHIDP_PREPARSED_DATA ppd
, HIDP_CAPS
*caps
, HANDLE device
, WCHAR
*device_path
)
177 struct hid_platform_private
*private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(struct hid_platform_private
));
179 if (!VerifyGamepad(ppd
, &controller
->caps
, private, caps
))
181 HeapFree(GetProcessHeap(), 0, private);
185 TRACE("Found gamepad %s\n", debugstr_w(device_path
));
188 private->device
= device
;
189 private->report_length
= caps
->InputReportByteLength
+ 1;
190 private->current_report
= 0;
191 private->reports
[0] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, private->report_length
);
192 private->reports
[1] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, private->report_length
);
193 size
= (lstrlenW(device_path
) + 1) * sizeof(WCHAR
);
194 private->device_path
= HeapAlloc(GetProcessHeap(), 0, size
);
195 memcpy(private->device_path
, device_path
, size
);
196 private->enabled
= TRUE
;
198 memset(&controller
->state
, 0, sizeof(controller
->state
));
199 memset(&controller
->vibration
, 0, sizeof(controller
->vibration
));
201 controller
->platform_private
= private;
206 void HID_find_gamepads(xinput_controller
*devices
)
208 HDEVINFO device_info_set
;
210 SP_DEVICE_INTERFACE_DATA interface_data
;
211 SP_DEVICE_INTERFACE_DETAIL_DATA_W
*data
;
212 PHIDP_PREPARSED_DATA ppd
;
213 DWORD detail_size
= MAX_PATH
* sizeof(WCHAR
);
217 int i
, open_device_idx
;
219 idx
= GetTickCount();
220 if ((idx
- last_check
) < 2000)
223 EnterCriticalSection(&xinput_crit
);
225 if ((idx
- last_check
) < 2000)
227 LeaveCriticalSection(&xinput_crit
);
232 HidD_GetHidGuid(&hid_guid
);
234 device_info_set
= SetupDiGetClassDevsW(&hid_guid
, NULL
, NULL
, DIGCF_DEVICEINTERFACE
| DIGCF_PRESENT
);
236 data
= HeapAlloc(GetProcessHeap(), 0 , sizeof(*data
) + detail_size
);
237 data
->cbSize
= sizeof(*data
);
239 ZeroMemory(&interface_data
, sizeof(interface_data
));
240 interface_data
.cbSize
= sizeof(interface_data
);
243 while (SetupDiEnumDeviceInterfaces(device_info_set
, NULL
, &hid_guid
, idx
++,
246 if (!SetupDiGetDeviceInterfaceDetailW(device_info_set
,
247 &interface_data
, data
, sizeof(*data
) + detail_size
, NULL
, NULL
))
250 if (!wcsstr(data
->DevicePath
, L
"IG_"))
253 open_device_idx
= -1;
254 for (i
= 0; i
< XUSER_MAX_COUNT
; i
++)
256 struct hid_platform_private
*private = devices
[i
].platform_private
;
257 if (devices
[i
].platform_private
)
259 if (!wcscmp(data
->DevicePath
, private->device_path
))
262 else if(open_device_idx
< 0)
265 if (i
!= XUSER_MAX_COUNT
)
266 /* this device is already opened */
268 if (open_device_idx
< 0)
269 /* no open device slots */
271 device
= CreateFileW(data
->DevicePath
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, 0 );
272 if (device
== INVALID_HANDLE_VALUE
)
275 HidD_GetPreparsedData(device
, &ppd
);
276 HidP_GetCaps(ppd
, &Caps
);
277 if (Caps
.UsagePage
== HID_USAGE_PAGE_GENERIC
&&
278 (Caps
.Usage
== HID_USAGE_GENERIC_GAMEPAD
||
279 Caps
.Usage
== HID_USAGE_GENERIC_JOYSTICK
||
280 Caps
.Usage
== 0x8 /* Multi-axis Controller */))
282 if(!init_controller(&devices
[open_device_idx
], ppd
, &Caps
, device
, data
->DevicePath
))
285 HidD_FreePreparsedData(ppd
);
291 HidD_FreePreparsedData(ppd
);
294 HeapFree(GetProcessHeap(), 0, data
);
295 SetupDiDestroyDeviceInfoList(device_info_set
);
296 LeaveCriticalSection(&xinput_crit
);
299 static void remove_gamepad(xinput_controller
*device
)
301 EnterCriticalSection(&device
->crit
);
303 if (device
->platform_private
)
305 struct hid_platform_private
*private = device
->platform_private
;
307 device
->platform_private
= NULL
;
309 CloseHandle(private->device
);
310 HeapFree(GetProcessHeap(), 0, private->reports
[0]);
311 HeapFree(GetProcessHeap(), 0, private->reports
[1]);
312 HeapFree(GetProcessHeap(), 0, private->device_path
);
313 HidD_FreePreparsedData(private->ppd
);
314 HeapFree(GetProcessHeap(), 0, private);
317 LeaveCriticalSection(&device
->crit
);
320 void HID_destroy_gamepads(xinput_controller
*devices
)
323 for (i
= 0; i
< XUSER_MAX_COUNT
; i
++)
324 remove_gamepad(&devices
[i
]);
327 static SHORT
scale_short(LONG value
, const struct axis_info
*axis
)
329 return ((((ULONGLONG
)(value
- axis
->min
)) * 0xffff) / axis
->range
) - 32768;
332 static BYTE
scale_byte(LONG value
, const struct axis_info
*axis
)
334 return (((ULONGLONG
)(value
- axis
->min
)) * 0xff) / axis
->range
;
337 void HID_update_state(xinput_controller
*device
, XINPUT_STATE
*state
)
339 struct hid_platform_private
*private = device
->platform_private
;
341 CHAR
*report
= private->reports
[(private->current_report
)%2];
342 CHAR
*target_report
= private->reports
[(private->current_report
+1)%2];
345 ULONG button_length
, hat_value
;
348 if (!private->enabled
)
351 if (!HidD_GetInputReport(private->device
, target_report
, private->report_length
))
353 if (GetLastError() == ERROR_ACCESS_DENIED
|| GetLastError() == ERROR_INVALID_HANDLE
)
355 EnterCriticalSection(&xinput_crit
);
356 remove_gamepad(device
);
357 LeaveCriticalSection(&xinput_crit
);
360 ERR("Failed to get Input Report (%x)\n", GetLastError());
363 if (memcmp(report
, target_report
, private->report_length
) != 0)
365 private->current_report
= (private->current_report
+1)%2;
367 device
->state
.dwPacketNumber
++;
368 button_length
= ARRAY_SIZE(buttons
);
369 HidP_GetUsages(HidP_Input
, HID_USAGE_PAGE_BUTTON
, 0, buttons
, &button_length
, private->ppd
, target_report
, private->report_length
);
371 device
->state
.Gamepad
.wButtons
= 0;
372 for (i
= 0; i
< button_length
; i
++)
376 case 1: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_A
; break;
377 case 2: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_B
; break;
378 case 3: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_X
; break;
379 case 4: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_Y
; break;
380 case 5: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_LEFT_SHOULDER
; break;
381 case 6: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_RIGHT_SHOULDER
; break;
382 case 7: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_BACK
; break;
383 case 8: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_START
; break;
384 case 9: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_LEFT_THUMB
; break;
385 case 10: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_RIGHT_THUMB
; break;
386 case 11: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_GUIDE
; break;
390 if(HidP_GetUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_HATSWITCH
, &hat_value
,
391 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
400 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_UP
;
403 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_UP
| XINPUT_GAMEPAD_DPAD_RIGHT
;
406 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_RIGHT
;
409 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_RIGHT
| XINPUT_GAMEPAD_DPAD_DOWN
;
412 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_DOWN
;
415 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_DOWN
| XINPUT_GAMEPAD_DPAD_LEFT
;
418 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_LEFT
;
421 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_LEFT
| XINPUT_GAMEPAD_DPAD_UP
;
426 if(HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_X
, &value
,
427 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
428 device
->state
.Gamepad
.sThumbLX
= scale_short(value
, &private->lx
);
430 if(HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_Y
, &value
,
431 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
432 device
->state
.Gamepad
.sThumbLY
= -scale_short(value
, &private->ly
) - 1;
434 if(HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_RX
, &value
,
435 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
436 device
->state
.Gamepad
.sThumbRX
= scale_short(value
, &private->rx
);
438 if(HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_RY
, &value
,
439 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
440 device
->state
.Gamepad
.sThumbRY
= -scale_short(value
, &private->ry
) - 1;
442 if(HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_RZ
, &value
,
443 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
444 device
->state
.Gamepad
.bRightTrigger
= scale_byte(value
, &private->rtrigger
);
446 if(HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_Z
, &value
,
447 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
448 device
->state
.Gamepad
.bLeftTrigger
= scale_byte(value
, &private->ltrigger
);
451 memcpy(state
, &device
->state
, sizeof(*state
));
454 DWORD
HID_set_state(xinput_controller
* device
, XINPUT_VIBRATION
* state
)
456 struct hid_platform_private
*private = device
->platform_private
;
466 if (device
->caps
.Flags
& XINPUT_CAPS_FFB_SUPPORTED
)
468 device
->vibration
.wLeftMotorSpeed
= state
->wLeftMotorSpeed
;
469 device
->vibration
.wRightMotorSpeed
= state
->wRightMotorSpeed
;
471 if (private->enabled
)
476 report
.pad1
[0] = 0x8;
477 report
.pad1
[1] = 0x0;
478 report
.left
= (BYTE
)(state
->wLeftMotorSpeed
/ 256);
479 report
.right
= (BYTE
)(state
->wRightMotorSpeed
/ 256);
480 memset(&report
.pad2
, 0, sizeof(report
.pad2
));
482 rc
= HidD_SetOutputReport(private->device
, &report
, sizeof(report
));
484 return ERROR_SUCCESS
;
485 return GetLastError();
489 return ERROR_SUCCESS
;
492 void HID_enable(xinput_controller
* device
, BOOL enable
)
494 struct hid_platform_private
*private = device
->platform_private
;
496 if (device
->caps
.Flags
& XINPUT_CAPS_FFB_SUPPORTED
)
498 if (private->enabled
&& !enable
)
500 XINPUT_VIBRATION state
;
501 state
.wLeftMotorSpeed
= 0;
502 state
.wRightMotorSpeed
= 0;
503 HID_set_state(device
, &state
);
505 else if (!private->enabled
&& enable
)
507 HID_set_state(device
, &device
->vibration
);
511 private->enabled
= enable
;