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 wxMacCFStringHolder( cfsProduct
,
147 //Get the Product ID Key
148 CFNumberRef cfnProductId
= (CFNumberRef
)
149 CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductIDKey
));
152 CFNumberGetValue(cfnProductId
, kCFNumberIntType
, &m_nProductId
);
155 //Get the Vendor ID Key
156 CFNumberRef cfnVendorId
= (CFNumberRef
)
157 CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDVendorIDKey
));
160 CFNumberGetValue(cfnVendorId
, kCFNumberIntType
, &m_nManufacturerId
);
164 // End attribute getting
167 //Create the interface (good grief - long function names!)
169 IOCFPlugInInterface
** ppPlugin
;
170 if(IOCreatePlugInInterfaceForService(pObject
,
171 kIOHIDDeviceUserClientTypeID
,
172 kIOCFPlugInInterfaceID
, &ppPlugin
,
173 &nScore
) != kIOReturnSuccess
)
175 wxLogSysError(wxT("Could not create HID Interface for product"));
179 //Now, the final thing we can check before we fall back to asserts
180 //(because the dtor only checks if the device is ok, so if anything
181 //fails from now on the dtor will delete the device anyway, so we can't break from this).
183 //Get the HID interface from the plugin to the mach port
184 if((*ppPlugin
)->QueryInterface(ppPlugin
,
185 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID
),
186 (void**) &m_ppDevice
) != S_OK
)
188 wxLogSysError(wxT("Could not get device interface from HID interface"));
193 (*ppPlugin
)->Release(ppPlugin
);
195 //open the HID interface...
196 if ( (*m_ppDevice
)->open(m_ppDevice
, 0) != S_OK
)
197 wxLogDebug(_T("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( _T("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 maching services
265 io_iterator_t pIterator
;
266 if( IOServiceGetMatchingServices(pPort
,
267 pDictionary
, &pIterator
) != kIOReturnSuccess
)
269 wxLogSysError(_T("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
)
320 wxLogDebug(_T("HID device: adding element failed"));
323 // ----------------------------------------------------------------------------
324 // wxHIDDevice::InitCookies
326 // Create the internal cookie array, optionally creating a HID Queue
327 // ----------------------------------------------------------------------------
328 void wxHIDDevice::InitCookies(size_t dwSize
, bool bQueue
)
330 m_pCookies
= new IOHIDElementCookie
[dwSize
];
333 wxASSERT( m_ppQueue
== NULL
);
334 m_ppQueue
= (*m_ppDevice
)->allocQueue(m_ppDevice
);
337 wxLogDebug(_T("HID device: allocQueue failed"));
341 //Param 2, flags, none yet
342 if ( (*m_ppQueue
)->create(m_ppQueue
, 0, 512) != S_OK
)
344 wxLogDebug(_T("HID device: create failed"));
348 //make sure that cookie array is clear
349 memset(m_pCookies
, 0, sizeof(*m_pCookies
) * dwSize
);
352 // ----------------------------------------------------------------------------
353 // wxHIDDevice::IsActive
355 // Returns true if a cookie of the device is active - for example if a key is
356 // held down, joystick button pressed, caps lock active, etc..
357 // ----------------------------------------------------------------------------
358 bool wxHIDDevice::IsActive(int nIndex
)
360 if(!HasElement(nIndex
))
362 //cookie at index does not exist - getElementValue
363 //could return true which would be incorrect so we
368 IOHIDEventStruct Event
;
369 (*m_ppDevice
)->getElementValue(m_ppDevice
, m_pCookies
[nIndex
], &Event
);
370 return !!Event
.value
;
373 // ----------------------------------------------------------------------------
374 // wxHIDDevice::HasElement
376 // Returns true if the element in the internal cookie array exists
377 // ----------------------------------------------------------------------------
378 bool wxHIDDevice::HasElement(int nIndex
)
380 return m_pCookies
[nIndex
] != NULL
;
383 // ----------------------------------------------------------------------------
384 // wxHIDDevice Destructor
386 // Frees all memory and objects from the structure
387 // ----------------------------------------------------------------------------
388 wxHIDDevice::~wxHIDDevice()
390 if (m_ppDevice
!= NULL
)
392 if (m_ppQueue
!= NULL
)
394 (*m_ppQueue
)->stop(m_ppQueue
);
395 (*m_ppQueue
)->dispose(m_ppQueue
);
396 (*m_ppQueue
)->Release(m_ppQueue
);
398 (*m_ppDevice
)->close(m_ppDevice
);
399 (*m_ppDevice
)->Release(m_ppDevice
);
400 mach_port_deallocate(mach_task_self(), m_pPort
);
403 if (m_pCookies
!= NULL
)
405 delete [] m_pCookies
;
409 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
413 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
415 //There are no right shift, alt etc. in the wx headers yet so just sort
416 //of "define our own" for now
425 // ----------------------------------------------------------------------------
426 // wxHIDKeyboard::GetCount [static]
428 // Get number of HID keyboards available
429 // ----------------------------------------------------------------------------
430 int wxHIDKeyboard::GetCount()
432 return wxHIDDevice::GetCount(kHIDPage_GenericDesktop
,
433 kHIDUsage_GD_Keyboard
);
436 // ----------------------------------------------------------------------------
437 // wxHIDKeyboard::Create
439 // Create the HID Keyboard
440 // ----------------------------------------------------------------------------
441 bool wxHIDKeyboard::Create(int nDev
/* = 1*/)
443 return wxHIDDevice::Create(kHIDPage_GenericDesktop
,
444 kHIDUsage_GD_Keyboard
,
448 // ----------------------------------------------------------------------------
449 // wxHIDKeyboard::AddCookie
451 // Overloaded version of wxHIDDevice::AddCookie that simply does not
452 // add a cookie if a duplicate is found
453 // ----------------------------------------------------------------------------
454 void wxHIDKeyboard::AddCookie(CFTypeRef Data
, int i
)
457 wxHIDDevice::AddCookie(Data
, i
);
460 // ----------------------------------------------------------------------------
461 // wxHIDKeyboard::BuildCookies
463 // Callback from Create() to build the HID cookies for the internal cookie
465 // ----------------------------------------------------------------------------
466 void wxHIDKeyboard::BuildCookies(CFArrayRef Array
)
468 //Create internal cookie array
471 //Begin recursing in array
472 DoBuildCookies(Array
);
475 void wxHIDKeyboard::DoBuildCookies(CFArrayRef Array
)
477 //Now go through each possible cookie
480 // bool bEOTriggered = false;
481 for (i
= 0; i
< CFArrayGetCount(Array
); ++i
)
483 const void* ref
= CFDictionaryGetValue(
484 (CFDictionaryRef
)CFArrayGetValueAtIndex(Array
, i
),
485 CFSTR(kIOHIDElementKey
)
490 DoBuildCookies((CFArrayRef
) ref
);
500 CFDictionaryGetValue((CFDictionaryRef
)
501 CFArrayGetValueAtIndex(Array
, i
),
502 CFSTR(kIOHIDElementUsageKey
)
508 // Now translate the usage # into a wx keycode
512 // OK, this is strange - basically this kind of strange -
513 // Starting from 0xEO these elements (like shift) appear twice in
514 // the array! The ones at the end are bogus I guess - the funny part
515 // is that besides the fact that the ones at the front have a Unit
516 // and UnitExponent key with a value of 0 and a different cookie value,
517 // there is no discernable difference between the two...
519 // Will the real shift please stand up?
521 // Something to spend a support request on, if I had one, LOL.
527 // bEOTriggered = true;
529 //Instead of that though we now just don't add duplicate keys
531 if (nUsage
>= kHIDUsage_KeyboardA
&& nUsage
<= kHIDUsage_KeyboardZ
)
532 AddCookie(CFArrayGetValueAtIndex(Array
, i
), 'A' + (nUsage
- kHIDUsage_KeyboardA
) );
533 else if (nUsage
>= kHIDUsage_Keyboard1
&& nUsage
<= kHIDUsage_Keyboard9
)
534 AddCookie(CFArrayGetValueAtIndex(Array
, i
), '1' + (nUsage
- kHIDUsage_Keyboard1
) );
535 else if (nUsage
>= kHIDUsage_KeyboardF1
&& nUsage
<= kHIDUsage_KeyboardF12
)
536 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F1
+ (nUsage
- kHIDUsage_KeyboardF1
) );
537 else if (nUsage
>= kHIDUsage_KeyboardF13
&& nUsage
<= kHIDUsage_KeyboardF24
)
538 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F13
+ (nUsage
- kHIDUsage_KeyboardF13
) );
539 else if (nUsage
>= kHIDUsage_Keypad1
&& nUsage
<= kHIDUsage_Keypad9
)
540 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD1
+ (nUsage
- kHIDUsage_Keypad1
) );
543 //0's (wx & ascii go 0-9, but HID goes 1-0)
544 case kHIDUsage_Keyboard0
:
545 AddCookie(CFArrayGetValueAtIndex(Array
, i
), '0');
547 case kHIDUsage_Keypad0
:
548 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD0
);
552 case kHIDUsage_KeyboardReturnOrEnter
:
553 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RETURN
);
555 case kHIDUsage_KeyboardEscape
:
556 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_ESCAPE
);
558 case kHIDUsage_KeyboardDeleteOrBackspace
:
559 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_BACK
);
561 case kHIDUsage_KeyboardTab
:
562 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_TAB
);
564 case kHIDUsage_KeyboardSpacebar
:
565 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_SPACE
);
567 case kHIDUsage_KeyboardPageUp
:
568 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEUP
);
570 case kHIDUsage_KeyboardEnd
:
571 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_END
);
573 case kHIDUsage_KeyboardPageDown
:
574 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEDOWN
);
576 case kHIDUsage_KeyboardRightArrow
:
577 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RIGHT
);
579 case kHIDUsage_KeyboardLeftArrow
:
580 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_LEFT
);
582 case kHIDUsage_KeyboardDownArrow
:
583 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_DOWN
);
585 case kHIDUsage_KeyboardUpArrow
:
586 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_UP
);
590 case kHIDUsage_KeyboardCapsLock
:
591 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CAPITAL
);
593 case kHIDUsage_KeypadNumLock
:
594 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_NUMLOCK
);
596 case kHIDUsage_KeyboardScrollLock
:
597 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SCROLL
);
600 //Menu keys, Shift, other specials
601 case kHIDUsage_KeyboardLeftControl
:
602 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CONTROL
);
604 case kHIDUsage_KeyboardLeftShift
:
605 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SHIFT
);
607 case kHIDUsage_KeyboardLeftAlt
:
608 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_ALT
);
610 case kHIDUsage_KeyboardLeftGUI
:
611 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_MENU
);
613 case kHIDUsage_KeyboardRightControl
:
614 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RCONTROL
);
616 case kHIDUsage_KeyboardRightShift
:
617 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RSHIFT
);
619 case kHIDUsage_KeyboardRightAlt
:
620 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RALT
);
622 case kHIDUsage_KeyboardRightGUI
:
623 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RMENU
);
628 //not in wx keycodes - do nothing....
630 } //end mightly long switch
631 } //end if the current element is not an array...
632 } //end for loop for Array
635 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
639 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
641 class wxHIDModule
: public wxModule
643 DECLARE_DYNAMIC_CLASS(wxHIDModule
)
646 static wxArrayPtrVoid sm_keyboards
;
647 virtual bool OnInit()
651 virtual void OnExit()
653 for(size_t i
= 0; i
< sm_keyboards
.GetCount(); ++i
)
654 delete (wxHIDKeyboard
*) sm_keyboards
[i
];
658 IMPLEMENT_DYNAMIC_CLASS(wxHIDModule
, wxModule
)
660 wxArrayPtrVoid
wxHIDModule::sm_keyboards
;
662 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
666 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
668 bool wxGetKeyState (wxKeyCode key
)
670 wxASSERT_MSG(key
!= WXK_LBUTTON
&& key
!= WXK_RBUTTON
&& key
!=
671 WXK_MBUTTON
, wxT("can't use wxGetKeyState() for mouse buttons"));
673 if (wxHIDModule::sm_keyboards
.GetCount() == 0)
675 int nKeyboards
= wxHIDKeyboard::GetCount();
677 for(int i
= 1; i
<= nKeyboards
; ++i
)
679 wxHIDKeyboard
* keyboard
= new wxHIDKeyboard();
680 if(keyboard
->Create(i
))
682 wxHIDModule::sm_keyboards
.Add(keyboard
);
691 wxASSERT_MSG(wxHIDModule::sm_keyboards
.GetCount() != 0,
692 wxT("No keyboards found!"));
695 for(size_t i
= 0; i
< wxHIDModule::sm_keyboards
.GetCount(); ++i
)
697 wxHIDKeyboard
* keyboard
= (wxHIDKeyboard
*)
698 wxHIDModule::sm_keyboards
[i
];
703 if( keyboard
->IsActive(WXK_SHIFT
) ||
704 keyboard
->IsActive(WXK_RSHIFT
) )
710 if( keyboard
->IsActive(WXK_ALT
) ||
711 keyboard
->IsActive(WXK_RALT
) )
717 if( keyboard
->IsActive(WXK_CONTROL
) ||
718 keyboard
->IsActive(WXK_RCONTROL
) )
724 if( keyboard
->IsActive(WXK_MENU
) ||
725 keyboard
->IsActive(WXK_RMENU
) )
731 if( keyboard
->IsActive(key
) )
739 return false; //not down/error