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"
31 #include "wx/string.h"
33 #include "wx/mac/corefoundation/cfstring.h"
36 #include "wx/module.h"
37 #include "wx/dynarray.h"
39 // ============================================================================
41 // ============================================================================
43 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
47 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
49 // ----------------------------------------------------------------------------
50 // wxHIDDevice::Create
52 // nClass is the HID Page such as
53 // kHIDPage_GenericDesktop
54 // nType is the HID Usage such as
55 // kHIDUsage_GD_Joystick,kHIDUsage_GD_Mouse,kHIDUsage_GD_Keyboard
56 // nDev is the device number to use
58 // ----------------------------------------------------------------------------
59 bool wxHIDDevice::Create (int nClass
, int nType
, int nDev
)
61 //Create the mach port
62 if(IOMasterPort(bootstrap_port
, &m_pPort
) != kIOReturnSuccess
)
64 wxLogSysError(wxT("Could not create mach port"));
68 //Dictionary that will hold first
69 //the matching dictionary for determining which kind of devices we want,
70 //then later some registry properties from an iterator (see below)
72 //The call to IOServiceMatching filters down the
73 //the services we want to hid services (and also eats the
74 //dictionary up for us (consumes one reference))
75 CFMutableDictionaryRef pDictionary
= IOServiceMatching(kIOHIDDeviceKey
);
76 if(pDictionary
== NULL
)
78 wxLogSysError( _T("IOServiceMatching(kIOHIDDeviceKey) failed") );
82 //Here we'll filter down the services to what we want
85 CFNumberRef pType
= CFNumberCreate(kCFAllocatorDefault
,
86 kCFNumberIntType
, &nType
);
87 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsageKey
), pType
);
92 CFNumberRef pClass
= CFNumberCreate(kCFAllocatorDefault
,
93 kCFNumberIntType
, &nClass
);
94 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsagePageKey
), pClass
);
98 //Now get the maching services
99 io_iterator_t pIterator
;
100 if( IOServiceGetMatchingServices(m_pPort
,
101 pDictionary
, &pIterator
) != kIOReturnSuccess
)
103 wxLogSysError(_T("No Matching HID Services"));
107 //Were there any devices matched?
109 return false; // No devices found
111 //Now we iterate through them
113 while ( (pObject
= IOIteratorNext(pIterator
)) != 0)
117 IOObjectRelease(pObject
);
121 if ( IORegistryEntryCreateCFProperties
129 wxLogDebug(_T("IORegistryEntryCreateCFProperties failed"));
133 // Now we get the attributes of each "product" in the iterator
137 CFStringRef cfsProduct
= (CFStringRef
)
138 CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductKey
));
140 wxMacCFStringHolder( cfsProduct
,
144 //Get the Product ID Key
145 CFNumberRef cfnProductId
= (CFNumberRef
)
146 CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductIDKey
));
149 CFNumberGetValue(cfnProductId
, kCFNumberIntType
, &m_nProductId
);
152 //Get the Vendor ID Key
153 CFNumberRef cfnVendorId
= (CFNumberRef
)
154 CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDVendorIDKey
));
157 CFNumberGetValue(cfnVendorId
, kCFNumberIntType
, &m_nManufacturerId
);
161 // End attribute getting
164 //Create the interface (good grief - long function names!)
166 IOCFPlugInInterface
** ppPlugin
;
167 if(IOCreatePlugInInterfaceForService(pObject
,
168 kIOHIDDeviceUserClientTypeID
,
169 kIOCFPlugInInterfaceID
, &ppPlugin
,
170 &nScore
) != kIOReturnSuccess
)
172 wxLogSysError(wxT("Could not create HID Interface for product"));
176 //Now, the final thing we can check before we fall back to asserts
177 //(because the dtor only checks if the device is ok, so if anything
178 //fails from now on the dtor will delete the device anyway, so we can't break from this).
180 //Get the HID interface from the plugin to the mach port
181 if((*ppPlugin
)->QueryInterface(ppPlugin
,
182 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID
),
183 (void**) &m_ppDevice
) != S_OK
)
185 wxLogSysError(wxT("Could not get device interface from HID interface"));
190 (*ppPlugin
)->Release(ppPlugin
);
192 //open the HID interface...
193 if ( (*m_ppDevice
)->open(m_ppDevice
, 0) != S_OK
)
194 wxLogDebug(_T("HID device: open failed"));
197 //Now the hard part - in order to scan things we need "cookies"
199 CFArrayRef cfaCookies
= (CFArrayRef
)CFDictionaryGetValue(pDictionary
,
200 CFSTR(kIOHIDElementKey
));
201 BuildCookies(cfaCookies
);
204 CFRelease(pDictionary
);
205 IOObjectRelease(pObject
);
208 IOObjectRelease(pIterator
);
214 IOObjectRelease(pIterator
);
216 return false; //no device
219 // ----------------------------------------------------------------------------
220 // wxHIDDevice::GetCount [static]
222 // Obtains the number of devices on a system for a given HID Page (nClass)
223 // and HID Usage (nType).
224 // ----------------------------------------------------------------------------
225 size_t wxHIDDevice::GetCount (int nClass
, int nType
)
227 //Create the mach port
229 if(IOMasterPort(bootstrap_port
, &pPort
) != kIOReturnSuccess
)
231 wxLogSysError(wxT("Could not create mach port"));
235 //Dictionary that will hold first
236 //the matching dictionary for determining which kind of devices we want,
237 //then later some registry properties from an iterator (see below)
238 CFMutableDictionaryRef pDictionary
= IOServiceMatching(kIOHIDDeviceKey
);
239 if(pDictionary
== NULL
)
241 wxLogSysError( _T("IOServiceMatching(kIOHIDDeviceKey) failed") );
245 //Here we'll filter down the services to what we want
248 CFNumberRef pType
= CFNumberCreate(kCFAllocatorDefault
,
249 kCFNumberIntType
, &nType
);
250 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsageKey
), pType
);
255 CFNumberRef pClass
= CFNumberCreate(kCFAllocatorDefault
,
256 kCFNumberIntType
, &nClass
);
257 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsagePageKey
), pClass
);
261 //Now get the maching services
262 io_iterator_t pIterator
;
263 if( IOServiceGetMatchingServices(pPort
,
264 pDictionary
, &pIterator
) != kIOReturnSuccess
)
266 wxLogSysError(_T("No Matching HID Services"));
270 //If the iterator doesn't exist there are no devices :)
274 //Now we iterate through them
277 while ( (pObject
= IOIteratorNext(pIterator
)) != 0)
280 IOObjectRelease(pObject
);
284 IOObjectRelease(pIterator
);
285 mach_port_deallocate(mach_task_self(), pPort
);
290 // ----------------------------------------------------------------------------
291 // wxHIDDevice::AddCookie
293 // Adds a cookie to the internal cookie array from a CFType
294 // ----------------------------------------------------------------------------
295 void wxHIDDevice::AddCookie(CFTypeRef Data
, int i
)
298 (CFNumberRef
) CFDictionaryGetValue ( (CFDictionaryRef
) Data
299 , CFSTR(kIOHIDElementCookieKey
)
306 // ----------------------------------------------------------------------------
307 // wxHIDDevice::AddCookieInQueue
309 // Adds a cookie to the internal cookie array from a CFType and additionally
310 // adds it to the internal HID Queue
311 // ----------------------------------------------------------------------------
312 void wxHIDDevice::AddCookieInQueue(CFTypeRef Data
, int i
)
314 //3rd Param flags (none yet)
316 if ( (*m_ppQueue
)->addElement(m_ppQueue
, m_pCookies
[i
], 0) != S_OK
)
317 wxLogDebug(_T("HID device: adding element failed"));
320 // ----------------------------------------------------------------------------
321 // wxHIDDevice::InitCookies
323 // Create the internal cookie array, optionally creating a HID Queue
324 // ----------------------------------------------------------------------------
325 void wxHIDDevice::InitCookies(size_t dwSize
, bool bQueue
)
327 m_pCookies
= new IOHIDElementCookie
[dwSize
];
330 wxASSERT( m_ppQueue
== NULL
);
331 m_ppQueue
= (*m_ppDevice
)->allocQueue(m_ppDevice
);
334 wxLogDebug(_T("HID device: allocQueue failed"));
338 //Param 2, flags, none yet
339 if ( (*m_ppQueue
)->create(m_ppQueue
, 0, 512) != S_OK
)
341 wxLogDebug(_T("HID device: create failed"));
345 //make sure that cookie array is clear
346 memset(m_pCookies
, 0, sizeof(*m_pCookies
) * dwSize
);
349 // ----------------------------------------------------------------------------
350 // wxHIDDevice::IsActive
352 // Returns true if a cookie of the device is active - for example if a key is
353 // held down, joystick button pressed, caps lock active, etc..
354 // ----------------------------------------------------------------------------
355 bool wxHIDDevice::IsActive(int nIndex
)
357 if(!HasElement(nIndex
))
359 //cookie at index does not exist - getElementValue
360 //could return true which would be incorrect so we
365 IOHIDEventStruct Event
;
366 (*m_ppDevice
)->getElementValue(m_ppDevice
, m_pCookies
[nIndex
], &Event
);
367 return !!Event
.value
;
370 // ----------------------------------------------------------------------------
371 // wxHIDDevice::HasElement
373 // Returns true if the element in the internal cookie array exists
374 // ----------------------------------------------------------------------------
375 bool wxHIDDevice::HasElement(int nIndex
)
377 return m_pCookies
[nIndex
] != NULL
;
380 // ----------------------------------------------------------------------------
381 // wxHIDDevice Destructor
383 // Frees all memory and objects from the structure
384 // ----------------------------------------------------------------------------
385 wxHIDDevice::~wxHIDDevice()
387 if (m_ppDevice
!= NULL
)
389 if (m_ppQueue
!= NULL
)
391 (*m_ppQueue
)->stop(m_ppQueue
);
392 (*m_ppQueue
)->dispose(m_ppQueue
);
393 (*m_ppQueue
)->Release(m_ppQueue
);
395 (*m_ppDevice
)->close(m_ppDevice
);
396 (*m_ppDevice
)->Release(m_ppDevice
);
397 mach_port_deallocate(mach_task_self(), m_pPort
);
400 if (m_pCookies
!= NULL
)
402 delete [] m_pCookies
;
406 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
410 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
412 //There are no right shift, alt etc. in the wx headers yet so just sort
413 //of "define our own" for now
422 // ----------------------------------------------------------------------------
423 // wxHIDKeyboard::GetCount [static]
425 // Get number of HID keyboards available
426 // ----------------------------------------------------------------------------
427 int wxHIDKeyboard::GetCount()
429 return wxHIDDevice::GetCount(kHIDPage_GenericDesktop
,
430 kHIDUsage_GD_Keyboard
);
433 // ----------------------------------------------------------------------------
434 // wxHIDKeyboard::Create
436 // Create the HID Keyboard
437 // ----------------------------------------------------------------------------
438 bool wxHIDKeyboard::Create(int nDev
/* = 1*/)
440 return wxHIDDevice::Create(kHIDPage_GenericDesktop
,
441 kHIDUsage_GD_Keyboard
,
445 // ----------------------------------------------------------------------------
446 // wxHIDKeyboard::AddCookie
448 // Overloaded version of wxHIDDevice::AddCookie that simply does not
449 // add a cookie if a duplicate is found
450 // ----------------------------------------------------------------------------
451 void wxHIDKeyboard::AddCookie(CFTypeRef Data
, int i
)
454 wxHIDDevice::AddCookie(Data
, i
);
457 // ----------------------------------------------------------------------------
458 // wxHIDKeyboard::BuildCookies
460 // Callback from Create() to build the HID cookies for the internal cookie
462 // ----------------------------------------------------------------------------
463 void wxHIDKeyboard::BuildCookies(CFArrayRef Array
)
465 //Create internal cookie array
468 //Begin recursing in array
469 DoBuildCookies(Array
);
472 void wxHIDKeyboard::DoBuildCookies(CFArrayRef Array
)
474 //Now go through each possible cookie
477 // bool bEOTriggered = false;
478 for (i
= 0; i
< CFArrayGetCount(Array
); ++i
)
480 const void* ref
= CFDictionaryGetValue(
481 (CFDictionaryRef
)CFArrayGetValueAtIndex(Array
, i
),
482 CFSTR(kIOHIDElementKey
)
487 DoBuildCookies((CFArrayRef
) ref
);
497 CFDictionaryGetValue((CFDictionaryRef
)
498 CFArrayGetValueAtIndex(Array
, i
),
499 CFSTR(kIOHIDElementUsageKey
)
505 // Now translate the usage # into a wx keycode
509 // OK, this is strange - basically this kind of strange -
510 // Starting from 0xEO these elements (like shift) appear twice in
511 // the array! The ones at the end are bogus I guess - the funny part
512 // is that besides the fact that the ones at the front have a Unit
513 // and UnitExponent key with a value of 0 and a different cookie value,
514 // there is no discernable difference between the two...
516 // Will the real shift please stand up?
518 // Something to spend a support request on, if I had one, LOL.
524 // bEOTriggered = true;
526 //Instead of that though we now just don't add duplicate keys
528 if (nUsage
>= kHIDUsage_KeyboardA
&& nUsage
<= kHIDUsage_KeyboardZ
)
529 AddCookie(CFArrayGetValueAtIndex(Array
, i
), 'A' + (nUsage
- kHIDUsage_KeyboardA
) );
530 else if (nUsage
>= kHIDUsage_Keyboard1
&& nUsage
<= kHIDUsage_Keyboard9
)
531 AddCookie(CFArrayGetValueAtIndex(Array
, i
), '1' + (nUsage
- kHIDUsage_Keyboard1
) );
532 else if (nUsage
>= kHIDUsage_KeyboardF1
&& nUsage
<= kHIDUsage_KeyboardF12
)
533 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F1
+ (nUsage
- kHIDUsage_KeyboardF1
) );
534 else if (nUsage
>= kHIDUsage_KeyboardF13
&& nUsage
<= kHIDUsage_KeyboardF24
)
535 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_F13
+ (nUsage
- kHIDUsage_KeyboardF13
) );
536 else if (nUsage
>= kHIDUsage_Keypad1
&& nUsage
<= kHIDUsage_Keypad9
)
537 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD1
+ (nUsage
- kHIDUsage_Keypad1
) );
540 //0's (wx & ascii go 0-9, but HID goes 1-0)
541 case kHIDUsage_Keyboard0
:
542 AddCookie(CFArrayGetValueAtIndex(Array
, i
), '0');
544 case kHIDUsage_Keypad0
:
545 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_NUMPAD0
);
549 case kHIDUsage_KeyboardReturnOrEnter
:
550 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RETURN
);
552 case kHIDUsage_KeyboardEscape
:
553 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_ESCAPE
);
555 case kHIDUsage_KeyboardDeleteOrBackspace
:
556 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_BACK
);
558 case kHIDUsage_KeyboardTab
:
559 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_TAB
);
561 case kHIDUsage_KeyboardSpacebar
:
562 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_SPACE
);
564 case kHIDUsage_KeyboardPageUp
:
565 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEUP
);
567 case kHIDUsage_KeyboardEnd
:
568 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_END
);
570 case kHIDUsage_KeyboardPageDown
:
571 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_PAGEDOWN
);
573 case kHIDUsage_KeyboardRightArrow
:
574 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_RIGHT
);
576 case kHIDUsage_KeyboardLeftArrow
:
577 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_LEFT
);
579 case kHIDUsage_KeyboardDownArrow
:
580 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_DOWN
);
582 case kHIDUsage_KeyboardUpArrow
:
583 AddCookie(CFArrayGetValueAtIndex(Array
, i
), WXK_UP
);
587 case kHIDUsage_KeyboardCapsLock
:
588 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CAPITAL
);
590 case kHIDUsage_KeypadNumLock
:
591 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_NUMLOCK
);
593 case kHIDUsage_KeyboardScrollLock
:
594 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SCROLL
);
597 //Menu keys, Shift, other specials
598 case kHIDUsage_KeyboardLeftControl
:
599 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_CONTROL
);
601 case kHIDUsage_KeyboardLeftShift
:
602 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_SHIFT
);
604 case kHIDUsage_KeyboardLeftAlt
:
605 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_ALT
);
607 case kHIDUsage_KeyboardLeftGUI
:
608 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_MENU
);
610 case kHIDUsage_KeyboardRightControl
:
611 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RCONTROL
);
613 case kHIDUsage_KeyboardRightShift
:
614 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RSHIFT
);
616 case kHIDUsage_KeyboardRightAlt
:
617 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RALT
);
619 case kHIDUsage_KeyboardRightGUI
:
620 AddCookie(CFArrayGetValueAtIndex(Array
, i
),WXK_RMENU
);
625 //not in wx keycodes - do nothing....
627 } //end mightly long switch
628 } //end if the current element is not an array...
629 } //end for loop for Array
632 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
636 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
638 class wxHIDModule
: public wxModule
640 DECLARE_DYNAMIC_CLASS(wxHIDModule
)
643 static wxArrayPtrVoid sm_keyboards
;
644 virtual bool OnInit()
648 virtual void OnExit()
650 for(size_t i
= 0; i
< sm_keyboards
.GetCount(); ++i
)
651 delete (wxHIDKeyboard
*) sm_keyboards
[i
];
655 IMPLEMENT_DYNAMIC_CLASS(wxHIDModule
, wxModule
)
657 wxArrayPtrVoid
wxHIDModule::sm_keyboards
;
659 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
663 // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
665 bool wxGetKeyState (wxKeyCode key
)
667 wxASSERT_MSG(key
!= WXK_LBUTTON
&& key
!= WXK_RBUTTON
&& key
!=
668 WXK_MBUTTON
, wxT("can't use wxGetKeyState() for mouse buttons"));
670 if (wxHIDModule::sm_keyboards
.GetCount() == 0)
672 int nKeyboards
= wxHIDKeyboard::GetCount();
674 for(int i
= 1; i
<= nKeyboards
; ++i
)
676 wxHIDKeyboard
* keyboard
= new wxHIDKeyboard();
677 if(keyboard
->Create(i
))
679 wxHIDModule::sm_keyboards
.Add(keyboard
);
688 wxASSERT_MSG(wxHIDModule::sm_keyboards
.GetCount() != 0,
689 wxT("No keyboards found!"));
692 for(size_t i
= 0; i
< wxHIDModule::sm_keyboards
.GetCount(); ++i
)
694 wxHIDKeyboard
* keyboard
= (wxHIDKeyboard
*)
695 wxHIDModule::sm_keyboards
[i
];
700 if( keyboard
->IsActive(WXK_SHIFT
) ||
701 keyboard
->IsActive(WXK_RSHIFT
) )
707 if( keyboard
->IsActive(WXK_ALT
) ||
708 keyboard
->IsActive(WXK_RALT
) )
714 if( keyboard
->IsActive(WXK_CONTROL
) ||
715 keyboard
->IsActive(WXK_RCONTROL
) )
721 if( keyboard
->IsActive(WXK_MENU
) ||
722 keyboard
->IsActive(WXK_RMENU
) )
728 if( keyboard
->IsActive(key
) )
736 return false; //not down/error