VirtualBox

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

Last change on this file was 107411, checked in by vboxsync, 2 weeks ago

FE/Qt: bugref:3409: Runtime UI: Fixing parfait warnings in frame-buffer code, related to implicit type conversion.

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