VirtualBox

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

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

gcc warning

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

© 2023 Oracle
ContactPrivacy policyTerms of Use