]>
Commit | Line | Data |
---|---|---|
4cb1d3da RN |
1 | ///////////////////////////////////////////////////////////////////////////// |
2 | // Name: joystick.cpp | |
3 | // Purpose: wxJoystick class | |
4 | // Author: Ryan Norton | |
5 | // Modified by: | |
6 | // Created: 2/13/2005 | |
7 | // RCS-ID: $Id$ | |
8 | // Copyright: (c) Ryan Norton | |
9 | // Licence: wxWindows licence | |
10 | ///////////////////////////////////////////////////////////////////////////// | |
11 | ||
4cb1d3da RN |
12 | #include "wx/wxprec.h" |
13 | ||
14 | #ifdef __DARWIN__ | |
15 | ||
16 | #if wxUSE_JOYSTICK | |
17 | ||
18 | #include "wx/event.h" | |
19 | #include "wx/log.h" | |
20 | #include "wx/joystick.h" | |
21 | #include "wx/thread.h" | |
22 | #include "wx/window.h" | |
23 | ||
24 | #include "wx/mac/corefoundation/hid.h" | |
25 | ||
26 | #include <CoreServices/CoreServices.h> | |
27 | #include <mach/mach.h> | |
28 | #include <mach/mach_time.h> | |
29 | #include <unistd.h> | |
30 | ||
31 | enum { | |
65442ab6 | 32 | wxJS_AXIS_X = 40, |
4cb1d3da RN |
33 | wxJS_AXIS_Y, |
34 | wxJS_AXIS_Z, | |
35 | wxJS_AXIS_RUDDER, | |
36 | wxJS_AXIS_U, | |
37 | wxJS_AXIS_V, | |
38 | ||
65442ab6 RN |
39 | wxJS_AXIS_MAX = 255, //32767, |
40 | wxJS_AXIS_MIN = 0, //-32767 | |
4cb1d3da RN |
41 | }; |
42 | ||
43 | class wxHIDJoystick : public wxHIDDevice | |
44 | { | |
45 | public: | |
46 | bool Create(int nWhich); | |
47 | virtual void BuildCookies(wxCFArray& Array); | |
65442ab6 | 48 | void MakeCookies(wxCFArray& Array); |
4cb1d3da | 49 | IOHIDElementCookie* GetCookies() {return m_pCookies;} |
4cb1d3da | 50 | IOHIDQueueInterface** GetQueue() {return m_ppQueue;} |
65442ab6 RN |
51 | |
52 | friend class wxJoystick; | |
4cb1d3da RN |
53 | }; |
54 | ||
55 | ||
56 | bool wxHIDJoystick::Create(int nWhich) | |
57 | { | |
58 | int nJoysticks = GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick); | |
59 | ||
60 | if (nWhich <= nJoysticks) | |
61 | return wxHIDDevice::Create(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick); | |
62 | else | |
63 | nWhich -= nJoysticks; | |
64 | ||
65 | int nGamePads = GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad); | |
66 | ||
67 | if (nWhich <= nGamePads) | |
68 | return wxHIDDevice::Create(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad); | |
69 | else | |
70 | return false; | |
71 | } | |
72 | ||
73 | void wxHIDJoystick::BuildCookies(wxCFArray& Array) | |
74 | { | |
75 | Array = CFDictionaryGetValue((CFDictionaryRef)Array[0], CFSTR(kIOHIDElementKey)); | |
76 | InitCookies(50, true); | |
65442ab6 RN |
77 | |
78 | memset(m_pCookies, 0, sizeof(*m_pCookies) * 50); | |
79 | MakeCookies(Array); | |
80 | ||
81 | // for(int i = 0; i < 50; ++i) | |
82 | // wxPrintf(wxT("\nVAL #%i:[%i]"), i, m_pCookies[i]); | |
83 | }//end buildcookies | |
84 | ||
85 | void wxHIDJoystick::MakeCookies(wxCFArray& Array) | |
86 | { | |
4cb1d3da RN |
87 | int i, |
88 | nUsage, | |
89 | nPage; | |
2a32f173 | 90 | for (i = 0; i < Array.Count(); ++i) |
4cb1d3da | 91 | { |
65442ab6 RN |
92 | const void* ref = CFDictionaryGetValue((CFDictionaryRef)Array[i], CFSTR(kIOHIDElementKey)); |
93 | ||
94 | // wxPrintf(wxT("ELM\n")); | |
95 | if (ref != NULL) | |
96 | { | |
97 | wxCFArray newarray(ref); | |
98 | MakeCookies(newarray); | |
99 | } | |
100 | else | |
101 | { | |
102 | CFNumberGetValue( | |
4cb1d3da RN |
103 | (CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) Array[i], CFSTR(kIOHIDElementUsageKey)), |
104 | kCFNumberLongType, &nUsage); | |
105 | ||
65442ab6 | 106 | CFNumberGetValue( |
4cb1d3da RN |
107 | (CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) Array[i], CFSTR(kIOHIDElementUsagePageKey)), |
108 | kCFNumberLongType, &nPage); | |
109 | ||
65442ab6 RN |
110 | if (nPage == kHIDPage_Button && nUsage <= 40) |
111 | AddCookieInQueue(Array[i], nUsage-1 ); | |
112 | else if (nPage == kHIDPage_GenericDesktop) | |
4cb1d3da | 113 | { |
65442ab6 RN |
114 | switch(nUsage) |
115 | { | |
116 | case kHIDUsage_GD_X: | |
117 | AddCookieInQueue(Array[i], wxJS_AXIS_X); | |
118 | break; | |
119 | case kHIDUsage_GD_Y: | |
120 | AddCookieInQueue(Array[i], wxJS_AXIS_Y); | |
121 | break; | |
122 | case kHIDUsage_GD_Z: | |
123 | AddCookieInQueue(Array[i], wxJS_AXIS_Z); | |
124 | break; | |
125 | default: | |
126 | break; | |
127 | } | |
4cb1d3da | 128 | } |
65442ab6 RN |
129 | else if (nPage == kHIDPage_Simulation && nUsage == kHIDUsage_Sim_Rudder) |
130 | AddCookieInQueue(Array[i], wxJS_AXIS_RUDDER ); | |
4cb1d3da | 131 | } |
4cb1d3da | 132 | } |
65442ab6 | 133 | } |
4cb1d3da RN |
134 | |
135 | ||
136 | ||
137 | IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject) | |
138 | ||
139 | ||
140 | //////////////////////////////////////////////////////////////////////////// | |
141 | // Background thread for reading the joystick device | |
142 | //////////////////////////////////////////////////////////////////////////// | |
143 | ||
144 | class wxJoystickThread : public wxThread | |
145 | { | |
146 | public: | |
147 | wxJoystickThread(wxHIDJoystick* hid, int joystick); | |
148 | void* Entry(); | |
149 | ||
65442ab6 RN |
150 | static void HIDCallback(void* target, IOReturn res, void* context, void* sender) |
151 | { | |
152 | IOHIDEventStruct hidevent; | |
153 | AbsoluteTime bogustime = {0,0}; | |
154 | IOReturn ret; | |
155 | wxJoystickThread* pThis = (wxJoystickThread*) context; | |
156 | wxHIDJoystick* m_hid = pThis->m_hid; | |
157 | ||
158 | // wxMutexGuiEnter(); | |
159 | ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(), | |
160 | &hidevent, bogustime, 0); | |
161 | // wxMutexGuiLeave(); | |
162 | while ( ret != kIOReturnUnderrun ) | |
163 | { | |
164 | if (pThis->TestDestroy()) | |
165 | break; | |
166 | ||
167 | // wxPrintf(wxT("ENTER\n")); | |
168 | if(ret != kIOReturnSuccess) | |
169 | { | |
170 | wxLogSysError(wxString::Format(wxT("wxJoystick Error:[%i]"), ret)); | |
171 | return; | |
172 | } | |
173 | ||
174 | wxJoystickEvent wxevent; | |
175 | ||
176 | int nIndex = 0; | |
177 | IOHIDElementCookie* pCookies = m_hid->GetCookies(); | |
178 | while(nIndex < 50) | |
179 | { | |
180 | if(hidevent.elementCookie == pCookies[nIndex]) | |
181 | break; | |
182 | ||
183 | ++nIndex; | |
184 | } | |
185 | if(nIndex == 50) | |
186 | { | |
187 | wxLogSysError(wxString::Format(wxT("wxJoystick Out Of Bounds Error"))); | |
188 | break; | |
189 | } | |
190 | ||
191 | if (nIndex < 40) | |
192 | { | |
193 | if (hidevent.value) | |
194 | { | |
195 | pThis->m_buttons |= (1 << nIndex); | |
196 | wxevent.SetEventType(wxEVT_JOY_BUTTON_DOWN); | |
197 | } | |
198 | else | |
199 | { | |
200 | pThis->m_buttons &= ~(1 << nIndex); | |
201 | wxevent.SetEventType(wxEVT_JOY_BUTTON_UP); | |
202 | } | |
203 | ||
204 | wxevent.SetButtonChange(nIndex+1); | |
205 | } | |
206 | else if (nIndex == wxJS_AXIS_X) | |
207 | { | |
208 | pThis->m_lastposition.x = hidevent.value; | |
209 | wxevent.SetEventType(wxEVT_JOY_MOVE); | |
210 | pThis->m_axe[0] = hidevent.value; | |
211 | } | |
212 | else if (nIndex == wxJS_AXIS_Y) | |
213 | { | |
214 | pThis->m_lastposition.y = hidevent.value; | |
215 | wxevent.SetEventType(wxEVT_JOY_MOVE); | |
216 | pThis->m_axe[1] = hidevent.value; | |
217 | } | |
218 | else if (nIndex == wxJS_AXIS_Z) | |
219 | { | |
220 | wxevent.SetEventType(wxEVT_JOY_ZMOVE); | |
221 | pThis->m_axe[2] = hidevent.value; | |
222 | } | |
223 | else | |
224 | wxevent.SetEventType(wxEVT_JOY_MOVE); | |
225 | ||
226 | Nanoseconds timestamp = AbsoluteToNanoseconds(hidevent.timestamp); | |
227 | ||
228 | wxULongLong llTime(timestamp.hi, timestamp.lo); | |
229 | ||
230 | llTime /= 1000000; | |
231 | ||
232 | wxevent.SetTimestamp(llTime.GetValue()); | |
233 | wxevent.SetJoystick(pThis->m_joystick); | |
234 | wxevent.SetButtonState(pThis->m_buttons); | |
235 | wxevent.SetPosition(pThis->m_lastposition); | |
236 | wxevent.SetZPosition(pThis->m_axe[2]); | |
237 | wxevent.SetEventObject(pThis->m_catchwin); | |
238 | ||
239 | // wxPrintf(wxT("SEND\n")); | |
240 | ||
241 | if (pThis->m_catchwin) | |
242 | pThis->m_catchwin->AddPendingEvent(wxevent); | |
243 | ||
244 | // wxMutexGuiEnter(); | |
245 | ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(), | |
246 | &hidevent, bogustime, 0); | |
247 | // wxMutexGuiLeave(); | |
248 | } | |
249 | } | |
250 | ||
4cb1d3da RN |
251 | private: |
252 | wxHIDJoystick* m_hid; | |
253 | int m_joystick; | |
254 | wxPoint m_lastposition; | |
255 | int m_axe[15]; | |
256 | int m_buttons; | |
257 | wxWindow* m_catchwin; | |
258 | int m_polling; | |
259 | ||
260 | friend class wxJoystick; | |
261 | }; | |
262 | ||
263 | ||
264 | wxJoystickThread::wxJoystickThread(wxHIDJoystick* hid, int joystick) | |
265 | : m_hid(hid), | |
266 | m_joystick(joystick), | |
65442ab6 | 267 | m_lastposition(127,127), |
4cb1d3da RN |
268 | m_buttons(0), |
269 | m_catchwin(NULL), | |
270 | m_polling(0) | |
271 | { | |
272 | for (int i=0; i<15; i++) | |
273 | m_axe[i] = 0; | |
274 | } | |
275 | ||
276 | ||
277 | # define wxJSVERIFY(arg) if(!(arg)) {wxLogSysError(wxT(#arg)); return NULL;} | |
278 | # define wxJSASSERT(arg) wxJSVERIFY(arg) | |
279 | ||
280 | void* wxJoystickThread::Entry() | |
281 | { | |
282 | CFRunLoopSourceRef pRLSource = NULL; | |
283 | ||
284 | wxJSVERIFY( (*m_hid->GetQueue())->createAsyncEventSource(m_hid->GetQueue(), &pRLSource) | |
285 | == kIOReturnSuccess ); | |
286 | wxJSASSERT(pRLSource != NULL); | |
287 | ||
288 | //attach runloop source to main run loop in thread | |
289 | CFRunLoopRef pRL = CFRunLoopGetCurrent(); | |
290 | CFRunLoopAddSource(pRL, pRLSource, kCFRunLoopDefaultMode); | |
291 | ||
65442ab6 RN |
292 | wxJSVERIFY( (*m_hid->GetQueue())->start(m_hid->GetQueue()) == kIOReturnSuccess ); |
293 | wxJSVERIFY( (*m_hid->GetQueue())->setEventCallout(m_hid->GetQueue(), &wxJoystickThread::HIDCallback, this, this) == kIOReturnSuccess ); | |
4cb1d3da RN |
294 | |
295 | double dTime; | |
4cb1d3da RN |
296 | |
297 | while(true) | |
298 | { | |
299 | if (TestDestroy()) | |
300 | break; | |
301 | ||
302 | if (m_polling) | |
303 | dTime = 0.0001 * m_polling; | |
304 | else | |
305 | dTime = 0.0001 * 10; // check at least every 10 msec in blocking case | |
306 | ||
65442ab6 | 307 | CFRunLoopRunInMode(kCFRunLoopDefaultMode, dTime, true); |
4cb1d3da | 308 | } |
65442ab6 RN |
309 | |
310 | wxJSASSERT( CFRunLoopContainsSource(pRL, pRLSource, kCFRunLoopDefaultMode) ); | |
311 | CFRunLoopRemoveSource(pRL, pRLSource, kCFRunLoopDefaultMode); | |
312 | CFRelease(pRLSource); | |
313 | ||
4cb1d3da RN |
314 | return NULL; |
315 | } | |
316 | ||
317 | ||
318 | //////////////////////////////////////////////////////////////////////////// | |
319 | ||
320 | wxJoystick::wxJoystick(int joystick) | |
321 | : m_joystick(joystick), | |
322 | m_thread(NULL) | |
323 | { | |
324 | m_hid = new wxHIDJoystick(); | |
325 | ||
326 | if (m_hid->Create(m_joystick)) | |
327 | { | |
328 | m_thread = new wxJoystickThread(m_hid, m_joystick); | |
329 | m_thread->Create(); | |
330 | m_thread->Run(); | |
331 | } | |
332 | else | |
333 | { | |
334 | delete m_hid; | |
335 | m_hid = NULL; | |
336 | } | |
337 | } | |
338 | ||
339 | ||
340 | wxJoystick::~wxJoystick() | |
341 | { | |
342 | ReleaseCapture(); | |
343 | if (m_thread) | |
344 | m_thread->Delete(); // It's detached so it will delete itself | |
345 | ||
346 | if (m_hid) | |
347 | delete m_hid; | |
348 | } | |
349 | ||
350 | ||
351 | //////////////////////////////////////////////////////////////////////////// | |
352 | // State | |
353 | //////////////////////////////////////////////////////////////////////////// | |
354 | ||
355 | wxPoint wxJoystick::GetPosition() const | |
356 | { | |
357 | wxPoint pos(wxDefaultPosition); | |
358 | if (m_thread) pos = m_thread->m_lastposition; | |
359 | return pos; | |
360 | } | |
361 | ||
362 | int wxJoystick::GetZPosition() const | |
363 | { | |
364 | if (m_thread) | |
365 | return m_thread->m_axe[wxJS_AXIS_Z]; | |
366 | return 0; | |
367 | } | |
368 | ||
369 | int wxJoystick::GetButtonState() const | |
370 | { | |
371 | if (m_thread) | |
372 | return m_thread->m_buttons; | |
373 | return 0; | |
374 | } | |
375 | ||
376 | int wxJoystick::GetPOVPosition() const | |
377 | { return -1; } | |
378 | ||
379 | int wxJoystick::GetPOVCTSPosition() const | |
380 | { return -1; } | |
381 | ||
382 | int wxJoystick::GetRudderPosition() const | |
383 | { | |
384 | if (m_thread) | |
385 | return m_thread->m_axe[wxJS_AXIS_RUDDER]; | |
386 | return 0; | |
387 | } | |
388 | ||
389 | int wxJoystick::GetUPosition() const | |
390 | { | |
391 | if (m_thread) | |
392 | return m_thread->m_axe[wxJS_AXIS_U]; | |
393 | return 0; | |
394 | } | |
395 | ||
396 | int wxJoystick::GetVPosition() const | |
397 | { | |
398 | if (m_thread) | |
399 | return m_thread->m_axe[wxJS_AXIS_V]; | |
400 | return 0; | |
401 | } | |
402 | ||
403 | int wxJoystick::GetMovementThreshold() const | |
404 | { return 0; } | |
405 | ||
406 | void wxJoystick::SetMovementThreshold(int threshold) | |
407 | { } | |
408 | ||
409 | //////////////////////////////////////////////////////////////////////////// | |
410 | // Capabilities | |
411 | //////////////////////////////////////////////////////////////////////////// | |
412 | ||
413 | bool wxJoystick::IsOk() const | |
414 | { return m_hid != NULL; } | |
415 | ||
416 | int wxJoystick::GetNumberJoysticks() const | |
417 | { return wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick) + | |
418 | wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad); } | |
419 | ||
420 | int wxJoystick::GetManufacturerId() const | |
65442ab6 | 421 | { return m_hid->m_nManufacturerId; } |
4cb1d3da RN |
422 | |
423 | int wxJoystick::GetProductId() const | |
65442ab6 | 424 | { return m_hid->m_nProductId; } |
4cb1d3da RN |
425 | |
426 | wxString wxJoystick::GetProductName() const | |
65442ab6 | 427 | { return m_hid->m_szProductName; } |
4cb1d3da RN |
428 | |
429 | int wxJoystick::GetXMin() const | |
430 | { return wxJS_AXIS_MIN; } | |
431 | ||
432 | int wxJoystick::GetYMin() const | |
433 | { return wxJS_AXIS_MIN; } | |
434 | ||
435 | int wxJoystick::GetZMin() const | |
436 | { return wxJS_AXIS_MIN; } | |
437 | ||
438 | int wxJoystick::GetXMax() const | |
439 | { return wxJS_AXIS_MAX; } | |
440 | ||
441 | int wxJoystick::GetYMax() const | |
442 | { return wxJS_AXIS_MAX; } | |
443 | ||
444 | int wxJoystick::GetZMax() const | |
445 | { return wxJS_AXIS_MAX; } | |
446 | ||
447 | int wxJoystick::GetNumberButtons() const | |
448 | { | |
449 | int nCount = 0; | |
450 | ||
451 | for(int nIndex = 0; nIndex < 40; ++nIndex) | |
452 | { | |
453 | if(m_hid->HasElement(nIndex)) | |
454 | ++nCount; | |
455 | } | |
456 | ||
457 | return nCount; | |
458 | } | |
459 | ||
460 | int wxJoystick::GetNumberAxes() const | |
461 | { | |
462 | int nCount = 0; | |
463 | ||
464 | for(int nIndex = 40; nIndex < 50; ++nIndex) | |
465 | { | |
466 | if(m_hid->HasElement(nIndex)) | |
467 | ++nCount; | |
468 | } | |
469 | ||
470 | return nCount; | |
471 | } | |
472 | ||
473 | // | |
474 | // internal | |
475 | // | |
476 | int wxJoystick::GetMaxButtons() const | |
477 | { return 15; } | |
478 | ||
479 | int wxJoystick::GetMaxAxes() const | |
480 | { return 10; } | |
481 | ||
482 | int wxJoystick::GetPollingMin() const | |
483 | { return 10; } | |
484 | ||
485 | int wxJoystick::GetPollingMax() const | |
486 | { return 1000; } | |
487 | ||
488 | int wxJoystick::GetRudderMin() const | |
489 | { return wxJS_AXIS_MIN; } | |
490 | ||
491 | int wxJoystick::GetRudderMax() const | |
492 | { return wxJS_AXIS_MAX; } | |
493 | ||
494 | int wxJoystick::GetUMin() const | |
495 | { return wxJS_AXIS_MIN; } | |
496 | ||
497 | int wxJoystick::GetUMax() const | |
498 | { return wxJS_AXIS_MAX; } | |
499 | ||
500 | int wxJoystick::GetVMin() const | |
501 | { return wxJS_AXIS_MIN; } | |
502 | ||
503 | int wxJoystick::GetVMax() const | |
504 | { return wxJS_AXIS_MAX; } | |
505 | ||
506 | bool wxJoystick::HasRudder() const | |
507 | { return m_hid->HasElement(wxJS_AXIS_RUDDER); } | |
508 | ||
509 | bool wxJoystick::HasZ() const | |
510 | { return m_hid->HasElement(wxJS_AXIS_Z); } | |
511 | ||
512 | bool wxJoystick::HasU() const | |
513 | { return m_hid->HasElement(wxJS_AXIS_U); } | |
514 | ||
515 | bool wxJoystick::HasV() const | |
516 | { return m_hid->HasElement(wxJS_AXIS_V); } | |
517 | ||
518 | bool wxJoystick::HasPOV() const | |
519 | { return false; } | |
520 | ||
521 | bool wxJoystick::HasPOV4Dir() const | |
522 | { return false; } | |
523 | ||
524 | bool wxJoystick::HasPOVCTS() const | |
525 | { return false; } | |
526 | ||
527 | //////////////////////////////////////////////////////////////////////////// | |
528 | // Operations | |
529 | //////////////////////////////////////////////////////////////////////////// | |
530 | ||
531 | bool wxJoystick::SetCapture(wxWindow* win, int pollingFreq) | |
532 | { | |
533 | if (m_thread) | |
534 | { | |
535 | m_thread->m_catchwin = win; | |
536 | m_thread->m_polling = pollingFreq; | |
537 | return true; | |
538 | } | |
539 | return false; | |
540 | } | |
541 | ||
542 | bool wxJoystick::ReleaseCapture() | |
543 | { | |
544 | if (m_thread) | |
545 | { | |
546 | m_thread->m_catchwin = NULL; | |
547 | m_thread->m_polling = 0; | |
548 | return true; | |
549 | } | |
550 | return false; | |
551 | } | |
552 | #endif | |
553 | //OSX | |
554 | ||
555 | #endif | |
556 | // wxUSE_JOYSTICK | |
557 |