]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/osx/core/hid.cpp
wxMessageBox off the main thread lost result code.
[wxWidgets.git] / src / osx / core / hid.cpp
... / ...
CommitLineData
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/osx/core/hid.cpp
3// Purpose: DARWIN HID layer for WX Implementation
4// Author: Ryan Norton
5// Modified by:
6// Created: 11/11/2003
7// Copyright: (c) Ryan Norton
8// Licence: wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// ===========================================================================
12// declarations
13// ===========================================================================
14
15// ---------------------------------------------------------------------------
16// headers
17// ---------------------------------------------------------------------------
18
19// For compilers that support precompilation, includes "wx.h".
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
23 #pragma hdrstop
24#endif
25
26#if wxOSX_USE_COCOA_OR_CARBON
27
28#include "wx/osx/core/hid.h"
29
30#ifndef WX_PRECOMP
31 #include "wx/dynarray.h"
32 #include "wx/string.h"
33 #include "wx/log.h"
34 #include "wx/utils.h"
35 #include "wx/module.h"
36#endif
37
38#include "wx/osx/private.h"
39
40// ============================================================================
41// implementation
42// ============================================================================
43
44// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
45//
46// wxHIDDevice
47//
48// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
49
50// ----------------------------------------------------------------------------
51// wxHIDDevice::Create
52//
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
58//
59// ----------------------------------------------------------------------------
60bool wxHIDDevice::Create (int nClass, int nType, int nDev)
61{
62 //Create the mach port
63 if(IOMasterPort(bootstrap_port, &m_pPort) != kIOReturnSuccess)
64 {
65 wxLogSysError(wxT("Could not create mach port"));
66 return false;
67 }
68
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)
72 //
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)
78 {
79 wxLogSysError( wxT("IOServiceMatching(kIOHIDDeviceKey) failed") );
80 return false;
81 }
82
83 //Here we'll filter down the services to what we want
84 if (nType != -1)
85 {
86 CFNumberRef pType = CFNumberCreate(kCFAllocatorDefault,
87 kCFNumberIntType, &nType);
88 CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsageKey), pType);
89 CFRelease(pType);
90 }
91 if (nClass != -1)
92 {
93 CFNumberRef pClass = CFNumberCreate(kCFAllocatorDefault,
94 kCFNumberIntType, &nClass);
95 CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsagePageKey), pClass);
96 CFRelease(pClass);
97 }
98
99 //Now get the matching services
100 io_iterator_t pIterator;
101 if( IOServiceGetMatchingServices(m_pPort,
102 pDictionary, &pIterator) != kIOReturnSuccess )
103 {
104 wxLogSysError(wxT("No Matching HID Services"));
105 return false;
106 }
107
108 //Were there any devices matched?
109 if(pIterator == 0)
110 return false; // No devices found
111
112 //Now we iterate through them
113 io_object_t pObject;
114 while ( (pObject = IOIteratorNext(pIterator)) != 0)
115 {
116 if(--nDev != 0)
117 {
118 IOObjectRelease(pObject);
119 continue;
120 }
121
122 if ( IORegistryEntryCreateCFProperties
123 (
124 pObject,
125 &pDictionary,
126 kCFAllocatorDefault,
127 kNilOptions
128 ) != KERN_SUCCESS )
129 {
130 wxLogDebug(wxT("IORegistryEntryCreateCFProperties failed"));
131 }
132
133 //
134 // Now we get the attributes of each "product" in the iterator
135 //
136
137 //Get [product] name
138 CFStringRef cfsProduct = (CFStringRef)
139 CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDProductKey));
140 m_szProductName =
141 wxCFStringRef( wxCFRetain(cfsProduct)
142 ).AsString();
143
144 //Get the Product ID Key
145 CFNumberRef cfnProductId = (CFNumberRef)
146 CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDProductIDKey));
147 if (cfnProductId)
148 {
149 CFNumberGetValue(cfnProductId, kCFNumberIntType, &m_nProductId);
150 }
151
152 //Get the Vendor ID Key
153 CFNumberRef cfnVendorId = (CFNumberRef)
154 CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDVendorIDKey));
155 if (cfnVendorId)
156 {
157 CFNumberGetValue(cfnVendorId, kCFNumberIntType, &m_nManufacturerId);
158 }
159
160 //
161 // End attribute getting
162 //
163
164 //Create the interface (good grief - long function names!)
165 SInt32 nScore;
166 IOCFPlugInInterface** ppPlugin;
167 if(IOCreatePlugInInterfaceForService(pObject,
168 kIOHIDDeviceUserClientTypeID,
169 kIOCFPlugInInterfaceID, &ppPlugin,
170 &nScore) != kIOReturnSuccess)
171 {
172 wxLogSysError(wxT("Could not create HID Interface for product"));
173 return false;
174 }
175
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).
179
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)
184 {
185 wxLogSysError(wxT("Could not get device interface from HID interface"));
186 return false;
187 }
188
189 //release the plugin
190 (*ppPlugin)->Release(ppPlugin);
191
192 //open the HID interface...
193 if ( (*m_ppDevice)->open(m_ppDevice, 0) != S_OK )
194 {
195 wxLogDebug(wxT("HID device: open failed"));
196 }
197
198 //
199 //Now the hard part - in order to scan things we need "cookies"
200 //
201 CFArrayRef cfaCookies = (CFArrayRef)CFDictionaryGetValue(pDictionary,
202 CFSTR(kIOHIDElementKey));
203 BuildCookies(cfaCookies);
204
205 //cleanup
206 CFRelease(pDictionary);
207 IOObjectRelease(pObject);
208
209 //iterator cleanup
210 IOObjectRelease(pIterator);
211
212 return true;
213 }
214
215 //iterator cleanup
216 IOObjectRelease(pIterator);
217
218 return false; //no device
219}//end Create()
220
221// ----------------------------------------------------------------------------
222// wxHIDDevice::GetCount [static]
223//
224// Obtains the number of devices on a system for a given HID Page (nClass)
225// and HID Usage (nType).
226// ----------------------------------------------------------------------------
227size_t wxHIDDevice::GetCount (int nClass, int nType)
228{
229 //Create the mach port
230 mach_port_t pPort;
231 if(IOMasterPort(bootstrap_port, &pPort) != kIOReturnSuccess)
232 {
233 wxLogSysError(wxT("Could not create mach port"));
234 return false;
235 }
236
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)
242 {
243 wxLogSysError( wxT("IOServiceMatching(kIOHIDDeviceKey) failed") );
244 return false;
245 }
246
247 //Here we'll filter down the services to what we want
248 if (nType != -1)
249 {
250 CFNumberRef pType = CFNumberCreate(kCFAllocatorDefault,
251 kCFNumberIntType, &nType);
252 CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsageKey), pType);
253 CFRelease(pType);
254 }
255 if (nClass != -1)
256 {
257 CFNumberRef pClass = CFNumberCreate(kCFAllocatorDefault,
258 kCFNumberIntType, &nClass);
259 CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsagePageKey), pClass);
260 CFRelease(pClass);
261 }
262
263 //Now get the matching services
264 io_iterator_t pIterator;
265 if( IOServiceGetMatchingServices(pPort,
266 pDictionary, &pIterator) != kIOReturnSuccess )
267 {
268 wxLogSysError(wxT("No Matching HID Services"));
269 return false;
270 }
271
272 //If the iterator doesn't exist there are no devices :)
273 if ( !pIterator )
274 return 0;
275
276 //Now we iterate through them
277 size_t nCount = 0;
278 io_object_t pObject;
279 while ( (pObject = IOIteratorNext(pIterator)) != 0)
280 {
281 ++nCount;
282 IOObjectRelease(pObject);
283 }
284
285 //cleanup
286 IOObjectRelease(pIterator);
287 mach_port_deallocate(mach_task_self(), pPort);
288
289 return nCount;
290}//end Create()
291
292// ----------------------------------------------------------------------------
293// wxHIDDevice::AddCookie
294//
295// Adds a cookie to the internal cookie array from a CFType
296// ----------------------------------------------------------------------------
297void wxHIDDevice::AddCookie(CFTypeRef Data, int i)
298{
299 CFNumberGetValue(
300 (CFNumberRef) CFDictionaryGetValue ( (CFDictionaryRef) Data
301 , CFSTR(kIOHIDElementCookieKey)
302 ),
303 kCFNumberIntType,
304 &m_pCookies[i]
305 );
306}
307
308// ----------------------------------------------------------------------------
309// wxHIDDevice::AddCookieInQueue
310//
311// Adds a cookie to the internal cookie array from a CFType and additionally
312// adds it to the internal HID Queue
313// ----------------------------------------------------------------------------
314void wxHIDDevice::AddCookieInQueue(CFTypeRef Data, int i)
315{
316 //3rd Param flags (none yet)
317 AddCookie(Data, i);
318 if ( (*m_ppQueue)->addElement(m_ppQueue, m_pCookies[i], 0) != S_OK )
319 {
320 wxLogDebug(wxT("HID device: adding element failed"));
321 }
322}
323
324// ----------------------------------------------------------------------------
325// wxHIDDevice::InitCookies
326//
327// Create the internal cookie array, optionally creating a HID Queue
328// ----------------------------------------------------------------------------
329void wxHIDDevice::InitCookies(size_t dwSize, bool bQueue)
330{
331 m_pCookies = new IOHIDElementCookie[dwSize];
332 if (bQueue)
333 {
334 wxASSERT( m_ppQueue == NULL);
335 m_ppQueue = (*m_ppDevice)->allocQueue(m_ppDevice);
336 if ( !m_ppQueue )
337 {
338 wxLogDebug(wxT("HID device: allocQueue failed"));
339 return;
340 }
341
342 //Param 2, flags, none yet
343 if ( (*m_ppQueue)->create(m_ppQueue, 0, 512) != S_OK )
344 {
345 wxLogDebug(wxT("HID device: create failed"));
346 }
347 }
348
349 //make sure that cookie array is clear
350 memset(m_pCookies, 0, sizeof(*m_pCookies) * dwSize);
351}
352
353// ----------------------------------------------------------------------------
354// wxHIDDevice::IsActive
355//
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// ----------------------------------------------------------------------------
359bool wxHIDDevice::IsActive(int nIndex)
360{
361 if(!HasElement(nIndex))
362 {
363 //cookie at index does not exist - getElementValue
364 //could return true which would be incorrect so we
365 //check here
366 return false;
367 }
368
369 IOHIDEventStruct Event;
370 (*m_ppDevice)->getElementValue(m_ppDevice, m_pCookies[nIndex], &Event);
371 return !!Event.value;
372}
373
374// ----------------------------------------------------------------------------
375// wxHIDDevice::HasElement
376//
377// Returns true if the element in the internal cookie array exists
378// ----------------------------------------------------------------------------
379bool wxHIDDevice::HasElement(int nIndex)
380{
381 return m_pCookies[nIndex] != 0;
382}
383
384// ----------------------------------------------------------------------------
385// wxHIDDevice Destructor
386//
387// Frees all memory and objects from the structure
388// ----------------------------------------------------------------------------
389wxHIDDevice::~wxHIDDevice()
390{
391 if (m_ppDevice != NULL)
392 {
393 if (m_ppQueue != NULL)
394 {
395 (*m_ppQueue)->stop(m_ppQueue);
396 (*m_ppQueue)->dispose(m_ppQueue);
397 (*m_ppQueue)->Release(m_ppQueue);
398 }
399 (*m_ppDevice)->close(m_ppDevice);
400 (*m_ppDevice)->Release(m_ppDevice);
401 mach_port_deallocate(mach_task_self(), m_pPort);
402 }
403
404 if (m_pCookies != NULL)
405 {
406 delete [] m_pCookies;
407 }
408}
409
410// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
411//
412// wxHIDKeyboard
413//
414// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
415
416//There are no right shift, alt etc. in the wx headers yet so just sort
417//of "define our own" for now
418enum
419{
420 WXK_RSHIFT = 400,
421 WXK_RALT,
422 WXK_RCONTROL,
423 WXK_RAW_RCONTROL,
424};
425
426// ----------------------------------------------------------------------------
427// wxHIDKeyboard::GetCount [static]
428//
429// Get number of HID keyboards available
430// ----------------------------------------------------------------------------
431int wxHIDKeyboard::GetCount()
432{
433 return wxHIDDevice::GetCount(kHIDPage_GenericDesktop,
434 kHIDUsage_GD_Keyboard);
435}
436
437// ----------------------------------------------------------------------------
438// wxHIDKeyboard::Create
439//
440// Create the HID Keyboard
441// ----------------------------------------------------------------------------
442bool wxHIDKeyboard::Create(int nDev /* = 1*/)
443{
444 return wxHIDDevice::Create(kHIDPage_GenericDesktop,
445 kHIDUsage_GD_Keyboard,
446 nDev);
447}
448
449// ----------------------------------------------------------------------------
450// wxHIDKeyboard::AddCookie
451//
452// Overloaded version of wxHIDDevice::AddCookie that simply does not
453// add a cookie if a duplicate is found
454// ----------------------------------------------------------------------------
455void wxHIDKeyboard::AddCookie(CFTypeRef Data, int i)
456{
457 if(!HasElement(i))
458 wxHIDDevice::AddCookie(Data, i);
459}
460
461// ----------------------------------------------------------------------------
462// wxHIDKeyboard::BuildCookies
463//
464// Callback from Create() to build the HID cookies for the internal cookie
465// array
466// ----------------------------------------------------------------------------
467void wxHIDKeyboard::BuildCookies(CFArrayRef Array)
468{
469 //Create internal cookie array
470 InitCookies(500);
471
472 //Begin recursing in array
473 DoBuildCookies(Array);
474}
475
476void wxHIDKeyboard::DoBuildCookies(CFArrayRef Array)
477{
478 //Now go through each possible cookie
479 int i;
480 long nUsage;
481// bool bEOTriggered = false;
482 for (i = 0; i < CFArrayGetCount(Array); ++i)
483 {
484 const void* ref = CFDictionaryGetValue(
485 (CFDictionaryRef)CFArrayGetValueAtIndex(Array, i),
486 CFSTR(kIOHIDElementKey)
487 );
488
489 if (ref != NULL)
490 {
491 DoBuildCookies((CFArrayRef) ref);
492 }
493 else
494 {
495
496 //
497 // Get the usage #
498 //
499 CFNumberGetValue(
500 (CFNumberRef)
501 CFDictionaryGetValue((CFDictionaryRef)
502 CFArrayGetValueAtIndex(Array, i),
503 CFSTR(kIOHIDElementUsageKey)
504 ),
505 kCFNumberLongType,
506 &nUsage);
507
508 //
509 // Now translate the usage # into a wx keycode
510 //
511
512 //
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...
519 //
520 // Will the real shift please stand up?
521 //
522 // Something to spend a support request on, if I had one, LOL.
523 //
524 //if(nUsage == 0xE0)
525 //{
526 // if(bEOTriggered)
527 // break;
528 // bEOTriggered = true;
529 //}
530 //Instead of that though we now just don't add duplicate keys
531
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) );
542 else switch (nUsage)
543 {
544 //0's (wx & ascii go 0-9, but HID goes 1-0)
545 case kHIDUsage_Keyboard0:
546 AddCookie(CFArrayGetValueAtIndex(Array, i), '0');
547 break;
548 case kHIDUsage_Keypad0:
549 AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_NUMPAD0);
550 break;
551
552 //Basic
553 case kHIDUsage_KeyboardReturnOrEnter:
554 AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_RETURN);
555 break;
556 case kHIDUsage_KeyboardEscape:
557 AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_ESCAPE);
558 break;
559 case kHIDUsage_KeyboardDeleteOrBackspace:
560 AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_BACK);
561 break;
562 case kHIDUsage_KeyboardTab:
563 AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_TAB);
564 break;
565 case kHIDUsage_KeyboardSpacebar:
566 AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_SPACE);
567 break;
568 case kHIDUsage_KeyboardPageUp:
569 AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_PAGEUP);
570 break;
571 case kHIDUsage_KeyboardEnd:
572 AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_END);
573 break;
574 case kHIDUsage_KeyboardPageDown:
575 AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_PAGEDOWN);
576 break;
577 case kHIDUsage_KeyboardRightArrow:
578 AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_RIGHT);
579 break;
580 case kHIDUsage_KeyboardLeftArrow:
581 AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_LEFT);
582 break;
583 case kHIDUsage_KeyboardDownArrow:
584 AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_DOWN);
585 break;
586 case kHIDUsage_KeyboardUpArrow:
587 AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_UP);
588 break;
589
590 //LEDS
591 case kHIDUsage_KeyboardCapsLock:
592 AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_CAPITAL);
593 break;
594 case kHIDUsage_KeypadNumLock:
595 AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_NUMLOCK);
596 break;
597 case kHIDUsage_KeyboardScrollLock:
598 AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_SCROLL);
599 break;
600
601 //Menu keys, Shift, other specials
602 case kHIDUsage_KeyboardLeftControl:
603 AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RAW_CONTROL);
604 break;
605 case kHIDUsage_KeyboardLeftShift:
606 AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_SHIFT);
607 break;
608 case kHIDUsage_KeyboardLeftAlt:
609 AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_ALT);
610 break;
611 case kHIDUsage_KeyboardLeftGUI:
612 AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_CONTROL);
613 break;
614 case kHIDUsage_KeyboardRightControl:
615 AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RAW_RCONTROL);
616 break;
617 case kHIDUsage_KeyboardRightShift:
618 AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RSHIFT);
619 break;
620 case kHIDUsage_KeyboardRightAlt:
621 AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RALT);
622 break;
623 case kHIDUsage_KeyboardRightGUI:
624 AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RCONTROL);
625 break;
626
627 //Default
628 default:
629 //not in wx keycodes - do nothing....
630 break;
631 } //end mightly long switch
632 } //end if the current element is not an array...
633 } //end for loop for Array
634}//end buildcookies
635
636// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
637//
638// wxHIDModule
639//
640// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
641
642class wxHIDModule : public wxModule
643{
644 DECLARE_DYNAMIC_CLASS(wxHIDModule)
645
646 public:
647 static wxArrayPtrVoid sm_keyboards;
648 virtual bool OnInit()
649 {
650 return true;
651 }
652 virtual void OnExit()
653 {
654 for(size_t i = 0; i < sm_keyboards.GetCount(); ++i)
655 delete (wxHIDKeyboard*) sm_keyboards[i];
656 sm_keyboards.Clear();
657 }
658};
659
660IMPLEMENT_DYNAMIC_CLASS(wxHIDModule, wxModule)
661
662wxArrayPtrVoid wxHIDModule::sm_keyboards;
663
664// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
665//
666// wxGetKeyState()
667//
668// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
669
670bool wxGetKeyState (wxKeyCode key)
671{
672 wxASSERT_MSG(key != WXK_LBUTTON && key != WXK_RBUTTON && key !=
673 WXK_MBUTTON, wxT("can't use wxGetKeyState() for mouse buttons"));
674
675 CGKeyCode cgcode = wxCharCodeWXToOSX((wxKeyCode)key);
676 return CGEventSourceKeyState(kCGEventSourceStateCombinedSessionState, cgcode);
677}
678
679#endif //__DARWIN__