• 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 "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