1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/osx/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/osx/core/hid.h"
33 #include "wx/dynarray.h"
34 #include "wx/string.h"
37 #include "wx/module.h"
40 #include "wx/osx/core/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( wxT("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(wxT("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(wxT("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
)
197 wxLogDebug(wxT("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( wxT("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(wxT("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
)
322 wxLogDebug(wxT("HID device: adding element failed"));
326 // ----------------------------------------------------------------------------
327 // wxHIDDevice::InitCookies
329 // Create the internal cookie array, optionally creating a HID Queue
330 // ----------------------------------------------------------------------------
331 void wxHIDDevice::InitCookies(size_t dwSize
, bool bQueue
)
333 m_pCookies
= new IOHIDElementCookie
[dwSize
];
336 wxASSERT( m_ppQueue
== NULL
);
337 m_ppQueue
= (*m_ppDevice
)->allocQueue(m_ppDevice
);
340 wxLogDebug(wxT("HID device: allocQueue failed"));
344 //Param 2, flags, none yet
345 if ( (*m_ppQueue
)->create(m_ppQueue
, 0, 512) != S_OK
)
347 wxLogDebug(wxT("HID device: create failed"));
351 //make sure that cookie array is clear
352 memset(m_pCookies
, 0, sizeof(*m_pCookies
) * dwSize
);
355 // ----------------------------------------------------------------------------
356 // wxHIDDevice::IsActive
358 // Returns true if a cookie of the device is active - for example if a key is
359 // held down, joystick button pressed, caps lock active, etc..
360 // ----------------------------------------------------------------------------
361 bool wxHIDDevice::IsActive(int nIndex
)
363 if(!HasElement(nIndex
))
365 //cookie at index does not exist - getElementValue
366 //could return true which would be incorrect so we
371 IOHIDEventStruct Event
;
372 (*m_ppDevice
)->getElementValue(m_ppDevice
, m_pCookies
[nIndex
], &Event
);
373 return !!Event
.value
;
376 // ----------------------------------------------------------------------------
377 // wxHIDDevice::HasElement
379 // Returns true if the element in the internal cookie array exists
380 // ----------------------------------------------------------------------------
381 bool wxHIDDevice::HasElement(int nIndex
)
383 return (void*) m_pCookies
[nIndex
] != NULL
;
386 // ----------------------------------------------------------------------------
387 // wxHIDDevice Destructor
389 // Frees all memory and objects from the structure
390 // ----------------------------------------------------------------------------
391 wxHIDDevice::~wxHIDDevice()
393 if (m_ppDevice
!= NULL
)
395 if (m_ppQueue
!= NULL
)
397 (*m_ppQueue
)->stop(m_ppQueue
);
398 (*m_ppQueue
)->dispose(m_ppQueue
);
399 (*m_ppQueue
)->Release(m_ppQueue
);
401 (*m_ppDevice
)->close(m_ppDevice
);
402 (*m_ppDevice
)->Release(m_ppDevice
);
403 mach_port_deallocate(mach_task_self(), m_pPort
);
406 if (m_pCookies
!= NULL
)
408 delete [] m_pCookies
;
412 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
416 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
418 //There are no right shift, alt etc. in the wx headers yet so just sort
419 //of "define our own" for now
428 // ----------------------------------------------------------------------------
429 // wxHIDKeyboard::GetCount [static]
431 // Get number of HID keyboards available
432 // ----------------------------------------------------------------------------
433 int wxHIDKeyboard::GetCount()
435 return wxHIDDevice::GetCount(kHIDPage_GenericDesktop
,
436 kHIDUsage_GD_Keyboard
);
439 // ----------------------------------------------------------------------------
440 // wxHIDKeyboard::Create
442 // Create the HID Keyboard
443 // ----------------------------------------------------------------------------
444 bool wxHIDKeyboard::Create(int nDev
/* = 1*/)
446 return wxHIDDevice::Create(kHIDPage_GenericDesktop
,
447 kHIDUsage_GD_Keyboard
,
451 // ----------------------------------------------------------------------------
452 // wxHIDKeyboard::AddCookie
454 // Overloaded version of wxHIDDevice::AddCookie that simply does not
455 // add a cookie if a duplicate is found
456 // ----------------------------------------------------------------------------
457 void wxHIDKeyboard::AddCookie(CFTypeRef Data
, int i
)
460 wxHIDDevice::AddCookie(Data
, i
);
463 // ----------------------------------------------------------------------------
464 // wxHIDKeyboard::BuildCookies
466 // Callback from Create() to build the HID cookies for the internal cookie
468 // ----------------------------------------------------------------------------
469 void wxHIDKeyboard::BuildCookies(CFArrayRef Array
)
471 //Create internal cookie array
474 //Begin recursing in array
475 DoBuildCookies(Array
);
478 void wxHIDKeyboard::DoBuildCookies(CFArrayRef Array
)
480 //Now go through each possible cookie
483 // bool bEOTriggered = false;
484 for (i
= 0; i
< CFArrayGetCount(Array
); ++i
)
486 const void* ref
= CFDictionaryGetValue(
487 (CFDictionaryRef
)CFArrayGetValueAtIndex(Array
, i
),
488 CFSTR(kIOHIDElementKey
)
493 DoBuildCookies((CFArrayRef
) ref
);
503 CFDictionaryGetValue((CFDictionaryRef
)
504 CFArrayGetValueAtIndex(Array
, i
),
505 CFSTR(kIOHIDElementUsageKey
)
511 // Now translate the usage # into a wx keycode
515 // OK, this is strange - basically this kind of strange -
516 // Starting from 0xEO these elements (like shift) appear twice in
517 // the array! The ones at the end are bogus I guess - the funny part
518 // is that besides the fact that the ones at the front have a Unit
519 // and UnitExponent key with a value of 0 and a different cookie value,
520 // there is no discernable difference between the two...
522 // Will the real shift please stand up?
524 // Something to spend a support request on, if I had one, LOL.
530 // bEOTriggered = true;
532 //Instead of that though we now just don't add duplicate keys
534 if (nUsage
>= kHIDUsage_KeyboardA
&& nUsage
<= kHIDUsage_KeyboardZ
)
535 AddCookie(CFArrayGetValueAtIndex(Array
, i
), 'A' + (nUsage
- kHIDUsage_KeyboardA
) );
536 else if (nUsage
>= kHIDUsage_Keyboard1
&& nUsage
<= kHIDUsage_Keyboard9
)
537 AddCookie(CFArrayGetValueAtIndex(Array
, i
), '1' + (nUsage
- kHIDUsage_Keyboard1
) );
538 else if (nUsage
>= kHIDUsage_KeyboardF1
&& nUsage
<= kHIDUsage_KeyboardF12
)
539 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F1
+ (nUsage
- kHIDUsage_KeyboardF1
) );
540 else if (nUsage
>= kHIDUsage_KeyboardF13
&& nUsage
<= kHIDUsage_KeyboardF24
)
541 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F13
+ (nUsage
- kHIDUsage_KeyboardF13
) );
542 else if (nUsage
>= kHIDUsage_Keypad1
&& nUsage
<= kHIDUsage_Keypad9
)
543 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD1
+ (nUsage
- kHIDUsage_Keypad1
) );
546 //0's (wx & ascii go 0-9, but HID goes 1-0)
547 case kHIDUsage_Keyboard0
:
548 AddCookie(CFArrayGetValueAtIndex(Array
, i
), '0');
550 case kHIDUsage_Keypad0
:
551 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD0
);
555 case kHIDUsage_KeyboardReturnOrEnter
:
556 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RETURN
);
558 case kHIDUsage_KeyboardEscape
:
559 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_ESCAPE
);
561 case kHIDUsage_KeyboardDeleteOrBackspace
:
562 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_BACK
);
564 case kHIDUsage_KeyboardTab
:
565 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_TAB
);
567 case kHIDUsage_KeyboardSpacebar
:
568 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_SPACE
);
570 case kHIDUsage_KeyboardPageUp
:
571 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEUP
);
573 case kHIDUsage_KeyboardEnd
:
574 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_END
);
576 case kHIDUsage_KeyboardPageDown
:
577 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEDOWN
);
579 case kHIDUsage_KeyboardRightArrow
:
580 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RIGHT
);
582 case kHIDUsage_KeyboardLeftArrow
:
583 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_LEFT
);
585 case kHIDUsage_KeyboardDownArrow
:
586 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_DOWN
);
588 case kHIDUsage_KeyboardUpArrow
:
589 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_UP
);
593 case kHIDUsage_KeyboardCapsLock
:
594 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CAPITAL
);
596 case kHIDUsage_KeypadNumLock
:
597 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_NUMLOCK
);
599 case kHIDUsage_KeyboardScrollLock
:
600 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SCROLL
);
603 //Menu keys, Shift, other specials
604 case kHIDUsage_KeyboardLeftControl
:
605 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CONTROL
);
607 case kHIDUsage_KeyboardLeftShift
:
608 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SHIFT
);
610 case kHIDUsage_KeyboardLeftAlt
:
611 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_ALT
);
613 case kHIDUsage_KeyboardLeftGUI
:
614 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_MENU
);
616 case kHIDUsage_KeyboardRightControl
:
617 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RCONTROL
);
619 case kHIDUsage_KeyboardRightShift
:
620 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RSHIFT
);
622 case kHIDUsage_KeyboardRightAlt
:
623 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RALT
);
625 case kHIDUsage_KeyboardRightGUI
:
626 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RMENU
);
631 //not in wx keycodes - do nothing....
633 } //end mightly long switch
634 } //end if the current element is not an array...
635 } //end for loop for Array
638 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
642 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
644 class wxHIDModule
: public wxModule
646 DECLARE_DYNAMIC_CLASS(wxHIDModule
)
649 static wxArrayPtrVoid sm_keyboards
;
650 virtual bool OnInit()
654 virtual void OnExit()
656 for(size_t i
= 0; i
< sm_keyboards
.GetCount(); ++i
)
657 delete (wxHIDKeyboard
*) sm_keyboards
[i
];
658 sm_keyboards
.Clear();
662 IMPLEMENT_DYNAMIC_CLASS(wxHIDModule
, wxModule
)
664 wxArrayPtrVoid
wxHIDModule::sm_keyboards
;
666 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
670 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
672 bool wxGetKeyState (wxKeyCode key
)
674 wxASSERT_MSG(key
!= WXK_LBUTTON
&& key
!= WXK_RBUTTON
&& key
!=
675 WXK_MBUTTON
, wxT("can't use wxGetKeyState() for mouse buttons"));
677 if (wxHIDModule::sm_keyboards
.GetCount() == 0)
679 int nKeyboards
= wxHIDKeyboard::GetCount();
681 for(int i
= 1; i
<= nKeyboards
; ++i
)
683 wxHIDKeyboard
* keyboard
= new wxHIDKeyboard();
684 if(keyboard
->Create(i
))
686 wxHIDModule::sm_keyboards
.Add(keyboard
);
695 wxASSERT_MSG(wxHIDModule::sm_keyboards
.GetCount() != 0,
696 wxT("No keyboards found!"));
699 for(size_t i
= 0; i
< wxHIDModule::sm_keyboards
.GetCount(); ++i
)
701 wxHIDKeyboard
* keyboard
= (wxHIDKeyboard
*)
702 wxHIDModule::sm_keyboards
[i
];
707 if( keyboard
->IsActive(WXK_SHIFT
) ||
708 keyboard
->IsActive(WXK_RSHIFT
) )
714 if( keyboard
->IsActive(WXK_ALT
) ||
715 keyboard
->IsActive(WXK_RALT
) )
721 if( keyboard
->IsActive(WXK_CONTROL
) ||
722 keyboard
->IsActive(WXK_RCONTROL
) )
728 if( keyboard
->IsActive(WXK_MENU
) ||
729 keyboard
->IsActive(WXK_RMENU
) )
735 if( keyboard
->IsActive(key
) )
743 return false; //not down/error