VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.cpp@ 35740

Last change on this file since 35740 was 35740, checked in by vboxsync, 13 years ago

FE/Qt4-OSX: Prevent the mouse from leaving the window in capture mode.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.8 KB
Line 
1/* $Id: UIKeyboardHandler.cpp 35740 2011-01-27 14:36:03Z vboxsync $ */
2/** @file
3 *
4 * VBox frontends: Qt GUI ("VirtualBox"):
5 * UIKeyboardHandler class implementation
6 */
7
8/*
9 * Copyright (C) 2010 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20/* Global includes */
21#include <QKeyEvent>
22
23/* Local includes */
24#include "VBoxGlobal.h"
25#include "VBoxProblemReporter.h"
26#include "UIActionsPool.h"
27#include "UIKeyboardHandlerNormal.h"
28#include "UIKeyboardHandlerFullscreen.h"
29#include "UIKeyboardHandlerSeamless.h"
30#include "UIKeyboardHandlerScale.h"
31#include "UIMouseHandler.h"
32#include "UISession.h"
33#include "UIMachineLogic.h"
34#include "UIMachineWindow.h"
35#include "UIMachineView.h"
36#include "UIHotKeyEditor.h"
37
38#ifdef Q_WS_X11
39# include <QX11Info>
40# include <X11/XKBlib.h>
41# include <X11/keysym.h>
42# ifdef KeyPress
43const int XFocusOut = FocusOut;
44const int XFocusIn = FocusIn;
45const int XKeyPress = KeyPress;
46const int XKeyRelease = KeyRelease;
47# undef KeyRelease
48# undef KeyPress
49# undef FocusOut
50# undef FocusIn
51# endif /* KeyPress */
52# include "XKeyboard.h"
53#endif /* Q_WS_X11 */
54
55#ifdef Q_WS_MAC
56# include "VBoxUtils-darwin.h"
57# include "DarwinKeyboard.h"
58# include "UICocoaApplication.h"
59# include <Carbon/Carbon.h>
60#endif /* Q_WS_MAC */
61
62/* Enums representing different keyboard-states: */
63enum { KeyExtended = 0x01, KeyPressed = 0x02, KeyPause = 0x04, KeyPrint = 0x08 };
64enum { IsKeyPressed = 0x01, IsExtKeyPressed = 0x02, IsKbdCaptured = 0x80 };
65
66#ifdef Q_WS_WIN
67UIKeyboardHandler* UIKeyboardHandler::m_spKeyboardHandler = 0;
68#endif /* Q_WS_WIN */
69
70/* Factory function to create keyboard-handler: */
71UIKeyboardHandler* UIKeyboardHandler::create(UIMachineLogic *pMachineLogic,
72 UIVisualStateType visualStateType)
73{
74 /* Prepare keyboard-handler: */
75 UIKeyboardHandler *pKeyboardHandler = 0;
76 /* Depending on visual-state type: */
77 switch (visualStateType)
78 {
79 case UIVisualStateType_Normal:
80 pKeyboardHandler = new UIKeyboardHandlerNormal(pMachineLogic);
81 break;
82 case UIVisualStateType_Fullscreen:
83 pKeyboardHandler = new UIKeyboardHandlerFullscreen(pMachineLogic);
84 break;
85 case UIVisualStateType_Seamless:
86 pKeyboardHandler = new UIKeyboardHandlerSeamless(pMachineLogic);
87 break;
88 case UIVisualStateType_Scale:
89 pKeyboardHandler = new UIKeyboardHandlerScale(pMachineLogic);
90 break;
91 default:
92 break;
93 }
94#ifdef Q_WS_WIN
95 /* Its required to have static pointer to created handler
96 * because windows keyboard-hook works only with static members: */
97 m_spKeyboardHandler = pKeyboardHandler;
98#endif /* Q_WS_WIN */
99 /* Return prepared keyboard-handler: */
100 return pKeyboardHandler;
101}
102
103/* Factory function to destroy keyboard-handler: */
104void UIKeyboardHandler::destroy(UIKeyboardHandler *pKeyboardHandler)
105{
106 /* Delete keyboard-handler: */
107#ifdef Q_WS_WIN
108 m_spKeyboardHandler = 0;
109#endif /* Q_WS_WIN */
110 delete pKeyboardHandler;
111}
112
113/* Prepare listened objects: */
114void UIKeyboardHandler::prepareListener(ulong uIndex, UIMachineWindow *pMachineWindow)
115{
116 /* If that window is NOT registered yet: */
117 if (!m_windows.contains(uIndex))
118 {
119 /* Add window: */
120 m_windows.insert(uIndex, pMachineWindow);
121 /* Install event-filter for window: */
122 m_windows[uIndex]->machineWindow()->installEventFilter(this);
123 }
124
125 /* If that view is NOT registered yet: */
126 if (!m_views.contains(uIndex))
127 {
128 /* Add view: */
129 m_views.insert(uIndex, pMachineWindow->machineView());
130 /* Install event-filter for view: */
131 m_views[uIndex]->installEventFilter(this);
132 }
133}
134
135/* Cleanup listened objects: */
136void UIKeyboardHandler::cleanupListener(ulong uIndex)
137{
138 /* Check if we should release keyboard first: */
139 if ((int)uIndex == m_iKeyboardCaptureViewIndex)
140 releaseKeyboard();
141
142 /* If window still registered: */
143 if (m_windows.contains(uIndex))
144 {
145 /* Remove window: */
146 m_windows.remove(uIndex);
147 }
148
149 /* If view still registered: */
150 if (m_views.contains(uIndex))
151 {
152 /* Remove view: */
153 m_views.remove(uIndex);
154 }
155}
156
157void UIKeyboardHandler::captureKeyboard(ulong uScreenId)
158{
159 /* Do NOT capture keyboard if its captured already: */
160 if (m_fIsKeyboardCaptured)
161 return;
162
163 /* If such view exists: */
164 if (m_views.contains(uScreenId))
165 {
166 /* Store new keyboard-captured state value: */
167 m_fIsKeyboardCaptured = true;
168
169 /* Remember which screen had captured keyboard: */
170 m_iKeyboardCaptureViewIndex = uScreenId;
171
172#if defined(Q_WS_WIN)
173 /* On Win, keyboard grabbing is ineffective, a low-level keyboard hook is used instead. */
174#elif defined(Q_WS_X11)
175 /* On X11, we are using passive XGrabKey for normal (windowed) mode
176 * instead of XGrabKeyboard (called by QWidget::grabKeyboard())
177 * because XGrabKeyboard causes a problem under metacity - a window cannot be moved
178 * using the mouse if it is currently actively grabbing the keyboard;
179 * For static modes we are using usual (active) keyboard grabbing. */
180 switch (machineLogic()->visualStateType())
181 {
182 /* If window is moveable we are making passive keyboard grab: */
183 case UIVisualStateType_Normal:
184 case UIVisualStateType_Scale:
185 {
186 XGrabKey(QX11Info::display(), AnyKey, AnyModifier, m_windows[m_iKeyboardCaptureViewIndex]->machineWindow()->winId(), False, GrabModeAsync, GrabModeAsync);
187 break;
188 }
189 /* If window is NOT moveable we are making active keyboard grab: */
190 case UIVisualStateType_Fullscreen:
191 case UIVisualStateType_Seamless:
192 {
193 /* Keyboard grabbing can fail because of some keyboard shortcut is still grabbed by window manager.
194 * We can't be sure this shortcut will be released at all, so we will retry to grab keyboard for 50 times,
195 * and after we will just ignore that issue: */
196 int cTriesLeft = 50;
197 while (cTriesLeft && XGrabKeyboard(QX11Info::display(), m_windows[m_iKeyboardCaptureViewIndex]->machineWindow()->winId(), False, GrabModeAsync, GrabModeAsync, CurrentTime)) { --cTriesLeft; }
198 break;
199 }
200 /* Should we try to grab keyboard in default case? I think - NO. */
201 default:
202 break;
203 }
204#elif defined(Q_WS_MAC)
205 /* On Mac, we use the Qt methods + disabling global hot keys + watching modifiers (for right/left separation). */
206 ::DarwinDisableGlobalHotKeys(true);
207 m_views[m_iKeyboardCaptureViewIndex]->grabKeyboard();
208#else
209 /* On other platforms we are just praying Qt method will work. */
210 m_views[m_iKeyboardCaptureViewIndex]->grabKeyboard();
211#endif
212
213 /* Notify all the listeners: */
214 emit keyboardStateChanged(keyboardState());
215 }
216}
217
218void UIKeyboardHandler::releaseKeyboard()
219{
220 /* Do NOT capture keyboard if its captured already: */
221 if (!m_fIsKeyboardCaptured)
222 return;
223
224 /* If such view exists: */
225 if (m_views.contains(m_iKeyboardCaptureViewIndex))
226 {
227 /* Store new keyboard-captured state value: */
228 m_fIsKeyboardCaptured = false;
229
230#if defined(Q_WS_WIN)
231 /* On Win, keyboard grabbing is ineffective, a low-level keyboard hook is used instead. */
232#elif defined(Q_WS_X11)
233 /* On X11, we are using passive XGrabKey for normal (windowed) mode
234 * instead of XGrabKeyboard (called by QWidget::grabKeyboard())
235 * because XGrabKeyboard causes a problem under metacity - a window cannot be moved
236 * using the mouse if it is currently actively grabbing the keyboard;
237 * For static modes we are using usual (active) keyboard grabbing. */
238 switch (machineLogic()->visualStateType())
239 {
240 /* If window is moveable we are making passive keyboard ungrab: */
241 case UIVisualStateType_Normal:
242 case UIVisualStateType_Scale:
243 {
244 XUngrabKey(QX11Info::display(), AnyKey, AnyModifier, m_windows[m_iKeyboardCaptureViewIndex]->machineWindow()->winId());
245 break;
246 }
247 /* If window is NOT moveable we are making active keyboard ungrab: */
248 case UIVisualStateType_Fullscreen:
249 case UIVisualStateType_Seamless:
250 {
251 XUngrabKeyboard(QX11Info::display(), CurrentTime);
252 break;
253 }
254 /* Should we try to release keyboard in default case? I think - NO. */
255 default:
256 break;
257 }
258#elif defined(Q_WS_MAC)
259 ::DarwinDisableGlobalHotKeys(false);
260 m_views[m_iKeyboardCaptureViewIndex]->releaseKeyboard();
261#else
262 m_views[m_iKeyboardCaptureViewIndex]->releaseKeyboard();
263#endif
264
265 /* Reset keyboard-capture index: */
266 m_iKeyboardCaptureViewIndex = -1;
267
268 /* Notify all the listeners: */
269 emit keyboardStateChanged(keyboardState());
270 }
271}
272
273void UIKeyboardHandler::releaseAllPressedKeys(bool aReleaseHostKey /* = true */)
274{
275 CKeyboard keyboard = session().GetConsole().GetKeyboard();
276 bool fSentRESEND = false;
277
278 /* Send a dummy scan code (RESEND) to prevent the guest OS from recognizing
279 * a single key click (for ex., Alt) and performing an unwanted action
280 * (for ex., activating the menu) when we release all pressed keys below.
281 * Note, that it's just a guess that sending RESEND will give the desired
282 * effect :), but at least it works with NT and W2k guests. */
283 for (uint i = 0; i < SIZEOF_ARRAY (m_pressedKeys); i++)
284 {
285 if (m_pressedKeys[i] & IsKeyPressed)
286 {
287 if (!fSentRESEND)
288 {
289 keyboard.PutScancode (0xFE);
290 fSentRESEND = true;
291 }
292 keyboard.PutScancode(i | 0x80);
293 }
294 else if (m_pressedKeys[i] & IsExtKeyPressed)
295 {
296 if (!fSentRESEND)
297 {
298 keyboard.PutScancode(0xFE);
299 fSentRESEND = true;
300 }
301 QVector <LONG> codes(2);
302 codes[0] = 0xE0;
303 codes[1] = i | 0x80;
304 keyboard.PutScancodes(codes);
305 }
306 m_pressedKeys[i] = 0;
307 }
308
309 if (aReleaseHostKey)
310 m_bIsHostComboPressed = false;
311
312#ifdef Q_WS_MAC
313 // TODO: To NaN: Please fix:
314// /* Clear most of the modifiers: */
315// m_darwinKeyModifiers &=
316// alphaLock | kEventKeyModifierNumLockMask |
317// (aReleaseHostKey ? 0 : ::DarwinKeyCodeToDarwinModifierMask(m_globalSettings.hostKey()));
318#endif
319
320 emit keyboardStateChanged(keyboardState());
321}
322
323/* Current keyboard state: */
324int UIKeyboardHandler::keyboardState() const
325{
326 return (m_fIsKeyboardCaptured ? UIViewStateType_KeyboardCaptured : 0) |
327 (m_bIsHostComboPressed ? UIViewStateType_HostKeyPressed : 0);
328}
329
330#if defined(Q_WS_WIN)
331
332bool UIKeyboardHandler::winEventFilter(MSG *pMsg, ulong uScreenId)
333{
334 /* Check if some system event should be filtered-out.
335 * Returning 'true' means filtering-out,
336 * Returning 'false' means passing event to Qt. */
337 bool fResult = false; /* Pass to Qt by default: */
338 switch (pMsg->message)
339 {
340 case WM_KEYDOWN:
341 case WM_KEYUP:
342 case WM_SYSKEYDOWN:
343 case WM_SYSKEYUP:
344 {
345 /* Check for the special flag possibly set at the end of this function: */
346 if (pMsg->lParam & (0x1 << 25))
347 {
348 pMsg->lParam &= ~(0x1 << 25);
349 fResult = false;
350 break;
351 }
352
353 /* Scancodes 0x80 and 0x00 are ignored: */
354 unsigned scan = (pMsg->lParam >> 16) & 0x7F;
355 if (!scan)
356 {
357 fResult = true;
358 break;
359 }
360
361 int vkey = pMsg->wParam;
362
363 int flags = 0;
364 if (pMsg->lParam & 0x1000000)
365 flags |= KeyExtended;
366 if (!(pMsg->lParam & 0x80000000))
367 flags |= KeyPressed;
368
369 /* Check for special Korean keys. Based on the keyboard layout selected
370 * on the host, the scancode in lParam might be 0x71/0x72 or 0xF1/0xF2.
371 * In either case, we must deliver 0xF1/0xF2 scancode to the guest when
372 * the key is pressed and nothing when it's released. */
373 if (scan == 0x71 || scan == 0x72)
374 {
375 scan |= 0x80;
376 flags = KeyPressed; /* Because a release would be ignored. */
377 vkey = VK_PROCESSKEY; /* In case it was 0xFF */
378 }
379
380 /* When one of the SHIFT keys is held and one of the cursor movement
381 * keys is pressed, Windows duplicates SHIFT press/release messages,
382 * but with the virtual key code set to 0xFF. These virtual keys are also
383 * sent in some other situations (Pause, PrtScn, etc.). Ignore suc messages. */
384 if (vkey == 0xFF)
385 {
386 fResult = true;
387 break;
388 }
389
390 switch (vkey)
391 {
392 case VK_SHIFT:
393 case VK_CONTROL:
394 case VK_MENU:
395 {
396 /* Overcome Win32 modifier key generalization: */
397 int keyscan = scan;
398 if (flags & KeyExtended)
399 keyscan |= 0xE000;
400 switch (keyscan)
401 {
402 case 0x002A: vkey = VK_LSHIFT; break;
403 case 0x0036: vkey = VK_RSHIFT; break;
404 case 0x001D: vkey = VK_LCONTROL; break;
405 case 0xE01D: vkey = VK_RCONTROL; break;
406 case 0x0038: vkey = VK_LMENU; break;
407 case 0xE038: vkey = VK_RMENU; break;
408 }
409 break;
410 }
411 case VK_NUMLOCK:
412 /* Win32 sets the extended bit for the NumLock key. Reset it: */
413 flags &= ~KeyExtended;
414 break;
415 case VK_SNAPSHOT:
416 flags |= KeyPrint;
417 break;
418 case VK_PAUSE:
419 flags |= KeyPause;
420 break;
421 }
422
423 bool result = keyEvent(vkey, scan, flags, uScreenId);
424 if (!result && m_fIsKeyboardCaptured)
425 {
426 /* keyEvent() returned that it didn't process the message, but since the
427 * keyboard is captured, we don't want to pass it to Windows. We just want
428 * to let Qt process the message (to handle non-alphanumeric <HOST>+key
429 * shortcuts for example). So send it directly to the window with the
430 * special flag in the reserved area of lParam (to avoid recursion). */
431 ::SendMessage(pMsg->hwnd, pMsg->message,
432 pMsg->wParam, pMsg->lParam | (0x1 << 25));
433 fResult = true;
434 break;
435 }
436
437 /* These special keys have to be handled by Windows as well to update the
438 * internal modifier state and to enable/disable the keyboard LED */
439 if (vkey == VK_NUMLOCK || vkey == VK_CAPITAL || vkey == VK_LSHIFT || vkey == VK_RSHIFT)
440 {
441 fResult = false;
442 break;
443 }
444
445 fResult = result;
446 break;
447 }
448 default:
449 break;
450 }
451 /* Return result: */
452 return fResult;
453}
454
455#elif defined(Q_WS_X11)
456
457static Bool UIKeyboardHandlerCompEvent(Display*, XEvent *pEvent, XPointer pvArg)
458{
459 XEvent *pKeyEvent = (XEvent*)pvArg;
460 if ((pEvent->type == XKeyPress) && (pEvent->xkey.keycode == pKeyEvent->xkey.keycode))
461 return True;
462 else
463 return False;
464}
465
466bool UIKeyboardHandler::x11EventFilter(XEvent *pEvent, ulong uScreenId)
467{
468 /* Check if some system event should be filtered-out.
469 * Returning 'true' means filtering-out,
470 * Returning 'false' means passing event to Qt. */
471 bool fResult = false; /* Pass to Qt by default: */
472 switch (pEvent->type)
473 {
474 /* We have to handle XFocusOut right here as this event is not passed to UIMachineView::event().
475 * Handling this event is important for releasing the keyboard before the screen saver gets active.
476 * See public ticket #3894: Apparently this makes problems with newer versions of Qt
477 * and this hack is probably not necessary anymore. So disable it for Qt >= 4.5.0. */
478 case XFocusOut:
479 case XFocusIn:
480 {
481 if (uisession()->isRunning())
482 {
483 if (VBoxGlobal::qtRTVersion() < ((4 << 16) | (5 << 8) | 0))
484 {
485 if (pEvent->type == XFocusIn)
486 {
487 /* Capture keyboard by chosen view number: */
488 captureKeyboard(uScreenId);
489 /* Reset the single-time disable capture flag: */
490 if (uisession()->isAutoCaptureDisabled())
491 uisession()->setAutoCaptureDisabled(false);
492 }
493 else
494 {
495 /* Release keyboard: */
496 releaseKeyboard();
497 /* And all pressed keys including host-one: */
498 releaseAllPressedKeys(true);
499 }
500 }
501 }
502 fResult = false;
503 }
504 case XKeyPress:
505 case XKeyRelease:
506 {
507 /* Translate the keycode to a PC scan code. */
508 unsigned scan = handleXKeyEvent(pEvent);
509
510 /* Scancodes 0x00 (no valid translation) and 0x80 are ignored: */
511 if (!scan & 0x7F)
512 {
513 fResult = true;
514 break;
515 }
516
517 /* Fix for http://www.virtualbox.org/ticket/1296:
518 * when X11 sends events for repeated keys, it always inserts an XKeyRelease before the XKeyPress. */
519 XEvent returnEvent;
520 if ((pEvent->type == XKeyRelease) && (XCheckIfEvent(pEvent->xkey.display, &returnEvent,
521 UIKeyboardHandlerCompEvent, (XPointer)pEvent) == True))
522 {
523 XPutBackEvent(pEvent->xkey.display, &returnEvent);
524 fResult = true;
525 break;
526 }
527
528 KeySym ks = ::XKeycodeToKeysym(pEvent->xkey.display, pEvent->xkey.keycode, 0);
529
530 int flags = 0;
531 if (scan >> 8)
532 flags |= KeyExtended;
533 if (pEvent->type == XKeyPress)
534 flags |= KeyPressed;
535
536 /* Remove the extended flag: */
537 scan &= 0x7F;
538
539 /* Special Korean keys must send scancode 0xF1/0xF2 when pressed and nothing
540 * when released.
541 */
542 if (scan == 0x71 || scan == 0x72)
543 {
544 if (pEvent->type == XKeyRelease) /* Ignore. */
545 {
546 fResult = true;
547 break;
548 }
549 scan |= 0x80; /* Re-create the bizarre scancode. */
550 }
551
552 switch (ks)
553 {
554 case XK_Print:
555 flags |= KeyPrint;
556 break;
557 case XK_Pause:
558 if (pEvent->xkey.state & ControlMask) /* Break */
559 {
560 ks = XK_Break;
561 flags |= KeyExtended;
562 scan = 0x46;
563 }
564 else
565 flags |= KeyPause;
566 break;
567 }
568
569 fResult = keyEvent(ks, scan, flags, uScreenId);
570 }
571 default:
572 break;
573 }
574 /* Return result: */
575 return fResult;
576}
577
578#endif
579
580/* Machine state-change handler: */
581void UIKeyboardHandler::sltMachineStateChanged()
582{
583 /* Get machine state: */
584 KMachineState state = uisession()->machineState();
585 /* Handle particular machine states: */
586 switch (state)
587 {
588 case KMachineState_Paused:
589 case KMachineState_TeleportingPausedVM:
590 case KMachineState_Stuck:
591 {
592 /* Release the keyboard: */
593 releaseKeyboard();
594 /* And all pressed keys except the host-one : */
595 releaseAllPressedKeys(false /* release host-key? */);
596 break;
597 }
598 case KMachineState_Running:
599 {
600 /* Capture the keyboard by the first focused view: */
601 QList<ulong> theListOfViewIds = m_views.keys();
602 for (int i = 0; i < theListOfViewIds.size(); ++i)
603 {
604 if (m_views[theListOfViewIds[i]]->hasFocus())
605 {
606 /* Capture keyboard: */
607#ifdef Q_WS_WIN
608 if (!uisession()->isAutoCaptureDisabled() && m_globalSettings.autoCapture() &&
609 GetAncestor(m_views[theListOfViewIds[i]]->winId(), GA_ROOT) == GetForegroundWindow())
610#else /* Q_WS_WIN */
611 if (!uisession()->isAutoCaptureDisabled() && m_globalSettings.autoCapture())
612#endif /* !Q_WS_WIN */
613 captureKeyboard(theListOfViewIds[i]);
614 /* Reset the single-time disable capture flag: */
615 if (uisession()->isAutoCaptureDisabled())
616 uisession()->setAutoCaptureDisabled(false);
617 break;
618 }
619 }
620 break;
621 }
622 default:
623 break;
624 }
625}
626
627/* Keyboard-handler constructor: */
628UIKeyboardHandler::UIKeyboardHandler(UIMachineLogic *pMachineLogic)
629 : QObject(pMachineLogic)
630 , m_pMachineLogic(pMachineLogic)
631 , m_iKeyboardCaptureViewIndex(-1)
632 , m_globalSettings(vboxGlobal().settings())
633 , m_fIsKeyboardCaptured(false)
634 , m_bIsHostComboPressed(false)
635 , m_bIsHostComboAlone (false)
636 , m_fPassCAD(false)
637#if defined(Q_WS_WIN)
638 , m_bIsHostkeyInCapture(false)
639 , m_iKeyboardHookViewIndex(-1)
640#elif defined(Q_WS_MAC)
641 , m_darwinKeyModifiers(0)
642 , m_fKeyboardGrabbed(false)
643 , m_iKeyboardGrabViewIndex(-1)
644#endif
645{
646 /* Prepare: */
647 prepareCommon();
648
649 /* Load settings: */
650 loadSettings();
651
652 /* Initialize: */
653 sltMachineStateChanged();
654}
655
656/* Keyboard-handler destructor: */
657UIKeyboardHandler::~UIKeyboardHandler()
658{
659 /* Cleanup: */
660 cleanupCommon();
661}
662
663void UIKeyboardHandler::prepareCommon()
664{
665 /* Machine state-change updater: */
666 connect(uisession(), SIGNAL(sigMachineStateChange()), this, SLOT(sltMachineStateChanged()));
667
668 /* Pressed keys: */
669 ::memset(m_pressedKeys, 0, sizeof(m_pressedKeys));
670}
671
672void UIKeyboardHandler::loadSettings()
673{
674 /* Global settings: */
675#ifdef Q_WS_X11
676 /* Initialize the X keyboard subsystem: */
677 initMappedX11Keyboard(QX11Info::display(), vboxGlobal().settings().publicProperty("GUI/RemapScancodes"));
678#endif
679
680 /* Extra data settings: */
681 {
682 /* CAD settings: */
683 QString passCAD = session().GetConsole().GetMachine().GetExtraData(VBoxDefs::GUI_PassCAD);
684 if (!passCAD.isEmpty() && passCAD != "false" && passCAD != "no")
685 m_fPassCAD = true;
686 }
687}
688
689void UIKeyboardHandler::cleanupCommon()
690{
691#if defined(Q_WS_WIN)
692 /* Cleaning keyboard-hook: */
693 if (m_keyboardHook)
694 {
695 UnhookWindowsHookEx(m_keyboardHook);
696 m_keyboardHook = NULL;
697 }
698#elif defined(Q_WS_MAC)
699 /* We have to make sure the callback for the keyboard events
700 * is released when closing this view. */
701 if (m_fKeyboardGrabbed)
702 darwinGrabKeyboardEvents(false);
703#endif
704}
705
706/* Machine-logic getter: */
707UIMachineLogic* UIKeyboardHandler::machineLogic() const
708{
709 return m_pMachineLogic;
710}
711
712/* UI Session getter: */
713UISession* UIKeyboardHandler::uisession() const
714{
715 return machineLogic()->uisession();
716}
717
718/* Main Session getter: */
719CSession& UIKeyboardHandler::session() const
720{
721 return uisession()->session();
722}
723
724/* Event handler for prepared listener(s): */
725bool UIKeyboardHandler::eventFilter(QObject *pWatchedObject, QEvent *pEvent)
726{
727 /* Check if pWatchedObject object is window: */
728 if (UIMachineWindow *pWatchedWindow = isItListenedWindow(pWatchedObject))
729 {
730 /* Get corresponding screen index: */
731 ulong uScreenId = m_windows.key(pWatchedWindow);
732 NOREF(uScreenId);
733 /* Handle window events: */
734 switch (pEvent->type())
735 {
736#if defined(Q_WS_WIN)
737 /* Install/uninstall low-level keyboard-hook on every activation/deactivation to:
738 * a) avoid excess hook calls when we're not active and;
739 * b) be always in front of any other possible hooks. */
740 case QEvent::WindowActivate:
741 {
742 /* If keyboard hook is NOT currently created;
743 * Or created but NOT for that window: */
744 if (!m_keyboardHook || m_iKeyboardHookViewIndex != uScreenId)
745 {
746 /* If keyboard-hook present: */
747 if (m_keyboardHook)
748 {
749 /* We should remove existing keyboard-hook first: */
750 UnhookWindowsHookEx(m_keyboardHook);
751 m_keyboardHook = NULL;
752 }
753 /* Register new keyboard-hook: */
754 m_keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, lowLevelKeyboardProc, GetModuleHandle(NULL), 0);
755 AssertMsg(m_keyboardHook, ("SetWindowsHookEx(): err=%d", GetLastError()));
756 /* Remember which view had captured keyboard: */
757 m_iKeyboardHookViewIndex = uScreenId;
758 }
759 break;
760 }
761 case QEvent::WindowDeactivate:
762 {
763 /* If keyboard is currently captured: */
764 if (m_keyboardHook && m_iKeyboardHookViewIndex == uScreenId)
765 {
766 /* We should remove existing keyboard-hook: */
767 UnhookWindowsHookEx(m_keyboardHook);
768 m_keyboardHook = NULL;
769 /* Remember what there is no window captured keyboard: */
770 m_iKeyboardHookViewIndex = -1;
771 }
772 break;
773 }
774#elif defined(Q_WS_MAC)
775 case QEvent::WindowActivate:
776 {
777 /* If keyboard event handler is NOT currently installed;
778 * Or installed but NOT for that window: */
779 if (m_iKeyboardGrabViewIndex != (int)uScreenId)
780 {
781 /* If keyboard event handler is NOT currently installed: */
782 if (m_iKeyboardGrabViewIndex == -1)
783 {
784 /* Install the keyboard event handler: */
785 darwinGrabKeyboardEvents(true);
786 }
787 /* Update the id: */
788 m_iKeyboardGrabViewIndex = uScreenId;
789 }
790 break;
791 }
792 case QEvent::WindowDeactivate:
793 {
794 /* If keyboard event handler is installed exactly for that window: */
795 if (m_iKeyboardGrabViewIndex == (int)uScreenId)
796 {
797 /* Remove the keyboard event handler: */
798 darwinGrabKeyboardEvents(false);
799 /* Update the id: */
800 m_iKeyboardGrabViewIndex = -1;
801 }
802 break;
803 }
804#endif
805 default:
806 break;
807 }
808 }
809
810 else
811
812 /* Check if pWatchedObject object is view: */
813 if (UIMachineView *pWatchedView = isItListenedView(pWatchedObject))
814 {
815 /* Get corresponding screen index: */
816 ulong uScreenId = m_views.key(pWatchedView);
817 NOREF(uScreenId);
818 /* Handle view events: */
819 switch (pEvent->type())
820 {
821 case QEvent::FocusIn:
822 if (uisession()->isRunning())
823 {
824 /* Capture keyboard: */
825#ifdef Q_WS_WIN
826 if (!uisession()->isAutoCaptureDisabled() && m_globalSettings.autoCapture() &&
827 GetAncestor(pWatchedView->winId(), GA_ROOT) == GetForegroundWindow())
828#else /* Q_WS_WIN */
829 if (!uisession()->isAutoCaptureDisabled() && m_globalSettings.autoCapture())
830#endif /* !Q_WS_WIN */
831 captureKeyboard(uScreenId);
832 /* Reset the single-time disable capture flag: */
833 if (uisession()->isAutoCaptureDisabled())
834 uisession()->setAutoCaptureDisabled(false);
835 }
836 break;
837 case QEvent::FocusOut:
838 /* Release keyboard: */
839 if (uisession()->isRunning())
840 releaseKeyboard();
841 /* And all pressed keys: */
842 releaseAllPressedKeys(true);
843 break;
844 case QEvent::KeyPress:
845 case QEvent::KeyRelease:
846 {
847 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);
848
849 if (m_bIsHostComboPressed && pEvent->type() == QEvent::KeyPress)
850 {
851 /* Passing F1-F12 keys to the guest: */
852 if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F12)
853 {
854 QVector <LONG> combo(6);
855 combo[0] = 0x1d; /* Ctrl down */
856 combo[1] = 0x38; /* Alt down */
857 combo[4] = 0xb8; /* Alt up */
858 combo[5] = 0x9d; /* Ctrl up */
859 if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F10)
860 {
861 combo[2] = 0x3b + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 down */
862 combo[3] = 0xbb + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 up */
863 }
864 /* There is some scan slice between F10 and F11 keys, so its separated: */
865 else if (pKeyEvent->key() >= Qt::Key_F11 && pKeyEvent->key() <= Qt::Key_F12)
866 {
867 combo[2] = 0x57 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 down */
868 combo[3] = 0xd7 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 up */
869 }
870 CKeyboard keyboard = session().GetConsole().GetKeyboard();
871 keyboard.PutScancodes(combo);
872 }
873 /* Process hot keys not processed in keyEvent() (as in case of non-alphanumeric keys): */
874 machineLogic()->actionsPool()->processHotKey(QKeySequence(pKeyEvent->key()));
875 }
876 else if (!m_bIsHostComboPressed && pEvent->type() == QEvent::KeyRelease)
877 {
878 /* Show a possible warning on key release which seems to be more expected by the end user: */
879 if (uisession()->isPaused())
880 {
881 /* If the reminder is disabled we pass the event to Qt to enable normal
882 * keyboard functionality (for example, menu access with Alt+Letter): */
883 if (!vboxProblem().remindAboutPausedVMInput())
884 break;
885 }
886 }
887 break;
888 }
889 default:
890 break;
891 }
892 }
893
894 /* Else just propagate to base-class: */
895 return QObject::eventFilter(pWatchedObject, pEvent);
896}
897
898#if defined(Q_WS_WIN)
899
900LRESULT CALLBACK UIKeyboardHandler::lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
901{
902 if (nCode == HC_ACTION && m_spKeyboardHandler && m_spKeyboardHandler->winLowKeyboardEvent(wParam, *(KBDLLHOOKSTRUCT*)lParam))
903 return 1;
904
905 return CallNextHookEx(NULL, nCode, wParam, lParam);
906}
907
908bool UIKeyboardHandler::winLowKeyboardEvent(UINT msg, const KBDLLHOOKSTRUCT &event)
909{
910 /* Check what related machine-view was NOT unregistered yet: */
911 if (!m_views.contains(m_iKeyboardHookViewIndex))
912 return false;
913
914 if (!m_fIsKeyboardCaptured)
915 return false;
916
917 /* Sometimes it happens that Win inserts additional events on some key
918 * press/release. For example, it prepends ALT_GR in German layout with
919 * the VK_LCONTROL vkey with curious 0x21D scan code (seems to be necessary
920 * to specially treat ALT_GR to enter additional chars to regular apps).
921 * These events are definitely unwanted in VM, so filter them out. */
922 /* Note (michael): it also sometimes sends the VK_CAPITAL vkey with scan
923 * code 0x23a. If this is not passed through then it is impossible to
924 * cancel CapsLock on a French keyboard. I didn't find any other examples
925 * of these strange events. Let's hope we are not missing anything else
926 * of importance! */
927 if (m_views[m_iKeyboardHookViewIndex]->hasFocus() && (event.scanCode & ~0xFF))
928 {
929 if (event.vkCode == VK_CAPITAL)
930 return false;
931 else
932 return true;
933 }
934
935 /* It's possible that a key has been pressed while the keyboard was not
936 * captured, but is being released under the capture. Detect this situation
937 * and return false to let Windows process the message normally and update
938 * its key state table (to avoid the stuck key effect). */
939 uint8_t what_pressed = (event.flags & 0x01) && (event.vkCode != VK_RSHIFT) ? IsExtKeyPressed : IsKeyPressed;
940 if ((event.flags & 0x80) /* released */ &&
941 ((UIHotKeyCombination::toKeyCodeList(m_globalSettings.hostCombo()).contains(event.vkCode) && !m_bIsHostkeyInCapture) ||
942 (m_pressedKeys[event.scanCode] & (IsKbdCaptured | what_pressed)) == what_pressed))
943 return false;
944
945 MSG message;
946 message.hwnd = m_views[m_iKeyboardHookViewIndex]->winId();
947 message.message = msg;
948 message.wParam = event.vkCode;
949 message.lParam = 1 | (event.scanCode & 0xFF) << 16 | (event.flags & 0xFF) << 24;
950
951 /* Windows sets here the extended bit when the Right Shift key is pressed,
952 * which is totally wrong. Undo it. */
953 if (event.vkCode == VK_RSHIFT)
954 message.lParam &= ~0x1000000;
955
956 /* We suppose here that this hook is always called on the main GUI thread */
957 long dummyResult;
958 return m_views[m_iKeyboardHookViewIndex]->winEvent(&message, &dummyResult);
959}
960
961#elif defined(Q_WS_MAC)
962
963void UIKeyboardHandler::darwinGrabKeyboardEvents(bool fGrab)
964{
965 m_fKeyboardGrabbed = fGrab;
966 if (fGrab)
967 {
968 /* Disable mouse and keyboard event compression/delaying to make sure we *really* get all of the events. */
969 ::CGSetLocalEventsSuppressionInterval(0.0);
970 ::darwinSetMouseCoalescingEnabled(false);
971
972 /* Register the event callback/hook and grab the keyboard. */
973 UICocoaApplication::instance()->registerForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */,
974 UIKeyboardHandler::darwinEventHandlerProc, this);
975
976 ::DarwinGrabKeyboard (false);
977 }
978 else
979 {
980 ::DarwinReleaseKeyboard();
981 UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */,
982 UIKeyboardHandler::darwinEventHandlerProc, this);
983 }
984}
985
986bool UIKeyboardHandler::darwinEventHandlerProc(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser)
987{
988 UIKeyboardHandler *pKeyboardHandler = (UIKeyboardHandler*)pvUser;
989 EventRef inEvent = (EventRef)pvCarbonEvent;
990 UInt32 eventClass = ::GetEventClass(inEvent);
991
992 /* Check if this is an application key combo. In that case we will not pass
993 * the event to the guest, but let the host process it. */
994 if (::darwinIsApplicationCommand(pvCocoaEvent))
995 return false;
996
997 /* All keyboard class events needs to be handled. */
998 if (eventClass == kEventClassKeyboard)
999 {
1000 if (pKeyboardHandler->darwinKeyboardEvent (pvCocoaEvent, inEvent))
1001 return true;
1002 }
1003 /* Pass the event along. */
1004 return false;
1005}
1006
1007bool UIKeyboardHandler::darwinKeyboardEvent(const void *pvCocoaEvent, EventRef inEvent)
1008{
1009 bool ret = false;
1010 UInt32 EventKind = ::GetEventKind(inEvent);
1011 if (EventKind != kEventRawKeyModifiersChanged)
1012 {
1013 /* Convert keycode to set 1 scan code. */
1014 UInt32 keyCode = ~0U;
1015 ::GetEventParameter(inEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof (keyCode), NULL, &keyCode);
1016 /* The usb keyboard driver translates these codes to different virtual
1017 * key codes depending of the keyboard type. There are ANSI, ISO, JIS
1018 * and unknown. For European keyboards (ISO) the key 0xa and 0x32 have
1019 * to be switched. Here we are doing this at runtime, cause the user
1020 * can have more than one keyboard (of different type), where he may
1021 * switch at will all the time. Default is the ANSI standard as defined
1022 * in g_aDarwinToSet1. Please note that the "~" on some English ISO
1023 * keyboards will be wrongly swapped. This can maybe fixed by
1024 * using a Apple keyboard layout in the guest. */
1025 if ( (keyCode == 0xa || keyCode == 0x32)
1026 && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO)
1027 keyCode = 0x3c - keyCode;
1028 unsigned scanCode = ::DarwinKeycodeToSet1Scancode(keyCode);
1029 if (scanCode)
1030 {
1031 /* Calc flags. */
1032 int flags = 0;
1033 if (EventKind != kEventRawKeyUp)
1034 flags |= KeyPressed;
1035 if (scanCode & VBOXKEY_EXTENDED)
1036 flags |= KeyExtended;
1037 /** @todo KeyPause, KeyPrint. */
1038 scanCode &= VBOXKEY_SCANCODE_MASK;
1039
1040 /* Get the unicode string (if present). */
1041 AssertCompileSize(wchar_t, 2);
1042 AssertCompileSize(UniChar, 2);
1043 ByteCount cbWritten = 0;
1044 wchar_t ucs[8];
1045 if (::GetEventParameter(inEvent, kEventParamKeyUnicodes, typeUnicodeText, NULL,
1046 sizeof(ucs), &cbWritten, &ucs[0]) != 0)
1047 cbWritten = 0;
1048 ucs[cbWritten / sizeof(wchar_t)] = 0; /* The api doesn't terminate it. */
1049
1050 ret = keyEvent(keyCode, scanCode, flags, m_iKeyboardGrabViewIndex, ucs[0] ? ucs : NULL);
1051 }
1052 }
1053 else
1054 {
1055 /* May contain multiple modifier changes, kind of annoying. */
1056 UInt32 newMask = 0;
1057 ::GetEventParameter(inEvent, kEventParamKeyModifiers, typeUInt32, NULL,
1058 sizeof(newMask), NULL, &newMask);
1059 newMask = ::DarwinAdjustModifierMask(newMask, pvCocoaEvent);
1060 UInt32 changed = newMask ^ m_darwinKeyModifiers;
1061 if (changed)
1062 {
1063 for (UInt32 bit = 0; bit < 32; bit++)
1064 {
1065 if (!(changed & (1 << bit)))
1066 continue;
1067 unsigned scanCode = ::DarwinModifierMaskToSet1Scancode(1 << bit);
1068 if (!scanCode)
1069 continue;
1070 unsigned keyCode = ::DarwinModifierMaskToDarwinKeycode(1 << bit);
1071 Assert(keyCode);
1072
1073 if (!(scanCode & VBOXKEY_LOCK))
1074 {
1075 unsigned flags = (newMask & (1 << bit)) ? KeyPressed : 0;
1076 if (scanCode & VBOXKEY_EXTENDED)
1077 flags |= KeyExtended;
1078 scanCode &= VBOXKEY_SCANCODE_MASK;
1079 ret |= keyEvent(keyCode, scanCode & 0xff, flags, m_iKeyboardGrabViewIndex);
1080 }
1081 else
1082 {
1083 unsigned flags = 0;
1084 if (scanCode & VBOXKEY_EXTENDED)
1085 flags |= KeyExtended;
1086 scanCode &= VBOXKEY_SCANCODE_MASK;
1087 keyEvent(keyCode, scanCode, flags | KeyPressed, m_iKeyboardGrabViewIndex);
1088 keyEvent(keyCode, scanCode, flags, m_iKeyboardGrabViewIndex);
1089 }
1090 }
1091 }
1092
1093 m_darwinKeyModifiers = newMask;
1094
1095 /* Always return true here because we'll otherwise getting a Qt event
1096 we don't want and that will only cause the Pause warning to pop up. */
1097 ret = true;
1098 }
1099
1100 return ret;
1101}
1102
1103#endif
1104
1105bool UIKeyboardHandler::keyEvent(int iKey, uint8_t uScan, int fFlags, ulong uScreenId, wchar_t *pUniKey /* = 0 */)
1106{
1107 /* Get host-combo key list: */
1108 QSet<int> allHostComboKeys = UIHotKeyCombination::toKeyCodeList(m_globalSettings.hostCombo()).toSet();
1109
1110 /* Update the map of pressed host-combo keys: */
1111 if (fFlags & KeyPressed)
1112 {
1113 if (allHostComboKeys.contains(iKey))
1114 {
1115 if (!m_pressedHostComboKeys.contains(iKey))
1116 m_pressedHostComboKeys.insert(iKey, uScan);
1117 else if (m_bIsHostComboPressed)
1118 return true;
1119 }
1120 }
1121 else
1122 {
1123 if (allHostComboKeys.contains(iKey) && m_pressedHostComboKeys.contains(iKey))
1124 m_pressedHostComboKeys.remove(iKey);
1125 }
1126 /* Check if we are currently holding FULL host-combo: */
1127 bool fIsFullHostComboPresent = allHostComboKeys == m_pressedHostComboKeys.keys().toSet();
1128 /* Check if currently pressed/released key had changed host-combo state: */
1129 const bool isHostComboStateChanged = (!m_bIsHostComboPressed && fIsFullHostComboPresent) ||
1130 (m_bIsHostComboPressed && !fIsFullHostComboPresent);
1131
1132#ifdef Q_WS_WIN
1133 if (m_bIsHostComboPressed || isHostComboStateChanged)
1134 {
1135 /* Currently this is used in winLowKeyboardEvent() only: */
1136 m_bIsHostkeyInCapture = m_fIsKeyboardCaptured;
1137 }
1138#endif /* Q_WS_WIN */
1139
1140 /* Check if it's C-A-D and GUI/PassCAD is not true: */
1141 if (!m_fPassCAD &&
1142 uScan == 0x53 /* Del */ &&
1143 ((m_pressedKeys[0x38] & IsKeyPressed) /* Alt */ ||
1144 (m_pressedKeys[0x38] & IsExtKeyPressed)) &&
1145 ((m_pressedKeys[0x1d] & IsKeyPressed) /* Ctrl */ ||
1146 (m_pressedKeys[0x1d] & IsExtKeyPressed)))
1147 {
1148 /* Use the C-A-D combination as a last resort to get the keyboard and mouse back
1149 * to the host when the user forgets the Host Key. Note that it's always possible
1150 * to send C-A-D to the guest using the Host+Del combination: */
1151 if (uisession()->isRunning() && m_fIsKeyboardCaptured)
1152 {
1153 releaseKeyboard();
1154 if (!uisession()->isMouseSupportsAbsolute() || !uisession()->isMouseIntegrated())
1155 machineLogic()->mouseHandler()->releaseMouse();
1156 }
1157 return true;
1158 }
1159
1160 /* Prepare empty code-buffer: */
1161 LONG aCodesBuffer[16];
1162 LONG *pCodes = aCodesBuffer;
1163 uint uCodesCount = 0;
1164 /* Processing usual key-presses/releases without host-key being held: */
1165 if (!m_bIsHostComboPressed || isHostComboStateChanged)
1166 {
1167 /* Special flags handling (KeyPrint): */
1168 if (fFlags & KeyPrint)
1169 {
1170 if (fFlags & KeyPressed)
1171 {
1172 static LONG PrintMake[] = { 0xE0, 0x2A, 0xE0, 0x37 };
1173 pCodes = PrintMake;
1174 uCodesCount = SIZEOF_ARRAY(PrintMake);
1175 }
1176 else
1177 {
1178 static LONG PrintBreak[] = { 0xE0, 0xB7, 0xE0, 0xAA };
1179 pCodes = PrintBreak;
1180 uCodesCount = SIZEOF_ARRAY(PrintBreak);
1181 }
1182 }
1183 /* Special flags handling (KeyPause): */
1184 else if (fFlags & KeyPause)
1185 {
1186 if (fFlags & KeyPressed)
1187 {
1188 static LONG Pause[] = { 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 };
1189 pCodes = Pause;
1190 uCodesCount = SIZEOF_ARRAY(Pause);
1191 }
1192 else
1193 {
1194 /* Pause shall not produce a break code: */
1195 return true;
1196 }
1197 }
1198 /* Common flags handling: */
1199 else
1200 {
1201 /* Get the type of key - simple or extended: */
1202 uint8_t uWhatPressed = fFlags & KeyExtended ? IsExtKeyPressed : IsKeyPressed;
1203
1204 /* If some key was pressed or some previously pressed key was released =>
1205 * we are updating the list of pressed keys and preparing scancodes: */
1206 if ((fFlags & KeyPressed) || (m_pressedKeys[uScan] & uWhatPressed))
1207 {
1208 /* Check if the guest has the same view on the modifier keys
1209 * (NumLock, CapsLock, ScrollLock) as the X server.
1210 * If not, send KeyPress events to synchronize the state: */
1211 if (fFlags & KeyPressed)
1212 fixModifierState(pCodes, &uCodesCount);
1213
1214 /* Prepend 'extended' scancode if needed: */
1215 if (fFlags & KeyExtended)
1216 pCodes[uCodesCount++] = 0xE0;
1217
1218 /* Process key-press: */
1219 if (fFlags & KeyPressed)
1220 {
1221 /* Append scancode: */
1222 pCodes[uCodesCount++] = uScan;
1223 m_pressedKeys[uScan] |= uWhatPressed;
1224 }
1225 /* Process key-release if that key was pressed before: */
1226 else if (m_pressedKeys[uScan] & uWhatPressed)
1227 {
1228 /* Append scancode: */
1229 pCodes[uCodesCount++] = uScan | 0x80;
1230 m_pressedKeys[uScan] &= ~uWhatPressed;
1231 }
1232
1233 /* Update keyboard-captured flag: */
1234 if (m_fIsKeyboardCaptured)
1235 m_pressedKeys[uScan] |= IsKbdCaptured;
1236 else
1237 m_pressedKeys[uScan] &= ~IsKbdCaptured;
1238 }
1239 /* Ignore key-release if that key was NOT pressed before,
1240 * but only if thats not one of the host-combination keys: */
1241 else if (!allHostComboKeys.contains(iKey))
1242 return true;
1243 }
1244 }
1245
1246 /* Process the host-combo funtionality: */
1247 if (fFlags & KeyPressed)
1248 {
1249 if (isHostComboStateChanged)
1250 {
1251 if (!m_bIsHostComboPressed)
1252 {
1253 m_bIsHostComboPressed = true;
1254 m_bIsHostComboAlone = true;
1255 if (uisession()->isRunning())
1256 saveKeyStates();
1257 }
1258 }
1259 else
1260 {
1261 if (m_bIsHostComboPressed)
1262 {
1263 if (m_bIsHostComboAlone)
1264 {
1265 m_bIsHostComboAlone = false;
1266 m_pressedHostComboKeys.clear();
1267 /* Process Host+<key> shortcuts.
1268 * Currently, <key> is limited to alphanumeric chars.
1269 * Other Host+<key> combinations are handled in Qt event(): */
1270 return processHotKey(iKey, pUniKey);
1271 }
1272 }
1273 }
1274 }
1275 else
1276 {
1277 if (isHostComboStateChanged)
1278 {
1279 if (m_bIsHostComboPressed)
1280 {
1281 m_bIsHostComboPressed = false;
1282 if (m_bIsHostComboAlone)
1283 {
1284 /* Capturing/releasing keyboard/mouse: */
1285 if (uisession()->isRunning())
1286 {
1287 bool ok = true;
1288 if (!m_fIsKeyboardCaptured)
1289 {
1290 /* Temporarily disable auto-capture that will take place after
1291 * this dialog is dismissed because the capture state is to be
1292 * defined by the dialog result itself: */
1293 uisession()->setAutoCaptureDisabled(true);
1294 bool fIsAutoConfirmed = false;
1295 ok = vboxProblem().confirmInputCapture(&fIsAutoConfirmed);
1296 if (fIsAutoConfirmed)
1297 uisession()->setAutoCaptureDisabled(false);
1298 else
1299 m_pressedHostComboKeys.clear();
1300 /* Otherwise, the disable flag will be reset in the next
1301 * machine-view's focus-in event (since may happen asynchronously
1302 * on some platforms, after we return from this code): */
1303 }
1304 if (ok)
1305 {
1306 if (m_fIsKeyboardCaptured)
1307 releaseKeyboard();
1308 else
1309 captureKeyboard(uScreenId);
1310 if (!uisession()->isMouseSupportsAbsolute() || !uisession()->isMouseIntegrated())
1311 {
1312#ifdef Q_WS_X11
1313 /* Make sure that pending FocusOut events from the
1314 * previous message box are handled, otherwise the
1315 * mouse is immediately ungrabbed: */
1316 qApp->processEvents();
1317#endif /* Q_WS_X11 */
1318 if (m_fIsKeyboardCaptured)
1319 machineLogic()->mouseHandler()->captureMouse(uScreenId);
1320 else
1321 machineLogic()->mouseHandler()->releaseMouse();
1322 }
1323 }
1324 }
1325 }
1326 if (uisession()->isRunning())
1327 sendChangedKeyStates();
1328 }
1329 }
1330 else
1331 {
1332 if (m_bIsHostComboPressed)
1333 m_bIsHostComboAlone = false;
1334 }
1335 }
1336
1337 /* Notify all listeners: */
1338 emit keyboardStateChanged(keyboardState());
1339
1340 /* If the VM is NOT paused and there are scancodes to send: */
1341 if (!uisession()->isPaused() && uCodesCount)
1342 {
1343 /* Get the VM keyboard to pass key in there: */
1344 CKeyboard keyboard = session().GetConsole().GetKeyboard();
1345 Assert(!keyboard.isNull());
1346
1347 /* Pass this key to the guest: */
1348 std::vector<LONG> scancodes(pCodes, &pCodes[uCodesCount]);
1349 keyboard.PutScancodes(QVector<LONG>::fromStdVector(scancodes));
1350
1351 /* If full host-key sequence was just finalized
1352 * and the last key of host-combination was just sent to the guest =>
1353 * we have to notify guest to make it release keys from the host-combination: */
1354 if (isHostComboStateChanged && m_bIsHostComboPressed)
1355 {
1356 QList<uint8_t> hostComboScans = m_pressedHostComboKeys.values();
1357 for (int i = 0 ; i < hostComboScans.size(); ++i)
1358 {
1359 uint8_t uScan = hostComboScans[i];
1360 if (m_pressedKeys[uScan] & IsKeyPressed)
1361 {
1362 keyboard.PutScancode(uScan | 0x80);
1363 }
1364 else if (m_pressedKeys[uScan] & IsExtKeyPressed)
1365 {
1366 QVector<LONG> scancodes(2);
1367 scancodes[0] = 0xE0;
1368 scancodes[1] = uScan | 0x80;
1369 keyboard.PutScancodes(scancodes);
1370 }
1371 m_pressedKeys[uScan] = 0;
1372 }
1373 }
1374 }
1375
1376 /* Prevent the key from going to Qt: */
1377 return true;
1378}
1379
1380bool UIKeyboardHandler::processHotKey(int iHotKey, wchar_t *pHotKey)
1381{
1382 /* Prepare processing result: */
1383 bool fWasProcessed = false;
1384
1385#ifdef Q_WS_WIN
1386 Q_UNUSED(pHotKey);
1387 int iKeyboardLayout = GetKeyboardLayoutList(0, NULL);
1388 Assert(iKeyboardLayout);
1389 HKL *pList = new HKL[iKeyboardLayout];
1390 GetKeyboardLayoutList(iKeyboardLayout, pList);
1391 for (int i = 0; i < iKeyboardLayout && !fWasProcessed; ++i)
1392 {
1393 wchar_t symbol;
1394 static BYTE keys[256] = {0};
1395 if (!ToUnicodeEx(iHotKey, 0, keys, &symbol, 1, 0, pList[i]) == 1)
1396 symbol = 0;
1397 if (symbol)
1398 fWasProcessed = machineLogic()->actionsPool()->processHotKey(QKeySequence((Qt::UNICODE_ACCEL + QChar(symbol).toUpper().unicode())));
1399 }
1400 delete[] pList;
1401#endif /* Q_WS_WIN */
1402
1403#ifdef Q_WS_X11
1404 Q_UNUSED(pHotKey);
1405 Display *pDisplay = QX11Info::display();
1406 int iKeysymsPerKeycode = getKeysymsPerKeycode();
1407 KeyCode keyCode = XKeysymToKeycode(pDisplay, iHotKey);
1408 for (int i = 0; i < iKeysymsPerKeycode && !fWasProcessed; i += 2)
1409 {
1410 KeySym ks = XKeycodeToKeysym(pDisplay, keyCode, i);
1411 char symbol = 0;
1412 if (!XkbTranslateKeySym(pDisplay, &ks, 0, &symbol, 1, NULL) == 1)
1413 symbol = 0;
1414 if (symbol)
1415 {
1416 QChar qtSymbol = QString::fromLocal8Bit(&symbol, 1)[0];
1417 fWasProcessed = machineLogic()->actionsPool()->processHotKey(QKeySequence((Qt::UNICODE_ACCEL + qtSymbol.toUpper().unicode())));
1418 }
1419 }
1420#endif /* Q_WS_X11 */
1421
1422#ifdef Q_WS_MAC
1423 Q_UNUSED(iHotKey);
1424 if (pHotKey && pHotKey[0] && !pHotKey[1])
1425 fWasProcessed = machineLogic()->actionsPool()->processHotKey(QKeySequence(Qt::UNICODE_ACCEL + QChar(pHotKey[0]).toUpper().unicode()));
1426#endif /* Q_WS_MAC */
1427
1428 /* Grab the key from the Qt if it was processed, or pass it to the Qt otherwise
1429 * in order to process non-alphanumeric keys in event(), after they are converted to Qt virtual keys: */
1430 return fWasProcessed;
1431}
1432
1433void UIKeyboardHandler::fixModifierState(LONG *piCodes, uint *puCount)
1434{
1435 /* Synchronize the views of the host and the guest to the modifier keys.
1436 * This function will add up to 6 additional keycodes to codes. */
1437#if defined(Q_WS_X11)
1438 Window wDummy1, wDummy2;
1439 int iDummy3, iDummy4, iDummy5, iDummy6;
1440 unsigned uMask;
1441 unsigned uKeyMaskNum = 0, uKeyMaskCaps = 0, uKeyMaskScroll = 0;
1442
1443 uKeyMaskCaps = LockMask;
1444 XModifierKeymap* map = XGetModifierMapping(QX11Info::display());
1445 KeyCode keyCodeNum = XKeysymToKeycode(QX11Info::display(), XK_Num_Lock);
1446 KeyCode keyCodeScroll = XKeysymToKeycode(QX11Info::display(), XK_Scroll_Lock);
1447
1448 for (int i = 0; i < 8; ++ i)
1449 {
1450 if (keyCodeNum != NoSymbol && map->modifiermap[map->max_keypermod * i] == keyCodeNum)
1451 uKeyMaskNum = 1 << i;
1452 else if (keyCodeScroll != NoSymbol && map->modifiermap[map->max_keypermod * i] == keyCodeScroll)
1453 uKeyMaskScroll = 1 << i;
1454 }
1455 XQueryPointer(QX11Info::display(), DefaultRootWindow(QX11Info::display()), &wDummy1, &wDummy2,
1456 &iDummy3, &iDummy4, &iDummy5, &iDummy6, &uMask);
1457 XFreeModifiermap(map);
1458
1459 if (uisession()->numLockAdaptionCnt() && (uisession()->isNumLock() ^ !!(uMask & uKeyMaskNum)))
1460 {
1461 uisession()->setNumLockAdaptionCnt(uisession()->numLockAdaptionCnt() - 1);
1462 piCodes[(*puCount)++] = 0x45;
1463 piCodes[(*puCount)++] = 0x45 | 0x80;
1464 }
1465 if (uisession()->capsLockAdaptionCnt() && (uisession()->isCapsLock() ^ !!(uMask & uKeyMaskCaps)))
1466 {
1467 uisession()->setCapsLockAdaptionCnt(uisession()->capsLockAdaptionCnt() - 1);
1468 piCodes[(*puCount)++] = 0x3a;
1469 piCodes[(*puCount)++] = 0x3a | 0x80;
1470 /* Some keyboard layouts require shift to be pressed to break
1471 * capslock. For simplicity, only do this if shift is not
1472 * already held down. */
1473 if (uisession()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))
1474 {
1475 piCodes[(*puCount)++] = 0x2a;
1476 piCodes[(*puCount)++] = 0x2a | 0x80;
1477 }
1478 }
1479#elif defined(Q_WS_WIN)
1480 if (uisession()->numLockAdaptionCnt() && (uisession()->isNumLock() ^ !!(GetKeyState(VK_NUMLOCK))))
1481 {
1482 uisession()->setNumLockAdaptionCnt(uisession()->numLockAdaptionCnt() - 1);
1483 piCodes[(*puCount)++] = 0x45;
1484 piCodes[(*puCount)++] = 0x45 | 0x80;
1485 }
1486 if (uisession()->capsLockAdaptionCnt() && (uisession()->isCapsLock() ^ !!(GetKeyState(VK_CAPITAL))))
1487 {
1488 uisession()->setCapsLockAdaptionCnt(uisession()->capsLockAdaptionCnt() - 1);
1489 piCodes[(*puCount)++] = 0x3a;
1490 piCodes[(*puCount)++] = 0x3a | 0x80;
1491 /* Some keyboard layouts require shift to be pressed to break
1492 * capslock. For simplicity, only do this if shift is not
1493 * already held down. */
1494 if (uisession()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))
1495 {
1496 piCodes[(*puCount)++] = 0x2a;
1497 piCodes[(*puCount)++] = 0x2a | 0x80;
1498 }
1499 }
1500#elif defined(Q_WS_MAC)
1501 /* if (uisession()->numLockAdaptionCnt()) ... - NumLock isn't implemented by Mac OS X so ignore it. */
1502 if (uisession()->capsLockAdaptionCnt() && (uisession()->isCapsLock() ^ !!(::GetCurrentEventKeyModifiers() & alphaLock)))
1503 {
1504 uisession()->setCapsLockAdaptionCnt(uisession()->capsLockAdaptionCnt() - 1);
1505 piCodes[(*puCount)++] = 0x3a;
1506 piCodes[(*puCount)++] = 0x3a | 0x80;
1507 /* Some keyboard layouts require shift to be pressed to break
1508 * capslock. For simplicity, only do this if shift is not
1509 * already held down. */
1510 if (uisession()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))
1511 {
1512 piCodes[(*puCount)++] = 0x2a;
1513 piCodes[(*puCount)++] = 0x2a | 0x80;
1514 }
1515 }
1516#else
1517//#warning Adapt UIKeyboardHandler::fixModifierState
1518#endif
1519}
1520
1521void UIKeyboardHandler::saveKeyStates()
1522{
1523 ::memcpy(m_pressedKeysCopy, m_pressedKeys, sizeof(m_pressedKeys));
1524}
1525
1526void UIKeyboardHandler::sendChangedKeyStates()
1527{
1528 QVector <LONG> codes(2);
1529 CKeyboard keyboard = session().GetConsole().GetKeyboard();
1530 for (uint i = 0; i < SIZEOF_ARRAY(m_pressedKeys); ++ i)
1531 {
1532 uint8_t os = m_pressedKeysCopy[i];
1533 uint8_t ns = m_pressedKeys[i];
1534 if ((os & IsKeyPressed) != (ns & IsKeyPressed))
1535 {
1536 codes[0] = i;
1537 if (!(ns & IsKeyPressed))
1538 codes[0] |= 0x80;
1539 keyboard.PutScancode(codes[0]);
1540 }
1541 else if ((os & IsExtKeyPressed) != (ns & IsExtKeyPressed))
1542 {
1543 codes[0] = 0xE0;
1544 codes[1] = i;
1545 if (!(ns & IsExtKeyPressed))
1546 codes[1] |= 0x80;
1547 keyboard.PutScancodes(codes);
1548 }
1549 }
1550}
1551
1552UIMachineWindow* UIKeyboardHandler::isItListenedWindow(QObject *pWatchedObject) const
1553{
1554 UIMachineWindow *pResultWindow = 0;
1555 QMap<ulong, UIMachineWindow*>::const_iterator i = m_windows.constBegin();
1556 while (!pResultWindow && i != m_windows.constEnd())
1557 {
1558 UIMachineWindow *pIteratedWindow = i.value();
1559 if (pIteratedWindow->machineWindow() == pWatchedObject)
1560 {
1561 pResultWindow = pIteratedWindow;
1562 continue;
1563 }
1564 ++i;
1565 }
1566 return pResultWindow;
1567}
1568
1569UIMachineView* UIKeyboardHandler::isItListenedView(QObject *pWatchedObject) const
1570{
1571 UIMachineView *pResultView = 0;
1572 QMap<ulong, UIMachineView*>::const_iterator i = m_views.constBegin();
1573 while (!pResultView && i != m_views.constEnd())
1574 {
1575 UIMachineView *pIteratedView = i.value();
1576 if (pIteratedView == pWatchedObject)
1577 {
1578 pResultView = pIteratedView;
1579 continue;
1580 }
1581 ++i;
1582 }
1583 return pResultView;
1584}
1585
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use