1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxJoystick class
8 // Copyright: (c) Ryan Norton
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "joystick.h"
16 #include "wx/wxprec.h"
24 #include "wx/joystick.h"
25 #include "wx/thread.h"
26 #include "wx/window.h"
28 #include "wx/mac/corefoundation/hid.h"
30 #include <CoreServices/CoreServices.h>
31 #include <mach/mach.h>
32 #include <mach/mach_time.h>
43 wxJS_AXIS_MAX
= 255, //32767,
44 wxJS_AXIS_MIN
= 0, //-32767
47 class wxHIDJoystick
: public wxHIDDevice
50 bool Create(int nWhich
);
51 virtual void BuildCookies(wxCFArray
& Array
);
52 void MakeCookies(wxCFArray
& Array
);
53 IOHIDElementCookie
* GetCookies() {return m_pCookies
;}
54 IOHIDQueueInterface
** GetQueue() {return m_ppQueue
;}
56 friend class wxJoystick
;
60 bool wxHIDJoystick::Create(int nWhich
)
62 int nJoysticks
= GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
);
64 if (nWhich
<= nJoysticks
)
65 return wxHIDDevice::Create(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
);
69 int nGamePads
= GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
);
71 if (nWhich
<= nGamePads
)
72 return wxHIDDevice::Create(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
);
77 void wxHIDJoystick::BuildCookies(wxCFArray
& Array
)
79 Array
= CFDictionaryGetValue((CFDictionaryRef
)Array
[0], CFSTR(kIOHIDElementKey
));
80 InitCookies(50, true);
82 memset(m_pCookies
, 0, sizeof(*m_pCookies
) * 50);
85 // for(int i = 0; i < 50; ++i)
86 // wxPrintf(wxT("\nVAL #%i:[%i]"), i, m_pCookies[i]);
89 void wxHIDJoystick::MakeCookies(wxCFArray
& Array
)
94 for (i
= 0; i
< Array
.GetCount(); ++i
)
96 const void* ref
= CFDictionaryGetValue((CFDictionaryRef
)Array
[i
], CFSTR(kIOHIDElementKey
));
98 // wxPrintf(wxT("ELM\n"));
101 wxCFArray
newarray(ref
);
102 MakeCookies(newarray
);
107 (CFNumberRef
) CFDictionaryGetValue((CFDictionaryRef
) Array
[i
], CFSTR(kIOHIDElementUsageKey
)),
108 kCFNumberLongType
, &nUsage
);
111 (CFNumberRef
) CFDictionaryGetValue((CFDictionaryRef
) Array
[i
], CFSTR(kIOHIDElementUsagePageKey
)),
112 kCFNumberLongType
, &nPage
);
114 if (nPage
== kHIDPage_Button
&& nUsage
<= 40)
115 AddCookieInQueue(Array
[i
], nUsage
-1 );
116 else if (nPage
== kHIDPage_GenericDesktop
)
121 AddCookieInQueue(Array
[i
], wxJS_AXIS_X
);
124 AddCookieInQueue(Array
[i
], wxJS_AXIS_Y
);
127 AddCookieInQueue(Array
[i
], wxJS_AXIS_Z
);
133 else if (nPage
== kHIDPage_Simulation
&& nUsage
== kHIDUsage_Sim_Rudder
)
134 AddCookieInQueue(Array
[i
], wxJS_AXIS_RUDDER
);
141 IMPLEMENT_DYNAMIC_CLASS(wxJoystick
, wxObject
)
144 ////////////////////////////////////////////////////////////////////////////
145 // Background thread for reading the joystick device
146 ////////////////////////////////////////////////////////////////////////////
148 class wxJoystickThread
: public wxThread
151 wxJoystickThread(wxHIDJoystick
* hid
, int joystick
);
154 static void HIDCallback(void* target
, IOReturn res
, void* context
, void* sender
)
156 IOHIDEventStruct hidevent
;
157 AbsoluteTime bogustime
= {0,0};
159 wxJoystickThread
* pThis
= (wxJoystickThread
*) context
;
160 wxHIDJoystick
* m_hid
= pThis
->m_hid
;
162 // wxMutexGuiEnter();
163 ret
= (*m_hid
->GetQueue())->getNextEvent(m_hid
->GetQueue(),
164 &hidevent
, bogustime
, 0);
165 // wxMutexGuiLeave();
166 while ( ret
!= kIOReturnUnderrun
)
168 if (pThis
->TestDestroy())
171 // wxPrintf(wxT("ENTER\n"));
172 if(ret
!= kIOReturnSuccess
)
174 wxLogSysError(wxString::Format(wxT("wxJoystick Error:[%i]"), ret
));
178 wxJoystickEvent wxevent
;
181 IOHIDElementCookie
* pCookies
= m_hid
->GetCookies();
184 if(hidevent
.elementCookie
== pCookies
[nIndex
])
191 wxLogSysError(wxString::Format(wxT("wxJoystick Out Of Bounds Error")));
199 pThis
->m_buttons
|= (1 << nIndex
);
200 wxevent
.SetEventType(wxEVT_JOY_BUTTON_DOWN
);
204 pThis
->m_buttons
&= ~(1 << nIndex
);
205 wxevent
.SetEventType(wxEVT_JOY_BUTTON_UP
);
208 wxevent
.SetButtonChange(nIndex
+1);
210 else if (nIndex
== wxJS_AXIS_X
)
212 pThis
->m_lastposition
.x
= hidevent
.value
;
213 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
214 pThis
->m_axe
[0] = hidevent
.value
;
216 else if (nIndex
== wxJS_AXIS_Y
)
218 pThis
->m_lastposition
.y
= hidevent
.value
;
219 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
220 pThis
->m_axe
[1] = hidevent
.value
;
222 else if (nIndex
== wxJS_AXIS_Z
)
224 wxevent
.SetEventType(wxEVT_JOY_ZMOVE
);
225 pThis
->m_axe
[2] = hidevent
.value
;
228 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
230 Nanoseconds timestamp
= AbsoluteToNanoseconds(hidevent
.timestamp
);
232 wxULongLong
llTime(timestamp
.hi
, timestamp
.lo
);
236 wxevent
.SetTimestamp(llTime
.GetValue());
237 wxevent
.SetJoystick(pThis
->m_joystick
);
238 wxevent
.SetButtonState(pThis
->m_buttons
);
239 wxevent
.SetPosition(pThis
->m_lastposition
);
240 wxevent
.SetZPosition(pThis
->m_axe
[2]);
241 wxevent
.SetEventObject(pThis
->m_catchwin
);
243 // wxPrintf(wxT("SEND\n"));
245 if (pThis
->m_catchwin
)
246 pThis
->m_catchwin
->AddPendingEvent(wxevent
);
248 // wxMutexGuiEnter();
249 ret
= (*m_hid
->GetQueue())->getNextEvent(m_hid
->GetQueue(),
250 &hidevent
, bogustime
, 0);
251 // wxMutexGuiLeave();
256 wxHIDJoystick
* m_hid
;
258 wxPoint m_lastposition
;
261 wxWindow
* m_catchwin
;
264 friend class wxJoystick
;
268 wxJoystickThread::wxJoystickThread(wxHIDJoystick
* hid
, int joystick
)
270 m_joystick(joystick
),
271 m_lastposition(127,127),
276 for (int i
=0; i
<15; i
++)
281 # define wxJSVERIFY(arg) if(!(arg)) {wxLogSysError(wxT(#arg)); return NULL;}
282 # define wxJSASSERT(arg) wxJSVERIFY(arg)
284 void* wxJoystickThread::Entry()
286 CFRunLoopSourceRef pRLSource
= NULL
;
288 wxJSVERIFY( (*m_hid
->GetQueue())->createAsyncEventSource(m_hid
->GetQueue(), &pRLSource
)
289 == kIOReturnSuccess
);
290 wxJSASSERT(pRLSource
!= NULL
);
292 //attach runloop source to main run loop in thread
293 CFRunLoopRef pRL
= CFRunLoopGetCurrent();
294 CFRunLoopAddSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
);
296 wxJSVERIFY( (*m_hid
->GetQueue())->start(m_hid
->GetQueue()) == kIOReturnSuccess
);
297 wxJSVERIFY( (*m_hid
->GetQueue())->setEventCallout(m_hid
->GetQueue(), &wxJoystickThread::HIDCallback
, this, this) == kIOReturnSuccess
);
307 dTime
= 0.0001 * m_polling
;
309 dTime
= 0.0001 * 10; // check at least every 10 msec in blocking case
311 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, dTime
, true);
314 wxJSASSERT( CFRunLoopContainsSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
) );
315 CFRunLoopRemoveSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
);
316 CFRelease(pRLSource
);
322 ////////////////////////////////////////////////////////////////////////////
324 wxJoystick::wxJoystick(int joystick
)
325 : m_joystick(joystick
),
328 m_hid
= new wxHIDJoystick();
330 if (m_hid
->Create(m_joystick
))
332 m_thread
= new wxJoystickThread(m_hid
, m_joystick
);
344 wxJoystick::~wxJoystick()
348 m_thread
->Delete(); // It's detached so it will delete itself
355 ////////////////////////////////////////////////////////////////////////////
357 ////////////////////////////////////////////////////////////////////////////
359 wxPoint
wxJoystick::GetPosition() const
361 wxPoint
pos(wxDefaultPosition
);
362 if (m_thread
) pos
= m_thread
->m_lastposition
;
366 int wxJoystick::GetZPosition() const
369 return m_thread
->m_axe
[wxJS_AXIS_Z
];
373 int wxJoystick::GetButtonState() const
376 return m_thread
->m_buttons
;
380 int wxJoystick::GetPOVPosition() const
383 int wxJoystick::GetPOVCTSPosition() const
386 int wxJoystick::GetRudderPosition() const
389 return m_thread
->m_axe
[wxJS_AXIS_RUDDER
];
393 int wxJoystick::GetUPosition() const
396 return m_thread
->m_axe
[wxJS_AXIS_U
];
400 int wxJoystick::GetVPosition() const
403 return m_thread
->m_axe
[wxJS_AXIS_V
];
407 int wxJoystick::GetMovementThreshold() const
410 void wxJoystick::SetMovementThreshold(int threshold
)
413 ////////////////////////////////////////////////////////////////////////////
415 ////////////////////////////////////////////////////////////////////////////
417 bool wxJoystick::IsOk() const
418 { return m_hid
!= NULL
; }
420 int wxJoystick::GetNumberJoysticks() const
421 { return wxHIDDevice::GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
) +
422 wxHIDDevice::GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
); }
424 int wxJoystick::GetManufacturerId() const
425 { return m_hid
->m_nManufacturerId
; }
427 int wxJoystick::GetProductId() const
428 { return m_hid
->m_nProductId
; }
430 wxString
wxJoystick::GetProductName() const
431 { return m_hid
->m_szProductName
; }
433 int wxJoystick::GetXMin() const
434 { return wxJS_AXIS_MIN
; }
436 int wxJoystick::GetYMin() const
437 { return wxJS_AXIS_MIN
; }
439 int wxJoystick::GetZMin() const
440 { return wxJS_AXIS_MIN
; }
442 int wxJoystick::GetXMax() const
443 { return wxJS_AXIS_MAX
; }
445 int wxJoystick::GetYMax() const
446 { return wxJS_AXIS_MAX
; }
448 int wxJoystick::GetZMax() const
449 { return wxJS_AXIS_MAX
; }
451 int wxJoystick::GetNumberButtons() const
455 for(int nIndex
= 0; nIndex
< 40; ++nIndex
)
457 if(m_hid
->HasElement(nIndex
))
464 int wxJoystick::GetNumberAxes() const
468 for(int nIndex
= 40; nIndex
< 50; ++nIndex
)
470 if(m_hid
->HasElement(nIndex
))
480 int wxJoystick::GetMaxButtons() const
483 int wxJoystick::GetMaxAxes() const
486 int wxJoystick::GetPollingMin() const
489 int wxJoystick::GetPollingMax() const
492 int wxJoystick::GetRudderMin() const
493 { return wxJS_AXIS_MIN
; }
495 int wxJoystick::GetRudderMax() const
496 { return wxJS_AXIS_MAX
; }
498 int wxJoystick::GetUMin() const
499 { return wxJS_AXIS_MIN
; }
501 int wxJoystick::GetUMax() const
502 { return wxJS_AXIS_MAX
; }
504 int wxJoystick::GetVMin() const
505 { return wxJS_AXIS_MIN
; }
507 int wxJoystick::GetVMax() const
508 { return wxJS_AXIS_MAX
; }
510 bool wxJoystick::HasRudder() const
511 { return m_hid
->HasElement(wxJS_AXIS_RUDDER
); }
513 bool wxJoystick::HasZ() const
514 { return m_hid
->HasElement(wxJS_AXIS_Z
); }
516 bool wxJoystick::HasU() const
517 { return m_hid
->HasElement(wxJS_AXIS_U
); }
519 bool wxJoystick::HasV() const
520 { return m_hid
->HasElement(wxJS_AXIS_V
); }
522 bool wxJoystick::HasPOV() const
525 bool wxJoystick::HasPOV4Dir() const
528 bool wxJoystick::HasPOVCTS() const
531 ////////////////////////////////////////////////////////////////////////////
533 ////////////////////////////////////////////////////////////////////////////
535 bool wxJoystick::SetCapture(wxWindow
* win
, int pollingFreq
)
539 m_thread
->m_catchwin
= win
;
540 m_thread
->m_polling
= pollingFreq
;
546 bool wxJoystick::ReleaseCapture()
550 m_thread
->m_catchwin
= NULL
;
551 m_thread
->m_polling
= 0;