VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIMachineView.cpp@ 35740

Last change on this file since 35740 was 35247, checked in by vboxsync, 13 years ago

FE/Qt: 3635: Scale mode: Keep aspect-ratio during manual window resize (win-host only).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.7 KB
RevLine 
[26709]1/* $Id: UIMachineView.cpp 35247 2010-12-20 14:19:48Z vboxsync $ */
[382]2/** @file
3 *
4 * VBox frontends: Qt GUI ("VirtualBox"):
[26637]5 * UIMachineView class implementation
[382]6 */
7
8/*
[28800]9 * Copyright (C) 2010 Oracle Corporation
[382]10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
[5999]14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
[382]18 */
19
[26637]20/* Global includes */
21#include <QDesktopWidget>
22#include <QTimer>
23#include <QPainter>
24#include <QScrollBar>
[19817]25#include <VBox/VBoxVideo.h>
26
[26637]27/* Local includes */
[382]28#include "VBoxGlobal.h"
29#include "VBoxProblemReporter.h"
[26637]30#include "UIFrameBuffer.h"
[26797]31#include "UIFrameBufferQGL.h"
[26823]32#include "UIFrameBufferQImage.h"
[26781]33#include "UIFrameBufferQuartz2D.h"
[26822]34#include "UIFrameBufferSDL.h"
[27682]35#include "VBoxFBOverlay.h"
[26691]36#include "UISession.h"
[26637]37#include "UIActionsPool.h"
[30637]38#include "UIKeyboardHandler.h"
[30408]39#include "UIMouseHandler.h"
[26637]40#include "UIMachineLogic.h"
41#include "UIMachineWindow.h"
42#include "UIMachineViewNormal.h"
[26889]43#include "UIMachineViewFullscreen.h"
[27012]44#include "UIMachineViewSeamless.h"
[30753]45#include "UIMachineViewScale.h"
[382]46
47#ifdef Q_WS_X11
[26637]48# include <QX11Info>
49# include <X11/XKBlib.h>
50# ifdef KeyPress
[382]51const int XFocusOut = FocusOut;
52const int XFocusIn = FocusIn;
53const int XKeyPress = KeyPress;
54const int XKeyRelease = KeyRelease;
[26637]55# undef KeyRelease
56# undef KeyPress
57# undef FocusOut
58# undef FocusIn
59# endif
[30549]60#endif /* Q_WS_X11 */
[382]61
[26998]62#ifdef Q_WS_MAC
[24377]63# include "DockIconPreview.h"
[1285]64# include "DarwinKeyboard.h"
[30114]65# include "UICocoaApplication.h"
[1391]66# include <VBox/err.h>
[29816]67# include <Carbon/Carbon.h>
[27215]68#endif /* Q_WS_MAC */
[1285]69
[30551]70class UIViewport: public QWidget
[26637]71{
72public:
[382]73
[30551]74 UIViewport(QWidget *pParent) : QWidget(pParent)
[26637]75 {
[26773]76 /* No need for background drawing: */
[26637]77 setAttribute(Qt::WA_OpaquePaintEvent);
78 }
79
80 QPaintEngine *paintEngine() const
81 {
82 if (testAttribute(Qt::WA_PaintOnScreen))
83 return NULL;
84 else
85 return QWidget::paintEngine();
86 }
87};
88
89UIMachineView* UIMachineView::create( UIMachineWindow *pMachineWindow
[30544]90 , ulong uScreenId
91 , UIVisualStateType visualStateType
[26637]92#ifdef VBOX_WITH_VIDEOHWACCEL
93 , bool bAccelerate2DVideo
[30549]94#endif /* VBOX_WITH_VIDEOHWACCEL */
[30544]95 )
[7590]96{
[30551]97 UIMachineView *pMachineView = 0;
[26637]98 switch (visualStateType)
[7590]99 {
[26637]100 case UIVisualStateType_Normal:
[30551]101 pMachineView = new UIMachineViewNormal( pMachineWindow
102 , uScreenId
[26637]103#ifdef VBOX_WITH_VIDEOHWACCEL
[30551]104 , bAccelerate2DVideo
[30549]105#endif /* VBOX_WITH_VIDEOHWACCEL */
[30551]106 );
[26637]107 break;
108 case UIVisualStateType_Fullscreen:
[30551]109 pMachineView = new UIMachineViewFullscreen( pMachineWindow
110 , uScreenId
[26637]111#ifdef VBOX_WITH_VIDEOHWACCEL
[30551]112 , bAccelerate2DVideo
[30549]113#endif /* VBOX_WITH_VIDEOHWACCEL */
[30551]114 );
[26637]115 break;
116 case UIVisualStateType_Seamless:
[30551]117 pMachineView = new UIMachineViewSeamless( pMachineWindow
118 , uScreenId
[26637]119#ifdef VBOX_WITH_VIDEOHWACCEL
[30551]120 , bAccelerate2DVideo
[30549]121#endif /* VBOX_WITH_VIDEOHWACCEL */
[30551]122 );
[26637]123 break;
[30753]124 case UIVisualStateType_Scale:
125 pMachineView = new UIMachineViewScale( pMachineWindow
126 , uScreenId
127#ifdef VBOX_WITH_VIDEOHWACCEL
128 , bAccelerate2DVideo
129#endif
130 );
131 break;
[26637]132 default:
133 break;
[7590]134 }
[30551]135 return pMachineView;
[26637]136}
137
[30544]138void UIMachineView::destroy(UIMachineView *pMachineView)
[26878]139{
[30544]140 delete pMachineView;
[26878]141}
142
[35247]143double UIMachineView::aspectRatio() const
144{
145 return frameBuffer() ? (double)(frameBuffer()->width()) / frameBuffer()->height() : 0;
146}
147
[30549]148void UIMachineView::sltMachineStateChanged()
149{
150 /* Get machine state: */
151 KMachineState state = uisession()->machineState();
152 switch (state)
153 {
154 case KMachineState_Paused:
155 case KMachineState_TeleportingPausedVM:
156 {
[31341]157 if ( vboxGlobal().vmRenderMode() != VBoxDefs::TimerMode
158 && m_pFrameBuffer
159 &&
160 ( state != KMachineState_TeleportingPausedVM
161 || m_previousState != KMachineState_Teleporting))
[30549]162 {
[31341]163 takePauseShotLive();
[30549]164 /* Fully repaint to pick up m_pauseShot: */
[30673]165 viewport()->update();
[30549]166 }
167 break;
168 }
[31341]169 case KMachineState_Restoring:
170 {
171 /* Only works with the primary screen currently. */
172 if (screenId() == 0)
173 {
174 takePauseShotSnapshot();
175 /* Fully repaint to pick up m_pauseShot: */
176 viewport()->update();
177 }
178 break;
179 }
[30549]180 case KMachineState_Running:
181 {
182 if ( m_previousState == KMachineState_Paused
183 || m_previousState == KMachineState_TeleportingPausedVM
184 || m_previousState == KMachineState_Restoring)
185 {
186 if (vboxGlobal().vmRenderMode() != VBoxDefs::TimerMode && m_pFrameBuffer)
187 {
188 /* Reset the pixmap to free memory: */
[31341]189 resetPauseShot();
[30549]190 /* Ask for full guest display update (it will also update
191 * the viewport through IFramebuffer::NotifyUpdate): */
192 CDisplay dsp = session().GetConsole().GetDisplay();
193 dsp.InvalidateAndUpdate();
194 }
195 }
196 break;
197 }
198 default:
199 break;
200 }
[31341]201
[30549]202 m_previousState = state;
203}
204
[26637]205UIMachineView::UIMachineView( UIMachineWindow *pMachineWindow
[30544]206 , ulong uScreenId
[22816]207#ifdef VBOX_WITH_VIDEOHWACCEL
[26637]208 , bool bAccelerate2DVideo
[30549]209#endif /* VBOX_WITH_VIDEOHWACCEL */
[30544]210 )
[26637]211 : QAbstractScrollArea(pMachineWindow->machineWindow())
212 , m_pMachineWindow(pMachineWindow)
[26921]213 , m_uScreenId(uScreenId)
[26754]214 , m_pFrameBuffer(0)
[26816]215 , m_previousState(KMachineState_Null)
[27012]216 , m_desktopGeometryType(DesktopGeo_Invalid)
[26878]217 , m_bIsMachineWindowResizeIgnored(false)
[22816]218#ifdef VBOX_WITH_VIDEOHWACCEL
[26754]219 , m_fAccelerate2DVideo(bAccelerate2DVideo)
[27215]220#endif /* VBOX_WITH_VIDEOHWACCEL */
[382]221{
[26730]222}
[382]223
[26730]224UIMachineView::~UIMachineView()
225{
226}
[14397]227
[30752]228void UIMachineView::prepareViewport()
[26730]229{
[26858]230 /* Prepare viewport: */
[27682]231#ifdef VBOX_GUI_USE_QGLFB
[30551]232 QWidget *pViewport = 0;
[30347]233 switch (vboxGlobal().vmRenderMode())
[26858]234 {
235 case VBoxDefs::QGLMode:
[26862]236 pViewport = new VBoxGLWidget(session().GetConsole(), this, NULL);
[26858]237 break;
238 default:
[30551]239 pViewport = new UIViewport(this);
[26858]240 }
[30549]241#else /* VBOX_GUI_USE_QGLFB */
[30551]242 UIViewport *pViewport = new UIViewport(this);
[30549]243#endif /* !VBOX_GUI_USE_QGLFB */
[26858]244 setViewport(pViewport);
[30752]245}
[26858]246
[30752]247void UIMachineView::prepareFrameBuffer()
248{
249 /* Prepare frame-buffer depending on render-mode: */
[30347]250 switch (vboxGlobal().vmRenderMode())
[382]251 {
[26822]252#ifdef VBOX_GUI_USE_QIMAGE
[382]253 case VBoxDefs::QImageMode:
[27682]254# ifdef VBOX_WITH_VIDEOHWACCEL
[27697]255 if (m_fAccelerate2DVideo)
256 {
[27768]257 UIFrameBuffer* pFramebuffer = uisession()->frameBuffer(screenId());
[27757]258 if (pFramebuffer)
259 pFramebuffer->setView(this);
260 else
261 {
262 /* these two additional template args is a workaround to this [VBox|UI] duplication
263 * @todo: they are to be removed once VBox stuff is gone */
[27839]264 pFramebuffer = new VBoxOverlayFrameBuffer<UIFrameBufferQImage, UIMachineView, UIResizeEvent>(this, &machineWindowWrapper()->session(), (uint32_t)screenId());
[27768]265 uisession()->setFrameBuffer(screenId(), pFramebuffer);
[27757]266 }
267 m_pFrameBuffer = pFramebuffer;
[27697]268 }
269 else
270 m_pFrameBuffer = new UIFrameBufferQImage(this);
[30549]271# else /* VBOX_WITH_VIDEOHWACCEL */
[26754]272 m_pFrameBuffer = new UIFrameBufferQImage(this);
[30549]273# endif /* !VBOX_WITH_VIDEOHWACCEL */
[382]274 break;
[26822]275#endif /* VBOX_GUI_USE_QIMAGE */
[27682]276#ifdef VBOX_GUI_USE_QGLFB
[26730]277 case VBoxDefs::QGLMode:
[26797]278 m_pFrameBuffer = new UIFrameBufferQGL(this);
[26730]279 break;
[26797]280// case VBoxDefs::QGLOverlayMode:
281// m_pFrameBuffer = new UIQGLOverlayFrameBuffer(this);
282// break;
[27682]283#endif /* VBOX_GUI_USE_QGLFB */
[26822]284#ifdef VBOX_GUI_USE_SDL
[382]285 case VBoxDefs::SDLMode:
[26730]286 /* Indicate that we are doing all drawing stuff ourself: */
[26822]287 // TODO_NEW_CORE
288 viewport()->setAttribute(Qt::WA_PaintOnScreen);
[6059]289# ifdef Q_WS_X11
[26730]290 /* This is somehow necessary to prevent strange X11 warnings on i386 and segfaults on x86_64: */
[7407]291 XFlush(QX11Info::display());
[30549]292# endif /* Q_WS_X11 */
[27682]293# if defined(VBOX_WITH_VIDEOHWACCEL) && defined(DEBUG_misha) /* not tested yet */
[27758]294 if (m_fAccelerate2DVideo)
295 {
[27769]296 class UIFrameBuffer* pFramebuffer = uisession()->frameBuffer(screenId());
[27758]297 if (pFramebuffer)
298 pFramebuffer->setView(this);
299 else
300 {
301 /* these two additional template args is a workaround to this [VBox|UI] duplication
302 * @todo: they are to be removed once VBox stuff is gone */
[27839]303 pFramebuffer = new VBoxOverlayFrameBuffer<UIFrameBufferSDL, UIMachineView, UIResizeEvent>(this, &machineWindowWrapper()->session(), (uint32_t)screenId());
[27769]304 uisession()->setFrameBuffer(screenId(), pFramebuffer);
[27758]305 }
306 m_pFrameBuffer = pFramebuffer;
307 }
308 else
309 m_pFrameBuffer = new UIFrameBufferSDL(this);
[27682]310# else
[26822]311 m_pFrameBuffer = new UIFrameBufferSDL(this);
[27682]312# endif
[26730]313 /* Disable scrollbars because we cannot correctly draw in a scrolled window using SDL: */
[26637]314 horizontalScrollBar()->setEnabled(false);
315 verticalScrollBar()->setEnabled(false);
[382]316 break;
[26822]317#endif /* VBOX_GUI_USE_SDL */
[26730]318#if 0 // TODO: Enable DDraw frame buffer!
[26822]319#ifdef VBOX_GUI_USE_DDRAW
[382]320 case VBoxDefs::DDRAWMode:
[26754]321 m_pFrameBuffer = new UIDDRAWFrameBuffer(this);
322 if (!m_pFrameBuffer || m_pFrameBuffer->address() == NULL)
[26730]323 {
[26754]324 if (m_pFrameBuffer)
325 delete m_pFrameBuffer;
326 m_mode = VBoxDefs::QImageMode;
327 m_pFrameBuffer = new UIFrameBufferQImage(this);
[26730]328 }
[382]329 break;
[26822]330#endif /* VBOX_GUI_USE_DDRAW */
[382]331#endif
[26822]332#ifdef VBOX_GUI_USE_QUARTZ2D
[6645]333 case VBoxDefs::Quartz2DMode:
[26730]334 /* Indicate that we are doing all drawing stuff ourself: */
[26859]335 viewport()->setAttribute(Qt::WA_PaintOnScreen);
[27682]336# ifdef VBOX_WITH_VIDEOHWACCEL
[27758]337 if (m_fAccelerate2DVideo)
338 {
[27769]339 UIFrameBuffer* pFramebuffer = uisession()->frameBuffer(screenId());
[27758]340 if (pFramebuffer)
341 pFramebuffer->setView(this);
342 else
343 {
344 /* these two additional template args is a workaround to this [VBox|UI] duplication
345 * @todo: they are to be removed once VBox stuff is gone */
[27839]346 pFramebuffer = new VBoxOverlayFrameBuffer<UIFrameBufferQuartz2D, UIMachineView, UIResizeEvent>(this, &machineWindowWrapper()->session(), (uint32_t)screenId());
[27769]347 uisession()->setFrameBuffer(screenId(), pFramebuffer);
[27758]348 }
349 m_pFrameBuffer = pFramebuffer;
350 }
351 else
352 m_pFrameBuffer = new UIFrameBufferQuartz2D(this);
[30549]353# else /* VBOX_WITH_VIDEOHWACCEL */
[26754]354 m_pFrameBuffer = new UIFrameBufferQuartz2D(this);
[30549]355# endif /* !VBOX_WITH_VIDEOHWACCEL */
[6645]356 break;
[26822]357#endif /* VBOX_GUI_USE_QUARTZ2D */
[382]358 default:
[30347]359 AssertReleaseMsgFailed(("Render mode must be valid: %d\n", vboxGlobal().vmRenderMode()));
360 LogRel(("Invalid render mode: %d\n", vboxGlobal().vmRenderMode()));
[26730]361 qApp->exit(1);
[382]362 break;
363 }
[30752]364
365 /* If frame-buffer was prepared: */
[26754]366 if (m_pFrameBuffer)
[382]367 {
[30752]368 /* Prepare display: */
369 CDisplay display = session().GetConsole().GetDisplay();
370 Assert(!display.isNull());
[27760]371#ifdef VBOX_WITH_VIDEOHWACCEL
372 CFramebuffer fb(NULL);
373 if (m_fAccelerate2DVideo)
374 {
375 LONG XOrigin, YOrigin;
[30752]376 /* Check if the framebuffer is already assigned;
377 * in this case we do not need to re-assign it neither do we need to AddRef. */
[27760]378 display.GetFramebuffer(m_uScreenId, fb, XOrigin, YOrigin);
379 }
380 if (fb.raw() != m_pFrameBuffer) /* <-this will evaluate to true iff m_fAccelerate2DVideo is disabled or iff no framebuffer is yet assigned */
[30549]381#endif /* VBOX_WITH_VIDEOHWACCEL */
[27760]382 {
383 m_pFrameBuffer->AddRef();
384 }
[30752]385 /* Always perform SetFramebuffer to ensure 3D gets notified: */
[28291]386 display.SetFramebuffer(m_uScreenId, CFramebuffer(m_pFrameBuffer));
[382]387 }
[29558]388
[31341]389 QSize size;
[29558]390#ifdef Q_WS_X11
[31341]391 /* Processing pseudo resize-event to synchronize frame-buffer with stored
392 * framebuffer size. On X11 this will be additional done when the machine
393 * state was 'saved'. */
394 if (session().GetMachine().GetState() == KMachineState_Saved)
[33579]395 size = guestSizeHint();
[31341]396#endif /* Q_WS_X11 */
397 /* If there is a preview image saved, we will resize the framebuffer to the
398 * size of that image. */
399 ULONG buffer = 0, width = 0, height = 0;
400 CMachine machine = session().GetMachine();
401 machine.QuerySavedScreenshotPNGSize(0, buffer, width, height);
402 if (buffer > 0)
[32407]403 {
404 /* Init with the screenshot size */
405 size = QSize(width, height);
406 /* Try to get the real guest dimensions from the save state */
407 ULONG guestWidth = 0, guestHeight = 0;
408 machine.QuerySavedGuestSize(0, guestWidth, guestHeight);
409 if ( guestWidth > 0
410 && guestHeight > 0)
411 size = QSize(guestWidth, guestHeight);
412 }
[31341]413 /* If we have a valid size, resize the framebuffer. */
414 if ( size.width() > 0
415 && size.height() > 0)
416 {
417 UIResizeEvent event(FramebufferPixelFormat_Opaque, NULL, 0, 0, size.width(), size.height());
418 frameBuffer()->resizeEvent(&event);
419 }
[26730]420}
[382]421
[26730]422void UIMachineView::prepareCommon()
423{
424 /* Prepare view frame: */
[26889]425 setFrameStyle(QFrame::NoFrame);
[26730]426
427 /* Setup palette: */
[26637]428 QPalette palette(viewport()->palette());
429 palette.setColor(viewport()->backgroundRole(), Qt::black);
430 viewport()->setPalette(palette);
[382]431
[26730]432 /* Setup focus policy: */
[26637]433 setFocusPolicy(Qt::WheelFocus);
[382]434}
435
[26730]436void UIMachineView::prepareFilters()
[382]437{
[26730]438 /* Enable MouseMove events: */
439 viewport()->setMouseTracking(true);
440
441 /* QScrollView does the below on its own, but let's
442 * do it anyway for the case it will not do it in the future: */
443 viewport()->installEventFilter(this);
444
445 /* We want to be notified on some parent's events: */
446 machineWindowWrapper()->machineWindow()->installEventFilter(this);
447}
448
[26754]449void UIMachineView::prepareConsoleConnections()
450{
[26773]451 /* Machine state-change updater: */
[26878]452 connect(uisession(), SIGNAL(sigMachineStateChange()), this, SLOT(sltMachineStateChanged()));
[26754]453}
454
[26730]455void UIMachineView::loadMachineViewSettings()
456{
457 /* Global settings: */
458 {
[26889]459 /* Remember the desktop geometry and register for geometry
460 * change events for telling the guest about video modes we like: */
461 QString desktopGeometry = vboxGlobal().settings().publicProperty("GUI/MaxGuestResolution");
462 if ((desktopGeometry == QString::null) || (desktopGeometry == "auto"))
463 setDesktopGeometry(DesktopGeo_Automatic, 0, 0);
464 else if (desktopGeometry == "any")
465 setDesktopGeometry(DesktopGeo_Any, 0, 0);
466 else
467 {
468 int width = desktopGeometry.section(',', 0, 0).toInt();
469 int height = desktopGeometry.section(',', 1, 1).toInt();
470 setDesktopGeometry(DesktopGeo_Fixed, width, height);
471 }
[26730]472 }
473}
474
475void UIMachineView::cleanupFrameBuffer()
[26637]476{
[26754]477 if (m_pFrameBuffer)
[26637]478 {
[27454]479 /* Process pending frame-buffer resize events: */
480 QApplication::sendPostedEvents(this, VBoxDefs::ResizeEventType);
[27757]481#ifdef VBOX_WITH_VIDEOHWACCEL
482 if (m_fAccelerate2DVideo)
483 {
484 /* When 2D is enabled we do not re-create Framebuffers. This is done to
485 * 1. avoid 2D command loss during the time slot when no framebuffer is assigned to the display
486 * 2. make it easier to preserve the current 2D state */
[27768]487 Assert(m_pFrameBuffer == uisession()->frameBuffer(screenId()));
[27757]488 m_pFrameBuffer->setView(NULL);
489 }
490 else
[30549]491#endif /* VBOX_WITH_VIDEOHWACCEL */
[27757]492 {
[27760]493 /* Warn framebuffer about its no more necessary: */
494 m_pFrameBuffer->setDeleted(true);
495 /* Detach framebuffer from Display: */
496 CDisplay display = session().GetConsole().GetDisplay();
497 display.SetFramebuffer(m_uScreenId, CFramebuffer(NULL));
498 /* Release the reference: */
499 m_pFrameBuffer->Release();
500// delete m_pFrameBuffer; // TODO_NEW_CORE: possibly necessary to really cleanup
501 m_pFrameBuffer = NULL;
[27757]502 }
[26637]503 }
504}
[382]505
[30549]506UIMachineLogic* UIMachineView::machineLogic() const
507{
508 return machineWindowWrapper()->machineLogic();
509}
510
511UISession* UIMachineView::uisession() const
512{
513 return machineLogic()->uisession();
514}
515
516CSession& UIMachineView::session()
517{
518 return uisession()->session();
519}
520
521QSize UIMachineView::sizeHint() const
522{
523#ifdef VBOX_WITH_DEBUGGER
524 // TODO: Fix all DEBUGGER stuff!
525 /* HACK ALERT! Really ugly workaround for the resizing to 9x1 done by DevVGA if provoked before power on. */
526 QSize fb(m_pFrameBuffer->width(), m_pFrameBuffer->height());
[33579]527 if (fb.width() < 16 || fb.height() < 16)
[32741]528 {
529 CMachine machine = uisession()->session().GetMachine();
[33579]530 if ( vboxGlobal().isStartPausedEnabled()
[32741]531 || vboxGlobal().isDebuggerAutoShowEnabled(machine))
[30549]532 fb = QSize(640, 480);
[32741]533 }
[30549]534 return QSize(fb.width() + frameWidth() * 2, fb.height() + frameWidth() * 2);
535#else /* VBOX_WITH_DEBUGGER */
536 return QSize(m_pFrameBuffer->width() + frameWidth() * 2, m_pFrameBuffer->height() + frameWidth() * 2);
537#endif /* !VBOX_WITH_DEBUGGER */
538}
539
540int UIMachineView::contentsX() const
541{
542 return horizontalScrollBar()->value();
543}
544
545int UIMachineView::contentsY() const
546{
547 return verticalScrollBar()->value();
548}
549
550int UIMachineView::contentsWidth() const
551{
552 return m_pFrameBuffer->width();
553}
554
555int UIMachineView::contentsHeight() const
556{
557 return m_pFrameBuffer->height();
558}
559
560int UIMachineView::visibleWidth() const
561{
562 return horizontalScrollBar()->pageStep();
563}
564
565int UIMachineView::visibleHeight() const
566{
567 return verticalScrollBar()->pageStep();
568}
569
570QSize UIMachineView::desktopGeometry() const
571{
572 QSize geometry;
573 switch (m_desktopGeometryType)
574 {
575 case DesktopGeo_Fixed:
576 case DesktopGeo_Automatic:
577 geometry = QSize(qMax(m_desktopGeometry.width(), m_storedConsoleSize.width()),
578 qMax(m_desktopGeometry.height(), m_storedConsoleSize.height()));
579 break;
580 case DesktopGeo_Any:
581 geometry = QSize(0, 0);
582 break;
583 default:
584 AssertMsgFailed(("Bad geometry type %d!\n", m_desktopGeometryType));
585 }
586 return geometry;
587}
588
589QSize UIMachineView::guestSizeHint()
590{
591 /* Result: */
592 QSize sizeHint;
593
594 /* Get current machine: */
595 CMachine machine = session().GetMachine();
596
597 /* Load machine view hint: */
598 QString strKey = m_uScreenId == 0 ? QString("%1").arg(VBoxDefs::GUI_LastGuestSizeHint) :
599 QString("%1%2").arg(VBoxDefs::GUI_LastGuestSizeHint).arg(m_uScreenId);
600 QString strValue = machine.GetExtraData(strKey);
601
602 bool ok = true;
603 int width = 0, height = 0;
604 if (ok)
605 width = strValue.section(',', 0, 0).toInt(&ok);
606 if (ok)
607 height = strValue.section(',', 1, 1).toInt(&ok);
608
609 if (ok /* If previous parameters were read correctly! */)
610 {
611 /* Compose guest size hint from loaded values: */
612 sizeHint = QSize(width, height);
613 }
614 else
615 {
616 /* Compose guest size hint from default attributes: */
617 sizeHint = QSize(800, 600);
618 }
619
620 /* Return result: */
621 return sizeHint;
622}
623
624void UIMachineView::setDesktopGeometry(DesktopGeo geometry, int aWidth, int aHeight)
625{
626 switch (geometry)
627 {
628 case DesktopGeo_Fixed:
629 m_desktopGeometryType = DesktopGeo_Fixed;
630 if (aWidth != 0 && aHeight != 0)
631 m_desktopGeometry = QSize(aWidth, aHeight);
632 else
633 m_desktopGeometry = QSize(0, 0);
634 storeConsoleSize(0, 0);
635 break;
636 case DesktopGeo_Automatic:
637 m_desktopGeometryType = DesktopGeo_Automatic;
638 m_desktopGeometry = QSize(0, 0);
639 storeConsoleSize(0, 0);
640 break;
641 case DesktopGeo_Any:
642 m_desktopGeometryType = DesktopGeo_Any;
643 m_desktopGeometry = QSize(0, 0);
644 break;
645 default:
646 AssertMsgFailed(("Invalid desktop geometry type %d\n", geometry));
647 m_desktopGeometryType = DesktopGeo_Invalid;
648 }
649}
650
651void UIMachineView::storeConsoleSize(int iWidth, int iHeight)
652{
653 m_storedConsoleSize = QSize(iWidth, iHeight);
654}
655
656void UIMachineView::storeGuestSizeHint(const QSize &sizeHint)
657{
658 /* Get current machine: */
659 CMachine machine = session().GetMachine();
660
661 /* Save machine view hint: */
662 QString strKey = m_uScreenId == 0 ? QString("%1").arg(VBoxDefs::GUI_LastGuestSizeHint) :
663 QString("%1%2").arg(VBoxDefs::GUI_LastGuestSizeHint).arg(m_uScreenId);
664 QString strValue = QString("%1,%2").arg(sizeHint.width()).arg(sizeHint.height());
665 machine.SetExtraData(strKey, strValue);
666}
667
[31341]668void UIMachineView::takePauseShotLive()
669{
670 /* Take a screen snapshot. Note that TakeScreenShot() always needs a 32bpp image: */
671 QImage shot = QImage(m_pFrameBuffer->width(), m_pFrameBuffer->height(), QImage::Format_RGB32);
672 /* If TakeScreenShot fails or returns no image, just show a black image. */
673 shot.fill(0);
674 CDisplay dsp = session().GetConsole().GetDisplay();
675 dsp.TakeScreenShot(screenId(), shot.bits(), shot.width(), shot.height());
676 /* TakeScreenShot() may fail if, e.g. the Paused notification was delivered
677 * after the machine execution was resumed. It's not fatal: */
678 if (dsp.isOk())
679 dimImage(shot);
680 m_pauseShot = QPixmap::fromImage(shot);
681}
682
683void UIMachineView::takePauseShotSnapshot()
684{
685 CMachine machine = session().GetMachine();
686 ULONG width = 0, height = 0;
687 QVector<BYTE> screenData = machine.ReadSavedScreenshotPNGToArray(0, width, height);
688 if (screenData.size() != 0)
689 {
[32407]690 ULONG guestWidth = 0, guestHeight = 0;
691 machine.QuerySavedGuestSize(0, guestWidth, guestHeight);
692 QImage shot = QImage::fromData(screenData.data(), screenData.size(), "PNG").scaled(guestWidth > 0 ? QSize(guestWidth, guestHeight) : guestSizeHint());
[31341]693 dimImage(shot);
694 m_pauseShot = QPixmap::fromImage(shot);
695 }
696}
697
[30549]698void UIMachineView::updateSliders()
699{
700 QSize p = viewport()->size();
701 QSize m = maximumViewportSize();
702
703 QSize v = QSize(frameBuffer()->width(), frameBuffer()->height());
704 /* No scroll bars needed: */
705 if (m.expandedTo(v) == m)
706 p = m;
707
708 horizontalScrollBar()->setRange(0, v.width() - p.width());
709 verticalScrollBar()->setRange(0, v.height() - p.height());
710 horizontalScrollBar()->setPageStep(p.width());
711 verticalScrollBar()->setPageStep(p.height());
712}
713
714QPoint UIMachineView::viewportToContents(const QPoint &vp) const
715{
716 return QPoint(vp.x() + contentsX(), vp.y() + contentsY());
717}
718
719void UIMachineView::scrollBy(int dx, int dy)
720{
721 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + dx);
722 verticalScrollBar()->setValue(verticalScrollBar()->value() + dy);
723}
724
725void UIMachineView::dimImage(QImage &img)
726{
727 for (int y = 0; y < img.height(); ++ y)
728 {
729 if (y % 2)
730 {
731 if (img.depth() == 32)
732 {
733 for (int x = 0; x < img.width(); ++ x)
734 {
735 int gray = qGray(img.pixel (x, y)) / 2;
736 img.setPixel(x, y, qRgb (gray, gray, gray));
737 }
738 }
739 else
740 {
741 ::memset(img.scanLine (y), 0, img.bytesPerLine());
742 }
743 }
744 else
745 {
746 if (img.depth() == 32)
747 {
748 for (int x = 0; x < img.width(); ++ x)
749 {
750 int gray = (2 * qGray (img.pixel (x, y))) / 3;
751 img.setPixel(x, y, qRgb (gray, gray, gray));
752 }
753 }
754 }
755 }
756}
757
758#ifdef VBOX_WITH_VIDEOHWACCEL
759void UIMachineView::scrollContentsBy(int dx, int dy)
760{
761 if (m_pFrameBuffer)
762 {
763 m_pFrameBuffer->viewportScrolled(dx, dy);
764 }
765 QAbstractScrollArea::scrollContentsBy(dx, dy);
766}
767#endif /* VBOX_WITH_VIDEOHWACCEL */
768
769#ifdef Q_WS_MAC
770void UIMachineView::updateDockIcon()
771{
772 machineLogic()->updateDockIcon();
773}
774
775CGImageRef UIMachineView::vmContentImage()
776{
777 if (!m_pauseShot.isNull())
778 {
779 CGImageRef pauseImg = ::darwinToCGImageRef(&m_pauseShot);
780 /* Use the pause image as background */
781 return pauseImg;
782 }
783 else
784 {
785# ifdef VBOX_GUI_USE_QUARTZ2D
786 if (vboxGlobal().vmRenderMode() == VBoxDefs::Quartz2DMode)
787 {
788 /* If the render mode is Quartz2D we could use the CGImageRef
789 * of the framebuffer for the dock icon creation. This saves
790 * some conversion time. */
791 CGImageRef image = static_cast<UIFrameBufferQuartz2D*>(m_pFrameBuffer)->imageRef();
792 CGImageRetain(image); /* Retain it, cause the consumer will release it. */
793 return image;
794 }
795 else
796# endif /* VBOX_GUI_USE_QUARTZ2D */
797 /* In image mode we have to create the image ref out of the
798 * framebuffer */
799 return frameBuffertoCGImageRef(m_pFrameBuffer);
800 }
801 return 0;
802}
803
804CGImageRef UIMachineView::frameBuffertoCGImageRef(UIFrameBuffer *pFrameBuffer)
805{
806 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
807 Assert(cs);
808 /* Create the image copy of the framebuffer */
809 CGDataProviderRef dp = CGDataProviderCreateWithData(pFrameBuffer, pFrameBuffer->address(), pFrameBuffer->bitsPerPixel() / 8 * pFrameBuffer->width() * pFrameBuffer->height(), NULL);
810 Assert(dp);
811 CGImageRef ir = CGImageCreate(pFrameBuffer->width(), pFrameBuffer->height(), 8, 32, pFrameBuffer->bytesPerLine(), cs,
812 kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, dp, 0, false,
813 kCGRenderingIntentDefault);
814 Assert(ir);
815 CGDataProviderRelease(dp);
816 CGColorSpaceRelease(cs);
817
818 return ir;
819}
820#endif /* Q_WS_MAC */
821
[26754]822bool UIMachineView::event(QEvent *pEvent)
[2835]823{
[26754]824 switch (pEvent->type())
[382]825 {
[26754]826 case VBoxDefs::RepaintEventType:
827 {
828 UIRepaintEvent *pPaintEvent = static_cast<UIRepaintEvent*>(pEvent);
[31248]829 viewport()->update(pPaintEvent->x() - contentsX(), pPaintEvent->y() - contentsY(),
[26754]830 pPaintEvent->width(), pPaintEvent->height());
831 return true;
832 }
[25505]833
[2968]834#ifdef Q_WS_MAC
[30551]835 /* Event posted OnShowWindow: */
[26754]836 case VBoxDefs::ShowWindowEventType:
837 {
838 /* Dunno what Qt3 thinks a window that has minimized to the dock should be - it is not hidden,
839 * neither is it minimized. OTOH it is marked shown and visible, but not activated.
840 * This latter isn't of much help though, since at this point nothing is marked activated.
841 * I might have overlooked something, but I'm buggered what if I know what. So, I'll just always
842 * show & activate the stupid window to make it get out of the dock when the user wishes to show a VM: */
843 window()->show();
844 window()->activateWindow();
845 return true;
846 }
[30549]847#endif /* Q_WS_MAC */
[26754]848
[30551]849#ifdef VBOX_WITH_VIDEOHWACCEL
850 case VBoxDefs::VHWACommandProcessType:
851 {
852 m_pFrameBuffer->doProcessVHWACommand(pEvent);
853 return true;
854 }
855#endif /* VBOX_WITH_VIDEOHWACCEL */
856
[26754]857 default:
858 break;
[382]859 }
860
[26754]861 return QAbstractScrollArea::event(pEvent);
[382]862}
863
[26754]864bool UIMachineView::eventFilter(QObject *pWatched, QEvent *pEvent)
[22834]865{
[30408]866#ifdef VBOX_WITH_VIDEOHWACCEL
[26754]867 if (pWatched == viewport())
[22834]868 {
[26754]869 switch (pEvent->type())
[382]870 {
871 case QEvent::Resize:
872 {
[26754]873 if (m_pFrameBuffer)
874 m_pFrameBuffer->viewportResized(static_cast<QResizeEvent*>(pEvent));
[15642]875 break;
[382]876 }
877 default:
878 break;
879 }
880 }
[30408]881#endif /* VBOX_WITH_VIDEOHWACCEL */
882 if (pWatched == machineWindowWrapper()->machineWindow())
[382]883 {
[26754]884 switch (pEvent->type())
[382]885 {
[26754]886 case QEvent::WindowStateChange:
887 {
[26878]888 /* During minimizing and state restoring machineWindowWrapper() gets
889 * the focus which belongs to console view window, so returning it properly. */
[26754]890 QWindowStateChangeEvent *pWindowEvent = static_cast<QWindowStateChangeEvent*>(pEvent);
891 if (pWindowEvent->oldState() & Qt::WindowMinimized)
892 {
893 if (QApplication::focusWidget())
894 {
895 QApplication::focusWidget()->clearFocus();
896 qApp->processEvents();
897 }
898 QTimer::singleShot(0, this, SLOT(setFocus()));
899 }
900 break;
901 }
[382]902 default:
903 break;
904 }
905 }
[26878]906
[30551]907 return QAbstractScrollArea::eventFilter(pWatched, pEvent);
[26878]908}
909
[26754]910void UIMachineView::resizeEvent(QResizeEvent *pEvent)
[382]911{
[26637]912 updateSliders();
[26754]913 return QAbstractScrollArea::resizeEvent(pEvent);
[26637]914}
915
[26754]916void UIMachineView::moveEvent(QMoveEvent *pEvent)
[26637]917{
[26754]918 return QAbstractScrollArea::moveEvent(pEvent);
[26637]919}
920
[26754]921void UIMachineView::paintEvent(QPaintEvent *pPaintEvent)
[26637]922{
[26754]923 if (m_pauseShot.isNull())
[26637]924 {
[26754]925 /* Delegate the paint function to the VBoxFrameBuffer interface: */
[31283]926 if (m_pFrameBuffer && !uisession()->isTurnedOff())
[26754]927 m_pFrameBuffer->paintEvent(pPaintEvent);
[26637]928#ifdef Q_WS_MAC
929 /* Update the dock icon if we are in the running state */
[27215]930 if (uisession()->isRunning())
931 updateDockIcon();
932#endif /* Q_WS_MAC */
[26637]933 return;
934 }
935
936#ifdef VBOX_GUI_USE_QUARTZ2D
[30347]937 if (vboxGlobal().vmRenderMode() == VBoxDefs::Quartz2DMode && m_pFrameBuffer)
[27071]938 {
939 m_pFrameBuffer->paintEvent(pPaintEvent);
[27215]940 updateDockIcon();
[27071]941 }
942 else
[27215]943#endif /* VBOX_GUI_USE_QUARTZ2D */
[27071]944 {
[26929]945 /* We have a snapshot for the paused state: */
946 QRect r = pPaintEvent->rect().intersect(viewport()->rect());
947 /* We have to disable paint on screen if we are using the regular painter: */
948 bool paintOnScreen = viewport()->testAttribute(Qt::WA_PaintOnScreen);
949 viewport()->setAttribute(Qt::WA_PaintOnScreen, false);
950 QPainter pnt(viewport());
[31341]951 pnt.drawPixmap(r, m_pauseShot, QRect(r.x() + contentsX(), r.y() + contentsY(), r.width(), r.height()));
[26929]952 /* Restore the attribute to its previous state: */
953 viewport()->setAttribute(Qt::WA_PaintOnScreen, paintOnScreen);
[26637]954#ifdef Q_WS_MAC
[27215]955 updateDockIcon();
956#endif /* Q_WS_MAC */
[27071]957 }
[26637]958}
959
[30549]960#if defined(Q_WS_WIN)
[26754]961
[30637]962bool UIMachineView::winEvent(MSG *pMsg, long* /* piResult */)
[26754]963{
[30637]964 /* Check if some system event should be filtered-out.
965 * Returning 'true' means filtering-out,
966 * Returning 'false' means passing event to Qt. */
967 bool fResult = false; /* Pass to Qt by default: */
968 switch (pMsg->message)
[26754]969 {
[30637]970 case WM_KEYDOWN:
971 case WM_KEYUP:
972 case WM_SYSKEYDOWN:
973 case WM_SYSKEYUP:
[26754]974 {
[30637]975 /* Filter using keyboard-filter: */
976 bool fKeyboardFilteringResult = machineLogic()->keyboardHandler()->winEventFilter(pMsg, screenId());
977 /* Keyboard filter rules the result: */
978 fResult = fKeyboardFilteringResult;
[26754]979 break;
980 }
[30637]981 default:
[26754]982 break;
983 }
[30637]984 /* Return result: */
985 return fResult;
[26754]986}
987
988#elif defined(Q_WS_X11)
989
990bool UIMachineView::x11Event(XEvent *pEvent)
991{
[30637]992 /* Check if some system event should be filtered-out.
993 * Returning 'true' means filtering-out,
994 * Returning 'false' means passing event to Qt. */
995 bool fResult = false; /* Pass to Qt by default: */
[26754]996 switch (pEvent->type)
997 {
998 case XFocusOut:
999 case XFocusIn:
1000 case XKeyPress:
1001 case XKeyRelease:
[30637]1002 {
1003 /* Filter using keyboard-filter: */
1004 bool fKeyboardFilteringResult = machineLogic()->keyboardHandler()->x11EventFilter(pEvent, screenId());
1005 /* Filter using mouse-filter: */
1006 bool fMouseFilteringResult = machineLogic()->mouseHandler()->x11EventFilter(pEvent, screenId());
1007 /* If at least one of filters wants to filter event out then the result is 'true': */
1008 fResult = fKeyboardFilteringResult || fMouseFilteringResult;
[26754]1009 break;
[30637]1010 }
[26754]1011 default:
1012 break;
1013 }
[30637]1014 /* Return result: */
1015 return fResult;
[26754]1016}
1017
1018#endif
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use