VirtualBox

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

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

Copyright year updates by scm.

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