VirtualBox

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

Last change on this file was 104393, checked in by vboxsync, 13 days ago

FE/Qt. bugref:10622. Using new UITranslationEventListener in the UIActionPool class.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use