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