1 /* ______ ___ ___
2 * /\ _ \ /\_ \ /\_ \
3 * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
5 * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
6 * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 * /\____/
9 * \_/__/
10 *
11 * Linux haptic (force-feedback) device driver.
12 *
13 * By Beoran.
14 *
15 * See LICENSE.txt for copyright information.
16 */
17
18
19 #ifndef _GNU_SOURCE
20 #define _GNU_SOURCE
21 #endif
22
23 #include <math.h>
24 #include <stdio.h>
25 #include <sys/time.h>
26
27 #include "allegro5/allegro.h"
28 #include "allegro5/internal/aintern_haptic.h"
29 #include "allegro5/internal/aintern_ljoynu.h"
30 #include "allegro5/platform/aintunix.h"
31
32 #ifdef ALLEGRO_HAVE_LINUX_INPUT_H
33
34 #include <linux/input.h>
35
36 ALLEGRO_DEBUG_CHANNEL("lhaptic")
37
38
39 /* Support at most 32 haptic devices. */
40 #define HAPTICS_MAX 32
41
42 /* Support at most 16 effects per device. */
43 #define HAPTICS_EFFECTS_MAX 16
44
45
46 typedef struct
47 {
48 struct ALLEGRO_HAPTIC parent; /* must be first */
49 bool active;
50 int fd;
51 int flags;
52 int effects[HAPTICS_EFFECTS_MAX];
53 } ALLEGRO_HAPTIC_LINUX;
54
55
56 #define LONG_BITS (sizeof(long) * 8)
57 #define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS)
58 /* Tests if a bit in an array of longs is set. */
59 #define TEST_BIT(nr, addr) \
60 ((1UL << ((nr) % LONG_BITS)) & (addr)[(nr) / LONG_BITS])
61
62
63 /* forward declarations */
64 static bool lhap_init_haptic(void);
65 static void lhap_exit_haptic(void);
66
67 static bool lhap_is_mouse_haptic(ALLEGRO_MOUSE *dev);
68 static bool lhap_is_joystick_haptic(ALLEGRO_JOYSTICK *);
69 static bool lhap_is_keyboard_haptic(ALLEGRO_KEYBOARD *dev);
70 static bool lhap_is_display_haptic(ALLEGRO_DISPLAY *dev);
71 static bool lhap_is_touch_input_haptic(ALLEGRO_TOUCH_INPUT *dev);
72
73 static ALLEGRO_HAPTIC *lhap_get_from_mouse(ALLEGRO_MOUSE *dev);
74 static ALLEGRO_HAPTIC *lhap_get_from_joystick(ALLEGRO_JOYSTICK *dev);
75 static ALLEGRO_HAPTIC *lhap_get_from_keyboard(ALLEGRO_KEYBOARD *dev);
76 static ALLEGRO_HAPTIC *lhap_get_from_display(ALLEGRO_DISPLAY *dev);
77 static ALLEGRO_HAPTIC *lhap_get_from_touch_input(ALLEGRO_TOUCH_INPUT *dev);
78
79 static bool lhap_release(ALLEGRO_HAPTIC *haptic);
80
81 static bool lhap_get_active(ALLEGRO_HAPTIC *hap);
82 static int lhap_get_capabilities(ALLEGRO_HAPTIC *dev);
83 static double lhap_get_gain(ALLEGRO_HAPTIC *dev);
84 static bool lhap_set_gain(ALLEGRO_HAPTIC *dev, double);
85 static int lhap_get_max_effects(ALLEGRO_HAPTIC *dev);
86
87 static bool lhap_is_effect_ok(ALLEGRO_HAPTIC *dev, ALLEGRO_HAPTIC_EFFECT *eff);
88 static bool lhap_upload_effect(ALLEGRO_HAPTIC *dev,
89 ALLEGRO_HAPTIC_EFFECT *eff,
90 ALLEGRO_HAPTIC_EFFECT_ID *id);
91 static bool lhap_play_effect(ALLEGRO_HAPTIC_EFFECT_ID *id, int loop);
92 static bool lhap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID *id);
93 static bool lhap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID *id);
94 static bool lhap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID *id);
95
96 static double lhap_get_autocenter(ALLEGRO_HAPTIC *dev);
97 static bool lhap_set_autocenter(ALLEGRO_HAPTIC *dev, double);
98
99 ALLEGRO_HAPTIC_DRIVER _al_hapdrv_linux =
100 {
101 _ALLEGRO_HAPDRV_LINUX,
102 "",
103 "",
104 "Linux haptic(s)",
105 lhap_init_haptic,
106 lhap_exit_haptic,
107
108 lhap_is_mouse_haptic,
109 lhap_is_joystick_haptic,
110 lhap_is_keyboard_haptic,
111 lhap_is_display_haptic,
112 lhap_is_touch_input_haptic,
113
114 lhap_get_from_mouse,
115 lhap_get_from_joystick,
116 lhap_get_from_keyboard,
117 lhap_get_from_display,
118 lhap_get_from_touch_input,
119
120 lhap_get_active,
121 lhap_get_capabilities,
122 lhap_get_gain,
123 lhap_set_gain,
124 lhap_get_max_effects,
125
126 lhap_is_effect_ok,
127 lhap_upload_effect,
128 lhap_play_effect,
129 lhap_stop_effect,
130 lhap_is_effect_playing,
131 lhap_release_effect,
132
133 lhap_release,
134
135 lhap_get_autocenter,
136 lhap_set_autocenter
137 };
138
139
140 static ALLEGRO_HAPTIC_LINUX haptics[HAPTICS_MAX];
141 static ALLEGRO_MUTEX *haptic_mutex = NULL;
142
143
144 struct CAP_MAP {
145 int linux_bit;
146 int allegro_bit;
147 };
148
149 static const struct CAP_MAP cap_map[] = {
150 { FF_PERIODIC, ALLEGRO_HAPTIC_PERIODIC },
151 { FF_RUMBLE, ALLEGRO_HAPTIC_RUMBLE },
152 { FF_CONSTANT, ALLEGRO_HAPTIC_CONSTANT },
153 { FF_SPRING, ALLEGRO_HAPTIC_SPRING },
154 { FF_FRICTION, ALLEGRO_HAPTIC_FRICTION },
155 { FF_DAMPER, ALLEGRO_HAPTIC_DAMPER },
156 { FF_INERTIA, ALLEGRO_HAPTIC_INERTIA },
157 { FF_RAMP, ALLEGRO_HAPTIC_RAMP },
158 { FF_SQUARE, ALLEGRO_HAPTIC_SQUARE },
159 { FF_TRIANGLE, ALLEGRO_HAPTIC_TRIANGLE },
160 { FF_SINE, ALLEGRO_HAPTIC_SINE },
161 { FF_SAW_UP, ALLEGRO_HAPTIC_SAW_UP },
162 { FF_SAW_DOWN, ALLEGRO_HAPTIC_SAW_DOWN },
163 { FF_CUSTOM, ALLEGRO_HAPTIC_CUSTOM },
164 { FF_GAIN, ALLEGRO_HAPTIC_GAIN },
165 { FF_AUTOCENTER, ALLEGRO_HAPTIC_AUTOCENTER },
166 { -1, -1 }
167 };
168
169
lhap_init_haptic(void)170 static bool lhap_init_haptic(void)
171 {
172 int i;
173
174 ASSERT(haptic_mutex == NULL);
175 haptic_mutex = al_create_mutex();
176 if (!haptic_mutex)
177 return false;
178
179 for (i = 0; i < HAPTICS_MAX; i++) {
180 haptics[i].active = false;
181 }
182
183 return true;
184 }
185
186
lhap_get_available_haptic(void)187 static ALLEGRO_HAPTIC_LINUX *lhap_get_available_haptic(void)
188 {
189 int i;
190
191 for (i = 0; i < HAPTICS_MAX; i++) {
192 if (!haptics[i].active) {
193 haptics[i].active = true;
194 return &haptics[i];
195 }
196 }
197
198 return NULL;
199 }
200
201
202 /* Converts a generic haptic device to a Linux-specific one. */
lhap_from_al(ALLEGRO_HAPTIC * hap)203 static ALLEGRO_HAPTIC_LINUX *lhap_from_al(ALLEGRO_HAPTIC *hap)
204 {
205 return (ALLEGRO_HAPTIC_LINUX *)hap;
206 }
207
208
lhap_exit_haptic(void)209 static void lhap_exit_haptic(void)
210 {
211 ASSERT(haptic_mutex);
212 al_destroy_mutex(haptic_mutex);
213 haptic_mutex = NULL;
214 }
215
216
lhap_type2lin(__u16 * res,int type)217 static bool lhap_type2lin(__u16 *res, int type)
218 {
219 ASSERT(res);
220
221 switch (type) {
222 case ALLEGRO_HAPTIC_RUMBLE:
223 (*res) = FF_RUMBLE;
224 break;
225 case ALLEGRO_HAPTIC_PERIODIC:
226 (*res) = FF_PERIODIC;
227 break;
228 case ALLEGRO_HAPTIC_CONSTANT:
229 (*res) = FF_CONSTANT;
230 break;
231 case ALLEGRO_HAPTIC_SPRING:
232 (*res) = FF_SPRING;
233 break;
234 case ALLEGRO_HAPTIC_FRICTION:
235 (*res) = FF_FRICTION;
236 break;
237 case ALLEGRO_HAPTIC_DAMPER:
238 (*res) = FF_DAMPER;
239 break;
240 case ALLEGRO_HAPTIC_INERTIA:
241 (*res) = FF_INERTIA;
242 break;
243 case ALLEGRO_HAPTIC_RAMP:
244 (*res) = FF_RAMP;
245 break;
246 default:
247 return false;
248 }
249 return true;
250 }
251
252
lhap_wave2lin(__u16 * res,int type)253 static bool lhap_wave2lin(__u16 *res, int type)
254 {
255 ASSERT(res);
256
257 switch (type) {
258 case ALLEGRO_HAPTIC_SQUARE:
259 (*res) = FF_SQUARE;
260 break;
261 case ALLEGRO_HAPTIC_TRIANGLE:
262 (*res) = FF_TRIANGLE;
263 break;
264 case ALLEGRO_HAPTIC_SINE:
265 (*res) = FF_SINE;
266 break;
267 case ALLEGRO_HAPTIC_SAW_UP:
268 (*res) = FF_SAW_UP;
269 break;
270 case ALLEGRO_HAPTIC_SAW_DOWN:
271 (*res) = FF_SAW_DOWN;
272 break;
273 case ALLEGRO_HAPTIC_CUSTOM:
274 (*res) = FF_CUSTOM;
275 break;
276 default:
277 return false;
278 }
279 return true;
280 }
281
282
283 /* Converts the time in seconds to a Linux-compatible time.
284 * Return false if out of bounds.
285 */
lhap_time2lin(__u16 * res,double sec)286 static bool lhap_time2lin(__u16 *res, double sec)
287 {
288 ASSERT(res);
289
290 if (sec < 0.0 || sec > 32.767)
291 return false;
292 (*res) = (__u16) round(sec * 1000.0);
293 return true;
294 }
295
296
297 /* Converts the time in seconds to a Linux-compatible time.
298 * Return false if out of bounds. This one allows negative times.
299 */
lhap_stime2lin(__s16 * res,double sec)300 static bool lhap_stime2lin(__s16 *res, double sec)
301 {
302 ASSERT(res);
303
304 if (sec < -32.767 || sec > 32.767)
305 return false;
306 (*res) = (__s16) round(sec * 1000.0);
307 return true;
308 }
309
310
311 /* Converts replay data to Linux-compatible data. */
lhap_replay2lin(struct ff_replay * lin,struct ALLEGRO_HAPTIC_REPLAY * al)312 static bool lhap_replay2lin(struct ff_replay *lin,
313 struct ALLEGRO_HAPTIC_REPLAY *al)
314 {
315 return lhap_time2lin(&lin->delay, al->delay)
316 && lhap_time2lin(&lin->length, al->length);
317 }
318
319
320 /* Converts the level in range 0.0 to 1.0 to a Linux-compatible level.
321 * Returns false if out of bounds.
322 */
lhap_level2lin(__u16 * res,double level)323 static bool lhap_level2lin(__u16 *res, double level)
324 {
325 ASSERT(res);
326
327 if (level < 0.0 || level > 1.0)
328 return false;
329 *res = (__u16) round(level * (double)0x7fff);
330 return true;
331 }
332
333
334 /* Converts the level in range -1.0 to 1.0 to a Linux-compatible level.
335 * Returns false if out of bounds.
336 */
lhap_slevel2lin(__s16 * res,double level)337 static bool lhap_slevel2lin(__s16 *res, double level)
338 {
339 ASSERT(res);
340
341 if (level < -1.0 || level > 1.0)
342 return false;
343 *res = (__s16) round(level * (double)0x7ffe);
344 return true;
345 }
346
347
348 /* Converts an Allegro haptic effect envelope to Linux input API. */
lhap_envelope2lin(struct ff_envelope * lin,struct ALLEGRO_HAPTIC_ENVELOPE * al)349 static bool lhap_envelope2lin(struct ff_envelope *lin,
350 struct ALLEGRO_HAPTIC_ENVELOPE *al)
351 {
352 return lhap_time2lin(&lin->attack_length, al->attack_length)
353 && lhap_time2lin(&lin->fade_length, al->fade_length)
354 && lhap_level2lin(&lin->attack_level, al->attack_level)
355 && lhap_level2lin(&lin->fade_level, al->fade_level);
356 }
357
358
359 /* Converts a rumble effect to Linux input API. */
lhap_rumble2lin(struct ff_rumble_effect * lin,struct ALLEGRO_HAPTIC_RUMBLE_EFFECT * al)360 static bool lhap_rumble2lin(struct ff_rumble_effect *lin,
361 struct ALLEGRO_HAPTIC_RUMBLE_EFFECT *al)
362 {
363 return lhap_level2lin(&lin->strong_magnitude, al->strong_magnitude)
364 && lhap_level2lin(&lin->weak_magnitude, al->weak_magnitude);
365 }
366
367
368 /* Converts a constant effect to Linux input API. */
lhap_constant2lin(struct ff_constant_effect * lin,struct ALLEGRO_HAPTIC_CONSTANT_EFFECT * al)369 static bool lhap_constant2lin(struct ff_constant_effect *lin,
370 struct ALLEGRO_HAPTIC_CONSTANT_EFFECT *al)
371 {
372 return lhap_envelope2lin(&lin->envelope, &al->envelope)
373 && lhap_slevel2lin(&lin->level, al->level);
374 }
375
376
377 /* Converts a ramp effect to Linux input API. */
lhap_ramp2lin(struct ff_ramp_effect * lin,struct ALLEGRO_HAPTIC_RAMP_EFFECT * al)378 static bool lhap_ramp2lin(struct ff_ramp_effect *lin,
379 struct ALLEGRO_HAPTIC_RAMP_EFFECT *al)
380 {
381 return lhap_envelope2lin(&lin->envelope, &al->envelope)
382 && lhap_slevel2lin(&lin->start_level, al->start_level)
383 && lhap_slevel2lin(&lin->end_level, al->end_level);
384 }
385
386
387 /* Converts a ramp effect to Linux input API. */
lhap_condition2lin(struct ff_condition_effect * lin,struct ALLEGRO_HAPTIC_CONDITION_EFFECT * al)388 static bool lhap_condition2lin(struct ff_condition_effect *lin,
389 struct ALLEGRO_HAPTIC_CONDITION_EFFECT *al)
390 {
391 return lhap_slevel2lin(&lin->center, al->center)
392 && lhap_level2lin(&lin->deadband, al->deadband)
393 && lhap_slevel2lin(&lin->right_coeff, al->right_coeff)
394 && lhap_level2lin(&lin->right_saturation, al->right_saturation)
395 && lhap_slevel2lin(&lin->left_coeff, al->left_coeff)
396 && lhap_level2lin(&lin->left_saturation, al->left_saturation);
397 }
398
399
400 /* Converts a periodic effect to linux input API. */
lhap_periodic2lin(struct ff_periodic_effect * lin,struct ALLEGRO_HAPTIC_PERIODIC_EFFECT * al)401 static bool lhap_periodic2lin(struct ff_periodic_effect *lin,
402 struct ALLEGRO_HAPTIC_PERIODIC_EFFECT *al)
403 {
404 /* Custom data is not supported yet, because currently no Linux
405 * haptic driver supports it.
406 */
407 if (al->custom_data)
408 return false;
409
410 return lhap_slevel2lin(&lin->magnitude, al->magnitude)
411 && lhap_stime2lin(&lin->offset, al->offset)
412 && lhap_time2lin(&lin->period, al->period)
413 && lhap_time2lin(&lin->phase, al->phase)
414 && lhap_wave2lin(&lin->waveform, al->waveform)
415 && lhap_envelope2lin(&lin->envelope, &al->envelope);
416 }
417
418
419 /* Converts Allegro haptic effect to Linux input API. */
lhap_effect2lin(struct ff_effect * lin,ALLEGRO_HAPTIC_EFFECT * al)420 static bool lhap_effect2lin(struct ff_effect *lin, ALLEGRO_HAPTIC_EFFECT *al)
421 {
422 memset(lin, 0, sizeof(*lin));
423
424 if (!lhap_type2lin(&lin->type, al->type))
425 return false;
426 /* lin_effect->replay = effect->re; */
427 lin->direction = (__u16)
428 round(((double)0xC000 * al->direction.angle) / (2 * M_PI));
429 lin->id = -1;
430 if (!lhap_replay2lin(&lin->replay, &al->replay))
431 return false;
432 switch (lin->type) {
433 case FF_RUMBLE:
434 return lhap_rumble2lin(&lin->u.rumble, &al->data.rumble);
435 case FF_PERIODIC:
436 return lhap_periodic2lin(&lin->u.periodic, &al->data.periodic);
437 case FF_CONSTANT:
438 return lhap_constant2lin(&lin->u.constant, &al->data.constant);
439 case FF_RAMP:
440 return lhap_ramp2lin(&lin->u.ramp, &al->data.ramp);
441 case FF_SPRING: /* fall through */
442 case FF_FRICTION: /* fall through */
443 case FF_DAMPER: /* fall through */
444 case FF_INERTIA:
445 return lhap_condition2lin(&lin->u.condition[0], &al->data.condition);
446 default:
447 return false;
448 }
449 }
450
451
lhap_get_active(ALLEGRO_HAPTIC * haptic)452 static bool lhap_get_active(ALLEGRO_HAPTIC *haptic)
453 {
454 ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(haptic);
455 return lhap->active;
456 }
457
458
lhap_is_mouse_haptic(ALLEGRO_MOUSE * mouse)459 static bool lhap_is_mouse_haptic(ALLEGRO_MOUSE *mouse)
460 {
461 (void)mouse;
462 return false;
463 }
464
465
lhap_fd_can_ff(int fd)466 static bool lhap_fd_can_ff(int fd)
467 {
468 long bitmask[NLONGS(EV_CNT)] = { 0 };
469
470 if (ioctl(fd, EVIOCGBIT(0, sizeof(bitmask)), bitmask) < 0) {
471 return false;
472 }
473 if (TEST_BIT(EV_FF, bitmask)) {
474 return true;
475 }
476 return false;
477 }
478
479
lhap_is_joystick_haptic(ALLEGRO_JOYSTICK * joy)480 static bool lhap_is_joystick_haptic(ALLEGRO_JOYSTICK *joy)
481 {
482 ALLEGRO_JOYSTICK_LINUX *ljoy = (ALLEGRO_JOYSTICK_LINUX *) joy;
483 if (!al_is_joystick_installed())
484 return false;
485 if (!al_get_joystick_active(joy))
486 return false;
487 if (ljoy->fd <= 0)
488 return false;
489 return lhap_fd_can_ff(ljoy->fd);
490 }
491
492
lhap_is_display_haptic(ALLEGRO_DISPLAY * dev)493 static bool lhap_is_display_haptic(ALLEGRO_DISPLAY *dev)
494 {
495 (void)dev;
496 return false;
497 }
498
499
lhap_is_keyboard_haptic(ALLEGRO_KEYBOARD * dev)500 static bool lhap_is_keyboard_haptic(ALLEGRO_KEYBOARD *dev)
501 {
502 (void)dev;
503 return false;
504 }
505
506
lhap_is_touch_input_haptic(ALLEGRO_TOUCH_INPUT * dev)507 static bool lhap_is_touch_input_haptic(ALLEGRO_TOUCH_INPUT *dev)
508 {
509 (void)dev;
510 return false;
511 }
512
513
lhap_get_from_mouse(ALLEGRO_MOUSE * mouse)514 static ALLEGRO_HAPTIC *lhap_get_from_mouse(ALLEGRO_MOUSE *mouse)
515 {
516 (void)mouse;
517 return NULL;
518 }
519
520
get_haptic_capabilities(int fd,int * capabilities)521 static bool get_haptic_capabilities(int fd, int *capabilities)
522 {
523 unsigned long bitmask[NLONGS(FF_CNT)] = { 0 };
524 int caps;
525 int i;
526
527 if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(bitmask)), bitmask) < 0) {
528 ALLEGRO_ERROR("EVIOCGBIT failed for fd %d", fd);
529 return false;
530 }
531
532 caps = 0;
533 for (i = 0; cap_map[i].allegro_bit >= 0; i++) {
534 if (TEST_BIT(cap_map[i].linux_bit, bitmask)) {
535 caps |= cap_map[i].allegro_bit;
536 }
537 }
538 (*capabilities) = caps;
539 ALLEGRO_INFO("Capabilities: 0x%x\n", caps);
540 return true;
541 }
542
543
lhap_get_from_joystick(ALLEGRO_JOYSTICK * joy)544 static ALLEGRO_HAPTIC *lhap_get_from_joystick(ALLEGRO_JOYSTICK *joy)
545 {
546 ALLEGRO_JOYSTICK_LINUX *ljoy = (ALLEGRO_JOYSTICK_LINUX *) joy;
547 ALLEGRO_HAPTIC_LINUX *lhap;
548 int i;
549
550 if (!al_is_joystick_haptic(joy))
551 return NULL;
552
553 al_lock_mutex(haptic_mutex);
554
555 lhap = lhap_get_available_haptic();
556
557 if (!lhap) {
558 al_unlock_mutex(haptic_mutex);
559 return NULL;
560 }
561
562 lhap->parent.device = joy;
563 lhap->parent.from = _AL_HAPTIC_FROM_JOYSTICK;
564
565 lhap->fd = ljoy->fd;
566 lhap->active = true;
567 for (i = 0; i < HAPTICS_EFFECTS_MAX; i++) {
568 lhap->effects[i] = -1; /* not in use */
569 }
570 lhap->parent.gain = 1.0;
571 get_haptic_capabilities(lhap->fd, &lhap->flags);
572
573 al_unlock_mutex(haptic_mutex);
574
575 return &lhap->parent;
576 }
577
578
lhap_get_from_display(ALLEGRO_DISPLAY * dev)579 static ALLEGRO_HAPTIC *lhap_get_from_display(ALLEGRO_DISPLAY *dev)
580 {
581 (void)dev;
582 return NULL;
583 }
584
585
lhap_get_from_keyboard(ALLEGRO_KEYBOARD * dev)586 static ALLEGRO_HAPTIC *lhap_get_from_keyboard(ALLEGRO_KEYBOARD *dev)
587 {
588 (void)dev;
589 return NULL;
590 }
591
592
lhap_get_from_touch_input(ALLEGRO_TOUCH_INPUT * dev)593 static ALLEGRO_HAPTIC *lhap_get_from_touch_input(ALLEGRO_TOUCH_INPUT *dev)
594 {
595 (void)dev;
596 return NULL;
597 }
598
599
lhap_get_capabilities(ALLEGRO_HAPTIC * dev)600 static int lhap_get_capabilities(ALLEGRO_HAPTIC *dev)
601 {
602 ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(dev);
603 return lhap->flags;
604 }
605
606
lhap_get_gain(ALLEGRO_HAPTIC * dev)607 static double lhap_get_gain(ALLEGRO_HAPTIC *dev)
608 {
609 ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(dev);
610 (void)dev;
611
612 if(!al_is_haptic_capable(dev, ALLEGRO_HAPTIC_GAIN)) {
613 return 0.0;
614 }
615
616 /* Unfortunately there seems to be no API to GET gain, only to set?!
617 * So, return the stored gain.
618 */
619 return lhap->parent.gain;
620 }
621
622
lhap_set_gain(ALLEGRO_HAPTIC * dev,double gain)623 static bool lhap_set_gain(ALLEGRO_HAPTIC *dev, double gain)
624 {
625 ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(dev);
626 struct input_event ie;
627
628 lhap->parent.gain = gain;
629 timerclear(&ie.time);
630 ie.type = EV_FF;
631 ie.code = FF_GAIN;
632 ie.value = (__s32) ((double)0xFFFF * gain);
633 if (write(lhap->fd, &ie, sizeof(ie)) < 0) {
634 return false;
635 }
636 return true;
637 }
638
639
lhap_set_autocenter(ALLEGRO_HAPTIC * dev,double autocenter)640 static bool lhap_set_autocenter(ALLEGRO_HAPTIC *dev, double autocenter)
641 {
642 ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(dev);
643 struct input_event ie;
644
645 lhap->parent.autocenter = autocenter;
646 timerclear(&ie.time);
647 ie.type = EV_FF;
648 ie.code = FF_AUTOCENTER;
649 ie.value = (__s32) ((double)0xFFFF * autocenter);
650 if (write(lhap->fd, &ie, sizeof(ie)) < 0) {
651 return false;
652 }
653 return true;
654 }
655
lhap_get_autocenter(ALLEGRO_HAPTIC * dev)656 static double lhap_get_autocenter(ALLEGRO_HAPTIC *dev)
657 {
658 ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(dev);
659 (void)dev;
660
661 if(!al_is_haptic_capable(dev, ALLEGRO_HAPTIC_AUTOCENTER)) {
662 return 0.0;
663 }
664
665 /* Unfortunately there seems to be no API to GET gain, only to set?!
666 * So, return the stored autocenter.
667 */
668 return lhap->parent.autocenter;
669 }
670
lhap_get_max_effects(ALLEGRO_HAPTIC * dev)671 int lhap_get_max_effects(ALLEGRO_HAPTIC *dev)
672 {
673 ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(dev);
674 int n_effects;
675
676 if (ioctl(lhap->fd, EVIOCGEFFECTS, &n_effects) < 0) {
677 ALLEGRO_WARN("EVIOCGEFFECTS failed on fd %d\n", lhap->fd);
678 n_effects = HAPTICS_EFFECTS_MAX;
679 }
680
681 if (n_effects < HAPTICS_EFFECTS_MAX)
682 return n_effects;
683 else
684 return HAPTICS_EFFECTS_MAX;
685 }
686
687
lhap_is_effect_ok(ALLEGRO_HAPTIC * haptic,ALLEGRO_HAPTIC_EFFECT * effect)688 static bool lhap_is_effect_ok(ALLEGRO_HAPTIC *haptic,
689 ALLEGRO_HAPTIC_EFFECT *effect)
690 {
691 int caps;
692 struct ff_effect leff;
693
694 caps = al_get_haptic_capabilities(haptic);
695 if (caps & effect->type) {
696 return lhap_effect2lin(&leff, effect);
697 }
698 return false;
699 }
700
701
lhap_upload_effect(ALLEGRO_HAPTIC * dev,ALLEGRO_HAPTIC_EFFECT * effect,ALLEGRO_HAPTIC_EFFECT_ID * id)702 static bool lhap_upload_effect(ALLEGRO_HAPTIC *dev,
703 ALLEGRO_HAPTIC_EFFECT *effect, ALLEGRO_HAPTIC_EFFECT_ID *id)
704 {
705 ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(dev);
706 struct ff_effect leff;
707 int found;
708 int i;
709
710 ASSERT(dev);
711 ASSERT(id);
712 ASSERT(effect);
713
714 /* Set id's values to indicate failure. */
715 id->_haptic = NULL;
716 id->_id = -1;
717 id->_handle = -1;
718
719 if (!lhap_effect2lin(&leff, effect)) {
720 ALLEGRO_WARN("lhap_effect2lin failed");
721 return false;
722 }
723
724 /* Find empty spot for effect . */
725 found = -1;
726 for (i = 0; i < al_get_max_haptic_effects(dev); i++) {
727 if (lhap->effects[i] < 0) {
728 found = i;
729 break;
730 }
731 }
732
733 /* No more space for an effect. */
734 if (found < 0) {
735 ALLEGRO_WARN("No free effect slot.");
736 return false;
737 }
738
739 /* Upload effect. */
740 leff.id = -1;
741 if (ioctl(lhap->fd, EVIOCSFF, &leff) < 0) {
742 ALLEGRO_ERROR("EVIOCSFF failed for fd %d\n", lhap->fd);
743 return false;
744 }
745
746 id->_haptic = dev;
747 id->_id = found;
748 id->_handle = leff.id;
749 id->_effect_duration = al_get_haptic_effect_duration(effect);
750 id->_playing = false;
751
752 /* XXX should be bool or something? */
753 lhap->effects[i] = found;
754
755 return true;
756 }
757
758
lhap_play_effect(ALLEGRO_HAPTIC_EFFECT_ID * id,int loops)759 static bool lhap_play_effect(ALLEGRO_HAPTIC_EFFECT_ID *id, int loops)
760 {
761 ALLEGRO_HAPTIC_LINUX *lhap = (ALLEGRO_HAPTIC_LINUX *) id->_haptic;
762 struct input_event play;
763 int fd;
764 double now;
765 double duration;
766
767 if (!lhap)
768 return false;
769
770 fd = lhap->fd;
771
772 timerclear(&play.time);
773 play.type = EV_FF;
774 play.code = id->_handle;
775 loops = (loops < 0) ? 1 : loops;
776 play.value = loops; /* play: 1, stop: 0 */
777
778 if (write(fd, (const void *)&play, sizeof(play)) < 0) {
779 ALLEGRO_ERROR("Effect play failed.\n");
780 return false;
781 }
782
783 now = al_get_time();
784 duration = loops * id->_effect_duration;
785
786 id->_playing = true;
787 id->_start_time = now;
788 id->_end_time = now + duration;
789
790 return true;
791 }
792
793
lhap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID * id)794 static bool lhap_stop_effect(ALLEGRO_HAPTIC_EFFECT_ID *id)
795 {
796 ALLEGRO_HAPTIC_LINUX *lhap = (ALLEGRO_HAPTIC_LINUX *) id->_haptic;
797 struct input_event play;
798
799 if (!lhap)
800 return false;
801
802 memset(&play, 0, sizeof(play));
803
804 play.type = EV_FF;
805 play.code = id->_handle;
806 play.value = 0;
807 if (write(lhap->fd, (const void *)&play, sizeof(play)) < 0) {
808 ALLEGRO_ERROR("Stop effect failed.\n");
809 return false;
810 }
811 id->_playing = false;
812 return true;
813 }
814
815
lhap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID * id)816 static bool lhap_is_effect_playing(ALLEGRO_HAPTIC_EFFECT_ID *id)
817 {
818 ASSERT(id);
819
820 /* Since AFAICS there is no Linux API to test this, use a timer to check
821 * if the effect has been playing long enough to be finished or not.
822 */
823 return (id->_playing && al_get_time() < id->_end_time);
824 }
825
826
lhap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID * id)827 static bool lhap_release_effect(ALLEGRO_HAPTIC_EFFECT_ID *id)
828 {
829 ALLEGRO_HAPTIC_LINUX *lhap = (ALLEGRO_HAPTIC_LINUX *)id->_haptic;
830
831 lhap_stop_effect(id);
832
833 if (ioctl(lhap->fd, EVIOCRMFF, id->_handle) < 0) {
834 ALLEGRO_ERROR("EVIOCRMFF failed.\n");
835 return false;
836 }
837 lhap->effects[id->_id] = -1; /* not in use */
838 return true;
839 }
840
841
lhap_release(ALLEGRO_HAPTIC * haptic)842 static bool lhap_release(ALLEGRO_HAPTIC *haptic)
843 {
844 ALLEGRO_HAPTIC_LINUX *lhap = lhap_from_al(haptic);
845 ASSERT(haptic);
846
847 if (!lhap->active)
848 return false;
849
850 lhap->active = false;
851 lhap->fd = -1;
852 return true;
853 }
854
855
856 #endif /* ALLEGRO_HAVE_LINUX_INPUT_H */
857
858
859 /* vim: set sts=3 sw=3 et: */
860