Mercurial > sdl-ios-xcode
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 */ |