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