VirtualBox

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

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

Apply RT_OVERRIDE/NS_OVERRIDE where required to shut up clang.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 59.4 KB
Line 
1/* $Id: UIFrameBuffer.cpp 103977 2024-03-21 02:04:52Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIFrameBuffer 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 <QImage>
30#include <QRegion>
31#include <QPainter>
32#include <QTransform>
33
34/* GUI includes: */
35#include "UIActionPoolRuntime.h"
36#include "UICommon.h"
37#include "UIExtraDataManager.h"
38#include "UIFrameBuffer.h"
39#include "UILoggingDefs.h"
40#include "UIMachine.h"
41#include "UIMachineLogic.h"
42#include "UIMachineView.h"
43#include "UIMachineWindow.h"
44#include "UINotificationCenter.h"
45#include "UISession.h"
46
47/* COM includes: */
48#include "CDisplay.h"
49#include "CFramebuffer.h"
50#include "CDisplaySourceBitmap.h"
51
52/* VirtualBox interface declarations: */
53#include <VBox/com/VirtualBox.h>
54
55/* Other VBox includes: */
56#include <iprt/critsect.h>
57#include <VBoxVideo3D.h>
58
59/* Other includes: */
60#include <math.h>
61#ifdef VBOX_WS_NIX
62# include <X11/Xlib.h>
63# undef Bool // Qt5 vs Xlib gift..
64#endif /* VBOX_WS_NIX */
65
66
67/** IFramebuffer implementation used to maintain VM display video memory. */
68class ATL_NO_VTABLE UIFrameBufferPrivate : public QObject,
69 public ATL::CComObjectRootEx<ATL::CComMultiThreadModel>,
70 VBOX_SCRIPTABLE_IMPL(IFramebuffer)
71{
72 Q_OBJECT;
73
74signals:
75
76 /** Notifies listener about guest-screen resolution changes. */
77 void sigNotifyChange(int iWidth, int iHeight);
78 /** Notifies listener about guest-screen updates. */
79 void sigNotifyUpdate(int iX, int iY, int iWidth, int iHeight);
80 /** Notifies listener about guest-screen visible-region changes. */
81 void sigSetVisibleRegion(QRegion region);
82
83public:
84
85 /** Constructs frame-buffer. */
86 UIFrameBufferPrivate();
87 /** Destructs frame-buffer. */
88 virtual ~UIFrameBufferPrivate() RT_OVERRIDE;
89
90 /** Frame-buffer initialization.
91 * @param pMachineView defines machine-view this frame-buffer is bounded to. */
92 virtual HRESULT init(UIMachineView *pMachineView);
93
94 /** Assigns machine-view frame-buffer will be bounded to.
95 * @param pMachineView defines machine-view this frame-buffer is bounded to. */
96 virtual void setView(UIMachineView *pMachineView);
97
98 /** Returns the copy of the IDisplay wrapper. */
99 CDisplay display() const { return m_comDisplay; }
100 /** Attach frame-buffer to IDisplay. */
101 void attach();
102 /** Detach frame-buffer from IDisplay. */
103 void detach();
104
105 /** Returns frame-buffer data address. */
106 uchar *address() { return m_image.bits(); }
107 /** Returns frame-buffer width. */
108 ulong width() const { return m_iWidth; }
109 /** Returns frame-buffer height. */
110 ulong height() const { return m_iHeight; }
111 /** Returns frame-buffer bits-per-pixel value. */
112 ulong bitsPerPixel() const { return m_image.depth(); }
113 /** Returns frame-buffer bytes-per-line value. */
114 ulong bytesPerLine() const { return m_image.bytesPerLine(); }
115 /** Returns default frame-buffer pixel-format. */
116 ulong pixelFormat() const { return KBitmapFormat_BGR; }
117
118 /** Defines whether frame-buffer is <b>unused</b>.
119 * @note Refer to m_fUnused for more information.
120 * @note Calls to this and any other EMT callback are synchronized (from GUI side). */
121 void setMarkAsUnused(bool fUnused);
122
123 /** Returns the frame-buffer's scaled-size. */
124 QSize scaledSize() const { return m_scaledSize; }
125 /** Defines host-to-guest scale ratio as @a size. */
126 void setScaledSize(const QSize &size = QSize()) { m_scaledSize = size; }
127 /** Returns x-origin of the host (scaled) content corresponding to x-origin of guest (actual) content. */
128 inline int convertGuestXTo(int x) const { return m_scaledSize.isValid() ? qRound((double)m_scaledSize.width() / m_iWidth * x) : x; }
129 /** Returns y-origin of the host (scaled) content corresponding to y-origin of guest (actual) content. */
130 inline int convertGuestYTo(int y) const { return m_scaledSize.isValid() ? qRound((double)m_scaledSize.height() / m_iHeight * y) : y; }
131 /** Returns x-origin of the guest (actual) content corresponding to x-origin of host (scaled) content. */
132 inline int convertHostXTo(int x) const { return m_scaledSize.isValid() ? qRound((double)m_iWidth / m_scaledSize.width() * x) : x; }
133 /** Returns y-origin of the guest (actual) content corresponding to y-origin of host (scaled) content. */
134 inline int convertHostYTo(int y) const { return m_scaledSize.isValid() ? qRound((double)m_iHeight / m_scaledSize.height() * y) : y; }
135
136 /** Returns the scale-factor used by the frame-buffer. */
137 double scaleFactor() const { return m_dScaleFactor; }
138 /** Define the scale-factor used by the frame-buffer. */
139 void setScaleFactor(double dScaleFactor) { m_dScaleFactor = dScaleFactor; }
140
141 /** Returns device-pixel-ratio set for HiDPI frame-buffer. */
142 double devicePixelRatio() const { return m_dDevicePixelRatio; }
143 /** Defines device-pixel-ratio set for HiDPI frame-buffer. */
144 void setDevicePixelRatio(double dDevicePixelRatio) { m_dDevicePixelRatio = dDevicePixelRatio; }
145 /** Returns actual device-pixel-ratio set for HiDPI frame-buffer. */
146 double devicePixelRatioActual() const { return m_dDevicePixelRatioActual; }
147 /** Defines actual device-pixel-ratio set for HiDPI frame-buffer. */
148 void setDevicePixelRatioActual(double dDevicePixelRatioActual) { m_dDevicePixelRatioActual = dDevicePixelRatioActual; }
149
150 /** Returns whether frame-buffer should use unscaled HiDPI output. */
151 bool useUnscaledHiDPIOutput() const { return m_fUseUnscaledHiDPIOutput; }
152 /** Defines whether frame-buffer should use unscaled HiDPI output. */
153 void setUseUnscaledHiDPIOutput(bool fUseUnscaledHiDPIOutput) { m_fUseUnscaledHiDPIOutput = fUseUnscaledHiDPIOutput; }
154
155 /** Returns frame-buffer scaling optimization type. */
156 ScalingOptimizationType scalingOptimizationType() const { return m_enmScalingOptimizationType; }
157 /** Defines frame-buffer scaling optimization type: */
158 void setScalingOptimizationType(ScalingOptimizationType type) { m_enmScalingOptimizationType = type; }
159
160 DECLARE_NOT_AGGREGATABLE(UIFrameBufferPrivate)
161
162 DECLARE_PROTECT_FINAL_CONSTRUCT()
163
164 BEGIN_COM_MAP(UIFrameBufferPrivate)
165 COM_INTERFACE_ENTRY(IFramebuffer)
166 COM_INTERFACE_ENTRY2(IDispatch,IFramebuffer)
167 COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pUnkMarshaler.m_p)
168 END_COM_MAP()
169
170 HRESULT FinalConstruct();
171 void FinalRelease();
172
173 STDMETHOD(COMGETTER(Width))(ULONG *puWidth) RT_OVERRIDE;
174 STDMETHOD(COMGETTER(Height))(ULONG *puHeight) RT_OVERRIDE;
175 STDMETHOD(COMGETTER(BitsPerPixel))(ULONG *puBitsPerPixel) RT_OVERRIDE;
176 STDMETHOD(COMGETTER(BytesPerLine))(ULONG *puBytesPerLine) RT_OVERRIDE;
177 STDMETHOD(COMGETTER(PixelFormat))(BitmapFormat_T *puPixelFormat) RT_OVERRIDE;
178 STDMETHOD(COMGETTER(HeightReduction))(ULONG *puHeightReduction) RT_OVERRIDE;
179 STDMETHOD(COMGETTER(Overlay))(IFramebufferOverlay **ppOverlay) RT_OVERRIDE;
180 STDMETHOD(COMGETTER(WinId))(LONG64 *pWinId) RT_OVERRIDE;
181 STDMETHOD(COMGETTER(Capabilities))(ComSafeArrayOut(FramebufferCapabilities_T, aCapabilities)) RT_OVERRIDE;
182
183 /** EMT callback: Notifies frame-buffer about guest-screen size change.
184 * @param uScreenId Guest screen number.
185 * @param uX Horizontal origin of the update rectangle, in pixels.
186 * @param uY Vertical origin of the update rectangle, in pixels.
187 * @param uWidth Width of the guest display, in pixels.
188 * @param uHeight Height of the guest display, in pixels.
189 * @note Any EMT callback is subsequent. No any other EMT callback can be called until this one processed.
190 * @note Calls to this and #setMarkAsUnused method are synchronized (from GUI side). */
191 STDMETHOD(NotifyChange)(ULONG uScreenId, ULONG uX, ULONG uY, ULONG uWidth, ULONG uHeight) RT_OVERRIDE;
192
193 /** EMT callback: Notifies frame-buffer about guest-screen update.
194 * @param uX Horizontal origin of the update rectangle, in pixels.
195 * @param uY Vertical origin of the update rectangle, in pixels.
196 * @param uWidth Width of the update rectangle, in pixels.
197 * @param uHeight Height of the update rectangle, in pixels.
198 * @note Any EMT callback is subsequent. No any other EMT callback can be called until this one processed.
199 * @note Calls to this and #setMarkAsUnused method are synchronized (from GUI side). */
200 STDMETHOD(NotifyUpdate)(ULONG uX, ULONG uY, ULONG uWidth, ULONG uHeight) RT_OVERRIDE;
201
202 /** EMT callback: Notifies frame-buffer about guest-screen update.
203 * @param uX Horizontal origin of the update rectangle, in pixels.
204 * @param uY Vertical origin of the update rectangle, in pixels.
205 * @param uWidth Width of the update rectangle, in pixels.
206 * @param uHeight Height of the update rectangle, in pixels.
207 * @param image Brings image container which can be used to copy data from.
208 * @note Any EMT callback is subsequent. No any other EMT callback can be called until this one processed.
209 * @note Calls to this and #setMarkAsUnused method are synchronized (from GUI side). */
210 STDMETHOD(NotifyUpdateImage)(ULONG uX, ULONG uY, ULONG uWidth, ULONG uHeight, ComSafeArrayIn(BYTE, image)) RT_OVERRIDE;
211
212 /** EMT callback: Returns whether the frame-buffer implementation is willing to support a given video-mode.
213 * @param uWidth Width of the guest display, in pixels.
214 * @param uHeight Height of the guest display, in pixels.
215 * @param uBPP Color depth, bits per pixel.
216 * @param pfSupported Is frame-buffer able/willing to render the video mode or not.
217 * @note Any EMT callback is subsequent. No any other EMT callback can be called until this one processed.
218 * @note Calls to this and #setMarkAsUnused method are synchronized (from GUI side). */
219 STDMETHOD(VideoModeSupported)(ULONG uWidth, ULONG uHeight, ULONG uBPP, BOOL *pbSupported) RT_OVERRIDE;
220
221 /** EMT callback which is not used in current implementation. */
222 STDMETHOD(GetVisibleRegion)(BYTE *pRectangles, ULONG uCount, ULONG *puCountCopied) RT_OVERRIDE;
223 /** EMT callback: Suggests new visible-region to this frame-buffer.
224 * @param pRectangles Pointer to the RTRECT array.
225 * @param uCount Number of RTRECT elements in the rectangles array.
226 * @note Any EMT callback is subsequent. No any other EMT callback can be called until this one processed.
227 * @note Calls to this and #setMarkAsUnused method are synchronized (from GUI side). */
228 STDMETHOD(SetVisibleRegion)(BYTE *pRectangles, ULONG uCount) RT_OVERRIDE;
229
230 /** EMT callback which is not used in current implementation. */
231 STDMETHOD(ProcessVHWACommand)(BYTE *pCommand, LONG enmCmd, BOOL fGuestCmd) RT_OVERRIDE;
232
233 /** EMT callback: Notifies frame-buffer about 3D backend event.
234 * @param uType Event type. VBOX3D_NOTIFY_TYPE_*.
235 * @param aData Event-specific data, depends on the supplied event type.
236 * @note Any EMT callback is subsequent. No any other EMT callback can be called until this one processed.
237 * @note Calls to this and #setMarkAsUnused method are synchronized (from GUI side). */
238 STDMETHOD(Notify3DEvent)(ULONG uType, ComSafeArrayIn(BYTE, data)) RT_OVERRIDE;
239
240 /** Locks frame-buffer access. */
241 void lock() const { RTCritSectEnter(&m_critSect); }
242 /** Unlocks frame-buffer access. */
243 void unlock() const { RTCritSectLeave(&m_critSect); }
244
245 /** Handles frame-buffer notify-change-event. */
246 virtual void handleNotifyChange(int iWidth, int iHeight);
247 /** Handles frame-buffer paint-event. */
248 virtual void handlePaintEvent(QPaintEvent *pEvent);
249 /** Handles frame-buffer set-visible-region-event. */
250 virtual void handleSetVisibleRegion(const QRegion &region);
251
252 /** Performs frame-buffer resizing. */
253 virtual void performResize(int iWidth, int iHeight);
254 /** Performs frame-buffer rescaling. */
255 virtual void performRescale();
256
257 /** Handles viewport resize-event. */
258 virtual void viewportResized(QResizeEvent*)
259 {
260 }
261
262protected slots:
263
264 /** Handles guest requests to change mouse pointer shape or position. */
265 void sltMousePointerShapeOrPositionChange();
266
267protected:
268
269 /** Prepare connections routine. */
270 void prepareConnections();
271 /** Cleanup connections routine. */
272 void cleanupConnections();
273
274 /** Updates coordinate-system: */
275 void updateCoordinateSystem();
276
277 /** Default paint routine. */
278 void paintDefault(QPaintEvent *pEvent);
279 /** Paint routine for seamless mode. */
280 void paintSeamless(QPaintEvent *pEvent);
281
282 /** Returns the transformation mode corresponding to the passed @a dScaleFactor and ScalingOptimizationType. */
283 static Qt::TransformationMode transformationMode(ScalingOptimizationType type, double dScaleFactor = 0);
284
285 /** Erases corresponding @a rect with @a painter. */
286 static void eraseImageRect(QPainter &painter, const QRect &rect,
287 double dDevicePixelRatio);
288 /** Draws corresponding @a rect of passed @a image with @a painter. */
289 static void drawImageRect(QPainter &painter, const QImage &image, const QRect &rect,
290 int iContentsShiftX, int iContentsShiftY,
291 double dDevicePixelRatio);
292
293 /** Holds the screen-id. */
294 ulong m_uScreenId;
295
296 /** Holds the QImage buffer. */
297 QImage m_image;
298 /** Holds frame-buffer width. */
299 int m_iWidth;
300 /** Holds frame-buffer height. */
301 int m_iHeight;
302
303 /** Holds the copy of the IDisplay wrapper. */
304 CDisplay m_comDisplay;
305 /** Source bitmap from IDisplay. */
306 CDisplaySourceBitmap m_sourceBitmap;
307 /** Source bitmap from IDisplay (acquired but not yet applied). */
308 CDisplaySourceBitmap m_pendingSourceBitmap;
309 /** Holds whether there is a pending source bitmap which must be applied. */
310 bool m_fPendingSourceBitmap;
311
312 /** Holds machine-view this frame-buffer is bounded to. */
313 UIMachineView *m_pMachineView;
314 /** Holds window ID this frame-buffer referring to. */
315 int64_t m_iWinId;
316
317 /** Reflects whether screen-updates are allowed. */
318 bool m_fUpdatesAllowed;
319
320 /** Defines whether frame-buffer is <b>unused</b>.
321 * <b>Unused</b> status determines whether frame-buffer should ignore EMT events or not. */
322 bool m_fUnused;
323
324 /** RTCRITSECT object to protect frame-buffer access. */
325 mutable RTCRITSECT m_critSect;
326
327 /** @name Scale-factor related variables.
328 * @{ */
329 /** Holds the scale-factor used by the scaled-size. */
330 double m_dScaleFactor;
331 /** Holds the scaling optimization type used by the scaling mechanism. */
332 ScalingOptimizationType m_enmScalingOptimizationType;
333 /** Holds the coordinate-system for the scale-factor above. */
334 QTransform m_transform;
335 /** Holds the frame-buffer's scaled-size. */
336 QSize m_scaledSize;
337 /** @} */
338
339 /** @name Seamless mode related variables.
340 * @{ */
341 /* To avoid a seamless flicker which caused by the latency between
342 * the initial visible-region arriving from EMT thread
343 * and actual visible-region appliance on GUI thread
344 * it was decided to use two visible-region instances: */
345 /** Sync visible-region which being updated synchronously by locking EMT thread.
346 * Used for immediate manual clipping of the painting operations. */
347 QRegion m_syncVisibleRegion;
348 /** Async visible-region which being updated asynchronously by posting async-event from EMT to GUI thread,
349 * Used to update viewport parts for visible-region changes,
350 * because NotifyUpdate doesn't take into account these changes. */
351 QRegion m_asyncVisibleRegion;
352 /** When the frame-buffer is being resized, visible region is saved here.
353 * The saved region is applied when updates are enabled again. */
354 QRegion m_pendingSyncVisibleRegion;
355 /** @} */
356
357 /** @name HiDPI screens related variables.
358 * @{ */
359 /** Holds device-pixel-ratio set for HiDPI frame-buffer. */
360 double m_dDevicePixelRatio;
361 /** Holds actual device-pixel-ratio set for HiDPI frame-buffer. */
362 double m_dDevicePixelRatioActual;
363 /** Holds whether frame-buffer should use unscaled HiDPI output. */
364 bool m_fUseUnscaledHiDPIOutput;
365 /** @} */
366
367private:
368
369#ifdef VBOX_WS_WIN
370 ComPtr<IUnknown> m_pUnkMarshaler;
371#endif
372 /** Identifier returned by AttachFramebuffer. Used in DetachFramebuffer. */
373 QUuid m_uFramebufferId;
374
375 /** Holds the last cursor rectangle. */
376 QRect m_cursorRectangle;
377};
378
379
380#ifdef VBOX_WITH_XPCOM
381NS_DECL_CLASSINFO(UIFrameBufferPrivate)
382NS_IMPL_THREADSAFE_ISUPPORTS1_CI(UIFrameBufferPrivate, IFramebuffer)
383#endif /* VBOX_WITH_XPCOM */
384
385
386UIFrameBufferPrivate::UIFrameBufferPrivate()
387 : m_uScreenId(0)
388 , m_iWidth(0), m_iHeight(0)
389 , m_fPendingSourceBitmap(false)
390 , m_pMachineView(NULL)
391 , m_iWinId(0)
392 , m_fUpdatesAllowed(false)
393 , m_fUnused(false)
394 , m_dScaleFactor(1.0)
395 , m_enmScalingOptimizationType(ScalingOptimizationType_None)
396 , m_dDevicePixelRatio(1.0)
397 , m_dDevicePixelRatioActual(1.0)
398 , m_fUseUnscaledHiDPIOutput(false)
399{
400 LogRel2(("GUI: UIFrameBufferPrivate::UIFrameBufferPrivate %p\n", this));
401
402 /* Update coordinate-system: */
403 updateCoordinateSystem();
404}
405
406UIFrameBufferPrivate::~UIFrameBufferPrivate()
407{
408 LogRel2(("GUI: UIFrameBufferPrivate::~UIFrameBufferPrivate %p\n", this));
409
410 /* Disconnect handlers: */
411 if (m_pMachineView)
412 cleanupConnections();
413
414 /* Deinitialize critical-section: */
415 RTCritSectDelete(&m_critSect);
416}
417
418HRESULT UIFrameBufferPrivate::init(UIMachineView *pMachineView)
419{
420 LogRel2(("GUI: UIFrameBufferPrivate::init %p\n", this));
421
422 /* Fetch passed view: */
423 setView(pMachineView);
424
425 /* Assign display: */
426 m_comDisplay = gpMachine->uisession()->display();
427
428 /* Initialize critical-section: */
429 int rc = RTCritSectInit(&m_critSect);
430 AssertRC(rc);
431
432 /* Resize/rescale frame-buffer to the default size: */
433 performResize(640, 480);
434 performRescale();
435
436#ifdef VBOX_WS_WIN
437 CoCreateFreeThreadedMarshaler(this, m_pUnkMarshaler.asOutParam());
438#endif
439
440 return S_OK;
441}
442
443void UIFrameBufferPrivate::setView(UIMachineView *pMachineView)
444{
445 /* Disconnect old handlers: */
446 if (m_pMachineView)
447 cleanupConnections();
448
449 /* Reassign machine-view: */
450 m_pMachineView = pMachineView;
451 /* Reassign index: */
452 m_uScreenId = m_pMachineView ? m_pMachineView->screenId() : 0;
453 /* Recache window ID: */
454 m_iWinId = (m_pMachineView && m_pMachineView->viewport()) ? (LONG64)m_pMachineView->viewport()->winId() : 0;
455
456#ifdef VBOX_WS_NIX
457 if (uiCommon().X11ServerAvailable())
458 /* Resync Qt and X11 Server (see xTracker #7547). */
459 XSync(NativeWindowSubsystem::X11GetDisplay(), false);
460#endif
461
462 /* Reconnect new handlers: */
463 if (m_pMachineView)
464 prepareConnections();
465}
466
467void UIFrameBufferPrivate::attach()
468{
469 m_uFramebufferId = display().AttachFramebuffer(m_uScreenId, CFramebuffer(this));
470}
471
472void UIFrameBufferPrivate::detach()
473{
474 CFramebuffer comFramebuffer = display().QueryFramebuffer(m_uScreenId);
475 if (!comFramebuffer.isNull())
476 {
477 display().DetachFramebuffer(m_uScreenId, m_uFramebufferId);
478 m_uFramebufferId = QUuid();
479 }
480}
481
482void UIFrameBufferPrivate::setMarkAsUnused(bool fUnused)
483{
484 lock();
485 m_fUnused = fUnused;
486 unlock();
487}
488
489HRESULT UIFrameBufferPrivate::FinalConstruct()
490{
491 return 0;
492}
493
494void UIFrameBufferPrivate::FinalRelease()
495{
496 return;
497}
498
499STDMETHODIMP UIFrameBufferPrivate::COMGETTER(Width)(ULONG *puWidth)
500{
501 if (!puWidth)
502 return E_POINTER;
503 *puWidth = (ULONG)width();
504 return S_OK;
505}
506
507STDMETHODIMP UIFrameBufferPrivate::COMGETTER(Height)(ULONG *puHeight)
508{
509 if (!puHeight)
510 return E_POINTER;
511 *puHeight = (ULONG)height();
512 return S_OK;
513}
514
515STDMETHODIMP UIFrameBufferPrivate::COMGETTER(BitsPerPixel)(ULONG *puBitsPerPixel)
516{
517 if (!puBitsPerPixel)
518 return E_POINTER;
519 *puBitsPerPixel = bitsPerPixel();
520 return S_OK;
521}
522
523STDMETHODIMP UIFrameBufferPrivate::COMGETTER(BytesPerLine)(ULONG *puBytesPerLine)
524{
525 if (!puBytesPerLine)
526 return E_POINTER;
527 *puBytesPerLine = bytesPerLine();
528 return S_OK;
529}
530
531STDMETHODIMP UIFrameBufferPrivate::COMGETTER(PixelFormat)(BitmapFormat_T *puPixelFormat)
532{
533 if (!puPixelFormat)
534 return E_POINTER;
535 *puPixelFormat = (BitmapFormat_T)pixelFormat();
536 return S_OK;
537}
538
539STDMETHODIMP UIFrameBufferPrivate::COMGETTER(HeightReduction)(ULONG *puHeightReduction)
540{
541 if (!puHeightReduction)
542 return E_POINTER;
543 *puHeightReduction = 0;
544 return S_OK;
545}
546
547STDMETHODIMP UIFrameBufferPrivate::COMGETTER(Overlay)(IFramebufferOverlay **ppOverlay)
548{
549 if (!ppOverlay)
550 return E_POINTER;
551 *ppOverlay = 0;
552 return S_OK;
553}
554
555STDMETHODIMP UIFrameBufferPrivate::COMGETTER(WinId)(LONG64 *pWinId)
556{
557 if (!pWinId)
558 return E_POINTER;
559 *pWinId = m_iWinId;
560 return S_OK;
561}
562
563STDMETHODIMP UIFrameBufferPrivate::COMGETTER(Capabilities)(ComSafeArrayOut(FramebufferCapabilities_T, enmCapabilities))
564{
565 if (ComSafeArrayOutIsNull(enmCapabilities))
566 return E_POINTER;
567
568 com::SafeArray<FramebufferCapabilities_T> caps;
569 if (uiCommon().isSeparateProcess())
570 {
571 caps.resize(2);
572 caps[0] = FramebufferCapabilities_UpdateImage;
573 caps[1] = FramebufferCapabilities_RenderCursor;
574 }
575 else
576 {
577 caps.resize(3);
578 caps[0] = FramebufferCapabilities_VHWA;
579 caps[1] = FramebufferCapabilities_VisibleRegion;
580 caps[2] = FramebufferCapabilities_RenderCursor;
581 }
582
583 caps.detachTo(ComSafeArrayOutArg(enmCapabilities));
584 return S_OK;
585}
586
587STDMETHODIMP UIFrameBufferPrivate::NotifyChange(ULONG uScreenId, ULONG uX, ULONG uY, ULONG uWidth, ULONG uHeight)
588{
589 CDisplaySourceBitmap sourceBitmap;
590 if (!uiCommon().isSeparateProcess())
591 display().QuerySourceBitmap(uScreenId, sourceBitmap);
592
593 /* Lock access to frame-buffer: */
594 lock();
595
596 /* Make sure frame-buffer is used: */
597 if (m_fUnused)
598 {
599 LogRel(("GUI: UIFrameBufferPrivate::NotifyChange: Screen=%lu, Origin=%lux%lu, Size=%lux%lu, Ignored!\n",
600 (unsigned long)uScreenId,
601 (unsigned long)uX, (unsigned long)uY,
602 (unsigned long)uWidth, (unsigned long)uHeight));
603
604 /* Unlock access to frame-buffer: */
605 unlock();
606
607 /* Ignore NotifyChange: */
608 return E_FAIL;
609 }
610
611 /* Disable screen updates: */
612 m_fUpdatesAllowed = false;
613
614 /* While updates are disabled, visible region will be saved: */
615 m_pendingSyncVisibleRegion = QRegion();
616
617 if (!uiCommon().isSeparateProcess())
618 {
619 /* Acquire new pending bitmap: */
620 m_pendingSourceBitmap = sourceBitmap;
621 m_fPendingSourceBitmap = true;
622 }
623
624 /* Widget resize is NOT thread-safe and *probably* never will be,
625 * We have to notify machine-view with the async-signal to perform resize operation. */
626 LogRel2(("GUI: UIFrameBufferPrivate::NotifyChange: Screen=%lu, Origin=%lux%lu, Size=%lux%lu, Sending to async-handler\n",
627 (unsigned long)uScreenId,
628 (unsigned long)uX, (unsigned long)uY,
629 (unsigned long)uWidth, (unsigned long)uHeight));
630 emit sigNotifyChange(uWidth, uHeight);
631
632 /* Unlock access to frame-buffer: */
633 unlock();
634
635 /* Give up control token to other thread: */
636 RTThreadYield();
637
638 /* Confirm NotifyChange: */
639 return S_OK;
640}
641
642STDMETHODIMP UIFrameBufferPrivate::NotifyUpdate(ULONG uX, ULONG uY, ULONG uWidth, ULONG uHeight)
643{
644 /* Lock access to frame-buffer: */
645 lock();
646
647 /* Make sure frame-buffer is used: */
648 if (m_fUnused)
649 {
650 LogRel3(("GUI: UIFrameBufferPrivate::NotifyUpdate: Origin=%lux%lu, Size=%lux%lu, Ignored!\n",
651 (unsigned long)uX, (unsigned long)uY,
652 (unsigned long)uWidth, (unsigned long)uHeight));
653 /* Unlock access to frame-buffer: */
654 unlock();
655
656 /* Ignore NotifyUpdate: */
657 return E_FAIL;
658 }
659
660 /* Widget update is NOT thread-safe and *seems* never will be,
661 * We have to notify machine-view with the async-signal to perform update operation. */
662 LogRel3(("GUI: UIFrameBufferPrivate::NotifyUpdate: Origin=%lux%lu, Size=%lux%lu, Sending to async-handler\n",
663 (unsigned long)uX, (unsigned long)uY,
664 (unsigned long)uWidth, (unsigned long)uHeight));
665 emit sigNotifyUpdate(uX, uY, uWidth, uHeight);
666
667 /* Unlock access to frame-buffer: */
668 unlock();
669
670 /* Confirm NotifyUpdate: */
671 return S_OK;
672}
673
674STDMETHODIMP UIFrameBufferPrivate::NotifyUpdateImage(ULONG uX, ULONG uY,
675 ULONG uWidth, ULONG uHeight,
676 ComSafeArrayIn(BYTE, image))
677{
678 /* Wrapping received data: */
679 com::SafeArray<BYTE> imageData(ComSafeArrayInArg(image));
680
681 /* Lock access to frame-buffer: */
682 lock();
683
684 /* Make sure frame-buffer is used: */
685 if (m_fUnused)
686 {
687 LogRel3(("GUI: UIFrameBufferPrivate::NotifyUpdateImage: Origin=%lux%lu, Size=%lux%lu, Ignored!\n",
688 (unsigned long)uX, (unsigned long)uY,
689 (unsigned long)uWidth, (unsigned long)uHeight));
690
691 /* Unlock access to frame-buffer: */
692 unlock();
693
694 /* Ignore NotifyUpdate: */
695 return E_FAIL;
696 }
697 /* Directly update m_image if update fits: */
698 if ( m_fUpdatesAllowed
699 && uX + uWidth <= (ULONG)m_image.width()
700 && uY + uHeight <= (ULONG)m_image.height())
701 {
702 /* Copy to m_image: */
703 uchar *pu8Dst = m_image.bits() + uY * m_image.bytesPerLine() + uX * 4;
704 uchar *pu8Src = imageData.raw();
705 ULONG h;
706 for (h = 0; h < uHeight; ++h)
707 {
708 memcpy(pu8Dst, pu8Src, uWidth * 4);
709 pu8Dst += m_image.bytesPerLine();
710 pu8Src += uWidth * 4;
711 }
712
713 /* Widget update is NOT thread-safe and *seems* never will be,
714 * We have to notify machine-view with the async-signal to perform update operation. */
715 LogRel3(("GUI: UIFrameBufferPrivate::NotifyUpdateImage: Origin=%lux%lu, Size=%lux%lu, Sending to async-handler\n",
716 (unsigned long)uX, (unsigned long)uY,
717 (unsigned long)uWidth, (unsigned long)uHeight));
718 emit sigNotifyUpdate(uX, uY, uWidth, uHeight);
719 }
720
721 /* Unlock access to frame-buffer: */
722 unlock();
723
724 /* Confirm NotifyUpdateImage: */
725 return S_OK;
726}
727
728STDMETHODIMP UIFrameBufferPrivate::VideoModeSupported(ULONG uWidth, ULONG uHeight, ULONG uBPP, BOOL *pfSupported)
729{
730 /* Make sure result pointer is valid: */
731 if (!pfSupported)
732 {
733 LogRel2(("GUI: UIFrameBufferPrivate::IsVideoModeSupported: Mode: BPP=%lu, Size=%lux%lu, Invalid pfSupported pointer!\n",
734 (unsigned long)uBPP, (unsigned long)uWidth, (unsigned long)uHeight));
735
736 return E_POINTER;
737 }
738
739 /* Lock access to frame-buffer: */
740 lock();
741
742 /* Make sure frame-buffer is used: */
743 if (m_fUnused)
744 {
745 LogRel2(("GUI: UIFrameBufferPrivate::IsVideoModeSupported: Mode: BPP=%lu, Size=%lux%lu, Ignored!\n",
746 (unsigned long)uBPP, (unsigned long)uWidth, (unsigned long)uHeight));
747
748 /* Unlock access to frame-buffer: */
749 unlock();
750
751 /* Ignore VideoModeSupported: */
752 return E_FAIL;
753 }
754
755 /* Determine if supported: */
756 *pfSupported = TRUE;
757 const QSize screenSize = m_pMachineView->maximumGuestSize();
758 if ( (screenSize.width() != 0)
759 && (uWidth > (ULONG)screenSize.width())
760 && (uWidth > (ULONG)width()))
761 *pfSupported = FALSE;
762 if ( (screenSize.height() != 0)
763 && (uHeight > (ULONG)screenSize.height())
764 && (uHeight > (ULONG)height()))
765 *pfSupported = FALSE;
766 if (*pfSupported)
767 LogRel2(("GUI: UIFrameBufferPrivate::IsVideoModeSupported: Mode: BPP=%lu, Size=%lux%lu is supported\n",
768 (unsigned long)uBPP, (unsigned long)uWidth, (unsigned long)uHeight));
769 else
770 LogRel(("GUI: UIFrameBufferPrivate::IsVideoModeSupported: Mode: BPP=%lu, Size=%lux%lu is NOT supported\n",
771 (unsigned long)uBPP, (unsigned long)uWidth, (unsigned long)uHeight));
772
773 /* Unlock access to frame-buffer: */
774 unlock();
775
776 /* Confirm VideoModeSupported: */
777 return S_OK;
778}
779
780STDMETHODIMP UIFrameBufferPrivate::GetVisibleRegion(BYTE *pRectangles, ULONG uCount, ULONG *puCountCopied)
781{
782 PRTRECT rects = (PRTRECT)pRectangles;
783
784 if (!rects)
785 return E_POINTER;
786
787 Q_UNUSED(uCount);
788 Q_UNUSED(puCountCopied);
789
790 return S_OK;
791}
792
793STDMETHODIMP UIFrameBufferPrivate::SetVisibleRegion(BYTE *pRectangles, ULONG uCount)
794{
795 /* Make sure rectangles were passed: */
796 if (!pRectangles)
797 {
798 LogRel2(("GUI: UIFrameBufferPrivate::SetVisibleRegion: Rectangle count=%lu, Invalid pRectangles pointer!\n",
799 (unsigned long)uCount));
800
801 return E_POINTER;
802 }
803
804 /* Lock access to frame-buffer: */
805 lock();
806
807 /* Make sure frame-buffer is used: */
808 if (m_fUnused)
809 {
810 LogRel2(("GUI: UIFrameBufferPrivate::SetVisibleRegion: Rectangle count=%lu, Ignored!\n",
811 (unsigned long)uCount));
812
813 /* Unlock access to frame-buffer: */
814 unlock();
815
816 /* Ignore SetVisibleRegion: */
817 return E_FAIL;
818 }
819
820 /* Compose region: */
821 QRegion region;
822 PRTRECT rects = (PRTRECT)pRectangles;
823 for (ULONG uIndex = 0; uIndex < uCount; ++uIndex)
824 {
825 /* Get current rectangle: */
826 QRect rect;
827 rect.setLeft(rects->xLeft);
828 rect.setTop(rects->yTop);
829 /* Which is inclusive: */
830 rect.setRight(rects->xRight - 1);
831 rect.setBottom(rects->yBottom - 1);
832 /* Append region: */
833 region += rect;
834 ++rects;
835 }
836 /* Tune according scale-factor: */
837 if (scaleFactor() != 1.0 || devicePixelRatio() > 1.0)
838 region = m_transform.map(region);
839
840 if (m_fUpdatesAllowed)
841 {
842 /* We are directly updating synchronous visible-region: */
843 m_syncVisibleRegion = region;
844 /* And send async-signal to update asynchronous one: */
845 LogRel2(("GUI: UIFrameBufferPrivate::SetVisibleRegion: Rectangle count=%lu, Sending to async-handler\n",
846 (unsigned long)uCount));
847 emit sigSetVisibleRegion(region);
848 }
849 else
850 {
851 /* Save the region. */
852 m_pendingSyncVisibleRegion = region;
853 LogRel2(("GUI: UIFrameBufferPrivate::SetVisibleRegion: Rectangle count=%lu, Saved\n",
854 (unsigned long)uCount));
855 }
856
857 /* Unlock access to frame-buffer: */
858 unlock();
859
860 /* Confirm SetVisibleRegion: */
861 return S_OK;
862}
863
864STDMETHODIMP UIFrameBufferPrivate::ProcessVHWACommand(BYTE *pCommand, LONG enmCmd, BOOL fGuestCmd)
865{
866 RT_NOREF(pCommand, enmCmd, fGuestCmd);
867 return E_NOTIMPL;
868}
869
870STDMETHODIMP UIFrameBufferPrivate::Notify3DEvent(ULONG uType, ComSafeArrayIn(BYTE, data))
871{
872 /* Lock access to frame-buffer: */
873 lock();
874
875 /* Make sure frame-buffer is used: */
876 if (m_fUnused)
877 {
878 LogRel2(("GUI: UIFrameBufferPrivate::Notify3DEvent: Ignored!\n"));
879
880 /* Unlock access to frame-buffer: */
881 unlock();
882
883 /* Ignore Notify3DEvent: */
884 return E_FAIL;
885 }
886
887 Q_UNUSED(data);
888#ifdef VBOX_WITH_XPCOM
889 Q_UNUSED(dataSize);
890#endif /* VBOX_WITH_XPCOM */
891 // com::SafeArray<BYTE> eventData(ComSafeArrayInArg(data));
892 switch (uType)
893 {
894 case VBOX3D_NOTIFY_TYPE_3DDATA_VISIBLE:
895 case VBOX3D_NOTIFY_TYPE_3DDATA_HIDDEN:
896 {
897 /// @todo wipe out whole case when confirmed
898 // We are no more supporting this, am I right?
899 AssertFailed();
900 /* Confirm Notify3DEvent: */
901 return S_OK;
902 }
903
904 case VBOX3D_NOTIFY_TYPE_TEST_FUNCTIONAL:
905 {
906 HRESULT hr = m_fUnused ? E_FAIL : S_OK;
907 unlock();
908 return hr;
909 }
910
911 default:
912 break;
913 }
914
915 /* Unlock access to frame-buffer: */
916 unlock();
917
918 /* Ignore Notify3DEvent: */
919 return E_INVALIDARG;
920}
921
922void UIFrameBufferPrivate::handleNotifyChange(int iWidth, int iHeight)
923{
924 LogRel2(("GUI: UIFrameBufferPrivate::handleNotifyChange: Size=%dx%d\n", iWidth, iHeight));
925
926 /* Make sure machine-view is assigned: */
927 AssertPtrReturnVoid(m_pMachineView);
928
929 /* Lock access to frame-buffer: */
930 lock();
931
932 /* If there is NO pending source-bitmap: */
933 if (!uiCommon().isSeparateProcess() && !m_fPendingSourceBitmap)
934 {
935 /* Do nothing, change-event already processed: */
936 LogRel2(("GUI: UIFrameBufferPrivate::handleNotifyChange: Already processed.\n"));
937 /* Unlock access to frame-buffer: */
938 unlock();
939 /* Return immediately: */
940 return;
941 }
942
943 /* Release the current bitmap and keep the pending one: */
944 m_sourceBitmap = m_pendingSourceBitmap;
945 m_pendingSourceBitmap = 0;
946 m_fPendingSourceBitmap = false;
947
948 /* Unlock access to frame-buffer: */
949 unlock();
950
951 /* Perform frame-buffer resize: */
952 performResize(iWidth, iHeight);
953}
954
955void UIFrameBufferPrivate::handlePaintEvent(QPaintEvent *pEvent)
956{
957 LogRel3(("GUI: UIFrameBufferPrivate::handlePaintEvent: Origin=%lux%lu, Size=%dx%d\n",
958 pEvent->rect().x(), pEvent->rect().y(),
959 pEvent->rect().width(), pEvent->rect().height()));
960
961 /* On mode switch the enqueued paint-event may still come
962 * while the machine-view is already null (before the new machine-view set),
963 * ignore paint-event in that case. */
964 if (!m_pMachineView)
965 return;
966
967 /* Lock access to frame-buffer: */
968 lock();
969
970 /* But if updates disabled: */
971 if (!m_fUpdatesAllowed)
972 {
973 /* Unlock access to frame-buffer: */
974 unlock();
975 /* And return immediately: */
976 return;
977 }
978
979 /* Depending on visual-state type: */
980 switch (m_pMachineView->machineLogic()->visualStateType())
981 {
982 case UIVisualStateType_Seamless:
983 paintSeamless(pEvent);
984 break;
985 default:
986 paintDefault(pEvent);
987 break;
988 }
989
990 /* Unlock access to frame-buffer: */
991 unlock();
992}
993
994void UIFrameBufferPrivate::handleSetVisibleRegion(const QRegion &region)
995{
996 /* Make sure async visible-region has changed or wasn't yet applied: */
997 if ( m_asyncVisibleRegion == region
998#ifdef VBOX_WITH_MASKED_SEAMLESS
999 && m_asyncVisibleRegion == m_pMachineView->machineWindow()->mask()
1000#endif /* VBOX_WITH_MASKED_SEAMLESS */
1001 )
1002 return;
1003
1004 /* We are accounting async visible-regions one-by-one
1005 * to keep corresponding viewport area always updated! */
1006 if (!m_asyncVisibleRegion.isEmpty())
1007 m_pMachineView->viewport()->update(m_asyncVisibleRegion - region);
1008
1009 /* Remember last visible region: */
1010 m_asyncVisibleRegion = region;
1011
1012#ifdef VBOX_WITH_MASKED_SEAMLESS
1013 /* We have to use async visible-region to apply to [Q]Widget [set]Mask: */
1014 m_pMachineView->machineWindow()->setMask(m_asyncVisibleRegion);
1015#endif /* VBOX_WITH_MASKED_SEAMLESS */
1016}
1017
1018void UIFrameBufferPrivate::performResize(int iWidth, int iHeight)
1019{
1020 /* Make sure machine-view is assigned: */
1021 AssertReturnVoidStmt(m_pMachineView, LogRel(("GUI: UIFrameBufferPrivate::performResize: Size=%dx%d\n", iWidth, iHeight)));
1022
1023 /* Invalidate visible-region (if necessary): */
1024 if (m_pMachineView->machineLogic()->visualStateType() == UIVisualStateType_Seamless &&
1025 (m_iWidth != iWidth || m_iHeight != iHeight))
1026 {
1027 lock();
1028 m_syncVisibleRegion = QRegion();
1029 m_asyncVisibleRegion = QRegion();
1030 unlock();
1031 }
1032
1033 /* If source-bitmap invalid: */
1034 if (m_sourceBitmap.isNull())
1035 {
1036 /* Remember new size came from hint: */
1037 m_iWidth = iWidth;
1038 m_iHeight = iHeight;
1039 LogRel(("GUI: UIFrameBufferPrivate::performResize: Size=%dx%d, Using fallback buffer since no source bitmap is provided\n",
1040 m_iWidth, m_iHeight));
1041
1042 /* And recreate fallback buffer: */
1043 m_image = QImage(m_iWidth, m_iHeight, QImage::Format_RGB32);
1044 m_image.fill(0);
1045 }
1046 /* If source-bitmap valid: */
1047 else
1048 {
1049 /* Acquire source-bitmap attributes: */
1050 BYTE *pAddress = NULL;
1051 ULONG ulWidth = 0;
1052 ULONG ulHeight = 0;
1053 ULONG ulBitsPerPixel = 0;
1054 ULONG ulBytesPerLine = 0;
1055 KBitmapFormat bitmapFormat = KBitmapFormat_Opaque;
1056 m_sourceBitmap.QueryBitmapInfo(pAddress,
1057 ulWidth,
1058 ulHeight,
1059 ulBitsPerPixel,
1060 ulBytesPerLine,
1061 bitmapFormat);
1062 Assert(ulBitsPerPixel == 32);
1063
1064 /* Remember new actual size: */
1065 m_iWidth = (int)ulWidth;
1066 m_iHeight = (int)ulHeight;
1067 LogRel2(("GUI: UIFrameBufferPrivate::performResize: Size=%dx%d, Directly using source bitmap content\n",
1068 m_iWidth, m_iHeight));
1069
1070 /* Recreate QImage on the basis of source-bitmap content: */
1071 m_image = QImage(pAddress, m_iWidth, m_iHeight, ulBytesPerLine, QImage::Format_RGB32);
1072
1073 /* Check whether guest color depth differs from the bitmap color depth: */
1074 ULONG ulGuestBitsPerPixel = 0;
1075 LONG xOrigin = 0;
1076 LONG yOrigin = 0;
1077 KGuestMonitorStatus monitorStatus = KGuestMonitorStatus_Enabled;
1078 display().GetScreenResolution(m_uScreenId, ulWidth, ulHeight, ulGuestBitsPerPixel, xOrigin, yOrigin, monitorStatus);
1079
1080 /* Remind user if necessary, ignore text and VGA modes: */
1081 /* This check (supports graphics) is not quite right due to past mistakes
1082 * in the Guest Additions protocol, but in practice it should be fine. */
1083 if ( ulGuestBitsPerPixel != ulBitsPerPixel
1084 && ulGuestBitsPerPixel != 0
1085 && m_pMachineView->uimachine()->isGuestSupportsGraphics())
1086 UINotificationMessage::remindAboutWrongColorDepth(ulGuestBitsPerPixel, ulBitsPerPixel);
1087 else
1088 UINotificationMessage::forgetAboutWrongColorDepth();
1089 }
1090
1091 lock();
1092
1093 /* Enable screen updates: */
1094 m_fUpdatesAllowed = true;
1095
1096 if (!m_pendingSyncVisibleRegion.isEmpty())
1097 {
1098 /* Directly update synchronous visible-region: */
1099 m_syncVisibleRegion = m_pendingSyncVisibleRegion;
1100 m_pendingSyncVisibleRegion = QRegion();
1101
1102 /* And send async-signal to update asynchronous one: */
1103 LogRel2(("GUI: UIFrameBufferPrivate::performResize: Rectangle count=%lu, Sending to async-handler\n",
1104 (unsigned long)m_syncVisibleRegion.rectCount()));
1105 emit sigSetVisibleRegion(m_syncVisibleRegion);
1106 }
1107
1108 /* Make sure that the current screen image is immediately displayed: */
1109 m_pMachineView->viewport()->update();
1110
1111 unlock();
1112
1113 /* Make sure action-pool knows frame-buffer size: */
1114 m_pMachineView->uimachine()->actionPool()->toRuntime()->setGuestScreenSize(m_pMachineView->screenId(),
1115 QSize(m_iWidth, m_iHeight));
1116}
1117
1118void UIFrameBufferPrivate::performRescale()
1119{
1120// printf("UIFrameBufferPrivate::performRescale\n");
1121
1122 /* Make sure machine-view is assigned: */
1123 AssertPtrReturnVoid(m_pMachineView);
1124
1125 /* Depending on current visual state: */
1126 switch (m_pMachineView->machineLogic()->visualStateType())
1127 {
1128 case UIVisualStateType_Scale:
1129 m_scaledSize = scaledSize().width() == m_iWidth && scaledSize().height() == m_iHeight ? QSize() : scaledSize();
1130 break;
1131 default:
1132 m_scaledSize = scaleFactor() == 1.0 ? QSize() : QSize((int)(m_iWidth * scaleFactor()), (int)(m_iHeight * scaleFactor()));
1133 break;
1134 }
1135
1136 /* Update coordinate-system: */
1137 updateCoordinateSystem();
1138
1139// printf("UIFrameBufferPrivate::performRescale: Complete: Scale-factor=%f, Scaled-size=%dx%d\n",
1140// scaleFactor(), scaledSize().width(), scaledSize().height());
1141}
1142
1143void UIFrameBufferPrivate::sltMousePointerShapeOrPositionChange()
1144{
1145 /* Do we have view and valid cursor position?
1146 * Also, please take into account, we are not currently painting
1147 * framebuffer cursor if mouse integration is supported and enabled. */
1148 if ( m_pMachineView
1149 && !m_pMachineView->uimachine()->isHidingHostPointer()
1150 && m_pMachineView->uimachine()->isValidPointerShapePresent()
1151 && m_pMachineView->uimachine()->isValidCursorPositionPresent()
1152 && ( !m_pMachineView->uimachine()->isMouseIntegrated()
1153 || !m_pMachineView->uimachine()->isMouseSupportsAbsolute()))
1154 {
1155 /* Acquire cursor hotspot: */
1156 QPoint cursorHotspot = m_pMachineView->uimachine()->cursorHotspot();
1157 /* Apply the scale-factor if necessary: */
1158 cursorHotspot /= scaleFactor();
1159 /* Take the device-pixel-ratio into account: */
1160 if (!useUnscaledHiDPIOutput())
1161 cursorHotspot /= devicePixelRatioActual();
1162
1163 /* Acquire cursor position and size: */
1164 QPoint cursorPosition = m_pMachineView->uimachine()->cursorPosition() - cursorHotspot;
1165 QSize cursorSize = m_pMachineView->uimachine()->cursorSize();
1166 /* Apply the scale-factor if necessary: */
1167 cursorPosition *= scaleFactor();
1168 cursorSize *= scaleFactor();
1169 /* Take the device-pixel-ratio into account: */
1170 if (!useUnscaledHiDPIOutput())
1171 {
1172 cursorPosition *= devicePixelRatioActual();
1173 cursorSize *= devicePixelRatioActual();
1174 }
1175 cursorPosition /= devicePixelRatio();
1176 cursorSize /= devicePixelRatio();
1177
1178 /* Call for a viewport update, we need to update cumulative
1179 * region containing previous and current cursor rectagles. */
1180 const QRect cursorRectangle = QRect(cursorPosition, cursorSize);
1181 m_pMachineView->viewport()->update(QRegion(m_cursorRectangle) + cursorRectangle);
1182
1183 /* Remember current cursor rectangle: */
1184 m_cursorRectangle = cursorRectangle;
1185 }
1186 /* Don't forget to clear the rectangle in opposite case: */
1187 else if ( m_pMachineView
1188 && m_cursorRectangle.isValid())
1189 {
1190 /* Call for a cursor area update: */
1191 m_pMachineView->viewport()->update(m_cursorRectangle);
1192 }
1193}
1194
1195void UIFrameBufferPrivate::prepareConnections()
1196{
1197 /* Attach EMT connections: */
1198 connect(this, &UIFrameBufferPrivate::sigNotifyChange,
1199 m_pMachineView, &UIMachineView::sltHandleNotifyChange,
1200 Qt::QueuedConnection);
1201 connect(this, &UIFrameBufferPrivate::sigNotifyUpdate,
1202 m_pMachineView, &UIMachineView::sltHandleNotifyUpdate,
1203 Qt::QueuedConnection);
1204 connect(this, &UIFrameBufferPrivate::sigSetVisibleRegion,
1205 m_pMachineView, &UIMachineView::sltHandleSetVisibleRegion,
1206 Qt::QueuedConnection);
1207
1208 /* Attach GUI connections: */
1209 connect(m_pMachineView->uimachine(), &UIMachine::sigMousePointerShapeChange,
1210 this, &UIFrameBufferPrivate::sltMousePointerShapeOrPositionChange);
1211 connect(m_pMachineView->uimachine(), &UIMachine::sigCursorPositionChange,
1212 this, &UIFrameBufferPrivate::sltMousePointerShapeOrPositionChange);
1213}
1214
1215void UIFrameBufferPrivate::cleanupConnections()
1216{
1217 /* Detach EMT connections: */
1218 disconnect(this, &UIFrameBufferPrivate::sigNotifyChange,
1219 m_pMachineView, &UIMachineView::sltHandleNotifyChange);
1220 disconnect(this, &UIFrameBufferPrivate::sigNotifyUpdate,
1221 m_pMachineView, &UIMachineView::sltHandleNotifyUpdate);
1222 disconnect(this, &UIFrameBufferPrivate::sigSetVisibleRegion,
1223 m_pMachineView, &UIMachineView::sltHandleSetVisibleRegion);
1224
1225 /* Detach GUI connections: */
1226 disconnect(m_pMachineView->uimachine(), &UIMachine::sigMousePointerShapeChange,
1227 this, &UIFrameBufferPrivate::sltMousePointerShapeOrPositionChange);
1228 disconnect(m_pMachineView->uimachine(), &UIMachine::sigCursorPositionChange,
1229 this, &UIFrameBufferPrivate::sltMousePointerShapeOrPositionChange);
1230}
1231
1232void UIFrameBufferPrivate::updateCoordinateSystem()
1233{
1234 /* Reset to default: */
1235 m_transform = QTransform();
1236
1237 /* Apply the scale-factor if necessary: */
1238 if (scaleFactor() != 1.0)
1239 m_transform = m_transform.scale(scaleFactor(), scaleFactor());
1240
1241 /* Take the device-pixel-ratio into account: */
1242 if (!useUnscaledHiDPIOutput())
1243 m_transform = m_transform.scale(devicePixelRatioActual(), devicePixelRatioActual());
1244 m_transform = m_transform.scale(1.0 / devicePixelRatio(), 1.0 / devicePixelRatio());
1245}
1246
1247void UIFrameBufferPrivate::paintDefault(QPaintEvent *pEvent)
1248{
1249 /* Make sure cached image is valid: */
1250 if (m_image.isNull())
1251 return;
1252
1253 /* First we take the cached image as the source: */
1254 QImage *pSourceImage = &m_image;
1255
1256 /* But if we should scale image by some reason: */
1257 if ( scaledSize().isValid()
1258 || (!useUnscaledHiDPIOutput() && devicePixelRatioActual() != 1.0))
1259 {
1260 /* Calculate final scaled size: */
1261 QSize effectiveSize = !scaledSize().isValid() ? pSourceImage->size() : scaledSize();
1262 /* Take the device-pixel-ratio into account: */
1263 if (!useUnscaledHiDPIOutput() && devicePixelRatioActual() != 1.0)
1264 effectiveSize *= devicePixelRatioActual();
1265 /* We scale the image to requested size and retain it
1266 * by making heap shallow copy of that temporary object: */
1267 switch (m_pMachineView->visualStateType())
1268 {
1269 case UIVisualStateType_Scale:
1270 pSourceImage = new QImage(pSourceImage->scaled(effectiveSize, Qt::IgnoreAspectRatio,
1271 transformationMode(scalingOptimizationType())));
1272 break;
1273 default:
1274 pSourceImage = new QImage(pSourceImage->scaled(effectiveSize, Qt::IgnoreAspectRatio,
1275 transformationMode(scalingOptimizationType(), m_dScaleFactor)));
1276 break;
1277 }
1278 }
1279
1280 /* Take the device-pixel-ratio into account: */
1281 pSourceImage->setDevicePixelRatio(devicePixelRatio());
1282
1283 /* Prepare the base and hidpi paint rectangles: */
1284 const QRect paintRect = pEvent->rect();
1285 QRect paintRectHiDPI = paintRect;
1286
1287 /* Take the device-pixel-ratio into account: */
1288 paintRectHiDPI.moveTo(paintRectHiDPI.topLeft() * devicePixelRatio());
1289 paintRectHiDPI.setSize(paintRectHiDPI.size() * devicePixelRatio());
1290
1291 /* Make sure hidpi paint rectangle is within the image boundary: */
1292 paintRectHiDPI = paintRectHiDPI.intersected(pSourceImage->rect());
1293 if (paintRectHiDPI.isEmpty())
1294 return;
1295
1296 /* Create painter: */
1297 QPainter painter(m_pMachineView->viewport());
1298
1299#ifdef VBOX_WS_MAC
1300 /* On OSX for Qt5 we need to fill the backing store first: */
1301 painter.setCompositionMode(QPainter::CompositionMode_Source);
1302 painter.fillRect(paintRect, QColor(Qt::black));
1303 painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
1304#endif /* VBOX_WS_MAC */
1305
1306 /* Draw hidpi image rectangle: */
1307 drawImageRect(painter, *pSourceImage, paintRectHiDPI,
1308 m_pMachineView->contentsX(), m_pMachineView->contentsY(),
1309 devicePixelRatio());
1310
1311 /* If we had to scale image for some reason: */
1312 if ( scaledSize().isValid()
1313 || (!useUnscaledHiDPIOutput() && devicePixelRatioActual() != 1.0))
1314 {
1315 /* Wipe out copied image: */
1316 delete pSourceImage;
1317 pSourceImage = 0;
1318 }
1319
1320 /* Paint cursor if it has valid shape and position.
1321 * Also, please take into account, we are not currently painting
1322 * framebuffer cursor if mouse integration is supported and enabled. */
1323 if ( !m_cursorRectangle.isNull()
1324 && !m_pMachineView->uimachine()->isHidingHostPointer()
1325 && m_pMachineView->uimachine()->isValidPointerShapePresent()
1326 && m_pMachineView->uimachine()->isValidCursorPositionPresent()
1327 && ( !m_pMachineView->uimachine()->isMouseIntegrated()
1328 || !m_pMachineView->uimachine()->isMouseSupportsAbsolute()))
1329 {
1330 /* Acquire session cursor shape pixmap: */
1331 QPixmap cursorPixmap = m_pMachineView->uimachine()->cursorShapePixmap();
1332
1333 /* Take the device-pixel-ratio into account: */
1334 cursorPixmap.setDevicePixelRatio(devicePixelRatio());
1335
1336 /* Draw sub-pixmap: */
1337 painter.drawPixmap(m_cursorRectangle.topLeft(), cursorPixmap);
1338 }
1339}
1340
1341void UIFrameBufferPrivate::paintSeamless(QPaintEvent *pEvent)
1342{
1343 /* Make sure cached image is valid: */
1344 if (m_image.isNull())
1345 return;
1346
1347 /* First we take the cached image as the source: */
1348 QImage *pSourceImage = &m_image;
1349
1350 /* But if we should scale image by some reason: */
1351 if ( scaledSize().isValid()
1352 || (!useUnscaledHiDPIOutput() && devicePixelRatioActual() != 1.0))
1353 {
1354 /* Calculate final scaled size: */
1355 QSize effectiveSize = !scaledSize().isValid() ? pSourceImage->size() : scaledSize();
1356 /* Take the device-pixel-ratio into account: */
1357 if (!useUnscaledHiDPIOutput() && devicePixelRatioActual() != 1.0)
1358 effectiveSize *= devicePixelRatioActual();
1359 /* We scale the image to requested size and retain it
1360 * by making heap shallow copy of that temporary object: */
1361 switch (m_pMachineView->visualStateType())
1362 {
1363 case UIVisualStateType_Scale:
1364 pSourceImage = new QImage(pSourceImage->scaled(effectiveSize, Qt::IgnoreAspectRatio,
1365 transformationMode(scalingOptimizationType())));
1366 break;
1367 default:
1368 pSourceImage = new QImage(pSourceImage->scaled(effectiveSize, Qt::IgnoreAspectRatio,
1369 transformationMode(scalingOptimizationType(), m_dScaleFactor)));
1370 break;
1371 }
1372 }
1373
1374 /* Take the device-pixel-ratio into account: */
1375 pSourceImage->setDevicePixelRatio(devicePixelRatio());
1376
1377 /* Prepare the base and hidpi paint rectangles: */
1378 const QRect paintRect = pEvent->rect();
1379 QRect paintRectHiDPI = paintRect;
1380
1381 /* Take the device-pixel-ratio into account: */
1382 paintRectHiDPI.moveTo(paintRectHiDPI.topLeft() * devicePixelRatio());
1383 paintRectHiDPI.setSize(paintRectHiDPI.size() * devicePixelRatio());
1384
1385 /* Make sure hidpi paint rectangle is within the image boundary: */
1386 paintRectHiDPI = paintRectHiDPI.intersected(pSourceImage->rect());
1387 if (paintRectHiDPI.isEmpty())
1388 return;
1389
1390 /* Create painter: */
1391 QPainter painter(m_pMachineView->viewport());
1392
1393 /* Adjust painter for erasing: */
1394 lock();
1395 painter.setClipRegion(QRegion(paintRect) - m_syncVisibleRegion);
1396 painter.setCompositionMode(QPainter::CompositionMode_Clear);
1397 unlock();
1398
1399 /* Erase hidpi rectangle: */
1400 eraseImageRect(painter, paintRectHiDPI,
1401 devicePixelRatio());
1402
1403 /* Adjust painter for painting: */
1404 lock();
1405 painter.setClipRegion(QRegion(paintRect) & m_syncVisibleRegion);
1406 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
1407 unlock();
1408
1409#ifdef VBOX_WITH_TRANSLUCENT_SEAMLESS
1410 /* In case of translucent seamless for Qt5 we need to fill the backing store first: */
1411 painter.setCompositionMode(QPainter::CompositionMode_Source);
1412 painter.fillRect(paintRect, QColor(Qt::black));
1413 painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
1414#endif /* VBOX_WITH_TRANSLUCENT_SEAMLESS */
1415
1416 /* Draw hidpi image rectangle: */
1417 drawImageRect(painter, *pSourceImage, paintRectHiDPI,
1418 m_pMachineView->contentsX(), m_pMachineView->contentsY(),
1419 devicePixelRatio());
1420
1421 /* If we had to scale image for some reason: */
1422 if ( scaledSize().isValid()
1423 || (!useUnscaledHiDPIOutput() && devicePixelRatioActual() != 1.0))
1424 {
1425 /* Wipe out copied image: */
1426 delete pSourceImage;
1427 pSourceImage = 0;
1428 }
1429
1430 /* Paint cursor if it has valid shape and position.
1431 * Also, please take into account, we are not currently painting
1432 * framebuffer cursor if mouse integration is supported and enabled. */
1433 if ( !m_cursorRectangle.isNull()
1434 && !m_pMachineView->uimachine()->isHidingHostPointer()
1435 && m_pMachineView->uimachine()->isValidPointerShapePresent()
1436 && m_pMachineView->uimachine()->isValidCursorPositionPresent()
1437 && ( !m_pMachineView->uimachine()->isMouseIntegrated()
1438 || !m_pMachineView->uimachine()->isMouseSupportsAbsolute()))
1439 {
1440 /* Acquire session cursor shape pixmap: */
1441 QPixmap cursorPixmap = m_pMachineView->uimachine()->cursorShapePixmap();
1442
1443 /* Take the device-pixel-ratio into account: */
1444 cursorPixmap.setDevicePixelRatio(devicePixelRatio());
1445
1446 /* Draw sub-pixmap: */
1447 painter.drawPixmap(m_cursorRectangle.topLeft(), cursorPixmap);
1448 }
1449}
1450
1451/* static */
1452Qt::TransformationMode UIFrameBufferPrivate::transformationMode(ScalingOptimizationType type, double dScaleFactor /* = 0 */)
1453{
1454 switch (type)
1455 {
1456 /* Check if optimization type is forced to be 'Performance': */
1457 case ScalingOptimizationType_Performance: return Qt::FastTransformation;
1458 default: break;
1459 }
1460 /* For integer-scaling we are choosing the 'Performance' optimization type ourselves: */
1461 return dScaleFactor && floor(dScaleFactor) == dScaleFactor ? Qt::FastTransformation : Qt::SmoothTransformation;;
1462}
1463
1464/* static */
1465void UIFrameBufferPrivate::eraseImageRect(QPainter &painter, const QRect &rect,
1466 double dDevicePixelRatio)
1467{
1468 /* Prepare sub-pixmap: */
1469 QPixmap subPixmap = QPixmap(rect.width(), rect.height());
1470 /* Take the device-pixel-ratio into account: */
1471 subPixmap.setDevicePixelRatio(dDevicePixelRatio);
1472
1473 /* Which point we should draw corresponding sub-pixmap? */
1474 QPoint paintPoint = rect.topLeft();
1475 /* Take the device-pixel-ratio into account: */
1476 paintPoint /= dDevicePixelRatio;
1477
1478 /* Draw sub-pixmap: */
1479 painter.drawPixmap(paintPoint, subPixmap);
1480}
1481
1482/* static */
1483void UIFrameBufferPrivate::drawImageRect(QPainter &painter, const QImage &image, const QRect &rect,
1484 int iContentsShiftX, int iContentsShiftY,
1485 double dDevicePixelRatio)
1486{
1487 /* Calculate offset: */
1488 const size_t offset = (rect.x() + iContentsShiftX) * image.depth() / 8 +
1489 (rect.y() + iContentsShiftY) * image.bytesPerLine();
1490
1491 /* Restrain boundaries: */
1492 const int iSubImageWidth = qMin(rect.width(), image.width() - rect.x() - iContentsShiftX);
1493 const int iSubImageHeight = qMin(rect.height(), image.height() - rect.y() - iContentsShiftY);
1494
1495 /* Create sub-image (no copy involved): */
1496 QImage subImage = QImage(image.bits() + offset,
1497 iSubImageWidth, iSubImageHeight,
1498 image.bytesPerLine(), image.format());
1499
1500 /* Create sub-pixmap on the basis of sub-image above (1st copy involved): */
1501 QPixmap subPixmap = QPixmap::fromImage(subImage);
1502 /* Take the device-pixel-ratio into account: */
1503 subPixmap.setDevicePixelRatio(dDevicePixelRatio);
1504
1505 /* Which point we should draw corresponding sub-pixmap? */
1506 QPoint paintPoint = rect.topLeft();
1507 /* Take the device-pixel-ratio into account: */
1508 paintPoint /= dDevicePixelRatio;
1509
1510 /* Draw sub-pixmap: */
1511 painter.drawPixmap(paintPoint, subPixmap);
1512}
1513
1514
1515UIFrameBuffer::UIFrameBuffer()
1516 : m_fInitialized(false)
1517{
1518 prepare();
1519}
1520
1521UIFrameBuffer::~UIFrameBuffer()
1522{
1523 cleanup();
1524}
1525
1526HRESULT UIFrameBuffer::init(UIMachineView *pMachineView)
1527{
1528 const HRESULT rc = m_pFrameBuffer->init(pMachineView);
1529 m_fInitialized = true;
1530 return rc;
1531}
1532
1533void UIFrameBuffer::attach()
1534{
1535 m_pFrameBuffer->attach();
1536}
1537
1538void UIFrameBuffer::detach()
1539{
1540 m_pFrameBuffer->detach();
1541}
1542
1543uchar *UIFrameBuffer::address()
1544{
1545 return m_pFrameBuffer->address();
1546}
1547
1548ulong UIFrameBuffer::width() const
1549{
1550 return m_pFrameBuffer->width();
1551}
1552
1553ulong UIFrameBuffer::height() const
1554{
1555 return m_pFrameBuffer->height();
1556}
1557
1558ulong UIFrameBuffer::bitsPerPixel() const
1559{
1560 return m_pFrameBuffer->bitsPerPixel();
1561}
1562
1563ulong UIFrameBuffer::bytesPerLine() const
1564{
1565 return m_pFrameBuffer->bytesPerLine();
1566}
1567
1568void UIFrameBuffer::setView(UIMachineView *pMachineView)
1569{
1570 m_pFrameBuffer->setView(pMachineView);
1571}
1572
1573void UIFrameBuffer::setMarkAsUnused(bool fUnused)
1574{
1575 m_pFrameBuffer->setMarkAsUnused(fUnused);
1576}
1577
1578QSize UIFrameBuffer::scaledSize() const
1579{
1580 return m_pFrameBuffer->scaledSize();
1581}
1582
1583void UIFrameBuffer::setScaledSize(const QSize &size /* = QSize() */)
1584{
1585 m_pFrameBuffer->setScaledSize(size);
1586}
1587
1588int UIFrameBuffer::convertHostXTo(int iX) const
1589{
1590 return m_pFrameBuffer->convertHostXTo(iX);
1591}
1592
1593int UIFrameBuffer::convertHostYTo(int iY) const
1594{
1595 return m_pFrameBuffer->convertHostXTo(iY);
1596}
1597
1598double UIFrameBuffer::scaleFactor() const
1599{
1600 return m_pFrameBuffer->scaleFactor();
1601}
1602
1603void UIFrameBuffer::setScaleFactor(double dScaleFactor)
1604{
1605 m_pFrameBuffer->setScaleFactor(dScaleFactor);
1606}
1607
1608double UIFrameBuffer::devicePixelRatio() const
1609{
1610 return m_pFrameBuffer->devicePixelRatio();
1611}
1612
1613void UIFrameBuffer::setDevicePixelRatio(double dDevicePixelRatio)
1614{
1615 m_pFrameBuffer->setDevicePixelRatio(dDevicePixelRatio);
1616}
1617
1618double UIFrameBuffer::devicePixelRatioActual() const
1619{
1620 return m_pFrameBuffer->devicePixelRatioActual();
1621}
1622
1623void UIFrameBuffer::setDevicePixelRatioActual(double dDevicePixelRatioActual)
1624{
1625 m_pFrameBuffer->setDevicePixelRatioActual(dDevicePixelRatioActual);
1626}
1627
1628bool UIFrameBuffer::useUnscaledHiDPIOutput() const
1629{
1630 return m_pFrameBuffer->useUnscaledHiDPIOutput();
1631}
1632
1633void UIFrameBuffer::setUseUnscaledHiDPIOutput(bool fUseUnscaledHiDPIOutput)
1634{
1635 m_pFrameBuffer->setUseUnscaledHiDPIOutput(fUseUnscaledHiDPIOutput);
1636}
1637
1638ScalingOptimizationType UIFrameBuffer::scalingOptimizationType() const
1639{
1640 return m_pFrameBuffer->scalingOptimizationType();
1641}
1642
1643void UIFrameBuffer::setScalingOptimizationType(ScalingOptimizationType type)
1644{
1645 m_pFrameBuffer->setScalingOptimizationType(type);
1646}
1647
1648void UIFrameBuffer::handleNotifyChange(int iWidth, int iHeight)
1649{
1650 m_pFrameBuffer->handleNotifyChange(iWidth, iHeight);
1651}
1652
1653void UIFrameBuffer::handlePaintEvent(QPaintEvent *pEvent)
1654{
1655 m_pFrameBuffer->handlePaintEvent(pEvent);
1656}
1657
1658void UIFrameBuffer::handleSetVisibleRegion(const QRegion &region)
1659{
1660 m_pFrameBuffer->handleSetVisibleRegion(region);
1661}
1662
1663void UIFrameBuffer::performResize(int iWidth, int iHeight)
1664{
1665 m_pFrameBuffer->performResize(iWidth, iHeight);
1666}
1667
1668void UIFrameBuffer::performRescale()
1669{
1670 m_pFrameBuffer->performRescale();
1671}
1672
1673void UIFrameBuffer::viewportResized(QResizeEvent *pEvent)
1674{
1675 m_pFrameBuffer->viewportResized(pEvent);
1676}
1677
1678void UIFrameBuffer::prepare()
1679{
1680 /* Creates COM object we are linked to: */
1681 m_pFrameBuffer.createObject();
1682
1683 /* Take scaling optimization type into account: */
1684 setScalingOptimizationType(gEDataManager->scalingOptimizationType(uiCommon().managedVMUuid()));
1685}
1686
1687void UIFrameBuffer::cleanup()
1688{
1689 /* Detach COM object we are linked to: */
1690 m_pFrameBuffer.setNull();
1691}
1692
1693#include "UIFrameBuffer.moc"
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