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"
37 #include "wx/module.h"
40 #include "wx/mac/corefoundation/cfstring.h"
42 // ============================================================================
44 // ============================================================================
46 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
50 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
52 // ----------------------------------------------------------------------------
53 // wxHIDDevice::Create
55 // nClass is the HID Page such as
56 // kHIDPage_GenericDesktop
57 // nType is the HID Usage such as
58 // kHIDUsage_GD_Joystick,kHIDUsage_GD_Mouse,kHIDUsage_GD_Keyboard
59 // nDev is the device number to use
61 // ----------------------------------------------------------------------------
62 bool wxHIDDevice::Create (int nClass
, int nType
, int nDev
)
64 //Create the mach port
65 if(IOMasterPort(bootstrap_port
, &m_pPort
) != kIOReturnSuccess
)
67 wxLogSysError(wxT("Could not create mach port"));
71 //Dictionary that will hold first
72 //the matching dictionary for determining which kind of devices we want,
73 //then later some registry properties from an iterator (see below)
75 //The call to IOServiceMatching filters down the
76 //the services we want to hid services (and also eats the
77 //dictionary up for us (consumes one reference))
78 CFMutableDictionaryRef pDictionary
= IOServiceMatching(kIOHIDDeviceKey
);
79 if(pDictionary
== NULL
)
81 wxLogSysError( _T("IOServiceMatching(kIOHIDDeviceKey) failed") );
85 //Here we'll filter down the services to what we want
88 CFNumberRef pType
= CFNumberCreate(kCFAllocatorDefault
,
89 kCFNumberIntType
, &nType
);
90 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsageKey
), pType
);
95 CFNumberRef pClass
= CFNumberCreate(kCFAllocatorDefault
,
96 kCFNumberIntType
, &nClass
);
97 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsagePageKey
), pClass
);
101 //Now get the maching services
102 io_iterator_t pIterator
;
103 if( IOServiceGetMatchingServices(m_pPort
,
104 pDictionary
, &pIterator
) != kIOReturnSuccess
)
106 wxLogSysError(_T("No Matching HID Services"));
110 //Were there any devices matched?
112 return false; // No devices found
114 //Now we iterate through them
116 while ( (pObject
= IOIteratorNext(pIterator
)) != 0)
120 IOObjectRelease(pObject
);
124 if ( IORegistryEntryCreateCFProperties
132 wxLogDebug(_T("IORegistryEntryCreateCFProperties failed"));
136 // Now we get the attributes of each "product" in the iterator
140 CFStringRef cfsProduct
= (CFStringRef
)
141 CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductKey
));
143 wxCFStringRef( wxCFRetain(cfsProduct
)
146 //Get the Product ID Key
147 CFNumberRef cfnProductId
= (CFNumberRef
)
148 CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductIDKey
));
151 CFNumberGetValue(cfnProductId
, kCFNumberIntType
, &m_nProductId
);
154 //Get the Vendor ID Key
155 CFNumberRef cfnVendorId
= (CFNumberRef
)
156 CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDVendorIDKey
));
159 CFNumberGetValue(cfnVendorId
, kCFNumberIntType
, &m_nManufacturerId
);
163 // End attribute getting
166 //Create the interface (good grief - long function names!)
168 IOCFPlugInInterface
** ppPlugin
;
169 if(IOCreatePlugInInterfaceForService(pObject
,
170 kIOHIDDeviceUserClientTypeID
,
171 kIOCFPlugInInterfaceID
, &ppPlugin
,
172 &nScore
) != kIOReturnSuccess
)
174 wxLogSysError(wxT("Could not create HID Interface for product"));
178 //Now, the final thing we can check before we fall back to asserts
179 //(because the dtor only checks if the device is ok, so if anything
180 //fails from now on the dtor will delete the device anyway, so we can't break from this).
182 //Get the HID interface from the plugin to the mach port
183 if((*ppPlugin
)->QueryInterface(ppPlugin
,
184 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID
),
185 (void**) &m_ppDevice
) != S_OK
)
187 wxLogSysError(wxT("Could not get device interface from HID interface"));
192 (*ppPlugin
)->Release(ppPlugin
);
194 //open the HID interface...
195 if ( (*m_ppDevice
)->open(m_ppDevice
, 0) != S_OK
)
196 wxLogDebug(_T("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( _T("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 maching services
264 io_iterator_t pIterator
;
265 if( IOServiceGetMatchingServices(pPort
,
266 pDictionary
, &pIterator
) != kIOReturnSuccess
)
268 wxLogSysError(_T("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
)
319 wxLogDebug(_T("HID device: adding element failed"));
322 // ----------------------------------------------------------------------------
323 // wxHIDDevice::InitCookies
325 // Create the internal cookie array, optionally creating a HID Queue
326 // ----------------------------------------------------------------------------
327 void wxHIDDevice::InitCookies(size_t dwSize
, bool bQueue
)
329 m_pCookies
= new IOHIDElementCookie
[dwSize
];
332 wxASSERT( m_ppQueue
== NULL
);
333 m_ppQueue
= (*m_ppDevice
)->allocQueue(m_ppDevice
);
336 wxLogDebug(_T("HID device: allocQueue failed"));
340 //Param 2, flags, none yet
341 if ( (*m_ppQueue
)->create(m_ppQueue
, 0, 512) != S_OK
)
343 wxLogDebug(_T("HID device: create failed"));
347 //make sure that cookie array is clear
348 memset(m_pCookies
, 0, sizeof(*m_pCookies
) * dwSize
);
351 // ----------------------------------------------------------------------------
352 // wxHIDDevice::IsActive
354 // Returns true if a cookie of the device is active - for example if a key is
355 // held down, joystick button pressed, caps lock active, etc..
356 // ----------------------------------------------------------------------------
357 bool wxHIDDevice::IsActive(int nIndex
)
359 if(!HasElement(nIndex
))
361 //cookie at index does not exist - getElementValue
362 //could return true which would be incorrect so we
367 IOHIDEventStruct Event
;
368 (*m_ppDevice
)->getElementValue(m_ppDevice
, m_pCookies
[nIndex
], &Event
);
369 return !!Event
.value
;
372 // ----------------------------------------------------------------------------
373 // wxHIDDevice::HasElement
375 // Returns true if the element in the internal cookie array exists
376 // ----------------------------------------------------------------------------
377 bool wxHIDDevice::HasElement(int nIndex
)
379 return m_pCookies
[nIndex
] != NULL
;
382 // ----------------------------------------------------------------------------
383 // wxHIDDevice Destructor
385 // Frees all memory and objects from the structure
386 // ----------------------------------------------------------------------------
387 wxHIDDevice::~wxHIDDevice()
389 if (m_ppDevice
!= NULL
)
391 if (m_ppQueue
!= NULL
)
393 (*m_ppQueue
)->stop(m_ppQueue
);
394 (*m_ppQueue
)->dispose(m_ppQueue
);
395 (*m_ppQueue
)->Release(m_ppQueue
);
397 (*m_ppDevice
)->close(m_ppDevice
);
398 (*m_ppDevice
)->Release(m_ppDevice
);
399 mach_port_deallocate(mach_task_self(), m_pPort
);
402 if (m_pCookies
!= NULL
)
404 delete [] m_pCookies
;
408 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
412 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
414 //There are no right shift, alt etc. in the wx headers yet so just sort
415 //of "define our own" for now
424 // ----------------------------------------------------------------------------
425 // wxHIDKeyboard::GetCount [static]
427 // Get number of HID keyboards available
428 // ----------------------------------------------------------------------------
429 int wxHIDKeyboard::GetCount()
431 return wxHIDDevice::GetCount(kHIDPage_GenericDesktop
,
432 kHIDUsage_GD_Keyboard
);
435 // ----------------------------------------------------------------------------
436 // wxHIDKeyboard::Create
438 // Create the HID Keyboard
439 // ----------------------------------------------------------------------------
440 bool wxHIDKeyboard::Create(int nDev
/* = 1*/)
442 return wxHIDDevice::Create(kHIDPage_GenericDesktop
,
443 kHIDUsage_GD_Keyboard
,
447 // ----------------------------------------------------------------------------
448 // wxHIDKeyboard::AddCookie
450 // Overloaded version of wxHIDDevice::AddCookie that simply does not
451 // add a cookie if a duplicate is found
452 // ----------------------------------------------------------------------------
453 void wxHIDKeyboard::AddCookie(CFTypeRef Data
, int i
)
456 wxHIDDevice::AddCookie(Data
, i
);
459 // ----------------------------------------------------------------------------
460 // wxHIDKeyboard::BuildCookies
462 // Callback from Create() to build the HID cookies for the internal cookie
464 // ----------------------------------------------------------------------------
465 void wxHIDKeyboard::BuildCookies(CFArrayRef Array
)
467 //Create internal cookie array
470 //Begin recursing in array
471 DoBuildCookies(Array
);
474 void wxHIDKeyboard::DoBuildCookies(CFArrayRef Array
)
476 //Now go through each possible cookie
479 // bool bEOTriggered = false;
480 for (i
= 0; i
< CFArrayGetCount(Array
); ++i
)
482 const void* ref
= CFDictionaryGetValue(
483 (CFDictionaryRef
)CFArrayGetValueAtIndex(Array
, i
),
484 CFSTR(kIOHIDElementKey
)
489 DoBuildCookies((CFArrayRef
) ref
);
499 CFDictionaryGetValue((CFDictionaryRef
)
500 CFArrayGetValueAtIndex(Array
, i
),
501 CFSTR(kIOHIDElementUsageKey
)
507 // Now translate the usage # into a wx keycode
511 // OK, this is strange - basically this kind of strange -
512 // Starting from 0xEO these elements (like shift) appear twice in
513 // the array! The ones at the end are bogus I guess - the funny part
514 // is that besides the fact that the ones at the front have a Unit
515 // and UnitExponent key with a value of 0 and a different cookie value,
516 // there is no discernable difference between the two...
518 // Will the real shift please stand up?
520 // Something to spend a support request on, if I had one, LOL.
526 // bEOTriggered = true;
528 //Instead of that though we now just don't add duplicate keys
530 if (nUsage
>= kHIDUsage_KeyboardA
&& nUsage
<= kHIDUsage_KeyboardZ
)
531 AddCookie(CFArrayGetValueAtIndex(Array
, i
), 'A' + (nUsage
- kHIDUsage_KeyboardA
) );
532 else if (nUsage
>= kHIDUsage_Keyboard1
&& nUsage
<= kHIDUsage_Keyboard9
)
533 AddCookie(CFArrayGetValueAtIndex(Array
, i
), '1' + (nUsage
- kHIDUsage_Keyboard1
) );
534 else if (nUsage
>= kHIDUsage_KeyboardF1
&& nUsage
<= kHIDUsage_KeyboardF12
)
535 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F1
+ (nUsage
- kHIDUsage_KeyboardF1
) );
536 else if (nUsage
>= kHIDUsage_KeyboardF13
&& nUsage
<= kHIDUsage_KeyboardF24
)
537 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F13
+ (nUsage
- kHIDUsage_KeyboardF13
) );
538 else if (nUsage
>= kHIDUsage_Keypad1
&& nUsage
<= kHIDUsage_Keypad9
)
539 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD1
+ (nUsage
- kHIDUsage_Keypad1
) );
542 //0's (wx & ascii go 0-9, but HID goes 1-0)
543 case kHIDUsage_Keyboard0
:
544 AddCookie(CFArrayGetValueAtIndex(Array
, i
), '0');
546 case kHIDUsage_Keypad0
:
547 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD0
);
551 case kHIDUsage_KeyboardReturnOrEnter
:
552 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RETURN
);
554 case kHIDUsage_KeyboardEscape
:
555 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_ESCAPE
);
557 case kHIDUsage_KeyboardDeleteOrBackspace
:
558 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_BACK
);
560 case kHIDUsage_KeyboardTab
:
561 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_TAB
);
563 case kHIDUsage_KeyboardSpacebar
:
564 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_SPACE
);
566 case kHIDUsage_KeyboardPageUp
:
567 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEUP
);
569 case kHIDUsage_KeyboardEnd
:
570 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_END
);
572 case kHIDUsage_KeyboardPageDown
:
573 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEDOWN
);
575 case kHIDUsage_KeyboardRightArrow
:
576 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RIGHT
);
578 case kHIDUsage_KeyboardLeftArrow
:
579 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_LEFT
);
581 case kHIDUsage_KeyboardDownArrow
:
582 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_DOWN
);
584 case kHIDUsage_KeyboardUpArrow
:
585 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_UP
);
589 case kHIDUsage_KeyboardCapsLock
:
590 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CAPITAL
);
592 case kHIDUsage_KeypadNumLock
:
593 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_NUMLOCK
);
595 case kHIDUsage_KeyboardScrollLock
:
596 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SCROLL
);
599 //Menu keys, Shift, other specials
600 case kHIDUsage_KeyboardLeftControl
:
601 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CONTROL
);
603 case kHIDUsage_KeyboardLeftShift
:
604 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SHIFT
);
606 case kHIDUsage_KeyboardLeftAlt
:
607 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_ALT
);
609 case kHIDUsage_KeyboardLeftGUI
:
610 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_MENU
);
612 case kHIDUsage_KeyboardRightControl
:
613 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RCONTROL
);
615 case kHIDUsage_KeyboardRightShift
:
616 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RSHIFT
);
618 case kHIDUsage_KeyboardRightAlt
:
619 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RALT
);
621 case kHIDUsage_KeyboardRightGUI
:
622 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RMENU
);
627 //not in wx keycodes - do nothing....
629 } //end mightly long switch
630 } //end if the current element is not an array...
631 } //end for loop for Array
634 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
638 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
640 class wxHIDModule
: public wxModule
642 DECLARE_DYNAMIC_CLASS(wxHIDModule
)
645 static wxArrayPtrVoid sm_keyboards
;
646 virtual bool OnInit()
650 virtual void OnExit()
652 for(size_t i
= 0; i
< sm_keyboards
.GetCount(); ++i
)
653 delete (wxHIDKeyboard
*) sm_keyboards
[i
];
657 IMPLEMENT_DYNAMIC_CLASS(wxHIDModule
, wxModule
)
659 wxArrayPtrVoid
wxHIDModule::sm_keyboards
;
661 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
665 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
667 bool wxGetKeyState (wxKeyCode key
)
669 wxASSERT_MSG(key
!= WXK_LBUTTON
&& key
!= WXK_RBUTTON
&& key
!=
670 WXK_MBUTTON
, wxT("can't use wxGetKeyState() for mouse buttons"));
672 if (wxHIDModule::sm_keyboards
.GetCount() == 0)
674 int nKeyboards
= wxHIDKeyboard::GetCount();
676 for(int i
= 1; i
<= nKeyboards
; ++i
)
678 wxHIDKeyboard
* keyboard
= new wxHIDKeyboard();
679 if(keyboard
->Create(i
))
681 wxHIDModule::sm_keyboards
.Add(keyboard
);
690 wxASSERT_MSG(wxHIDModule::sm_keyboards
.GetCount() != 0,
691 wxT("No keyboards found!"));
694 for(size_t i
= 0; i
< wxHIDModule::sm_keyboards
.GetCount(); ++i
)
696 wxHIDKeyboard
* keyboard
= (wxHIDKeyboard
*)
697 wxHIDModule::sm_keyboards
[i
];
702 if( keyboard
->IsActive(WXK_SHIFT
) ||
703 keyboard
->IsActive(WXK_RSHIFT
) )
709 if( keyboard
->IsActive(WXK_ALT
) ||
710 keyboard
->IsActive(WXK_RALT
) )
716 if( keyboard
->IsActive(WXK_CONTROL
) ||
717 keyboard
->IsActive(WXK_RCONTROL
) )
723 if( keyboard
->IsActive(WXK_MENU
) ||
724 keyboard
->IsActive(WXK_RMENU
) )
730 if( keyboard
->IsActive(key
) )
738 return false; //not down/error