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"
39 #include "wx/mac/corefoundation/cfstring.h"
41 #include "wx/module.h"
43 // ============================================================================
45 // ============================================================================
47 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
51 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
53 // ----------------------------------------------------------------------------
54 // wxHIDDevice::Create
56 // nClass is the HID Page such as
57 // kHIDPage_GenericDesktop
58 // nType is the HID Usage such as
59 // kHIDUsage_GD_Joystick,kHIDUsage_GD_Mouse,kHIDUsage_GD_Keyboard
60 // nDev is the device number to use
62 // ----------------------------------------------------------------------------
63 bool wxHIDDevice::Create (int nClass
, int nType
, int nDev
)
65 //Create the mach port
66 if(IOMasterPort(bootstrap_port
, &m_pPort
) != kIOReturnSuccess
)
68 wxLogSysError(wxT("Could not create mach port"));
72 //Dictionary that will hold first
73 //the matching dictionary for determining which kind of devices we want,
74 //then later some registry properties from an iterator (see below)
76 //The call to IOServiceMatching filters down the
77 //the services we want to hid services (and also eats the
78 //dictionary up for us (consumes one reference))
79 CFMutableDictionaryRef pDictionary
= IOServiceMatching(kIOHIDDeviceKey
);
80 if(pDictionary
== NULL
)
82 wxLogSysError( _T("IOServiceMatching(kIOHIDDeviceKey) failed") );
86 //Here we'll filter down the services to what we want
89 CFNumberRef pType
= CFNumberCreate(kCFAllocatorDefault
,
90 kCFNumberIntType
, &nType
);
91 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsageKey
), pType
);
96 CFNumberRef pClass
= CFNumberCreate(kCFAllocatorDefault
,
97 kCFNumberIntType
, &nClass
);
98 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsagePageKey
), pClass
);
102 //Now get the maching services
103 io_iterator_t pIterator
;
104 if( IOServiceGetMatchingServices(m_pPort
,
105 pDictionary
, &pIterator
) != kIOReturnSuccess
)
107 wxLogSysError(_T("No Matching HID Services"));
111 //Were there any devices matched?
113 return false; // No devices found
115 //Now we iterate through them
117 while ( (pObject
= IOIteratorNext(pIterator
)) != 0)
121 IOObjectRelease(pObject
);
125 if ( IORegistryEntryCreateCFProperties
133 wxLogDebug(_T("IORegistryEntryCreateCFProperties failed"));
137 // Now we get the attributes of each "product" in the iterator
141 CFStringRef cfsProduct
= (CFStringRef
)
142 CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductKey
));
144 wxMacCFStringHolder( cfsProduct
,
148 //Get the Product ID Key
149 CFNumberRef cfnProductId
= (CFNumberRef
)
150 CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductIDKey
));
153 CFNumberGetValue(cfnProductId
, kCFNumberIntType
, &m_nProductId
);
156 //Get the Vendor ID Key
157 CFNumberRef cfnVendorId
= (CFNumberRef
)
158 CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDVendorIDKey
));
161 CFNumberGetValue(cfnVendorId
, kCFNumberIntType
, &m_nManufacturerId
);
165 // End attribute getting
168 //Create the interface (good grief - long function names!)
170 IOCFPlugInInterface
** ppPlugin
;
171 if(IOCreatePlugInInterfaceForService(pObject
,
172 kIOHIDDeviceUserClientTypeID
,
173 kIOCFPlugInInterfaceID
, &ppPlugin
,
174 &nScore
) != kIOReturnSuccess
)
176 wxLogSysError(wxT("Could not create HID Interface for product"));
180 //Now, the final thing we can check before we fall back to asserts
181 //(because the dtor only checks if the device is ok, so if anything
182 //fails from now on the dtor will delete the device anyway, so we can't break from this).
184 //Get the HID interface from the plugin to the mach port
185 if((*ppPlugin
)->QueryInterface(ppPlugin
,
186 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID
),
187 (void**) &m_ppDevice
) != S_OK
)
189 wxLogSysError(wxT("Could not get device interface from HID interface"));
194 (*ppPlugin
)->Release(ppPlugin
);
196 //open the HID interface...
197 if ( (*m_ppDevice
)->open(m_ppDevice
, 0) != S_OK
)
198 wxLogDebug(_T("HID device: open failed"));
201 //Now the hard part - in order to scan things we need "cookies"
203 CFArrayRef cfaCookies
= (CFArrayRef
)CFDictionaryGetValue(pDictionary
,
204 CFSTR(kIOHIDElementKey
));
205 BuildCookies(cfaCookies
);
208 CFRelease(pDictionary
);
209 IOObjectRelease(pObject
);
212 IOObjectRelease(pIterator
);
218 IOObjectRelease(pIterator
);
220 return false; //no device
223 // ----------------------------------------------------------------------------
224 // wxHIDDevice::GetCount [static]
226 // Obtains the number of devices on a system for a given HID Page (nClass)
227 // and HID Usage (nType).
228 // ----------------------------------------------------------------------------
229 size_t wxHIDDevice::GetCount (int nClass
, int nType
)
231 //Create the mach port
233 if(IOMasterPort(bootstrap_port
, &pPort
) != kIOReturnSuccess
)
235 wxLogSysError(wxT("Could not create mach port"));
239 //Dictionary that will hold first
240 //the matching dictionary for determining which kind of devices we want,
241 //then later some registry properties from an iterator (see below)
242 CFMutableDictionaryRef pDictionary
= IOServiceMatching(kIOHIDDeviceKey
);
243 if(pDictionary
== NULL
)
245 wxLogSysError( _T("IOServiceMatching(kIOHIDDeviceKey) failed") );
249 //Here we'll filter down the services to what we want
252 CFNumberRef pType
= CFNumberCreate(kCFAllocatorDefault
,
253 kCFNumberIntType
, &nType
);
254 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsageKey
), pType
);
259 CFNumberRef pClass
= CFNumberCreate(kCFAllocatorDefault
,
260 kCFNumberIntType
, &nClass
);
261 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsagePageKey
), pClass
);
265 //Now get the maching services
266 io_iterator_t pIterator
;
267 if( IOServiceGetMatchingServices(pPort
,
268 pDictionary
, &pIterator
) != kIOReturnSuccess
)
270 wxLogSysError(_T("No Matching HID Services"));
274 //If the iterator doesn't exist there are no devices :)
278 //Now we iterate through them
281 while ( (pObject
= IOIteratorNext(pIterator
)) != 0)
284 IOObjectRelease(pObject
);
288 IOObjectRelease(pIterator
);
289 mach_port_deallocate(mach_task_self(), pPort
);
294 // ----------------------------------------------------------------------------
295 // wxHIDDevice::AddCookie
297 // Adds a cookie to the internal cookie array from a CFType
298 // ----------------------------------------------------------------------------
299 void wxHIDDevice::AddCookie(CFTypeRef Data
, int i
)
302 (CFNumberRef
) CFDictionaryGetValue ( (CFDictionaryRef
) Data
303 , CFSTR(kIOHIDElementCookieKey
)
310 // ----------------------------------------------------------------------------
311 // wxHIDDevice::AddCookieInQueue
313 // Adds a cookie to the internal cookie array from a CFType and additionally
314 // adds it to the internal HID Queue
315 // ----------------------------------------------------------------------------
316 void wxHIDDevice::AddCookieInQueue(CFTypeRef Data
, int i
)
318 //3rd Param flags (none yet)
320 if ( (*m_ppQueue
)->addElement(m_ppQueue
, m_pCookies
[i
], 0) != S_OK
)
321 wxLogDebug(_T("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(_T("HID device: allocQueue failed"));
342 //Param 2, flags, none yet
343 if ( (*m_ppQueue
)->create(m_ppQueue
, 0, 512) != S_OK
)
345 wxLogDebug(_T("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
] != NULL
;
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_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_MENU
);
614 case kHIDUsage_KeyboardRightControl
:
615 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_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_RMENU
);
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
];
659 IMPLEMENT_DYNAMIC_CLASS(wxHIDModule
, wxModule
)
661 wxArrayPtrVoid
wxHIDModule::sm_keyboards
;
663 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
667 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
669 bool wxGetKeyState (wxKeyCode key
)
671 wxASSERT_MSG(key
!= WXK_LBUTTON
&& key
!= WXK_RBUTTON
&& key
!=
672 WXK_MBUTTON
, wxT("can't use wxGetKeyState() for mouse buttons"));
674 if (wxHIDModule::sm_keyboards
.GetCount() == 0)
676 int nKeyboards
= wxHIDKeyboard::GetCount();
678 for(int i
= 1; i
<= nKeyboards
; ++i
)
680 wxHIDKeyboard
* keyboard
= new wxHIDKeyboard();
681 if(keyboard
->Create(i
))
683 wxHIDModule::sm_keyboards
.Add(keyboard
);
692 wxASSERT_MSG(wxHIDModule::sm_keyboards
.GetCount() != 0,
693 wxT("No keyboards found!"));
696 for(size_t i
= 0; i
< wxHIDModule::sm_keyboards
.GetCount(); ++i
)
698 wxHIDKeyboard
* keyboard
= (wxHIDKeyboard
*)
699 wxHIDModule::sm_keyboards
[i
];
704 if( keyboard
->IsActive(WXK_SHIFT
) ||
705 keyboard
->IsActive(WXK_RSHIFT
) )
711 if( keyboard
->IsActive(WXK_ALT
) ||
712 keyboard
->IsActive(WXK_RALT
) )
718 if( keyboard
->IsActive(WXK_CONTROL
) ||
719 keyboard
->IsActive(WXK_RCONTROL
) )
725 if( keyboard
->IsActive(WXK_MENU
) ||
726 keyboard
->IsActive(WXK_RMENU
) )
732 if( keyboard
->IsActive(key
) )
740 return false; //not down/error