1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 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 #ifdef SDL_HAPTIC_IOKIT
24
25 #include "SDL_assert.h"
26 #include "SDL_stdinc.h"
27 #include "SDL_haptic.h"
28 #include "../SDL_syshaptic.h"
29 #include "SDL_joystick.h"
30 #include "../../joystick/SDL_sysjoystick.h" /* For the real SDL_Joystick */
31 #include "../../joystick/darwin/SDL_sysjoystick_c.h" /* For joystick hwdata */
32 #include "SDL_syshaptic_c.h"
33
34 #include <IOKit/IOKitLib.h>
35 #include <IOKit/hid/IOHIDKeys.h>
36 #include <IOKit/hid/IOHIDUsageTables.h>
37 #include <ForceFeedback/ForceFeedback.h>
38 #include <ForceFeedback/ForceFeedbackConstants.h>
39
40 #ifndef IO_OBJECT_NULL
41 #define IO_OBJECT_NULL ((io_service_t)0)
42 #endif
43
44 /*
45 * List of available haptic devices.
46 */
47 typedef struct SDL_hapticlist_item
48 {
49 char name[256]; /* Name of the device. */
50
51 io_service_t dev; /* Node we use to create the device. */
52 SDL_Haptic *haptic; /* Haptic currently associated with it. */
53
54 /* Usage pages for determining if it's a mouse or not. */
55 long usage;
56 long usagePage;
57
58 struct SDL_hapticlist_item *next;
59 } SDL_hapticlist_item;
60
61
62 /*
63 * Haptic system hardware data.
64 */
65 struct haptic_hwdata
66 {
67 FFDeviceObjectReference device; /* Hardware device. */
68 UInt8 axes[3];
69 };
70
71
72 /*
73 * Haptic system effect data.
74 */
75 struct haptic_hweffect
76 {
77 FFEffectObjectReference ref; /* Reference. */
78 struct FFEFFECT effect; /* Hardware effect. */
79 };
80
81 /*
82 * Prototypes.
83 */
84 static void SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type);
85 static int HIDGetDeviceProduct(io_service_t dev, char *name);
86
87 static SDL_hapticlist_item *SDL_hapticlist = NULL;
88 static SDL_hapticlist_item *SDL_hapticlist_tail = NULL;
89 static int numhaptics = -1;
90
91 /*
92 * Like strerror but for force feedback errors.
93 */
94 static const char *
FFStrError(unsigned int err)95 FFStrError(unsigned int err)
96 {
97 switch (err) {
98 case FFERR_DEVICEFULL:
99 return "device full";
100 /* This should be valid, but for some reason isn't defined... */
101 /* case FFERR_DEVICENOTREG:
102 return "device not registered"; */
103 case FFERR_DEVICEPAUSED:
104 return "device paused";
105 case FFERR_DEVICERELEASED:
106 return "device released";
107 case FFERR_EFFECTPLAYING:
108 return "effect playing";
109 case FFERR_EFFECTTYPEMISMATCH:
110 return "effect type mismatch";
111 case FFERR_EFFECTTYPENOTSUPPORTED:
112 return "effect type not supported";
113 case FFERR_GENERIC:
114 return "undetermined error";
115 case FFERR_HASEFFECTS:
116 return "device has effects";
117 case FFERR_INCOMPLETEEFFECT:
118 return "incomplete effect";
119 case FFERR_INTERNAL:
120 return "internal fault";
121 case FFERR_INVALIDDOWNLOADID:
122 return "invalid download id";
123 case FFERR_INVALIDPARAM:
124 return "invalid parameter";
125 case FFERR_MOREDATA:
126 return "more data";
127 case FFERR_NOINTERFACE:
128 return "interface not supported";
129 case FFERR_NOTDOWNLOADED:
130 return "effect is not downloaded";
131 case FFERR_NOTINITIALIZED:
132 return "object has not been initialized";
133 case FFERR_OUTOFMEMORY:
134 return "out of memory";
135 case FFERR_UNPLUGGED:
136 return "device is unplugged";
137 case FFERR_UNSUPPORTED:
138 return "function call unsupported";
139 case FFERR_UNSUPPORTEDAXIS:
140 return "axis unsupported";
141
142 default:
143 return "unknown error";
144 }
145 }
146
147
148 /*
149 * Initializes the haptic subsystem.
150 */
151 int
SDL_SYS_HapticInit(void)152 SDL_SYS_HapticInit(void)
153 {
154 IOReturn result;
155 io_iterator_t iter;
156 CFDictionaryRef match;
157 io_service_t device;
158
159 if (numhaptics != -1) {
160 return SDL_SetError("Haptic subsystem already initialized!");
161 }
162 numhaptics = 0;
163
164 /* Get HID devices. */
165 match = IOServiceMatching(kIOHIDDeviceKey);
166 if (match == NULL) {
167 return SDL_SetError("Haptic: Failed to get IOServiceMatching.");
168 }
169
170 /* Now search I/O Registry for matching devices. */
171 result = IOServiceGetMatchingServices(kIOMasterPortDefault, match, &iter);
172 if (result != kIOReturnSuccess) {
173 return SDL_SetError("Haptic: Couldn't create a HID object iterator.");
174 }
175 /* IOServiceGetMatchingServices consumes dictionary. */
176
177 if (!IOIteratorIsValid(iter)) { /* No iterator. */
178 return 0;
179 }
180
181 while ((device = IOIteratorNext(iter)) != IO_OBJECT_NULL) {
182 MacHaptic_MaybeAddDevice(device);
183 /* always release as the AddDevice will retain IF it's a forcefeedback device */
184 IOObjectRelease(device);
185 }
186 IOObjectRelease(iter);
187
188 return numhaptics;
189 }
190
191 int
SDL_SYS_NumHaptics()192 SDL_SYS_NumHaptics()
193 {
194 return numhaptics;
195 }
196
197 static SDL_hapticlist_item *
HapticByDevIndex(int device_index)198 HapticByDevIndex(int device_index)
199 {
200 SDL_hapticlist_item *item = SDL_hapticlist;
201
202 if ((device_index < 0) || (device_index >= numhaptics)) {
203 return NULL;
204 }
205
206 while (device_index > 0) {
207 SDL_assert(item != NULL);
208 --device_index;
209 item = item->next;
210 }
211
212 return item;
213 }
214
215 int
MacHaptic_MaybeAddDevice(io_object_t device)216 MacHaptic_MaybeAddDevice( io_object_t device )
217 {
218 IOReturn result;
219 CFMutableDictionaryRef hidProperties;
220 CFTypeRef refCF;
221 SDL_hapticlist_item *item;
222
223 if (numhaptics == -1) {
224 return -1; /* not initialized. We'll pick these up on enumeration if we init later. */
225 }
226
227 /* Check for force feedback. */
228 if (FFIsForceFeedback(device) != FF_OK) {
229 return -1;
230 }
231
232 /* Make sure we don't already have it */
233 for (item = SDL_hapticlist; item ; item = item->next)
234 {
235 if (IOObjectIsEqualTo((io_object_t) item->dev, device)) {
236 /* Already added */
237 return -1;
238 }
239 }
240
241 item = (SDL_hapticlist_item *)SDL_calloc(1, sizeof(SDL_hapticlist_item));
242 if (item == NULL) {
243 return SDL_SetError("Could not allocate haptic storage");
244 }
245
246 /* retain it as we are going to keep it around a while */
247 IOObjectRetain(device);
248
249 /* Set basic device data. */
250 HIDGetDeviceProduct(device, item->name);
251 item->dev = device;
252
253 /* Set usage pages. */
254 hidProperties = 0;
255 refCF = 0;
256 result = IORegistryEntryCreateCFProperties(device,
257 &hidProperties,
258 kCFAllocatorDefault,
259 kNilOptions);
260 if ((result == KERN_SUCCESS) && hidProperties) {
261 refCF = CFDictionaryGetValue(hidProperties,
262 CFSTR(kIOHIDPrimaryUsagePageKey));
263 if (refCF) {
264 if (!CFNumberGetValue(refCF, kCFNumberLongType, &item->usagePage)) {
265 SDL_SetError("Haptic: Receiving device's usage page.");
266 }
267 refCF = CFDictionaryGetValue(hidProperties,
268 CFSTR(kIOHIDPrimaryUsageKey));
269 if (refCF) {
270 if (!CFNumberGetValue(refCF, kCFNumberLongType, &item->usage)) {
271 SDL_SetError("Haptic: Receiving device's usage.");
272 }
273 }
274 }
275 CFRelease(hidProperties);
276 }
277
278 if (SDL_hapticlist_tail == NULL) {
279 SDL_hapticlist = SDL_hapticlist_tail = item;
280 } else {
281 SDL_hapticlist_tail->next = item;
282 SDL_hapticlist_tail = item;
283 }
284
285 /* Device has been added. */
286 ++numhaptics;
287
288 return numhaptics;
289 }
290
291 int
MacHaptic_MaybeRemoveDevice(io_object_t device)292 MacHaptic_MaybeRemoveDevice( io_object_t device )
293 {
294 SDL_hapticlist_item *item;
295 SDL_hapticlist_item *prev = NULL;
296
297 if (numhaptics == -1) {
298 return -1; /* not initialized. ignore this. */
299 }
300
301 for (item = SDL_hapticlist; item != NULL; item = item->next) {
302 /* found it, remove it. */
303 if (IOObjectIsEqualTo((io_object_t) item->dev, device)) {
304 const int retval = item->haptic ? item->haptic->index : -1;
305
306 if (prev != NULL) {
307 prev->next = item->next;
308 } else {
309 SDL_assert(SDL_hapticlist == item);
310 SDL_hapticlist = item->next;
311 }
312 if (item == SDL_hapticlist_tail) {
313 SDL_hapticlist_tail = prev;
314 }
315
316 /* Need to decrement the haptic count */
317 --numhaptics;
318 /* !!! TODO: Send a haptic remove event? */
319
320 IOObjectRelease(item->dev);
321 SDL_free(item);
322 return retval;
323 }
324 prev = item;
325 }
326
327 return -1;
328 }
329
330 /*
331 * Return the name of a haptic device, does not need to be opened.
332 */
333 const char *
SDL_SYS_HapticName(int index)334 SDL_SYS_HapticName(int index)
335 {
336 SDL_hapticlist_item *item;
337 item = HapticByDevIndex(index);
338 return item->name;
339 }
340
341 /*
342 * Gets the device's product name.
343 */
344 static int
HIDGetDeviceProduct(io_service_t dev,char * name)345 HIDGetDeviceProduct(io_service_t dev, char *name)
346 {
347 CFMutableDictionaryRef hidProperties, usbProperties;
348 io_registry_entry_t parent1, parent2;
349 kern_return_t ret;
350
351 hidProperties = usbProperties = 0;
352
353 ret = IORegistryEntryCreateCFProperties(dev, &hidProperties,
354 kCFAllocatorDefault, kNilOptions);
355 if ((ret != KERN_SUCCESS) || !hidProperties) {
356 return SDL_SetError("Haptic: Unable to create CFProperties.");
357 }
358
359 /* Mac OS X currently is not mirroring all USB properties to HID page so need to look at USB device page also
360 * get dictionary for USB properties: step up two levels and get CF dictionary for USB properties
361 */
362 if ((KERN_SUCCESS ==
363 IORegistryEntryGetParentEntry(dev, kIOServicePlane, &parent1))
364 && (KERN_SUCCESS ==
365 IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2))
366 && (KERN_SUCCESS ==
367 IORegistryEntryCreateCFProperties(parent2, &usbProperties,
368 kCFAllocatorDefault,
369 kNilOptions))) {
370 if (usbProperties) {
371 CFTypeRef refCF = 0;
372 /* get device info
373 * try hid dictionary first, if fail then go to USB dictionary
374 */
375
376
377 /* Get product name */
378 refCF = CFDictionaryGetValue(hidProperties, CFSTR(kIOHIDProductKey));
379 if (!refCF) {
380 refCF = CFDictionaryGetValue(usbProperties,
381 CFSTR("USB Product Name"));
382 }
383 if (refCF) {
384 if (!CFStringGetCString(refCF, name, 256,
385 CFStringGetSystemEncoding())) {
386 return SDL_SetError("Haptic: CFStringGetCString error retrieving pDevice->product.");
387 }
388 }
389
390 CFRelease(usbProperties);
391 } else {
392 return SDL_SetError("Haptic: IORegistryEntryCreateCFProperties failed to create usbProperties.");
393 }
394
395 /* Release stuff. */
396 if (kIOReturnSuccess != IOObjectRelease(parent2)) {
397 SDL_SetError("Haptic: IOObjectRelease error with parent2.");
398 }
399 if (kIOReturnSuccess != IOObjectRelease(parent1)) {
400 SDL_SetError("Haptic: IOObjectRelease error with parent1.");
401 }
402 } else {
403 return SDL_SetError("Haptic: Error getting registry entries.");
404 }
405
406 return 0;
407 }
408
409
410 #define FF_TEST(ff, s) \
411 if (features.supportedEffects & (ff)) supported |= (s)
412 /*
413 * Gets supported features.
414 */
415 static unsigned int
GetSupportedFeatures(SDL_Haptic * haptic)416 GetSupportedFeatures(SDL_Haptic * haptic)
417 {
418 HRESULT ret;
419 FFDeviceObjectReference device;
420 FFCAPABILITIES features;
421 unsigned int supported;
422 Uint32 val;
423
424 device = haptic->hwdata->device;
425
426 ret = FFDeviceGetForceFeedbackCapabilities(device, &features);
427 if (ret != FF_OK) {
428 return SDL_SetError("Haptic: Unable to get device's supported features.");
429 }
430
431 supported = 0;
432
433 /* Get maximum effects. */
434 haptic->neffects = features.storageCapacity;
435 haptic->nplaying = features.playbackCapacity;
436
437 /* Test for effects. */
438 FF_TEST(FFCAP_ET_CONSTANTFORCE, SDL_HAPTIC_CONSTANT);
439 FF_TEST(FFCAP_ET_RAMPFORCE, SDL_HAPTIC_RAMP);
440 /* !!! FIXME: put this back when we have more bits in 2.1 */
441 /* FF_TEST(FFCAP_ET_SQUARE, SDL_HAPTIC_SQUARE); */
442 FF_TEST(FFCAP_ET_SINE, SDL_HAPTIC_SINE);
443 FF_TEST(FFCAP_ET_TRIANGLE, SDL_HAPTIC_TRIANGLE);
444 FF_TEST(FFCAP_ET_SAWTOOTHUP, SDL_HAPTIC_SAWTOOTHUP);
445 FF_TEST(FFCAP_ET_SAWTOOTHDOWN, SDL_HAPTIC_SAWTOOTHDOWN);
446 FF_TEST(FFCAP_ET_SPRING, SDL_HAPTIC_SPRING);
447 FF_TEST(FFCAP_ET_DAMPER, SDL_HAPTIC_DAMPER);
448 FF_TEST(FFCAP_ET_INERTIA, SDL_HAPTIC_INERTIA);
449 FF_TEST(FFCAP_ET_FRICTION, SDL_HAPTIC_FRICTION);
450 FF_TEST(FFCAP_ET_CUSTOMFORCE, SDL_HAPTIC_CUSTOM);
451
452 /* Check if supports gain. */
453 ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_FFGAIN,
454 &val, sizeof(val));
455 if (ret == FF_OK) {
456 supported |= SDL_HAPTIC_GAIN;
457 } else if (ret != FFERR_UNSUPPORTED) {
458 return SDL_SetError("Haptic: Unable to get if device supports gain: %s.",
459 FFStrError(ret));
460 }
461
462 /* Checks if supports autocenter. */
463 ret = FFDeviceGetForceFeedbackProperty(device, FFPROP_AUTOCENTER,
464 &val, sizeof(val));
465 if (ret == FF_OK) {
466 supported |= SDL_HAPTIC_AUTOCENTER;
467 } else if (ret != FFERR_UNSUPPORTED) {
468 return SDL_SetError
469 ("Haptic: Unable to get if device supports autocenter: %s.",
470 FFStrError(ret));
471 }
472
473 /* Check for axes, we have an artificial limit on axes */
474 haptic->naxes = ((features.numFfAxes) > 3) ? 3 : features.numFfAxes;
475 /* Actually store the axes we want to use */
476 SDL_memcpy(haptic->hwdata->axes, features.ffAxes,
477 haptic->naxes * sizeof(Uint8));
478
479 /* Always supported features. */
480 supported |= SDL_HAPTIC_STATUS | SDL_HAPTIC_PAUSE;
481
482 haptic->supported = supported;
483 return 0;
484 }
485
486
487 /*
488 * Opens the haptic device from the file descriptor.
489 */
490 static int
SDL_SYS_HapticOpenFromService(SDL_Haptic * haptic,io_service_t service)491 SDL_SYS_HapticOpenFromService(SDL_Haptic * haptic, io_service_t service)
492 {
493 HRESULT ret;
494 int ret2;
495
496 /* Allocate the hwdata */
497 haptic->hwdata = (struct haptic_hwdata *)
498 SDL_malloc(sizeof(*haptic->hwdata));
499 if (haptic->hwdata == NULL) {
500 SDL_OutOfMemory();
501 goto creat_err;
502 }
503 SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
504
505 /* Open the device */
506 ret = FFCreateDevice(service, &haptic->hwdata->device);
507 if (ret != FF_OK) {
508 SDL_SetError("Haptic: Unable to create device from service: %s.",
509 FFStrError(ret));
510 goto creat_err;
511 }
512
513 /* Get supported features. */
514 ret2 = GetSupportedFeatures(haptic);
515 if (ret2 < 0) {
516 goto open_err;
517 }
518
519
520 /* Reset and then enable actuators. */
521 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
522 FFSFFC_RESET);
523 if (ret != FF_OK) {
524 SDL_SetError("Haptic: Unable to reset device: %s.", FFStrError(ret));
525 goto open_err;
526 }
527 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
528 FFSFFC_SETACTUATORSON);
529 if (ret != FF_OK) {
530 SDL_SetError("Haptic: Unable to enable actuators: %s.",
531 FFStrError(ret));
532 goto open_err;
533 }
534
535
536 /* Allocate effects memory. */
537 haptic->effects = (struct haptic_effect *)
538 SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
539 if (haptic->effects == NULL) {
540 SDL_OutOfMemory();
541 goto open_err;
542 }
543 /* Clear the memory */
544 SDL_memset(haptic->effects, 0,
545 sizeof(struct haptic_effect) * haptic->neffects);
546
547 return 0;
548
549 /* Error handling */
550 open_err:
551 FFReleaseDevice(haptic->hwdata->device);
552 creat_err:
553 if (haptic->hwdata != NULL) {
554 SDL_free(haptic->hwdata);
555 haptic->hwdata = NULL;
556 }
557 return -1;
558
559 }
560
561
562 /*
563 * Opens a haptic device for usage.
564 */
565 int
SDL_SYS_HapticOpen(SDL_Haptic * haptic)566 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
567 {
568 SDL_hapticlist_item *item;
569 item = HapticByDevIndex(haptic->index);
570
571 return SDL_SYS_HapticOpenFromService(haptic, item->dev);
572 }
573
574
575 /*
576 * Opens a haptic device from first mouse it finds for usage.
577 */
578 int
SDL_SYS_HapticMouse(void)579 SDL_SYS_HapticMouse(void)
580 {
581 int device_index = 0;
582 SDL_hapticlist_item *item;
583
584 for (item = SDL_hapticlist; item; item = item->next) {
585 if ((item->usagePage == kHIDPage_GenericDesktop) &&
586 (item->usage == kHIDUsage_GD_Mouse)) {
587 return device_index;
588 }
589 ++device_index;
590 }
591
592 return -1;
593 }
594
595
596 /*
597 * Checks to see if a joystick has haptic features.
598 */
599 int
SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)600 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
601 {
602 if (joystick->hwdata->ffservice != 0) {
603 return SDL_TRUE;
604 }
605 return SDL_FALSE;
606 }
607
608
609 /*
610 * Checks to see if the haptic device and joystick are in reality the same.
611 */
612 int
SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic,SDL_Joystick * joystick)613 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
614 {
615 if (IOObjectIsEqualTo((io_object_t) ((size_t)haptic->hwdata->device),
616 joystick->hwdata->ffservice)) {
617 return 1;
618 }
619 return 0;
620 }
621
622
623 /*
624 * Opens a SDL_Haptic from a SDL_Joystick.
625 */
626 int
SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic,SDL_Joystick * joystick)627 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
628 {
629 int device_index = 0;
630 SDL_hapticlist_item *item;
631
632 for (item = SDL_hapticlist; item; item = item->next) {
633 if (IOObjectIsEqualTo((io_object_t) item->dev,
634 joystick->hwdata->ffservice)) {
635 haptic->index = device_index;
636 break;
637 }
638 ++device_index;
639 }
640
641 return SDL_SYS_HapticOpenFromService(haptic, joystick->hwdata->ffservice);
642 }
643
644
645 /*
646 * Closes the haptic device.
647 */
648 void
SDL_SYS_HapticClose(SDL_Haptic * haptic)649 SDL_SYS_HapticClose(SDL_Haptic * haptic)
650 {
651 if (haptic->hwdata) {
652
653 /* Free Effects. */
654 SDL_free(haptic->effects);
655 haptic->effects = NULL;
656 haptic->neffects = 0;
657
658 /* Clean up */
659 FFReleaseDevice(haptic->hwdata->device);
660
661 /* Free */
662 SDL_free(haptic->hwdata);
663 haptic->hwdata = NULL;
664 }
665 }
666
667
668 /*
669 * Clean up after system specific haptic stuff
670 */
671 void
SDL_SYS_HapticQuit(void)672 SDL_SYS_HapticQuit(void)
673 {
674 SDL_hapticlist_item *item;
675 SDL_hapticlist_item *next = NULL;
676
677 for (item = SDL_hapticlist; item; item = next) {
678 next = item->next;
679 /* Opened and not closed haptics are leaked, this is on purpose.
680 * Close your haptic devices after usage. */
681
682 /* Free the io_service_t */
683 IOObjectRelease(item->dev);
684 SDL_free(item);
685 }
686
687 numhaptics = -1;
688 SDL_hapticlist = NULL;
689 SDL_hapticlist_tail = NULL;
690 }
691
692
693 /*
694 * Converts an SDL trigger button to an FFEFFECT trigger button.
695 */
696 static DWORD
FFGetTriggerButton(Uint16 button)697 FFGetTriggerButton(Uint16 button)
698 {
699 DWORD dwTriggerButton;
700
701 dwTriggerButton = FFEB_NOTRIGGER;
702
703 if (button != 0) {
704 dwTriggerButton = FFJOFS_BUTTON(button - 1);
705 }
706
707 return dwTriggerButton;
708 }
709
710
711 /*
712 * Sets the direction.
713 */
714 static int
SDL_SYS_SetDirection(FFEFFECT * effect,SDL_HapticDirection * dir,int naxes)715 SDL_SYS_SetDirection(FFEFFECT * effect, SDL_HapticDirection * dir, int naxes)
716 {
717 LONG *rglDir;
718
719 /* Handle no axes a part. */
720 if (naxes == 0) {
721 effect->dwFlags |= FFEFF_SPHERICAL; /* Set as default. */
722 effect->rglDirection = NULL;
723 return 0;
724 }
725
726 /* Has axes. */
727 rglDir = SDL_malloc(sizeof(LONG) * naxes);
728 if (rglDir == NULL) {
729 return SDL_OutOfMemory();
730 }
731 SDL_memset(rglDir, 0, sizeof(LONG) * naxes);
732 effect->rglDirection = rglDir;
733
734 switch (dir->type) {
735 case SDL_HAPTIC_POLAR:
736 effect->dwFlags |= FFEFF_POLAR;
737 rglDir[0] = dir->dir[0];
738 return 0;
739 case SDL_HAPTIC_CARTESIAN:
740 effect->dwFlags |= FFEFF_CARTESIAN;
741 rglDir[0] = dir->dir[0];
742 if (naxes > 1) {
743 rglDir[1] = dir->dir[1];
744 }
745 if (naxes > 2) {
746 rglDir[2] = dir->dir[2];
747 }
748 return 0;
749 case SDL_HAPTIC_SPHERICAL:
750 effect->dwFlags |= FFEFF_SPHERICAL;
751 rglDir[0] = dir->dir[0];
752 if (naxes > 1) {
753 rglDir[1] = dir->dir[1];
754 }
755 if (naxes > 2) {
756 rglDir[2] = dir->dir[2];
757 }
758 return 0;
759
760 default:
761 return SDL_SetError("Haptic: Unknown direction type.");
762 }
763 }
764
765
766 /* Clamps and converts. */
767 #define CCONVERT(x) (((x) > 0x7FFF) ? 10000 : ((x)*10000) / 0x7FFF)
768 /* Just converts. */
769 #define CONVERT(x) (((x)*10000) / 0x7FFF)
770 /*
771 * Creates the FFEFFECT from a SDL_HapticEffect.
772 */
773 static int
SDL_SYS_ToFFEFFECT(SDL_Haptic * haptic,FFEFFECT * dest,SDL_HapticEffect * src)774 SDL_SYS_ToFFEFFECT(SDL_Haptic * haptic, FFEFFECT * dest, SDL_HapticEffect * src)
775 {
776 int i;
777 FFCONSTANTFORCE *constant = NULL;
778 FFPERIODIC *periodic = NULL;
779 FFCONDITION *condition = NULL; /* Actually an array of conditions - one per axis. */
780 FFRAMPFORCE *ramp = NULL;
781 FFCUSTOMFORCE *custom = NULL;
782 FFENVELOPE *envelope = NULL;
783 SDL_HapticConstant *hap_constant = NULL;
784 SDL_HapticPeriodic *hap_periodic = NULL;
785 SDL_HapticCondition *hap_condition = NULL;
786 SDL_HapticRamp *hap_ramp = NULL;
787 SDL_HapticCustom *hap_custom = NULL;
788 DWORD *axes = NULL;
789
790 /* Set global stuff. */
791 SDL_memset(dest, 0, sizeof(FFEFFECT));
792 dest->dwSize = sizeof(FFEFFECT); /* Set the structure size. */
793 dest->dwSamplePeriod = 0; /* Not used by us. */
794 dest->dwGain = 10000; /* Gain is set globally, not locally. */
795 dest->dwFlags = FFEFF_OBJECTOFFSETS; /* Seems obligatory. */
796
797 /* Envelope. */
798 envelope = SDL_malloc(sizeof(FFENVELOPE));
799 if (envelope == NULL) {
800 return SDL_OutOfMemory();
801 }
802 SDL_memset(envelope, 0, sizeof(FFENVELOPE));
803 dest->lpEnvelope = envelope;
804 envelope->dwSize = sizeof(FFENVELOPE); /* Always should be this. */
805
806 /* Axes. */
807 dest->cAxes = haptic->naxes;
808 if (dest->cAxes > 0) {
809 axes = SDL_malloc(sizeof(DWORD) * dest->cAxes);
810 if (axes == NULL) {
811 return SDL_OutOfMemory();
812 }
813 axes[0] = haptic->hwdata->axes[0]; /* Always at least one axis. */
814 if (dest->cAxes > 1) {
815 axes[1] = haptic->hwdata->axes[1];
816 }
817 if (dest->cAxes > 2) {
818 axes[2] = haptic->hwdata->axes[2];
819 }
820 dest->rgdwAxes = axes;
821 }
822
823
824 /* The big type handling switch, even bigger then Linux's version. */
825 switch (src->type) {
826 case SDL_HAPTIC_CONSTANT:
827 hap_constant = &src->constant;
828 constant = SDL_malloc(sizeof(FFCONSTANTFORCE));
829 if (constant == NULL) {
830 return SDL_OutOfMemory();
831 }
832 SDL_memset(constant, 0, sizeof(FFCONSTANTFORCE));
833
834 /* Specifics */
835 constant->lMagnitude = CONVERT(hap_constant->level);
836 dest->cbTypeSpecificParams = sizeof(FFCONSTANTFORCE);
837 dest->lpvTypeSpecificParams = constant;
838
839 /* Generics */
840 dest->dwDuration = hap_constant->length * 1000; /* In microseconds. */
841 dest->dwTriggerButton = FFGetTriggerButton(hap_constant->button);
842 dest->dwTriggerRepeatInterval = hap_constant->interval;
843 dest->dwStartDelay = hap_constant->delay * 1000; /* In microseconds. */
844
845 /* Direction. */
846 if (SDL_SYS_SetDirection(dest, &hap_constant->direction, dest->cAxes)
847 < 0) {
848 return -1;
849 }
850
851 /* Envelope */
852 if ((hap_constant->attack_length == 0)
853 && (hap_constant->fade_length == 0)) {
854 SDL_free(envelope);
855 dest->lpEnvelope = NULL;
856 } else {
857 envelope->dwAttackLevel = CCONVERT(hap_constant->attack_level);
858 envelope->dwAttackTime = hap_constant->attack_length * 1000;
859 envelope->dwFadeLevel = CCONVERT(hap_constant->fade_level);
860 envelope->dwFadeTime = hap_constant->fade_length * 1000;
861 }
862
863 break;
864
865 case SDL_HAPTIC_SINE:
866 /* !!! FIXME: put this back when we have more bits in 2.1 */
867 /* case SDL_HAPTIC_SQUARE: */
868 case SDL_HAPTIC_TRIANGLE:
869 case SDL_HAPTIC_SAWTOOTHUP:
870 case SDL_HAPTIC_SAWTOOTHDOWN:
871 hap_periodic = &src->periodic;
872 periodic = SDL_malloc(sizeof(FFPERIODIC));
873 if (periodic == NULL) {
874 return SDL_OutOfMemory();
875 }
876 SDL_memset(periodic, 0, sizeof(FFPERIODIC));
877
878 /* Specifics */
879 periodic->dwMagnitude = CONVERT(SDL_abs(hap_periodic->magnitude));
880 periodic->lOffset = CONVERT(hap_periodic->offset);
881 periodic->dwPhase =
882 (hap_periodic->phase + (hap_periodic->magnitude < 0 ? 18000 : 0)) % 36000;
883 periodic->dwPeriod = hap_periodic->period * 1000;
884 dest->cbTypeSpecificParams = sizeof(FFPERIODIC);
885 dest->lpvTypeSpecificParams = periodic;
886
887 /* Generics */
888 dest->dwDuration = hap_periodic->length * 1000; /* In microseconds. */
889 dest->dwTriggerButton = FFGetTriggerButton(hap_periodic->button);
890 dest->dwTriggerRepeatInterval = hap_periodic->interval;
891 dest->dwStartDelay = hap_periodic->delay * 1000; /* In microseconds. */
892
893 /* Direction. */
894 if (SDL_SYS_SetDirection(dest, &hap_periodic->direction, dest->cAxes)
895 < 0) {
896 return -1;
897 }
898
899 /* Envelope */
900 if ((hap_periodic->attack_length == 0)
901 && (hap_periodic->fade_length == 0)) {
902 SDL_free(envelope);
903 dest->lpEnvelope = NULL;
904 } else {
905 envelope->dwAttackLevel = CCONVERT(hap_periodic->attack_level);
906 envelope->dwAttackTime = hap_periodic->attack_length * 1000;
907 envelope->dwFadeLevel = CCONVERT(hap_periodic->fade_level);
908 envelope->dwFadeTime = hap_periodic->fade_length * 1000;
909 }
910
911 break;
912
913 case SDL_HAPTIC_SPRING:
914 case SDL_HAPTIC_DAMPER:
915 case SDL_HAPTIC_INERTIA:
916 case SDL_HAPTIC_FRICTION:
917 hap_condition = &src->condition;
918 if (dest->cAxes > 0) {
919 condition = SDL_malloc(sizeof(FFCONDITION) * dest->cAxes);
920 if (condition == NULL) {
921 return SDL_OutOfMemory();
922 }
923 SDL_memset(condition, 0, sizeof(FFCONDITION));
924
925 /* Specifics */
926 for (i = 0; i < dest->cAxes; i++) {
927 condition[i].lOffset = CONVERT(hap_condition->center[i]);
928 condition[i].lPositiveCoefficient =
929 CONVERT(hap_condition->right_coeff[i]);
930 condition[i].lNegativeCoefficient =
931 CONVERT(hap_condition->left_coeff[i]);
932 condition[i].dwPositiveSaturation =
933 CCONVERT(hap_condition->right_sat[i] / 2);
934 condition[i].dwNegativeSaturation =
935 CCONVERT(hap_condition->left_sat[i] / 2);
936 condition[i].lDeadBand = CCONVERT(hap_condition->deadband[i] / 2);
937 }
938 }
939
940 dest->cbTypeSpecificParams = sizeof(FFCONDITION) * dest->cAxes;
941 dest->lpvTypeSpecificParams = condition;
942
943 /* Generics */
944 dest->dwDuration = hap_condition->length * 1000; /* In microseconds. */
945 dest->dwTriggerButton = FFGetTriggerButton(hap_condition->button);
946 dest->dwTriggerRepeatInterval = hap_condition->interval;
947 dest->dwStartDelay = hap_condition->delay * 1000; /* In microseconds. */
948
949 /* Direction. */
950 if (SDL_SYS_SetDirection(dest, &hap_condition->direction, dest->cAxes)
951 < 0) {
952 return -1;
953 }
954
955 /* Envelope - Not actually supported by most CONDITION implementations. */
956 SDL_free(dest->lpEnvelope);
957 dest->lpEnvelope = NULL;
958
959 break;
960
961 case SDL_HAPTIC_RAMP:
962 hap_ramp = &src->ramp;
963 ramp = SDL_malloc(sizeof(FFRAMPFORCE));
964 if (ramp == NULL) {
965 return SDL_OutOfMemory();
966 }
967 SDL_memset(ramp, 0, sizeof(FFRAMPFORCE));
968
969 /* Specifics */
970 ramp->lStart = CONVERT(hap_ramp->start);
971 ramp->lEnd = CONVERT(hap_ramp->end);
972 dest->cbTypeSpecificParams = sizeof(FFRAMPFORCE);
973 dest->lpvTypeSpecificParams = ramp;
974
975 /* Generics */
976 dest->dwDuration = hap_ramp->length * 1000; /* In microseconds. */
977 dest->dwTriggerButton = FFGetTriggerButton(hap_ramp->button);
978 dest->dwTriggerRepeatInterval = hap_ramp->interval;
979 dest->dwStartDelay = hap_ramp->delay * 1000; /* In microseconds. */
980
981 /* Direction. */
982 if (SDL_SYS_SetDirection(dest, &hap_ramp->direction, dest->cAxes) < 0) {
983 return -1;
984 }
985
986 /* Envelope */
987 if ((hap_ramp->attack_length == 0) && (hap_ramp->fade_length == 0)) {
988 SDL_free(envelope);
989 dest->lpEnvelope = NULL;
990 } else {
991 envelope->dwAttackLevel = CCONVERT(hap_ramp->attack_level);
992 envelope->dwAttackTime = hap_ramp->attack_length * 1000;
993 envelope->dwFadeLevel = CCONVERT(hap_ramp->fade_level);
994 envelope->dwFadeTime = hap_ramp->fade_length * 1000;
995 }
996
997 break;
998
999 case SDL_HAPTIC_CUSTOM:
1000 hap_custom = &src->custom;
1001 custom = SDL_malloc(sizeof(FFCUSTOMFORCE));
1002 if (custom == NULL) {
1003 return SDL_OutOfMemory();
1004 }
1005 SDL_memset(custom, 0, sizeof(FFCUSTOMFORCE));
1006
1007 /* Specifics */
1008 custom->cChannels = hap_custom->channels;
1009 custom->dwSamplePeriod = hap_custom->period * 1000;
1010 custom->cSamples = hap_custom->samples;
1011 custom->rglForceData =
1012 SDL_malloc(sizeof(LONG) * custom->cSamples * custom->cChannels);
1013 for (i = 0; i < hap_custom->samples * hap_custom->channels; i++) { /* Copy data. */
1014 custom->rglForceData[i] = CCONVERT(hap_custom->data[i]);
1015 }
1016 dest->cbTypeSpecificParams = sizeof(FFCUSTOMFORCE);
1017 dest->lpvTypeSpecificParams = custom;
1018
1019 /* Generics */
1020 dest->dwDuration = hap_custom->length * 1000; /* In microseconds. */
1021 dest->dwTriggerButton = FFGetTriggerButton(hap_custom->button);
1022 dest->dwTriggerRepeatInterval = hap_custom->interval;
1023 dest->dwStartDelay = hap_custom->delay * 1000; /* In microseconds. */
1024
1025 /* Direction. */
1026 if (SDL_SYS_SetDirection(dest, &hap_custom->direction, dest->cAxes) <
1027 0) {
1028 return -1;
1029 }
1030
1031 /* Envelope */
1032 if ((hap_custom->attack_length == 0)
1033 && (hap_custom->fade_length == 0)) {
1034 SDL_free(envelope);
1035 dest->lpEnvelope = NULL;
1036 } else {
1037 envelope->dwAttackLevel = CCONVERT(hap_custom->attack_level);
1038 envelope->dwAttackTime = hap_custom->attack_length * 1000;
1039 envelope->dwFadeLevel = CCONVERT(hap_custom->fade_level);
1040 envelope->dwFadeTime = hap_custom->fade_length * 1000;
1041 }
1042
1043 break;
1044
1045
1046 default:
1047 return SDL_SetError("Haptic: Unknown effect type.");
1048 }
1049
1050 return 0;
1051 }
1052
1053
1054 /*
1055 * Frees an FFEFFECT allocated by SDL_SYS_ToFFEFFECT.
1056 */
1057 static void
SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect,int type)1058 SDL_SYS_HapticFreeFFEFFECT(FFEFFECT * effect, int type)
1059 {
1060 FFCUSTOMFORCE *custom;
1061
1062 SDL_free(effect->lpEnvelope);
1063 effect->lpEnvelope = NULL;
1064 SDL_free(effect->rgdwAxes);
1065 effect->rgdwAxes = NULL;
1066 if (effect->lpvTypeSpecificParams != NULL) {
1067 if (type == SDL_HAPTIC_CUSTOM) { /* Must free the custom data. */
1068 custom = (FFCUSTOMFORCE *) effect->lpvTypeSpecificParams;
1069 SDL_free(custom->rglForceData);
1070 custom->rglForceData = NULL;
1071 }
1072 SDL_free(effect->lpvTypeSpecificParams);
1073 effect->lpvTypeSpecificParams = NULL;
1074 }
1075 SDL_free(effect->rglDirection);
1076 effect->rglDirection = NULL;
1077 }
1078
1079
1080 /*
1081 * Gets the effect type from the generic SDL haptic effect wrapper.
1082 */
1083 CFUUIDRef
SDL_SYS_HapticEffectType(Uint16 type)1084 SDL_SYS_HapticEffectType(Uint16 type)
1085 {
1086 switch (type) {
1087 case SDL_HAPTIC_CONSTANT:
1088 return kFFEffectType_ConstantForce_ID;
1089
1090 case SDL_HAPTIC_RAMP:
1091 return kFFEffectType_RampForce_ID;
1092
1093 /* !!! FIXME: put this back when we have more bits in 2.1 */
1094 /* case SDL_HAPTIC_SQUARE:
1095 return kFFEffectType_Square_ID; */
1096
1097 case SDL_HAPTIC_SINE:
1098 return kFFEffectType_Sine_ID;
1099
1100 case SDL_HAPTIC_TRIANGLE:
1101 return kFFEffectType_Triangle_ID;
1102
1103 case SDL_HAPTIC_SAWTOOTHUP:
1104 return kFFEffectType_SawtoothUp_ID;
1105
1106 case SDL_HAPTIC_SAWTOOTHDOWN:
1107 return kFFEffectType_SawtoothDown_ID;
1108
1109 case SDL_HAPTIC_SPRING:
1110 return kFFEffectType_Spring_ID;
1111
1112 case SDL_HAPTIC_DAMPER:
1113 return kFFEffectType_Damper_ID;
1114
1115 case SDL_HAPTIC_INERTIA:
1116 return kFFEffectType_Inertia_ID;
1117
1118 case SDL_HAPTIC_FRICTION:
1119 return kFFEffectType_Friction_ID;
1120
1121 case SDL_HAPTIC_CUSTOM:
1122 return kFFEffectType_CustomForce_ID;
1123
1124 default:
1125 SDL_SetError("Haptic: Unknown effect type.");
1126 return NULL;
1127 }
1128 }
1129
1130
1131 /*
1132 * Creates a new haptic effect.
1133 */
1134 int
SDL_SYS_HapticNewEffect(SDL_Haptic * haptic,struct haptic_effect * effect,SDL_HapticEffect * base)1135 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1136 SDL_HapticEffect * base)
1137 {
1138 HRESULT ret;
1139 CFUUIDRef type;
1140
1141 /* Alloc the effect. */
1142 effect->hweffect = (struct haptic_hweffect *)
1143 SDL_malloc(sizeof(struct haptic_hweffect));
1144 if (effect->hweffect == NULL) {
1145 SDL_OutOfMemory();
1146 goto err_hweffect;
1147 }
1148
1149 /* Get the type. */
1150 type = SDL_SYS_HapticEffectType(base->type);
1151 if (type == NULL) {
1152 goto err_hweffect;
1153 }
1154
1155 /* Get the effect. */
1156 if (SDL_SYS_ToFFEFFECT(haptic, &effect->hweffect->effect, base) < 0) {
1157 goto err_effectdone;
1158 }
1159
1160 /* Create the actual effect. */
1161 ret = FFDeviceCreateEffect(haptic->hwdata->device, type,
1162 &effect->hweffect->effect,
1163 &effect->hweffect->ref);
1164 if (ret != FF_OK) {
1165 SDL_SetError("Haptic: Unable to create effect: %s.", FFStrError(ret));
1166 goto err_effectdone;
1167 }
1168
1169 return 0;
1170
1171 err_effectdone:
1172 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, base->type);
1173 err_hweffect:
1174 SDL_free(effect->hweffect);
1175 effect->hweffect = NULL;
1176 return -1;
1177 }
1178
1179
1180 /*
1181 * Updates an effect.
1182 */
1183 int
SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,struct haptic_effect * effect,SDL_HapticEffect * data)1184 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
1185 struct haptic_effect *effect,
1186 SDL_HapticEffect * data)
1187 {
1188 HRESULT ret;
1189 FFEffectParameterFlag flags;
1190 FFEFFECT temp;
1191
1192 /* Get the effect. */
1193 SDL_memset(&temp, 0, sizeof(FFEFFECT));
1194 if (SDL_SYS_ToFFEFFECT(haptic, &temp, data) < 0) {
1195 goto err_update;
1196 }
1197
1198 /* Set the flags. Might be worthwhile to diff temp with loaded effect and
1199 * only change those parameters. */
1200 flags = FFEP_DIRECTION |
1201 FFEP_DURATION |
1202 FFEP_ENVELOPE |
1203 FFEP_STARTDELAY |
1204 FFEP_TRIGGERBUTTON |
1205 FFEP_TRIGGERREPEATINTERVAL | FFEP_TYPESPECIFICPARAMS;
1206
1207 /* Create the actual effect. */
1208 ret = FFEffectSetParameters(effect->hweffect->ref, &temp, flags);
1209 if (ret != FF_OK) {
1210 SDL_SetError("Haptic: Unable to update effect: %s.", FFStrError(ret));
1211 goto err_update;
1212 }
1213
1214 /* Copy it over. */
1215 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect, data->type);
1216 SDL_memcpy(&effect->hweffect->effect, &temp, sizeof(FFEFFECT));
1217
1218 return 0;
1219
1220 err_update:
1221 SDL_SYS_HapticFreeFFEFFECT(&temp, data->type);
1222 return -1;
1223 }
1224
1225
1226 /*
1227 * Runs an effect.
1228 */
1229 int
SDL_SYS_HapticRunEffect(SDL_Haptic * haptic,struct haptic_effect * effect,Uint32 iterations)1230 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
1231 Uint32 iterations)
1232 {
1233 HRESULT ret;
1234 Uint32 iter;
1235
1236 /* Check if it's infinite. */
1237 if (iterations == SDL_HAPTIC_INFINITY) {
1238 iter = FF_INFINITE;
1239 } else
1240 iter = iterations;
1241
1242 /* Run the effect. */
1243 ret = FFEffectStart(effect->hweffect->ref, iter, 0);
1244 if (ret != FF_OK) {
1245 return SDL_SetError("Haptic: Unable to run the effect: %s.",
1246 FFStrError(ret));
1247 }
1248
1249 return 0;
1250 }
1251
1252
1253 /*
1254 * Stops an effect.
1255 */
1256 int
SDL_SYS_HapticStopEffect(SDL_Haptic * haptic,struct haptic_effect * effect)1257 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1258 {
1259 HRESULT ret;
1260
1261 ret = FFEffectStop(effect->hweffect->ref);
1262 if (ret != FF_OK) {
1263 return SDL_SetError("Haptic: Unable to stop the effect: %s.",
1264 FFStrError(ret));
1265 }
1266
1267 return 0;
1268 }
1269
1270
1271 /*
1272 * Frees the effect.
1273 */
1274 void
SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic,struct haptic_effect * effect)1275 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
1276 {
1277 HRESULT ret;
1278
1279 ret = FFDeviceReleaseEffect(haptic->hwdata->device, effect->hweffect->ref);
1280 if (ret != FF_OK) {
1281 SDL_SetError("Haptic: Error removing the effect from the device: %s.",
1282 FFStrError(ret));
1283 }
1284 SDL_SYS_HapticFreeFFEFFECT(&effect->hweffect->effect,
1285 effect->effect.type);
1286 SDL_free(effect->hweffect);
1287 effect->hweffect = NULL;
1288 }
1289
1290
1291 /*
1292 * Gets the status of a haptic effect.
1293 */
1294 int
SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,struct haptic_effect * effect)1295 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
1296 struct haptic_effect *effect)
1297 {
1298 HRESULT ret;
1299 FFEffectStatusFlag status;
1300
1301 ret = FFEffectGetEffectStatus(effect->hweffect->ref, &status);
1302 if (ret != FF_OK) {
1303 SDL_SetError("Haptic: Unable to get effect status: %s.",
1304 FFStrError(ret));
1305 return -1;
1306 }
1307
1308 if (status == 0) {
1309 return SDL_FALSE;
1310 }
1311 return SDL_TRUE; /* Assume it's playing or emulated. */
1312 }
1313
1314
1315 /*
1316 * Sets the gain.
1317 */
1318 int
SDL_SYS_HapticSetGain(SDL_Haptic * haptic,int gain)1319 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
1320 {
1321 HRESULT ret;
1322 Uint32 val;
1323
1324 val = gain * 100; /* Mac OS X uses 0 to 10,000 */
1325 ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
1326 FFPROP_FFGAIN, &val);
1327 if (ret != FF_OK) {
1328 return SDL_SetError("Haptic: Error setting gain: %s.", FFStrError(ret));
1329 }
1330
1331 return 0;
1332 }
1333
1334
1335 /*
1336 * Sets the autocentering.
1337 */
1338 int
SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic,int autocenter)1339 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
1340 {
1341 HRESULT ret;
1342 Uint32 val;
1343
1344 /* Mac OS X only has 0 (off) and 1 (on) */
1345 if (autocenter == 0) {
1346 val = 0;
1347 } else {
1348 val = 1;
1349 }
1350
1351 ret = FFDeviceSetForceFeedbackProperty(haptic->hwdata->device,
1352 FFPROP_AUTOCENTER, &val);
1353 if (ret != FF_OK) {
1354 return SDL_SetError("Haptic: Error setting autocenter: %s.",
1355 FFStrError(ret));
1356 }
1357
1358 return 0;
1359 }
1360
1361
1362 /*
1363 * Pauses the device.
1364 */
1365 int
SDL_SYS_HapticPause(SDL_Haptic * haptic)1366 SDL_SYS_HapticPause(SDL_Haptic * haptic)
1367 {
1368 HRESULT ret;
1369
1370 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1371 FFSFFC_PAUSE);
1372 if (ret != FF_OK) {
1373 return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
1374 }
1375
1376 return 0;
1377 }
1378
1379
1380 /*
1381 * Unpauses the device.
1382 */
1383 int
SDL_SYS_HapticUnpause(SDL_Haptic * haptic)1384 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
1385 {
1386 HRESULT ret;
1387
1388 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1389 FFSFFC_CONTINUE);
1390 if (ret != FF_OK) {
1391 return SDL_SetError("Haptic: Error pausing device: %s.", FFStrError(ret));
1392 }
1393
1394 return 0;
1395 }
1396
1397
1398 /*
1399 * Stops all currently playing effects.
1400 */
1401 int
SDL_SYS_HapticStopAll(SDL_Haptic * haptic)1402 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
1403 {
1404 HRESULT ret;
1405
1406 ret = FFDeviceSendForceFeedbackCommand(haptic->hwdata->device,
1407 FFSFFC_STOPALL);
1408 if (ret != FF_OK) {
1409 return SDL_SetError("Haptic: Error stopping device: %s.", FFStrError(ret));
1410 }
1411
1412 return 0;
1413 }
1414
1415 #endif /* SDL_HAPTIC_IOKIT */
1416
1417 /* vi: set ts=4 sw=4 expandtab: */
1418