VirtualBox

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

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

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

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

© 2023 Oracle
ContactPrivacy policyTerms of Use