1 /*
2 The zlib/libpng License
3
4 Copyright (c) 2005-2007 Phillip Castaneda (pjcast -- www.wreckedgames.com)
5
6 This software is provided 'as-is', without any express or implied warranty. In no event will
7 the authors be held liable for any damages arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose, including commercial
10 applications, and to alter it and redistribute it freely, subject to the following
11 restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not claim that
14 you wrote the original software. If you use this software in a product,
15 an acknowledgment in the product documentation would be appreciated but is
16 not required.
17
18 2. Altered source versions must be plainly marked as such, and must not be
19 misrepresented as being the original software.
20
21 3. This notice may not be removed or altered from any source distribution.
22 */
23 #include "linux/EventHelpers.h"
24 #include "linux/LinuxPrereqs.h"
25 #include "linux/LinuxForceFeedback.h"
26 #include "OISException.h"
27 #include "OISJoyStick.h"
28
29 #include <linux/input.h>
30 #include <cstring>
31
32 //#define OIS_LINUX_JOY_DEBUG
33
34 #ifdef OIS_LINUX_JOY_DEBUG
35 # include <iostream>
36 #endif
37
38 using namespace std;
39 using namespace OIS;
40
41 class DeviceComponentInfo
42 {
43 public:
44 vector<int> buttons, relAxes, absAxes, hats;
45 };
46
isBitSet(unsigned char bits[],unsigned int bit)47 bool inline isBitSet(unsigned char bits[], unsigned int bit)
48 {
49 return (bits[(bit)/(sizeof(unsigned char)*8)] >> ((bit)%(sizeof(unsigned char)*8))) & 1;
50 }
51
52 //-----------------------------------------------------------------------------//
getComponentInfo(int deviceID)53 DeviceComponentInfo getComponentInfo( int deviceID )
54 {
55 unsigned char ev_bits[1 + EV_MAX/8/sizeof(unsigned char)];
56 memset( ev_bits, 0, sizeof(ev_bits) );
57
58 //Read "all" (hence 0) components of the device
59 #ifdef OIS_LINUX_JOY_DEBUG
60 cout << "EventUtils::getComponentInfo(" << deviceID
61 << ") : Reading device events features" << endl;
62 #endif
63 if (ioctl(deviceID, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1)
64 OIS_EXCEPT( E_General, "Could not read device events features");
65
66 DeviceComponentInfo components;
67
68 for (int i = 0; i < EV_MAX; i++)
69 {
70 if( isBitSet(ev_bits, i) )
71 {
72 // Absolute axis.
73 if(i == EV_ABS)
74 {
75 unsigned char abs_bits[1 + ABS_MAX/8/sizeof(unsigned char)];
76 memset( abs_bits, 0, sizeof(abs_bits) );
77
78 #ifdef OIS_LINUX_JOY_DEBUG
79 cout << "EventUtils::getComponentInfo(" << deviceID
80 << ") : Reading device absolute axis features" << endl;
81 #endif
82
83 if (ioctl(deviceID, EVIOCGBIT(i, sizeof(abs_bits)), abs_bits) == -1)
84 OIS_EXCEPT( E_General, "Could not read device absolute axis features");
85
86 for (int j = 0; j < ABS_MAX; j++)
87 {
88 if( isBitSet(abs_bits, j) )
89 {
90 //input_absinfo abInfo;
91 //ioctl( fd, EVIOCGABS(j), abInfo );
92 if( j >= ABS_HAT0X && j <= ABS_HAT3Y )
93 {
94 components.hats.push_back(j);
95 }
96 else
97 {
98 components.absAxes.push_back(j);
99 //input_absinfo absinfo;
100 //ioctl(deviceID, EVIOCGABS(j), &absinfo);
101 //We cannot actually change these values :|
102 //absinfo.minimum = JoyStick::MIN_AXIS;
103 //absinfo.maximum = JoyStick::MAX_AXIS;
104 //ioctl(deviceID, EVIOCSABS(j), &absinfo);
105 }
106 }
107 }
108 }
109 else if(i == EV_REL)
110 {
111 unsigned char rel_bits[1 + REL_MAX/8/sizeof(unsigned char)];
112 memset( rel_bits, 0, sizeof(rel_bits) );
113
114 #ifdef OIS_LINUX_JOY_DEBUG
115 cout << "EventUtils::getComponentInfo(" << deviceID
116 << ") : Reading device relative axis features" << endl;
117 #endif
118
119 if (ioctl(deviceID, EVIOCGBIT(i, sizeof(rel_bits)), rel_bits) == -1)
120 OIS_EXCEPT( E_General, "Could not read device relative axis features");
121
122 for (int j = 0; j < REL_MAX; j++)
123 {
124 if( isBitSet(rel_bits, j) )
125 {
126 components.relAxes.push_back(j);
127 }
128 }
129 }
130 else if(i == EV_KEY)
131 {
132 unsigned char key_bits[1 + KEY_MAX/8/sizeof(unsigned char)];
133 memset( key_bits, 0, sizeof(key_bits) );
134
135 #ifdef OIS_LINUX_JOY_DEBUG
136 cout << "EventUtils::getComponentInfo(" << deviceID
137 << ") : Reading device buttons features" << endl;
138 #endif
139
140 if (ioctl(deviceID, EVIOCGBIT(i, sizeof(key_bits)), key_bits) == -1)
141 OIS_EXCEPT( E_General, "Could not read device buttons features");
142
143 for (int j = 0; j < KEY_MAX; j++)
144 {
145 if( isBitSet(key_bits, j) )
146 {
147 components.buttons.push_back(j);
148 }
149 }
150 }
151 }
152 }
153
154 return components;
155 }
156
157 //-----------------------------------------------------------------------------//
isJoyStick(int deviceID,JoyStickInfo & js)158 bool EventUtils::isJoyStick( int deviceID, JoyStickInfo &js )
159 {
160 if( deviceID == -1 )
161 OIS_EXCEPT( E_General, "Error with File Descriptor" );
162
163 DeviceComponentInfo info = getComponentInfo( deviceID );
164
165 int buttons = 0;
166 bool joyButtonFound = false;
167 js.button_map.clear();
168
169 #ifdef OIS_LINUX_JOY_DEBUG
170 cout << endl << "Displaying ButtonMapping Status:" << endl;
171 #endif
172 for(vector<int>::iterator i = info.buttons.begin(), e = info.buttons.end(); i != e; ++i )
173 {
174 //Check to ensure we find at least one joy only button
175 if( (*i >= BTN_JOYSTICK && *i < BTN_GAMEPAD)
176 || (*i >= BTN_GAMEPAD && *i < BTN_DIGI)
177 || (*i >= BTN_WHEEL && *i < KEY_OK) )
178 joyButtonFound = true;
179
180 js.button_map[*i] = buttons++;
181
182 #ifdef OIS_LINUX_JOY_DEBUG
183 cout << "Button Mapping ID (hex): " << hex << *i
184 << " OIS Button Num: " << dec << buttons-1 << endl;
185 #endif
186 }
187 #ifdef OIS_LINUX_JOY_DEBUG
188 cout << endl;
189 #endif
190
191 //Joy Buttons found, so it must be a joystick or pad
192 if( joyButtonFound )
193 {
194 js.joyFileD = deviceID;
195 js.vendor = getName(deviceID);
196 js.buttons = buttons;
197 js.axes = info.relAxes.size() + info.absAxes.size();
198 js.hats = info.hats.size();
199 #ifdef OIS_LINUX_JOY_DEBUG
200 cout << endl << "Device name:" << js.vendor << endl;
201 cout << "Device unique Id:" << getUniqueId(deviceID) << endl;
202 cout << "Device physical location:" << getPhysicalLocation(deviceID) << endl;
203 #endif
204
205 //Map the Axes
206 #ifdef OIS_LINUX_JOY_DEBUG
207 cout << endl << "Displaying AxisMapping Status:" << endl;
208 #endif
209 int axes = 0;
210 for(vector<int>::iterator i = info.absAxes.begin(), e = info.absAxes.end(); i != e; ++i )
211 {
212 js.axis_map[*i] = axes;
213
214 #ifdef OIS_LINUX_JOY_DEBUG
215 cout << "EventUtils::isJoyStick(" << deviceID
216 << ") : Reading device absolute axis #" << *i << " features" << endl;
217 #endif
218
219 input_absinfo absinfo;
220 if (ioctl(deviceID, EVIOCGABS(*i), &absinfo) == -1)
221 OIS_EXCEPT( E_General, "Could not read device absolute axis features");
222 js.axis_range[axes] = Range(absinfo.minimum, absinfo.maximum);
223
224 #ifdef OIS_LINUX_JOY_DEBUG
225 cout << "Axis Mapping ID (hex): " << hex << *i
226 << " OIS Axis Num: " << dec << axes << endl;
227 #endif
228
229 ++axes;
230 }
231 }
232
233 return joyButtonFound;
234 }
235
236 //-----------------------------------------------------------------------------//
getName(int deviceID)237 string EventUtils::getName( int deviceID )
238 {
239 #ifdef OIS_LINUX_JOY_DEBUG
240 cout << "EventUtils::getName(" << deviceID
241 << ") : Reading device name" << endl;
242 #endif
243
244 char name[OIS_DEVICE_NAME];
245 if (ioctl(deviceID, EVIOCGNAME(OIS_DEVICE_NAME), name) == -1)
246 OIS_EXCEPT( E_General, "Could not read device name");
247 return string(name);
248 }
249
250 //-----------------------------------------------------------------------------//
getUniqueId(int deviceID)251 string EventUtils::getUniqueId( int deviceID )
252 {
253 #ifdef OIS_LINUX_JOY_DEBUG
254 cout << "EventUtils::getUniqueId(" << deviceID
255 << ") : Reading device unique Id" << endl;
256 #endif
257
258 #define OIS_DEVICE_UNIQUE_ID 128
259 char uId[OIS_DEVICE_UNIQUE_ID];
260 if (ioctl(deviceID, EVIOCGUNIQ(OIS_DEVICE_UNIQUE_ID), uId) == -1)
261 OIS_EXCEPT( E_General, "Could not read device unique Id");
262 return string(uId);
263 }
264
265 //-----------------------------------------------------------------------------//
getPhysicalLocation(int deviceID)266 string EventUtils::getPhysicalLocation( int deviceID )
267 {
268 #ifdef OIS_LINUX_JOY_DEBUG
269 cout << "EventUtils::getPhysicalLocation(" << deviceID
270 << ") : Reading device physical location" << endl;
271 #endif
272
273 #define OIS_DEVICE_PHYSICAL_LOCATION 128
274 char physLoc[OIS_DEVICE_PHYSICAL_LOCATION];
275 if (ioctl(deviceID, EVIOCGPHYS(OIS_DEVICE_PHYSICAL_LOCATION), physLoc) == -1)
276 OIS_EXCEPT( E_General, "Could not read device physical location");
277 return string(physLoc);
278 }
279
280 //-----------------------------------------------------------------------------//
enumerateForceFeedback(int deviceID,LinuxForceFeedback ** ff)281 void EventUtils::enumerateForceFeedback( int deviceID, LinuxForceFeedback** ff )
282 {
283 //Linux Event to OIS Event Mappings
284 map<int, Effect::EType> typeMap;
285 typeMap[FF_CONSTANT] = Effect::Constant;
286 typeMap[FF_RAMP] = Effect::Ramp;
287 typeMap[FF_SPRING] = Effect::Spring;
288 typeMap[FF_FRICTION] = Effect::Friction;
289 typeMap[FF_SQUARE] = Effect::Square;
290 typeMap[FF_TRIANGLE] = Effect::Triangle;
291 typeMap[FF_SINE] = Effect::Sine;
292 typeMap[FF_SAW_UP] = Effect::SawToothUp;
293 typeMap[FF_SAW_DOWN] = Effect::SawToothDown;
294 typeMap[FF_DAMPER] = Effect::Damper;
295 typeMap[FF_INERTIA] = Effect::Inertia;
296 typeMap[FF_CUSTOM] = Effect::Custom;
297
298 map<int, Effect::EForce> forceMap;
299 forceMap[FF_CONSTANT] = Effect::ConstantForce;
300 forceMap[FF_RAMP] = Effect::RampForce;
301 forceMap[FF_SPRING] = Effect::ConditionalForce;
302 forceMap[FF_FRICTION] = Effect::ConditionalForce;
303 forceMap[FF_SQUARE] = Effect::PeriodicForce;
304 forceMap[FF_TRIANGLE] = Effect::PeriodicForce;
305 forceMap[FF_SINE] = Effect::PeriodicForce;
306 forceMap[FF_SAW_UP] = Effect::PeriodicForce;
307 forceMap[FF_SAW_DOWN] = Effect::PeriodicForce;
308 forceMap[FF_DAMPER] = Effect::ConditionalForce;
309 forceMap[FF_INERTIA] = Effect::ConditionalForce;
310 forceMap[FF_CUSTOM] = Effect::CustomForce;
311
312 //Remove any previously existing memory and create fresh
313 removeForceFeedback( ff );
314 *ff = new LinuxForceFeedback(deviceID);
315
316 //Read overall force feedback features
317 unsigned char ff_bits[1 + FF_MAX/8/sizeof(unsigned char)];
318 memset(ff_bits, 0, sizeof(ff_bits));
319
320 #ifdef OIS_LINUX_JOY_DEBUG
321 cout << "EventUtils::enumerateForceFeedback(" << deviceID
322 << ") : Reading device force feedback features" << endl;
323 #endif
324
325 if (ioctl(deviceID, EVIOCGBIT(EV_FF, sizeof(ff_bits)), ff_bits) == -1)
326 OIS_EXCEPT( E_General, "Could not read device force feedback features");
327
328
329 #ifdef OIS_LINUX_JOY_DEBUG
330 cout << "FF bits: " << hex;
331 for (int i = 0; i < sizeof(ff_bits); i++)
332 cout << (int)ff_bits[i];
333 cout << endl << dec;
334 #endif
335
336 //FF Axes
337 //if( isBitSet(ff_bits, ABS_X) ) //X Axis
338 //if( isBitSet(ff_bits, ABS_Y) ) //Y Axis
339 //if( isBitSet(ff_bits, ABS_WHEEL) ) //Wheel
340
341 //FF Effects
342 for( int effect = FF_EFFECT_MIN; effect <= FF_WAVEFORM_MAX; effect++ )
343 {
344 // The RUMBLE force type is ignored, as periodic force one is more powerfull.
345 // The PERIODIC force type is processed later, for each associated periodic effect type.
346 if (effect == FF_RUMBLE || effect == FF_PERIODIC)
347 continue;
348
349 if(isBitSet(ff_bits, effect))
350 {
351 #ifdef OIS_LINUX_JOY_DEBUG
352 cout << " Effect Type: " << Effect::getEffectTypeName(typeMap[effect]) << endl;
353 #endif
354
355 (*ff)->_addEffectTypes( forceMap[effect], typeMap[effect] );
356 }
357 }
358
359 //FF device properties
360 if (isBitSet(ff_bits, FF_GAIN))
361 (*ff)->_setGainSupport(true);
362
363 if (isBitSet(ff_bits, FF_AUTOCENTER))
364 (*ff)->_setAutoCenterSupport(true);
365
366 //Check to see if any effects were added, else destroy the pointer
367 const ForceFeedback::SupportedEffectList &list = (*ff)->getSupportedEffects();
368 if( list.size() == 0 )
369 removeForceFeedback( ff );
370 }
371
372 //-----------------------------------------------------------------------------//
removeForceFeedback(LinuxForceFeedback ** ff)373 void EventUtils::removeForceFeedback( LinuxForceFeedback** ff )
374 {
375 delete *ff;
376 *ff = 0;
377 }
378