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 RegisterClassExW(&cls
);
711 hwnd
= CreateWindowExW(0, cls
.lpszClassName
, NULL
, 0, 0, 0, 0, 0,
712 HWND_MESSAGE
, NULL
, NULL
, NULL
);
713 notif
= RegisterDeviceNotificationW(hwnd
, &filter
, DEVICE_NOTIFY_WINDOW_HANDLE
);
715 update_controller_list();
716 SetEvent(start_event
);
720 if (ret
== count
) while (PeekMessageW(&msg
, hwnd
, 0, 0, PM_REMOVE
)) DispatchMessageW(&msg
);
721 if (ret
== WAIT_TIMEOUT
) update_controller_list();
722 if (ret
< count
- 2) read_controller_state(devices
[ret
]);
725 for (i
= 0; i
< XUSER_MAX_COUNT
; ++i
)
727 if (!controllers
[i
].device
) continue;
728 EnterCriticalSection(&controllers
[i
].crit
);
729 if (controllers
[i
].enabled
)
731 devices
[count
] = controllers
+ i
;
732 events
[count
] = controllers
[i
].hid
.read_event
;
735 LeaveCriticalSection(&controllers
[i
].crit
);
737 events
[count
++] = update_event
;
738 events
[count
++] = stop_event
;
740 while ((ret
= MsgWaitForMultipleObjectsEx(count
, events
, 2000, QS_ALLINPUT
, MWMO_ALERTABLE
)) < count
- 1 ||
741 ret
== count
|| ret
== WAIT_TIMEOUT
);
743 UnregisterDeviceNotification(notif
);
745 UnregisterClassW(cls
.lpszClassName
, xinput_instance
);
747 if (ret
!= count
- 1) ERR("update thread exited unexpectedly, ret %lu\n", ret
);
748 SetEvent(done_event
);
752 static BOOL WINAPI
start_update_thread_once( INIT_ONCE
*once
, void *param
, void **context
)
756 start_event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
757 if (!start_event
) ERR("failed to create start event, error %lu\n", GetLastError());
759 stop_event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
760 if (!stop_event
) ERR("failed to create stop event, error %lu\n", GetLastError());
762 done_event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
763 if (!done_event
) ERR("failed to create done event, error %lu\n", GetLastError());
765 update_event
= CreateEventA(NULL
, FALSE
, FALSE
, NULL
);
766 if (!update_event
) ERR("failed to create update event, error %lu\n", GetLastError());
768 thread
= CreateThread(NULL
, 0, hid_update_thread_proc
, NULL
, 0, NULL
);
769 if (!thread
) ERR("failed to create update thread, error %lu\n", GetLastError());
772 WaitForSingleObject(start_event
, INFINITE
);
776 static void start_update_thread(void)
778 static INIT_ONCE init_once
= INIT_ONCE_STATIC_INIT
;
779 InitOnceExecuteOnce(&init_once
, start_update_thread_once
, NULL
, NULL
);
782 static BOOL
controller_lock(struct xinput_controller
*controller
)
784 if (!controller
->device
) return FALSE
;
786 EnterCriticalSection(&controller
->crit
);
788 if (!controller
->device
)
790 LeaveCriticalSection(&controller
->crit
);
797 static void controller_unlock(struct xinput_controller
*controller
)
799 LeaveCriticalSection(&controller
->crit
);
802 BOOL WINAPI
DllMain(HINSTANCE inst
, DWORD reason
, LPVOID reserved
)
804 TRACE("inst %p, reason %lu, reserved %p.\n", inst
, reason
, reserved
);
808 case DLL_PROCESS_ATTACH
:
809 xinput_instance
= inst
;
810 DisableThreadLibraryCalls(inst
);
812 case DLL_PROCESS_DETACH
:
814 stop_update_thread();
820 void WINAPI DECLSPEC_HOTPATCH
XInputEnable(BOOL enable
)
824 TRACE("enable %d.\n", enable
);
826 /* Setting to false will stop messages from XInputSetState being sent
827 to the controllers. Setting to true will send the last vibration
828 value (sent to XInputSetState) to the controller and allow messages to
830 start_update_thread();
832 for (index
= 0; index
< XUSER_MAX_COUNT
; index
++)
834 if (!controller_lock(&controllers
[index
])) continue;
835 if (enable
) controller_enable(&controllers
[index
]);
836 else controller_disable(&controllers
[index
]);
837 controller_unlock(&controllers
[index
]);
841 DWORD WINAPI DECLSPEC_HOTPATCH
XInputSetState(DWORD index
, XINPUT_VIBRATION
*vibration
)
845 TRACE("index %lu, vibration %p.\n", index
, vibration
);
847 start_update_thread();
849 if (index
>= XUSER_MAX_COUNT
) return ERROR_BAD_ARGUMENTS
;
850 if (!controller_lock(&controllers
[index
])) return ERROR_DEVICE_NOT_CONNECTED
;
852 ret
= HID_set_state(&controllers
[index
], vibration
);
854 controller_unlock(&controllers
[index
]);
859 /* Some versions of SteamOverlayRenderer hot-patch XInputGetStateEx() and call
860 * XInputGetState() in the hook, so we need a wrapper. */
861 static DWORD
xinput_get_state(DWORD index
, XINPUT_STATE
*state
)
863 if (!state
) return ERROR_BAD_ARGUMENTS
;
865 start_update_thread();
867 if (index
>= XUSER_MAX_COUNT
) return ERROR_BAD_ARGUMENTS
;
868 if (!controller_lock(&controllers
[index
])) return ERROR_DEVICE_NOT_CONNECTED
;
870 *state
= controllers
[index
].state
;
871 controller_unlock(&controllers
[index
]);
873 return ERROR_SUCCESS
;
876 DWORD WINAPI DECLSPEC_HOTPATCH
XInputGetState(DWORD index
, XINPUT_STATE
*state
)
880 TRACE("index %lu, state %p.\n", index
, state
);
882 ret
= xinput_get_state(index
, state
);
883 if (ret
!= ERROR_SUCCESS
) return ret
;
885 /* The main difference between this and the Ex version is the media guide button */
886 state
->Gamepad
.wButtons
&= ~XINPUT_GAMEPAD_GUIDE
;
888 return ERROR_SUCCESS
;
891 DWORD WINAPI DECLSPEC_HOTPATCH
XInputGetStateEx(DWORD index
, XINPUT_STATE
*state
)
893 TRACE("index %lu, state %p.\n", index
, state
);
895 return xinput_get_state(index
, state
);
898 static const int JS_STATE_OFF
= 0;
899 static const int JS_STATE_LOW
= 1;
900 static const int JS_STATE_HIGH
= 2;
902 static int joystick_state(const SHORT value
)
904 if (value
> 20000) return JS_STATE_HIGH
;
905 if (value
< -20000) return JS_STATE_LOW
;
909 static WORD
js_vk_offs(const int x
, const int y
)
911 if (y
== JS_STATE_OFF
)
913 /*if (x == JS_STATE_OFF) shouldn't get here */
914 if (x
== JS_STATE_LOW
) return 3; /* LEFT */
915 /*if (x == JS_STATE_HIGH)*/ return 2; /* RIGHT */
917 if (y
== JS_STATE_HIGH
)
919 if (x
== JS_STATE_OFF
) return 0; /* UP */
920 if (x
== JS_STATE_LOW
) return 4; /* UPLEFT */
921 /*if (x == JS_STATE_HIGH)*/ return 5; /* UPRIGHT */
923 /*if (y == JS_STATE_LOW)*/
925 if (x
== JS_STATE_OFF
) return 1; /* DOWN */
926 if (x
== JS_STATE_LOW
) return 7; /* DOWNLEFT */
927 /*if (x == JS_STATE_HIGH)*/ return 6; /* DOWNRIGHT */
931 static DWORD
check_joystick_keystroke(const DWORD index
, XINPUT_KEYSTROKE
*keystroke
, const SHORT
*cur_x
,
932 const SHORT
*cur_y
, SHORT
*last_x
, SHORT
*last_y
, const WORD base_vk
)
934 int cur_vk
= 0, cur_x_st
, cur_y_st
;
935 int last_vk
= 0, last_x_st
, last_y_st
;
937 cur_x_st
= joystick_state(*cur_x
);
938 cur_y_st
= joystick_state(*cur_y
);
939 if (cur_x_st
|| cur_y_st
)
940 cur_vk
= base_vk
+ js_vk_offs(cur_x_st
, cur_y_st
);
942 last_x_st
= joystick_state(*last_x
);
943 last_y_st
= joystick_state(*last_y
);
944 if (last_x_st
|| last_y_st
)
945 last_vk
= base_vk
+ js_vk_offs(last_x_st
, last_y_st
);
947 if (cur_vk
!= last_vk
)
951 /* joystick was set, and now different. send a KEYUP event, and set
952 * last pos to centered, so the appropriate KEYDOWN event will be
953 * sent on the next call. */
954 keystroke
->VirtualKey
= last_vk
;
955 keystroke
->Unicode
= 0; /* unused */
956 keystroke
->Flags
= XINPUT_KEYSTROKE_KEYUP
;
957 keystroke
->UserIndex
= index
;
958 keystroke
->HidCode
= 0;
963 return ERROR_SUCCESS
;
966 /* joystick was unset, send KEYDOWN. */
967 keystroke
->VirtualKey
= cur_vk
;
968 keystroke
->Unicode
= 0; /* unused */
969 keystroke
->Flags
= XINPUT_KEYSTROKE_KEYDOWN
;
970 keystroke
->UserIndex
= index
;
971 keystroke
->HidCode
= 0;
976 return ERROR_SUCCESS
;
985 static BOOL
trigger_is_on(const BYTE value
)
990 static DWORD
check_for_keystroke(const DWORD index
, XINPUT_KEYSTROKE
*keystroke
)
992 struct xinput_controller
*controller
= &controllers
[index
];
993 const XINPUT_GAMEPAD
*cur
;
994 DWORD ret
= ERROR_EMPTY
;
1002 { XINPUT_GAMEPAD_DPAD_UP
, VK_PAD_DPAD_UP
},
1003 { XINPUT_GAMEPAD_DPAD_DOWN
, VK_PAD_DPAD_DOWN
},
1004 { XINPUT_GAMEPAD_DPAD_LEFT
, VK_PAD_DPAD_LEFT
},
1005 { XINPUT_GAMEPAD_DPAD_RIGHT
, VK_PAD_DPAD_RIGHT
},
1006 { XINPUT_GAMEPAD_START
, VK_PAD_START
},
1007 { XINPUT_GAMEPAD_BACK
, VK_PAD_BACK
},
1008 { XINPUT_GAMEPAD_LEFT_THUMB
, VK_PAD_LTHUMB_PRESS
},
1009 { XINPUT_GAMEPAD_RIGHT_THUMB
, VK_PAD_RTHUMB_PRESS
},
1010 { XINPUT_GAMEPAD_LEFT_SHOULDER
, VK_PAD_LSHOULDER
},
1011 { XINPUT_GAMEPAD_RIGHT_SHOULDER
, VK_PAD_RSHOULDER
},
1012 { XINPUT_GAMEPAD_A
, VK_PAD_A
},
1013 { XINPUT_GAMEPAD_B
, VK_PAD_B
},
1014 { XINPUT_GAMEPAD_X
, VK_PAD_X
},
1015 { XINPUT_GAMEPAD_Y
, VK_PAD_Y
},
1016 /* note: guide button does not send an event */
1019 if (!controller_lock(controller
)) return ERROR_DEVICE_NOT_CONNECTED
;
1021 cur
= &controller
->state
.Gamepad
;
1024 for (i
= 0; i
< ARRAY_SIZE(buttons
); ++i
)
1026 if ((cur
->wButtons
& buttons
[i
].mask
) ^ (controller
->last_keystroke
.wButtons
& buttons
[i
].mask
))
1028 keystroke
->VirtualKey
= buttons
[i
].vk
;
1029 keystroke
->Unicode
= 0; /* unused */
1030 if (cur
->wButtons
& buttons
[i
].mask
)
1032 keystroke
->Flags
= XINPUT_KEYSTROKE_KEYDOWN
;
1033 controller
->last_keystroke
.wButtons
|= buttons
[i
].mask
;
1037 keystroke
->Flags
= XINPUT_KEYSTROKE_KEYUP
;
1038 controller
->last_keystroke
.wButtons
&= ~buttons
[i
].mask
;
1040 keystroke
->UserIndex
= index
;
1041 keystroke
->HidCode
= 0;
1042 ret
= ERROR_SUCCESS
;
1048 if (trigger_is_on(cur
->bLeftTrigger
) ^ trigger_is_on(controller
->last_keystroke
.bLeftTrigger
))
1050 keystroke
->VirtualKey
= VK_PAD_LTRIGGER
;
1051 keystroke
->Unicode
= 0; /* unused */
1052 keystroke
->Flags
= trigger_is_on(cur
->bLeftTrigger
) ? XINPUT_KEYSTROKE_KEYDOWN
: XINPUT_KEYSTROKE_KEYUP
;
1053 keystroke
->UserIndex
= index
;
1054 keystroke
->HidCode
= 0;
1055 controller
->last_keystroke
.bLeftTrigger
= cur
->bLeftTrigger
;
1056 ret
= ERROR_SUCCESS
;
1060 if (trigger_is_on(cur
->bRightTrigger
) ^ trigger_is_on(controller
->last_keystroke
.bRightTrigger
))
1062 keystroke
->VirtualKey
= VK_PAD_RTRIGGER
;
1063 keystroke
->Unicode
= 0; /* unused */
1064 keystroke
->Flags
= trigger_is_on(cur
->bRightTrigger
) ? XINPUT_KEYSTROKE_KEYDOWN
: XINPUT_KEYSTROKE_KEYUP
;
1065 keystroke
->UserIndex
= index
;
1066 keystroke
->HidCode
= 0;
1067 controller
->last_keystroke
.bRightTrigger
= cur
->bRightTrigger
;
1068 ret
= ERROR_SUCCESS
;
1073 ret
= check_joystick_keystroke(index
, keystroke
, &cur
->sThumbLX
, &cur
->sThumbLY
,
1074 &controller
->last_keystroke
.sThumbLX
,
1075 &controller
->last_keystroke
.sThumbLY
, VK_PAD_LTHUMB_UP
);
1076 if (ret
== ERROR_SUCCESS
)
1079 ret
= check_joystick_keystroke(index
, keystroke
, &cur
->sThumbRX
, &cur
->sThumbRY
,
1080 &controller
->last_keystroke
.sThumbRX
,
1081 &controller
->last_keystroke
.sThumbRY
, VK_PAD_RTHUMB_UP
);
1082 if (ret
== ERROR_SUCCESS
)
1086 controller_unlock(controller
);
1091 DWORD WINAPI DECLSPEC_HOTPATCH
XInputGetKeystroke(DWORD index
, DWORD reserved
, PXINPUT_KEYSTROKE keystroke
)
1093 TRACE("index %lu, reserved %lu, keystroke %p.\n", index
, reserved
, keystroke
);
1095 if (index
>= XUSER_MAX_COUNT
&& index
!= XUSER_INDEX_ANY
) return ERROR_BAD_ARGUMENTS
;
1097 if (index
== XUSER_INDEX_ANY
)
1100 for (i
= 0; i
< XUSER_MAX_COUNT
; ++i
)
1101 if (check_for_keystroke(i
, keystroke
) == ERROR_SUCCESS
)
1102 return ERROR_SUCCESS
;
1106 return check_for_keystroke(index
, keystroke
);
1109 DWORD WINAPI DECLSPEC_HOTPATCH
XInputGetCapabilities(DWORD index
, DWORD flags
, XINPUT_CAPABILITIES
*capabilities
)
1111 TRACE("index %lu, flags %#lx, capabilities %p.\n", index
, flags
, capabilities
);
1113 start_update_thread();
1115 if (index
>= XUSER_MAX_COUNT
) return ERROR_BAD_ARGUMENTS
;
1117 if (!controller_lock(&controllers
[index
])) return ERROR_DEVICE_NOT_CONNECTED
;
1119 if (flags
& XINPUT_FLAG_GAMEPAD
&& controllers
[index
].caps
.SubType
!= XINPUT_DEVSUBTYPE_GAMEPAD
)
1121 controller_unlock(&controllers
[index
]);
1122 return ERROR_DEVICE_NOT_CONNECTED
;
1125 memcpy(capabilities
, &controllers
[index
].caps
, sizeof(*capabilities
));
1127 controller_unlock(&controllers
[index
]);
1129 return ERROR_SUCCESS
;
1132 DWORD WINAPI DECLSPEC_HOTPATCH
XInputGetDSoundAudioDeviceGuids(DWORD index
, GUID
*render_guid
, GUID
*capture_guid
)
1134 FIXME("index %lu, render_guid %s, capture_guid %s stub!\n", index
, debugstr_guid(render_guid
),
1135 debugstr_guid(capture_guid
));
1137 if (index
>= XUSER_MAX_COUNT
) return ERROR_BAD_ARGUMENTS
;
1138 if (!controllers
[index
].device
) return ERROR_DEVICE_NOT_CONNECTED
;
1140 return ERROR_NOT_SUPPORTED
;
1143 DWORD WINAPI DECLSPEC_HOTPATCH
XInputGetBatteryInformation(DWORD index
, BYTE type
, XINPUT_BATTERY_INFORMATION
* battery
)
1147 if (!once
++) FIXME("index %lu, type %u, battery %p.\n", index
, type
, battery
);
1149 if (index
>= XUSER_MAX_COUNT
) return ERROR_BAD_ARGUMENTS
;
1150 if (!controllers
[index
].device
) return ERROR_DEVICE_NOT_CONNECTED
;
1152 return ERROR_NOT_SUPPORTED
;