winex11.drv: Map coordinates before calling send_mouse_input.
[wine/zf.git] / dlls / xinput1_3 / xinput_main.c
blob63b7dd7f0b5f52007a8bc378d3a21df95ac9d0e5
1 /*
2 * The Wine project - Xinput Joystick Library
3 * Copyright 2008 Andrew Fenn
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 <assert.h>
21 #include <stdarg.h>
22 #include <string.h>
24 #include "wine/debug.h"
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
29 #include "xinput.h"
30 #include "xinput_private.h"
32 /* Not defined in the headers, used only by XInputGetStateEx */
33 #define XINPUT_GAMEPAD_GUIDE 0x0400
35 WINE_DEFAULT_DEBUG_CHANNEL(xinput);
37 /* xinput_crit guards controllers array */
38 static CRITICAL_SECTION_DEBUG xinput_critsect_debug =
40 0, 0, &xinput_crit,
41 { &xinput_critsect_debug.ProcessLocksList, &xinput_critsect_debug.ProcessLocksList },
42 0, 0, { (DWORD_PTR)(__FILE__ ": xinput_crit") }
44 CRITICAL_SECTION xinput_crit = { &xinput_critsect_debug, -1, 0, 0, 0, 0 };
46 static CRITICAL_SECTION_DEBUG controller_critsect_debug[XUSER_MAX_COUNT] =
49 0, 0, &controllers[0].crit,
50 { &controller_critsect_debug[0].ProcessLocksList, &controller_critsect_debug[0].ProcessLocksList },
51 0, 0, { (DWORD_PTR)(__FILE__ ": controllers[0].crit") }
54 0, 0, &controllers[1].crit,
55 { &controller_critsect_debug[1].ProcessLocksList, &controller_critsect_debug[1].ProcessLocksList },
56 0, 0, { (DWORD_PTR)(__FILE__ ": controllers[1].crit") }
59 0, 0, &controllers[2].crit,
60 { &controller_critsect_debug[2].ProcessLocksList, &controller_critsect_debug[2].ProcessLocksList },
61 0, 0, { (DWORD_PTR)(__FILE__ ": controllers[2].crit") }
64 0, 0, &controllers[3].crit,
65 { &controller_critsect_debug[3].ProcessLocksList, &controller_critsect_debug[3].ProcessLocksList },
66 0, 0, { (DWORD_PTR)(__FILE__ ": controllers[3].crit") }
70 xinput_controller controllers[XUSER_MAX_COUNT] = {
71 {{ &controller_critsect_debug[0], -1, 0, 0, 0, 0 }},
72 {{ &controller_critsect_debug[1], -1, 0, 0, 0, 0 }},
73 {{ &controller_critsect_debug[2], -1, 0, 0, 0, 0 }},
74 {{ &controller_critsect_debug[3], -1, 0, 0, 0, 0 }},
77 static BOOL verify_and_lock_device(xinput_controller *device)
79 if (!device->platform_private)
80 return FALSE;
82 EnterCriticalSection(&device->crit);
84 if (!device->platform_private)
86 LeaveCriticalSection(&device->crit);
87 return FALSE;
90 return TRUE;
93 static void unlock_device(xinput_controller *device)
95 LeaveCriticalSection(&device->crit);
98 BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved)
100 switch(reason)
102 case DLL_PROCESS_ATTACH:
103 DisableThreadLibraryCalls(inst);
104 break;
105 case DLL_PROCESS_DETACH:
106 if (reserved) break;
107 HID_destroy_gamepads(controllers);
108 break;
110 return TRUE;
113 void WINAPI DECLSPEC_HOTPATCH XInputEnable(BOOL enable)
115 int index;
117 TRACE("(enable %d)\n", enable);
119 /* Setting to false will stop messages from XInputSetState being sent
120 to the controllers. Setting to true will send the last vibration
121 value (sent to XInputSetState) to the controller and allow messages to
122 be sent */
123 HID_find_gamepads(controllers);
125 for (index = 0; index < XUSER_MAX_COUNT; index ++)
127 if (!verify_and_lock_device(&controllers[index])) continue;
128 HID_enable(&controllers[index], enable);
129 unlock_device(&controllers[index]);
133 DWORD WINAPI DECLSPEC_HOTPATCH XInputSetState(DWORD index, XINPUT_VIBRATION* vibration)
135 DWORD ret;
137 TRACE("(index %u, vibration %p)\n", index, vibration);
139 HID_find_gamepads(controllers);
141 if (index >= XUSER_MAX_COUNT)
142 return ERROR_BAD_ARGUMENTS;
143 if (!verify_and_lock_device(&controllers[index]))
144 return ERROR_DEVICE_NOT_CONNECTED;
146 ret = HID_set_state(&controllers[index], vibration);
148 unlock_device(&controllers[index]);
150 return ret;
153 /* Some versions of SteamOverlayRenderer hot-patch XInputGetStateEx() and call
154 * XInputGetState() in the hook, so we need a wrapper. */
155 static DWORD xinput_get_state(DWORD index, XINPUT_STATE *state)
157 if (!state)
158 return ERROR_BAD_ARGUMENTS;
160 HID_find_gamepads(controllers);
162 if (index >= XUSER_MAX_COUNT)
163 return ERROR_BAD_ARGUMENTS;
164 if (!verify_and_lock_device(&controllers[index]))
165 return ERROR_DEVICE_NOT_CONNECTED;
167 HID_update_state(&controllers[index], state);
169 if (!controllers[index].platform_private)
171 /* update_state may have disconnected the controller */
172 unlock_device(&controllers[index]);
173 return ERROR_DEVICE_NOT_CONNECTED;
176 unlock_device(&controllers[index]);
178 return ERROR_SUCCESS;
182 DWORD WINAPI DECLSPEC_HOTPATCH XInputGetState(DWORD index, XINPUT_STATE* state)
184 DWORD ret;
186 TRACE("(index %u, state %p)!\n", index, state);
188 ret = xinput_get_state(index, state);
189 if (ret != ERROR_SUCCESS)
190 return ret;
192 /* The main difference between this and the Ex version is the media guide button */
193 state->Gamepad.wButtons &= ~XINPUT_GAMEPAD_GUIDE;
195 return ERROR_SUCCESS;
198 DWORD WINAPI DECLSPEC_HOTPATCH XInputGetStateEx(DWORD index, XINPUT_STATE* state)
200 TRACE("(index %u, state %p)!\n", index, state);
202 return xinput_get_state(index, state);
205 static const int JS_STATE_OFF = 0;
206 static const int JS_STATE_LOW = 1;
207 static const int JS_STATE_HIGH = 2;
209 static int joystick_state(const SHORT value)
211 if (value > 20000)
212 return JS_STATE_HIGH;
213 if (value < -20000)
214 return JS_STATE_LOW;
215 return JS_STATE_OFF;
218 static WORD js_vk_offs(const int x, const int y)
220 if (y == JS_STATE_OFF)
222 /*if (x == JS_STATE_OFF) shouldn't get here */
223 if (x == JS_STATE_LOW) return 3; /* LEFT */
224 /*if (x == JS_STATE_HIGH)*/ return 2; /* RIGHT */
226 if (y == JS_STATE_HIGH)
228 if (x == JS_STATE_OFF) return 0; /* UP */
229 if (x == JS_STATE_LOW) return 4; /* UPLEFT */
230 /*if (x == JS_STATE_HIGH)*/ return 5; /* UPRIGHT */
232 /*if (y == JS_STATE_LOW)*/
234 if (x == JS_STATE_OFF) return 1; /* DOWN */
235 if (x == JS_STATE_LOW) return 7; /* DOWNLEFT */
236 /*if (x == JS_STATE_HIGH)*/ return 6; /* DOWNRIGHT */
240 static DWORD check_joystick_keystroke(const DWORD index, XINPUT_KEYSTROKE *keystroke,
241 const SHORT *cur_x, const SHORT *cur_y, SHORT *last_x, SHORT *last_y,
242 const WORD base_vk)
244 int cur_vk = 0, cur_x_st, cur_y_st;
245 int last_vk = 0, last_x_st, last_y_st;
247 cur_x_st = joystick_state(*cur_x);
248 cur_y_st = joystick_state(*cur_y);
249 if (cur_x_st || cur_y_st)
250 cur_vk = base_vk + js_vk_offs(cur_x_st, cur_y_st);
252 last_x_st = joystick_state(*last_x);
253 last_y_st = joystick_state(*last_y);
254 if (last_x_st || last_y_st)
255 last_vk = base_vk + js_vk_offs(last_x_st, last_y_st);
257 if (cur_vk != last_vk)
259 if (last_vk)
261 /* joystick was set, and now different. send a KEYUP event, and set
262 * last pos to centered, so the appropriate KEYDOWN event will be
263 * sent on the next call. */
264 keystroke->VirtualKey = last_vk;
265 keystroke->Unicode = 0; /* unused */
266 keystroke->Flags = XINPUT_KEYSTROKE_KEYUP;
267 keystroke->UserIndex = index;
268 keystroke->HidCode = 0;
270 *last_x = 0;
271 *last_y = 0;
273 return ERROR_SUCCESS;
276 /* joystick was unset, send KEYDOWN. */
277 keystroke->VirtualKey = cur_vk;
278 keystroke->Unicode = 0; /* unused */
279 keystroke->Flags = XINPUT_KEYSTROKE_KEYDOWN;
280 keystroke->UserIndex = index;
281 keystroke->HidCode = 0;
283 *last_x = *cur_x;
284 *last_y = *cur_y;
286 return ERROR_SUCCESS;
289 *last_x = *cur_x;
290 *last_y = *cur_y;
292 return ERROR_EMPTY;
295 static BOOL trigger_is_on(const BYTE value)
297 return value > 30;
300 static DWORD check_for_keystroke(const DWORD index, XINPUT_KEYSTROKE *keystroke)
302 xinput_controller *device = &controllers[index];
303 const XINPUT_GAMEPAD *cur;
304 DWORD ret = ERROR_EMPTY;
305 int i;
307 static const struct {
308 int mask;
309 WORD vk;
310 } buttons[] = {
311 { XINPUT_GAMEPAD_DPAD_UP, VK_PAD_DPAD_UP },
312 { XINPUT_GAMEPAD_DPAD_DOWN, VK_PAD_DPAD_DOWN },
313 { XINPUT_GAMEPAD_DPAD_LEFT, VK_PAD_DPAD_LEFT },
314 { XINPUT_GAMEPAD_DPAD_RIGHT, VK_PAD_DPAD_RIGHT },
315 { XINPUT_GAMEPAD_START, VK_PAD_START },
316 { XINPUT_GAMEPAD_BACK, VK_PAD_BACK },
317 { XINPUT_GAMEPAD_LEFT_THUMB, VK_PAD_LTHUMB_PRESS },
318 { XINPUT_GAMEPAD_RIGHT_THUMB, VK_PAD_RTHUMB_PRESS },
319 { XINPUT_GAMEPAD_LEFT_SHOULDER, VK_PAD_LSHOULDER },
320 { XINPUT_GAMEPAD_RIGHT_SHOULDER, VK_PAD_RSHOULDER },
321 { XINPUT_GAMEPAD_A, VK_PAD_A },
322 { XINPUT_GAMEPAD_B, VK_PAD_B },
323 { XINPUT_GAMEPAD_X, VK_PAD_X },
324 { XINPUT_GAMEPAD_Y, VK_PAD_Y },
325 /* note: guide button does not send an event */
328 if (!verify_and_lock_device(device))
329 return ERROR_DEVICE_NOT_CONNECTED;
331 cur = &device->state.Gamepad;
333 /*** buttons ***/
334 for (i = 0; i < ARRAY_SIZE(buttons); ++i)
336 if ((cur->wButtons & buttons[i].mask) ^ (device->last_keystroke.wButtons & buttons[i].mask))
338 keystroke->VirtualKey = buttons[i].vk;
339 keystroke->Unicode = 0; /* unused */
340 if (cur->wButtons & buttons[i].mask)
342 keystroke->Flags = XINPUT_KEYSTROKE_KEYDOWN;
343 device->last_keystroke.wButtons |= buttons[i].mask;
345 else
347 keystroke->Flags = XINPUT_KEYSTROKE_KEYUP;
348 device->last_keystroke.wButtons &= ~buttons[i].mask;
350 keystroke->UserIndex = index;
351 keystroke->HidCode = 0;
352 ret = ERROR_SUCCESS;
353 goto done;
357 /*** triggers ***/
358 if (trigger_is_on(cur->bLeftTrigger) ^ trigger_is_on(device->last_keystroke.bLeftTrigger))
360 keystroke->VirtualKey = VK_PAD_LTRIGGER;
361 keystroke->Unicode = 0; /* unused */
362 keystroke->Flags = trigger_is_on(cur->bLeftTrigger) ? XINPUT_KEYSTROKE_KEYDOWN : XINPUT_KEYSTROKE_KEYUP;
363 keystroke->UserIndex = index;
364 keystroke->HidCode = 0;
365 device->last_keystroke.bLeftTrigger = cur->bLeftTrigger;
366 ret = ERROR_SUCCESS;
367 goto done;
370 if (trigger_is_on(cur->bRightTrigger) ^ trigger_is_on(device->last_keystroke.bRightTrigger))
372 keystroke->VirtualKey = VK_PAD_RTRIGGER;
373 keystroke->Unicode = 0; /* unused */
374 keystroke->Flags = trigger_is_on(cur->bRightTrigger) ? XINPUT_KEYSTROKE_KEYDOWN : XINPUT_KEYSTROKE_KEYUP;
375 keystroke->UserIndex = index;
376 keystroke->HidCode = 0;
377 device->last_keystroke.bRightTrigger = cur->bRightTrigger;
378 ret = ERROR_SUCCESS;
379 goto done;
382 /*** joysticks ***/
383 ret = check_joystick_keystroke(index, keystroke, &cur->sThumbLX, &cur->sThumbLY,
384 &device->last_keystroke.sThumbLX,
385 &device->last_keystroke.sThumbLY, VK_PAD_LTHUMB_UP);
386 if (ret == ERROR_SUCCESS)
387 goto done;
389 ret = check_joystick_keystroke(index, keystroke, &cur->sThumbRX, &cur->sThumbRY,
390 &device->last_keystroke.sThumbRX,
391 &device->last_keystroke.sThumbRY, VK_PAD_RTHUMB_UP);
392 if (ret == ERROR_SUCCESS)
393 goto done;
395 done:
396 unlock_device(device);
398 return ret;
401 DWORD WINAPI DECLSPEC_HOTPATCH XInputGetKeystroke(DWORD index, DWORD reserved, PXINPUT_KEYSTROKE keystroke)
403 TRACE("(index %u, reserved %u, keystroke %p)\n", index, reserved, keystroke);
405 if (index >= XUSER_MAX_COUNT && index != XUSER_INDEX_ANY)
406 return ERROR_BAD_ARGUMENTS;
408 if (index == XUSER_INDEX_ANY)
410 int i;
411 for (i = 0; i < XUSER_MAX_COUNT; ++i)
412 if (check_for_keystroke(i, keystroke) == ERROR_SUCCESS)
413 return ERROR_SUCCESS;
414 return ERROR_EMPTY;
417 return check_for_keystroke(index, keystroke);
420 DWORD WINAPI DECLSPEC_HOTPATCH XInputGetCapabilities(DWORD index, DWORD flags, XINPUT_CAPABILITIES* capabilities)
422 TRACE("(index %u, flags 0x%x, capabilities %p)\n", index, flags, capabilities);
424 HID_find_gamepads(controllers);
426 if (index >= XUSER_MAX_COUNT)
427 return ERROR_BAD_ARGUMENTS;
429 if (!verify_and_lock_device(&controllers[index]))
430 return ERROR_DEVICE_NOT_CONNECTED;
432 if (flags & XINPUT_FLAG_GAMEPAD && controllers[index].caps.SubType != XINPUT_DEVSUBTYPE_GAMEPAD)
434 unlock_device(&controllers[index]);
435 return ERROR_DEVICE_NOT_CONNECTED;
438 memcpy(capabilities, &controllers[index].caps, sizeof(*capabilities));
440 unlock_device(&controllers[index]);
442 return ERROR_SUCCESS;
445 DWORD WINAPI DECLSPEC_HOTPATCH XInputGetDSoundAudioDeviceGuids(DWORD index, GUID* render_guid, GUID* capture_guid)
447 FIXME("(index %u, render guid %p, capture guid %p) Stub!\n", index, render_guid, capture_guid);
449 if (index >= XUSER_MAX_COUNT)
450 return ERROR_BAD_ARGUMENTS;
451 if (!controllers[index].platform_private)
452 return ERROR_DEVICE_NOT_CONNECTED;
454 return ERROR_NOT_SUPPORTED;
457 DWORD WINAPI DECLSPEC_HOTPATCH XInputGetBatteryInformation(DWORD index, BYTE type, XINPUT_BATTERY_INFORMATION* battery)
459 static int once;
461 if (!once++)
462 FIXME("(index %u, type %u, battery %p) Stub!\n", index, type, battery);
464 if (index >= XUSER_MAX_COUNT)
465 return ERROR_BAD_ARGUMENTS;
466 if (!controllers[index].platform_private)
467 return ERROR_DEVICE_NOT_CONNECTED;
469 return ERROR_NOT_SUPPORTED;