• 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 
24 #include "mac/MacJoyStick.h"
25 #include "mac/MacHIDManager.h"
26 #include "mac/MacInputManager.h"
27 #include "OISEvents.h"
28 #include "OISException.h"
29 
30 #include <cassert>
31 
32 using namespace OIS;
33 
34 //--------------------------------------------------------------------------------------------------//
MacJoyStick(const std::string & vendor,bool buffered,HidInfo * info,InputManager * creator,int devID)35 MacJoyStick::MacJoyStick(const std::string &vendor, bool buffered, HidInfo* info, InputManager* creator, int devID) :
36 JoyStick(vendor, buffered, devID, creator), mInfo(info)
37 {
38 
39 }
40 
41 //--------------------------------------------------------------------------------------------------//
~MacJoyStick()42 MacJoyStick::~MacJoyStick()
43 {
44 	//TODO: check if the queue has been started first?
45 	//(*mQueue)->stop(mQueue);
46 	(*mQueue)->dispose(mQueue);
47 	(*mQueue)->Release(mQueue);
48 
49 
50 	//TODO: check if the interface has been opened first?
51 	(*mInfo->interface)->close(mInfo->interface);
52 	(*mInfo->interface)->Release(mInfo->interface);
53 }
54 
55 //--------------------------------------------------------------------------------------------------//
_initialize()56 void MacJoyStick::_initialize()
57 {
58 	assert(mInfo && "Given HidInfo invalid");
59 	assert(mInfo->interface && "Joystick interface invalid");
60 
61 	//TODO: Is this necessary?
62 	//Clear old state
63 	mState.mAxes.clear();
64 
65 	if ((*mInfo->interface)->open(mInfo->interface, 0) != KERN_SUCCESS)
66 		OIS_EXCEPT(E_General, "MacJoyStick::_initialize() >> Could not initialize joy device!");
67 
68 	mState.clear();
69 
70 	_enumerateCookies();
71 
72 	mState.mButtons.resize(mInfo->numButtons);
73 	mState.mAxes.resize(mInfo->numAxes);
74 
75 	mQueue = _createQueue();
76 }
77 
78 class FindAxisCookie : public std::unary_function<std::pair<IOHIDElementCookie, AxisInfo>&, bool>
79 {
80 public:
FindAxisCookie(IOHIDElementCookie cookie)81 	FindAxisCookie(IOHIDElementCookie cookie) : m_Cookie(cookie) {}
operator ()(const std::pair<IOHIDElementCookie,AxisInfo> & pair) const82 	bool operator()(const std::pair<IOHIDElementCookie, AxisInfo>& pair) const
83 	{
84 		return pair.first == m_Cookie;
85 	}
86 private:
87 	IOHIDElementCookie m_Cookie;
88 };
89 
90 //--------------------------------------------------------------------------------------------------//
capture()91 void MacJoyStick::capture()
92 {
93 	assert(mQueue && "Queue must be initialized before calling MacJoyStick::capture()");
94 
95 	AbsoluteTime zeroTime = {0,0};
96 
97 	IOHIDEventStruct event;
98 	IOReturn result = (*mQueue)->getNextEvent(mQueue, &event, zeroTime, 0);
99 	while(result == kIOReturnSuccess)
100 	{
101 		switch(event.type)
102 		{
103 			case kIOHIDElementTypeInput_Button:
104 			{
105 				std::vector<IOHIDElementCookie>::iterator buttonIt = std::find(mCookies.buttonCookies.begin(), mCookies.buttonCookies.end(), event.elementCookie);
106 				int button = std::distance(mCookies.buttonCookies.begin(), buttonIt);
107 				mState.mButtons[button] = (event.value == 1);
108 
109 				if(mBuffered && mListener)
110 				{
111 					if(event.value == 1)
112 						mListener->buttonPressed(JoyStickEvent(this, mState), button);
113 					else if(event.value == 0)
114 						mListener->buttonReleased(JoyStickEvent(this, mState), button);
115 				}
116 				break;
117 			}
118 			case kIOHIDElementTypeInput_Misc:
119 				//TODO: It's an axis! - kind of - for gamepads - or should this be a pov?
120 			case kIOHIDElementTypeInput_Axis:
121 				std::map<IOHIDElementCookie, AxisInfo>::iterator axisIt = std::find_if(mCookies.axisCookies.begin(), mCookies.axisCookies.end(), FindAxisCookie(event.elementCookie));
122 				int axis = std::distance(mCookies.axisCookies.begin(), axisIt);
123 
124 				//Copied from LinuxJoyStickEvents.cpp, line 149
125 				const AxisInfo& axisInfo = axisIt->second;
126 				float proportion = (float) (event.value - axisInfo.max) / (float) (axisInfo.min - axisInfo.max);
127 				mState.mAxes[axis].abs = -JoyStick::MIN_AXIS - (JoyStick::MAX_AXIS * 2 * proportion);
128 
129 				if(mBuffered && mListener) mListener->axisMoved(JoyStickEvent(this, mState), axis);
130 				break;
131 		}
132 
133 		result = (*mQueue)->getNextEvent(mQueue, &event, zeroTime, 0);
134 	}
135 }
136 
137 //--------------------------------------------------------------------------------------------------//
setBuffered(bool buffered)138 void MacJoyStick::setBuffered(bool buffered)
139 {
140 	mBuffered = buffered;
141 }
142 
143 //--------------------------------------------------------------------------------------------------//
queryInterface(Interface::IType type)144 Interface* MacJoyStick::queryInterface(Interface::IType type)
145 {
146 	//Thought about using covariant return type here.. however,
147 	//some devices may allow LED light changing, or other interface stuff
148 
149 	//f( ff_device && type == Interface::ForceFeedback )
150 	//return ff_device;
151 	//else
152 	return 0;
153 }
154 
155 //--------------------------------------------------------------------------------------------------//
_enumerateCookies()156 void MacJoyStick::_enumerateCookies()
157 {
158 	assert(mInfo && "Given HidInfo invalid");
159 	assert(mInfo->interface && "Joystick interface invalid");
160 
161 	CFTypeRef                               object;
162 	long                                    number;
163 	IOHIDElementCookie                      cookie;
164 	long                                    usage;
165 	long                                    usagePage;
166 	int										min;
167 	int										max;
168 
169 	CFDictionaryRef                         element;
170 
171 	// Copy all elements, since we're grabbing most of the elements
172 	// for this device anyway, and thus, it's faster to iterate them
173 	// ourselves. When grabbing only one or two elements, a matching
174 	// dictionary should be passed in here instead of NULL.
175 	CFArrayRef elements;
176 	IOReturn success = reinterpret_cast<IOHIDDeviceInterface122*>(*mInfo->interface)->copyMatchingElements(mInfo->interface, NULL, &elements);
177 
178 	if (success == kIOReturnSuccess)
179 	{
180 		const CFIndex numOfElements = CFArrayGetCount(elements);
181 		for (CFIndex i = 0; i < numOfElements; ++i)
182 		{
183 			element = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(elements, i));
184 
185 			//Get cookie
186 			object = (CFDictionaryGetValue(element,
187 										   CFSTR(kIOHIDElementCookieKey)));
188 			if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
189 				continue;
190 			if(!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,
191 								 &number))
192 				continue;
193 			cookie = (IOHIDElementCookie) number;
194 
195 			//Get usage
196 			object = CFDictionaryGetValue(element,
197 										  CFSTR(kIOHIDElementUsageKey));
198 			if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
199 				continue;
200 			if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,
201 								  &number))
202 				continue;
203 			usage = number;
204 
205 			//Get min
206 			object = CFDictionaryGetValue(element,
207 										  CFSTR(kIOHIDElementMinKey)); // kIOHIDElementMinKey or kIOHIDElementScaledMinKey?, no idea ...
208 			if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
209 				continue;
210 			if (!CFNumberGetValue((CFNumberRef) object, kCFNumberIntType,
211 								  &number))
212 				continue;
213 			min = number;
214 
215 			//Get max
216 			object = CFDictionaryGetValue(element,
217 										  CFSTR(kIOHIDElementMaxKey)); // kIOHIDElementMaxKey or kIOHIDElementScaledMaxKey?, no idea ...
218 			if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
219 				continue;
220 			if (!CFNumberGetValue((CFNumberRef) object, kCFNumberIntType,
221 								  &number))
222 				continue;
223 			max = number;
224 
225 			//Get usage page
226 			object = CFDictionaryGetValue(element,
227 										  CFSTR(kIOHIDElementUsagePageKey));
228 
229 			if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
230 				continue;
231 
232 			if (!CFNumberGetValue((CFNumberRef) object, kCFNumberLongType,
233 								  &number))
234 				continue;
235 
236 			usagePage = number;
237 			switch(usagePage)
238 			{
239 				case kHIDPage_GenericDesktop:
240 					switch(usage)
241 				{
242 					case kHIDUsage_GD_Pointer:
243 						break;
244 					case kHIDUsage_GD_X:
245 					case kHIDUsage_GD_Y:
246 					case kHIDUsage_GD_Z:
247 					case kHIDUsage_GD_Rx:
248 					case kHIDUsage_GD_Ry:
249 					case kHIDUsage_GD_Rz:
250 						mCookies.axisCookies.insert(std::make_pair(cookie, AxisInfo(min, max)));
251 						break;
252 					case kHIDUsage_GD_Slider:
253 					case kHIDUsage_GD_Dial:
254 					case kHIDUsage_GD_Wheel:
255 						break;
256 					case kHIDUsage_GD_Hatswitch:
257 						break;
258 				}
259 					break;
260 				case kHIDPage_Button:
261 					mCookies.buttonCookies.push_back(cookie);
262 					break;
263 			}
264 		}
265 
266 		mInfo->numButtons = mCookies.buttonCookies.size();
267 		mInfo->numAxes = mCookies.axisCookies.size();
268 
269 	}
270 	else
271 	{
272 		OIS_EXCEPT(E_General, "JoyStick elements could not be copied: copyMatchingElements failed with error: " + success);
273 	}
274 
275 }
276 
277 //--------------------------------------------------------------------------------------------------//
_createQueue(unsigned int depth)278 IOHIDQueueInterface** MacJoyStick::_createQueue(unsigned int depth)
279 {
280 	assert(mInfo && "Given HidInfo invalid");
281 	assert(mInfo->interface && "Joystick interface invalid");
282 
283 	IOHIDQueueInterface** queue = (*mInfo->interface)->allocQueue(mInfo->interface);
284 
285 	if (queue)
286 	{
287 		//create the queue
288 		IOReturn result = (*queue)->create(queue, 0, depth);
289 
290 		if(result == kIOReturnSuccess)
291 		{
292 			//add elements to the queue
293 			std::map<IOHIDElementCookie, AxisInfo>::iterator axisIt = mCookies.axisCookies.begin();
294 			for(; axisIt != mCookies.axisCookies.end(); ++axisIt)
295 			{
296 				result = (*queue)->addElement(queue, axisIt->first, 0);
297 			}
298 
299 			std::vector<IOHIDElementCookie>::iterator buttonIt = mCookies.buttonCookies.begin();
300 			for(; buttonIt != mCookies.buttonCookies.end(); ++buttonIt)
301 			{
302 				result = (*queue)->addElement(queue, (*buttonIt), 0);
303 			}
304 
305 			//start data delivery to queue
306 			result = (*queue)->start(queue);
307 			if(result == kIOReturnSuccess)
308 			{
309 				return queue;
310 			}
311 			else
312 			{
313 				OIS_EXCEPT(E_General, "Queue could not be started.");
314 			}
315 		}
316 		else
317 		{
318 			OIS_EXCEPT(E_General, "Queue could not be created.");
319 		}
320 	}
321 	else
322 	{
323 		OIS_EXCEPT(E_General, "Queue allocation failed.");
324 	}
325 }
326