VirtualBox

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

Last change on this file since 103977 was 103711, checked in by vboxsync, 9 months ago

FE/Qt: Get rid of even more iprt includes, s.a. r162071 and r162072.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette