1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/mac/corefoundation/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" 
  30 #include "wx/mac/corefoundation/hid.h" 
  33     #include "wx/dynarray.h" 
  34     #include "wx/string.h" 
  37     #include "wx/module.h" 
  40 #include "wx/mac/corefoundation/cfstring.h" 
  42 // ============================================================================ 
  44 // ============================================================================ 
  46 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
  50 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
  52 // ---------------------------------------------------------------------------- 
  53 // wxHIDDevice::Create 
  55 //  nClass is the HID Page such as 
  56 //      kHIDPage_GenericDesktop 
  57 //  nType is the HID Usage such as 
  58 //      kHIDUsage_GD_Joystick,kHIDUsage_GD_Mouse,kHIDUsage_GD_Keyboard 
  59 //  nDev is the device number to use 
  61 // ---------------------------------------------------------------------------- 
  62 bool wxHIDDevice::Create (int nClass
, int nType
, int nDev
) 
  64     //Create the mach port 
  65     if(IOMasterPort(bootstrap_port
, &m_pPort
) != kIOReturnSuccess
) 
  67         wxLogSysError(wxT("Could not create mach port")); 
  71     //Dictionary that will hold first 
  72     //the matching dictionary for determining which kind of devices we want, 
  73     //then later some registry properties from an iterator (see below) 
  75     //The call to IOServiceMatching filters down the 
  76     //the services we want to hid services (and also eats the 
  77     //dictionary up for us (consumes one reference)) 
  78     CFMutableDictionaryRef pDictionary 
= IOServiceMatching(kIOHIDDeviceKey
); 
  79     if(pDictionary 
== NULL
) 
  81         wxLogSysError( _T("IOServiceMatching(kIOHIDDeviceKey) failed") ); 
  85     //Here we'll filter down the services to what we want 
  88         CFNumberRef pType 
= CFNumberCreate(kCFAllocatorDefault
, 
  89                                     kCFNumberIntType
, &nType
); 
  90         CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsageKey
), pType
); 
  95         CFNumberRef pClass 
= CFNumberCreate(kCFAllocatorDefault
, 
  96                                     kCFNumberIntType
, &nClass
); 
  97         CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsagePageKey
), pClass
); 
 101     //Now get the maching services 
 102     io_iterator_t pIterator
; 
 103     if( IOServiceGetMatchingServices(m_pPort
, 
 104                         pDictionary
, &pIterator
) != kIOReturnSuccess 
) 
 106         wxLogSysError(_T("No Matching HID Services")); 
 110     //Were there any devices matched? 
 112         return false; // No devices found 
 114     //Now we iterate through them 
 116     while ( (pObject 
= IOIteratorNext(pIterator
)) != 0) 
 120             IOObjectRelease(pObject
); 
 124         if ( IORegistryEntryCreateCFProperties
 
 132             wxLogDebug(_T("IORegistryEntryCreateCFProperties failed")); 
 136         // Now we get the attributes of each "product" in the iterator 
 140         CFStringRef cfsProduct 
= (CFStringRef
) 
 141             CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductKey
)); 
 143             wxMacCFStringHolder( cfsProduct
, 
 147         //Get the Product ID Key 
 148         CFNumberRef cfnProductId 
= (CFNumberRef
) 
 149             CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductIDKey
)); 
 152             CFNumberGetValue(cfnProductId
, kCFNumberIntType
, &m_nProductId
); 
 155         //Get the Vendor ID Key 
 156         CFNumberRef cfnVendorId 
= (CFNumberRef
) 
 157             CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDVendorIDKey
)); 
 160             CFNumberGetValue(cfnVendorId
, kCFNumberIntType
, &m_nManufacturerId
); 
 164         // End attribute getting 
 167         //Create the interface (good grief - long function names!) 
 169         IOCFPlugInInterface
** ppPlugin
; 
 170         if(IOCreatePlugInInterfaceForService(pObject
, 
 171                                              kIOHIDDeviceUserClientTypeID
, 
 172                                              kIOCFPlugInInterfaceID
, &ppPlugin
, 
 173                                              &nScore
) !=  kIOReturnSuccess
) 
 175             wxLogSysError(wxT("Could not create HID Interface for product")); 
 179         //Now, the final thing we can check before we fall back to asserts 
 180         //(because the dtor only checks if the device is ok, so if anything 
 181         //fails from now on the dtor will delete the device anyway, so we can't break from this). 
 183         //Get the HID interface from the plugin to the mach port 
 184         if((*ppPlugin
)->QueryInterface(ppPlugin
, 
 185                                CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID
), 
 186                                (void**) &m_ppDevice
) != S_OK
) 
 188             wxLogSysError(wxT("Could not get device interface from HID interface")); 
 193         (*ppPlugin
)->Release(ppPlugin
); 
 195         //open the HID interface... 
 196         if ( (*m_ppDevice
)->open(m_ppDevice
, 0) != S_OK 
) 
 197             wxLogDebug(_T("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( _T("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 maching services 
 265     io_iterator_t pIterator
; 
 266     if( IOServiceGetMatchingServices(pPort
, 
 267                                      pDictionary
, &pIterator
) != kIOReturnSuccess 
) 
 269         wxLogSysError(_T("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 
) 
 320         wxLogDebug(_T("HID device: adding element failed")); 
 323 // ---------------------------------------------------------------------------- 
 324 // wxHIDDevice::InitCookies 
 326 // Create the internal cookie array, optionally creating a HID Queue 
 327 // ---------------------------------------------------------------------------- 
 328 void wxHIDDevice::InitCookies(size_t dwSize
, bool bQueue
) 
 330     m_pCookies 
= new IOHIDElementCookie
[dwSize
]; 
 333         wxASSERT( m_ppQueue 
== NULL
); 
 334         m_ppQueue 
= (*m_ppDevice
)->allocQueue(m_ppDevice
); 
 337             wxLogDebug(_T("HID device: allocQueue failed")); 
 341         //Param 2, flags, none yet 
 342         if ( (*m_ppQueue
)->create(m_ppQueue
, 0, 512) != S_OK 
) 
 344             wxLogDebug(_T("HID device: create failed")); 
 348     //make sure that cookie array is clear 
 349     memset(m_pCookies
, 0, sizeof(*m_pCookies
) * dwSize
); 
 352 // ---------------------------------------------------------------------------- 
 353 // wxHIDDevice::IsActive 
 355 // Returns true if a cookie of the device is active - for example if a key is 
 356 // held down, joystick button pressed, caps lock active, etc.. 
 357 // ---------------------------------------------------------------------------- 
 358 bool wxHIDDevice::IsActive(int nIndex
) 
 360     if(!HasElement(nIndex
)) 
 362         //cookie at index does not exist - getElementValue 
 363         //could return true which would be incorrect so we 
 368     IOHIDEventStruct Event
; 
 369     (*m_ppDevice
)->getElementValue(m_ppDevice
, m_pCookies
[nIndex
], &Event
); 
 370     return !!Event
.value
; 
 373 // ---------------------------------------------------------------------------- 
 374 // wxHIDDevice::HasElement 
 376 // Returns true if the element in the internal cookie array exists 
 377 // ---------------------------------------------------------------------------- 
 378 bool wxHIDDevice::HasElement(int nIndex
) 
 380     return m_pCookies
[nIndex
] != NULL
; 
 383 // ---------------------------------------------------------------------------- 
 384 // wxHIDDevice Destructor 
 386 // Frees all memory and objects from the structure 
 387 // ---------------------------------------------------------------------------- 
 388 wxHIDDevice::~wxHIDDevice() 
 390     if (m_ppDevice 
!= NULL
) 
 392         if (m_ppQueue 
!= NULL
) 
 394             (*m_ppQueue
)->stop(m_ppQueue
); 
 395             (*m_ppQueue
)->dispose(m_ppQueue
); 
 396             (*m_ppQueue
)->Release(m_ppQueue
); 
 398         (*m_ppDevice
)->close(m_ppDevice
); 
 399         (*m_ppDevice
)->Release(m_ppDevice
); 
 400         mach_port_deallocate(mach_task_self(), m_pPort
); 
 403     if (m_pCookies 
!= NULL
) 
 405         delete [] m_pCookies
; 
 409 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 413 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 415 //There are no right shift, alt etc. in the wx headers yet so just sort 
 416 //of "define our own" for now 
 425 // ---------------------------------------------------------------------------- 
 426 // wxHIDKeyboard::GetCount [static] 
 428 // Get number of HID keyboards available 
 429 // ---------------------------------------------------------------------------- 
 430 int wxHIDKeyboard::GetCount() 
 432     return wxHIDDevice::GetCount(kHIDPage_GenericDesktop
, 
 433                                kHIDUsage_GD_Keyboard
); 
 436 // ---------------------------------------------------------------------------- 
 437 // wxHIDKeyboard::Create 
 439 // Create the HID Keyboard 
 440 // ---------------------------------------------------------------------------- 
 441 bool wxHIDKeyboard::Create(int nDev 
/* = 1*/) 
 443     return wxHIDDevice::Create(kHIDPage_GenericDesktop
, 
 444                                kHIDUsage_GD_Keyboard
, 
 448 // ---------------------------------------------------------------------------- 
 449 // wxHIDKeyboard::AddCookie 
 451 // Overloaded version of wxHIDDevice::AddCookie that simply does not 
 452 // add a cookie if a duplicate is found 
 453 // ---------------------------------------------------------------------------- 
 454 void wxHIDKeyboard::AddCookie(CFTypeRef Data
, int i
) 
 457         wxHIDDevice::AddCookie(Data
, i
); 
 460 // ---------------------------------------------------------------------------- 
 461 // wxHIDKeyboard::BuildCookies 
 463 // Callback from Create() to build the HID cookies for the internal cookie 
 465 // ---------------------------------------------------------------------------- 
 466 void wxHIDKeyboard::BuildCookies(CFArrayRef Array
) 
 468     //Create internal cookie array 
 471     //Begin recursing in array 
 472     DoBuildCookies(Array
); 
 475 void wxHIDKeyboard::DoBuildCookies(CFArrayRef Array
) 
 477     //Now go through each possible cookie 
 480 //    bool bEOTriggered = false; 
 481     for (i 
= 0; i 
< CFArrayGetCount(Array
); ++i
) 
 483         const void* ref 
= CFDictionaryGetValue( 
 484                 (CFDictionaryRef
)CFArrayGetValueAtIndex(Array
, i
), 
 485                 CFSTR(kIOHIDElementKey
) 
 490             DoBuildCookies((CFArrayRef
) ref
); 
 500                     CFDictionaryGetValue((CFDictionaryRef
) 
 501                         CFArrayGetValueAtIndex(Array
, i
), 
 502                         CFSTR(kIOHIDElementUsageKey
) 
 508             // Now translate the usage # into a wx keycode 
 512         // OK, this is strange - basically this kind of strange - 
 513         // Starting from 0xEO these elements (like shift) appear twice in 
 514         // the array!  The ones at the end are bogus I guess - the funny part 
 515         // is that besides the fact that the ones at the front have a Unit 
 516         // and UnitExponent key with a value of 0 and a different cookie value, 
 517         // there is no discernable difference between the two... 
 519         // Will the real shift please stand up? 
 521         // Something to spend a support request on, if I had one, LOL. 
 527         //    bEOTriggered = true; 
 529         //Instead of that though we now just don't add duplicate keys 
 531         if (nUsage 
>= kHIDUsage_KeyboardA 
&& nUsage 
<= kHIDUsage_KeyboardZ
) 
 532             AddCookie(CFArrayGetValueAtIndex(Array
, i
), 'A' + (nUsage 
- kHIDUsage_KeyboardA
) ); 
 533         else if (nUsage 
>= kHIDUsage_Keyboard1 
&& nUsage 
<= kHIDUsage_Keyboard9
) 
 534             AddCookie(CFArrayGetValueAtIndex(Array
, i
), '1' + (nUsage 
- kHIDUsage_Keyboard1
) ); 
 535         else if (nUsage 
>= kHIDUsage_KeyboardF1 
&& nUsage 
<= kHIDUsage_KeyboardF12
) 
 536             AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F1 
+ (nUsage 
- kHIDUsage_KeyboardF1
) ); 
 537         else if (nUsage 
>= kHIDUsage_KeyboardF13 
&& nUsage 
<= kHIDUsage_KeyboardF24
) 
 538             AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F13 
+ (nUsage 
- kHIDUsage_KeyboardF13
) ); 
 539         else if (nUsage 
>= kHIDUsage_Keypad1 
&& nUsage 
<= kHIDUsage_Keypad9
) 
 540             AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD1 
+ (nUsage 
- kHIDUsage_Keypad1
) ); 
 543             //0's (wx & ascii go 0-9, but HID goes 1-0) 
 544             case kHIDUsage_Keyboard0
: 
 545                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), '0'); 
 547             case kHIDUsage_Keypad0
: 
 548                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD0
); 
 552             case kHIDUsage_KeyboardReturnOrEnter
: 
 553                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RETURN
); 
 555             case kHIDUsage_KeyboardEscape
: 
 556                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_ESCAPE
); 
 558             case kHIDUsage_KeyboardDeleteOrBackspace
: 
 559                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_BACK
); 
 561             case kHIDUsage_KeyboardTab
: 
 562                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_TAB
); 
 564             case kHIDUsage_KeyboardSpacebar
: 
 565                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_SPACE
); 
 567             case kHIDUsage_KeyboardPageUp
: 
 568                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEUP
); 
 570             case kHIDUsage_KeyboardEnd
: 
 571                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_END
); 
 573             case kHIDUsage_KeyboardPageDown
: 
 574                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEDOWN
); 
 576             case kHIDUsage_KeyboardRightArrow
: 
 577                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RIGHT
); 
 579             case kHIDUsage_KeyboardLeftArrow
: 
 580                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_LEFT
); 
 582             case kHIDUsage_KeyboardDownArrow
: 
 583                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_DOWN
); 
 585             case kHIDUsage_KeyboardUpArrow
: 
 586                 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_UP
); 
 590             case kHIDUsage_KeyboardCapsLock
: 
 591                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CAPITAL
); 
 593             case kHIDUsage_KeypadNumLock
: 
 594                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_NUMLOCK
); 
 596             case kHIDUsage_KeyboardScrollLock
: 
 597                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SCROLL
); 
 600             //Menu keys, Shift, other specials 
 601             case kHIDUsage_KeyboardLeftControl
: 
 602                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CONTROL
); 
 604             case kHIDUsage_KeyboardLeftShift
: 
 605                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SHIFT
); 
 607             case kHIDUsage_KeyboardLeftAlt
: 
 608                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_ALT
); 
 610             case kHIDUsage_KeyboardLeftGUI
: 
 611                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_MENU
); 
 613             case kHIDUsage_KeyboardRightControl
: 
 614                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RCONTROL
); 
 616             case kHIDUsage_KeyboardRightShift
: 
 617                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RSHIFT
); 
 619             case kHIDUsage_KeyboardRightAlt
: 
 620                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RALT
); 
 622             case kHIDUsage_KeyboardRightGUI
: 
 623                 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RMENU
); 
 628             //not in wx keycodes - do nothing.... 
 630             } //end mightly long switch 
 631         } //end if the current element is not an array... 
 632     } //end for loop for Array 
 635 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 639 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 641 class wxHIDModule 
: public wxModule
 
 643     DECLARE_DYNAMIC_CLASS(wxHIDModule
) 
 646         static wxArrayPtrVoid sm_keyboards
; 
 647         virtual bool OnInit() 
 651         virtual void OnExit() 
 653             for(size_t i 
= 0; i 
< sm_keyboards
.GetCount(); ++i
) 
 654                 delete (wxHIDKeyboard
*) sm_keyboards
[i
]; 
 658 IMPLEMENT_DYNAMIC_CLASS(wxHIDModule
, wxModule
) 
 660 wxArrayPtrVoid 
wxHIDModule::sm_keyboards
; 
 662 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 666 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 668 bool wxGetKeyState (wxKeyCode key
) 
 670     wxASSERT_MSG(key 
!= WXK_LBUTTON 
&& key 
!= WXK_RBUTTON 
&& key 
!= 
 671         WXK_MBUTTON
, wxT("can't use wxGetKeyState() for mouse buttons")); 
 673     if (wxHIDModule::sm_keyboards
.GetCount() == 0) 
 675         int nKeyboards 
= wxHIDKeyboard::GetCount(); 
 677         for(int i 
= 1; i 
<= nKeyboards
; ++i
) 
 679             wxHIDKeyboard
* keyboard 
= new wxHIDKeyboard(); 
 680             if(keyboard
->Create(i
)) 
 682                 wxHIDModule::sm_keyboards
.Add(keyboard
); 
 691         wxASSERT_MSG(wxHIDModule::sm_keyboards
.GetCount() != 0, 
 692                      wxT("No keyboards found!")); 
 695     for(size_t i 
= 0; i 
< wxHIDModule::sm_keyboards
.GetCount(); ++i
) 
 697         wxHIDKeyboard
* keyboard 
= (wxHIDKeyboard
*) 
 698                                 wxHIDModule::sm_keyboards
[i
]; 
 703             if( keyboard
->IsActive(WXK_SHIFT
) || 
 704                    keyboard
->IsActive(WXK_RSHIFT
) ) 
 710             if( keyboard
->IsActive(WXK_ALT
) || 
 711                    keyboard
->IsActive(WXK_RALT
) ) 
 717             if( keyboard
->IsActive(WXK_CONTROL
) || 
 718                    keyboard
->IsActive(WXK_RCONTROL
) ) 
 724             if( keyboard
->IsActive(WXK_MENU
) || 
 725                    keyboard
->IsActive(WXK_RMENU
) ) 
 731             if( keyboard
->IsActive(key
) ) 
 739     return false; //not down/error