Changeset 30637 in vbox
- Timestamp:
- Jul 5, 2010 10:36:29 PM (15 years ago)
- Location:
- trunk/src/VBox/Frontends/VirtualBox
- Files:
-
- 12 edited
- 8 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Frontends/VirtualBox/Makefile.kmk
r30408 r30637 327 327 src/runtime/UIActionsPool.h \ 328 328 src/runtime/UIIndicatorsPool.h \ 329 src/runtime/UIKeyboardHandler.h \ 329 330 src/runtime/UIMouseHandler.h \ 330 331 src/runtime/UIMachineLogic.h \ 331 332 src/runtime/UIMachineView.h \ 332 333 src/runtime/UIMultiScreenLayout.h \ 334 src/runtime/normal/UIKeyboardHandlerNormal.h \ 333 335 src/runtime/normal/UIMachineLogicNormal.h \ 334 336 src/runtime/normal/UIMachineWindowNormal.h \ 335 337 src/runtime/normal/UIMachineViewNormal.h \ 338 src/runtime/fullscreen/UIKeyboardHandlerFullscreen.h \ 336 339 src/runtime/fullscreen/UIMachineLogicFullscreen.h \ 337 340 src/runtime/fullscreen/UIMachineWindowFullscreen.h \ 338 341 src/runtime/fullscreen/UIMachineViewFullscreen.h \ 342 src/runtime/seamless/UIKeyboardHandlerSeamless.h \ 339 343 src/runtime/seamless/UIMachineLogicSeamless.h \ 340 344 src/runtime/seamless/UIMachineWindowSeamless.h \ … … 461 465 src/runtime/UIActionsPool.cpp \ 462 466 src/runtime/UIIndicatorsPool.cpp \ 467 src/runtime/UIKeyboardHandler.cpp \ 463 468 src/runtime/UIMouseHandler.cpp \ 464 469 src/runtime/UIFrameBuffer.cpp \ … … 471 476 src/runtime/UIMachineMenuBar.cpp \ 472 477 src/runtime/UIMultiScreenLayout.cpp \ 478 src/runtime/normal/UIKeyboardHandlerNormal.cpp \ 473 479 src/runtime/normal/UIMachineLogicNormal.cpp \ 474 480 src/runtime/normal/UIMachineWindowNormal.cpp \ 475 481 src/runtime/normal/UIMachineViewNormal.cpp \ 482 src/runtime/fullscreen/UIKeyboardHandlerFullscreen.cpp \ 476 483 src/runtime/fullscreen/UIMachineLogicFullscreen.cpp \ 477 484 src/runtime/fullscreen/UIMachineWindowFullscreen.cpp \ 478 485 src/runtime/fullscreen/UIMachineViewFullscreen.cpp \ 486 src/runtime/seamless/UIKeyboardHandlerSeamless.cpp \ 479 487 src/runtime/seamless/UIMachineLogicSeamless.cpp \ 480 488 src/runtime/seamless/UIMachineWindowSeamless.cpp \ -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.cpp
r30458 r30637 3 3 * 4 4 * VBox frontends: Qt GUI ("VirtualBox"): 5 * UI MachineViewclass implementation5 * UIKeyboardHandler class implementation 6 6 */ 7 7 … … 19 19 20 20 /* Global includes */ 21 #include <QDesktopWidget> 22 #include <QTimer> 23 #include <QPainter> 24 #include <QScrollBar> 25 #include <VBox/VBoxVideo.h> 21 #include <QKeyEvent> 26 22 27 23 /* Local includes */ 28 24 #include "VBoxGlobal.h" 29 25 #include "VBoxProblemReporter.h" 30 #include "UIFrameBuffer.h" 31 #include "UIFrameBufferQGL.h" 32 #include "UIFrameBufferQImage.h" 33 #include "UIFrameBufferQuartz2D.h" 34 #include "UIFrameBufferSDL.h" 35 #include "VBoxFBOverlay.h" 26 #include "UIActionsPool.h" 27 #include "UIKeyboardHandlerNormal.h" 28 #include "UIKeyboardHandlerFullscreen.h" 29 #include "UIKeyboardHandlerSeamless.h" 30 #include "UIMouseHandler.h" 36 31 #include "UISession.h" 37 #include "UIActionsPool.h"38 #include "UIMouseHandler.h"39 32 #include "UIMachineLogic.h" 40 #include "UIMachineWindow.h"41 #include "UIMachineView.h"42 33 #include "UIMachineWindowNormal.h" 43 34 #include "UIMachineWindowFullscreen.h" 44 35 #include "UIMachineWindowSeamless.h" 45 #include "UIMachineViewNormal.h" 46 #include "UIMachineViewFullscreen.h" 47 #include "UIMachineViewSeamless.h" 48 49 #ifdef Q_WS_PM 50 # include "QIHotKeyEdit.h" 51 #endif /* defined (Q_WS_PM) */ 52 53 #ifdef Q_WS_WIN 54 #undef LOWORD 55 #undef HIWORD 56 #undef LOBYTE 57 #undef HIBYTE 58 #include <windows.h> 59 static UIMachineView *gView = 0; 60 static HHOOK gKbdHook = 0; 61 #endif /* defined (Q_WS_WIN) */ 36 #include "UIMachineView.h" 62 37 63 38 #ifdef Q_WS_X11 64 39 # include <QX11Info> 65 # define XK_XKB_KEYS66 # define XK_MISCELLANY67 40 # include <X11/XKBlib.h> 68 41 # include <X11/keysym.h> … … 76 49 # undef FocusOut 77 50 # undef FocusIn 78 # endif 51 # endif /* KeyPress */ 79 52 # include "XKeyboard.h" 80 #endif /* defined (Q_WS_X11) */ 81 82 #ifdef Q_WS_MAC 83 # include "DockIconPreview.h" 84 # include "DarwinKeyboard.h" 85 # include "UICocoaApplication.h" 86 # include <VBox/err.h> 87 # include <Carbon/Carbon.h> 88 #endif /* Q_WS_MAC */ 89 90 class VBoxViewport: public QWidget 91 { 92 public: 93 94 VBoxViewport(QWidget *pParent) : QWidget(pParent) 95 { 96 /* No need for background drawing: */ 97 setAttribute(Qt::WA_OpaquePaintEvent); 98 } 99 100 QPaintEngine *paintEngine() const 101 { 102 if (testAttribute(Qt::WA_PaintOnScreen)) 103 return NULL; 104 else 105 return QWidget::paintEngine(); 106 } 107 }; 108 53 #endif /* Q_WS_X11 */ 54 55 /* Enums representing different keyboard-states: */ 109 56 enum { KeyExtended = 0x01, KeyPressed = 0x02, KeyPause = 0x04, KeyPrint = 0x08 }; 110 57 enum { IsKeyPressed = 0x01, IsExtKeyPressed = 0x02, IsKbdCaptured = 0x80 }; 111 58 112 UIMachineView* UIMachineView::create( UIMachineWindow *pMachineWindow 113 #ifdef VBOX_WITH_VIDEOHWACCEL 114 , bool bAccelerate2DVideo 115 #endif 116 , UIVisualStateType visualStateType 117 , ulong uScreenId) 118 { 119 UIMachineView *view = 0; 59 #ifdef Q_WS_WIN 60 UIKeyboardHandler* UIKeyboardHandler::m_pKeyboardHandler = 0; 61 #endif /* Q_WS_WIN */ 62 63 /* Factory function to create keyboard-handler: */ 64 UIKeyboardHandler* UIKeyboardHandler::create(UIMachineLogic *pMachineLogic, 65 UIVisualStateType visualStateType) 66 { 67 /* Prepare keyboard-handler: */ 68 UIKeyboardHandler *pKeyboardHandler = 0; 69 /* Depending on visual-state type: */ 120 70 switch (visualStateType) 121 71 { 122 72 case UIVisualStateType_Normal: 123 view = new UIMachineViewNormal( pMachineWindow 124 #ifdef VBOX_WITH_VIDEOHWACCEL 125 , bAccelerate2DVideo 126 #endif 127 , uScreenId); 73 pKeyboardHandler = new UIKeyboardHandlerNormal(pMachineLogic); 128 74 break; 129 75 case UIVisualStateType_Fullscreen: 130 view = new UIMachineViewFullscreen( pMachineWindow 131 #ifdef VBOX_WITH_VIDEOHWACCEL 132 , bAccelerate2DVideo 133 #endif 134 , uScreenId); 76 pKeyboardHandler = new UIKeyboardHandlerFullscreen(pMachineLogic); 135 77 break; 136 78 case UIVisualStateType_Seamless: 137 view = new UIMachineViewSeamless( pMachineWindow 138 #ifdef VBOX_WITH_VIDEOHWACCEL 139 , bAccelerate2DVideo 140 #endif 141 , uScreenId); 79 pKeyboardHandler = new UIKeyboardHandlerSeamless(pMachineLogic); 142 80 break; 143 81 default: 144 82 break; 145 83 } 146 return view; 147 } 148 149 void UIMachineView::destroy(UIMachineView *pWhichView) 150 { 151 delete pWhichView; 152 } 153 154 int UIMachineView::keyboardState() const 155 { 156 return (m_bIsKeyboardCaptured ? UIViewStateType_KeyboardCaptured : 0) | 84 #ifdef Q_WS_WIN 85 /* Its required to have static pointer to created handler 86 * because windows keyboard-hook works only with static members: */ 87 m_pKeyboardHandler = pKeyboardHandler; 88 #endif /* Q_WS_WIN */ 89 /* Return prepared keyboard-handler: */ 90 return pKeyboardHandler; 91 } 92 93 /* Factory function to destroy keyboard-handler: */ 94 void UIKeyboardHandler::destroy(UIKeyboardHandler *pKeyboardHandler) 95 { 96 /* Delete keyboard-handler: */ 97 #ifdef Q_WS_WIN 98 m_pKeyboardHandler = 0; 99 #endif /* Q_WS_WIN */ 100 delete pKeyboardHandler; 101 } 102 103 /* Prepare listened objects: */ 104 void UIKeyboardHandler::prepareListener(ulong uIndex, UIMachineWindow *pMachineWindow) 105 { 106 /* If that window is NOT registered yet: */ 107 if (!m_windows.contains(uIndex)) 108 { 109 /* Add window: */ 110 m_windows.insert(uIndex, pMachineWindow); 111 /* Install event-filter for window: */ 112 switch (machineLogic()->visualStateType()) 113 { 114 case UIVisualStateType_Normal: 115 static_cast<UIMachineWindowNormal*>(m_windows[uIndex])->installEventFilter(this); 116 break; 117 case UIVisualStateType_Fullscreen: 118 static_cast<UIMachineWindowFullscreen*>(m_windows[uIndex])->installEventFilter(this); 119 break; 120 case UIVisualStateType_Seamless: 121 static_cast<UIMachineWindowSeamless*>(m_windows[uIndex])->installEventFilter(this); 122 break; 123 default: 124 break; 125 } 126 } 127 128 /* If that view is NOT registered yet: */ 129 if (!m_views.contains(uIndex)) 130 { 131 /* Add view: */ 132 m_views.insert(uIndex, pMachineWindow->machineView()); 133 /* Install event-filter for view: */ 134 m_views[uIndex]->installEventFilter(this); 135 } 136 } 137 138 /* Cleanup listened objects: */ 139 void UIKeyboardHandler::cleanupListener(ulong uIndex) 140 { 141 /* If window still registered: */ 142 if (m_windows.contains(uIndex)) 143 { 144 /* Remove window: */ 145 m_windows.remove(uIndex); 146 } 147 148 /* If view still registered: */ 149 if (m_views.contains(uIndex)) 150 { 151 /* Remove view: */ 152 m_views.remove(uIndex); 153 } 154 } 155 156 void UIKeyboardHandler::captureKeyboard(ulong uScreenId) 157 { 158 /* Do NOT capture keyboard if its captured already: */ 159 if (m_fIsKeyboardCaptured) 160 return; 161 162 /* If such view exists: */ 163 if (m_views.contains(uScreenId)) 164 { 165 /* Store new keyboard-captured state value: */ 166 m_fIsKeyboardCaptured = true; 167 168 /* Remember which screen had captured keyboard: */ 169 m_iKeyboardCaptureViewIndex = uScreenId; 170 171 #if defined(Q_WS_WIN) 172 /* On Win, keyboard grabbing is ineffective, a low-level keyboard hook is used instead. */ 173 #elif defined(Q_WS_X11) 174 /* On X11, we are using passive XGrabKey for normal (windowed) mode 175 * instead of XGrabKeyboard (called by QWidget::grabKeyboard()) 176 * because XGrabKeyboard causes a problem under metacity - a window cannot be moved 177 * using the mouse if it is currently actively grabing the keyboard; 178 * For static modes we are using usual (active) keyboard grabbing. */ 179 switch (machineLogic()->visualStateType()) 180 { 181 /* If window is moveable we are making passive keyboard grab: */ 182 case UIVisualStateType_Normal: 183 { 184 XGrabKey(QX11Info::display(), AnyKey, AnyModifier, m_windows[m_iKeyboardCaptureViewIndex]->machineWindow()->winId(), False, GrabModeAsync, GrabModeAsync); 185 break; 186 } 187 /* If window is NOT moveable we are making active keyboard grab: */ 188 case UIVisualStateType_Fullscreen: 189 case UIVisualStateType_Seamless: 190 { 191 /* Keyboard grabbing can fail because of some keyboard shortcut is still grabbed by window manager. 192 * We can't be sure this shortcut will be released at all, so we will retry to grab keyboard for 50 times, 193 * and after we will just ignore that issue: */ 194 int cTriesLeft = 50; 195 while (cTriesLeft && XGrabKeyboard(QX11Info::display(), m_windows[m_iKeyboardCaptureViewIndex]->machineWindow()->winId(), False, GrabModeAsync, GrabModeAsync, CurrentTime)) { --cTriesLeft; } 196 break; 197 } 198 /* Should we try to grab keyboard in default case? I think - NO. */ 199 default: 200 break; 201 } 202 #elif defined(Q_WS_MAC) 203 /* On Mac, we use the Qt methods + disabling global hot keys + watching modifiers (for right/left separation). */ 204 ::DarwinDisableGlobalHotKeys(true); 205 m_views[m_iKeyboardCaptureViewIndex]->grabKeyboard(); 206 #else 207 /* On other platforms we are just praying Qt method will work. */ 208 m_views[m_iKeyboardCaptureViewIndex]->grabKeyboard(); 209 #endif 210 211 /* Notify all the listeners: */ 212 emit keyboardStateChanged(keyboardState()); 213 } 214 } 215 216 void UIKeyboardHandler::releaseKeyboard() 217 { 218 /* Do NOT capture keyboard if its captured already: */ 219 if (!m_fIsKeyboardCaptured) 220 return; 221 222 /* If such view exists: */ 223 if (m_views.contains(m_iKeyboardCaptureViewIndex)) 224 { 225 /* Store new keyboard-captured state value: */ 226 m_fIsKeyboardCaptured = false; 227 228 #if defined(Q_WS_WIN) 229 /* On Win, keyboard grabbing is ineffective, a low-level keyboard hook is used instead. */ 230 #elif defined(Q_WS_X11) 231 /* On X11, we are using passive XGrabKey for normal (windowed) mode 232 * instead of XGrabKeyboard (called by QWidget::grabKeyboard()) 233 * because XGrabKeyboard causes a problem under metacity - a window cannot be moved 234 * using the mouse if it is currently actively grabing the keyboard; 235 * For static modes we are using usual (active) keyboard grabbing. */ 236 switch (machineLogic()->visualStateType()) 237 { 238 /* If window is moveable we are making passive keyboard ungrab: */ 239 case UIVisualStateType_Normal: 240 { 241 XUngrabKey(QX11Info::display(), AnyKey, AnyModifier, m_windows[m_iKeyboardCaptureViewIndex]->machineWindow()->winId()); 242 break; 243 } 244 /* If window is NOT moveable we are making active keyboard ungrab: */ 245 case UIVisualStateType_Fullscreen: 246 case UIVisualStateType_Seamless: 247 { 248 XUngrabKeyboard(QX11Info::display(), CurrentTime); 249 break; 250 } 251 /* Should we try to release keyboard in default case? I think - NO. */ 252 default: 253 break; 254 } 255 #elif defined(Q_WS_MAC) 256 ::DarwinDisableGlobalHotKeys(false); 257 m_views[m_iKeyboardCaptureViewIndex]->releaseKeyboard(); 258 #else 259 m_views[m_iKeyboardCaptureViewIndex]->releaseKeyboard(); 260 #endif 261 262 /* Reset keyboard-capture index: */ 263 m_iKeyboardCaptureViewIndex = -1; 264 265 /* Notify all the listeners: */ 266 emit keyboardStateChanged(keyboardState()); 267 } 268 } 269 270 /* Current keyboard state: */ 271 int UIKeyboardHandler::keyboardState() const 272 { 273 return (m_fIsKeyboardCaptured ? UIViewStateType_KeyboardCaptured : 0) | 157 274 (m_bIsHostkeyPressed ? UIViewStateType_HostKeyPressed : 0); 158 275 } 159 276 160 UIMachineView::UIMachineView( UIMachineWindow *pMachineWindow 161 #ifdef VBOX_WITH_VIDEOHWACCEL 162 , bool bAccelerate2DVideo 277 #if defined(Q_WS_WIN) 278 279 bool UIKeyboardHandler::winEventFilter(MSG *pMsg, ulong uScreenId) 280 { 281 /* Check if some system event should be filtered-out. 282 * Returning 'true' means filtering-out, 283 * Returning 'false' means passing event to Qt. */ 284 bool fResult = false; /* Pass to Qt by default: */ 285 switch (pMsg->message) 286 { 287 case WM_KEYDOWN: 288 case WM_KEYUP: 289 case WM_SYSKEYDOWN: 290 case WM_SYSKEYUP: 291 { 292 /* Check for the special flag possibly set at the end of this function: */ 293 if (pMsg->lParam & (0x1 << 25)) 294 { 295 pMsg->lParam &= ~(0x1 << 25); 296 fResult = false; 297 break; 298 } 299 300 /* Scancodes 0x80 and 0x00 are ignored: */ 301 uint8_t scan = (pMsg->lParam >> 16) & 0x7F; 302 if (!scan) 303 { 304 fResult = true; 305 break; 306 } 307 308 int vkey = pMsg->wParam; 309 310 /* When one of the SHIFT keys is held and one of the cursor movement 311 * keys is pressed, Windows duplicates SHIFT press/release messages, 312 * but with the virtual key code set to 0xFF. These virtual keys are also 313 * sent in some other situations (Pause, PrtScn, etc.). Ignore suc messages. */ 314 if (vkey == 0xFF) 315 { 316 fResult = true; 317 break; 318 } 319 320 int flags = 0; 321 if (pMsg->lParam & 0x1000000) 322 flags |= KeyExtended; 323 if (!(pMsg->lParam & 0x80000000)) 324 flags |= KeyPressed; 325 326 switch (vkey) 327 { 328 case VK_SHIFT: 329 case VK_CONTROL: 330 case VK_MENU: 331 { 332 /* Overcome stupid Win32 modifier key generalization: */ 333 int keyscan = scan; 334 if (flags & KeyExtended) 335 keyscan |= 0xE000; 336 switch (keyscan) 337 { 338 case 0x002A: vkey = VK_LSHIFT; break; 339 case 0x0036: vkey = VK_RSHIFT; break; 340 case 0x001D: vkey = VK_LCONTROL; break; 341 case 0xE01D: vkey = VK_RCONTROL; break; 342 case 0x0038: vkey = VK_LMENU; break; 343 case 0xE038: vkey = VK_RMENU; break; 344 } 345 break; 346 } 347 case VK_NUMLOCK: 348 /* Win32 sets the extended bit for the NumLock key. Reset it: */ 349 flags &= ~KeyExtended; 350 break; 351 case VK_SNAPSHOT: 352 flags |= KeyPrint; 353 break; 354 case VK_PAUSE: 355 flags |= KeyPause; 356 break; 357 } 358 359 bool result = keyEvent(vkey, scan, flags, uScreenId); 360 if (!result && m_fIsKeyboardCaptured) 361 { 362 /* keyEvent() returned that it didn't process the message, but since the 363 * keyboard is captured, we don't want to pass it to Windows. We just want 364 * to let Qt process the message (to handle non-alphanumeric <HOST>+key 365 * shortcuts for example). So send it direcltly to the window with the 366 * special flag in the reserved area of lParam (to avoid recursion). */ 367 ::SendMessage(pMsg->hwnd, pMsg->message, 368 pMsg->wParam, pMsg->lParam | (0x1 << 25)); 369 fResult = true; 370 break; 371 } 372 373 /* These special keys have to be handled by Windows as well to update the 374 * internal modifier state and to enable/disable the keyboard LED */ 375 if (vkey == VK_NUMLOCK || vkey == VK_CAPITAL || vkey == VK_LSHIFT || vkey == VK_RSHIFT) 376 { 377 fResult = false; 378 break; 379 } 380 381 fResult = result; 382 break; 383 } 384 default: 385 break; 386 } 387 /* Return result: */ 388 return fResult; 389 } 390 391 #elif defined(Q_WS_X11) 392 393 static Bool UIKeyboardHandlerCompEvent(Display*, XEvent *pEvent, XPointer pvArg) 394 { 395 XEvent *pKeyEvent = (XEvent*)pvArg; 396 if ((pEvent->type == XKeyPress) && (pEvent->xkey.keycode == pKeyEvent->xkey.keycode)) 397 return True; 398 else 399 return False; 400 } 401 402 bool UIKeyboardHandler::x11EventFilter(XEvent *pEvent, ulong uScreenId) 403 { 404 /* Check if some system event should be filtered-out. 405 * Returning 'true' means filtering-out, 406 * Returning 'false' means passing event to Qt. */ 407 bool fResult = false; /* Pass to Qt by default: */ 408 switch (pEvent->type) 409 { 410 /* We have to handle XFocusOut right here as this event is not passed to UIMachineView::event(). 411 * Handling this event is important for releasing the keyboard before the screen saver gets active. 412 * See public ticket #3894: Apparently this makes problems with newer versions of Qt 413 * and this hack is probably not necessary anymore. So disable it for Qt >= 4.5.0. */ 414 case XFocusOut: 415 case XFocusIn: 416 { 417 if (uisession()->isRunning()) 418 { 419 if (VBoxGlobal::qtRTVersion() < ((4 << 16) | (5 << 8) | 0)) 420 { 421 if (pEvent->type == XFocusIn) 422 { 423 /* Capture keyboard by chosen view number: */ 424 captureKeyboard(uScreenId); 425 /* Reset the single-time disable capture flag: */ 426 if (uisession()->isAutoCaptureDisabled()) 427 uisession()->setAutoCaptureDisabled(false); 428 } 429 else 430 { 431 /* Release keyboard: */ 432 releaseKeyboard(); 433 /* And all pressed keys including host-one: */ 434 releaseAllPressedKeys(true); 435 } 436 } 437 } 438 fResult = false; 439 } 440 case XKeyPress: 441 case XKeyRelease: 442 { 443 /* Translate the keycode to a PC scan code. */ 444 uint8_t scan = handleXKeyEvent(pEvent); 445 446 /* Scancodes 0x00 (no valid translation) and 0x80 are ignored: */ 447 if (!scan & 0x7F) 448 { 449 fResult = true; 450 break; 451 } 452 453 /* Fix for http://www.virtualbox.org/ticket/1296: 454 * when X11 sends events for repeated keys, it always inserts an XKeyRelease before the XKeyPress. */ 455 XEvent returnEvent; 456 if ((pEvent->type == XKeyRelease) && (XCheckIfEvent(pEvent->xkey.display, &returnEvent, 457 UIKeyboardHandlerCompEvent, (XPointer)pEvent) == True)) 458 { 459 XPutBackEvent(pEvent->xkey.display, &returnEvent); 460 fResult = true; 461 break; 462 } 463 464 KeySym ks = ::XKeycodeToKeysym(pEvent->xkey.display, pEvent->xkey.keycode, 0); 465 466 int flags = 0; 467 if (scan >> 8) 468 flags |= KeyExtended; 469 if (pEvent->type == XKeyPress) 470 flags |= KeyPressed; 471 472 /* Remove the extended flag: */ 473 scan &= 0x7F; 474 475 switch (ks) 476 { 477 case XK_Print: 478 flags |= KeyPrint; 479 break; 480 case XK_Pause: 481 flags |= KeyPause; 482 break; 483 } 484 485 fResult = keyEvent(ks, scan, flags, uScreenId); 486 } 487 default: 488 break; 489 } 490 /* Return result: */ 491 return fResult; 492 } 493 163 494 #endif 164 , ulong uScreenId) 165 // TODO_NEW_CORE: really think of if this is right 166 // : QAbstractScrollArea(((QMainWindow*)pMachineWindow->machineWindow())->centralWidget()) 167 : QAbstractScrollArea(pMachineWindow->machineWindow()) 168 , m_pMachineWindow(pMachineWindow) 169 , m_uScreenId(uScreenId) 495 496 /* Machine state-change handler: */ 497 void UIKeyboardHandler::sltMachineStateChanged() 498 { 499 /* Get machine state: */ 500 KMachineState state = uisession()->machineState(); 501 /* Handle particular machine states: */ 502 switch (state) 503 { 504 case KMachineState_Paused: 505 case KMachineState_TeleportingPausedVM: 506 case KMachineState_Stuck: 507 { 508 /* Release the keyboard: */ 509 releaseKeyboard(); 510 /* And all pressed keys except the host-one : */ 511 releaseAllPressedKeys(false /* release host-key? */); 512 break; 513 } 514 case KMachineState_Running: 515 { 516 /* Capture the keyboard by the first focused view: */ 517 QList<ulong> theListOfViewIds = m_views.keys(); 518 for (int i = 0; i < theListOfViewIds.size(); ++i) 519 { 520 if (m_views[theListOfViewIds[i]]->hasFocus()) 521 { 522 captureKeyboard(theListOfViewIds[i]); 523 break; 524 } 525 } 526 break; 527 } 528 default: 529 break; 530 } 531 } 532 533 /* Keyboard-handler constructor: */ 534 UIKeyboardHandler::UIKeyboardHandler(UIMachineLogic *pMachineLogic) 535 : QObject(pMachineLogic) 536 , m_pMachineLogic(pMachineLogic) 537 , m_iKeyboardCaptureViewIndex(-1) 170 538 , m_globalSettings(vboxGlobal().settings()) 171 , m_pFrameBuffer(0) 172 , m_previousState(KMachineState_Null) 173 , m_desktopGeometryType(DesktopGeo_Invalid) 174 , m_bIsKeyboardCaptured(false) 539 , m_fIsKeyboardCaptured(false) 175 540 , m_bIsHostkeyPressed(false) 176 541 , m_bIsHostkeyAlone (false) 177 542 , m_bIsHostkeyInCapture(false) 178 , m_bIsMachineWindowResizeIgnored(false)179 543 , m_fPassCAD(false) 180 #ifdef VBOX_WITH_VIDEOHWACCEL 181 , m_fAccelerate2DVideo(bAccelerate2DVideo) 182 #endif /* VBOX_WITH_VIDEOHWACCEL */ 183 #ifdef Q_WS_MAC 544 #if defined(Q_WS_WIN) 545 , m_iKeyboardHookViewIndex(-1) 546 #elif defined(Q_WS_MAC) 184 547 , m_darwinKeyModifiers(0) 185 , m_fKeyboardGrabbed (false) 186 #endif /* Q_WS_MAC */ 187 { 188 } 189 190 UIMachineView::~UIMachineView() 191 { 192 } 193 194 UISession* UIMachineView::uisession() const 195 { 196 return machineLogic()->uisession(); 197 } 198 199 CSession& UIMachineView::session() 200 { 201 return uisession()->session(); 202 } 203 204 QSize UIMachineView::sizeHint() const 205 { 206 #ifdef VBOX_WITH_DEBUGGER 207 // TODO: Fix all DEBUGGER stuff! 208 /* HACK ALERT! Really ugly workaround for the resizing to 9x1 done by DevVGA if provoked before power on. */ 209 QSize fb(m_pFrameBuffer->width(), m_pFrameBuffer->height()); 210 if ((fb.width() < 16 || fb.height() < 16) && (vboxGlobal().isStartPausedEnabled() || vboxGlobal().isDebuggerAutoShowEnabled())) 211 fb = QSize(640, 480); 212 return QSize(fb.width() + frameWidth() * 2, fb.height() + frameWidth() * 2); 213 #else 214 return QSize(m_pFrameBuffer->width() + frameWidth() * 2, m_pFrameBuffer->height() + frameWidth() * 2); 548 , m_fKeyboardGrabbed(false) 215 549 #endif 216 } 217 218 int UIMachineView::contentsX() const 219 { 220 return horizontalScrollBar()->value(); 221 } 222 223 int UIMachineView::contentsY() const 224 { 225 return verticalScrollBar()->value(); 226 } 227 228 int UIMachineView::contentsWidth() const 229 { 230 return m_pFrameBuffer->width(); 231 } 232 233 int UIMachineView::contentsHeight() const 234 { 235 return m_pFrameBuffer->height(); 236 } 237 238 int UIMachineView::visibleWidth() const 239 { 240 return horizontalScrollBar()->pageStep(); 241 } 242 243 int UIMachineView::visibleHeight() const 244 { 245 return verticalScrollBar()->pageStep(); 246 } 247 248 UIMachineLogic* UIMachineView::machineLogic() const 249 { 250 return machineWindowWrapper()->machineLogic(); 251 } 252 253 QSize UIMachineView::desktopGeometry() const 254 { 255 QSize geometry; 256 switch (m_desktopGeometryType) 257 { 258 case DesktopGeo_Fixed: 259 case DesktopGeo_Automatic: 260 geometry = QSize(qMax(m_desktopGeometry.width(), m_storedConsoleSize.width()), 261 qMax(m_desktopGeometry.height(), m_storedConsoleSize.height())); 262 break; 263 case DesktopGeo_Any: 264 geometry = QSize(0, 0); 265 break; 266 default: 267 AssertMsgFailed(("Bad geometry type %d!\n", m_desktopGeometryType)); 268 } 269 return geometry; 270 } 271 272 QSize UIMachineView::guestSizeHint() 273 { 274 /* Result: */ 275 QSize sizeHint; 276 277 /* Get current machine: */ 278 CMachine machine = session().GetMachine(); 279 280 /* Load machine view hint: */ 281 QString strKey = m_uScreenId == 0 ? QString("%1").arg(VBoxDefs::GUI_LastGuestSizeHint) : 282 QString("%1%2").arg(VBoxDefs::GUI_LastGuestSizeHint).arg(m_uScreenId); 283 QString strValue = machine.GetExtraData(strKey); 284 285 bool ok = true; 286 int width = 0, height = 0; 287 if (ok) 288 width = strValue.section(',', 0, 0).toInt(&ok); 289 if (ok) 290 height = strValue.section(',', 1, 1).toInt(&ok); 291 292 if (ok /* If previous parameters were read correctly! */) 293 { 294 /* Compose guest size hint from loaded values: */ 295 sizeHint = QSize(width, height); 296 } 297 else 298 { 299 /* Compose guest size hint from default attributes: */ 300 sizeHint = QSize(800, 600); 301 } 302 303 /* Return result: */ 304 return sizeHint; 305 } 306 307 void UIMachineView::setDesktopGeometry(DesktopGeo geometry, int aWidth, int aHeight) 308 { 309 switch (geometry) 310 { 311 case DesktopGeo_Fixed: 312 m_desktopGeometryType = DesktopGeo_Fixed; 313 if (aWidth != 0 && aHeight != 0) 314 m_desktopGeometry = QSize(aWidth, aHeight); 315 else 316 m_desktopGeometry = QSize(0, 0); 317 storeConsoleSize(0, 0); 318 break; 319 case DesktopGeo_Automatic: 320 m_desktopGeometryType = DesktopGeo_Automatic; 321 m_desktopGeometry = QSize(0, 0); 322 storeConsoleSize(0, 0); 323 break; 324 case DesktopGeo_Any: 325 m_desktopGeometryType = DesktopGeo_Any; 326 m_desktopGeometry = QSize(0, 0); 327 break; 328 default: 329 AssertMsgFailed(("Invalid desktop geometry type %d\n", geometry)); 330 m_desktopGeometryType = DesktopGeo_Invalid; 331 } 332 } 333 334 void UIMachineView::storeConsoleSize(int iWidth, int iHeight) 335 { 336 m_storedConsoleSize = QSize(iWidth, iHeight); 337 } 338 339 void UIMachineView::storeGuestSizeHint(const QSize &sizeHint) 340 { 341 /* Get current machine: */ 342 CMachine machine = session().GetMachine(); 343 344 /* Save machine view hint: */ 345 QString strKey = m_uScreenId == 0 ? QString("%1").arg(VBoxDefs::GUI_LastGuestSizeHint) : 346 QString("%1%2").arg(VBoxDefs::GUI_LastGuestSizeHint).arg(m_uScreenId); 347 QString strValue = QString("%1,%2").arg(sizeHint.width()).arg(sizeHint.height()); 348 machine.SetExtraData(strKey, strValue); 349 } 350 351 void UIMachineView::updateSliders() 352 { 353 QSize p = viewport()->size(); 354 QSize m = maximumViewportSize(); 355 356 QSize v = QSize(frameBuffer()->width(), frameBuffer()->height()); 357 /* No scroll bars needed: */ 358 if (m.expandedTo(v) == m) 359 p = m; 360 361 horizontalScrollBar()->setRange(0, v.width() - p.width()); 362 verticalScrollBar()->setRange(0, v.height() - p.height()); 363 horizontalScrollBar()->setPageStep(p.width()); 364 verticalScrollBar()->setPageStep(p.height()); 365 } 366 367 void UIMachineView::prepareFrameBuffer() 368 { 369 /* Prepare viewport: */ 370 #ifdef VBOX_GUI_USE_QGLFB 371 QWidget *pViewport; 372 switch (vboxGlobal().vmRenderMode()) 373 { 374 case VBoxDefs::QGLMode: 375 pViewport = new VBoxGLWidget(session().GetConsole(), this, NULL); 376 break; 377 default: 378 pViewport = new VBoxViewport(this); 379 } 380 #else 381 VBoxViewport *pViewport = new VBoxViewport(this); 382 #endif 383 setViewport(pViewport); 384 385 CDisplay display = session().GetConsole().GetDisplay(); 386 Assert(!display.isNull()); 387 m_pFrameBuffer = NULL; 388 389 switch (vboxGlobal().vmRenderMode()) 390 { 391 #ifdef VBOX_GUI_USE_QIMAGE 392 case VBoxDefs::QImageMode: 393 # ifdef VBOX_WITH_VIDEOHWACCEL 394 if (m_fAccelerate2DVideo) 395 { 396 UIFrameBuffer* pFramebuffer = uisession()->frameBuffer(screenId()); 397 if (pFramebuffer) 398 pFramebuffer->setView(this); 399 else 400 { 401 /* these two additional template args is a workaround to this [VBox|UI] duplication 402 * @todo: they are to be removed once VBox stuff is gone */ 403 pFramebuffer = new VBoxOverlayFrameBuffer<UIFrameBufferQImage, UIMachineView, UIResizeEvent>(this, &machineWindowWrapper()->session(), (uint32_t)screenId()); 404 uisession()->setFrameBuffer(screenId(), pFramebuffer); 405 } 406 m_pFrameBuffer = pFramebuffer; 407 } 408 else 409 m_pFrameBuffer = new UIFrameBufferQImage(this); 410 # else 411 m_pFrameBuffer = new UIFrameBufferQImage(this); 412 # endif 413 break; 414 #endif /* VBOX_GUI_USE_QIMAGE */ 415 #ifdef VBOX_GUI_USE_QGLFB 416 case VBoxDefs::QGLMode: 417 m_pFrameBuffer = new UIFrameBufferQGL(this); 418 break; 419 // case VBoxDefs::QGLOverlayMode: 420 // m_pFrameBuffer = new UIQGLOverlayFrameBuffer(this); 421 // break; 422 #endif /* VBOX_GUI_USE_QGLFB */ 423 #ifdef VBOX_GUI_USE_SDL 424 case VBoxDefs::SDLMode: 425 /* Indicate that we are doing all drawing stuff ourself: */ 426 // TODO_NEW_CORE 427 viewport()->setAttribute(Qt::WA_PaintOnScreen); 428 # ifdef Q_WS_X11 429 /* This is somehow necessary to prevent strange X11 warnings on i386 and segfaults on x86_64: */ 430 XFlush(QX11Info::display()); 431 # endif 432 # if defined(VBOX_WITH_VIDEOHWACCEL) && defined(DEBUG_misha) /* not tested yet */ 433 if (m_fAccelerate2DVideo) 434 { 435 class UIFrameBuffer* pFramebuffer = uisession()->frameBuffer(screenId()); 436 if (pFramebuffer) 437 pFramebuffer->setView(this); 438 else 439 { 440 /* these two additional template args is a workaround to this [VBox|UI] duplication 441 * @todo: they are to be removed once VBox stuff is gone */ 442 pFramebuffer = new VBoxOverlayFrameBuffer<UIFrameBufferSDL, UIMachineView, UIResizeEvent>(this, &machineWindowWrapper()->session(), (uint32_t)screenId()); 443 uisession()->setFrameBuffer(screenId(), pFramebuffer); 444 } 445 m_pFrameBuffer = pFramebuffer; 446 } 447 else 448 m_pFrameBuffer = new UIFrameBufferSDL(this); 449 # else 450 m_pFrameBuffer = new UIFrameBufferSDL(this); 451 # endif 452 /* Disable scrollbars because we cannot correctly draw in a scrolled window using SDL: */ 453 horizontalScrollBar()->setEnabled(false); 454 verticalScrollBar()->setEnabled(false); 455 break; 456 #endif /* VBOX_GUI_USE_SDL */ 457 #if 0 // TODO: Enable DDraw frame buffer! 458 #ifdef VBOX_GUI_USE_DDRAW 459 case VBoxDefs::DDRAWMode: 460 m_pFrameBuffer = new UIDDRAWFrameBuffer(this); 461 if (!m_pFrameBuffer || m_pFrameBuffer->address() == NULL) 462 { 463 if (m_pFrameBuffer) 464 delete m_pFrameBuffer; 465 m_mode = VBoxDefs::QImageMode; 466 m_pFrameBuffer = new UIFrameBufferQImage(this); 467 } 468 break; 469 #endif /* VBOX_GUI_USE_DDRAW */ 470 #endif 471 #ifdef VBOX_GUI_USE_QUARTZ2D 472 case VBoxDefs::Quartz2DMode: 473 /* Indicate that we are doing all drawing stuff ourself: */ 474 viewport()->setAttribute(Qt::WA_PaintOnScreen); 475 # ifdef VBOX_WITH_VIDEOHWACCEL 476 if (m_fAccelerate2DVideo) 477 { 478 UIFrameBuffer* pFramebuffer = uisession()->frameBuffer(screenId()); 479 if (pFramebuffer) 480 pFramebuffer->setView(this); 481 else 482 { 483 /* these two additional template args is a workaround to this [VBox|UI] duplication 484 * @todo: they are to be removed once VBox stuff is gone */ 485 pFramebuffer = new VBoxOverlayFrameBuffer<UIFrameBufferQuartz2D, UIMachineView, UIResizeEvent>(this, &machineWindowWrapper()->session(), (uint32_t)screenId()); 486 uisession()->setFrameBuffer(screenId(), pFramebuffer); 487 } 488 m_pFrameBuffer = pFramebuffer; 489 } 490 else 491 m_pFrameBuffer = new UIFrameBufferQuartz2D(this); 492 # else 493 m_pFrameBuffer = new UIFrameBufferQuartz2D(this); 494 # endif 495 break; 496 #endif /* VBOX_GUI_USE_QUARTZ2D */ 497 default: 498 AssertReleaseMsgFailed(("Render mode must be valid: %d\n", vboxGlobal().vmRenderMode())); 499 LogRel(("Invalid render mode: %d\n", vboxGlobal().vmRenderMode())); 500 qApp->exit(1); 501 break; 502 } 503 if (m_pFrameBuffer) 504 { 505 #ifdef VBOX_WITH_VIDEOHWACCEL 506 CFramebuffer fb(NULL); 507 if (m_fAccelerate2DVideo) 508 { 509 LONG XOrigin, YOrigin; 510 /* check if the framebuffer is already assigned 511 * in this case we do not need to re-assign it 512 * neither do we need to AddRef */ 513 display.GetFramebuffer(m_uScreenId, fb, XOrigin, YOrigin); 514 } 515 if (fb.raw() != m_pFrameBuffer) /* <-this will evaluate to true iff m_fAccelerate2DVideo is disabled or iff no framebuffer is yet assigned */ 516 #endif 517 { 518 m_pFrameBuffer->AddRef(); 519 } 520 521 /* always perform SetFramebuffer to ensure 3D gets notified */ 522 display.SetFramebuffer(m_uScreenId, CFramebuffer(m_pFrameBuffer)); 523 } 524 525 #ifdef Q_WS_X11 526 /* Processing pseudo resize-event to synchronize frame-buffer 527 * with stored framebuffer size in case of machine state was 'saved': */ 528 if (session().GetMachine().GetState() == KMachineState_Saved) 529 { 530 QSize size = guestSizeHint(); 531 UIResizeEvent event(FramebufferPixelFormat_Opaque, NULL, 0, 0, size.width(), size.height()); 532 frameBuffer()->resizeEvent(&event); 533 } 534 #endif /* Q_WS_X11 */ 535 } 536 537 void UIMachineView::prepareCommon() 538 { 539 /* Prepare view frame: */ 540 setFrameStyle(QFrame::NoFrame); 550 { 551 /* Prepare: */ 552 prepareCommon(); 553 554 /* Load settings: */ 555 loadSettings(); 556 557 /* Initialize: */ 558 sltMachineStateChanged(); 559 } 560 561 /* Keyboard-handler destructor: */ 562 UIKeyboardHandler::~UIKeyboardHandler() 563 { 564 /* Cleanup: */ 565 cleanupCommon(); 566 } 567 568 void UIKeyboardHandler::prepareCommon() 569 { 570 /* Machine state-change updater: */ 571 connect(uisession(), SIGNAL(sigMachineStateChange()), this, SLOT(sltMachineStateChanged())); 541 572 542 573 /* Pressed keys: */ 543 574 ::memset(m_pressedKeys, 0, sizeof(m_pressedKeys)); 544 545 /* Setup palette: */ 546 QPalette palette(viewport()->palette()); 547 palette.setColor(viewport()->backgroundRole(), Qt::black); 548 viewport()->setPalette(palette); 549 550 /* Setup focus policy: */ 551 setFocusPolicy(Qt::WheelFocus); 552 553 #if defined Q_WS_PM 554 bool ok = VBoxHlpInstallKbdHook(0, winId(), UM_PREACCEL_CHAR); 555 Assert(ok); 556 NOREF(ok); 575 } 576 577 void UIKeyboardHandler::loadSettings() 578 { 579 /* Global settings: */ 580 #ifdef Q_WS_X11 581 /* Initialize the X keyboard subsystem: */ 582 initMappedX11Keyboard(QX11Info::display(), vboxGlobal().settings().publicProperty("GUI/RemapScancodes")); 557 583 #endif 558 584 559 /* Register mouse-handler: */ 560 machineLogic()->mouseHandler()->addMachineView(screenId(), this); 561 } 562 563 void UIMachineView::prepareFilters() 564 { 565 /* Enable MouseMove events: */ 566 viewport()->setMouseTracking(true); 567 568 /* QScrollView does the below on its own, but let's 569 * do it anyway for the case it will not do it in the future: */ 570 viewport()->installEventFilter(this); 571 572 /* We want to be notified on some parent's events: */ 573 machineWindowWrapper()->machineWindow()->installEventFilter(this); 574 } 575 576 void UIMachineView::prepareConsoleConnections() 577 { 578 /* Machine state-change updater: */ 579 connect(uisession(), SIGNAL(sigMachineStateChange()), this, SLOT(sltMachineStateChanged())); 580 } 581 582 void UIMachineView::loadMachineViewSettings() 583 { 584 /* Global settings: */ 585 { 586 #ifdef Q_WS_X11 587 /* Initialize the X keyboard subsystem: */ 588 initMappedX11Keyboard(QX11Info::display(), vboxGlobal().settings().publicProperty("GUI/RemapScancodes")); 589 #endif 590 591 /* Remember the desktop geometry and register for geometry 592 * change events for telling the guest about video modes we like: */ 593 QString desktopGeometry = vboxGlobal().settings().publicProperty("GUI/MaxGuestResolution"); 594 if ((desktopGeometry == QString::null) || (desktopGeometry == "auto")) 595 setDesktopGeometry(DesktopGeo_Automatic, 0, 0); 596 else if (desktopGeometry == "any") 597 setDesktopGeometry(DesktopGeo_Any, 0, 0); 598 else 599 { 600 int width = desktopGeometry.section(',', 0, 0).toInt(); 601 int height = desktopGeometry.section(',', 1, 1).toInt(); 602 setDesktopGeometry(DesktopGeo_Fixed, width, height); 603 } 604 } 605 606 /* Exatra data settings: */ 585 /* Extra data settings: */ 607 586 { 608 587 /* CAD settings: */ 609 588 QString passCAD = session().GetConsole().GetMachine().GetExtraData(VBoxDefs::GUI_PassCAD); 610 if (!passCAD.isEmpty() && ((passCAD != "false") || (passCAD != "no")))589 if (!passCAD.isEmpty() && passCAD != "false" && passCAD != "no") 611 590 m_fPassCAD = true; 612 591 } 613 592 } 614 593 615 void UIMachineView::cleanupCommon() 616 { 617 /* Unregister mouse-handler: */ 618 machineLogic()->mouseHandler()->delMachineView(screenId()); 619 620 #ifdef Q_WS_PM 621 bool ok = VBoxHlpUninstallKbdHook(0, winId(), UM_PREACCEL_CHAR); 622 Assert(ok); 623 NOREF(ok); 624 #endif /* Q_WS_PM */ 625 626 #ifdef Q_WS_WIN 627 if (gKbdHook) 628 UnhookWindowsHookEx(gKbdHook); 629 gView = 0; 630 #endif /* Q_WS_WIN */ 631 632 #ifdef Q_WS_MAC 633 /* We have to make sure the callback for the keyboard events is released 634 * when closing this view. */ 594 void UIKeyboardHandler::cleanupCommon() 595 { 596 #if defined(Q_WS_WIN) 597 /* Cleaning keyboard-hook: */ 598 if (m_keyboardHook) 599 { 600 UnhookWindowsHookEx(m_keyboardHook); 601 m_keyboardHook = NULL; 602 } 603 #elif defined(Q_WS_MAC) 604 /* We have to make sure the callback for the keyboard events 605 * is released when closing this view. */ 635 606 if (m_fKeyboardGrabbed) 636 darwinGrabKeyboardEvents (false); 637 #endif /* Q_WS_MAC */ 638 } 639 640 void UIMachineView::cleanupFrameBuffer() 641 { 642 if (m_pFrameBuffer) 643 { 644 /* Process pending frame-buffer resize events: */ 645 QApplication::sendPostedEvents(this, VBoxDefs::ResizeEventType); 646 #ifdef VBOX_WITH_VIDEOHWACCEL 647 if (m_fAccelerate2DVideo) 648 { 649 /* When 2D is enabled we do not re-create Framebuffers. This is done to 650 * 1. avoid 2D command loss during the time slot when no framebuffer is assigned to the display 651 * 2. make it easier to preserve the current 2D state */ 652 Assert(m_pFrameBuffer == uisession()->frameBuffer(screenId())); 653 m_pFrameBuffer->setView(NULL); 654 } 655 else 607 darwinGrabKeyboardEvents(false); 656 608 #endif 657 { 658 /* Warn framebuffer about its no more necessary: */ 659 m_pFrameBuffer->setDeleted(true); 660 /* Detach framebuffer from Display: */ 661 CDisplay display = session().GetConsole().GetDisplay(); 662 display.SetFramebuffer(m_uScreenId, CFramebuffer(NULL)); 663 /* Release the reference: */ 664 m_pFrameBuffer->Release(); 665 // delete m_pFrameBuffer; // TODO_NEW_CORE: possibly necessary to really cleanup 666 m_pFrameBuffer = NULL; 667 } 668 } 669 } 670 671 bool UIMachineView::event(QEvent *pEvent) 672 { 673 switch (pEvent->type()) 674 { 675 case QEvent::FocusIn: 676 { 677 if (uisession()->isRunning()) 678 focusEvent(true); 679 break; 680 } 681 case QEvent::FocusOut: 682 { 683 if (uisession()->isRunning()) 684 focusEvent(false); 685 else 686 { 687 /* Release the host key and all other pressed keys too even when paused 688 * (otherwise, we will get stuck keys in the guest when doing sendChangedKeyStates() on resume 689 * because key presses were already recorded in m_pressedKeys but key releases will most likely 690 * not reach us but the new focus window instead): */ 691 releaseAllPressedKeys(true /* including host key? */); 692 } 693 break; 694 } 695 696 case VBoxDefs::RepaintEventType: 697 { 698 UIRepaintEvent *pPaintEvent = static_cast<UIRepaintEvent*>(pEvent); 699 viewport()->repaint(pPaintEvent->x() - contentsX(), pPaintEvent->y() - contentsY(), 700 pPaintEvent->width(), pPaintEvent->height()); 701 702 return true; 703 } 704 705 #ifdef VBOX_WITH_VIDEOHWACCEL 706 case VBoxDefs::VHWACommandProcessType: 707 { 708 m_pFrameBuffer->doProcessVHWACommand(pEvent); 709 return true; 710 } 711 #endif 712 713 case QEvent::KeyPress: 714 case QEvent::KeyRelease: 715 { 716 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent); 717 718 #ifdef Q_WS_PM 719 // TODO: that a temporary solution to send Alt+Tab and friends to the guest. 720 // The proper solution is to write a keyboard driver that will steal these combos from the host 721 // (it's impossible to do so using hooks on OS/2): 722 723 if (m_bIsHostkeyPressed) 724 { 725 bool pressed = pEvent->type() == QEvent::KeyPress; 726 CKeyboard keyboard = session().GetConsole().GetKeyboard(); 727 728 /* Whether the host key is Shift so that it will modify the hot key values? 729 * Note that we don't distinguish between left and right shift here (too much hassle): */ 730 const bool kShift = (m_globalSettings.hostKey() == VK_SHIFT || 731 m_globalSettings.hostKey() == VK_LSHIFT) && 732 (pKeyEvent->state() & Qt::ShiftModifier); 733 /* define hot keys according to the Shift state */ 734 const int kAltTab = kShift ? Qt::Key_Exclam : Qt::Key_1; 735 const int kAltShiftTab = kShift ? Qt::Key_At : Qt::Key_2; 736 const int kCtrlEsc = kShift ? Qt::Key_AsciiTilde : Qt::Key_QuoteLeft; 737 738 /* Simulate Alt+Tab on Host+1 and Alt+Shift+Tab on Host+2 */ 739 if (pKeyEvent->key() == kAltTab || pKeyEvent->key() == kAltShiftTab) 740 { 741 if (pressed) 609 } 610 611 /* Machine-logic getter: */ 612 UIMachineLogic* UIKeyboardHandler::machineLogic() const 613 { 614 return m_pMachineLogic; 615 } 616 617 /* UI Session getter: */ 618 UISession* UIKeyboardHandler::uisession() const 619 { 620 return machineLogic()->uisession(); 621 } 622 623 /* Main Session getter: */ 624 CSession& UIKeyboardHandler::session() const 625 { 626 return uisession()->session(); 627 } 628 629 /* Event handler for prepared listener(s): */ 630 bool UIKeyboardHandler::eventFilter(QObject *pWatchedObject, QEvent *pEvent) 631 { 632 /* Check if pWatchedObject object is window: */ 633 if (UIMachineWindow *pWatchedWindow = isItListenedWindow(pWatchedObject)) 634 { 635 /* Get corresponding screen index: */ 636 ulong uScreenId = m_windows.key(pWatchedWindow); 637 NOREF(uScreenId); 638 /* Handle window events: */ 639 switch (pEvent->type()) 640 { 641 #if defined(Q_WS_WIN) 642 /* Install/uninstall low-level keyboard-hook on every activation/deactivation to: 643 * a) avoid excess hook calls when we're not active and; 644 * b) be always in front of any other possible hooks. */ 645 case QEvent::WindowActivate: 646 { 647 /* If keyboard hook is NOT currently created; 648 * Or created but NOT for that window: */ 649 if (!m_keyboardHook || m_iKeyboardHookViewIndex != uScreenId) 650 { 651 /* If keyboard-hook present: */ 652 if (m_keyboardHook) 742 653 { 743 /* Send the Alt press to the guest */ 744 if (!(m_pressedKeysCopy[0x38] & IsKeyPressed)) 745 { 746 /* Store the press in *Copy to have it automatically 747 * released when the Host key is released: */ 748 m_pressedKeysCopy[0x38] |= IsKeyPressed; 749 keyboard.PutScancode(0x38); 750 } 751 752 /* Make sure Shift is pressed if it's Key_2 and released if it's Key_1: */ 753 if (pKeyEvent->key() == kAltTab && 754 (m_pressedKeysCopy[0x2A] & IsKeyPressed)) 755 { 756 m_pressedKeysCopy[0x2A] &= ~IsKeyPressed; 757 keyboard.PutScancode(0xAA); 758 } 759 else 760 if (pKeyEvent->key() == kAltShiftTab && 761 !(m_pressedKeysCopy[0x2A] & IsKeyPressed)) 762 { 763 m_pressedKeysCopy[0x2A] |= IsKeyPressed; 764 keyboard.PutScancode(0x2A); 765 } 654 /* We should remove existing keyboard-hook first: */ 655 UnhookWindowsHookEx(m_keyboardHook); 656 m_keyboardHook = NULL; 766 657 } 767 768 keyboard.PutScancode(pressed ? 0x0F : 0x8F); 769 770 pKeyEvent->accept(); 771 return true; 772 } 773 774 /* Simulate Ctrl+Esc on Host+Tilde */ 775 if (pKeyEvent->key() == kCtrlEsc) 776 { 777 /* Send the Ctrl press to the guest */ 778 if (pressed && !(m_pressedKeysCopy[0x1d] & IsKeyPressed)) 779 { 780 /* store the press in *Copy to have it automatically 781 * released when the Host key is released */ 782 m_pressedKeysCopy[0x1d] |= IsKeyPressed; 783 keyboard.PutScancode(0x1d); 784 } 785 786 keyboard.PutScancode(pressed ? 0x01 : 0x81); 787 788 pKeyEvent->accept(); 789 return true; 790 } 791 } 792 #endif /* Q_WS_PM */ 793 794 if (m_bIsHostkeyPressed && pEvent->type() == QEvent::KeyPress) 795 { 796 /* Passing F1-F12 keys to the guest: */ 797 if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F12) 798 { 799 QVector <LONG> combo(6); 800 combo[0] = 0x1d; /* Ctrl down */ 801 combo[1] = 0x38; /* Alt down */ 802 combo[4] = 0xb8; /* Alt up */ 803 combo[5] = 0x9d; /* Ctrl up */ 804 if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F10) 805 { 806 combo[2] = 0x3b + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 down */ 807 combo[3] = 0xbb + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 up */ 808 } 809 /* some scan slice */ 810 else if (pKeyEvent->key() >= Qt::Key_F11 && pKeyEvent->key() <= Qt::Key_F12) 811 { 812 combo[2] = 0x57 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 down */ 813 combo[3] = 0xd7 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 up */ 814 } 815 else 816 Assert(0); 817 818 CKeyboard keyboard = session().GetConsole().GetKeyboard(); 819 keyboard.PutScancodes(combo); 820 } 821 822 /* Process hot keys not processed in keyEvent() (as in case of non-alphanumeric keys): */ 823 machineWindowWrapper()->machineLogic()->actionsPool()->processHotKey(QKeySequence(pKeyEvent->key())); 824 } 825 else if (!m_bIsHostkeyPressed && pEvent->type() == QEvent::KeyRelease) 826 { 827 /* Show a possible warning on key release which seems to be more expected by the end user: */ 828 if (uisession()->isPaused()) 829 { 830 /* Iif the reminder is disabled we pass the event to Qt to enable normal 831 * keyboard functionality (for example, menu access with Alt+Letter): */ 832 if (!vboxProblem().remindAboutPausedVMInput()) 833 break; 834 } 835 } 836 837 pKeyEvent->accept(); 838 return true; 839 } 840 841 #ifdef Q_WS_MAC 842 /* posted OnShowWindow */ 843 case VBoxDefs::ShowWindowEventType: 844 { 845 /* Dunno what Qt3 thinks a window that has minimized to the dock should be - it is not hidden, 846 * neither is it minimized. OTOH it is marked shown and visible, but not activated. 847 * This latter isn't of much help though, since at this point nothing is marked activated. 848 * I might have overlooked something, but I'm buggered what if I know what. So, I'll just always 849 * show & activate the stupid window to make it get out of the dock when the user wishes to show a VM: */ 850 window()->show(); 851 window()->activateWindow(); 852 return true; 853 } 854 #endif 855 856 default: 857 break; 858 } 859 860 return QAbstractScrollArea::event(pEvent); 861 } 862 863 bool UIMachineView::eventFilter(QObject *pWatched, QEvent *pEvent) 864 { 865 #ifdef VBOX_WITH_VIDEOHWACCEL 866 if (pWatched == viewport()) 867 { 868 switch (pEvent->type()) 869 { 870 case QEvent::Resize: 871 { 872 if (m_pFrameBuffer) 873 m_pFrameBuffer->viewportResized(static_cast<QResizeEvent*>(pEvent)); 874 break; 875 } 876 default: 877 break; 878 } 879 } 880 #endif /* VBOX_WITH_VIDEOHWACCEL */ 881 if (pWatched == machineWindowWrapper()->machineWindow()) 882 { 883 switch (pEvent->type()) 884 { 885 case QEvent::WindowStateChange: 886 { 887 /* During minimizing and state restoring machineWindowWrapper() gets 888 * the focus which belongs to console view window, so returning it properly. */ 889 QWindowStateChangeEvent *pWindowEvent = static_cast<QWindowStateChangeEvent*>(pEvent); 890 if (pWindowEvent->oldState() & Qt::WindowMinimized) 891 { 892 if (QApplication::focusWidget()) 893 { 894 QApplication::focusWidget()->clearFocus(); 895 qApp->processEvents(); 896 } 897 QTimer::singleShot(0, this, SLOT(setFocus())); 898 } 899 break; 900 } 901 #ifdef Q_WS_WIN 902 /* Install/uninstall low-level kbd hook on every activation/deactivation to: 903 * a) avoid excess hook calls when we're not active and 904 * b) be always in front of any other possible hooks */ 905 case QEvent::WindowActivate: 906 { 907 gView = this; 908 gKbdHook = SetWindowsHookEx(WH_KEYBOARD_LL, lowLevelKeyboardProc, GetModuleHandle(NULL), 0); 909 AssertMsg(gKbdHook, ("SetWindowsHookEx(): err=%d", GetLastError())); 658 /* Register new keyboard-hook: */ 659 m_keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, lowLevelKeyboardProc, GetModuleHandle(NULL), 0); 660 AssertMsg(m_keyboardHook, ("SetWindowsHookEx(): err=%d", GetLastError())); 661 /* Remember which view had captured keyboard: */ 662 m_iKeyboardHookViewIndex = uScreenId; 663 } 910 664 break; 911 665 } 912 666 case QEvent::WindowDeactivate: 913 667 { 914 if (gKbdHook) 915 { 916 UnhookWindowsHookEx(gKbdHook); 917 gKbdHook = NULL; 918 if (gView == this) 919 gView = 0; 920 } 921 break; 922 } 923 #endif /* Q_WS_WIN */ 924 #ifdef Q_WS_MAC 925 /* Install/remove the keyboard event handler: */ 668 /* If keyboard is currently captured: */ 669 if (m_keyboardHook && m_iKeyboardHookViewIndex == uScreenId) 670 { 671 /* We should remove existing keyboard-hook: */ 672 UnhookWindowsHookEx(m_keyboardHook); 673 m_keyboardHook = NULL; 674 /* Remember what there is no window captured keyboard: */ 675 m_iKeyboardHookViewIndex = -1; 676 } 677 break; 678 } 679 #elif defined(Q_WS_MAC) 680 /* Install the keyboard event handler: */ 926 681 case QEvent::WindowActivate: 927 682 darwinGrabKeyboardEvents(true); 928 683 break; 684 /* Remove the keyboard event handler: */ 929 685 case QEvent::WindowDeactivate: 930 686 darwinGrabKeyboardEvents(false); 931 687 break; 932 #endif /* Q_WS_MAC */688 #endif 933 689 default: 934 690 break; … … 936 692 } 937 693 938 return QAbstractScrollArea::eventFilter (pWatched, pEvent);939 }940 941 void UIMachineView::sltMachineStateChanged()942 {943 /* Get machine state: */944 KMachineState state = uisession()->machineState();945 switch (state)946 {947 case KMachineState_Paused:948 case KMachineState_TeleportingPausedVM:949 {950 if (vboxGlobal().vmRenderMode() != VBoxDefs::TimerMode && m_pFrameBuffer &&951 (state != KMachineState_TeleportingPausedVM || m_previousState != KMachineState_Teleporting))952 {953 /* Take a screen snapshot. Note that TakeScreenShot() always needs a 32bpp image: */954 QImage shot = QImage(m_pFrameBuffer->width(), m_pFrameBuffer->height(), QImage::Format_RGB32);955 /* If TakeScreenShot fails or returns no image, just show a black image. */956 shot.fill(0);957 CDisplay dsp = session().GetConsole().GetDisplay();958 dsp.TakeScreenShot(screenId(), shot.bits(), shot.width(), shot.height());959 /* TakeScreenShot() may fail if, e.g. the Paused notification was delivered960 * after the machine execution was resumed. It's not fatal: */961 if (dsp.isOk())962 {963 dimImage(shot);964 }965 m_pauseShot = QPixmap::fromImage(shot);966 /* Fully repaint to pick up m_pauseShot: */967 viewport()->repaint();968 }969 /* Reuse the focus event handler to uncapture keyboard: */970 if (hasFocus())971 focusEvent(false /* aHasFocus*/, false /* aReleaseHostKey */);972 break;973 }974 case KMachineState_Stuck:975 {976 /* Reuse the focus event handler to uncapture keyboard: */977 if (hasFocus())978 focusEvent(false /* aHasFocus*/, false /* aReleaseHostKey */);979 break;980 }981 case KMachineState_Running:982 {983 if ( m_previousState == KMachineState_Paused984 || m_previousState == KMachineState_TeleportingPausedVM985 || m_previousState == KMachineState_Restoring)986 {987 if (vboxGlobal().vmRenderMode() != VBoxDefs::TimerMode && m_pFrameBuffer)988 {989 /* Reset the pixmap to free memory: */990 m_pauseShot = QPixmap();991 /* Ask for full guest display update (it will also update992 * the viewport through IFramebuffer::NotifyUpdate): */993 CDisplay dsp = session().GetConsole().GetDisplay();994 dsp.InvalidateAndUpdate();995 }996 }997 /* Reuse the focus event handler to capture keyboard: */998 if (hasFocus())999 focusEvent(true /* aHasFocus */);1000 break;1001 }1002 default:1003 break;1004 }1005 1006 m_previousState = state;1007 }1008 1009 void UIMachineView::focusEvent(bool fHasFocus, bool fReleaseHostKey /* = true */)1010 {1011 if (fHasFocus)1012 {1013 #ifdef Q_WS_WIN321014 if (!uisession()->isAutoCaptureDisabled() && m_globalSettings.autoCapture() && GetAncestor(winId(), GA_ROOT) == GetForegroundWindow())1015 #else1016 if (!uisession()->isAutoCaptureDisabled() && m_globalSettings.autoCapture())1017 #endif1018 {1019 captureKbd(true);1020 }1021 1022 /* Reset the single-time disable capture flag: */1023 if (uisession()->isAutoCaptureDisabled())1024 uisession()->setAutoCaptureDisabled(false);1025 }1026 694 else 1027 { 1028 captureKbd(false, false); 1029 releaseAllPressedKeys(fReleaseHostKey); 1030 } 1031 } 1032 1033 bool UIMachineView::keyEvent(int iKey, uint8_t uScan, int fFlags, wchar_t *pUniKey /* = NULL */) 1034 { 1035 const bool isHostKey = iKey == m_globalSettings.hostKey(); 1036 1037 LONG buf[16]; 1038 LONG *codes = buf; 1039 uint count = 0; 1040 uint8_t whatPressed = 0; 1041 1042 if (!isHostKey && !m_bIsHostkeyPressed) 1043 { 1044 if (fFlags & KeyPrint) 1045 { 1046 static LONG PrintMake[] = { 0xE0, 0x2A, 0xE0, 0x37 }; 1047 static LONG PrintBreak[] = { 0xE0, 0xB7, 0xE0, 0xAA }; 1048 if (fFlags & KeyPressed) 1049 { 1050 codes = PrintMake; 1051 count = SIZEOF_ARRAY(PrintMake); 1052 } 1053 else 1054 { 1055 codes = PrintBreak; 1056 count = SIZEOF_ARRAY(PrintBreak); 1057 } 1058 } 1059 else if (fFlags & KeyPause) 1060 { 1061 if (fFlags & KeyPressed) 1062 { 1063 static LONG Pause[] = { 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 }; 1064 codes = Pause; 1065 count = SIZEOF_ARRAY(Pause); 1066 } 1067 else 1068 { 1069 /* Pause shall not produce a break code */ 1070 return true; 1071 } 1072 } 1073 else 1074 { 1075 if (fFlags & KeyPressed) 1076 { 1077 /* Check if the guest has the same view on the modifier keys (NumLock, 1078 * CapsLock, ScrollLock) as the X server. If not, send KeyPress events 1079 * to synchronize the state. */ 1080 fixModifierState(codes, &count); 1081 } 1082 1083 /* Check if it's C-A-D and GUI/PassCAD is not true */ 1084 if (!m_fPassCAD && 1085 uScan == 0x53 /* Del */ && 1086 ((m_pressedKeys[0x38] & IsKeyPressed) /* Alt */ || 1087 (m_pressedKeys[0x38] & IsExtKeyPressed)) && 1088 ((m_pressedKeys[0x1d] & IsKeyPressed) /* Ctrl */ || 1089 (m_pressedKeys[0x1d] & IsExtKeyPressed))) 1090 { 1091 /* Use the C-A-D combination as a last resort to get the 1092 * keyboard and mouse back to the host when the user forgets 1093 * the Host Key. Note that it's always possible to send C-A-D 1094 * to the guest using the Host+Del combination. BTW, it would 1095 * be preferrable to completely ignore C-A-D in guests, but 1096 * that's not possible because we cannot predict what other 1097 * keys will be pressed next when one of C, A, D is held. */ 1098 if (uisession()->isRunning() && m_bIsKeyboardCaptured) 1099 { 1100 captureKbd(false); 1101 if (!(uisession()->isMouseSupportsAbsolute() && uisession()->isMouseIntegrated())) 1102 machineLogic()->mouseHandler()->captureMouse(screenId()); 1103 } 1104 1105 return true; 1106 } 1107 1108 /* Process the scancode and update the table of pressed keys: */ 1109 whatPressed = IsKeyPressed; 1110 1111 if (fFlags & KeyExtended) 1112 { 1113 codes[count++] = 0xE0; 1114 whatPressed = IsExtKeyPressed; 1115 } 1116 1117 if (fFlags & KeyPressed) 1118 { 1119 codes[count++] = uScan; 1120 m_pressedKeys[uScan] |= whatPressed; 1121 } 1122 else 1123 { 1124 /* If we haven't got this key's press message, we ignore its release: */ 1125 if (!(m_pressedKeys[uScan] & whatPressed)) 1126 return true; 1127 codes[count++] = uScan | 0x80; 1128 m_pressedKeys[uScan] &= ~whatPressed; 1129 } 1130 1131 if (m_bIsKeyboardCaptured) 1132 m_pressedKeys[uScan] |= IsKbdCaptured; 1133 else 1134 m_pressedKeys[uScan] &= ~IsKbdCaptured; 1135 } 1136 } 1137 else 1138 { 1139 /* Currently this is used in winLowKeyboardEvent() only: */ 1140 m_bIsHostkeyInCapture = m_bIsKeyboardCaptured; 1141 } 1142 1143 bool emitSignal = false; 1144 int hotkey = 0; 1145 1146 /* Process the host key: */ 1147 if (fFlags & KeyPressed) 1148 { 1149 if (isHostKey) 1150 { 1151 if (!m_bIsHostkeyPressed) 1152 { 1153 m_bIsHostkeyPressed = m_bIsHostkeyAlone = true; 695 696 /* Check if pWatchedObject object is view: */ 697 if (UIMachineView *pWatchedView = isItListenedView(pWatchedObject)) 698 { 699 /* Get corresponding screen index: */ 700 ulong uScreenId = m_views.key(pWatchedView); 701 NOREF(uScreenId); 702 /* Handle view events: */ 703 switch (pEvent->type()) 704 { 705 case QEvent::FocusIn: 1154 706 if (uisession()->isRunning()) 1155 saveKeyStates(); 1156 emitSignal = true; 1157 } 1158 } 1159 else 1160 { 1161 if (m_bIsHostkeyPressed) 1162 { 1163 if (m_bIsHostkeyAlone) 1164 { 1165 hotkey = iKey; 1166 m_bIsHostkeyAlone = false; 1167 } 1168 } 1169 } 1170 } 1171 else 1172 { 1173 if (isHostKey) 1174 { 1175 if (m_bIsHostkeyPressed) 1176 { 1177 m_bIsHostkeyPressed = false; 1178 1179 if (m_bIsHostkeyAlone) 1180 { 707 { 708 /* Capture keyboard: */ 709 #ifdef Q_WS_WIN 710 if (!uisession()->isAutoCaptureDisabled() && m_globalSettings.autoCapture() && 711 GetAncestor(pWatchedView->winId(), GA_ROOT) == GetForegroundWindow()) 712 #else /* Q_WS_WIN */ 713 if (!uisession()->isAutoCaptureDisabled() && m_globalSettings.autoCapture()) 714 #endif /* !Q_WS_WIN */ 715 captureKeyboard(uScreenId); 716 /* Reset the single-time disable capture flag: */ 717 if (uisession()->isAutoCaptureDisabled()) 718 uisession()->setAutoCaptureDisabled(false); 719 } 720 break; 721 case QEvent::FocusOut: 722 /* Release keyboard: */ 723 if (uisession()->isRunning()) 724 releaseKeyboard(); 725 /* And all pressed keys: */ 726 releaseAllPressedKeys(true); 727 break; 728 case QEvent::KeyPress: 729 case QEvent::KeyRelease: 730 { 731 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent); 732 733 if (m_bIsHostkeyPressed && pEvent->type() == QEvent::KeyPress) 734 { 735 /* Passing F1-F12 keys to the guest: */ 736 if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F12) 737 { 738 QVector <LONG> combo(6); 739 combo[0] = 0x1d; /* Ctrl down */ 740 combo[1] = 0x38; /* Alt down */ 741 combo[4] = 0xb8; /* Alt up */ 742 combo[5] = 0x9d; /* Ctrl up */ 743 if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F10) 744 { 745 combo[2] = 0x3b + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 down */ 746 combo[3] = 0xbb + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 up */ 747 } 748 /* There is some scan slice between F10 and F11 keys, so its separated: */ 749 else if (pKeyEvent->key() >= Qt::Key_F11 && pKeyEvent->key() <= Qt::Key_F12) 750 { 751 combo[2] = 0x57 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 down */ 752 combo[3] = 0xd7 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 up */ 753 } 754 CKeyboard keyboard = session().GetConsole().GetKeyboard(); 755 keyboard.PutScancodes(combo); 756 } 757 /* Process hot keys not processed in keyEvent() (as in case of non-alphanumeric keys): */ 758 machineLogic()->actionsPool()->processHotKey(QKeySequence(pKeyEvent->key())); 759 } 760 else if (!m_bIsHostkeyPressed && pEvent->type() == QEvent::KeyRelease) 761 { 762 /* Show a possible warning on key release which seems to be more expected by the end user: */ 1181 763 if (uisession()->isPaused()) 1182 764 { 1183 vboxProblem().remindAboutPausedVMInput(); 765 /* If the reminder is disabled we pass the event to Qt to enable normal 766 * keyboard functionality (for example, menu access with Alt+Letter): */ 767 if (!vboxProblem().remindAboutPausedVMInput()) 768 break; 1184 769 } 1185 else if (uisession()->isRunning()) 1186 { 1187 bool captured = m_bIsKeyboardCaptured; 1188 bool ok = true; 1189 if (!captured) 1190 { 1191 /* temporarily disable auto capture that will take 1192 * place after this dialog is dismissed because 1193 * the capture state is to be defined by the 1194 * dialog result itself */ 1195 uisession()->setAutoCaptureDisabled(true); 1196 bool autoConfirmed = false; 1197 ok = vboxProblem().confirmInputCapture (&autoConfirmed); 1198 if (autoConfirmed) 1199 uisession()->setAutoCaptureDisabled(false); 1200 /* otherwise, the disable flag will be reset in 1201 * the next console view's foucs in event (since 1202 * may happen asynchronously on some platforms, 1203 * after we return from this code) */ 1204 } 1205 1206 if (ok) 1207 { 1208 captureKbd (!captured, false); 1209 if (!(uisession()->isMouseSupportsAbsolute() && uisession()->isMouseIntegrated())) 1210 { 1211 #ifdef Q_WS_X11 1212 /* make sure that pending FocusOut events from the 1213 * previous message box are handled, otherwise the 1214 * mouse is immediately ungrabbed. */ 1215 qApp->processEvents(); 1216 #endif 1217 if (m_bIsKeyboardCaptured) 1218 machineLogic()->mouseHandler()->captureMouse(screenId()); 1219 else 1220 machineLogic()->mouseHandler()->releaseMouse(); 1221 } 1222 } 1223 } 1224 } 1225 1226 if (uisession()->isRunning()) 1227 sendChangedKeyStates(); 1228 1229 emitSignal = true; 1230 } 1231 } 1232 else 1233 { 1234 if (m_bIsHostkeyPressed) 1235 m_bIsHostkeyAlone = false; 1236 } 1237 } 1238 1239 /* emit the keyboard state change signal */ 1240 if (emitSignal) 1241 emitKeyboardStateChanged(); 1242 1243 /* Process Host+<key> shortcuts. currently, <key> is limited to 1244 * alphanumeric chars. Other Host+<key> combinations are handled in 1245 * event(). */ 1246 if (hotkey) 1247 { 1248 bool processed = false; 1249 #if defined (Q_WS_WIN32) 1250 NOREF(pUniKey); 1251 int n = GetKeyboardLayoutList(0, NULL); 1252 Assert (n); 1253 HKL *list = new HKL[n]; 1254 GetKeyboardLayoutList(n, list); 1255 for (int i = 0; i < n && !processed; i++) 1256 { 1257 wchar_t ch; 1258 static BYTE keys[256] = {0}; 1259 if (!ToUnicodeEx(hotkey, 0, keys, &ch, 1, 0, list[i]) == 1) 1260 ch = 0; 1261 if (ch) 1262 processed = machineWindowWrapper()->machineLogic()->actionsPool()->processHotKey(QKeySequence((Qt::UNICODE_ACCEL + QChar(ch).toUpper().unicode()))); 1263 } 1264 delete[] list; 1265 #elif defined (Q_WS_X11) 1266 NOREF(pUniKey); 1267 Display *display = QX11Info::display(); 1268 int keysyms_per_keycode = getKeysymsPerKeycode(); 1269 KeyCode kc = XKeysymToKeycode (display, iKey); 1270 for (int i = 0; i < keysyms_per_keycode && !processed; i += 2) 1271 { 1272 KeySym ks = XKeycodeToKeysym(display, kc, i); 1273 char ch = 0; 1274 if (!XkbTranslateKeySym(display, &ks, 0, &ch, 1, NULL) == 1) 1275 ch = 0; 1276 if (ch) 1277 QChar c = QString::fromLocal8Bit(&ch, 1)[0]; 1278 } 1279 #elif defined (Q_WS_MAC) 1280 if (pUniKey && pUniKey[0] && !pUniKey[1]) 1281 processed = machineWindowWrapper()->machineLogic()->actionsPool()->processHotKey(QKeySequence(Qt::UNICODE_ACCEL + QChar(pUniKey[0]).toUpper().unicode())); 1282 1283 /* Don't consider the hot key as pressed since the guest never saw 1284 * it. (probably a generic thing) */ 1285 m_pressedKeys[uScan] &= ~whatPressed; 1286 #endif 1287 1288 /* Grab the key from Qt if processed, or pass it to Qt otherwise 1289 * in order to process non-alphanumeric keys in event(), after they are 1290 * converted to Qt virtual keys. */ 1291 return processed; 1292 } 1293 1294 /* No more to do, if the host key is in action or the VM is paused: */ 1295 if (m_bIsHostkeyPressed || isHostKey || uisession()->isPaused()) 1296 { 1297 /* Grab the key from Qt and from VM if it's a host key, 1298 * otherwise just pass it to Qt */ 1299 return isHostKey; 1300 } 1301 1302 CKeyboard keyboard = session().GetConsole().GetKeyboard(); 1303 Assert(!keyboard.isNull()); 1304 1305 #if defined (Q_WS_WIN32) 1306 /* send pending WM_PAINT events */ 1307 ::UpdateWindow(viewport()->winId()); 1308 #endif 1309 1310 std::vector <LONG> scancodes(codes, &codes[count]); 1311 keyboard.PutScancodes(QVector<LONG>::fromStdVector(scancodes)); 1312 1313 /* Grab the key from Qt: */ 1314 return true; 1315 } 1316 1317 void UIMachineView::resizeEvent(QResizeEvent *pEvent) 1318 { 1319 updateSliders(); 1320 return QAbstractScrollArea::resizeEvent(pEvent); 1321 } 1322 1323 void UIMachineView::moveEvent(QMoveEvent *pEvent) 1324 { 1325 return QAbstractScrollArea::moveEvent(pEvent); 1326 } 1327 1328 void UIMachineView::paintEvent(QPaintEvent *pPaintEvent) 1329 { 1330 if (m_pauseShot.isNull()) 1331 { 1332 /* Delegate the paint function to the VBoxFrameBuffer interface: */ 1333 if (m_pFrameBuffer) 1334 m_pFrameBuffer->paintEvent(pPaintEvent); 1335 #ifdef Q_WS_MAC 1336 /* Update the dock icon if we are in the running state */ 1337 if (uisession()->isRunning()) 1338 updateDockIcon(); 1339 #endif /* Q_WS_MAC */ 1340 return; 1341 } 1342 1343 #ifdef VBOX_GUI_USE_QUARTZ2D 1344 if (vboxGlobal().vmRenderMode() == VBoxDefs::Quartz2DMode && m_pFrameBuffer) 1345 { 1346 m_pFrameBuffer->paintEvent(pPaintEvent); 1347 updateDockIcon(); 1348 } 1349 else 1350 #endif /* VBOX_GUI_USE_QUARTZ2D */ 1351 { 1352 /* We have a snapshot for the paused state: */ 1353 QRect r = pPaintEvent->rect().intersect(viewport()->rect()); 1354 /* We have to disable paint on screen if we are using the regular painter: */ 1355 bool paintOnScreen = viewport()->testAttribute(Qt::WA_PaintOnScreen); 1356 viewport()->setAttribute(Qt::WA_PaintOnScreen, false); 1357 QPainter pnt(viewport()); 1358 pnt.drawPixmap(r.x(), r.y(), m_pauseShot, r.x() + contentsX(), r.y() + contentsY(), r.width(), r.height()); 1359 /* Restore the attribute to its previous state: */ 1360 viewport()->setAttribute(Qt::WA_PaintOnScreen, paintOnScreen); 1361 #ifdef Q_WS_MAC 1362 updateDockIcon(); 1363 #endif /* Q_WS_MAC */ 1364 } 1365 } 1366 1367 #if defined(Q_WS_WIN32) 1368 1369 LRESULT CALLBACK UIMachineView::lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) 1370 { 1371 if (gView && nCode == HC_ACTION && gView->winLowKeyboardEvent(wParam, *(KBDLLHOOKSTRUCT *)lParam)) 770 } 771 break; 772 } 773 default: 774 break; 775 } 776 } 777 778 /* Else just propagate to base-class: */ 779 return QObject::eventFilter(pWatchedObject, pEvent); 780 } 781 782 #if defined(Q_WS_WIN) 783 784 LRESULT CALLBACK UIKeyboardHandler::lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) 785 { 786 if (nCode == HC_ACTION && m_pKeyboardHandler->winLowKeyboardEvent(wParam, *(KBDLLHOOKSTRUCT*)lParam)) 1372 787 return 1; 1373 788 … … 1375 790 } 1376 791 1377 bool UI MachineView::winLowKeyboardEvent(UINT msg, const KBDLLHOOKSTRUCT &event)792 bool UIKeyboardHandler::winLowKeyboardEvent(UINT msg, const KBDLLHOOKSTRUCT &event) 1378 793 { 1379 794 /* Sometimes it happens that Win inserts additional events on some key … … 1387 802 * of these strange events. Let's hope we are not missing anything else 1388 803 * of importance! */ 1389 if ( hasFocus() && (event.scanCode & ~0xFF))804 if (m_views[m_iKeyboardHookViewIndex]->hasFocus() && (event.scanCode & ~0xFF)) 1390 805 { 1391 806 if (event.vkCode == VK_CAPITAL) … … 1395 810 } 1396 811 1397 if (!m_ bIsKeyboardCaptured)812 if (!m_fIsKeyboardCaptured) 1398 813 return false; 1399 814 1400 /* it's possible that a key has been pressed while the keyboard was not815 /* It's possible that a key has been pressed while the keyboard was not 1401 816 * captured, but is being released under the capture. Detect this situation 1402 817 * and return false to let Windows process the message normally and update … … 1409 824 1410 825 MSG message; 1411 message.hwnd = winId();826 message.hwnd = m_views[m_iKeyboardHookViewIndex]->winId(); 1412 827 message.message = msg; 1413 828 message.wParam = event.vkCode; … … 1419 834 message.lParam &= ~0x1000000; 1420 835 1421 /* we suppose here that this hook is always called on the main GUI thread */836 /* We suppose here that this hook is always called on the main GUI thread */ 1422 837 long dummyResult; 1423 return winEvent(&message, &dummyResult); 1424 } 1425 1426 bool UIMachineView::winEvent(MSG *aMsg, long* /* aResult */) 1427 { 1428 if (!(aMsg->message == WM_KEYDOWN || aMsg->message == WM_SYSKEYDOWN || 1429 aMsg->message == WM_KEYUP || aMsg->message == WM_SYSKEYUP)) 838 return m_views[m_iKeyboardHookViewIndex]->winEvent(&message, &dummyResult); 839 } 840 841 #elif defined(Q_WS_MAC) 842 843 void UIKeyboardHandler::darwinGrabKeyboardEvents(bool fGrab) 844 { 845 m_fKeyboardGrabbed = fGrab; 846 if (fGrab) 847 { 848 /* Disable mouse and keyboard event compression/delaying to make sure we *really* get all of the events. */ 849 ::CGSetLocalEventsSuppressionInterval(0.0); 850 machineLogic()->mouseHandler()->setMouseCoalescingEnabled(false); 851 852 /* Register the event callback/hook and grab the keyboard. */ 853 UICocoaApplication::instance()->registerForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */, 854 UIKeyboardHandler::darwinEventHandlerProc, this); 855 856 ::DarwinGrabKeyboard(false); 857 } 858 else 859 { 860 ::DarwinReleaseKeyboard(); 861 UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */, 862 UIKeyboardHandler::darwinEventHandlerProc, this); 863 } 864 } 865 866 bool UIKeyboardHandler::darwinEventHandlerProc(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser) 867 { 868 UIKeyboardHandler *pKeyboardHandler = (UIKeyboardHandler*)pvUser; 869 EventRef inEvent = (EventRef)pvCarbonEvent; 870 UInt32 eventClass = ::GetEventClass(inEvent); 871 872 /* Check if this is an application key combo. In that case we will not pass 873 * the event to the guest, but let the host process it. */ 874 if (::darwinIsApplicationCommand(pvCocoaEvent)) 1430 875 return false; 1431 876 1432 /* Check for the special flag possibly set at the end of this function */ 1433 if (aMsg->lParam & (0x1 << 25)) 1434 { 1435 aMsg->lParam &= ~(0x1 << 25); 1436 return false; 1437 } 1438 1439 int scan = (aMsg->lParam >> 16) & 0x7F; 1440 /* scancodes 0x80 and 0x00 are ignored */ 1441 if (!scan) 1442 return true; 1443 1444 int vkey = aMsg->wParam; 1445 1446 /* When one of the SHIFT keys is held and one of the cursor movement 1447 * keys is pressed, Windows duplicates SHIFT press/release messages, 1448 * but with the virtual key code set to 0xFF. These virtual keys are also 1449 * sent in some other situations (Pause, PrtScn, etc.). Ignore such 1450 * messages. */ 1451 if (vkey == 0xFF) 1452 return true; 1453 1454 int flags = 0; 1455 if (aMsg->lParam & 0x1000000) 1456 flags |= KeyExtended; 1457 if (!(aMsg->lParam & 0x80000000)) 1458 flags |= KeyPressed; 1459 1460 switch (vkey) 1461 { 1462 case VK_SHIFT: 1463 case VK_CONTROL: 1464 case VK_MENU: 1465 { 1466 /* overcome stupid Win32 modifier key generalization */ 1467 int keyscan = scan; 1468 if (flags & KeyExtended) 1469 keyscan |= 0xE000; 1470 switch (keyscan) 1471 { 1472 case 0x002A: vkey = VK_LSHIFT; break; 1473 case 0x0036: vkey = VK_RSHIFT; break; 1474 case 0x001D: vkey = VK_LCONTROL; break; 1475 case 0xE01D: vkey = VK_RCONTROL; break; 1476 case 0x0038: vkey = VK_LMENU; break; 1477 case 0xE038: vkey = VK_RMENU; break; 1478 } 1479 break; 1480 } 1481 case VK_NUMLOCK: 1482 /* Win32 sets the extended bit for the NumLock key. Reset it. */ 1483 flags &= ~KeyExtended; 1484 break; 1485 case VK_SNAPSHOT: 1486 flags |= KeyPrint; 1487 break; 1488 case VK_PAUSE: 1489 flags |= KeyPause; 1490 break; 1491 } 1492 1493 bool result = keyEvent(vkey, scan, flags); 1494 if (!result && m_bIsKeyboardCaptured) 1495 { 1496 /* keyEvent() returned that it didn't process the message, but since the 1497 * keyboard is captured, we don't want to pass it to Windows. We just want 1498 * to let Qt process the message (to handle non-alphanumeric <HOST>+key 1499 * shortcuts for example). So send it direcltly to the window with the 1500 * special flag in the reserved area of lParam (to avoid recursion). */ 1501 ::SendMessage(aMsg->hwnd, aMsg->message, 1502 aMsg->wParam, aMsg->lParam | (0x1 << 25)); 1503 return true; 1504 } 1505 1506 /* These special keys have to be handled by Windows as well to update the 1507 * internal modifier state and to enable/disable the keyboard LED */ 1508 if (vkey == VK_NUMLOCK || vkey == VK_CAPITAL || vkey == VK_LSHIFT || vkey == VK_RSHIFT) 1509 return false; 1510 1511 return result; 1512 } 1513 1514 #elif defined(Q_WS_PM) 1515 1516 bool UIMachineView::pmEvent(QMSG *aMsg) 1517 { 1518 if (aMsg->msg == UM_PREACCEL_CHAR) 1519 { 1520 /* We are inside the input hook 1521 * let the message go through the normal system pipeline. */ 1522 if (!m_bIsKeyboardCaptured) 1523 return false; 1524 } 1525 1526 if (aMsg->msg != WM_CHAR && aMsg->msg != UM_PREACCEL_CHAR) 1527 return false; 1528 1529 /* check for the special flag possibly set at the end of this function */ 1530 if (SHORT2FROMMP(aMsg->mp2) & 0x8000) 1531 { 1532 aMsg->mp2 = MPFROM2SHORT(SHORT1FROMMP(aMsg->mp2), SHORT2FROMMP(aMsg->mp2) & ~0x8000); 1533 return false; 1534 } 1535 1536 USHORT ch = SHORT1FROMMP(aMsg->mp2); 1537 USHORT f = SHORT1FROMMP(aMsg->mp1); 1538 1539 int scan = (unsigned int)CHAR4FROMMP(aMsg->mp1); 1540 if (!scan || scan > 0x7F) 1541 return true; 1542 1543 int vkey = QIHotKeyEdit::virtualKey(aMsg); 1544 1545 int flags = 0; 1546 1547 if ((ch & 0xFF) == 0xE0) 1548 { 1549 flags |= KeyExtended; 1550 scan = ch >> 8; 1551 } 1552 else if (scan == 0x5C && (ch & 0xFF) == '/') 1553 { 1554 /* this is the '/' key on the keypad */ 1555 scan = 0x35; 1556 flags |= KeyExtended; 1557 } 1558 else 1559 { 1560 /* For some keys, the scan code passed in QMSG is a pseudo scan 1561 * code. We replace it with a real hardware scan code, according to 1562 * http://www.computer-engineering.org/ps2keyboard/scancodes1.html. 1563 * Also detect Pause and PrtScn and set flags. */ 1564 switch (vkey) 1565 { 1566 case VK_ENTER: scan = 0x1C; flags |= KeyExtended; break; 1567 case VK_CTRL: scan = 0x1D; flags |= KeyExtended; break; 1568 case VK_ALTGRAF: scan = 0x38; flags |= KeyExtended; break; 1569 case VK_LWIN: scan = 0x5B; flags |= KeyExtended; break; 1570 case VK_RWIN: scan = 0x5C; flags |= KeyExtended; break; 1571 case VK_WINMENU: scan = 0x5D; flags |= KeyExtended; break; 1572 case VK_FORWARD: scan = 0x69; flags |= KeyExtended; break; 1573 case VK_BACKWARD: scan = 0x6A; flags |= KeyExtended; break; 1574 #if 0 1575 /// @todo this would send 0xE0 0x46 0xE0 0xC6. It's not fully 1576 // clear what is more correct 1577 case VK_BREAK: scan = 0x46; flags |= KeyExtended; break; 1578 #else 1579 case VK_BREAK: scan = 0; flags |= KeyPause; break; 1580 #endif 1581 case VK_PAUSE: scan = 0; flags |= KeyPause; break; 1582 case VK_PRINTSCRN: scan = 0; flags |= KeyPrint; break; 1583 default:; 1584 } 1585 } 1586 1587 if (!(f & KC_KEYUP)) 1588 flags |= KeyPressed; 1589 1590 bool result = keyEvent (vkey, scan, flags); 1591 if (!result && m_bIsKeyboardCaptured) 1592 { 1593 /* keyEvent() returned that it didn't process the message, but since the 1594 * keyboard is captured, we don't want to pass it to PM. We just want 1595 * to let Qt process the message (to handle non-alphanumeric <HOST>+key 1596 * shortcuts for example). So send it direcltly to the window with the 1597 * special flag in the reserved area of lParam (to avoid recursion). */ 1598 ::WinSendMsg (aMsg->hwnd, WM_CHAR, aMsg->mp1, 1599 MPFROM2SHORT (SHORT1FROMMP (aMsg->mp2), SHORT2FROMMP (aMsg->mp2) | 0x8000)); 1600 return true; 1601 } 1602 return result; 1603 } 1604 1605 #elif defined(Q_WS_X11) 1606 1607 static Bool VBoxConsoleViewCompEvent(Display *, XEvent *pEvent, XPointer pvArg) 1608 { 1609 XEvent *pKeyEvent = (XEvent*)pvArg; 1610 if ((pEvent->type == XKeyPress) && (pEvent->xkey.keycode == pKeyEvent->xkey.keycode)) 1611 return True; 1612 else 1613 return False; 1614 } 1615 1616 bool UIMachineView::x11Event(XEvent *pEvent) 1617 { 1618 switch (pEvent->type) 1619 { 1620 /* We have to handle XFocusOut right here as this event is not passed 1621 * to UIMachineView::event(). Handling this event is important for 1622 * releasing the keyboard before the screen saver gets active. 1623 * 1624 * See public ticket #3894: Apparently this makes problems with newer 1625 * versions of Qt and this hack is probably not necessary anymore. 1626 * So disable it for Qt >= 4.5.0. */ 1627 case XFocusOut: 1628 case XFocusIn: 1629 if (uisession()->isRunning()) 1630 { 1631 if (VBoxGlobal::qtRTVersion() < ((4 << 16) | (5 << 8) | 0)) 1632 { 1633 focusEvent(pEvent->type == XFocusIn); 1634 if (pEvent->type == XFocusOut) 1635 machineLogic()->mouseHandler()->releaseMouse(); 1636 } 1637 } 1638 return false; 1639 case XKeyPress: 1640 case XKeyRelease: 1641 break; 1642 default: 1643 return false; /* pass the event to Qt */ 1644 } 1645 1646 /* Translate the keycode to a PC scan code. */ 1647 unsigned scan = handleXKeyEvent(pEvent); 1648 1649 /* scancodes 0x00 (no valid translation) and 0x80 are ignored */ 1650 if (!scan & 0x7F) 1651 return true; 1652 1653 /* Fix for http://www.virtualbox.org/ticket/1296: 1654 * when X11 sends events for repeated keys, it always inserts an 1655 * XKeyRelease before the XKeyPress. */ 1656 XEvent returnEvent; 1657 if ((pEvent->type == XKeyRelease) && (XCheckIfEvent(pEvent->xkey.display, &returnEvent, 1658 VBoxConsoleViewCompEvent, (XPointer)pEvent) == True)) 1659 { 1660 XPutBackEvent(pEvent->xkey.display, &returnEvent); 1661 /* Discard it, don't pass it to Qt. */ 1662 return true; 1663 } 1664 1665 KeySym ks = ::XKeycodeToKeysym(pEvent->xkey.display, pEvent->xkey.keycode, 0); 1666 1667 int flags = 0; 1668 if (scan >> 8) 1669 flags |= KeyExtended; 1670 if (pEvent->type == XKeyPress) 1671 flags |= KeyPressed; 1672 1673 /* Remove the extended flag */ 1674 scan &= 0x7F; 1675 1676 switch (ks) 1677 { 1678 case XK_Print: 1679 flags |= KeyPrint; 1680 break; 1681 case XK_Pause: 1682 flags |= KeyPause; 1683 break; 1684 } 1685 1686 return keyEvent(ks, scan, flags); 1687 } 1688 1689 #elif defined(Q_WS_MAC) 1690 1691 bool UIMachineView::darwinKeyboardEvent(const void *pvCocoaEvent, EventRef inEvent) 877 /* All keyboard class events needs to be handled. */ 878 if (eventClass == kEventClassKeyboard) 879 { 880 if (pKeyboardHandler->darwinKeyboardEvent(pvCocoaEvent, inEvent)) 881 return true; 882 } 883 /* Pass the event along. */ 884 return false; 885 } 886 887 bool UIKeyboardHandler::darwinKeyboardEvent(const void *pvCocoaEvent, EventRef inEvent) 1692 888 { 1693 889 bool ret = false; … … 1695 891 if (EventKind != kEventRawKeyModifiersChanged) 1696 892 { 1697 /* convert keycode to set 1 scan code. */893 /* Convert keycode to set 1 scan code. */ 1698 894 UInt32 keyCode = ~0U; 1699 895 ::GetEventParameter(inEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof (keyCode), NULL, &keyCode); 1700 u nsignedscanCode = ::DarwinKeycodeToSet1Scancode(keyCode);896 uint8_t scanCode = ::DarwinKeycodeToSet1Scancode(keyCode); 1701 897 if (scanCode) 1702 898 { 1703 /* calc flags. */899 /* Calc flags. */ 1704 900 int flags = 0; 1705 901 if (EventKind != kEventRawKeyUp) … … 1710 906 scanCode &= VBOXKEY_SCANCODE_MASK; 1711 907 1712 /* get the unicode string (if present). */908 /* Get the unicode string (if present). */ 1713 909 AssertCompileSize(wchar_t, 2); 1714 910 AssertCompileSize(UniChar, 2); … … 1720 916 ucs[cbWritten / sizeof(wchar_t)] = 0; /* The api doesn't terminate it. */ 1721 917 1722 ret = keyEvent(keyCode, scanCode, flags, u cs[0] ? ucs : NULL);918 ret = keyEvent(keyCode, scanCode, flags, uScreenId, ucs[0] ? ucs : NULL); 1723 919 } 1724 920 } … … 1737 933 if (!(changed & (1 << bit))) 1738 934 continue; 1739 u nsignedscanCode = ::DarwinModifierMaskToSet1Scancode(1 << bit);935 uint8_t scanCode = ::DarwinModifierMaskToSet1Scancode(1 << bit); 1740 936 if (!scanCode) 1741 937 continue; … … 1749 945 flags |= KeyExtended; 1750 946 scanCode &= VBOXKEY_SCANCODE_MASK; 1751 ret |= keyEvent(keyCode, scanCode & 0xff, flags );947 ret |= keyEvent(keyCode, scanCode & 0xff, flags, uScreenId); 1752 948 } 1753 949 else … … 1757 953 flags |= KeyExtended; 1758 954 scanCode &= VBOXKEY_SCANCODE_MASK; 1759 keyEvent(keyCode, scanCode, flags | KeyPressed );1760 keyEvent(keyCode, scanCode, flags );955 keyEvent(keyCode, scanCode, flags | KeyPressed, uScreenId); 956 keyEvent(keyCode, scanCode, flags, uScreenId); 1761 957 } 1762 958 } … … 1773 969 } 1774 970 1775 void UIMachineView::darwinGrabKeyboardEvents(bool fGrab) 1776 { 1777 m_fKeyboardGrabbed = fGrab; 1778 if (fGrab) 1779 { 1780 /* Disable mouse and keyboard event compression/delaying to make sure we *really* get all of the events. */ 1781 ::CGSetLocalEventsSuppressionInterval(0.0); 1782 machineLogic()->mouseHandler()->setMouseCoalescingEnabled(false); 1783 1784 /* Register the event callback/hook and grab the keyboard. */ 1785 UICocoaApplication::instance()->registerForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */, 1786 UIMachineView::darwinEventHandlerProc, this); 1787 1788 ::DarwinGrabKeyboard (false); 971 #endif 972 973 bool UIKeyboardHandler::keyEvent(int iKey, uint8_t uScan, int fFlags, ulong uScreenId, wchar_t *pUniKey /* = NULL */) 974 { 975 const bool isHostKey = iKey == m_globalSettings.hostKey(); 976 977 LONG buf[16]; 978 LONG *codes = buf; 979 uint count = 0; 980 uint8_t whatPressed = 0; 981 982 if (!isHostKey && !m_bIsHostkeyPressed) 983 { 984 if (fFlags & KeyPrint) 985 { 986 static LONG PrintMake[] = { 0xE0, 0x2A, 0xE0, 0x37 }; 987 static LONG PrintBreak[] = { 0xE0, 0xB7, 0xE0, 0xAA }; 988 if (fFlags & KeyPressed) 989 { 990 codes = PrintMake; 991 count = SIZEOF_ARRAY(PrintMake); 992 } 993 else 994 { 995 codes = PrintBreak; 996 count = SIZEOF_ARRAY(PrintBreak); 997 } 998 } 999 else if (fFlags & KeyPause) 1000 { 1001 if (fFlags & KeyPressed) 1002 { 1003 static LONG Pause[] = { 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 }; 1004 codes = Pause; 1005 count = SIZEOF_ARRAY(Pause); 1006 } 1007 else 1008 { 1009 /* Pause shall not produce a break code */ 1010 return true; 1011 } 1012 } 1013 else 1014 { 1015 if (fFlags & KeyPressed) 1016 { 1017 /* Check if the guest has the same view on the modifier keys (NumLock, 1018 * CapsLock, ScrollLock) as the X server. If not, send KeyPress events 1019 * to synchronize the state. */ 1020 fixModifierState(codes, &count); 1021 } 1022 1023 /* Check if it's C-A-D and GUI/PassCAD is not true */ 1024 if (!m_fPassCAD && 1025 uScan == 0x53 /* Del */ && 1026 ((m_pressedKeys[0x38] & IsKeyPressed) /* Alt */ || 1027 (m_pressedKeys[0x38] & IsExtKeyPressed)) && 1028 ((m_pressedKeys[0x1d] & IsKeyPressed) /* Ctrl */ || 1029 (m_pressedKeys[0x1d] & IsExtKeyPressed))) 1030 { 1031 /* Use the C-A-D combination as a last resort to get the 1032 * keyboard and mouse back to the host when the user forgets 1033 * the Host Key. Note that it's always possible to send C-A-D 1034 * to the guest using the Host+Del combination. BTW, it would 1035 * be preferrable to completely ignore C-A-D in guests, but 1036 * that's not possible because we cannot predict what other 1037 * keys will be pressed next when one of C, A, D is held. */ 1038 if (uisession()->isRunning() && m_fIsKeyboardCaptured) 1039 { 1040 releaseKeyboard(); 1041 if (!uisession()->isMouseSupportsAbsolute() || !uisession()->isMouseIntegrated()) 1042 machineLogic()->mouseHandler()->releaseMouse(); 1043 } 1044 return true; 1045 } 1046 1047 /* Process the scancode and update the table of pressed keys: */ 1048 whatPressed = IsKeyPressed; 1049 1050 if (fFlags & KeyExtended) 1051 { 1052 codes[count++] = 0xE0; 1053 whatPressed = IsExtKeyPressed; 1054 } 1055 1056 if (fFlags & KeyPressed) 1057 { 1058 codes[count++] = uScan; 1059 m_pressedKeys[uScan] |= whatPressed; 1060 } 1061 else 1062 { 1063 /* If we haven't got this key's press message, we ignore its release: */ 1064 if (!(m_pressedKeys[uScan] & whatPressed)) 1065 return true; 1066 codes[count++] = uScan | 0x80; 1067 m_pressedKeys[uScan] &= ~whatPressed; 1068 } 1069 1070 if (m_fIsKeyboardCaptured) 1071 m_pressedKeys[uScan] |= IsKbdCaptured; 1072 else 1073 m_pressedKeys[uScan] &= ~IsKbdCaptured; 1074 } 1789 1075 } 1790 1076 else 1791 1077 { 1792 ::DarwinReleaseKeyboard(); 1793 UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */, 1794 UIMachineView::darwinEventHandlerProc, this); 1795 } 1796 } 1797 1798 bool UIMachineView::darwinEventHandlerProc(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser) 1799 { 1800 UIMachineView *view = (UIMachineView*)pvUser; 1801 EventRef inEvent = (EventRef)pvCarbonEvent; 1802 UInt32 eventClass = ::GetEventClass(inEvent); 1803 1804 /* Check if this is an application key combo. In that case we will not pass 1805 * the event to the guest, but let the host process it. */ 1806 if (::darwinIsApplicationCommand(pvCocoaEvent)) 1807 return false; 1808 1809 /* All keyboard class events needs to be handled. */ 1810 if (eventClass == kEventClassKeyboard) 1811 { 1812 if (view->darwinKeyboardEvent (pvCocoaEvent, inEvent)) 1813 return true; 1814 } 1815 /* Pass the event along. */ 1816 return false; 1817 } 1818 1078 /* Currently this is used in winLowKeyboardEvent() only: */ 1079 m_bIsHostkeyInCapture = m_fIsKeyboardCaptured; 1080 } 1081 1082 bool emitSignal = false; 1083 int hotkey = 0; 1084 1085 /* Process the host key: */ 1086 if (fFlags & KeyPressed) 1087 { 1088 if (isHostKey) 1089 { 1090 if (!m_bIsHostkeyPressed) 1091 { 1092 m_bIsHostkeyPressed = m_bIsHostkeyAlone = true; 1093 if (uisession()->isRunning()) 1094 saveKeyStates(); 1095 emitSignal = true; 1096 } 1097 } 1098 else 1099 { 1100 if (m_bIsHostkeyPressed) 1101 { 1102 if (m_bIsHostkeyAlone) 1103 { 1104 hotkey = iKey; 1105 m_bIsHostkeyAlone = false; 1106 } 1107 } 1108 } 1109 } 1110 else 1111 { 1112 if (isHostKey) 1113 { 1114 if (m_bIsHostkeyPressed) 1115 { 1116 m_bIsHostkeyPressed = false; 1117 1118 if (m_bIsHostkeyAlone) 1119 { 1120 if (uisession()->isPaused()) 1121 { 1122 vboxProblem().remindAboutPausedVMInput(); 1123 } 1124 else if (uisession()->isRunning()) 1125 { 1126 bool ok = true; 1127 if (!m_fIsKeyboardCaptured) 1128 { 1129 /* Temporarily disable auto capture that will take 1130 * place after this dialog is dismissed because 1131 * the capture state is to be defined by the 1132 * dialog result itself */ 1133 uisession()->setAutoCaptureDisabled(true); 1134 bool autoConfirmed = false; 1135 ok = vboxProblem().confirmInputCapture(&autoConfirmed); 1136 if (autoConfirmed) 1137 uisession()->setAutoCaptureDisabled(false); 1138 /* Otherwise, the disable flag will be reset in 1139 * the next console view's foucs in event (since 1140 * may happen asynchronously on some platforms, 1141 * after we return from this code) */ 1142 } 1143 1144 if (ok) 1145 { 1146 if (m_fIsKeyboardCaptured) 1147 releaseKeyboard(); 1148 else 1149 captureKeyboard(uScreenId); 1150 if (!uisession()->isMouseSupportsAbsolute() || !uisession()->isMouseIntegrated()) 1151 { 1152 #ifdef Q_WS_X11 1153 /* Make sure that pending FocusOut events from the 1154 * previous message box are handled, otherwise the 1155 * mouse is immediately ungrabbed. */ 1156 qApp->processEvents(); 1819 1157 #endif 1820 1821 void UIMachineView::fixModifierState(LONG *piCodes, uint *puCount) 1158 if (m_fIsKeyboardCaptured) 1159 machineLogic()->mouseHandler()->captureMouse(uScreenId); 1160 else 1161 machineLogic()->mouseHandler()->releaseMouse(); 1162 } 1163 } 1164 } 1165 } 1166 1167 if (uisession()->isRunning()) 1168 sendChangedKeyStates(); 1169 1170 emitSignal = true; 1171 } 1172 } 1173 else 1174 { 1175 if (m_bIsHostkeyPressed) 1176 m_bIsHostkeyAlone = false; 1177 } 1178 } 1179 1180 /* Notify all listeners: */ 1181 emit keyboardStateChanged(keyboardState()); 1182 1183 /* Process Host+<key> shortcuts. currently, <key> is limited to 1184 * alphanumeric chars. Other Host+<key> combinations are handled in 1185 * event(). */ 1186 if (hotkey) 1187 { 1188 bool processed = false; 1189 #if defined (Q_WS_WIN) 1190 NOREF(pUniKey); 1191 int n = GetKeyboardLayoutList(0, NULL); 1192 Assert(n); 1193 HKL *list = new HKL[n]; 1194 GetKeyboardLayoutList(n, list); 1195 for (int i = 0; i < n && !processed; i++) 1196 { 1197 wchar_t ch; 1198 static BYTE keys[256] = {0}; 1199 if (!ToUnicodeEx(hotkey, 0, keys, &ch, 1, 0, list[i]) == 1) 1200 ch = 0; 1201 if (ch) 1202 processed = machineLogic()->actionsPool()->processHotKey(QKeySequence((Qt::UNICODE_ACCEL + QChar(ch).toUpper().unicode()))); 1203 } 1204 delete[] list; 1205 #elif defined (Q_WS_X11) 1206 NOREF(pUniKey); 1207 Display *display = QX11Info::display(); 1208 int keysyms_per_keycode = getKeysymsPerKeycode(); 1209 KeyCode kc = XKeysymToKeycode (display, iKey); 1210 for (int i = 0; i < keysyms_per_keycode && !processed; i += 2) 1211 { 1212 KeySym ks = XKeycodeToKeysym(display, kc, i); 1213 char ch = 0; 1214 if (!XkbTranslateKeySym(display, &ks, 0, &ch, 1, NULL) == 1) 1215 ch = 0; 1216 if (ch) 1217 QChar c = QString::fromLocal8Bit(&ch, 1)[0]; 1218 } 1219 #elif defined (Q_WS_MAC) 1220 if (pUniKey && pUniKey[0] && !pUniKey[1]) 1221 processed = machineLogic()->actionsPool()->processHotKey(QKeySequence(Qt::UNICODE_ACCEL + QChar(pUniKey[0]).toUpper().unicode())); 1222 1223 /* Don't consider the hot key as pressed since the guest never saw 1224 * it. (probably a generic thing) */ 1225 m_pressedKeys[uScan] &= ~whatPressed; 1226 #endif 1227 /* Grab the key from Qt if processed, or pass it to Qt otherwise 1228 * in order to process non-alphanumeric keys in event(), after they are 1229 * converted to Qt virtual keys. */ 1230 return processed; 1231 } 1232 1233 /* No more to do, if the host key is in action or the VM is paused: */ 1234 if (m_bIsHostkeyPressed || isHostKey || uisession()->isPaused()) 1235 { 1236 /* Grab the key from Qt and from VM if it's a host key, 1237 * otherwise just pass it to Qt */ 1238 return isHostKey; 1239 } 1240 1241 CKeyboard keyboard = session().GetConsole().GetKeyboard(); 1242 Assert(!keyboard.isNull()); 1243 1244 #ifdef Q_WS_WIN 1245 /* Send pending WM_PAINT events: */ 1246 ::UpdateWindow(m_views[uScreenId]->viewport()->winId()); 1247 #endif /* Q_WS_WIN */ 1248 1249 std::vector <LONG> scancodes(codes, &codes[count]); 1250 keyboard.PutScancodes(QVector<LONG>::fromStdVector(scancodes)); 1251 1252 /* Grab the key from Qt: */ 1253 return true; 1254 } 1255 1256 void UIKeyboardHandler::fixModifierState(LONG *piCodes, uint *puCount) 1822 1257 { 1823 1258 /* Synchronize the views of the host and the guest to the modifier keys. 1824 1259 * This function will add up to 6 additional keycodes to codes. */ 1825 1826 1260 #if defined(Q_WS_X11) 1827 1828 1261 Window wDummy1, wDummy2; 1829 1262 int iDummy3, iDummy4, iDummy5, iDummy6; … … 1867 1300 } 1868 1301 } 1869 1870 #elif defined(Q_WS_WIN32) 1871 1302 #elif defined(Q_WS_WIN) 1872 1303 if (uisession()->numLockAdaptionCnt() && (uisession()->isNumLock() ^ !!(GetKeyState(VK_NUMLOCK)))) 1873 1304 { … … 1890 1321 } 1891 1322 } 1892 1893 1323 #elif defined(Q_WS_MAC) 1894 1895 1324 /* if (uisession()->numLockAdaptionCnt()) ... - NumLock isn't implemented by Mac OS X so ignore it. */ 1896 1325 if (uisession()->capsLockAdaptionCnt() && (uisession()->isCapsLock() ^ !!(::GetCurrentEventKeyModifiers() & alphaLock))) … … 1908 1337 } 1909 1338 } 1910 1911 1339 #else 1912 1913 //#warning Adapt UIMachineView::fixModifierState 1914 1340 //#warning Adapt UIKeyboardHandler::fixModifierState 1915 1341 #endif 1916 1342 } 1917 1343 1918 QPoint UIMachineView::viewportToContents(const QPoint &vp) const 1919 { 1920 return QPoint(vp.x() + contentsX(), vp.y() + contentsY()); 1921 } 1922 1923 void UIMachineView::scrollBy(int dx, int dy) 1924 { 1925 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + dx); 1926 verticalScrollBar()->setValue(verticalScrollBar()->value() + dy); 1927 } 1928 1929 #ifdef VBOX_WITH_VIDEOHWACCEL 1930 void UIMachineView::scrollContentsBy(int dx, int dy) 1931 { 1932 if (m_pFrameBuffer) 1933 { 1934 m_pFrameBuffer->viewportScrolled(dx, dy); 1935 } 1936 QAbstractScrollArea::scrollContentsBy(dx, dy); 1937 } 1938 #endif 1939 1940 void UIMachineView::emitKeyboardStateChanged() 1941 { 1942 emit keyboardStateChanged(keyboardState()); 1943 } 1944 1945 void UIMachineView::captureKbd(bool fCapture, bool fEmitSignal /* = true */) 1946 { 1947 if (m_bIsKeyboardCaptured == fCapture) 1948 return; 1949 1950 #if defined(Q_WS_WIN32) 1951 /* On Win32, keyboard grabbing is ineffective, a low-level keyboard hook is used instead. */ 1952 #elif defined(Q_WS_X11) 1953 /* On X11, we are using passive XGrabKey for normal (windowed) mode 1954 * instead of XGrabKeyboard (called by QWidget::grabKeyboard()) 1955 * because XGrabKeyboard causes a problem under metacity - a window cannot be moved 1956 * using the mouse if it is currently actively grabing the keyboard; 1957 * For static modes we are using usual (active) keyboard grabbing. */ 1958 switch (machineLogic()->visualStateType()) 1959 { 1960 /* If window is moveable we are making passive keyboard grab: */ 1961 case UIVisualStateType_Normal: 1962 { 1963 if (fCapture) 1964 XGrabKey(QX11Info::display(), AnyKey, AnyModifier, machineWindowWrapper()->machineWindow()->winId(), False, GrabModeAsync, GrabModeAsync); 1965 else 1966 XUngrabKey(QX11Info::display(), AnyKey, AnyModifier, machineWindowWrapper()->machineWindow()->winId()); 1967 break; 1968 } 1969 /* If window is NOT moveable we are making active keyboard grab: */ 1970 case UIVisualStateType_Fullscreen: 1971 case UIVisualStateType_Seamless: 1972 { 1973 if (fCapture) 1974 { 1975 /* Keyboard grabbing can fail because of some keyboard shortcut is still grabbed by window manager. 1976 * We can't be sure this shortcut will be released at all, so we will retry to grab keyboard for 50 times, 1977 * and after we will just ignore that issue: */ 1978 int cTriesLeft = 50; 1979 while (cTriesLeft && XGrabKeyboard(QX11Info::display(), machineWindowWrapper()->machineWindow()->winId(), False, GrabModeAsync, GrabModeAsync, CurrentTime)) { --cTriesLeft; } 1980 } 1981 else 1982 XUngrabKeyboard(QX11Info::display(), CurrentTime); 1983 break; 1984 } 1985 /* Should we try to grab keyboard in default case? I think - NO. */ 1986 default: 1987 break; 1988 } 1989 #elif defined(Q_WS_MAC) 1990 /* On Mac OS X, we use the Qt methods + disabling global hot keys + watching modifiers 1991 * (for right/left separation). */ 1992 if (fCapture) 1993 { 1994 ::DarwinDisableGlobalHotKeys(true); 1995 grabKeyboard(); 1996 } 1997 else 1998 { 1999 ::DarwinDisableGlobalHotKeys(false); 2000 releaseKeyboard(); 2001 } 2002 #else 2003 if (fCapture) 2004 grabKeyboard(); 2005 else 2006 releaseKeyboard(); 2007 #endif 2008 2009 m_bIsKeyboardCaptured = fCapture; 2010 2011 if (fEmitSignal) 2012 emitKeyboardStateChanged(); 2013 } 2014 2015 void UIMachineView::saveKeyStates() 1344 void UIKeyboardHandler::saveKeyStates() 2016 1345 { 2017 1346 ::memcpy(m_pressedKeysCopy, m_pressedKeys, sizeof(m_pressedKeys)); 2018 1347 } 2019 1348 2020 void UI MachineView::releaseAllPressedKeys(bool aReleaseHostKey /* = true */)1349 void UIKeyboardHandler::releaseAllPressedKeys(bool aReleaseHostKey /* = true */) 2021 1350 { 2022 1351 CKeyboard keyboard = session().GetConsole().GetKeyboard(); … … 2061 1390 m_darwinKeyModifiers &= 2062 1391 alphaLock | kEventKeyModifierNumLockMask | 2063 (aReleaseHostKey ? 0 : ::DarwinKeyCodeToDarwinModifierMask 1392 (aReleaseHostKey ? 0 : ::DarwinKeyCodeToDarwinModifierMask(m_globalSettings.hostKey())); 2064 1393 #endif 2065 1394 2066 emit KeyboardStateChanged();2067 } 2068 2069 void UI MachineView::sendChangedKeyStates()1395 emit keyboardStateChanged(keyboardState()); 1396 } 1397 1398 void UIKeyboardHandler::sendChangedKeyStates() 2070 1399 { 2071 1400 QVector <LONG> codes(2); … … 2093 1422 } 2094 1423 2095 void UIMachineView::dimImage(QImage &img) 2096 { 2097 for (int y = 0; y < img.height(); ++ y) 2098 { 2099 if (y % 2) 2100 { 2101 if (img.depth() == 32) 2102 { 2103 for (int x = 0; x < img.width(); ++ x) 2104 { 2105 int gray = qGray(img.pixel (x, y)) / 2; 2106 img.setPixel(x, y, qRgb (gray, gray, gray)); 2107 } 2108 } 2109 else 2110 { 2111 ::memset(img.scanLine (y), 0, img.bytesPerLine()); 2112 } 2113 } 2114 else 2115 { 2116 if (img.depth() == 32) 2117 { 2118 for (int x = 0; x < img.width(); ++ x) 2119 { 2120 int gray = (2 * qGray (img.pixel (x, y))) / 3; 2121 img.setPixel(x, y, qRgb (gray, gray, gray)); 2122 } 2123 } 2124 } 2125 } 2126 } 2127 2128 #ifdef Q_WS_MAC 2129 CGImageRef UIMachineView::vmContentImage() 2130 { 2131 if (!m_pauseShot.isNull()) 2132 { 2133 CGImageRef pauseImg = ::darwinToCGImageRef(&m_pauseShot); 2134 /* Use the pause image as background */ 2135 return pauseImg; 2136 } 2137 else 2138 { 2139 # ifdef VBOX_GUI_USE_QUARTZ2D 2140 if (vboxGlobal().vmRenderMode() == VBoxDefs::Quartz2DMode) 2141 { 2142 /* If the render mode is Quartz2D we could use the CGImageRef 2143 * of the framebuffer for the dock icon creation. This saves 2144 * some conversion time. */ 2145 CGImageRef image = static_cast<UIFrameBufferQuartz2D*>(m_pFrameBuffer)->imageRef(); 2146 CGImageRetain(image); /* Retain it, cause the consumer will release it. */ 2147 return image; 2148 } 2149 else 2150 # endif /* VBOX_GUI_USE_QUARTZ2D */ 2151 /* In image mode we have to create the image ref out of the 2152 * framebuffer */ 2153 return frameBuffertoCGImageRef(m_pFrameBuffer); 2154 } 2155 return 0; 2156 } 2157 2158 CGImageRef UIMachineView::frameBuffertoCGImageRef(UIFrameBuffer *pFrameBuffer) 2159 { 2160 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); 2161 Assert(cs); 2162 /* Create the image copy of the framebuffer */ 2163 CGDataProviderRef dp = CGDataProviderCreateWithData(pFrameBuffer, pFrameBuffer->address(), pFrameBuffer->bitsPerPixel() / 8 * pFrameBuffer->width() * pFrameBuffer->height(), NULL); 2164 Assert(dp); 2165 CGImageRef ir = CGImageCreate(pFrameBuffer->width(), pFrameBuffer->height(), 8, 32, pFrameBuffer->bytesPerLine(), cs, 2166 kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, dp, 0, false, 2167 kCGRenderingIntentDefault); 2168 Assert(ir); 2169 CGDataProviderRelease(dp); 2170 CGColorSpaceRelease(cs); 2171 2172 return ir; 2173 } 2174 2175 void UIMachineView::updateDockIcon() 2176 { 2177 machineLogic()->updateDockIcon(); 2178 } 2179 #endif /* Q_WS_MAC */ 2180 1424 UIMachineWindow* UIKeyboardHandler::isItListenedWindow(QObject *pWatchedObject) const 1425 { 1426 UIMachineWindow *pResultWindow = 0; 1427 QMap<ulong, UIMachineWindow*>::const_iterator i = m_windows.constBegin(); 1428 while (!pResultWindow && i != m_windows.constEnd()) 1429 { 1430 switch (machineLogic()->visualStateType()) 1431 { 1432 case UIVisualStateType_Normal: 1433 { 1434 UIMachineWindowNormal *pIteratedWindow = static_cast<UIMachineWindowNormal*>(i.value()); 1435 if (pIteratedWindow == pWatchedObject) 1436 { 1437 pResultWindow = pIteratedWindow; 1438 continue; 1439 } 1440 break; 1441 } 1442 case UIVisualStateType_Fullscreen: 1443 { 1444 UIMachineWindowFullscreen *pIteratedWindow = static_cast<UIMachineWindowFullscreen*>(i.value()); 1445 if (pIteratedWindow == pWatchedObject) 1446 { 1447 pResultWindow = pIteratedWindow; 1448 continue; 1449 } 1450 break; 1451 } 1452 case UIVisualStateType_Seamless: 1453 { 1454 UIMachineWindowSeamless *pIteratedWindow = static_cast<UIMachineWindowSeamless*>(i.value()); 1455 if (pIteratedWindow == pWatchedObject) 1456 { 1457 pResultWindow = pIteratedWindow; 1458 continue; 1459 } 1460 break; 1461 } 1462 default: 1463 break; 1464 } 1465 ++i; 1466 } 1467 return pResultWindow; 1468 } 1469 1470 UIMachineView* UIKeyboardHandler::isItListenedView(QObject *pWatchedObject) const 1471 { 1472 UIMachineView *pResultView = 0; 1473 QMap<ulong, UIMachineView*>::const_iterator i = m_views.constBegin(); 1474 while (!pResultView && i != m_views.constEnd()) 1475 { 1476 UIMachineView *pIteratedView = qobject_cast<UIMachineView*>(i.value()); 1477 if (pIteratedView == pWatchedObject) 1478 { 1479 pResultView = pIteratedView; 1480 continue; 1481 } 1482 ++i; 1483 } 1484 return pResultView; 1485 } 1486 -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIKeyboardHandler.h
r30458 r30637 2 2 * 3 3 * VBox frontends: Qt GUI ("VirtualBox"): 4 * UI MachineViewclass declaration4 * UIKeyboardHandler class declaration 5 5 */ 6 6 … … 17 17 */ 18 18 19 #ifndef ___UI MachineView_h___20 #define ___UI MachineView_h___19 #ifndef ___UIKeyboardHandler_h___ 20 #define ___UIKeyboardHandler_h___ 21 21 22 22 /* Global includes */ 23 #include <Q AbstractScrollArea>24 #include <Q EventLoop>23 #include <QObject> 24 #include <QMap> 25 25 26 26 /* Local includes */ 27 #include "UIMachineDefs.h" 27 28 #include "COMDefs.h" 28 #include "UIMachineDefs.h"29 29 30 #ifdef Q_WS_MAC 31 # include <CoreFoundation/CFBase.h> 32 #endif /* Q_WS_MAC */ 30 /* Global forwards */ 31 class QWidget; 33 32 34 33 /* Local forwards */ 34 class CSession; 35 35 class UISession; 36 class UI FrameBuffer;36 class UIMachineLogic; 37 37 class UIMachineWindow; 38 class UIMachine Logic;38 class UIMachineView; 39 39 class VBoxGlobalSettings; 40 #ifdef Q_WS_X11 41 typedef union _XEvent XEvent; 42 #endif /* Q_WS_X11 */ 40 43 41 class UIMachineView : public QAbstractScrollArea 44 /* Delegate to control VM keyboard functionality: */ 45 class UIKeyboardHandler : public QObject 42 46 { 43 47 Q_OBJECT; … … 45 49 public: 46 50 47 /* Desktop geometry types: */ 48 enum DesktopGeo { DesktopGeo_Invalid = 0, DesktopGeo_Fixed, DesktopGeo_Automatic, DesktopGeo_Any }; 51 /* Factory functions to create/destroy keyboard-handler: */ 52 static UIKeyboardHandler* create(UIMachineLogic *pMachineLogic, UIVisualStateType visualStateType); 53 static void destroy(UIKeyboardHandler *pKeyboardHandler); 49 54 50 /* Factory function to create required view sub-child: */ 51 static UIMachineView* create( UIMachineWindow *pMachineWindow 52 #ifdef VBOX_WITH_VIDEOHWACCEL 53 , bool bAccelerate2DVideo 54 #endif 55 , UIVisualStateType visualStateType 56 , ulong uScreenId); 57 static void destroy(UIMachineView *pWhichView); 55 /* Prepare/cleanup listeners: */ 56 void prepareListener(ulong uIndex, UIMachineWindow *pMachineWindow); 57 void cleanupListener(ulong uIndex); 58 58 59 /* Public getters: */ 59 /* Commands to capture/release keyboard: */ 60 void captureKeyboard(ulong uScreenId); 61 void releaseKeyboard(); 62 63 /* Current keyboard state: */ 60 64 int keyboardState() const; 61 virtual QRegion lastVisibleRegion() const { return QRegion(); }62 65 63 /* Public setters: */ 64 virtual void setGuestAutoresizeEnabled(bool /* fEnabled */) {} 65 66 /* Public members: */ 67 virtual void normalizeGeometry(bool /* bAdjustPosition = false */) = 0; 68 69 signals: 70 71 /* Keyboard state-change signals: */ 72 void keyboardStateChanged(int iState); 73 74 /* Utility signals: */ 75 void resizeHintDone(); 76 77 protected: 78 79 /* Machine view constructor/destructor: */ 80 UIMachineView( UIMachineWindow *pMachineWindow 81 #ifdef VBOX_WITH_VIDEOHWACCEL 82 , bool bAccelerate2DVideo 83 #endif 84 , ulong uScreenId); 85 virtual ~UIMachineView(); 86 87 /* Protected getters: */ 88 UISession* uisession() const; 89 CSession& session(); 90 QSize sizeHint() const; 91 int contentsX() const; 92 int contentsY() const; 93 int contentsWidth() const; 94 int contentsHeight() const; 95 int visibleWidth() const; 96 int visibleHeight() const; 97 ulong screenId() const { return m_uScreenId; } 98 UIFrameBuffer* frameBuffer() const { return m_pFrameBuffer; } 99 UIMachineWindow* machineWindowWrapper() const { return m_pMachineWindow; } 100 UIMachineLogic* machineLogic() const; 66 /* Some getters required by side-code: */ 101 67 bool isHostKeyPressed() const { return m_bIsHostkeyPressed; } 102 bool isMachineWindowResizeIgnored() const { return m_bIsMachineWindowResizeIgnored; }103 const QPixmap& pauseShot() const { return m_pauseShot; }104 QSize storedConsoleSize() const { return m_storedConsoleSize; }105 DesktopGeo desktopGeometryType() const { return m_desktopGeometryType; }106 QSize desktopGeometry() const;107 QSize guestSizeHint();108 68 #ifdef Q_WS_MAC 109 /* These getters are temporary here while UIKeyboardHandler is not implemented: */110 69 bool isHostKeyAlone() const { return m_bIsHostkeyAlone; } 111 70 bool isKeyboardGrabbed() const { return m_fKeyboardGrabbed; } 112 71 #endif /* Q_WS_MAC */ 113 72 114 /* Protected setters: */ 115 void setDesktopGeometry(DesktopGeo geometry, int iWidth, int iHeight); 116 void storeConsoleSize(int iWidth, int iHeight); 117 void setMachineWindowResizeIgnored(bool fIgnore = true) { m_bIsMachineWindowResizeIgnored = fIgnore; } 118 void storeGuestSizeHint(const QSize &sizeHint); 73 /* External event-filters: */ 74 #if defined(Q_WS_WIN) 75 bool winEventFilter(MSG *pMsg, ulong uScreenId); 76 #elif defined(Q_WS_X11) 77 bool x11EventFilter(XEvent *pEvent, ulong uScreenId); 78 #endif 119 79 120 /* Protected helpers: */ 121 virtual QRect workingArea() = 0; 122 virtual void calculateDesktopGeometry() = 0; 123 virtual void maybeRestrictMinimumSize() = 0; 124 virtual void updateSliders(); 80 signals: 125 81 126 #ifdef Q_WS_MAC 127 void updateDockIcon(); 128 CGImageRef vmContentImage(); 129 CGImageRef frameBuffertoCGImageRef(UIFrameBuffer *pFrameBuffer); 130 #endif /* Q_WS_MAC */ 131 132 /* Prepare routines: */ 133 virtual void prepareFrameBuffer(); 134 virtual void prepareCommon(); 135 virtual void prepareFilters(); 136 virtual void prepareConsoleConnections(); 137 virtual void loadMachineViewSettings(); 138 139 /* Cleanup routines: */ 140 //virtual void saveMachineViewSettings() {} 141 //virtual void cleanupConsoleConnections() {} 142 //virtual void cleanupFilters() {} 143 virtual void cleanupCommon(); 144 virtual void cleanupFrameBuffer(); 145 146 /* Cross-platforms event processors: */ 147 bool event(QEvent *pEvent); 148 bool eventFilter(QObject *pWatched, QEvent *pEvent); 149 150 /* Protected variables: */ 151 QSize m_desktopGeometry; 82 /* Notifies listeners about keyboard state-change: */ 83 void keyboardStateChanged(int iNewState); 152 84 153 85 protected slots: 154 86 155 /* Console callback handlers: */87 /* Machine state-change handler: */ 156 88 virtual void sltMachineStateChanged(); 157 virtual void sltPerformGuestResize(const QSize & /* toSize */) {};158 89 159 /* Various helper slots: */ 160 virtual void sltNormalizeGeometry() { normalizeGeometry(true); } 90 protected: 161 91 162 private: 92 /* Keyboard-handler constructor/destructor: */ 93 UIKeyboardHandler(UIMachineLogic *pMachineLogic); 94 virtual ~UIKeyboardHandler(); 163 95 164 /* Cross-platforms event processors: */ 165 void focusEvent(bool aHasFocus, bool aReleaseHostKey = true); 166 bool keyEvent(int aKey, uint8_t aScan, int aFlags, wchar_t *aUniKey = NULL); 167 void resizeEvent(QResizeEvent *pEvent); 168 void moveEvent(QMoveEvent *pEvent); 169 void paintEvent(QPaintEvent *pEvent); 96 /* Prepare helpers: */ 97 virtual void prepareCommon(); 98 virtual void loadSettings(); 170 99 171 /* Platform specific event processors: */ 172 #if defined(Q_WS_WIN32) 100 /* Cleanup helpers: */ 101 //virtual void saveSettings() {} 102 virtual void cleanupCommon(); 103 104 /* Common getters: */ 105 UIMachineLogic* machineLogic() const; 106 UISession* uisession() const; 107 CSession& session() const; 108 109 /* Event handler for registered machine-view(s): */ 110 bool eventFilter(QObject *pWatchedObject, QEvent *pEvent); 111 #if defined(Q_WS_WIN) 173 112 static LRESULT CALLBACK lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam); 174 113 bool winLowKeyboardEvent(UINT msg, const KBDLLHOOKSTRUCT &event); 175 bool winEvent(MSG *aMsg, long *aResult);176 #elif defined(Q_WS_PM)177 bool pmEvent(QMSG *aMsg);178 #elif defined(Q_WS_X11)179 bool x11Event(XEvent *event);180 114 #elif defined(Q_WS_MAC) 115 void darwinGrabKeyboardEvents(bool fGrab); 181 116 bool darwinKeyboardEvent(const void *pvCocoaEvent, EventRef inEvent); 182 void darwinGrabKeyboardEvents(bool fGrab);183 117 static bool darwinEventHandlerProc(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser); 184 118 #endif 185 119 120 /* Separate function to handle most of existing keyboard-events: */ 121 bool keyEvent(int iKey, uint8_t uScan, int fFlags, ulong uScreenId, wchar_t *pUniKey = 0); 122 186 123 /* Private helpers: */ 187 124 void fixModifierState(LONG *piCodes, uint *puCount); 188 QPoint viewportToContents(const QPoint &vp) const;189 void scrollBy(int dx, int dy);190 #ifdef VBOX_WITH_VIDEOHWACCEL191 void scrollContentsBy(int dx, int dy);192 #endif193 void emitKeyboardStateChanged();194 void captureKbd(bool fCapture, bool fEmitSignal = true);195 125 void saveKeyStates(); 196 126 void releaseAllPressedKeys(bool aReleaseHostKey = true); 197 127 void sendChangedKeyStates(); 198 128 199 static void dimImage(QImage &img); 129 UIMachineWindow* isItListenedWindow(QObject *pWatchedObject) const; 130 UIMachineView* isItListenedView(QObject *pWatchedObject) const; 200 131 201 /* Private members: */ 202 UIMachineWindow *m_pMachineWindow; 203 ulong m_uScreenId; 132 /* Machine logic parent: */ 133 UIMachineLogic *m_pMachineLogic; 134 135 /* Registered machine-window(s): */ 136 QMap<ulong, UIMachineWindow*> m_windows; 137 /* Registered machine-view(s): */ 138 QMap<ulong, UIMachineView*> m_views; 139 140 /* Other keyboard variables: */ 141 int m_iKeyboardCaptureViewIndex; 204 142 const VBoxGlobalSettings &m_globalSettings; 205 UIFrameBuffer *m_pFrameBuffer;206 KMachineState m_previousState;207 208 DesktopGeo m_desktopGeometryType;209 QSize m_storedConsoleSize;210 143 211 144 uint8_t m_pressedKeys[128]; 212 145 uint8_t m_pressedKeysCopy[128]; 213 146 214 bool m_ bIsKeyboardCaptured : 1;147 bool m_fIsKeyboardCaptured : 1; 215 148 bool m_bIsHostkeyPressed : 1; 216 149 bool m_bIsHostkeyAlone : 1; 217 150 bool m_bIsHostkeyInCapture : 1; 218 bool m_bIsMachineWindowResizeIgnored : 1;219 151 bool m_fPassCAD : 1; 220 #ifdef VBOX_WITH_VIDEOHWACCEL221 bool m_fAccelerate2DVideo;222 #endif223 152 224 #ifdef Q_WS_MAC 225 /** The current modifier key mask. Used to figure out which modifier 226 * key was pressed when we get a kEventRawKeyModifiersChanged event. */ 153 #if defined(Q_WS_WIN) 154 /* Keyboard hook required to capture keyboard event under windows. */ 155 static UIKeyboardHandler *m_pKeyboardHandler; 156 HHOOK m_keyboardHook; 157 int m_iKeyboardHookViewIndex; 158 #elif defined(Q_WS_MAC) 159 /* The current modifier key mask. Used to figure out which modifier 160 * key was pressed when we get a kEventRawKeyModifiersChanged event. */ 227 161 UInt32 m_darwinKeyModifiers; 228 162 bool m_fKeyboardGrabbed; 229 #endif /* Q_WS_MAC */ 230 231 QPixmap m_pauseShot; 232 233 /* Friend classes: */ 234 friend class UIMouseHandler; 235 friend class UIMachineLogic; 236 friend class UIFrameBuffer; 237 friend class UIFrameBufferQImage; 238 friend class UIFrameBufferQuartz2D; 239 friend class UIFrameBufferQGL; 240 template<class, class, class> friend class VBoxOverlayFrameBuffer; 163 #endif 241 164 }; 242 165 243 /* This maintenance class is a part of future roll-back mechanism. 244 * It allows to block main GUI thread until specific event received. 245 * Later it will become more abstract but now its just used to help 246 * fullscreen & seamless modes to restore normal guest size hint. */ 247 class UIMachineViewBlocker : public QEventLoop 248 { 249 Q_OBJECT; 166 #endif // !___UIKeyboardHandler_h___ 250 167 251 public:252 253 UIMachineViewBlocker()254 : QEventLoop(0)255 , m_iTimerId(0)256 {257 /* Also start timer to unlock pool in case of258 * required condition doesn't happens by some reason: */259 m_iTimerId = startTimer(3000);260 }261 262 virtual ~UIMachineViewBlocker()263 {264 /* Kill the timer: */265 killTimer(m_iTimerId);266 }267 268 protected:269 270 void timerEvent(QTimerEvent *pEvent)271 {272 /* If that timer event occurs => it seems273 * guest resize event doesn't comes in time,274 * shame on it, but we just unlocking 'this': */275 QEventLoop::timerEvent(pEvent);276 exit();277 }278 279 int m_iTimerId;280 };281 282 #endif // !___UIMachineView_h___283 -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineLogic.cpp
r30543 r30637 24 24 #include "UIDownloaderAdditions.h" 25 25 #include "UIIconPool.h" 26 #include "UIKeyboardHandler.h" 26 27 #include "UIMouseHandler.h" 27 28 #include "UIMachineLogic.h" … … 372 373 { 373 374 m_machineWindowsList << pMachineWindow; 375 } 376 377 void UIMachineLogic::setKeyboardHandler(UIKeyboardHandler *pKeyboardHandler) 378 { 379 m_pKeyboardHandler = pKeyboardHandler; 374 380 } 375 381 … … 556 562 void UIMachineLogic::prepareHandlers() 557 563 { 564 /* Create keyboard-handler: */ 565 setKeyboardHandler(UIKeyboardHandler::create(this, visualStateType())); 566 558 567 /* Create mouse-handler: */ 559 568 setMouseHandler(UIMouseHandler::create(this, visualStateType())); … … 658 667 /* Cleanup mouse-handler: */ 659 668 UIMouseHandler::destroy(mouseHandler()); 669 670 /* Cleanup keyboard-handler: */ 671 UIKeyboardHandler::destroy(keyboardHandler()); 660 672 } 661 673 -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineLogic.h
r30543 r30637 39 39 class UISession; 40 40 class UIActionsPool; 41 class UIKeyboardHandler; 41 42 class UIMouseHandler; 42 43 class UIMachineWindow; … … 69 70 UIVisualStateType visualStateType() const { return m_visualStateType; } 70 71 const QList<UIMachineWindow*>& machineWindows() const { return m_machineWindowsList; } 72 UIKeyboardHandler* keyboardHandler() const { return m_pKeyboardHandler; } 71 73 UIMouseHandler* mouseHandler() const { return m_pMouseHandler; } 72 74 UIMachineWindow* mainMachineWindow() const; … … 102 104 103 105 /* Protected members: */ 106 void setKeyboardHandler(UIKeyboardHandler *pKeyboardHandler); 104 107 void setMouseHandler(UIMouseHandler *pMouseHandler); 105 108 void addMachineWindow(UIMachineWindow *pMachineWindow); … … 197 200 UIActionsPool *m_pActionsPool; 198 201 UIVisualStateType m_visualStateType; 202 UIKeyboardHandler *m_pKeyboardHandler; 199 203 UIMouseHandler *m_pMouseHandler; 200 204 QList<UIMachineWindow*> m_machineWindowsList; -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineView.cpp
r30551 r30637 36 36 #include "UISession.h" 37 37 #include "UIActionsPool.h" 38 #include "UIKeyboardHandler.h" 38 39 #include "UIMouseHandler.h" 39 40 #include "UIMachineLogic.h" … … 47 48 #include "UIMachineViewSeamless.h" 48 49 49 #ifdef Q_WS_WIN50 #undef LOWORD51 #undef HIWORD52 #undef LOBYTE53 #undef HIBYTE54 #include <windows.h>55 static UIMachineView *gView = 0;56 static HHOOK gKbdHook = 0;57 #endif /* Q_WS_WIN */58 59 50 #ifdef Q_WS_X11 60 51 # include <QX11Info> 61 # define XK_XKB_KEYS62 # define XK_MISCELLANY63 52 # include <X11/XKBlib.h> 64 # include <X11/keysym.h>65 53 # ifdef KeyPress 66 54 const int XFocusOut = FocusOut; … … 73 61 # undef FocusIn 74 62 # endif 75 # include "XKeyboard.h"76 63 #endif /* Q_WS_X11 */ 77 64 … … 103 90 }; 104 91 105 enum { KeyExtended = 0x01, KeyPressed = 0x02, KeyPause = 0x04, KeyPrint = 0x08 };106 enum { IsKeyPressed = 0x01, IsExtKeyPressed = 0x02, IsKbdCaptured = 0x80 };107 108 92 UIMachineView* UIMachineView::create( UIMachineWindow *pMachineWindow 109 93 , ulong uScreenId … … 150 134 { 151 135 delete pMachineView; 152 }153 154 int UIMachineView::keyboardState() const155 {156 return (m_bIsKeyboardCaptured ? UIViewStateType_KeyboardCaptured : 0) |157 (m_bIsHostkeyPressed ? UIViewStateType_HostKeyPressed : 0);158 136 } 159 137 … … 186 164 viewport()->repaint(); 187 165 } 188 /* Reuse the focus event handler to uncapture keyboard: */189 if (hasFocus())190 focusEvent(false /* aHasFocus*/, false /* aReleaseHostKey */);191 break;192 }193 case KMachineState_Stuck:194 {195 /* Reuse the focus event handler to uncapture keyboard: */196 if (hasFocus())197 focusEvent(false /* aHasFocus*/, false /* aReleaseHostKey */);198 166 break; 199 167 } … … 214 182 } 215 183 } 216 /* Reuse the focus event handler to capture keyboard: */217 if (hasFocus())218 focusEvent(true /* aHasFocus */);219 184 break; 220 185 } … … 234 199 , m_pMachineWindow(pMachineWindow) 235 200 , m_uScreenId(uScreenId) 236 , m_globalSettings(vboxGlobal().settings())237 201 , m_pFrameBuffer(0) 238 202 , m_previousState(KMachineState_Null) 239 203 , m_desktopGeometryType(DesktopGeo_Invalid) 240 , m_bIsKeyboardCaptured(false)241 , m_bIsHostkeyPressed(false)242 , m_bIsHostkeyAlone (false)243 , m_bIsHostkeyInCapture(false)244 204 , m_bIsMachineWindowResizeIgnored(false) 245 , m_fPassCAD(false)246 205 #ifdef VBOX_WITH_VIDEOHWACCEL 247 206 , m_fAccelerate2DVideo(bAccelerate2DVideo) 248 207 #endif /* VBOX_WITH_VIDEOHWACCEL */ 249 #ifdef Q_WS_MAC250 , m_darwinKeyModifiers(0)251 , m_fKeyboardGrabbed (false)252 #endif /* Q_WS_MAC */253 208 { 254 209 } … … 433 388 setFrameStyle(QFrame::NoFrame); 434 389 435 /* Pressed keys: */436 ::memset(m_pressedKeys, 0, sizeof(m_pressedKeys));437 438 390 /* Setup palette: */ 439 391 QPalette palette(viewport()->palette()); … … 468 420 /* Global settings: */ 469 421 { 470 #ifdef Q_WS_X11471 /* Initialize the X keyboard subsystem: */472 initMappedX11Keyboard(QX11Info::display(), vboxGlobal().settings().publicProperty("GUI/RemapScancodes"));473 #endif /* Q_WS_X11 */474 475 422 /* Remember the desktop geometry and register for geometry 476 423 * change events for telling the guest about video modes we like: */ … … 487 434 } 488 435 } 489 490 /* Exatra data settings: */491 {492 /* CAD settings: */493 QString passCAD = session().GetConsole().GetMachine().GetExtraData(VBoxDefs::GUI_PassCAD);494 if (!passCAD.isEmpty() && ((passCAD != "false") || (passCAD != "no")))495 m_fPassCAD = true;496 }497 }498 499 void UIMachineView::cleanupCommon()500 {501 #ifdef Q_WS_WIN502 if (gKbdHook)503 UnhookWindowsHookEx(gKbdHook);504 gView = 0;505 #endif /* Q_WS_WIN */506 507 #ifdef Q_WS_MAC508 /* We have to make sure the callback for the keyboard events is released509 * when closing this view. */510 if (m_fKeyboardGrabbed)511 darwinGrabKeyboardEvents (false);512 #endif /* Q_WS_MAC */513 436 } 514 437 … … 717 640 } 718 641 719 void UIMachineView::fixModifierState(LONG *piCodes, uint *puCount)720 {721 /* Synchronize the views of the host and the guest to the modifier keys.722 * This function will add up to 6 additional keycodes to codes. */723 724 #if defined(Q_WS_X11)725 726 Window wDummy1, wDummy2;727 int iDummy3, iDummy4, iDummy5, iDummy6;728 unsigned uMask;729 unsigned uKeyMaskNum = 0, uKeyMaskCaps = 0, uKeyMaskScroll = 0;730 731 uKeyMaskCaps = LockMask;732 XModifierKeymap* map = XGetModifierMapping(QX11Info::display());733 KeyCode keyCodeNum = XKeysymToKeycode(QX11Info::display(), XK_Num_Lock);734 KeyCode keyCodeScroll = XKeysymToKeycode(QX11Info::display(), XK_Scroll_Lock);735 736 for (int i = 0; i < 8; ++ i)737 {738 if (keyCodeNum != NoSymbol && map->modifiermap[map->max_keypermod * i] == keyCodeNum)739 uKeyMaskNum = 1 << i;740 else if (keyCodeScroll != NoSymbol && map->modifiermap[map->max_keypermod * i] == keyCodeScroll)741 uKeyMaskScroll = 1 << i;742 }743 XQueryPointer(QX11Info::display(), DefaultRootWindow(QX11Info::display()), &wDummy1, &wDummy2,744 &iDummy3, &iDummy4, &iDummy5, &iDummy6, &uMask);745 XFreeModifiermap(map);746 747 if (uisession()->numLockAdaptionCnt() && (uisession()->isNumLock() ^ !!(uMask & uKeyMaskNum)))748 {749 uisession()->setNumLockAdaptionCnt(uisession()->numLockAdaptionCnt() - 1);750 piCodes[(*puCount)++] = 0x45;751 piCodes[(*puCount)++] = 0x45 | 0x80;752 }753 if (uisession()->capsLockAdaptionCnt() && (uisession()->isCapsLock() ^ !!(uMask & uKeyMaskCaps)))754 {755 uisession()->setCapsLockAdaptionCnt(uisession()->capsLockAdaptionCnt() - 1);756 piCodes[(*puCount)++] = 0x3a;757 piCodes[(*puCount)++] = 0x3a | 0x80;758 /* Some keyboard layouts require shift to be pressed to break759 * capslock. For simplicity, only do this if shift is not760 * already held down. */761 if (uisession()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))762 {763 piCodes[(*puCount)++] = 0x2a;764 piCodes[(*puCount)++] = 0x2a | 0x80;765 }766 }767 768 #elif defined(Q_WS_WIN)769 770 if (uisession()->numLockAdaptionCnt() && (uisession()->isNumLock() ^ !!(GetKeyState(VK_NUMLOCK))))771 {772 uisession()->setNumLockAdaptionCnt(uisession()->numLockAdaptionCnt() - 1);773 piCodes[(*puCount)++] = 0x45;774 piCodes[(*puCount)++] = 0x45 | 0x80;775 }776 if (uisession()->capsLockAdaptionCnt() && (uisession()->isCapsLock() ^ !!(GetKeyState(VK_CAPITAL))))777 {778 uisession()->setCapsLockAdaptionCnt(uisession()->capsLockAdaptionCnt() - 1);779 piCodes[(*puCount)++] = 0x3a;780 piCodes[(*puCount)++] = 0x3a | 0x80;781 /* Some keyboard layouts require shift to be pressed to break782 * capslock. For simplicity, only do this if shift is not783 * already held down. */784 if (uisession()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))785 {786 piCodes[(*puCount)++] = 0x2a;787 piCodes[(*puCount)++] = 0x2a | 0x80;788 }789 }790 791 #elif defined(Q_WS_MAC)792 793 /* if (uisession()->numLockAdaptionCnt()) ... - NumLock isn't implemented by Mac OS X so ignore it. */794 if (uisession()->capsLockAdaptionCnt() && (uisession()->isCapsLock() ^ !!(::GetCurrentEventKeyModifiers() & alphaLock)))795 {796 uisession()->setCapsLockAdaptionCnt(uisession()->capsLockAdaptionCnt() - 1);797 piCodes[(*puCount)++] = 0x3a;798 piCodes[(*puCount)++] = 0x3a | 0x80;799 /* Some keyboard layouts require shift to be pressed to break800 * capslock. For simplicity, only do this if shift is not801 * already held down. */802 if (uisession()->isCapsLock() && !(m_pressedKeys[0x2a] & IsKeyPressed))803 {804 piCodes[(*puCount)++] = 0x2a;805 piCodes[(*puCount)++] = 0x2a | 0x80;806 }807 }808 809 #else810 811 //#warning Adapt UIMachineView::fixModifierState812 813 #endif814 }815 816 642 QPoint UIMachineView::viewportToContents(const QPoint &vp) const 817 643 { … … 823 649 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + dx); 824 650 verticalScrollBar()->setValue(verticalScrollBar()->value() + dy); 825 }826 827 void UIMachineView::emitKeyboardStateChanged()828 {829 emit keyboardStateChanged(keyboardState());830 }831 832 void UIMachineView::captureKbd(bool fCapture, bool fEmitSignal /* = true */)833 {834 if (m_bIsKeyboardCaptured == fCapture)835 return;836 837 #if defined(Q_WS_WIN)838 /* On Win32, keyboard grabbing is ineffective, a low-level keyboard hook is used instead. */839 #elif defined(Q_WS_X11)840 /* On X11, we are using passive XGrabKey for normal (windowed) mode841 * instead of XGrabKeyboard (called by QWidget::grabKeyboard())842 * because XGrabKeyboard causes a problem under metacity - a window cannot be moved843 * using the mouse if it is currently actively grabing the keyboard;844 * For static modes we are using usual (active) keyboard grabbing. */845 switch (machineLogic()->visualStateType())846 {847 /* If window is moveable we are making passive keyboard grab: */848 case UIVisualStateType_Normal:849 {850 if (fCapture)851 XGrabKey(QX11Info::display(), AnyKey, AnyModifier, machineWindowWrapper()->machineWindow()->winId(), False, GrabModeAsync, GrabModeAsync);852 else853 XUngrabKey(QX11Info::display(), AnyKey, AnyModifier, machineWindowWrapper()->machineWindow()->winId());854 break;855 }856 /* If window is NOT moveable we are making active keyboard grab: */857 case UIVisualStateType_Fullscreen:858 case UIVisualStateType_Seamless:859 {860 if (fCapture)861 {862 /* Keyboard grabbing can fail because of some keyboard shortcut is still grabbed by window manager.863 * We can't be sure this shortcut will be released at all, so we will retry to grab keyboard for 50 times,864 * and after we will just ignore that issue: */865 int cTriesLeft = 50;866 while (cTriesLeft && XGrabKeyboard(QX11Info::display(), machineWindowWrapper()->machineWindow()->winId(), False, GrabModeAsync, GrabModeAsync, CurrentTime)) { --cTriesLeft; }867 }868 else869 XUngrabKeyboard(QX11Info::display(), CurrentTime);870 break;871 }872 /* Should we try to grab keyboard in default case? I think - NO. */873 default:874 break;875 }876 #elif defined(Q_WS_MAC)877 /* On Mac OS X, we use the Qt methods + disabling global hot keys + watching modifiers878 * (for right/left separation). */879 if (fCapture)880 {881 ::DarwinDisableGlobalHotKeys(true);882 grabKeyboard();883 }884 else885 {886 ::DarwinDisableGlobalHotKeys(false);887 releaseKeyboard();888 }889 #else890 if (fCapture)891 grabKeyboard();892 else893 releaseKeyboard();894 #endif895 896 m_bIsKeyboardCaptured = fCapture;897 898 if (fEmitSignal)899 emitKeyboardStateChanged();900 }901 902 void UIMachineView::saveKeyStates()903 {904 ::memcpy(m_pressedKeysCopy, m_pressedKeys, sizeof(m_pressedKeys));905 }906 907 void UIMachineView::releaseAllPressedKeys(bool aReleaseHostKey /* = true */)908 {909 CKeyboard keyboard = session().GetConsole().GetKeyboard();910 bool fSentRESEND = false;911 912 /* Send a dummy scan code (RESEND) to prevent the guest OS from recognizing913 * a single key click (for ex., Alt) and performing an unwanted action914 * (for ex., activating the menu) when we release all pressed keys below.915 * Note, that it's just a guess that sending RESEND will give the desired916 * effect :), but at least it works with NT and W2k guests. */917 for (uint i = 0; i < SIZEOF_ARRAY (m_pressedKeys); i++)918 {919 if (m_pressedKeys[i] & IsKeyPressed)920 {921 if (!fSentRESEND)922 {923 keyboard.PutScancode (0xFE);924 fSentRESEND = true;925 }926 keyboard.PutScancode(i | 0x80);927 }928 else if (m_pressedKeys[i] & IsExtKeyPressed)929 {930 if (!fSentRESEND)931 {932 keyboard.PutScancode(0xFE);933 fSentRESEND = true;934 }935 QVector <LONG> codes(2);936 codes[0] = 0xE0;937 codes[1] = i | 0x80;938 keyboard.PutScancodes(codes);939 }940 m_pressedKeys[i] = 0;941 }942 943 if (aReleaseHostKey)944 m_bIsHostkeyPressed = false;945 946 #ifdef Q_WS_MAC947 /* Clear most of the modifiers: */948 m_darwinKeyModifiers &=949 alphaLock | kEventKeyModifierNumLockMask |950 (aReleaseHostKey ? 0 : ::DarwinKeyCodeToDarwinModifierMask (m_globalSettings.hostKey()));951 #endif /* Q_WS_MAC */952 953 emitKeyboardStateChanged();954 }955 956 void UIMachineView::sendChangedKeyStates()957 {958 QVector <LONG> codes(2);959 CKeyboard keyboard = session().GetConsole().GetKeyboard();960 for (uint i = 0; i < SIZEOF_ARRAY(m_pressedKeys); ++ i)961 {962 uint8_t os = m_pressedKeysCopy[i];963 uint8_t ns = m_pressedKeys[i];964 if ((os & IsKeyPressed) != (ns & IsKeyPressed))965 {966 codes[0] = i;967 if (!(ns & IsKeyPressed))968 codes[0] |= 0x80;969 keyboard.PutScancode(codes[0]);970 }971 else if ((os & IsExtKeyPressed) != (ns & IsExtKeyPressed))972 {973 codes[0] = 0xE0;974 codes[1] = i;975 if (!(ns & IsExtKeyPressed))976 codes[1] |= 0x80;977 keyboard.PutScancodes(codes);978 }979 }980 651 } 981 652 … … 1081 752 switch (pEvent->type()) 1082 753 { 1083 case QEvent::FocusIn:1084 {1085 if (uisession()->isRunning())1086 focusEvent(true);1087 break;1088 }1089 case QEvent::FocusOut:1090 {1091 if (uisession()->isRunning())1092 focusEvent(false);1093 else1094 {1095 /* Release the host key and all other pressed keys too even when paused1096 * (otherwise, we will get stuck keys in the guest when doing sendChangedKeyStates() on resume1097 * because key presses were already recorded in m_pressedKeys but key releases will most likely1098 * not reach us but the new focus window instead): */1099 releaseAllPressedKeys(true /* including host key? */);1100 }1101 break;1102 }1103 1104 754 case VBoxDefs::RepaintEventType: 1105 755 { … … 1107 757 viewport()->repaint(pPaintEvent->x() - contentsX(), pPaintEvent->y() - contentsY(), 1108 758 pPaintEvent->width(), pPaintEvent->height()); 1109 1110 return true;1111 }1112 1113 case QEvent::KeyPress:1114 case QEvent::KeyRelease:1115 {1116 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);1117 1118 if (m_bIsHostkeyPressed && pEvent->type() == QEvent::KeyPress)1119 {1120 /* Passing F1-F12 keys to the guest: */1121 if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F12)1122 {1123 QVector <LONG> combo(6);1124 combo[0] = 0x1d; /* Ctrl down */1125 combo[1] = 0x38; /* Alt down */1126 combo[4] = 0xb8; /* Alt up */1127 combo[5] = 0x9d; /* Ctrl up */1128 if (pKeyEvent->key() >= Qt::Key_F1 && pKeyEvent->key() <= Qt::Key_F10)1129 {1130 combo[2] = 0x3b + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 down */1131 combo[3] = 0xbb + (pKeyEvent->key() - Qt::Key_F1); /* F1-F10 up */1132 }1133 /* some scan slice */1134 else if (pKeyEvent->key() >= Qt::Key_F11 && pKeyEvent->key() <= Qt::Key_F12)1135 {1136 combo[2] = 0x57 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 down */1137 combo[3] = 0xd7 + (pKeyEvent->key() - Qt::Key_F11); /* F11-F12 up */1138 }1139 else1140 Assert(0);1141 1142 CKeyboard keyboard = session().GetConsole().GetKeyboard();1143 keyboard.PutScancodes(combo);1144 }1145 1146 /* Process hot keys not processed in keyEvent() (as in case of non-alphanumeric keys): */1147 machineWindowWrapper()->machineLogic()->actionsPool()->processHotKey(QKeySequence(pKeyEvent->key()));1148 }1149 else if (!m_bIsHostkeyPressed && pEvent->type() == QEvent::KeyRelease)1150 {1151 /* Show a possible warning on key release which seems to be more expected by the end user: */1152 if (uisession()->isPaused())1153 {1154 /* Iif the reminder is disabled we pass the event to Qt to enable normal1155 * keyboard functionality (for example, menu access with Alt+Letter): */1156 if (!vboxProblem().remindAboutPausedVMInput())1157 break;1158 }1159 }1160 1161 pKeyEvent->accept();1162 759 return true; 1163 760 } … … 1231 828 break; 1232 829 } 1233 #ifdef Q_WS_WIN1234 /* Install/uninstall low-level kbd hook on every activation/deactivation to:1235 * a) avoid excess hook calls when we're not active and1236 * b) be always in front of any other possible hooks */1237 case QEvent::WindowActivate:1238 {1239 gView = this;1240 gKbdHook = SetWindowsHookEx(WH_KEYBOARD_LL, lowLevelKeyboardProc, GetModuleHandle(NULL), 0);1241 AssertMsg(gKbdHook, ("SetWindowsHookEx(): err=%d", GetLastError()));1242 break;1243 }1244 case QEvent::WindowDeactivate:1245 {1246 if (gKbdHook)1247 {1248 UnhookWindowsHookEx(gKbdHook);1249 gKbdHook = NULL;1250 if (gView == this)1251 gView = 0;1252 }1253 break;1254 }1255 #endif /* Q_WS_WIN */1256 #ifdef Q_WS_MAC1257 /* Install/remove the keyboard event handler: */1258 case QEvent::WindowActivate:1259 darwinGrabKeyboardEvents(true);1260 break;1261 case QEvent::WindowDeactivate:1262 darwinGrabKeyboardEvents(false);1263 break;1264 #endif /* Q_WS_MAC */1265 830 default: 1266 831 break; … … 1269 834 1270 835 return QAbstractScrollArea::eventFilter(pWatched, pEvent); 1271 }1272 1273 void UIMachineView::focusEvent(bool fHasFocus, bool fReleaseHostKey /* = true */)1274 {1275 if (fHasFocus)1276 {1277 #ifdef Q_WS_WIN1278 if (!uisession()->isAutoCaptureDisabled() && m_globalSettings.autoCapture() && GetAncestor(winId(), GA_ROOT) == GetForegroundWindow())1279 #else /* Q_WS_WIN */1280 if (!uisession()->isAutoCaptureDisabled() && m_globalSettings.autoCapture())1281 #endif /* !Q_WS_WIN */1282 {1283 captureKbd(true);1284 }1285 1286 /* Reset the single-time disable capture flag: */1287 if (uisession()->isAutoCaptureDisabled())1288 uisession()->setAutoCaptureDisabled(false);1289 }1290 else1291 {1292 captureKbd(false, false);1293 releaseAllPressedKeys(fReleaseHostKey);1294 }1295 }1296 1297 bool UIMachineView::keyEvent(int iKey, uint8_t uScan, int fFlags, wchar_t *pUniKey /* = NULL */)1298 {1299 const bool isHostKey = iKey == m_globalSettings.hostKey();1300 1301 LONG buf[16];1302 LONG *codes = buf;1303 uint count = 0;1304 uint8_t whatPressed = 0;1305 1306 if (!isHostKey && !m_bIsHostkeyPressed)1307 {1308 if (fFlags & KeyPrint)1309 {1310 static LONG PrintMake[] = { 0xE0, 0x2A, 0xE0, 0x37 };1311 static LONG PrintBreak[] = { 0xE0, 0xB7, 0xE0, 0xAA };1312 if (fFlags & KeyPressed)1313 {1314 codes = PrintMake;1315 count = SIZEOF_ARRAY(PrintMake);1316 }1317 else1318 {1319 codes = PrintBreak;1320 count = SIZEOF_ARRAY(PrintBreak);1321 }1322 }1323 else if (fFlags & KeyPause)1324 {1325 if (fFlags & KeyPressed)1326 {1327 static LONG Pause[] = { 0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5 };1328 codes = Pause;1329 count = SIZEOF_ARRAY(Pause);1330 }1331 else1332 {1333 /* Pause shall not produce a break code */1334 return true;1335 }1336 }1337 else1338 {1339 if (fFlags & KeyPressed)1340 {1341 /* Check if the guest has the same view on the modifier keys (NumLock,1342 * CapsLock, ScrollLock) as the X server. If not, send KeyPress events1343 * to synchronize the state. */1344 fixModifierState(codes, &count);1345 }1346 1347 /* Check if it's C-A-D and GUI/PassCAD is not true */1348 if (!m_fPassCAD &&1349 uScan == 0x53 /* Del */ &&1350 ((m_pressedKeys[0x38] & IsKeyPressed) /* Alt */ ||1351 (m_pressedKeys[0x38] & IsExtKeyPressed)) &&1352 ((m_pressedKeys[0x1d] & IsKeyPressed) /* Ctrl */ ||1353 (m_pressedKeys[0x1d] & IsExtKeyPressed)))1354 {1355 /* Use the C-A-D combination as a last resort to get the1356 * keyboard and mouse back to the host when the user forgets1357 * the Host Key. Note that it's always possible to send C-A-D1358 * to the guest using the Host+Del combination. BTW, it would1359 * be preferrable to completely ignore C-A-D in guests, but1360 * that's not possible because we cannot predict what other1361 * keys will be pressed next when one of C, A, D is held. */1362 if (uisession()->isRunning() && m_bIsKeyboardCaptured)1363 {1364 captureKbd(false);1365 if (!(uisession()->isMouseSupportsAbsolute() && uisession()->isMouseIntegrated()))1366 machineLogic()->mouseHandler()->captureMouse(screenId());1367 }1368 1369 return true;1370 }1371 1372 /* Process the scancode and update the table of pressed keys: */1373 whatPressed = IsKeyPressed;1374 1375 if (fFlags & KeyExtended)1376 {1377 codes[count++] = 0xE0;1378 whatPressed = IsExtKeyPressed;1379 }1380 1381 if (fFlags & KeyPressed)1382 {1383 codes[count++] = uScan;1384 m_pressedKeys[uScan] |= whatPressed;1385 }1386 else1387 {1388 /* If we haven't got this key's press message, we ignore its release: */1389 if (!(m_pressedKeys[uScan] & whatPressed))1390 return true;1391 codes[count++] = uScan | 0x80;1392 m_pressedKeys[uScan] &= ~whatPressed;1393 }1394 1395 if (m_bIsKeyboardCaptured)1396 m_pressedKeys[uScan] |= IsKbdCaptured;1397 else1398 m_pressedKeys[uScan] &= ~IsKbdCaptured;1399 }1400 }1401 else1402 {1403 /* Currently this is used in winLowKeyboardEvent() only: */1404 m_bIsHostkeyInCapture = m_bIsKeyboardCaptured;1405 }1406 1407 bool emitSignal = false;1408 int hotkey = 0;1409 1410 /* Process the host key: */1411 if (fFlags & KeyPressed)1412 {1413 if (isHostKey)1414 {1415 if (!m_bIsHostkeyPressed)1416 {1417 m_bIsHostkeyPressed = m_bIsHostkeyAlone = true;1418 if (uisession()->isRunning())1419 saveKeyStates();1420 emitSignal = true;1421 }1422 }1423 else1424 {1425 if (m_bIsHostkeyPressed)1426 {1427 if (m_bIsHostkeyAlone)1428 {1429 hotkey = iKey;1430 m_bIsHostkeyAlone = false;1431 }1432 }1433 }1434 }1435 else1436 {1437 if (isHostKey)1438 {1439 if (m_bIsHostkeyPressed)1440 {1441 m_bIsHostkeyPressed = false;1442 1443 if (m_bIsHostkeyAlone)1444 {1445 if (uisession()->isPaused())1446 {1447 vboxProblem().remindAboutPausedVMInput();1448 }1449 else if (uisession()->isRunning())1450 {1451 bool captured = m_bIsKeyboardCaptured;1452 bool ok = true;1453 if (!captured)1454 {1455 /* temporarily disable auto capture that will take1456 * place after this dialog is dismissed because1457 * the capture state is to be defined by the1458 * dialog result itself */1459 uisession()->setAutoCaptureDisabled(true);1460 bool autoConfirmed = false;1461 ok = vboxProblem().confirmInputCapture (&autoConfirmed);1462 if (autoConfirmed)1463 uisession()->setAutoCaptureDisabled(false);1464 /* otherwise, the disable flag will be reset in1465 * the next console view's foucs in event (since1466 * may happen asynchronously on some platforms,1467 * after we return from this code) */1468 }1469 1470 if (ok)1471 {1472 captureKbd (!captured, false);1473 if (!(uisession()->isMouseSupportsAbsolute() && uisession()->isMouseIntegrated()))1474 {1475 #ifdef Q_WS_X111476 /* make sure that pending FocusOut events from the1477 * previous message box are handled, otherwise the1478 * mouse is immediately ungrabbed. */1479 qApp->processEvents();1480 #endif /* Q_WS_X11 */1481 if (m_bIsKeyboardCaptured)1482 machineLogic()->mouseHandler()->captureMouse(screenId());1483 else1484 machineLogic()->mouseHandler()->releaseMouse();1485 }1486 }1487 }1488 }1489 1490 if (uisession()->isRunning())1491 sendChangedKeyStates();1492 1493 emitSignal = true;1494 }1495 }1496 else1497 {1498 if (m_bIsHostkeyPressed)1499 m_bIsHostkeyAlone = false;1500 }1501 }1502 1503 /* emit the keyboard state change signal */1504 if (emitSignal)1505 emitKeyboardStateChanged();1506 1507 /* Process Host+<key> shortcuts. currently, <key> is limited to1508 * alphanumeric chars. Other Host+<key> combinations are handled in1509 * event(). */1510 if (hotkey)1511 {1512 bool processed = false;1513 #if defined (Q_WS_WIN)1514 NOREF(pUniKey);1515 int n = GetKeyboardLayoutList(0, NULL);1516 Assert (n);1517 HKL *list = new HKL[n];1518 GetKeyboardLayoutList(n, list);1519 for (int i = 0; i < n && !processed; i++)1520 {1521 wchar_t ch;1522 static BYTE keys[256] = {0};1523 if (!ToUnicodeEx(hotkey, 0, keys, &ch, 1, 0, list[i]) == 1)1524 ch = 0;1525 if (ch)1526 processed = machineWindowWrapper()->machineLogic()->actionsPool()->processHotKey(QKeySequence((Qt::UNICODE_ACCEL + QChar(ch).toUpper().unicode())));1527 }1528 delete[] list;1529 #elif defined (Q_WS_X11)1530 NOREF(pUniKey);1531 Display *display = QX11Info::display();1532 int keysyms_per_keycode = getKeysymsPerKeycode();1533 KeyCode kc = XKeysymToKeycode (display, iKey);1534 for (int i = 0; i < keysyms_per_keycode && !processed; i += 2)1535 {1536 KeySym ks = XKeycodeToKeysym(display, kc, i);1537 char ch = 0;1538 if (!XkbTranslateKeySym(display, &ks, 0, &ch, 1, NULL) == 1)1539 ch = 0;1540 if (ch)1541 QChar c = QString::fromLocal8Bit(&ch, 1)[0];1542 }1543 #elif defined (Q_WS_MAC)1544 if (pUniKey && pUniKey[0] && !pUniKey[1])1545 processed = machineWindowWrapper()->machineLogic()->actionsPool()->processHotKey(QKeySequence(Qt::UNICODE_ACCEL + QChar(pUniKey[0]).toUpper().unicode()));1546 1547 /* Don't consider the hot key as pressed since the guest never saw1548 * it. (probably a generic thing) */1549 m_pressedKeys[uScan] &= ~whatPressed;1550 #endif1551 1552 /* Grab the key from Qt if processed, or pass it to Qt otherwise1553 * in order to process non-alphanumeric keys in event(), after they are1554 * converted to Qt virtual keys. */1555 return processed;1556 }1557 1558 /* No more to do, if the host key is in action or the VM is paused: */1559 if (m_bIsHostkeyPressed || isHostKey || uisession()->isPaused())1560 {1561 /* Grab the key from Qt and from VM if it's a host key,1562 * otherwise just pass it to Qt */1563 return isHostKey;1564 }1565 1566 CKeyboard keyboard = session().GetConsole().GetKeyboard();1567 Assert(!keyboard.isNull());1568 1569 #ifdef Q_WS_WIN1570 /* send pending WM_PAINT events */1571 ::UpdateWindow(viewport()->winId());1572 #endif /* Q_WS_WIN */1573 1574 std::vector <LONG> scancodes(codes, &codes[count]);1575 keyboard.PutScancodes(QVector<LONG>::fromStdVector(scancodes));1576 1577 /* Grab the key from Qt: */1578 return true;1579 836 } 1580 837 … … 1631 888 #if defined(Q_WS_WIN) 1632 889 1633 LRESULT CALLBACK UIMachineView::lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) 1634 { 1635 if (gView && nCode == HC_ACTION && gView->winLowKeyboardEvent(wParam, *(KBDLLHOOKSTRUCT *)lParam)) 1636 return 1; 1637 1638 return CallNextHookEx(NULL, nCode, wParam, lParam); 1639 } 1640 1641 bool UIMachineView::winLowKeyboardEvent(UINT msg, const KBDLLHOOKSTRUCT &event) 1642 { 1643 /* Sometimes it happens that Win inserts additional events on some key 1644 * press/release. For example, it prepends ALT_GR in German layout with 1645 * the VK_LCONTROL vkey with curious 0x21D scan code (seems to be necessary 1646 * to specially treat ALT_GR to enter additional chars to regular apps). 1647 * These events are definitely unwanted in VM, so filter them out. */ 1648 /* Note (michael): it also sometimes sends the VK_CAPITAL vkey with scan 1649 * code 0x23a. If this is not passed through then it is impossible to 1650 * cancel CapsLock on a French keyboard. I didn't find any other examples 1651 * of these strange events. Let's hope we are not missing anything else 1652 * of importance! */ 1653 if (hasFocus() && (event.scanCode & ~0xFF)) 1654 { 1655 if (event.vkCode == VK_CAPITAL) 1656 return false; 1657 else 1658 return true; 1659 } 1660 1661 if (!m_bIsKeyboardCaptured) 1662 return false; 1663 1664 /* it's possible that a key has been pressed while the keyboard was not 1665 * captured, but is being released under the capture. Detect this situation 1666 * and return false to let Windows process the message normally and update 1667 * its key state table (to avoid the stuck key effect). */ 1668 uint8_t what_pressed = (event.flags & 0x01) && (event.vkCode != VK_RSHIFT) ? IsExtKeyPressed : IsKeyPressed; 1669 if ((event.flags & 0x80) /* released */ && 1670 ((event.vkCode == m_globalSettings.hostKey() && !m_bIsHostkeyInCapture) || 1671 (m_pressedKeys[event.scanCode] & (IsKbdCaptured | what_pressed)) == what_pressed)) 1672 return false; 1673 1674 MSG message; 1675 message.hwnd = winId(); 1676 message.message = msg; 1677 message.wParam = event.vkCode; 1678 message.lParam = 1 | (event.scanCode & 0xFF) << 16 | (event.flags & 0xFF) << 24; 1679 1680 /* Windows sets here the extended bit when the Right Shift key is pressed, 1681 * which is totally wrong. Undo it. */ 1682 if (event.vkCode == VK_RSHIFT) 1683 message.lParam &= ~0x1000000; 1684 1685 /* we suppose here that this hook is always called on the main GUI thread */ 1686 long dummyResult; 1687 return winEvent(&message, &dummyResult); 1688 } 1689 1690 bool UIMachineView::winEvent(MSG *aMsg, long* /* aResult */) 1691 { 1692 if (!(aMsg->message == WM_KEYDOWN || aMsg->message == WM_SYSKEYDOWN || 1693 aMsg->message == WM_KEYUP || aMsg->message == WM_SYSKEYUP)) 1694 return false; 1695 1696 /* Check for the special flag possibly set at the end of this function */ 1697 if (aMsg->lParam & (0x1 << 25)) 1698 { 1699 aMsg->lParam &= ~(0x1 << 25); 1700 return false; 1701 } 1702 1703 int scan = (aMsg->lParam >> 16) & 0x7F; 1704 /* scancodes 0x80 and 0x00 are ignored */ 1705 if (!scan) 1706 return true; 1707 1708 int vkey = aMsg->wParam; 1709 1710 /* When one of the SHIFT keys is held and one of the cursor movement 1711 * keys is pressed, Windows duplicates SHIFT press/release messages, 1712 * but with the virtual key code set to 0xFF. These virtual keys are also 1713 * sent in some other situations (Pause, PrtScn, etc.). Ignore such 1714 * messages. */ 1715 if (vkey == 0xFF) 1716 return true; 1717 1718 int flags = 0; 1719 if (aMsg->lParam & 0x1000000) 1720 flags |= KeyExtended; 1721 if (!(aMsg->lParam & 0x80000000)) 1722 flags |= KeyPressed; 1723 1724 switch (vkey) 1725 { 1726 case VK_SHIFT: 1727 case VK_CONTROL: 1728 case VK_MENU: 1729 { 1730 /* overcome stupid Win32 modifier key generalization */ 1731 int keyscan = scan; 1732 if (flags & KeyExtended) 1733 keyscan |= 0xE000; 1734 switch (keyscan) 1735 { 1736 case 0x002A: vkey = VK_LSHIFT; break; 1737 case 0x0036: vkey = VK_RSHIFT; break; 1738 case 0x001D: vkey = VK_LCONTROL; break; 1739 case 0xE01D: vkey = VK_RCONTROL; break; 1740 case 0x0038: vkey = VK_LMENU; break; 1741 case 0xE038: vkey = VK_RMENU; break; 1742 } 1743 break; 1744 } 1745 case VK_NUMLOCK: 1746 /* Win32 sets the extended bit for the NumLock key. Reset it. */ 1747 flags &= ~KeyExtended; 1748 break; 1749 case VK_SNAPSHOT: 1750 flags |= KeyPrint; 1751 break; 1752 case VK_PAUSE: 1753 flags |= KeyPause; 1754 break; 1755 } 1756 1757 bool result = keyEvent(vkey, scan, flags); 1758 if (!result && m_bIsKeyboardCaptured) 1759 { 1760 /* keyEvent() returned that it didn't process the message, but since the 1761 * keyboard is captured, we don't want to pass it to Windows. We just want 1762 * to let Qt process the message (to handle non-alphanumeric <HOST>+key 1763 * shortcuts for example). So send it direcltly to the window with the 1764 * special flag in the reserved area of lParam (to avoid recursion). */ 1765 ::SendMessage(aMsg->hwnd, aMsg->message, 1766 aMsg->wParam, aMsg->lParam | (0x1 << 25)); 1767 return true; 1768 } 1769 1770 /* These special keys have to be handled by Windows as well to update the 1771 * internal modifier state and to enable/disable the keyboard LED */ 1772 if (vkey == VK_NUMLOCK || vkey == VK_CAPITAL || vkey == VK_LSHIFT || vkey == VK_RSHIFT) 1773 return false; 1774 1775 return result; 890 bool UIMachineView::winEvent(MSG *pMsg, long* /* piResult */) 891 { 892 /* Check if some system event should be filtered-out. 893 * Returning 'true' means filtering-out, 894 * Returning 'false' means passing event to Qt. */ 895 bool fResult = false; /* Pass to Qt by default: */ 896 switch (pMsg->message) 897 { 898 case WM_KEYDOWN: 899 case WM_KEYUP: 900 case WM_SYSKEYDOWN: 901 case WM_SYSKEYUP: 902 { 903 /* Filter using keyboard-filter: */ 904 bool fKeyboardFilteringResult = machineLogic()->keyboardHandler()->winEventFilter(pMsg, screenId()); 905 /* Keyboard filter rules the result: */ 906 fResult = fKeyboardFilteringResult; 907 break; 908 } 909 default: 910 break; 911 } 912 /* Return result: */ 913 return fResult; 1776 914 } 1777 915 1778 916 #elif defined(Q_WS_X11) 1779 917 1780 static Bool VBoxConsoleViewCompEvent(Display *, XEvent *pEvent, XPointer pvArg)1781 {1782 XEvent *pKeyEvent = (XEvent*)pvArg;1783 if ((pEvent->type == XKeyPress) && (pEvent->xkey.keycode == pKeyEvent->xkey.keycode))1784 return True;1785 else1786 return False;1787 }1788 1789 918 bool UIMachineView::x11Event(XEvent *pEvent) 1790 919 { 920 /* Check if some system event should be filtered-out. 921 * Returning 'true' means filtering-out, 922 * Returning 'false' means passing event to Qt. */ 923 bool fResult = false; /* Pass to Qt by default: */ 1791 924 switch (pEvent->type) 1792 925 { 1793 /* We have to handle XFocusOut right here as this event is not passed1794 * to UIMachineView::event(). Handling this event is important for1795 * releasing the keyboard before the screen saver gets active.1796 *1797 * See public ticket #3894: Apparently this makes problems with newer1798 * versions of Qt and this hack is probably not necessary anymore.1799 * So disable it for Qt >= 4.5.0. */1800 926 case XFocusOut: 1801 927 case XFocusIn: 1802 if (uisession()->isRunning())1803 {1804 if (VBoxGlobal::qtRTVersion() < ((4 << 16) | (5 << 8) | 0))1805 {1806 focusEvent(pEvent->type == XFocusIn);1807 if (pEvent->type == XFocusOut)1808 machineLogic()->mouseHandler()->releaseMouse();1809 }1810 }1811 return false;1812 928 case XKeyPress: 1813 929 case XKeyRelease: 1814 break; 930 { 931 /* Filter using keyboard-filter: */ 932 bool fKeyboardFilteringResult = machineLogic()->keyboardHandler()->x11EventFilter(pEvent, screenId()); 933 /* Filter using mouse-filter: */ 934 bool fMouseFilteringResult = machineLogic()->mouseHandler()->x11EventFilter(pEvent, screenId()); 935 /* If at least one of filters wants to filter event out then the result is 'true': */ 936 fResult = fKeyboardFilteringResult || fMouseFilteringResult; 937 break; 938 } 1815 939 default: 1816 return false; /* pass the event to Qt */ 1817 } 1818 1819 /* Translate the keycode to a PC scan code. */ 1820 unsigned scan = handleXKeyEvent(pEvent); 1821 1822 /* scancodes 0x00 (no valid translation) and 0x80 are ignored */ 1823 if (!scan & 0x7F) 1824 return true; 1825 1826 /* Fix for http://www.virtualbox.org/ticket/1296: 1827 * when X11 sends events for repeated keys, it always inserts an 1828 * XKeyRelease before the XKeyPress. */ 1829 XEvent returnEvent; 1830 if ((pEvent->type == XKeyRelease) && (XCheckIfEvent(pEvent->xkey.display, &returnEvent, 1831 VBoxConsoleViewCompEvent, (XPointer)pEvent) == True)) 1832 { 1833 XPutBackEvent(pEvent->xkey.display, &returnEvent); 1834 /* Discard it, don't pass it to Qt. */ 1835 return true; 1836 } 1837 1838 KeySym ks = ::XKeycodeToKeysym(pEvent->xkey.display, pEvent->xkey.keycode, 0); 1839 1840 int flags = 0; 1841 if (scan >> 8) 1842 flags |= KeyExtended; 1843 if (pEvent->type == XKeyPress) 1844 flags |= KeyPressed; 1845 1846 /* Remove the extended flag */ 1847 scan &= 0x7F; 1848 1849 switch (ks) 1850 { 1851 case XK_Print: 1852 flags |= KeyPrint; 1853 break; 1854 case XK_Pause: 1855 flags |= KeyPause; 1856 break; 1857 } 1858 1859 return keyEvent(ks, scan, flags); 1860 } 1861 1862 #elif defined(Q_WS_MAC) 1863 1864 void UIMachineView::darwinGrabKeyboardEvents(bool fGrab) 1865 { 1866 m_fKeyboardGrabbed = fGrab; 1867 if (fGrab) 1868 { 1869 /* Disable mouse and keyboard event compression/delaying to make sure we *really* get all of the events. */ 1870 ::CGSetLocalEventsSuppressionInterval(0.0); 1871 machineLogic()->mouseHandler()->setMouseCoalescingEnabled(false); 1872 1873 /* Register the event callback/hook and grab the keyboard. */ 1874 UICocoaApplication::instance()->registerForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */, 1875 UIMachineView::darwinEventHandlerProc, this); 1876 1877 ::DarwinGrabKeyboard (false); 1878 } 1879 else 1880 { 1881 ::DarwinReleaseKeyboard(); 1882 UICocoaApplication::instance()->unregisterForNativeEvents(RT_BIT_32(10) | RT_BIT_32(11) | RT_BIT_32(12) /* NSKeyDown | NSKeyUp | | NSFlagsChanged */, 1883 UIMachineView::darwinEventHandlerProc, this); 1884 } 1885 } 1886 1887 bool UIMachineView::darwinEventHandlerProc(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser) 1888 { 1889 UIMachineView *view = (UIMachineView*)pvUser; 1890 EventRef inEvent = (EventRef)pvCarbonEvent; 1891 UInt32 eventClass = ::GetEventClass(inEvent); 1892 1893 /* Check if this is an application key combo. In that case we will not pass 1894 * the event to the guest, but let the host process it. */ 1895 if (::darwinIsApplicationCommand(pvCocoaEvent)) 1896 return false; 1897 1898 /* All keyboard class events needs to be handled. */ 1899 if (eventClass == kEventClassKeyboard) 1900 { 1901 if (view->darwinKeyboardEvent (pvCocoaEvent, inEvent)) 1902 return true; 1903 } 1904 /* Pass the event along. */ 1905 return false; 1906 } 1907 1908 bool UIMachineView::darwinKeyboardEvent(const void *pvCocoaEvent, EventRef inEvent) 1909 { 1910 bool ret = false; 1911 UInt32 EventKind = ::GetEventKind(inEvent); 1912 if (EventKind != kEventRawKeyModifiersChanged) 1913 { 1914 /* convert keycode to set 1 scan code. */ 1915 UInt32 keyCode = ~0U; 1916 ::GetEventParameter(inEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof (keyCode), NULL, &keyCode); 1917 unsigned scanCode = ::DarwinKeycodeToSet1Scancode(keyCode); 1918 if (scanCode) 1919 { 1920 /* calc flags. */ 1921 int flags = 0; 1922 if (EventKind != kEventRawKeyUp) 1923 flags |= KeyPressed; 1924 if (scanCode & VBOXKEY_EXTENDED) 1925 flags |= KeyExtended; 1926 /** @todo KeyPause, KeyPrint. */ 1927 scanCode &= VBOXKEY_SCANCODE_MASK; 1928 1929 /* get the unicode string (if present). */ 1930 AssertCompileSize(wchar_t, 2); 1931 AssertCompileSize(UniChar, 2); 1932 ByteCount cbWritten = 0; 1933 wchar_t ucs[8]; 1934 if (::GetEventParameter(inEvent, kEventParamKeyUnicodes, typeUnicodeText, NULL, 1935 sizeof(ucs), &cbWritten, &ucs[0]) != 0) 1936 cbWritten = 0; 1937 ucs[cbWritten / sizeof(wchar_t)] = 0; /* The api doesn't terminate it. */ 1938 1939 ret = keyEvent(keyCode, scanCode, flags, ucs[0] ? ucs : NULL); 1940 } 1941 } 1942 else 1943 { 1944 /* May contain multiple modifier changes, kind of annoying. */ 1945 UInt32 newMask = 0; 1946 ::GetEventParameter(inEvent, kEventParamKeyModifiers, typeUInt32, NULL, 1947 sizeof(newMask), NULL, &newMask); 1948 newMask = ::DarwinAdjustModifierMask(newMask, pvCocoaEvent); 1949 UInt32 changed = newMask ^ m_darwinKeyModifiers; 1950 if (changed) 1951 { 1952 for (UInt32 bit = 0; bit < 32; bit++) 1953 { 1954 if (!(changed & (1 << bit))) 1955 continue; 1956 unsigned scanCode = ::DarwinModifierMaskToSet1Scancode(1 << bit); 1957 if (!scanCode) 1958 continue; 1959 unsigned keyCode = ::DarwinModifierMaskToDarwinKeycode(1 << bit); 1960 Assert(keyCode); 1961 1962 if (!(scanCode & VBOXKEY_LOCK)) 1963 { 1964 unsigned flags = (newMask & (1 << bit)) ? KeyPressed : 0; 1965 if (scanCode & VBOXKEY_EXTENDED) 1966 flags |= KeyExtended; 1967 scanCode &= VBOXKEY_SCANCODE_MASK; 1968 ret |= keyEvent(keyCode, scanCode & 0xff, flags); 1969 } 1970 else 1971 { 1972 unsigned flags = 0; 1973 if (scanCode & VBOXKEY_EXTENDED) 1974 flags |= KeyExtended; 1975 scanCode &= VBOXKEY_SCANCODE_MASK; 1976 keyEvent(keyCode, scanCode, flags | KeyPressed); 1977 keyEvent(keyCode, scanCode, flags); 1978 } 1979 } 1980 } 1981 1982 m_darwinKeyModifiers = newMask; 1983 1984 /* Always return true here because we'll otherwise getting a Qt event 1985 we don't want and that will only cause the Pause warning to pop up. */ 1986 ret = true; 1987 } 1988 1989 return ret; 940 break; 941 } 942 /* Return result: */ 943 return fResult; 1990 944 } 1991 945 -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineView.h
r30636 r30637 37 37 class UIMachineWindow; 38 38 class UIFrameBuffer; 39 class VBoxGlobalSettings;40 39 41 40 class UIMachineView : public QAbstractScrollArea … … 59 58 static void destroy(UIMachineView *pMachineView); 60 59 61 /* Public getters: */62 int keyboardState() const;63 64 60 /* Public setters: */ 65 61 virtual void setGuestAutoresizeEnabled(bool /* fEnabled */) {} … … 69 65 70 66 signals: 71 72 /* Keyboard state-change signals: */73 void keyboardStateChanged(int iState);74 67 75 68 /* Utility signals: */ … … 104 97 //virtual void cleanupConsoleConnections() {} 105 98 //virtual void cleanupFilters() {} 106 virtual void cleanupCommon();99 //virtual void cleanupCommon() {} 107 100 virtual void cleanupFrameBuffer(); 108 101 … … 121 114 ulong screenId() const { return m_uScreenId; } 122 115 UIFrameBuffer* frameBuffer() const { return m_pFrameBuffer; } 123 bool isHostKeyPressed() const { return m_bIsHostkeyPressed; }124 116 bool isMachineWindowResizeIgnored() const { return m_bIsMachineWindowResizeIgnored; } 125 117 const QPixmap& pauseShot() const { return m_pauseShot; } … … 128 120 QSize desktopGeometry() const; 129 121 QSize guestSizeHint(); 130 #ifdef Q_WS_MAC131 /* These getters are temporary here while UIKeyboardHandler is not implemented: */132 bool isHostKeyAlone() const { return m_bIsHostkeyAlone; }133 bool isKeyboardGrabbed() const { return m_fKeyboardGrabbed; }134 #endif /* Q_WS_MAC */135 122 136 123 /* Protected setters: */ … … 145 132 virtual void maybeRestrictMinimumSize() = 0; 146 133 virtual void updateSliders(); 147 void fixModifierState(LONG *piCodes, uint *puCount);148 134 QPoint viewportToContents(const QPoint &vp) const; 149 135 void scrollBy(int dx, int dy); 150 void emitKeyboardStateChanged();151 void captureKbd(bool fCapture, bool fEmitSignal = true);152 void saveKeyStates();153 void releaseAllPressedKeys(bool aReleaseHostKey = true);154 void sendChangedKeyStates();155 136 static void dimImage(QImage &img); 156 137 #ifdef VBOX_WITH_VIDEOHWACCEL … … 166 147 bool event(QEvent *pEvent); 167 148 bool eventFilter(QObject *pWatched, QEvent *pEvent); 168 void focusEvent(bool aHasFocus, bool aReleaseHostKey = true);169 bool keyEvent(int aKey, uint8_t aScan, int aFlags, wchar_t *aUniKey = NULL);170 149 void resizeEvent(QResizeEvent *pEvent); 171 150 void moveEvent(QMoveEvent *pEvent); … … 174 153 /* Platform specific event processors: */ 175 154 #if defined(Q_WS_WIN) 176 static LRESULT CALLBACK lowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);177 bool winLowKeyboardEvent(UINT msg, const KBDLLHOOKSTRUCT &event);178 155 bool winEvent(MSG *pMsg, long *puResult); 179 156 #elif defined(Q_WS_X11) 180 157 bool x11Event(XEvent *event); 181 #elif defined(Q_WS_MAC)182 void darwinGrabKeyboardEvents(bool fGrab);183 static bool darwinEventHandlerProc(const void *pvCocoaEvent, const void *pvCarbonEvent, void *pvUser);184 bool darwinKeyboardEvent(const void *pvCocoaEvent, EventRef inEvent);185 158 #endif 186 159 … … 188 161 UIMachineWindow *m_pMachineWindow; 189 162 ulong m_uScreenId; 190 const VBoxGlobalSettings &m_globalSettings;191 163 UIFrameBuffer *m_pFrameBuffer; 192 164 KMachineState m_previousState; … … 196 168 QSize m_storedConsoleSize; 197 169 198 uint8_t m_pressedKeys[128];199 uint8_t m_pressedKeysCopy[128];200 201 bool m_bIsKeyboardCaptured : 1;202 bool m_bIsHostkeyPressed : 1;203 bool m_bIsHostkeyAlone : 1;204 bool m_bIsHostkeyInCapture : 1;205 170 bool m_bIsMachineWindowResizeIgnored : 1; 206 bool m_fPassCAD : 1;207 171 #ifdef VBOX_WITH_VIDEOHWACCEL 208 172 bool m_fAccelerate2DVideo : 1; 209 173 #endif /* VBOX_WITH_VIDEOHWACCEL */ 210 174 211 #ifdef Q_WS_MAC212 /** The current modifier key mask. Used to figure out which modifier213 * key was pressed when we get a kEventRawKeyModifiersChanged event. */214 UInt32 m_darwinKeyModifiers;215 bool m_fKeyboardGrabbed;216 #endif /* Q_WS_MAC */217 218 175 QPixmap m_pauseShot; 219 176 220 177 /* Friend classes: */ 178 friend class UIKeyboardHandler; 221 179 friend class UIMouseHandler; 222 180 friend class UIMachineLogic; -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineWindow.cpp
r30636 r30637 35 35 #include "UISession.h" 36 36 #include "UIActionsPool.h" 37 #include "UIKeyboardHandler.h" 37 38 #include "UIMouseHandler.h" 38 39 #include "UIMachineLogic.h" … … 466 467 void UIMachineWindow::prepareHandlers() 467 468 { 469 /* Register keyboard-handler: */ 470 machineLogic()->keyboardHandler()->prepareListener(m_uScreenId, this); 471 468 472 /* Register machine-view in mouse-handler: */ 469 473 machineLogic()->mouseHandler()->addMachineView(m_uScreenId, this->machineView()); … … 474 478 /* Unregister machine-view from mouse-handler: */ 475 479 machineLogic()->mouseHandler()->delMachineView(m_uScreenId); 480 481 /* Unregister keyboard-handler: */ 482 machineLogic()->keyboardHandler()->cleanupListener(m_uScreenId); 476 483 } 477 484 -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMouseHandler.cpp
r30448 r30637 25 25 #include "VBoxGlobal.h" 26 26 #include "VBoxProblemReporter.h" 27 #include "UIKeyboardHandler.h" 27 28 #include "UIMouseHandler.h" 28 29 #include "UISession.h" 29 30 #include "UIMachineLogic.h" 30 31 #include "UIMachineView.h" 32 33 #ifdef Q_WS_X11 34 # include <X11/XKBlib.h> 35 # ifdef KeyPress 36 const int XFocusOut = FocusOut; 37 const int XFocusIn = FocusIn; 38 const int XKeyPress = KeyPress; 39 const int XKeyRelease = KeyRelease; 40 # undef KeyRelease 41 # undef KeyPress 42 # undef FocusOut 43 # undef FocusIn 44 # endif /* KeyPress */ 45 #endif /* Q_WS_X11 */ 31 46 32 47 #ifdef Q_WS_MAC … … 211 226 } 212 227 #endif /* Q_WS_MAC */ 228 229 #ifdef Q_WS_X11 230 bool UIMouseHandler::x11EventFilter(XEvent *pEvent, ulong /* uScreenId */) 231 { 232 /* Check if some system event should be filtered-out. 233 * Returning 'true' means filtering-out, 234 * Returning 'false' means passing event to Qt. */ 235 bool fResult = false; /* Pass to Qt by default: */ 236 switch (pEvent->type) 237 { 238 /* We have to handle XFocusOut right here as this event is not passed to UIMachineView::event(). 239 * Handling this event is important for releasing the keyboard before the screen saver gets active. 240 * See public ticket #3894: Apparently this makes problems with newer versions of Qt 241 * and this hack is probably not necessary anymore. So disable it for Qt >= 4.5.0. */ 242 case XFocusOut: 243 { 244 if (uisession()->isRunning()) 245 { 246 if (VBoxGlobal::qtRTVersion() < ((4 << 16) | (5 << 8) | 0)) 247 releaseMouse(); 248 } 249 fResult = false; 250 } 251 default: 252 break; 253 } 254 /* Return result: */ 255 return fResult; 256 } 257 #endif /* Q_WS_X11 */ 213 258 214 259 /* Machine state-change handler: */ … … 483 528 * Only do this if the keyboard/mouse is grabbed 484 529 * (this is when we have a valid event handler): */ 485 if (m _views[uScreenId]->isKeyboardGrabbed())530 if (machineLogic()->keyboardHandler()->isKeyboardGrabbed()) 486 531 setMouseCoalescingEnabled(false); 487 532 break; … … 545 590 #ifdef Q_WS_MAC 546 591 /* Simulate the right click on host-key + left-mouse-button: */ 547 if (m _views[uScreenId]->isHostKeyPressed() &&548 m _views[uScreenId]->isHostKeyAlone() &&592 if (machineLogic()->keyboardHandler()->isHostKeyPressed() && 593 machineLogic()->keyboardHandler()->isHostKeyAlone() && 549 594 iMouseButtonsState == KMouseButtonState_LeftButton) 550 595 iMouseButtonsState = KMouseButtonState_RightButton; … … 744 789 qApp->processEvents(); 745 790 #endif 746 /* Actually that will be a call to keyboard-handler, not no machine-view, 747 * but now keyboard-handler is not implemented yet. */ 748 m_views[uScreenId]->captureKbd(true); 749 791 machineLogic()->keyboardHandler()->captureKeyboard(uScreenId); 750 792 captureMouse(uScreenId); 751 793 } -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMouseHandler.h
r30410 r30637 36 36 class UIMachineLogic; 37 37 class UIMachineView; 38 #ifdef Q_WS_X11 39 typedef union _XEvent XEvent; 40 #endif /* Q_WS_X11 */ 38 41 39 42 /* Delegate to control VM mouse functionality: */ … … 69 72 void setMouseCoalescingEnabled(bool fOn); 70 73 #endif /* Q_WS_MAC */ 74 75 #ifdef Q_WS_X11 76 bool x11EventFilter(XEvent *pEvent, ulong uScreenId); 77 #endif /* Q_WS_X11 */ 71 78 72 79 signals: -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIKeyboardHandlerFullscreen.cpp
r30551 r30637 3 3 * 4 4 * VBox frontends: Qt GUI ("VirtualBox"): 5 * UI MachineViewFullscreen class implementation5 * UIKeyboardHandlerFullscreen class implementation 6 6 */ 7 7 … … 19 19 20 20 /* Global includes */ 21 #include <QApplication> 22 #include <QDesktopWidget> 23 #include <QMainWindow> 21 #include <QKeyEvent> 24 22 #include <QTimer> 25 #ifdef Q_WS_MAC 26 #include <QMenuBar> 27 #endif 28 #ifdef Q_WS_X11 29 #include <limits.h> 30 #endif 23 #include <QWidget> 31 24 32 25 /* Local includes */ 33 #include "VBoxGlobal.h" 34 #include "UISession.h" 35 #include "UIActionsPool.h" 36 #include "UIMachineLogic.h" 26 #include "UIKeyboardHandlerFullscreen.h" 37 27 #include "UIMachineWindow.h" 38 #include "UIFrameBuffer.h"39 #include "UIMachineLogicFullscreen.h"40 #include "UIMachineViewFullscreen.h"41 28 42 UIMachineViewFullscreen::UIMachineViewFullscreen( UIMachineWindow *pMachineWindow 43 , ulong uScreenId 44 #ifdef VBOX_WITH_VIDEOHWACCEL 45 , bool bAccelerate2DVideo 46 #endif 47 ) 48 : UIMachineView( pMachineWindow 49 , uScreenId 50 #ifdef VBOX_WITH_VIDEOHWACCEL 51 , bAccelerate2DVideo 52 #endif 53 ) 54 , m_bIsGuestAutoresizeEnabled(pMachineWindow->machineLogic()->actionsPool()->action(UIActionIndex_Toggle_GuestAutoresize)->isChecked()) 55 , m_fShouldWeDoResize(false) 56 , m_pSyncBlocker(0) 29 /* Fullscreen keyboard-handler constructor: */ 30 UIKeyboardHandlerFullscreen::UIKeyboardHandlerFullscreen(UIMachineLogic* pMachineLogic) 31 : UIKeyboardHandler(pMachineLogic) 57 32 { 58 /* Load machine view settings: */59 loadMachineViewSettings();60 61 /* Prepare frame buffer: */62 prepareFrameBuffer();63 64 /* Prepare common things: */65 prepareCommon();66 67 /* Prepare event-filters: */68 prepareFilters();69 70 /* Prepare console connections: */71 prepareConsoleConnections();72 73 /* Prepare fullscreen: */74 prepareFullscreen();75 76 /* Initialization: */77 sltMachineStateChanged();78 33 } 79 34 80 UIMachineViewFullscreen::~UIMachineViewFullscreen() 35 /* Fullscreen keyboard-handler destructor: */ 36 UIKeyboardHandlerFullscreen::~UIKeyboardHandlerFullscreen() 81 37 { 82 /* Cleanup fullscreen: */83 cleanupFullscreen();84 85 /* Cleanup common things: */86 cleanupCommon();87 88 /* Cleanup frame buffer: */89 cleanupFrameBuffer();90 38 } 91 39 92 void UIMachineViewFullscreen::sltPerformGuestResize(const QSize &toSize) 40 /* Event handler for prepared listener(s): */ 41 bool UIKeyboardHandlerFullscreen::eventFilter(QObject *pWatchedObject, QEvent *pEvent) 93 42 { 94 if (m_bIsGuestAutoresizeEnabled && uisession()->isGuestSupportsGraphics()) 43 /* Check if pWatchedObject object is view: */ 44 if (UIMachineView *pWatchedView = isItListenedView(pWatchedObject)) 95 45 { 96 /* Get machine window: */ 97 QMainWindow *pMachineWindow = machineWindowWrapper() && machineWindowWrapper()->machineWindow() ? 98 qobject_cast<QMainWindow*>(machineWindowWrapper()->machineWindow()) : 0; 99 100 /* If this slot is invoked directly then use the passed size otherwise get 101 * the available size for the guest display. We assume here that centralWidget() 102 * contains this view only and gives it all available space: */ 103 QSize newSize(toSize.isValid() ? toSize : pMachineWindow ? pMachineWindow->centralWidget()->size() : QSize()); 104 AssertMsg(newSize.isValid(), ("Size should be valid!\n")); 105 106 /* Do not send the same hints as we already have: */ 107 if ((newSize.width() == storedConsoleSize().width()) && (newSize.height() == storedConsoleSize().height())) 108 return; 109 110 /* We only actually send the hint if either an explicit new size was given 111 * (e.g. if the request was triggered directly by a console resize event) or 112 * if no explicit size was specified but a resize is flagged as being needed 113 * (e.g. the autoresize was just enabled and the console was resized while it was disabled). */ 114 if (toSize.isValid() || m_fShouldWeDoResize) 115 { 116 /* Remember the new size: */ 117 storeConsoleSize(newSize.width(), newSize.height()); 118 119 /* Send new size-hint to the guest: */ 120 session().GetConsole().GetDisplay().SetVideoModeHint(newSize.width(), newSize.height(), 0, screenId()); 121 } 122 123 /* We had requested resize now, rejecting other accident requests: */ 124 m_fShouldWeDoResize = false; 125 } 126 } 127 128 void UIMachineViewFullscreen::sltAdditionsStateChanged() 129 { 130 /* Check if we should restrict minimum size: */ 131 maybeRestrictMinimumSize(); 132 133 /* Check if we should resize guest to fullscreen, all the 134 * required features will be tested in sltPerformGuestResize(...): */ 135 if ((int)frameBuffer()->width() != workingArea().size().width() || 136 (int)frameBuffer()->height() != workingArea().size().height()) 137 sltPerformGuestResize(workingArea().size()); 138 } 139 140 void UIMachineViewFullscreen::sltDesktopResized() 141 { 142 /* If the desktop geometry is set automatically, this will update it: */ 143 calculateDesktopGeometry(); 144 } 145 146 bool UIMachineViewFullscreen::event(QEvent *pEvent) 147 { 148 switch (pEvent->type()) 149 { 150 case VBoxDefs::ResizeEventType: 151 { 152 /* Some situations requires framebuffer resize events to be ignored at all, 153 * leaving machine-window, machine-view and framebuffer sizes preserved: */ 154 if (uisession()->isGuestResizeIgnored()) 155 return true; 156 157 /* We are starting to perform machine-view resize: */ 158 bool oldIgnoreMainwndResize = isMachineWindowResizeIgnored(); 159 setMachineWindowResizeIgnored(true); 160 161 /* Get guest resize-event: */ 162 UIResizeEvent *pResizeEvent = static_cast<UIResizeEvent*>(pEvent); 163 164 /* Perform framebuffer resize: */ 165 frameBuffer()->resizeEvent(pResizeEvent); 166 167 /* Reapply maximum size restriction for machine-view: */ 168 setMaximumSize(sizeHint()); 169 170 /* Store the new size to prevent unwanted resize hints being sent back: */ 171 storeConsoleSize(pResizeEvent->width(), pResizeEvent->height()); 172 173 /* Perform machine-view resize: */ 174 resize(pResizeEvent->width(), pResizeEvent->height()); 175 176 /* Let our toplevel widget calculate its sizeHint properly. */ 177 #ifdef Q_WS_X11 178 /* We use processEvents rather than sendPostedEvents & set the time out value to max cause on X11 otherwise 179 * the layout isn't calculated correctly. Dosn't find the bug in Qt, but this could be triggered through 180 * the async nature of the X11 window event system. */ 181 QCoreApplication::processEvents(QEventLoop::AllEvents, INT_MAX); 182 #else /* Q_WS_X11 */ 183 QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest); 184 #endif /* Q_WS_X11 */ 185 186 #ifdef Q_WS_MAC 187 machineLogic()->updateDockIconSize(screenId(), pResizeEvent->width(), pResizeEvent->height()); 188 #endif /* Q_WS_MAC */ 189 190 /* May be we have to restrict minimum size? */ 191 maybeRestrictMinimumSize(); 192 193 /* Update machine-view sliders: */ 194 updateSliders(); 195 196 /* Report to the VM thread that we finished resizing */ 197 session().GetConsole().GetDisplay().ResizeCompleted(screenId()); 198 199 /* We are finishing to perform machine-view resize: */ 200 setMachineWindowResizeIgnored(oldIgnoreMainwndResize); 201 202 /* Make sure that all posted signals are processed: */ 203 qApp->processEvents(); 204 205 /* We also recalculate the desktop geometry if this is determined 206 * automatically. In fact, we only need this on the first resize, 207 * but it is done every time to keep the code simpler. */ 208 calculateDesktopGeometry(); 209 210 /* Emit a signal about guest was resized: */ 211 emit resizeHintDone(); 212 213 /* Unlock after processing guest resize event: */ 214 if (m_pSyncBlocker && m_pSyncBlocker->isRunning()) 215 m_pSyncBlocker->quit(); 216 217 return true; 218 } 219 220 case QEvent::KeyPress: 221 case QEvent::KeyRelease: 222 { 223 /* Get key-event: */ 224 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent); 225 226 /* Process Host+Home for menu popup: */ 227 if (isHostKeyPressed() && pEvent->type() == QEvent::KeyPress) 228 { 229 if (pKeyEvent->key() == Qt::Key_Home) 230 QTimer::singleShot(0, machineWindowWrapper()->machineWindow(), SLOT(sltPopupMainMenu())); 231 else 232 pEvent->ignore(); 233 } 234 235 break; 236 } 237 238 default: 239 break; 240 } 241 return UIMachineView::event(pEvent); 242 } 243 244 bool UIMachineViewFullscreen::eventFilter(QObject *pWatched, QEvent *pEvent) 245 { 246 /* Who are we watching? */ 247 QMainWindow *pMainDialog = machineWindowWrapper() && machineWindowWrapper()->machineWindow() ? 248 qobject_cast<QMainWindow*>(machineWindowWrapper()->machineWindow()) : 0; 249 250 if (pWatched != 0 && pWatched == pMainDialog) 251 { 46 /* Get corresponding screen index: */ 47 ulong uScreenId = m_views.key(pWatchedView); 48 NOREF(uScreenId); 49 /* Handle view events: */ 252 50 switch (pEvent->type()) 253 51 { 254 case QEvent:: Resize:52 case QEvent::KeyPress: 255 53 { 256 /* Send guest-resize hint only if top window resizing to required dimension: */ 257 QResizeEvent *pResizeEvent = static_cast<QResizeEvent*>(pEvent); 258 if (pResizeEvent->size() != workingArea().size()) 259 break; 260 261 /* Set the "guest needs to resize" hint. 262 * This hint is acted upon when (and only when) the autoresize property is "true": */ 263 m_fShouldWeDoResize = uisession()->isGuestSupportsGraphics(); 264 if (m_bIsGuestAutoresizeEnabled && m_fShouldWeDoResize) 265 QTimer::singleShot(0, this, SLOT(sltPerformGuestResize())); 54 /* Get key-event: */ 55 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent); 56 /* Process Host+Home for menu popup: */ 57 if (isHostKeyPressed() && pEvent->type() == QEvent::KeyPress) 58 { 59 if (pKeyEvent->key() == Qt::Key_Home) 60 { 61 /* Post request to show popup-menu: */ 62 QTimer::singleShot(0, m_windows[uScreenId]->machineWindow(), SLOT(sltPopupMainMenu())); 63 /* Filter-out this event: */ 64 return true; 65 } 66 } 266 67 break; 267 68 } … … 271 72 } 272 73 273 return UIMachineView::eventFilter(pWatched, pEvent); 74 /* Else just propagate to base-class: */ 75 return UIKeyboardHandler::eventFilter(pWatchedObject, pEvent); 274 76 } 275 77 276 void UIMachineViewFullscreen::prepareCommon()277 {278 /* Base class common settings: */279 UIMachineView::prepareCommon();280 281 /* Setup size-policy: */282 setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));283 /* Maximum size to sizehint: */284 setMaximumSize(sizeHint());285 /* Minimum size is ignored: */286 setMinimumSize(0, 0);287 /* No scrollbars: */288 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);289 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);290 }291 292 void UIMachineViewFullscreen::prepareFilters()293 {294 /* Base class filters: */295 UIMachineView::prepareFilters();296 297 #ifdef Q_WS_MAC // TODO: Is it really needed? See UIMachineViewSeamless::eventFilter(...);298 /* Menu bar filter: */299 qobject_cast<QMainWindow*>(machineWindowWrapper()->machineWindow())->menuBar()->installEventFilter(this);300 #endif301 }302 303 void UIMachineViewFullscreen::prepareConnections()304 {305 connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(sltDesktopResized()));306 }307 308 void UIMachineViewFullscreen::prepareConsoleConnections()309 {310 /* Base class connections: */311 UIMachineView::prepareConsoleConnections();312 313 /* Guest additions state-change updater: */314 connect(uisession(), SIGNAL(sigAdditionsStateChange()), this, SLOT(sltAdditionsStateChanged()));315 }316 317 void UIMachineViewFullscreen::prepareFullscreen()318 {319 /* Create sync-blocker: */320 m_pSyncBlocker = new UIMachineViewBlocker;321 }322 323 void UIMachineViewFullscreen::cleanupFullscreen()324 {325 /* If machine still running: */326 if (uisession()->isRunning())327 {328 /* And guest supports advanced graphics management which is enabled: */329 if (m_bIsGuestAutoresizeEnabled && uisession()->isGuestSupportsGraphics())330 {331 /* Rollback seamless frame-buffer size to normal: */332 machineWindowWrapper()->machineWindow()->hide();333 sltPerformGuestResize(guestSizeHint());334 m_pSyncBlocker->exec();335 336 /* Request to delete sync-blocker: */337 m_pSyncBlocker->deleteLater();338 }339 }340 }341 342 void UIMachineViewFullscreen::setGuestAutoresizeEnabled(bool fEnabled)343 {344 if (m_bIsGuestAutoresizeEnabled != fEnabled)345 {346 m_bIsGuestAutoresizeEnabled = fEnabled;347 348 maybeRestrictMinimumSize();349 350 sltPerformGuestResize();351 }352 }353 354 QRect UIMachineViewFullscreen::workingArea()355 {356 /* Get corresponding screen: */357 int iScreen = static_cast<UIMachineLogicFullscreen*>(machineLogic())->hostScreenForGuestScreen(screenId());358 /* Return available geometry for that screen: */359 return QApplication::desktop()->screenGeometry(iScreen);360 }361 362 void UIMachineViewFullscreen::calculateDesktopGeometry()363 {364 /* This method should not get called until we have initially set up the desktop geometry type: */365 Assert((desktopGeometryType() != DesktopGeo_Invalid));366 /* If we are not doing automatic geometry calculation then there is nothing to do: */367 if (desktopGeometryType() == DesktopGeo_Automatic)368 m_desktopGeometry = workingArea().size();369 }370 371 void UIMachineViewFullscreen::maybeRestrictMinimumSize()372 {373 /* Sets the minimum size restriction depending on the auto-resize feature state and the current rendering mode.374 * Currently, the restriction is set only in SDL mode and only when the auto-resize feature is inactive.375 * We need to do that because we cannot correctly draw in a scrolled window in SDL mode.376 * In all other modes, or when auto-resize is in force, this function does nothing. */377 if (vboxGlobal().vmRenderMode() == VBoxDefs::SDLMode)378 {379 if (!uisession()->isGuestSupportsGraphics() || !m_bIsGuestAutoresizeEnabled)380 setMinimumSize(sizeHint());381 else382 setMinimumSize(0, 0);383 }384 }385 -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIKeyboardHandlerFullscreen.h
r30551 r30637 2 2 * 3 3 * VBox frontends: Qt GUI ("VirtualBox"): 4 * UI MachineViewFullscreen class declaration4 * UIKeyboardHandlerFullscreen class declaration 5 5 */ 6 6 … … 17 17 */ 18 18 19 #ifndef ___UI MachineViewFullscreen_h___20 #define ___UI MachineViewFullscreen_h___19 #ifndef ___UIKeyboardHandlerFullscreen_h___ 20 #define ___UIKeyboardHandlerFullscreen_h___ 21 21 22 22 /* Local includes */ 23 #include "UI MachineView.h"23 #include "UIKeyboardHandler.h" 24 24 25 class UI MachineViewFullscreen : public UIMachineView25 class UIKeyboardHandlerFullscreen : public UIKeyboardHandler 26 26 { 27 27 Q_OBJECT; … … 29 29 protected: 30 30 31 /* Fullscreen machine-view constructor: */ 32 UIMachineViewFullscreen( UIMachineWindow *pMachineWindow 33 , ulong uScreenId 34 #ifdef VBOX_WITH_VIDEOHWACCEL 35 , bool bAccelerate2DVideo 36 #endif 37 ); 38 /* Fullscreen machine-view destructor: */ 39 virtual ~UIMachineViewFullscreen(); 40 41 private slots: 42 43 /* Slot to perform guest resize: */ 44 void sltPerformGuestResize(const QSize &aSize = QSize()); 45 46 /* Console callback handlers: */ 47 void sltAdditionsStateChanged(); 48 49 /* Watch dog for desktop resizes: */ 50 void sltDesktopResized(); 31 /* Fullscreen keyboard-handler constructor/destructor: */ 32 UIKeyboardHandlerFullscreen(UIMachineLogic *pMachineLogic); 33 virtual ~UIKeyboardHandlerFullscreen(); 51 34 52 35 private: 53 36 54 37 /* Event handlers: */ 55 bool event(QEvent *pEvent);56 38 bool eventFilter(QObject *pWatched, QEvent *pEvent); 57 39 58 /* Prepare routines: */59 void prepareCommon();60 void prepareFilters();61 void prepareConnections();62 void prepareConsoleConnections();63 void prepareFullscreen();64 65 /* Cleanup routines: */66 void cleanupFullscreen();67 //void cleanupConsoleConnections() {}68 //void cleanupConnections() {}69 //void cleanupFilters() {}70 //void cleanupCommon() {}71 72 /* Private setters: */73 void setGuestAutoresizeEnabled(bool bEnabled);74 75 /* Private helpers: */76 void normalizeGeometry(bool /* fAdjustPosition */) {}77 QRect workingArea();78 void calculateDesktopGeometry();79 void maybeRestrictMinimumSize();80 81 /* Private variables: */82 bool m_bIsGuestAutoresizeEnabled : 1;83 bool m_fShouldWeDoResize : 1;84 UIMachineViewBlocker *m_pSyncBlocker;85 86 40 /* Friend classes: */ 87 friend class UI MachineView;41 friend class UIKeyboardHandler; 88 42 }; 89 43 90 #endif // !___UI MachineViewFullscreen_h___44 #endif // !___UIKeyboardHandlerFullscreen_h___ 91 45 -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/fullscreen/UIMachineViewFullscreen.cpp
r30544 r30637 83 83 cleanupFullscreen(); 84 84 85 /* Cleanup common things: */86 cleanupCommon();87 88 85 /* Cleanup frame buffer: */ 89 86 cleanupFrameBuffer(); … … 216 213 217 214 return true; 218 }219 220 case QEvent::KeyPress:221 case QEvent::KeyRelease:222 {223 /* Get key-event: */224 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);225 226 /* Process Host+Home for menu popup: */227 if (isHostKeyPressed() && pEvent->type() == QEvent::KeyPress)228 {229 if (pKeyEvent->key() == Qt::Key_Home)230 QTimer::singleShot(0, machineWindowWrapper()->machineWindow(), SLOT(sltPopupMainMenu()));231 else232 pEvent->ignore();233 }234 235 break;236 215 } 237 216 -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIKeyboardHandlerNormal.cpp
r30551 r30637 3 3 * 4 4 * VBox frontends: Qt GUI ("VirtualBox"): 5 * UI MachineViewNormal class implementation5 * UIKeyboardHandlerNormal class implementation 6 6 */ 7 7 … … 19 19 20 20 /* Global includes */ 21 #include <QApplication>22 #include <QDesktopWidget>23 21 #include <QMainWindow> 24 22 #include <QMenuBar> 25 #include <QScrollBar> 26 #include <QTimer> 23 #include <QKeyEvent> 27 24 28 25 /* Local includes */ 29 #include "VBoxGlobal.h" 30 #include "UISession.h" 31 #include "UIActionsPool.h" 32 #include "UIMachineLogic.h" 26 #include "UIKeyboardHandlerNormal.h" 33 27 #include "UIMachineWindow.h" 34 #include "UIFrameBuffer.h"35 #include "UIMachineViewNormal.h"36 28 37 UIMachineViewNormal::UIMachineViewNormal( UIMachineWindow *pMachineWindow 38 , ulong uScreenId 39 #ifdef VBOX_WITH_VIDEOHWACCEL 40 , bool bAccelerate2DVideo 41 #endif 42 ) 43 : UIMachineView( pMachineWindow 44 , uScreenId 45 #ifdef VBOX_WITH_VIDEOHWACCEL 46 , bAccelerate2DVideo 47 #endif 48 ) 49 , m_bIsGuestAutoresizeEnabled(pMachineWindow->machineLogic()->actionsPool()->action(UIActionIndex_Toggle_GuestAutoresize)->isChecked()) 50 , m_fShouldWeDoResize(false) 29 /* Fullscreen keyboard-handler constructor: */ 30 UIKeyboardHandlerNormal::UIKeyboardHandlerNormal(UIMachineLogic* pMachineLogic) 31 : UIKeyboardHandler(pMachineLogic) 51 32 { 52 /* Load machine view settings: */53 loadMachineViewSettings();54 55 /* Prepare frame buffer: */56 prepareFrameBuffer();57 58 /* Prepare common things: */59 prepareCommon();60 61 /* Prepare event-filters: */62 prepareFilters();63 64 /* Prepare connections: */65 prepareConnections();66 67 /* Prepare console connections: */68 prepareConsoleConnections();69 70 /* Initialization: */71 sltMachineStateChanged();72 sltAdditionsStateChanged();73 33 } 74 34 75 UIMachineViewNormal::~UIMachineViewNormal() 35 /* Fullscreen keyboard-handler destructor: */ 36 UIKeyboardHandlerNormal::~UIKeyboardHandlerNormal() 76 37 { 77 /* Save machine view settings: */78 saveMachineViewSettings();79 80 /* Cleanup common things: */81 cleanupCommon();82 83 /* Cleanup frame buffer: */84 cleanupFrameBuffer();85 38 } 86 39 87 void UIMachineViewNormal::sltPerformGuestResize(const QSize &toSize) 40 /* Event handler for prepared listener(s): */ 41 bool UIKeyboardHandlerNormal::eventFilter(QObject *pWatchedObject, QEvent *pEvent) 88 42 { 89 if (m_bIsGuestAutoresizeEnabled && uisession()->isGuestSupportsGraphics()) 43 /* Check if pWatchedObject object is view: */ 44 if (UIMachineView *pWatchedView = isItListenedView(pWatchedObject)) 90 45 { 91 /* Get machine window: */ 92 QMainWindow *pMachineWindow = machineWindowWrapper() && machineWindowWrapper()->machineWindow() ? 93 qobject_cast<QMainWindow*>(machineWindowWrapper()->machineWindow()) : 0; 94 95 /* If this slot is invoked directly then use the passed size otherwise get 96 * the available size for the guest display. We assume here that centralWidget() 97 * contains this view only and gives it all available space: */ 98 QSize newSize(toSize.isValid() ? toSize : pMachineWindow ? pMachineWindow->centralWidget()->size() : QSize()); 99 AssertMsg(newSize.isValid(), ("Size should be valid!\n")); 100 101 /* Do not send the same hints as we already have: */ 102 if ((newSize.width() == storedConsoleSize().width()) && (newSize.height() == storedConsoleSize().height())) 103 return; 104 105 /* We only actually send the hint if either an explicit new size was given 106 * (e.g. if the request was triggered directly by a console resize event) or 107 * if no explicit size was specified but a resize is flagged as being needed 108 * (e.g. the autoresize was just enabled and the console was resized while it was disabled). */ 109 if (toSize.isValid() || m_fShouldWeDoResize) 46 /* Get corresponding screen index: */ 47 ulong uScreenId = m_views.key(pWatchedView); 48 NOREF(uScreenId); 49 /* Handle view events: */ 50 switch (pEvent->type()) 110 51 { 111 /* Remember the new size: */112 storeConsoleSize(newSize.width(), newSize.height());113 114 /* Send new size-hint to the guest: */115 session().GetConsole().GetDisplay().SetVideoModeHint(newSize.width(), newSize.height(), 0, screenId());116 }117 118 /* We had requested resize now, rejecting other accident requests: */119 m_fShouldWeDoResize = false;120 }121 }122 123 void UIMachineViewNormal::sltAdditionsStateChanged()124 {125 /* Check if we should restrict minimum size: */126 maybeRestrictMinimumSize();127 }128 129 void UIMachineViewNormal::sltDesktopResized()130 {131 /* If the desktop geometry is set automatically, this will update it: */132 calculateDesktopGeometry();133 }134 135 bool UIMachineViewNormal::event(QEvent *pEvent)136 {137 switch (pEvent->type())138 {139 case VBoxDefs::ResizeEventType:140 {141 /* Some situations require framebuffer resize events to be ignored at all,142 * leaving machine-window, machine-view and framebuffer sizes preserved: */143 if (uisession()->isGuestResizeIgnored())144 return true;145 146 /* We are starting to perform machine-view resize: */147 bool oldIgnoreMainwndResize = isMachineWindowResizeIgnored();148 setMachineWindowResizeIgnored(true);149 150 /* Get guest resize-event: */151 UIResizeEvent *pResizeEvent = static_cast<UIResizeEvent*>(pEvent);152 153 /* Perform framebuffer resize: */154 frameBuffer()->resizeEvent(pResizeEvent);155 156 /* Reapply maximum size restriction for machine-view: */157 setMaximumSize(sizeHint());158 159 /* Store the new size to prevent unwanted resize hints being sent back: */160 storeConsoleSize(pResizeEvent->width(), pResizeEvent->height());161 162 /* Perform machine-view resize: */163 resize(pResizeEvent->width(), pResizeEvent->height());164 165 /* Let our toplevel widget calculate its sizeHint properly. */166 #ifdef Q_WS_X11167 /* We use processEvents rather than sendPostedEvents & set the time out value to max cause on X11 otherwise168 * the layout isn't calculated correctly. Dosn't find the bug in Qt, but this could be triggered through169 * the async nature of the X11 window event system. */170 QCoreApplication::processEvents(QEventLoop::AllEvents, INT_MAX);171 #else /* Q_WS_X11 */172 QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);173 #endif /* Q_WS_X11 */174 175 #ifdef Q_WS_MAC176 machineLogic()->updateDockIconSize(screenId(), pResizeEvent->width(), pResizeEvent->height());177 #endif /* Q_WS_MAC */178 179 /* May be we have to restrict minimum size? */180 maybeRestrictMinimumSize();181 182 /* Update machine-view sliders: */183 updateSliders();184 185 /* Normalize geometry: */186 normalizeGeometry(true /* adjustPosition */);187 188 /* Report to the VM thread that we finished resizing */189 session().GetConsole().GetDisplay().ResizeCompleted(screenId());190 191 /* We are finishing to perform machine-view resize: */192 setMachineWindowResizeIgnored(oldIgnoreMainwndResize);193 194 /* Make sure that all posted signals are processed: */195 qApp->processEvents();196 197 /* We also recalculate the desktop geometry if this is determined198 * automatically. In fact, we only need this on the first resize,199 * but it is done every time to keep the code simpler. */200 calculateDesktopGeometry();201 202 /* Emit a signal about guest was resized: */203 emit resizeHintDone();204 205 return true;206 }207 208 52 #ifndef Q_WS_MAC 209 /* We don't want this on the Mac, cause there the menu bar isn't within the 210 * window and popping up a menu there looks really ugly. */ 211 case QEvent::KeyPress: 212 case QEvent::KeyRelease: 213 { 214 /* Get key-event: */ 215 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent); 216 217 /* Process Host+Home as menu-bar activator: */ 218 if (isHostKeyPressed() && pEvent->type() == QEvent::KeyPress) 53 /* We don't want this on the Mac, cause there the menu-bar isn't within the 54 * window and popping up a menu there looks really ugly. */ 55 case QEvent::KeyPress: 219 56 { 220 if (pKeyEvent->key() == Qt::Key_Home) 57 /* Get key-event: */ 58 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent); 59 /* Process Host+Home as menu-bar activator: */ 60 if (isHostKeyPressed() && pEvent->type() == QEvent::KeyPress) 221 61 { 222 /* Trying to get menu-bar: */ 223 QMenuBar *pMenuBar = machineWindowWrapper() && machineWindowWrapper()->machineWindow() ? 224 qobject_cast<QMainWindow*>(machineWindowWrapper()->machineWindow())->menuBar() : 0; 225 226 /* If menu-bar is present and have actions: */ 227 if (pMenuBar && !pMenuBar->actions().isEmpty()) 62 if (pKeyEvent->key() == Qt::Key_Home) 228 63 { 229 /* If 'active' action is NOT chosen: */ 230 if (!pMenuBar->activeAction()) 231 /* Set first menu-bar action as 'active': */ 232 pMenuBar->setActiveAction(pMenuBar->actions()[0]); 233 234 /* If 'active' action is chosen: */ 235 if (pMenuBar->activeAction()) 64 /* Trying to get menu-bar: */ 65 QMenuBar *pMenuBar = qobject_cast<QMainWindow*>(m_windows[uScreenId]->machineWindow())->menuBar(); 66 /* If menu-bar is present and have actions: */ 67 if (pMenuBar && !pMenuBar->actions().isEmpty()) 236 68 { 237 /* Activate 'active' menu-bar action: */ 238 pMenuBar->activeAction()->activate(QAction::Trigger); 239 69 /* If 'active' action is NOT chosen: */ 70 if (!pMenuBar->activeAction()) 71 /* Set first menu-bar action as 'active': */ 72 pMenuBar->setActiveAction(pMenuBar->actions()[0]); 73 /* If 'active' action is chosen: */ 74 if (pMenuBar->activeAction()) 75 { 76 /* Activate 'active' menu-bar action: */ 77 pMenuBar->activeAction()->activate(QAction::Trigger); 240 78 #ifdef Q_WS_WIN 241 /* Windows host needs separate 'focus set'242 * to let menubar operate while popped up: */243 pMenuBar->setFocus();79 /* Windows host needs separate 'focus set' 80 * to let menubar operate while popped up: */ 81 pMenuBar->setFocus(); 244 82 #endif /* Q_WS_WIN */ 245 246 /* Accept this event: */ 247 pEvent->accept(); 248 return true; 83 /* Filter-out this event: */ 84 return true; 85 } 249 86 } 250 87 } 251 88 } 252 else253 pEvent->ignore();254 }255 256 break;257 }258 #endif /* !Q_WS_MAC */259 260 default:261 break;262 }263 return UIMachineView::event(pEvent);264 }265 266 bool UIMachineViewNormal::eventFilter(QObject *pWatched, QEvent *pEvent)267 {268 /* Who are we watching? */269 QMainWindow *pMainDialog = machineWindowWrapper() && machineWindowWrapper()->machineWindow() ?270 qobject_cast<QMainWindow*>(machineWindowWrapper()->machineWindow()) : 0;271 #ifdef Q_WS_WIN272 QMenuBar *pMenuBar = pMainDialog ? pMainDialog->menuBar() : 0;273 #endif /* Q_WS_WIN */274 275 if (pWatched != 0 && pWatched == pMainDialog)276 {277 switch (pEvent->type())278 {279 case QEvent::Resize:280 {281 /* Set the "guest needs to resize" hint.282 * This hint is acted upon when (and only when) the autoresize property is "true": */283 m_fShouldWeDoResize = uisession()->isGuestSupportsGraphics();284 if (!isMachineWindowResizeIgnored() && m_bIsGuestAutoresizeEnabled && uisession()->isGuestSupportsGraphics())285 QTimer::singleShot(300, this, SLOT(sltPerformGuestResize()));286 89 break; 287 90 } 288 #if defined (Q_WS_WIN32) 289 # if defined (VBOX_GUI_USE_DDRAW) 290 case QEvent::Move: 291 { 292 /* Notification from our parent that it has moved. We need this in order 293 * to possibly adjust the direct screen blitting: */ 294 if (frameBuffer()) 295 frameBuffer()->moveEvent(static_cast<QMoveEvent*>(pEvent)); 296 break; 297 } 298 # endif /* defined (VBOX_GUI_USE_DDRAW) */ 299 #endif /* defined (Q_WS_WIN32) */ 91 #endif /* !Q_WS_MAC */ 300 92 default: 301 93 break; … … 303 95 } 304 96 305 #ifdef Q_WS_WIN 306 else if (pWatched != 0 && pWatched == pMenuBar) 307 { 308 /* Due to windows host uses separate 'focus set' to let menubar to 309 * operate while popped up (see UIMachineViewNormal::event() for details), 310 * it also requires backward processing: */ 311 switch (pEvent->type()) 312 { 313 /* If menubar gets the focus while not popped up => give it back: */ 314 case QEvent::FocusIn: 315 { 316 if (!QApplication::activePopupWidget()) 317 setFocus(); 318 } 319 default: 320 break; 321 } 322 } 323 #endif /* Q_WS_WIN */ 324 325 return UIMachineView::eventFilter(pWatched, pEvent); 97 /* Else just propagate to base-class: */ 98 return UIKeyboardHandler::eventFilter(pWatchedObject, pEvent); 326 99 } 327 100 328 void UIMachineViewNormal::prepareCommon()329 {330 /* Base class common settings: */331 UIMachineView::prepareCommon();332 333 /* Setup size-policy: */334 setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));335 /* Maximum size to sizehint: */336 setMaximumSize(sizeHint());337 }338 339 void UIMachineViewNormal::prepareFilters()340 {341 /* Base class filters: */342 UIMachineView::prepareFilters();343 344 /* Menu bar filters: */345 qobject_cast<QMainWindow*>(machineWindowWrapper()->machineWindow())->menuBar()->installEventFilter(this);346 }347 348 void UIMachineViewNormal::prepareConnections()349 {350 connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(sltDesktopResized()));351 }352 353 void UIMachineViewNormal::prepareConsoleConnections()354 {355 /* Base class connections: */356 UIMachineView::prepareConsoleConnections();357 358 /* Guest additions state-change updater: */359 connect(uisession(), SIGNAL(sigAdditionsStateChange()), this, SLOT(sltAdditionsStateChanged()));360 }361 362 void UIMachineViewNormal::saveMachineViewSettings()363 {364 /* Store guest size hint: */365 storeGuestSizeHint(QSize(frameBuffer()->width(), frameBuffer()->height()));366 }367 368 void UIMachineViewNormal::setGuestAutoresizeEnabled(bool fEnabled)369 {370 if (m_bIsGuestAutoresizeEnabled != fEnabled)371 {372 m_bIsGuestAutoresizeEnabled = fEnabled;373 374 maybeRestrictMinimumSize();375 376 if (m_bIsGuestAutoresizeEnabled && uisession()->isGuestSupportsGraphics())377 sltPerformGuestResize();378 }379 }380 381 void UIMachineViewNormal::normalizeGeometry(bool bAdjustPosition)382 {383 QWidget *pTopLevelWidget = window();384 385 /* Make no normalizeGeometry in case we are in manual resize mode or main window is maximized: */386 if (pTopLevelWidget->isMaximized())387 return;388 389 /* Calculate client window offsets: */390 QRect frameGeo = pTopLevelWidget->frameGeometry();391 QRect geo = pTopLevelWidget->geometry();392 int dl = geo.left() - frameGeo.left();393 int dt = geo.top() - frameGeo.top();394 int dr = frameGeo.right() - geo.right();395 int db = frameGeo.bottom() - geo.bottom();396 397 /* Get the best size w/o scroll bars: */398 QSize s = pTopLevelWidget->sizeHint();399 400 /* Resize the frame to fit the contents: */401 s -= pTopLevelWidget->size();402 frameGeo.setRight(frameGeo.right() + s.width());403 frameGeo.setBottom(frameGeo.bottom() + s.height());404 405 if (bAdjustPosition)406 {407 QRegion availableGeo;408 QDesktopWidget *dwt = QApplication::desktop();409 if (dwt->isVirtualDesktop())410 /* Compose complex available region */411 for (int i = 0; i < dwt->numScreens(); ++ i)412 availableGeo += dwt->availableGeometry(i);413 else414 /* Get just a simple available rectangle */415 availableGeo = dwt->availableGeometry(pTopLevelWidget->pos());416 417 frameGeo = VBoxGlobal::normalizeGeometry(frameGeo, availableGeo, vboxGlobal().vmRenderMode() != VBoxDefs::SDLMode /* can resize? */);418 }419 420 #if 0421 /* Center the frame on the desktop: */422 frameGeo.moveCenter(availableGeo.center());423 #endif424 425 /* Finally, set the frame geometry */426 pTopLevelWidget->setGeometry(frameGeo.left() + dl, frameGeo.top() + dt, frameGeo.width() - dl - dr, frameGeo.height() - dt - db);427 }428 429 QRect UIMachineViewNormal::workingArea()430 {431 return QApplication::desktop()->availableGeometry(this);432 }433 434 void UIMachineViewNormal::calculateDesktopGeometry()435 {436 /* This method should not get called until we have initially set up the desktop geometry type: */437 Assert((desktopGeometryType() != DesktopGeo_Invalid));438 /* If we are not doing automatic geometry calculation then there is nothing to do: */439 if (desktopGeometryType() == DesktopGeo_Automatic)440 {441 /* The area taken up by the machine window on the desktop,442 * including window frame, title, menu bar and status bar: */443 QRect windowGeo = machineWindowWrapper()->machineWindow()->frameGeometry();444 /* The area taken up by the machine central widget, so excluding all decorations: */445 QRect centralWidgetGeo = static_cast<QMainWindow*>(machineWindowWrapper()->machineWindow())->centralWidget()->geometry();446 /* To work out how big we can make the console window while still fitting on the desktop,447 * we calculate workingArea() - (windowGeo - centralWidgetGeo).448 * This works because the difference between machine window and machine central widget449 * (or at least its width and height) is a constant. */450 m_desktopGeometry = QSize(workingArea().width() - (windowGeo.width() - centralWidgetGeo.width()),451 workingArea().height() - (windowGeo.height() - centralWidgetGeo.height()));452 }453 }454 455 void UIMachineViewNormal::maybeRestrictMinimumSize()456 {457 /* Sets the minimum size restriction depending on the auto-resize feature state and the current rendering mode.458 * Currently, the restriction is set only in SDL mode and only when the auto-resize feature is inactive.459 * We need to do that because we cannot correctly draw in a scrolled window in SDL mode.460 * In all other modes, or when auto-resize is in force, this function does nothing. */461 if (vboxGlobal().vmRenderMode() == VBoxDefs::SDLMode)462 {463 if (!uisession()->isGuestSupportsGraphics() || !m_bIsGuestAutoresizeEnabled)464 setMinimumSize(sizeHint());465 else466 setMinimumSize(0, 0);467 }468 }469 -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIKeyboardHandlerNormal.h
r30551 r30637 2 2 * 3 3 * VBox frontends: Qt GUI ("VirtualBox"): 4 * UI MachineViewNormal class declaration4 * UIKeyboardHandlerNormal class declaration 5 5 */ 6 6 … … 17 17 */ 18 18 19 #ifndef ___UI MachineViewNormal_h___20 #define ___UI MachineViewNormal_h___19 #ifndef ___UIKeyboardHandlerNormal_h___ 20 #define ___UIKeyboardHandlerNormal_h___ 21 21 22 22 /* Local includes */ 23 #include "UI MachineView.h"23 #include "UIKeyboardHandler.h" 24 24 25 class UI MachineViewNormal : public UIMachineView25 class UIKeyboardHandlerNormal : public UIKeyboardHandler 26 26 { 27 27 Q_OBJECT; … … 29 29 protected: 30 30 31 /* Normal machine-view constructor: */ 32 UIMachineViewNormal( UIMachineWindow *pMachineWindow 33 , ulong uScreenId 34 #ifdef VBOX_WITH_VIDEOHWACCEL 35 , bool bAccelerate2DVideo 36 #endif 37 ); 38 /* Normal machine-view destructor: */ 39 virtual ~UIMachineViewNormal(); 40 41 private slots: 42 43 /* Slot to perform guest resize: */ 44 void sltPerformGuestResize(const QSize &size = QSize()); 45 46 /* Console callback handlers: */ 47 void sltAdditionsStateChanged(); 48 49 /* Watch dog for desktop resizes: */ 50 void sltDesktopResized(); 51 52 #ifdef Q_WS_X11 53 /* Slot to perform synchronized geometry normalization. 54 * Currently its only required under X11 as of its async nature: */ 55 virtual void sltNormalizeGeometry() { normalizeGeometry(true); } 56 #endif /* Q_WS_X11 */ 31 /* Fullscreen keyboard-handler constructor/destructor: */ 32 UIKeyboardHandlerNormal(UIMachineLogic *pMachineLogic); 33 virtual ~UIKeyboardHandlerNormal(); 57 34 58 35 private: 59 36 60 37 /* Event handlers: */ 61 bool event(QEvent *pEvent);62 38 bool eventFilter(QObject *pWatched, QEvent *pEvent); 63 39 64 /* Prepare helpers: */65 void prepareCommon();66 void prepareFilters();67 void prepareConnections();68 void prepareConsoleConnections();69 //void loadMachineViewSettings();70 71 /* Cleanup helpers: */72 void saveMachineViewSettings();73 //void cleanupConsoleConnections() {}74 //void prepareConnections() {}75 //void cleanupFilters() {}76 //void cleanupCommon() {}77 78 /* Hidden setters: */79 void setGuestAutoresizeEnabled(bool bEnabled);80 81 /* Private helpers: */82 void normalizeGeometry(bool fAdjustPosition);83 QRect workingArea();84 void calculateDesktopGeometry();85 void maybeRestrictMinimumSize();86 87 /* Private members: */88 bool m_bIsGuestAutoresizeEnabled : 1;89 bool m_fShouldWeDoResize : 1;90 91 40 /* Friend classes: */ 92 friend class UI MachineView;41 friend class UIKeyboardHandler; 93 42 }; 94 43 95 #endif // !___UI MachineViewNormal_h___44 #endif // !___UIKeyboardHandlerNormal_h___ 96 45 -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineViewNormal.cpp
r30544 r30637 78 78 saveMachineViewSettings(); 79 79 80 /* Cleanup common things: */81 cleanupCommon();82 83 80 /* Cleanup frame buffer: */ 84 81 cleanupFrameBuffer(); … … 205 202 return true; 206 203 } 207 208 #ifndef Q_WS_MAC209 /* We don't want this on the Mac, cause there the menu bar isn't within the210 * window and popping up a menu there looks really ugly. */211 case QEvent::KeyPress:212 case QEvent::KeyRelease:213 {214 /* Get key-event: */215 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);216 217 /* Process Host+Home as menu-bar activator: */218 if (isHostKeyPressed() && pEvent->type() == QEvent::KeyPress)219 {220 if (pKeyEvent->key() == Qt::Key_Home)221 {222 /* Trying to get menu-bar: */223 QMenuBar *pMenuBar = machineWindowWrapper() && machineWindowWrapper()->machineWindow() ?224 qobject_cast<QMainWindow*>(machineWindowWrapper()->machineWindow())->menuBar() : 0;225 226 /* If menu-bar is present and have actions: */227 if (pMenuBar && !pMenuBar->actions().isEmpty())228 {229 /* If 'active' action is NOT chosen: */230 if (!pMenuBar->activeAction())231 /* Set first menu-bar action as 'active': */232 pMenuBar->setActiveAction(pMenuBar->actions()[0]);233 234 /* If 'active' action is chosen: */235 if (pMenuBar->activeAction())236 {237 /* Activate 'active' menu-bar action: */238 pMenuBar->activeAction()->activate(QAction::Trigger);239 240 #ifdef Q_WS_WIN241 /* Windows host needs separate 'focus set'242 * to let menubar operate while popped up: */243 pMenuBar->setFocus();244 #endif /* Q_WS_WIN */245 246 /* Accept this event: */247 pEvent->accept();248 return true;249 }250 }251 }252 else253 pEvent->ignore();254 }255 256 break;257 }258 #endif /* !Q_WS_MAC */259 204 260 205 default: -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/normal/UIMachineWindowNormal.cpp
r30546 r30637 32 32 #include "UIActionsPool.h" 33 33 #include "UIIndicatorsPool.h" 34 #include "UIKeyboardHandler.h" 34 35 #include "UIMouseHandler.h" 35 36 #include "UIMachineLogic.h" … … 494 495 { 495 496 /* Keyboard state-change updater: */ 496 connect(machine View(), SIGNAL(keyboardStateChanged(int)), indicatorsPool()->indicator(UIIndicatorIndex_Hostkey), SLOT(setState(int)));497 connect(machineLogic()->keyboardHandler(), SIGNAL(keyboardStateChanged(int)), indicatorsPool()->indicator(UIIndicatorIndex_Hostkey), SLOT(setState(int))); 497 498 498 499 /* Mouse state-change updater: */ … … 500 501 501 502 /* Early initialize required connections: */ 502 indicatorsPool()->indicator(UIIndicatorIndex_Hostkey)->setState(machine View()->keyboardState());503 indicatorsPool()->indicator(UIIndicatorIndex_Hostkey)->setState(machineLogic()->keyboardHandler()->keyboardState()); 503 504 indicatorsPool()->indicator(UIIndicatorIndex_Mouse)->setState(machineLogic()->mouseHandler()->mouseState()); 504 505 } -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIKeyboardHandlerSeamless.cpp
r30551 r30637 3 3 * 4 4 * VBox frontends: Qt GUI ("VirtualBox"): 5 * UI MachineViewSeamless class implementation5 * UIKeyboardHandlerSeamless class implementation 6 6 */ 7 7 … … 19 19 20 20 /* Global includes */ 21 #include <QApplication> 22 #include <QDesktopWidget> 23 #include <QMainWindow> 21 #include <QKeyEvent> 24 22 #include <QTimer> 25 #ifdef Q_WS_MAC 26 #include <QMenuBar> 27 #endif 28 #ifdef Q_WS_X11 29 #include <limits.h> 30 #endif 23 #include <QWidget> 31 24 32 25 /* Local includes */ 33 #include "VBoxGlobal.h" 34 #include "UISession.h" 26 #include "UIKeyboardHandlerSeamless.h" 35 27 #include "UIMachineWindow.h" 36 #include "UIMachineLogic.h"37 #include "UIFrameBuffer.h"38 #include "UIMachineLogicSeamless.h"39 #include "UIMachineViewSeamless.h"40 28 41 UIMachineViewSeamless::UIMachineViewSeamless( UIMachineWindow *pMachineWindow 42 , ulong uScreenId 43 #ifdef VBOX_WITH_VIDEOHWACCEL 44 , bool bAccelerate2DVideo 45 #endif 46 ) 47 : UIMachineView( pMachineWindow 48 , uScreenId 49 #ifdef VBOX_WITH_VIDEOHWACCEL 50 , bAccelerate2DVideo 51 #endif 52 ) 53 , m_fShouldWeDoResize(false) 54 , m_pSyncBlocker(0) 29 /* Fullscreen keyboard-handler constructor: */ 30 UIKeyboardHandlerSeamless::UIKeyboardHandlerSeamless(UIMachineLogic* pMachineLogic) 31 : UIKeyboardHandler(pMachineLogic) 55 32 { 56 /* Load machine view settings: */57 loadMachineViewSettings();58 59 /* Prepare frame buffer: */60 prepareFrameBuffer();61 62 /* Prepare common things: */63 prepareCommon();64 65 /* Prepare event-filters: */66 prepareFilters();67 68 /* Prepare connections: */69 prepareConnections();70 71 /* Prepare console connections: */72 prepareConsoleConnections();73 74 /* Prepare seamless view: */75 prepareSeamless();76 77 /* Initialization: */78 sltMachineStateChanged();79 sltAdditionsStateChanged();80 33 } 81 34 82 UIMachineViewSeamless::~UIMachineViewSeamless() 35 /* Fullscreen keyboard-handler destructor: */ 36 UIKeyboardHandlerSeamless::~UIKeyboardHandlerSeamless() 83 37 { 84 /* Cleanup seamless mode: */85 cleanupSeamless();86 87 /* Cleanup common things: */88 cleanupCommon();89 90 /* Cleanup frame buffer: */91 cleanupFrameBuffer();92 38 } 93 39 94 void UIMachineViewSeamless::sltPerformGuestResize(const QSize &toSize) 40 /* Event handler for prepared listener(s): */ 41 bool UIKeyboardHandlerSeamless::eventFilter(QObject *pWatchedObject, QEvent *pEvent) 95 42 { 96 if (uisession()->isGuestSupportsGraphics()) 43 /* Check if pWatchedObject object is view: */ 44 if (UIMachineView *pWatchedView = isItListenedView(pWatchedObject)) 97 45 { 98 /* Get machine window: */ 99 QMainWindow *pMachineWindow = machineWindowWrapper() && machineWindowWrapper()->machineWindow() ? 100 qobject_cast<QMainWindow*>(machineWindowWrapper()->machineWindow()) : 0; 101 102 /* If this slot is invoked directly then use the passed size otherwise get 103 * the available size for the guest display. We assume here that centralWidget() 104 * contains this view only and gives it all available space: */ 105 QSize newSize(toSize.isValid() ? toSize : pMachineWindow ? pMachineWindow->centralWidget()->size() : QSize()); 106 AssertMsg(newSize.isValid(), ("Size should be valid!\n")); 107 108 /* Do not send the same hints as we already have: */ 109 if ((newSize.width() == storedConsoleSize().width()) && (newSize.height() == storedConsoleSize().height())) 110 return; 111 112 /* We only actually send the hint if either an explicit new size was given 113 * (e.g. if the request was triggered directly by a console resize event) or 114 * if no explicit size was specified but a resize is flagged as being needed 115 * (e.g. the autoresize was just enabled and the console was resized while it was disabled). */ 116 if (toSize.isValid() || m_fShouldWeDoResize) 117 { 118 /* Remember the new size: */ 119 storeConsoleSize(newSize.width(), newSize.height()); 120 121 /* Send new size-hint to the guest: */ 122 session().GetConsole().GetDisplay().SetVideoModeHint(newSize.width(), newSize.height(), 0, screenId()); 123 } 124 125 /* We had requested resize now, rejecting other accident requests: */ 126 m_fShouldWeDoResize = false; 127 } 128 } 129 130 void UIMachineViewSeamless::sltAdditionsStateChanged() 131 { 132 // TODO: Exit seamless if additions doesn't support it! 133 } 134 135 void UIMachineViewSeamless::sltDesktopResized() 136 { 137 // TODO: Try to resize framebuffer according new desktop size, exit seamless if resize is failed! 138 139 /* If the desktop geometry is set automatically, this will update it: */ 140 calculateDesktopGeometry(); 141 } 142 143 bool UIMachineViewSeamless::event(QEvent *pEvent) 144 { 145 switch (pEvent->type()) 146 { 147 case VBoxDefs::SetRegionEventType: 148 { 149 /* Get region-update event: */ 150 UISetRegionEvent *pSetRegionEvent = static_cast<UISetRegionEvent*>(pEvent); 151 152 /* Apply new region: */ 153 if (pSetRegionEvent->region() != m_lastVisibleRegion) 154 { 155 m_lastVisibleRegion = pSetRegionEvent->region(); 156 machineWindowWrapper()->setMask(m_lastVisibleRegion); 157 } 158 return true; 159 } 160 161 case VBoxDefs::ResizeEventType: 162 { 163 /* Some situations require framebuffer resize events to be ignored at all, 164 * leaving machine-window, machine-view and framebuffer sizes preserved: */ 165 if (uisession()->isGuestResizeIgnored()) 166 return true; 167 168 /* We are starting to perform machine-view resize: */ 169 bool oldIgnoreMainwndResize = isMachineWindowResizeIgnored(); 170 setMachineWindowResizeIgnored(true); 171 172 /* Get guest resize-event: */ 173 UIResizeEvent *pResizeEvent = static_cast<UIResizeEvent*>(pEvent); 174 175 /* Perform framebuffer resize: */ 176 frameBuffer()->resizeEvent(pResizeEvent); 177 178 /* Reapply maximum size restriction for machine-view: */ 179 setMaximumSize(sizeHint()); 180 181 /* Store the new size to prevent unwanted resize hints being sent back: */ 182 storeConsoleSize(pResizeEvent->width(), pResizeEvent->height()); 183 184 /* Perform machine-view resize: */ 185 resize(pResizeEvent->width(), pResizeEvent->height()); 186 187 /* Let our toplevel widget calculate its sizeHint properly. */ 188 #ifdef Q_WS_X11 189 /* We use processEvents rather than sendPostedEvents & set the time out value to max cause on X11 otherwise 190 * the layout isn't calculated correctly. Dosn't find the bug in Qt, but this could be triggered through 191 * the async nature of the X11 window event system. */ 192 QCoreApplication::processEvents(QEventLoop::AllEvents, INT_MAX); 193 #else /* Q_WS_X11 */ 194 QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest); 195 #endif /* Q_WS_X11 */ 196 197 #ifdef Q_WS_MAC 198 machineLogic()->updateDockIconSize(screenId(), pResizeEvent->width(), pResizeEvent->height()); 199 #endif /* Q_WS_MAC */ 200 201 /* Update machine-view sliders: */ 202 updateSliders(); 203 204 /* Report to the VM thread that we finished resizing */ 205 session().GetConsole().GetDisplay().ResizeCompleted(screenId()); 206 207 /* We are finishing to perform machine-view resize: */ 208 setMachineWindowResizeIgnored(oldIgnoreMainwndResize); 209 210 /* Make sure that all posted signals are processed: */ 211 qApp->processEvents(); 212 213 /* We also recalculate the desktop geometry if this is determined 214 * automatically. In fact, we only need this on the first resize, 215 * but it is done every time to keep the code simpler. */ 216 calculateDesktopGeometry(); 217 218 /* Emit a signal about guest was resized: */ 219 emit resizeHintDone(); 220 221 /* Unlock after processing guest resize event: */ 222 if (m_pSyncBlocker && m_pSyncBlocker->isRunning()) 223 m_pSyncBlocker->quit(); 224 225 return true; 226 } 227 228 case QEvent::KeyPress: 229 case QEvent::KeyRelease: 230 { 231 /* Get key-event: */ 232 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent); 233 234 /* Process Host+Home for menu popup: */ 235 if (isHostKeyPressed() && pEvent->type() == QEvent::KeyPress) 236 { 237 if (pKeyEvent->key() == Qt::Key_Home) 238 QTimer::singleShot(0, machineWindowWrapper()->machineWindow(), SLOT(sltPopupMainMenu())); 239 else 240 pEvent->ignore(); 241 } 242 243 break; 244 } 245 246 default: 247 break; 248 } 249 return UIMachineView::event(pEvent); 250 } 251 252 bool UIMachineViewSeamless::eventFilter(QObject *pWatched, QEvent *pEvent) 253 { 254 /* Who are we watching? */ 255 QMainWindow *pMainDialog = machineWindowWrapper() && machineWindowWrapper()->machineWindow() ? 256 qobject_cast<QMainWindow*>(machineWindowWrapper()->machineWindow()) : 0; 257 258 if (pWatched != 0 && pWatched == pMainDialog) 259 { 46 /* Get corresponding screen index: */ 47 ulong uScreenId = m_views.key(pWatchedView); 48 NOREF(uScreenId); 49 /* Handle view events: */ 260 50 switch (pEvent->type()) 261 51 { 262 case QEvent:: Resize:52 case QEvent::KeyPress: 263 53 { 264 /* Send guest-resize hint only if top window resizing to required dimension: */ 265 QResizeEvent *pResizeEvent = static_cast<QResizeEvent*>(pEvent); 266 if (pResizeEvent->size() != workingArea().size()) 267 break; 268 269 /* Set the "guest needs to resize" hint. 270 * This hint is acted upon when (and only when) the autoresize property is "true": */ 271 m_fShouldWeDoResize = uisession()->isGuestSupportsGraphics(); 272 if (m_fShouldWeDoResize) 273 QTimer::singleShot(0, this, SLOT(sltPerformGuestResize())); 54 /* Get key-event: */ 55 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent); 56 /* Process Host+Home for menu popup: */ 57 if (isHostKeyPressed() && pEvent->type() == QEvent::KeyPress) 58 { 59 if (pKeyEvent->key() == Qt::Key_Home) 60 { 61 /* Post request to show popup-menu: */ 62 QTimer::singleShot(0, m_windows[uScreenId]->machineWindow(), SLOT(sltPopupMainMenu())); 63 /* Filter-out this event: */ 64 return true; 65 } 66 } 274 67 break; 275 68 } … … 279 72 } 280 73 281 return UIMachineView::eventFilter(pWatched, pEvent); 74 /* Else just propagate to base-class: */ 75 return UIKeyboardHandler::eventFilter(pWatchedObject, pEvent); 282 76 } 283 77 284 void UIMachineViewSeamless::prepareCommon()285 {286 /* Base class common settings: */287 UIMachineView::prepareCommon();288 289 /* Setup size-policy: */290 setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum));291 /* Maximum size to sizehint: */292 setMaximumSize(sizeHint());293 /* Minimum size is ignored: */294 setMinimumSize(0, 0);295 /* No scrollbars: */296 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);297 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);298 }299 300 void UIMachineViewSeamless::prepareFilters()301 {302 /* Base class filters: */303 UIMachineView::prepareFilters();304 305 #ifdef Q_WS_MAC // TODO: Is it really needed? See UIMachineViewSeamless::eventFilter(...);306 /* Menu bar filter: */307 qobject_cast<QMainWindow*>(machineWindowWrapper()->machineWindow())->menuBar()->installEventFilter(this);308 #endif309 }310 311 void UIMachineViewSeamless::prepareConnections()312 {313 connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(sltDesktopResized()));314 }315 316 void UIMachineViewSeamless::prepareConsoleConnections()317 {318 /* Base class connections: */319 UIMachineView::prepareConsoleConnections();320 321 /* Guest additions state-change updater: */322 connect(uisession(), SIGNAL(sigAdditionsStateChange()), this, SLOT(sltAdditionsStateChanged()));323 }324 325 void UIMachineViewSeamless::prepareSeamless()326 {327 /* Set seamless feature flag to the guest: */328 session().GetConsole().GetDisplay().SetSeamlessMode(true);329 /* Create sync-blocker: */330 m_pSyncBlocker = new UIMachineViewBlocker;331 }332 333 void UIMachineViewSeamless::cleanupSeamless()334 {335 /* If machine still running: */336 if (uisession()->isRunning())337 {338 /* Reset seamless feature flag of the guest: */339 session().GetConsole().GetDisplay().SetSeamlessMode(false);340 341 /* Rollback seamless frame-buffer size to normal: */342 machineWindowWrapper()->machineWindow()->hide();343 sltPerformGuestResize(guestSizeHint());344 m_pSyncBlocker->exec();345 346 /* Delete sync-blocker: */347 m_pSyncBlocker->deleteLater();348 }349 }350 351 QRect UIMachineViewSeamless::workingArea()352 {353 /* Get corresponding screen: */354 int iScreen = static_cast<UIMachineLogicSeamless*>(machineLogic())->hostScreenForGuestScreen(screenId());355 /* Return available geometry for that screen: */356 return vboxGlobal().availableGeometry(iScreen);357 }358 359 void UIMachineViewSeamless::calculateDesktopGeometry()360 {361 /* This method should not get called until we have initially set up the desktop geometry type: */362 Assert((desktopGeometryType() != DesktopGeo_Invalid));363 /* If we are not doing automatic geometry calculation then there is nothing to do: */364 if (desktopGeometryType() == DesktopGeo_Automatic)365 m_desktopGeometry = workingArea().size();366 }367 -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIKeyboardHandlerSeamless.h
r30551 r30637 2 2 * 3 3 * VBox frontends: Qt GUI ("VirtualBox"): 4 * UI MachineViewSeamless class declaration4 * UIKeyboardHandlerSeamless class declaration 5 5 */ 6 6 … … 17 17 */ 18 18 19 #ifndef ___UI MachineViewSeamless_h___20 #define ___UI MachineViewSeamless_h___19 #ifndef ___UIKeyboardHandlerSeamless_h___ 20 #define ___UIKeyboardHandlerSeamless_h___ 21 21 22 22 /* Local includes */ 23 #include "UI MachineView.h"23 #include "UIKeyboardHandler.h" 24 24 25 class UI MachineViewSeamless : public UIMachineView25 class UIKeyboardHandlerSeamless : public UIKeyboardHandler 26 26 { 27 27 Q_OBJECT; 28 28 29 public:30 31 /* Public getters: */32 QRegion lastVisibleRegion() const { return m_lastVisibleRegion; }33 34 29 protected: 35 30 36 /* Seamless machine-view constructor: */ 37 UIMachineViewSeamless( UIMachineWindow *pMachineWindow 38 , ulong uScreenId 39 #ifdef VBOX_WITH_VIDEOHWACCEL 40 , bool bAccelerate2DVideo 41 #endif 42 ); 43 /* Seamless machine-view destructor: */ 44 virtual ~UIMachineViewSeamless(); 45 46 private slots: 47 48 /* Slot to perform guest resize: */ 49 void sltPerformGuestResize(const QSize &aSize = QSize()); 50 51 /* Console callback handlers: */ 52 void sltAdditionsStateChanged(); 53 54 /* Watch dog for desktop resizes: */ 55 void sltDesktopResized(); 31 /* Fullscreen keyboard-handler constructor/destructor: */ 32 UIKeyboardHandlerSeamless(UIMachineLogic *pMachineLogic); 33 virtual ~UIKeyboardHandlerSeamless(); 56 34 57 35 private: 58 36 59 37 /* Event handlers: */ 60 bool event(QEvent *pEvent);61 38 bool eventFilter(QObject *pWatched, QEvent *pEvent); 62 39 63 /* Prepare helpers: */64 void prepareCommon();65 void prepareFilters();66 void prepareConnections();67 void prepareConsoleConnections();68 void prepareSeamless();69 70 /* Cleanup helpers: */71 void cleanupSeamless();72 //void cleanupConsoleConnections() {}73 //void prepareConnections() {}74 //void cleanupFilters() {}75 //void cleanupCommon() {}76 77 /* Private helpers: */78 void normalizeGeometry(bool /* fAdjustPosition */) {}79 QRect workingArea();80 void calculateDesktopGeometry();81 void maybeRestrictMinimumSize() {}82 83 /* Private variables: */84 bool m_fShouldWeDoResize : 1;85 QRegion m_lastVisibleRegion;86 UIMachineViewBlocker *m_pSyncBlocker;87 88 40 /* Friend classes: */ 89 friend class UI MachineView;41 friend class UIKeyboardHandler; 90 42 }; 91 43 92 #endif // !___UI MachineViewSeamless_h___44 #endif // !___UIKeyboardHandlerSeamless_h___ 93 45 -
trunk/src/VBox/Frontends/VirtualBox/src/runtime/seamless/UIMachineViewSeamless.cpp
r30544 r30637 85 85 cleanupSeamless(); 86 86 87 /* Cleanup common things: */88 cleanupCommon();89 90 87 /* Cleanup frame buffer: */ 91 88 cleanupFrameBuffer(); … … 224 221 225 222 return true; 226 }227 228 case QEvent::KeyPress:229 case QEvent::KeyRelease:230 {231 /* Get key-event: */232 QKeyEvent *pKeyEvent = static_cast<QKeyEvent*>(pEvent);233 234 /* Process Host+Home for menu popup: */235 if (isHostKeyPressed() && pEvent->type() == QEvent::KeyPress)236 {237 if (pKeyEvent->key() == Qt::Key_Home)238 QTimer::singleShot(0, machineWindowWrapper()->machineWindow(), SLOT(sltPopupMainMenu()));239 else240 pEvent->ignore();241 }242 243 break;244 223 } 245 224
Note:
See TracChangeset
for help on using the changeset viewer.