mshtml: Implement MediaQueryList's addListener method.
[wine.git] / dlls / xinput1_3 / main.c
blob2abf33a8d45f6c677f3b5d877bd52549764f8964
1 /*
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
22 #include <assert.h>
23 #include <stdarg.h>
24 #include <string.h>
25 #include <stdlib.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winerror.h"
30 #include "winuser.h"
31 #include "winreg.h"
32 #include "wingdi.h"
33 #include "winnls.h"
34 #include "winternl.h"
36 #include "dbt.h"
37 #include "setupapi.h"
38 #include "devpkey.h"
39 #include "hidusage.h"
40 #include "ddk/hidsdi.h"
41 #include "initguid.h"
42 #include "devguid.h"
43 #include "xinput.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;
58 XINPUT_STATE state;
59 XINPUT_GAMEPAD last_keystroke;
60 XINPUT_VIBRATION vibration;
61 HANDLE device;
62 WCHAR device_path[MAX_PATH];
63 BOOL enabled;
65 struct
67 PHIDP_PREPARSED_DATA preparsed;
68 HIDP_CAPS caps;
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;
76 HANDLE read_event;
77 OVERLAPPED read_ovl;
79 char *input_report_buf;
80 char *output_report_buf;
81 char *feature_report_buf;
83 BYTE haptics_report;
84 HIDP_VALUE_CAPS haptics_rumble_caps;
85 HIDP_VALUE_CAPS haptics_buzz_caps;
86 } hid;
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)
130 int i;
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;
138 return FALSE;
141 static void check_value_caps(struct xinput_controller *controller, USHORT usage, HIDP_VALUE_CAPS *caps)
143 switch (usage)
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;
162 NTSTATUS status;
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");
172 return;
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());
182 return;
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);
189 count = 1;
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;
222 NTSTATUS status;
224 /* Count buttons */
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)
233 continue;
234 if (button_caps[i].IsRange)
235 button_count = max(button_count, button_caps[i].Range.UsageMax);
236 else
237 button_count = max(button_count, button_caps[i].NotRange.Usage);
239 free(button_caps);
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);
254 free(value_caps);
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);
288 free(collections);
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;
298 return TRUE;
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;
307 USHORT collection;
308 NTSTATUS status;
309 BYTE report_id;
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());
337 return 0;
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;
349 BOOL ret;
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)
378 HANDLE event = NULL;
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);
401 return TRUE;
403 failed:
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));
408 CloseHandle(event);
409 return FALSE;
412 static void get_registry_keys(HKEY *defkey, HKEY *appkey)
414 WCHAR buffer[MAX_PATH + 26], *name = buffer, *tmp;
415 DWORD len;
416 HKEY hkey;
418 *appkey = 0;
419 if (RegOpenKeyW(HKEY_CURRENT_USER, L"Software\\Wine\\DirectInput\\Joysticks", defkey))
420 *defkey = 0;
422 if (!(len = GetModuleFileNameW(0, buffer, MAX_PATH)) || len >= MAX_PATH)
423 return;
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;
431 RegCloseKey(hkey);
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;
440 HKEY defkey, appkey;
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);
455 return disable;
458 static BOOL try_add_device(const WCHAR *device_path)
460 SP_DEVICE_INTERFACE_DATA iface = {sizeof(iface)};
461 PHIDP_PREPARSED_DATA preparsed;
462 HIDP_CAPS caps;
463 NTSTATUS status;
464 HANDLE device;
465 int i;
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;
474 preparsed = NULL;
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");
488 else
489 return TRUE;
491 CloseHandle(device);
492 HidD_FreePreparsedData(preparsed);
493 return TRUE;
496 static void try_remove_device(const WCHAR *device_path)
498 int i;
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)};
509 HDEVINFO set;
510 DWORD idx;
511 GUID guid;
513 guid = GUID_DEVINTERFACE_WINEXINPUT;
515 set = SetupDiGetClassDevsW(&guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
516 detail->cbSize = sizeof(*detail);
518 idx = 0;
519 while (SetupDiEnumDeviceInterfaces(set, NULL, &guid, idx++, &iface))
521 if (!SetupDiGetDeviceInterfaceDetailW(set, &iface, detail, sizeof(buffer), NULL, NULL))
522 continue;
523 if (!try_add_device(detail->DevicePath))
524 break;
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)
552 int i;
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;
584 XINPUT_STATE state;
585 NTSTATUS status;
586 USAGE buttons[11];
587 ULONG i, button_length, value;
588 BOOL ret;
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());
595 return;
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++)
605 switch (buttons[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);
623 else switch (value)
625 /* 8 1 2
626 * 7 0 3
627 * 6 5 4 */
628 case 0: break;
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,
699 WNDCLASSEXW cls =
701 .cbSize = sizeof(WNDCLASSEXW),
702 .hInstance = xinput_instance,
703 .lpszClassName = L"__wine_xinput_devnotify",
704 .lpfnWndProc = xinput_devnotify_wndproc,
706 HDEVNOTIFY notif;
707 HWND hwnd;
708 MSG msg;
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]);
726 count = 0;
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;
735 count++;
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);
746 DestroyWindow(hwnd);
747 UnregisterClassW(cls.lpszClassName, xinput_instance);
749 if (ret != count - 1) ERR("update thread exited unexpectedly, ret %lu\n", ret);
750 SetEvent(done_event);
751 return ret;
754 static BOOL WINAPI start_update_thread_once( INIT_ONCE *once, void *param, void **context )
756 HANDLE thread;
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());
772 CloseHandle(thread);
774 WaitForSingleObject(start_event, INFINITE);
775 return TRUE;
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);
793 return FALSE;
796 return TRUE;
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);
808 switch (reason)
810 case DLL_PROCESS_ATTACH:
811 xinput_instance = inst;
812 DisableThreadLibraryCalls(inst);
813 break;
814 case DLL_PROCESS_DETACH:
815 if (reserved) break;
816 stop_update_thread();
817 break;
819 return TRUE;
822 void WINAPI DECLSPEC_HOTPATCH XInputEnable(BOOL enable)
824 int index;
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
831 be sent */
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)
845 DWORD ret;
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]);
858 return ret;
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)
880 DWORD ret;
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;
908 return JS_STATE_OFF;
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)
951 if (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;
962 *last_x = 0;
963 *last_y = 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;
975 *last_x = *cur_x;
976 *last_y = *cur_y;
978 return ERROR_SUCCESS;
981 *last_x = *cur_x;
982 *last_y = *cur_y;
984 return ERROR_EMPTY;
987 static BOOL trigger_is_on(const BYTE value)
989 return value > 30;
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;
997 int i;
999 static const struct
1001 int mask;
1002 WORD vk;
1003 } buttons[] = {
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;
1025 /*** buttons ***/
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;
1037 else
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;
1045 goto done;
1049 /*** triggers ***/
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;
1059 goto done;
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;
1071 goto done;
1074 /*** joysticks ***/
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)
1079 goto done;
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)
1085 goto done;
1087 done:
1088 controller_unlock(controller);
1090 return ret;
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)
1101 int i;
1102 for (i = 0; i < XUSER_MAX_COUNT; ++i)
1103 if (check_for_keystroke(i, keystroke) == ERROR_SUCCESS)
1104 return ERROR_SUCCESS;
1105 return ERROR_EMPTY;
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)
1147 static int once;
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;