zenilib  0.5.3.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
SDL_syshaptic.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2013 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_config.h"
22 
23 #ifdef SDL_HAPTIC_DINPUT
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_dxjoystick_c.h" /* For joystick hwdata */
35 
36 #define MAX_HAPTICS 32
37 
38 /*
39  * List of available haptic devices.
40  */
41 static struct
42 {
43  DIDEVICEINSTANCE instance;
44  char *name;
45  SDL_Haptic *haptic;
46  DIDEVCAPS capabilities;
47  Uint8 bXInputHaptic; /* Supports force feedback via XInput. */
48  Uint8 userid; /* XInput userid index for this joystick */
49 } SDL_hapticlist[MAX_HAPTICS];
50 
51 
52 /*
53  * Haptic system hardware data.
54  */
55 struct haptic_hwdata
56 {
57  LPDIRECTINPUTDEVICE8 device;
58  DWORD axes[3]; /* Axes to use. */
59  SDL_bool is_joystick; /* Device is loaded as joystick. */
60  Uint8 bXInputHaptic; /* Supports force feedback via XInput. */
61  Uint8 userid; /* XInput userid index for this joystick */
63  SDL_mutex *mutex;
64  volatile Uint32 stopTicks;
65  volatile int stopThread;
66 };
67 
68 
69 /*
70  * Haptic system effect data.
71  */
72 struct haptic_hweffect
73 {
74  DIEFFECT effect;
75  LPDIRECTINPUTEFFECT ref;
76  XINPUT_VIBRATION vibration;
77 };
78 
79 
80 /*
81  * Internal stuff.
82  */
83 static SDL_bool coinitialized = SDL_FALSE;
84 static LPDIRECTINPUT8 dinput = NULL;
85 static SDL_bool loaded_xinput = SDL_FALSE;
86 
87 
88 /*
89  * External stuff.
90  */
91 extern HWND SDL_HelperWindow;
92 
93 
94 /*
95  * Prototypes.
96  */
97 static int DI_SetError(const char *str, HRESULT err);
98 static int DI_GUIDIsSame(const GUID * a, const GUID * b);
99 static int SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic,
100  DIDEVICEINSTANCE instance);
101 static int SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic,
102  LPDIRECTINPUTDEVICE8 device8,
103  SDL_bool is_joystick);
104 static int SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, Uint8 userid);
105 static DWORD DIGetTriggerButton(Uint16 button);
106 static int SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir,
107  int naxes);
108 static int SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
110 static void SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type);
111 static REFGUID SDL_SYS_HapticEffectType(SDL_HapticEffect * effect);
112 static int SDLCALL SDL_RunXInputHaptic(void *arg);
113 
114 /* Callbacks. */
115 static BOOL CALLBACK EnumHapticsCallback(const DIDEVICEINSTANCE *
116  pdidInstance, VOID * pContext);
117 static BOOL CALLBACK DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv);
118 
119 
120 /*
121  * Like SDL_SetError but for DX error codes.
122  */
123 static int
124 DI_SetError(const char *str, HRESULT err)
125 {
126  /*
127  SDL_SetError("Haptic: %s - %s: %s", str,
128  DXGetErrorString8A(err), DXGetErrorDescription8A(err));
129  */
130  return SDL_SetError("Haptic error %s", str);
131 }
132 
133 
134 /*
135  * Checks to see if two GUID are the same.
136  */
137 static int
138 DI_GUIDIsSame(const GUID * a, const GUID * b)
139 {
140  return (SDL_memcmp(a, b, sizeof (GUID)) == 0);
141 }
142 
143 
144 /*
145  * Initializes the haptic subsystem.
146  */
147 int
148 SDL_SYS_HapticInit(void)
149 {
150  const char *env = SDL_GetHint(SDL_HINT_XINPUT_ENABLED);
151  HRESULT ret;
152  HINSTANCE instance;
153 
154  if (dinput != NULL) { /* Already open. */
155  return SDL_SetError("Haptic: SubSystem already open.");
156  }
157 
158  /* Clear all the memory. */
159  SDL_memset(SDL_hapticlist, 0, sizeof(SDL_hapticlist));
160 
161  SDL_numhaptics = 0;
162 
163  ret = WIN_CoInitialize();
164  if (FAILED(ret)) {
165  return DI_SetError("Coinitialize", ret);
166  }
167 
168  coinitialized = SDL_TRUE;
169 
170  ret = CoCreateInstance(&CLSID_DirectInput8, NULL, CLSCTX_INPROC_SERVER,
171  &IID_IDirectInput8, (LPVOID) & dinput);
172  if (FAILED(ret)) {
174  return DI_SetError("CoCreateInstance", ret);
175  }
176 
177  /* Because we used CoCreateInstance, we need to Initialize it, first. */
178  instance = GetModuleHandle(NULL);
179  if (instance == NULL) {
181  return SDL_SetError("GetModuleHandle() failed with error code %d.",
182  GetLastError());
183  }
184  ret = IDirectInput8_Initialize(dinput, instance, DIRECTINPUT_VERSION);
185  if (FAILED(ret)) {
187  return DI_SetError("Initializing DirectInput device", ret);
188  }
189 
190  /* Look for haptic devices. */
191  ret = IDirectInput8_EnumDevices(dinput,
192  0,
193  EnumHapticsCallback,
194  NULL,
195  DIEDFL_FORCEFEEDBACK |
196  DIEDFL_ATTACHEDONLY);
197  if (FAILED(ret)) {
199  return DI_SetError("Enumerating DirectInput devices", ret);
200  }
201 
202  if (!env || SDL_atoi(env)) {
203  loaded_xinput = (WIN_LoadXInputDLL() == 0);
204  }
205 
206  if (loaded_xinput) {
207  DWORD i;
208  const SDL_bool bIs14OrLater = (SDL_XInputVersion >= ((1<<16)|4));
209 
210  for (i = 0; (i < SDL_XINPUT_MAX_DEVICES) && (SDL_numhaptics < MAX_HAPTICS); i++) {
211  XINPUT_CAPABILITIES caps;
212  if (XINPUTGETCAPABILITIES(i, XINPUT_FLAG_GAMEPAD, &caps) == ERROR_SUCCESS) {
213  if ((!bIs14OrLater) || (caps.Flags & XINPUT_CAPS_FFB_SUPPORTED)) {
214  /* !!! FIXME: I'm not bothering to query for a real name right now. */
215  char buf[64];
216  SDL_snprintf(buf, sizeof (buf), "XInput Controller #%u", i+1);
217  SDL_hapticlist[SDL_numhaptics].name = SDL_strdup(buf);
218  SDL_hapticlist[SDL_numhaptics].bXInputHaptic = 1;
219  SDL_hapticlist[SDL_numhaptics].userid = (Uint8) i;
220  SDL_numhaptics++;
221  }
222  }
223  }
224  }
225 
226  return SDL_numhaptics;
227 }
228 
229 /*
230  * Callback to find the haptic devices.
231  */
232 static BOOL CALLBACK
233 EnumHapticsCallback(const DIDEVICEINSTANCE * pdidInstance, VOID * pContext)
234 {
235  HRESULT ret;
236  LPDIRECTINPUTDEVICE8 device;
237 
238  /* Copy the instance over, useful for creating devices. */
239  SDL_memcpy(&SDL_hapticlist[SDL_numhaptics].instance, pdidInstance,
240  sizeof(DIDEVICEINSTANCE));
241 
242  /* Open the device */
243  ret = IDirectInput8_CreateDevice(dinput, &pdidInstance->guidInstance,
244  &device, NULL);
245  if (FAILED(ret)) {
246  /* DI_SetError("Creating DirectInput device",ret); */
247  return DIENUM_CONTINUE;
248  }
249 
250  /* Get capabilities. */
251  SDL_hapticlist[SDL_numhaptics].capabilities.dwSize = sizeof(DIDEVCAPS);
252  ret = IDirectInputDevice8_GetCapabilities(device,
253  &SDL_hapticlist[SDL_numhaptics].
254  capabilities);
255  if (FAILED(ret)) {
256  /* DI_SetError("Getting device capabilities",ret); */
257  IDirectInputDevice8_Release(device);
258  return DIENUM_CONTINUE;
259  }
260 
261  /* Copy the name */
262  SDL_hapticlist[SDL_numhaptics].name = WIN_StringToUTF8(SDL_hapticlist[SDL_numhaptics].instance.tszProductName);
263 
264  /* Close up device and count it. */
265  IDirectInputDevice8_Release(device);
266  SDL_numhaptics++;
267 
268  /* Watch out for hard limit. */
269  if (SDL_numhaptics >= MAX_HAPTICS)
270  return DIENUM_STOP;
271 
272  return DIENUM_CONTINUE;
273 }
274 
275 
276 /*
277  * Return the name of a haptic device, does not need to be opened.
278  */
279 const char *
281 {
282  return SDL_hapticlist[index].name;
283 }
284 
285 
286 /*
287  * Callback to get all supported effects.
288  */
289 #define EFFECT_TEST(e,s) \
290 if (DI_GUIDIsSame(&pei->guid, &(e))) \
291  haptic->supported |= (s)
292 static BOOL CALLBACK
293 DI_EffectCallback(LPCDIEFFECTINFO pei, LPVOID pv)
294 {
295  /* Prepare the haptic device. */
296  SDL_Haptic *haptic = (SDL_Haptic *) pv;
297 
298  /* Get supported. */
299  EFFECT_TEST(GUID_Spring, SDL_HAPTIC_SPRING);
300  EFFECT_TEST(GUID_Damper, SDL_HAPTIC_DAMPER);
301  EFFECT_TEST(GUID_Inertia, SDL_HAPTIC_INERTIA);
302  EFFECT_TEST(GUID_Friction, SDL_HAPTIC_FRICTION);
303  EFFECT_TEST(GUID_ConstantForce, SDL_HAPTIC_CONSTANT);
304  EFFECT_TEST(GUID_CustomForce, SDL_HAPTIC_CUSTOM);
305  EFFECT_TEST(GUID_Sine, SDL_HAPTIC_SINE);
306  /* !!! FIXME: put this back when we have more bits in 2.1 */
307  /* EFFECT_TEST(GUID_Square, SDL_HAPTIC_SQUARE); */
308  EFFECT_TEST(GUID_Triangle, SDL_HAPTIC_TRIANGLE);
309  EFFECT_TEST(GUID_SawtoothUp, SDL_HAPTIC_SAWTOOTHUP);
310  EFFECT_TEST(GUID_SawtoothDown, SDL_HAPTIC_SAWTOOTHDOWN);
311  EFFECT_TEST(GUID_RampForce, SDL_HAPTIC_RAMP);
312 
313  /* Check for more. */
314  return DIENUM_CONTINUE;
315 }
316 
317 
318 /*
319  * Callback to get supported axes.
320  */
321 static BOOL CALLBACK
322 DI_DeviceObjectCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
323 {
324  SDL_Haptic *haptic = (SDL_Haptic *) pvRef;
325 
326  if ((dev->dwType & DIDFT_AXIS) && (dev->dwFlags & DIDOI_FFACTUATOR)) {
327 
328  haptic->hwdata->axes[haptic->naxes] = dev->dwOfs;
329  haptic->naxes++;
330 
331  /* Currently using the artificial limit of 3 axes. */
332  if (haptic->naxes >= 3) {
333  return DIENUM_STOP;
334  }
335  }
336 
337  return DIENUM_CONTINUE;
338 }
339 
340 
341 /*
342  * Opens the haptic device from the file descriptor.
343  *
344  * Steps:
345  * - Open temporary DirectInputDevice interface.
346  * - Create DirectInputDevice8 interface.
347  * - Release DirectInputDevice interface.
348  * - Call SDL_SYS_HapticOpenFromDevice8
349  */
350 static int
351 SDL_SYS_HapticOpenFromInstance(SDL_Haptic * haptic, DIDEVICEINSTANCE instance)
352 {
353  HRESULT ret;
354  int ret2;
355  LPDIRECTINPUTDEVICE8 device;
356  LPDIRECTINPUTDEVICE8 device8;
357 
358  /* Open the device */
359  ret = IDirectInput8_CreateDevice(dinput, &instance.guidInstance,
360  &device, NULL);
361  if (FAILED(ret)) {
362  DI_SetError("Creating DirectInput device", ret);
363  return -1;
364  }
365 
366  /* Now get the IDirectInputDevice8 interface, instead. */
367  ret = IDirectInputDevice8_QueryInterface(device,
368  &IID_IDirectInputDevice8,
369  (LPVOID *) &device8);
370  /* Done with the temporary one now. */
371  IDirectInputDevice8_Release(device);
372  if (FAILED(ret)) {
373  DI_SetError("Querying DirectInput interface", ret);
374  return -1;
375  }
376 
377  ret2 = SDL_SYS_HapticOpenFromDevice8(haptic, device8, SDL_FALSE);
378  if (ret2 < 0) {
379  IDirectInputDevice8_Release(device8);
380  return -1;
381  }
382 
383  return 0;
384 }
385 
386 static int
387 SDL_SYS_HapticOpenFromXInput(SDL_Haptic * haptic, Uint8 userid)
388 {
389  char threadName[32];
390  XINPUT_VIBRATION vibration = { 0, 0 }; /* stop any current vibration */
391  XINPUTSETSTATE(userid, &vibration);
392 
394 
395  haptic->neffects = 1;
396  haptic->nplaying = 1;
397 
398  /* Prepare effects memory. */
399  haptic->effects = (struct haptic_effect *)
400  SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
401  if (haptic->effects == NULL) {
402  return SDL_OutOfMemory();
403  }
404  /* Clear the memory */
405  SDL_memset(haptic->effects, 0,
406  sizeof(struct haptic_effect) * haptic->neffects);
407 
408  haptic->hwdata = (struct haptic_hwdata *) SDL_malloc(sizeof(*haptic->hwdata));
409  if (haptic->hwdata == NULL) {
410  SDL_free(haptic->effects);
411  haptic->effects = NULL;
412  return SDL_OutOfMemory();
413  }
414  SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
415 
416  haptic->hwdata->bXInputHaptic = 1;
417  haptic->hwdata->userid = userid;
418 
419  haptic->hwdata->mutex = SDL_CreateMutex();
420  if (haptic->hwdata->mutex == NULL) {
421  SDL_free(haptic->effects);
422  SDL_free(haptic->hwdata);
423  haptic->effects = NULL;
424  return SDL_SetError("Couldn't create XInput haptic mutex");
425  }
426 
427  SDL_snprintf(threadName, sizeof (threadName), "SDLXInputDev%d", (int) userid);
428 
429 #if defined(__WIN32__) && !defined(HAVE_LIBC) /* !!! FIXME: this is nasty. */
430  #undef SDL_CreateThread
431  haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata, NULL, NULL);
432 #else
433  haptic->hwdata->thread = SDL_CreateThread(SDL_RunXInputHaptic, threadName, haptic->hwdata);
434 #endif
435  if (haptic->hwdata->thread == NULL) {
436  SDL_DestroyMutex(haptic->hwdata->mutex);
437  SDL_free(haptic->effects);
438  SDL_free(haptic->hwdata);
439  haptic->effects = NULL;
440  return SDL_SetError("Couldn't create XInput haptic thread");
441  }
442 
443  return 0;
444  }
445 
446 /*
447  * Opens the haptic device from the file descriptor.
448  *
449  * Steps:
450  * - Set cooperative level.
451  * - Set data format.
452  * - Acquire exclusiveness.
453  * - Reset actuators.
454  * - Get supported features.
455  */
456 static int
457 SDL_SYS_HapticOpenFromDevice8(SDL_Haptic * haptic,
458  LPDIRECTINPUTDEVICE8 device8, SDL_bool is_joystick)
459 {
460  HRESULT ret;
461  DIPROPDWORD dipdw;
462 
463  /* Allocate the hwdata */
464  haptic->hwdata = (struct haptic_hwdata *)SDL_malloc(sizeof(*haptic->hwdata));
465  if (haptic->hwdata == NULL) {
466  return SDL_OutOfMemory();
467  }
468  SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
469 
470  /* We'll use the device8 from now on. */
471  haptic->hwdata->device = device8;
472  haptic->hwdata->is_joystick = is_joystick;
473 
474  /* Grab it exclusively to use force feedback stuff. */
475  ret = IDirectInputDevice8_SetCooperativeLevel(haptic->hwdata->device,
476  SDL_HelperWindow,
477  DISCL_EXCLUSIVE |
478  DISCL_BACKGROUND);
479  if (FAILED(ret)) {
480  DI_SetError("Setting cooperative level to exclusive", ret);
481  goto acquire_err;
482  }
483 
484  /* Set data format. */
485  ret = IDirectInputDevice8_SetDataFormat(haptic->hwdata->device,
486  &c_dfDIJoystick2);
487  if (FAILED(ret)) {
488  DI_SetError("Setting data format", ret);
489  goto acquire_err;
490  }
491 
492  /* Get number of axes. */
493  ret = IDirectInputDevice8_EnumObjects(haptic->hwdata->device,
494  DI_DeviceObjectCallback,
495  haptic, DIDFT_AXIS);
496  if (FAILED(ret)) {
497  DI_SetError("Getting device axes", ret);
498  goto acquire_err;
499  }
500 
501  /* Acquire the device. */
502  ret = IDirectInputDevice8_Acquire(haptic->hwdata->device);
503  if (FAILED(ret)) {
504  DI_SetError("Acquiring DirectInput device", ret);
505  goto acquire_err;
506  }
507 
508  /* Reset all actuators - just in case. */
509  ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
510  DISFFC_RESET);
511  if (FAILED(ret)) {
512  DI_SetError("Resetting device", ret);
513  goto acquire_err;
514  }
515 
516  /* Enabling actuators. */
517  ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
518  DISFFC_SETACTUATORSON);
519  if (FAILED(ret)) {
520  DI_SetError("Enabling actuators", ret);
521  goto acquire_err;
522  }
523 
524  /* Get supported effects. */
525  ret = IDirectInputDevice8_EnumEffects(haptic->hwdata->device,
526  DI_EffectCallback, haptic,
527  DIEFT_ALL);
528  if (FAILED(ret)) {
529  DI_SetError("Enumerating supported effects", ret);
530  goto acquire_err;
531  }
532  if (haptic->supported == 0) { /* Error since device supports nothing. */
533  SDL_SetError("Haptic: Internal error on finding supported effects.");
534  goto acquire_err;
535  }
536 
537  /* Check autogain and autocenter. */
538  dipdw.diph.dwSize = sizeof(DIPROPDWORD);
539  dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
540  dipdw.diph.dwObj = 0;
541  dipdw.diph.dwHow = DIPH_DEVICE;
542  dipdw.dwData = 10000;
543  ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
544  DIPROP_FFGAIN, &dipdw.diph);
545  if (!FAILED(ret)) { /* Gain is supported. */
546  haptic->supported |= SDL_HAPTIC_GAIN;
547  }
548  dipdw.diph.dwObj = 0;
549  dipdw.diph.dwHow = DIPH_DEVICE;
550  dipdw.dwData = DIPROPAUTOCENTER_OFF;
551  ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
552  DIPROP_AUTOCENTER, &dipdw.diph);
553  if (!FAILED(ret)) { /* Autocenter is supported. */
554  haptic->supported |= SDL_HAPTIC_AUTOCENTER;
555  }
556 
557  /* Status is always supported. */
559 
560  /* Check maximum effects. */
561  haptic->neffects = 128; /* This is not actually supported as thus under windows,
562  there is no way to tell the number of EFFECTS that a
563  device can hold, so we'll just use a "random" number
564  instead and put warnings in SDL_haptic.h */
565  haptic->nplaying = 128; /* Even more impossible to get this then neffects. */
566 
567  /* Prepare effects memory. */
568  haptic->effects = (struct haptic_effect *)
569  SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
570  if (haptic->effects == NULL) {
571  SDL_OutOfMemory();
572  goto acquire_err;
573  }
574  /* Clear the memory */
575  SDL_memset(haptic->effects, 0,
576  sizeof(struct haptic_effect) * haptic->neffects);
577 
578  return 0;
579 
580  /* Error handling */
581  acquire_err:
582  IDirectInputDevice8_Unacquire(haptic->hwdata->device);
583  return -1;
584 
585 }
586 
587 
588 /*
589  * Opens a haptic device for usage.
590  */
591 int
593 {
594  if (SDL_hapticlist[haptic->index].bXInputHaptic) {
595  return SDL_SYS_HapticOpenFromXInput(haptic, SDL_hapticlist[haptic->index].userid);
596  }
597 
598  return SDL_SYS_HapticOpenFromInstance(haptic, SDL_hapticlist[haptic->index].instance);
599 }
600 
601 
602 /*
603  * Opens a haptic device from first mouse it finds for usage.
604  */
605 int
607 {
608  int i;
609 
610  /* Grab the first mouse haptic device we find. */
611  for (i = 0; i < SDL_numhaptics; i++) {
612  if (SDL_hapticlist[i].capabilities.dwDevType == DI8DEVCLASS_POINTER ) {
613  return i;
614  }
615  }
616 
617  return -1;
618 }
619 
620 
621 /*
622  * Checks to see if a joystick has haptic features.
623  */
624 int
626 {
627  const struct joystick_hwdata *hwdata = joystick->hwdata;
628  return ( (hwdata->bXInputHaptic) ||
629  ((hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) != 0) );
630 }
631 
632 
633 /*
634  * Checks to see if the haptic device and joystick are in reality the same.
635  */
636 int
638 {
639  if ((joystick->hwdata->bXInputHaptic == haptic->hwdata->bXInputHaptic)) {
640  return haptic->hwdata->userid == joystick->hwdata->userid;
641  } else {
642  HRESULT ret;
643  DIDEVICEINSTANCE hap_instance, joy_instance;
644 
645  hap_instance.dwSize = sizeof(DIDEVICEINSTANCE);
646  joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
647 
648  /* Get the device instances. */
649  ret = IDirectInputDevice8_GetDeviceInfo(haptic->hwdata->device,
650  &hap_instance);
651  if (FAILED(ret)) {
652  return 0;
653  }
654  ret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice,
655  &joy_instance);
656  if (FAILED(ret)) {
657  return 0;
658  }
659 
660  if (DI_GUIDIsSame(&hap_instance.guidInstance, &joy_instance.guidInstance))
661  return 1;
662  }
663 
664  return 0;
665 }
666 
667 
668 /*
669  * Opens a SDL_Haptic from a SDL_Joystick.
670  */
671 int
673 {
674  int i;
675  HRESULT idret;
676  DIDEVICEINSTANCE joy_instance;
677  joy_instance.dwSize = sizeof(DIDEVICEINSTANCE);
678 
679  /* Since it comes from a joystick we have to try to match it with a haptic device on our haptic list. */
680  if (joystick->hwdata->bXInputDevice) {
681  const Uint8 userid = joystick->hwdata->userid;
682  for (i=0; i<SDL_numhaptics; i++) {
683  if ((SDL_hapticlist[i].bXInputHaptic) && (SDL_hapticlist[i].userid == userid)) {
684  SDL_assert(joystick->hwdata->bXInputHaptic);
685  haptic->index = i;
686  return SDL_SYS_HapticOpenFromXInput(haptic, SDL_hapticlist[haptic->index].userid);
687  }
688  }
689  } else {
690  for (i=0; i<SDL_numhaptics; i++) {
691  idret = IDirectInputDevice8_GetDeviceInfo(joystick->hwdata->InputDevice, &joy_instance);
692  if (FAILED(idret)) {
693  return -1;
694  }
695  if (DI_GUIDIsSame(&SDL_hapticlist[i].instance.guidInstance,
696  &joy_instance.guidInstance)) {
697  haptic->index = i;
698  return SDL_SYS_HapticOpenFromDevice8(haptic, joystick->hwdata->InputDevice, SDL_TRUE);
699  }
700  }
701  }
702  /* No match to our haptic list */
703  return -1;
704 }
705 
706 
707 /*
708  * Closes the haptic device.
709  */
710 void
712 {
713  if (haptic->hwdata) {
714 
715  /* Free effects. */
716  SDL_free(haptic->effects);
717  haptic->effects = NULL;
718  haptic->neffects = 0;
719 
720  /* Clean up */
721  if (haptic->hwdata->bXInputHaptic) {
722  haptic->hwdata->stopThread = 1;
723  SDL_WaitThread(haptic->hwdata->thread, NULL);
724  SDL_DestroyMutex(haptic->hwdata->mutex);
725  } else {
726  IDirectInputDevice8_Unacquire(haptic->hwdata->device);
727  /* Only release if isn't grabbed by a joystick. */
728  if (haptic->hwdata->is_joystick == 0) {
729  IDirectInputDevice8_Release(haptic->hwdata->device);
730  }
731  }
732 
733  /* Free */
734  SDL_free(haptic->hwdata);
735  haptic->hwdata = NULL;
736  }
737 }
738 
739 
740 /*
741  * Clean up after system specific haptic stuff
742  */
743 void
744 SDL_SYS_HapticQuit(void)
745 {
746  int i;
747 
748  if (loaded_xinput) {
750  loaded_xinput = SDL_FALSE;
751  }
752 
753  for (i = 0; i < SDL_arraysize(SDL_hapticlist); ++i) {
754  SDL_free(SDL_hapticlist[i].name);
755  SDL_hapticlist[i].name = NULL;
756  }
757 
758  if (dinput != NULL) {
759  IDirectInput8_Release(dinput);
760  dinput = NULL;
761  }
762 
763  if (coinitialized) {
765  coinitialized = SDL_FALSE;
766  }
767 }
768 
769 
770 /*
771  * Converts an SDL trigger button to an DIEFFECT trigger button.
772  */
773 static DWORD
774 DIGetTriggerButton(Uint16 button)
775 {
776  DWORD dwTriggerButton;
777 
778  dwTriggerButton = DIEB_NOTRIGGER;
779 
780  if (button != 0) {
781  dwTriggerButton = DIJOFS_BUTTON(button - 1);
782  }
783 
784  return dwTriggerButton;
785 }
786 
787 
788 /*
789  * Sets the direction.
790  */
791 static int
792 SDL_SYS_SetDirection(DIEFFECT * effect, SDL_HapticDirection * dir, int naxes)
793 {
794  LONG *rglDir;
795 
796  /* Handle no axes a part. */
797  if (naxes == 0) {
798  effect->dwFlags |= DIEFF_SPHERICAL; /* Set as default. */
799  effect->rglDirection = NULL;
800  return 0;
801  }
802 
803  /* Has axes. */
804  rglDir = SDL_malloc(sizeof(LONG) * naxes);
805  if (rglDir == NULL) {
806  return SDL_OutOfMemory();
807  }
808  SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
809  effect->rglDirection = rglDir;
810 
811  switch (dir->type) {
812  case SDL_HAPTIC_POLAR:
813  effect->dwFlags |= DIEFF_POLAR;
814  rglDir[0] = dir->dir[0];
815  return 0;
817  effect->dwFlags |= DIEFF_CARTESIAN;
818  rglDir[0] = dir->dir[0];
819  if (naxes > 1)
820  rglDir[1] = dir->dir[1];
821  if (naxes > 2)
822  rglDir[2] = dir->dir[2];
823  return 0;
825  effect->dwFlags |= DIEFF_SPHERICAL;
826  rglDir[0] = dir->dir[0];
827  if (naxes > 1)
828  rglDir[1] = dir->dir[1];
829  if (naxes > 2)
830  rglDir[2] = dir->dir[2];
831  return 0;
832 
833  default:
834  return SDL_SetError("Haptic: Unknown direction type.");
835  }
836 }
837 
838 #define CONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
839 /*
840  * Creates the DIEFFECT from a SDL_HapticEffect.
841  */
842 static int
843 SDL_SYS_ToDIEFFECT(SDL_Haptic * haptic, DIEFFECT * dest,
845 {
846  int i;
847  DICONSTANTFORCE *constant;
848  DIPERIODIC *periodic;
849  DICONDITION *condition; /* Actually an array of conditions - one per axis. */
850  DIRAMPFORCE *ramp;
851  DICUSTOMFORCE *custom;
852  DIENVELOPE *envelope;
853  SDL_HapticConstant *hap_constant;
854  SDL_HapticPeriodic *hap_periodic;
855  SDL_HapticCondition *hap_condition;
856  SDL_HapticRamp *hap_ramp;
857  SDL_HapticCustom *hap_custom;
858  DWORD *axes;
859 
860  /* Set global stuff. */
861  SDL_memset(dest, 0, sizeof(DIEFFECT));
862  dest->dwSize = sizeof(DIEFFECT); /* Set the structure size. */
863  dest->dwSamplePeriod = 0; /* Not used by us. */
864  dest->dwGain = 10000; /* Gain is set globally, not locally. */
865  dest->dwFlags = DIEFF_OBJECTOFFSETS; /* Seems obligatory. */
866 
867  /* Envelope. */
868  envelope = SDL_malloc(sizeof(DIENVELOPE));
869  if (envelope == NULL) {
870  return SDL_OutOfMemory();
871  }
872  SDL_memset(envelope, 0, sizeof(DIENVELOPE));
873  dest->lpEnvelope = envelope;
874  envelope->dwSize = sizeof(DIENVELOPE); /* Always should be this. */
875 
876  /* Axes. */
877  dest->cAxes = haptic->naxes;
878  if (dest->cAxes > 0) {
879  axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
880  if (axes == NULL) {
881  return SDL_OutOfMemory();
882  }
883  axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */
884  if (dest->cAxes > 1) {
885  axes[1] = haptic->hwdata->axes[1];
886  }
887  if (dest->cAxes > 2) {
888  axes[2] = haptic->hwdata->axes[2];
889  }
890  dest->rgdwAxes = axes;
891  }
892 
893 
894  /* The big type handling switch, even bigger then Linux's version. */
895  switch (src->type) {
896  case SDL_HAPTIC_CONSTANT:
897  hap_constant = &src->constant;
898  constant = SDL_malloc(sizeof(DICONSTANTFORCE));
899  if (constant == NULL) {
900  return SDL_OutOfMemory();
901  }
902  SDL_memset(constant, 0, sizeof(DICONSTANTFORCE));
903 
904  /* Specifics */
905  constant->lMagnitude = CONVERT(hap_constant->level);
906  dest->cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
907  dest->lpvTypeSpecificParams = constant;
908 
909  /* Generics */
910  dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
911  dest->dwTriggerButton = DIGetTriggerButton(hap_constant->button);
912  dest->dwTriggerRepeatInterval = hap_constant->interval;
913  dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */
914 
915  /* Direction. */
916  if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
917  < 0) {
918  return -1;
919  }
920 
921  /* Envelope */
922  if ((hap_constant->attack_length == 0)
923  && (hap_constant->fade_length == 0)) {
924  SDL_free(dest->lpEnvelope);
925  dest->lpEnvelope = NULL;
926  } else {
927  envelope->dwAttackLevel = CONVERT(hap_constant->attack_level);
928  envelope->dwAttackTime = hap_constant->attack_length * 1000;
929  envelope->dwFadeLevel = CONVERT(hap_constant->fade_level);
930  envelope->dwFadeTime = hap_constant->fade_length * 1000;
931  }
932 
933  break;
934 
935  case SDL_HAPTIC_SINE:
936  /* !!! FIXME: put this back when we have more bits in 2.1 */
937  /* case SDL_HAPTIC_SQUARE: */
938  case SDL_HAPTIC_TRIANGLE:
941  hap_periodic = &src->periodic;
942  periodic = SDL_malloc(sizeof(DIPERIODIC));
943  if (periodic == NULL) {
944  return SDL_OutOfMemory();
945  }
946  SDL_memset(periodic, 0, sizeof(DIPERIODIC));
947 
948  /* Specifics */
949  periodic->dwMagnitude = CONVERT(hap_periodic->magnitude);
950  periodic->lOffset = CONVERT(hap_periodic->offset);
951  periodic->dwPhase = hap_periodic->phase;
952  periodic->dwPeriod = hap_periodic->period * 1000;
953  dest->cbTypeSpecificParams = sizeof(DIPERIODIC);
954  dest->lpvTypeSpecificParams = periodic;
955 
956  /* Generics */
957  dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
958  dest->dwTriggerButton = DIGetTriggerButton(hap_periodic->button);
959  dest->dwTriggerRepeatInterval = hap_periodic->interval;
960  dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */
961 
962  /* Direction. */
963  if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
964  < 0) {
965  return -1;
966  }
967 
968  /* Envelope */
969  if ((hap_periodic->attack_length == 0)
970  && (hap_periodic->fade_length == 0)) {
971  SDL_free(dest->lpEnvelope);
972  dest->lpEnvelope = NULL;
973  } else {
974  envelope->dwAttackLevel = CONVERT(hap_periodic->attack_level);
975  envelope->dwAttackTime = hap_periodic->attack_length * 1000;
976  envelope->dwFadeLevel = CONVERT(hap_periodic->fade_level);
977  envelope->dwFadeTime = hap_periodic->fade_length * 1000;
978  }
979 
980  break;
981 
982  case SDL_HAPTIC_SPRING:
983  case SDL_HAPTIC_DAMPER:
984  case SDL_HAPTIC_INERTIA:
985  case SDL_HAPTIC_FRICTION:
986  hap_condition = &src->condition;
987  condition = SDL_malloc(sizeof(DICONDITION) * dest->cAxes);
988  if (condition == NULL) {
989  return SDL_OutOfMemory();
990  }
991  SDL_memset(condition, 0, sizeof(DICONDITION));
992 
993  /* Specifics */
994  for (i = 0; i < (int) dest->cAxes; i++) {
995  condition[i].lOffset = CONVERT(hap_condition->center[i]);
996  condition[i].lPositiveCoefficient =
997  CONVERT(hap_condition->right_coeff[i]);
998  condition[i].lNegativeCoefficient =
999  CONVERT(hap_condition->left_coeff[i]);
1000  condition[i].dwPositiveSaturation =
1001  CONVERT(hap_condition->right_sat[i]);
1002  condition[i].dwNegativeSaturation =
1003  CONVERT(hap_condition->left_sat[i]);
1004  condition[i].lDeadBand = CONVERT(hap_condition->deadband[i]);
1005  }
1006  dest->cbTypeSpecificParams = sizeof(DICONDITION) * dest->cAxes;
1007  dest->lpvTypeSpecificParams = condition;
1008 
1009  /* Generics */
1010  dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */
1011  dest->dwTriggerButton = DIGetTriggerButton(hap_condition->button);
1012  dest->dwTriggerRepeatInterval = hap_condition->interval;
1013  dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */
1014 
1015  /* Direction. */
1016  if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
1017  < 0) {
1018  return -1;
1019  }
1020 
1021  /* Envelope - Not actually supported by most CONDITION implementations. */
1022  SDL_free(dest->lpEnvelope);
1023  dest->lpEnvelope = NULL;
1024 
1025  break;
1026 
1027  case SDL_HAPTIC_RAMP:
1028  hap_ramp = &src->ramp;
1029  ramp = SDL_malloc(sizeof(DIRAMPFORCE));
1030  if (ramp == NULL) {
1031  return SDL_OutOfMemory();
1032  }
1033  SDL_memset(ramp, 0, sizeof(DIRAMPFORCE));
1034 
1035  /* Specifics */
1036  ramp->lStart = CONVERT(hap_ramp->start);
1037  ramp->lEnd = CONVERT(hap_ramp->end);
1038  dest->cbTypeSpecificParams = sizeof(DIRAMPFORCE);
1039  dest->lpvTypeSpecificParams = ramp;
1040 
1041  /* Generics */
1042  dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */
1043  dest->dwTriggerButton = DIGetTriggerButton(hap_ramp->button);
1044  dest->dwTriggerRepeatInterval = hap_ramp->interval;
1045  dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */
1046 
1047  /* Direction. */
1048  if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
1049  return -1;
1050  }
1051 
1052  /* Envelope */
1053  if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
1054  SDL_free(dest->lpEnvelope);
1055  dest->lpEnvelope = NULL;
1056  } else {
1057  envelope->dwAttackLevel = CONVERT(hap_ramp->attack_level);
1058  envelope->dwAttackTime = hap_ramp->attack_length * 1000;
1059  envelope->dwFadeLevel = CONVERT(hap_ramp->fade_level);
1060  envelope->dwFadeTime = hap_ramp->fade_length * 1000;
1061  }
1062 
1063  break;
1064 
1065  case SDL_HAPTIC_CUSTOM:
1066  hap_custom = &src->custom;
1067  custom = SDL_malloc(sizeof(DICUSTOMFORCE));
1068  if (custom == NULL) {
1069  return SDL_OutOfMemory();
1070  }
1071  SDL_memset(custom, 0, sizeof(DICUSTOMFORCE));
1072 
1073  /* Specifics */
1074  custom->cChannels = hap_custom->channels;
1075  custom->dwSamplePeriod = hap_custom->period * 1000;
1076  custom->cSamples = hap_custom->samples;
1077  custom->rglForceData =
1078  SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
1079  for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */
1080  custom->rglForceData[i] = CONVERT(hap_custom->data[i]);
1081  }
1082  dest->cbTypeSpecificParams = sizeof(DICUSTOMFORCE);
1083  dest->lpvTypeSpecificParams = custom;
1084 
1085  /* Generics */
1086  dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */
1087  dest->dwTriggerButton = DIGetTriggerButton(hap_custom->button);
1088  dest->dwTriggerRepeatInterval = hap_custom->interval;
1089  dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */
1090 
1091  /* Direction. */
1092  if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
1093  0) {
1094  return -1;
1095  }
1096 
1097  /* Envelope */
1098  if ((hap_custom->attack_length == 0)
1099  && (hap_custom->fade_length == 0)) {
1100  SDL_free(dest->lpEnvelope);
1101  dest->lpEnvelope = NULL;
1102  } else {
1103  envelope->dwAttackLevel = CONVERT(hap_custom->attack_level);
1104  envelope->dwAttackTime = hap_custom->attack_length * 1000;
1105  envelope->dwFadeLevel = CONVERT(hap_custom->fade_level);
1106  envelope->dwFadeTime = hap_custom->fade_length * 1000;
1107  }
1108 
1109  break;
1110 
1111 
1112  default:
1113  return SDL_SetError("Haptic: Unknown effect type.");
1114  }
1115 
1116  return 0;
1117 }
1118 
1119 
1120 /*
1121  * Frees an DIEFFECT allocated by SDL_SYS_ToDIEFFECT.
1122  */
1123 static void
1124 SDL_SYS_HapticFreeDIEFFECT(DIEFFECT * effect, int type)
1125 {
1126  DICUSTOMFORCE *custom;
1127 
1128  SDL_free(effect->lpEnvelope);
1129  effect->lpEnvelope = NULL;
1130  SDL_free(effect->rgdwAxes);
1131  effect->rgdwAxes = NULL;
1132  if (effect->lpvTypeSpecificParams != NULL) {
1133  if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */
1134  custom = (DICUSTOMFORCE *) effect->lpvTypeSpecificParams;
1135  SDL_free(custom->rglForceData);
1136  custom->rglForceData = NULL;
1137  }
1138  SDL_free(effect->lpvTypeSpecificParams);
1139  effect->lpvTypeSpecificParams = NULL;
1140  }
1141  SDL_free(effect->rglDirection);
1142  effect->rglDirection = NULL;
1143 }
1144 
1145 
1146 /*
1147  * Gets the effect type from the generic SDL haptic effect wrapper.
1148  */
1149 static REFGUID
1150 SDL_SYS_HapticEffectType(SDL_HapticEffect * effect)
1151 {
1152  switch (effect->type) {
1153  case SDL_HAPTIC_CONSTANT:
1154  return &GUID_ConstantForce;
1155 
1156  case SDL_HAPTIC_RAMP:
1157  return &GUID_RampForce;
1158 
1159  /* !!! FIXME: put this back when we have more bits in 2.1 */
1160  /* case SDL_HAPTIC_SQUARE:
1161  return &GUID_Square; */
1162 
1163  case SDL_HAPTIC_SINE:
1164  return &GUID_Sine;
1165 
1166  case SDL_HAPTIC_TRIANGLE:
1167  return &GUID_Triangle;
1168 
1169  case SDL_HAPTIC_SAWTOOTHUP:
1170  return &GUID_SawtoothUp;
1171 
1173  return &GUID_SawtoothDown;
1174 
1175  case SDL_HAPTIC_SPRING:
1176  return &GUID_Spring;
1177 
1178  case SDL_HAPTIC_DAMPER:
1179  return &GUID_Damper;
1180 
1181  case SDL_HAPTIC_INERTIA:
1182  return &GUID_Inertia;
1183 
1184  case SDL_HAPTIC_FRICTION:
1185  return &GUID_Friction;
1186 
1187  case SDL_HAPTIC_CUSTOM:
1188  case SDL_HAPTIC_LEFTRIGHT:
1189  return &GUID_CustomForce;
1190 
1191  default:
1192  SDL_SetError("Haptic: Unknown effect type.");
1193  return NULL;
1194  }
1195 }
1196 
1197 
1198 /*
1199  * Creates a new haptic effect.
1200  */
1201 int
1202 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1203  SDL_HapticEffect * base)
1204 {
1205  HRESULT ret;
1206  REFGUID type = SDL_SYS_HapticEffectType(base);
1207 
1208  if ((type == NULL) && (!haptic->hwdata->bXInputHaptic)) {
1209  goto err_hweffect;
1210  }
1211 
1212  /* Alloc the effect. */
1213  effect->hweffect = (struct haptic_hweffect *)
1214  SDL_malloc(sizeof(struct haptic_hweffect));
1215  if (effect->hweffect == NULL) {
1216  SDL_OutOfMemory();
1217  goto err_hweffect;
1218  }
1219 
1220  SDL_zerop(effect->hweffect);
1221 
1222  if (haptic->hwdata->bXInputHaptic) {
1223  SDL_assert(base->type == SDL_HAPTIC_LEFTRIGHT); /* should catch this at higher level */
1224  return SDL_SYS_HapticUpdateEffect(haptic, effect, base);
1225  }
1226 
1227  /* Get the effect. */
1228  if (SDL_SYS_ToDIEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
1229  goto err_effectdone;
1230  }
1231 
1232  /* Create the actual effect. */
1233  ret = IDirectInputDevice8_CreateEffect(haptic->hwdata->device, type,
1234  &effect->hweffect->effect,
1235  &effect->hweffect->ref, NULL);
1236  if (FAILED(ret)) {
1237  DI_SetError("Unable to create effect", ret);
1238  goto err_effectdone;
1239  }
1240 
1241  return 0;
1242 
1243  err_effectdone:
1244  SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, base->type);
1245  err_hweffect:
1246  SDL_free(effect->hweffect);
1247  effect->hweffect = NULL;
1248  return -1;
1249 }
1250 
1251 
1252 /*
1253  * Updates an effect.
1254  */
1255 int
1257  struct haptic_effect *effect,
1259 {
1260  HRESULT ret;
1261  DWORD flags;
1262  DIEFFECT temp;
1263 
1264  if (haptic->hwdata->bXInputHaptic) {
1265  XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
1267  vib->wLeftMotorSpeed = data->leftright.large_magnitude;
1268  vib->wRightMotorSpeed = data->leftright.small_magnitude;
1269  SDL_LockMutex(haptic->hwdata->mutex);
1270  if (haptic->hwdata->stopTicks) { /* running right now? Update it. */
1271  XINPUTSETSTATE(haptic->hwdata->userid, vib);
1272  }
1273  SDL_UnlockMutex(haptic->hwdata->mutex);
1274  return 0;
1275  }
1276 
1277  /* Get the effect. */
1278  SDL_memset(&temp, 0, sizeof(DIEFFECT));
1279  if (SDL_SYS_ToDIEFFECT(haptic, &temp, data) < 0) {
1280  goto err_update;
1281  }
1282 
1283  /* Set the flags. Might be worthwhile to diff temp with loaded effect and
1284  * only change those parameters. */
1285  flags = DIEP_DIRECTION |
1286  DIEP_DURATION |
1287  DIEP_ENVELOPE |
1288  DIEP_STARTDELAY |
1289  DIEP_TRIGGERBUTTON |
1290  DIEP_TRIGGERREPEATINTERVAL | DIEP_TYPESPECIFICPARAMS;
1291 
1292  /* Create the actual effect. */
1293  ret =
1294  IDirectInputEffect_SetParameters(effect->hweffect->ref, &temp, flags);
1295  if (FAILED(ret)) {
1296  DI_SetError("Unable to update effect", ret);
1297  goto err_update;
1298  }
1299 
1300  /* Copy it over. */
1301  SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect, data->type);
1302  SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(DIEFFECT));
1303 
1304  return 0;
1305 
1306  err_update:
1307  SDL_SYS_HapticFreeDIEFFECT(&temp, data->type);
1308  return -1;
1309 }
1310 
1311 
1312 /*
1313  * Runs an effect.
1314  */
1315 int
1316 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1317  Uint32 iterations)
1318 {
1319  HRESULT ret;
1320  DWORD iter;
1321 
1322  if (haptic->hwdata->bXInputHaptic) {
1323  XINPUT_VIBRATION *vib = &effect->hweffect->vibration;
1324  SDL_assert(effect->effect.type == SDL_HAPTIC_LEFTRIGHT); /* should catch this at higher level */
1325  SDL_LockMutex(haptic->hwdata->mutex);
1326  if(effect->effect.leftright.length == SDL_HAPTIC_INFINITY || iterations == SDL_HAPTIC_INFINITY)
1327  haptic->hwdata->stopTicks = SDL_HAPTIC_INFINITY;
1328  else
1329  haptic->hwdata->stopTicks = SDL_GetTicks() + (effect->effect.leftright.length * iterations);
1330  SDL_UnlockMutex(haptic->hwdata->mutex);
1331  return (XINPUTSETSTATE(haptic->hwdata->userid, vib) == ERROR_SUCCESS) ? 0 : -1;
1332  }
1333 
1334  /* Check if it's infinite. */
1335  if (iterations == SDL_HAPTIC_INFINITY) {
1336  iter = INFINITE;
1337  } else
1338  iter = iterations;
1339 
1340  /* Run the effect. */
1341  ret = IDirectInputEffect_Start(effect->hweffect->ref, iter, 0);
1342  if (FAILED(ret)) {
1343  return DI_SetError("Running the effect", ret);
1344  }
1345 
1346  return 0;
1347 }
1348 
1349 
1350 /*
1351  * Stops an effect.
1352  */
1353 int
1354 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1355 {
1356  HRESULT ret;
1357 
1358  if (haptic->hwdata->bXInputHaptic) {
1359  XINPUT_VIBRATION vibration = { 0, 0 };
1360  SDL_LockMutex(haptic->hwdata->mutex);
1361  haptic->hwdata->stopTicks = 0;
1362  SDL_UnlockMutex(haptic->hwdata->mutex);
1363  return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
1364  }
1365 
1366  ret = IDirectInputEffect_Stop(effect->hweffect->ref);
1367  if (FAILED(ret)) {
1368  return DI_SetError("Unable to stop effect", ret);
1369  }
1370 
1371  return 0;
1372 }
1373 
1374 
1375 /*
1376  * Frees the effect.
1377  */
1378 void
1379 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1380 {
1381  HRESULT ret;
1382 
1383  if (haptic->hwdata->bXInputHaptic) {
1384  SDL_SYS_HapticStopEffect(haptic, effect);
1385  } else {
1386  ret = IDirectInputEffect_Unload(effect->hweffect->ref);
1387  if (FAILED(ret)) {
1388  DI_SetError("Removing effect from the device", ret);
1389  }
1390  SDL_SYS_HapticFreeDIEFFECT(&effect->hweffect->effect,
1391  effect->effect.type);
1392  }
1393  SDL_free(effect->hweffect);
1394  effect->hweffect = NULL;
1395 }
1396 
1397 
1398 /*
1399  * Gets the status of a haptic effect.
1400  */
1401 int
1403  struct haptic_effect *effect)
1404 {
1405  HRESULT ret;
1406  DWORD status;
1407 
1408  ret = IDirectInputEffect_GetEffectStatus(effect->hweffect->ref, &status);
1409  if (FAILED(ret)) {
1410  return DI_SetError("Getting effect status", ret);
1411  }
1412 
1413  if (status == 0)
1414  return SDL_FALSE;
1415  return SDL_TRUE;
1416 }
1417 
1418 
1419 /*
1420  * Sets the gain.
1421  */
1422 int
1423 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
1424 {
1425  HRESULT ret;
1426  DIPROPDWORD dipdw;
1427 
1428  /* Create the weird structure thingy. */
1429  dipdw.diph.dwSize = sizeof(DIPROPDWORD);
1430  dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
1431  dipdw.diph.dwObj = 0;
1432  dipdw.diph.dwHow = DIPH_DEVICE;
1433  dipdw.dwData = gain * 100; /* 0 to 10,000 */
1434 
1435  /* Try to set the autocenter. */
1436  ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
1437  DIPROP_FFGAIN, &dipdw.diph);
1438  if (FAILED(ret)) {
1439  return DI_SetError("Setting gain", ret);
1440  }
1441 
1442  return 0;
1443 }
1444 
1445 
1446 /*
1447  * Sets the autocentering.
1448  */
1449 int
1450 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
1451 {
1452  HRESULT ret;
1453  DIPROPDWORD dipdw;
1454 
1455  /* Create the weird structure thingy. */
1456  dipdw.diph.dwSize = sizeof(DIPROPDWORD);
1457  dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
1458  dipdw.diph.dwObj = 0;
1459  dipdw.diph.dwHow = DIPH_DEVICE;
1460  dipdw.dwData = (autocenter == 0) ? DIPROPAUTOCENTER_OFF :
1461  DIPROPAUTOCENTER_ON;
1462 
1463  /* Try to set the autocenter. */
1464  ret = IDirectInputDevice8_SetProperty(haptic->hwdata->device,
1465  DIPROP_AUTOCENTER, &dipdw.diph);
1466  if (FAILED(ret)) {
1467  return DI_SetError("Setting autocenter", ret);
1468  }
1469 
1470  return 0;
1471 }
1472 
1473 
1474 /*
1475  * Pauses the device.
1476  */
1477 int
1479 {
1480  HRESULT ret;
1481 
1482  /* Pause the device. */
1483  ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
1484  DISFFC_PAUSE);
1485  if (FAILED(ret)) {
1486  return DI_SetError("Pausing the device", ret);
1487  }
1488 
1489  return 0;
1490 }
1491 
1492 
1493 /*
1494  * Pauses the device.
1495  */
1496 int
1498 {
1499  HRESULT ret;
1500 
1501  /* Unpause the device. */
1502  ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
1503  DISFFC_CONTINUE);
1504  if (FAILED(ret)) {
1505  return DI_SetError("Pausing the device", ret);
1506  }
1507 
1508  return 0;
1509 }
1510 
1511 
1512 /*
1513  * Stops all the playing effects on the device.
1514  */
1515 int
1517 {
1518  HRESULT ret;
1519 
1520  if (haptic->hwdata->bXInputHaptic) {
1521  XINPUT_VIBRATION vibration = { 0, 0 };
1522  SDL_LockMutex(haptic->hwdata->mutex);
1523  haptic->hwdata->stopTicks = 0;
1524  SDL_UnlockMutex(haptic->hwdata->mutex);
1525  return (XINPUTSETSTATE(haptic->hwdata->userid, &vibration) == ERROR_SUCCESS) ? 0 : -1;
1526  }
1527 
1528  /* Try to stop the effects. */
1529  ret = IDirectInputDevice8_SendForceFeedbackCommand(haptic->hwdata->device,
1530  DISFFC_STOPALL);
1531  if (FAILED(ret)) {
1532  return DI_SetError("Stopping the device", ret);
1533  }
1534 
1535  return 0;
1536 }
1537 
1538 
1539 /* !!! FIXME: this is a hack, remove this later. */
1540 /* Since XInput doesn't offer a way to vibrate for X time, we hook into
1541  * SDL_PumpEvents() to check if it's time to stop vibrating with some
1542  * frequency.
1543  * In practice, this works for 99% of use cases. But in an ideal world,
1544  * we do this in a separate thread so that:
1545  * - we aren't bound to when the app chooses to pump the event queue.
1546  * - we aren't adding more polling to the event queue
1547  * - we can emulate all the haptic effects correctly (start on a delay,
1548  * mix multiple effects, etc).
1549  *
1550  * Mostly, this is here to get rumbling to work, and all the other features
1551  * are absent in the XInput path for now. :(
1552  */
1553 static int SDLCALL
1554 SDL_RunXInputHaptic(void *arg)
1555 {
1556  struct haptic_hwdata *hwdata = (struct haptic_hwdata *) arg;
1557 
1558  while (!hwdata->stopThread) {
1559  SDL_Delay(50);
1560  SDL_LockMutex(hwdata->mutex);
1561  /* If we're currently running and need to stop... */
1562  if ((hwdata->stopTicks) && (hwdata->stopTicks < SDL_GetTicks())) {
1563  XINPUT_VIBRATION vibration = { 0, 0 };
1564  hwdata->stopTicks = 0;
1565  XINPUTSETSTATE(hwdata->userid, &vibration);
1566  }
1567  SDL_UnlockMutex(hwdata->mutex);
1568  }
1569 
1570  return 0;
1571 }
1572 
1573 #endif /* SDL_HAPTIC_DINPUT */
1574 
1575 /* vi: set ts=4 sw=4 expandtab: */
int SDL_SYS_HapticOpenFromJoystick(SDL_Haptic *haptic, SDL_Joystick *joystick)
struct haptic_hwdata * hwdata
Definition: SDL_syshaptic.h:52
Uint16 deadband[3]
Definition: SDL_haptic.h:611
#define DIRECTINPUT_VERSION
Definition: directx.h:74
Structure that represents a haptic direction.
Definition: SDL_haptic.h:439
GLenum condition
Definition: gl2ext.h:1403
struct haptic_effect * effects
Definition: SDL_syshaptic.h:46
#define SDL_HAPTIC_SPHERICAL
Uses spherical coordinates for the direction.
Definition: SDL_haptic.h:329
LPDIRECTINPUTDEVICE8 InputDevice
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum GLenum type
Definition: gl2ext.h:845
#define SDL_HAPTIC_AUTOCENTER
Device can set autocenter.
Definition: SDL_haptic.h:285
A structure containing a template for a Periodic effect.
Definition: SDL_haptic.h:538
#define NULL
Definition: ftobjs.h:61
DECLSPEC int SDLCALL SDL_snprintf(char *text, size_t maxlen, const char *fmt,...)
Definition: SDL_string.c:1277
#define SDL_HAPTIC_GAIN
Device can set global gain.
Definition: SDL_haptic.h:276
typedef HRESULT(WINAPI *LPD3DXIMTSIGNALCALLBACK)(CONST D3DXVECTOR2 *uv
#define SDL_HAPTIC_CUSTOM
Custom effect is supported.
Definition: SDL_haptic.h:263
DECLSPEC int SDLCALL SDL_LockMutex(SDL_mutex *mutex)
Definition: SDL_sysmutex.c:73
#define SDL_HAPTIC_TRIANGLE
Triangle wave effect supported.
Definition: SDL_haptic.h:189
SDL_HapticDirection direction
Definition: SDL_haptic.h:596
SDL_bool
Definition: SDL_stdinc.h:116
int SDL_SYS_HapticOpen(SDL_Haptic *haptic)
SDL_HapticCustom custom
Definition: SDL_haptic.h:796
int SDL_SYS_HapticMouse(void)
int SDL_SYS_JoystickSameHaptic(SDL_Haptic *haptic, SDL_Joystick *joystick)
#define SDL_HAPTIC_INERTIA
Inertia effect supported - uses axes acceleration.
Definition: SDL_haptic.h:246
DECLSPEC SDL_mutex *SDLCALL SDL_CreateMutex(void)
Definition: SDL_sysmutex.c:38
DECLSPEC void SDLCALL SDL_free(void *mem)
Uint16 fade_level
Definition: SDL_haptic.h:650
SDL_HapticRamp ramp
Definition: SDL_haptic.h:794
int SDL_SYS_HapticStopEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:8736
const char * SDL_SYS_HapticName(int index)
A structure containing a template for a Condition effect.
Definition: SDL_haptic.h:591
EGLImageKHR EGLint * name
Definition: eglext.h:284
DECLSPEC int SDLCALL SDL_memcmp(const void *s1, const void *s2, size_t len)
Definition: SDL_string.c:370
The SDL Haptic subsystem allows you to control haptic (force feedback) devices.
int SDL_SYS_HapticUnpause(SDL_Haptic *haptic)
Sint16 left_coeff[3]
Definition: SDL_haptic.h:610
Uint16 right_sat[3]
Definition: SDL_haptic.h:607
#define XINPUT_CAPS_FFB_SUPPORTED
#define SDLCALL
Definition: begin_code.h:72
Uint16 fade_length
Definition: SDL_haptic.h:714
Sint16 right_coeff[3]
Definition: SDL_haptic.h:609
#define SDL_HAPTIC_SINE
Sine wave effect supported.
Definition: SDL_haptic.h:166
#define XINPUTGETCAPABILITIES
#define XINPUTSETSTATE
#define SDL_XINPUT_MAX_DEVICES
int SDL_SYS_HapticGetEffectStatus(SDL_Haptic *haptic, struct haptic_effect *effect)
DECLSPEC int SDLCALL SDL_UnlockMutex(SDL_mutex *mutex)
Definition: SDL_sysmutex.c:160
struct joystick_hwdata * hwdata
ret
Definition: glew_str_glx.c:2
A structure containing a template for a Constant effect.
Definition: SDL_haptic.h:457
Uint8 SDL_numhaptics
Definition: SDL_haptic.c:28
DIDEVCAPS Capabilities
#define SDL_HINT_XINPUT_ENABLED
A variable that lets you disable the detection and use of Xinput gamepad devices. ...
Definition: SDL_hints.h:215
#define SDL_HAPTIC_INFINITY
Used to play a device an infinite number of times.
Definition: SDL_haptic.h:344
Uint16 interval
Definition: SDL_haptic.h:640
#define SDL_HAPTIC_CARTESIAN
Uses cartesian coordinates for the direction.
Definition: SDL_haptic.h:322
uint32_t Uint32
An unsigned 32-bit integer type.
Definition: SDL_stdinc.h:145
unsigned int supported
Definition: SDL_syshaptic.h:49
DECLSPEC void SDLCALL SDL_WaitThread(SDL_Thread *thread, int *status)
Definition: SDL_thread.c:399
SDL_HapticCondition condition
Definition: SDL_haptic.h:793
DECLSPEC Uint32 SDLCALL SDL_GetTicks(void)
Get the number of milliseconds since the SDL library initialization.
Definition: SDL_systimer.c:44
The generic template for any haptic effect.
Definition: SDL_haptic.h:787
#define SDL_HAPTIC_CONSTANT
Constant effect supported.
Definition: SDL_haptic.h:157
int SDL_SYS_HapticSetAutocenter(SDL_Haptic *haptic, int autocenter)
int
Definition: SDL_systhread.c:37
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl2ext.h:848
void WIN_UnloadXInputDLL(void)
HRESULT WIN_CoInitialize(void)
DECLSPEC char *SDLCALL SDL_strdup(const char *str)
Definition: SDL_string.c:511
#define SDL_HAPTIC_POLAR
Uses polar coordinates for the direction.
Definition: SDL_haptic.h:315
DECLSPEC void SDLCALL SDL_Delay(Uint32 ms)
Wait a specified number of milliseconds before returning.
Definition: SDL_systimer.c:70
static void * pContext
Definition: dsound.c:51
DECLSPEC void *SDLCALL SDL_memset(void *dst, int c, size_t len)
Definition: SDL_string.c:261
#define WIN_StringToUTF8(S)
Definition: SDL_windows.h:41
SDL_HapticConstant constant
Definition: SDL_haptic.h:791
DECLSPEC int SDLCALL SDL_SetError(const char *fmt,...)
Definition: SDL_error.c:53
#define SDL_HAPTIC_PAUSE
Device can be paused.
Definition: SDL_haptic.h:302
DECLSPEC void *SDLCALL SDL_malloc(size_t size)
GLuint index
Definition: glew.h:1800
int SDL_SYS_HapticUpdateEffect(SDL_Haptic *haptic, struct haptic_effect *effect, SDL_HapticEffect *data)
int SDL_SYS_JoystickIsHaptic(SDL_Joystick *joystick)
SDL_HapticDirection direction
Definition: SDL_haptic.h:632
DECLSPEC const char *SDLCALL SDL_GetHint(const char *name)
Get a hint.
Definition: SDL_hints.c:104
A structure containing a template for a Ramp effect.
Definition: SDL_haptic.h:628
jmp_buf env
Definition: jumphack.c:12
DECLSPEC SDL_Thread *SDLCALL SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data)
void SDL_SYS_HapticDestroyEffect(SDL_Haptic *haptic, struct haptic_effect *effect)
Uint16 attack_length
Definition: SDL_haptic.h:647
typedef LPVOID(WINAPI *PFNWGLCREATEIMAGEBUFFERI3DPROC)(HDC hDC
DWORD SDL_XInputVersion
#define SDL_assert(condition)
Definition: SDL_assert.h:159
static SDL_Thread * thread
Uint16 fade_length
Definition: SDL_haptic.h:649
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
void WIN_CoUninitialize(void)
SDL_HapticEffect effect
Definition: SDL_syshaptic.h:35
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:83
struct SDL_mutex SDL_mutex
Definition: SDL_mutex.h:59
int SDL_SYS_HapticInit(void)
void SDL_SYS_HapticQuit(void)
struct haptic_hweffect * hweffect
Definition: SDL_syshaptic.h:36
int WIN_LoadXInputDLL(void)
#define SDL_HAPTIC_STATUS
Device can be queried for effect status.
Definition: SDL_haptic.h:294
void SDL_SYS_HapticClose(SDL_Haptic *haptic)
int SDL_SYS_HapticPause(SDL_Haptic *haptic)
DECLSPEC void *SDLCALL SDL_memcpy(void *dst, const void *src, size_t len)
Definition: SDL_string.c:293
SDL_HapticDirection direction
Definition: SDL_haptic.h:544
SDL_HapticLeftRight leftright
Definition: SDL_haptic.h:795
GLenum GLuint GLsizei const GLchar * buf
Definition: glew.h:2539
GLenum GLsizei GLsizei GLsizei GLsizei GLbitfield flags
Definition: glew.h:2767
Uint16 attack_level
Definition: SDL_haptic.h:713
#define SDL_HAPTIC_RAMP
Ramp effect supported.
Definition: SDL_haptic.h:216
uint8_t Uint8
An unsigned 8-bit integer type.
Definition: SDL_stdinc.h:129
#define SDL_HAPTIC_SPRING
Spring effect supported - uses axes position.
Definition: SDL_haptic.h:226
DECLSPEC int SDLCALL SDL_atoi(const char *str)
Definition: SDL_string.c:774
typedef DWORD(WINAPI *XInputGetState_t)(DWORD dwUserIndex
#define SDL_zerop(x)
Definition: SDL_stdinc.h:255
GLdouble GLdouble GLdouble b
Definition: glew.h:8383
A structure containing a template for the SDL_HAPTIC_CUSTOM effect.
Definition: SDL_haptic.h:691
int SDL_SYS_HapticStopAll(SDL_Haptic *haptic)
uint16_t Uint16
An unsigned 16-bit integer type.
Definition: SDL_stdinc.h:137
GLclampf ref
Definition: gl2ext.h:1455
#define SDL_HAPTIC_SAWTOOTHUP
Sawtoothup wave effect supported.
Definition: SDL_haptic.h:198
#define str(s)
DECLSPEC void SDLCALL SDL_DestroyMutex(SDL_mutex *mutex)
Definition: SDL_sysmutex.c:61
int SDL_SYS_HapticSetGain(SDL_Haptic *haptic, int gain)
GLenum src
Definition: glew.h:2396
int i
Definition: pngrutil.c:1377
#define SDL_HAPTIC_LEFTRIGHT
Left/Right effect supported.
Definition: SDL_haptic.h:177
int SDL_SYS_HapticRunEffect(SDL_Haptic *haptic, struct haptic_effect *effect, Uint32 iterations)
SDL_HapticPeriodic periodic
Definition: SDL_haptic.h:792
Uint16 attack_level
Definition: SDL_haptic.h:648
Uint16 attack_length
Definition: SDL_haptic.h:712
#define SDL_HAPTIC_FRICTION
Friction effect supported - uses axes movement.
Definition: SDL_haptic.h:256
typedef VOID(WINAPI *PFNWGLBLITCONTEXTFRAMEBUFFERAMDPROC)(HGLRC dstCtx
#define SDL_HAPTIC_SAWTOOTHDOWN
Sawtoothdown wave effect supported.
Definition: SDL_haptic.h:207
SDL_HapticDirection direction
Definition: SDL_haptic.h:461
SDL_HapticDirection direction
Definition: SDL_haptic.h:695
int SDL_SYS_HapticNewEffect(SDL_Haptic *haptic, struct haptic_effect *effect, SDL_HapticEffect *base)
#define SDL_HAPTIC_DAMPER
Damper effect supported - uses axes velocity.
Definition: SDL_haptic.h:236
Uint16 left_sat[3]
Definition: SDL_haptic.h:608
typedef BOOL(WINAPI *PFNWGLSETSTEREOEMITTERSTATE3DLPROC)(HDC hDC