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