1
/////////////////////////////////////////////////////////////////////////////
2
// Name: src/osx/core/hid.cpp
3
// Purpose: DARWIN HID layer for WX Implementation
7
// RCS-ID: $Id: hid.cpp 70251 2012-01-03 10:34:14Z SC $
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"
27
#if wxOSX_USE_COCOA_OR_CARBON
29
#include "wx/osx/core/hid.h"
32
#include "wx/dynarray.h"
33
#include "wx/string.h"
36
#include "wx/module.h"
39
#include "wx/osx/core/cfstring.h"
41
// ============================================================================
43
// ============================================================================
45
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
49
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
51
// ----------------------------------------------------------------------------
52
// wxHIDDevice::Create
54
// nClass is the HID Page such as
55
// kHIDPage_GenericDesktop
56
// nType is the HID Usage such as
57
// kHIDUsage_GD_Joystick,kHIDUsage_GD_Mouse,kHIDUsage_GD_Keyboard
58
// nDev is the device number to use
60
// ----------------------------------------------------------------------------
61
bool wxHIDDevice::Create (int nClass, int nType, int nDev)
63
//Create the mach port
64
if(IOMasterPort(bootstrap_port, &m_pPort) != kIOReturnSuccess)
66
wxLogSysError(wxT("Could not create mach port"));
70
//Dictionary that will hold first
71
//the matching dictionary for determining which kind of devices we want,
72
//then later some registry properties from an iterator (see below)
74
//The call to IOServiceMatching filters down the
75
//the services we want to hid services (and also eats the
76
//dictionary up for us (consumes one reference))
77
CFMutableDictionaryRef pDictionary = IOServiceMatching(kIOHIDDeviceKey);
78
if(pDictionary == NULL)
80
wxLogSysError( wxT("IOServiceMatching(kIOHIDDeviceKey) failed") );
84
//Here we'll filter down the services to what we want
87
CFNumberRef pType = CFNumberCreate(kCFAllocatorDefault,
88
kCFNumberIntType, &nType);
89
CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsageKey), pType);
94
CFNumberRef pClass = CFNumberCreate(kCFAllocatorDefault,
95
kCFNumberIntType, &nClass);
96
CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsagePageKey), pClass);
100
//Now get the maching services
101
io_iterator_t pIterator;
102
if( IOServiceGetMatchingServices(m_pPort,
103
pDictionary, &pIterator) != kIOReturnSuccess )
105
wxLogSysError(wxT("No Matching HID Services"));
109
//Were there any devices matched?
111
return false; // No devices found
113
//Now we iterate through them
115
while ( (pObject = IOIteratorNext(pIterator)) != 0)
119
IOObjectRelease(pObject);
123
if ( IORegistryEntryCreateCFProperties
131
wxLogDebug(wxT("IORegistryEntryCreateCFProperties failed"));
135
// Now we get the attributes of each "product" in the iterator
139
CFStringRef cfsProduct = (CFStringRef)
140
CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDProductKey));
142
wxCFStringRef( wxCFRetain(cfsProduct)
145
//Get the Product ID Key
146
CFNumberRef cfnProductId = (CFNumberRef)
147
CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDProductIDKey));
150
CFNumberGetValue(cfnProductId, kCFNumberIntType, &m_nProductId);
153
//Get the Vendor ID Key
154
CFNumberRef cfnVendorId = (CFNumberRef)
155
CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDVendorIDKey));
158
CFNumberGetValue(cfnVendorId, kCFNumberIntType, &m_nManufacturerId);
162
// End attribute getting
165
//Create the interface (good grief - long function names!)
167
IOCFPlugInInterface** ppPlugin;
168
if(IOCreatePlugInInterfaceForService(pObject,
169
kIOHIDDeviceUserClientTypeID,
170
kIOCFPlugInInterfaceID, &ppPlugin,
171
&nScore) != kIOReturnSuccess)
173
wxLogSysError(wxT("Could not create HID Interface for product"));
177
//Now, the final thing we can check before we fall back to asserts
178
//(because the dtor only checks if the device is ok, so if anything
179
//fails from now on the dtor will delete the device anyway, so we can't break from this).
181
//Get the HID interface from the plugin to the mach port
182
if((*ppPlugin)->QueryInterface(ppPlugin,
183
CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
184
(void**) &m_ppDevice) != S_OK)
186
wxLogSysError(wxT("Could not get device interface from HID interface"));
191
(*ppPlugin)->Release(ppPlugin);
193
//open the HID interface...
194
if ( (*m_ppDevice)->open(m_ppDevice, 0) != S_OK )
196
wxLogDebug(wxT("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( wxT("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(wxT("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 )
321
wxLogDebug(wxT("HID device: adding element failed"));
325
// ----------------------------------------------------------------------------
326
// wxHIDDevice::InitCookies
328
// Create the internal cookie array, optionally creating a HID Queue
329
// ----------------------------------------------------------------------------
330
void wxHIDDevice::InitCookies(size_t dwSize, bool bQueue)
332
m_pCookies = new IOHIDElementCookie[dwSize];
335
wxASSERT( m_ppQueue == NULL);
336
m_ppQueue = (*m_ppDevice)->allocQueue(m_ppDevice);
339
wxLogDebug(wxT("HID device: allocQueue failed"));
343
//Param 2, flags, none yet
344
if ( (*m_ppQueue)->create(m_ppQueue, 0, 512) != S_OK )
346
wxLogDebug(wxT("HID device: create failed"));
350
//make sure that cookie array is clear
351
memset(m_pCookies, 0, sizeof(*m_pCookies) * dwSize);
354
// ----------------------------------------------------------------------------
355
// wxHIDDevice::IsActive
357
// Returns true if a cookie of the device is active - for example if a key is
358
// held down, joystick button pressed, caps lock active, etc..
359
// ----------------------------------------------------------------------------
360
bool wxHIDDevice::IsActive(int nIndex)
362
if(!HasElement(nIndex))
364
//cookie at index does not exist - getElementValue
365
//could return true which would be incorrect so we
370
IOHIDEventStruct Event;
371
(*m_ppDevice)->getElementValue(m_ppDevice, m_pCookies[nIndex], &Event);
372
return !!Event.value;
375
// ----------------------------------------------------------------------------
376
// wxHIDDevice::HasElement
378
// Returns true if the element in the internal cookie array exists
379
// ----------------------------------------------------------------------------
380
bool wxHIDDevice::HasElement(int nIndex)
382
return (void*) m_pCookies[nIndex] != NULL;
385
// ----------------------------------------------------------------------------
386
// wxHIDDevice Destructor
388
// Frees all memory and objects from the structure
389
// ----------------------------------------------------------------------------
390
wxHIDDevice::~wxHIDDevice()
392
if (m_ppDevice != NULL)
394
if (m_ppQueue != NULL)
396
(*m_ppQueue)->stop(m_ppQueue);
397
(*m_ppQueue)->dispose(m_ppQueue);
398
(*m_ppQueue)->Release(m_ppQueue);
400
(*m_ppDevice)->close(m_ppDevice);
401
(*m_ppDevice)->Release(m_ppDevice);
402
mach_port_deallocate(mach_task_self(), m_pPort);
405
if (m_pCookies != NULL)
407
delete [] m_pCookies;
411
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
415
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
417
//There are no right shift, alt etc. in the wx headers yet so just sort
418
//of "define our own" for now
427
// ----------------------------------------------------------------------------
428
// wxHIDKeyboard::GetCount [static]
430
// Get number of HID keyboards available
431
// ----------------------------------------------------------------------------
432
int wxHIDKeyboard::GetCount()
434
return wxHIDDevice::GetCount(kHIDPage_GenericDesktop,
435
kHIDUsage_GD_Keyboard);
438
// ----------------------------------------------------------------------------
439
// wxHIDKeyboard::Create
441
// Create the HID Keyboard
442
// ----------------------------------------------------------------------------
443
bool wxHIDKeyboard::Create(int nDev /* = 1*/)
445
return wxHIDDevice::Create(kHIDPage_GenericDesktop,
446
kHIDUsage_GD_Keyboard,
450
// ----------------------------------------------------------------------------
451
// wxHIDKeyboard::AddCookie
453
// Overloaded version of wxHIDDevice::AddCookie that simply does not
454
// add a cookie if a duplicate is found
455
// ----------------------------------------------------------------------------
456
void wxHIDKeyboard::AddCookie(CFTypeRef Data, int i)
459
wxHIDDevice::AddCookie(Data, i);
462
// ----------------------------------------------------------------------------
463
// wxHIDKeyboard::BuildCookies
465
// Callback from Create() to build the HID cookies for the internal cookie
467
// ----------------------------------------------------------------------------
468
void wxHIDKeyboard::BuildCookies(CFArrayRef Array)
470
//Create internal cookie array
473
//Begin recursing in array
474
DoBuildCookies(Array);
477
void wxHIDKeyboard::DoBuildCookies(CFArrayRef Array)
479
//Now go through each possible cookie
482
// bool bEOTriggered = false;
483
for (i = 0; i < CFArrayGetCount(Array); ++i)
485
const void* ref = CFDictionaryGetValue(
486
(CFDictionaryRef)CFArrayGetValueAtIndex(Array, i),
487
CFSTR(kIOHIDElementKey)
492
DoBuildCookies((CFArrayRef) ref);
502
CFDictionaryGetValue((CFDictionaryRef)
503
CFArrayGetValueAtIndex(Array, i),
504
CFSTR(kIOHIDElementUsageKey)
510
// Now translate the usage # into a wx keycode
514
// OK, this is strange - basically this kind of strange -
515
// Starting from 0xEO these elements (like shift) appear twice in
516
// the array! The ones at the end are bogus I guess - the funny part
517
// is that besides the fact that the ones at the front have a Unit
518
// and UnitExponent key with a value of 0 and a different cookie value,
519
// there is no discernable difference between the two...
521
// Will the real shift please stand up?
523
// Something to spend a support request on, if I had one, LOL.
529
// bEOTriggered = true;
531
//Instead of that though we now just don't add duplicate keys
533
if (nUsage >= kHIDUsage_KeyboardA && nUsage <= kHIDUsage_KeyboardZ)
534
AddCookie(CFArrayGetValueAtIndex(Array, i), 'A' + (nUsage - kHIDUsage_KeyboardA) );
535
else if (nUsage >= kHIDUsage_Keyboard1 && nUsage <= kHIDUsage_Keyboard9)
536
AddCookie(CFArrayGetValueAtIndex(Array, i), '1' + (nUsage - kHIDUsage_Keyboard1) );
537
else if (nUsage >= kHIDUsage_KeyboardF1 && nUsage <= kHIDUsage_KeyboardF12)
538
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_F1 + (nUsage - kHIDUsage_KeyboardF1) );
539
else if (nUsage >= kHIDUsage_KeyboardF13 && nUsage <= kHIDUsage_KeyboardF24)
540
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_F13 + (nUsage - kHIDUsage_KeyboardF13) );
541
else if (nUsage >= kHIDUsage_Keypad1 && nUsage <= kHIDUsage_Keypad9)
542
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_NUMPAD1 + (nUsage - kHIDUsage_Keypad1) );
545
//0's (wx & ascii go 0-9, but HID goes 1-0)
546
case kHIDUsage_Keyboard0:
547
AddCookie(CFArrayGetValueAtIndex(Array, i), '0');
549
case kHIDUsage_Keypad0:
550
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_NUMPAD0);
554
case kHIDUsage_KeyboardReturnOrEnter:
555
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_RETURN);
557
case kHIDUsage_KeyboardEscape:
558
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_ESCAPE);
560
case kHIDUsage_KeyboardDeleteOrBackspace:
561
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_BACK);
563
case kHIDUsage_KeyboardTab:
564
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_TAB);
566
case kHIDUsage_KeyboardSpacebar:
567
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_SPACE);
569
case kHIDUsage_KeyboardPageUp:
570
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_PAGEUP);
572
case kHIDUsage_KeyboardEnd:
573
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_END);
575
case kHIDUsage_KeyboardPageDown:
576
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_PAGEDOWN);
578
case kHIDUsage_KeyboardRightArrow:
579
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_RIGHT);
581
case kHIDUsage_KeyboardLeftArrow:
582
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_LEFT);
584
case kHIDUsage_KeyboardDownArrow:
585
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_DOWN);
587
case kHIDUsage_KeyboardUpArrow:
588
AddCookie(CFArrayGetValueAtIndex(Array, i), WXK_UP);
592
case kHIDUsage_KeyboardCapsLock:
593
AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_CAPITAL);
595
case kHIDUsage_KeypadNumLock:
596
AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_NUMLOCK);
598
case kHIDUsage_KeyboardScrollLock:
599
AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_SCROLL);
602
//Menu keys, Shift, other specials
603
case kHIDUsage_KeyboardLeftControl:
604
AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RAW_CONTROL);
606
case kHIDUsage_KeyboardLeftShift:
607
AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_SHIFT);
609
case kHIDUsage_KeyboardLeftAlt:
610
AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_ALT);
612
case kHIDUsage_KeyboardLeftGUI:
613
AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_CONTROL);
615
case kHIDUsage_KeyboardRightControl:
616
AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RAW_RCONTROL);
618
case kHIDUsage_KeyboardRightShift:
619
AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RSHIFT);
621
case kHIDUsage_KeyboardRightAlt:
622
AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RALT);
624
case kHIDUsage_KeyboardRightGUI:
625
AddCookie(CFArrayGetValueAtIndex(Array, i),WXK_RCONTROL);
630
//not in wx keycodes - do nothing....
632
} //end mightly long switch
633
} //end if the current element is not an array...
634
} //end for loop for Array
637
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
641
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
643
class wxHIDModule : public wxModule
645
DECLARE_DYNAMIC_CLASS(wxHIDModule)
648
static wxArrayPtrVoid sm_keyboards;
649
virtual bool OnInit()
653
virtual void OnExit()
655
for(size_t i = 0; i < sm_keyboards.GetCount(); ++i)
656
delete (wxHIDKeyboard*) sm_keyboards[i];
657
sm_keyboards.Clear();
661
IMPLEMENT_DYNAMIC_CLASS(wxHIDModule, wxModule)
663
wxArrayPtrVoid wxHIDModule::sm_keyboards;
665
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
669
// ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
671
bool wxGetKeyState (wxKeyCode key)
673
wxASSERT_MSG(key != WXK_LBUTTON && key != WXK_RBUTTON && key !=
674
WXK_MBUTTON, wxT("can't use wxGetKeyState() for mouse buttons"));
676
if (wxHIDModule::sm_keyboards.GetCount() == 0)
678
int nKeyboards = wxHIDKeyboard::GetCount();
680
for(int i = 1; i <= nKeyboards; ++i)
682
wxHIDKeyboard* keyboard = new wxHIDKeyboard();
683
if(keyboard->Create(i))
685
wxHIDModule::sm_keyboards.Add(keyboard);
694
wxASSERT_MSG(wxHIDModule::sm_keyboards.GetCount() != 0,
695
wxT("No keyboards found!"));
698
for(size_t i = 0; i < wxHIDModule::sm_keyboards.GetCount(); ++i)
700
wxHIDKeyboard* keyboard = (wxHIDKeyboard*)
701
wxHIDModule::sm_keyboards[i];
706
if( keyboard->IsActive(WXK_SHIFT) ||
707
keyboard->IsActive(WXK_RSHIFT) )
713
if( keyboard->IsActive(WXK_ALT) ||
714
keyboard->IsActive(WXK_RALT) )
720
if( keyboard->IsActive(WXK_CONTROL) ||
721
keyboard->IsActive(WXK_RCONTROL) )
726
case WXK_RAW_CONTROL:
727
if( keyboard->IsActive(WXK_RAW_CONTROL) ||
728
keyboard->IsActive(WXK_RAW_RCONTROL) )
734
if( keyboard->IsActive(key) )
742
return false; //not down/error