1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/osx/core/hid.cpp 
   3 // Purpose:     DARWIN HID layer for WX Implementation 
   7 // Copyright:   (c) Ryan Norton 
   8 // Licence:     wxWindows licence 
   9 ///////////////////////////////////////////////////////////////////////////// 
  11 // =========================================================================== 
  13 // =========================================================================== 
  15 // --------------------------------------------------------------------------- 
  17 // --------------------------------------------------------------------------- 
  19 // For compilers that support precompilation, includes "wx.h". 
  20 #include "wx/wxprec.h" 
  26 #if wxOSX_USE_COCOA_OR_CARBON 
  28 #include "wx/osx/core/hid.h" 
  31     #include "wx/dynarray.h" 
  32     #include "wx/string.h" 
  35     #include "wx/module.h" 
  38 #include "wx/osx/private.h" 
  40 // ============================================================================ 
  42 // ============================================================================ 
  44 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
  48 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
  50 // ---------------------------------------------------------------------------- 
  51 // wxHIDDevice::Create 
  53 //  nClass is the HID Page such as 
  54 //      kHIDPage_GenericDesktop 
  55 //  nType is the HID Usage such as 
  56 //      kHIDUsage_GD_Joystick,kHIDUsage_GD_Mouse,kHIDUsage_GD_Keyboard 
  57 //  nDev is the device number to use 
  59 // ---------------------------------------------------------------------------- 
  60 bool wxHIDDevice::Create (int nClass
, int nType
, int nDev
) 
  62     //Create the mach port 
  63     if(IOMasterPort(bootstrap_port
, &m_pPort
) != kIOReturnSuccess
) 
  65         wxLogSysError(wxT("Could not create mach port")); 
  69     //Dictionary that will hold first 
  70     //the matching dictionary for determining which kind of devices we want, 
  71     //then later some registry properties from an iterator (see below) 
  73     //The call to IOServiceMatching filters down the 
  74     //the services we want to hid services (and also eats the 
  75     //dictionary up for us (consumes one reference)) 
  76     CFMutableDictionaryRef pDictionary 
= IOServiceMatching(kIOHIDDeviceKey
); 
  77     if(pDictionary 
== NULL
) 
  79         wxLogSysError( wxT("IOServiceMatching(kIOHIDDeviceKey) failed") ); 
  83     //Here we'll filter down the services to what we want 
  86         CFNumberRef pType 
= CFNumberCreate(kCFAllocatorDefault
, 
  87                                     kCFNumberIntType
, &nType
); 
  88         CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsageKey
), pType
); 
  93         CFNumberRef pClass 
= CFNumberCreate(kCFAllocatorDefault
, 
  94                                     kCFNumberIntType
, &nClass
); 
  95         CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsagePageKey
), pClass
); 
  99     //Now get the matching services 
 100     io_iterator_t pIterator
; 
 101     if( IOServiceGetMatchingServices(m_pPort
, 
 102                         pDictionary
, &pIterator
) != kIOReturnSuccess 
) 
 104         wxLogSysError(wxT("No Matching HID Services")); 
 108     //Were there any devices matched? 
 110         return false; // No devices found 
 112     //Now we iterate through them 
 114     while ( (pObject 
= IOIteratorNext(pIterator
)) != 0) 
 118             IOObjectRelease(pObject
); 
 122         if ( IORegistryEntryCreateCFProperties
 
 130             wxLogDebug(wxT("IORegistryEntryCreateCFProperties failed")); 
 134         // Now we get the attributes of each "product" in the iterator 
 138         CFStringRef cfsProduct 
= (CFStringRef
) 
 139             CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductKey
)); 
 141             wxCFStringRef( wxCFRetain(cfsProduct
) 
 144         //Get the Product ID Key 
 145         CFNumberRef cfnProductId 
= (CFNumberRef
) 
 146             CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductIDKey
)); 
 149             CFNumberGetValue(cfnProductId
, kCFNumberIntType
, &m_nProductId
); 
 152         //Get the Vendor ID Key 
 153         CFNumberRef cfnVendorId 
= (CFNumberRef
) 
 154             CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDVendorIDKey
)); 
 157             CFNumberGetValue(cfnVendorId
, kCFNumberIntType
, &m_nManufacturerId
); 
 161         // End attribute getting 
 164         //Create the interface (good grief - long function names!) 
 166         IOCFPlugInInterface
** ppPlugin
; 
 167         if(IOCreatePlugInInterfaceForService(pObject
, 
 168                                              kIOHIDDeviceUserClientTypeID
, 
 169                                              kIOCFPlugInInterfaceID
, &ppPlugin
, 
 170                                              &nScore
) !=  kIOReturnSuccess
) 
 172             wxLogSysError(wxT("Could not create HID Interface for product")); 
 176         //Now, the final thing we can check before we fall back to asserts 
 177         //(because the dtor only checks if the device is ok, so if anything 
 178         //fails from now on the dtor will delete the device anyway, so we can't break from this). 
 180         //Get the HID interface from the plugin to the mach port 
 181         if((*ppPlugin
)->QueryInterface(ppPlugin
, 
 182                                CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID
), 
 183                                (void**) &m_ppDevice
) != S_OK
) 
 185             wxLogSysError(wxT("Could not get device interface from HID interface")); 
 190         (*ppPlugin
)->Release(ppPlugin
); 
 192         //open the HID interface... 
 193         if ( (*m_ppDevice
)->open(m_ppDevice
, 0) != S_OK 
) 
 195             wxLogDebug(wxT("HID device: open failed")); 
 199         //Now the hard part - in order to scan things we need "cookies" 
 201         CFArrayRef cfaCookies 
= (CFArrayRef
)CFDictionaryGetValue(pDictionary
, 
 202                                  CFSTR(kIOHIDElementKey
)); 
 203         BuildCookies(cfaCookies
); 
 206         CFRelease(pDictionary
); 
 207         IOObjectRelease(pObject
); 
 210         IOObjectRelease(pIterator
); 
 216     IOObjectRelease(pIterator
); 
 218     return false; //no device 
 221 // ---------------------------------------------------------------------------- 
 222 // wxHIDDevice::GetCount [static] 
 224 //  Obtains the number of devices on a system for a given HID Page (nClass) 
 225 // and HID Usage (nType). 
 226 // ---------------------------------------------------------------------------- 
 227 size_t wxHIDDevice::GetCount (int nClass
, int nType
) 
 229     //Create the mach port 
 231     if(IOMasterPort(bootstrap_port
, &pPort
) != kIOReturnSuccess
) 
 233         wxLogSysError(wxT("Could not create mach port")); 
 237     //Dictionary that will hold first 
 238     //the matching dictionary for determining which kind of devices we want, 
 239     //then later some registry properties from an iterator (see below) 
 240     CFMutableDictionaryRef pDictionary 
= IOServiceMatching(kIOHIDDeviceKey
); 
 241     if(pDictionary 
== NULL
) 
 243         wxLogSysError( wxT("IOServiceMatching(kIOHIDDeviceKey) failed") ); 
 247     //Here we'll filter down the services to what we want 
 250         CFNumberRef pType 
= CFNumberCreate(kCFAllocatorDefault
, 
 251                                     kCFNumberIntType
, &nType
); 
 252         CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsageKey
), pType
); 
 257         CFNumberRef pClass 
= CFNumberCreate(kCFAllocatorDefault
, 
 258                                     kCFNumberIntType
, &nClass
); 
 259         CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsagePageKey
), pClass
); 
 263     //Now get the matching services 
 264     io_iterator_t pIterator
; 
 265     if( IOServiceGetMatchingServices(pPort
, 
 266                                      pDictionary
, &pIterator
) != kIOReturnSuccess 
) 
 268         wxLogSysError(wxT("No Matching HID Services")); 
 272     //If the iterator doesn't exist there are no devices :) 
 276     //Now we iterate through them 
 279     while ( (pObject 
= IOIteratorNext(pIterator
)) != 0) 
 282         IOObjectRelease(pObject
); 
 286     IOObjectRelease(pIterator
); 
 287     mach_port_deallocate(mach_task_self(), pPort
); 
 292 // ---------------------------------------------------------------------------- 
 293 // wxHIDDevice::AddCookie 
 295 // Adds a cookie to the internal cookie array from a CFType 
 296 // ---------------------------------------------------------------------------- 
 297 void wxHIDDevice::AddCookie(CFTypeRef Data
, int i
) 
 300                 (CFNumberRef
) CFDictionaryGetValue    ( (CFDictionaryRef
) Data
 
 301                                         , CFSTR(kIOHIDElementCookieKey
) 
 308 // ---------------------------------------------------------------------------- 
 309 // wxHIDDevice::AddCookieInQueue 
 311 // Adds a cookie to the internal cookie array from a CFType and additionally 
 312 // adds it to the internal HID Queue 
 313 // ---------------------------------------------------------------------------- 
 314 void wxHIDDevice::AddCookieInQueue(CFTypeRef Data
, int i
) 
 316     //3rd Param flags (none yet) 
 318     if ( (*m_ppQueue
)->addElement(m_ppQueue
, m_pCookies
[i
], 0) != S_OK 
) 
 320         wxLogDebug(wxT("HID device: adding element failed")); 
 324 // ---------------------------------------------------------------------------- 
 325 // wxHIDDevice::InitCookies 
 327 // Create the internal cookie array, optionally creating a HID Queue 
 328 // ---------------------------------------------------------------------------- 
 329 void wxHIDDevice::InitCookies(size_t dwSize
, bool bQueue
) 
 331     m_pCookies 
= new IOHIDElementCookie
[dwSize
]; 
 334         wxASSERT( m_ppQueue 
== NULL
); 
 335         m_ppQueue 
= (*m_ppDevice
)->allocQueue(m_ppDevice
); 
 338             wxLogDebug(wxT("HID device: allocQueue failed")); 
 342         //Param 2, flags, none yet 
 343         if ( (*m_ppQueue
)->create(m_ppQueue
, 0, 512) != S_OK 
) 
 345             wxLogDebug(wxT("HID device: create failed")); 
 349     //make sure that cookie array is clear 
 350     memset(m_pCookies
, 0, sizeof(*m_pCookies
) * dwSize
); 
 353 // ---------------------------------------------------------------------------- 
 354 // wxHIDDevice::IsActive 
 356 // Returns true if a cookie of the device is active - for example if a key is 
 357 // held down, joystick button pressed, caps lock active, etc.. 
 358 // ---------------------------------------------------------------------------- 
 359 bool wxHIDDevice::IsActive(int nIndex
) 
 361     if(!HasElement(nIndex
)) 
 363         //cookie at index does not exist - getElementValue 
 364         //could return true which would be incorrect so we 
 369     IOHIDEventStruct Event
; 
 370     (*m_ppDevice
)->getElementValue(m_ppDevice
, m_pCookies
[nIndex
], &Event
); 
 371     return !!Event
.value
; 
 374 // ---------------------------------------------------------------------------- 
 375 // wxHIDDevice::HasElement 
 377 // Returns true if the element in the internal cookie array exists 
 378 // ---------------------------------------------------------------------------- 
 379 bool wxHIDDevice::HasElement(int nIndex
) 
 381     return (void*) m_pCookies
[nIndex
] != NULL
; 
 384 // ---------------------------------------------------------------------------- 
 385 // wxHIDDevice Destructor 
 387 // Frees all memory and objects from the structure 
 388 // ---------------------------------------------------------------------------- 
 389 wxHIDDevice::~wxHIDDevice() 
 391     if (m_ppDevice 
!= NULL
) 
 393         if (m_ppQueue 
!= NULL
) 
 395             (*m_ppQueue
)->stop(m_ppQueue
); 
 396             (*m_ppQueue
)->dispose(m_ppQueue
); 
 397             (*m_ppQueue
)->Release(m_ppQueue
); 
 399         (*m_ppDevice
)->close(m_ppDevice
); 
 400         (*m_ppDevice
)->Release(m_ppDevice
); 
 401         mach_port_deallocate(mach_task_self(), m_pPort
); 
 404     if (m_pCookies 
!= NULL
) 
 406         delete [] m_pCookies
; 
 410 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 414 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 416 //There are no right shift, alt etc. in the wx headers yet so just sort 
 417 //of "define our own" for now 
 426 // ---------------------------------------------------------------------------- 
 427 // wxHIDKeyboard::GetCount [static] 
 429 // Get number of HID keyboards available 
 430 // ---------------------------------------------------------------------------- 
 431 int wxHIDKeyboard::GetCount() 
 433     return wxHIDDevice::GetCount(kHIDPage_GenericDesktop
, 
 434                                kHIDUsage_GD_Keyboard
); 
 437 // ---------------------------------------------------------------------------- 
 438 // wxHIDKeyboard::Create 
 440 // Create the HID Keyboard 
 441 // ---------------------------------------------------------------------------- 
 442 bool wxHIDKeyboard::Create(int nDev 
/* = 1*/) 
 444     return wxHIDDevice::Create(kHIDPage_GenericDesktop
, 
 445                                kHIDUsage_GD_Keyboard
, 
 449 // ---------------------------------------------------------------------------- 
 450 // wxHIDKeyboard::AddCookie 
 452 // Overloaded version of wxHIDDevice::AddCookie that simply does not 
 453 // add a cookie if a duplicate is found 
 454 // ---------------------------------------------------------------------------- 
 455 void wxHIDKeyboard::AddCookie(CFTypeRef Data
, int i
) 
 458         wxHIDDevice::AddCookie(Data
, i
); 
 461 // ---------------------------------------------------------------------------- 
 462 // wxHIDKeyboard::BuildCookies 
 464 // Callback from Create() to build the HID cookies for the internal cookie 
 466 // ---------------------------------------------------------------------------- 
 467 void wxHIDKeyboard::BuildCookies(CFArrayRef Array
) 
 469     //Create internal cookie array 
 472     //Begin recursing in array 
 473     DoBuildCookies(Array
); 
 476 void wxHIDKeyboard::DoBuildCookies(CFArrayRef Array
) 
 478     //Now go through each possible cookie 
 481 //    bool bEOTriggered = false; 
 482     for (i 
= 0; i 
< CFArrayGetCount(Array
); ++i
) 
 484         const void* ref 
= CFDictionaryGetValue( 
 485                 (CFDictionaryRef
)CFArrayGetValueAtIndex(Array
, i
), 
 486                 CFSTR(kIOHIDElementKey
) 
 491             DoBuildCookies((CFArrayRef
) ref
); 
 501                     CFDictionaryGetValue((CFDictionaryRef
) 
 502                         CFArrayGetValueAtIndex(Array
, i
), 
 503                         CFSTR(kIOHIDElementUsageKey
) 
 509             // Now translate the usage # into a wx keycode 
 513         // OK, this is strange - basically this kind of strange - 
 514         // Starting from 0xEO these elements (like shift) appear twice in 
 515         // the array!  The ones at the end are bogus I guess - the funny part 
 516         // is that besides the fact that the ones at the front have a Unit 
 517         // and UnitExponent key with a value of 0 and a different cookie value, 
 518         // there is no discernable difference between the two... 
 520         // Will the real shift please stand up? 
 522         // Something to spend a support request on, if I had one, LOL. 
 528         //    bEOTriggered = true; 
 530         //Instead of that though we now just don't add duplicate keys 
 532         if (nUsage 
>= kHIDUsage_KeyboardA 
&& nUsage 
<= kHIDUsage_KeyboardZ
) 
 533             AddCookie(CFArrayGetValueAtIndex(Array
, i
), 'A' + (nUsage 
- kHIDUsage_KeyboardA
) ); 
 534         else if (nUsage 
>= kHIDUsage_Keyboard1 
&& nUsage 
<= kHIDUsage_Keyboard9
) 
 535             AddCookie(CFArrayGetValueAtIndex(Array
, i
), '1' + (nUsage 
- kHIDUsage_Keyboard1
) ); 
 536         else if (nUsage 
>= kHIDUsage_KeyboardF1 
&& nUsage 
<= kHIDUsage_KeyboardF12
) 
 537             AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F1 
+ (nUsage 
- kHIDUsage_KeyboardF1
) ); 
 538         else if (nUsage 
>= kHIDUsage_KeyboardF13 
&& nUsage 
<= kHIDUsage_KeyboardF24
) 
 539             AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F13 
+ (nUsage 
- kHIDUsage_KeyboardF13
) ); 
 540         else if (nUsage 
>= kHIDUsage_Keypad1 
&& nUsage 
<= kHIDUsage_Keypad9
) 
 541             AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD1 
+ (nUsage 
- kHIDUsage_Keypad1
) ); 
 544             //0's (wx & ascii go 0-9, but HID goes 1-0) 
 545             case kHIDUsage_Keyboard0
: 
 546                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), '0'); 
 548             case kHIDUsage_Keypad0
: 
 549                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD0
); 
 553             case kHIDUsage_KeyboardReturnOrEnter
: 
 554                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RETURN
); 
 556             case kHIDUsage_KeyboardEscape
: 
 557                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_ESCAPE
); 
 559             case kHIDUsage_KeyboardDeleteOrBackspace
: 
 560                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_BACK
); 
 562             case kHIDUsage_KeyboardTab
: 
 563                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_TAB
); 
 565             case kHIDUsage_KeyboardSpacebar
: 
 566                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_SPACE
); 
 568             case kHIDUsage_KeyboardPageUp
: 
 569                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEUP
); 
 571             case kHIDUsage_KeyboardEnd
: 
 572                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_END
); 
 574             case kHIDUsage_KeyboardPageDown
: 
 575                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEDOWN
); 
 577             case kHIDUsage_KeyboardRightArrow
: 
 578                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RIGHT
); 
 580             case kHIDUsage_KeyboardLeftArrow
: 
 581                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_LEFT
); 
 583             case kHIDUsage_KeyboardDownArrow
: 
 584                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_DOWN
); 
 586             case kHIDUsage_KeyboardUpArrow
: 
 587                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_UP
); 
 591             case kHIDUsage_KeyboardCapsLock
: 
 592                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CAPITAL
); 
 594             case kHIDUsage_KeypadNumLock
: 
 595                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_NUMLOCK
); 
 597             case kHIDUsage_KeyboardScrollLock
: 
 598                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SCROLL
); 
 601             //Menu keys, Shift, other specials 
 602             case kHIDUsage_KeyboardLeftControl
: 
 603                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RAW_CONTROL
); 
 605             case kHIDUsage_KeyboardLeftShift
: 
 606                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SHIFT
); 
 608             case kHIDUsage_KeyboardLeftAlt
: 
 609                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_ALT
); 
 611             case kHIDUsage_KeyboardLeftGUI
: 
 612                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CONTROL
); 
 614             case kHIDUsage_KeyboardRightControl
: 
 615                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RAW_RCONTROL
); 
 617             case kHIDUsage_KeyboardRightShift
: 
 618                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RSHIFT
); 
 620             case kHIDUsage_KeyboardRightAlt
: 
 621                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RALT
); 
 623             case kHIDUsage_KeyboardRightGUI
: 
 624                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RCONTROL
); 
 629             //not in wx keycodes - do nothing.... 
 631             } //end mightly long switch 
 632         } //end if the current element is not an array... 
 633     } //end for loop for Array 
 636 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 640 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 642 class wxHIDModule 
: public wxModule
 
 644     DECLARE_DYNAMIC_CLASS(wxHIDModule
) 
 647         static wxArrayPtrVoid sm_keyboards
; 
 648         virtual bool OnInit() 
 652         virtual void OnExit() 
 654             for(size_t i 
= 0; i 
< sm_keyboards
.GetCount(); ++i
) 
 655                 delete (wxHIDKeyboard
*) sm_keyboards
[i
]; 
 656             sm_keyboards
.Clear(); 
 660 IMPLEMENT_DYNAMIC_CLASS(wxHIDModule
, wxModule
) 
 662 wxArrayPtrVoid 
wxHIDModule::sm_keyboards
; 
 664 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 668 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 670 bool wxGetKeyState (wxKeyCode key
) 
 672     wxASSERT_MSG(key 
!= WXK_LBUTTON 
&& key 
!= WXK_RBUTTON 
&& key 
!= 
 673         WXK_MBUTTON
, wxT("can't use wxGetKeyState() for mouse buttons")); 
 675     CGKeyCode cgcode 
= wxCharCodeWXToOSX((wxKeyCode
)key
); 
 676     return CGEventSourceKeyState(kCGEventSourceStateCombinedSessionState
, cgcode
);