1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: DARWIN HID layer for WX Implementation
8 // Copyright: (c) Ryan Norton
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ===========================================================================
14 // ===========================================================================
16 // ---------------------------------------------------------------------------
18 // ---------------------------------------------------------------------------
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "hid.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
34 #include "wx/mac/corefoundation/hid.h"
35 #include "wx/string.h"
37 #include "wx/mac/corefoundation/cfstring.h"
40 // ---------------------------------------------------------------------------
42 // ---------------------------------------------------------------------------
44 #define wxFORCECHECK_MSG(arg, msg) \
48 wxLogSysError(wxString::Format(wxT("Message:%s\nHID: %s failed!"), wxT(msg), wxT(#arg)));\
52 #define wxIOCHECK(arg, msg) wxFORCECHECK_MSG(arg != kIOReturnSuccess, msg)
53 #define wxKERNCHECK(arg, msg) wxFORCECHECK_MSG(arg != KERN_SUCCESS, msg)
54 #define wxSCHECK(arg, msg) wxFORCECHECK_MSG(arg != S_OK, msg)
57 # define wxVERIFY(arg) wxASSERT(arg)
59 # define wxVERIFY(arg) arg
63 void CFShowTypeIDDescription(CFTypeRef pData)
72 CFStringGetCStringPtr(
73 CFCopyTypeIDDescription(CFGetTypeID(pData)),CFStringGetSystemEncoding()
79 // ============================================================================
81 // ============================================================================
83 // ---------------------------------------------------------------------------
85 // ---------------------------------------------------------------------------
87 bool wxHIDDevice::Create (int nClass
, int nType
, int nDev
)
89 //Create the mach port
90 wxIOCHECK(IOMasterPort(bootstrap_port
, &m_pPort
), "Could not create mach port");
92 //Dictionary that will hold first
93 //the matching dictionary for determining which kind of devices we want,
94 //then later some registry properties from an iterator (see below)
95 CFMutableDictionaryRef pDictionary
;
98 //The call to IOServiceMatching filters down the
99 //the services we want to hid services (and also eats the
100 //dictionary up for us (consumes one reference))
101 wxVERIFY((pDictionary
= IOServiceMatching(kIOHIDDeviceKey
)) != NULL
);
103 //Here we'll filter down the services to what we want
106 CFNumberRef pType
= CFNumberCreate(kCFAllocatorDefault
,
107 kCFNumberIntType
, &nType
);
108 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsageKey
), pType
);
113 CFNumberRef pClass
= CFNumberCreate(kCFAllocatorDefault
,
114 kCFNumberIntType
, &nClass
);
115 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsagePageKey
), pClass
);
119 //Now get the maching services
120 io_iterator_t pIterator
;
121 wxIOCHECK(IOServiceGetMatchingServices(m_pPort
, pDictionary
, &pIterator
), "No Matching HID Services");
122 wxASSERT_MSG(pIterator
!= 0, wxT("No devices found!"));
124 //Now we iterate through them
126 while ( (pObject
= IOIteratorNext(pIterator
)) != 0)
131 wxVERIFY(IORegistryEntryCreateCFProperties(pObject
, &pDictionary
,
132 kCFAllocatorDefault
, kNilOptions
) == KERN_SUCCESS
);
135 wxASSERT(CFGetTypeID(CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductKey
))) == CFStringGetTypeID());
141 kIOHIDVersionNumberKey;
142 kIOHIDManufacturerKey;
143 kIOHIDSerialNumberKey;
144 if !kIOHIDLocationIDKey
145 kUSBDevicePropertyLocationID
146 kIOHIDPrimaryUsageKey
147 kIOHIDPrimaryUsagePageKey
153 m_szProductName
= wxMacCFStringHolder( (CFStringRef
) CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductKey
)), false ).AsString();
155 CFNumberRef nref
= (CFNumberRef
) CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDProductIDKey
));
164 nref
= (CFNumberRef
) CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDVendorIDKey
));
172 //Create the interface (good grief - long function names!)
174 IOCFPlugInInterface
** ppPlugin
;
175 wxIOCHECK(IOCreatePlugInInterfaceForService(pObject
, kIOHIDDeviceUserClientTypeID
,
176 kIOCFPlugInInterfaceID
, &ppPlugin
, &nScore
), "");
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 wxSCHECK((*ppPlugin
)->QueryInterface(ppPlugin
,
184 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID
), (void**) &m_ppDevice
), "");
187 (*ppPlugin
)->Release(ppPlugin
);
189 //open the HID interface...
190 wxVERIFY((*m_ppDevice
)->open(m_ppDevice
, 0) == S_OK
);
193 //Now the hard part - in order to scan things we need "cookies" -
195 wxCFArray CookieArray
= CFDictionaryGetValue(pDictionary
, CFSTR(kIOHIDElementKey
));
196 BuildCookies(CookieArray
);
199 CFRelease(pDictionary
);
200 IOObjectRelease(pObject
);
204 IOObjectRelease(pIterator
);
209 int wxHIDDevice::GetCount (int nClass
, int nType
)
213 //Create the mach port
214 wxIOCHECK(IOMasterPort(bootstrap_port
, &m_pPort
), "Could not create mach port");
216 //Dictionary that will hold first
217 //the matching dictionary for determining which kind of devices we want,
218 //then later some registry properties from an iterator (see below)
219 CFMutableDictionaryRef pDictionary
;
221 //Create a dictionary
222 //The call to IOServiceMatching filters down the
223 //the services we want to hid services (and also eats the
224 //dictionary up for us (consumes one reference))
225 wxVERIFY((pDictionary
= IOServiceMatching(kIOHIDDeviceKey
)) != NULL
);
227 //Here we'll filter down the services to what we want
230 CFNumberRef pType
= CFNumberCreate(kCFAllocatorDefault
,
231 kCFNumberIntType
, &nType
);
232 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsageKey
), pType
);
237 CFNumberRef pClass
= CFNumberCreate(kCFAllocatorDefault
,
238 kCFNumberIntType
, &nClass
);
239 CFDictionarySetValue(pDictionary
, CFSTR(kIOHIDPrimaryUsagePageKey
), pClass
);
243 //Now get the maching services
244 io_iterator_t pIterator
;
245 wxIOCHECK(IOServiceGetMatchingServices(m_pPort
, pDictionary
, &pIterator
), "No Matching HID Services");
247 if(pIterator
== NULL
)
250 //Now we iterate through them
255 while ( (pObject
= IOIteratorNext(pIterator
)) != 0)
259 IOObjectRelease(pIterator
);
264 void wxHIDDevice::AddCookie(CFTypeRef Data
, int i
)
267 (CFNumberRef
) CFDictionaryGetValue ( (CFDictionaryRef
) Data
268 , CFSTR(kIOHIDElementCookieKey
)
275 void wxHIDDevice::AddCookieInQueue(CFTypeRef Data
, int i
)
278 wxVERIFY((*m_ppQueue
)->addElement(m_ppQueue
, m_pCookies
[i
], 0) == S_OK
);//3rd Param flags (none yet)
281 void wxHIDDevice::InitCookies(size_t dwSize
, bool bQueue
)
283 m_pCookies
= new IOHIDElementCookie
[dwSize
];
286 wxASSERT( m_ppQueue
== NULL
);
287 wxVERIFY( (m_ppQueue
= (*m_ppDevice
)->allocQueue(m_ppDevice
)) != NULL
);
288 wxVERIFY( (*m_ppQueue
)->create(m_ppQueue
, 0, 512) == S_OK
); //Param 2, flags, none yet
292 bool wxHIDDevice::IsActive(int nIndex
)
294 wxASSERT(m_pCookies
[nIndex
] != NULL
);
295 IOHIDEventStruct Event
;
296 (*m_ppDevice
)->getElementValue(m_ppDevice
, m_pCookies
[nIndex
], &Event
);
299 ss << _T("[") << (int) m_pCookies[nIndex] << _T("] = ") << Event.value << _T(" SIZE:") << Event.longValueSize;
303 return !!Event
.value
;
306 bool wxHIDDevice::HasElement(int nIndex
)
308 return m_pCookies
[nIndex
] != NULL
;
311 wxHIDDevice::~wxHIDDevice()
313 if (m_ppDevice
!= NULL
)
315 if (m_ppQueue
!= NULL
)
317 (*m_ppQueue
)->stop(m_ppQueue
);
318 (*m_ppQueue
)->dispose(m_ppQueue
);
319 (*m_ppQueue
)->Release(m_ppQueue
);
321 (*m_ppDevice
)->close(m_ppDevice
);
322 (*m_ppDevice
)->Release(m_ppDevice
);
323 mach_port_deallocate(mach_task_self(), m_pPort
);
326 if (m_pCookies
!= NULL
)
328 delete [] m_pCookies
;
332 // ---------------------------------------------------------------------------
334 // ---------------------------------------------------------------------------
345 bool wxHIDKeyboard::Create()
347 return wxHIDDevice::Create(kHIDPage_GenericDesktop
, kHIDUsage_GD_Keyboard
);
350 void wxHIDKeyboard::BuildCookies(wxCFArray
& Array
)
352 Array
= CFDictionaryGetValue((CFDictionaryRef
)Array
[0], CFSTR(kIOHIDElementKey
));
356 bool bEOTriggered
= false;
357 for (i
= 0; i
< Array
.Count(); ++i
)
360 (CFNumberRef
) CFDictionaryGetValue((CFDictionaryRef
) Array
[i
], CFSTR(kIOHIDElementUsageKey
)),
361 kCFNumberLongType
, &nUsage
);
364 // OK, this is strange - basically this kind of strange -
365 // Starting from 0xEO these elements (like shift) appear twice in
366 // the array! The ones at the end are bogus I guess - the funny part
367 // is that besides the fact that the ones at the front have a Unit
368 // and UnitExponent key with a value of 0 and a different cookie value,
369 // there is no discernable difference between the two...
371 // Will the real shift please stand up?
373 // Something to spend a support request on, if I had one, LOL.
385 (CFNumberRef) CFDictionaryGetValue ( (CFDictionaryRef) Array[i]
386 , CFSTR(kIOHIDElementCookieKey)
392 msg << wxT("KEY:") << nUsage << wxT("COOKIE:") << cookie;
396 if (nUsage
>= kHIDUsage_KeyboardA
&& nUsage
<= kHIDUsage_KeyboardZ
)
397 AddCookie(Array
[i
], 'A' + (nUsage
- kHIDUsage_KeyboardA
) );
398 else if (nUsage
>= kHIDUsage_Keyboard1
&& nUsage
<= kHIDUsage_Keyboard9
)
399 AddCookie(Array
[i
], '1' + (nUsage
- kHIDUsage_Keyboard1
) );
400 else if (nUsage
>= kHIDUsage_KeyboardF1
&& nUsage
<= kHIDUsage_KeyboardF12
)
401 AddCookie(Array
[i
], WXK_F1
+ (nUsage
- kHIDUsage_KeyboardF1
) );
402 else if (nUsage
>= kHIDUsage_KeyboardF13
&& nUsage
<= kHIDUsage_KeyboardF24
)
403 AddCookie(Array
[i
], WXK_F13
+ (nUsage
- kHIDUsage_KeyboardF13
) );
404 else if (nUsage
>= kHIDUsage_Keypad1
&& nUsage
<= kHIDUsage_Keypad9
)
405 AddCookie(Array
[i
], WXK_NUMPAD1
+ (nUsage
- kHIDUsage_Keypad1
) );
408 //0's (wx & ascii go 0-9, but HID goes 1-0)
409 case kHIDUsage_Keyboard0
:
410 AddCookie(Array
[i
],'0');
412 case kHIDUsage_Keypad0
:
413 AddCookie(Array
[i
],WXK_NUMPAD0
);
417 case kHIDUsage_KeyboardReturnOrEnter
:
418 AddCookie(Array
[i
], WXK_RETURN
);
420 case kHIDUsage_KeyboardEscape
:
421 AddCookie(Array
[i
], WXK_ESCAPE
);
423 case kHIDUsage_KeyboardDeleteOrBackspace
:
424 AddCookie(Array
[i
], WXK_BACK
);
426 case kHIDUsage_KeyboardTab
:
427 AddCookie(Array
[i
], WXK_TAB
);
429 case kHIDUsage_KeyboardSpacebar
:
430 AddCookie(Array
[i
], WXK_SPACE
);
432 case kHIDUsage_KeyboardPageUp
:
433 AddCookie(Array
[i
], WXK_PRIOR
);
435 case kHIDUsage_KeyboardEnd
:
436 AddCookie(Array
[i
], WXK_END
);
438 case kHIDUsage_KeyboardPageDown
:
439 AddCookie(Array
[i
], WXK_NEXT
);
441 case kHIDUsage_KeyboardRightArrow
:
442 AddCookie(Array
[i
], WXK_RIGHT
);
444 case kHIDUsage_KeyboardLeftArrow
:
445 AddCookie(Array
[i
], WXK_LEFT
);
447 case kHIDUsage_KeyboardDownArrow
:
448 AddCookie(Array
[i
], WXK_DOWN
);
450 case kHIDUsage_KeyboardUpArrow
:
451 AddCookie(Array
[i
], WXK_UP
);
455 case kHIDUsage_KeyboardCapsLock
:
456 AddCookie(Array
[i
],WXK_CAPITAL
);
458 case kHIDUsage_KeypadNumLock
:
459 AddCookie(Array
[i
],WXK_NUMLOCK
);
461 case kHIDUsage_KeyboardScrollLock
:
462 AddCookie(Array
[i
],WXK_SCROLL
);
465 //Menu keys, Shift, other specials
466 case kHIDUsage_KeyboardLeftControl
:
467 AddCookie(Array
[i
],WXK_CONTROL
);
469 case kHIDUsage_KeyboardLeftShift
:
470 AddCookie(Array
[i
],WXK_SHIFT
);
472 case kHIDUsage_KeyboardLeftAlt
:
473 AddCookie(Array
[i
],WXK_ALT
);
475 case kHIDUsage_KeyboardLeftGUI
:
476 AddCookie(Array
[i
],WXK_MENU
);
478 case kHIDUsage_KeyboardRightControl
:
479 AddCookie(Array
[i
],WXK_RCONTROL
);
481 case kHIDUsage_KeyboardRightShift
:
482 AddCookie(Array
[i
],WXK_RSHIFT
);
484 case kHIDUsage_KeyboardRightAlt
:
485 AddCookie(Array
[i
],WXK_RALT
);
487 case kHIDUsage_KeyboardRightGUI
:
488 AddCookie(Array
[i
],WXK_RMENU
);
493 //not in wx keycodes - do nothing....
503 #include "wx/utils.h"
504 #include "wx/module.h"
506 class wxHIDModule
: public wxModule
508 DECLARE_DYNAMIC_CLASS(wxHIDModule
)
511 static wxHIDKeyboard
* sm_keyboard
;
513 virtual bool OnInit()
518 virtual void OnExit()
525 IMPLEMENT_DYNAMIC_CLASS(wxHIDModule
, wxModule
)
527 wxHIDKeyboard
* wxHIDModule::sm_keyboard
;
529 bool wxGetKeyState (wxKeyCode key
)
531 wxASSERT_MSG(key
!= WXK_LBUTTON
&& key
!= WXK_RBUTTON
&& key
!=
532 WXK_MBUTTON
, wxT("can't use wxGetKeyState() for mouse buttons"));
534 if (!wxHIDModule::sm_keyboard
)
536 wxHIDModule::sm_keyboard
= new wxHIDKeyboard();
537 bool bOK
= wxHIDModule::sm_keyboard
->Create();
541 delete wxHIDModule::sm_keyboard
;
542 wxHIDModule::sm_keyboard
= NULL
;
550 return wxHIDModule::sm_keyboard
->IsActive(WXK_SHIFT
) ||
551 wxHIDModule::sm_keyboard
->IsActive(WXK_RSHIFT
);
554 return wxHIDModule::sm_keyboard
->IsActive(WXK_ALT
) ||
555 wxHIDModule::sm_keyboard
->IsActive(WXK_RALT
);
558 return wxHIDModule::sm_keyboard
->IsActive(WXK_CONTROL
) ||
559 wxHIDModule::sm_keyboard
->IsActive(WXK_RCONTROL
);
562 return wxHIDModule::sm_keyboard
->IsActive(WXK_MENU
) ||
563 wxHIDModule::sm_keyboard
->IsActive(WXK_RMENU
);
566 return wxHIDModule::sm_keyboard
->IsActive(key
);
572 // TODO: Find a better file to put this in
577 #include <CoreFoundation/CoreFoundation.h>
578 #include <ApplicationServices/ApplicationServices.h>
580 #include "wx/mac/private.h"
581 #include "LaunchServices.h"
585 #include "wx/mac/corefoundation/cfstring.h"
587 long wxMacExecute(wxChar
**argv
,
591 CFIndex cfiCount
= 0;
593 for(wxChar
** argvcopy
= argv
; *argvcopy
!= NULL
; ++argvcopy
)
598 if(cfiCount
== 0) //no file to launch?
600 wxLogDebug(wxT("wxMacExecute No file to launch!"));
604 CFURLRef cfurlApp
= CFURLCreateWithString(
606 wxMacCFStringHolder(*argv
++, wxLocale::GetSystemEncoding()),
610 CFBundleRef cfbApp
= CFBundleCreate(kCFAllocatorDefault
, cfurlApp
);
613 wxLogDebug(wxT("wxMacExecute Bad bundle"));
619 UInt32 dwBundleType
, dwBundleCreator
;
620 CFBundleGetPackageInfo(cfbApp
, &dwBundleType
, &dwBundleCreator
);
622 //Only call wxMacExecute for .app bundles - others could be actual unix programs
623 if(dwBundleType
!= 'APPL')
630 // We have a good bundle - so let's launch it!
633 CFMutableArrayRef cfaFiles
= CFArrayCreateMutable(kCFAllocatorDefault
, cfiCount
- 1, NULL
);
639 for( ; *argv
!= NULL
; ++argv
)
641 // wxLogDebug(*argv);
642 wxString sCurrentFile
;
644 if(wxURI(*argv
).IsReference())
645 sCurrentFile
= wxString(wxT("file://")) + *argv
;
647 sCurrentFile
= *argv
;
649 CFURLRef cfurlCurrentFile
= CFURLCreateWithString(
651 wxMacCFStringHolder(sCurrentFile
, wxLocale::GetSystemEncoding()),
653 wxASSERT(cfurlCurrentFile
);
662 LSLaunchURLSpec launchspec
;
663 launchspec
.appURL
= cfurlApp
;
664 launchspec
.itemURLs
= cfaFiles
;
665 launchspec
.passThruParams
= NULL
; //AEDesc*
666 launchspec
.launchFlags
= kLSLaunchDefaults
| kLSLaunchDontSwitch
; //TODO: Possibly be smarter with flags
667 launchspec
.asyncRefCon
= NULL
;
669 OSStatus status
= LSOpenFromURLSpec(&launchspec
,
670 NULL
); //2nd is CFURLRef* really launched
679 wxLogDebug(wxString::Format(wxT("wxMacExecute ERROR: %d")), (int)status
);