VirtualBox

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

Last change on this file was 106611, checked in by vboxsync, 6 weeks ago

FE/Qt: bugref:10407. Handle machine view key press event in keyboard handler.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette