1
/////////////////////////////////////////////////////////////////////////////
3
// Purpose: wxJoystick class
7
// RCS-ID: $Id: hidjoystick.cpp,v 1.4.2.2 2006/01/19 10:16:32 JS Exp $
8
// Copyright: (c) Ryan Norton
9
// Licence: wxWindows licence
10
/////////////////////////////////////////////////////////////////////////////
12
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13
#pragma implementation "joystick.h"
16
//===========================================================================
18
//===========================================================================
20
//---------------------------------------------------------------------------
21
// Pre-compiled header stuff
22
//---------------------------------------------------------------------------
24
// For compilers that support precompilation, includes "wx.h".
25
#include "wx/wxprec.h"
27
//---------------------------------------------------------------------------
29
//---------------------------------------------------------------------------
31
//we only support HID on OSX (DARWIN), since it requires DARWIN...
32
#if wxUSE_JOYSTICK && defined(__DARWIN__)
34
//---------------------------------------------------------------------------
36
//---------------------------------------------------------------------------
37
#include "wx/event.h" //joystick wxEvents
38
#include "wx/log.h" //logging...
39
#include "wx/joystick.h" //...
40
#include "wx/thread.h" //wxThread for polling thread/ wxCriticalSection
41
#include "wx/window.h" //for wxWindow to "capture" joystick
44
#include "wx/mac/corefoundation/hid.h" //private mac hid stuff
47
#include <CoreServices/CoreServices.h>
48
#include <mach/mach.h>
49
#include <mach/mach_time.h>
52
//---------------------------------------------------------------------------
53
// Definitions/Enumerations
54
//---------------------------------------------------------------------------
56
#define wxJS_MAX_AXES 10 /*max number of axes*/
57
#define wxJS_MAX_BUTTONS 40 /*max number of buttons*/
61
//These are positions within the cookie array
62
//in wxHIDJoystick that the cookies that store the axis' are
71
//---------------------------------------------------------------------------
73
//---------------------------------------------------------------------------
74
class wxHIDJoystick : public wxHIDDevice
80
bool Create(int nWhich);
81
virtual void BuildCookies(wxCFArray& Array);
82
void MakeCookies(wxCFArray& Array);
83
IOHIDElementCookie* GetCookies();
84
IOHIDQueueInterface** GetQueue();
86
int m_nXMax, m_nYMax, m_nZMax, m_nRudderMax, m_nUMax, m_nVMax,
87
m_nXMin, m_nYMin, m_nZMin, m_nRudderMin, m_nUMin, m_nVMin;
89
friend class wxJoystick;
92
//---------------------------------------------------------------------------
94
//---------------------------------------------------------------------------
95
class wxJoystickThread : public wxThread
98
wxJoystickThread(wxHIDJoystick* hid, int joystick);
100
static void HIDCallback(void* target, IOReturn res, void* context, void* sender);
103
wxHIDJoystick* m_hid;
105
wxPoint m_lastposition;
106
int m_axe[wxJS_MAX_AXES];
108
wxWindow* m_catchwin;
111
friend class wxJoystick;
114
//===========================================================================
116
//===========================================================================
118
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
119
// wxGetIntFromCFDictionary
121
// Helper function that gets a integer from a dictionary key
122
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
123
void wxGetIntFromCFDictionary(CFTypeRef cfDict, CFStringRef key, int* pOut)
126
(CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) cfDict,
128
kCFNumberIntType, pOut);
131
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
135
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
137
IMPLEMENT_DYNAMIC_CLASS(wxJoystick, wxObject)
139
//---------------------------------------------------------------------------
140
// wxJoystick Constructor
142
// 1) Initializes member variables
143
// 2) Attempts to create the native HID joystick implementation - if none
144
// could be found (no joysticks, etc.) then it sets it to NULL
145
//---------------------------------------------------------------------------
146
wxJoystick::wxJoystick(int joystick)
147
: m_joystick(joystick),
150
m_hid = new wxHIDJoystick();
152
if (m_hid->Create(m_joystick))
154
m_thread = new wxJoystickThread(m_hid, m_joystick);
165
//---------------------------------------------------------------------------
166
// wxJoystick Destructor
168
// Releases the capture of the thread, deletes it, and deletes
169
// the native implementation.
170
//---------------------------------------------------------------------------
171
wxJoystick::~wxJoystick()
175
m_thread->Delete(); // It's detached so it will delete itself
181
//---------------------------------------------------------------------------
182
// wxJoystick::Get[XXX]Position
184
// Returns the value of an axis that was polled from the thread. In the
185
// case of GetPosition returns the X and Y values in a wxPoint
186
//---------------------------------------------------------------------------
187
wxPoint wxJoystick::GetPosition() const
189
wxPoint pos(wxDefaultPosition);
190
if (m_thread) pos = m_thread->m_lastposition;
193
int wxJoystick::GetZPosition() const
196
return m_thread->m_axe[wxJS_AXIS_Z];
199
int wxJoystick::GetRudderPosition() const
202
return m_thread->m_axe[wxJS_AXIS_RUDDER];
205
int wxJoystick::GetUPosition() const
208
return m_thread->m_axe[wxJS_AXIS_U];
211
int wxJoystick::GetVPosition() const
214
return m_thread->m_axe[wxJS_AXIS_V];
218
//---------------------------------------------------------------------------
219
// wxJoystick::GetButtonState
221
// Returns the state of the buttons in a bitmask as dictated by the
222
// wx manual (the real work takes place in the thread, as always)
223
//---------------------------------------------------------------------------
224
int wxJoystick::GetButtonState() const
227
return m_thread->m_buttons;
231
//---------------------------------------------------------------------------
234
// Returns whether the joystick initialized successfully - in this case
235
// if the native implementation doesn't exist (in constructor)
236
//---------------------------------------------------------------------------
237
bool wxJoystick::IsOk() const
239
return m_hid != NULL;
242
//---------------------------------------------------------------------------
243
// wxJoystick::Get[XXX](Id/Name)
245
// Simple accessors to the native HID implementation
246
//---------------------------------------------------------------------------
247
int wxJoystick::GetManufacturerId() const
248
{ return m_hid->m_nManufacturerId; }
249
int wxJoystick::GetProductId() const
250
{ return m_hid->m_nProductId; }
251
wxString wxJoystick::GetProductName() const
252
{ return m_hid->m_szProductName; }
254
//---------------------------------------------------------------------------
255
// wxJoystick::GetNumberButtons
256
// wxJoystick::GetNumberAxes
258
// Queries the joystick for an active number of buttons/axes.
260
// In the native HID implementation, the cookies:
261
// 0-40 are the buttons of the joystick
262
// 40-50 are the axes of the joystick
264
// These just query the native HID implementation as above.
265
//---------------------------------------------------------------------------
266
int wxJoystick::GetNumberButtons() const
270
for(int nIndex = 0; nIndex < 40; ++nIndex)
272
if(m_hid->HasElement(nIndex))
278
int wxJoystick::GetNumberAxes() const
282
for(int nIndex = 40; nIndex < 50; ++nIndex)
284
if(m_hid->HasElement(nIndex))
291
//---------------------------------------------------------------------------
292
// wxJoystick::GetNumberJoysticks
294
// Gets the number of joysticks on the system. In HID that
295
// is all devices with the kHIDUsage_GD_Joystick or kHIDUsage_GD_GamePad
297
//---------------------------------------------------------------------------
298
int wxJoystick::GetNumberJoysticks() const
301
wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick) +
302
wxHIDDevice::GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
305
//---------------------------------------------------------------------------
306
// wxJoystick::SetCapture
308
// Stops sending events from the thread to the window set in
309
// SetCapture and stops polling the joystick
310
//---------------------------------------------------------------------------
311
bool wxJoystick::SetCapture(wxWindow* win, int pollingFreq)
315
m_thread->m_catchwin = win;
316
m_thread->m_polling = pollingFreq;
322
//---------------------------------------------------------------------------
323
// wxJoystick::ReleaseCapture
325
// Stops sending events from the thread to the window set in
326
// SetCapture and stops polling the joystick
327
//---------------------------------------------------------------------------
328
bool wxJoystick::ReleaseCapture()
332
m_thread->m_catchwin = NULL;
333
m_thread->m_polling = 0;
339
//---------------------------------------------------------------------------
340
// wxJoystick::Get[XXX]
342
// Gets the minimum and maximum values for each axis, returning 0 if the
343
// axis doesn't exist.
344
//---------------------------------------------------------------------------
345
int wxJoystick::GetXMin() const
346
{ return m_hid->m_nXMin; }
347
int wxJoystick::GetYMin() const
348
{ return m_hid->m_nYMin; }
349
int wxJoystick::GetZMin() const
350
{ return m_hid->m_nZMin; }
351
int wxJoystick::GetRudderMin() const
352
{ return m_hid->m_nRudderMin; }
353
int wxJoystick::GetUMin() const
354
{ return m_hid->m_nUMin; }
355
int wxJoystick::GetVMin() const
356
{ return m_hid->m_nVMin; }
358
int wxJoystick::GetXMax() const
359
{ return m_hid->m_nXMax; }
360
int wxJoystick::GetYMax() const
361
{ return m_hid->m_nYMax; }
362
int wxJoystick::GetZMax() const
363
{ return m_hid->m_nZMax; }
364
int wxJoystick::GetRudderMax() const
365
{ return m_hid->m_nRudderMax; }
366
int wxJoystick::GetUMax() const
367
{ return m_hid->m_nUMax; }
368
int wxJoystick::GetVMax() const
369
{ return m_hid->m_nVMax; }
371
//---------------------------------------------------------------------------
372
// wxJoystick::Get[XXX]
374
// Min/Max values for buttons, axes, etc.. Polling in this case is just
375
// what the linux port has.
376
//---------------------------------------------------------------------------
377
int wxJoystick::GetMaxButtons() const
378
{ return wxJS_MAX_BUTTONS; }
379
int wxJoystick::GetMaxAxes() const
380
{ return wxJS_MAX_AXES; }
381
int wxJoystick::GetPollingMin() const
383
int wxJoystick::GetPollingMax() const
386
//---------------------------------------------------------------------------
387
// wxJoystick::Has[XXX]
389
// Just queries the native hid implementation if the cookie was found
390
// when enumerating the cookies of the joystick device
391
//---------------------------------------------------------------------------
392
bool wxJoystick::HasZ() const
393
{ return m_hid->HasElement(wxJS_AXIS_Z); }
394
bool wxJoystick::HasRudder() const
395
{ return m_hid->HasElement(wxJS_AXIS_RUDDER); }
396
bool wxJoystick::HasU() const
397
{ return m_hid->HasElement(wxJS_AXIS_U); }
398
bool wxJoystick::HasV() const
399
{ return m_hid->HasElement(wxJS_AXIS_V); }
401
//---------------------------------------------------------------------------
403
//---------------------------------------------------------------------------
404
int wxJoystick::GetPOVPosition() const
406
int wxJoystick::GetPOVCTSPosition() const
408
int wxJoystick::GetMovementThreshold() const
410
void wxJoystick::SetMovementThreshold(int threshold)
412
bool wxJoystick::HasPOV() const
414
bool wxJoystick::HasPOV4Dir() const
416
bool wxJoystick::HasPOVCTS() const
419
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
423
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
425
//---------------------------------------------------------------------------
426
// wxHIDJoystick ctor
428
// Initializes the min/max members
429
//---------------------------------------------------------------------------
430
wxHIDJoystick::wxHIDJoystick() :
431
m_nXMax(0), m_nYMax(0), m_nZMax(0), m_nRudderMax(0), m_nUMax(0), m_nVMax(0),
432
m_nXMin(0), m_nYMin(0), m_nZMin(0), m_nRudderMin(0), m_nUMin(0), m_nVMin(0)
436
//---------------------------------------------------------------------------
437
// wxHIDJoystick dtor
440
//---------------------------------------------------------------------------
441
wxHIDJoystick::~wxHIDJoystick()
445
//---------------------------------------------------------------------------
446
// wxHIDJoystick::Create
448
// Creates the native HID device (joysticks are of either
449
// kHIDUsage_GD_Joystick or kHIDUsage_GD_GamePad)
450
//---------------------------------------------------------------------------
451
bool wxHIDJoystick::Create(int nWhich)
453
int nJoysticks = GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
455
if (nWhich <= nJoysticks)
456
return wxHIDDevice::Create(kHIDPage_GenericDesktop, kHIDUsage_GD_Joystick);
458
nWhich -= nJoysticks;
460
int nGamePads = GetCount(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
462
if (nWhich <= nGamePads)
463
return wxHIDDevice::Create(kHIDPage_GenericDesktop, kHIDUsage_GD_GamePad);
468
//---------------------------------------------------------------------------
469
// wxHIDJoystick::BuildCookies
470
// wxHIDJoystick::MakeCookies
472
// Sets up the cookies for the HID device (called from Create) - as
473
// mentioned 0-40 are the buttons and 40-50 are the axes.
475
// MakeCookies is just a recursive function for each array within
477
//---------------------------------------------------------------------------
478
void wxHIDJoystick::BuildCookies(wxCFArray& Array)
480
Array = CFDictionaryGetValue((CFDictionaryRef)Array[0], CFSTR(kIOHIDElementKey));
481
InitCookies(50, true);
483
memset(m_pCookies, 0, sizeof(*m_pCookies) * 50);
486
// I wasted two hours of my life on this line :(
487
// accidently removed it during some source cleaning...
491
//paranoid debugging stuff
493
for(int i = 0; i < 50; ++i)
494
wxPrintf(wxT("\nVAL #%i:[%i]"), i, m_pCookies[i]);
498
void wxHIDJoystick::MakeCookies(wxCFArray& Array)
500
int i, nUsage, nPage;
502
for (i = 0; i < Array.Count(); ++i)
504
const void* ref = CFDictionaryGetValue((CFDictionaryRef)Array[i], CFSTR(kIOHIDElementKey));
508
wxCFArray newarray(ref);
509
MakeCookies(newarray);
514
(CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) Array[i], CFSTR(kIOHIDElementUsageKey)),
515
kCFNumberIntType, &nUsage);
518
(CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) Array[i], CFSTR(kIOHIDElementUsagePageKey)),
519
kCFNumberIntType, &nPage);
522
wxLogSysError(wxT("[%i][%i]"), nUsage, nPage);
524
if (nPage == kHIDPage_Button && nUsage <= 40)
525
AddCookieInQueue(Array[i], nUsage-1 );
526
else if (nPage == kHIDPage_GenericDesktop)
532
AddCookieInQueue(Array[i], wxJS_AXIS_X);
533
wxGetIntFromCFDictionary(Array[i], CFSTR(kIOHIDElementMaxKey),
535
wxGetIntFromCFDictionary(Array[i], CFSTR(kIOHIDElementMinKey),
539
AddCookieInQueue(Array[i], wxJS_AXIS_Y);
540
wxGetIntFromCFDictionary(Array[i], CFSTR(kIOHIDElementMaxKey),
542
wxGetIntFromCFDictionary(Array[i], CFSTR(kIOHIDElementMinKey),
546
AddCookieInQueue(Array[i], wxJS_AXIS_Z);
547
wxGetIntFromCFDictionary(Array[i], CFSTR(kIOHIDElementMaxKey),
549
wxGetIntFromCFDictionary(Array[i], CFSTR(kIOHIDElementMinKey),
556
else if (nPage == kHIDPage_Simulation && nUsage == kHIDUsage_Sim_Rudder)
559
AddCookieInQueue(Array[i], wxJS_AXIS_RUDDER );
560
wxGetIntFromCFDictionary(Array[i], CFSTR(kIOHIDElementMaxKey),
562
wxGetIntFromCFDictionary(Array[i], CFSTR(kIOHIDElementMinKey),
569
//---------------------------------------------------------------------------
570
// wxHIDJoystick::Get[XXX]
572
// Simple accessors so that the HID callback and the thread procedure
573
// can access members from wxHIDDevice (our parent here).
574
//---------------------------------------------------------------------------
575
IOHIDElementCookie* wxHIDJoystick::GetCookies()
576
{ return m_pCookies; }
577
IOHIDQueueInterface** wxHIDJoystick::GetQueue()
578
{ return m_ppQueue; }
580
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
584
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
586
//---------------------------------------------------------------------------
587
// wxJoystickThread Constructor
589
// Just initializes members
590
//---------------------------------------------------------------------------
591
wxJoystickThread::wxJoystickThread(wxHIDJoystick* hid, int joystick)
593
m_joystick(joystick),
594
m_lastposition(127,127),
599
memset(m_axe, 0, sizeof(int) * wxJS_MAX_AXES);
602
//---------------------------------------------------------------------------
603
// wxJoystickThread::Entry
607
// Runs a CFRunLoop for polling. Basically, it sets the HID queue to
608
// call wxJoystickThread::HIDCallback in the context of this thread
609
// when something changes on the device. It polls as long as the user
610
// wants, or a certain amount if the user wants to "block". Note that
611
// we don't actually block here since this is in a secondary thread.
612
//---------------------------------------------------------------------------
613
void* wxJoystickThread::Entry()
615
CFRunLoopSourceRef pRLSource = NULL;
617
if ((*m_hid->GetQueue())->createAsyncEventSource(
618
m_hid->GetQueue(), &pRLSource) != kIOReturnSuccess )
620
wxLogSysError(wxT("Couldn't create async event source"));
624
wxASSERT(pRLSource != NULL);
626
//attach runloop source to main run loop in thread
627
CFRunLoopRef pRL = CFRunLoopGetCurrent();
628
CFRunLoopAddSource(pRL, pRLSource, kCFRunLoopDefaultMode);
629
wxASSERT( CFRunLoopContainsSource(pRL, pRLSource, kCFRunLoopDefaultMode) );
632
if( (*m_hid->GetQueue())->setEventCallout(m_hid->GetQueue(),
633
wxJoystickThread::HIDCallback, this, this) != kIOReturnSuccess )
635
wxLogSysError(wxT("Could not set event callout for queue"));
639
if( (*m_hid->GetQueue())->start(m_hid->GetQueue()) != kIOReturnSuccess )
641
wxLogSysError(wxT("Could not start queue"));
653
dTime = 0.0001 * m_polling;
655
dTime = 0.0001 * 10; // check at least every 10 msec in "blocking" case
657
//true just "handles and returns" - false forces it to stay the time
660
CFRunLoopRunInMode(kCFRunLoopDefaultMode, dTime, true);
663
HIDCallback(this, ret, this, this);
668
wxASSERT( CFRunLoopContainsSource(pRL, pRLSource, kCFRunLoopDefaultMode) );
670
CFRunLoopRemoveSource(pRL, pRLSource, kCFRunLoopDefaultMode);
671
CFRelease(pRLSource);
676
//---------------------------------------------------------------------------
677
// wxJoystickThread::HIDCallback (static)
679
// Callback for the native HID device when it recieves input.
681
// This is where the REAL dirty work gets done.
683
// 1) Loops through each event the queue has recieved
684
// 2) First, checks if the thread that is running the loop for
685
// the polling has ended - if so it breaks out
686
// 3) Next, it checks if there was an error getting this event from
687
// the HID queue, if there was, it logs an error and returns
688
// 4) Now it does the real dirty work by getting the button states
689
// from cookies 0-40 and axes positions/states from cookies 40-50
690
// in the native HID device by quering cookie values.
691
// 5) Sends the event to the polling window (if any)
692
// 6) Gets the next event and goes back to (1)
693
//---------------------------------------------------------------------------
694
/*static*/ void wxJoystickThread::HIDCallback(void* target, IOReturn res,
695
void* context, void* sender)
697
IOHIDEventStruct hidevent;
698
AbsoluteTime bogustime = {0,0};
700
wxJoystickThread* pThis = (wxJoystickThread*) context;
701
wxHIDJoystick* m_hid = pThis->m_hid;
703
//Get the "first" event from the queue
704
//bogustime tells it we don't care at what time to start
705
//where it gets the next from
706
ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(),
707
&hidevent, bogustime, 0);
709
while (ret != kIOReturnUnderrun)
711
if (pThis->TestDestroy())
714
if(ret != kIOReturnSuccess)
716
wxLogSysError(wxString::Format(wxT("wxJoystick Error:[%i]"), ret));
720
wxJoystickEvent wxevent;
722
//Find the cookie that changed
724
IOHIDElementCookie* pCookies = m_hid->GetCookies();
727
if(hidevent.elementCookie == pCookies[nIndex])
737
wxLogSysError(wxString::Format(wxT("wxJoystick Out Of Bounds Error")));
742
//is the cookie a button?
747
pThis->m_buttons |= (1 << nIndex);
748
wxevent.SetEventType(wxEVT_JOY_BUTTON_DOWN);
752
pThis->m_buttons &= ~(1 << nIndex);
753
wxevent.SetEventType(wxEVT_JOY_BUTTON_UP);
756
wxevent.SetButtonChange(nIndex+1);
758
else if (nIndex == wxJS_AXIS_X)
760
pThis->m_lastposition.x = hidevent.value;
761
wxevent.SetEventType(wxEVT_JOY_MOVE);
762
pThis->m_axe[0] = hidevent.value;
764
else if (nIndex == wxJS_AXIS_Y)
766
pThis->m_lastposition.y = hidevent.value;
767
wxevent.SetEventType(wxEVT_JOY_MOVE);
768
pThis->m_axe[1] = hidevent.value;
770
else if (nIndex == wxJS_AXIS_Z)
772
wxevent.SetEventType(wxEVT_JOY_ZMOVE);
773
pThis->m_axe[2] = hidevent.value;
776
wxevent.SetEventType(wxEVT_JOY_MOVE);
778
Nanoseconds timestamp = AbsoluteToNanoseconds(hidevent.timestamp);
780
wxULongLong llTime(timestamp.hi, timestamp.lo);
784
wxevent.SetTimestamp(llTime.GetValue());
785
wxevent.SetJoystick(pThis->m_joystick);
786
wxevent.SetButtonState(pThis->m_buttons);
787
wxevent.SetPosition(pThis->m_lastposition);
788
wxevent.SetZPosition(pThis->m_axe[2]);
789
wxevent.SetEventObject(pThis->m_catchwin);
791
if (pThis->m_catchwin)
792
pThis->m_catchwin->AddPendingEvent(wxevent);
794
ret = (*m_hid->GetQueue())->getNextEvent(m_hid->GetQueue(),
795
&hidevent, bogustime, 0);
799
#endif // wxUSE_JOYSTICK && defined(__DARWIN__)