]> git.saurik.com Git - wxWidgets.git/blame - src/mac/corefoundation/hid.cpp
Now works when derived from wxTextCtrlBase, using wxScrollHelper.
[wxWidgets.git] / src / mac / corefoundation / hid.cpp
CommitLineData
ec8bd392
RN
1/////////////////////////////////////////////////////////////////////////////
2// Name: hid.cpp
3// Purpose: DARWIN HID layer for WX Implementation
4// Author: Ryan Norton
5// Modified by:
6// Created: 11/11/2003
7// RCS-ID: $Id$
8// Copyright: (c) Ryan Norton
9// Licence: wxWindows licence
10/////////////////////////////////////////////////////////////////////////////
11
1553abf4
RN
12// ===========================================================================
13// declarations
14// ===========================================================================
15
16// ---------------------------------------------------------------------------
17// headers
18// ---------------------------------------------------------------------------
19
1553abf4
RN
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
24 #pragma hdrstop
25#endif
26
ec8bd392
RN
27//DARWIN _ONLY_
28#ifdef __DARWIN__
29
4cb1d3da 30#include "wx/mac/corefoundation/hid.h"
ec8bd392
RN
31#include "wx/string.h"
32#include "wx/log.h"
65442ab6 33#include "wx/mac/corefoundation/cfstring.h"
7f71c4c8 34
1553abf4
RN
35
36// ---------------------------------------------------------------------------
37// assertion macros
38// ---------------------------------------------------------------------------
39
7f71c4c8
RN
40#define wxFORCECHECK_MSG(arg, msg) \
41{\
26dfc728
VZ
42 if (arg) \
43 {\
44 wxLogSysError(wxString::Format(wxT("Message:%s\nHID: %s failed!"), wxT(msg), wxT(#arg)));\
45 return false;\
46 }\
7f71c4c8
RN
47}
48#define wxIOCHECK(arg, msg) wxFORCECHECK_MSG(arg != kIOReturnSuccess, msg)
49#define wxKERNCHECK(arg, msg) wxFORCECHECK_MSG(arg != KERN_SUCCESS, msg)
50#define wxSCHECK(arg, msg) wxFORCECHECK_MSG(arg != S_OK, msg)
51
ec8bd392 52/*
7f71c4c8
RN
53void CFShowTypeIDDescription(CFTypeRef pData)
54{
26dfc728
VZ
55 if(!pData)
56 {
57 wxASSERT(false);
58 return;
59 }
60
61 wxMessageBox(
62 CFStringGetCStringPtr(
63 CFCopyTypeIDDescription(CFGetTypeID(pData)),CFStringGetSystemEncoding()
64 )
65 );
7f71c4c8 66}
ec8bd392 67*/
7f71c4c8
RN
68
69// ============================================================================
70// implementation
71// ============================================================================
72
1553abf4
RN
73// ---------------------------------------------------------------------------
74// wxHIDDevice
75// ---------------------------------------------------------------------------
7f71c4c8 76
4cb1d3da 77bool wxHIDDevice::Create (int nClass, int nType, int nDev)
7f71c4c8 78{
26dfc728
VZ
79 //Create the mach port
80 wxIOCHECK(IOMasterPort(bootstrap_port, &m_pPort), "Could not create mach port");
81
82 //Dictionary that will hold first
83 //the matching dictionary for determining which kind of devices we want,
84 //then later some registry properties from an iterator (see below)
a8ee8060 85 //
26dfc728
VZ
86 //The call to IOServiceMatching filters down the
87 //the services we want to hid services (and also eats the
88 //dictionary up for us (consumes one reference))
a8ee8060
VZ
89 CFMutableDictionaryRef pDictionary = IOServiceMatching(kIOHIDDeviceKey);
90 wxCHECK_MSG( pDictionary, false,
91 _T("IOServiceMatching(kIOHIDDeviceKey) failed") );
92
93 wxASSERT( pDictionary );
26dfc728
VZ
94
95 //Here we'll filter down the services to what we want
96 if (nType != -1)
97 {
98 CFNumberRef pType = CFNumberCreate(kCFAllocatorDefault,
99 kCFNumberIntType, &nType);
100 CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsageKey), pType);
101 CFRelease(pType);
102 }
103 if (nClass != -1)
104 {
105 CFNumberRef pClass = CFNumberCreate(kCFAllocatorDefault,
106 kCFNumberIntType, &nClass);
107 CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsagePageKey), pClass);
108 CFRelease(pClass);
109 }
110
111 //Now get the maching services
112 io_iterator_t pIterator;
113 wxIOCHECK(IOServiceGetMatchingServices(m_pPort, pDictionary, &pIterator), "No Matching HID Services");
114 wxASSERT_MSG(pIterator != 0, wxT("No devices found!"));
115
116 //Now we iterate through them
117 io_object_t pObject;
118 while ( (pObject = IOIteratorNext(pIterator)) != 0)
119 {
4cb1d3da
RN
120 if(--nDev != 0)
121 continue;
7f71c4c8 122
a8ee8060
VZ
123 if ( IORegistryEntryCreateCFProperties
124 (
125 pObject,
126 &pDictionary,
127 kCFAllocatorDefault,
128 kNilOptions
129 ) != KERN_SUCCESS )
130 {
131 wxLogDebug(_T("IORegistryEntryCreateCFProperties failed"));
132 }
26dfc728
VZ
133
134 //Just for sanity :)
135 wxASSERT(CFGetTypeID(CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDProductKey))) == CFStringGetTypeID());
136
65442ab6
RN
137/*
138 kIOHIDTransportKey;
139 kIOHIDVendorIDKey;
140 kIOHIDProductIDKey;
141 kIOHIDVersionNumberKey;
142 kIOHIDManufacturerKey;
143 kIOHIDSerialNumberKey;
144 if !kIOHIDLocationIDKey
145 kUSBDevicePropertyLocationID
146 kIOHIDPrimaryUsageKey
147kIOHIDPrimaryUsagePageKey
148idProduct
149idVendor
150USB Product Name
151*/
26dfc728
VZ
152 //Get [product] name
153 m_szProductName = wxMacCFStringHolder( (CFStringRef) CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDProductKey)), false ).AsString();
154
2ff29ddf 155 CFNumberRef nref = (CFNumberRef) CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDProductIDKey));
26dfc728 156
2ff29ddf
RN
157 if (nref)
158 CFNumberGetValue(
26dfc728
VZ
159 nref,
160 kCFNumberIntType,
65442ab6 161 &m_nProductId
26dfc728 162 );
65442ab6 163
2ff29ddf
RN
164 nref = (CFNumberRef) CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDVendorIDKey));
165 if (nref)
26dfc728
VZ
166 CFNumberGetValue(
167 nref,
168 kCFNumberIntType,
65442ab6 169 &m_nManufacturerId
26dfc728
VZ
170 );
171
172 //Create the interface (good grief - long function names!)
173 SInt32 nScore;
174 IOCFPlugInInterface** ppPlugin;
175 wxIOCHECK(IOCreatePlugInInterfaceForService(pObject, kIOHIDDeviceUserClientTypeID,
176 kIOCFPlugInInterfaceID, &ppPlugin, &nScore), "");
177
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).
181
182 //Get the HID interface from the plugin to the mach port
183 wxSCHECK((*ppPlugin)->QueryInterface(ppPlugin,
184 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), (void**) &m_ppDevice), "");
185
186 //release the plugin
187 (*ppPlugin)->Release(ppPlugin);
188
189 //open the HID interface...
a8ee8060
VZ
190 if ( (*m_ppDevice)->open(m_ppDevice, 0) != S_OK )
191 wxLogDebug(_T("HID device: open failed"));
26dfc728
VZ
192
193 //
194 //Now the hard part - in order to scan things we need "cookies" -
195 //
196 wxCFArray CookieArray = CFDictionaryGetValue(pDictionary, CFSTR(kIOHIDElementKey));
197 BuildCookies(CookieArray);
198
199 //cleanup
200 CFRelease(pDictionary);
201 IOObjectRelease(pObject);
202 break;
203 }
204 //iterator cleanup
205 IOObjectRelease(pIterator);
206
207 return true;
7f71c4c8 208}//end Create()
26dfc728 209
4cb1d3da
RN
210int wxHIDDevice::GetCount (int nClass, int nType)
211{
26dfc728
VZ
212 mach_port_t m_pPort;
213
214 //Create the mach port
215 wxIOCHECK(IOMasterPort(bootstrap_port, &m_pPort), "Could not create mach port");
216
217 //Dictionary that will hold first
218 //the matching dictionary for determining which kind of devices we want,
219 //then later some registry properties from an iterator (see below)
a8ee8060
VZ
220 CFMutableDictionaryRef pDictionary = IOServiceMatching(kIOHIDDeviceKey);
221 wxCHECK_MSG( pDictionary, 0,
222 _T("IOServiceMatching(kIOHIDDeviceKey) failed") );
26dfc728
VZ
223
224 //Here we'll filter down the services to what we want
225 if (nType != -1)
226 {
227 CFNumberRef pType = CFNumberCreate(kCFAllocatorDefault,
228 kCFNumberIntType, &nType);
229 CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsageKey), pType);
230 CFRelease(pType);
231 }
232 if (nClass != -1)
233 {
234 CFNumberRef pClass = CFNumberCreate(kCFAllocatorDefault,
235 kCFNumberIntType, &nClass);
236 CFDictionarySetValue(pDictionary, CFSTR(kIOHIDPrimaryUsagePageKey), pClass);
237 CFRelease(pClass);
238 }
239
240 //Now get the maching services
241 io_iterator_t pIterator;
242 wxIOCHECK(IOServiceGetMatchingServices(m_pPort, pDictionary, &pIterator), "No Matching HID Services");
243
a8ee8060 244 if ( !pIterator )
65442ab6 245 return 0;
4cb1d3da 246
26dfc728
VZ
247 //Now we iterate through them
248 io_object_t pObject;
249
4cb1d3da 250 int nCount = 0;
26dfc728
VZ
251
252 while ( (pObject = IOIteratorNext(pIterator)) != 0)
4cb1d3da 253 ++nCount;
26dfc728 254
4cb1d3da 255 //iterator cleanup
26dfc728
VZ
256 IOObjectRelease(pIterator);
257
258 return nCount;
4cb1d3da
RN
259}//end Create()
260
261void wxHIDDevice::AddCookie(CFTypeRef Data, int i)
7f71c4c8 262{
26dfc728
VZ
263 CFNumberGetValue(
264 (CFNumberRef) CFDictionaryGetValue ( (CFDictionaryRef) Data
265 , CFSTR(kIOHIDElementCookieKey)
266 ),
267 kCFNumberIntType,
268 &m_pCookies[i]
269 );
7f71c4c8
RN
270}
271
4cb1d3da 272void wxHIDDevice::AddCookieInQueue(CFTypeRef Data, int i)
7f71c4c8 273{
a8ee8060 274 //3rd Param flags (none yet)
26dfc728 275 AddCookie(Data, i);
a8ee8060
VZ
276 if ( (*m_ppQueue)->addElement(m_ppQueue, m_pCookies[i], 0) != S_OK )
277 wxLogDebug(_T("HID device: adding element failed"));
7f71c4c8 278}
26dfc728 279
4cb1d3da 280void wxHIDDevice::InitCookies(size_t dwSize, bool bQueue)
7f71c4c8 281{
26dfc728
VZ
282 m_pCookies = new IOHIDElementCookie[dwSize];
283 if (bQueue)
284 {
285 wxASSERT( m_ppQueue == NULL);
a8ee8060
VZ
286 m_ppQueue = (*m_ppDevice)->allocQueue(m_ppDevice);
287 if ( !m_ppQueue )
288 {
289 wxLogDebug(_T("HID device: allocQueue failed"));
290 return;
291 }
292
293 //Param 2, flags, none yet
294 if ( (*m_ppQueue)->create(m_ppQueue, 0, 512) != S_OK )
295 {
296 wxLogDebug(_T("HID device: create failed"));
297 }
26dfc728 298 }
7f71c4c8
RN
299}
300
4cb1d3da 301bool wxHIDDevice::IsActive(int nIndex)
7f71c4c8 302{
26dfc728
VZ
303 wxASSERT(m_pCookies[nIndex] != NULL);
304 IOHIDEventStruct Event;
305 (*m_ppDevice)->getElementValue(m_ppDevice, m_pCookies[nIndex], &Event);
09d27083
RN
306/*
307 wxString ss;
308 ss << _T("[") << (int) m_pCookies[nIndex] << _T("] = ") << Event.value << _T(" SIZE:") << Event.longValueSize;
26dfc728 309
09d27083
RN
310 wxLogDebug(ss);
311*/
26dfc728 312 return !!Event.value;
7f71c4c8 313}
26dfc728 314
4cb1d3da
RN
315bool wxHIDDevice::HasElement(int nIndex)
316{
317 return m_pCookies[nIndex] != NULL;
318}
7f71c4c8
RN
319
320wxHIDDevice::~wxHIDDevice()
321{
26dfc728
VZ
322 if (m_ppDevice != NULL)
323 {
324 if (m_ppQueue != NULL)
325 {
326 (*m_ppQueue)->stop(m_ppQueue);
327 (*m_ppQueue)->dispose(m_ppQueue);
328 (*m_ppQueue)->Release(m_ppQueue);
329 }
330 (*m_ppDevice)->close(m_ppDevice);
331 (*m_ppDevice)->Release(m_ppDevice);
332 mach_port_deallocate(mach_task_self(), m_pPort);
333 }
334
335 if (m_pCookies != NULL)
336 {
337 delete [] m_pCookies;
338 }
7f71c4c8 339}
7f71c4c8 340
1553abf4
RN
341// ---------------------------------------------------------------------------
342// wxHIDKeyboard
343// ---------------------------------------------------------------------------
344
7f71c4c8
RN
345enum
346{
26dfc728
VZ
347 WXK_RSHIFT = 400,
348 WXK_RALT,
349 WXK_RCONTROL,
350 WXK_RMENU
351
7f71c4c8
RN
352};
353
354bool wxHIDKeyboard::Create()
355{
26dfc728 356 return wxHIDDevice::Create(kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
7f71c4c8
RN
357}
358
359void wxHIDKeyboard::BuildCookies(wxCFArray& Array)
360{
26dfc728
VZ
361 Array = CFDictionaryGetValue((CFDictionaryRef)Array[0], CFSTR(kIOHIDElementKey));
362 InitCookies(500);
363 int i,
364 nUsage;
09d27083 365 bool bEOTriggered = false;
26dfc728
VZ
366 for (i = 0; i < Array.Count(); ++i)
367 {
368 CFNumberGetValue(
369 (CFNumberRef) CFDictionaryGetValue((CFDictionaryRef) Array[i], CFSTR(kIOHIDElementUsageKey)),
370 kCFNumberLongType, &nUsage);
371
09d27083 372 //
26dfc728 373 // OK, this is strange - basically this kind of strange -
09d27083
RN
374 // Starting from 0xEO these elements (like shift) appear twice in
375 // the array! The ones at the end are bogus I guess - the funny part
376 // is that besides the fact that the ones at the front have a Unit
377 // and UnitExponent key with a value of 0 and a different cookie value,
26dfc728 378 // there is no discernable difference between the two...
09d27083
RN
379 //
380 // Will the real shift please stand up?
381 //
382 // Something to spend a support request on, if I had one, LOL.
383 //
384 if(nUsage == 0xE0)
385 {
386 if(bEOTriggered)
387 break;
388 bEOTriggered = true;
389 }
390/*
391 wxString msg;
392 int cookie;
26dfc728
VZ
393 CFNumberGetValue(
394 (CFNumberRef) CFDictionaryGetValue ( (CFDictionaryRef) Array[i]
395 , CFSTR(kIOHIDElementCookieKey)
396 ),
397 kCFNumberIntType,
398 &cookie
399 );
09d27083
RN
400
401 msg << wxT("KEY:") << nUsage << wxT("COOKIE:") << cookie;
26dfc728 402 wxLogDebug(msg);
09d27083
RN
403*/
404
26dfc728
VZ
405 if (nUsage >= kHIDUsage_KeyboardA && nUsage <= kHIDUsage_KeyboardZ)
406 AddCookie(Array[i], 'A' + (nUsage - kHIDUsage_KeyboardA) );
407 else if (nUsage >= kHIDUsage_Keyboard1 && nUsage <= kHIDUsage_Keyboard9)
408 AddCookie(Array[i], '1' + (nUsage - kHIDUsage_Keyboard1) );
409 else if (nUsage >= kHIDUsage_KeyboardF1 && nUsage <= kHIDUsage_KeyboardF12)
410 AddCookie(Array[i], WXK_F1 + (nUsage - kHIDUsage_KeyboardF1) );
411 else if (nUsage >= kHIDUsage_KeyboardF13 && nUsage <= kHIDUsage_KeyboardF24)
412 AddCookie(Array[i], WXK_F13 + (nUsage - kHIDUsage_KeyboardF13) );
413 else if (nUsage >= kHIDUsage_Keypad1 && nUsage <= kHIDUsage_Keypad9)
414 AddCookie(Array[i], WXK_NUMPAD1 + (nUsage - kHIDUsage_Keypad1) );
415 else switch (nUsage)
416 {
417 //0's (wx & ascii go 0-9, but HID goes 1-0)
418 case kHIDUsage_Keyboard0:
419 AddCookie(Array[i],'0');
420 break;
421 case kHIDUsage_Keypad0:
422 AddCookie(Array[i],WXK_NUMPAD0);
423 break;
424
425 //Basic
426 case kHIDUsage_KeyboardReturnOrEnter:
427 AddCookie(Array[i], WXK_RETURN);
428 break;
429 case kHIDUsage_KeyboardEscape:
430 AddCookie(Array[i], WXK_ESCAPE);
431 break;
432 case kHIDUsage_KeyboardDeleteOrBackspace:
433 AddCookie(Array[i], WXK_BACK);
434 break;
435 case kHIDUsage_KeyboardTab:
436 AddCookie(Array[i], WXK_TAB);
437 break;
438 case kHIDUsage_KeyboardSpacebar:
439 AddCookie(Array[i], WXK_SPACE);
440 break;
441 case kHIDUsage_KeyboardPageUp:
442 AddCookie(Array[i], WXK_PRIOR);
443 break;
444 case kHIDUsage_KeyboardEnd:
445 AddCookie(Array[i], WXK_END);
446 break;
447 case kHIDUsage_KeyboardPageDown:
448 AddCookie(Array[i], WXK_NEXT);
449 break;
450 case kHIDUsage_KeyboardRightArrow:
451 AddCookie(Array[i], WXK_RIGHT);
452 break;
453 case kHIDUsage_KeyboardLeftArrow:
454 AddCookie(Array[i], WXK_LEFT);
455 break;
456 case kHIDUsage_KeyboardDownArrow:
457 AddCookie(Array[i], WXK_DOWN);
458 break;
459 case kHIDUsage_KeyboardUpArrow:
460 AddCookie(Array[i], WXK_UP);
461 break;
462
463 //LEDS
464 case kHIDUsage_KeyboardCapsLock:
465 AddCookie(Array[i],WXK_CAPITAL);
466 break;
467 case kHIDUsage_KeypadNumLock:
468 AddCookie(Array[i],WXK_NUMLOCK);
469 break;
470 case kHIDUsage_KeyboardScrollLock:
471 AddCookie(Array[i],WXK_SCROLL);
472 break;
473
474 //Menu keys, Shift, other specials
475 case kHIDUsage_KeyboardLeftControl:
476 AddCookie(Array[i],WXK_CONTROL);
477 break;
478 case kHIDUsage_KeyboardLeftShift:
479 AddCookie(Array[i],WXK_SHIFT);
480 break;
481 case kHIDUsage_KeyboardLeftAlt:
482 AddCookie(Array[i],WXK_ALT);
483 break;
484 case kHIDUsage_KeyboardLeftGUI:
485 AddCookie(Array[i],WXK_MENU);
486 break;
487 case kHIDUsage_KeyboardRightControl:
488 AddCookie(Array[i],WXK_RCONTROL);
489 break;
490 case kHIDUsage_KeyboardRightShift:
491 AddCookie(Array[i],WXK_RSHIFT);
492 break;
493 case kHIDUsage_KeyboardRightAlt:
494 AddCookie(Array[i],WXK_RALT);
495 break;
496 case kHIDUsage_KeyboardRightGUI:
497 AddCookie(Array[i],WXK_RMENU);
498 break;
499
500 //Default
501 default:
502 //not in wx keycodes - do nothing....
503 break;
504 }
505 }
7f71c4c8 506}//end buildcookies
ec8bd392 507
4cb1d3da
RN
508//
509// wxGetKeyState
510//
511
512#include "wx/utils.h"
513#include "wx/module.h"
514
515class wxHIDModule : public wxModule
516{
517 DECLARE_DYNAMIC_CLASS(wxHIDModule)
26dfc728 518
4cb1d3da
RN
519 public:
520 static wxHIDKeyboard* sm_keyboard;
26dfc728 521
4cb1d3da
RN
522 virtual bool OnInit()
523 {
524 sm_keyboard = NULL;
525 return true;
526 }
527 virtual void OnExit()
528 {
529 if (sm_keyboard)
530 delete sm_keyboard;
531 }
532};
533
534IMPLEMENT_DYNAMIC_CLASS(wxHIDModule, wxModule)
535
536wxHIDKeyboard* wxHIDModule::sm_keyboard;
537
538bool wxGetKeyState (wxKeyCode key)
539{
44353523
VZ
540 wxASSERT_MSG(key != WXK_LBUTTON && key != WXK_RBUTTON && key !=
541 WXK_MBUTTON, wxT("can't use wxGetKeyState() for mouse buttons"));
542
4cb1d3da
RN
543 if (!wxHIDModule::sm_keyboard)
544 {
545 wxHIDModule::sm_keyboard = new wxHIDKeyboard();
546 bool bOK = wxHIDModule::sm_keyboard->Create();
547 wxASSERT(bOK);
548 if(!bOK)
549 {
550 delete wxHIDModule::sm_keyboard;
551 wxHIDModule::sm_keyboard = NULL;
552 return false;
553 }
554 }
26dfc728 555
09d27083
RN
556 switch(key)
557 {
558 case WXK_SHIFT:
559 return wxHIDModule::sm_keyboard->IsActive(WXK_SHIFT) ||
560 wxHIDModule::sm_keyboard->IsActive(WXK_RSHIFT);
561 break;
26dfc728 562 case WXK_ALT:
09d27083
RN
563 return wxHIDModule::sm_keyboard->IsActive(WXK_ALT) ||
564 wxHIDModule::sm_keyboard->IsActive(WXK_RALT);
565 break;
26dfc728 566 case WXK_CONTROL:
09d27083
RN
567 return wxHIDModule::sm_keyboard->IsActive(WXK_CONTROL) ||
568 wxHIDModule::sm_keyboard->IsActive(WXK_RCONTROL);
569 break;
26dfc728 570 case WXK_MENU:
09d27083
RN
571 return wxHIDModule::sm_keyboard->IsActive(WXK_MENU) ||
572 wxHIDModule::sm_keyboard->IsActive(WXK_RMENU);
573 break;
574 default:
575 return wxHIDModule::sm_keyboard->IsActive(key);
576 break;
577 }
4cb1d3da
RN
578}
579
ec8bd392 580#endif //__DARWIN__