Fawkes API  Fawkes Development Version
force_feedback.cpp
1 
2 /***************************************************************************
3  * force_feedback.cpp - Force feedback for joysticks using Linux input API
4  *
5  * Created: Mon Feb 07 01:35:29 2011 (Super Bowl XLV)
6  * Copyright 2006-2011 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Library General Public License for more details.
19  *
20  * Read the full text in the LICENSE.GPL file in the doc directory.
21  */
22 
23 #include "force_feedback.h"
24 
25 #include <fnmatch.h>
26 #include <sys/types.h>
27 #include <dirent.h>
28 #include <cstdio>
29 #include <cstdlib>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <cstring>
34 #include <cerrno>
35 
36 #include <core/exception.h>
37 
38 #define BITS_PER_LONG (sizeof(long) * 8)
39 #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
40 #define OFF(x) ((x)%BITS_PER_LONG)
41 #define BIT(x) (1UL<<OFF(x))
42 #define LONG(x) ((x)/BITS_PER_LONG)
43 #define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
44 
45 using namespace fawkes;
46 
47 /** @class JoystickForceFeedback "force_feedback.h"
48  * Cause force feedback on a joystick.
49  * An instance of this class opens an input device which belongs to
50  * the given device name. It searches all input devices to find the
51  * correct device file. Once opened, it detects the available features
52  * of the joystick and provides conventient access to it allowing for
53  * rumbling effects, for instance.
54  * @author Tim Niemueller
55  *
56  * @fn bool JoystickForceFeedback::is_rumbling()
57  * Check if rumbling effect is active.
58  * @return true if effect is active, false otherwise
59  *
60  * @fn bool JoystickForceFeedback::can_rumble()
61  * Check if rumbling effect is supported.
62  * @return true if effect is supported, false otherwise
63  *
64  * @fn bool JoystickForceFeedback::can_periodic()
65  * Check if periodic effect is supported.
66  * @return true if effect is supported, false otherwise
67  *
68  * @fn bool JoystickForceFeedback::can_constant()
69  * Check if constant effect is supported.
70  * @return true if effect is supported, false otherwise
71  *
72  * @fn bool JoystickForceFeedback::can_spring()
73  * Check if spring effect is supported.
74  * @return true if effect is supported, false otherwise
75  *
76  * @fn bool JoystickForceFeedback::can_friction()
77  * Check if friction effect is supported.
78  * @return true if effect is supported, false otherwise
79  *
80  * @fn bool JoystickForceFeedback::can_damper()
81  * Check if damper effect is supported.
82  * @return true if effect is supported, false otherwise
83  *
84  * @fn bool JoystickForceFeedback::can_inertia()
85  * Check if inertia effect is supported.
86  * @return true if effect is supported, false otherwise
87  *
88  * @fn bool JoystickForceFeedback::can_ramp()
89  * Check if ramp effect is supported.
90  * @return true if effect is supported, false otherwise
91  *
92  * @fn bool JoystickForceFeedback::can_square()
93  * Check if square effect is supported.
94  * @return true if effect is supported, false otherwise
95  *
96  * @fn bool JoystickForceFeedback::can_triangle()
97  * Check if triangle effect is supported.
98  * @return true if effect is supported, false otherwise
99  *
100  * @fn bool JoystickForceFeedback::can_sine()
101  * Check if sine effect is supported.
102  * @return true if effect is supported, false otherwise
103  *
104  * @fn bool JoystickForceFeedback::can_saw_up()
105  * Check if upward saw effect is supported.
106  * @return true if effect is supported, false otherwise
107  *
108  * @fn bool JoystickForceFeedback::can_saw_down()
109  * Check if downward saw effect is supported.
110  * @return true if effect is supported, false otherwise
111  *
112  * @fn bool JoystickForceFeedback::can_custom()
113  * Check if custom effect is supported.
114  * @return true if effect is supported, false otherwise
115  *
116  */
117 
118 /** Constructor.
119  * @param device_name device name, note that this is not the device
120  * file, but rather the event files are tried and the device name is
121  * compared.
122  */
124 {
125  __fd = -1;
126 
127  DIR *d = opendir("/dev/input");
128 
129  if (d == NULL) {
130  throw Exception("Could not open directory /dev/input");
131  }
132 
133  struct dirent *de;
134  while ((de = readdir(d)) != NULL) {
135  if (fnmatch("event*", de->d_name, 0) != FNM_NOMATCH) {
136  char *path;
137  if (asprintf(&path, "/dev/input/%s", de->d_name) == -1) {
138  continue;
139  }
140 
141  __fd = open(path, O_RDWR);
142  if (__fd == -1) {
143  free(path);
144  continue;
145  }
146  free(path);
147 
148  char name[256]= "Unknown";
149  if(ioctl(__fd, EVIOCGNAME(sizeof(name)), name) < 0) {
150  close(__fd);
151  __fd = -1;
152  continue;
153  }
154 
155  if (strcmp(name, device_name) != 0) {
156  close(__fd);
157  __fd = -1;
158  continue;
159  }
160 
161  long features[NBITS(EV_MAX)];
162  memset(features, 0, sizeof(features));
163  if (ioctl(__fd, EVIOCGBIT(0, EV_MAX), features) < 0) {
164  close(__fd);
165  __fd = -1;
166  throw Exception("Cannot get feedback feature vector");
167  }
168 
169  if (! test_bit(EV_FF, features)) {
170  close(__fd);
171  __fd = -1;
172  throw Exception("Device '%s' does not support force-feedback", device_name);
173  }
174 
175  long ff_features[NBITS(FF_MAX)];
176 
177  memset(ff_features, 0, sizeof(ff_features));
178  if (ioctl(__fd, EVIOCGBIT(EV_FF, FF_MAX), ff_features) < 0) {
179  close(__fd);
180  __fd = -1;
181  throw Exception("Cannot get device force feedback feature vector");
182  }
183 
184  long no_ff_features[NBITS(FF_MAX)];
185  memset(no_ff_features, 0, sizeof(no_ff_features));
186  if (memcmp(ff_features, no_ff_features, sizeof(no_ff_features)) == 0) {
187  close(__fd);
188  __fd = -1;
189  throw Exception("Device has no force feedback features");
190  }
191 
192  __can_rumble = test_bit(FF_RUMBLE, ff_features);
193  __can_periodic = test_bit(FF_PERIODIC, ff_features);
194  __can_constant = test_bit(FF_CONSTANT, ff_features);
195  __can_spring = test_bit(FF_SPRING, ff_features);
196  __can_friction = test_bit(FF_FRICTION, ff_features);
197  __can_damper = test_bit(FF_DAMPER, ff_features);
198  __can_inertia = test_bit(FF_INERTIA, ff_features);
199  __can_ramp = test_bit(FF_RAMP, ff_features);
200  __can_square = test_bit(FF_SQUARE, ff_features);
201  __can_triangle = test_bit(FF_TRIANGLE, ff_features);
202  __can_sine = test_bit(FF_SINE, ff_features);
203  __can_saw_up = test_bit(FF_SAW_UP, ff_features);
204  __can_saw_down = test_bit(FF_SAW_DOWN, ff_features);
205  __can_custom = test_bit(FF_CUSTOM, ff_features);
206 
207  if (ioctl(__fd, EVIOCGEFFECTS, &__num_effects) < 0) {
208  __num_effects = 1;
209  }
210 
211  break;
212  }
213  }
214 
215  closedir(d);
216 
217  if (__fd == -1) {
218  throw Exception("Force feedback joystick '%s' not found", device_name);
219  }
220 
221  memset(&__rumble, 0, sizeof(__rumble));
222  __rumble.type = FF_RUMBLE;
223  __rumble.id = -1;
224 }
225 
226 
227 /** Destructor. */
229 {
230  close(__fd);
231 }
232 
233 
234 /** Rumble the joystick.
235 
236  * This is the most basic force feedback for example in force feedback
237  * joypads. Often such joysticks provide two effect magnitudes, a
238  * strong heavier motor for larger effects, and a smaller one for
239  * vibrating effects.
240  * @param strong_magnitude magnitude to use on the larger motor
241  * @param weak_magnitude magnitude to use on the smaller motor
242  * @param direction direction of the effect, meaningful on joysticks
243  * (rather than joypads)
244  * @param length length of the effect in ms
245  * @param delay delay before the effect starts in ms
246  */
247 void
248 JoystickForceFeedback::rumble(uint16_t strong_magnitude, uint16_t weak_magnitude,
249  Direction direction, uint16_t length, uint16_t delay)
250 {
251  if ( (__rumble.id == -1) ||
252  (__rumble.u.rumble.strong_magnitude != strong_magnitude) ||
253  (__rumble.u.rumble.weak_magnitude != weak_magnitude) ||
254  (__rumble.direction != direction) ||
255  (__rumble.replay.length != length) ||
256  (__rumble.replay.delay != length) )
257  {
258  // we need to upload
259  __rumble.u.rumble.strong_magnitude = strong_magnitude;
260  __rumble.u.rumble.weak_magnitude = weak_magnitude;
261  __rumble.direction = direction;
262  __rumble.replay.length = length;
263  __rumble.replay.delay = delay;
264 
265  if (ioctl(__fd, EVIOCSFF, &__rumble) < 0) {
266  throw Exception("Failed to upload rumble effect");
267  }
268  }
269 
270  struct input_event play;
271  play.type = EV_FF;
272  play.code = __rumble.id;
273  play.value = 1;
274 
275  if (write(__fd, &play, sizeof(play)) < 0) {
276  throw Exception("Failed to start rumble effect");
277  }
278 }
279 
280 
281 /** Stop rumbling. */
282 void
284 {
285  if (__rumble.id != -1) {
286  if (ioctl(__fd, EVIOCRMFF, __rumble.id) < 0) {
287  throw Exception("Failed to stop rumble effect");
288  }
289  __rumble.id = -1;
290  }
291 }
292 
293 
294 /** Stop all current effects. */
295 void
297 {
298  stop_rumble();
299 }
Fawkes library namespace.
~JoystickForceFeedback()
Destructor.
void stop_rumble()
Stop rumbling.
void stop_all()
Stop all current effects.
void rumble(uint16_t strong_magnitude, uint16_t weak_magnitude, Direction direction=DIRECTION_DOWN, uint16_t length=0, uint16_t delay=0)
Rumble the joystick.
Base class for exceptions in Fawkes.
Definition: exception.h:36
Direction
Direction of the effect.
JoystickForceFeedback(const char *device_name)
Constructor.