1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxJoystick class
8 // Copyright: (c) Ryan Norton
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 //===========================================================================
14 //===========================================================================
16 //---------------------------------------------------------------------------
17 // Pre-compiled header stuff
18 //---------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
23 //---------------------------------------------------------------------------
25 //---------------------------------------------------------------------------
27 //we only support HID on OSX (DARWIN), since it requires DARWIN...
28 #if wxUSE_JOYSTICK && defined(__DARWIN__)
30 //---------------------------------------------------------------------------
32 //---------------------------------------------------------------------------
33 #include "wx/event.h" //joystick wxEvents
34 #include "wx/log.h" //logging...
35 #include "wx/joystick.h" //...
36 #include "wx/thread.h" //wxThread for polling thread/ wxCriticalSection
37 #include "wx/window.h" //for wxWindow to "capture" joystick
40 #include "wx/mac/corefoundation/hid.h" //private mac hid stuff
43 #include <CoreServices/CoreServices.h>
44 #include <mach/mach.h>
45 #include <mach/mach_time.h>
48 //---------------------------------------------------------------------------
49 // Definitions/Enumerations
50 //---------------------------------------------------------------------------
52 #define wxJS_MAX_AXES 10 /*max number of axes*/
53 #define wxJS_MAX_BUTTONS 40 /*max number of buttons*/
57 //These are positions within the cookie array
58 //in wxHIDJoystick that the cookies that store the axis' are
66 //For the Get[XXX](Min/Max) functions
67 wxJS_AXIS_MAX
= 255, //32767,
68 wxJS_AXIS_MIN
= 0, //-32767
71 //---------------------------------------------------------------------------
73 //---------------------------------------------------------------------------
74 class wxHIDJoystick
: public wxHIDDevice
77 bool Create(int nWhich
);
78 virtual void BuildCookies(wxCFArray
& Array
);
79 void MakeCookies(wxCFArray
& Array
);
80 IOHIDElementCookie
* GetCookies();
81 IOHIDQueueInterface
** GetQueue();
83 friend class wxJoystick
;
86 //---------------------------------------------------------------------------
88 //---------------------------------------------------------------------------
89 class wxJoystickThread
: public wxThread
92 wxJoystickThread(wxHIDJoystick
* hid
, int joystick
);
94 static void HIDCallback(void* target
, IOReturn res
, void* context
, void* sender
);
99 wxPoint m_lastposition
;
100 int m_axe
[wxJS_MAX_AXES
];
102 wxWindow
* m_catchwin
;
105 friend class wxJoystick
;
108 //===========================================================================
110 //===========================================================================
112 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
116 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
118 IMPLEMENT_DYNAMIC_CLASS(wxJoystick
, wxObject
)
120 //---------------------------------------------------------------------------
121 // wxJoystick Constructor
123 // 1) Initializes member variables
124 // 2) Attempts to create the native HID joystick implementation - if none
125 // could be found (no joysticks, etc.) then it sets it to NULL
126 //---------------------------------------------------------------------------
127 wxJoystick::wxJoystick(int joystick
)
128 : m_joystick(joystick
),
131 m_hid
= new wxHIDJoystick();
133 if (m_hid
->Create(m_joystick
))
135 m_thread
= new wxJoystickThread(m_hid
, m_joystick
);
146 //---------------------------------------------------------------------------
147 // wxJoystick Destructor
149 // Releases the capture of the thread, deletes it, and deletes
150 // the native implementation.
151 //---------------------------------------------------------------------------
152 wxJoystick::~wxJoystick()
156 m_thread
->Delete(); // It's detached so it will delete itself
162 //---------------------------------------------------------------------------
163 // wxJoystick::Get[XXX]Position
165 // Returns the value of an axis that was polled from the thread. In the
166 // case of GetPosition returns the X and Y values in a wxPoint
167 //---------------------------------------------------------------------------
168 wxPoint
wxJoystick::GetPosition() const
170 wxPoint
pos(wxDefaultPosition
);
171 if (m_thread
) pos
= m_thread
->m_lastposition
;
174 int wxJoystick::GetZPosition() const
177 return m_thread
->m_axe
[wxJS_AXIS_Z
];
180 int wxJoystick::GetRudderPosition() const
183 return m_thread
->m_axe
[wxJS_AXIS_RUDDER
];
186 int wxJoystick::GetUPosition() const
189 return m_thread
->m_axe
[wxJS_AXIS_U
];
192 int wxJoystick::GetVPosition() const
195 return m_thread
->m_axe
[wxJS_AXIS_V
];
199 //---------------------------------------------------------------------------
200 // wxJoystick::GetButtonState
202 // Returns the state of the buttons in a bitmask as dictated by the
203 // wx manual (the real work takes place in the thread, as always)
204 //---------------------------------------------------------------------------
205 int wxJoystick::GetButtonState() const
208 return m_thread
->m_buttons
;
212 //---------------------------------------------------------------------------
215 // Returns whether the joystick initialized successfully - in this case
216 // if the native implementation doesn't exist (in constructor)
217 //---------------------------------------------------------------------------
218 bool wxJoystick::IsOk() const
220 return m_hid
!= NULL
;
223 //---------------------------------------------------------------------------
224 // wxJoystick::Get[XXX](Id/Name)
226 // Simple accessors to the native HID implementation
227 //---------------------------------------------------------------------------
228 int wxJoystick::GetManufacturerId() const
229 { return m_hid
->m_nManufacturerId
; }
230 int wxJoystick::GetProductId() const
231 { return m_hid
->m_nProductId
; }
232 wxString
wxJoystick::GetProductName() const
233 { return m_hid
->m_szProductName
; }
235 //---------------------------------------------------------------------------
236 // wxJoystick::GetNumberButtons
237 // wxJoystick::GetNumberAxes
239 // Queries the joystick for an active number of buttons/axes.
241 // In the native HID implementation, the cookies:
242 // 0-40 are the buttons of the joystick
243 // 40-50 are the axes of the joystick
245 // These just query the native HID implementation as above.
246 //---------------------------------------------------------------------------
247 int wxJoystick::GetNumberButtons() const
251 for(int nIndex
= 0; nIndex
< 40; ++nIndex
)
253 if(m_hid
->HasElement(nIndex
))
259 int wxJoystick::GetNumberAxes() const
263 for(int nIndex
= 40; nIndex
< 50; ++nIndex
)
265 if(m_hid
->HasElement(nIndex
))
272 //---------------------------------------------------------------------------
273 // wxJoystick::GetNumberJoysticks
275 // Gets the number of joysticks on the system. In HID that
276 // is all devices with the kHIDUsage_GD_Joystick or kHIDUsage_GD_GamePad
278 //---------------------------------------------------------------------------
279 int wxJoystick::GetNumberJoysticks() const
282 wxHIDDevice::GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
) +
283 wxHIDDevice::GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
);
286 //---------------------------------------------------------------------------
287 // wxJoystick::SetCapture
289 // Stops sending events from the thread to the window set in
290 // SetCapture and stops polling the joystick
291 //---------------------------------------------------------------------------
292 bool wxJoystick::SetCapture(wxWindow
* win
, int pollingFreq
)
296 m_thread
->m_catchwin
= win
;
297 m_thread
->m_polling
= pollingFreq
;
303 //---------------------------------------------------------------------------
304 // wxJoystick::ReleaseCapture
306 // Stops sending events from the thread to the window set in
307 // SetCapture and stops polling the joystick
308 //---------------------------------------------------------------------------
309 bool wxJoystick::ReleaseCapture()
313 m_thread
->m_catchwin
= NULL
;
314 m_thread
->m_polling
= 0;
320 //---------------------------------------------------------------------------
321 // wxJoystick::Get[XXX]
323 // All values in hid range from 0 to 255, making these all kind of
324 // superflous. These are mainly here due to the msw-centric api
325 // that wxJoystick has... it should REALLY do its own scaling... oh well :)
326 //---------------------------------------------------------------------------
327 int wxJoystick::GetXMin() const
328 { return wxJS_AXIS_MIN
; }
329 int wxJoystick::GetYMin() const
330 { return wxJS_AXIS_MIN
; }
331 int wxJoystick::GetZMin() const
332 { return wxJS_AXIS_MIN
; }
333 int wxJoystick::GetUMin() const
334 { return wxJS_AXIS_MIN
; }
335 int wxJoystick::GetVMin() const
336 { return wxJS_AXIS_MIN
; }
337 int wxJoystick::GetRudderMin() const
338 { return wxJS_AXIS_MIN
; }
340 int wxJoystick::GetXMax() const
341 { return wxJS_AXIS_MAX
; }
342 int wxJoystick::GetYMax() const
343 { return wxJS_AXIS_MAX
; }
344 int wxJoystick::GetZMax() const
345 { return wxJS_AXIS_MAX
; }
346 int wxJoystick::GetUMax() const
347 { return wxJS_AXIS_MAX
; }
348 int wxJoystick::GetVMax() const
349 { return wxJS_AXIS_MAX
; }
350 int wxJoystick::GetRudderMax() const
351 { return wxJS_AXIS_MAX
; }
353 //---------------------------------------------------------------------------
354 // wxJoystick::Get[XXX]
356 // Min/Max values for buttons, axes, etc.. Polling in this case is just
357 // what the linux port has.
358 //---------------------------------------------------------------------------
359 int wxJoystick::GetMaxButtons() const
360 { return wxJS_MAX_BUTTONS
; }
361 int wxJoystick::GetMaxAxes() const
362 { return wxJS_MAX_AXES
; }
363 int wxJoystick::GetPollingMin() const
365 int wxJoystick::GetPollingMax() const
368 //---------------------------------------------------------------------------
369 // wxJoystick::Has[XXX]
371 // Just queries the native hid implementation if the cookie was found
372 // when enumerating the cookies of the joystick device
373 //---------------------------------------------------------------------------
374 bool wxJoystick::HasRudder() const
375 { return m_hid
->HasElement(wxJS_AXIS_RUDDER
); }
376 bool wxJoystick::HasZ() const
377 { return m_hid
->HasElement(wxJS_AXIS_Z
); }
378 bool wxJoystick::HasU() const
379 { return m_hid
->HasElement(wxJS_AXIS_U
); }
380 bool wxJoystick::HasV() const
381 { return m_hid
->HasElement(wxJS_AXIS_V
); }
383 //---------------------------------------------------------------------------
385 //---------------------------------------------------------------------------
386 int wxJoystick::GetPOVPosition() const
388 int wxJoystick::GetPOVCTSPosition() const
390 int wxJoystick::GetMovementThreshold() const
392 void wxJoystick::SetMovementThreshold(int threshold
)
394 bool wxJoystick::HasPOV() const
396 bool wxJoystick::HasPOV4Dir() const
398 bool wxJoystick::HasPOVCTS() const
401 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
405 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
407 //---------------------------------------------------------------------------
408 // wxHIDJoystick::Create
410 // Creates the native HID device (joysticks are of either
411 // kHIDUsage_GD_Joystick or kHIDUsage_GD_GamePad)
412 //---------------------------------------------------------------------------
413 bool wxHIDJoystick::Create(int nWhich
)
415 int nJoysticks
= GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
);
417 if (nWhich
<= nJoysticks
)
418 return wxHIDDevice::Create(kHIDPage_GenericDesktop
, kHIDUsage_GD_Joystick
);
420 nWhich
-= nJoysticks
;
422 int nGamePads
= GetCount(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
);
424 if (nWhich
<= nGamePads
)
425 return wxHIDDevice::Create(kHIDPage_GenericDesktop
, kHIDUsage_GD_GamePad
);
430 //---------------------------------------------------------------------------
431 // wxHIDJoystick::BuildCookies
432 // wxHIDJoystick::MakeCookies
434 // Sets up the cookies for the HID device (called from Create) - as
435 // mentioned 0-40 are the buttons and 40-50 are the axes.
437 // MakeCookies is just a recursive function for each array within
439 //---------------------------------------------------------------------------
440 void wxHIDJoystick::BuildCookies(wxCFArray
& Array
)
442 Array
= CFDictionaryGetValue((CFDictionaryRef
)Array
[0], CFSTR(kIOHIDElementKey
));
443 InitCookies(50, true);
445 memset(m_pCookies
, 0, sizeof(*m_pCookies
) * 50);
448 // I wasted two hours of my life on this line :(
449 // accidently removed it during some source cleaning...
453 //paranoid debugging stuff
455 for(int i
= 0; i
< 50; ++i
)
456 wxPrintf(wxT("\nVAL #%i:[%i]"), i
, m_pCookies
[i
]);
460 void wxHIDJoystick::MakeCookies(wxCFArray
& Array
)
462 int i
, nUsage
, nPage
;
464 for (i
= 0; i
< Array
.Count(); ++i
)
466 const void* ref
= CFDictionaryGetValue((CFDictionaryRef
)Array
[i
], CFSTR(kIOHIDElementKey
));
470 wxCFArray
newarray(ref
);
471 MakeCookies(newarray
);
476 (CFNumberRef
) CFDictionaryGetValue((CFDictionaryRef
) Array
[i
], CFSTR(kIOHIDElementUsageKey
)),
477 kCFNumberLongType
, &nUsage
);
480 (CFNumberRef
) CFDictionaryGetValue((CFDictionaryRef
) Array
[i
], CFSTR(kIOHIDElementUsagePageKey
)),
481 kCFNumberLongType
, &nPage
);
484 wxLogSysError(wxT("[%i][%i]"), nUsage
, nPage
);
486 if (nPage
== kHIDPage_Button
&& nUsage
<= 40)
487 AddCookieInQueue(Array
[i
], nUsage
-1 );
488 else if (nPage
== kHIDPage_GenericDesktop
)
493 AddCookieInQueue(Array
[i
], wxJS_AXIS_X
);
496 AddCookieInQueue(Array
[i
], wxJS_AXIS_Y
);
499 AddCookieInQueue(Array
[i
], wxJS_AXIS_Z
);
505 else if (nPage
== kHIDPage_Simulation
&& nUsage
== kHIDUsage_Sim_Rudder
)
506 AddCookieInQueue(Array
[i
], wxJS_AXIS_RUDDER
);
511 //---------------------------------------------------------------------------
512 // wxHIDJoystick::Get[XXX]
514 // Simple accessors so that the HID callback and the thread procedure
515 // can access members from wxHIDDevice (our parent here).
516 //---------------------------------------------------------------------------
517 IOHIDElementCookie
* wxHIDJoystick::GetCookies()
518 { return m_pCookies
; }
519 IOHIDQueueInterface
** wxHIDJoystick::GetQueue()
520 { return m_ppQueue
; }
522 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
526 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
528 //---------------------------------------------------------------------------
529 // wxJoystickThread Constructor
531 // Just initializes members
532 //---------------------------------------------------------------------------
533 wxJoystickThread::wxJoystickThread(wxHIDJoystick
* hid
, int joystick
)
535 m_joystick(joystick
),
536 m_lastposition(127,127),
541 memset(m_axe
, 0, sizeof(int) * wxJS_MAX_AXES
);
544 //---------------------------------------------------------------------------
545 // wxJoystickThread::Entry
549 // Runs a CFRunLoop for polling. Basically, it sets the HID queue to
550 // call wxJoystickThread::HIDCallback in the context of this thread
551 // when something changes on the device. It polls as long as the user
552 // wants, or a certain amount if the user wants to "block". Note that
553 // we don't actually block here since this is in a secondary thread.
554 //---------------------------------------------------------------------------
555 void* wxJoystickThread::Entry()
557 CFRunLoopSourceRef pRLSource
= NULL
;
559 if ((*m_hid
->GetQueue())->createAsyncEventSource(
560 m_hid
->GetQueue(), &pRLSource
) != kIOReturnSuccess
)
562 wxLogSysError(wxT("Couldn't create async event source"));
566 wxASSERT(pRLSource
!= NULL
);
568 //attach runloop source to main run loop in thread
569 CFRunLoopRef pRL
= CFRunLoopGetCurrent();
570 CFRunLoopAddSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
);
571 wxASSERT( CFRunLoopContainsSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
) );
574 if( (*m_hid
->GetQueue())->setEventCallout(m_hid
->GetQueue(),
575 wxJoystickThread::HIDCallback
, this, this) != kIOReturnSuccess
)
577 wxLogSysError(wxT("Could not set event callout for queue"));
581 if( (*m_hid
->GetQueue())->start(m_hid
->GetQueue()) != kIOReturnSuccess
)
583 wxLogSysError(wxT("Could not start queue"));
595 dTime
= 0.0001 * m_polling
;
597 dTime
= 0.0001 * 10; // check at least every 10 msec in "blocking" case
599 //true just "handles and returns" - false forces it to stay the time
602 CFRunLoopRunInMode(kCFRunLoopDefaultMode
, dTime
, true);
605 HIDCallback(this, ret
, this, this);
610 wxASSERT( CFRunLoopContainsSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
) );
612 CFRunLoopRemoveSource(pRL
, pRLSource
, kCFRunLoopDefaultMode
);
613 CFRelease(pRLSource
);
618 //---------------------------------------------------------------------------
619 // wxJoystickThread::HIDCallback (static)
621 // Callback for the native HID device when it recieves input.
623 // This is where the REAL dirty work gets done.
625 // 1) Loops through each event the queue has recieved
626 // 2) First, checks if the thread that is running the loop for
627 // the polling has ended - if so it breaks out
628 // 3) Next, it checks if there was an error getting this event from
629 // the HID queue, if there was, it logs an error and returns
630 // 4) Now it does the real dirty work by getting the button states
631 // from cookies 0-40 and axes positions/states from cookies 40-50
632 // in the native HID device by quering cookie values.
633 // 5) Sends the event to the polling window (if any)
634 // 6) Gets the next event and goes back to (1)
635 //---------------------------------------------------------------------------
636 /*static*/ void wxJoystickThread::HIDCallback(void* target
, IOReturn res
,
637 void* context
, void* sender
)
639 IOHIDEventStruct hidevent
;
640 AbsoluteTime bogustime
= {0,0};
642 wxJoystickThread
* pThis
= (wxJoystickThread
*) context
;
643 wxHIDJoystick
* m_hid
= pThis
->m_hid
;
645 //Get the "first" event from the queue
646 //bogustime tells it we don't care at what time to start
647 //where it gets the next from
648 ret
= (*m_hid
->GetQueue())->getNextEvent(m_hid
->GetQueue(),
649 &hidevent
, bogustime
, 0);
651 while (ret
!= kIOReturnUnderrun
)
653 if (pThis
->TestDestroy())
656 if(ret
!= kIOReturnSuccess
)
658 wxLogSysError(wxString::Format(wxT("wxJoystick Error:[%i]"), ret
));
662 wxJoystickEvent wxevent
;
664 //Find the cookie that changed
666 IOHIDElementCookie
* pCookies
= m_hid
->GetCookies();
669 if(hidevent
.elementCookie
== pCookies
[nIndex
])
679 wxLogSysError(wxString::Format(wxT("wxJoystick Out Of Bounds Error")));
684 //is the cookie a button?
689 pThis
->m_buttons
|= (1 << nIndex
);
690 wxevent
.SetEventType(wxEVT_JOY_BUTTON_DOWN
);
694 pThis
->m_buttons
&= ~(1 << nIndex
);
695 wxevent
.SetEventType(wxEVT_JOY_BUTTON_UP
);
698 wxevent
.SetButtonChange(nIndex
+1);
700 else if (nIndex
== wxJS_AXIS_X
)
702 pThis
->m_lastposition
.x
= hidevent
.value
;
703 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
704 pThis
->m_axe
[0] = hidevent
.value
;
706 else if (nIndex
== wxJS_AXIS_Y
)
708 pThis
->m_lastposition
.y
= hidevent
.value
;
709 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
710 pThis
->m_axe
[1] = hidevent
.value
;
712 else if (nIndex
== wxJS_AXIS_Z
)
714 wxevent
.SetEventType(wxEVT_JOY_ZMOVE
);
715 pThis
->m_axe
[2] = hidevent
.value
;
718 wxevent
.SetEventType(wxEVT_JOY_MOVE
);
720 Nanoseconds timestamp
= AbsoluteToNanoseconds(hidevent
.timestamp
);
722 wxULongLong
llTime(timestamp
.hi
, timestamp
.lo
);
726 wxevent
.SetTimestamp(llTime
.GetValue());
727 wxevent
.SetJoystick(pThis
->m_joystick
);
728 wxevent
.SetButtonState(pThis
->m_buttons
);
729 wxevent
.SetPosition(pThis
->m_lastposition
);
730 wxevent
.SetZPosition(pThis
->m_axe
[2]);
731 wxevent
.SetEventObject(pThis
->m_catchwin
);
733 if (pThis
->m_catchwin
)
734 pThis
->m_catchwin
->AddPendingEvent(wxevent
);
736 ret
= (*m_hid
->GetQueue())->getNextEvent(m_hid
->GetQueue(),
737 &hidevent
, bogustime
, 0);
741 #endif // wxUSE_JOYSTICK && defined(__DARWIN__)