1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 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 #if SDL_HAPTIC_DINPUT || SDL_HAPTIC_XINPUT
24 
25 #include "SDL_assert.h"
26 #include "SDL_thread.h"
27 #include "SDL_mutex.h"
28 #include "SDL_timer.h"
29 #include "SDL_hints.h"
30 #include "SDL_haptic.h"
31 #include "../SDL_syshaptic.h"
32 #include "SDL_joystick.h"
33 #include "../../joystick/SDL_sysjoystick.h"     /* For the real SDL_Joystick */
34 #include "../../joystick/windows/SDL_windowsjoystick_c.h"      /* For joystick hwdata */
35 #include "../../joystick/windows/SDL_xinputjoystick_c.h"      /* For xinput rumble */
36 
37 #include "SDL_windowshaptic_c.h"
38 #include "SDL_dinputhaptic_c.h"
39 #include "SDL_xinputhaptic_c.h"
40 
41 
42 /*
43  * Internal stuff.
44  */
45 SDL_hapticlist_item *SDL_hapticlist = NULL;
46 static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
47 static int numhaptics = 0;
48 
49 
50 /*
51  * Initializes the haptic subsystem.
52  */
53 int
SDL_SYS_HapticInit(void)54 SDL_SYS_HapticInit(void)
55 {
56     if (SDL_DINPUT_HapticInit() < 0) {
57         return -1;
58     }
59     if (SDL_XINPUT_HapticInit() < 0) {
60         return -1;
61     }
62     return numhaptics;
63 }
64 
65 int
SDL_SYS_AddHapticDevice(SDL_hapticlist_item * item)66 SDL_SYS_AddHapticDevice(SDL_hapticlist_item *item)
67 {
68     if (SDL_hapticlist_tail == NULL) {
69         SDL_hapticlist = SDL_hapticlist_tail = item;
70     } else {
71         SDL_hapticlist_tail->next = item;
72         SDL_hapticlist_tail = item;
73     }
74 
75     /* Device has been added. */
76     ++numhaptics;
77 
78     return numhaptics;
79 }
80 
81 int
SDL_SYS_RemoveHapticDevice(SDL_hapticlist_item * prev,SDL_hapticlist_item * item)82 SDL_SYS_RemoveHapticDevice(SDL_hapticlist_item *prev, SDL_hapticlist_item *item)
83 {
84     const int retval = item->haptic ? item->haptic->index : -1;
85     if (prev != NULL) {
86         prev->next = item->next;
87     } else {
88         SDL_assert(SDL_hapticlist == item);
89         SDL_hapticlist = item->next;
90     }
91     if (item == SDL_hapticlist_tail) {
92         SDL_hapticlist_tail = prev;
93     }
94     --numhaptics;
95     /* !!! TODO: Send a haptic remove event? */
96     SDL_free(item);
97     return retval;
98 }
99 
100 int
SDL_SYS_NumHaptics(void)101 SDL_SYS_NumHaptics(void)
102 {
103     return numhaptics;
104 }
105 
106 static SDL_hapticlist_item *
HapticByDevIndex(int device_index)107 HapticByDevIndex(int device_index)
108 {
109     SDL_hapticlist_item *item = SDL_hapticlist;
110 
111     if ((device_index < 0) || (device_index >= numhaptics)) {
112         return NULL;
113     }
114 
115     while (device_index > 0) {
116         SDL_assert(item != NULL);
117         --device_index;
118         item = item->next;
119     }
120     return item;
121 }
122 
123 /*
124  * Return the name of a haptic device, does not need to be opened.
125  */
126 const char *
SDL_SYS_HapticName(int index)127 SDL_SYS_HapticName(int index)
128 {
129     SDL_hapticlist_item *item = HapticByDevIndex(index);
130     return item->name;
131 }
132 
133 /*
134  * Opens a haptic device for usage.
135  */
136 int
SDL_SYS_HapticOpen(SDL_Haptic * haptic)137 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
138 {
139     SDL_hapticlist_item *item = HapticByDevIndex(haptic->index);
140     if (item->bXInputHaptic) {
141         return SDL_XINPUT_HapticOpen(haptic, item);
142     } else {
143         return SDL_DINPUT_HapticOpen(haptic, item);
144     }
145 }
146 
147 
148 /*
149  * Opens a haptic device from first mouse it finds for usage.
150  */
151 int
SDL_SYS_HapticMouse(void)152 SDL_SYS_HapticMouse(void)
153 {
154 #if SDL_HAPTIC_DINPUT
155     SDL_hapticlist_item *item;
156     int index = 0;
157 
158     /* Grab the first mouse haptic device we find. */
159     for (item = SDL_hapticlist; item != NULL; item = item->next) {
160         if (item->capabilities.dwDevType == DI8DEVCLASS_POINTER) {
161             return index;
162         }
163         ++index;
164     }
165 #endif /* SDL_HAPTIC_DINPUT */
166     return -1;
167 }
168 
169 
170 /*
171  * Checks to see if a joystick has haptic features.
172  */
173 int
SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)174 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
175 {
176     if (joystick->driver != &SDL_WINDOWS_JoystickDriver) {
177         return 0;
178     }
179 #if SDL_HAPTIC_XINPUT
180     if (joystick->hwdata->bXInputHaptic) {
181         return 1;
182     }
183 #endif
184 #if SDL_HAPTIC_DINPUT
185     if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
186         return 1;
187     }
188 #endif
189     return 0;
190 }
191 
192 /*
193  * Checks to see if the haptic device and joystick are in reality the same.
194  */
195 int
SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic,SDL_Joystick * joystick)196 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
197 {
198     if (joystick->driver != &SDL_WINDOWS_JoystickDriver) {
199         return 0;
200     }
201     if (joystick->hwdata->bXInputHaptic != haptic->hwdata->bXInputHaptic) {
202         return 0;  /* one is XInput, one is not; not the same device. */
203     } else if (joystick->hwdata->bXInputHaptic) {
204         return SDL_XINPUT_JoystickSameHaptic(haptic, joystick);
205     } else {
206         return SDL_DINPUT_JoystickSameHaptic(haptic, joystick);
207     }
208 }
209 
210 /*
211  * Opens a SDL_Haptic from a SDL_Joystick.
212  */
213 int
SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic,SDL_Joystick * joystick)214 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
215 {
216     SDL_assert(joystick->driver == &SDL_WINDOWS_JoystickDriver);
217 
218     if (joystick->hwdata->bXInputDevice) {
219         return SDL_XINPUT_HapticOpenFromJoystick(haptic, joystick);
220     } else {
221         return SDL_DINPUT_HapticOpenFromJoystick(haptic, joystick);
222     }
223 }
224 
225 /*
226  * Closes the haptic device.
227  */
228 void
SDL_SYS_HapticClose(SDL_Haptic * haptic)229 SDL_SYS_HapticClose(SDL_Haptic * haptic)
230 {
231     if (haptic->hwdata) {
232 
233         /* Free effects. */
234         SDL_free(haptic->effects);
235         haptic->effects = NULL;
236         haptic->neffects = 0;
237 
238         /* Clean up */
239         if (haptic->hwdata->bXInputHaptic) {
240             SDL_XINPUT_HapticClose(haptic);
241         } else {
242             SDL_DINPUT_HapticClose(haptic);
243         }
244 
245         /* Free */
246         SDL_free(haptic->hwdata);
247         haptic->hwdata = NULL;
248     }
249 }
250 
251 /*
252  * Clean up after system specific haptic stuff
253  */
254 void
SDL_SYS_HapticQuit(void)255 SDL_SYS_HapticQuit(void)
256 {
257     SDL_hapticlist_item *item;
258     SDL_hapticlist_item *next = NULL;
259     SDL_Haptic *hapticitem = NULL;
260 
261     extern SDL_Haptic *SDL_haptics;
262     for (hapticitem = SDL_haptics; hapticitem; hapticitem = hapticitem->next) {
263         if ((hapticitem->hwdata->bXInputHaptic) && (hapticitem->hwdata->thread)) {
264             /* we _have_ to stop the thread before we free the XInput DLL! */
265             SDL_AtomicSet(&hapticitem->hwdata->stopThread, 1);
266             SDL_WaitThread(hapticitem->hwdata->thread, NULL);
267             hapticitem->hwdata->thread = NULL;
268         }
269     }
270 
271     for (item = SDL_hapticlist; item; item = next) {
272         /* Opened and not closed haptics are leaked, this is on purpose.
273          * Close your haptic devices after usage. */
274         /* !!! FIXME: (...is leaking on purpose a good idea?) - No, of course not. */
275         next = item->next;
276         SDL_free(item->name);
277         SDL_free(item);
278     }
279 
280     SDL_XINPUT_HapticQuit();
281     SDL_DINPUT_HapticQuit();
282 
283     numhaptics = 0;
284     SDL_hapticlist = NULL;
285     SDL_hapticlist_tail = NULL;
286 }
287 
288 /*
289  * Creates a new haptic effect.
290  */
291 int
SDL_SYS_HapticNewEffect(SDL_Haptic * haptic,struct haptic_effect * effect,SDL_HapticEffect * base)292 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
293                         SDL_HapticEffect * base)
294 {
295     int result;
296 
297     /* Alloc the effect. */
298     effect->hweffect = (struct haptic_hweffect *)
299         SDL_malloc(sizeof(struct haptic_hweffect));
300     if (effect->hweffect == NULL) {
301         SDL_OutOfMemory();
302         return -1;
303     }
304     SDL_zerop(effect->hweffect);
305 
306     if (haptic->hwdata->bXInputHaptic) {
307         result = SDL_XINPUT_HapticNewEffect(haptic, effect, base);
308     } else {
309         result = SDL_DINPUT_HapticNewEffect(haptic, effect, base);
310     }
311     if (result < 0) {
312         SDL_free(effect->hweffect);
313         effect->hweffect = NULL;
314     }
315     return result;
316 }
317 
318 /*
319  * Updates an effect.
320  */
321 int
SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,struct haptic_effect * effect,SDL_HapticEffect * data)322 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
323                            struct haptic_effect *effect,
324                            SDL_HapticEffect * data)
325 {
326     if (haptic->hwdata->bXInputHaptic) {
327         return SDL_XINPUT_HapticUpdateEffect(haptic, effect, data);
328     } else {
329         return SDL_DINPUT_HapticUpdateEffect(haptic, effect, data);
330     }
331 }
332 
333 /*
334  * Runs an effect.
335  */
336 int
SDL_SYS_HapticRunEffect(SDL_Haptic * haptic,struct haptic_effect * effect,Uint32 iterations)337 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
338                         Uint32 iterations)
339 {
340     if (haptic->hwdata->bXInputHaptic) {
341         return SDL_XINPUT_HapticRunEffect(haptic, effect, iterations);
342     } else {
343         return SDL_DINPUT_HapticRunEffect(haptic, effect, iterations);
344     }
345 }
346 
347 /*
348  * Stops an effect.
349  */
350 int
SDL_SYS_HapticStopEffect(SDL_Haptic * haptic,struct haptic_effect * effect)351 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
352 {
353     if (haptic->hwdata->bXInputHaptic) {
354         return SDL_XINPUT_HapticStopEffect(haptic, effect);
355     } else {
356         return SDL_DINPUT_HapticStopEffect(haptic, effect);
357     }
358 }
359 
360 /*
361  * Frees the effect.
362  */
363 void
SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic,struct haptic_effect * effect)364 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
365 {
366     if (haptic->hwdata->bXInputHaptic) {
367         SDL_XINPUT_HapticDestroyEffect(haptic, effect);
368     } else {
369         SDL_DINPUT_HapticDestroyEffect(haptic, effect);
370     }
371     SDL_free(effect->hweffect);
372     effect->hweffect = NULL;
373 }
374 
375 /*
376  * Gets the status of a haptic effect.
377  */
378 int
SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,struct haptic_effect * effect)379 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
380                               struct haptic_effect *effect)
381 {
382     if (haptic->hwdata->bXInputHaptic) {
383         return SDL_XINPUT_HapticGetEffectStatus(haptic, effect);
384     } else {
385         return SDL_DINPUT_HapticGetEffectStatus(haptic, effect);
386     }
387 }
388 
389 /*
390  * Sets the gain.
391  */
392 int
SDL_SYS_HapticSetGain(SDL_Haptic * haptic,int gain)393 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
394 {
395     if (haptic->hwdata->bXInputHaptic) {
396         return SDL_XINPUT_HapticSetGain(haptic, gain);
397     } else {
398         return SDL_DINPUT_HapticSetGain(haptic, gain);
399     }
400 }
401 
402 /*
403  * Sets the autocentering.
404  */
405 int
SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic,int autocenter)406 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
407 {
408     if (haptic->hwdata->bXInputHaptic) {
409         return SDL_XINPUT_HapticSetAutocenter(haptic, autocenter);
410     } else {
411         return SDL_DINPUT_HapticSetAutocenter(haptic, autocenter);
412     }
413 }
414 
415 /*
416  * Pauses the device.
417  */
418 int
SDL_SYS_HapticPause(SDL_Haptic * haptic)419 SDL_SYS_HapticPause(SDL_Haptic * haptic)
420 {
421     if (haptic->hwdata->bXInputHaptic) {
422         return SDL_XINPUT_HapticPause(haptic);
423     } else {
424         return SDL_DINPUT_HapticPause(haptic);
425     }
426 }
427 
428 /*
429  * Pauses the device.
430  */
431 int
SDL_SYS_HapticUnpause(SDL_Haptic * haptic)432 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
433 {
434     if (haptic->hwdata->bXInputHaptic) {
435         return SDL_XINPUT_HapticUnpause(haptic);
436     } else {
437         return SDL_DINPUT_HapticUnpause(haptic);
438     }
439 }
440 
441 /*
442  * Stops all the playing effects on the device.
443  */
444 int
SDL_SYS_HapticStopAll(SDL_Haptic * haptic)445 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
446 {
447     if (haptic->hwdata->bXInputHaptic) {
448         return SDL_XINPUT_HapticStopAll(haptic);
449     } else {
450         return SDL_DINPUT_HapticStopAll(haptic);
451     }
452 }
453 
454 #endif /* SDL_HAPTIC_DINPUT || SDL_HAPTIC_XINPUT */
455 
456 /* vi: set ts=4 sw=4 expandtab: */
457