VirtualBox

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

Last change on this file since 82781 was 82008, checked in by vboxsync, 5 years ago

FE/Qt: GUI coding-style fixes for r134761; Some places too old to be updated, but new taken into account.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use