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 m_pCookies
[nIndex
] != 0;
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
);