SDL 2.0
SDL_xinputhaptic.c
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "../../SDL_internal.h"
22
23#include "SDL_error.h"
24#include "SDL_haptic.h"
25#include "../SDL_syshaptic.h"
26
27#if SDL_HAPTIC_XINPUT
28
29#include "SDL_assert.h"
30#include "SDL_hints.h"
31#include "SDL_timer.h"
32#include "SDL_windowshaptic_c.h"
33#include "SDL_xinputhaptic_c.h"
34#include "../../core/windows/SDL_xinput.h"
35#include "../../joystick/windows/SDL_windowsjoystick_c.h"
36#include "../../thread/SDL_systhread.h"
37
38/*
39 * Internal stuff.
40 */
41static SDL_bool loaded_xinput = SDL_FALSE;
42
43
44int
46{
48 loaded_xinput = (WIN_LoadXInputDLL() == 0);
49 }
50
51 if (loaded_xinput) {
52 DWORD i;
53 for (i = 0; i < XUSER_MAX_COUNT; i++) {
55 }
56 }
57 return 0;
58}
59
60int
61SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid)
62{
63 const Uint8 userid = (Uint8)dwUserid;
65 XINPUT_VIBRATION state;
66
67 if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) {
68 return -1;
69 }
70
71 /* Make sure we don't already have it */
72 for (item = SDL_hapticlist; item; item = item->next) {
73 if (item->bXInputHaptic && item->userid == userid) {
74 return -1; /* Already added */
75 }
76 }
77
79 if (XINPUTSETSTATE(dwUserid, &state) != ERROR_SUCCESS) {
80 return -1; /* no force feedback on this device. */
81 }
82
84 if (item == NULL) {
85 return SDL_OutOfMemory();
86 }
87
88 SDL_zerop(item);
89
90 /* !!! FIXME: I'm not bothering to query for a real name right now (can we even?) */
91 {
92 char buf[64];
93 SDL_snprintf(buf, sizeof(buf), "XInput Controller #%u", (unsigned int)(userid + 1));
94 item->name = SDL_strdup(buf);
95 }
96
97 if (!item->name) {
98 SDL_free(item);
99 return -1;
100 }
101
102 /* Copy the instance over, useful for creating devices. */
103 item->bXInputHaptic = SDL_TRUE;
104 item->userid = userid;
105
106 return SDL_SYS_AddHapticDevice(item);
107}
108
109int
110SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid)
111{
112 const Uint8 userid = (Uint8)dwUserid;
115
116 if ((!loaded_xinput) || (dwUserid >= XUSER_MAX_COUNT)) {
117 return -1;
118 }
119
120 for (item = SDL_hapticlist; item != NULL; item = item->next) {
121 if (item->bXInputHaptic && item->userid == userid) {
122 /* found it, remove it. */
123 return SDL_SYS_RemoveHapticDevice(prev, item);
124 }
125 prev = item;
126 }
127 return -1;
128}
129
130/* !!! FIXME: this is a hack, remove this later. */
131/* Since XInput doesn't offer a way to vibrate for X time, we hook into
132 * SDL_PumpEvents() to check if it's time to stop vibrating with some
133 * frequency.
134 * In practice, this works for 99% of use cases. But in an ideal world,
135 * we do this in a separate thread so that:
136 * - we aren't bound to when the app chooses to pump the event queue.
137 * - we aren't adding more polling to the event queue
138 * - we can emulate all the haptic effects correctly (start on a delay,
139 * mix multiple effects, etc).
140 *
141 * Mostly, this is here to get rumbling to work, and all the other features
142 * are absent in the XInput path for now. :(
143 */
144static int SDLCALL
145SDL_RunXInputHaptic(void *arg)
146{
147 struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg;
148
149 while (!SDL_AtomicGet(&hwdata->stopThread)) {
150 SDL_Delay(50);
151 SDL_LockMutex(hwdata->mutex);
152 /* If we're currently running and need to stop... */
153 if (hwdata->stopTicks) {
154 if ((hwdata->stopTicks != SDL_HAPTIC_INFINITY) && SDL_TICKS_PASSED(SDL_GetTicks(), hwdata->stopTicks)) {
155 XINPUT_VIBRATION vibration = { 0, 0 };
156 hwdata->stopTicks = 0;
157 XINPUTSETSTATE(hwdata->userid, &vibration);
158 }
159 }
160 SDL_UnlockMutex(hwdata->mutex);
161 }
162
163 return 0;
164}
165
166static int
167SDL_XINPUT_HapticOpenFromUserIndex(SDL_Haptic *haptic, const Uint8 userid)
168{
169 char threadName[32];
170 XINPUT_VIBRATION vibration = { 0, 0 }; /* stop any current vibration */
171 XINPUTSETSTATE(userid, &vibration);
172
173 haptic->supported = SDL_HAPTIC_LEFTRIGHT;
174
175 haptic->neffects = 1;
176 haptic->nplaying = 1;
177
178 /* Prepare effects memory. */
179 haptic->effects = (struct haptic_effect *)
180 SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
181 if (haptic->effects == NULL) {
182 return SDL_OutOfMemory();
183 }
184 /* Clear the memory */
185 SDL_memset(haptic->effects, 0,
186 sizeof(struct haptic_effect) * haptic->neffects);
187
188 haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata));
189 if (haptic->hwdata == NULL) {
190 SDL_free(haptic->effects);
191 haptic->effects = NULL;
192 return SDL_OutOfMemory();
193 }
194 SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
195
196 haptic->hwdata->bXInputHaptic = 1;
197 haptic->hwdata->userid = userid;
198
199 haptic->hwdata->mutex = SDL_CreateMutex();
200 if (haptic->hwdata->mutex == NULL) {
201 SDL_free(haptic->effects);
202 SDL_free(haptic->hwdata);
203 haptic->effects = NULL;
204 return SDL_SetError("Couldn't create XInput haptic mutex");
205 }
206
207 SDL_snprintf(threadName, sizeof(threadName), "SDLXInputDev%d", (int)userid);
208 haptic->hwdata->thread = SDL_CreateThreadInternal(SDL_RunXInputHaptic, threadName, 64 * 1024, haptic->hwdata);
209
210 if (haptic->hwdata->thread == NULL) {
211 SDL_DestroyMutex(haptic->hwdata->mutex);
212 SDL_free(haptic->effects);
213 SDL_free(haptic->hwdata);
214 haptic->effects = NULL;
215 return SDL_SetError("Couldn't create XInput haptic thread");
216 }
217
218 return 0;
219}
220
221int
223{
224 return SDL_XINPUT_HapticOpenFromUserIndex(haptic, item->userid);
225}
226
227int
228SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
229{
230 return (haptic->hwdata->userid == joystick->hwdata->userid);
231}
232
233int
234SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
235{
237 int index = 0;
238
239 /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */
240 for (item = SDL_hapticlist; item != NULL; item = item->next) {
241 if (item->bXInputHaptic && item->userid == joystick->hwdata->userid) {
242 haptic->index = index;
243 return SDL_XINPUT_HapticOpenFromUserIndex(haptic, joystick->hwdata->userid);
244 }
245 ++index;
246 }
247
248 SDL_SetError("Couldn't find joystick in haptic device list");
249 return -1;
250}
251
252void
253SDL_XINPUT_HapticClose(SDL_Haptic * haptic)
254{
255 SDL_AtomicSet(&haptic->hwdata->stopThread, 1);
256 SDL_WaitThread(haptic->hwdata->thread, NULL);
257 SDL_DestroyMutex(haptic->hwdata->mutex);
258}
259
260void
262{
263 if (loaded_xinput) {
264 WIN_UnloadXInputDLL();
265 loaded_xinput = SDL_FALSE;
266 }
267}
268
269int
270SDL_XINPUT_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect, SDL_HapticEffect * base)
271{
272 SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT); /* should catch this at higher level */
273 return SDL_XINPUT_HapticUpdateEffect(haptic, effect, base);
274}
275
276int
278{
279 XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
281 /* SDL_HapticEffect has max magnitude of 32767, XInput expects 65535 max, so multiply */
282 vib->wLeftMotorSpeed = data->leftright.large_magnitude * 2;
283 vib->wRightMotorSpeed = data->leftright.small_magnitude * 2;
284 SDL_LockMutex(haptic->hwdata->mutex);
285 if (haptic->hwdata->stopTicks) { /* running right now? Update it. */
286 XINPUTSETSTATE(haptic->hwdata->userid, vib);
287 }
288 SDL_UnlockMutex(haptic->hwdata->mutex);
289 return 0;
290}
291
292int
294{
295 XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
296 SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT); /* should catch this at higher level */
297 SDL_LockMutex(haptic->hwdata->mutex);
299 haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY;
300 } else if ((!effect->effect.leftright.length) || (!iterations)) {
301 /* do nothing. Effect runs for zero milliseconds. */
302 } else {
303 haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations);
304 if ((haptic->hwdata->stopTicks == SDL_HAPTIC_INFINITY) || (haptic->hwdata->stopTicks == 0)) {
305 haptic->hwdata->stopTicks = 1; /* fix edge cases. */
306 }
307 }
308 SDL_UnlockMutex(haptic->hwdata->mutex);
309 return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1;
310}
311
312int
313SDL_XINPUT_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
314{
315 XINPUT_VIBRATION vibration = { 0, 0 };
316 SDL_LockMutex(haptic->hwdata->mutex);
317 haptic->hwdata->stopTicks = 0;
318 SDL_UnlockMutex(haptic->hwdata->mutex);
319 return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
320}
321
322void
323SDL_XINPUT_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
324{
326}
327
328int
329SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic * haptic, struct haptic_effect *effect)
330{
331 return SDL_Unsupported();
332}
333
334int
335SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain)
336{
337 return SDL_Unsupported();
338}
339
340int
341SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
342{
343 return SDL_Unsupported();
344}
345
346int
347SDL_XINPUT_HapticPause(SDL_Haptic * haptic)
348{
349 return SDL_Unsupported();
350}
351
352int
354{
355 return SDL_Unsupported();
356}
357
358int
360{
361 XINPUT_VIBRATION vibration = { 0, 0 };
362 SDL_LockMutex(haptic->hwdata->mutex);
363 haptic->hwdata->stopTicks = 0;
364 SDL_UnlockMutex(haptic->hwdata->mutex);
365 return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
366}
367
368#else /* !SDL_HAPTIC_XINPUT */
369
370#include "../../core/windows/SDL_windows.h"
371
373
374int
376{
377 return 0;
378}
379
380int
381SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid)
382{
383 return SDL_Unsupported();
384}
385
386int
387SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid)
388{
389 return SDL_Unsupported();
390}
391
392int
394{
395 return SDL_Unsupported();
396}
397
398int
399SDL_XINPUT_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
400{
401 return SDL_Unsupported();
402}
403
404int
405SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
406{
407 return SDL_Unsupported();
408}
409
410void
412{
413}
414
415void
417{
418}
419
420int
422{
423 return SDL_Unsupported();
424}
425
426int
428{
429 return SDL_Unsupported();
430}
431
432int
434{
435 return SDL_Unsupported();
436}
437
438int
440{
441 return SDL_Unsupported();
442}
443
444void
446{
447}
448
449int
451{
452 return SDL_Unsupported();
453}
454
455int
456SDL_XINPUT_HapticSetGain(SDL_Haptic * haptic, int gain)
457{
458 return SDL_Unsupported();
459}
460
461int
462SDL_XINPUT_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
463{
464 return SDL_Unsupported();
465}
466
467int
469{
470 return SDL_Unsupported();
471}
472
473int
475{
476 return SDL_Unsupported();
477}
478
479int
481{
482 return SDL_Unsupported();
483}
484
485#endif /* SDL_HAPTIC_XINPUT */
486
487/* vi: set ts=4 sw=4 expandtab: */
#define SDL_assert(condition)
Definition: SDL_assert.h:169
#define SDL_AtomicSet
#define SDL_SetError
#define SDL_memset
#define SDL_LockMutex
#define SDL_malloc
#define SDL_CreateMutex
#define SDL_free
#define SDL_strdup
#define SDL_Delay
#define SDL_WaitThread
#define SDL_GetHintBoolean
#define SDL_AtomicGet
#define SDL_DestroyMutex
#define SDL_snprintf
#define SDL_UnlockMutex
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
#define SDL_Unsupported()
Definition: SDL_error.h:53
The SDL haptic subsystem allows you to control haptic (force feedback) devices.
#define SDL_HAPTIC_INFINITY
Used to play a device an infinite number of times.
Definition: SDL_haptic.h:352
#define SDL_HAPTIC_LEFTRIGHT
Left/Right effect supported.
Definition: SDL_haptic.h:183
#define SDL_HINT_XINPUT_ENABLED
A variable that lets you disable the detection and use of Xinput gamepad devices.
Definition: SDL_hints.h:428
#define SDLCALL
Definition: SDL_internal.h:49
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLuint index
GLenum GLuint GLenum GLsizei const GLchar * buf
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
uint32_t Uint32
Definition: SDL_stdinc.h:203
#define SDL_zerop(x)
Definition: SDL_stdinc.h:417
uint8_t Uint8
Definition: SDL_stdinc.h:179
SDL_Thread * SDL_CreateThreadInternal(int(*fn)(void *), const char *name, const size_t stacksize, void *data)
Definition: SDL_thread.c:429
Uint32 SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
#define SDL_TICKS_PASSED(A, B)
Compare SDL ticks values, and return true if A has passed B.
Definition: SDL_timer.h:56
struct xkb_state * state
int SDL_SYS_AddHapticDevice(SDL_hapticlist_item *item)
SDL_hapticlist_item * SDL_hapticlist
int SDL_SYS_RemoveHapticDevice(SDL_hapticlist_item *prev, SDL_hapticlist_item *item)
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
int SDL_XINPUT_HapticPause(SDL_Haptic *haptic)
int SDL_XINPUT_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
int SDL_XINPUT_HapticUpdateEffect(SDL_Haptic *haptic, struct haptic_effect *effect, SDL_HapticEffect *data)
void SDL_XINPUT_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
int SDL_XINPUT_HapticSetGain(SDL_Haptic *haptic, int gain)
int SDL_XINPUT_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect, SDL_HapticEffect *base)
int SDL_XINPUT_MaybeRemoveDevice(const DWORD dwUserid)
int SDL_XINPUT_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick)
void SDL_XINPUT_HapticQuit(void)
int SDL_XINPUT_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
void SDL_XINPUT_HapticClose(SDL_Haptic *haptic)
int SDL_XINPUT_MaybeAddDevice(const DWORD dwUserid)
int SDL_XINPUT_HapticOpen(SDL_Haptic *haptic, SDL_hapticlist_item *item)
int SDL_XINPUT_HapticInit(void)
int SDL_XINPUT_HapticStopAll(SDL_Haptic *haptic)
int SDL_XINPUT_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, Uint32 iterations)
int SDL_XINPUT_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
int SDL_XINPUT_HapticGetEffectStatus(SDL_Haptic *haptic, struct haptic_effect *effect)
int SDL_XINPUT_HapticUnpause(SDL_Haptic *haptic)
#define NULL
Definition: begin_code.h:167
struct SDL_hapticlist_item * next
SDL_HapticEffect effect
Definition: SDL_syshaptic.h:32
struct haptic_hweffect * hweffect
Definition: SDL_syshaptic.h:33
SDL_atomic_t stopThread
static SDL_Haptic * haptic
Definition: testhaptic.c:25
static int iterations
Definition: testsprite2.c:45
The generic template for any haptic effect.
Definition: SDL_haptic.h:801
SDL_HapticLeftRight leftright
Definition: SDL_haptic.h:808