comparison src/haptic/darwin/SDL_syshaptic.c @ 2713:0906692aa6a4

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