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
33 #include <sys/types.h>
42 #define WIN32_NO_STATUS
49 #include "ddk/hidtypes.h"
50 #include "ddk/hidsdi.h"
53 #include "wine/debug.h"
55 #include "wine/unixlib.h"
57 #include "unix_private.h"
59 WINE_DEFAULT_DEBUG_CHANNEL(hid
);
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)
137 struct unix_device unix_device
;
139 SDL_Joystick
*sdl_joystick
;
140 SDL_GameController
*sdl_controller
;
144 DWORD effect_support
;
145 SDL_Haptic
*sdl_haptic
;
146 int haptic_effect_id
;
148 int effect_state
[256];
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
;
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
)
182 static void set_hat_value(struct unix_device
*iface
, int index
, int 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
)
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;
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
))
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
))
245 impl
->haptic_effect_id
= -1;
246 for (i
= 0; i
< ARRAY_SIZE(impl
->effect_ids
); ++i
) impl
->effect_ids
[i
] = -1;
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
);
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
;
319 case SDL_JOYSTICK_TYPE_GAMECONTROLLER
:
320 physical_usage
.UsagePage
= HID_USAGE_PAGE_GENERIC
;
321 physical_usage
.Usage
= HID_USAGE_GENERIC_GAMEPAD
;
323 case SDL_JOYSTICK_TYPE_WHEEL
:
324 physical_usage
.UsagePage
= HID_USAGE_PAGE_SIMULATION
;
325 physical_usage
.Usage
= HID_USAGE_SIMULATION_AUTOMOBILE_SIMULATION_DEVICE
;
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
;
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;
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
,
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
);
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
);
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
);
540 TRACE("iface %p, control %#04x.\n", iface
, 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
;
602 case PID_USAGE_OP_EFFECT_START_SOLO
:
603 pSDL_HapticStopAll(impl
->sdl_haptic
);
605 case PID_USAGE_OP_EFFECT_START
:
606 pSDL_HapticRunEffect(impl
->sdl_haptic
, id
, (iterations
== 0xff ? SDL_HAPTIC_INFINITY
: iterations
));
608 case PID_USAGE_OP_EFFECT_STOP
:
609 pSDL_HapticStopEffect(impl
->sdl_haptic
, id
);
613 return STATUS_SUCCESS
;
616 static NTSTATUS
set_effect_type_from_usage(SDL_HapticEffect
*effect
, USAGE 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
;
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};
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
;
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
;
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
;
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
;
765 case PID_USAGE_ET_CUSTOM_FORCE_DATA
:
766 FIXME("not implemented!\n");
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
=
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);
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;
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 */
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
);
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
);
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
);
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
);
870 ERR("TODO: Process Report (0x%x)\n",event
->type
);
873 check_device_effects_state(impl
);
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
;
884 case SDL_CONTROLLERBUTTONDOWN
:
885 case SDL_CONTROLLERBUTTONUP
:
887 SDL_ControllerButtonEvent
*ie
= &event
->cbutton
;
890 switch ((button
= ie
->button
))
892 case SDL_CONTROLLER_BUTTON_DPAD_UP
:
893 hid_device_move_hatswitch(iface
, 0, 0, ie
->state
? -1 : +1);
895 case SDL_CONTROLLER_BUTTON_DPAD_DOWN
:
896 hid_device_move_hatswitch(iface
, 0, 0, ie
->state
? +1 : -1);
898 case SDL_CONTROLLER_BUTTON_DPAD_LEFT
:
899 hid_device_move_hatswitch(iface
, 0, ie
->state
? -1 : +1, 0);
901 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT
:
902 hid_device_move_hatswitch(iface
, 0, ie
->state
? +1 : -1, 0);
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
);
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
);
926 ERR("TODO: Process Report (%x)\n",event
->type
);
929 check_device_effects_state(impl
);
933 static void sdl_add_device(unsigned int index
)
935 struct device_desc desc
=
938 .manufacturer
= {'S','D','L',0},
939 .serialnumber
= {'0','0','0','0',0},
941 struct sdl_device
*impl
;
943 SDL_Joystick
* joystick
;
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());
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
);
973 desc
.pid
= pSDL_JoystickInstanceID(joystick
) + 1;
977 if (pSDL_JoystickGetSerial
&& (sdl_serial
= pSDL_JoystickGetSerial(joystick
)))
979 ntdll_umbstowcs(sdl_serial
, strlen(sdl_serial
) + 1, desc
.serialnumber
, ARRAY_SIZE(desc
.serialnumber
));
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
));
993 desc
.is_gamepad
= TRUE
;
994 desc
.usages
.UsagePage
= HID_USAGE_PAGE_GENERIC
;
995 desc
.usages
.Usage
= HID_USAGE_GENERIC_GAMEPAD
;
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
;
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
;
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
);
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
;
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); \
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
);
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());
1157 if ((quit_event
= pSDL_RegisterEvents(1)) == -1)
1159 ERR("error registering quit event\n");
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
;
1186 dlclose(sdl_handle
);
1188 return STATUS_UNSUCCESSFUL
;
1191 NTSTATUS
sdl_bus_wait(void *args
)
1193 struct bus_event
*result
= args
;
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
);
1210 return STATUS_SUCCESS
;
1213 NTSTATUS
sdl_bus_stop(void *args
)
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
;
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 */