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;