VirtualBox

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

Last change on this file since 100064 was 100064, checked in by vboxsync, 12 months ago

FE/Qt: bugref:10421. Replacing VBOX_WS_X11 with VBOX_WS_NIX.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use