VirtualBox

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

Last change on this file was 102518, checked in by vboxsync, 5 months ago

FE/Qt: Added a --no-keyboard-grabbing option to help with debugging VirtualBoxVM on windows. Ignored on other hosts atm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.6 KB
Line 
1/* $Id: UIKeyboardHandler.cpp 102518 2023-12-07 11:42:19Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIKeyboardHandler class implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/*
29 * Things worth testing when changing this code:
30 * - That automatic keyboard capture works.
31 * - That the keyboard is captured when the mouse is.
32 * - That the host key releases the keyboard when
33 * the keyboard is captured but the mouse not, and both when both are.
34 * - That the host key captures both keyboard and mouse.
35 * - That the keyboard is captured when the mouse capture notification is
36 * displayed.
37 * - That keyboard capture works on X11 hosts when windows are dragged with
38 * various window managers.
39 * - That multiple machine windows do not fight for the focus on X11 hosts
40 * (noticeable through strange modifier key and capitals behaviour).
41 */
42
43/* Qt includes: */
44#include <QKeyEvent>
45#include <QTimer>
46
47/* GUI includes: */
48#include "UIActionPool.h"
49#include "UICommon.h"
50#include "UIExtraDataManager.h"
51#include "UIHostComboEditor.h"
52#include "UIKeyboardHandlerFullscreen.h"
53#include "UIKeyboardHandlerNormal.h"
54#include "UIKeyboardHandlerScale.h"
55#include "UIKeyboardHandlerSeamless.h"
56#include "UIMachine.h"
57#include "UIMachineLogic.h"
58#include "UIMachineView.h"
59#include "UIMachineWindow.h"
60#include "UIMessageCenter.h"
61#include "UIMouseHandler.h"
62#include "UINotificationCenter.h"
63#ifdef VBOX_WS_MAC
64# include "UICocoaApplication.h"
65# include "VBoxUtils-darwin.h"
66# include "DarwinKeyboard.h"
67#endif
68#ifdef VBOX_WS_WIN
69# include "WinKeyboard.h"
70#endif
71#ifdef VBOX_WS_NIX
72# include "XKeyboard.h"
73# include "VBoxUtils-nix.h"
74#endif
75
76/* Other VBox includes: */
77#ifdef VBOX_WS_MAC
78# include "iprt/cpp/utils.h"
79#endif
80
81/* External includes: */
82#ifdef VBOX_WS_MAC
83# include <Carbon/Carbon.h>
84#endif
85#ifdef VBOX_WS_NIX
86# include <X11/XKBlib.h>
87# include <X11/keysym.h>
88# ifdef KeyPress
89const int XFocusIn = FocusIn;
90const int XFocusOut = FocusOut;
91const int XKeyPress = KeyPress;
92const int XKeyRelease = KeyRelease;
93# undef KeyRelease
94# undef KeyPress
95# undef FocusOut
96# undef FocusIn
97# endif /* KeyPress */
98# include <xcb/xcb.h>
99#endif /* VBOX_WS_NIX */
100
101/* Enums representing different keyboard-states: */
102enum { KeyExtended = 0x01, KeyPressed = 0x02, KeyPause = 0x04, KeyPrint = 0x08 };
103enum { IsKeyPressed = 0x01, IsExtKeyPressed = 0x02, IsKbdCaptured = 0x80 };
104
105
106#ifdef VBOX_WS_WIN
107UIKeyboardHandler *UIKeyboardHandler::m_spKeyboardHandler = 0;
108#endif /* VBOX_WS_WIN */
109
110/* Factory function to create keyboard-handler: */
111UIKeyboardHandler* UIKeyboardHandler::create(UIMachineLogic *pMachineLogic,
112 UIVisualStateType visualStateType)
113{
114 /* Prepare keyboard-handler: */
115 UIKeyboardHandler *pKeyboardHandler = 0;
116
117 /* Depending on visual-state type: */
118 switch (visualStateType)
119 {
120 case UIVisualStateType_Normal:
121 pKeyboardHandler = new UIKeyboardHandlerNormal(pMachineLogic);
122 break;
123 case UIVisualStateType_Fullscreen:
124 pKeyboardHandler = new UIKeyboardHandlerFullscreen(pMachineLogic);
125 break;
126 case UIVisualStateType_Seamless:
127 pKeyboardHandler = new UIKeyboardHandlerSeamless(pMachineLogic);
128 break;
129 case UIVisualStateType_Scale:
130 pKeyboardHandler = new UIKeyboardHandlerScale(pMachineLogic);
131 break;
132 default:
133 break;
134 }
135
136#ifdef VBOX_WS_WIN
137 /* It is necessary to have static pointer to created handler
138 * because windows keyboard-hook works only with static members: */
139 m_spKeyboardHandler = pKeyboardHandler;
140#endif /* VBOX_WS_WIN */
141
142 /* Return prepared keyboard-handler: */
143 return pKeyboardHandler;
144}
145
146/* Factory function to destroy keyboard-handler: */
147void UIKeyboardHandler::destroy(UIKeyboardHandler *pKeyboardHandler)
148{
149#ifdef VBOX_WS_WIN
150 /* It was necessary to have static pointer to created handler
151 * because windows keyboard-hook works only with static members: */
152 m_spKeyboardHandler = 0;
153#endif /* VBOX_WS_WIN */
154
155 /* Delete keyboard-handler: */
156 delete pKeyboardHandler;
157}
158
159/* Prepare listened objects: */
160void UIKeyboardHandler::prepareListener(ulong uScreenId, UIMachineWindow *pMachineWindow)
161{
162 /* If that window is NOT registered yet: */
163 if (!m_windows.contains(uScreenId))
164 {
165 /* Add window: */
166 m_windows.insert(uScreenId, pMachineWindow);
167 /* Install event-filter for window: */
168 m_windows[uScreenId]->installEventFilter(this);
169 }
170
171 /* If that view is NOT registered yet: */
172 if (!m_views.contains(uScreenId))
173 {
174 /* Add view: */
175 m_views.insert(uScreenId, pMachineWindow->machineView());
176 /* Install event-filter for view: */
177 m_views[uScreenId]->installEventFilter(this);
178 }
179}
180
181/* Cleanup listened objects: */
182void UIKeyboardHandler::cleanupListener(ulong uScreenId)
183{
184 /* Check if we should release keyboard first: */
185 if ((int)uScreenId == m_iKeyboardCaptureViewIndex)
186 releaseKeyboard();
187
188 /* If window still registered: */
189 if (m_windows.contains(uScreenId))
190 {
191 /* Remove window: */
192 m_windows.remove(uScreenId);
193 }
194
195 /* If view still registered: */
196 if (m_views.contains(uScreenId))
197 {
198 /* Remove view: */
199 m_views.remove(uScreenId);
200 }
201}
202
203void UIKeyboardHandler::captureKeyboard(ulong uScreenId)
204{
205 /* Do NOT capture the keyboard if it is already captured: */
206 if (m_fKeyboardCaptured)
207 {
208 /* Make sure the right screen had captured the keyboard: */
209 Assert((int)uScreenId == m_iKeyboardCaptureViewIndex);
210 return;
211 }
212
213 /* If the view exists: */
214 if (m_views.contains(uScreenId))
215 {
216 /* Remember which screen wishes to capture the keyboard: */
217 m_iKeyboardCaptureViewIndex = uScreenId;
218
219 /* On X11, we do not grab the keyboard as soon as it is captured, but delay it
220 * for 300 milliseconds after the formal capture. We do this for several reasons:
221 * - First, when several windows are created they all try to capture the keyboard when
222 * they get the focus. Due to the asynchronous nature of X11 the first window may only
223 * gets notified after the last is created, and there is a dance if they respond to
224 * the notifications by grabbing the keyboard and trigger new focus changes in the process.
225 * - Second, grabbing the keyboard immediately on focus change upsets some window managers,
226 * they give us the focus then try to grab the keyboard themselves, and sulk if they fail
227 * by refusing to e.g. drag a window using its title bar.
228 *
229 * IMPORTANT! We do the same under all other hosts as well mainly to have the
230 * common behavior everywhere while X11 is forced to behave that way. */
231 QTimer::singleShot(300, this, SLOT(sltFinaliseCaptureKeyboard()));
232 }
233}
234
235bool UIKeyboardHandler::finaliseCaptureKeyboard()
236{
237 /* Do NOT capture the keyboard if it is already captured: */
238 if (m_fKeyboardCaptured)
239 return true;
240
241 /* Make sure capture was really requested: */
242 if (m_iKeyboardCaptureViewIndex == -1)
243 return true;
244
245 /* If the view exists: */
246 if (m_views.contains(m_iKeyboardCaptureViewIndex))
247 {
248#if defined(VBOX_WS_MAC)
249
250 /* On Mac, keyboard grabbing is ineffective, a low-level keyboard-hook is used instead.
251 * It is being installed on focus-in event and uninstalled on focus-out.
252 * S.a. UIKeyboardHandler::eventFilter for more information. */
253
254 /* Besides that, we are not just using the Qt stuff to grab the keyboard,
255 * we also disable global hot keys and enable watching
256 * modifiers (for right/left separation). */
257 /// @todo Is that really needed?
258 ::DarwinDisableGlobalHotKeys(true);
259 m_views[m_iKeyboardCaptureViewIndex]->grabKeyboard();
260
261#elif defined(VBOX_WS_WIN)
262
263 /* On Win, keyboard grabbing is ineffective, a low-level keyboard-hook is used instead.
264 * It is being installed on focus-in event and uninstalled on focus-out.
265 * S.a. UIKeyboardHandler::eventFilter for more information. */
266
267#elif defined(VBOX_WS_NIX)
268 if (uiCommon().X11ServerAvailable())
269 {
270 /* On X11, we are using XCB stuff to grab the keyboard.
271 * This stuff is a part of the active keyboard grabbing functionality.
272 * Active keyboard grabbing causes a problems on many window managers - a window cannot
273 * be moved using the mouse. So we additionally grab the mouse buttons as well to detect
274 * that the user is trying to click outside of the internal window geometry and release
275 * the keyboard before the target window sees the click. (GNOME Shell's hot corner has
276 * the same problem. At present we just let that problem be.) */
277
278# if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
279 /* Make sure we really do still have the focus. Qt as of version 5.13 started
280 * reporting it with delay, so ask the X server directly. We could remove the
281 * version check some time in the future. If we do, remove the comment above
282 * about the focus notification dance, as it will no longer be relevant. */
283 xcb_get_input_focus_cookie_t xcbFocusCookie = xcb_get_input_focus(NativeWindowSubsystem::X11GetConnection());
284 xcb_get_input_focus_reply_t *pFocusReply = xcb_get_input_focus_reply(NativeWindowSubsystem::X11GetConnection(),
285 xcbFocusCookie, 0);
286 xcb_window_t xcbFocusWindow = pFocusReply->focus;
287 free(pFocusReply);
288 if (xcbFocusWindow != m_windows[m_iKeyboardCaptureViewIndex]->winId())
289 return true;
290# endif
291
292 /* Grab the mouse button.
293 * We do not check for failure as we do not currently implement a back-up plan. */
294 /* If any previous grab is still in process, release it. */
295 if (m_hButtonGrabWindow != 0)
296 xcb_ungrab_button_checked(NativeWindowSubsystem::X11GetConnection(), XCB_BUTTON_INDEX_ANY,
297 m_hButtonGrabWindow, XCB_MOD_MASK_ANY);
298 m_hButtonGrabWindow = NativeWindowSubsystem::X11GetAppRootWindow();
299 xcb_grab_button_checked(NativeWindowSubsystem::X11GetConnection(), 0, m_hButtonGrabWindow,
300 XCB_EVENT_MASK_BUTTON_PRESS, XCB_GRAB_MODE_SYNC, XCB_GRAB_MODE_ASYNC,
301 XCB_NONE, XCB_NONE, XCB_BUTTON_INDEX_ANY, XCB_MOD_MASK_ANY);
302 /* And grab the keyboard, using XCB directly, as Qt does not report failure. */
303 xcb_grab_keyboard_cookie_t xcbGrabCookie = xcb_grab_keyboard(NativeWindowSubsystem::X11GetConnection(), false,
304 m_views[m_iKeyboardCaptureViewIndex]->winId(),
305 XCB_TIME_CURRENT_TIME, XCB_GRAB_MODE_ASYNC, XCB_GRAB_MODE_ASYNC);
306 xcb_grab_keyboard_reply_t *pGrabReply = xcb_grab_keyboard_reply(NativeWindowSubsystem::X11GetConnection(),
307 xcbGrabCookie, NULL);
308 if (pGrabReply == NULL || pGrabReply->status != XCB_GRAB_STATUS_SUCCESS)
309 {
310 /* Release the mouse button grab.
311 * We do not check for failure as we do not currently implement a back-up plan. */
312 xcb_ungrab_button_checked(NativeWindowSubsystem::X11GetConnection(), XCB_BUTTON_INDEX_ANY,
313 m_hButtonGrabWindow, XCB_MOD_MASK_ANY);
314 m_hButtonGrabWindow = 0;
315 /* Try again later: */
316 free(pGrabReply);
317 return false;
318 }
319 free(pGrabReply);
320 }
321#else
322
323 /* On other platforms we are just praying Qt method to work: */
324 m_views[m_iKeyboardCaptureViewIndex]->grabKeyboard();
325
326#endif
327
328 /* Store new keyboard-captured state value: */
329 m_fKeyboardCaptured = true;
330
331 /* Notify all the listeners: */
332 emit sigStateChange(state());
333
334 return true;
335 }
336
337 return false;
338}
339
340void UIKeyboardHandler::releaseKeyboard()
341{
342 /* Do NOT release the keyboard if it is already released: */
343 if (!m_fKeyboardCaptured)
344 {
345 /* If a delayed capture is scheduled then cancel it: */
346 m_iKeyboardCaptureViewIndex = -1;
347 return;
348 }
349
350 /* If the view exists: */
351 if (m_views.contains(m_iKeyboardCaptureViewIndex))
352 {
353#if defined(VBOX_WS_MAC)
354
355 /* On Mac, keyboard grabbing is ineffective, a low-level keyboard-hook is used instead.
356 * It is being installed on focus-in event and uninstalled on focus-out.
357 * S.a. UIKeyboardHandler::eventFilter for more information. */
358
359 /* Besides that, we are not just using the Qt stuff to ungrab the keyboard,
360 * we also enable global hot keys and disable watching
361 * modifiers (for right/left separation). */
362 /// @todo Is that really needed?
363 ::DarwinDisableGlobalHotKeys(false);
364 m_views[m_iKeyboardCaptureViewIndex]->releaseKeyboard();
365
366#elif defined(VBOX_WS_WIN)
367
368 /* On Win, keyboard grabbing is ineffective, a low-level keyboard-hook is used instead.
369 * It is being installed on focus-in event and uninstalled on focus-out.
370 * S.a. UIKeyboardHandler::eventFilter for more information. */
371
372#elif defined(VBOX_WS_NIX)
373 if (uiCommon().X11ServerAvailable())
374 {
375 /* On X11, we are using XCB stuff to grab the keyboard.
376 * This stuff is a part of the active keyboard grabbing functionality.
377 * Active keyboard grabbing causes a problems on many window managers - a window cannot
378 * be moved using the mouse. So we finally releasing additionally grabbed mouse as well
379 * to allow further user interactions. */
380
381 /* Ungrab using XCB: */
382 xcb_ungrab_keyboard(NativeWindowSubsystem::X11GetConnection(), XCB_TIME_CURRENT_TIME);
383 /* Release the mouse button grab.
384 * We do not check for failure as we do not currently implement a back-up plan. */
385 xcb_ungrab_button_checked(NativeWindowSubsystem::X11GetConnection(), XCB_BUTTON_INDEX_ANY,
386 m_hButtonGrabWindow, XCB_MOD_MASK_ANY);
387 m_hButtonGrabWindow = 0;
388 }
389#else
390
391 /* On other platforms we are just praying Qt method to work: */
392 m_views[m_iKeyboardCaptureViewIndex]->releaseKeyboard();
393
394#endif
395
396 /* Forget which screen had captured the keyboard: */
397 m_iKeyboardCaptureViewIndex = -1;
398
399 /* Store new keyboard-captured state value: */
400 m_fKeyboardCaptured = false;
401
402 /* Notify all the listeners: */
403 emit sigStateChange(state());
404 }
405}
406
407void UIKeyboardHandler::releaseAllPressedKeys(bool aReleaseHostKey /* = true */)
408{
409 bool fSentRESEND = false;
410
411 /* Send a dummy modifier sequence to prevent the guest OS from recognizing
412 * a single key click (for ex., Alt) and performing an unwanted action
413 * (for ex., activating the menu) when we release all pressed keys below.
414 * This is just a work-around and is likely to fail in some cases. We are
415 * not aware of any ideal solution. Historically we sent an 0xFE scan code,
416 * but this is a real key release code on Brazilian keyboards. Now we send
417 * a sequence of all modifier keys contained in the host sequence, hoping
418 * that the user will choose something which the guest does not interpret. */
419 for (uint i = 0; i < RT_ELEMENTS(m_pressedKeys); i++)
420 {
421 if ((m_pressedKeys[i] & IsKeyPressed) || (m_pressedKeys[i] & IsExtKeyPressed))
422 {
423 if (!fSentRESEND)
424 {
425 QList <unsigned> shortCodes = UIHostCombo::modifiersToScanCodes(gEDataManager->hostKeyCombination());
426 QVector <LONG> codes;
427 foreach (unsigned idxCode, shortCodes)
428 {
429 if (idxCode & 0x100)
430 codes << 0xE0;
431 codes << (idxCode & 0x7F);
432 m_pressedKeys[idxCode & 0x7F] &= idxCode & 0x100 ? ~IsExtKeyPressed : ~IsKeyPressed;
433 }
434 foreach (unsigned idxCode, shortCodes)
435 {
436 if (idxCode & 0x100)
437 codes << 0xE0;
438 codes << ((idxCode & 0x7F) | 0x80);
439 }
440 uimachine()->putScancodes(codes);
441 fSentRESEND = true;
442 }
443 if (m_pressedKeys[i] & IsKeyPressed)
444 uimachine()->putScancode(i | 0x80);
445 else
446 {
447 QVector <LONG> codes(2);
448 codes[0] = 0xE0;
449 codes[1] = i | 0x80;
450 uimachine()->putScancodes(codes);
451 }
452 }
453 m_pressedKeys[i] = 0;
454 }
455
456 if (aReleaseHostKey)
457 {
458 m_fHostComboPressed = false;
459 m_pressedHostComboKeys.clear();
460 }
461
462#ifdef VBOX_WS_MAC
463 unsigned int hostComboModifierMask = 0;
464 QList<int> hostCombo = UIHostCombo::toKeyCodeList(gEDataManager->hostKeyCombination());
465 for (int i = 0; i < hostCombo.size(); ++i)
466 hostComboModifierMask |= ::DarwinKeyCodeToDarwinModifierMask(hostCombo.at(i));
467 /* Clear most of the modifiers: */
468 m_uDarwinKeyModifiers &=
469 alphaLock | kEventKeyModifierNumLockMask |
470 (aReleaseHostKey ? 0 : hostComboModifierMask);
471#endif /* VBOX_WS_MAC */
472
473 /* Notify all the listeners: */
474 emit sigStateChange(state());
475}
476
477/* Current keyboard state: */
478int UIKeyboardHandler::state() const
479{
480 return uimachine()->machineState() == KMachineState_Null ? UIKeyboardStateType_KeyboardUnavailable :
481 UIKeyboardStateType_KeyboardAvailable |
482 (m_fKeyboardCaptured ? UIKeyboardStateType_KeyboardCaptured : 0) |
483 (m_fHostComboPressed ? UIKeyboardStateType_HostKeyPressed : 0) |
484 (m_fHostKeyComboPressInserted ? UIKeyboardStateType_HostKeyPressedInsertion : 0);
485}
486
487#ifdef VBOX_WITH_DEBUGGER_GUI
488void UIKeyboardHandler::setDebuggerActive(bool aActive /* = true*/)
489{
490 if (aActive)
491 {
492 m_fDebuggerActive = true;
493 releaseKeyboard();
494 }
495 else
496 m_fDebuggerActive = false;
497}
498
499#endif /* VBOX_WITH_DEBUGGER_GUI */
500
501#ifdef VBOX_WS_WIN
502void UIKeyboardHandler::winSkipKeyboardEvents(bool fSkip)
503{
504 m_fSkipKeyboardEvents = fSkip;
505}
506#endif /* VBOX_WS_WIN */
507
508bool UIKeyboardHandler::nativeEventFilter(void *pMessage, ulong uScreenId)
509{
510 /* Make sure view with passed index exists: */
511 if (!m_views.contains(uScreenId))
512 return false;
513
514 /* Check if some system event should be filtered out.
515 * Returning @c true means filtering-out,
516 * Returning @c false means passing event to Qt. */
517 bool fResult = false; /* Pass to Qt by default. */
518
519# if defined(VBOX_WS_MAC)
520
521 /* Acquire carbon event reference from the cocoa one: */
522 EventRef event = static_cast<EventRef>(darwinCocoaToCarbonEvent(pMessage));
523
524 /* Depending on event kind: */
525 const UInt32 uEventKind = ::GetEventKind(event);
526 switch (uEventKind)
527 {
528 /* Watch for simple key-events: */
529 case kEventRawKeyDown:
530 case kEventRawKeyRepeat:
531 case kEventRawKeyUp:
532 {
533 /* Acquire keycode: */
534 UInt32 uKeyCode = ~0U;
535 ::GetEventParameter(event, kEventParamKeyCode, typeUInt32,
536 NULL, sizeof(uKeyCode), NULL, &uKeyCode);
537
538 /* The usb keyboard driver translates these codes to different virtual
539 * keycodes depending of the keyboard type. There are ANSI, ISO, JIS
540 * and unknown. For European keyboards (ISO) the key 0xa and 0x32 have
541 * to be switched. Here we are doing this at runtime, cause the user
542 * can have more than one keyboard (of different type), where he may
543 * switch at will all the time. Default is the ANSI standard as defined
544 * in g_aDarwinToSet1. Please note that the "~" on some English ISO
545 * keyboards will be wrongly swapped. This can maybe fixed by
546 * using a Apple keyboard layout in the guest. */
547 if ( (uKeyCode == 0xa || uKeyCode == 0x32)
548 && KBGetLayoutType(LMGetKbdType()) == kKeyboardISO)
549 uKeyCode = 0x3c - uKeyCode;
550
551 /* Translate keycode to set 1 scan code: */
552 unsigned uScanCode = ::DarwinKeycodeToSet1Scancode(uKeyCode);
553
554 /* If scan code is valid: */
555 if (uScanCode)
556 {
557 /* Calculate flags: */
558 int iFlags = 0;
559 if (uEventKind != kEventRawKeyUp)
560 iFlags |= KeyPressed;
561 if (uScanCode & VBOXKEY_EXTENDED)
562 iFlags |= KeyExtended;
563 /** @todo KeyPause, KeyPrint. */
564 uScanCode &= VBOXKEY_SCANCODE_MASK;
565
566 /* Get the unicode string (if present): */
567 AssertCompileSize(wchar_t, 2);
568 AssertCompileSize(UniChar, 2);
569 ByteCount cbWritten = 0;
570 wchar_t ucs[8];
571 if (::GetEventParameter(event, kEventParamKeyUnicodes, typeUnicodeText,
572 NULL, sizeof(ucs), &cbWritten, &ucs[0]) != 0)
573 cbWritten = 0;
574 ucs[cbWritten / sizeof(wchar_t)] = 0; /* The api doesn't terminate it. */
575
576 /* Finally, handle parsed key-event: */
577 fResult = keyEvent(uKeyCode, uScanCode, iFlags, uScreenId, ucs[0] ? ucs : NULL);
578 }
579
580 break;
581 }
582 /* Watch for modifier key-events: */
583 case kEventRawKeyModifiersChanged:
584 {
585 /* Acquire new modifier mask, it may contain
586 * multiple modifier changes, kind of annoying: */
587 UInt32 uNewMask = 0;
588 ::GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
589 NULL, sizeof(uNewMask), NULL, &uNewMask);
590
591 /* Adjust new modifier mask to distinguish left/right modifiers: */
592 uNewMask = ::DarwinAdjustModifierMask(uNewMask, pMessage);
593
594 /* Determine what is really changed: */
595 const UInt32 changed = uNewMask ^ m_uDarwinKeyModifiers;
596 if (changed)
597 {
598 for (UInt32 bit = 0; bit < 32; ++bit)
599 {
600 /* Skip unchanged bits: */
601 if (!(changed & (1 << bit)))
602 continue;
603 /* Acquire set 1 scan code from new mask: */
604 unsigned uScanCode = ::DarwinModifierMaskToSet1Scancode(1 << bit);
605 /* Skip invalid scan codes: */
606 if (!uScanCode)
607 continue;
608 /* Acquire darwin keycode from new mask: */
609 unsigned uKeyCode = ::DarwinModifierMaskToDarwinKeycode(1 << bit);
610 /* Assert invalid keycodes: */
611 Assert(uKeyCode);
612
613 /* For non-lockable modifier: */
614 if (!(uScanCode & VBOXKEY_LOCK))
615 {
616 /* Calculate flags: */
617 unsigned uFlags = (uNewMask & (1 << bit)) ? KeyPressed : 0;
618 if (uScanCode & VBOXKEY_EXTENDED)
619 uFlags |= KeyExtended;
620 uScanCode &= VBOXKEY_SCANCODE_MASK;
621
622 /* Finally, handle parsed key-event: */
623 keyEvent(uKeyCode, uScanCode & 0xff, uFlags, uScreenId);
624 }
625 /* For lockable modifier: */
626 else
627 {
628 /* Calculate flags: */
629 unsigned uFlags = 0;
630 if (uScanCode & VBOXKEY_EXTENDED)
631 uFlags |= KeyExtended;
632 uScanCode &= VBOXKEY_SCANCODE_MASK;
633
634 /* Finally, handle parsed press/release pair: */
635 keyEvent(uKeyCode, uScanCode, uFlags | KeyPressed, uScreenId);
636 keyEvent(uKeyCode, uScanCode, uFlags, uScreenId);
637 }
638 }
639 }
640
641 /* Remember new modifier mask: */
642 m_uDarwinKeyModifiers = uNewMask;
643
644 /* Always return true here because we'll otherwise getting a Qt event
645 * we don't want and that will only cause the Pause warning to pop up: */
646 fResult = true;
647
648 break;
649 }
650 default:
651 break;
652 }
653
654# elif defined(VBOX_WS_WIN)
655
656 /* Ignore this event if m_fSkipKeyboardEvents is set by winSkipKeyboardEvents(). */
657 if (m_fSkipKeyboardEvents)
658 return false;
659
660 /* Cast to MSG event: */
661 MSG *pMsg = static_cast<MSG*>(pMessage);
662
663 /* Depending on message type: */
664 switch (pMsg->message)
665 {
666 /* Watch for key-events: */
667 case WM_KEYDOWN:
668 case WM_KEYUP:
669 case WM_SYSKEYDOWN:
670 case WM_SYSKEYUP:
671 {
672 // WORKAROUND:
673 // Can't do COM inter-process calls from a SendMessage handler,
674 // see http://support.microsoft.com/kb/131056.
675 if (uiCommon().isSeparateProcess() && InSendMessage())
676 {
677 PostMessage(pMsg->hwnd, pMsg->message, pMsg->wParam, pMsg->lParam);
678 fResult = true;
679 break;
680 }
681
682 /* Check for our own special flag to ignore this event.
683 * That flag could only be set later in this function
684 * so having it here means this event came here
685 * for the second time already. */
686 if (pMsg->lParam & (0x1 << 25))
687 {
688 /* Remove that flag as well: */
689 pMsg->lParam &= ~(0x1 << 25);
690 fResult = false;
691 break;
692 }
693
694 /* Scan codes 0x80 and 0x00 should be filtered out: */
695 unsigned uScan = (pMsg->lParam >> 16) & 0x7F;
696 if (!uScan)
697 {
698 fResult = true;
699 break;
700 }
701
702 /* Get the virtual key: */
703 int iVKey = pMsg->wParam;
704
705 /* Calculate flags: */
706 int iFlags = 0;
707 if (pMsg->lParam & 0x1000000)
708 iFlags |= KeyExtended;
709 if (!(pMsg->lParam & 0x80000000))
710 iFlags |= KeyPressed;
711
712 /* Make sure AltGr monitor exists: */
713 AssertPtrReturn(m_pAltGrMonitor, false);
714 {
715 /* Filter event out if we are sure that this is a fake left control event: */
716 if (m_pAltGrMonitor->isCurrentEventDefinitelyFake(uScan, iFlags & KeyPressed, iFlags & KeyExtended))
717 {
718 fResult = true;
719 break;
720 }
721 /* Update AltGR monitor state from key-event: */
722 m_pAltGrMonitor->updateStateFromKeyEvent(uScan, iFlags & KeyPressed, iFlags & KeyExtended);
723 /* And release left Ctrl key early (if required): */
724 if (m_pAltGrMonitor->isLeftControlReleaseNeeded())
725 uimachine()->putScancode(0x1D | 0x80);
726 }
727
728 /* Check for special Korean keys. Based on the keyboard layout selected
729 * on the host, the scan code in lParam might be 0x71/0x72 or 0xF1/0xF2.
730 * In either case, we must deliver 0xF1/0xF2 scan code to the guest when
731 * the key is pressed and nothing when it's released. */
732 if (uScan == 0x71 || uScan == 0x72)
733 {
734 uScan |= 0x80;
735 iFlags = KeyPressed; /* Because a release would be ignored. */
736 iVKey = VK_PROCESSKEY; /* In case it was 0xFF. */
737 }
738
739 /* When one of the SHIFT keys is held and one of the cursor movement
740 * keys is pressed, Windows duplicates SHIFT press/release messages,
741 * but with the virtual keycode set to 0xFF. These virtual keys are also
742 * sent in some other situations (Pause, PrtScn, etc.). Filter out such messages. */
743 if (iVKey == 0xFF)
744 {
745 fResult = true;
746 break;
747 }
748
749 /* Handle special virtual keys: */
750 switch (iVKey)
751 {
752 case VK_SHIFT:
753 case VK_CONTROL:
754 case VK_MENU:
755 {
756 /* Overcome Win32 modifier key generalization: */
757 int iKeyscan = uScan;
758 if (iFlags & KeyExtended)
759 iKeyscan |= 0xE000;
760 switch (iKeyscan)
761 {
762 case 0x002A: iVKey = VK_LSHIFT; break;
763 case 0x0036: iVKey = VK_RSHIFT; break;
764 case 0x001D: iVKey = VK_LCONTROL; break;
765 case 0xE01D: iVKey = VK_RCONTROL; break;
766 case 0x0038: iVKey = VK_LMENU; break;
767 case 0xE038: iVKey = VK_RMENU; break;
768 }
769 break;
770 }
771 case VK_NUMLOCK:
772 /* Win32 sets the extended bit for the NumLock key. Reset it: */
773 iFlags &= ~KeyExtended;
774 break;
775 case VK_SNAPSHOT:
776 iFlags |= KeyPrint;
777 break;
778 case VK_PAUSE:
779 iFlags |= KeyPause;
780 break;
781 }
782
783 /* Finally, handle parsed key-event: */
784 fResult = keyEvent(iVKey, uScan, iFlags, uScreenId);
785
786 /* Always let Windows see key releases to prevent stuck keys.
787 * Hopefully this won't cause any other issues. */
788 if (pMsg->message == WM_KEYUP || pMsg->message == WM_SYSKEYUP)
789 {
790 fResult = false;
791 break;
792 }
793
794 /* Above keyEvent() returned that it didn't processed the event, but since the
795 * keyboard is captured, we don't want to pass it to Windows. We just want
796 * to let Qt process the message (to handle non-alphanumeric <HOST>+key
797 * shortcuts for example). So send it directly to the window with the
798 * special flag in the reserved area of lParam (to avoid recursion). */
799 if (!fResult && m_fKeyboardCaptured)
800 {
801 ::SendMessage(pMsg->hwnd, pMsg->message,
802 pMsg->wParam, pMsg->lParam | (0x1 << 25));
803 fResult = true;
804 break;
805 }
806
807 /* These special keys have to be handled by Windows as well to update the
808 * internal modifier state and to enable/disable the keyboard LED: */
809 if (iVKey == VK_NUMLOCK || iVKey == VK_CAPITAL || iVKey == VK_LSHIFT || iVKey == VK_RSHIFT)
810 {
811 fResult = false;
812 break;
813 }
814
815 break;
816 }
817 default:
818 break;
819 }
820
821# elif defined(VBOX_WS_NIX)
822 if (uiCommon().X11ServerAvailable())
823 {
824 /* Cast to XCB event: */
825 xcb_generic_event_t *pEvent = static_cast<xcb_generic_event_t*>(pMessage);
826
827 /* Depending on event type: */
828 switch (pEvent->response_type & ~0x80)
829 {
830 /* Watch for key-events: */
831 case XCB_KEY_PRESS:
832 case XCB_KEY_RELEASE:
833 {
834 /* Cast to XCB key-event: */
835 xcb_key_press_event_t *pKeyEvent = static_cast<xcb_key_press_event_t*>(pMessage);
836
837 /* Translate the keycode to a PC scan code: */
838 unsigned uScan = handleXKeyEvent(NativeWindowSubsystem::X11GetDisplay(), pKeyEvent->detail);
839
840 /* Scan codes 0x00 (no valid translation) and 0x80 (extended flag) are ignored: */
841 if (!(uScan & 0x7F))
842 {
843 fResult = true;
844 break;
845 }
846
847 /* Calculate flags: */
848 int iflags = 0;
849 if (uScan >> 8)
850 iflags |= KeyExtended;
851 if ((pEvent->response_type & ~0x80) == XCB_KEY_PRESS)
852 iflags |= KeyPressed;
853
854 /* Remove the extended flag: */
855 uScan &= 0x7F;
856
857 /* Special Korean keys must send scan code 0xF1/0xF2
858 * when pressed and nothing when released. */
859 if (uScan == 0x71 || uScan == 0x72)
860 {
861 if ((pEvent->response_type & ~0x80) == XCB_KEY_RELEASE)
862 {
863 fResult = true;
864 break;
865 }
866 /* Re-create the bizarre scan code: */
867 uScan |= 0x80;
868 }
869
870 /* Translate the keycode to a keysym: */
871 KeySym ks = ::wrapXkbKeycodeToKeysym(NativeWindowSubsystem::X11GetDisplay(), pKeyEvent->detail, 0, 0);
872
873 /* Update special flags: */
874 switch (ks)
875 {
876 case XK_Print:
877 iflags |= KeyPrint;
878 break;
879 case XK_Pause:
880 if (pKeyEvent->state & ControlMask) /* Break */
881 {
882 ks = XK_Break;
883 iflags |= KeyExtended;
884 uScan = 0x46;
885 }
886 else
887 iflags |= KeyPause;
888 break;
889 }
890
891 /* Finally, handle parsed key-event: */
892 fResult = keyEvent(ks, uScan, iflags, uScreenId);
893
894 break;
895 }
896 default:
897 break;
898 }
899 }
900
901# else
902
903# warning "port me!"
904
905# endif
906
907 /* Return result: */
908 return fResult;
909}
910
911/* Machine state-change handler: */
912void UIKeyboardHandler::sltMachineStateChanged()
913{
914 /* Get cached machine states: */
915 const KMachineState enmState = uimachine()->machineState();
916
917 /* Notify all the listeners that machine state was [de]initialized: */
918 if ( enmState == KMachineState_Null
919 || enmState == KMachineState_Starting)
920 emit sigStateChange(state());
921
922 /* Handle particular machine states: */
923 switch (enmState)
924 {
925 case KMachineState_Paused:
926 case KMachineState_TeleportingPausedVM:
927 case KMachineState_Stuck:
928 {
929 /* Release the keyboard: */
930 releaseKeyboard();
931 /* And all pressed keys except the host-one : */
932 releaseAllPressedKeys(false /* release host-key? */);
933 break;
934 }
935 case KMachineState_Running:
936 {
937 /* Capture the keyboard by the first focused view: */
938 QList<ulong> theListOfViewIds = m_views.keys();
939 for (int i = 0; i < theListOfViewIds.size(); ++i)
940 {
941 if (viewHasFocus(theListOfViewIds[i]))
942 {
943 /* Capture keyboard: */
944#ifdef VBOX_WS_WIN
945 if (!isAutoCaptureDisabled() && autoCaptureSetGlobally() &&
946 GetAncestor((HWND)m_views[theListOfViewIds[i]]->winId(), GA_ROOT) == GetForegroundWindow())
947#else /* !VBOX_WS_WIN */
948 if (!isAutoCaptureDisabled() && autoCaptureSetGlobally())
949#endif /* !VBOX_WS_WIN */
950 captureKeyboard(theListOfViewIds[i]);
951 /* Reset the single-time disable capture flag: */
952 if (isAutoCaptureDisabled())
953 setAutoCaptureDisabled(false);
954 break;
955 }
956 }
957 break;
958 }
959 default:
960 break;
961 }
962
963 /* Recall reminder about paused VM input
964 * if we are not in paused VM state already: */
965 if (machineLogic()->activeMachineWindow() &&
966 enmState != KMachineState_Paused &&
967 enmState != KMachineState_TeleportingPausedVM)
968 UINotificationMessage::forgetAboutPausedVMInput();
969}
970
971void UIKeyboardHandler::sltFinaliseCaptureKeyboard()
972{
973 /* Try to finalise keyboard capture: */
974 if (!finaliseCaptureKeyboard())
975 {
976 /* Try again in another 300 milliseconds in case of failure: */
977 QTimer::singleShot(300, this, SLOT(sltFinaliseCaptureKeyboard()));
978 }
979}
980
981/* Keyboard-handler constructor: */
982UIKeyboardHandler::UIKeyboardHandler(UIMachineLogic *pMachineLogic)
983 : QObject(pMachineLogic)
984 , m_pMachineLogic(pMachineLogic)
985 , m_iKeyboardCaptureViewIndex(-1)
986 , m_fKeyboardCaptured(false)
987 , m_fHostComboPressed(false)
988 , m_bIsHostComboAlone(false)
989 , m_bIsHostComboProcessed(false)
990 , m_fPassCADtoGuest(false)
991 , m_fHostKeyComboPressInserted(false)
992 , m_fDebuggerActive(false)
993 , m_iKeyboardHookViewIndex(-1)
994#if defined(VBOX_WS_MAC)
995 , m_uDarwinKeyModifiers(0)
996#elif defined(VBOX_WS_WIN)
997 , m_fIsHostkeyInCapture(false)
998 , m_fSkipKeyboardEvents(false)
999 , m_keyboardHook(NULL)
1000 , m_pAltGrMonitor(0)
1001#elif defined(VBOX_WS_NIX)
1002 , m_hButtonGrabWindow(0)
1003#endif /* VBOX_WS_NIX */
1004{
1005 /* Prepare: */
1006 prepareCommon();
1007
1008 /* Load settings: */
1009 loadSettings();
1010
1011 /* Initialize: */
1012 sltMachineStateChanged();
1013}
1014
1015/* Keyboard-handler destructor: */
1016UIKeyboardHandler::~UIKeyboardHandler()
1017{
1018 /* Cleanup: */
1019 cleanupCommon();
1020}
1021
1022void UIKeyboardHandler::prepareCommon()
1023{
1024#ifdef VBOX_WS_WIN
1025 /* Prepare AltGR monitor: */
1026 m_pAltGrMonitor = new WinAltGrMonitor;
1027#endif /* VBOX_WS_WIN */
1028
1029 /* Machine state-change updater: */
1030 connect(uimachine(), &UIMachine::sigMachineStateChange, this, &UIKeyboardHandler::sltMachineStateChanged);
1031
1032 /* Pressed keys: */
1033 ::memset(m_pressedKeys, 0, sizeof(m_pressedKeys));
1034 ::memset(m_pressedKeysCopy, 0, sizeof(m_pressedKeysCopy));
1035}
1036
1037void UIKeyboardHandler::loadSettings()
1038{
1039 /* Global settings: */
1040#ifdef VBOX_WS_NIX
1041 if (uiCommon().X11ServerAvailable())
1042 {
1043 /* Initialize the X keyboard subsystem: */
1044 initMappedX11Keyboard(NativeWindowSubsystem::X11GetDisplay(), gEDataManager->remappedScanCodes());
1045 /* Fix for http://www.virtualbox.org/ticket/1296:
1046 * when X11 sends events for repeated keys, it always inserts an XKeyRelease
1047 * before the XKeyPress. */
1048 /* Disable key release events during key auto-repeat: */
1049 XkbSetDetectableAutoRepeat(NativeWindowSubsystem::X11GetDisplay(), True, NULL);
1050 }
1051#endif /* VBOX_WS_NIX */
1052
1053 /* Extra data settings: */
1054 {
1055 /* CAD setting: */
1056 m_fPassCADtoGuest = gEDataManager->passCADtoGuest(uiCommon().managedVMUuid());
1057 }
1058}
1059
1060void UIKeyboardHandler::cleanupCommon()
1061{
1062#if defined(VBOX_WS_MAC)
1063
1064 /* Cleanup keyboard-hook: */
1065 if (m_iKeyboardHookViewIndex != -1)
1066 {
1067 /* Ungrab the keyboard and unregister the event callback/hook: */
1068 ::DarwinReleaseKeyboard();
1069 UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */,
1070 UIKeyboardHandler::macKeyboardProc, this);
1071 }
1072
1073#elif defined(VBOX_WS_WIN)
1074
1075 /* Cleanup AltGR monitor: */
1076 delete m_pAltGrMonitor;
1077 m_pAltGrMonitor = 0;
1078
1079 /* If keyboard-hook is installed: */
1080 if (m_keyboardHook)
1081 {
1082 /* Uninstall existing keyboard-hook: */
1083 UnhookWindowsHookEx(m_keyboardHook);
1084 m_keyboardHook = 0;
1085 }
1086
1087#endif /* VBOX_WS_WIN */
1088
1089 /* Update keyboard hook view index: */
1090 m_iKeyboardHookViewIndex = -1;
1091}
1092
1093UIActionPool *UIKeyboardHandler::actionPool() const
1094{
1095 return machineLogic()->actionPool();
1096}
1097
1098UIMachine *UIKeyboardHandler::uimachine() const
1099{
1100 return machineLogic()->uimachine();
1101}
1102
1103/* Event handler for prepared listener(s): */
1104bool UIKeyboardHandler::eventFilter(QObject *pWatchedObject, QEvent *pEvent)
1105{
1106 /* Check if pWatchedObject object is view: */
1107 if (UIMachineView *pWatchedView = isItListenedView(pWatchedObject))
1108 {
1109 /* Get corresponding screen index: */
1110 ulong uScreenId = m_views.key(pWatchedView);
1111 NOREF(uScreenId);
1112 /* Handle view events: */
1113 switch (pEvent->type())
1114 {
1115 case QEvent::FocusIn:
1116 {
1117#if defined(VBOX_WS_MAC)
1118
1119 /* If keyboard-hook is NOT installed;
1120 * Or installed but NOT for that view: */
1121 if ((int)uScreenId != m_iKeyboardHookViewIndex)
1122 {
1123 /* If keyboard-hook is NOT installed: */
1124 if (m_iKeyboardHookViewIndex == -1)
1125 {
1126 /* Disable mouse and keyboard event compression/delaying
1127 * to make sure we *really* get all of the events: */
1128 ::CGSetLocalEventsSuppressionInterval(0.0); /** @todo replace with CGEventSourceSetLocalEventsSuppressionInterval ? */
1129 ::darwinSetMouseCoalescingEnabled(false);
1130
1131 /* Bring the caps lock state up to date,
1132 * otherwise e.g. a later Shift key press will accidentally inject a CapsLock key press and release,
1133 * see UIKeyboardHandler::macKeyboardEvent for the code handling modifier key state changes. */
1134 m_uDarwinKeyModifiers ^= (m_uDarwinKeyModifiers ^ ::GetCurrentEventKeyModifiers()) & alphaLock;
1135
1136 /* Register the event callback/hook and grab the keyboard: */
1137 UICocoaApplication::instance()->registerForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */,
1138 UIKeyboardHandler::macKeyboardProc, this);
1139 ::DarwinGrabKeyboard(false);
1140 }
1141 }
1142
1143#elif defined(VBOX_WS_WIN)
1144
1145 /* If keyboard-hook is NOT installed;
1146 * Or installed but NOT for that view
1147 * AND no --no-keyboard-grabbing option: */
1148 if ( (!m_keyboardHook || (int)uScreenId != m_iKeyboardHookViewIndex)
1149 && !uiCommon().shouldNotGrabKeyboard())
1150 {
1151 /* If keyboard-hook is installed: */
1152 if (m_keyboardHook)
1153 {
1154 /* Uninstall existing keyboard-hook: */
1155 UnhookWindowsHookEx(m_keyboardHook);
1156 m_keyboardHook = 0;
1157 }
1158 /* Install new keyboard-hook: */
1159 m_keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, UIKeyboardHandler::winKeyboardProc, GetModuleHandle(NULL), 0);
1160 AssertMsg(m_keyboardHook, ("SetWindowsHookEx(): err=%d", GetLastError()));
1161 }
1162
1163#endif /* VBOX_WS_WIN */
1164
1165 /* Update keyboard hook view index: */
1166 m_iKeyboardHookViewIndex = uScreenId;
1167
1168 if (isSessionRunning())
1169 {
1170 /* Capture keyboard: */
1171#ifdef VBOX_WS_WIN
1172 if (!isAutoCaptureDisabled() && autoCaptureSetGlobally() &&
1173 GetAncestor((HWND)pWatchedView->winId(), GA_ROOT) == GetForegroundWindow())
1174#else /* !VBOX_WS_WIN */
1175 if (!isAutoCaptureDisabled() && autoCaptureSetGlobally())
1176#endif /* !VBOX_WS_WIN */
1177 captureKeyboard(uScreenId);
1178 /* Reset the single-time disable capture flag: */
1179 if (isAutoCaptureDisabled())
1180 setAutoCaptureDisabled(false);
1181 }
1182
1183 break;
1184 }
1185 case QEvent::FocusOut:
1186 {
1187 /* If host key combo press has been inserted (with no release yet) insert a release now: */
1188 if (m_fHostKeyComboPressInserted)
1189 machineLogic()->typeHostKeyComboPressRelease(false);
1190
1191#if defined(VBOX_WS_MAC)
1192
1193 /* If keyboard-hook is installed: */
1194 if ((int)uScreenId == m_iKeyboardHookViewIndex)
1195 {
1196 /* Ungrab the keyboard and unregister the event callback/hook: */
1197 ::DarwinReleaseKeyboard();
1198 UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */,
1199 UIKeyboardHandler::macKeyboardProc, this);
1200 }
1201
1202#elif defined(VBOX_WS_WIN)
1203
1204 /* If keyboard-hook is installed: */
1205 if (m_keyboardHook)
1206 {
1207 /* Uninstall existing keyboard-hook: */
1208 UnhookWindowsHookEx(m_keyboardHook);
1209 m_keyboardHook = 0;
1210 }
1211
1212#endif /* VBOX_WS_WIN */
1213
1214 /* Update keyboard hook view index: */
1215 m_iKeyboardHookViewIndex = -1;
1216
1217 /* Release keyboard: */
1218 if (isSessionRunning() || isSessionStuck())
1219 releaseKeyboard();
1220 /* And all pressed keys: */
1221 releaseAllPressedKeys(true);
1222
1223 break;
1224 }
1225 case QEvent::KeyPress:
1226 case QEvent::KeyRelease:
1227 {
1228 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);
1229
1230 if (m_fHostComboPressed && pEvent->type() == QEvent::KeyPress)
1231 {
1232 /* Passing F1-F12 keys to the guest: */
1233 if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F12)
1234 {
1235 QVector <LONG> combo(6);
1236 combo[0] = 0x1d; /* Ctrl down */
1237 combo[1] = 0x38; /* Alt down */
1238 combo[4] = 0xb8; /* Alt up */
1239 combo[5] = 0x9d; /* Ctrl up */
1240 if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F10)
1241 {
1242 combo[2] = 0x3b + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 down */
1243 combo[3] = 0xbb + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 up */
1244 }
1245 /* There is some scan slice between F10 and F11 keys, so its separated: */
1246 else if (pKeyEvent->key() >= Qt::Key_F11 && pKeyEvent->key() <= Qt::Key_F12)
1247 {
1248 combo[2] = 0x57 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 down */
1249 combo[3] = 0xd7 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 up */
1250 }
1251 uimachine()->putScancodes(combo);
1252 }
1253 /* Process hot keys not processed in keyEvent() (as in case of non-alphanumeric keys): */
1254 actionPool()->processHotKey(QKeySequence(pKeyEvent->key()));
1255 }
1256 else if (!m_fHostComboPressed && pEvent->type() == QEvent::KeyRelease)
1257 {
1258 /* Show a possible warning on key release which seems to be more expected by the end user: */
1259 if (uimachine()->isPaused())
1260 UINotificationMessage::remindAboutPausedVMInput();
1261 }
1262
1263 break;
1264 }
1265 default:
1266 break;
1267 }
1268 }
1269
1270 /* Else just propagate to base-class: */
1271 return QObject::eventFilter(pWatchedObject, pEvent);
1272}
1273
1274#if defined(VBOX_WS_MAC)
1275
1276/* static */
1277bool UIKeyboardHandler::macKeyboardProc(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser)
1278{
1279 /* Determine the event class: */
1280 EventRef event = (EventRef)pvCarbonEvent;
1281 UInt32 uEventClass = ::GetEventClass(event);
1282
1283 /* Check if this is an application key combo. In that case we will not pass
1284 * the event to the guest, but let the host process it. */
1285 if (::darwinIsApplicationCommand(pvCocoaEvent))
1286 return false;
1287
1288 /* Get the keyboard handler from the user's void data: */
1289 UIKeyboardHandler *pKeyboardHandler = static_cast<UIKeyboardHandler*>(pvUser);
1290
1291 /* All keyboard class events needs to be handled: */
1292 if (uEventClass == kEventClassKeyboard && pKeyboardHandler && pKeyboardHandler->macKeyboardEvent(pvCocoaEvent, event))
1293 return true;
1294
1295 /* Pass the event along: */
1296 return false;
1297}
1298
1299bool UIKeyboardHandler::macKeyboardEvent(const void *pvCocoaEvent, EventRef event)
1300{
1301 /* Check what related machine-view was NOT unregistered yet: */
1302 if (!m_views.contains(m_iKeyboardHookViewIndex))
1303 return false;
1304
1305 /* Pass event to machine-view's event handler: */
1306 Q_UNUSED(event);
1307 return nativeEventFilter(unconst(pvCocoaEvent), m_iKeyboardHookViewIndex);
1308}
1309
1310#elif defined(VBOX_WS_WIN)
1311
1312/* static */
1313LRESULT CALLBACK UIKeyboardHandler::winKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
1314{
1315 /* All keyboard class events needs to be handled: */
1316 if (nCode == HC_ACTION && m_spKeyboardHandler && m_spKeyboardHandler->winKeyboardEvent(wParam, *(KBDLLHOOKSTRUCT*)lParam))
1317 return 1;
1318
1319 /* Pass the event along: */
1320 return CallNextHookEx(NULL, nCode, wParam, lParam);
1321}
1322
1323bool UIKeyboardHandler::winKeyboardEvent(UINT msg, const KBDLLHOOKSTRUCT &event)
1324{
1325 /* Check that related machine-view was NOT unregistered yet: */
1326 if (!m_views.contains(m_iKeyboardHookViewIndex))
1327 return false;
1328
1329 /* HACK ALERT! Check that we're not in cleanup, as we're using gEDataManger
1330 to get host key combinations and it as probably been cleaned up already.
1331 We don't want to cause it to re-instantiate, with all the COM traffic
1332 that might involve. Sample assertion stack (IPRT not windbg, sorry):
1333
1334 !!Assertion Failed!!
1335 Expression: mRC != RPC_E_CANTCALLOUT_ININPUTSYNCCALL
1336 Location : E:\vbox\svn\trunk\out\win.amd64\debug\obj\UICommon\include\COMWrappers.cpp(3857) class QVector<class QString> __cdecl CVirtualBox::GetExtraDataKeys(void)
1337 Stack :
1338 00007fff39aa6634 VBoxRT.dll!RTAssertMsg1+0x274 (rva:0x246634)
1339 [E:\vbox\svn\trunk\src\VBox\Runtime\common\misc\assert.cpp:159]
1340 00007fff39aa542f VBoxRT.dll!RTAssertMsg1Weak+0x2f (rva:0x24542f)
1341 [E:\vbox\svn\trunk\src\VBox\Runtime\common\misc\RTAssertMsg1Weak.cpp:40 (+0x0)]
1342 00007fff36e5c00f UICommon.dll!CVirtualBox::GetExtraDataKeys+0x23f (rva:0x3fc00f)
1343 [E:\vbox\svn\trunk\out\win.amd64\debug\obj\UICommon\include\COMWrappers.cpp:3857]
1344 00007fff36ac2cc9 UICommon.dll!UIExtraDataManager::prepareGlobalExtraDataMap+0xb9 (rva:0x62cc9)
1345 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\extradata\UIExtraDataManager.cpp:4845]
1346 00007fff36ac2bf8 UICommon.dll!UIExtraDataManager::prepare+0x28 (rva:0x62bf8)
1347 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\extradata\UIExtraDataManager.cpp:4833 (+0x0)]
1348 00007fff36ab1896 UICommon.dll!UIExtraDataManager::instance+0x66 (rva:0x51896)
1349 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\extradata\UIExtraDataManager.cpp:2011 (+0x0)]
1350 00007ff771b05b06 VirtualBoxVM.exe!UIKeyboardHandler::winKeyboardEvent+0xe6 (rva:0x35b06)
1351 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\runtime\UIKeyboardHandler.cpp:1324]
1352 00007ff771b059ec VirtualBoxVM.exe!UIKeyboardHandler::winKeyboardProc+0x4c (rva:0x359ec)
1353 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\runtime\UIKeyboardHandler.cpp:1304]
1354 00007fff763311f2 USER32.dll!GetDlgCtrlID+0x232 (rva:0x211f2)
1355 00007fff7639ac89 USER32.dll!CreateSystemThreads+0xa29 (rva:0x8ac89)
1356 00007fff76b30ba4 ntdll.dll!KiUserCallbackDispatcher+0x24 (rva:0xa0ba4)
1357 00007fff749b1064 win32u.dll!NtUserPeekMessage+0x14 (rva:0x1064)
1358 00007fff7631a553 USER32.dll!PeekMessageW+0x1e3 (rva:0xa553)
1359 00007fff7631a4b3 USER32.dll!PeekMessageW+0x143 (rva:0xa4b3)
1360 00007e11000270dc ConEmuHk64.dll!SetLoadLibraryCallback+0xc3ac (rva:0x270dc)
1361 00007fff759be71b combase.dll!CoRegisterPSClsid+0x82b (rva:0x6e71b)
1362 00007fff759be685 combase.dll!CoRegisterPSClsid+0x795 (rva:0x6e685)
1363 00007fff759bcec1 combase.dll!Ordinal87+0x2851 (rva:0x6cec1)
1364 00007fff759bcbbb combase.dll!Ordinal87+0x254b (rva:0x6cbbb)
1365 00007fff75994956 combase.dll!RoGetApartmentIdentifier+0x55f6 (rva:0x44956)
1366 cccccccccccccccc */
1367 if (UICommon::instance()->isCleaningUp())
1368 return false;
1369
1370 if (!m_fKeyboardCaptured)
1371 return false;
1372
1373 /* It's possible that a key has been pressed while the keyboard was not
1374 * captured, but is being released under the capture. Detect this situation
1375 * and do not pass on the key press to the virtual machine. */
1376 uint8_t what_pressed = (event.flags & 0x01)
1377 && (event.vkCode != VK_RSHIFT)
1378 ? IsExtKeyPressed : IsKeyPressed;
1379 if ( (event.flags & 0x80) /* released */
1380 && ( ( UIHostCombo::toKeyCodeList(gEDataManager->hostKeyCombination()).contains((int)event.vkCode)
1381 && !m_fIsHostkeyInCapture)
1382 || ( m_pressedKeys[event.scanCode & 0x7F]
1383 & (IsKbdCaptured | what_pressed))
1384 == what_pressed))
1385 return false;
1386
1387 /* For normal user applications, Windows defines AltGr to be the same as
1388 * LControl + RAlt. Without a low-level hook it is hard to recognise the
1389 * additional LControl event inserted, but in a hook we recognise it by
1390 * its special 0x21D scan code. */
1391 if ( m_views[m_iKeyboardHookViewIndex]->hasFocus()
1392 && ((event.scanCode & ~0x80) == 0x21D))
1393 return true;
1394
1395 /* Compose the MSG: */
1396 MSG message;
1397 message.hwnd = (HWND)m_views[m_iKeyboardHookViewIndex]->winId();
1398 message.message = msg;
1399 message.wParam = event.vkCode;
1400 message.lParam = 1 | (event.scanCode & 0xFF) << 16 | (event.flags & 0xFF) << 24;
1401
1402 /* Windows sets here the extended bit when the Right Shift key is pressed,
1403 * which is totally wrong. Undo it. */
1404 if (event.vkCode == VK_RSHIFT)
1405 message.lParam &= ~0x1000000;
1406
1407 /* Pass event to view's event handler: */
1408 return nativeEventFilter(&message, m_iKeyboardHookViewIndex);
1409}
1410
1411#endif /* VBOX_WS_WIN */
1412
1413/**
1414 * If the user has just completed a control-alt-del combination then handle
1415 * that.
1416 * @returns true if handling should stop here, false otherwise
1417 */
1418bool UIKeyboardHandler::keyEventCADHandled(uint8_t uScan)
1419{
1420 /* Check if it's C-A-D and GUI/PassCAD is not set/allowed: */
1421 if (!m_fPassCADtoGuest &&
1422 uScan == 0x53 /* Del */ &&
1423 ((m_pressedKeys[0x38] & IsKeyPressed) /* Alt */ ||
1424 (m_pressedKeys[0x38] & IsExtKeyPressed)) &&
1425 ((m_pressedKeys[0x1d] & IsKeyPressed) /* Ctrl */ ||
1426 (m_pressedKeys[0x1d] & IsExtKeyPressed)))
1427 {
1428 /* Use the C-A-D combination as a last resort to get the keyboard and mouse back
1429 * to the host when the user forgets the Host Key. Note that it's always possible
1430 * to send C-A-D to the guest using the Host+Del combination: */
1431 if (isSessionRunning() && m_fKeyboardCaptured)
1432 {
1433 releaseKeyboard();
1434 if (!uimachine()->isMouseSupportsAbsolute() || !uimachine()->isMouseIntegrated())
1435 machineLogic()->mouseHandler()->releaseMouse();
1436 }
1437 return true;
1438 }
1439 return false;
1440}
1441
1442/**
1443 * Handle a non-special (C-A-D, pause, print) key press or release
1444 * @returns true if handling should stop here, false otherwise
1445 */
1446bool UIKeyboardHandler::keyEventHandleNormal(int iKey, uint8_t uScan, int fFlags, LONG *pCodes, uint *puCodesCount)
1447{
1448 /* Get the type of key - simple or extended: */
1449 uint8_t uWhatPressed = fFlags & KeyExtended ? IsExtKeyPressed : IsKeyPressed;
1450
1451 /* If some key was pressed or some previously pressed key was released =>
1452 * we are updating the list of pressed keys and preparing scan codes: */
1453 if ((fFlags & KeyPressed) || (m_pressedKeys[uScan] & uWhatPressed))
1454 {
1455 /* If HID LEDs sync is disabled or not supported, check if the guest has the
1456 * same view on the modifier keys (NumLock, CapsLock, ScrollLock) as the host. */
1457 if (!uimachine()->isHidLedsSyncEnabled())
1458 if (fFlags & KeyPressed)
1459 fixModifierState(pCodes, puCodesCount);
1460
1461 /* Prepend 'extended' scan code if needed: */
1462 if (fFlags & KeyExtended)
1463 pCodes[(*puCodesCount)++] = 0xE0;
1464
1465 /* Process key-press: */
1466 if (fFlags & KeyPressed)
1467 {
1468 /* Append scan code: */
1469 pCodes[(*puCodesCount)++] = uScan;
1470 m_pressedKeys[uScan] |= uWhatPressed;
1471 }
1472 /* Process key-release if that key was pressed before: */
1473 else if (m_pressedKeys[uScan] & uWhatPressed)
1474 {
1475 /* Append scan code: */
1476 pCodes[(*puCodesCount)++] = uScan | 0x80;
1477 m_pressedKeys[uScan] &= ~uWhatPressed;
1478 }
1479
1480 /* Update keyboard-captured flag: */
1481 if (m_fKeyboardCaptured)
1482 m_pressedKeys[uScan] |= IsKbdCaptured;
1483 else
1484 m_pressedKeys[uScan] &= ~IsKbdCaptured;
1485 }
1486 /* Ignore key-release if that key was NOT pressed before,
1487 * but only if thats not one of the host-combination keys: */
1488 else
1489 {
1490 /* Get host-combo key list: */
1491 QList<int> lstAllHostComboKey = UIHostCombo::toKeyCodeList(gEDataManager->hostKeyCombination());
1492 if (!lstAllHostComboKey.contains(iKey))
1493 return true;
1494 }
1495 return false;
1496}
1497
1498/**
1499 * Check whether the key pressed results in a host key combination being
1500 * handled.
1501 * @returns true if a combination was handled, false otherwise
1502 * @param pfResult where to store the result of the handling
1503 */
1504bool UIKeyboardHandler::keyEventHostComboHandled(int iKey, wchar_t *pUniKey, bool isHostComboStateChanged, bool *pfResult)
1505{
1506 if (isHostComboStateChanged)
1507 {
1508 if (!m_fHostComboPressed)
1509 {
1510 m_fHostComboPressed = true;
1511 m_bIsHostComboAlone = true;
1512 m_bIsHostComboProcessed = false;
1513 if (isSessionRunning())
1514 saveKeyStates();
1515 }
1516 }
1517 else
1518 {
1519 if (m_fHostComboPressed)
1520 {
1521 if (m_bIsHostComboAlone)
1522 {
1523 m_bIsHostComboAlone = false;
1524 m_bIsHostComboProcessed = true;
1525 /* Process Host+<key> shortcuts.
1526 * Currently, <key> is limited to alphanumeric chars.
1527 * Other Host+<key> combinations are handled in Qt event(): */
1528 *pfResult = processHotKey(iKey, pUniKey);
1529 return true;
1530 }
1531 }
1532 }
1533 return false;
1534}
1535
1536/**
1537 * Handle a key event that releases the host key combination
1538 */
1539void UIKeyboardHandler::keyEventHandleHostComboRelease(ulong uScreenId)
1540{
1541 if (m_fHostComboPressed)
1542 {
1543 m_fHostComboPressed = false;
1544 /* Capturing/releasing keyboard/mouse if necessary: */
1545 if (m_bIsHostComboAlone && !m_bIsHostComboProcessed)
1546 {
1547 if (isSessionRunning())
1548 {
1549 bool ok = true;
1550 if (!m_fKeyboardCaptured)
1551 {
1552 /* Temporarily disable auto-capture that will take place after
1553 * this dialog is dismissed because the capture state is to be
1554 * defined by the dialog result itself: */
1555 setAutoCaptureDisabled(true);
1556 bool fIsAutoConfirmed = false;
1557 ok = msgCenter().confirmInputCapture(fIsAutoConfirmed);
1558 if (fIsAutoConfirmed)
1559 setAutoCaptureDisabled(false);
1560 /* Otherwise, the disable flag will be reset in the next
1561 * machine-view's focus-in event (since may happen asynchronously
1562 * on some platforms, after we return from this code): */
1563 }
1564 if (ok)
1565 {
1566 /* Determine whether the mouse can be captured: */
1567 bool fCaptureMouse = !uimachine()->isMouseSupportsAbsolute()
1568 || !uimachine()->isMouseIntegrated();
1569
1570 if (m_fKeyboardCaptured)
1571 {
1572 releaseKeyboard();
1573 if (fCaptureMouse)
1574 machineLogic()->mouseHandler()->releaseMouse();
1575 }
1576 else
1577 {
1578 captureKeyboard(uScreenId);
1579#ifdef VBOX_WS_NIX
1580 /* Make sure that pending FocusOut events from the
1581 * previous message box are handled, otherwise the
1582 * mouse is immediately ungrabbed: */
1583 /// @todo Is that really needed?
1584 qApp->processEvents();
1585#endif /* VBOX_WS_NIX */
1586 finaliseCaptureKeyboard();
1587 if (fCaptureMouse)
1588 {
1589 const MouseCapturePolicy mcp = gEDataManager->mouseCapturePolicy(uiCommon().managedVMUuid());
1590 if (mcp == MouseCapturePolicy_Default || mcp == MouseCapturePolicy_HostComboOnly)
1591 machineLogic()->mouseHandler()->captureMouse(uScreenId);
1592 }
1593 }
1594 }
1595 }
1596 }
1597 if (isSessionRunning())
1598 sendChangedKeyStates();
1599 }
1600}
1601
1602bool UIKeyboardHandler::keyEvent(int iKey, uint8_t uScan, int fFlags, ulong uScreenId, wchar_t *pUniKey /* = 0 */)
1603{
1604 /* Get host-combo key list: */
1605 QList<int> allHostComboKeys = UIHostCombo::toKeyCodeList(gEDataManager->hostKeyCombination());
1606
1607 /* Update the map of pressed host-combo keys: */
1608 if (allHostComboKeys.contains(iKey))
1609 {
1610 if (fFlags & KeyPressed)
1611 {
1612 if (!m_pressedHostComboKeys.contains(iKey))
1613 m_pressedHostComboKeys.insert(iKey, uScan);
1614 else if (m_fHostComboPressed)
1615 return true;
1616 }
1617 else
1618 {
1619 if (m_pressedHostComboKeys.contains(iKey))
1620 m_pressedHostComboKeys.remove(iKey);
1621 }
1622 }
1623
1624 /* Check if we are currently holding FULL host-combo: */
1625 bool fIsFullHostComboPresent = false;
1626 if (!allHostComboKeys.isEmpty())
1627 {
1628 const QList<int> &pressedKeyList = m_pressedHostComboKeys.keys();
1629#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
1630 fIsFullHostComboPresent = QSet<int>(allHostComboKeys.begin(), allHostComboKeys.end())
1631 == QSet<int>(pressedKeyList.begin(), pressedKeyList.end());
1632#else
1633 fIsFullHostComboPresent = allHostComboKeys.toSet() == pressedKeyList.toSet();
1634#endif
1635 }
1636
1637 /* Check if currently pressed/released key had changed host-combo state: */
1638 const bool isHostComboStateChanged = (!m_fHostComboPressed && fIsFullHostComboPresent) ||
1639 ( m_fHostComboPressed && !fIsFullHostComboPresent);
1640
1641#ifdef VBOX_WS_WIN
1642 if (m_fHostComboPressed || isHostComboStateChanged)
1643 {
1644 /* Currently this is used in winKeyboardEvent() only: */
1645 m_fIsHostkeyInCapture = m_fKeyboardCaptured;
1646 }
1647#endif /* VBOX_WS_WIN */
1648
1649 if (keyEventCADHandled(uScan))
1650 return true;
1651
1652 /* Preparing the press/release scan-codes array for sending to the guest:
1653 * 1. if host-combo is NOT pressed, taking into account currently pressed key too,
1654 * 2. if currently released key releases host-combo too.
1655 * Using that rule, we are NOT sending to the guest:
1656 * 1. the last key-press of host-combo,
1657 * 2. all keys pressed while the host-combo being held (but we still send releases). */
1658 LONG aCodesBuffer[16];
1659 LONG *pCodes = aCodesBuffer;
1660 uint uCodesCount = 0;
1661 uint8_t uWhatPressed = fFlags & KeyExtended ? IsExtKeyPressed : IsKeyPressed;
1662 if ((!m_fHostComboPressed && !isHostComboStateChanged) ||
1663 ( m_fHostComboPressed && isHostComboStateChanged) ||
1664 (!(fFlags & KeyPressed) && (m_pressedKeys[uScan] & uWhatPressed)))
1665 {
1666 /* Special flags handling (KeyPrint): */
1667 if (fFlags & KeyPrint)
1668 {
1669 if (fFlags & KeyPressed)
1670 {
1671 static LONG PrintMake[] = { 0xE0, 0x37 };
1672 pCodes = PrintMake;
1673 uCodesCount = RT_ELEMENTS(PrintMake);
1674 }
1675 else
1676 {
1677 static LONG PrintBreak[] = { 0xE0, 0xB7 };
1678 pCodes = PrintBreak;
1679 uCodesCount = RT_ELEMENTS(PrintBreak);
1680 }
1681 }
1682 /* Special flags handling (KeyPause): */
1683 else if (fFlags & KeyPause)
1684 {
1685 if (fFlags & KeyPressed)
1686 {
1687 static LONG Pause[] = { 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 };
1688 pCodes = Pause;
1689 uCodesCount = RT_ELEMENTS(Pause);
1690 }
1691 else
1692 {
1693 /* Pause shall not produce a break code: */
1694 return true;
1695 }
1696 }
1697 /* Common flags handling: */
1698 else
1699 if (keyEventHandleNormal(iKey, uScan, fFlags, pCodes, &uCodesCount))
1700 return true;
1701 }
1702
1703 /* Process the host-combo funtionality: */
1704 if (fFlags & KeyPressed)
1705 {
1706 bool fResult;
1707 if (keyEventHostComboHandled(iKey, pUniKey, isHostComboStateChanged, &fResult))
1708 return fResult;
1709 }
1710 else
1711 {
1712 if (isHostComboStateChanged)
1713 keyEventHandleHostComboRelease(uScreenId);
1714 else
1715 {
1716 if (m_fHostComboPressed)
1717 m_bIsHostComboAlone = true;
1718 }
1719 }
1720
1721 /* Notify all the listeners: */
1722 emit sigStateChange(state());
1723
1724 /* If the VM is NOT paused: */
1725 if (!uimachine()->isPaused())
1726 {
1727 /* If there are scan-codes to send: */
1728 if (uCodesCount)
1729 {
1730 /* Send prepared scan-codes to the guest: */
1731 QVector<LONG> scancodes;
1732 for (uint i = 0; i < uCodesCount; ++i)
1733 scancodes.append(pCodes[i]);
1734 uimachine()->putScancodes(scancodes);
1735 }
1736
1737 /* If full host-key sequence was just finalized: */
1738 if (isHostComboStateChanged && m_fHostComboPressed)
1739 {
1740 /* We have to make guest to release pressed keys from the host-combination: */
1741 foreach (const uint8_t &uCurrentScan, m_pressedHostComboKeys.values())
1742 {
1743 if (m_pressedKeys[uCurrentScan] & IsKeyPressed)
1744 {
1745 uimachine()->putScancode(uCurrentScan | 0x80);
1746 }
1747 else if (m_pressedKeys[uCurrentScan] & IsExtKeyPressed)
1748 {
1749 QVector<LONG> scancodes(2);
1750 scancodes[0] = 0xE0;
1751 scancodes[1] = uCurrentScan | 0x80;
1752 uimachine()->putScancodes(scancodes);
1753 }
1754 m_pressedKeys[uCurrentScan] = 0;
1755 }
1756 }
1757 }
1758
1759 /* Prevent the key from going to Qt: */
1760 return true;
1761}
1762
1763bool UIKeyboardHandler::processHotKey(int iHotKey, wchar_t *pHotKey)
1764{
1765 /* Prepare processing result: */
1766 bool fWasProcessed = false;
1767
1768#if defined(VBOX_WS_MAC)
1769
1770 Q_UNUSED(iHotKey);
1771 if (pHotKey && pHotKey[0] && !pHotKey[1])
1772 fWasProcessed = actionPool()->processHotKey(QKeySequence(QChar(pHotKey[0]).toUpper().unicode()));
1773
1774#elif defined(VBOX_WS_WIN)
1775
1776 Q_UNUSED(pHotKey);
1777 int iKeyboardLayout = GetKeyboardLayoutList(0, NULL);
1778 Assert(iKeyboardLayout);
1779 HKL *pList = new HKL[iKeyboardLayout];
1780 GetKeyboardLayoutList(iKeyboardLayout, pList);
1781 for (int i = 0; i < iKeyboardLayout && !fWasProcessed; ++i)
1782 {
1783 wchar_t symbol;
1784 static BYTE keys[256] = {0};
1785 if (!ToUnicodeEx(iHotKey, 0, keys, &symbol, 1, 0, pList[i]) == 1)
1786 symbol = 0;
1787 if (symbol)
1788 fWasProcessed = actionPool()->processHotKey(QKeySequence(QChar(symbol).toUpper().unicode()));
1789 }
1790 delete[] pList;
1791
1792#elif defined(VBOX_WS_NIX)
1793 if (uiCommon().X11ServerAvailable())
1794 {
1795 Q_UNUSED(pHotKey);
1796 Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
1797 KeyCode keyCode = XKeysymToKeycode(pDisplay, iHotKey);
1798 for (int i = 0; i < 4 && !fWasProcessed; ++i) /* Up to four groups. */
1799 {
1800 KeySym ks = wrapXkbKeycodeToKeysym(pDisplay, keyCode, i, 0);
1801 char symbol = 0;
1802 if (XkbTranslateKeySym(pDisplay, &ks, 0, &symbol, 1, NULL) == 0)
1803 symbol = 0;
1804 if (symbol)
1805 {
1806 QChar qtSymbol = QString::fromLocal8Bit(&symbol, 1)[0];
1807 fWasProcessed = actionPool()->processHotKey(QKeySequence(qtSymbol.toUpper().unicode()));
1808 }
1809 }
1810 }
1811
1812#else
1813
1814# warning "port me!"
1815
1816#endif
1817
1818 /* Grab the key from the Qt if it was processed, or pass it to the Qt otherwise
1819 * in order to process non-alphanumeric keys in event(), after they are converted to Qt virtual keys: */
1820 return fWasProcessed;
1821}
1822
1823void UIKeyboardHandler::fixModifierState(LONG *piCodes, uint *puCount)
1824{
1825 /* Synchronize the views of the host and the guest to the modifier keys.
1826 * This function will add up to 6 additional keycodes to codes. */
1827
1828#if defined(VBOX_WS_MAC)
1829
1830 /* if (uimachine()->numLockAdaptionCnt()) ... - NumLock isn't implemented by Mac OS X so ignore it. */
1831 if (uimachine()->capsLockAdaptionCnt() && (uimachine()->isCapsLock() ^ !!(::GetCurrentEventKeyModifiers() & alphaLock)))
1832 {
1833 uimachine()->setCapsLockAdaptionCnt(uimachine()->capsLockAdaptionCnt() - 1);
1834 piCodes[(*puCount)++] = 0x3a;
1835 piCodes[(*puCount)++] = 0x3a | 0x80;
1836 /* Some keyboard layouts require shift to be pressed to break
1837 * capslock. For simplicity, only do this if shift is not
1838 * already held down. */
1839 if (uimachine()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))
1840 {
1841 piCodes[(*puCount)++] = 0x2a;
1842 piCodes[(*puCount)++] = 0x2a | 0x80;
1843 }
1844 }
1845
1846#elif defined(VBOX_WS_WIN)
1847
1848 if (uimachine()->numLockAdaptionCnt() && (uimachine()->isNumLock() ^ !!(GetKeyState(VK_NUMLOCK))))
1849 {
1850 uimachine()->setNumLockAdaptionCnt(uimachine()->numLockAdaptionCnt() - 1);
1851 piCodes[(*puCount)++] = 0x45;
1852 piCodes[(*puCount)++] = 0x45 | 0x80;
1853 }
1854 if (uimachine()->capsLockAdaptionCnt() && (uimachine()->isCapsLock() ^ !!(GetKeyState(VK_CAPITAL))))
1855 {
1856 uimachine()->setCapsLockAdaptionCnt(uimachine()->capsLockAdaptionCnt() - 1);
1857 piCodes[(*puCount)++] = 0x3a;
1858 piCodes[(*puCount)++] = 0x3a | 0x80;
1859 /* Some keyboard layouts require shift to be pressed to break
1860 * capslock. For simplicity, only do this if shift is not
1861 * already held down. */
1862 if (uimachine()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))
1863 {
1864 piCodes[(*puCount)++] = 0x2a;
1865 piCodes[(*puCount)++] = 0x2a | 0x80;
1866 }
1867 }
1868
1869#elif defined(VBOX_WS_NIX)
1870 if (uiCommon().X11ServerAvailable())
1871 {
1872 Window wDummy1, wDummy2;
1873 int iDummy3, iDummy4, iDummy5, iDummy6;
1874 unsigned uMask;
1875 unsigned uKeyMaskNum = 0, uKeyMaskCaps = 0;
1876 Display * const pDisplay = NativeWindowSubsystem::X11GetDisplay();
1877
1878 uKeyMaskCaps = LockMask;
1879 XModifierKeymap* map = XGetModifierMapping(pDisplay);
1880 KeyCode keyCodeNum = XKeysymToKeycode(pDisplay, XK_Num_Lock);
1881
1882 for (int i = 0; i < 8; ++ i)
1883 if (keyCodeNum != NoSymbol && map->modifiermap[map->max_keypermod * i] == keyCodeNum)
1884 uKeyMaskNum = 1 << i;
1885 XQueryPointer(pDisplay, DefaultRootWindow(pDisplay), &wDummy1, &wDummy2,
1886 &iDummy3, &iDummy4, &iDummy5, &iDummy6, &uMask);
1887 XFreeModifiermap(map);
1888
1889 if (uimachine()->numLockAdaptionCnt() && (uimachine()->isNumLock() ^ !!(uMask & uKeyMaskNum)))
1890 {
1891 uimachine()->setNumLockAdaptionCnt(uimachine()->numLockAdaptionCnt() - 1);
1892 piCodes[(*puCount)++] = 0x45;
1893 piCodes[(*puCount)++] = 0x45 | 0x80;
1894 }
1895 if (uimachine()->capsLockAdaptionCnt() && (uimachine()->isCapsLock() ^ !!(uMask & uKeyMaskCaps)))
1896 {
1897 uimachine()->setCapsLockAdaptionCnt(uimachine()->capsLockAdaptionCnt() - 1);
1898 piCodes[(*puCount)++] = 0x3a;
1899 piCodes[(*puCount)++] = 0x3a | 0x80;
1900 /* Some keyboard layouts require shift to be pressed to break
1901 * capslock. For simplicity, only do this if shift is not
1902 * already held down. */
1903 if (uimachine()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))
1904 {
1905 piCodes[(*puCount)++] = 0x2a;
1906 piCodes[(*puCount)++] = 0x2a | 0x80;
1907 }
1908 }
1909 }
1910#else
1911
1912# warning "port me!"
1913
1914#endif
1915}
1916
1917void UIKeyboardHandler::saveKeyStates()
1918{
1919 ::memcpy(m_pressedKeysCopy, m_pressedKeys, sizeof(m_pressedKeys));
1920}
1921
1922void UIKeyboardHandler::sendChangedKeyStates()
1923{
1924 QVector <LONG> codes(2);
1925 for (uint i = 0; i < RT_ELEMENTS(m_pressedKeys); ++ i)
1926 {
1927 uint8_t os = m_pressedKeysCopy[i];
1928 uint8_t ns = m_pressedKeys[i];
1929 if ((os & IsKeyPressed) != (ns & IsKeyPressed))
1930 {
1931 codes[0] = i;
1932 if (!(ns & IsKeyPressed))
1933 codes[0] |= 0x80;
1934 uimachine()->putScancode(codes[0]);
1935 }
1936 else if ((os & IsExtKeyPressed) != (ns & IsExtKeyPressed))
1937 {
1938 codes[0] = 0xE0;
1939 codes[1] = i;
1940 if (!(ns & IsExtKeyPressed))
1941 codes[1] |= 0x80;
1942 uimachine()->putScancodes(codes);
1943 }
1944 }
1945}
1946
1947bool UIKeyboardHandler::isAutoCaptureDisabled()
1948{
1949 return uimachine()->isAutoCaptureDisabled();
1950}
1951
1952void UIKeyboardHandler::setAutoCaptureDisabled(bool fIsAutoCaptureDisabled)
1953{
1954 uimachine()->setAutoCaptureDisabled(fIsAutoCaptureDisabled);
1955}
1956
1957bool UIKeyboardHandler::autoCaptureSetGlobally()
1958{
1959 return gEDataManager->autoCaptureEnabled() && !m_fDebuggerActive;
1960}
1961
1962bool UIKeyboardHandler::viewHasFocus(ulong uScreenId)
1963{
1964 return m_views[uScreenId]->hasFocus();
1965}
1966
1967bool UIKeyboardHandler::isSessionRunning()
1968{
1969 return uimachine()->isRunning();
1970}
1971
1972bool UIKeyboardHandler::isSessionStuck()
1973{
1974 return uimachine()->isStuck();
1975}
1976
1977UIMachineWindow* UIKeyboardHandler::isItListenedWindow(QObject *pWatchedObject) const
1978{
1979 UIMachineWindow *pResultWindow = 0;
1980 QMap<ulong, UIMachineWindow*>::const_iterator i = m_windows.constBegin();
1981 while (!pResultWindow && i != m_windows.constEnd())
1982 {
1983 UIMachineWindow *pIteratedWindow = i.value();
1984 if (pIteratedWindow == pWatchedObject)
1985 {
1986 pResultWindow = pIteratedWindow;
1987 continue;
1988 }
1989 ++i;
1990 }
1991 return pResultWindow;
1992}
1993
1994UIMachineView* UIKeyboardHandler::isItListenedView(QObject *pWatchedObject) const
1995{
1996 UIMachineView *pResultView = 0;
1997 QMap<ulong, UIMachineView*>::const_iterator i = m_views.constBegin();
1998 while (!pResultView && i != m_views.constEnd())
1999 {
2000 UIMachineView *pIteratedView = i.value();
2001 if (pIteratedView == pWatchedObject)
2002 {
2003 pResultView = pIteratedView;
2004 continue;
2005 }
2006 ++i;
2007 }
2008 return pResultView;
2009}
2010
2011void UIKeyboardHandler::setHostKeyComboPressedFlag(bool bPressed)
2012{
2013 m_fHostKeyComboPressInserted = bPressed;
2014 emit sigStateChange(state());
2015}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use