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 "OISConfig.h"
24
25 #include "linux/LinuxJoyStickEvents.h"
26 #include "linux/LinuxInputManager.h"
27 #include "linux/LinuxForceFeedback.h"
28 #include "linux/EventHelpers.h"
29
30 #include "OISEvents.h"
31 #include "OISException.h"
32
33 #include <fcntl.h> //Needed to Open a file descriptor
34 #include <cassert>
35 #include <linux/input.h>
36
37
38 #include <sstream>
39 # include <iostream>
40 using namespace std;
41
42 using namespace OIS;
43
44 //#define OIS_LINUX_JOY_DEBUG
45
46 //-------------------------------------------------------------------//
LinuxJoyStick(InputManager * creator,bool buffered,const JoyStickInfo & js)47 LinuxJoyStick::LinuxJoyStick(InputManager* creator, bool buffered, const JoyStickInfo& js)
48 : JoyStick(js.vendor, buffered, js.devId, creator)
49 {
50 mJoyStick = js.joyFileD;
51
52 mState.mAxes.clear();
53 mState.mAxes.resize(js.axes);
54 mState.mButtons.clear();
55 mState.mButtons.resize(js.buttons);
56
57 mPOVs = js.hats;
58
59 mButtonMap = js.button_map;
60 mAxisMap = js.axis_map;
61 mRanges = js.axis_range;
62
63 ff_effect = 0;
64 }
65
66 //-------------------------------------------------------------------//
~LinuxJoyStick()67 LinuxJoyStick::~LinuxJoyStick()
68 {
69 EventUtils::removeForceFeedback( &ff_effect );
70 }
71
72 //-------------------------------------------------------------------//
_initialize()73 void LinuxJoyStick::_initialize()
74 {
75 //Clear old joy state
76 mState.mAxes.resize(mAxisMap.size());
77 mState.clear();
78
79 //This will create and new us a force feedback structure if it exists
80 EventUtils::enumerateForceFeedback( mJoyStick, &ff_effect );
81
82 if( mJoyStick == -1 )
83 OIS_EXCEPT(E_InputDeviceNonExistant, "LinuxJoyStick::_initialize() >> JoyStick Not Found!");
84 }
85
86 //-------------------------------------------------------------------//
capture()87 void LinuxJoyStick::capture()
88 {
89 static const short POV_MASK[8] = {0,0,1,1,2,2,3,3};
90
91 //Used to determine if an axis has been changed and needs an event
92 bool axisMoved[32] = {false, false, false, false, false, false, false, false, false, false, false, false, false,
93 false, false, false, false, false, false, false, false, false, false, false, false, false,
94 false, false, false, false, false, false};
95
96 //We are in non blocking mode - we just read once, and try to fill up buffer
97 input_event js[JOY_BUFFERSIZE];
98 while(true)
99 {
100 int ret = read(mJoyStick, &js, sizeof(struct input_event) * JOY_BUFFERSIZE);
101 if( ret < 0 )
102 break;
103
104 //Determine how many whole events re read up
105 ret /= sizeof(struct input_event);
106 for(int i = 0; i < ret; ++i)
107 {
108 switch(js[i].type)
109 {
110 case EV_KEY: //Button
111 {
112 int button = mButtonMap[js[i].code];
113
114 #ifdef OIS_LINUX_JOY_DEBUG
115 cout << "\nButton Code: " << js[i].code << ", OIS Value: " << button << endl;
116 #endif
117
118 //Check to see whether push or released event...
119 if(js[i].value)
120 {
121 mState.mButtons[button] = true;
122 if( mBuffered && mListener )
123 if(!mListener->buttonPressed(JoyStickEvent(this,mState), button)) return;
124 }
125 else
126 {
127 mState.mButtons[button] = false;
128 if( mBuffered && mListener )
129 if(!mListener->buttonReleased(JoyStickEvent(this,mState), button)) return;
130 }
131 break;
132 }
133
134 case EV_ABS: //Absolute Axis
135 {
136 //A Stick (BrakeDefine is the highest possible Axis)
137 if( js[i].code <= ABS_BRAKE )
138 {
139 int axis = mAxisMap[js[i].code];
140 assert( axis < 32 && "Too many axes (Max supported is 32). Report this to OIS forums!" );
141
142 axisMoved[axis] = true;
143
144 //check for rescaling:
145 if( mRanges[axis].min == JoyStick::MIN_AXIS && mRanges[axis].max != JoyStick::MAX_AXIS )
146 { //Scale is perfect
147 mState.mAxes[axis].abs = js[i].value;
148 }
149 else
150 { //Rescale
151 float proportion = (float)(js[i].value-mRanges[axis].max)/(float)(mRanges[axis].min-mRanges[axis].max);
152 mState.mAxes[axis].abs = (int)(32767.0f - (65535.0f * proportion));
153 }
154 }
155 else if( js[i].code <= ABS_HAT3Y ) //A POV - Max four POVs allowed
156 {
157 //Normalise the POV to between 0-7
158 //Even is X Axis, Odd is Y Axis
159 unsigned char LinuxPovNumber = js[i].code - 16;
160 short OIS_POVIndex = POV_MASK[LinuxPovNumber];
161
162 //Handle X Axis first (Even) (left right)
163 if((LinuxPovNumber & 0x0001) == 0)
164 {
165 //Why do this? Because, we use a bit field, and when this axis is east,
166 //it can't possibly be west too. So clear out the two X axes, then refil
167 //it in with the new direction bit.
168 //Clear the East/West Bit Flags first
169 mState.mPOV[OIS_POVIndex].direction &= 0x11110011;
170 if( js[i].value == -1 ) //Left
171 mState.mPOV[OIS_POVIndex].direction |= Pov::West;
172 else if( js[i].value == 1 ) //Right
173 mState.mPOV[OIS_POVIndex].direction |= Pov::East;
174 }
175 //Handle Y Axis (Odd) (up down)
176 else
177 {
178 //Clear the North/South Bit Flags first
179 mState.mPOV[OIS_POVIndex].direction &= 0x11111100;
180 if( js[i].value == -1 ) //Up
181 mState.mPOV[OIS_POVIndex].direction |= Pov::North;
182 else if( js[i].value == 1 ) //Down
183 mState.mPOV[OIS_POVIndex].direction |= Pov::South;
184 }
185
186 if( mBuffered && mListener )
187 if( mListener->povMoved( JoyStickEvent(this,mState), OIS_POVIndex) == false )
188 return;
189 }
190 break;
191 }
192
193
194 case EV_REL: //Relative Axes (Do any joystick actually have a relative axis?)
195 #ifdef OIS_LINUX_JOY_DEBUG
196 cout << "\nWarning: Relatives axes not supported yet" << endl;
197 #endif
198 break;
199 default: break;
200 }
201 }
202 }
203
204 //All axes and POVs are combined into one movement per pair per captured frame
205 if( mBuffered && mListener )
206 {
207 for( int i = 0; i < 32; ++i )
208 if( axisMoved[i] )
209 if( mListener->axisMoved( JoyStickEvent(this,mState), i) == false )
210 return;
211 }
212 }
213
214 //-------------------------------------------------------------------//
setBuffered(bool buffered)215 void LinuxJoyStick::setBuffered(bool buffered)
216 {
217 if( buffered != mBuffered )
218 {
219 mBuffered = buffered;
220 _initialize();
221 }
222 }
223
224 //-------------------------------------------------------------------//
_getJoyInfo()225 JoyStickInfo LinuxJoyStick::_getJoyInfo()
226 {
227 JoyStickInfo js;
228
229 js.devId = mDevID;
230 js.joyFileD = mJoyStick;
231 js.vendor = mVendor;
232 js.axes = (int)mState.mAxes.size();
233 js.buttons = (int)mState.mButtons.size();
234 js.hats = mPOVs;
235 js.button_map = mButtonMap;
236 js.axis_map = mAxisMap;
237 js.axis_range = mRanges;
238
239 return js;
240 }
241
242 //-------------------------------------------------------------------//
_scanJoys()243 JoyStickInfoList LinuxJoyStick::_scanJoys()
244 {
245 JoyStickInfoList joys;
246
247 //Search through all of the event devices.. and identify which ones are joysticks
248 //xxx move this to InputManager, as it can also scan all other events
249 for(int i = 0; i < 64; ++i )
250 {
251 stringstream s;
252 s << "/dev/input/event" << i;
253 int fd = open( s.str().c_str(), O_RDWR |O_NONBLOCK );
254 if(fd == -1)
255 continue;
256
257 #ifdef OIS_LINUX_JOY_DEBUG
258 cout << "Opening " << s.str() << "..." << endl;
259 #endif
260 try
261 {
262 JoyStickInfo js;
263 if( EventUtils::isJoyStick(fd, js) )
264 {
265 joys.push_back(js);
266 #ifdef OIS_LINUX_JOY_DEBUG
267 cout << "=> Joystick added to list." << endl;
268 #endif
269 }
270 else
271 {
272 #ifdef OIS_LINUX_JOY_DEBUG
273 cout << "=> Not a joystick." << endl;
274 #endif
275 close(fd);
276 }
277 }
278 catch(...)
279 {
280 #ifdef OIS_LINUX_JOY_DEBUG
281 cout << "Exception caught!!" << endl;
282 #endif
283 close(fd);
284 }
285 }
286
287 return joys;
288 }
289
290 //-------------------------------------------------------------------//
_clearJoys(JoyStickInfoList & joys)291 void LinuxJoyStick::_clearJoys(JoyStickInfoList &joys)
292 {
293 for(JoyStickInfoList::iterator i = joys.begin(); i != joys.end(); ++i)
294 close(i->joyFileD);
295 joys.clear();
296 }
297
298 //-------------------------------------------------------------------//
queryInterface(Interface::IType type)299 Interface* LinuxJoyStick::queryInterface(Interface::IType type)
300 {
301 if( ff_effect && type == Interface::ForceFeedback )
302 return ff_effect;
303
304 return 0;
305 }
306