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