include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / winebus.sys / bus_sdl.c
blob71f91162fb172ed6738321076f703c3c45043637
1 /*
2 * Plug and Play support for hid devices found through SDL2
4 * Copyright 2017 CodeWeavers, Aric Stewart
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include "config.h"
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <sys/types.h>
34 #include <dlfcn.h>
35 #ifdef HAVE_SDL_H
36 # include <SDL.h>
37 #endif
39 #include <pthread.h>
41 #include "ntstatus.h"
42 #define WIN32_NO_STATUS
43 #include "windef.h"
44 #include "winbase.h"
45 #include "winnls.h"
46 #include "winreg.h"
47 #include "winternl.h"
48 #include "ddk/wdm.h"
49 #include "ddk/hidtypes.h"
50 #include "ddk/hidsdi.h"
51 #include "hidusage.h"
53 #include "wine/debug.h"
54 #include "wine/hid.h"
55 #include "wine/unixlib.h"
57 #include "unix_private.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(hid);
61 #ifdef SONAME_LIBSDL2
63 static pthread_mutex_t sdl_cs = PTHREAD_MUTEX_INITIALIZER;
64 static struct sdl_bus_options options;
66 static void *sdl_handle = NULL;
67 static UINT quit_event = -1;
68 static struct list event_queue = LIST_INIT(event_queue);
69 static struct list device_list = LIST_INIT(device_list);
71 #define MAKE_FUNCPTR(f) static typeof(f) * p##f = NULL
72 MAKE_FUNCPTR(SDL_GetError);
73 MAKE_FUNCPTR(SDL_Init);
74 MAKE_FUNCPTR(SDL_JoystickClose);
75 MAKE_FUNCPTR(SDL_JoystickEventState);
76 MAKE_FUNCPTR(SDL_JoystickGetGUID);
77 MAKE_FUNCPTR(SDL_JoystickGetGUIDString);
78 MAKE_FUNCPTR(SDL_JoystickInstanceID);
79 MAKE_FUNCPTR(SDL_JoystickName);
80 MAKE_FUNCPTR(SDL_JoystickNumAxes);
81 MAKE_FUNCPTR(SDL_JoystickOpen);
82 MAKE_FUNCPTR(SDL_WaitEventTimeout);
83 MAKE_FUNCPTR(SDL_JoystickNumButtons);
84 MAKE_FUNCPTR(SDL_JoystickNumBalls);
85 MAKE_FUNCPTR(SDL_JoystickNumHats);
86 MAKE_FUNCPTR(SDL_JoystickGetAxis);
87 MAKE_FUNCPTR(SDL_JoystickGetHat);
88 MAKE_FUNCPTR(SDL_IsGameController);
89 MAKE_FUNCPTR(SDL_GameControllerClose);
90 MAKE_FUNCPTR(SDL_GameControllerGetAxis);
91 MAKE_FUNCPTR(SDL_GameControllerGetButton);
92 MAKE_FUNCPTR(SDL_GameControllerName);
93 MAKE_FUNCPTR(SDL_GameControllerOpen);
94 MAKE_FUNCPTR(SDL_GameControllerEventState);
95 MAKE_FUNCPTR(SDL_HapticClose);
96 MAKE_FUNCPTR(SDL_HapticDestroyEffect);
97 MAKE_FUNCPTR(SDL_HapticGetEffectStatus);
98 MAKE_FUNCPTR(SDL_HapticNewEffect);
99 MAKE_FUNCPTR(SDL_HapticOpenFromJoystick);
100 MAKE_FUNCPTR(SDL_HapticPause);
101 MAKE_FUNCPTR(SDL_HapticQuery);
102 MAKE_FUNCPTR(SDL_HapticRumbleInit);
103 MAKE_FUNCPTR(SDL_HapticRumblePlay);
104 MAKE_FUNCPTR(SDL_HapticRumbleStop);
105 MAKE_FUNCPTR(SDL_HapticRumbleSupported);
106 MAKE_FUNCPTR(SDL_HapticRunEffect);
107 MAKE_FUNCPTR(SDL_HapticSetGain);
108 MAKE_FUNCPTR(SDL_HapticSetAutocenter);
109 MAKE_FUNCPTR(SDL_HapticStopAll);
110 MAKE_FUNCPTR(SDL_HapticStopEffect);
111 MAKE_FUNCPTR(SDL_HapticUnpause);
112 MAKE_FUNCPTR(SDL_HapticUpdateEffect);
113 MAKE_FUNCPTR(SDL_JoystickIsHaptic);
114 MAKE_FUNCPTR(SDL_GameControllerAddMapping);
115 MAKE_FUNCPTR(SDL_RegisterEvents);
116 MAKE_FUNCPTR(SDL_PushEvent);
117 MAKE_FUNCPTR(SDL_GetTicks);
118 static int (*pSDL_JoystickRumble)(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
119 static int (*pSDL_JoystickRumbleTriggers)(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms);
120 static Uint16 (*pSDL_JoystickGetProduct)(SDL_Joystick * joystick);
121 static Uint16 (*pSDL_JoystickGetProductVersion)(SDL_Joystick * joystick);
122 static Uint16 (*pSDL_JoystickGetVendor)(SDL_Joystick * joystick);
123 static SDL_JoystickType (*pSDL_JoystickGetType)(SDL_Joystick * joystick);
124 static const char *(*pSDL_JoystickGetSerial)(SDL_Joystick * joystick);
126 /* internal bits for extended rumble support, SDL_Haptic types are 16-bits */
127 #define WINE_SDL_JOYSTICK_RUMBLE 0x40000000 /* using SDL_JoystickRumble API */
128 #define WINE_SDL_HAPTIC_RUMBLE 0x80000000 /* using SDL_HapticRumble API */
130 #define EFFECT_SUPPORT_HAPTICS (SDL_HAPTIC_LEFTRIGHT|WINE_SDL_HAPTIC_RUMBLE|WINE_SDL_JOYSTICK_RUMBLE)
131 #define EFFECT_SUPPORT_PHYSICAL (SDL_HAPTIC_CONSTANT|SDL_HAPTIC_RAMP|SDL_HAPTIC_SINE|SDL_HAPTIC_TRIANGLE| \
132 SDL_HAPTIC_SAWTOOTHUP|SDL_HAPTIC_SAWTOOTHDOWN|SDL_HAPTIC_SPRING|SDL_HAPTIC_DAMPER|SDL_HAPTIC_INERTIA| \
133 SDL_HAPTIC_FRICTION|SDL_HAPTIC_CUSTOM)
135 struct sdl_device
137 struct unix_device unix_device;
139 SDL_Joystick *sdl_joystick;
140 SDL_GameController *sdl_controller;
141 SDL_JoystickID id;
142 BOOL started;
144 DWORD effect_support;
145 SDL_Haptic *sdl_haptic;
146 int haptic_effect_id;
147 int effect_ids[256];
148 int effect_state[256];
149 LONG effect_flags;
150 int axis_offset;
153 static inline struct sdl_device *impl_from_unix_device(struct unix_device *iface)
155 return CONTAINING_RECORD(iface, struct sdl_device, unix_device);
158 static struct sdl_device *find_device_from_id(SDL_JoystickID id)
160 struct sdl_device *impl;
162 LIST_FOR_EACH_ENTRY(impl, &device_list, struct sdl_device, unix_device.entry)
163 if (impl->id == id && impl->axis_offset == 0) return impl;
165 return NULL;
168 static struct sdl_device *find_device_from_id_and_axis(SDL_JoystickID id, int axis)
170 struct sdl_device *impl;
172 LIST_FOR_EACH_ENTRY(impl, &device_list, struct sdl_device, unix_device.entry)
174 USHORT count = impl->unix_device.hid_device_state.abs_axis_count;
175 if (impl->id == id && impl->axis_offset <= axis && impl->axis_offset + count > axis)
176 return impl;
179 return NULL;
182 static void set_hat_value(struct unix_device *iface, int index, int value)
184 LONG x = 0, y = 0;
185 switch (value)
187 case SDL_HAT_CENTERED: break;
188 case SDL_HAT_DOWN: y = 1; break;
189 case SDL_HAT_RIGHTDOWN: y = x = 1; break;
190 case SDL_HAT_RIGHT: x = 1; break;
191 case SDL_HAT_RIGHTUP: x = 1; y = -1; break;
192 case SDL_HAT_UP: y = -1; break;
193 case SDL_HAT_LEFTUP: x = y = -1; break;
194 case SDL_HAT_LEFT: x = -1; break;
195 case SDL_HAT_LEFTDOWN: x = -1; y = 1; break;
197 hid_device_set_hatswitch_x(iface, index, x);
198 hid_device_set_hatswitch_y(iface, index, y);
201 static BOOL descriptor_add_haptic(struct sdl_device *impl)
203 USHORT i, count = 0;
204 USAGE usages[16];
206 if (impl->axis_offset > 0 || !pSDL_JoystickIsHaptic(impl->sdl_joystick) ||
207 !(impl->sdl_haptic = pSDL_HapticOpenFromJoystick(impl->sdl_joystick)))
208 impl->effect_support = 0;
209 else
211 impl->effect_support = pSDL_HapticQuery(impl->sdl_haptic);
212 if (!(impl->effect_support & EFFECT_SUPPORT_HAPTICS) &&
213 pSDL_HapticRumbleSupported(impl->sdl_haptic) &&
214 pSDL_HapticRumbleInit(impl->sdl_haptic) == 0)
215 impl->effect_support |= WINE_SDL_HAPTIC_RUMBLE;
218 if (impl->axis_offset == 0 && pSDL_JoystickRumble && !pSDL_JoystickRumble(impl->sdl_joystick, 0, 0, 0))
219 impl->effect_support |= WINE_SDL_JOYSTICK_RUMBLE;
221 if (impl->effect_support & EFFECT_SUPPORT_HAPTICS)
223 if (!hid_device_add_haptics(&impl->unix_device))
224 return FALSE;
227 if ((impl->effect_support & EFFECT_SUPPORT_PHYSICAL))
229 /* SDL_HAPTIC_SQUARE doesn't exist */
230 if (impl->effect_support & SDL_HAPTIC_SINE) usages[count++] = PID_USAGE_ET_SINE;
231 if (impl->effect_support & SDL_HAPTIC_TRIANGLE) usages[count++] = PID_USAGE_ET_TRIANGLE;
232 if (impl->effect_support & SDL_HAPTIC_SAWTOOTHUP) usages[count++] = PID_USAGE_ET_SAWTOOTH_UP;
233 if (impl->effect_support & SDL_HAPTIC_SAWTOOTHDOWN) usages[count++] = PID_USAGE_ET_SAWTOOTH_DOWN;
234 if (impl->effect_support & SDL_HAPTIC_SPRING) usages[count++] = PID_USAGE_ET_SPRING;
235 if (impl->effect_support & SDL_HAPTIC_DAMPER) usages[count++] = PID_USAGE_ET_DAMPER;
236 if (impl->effect_support & SDL_HAPTIC_INERTIA) usages[count++] = PID_USAGE_ET_INERTIA;
237 if (impl->effect_support & SDL_HAPTIC_FRICTION) usages[count++] = PID_USAGE_ET_FRICTION;
238 if (impl->effect_support & SDL_HAPTIC_CONSTANT) usages[count++] = PID_USAGE_ET_CONSTANT_FORCE;
239 if (impl->effect_support & SDL_HAPTIC_RAMP) usages[count++] = PID_USAGE_ET_RAMP;
241 if (!hid_device_add_physical(&impl->unix_device, usages, count))
242 return FALSE;
245 impl->haptic_effect_id = -1;
246 for (i = 0; i < ARRAY_SIZE(impl->effect_ids); ++i) impl->effect_ids[i] = -1;
247 return TRUE;
250 static const USAGE_AND_PAGE absolute_axis_usages[] =
252 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_X},
253 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_Y},
254 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_Z},
255 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_RX},
256 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_RY},
257 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_RZ},
258 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_SLIDER},
259 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_DIAL},
261 static const USAGE_AND_PAGE relative_axis_usages[] =
263 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_X},
264 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_Y},
265 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_RX},
266 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_RY},
267 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_Z},
268 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_RZ},
269 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_SLIDER},
270 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_DIAL},
271 {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_WHEEL},
274 static NTSTATUS build_joystick_report_descriptor(struct unix_device *iface)
276 const USAGE_AND_PAGE device_usage = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_JOYSTICK};
277 struct sdl_device *impl = impl_from_unix_device(iface);
278 int i, button_count, axis_count, ball_count, hat_count;
279 USAGE_AND_PAGE physical_usage;
281 axis_count = pSDL_JoystickNumAxes(impl->sdl_joystick);
282 if (options.split_controllers) axis_count = min(6, axis_count - impl->axis_offset);
283 if (axis_count > ARRAY_SIZE(absolute_axis_usages))
285 FIXME("More than %zu absolute axes found, ignoring.\n", ARRAY_SIZE(absolute_axis_usages));
286 axis_count = ARRAY_SIZE(absolute_axis_usages);
289 ball_count = pSDL_JoystickNumBalls(impl->sdl_joystick);
290 if (ball_count > ARRAY_SIZE(relative_axis_usages) / 2)
292 FIXME("More than %zu relative axes found, ignoring.\n", ARRAY_SIZE(relative_axis_usages));
293 ball_count = ARRAY_SIZE(relative_axis_usages) / 2;
296 if (impl->axis_offset == 0)
298 hat_count = pSDL_JoystickNumHats(impl->sdl_joystick);
299 button_count = pSDL_JoystickNumButtons(impl->sdl_joystick);
301 else
303 hat_count = 0;
304 button_count = 0;
307 if (!pSDL_JoystickGetType) physical_usage = device_usage;
308 else switch (pSDL_JoystickGetType(impl->sdl_joystick))
310 case SDL_JOYSTICK_TYPE_ARCADE_PAD:
311 case SDL_JOYSTICK_TYPE_ARCADE_STICK:
312 case SDL_JOYSTICK_TYPE_DANCE_PAD:
313 case SDL_JOYSTICK_TYPE_DRUM_KIT:
314 case SDL_JOYSTICK_TYPE_GUITAR:
315 case SDL_JOYSTICK_TYPE_UNKNOWN:
316 physical_usage.UsagePage = HID_USAGE_PAGE_GENERIC;
317 physical_usage.Usage = HID_USAGE_GENERIC_JOYSTICK;
318 break;
319 case SDL_JOYSTICK_TYPE_GAMECONTROLLER:
320 physical_usage.UsagePage = HID_USAGE_PAGE_GENERIC;
321 physical_usage.Usage = HID_USAGE_GENERIC_GAMEPAD;
322 break;
323 case SDL_JOYSTICK_TYPE_WHEEL:
324 physical_usage.UsagePage = HID_USAGE_PAGE_SIMULATION;
325 physical_usage.Usage = HID_USAGE_SIMULATION_AUTOMOBILE_SIMULATION_DEVICE;
326 break;
327 case SDL_JOYSTICK_TYPE_FLIGHT_STICK:
328 case SDL_JOYSTICK_TYPE_THROTTLE:
329 physical_usage.UsagePage = HID_USAGE_PAGE_SIMULATION;
330 physical_usage.Usage = HID_USAGE_SIMULATION_FLIGHT_SIMULATION_DEVICE;
331 break;
334 if (!hid_device_begin_report_descriptor(iface, &device_usage))
335 return STATUS_NO_MEMORY;
337 if (!hid_device_begin_input_report(iface, &physical_usage))
338 return STATUS_NO_MEMORY;
340 for (i = 0; i < axis_count; i++)
342 if (!hid_device_add_axes(iface, 1, absolute_axis_usages[i].UsagePage,
343 &absolute_axis_usages[i].Usage, FALSE, -32768, 32767))
344 return STATUS_NO_MEMORY;
347 for (i = 0; i < ball_count; i++)
349 if (!hid_device_add_axes(iface, 2, relative_axis_usages[2 * i].UsagePage,
350 &relative_axis_usages[2 * i].Usage, TRUE, INT32_MIN, INT32_MAX))
351 return STATUS_NO_MEMORY;
354 if (hat_count && !hid_device_add_hatswitch(iface, hat_count))
355 return STATUS_NO_MEMORY;
357 if (button_count && !hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, button_count))
358 return STATUS_NO_MEMORY;
360 if (!hid_device_end_input_report(iface))
361 return STATUS_NO_MEMORY;
363 if (!descriptor_add_haptic(impl))
364 return STATUS_NO_MEMORY;
366 if (!hid_device_end_report_descriptor(iface))
367 return STATUS_NO_MEMORY;
369 /* Initialize axis in the report */
370 for (i = 0; i < axis_count; i++)
371 hid_device_set_abs_axis(iface, i, pSDL_JoystickGetAxis(impl->sdl_joystick, i));
372 for (i = 0; i < hat_count; i++)
373 set_hat_value(iface, i, pSDL_JoystickGetHat(impl->sdl_joystick, i));
375 return STATUS_SUCCESS;
378 static NTSTATUS build_controller_report_descriptor(struct unix_device *iface)
380 const USAGE_AND_PAGE device_usage = {.UsagePage = HID_USAGE_PAGE_GENERIC, .Usage = HID_USAGE_GENERIC_GAMEPAD};
381 static const USAGE left_axis_usages[] = {HID_USAGE_GENERIC_X, HID_USAGE_GENERIC_Y};
382 static const USAGE right_axis_usages[] = {HID_USAGE_GENERIC_RX, HID_USAGE_GENERIC_RY};
383 static const USAGE trigger_axis_usages[] = {HID_USAGE_GENERIC_Z, HID_USAGE_GENERIC_RZ};
384 struct sdl_device *impl = impl_from_unix_device(iface);
385 ULONG i, button_count = SDL_CONTROLLER_BUTTON_MAX - 1;
386 BOOL state;
388 C_ASSERT(SDL_CONTROLLER_AXIS_MAX == 6);
390 if (!hid_device_begin_report_descriptor(iface, &device_usage))
391 return STATUS_NO_MEMORY;
393 if (!hid_device_begin_input_report(iface, &device_usage))
394 return STATUS_NO_MEMORY;
396 if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, left_axis_usages,
397 FALSE, -32768, 32767))
398 return STATUS_NO_MEMORY;
400 if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, right_axis_usages,
401 FALSE, -32768, 32767))
402 return STATUS_NO_MEMORY;
404 if (!hid_device_add_axes(iface, 2, HID_USAGE_PAGE_GENERIC, trigger_axis_usages,
405 FALSE, 0, 32767))
406 return STATUS_NO_MEMORY;
408 if (!hid_device_add_hatswitch(iface, 1))
409 return STATUS_NO_MEMORY;
411 if (!hid_device_add_buttons(iface, HID_USAGE_PAGE_BUTTON, 1, button_count))
412 return STATUS_NO_MEMORY;
414 if (!hid_device_end_input_report(iface))
415 return STATUS_NO_MEMORY;
417 if (!descriptor_add_haptic(impl))
418 return STATUS_NO_MEMORY;
420 if (!hid_device_end_report_descriptor(iface))
421 return STATUS_NO_MEMORY;
423 /* Initialize axis in the report */
424 for (i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++)
425 hid_device_set_abs_axis(iface, i, pSDL_GameControllerGetAxis(impl->sdl_controller, i));
427 state = pSDL_GameControllerGetButton(impl->sdl_controller, SDL_CONTROLLER_BUTTON_DPAD_UP);
428 hid_device_move_hatswitch(iface, 0, 0, state ? -1 : +1);
429 state = pSDL_GameControllerGetButton(impl->sdl_controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN);
430 hid_device_move_hatswitch(iface, 0, 0, state ? +1 : -1);
431 state = pSDL_GameControllerGetButton(impl->sdl_controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT);
432 hid_device_move_hatswitch(iface, 0, state ? -1 : +1, 0);
433 state = pSDL_GameControllerGetButton(impl->sdl_controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
434 hid_device_move_hatswitch(iface, 0, state ? +1 : -1, 0);
436 return STATUS_SUCCESS;
439 static void sdl_device_destroy(struct unix_device *iface)
443 static NTSTATUS sdl_device_start(struct unix_device *iface)
445 struct sdl_device *impl = impl_from_unix_device(iface);
446 NTSTATUS status;
448 pthread_mutex_lock(&sdl_cs);
450 if (impl->sdl_controller) status = build_controller_report_descriptor(iface);
451 else status = build_joystick_report_descriptor(iface);
452 impl->started = !status;
454 pthread_mutex_unlock(&sdl_cs);
456 return status;
459 static void sdl_device_stop(struct unix_device *iface)
461 struct sdl_device *impl = impl_from_unix_device(iface);
463 pSDL_JoystickClose(impl->sdl_joystick);
464 if (impl->sdl_controller) pSDL_GameControllerClose(impl->sdl_controller);
465 if (impl->sdl_haptic) pSDL_HapticClose(impl->sdl_haptic);
467 pthread_mutex_lock(&sdl_cs);
468 impl->started = FALSE;
469 list_remove(&impl->unix_device.entry);
470 pthread_mutex_unlock(&sdl_cs);
473 static NTSTATUS sdl_device_haptics_start(struct unix_device *iface, UINT duration_ms,
474 USHORT rumble_intensity, USHORT buzz_intensity,
475 USHORT left_intensity, USHORT right_intensity)
477 struct sdl_device *impl = impl_from_unix_device(iface);
479 TRACE("iface %p, duration_ms %u, rumble_intensity %u, buzz_intensity %u, left_intensity %u, right_intensity %u.\n",
480 iface, duration_ms, rumble_intensity, buzz_intensity, left_intensity, right_intensity);
482 if (!(impl->effect_support & EFFECT_SUPPORT_HAPTICS)) return STATUS_NOT_SUPPORTED;
484 if (impl->effect_support & WINE_SDL_JOYSTICK_RUMBLE)
486 pSDL_JoystickRumble(impl->sdl_joystick, rumble_intensity, buzz_intensity, duration_ms);
487 if (pSDL_JoystickRumbleTriggers)
488 pSDL_JoystickRumbleTriggers(impl->sdl_joystick, left_intensity, right_intensity, duration_ms);
490 else if (impl->effect_support & SDL_HAPTIC_LEFTRIGHT)
492 SDL_HapticEffect effect;
494 memset(&effect, 0, sizeof(SDL_HapticEffect));
495 effect.type = SDL_HAPTIC_LEFTRIGHT;
496 effect.leftright.length = duration_ms;
497 effect.leftright.large_magnitude = rumble_intensity;
498 effect.leftright.small_magnitude = buzz_intensity;
500 if (impl->haptic_effect_id >= 0)
501 pSDL_HapticDestroyEffect(impl->sdl_haptic, impl->haptic_effect_id);
502 impl->haptic_effect_id = pSDL_HapticNewEffect(impl->sdl_haptic, &effect);
503 if (impl->haptic_effect_id >= 0)
504 pSDL_HapticRunEffect(impl->sdl_haptic, impl->haptic_effect_id, 1);
506 else if (impl->effect_support & WINE_SDL_HAPTIC_RUMBLE)
508 float magnitude = (rumble_intensity + buzz_intensity) / 2.0 / 32767.0;
509 pSDL_HapticRumblePlay(impl->sdl_haptic, magnitude, duration_ms);
512 return STATUS_SUCCESS;
515 static NTSTATUS sdl_device_haptics_stop(struct unix_device *iface)
517 struct sdl_device *impl = impl_from_unix_device(iface);
519 TRACE("iface %p.\n", iface);
521 if (impl->effect_support & WINE_SDL_JOYSTICK_RUMBLE)
523 pSDL_JoystickRumble(impl->sdl_joystick, 0, 0, 0);
524 if (pSDL_JoystickRumbleTriggers)
525 pSDL_JoystickRumbleTriggers(impl->sdl_joystick, 0, 0, 0);
527 else if (impl->effect_support & SDL_HAPTIC_LEFTRIGHT)
528 pSDL_HapticStopAll(impl->sdl_haptic);
529 else if (impl->effect_support & WINE_SDL_HAPTIC_RUMBLE)
530 pSDL_HapticRumbleStop(impl->sdl_haptic);
532 return STATUS_SUCCESS;
535 static NTSTATUS sdl_device_physical_device_control(struct unix_device *iface, USAGE control)
537 struct sdl_device *impl = impl_from_unix_device(iface);
538 unsigned int i;
540 TRACE("iface %p, control %#04x.\n", iface, control);
542 switch (control)
544 case PID_USAGE_DC_ENABLE_ACTUATORS:
545 pSDL_HapticSetGain(impl->sdl_haptic, 100);
546 InterlockedOr(&impl->effect_flags, EFFECT_STATE_ACTUATORS_ENABLED);
547 return STATUS_SUCCESS;
548 case PID_USAGE_DC_DISABLE_ACTUATORS:
549 pSDL_HapticSetGain(impl->sdl_haptic, 0);
550 InterlockedAnd(&impl->effect_flags, ~EFFECT_STATE_ACTUATORS_ENABLED);
551 return STATUS_SUCCESS;
552 case PID_USAGE_DC_STOP_ALL_EFFECTS:
553 pSDL_HapticStopAll(impl->sdl_haptic);
554 pSDL_HapticSetAutocenter(impl->sdl_haptic, 0);
555 return STATUS_SUCCESS;
556 case PID_USAGE_DC_DEVICE_RESET:
557 pSDL_HapticStopAll(impl->sdl_haptic);
558 for (i = 0; i < ARRAY_SIZE(impl->effect_ids); ++i)
560 if (impl->effect_ids[i] < 0) continue;
561 pSDL_HapticDestroyEffect(impl->sdl_haptic, impl->effect_ids[i]);
562 impl->effect_ids[i] = -1;
564 pSDL_HapticSetAutocenter(impl->sdl_haptic, 100);
565 return STATUS_SUCCESS;
566 case PID_USAGE_DC_DEVICE_PAUSE:
567 pSDL_HapticPause(impl->sdl_haptic);
568 InterlockedOr(&impl->effect_flags, EFFECT_STATE_DEVICE_PAUSED);
569 return STATUS_SUCCESS;
570 case PID_USAGE_DC_DEVICE_CONTINUE:
571 pSDL_HapticUnpause(impl->sdl_haptic);
572 InterlockedAnd(&impl->effect_flags, ~EFFECT_STATE_DEVICE_PAUSED);
573 return STATUS_SUCCESS;
576 return STATUS_NOT_SUPPORTED;
579 static NTSTATUS sdl_device_physical_device_set_gain(struct unix_device *iface, BYTE percent)
581 struct sdl_device *impl = impl_from_unix_device(iface);
583 TRACE("iface %p, percent %#x.\n", iface, percent);
585 pSDL_HapticSetGain(impl->sdl_haptic, percent);
587 return STATUS_SUCCESS;
590 static NTSTATUS sdl_device_physical_effect_control(struct unix_device *iface, BYTE index,
591 USAGE control, BYTE iterations)
593 struct sdl_device *impl = impl_from_unix_device(iface);
594 int id = impl->effect_ids[index];
596 TRACE("iface %p, index %u, control %04x, iterations %u.\n", iface, index, control, iterations);
598 if (impl->effect_ids[index] < 0) return STATUS_UNSUCCESSFUL;
600 switch (control)
602 case PID_USAGE_OP_EFFECT_START_SOLO:
603 pSDL_HapticStopAll(impl->sdl_haptic);
604 /* fallthrough */
605 case PID_USAGE_OP_EFFECT_START:
606 pSDL_HapticRunEffect(impl->sdl_haptic, id, (iterations == 0xff ? SDL_HAPTIC_INFINITY : iterations));
607 break;
608 case PID_USAGE_OP_EFFECT_STOP:
609 pSDL_HapticStopEffect(impl->sdl_haptic, id);
610 break;
613 return STATUS_SUCCESS;
616 static NTSTATUS set_effect_type_from_usage(SDL_HapticEffect *effect, USAGE type)
618 switch (type)
620 case PID_USAGE_ET_SINE:
621 effect->type = SDL_HAPTIC_SINE;
622 return STATUS_SUCCESS;
623 case PID_USAGE_ET_TRIANGLE:
624 effect->type = SDL_HAPTIC_TRIANGLE;
625 return STATUS_SUCCESS;
626 case PID_USAGE_ET_SAWTOOTH_UP:
627 effect->type = SDL_HAPTIC_SAWTOOTHUP;
628 return STATUS_SUCCESS;
629 case PID_USAGE_ET_SAWTOOTH_DOWN:
630 effect->type = SDL_HAPTIC_SAWTOOTHDOWN;
631 return STATUS_SUCCESS;
632 case PID_USAGE_ET_SPRING:
633 effect->type = SDL_HAPTIC_SPRING;
634 return STATUS_SUCCESS;
635 case PID_USAGE_ET_DAMPER:
636 effect->type = SDL_HAPTIC_DAMPER;
637 return STATUS_SUCCESS;
638 case PID_USAGE_ET_INERTIA:
639 effect->type = SDL_HAPTIC_INERTIA;
640 return STATUS_SUCCESS;
641 case PID_USAGE_ET_FRICTION:
642 effect->type = SDL_HAPTIC_FRICTION;
643 return STATUS_SUCCESS;
644 case PID_USAGE_ET_CONSTANT_FORCE:
645 effect->type = SDL_HAPTIC_CONSTANT;
646 return STATUS_SUCCESS;
647 case PID_USAGE_ET_RAMP:
648 effect->type = SDL_HAPTIC_RAMP;
649 return STATUS_SUCCESS;
650 case PID_USAGE_ET_CUSTOM_FORCE_DATA:
651 effect->type = SDL_HAPTIC_CUSTOM;
652 return STATUS_SUCCESS;
653 default:
654 return STATUS_NOT_SUPPORTED;
658 static NTSTATUS sdl_device_physical_effect_update(struct unix_device *iface, BYTE index,
659 struct effect_params *params)
661 struct sdl_device *impl = impl_from_unix_device(iface);
662 int id = impl->effect_ids[index];
663 SDL_HapticEffect effect = {0};
664 INT16 direction;
665 NTSTATUS status;
667 TRACE("iface %p, index %u, params %p.\n", iface, index, params);
669 if (params->effect_type == PID_USAGE_UNDEFINED) return STATUS_SUCCESS;
670 if ((status = set_effect_type_from_usage(&effect, params->effect_type))) return status;
672 /* The first direction we get from PID is in polar coordinate space, so we need to
673 * remove 90° to make it match SDL spherical coordinates. */
674 direction = (params->direction[0] - 9000) % 36000;
675 if (direction < 0) direction += 36000;
677 switch (params->effect_type)
679 case PID_USAGE_ET_SINE:
680 case PID_USAGE_ET_SQUARE:
681 case PID_USAGE_ET_TRIANGLE:
682 case PID_USAGE_ET_SAWTOOTH_UP:
683 case PID_USAGE_ET_SAWTOOTH_DOWN:
684 effect.periodic.length = (params->duration == 0xffff ? SDL_HAPTIC_INFINITY : params->duration);
685 effect.periodic.delay = params->start_delay;
686 effect.periodic.button = params->trigger_button;
687 effect.periodic.interval = params->trigger_repeat_interval;
688 effect.periodic.direction.type = SDL_HAPTIC_SPHERICAL;
689 effect.periodic.direction.dir[0] = direction;
690 effect.periodic.direction.dir[1] = params->direction[1];
691 effect.periodic.period = params->periodic.period;
692 effect.periodic.magnitude = (params->periodic.magnitude * params->gain_percent) / 100;
693 effect.periodic.offset = params->periodic.offset;
694 effect.periodic.phase = params->periodic.phase;
695 effect.periodic.attack_length = params->envelope.attack_time;
696 effect.periodic.attack_level = params->envelope.attack_level;
697 effect.periodic.fade_length = params->envelope.fade_time;
698 effect.periodic.fade_level = params->envelope.fade_level;
699 break;
701 case PID_USAGE_ET_SPRING:
702 case PID_USAGE_ET_DAMPER:
703 case PID_USAGE_ET_INERTIA:
704 case PID_USAGE_ET_FRICTION:
705 effect.condition.length = (params->duration == 0xffff ? SDL_HAPTIC_INFINITY : params->duration);
706 effect.condition.delay = params->start_delay;
707 effect.condition.button = params->trigger_button;
708 effect.condition.interval = params->trigger_repeat_interval;
709 effect.condition.direction.type = SDL_HAPTIC_SPHERICAL;
710 effect.condition.direction.dir[0] = direction;
711 effect.condition.direction.dir[1] = params->direction[1];
712 if (params->condition_count >= 1)
714 effect.condition.right_sat[0] = params->condition[0].positive_saturation;
715 effect.condition.left_sat[0] = params->condition[0].negative_saturation;
716 effect.condition.right_coeff[0] = params->condition[0].positive_coefficient;
717 effect.condition.left_coeff[0] = params->condition[0].negative_coefficient;
718 effect.condition.deadband[0] = params->condition[0].dead_band;
719 effect.condition.center[0] = params->condition[0].center_point_offset;
721 if (params->condition_count >= 2)
723 effect.condition.right_sat[1] = params->condition[1].positive_saturation;
724 effect.condition.left_sat[1] = params->condition[1].negative_saturation;
725 effect.condition.right_coeff[1] = params->condition[1].positive_coefficient;
726 effect.condition.left_coeff[1] = params->condition[1].negative_coefficient;
727 effect.condition.deadband[1] = params->condition[1].dead_band;
728 effect.condition.center[1] = params->condition[1].center_point_offset;
730 break;
732 case PID_USAGE_ET_CONSTANT_FORCE:
733 effect.constant.length = (params->duration == 0xffff ? SDL_HAPTIC_INFINITY : params->duration);
734 effect.constant.delay = params->start_delay;
735 effect.constant.button = params->trigger_button;
736 effect.constant.interval = params->trigger_repeat_interval;
737 effect.constant.direction.type = SDL_HAPTIC_SPHERICAL;
738 effect.constant.direction.dir[0] = direction;
739 effect.constant.direction.dir[1] = params->direction[1];
740 effect.constant.level = (params->constant_force.magnitude * params->gain_percent) / 100;
741 effect.constant.attack_length = params->envelope.attack_time;
742 effect.constant.attack_level = params->envelope.attack_level;
743 effect.constant.fade_length = params->envelope.fade_time;
744 effect.constant.fade_level = params->envelope.fade_level;
745 break;
747 /* According to the SDL documentation, ramp effect doesn't
748 * support SDL_HAPTIC_INFINITY. */
749 case PID_USAGE_ET_RAMP:
750 effect.ramp.length = params->duration;
751 effect.ramp.delay = params->start_delay;
752 effect.ramp.button = params->trigger_button;
753 effect.ramp.interval = params->trigger_repeat_interval;
754 effect.ramp.direction.type = SDL_HAPTIC_SPHERICAL;
755 effect.ramp.direction.dir[0] = params->direction[0];
756 effect.ramp.direction.dir[1] = params->direction[1];
757 effect.ramp.start = (params->ramp_force.ramp_start * params->gain_percent) / 100;
758 effect.ramp.end = (params->ramp_force.ramp_end * params->gain_percent) / 100;
759 effect.ramp.attack_length = params->envelope.attack_time;
760 effect.ramp.attack_level = params->envelope.attack_level;
761 effect.ramp.fade_length = params->envelope.fade_time;
762 effect.ramp.fade_level = params->envelope.fade_level;
763 break;
765 case PID_USAGE_ET_CUSTOM_FORCE_DATA:
766 FIXME("not implemented!\n");
767 break;
770 if (id < 0) impl->effect_ids[index] = pSDL_HapticNewEffect(impl->sdl_haptic, &effect);
771 else pSDL_HapticUpdateEffect(impl->sdl_haptic, id, &effect);
773 return STATUS_SUCCESS;
776 static const struct hid_device_vtbl sdl_device_vtbl =
778 sdl_device_destroy,
779 sdl_device_start,
780 sdl_device_stop,
781 sdl_device_haptics_start,
782 sdl_device_haptics_stop,
783 sdl_device_physical_device_control,
784 sdl_device_physical_device_set_gain,
785 sdl_device_physical_effect_control,
786 sdl_device_physical_effect_update,
789 static void check_device_effects_state(struct sdl_device *impl)
791 struct unix_device *iface = &impl->unix_device;
792 struct hid_effect_state *effect_state = &iface->hid_physical.effect_state;
793 ULONG effect_flags = InterlockedOr(&impl->effect_flags, 0);
794 unsigned int i, ret;
796 if (!impl->sdl_haptic) return;
797 if (!(impl->effect_support & EFFECT_SUPPORT_PHYSICAL)) return;
799 for (i = 0; i < ARRAY_SIZE(impl->effect_ids); ++i)
801 if (impl->effect_ids[i] == -1) continue;
802 if (!(impl->effect_support & SDL_HAPTIC_STATUS)) ret = 1;
803 else ret = pSDL_HapticGetEffectStatus(impl->sdl_haptic, impl->effect_ids[i]);
804 if (impl->effect_state[i] == ret) continue;
805 impl->effect_state[i] = ret;
806 hid_device_set_effect_state(iface, i, effect_flags | (ret == 1 ? EFFECT_STATE_EFFECT_PLAYING : 0));
807 bus_event_queue_input_report(&event_queue, iface, effect_state->report_buf, effect_state->report_len);
811 static void check_all_devices_effects_state(void)
813 static UINT last_ticks = 0;
814 UINT ticks = pSDL_GetTicks();
815 struct sdl_device *impl;
817 if (ticks - last_ticks < 10) return;
818 last_ticks = ticks;
820 pthread_mutex_lock(&sdl_cs);
821 LIST_FOR_EACH_ENTRY(impl, &device_list, struct sdl_device, unix_device.entry)
822 check_device_effects_state(impl);
823 pthread_mutex_unlock(&sdl_cs);
826 static BOOL set_report_from_joystick_event(struct sdl_device *impl, SDL_Event *event)
828 struct unix_device *iface = &impl->unix_device;
829 struct hid_device_state *state = &iface->hid_device_state;
831 if (impl->sdl_controller) return TRUE; /* use controller events instead */
833 switch (event->type)
835 case SDL_JOYBUTTONDOWN:
836 case SDL_JOYBUTTONUP:
838 SDL_JoyButtonEvent *ie = &event->jbutton;
840 hid_device_set_button(iface, ie->button, ie->state);
841 bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len);
842 break;
844 case SDL_JOYAXISMOTION:
846 SDL_JoyAxisEvent *ie = &event->jaxis;
848 if (!hid_device_set_abs_axis(iface, ie->axis, ie->value)) break;
849 bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len);
850 break;
852 case SDL_JOYBALLMOTION:
854 SDL_JoyBallEvent *ie = &event->jball;
856 if (!hid_device_set_rel_axis(iface, 2 * ie->ball, ie->xrel)) break;
857 hid_device_set_rel_axis(iface, 2 * ie->ball + 1, ie->yrel);
858 bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len);
859 break;
861 case SDL_JOYHATMOTION:
863 SDL_JoyHatEvent *ie = &event->jhat;
865 set_hat_value(iface, ie->hat, ie->value);
866 bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len);
867 break;
869 default:
870 ERR("TODO: Process Report (0x%x)\n",event->type);
873 check_device_effects_state(impl);
874 return FALSE;
877 static BOOL set_report_from_controller_event(struct sdl_device *impl, SDL_Event *event)
879 struct unix_device *iface = &impl->unix_device;
880 struct hid_device_state *state = &iface->hid_device_state;
882 switch (event->type)
884 case SDL_CONTROLLERBUTTONDOWN:
885 case SDL_CONTROLLERBUTTONUP:
887 SDL_ControllerButtonEvent *ie = &event->cbutton;
888 int button;
890 switch ((button = ie->button))
892 case SDL_CONTROLLER_BUTTON_DPAD_UP:
893 hid_device_move_hatswitch(iface, 0, 0, ie->state ? -1 : +1);
894 break;
895 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
896 hid_device_move_hatswitch(iface, 0, 0, ie->state ? +1 : -1);
897 break;
898 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
899 hid_device_move_hatswitch(iface, 0, ie->state ? -1 : +1, 0);
900 break;
901 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
902 hid_device_move_hatswitch(iface, 0, ie->state ? +1 : -1, 0);
903 break;
904 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: button = 4; break;
905 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER: button = 5; break;
906 case SDL_CONTROLLER_BUTTON_BACK: button = 6; break;
907 case SDL_CONTROLLER_BUTTON_START: button = 7; break;
908 case SDL_CONTROLLER_BUTTON_LEFTSTICK: button = 8; break;
909 case SDL_CONTROLLER_BUTTON_RIGHTSTICK: button = 9; break;
910 case SDL_CONTROLLER_BUTTON_GUIDE: button = 10; break;
913 hid_device_set_button(iface, button, ie->state);
914 bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len);
915 break;
917 case SDL_CONTROLLERAXISMOTION:
919 SDL_ControllerAxisEvent *ie = &event->caxis;
921 hid_device_set_abs_axis(iface, ie->axis, ie->value);
922 bus_event_queue_input_report(&event_queue, iface, state->report_buf, state->report_len);
923 break;
925 default:
926 ERR("TODO: Process Report (%x)\n",event->type);
929 check_device_effects_state(impl);
930 return FALSE;
933 static void sdl_add_device(unsigned int index)
935 struct device_desc desc =
937 .input = -1,
938 .manufacturer = {'S','D','L',0},
939 .serialnumber = {'0','0','0','0',0},
941 struct sdl_device *impl;
943 SDL_Joystick* joystick;
944 SDL_JoystickID id;
945 SDL_GameController *controller = NULL;
946 const char *product, *sdl_serial;
947 char guid_str[33], buffer[ARRAY_SIZE(desc.product)];
948 int axis_count, axis_offset;
950 if ((joystick = pSDL_JoystickOpen(index)) == NULL)
952 WARN("Unable to open sdl device %i: %s\n", index, pSDL_GetError());
953 return;
956 if (options.map_controllers && pSDL_IsGameController(index))
957 controller = pSDL_GameControllerOpen(index);
959 if (controller) product = pSDL_GameControllerName(controller);
960 else product = pSDL_JoystickName(joystick);
961 if (!product) product = "Joystick";
963 id = pSDL_JoystickInstanceID(joystick);
965 if (pSDL_JoystickGetProductVersion != NULL) {
966 desc.vid = pSDL_JoystickGetVendor(joystick);
967 desc.pid = pSDL_JoystickGetProduct(joystick);
968 desc.version = pSDL_JoystickGetProductVersion(joystick);
970 else
972 desc.vid = 0x01;
973 desc.pid = pSDL_JoystickInstanceID(joystick) + 1;
974 desc.version = 0;
977 if (pSDL_JoystickGetSerial && (sdl_serial = pSDL_JoystickGetSerial(joystick)))
979 ntdll_umbstowcs(sdl_serial, strlen(sdl_serial) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber));
981 else
983 /* Overcooked! All You Can Eat only adds controllers with unique serial numbers
984 * Prefer keeping serial numbers unique over keeping them consistent across runs */
985 pSDL_JoystickGetGUIDString(pSDL_JoystickGetGUID(joystick), guid_str, sizeof(guid_str));
986 snprintf(buffer, sizeof(buffer), "%s.%d", guid_str, index);
987 TRACE("Making up serial number for %s: %s\n", product, buffer);
988 ntdll_umbstowcs(buffer, strlen(buffer) + 1, desc.serialnumber, ARRAY_SIZE(desc.serialnumber));
991 if (controller)
993 desc.is_gamepad = TRUE;
994 desc.usages.UsagePage = HID_USAGE_PAGE_GENERIC;
995 desc.usages.Usage = HID_USAGE_GENERIC_GAMEPAD;
996 axis_count = 6;
998 else
1000 int button_count = pSDL_JoystickNumButtons(joystick);
1001 axis_count = pSDL_JoystickNumAxes(joystick);
1002 desc.is_gamepad = (axis_count == 6 && button_count >= 14);
1003 desc.usages.UsagePage = HID_USAGE_PAGE_GENERIC;
1004 desc.usages.Usage = HID_USAGE_GENERIC_JOYSTICK;
1007 for (axis_offset = 0; axis_offset < axis_count; axis_offset += (options.split_controllers ? 6 : axis_count))
1009 if (!axis_offset) strcpy(buffer, product);
1010 else snprintf(buffer, ARRAY_SIZE(buffer), "%s %d", product, axis_offset / 6);
1011 ntdll_umbstowcs(buffer, strlen(buffer) + 1, desc.product, ARRAY_SIZE(desc.product));
1013 TRACE("%s id %d, axis_offset %u, desc %s.\n", controller ? "controller" : "joystick", id, axis_offset, debugstr_device_desc(&desc));
1015 if (!(impl = hid_device_create(&sdl_device_vtbl, sizeof(struct sdl_device)))) return;
1016 list_add_tail(&device_list, &impl->unix_device.entry);
1017 impl->sdl_joystick = joystick;
1018 impl->sdl_controller = controller;
1019 impl->id = id;
1020 impl->axis_offset = axis_offset;
1022 bus_event_queue_device_created(&event_queue, &impl->unix_device, &desc);
1026 static void process_device_event(SDL_Event *event)
1028 struct sdl_device *impl;
1029 SDL_JoystickID id;
1031 TRACE("Received action %x\n", event->type);
1033 pthread_mutex_lock(&sdl_cs);
1035 if (event->type == SDL_JOYDEVICEADDED)
1036 sdl_add_device(((SDL_JoyDeviceEvent *)event)->which);
1037 else if (event->type == SDL_JOYDEVICEREMOVED)
1039 id = ((SDL_JoyDeviceEvent *)event)->which;
1040 impl = find_device_from_id(id);
1041 if (impl) bus_event_queue_device_removed(&event_queue, &impl->unix_device);
1042 else WARN("Failed to find device with id %d\n", id);
1044 else if (event->type == SDL_JOYAXISMOTION && options.split_controllers)
1046 id = event->jaxis.which;
1047 impl = find_device_from_id_and_axis(id, event->jaxis.axis);
1048 if (!impl) WARN("Failed to find device with id %d for axis %d\n", id, event->jaxis.axis);
1049 else if (!impl->started) WARN("Device %p with id %d is stopped, ignoring event %#x\n", impl, id, event->type);
1050 else
1052 event->jaxis.axis -= impl->axis_offset;
1053 set_report_from_joystick_event(impl, event);
1056 else if (event->type >= SDL_JOYAXISMOTION && event->type <= SDL_JOYBUTTONUP)
1058 id = ((SDL_JoyButtonEvent *)event)->which;
1059 impl = find_device_from_id(id);
1060 if (!impl) WARN("Failed to find device with id %d\n", id);
1061 else if (!impl->started) WARN("Device %p with id %d is stopped, ignoring event %#x\n", impl, id, event->type);
1062 else set_report_from_joystick_event(impl, event);
1064 else if (event->type >= SDL_CONTROLLERAXISMOTION && event->type <= SDL_CONTROLLERBUTTONUP)
1066 id = ((SDL_ControllerButtonEvent *)event)->which;
1067 impl = find_device_from_id(id);
1068 if (!impl) WARN("Failed to find device with id %d\n", id);
1069 else if (!impl->started) WARN("Device %p with id %d is stopped, ignoring event %#x\n", impl, id, event->type);
1070 else set_report_from_controller_event(impl, event);
1073 pthread_mutex_unlock(&sdl_cs);
1076 NTSTATUS sdl_bus_init(void *args)
1078 const char *mapping;
1079 int i;
1081 TRACE("args %p\n", args);
1083 options = *(struct sdl_bus_options *)args;
1085 if (!(sdl_handle = dlopen(SONAME_LIBSDL2, RTLD_NOW)))
1087 WARN("could not load %s\n", SONAME_LIBSDL2);
1088 return STATUS_UNSUCCESSFUL;
1090 #define LOAD_FUNCPTR(f) \
1091 if ((p##f = dlsym(sdl_handle, #f)) == NULL) \
1093 WARN("could not find symbol %s\n", #f); \
1094 goto failed; \
1096 LOAD_FUNCPTR(SDL_GetError);
1097 LOAD_FUNCPTR(SDL_Init);
1098 LOAD_FUNCPTR(SDL_JoystickClose);
1099 LOAD_FUNCPTR(SDL_JoystickEventState);
1100 LOAD_FUNCPTR(SDL_JoystickGetGUID);
1101 LOAD_FUNCPTR(SDL_JoystickGetGUIDString);
1102 LOAD_FUNCPTR(SDL_JoystickInstanceID);
1103 LOAD_FUNCPTR(SDL_JoystickName);
1104 LOAD_FUNCPTR(SDL_JoystickNumAxes);
1105 LOAD_FUNCPTR(SDL_JoystickOpen);
1106 LOAD_FUNCPTR(SDL_WaitEventTimeout);
1107 LOAD_FUNCPTR(SDL_JoystickNumButtons);
1108 LOAD_FUNCPTR(SDL_JoystickNumBalls);
1109 LOAD_FUNCPTR(SDL_JoystickNumHats);
1110 LOAD_FUNCPTR(SDL_JoystickGetAxis);
1111 LOAD_FUNCPTR(SDL_JoystickGetHat);
1112 LOAD_FUNCPTR(SDL_IsGameController);
1113 LOAD_FUNCPTR(SDL_GameControllerClose);
1114 LOAD_FUNCPTR(SDL_GameControllerGetAxis);
1115 LOAD_FUNCPTR(SDL_GameControllerGetButton);
1116 LOAD_FUNCPTR(SDL_GameControllerName);
1117 LOAD_FUNCPTR(SDL_GameControllerOpen);
1118 LOAD_FUNCPTR(SDL_GameControllerEventState);
1119 LOAD_FUNCPTR(SDL_HapticClose);
1120 LOAD_FUNCPTR(SDL_HapticDestroyEffect);
1121 LOAD_FUNCPTR(SDL_HapticGetEffectStatus);
1122 LOAD_FUNCPTR(SDL_HapticNewEffect);
1123 LOAD_FUNCPTR(SDL_HapticOpenFromJoystick);
1124 LOAD_FUNCPTR(SDL_HapticPause);
1125 LOAD_FUNCPTR(SDL_HapticQuery);
1126 LOAD_FUNCPTR(SDL_HapticRumbleInit);
1127 LOAD_FUNCPTR(SDL_HapticRumblePlay);
1128 LOAD_FUNCPTR(SDL_HapticRumbleStop);
1129 LOAD_FUNCPTR(SDL_HapticRumbleSupported);
1130 LOAD_FUNCPTR(SDL_HapticRunEffect);
1131 LOAD_FUNCPTR(SDL_HapticSetGain);
1132 LOAD_FUNCPTR(SDL_HapticSetAutocenter);
1133 LOAD_FUNCPTR(SDL_HapticStopAll);
1134 LOAD_FUNCPTR(SDL_HapticStopEffect);
1135 LOAD_FUNCPTR(SDL_HapticUnpause);
1136 LOAD_FUNCPTR(SDL_HapticUpdateEffect);
1137 LOAD_FUNCPTR(SDL_JoystickIsHaptic);
1138 LOAD_FUNCPTR(SDL_GameControllerAddMapping);
1139 LOAD_FUNCPTR(SDL_RegisterEvents);
1140 LOAD_FUNCPTR(SDL_PushEvent);
1141 LOAD_FUNCPTR(SDL_GetTicks);
1142 #undef LOAD_FUNCPTR
1143 pSDL_JoystickRumble = dlsym(sdl_handle, "SDL_JoystickRumble");
1144 pSDL_JoystickRumbleTriggers = dlsym(sdl_handle, "SDL_JoystickRumbleTriggers");
1145 pSDL_JoystickGetProduct = dlsym(sdl_handle, "SDL_JoystickGetProduct");
1146 pSDL_JoystickGetProductVersion = dlsym(sdl_handle, "SDL_JoystickGetProductVersion");
1147 pSDL_JoystickGetVendor = dlsym(sdl_handle, "SDL_JoystickGetVendor");
1148 pSDL_JoystickGetType = dlsym(sdl_handle, "SDL_JoystickGetType");
1149 pSDL_JoystickGetSerial = dlsym(sdl_handle, "SDL_JoystickGetSerial");
1151 if (pSDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC) < 0)
1153 ERR("could not init SDL: %s\n", pSDL_GetError());
1154 goto failed;
1157 if ((quit_event = pSDL_RegisterEvents(1)) == -1)
1159 ERR("error registering quit event\n");
1160 goto failed;
1163 pSDL_JoystickEventState(SDL_ENABLE);
1164 pSDL_GameControllerEventState(SDL_ENABLE);
1166 /* Process mappings */
1167 if (pSDL_GameControllerAddMapping)
1169 if ((mapping = getenv("SDL_GAMECONTROLLERCONFIG")))
1171 TRACE("Setting environment mapping %s\n", debugstr_a(mapping));
1172 if (pSDL_GameControllerAddMapping(mapping) < 0)
1173 WARN("Failed to add environment mapping %s\n", pSDL_GetError());
1175 else for (i = 0; i < options.mappings_count; ++i)
1177 TRACE("Setting registry mapping %s\n", debugstr_a(options.mappings[i]));
1178 if (pSDL_GameControllerAddMapping(options.mappings[i]) < 0)
1179 WARN("Failed to add registry mapping %s\n", pSDL_GetError());
1183 return STATUS_SUCCESS;
1185 failed:
1186 dlclose(sdl_handle);
1187 sdl_handle = NULL;
1188 return STATUS_UNSUCCESSFUL;
1191 NTSTATUS sdl_bus_wait(void *args)
1193 struct bus_event *result = args;
1194 SDL_Event event;
1196 /* cleanup previously returned event */
1197 bus_event_cleanup(result);
1201 if (bus_event_queue_pop(&event_queue, result)) return STATUS_PENDING;
1202 if (pSDL_WaitEventTimeout(&event, 10) != 0) process_device_event(&event);
1203 else check_all_devices_effects_state();
1204 } while (event.type != quit_event);
1206 TRACE("SDL main loop exiting\n");
1207 bus_event_queue_destroy(&event_queue);
1208 dlclose(sdl_handle);
1209 sdl_handle = NULL;
1210 return STATUS_SUCCESS;
1213 NTSTATUS sdl_bus_stop(void *args)
1215 SDL_Event event;
1217 if (!sdl_handle) return STATUS_SUCCESS;
1219 event.type = quit_event;
1220 if (pSDL_PushEvent(&event) != 1)
1222 ERR("error pushing quit event\n");
1223 return STATUS_UNSUCCESSFUL;
1226 return STATUS_SUCCESS;
1229 #else
1231 NTSTATUS sdl_bus_init(void *args)
1233 WARN("SDL support not compiled in!\n");
1234 return STATUS_NOT_IMPLEMENTED;
1237 NTSTATUS sdl_bus_wait(void *args)
1239 WARN("SDL support not compiled in!\n");
1240 return STATUS_NOT_IMPLEMENTED;
1243 NTSTATUS sdl_bus_stop(void *args)
1245 WARN("SDL support not compiled in!\n");
1246 return STATUS_NOT_IMPLEMENTED;
1249 #endif /* SONAME_LIBSDL2 */