ntdll: Add a wrapper to call the unhandled exception filter.
[wine.git] / dlls / xinput1_3 / hid.c
blob8a3544375390b2758b0e94b80d46605eb0c92597
1 /*
2 * The Wine project - Xinput Joystick HID interface
3 * Copyright 2018 Aric Stewart
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include "config.h"
21 #include "wine/port.h"
23 #include <assert.h>
24 #include <stdarg.h>
25 #include <string.h>
27 #include "wine/debug.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "winerror.h"
32 #include "winreg.h"
33 #include "wingdi.h"
34 #include "winnls.h"
35 #include "winternl.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"
44 #include "xinput.h"
45 #include "xinput_private.h"
47 #include "wine/unicode.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(xinput);
51 #define XINPUT_GAMEPAD_GUIDE 0x0400
53 static CRITICAL_SECTION hid_xinput_crit;
54 static CRITICAL_SECTION_DEBUG hid_critsect_debug =
56 0, 0, &hid_xinput_crit,
57 { &hid_critsect_debug.ProcessLocksList, &hid_critsect_debug.ProcessLocksList },
58 0, 0, { (DWORD_PTR)(__FILE__ ": hid_xinput_crit") }
60 static CRITICAL_SECTION hid_xinput_crit = { &hid_critsect_debug, -1, 0, 0, 0, 0 };
62 struct hid_platform_private {
63 PHIDP_PREPARSED_DATA ppd;
64 HANDLE device;
65 WCHAR *device_path;
66 BOOL enabled;
68 CRITICAL_SECTION crit;
70 DWORD report_length;
71 BYTE current_report;
72 CHAR *reports[2];
74 LONG ThumbLXRange[3];
75 LONG ThumbLYRange[3];
76 LONG LeftTriggerRange[3];
77 LONG ThumbRXRange[3];
78 LONG ThumbRYRange[3];
79 LONG RightTriggerRange[3];
82 static DWORD last_check = 0;
84 static void MarkUsage(struct hid_platform_private *private, WORD usage, LONG min, LONG max, USHORT bits)
86 switch (usage)
88 case HID_USAGE_GENERIC_X:
89 private->ThumbLXRange[0] = min;
90 private->ThumbLXRange[1] = bits;
91 private->ThumbLXRange[2] = max - min;
92 break;
93 case HID_USAGE_GENERIC_Y:
94 private->ThumbLYRange[0] = min;
95 private->ThumbLYRange[1] = bits;
96 private->ThumbLYRange[2] = max - min;
97 break;
98 case HID_USAGE_GENERIC_Z:
99 private->LeftTriggerRange[0] = min;
100 private->LeftTriggerRange[1] = bits;
101 private->LeftTriggerRange[2] = max - min;
102 break;
103 case HID_USAGE_GENERIC_RX:
104 private->ThumbRXRange[0] = min;
105 private->ThumbRXRange[1] = bits;
106 private->ThumbRXRange[2] = max - min;
107 break;
108 case HID_USAGE_GENERIC_RY:
109 private->ThumbRYRange[0] = min;
110 private->ThumbRYRange[1] = bits;
111 private->ThumbRYRange[2] = max - min;
112 break;
113 case HID_USAGE_GENERIC_RZ:
114 private->RightTriggerRange[0] = min;
115 private->RightTriggerRange[1] = bits;
116 private->RightTriggerRange[2] = max - min;
117 break;
121 static BOOL VerifyGamepad(PHIDP_PREPARSED_DATA ppd, XINPUT_CAPABILITIES *xinput_caps, struct hid_platform_private *private, HIDP_CAPS *caps)
123 HIDP_BUTTON_CAPS *button_caps;
124 HIDP_VALUE_CAPS *value_caps;
126 int i;
127 int button_count = 0;
128 USHORT button_caps_count = 0;
129 USHORT value_caps_count = 0;
131 /* Count buttons */
132 memset(xinput_caps, 0, sizeof(XINPUT_CAPABILITIES));
134 button_caps_count = caps->NumberInputButtonCaps;
135 button_caps = HeapAlloc(GetProcessHeap(), 0, sizeof(*button_caps) * button_caps_count);
136 HidP_GetButtonCaps(HidP_Input, button_caps, &button_caps_count, ppd);
137 for (i = 0; i < button_caps_count; i++)
139 if (button_caps[i].UsagePage != HID_USAGE_PAGE_BUTTON)
140 continue;
141 if (button_caps[i].IsRange)
142 button_count = max(button_count, button_caps[i].Range.UsageMax);
143 else
144 button_count = max(button_count, button_caps[i].NotRange.Usage);
146 HeapFree(GetProcessHeap(), 0, button_caps);
147 if (button_count < 14)
148 WARN("Too few buttons, Continue\n");
149 xinput_caps->Gamepad.wButtons = 0xffff;
151 value_caps_count = caps->NumberInputValueCaps;
152 value_caps = HeapAlloc(GetProcessHeap(), 0, sizeof(*value_caps) * value_caps_count);
153 HidP_GetValueCaps(HidP_Input, value_caps, &value_caps_count, ppd);
154 for (i = 0; i < value_caps_count; i++)
156 if (value_caps[i].UsagePage != HID_USAGE_PAGE_GENERIC)
157 continue;
158 if (value_caps[i].IsRange)
160 int u;
161 for (u = value_caps[i].Range.UsageMin; u <=value_caps[i].Range.UsageMax; u++)
162 MarkUsage(private, u, value_caps[i].LogicalMin, value_caps[i].LogicalMax, value_caps[i].BitSize);
164 else
165 MarkUsage(private, value_caps[i].NotRange.Usage, value_caps[i].LogicalMin, value_caps[i].LogicalMax, value_caps[i].BitSize);
167 HeapFree(GetProcessHeap(), 0, value_caps);
169 if (private->LeftTriggerRange[1])
170 xinput_caps->Gamepad.bLeftTrigger = (1u << (sizeof(xinput_caps->Gamepad.bLeftTrigger) + 1)) - 1;
171 else
172 WARN("Missing axis LeftTrigger\n");
173 if (private->RightTriggerRange[1])
174 xinput_caps->Gamepad.bRightTrigger = (1u << (sizeof(xinput_caps->Gamepad.bRightTrigger) + 1)) - 1;
175 else
176 WARN("Missing axis RightTrigger\n");
177 if (private->ThumbLXRange[1])
178 xinput_caps->Gamepad.sThumbLX = (1u << (sizeof(xinput_caps->Gamepad.sThumbLX) + 1)) - 1;
179 else
180 WARN("Missing axis ThumbLX\n");
181 if (private->ThumbLYRange[1])
182 xinput_caps->Gamepad.sThumbLY = (1u << (sizeof(xinput_caps->Gamepad.sThumbLY) + 1)) - 1;
183 else
184 WARN("Missing axis ThumbLY\n");
185 if (private->ThumbRXRange[1])
186 xinput_caps->Gamepad.sThumbRX = (1u << (sizeof(xinput_caps->Gamepad.sThumbRX) + 1)) - 1;
187 else
188 WARN("Missing axis ThumbRX\n");
189 if (private->ThumbRYRange[1])
190 xinput_caps->Gamepad.sThumbRY = (1u << (sizeof(xinput_caps->Gamepad.sThumbRY) + 1)) - 1;
191 else
192 WARN("Missing axis ThumbRY\n");
194 xinput_caps->Type = XINPUT_DEVTYPE_GAMEPAD;
195 xinput_caps->SubType = XINPUT_DEVSUBTYPE_GAMEPAD;
197 value_caps_count = caps->NumberOutputValueCaps;
198 if (value_caps_count > 0)
199 xinput_caps->Flags |= XINPUT_CAPS_FFB_SUPPORTED;
201 return TRUE;
204 static void build_private(struct hid_platform_private *private, PHIDP_PREPARSED_DATA ppd, HIDP_CAPS *caps, HANDLE device, WCHAR *path)
206 size_t size;
207 private->ppd = ppd;
208 private->device = device;
209 private->report_length = caps->InputReportByteLength + 1;
210 private->current_report = 0;
211 private->reports[0] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, private->report_length);
212 private->reports[1] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, private->report_length);
213 size = (strlenW(path) + 1) * sizeof(WCHAR);
214 private->device_path = HeapAlloc(GetProcessHeap(), 0, size);
215 memcpy(private->device_path, path, size);
216 private->enabled = TRUE;
218 InitializeCriticalSection(&private->crit);
219 private->crit.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": JoystickImpl*->generic.base.crit");
222 void HID_find_gamepads(xinput_controller *devices)
224 HDEVINFO device_info_set;
225 GUID hid_guid;
226 SP_DEVICE_INTERFACE_DATA interface_data;
227 SP_DEVICE_INTERFACE_DETAIL_DATA_W *data;
228 PHIDP_PREPARSED_DATA ppd;
229 DWORD detail_size = MAX_PATH * sizeof(WCHAR);
230 HANDLE device = INVALID_HANDLE_VALUE;
231 HIDP_CAPS Caps;
232 DWORD idx,didx;
233 int i;
235 idx = GetTickCount();
236 if ((idx - last_check) < 2000)
237 return;
238 last_check = idx;
240 HidD_GetHidGuid(&hid_guid);
242 EnterCriticalSection(&hid_xinput_crit);
244 device_info_set = SetupDiGetClassDevsW(&hid_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
246 data = HeapAlloc(GetProcessHeap(), 0 , sizeof(*data) + detail_size);
247 data->cbSize = sizeof(*data);
249 ZeroMemory(&interface_data, sizeof(interface_data));
250 interface_data.cbSize = sizeof(interface_data);
252 idx = didx = 0;
253 while (SetupDiEnumDeviceInterfaces(device_info_set, NULL, &hid_guid, idx++,
254 &interface_data) && didx < XUSER_MAX_COUNT)
256 static const WCHAR ig[] = {'I','G','_',0};
257 if (!SetupDiGetDeviceInterfaceDetailW(device_info_set,
258 &interface_data, data, sizeof(*data) + detail_size, NULL, NULL))
259 continue;
261 if (!strstrW(data->DevicePath, ig))
262 continue;
264 for (i = 0; i < XUSER_MAX_COUNT; i++)
266 struct hid_platform_private *private = devices[i].platform_private;
267 if (devices[i].connected && !strcmpW(data->DevicePath, private->device_path))
268 break;
270 if (i != XUSER_MAX_COUNT)
271 continue;
272 device = CreateFileW(data->DevicePath, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0 );
273 if (device == INVALID_HANDLE_VALUE)
274 continue;
276 HidD_GetPreparsedData(device, &ppd);
277 HidP_GetCaps(ppd, &Caps);
278 if (Caps.UsagePage == HID_USAGE_PAGE_GENERIC &&
279 (Caps.Usage == HID_USAGE_GENERIC_GAMEPAD ||
280 Caps.Usage == HID_USAGE_GENERIC_JOYSTICK ||
281 Caps.Usage == 0x8 /* Multi-axis Controller */))
283 struct hid_platform_private *private = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct hid_platform_private));
284 if (VerifyGamepad(ppd, &(devices[didx].caps), private, &Caps))
286 TRACE("Found gamepad %i %s\n",didx, debugstr_w(data->DevicePath));
287 devices[didx].connected = TRUE;
288 build_private(private, ppd, &Caps, device, data->DevicePath);
289 devices[didx].platform_private = private;
290 didx++;
292 else
294 CloseHandle(device);
295 HidD_FreePreparsedData(ppd);
296 HeapFree(GetProcessHeap(), 0, private);
299 else
301 CloseHandle(device);
302 HidD_FreePreparsedData(ppd);
304 device = INVALID_HANDLE_VALUE;
306 HeapFree(GetProcessHeap(), 0, data);
307 SetupDiDestroyDeviceInfoList(device_info_set);
308 LeaveCriticalSection(&hid_xinput_crit);
309 return;
312 static void remove_gamepad(xinput_controller *device)
314 if (device->connected)
316 struct hid_platform_private *private = device->platform_private;
318 EnterCriticalSection(&private->crit);
319 CloseHandle(private->device);
320 HeapFree(GetProcessHeap(), 0, private->reports[0]);
321 HeapFree(GetProcessHeap(), 0, private->reports[1]);
322 HeapFree(GetProcessHeap(), 0, private->device_path);
323 HidD_FreePreparsedData(private->ppd);
324 device->platform_private = NULL;
325 device->connected = FALSE;
326 LeaveCriticalSection(&private->crit);
327 DeleteCriticalSection(&private->crit);
328 HeapFree(GetProcessHeap(), 0, private);
332 void HID_destroy_gamepads(xinput_controller *devices)
334 int i;
335 EnterCriticalSection(&hid_xinput_crit);
336 for (i = 0; i < XUSER_MAX_COUNT; i++)
337 remove_gamepad(&devices[i]);
338 LeaveCriticalSection(&hid_xinput_crit);
341 #define SIGN(v,b) ((b==8)?(BYTE)v:(b==16)?(SHORT)v:(INT)v)
342 #define SCALE_SHORT(v,r) (SHORT)((((0xffff)*(SIGN(v,r[1]) - r[0]))/r[2])-32767)
343 #define SCALE_BYTE(v,r) (BYTE)((((0xff)*(SIGN(v,r[1]) - r[0]))/r[2]))
345 void HID_update_state(xinput_controller* device)
347 struct hid_platform_private *private = device->platform_private;
348 int i;
349 CHAR *report = private->reports[(private->current_report)%2];
350 CHAR *target_report = private->reports[(private->current_report+1)%2];
352 USAGE buttons[15];
353 ULONG button_length;
354 ULONG value;
356 if (!private->enabled)
357 return;
359 EnterCriticalSection(&private->crit);
360 if (!HidD_GetInputReport(private->device, target_report, private->report_length))
362 if (GetLastError() == ERROR_ACCESS_DENIED || GetLastError() == ERROR_INVALID_HANDLE)
363 remove_gamepad(device);
364 else
365 ERR("Failed to get Input Report (%x)\n", GetLastError());
366 LeaveCriticalSection(&private->crit);
367 return;
369 if (memcmp(report, target_report, private->report_length) == 0)
371 LeaveCriticalSection(&private->crit);
372 return;
375 private->current_report = (private->current_report+1)%2;
377 device->state.dwPacketNumber++;
378 button_length = 15;
379 HidP_GetUsages(HidP_Input, HID_USAGE_PAGE_BUTTON, 0, buttons, &button_length, private->ppd, target_report, private->report_length);
381 device->state.Gamepad.wButtons = 0;
382 for (i = 0; i < button_length; i++)
384 switch (buttons[i])
386 case 1: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_A; break;
387 case 2: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_B; break;
388 case 3: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_X; break;
389 case 4: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_Y; break;
390 case 5: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_SHOULDER; break;
391 case 6: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_SHOULDER; break;
392 case 7: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; break;
393 case 8: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_THUMB; break;
395 case 9: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_START; break;
396 case 10: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_BACK; break;
397 case 11: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_GUIDE; break;
398 case 12: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP; break;
399 case 13: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; break;
400 case 14: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT; break;
401 case 15: device->state.Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; break;
405 HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_X, &value, private->ppd, target_report, private->report_length);
406 device->state.Gamepad.sThumbLX = SCALE_SHORT(value, private->ThumbLXRange);
408 HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Y, &value, private->ppd, target_report, private->report_length);
409 device->state.Gamepad.sThumbLY = -SCALE_SHORT(value, private->ThumbLYRange);
411 HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RX, &value, private->ppd, target_report, private->report_length);
412 device->state.Gamepad.sThumbRX = SCALE_SHORT(value, private->ThumbRXRange);
414 HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RY, &value, private->ppd, target_report, private->report_length);
415 device->state.Gamepad.sThumbRY = -SCALE_SHORT(value, private->ThumbRYRange);
417 HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_RZ, &value, private->ppd, target_report, private->report_length);
418 device->state.Gamepad.bRightTrigger = SCALE_BYTE(value, private->RightTriggerRange);
420 HidP_GetUsageValue(HidP_Input, HID_USAGE_PAGE_GENERIC, 0, HID_USAGE_GENERIC_Z, &value, private->ppd, target_report, private->report_length);
421 device->state.Gamepad.bLeftTrigger = SCALE_BYTE(value, private->LeftTriggerRange);
422 LeaveCriticalSection(&private->crit);
425 DWORD HID_set_state(xinput_controller* device, XINPUT_VIBRATION* state)
427 struct hid_platform_private *private = device->platform_private;
429 struct {
430 BYTE report;
431 BYTE pad1[2];
432 BYTE left;
433 BYTE right;
434 BYTE pad2[3];
435 } report;
437 if (device->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED)
439 device->caps.Vibration.wLeftMotorSpeed = state->wLeftMotorSpeed;
440 device->caps.Vibration.wRightMotorSpeed = state->wRightMotorSpeed;
442 if (private->enabled)
444 BOOLEAN rc;
446 report.report = 0;
447 report.pad1[0] = 0x8;
448 report.pad1[1] = 0x0;
449 report.left = (BYTE)(state->wLeftMotorSpeed / 255);
450 report.right = (BYTE)(state->wRightMotorSpeed / 255);
451 memset(&report.pad2, 0, sizeof(report.pad2));
453 EnterCriticalSection(&private->crit);
454 rc = HidD_SetOutputReport(private->device, &report, sizeof(report));
455 LeaveCriticalSection(&private->crit);
456 if (rc)
457 return ERROR_SUCCESS;
458 return GetLastError();
460 return ERROR_SUCCESS;
463 return ERROR_NOT_SUPPORTED;
466 void HID_enable(xinput_controller* device, BOOL enable)
468 struct hid_platform_private *private = device->platform_private;
470 if (device->caps.Flags & XINPUT_CAPS_FFB_SUPPORTED)
472 EnterCriticalSection(&private->crit);
473 if (private->enabled && !enable)
475 XINPUT_VIBRATION state;
476 state.wLeftMotorSpeed = 0;
477 state.wRightMotorSpeed = 0;
478 HID_set_state(device, &state);
480 else if (!private->enabled && enable)
482 HID_set_state(device, &device->caps.Vibration);
484 LeaveCriticalSection(&private->crit);
487 private->enabled = enable;