VirtualBox

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

Last change on this file since 100064 was 100064, checked in by vboxsync, 11 months ago

FE/Qt: bugref:10421. Replacing VBOX_WS_X11 with VBOX_WS_NIX.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 75.5 KB
Line 
1/* $Id: UIKeyboardHandler.cpp 100064 2023-06-04 09:10:01Z 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 if (!m_keyboardHook || (int)uScreenId != m_iKeyboardHookViewIndex)
1148 {
1149 /* If keyboard-hook is installed: */
1150 if (m_keyboardHook)
1151 {
1152 /* Uninstall existing keyboard-hook: */
1153 UnhookWindowsHookEx(m_keyboardHook);
1154 m_keyboardHook = 0;
1155 }
1156 /* Install new keyboard-hook: */
1157 m_keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, UIKeyboardHandler::winKeyboardProc, GetModuleHandle(NULL), 0);
1158 AssertMsg(m_keyboardHook, ("SetWindowsHookEx(): err=%d", GetLastError()));
1159 }
1160
1161#endif /* VBOX_WS_WIN */
1162
1163 /* Update keyboard hook view index: */
1164 m_iKeyboardHookViewIndex = uScreenId;
1165
1166 if (isSessionRunning())
1167 {
1168 /* Capture keyboard: */
1169#ifdef VBOX_WS_WIN
1170 if (!isAutoCaptureDisabled() && autoCaptureSetGlobally() &&
1171 GetAncestor((HWND)pWatchedView->winId(), GA_ROOT) == GetForegroundWindow())
1172#else /* !VBOX_WS_WIN */
1173 if (!isAutoCaptureDisabled() && autoCaptureSetGlobally())
1174#endif /* !VBOX_WS_WIN */
1175 captureKeyboard(uScreenId);
1176 /* Reset the single-time disable capture flag: */
1177 if (isAutoCaptureDisabled())
1178 setAutoCaptureDisabled(false);
1179 }
1180
1181 break;
1182 }
1183 case QEvent::FocusOut:
1184 {
1185 /* If host key combo press has been inserted (with no release yet) insert a release now: */
1186 if (m_fHostKeyComboPressInserted)
1187 machineLogic()->typeHostKeyComboPressRelease(false);
1188
1189#if defined(VBOX_WS_MAC)
1190
1191 /* If keyboard-hook is installed: */
1192 if ((int)uScreenId == m_iKeyboardHookViewIndex)
1193 {
1194 /* Ungrab the keyboard and unregister the event callback/hook: */
1195 ::DarwinReleaseKeyboard();
1196 UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */,
1197 UIKeyboardHandler::macKeyboardProc, this);
1198 }
1199
1200#elif defined(VBOX_WS_WIN)
1201
1202 /* If keyboard-hook is installed: */
1203 if (m_keyboardHook)
1204 {
1205 /* Uninstall existing keyboard-hook: */
1206 UnhookWindowsHookEx(m_keyboardHook);
1207 m_keyboardHook = 0;
1208 }
1209
1210#endif /* VBOX_WS_WIN */
1211
1212 /* Update keyboard hook view index: */
1213 m_iKeyboardHookViewIndex = -1;
1214
1215 /* Release keyboard: */
1216 if (isSessionRunning() || isSessionStuck())
1217 releaseKeyboard();
1218 /* And all pressed keys: */
1219 releaseAllPressedKeys(true);
1220
1221 break;
1222 }
1223 case QEvent::KeyPress:
1224 case QEvent::KeyRelease:
1225 {
1226 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);
1227
1228 if (m_fHostComboPressed && pEvent->type() == QEvent::KeyPress)
1229 {
1230 /* Passing F1-F12 keys to the guest: */
1231 if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F12)
1232 {
1233 QVector <LONG> combo(6);
1234 combo[0] = 0x1d; /* Ctrl down */
1235 combo[1] = 0x38; /* Alt down */
1236 combo[4] = 0xb8; /* Alt up */
1237 combo[5] = 0x9d; /* Ctrl up */
1238 if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F10)
1239 {
1240 combo[2] = 0x3b + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 down */
1241 combo[3] = 0xbb + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 up */
1242 }
1243 /* There is some scan slice between F10 and F11 keys, so its separated: */
1244 else if (pKeyEvent->key() >= Qt::Key_F11 && pKeyEvent->key() <= Qt::Key_F12)
1245 {
1246 combo[2] = 0x57 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 down */
1247 combo[3] = 0xd7 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 up */
1248 }
1249 uimachine()->putScancodes(combo);
1250 }
1251 /* Process hot keys not processed in keyEvent() (as in case of non-alphanumeric keys): */
1252 actionPool()->processHotKey(QKeySequence(pKeyEvent->key()));
1253 }
1254 else if (!m_fHostComboPressed && pEvent->type() == QEvent::KeyRelease)
1255 {
1256 /* Show a possible warning on key release which seems to be more expected by the end user: */
1257 if (uimachine()->isPaused())
1258 UINotificationMessage::remindAboutPausedVMInput();
1259 }
1260
1261 break;
1262 }
1263 default:
1264 break;
1265 }
1266 }
1267
1268 /* Else just propagate to base-class: */
1269 return QObject::eventFilter(pWatchedObject, pEvent);
1270}
1271
1272#if defined(VBOX_WS_MAC)
1273
1274/* static */
1275bool UIKeyboardHandler::macKeyboardProc(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser)
1276{
1277 /* Determine the event class: */
1278 EventRef event = (EventRef)pvCarbonEvent;
1279 UInt32 uEventClass = ::GetEventClass(event);
1280
1281 /* Check if this is an application key combo. In that case we will not pass
1282 * the event to the guest, but let the host process it. */
1283 if (::darwinIsApplicationCommand(pvCocoaEvent))
1284 return false;
1285
1286 /* Get the keyboard handler from the user's void data: */
1287 UIKeyboardHandler *pKeyboardHandler = static_cast<UIKeyboardHandler*>(pvUser);
1288
1289 /* All keyboard class events needs to be handled: */
1290 if (uEventClass == kEventClassKeyboard && pKeyboardHandler && pKeyboardHandler->macKeyboardEvent(pvCocoaEvent, event))
1291 return true;
1292
1293 /* Pass the event along: */
1294 return false;
1295}
1296
1297bool UIKeyboardHandler::macKeyboardEvent(const void *pvCocoaEvent, EventRef event)
1298{
1299 /* Check what related machine-view was NOT unregistered yet: */
1300 if (!m_views.contains(m_iKeyboardHookViewIndex))
1301 return false;
1302
1303 /* Pass event to machine-view's event handler: */
1304 Q_UNUSED(event);
1305 return nativeEventFilter(unconst(pvCocoaEvent), m_iKeyboardHookViewIndex);
1306}
1307
1308#elif defined(VBOX_WS_WIN)
1309
1310/* static */
1311LRESULT CALLBACK UIKeyboardHandler::winKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) RT_NOTHROW_DEF
1312{
1313 /* All keyboard class events needs to be handled: */
1314 if (nCode == HC_ACTION && m_spKeyboardHandler && m_spKeyboardHandler->winKeyboardEvent(wParam, *(KBDLLHOOKSTRUCT*)lParam))
1315 return 1;
1316
1317 /* Pass the event along: */
1318 return CallNextHookEx(NULL, nCode, wParam, lParam);
1319}
1320
1321bool UIKeyboardHandler::winKeyboardEvent(UINT msg, const KBDLLHOOKSTRUCT &event)
1322{
1323 /* Check that related machine-view was NOT unregistered yet: */
1324 if (!m_views.contains(m_iKeyboardHookViewIndex))
1325 return false;
1326
1327 /* HACK ALERT! Check that we're not in cleanup, as we're using gEDataManger
1328 to get host key combinations and it as probably been cleaned up already.
1329 We don't want to cause it to re-instantiate, with all the COM traffic
1330 that might involve. Sample assertion stack (IPRT not windbg, sorry):
1331
1332 !!Assertion Failed!!
1333 Expression: mRC != RPC_E_CANTCALLOUT_ININPUTSYNCCALL
1334 Location : E:\vbox\svn\trunk\out\win.amd64\debug\obj\UICommon\include\COMWrappers.cpp(3857) class QVector<class QString> __cdecl CVirtualBox::GetExtraDataKeys(void)
1335 Stack :
1336 00007fff39aa6634 VBoxRT.dll!RTAssertMsg1+0x274 (rva:0x246634)
1337 [E:\vbox\svn\trunk\src\VBox\Runtime\common\misc\assert.cpp:159]
1338 00007fff39aa542f VBoxRT.dll!RTAssertMsg1Weak+0x2f (rva:0x24542f)
1339 [E:\vbox\svn\trunk\src\VBox\Runtime\common\misc\RTAssertMsg1Weak.cpp:40 (+0x0)]
1340 00007fff36e5c00f UICommon.dll!CVirtualBox::GetExtraDataKeys+0x23f (rva:0x3fc00f)
1341 [E:\vbox\svn\trunk\out\win.amd64\debug\obj\UICommon\include\COMWrappers.cpp:3857]
1342 00007fff36ac2cc9 UICommon.dll!UIExtraDataManager::prepareGlobalExtraDataMap+0xb9 (rva:0x62cc9)
1343 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\extradata\UIExtraDataManager.cpp:4845]
1344 00007fff36ac2bf8 UICommon.dll!UIExtraDataManager::prepare+0x28 (rva:0x62bf8)
1345 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\extradata\UIExtraDataManager.cpp:4833 (+0x0)]
1346 00007fff36ab1896 UICommon.dll!UIExtraDataManager::instance+0x66 (rva:0x51896)
1347 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\extradata\UIExtraDataManager.cpp:2011 (+0x0)]
1348 00007ff771b05b06 VirtualBoxVM.exe!UIKeyboardHandler::winKeyboardEvent+0xe6 (rva:0x35b06)
1349 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\runtime\UIKeyboardHandler.cpp:1324]
1350 00007ff771b059ec VirtualBoxVM.exe!UIKeyboardHandler::winKeyboardProc+0x4c (rva:0x359ec)
1351 [E:\vbox\svn\trunk\src\VBox\Frontends\VirtualBox\src\runtime\UIKeyboardHandler.cpp:1304]
1352 00007fff763311f2 USER32.dll!GetDlgCtrlID+0x232 (rva:0x211f2)
1353 00007fff7639ac89 USER32.dll!CreateSystemThreads+0xa29 (rva:0x8ac89)
1354 00007fff76b30ba4 ntdll.dll!KiUserCallbackDispatcher+0x24 (rva:0xa0ba4)
1355 00007fff749b1064 win32u.dll!NtUserPeekMessage+0x14 (rva:0x1064)
1356 00007fff7631a553 USER32.dll!PeekMessageW+0x1e3 (rva:0xa553)
1357 00007fff7631a4b3 USER32.dll!PeekMessageW+0x143 (rva:0xa4b3)
1358 00007e11000270dc ConEmuHk64.dll!SetLoadLibraryCallback+0xc3ac (rva:0x270dc)
1359 00007fff759be71b combase.dll!CoRegisterPSClsid+0x82b (rva:0x6e71b)
1360 00007fff759be685 combase.dll!CoRegisterPSClsid+0x795 (rva:0x6e685)
1361 00007fff759bcec1 combase.dll!Ordinal87+0x2851 (rva:0x6cec1)
1362 00007fff759bcbbb combase.dll!Ordinal87+0x254b (rva:0x6cbbb)
1363 00007fff75994956 combase.dll!RoGetApartmentIdentifier+0x55f6 (rva:0x44956)
1364 cccccccccccccccc */
1365 if (UICommon::instance()->isCleaningUp())
1366 return false;
1367
1368 if (!m_fKeyboardCaptured)
1369 return false;
1370
1371 /* It's possible that a key has been pressed while the keyboard was not
1372 * captured, but is being released under the capture. Detect this situation
1373 * and do not pass on the key press to the virtual machine. */
1374 uint8_t what_pressed = (event.flags & 0x01)
1375 && (event.vkCode != VK_RSHIFT)
1376 ? IsExtKeyPressed : IsKeyPressed;
1377 if ( (event.flags & 0x80) /* released */
1378 && ( ( UIHostCombo::toKeyCodeList(gEDataManager->hostKeyCombination()).contains((int)event.vkCode)
1379 && !m_fIsHostkeyInCapture)
1380 || ( m_pressedKeys[event.scanCode & 0x7F]
1381 & (IsKbdCaptured | what_pressed))
1382 == what_pressed))
1383 return false;
1384
1385 /* For normal user applications, Windows defines AltGr to be the same as
1386 * LControl + RAlt. Without a low-level hook it is hard to recognise the
1387 * additional LControl event inserted, but in a hook we recognise it by
1388 * its special 0x21D scan code. */
1389 if ( m_views[m_iKeyboardHookViewIndex]->hasFocus()
1390 && ((event.scanCode & ~0x80) == 0x21D))
1391 return true;
1392
1393 /* Compose the MSG: */
1394 MSG message;
1395 message.hwnd = (HWND)m_views[m_iKeyboardHookViewIndex]->winId();
1396 message.message = msg;
1397 message.wParam = event.vkCode;
1398 message.lParam = 1 | (event.scanCode & 0xFF) << 16 | (event.flags & 0xFF) << 24;
1399
1400 /* Windows sets here the extended bit when the Right Shift key is pressed,
1401 * which is totally wrong. Undo it. */
1402 if (event.vkCode == VK_RSHIFT)
1403 message.lParam &= ~0x1000000;
1404
1405 /* Pass event to view's event handler: */
1406 return nativeEventFilter(&message, m_iKeyboardHookViewIndex);
1407}
1408
1409#endif /* VBOX_WS_WIN */
1410
1411/**
1412 * If the user has just completed a control-alt-del combination then handle
1413 * that.
1414 * @returns true if handling should stop here, false otherwise
1415 */
1416bool UIKeyboardHandler::keyEventCADHandled(uint8_t uScan)
1417{
1418 /* Check if it's C-A-D and GUI/PassCAD is not set/allowed: */
1419 if (!m_fPassCADtoGuest &&
1420 uScan == 0x53 /* Del */ &&
1421 ((m_pressedKeys[0x38] & IsKeyPressed) /* Alt */ ||
1422 (m_pressedKeys[0x38] & IsExtKeyPressed)) &&
1423 ((m_pressedKeys[0x1d] & IsKeyPressed) /* Ctrl */ ||
1424 (m_pressedKeys[0x1d] & IsExtKeyPressed)))
1425 {
1426 /* Use the C-A-D combination as a last resort to get the keyboard and mouse back
1427 * to the host when the user forgets the Host Key. Note that it's always possible
1428 * to send C-A-D to the guest using the Host+Del combination: */
1429 if (isSessionRunning() && m_fKeyboardCaptured)
1430 {
1431 releaseKeyboard();
1432 if (!uimachine()->isMouseSupportsAbsolute() || !uimachine()->isMouseIntegrated())
1433 machineLogic()->mouseHandler()->releaseMouse();
1434 }
1435 return true;
1436 }
1437 return false;
1438}
1439
1440/**
1441 * Handle a non-special (C-A-D, pause, print) key press or release
1442 * @returns true if handling should stop here, false otherwise
1443 */
1444bool UIKeyboardHandler::keyEventHandleNormal(int iKey, uint8_t uScan, int fFlags, LONG *pCodes, uint *puCodesCount)
1445{
1446 /* Get the type of key - simple or extended: */
1447 uint8_t uWhatPressed = fFlags & KeyExtended ? IsExtKeyPressed : IsKeyPressed;
1448
1449 /* If some key was pressed or some previously pressed key was released =>
1450 * we are updating the list of pressed keys and preparing scan codes: */
1451 if ((fFlags & KeyPressed) || (m_pressedKeys[uScan] & uWhatPressed))
1452 {
1453 /* If HID LEDs sync is disabled or not supported, check if the guest has the
1454 * same view on the modifier keys (NumLock, CapsLock, ScrollLock) as the host. */
1455 if (!uimachine()->isHidLedsSyncEnabled())
1456 if (fFlags & KeyPressed)
1457 fixModifierState(pCodes, puCodesCount);
1458
1459 /* Prepend 'extended' scan code if needed: */
1460 if (fFlags & KeyExtended)
1461 pCodes[(*puCodesCount)++] = 0xE0;
1462
1463 /* Process key-press: */
1464 if (fFlags & KeyPressed)
1465 {
1466 /* Append scan code: */
1467 pCodes[(*puCodesCount)++] = uScan;
1468 m_pressedKeys[uScan] |= uWhatPressed;
1469 }
1470 /* Process key-release if that key was pressed before: */
1471 else if (m_pressedKeys[uScan] & uWhatPressed)
1472 {
1473 /* Append scan code: */
1474 pCodes[(*puCodesCount)++] = uScan | 0x80;
1475 m_pressedKeys[uScan] &= ~uWhatPressed;
1476 }
1477
1478 /* Update keyboard-captured flag: */
1479 if (m_fKeyboardCaptured)
1480 m_pressedKeys[uScan] |= IsKbdCaptured;
1481 else
1482 m_pressedKeys[uScan] &= ~IsKbdCaptured;
1483 }
1484 /* Ignore key-release if that key was NOT pressed before,
1485 * but only if thats not one of the host-combination keys: */
1486 else
1487 {
1488 /* Get host-combo key list: */
1489 QList<int> lstAllHostComboKey = UIHostCombo::toKeyCodeList(gEDataManager->hostKeyCombination());
1490 if (!lstAllHostComboKey.contains(iKey))
1491 return true;
1492 }
1493 return false;
1494}
1495
1496/**
1497 * Check whether the key pressed results in a host key combination being
1498 * handled.
1499 * @returns true if a combination was handled, false otherwise
1500 * @param pfResult where to store the result of the handling
1501 */
1502bool UIKeyboardHandler::keyEventHostComboHandled(int iKey, wchar_t *pUniKey, bool isHostComboStateChanged, bool *pfResult)
1503{
1504 if (isHostComboStateChanged)
1505 {
1506 if (!m_fHostComboPressed)
1507 {
1508 m_fHostComboPressed = true;
1509 m_bIsHostComboAlone = true;
1510 m_bIsHostComboProcessed = false;
1511 if (isSessionRunning())
1512 saveKeyStates();
1513 }
1514 }
1515 else
1516 {
1517 if (m_fHostComboPressed)
1518 {
1519 if (m_bIsHostComboAlone)
1520 {
1521 m_bIsHostComboAlone = false;
1522 m_bIsHostComboProcessed = true;
1523 /* Process Host+<key> shortcuts.
1524 * Currently, <key> is limited to alphanumeric chars.
1525 * Other Host+<key> combinations are handled in Qt event(): */
1526 *pfResult = processHotKey(iKey, pUniKey);
1527 return true;
1528 }
1529 }
1530 }
1531 return false;
1532}
1533
1534/**
1535 * Handle a key event that releases the host key combination
1536 */
1537void UIKeyboardHandler::keyEventHandleHostComboRelease(ulong uScreenId)
1538{
1539 if (m_fHostComboPressed)
1540 {
1541 m_fHostComboPressed = false;
1542 /* Capturing/releasing keyboard/mouse if necessary: */
1543 if (m_bIsHostComboAlone && !m_bIsHostComboProcessed)
1544 {
1545 if (isSessionRunning())
1546 {
1547 bool ok = true;
1548 if (!m_fKeyboardCaptured)
1549 {
1550 /* Temporarily disable auto-capture that will take place after
1551 * this dialog is dismissed because the capture state is to be
1552 * defined by the dialog result itself: */
1553 setAutoCaptureDisabled(true);
1554 bool fIsAutoConfirmed = false;
1555 ok = msgCenter().confirmInputCapture(fIsAutoConfirmed);
1556 if (fIsAutoConfirmed)
1557 setAutoCaptureDisabled(false);
1558 /* Otherwise, the disable flag will be reset in the next
1559 * machine-view's focus-in event (since may happen asynchronously
1560 * on some platforms, after we return from this code): */
1561 }
1562 if (ok)
1563 {
1564 /* Determine whether the mouse can be captured: */
1565 bool fCaptureMouse = !uimachine()->isMouseSupportsAbsolute()
1566 || !uimachine()->isMouseIntegrated();
1567
1568 if (m_fKeyboardCaptured)
1569 {
1570 releaseKeyboard();
1571 if (fCaptureMouse)
1572 machineLogic()->mouseHandler()->releaseMouse();
1573 }
1574 else
1575 {
1576 captureKeyboard(uScreenId);
1577#ifdef VBOX_WS_NIX
1578 /* Make sure that pending FocusOut events from the
1579 * previous message box are handled, otherwise the
1580 * mouse is immediately ungrabbed: */
1581 /// @todo Is that really needed?
1582 qApp->processEvents();
1583#endif /* VBOX_WS_NIX */
1584 finaliseCaptureKeyboard();
1585 if (fCaptureMouse)
1586 {
1587 const MouseCapturePolicy mcp = gEDataManager->mouseCapturePolicy(uiCommon().managedVMUuid());
1588 if (mcp == MouseCapturePolicy_Default || mcp == MouseCapturePolicy_HostComboOnly)
1589 machineLogic()->mouseHandler()->captureMouse(uScreenId);
1590 }
1591 }
1592 }
1593 }
1594 }
1595 if (isSessionRunning())
1596 sendChangedKeyStates();
1597 }
1598}
1599
1600bool UIKeyboardHandler::keyEvent(int iKey, uint8_t uScan, int fFlags, ulong uScreenId, wchar_t *pUniKey /* = 0 */)
1601{
1602 /* Get host-combo key list: */
1603 QList<int> allHostComboKeys = UIHostCombo::toKeyCodeList(gEDataManager->hostKeyCombination());
1604
1605 /* Update the map of pressed host-combo keys: */
1606 if (allHostComboKeys.contains(iKey))
1607 {
1608 if (fFlags & KeyPressed)
1609 {
1610 if (!m_pressedHostComboKeys.contains(iKey))
1611 m_pressedHostComboKeys.insert(iKey, uScan);
1612 else if (m_fHostComboPressed)
1613 return true;
1614 }
1615 else
1616 {
1617 if (m_pressedHostComboKeys.contains(iKey))
1618 m_pressedHostComboKeys.remove(iKey);
1619 }
1620 }
1621
1622 /* Check if we are currently holding FULL host-combo: */
1623 bool fIsFullHostComboPresent = false;
1624 if (!allHostComboKeys.isEmpty())
1625 {
1626 const QList<int> &pressedKeyList = m_pressedHostComboKeys.keys();
1627#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
1628 fIsFullHostComboPresent = QSet<int>(allHostComboKeys.begin(), allHostComboKeys.end())
1629 == QSet<int>(pressedKeyList.begin(), pressedKeyList.end());
1630#else
1631 fIsFullHostComboPresent = allHostComboKeys.toSet() == pressedKeyList.toSet();
1632#endif
1633 }
1634
1635 /* Check if currently pressed/released key had changed host-combo state: */
1636 const bool isHostComboStateChanged = (!m_fHostComboPressed && fIsFullHostComboPresent) ||
1637 ( m_fHostComboPressed && !fIsFullHostComboPresent);
1638
1639#ifdef VBOX_WS_WIN
1640 if (m_fHostComboPressed || isHostComboStateChanged)
1641 {
1642 /* Currently this is used in winKeyboardEvent() only: */
1643 m_fIsHostkeyInCapture = m_fKeyboardCaptured;
1644 }
1645#endif /* VBOX_WS_WIN */
1646
1647 if (keyEventCADHandled(uScan))
1648 return true;
1649
1650 /* Preparing the press/release scan-codes array for sending to the guest:
1651 * 1. if host-combo is NOT pressed, taking into account currently pressed key too,
1652 * 2. if currently released key releases host-combo too.
1653 * Using that rule, we are NOT sending to the guest:
1654 * 1. the last key-press of host-combo,
1655 * 2. all keys pressed while the host-combo being held (but we still send releases). */
1656 LONG aCodesBuffer[16];
1657 LONG *pCodes = aCodesBuffer;
1658 uint uCodesCount = 0;
1659 uint8_t uWhatPressed = fFlags & KeyExtended ? IsExtKeyPressed : IsKeyPressed;
1660 if ((!m_fHostComboPressed && !isHostComboStateChanged) ||
1661 ( m_fHostComboPressed && isHostComboStateChanged) ||
1662 (!(fFlags & KeyPressed) && (m_pressedKeys[uScan] & uWhatPressed)))
1663 {
1664 /* Special flags handling (KeyPrint): */
1665 if (fFlags & KeyPrint)
1666 {
1667 if (fFlags & KeyPressed)
1668 {
1669 static LONG PrintMake[] = { 0xE0, 0x37 };
1670 pCodes = PrintMake;
1671 uCodesCount = RT_ELEMENTS(PrintMake);
1672 }
1673 else
1674 {
1675 static LONG PrintBreak[] = { 0xE0, 0xB7 };
1676 pCodes = PrintBreak;
1677 uCodesCount = RT_ELEMENTS(PrintBreak);
1678 }
1679 }
1680 /* Special flags handling (KeyPause): */
1681 else if (fFlags & KeyPause)
1682 {
1683 if (fFlags & KeyPressed)
1684 {
1685 static LONG Pause[] = { 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 };
1686 pCodes = Pause;
1687 uCodesCount = RT_ELEMENTS(Pause);
1688 }
1689 else
1690 {
1691 /* Pause shall not produce a break code: */
1692 return true;
1693 }
1694 }
1695 /* Common flags handling: */
1696 else
1697 if (keyEventHandleNormal(iKey, uScan, fFlags, pCodes, &uCodesCount))
1698 return true;
1699 }
1700
1701 /* Process the host-combo funtionality: */
1702 if (fFlags & KeyPressed)
1703 {
1704 bool fResult;
1705 if (keyEventHostComboHandled(iKey, pUniKey, isHostComboStateChanged, &fResult))
1706 return fResult;
1707 }
1708 else
1709 {
1710 if (isHostComboStateChanged)
1711 keyEventHandleHostComboRelease(uScreenId);
1712 else
1713 {
1714 if (m_fHostComboPressed)
1715 m_bIsHostComboAlone = true;
1716 }
1717 }
1718
1719 /* Notify all the listeners: */
1720 emit sigStateChange(state());
1721
1722 /* If the VM is NOT paused: */
1723 if (!uimachine()->isPaused())
1724 {
1725 /* If there are scan-codes to send: */
1726 if (uCodesCount)
1727 {
1728 /* Send prepared scan-codes to the guest: */
1729 QVector<LONG> scancodes;
1730 for (uint i = 0; i < uCodesCount; ++i)
1731 scancodes.append(pCodes[i]);
1732 uimachine()->putScancodes(scancodes);
1733 }
1734
1735 /* If full host-key sequence was just finalized: */
1736 if (isHostComboStateChanged && m_fHostComboPressed)
1737 {
1738 /* We have to make guest to release pressed keys from the host-combination: */
1739 foreach (const uint8_t &uCurrentScan, m_pressedHostComboKeys.values())
1740 {
1741 if (m_pressedKeys[uCurrentScan] & IsKeyPressed)
1742 {
1743 uimachine()->putScancode(uCurrentScan | 0x80);
1744 }
1745 else if (m_pressedKeys[uCurrentScan] & IsExtKeyPressed)
1746 {
1747 QVector<LONG> scancodes(2);
1748 scancodes[0] = 0xE0;
1749 scancodes[1] = uCurrentScan | 0x80;
1750 uimachine()->putScancodes(scancodes);
1751 }
1752 m_pressedKeys[uCurrentScan] = 0;
1753 }
1754 }
1755 }
1756
1757 /* Prevent the key from going to Qt: */
1758 return true;
1759}
1760
1761bool UIKeyboardHandler::processHotKey(int iHotKey, wchar_t *pHotKey)
1762{
1763 /* Prepare processing result: */
1764 bool fWasProcessed = false;
1765
1766#if defined(VBOX_WS_MAC)
1767
1768 Q_UNUSED(iHotKey);
1769 if (pHotKey && pHotKey[0] && !pHotKey[1])
1770 fWasProcessed = actionPool()->processHotKey(QKeySequence(QChar(pHotKey[0]).toUpper().unicode()));
1771
1772#elif defined(VBOX_WS_WIN)
1773
1774 Q_UNUSED(pHotKey);
1775 int iKeyboardLayout = GetKeyboardLayoutList(0, NULL);
1776 Assert(iKeyboardLayout);
1777 HKL *pList = new HKL[iKeyboardLayout];
1778 GetKeyboardLayoutList(iKeyboardLayout, pList);
1779 for (int i = 0; i < iKeyboardLayout && !fWasProcessed; ++i)
1780 {
1781 wchar_t symbol;
1782 static BYTE keys[256] = {0};
1783 if (!ToUnicodeEx(iHotKey, 0, keys, &symbol, 1, 0, pList[i]) == 1)
1784 symbol = 0;
1785 if (symbol)
1786 fWasProcessed = actionPool()->processHotKey(QKeySequence(QChar(symbol).toUpper().unicode()));
1787 }
1788 delete[] pList;
1789
1790#elif defined(VBOX_WS_NIX)
1791 if (uiCommon().X11ServerAvailable())
1792 {
1793 Q_UNUSED(pHotKey);
1794 Display *pDisplay = NativeWindowSubsystem::X11GetDisplay();
1795 KeyCode keyCode = XKeysymToKeycode(pDisplay, iHotKey);
1796 for (int i = 0; i < 4 && !fWasProcessed; ++i) /* Up to four groups. */
1797 {
1798 KeySym ks = wrapXkbKeycodeToKeysym(pDisplay, keyCode, i, 0);
1799 char symbol = 0;
1800 if (XkbTranslateKeySym(pDisplay, &ks, 0, &symbol, 1, NULL) == 0)
1801 symbol = 0;
1802 if (symbol)
1803 {
1804 QChar qtSymbol = QString::fromLocal8Bit(&symbol, 1)[0];
1805 fWasProcessed = actionPool()->processHotKey(QKeySequence(qtSymbol.toUpper().unicode()));
1806 }
1807 }
1808 }
1809
1810#else
1811
1812# warning "port me!"
1813
1814#endif
1815
1816 /* Grab the key from the Qt if it was processed, or pass it to the Qt otherwise
1817 * in order to process non-alphanumeric keys in event(), after they are converted to Qt virtual keys: */
1818 return fWasProcessed;
1819}
1820
1821void UIKeyboardHandler::fixModifierState(LONG *piCodes, uint *puCount)
1822{
1823 /* Synchronize the views of the host and the guest to the modifier keys.
1824 * This function will add up to 6 additional keycodes to codes. */
1825
1826#if defined(VBOX_WS_MAC)
1827
1828 /* if (uimachine()->numLockAdaptionCnt()) ... - NumLock isn't implemented by Mac OS X so ignore it. */
1829 if (uimachine()->capsLockAdaptionCnt() && (uimachine()->isCapsLock() ^ !!(::GetCurrentEventKeyModifiers() & alphaLock)))
1830 {
1831 uimachine()->setCapsLockAdaptionCnt(uimachine()->capsLockAdaptionCnt() - 1);
1832 piCodes[(*puCount)++] = 0x3a;
1833 piCodes[(*puCount)++] = 0x3a | 0x80;
1834 /* Some keyboard layouts require shift to be pressed to break
1835 * capslock. For simplicity, only do this if shift is not
1836 * already held down. */
1837 if (uimachine()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))
1838 {
1839 piCodes[(*puCount)++] = 0x2a;
1840 piCodes[(*puCount)++] = 0x2a | 0x80;
1841 }
1842 }
1843
1844#elif defined(VBOX_WS_WIN)
1845
1846 if (uimachine()->numLockAdaptionCnt() && (uimachine()->isNumLock() ^ !!(GetKeyState(VK_NUMLOCK))))
1847 {
1848 uimachine()->setNumLockAdaptionCnt(uimachine()->numLockAdaptionCnt() - 1);
1849 piCodes[(*puCount)++] = 0x45;
1850 piCodes[(*puCount)++] = 0x45 | 0x80;
1851 }
1852 if (uimachine()->capsLockAdaptionCnt() && (uimachine()->isCapsLock() ^ !!(GetKeyState(VK_CAPITAL))))
1853 {
1854 uimachine()->setCapsLockAdaptionCnt(uimachine()->capsLockAdaptionCnt() - 1);
1855 piCodes[(*puCount)++] = 0x3a;
1856 piCodes[(*puCount)++] = 0x3a | 0x80;
1857 /* Some keyboard layouts require shift to be pressed to break
1858 * capslock. For simplicity, only do this if shift is not
1859 * already held down. */
1860 if (uimachine()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))
1861 {
1862 piCodes[(*puCount)++] = 0x2a;
1863 piCodes[(*puCount)++] = 0x2a | 0x80;
1864 }
1865 }
1866
1867#elif defined(VBOX_WS_NIX)
1868 if (uiCommon().X11ServerAvailable())
1869 {
1870 Window wDummy1, wDummy2;
1871 int iDummy3, iDummy4, iDummy5, iDummy6;
1872 unsigned uMask;
1873 unsigned uKeyMaskNum = 0, uKeyMaskCaps = 0;
1874 Display * const pDisplay = NativeWindowSubsystem::X11GetDisplay();
1875
1876 uKeyMaskCaps = LockMask;
1877 XModifierKeymap* map = XGetModifierMapping(pDisplay);
1878 KeyCode keyCodeNum = XKeysymToKeycode(pDisplay, XK_Num_Lock);
1879
1880 for (int i = 0; i < 8; ++ i)
1881 if (keyCodeNum != NoSymbol && map->modifiermap[map->max_keypermod * i] == keyCodeNum)
1882 uKeyMaskNum = 1 << i;
1883 XQueryPointer(pDisplay, DefaultRootWindow(pDisplay), &wDummy1, &wDummy2,
1884 &iDummy3, &iDummy4, &iDummy5, &iDummy6, &uMask);
1885 XFreeModifiermap(map);
1886
1887 if (uimachine()->numLockAdaptionCnt() && (uimachine()->isNumLock() ^ !!(uMask & uKeyMaskNum)))
1888 {
1889 uimachine()->setNumLockAdaptionCnt(uimachine()->numLockAdaptionCnt() - 1);
1890 piCodes[(*puCount)++] = 0x45;
1891 piCodes[(*puCount)++] = 0x45 | 0x80;
1892 }
1893 if (uimachine()->capsLockAdaptionCnt() && (uimachine()->isCapsLock() ^ !!(uMask & uKeyMaskCaps)))
1894 {
1895 uimachine()->setCapsLockAdaptionCnt(uimachine()->capsLockAdaptionCnt() - 1);
1896 piCodes[(*puCount)++] = 0x3a;
1897 piCodes[(*puCount)++] = 0x3a | 0x80;
1898 /* Some keyboard layouts require shift to be pressed to break
1899 * capslock. For simplicity, only do this if shift is not
1900 * already held down. */
1901 if (uimachine()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))
1902 {
1903 piCodes[(*puCount)++] = 0x2a;
1904 piCodes[(*puCount)++] = 0x2a | 0x80;
1905 }
1906 }
1907 }
1908#else
1909
1910# warning "port me!"
1911
1912#endif
1913}
1914
1915void UIKeyboardHandler::saveKeyStates()
1916{
1917 ::memcpy(m_pressedKeysCopy, m_pressedKeys, sizeof(m_pressedKeys));
1918}
1919
1920void UIKeyboardHandler::sendChangedKeyStates()
1921{
1922 QVector <LONG> codes(2);
1923 for (uint i = 0; i < RT_ELEMENTS(m_pressedKeys); ++ i)
1924 {
1925 uint8_t os = m_pressedKeysCopy[i];
1926 uint8_t ns = m_pressedKeys[i];
1927 if ((os & IsKeyPressed) != (ns & IsKeyPressed))
1928 {
1929 codes[0] = i;
1930 if (!(ns & IsKeyPressed))
1931 codes[0] |= 0x80;
1932 uimachine()->putScancode(codes[0]);
1933 }
1934 else if ((os & IsExtKeyPressed) != (ns & IsExtKeyPressed))
1935 {
1936 codes[0] = 0xE0;
1937 codes[1] = i;
1938 if (!(ns & IsExtKeyPressed))
1939 codes[1] |= 0x80;
1940 uimachine()->putScancodes(codes);
1941 }
1942 }
1943}
1944
1945bool UIKeyboardHandler::isAutoCaptureDisabled()
1946{
1947 return uimachine()->isAutoCaptureDisabled();
1948}
1949
1950void UIKeyboardHandler::setAutoCaptureDisabled(bool fIsAutoCaptureDisabled)
1951{
1952 uimachine()->setAutoCaptureDisabled(fIsAutoCaptureDisabled);
1953}
1954
1955bool UIKeyboardHandler::autoCaptureSetGlobally()
1956{
1957 return gEDataManager->autoCaptureEnabled() && !m_fDebuggerActive;
1958}
1959
1960bool UIKeyboardHandler::viewHasFocus(ulong uScreenId)
1961{
1962 return m_views[uScreenId]->hasFocus();
1963}
1964
1965bool UIKeyboardHandler::isSessionRunning()
1966{
1967 return uimachine()->isRunning();
1968}
1969
1970bool UIKeyboardHandler::isSessionStuck()
1971{
1972 return uimachine()->isStuck();
1973}
1974
1975UIMachineWindow* UIKeyboardHandler::isItListenedWindow(QObject *pWatchedObject) const
1976{
1977 UIMachineWindow *pResultWindow = 0;
1978 QMap<ulong, UIMachineWindow*>::const_iterator i = m_windows.constBegin();
1979 while (!pResultWindow && i != m_windows.constEnd())
1980 {
1981 UIMachineWindow *pIteratedWindow = i.value();
1982 if (pIteratedWindow == pWatchedObject)
1983 {
1984 pResultWindow = pIteratedWindow;
1985 continue;
1986 }
1987 ++i;
1988 }
1989 return pResultWindow;
1990}
1991
1992UIMachineView* UIKeyboardHandler::isItListenedView(QObject *pWatchedObject) const
1993{
1994 UIMachineView *pResultView = 0;
1995 QMap<ulong, UIMachineView*>::const_iterator i = m_views.constBegin();
1996 while (!pResultView && i != m_views.constEnd())
1997 {
1998 UIMachineView *pIteratedView = i.value();
1999 if (pIteratedView == pWatchedObject)
2000 {
2001 pResultView = pIteratedView;
2002 continue;
2003 }
2004 ++i;
2005 }
2006 return pResultView;
2007}
2008
2009void UIKeyboardHandler::setHostKeyComboPressedFlag(bool bPressed)
2010{
2011 m_fHostKeyComboPressInserted = bPressed;
2012 emit sigStateChange(state());
2013}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use