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