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
= 32767,
44 wxJS_AXIS_MIN
= -32767
47 class wxHIDJoystick
: public wxHIDDevice
50 bool Create(int nWhich
);
51 virtual void BuildCookies(wxCFArray
& Array
);
52 IOHIDElementCookie
* GetCookies() {return m_pCookies
;}
54 IOHIDQueueInterface
** GetQueue() {return m_ppQueue
;}
58 bool wxHIDJoystick::Create(int nWhich
)
60 int nJoysticks
= GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
);
62 if (nWhich
<= nJoysticks
)
63 return wxHIDDevice::Create(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
);
67 int nGamePads
= GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
);
69 if (nWhich
<= nGamePads
)
70 return wxHIDDevice::Create(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
);
75 void wxHIDJoystick::BuildCookies(wxCFArray
& Array
)
77 Array
= CFDictionaryGetValue((CFDictionaryRef
)Array
[0], CFSTR(kIOHIDElementKey
));
78 InitCookies(50, true);
82 for (i
= 0; i
< Array
.Count(); ++i
)
85 (CFNumberRef
) CFDictionaryGetValue((CFDictionaryRef
) Array
[i
], CFSTR(kIOHIDElementUsageKey
)),
86 kCFNumberLongType
, &nUsage
);
89 (CFNumberRef
) CFDictionaryGetValue((CFDictionaryRef
) Array
[i
], CFSTR(kIOHIDElementUsagePageKey
)),
90 kCFNumberLongType
, &nPage
);
92 if (nPage
== kHIDPage_Button
&& nUsage
<= 40)
93 AddCookieInQueue(Array
[i
], nUsage
);
94 else if (nPage
== kHIDPage_GenericDesktop
)
99 AddCookieInQueue(Array
[i
], wxJS_AXIS_X
);
102 AddCookieInQueue(Array
[i
], wxJS_AXIS_Y
);
105 AddCookieInQueue(Array
[i
], wxJS_AXIS_Z
);
111 else if (nPage
== kHIDPage_Simulation
&& nUsage
== kHIDUsage_Sim_Rudder
)
112 AddCookieInQueue(Array
[i
], wxJS_AXIS_RUDDER
);
119 IMPLEMENT_DYNAMIC_CLASS(wxJoystick
, wxObject
)
122 ////////////////////////////////////////////////////////////////////////////
123 // Background thread for reading the joystick device
124 ////////////////////////////////////////////////////////////////////////////
126 class wxJoystickThread
: public wxThread
129 wxJoystickThread(wxHIDJoystick
* hid
, int joystick
);
133 wxHIDJoystick
* m_hid
;
135 wxPoint m_lastposition
;
138 wxWindow
* m_catchwin
;
141 friend class wxJoystick
;
145 wxJoystickThread::wxJoystickThread(wxHIDJoystick
* hid
, int joystick
)
147 m_joystick(joystick
),
148 m_lastposition(wxDefaultPosition
),
153 for (int i
=0; i
<15; i
++)
158 # define wxJSVERIFY(arg) if(!(arg)) {wxLogSysError(wxT(#arg)); return NULL;}
159 # define wxJSASSERT(arg) wxJSVERIFY(arg)
161 void* wxJoystickThread::Entry()
163 CFRunLoopSourceRef pRLSource
= NULL
;
165 wxJSVERIFY( (*m_hid
->GetQueue())->createAsyncEventSource(m_hid
->GetQueue(), &pRLSource
)
166 == kIOReturnSuccess
);
167 wxJSASSERT(pRLSource
!= NULL
);
169 //attach runloop source to main run loop in thread
170 CFRunLoopRef pRL
= CFRunLoopGetCurrent();
171 CFRunLoopAddSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
);
173 // wxJSVERIFY( (*m_hid->GetQueue())->start(m_hid->GetQueue()) == kIOReturnSuccess );
176 IOHIDEventStruct hidevent
;
177 AbsoluteTime bogustime
= {0,0};
186 dTime
= 0.0001 * m_polling
;
188 dTime
= 0.0001 * 10; // check at least every 10 msec in blocking case
190 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, dTime
, m_polling
);
192 while ( (ret
= (*m_hid
->GetQueue())->getNextEvent(m_hid
->GetQueue(),
193 &hidevent
, bogustime
, 0)) != kIOReturnUnderrun
)
198 wxJSASSERT(ret
== kIOReturnSuccess
);
199 wxJoystickEvent wxevent
;
202 IOHIDElementCookie
* pCookies
= m_hid
->GetCookies();
205 if(hidevent
.elementCookie
== pCookies
[nIndex
])
208 wxASSERT(nIndex
!= 50);
214 m_buttons
|= (1 << nIndex
);
215 wxevent
.SetEventType(wxEVT_JOY_BUTTON_DOWN
);
219 m_buttons
&= ~(1 << nIndex
);
220 wxevent
.SetEventType(wxEVT_JOY_BUTTON_UP
);
223 wxevent
.SetButtonChange(nIndex
);
225 else if (nIndex
== wxJS_AXIS_X
)
227 m_lastposition
.x
= hidevent
.value
;
228 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
229 m_axe
[nIndex
- 39] = hidevent
.value
;
231 else if (nIndex
== wxJS_AXIS_Y
)
233 m_lastposition
.y
= hidevent
.value
;
234 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
235 m_axe
[nIndex
- 39] = hidevent
.value
;
237 else if (nIndex
== wxJS_AXIS_Z
)
239 wxevent
.SetEventType(wxEVT_JOY_ZMOVE
);
240 m_axe
[nIndex
- 39] = hidevent
.value
;
243 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
245 Nanoseconds timestamp
= AbsoluteToNanoseconds(hidevent
.timestamp
);
247 wxULongLong
llTime(timestamp
.hi
, timestamp
.lo
);
251 wxevent
.SetTimestamp(llTime
.GetValue());
252 wxevent
.SetJoystick(m_joystick
);
253 wxevent
.SetButtonState(m_buttons
);
254 wxevent
.SetPosition(m_lastposition
);
255 wxevent
.SetZPosition(m_axe
[3]);
256 wxevent
.SetEventObject(m_catchwin
);
259 m_catchwin
->AddPendingEvent(wxevent
);
266 ////////////////////////////////////////////////////////////////////////////
268 wxJoystick::wxJoystick(int joystick
)
269 : m_joystick(joystick
),
272 m_hid
= new wxHIDJoystick();
274 if (m_hid
->Create(m_joystick
))
276 m_thread
= new wxJoystickThread(m_hid
, m_joystick
);
288 wxJoystick::~wxJoystick()
292 m_thread
->Delete(); // It's detached so it will delete itself
299 ////////////////////////////////////////////////////////////////////////////
301 ////////////////////////////////////////////////////////////////////////////
303 wxPoint
wxJoystick::GetPosition() const
305 wxPoint
pos(wxDefaultPosition
);
306 if (m_thread
) pos
= m_thread
->m_lastposition
;
310 int wxJoystick::GetZPosition() const
313 return m_thread
->m_axe
[wxJS_AXIS_Z
];
317 int wxJoystick::GetButtonState() const
320 return m_thread
->m_buttons
;
324 int wxJoystick::GetPOVPosition() const
327 int wxJoystick::GetPOVCTSPosition() const
330 int wxJoystick::GetRudderPosition() const
333 return m_thread
->m_axe
[wxJS_AXIS_RUDDER
];
337 int wxJoystick::GetUPosition() const
340 return m_thread
->m_axe
[wxJS_AXIS_U
];
344 int wxJoystick::GetVPosition() const
347 return m_thread
->m_axe
[wxJS_AXIS_V
];
351 int wxJoystick::GetMovementThreshold() const
354 void wxJoystick::SetMovementThreshold(int threshold
)
357 ////////////////////////////////////////////////////////////////////////////
359 ////////////////////////////////////////////////////////////////////////////
361 bool wxJoystick::IsOk() const
362 { return m_hid
!= NULL
; }
364 int wxJoystick::GetNumberJoysticks() const
365 { return wxHIDDevice::GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
) +
366 wxHIDDevice::GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
); }
368 int wxJoystick::GetManufacturerId() const
371 int wxJoystick::GetProductId() const
374 wxString
wxJoystick::GetProductName() const
375 { return wxT("unknown"); }
377 int wxJoystick::GetXMin() const
378 { return wxJS_AXIS_MIN
; }
380 int wxJoystick::GetYMin() const
381 { return wxJS_AXIS_MIN
; }
383 int wxJoystick::GetZMin() const
384 { return wxJS_AXIS_MIN
; }
386 int wxJoystick::GetXMax() const
387 { return wxJS_AXIS_MAX
; }
389 int wxJoystick::GetYMax() const
390 { return wxJS_AXIS_MAX
; }
392 int wxJoystick::GetZMax() const
393 { return wxJS_AXIS_MAX
; }
395 int wxJoystick::GetNumberButtons() const
399 for(int nIndex
= 0; nIndex
< 40; ++nIndex
)
401 if(m_hid
->HasElement(nIndex
))
408 int wxJoystick::GetNumberAxes() const
412 for(int nIndex
= 40; nIndex
< 50; ++nIndex
)
414 if(m_hid
->HasElement(nIndex
))
424 int wxJoystick::GetMaxButtons() const
427 int wxJoystick::GetMaxAxes() const
430 int wxJoystick::GetPollingMin() const
433 int wxJoystick::GetPollingMax() const
436 int wxJoystick::GetRudderMin() const
437 { return wxJS_AXIS_MIN
; }
439 int wxJoystick::GetRudderMax() const
440 { return wxJS_AXIS_MAX
; }
442 int wxJoystick::GetUMin() const
443 { return wxJS_AXIS_MIN
; }
445 int wxJoystick::GetUMax() const
446 { return wxJS_AXIS_MAX
; }
448 int wxJoystick::GetVMin() const
449 { return wxJS_AXIS_MIN
; }
451 int wxJoystick::GetVMax() const
452 { return wxJS_AXIS_MAX
; }
454 bool wxJoystick::HasRudder() const
455 { return m_hid
->HasElement(wxJS_AXIS_RUDDER
); }
457 bool wxJoystick::HasZ() const
458 { return m_hid
->HasElement(wxJS_AXIS_Z
); }
460 bool wxJoystick::HasU() const
461 { return m_hid
->HasElement(wxJS_AXIS_U
); }
463 bool wxJoystick::HasV() const
464 { return m_hid
->HasElement(wxJS_AXIS_V
); }
466 bool wxJoystick::HasPOV() const
469 bool wxJoystick::HasPOV4Dir() const
472 bool wxJoystick::HasPOVCTS() const
475 ////////////////////////////////////////////////////////////////////////////
477 ////////////////////////////////////////////////////////////////////////////
479 bool wxJoystick::SetCapture(wxWindow
* win
, int pollingFreq
)
483 m_thread
->m_catchwin
= win
;
484 m_thread
->m_polling
= pollingFreq
;
490 bool wxJoystick::ReleaseCapture()
494 m_thread
->m_catchwin
= NULL
;
495 m_thread
->m_polling
= 0;