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