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 <core/exception.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 
29 #include <cerrno>
30 #include <cstdio>
31 #include <cstdlib>
32 #include <cstring>
33 #include <dirent.h>
34 #include <fcntl.h>
35 #include <fnmatch.h>
36 #include <unistd.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 /** Destructor. */
228 {
229  close(fd_);
230 }
231 
232 /** Rumble the joystick.
233 
234  * This is the most basic force feedback for example in force feedback
235  * joypads. Often such joysticks provide two effect magnitudes, a
236  * strong heavier motor for larger effects, and a smaller one for
237  * vibrating effects.
238  * @param strong_magnitude magnitude to use on the larger motor
239  * @param weak_magnitude magnitude to use on the smaller motor
240  * @param direction direction of the effect, meaningful on joysticks
241  * (rather than joypads)
242  * @param length length of the effect in ms
243  * @param delay delay before the effect starts in ms
244  */
245 void
246 JoystickForceFeedback::rumble(uint16_t strong_magnitude,
247  uint16_t weak_magnitude,
248  Direction direction,
249  uint16_t length,
250  uint16_t delay)
251 {
252  if ((rumble_.id == -1) || (rumble_.u.rumble.strong_magnitude != strong_magnitude)
253  || (rumble_.u.rumble.weak_magnitude != weak_magnitude) || (rumble_.direction != direction)
254  || (rumble_.replay.length != length) || (rumble_.replay.delay != length)) {
255  // we need to upload
256  rumble_.u.rumble.strong_magnitude = strong_magnitude;
257  rumble_.u.rumble.weak_magnitude = weak_magnitude;
258  rumble_.direction = direction;
259  rumble_.replay.length = length;
260  rumble_.replay.delay = delay;
261 
262  if (ioctl(fd_, EVIOCSFF, &rumble_) < 0) {
263  throw Exception("Failed to upload rumble effect");
264  }
265  }
266 
267  struct input_event play;
268  play.type = EV_FF;
269  play.code = rumble_.id;
270  play.value = 1;
271 
272  if (write(fd_, &play, sizeof(play)) < 0) {
273  throw Exception("Failed to start rumble effect");
274  }
275 }
276 
277 /** Stop rumbling. */
278 void
280 {
281  if (rumble_.id != -1) {
282  if (ioctl(fd_, EVIOCRMFF, rumble_.id) < 0) {
283  throw Exception("Failed to stop rumble effect");
284  }
285  rumble_.id = -1;
286  }
287 }
288 
289 /** Stop all current effects. */
290 void
292 {
293  stop_rumble();
294 }
JoystickForceFeedback::stop_all
void stop_all()
Stop all current effects.
Definition: force_feedback.cpp:291
JoystickForceFeedback::~JoystickForceFeedback
~JoystickForceFeedback()
Destructor.
Definition: force_feedback.cpp:227
fawkes
Fawkes library namespace.
JoystickForceFeedback::rumble
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.
Definition: force_feedback.cpp:246
JoystickForceFeedback::stop_rumble
void stop_rumble()
Stop rumbling.
Definition: force_feedback.cpp:279
JoystickForceFeedback::Direction
Direction
Direction of the effect.
Definition: force_feedback.h:34
JoystickForceFeedback::JoystickForceFeedback
JoystickForceFeedback(const char *device_name)
Constructor.
Definition: force_feedback.cpp:123
fawkes::Exception
Base class for exceptions in Fawkes.
Definition: exception.h:36