1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/osx/core/hid.cpp 
   3 // Purpose:     DARWIN HID layer for WX Implementation 
   8 // Copyright:   (c) Ryan Norton 
   9 // Licence:     wxWindows licence 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // =========================================================================== 
  14 // =========================================================================== 
  16 // --------------------------------------------------------------------------- 
  18 // --------------------------------------------------------------------------- 
  20 // For compilers that support precompilation, includes "wx.h". 
  21 #include "wx/wxprec.h" 
  27 #if wxOSX_USE_COCOA_OR_CARBON 
  29 #include "wx/osx/core/hid.h" 
  32     #include "wx/dynarray.h" 
  33     #include "wx/string.h" 
  36     #include "wx/module.h" 
  39 #include "wx/osx/core/cfstring.h" 
  41 // ============================================================================ 
  43 // ============================================================================ 
  45 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
  49 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
  51 // ---------------------------------------------------------------------------- 
  52 // wxHIDDevice::Create 
  54 //  nClass is the HID Page such as 
  55 //      kHIDPage_GenericDesktop 
  56 //  nType is the HID Usage such as 
  57 //      kHIDUsage_GD_Joystick,kHIDUsage_GD_Mouse,kHIDUsage_GD_Keyboard 
  58 //  nDev is the device number to use 
  60 // ---------------------------------------------------------------------------- 
  61 bool wxHIDDevice::Create (int nClass
, int nType
, int nDev
) 
  63     //Create the mach port 
  64     if(IOMasterPort(bootstrap_port
, &m_pPort
) != kIOReturnSuccess
) 
  66         wxLogSysError(wxT("Could not create mach port")); 
  70     //Dictionary that will hold first 
  71     //the matching dictionary for determining which kind of devices we want, 
  72     //then later some registry properties from an iterator (see below) 
  74     //The call to IOServiceMatching filters down the 
  75     //the services we want to hid services (and also eats the 
  76     //dictionary up for us (consumes one reference)) 
  77     CFMutableDictionaryRef pDictionary 
= IOServiceMatching(kIOHIDDeviceKey
); 
  78     if(pDictionary 
== NULL
) 
  80         wxLogSysError( wxT("IOServiceMatching(kIOHIDDeviceKey) failed") ); 
  84     //Here we'll filter down the services to what we want 
  87         CFNumberRef pType 
= CFNumberCreate(kCFAllocatorDefault
, 
  88                                     kCFNumberIntType
, &nType
); 
  89         CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsageKey
), pType
); 
  94         CFNumberRef pClass 
= CFNumberCreate(kCFAllocatorDefault
, 
  95                                     kCFNumberIntType
, &nClass
); 
  96         CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsagePageKey
), pClass
); 
 100     //Now get the matching services 
 101     io_iterator_t pIterator
; 
 102     if( IOServiceGetMatchingServices(m_pPort
, 
 103                         pDictionary
, &pIterator
) != kIOReturnSuccess 
) 
 105         wxLogSysError(wxT("No Matching HID Services")); 
 109     //Were there any devices matched? 
 111         return false; // No devices found 
 113     //Now we iterate through them 
 115     while ( (pObject 
= IOIteratorNext(pIterator
)) != 0) 
 119             IOObjectRelease(pObject
); 
 123         if ( IORegistryEntryCreateCFProperties
 
 131             wxLogDebug(wxT("IORegistryEntryCreateCFProperties failed")); 
 135         // Now we get the attributes of each "product" in the iterator 
 139         CFStringRef cfsProduct 
= (CFStringRef
) 
 140             CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductKey
)); 
 142             wxCFStringRef( wxCFRetain(cfsProduct
) 
 145         //Get the Product ID Key 
 146         CFNumberRef cfnProductId 
= (CFNumberRef
) 
 147             CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductIDKey
)); 
 150             CFNumberGetValue(cfnProductId
, kCFNumberIntType
, &m_nProductId
); 
 153         //Get the Vendor ID Key 
 154         CFNumberRef cfnVendorId 
= (CFNumberRef
) 
 155             CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDVendorIDKey
)); 
 158             CFNumberGetValue(cfnVendorId
, kCFNumberIntType
, &m_nManufacturerId
); 
 162         // End attribute getting 
 165         //Create the interface (good grief - long function names!) 
 167         IOCFPlugInInterface
** ppPlugin
; 
 168         if(IOCreatePlugInInterfaceForService(pObject
, 
 169                                              kIOHIDDeviceUserClientTypeID
, 
 170                                              kIOCFPlugInInterfaceID
, &ppPlugin
, 
 171                                              &nScore
) !=  kIOReturnSuccess
) 
 173             wxLogSysError(wxT("Could not create HID Interface for product")); 
 177         //Now, the final thing we can check before we fall back to asserts 
 178         //(because the dtor only checks if the device is ok, so if anything 
 179         //fails from now on the dtor will delete the device anyway, so we can't break from this). 
 181         //Get the HID interface from the plugin to the mach port 
 182         if((*ppPlugin
)->QueryInterface(ppPlugin
, 
 183                                CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID
), 
 184                                (void**) &m_ppDevice
) != S_OK
) 
 186             wxLogSysError(wxT("Could not get device interface from HID interface")); 
 191         (*ppPlugin
)->Release(ppPlugin
); 
 193         //open the HID interface... 
 194         if ( (*m_ppDevice
)->open(m_ppDevice
, 0) != S_OK 
) 
 196             wxLogDebug(wxT("HID device: open failed")); 
 200         //Now the hard part - in order to scan things we need "cookies" 
 202         CFArrayRef cfaCookies 
= (CFArrayRef
)CFDictionaryGetValue(pDictionary
, 
 203                                  CFSTR(kIOHIDElementKey
)); 
 204         BuildCookies(cfaCookies
); 
 207         CFRelease(pDictionary
); 
 208         IOObjectRelease(pObject
); 
 211         IOObjectRelease(pIterator
); 
 217     IOObjectRelease(pIterator
); 
 219     return false; //no device 
 222 // ---------------------------------------------------------------------------- 
 223 // wxHIDDevice::GetCount [static] 
 225 //  Obtains the number of devices on a system for a given HID Page (nClass) 
 226 // and HID Usage (nType). 
 227 // ---------------------------------------------------------------------------- 
 228 size_t wxHIDDevice::GetCount (int nClass
, int nType
) 
 230     //Create the mach port 
 232     if(IOMasterPort(bootstrap_port
, &pPort
) != kIOReturnSuccess
) 
 234         wxLogSysError(wxT("Could not create mach port")); 
 238     //Dictionary that will hold first 
 239     //the matching dictionary for determining which kind of devices we want, 
 240     //then later some registry properties from an iterator (see below) 
 241     CFMutableDictionaryRef pDictionary 
= IOServiceMatching(kIOHIDDeviceKey
); 
 242     if(pDictionary 
== NULL
) 
 244         wxLogSysError( wxT("IOServiceMatching(kIOHIDDeviceKey) failed") ); 
 248     //Here we'll filter down the services to what we want 
 251         CFNumberRef pType 
= CFNumberCreate(kCFAllocatorDefault
, 
 252                                     kCFNumberIntType
, &nType
); 
 253         CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsageKey
), pType
); 
 258         CFNumberRef pClass 
= CFNumberCreate(kCFAllocatorDefault
, 
 259                                     kCFNumberIntType
, &nClass
); 
 260         CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsagePageKey
), pClass
); 
 264     //Now get the matching services 
 265     io_iterator_t pIterator
; 
 266     if( IOServiceGetMatchingServices(pPort
, 
 267                                      pDictionary
, &pIterator
) != kIOReturnSuccess 
) 
 269         wxLogSysError(wxT("No Matching HID Services")); 
 273     //If the iterator doesn't exist there are no devices :) 
 277     //Now we iterate through them 
 280     while ( (pObject 
= IOIteratorNext(pIterator
)) != 0) 
 283         IOObjectRelease(pObject
); 
 287     IOObjectRelease(pIterator
); 
 288     mach_port_deallocate(mach_task_self(), pPort
); 
 293 // ---------------------------------------------------------------------------- 
 294 // wxHIDDevice::AddCookie 
 296 // Adds a cookie to the internal cookie array from a CFType 
 297 // ---------------------------------------------------------------------------- 
 298 void wxHIDDevice::AddCookie(CFTypeRef Data
, int i
) 
 301                 (CFNumberRef
) CFDictionaryGetValue    ( (CFDictionaryRef
) Data
 
 302                                         , CFSTR(kIOHIDElementCookieKey
) 
 309 // ---------------------------------------------------------------------------- 
 310 // wxHIDDevice::AddCookieInQueue 
 312 // Adds a cookie to the internal cookie array from a CFType and additionally 
 313 // adds it to the internal HID Queue 
 314 // ---------------------------------------------------------------------------- 
 315 void wxHIDDevice::AddCookieInQueue(CFTypeRef Data
, int i
) 
 317     //3rd Param flags (none yet) 
 319     if ( (*m_ppQueue
)->addElement(m_ppQueue
, m_pCookies
[i
], 0) != S_OK 
) 
 321         wxLogDebug(wxT("HID device: adding element failed")); 
 325 // ---------------------------------------------------------------------------- 
 326 // wxHIDDevice::InitCookies 
 328 // Create the internal cookie array, optionally creating a HID Queue 
 329 // ---------------------------------------------------------------------------- 
 330 void wxHIDDevice::InitCookies(size_t dwSize
, bool bQueue
) 
 332     m_pCookies 
= new IOHIDElementCookie
[dwSize
]; 
 335         wxASSERT( m_ppQueue 
== NULL
); 
 336         m_ppQueue 
= (*m_ppDevice
)->allocQueue(m_ppDevice
); 
 339             wxLogDebug(wxT("HID device: allocQueue failed")); 
 343         //Param 2, flags, none yet 
 344         if ( (*m_ppQueue
)->create(m_ppQueue
, 0, 512) != S_OK 
) 
 346             wxLogDebug(wxT("HID device: create failed")); 
 350     //make sure that cookie array is clear 
 351     memset(m_pCookies
, 0, sizeof(*m_pCookies
) * dwSize
); 
 354 // ---------------------------------------------------------------------------- 
 355 // wxHIDDevice::IsActive 
 357 // Returns true if a cookie of the device is active - for example if a key is 
 358 // held down, joystick button pressed, caps lock active, etc.. 
 359 // ---------------------------------------------------------------------------- 
 360 bool wxHIDDevice::IsActive(int nIndex
) 
 362     if(!HasElement(nIndex
)) 
 364         //cookie at index does not exist - getElementValue 
 365         //could return true which would be incorrect so we 
 370     IOHIDEventStruct Event
; 
 371     (*m_ppDevice
)->getElementValue(m_ppDevice
, m_pCookies
[nIndex
], &Event
); 
 372     return !!Event
.value
; 
 375 // ---------------------------------------------------------------------------- 
 376 // wxHIDDevice::HasElement 
 378 // Returns true if the element in the internal cookie array exists 
 379 // ---------------------------------------------------------------------------- 
 380 bool wxHIDDevice::HasElement(int nIndex
) 
 382     return (void*) m_pCookies
[nIndex
] != NULL
; 
 385 // ---------------------------------------------------------------------------- 
 386 // wxHIDDevice Destructor 
 388 // Frees all memory and objects from the structure 
 389 // ---------------------------------------------------------------------------- 
 390 wxHIDDevice::~wxHIDDevice() 
 392     if (m_ppDevice 
!= NULL
) 
 394         if (m_ppQueue 
!= NULL
) 
 396             (*m_ppQueue
)->stop(m_ppQueue
); 
 397             (*m_ppQueue
)->dispose(m_ppQueue
); 
 398             (*m_ppQueue
)->Release(m_ppQueue
); 
 400         (*m_ppDevice
)->close(m_ppDevice
); 
 401         (*m_ppDevice
)->Release(m_ppDevice
); 
 402         mach_port_deallocate(mach_task_self(), m_pPort
); 
 405     if (m_pCookies 
!= NULL
) 
 407         delete [] m_pCookies
; 
 411 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 415 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 417 //There are no right shift, alt etc. in the wx headers yet so just sort 
 418 //of "define our own" for now 
 427 // ---------------------------------------------------------------------------- 
 428 // wxHIDKeyboard::GetCount [static] 
 430 // Get number of HID keyboards available 
 431 // ---------------------------------------------------------------------------- 
 432 int wxHIDKeyboard::GetCount() 
 434     return wxHIDDevice::GetCount(kHIDPage_GenericDesktop
, 
 435                                kHIDUsage_GD_Keyboard
); 
 438 // ---------------------------------------------------------------------------- 
 439 // wxHIDKeyboard::Create 
 441 // Create the HID Keyboard 
 442 // ---------------------------------------------------------------------------- 
 443 bool wxHIDKeyboard::Create(int nDev 
/* = 1*/) 
 445     return wxHIDDevice::Create(kHIDPage_GenericDesktop
, 
 446                                kHIDUsage_GD_Keyboard
, 
 450 // ---------------------------------------------------------------------------- 
 451 // wxHIDKeyboard::AddCookie 
 453 // Overloaded version of wxHIDDevice::AddCookie that simply does not 
 454 // add a cookie if a duplicate is found 
 455 // ---------------------------------------------------------------------------- 
 456 void wxHIDKeyboard::AddCookie(CFTypeRef Data
, int i
) 
 459         wxHIDDevice::AddCookie(Data
, i
); 
 462 // ---------------------------------------------------------------------------- 
 463 // wxHIDKeyboard::BuildCookies 
 465 // Callback from Create() to build the HID cookies for the internal cookie 
 467 // ---------------------------------------------------------------------------- 
 468 void wxHIDKeyboard::BuildCookies(CFArrayRef Array
) 
 470     //Create internal cookie array 
 473     //Begin recursing in array 
 474     DoBuildCookies(Array
); 
 477 void wxHIDKeyboard::DoBuildCookies(CFArrayRef Array
) 
 479     //Now go through each possible cookie 
 482 //    bool bEOTriggered = false; 
 483     for (i 
= 0; i 
< CFArrayGetCount(Array
); ++i
) 
 485         const void* ref 
= CFDictionaryGetValue( 
 486                 (CFDictionaryRef
)CFArrayGetValueAtIndex(Array
, i
), 
 487                 CFSTR(kIOHIDElementKey
) 
 492             DoBuildCookies((CFArrayRef
) ref
); 
 502                     CFDictionaryGetValue((CFDictionaryRef
) 
 503                         CFArrayGetValueAtIndex(Array
, i
), 
 504                         CFSTR(kIOHIDElementUsageKey
) 
 510             // Now translate the usage # into a wx keycode 
 514         // OK, this is strange - basically this kind of strange - 
 515         // Starting from 0xEO these elements (like shift) appear twice in 
 516         // the array!  The ones at the end are bogus I guess - the funny part 
 517         // is that besides the fact that the ones at the front have a Unit 
 518         // and UnitExponent key with a value of 0 and a different cookie value, 
 519         // there is no discernable difference between the two... 
 521         // Will the real shift please stand up? 
 523         // Something to spend a support request on, if I had one, LOL. 
 529         //    bEOTriggered = true; 
 531         //Instead of that though we now just don't add duplicate keys 
 533         if (nUsage 
>= kHIDUsage_KeyboardA 
&& nUsage 
<= kHIDUsage_KeyboardZ
) 
 534             AddCookie(CFArrayGetValueAtIndex(Array
, i
), 'A' + (nUsage 
- kHIDUsage_KeyboardA
) ); 
 535         else if (nUsage 
>= kHIDUsage_Keyboard1 
&& nUsage 
<= kHIDUsage_Keyboard9
) 
 536             AddCookie(CFArrayGetValueAtIndex(Array
, i
), '1' + (nUsage 
- kHIDUsage_Keyboard1
) ); 
 537         else if (nUsage 
>= kHIDUsage_KeyboardF1 
&& nUsage 
<= kHIDUsage_KeyboardF12
) 
 538             AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F1 
+ (nUsage 
- kHIDUsage_KeyboardF1
) ); 
 539         else if (nUsage 
>= kHIDUsage_KeyboardF13 
&& nUsage 
<= kHIDUsage_KeyboardF24
) 
 540             AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F13 
+ (nUsage 
- kHIDUsage_KeyboardF13
) ); 
 541         else if (nUsage 
>= kHIDUsage_Keypad1 
&& nUsage 
<= kHIDUsage_Keypad9
) 
 542             AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD1 
+ (nUsage 
- kHIDUsage_Keypad1
) ); 
 545             //0's (wx & ascii go 0-9, but HID goes 1-0) 
 546             case kHIDUsage_Keyboard0
: 
 547                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), '0'); 
 549             case kHIDUsage_Keypad0
: 
 550                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD0
); 
 554             case kHIDUsage_KeyboardReturnOrEnter
: 
 555                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RETURN
); 
 557             case kHIDUsage_KeyboardEscape
: 
 558                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_ESCAPE
); 
 560             case kHIDUsage_KeyboardDeleteOrBackspace
: 
 561                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_BACK
); 
 563             case kHIDUsage_KeyboardTab
: 
 564                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_TAB
); 
 566             case kHIDUsage_KeyboardSpacebar
: 
 567                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_SPACE
); 
 569             case kHIDUsage_KeyboardPageUp
: 
 570                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEUP
); 
 572             case kHIDUsage_KeyboardEnd
: 
 573                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_END
); 
 575             case kHIDUsage_KeyboardPageDown
: 
 576                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEDOWN
); 
 578             case kHIDUsage_KeyboardRightArrow
: 
 579                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RIGHT
); 
 581             case kHIDUsage_KeyboardLeftArrow
: 
 582                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_LEFT
); 
 584             case kHIDUsage_KeyboardDownArrow
: 
 585                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_DOWN
); 
 587             case kHIDUsage_KeyboardUpArrow
: 
 588                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_UP
); 
 592             case kHIDUsage_KeyboardCapsLock
: 
 593                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CAPITAL
); 
 595             case kHIDUsage_KeypadNumLock
: 
 596                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_NUMLOCK
); 
 598             case kHIDUsage_KeyboardScrollLock
: 
 599                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SCROLL
); 
 602             //Menu keys, Shift, other specials 
 603             case kHIDUsage_KeyboardLeftControl
: 
 604                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RAW_CONTROL
); 
 606             case kHIDUsage_KeyboardLeftShift
: 
 607                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SHIFT
); 
 609             case kHIDUsage_KeyboardLeftAlt
: 
 610                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_ALT
); 
 612             case kHIDUsage_KeyboardLeftGUI
: 
 613                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CONTROL
); 
 615             case kHIDUsage_KeyboardRightControl
: 
 616                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RAW_RCONTROL
); 
 618             case kHIDUsage_KeyboardRightShift
: 
 619                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RSHIFT
); 
 621             case kHIDUsage_KeyboardRightAlt
: 
 622                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RALT
); 
 624             case kHIDUsage_KeyboardRightGUI
: 
 625                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RCONTROL
); 
 630             //not in wx keycodes - do nothing.... 
 632             } //end mightly long switch 
 633         } //end if the current element is not an array... 
 634     } //end for loop for Array 
 637 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 641 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 643 class wxHIDModule 
: public wxModule
 
 645     DECLARE_DYNAMIC_CLASS(wxHIDModule
) 
 648         static wxArrayPtrVoid sm_keyboards
; 
 649         virtual bool OnInit() 
 653         virtual void OnExit() 
 655             for(size_t i 
= 0; i 
< sm_keyboards
.GetCount(); ++i
) 
 656                 delete (wxHIDKeyboard
*) sm_keyboards
[i
]; 
 657             sm_keyboards
.Clear(); 
 661 IMPLEMENT_DYNAMIC_CLASS(wxHIDModule
, wxModule
) 
 663 wxArrayPtrVoid 
wxHIDModule::sm_keyboards
; 
 665 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 669 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 671 bool wxGetKeyState (wxKeyCode key
) 
 673     wxASSERT_MSG(key 
!= WXK_LBUTTON 
&& key 
!= WXK_RBUTTON 
&& key 
!= 
 674         WXK_MBUTTON
, wxT("can't use wxGetKeyState() for mouse buttons")); 
 676     if (wxHIDModule::sm_keyboards
.GetCount() == 0) 
 678         int nKeyboards 
= wxHIDKeyboard::GetCount(); 
 680         for(int i 
= 1; i 
<= nKeyboards
; ++i
) 
 682             wxHIDKeyboard
* keyboard 
= new wxHIDKeyboard(); 
 683             if(keyboard
->Create(i
)) 
 685                 wxHIDModule::sm_keyboards
.Add(keyboard
); 
 694         wxASSERT_MSG(wxHIDModule::sm_keyboards
.GetCount() != 0, 
 695                      wxT("No keyboards found!")); 
 698     for(size_t i 
= 0; i 
< wxHIDModule::sm_keyboards
.GetCount(); ++i
) 
 700         wxHIDKeyboard
* keyboard 
= (wxHIDKeyboard
*) 
 701                                 wxHIDModule::sm_keyboards
[i
]; 
 706             if( keyboard
->IsActive(WXK_SHIFT
) || 
 707                    keyboard
->IsActive(WXK_RSHIFT
) ) 
 713             if( keyboard
->IsActive(WXK_ALT
) || 
 714                    keyboard
->IsActive(WXK_RALT
) ) 
 720             if( keyboard
->IsActive(WXK_CONTROL
) || 
 721                    keyboard
->IsActive(WXK_RCONTROL
) ) 
 726     case WXK_RAW_CONTROL
: 
 727             if( keyboard
->IsActive(WXK_RAW_CONTROL
) || 
 728                    keyboard
->IsActive(WXK_RAW_RCONTROL
) ) 
 734             if( keyboard
->IsActive(key
) ) 
 742     return false; //not down/error