VirtualBox

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

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

FE/Qt: bugref:9598: UISession, UIMachineView and UIMouseHandler: Now the curious one, mouse pointer shape scaling should be done on per-screen basis, so we had to move corresponding functionality from session to particular machine-view and keep proper signal order hierarchy accordingly; This will probably break frame-buffer cursor functionality.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 70.7 KB
Line 
1/* $Id: UIMachineView.cpp 82032 2019-11-20 16:21:59Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIMachineView class implementation.
4 */
5
6/*
7 * Copyright (C) 2010-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Qt includes: */
19#include <QBitmap>
20#include <QMainWindow>
21#include <QPainter>
22#include <QScrollBar>
23#include <QTimer>
24#include <QAbstractNativeEventFilter>
25
26/* GUI includes: */
27#include "UICommon.h"
28#include "UIActionPoolRuntime.h"
29#include "UIDesktopWidgetWatchdog.h"
30#include "UIExtraDataManager.h"
31#include "UIMessageCenter.h"
32#include "UISession.h"
33#include "UIMachineLogic.h"
34#include "UIMachineWindow.h"
35#include "UIMachineViewNormal.h"
36#include "UIMachineViewFullscreen.h"
37#include "UIMachineViewSeamless.h"
38#include "UIMachineViewScale.h"
39#include "UIKeyboardHandler.h"
40#include "UIMouseHandler.h"
41#include "UIFrameBuffer.h"
42#include "VBoxFBOverlay.h"
43#ifdef VBOX_WS_MAC
44# include "UICocoaApplication.h"
45# include "DarwinKeyboard.h"
46# include "DockIconPreview.h"
47#endif
48#ifdef VBOX_WITH_DRAG_AND_DROP
49# include "UIDnDHandler.h"
50#endif
51
52/* VirtualBox interface declarations: */
53#include <VBox/com/VirtualBox.h>
54
55/* COM includes: */
56#include "CConsole.h"
57#include "CDisplay.h"
58#include "CGraphicsAdapter.h"
59#include "CSession.h"
60#include "CFramebuffer.h"
61#ifdef VBOX_WITH_DRAG_AND_DROP
62# include "CDnDSource.h"
63# include "CDnDTarget.h"
64# include "CGuest.h"
65# include "CGuestDnDSource.h"
66# include "CGuestDnDTarget.h"
67#endif
68
69/* Other VBox includes: */
70#include <VBox/VBoxOGL.h>
71#include <VBoxVideo.h>
72#include <iprt/asm.h>
73#include <iprt/errcore.h>
74
75/* External includes: */
76#include <math.h>
77#ifdef VBOX_WS_MAC
78# include <Carbon/Carbon.h>
79#endif
80#ifdef VBOX_WS_X11
81# include <xcb/xcb.h>
82#endif
83
84#ifdef DEBUG_andy
85/* Macro for debugging drag and drop actions which usually would
86 * go to Main's logging group. */
87# define DNDDEBUG(x) LogFlowFunc(x)
88#else
89# define DNDDEBUG(x)
90#endif
91
92
93/** QAbstractNativeEventFilter extension
94 * allowing to pre-process native platform events. */
95class UINativeEventFilter : public QAbstractNativeEventFilter
96{
97public:
98
99 /** Constructs native event filter storing @a pParent to redirect events to. */
100 UINativeEventFilter(UIMachineView *pParent)
101 : m_pParent(pParent)
102 {}
103
104 /** Redirects all the native events to parent. */
105 bool nativeEventFilter(const QByteArray &eventType, void *pMessage, long * /* pResult */)
106 {
107 return m_pParent->nativeEventPreprocessor(eventType, pMessage);
108 }
109
110private:
111
112 /** Holds the passed parent reference. */
113 UIMachineView *m_pParent;
114};
115
116
117/* static */
118UIMachineView* UIMachineView::create( UIMachineWindow *pMachineWindow
119 , ulong uScreenId
120 , UIVisualStateType visualStateType
121#ifdef VBOX_WITH_VIDEOHWACCEL
122 , bool bAccelerate2DVideo
123#endif /* VBOX_WITH_VIDEOHWACCEL */
124 )
125{
126 UIMachineView *pMachineView = 0;
127 switch (visualStateType)
128 {
129 case UIVisualStateType_Normal:
130 pMachineView = new UIMachineViewNormal( pMachineWindow
131 , uScreenId
132#ifdef VBOX_WITH_VIDEOHWACCEL
133 , bAccelerate2DVideo
134#endif /* VBOX_WITH_VIDEOHWACCEL */
135 );
136 break;
137 case UIVisualStateType_Fullscreen:
138 pMachineView = new UIMachineViewFullscreen( pMachineWindow
139 , uScreenId
140#ifdef VBOX_WITH_VIDEOHWACCEL
141 , bAccelerate2DVideo
142#endif /* VBOX_WITH_VIDEOHWACCEL */
143 );
144 break;
145 case UIVisualStateType_Seamless:
146 pMachineView = new UIMachineViewSeamless( pMachineWindow
147 , uScreenId
148#ifdef VBOX_WITH_VIDEOHWACCEL
149 , bAccelerate2DVideo
150#endif /* VBOX_WITH_VIDEOHWACCEL */
151 );
152 break;
153 case UIVisualStateType_Scale:
154 pMachineView = new UIMachineViewScale( pMachineWindow
155 , uScreenId
156#ifdef VBOX_WITH_VIDEOHWACCEL
157 , bAccelerate2DVideo
158#endif
159 );
160 break;
161 default:
162 break;
163 }
164
165 /* Load machine-view settings: */
166 pMachineView->loadMachineViewSettings();
167
168 /* Prepare viewport: */
169 pMachineView->prepareViewport();
170
171 /* Prepare frame-buffer: */
172 pMachineView->prepareFrameBuffer();
173
174 /* Prepare common things: */
175 pMachineView->prepareCommon();
176
177 /* Prepare event-filters: */
178 pMachineView->prepareFilters();
179
180 /* Prepare connections: */
181 pMachineView->prepareConnections();
182
183 /* Prepare console connections: */
184 pMachineView->prepareConsoleConnections();
185
186 /* Initialization: */
187 pMachineView->sltMachineStateChanged();
188 /** @todo Can we move the call to sltAdditionsStateChanged() from the
189 * subclass constructors here too? It is called for Normal and Seamless,
190 * but not for Fullscreen and Scale. However for Scale it is a no op.,
191 * so it would not hurt. Would it hurt for fullscreen? */
192
193 /* Set a preliminary maximum size: */
194 pMachineView->setMaxGuestSize();
195
196 /* Resend the last resize hint finally: */
197 pMachineView->resendSizeHint();
198
199 /* Return the created view: */
200 return pMachineView;
201}
202
203/* static */
204void UIMachineView::destroy(UIMachineView *pMachineView)
205{
206 if (!pMachineView)
207 return;
208
209 /* Cleanup frame-buffer: */
210 pMachineView->cleanupFrameBuffer();
211
212#ifdef VBOX_WITH_DRAG_AND_DROP
213 if (pMachineView->m_pDnDHandler)
214 {
215 delete pMachineView->m_pDnDHandler;
216 pMachineView->m_pDnDHandler = NULL;
217 }
218#endif
219
220 /* Cleanup native filters: */
221 pMachineView->cleanupNativeFilters();
222
223 delete pMachineView;
224}
225
226void UIMachineView::applyMachineViewScaleFactor()
227{
228 /* Sanity check: */
229 if (!frameBuffer())
230 return;
231
232 /* Acquire selected scale-factor: */
233 double dScaleFactor = gEDataManager->scaleFactor(uiCommon().managedVMUuid(), m_uScreenId);
234
235 /* Take the device-pixel-ratio into account: */
236 frameBuffer()->setDevicePixelRatio(gpDesktop->devicePixelRatio(machineWindow()));
237 frameBuffer()->setDevicePixelRatioActual(gpDesktop->devicePixelRatioActual(machineWindow()));
238 const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
239 const bool fUseUnscaledHiDPIOutput = dScaleFactor != dDevicePixelRatioActual;
240 dScaleFactor = fUseUnscaledHiDPIOutput ? dScaleFactor : 1.0;
241
242 /* Assign frame-buffer with new values: */
243 frameBuffer()->setScaleFactor(dScaleFactor);
244 frameBuffer()->setUseUnscaledHiDPIOutput(fUseUnscaledHiDPIOutput);
245
246 /* Propagate the scale-factor related attributes to 3D service if necessary: */
247 if (machine().GetGraphicsAdapter().GetAccelerate3DEnabled() && uiCommon().is3DAvailable())
248 {
249 double dScaleFactorFor3D = dScaleFactor;
250#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
251 // WORKAROUND:
252 // On Windows and Linux opposing to macOS it's only Qt which can auto scale up,
253 // not 3D overlay itself, so for auto scale-up mode we have to take that into account.
254 if (!fUseUnscaledHiDPIOutput)
255 dScaleFactorFor3D *= frameBuffer()->devicePixelRatioActual();
256#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
257 display().NotifyScaleFactorChange(m_uScreenId,
258 (uint32_t)(dScaleFactorFor3D * VBOX_OGL_SCALE_FACTOR_MULTIPLIER),
259 (uint32_t)(dScaleFactorFor3D * VBOX_OGL_SCALE_FACTOR_MULTIPLIER));
260 display().NotifyHiDPIOutputPolicyChange(fUseUnscaledHiDPIOutput);
261 }
262
263 /* Perform frame-buffer rescaling: */
264 frameBuffer()->performRescale();
265
266 /* Update console's display viewport and 3D overlay: */
267 updateViewport();
268}
269
270double UIMachineView::aspectRatio() const
271{
272 return frameBuffer() ? (double)(frameBuffer()->width()) / frameBuffer()->height() : 0;
273}
274
275void UIMachineView::updateViewport()
276{
277 display().ViewportChanged(screenId(), contentsX(), contentsY(), visibleWidth(), visibleHeight());
278}
279
280void UIMachineView::sltPerformGuestResize(const QSize &toSize)
281{
282 /* If this slot is invoked directly then use the passed size otherwise get
283 * the available size for the guest display. We assume here that centralWidget()
284 * contains this view only and gives it all available space: */
285 QSize size(toSize.isValid() ? toSize : machineWindow()->centralWidget()->size());
286 AssertMsg(size.isValid(), ("Size should be valid!\n"));
287
288 /* Take the scale-factor(s) into account: */
289 size = scaledBackward(size);
290
291 /* Expand current limitations: */
292 setMaxGuestSize(size);
293
294 /* Send new size-hint to the guest: */
295 LogRel(("GUI: UIMachineView::sltPerformGuestResize: "
296 "Sending guest size-hint to screen %d as %dx%d if necessary\n",
297 (int)screenId(), size.width(), size.height()));
298
299 /* Record the hint to extra data, needed for guests using VMSVGA: */
300 /* This should be done before the actual hint is sent in case the guest overrides it. */
301 /* Do not send a hint if nothing has changed to prevent the guest being notified about its own changes. */
302 if ( !isFullscreenOrSeamless()
303 && uisession()->isGuestSupportsGraphics()
304 && ( (int)m_pFrameBuffer->width() != size.width()
305 || (int)m_pFrameBuffer->height() != size.height()
306 || uisession()->isScreenVisible(screenId()) != uisession()->isScreenVisibleHostDesires(screenId())))
307 storeGuestSizeHint(size);
308
309 /* If auto-mount of guest-screens (auto-pilot) enabled: */
310 if (gEDataManager->autoMountGuestScreensEnabled(uiCommon().managedVMUuid()))
311 {
312 /* Do not send a hint if nothing has changed to prevent the guest being notified about its own changes: */
313 if ( (int)m_pFrameBuffer->width() != size.width() || (int)m_pFrameBuffer->height() != size.height()
314 || uisession()->isScreenVisible(screenId()) != uisession()->isScreenVisibleHostDesires(screenId()))
315 {
316 /* If host and guest have same opinion about guest-screen visibility: */
317 if (uisession()->isScreenVisible(screenId()) == uisession()->isScreenVisibleHostDesires(screenId()))
318 display().SetVideoModeHint(screenId(),
319 uisession()->isScreenVisible(screenId()),
320 false, 0, 0, size.width(), size.height(), 0, true);
321 /* If host desires to have guest-screen disabled and guest-screen is enabled, retrying: */
322 else if (!uisession()->isScreenVisibleHostDesires(screenId()))
323 display().SetVideoModeHint(screenId(), false, false, 0, 0, 0, 0, 0, true);
324 /* If host desires to have guest-screen enabled and guest-screen is disabled, retrying: */
325 else if (uisession()->isScreenVisibleHostDesires(screenId()))
326 display().SetVideoModeHint(screenId(), true, false, 0, 0, size.width(), size.height(), 0, true);
327 }
328 }
329 /* If auto-mount of guest-screens (auto-pilot) disabled: */
330 else
331 {
332 /* Do not send a hint if nothing has changed to prevent the guest being notified about its own changes: */
333 if ((int)m_pFrameBuffer->width() != size.width() || (int)m_pFrameBuffer->height() != size.height())
334 display().SetVideoModeHint(screenId(),
335 uisession()->isScreenVisible(screenId()),
336 false, 0, 0, size.width(), size.height(), 0, true);
337 }
338}
339
340void UIMachineView::sltHandleNotifyChange(int iWidth, int iHeight)
341{
342 LogRel2(("GUI: UIMachineView::sltHandleNotifyChange: Screen=%d, Size=%dx%d\n",
343 (unsigned long)m_uScreenId, iWidth, iHeight));
344
345 /* Some situations require frame-buffer resize-events to be ignored at all,
346 * leaving machine-window, machine-view and frame-buffer sizes preserved: */
347 if (uisession()->isGuestResizeIgnored())
348 return;
349
350 /* In some situations especially in some VM states, guest-screen is not drawable: */
351 if (uisession()->isGuestScreenUnDrawable())
352 return;
353
354 /* Get old frame-buffer size: */
355 const QSize frameBufferSizeOld = QSize(frameBuffer()->width(),
356 frameBuffer()->height());
357
358 /* Perform frame-buffer mode-change: */
359 frameBuffer()->handleNotifyChange(iWidth, iHeight);
360
361 /* Get new frame-buffer size: */
362 const QSize frameBufferSizeNew = QSize(frameBuffer()->width(),
363 frameBuffer()->height());
364
365 /* For 'scale' mode: */
366 if (visualStateType() == UIVisualStateType_Scale)
367 {
368 /* Assign new frame-buffer logical-size: */
369 QSize scaledSize = size();
370 const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
371 const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
372 scaledSize *= dDevicePixelRatioFormal;
373 if (!frameBuffer()->useUnscaledHiDPIOutput())
374 scaledSize /= dDevicePixelRatioActual;
375 frameBuffer()->setScaledSize(scaledSize);
376
377 /* Forget the last full-screen size: */
378 uisession()->setLastFullScreenSize(screenId(), QSize(-1, -1));
379 }
380 /* For other than 'scale' mode: */
381 else
382 {
383 /* Adjust maximum-size restriction for machine-view: */
384 setMaximumSize(sizeHint());
385
386 /* Disable the resize hint override hack and forget the last full-screen size: */
387 m_sizeHintOverride = QSize(-1, -1);
388 if (visualStateType() == UIVisualStateType_Normal)
389 uisession()->setLastFullScreenSize(screenId(), QSize(-1, -1));
390
391 /* Force machine-window update own layout: */
392 QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
393
394 /* Update machine-view sliders: */
395 updateSliders();
396
397 /* By some reason Win host forgets to update machine-window central-widget
398 * after main-layout was updated, let's do it for all the hosts: */
399 machineWindow()->centralWidget()->update();
400
401 /* Normalize 'normal' machine-window geometry if necessary: */
402 if (visualStateType() == UIVisualStateType_Normal &&
403 frameBufferSizeNew != frameBufferSizeOld)
404 machineWindow()->normalizeGeometry(true /* adjust position */);
405 }
406
407 /* Perform frame-buffer rescaling: */
408 frameBuffer()->performRescale();
409
410#ifdef VBOX_WS_MAC
411 /* Update MacOS X dock icon size: */
412 machineLogic()->updateDockIconSize(screenId(), iWidth, iHeight);
413#endif /* VBOX_WS_MAC */
414
415 /* Notify frame-buffer resize: */
416 emit sigFrameBufferResize();
417
418 /* Ask for just required guest display update (it will also update
419 * the viewport through IFramebuffer::NotifyUpdate): */
420 display().InvalidateAndUpdateScreen(m_uScreenId);
421
422 /* If we are in normal or scaled mode and if GA are active,
423 * remember the guest-screen size to be able to restore it when necessary: */
424 /* As machines with Linux/Solaris and VMSVGA are not able to tell us
425 * whether a resize was due to the system or user interaction we currently
426 * do not store hints for these systems except when we explicitly send them
427 * ourselves. Windows guests should use VBoxVGA controllers, not VMSVGA. */
428 if ( !isFullscreenOrSeamless()
429 && uisession()->isGuestSupportsGraphics()
430 && (machine().GetGraphicsAdapter().GetGraphicsControllerType() != KGraphicsControllerType_VMSVGA))
431 storeGuestSizeHint(QSize(iWidth, iHeight));
432
433 LogRelFlow(("GUI: UIMachineView::sltHandleNotifyChange: Complete for Screen=%d, Size=%dx%d\n",
434 (unsigned long)m_uScreenId, iWidth, iHeight));
435}
436
437void UIMachineView::sltHandleNotifyUpdate(int iX, int iY, int iWidth, int iHeight)
438{
439 /* Prepare corresponding viewport part: */
440 QRect rect(iX, iY, iWidth, iHeight);
441
442 /* Take the scaling into account: */
443 const double dScaleFactor = frameBuffer()->scaleFactor();
444 const QSize scaledSize = frameBuffer()->scaledSize();
445 if (scaledSize.isValid())
446 {
447 /* Calculate corresponding scale-factors: */
448 const double xScaleFactor = visualStateType() == UIVisualStateType_Scale ?
449 (double)scaledSize.width() / frameBuffer()->width() : dScaleFactor;
450 const double yScaleFactor = visualStateType() == UIVisualStateType_Scale ?
451 (double)scaledSize.height() / frameBuffer()->height() : dScaleFactor;
452 /* Adjust corresponding viewport part: */
453 rect.moveTo((int)floor((double)rect.x() * xScaleFactor) - 1,
454 (int)floor((double)rect.y() * yScaleFactor) - 1);
455 rect.setSize(QSize((int)ceil((double)rect.width() * xScaleFactor) + 2,
456 (int)ceil((double)rect.height() * yScaleFactor) + 2));
457 }
458
459 /* Shift has to be scaled by the device-pixel-ratio
460 * but not scaled by the scale-factor. */
461 rect.translate(-contentsX(), -contentsY());
462
463 /* Take the device-pixel-ratio into account: */
464 const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
465 const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
466 if (!frameBuffer()->useUnscaledHiDPIOutput() && dDevicePixelRatioActual != 1.0)
467 {
468 rect.moveTo((int)floor((double)rect.x() * dDevicePixelRatioActual) - 1,
469 (int)floor((double)rect.y() * dDevicePixelRatioActual) - 1);
470 rect.setSize(QSize((int)ceil((double)rect.width() * dDevicePixelRatioActual) + 2,
471 (int)ceil((double)rect.height() * dDevicePixelRatioActual) + 2));
472 }
473 if (dDevicePixelRatioFormal != 1.0)
474 {
475 rect.moveTo((int)floor((double)rect.x() / dDevicePixelRatioFormal) - 1,
476 (int)floor((double)rect.y() / dDevicePixelRatioFormal) - 1);
477 rect.setSize(QSize((int)ceil((double)rect.width() / dDevicePixelRatioFormal) + 2,
478 (int)ceil((double)rect.height() / dDevicePixelRatioFormal) + 2));
479 }
480
481 /* Limit the resulting part by the viewport rectangle: */
482 rect &= viewport()->rect();
483
484 /* Update corresponding viewport part: */
485 viewport()->update(rect);
486}
487
488void UIMachineView::sltHandleSetVisibleRegion(QRegion region)
489{
490 /* Used only in seamless-mode. */
491 Q_UNUSED(region);
492}
493
494void UIMachineView::sltHandle3DOverlayVisibilityChange(bool fVisible)
495{
496 machineLogic()->notifyAbout3DOverlayVisibilityChange(fVisible);
497}
498
499void UIMachineView::sltDesktopResized()
500{
501 setMaxGuestSize();
502}
503
504void UIMachineView::sltHandleScaleFactorChange(const QUuid &uMachineID)
505{
506 /* Skip unrelated machine IDs: */
507 if (uMachineID != uiCommon().managedVMUuid())
508 return;
509
510 /* Acquire selected scale-factor: */
511 double dScaleFactor = gEDataManager->scaleFactor(uiCommon().managedVMUuid(), m_uScreenId);
512
513 /* Take the device-pixel-ratio into account: */
514 const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
515 const bool fUseUnscaledHiDPIOutput = dScaleFactor != dDevicePixelRatioActual;
516 dScaleFactor = fUseUnscaledHiDPIOutput ? dScaleFactor : 1.0;
517
518 /* Assign frame-buffer with new values: */
519 frameBuffer()->setScaleFactor(dScaleFactor);
520 frameBuffer()->setUseUnscaledHiDPIOutput(fUseUnscaledHiDPIOutput);
521
522 /* Propagate the scale-factor related attributes to 3D service if necessary: */
523 if (machine().GetGraphicsAdapter().GetAccelerate3DEnabled() && uiCommon().is3DAvailable())
524 {
525 double dScaleFactorFor3D = dScaleFactor;
526#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
527 // WORKAROUND:
528 // On Windows and Linux opposing to macOS it's only Qt which can auto scale up,
529 // not 3D overlay itself, so for auto scale-up mode we have to take that into account.
530 if (!fUseUnscaledHiDPIOutput)
531 dScaleFactorFor3D *= frameBuffer()->devicePixelRatioActual();
532#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
533 display().NotifyScaleFactorChange(m_uScreenId,
534 (uint32_t)(dScaleFactorFor3D * VBOX_OGL_SCALE_FACTOR_MULTIPLIER),
535 (uint32_t)(dScaleFactorFor3D * VBOX_OGL_SCALE_FACTOR_MULTIPLIER));
536 display().NotifyHiDPIOutputPolicyChange(fUseUnscaledHiDPIOutput);
537 }
538
539 /* Handle scale attributes change: */
540 handleScaleChange();
541 /* Adjust guest-screen size: */
542 adjustGuestScreenSize();
543
544 /* Update scaled pause pixmap, if necessary: */
545 updateScaledPausePixmap();
546 viewport()->update();
547
548 /* Update console's display viewport and 3D overlay: */
549 updateViewport();
550}
551
552void UIMachineView::sltHandleScalingOptimizationChange(const QUuid &uMachineID)
553{
554 /* Skip unrelated machine IDs: */
555 if (uMachineID != uiCommon().managedVMUuid())
556 return;
557
558 /* Take the scaling-optimization type into account: */
559 frameBuffer()->setScalingOptimizationType(gEDataManager->scalingOptimizationType(uiCommon().managedVMUuid()));
560
561 /* Update viewport: */
562 viewport()->update();
563}
564
565void UIMachineView::sltMachineStateChanged()
566{
567 /* Get machine state: */
568 KMachineState state = uisession()->machineState();
569 switch (state)
570 {
571 case KMachineState_Paused:
572 case KMachineState_TeleportingPausedVM:
573 {
574 if ( m_pFrameBuffer
575 && ( state != KMachineState_TeleportingPausedVM
576 || m_previousState != KMachineState_Teleporting))
577 {
578 /* Take live pause-pixmap: */
579 takePausePixmapLive();
580 /* Fully repaint to pick up pause-pixmap: */
581 viewport()->update();
582 }
583 break;
584 }
585 case KMachineState_Restoring:
586 {
587 /* Only works with the primary screen currently. */
588 if (screenId() == 0)
589 {
590 /* Take snapshot pause-pixmap: */
591 takePausePixmapSnapshot();
592 /* Fully repaint to pick up pause-pixmap: */
593 viewport()->update();
594 }
595 break;
596 }
597 case KMachineState_Running:
598 {
599 if (m_previousState == KMachineState_Paused ||
600 m_previousState == KMachineState_TeleportingPausedVM ||
601 m_previousState == KMachineState_Restoring)
602 {
603 if (m_pFrameBuffer)
604 {
605 /* Reset pause-pixmap: */
606 resetPausePixmap();
607 /* Ask for full guest display update (it will also update
608 * the viewport through IFramebuffer::NotifyUpdate): */
609 display().InvalidateAndUpdate();
610 }
611 }
612 /* Reapply machine-view scale-factor: */
613 applyMachineViewScaleFactor();
614 break;
615 }
616 default:
617 break;
618 }
619
620 m_previousState = state;
621}
622
623void UIMachineView::sltMousePointerShapeChange()
624{
625 /* Fetch the shape and the mask: */
626 QPixmap pixmapShape = uisession()->cursorShapePixmap();
627 QPixmap pixmapMask = uisession()->cursorMaskPixmap();
628 const QPoint hotspot = uisession()->cursorHotspot();
629 uint uXHot = hotspot.x();
630 uint uYHot = hotspot.y();
631
632 /* If there is no mask: */
633 if (pixmapMask.isNull())
634 {
635 /* Scale the shape pixmap and
636 * compose the cursor on the basis of shape only: */
637 updateMousePointerPixmapScaling(pixmapShape, uXHot, uYHot);
638 m_cursor = QCursor(pixmapShape, uXHot, uYHot);
639 }
640 /* Otherwise: */
641 else
642 {
643 /* Scale the shape and the mask pixmaps and
644 * compose the cursor on the basis of shape and mask both: */
645 updateMousePointerPixmapScaling(pixmapShape, uXHot, uYHot);
646 /// @todo updateMousePointerPixmapScaling(pixmapMask, uXHot, uYHot);
647 m_cursor = QCursor(pixmapShape, pixmapMask, uXHot, uYHot);
648 }
649
650 /* Let the listeners know: */
651 emit sigMousePointerShapeChange();
652}
653
654UIMachineView::UIMachineView( UIMachineWindow *pMachineWindow
655 , ulong uScreenId
656#ifdef VBOX_WITH_VIDEOHWACCEL
657 , bool bAccelerate2DVideo
658#endif /* VBOX_WITH_VIDEOHWACCEL */
659 )
660 : QAbstractScrollArea(pMachineWindow->centralWidget())
661 , m_pMachineWindow(pMachineWindow)
662 , m_uScreenId(uScreenId)
663 , m_pFrameBuffer(0)
664 , m_previousState(KMachineState_Null)
665 , m_iHostScreenNumber(0)
666 , m_maxGuestSizePolicy(MaxGuestResolutionPolicy_Automatic)
667 , m_u64MaxGuestSize(0)
668#ifdef VBOX_WITH_VIDEOHWACCEL
669 , m_fAccelerate2DVideo(bAccelerate2DVideo)
670#endif /* VBOX_WITH_VIDEOHWACCEL */
671#ifdef VBOX_WITH_DRAG_AND_DROP_GH
672 , m_fIsDraggingFromGuest(false)
673#endif
674 , m_pNativeEventFilter(0)
675{
676}
677
678void UIMachineView::loadMachineViewSettings()
679{
680 /* Global settings: */
681 {
682 /* Remember the maximum guest size policy for
683 * telling the guest about video modes we like: */
684 m_maxGuestSizePolicy = gEDataManager->maxGuestResolutionPolicy();
685 if (m_maxGuestSizePolicy == MaxGuestResolutionPolicy_Fixed)
686 m_fixedMaxGuestSize = gEDataManager->maxGuestResolutionForPolicyFixed();
687 }
688}
689
690void UIMachineView::prepareViewport()
691{
692 /* Prepare viewport: */
693 AssertPtrReturnVoid(viewport());
694 {
695 /* Enable manual painting: */
696 viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
697 /* Enable multi-touch support: */
698 viewport()->setAttribute(Qt::WA_AcceptTouchEvents);
699 }
700}
701
702void UIMachineView::prepareFrameBuffer()
703{
704 /* Check whether we already have corresponding frame-buffer: */
705 UIFrameBuffer *pFrameBuffer = uisession()->frameBuffer(screenId());
706
707 /* If we do: */
708 if (pFrameBuffer)
709 {
710 /* Assign it's view: */
711 pFrameBuffer->setView(this);
712 /* Mark frame-buffer as used again: */
713 LogRelFlow(("GUI: UIMachineView::prepareFrameBuffer: Start EMT callbacks accepting for screen: %d\n", screenId()));
714 pFrameBuffer->setMarkAsUnused(false);
715 /* And remember our choice: */
716 m_pFrameBuffer = pFrameBuffer;
717 }
718 /* If we do not: */
719 else
720 {
721#ifdef VBOX_WITH_VIDEOHWACCEL
722 /* Create new frame-buffer: */
723 m_pFrameBuffer = new UIFrameBuffer(m_fAccelerate2DVideo);
724 m_pFrameBuffer->init(this);
725#else /* VBOX_WITH_VIDEOHWACCEL */
726 /* Create new frame-buffer: */
727 m_pFrameBuffer = new UIFrameBuffer;
728 m_pFrameBuffer->init(this);
729#endif /* !VBOX_WITH_VIDEOHWACCEL */
730
731 /* Take scaling optimization type into account: */
732 m_pFrameBuffer->setScalingOptimizationType(gEDataManager->scalingOptimizationType(uiCommon().managedVMUuid()));
733
734 /* Acquire selected scale-factor: */
735 double dScaleFactor = gEDataManager->scaleFactor(uiCommon().managedVMUuid(), m_uScreenId);
736
737 /* Take the device-pixel-ratio into account: */
738 const double dDevicePixelRatioFormal = gpDesktop->devicePixelRatio(machineWindow());
739 const double dDevicePixelRatioActual = gpDesktop->devicePixelRatioActual(machineWindow());
740 const bool fUseUnscaledHiDPIOutput = dScaleFactor != dDevicePixelRatioActual;
741 dScaleFactor = fUseUnscaledHiDPIOutput ? dScaleFactor : 1.0;
742
743 /* Assign frame-buffer with new values: */
744 m_pFrameBuffer->setDevicePixelRatio(dDevicePixelRatioFormal);
745 m_pFrameBuffer->setDevicePixelRatioActual(dDevicePixelRatioActual);
746 m_pFrameBuffer->setScaleFactor(dScaleFactor);
747 m_pFrameBuffer->setUseUnscaledHiDPIOutput(fUseUnscaledHiDPIOutput);
748
749 /* Propagate the scale-factor related attributes to 3D service if necessary: */
750 if (machine().GetGraphicsAdapter().GetAccelerate3DEnabled() && uiCommon().is3DAvailable())
751 {
752 double dScaleFactorFor3D = dScaleFactor;
753#if defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
754 // WORKAROUND:
755 // On Windows and Linux opposing to macOS it's only Qt which can auto scale up,
756 // not 3D overlay itself, so for auto scale-up mode we have to take that into account.
757 if (!fUseUnscaledHiDPIOutput)
758 dScaleFactorFor3D *= dDevicePixelRatioActual;
759#endif /* VBOX_WS_WIN || VBOX_WS_X11 */
760 display().NotifyScaleFactorChange(m_uScreenId,
761 (uint32_t)(dScaleFactorFor3D * VBOX_OGL_SCALE_FACTOR_MULTIPLIER),
762 (uint32_t)(dScaleFactorFor3D * VBOX_OGL_SCALE_FACTOR_MULTIPLIER));
763 display().NotifyHiDPIOutputPolicyChange(fUseUnscaledHiDPIOutput);
764 }
765
766 /* Perform frame-buffer rescaling: */
767 m_pFrameBuffer->performRescale();
768
769 /* Associate uisession with frame-buffer finally: */
770 uisession()->setFrameBuffer(screenId(), m_pFrameBuffer);
771 }
772
773 /* Make sure frame-buffer was prepared: */
774 AssertReturnVoid(m_pFrameBuffer);
775
776 /* Reattach to IDisplay: */
777 m_pFrameBuffer->detach();
778 m_pFrameBuffer->attach();
779
780 /* Calculate frame-buffer size: */
781 QSize size;
782 {
783#ifdef VBOX_WS_X11
784 /* Processing pseudo resize-event to synchronize frame-buffer with stored framebuffer size.
785 * On X11 this will be additional done when the machine state was 'saved'. */
786 if (machine().GetState() == KMachineState_Saved)
787 size = guestScreenSizeHint();
788#endif /* VBOX_WS_X11 */
789
790 /* If there is a preview image saved,
791 * we will resize the framebuffer to the size of that image: */
792 ULONG uWidth = 0, uHeight = 0;
793 QVector<KBitmapFormat> formats = machine().QuerySavedScreenshotInfo(0, uWidth, uHeight);
794 if (formats.size() > 0)
795 {
796 /* Init with the screenshot size: */
797 size = QSize(uWidth, uHeight);
798 /* Try to get the real guest dimensions from the save-state: */
799 ULONG uGuestOriginX = 0, uGuestOriginY = 0, uGuestWidth = 0, uGuestHeight = 0;
800 BOOL fEnabled = true;
801 machine().QuerySavedGuestScreenInfo(m_uScreenId, uGuestOriginX, uGuestOriginY, uGuestWidth, uGuestHeight, fEnabled);
802 if (uGuestWidth > 0 && uGuestHeight > 0)
803 size = QSize(uGuestWidth, uGuestHeight);
804 }
805
806 /* If we have a valid size, resize/rescale the frame-buffer. */
807 if (size.width() > 0 && size.height() > 0)
808 {
809 frameBuffer()->performResize(size.width(), size.height());
810 frameBuffer()->performRescale();
811 }
812 }
813}
814
815void UIMachineView::prepareCommon()
816{
817 /* Prepare view frame: */
818 setFrameStyle(QFrame::NoFrame);
819
820 /* Setup palette: */
821 QPalette palette(viewport()->palette());
822 palette.setColor(viewport()->backgroundRole(), Qt::black);
823 viewport()->setPalette(palette);
824
825 /* Setup focus policy: */
826 setFocusPolicy(Qt::WheelFocus);
827
828#ifdef VBOX_WITH_DRAG_AND_DROP
829 /* Enable drag & drop: */
830 setAcceptDrops(true);
831
832 /* Create the drag and drop handler instance: */
833 m_pDnDHandler = new UIDnDHandler(uisession(), this /* pParent */);
834#endif /* VBOX_WITH_DRAG_AND_DROP */
835}
836
837void UIMachineView::prepareFilters()
838{
839 /* Enable MouseMove events: */
840 viewport()->setMouseTracking(true);
841
842 /* We have to watch for own events too: */
843 installEventFilter(this);
844
845 /* QScrollView does the below on its own, but let's
846 * do it anyway for the case it will not do it in the future: */
847 viewport()->installEventFilter(this);
848
849 /* We want to be notified on some parent's events: */
850 machineWindow()->installEventFilter(this);
851}
852
853void UIMachineView::prepareConnections()
854{
855 /* Desktop resolution change (e.g. monitor hotplug): */
856 connect(gpDesktop, &UIDesktopWidgetWatchdog::sigHostScreenResized,
857 this, &UIMachineView::sltDesktopResized);
858 /* Scale-factor change: */
859 connect(gEDataManager, &UIExtraDataManager::sigScaleFactorChange,
860 this, &UIMachineView::sltHandleScaleFactorChange);
861 /* Scaling-optimization change: */
862 connect(gEDataManager, &UIExtraDataManager::sigScalingOptimizationTypeChange,
863 this, &UIMachineView::sltHandleScalingOptimizationChange);
864}
865
866void UIMachineView::prepareConsoleConnections()
867{
868 /* Machine state-change updater: */
869 connect(uisession(), &UISession::sigMachineStateChange, this, &UIMachineView::sltMachineStateChanged);
870 /* Mouse pointer shape updater: */
871 connect(uisession(), &UISession::sigMousePointerShapeChange, this, &UIMachineView::sltMousePointerShapeChange);
872}
873
874void UIMachineView::cleanupFrameBuffer()
875{
876 /* Make sure proper framebuffer assigned: */
877 AssertReturnVoid(m_pFrameBuffer);
878 AssertReturnVoid(m_pFrameBuffer == uisession()->frameBuffer(screenId()));
879
880 /* Mark framebuffer as unused: */
881 LogRelFlow(("GUI: UIMachineView::cleanupFrameBuffer: Stop EMT callbacks accepting for screen: %d\n", screenId()));
882 m_pFrameBuffer->setMarkAsUnused(true);
883
884 /* Process pending framebuffer events: */
885 QApplication::sendPostedEvents(this, QEvent::MetaCall);
886
887#ifdef VBOX_WITH_VIDEOHWACCEL
888 if (m_fAccelerate2DVideo)
889 QApplication::sendPostedEvents(this, VHWACommandProcessType);
890#endif /* VBOX_WITH_VIDEOHWACCEL */
891
892 /* Temporarily detach the framebuffer from IDisplay before detaching
893 * from view in order to respect the thread synchonisation logic (see UIFrameBuffer.h).
894 * Note: VBOX_WITH_CROGL additionally requires us to call DetachFramebuffer
895 * to ensure 3D gets notified of view being destroyed... */
896 if (console().isOk() && !display().isNull())
897 m_pFrameBuffer->detach();
898
899 /* Detach framebuffer from view: */
900 m_pFrameBuffer->setView(NULL);
901}
902
903void UIMachineView::cleanupNativeFilters()
904{
905 /* If native event filter exists: */
906 if (m_pNativeEventFilter)
907 {
908 /* Uninstall/destroy existing native event filter: */
909 qApp->removeNativeEventFilter(m_pNativeEventFilter);
910 delete m_pNativeEventFilter;
911 m_pNativeEventFilter = 0;
912 }
913}
914
915UISession* UIMachineView::uisession() const
916{
917 return machineWindow()->uisession();
918}
919
920CSession& UIMachineView::session() const
921{
922 return uisession()->session();
923}
924
925CMachine& UIMachineView::machine() const
926{
927 return uisession()->machine();
928}
929
930CConsole& UIMachineView::console() const
931{
932 return uisession()->console();
933}
934
935CDisplay& UIMachineView::display() const
936{
937 return uisession()->display();
938}
939
940CGuest& UIMachineView::guest() const
941{
942 return uisession()->guest();
943}
944
945UIActionPool* UIMachineView::actionPool() const
946{
947 return machineWindow()->actionPool();
948}
949
950UIMachineLogic* UIMachineView::machineLogic() const
951{
952 return machineWindow()->machineLogic();
953}
954
955QSize UIMachineView::sizeHint() const
956{
957 /* Temporarily restrict the size to prevent a brief resize to the
958 * frame-buffer dimensions when we exit full-screen. This is only
959 * applied if the frame-buffer is at full-screen dimensions and
960 * until the first machine view resize. */
961
962 /* Get the frame-buffer dimensions: */
963 QSize frameBufferSize(frameBuffer()->width(), frameBuffer()->height());
964 /* Take the scale-factor(s) into account: */
965 frameBufferSize = scaledForward(frameBufferSize);
966 /* Check against the last full-screen size. */
967 if (frameBufferSize == uisession()->lastFullScreenSize(screenId()) && m_sizeHintOverride.isValid())
968 return m_sizeHintOverride;
969
970 /* Get frame-buffer size-hint: */
971 QSize size(m_pFrameBuffer->width(), m_pFrameBuffer->height());
972
973 /* Take the scale-factor(s) into account: */
974 size = scaledForward(size);
975
976#ifdef VBOX_WITH_DEBUGGER_GUI
977 /// @todo Fix all DEBUGGER stuff!
978 /* HACK ALERT! Really ugly workaround for the resizing to 9x1 done by DevVGA if provoked before power on. */
979 if (size.width() < 16 || size.height() < 16)
980 if (uiCommon().shouldStartPaused() || uiCommon().isDebuggerAutoShowEnabled())
981 size = QSize(640, 480);
982#endif /* !VBOX_WITH_DEBUGGER_GUI */
983
984 /* Return the resulting size-hint: */
985 return QSize(size.width() + frameWidth() * 2, size.height() + frameWidth() * 2);
986}
987
988int UIMachineView::contentsX() const
989{
990 return horizontalScrollBar()->value();
991}
992
993int UIMachineView::contentsY() const
994{
995 return verticalScrollBar()->value();
996}
997
998int UIMachineView::contentsWidth() const
999{
1000 return m_pFrameBuffer->width();
1001}
1002
1003int UIMachineView::contentsHeight() const
1004{
1005 return m_pFrameBuffer->height();
1006}
1007
1008int UIMachineView::visibleWidth() const
1009{
1010 return horizontalScrollBar()->pageStep();
1011}
1012
1013int UIMachineView::visibleHeight() const
1014{
1015 return verticalScrollBar()->pageStep();
1016}
1017
1018void UIMachineView::setMaxGuestSize(const QSize &minimumSizeHint /* = QSize() */)
1019{
1020 QSize maxSize;
1021 switch (m_maxGuestSizePolicy)
1022 {
1023 case MaxGuestResolutionPolicy_Fixed:
1024 maxSize = m_fixedMaxGuestSize;
1025 break;
1026 case MaxGuestResolutionPolicy_Automatic:
1027 maxSize = calculateMaxGuestSize().expandedTo(minimumSizeHint);
1028 break;
1029 case MaxGuestResolutionPolicy_Any:
1030 /* (0, 0) means any of course. */
1031 maxSize = QSize(0, 0);
1032 }
1033 ASMAtomicWriteU64(&m_u64MaxGuestSize,
1034 RT_MAKE_U64(maxSize.height(), maxSize.width()));
1035}
1036
1037QSize UIMachineView::maxGuestSize()
1038{
1039 uint64_t u64Size = ASMAtomicReadU64(&m_u64MaxGuestSize);
1040 return QSize(int(RT_HI_U32(u64Size)), int(RT_LO_U32(u64Size)));
1041}
1042
1043bool UIMachineView::guestScreenVisibilityStatus() const
1044{
1045 /* Always 'true' for primary guest-screen: */
1046 if (m_uScreenId == 0)
1047 return true;
1048
1049 /* Actual value for other guest-screens: */
1050 return gEDataManager->lastGuestScreenVisibilityStatus(m_uScreenId, uiCommon().managedVMUuid());
1051}
1052
1053QSize UIMachineView::guestScreenSizeHint() const
1054{
1055 /* Load guest-screen size-hint: */
1056 QSize sizeHint = gEDataManager->lastGuestScreenSizeHint(m_uScreenId, uiCommon().managedVMUuid());
1057
1058 /* Invent the default if necessary: */
1059 if (!sizeHint.isValid())
1060 sizeHint = QSize(800, 600);
1061
1062 /* Take the scale-factor(s) into account: */
1063 sizeHint = scaledForward(sizeHint);
1064
1065 /* Return size-hint: */
1066 return sizeHint;
1067}
1068
1069void UIMachineView::storeGuestSizeHint(const QSize &sizeHint)
1070{
1071 /* Save guest-screen size-hint: */
1072 LogRel2(("GUI: UIMachineView::storeGuestSizeHint: Storing guest-screen size-hint for screen %d as %dx%d\n",
1073 (int)screenId(), sizeHint.width(), sizeHint.height()));
1074 gEDataManager->setLastGuestScreenSizeHint(m_uScreenId, sizeHint, uiCommon().managedVMUuid());
1075}
1076
1077void UIMachineView::handleScaleChange()
1078{
1079 LogRel(("GUI: UIMachineView::handleScaleChange: Screen=%d\n",
1080 (unsigned long)m_uScreenId));
1081
1082 /* If machine-window is visible: */
1083 if (uisession()->isScreenVisible(m_uScreenId))
1084 {
1085 /* For 'scale' mode: */
1086 if (visualStateType() == UIVisualStateType_Scale)
1087 {
1088 /* Assign new frame-buffer logical-size: */
1089 QSize scaledSize = size();
1090 const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
1091 const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
1092 scaledSize *= dDevicePixelRatioFormal;
1093 if (!frameBuffer()->useUnscaledHiDPIOutput())
1094 scaledSize /= dDevicePixelRatioActual;
1095 frameBuffer()->setScaledSize(scaledSize);
1096 }
1097 /* For other than 'scale' mode: */
1098 else
1099 {
1100 /* Adjust maximum-size restriction for machine-view: */
1101 setMaximumSize(sizeHint());
1102
1103 /* Force machine-window update own layout: */
1104 QCoreApplication::sendPostedEvents(0, QEvent::LayoutRequest);
1105
1106 /* Update machine-view sliders: */
1107 updateSliders();
1108
1109 /* By some reason Win host forgets to update machine-window central-widget
1110 * after main-layout was updated, let's do it for all the hosts: */
1111 machineWindow()->centralWidget()->update();
1112
1113 /* Normalize 'normal' machine-window geometry: */
1114 if (visualStateType() == UIVisualStateType_Normal)
1115 machineWindow()->normalizeGeometry(true /* adjust position */);
1116 }
1117
1118 /* Perform frame-buffer rescaling: */
1119 frameBuffer()->performRescale();
1120 }
1121
1122 LogRelFlow(("GUI: UIMachineView::handleScaleChange: Complete for Screen=%d\n",
1123 (unsigned long)m_uScreenId));
1124}
1125
1126void UIMachineView::resetPausePixmap()
1127{
1128 /* Reset pixmap(s): */
1129 m_pausePixmap = QPixmap();
1130 m_pausePixmapScaled = QPixmap();
1131}
1132
1133void UIMachineView::takePausePixmapLive()
1134{
1135 /* Prepare a screen-shot: */
1136 QImage screenShot = QImage(m_pFrameBuffer->width(), m_pFrameBuffer->height(), QImage::Format_RGB32);
1137 /* Which will be a 'black image' by default. */
1138 screenShot.fill(0);
1139
1140 /* For separate process: */
1141 if (uiCommon().isSeparateProcess())
1142 {
1143 /* Take screen-data to array: */
1144 const QVector<BYTE> screenData = display().TakeScreenShotToArray(screenId(), screenShot.width(), screenShot.height(), KBitmapFormat_BGR0);
1145 /* And copy that data to screen-shot if it is Ok: */
1146 if (display().isOk() && !screenData.isEmpty())
1147 memcpy(screenShot.bits(), screenData.data(), screenShot.width() * screenShot.height() * 4);
1148 }
1149 /* For the same process: */
1150 else
1151 {
1152 /* Take the screen-shot directly: */
1153 display().TakeScreenShot(screenId(), screenShot.bits(), screenShot.width(), screenShot.height(), KBitmapFormat_BGR0);
1154 }
1155
1156 /* Take the device-pixel-ratio into account: */
1157 const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
1158 if (!frameBuffer()->useUnscaledHiDPIOutput() && dDevicePixelRatioActual != 1.0)
1159 screenShot = screenShot.scaled(screenShot.size() * dDevicePixelRatioActual,
1160 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
1161
1162 /* Dim screen-shot if it is Ok: */
1163 if (display().isOk() && !screenShot.isNull())
1164 dimImage(screenShot);
1165
1166 /* Finally copy the screen-shot to pause-pixmap: */
1167 m_pausePixmap = QPixmap::fromImage(screenShot);
1168
1169 /* Take the device-pixel-ratio into account: */
1170 m_pausePixmap.setDevicePixelRatio(frameBuffer()->devicePixelRatio());
1171
1172 /* Update scaled pause pixmap: */
1173 updateScaledPausePixmap();
1174}
1175
1176void UIMachineView::takePausePixmapSnapshot()
1177{
1178 /* Acquire the screen-data from the saved-state: */
1179 ULONG uWidth = 0, uHeight = 0;
1180 const QVector<BYTE> screenData = machine().ReadSavedScreenshotToArray(0, KBitmapFormat_PNG, uWidth, uHeight);
1181
1182 /* Make sure there is saved-state screen-data: */
1183 if (screenData.isEmpty())
1184 return;
1185
1186 /* Acquire the screen-data properties from the saved-state: */
1187 ULONG uGuestOriginX = 0, uGuestOriginY = 0, uGuestWidth = 0, uGuestHeight = 0;
1188 BOOL fEnabled = true;
1189 machine().QuerySavedGuestScreenInfo(m_uScreenId, uGuestOriginX, uGuestOriginY, uGuestWidth, uGuestHeight, fEnabled);
1190
1191 /* Calculate effective size: */
1192 QSize effectiveSize = uGuestWidth > 0 ? QSize(uGuestWidth, uGuestHeight) : guestScreenSizeHint();
1193
1194 /* Take the device-pixel-ratio into account: */
1195 const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
1196 if (!frameBuffer()->useUnscaledHiDPIOutput() && dDevicePixelRatioActual != 1.0)
1197 effectiveSize *= dDevicePixelRatioActual;
1198
1199 /* Create a screen-shot on the basis of the screen-data we have in saved-state: */
1200 QImage screenShot = QImage::fromData(screenData.data(), screenData.size(), "PNG").scaled(effectiveSize);
1201
1202 /* Dim screen-shot if it is Ok: */
1203 if (machine().isOk() && !screenShot.isNull())
1204 dimImage(screenShot);
1205
1206 /* Finally copy the screen-shot to pause-pixmap: */
1207 m_pausePixmap = QPixmap::fromImage(screenShot);
1208
1209 /* Take the device-pixel-ratio into account: */
1210 m_pausePixmap.setDevicePixelRatio(frameBuffer()->devicePixelRatio());
1211
1212 /* Update scaled pause pixmap: */
1213 updateScaledPausePixmap();
1214}
1215
1216void UIMachineView::updateScaledPausePixmap()
1217{
1218 /* Make sure pause pixmap is not null: */
1219 if (pausePixmap().isNull())
1220 return;
1221
1222 /* Make sure scaled-size is not null: */
1223 QSize scaledSize = frameBuffer()->scaledSize();
1224 if (!scaledSize.isValid())
1225 return;
1226
1227 /* Take the device-pixel-ratio into account: */
1228 const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
1229 if (!frameBuffer()->useUnscaledHiDPIOutput() && dDevicePixelRatioActual != 1.0)
1230 scaledSize *= dDevicePixelRatioActual;
1231
1232 /* Update pause pixmap finally: */
1233 m_pausePixmapScaled = pausePixmap().scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
1234
1235 /* Take the device-pixel-ratio into account: */
1236 m_pausePixmapScaled.setDevicePixelRatio(frameBuffer()->devicePixelRatio());
1237}
1238
1239void UIMachineView::updateSliders()
1240{
1241 /* Get current viewport size: */
1242 QSize curViewportSize = viewport()->size();
1243 /* Get maximum viewport size: */
1244 const QSize maxViewportSize = maximumViewportSize();
1245 /* Get current frame-buffer size: */
1246 QSize frameBufferSize = QSize(frameBuffer()->width(), frameBuffer()->height());
1247
1248 /* Take the scale-factor(s) into account: */
1249 frameBufferSize = scaledForward(frameBufferSize);
1250
1251 /* If maximum viewport size can cover whole frame-buffer => no scroll-bars required: */
1252 if (maxViewportSize.expandedTo(frameBufferSize) == maxViewportSize)
1253 curViewportSize = maxViewportSize;
1254
1255 /* What length we want scroll-bars of? */
1256 int xRange = frameBufferSize.width() - curViewportSize.width();
1257 int yRange = frameBufferSize.height() - curViewportSize.height();
1258
1259 /* Take the device-pixel-ratio into account: */
1260 const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
1261 const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
1262 xRange *= dDevicePixelRatioFormal;
1263 yRange *= dDevicePixelRatioFormal;
1264 if (!frameBuffer()->useUnscaledHiDPIOutput())
1265 {
1266 xRange /= dDevicePixelRatioActual;
1267 yRange /= dDevicePixelRatioActual;
1268 }
1269
1270 /* Configure scroll-bars: */
1271 horizontalScrollBar()->setRange(0, xRange);
1272 verticalScrollBar()->setRange(0, yRange);
1273 horizontalScrollBar()->setPageStep(curViewportSize.width());
1274 verticalScrollBar()->setPageStep(curViewportSize.height());
1275}
1276
1277QPoint UIMachineView::viewportToContents(const QPoint &vp) const
1278{
1279 /* Get physical contents shifts of scroll-bars: */
1280 int iContentsX = contentsX();
1281 int iContentsY = contentsY();
1282
1283 /* Take the device-pixel-ratio into account: */
1284 const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
1285 const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
1286 if (!frameBuffer()->useUnscaledHiDPIOutput())
1287 {
1288 iContentsX *= dDevicePixelRatioActual;
1289 iContentsY *= dDevicePixelRatioActual;
1290 }
1291 iContentsX /= dDevicePixelRatioFormal;
1292 iContentsY /= dDevicePixelRatioFormal;
1293
1294 /* Return point shifted according scroll-bars: */
1295 return QPoint(vp.x() + iContentsX, vp.y() + iContentsY);
1296}
1297
1298void UIMachineView::scrollBy(int dx, int dy)
1299{
1300 horizontalScrollBar()->setValue(horizontalScrollBar()->value() + dx);
1301 verticalScrollBar()->setValue(verticalScrollBar()->value() + dy);
1302}
1303
1304void UIMachineView::dimImage(QImage &img)
1305{
1306 for (int y = 0; y < img.height(); ++ y)
1307 {
1308 if (y % 2)
1309 {
1310 if (img.depth() == 32)
1311 {
1312 for (int x = 0; x < img.width(); ++ x)
1313 {
1314 int gray = qGray(img.pixel (x, y)) / 2;
1315 img.setPixel(x, y, qRgb (gray, gray, gray));
1316 }
1317 }
1318 else
1319 {
1320 ::memset(img.scanLine (y), 0, img.bytesPerLine());
1321 }
1322 }
1323 else
1324 {
1325 if (img.depth() == 32)
1326 {
1327 for (int x = 0; x < img.width(); ++ x)
1328 {
1329 int gray = (2 * qGray (img.pixel (x, y))) / 3;
1330 img.setPixel(x, y, qRgb (gray, gray, gray));
1331 }
1332 }
1333 }
1334 }
1335}
1336
1337void UIMachineView::scrollContentsBy(int dx, int dy)
1338{
1339#ifdef VBOX_WITH_VIDEOHWACCEL
1340 if (m_pFrameBuffer)
1341 {
1342 m_pFrameBuffer->viewportScrolled(dx, dy);
1343 }
1344#endif /* VBOX_WITH_VIDEOHWACCEL */
1345 QAbstractScrollArea::scrollContentsBy(dx, dy);
1346
1347 /* Update console's display viewport and 3D overlay: */
1348 updateViewport();
1349}
1350
1351#ifdef VBOX_WS_MAC
1352void UIMachineView::updateDockIcon()
1353{
1354 machineLogic()->updateDockIcon();
1355}
1356
1357CGImageRef UIMachineView::vmContentImage()
1358{
1359 /* Use pause-image if exists: */
1360 if (!pausePixmap().isNull())
1361 return darwinToCGImageRef(&pausePixmap());
1362
1363 /* Create the image ref out of the frame-buffer: */
1364 return frameBuffertoCGImageRef(m_pFrameBuffer);
1365}
1366
1367CGImageRef UIMachineView::frameBuffertoCGImageRef(UIFrameBuffer *pFrameBuffer)
1368{
1369 CGImageRef ir = 0;
1370 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
1371 if (cs)
1372 {
1373 /* Create the image copy of the framebuffer */
1374 CGDataProviderRef dp = CGDataProviderCreateWithData(pFrameBuffer, pFrameBuffer->address(), pFrameBuffer->bitsPerPixel() / 8 * pFrameBuffer->width() * pFrameBuffer->height(), NULL);
1375 if (dp)
1376 {
1377 ir = CGImageCreate(pFrameBuffer->width(), pFrameBuffer->height(), 8, 32, pFrameBuffer->bytesPerLine(), cs,
1378 kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host, dp, 0, false,
1379 kCGRenderingIntentDefault);
1380 CGDataProviderRelease(dp);
1381 }
1382 CGColorSpaceRelease(cs);
1383 }
1384 return ir;
1385}
1386#endif /* VBOX_WS_MAC */
1387
1388UIVisualStateType UIMachineView::visualStateType() const
1389{
1390 return machineLogic()->visualStateType();
1391}
1392
1393bool UIMachineView::isFullscreenOrSeamless() const
1394{
1395 return visualStateType() == UIVisualStateType_Fullscreen
1396 || visualStateType() == UIVisualStateType_Seamless;
1397}
1398
1399bool UIMachineView::event(QEvent *pEvent)
1400{
1401 switch ((UIEventType)pEvent->type())
1402 {
1403#ifdef VBOX_WS_MAC
1404 /* Event posted OnShowWindow: */
1405 case ShowWindowEventType:
1406 {
1407 /* Dunno what Qt3 thinks a window that has minimized to the dock should be - it is not hidden,
1408 * neither is it minimized. OTOH it is marked shown and visible, but not activated.
1409 * This latter isn't of much help though, since at this point nothing is marked activated.
1410 * I might have overlooked something, but I'm buggered what if I know what. So, I'll just always
1411 * show & activate the stupid window to make it get out of the dock when the user wishes to show a VM: */
1412 window()->show();
1413 window()->activateWindow();
1414 return true;
1415 }
1416#endif /* VBOX_WS_MAC */
1417
1418#ifdef VBOX_WITH_VIDEOHWACCEL
1419 case VHWACommandProcessType:
1420 {
1421 m_pFrameBuffer->doProcessVHWACommand(pEvent);
1422 return true;
1423 }
1424#endif /* VBOX_WITH_VIDEOHWACCEL */
1425
1426 default:
1427 break;
1428 }
1429
1430 return QAbstractScrollArea::event(pEvent);
1431}
1432
1433bool UIMachineView::eventFilter(QObject *pWatched, QEvent *pEvent)
1434{
1435 if (pWatched == viewport())
1436 {
1437 switch (pEvent->type())
1438 {
1439 case QEvent::Resize:
1440 {
1441#ifdef VBOX_WITH_VIDEOHWACCEL
1442 QResizeEvent* pResizeEvent = static_cast<QResizeEvent*>(pEvent);
1443 if (m_pFrameBuffer)
1444 m_pFrameBuffer->viewportResized(pResizeEvent);
1445#endif /* VBOX_WITH_VIDEOHWACCEL */
1446 /* Update console's display viewport and 3D overlay: */
1447 updateViewport();
1448 break;
1449 }
1450 default:
1451 break;
1452 }
1453 }
1454
1455 if (pWatched == this)
1456 {
1457 switch (pEvent->type())
1458 {
1459 case QEvent::Move:
1460 {
1461 /* Update console's display viewport and 3D overlay: */
1462 updateViewport();
1463 break;
1464 }
1465 default:
1466 break;
1467 }
1468 }
1469
1470 if (pWatched == machineWindow())
1471 {
1472 switch (pEvent->type())
1473 {
1474 case QEvent::WindowStateChange:
1475 {
1476 /* During minimizing and state restoring machineWindow() gets
1477 * the focus which belongs to console view window, so returning it properly. */
1478 QWindowStateChangeEvent *pWindowEvent = static_cast<QWindowStateChangeEvent*>(pEvent);
1479 if (pWindowEvent->oldState() & Qt::WindowMinimized)
1480 {
1481 if (QApplication::focusWidget())
1482 {
1483 QApplication::focusWidget()->clearFocus();
1484 qApp->processEvents();
1485 }
1486 QTimer::singleShot(0, this, SLOT(setFocus()));
1487 }
1488 break;
1489 }
1490 case QEvent::Move:
1491 {
1492 /* Get current host-screen number: */
1493 const int iCurrentHostScreenNumber = gpDesktop->screenNumber(this);
1494 if (m_iHostScreenNumber != iCurrentHostScreenNumber)
1495 {
1496 /* Recache current host screen: */
1497 m_iHostScreenNumber = iCurrentHostScreenNumber;
1498 /* Reapply machine-view scale-factor if necessary: */
1499 applyMachineViewScaleFactor();
1500 /* For 'normal'/'scaled' visual state type: */
1501 if ( visualStateType() == UIVisualStateType_Normal
1502 || visualStateType() == UIVisualStateType_Scale)
1503 {
1504 /* Make sure action-pool is of 'runtime' type: */
1505 UIActionPoolRuntime *pActionPool = actionPool() && actionPool()->toRuntime() ? actionPool()->toRuntime() : 0;
1506 AssertPtr(pActionPool);
1507 if (pActionPool)
1508 {
1509 /* Inform action-pool about current guest-to-host screen mapping: */
1510 QMap<int, int> screenMap = pActionPool->hostScreenForGuestScreenMap();
1511 screenMap[m_uScreenId] = m_iHostScreenNumber;
1512 pActionPool->setHostScreenForGuestScreenMap(screenMap);
1513 }
1514 }
1515 }
1516 break;
1517 }
1518 default:
1519 break;
1520 }
1521 }
1522
1523 return QAbstractScrollArea::eventFilter(pWatched, pEvent);
1524}
1525
1526void UIMachineView::resizeEvent(QResizeEvent *pEvent)
1527{
1528 updateSliders();
1529 return QAbstractScrollArea::resizeEvent(pEvent);
1530}
1531
1532void UIMachineView::moveEvent(QMoveEvent *pEvent)
1533{
1534 return QAbstractScrollArea::moveEvent(pEvent);
1535}
1536
1537void UIMachineView::paintEvent(QPaintEvent *pPaintEvent)
1538{
1539 /* Use pause-image if exists: */
1540 if (!pausePixmap().isNull())
1541 {
1542 /* We have a snapshot for the paused state: */
1543 QRect rect = pPaintEvent->rect().intersected(viewport()->rect());
1544 QPainter painter(viewport());
1545 /* Take the scale-factor into account: */
1546 if (frameBuffer()->scaleFactor() == 1.0 && !frameBuffer()->scaledSize().isValid())
1547 painter.drawPixmap(rect.topLeft(), pausePixmap());
1548 else
1549 painter.drawPixmap(rect.topLeft(), pausePixmapScaled());
1550#ifdef VBOX_WS_MAC
1551 /* Update the dock icon: */
1552 updateDockIcon();
1553#endif /* VBOX_WS_MAC */
1554 return;
1555 }
1556
1557 /* Delegate the paint function to the UIFrameBuffer interface: */
1558 if (m_pFrameBuffer)
1559 m_pFrameBuffer->handlePaintEvent(pPaintEvent);
1560#ifdef VBOX_WS_MAC
1561 /* Update the dock icon if we are in the running state: */
1562 if (uisession()->isRunning())
1563 updateDockIcon();
1564#endif /* VBOX_WS_MAC */
1565}
1566
1567void UIMachineView::focusInEvent(QFocusEvent *pEvent)
1568{
1569 /* Call to base-class: */
1570 QAbstractScrollArea::focusInEvent(pEvent);
1571
1572 /* If native event filter isn't exists: */
1573 if (!m_pNativeEventFilter)
1574 {
1575 /* Create/install new native event filter: */
1576 m_pNativeEventFilter = new UINativeEventFilter(this);
1577 qApp->installNativeEventFilter(m_pNativeEventFilter);
1578 }
1579}
1580
1581void UIMachineView::focusOutEvent(QFocusEvent *pEvent)
1582{
1583 /* If native event filter exists: */
1584 if (m_pNativeEventFilter)
1585 {
1586 /* Uninstall/destroy existing native event filter: */
1587 qApp->removeNativeEventFilter(m_pNativeEventFilter);
1588 delete m_pNativeEventFilter;
1589 m_pNativeEventFilter = 0;
1590 }
1591
1592 /* Call to base-class: */
1593 QAbstractScrollArea::focusOutEvent(pEvent);
1594}
1595
1596#ifdef VBOX_WITH_DRAG_AND_DROP
1597
1598bool UIMachineView::dragAndDropCanAccept(void) const
1599{
1600 bool fAccept = m_pDnDHandler
1601# ifdef VBOX_WITH_DRAG_AND_DROP_GH
1602 && !m_fIsDraggingFromGuest
1603# endif
1604 && machine().GetDnDMode() != KDnDMode_Disabled;
1605 return fAccept;
1606}
1607
1608bool UIMachineView::dragAndDropIsActive(void) const
1609{
1610 return ( m_pDnDHandler
1611 && machine().GetDnDMode() != KDnDMode_Disabled);
1612}
1613
1614void UIMachineView::dragEnterEvent(QDragEnterEvent *pEvent)
1615{
1616 AssertPtrReturnVoid(pEvent);
1617
1618 int rc = dragAndDropCanAccept() ? VINF_SUCCESS : VERR_ACCESS_DENIED;
1619 if (RT_SUCCESS(rc))
1620 {
1621 /* Get mouse-pointer location. */
1622 const QPoint &cpnt = viewportToContents(pEvent->pos());
1623
1624 /* Ask the target for starting a DnD event. */
1625 Qt::DropAction result = m_pDnDHandler->dragEnter(screenId(),
1626 frameBuffer()->convertHostXTo(cpnt.x()),
1627 frameBuffer()->convertHostYTo(cpnt.y()),
1628 pEvent->proposedAction(),
1629 pEvent->possibleActions(),
1630 pEvent->mimeData());
1631
1632 /* Set the DnD action returned by the guest. */
1633 pEvent->setDropAction(result);
1634 pEvent->accept();
1635 }
1636
1637 DNDDEBUG(("DnD: dragEnterEvent ended with rc=%Rrc\n", rc));
1638}
1639
1640void UIMachineView::dragMoveEvent(QDragMoveEvent *pEvent)
1641{
1642 AssertPtrReturnVoid(pEvent);
1643
1644 int rc = dragAndDropCanAccept() ? VINF_SUCCESS : VERR_ACCESS_DENIED;
1645 if (RT_SUCCESS(rc))
1646 {
1647 /* Get mouse-pointer location. */
1648 const QPoint &cpnt = viewportToContents(pEvent->pos());
1649
1650 /* Ask the guest for moving the drop cursor. */
1651 Qt::DropAction result = m_pDnDHandler->dragMove(screenId(),
1652 frameBuffer()->convertHostXTo(cpnt.x()),
1653 frameBuffer()->convertHostYTo(cpnt.y()),
1654 pEvent->proposedAction(),
1655 pEvent->possibleActions(),
1656 pEvent->mimeData());
1657
1658 /* Set the DnD action returned by the guest. */
1659 pEvent->setDropAction(result);
1660 pEvent->accept();
1661 }
1662
1663 DNDDEBUG(("DnD: dragMoveEvent ended with rc=%Rrc\n", rc));
1664}
1665
1666void UIMachineView::dragLeaveEvent(QDragLeaveEvent *pEvent)
1667{
1668 AssertPtrReturnVoid(pEvent);
1669
1670 int rc = dragAndDropCanAccept() ? VINF_SUCCESS : VERR_ACCESS_DENIED;
1671 if (RT_SUCCESS(rc))
1672 {
1673 m_pDnDHandler->dragLeave(screenId());
1674
1675 pEvent->accept();
1676 }
1677
1678 DNDDEBUG(("DnD: dragLeaveEvent ended with rc=%Rrc\n", rc));
1679}
1680
1681int UIMachineView::dragCheckPending(void)
1682{
1683 int rc;
1684
1685 if (!dragAndDropIsActive())
1686 rc = VERR_ACCESS_DENIED;
1687# ifdef VBOX_WITH_DRAG_AND_DROP_GH
1688 else if (!m_fIsDraggingFromGuest)
1689 {
1690 /// @todo Add guest->guest DnD functionality here by getting
1691 // the source of guest B (when copying from B to A).
1692 rc = m_pDnDHandler->dragCheckPending(screenId());
1693 if (RT_SUCCESS(rc))
1694 m_fIsDraggingFromGuest = true;
1695 }
1696 else /* Already dragging, so report success. */
1697 rc = VINF_SUCCESS;
1698# else
1699 rc = VERR_NOT_SUPPORTED;
1700# endif
1701
1702 DNDDEBUG(("DnD: dragCheckPending ended with rc=%Rrc\n", rc));
1703 return rc;
1704}
1705
1706int UIMachineView::dragStart(void)
1707{
1708 int rc;
1709
1710 if (!dragAndDropIsActive())
1711 rc = VERR_ACCESS_DENIED;
1712# ifdef VBOX_WITH_DRAG_AND_DROP_GH
1713 else if (!m_fIsDraggingFromGuest)
1714 rc = VERR_WRONG_ORDER;
1715 else
1716 {
1717 /// @todo Add guest->guest DnD functionality here by getting
1718 // the source of guest B (when copying from B to A).
1719 rc = m_pDnDHandler->dragStart(screenId());
1720
1721 m_fIsDraggingFromGuest = false;
1722 }
1723# else
1724 rc = VERR_NOT_SUPPORTED;
1725# endif
1726
1727 DNDDEBUG(("DnD: dragStart ended with rc=%Rrc\n", rc));
1728 return rc;
1729}
1730
1731int UIMachineView::dragStop(void)
1732{
1733 int rc;
1734
1735 if (!dragAndDropIsActive())
1736 rc = VERR_ACCESS_DENIED;
1737# ifdef VBOX_WITH_DRAG_AND_DROP_GH
1738 else if (!m_fIsDraggingFromGuest)
1739 rc = VERR_WRONG_ORDER;
1740 else
1741 rc = m_pDnDHandler->dragStop(screenId());
1742# else
1743 rc = VERR_NOT_SUPPORTED;
1744# endif
1745
1746 DNDDEBUG(("DnD: dragStop ended with rc=%Rrc\n", rc));
1747 return rc;
1748}
1749
1750void UIMachineView::dropEvent(QDropEvent *pEvent)
1751{
1752 AssertPtrReturnVoid(pEvent);
1753
1754 int rc = dragAndDropCanAccept() ? VINF_SUCCESS : VERR_ACCESS_DENIED;
1755 if (RT_SUCCESS(rc))
1756 {
1757 /* Get mouse-pointer location. */
1758 const QPoint &cpnt = viewportToContents(pEvent->pos());
1759
1760 /* Ask the guest for dropping data. */
1761 Qt::DropAction result = m_pDnDHandler->dragDrop(screenId(),
1762 frameBuffer()->convertHostXTo(cpnt.x()),
1763 frameBuffer()->convertHostYTo(cpnt.y()),
1764 pEvent->proposedAction(),
1765 pEvent->possibleActions(),
1766 pEvent->mimeData());
1767
1768 /* Set the DnD action returned by the guest. */
1769 pEvent->setDropAction(result);
1770 pEvent->accept();
1771 }
1772
1773 DNDDEBUG(("DnD: dropEvent ended with rc=%Rrc\n", rc));
1774}
1775
1776#endif /* VBOX_WITH_DRAG_AND_DROP */
1777
1778bool UIMachineView::nativeEventPreprocessor(const QByteArray &eventType, void *pMessage)
1779{
1780 /* Check if some event should be filtered out.
1781 * Returning @c true means filtering-out,
1782 * Returning @c false means passing event to Qt. */
1783
1784# if defined(VBOX_WS_MAC)
1785
1786 /* Make sure it's generic NSEvent: */
1787 if (eventType != "mac_generic_NSEvent")
1788 return false;
1789 EventRef event = static_cast<EventRef>(darwinCocoaToCarbonEvent(pMessage));
1790
1791 switch (::GetEventClass(event))
1792 {
1793 // Keep in mind that this stuff should not be enabled while we are still using
1794 // own native keyboard filter installed through cocoa API, to be reworked.
1795 // S.a. registerForNativeEvents call in UIKeyboardHandler implementation.
1796#if 0
1797 /* Watch for keyboard-events: */
1798 case kEventClassKeyboard:
1799 {
1800 switch (::GetEventKind(event))
1801 {
1802 /* Watch for key-events: */
1803 case kEventRawKeyDown:
1804 case kEventRawKeyRepeat:
1805 case kEventRawKeyUp:
1806 case kEventRawKeyModifiersChanged:
1807 {
1808 /* Delegate key-event handling to the keyboard-handler: */
1809 return machineLogic()->keyboardHandler()->nativeEventFilter(pMessage, screenId());
1810 }
1811 default:
1812 break;
1813 }
1814 break;
1815 }
1816#endif
1817 /* Watch for mouse-events: */
1818 case kEventClassMouse:
1819 {
1820 switch (::GetEventKind(event))
1821 {
1822 /* Watch for button-events: */
1823 case kEventMouseDown:
1824 case kEventMouseUp:
1825 {
1826 /* Delegate button-event handling to the mouse-handler: */
1827 return machineLogic()->mouseHandler()->nativeEventFilter(pMessage, screenId());
1828 }
1829 default:
1830 break;
1831 }
1832 break;
1833 }
1834 default:
1835 break;
1836 }
1837
1838# elif defined(VBOX_WS_WIN)
1839
1840 /* Make sure it's generic MSG event: */
1841 if (eventType != "windows_generic_MSG")
1842 return false;
1843 MSG *pEvent = static_cast<MSG*>(pMessage);
1844
1845 switch (pEvent->message)
1846 {
1847 /* Watch for key-events: */
1848 case WM_KEYDOWN:
1849 case WM_SYSKEYDOWN:
1850 case WM_KEYUP:
1851 case WM_SYSKEYUP:
1852 {
1853 // WORKAROUND:
1854 // There is an issue in the Windows Qt5 event processing sequence
1855 // causing QAbstractNativeEventFilter to receive Windows native events
1856 // coming not just to the top-level window but to actual target as well.
1857 // They are calling one - "global event" and another one - "context event".
1858 // That way native events are always duplicated with almost no possibility
1859 // to distinguish copies except the fact that synthetic event always have
1860 // time set to 0 (actually that field was not initialized at all, we had
1861 // fixed that in our private Qt tool). We should skip such events instantly.
1862 if (pEvent->time == 0)
1863 return false;
1864
1865 /* Delegate key-event handling to the keyboard-handler: */
1866 return machineLogic()->keyboardHandler()->nativeEventFilter(pMessage, screenId());
1867 }
1868 default:
1869 break;
1870 }
1871
1872# elif defined(VBOX_WS_X11)
1873
1874 /* Make sure it's generic XCB event: */
1875 if (eventType != "xcb_generic_event_t")
1876 return false;
1877 xcb_generic_event_t *pEvent = static_cast<xcb_generic_event_t*>(pMessage);
1878
1879 switch (pEvent->response_type & ~0x80)
1880 {
1881 /* Watch for key-events: */
1882 case XCB_KEY_PRESS:
1883 case XCB_KEY_RELEASE:
1884 {
1885 /* Delegate key-event handling to the keyboard-handler: */
1886 return machineLogic()->keyboardHandler()->nativeEventFilter(pMessage, screenId());
1887 }
1888 /* Watch for button-events: */
1889 case XCB_BUTTON_PRESS:
1890 case XCB_BUTTON_RELEASE:
1891 {
1892 /* Delegate button-event handling to the mouse-handler: */
1893 return machineLogic()->mouseHandler()->nativeEventFilter(pMessage, screenId());
1894 }
1895 default:
1896 break;
1897 }
1898
1899# else
1900
1901# warning "port me!"
1902
1903# endif
1904
1905 /* Filter nothing by default: */
1906 return false;
1907}
1908
1909QSize UIMachineView::scaledForward(QSize size) const
1910{
1911 /* Take the scale-factor into account: */
1912 const double dScaleFactor = frameBuffer()->scaleFactor();
1913 if (dScaleFactor != 1.0)
1914 size = QSize((int)(size.width() * dScaleFactor), (int)(size.height() * dScaleFactor));
1915
1916 /* Take the device-pixel-ratio into account: */
1917 const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
1918 const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
1919 if (!frameBuffer()->useUnscaledHiDPIOutput())
1920 size = QSize(size.width() * dDevicePixelRatioActual, size.height() * dDevicePixelRatioActual);
1921 size = QSize(size.width() / dDevicePixelRatioFormal, size.height() / dDevicePixelRatioFormal);
1922
1923 /* Return result: */
1924 return size;
1925}
1926
1927QSize UIMachineView::scaledBackward(QSize size) const
1928{
1929 /* Take the device-pixel-ratio into account: */
1930 const double dDevicePixelRatioFormal = frameBuffer()->devicePixelRatio();
1931 const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
1932 size = QSize(size.width() * dDevicePixelRatioFormal, size.height() * dDevicePixelRatioFormal);
1933 if (!frameBuffer()->useUnscaledHiDPIOutput())
1934 size = QSize(size.width() / dDevicePixelRatioActual, size.height() / dDevicePixelRatioActual);
1935
1936 /* Take the scale-factor into account: */
1937 const double dScaleFactor = frameBuffer()->scaleFactor();
1938 if (dScaleFactor != 1.0)
1939 size = QSize((int)(size.width() / dScaleFactor), (int)(size.height() / dScaleFactor));
1940
1941 /* Return result: */
1942 return size;
1943}
1944
1945void UIMachineView::updateMousePointerPixmapScaling(QPixmap &pixmap, uint &uXHot, uint &uYHot)
1946{
1947#if defined(VBOX_WS_MAC)
1948
1949 /* Take into account scale-factor if necessary: */
1950 const double dScaleFactor = frameBuffer()->scaleFactor();
1951 //printf("Scale-factor: %f\n", dScaleFactor);
1952 if (dScaleFactor > 1.0)
1953 {
1954 /* Scale the pixmap up: */
1955 pixmap = pixmap.scaled(pixmap.width() * dScaleFactor, pixmap.height() * dScaleFactor,
1956 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
1957 uXHot *= dScaleFactor;
1958 uYHot *= dScaleFactor;
1959 }
1960
1961 /* Take into account device-pixel-ratio if necessary: */
1962 const double dDevicePixelRatio = frameBuffer()->devicePixelRatio();
1963 const bool fUseUnscaledHiDPIOutput = frameBuffer()->useUnscaledHiDPIOutput();
1964 //printf("Device-pixel-ratio: %f, Unscaled HiDPI Output: %d\n",
1965 // dDevicePixelRatio, fUseUnscaledHiDPIOutput);
1966 if (dDevicePixelRatio > 1.0 && fUseUnscaledHiDPIOutput)
1967 {
1968 /* Scale the pixmap down: */
1969 pixmap.setDevicePixelRatio(dDevicePixelRatio);
1970 uXHot /= dDevicePixelRatio;
1971 uYHot /= dDevicePixelRatio;
1972 }
1973
1974#elif defined(VBOX_WS_WIN) || defined(VBOX_WS_X11)
1975
1976 /* We want to scale the pixmap just once, so let's prepare cumulative multiplier: */
1977 double dScaleMultiplier = 1.0;
1978
1979 /* Take into account scale-factor if necessary: */
1980 const double dScaleFactor = frameBuffer()->scaleFactor();
1981 //printf("Scale-factor: %f\n", dScaleFactor);
1982 if (dScaleFactor > 1.0)
1983 dScaleMultiplier *= dScaleFactor;
1984
1985 /* Take into account device-pixel-ratio if necessary: */
1986# ifdef VBOX_WS_WIN
1987 const double dDevicePixelRatio = frameBuffer()->devicePixelRatio();
1988# endif
1989 const double dDevicePixelRatioActual = frameBuffer()->devicePixelRatioActual();
1990 const bool fUseUnscaledHiDPIOutput = frameBuffer()->useUnscaledHiDPIOutput();
1991 //printf("Device-pixel-ratio/actual: %f/%f, Unscaled HiDPI Output: %d\n",
1992 // dDevicePixelRatio, dDevicePixelRatioActual, fUseUnscaledHiDPIOutput);
1993 if (dDevicePixelRatioActual > 1.0 && !fUseUnscaledHiDPIOutput)
1994 dScaleMultiplier *= dDevicePixelRatioActual;
1995
1996 /* If scale multiplier was set: */
1997 if (dScaleMultiplier > 1.0)
1998 {
1999 /* Scale the pixmap up: */
2000 pixmap = pixmap.scaled(pixmap.width() * dScaleMultiplier, pixmap.height() * dScaleMultiplier,
2001 Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
2002 uXHot *= dScaleMultiplier;
2003 uYHot *= dScaleMultiplier;
2004 }
2005
2006# ifdef VBOX_WS_WIN
2007 /* If device pixel ratio was set: */
2008 if (dDevicePixelRatio > 1.0)
2009 {
2010 /* Scale the pixmap down: */
2011 pixmap.setDevicePixelRatio(dDevicePixelRatio);
2012 uXHot /= dDevicePixelRatio;
2013 uYHot /= dDevicePixelRatio;
2014 }
2015# endif
2016
2017#else
2018
2019 Q_UNUSED(pixmap);
2020 Q_UNUSED(uXHot);
2021 Q_UNUSED(uYHot);
2022
2023#endif
2024}
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use