2 * The Wine project - Xinput Joystick Library
3 * Copyright 2008 Andrew Fenn
4 * Copyright 2018 Aric Stewart
5 * Copyright 2021 RĂ©mi Bernon for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
40 #include "ddk/hidsdi.h"
45 #include "wine/debug.h"
47 DEFINE_GUID(GUID_DEVINTERFACE_WINEXINPUT
,0x6c53d5fd,0x6480,0x440f,0xb6,0x18,0x47,0x67,0x50,0xc5,0xe1,0xa6);
49 /* Not defined in the headers, used only by XInputGetStateEx */
50 #define XINPUT_GAMEPAD_GUIDE 0x0400
52 WINE_DEFAULT_DEBUG_CHANNEL(xinput
);
54 struct xinput_controller
56 CRITICAL_SECTION crit
;
57 XINPUT_CAPABILITIES caps
;
59 XINPUT_GAMEPAD last_keystroke
;
60 XINPUT_VIBRATION vibration
;
62 WCHAR device_path
[MAX_PATH
];
67 PHIDP_PREPARSED_DATA preparsed
;
69 HIDP_VALUE_CAPS lx_caps
;
70 HIDP_VALUE_CAPS ly_caps
;
71 HIDP_VALUE_CAPS lt_caps
;
72 HIDP_VALUE_CAPS rx_caps
;
73 HIDP_VALUE_CAPS ry_caps
;
74 HIDP_VALUE_CAPS rt_caps
;
79 char *input_report_buf
;
80 char *output_report_buf
;
81 char *feature_report_buf
;
84 HIDP_VALUE_CAPS haptics_rumble_caps
;
85 HIDP_VALUE_CAPS haptics_buzz_caps
;
89 static struct xinput_controller controllers
[XUSER_MAX_COUNT
];
90 static CRITICAL_SECTION_DEBUG controller_critsect_debug
[XUSER_MAX_COUNT
] =
93 0, 0, &controllers
[0].crit
,
94 { &controller_critsect_debug
[0].ProcessLocksList
, &controller_critsect_debug
[0].ProcessLocksList
},
95 0, 0, { (DWORD_PTR
)(__FILE__
": controllers[0].crit") }
98 0, 0, &controllers
[1].crit
,
99 { &controller_critsect_debug
[1].ProcessLocksList
, &controller_critsect_debug
[1].ProcessLocksList
},
100 0, 0, { (DWORD_PTR
)(__FILE__
": controllers[1].crit") }
103 0, 0, &controllers
[2].crit
,
104 { &controller_critsect_debug
[2].ProcessLocksList
, &controller_critsect_debug
[2].ProcessLocksList
},
105 0, 0, { (DWORD_PTR
)(__FILE__
": controllers[2].crit") }
108 0, 0, &controllers
[3].crit
,
109 { &controller_critsect_debug
[3].ProcessLocksList
, &controller_critsect_debug
[3].ProcessLocksList
},
110 0, 0, { (DWORD_PTR
)(__FILE__
": controllers[3].crit") }
114 static struct xinput_controller controllers
[XUSER_MAX_COUNT
] =
116 {{ &controller_critsect_debug
[0], -1, 0, 0, 0, 0 }},
117 {{ &controller_critsect_debug
[1], -1, 0, 0, 0, 0 }},
118 {{ &controller_critsect_debug
[2], -1, 0, 0, 0, 0 }},
119 {{ &controller_critsect_debug
[3], -1, 0, 0, 0, 0 }},
122 static HMODULE xinput_instance
;
123 static HANDLE start_event
;
124 static HANDLE stop_event
;
125 static HANDLE done_event
;
126 static HANDLE update_event
;
128 static BOOL
find_opened_device(const WCHAR
*device_path
, int *free_slot
)
132 *free_slot
= XUSER_MAX_COUNT
;
133 for (i
= XUSER_MAX_COUNT
; i
> 0; i
--)
135 if (!controllers
[i
- 1].device
) *free_slot
= i
- 1;
136 else if (!wcsicmp(device_path
, controllers
[i
- 1].device_path
)) return TRUE
;
141 static void check_value_caps(struct xinput_controller
*controller
, USHORT usage
, HIDP_VALUE_CAPS
*caps
)
145 case HID_USAGE_GENERIC_X
: controller
->hid
.lx_caps
= *caps
; break;
146 case HID_USAGE_GENERIC_Y
: controller
->hid
.ly_caps
= *caps
; break;
147 case HID_USAGE_GENERIC_Z
: controller
->hid
.lt_caps
= *caps
; break;
148 case HID_USAGE_GENERIC_RX
: controller
->hid
.rx_caps
= *caps
; break;
149 case HID_USAGE_GENERIC_RY
: controller
->hid
.ry_caps
= *caps
; break;
150 case HID_USAGE_GENERIC_RZ
: controller
->hid
.rt_caps
= *caps
; break;
154 static void check_waveform_caps(struct xinput_controller
*controller
, HANDLE device
, PHIDP_PREPARSED_DATA preparsed
,
155 HIDP_LINK_COLLECTION_NODE
*collections
, HIDP_VALUE_CAPS
*caps
)
157 USHORT count
, report_len
= controller
->hid
.caps
.FeatureReportByteLength
;
158 char *report_buf
= controller
->hid
.feature_report_buf
;
159 ULONG parent
= caps
->LinkCollection
, waveform
= 0;
160 HIDP_VALUE_CAPS value_caps
;
161 USAGE_AND_PAGE phy_usages
;
164 while (collections
[parent
].LinkUsagePage
!= HID_USAGE_PAGE_HAPTICS
||
165 collections
[parent
].LinkUsage
!= HID_USAGE_HAPTICS_SIMPLE_CONTROLLER
)
166 if (!(parent
= collections
[parent
].Parent
)) break;
168 if (collections
[parent
].LinkUsagePage
!= HID_USAGE_PAGE_HAPTICS
||
169 collections
[parent
].LinkUsage
!= HID_USAGE_HAPTICS_SIMPLE_CONTROLLER
)
171 WARN("Failed to find haptics simple controller collection\n");
174 phy_usages
.UsagePage
= collections
[collections
[parent
].Parent
].LinkUsagePage
;
175 phy_usages
.Usage
= collections
[collections
[parent
].Parent
].LinkUsage
;
177 status
= HidP_InitializeReportForID(HidP_Feature
, caps
->ReportID
, preparsed
, report_buf
, report_len
);
178 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_InitializeReportForID returned %#lx\n", status
);
179 if (!HidD_GetFeature(device
, report_buf
, report_len
))
181 WARN("Failed to get waveform list report, error %lu\n", GetLastError());
185 status
= HidP_GetUsageValue(HidP_Feature
, caps
->UsagePage
, caps
->LinkCollection
, caps
->NotRange
.Usage
,
186 &waveform
, preparsed
, report_buf
, report_len
);
187 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetUsageValue returned %#lx\n", status
);
190 status
= HidP_GetSpecificValueCaps(HidP_Output
, HID_USAGE_PAGE_HAPTICS
, parent
, HID_USAGE_HAPTICS_INTENSITY
,
191 &value_caps
, &count
, preparsed
);
192 if (status
!= HIDP_STATUS_SUCCESS
|| !count
) WARN("Failed to get waveform intensity caps, status %#lx\n", status
);
193 else if (phy_usages
.UsagePage
== HID_USAGE_PAGE_GENERIC
&& phy_usages
.Usage
== HID_USAGE_GENERIC_Z
)
194 TRACE( "Ignoring left rumble caps\n" );
195 else if (phy_usages
.UsagePage
== HID_USAGE_PAGE_GENERIC
&& phy_usages
.Usage
== HID_USAGE_GENERIC_RZ
)
196 TRACE( "Ignoring right rumble caps\n" );
197 else if (waveform
== HID_USAGE_HAPTICS_WAVEFORM_RUMBLE
)
199 TRACE("Found rumble caps, report %u collection %u\n", value_caps
.ReportID
, value_caps
.LinkCollection
);
200 controller
->hid
.haptics_report
= value_caps
.ReportID
;
201 controller
->hid
.haptics_rumble_caps
= value_caps
;
203 else if (waveform
== HID_USAGE_HAPTICS_WAVEFORM_BUZZ
)
205 TRACE("Found buzz caps, report %u collection %u\n", value_caps
.ReportID
, value_caps
.LinkCollection
);
206 controller
->hid
.haptics_report
= value_caps
.ReportID
;
207 controller
->hid
.haptics_buzz_caps
= value_caps
;
209 else FIXME("Unsupported waveform type %#lx\n", waveform
);
212 static BOOL
controller_check_caps(struct xinput_controller
*controller
, HANDLE device
, PHIDP_PREPARSED_DATA preparsed
)
214 USHORT caps_count
= 0, waveform_caps_count
= 0;
215 XINPUT_CAPABILITIES
*caps
= &controller
->caps
;
216 HIDP_LINK_COLLECTION_NODE
*collections
;
217 HIDP_VALUE_CAPS waveform_caps
[8];
218 HIDP_BUTTON_CAPS
*button_caps
;
219 ULONG collections_count
= 0;
220 HIDP_VALUE_CAPS
*value_caps
;
221 int i
, u
, button_count
= 0;
225 memset(caps
, 0, sizeof(XINPUT_CAPABILITIES
));
227 if (!(button_caps
= malloc(sizeof(*button_caps
) * controller
->hid
.caps
.NumberInputButtonCaps
))) return FALSE
;
228 status
= HidP_GetButtonCaps(HidP_Input
, button_caps
, &controller
->hid
.caps
.NumberInputButtonCaps
, preparsed
);
229 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetButtonCaps returned %#lx\n", status
);
230 else for (i
= 0; i
< controller
->hid
.caps
.NumberInputButtonCaps
; i
++)
232 if (button_caps
[i
].UsagePage
!= HID_USAGE_PAGE_BUTTON
)
234 if (button_caps
[i
].IsRange
)
235 button_count
= max(button_count
, button_caps
[i
].Range
.UsageMax
);
237 button_count
= max(button_count
, button_caps
[i
].NotRange
.Usage
);
240 if (button_count
< 11)
241 WARN("Too few buttons, continuing anyway\n");
242 caps
->Gamepad
.wButtons
= 0xffff;
244 if (!(value_caps
= malloc(sizeof(*value_caps
) * controller
->hid
.caps
.NumberInputValueCaps
))) return FALSE
;
245 status
= HidP_GetValueCaps(HidP_Input
, value_caps
, &controller
->hid
.caps
.NumberInputValueCaps
, preparsed
);
246 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetValueCaps returned %#lx\n", status
);
247 else for (i
= 0; i
< controller
->hid
.caps
.NumberInputValueCaps
; i
++)
249 HIDP_VALUE_CAPS
*caps
= value_caps
+ i
;
250 if (caps
->UsagePage
!= HID_USAGE_PAGE_GENERIC
) continue;
251 if (!caps
->IsRange
) check_value_caps(controller
, caps
->NotRange
.Usage
, caps
);
252 else for (u
= caps
->Range
.UsageMin
; u
<=caps
->Range
.UsageMax
; u
++) check_value_caps(controller
, u
, value_caps
+ i
);
256 if (!controller
->hid
.lt_caps
.UsagePage
) WARN("Missing axis LeftTrigger\n");
257 else caps
->Gamepad
.bLeftTrigger
= (1u << (sizeof(caps
->Gamepad
.bLeftTrigger
) + 1)) - 1;
258 if (!controller
->hid
.rt_caps
.UsagePage
) WARN("Missing axis RightTrigger\n");
259 else caps
->Gamepad
.bRightTrigger
= (1u << (sizeof(caps
->Gamepad
.bRightTrigger
) + 1)) - 1;
260 if (!controller
->hid
.lx_caps
.UsagePage
) WARN("Missing axis ThumbLX\n");
261 else caps
->Gamepad
.sThumbLX
= (1u << (sizeof(caps
->Gamepad
.sThumbLX
) + 1)) - 1;
262 if (!controller
->hid
.ly_caps
.UsagePage
) WARN("Missing axis ThumbLY\n");
263 else caps
->Gamepad
.sThumbLY
= (1u << (sizeof(caps
->Gamepad
.sThumbLY
) + 1)) - 1;
264 if (!controller
->hid
.rx_caps
.UsagePage
) WARN("Missing axis ThumbRX\n");
265 else caps
->Gamepad
.sThumbRX
= (1u << (sizeof(caps
->Gamepad
.sThumbRX
) + 1)) - 1;
266 if (!controller
->hid
.ry_caps
.UsagePage
) WARN("Missing axis ThumbRY\n");
267 else caps
->Gamepad
.sThumbRY
= (1u << (sizeof(caps
->Gamepad
.sThumbRY
) + 1)) - 1;
269 caps
->Type
= XINPUT_DEVTYPE_GAMEPAD
;
270 caps
->SubType
= XINPUT_DEVSUBTYPE_GAMEPAD
;
272 collections_count
= controller
->hid
.caps
.NumberLinkCollectionNodes
;
273 if (!(collections
= malloc(sizeof(*collections
) * controller
->hid
.caps
.NumberLinkCollectionNodes
))) return FALSE
;
274 status
= HidP_GetLinkCollectionNodes(collections
, &collections_count
, preparsed
);
275 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetLinkCollectionNodes returned %#lx\n", status
);
276 else for (i
= 0; i
< collections_count
; ++i
)
278 if (collections
[i
].LinkUsagePage
!= HID_USAGE_PAGE_HAPTICS
) continue;
279 if (collections
[i
].LinkUsage
== HID_USAGE_HAPTICS_WAVEFORM_LIST
)
281 caps_count
= ARRAY_SIZE(waveform_caps
) - waveform_caps_count
;
282 value_caps
= waveform_caps
+ waveform_caps_count
;
283 status
= HidP_GetSpecificValueCaps(HidP_Feature
, HID_USAGE_PAGE_ORDINAL
, i
, 0, value_caps
, &caps_count
, preparsed
);
284 if (status
== HIDP_STATUS_SUCCESS
) waveform_caps_count
+= caps_count
;
287 for (i
= 0; i
< waveform_caps_count
; ++i
) check_waveform_caps(controller
, device
, preparsed
, collections
, waveform_caps
+ i
);
290 if (controller
->hid
.haptics_rumble_caps
.UsagePage
||
291 controller
->hid
.haptics_buzz_caps
.UsagePage
)
293 caps
->Flags
|= XINPUT_CAPS_FFB_SUPPORTED
;
294 caps
->Vibration
.wLeftMotorSpeed
= 255;
295 caps
->Vibration
.wRightMotorSpeed
= 255;
301 static DWORD
HID_set_state(struct xinput_controller
*controller
, XINPUT_VIBRATION
*state
)
303 ULONG report_len
= controller
->hid
.caps
.OutputReportByteLength
;
304 PHIDP_PREPARSED_DATA preparsed
= controller
->hid
.preparsed
;
305 char *report_buf
= controller
->hid
.output_report_buf
;
306 BOOL ret
, update_rumble
, update_buzz
;
311 if (!(controller
->caps
.Flags
& XINPUT_CAPS_FFB_SUPPORTED
)) return ERROR_SUCCESS
;
313 update_rumble
= (controller
->vibration
.wLeftMotorSpeed
!= state
->wLeftMotorSpeed
);
314 controller
->vibration
.wLeftMotorSpeed
= state
->wLeftMotorSpeed
;
315 update_buzz
= (controller
->vibration
.wRightMotorSpeed
!= state
->wRightMotorSpeed
);
316 controller
->vibration
.wRightMotorSpeed
= state
->wRightMotorSpeed
;
318 if (!controller
->enabled
) return ERROR_SUCCESS
;
319 if (!update_rumble
&& !update_buzz
) return ERROR_SUCCESS
;
321 report_id
= controller
->hid
.haptics_report
;
322 status
= HidP_InitializeReportForID(HidP_Output
, report_id
, preparsed
, report_buf
, report_len
);
323 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_InitializeReportForID returned %#lx\n", status
);
325 collection
= controller
->hid
.haptics_rumble_caps
.LinkCollection
;
326 status
= HidP_SetUsageValue(HidP_Output
, HID_USAGE_PAGE_HAPTICS
, collection
, HID_USAGE_HAPTICS_INTENSITY
,
327 state
->wLeftMotorSpeed
, preparsed
, report_buf
, report_len
);
328 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_SetUsageValue INTENSITY returned %#lx\n", status
);
330 collection
= controller
->hid
.haptics_buzz_caps
.LinkCollection
;
331 status
= HidP_SetUsageValue(HidP_Output
, HID_USAGE_PAGE_HAPTICS
, collection
, HID_USAGE_HAPTICS_INTENSITY
,
332 state
->wRightMotorSpeed
, preparsed
, report_buf
, report_len
);
333 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_SetUsageValue INTENSITY returned %#lx\n", status
);
335 ret
= HidD_SetOutputReport(controller
->device
, report_buf
, report_len
);
336 if (!ret
) WARN("HidD_SetOutputReport failed with error %lu\n", GetLastError());
339 return ERROR_SUCCESS
;
342 static void controller_destroy(struct xinput_controller
*controller
, BOOL already_removed
);
344 static void controller_enable(struct xinput_controller
*controller
)
346 ULONG report_len
= controller
->hid
.caps
.InputReportByteLength
;
347 char *report_buf
= controller
->hid
.input_report_buf
;
348 XINPUT_VIBRATION state
= controller
->vibration
;
351 if (controller
->enabled
) return;
352 if (controller
->caps
.Flags
& XINPUT_CAPS_FFB_SUPPORTED
) HID_set_state(controller
, &state
);
353 controller
->enabled
= TRUE
;
355 memset(&controller
->hid
.read_ovl
, 0, sizeof(controller
->hid
.read_ovl
));
356 controller
->hid
.read_ovl
.hEvent
= controller
->hid
.read_event
;
357 ret
= ReadFile(controller
->device
, report_buf
, report_len
, NULL
, &controller
->hid
.read_ovl
);
358 if (!ret
&& GetLastError() != ERROR_IO_PENDING
) controller_destroy(controller
, TRUE
);
359 else SetEvent(update_event
);
362 static void controller_disable(struct xinput_controller
*controller
)
364 XINPUT_VIBRATION state
= {0};
366 if (!controller
->enabled
) return;
367 if (controller
->caps
.Flags
& XINPUT_CAPS_FFB_SUPPORTED
) HID_set_state(controller
, &state
);
368 controller
->enabled
= FALSE
;
370 CancelIoEx(controller
->device
, &controller
->hid
.read_ovl
);
371 WaitForSingleObject(controller
->hid
.read_ovl
.hEvent
, INFINITE
);
372 SetEvent(update_event
);
375 static BOOL
controller_init(struct xinput_controller
*controller
, PHIDP_PREPARSED_DATA preparsed
,
376 HIDP_CAPS
*caps
, HANDLE device
, const WCHAR
*device_path
)
380 controller
->hid
.caps
= *caps
;
381 if (!(controller
->hid
.feature_report_buf
= calloc(1, controller
->hid
.caps
.FeatureReportByteLength
))) goto failed
;
382 if (!controller_check_caps(controller
, device
, preparsed
)) goto failed
;
383 if (!(event
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
))) goto failed
;
385 TRACE("Found gamepad %s\n", debugstr_w(device_path
));
387 controller
->hid
.preparsed
= preparsed
;
388 controller
->hid
.read_event
= event
;
389 if (!(controller
->hid
.input_report_buf
= calloc(1, controller
->hid
.caps
.InputReportByteLength
))) goto failed
;
390 if (!(controller
->hid
.output_report_buf
= calloc(1, controller
->hid
.caps
.OutputReportByteLength
))) goto failed
;
392 memset(&controller
->state
, 0, sizeof(controller
->state
));
393 memset(&controller
->vibration
, 0, sizeof(controller
->vibration
));
394 lstrcpynW(controller
->device_path
, device_path
, MAX_PATH
);
395 controller
->enabled
= FALSE
;
397 EnterCriticalSection(&controller
->crit
);
398 controller
->device
= device
;
399 controller_enable(controller
);
400 LeaveCriticalSection(&controller
->crit
);
404 free(controller
->hid
.input_report_buf
);
405 free(controller
->hid
.output_report_buf
);
406 free(controller
->hid
.feature_report_buf
);
407 memset(&controller
->hid
, 0, sizeof(controller
->hid
));
412 static void get_registry_keys(HKEY
*defkey
, HKEY
*appkey
)
414 WCHAR buffer
[MAX_PATH
+ 26], *name
= buffer
, *tmp
;
419 if (RegOpenKeyW(HKEY_CURRENT_USER
, L
"Software\\Wine\\DirectInput\\Joysticks", defkey
))
422 if (!(len
= GetModuleFileNameW(0, buffer
, MAX_PATH
)) || len
>= MAX_PATH
)
425 if (!RegOpenKeyW(HKEY_CURRENT_USER
, L
"Software\\Wine\\AppDefaults", &hkey
))
427 if ((tmp
= wcsrchr(name
, '/'))) name
= tmp
+ 1;
428 if ((tmp
= wcsrchr(name
, '\\'))) name
= tmp
+ 1;
429 wcscat(name
, L
"\\DirectInput\\Joysticks");
430 if (RegOpenKeyW(hkey
, name
, appkey
)) *appkey
= 0;
435 static BOOL
device_is_overridden(HANDLE device
)
437 WCHAR name
[MAX_PATH
], buffer
[MAX_PATH
];
438 DWORD size
= sizeof(buffer
);
439 BOOL disable
= FALSE
;
442 if (!HidD_GetProductString(device
, name
, MAX_PATH
)) return FALSE
;
444 get_registry_keys(&defkey
, &appkey
);
445 if (!defkey
&& !appkey
) return FALSE
;
446 if ((appkey
&& !RegQueryValueExW(appkey
, name
, 0, NULL
, (LPBYTE
)buffer
, &size
)) ||
447 (defkey
&& !RegQueryValueExW(defkey
, name
, 0, NULL
, (LPBYTE
)buffer
, &size
)))
449 if ((disable
= !wcscmp(buffer
, L
"override")))
450 TRACE("Disabling gamepad '%s' based on registry key.\n", debugstr_w(name
));
453 if (appkey
) RegCloseKey(appkey
);
454 if (defkey
) RegCloseKey(defkey
);
458 static BOOL
try_add_device(const WCHAR
*device_path
)
460 SP_DEVICE_INTERFACE_DATA iface
= {sizeof(iface
)};
461 PHIDP_PREPARSED_DATA preparsed
;
467 if (find_opened_device(device_path
, &i
)) return TRUE
; /* already opened */
468 if (i
== XUSER_MAX_COUNT
) return FALSE
; /* no more slots */
470 device
= CreateFileW(device_path
, GENERIC_READ
| GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
471 NULL
, OPEN_EXISTING
, FILE_FLAG_OVERLAPPED
| FILE_FLAG_NO_BUFFERING
, NULL
);
472 if (device
== INVALID_HANDLE_VALUE
) return TRUE
;
475 if (!HidD_GetPreparsedData(device
, &preparsed
))
476 WARN("ignoring HID device, HidD_GetPreparsedData failed with error %lu\n", GetLastError());
477 else if ((status
= HidP_GetCaps(preparsed
, &caps
)) != HIDP_STATUS_SUCCESS
)
478 WARN("ignoring HID device, HidP_GetCaps returned %#lx\n", status
);
479 else if (caps
.UsagePage
!= HID_USAGE_PAGE_GENERIC
)
480 WARN("ignoring HID device, unsupported usage page %04x\n", caps
.UsagePage
);
481 else if (caps
.Usage
!= HID_USAGE_GENERIC_GAMEPAD
&& caps
.Usage
!= HID_USAGE_GENERIC_JOYSTICK
&&
482 caps
.Usage
!= HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER
)
483 WARN("ignoring HID device, unsupported usage %04x:%04x\n", caps
.UsagePage
, caps
.Usage
);
484 else if (device_is_overridden(device
))
485 WARN("ignoring HID device, overridden for dinput\n");
486 else if (!controller_init(&controllers
[i
], preparsed
, &caps
, device
, device_path
))
487 WARN("ignoring HID device, failed to initialize\n");
492 HidD_FreePreparsedData(preparsed
);
496 static void try_remove_device(const WCHAR
*device_path
)
500 if (find_opened_device(device_path
, &i
))
501 controller_destroy(&controllers
[i
], TRUE
);
504 static void update_controller_list(void)
506 char buffer
[sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W
) + MAX_PATH
* sizeof(WCHAR
)];
507 SP_DEVICE_INTERFACE_DETAIL_DATA_W
*detail
= (SP_DEVICE_INTERFACE_DETAIL_DATA_W
*)buffer
;
508 SP_DEVICE_INTERFACE_DATA iface
= {sizeof(iface
)};
513 guid
= GUID_DEVINTERFACE_WINEXINPUT
;
515 set
= SetupDiGetClassDevsW(&guid
, NULL
, NULL
, DIGCF_DEVICEINTERFACE
| DIGCF_PRESENT
);
516 detail
->cbSize
= sizeof(*detail
);
519 while (SetupDiEnumDeviceInterfaces(set
, NULL
, &guid
, idx
++, &iface
))
521 if (!SetupDiGetDeviceInterfaceDetailW(set
, &iface
, detail
, sizeof(buffer
), NULL
, NULL
))
523 if (!try_add_device(detail
->DevicePath
))
527 SetupDiDestroyDeviceInfoList(set
);
530 static void controller_destroy(struct xinput_controller
*controller
, BOOL already_removed
)
532 EnterCriticalSection(&controller
->crit
);
534 if (controller
->device
)
536 if (!already_removed
) controller_disable(controller
);
537 CloseHandle(controller
->device
);
538 controller
->device
= NULL
;
540 free(controller
->hid
.input_report_buf
);
541 free(controller
->hid
.output_report_buf
);
542 free(controller
->hid
.feature_report_buf
);
543 HidD_FreePreparsedData(controller
->hid
.preparsed
);
544 memset(&controller
->hid
, 0, sizeof(controller
->hid
));
547 LeaveCriticalSection(&controller
->crit
);
550 static void stop_update_thread(void)
554 SetEvent(stop_event
);
555 WaitForSingleObject(done_event
, INFINITE
);
557 CloseHandle(start_event
);
558 CloseHandle(stop_event
);
559 CloseHandle(done_event
);
560 CloseHandle(update_event
);
562 for (i
= 0; i
< XUSER_MAX_COUNT
; i
++) controller_destroy(&controllers
[i
], FALSE
);
565 static LONG
sign_extend(ULONG value
, const HIDP_VALUE_CAPS
*caps
)
567 UINT sign
= 1 << (caps
->BitSize
- 1);
568 if (sign
<= 1 || caps
->LogicalMin
>= 0) return value
;
569 return value
- ((value
& sign
) << 1);
572 static LONG
scale_value(ULONG value
, const HIDP_VALUE_CAPS
*caps
, LONG min
, LONG max
)
574 LONG tmp
= sign_extend(value
, caps
);
575 if (caps
->LogicalMin
> caps
->LogicalMax
) return 0;
576 if (caps
->LogicalMin
> tmp
|| caps
->LogicalMax
< tmp
) return 0;
577 return min
+ MulDiv(tmp
- caps
->LogicalMin
, max
- min
, caps
->LogicalMax
- caps
->LogicalMin
);
580 static void read_controller_state(struct xinput_controller
*controller
)
582 ULONG read_len
, report_len
= controller
->hid
.caps
.InputReportByteLength
;
583 char *report_buf
= controller
->hid
.input_report_buf
;
587 ULONG i
, button_length
, value
;
590 if (!GetOverlappedResult(controller
->device
, &controller
->hid
.read_ovl
, &read_len
, TRUE
))
592 if (GetLastError() == ERROR_OPERATION_ABORTED
) return;
593 if (GetLastError() == ERROR_ACCESS_DENIED
|| GetLastError() == ERROR_INVALID_HANDLE
) controller_destroy(controller
, TRUE
);
594 else ERR("Failed to read input report, GetOverlappedResult failed with error %lu\n", GetLastError());
598 button_length
= ARRAY_SIZE(buttons
);
599 status
= HidP_GetUsages(HidP_Input
, HID_USAGE_PAGE_BUTTON
, 0, buttons
, &button_length
, controller
->hid
.preparsed
, report_buf
, report_len
);
600 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetUsages HID_USAGE_PAGE_BUTTON returned %#lx\n", status
);
602 state
.Gamepad
.wButtons
= 0;
603 for (i
= 0; i
< button_length
; i
++)
607 case 1: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_A
; break;
608 case 2: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_B
; break;
609 case 3: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_X
; break;
610 case 4: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_Y
; break;
611 case 5: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_LEFT_SHOULDER
; break;
612 case 6: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_RIGHT_SHOULDER
; break;
613 case 7: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_BACK
; break;
614 case 8: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_START
; break;
615 case 9: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_LEFT_THUMB
; break;
616 case 10: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_RIGHT_THUMB
; break;
617 case 11: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_GUIDE
; break;
621 status
= HidP_GetUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_HATSWITCH
, &value
, controller
->hid
.preparsed
, report_buf
, report_len
);
622 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_HATSWITCH returned %#lx\n", status
);
629 case 1: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_UP
; break;
630 case 2: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_UP
| XINPUT_GAMEPAD_DPAD_RIGHT
; break;
631 case 3: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_RIGHT
; break;
632 case 4: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_RIGHT
| XINPUT_GAMEPAD_DPAD_DOWN
; break;
633 case 5: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_DOWN
; break;
634 case 6: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_DOWN
| XINPUT_GAMEPAD_DPAD_LEFT
; break;
635 case 7: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_LEFT
; break;
636 case 8: state
.Gamepad
.wButtons
|= XINPUT_GAMEPAD_DPAD_LEFT
| XINPUT_GAMEPAD_DPAD_UP
; break;
639 status
= HidP_GetUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_X
, &value
, controller
->hid
.preparsed
, report_buf
, report_len
);
640 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_X returned %#lx\n", status
);
641 else state
.Gamepad
.sThumbLX
= scale_value(value
, &controller
->hid
.lx_caps
, -32768, 32767);
643 status
= HidP_GetUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_Y
, &value
, controller
->hid
.preparsed
, report_buf
, report_len
);
644 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_Y returned %#lx\n", status
);
645 else state
.Gamepad
.sThumbLY
= -scale_value(value
, &controller
->hid
.ly_caps
, -32768, 32767) - 1;
647 status
= HidP_GetUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_RX
, &value
, controller
->hid
.preparsed
, report_buf
, report_len
);
648 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RX returned %#lx\n", status
);
649 else state
.Gamepad
.sThumbRX
= scale_value(value
, &controller
->hid
.rx_caps
, -32768, 32767);
651 status
= HidP_GetUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_RY
, &value
, controller
->hid
.preparsed
, report_buf
, report_len
);
652 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RY returned %#lx\n", status
);
653 else state
.Gamepad
.sThumbRY
= -scale_value(value
, &controller
->hid
.ry_caps
, -32768, 32767) - 1;
655 status
= HidP_GetUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_RZ
, &value
, controller
->hid
.preparsed
, report_buf
, report_len
);
656 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_RZ returned %#lx\n", status
);
657 else state
.Gamepad
.bRightTrigger
= scale_value(value
, &controller
->hid
.rt_caps
, 0, 255);
659 status
= HidP_GetUsageValue(HidP_Input
, HID_USAGE_PAGE_GENERIC
, 0, HID_USAGE_GENERIC_Z
, &value
, controller
->hid
.preparsed
, report_buf
, report_len
);
660 if (status
!= HIDP_STATUS_SUCCESS
) WARN("HidP_GetUsageValue HID_USAGE_PAGE_GENERIC / HID_USAGE_GENERIC_Z returned %#lx\n", status
);
661 else state
.Gamepad
.bLeftTrigger
= scale_value(value
, &controller
->hid
.lt_caps
, 0, 255);
663 EnterCriticalSection(&controller
->crit
);
664 if (controller
->enabled
)
666 state
.dwPacketNumber
= controller
->state
.dwPacketNumber
+ 1;
667 controller
->state
= state
;
668 memset(&controller
->hid
.read_ovl
, 0, sizeof(controller
->hid
.read_ovl
));
669 controller
->hid
.read_ovl
.hEvent
= controller
->hid
.read_event
;
670 ret
= ReadFile(controller
->device
, report_buf
, report_len
, NULL
, &controller
->hid
.read_ovl
);
671 if (!ret
&& GetLastError() != ERROR_IO_PENDING
) controller_destroy(controller
, TRUE
);
673 LeaveCriticalSection(&controller
->crit
);
676 static LRESULT CALLBACK
xinput_devnotify_wndproc(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
678 if (msg
== WM_DEVICECHANGE
)
680 DEV_BROADCAST_DEVICEINTERFACE_W
*iface
= (DEV_BROADCAST_DEVICEINTERFACE_W
*)lparam
;
681 if (wparam
== DBT_DEVICEARRIVAL
) try_add_device(iface
->dbcc_name
);
682 if (wparam
== DBT_DEVICEREMOVECOMPLETE
) try_remove_device(iface
->dbcc_name
);
685 return DefWindowProcW(hwnd
, msg
, wparam
, lparam
);
688 static DWORD WINAPI
hid_update_thread_proc(void *param
)
690 struct xinput_controller
*devices
[XUSER_MAX_COUNT
+ 2];
691 HANDLE events
[XUSER_MAX_COUNT
+ 2];
692 DWORD i
, count
= 2, ret
= WAIT_TIMEOUT
;
693 DEV_BROADCAST_DEVICEINTERFACE_W filter
=
695 .dbcc_size
= sizeof(DEV_BROADCAST_DEVICEINTERFACE_W
),
696 .dbcc_devicetype
= DBT_DEVTYP_DEVICEINTERFACE
,
697 .dbcc_classguid
= GUID_DEVINTERFACE_WINEXINPUT
,
701 .cbSize
= sizeof(WNDCLASSEXW
),
702 .hInstance
= xinput_instance
,
703 .lpszClassName
= L
"__wine_xinput_devnotify",
704 .lpfnWndProc
= xinput_devnotify_wndproc
,
710 SetThreadDescription(GetCurrentThread(), L
"wine_xinput_hid_update");
712 RegisterClassExW(&cls
);
713 hwnd
= CreateWindowExW(0, cls
.lpszClassName
, NULL
, 0, 0, 0, 0, 0,
714 HWND_MESSAGE
, NULL
, NULL
, NULL
);
715 notif
= RegisterDeviceNotificationW(hwnd
, &filter
, DEVICE_NOTIFY_WINDOW_HANDLE
);
717 update_controller_list();
718 SetEvent(start_event
);
722 if (ret
== count
) while (PeekMessageW(&msg
, hwnd
, 0, 0, PM_REMOVE
)) DispatchMessageW(&msg
);
723 if (ret
== WAIT_TIMEOUT
) update_controller_list();
724 if (ret
< count
- 2) read_controller_state(devices
[ret
]);
727 for (i
= 0; i
< XUSER_MAX_COUNT
; ++i
)
729 if (!controllers
[i
].device
) continue;
730 EnterCriticalSection(&controllers
[i
].crit
);
731 if (controllers
[i
].enabled
)
733 devices
[count
] = controllers
+ i
;
734 events
[count
] = controllers
[i
].hid
.read_event
;
737 LeaveCriticalSection(&controllers
[i
].crit
);
739 events
[count
++] = update_event
;
740 events
[count
++] = stop_event
;
742 while ((ret
= MsgWaitForMultipleObjectsEx(count
, events
, 2000, QS_ALLINPUT
, MWMO_ALERTABLE
)) < count
- 1 ||
743 ret
== count
|| ret
== WAIT_TIMEOUT
);
745 UnregisterDeviceNotification(notif
);
747 UnregisterClassW(cls
.lpszClassName
, xinput_instance
);
749 if (ret
!= count
- 1) ERR("update thread exited unexpectedly, ret %lu\n", ret
);
750 SetEvent(done_event
);
754 static BOOL WINAPI
start_update_thread_once( INIT_ONCE
*once
, void *param
, void **context
)
758 start_event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
759 if (!start_event
) ERR("failed to create start event, error %lu\n", GetLastError());
761 stop_event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
762 if (!stop_event
) ERR("failed to create stop event, error %lu\n", GetLastError());
764 done_event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
765 if (!done_event
) ERR("failed to create done event, error %lu\n", GetLastError());
767 update_event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
768 if (!update_event
) ERR("failed to create update event, error %lu\n", GetLastError());
770 thread
= CreateThread(NULL
, 0, hid_update_thread_proc
, NULL
, 0, NULL
);
771 if (!thread
) ERR("failed to create update thread, error %lu\n", GetLastError());
774 WaitForSingleObject(start_event
, INFINITE
);
778 static void start_update_thread(void)
780 static INIT_ONCE init_once
= INIT_ONCE_STATIC_INIT
;
781 InitOnceExecuteOnce(&init_once
, start_update_thread_once
, NULL
, NULL
);
784 static BOOL
controller_lock(struct xinput_controller
*controller
)
786 if (!controller
->device
) return FALSE
;
788 EnterCriticalSection(&controller
->crit
);
790 if (!controller
->device
)
792 LeaveCriticalSection(&controller
->crit
);
799 static void controller_unlock(struct xinput_controller
*controller
)
801 LeaveCriticalSection(&controller
->crit
);
804 BOOL WINAPI
DllMain(HINSTANCE inst
, DWORD reason
, LPVOID reserved
)
806 TRACE("inst %p, reason %lu, reserved %p.\n", inst
, reason
, reserved
);
810 case DLL_PROCESS_ATTACH
:
811 xinput_instance
= inst
;
812 DisableThreadLibraryCalls(inst
);
814 case DLL_PROCESS_DETACH
:
816 stop_update_thread();
822 void WINAPI DECLSPEC_HOTPATCH
XInputEnable(BOOL enable
)
826 TRACE("enable %d.\n", enable
);
828 /* Setting to false will stop messages from XInputSetState being sent
829 to the controllers. Setting to true will send the last vibration
830 value (sent to XInputSetState) to the controller and allow messages to
832 start_update_thread();
834 for (index
= 0; index
< XUSER_MAX_COUNT
; index
++)
836 if (!controller_lock(&controllers
[index
])) continue;
837 if (enable
) controller_enable(&controllers
[index
]);
838 else controller_disable(&controllers
[index
]);
839 controller_unlock(&controllers
[index
]);
843 DWORD WINAPI DECLSPEC_HOTPATCH
XInputSetState(DWORD index
, XINPUT_VIBRATION
*vibration
)
847 TRACE("index %lu, vibration %p.\n", index
, vibration
);
849 start_update_thread();
851 if (index
>= XUSER_MAX_COUNT
) return ERROR_BAD_ARGUMENTS
;
852 if (!controller_lock(&controllers
[index
])) return ERROR_DEVICE_NOT_CONNECTED
;
854 ret
= HID_set_state(&controllers
[index
], vibration
);
856 controller_unlock(&controllers
[index
]);
861 /* Some versions of SteamOverlayRenderer hot-patch XInputGetStateEx() and call
862 * XInputGetState() in the hook, so we need a wrapper. */
863 static DWORD
xinput_get_state(DWORD index
, XINPUT_STATE
*state
)
865 if (!state
) return ERROR_BAD_ARGUMENTS
;
867 start_update_thread();
869 if (index
>= XUSER_MAX_COUNT
) return ERROR_BAD_ARGUMENTS
;
870 if (!controller_lock(&controllers
[index
])) return ERROR_DEVICE_NOT_CONNECTED
;
872 *state
= controllers
[index
].state
;
873 controller_unlock(&controllers
[index
]);
875 return ERROR_SUCCESS
;
878 DWORD WINAPI DECLSPEC_HOTPATCH
XInputGetState(DWORD index
, XINPUT_STATE
*state
)
882 TRACE("index %lu, state %p.\n", index
, state
);
884 ret
= xinput_get_state(index
, state
);
885 if (ret
!= ERROR_SUCCESS
) return ret
;
887 /* The main difference between this and the Ex version is the media guide button */
888 state
->Gamepad
.wButtons
&= ~XINPUT_GAMEPAD_GUIDE
;
890 return ERROR_SUCCESS
;
893 DWORD WINAPI DECLSPEC_HOTPATCH
XInputGetStateEx(DWORD index
, XINPUT_STATE
*state
)
895 TRACE("index %lu, state %p.\n", index
, state
);
897 return xinput_get_state(index
, state
);
900 static const int JS_STATE_OFF
= 0;
901 static const int JS_STATE_LOW
= 1;
902 static const int JS_STATE_HIGH
= 2;
904 static int joystick_state(const SHORT value
)
906 if (value
> 20000) return JS_STATE_HIGH
;
907 if (value
< -20000) return JS_STATE_LOW
;
911 static WORD
js_vk_offs(const int x
, const int y
)
913 if (y
== JS_STATE_OFF
)
915 /*if (x == JS_STATE_OFF) shouldn't get here */
916 if (x
== JS_STATE_LOW
) return 3; /* LEFT */
917 /*if (x == JS_STATE_HIGH)*/ return 2; /* RIGHT */
919 if (y
== JS_STATE_HIGH
)
921 if (x
== JS_STATE_OFF
) return 0; /* UP */
922 if (x
== JS_STATE_LOW
) return 4; /* UPLEFT */
923 /*if (x == JS_STATE_HIGH)*/ return 5; /* UPRIGHT */
925 /*if (y == JS_STATE_LOW)*/
927 if (x
== JS_STATE_OFF
) return 1; /* DOWN */
928 if (x
== JS_STATE_LOW
) return 7; /* DOWNLEFT */
929 /*if (x == JS_STATE_HIGH)*/ return 6; /* DOWNRIGHT */
933 static DWORD
check_joystick_keystroke(const DWORD index
, XINPUT_KEYSTROKE
*keystroke
, const SHORT
*cur_x
,
934 const SHORT
*cur_y
, SHORT
*last_x
, SHORT
*last_y
, const WORD base_vk
)
936 int cur_vk
= 0, cur_x_st
, cur_y_st
;
937 int last_vk
= 0, last_x_st
, last_y_st
;
939 cur_x_st
= joystick_state(*cur_x
);
940 cur_y_st
= joystick_state(*cur_y
);
941 if (cur_x_st
|| cur_y_st
)
942 cur_vk
= base_vk
+ js_vk_offs(cur_x_st
, cur_y_st
);
944 last_x_st
= joystick_state(*last_x
);
945 last_y_st
= joystick_state(*last_y
);
946 if (last_x_st
|| last_y_st
)
947 last_vk
= base_vk
+ js_vk_offs(last_x_st
, last_y_st
);
949 if (cur_vk
!= last_vk
)
953 /* joystick was set, and now different. send a KEYUP event, and set
954 * last pos to centered, so the appropriate KEYDOWN event will be
955 * sent on the next call. */
956 keystroke
->VirtualKey
= last_vk
;
957 keystroke
->Unicode
= 0; /* unused */
958 keystroke
->Flags
= XINPUT_KEYSTROKE_KEYUP
;
959 keystroke
->UserIndex
= index
;
960 keystroke
->HidCode
= 0;
965 return ERROR_SUCCESS
;
968 /* joystick was unset, send KEYDOWN. */
969 keystroke
->VirtualKey
= cur_vk
;
970 keystroke
->Unicode
= 0; /* unused */
971 keystroke
->Flags
= XINPUT_KEYSTROKE_KEYDOWN
;
972 keystroke
->UserIndex
= index
;
973 keystroke
->HidCode
= 0;
978 return ERROR_SUCCESS
;
987 static BOOL
trigger_is_on(const BYTE value
)
992 static DWORD
check_for_keystroke(const DWORD index
, XINPUT_KEYSTROKE
*keystroke
)
994 struct xinput_controller
*controller
= &controllers
[index
];
995 const XINPUT_GAMEPAD
*cur
;
996 DWORD ret
= ERROR_EMPTY
;
1004 { XINPUT_GAMEPAD_DPAD_UP
, VK_PAD_DPAD_UP
},
1005 { XINPUT_GAMEPAD_DPAD_DOWN
, VK_PAD_DPAD_DOWN
},
1006 { XINPUT_GAMEPAD_DPAD_LEFT
, VK_PAD_DPAD_LEFT
},
1007 { XINPUT_GAMEPAD_DPAD_RIGHT
, VK_PAD_DPAD_RIGHT
},
1008 { XINPUT_GAMEPAD_START
, VK_PAD_START
},
1009 { XINPUT_GAMEPAD_BACK
, VK_PAD_BACK
},
1010 { XINPUT_GAMEPAD_LEFT_THUMB
, VK_PAD_LTHUMB_PRESS
},
1011 { XINPUT_GAMEPAD_RIGHT_THUMB
, VK_PAD_RTHUMB_PRESS
},
1012 { XINPUT_GAMEPAD_LEFT_SHOULDER
, VK_PAD_LSHOULDER
},
1013 { XINPUT_GAMEPAD_RIGHT_SHOULDER
, VK_PAD_RSHOULDER
},
1014 { XINPUT_GAMEPAD_A
, VK_PAD_A
},
1015 { XINPUT_GAMEPAD_B
, VK_PAD_B
},
1016 { XINPUT_GAMEPAD_X
, VK_PAD_X
},
1017 { XINPUT_GAMEPAD_Y
, VK_PAD_Y
},
1018 /* note: guide button does not send an event */
1021 if (!controller_lock(controller
)) return ERROR_DEVICE_NOT_CONNECTED
;
1023 cur
= &controller
->state
.Gamepad
;
1026 for (i
= 0; i
< ARRAY_SIZE(buttons
); ++i
)
1028 if ((cur
->wButtons
& buttons
[i
].mask
) ^ (controller
->last_keystroke
.wButtons
& buttons
[i
].mask
))
1030 keystroke
->VirtualKey
= buttons
[i
].vk
;
1031 keystroke
->Unicode
= 0; /* unused */
1032 if (cur
->wButtons
& buttons
[i
].mask
)
1034 keystroke
->Flags
= XINPUT_KEYSTROKE_KEYDOWN
;
1035 controller
->last_keystroke
.wButtons
|= buttons
[i
].mask
;
1039 keystroke
->Flags
= XINPUT_KEYSTROKE_KEYUP
;
1040 controller
->last_keystroke
.wButtons
&= ~buttons
[i
].mask
;
1042 keystroke
->UserIndex
= index
;
1043 keystroke
->HidCode
= 0;
1044 ret
= ERROR_SUCCESS
;
1050 if (trigger_is_on(cur
->bLeftTrigger
) ^ trigger_is_on(controller
->last_keystroke
.bLeftTrigger
))
1052 keystroke
->VirtualKey
= VK_PAD_LTRIGGER
;
1053 keystroke
->Unicode
= 0; /* unused */
1054 keystroke
->Flags
= trigger_is_on(cur
->bLeftTrigger
) ? XINPUT_KEYSTROKE_KEYDOWN
: XINPUT_KEYSTROKE_KEYUP
;
1055 keystroke
->UserIndex
= index
;
1056 keystroke
->HidCode
= 0;
1057 controller
->last_keystroke
.bLeftTrigger
= cur
->bLeftTrigger
;
1058 ret
= ERROR_SUCCESS
;
1062 if (trigger_is_on(cur
->bRightTrigger
) ^ trigger_is_on(controller
->last_keystroke
.bRightTrigger
))
1064 keystroke
->VirtualKey
= VK_PAD_RTRIGGER
;
1065 keystroke
->Unicode
= 0; /* unused */
1066 keystroke
->Flags
= trigger_is_on(cur
->bRightTrigger
) ? XINPUT_KEYSTROKE_KEYDOWN
: XINPUT_KEYSTROKE_KEYUP
;
1067 keystroke
->UserIndex
= index
;
1068 keystroke
->HidCode
= 0;
1069 controller
->last_keystroke
.bRightTrigger
= cur
->bRightTrigger
;
1070 ret
= ERROR_SUCCESS
;
1075 ret
= check_joystick_keystroke(index
, keystroke
, &cur
->sThumbLX
, &cur
->sThumbLY
,
1076 &controller
->last_keystroke
.sThumbLX
,
1077 &controller
->last_keystroke
.sThumbLY
, VK_PAD_LTHUMB_UP
);
1078 if (ret
== ERROR_SUCCESS
)
1081 ret
= check_joystick_keystroke(index
, keystroke
, &cur
->sThumbRX
, &cur
->sThumbRY
,
1082 &controller
->last_keystroke
.sThumbRX
,
1083 &controller
->last_keystroke
.sThumbRY
, VK_PAD_RTHUMB_UP
);
1084 if (ret
== ERROR_SUCCESS
)
1088 controller_unlock(controller
);
1093 DWORD WINAPI DECLSPEC_HOTPATCH
XInputGetKeystroke(DWORD index
, DWORD reserved
, PXINPUT_KEYSTROKE keystroke
)
1095 TRACE("index %lu, reserved %lu, keystroke %p.\n", index
, reserved
, keystroke
);
1097 if (index
>= XUSER_MAX_COUNT
&& index
!= XUSER_INDEX_ANY
) return ERROR_BAD_ARGUMENTS
;
1099 if (index
== XUSER_INDEX_ANY
)
1102 for (i
= 0; i
< XUSER_MAX_COUNT
; ++i
)
1103 if (check_for_keystroke(i
, keystroke
) == ERROR_SUCCESS
)
1104 return ERROR_SUCCESS
;
1108 return check_for_keystroke(index
, keystroke
);
1111 DWORD WINAPI DECLSPEC_HOTPATCH
XInputGetCapabilities(DWORD index
, DWORD flags
, XINPUT_CAPABILITIES
*capabilities
)
1113 TRACE("index %lu, flags %#lx, capabilities %p.\n", index
, flags
, capabilities
);
1115 start_update_thread();
1117 if (index
>= XUSER_MAX_COUNT
) return ERROR_BAD_ARGUMENTS
;
1119 if (!controller_lock(&controllers
[index
])) return ERROR_DEVICE_NOT_CONNECTED
;
1121 if (flags
& XINPUT_FLAG_GAMEPAD
&& controllers
[index
].caps
.SubType
!= XINPUT_DEVSUBTYPE_GAMEPAD
)
1123 controller_unlock(&controllers
[index
]);
1124 return ERROR_DEVICE_NOT_CONNECTED
;
1127 memcpy(capabilities
, &controllers
[index
].caps
, sizeof(*capabilities
));
1129 controller_unlock(&controllers
[index
]);
1131 return ERROR_SUCCESS
;
1134 DWORD WINAPI DECLSPEC_HOTPATCH
XInputGetDSoundAudioDeviceGuids(DWORD index
, GUID
*render_guid
, GUID
*capture_guid
)
1136 FIXME("index %lu, render_guid %s, capture_guid %s stub!\n", index
, debugstr_guid(render_guid
),
1137 debugstr_guid(capture_guid
));
1139 if (index
>= XUSER_MAX_COUNT
) return ERROR_BAD_ARGUMENTS
;
1140 if (!controllers
[index
].device
) return ERROR_DEVICE_NOT_CONNECTED
;
1142 return ERROR_NOT_SUPPORTED
;
1145 DWORD WINAPI DECLSPEC_HOTPATCH
XInputGetBatteryInformation(DWORD index
, BYTE type
, XINPUT_BATTERY_INFORMATION
* battery
)
1149 if (!once
++) FIXME("index %lu, type %u, battery %p.\n", index
, type
, battery
);
1151 if (index
>= XUSER_MAX_COUNT
) return ERROR_BAD_ARGUMENTS
;
1152 if (!controllers
[index
].device
) return ERROR_DEVICE_NOT_CONNECTED
;
1154 return ERROR_NOT_SUPPORTED
;