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