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/private.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 CGKeyCode cgcode
= wxCharCodeWXToOSX((wxKeyCode
)key
);
677 return CGEventSourceKeyState(kCGEventSourceStateCombinedSessionState
, cgcode
);