• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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