VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/darwin/DarwinKeyboard.cpp@ 2418

Last change on this file since 2418 was 2418, checked in by vboxsync, 18 years ago

Applied patch for Japanese keyboard from K.Seki.

File size: 40.1 KB
Line 
1/** @file
2 * Common GUI Library - Darwin Keyboard routines.
3 *
4 * @todo Move this up somewhere so that the two SDL GUIs can use parts of this code too (-HID crap).
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 */
22
23
24
25#define USE_HID_FOR_MODIFIERS
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#define LOG_GROUP LOG_GROUP_GUI
32#include "DarwinKeyboard.h"
33#include <iprt/assert.h>
34#include <iprt/asm.h>
35#include <iprt/time.h>
36#include <VBox/log.h>
37#ifdef DEBUG_PRINTF
38# include <iprt/stream.h>
39#endif
40
41#ifdef USE_HID_FOR_MODIFIERS
42# include <mach/mach.h>
43# include <mach/mach_error.h>
44# include <IOKit/IOKitLib.h>
45# include <IOKit/IOCFPlugIn.h>
46# include <IOKit/hid/IOHIDLib.h>
47# include <IOKit/hid/IOHIDUsageTables.h>
48# include <IOKit/usb/USB.h>
49# include <CoreFoundation/CoreFoundation.h>
50#endif
51#include <Carbon/Carbon.h>
52
53__BEGIN_DECLS
54/* Private interface in 10.3 and later. */
55typedef int CGSConnection;
56typedef enum
57{
58 kCGSGlobalHotKeyEnable = 0,
59 kCGSGlobalHotKeyDisable,
60 kCGSGlobalHotKeyInvalid = -1 /* bird */
61} CGSGlobalHotKeyOperatingMode;
62extern CGSConnection _CGSDefaultConnection(void);
63extern CGError CGSGetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode *enmMode);
64extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection Connection, CGSGlobalHotKeyOperatingMode enmMode);
65__END_DECLS
66
67
68/*******************************************************************************
69* Defined Constants And Macros *
70*******************************************************************************/
71
72#define QZ_RMETA 0x36
73#define QZ_LMETA 0x37
74#define QZ_LSHIFT 0x38
75#define QZ_CAPSLOCK 0x39
76#define QZ_LALT 0x3A
77#define QZ_LCTRL 0x3B
78#define QZ_RSHIFT 0x3C
79#define QZ_RALT 0x3D
80#define QZ_RCTRL 0x3E
81#define QZ_NUMLOCK 0x47
82
83/** short hand for an extended key. */
84#define K_EX VBOXKEY_EXTENDED
85/** short hand for a modifier key. */
86#define K_MOD VBOXKEY_MODIFIER
87/** short hand for a lock key. */
88#define K_LOCK VBOXKEY_LOCK
89
90#ifdef USE_HID_FOR_MODIFIERS
91
92/** An attempt at catching reference leaks. */
93#define MY_CHECK_CREFS(cRefs) do { AssertMsg(cRefs < 25, ("%ld\n", cRefs)); NOREF(cRefs); } while (0)
94
95#endif
96
97/*******************************************************************************
98* Global Variables *
99*******************************************************************************/
100/**
101 * This is derived partially from SDL_QuartzKeys.h and partially from testing.
102 *
103 * (The funny thing about the virtual scan codes on the mac is that they aren't
104 * offically documented, which is rather silly to say the least. Thus, the need
105 * for looking at SDL and other odd places for docs.)
106 */
107static const uint16_t g_aDarwinToSet1[] =
108{
109 /* set-1 SDL_QuartzKeys.h */
110 0x1e, /* QZ_a 0x00 */
111 0x1f, /* QZ_s 0x01 */
112 0x20, /* QZ_d 0x02 */
113 0x21, /* QZ_f 0x03 */
114 0x23, /* QZ_h 0x04 */
115 0x22, /* QZ_g 0x05 */
116 0x2c, /* QZ_z 0x06 */
117 0x2d, /* QZ_x 0x07 */
118 0x2e, /* QZ_c 0x08 */
119 0x2f, /* QZ_v 0x09 */
120 0x56, /* between lshift and z. 'INT 1'? */
121 0x30, /* QZ_b 0x0B */
122 0x10, /* QZ_q 0x0C */
123 0x11, /* QZ_w 0x0D */
124 0x12, /* QZ_e 0x0E */
125 0x13, /* QZ_r 0x0F */
126 0x15, /* QZ_y 0x10 */
127 0x14, /* QZ_t 0x11 */
128 0x02, /* QZ_1 0x12 */
129 0x03, /* QZ_2 0x13 */
130 0x04, /* QZ_3 0x14 */
131 0x05, /* QZ_4 0x15 */
132 0x07, /* QZ_6 0x16 */
133 0x06, /* QZ_5 0x17 */
134 0x0d, /* QZ_EQUALS 0x18 */
135 0x0a, /* QZ_9 0x19 */
136 0x08, /* QZ_7 0x1A */
137 0x0c, /* QZ_MINUS 0x1B */
138 0x09, /* QZ_8 0x1C */
139 0x0b, /* QZ_0 0x1D */
140 0x1b, /* QZ_RIGHTBRACKET 0x1E */
141 0x18, /* QZ_o 0x1F */
142 0x16, /* QZ_u 0x20 */
143 0x1a, /* QZ_LEFTBRACKET 0x21 */
144 0x17, /* QZ_i 0x22 */
145 0x19, /* QZ_p 0x23 */
146 0x1c, /* QZ_RETURN 0x24 */
147 0x26, /* QZ_l 0x25 */
148 0x24, /* QZ_j 0x26 */
149 0x28, /* QZ_QUOTE 0x27 */
150 0x25, /* QZ_k 0x28 */
151 0x27, /* QZ_SEMICOLON 0x29 */
152 0x2b, /* QZ_BACKSLASH 0x2A */
153 0x33, /* QZ_COMMA 0x2B */
154 0x35, /* QZ_SLASH 0x2C */
155 0x31, /* QZ_n 0x2D */
156 0x32, /* QZ_m 0x2E */
157 0x34, /* QZ_PERIOD 0x2F */
158 0x0f, /* QZ_TAB 0x30 */
159 0x39, /* QZ_SPACE 0x31 */
160 0x29, /* QZ_BACKQUOTE 0x32 */
161 0x0e, /* QZ_BACKSPACE 0x33 */
162 0x9c, /* QZ_IBOOK_ENTER 0x34 */
163 0x01, /* QZ_ESCAPE 0x35 */
164 0x5c|K_EX|K_MOD, /* QZ_RMETA 0x36 */
165 0x5b|K_EX|K_MOD, /* QZ_LMETA 0x37 */
166 0x2a|K_MOD, /* QZ_LSHIFT 0x38 */
167 0x3a|K_LOCK, /* QZ_CAPSLOCK 0x39 */
168 0x38|K_MOD, /* QZ_LALT 0x3A */
169 0x1d|K_MOD, /* QZ_LCTRL 0x3B */
170 0x36|K_MOD, /* QZ_RSHIFT 0x3C */
171 0x38|K_EX|K_MOD, /* QZ_RALT 0x3D */
172 0x1d|K_EX|K_MOD, /* QZ_RCTRL 0x3E */
173 0, /* */
174 0, /* */
175 0x53, /* QZ_KP_PERIOD 0x41 */
176 0, /* */
177 0x37, /* QZ_KP_MULTIPLY 0x43 */
178 0, /* */
179 0x4e, /* QZ_KP_PLUS 0x45 */
180 0, /* */
181 0x45|K_LOCK, /* QZ_NUMLOCK 0x47 */
182 0, /* */
183 0, /* */
184 0, /* */
185 0x35|K_EX, /* QZ_KP_DIVIDE 0x4B */
186 0x1c|K_EX, /* QZ_KP_ENTER 0x4C */
187 0, /* */
188 0x4a, /* QZ_KP_MINUS 0x4E */
189 0, /* */
190 0, /* */
191 0x0d/*?*/, /* QZ_KP_EQUALS 0x51 */
192 0x52, /* QZ_KP0 0x52 */
193 0x4f, /* QZ_KP1 0x53 */
194 0x50, /* QZ_KP2 0x54 */
195 0x51, /* QZ_KP3 0x55 */
196 0x4b, /* QZ_KP4 0x56 */
197 0x4c, /* QZ_KP5 0x57 */
198 0x4d, /* QZ_KP6 0x58 */
199 0x47, /* QZ_KP7 0x59 */
200 0, /* */
201 0x48, /* QZ_KP8 0x5B */
202 0x49, /* QZ_KP9 0x5C */
203 0x7d, /* yen, | (JIS) 0x5D */
204 0x73, /* _, ro (JIS) 0x5E */
205 0, /* */
206 0x3f, /* QZ_F5 0x60 */
207 0x40, /* QZ_F6 0x61 */
208 0x41, /* QZ_F7 0x62 */
209 0x3d, /* QZ_F3 0x63 */
210 0x42, /* QZ_F8 0x64 */
211 0x43, /* QZ_F9 0x65 */
212 0x29, /* Zen/Han (JIS) 0x66 */
213 0x57, /* QZ_F11 0x67 */
214 0x29, /* Zen/Han (JIS) 0x68 */
215 0x37|K_EX, /* QZ_PRINT / F13 0x69 */
216 0x63, /* QZ_F16 0x6A */
217 0x46|K_LOCK, /* QZ_SCROLLOCK 0x6B */
218 0, /* */
219 0x44, /* QZ_F10 0x6D */
220 0x5d|K_EX, /* */
221 0x58, /* QZ_F12 0x6F */
222 0, /* */
223 0/* 0xe1,0x1d,0x45*/, /* QZ_PAUSE 0x71 */
224 0x52|K_EX, /* QZ_INSERT / HELP 0x72 */
225 0x47|K_EX, /* QZ_HOME 0x73 */
226 0x49|K_EX, /* QZ_PAGEUP 0x74 */
227 0x53|K_EX, /* QZ_DELETE 0x75 */
228 0x3e, /* QZ_F4 0x76 */
229 0x4f|K_EX, /* QZ_END 0x77 */
230 0x3c, /* QZ_F2 0x78 */
231 0x51|K_EX, /* QZ_PAGEDOWN 0x79 */
232 0x3b, /* QZ_F1 0x7A */
233 0x4b|K_EX, /* QZ_LEFT 0x7B */
234 0x4d|K_EX, /* QZ_RIGHT 0x7C */
235 0x50|K_EX, /* QZ_DOWN 0x7D */
236 0x48|K_EX, /* QZ_UP 0x7E */
237 0x5e|K_EX, /* QZ_POWER 0x7F */ /* have different break key! */
238};
239
240
241/** Whether we've connected or not. */
242static bool g_fConnectedToCGS = false;
243/** Cached connection. */
244static CGSConnection g_CGSConnection;
245
246
247#ifdef USE_HID_FOR_MODIFIERS
248
249/** The IO Master Port. */
250static mach_port_t g_MasterPort = NULL;
251
252/** Keyboards in the cache. */
253static unsigned g_cKeyboards = 0;
254/** Array of cached keyboard data. */
255static struct KeyboardCacheData
256{
257 /** The device interface. */
258 IOHIDDeviceInterface **ppHidDeviceInterface;
259 /** The queue interface. */
260 IOHIDQueueInterface **ppHidQueueInterface;
261
262 /* cookie translation array. */
263 struct KeyboardCacheCookie
264 {
265 /** The cookie. */
266 IOHIDElementCookie Cookie;
267 /** The corresponding modifier mask. */
268 uint32_t fMask;
269 } aCookies[64];
270 /** Number of cookies in the array. */
271 unsigned cCookies;
272} g_aKeyboards[128];
273/** The keyboard cache creation timestamp. */
274static uint64_t g_u64KeyboardTS = 0;
275
276/** HID queue status. */
277static bool g_fHIDQueueEnabled;
278/** The current modifier mask. */
279static uint32_t g_fHIDModifierMask;
280/** The old modifier mask. */
281static uint32_t g_fOldHIDModifierMask;
282
283#endif /* USE_HID_FOR_MODIFIERS */
284
285
286/*******************************************************************************
287* Internal Functions *
288*******************************************************************************/
289static void darwinBruteForcePropertySearch(CFDictionaryRef DictRef, struct KeyboardCacheData *pKeyboardEntry);
290
291
292
293/**
294 * Converts a darwin (virtual) key code to a set 1 scan code.
295 *
296 * @returns set 1 scan code.
297 * @param uKeyCode The darwin key code.
298 */
299unsigned DarwinKeycodeToSet1Scancode(unsigned uKeyCode)
300{
301 if (uKeyCode >= RT_ELEMENTS(g_aDarwinToSet1))
302 return 0;
303 return g_aDarwinToSet1[uKeyCode];
304}
305
306
307/**
308 * Converts a single modifier to a set 1 scan code.
309 *
310 * @returns Set 1 scan code.
311 * @returns ~0U if more than one modifier is set.
312 * @param fModifiers The darwin modifier mask.
313 */
314unsigned DarwinModifierMaskToSet1Scancode(UInt32 fModifiers)
315{
316 unsigned uScanCode = DarwinModifierMaskToDarwinKeycode(fModifiers);
317 if (uScanCode < RT_ELEMENTS(g_aDarwinToSet1))
318 uScanCode = g_aDarwinToSet1[uScanCode];
319 else
320 Assert(uScanCode == ~0U);
321 return uScanCode;
322}
323
324
325/**
326 * Converts a single modifier to a darwin keycode.
327 *
328 * @returns Darwin keycode.
329 * @returns 0 if none of the support masks were set.
330 * @returns ~0U if more than one modifier is set.
331 * @param fModifiers The darwin modifier mask.
332 */
333unsigned DarwinModifierMaskToDarwinKeycode(UInt32 fModifiers)
334{
335 unsigned uKeyCode;
336
337 /** @todo find symbols for these keycodes... */
338 fModifiers &= shiftKey | rightShiftKey | controlKey | rightControlKey | optionKey | rightOptionKey | cmdKey
339 | kEventKeyModifierRightCmdKeyMask | kEventKeyModifierNumLockMask | alphaLock;
340 if (fModifiers == shiftKey)
341 uKeyCode = QZ_LSHIFT;
342 else if (fModifiers == rightShiftKey)
343 uKeyCode = QZ_RSHIFT;
344 else if (fModifiers == controlKey)
345 uKeyCode = QZ_LCTRL;
346 else if (fModifiers == rightControlKey)
347 uKeyCode = QZ_RCTRL;
348 else if (fModifiers == optionKey)
349 uKeyCode = QZ_LALT;
350 else if (fModifiers == rightOptionKey)
351 uKeyCode = QZ_RALT;
352 else if (fModifiers == cmdKey)
353 uKeyCode = QZ_LMETA;
354 else if (fModifiers == kEventKeyModifierRightCmdKeyMask /* hack */)
355 uKeyCode = QZ_RMETA;
356 else if (fModifiers == alphaLock)
357 uKeyCode = QZ_CAPSLOCK;
358 else if (fModifiers == kEventKeyModifierNumLockMask)
359 uKeyCode = QZ_NUMLOCK;
360 else if (fModifiers == 0)
361 uKeyCode = 0;
362 else
363 uKeyCode = ~0U; /* multiple */
364 return uKeyCode;
365}
366
367
368/**
369 * Converts a darwin keycode to a modifier mask.
370 *
371 * @returns Darwin modifier mask.
372 * @returns 0 if the keycode isn't a modifier we know.
373 * @param uKeyCode The darwin
374 */
375UInt32 DarwinKeyCodeToDarwinModifierMask(unsigned uKeyCode)
376{
377 UInt32 fModifiers;
378
379 /** @todo find symbols for these keycodes... */
380 if (uKeyCode == QZ_LSHIFT)
381 fModifiers = shiftKey;
382 else if (uKeyCode == QZ_RSHIFT)
383 fModifiers = rightShiftKey;
384 else if (uKeyCode == QZ_LCTRL)
385 fModifiers = controlKey;
386 else if (uKeyCode == QZ_RCTRL)
387 fModifiers = rightControlKey;
388 else if (uKeyCode == QZ_LALT)
389 fModifiers = optionKey;
390 else if (uKeyCode == QZ_RALT)
391 fModifiers = rightOptionKey;
392 else if (uKeyCode == QZ_LMETA)
393 fModifiers = cmdKey;
394 else if (uKeyCode == QZ_RMETA)
395 fModifiers = kEventKeyModifierRightCmdKeyMask; /* hack */
396 else if (uKeyCode == QZ_CAPSLOCK)
397 fModifiers = alphaLock;
398 else if (uKeyCode == QZ_NUMLOCK)
399 fModifiers = kEventKeyModifierNumLockMask;
400 else
401 fModifiers = 0;
402 return fModifiers;
403}
404
405
406/**
407 * Disables or enabled global hot keys.
408 *
409 * @param fDisable Pass 'true' to disable the hot keys, pass 'false' to re-enable them.
410 */
411void DarwinDisableGlobalHotKeys(bool fDisable)
412{
413 static unsigned s_cComplaints = 0;
414
415 /*
416 * Lazy connect to the core graphics service.
417 */
418 if (!g_fConnectedToCGS)
419 {
420 g_CGSConnection = _CGSDefaultConnection();
421 g_fConnectedToCGS = true;
422 }
423
424 /*
425 * Get the current mode.
426 */
427 CGSGlobalHotKeyOperatingMode enmMode = kCGSGlobalHotKeyInvalid;
428 CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmMode);
429 if ( enmMode != kCGSGlobalHotKeyEnable
430 && enmMode != kCGSGlobalHotKeyDisable)
431 {
432 AssertMsgFailed(("%d\n", enmMode));
433 if (s_cComplaints++ < 32)
434 LogRel(("DarwinDisableGlobalHotKeys: Unexpected enmMode=%d\n", enmMode));
435 return;
436 }
437
438 /*
439 * Calc the new mode.
440 */
441 if (fDisable)
442 {
443 if (enmMode != kCGSGlobalHotKeyEnable)
444 return;
445 enmMode = kCGSGlobalHotKeyDisable;
446 }
447 else
448 {
449 if (enmMode != kCGSGlobalHotKeyDisable)
450 return;
451 enmMode = kCGSGlobalHotKeyEnable;
452 }
453
454 /*
455 * Try set it and check the actual result.
456 */
457 CGSSetGlobalHotKeyOperatingMode(g_CGSConnection, enmMode);
458 CGSGlobalHotKeyOperatingMode enmNewMode = kCGSGlobalHotKeyInvalid;
459 CGSGetGlobalHotKeyOperatingMode(g_CGSConnection, &enmNewMode);
460 if (enmNewMode != enmMode)
461 {
462 /* If the screensaver kicks in we should ignore failure here. */
463 AssertMsg(enmMode == kCGSGlobalHotKeyEnable, ("enmNewMode=%d enmMode=%d\n", enmNewMode, enmMode));
464 if (s_cComplaints++ < 32)
465 LogRel(("DarwinDisableGlobalHotKeys: Failed to change mode; enmNewMode=%d enmMode=%d\n", enmNewMode, enmMode));
466 }
467}
468
469
470
471#ifdef USE_HID_FOR_MODIFIERS
472
473
474/**
475 * Callback function for consuming queued events.
476 *
477 * @param pvTarget queue?
478 * @param rcIn ?
479 * @param pvRefcon Pointer to the keyboard cache entry.
480 * @param pvSender ?
481 */
482static void darwinQueueCallback(void *pvTarget, IOReturn rcIn, void *pvRefcon, void *pvSender)
483{
484 struct KeyboardCacheData *pKeyboardEntry = (struct KeyboardCacheData *)pvRefcon;
485 if (!pKeyboardEntry->ppHidQueueInterface)
486 return;
487 NOREF(pvTarget);
488 NOREF(rcIn);
489 NOREF(pvSender);
490
491 /*
492 * Consume the events.
493 */
494 g_fOldHIDModifierMask = g_fHIDModifierMask;
495 for (;;)
496 {
497#ifdef DEBUG_PRINTF
498 RTPrintf("dbg-ev: "); RTStrmFlush(g_pStdOut);
499#endif
500 IOHIDEventStruct Event;
501 AbsoluteTime ZeroTime = {0,0};
502 IOReturn rc = (*pKeyboardEntry->ppHidQueueInterface)->getNextEvent(pKeyboardEntry->ppHidQueueInterface,
503 &Event, ZeroTime, 0);
504 if (rc != kIOReturnSuccess)
505 break;
506
507 /* Translate the cookie value to a modifier mask. */
508 uint32_t fMask = 0;
509 unsigned i = pKeyboardEntry->cCookies;
510 while (i-- > 0)
511 {
512 if (pKeyboardEntry->aCookies[i].Cookie == Event.elementCookie)
513 {
514 fMask = pKeyboardEntry->aCookies[i].fMask;
515 break;
516 }
517 }
518
519 /*
520 * Adjust the modifier mask.
521 *
522 * Note that we don't bother to deal with anyone pressing the same modifier
523 * on 2 or more keyboard. That's not worth the effort involved.
524 */
525 if (Event.value)
526 g_fHIDModifierMask |= fMask;
527 else
528 g_fHIDModifierMask &= ~fMask;
529#ifdef DEBUG_PRINTF
530 RTPrintf("t=%d c=%#x v=%#x cblv=%d lv=%p m=%#X\n", Event.type, Event.elementCookie, Event.value, Event.longValueSize, Event.value, fMask); RTStrmFlush(g_pStdOut);
531#endif
532 }
533#ifdef DEBUG_PRINTF
534 RTPrintf("dbg-ev: done\n"); RTStrmFlush(g_pStdOut);
535#endif
536}
537
538
539
540/**
541 * Element enumeration callback.
542 */
543static void darwinBruteForcePropertySearchApplier(const void *pvValue, void *pvCacheEntry)
544{
545 if (CFGetTypeID(pvValue) == CFDictionaryGetTypeID())
546 darwinBruteForcePropertySearch((CFMutableDictionaryRef)pvValue, (struct KeyboardCacheData *)pvCacheEntry);
547}
548
549
550/**
551 * Recurses thru the keyboard properties looking for certain keys.
552 *
553 * @remark Yes, this can probably be done in a more efficient way. If you
554 * know how to do this, don't hesitate to let us know!
555 */
556static void darwinBruteForcePropertySearch(CFDictionaryRef DictRef, struct KeyboardCacheData *pKeyboardEntry)
557{
558 CFTypeRef ObjRef;
559
560 /*
561 * Check for the usage page and usage key we want.
562 */
563 long lUsage;
564 ObjRef = CFDictionaryGetValue(DictRef, CFSTR(kIOHIDElementUsageKey));
565 if ( ObjRef
566 && CFGetTypeID(ObjRef) == CFNumberGetTypeID()
567 && CFNumberGetValue((CFNumberRef)ObjRef, kCFNumberLongType, &lUsage))
568 {
569 switch (lUsage)
570 {
571 case kHIDUsage_KeyboardLeftControl:
572 case kHIDUsage_KeyboardLeftShift:
573 case kHIDUsage_KeyboardLeftAlt:
574 case kHIDUsage_KeyboardLeftGUI:
575 case kHIDUsage_KeyboardRightControl:
576 case kHIDUsage_KeyboardRightShift:
577 case kHIDUsage_KeyboardRightAlt:
578 case kHIDUsage_KeyboardRightGUI:
579 {
580 long lPage;
581 ObjRef = CFDictionaryGetValue(DictRef, CFSTR(kIOHIDElementUsagePageKey));
582 if ( !ObjRef
583 || CFGetTypeID(ObjRef) != CFNumberGetTypeID()
584 || !CFNumberGetValue((CFNumberRef)ObjRef, kCFNumberLongType, &lPage)
585 || lPage != kHIDPage_KeyboardOrKeypad)
586 break;
587
588 if (pKeyboardEntry->cCookies >= RT_ELEMENTS(pKeyboardEntry->aCookies))
589 {
590 AssertMsgFailed(("too many cookies!\n"));
591 break;
592 }
593
594 /*
595 * Get the cookie and modifier mask.
596 */
597 long lCookie;
598 ObjRef = CFDictionaryGetValue(DictRef, CFSTR(kIOHIDElementCookieKey));
599 if ( !ObjRef
600 || CFGetTypeID(ObjRef) != CFNumberGetTypeID()
601 || !CFNumberGetValue((CFNumberRef)ObjRef, kCFNumberLongType, &lCookie))
602 break;
603
604 uint32_t fMask;
605 switch (lUsage)
606 {
607 case kHIDUsage_KeyboardLeftControl : fMask = controlKey; break;
608 case kHIDUsage_KeyboardLeftShift : fMask = shiftKey; break;
609 case kHIDUsage_KeyboardLeftAlt : fMask = optionKey; break;
610 case kHIDUsage_KeyboardLeftGUI : fMask = cmdKey; break;
611 case kHIDUsage_KeyboardRightControl: fMask = rightControlKey; break;
612 case kHIDUsage_KeyboardRightShift : fMask = rightShiftKey; break;
613 case kHIDUsage_KeyboardRightAlt : fMask = rightOptionKey; break;
614 case kHIDUsage_KeyboardRightGUI : fMask = kEventKeyModifierRightCmdKeyMask; break;
615 default: AssertMsgFailed(("%ld\n",lUsage)); fMask = 0; break;
616 }
617
618 /*
619 * If we've got a queue, add the cookie to the queue.
620 */
621 if (pKeyboardEntry->ppHidQueueInterface)
622 {
623 IOReturn rc = (*pKeyboardEntry->ppHidQueueInterface)->addElement(pKeyboardEntry->ppHidQueueInterface, (IOHIDElementCookie)lCookie, 0);
624 AssertMsg(rc == kIOReturnSuccess, ("rc=%d\n", rc));
625#ifdef DEBUG_PRINTF
626 RTPrintf("dbg-add: u=%#lx c=%#lx\n", lUsage, lCookie);
627#endif
628 }
629
630 /*
631 * Add the cookie to the keyboard entry.
632 */
633 pKeyboardEntry->aCookies[pKeyboardEntry->cCookies].Cookie = (IOHIDElementCookie)lCookie;
634 pKeyboardEntry->aCookies[pKeyboardEntry->cCookies].fMask = fMask;
635 ++pKeyboardEntry->cCookies;
636 break;
637 }
638 }
639 }
640
641
642 /*
643 * Get the elements key and recursivly iterate the elements looking
644 * for they key cookies.
645 */
646 ObjRef = CFDictionaryGetValue(DictRef, CFSTR(kIOHIDElementKey));
647 if ( ObjRef
648 && CFGetTypeID(ObjRef) == CFArrayGetTypeID())
649 {
650 CFArrayRef ArrayObjRef = (CFArrayRef)ObjRef;
651 CFRange Range = {0, CFArrayGetCount(ArrayObjRef)};
652 CFArrayApplyFunction(ArrayObjRef, Range, darwinBruteForcePropertySearchApplier, pKeyboardEntry);
653 }
654}
655
656
657/**
658 * Creates a keyboard cache entry.
659 *
660 * @returns true if the entry was created successfully, otherwise false.
661 * @param pKeyboardEntry Pointer to the entry.
662 * @param KeyboardDevice The keyboard device to create the entry for.
663 *
664 */
665static bool darwinHIDKeyboardCacheCreateEntry(struct KeyboardCacheData *pKeyboardEntry, io_object_t KeyboardDevice)
666{
667 unsigned long cRefs = 0;
668 memset(pKeyboardEntry, 0, sizeof(*pKeyboardEntry));
669
670 /*
671 * Query the HIDDeviceInterface for this HID (keyboard) object.
672 */
673 SInt32 Score = 0;
674 IOCFPlugInInterface **ppPlugInInterface = NULL;
675 IOReturn rc = IOCreatePlugInInterfaceForService(KeyboardDevice, kIOHIDDeviceUserClientTypeID,
676 kIOCFPlugInInterfaceID, &ppPlugInInterface, &Score);
677 if (rc == kIOReturnSuccess)
678 {
679 IOHIDDeviceInterface **ppHidDeviceInterface = NULL;
680 HRESULT hrc = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface,
681 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
682 (LPVOID *)&ppHidDeviceInterface);
683 cRefs = (*ppPlugInInterface)->Release(ppPlugInInterface); MY_CHECK_CREFS(cRefs);
684 ppPlugInInterface = NULL;
685 if (hrc == S_OK)
686 {
687 rc = (*ppHidDeviceInterface)->open(ppHidDeviceInterface, 0);
688 if (rc == kIOReturnSuccess)
689 {
690 /*
691 * create a removal callback.
692 */
693 /** @todo */
694
695
696 /*
697 * Create the queue so we can insert elements while searching the properties.
698 */
699 IOHIDQueueInterface **ppHidQueueInterface = (*ppHidDeviceInterface)->allocQueue(ppHidDeviceInterface);
700 if (ppHidQueueInterface)
701 {
702 rc = (*ppHidQueueInterface)->create(ppHidQueueInterface, 0, 32);
703 if (rc != kIOReturnSuccess)
704 {
705 AssertMsgFailed(("rc=%d\n", rc));
706 cRefs = (*ppHidQueueInterface)->Release(ppHidQueueInterface); MY_CHECK_CREFS(cRefs);
707 ppHidQueueInterface = NULL;
708 }
709 }
710 else
711 AssertFailed();
712 pKeyboardEntry->ppHidQueueInterface = ppHidQueueInterface;
713
714 /*
715 * Brute force getting of attributes.
716 */
717 /** @todo read up on how to do this in a less resource intensive way! Suggestions are welcome! */
718 CFMutableDictionaryRef PropertiesRef = 0;
719 kern_return_t krc = IORegistryEntryCreateCFProperties(KeyboardDevice, &PropertiesRef, kCFAllocatorDefault, kNilOptions);
720 if (krc == KERN_SUCCESS)
721 {
722 darwinBruteForcePropertySearch(PropertiesRef, pKeyboardEntry);
723 CFRelease(PropertiesRef);
724 }
725 else
726 AssertMsgFailed(("krc=%#x\n", krc));
727
728 if (ppHidQueueInterface)
729 {
730 /*
731 * Now install our queue callback.
732 */
733 CFRunLoopSourceRef RunLoopSrcRef = NULL;
734 rc = (*ppHidQueueInterface)->createAsyncEventSource(ppHidQueueInterface, &RunLoopSrcRef);
735 if (rc == kIOReturnSuccess)
736 {
737 CFRunLoopRef RunLoopRef = (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetMainEventLoop());
738 CFRunLoopAddSource(RunLoopRef, RunLoopSrcRef, kCFRunLoopDefaultMode);
739 }
740
741 /*
742 * Now install our queue callback.
743 */
744 rc = (*ppHidQueueInterface)->setEventCallout(ppHidQueueInterface, darwinQueueCallback, ppHidQueueInterface, pKeyboardEntry);
745 if (rc != kIOReturnSuccess)
746 AssertMsgFailed(("rc=%d\n", rc));
747 }
748
749 /*
750 * Complete the new keyboard cache entry.
751 */
752 pKeyboardEntry->ppHidDeviceInterface = ppHidDeviceInterface;
753 pKeyboardEntry->ppHidQueueInterface = ppHidQueueInterface;
754 return true;
755 }
756
757 AssertMsgFailed(("rc=%d\n", rc));
758 cRefs = (*ppHidDeviceInterface)->Release(ppHidDeviceInterface); MY_CHECK_CREFS(cRefs);
759 }
760 else
761 AssertMsgFailed(("hrc=%#x\n", hrc));
762 }
763 else
764 AssertMsgFailed(("rc=%d\n", rc));
765
766 return false;
767}
768
769
770/**
771 * Destroys a keyboard cache entry.
772 *
773 * @param pKeyboardEntry The entry.
774 */
775static void darwinHIDKeyboardCacheDestroyEntry(struct KeyboardCacheData *pKeyboardEntry)
776{
777 unsigned long cRefs;
778
779 /*
780 * Destroy the queue
781 */
782 if (pKeyboardEntry->ppHidQueueInterface)
783 {
784 IOHIDQueueInterface **ppHidQueueInterface = pKeyboardEntry->ppHidQueueInterface;
785 pKeyboardEntry->ppHidQueueInterface = NULL;
786
787 /* stop it just in case we haven't done so. doesn't really matter I think. */
788 (*ppHidQueueInterface)->stop(ppHidQueueInterface);
789
790 /* deal with the run loop source. */
791 CFRunLoopSourceRef RunLoopSrcRef = (*ppHidQueueInterface)->getAsyncEventSource(ppHidQueueInterface);
792 if (RunLoopSrcRef)
793 {
794 CFRunLoopRef RunLoopRef = (CFRunLoopRef)GetCFRunLoopFromEventLoop(GetMainEventLoop());
795 CFRunLoopRemoveSource(RunLoopRef, RunLoopSrcRef, kCFRunLoopDefaultMode);
796
797 CFRelease(RunLoopSrcRef);
798 }
799
800 /* dispose of and release the queue. */
801 (*ppHidQueueInterface)->dispose(ppHidQueueInterface);
802 cRefs = (*ppHidQueueInterface)->Release(ppHidQueueInterface); MY_CHECK_CREFS(cRefs);
803 }
804
805 /*
806 * Release the removal hook?
807 */
808 /** @todo */
809
810 /*
811 * Close and release the device interface.
812 */
813 if (pKeyboardEntry->ppHidDeviceInterface)
814 {
815 IOHIDDeviceInterface **ppHidDeviceInterface = pKeyboardEntry->ppHidDeviceInterface;
816 pKeyboardEntry->ppHidDeviceInterface = NULL;
817
818 (*ppHidDeviceInterface)->close(ppHidDeviceInterface);
819 cRefs = (*ppHidDeviceInterface)->Release(ppHidDeviceInterface); MY_CHECK_CREFS(cRefs);
820 }
821}
822
823
824/**
825 * Zap the keyboard cache.
826 */
827static void darwinHIDKeyboardCacheZap(void)
828{
829 /*
830 * Release the old cache data first.
831 */
832 while (g_cKeyboards > 0)
833 {
834 unsigned i = --g_cKeyboards;
835 darwinHIDKeyboardCacheDestroyEntry(&g_aKeyboards[i]);
836 }
837}
838
839
840/**
841 * Updates the cached keyboard data.
842 *
843 * @todo The current implementation is very brute force...
844 * Rewrite it so that it doesn't flush the cache completely but simply checks whether
845 * anything has changed in the HID config. With any luck, there might even be a callback
846 * or something we can poll for HID config changes...
847 * setRemovalCallback() is a start...
848 */
849static void darwinHIDKeyboardCacheDoUpdate(void)
850{
851 g_u64KeyboardTS = RTTimeMilliTS();
852
853 /*
854 * Dispense with the old cache data.
855 */
856 darwinHIDKeyboardCacheZap();
857
858 /*
859 * Open the master port on the first invocation.
860 */
861 if (!g_MasterPort)
862 {
863 kern_return_t krc = IOMasterPort(MACH_PORT_NULL, &g_MasterPort);
864 AssertReturnVoid(krc == KERN_SUCCESS);
865 }
866
867 /*
868 * Create a matching dictionary for searching for keyboards devices.
869 */
870 static const UInt32 s_Page = kHIDPage_GenericDesktop;
871 static const UInt32 s_Usage = kHIDUsage_GD_Keyboard;
872 CFMutableDictionaryRef RefMatchingDict = IOServiceMatching(kIOHIDDeviceKey);
873 AssertReturnVoid(RefMatchingDict);
874 CFDictionarySetValue(RefMatchingDict, CFSTR(kIOHIDPrimaryUsagePageKey),
875 CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &s_Page));
876 CFDictionarySetValue(RefMatchingDict, CFSTR(kIOHIDPrimaryUsageKey),
877 CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &s_Usage));
878
879 /*
880 * Perform the search and get a collection of keyboard devices.
881 */
882 io_iterator_t Keyboards = NULL;
883 IOReturn rc = IOServiceGetMatchingServices(g_MasterPort, RefMatchingDict, &Keyboards);
884 AssertMsgReturnVoid(rc == kIOReturnSuccess, ("rc=%d\n", rc));
885 RefMatchingDict = NULL; /* the reference is consumed by IOServiceGetMatchingServices. */
886
887 /*
888 * Enumerate the keyboards and query the cache data.
889 */
890 unsigned i = 0;
891 io_object_t KeyboardDevice;
892 while ( i < RT_ELEMENTS(g_aKeyboards)
893 && (KeyboardDevice = IOIteratorNext(Keyboards)) != 0)
894 {
895 if (darwinHIDKeyboardCacheCreateEntry(&g_aKeyboards[i], KeyboardDevice))
896 i++;
897 IOObjectRelease(KeyboardDevice);
898 }
899 g_cKeyboards = i;
900
901 IOObjectRelease(Keyboards);
902}
903
904
905/**
906 * Updates the keyboard cache if it's time to do it again.
907 */
908static void darwinHIDKeyboardCacheUpdate(void)
909{
910 if ( !g_cKeyboards
911 /*|| g_u64KeyboardTS - RTTimeMilliTS() > 7500*/ /* 7.5sec */)
912 darwinHIDKeyboardCacheDoUpdate();
913}
914
915
916/**
917 * Queries the modifier keys from the (IOKit) HID Manager.
918 *
919 * @returns Carbon modifier mask with right/left set correctly.
920 */
921static UInt32 darwinQueryHIDModifiers(void)
922{
923 /*
924 * Iterate thru the keyboards collecting their modifier masks.
925 */
926 UInt32 fHIDModifiers = 0;
927 unsigned i = g_cKeyboards;
928 while (i-- > 0)
929 {
930 IOHIDDeviceInterface **ppHidDeviceInterface = g_aKeyboards[i].ppHidDeviceInterface;
931 if (!ppHidDeviceInterface)
932 continue;
933
934 unsigned j = g_aKeyboards[i].cCookies;
935 while (j-- > 0)
936 {
937 IOHIDEventStruct HidEvent;
938 IOReturn rc = (*ppHidDeviceInterface)->getElementValue(ppHidDeviceInterface,
939 g_aKeyboards[i].aCookies[j].Cookie,
940 &HidEvent);
941 if (rc == kIOReturnSuccess)
942 {
943 if (HidEvent.value)
944 fHIDModifiers |= g_aKeyboards[i].aCookies[j].fMask;
945 }
946 else
947 AssertMsgFailed(("rc=%#x\n", rc));
948 }
949 }
950
951 return fHIDModifiers;
952}
953
954#endif /* USE_HID_FOR_MODIFIERS */
955
956
957
958/**
959 * Left / rigth adjust the modifier mask using the current
960 * keyboard state.
961 *
962 * @returns left/right adjusted fModifiers.
963 * @param fModifiers The mask to adjust.
964 */
965UInt32 DarwinAdjustModifierMask(UInt32 fModifiers)
966{
967#ifdef USE_HID_FOR_MODIFIERS
968 /*
969 * Update the keyboard cache.
970 */
971 darwinHIDKeyboardCacheUpdate();
972
973 /*
974 * Check if there is anything to adjust and perform the adjustment.
975 */
976 if (fModifiers & (shiftKey | rightShiftKey | controlKey | rightControlKey | optionKey | rightOptionKey | cmdKey | kEventKeyModifierRightCmdKeyMask))
977 {
978 const UInt32 fHIDModifiers = g_fHIDModifierMask;
979#ifdef DEBUG_PRINTF
980 RTPrintf("dbg-fHIDModifier=%#x fModifiers=%#x", fHIDModifiers, fModifiers);
981#endif
982 if ( (fModifiers & (rightShiftKey | shiftKey))
983 && (fHIDModifiers & (rightShiftKey | shiftKey)))
984 {
985 fModifiers &= ~(rightShiftKey | shiftKey);
986 fModifiers |= fHIDModifiers & (rightShiftKey | shiftKey);
987 }
988
989 if ( (fModifiers & (rightControlKey | controlKey))
990 && (fHIDModifiers & (rightControlKey | controlKey)))
991 {
992 fModifiers &= ~(rightControlKey | controlKey);
993 fModifiers |= fHIDModifiers & (rightControlKey | controlKey);
994 }
995
996 if ( (fModifiers & (optionKey | rightOptionKey))
997 && (fHIDModifiers & (optionKey | rightOptionKey)))
998 {
999 fModifiers &= ~(optionKey | rightOptionKey);
1000 fModifiers |= fHIDModifiers & (optionKey | rightOptionKey);
1001 }
1002
1003 if ( (fModifiers & (cmdKey | kEventKeyModifierRightCmdKeyMask))
1004 && (fHIDModifiers & (cmdKey | kEventKeyModifierRightCmdKeyMask)))
1005 {
1006 fModifiers &= ~(cmdKey | kEventKeyModifierRightCmdKeyMask);
1007 fModifiers |= fHIDModifiers & (cmdKey | kEventKeyModifierRightCmdKeyMask);
1008 }
1009#ifdef DEBUG_PRINTF
1010 RTPrintf(" -> %#x", fModifiers);
1011#endif
1012 }
1013#endif /* USE_HID_FOR_MODIFIERS */
1014
1015 return fModifiers;
1016}
1017
1018
1019/**
1020 * Start grabbing keyboard events.
1021 *
1022 * This only concerns itself with modifiers and disabling global hotkeys (if requested).
1023 *
1024 * @param fGlobalHotkeys Whether to disable global hotkeys or not.
1025 */
1026void DarwinGrabKeyboard(bool fGlobalHotkeys)
1027{
1028 LogFlow(("DarwinGrabKeyboard: fGlobalHotkeys=%RTbool\n", fGlobalHotkeys));
1029
1030#ifdef USE_HID_FOR_MODIFIERS
1031 /*
1032 * Update the keyboard cache.
1033 */
1034 darwinHIDKeyboardCacheUpdate();
1035
1036 /*
1037 * Start the keyboard queues and query the current mask.
1038 */
1039 g_fHIDQueueEnabled = true;
1040
1041 unsigned i = g_cKeyboards;
1042 while (i-- > 0)
1043 {
1044 if (g_aKeyboards[i].ppHidQueueInterface)
1045 (*g_aKeyboards[i].ppHidQueueInterface)->start(g_aKeyboards[i].ppHidQueueInterface);
1046 }
1047
1048 g_fHIDModifierMask = darwinQueryHIDModifiers();
1049#endif /* USE_HID_FOR_MODIFIERS */
1050
1051 /*
1052 * Disable hotkeys if requested.
1053 */
1054 if (fGlobalHotkeys)
1055 DarwinDisableGlobalHotKeys(true);
1056}
1057
1058
1059/**
1060 * Reverses the actions taken by DarwinGrabKeyboard.
1061 */
1062void DarwinReleaseKeyboard(void)
1063{
1064 LogFlow(("DarwinReleaseKeyboard\n"));
1065
1066 /*
1067 * Re-enable hotkeys.
1068 */
1069 DarwinDisableGlobalHotKeys(false);
1070
1071#ifdef USE_HID_FOR_MODIFIERS
1072 /*
1073 * Stop and drain the keyboard queues.
1074 */
1075 g_fHIDQueueEnabled = false;
1076
1077#if 0
1078 unsigned i = g_cKeyboards;
1079 while (i-- > 0)
1080 {
1081 if (g_aKeyboards[i].ppHidQueueInterface)
1082 {
1083
1084 (*g_aKeyboards[i].ppHidQueueInterface)->stop(g_aKeyboards[i].ppHidQueueInterface);
1085
1086 /* drain it */
1087 IOReturn rc;
1088 unsigned c = 0;
1089 do
1090 {
1091 IOHIDEventStruct Event;
1092 AbsoluteTime MaxTime = {0,0};
1093 rc = (*g_aKeyboards[i].ppHidQueueInterface)->getNextEvent(g_aKeyboards[i].ppHidQueueInterface,
1094 &Event, MaxTime, 0);
1095 } while ( rc == kIOReturnSuccess
1096 && c++ < 32);
1097 }
1098 }
1099#else
1100
1101 /*
1102 * Kill the keyboard cache.
1103 * This will hopefully fix the crash in getElementValue()/fillElementValue()...
1104 */
1105 darwinHIDKeyboardCacheZap();
1106#endif
1107
1108 /* Clear the modifier mask. */
1109 g_fHIDModifierMask = 0;
1110#endif /* USE_HID_FOR_MODIFIERS */
1111}
1112
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette