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__)