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