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
Line 
1/* $Id: UIMachineView.cpp 35247 2010-12-20 14:19:48Z vboxsync $ */
2/** @file
3 *
4 * VBox frontends: Qt GUI ("VirtualBox"):
5 * UIMachineView class implementation
6 */
7
8/*
9 * Copyright (C) 2010 Oracle Corporation
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
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.
18 */
19
20/* Global includes */
21#include <QDesktopWidget>
22#include <QTimer>
23#include <QPainter>
24#include <QScrollBar>
25#include <VBox/VBoxVideo.h>
26
27/* Local includes */
28#include "VBoxGlobal.h"
29#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"
36#include "UISession.h"
37#include "UIActionsPool.h"
38#include "UIKeyboardHandler.h"
39#include "UIMouseHandler.h"
40#include "UIMachineLogic.h"
41#include "UIMachineWindow.h"
42#include "UIMachineViewNormal.h"
43#include "UIMachineViewFullscreen.h"
44#include "UIMachineViewSeamless.h"
45#include "UIMachineViewScale.h"
46
47#ifdef Q_WS_X11
48# include <QX11Info>
49# include <X11/XKBlib.h>
50# ifdef KeyPress
51const int XFocusOut = FocusOut;
52const int XFocusIn = FocusIn;
53const int XKeyPress = KeyPress;
54const int XKeyRelease = KeyRelease;
55# undef KeyRelease
56# undef KeyPress
57# undef FocusOut
58# undef FocusIn
59# endif
60#endif /* Q_WS_X11 */
61
62#ifdef Q_WS_MAC
63# include "DockIconPreview.h"
64# include "DarwinKeyboard.h"
65# include "UICocoaApplication.h"
66# include <VBox/err.h>
67# include <Carbon/Carbon.h>
68#endif /* Q_WS_MAC */
69
70class UIViewport: public QWidget
71{
72public:
73
74 UIViewport(QWidget *pParent) : QWidget(pParent)
75 {
76 /* No need for background drawing: */
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
90 , ulong uScreenId
91 , UIVisualStateType visualStateType
92#ifdef VBOX_WITH_VIDEOHWACCEL
93 , bool bAccelerate2DVideo
94#endif /* VBOX_WITH_VIDEOHWACCEL */
95 )
96{
97 UIMachineView *pMachineView = 0;
98 switch (visualStateType)
99 {
100 case UIVisualStateType_Normal:
101 pMachineView = new UIMachineViewNormal( pMachineWindow
102 , uScreenId
103#ifdef VBOX_WITH_VIDEOHWACCEL
104 , bAccelerate2DVideo
105#endif /* VBOX_WITH_VIDEOHWACCEL */
106 );
107 break;
108 case UIVisualStateType_Fullscreen:
109 pMachineView = new UIMachineViewFullscreen( pMachineWindow
110 , uScreenId
111#ifdef VBOX_WITH_VIDEOHWACCEL
112 , bAccelerate2DVideo
113#endif /* VBOX_WITH_VIDEOHWACCEL */
114 );
115 break;
116 case UIVisualStateType_Seamless:
117 pMachineView = new UIMachineViewSeamless( pMachineWindow
118 , uScreenId
119#ifdef VBOX_WITH_VIDEOHWACCEL
120 , bAccelerate2DVideo
121#endif /* VBOX_WITH_VIDEOHWACCEL */
122 );
123 break;
124 case UIVisualStateType_Scale:
125 pMachineView = new UIMachineViewScale( pMachineWindow
126 , uScreenId
127#ifdef VBOX_WITH_VIDEOHWACCEL
128 , bAccelerate2DVideo
129#endif
130 );
131 break;
132 default:
133 break;
134 }
135 return pMachineView;
136}
137
138void UIMachineView::destroy(UIMachineView *pMachineView)
139{
140 delete pMachineView;
141}
142
143double UIMachineView::aspectRatio() const
144{
145 return frameBuffer() ? (double)(frameBuffer()->width()) / frameBuffer()->height() : 0;
146}
147
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 {
157 if ( vboxGlobal().vmRenderMode() != VBoxDefs::TimerMode
158 && m_pFrameBuffer
159 &&
160 ( state != KMachineState_TeleportingPausedVM
161 || m_previousState != KMachineState_Teleporting))
162 {
163 takePauseShotLive();
164 /* Fully repaint to pick up m_pauseShot: */
165 viewport()->update();
166 }
167 break;
168 }
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 }
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: */
189 resetPauseShot();
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 }
201
202 m_previousState = state;
203}
204
205UIMachineView::UIMachineView( UIMachineWindow *pMachineWindow
206 , ulong uScreenId
207#ifdef VBOX_WITH_VIDEOHWACCEL
208 , bool bAccelerate2DVideo
209#endif /* VBOX_WITH_VIDEOHWACCEL */
210 )
211 : QAbstractScrollArea(pMachineWindow->machineWindow())
212 , m_pMachineWindow(pMachineWindow)
213 , m_uScreenId(uScreenId)
214 , m_pFrameBuffer(0)
215 , m_previousState(KMachineState_Null)
216 , m_desktopGeometryType(DesktopGeo_Invalid)
217 , m_bIsMachineWindowResizeIgnored(false)
218#ifdef VBOX_WITH_VIDEOHWACCEL
219 , m_fAccelerate2DVideo(bAccelerate2DVideo)
220#endif /* VBOX_WITH_VIDEOHWACCEL */
221{
222}
223
224UIMachineView::~UIMachineView()
225{
226}
227
228void UIMachineView::prepareViewport()
229{
230 /* Prepare viewport: */
231#ifdef VBOX_GUI_USE_QGLFB
232 QWidget *pViewport = 0;
233 switch (vboxGlobal().vmRenderMode())
234 {
235 case VBoxDefs::QGLMode:
236 pViewport = new VBoxGLWidget(session().GetConsole(), this, NULL);
237 break;
238 default:
239 pViewport = new UIViewport(this);
240 }
241#else /* VBOX_GUI_USE_QGLFB */
242 UIViewport *pViewport = new UIViewport(this);
243#endif /* !VBOX_GUI_USE_QGLFB */
244 setViewport(pViewport);
245}
246
247void UIMachineView::prepareFrameBuffer()
248{
249 /* Prepare frame-buffer depending on render-mode: */
250 switch (vboxGlobal().vmRenderMode())
251 {
252#ifdef VBOX_GUI_USE_QIMAGE
253 case VBoxDefs::QImageMode:
254# ifdef VBOX_WITH_VIDEOHWACCEL
255 if (m_fAccelerate2DVideo)
256 {
257 UIFrameBuffer* pFramebuffer = uisession()->frameBuffer(screenId());
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 */
264 pFramebuffer = new VBoxOverlayFrameBuffer<UIFrameBufferQImage, UIMachineView, UIResizeEvent>(this, &machineWindowWrapper()->session(), (uint32_t)screenId());
265 uisession()->setFrameBuffer(screenId(), pFramebuffer);
266 }
267 m_pFrameBuffer = pFramebuffer;
268 }
269 else
270 m_pFrameBuffer = new UIFrameBufferQImage(this);
271# else /* VBOX_WITH_VIDEOHWACCEL */
272 m_pFrameBuffer = new UIFrameBufferQImage(this);
273# endif /* !VBOX_WITH_VIDEOHWACCEL */
274 break;
275#endif /* VBOX_GUI_USE_QIMAGE */
276#ifdef VBOX_GUI_USE_QGLFB
277 case VBoxDefs::QGLMode:
278 m_pFrameBuffer = new UIFrameBufferQGL(this);
279 break;
280// case VBoxDefs::QGLOverlayMode:
281// m_pFrameBuffer = new UIQGLOverlayFrameBuffer(this);
282// break;
283#endif /* VBOX_GUI_USE_QGLFB */
284#ifdef VBOX_GUI_USE_SDL
285 case VBoxDefs::SDLMode:
286 /* Indicate that we are doing all drawing stuff ourself: */
287 // TODO_NEW_CORE
288 viewport()->setAttribute(Qt::WA_PaintOnScreen);
289# ifdef Q_WS_X11
290 /* This is somehow necessary to prevent strange X11 warnings on i386 and segfaults on x86_64: */
291 XFlush(QX11Info::display());
292# endif /* Q_WS_X11 */
293# if defined(VBOX_WITH_VIDEOHWACCEL) && defined(DEBUG_misha) /* not tested yet */
294 if (m_fAccelerate2DVideo)
295 {
296 class UIFrameBuffer* pFramebuffer = uisession()->frameBuffer(screenId());
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 */
303 pFramebuffer = new VBoxOverlayFrameBuffer<UIFrameBufferSDL, UIMachineView, UIResizeEvent>(this, &machineWindowWrapper()->session(), (uint32_t)screenId());
304 uisession()->setFrameBuffer(screenId(), pFramebuffer);
305 }
306 m_pFrameBuffer = pFramebuffer;
307 }
308 else
309 m_pFrameBuffer = new UIFrameBufferSDL(this);
310# else
311 m_pFrameBuffer = new UIFrameBufferSDL(this);
312# endif
313 /* Disable scrollbars because we cannot correctly draw in a scrolled window using SDL: */
314 horizontalScrollBar()->setEnabled(false);
315 verticalScrollBar()->setEnabled(false);
316 break;
317#endif /* VBOX_GUI_USE_SDL */
318#if 0 // TODO: Enable DDraw frame buffer!
319#ifdef VBOX_GUI_USE_DDRAW
320 case VBoxDefs::DDRAWMode:
321 m_pFrameBuffer = new UIDDRAWFrameBuffer(this);
322 if (!m_pFrameBuffer || m_pFrameBuffer->address() == NULL)
323 {
324 if (m_pFrameBuffer)
325 delete m_pFrameBuffer;
326 m_mode = VBoxDefs::QImageMode;
327 m_pFrameBuffer = new UIFrameBufferQImage(this);
328 }
329 break;
330#endif /* VBOX_GUI_USE_DDRAW */
331#endif
332#ifdef VBOX_GUI_USE_QUARTZ2D
333 case VBoxDefs::Quartz2DMode:
334 /* Indicate that we are doing all drawing stuff ourself: */
335 viewport()->setAttribute(Qt::WA_PaintOnScreen);
336# ifdef VBOX_WITH_VIDEOHWACCEL
337 if (m_fAccelerate2DVideo)
338 {
339 UIFrameBuffer* pFramebuffer = uisession()->frameBuffer(screenId());
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 */
346 pFramebuffer = new VBoxOverlayFrameBuffer<UIFrameBufferQuartz2D, UIMachineView, UIResizeEvent>(this, &machineWindowWrapper()->session(), (uint32_t)screenId());
347 uisession()->setFrameBuffer(screenId(), pFramebuffer);
348 }
349 m_pFrameBuffer = pFramebuffer;
350 }
351 else
352 m_pFrameBuffer = new UIFrameBufferQuartz2D(this);
353# else /* VBOX_WITH_VIDEOHWACCEL */
354 m_pFrameBuffer = new UIFrameBufferQuartz2D(this);
355# endif /* !VBOX_WITH_VIDEOHWACCEL */
356 break;
357#endif /* VBOX_GUI_USE_QUARTZ2D */
358 default:
359 AssertReleaseMsgFailed(("Render mode must be valid: %d\n", vboxGlobal().vmRenderMode()));
360 LogRel(("Invalid render mode: %d\n", vboxGlobal().vmRenderMode()));
361 qApp->exit(1);
362 break;
363 }
364
365 /* If frame-buffer was prepared: */
366 if (m_pFrameBuffer)
367 {
368 /* Prepare display: */
369 CDisplay display = session().GetConsole().GetDisplay();
370 Assert(!display.isNull());
371#ifdef VBOX_WITH_VIDEOHWACCEL
372 CFramebuffer fb(NULL);
373 if (m_fAccelerate2DVideo)
374 {
375 LONG XOrigin, YOrigin;
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. */
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 */
381#endif /* VBOX_WITH_VIDEOHWACCEL */
382 {
383 m_pFrameBuffer->AddRef();
384 }
385 /* Always perform SetFramebuffer to ensure 3D gets notified: */
386 display.SetFramebuffer(m_uScreenId, CFramebuffer(m_pFrameBuffer));
387 }
388
389 QSize size;
390#ifdef Q_WS_X11
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)
395 size = guestSizeHint();
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)
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 }
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 }
420}
421
422void UIMachineView::prepareCommon()
423{
424 /* Prepare view frame: */
425 setFrameStyle(QFrame::NoFrame);
426
427 /* Setup palette: */
428 QPalette palette(viewport()->palette());
429 palette.setColor(viewport()->backgroundRole(), Qt::black);
430 viewport()->setPalette(palette);
431
432 /* Setup focus policy: */
433 setFocusPolicy(Qt::WheelFocus);
434}
435
436void UIMachineView::prepareFilters()
437{
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
449void UIMachineView::prepareConsoleConnections()
450{
451 /* Machine state-change updater: */
452 connect(uisession(), SIGNAL(sigMachineStateChange()), this, SLOT(sltMachineStateChanged()));
453}
454
455void UIMachineView::loadMachineViewSettings()
456{
457 /* Global settings: */
458 {
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 }
472 }
473}
474
475void UIMachineView::cleanupFrameBuffer()
476{
477 if (m_pFrameBuffer)
478 {
479 /* Process pending frame-buffer resize events: */
480 QApplication::sendPostedEvents(this, VBoxDefs::ResizeEventType);
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 */
487 Assert(m_pFrameBuffer == uisession()->frameBuffer(screenId()));
488 m_pFrameBuffer->setView(NULL);
489 }
490 else
491#endif /* VBOX_WITH_VIDEOHWACCEL */
492 {
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;
502 }
503 }
504}
505
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());
527 if (fb.width() < 16 || fb.height() < 16)
528 {
529 CMachine machine = uisession()->session().GetMachine();
530 if ( vboxGlobal().isStartPausedEnabled()
531 || vboxGlobal().isDebuggerAutoShowEnabled(machine))
532 fb = QSize(640, 480);
533 }
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
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 {
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());
693 dimImage(shot);
694 m_pauseShot = QPixmap::fromImage(shot);
695 }
696}
697
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
822bool UIMachineView::event(QEvent *pEvent)
823{
824 switch (pEvent->type())
825 {
826 case VBoxDefs::RepaintEventType:
827 {
828 UIRepaintEvent *pPaintEvent = static_cast<UIRepaintEvent*>(pEvent);
829 viewport()->update(pPaintEvent->x() - contentsX(), pPaintEvent->y() - contentsY(),
830 pPaintEvent->width(), pPaintEvent->height());
831 return true;
832 }
833
834#ifdef Q_WS_MAC
835 /* Event posted OnShowWindow: */
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 }
847#endif /* Q_WS_MAC */
848
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
857 default:
858 break;
859 }
860
861 return QAbstractScrollArea::event(pEvent);
862}
863
864bool UIMachineView::eventFilter(QObject *pWatched, QEvent *pEvent)
865{
866#ifdef VBOX_WITH_VIDEOHWACCEL
867 if (pWatched == viewport())
868 {
869 switch (pEvent->type())
870 {
871 case QEvent::Resize:
872 {
873 if (m_pFrameBuffer)
874 m_pFrameBuffer->viewportResized(static_cast<QResizeEvent*>(pEvent));
875 break;
876 }
877 default:
878 break;
879 }
880 }
881#endif /* VBOX_WITH_VIDEOHWACCEL */
882 if (pWatched == machineWindowWrapper()->machineWindow())
883 {
884 switch (pEvent->type())
885 {
886 case QEvent::WindowStateChange:
887 {
888 /* During minimizing and state restoring machineWindowWrapper() gets
889 * the focus which belongs to console view window, so returning it properly. */
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 }
902 default:
903 break;
904 }
905 }
906
907 return QAbstractScrollArea::eventFilter(pWatched, pEvent);
908}
909
910void UIMachineView::resizeEvent(QResizeEvent *pEvent)
911{
912 updateSliders();
913 return QAbstractScrollArea::resizeEvent(pEvent);
914}
915
916void UIMachineView::moveEvent(QMoveEvent *pEvent)
917{
918 return QAbstractScrollArea::moveEvent(pEvent);
919}
920
921void UIMachineView::paintEvent(QPaintEvent *pPaintEvent)
922{
923 if (m_pauseShot.isNull())
924 {
925 /* Delegate the paint function to the VBoxFrameBuffer interface: */
926 if (m_pFrameBuffer && !uisession()->isTurnedOff())
927 m_pFrameBuffer->paintEvent(pPaintEvent);
928#ifdef Q_WS_MAC
929 /* Update the dock icon if we are in the running state */
930 if (uisession()->isRunning())
931 updateDockIcon();
932#endif /* Q_WS_MAC */
933 return;
934 }
935
936#ifdef VBOX_GUI_USE_QUARTZ2D
937 if (vboxGlobal().vmRenderMode() == VBoxDefs::Quartz2DMode && m_pFrameBuffer)
938 {
939 m_pFrameBuffer->paintEvent(pPaintEvent);
940 updateDockIcon();
941 }
942 else
943#endif /* VBOX_GUI_USE_QUARTZ2D */
944 {
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());
951 pnt.drawPixmap(r, m_pauseShot, QRect(r.x() + contentsX(), r.y() + contentsY(), r.width(), r.height()));
952 /* Restore the attribute to its previous state: */
953 viewport()->setAttribute(Qt::WA_PaintOnScreen, paintOnScreen);
954#ifdef Q_WS_MAC
955 updateDockIcon();
956#endif /* Q_WS_MAC */
957 }
958}
959
960#if defined(Q_WS_WIN)
961
962bool UIMachineView::winEvent(MSG *pMsg, long* /* piResult */)
963{
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)
969 {
970 case WM_KEYDOWN:
971 case WM_KEYUP:
972 case WM_SYSKEYDOWN:
973 case WM_SYSKEYUP:
974 {
975 /* Filter using keyboard-filter: */
976 bool fKeyboardFilteringResult = machineLogic()->keyboardHandler()->winEventFilter(pMsg, screenId());
977 /* Keyboard filter rules the result: */
978 fResult = fKeyboardFilteringResult;
979 break;
980 }
981 default:
982 break;
983 }
984 /* Return result: */
985 return fResult;
986}
987
988#elif defined(Q_WS_X11)
989
990bool UIMachineView::x11Event(XEvent *pEvent)
991{
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: */
996 switch (pEvent->type)
997 {
998 case XFocusOut:
999 case XFocusIn:
1000 case XKeyPress:
1001 case XKeyRelease:
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;
1009 break;
1010 }
1011 default:
1012 break;
1013 }
1014 /* Return result: */
1015 return fResult;
1016}
1017
1018#endif
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use