| 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 |   using namespace std; | 
|---|
| 37 | #endif | 
|---|
| 38 |  | 
|---|
| 39 | using namespace OIS; | 
|---|
| 40 |  | 
|---|
| 41 | class DeviceComponentInfo | 
|---|
| 42 | { | 
|---|
| 43 | public: | 
|---|
| 44 |         std::vector<int> buttons, relAxes, absAxes, hats; | 
|---|
| 45 | }; | 
|---|
| 46 |  | 
|---|
| 47 | bool inline isBitSet(unsigned long bits[], unsigned int bit) | 
|---|
| 48 | { | 
|---|
| 49 |         return (bits[bit/(sizeof(long)*8)] >> ((bit)%(sizeof(long)*8))) & 1; | 
|---|
| 50 | } | 
|---|
| 51 | //-----------------------------------------------------------------------------// | 
|---|
| 52 | DeviceComponentInfo getComponentInfo( int deviceID ) | 
|---|
| 53 | { | 
|---|
| 54 |         unsigned long info[2][((KEY_MAX-1)/(sizeof(long)*8)) +1]; | 
|---|
| 55 |         memset( info, 0, sizeof(info) ); | 
|---|
| 56 |  | 
|---|
| 57 |         DeviceComponentInfo components; | 
|---|
| 58 |  | 
|---|
| 59 |         //Read "all" (hence 0) components of the device - read into first entry | 
|---|
| 60 |         ioctl(deviceID, EVIOCGBIT(0, EV_MAX), info[0]); | 
|---|
| 61 |  | 
|---|
| 62 |         for (int i = 0; i < EV_MAX; i++) | 
|---|
| 63 |         { | 
|---|
| 64 |                 if( isBitSet(info[0], i) ) | 
|---|
| 65 |                 { | 
|---|
| 66 |                         memset( info[1], 0, sizeof(info) / 2 ); | 
|---|
| 67 |                         ioctl(deviceID, EVIOCGBIT(i, KEY_MAX), info[1]); | 
|---|
| 68 |                         for (int j = 0; j < KEY_MAX; j++) | 
|---|
| 69 |                         { | 
|---|
| 70 |                                 if( isBitSet(info[1], j) ) | 
|---|
| 71 |                                 { | 
|---|
| 72 |                                         if(i == EV_ABS) | 
|---|
| 73 |                                         { | 
|---|
| 74 |                                                 //input_absinfo abInfo; | 
|---|
| 75 |                                                 //ioctl( fd, EVIOCGABS(j), abInfo ); | 
|---|
| 76 |                                                 if( j >= ABS_HAT0X && j <= ABS_HAT3Y ) | 
|---|
| 77 |                                                 { | 
|---|
| 78 |                                                         components.hats.push_back(j); | 
|---|
| 79 |                                                 } | 
|---|
| 80 |                                                 else | 
|---|
| 81 |                                                 { | 
|---|
| 82 |                                                         components.absAxes.push_back(j); | 
|---|
| 83 |                                                         //input_absinfo absinfo; | 
|---|
| 84 |                                                         //ioctl(deviceID, EVIOCGABS(j), &absinfo); | 
|---|
| 85 |                                                         //We cannot actually change these values :| | 
|---|
| 86 |                                                         //absinfo.minimum = JoyStick::MIN_AXIS; | 
|---|
| 87 |                                                         //absinfo.maximum = JoyStick::MAX_AXIS; | 
|---|
| 88 |                                                         //ioctl(deviceID, EVIOCSABS(j), &absinfo); | 
|---|
| 89 |                                                 } | 
|---|
| 90 |                                         } | 
|---|
| 91 |                                         else if(i == EV_REL) | 
|---|
| 92 |                                         { | 
|---|
| 93 |                                                 components.relAxes.push_back(j); | 
|---|
| 94 |                                         } | 
|---|
| 95 |                                         else if(i == EV_KEY) | 
|---|
| 96 |                                         { | 
|---|
| 97 |                                                 components.buttons.push_back(j); | 
|---|
| 98 |                                         } | 
|---|
| 99 |                                 } | 
|---|
| 100 |                         } | 
|---|
| 101 |                 } | 
|---|
| 102 |         } | 
|---|
| 103 |  | 
|---|
| 104 |         return components; | 
|---|
| 105 | } | 
|---|
| 106 |  | 
|---|
| 107 | //-----------------------------------------------------------------------------// | 
|---|
| 108 | bool EventUtils::isJoyStick( int deviceID, JoyStickInfo &js ) | 
|---|
| 109 | { | 
|---|
| 110 |         if( deviceID == -1 ) OIS_EXCEPT( E_General, "Error with File Descriptor" ); | 
|---|
| 111 |  | 
|---|
| 112 |         DeviceComponentInfo info = getComponentInfo( deviceID ); | 
|---|
| 113 |  | 
|---|
| 114 |         int buttons = 0; | 
|---|
| 115 |         bool joyButtonFound = false; | 
|---|
| 116 |         js.button_map.clear(); | 
|---|
| 117 |  | 
|---|
| 118 |         #ifdef OIS_LINUX_JOY_DEBUG | 
|---|
| 119 |           cout << "\n\nDisplaying ButtonMapping Status:"; | 
|---|
| 120 |         #endif | 
|---|
| 121 |         for(std::vector<int>::iterator i = info.buttons.begin(), e = info.buttons.end(); i != e; ++i ) | 
|---|
| 122 |         { | 
|---|
| 123 |                 //Check to ensure we find at least one joy only button | 
|---|
| 124 |                 if( (*i >= BTN_JOYSTICK && *i <= BTN_THUMBR) || (*i >= BTN_WHEEL && *i <= BTN_GEAR_UP ) ) | 
|---|
| 125 |                         joyButtonFound = true; | 
|---|
| 126 |  | 
|---|
| 127 |                 js.button_map[*i] = buttons++; | 
|---|
| 128 |  | 
|---|
| 129 |                 #ifdef OIS_LINUX_JOY_DEBUG | 
|---|
| 130 |                   cout << "\nButton Mapping ID (hex): " << hex << *i << " OIS Button Num: " << dec << (buttons-1); | 
|---|
| 131 |                 #endif | 
|---|
| 132 |         } | 
|---|
| 133 |  | 
|---|
| 134 |         //Joy Buttons found, so it must be a joystick or pad | 
|---|
| 135 |         if( joyButtonFound ) | 
|---|
| 136 |         { | 
|---|
| 137 |                 js.joyFileD = deviceID; | 
|---|
| 138 |                 js.vendor = getName(deviceID); | 
|---|
| 139 |                 js.buttons = buttons; | 
|---|
| 140 |                 js.axes = info.relAxes.size() + info.absAxes.size(); | 
|---|
| 141 |                 js.hats = info.hats.size(); | 
|---|
| 142 |  | 
|---|
| 143 |                 //Map the Axes | 
|---|
| 144 |                 #ifdef OIS_LINUX_JOY_DEBUG | 
|---|
| 145 |                   cout << "\n\nDisplaying AxisMapping Status:"; | 
|---|
| 146 |                 #endif | 
|---|
| 147 |                 int axes = 0; | 
|---|
| 148 |                 for(std::vector<int>::iterator i = info.absAxes.begin(), e = info.absAxes.end(); i != e; ++i ) | 
|---|
| 149 |                 { | 
|---|
| 150 |                         js.axis_map[*i] = axes; | 
|---|
| 151 |  | 
|---|
| 152 |                         input_absinfo absinfo; | 
|---|
| 153 |                         ioctl(deviceID, EVIOCGABS(*i), &absinfo); | 
|---|
| 154 |                         js.axis_range[axes] = Range(absinfo.minimum, absinfo.maximum); | 
|---|
| 155 |  | 
|---|
| 156 |                         #ifdef OIS_LINUX_JOY_DEBUG | 
|---|
| 157 |                           cout << "\nAxis Mapping ID (hex): " << hex << *i << " OIS Axis Num: " << dec << axes; | 
|---|
| 158 |                         #endif | 
|---|
| 159 |  | 
|---|
| 160 |                         ++axes; | 
|---|
| 161 |                 } | 
|---|
| 162 |         } | 
|---|
| 163 |  | 
|---|
| 164 |         return joyButtonFound; | 
|---|
| 165 | } | 
|---|
| 166 |  | 
|---|
| 167 | //-----------------------------------------------------------------------------// | 
|---|
| 168 | std::string EventUtils::getName( int deviceID ) | 
|---|
| 169 | { | 
|---|
| 170 |         char name[OIS_DEVICE_NAME]; | 
|---|
| 171 |         ioctl(deviceID, EVIOCGNAME(OIS_DEVICE_NAME), name); | 
|---|
| 172 |         return std::string(name); | 
|---|
| 173 | } | 
|---|
| 174 |  | 
|---|
| 175 | //-----------------------------------------------------------------------------// | 
|---|
| 176 | void EventUtils::enumerateForceFeedback( int deviceID, LinuxForceFeedback** ff ) | 
|---|
| 177 | { | 
|---|
| 178 |         //Linux Event to OIS Event Mappings | 
|---|
| 179 |         std::map<int, Effect::EType> typeMap; | 
|---|
| 180 |         typeMap[FF_CONSTANT] = Effect::Constant; | 
|---|
| 181 |         typeMap[FF_RAMP]     = Effect::Ramp; | 
|---|
| 182 |         typeMap[FF_SPRING]   = Effect::Spring; | 
|---|
| 183 |         typeMap[FF_FRICTION] = Effect::Friction; | 
|---|
| 184 |         typeMap[FF_SQUARE]   = Effect::Square; | 
|---|
| 185 |         typeMap[FF_TRIANGLE] = Effect::Triangle; | 
|---|
| 186 |         typeMap[FF_SINE]     = Effect::Sine; | 
|---|
| 187 |         typeMap[FF_SAW_UP]   = Effect::SawToothUp; | 
|---|
| 188 |         typeMap[FF_SAW_DOWN] = Effect::SawToothDown; | 
|---|
| 189 |         typeMap[FF_DAMPER]   = Effect::Damper; | 
|---|
| 190 |         typeMap[FF_INERTIA]  = Effect::Inertia; | 
|---|
| 191 |         typeMap[FF_CUSTOM]   = Effect::Custom; | 
|---|
| 192 |  | 
|---|
| 193 |         std::map<int, Effect::EForce> forceMap; | 
|---|
| 194 |         forceMap[FF_CONSTANT] = Effect::ConstantForce; | 
|---|
| 195 |         forceMap[FF_RAMP] = Effect::RampForce; | 
|---|
| 196 |         forceMap[FF_PERIODIC] = Effect::PeriodicForce; | 
|---|
| 197 |         forceMap[FF_CUSTOM] = Effect::CustomForce; | 
|---|
| 198 |  | 
|---|
| 199 |         //Remove any previously existing memory and create fresh | 
|---|
| 200 |         removeForceFeedback( ff ); | 
|---|
| 201 |         *ff = new LinuxForceFeedback(); | 
|---|
| 202 |  | 
|---|
| 203 |         unsigned long info[4] = {0,0,0,0}; | 
|---|
| 204 |         unsigned long subinfo[4]= {0,0,0,0}; | 
|---|
| 205 |  | 
|---|
| 206 |         //Read overall force feedback components of the device | 
|---|
| 207 |         ioctl(deviceID, EVIOCGBIT(EV_FF, sizeof(long)*4), info); | 
|---|
| 208 |  | 
|---|
| 209 |         //FF Axes | 
|---|
| 210 |         //if( isBitSet(info, ABS_X) ) //X Axis | 
|---|
| 211 |         //if( isBitSet(info, ABS_Y) ) //Y Axis | 
|---|
| 212 |         //if( isBitSet(info, ABS_WHEEL) ) //Wheel | 
|---|
| 213 |  | 
|---|
| 214 |         //FF Effects | 
|---|
| 215 |         for( int effect = ABS_WHEEL+1; effect < FF_MAX; effect++ ) | 
|---|
| 216 |         { | 
|---|
| 217 |                 if(isBitSet(info, effect)) | 
|---|
| 218 |                 { | 
|---|
| 219 |                         //std::cout << "\tEffect Type: " << effect << std::endl; | 
|---|
| 220 |                         memset(subinfo, 0, sizeof(subinfo)); | 
|---|
| 221 |                         //Read any info about this supported effect | 
|---|
| 222 |                         ioctl(deviceID, EVIOCGBIT(effect, sizeof(long)*4), subinfo); | 
|---|
| 223 |                         for( int force = 0; force < FF_MAX; force++ ) | 
|---|
| 224 |                         { | 
|---|
| 225 |                                 if(isBitSet(subinfo, force)) | 
|---|
| 226 |                                         (*ff)->_addEffectTypes( forceMap[force], typeMap[effect] ); | 
|---|
| 227 |                         } | 
|---|
| 228 |                 } | 
|---|
| 229 |         } | 
|---|
| 230 |  | 
|---|
| 231 |         //Check to see if any effects were added, else destroy the pointer | 
|---|
| 232 |         const ForceFeedback::SupportedEffectList &list = (*ff)->getSupportedEffects(); | 
|---|
| 233 |         if( list.size() == 0 ) | 
|---|
| 234 |                 removeForceFeedback( ff ); | 
|---|
| 235 | } | 
|---|
| 236 |  | 
|---|
| 237 | //-----------------------------------------------------------------------------// | 
|---|
| 238 | void EventUtils::removeForceFeedback( LinuxForceFeedback** ff ) | 
|---|
| 239 | { | 
|---|
| 240 |         delete *ff; | 
|---|
| 241 |         *ff = 0; | 
|---|
| 242 | } | 
|---|