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 void build_private(struct hid_platform_private
*private, PHIDP_PREPARSED_DATA ppd
, HIDP_CAPS
*caps
, HANDLE device
, WCHAR
*path
)
178 private->device
= device
;
179 private->report_length
= caps
->InputReportByteLength
+ 1;
180 private->current_report
= 0;
181 private->reports
[0] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, private->report_length
);
182 private->reports
[1] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, private->report_length
);
183 size
= (lstrlenW(path
) + 1) * sizeof(WCHAR
);
184 private->device_path
= HeapAlloc(GetProcessHeap(), 0, size
);
185 memcpy(private->device_path
, path
, size
);
186 private->enabled
= TRUE
;
189 void HID_find_gamepads(xinput_controller
*devices
)
191 HDEVINFO device_info_set
;
193 SP_DEVICE_INTERFACE_DATA interface_data
;
194 SP_DEVICE_INTERFACE_DETAIL_DATA_W
*data
;
195 PHIDP_PREPARSED_DATA ppd
;
196 DWORD detail_size
= MAX_PATH
* sizeof(WCHAR
);
200 int i
, open_device_idx
;
202 idx
= GetTickCount();
203 if ((idx
- last_check
) < 2000)
206 EnterCriticalSection(&xinput_crit
);
208 if ((idx
- last_check
) < 2000)
210 LeaveCriticalSection(&xinput_crit
);
215 HidD_GetHidGuid(&hid_guid
);
217 device_info_set
= SetupDiGetClassDevsW(&hid_guid
, NULL
, NULL
, DIGCF_DEVICEINTERFACE
| DIGCF_PRESENT
);
219 data
= HeapAlloc(GetProcessHeap(), 0 , sizeof(*data
) + detail_size
);
220 data
->cbSize
= sizeof(*data
);
222 ZeroMemory(&interface_data
, sizeof(interface_data
));
223 interface_data
.cbSize
= sizeof(interface_data
);
226 while (SetupDiEnumDeviceInterfaces(device_info_set
, NULL
, &hid_guid
, idx
++,
229 static const WCHAR ig
[] = {'I','G','_',0};
230 if (!SetupDiGetDeviceInterfaceDetailW(device_info_set
,
231 &interface_data
, data
, sizeof(*data
) + detail_size
, NULL
, NULL
))
234 if (!wcsstr(data
->DevicePath
, ig
))
237 open_device_idx
= -1;
238 for (i
= 0; i
< XUSER_MAX_COUNT
; i
++)
240 struct hid_platform_private
*private = devices
[i
].platform_private
;
241 if (devices
[i
].connected
)
243 if (!wcscmp(data
->DevicePath
, private->device_path
))
246 else if(open_device_idx
< 0)
249 if (i
!= XUSER_MAX_COUNT
)
250 /* this device is already opened */
252 if (open_device_idx
< 0)
253 /* no open device slots */
255 device
= CreateFileW(data
->DevicePath
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_EXISTING
, 0, 0 );
256 if (device
== INVALID_HANDLE_VALUE
)
259 HidD_GetPreparsedData(device
, &ppd
);
260 HidP_GetCaps(ppd
, &Caps
);
261 if (Caps
.UsagePage
== HID_USAGE_PAGE_GENERIC
&&
262 (Caps
.Usage
== HID_USAGE_GENERIC_GAMEPAD
||
263 Caps
.Usage
== HID_USAGE_GENERIC_JOYSTICK
||
264 Caps
.Usage
== 0x8 /* Multi-axis Controller */))
266 struct hid_platform_private
*private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(struct hid_platform_private
));
267 if (VerifyGamepad(ppd
, &devices
[open_device_idx
].caps
, private, &Caps
))
269 TRACE("Found gamepad %i %s\n", open_device_idx
, debugstr_w(data
->DevicePath
));
270 build_private(private, ppd
, &Caps
, device
, data
->DevicePath
);
271 devices
[open_device_idx
].platform_private
= private;
272 devices
[open_device_idx
].connected
= TRUE
;
277 HidD_FreePreparsedData(ppd
);
278 HeapFree(GetProcessHeap(), 0, private);
284 HidD_FreePreparsedData(ppd
);
287 HeapFree(GetProcessHeap(), 0, data
);
288 SetupDiDestroyDeviceInfoList(device_info_set
);
289 LeaveCriticalSection(&xinput_crit
);
292 static void remove_gamepad(xinput_controller
*device
)
294 EnterCriticalSection(&device
->crit
);
296 if (device
->connected
)
298 struct hid_platform_private
*private = device
->platform_private
;
300 device
->connected
= FALSE
;
302 CloseHandle(private->device
);
303 HeapFree(GetProcessHeap(), 0, private->reports
[0]);
304 HeapFree(GetProcessHeap(), 0, private->reports
[1]);
305 HeapFree(GetProcessHeap(), 0, private->device_path
);
306 HidD_FreePreparsedData(private->ppd
);
307 device
->platform_private
= NULL
;
308 HeapFree(GetProcessHeap(), 0, private);
311 LeaveCriticalSection(&device
->crit
);
314 void HID_destroy_gamepads(xinput_controller
*devices
)
317 for (i
= 0; i
< XUSER_MAX_COUNT
; i
++)
318 remove_gamepad(&devices
[i
]);
321 static SHORT
scale_short(LONG value
, const struct axis_info
*axis
)
323 return ((((ULONGLONG
)(value
- axis
->min
)) * 0xffff) / axis
->range
) - 32768;
326 static BYTE
scale_byte(LONG value
, const struct axis_info
*axis
)
328 return (((ULONGLONG
)(value
- axis
->min
)) * 0xff) / axis
->range
;
331 void HID_update_state(xinput_controller
*device
, XINPUT_STATE
*state
)
333 struct hid_platform_private
*private = device
->platform_private
;
335 CHAR
*report
= private->reports
[(private->current_report
)%2];
336 CHAR
*target_report
= private->reports
[(private->current_report
+1)%2];
339 ULONG button_length
, hat_value
;
342 if (!private->enabled
)
345 if (!HidD_GetInputReport(private->device
, target_report
, private->report_length
))
347 if (GetLastError() == ERROR_ACCESS_DENIED
|| GetLastError() == ERROR_INVALID_HANDLE
)
349 EnterCriticalSection(&xinput_crit
);
350 remove_gamepad(device
);
351 LeaveCriticalSection(&xinput_crit
);
354 ERR("Failed to get Input Report (%x)\n", GetLastError());
357 if (memcmp(report
, target_report
, private->report_length
) != 0)
359 private->current_report
= (private->current_report
+1)%2;
361 device
->state
.dwPacketNumber
++;
362 button_length
= ARRAY_SIZE(buttons
);
363 HidP_GetUsages(HidP_Input
, HID_USAGE_PAGE_BUTTON
, 0, buttons
, &button_length
, private->ppd
, target_report
, private->report_length
);
365 device
->state
.Gamepad
.wButtons
= 0;
366 for (i
= 0; i
< button_length
; i
++)
370 case 1: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_A
; break;
371 case 2: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_B
; break;
372 case 3: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_X
; break;
373 case 4: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_Y
; break;
374 case 5: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_LEFT_SHOULDER
; break;
375 case 6: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_RIGHT_SHOULDER
; break;
376 case 7: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_BACK
; break;
377 case 8: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_START
; break;
378 case 9: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_LEFT_THUMB
; break;
379 case 10: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_RIGHT_THUMB
; break;
380 case 11: device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_GUIDE
; break;
384 if(HidP_GetUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_HATSWITCH
, &hat_value
,
385 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
394 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_UP
;
397 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_UP
| XINPUT_GAMEPAD_DPAD_RIGHT
;
400 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_RIGHT
;
403 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_RIGHT
| XINPUT_GAMEPAD_DPAD_DOWN
;
406 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_DOWN
;
409 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_DOWN
| XINPUT_GAMEPAD_DPAD_LEFT
;
412 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_LEFT
;
415 device
->state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_LEFT
| XINPUT_GAMEPAD_DPAD_UP
;
420 if(HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_X
, &value
,
421 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
422 device
->state
.Gamepad
.sThumbLX
= scale_short(value
, &private->lx
);
424 if(HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_Y
, &value
,
425 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
426 device
->state
.Gamepad
.sThumbLY
= -scale_short(value
, &private->ly
) - 1;
428 if(HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_RX
, &value
,
429 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
430 device
->state
.Gamepad
.sThumbRX
= scale_short(value
, &private->rx
);
432 if(HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_RY
, &value
,
433 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
434 device
->state
.Gamepad
.sThumbRY
= -scale_short(value
, &private->ry
) - 1;
436 if(HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_RZ
, &value
,
437 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
438 device
->state
.Gamepad
.bRightTrigger
= scale_byte(value
, &private->rtrigger
);
440 if(HidP_GetScaledUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_Z
, &value
,
441 private->ppd
, target_report
, private->report_length
) == HIDP_STATUS_SUCCESS
)
442 device
->state
.Gamepad
.bLeftTrigger
= scale_byte(value
, &private->ltrigger
);
445 memcpy(state
, &device
->state
, sizeof(*state
));
448 DWORD
HID_set_state(xinput_controller
* device
, XINPUT_VIBRATION
* state
)
450 struct hid_platform_private
*private = device
->platform_private
;
460 if (device
->caps
.Flags
& XINPUT_CAPS_FFB_SUPPORTED
)
462 device
->vibration
.wLeftMotorSpeed
= state
->wLeftMotorSpeed
;
463 device
->vibration
.wRightMotorSpeed
= state
->wRightMotorSpeed
;
465 if (private->enabled
)
470 report
.pad1
[0] = 0x8;
471 report
.pad1
[1] = 0x0;
472 report
.left
= (BYTE
)(state
->wLeftMotorSpeed
/ 256);
473 report
.right
= (BYTE
)(state
->wRightMotorSpeed
/ 256);
474 memset(&report
.pad2
, 0, sizeof(report
.pad2
));
476 rc
= HidD_SetOutputReport(private->device
, &report
, sizeof(report
));
478 return ERROR_SUCCESS
;
479 return GetLastError();
483 return ERROR_SUCCESS
;
486 void HID_enable(xinput_controller
* device
, BOOL enable
)
488 struct hid_platform_private
*private = device
->platform_private
;
490 if (device
->caps
.Flags
& XINPUT_CAPS_FFB_SUPPORTED
)
492 if (private->enabled
&& !enable
)
494 XINPUT_VIBRATION state
;
495 state
.wLeftMotorSpeed
= 0;
496 state
.wRightMotorSpeed
= 0;
497 HID_set_state(device
, &state
);
499 else if (!private->enabled
&& enable
)
501 HID_set_state(device
, &device
->vibration
);
505 private->enabled
= enable
;