1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxJoystick class
8 // Copyright: (c) Ryan Norton
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 #include "wx/wxprec.h"
20 #include "wx/joystick.h"
21 #include "wx/thread.h"
22 #include "wx/window.h"
24 #include "wx/mac/corefoundation/hid.h"
26 #include <CoreServices/CoreServices.h>
27 #include <mach/mach.h>
28 #include <mach/mach_time.h>
39 wxJS_AXIS_MAX
= 255, //32767,
40 wxJS_AXIS_MIN
= 0, //-32767
43 class wxHIDJoystick
: public wxHIDDevice
46 bool Create(int nWhich
);
47 virtual void BuildCookies(wxCFArray
& Array
);
48 void MakeCookies(wxCFArray
& Array
);
49 IOHIDElementCookie
* GetCookies() {return m_pCookies
;}
50 IOHIDQueueInterface
** GetQueue() {return m_ppQueue
;}
52 friend class wxJoystick
;
56 bool wxHIDJoystick::Create(int nWhich
)
58 int nJoysticks
= GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
);
60 if (nWhich
<= nJoysticks
)
61 return wxHIDDevice::Create(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
);
65 int nGamePads
= GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
);
67 if (nWhich
<= nGamePads
)
68 return wxHIDDevice::Create(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
);
73 void wxHIDJoystick::BuildCookies(wxCFArray
& Array
)
75 Array
= CFDictionaryGetValue((CFDictionaryRef
)Array
[0], CFSTR(kIOHIDElementKey
));
76 InitCookies(50, true);
78 memset(m_pCookies
, 0, sizeof(*m_pCookies
) * 50);
81 // for(int i = 0; i < 50; ++i)
82 // wxPrintf(wxT("\nVAL #%i:[%i]"), i, m_pCookies[i]);
85 void wxHIDJoystick::MakeCookies(wxCFArray
& Array
)
90 for (i
= 0; i
< Array
.Count(); ++i
)
92 const void* ref
= CFDictionaryGetValue((CFDictionaryRef
)Array
[i
], CFSTR(kIOHIDElementKey
));
94 // wxPrintf(wxT("ELM\n"));
97 wxCFArray
newarray(ref
);
98 MakeCookies(newarray
);
103 (CFNumberRef
) CFDictionaryGetValue((CFDictionaryRef
) Array
[i
], CFSTR(kIOHIDElementUsageKey
)),
104 kCFNumberLongType
, &nUsage
);
107 (CFNumberRef
) CFDictionaryGetValue((CFDictionaryRef
) Array
[i
], CFSTR(kIOHIDElementUsagePageKey
)),
108 kCFNumberLongType
, &nPage
);
110 if (nPage
== kHIDPage_Button
&& nUsage
<= 40)
111 AddCookieInQueue(Array
[i
], nUsage
-1 );
112 else if (nPage
== kHIDPage_GenericDesktop
)
117 AddCookieInQueue(Array
[i
], wxJS_AXIS_X
);
120 AddCookieInQueue(Array
[i
], wxJS_AXIS_Y
);
123 AddCookieInQueue(Array
[i
], wxJS_AXIS_Z
);
129 else if (nPage
== kHIDPage_Simulation
&& nUsage
== kHIDUsage_Sim_Rudder
)
130 AddCookieInQueue(Array
[i
], wxJS_AXIS_RUDDER
);
137 IMPLEMENT_DYNAMIC_CLASS(wxJoystick
, wxObject
)
140 ////////////////////////////////////////////////////////////////////////////
141 // Background thread for reading the joystick device
142 ////////////////////////////////////////////////////////////////////////////
144 class wxJoystickThread
: public wxThread
147 wxJoystickThread(wxHIDJoystick
* hid
, int joystick
);
150 static void HIDCallback(void* target
, IOReturn res
, void* context
, void* sender
)
152 IOHIDEventStruct hidevent
;
153 AbsoluteTime bogustime
= {0,0};
155 wxJoystickThread
* pThis
= (wxJoystickThread
*) context
;
156 wxHIDJoystick
* m_hid
= pThis
->m_hid
;
158 // wxMutexGuiEnter();
159 ret
= (*m_hid
->GetQueue())->getNextEvent(m_hid
->GetQueue(),
160 &hidevent
, bogustime
, 0);
161 // wxMutexGuiLeave();
162 while ( ret
!= kIOReturnUnderrun
)
164 if (pThis
->TestDestroy())
167 // wxPrintf(wxT("ENTER\n"));
168 if(ret
!= kIOReturnSuccess
)
170 wxLogSysError(wxString::Format(wxT("wxJoystick Error:[%i]"), ret
));
174 wxJoystickEvent wxevent
;
177 IOHIDElementCookie
* pCookies
= m_hid
->GetCookies();
180 if(hidevent
.elementCookie
== pCookies
[nIndex
])
187 wxLogSysError(wxString::Format(wxT("wxJoystick Out Of Bounds Error")));
195 pThis
->m_buttons
|= (1 << nIndex
);
196 wxevent
.SetEventType(wxEVT_JOY_BUTTON_DOWN
);
200 pThis
->m_buttons
&= ~(1 << nIndex
);
201 wxevent
.SetEventType(wxEVT_JOY_BUTTON_UP
);
204 wxevent
.SetButtonChange(nIndex
+1);
206 else if (nIndex
== wxJS_AXIS_X
)
208 pThis
->m_lastposition
.x
= hidevent
.value
;
209 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
210 pThis
->m_axe
[0] = hidevent
.value
;
212 else if (nIndex
== wxJS_AXIS_Y
)
214 pThis
->m_lastposition
.y
= hidevent
.value
;
215 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
216 pThis
->m_axe
[1] = hidevent
.value
;
218 else if (nIndex
== wxJS_AXIS_Z
)
220 wxevent
.SetEventType(wxEVT_JOY_ZMOVE
);
221 pThis
->m_axe
[2] = hidevent
.value
;
224 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
226 Nanoseconds timestamp
= AbsoluteToNanoseconds(hidevent
.timestamp
);
228 wxULongLong
llTime(timestamp
.hi
, timestamp
.lo
);
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
);
239 // wxPrintf(wxT("SEND\n"));
241 if (pThis
->m_catchwin
)
242 pThis
->m_catchwin
->AddPendingEvent(wxevent
);
244 // wxMutexGuiEnter();
245 ret
= (*m_hid
->GetQueue())->getNextEvent(m_hid
->GetQueue(),
246 &hidevent
, bogustime
, 0);
247 // wxMutexGuiLeave();
252 wxHIDJoystick
* m_hid
;
254 wxPoint m_lastposition
;
257 wxWindow
* m_catchwin
;
260 friend class wxJoystick
;
264 wxJoystickThread::wxJoystickThread(wxHIDJoystick
* hid
, int joystick
)
266 m_joystick(joystick
),
267 m_lastposition(127,127),
272 for (int i
=0; i
<15; i
++)
277 # define wxJSVERIFY(arg) if(!(arg)) {wxLogSysError(wxT(#arg)); return NULL;}
278 # define wxJSASSERT(arg) wxJSVERIFY(arg)
280 void* wxJoystickThread::Entry()
282 CFRunLoopSourceRef pRLSource
= NULL
;
284 wxJSVERIFY( (*m_hid
->GetQueue())->createAsyncEventSource(m_hid
->GetQueue(), &pRLSource
)
285 == kIOReturnSuccess
);
286 wxJSASSERT(pRLSource
!= NULL
);
288 //attach runloop source to main run loop in thread
289 CFRunLoopRef pRL
= CFRunLoopGetCurrent();
290 CFRunLoopAddSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
);
292 wxJSVERIFY( (*m_hid
->GetQueue())->start(m_hid
->GetQueue()) == kIOReturnSuccess
);
293 wxJSVERIFY( (*m_hid
->GetQueue())->setEventCallout(m_hid
->GetQueue(), &wxJoystickThread::HIDCallback
, this, this) == kIOReturnSuccess
);
303 dTime
= 0.0001 * m_polling
;
305 dTime
= 0.0001 * 10; // check at least every 10 msec in blocking case
307 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, dTime
, true);
310 wxJSASSERT( CFRunLoopContainsSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
) );
311 CFRunLoopRemoveSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
);
312 CFRelease(pRLSource
);
318 ////////////////////////////////////////////////////////////////////////////
320 wxJoystick::wxJoystick(int joystick
)
321 : m_joystick(joystick
),
324 m_hid
= new wxHIDJoystick();
326 if (m_hid
->Create(m_joystick
))
328 m_thread
= new wxJoystickThread(m_hid
, m_joystick
);
340 wxJoystick::~wxJoystick()
344 m_thread
->Delete(); // It's detached so it will delete itself
351 ////////////////////////////////////////////////////////////////////////////
353 ////////////////////////////////////////////////////////////////////////////
355 wxPoint
wxJoystick::GetPosition() const
357 wxPoint
pos(wxDefaultPosition
);
358 if (m_thread
) pos
= m_thread
->m_lastposition
;
362 int wxJoystick::GetZPosition() const
365 return m_thread
->m_axe
[wxJS_AXIS_Z
];
369 int wxJoystick::GetButtonState() const
372 return m_thread
->m_buttons
;
376 int wxJoystick::GetPOVPosition() const
379 int wxJoystick::GetPOVCTSPosition() const
382 int wxJoystick::GetRudderPosition() const
385 return m_thread
->m_axe
[wxJS_AXIS_RUDDER
];
389 int wxJoystick::GetUPosition() const
392 return m_thread
->m_axe
[wxJS_AXIS_U
];
396 int wxJoystick::GetVPosition() const
399 return m_thread
->m_axe
[wxJS_AXIS_V
];
403 int wxJoystick::GetMovementThreshold() const
406 void wxJoystick::SetMovementThreshold(int threshold
)
409 ////////////////////////////////////////////////////////////////////////////
411 ////////////////////////////////////////////////////////////////////////////
413 bool wxJoystick::IsOk() const
414 { return m_hid
!= NULL
; }
416 int wxJoystick::GetNumberJoysticks() const
417 { return wxHIDDevice::GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
) +
418 wxHIDDevice::GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
); }
420 int wxJoystick::GetManufacturerId() const
421 { return m_hid
->m_nManufacturerId
; }
423 int wxJoystick::GetProductId() const
424 { return m_hid
->m_nProductId
; }
426 wxString
wxJoystick::GetProductName() const
427 { return m_hid
->m_szProductName
; }
429 int wxJoystick::GetXMin() const
430 { return wxJS_AXIS_MIN
; }
432 int wxJoystick::GetYMin() const
433 { return wxJS_AXIS_MIN
; }
435 int wxJoystick::GetZMin() const
436 { return wxJS_AXIS_MIN
; }
438 int wxJoystick::GetXMax() const
439 { return wxJS_AXIS_MAX
; }
441 int wxJoystick::GetYMax() const
442 { return wxJS_AXIS_MAX
; }
444 int wxJoystick::GetZMax() const
445 { return wxJS_AXIS_MAX
; }
447 int wxJoystick::GetNumberButtons() const
451 for(int nIndex
= 0; nIndex
< 40; ++nIndex
)
453 if(m_hid
->HasElement(nIndex
))
460 int wxJoystick::GetNumberAxes() const
464 for(int nIndex
= 40; nIndex
< 50; ++nIndex
)
466 if(m_hid
->HasElement(nIndex
))
476 int wxJoystick::GetMaxButtons() const
479 int wxJoystick::GetMaxAxes() const
482 int wxJoystick::GetPollingMin() const
485 int wxJoystick::GetPollingMax() const
488 int wxJoystick::GetRudderMin() const
489 { return wxJS_AXIS_MIN
; }
491 int wxJoystick::GetRudderMax() const
492 { return wxJS_AXIS_MAX
; }
494 int wxJoystick::GetUMin() const
495 { return wxJS_AXIS_MIN
; }
497 int wxJoystick::GetUMax() const
498 { return wxJS_AXIS_MAX
; }
500 int wxJoystick::GetVMin() const
501 { return wxJS_AXIS_MIN
; }
503 int wxJoystick::GetVMax() const
504 { return wxJS_AXIS_MAX
; }
506 bool wxJoystick::HasRudder() const
507 { return m_hid
->HasElement(wxJS_AXIS_RUDDER
); }
509 bool wxJoystick::HasZ() const
510 { return m_hid
->HasElement(wxJS_AXIS_Z
); }
512 bool wxJoystick::HasU() const
513 { return m_hid
->HasElement(wxJS_AXIS_U
); }
515 bool wxJoystick::HasV() const
516 { return m_hid
->HasElement(wxJS_AXIS_V
); }
518 bool wxJoystick::HasPOV() const
521 bool wxJoystick::HasPOV4Dir() const
524 bool wxJoystick::HasPOVCTS() const
527 ////////////////////////////////////////////////////////////////////////////
529 ////////////////////////////////////////////////////////////////////////////
531 bool wxJoystick::SetCapture(wxWindow
* win
, int pollingFreq
)
535 m_thread
->m_catchwin
= win
;
536 m_thread
->m_polling
= pollingFreq
;
542 bool wxJoystick::ReleaseCapture()
546 m_thread
->m_catchwin
= NULL
;
547 m_thread
->m_polling
= 0;