VirtualBox

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

Last change on this file was 104393, checked in by vboxsync, 4 weeks ago

FE/Qt. bugref:10622. Using new UITranslationEventListener in the UIActionPool class.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use