Home | History | Annotate | Download | only in linux
      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 
     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 //-----------------------------------------------------------------------------//
     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 //-----------------------------------------------------------------------------//
    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 //-----------------------------------------------------------------------------//
    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 //-----------------------------------------------------------------------------//
    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 //-----------------------------------------------------------------------------//
    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 //-----------------------------------------------------------------------------//
    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 //-----------------------------------------------------------------------------//
    373 void EventUtils::removeForceFeedback( LinuxForceFeedback** ff )
    374 {
    375 	delete *ff;
    376 	*ff = 0;
    377 }
    378