1 /* bzflag
2  * Copyright (c) 1993-2021 Tim Riker
3  *
4  * This package is free software;  you can redistribute it and/or
5  * modify it under the terms of the license found in the file
6  * named COPYING that should have accompanied this file.
7  *
8  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11  */
12 
13 /* common headers */
14 #include "common.h"
15 
16 /* interface headers */
17 #include "EvdevJoystick.h"
18 
19 #ifdef HAVE_LINUX_INPUT_H
20 
21 /* system headers */
22 #include <vector>
23 #include <string>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include <dirent.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <linux/input.h>
32 
33 /* implementation headers */
34 #include "ErrorHandler.h"
35 #include "bzfio.h"
36 
test_bit(int nr,const volatile void * addr)37 static inline int test_bit (int nr, const volatile void *addr)
38 {
39     return 1 & (((const volatile uint32_t *) addr)[nr >> 5] >> (nr & 31));
40 }
41 
isEvdevAvailable()42 bool         EvdevJoystick::isEvdevAvailable()
43 {
44     /* Test whether this driver should be used without actually
45      * loading it. Will return false if no event devices can be
46      * located, or if it has been specifically disabled by setting
47      * the environment variable BZFLAG_ENABLE_EVDEV=0
48      */
49 
50     char *envvar = getenv("BZFLAG_ENABLE_EVDEV");
51     if (envvar)
52         return atoi(envvar) != 0;
53 
54     std::map<std::string,EvdevJoystickInfo> joysticks;
55     scanForJoysticks(joysticks);
56     return !joysticks.empty();
57 }
58 
59 
EvdevJoystick()60 EvdevJoystick::EvdevJoystick()
61 {
62     joystickfd = 0;
63     currentJoystick = NULL;
64 #if defined(HAVE_FF_EFFECT_DIRECTIONAL) || defined(HAVE_FF_EFFECT_RUMBLE)
65     ff_rumble = new struct ff_effect;
66 #else
67     ff_rumble = NULL;
68 #endif
69     scanForJoysticks(joysticks);
70 }
71 
~EvdevJoystick()72 EvdevJoystick::~EvdevJoystick()
73 {
74     initJoystick("");
75     delete ff_rumble;
76 }
77 
scanForJoysticks(std::map<std::string,EvdevJoystickInfo> & joysticks)78 void         EvdevJoystick::scanForJoysticks(std::map<std::string,
79         EvdevJoystickInfo> &joysticks)
80 {
81     joysticks.clear();
82 
83     const std::string inputdirName = "/dev/input";
84     DIR* inputdir = opendir(inputdirName.c_str());
85     if (!inputdir)
86         return;
87 
88     struct dirent *dent;
89     while ((dent = readdir(inputdir)))
90     {
91         EvdevJoystickInfo info;
92 
93         /* Does it look like an event device? */
94         if (strncmp(dent->d_name, "event", 5))
95             continue;
96 
97         /* Can we open it for r/w? */
98         info.filename = inputdirName + "/" + dent->d_name;
99         int fd = open(info.filename.c_str(), O_RDWR);
100         /* if we can't open read/write, try just read...if it's not ff it'll work anyhow */
101         if (!fd)
102         {
103             fd = open(info.filename.c_str(), O_RDONLY);
104             if (fd) logDebugMessage(4,"Opened event device %s as read-only.\n", info.filename.c_str());
105         }
106         else
107             logDebugMessage(4,"Opened event device %s as read-write.\n", info.filename.c_str());
108         /* no good, can't open it */
109         if (!fd)
110         {
111             logDebugMessage(4,"Can't open event device %s.  Check permissions.\n", info.filename.c_str());
112             continue;
113         }
114 
115         /* Does it look like a joystick? */
116         if (!(collectJoystickBits(fd, info) && isJoystick(info)))
117         {
118             logDebugMessage(4,"Device %s doesn't seem to be a joystick.  Skipping.\n", info.filename.c_str());
119             close(fd);
120             continue;
121         }
122 
123         /* Can we get its name? */
124         char jsname[128];
125         if (ioctl(fd, EVIOCGNAME(sizeof(jsname)-1), jsname) < 0)
126         {
127             close(fd);
128             continue;
129         }
130         jsname[sizeof(jsname)-1] = '\0';
131 
132         close(fd);
133 
134         /* Yay, add it to our map.
135          *
136          * FIXME: we can't handle multiple joysticks with the same name yet.
137          *  This could be fixed by disambiguating jsname if it already
138          *  exists in 'joysticks', but the user would still have a hard
139          *  time knowing which device to pick.
140          */
141         joysticks[jsname] = info;
142     }
143 
144     closedir(inputdir);
145 }
146 
collectJoystickBits(int fd,struct EvdevJoystickInfo & info)147 bool            EvdevJoystick::collectJoystickBits(int fd, struct EvdevJoystickInfo &info)
148 {
149     /* Collect all the bitfields we're interested in from an event device
150      * at the given file descriptor.
151      */
152     if (ioctl(fd, EVIOCGBIT(0, sizeof(info.evbit)), info.evbit) < 0)
153         return false;
154     if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(info.keybit)), info.keybit) < 0)
155         return false;
156     if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(info.absbit)), info.absbit) < 0)
157         return false;
158 #if defined(HAVE_FF_EFFECT_DIRECTIONAL) || defined(HAVE_FF_EFFECT_RUMBLE)
159     if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(info.ffbit)), info.ffbit) < 0)
160         return false;
161 #endif
162 
163     /* Collect information about our absolute axes */
164     int axis;
165     for (axis=0; axis<8; axis++)
166     {
167         if (ioctl(fd, EVIOCGABS(axis + ABS_X), &info.axis_info[axis]) < 0)
168             return false;
169     }
170 
171     return true;
172 }
173 
isJoystick(struct EvdevJoystickInfo & info)174 bool            EvdevJoystick::isJoystick(struct EvdevJoystickInfo &info)
175 {
176     /* Look at the capability bitfields in the given EvdevJoystickInfo, and
177      * decide whether the device is indeed a joystick. This uses the same criteria
178      * that SDL does- it at least needs X and Y axes, and one joystick-like button.
179      */
180     if (!test_bit(EV_KEY, info.evbit))
181         return false;
182     if (!(test_bit(BTN_TRIGGER, info.keybit) ||
183             test_bit(BTN_A, info.keybit) ||
184             test_bit(BTN_1, info.keybit)))
185         return false;
186 
187     if (!test_bit(EV_ABS, info.evbit))
188         return false;
189     if (!(test_bit(ABS_X, info.absbit) &&
190             test_bit(ABS_Y, info.absbit)))
191         return false;
192 
193     return true;
194 }
195 
initJoystick(const char * joystickName)196 void            EvdevJoystick::initJoystick(const char* joystickName)
197 {
198     /* Close the previous joystick */
199     ffResetEffect();
200     if (joystickfd > 0)
201         close(joystickfd);
202     currentJoystick = NULL;
203     joystickfd = 0;
204 
205     if (!strcasecmp(joystickName, "off") || !strcmp(joystickName, ""))
206     {
207         /* No joystick configured, we're done */
208         return;
209     }
210 
211     std::map<std::string,EvdevJoystickInfo>::iterator iter;
212     iter = joysticks.find(joystickName);
213     if (iter == joysticks.end())
214     {
215         printError("The selected joystick no longer exists.");
216         return;
217     }
218 
219     /* Looks like we might have a valid joystick, try to open it */
220     EvdevJoystickInfo *info = &iter->second;
221     joystickfd = open(info->filename.c_str(), O_RDWR | O_NONBLOCK);
222     if (joystickfd > 0)
223     {
224         /* Yay, it worked */
225         currentJoystick = info;
226         currentJoystick->readonly = false;
227     }
228     else
229     {
230         joystickfd = open(info->filename.c_str(), O_RDONLY | O_NONBLOCK);
231         if (joystickfd > 0)
232         {
233             /* Got it in read-only */
234             currentJoystick = info;
235             currentJoystick->readonly = true;
236             printError("No write access to joystick device, force feedback disabled.");
237         }
238         else
239             printError("Error opening the selected joystick.");
240     }
241 
242     useaxis[0] = ABS_X;
243     useaxis[1] = ABS_Y;
244     buttons = 0;
245     //for some reason, I can't put this in getJoyHats
246     hataxes.assign(4 * 2, 0);
247 }
248 
joystick() const249 bool            EvdevJoystick::joystick() const
250 {
251     return currentJoystick != NULL;
252 }
253 
poll()254 void            EvdevJoystick::poll()
255 {
256     /* Read as many input events as are available, and update our current state
257      */
258     struct input_event ev;
259     while (read(joystickfd, &ev, sizeof(ev)) > 0)
260     {
261         switch (ev.type)
262         {
263 
264         case EV_ABS:
265             if (ev.code > ABS_WHEEL)
266             {
267                 if (ABS_HAT0X <= ev.code && ev.code <= ABS_HAT3Y)
268                     hataxes[ev.code - ABS_HAT0X] = ev.value;
269                 break;
270             }
271             currentJoystick->axis_info[ev.code - ABS_X].value = ev.value;
272             break;
273 
274         case EV_KEY:
275             setButton(mapButton(ev.code), ev.value);
276             break;
277 
278         }
279     }
280 }
281 
mapButton(int bit_num)282 int          EvdevJoystick::mapButton(int bit_num)
283 {
284     /* Given an evdev button number, map it back to a small integer that most
285      * people would consider the button's actual number. This also ensures
286      * that we can fit all buttons in "buttons" as long as the number of buttons
287      * is less than the architecture's word size ;)
288      *
289      * We just scan through the joystick's keybits, counting how many
290      * set bits we encounter before this one. If the indicated bit isn't
291      * set in keybits, this is a bad event and we return -1.
292      * If this linear scan becomes a noticeable performance drain, this could
293      * easily be precomputed and stored in an std:map.
294      */
295     int i;
296     int button_num = 0;
297     const int total_bits = sizeof(currentJoystick->keybit)*sizeof(unsigned long)*8;
298 
299     for (i=0; i<total_bits; i++)
300     {
301         if (i == bit_num)
302             return button_num;
303         if (test_bit(i, currentJoystick->keybit))
304             button_num++;
305     }
306     return -1;
307 }
308 
setButton(int button_num,int state)309 void            EvdevJoystick::setButton(int button_num, int state)
310 {
311 
312     if (button_num >= 0)
313     {
314         int mask = 1<<button_num;
315         if (state)
316             buttons |= mask;
317         else
318             buttons &= ~mask;
319     }
320 }
321 
getJoy(int & x,int & y)322 void            EvdevJoystick::getJoy(int& x, int& y)
323 {
324     if (currentJoystick)
325     {
326         poll();
327 
328         int axes[9];
329         int axis;
330         int value;
331         for (axis=0; axis<9; axis++)
332         {
333             if (!(test_bit(ABS_X + axis, currentJoystick->absbit)))
334                 continue;
335 
336             /* Each axis gets scaled from evdev's reported minimum
337              * and maximum into bzflag's [-1000, 1000] range.
338              */
339             value = currentJoystick->axis_info[axis].value;
340             value -= currentJoystick->axis_info[axis].minimum;
341             value = value * 2000 / (currentJoystick->axis_info[axis].maximum -
342                                     currentJoystick->axis_info[axis].minimum);
343             value -= 1000;
344 
345             /* No cheating by modifying joystick drivers, or using some that rate
346              * their maximum and minimum conservatively like the input spec allows.
347              */
348             if (value < -1000) value = -1000;
349             if (value >  1000) value =  1000;
350 
351             /* All the cool kids are doing it... */
352             value = (value * abs(value)) / 1000;
353 
354             axes[axis] = value;
355         }
356         x = axes[useaxis[0]];
357         y = axes[useaxis[1]];
358     }
359     else
360         x = y = 0;
361 }
362 
getNumHats()363 int          EvdevJoystick::getNumHats()
364 {
365     numHats = 0;
366     if (currentJoystick)
367     {
368         for (int i = 0; i < 4; ++i)
369         {
370             if (test_bit(ABS_HAT0X + i * 2, currentJoystick->absbit))
371                 numHats++;
372         }
373     }
374     //for some reason, I can't move this from initJoystick
375     //hataxes.assign(numHats * 2, 0);
376     return numHats;
377 }
378 
getJoyHat(int hat,float & hatX,float & hatY)379 void            EvdevJoystick::getJoyHat(int hat, float &hatX, float &hatY)
380 {
381     hatX = hatY = 0;
382     if (currentJoystick)
383     {
384         if (hat < 4)
385         {
386             poll();
387             hatX = hataxes[hat * 2];
388             hatY = hataxes[hat * 2 + 1];
389         }
390     }
391 }
392 
getJoyButtons()393 unsigned long       EvdevJoystick::getJoyButtons()
394 {
395     if (currentJoystick)
396     {
397         poll();
398         return buttons;
399     }
400     else
401         return 0;
402 }
403 
getJoyDevices(std::vector<std::string> & list) const404 void            EvdevJoystick::getJoyDevices(std::vector<std::string>
405         &list) const
406 {
407     std::map<std::string,EvdevJoystickInfo>::const_iterator i;
408     for (i = joysticks.begin(); i != joysticks.end(); ++i)
409         list.push_back(i->first);
410 }
411 
412 static const std::string anames[9] = { "X", "Y", "Z", "Rx", "Ry", "Rz", "Throttle", "Rudder", "Wheel" };
413 
getJoyDeviceAxes(std::vector<std::string> & list) const414 void        EvdevJoystick::getJoyDeviceAxes(std::vector<std::string>
415         &list) const
416 {
417     list.clear();
418 
419     if (!currentJoystick)
420         return;
421 
422     for (int i = 0; i < 9; ++i)
423         if (test_bit(ABS_X + i, currentJoystick->absbit))
424             list.push_back(anames[i]);
425 }
426 
setXAxis(const std::string & axis)427 void            EvdevJoystick::setXAxis(const std::string &axis)
428 {
429     for (int i = 0; i < 9; ++i)
430         if (anames[i] == axis)
431             useaxis[0] = ABS_X + i;
432 }
433 
setYAxis(const std::string & axis)434 void            EvdevJoystick::setYAxis(const std::string &axis)
435 {
436     for (int i = 0; i < 9; ++i)
437         if (anames[i] == axis)
438             useaxis[1] = ABS_X + i;
439 }
440 
ffHasRumble() const441 bool            EvdevJoystick::ffHasRumble() const
442 {
443 #ifdef HAVE_FF_EFFECT_RUMBLE
444     if (!currentJoystick)
445         return false;
446     else
447         return test_bit(EV_FF, currentJoystick->evbit) &&
448                test_bit(FF_RUMBLE, currentJoystick->ffbit) &&
449                !currentJoystick->readonly;
450 #else
451     return false;
452 #endif
453 }
454 
455 #if (defined HAVE_FF_EFFECT_DIRECTIONAL || defined HAVE_FF_EFFECT_RUMBLE)
writeJoystick(int count)456 void EvdevJoystick::writeJoystick(int count)
457 {
458     struct input_event event;
459     ssize_t     written_byte;
460 
461     event.type   = EV_FF;
462     event.code   = ff_rumble->id;
463     event.value  = count;
464     written_byte = write(joystickfd, &event, sizeof(event));
465     if (written_byte != sizeof(event))
466         printError("Unable to write on joystick");
467 }
468 #else
writeJoystick(int)469 void EvdevJoystick::writeJoystick(int)
470 {
471 }
472 #endif
473 
ffResetEffect()474 void            EvdevJoystick::ffResetEffect()
475 {
476 #if (defined HAVE_FF_EFFECT_DIRECTIONAL || defined HAVE_FF_EFFECT_RUMBLE)
477     /* Erase old effects before closing a device,
478      * if we had any, then initialize the ff_rumble struct.
479      */
480     if ((ffHasRumble() || ffHasDirectional()) && ff_rumble->id != -1)
481     {
482 
483         /* Stop the effect first */
484         writeJoystick(0);
485 
486         /* Erase the downloaded effect */
487         ioctl(joystickfd, EVIOCRMFF, ff_rumble->id);
488     }
489 
490     /* Reinit the ff_rumble struct. It starts out with
491      * an id of -1, prompting the driver to assign us one.
492      * Once that happens, we stick with the same effect slot
493      * as long as we have the device open.
494      */
495     memset(ff_rumble, 0, sizeof(*ff_rumble));
496     ff_rumble->id = -1;
497 #endif
498 }
499 
500 #ifdef HAVE_FF_EFFECT_RUMBLE
ffRumble(int count,float delay,float duration,float strong_motor,float weak_motor)501 void            EvdevJoystick::ffRumble(int count,
502                                         float delay, float duration,
503                                         float strong_motor,
504                                         float weak_motor)
505 {
506     if (!ffHasRumble())
507         return;
508 
509     /* Stop the previous effect we were playing, if any */
510     if (ff_rumble->id != -1)
511         writeJoystick(0);
512 
513     if (count > 0)
514     {
515         /* Download an updated effect */
516         ff_rumble->type = FF_RUMBLE;
517         ff_rumble->u.rumble.strong_magnitude = (int) (0xFFFF * strong_motor + 0.5);
518         ff_rumble->u.rumble.weak_magnitude = (int) (0xFFFF * weak_motor + 0.5);
519         ff_rumble->replay.length = (int) (duration * 1000 + 0.5);
520         ff_rumble->replay.delay = (int) (delay * 1000 + 0.5);
521         ioctl(joystickfd, EVIOCSFF, ff_rumble);
522 
523         /* Play it the indicated number of times */
524         writeJoystick(count);
525     }
526 }
527 #else
ffRumble(int,float,float,float,float)528 void EvdevJoystick::ffRumble(int, float, float, float, float)
529 {
530 }
531 #endif
532 
ffHasDirectional() const533 bool EvdevJoystick::ffHasDirectional() const
534 {
535 #ifdef HAVE_FF_EFFECT_DIRECTIONAL
536     if (!currentJoystick)
537         return false;
538     else
539         return test_bit(EV_FF, currentJoystick->evbit) &&
540                test_bit(FF_PERIODIC, currentJoystick->ffbit) &&
541                test_bit(FF_CONSTANT, currentJoystick->ffbit) &&
542                !currentJoystick->readonly;
543 #else
544     return false;
545 #endif
546 }
547 
548 #ifdef HAVE_FF_EFFECT_DIRECTIONAL
ffDirectionalConstant(int count,float delay,float duration,float x_direction,float y_direction,float strength)549 void EvdevJoystick::ffDirectionalConstant(int count, float delay, float duration,
550         float x_direction, float y_direction,
551         float strength)
552 {
553     if (!ffHasDirectional())
554         return;
555 
556     /* whenever we switch effect types we must reset the effect
557      * this could be avoided by tracking the slot numbers and only
558      * using one for each type.
559      * When we don't switch types we still must stop the previous
560      * effect we were playing, if any.
561      */
562     if (ff_rumble->type != FF_CONSTANT)
563         ffResetEffect();
564     else if (ff_rumble->id != -1)
565         writeJoystick(0);
566 
567     if (count > 0)
568     {
569         /* Download an updated effect */
570         ff_rumble->type = FF_CONSTANT;
571         ff_rumble->u.constant.level = (int) (0x7FFF * strength + 0.5f);
572         ff_rumble->direction = (int) (0xFFFF * (1.0 / (2.0 * M_PI)) *
573                                       atan2(x_direction, -y_direction) + 0.5);
574         ff_rumble->u.constant.envelope.attack_length = FF_NOMINAL_MIN;
575         ff_rumble->u.constant.envelope.fade_length = FF_NOMINAL_MIN;
576         ff_rumble->u.constant.envelope.attack_level = ff_rumble->u.constant.level;
577         ff_rumble->u.constant.envelope.fade_level = ff_rumble->u.constant.level;
578         ff_rumble->replay.length = (int) (duration * 1000 + 0.5f);
579         ff_rumble->replay.delay = (int) (delay * 1000 + 0.5f);
580         if (ioctl(joystickfd, EVIOCSFF, ff_rumble) == -1)
581             printError("Effect upload failed.");
582 
583         /* Play it the indicated number of times */
584         writeJoystick(count);
585     }
586 }
587 #else
ffDirectionalConstant(int,float,float,float,float,float)588 void EvdevJoystick::ffDirectionalConstant(int, float, float, float, float, float)
589 {
590 }
591 #endif
592 
593 #ifdef HAVE_FF_EFFECT_DIRECTIONAL
ffDirectionalPeriodic(int count,float delay,float duration,float x_direction,float y_direction,float amplitude,float period,PeriodicType type)594 void EvdevJoystick::ffDirectionalPeriodic(int count, float delay, float duration,
595         float x_direction, float y_direction,
596         float amplitude, float period,
597         PeriodicType type)
598 {
599     if (!ffHasDirectional())
600         return;
601 
602     /* whenever we switch effect types we must reset the effect
603      * this could be avoided by tracking the slot numbers and only
604      * using one for each type.
605      * When we don't switch types we still must stop the previous
606      * effect we were playing, if any.
607      */
608     if (ff_rumble->type != FF_PERIODIC)
609         ffResetEffect();
610     else if (ff_rumble->id != -1)
611         writeJoystick(0);
612 
613     if (count > 0)
614     {
615         /* Download an updated effect */
616         ff_rumble->type = FF_PERIODIC;
617         int wave = 0;
618         switch (type)
619         {
620         case FF_Sine:
621             wave = FF_SINE;
622             break;
623         case FF_Square:
624             wave = FF_SQUARE;
625             break;
626         case FF_Triangle:
627             wave = FF_TRIANGLE;
628             break;
629         case FF_SawtoothUp:
630             wave = FF_SAW_UP;
631             break;
632         case FF_SawtoothDown:
633             wave = FF_SAW_DOWN;
634             break;
635         default:
636             printError("Unknown periodic force feedback waveform.");
637             return;
638         }
639         ff_rumble->u.periodic.waveform = wave;
640         ff_rumble->u.periodic.magnitude = (int) (0x7FFF * amplitude + 0.5f);
641         ff_rumble->u.periodic.period = (int) (period * 1000 + 0.5f);
642         ff_rumble->u.periodic.offset = ff_rumble->u.periodic.phase = 0;
643         ff_rumble->direction = (int) (0xFFFF * (1.0 / (2.0 * M_PI)) *
644                                       atan2(x_direction, -y_direction) + 0.5);
645         ff_rumble->u.periodic.envelope.attack_length = FF_NOMINAL_MIN;
646         ff_rumble->u.periodic.envelope.fade_length = FF_NOMINAL_MIN;
647         ff_rumble->u.periodic.envelope.attack_level = ff_rumble->u.periodic.magnitude;
648         ff_rumble->u.periodic.envelope.fade_level = ff_rumble->u.periodic.magnitude;
649         ff_rumble->replay.length = (int) (duration * 1000 + 0.5f);
650         ff_rumble->replay.delay = (int) (delay * 1000 + 0.5f);
651         if (ioctl(joystickfd, EVIOCSFF, ff_rumble) == -1)
652             printError("Effect upload failed.");
653 
654         /* Play it the indicated number of times */
655         writeJoystick(count);
656     }
657 }
658 #else
ffDirectionalPeriodic(int,float,float,float,float,float,float,PeriodicType)659 void EvdevJoystick::ffDirectionalPeriodic(int, float, float, float, float, float,
660         float, PeriodicType)
661 {
662 }
663 #endif
664 
665 #ifdef HAVE_FF_EFFECT_DIRECTIONAL
ffDirectionalResistance(float time,float coefficient,float saturation,ResistanceType type)666 void EvdevJoystick::ffDirectionalResistance(float time, float coefficient,
667         float saturation, ResistanceType type)
668 {
669     if (!ffHasDirectional())
670         return;
671 
672     /* whenever we switch effect types we must reset the effect
673      * this could be avoided by tracking the slot numbers and only
674      * using one for each type.
675      * When we don't switch types we still must stop the previous
676      * effect we were playing, if any.
677      */
678     if ((ff_rumble->type != FF_SPRING) &&
679             (ff_rumble->type != FF_FRICTION) &&
680             (ff_rumble->type != FF_DAMPER))
681         ffResetEffect();
682     else if (ff_rumble->id != -1)
683         writeJoystick(0);
684 
685     if (1 > 0)
686     {
687         /* Download an updated effect */
688         int lintype;
689         switch (type)
690         {
691         case FF_Position:
692             lintype = FF_SPRING;
693             break;
694         case FF_Velocity:
695             lintype = FF_FRICTION;
696             break;
697         case FF_Acceleration:
698             lintype = FF_DAMPER;
699             break;
700         default:
701             printError("Unrecognized force feedback resistance type.");
702             return;
703         }
704         ff_rumble->type = lintype;
705         ff_rumble->u.condition[0].right_saturation =
706             ff_rumble->u.condition[0].left_saturation = (int) (0x7FFF * saturation + 0.5f);
707         ff_rumble->u.condition[0].right_coeff =
708             ff_rumble->u.condition[0].left_coeff = (int) (0x7FFF * coefficient + 0.5f);
709         ff_rumble->u.condition[0].deadband = 0;
710         ff_rumble->u.condition[0].center = 0;
711         ff_rumble->u.condition[1] = ff_rumble->u.condition[0];
712         ff_rumble->replay.length = (int) (time * 1000 + 0.5f);
713         ff_rumble->replay.delay = 0;
714         if (ioctl(joystickfd, EVIOCSFF, ff_rumble) == -1)
715             printError("Effect upload failed.");
716 
717         /* Play it just once */
718         writeJoystick(1);
719     }
720 }
721 #else
ffDirectionalResistance(float,float,float,ResistanceType)722 void EvdevJoystick::ffDirectionalResistance(float, float, float, ResistanceType)
723 {
724 }
725 #endif
726 
727 #endif /* HAVE_LINUX_INPUT_H */
728 
729 // Local Variables: ***
730 // mode: C++ ***
731 // tab-width: 4 ***
732 // c-basic-offset: 4 ***
733 // indent-tabs-mode: nil ***
734 // End: ***
735 // ex: shiftwidth=4 tabstop=4
736