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
26 #include "wine/debug.h"
39 #include "ddk/hidsdi.h"
44 #include "xinput_private.h"
47 WINE_DEFAULT_DEBUG_CHANNEL(xinput
);
49 #define XINPUT_GAMEPAD_GUIDE 0x0400
58 struct hid_platform_private
{
59 PHIDP_PREPARSED_DATA ppd
;
66 char *input_report_buf
[2];
67 char *output_report_buf
;
69 struct axis_info lx
, ly
, ltrigger
, rx
, ry
, rtrigger
;
72 static DWORD last_check
= 0;
74 static void MarkUsage(struct hid_platform_private
*private, WORD usage
, LONG min
, LONG max
, USHORT bits
)
76 struct axis_info info
= {min
, max
-min
, bits
};
80 case HID_USAGE_GENERIC_X
: private->lx
= info
; break;
81 case HID_USAGE_GENERIC_Y
: private->ly
= info
; break;
82 case HID_USAGE_GENERIC_Z
: private->ltrigger
= info
; break;
83 case HID_USAGE_GENERIC_RX
: private->rx
= info
; break;
84 case HID_USAGE_GENERIC_RY
: private->ry
= info
; break;
85 case HID_USAGE_GENERIC_RZ
: private->rtrigger
= info
; break;
89 static BOOL
VerifyGamepad(PHIDP_PREPARSED_DATA ppd
, XINPUT_CAPABILITIES
*xinput_caps
, struct hid_platform_private
*private)
91 HIDP_BUTTON_CAPS
*button_caps
;
92 HIDP_VALUE_CAPS
*value_caps
;
99 memset(xinput_caps
, 0, sizeof(XINPUT_CAPABILITIES
));
101 if (!(button_caps
= malloc(sizeof(*button_caps
) * private->caps
.NumberInputButtonCaps
))) return FALSE
;
102 status
= HidP_GetButtonCaps(HidP_Input
, button_caps
, &private->caps
.NumberInputButtonCaps
, ppd
);
103 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetButtonCaps returned %#x\n", status
);
104 else for (i
= 0; i
< private->caps
.NumberInputButtonCaps
; i
++)
106 if (button_caps
[i
].UsagePage
!= HID_USAGE_PAGE_BUTTON
)
108 if (button_caps
[i
].IsRange
)
109 button_count
= max(button_count
, button_caps
[i
].Range
.UsageMax
);
111 button_count
= max(button_count
, button_caps
[i
].NotRange
.Usage
);
114 if (button_count
< 11)
115 WARN("Too few buttons, continuing anyway\n");
116 xinput_caps
->Gamepad
.wButtons
= 0xffff;
118 if (!(value_caps
= malloc(sizeof(*value_caps
) * private->caps
.NumberInputValueCaps
))) return FALSE
;
119 status
= HidP_GetValueCaps(HidP_Input
, value_caps
, &private->caps
.NumberInputValueCaps
, ppd
);
120 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetValueCaps returned %#x\n", status
);
121 else for (i
= 0; i
< private->caps
.NumberInputValueCaps
; i
++)
123 if (value_caps
[i
].UsagePage
!= HID_USAGE_PAGE_GENERIC
)
125 if (value_caps
[i
].IsRange
)
128 for (u
= value_caps
[i
].Range
.UsageMin
; u
<=value_caps
[i
].Range
.UsageMax
; u
++)
129 MarkUsage(private, u
, value_caps
[i
].LogicalMin
, value_caps
[i
].LogicalMax
, value_caps
[i
].BitSize
);
132 MarkUsage(private, value_caps
[i
].NotRange
.Usage
, value_caps
[i
].LogicalMin
, value_caps
[i
].LogicalMax
, value_caps
[i
].BitSize
);
136 if (private->ltrigger
.bits
)
137 xinput_caps
->Gamepad
.bLeftTrigger
= (1u << (sizeof(xinput_caps
->Gamepad
.bLeftTrigger
) + 1)) - 1;
139 WARN("Missing axis LeftTrigger\n");
140 if (private->rtrigger
.bits
)
141 xinput_caps
->Gamepad
.bRightTrigger
= (1u << (sizeof(xinput_caps
->Gamepad
.bRightTrigger
) + 1)) - 1;
143 WARN("Missing axis RightTrigger\n");
144 if (private->lx
.bits
)
145 xinput_caps
->Gamepad
.sThumbLX
= (1u << (sizeof(xinput_caps
->Gamepad
.sThumbLX
) + 1)) - 1;
147 WARN("Missing axis ThumbLX\n");
148 if (private->ly
.bits
)
149 xinput_caps
->Gamepad
.sThumbLY
= (1u << (sizeof(xinput_caps
->Gamepad
.sThumbLY
) + 1)) - 1;
151 WARN("Missing axis ThumbLY\n");
152 if (private->rx
.bits
)
153 xinput_caps
->Gamepad
.sThumbRX
= (1u << (sizeof(xinput_caps
->Gamepad
.sThumbRX
) + 1)) - 1;
155 WARN("Missing axis ThumbRX\n");
156 if (private->ry
.bits
)
157 xinput_caps
->Gamepad
.sThumbRY
= (1u << (sizeof(xinput_caps
->Gamepad
.sThumbRY
) + 1)) - 1;
159 WARN("Missing axis ThumbRY\n");
161 xinput_caps
->Type
= XINPUT_DEVTYPE_GAMEPAD
;
162 xinput_caps
->SubType
= XINPUT_DEVSUBTYPE_GAMEPAD
;
164 if (private->caps
.NumberOutputValueCaps
> 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;
179 if (!(private = calloc(1, sizeof(struct hid_platform_private
)))) return FALSE
;
180 private->caps
= *caps
;
181 if (!VerifyGamepad(ppd
, &controller
->caps
, private)) goto failed
;
183 TRACE("Found gamepad %s\n", debugstr_w(device_path
));
186 private->device
= device
;
187 if (!(private->input_report_buf
[0] = calloc(1, private->caps
.InputReportByteLength
))) goto failed
;
188 if (!(private->input_report_buf
[1] = calloc(1, private->caps
.InputReportByteLength
))) goto failed
;
189 if (!(private->output_report_buf
= calloc(1, private->caps
.OutputReportByteLength
))) goto failed
;
190 size
= (lstrlenW(device_path
) + 1) * sizeof(WCHAR
);
191 if (!(private->device_path
= malloc(size
))) goto failed
;
192 memcpy(private->device_path
, device_path
, size
);
193 private->enabled
= TRUE
;
195 memset(&controller
->state
, 0, sizeof(controller
->state
));
196 memset(&controller
->vibration
, 0, sizeof(controller
->vibration
));
198 controller
->platform_private
= private;
202 free(private->device_path
);
203 free(private->input_report_buf
[0]);
204 free(private->input_report_buf
[1]);
205 free(private->output_report_buf
);
210 void HID_find_gamepads(xinput_controller
*devices
)
212 HDEVINFO device_info_set
;
214 SP_DEVICE_INTERFACE_DATA interface_data
;
215 SP_DEVICE_INTERFACE_DETAIL_DATA_W
*data
;
216 PHIDP_PREPARSED_DATA ppd
;
217 DWORD detail_size
= MAX_PATH
* sizeof(WCHAR
);
222 int i
, open_device_idx
;
224 idx
= GetTickCount();
225 if ((idx
- last_check
) < 2000)
228 EnterCriticalSection(&xinput_crit
);
230 if ((idx
- last_check
) < 2000)
232 LeaveCriticalSection(&xinput_crit
);
237 HidD_GetHidGuid(&hid_guid
);
239 device_info_set
= SetupDiGetClassDevsW(&hid_guid
, NULL
, NULL
, DIGCF_DEVICEINTERFACE
| DIGCF_PRESENT
);
241 if (!(data
= malloc(sizeof(*data
) + detail_size
))) goto done
;
242 data
->cbSize
= sizeof(*data
);
244 ZeroMemory(&interface_data
, sizeof(interface_data
));
245 interface_data
.cbSize
= sizeof(interface_data
);
248 while (SetupDiEnumDeviceInterfaces(device_info_set
, NULL
, &hid_guid
, idx
++,
251 if (!SetupDiGetDeviceInterfaceDetailW(device_info_set
,
252 &interface_data
, data
, sizeof(*data
) + detail_size
, NULL
, NULL
))
255 if (!wcsstr(data
->DevicePath
, L
"IG_"))
258 open_device_idx
= -1;
259 for (i
= 0; i
< XUSER_MAX_COUNT
; i
++)
261 struct hid_platform_private
*private = devices
[i
].platform_private
;
262 if (devices
[i
].platform_private
)
264 if (!wcscmp(data
->DevicePath
, private->device_path
))
267 else if(open_device_idx
< 0)
270 if (i
!= XUSER_MAX_COUNT
)
271 /* this device is already opened */
273 if (open_device_idx
< 0)
274 /* no open device slots */
276 device
= CreateFileW(data
->DevicePath
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, 0 );
277 if (device
== INVALID_HANDLE_VALUE
)
281 if (!HidD_GetPreparsedData(device
, &ppd
))
282 WARN("ignoring HID device, HidD_GetPreparsedData failed with error %u\n", GetLastError());
283 else if ((status
= HidP_GetCaps(ppd
, &caps
)) != HIDP_STATUS_SUCCESS
)
284 WARN("ignoring HID device, HidP_GetCaps returned %#x\n", status
);
285 else if (caps
.UsagePage
!= HID_USAGE_PAGE_GENERIC
)
286 WARN("ignoring HID device, unsupported usage page %04x\n", caps
.UsagePage
);
287 else if (caps
.Usage
!= HID_USAGE_GENERIC_GAMEPAD
&& caps
.Usage
!= HID_USAGE_GENERIC_JOYSTICK
&& caps
.Usage
!= HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER
)
288 WARN("ignoring HID device, unsupported usage %04x:%04x\n", caps
.UsagePage
, caps
.Usage
);
289 else if (!init_controller(&devices
[open_device_idx
], ppd
, &caps
, device
, data
->DevicePath
))
290 WARN("ignoring HID device, failed to initialize\n");
295 HidD_FreePreparsedData(ppd
);
300 SetupDiDestroyDeviceInfoList(device_info_set
);
301 LeaveCriticalSection(&xinput_crit
);
304 static void remove_gamepad(xinput_controller
*device
)
306 EnterCriticalSection(&device
->crit
);
308 if (device
->platform_private
)
310 struct hid_platform_private
*private = device
->platform_private
;
312 device
->platform_private
= NULL
;
314 CloseHandle(private->device
);
315 free(private->input_report_buf
[0]);
316 free(private->input_report_buf
[1]);
317 free(private->output_report_buf
);
318 free(private->device_path
);
319 HidD_FreePreparsedData(private->ppd
);
323 LeaveCriticalSection(&device
->crit
);
326 void HID_destroy_gamepads(xinput_controller
*devices
)
329 for (i
= 0; i
< XUSER_MAX_COUNT
; i
++)
330 remove_gamepad(&devices
[i
]);
333 static SHORT
scale_short(LONG value
, const struct axis_info
*axis
)
335 return ((((ULONGLONG
)(value
- axis
->min
)) * 0xffff) / axis
->range
) - 32768;
338 static BYTE
scale_byte(LONG value
, const struct axis_info
*axis
)
340 return (((ULONGLONG
)(value
- axis
->min
)) * 0xff) / axis
->range
;
343 void HID_update_state(xinput_controller
*device
, XINPUT_STATE
*state
)
345 struct hid_platform_private
*private = device
->platform_private
;
347 char **report_buf
= private->input_report_buf
, *tmp
;
348 ULONG report_len
= private->caps
.InputReportByteLength
;
352 ULONG button_length
, hat_value
;
355 if (!private->enabled
)
358 if (!HidD_GetInputReport(private->device
, report_buf
[0], report_len
))
360 if (GetLastError() == ERROR_ACCESS_DENIED
|| GetLastError() == ERROR_INVALID_HANDLE
)
362 EnterCriticalSection(&xinput_crit
);
363 remove_gamepad(device
);
364 LeaveCriticalSection(&xinput_crit
);
366 else ERR("Failed to get input report, HidD_GetInputReport failed with error %u\n", GetLastError());
370 if (memcmp(report_buf
[0], report_buf
[1], report_len
) != 0)
372 device
->state
.dwPacketNumber
++;
373 button_length
= ARRAY_SIZE(buttons
);
374 status
= HidP_GetUsages(HidP_Input
, HID_USAGE_PAGE_BUTTON
, 0, buttons
, &button_length
, private->ppd
, report_buf
[0], report_len
);
375 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetUsages HID_USAGE_PAGE_BUTTON returned %#x\n", status
);
377 device
->state
.Gamepad
.wButtons
= 0;
378 for (i
= 0; i
< button_length
; i
++)
382 case 1: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_A
; break;
383 case 2: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_B
; break;
384 case 3: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_X
; break;
385 case 4: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_Y
; break;
386 case 5: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_LEFT_SHOULDER
; break;
387 case 6: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_RIGHT_SHOULDER
; break;
388 case 7: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_BACK
; break;
389 case 8: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_START
; break;
390 case 9: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_LEFT_THUMB
; break;
391 case 10: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_RIGHT_THUMB
; break;
392 case 11: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_GUIDE
; break;
396 status
= HidP_GetUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_HATSWITCH
, &hat_value
, private->ppd
, report_buf
[0], report_len
);
397 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_HATSWITCH returned %#x\n", status
);
407 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_UP
;
410 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_UP
| XINPUT_GAMEPAD_DPAD_RIGHT
;
413 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_RIGHT
;
416 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_RIGHT
| XINPUT_GAMEPAD_DPAD_DOWN
;
419 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_DOWN
;
422 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_DOWN
| XINPUT_GAMEPAD_DPAD_LEFT
;
425 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_LEFT
;
428 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_LEFT
| XINPUT_GAMEPAD_DPAD_UP
;
433 status
= HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_X
, &value
, private->ppd
, report_buf
[0], report_len
);
434 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_X returned %#x\n", status
);
435 else device
->state
.Gamepad
.sThumbLX
= scale_short(value
, &private->lx
);
437 status
= HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_Y
, &value
, private->ppd
, report_buf
[0], report_len
);
438 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_Y returned %#x\n", status
);
439 else device
->state
.Gamepad
.sThumbLY
= -scale_short(value
, &private->ly
) - 1;
441 status
= HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_RX
, &value
, private->ppd
, report_buf
[0], report_len
);
442 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RX returned %#x\n", status
);
443 else device
->state
.Gamepad
.sThumbRX
= scale_short(value
, &private->rx
);
445 status
= HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_RY
, &value
, private->ppd
, report_buf
[0], report_len
);
446 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RY returned %#x\n", status
);
447 else device
->state
.Gamepad
.sThumbRY
= -scale_short(value
, &private->ry
) - 1;
449 status
= HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_RZ
, &value
, private->ppd
, report_buf
[0], report_len
);
450 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RZ returned %#x\n", status
);
451 else device
->state
.Gamepad
.bRightTrigger
= scale_byte(value
, &private->rtrigger
);
453 status
= HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_Z
, &value
, private->ppd
, report_buf
[0], report_len
);
454 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetScaledUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_Z returned %#x\n", status
);
455 else device
->state
.Gamepad
.bLeftTrigger
= scale_byte(value
, &private->ltrigger
);
459 report_buf
[0] = report_buf
[1];
461 memcpy(state
, &device
->state
, sizeof(*state
));
464 DWORD
HID_set_state(xinput_controller
* device
, XINPUT_VIBRATION
* state
)
466 struct hid_platform_private
*private = device
->platform_private
;
467 char *output_report_buf
= private->output_report_buf
;
468 ULONG output_report_len
= private->caps
.OutputReportByteLength
;
470 if (device
->caps
.Flags
& XINPUT_CAPS_FFB_SUPPORTED
)
472 device
->vibration
.wLeftMotorSpeed
= state
->wLeftMotorSpeed
;
473 device
->vibration
.wRightMotorSpeed
= state
->wRightMotorSpeed
;
475 if (private->enabled
)
477 memset(output_report_buf
, 0, output_report_len
);
478 output_report_buf
[0] = /* report id */ 0;
479 output_report_buf
[1] = 0x8;
480 output_report_buf
[3] = (BYTE
)(state
->wLeftMotorSpeed
/ 256);
481 output_report_buf
[4] = (BYTE
)(state
->wRightMotorSpeed
/ 256);
483 if (!HidD_SetOutputReport(private->device
, output_report_buf
, output_report_len
))
485 WARN("unable to set output report, HidD_SetOutputReport failed with error %u\n", GetLastError());
486 return GetLastError();
489 return ERROR_SUCCESS
;
493 return ERROR_SUCCESS
;
496 void HID_enable(xinput_controller
* device
, BOOL enable
)
498 struct hid_platform_private
*private = device
->platform_private
;
500 if (device
->caps
.Flags
& XINPUT_CAPS_FFB_SUPPORTED
)
502 if (private->enabled
&& !enable
)
504 XINPUT_VIBRATION state
;
505 state
.wLeftMotorSpeed
= 0;
506 state
.wRightMotorSpeed
= 0;
507 HID_set_state(device
, &state
);
509 else if (!private->enabled
&& enable
)
511 HID_set_state(device
, &device
->vibration
);
515 private->enabled
= enable
;