VirtualBox

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

Last change on this file since 16696 was 16696, checked in by vboxsync, 16 years ago

qt/darwin: another build fix.

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

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