comparison src/haptic/linux/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 4094b4f1c3a1
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_LINUX
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/linux/SDL_sysjoystick_c.h" /* For joystick hwdata */
31
32 #include <unistd.h> /* close */
33 #include <linux/input.h> /* Force feedback linux stuff. */
34 #include <fcntl.h> /* O_RDWR */
35 #include <limits.h> /* INT_MAX */
36 #include <errno.h> /* errno, strerror */
37 #include <math.h> /* atan2 */
38
39 /* Just in case. */
40 #ifndef M_PI
41 # define M_PI 3.14159265358979323846
42 #endif
43
44
45 #define MAX_HAPTICS 32 /* It's doubtful someone has more then 32 evdev */
46
47
48 /*
49 * List of available haptic devices.
50 */
51 static struct
52 {
53 char *fname; /* Dev path name (like /dev/input/event1) */
54 SDL_Haptic *haptic; /* Assosciated haptic. */
55 } SDL_hapticlist[MAX_HAPTICS];
56
57
58 /*
59 * Haptic system hardware data.
60 */
61 struct haptic_hwdata
62 {
63 int fd; /* File descriptor of the device. */
64 char *fname; /* Points to the name in SDL_hapticlist. */
65 };
66
67
68 /*
69 * Haptic system effect data.
70 */
71 struct haptic_hweffect
72 {
73 struct ff_effect effect; /* The linux kernel effect structure. */
74 };
75
76
77
78 #define test_bit(nr, addr) \
79 (((1UL << ((nr) & 31)) & (((const unsigned int *) addr)[(nr) >> 5])) != 0)
80 #define EV_TEST(ev,f) \
81 if (test_bit((ev), features)) ret |= (f);
82 /*
83 * Test whether a device has haptic properties.
84 * Returns available properties or 0 if there are none.
85 */
86 static int
87 EV_IsHaptic(int fd)
88 {
89 unsigned int ret;
90 unsigned long features[1 + FF_MAX / sizeof(unsigned long)];
91
92 /* Ask device for what it has. */
93 ret = 0;
94 if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(features)), features) < 0) {
95 SDL_SetError("Haptic: Unable to get device's features: %s",
96 strerror(errno));
97 return -1;
98 }
99
100 /* Convert supported features to SDL_HAPTIC platform-neutral features. */
101 EV_TEST(FF_CONSTANT, SDL_HAPTIC_CONSTANT);
102 EV_TEST(FF_SINE, SDL_HAPTIC_SINE);
103 EV_TEST(FF_SQUARE, SDL_HAPTIC_SQUARE);
104 EV_TEST(FF_TRIANGLE, SDL_HAPTIC_TRIANGLE);
105 EV_TEST(FF_SAW_UP, SDL_HAPTIC_SAWTOOTHUP);
106 EV_TEST(FF_SAW_DOWN, SDL_HAPTIC_SAWTOOTHDOWN);
107 EV_TEST(FF_RAMP, SDL_HAPTIC_RAMP);
108 EV_TEST(FF_SPRING, SDL_HAPTIC_SPRING);
109 EV_TEST(FF_FRICTION, SDL_HAPTIC_FRICTION);
110 EV_TEST(FF_DAMPER, SDL_HAPTIC_DAMPER);
111 EV_TEST(FF_INERTIA, SDL_HAPTIC_INERTIA);
112 EV_TEST(FF_CUSTOM, SDL_HAPTIC_CUSTOM);
113 EV_TEST(FF_GAIN, SDL_HAPTIC_GAIN);
114 EV_TEST(FF_AUTOCENTER, SDL_HAPTIC_AUTOCENTER);
115
116 /* Return what it supports. */
117 return ret;
118 }
119
120
121 /*
122 * Tests whether a device is a mouse or not.
123 */
124 static int
125 EV_IsMouse(int fd)
126 {
127 unsigned long argp[40];
128
129 /* Ask for supported features. */
130 if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(argp)), argp) < 0) {
131 return -1;
132 }
133
134 /* Currently we only test for BTN_MOUSE which can give fake positives. */
135 if (test_bit(BTN_MOUSE, argp) != 0) {
136 return 1;
137 }
138
139 return 0;
140 }
141
142 /*
143 * Initializes the haptic subsystem by finding available devices.
144 */
145 int
146 SDL_SYS_HapticInit(void)
147 {
148 const char joydev_pattern[] = "/dev/input/event%d";
149 dev_t dev_nums[MAX_HAPTICS];
150 char path[PATH_MAX];
151 struct stat sb;
152 int fd;
153 int i, j, k;
154 int duplicate;
155 int numhaptics;
156
157 numhaptics = 0;
158
159 /*
160 * Limit amount of checks to MAX_HAPTICS since we may or may not have
161 * permission to some or all devices.
162 */
163 i = 0;
164 for (j = 0; j < MAX_HAPTICS; ++j) {
165
166 snprintf(path, PATH_MAX, joydev_pattern, i++);
167
168 /* check to see if file exists */
169 if (stat(path, &sb) != 0)
170 break;
171
172 /* check for duplicates */
173 duplicate = 0;
174 for (k = 0; (k < numhaptics) && !duplicate; ++k) {
175 if (sb.st_rdev == dev_nums[k]) {
176 duplicate = 1;
177 }
178 }
179 if (duplicate) {
180 continue;
181 }
182
183 /* try to open */
184 fd = open(path, O_RDWR, 0);
185 if (fd < 0)
186 continue;
187
188 #ifdef DEBUG_INPUT_EVENTS
189 printf("Checking %s\n", path);
190 #endif
191
192 /* see if it works */
193 if (EV_IsHaptic(fd) > 0) {
194 SDL_hapticlist[numhaptics].fname = SDL_strdup(path);
195 SDL_hapticlist[numhaptics].haptic = NULL;
196 dev_nums[numhaptics] = sb.st_rdev;
197 ++numhaptics;
198 }
199 close(fd);
200 }
201
202 return numhaptics;
203 }
204
205
206 /*
207 * Gets the name from a file descriptor.
208 */
209 static const char *
210 SDL_SYS_HapticNameFromFD(int fd)
211 {
212 static char namebuf[128];
213
214 /* We use the evdev name ioctl. */
215 if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) <= 0) {
216 return NULL;
217 }
218
219 return namebuf;
220 }
221
222
223 /*
224 * Return the name of a haptic device, does not need to be opened.
225 */
226 const char *
227 SDL_SYS_HapticName(int index)
228 {
229 int fd;
230 const char *name;
231
232 /* Open the haptic device. */
233 name = NULL;
234 fd = open(SDL_hapticlist[index].fname, O_RDONLY, 0);
235
236 if (fd >= 0) {
237
238 name = SDL_SYS_HapticNameFromFD(fd);
239 if (name == NULL) {
240 /* No name found, return device character device */
241 name = SDL_hapticlist[index].fname;
242 }
243 }
244 close(fd);
245
246 return name;
247 }
248
249
250 /*
251 * Opens the haptic device from the file descriptor.
252 */
253 static int
254 SDL_SYS_HapticOpenFromFD(SDL_Haptic * haptic, int fd)
255 {
256 const char *name;
257
258 /* Allocate the hwdata */
259 haptic->hwdata = (struct haptic_hwdata *)
260 SDL_malloc(sizeof(*haptic->hwdata));
261 if (haptic->hwdata == NULL) {
262 SDL_OutOfMemory();
263 goto open_err;
264 }
265 SDL_memset(haptic->hwdata, 0, sizeof(*haptic->hwdata));
266
267 /* Set the data. */
268 haptic->hwdata->fd = fd;
269 haptic->supported = EV_IsHaptic(fd);
270 haptic->naxes = 2; /* Hardcoded for now, not sure if it's possible to find out. */
271
272 /* Set the effects */
273 if (ioctl(fd, EVIOCGEFFECTS, &haptic->neffects) < 0) {
274 SDL_SetError("Haptic: Unable to query device memory: %s",
275 strerror(errno));
276 goto open_err;
277 }
278 haptic->nplaying = haptic->neffects; /* Linux makes no distinction. */
279 haptic->effects = (struct haptic_effect *)
280 SDL_malloc(sizeof(struct haptic_effect) * haptic->neffects);
281 if (haptic->effects == NULL) {
282 SDL_OutOfMemory();
283 goto open_err;
284 }
285 /* Clear the memory */
286 SDL_memset(haptic->effects, 0,
287 sizeof(struct haptic_effect) * haptic->neffects);
288
289 return 0;
290
291 /* Error handling */
292 open_err:
293 close(fd);
294 if (haptic->hwdata != NULL) {
295 free(haptic->hwdata);
296 haptic->hwdata = NULL;
297 }
298 return -1;
299 }
300
301
302 /*
303 * Opens a haptic device for usage.
304 */
305 int
306 SDL_SYS_HapticOpen(SDL_Haptic * haptic)
307 {
308 int fd;
309 int ret;
310
311 /* Open the character device */
312 fd = open(SDL_hapticlist[haptic->index].fname, O_RDWR, 0);
313 if (fd < 0) {
314 SDL_SetError("Haptic: Unable to open %s: %s",
315 SDL_hapticlist[haptic->index], strerror(errno));
316 return -1;
317 }
318
319 /* Try to create the haptic. */
320 ret = SDL_SYS_HapticOpenFromFD(haptic, fd); /* Already closes on error. */
321 if (ret < 0) {
322 return -1;
323 }
324
325 /* Set the fname. */
326 haptic->hwdata->fname = SDL_hapticlist[haptic->index].fname;
327 return 0;
328 }
329
330
331 /*
332 * Opens a haptic device from first mouse it finds for usage.
333 */
334 int
335 SDL_SYS_HapticMouse(void)
336 {
337 int fd;
338 int i;
339
340 for (i = 0; i < SDL_numhaptics; i++) {
341
342 /* Open the device. */
343 fd = open(SDL_hapticlist[i].fname, O_RDWR, 0);
344 if (fd < 0) {
345 SDL_SetError("Haptic: Unable to open %s: %s",
346 SDL_hapticlist[i], strerror(errno));
347 return -1;
348 }
349
350 /* Is it a mouse? */
351 if (EV_IsMouse(fd)) {
352 close(fd);
353 return i;
354 }
355
356 close(fd);
357 }
358
359 return -1;
360 }
361
362
363 /*
364 * Checks to see if a joystick has haptic features.
365 */
366 int
367 SDL_SYS_JoystickIsHaptic(SDL_Joystick * joystick)
368 {
369 return EV_IsHaptic(joystick->hwdata->fd);
370 }
371
372
373 /*
374 * Checks to see if the haptic device and joystick and in reality the same.
375 */
376 int
377 SDL_SYS_JoystickSameHaptic(SDL_Haptic * haptic, SDL_Joystick * joystick)
378 {
379 /* We are assuming linux is using evdev which should trump the old
380 * joystick methods. */
381 if (SDL_strcmp(joystick->hwdata->fname, haptic->hwdata->fname) == 0) {
382 return 1;
383 }
384 return 0;
385 }
386
387
388 /*
389 * Opens a SDL_Haptic from a SDL_Joystick.
390 */
391 int
392 SDL_SYS_HapticOpenFromJoystick(SDL_Haptic * haptic, SDL_Joystick * joystick)
393 {
394 int i;
395 int fd;
396 int ret;
397
398 /* Find the joystick in the haptic list. */
399 for (i = 0; i < MAX_HAPTICS; i++) {
400 if (SDL_hapticlist[i].fname != NULL) {
401 if (SDL_strcmp(SDL_hapticlist[i].fname, joystick->hwdata->fname)
402 == 0) {
403 haptic->index = i;
404 }
405 }
406 }
407
408 fd = open(joystick->hwdata->fname, O_RDWR, 0);
409 ret = SDL_SYS_HapticOpenFromFD(haptic, fd); /* Already closes on error. */
410 if (ret < 0) {
411 return -1;
412 }
413
414 haptic->hwdata->fname = SDL_hapticlist[haptic->index].fname;
415 return 0;
416 }
417
418
419 /*
420 * Closes the haptic device.
421 */
422 void
423 SDL_SYS_HapticClose(SDL_Haptic * haptic)
424 {
425 if (haptic->hwdata) {
426
427 /* Free effects. */
428 SDL_free(haptic->effects);
429 haptic->effects = NULL;
430 haptic->neffects = 0;
431
432 /* Clean up */
433 close(haptic->hwdata->fd);
434
435 /* Free */
436 SDL_free(haptic->hwdata);
437 haptic->hwdata = NULL;
438 }
439
440 /* Clear the rest. */
441 SDL_memset(haptic, 0, sizeof(SDL_Haptic));
442 }
443
444
445 /*
446 * Clean up after system specific haptic stuff
447 */
448 void
449 SDL_SYS_HapticQuit(void)
450 {
451 int i;
452
453 for (i = 0; SDL_hapticlist[i].fname != NULL; i++) {
454 /* Opened and not closed haptics are leaked, this is on purpose.
455 * Close your haptic devices after usage. */
456
457 SDL_free(SDL_hapticlist[i].fname);
458 }
459 SDL_hapticlist[0].fname = NULL;
460 }
461
462
463 /*
464 * Converts an SDL button to a ff_trigger button.
465 */
466 static Uint16
467 SDL_SYS_ToButton(Uint16 button)
468 {
469 Uint16 ff_button;
470
471 ff_button = 0;
472
473 /*
474 * Not sure what the proper syntax is because this actually isn't implemented
475 * in the current kernel from what I've seen (2.6.26).
476 */
477 if (button != 0) {
478 ff_button = BTN_GAMEPAD + button - 1;
479 }
480
481 return ff_button;
482 }
483
484
485 /*
486 * Returns the ff_effect usable direction from a SDL_HapticDirection.
487 */
488 static Uint16
489 SDL_SYS_ToDirection(SDL_HapticDirection * dir)
490 {
491 Uint32 tmp;
492 float f; /* Ideally we'd use fixed point math instead of floats... */
493
494 switch (dir->type) {
495 case SDL_HAPTIC_POLAR:
496 /* Linux directions are inverted. */
497 tmp = (((18000 + dir->dir[0]) % 36000) * 0xFFFF) / 36000;
498 return (Uint16) tmp;
499
500 case SDL_HAPTIC_CARTESIAN:
501 /* We must invert "x" and "y" to go clockwise. */
502 f = atan2(dir->dir[0], dir->dir[1]);
503 tmp = (int) (f * 18000. / M_PI) % 36000;
504 tmp = (tmp * 0xFFFF) / 36000;
505 return (Uint16) tmp;
506
507 case SDL_HAPTIC_SPHERICAL:
508 tmp = (36000 - dir->dir[0]) + 27000; /* Convert to polars */
509 tmp = (((18000 + tmp) % 36000) * 0xFFFF) / 36000;
510 return (Uint16) tmp;
511
512 default:
513 SDL_SetError("Haptic: Unsupported direction type.");
514 return (Uint16) - 1;
515 }
516
517 return 0;
518 }
519
520
521 #define CLAMP(x) (((x) > 32767) ? 32767 : x)
522 /*
523 * Initializes the linux effect struct from a haptic_effect.
524 * Values above 32767 (for unsigned) are unspecified so we must clamp.
525 */
526 static int
527 SDL_SYS_ToFFEffect(struct ff_effect *dest, SDL_HapticEffect * src)
528 {
529 Uint32 tmp;
530 SDL_HapticConstant *constant;
531 SDL_HapticPeriodic *periodic;
532 SDL_HapticCondition *condition;
533 SDL_HapticRamp *ramp;
534
535 /* Clear up */
536 SDL_memset(dest, 0, sizeof(struct ff_effect));
537
538 switch (src->type) {
539 case SDL_HAPTIC_CONSTANT:
540 constant = &src->constant;
541
542 /* Header */
543 dest->type = FF_CONSTANT;
544 dest->direction = SDL_SYS_ToDirection(&constant->direction);
545 if (dest->direction == (Uint16) - 1)
546 return -1;
547
548 /* Replay */
549 dest->replay.length = (constant->length == SDL_HAPTIC_INFINITY) ?
550 0 : CLAMP(constant->length);
551 dest->replay.delay = CLAMP(constant->delay);
552
553 /* Trigger */
554 dest->trigger.button = SDL_SYS_ToButton(constant->button);
555 dest->trigger.interval = CLAMP(constant->interval);
556
557 /* Constant */
558 dest->u.constant.level = constant->level;
559
560 /* Envelope */
561 dest->u.constant.envelope.attack_length =
562 CLAMP(constant->attack_length);
563 dest->u.constant.envelope.attack_level =
564 CLAMP(constant->attack_level);
565 dest->u.constant.envelope.fade_length = CLAMP(constant->fade_length);
566 dest->u.constant.envelope.fade_level = CLAMP(constant->fade_level);
567
568 break;
569
570 case SDL_HAPTIC_SINE:
571 case SDL_HAPTIC_SQUARE:
572 case SDL_HAPTIC_TRIANGLE:
573 case SDL_HAPTIC_SAWTOOTHUP:
574 case SDL_HAPTIC_SAWTOOTHDOWN:
575 periodic = &src->periodic;
576
577 /* Header */
578 dest->type = FF_PERIODIC;
579 dest->direction = SDL_SYS_ToDirection(&periodic->direction);
580 if (dest->direction == (Uint16) - 1)
581 return -1;
582
583 /* Replay */
584 dest->replay.length = (periodic->length == SDL_HAPTIC_INFINITY) ?
585 0 : CLAMP(periodic->length);
586 dest->replay.delay = CLAMP(periodic->delay);
587
588 /* Trigger */
589 dest->trigger.button = SDL_SYS_ToButton(periodic->button);
590 dest->trigger.interval = CLAMP(periodic->interval);
591
592 /* Periodic */
593 if (periodic->type == SDL_HAPTIC_SINE)
594 dest->u.periodic.waveform = FF_SINE;
595 else if (periodic->type == SDL_HAPTIC_SQUARE)
596 dest->u.periodic.waveform = FF_SQUARE;
597 else if (periodic->type == SDL_HAPTIC_TRIANGLE)
598 dest->u.periodic.waveform = FF_TRIANGLE;
599 else if (periodic->type == SDL_HAPTIC_SAWTOOTHUP)
600 dest->u.periodic.waveform = FF_SAW_UP;
601 else if (periodic->type == SDL_HAPTIC_SAWTOOTHDOWN)
602 dest->u.periodic.waveform = FF_SAW_DOWN;
603 dest->u.periodic.period = CLAMP(periodic->period);
604 dest->u.periodic.magnitude = periodic->magnitude;
605 dest->u.periodic.offset = periodic->offset;
606 /* Phase is calculated based of offset from period and then clamped. */
607 tmp = ((periodic->phase % 36000) * dest->u.periodic.period) / 36000;
608 dest->u.periodic.phase = CLAMP(tmp);
609
610 /* Envelope */
611 dest->u.periodic.envelope.attack_length =
612 CLAMP(periodic->attack_length);
613 dest->u.periodic.envelope.attack_level =
614 CLAMP(periodic->attack_level);
615 dest->u.periodic.envelope.fade_length = CLAMP(periodic->fade_length);
616 dest->u.periodic.envelope.fade_level = CLAMP(periodic->fade_level);
617
618 break;
619
620 case SDL_HAPTIC_SPRING:
621 case SDL_HAPTIC_DAMPER:
622 case SDL_HAPTIC_INERTIA:
623 case SDL_HAPTIC_FRICTION:
624 condition = &src->condition;
625
626 /* Header */
627 if (condition->type == SDL_HAPTIC_SPRING)
628 dest->type = FF_SPRING;
629 else if (condition->type == SDL_HAPTIC_DAMPER)
630 dest->type = FF_DAMPER;
631 else if (condition->type == SDL_HAPTIC_INERTIA)
632 dest->type = FF_INERTIA;
633 else if (condition->type == SDL_HAPTIC_FRICTION)
634 dest->type = FF_FRICTION;
635 dest->direction = 0; /* Handled by the condition-specifics. */
636
637 /* Replay */
638 dest->replay.length = (condition->length == SDL_HAPTIC_INFINITY) ?
639 0 : CLAMP(condition->length);
640 dest->replay.delay = CLAMP(condition->delay);
641
642 /* Trigger */
643 dest->trigger.button = SDL_SYS_ToButton(condition->button);
644 dest->trigger.interval = CLAMP(condition->interval);
645
646 /* Condition */
647 /* X axis */
648 dest->u.condition[0].right_saturation =
649 CLAMP(condition->right_sat[0]);
650 dest->u.condition[0].left_saturation = CLAMP(condition->left_sat[0]);
651 dest->u.condition[0].right_coeff = condition->right_coeff[0];
652 dest->u.condition[0].left_coeff = condition->left_coeff[0];
653 dest->u.condition[0].deadband = CLAMP(condition->deadband[0]);
654 dest->u.condition[0].center = condition->center[0];
655 /* Y axis */
656 dest->u.condition[1].right_saturation =
657 CLAMP(condition->right_sat[1]);
658 dest->u.condition[1].left_saturation = CLAMP(condition->left_sat[1]);
659 dest->u.condition[1].right_coeff = condition->right_coeff[1];
660 dest->u.condition[1].left_coeff = condition->left_coeff[1];
661 dest->u.condition[1].deadband = CLAMP(condition->deadband[1]);
662 dest->u.condition[1].center = condition->center[1];
663
664 /*
665 * There is no envelope in the linux force feedback api for conditions.
666 */
667
668 break;
669
670 case SDL_HAPTIC_RAMP:
671 ramp = &src->ramp;
672
673 /* Header */
674 dest->type = FF_RAMP;
675 dest->direction = SDL_SYS_ToDirection(&ramp->direction);
676 if (dest->direction == (Uint16) - 1)
677 return -1;
678
679 /* Replay */
680 dest->replay.length = (ramp->length == SDL_HAPTIC_INFINITY) ?
681 0 : CLAMP(ramp->length);
682 dest->replay.delay = CLAMP(ramp->delay);
683
684 /* Trigger */
685 dest->trigger.button = SDL_SYS_ToButton(ramp->button);
686 dest->trigger.interval = CLAMP(ramp->interval);
687
688 /* Ramp */
689 dest->u.ramp.start_level = ramp->start;
690 dest->u.ramp.end_level = ramp->end;
691
692 /* Envelope */
693 dest->u.ramp.envelope.attack_length = CLAMP(ramp->attack_length);
694 dest->u.ramp.envelope.attack_level = CLAMP(ramp->attack_level);
695 dest->u.ramp.envelope.fade_length = CLAMP(ramp->fade_length);
696 dest->u.ramp.envelope.fade_level = CLAMP(ramp->fade_level);
697
698 break;
699
700
701 default:
702 SDL_SetError("Haptic: Unknown effect type.");
703 return -1;
704 }
705
706 return 0;
707 }
708
709
710 /*
711 * Creates a new haptic effect.
712 */
713 int
714 SDL_SYS_HapticNewEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
715 SDL_HapticEffect * base)
716 {
717 struct ff_effect *linux_effect;
718
719 /* Allocate the hardware effect */
720 effect->hweffect = (struct haptic_hweffect *)
721 SDL_malloc(sizeof(struct haptic_hweffect));
722 if (effect->hweffect == NULL) {
723 SDL_OutOfMemory();
724 return -1;
725 }
726
727 /* Prepare the ff_effect */
728 linux_effect = &effect->hweffect->effect;
729 if (SDL_SYS_ToFFEffect(linux_effect, base) != 0) {
730 goto new_effect_err;
731 }
732 linux_effect->id = -1; /* Have the kernel give it an id */
733
734 /* Upload the effect */
735 if (ioctl(haptic->hwdata->fd, EVIOCSFF, linux_effect) < 0) {
736 SDL_SetError("Haptic: Error uploading effect to the device: %s",
737 strerror(errno));
738 goto new_effect_err;
739 }
740
741 return 0;
742
743 new_effect_err:
744 free(effect->hweffect);
745 effect->hweffect = NULL;
746 return -1;
747 }
748
749
750 /*
751 * Updates an effect.
752 *
753 * Note: Dynamically updating the direction can in some cases force
754 * the effect to restart and run once.
755 */
756 int
757 SDL_SYS_HapticUpdateEffect(SDL_Haptic * haptic,
758 struct haptic_effect *effect,
759 SDL_HapticEffect * data)
760 {
761 struct ff_effect linux_effect;
762
763 /* Create the new effect */
764 if (SDL_SYS_ToFFEffect(&linux_effect, data) != 0) {
765 return -1;
766 }
767 linux_effect.id = effect->hweffect->effect.id;
768
769 /* See if it can be uploaded. */
770 if (ioctl(haptic->hwdata->fd, EVIOCSFF, &linux_effect) < 0) {
771 SDL_SetError("Haptic: Error updating the effect: %s",
772 strerror(errno));
773 return -1;
774 }
775
776 /* Copy the new effect into memory. */
777 SDL_memcpy(&effect->hweffect->effect, &linux_effect,
778 sizeof(struct ff_effect));
779
780 return effect->hweffect->effect.id;
781 }
782
783
784 /*
785 * Runs an effect.
786 */
787 int
788 SDL_SYS_HapticRunEffect(SDL_Haptic * haptic, struct haptic_effect *effect,
789 Uint32 iterations)
790 {
791 struct input_event run;
792
793 /* Prepare to run the effect */
794 run.type = EV_FF;
795 run.code = effect->hweffect->effect.id;
796 /* We don't actually have infinity here, so we just do INT_MAX which is pretty damn close. */
797 run.value = (iterations > INT_MAX) ? INT_MAX : iterations;
798
799 if (write(haptic->hwdata->fd, (const void *) &run, sizeof(run)) < 0) {
800 SDL_SetError("Haptic: Unable to run the effect: %s", strerror(errno));
801 return -1;
802 }
803
804 return 0;
805 }
806
807
808 /*
809 * Stops an effect.
810 */
811 int
812 SDL_SYS_HapticStopEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
813 {
814 struct input_event stop;
815
816 stop.type = EV_FF;
817 stop.code = effect->hweffect->effect.id;
818 stop.value = 0;
819
820 if (write(haptic->hwdata->fd, (const void *) &stop, sizeof(stop)) < 0) {
821 SDL_SetError("Haptic: Unable to stop the effect: %s",
822 strerror(errno));
823 return -1;
824 }
825
826 return 0;
827 }
828
829
830 /*
831 * Frees the effect.
832 */
833 void
834 SDL_SYS_HapticDestroyEffect(SDL_Haptic * haptic, struct haptic_effect *effect)
835 {
836 if (ioctl(haptic->hwdata->fd, EVIOCRMFF, effect->hweffect->effect.id) < 0) {
837 SDL_SetError("Haptic: Error removing the effect from the device: %s",
838 strerror(errno));
839 }
840 SDL_free(effect->hweffect);
841 effect->hweffect = NULL;
842 }
843
844
845 /*
846 * Gets the status of a haptic effect.
847 */
848 int
849 SDL_SYS_HapticGetEffectStatus(SDL_Haptic * haptic,
850 struct haptic_effect *effect)
851 {
852 #if 0 /* Not supported atm. */
853 struct input_event ie;
854
855 ie.type = EV_FF;
856 ie.type = EV_FF_STATUS;
857 ie.code = effect->hweffect->effect.id;
858
859 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
860 SDL_SetError("Haptic: Error getting device status.");
861 return -1;
862 }
863
864 return 0;
865 #endif
866
867 return -1;
868 }
869
870
871 /*
872 * Sets the gain.
873 */
874 int
875 SDL_SYS_HapticSetGain(SDL_Haptic * haptic, int gain)
876 {
877 struct input_event ie;
878
879 ie.type = EV_FF;
880 ie.code = FF_GAIN;
881 ie.value = (0xFFFFUL * gain) / 100;
882
883 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
884 SDL_SetError("Haptic: Error setting gain: %s", strerror(errno));
885 return -1;
886 }
887
888 return 0;
889 }
890
891
892 /*
893 * Sets the autocentering.
894 */
895 int
896 SDL_SYS_HapticSetAutocenter(SDL_Haptic * haptic, int autocenter)
897 {
898 struct input_event ie;
899
900 ie.type = EV_FF;
901 ie.code = FF_AUTOCENTER;
902 ie.value = (0xFFFFUL * autocenter) / 100;
903
904 if (write(haptic->hwdata->fd, &ie, sizeof(ie)) < 0) {
905 SDL_SetError("Haptic: Error setting autocenter: %s", strerror(errno));
906 return -1;
907 }
908
909 return 0;
910 }
911
912
913 /*
914 * Pausing is not supported atm by linux.
915 */
916 int
917 SDL_SYS_HapticPause(SDL_Haptic * haptic)
918 {
919 return -1;
920 }
921
922
923 /*
924 * Unpausing is not supported atm by linux.
925 */
926 int
927 SDL_SYS_HapticUnpause(SDL_Haptic * haptic)
928 {
929 return -1;
930 }
931
932
933 /*
934 * Stops all the currently playing effects.
935 */
936 int
937 SDL_SYS_HapticStopAll(SDL_Haptic * haptic)
938 {
939 int i, ret;
940
941 /* Linux does not support this natively so we have to loop. */
942 for (i = 0; i < haptic->neffects; i++) {
943 if (haptic->effects[i].hweffect != NULL) {
944 ret = SDL_SYS_HapticStopEffect(haptic, &haptic->effects[i]);
945 if (ret < 0) {
946 SDL_SetError
947 ("Haptic: Error while trying to stop all playing effects.");
948 return -1;
949 }
950 }
951 }
952 return 0;
953 }
954
955
956 #endif /* SDL_HAPTIC_LINUX */