UIFrameBuffer.cpp revision 0a2cd044b0d76309f5ff2c89b30c9e2d3a724838
/* $Id$ */
/** @file
* VBox Qt GUI - UIFrameBuffer class implementation.
*/
/*
* Copyright (C) 2010-2014 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
# include <precomp.h>
#else /* !VBOX_WITH_PRECOMPILED_HEADERS */
/* Qt includes: */
# include <QPainter>
/* GUI includes: */
# include "UIFrameBuffer.h"
# include "UISession.h"
# include "UIMachineLogic.h"
# include "UIMachineWindow.h"
# include "UIMachineView.h"
# include "UIPopupCenter.h"
# include "UIExtraDataManager.h"
# include "VBoxGlobal.h"
# ifdef VBOX_WITH_MASKED_SEAMLESS
# include "UIMachineWindow.h"
# endif /* VBOX_WITH_MASKED_SEAMLESS */
/* COM includes: */
# include "CConsole.h"
# include "CDisplay.h"
#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
/* Other VBox includes: */
#include <VBox/VBoxVideo3D.h>
/* COM stuff: */
#ifdef Q_WS_WIN
static CComModule _Module;
#else /* !Q_WS_WIN */
#endif /* !Q_WS_WIN */
#ifdef Q_WS_X11
# include <QX11Info>
#endif /* Q_WS_X11 */
, m_iWinId(0)
, m_fUpdatesAllowed(true)
, m_fUnused(false)
, m_fAutoEnabled(false)
, m_dScaleFactor(1.0)
, m_dBackingScaleFactor(1.0)
, m_fUseUnscaledHiDPIOutput(false)
{
/* Update coordinate-system: */
}
{
LogRel2(("UIFrameBuffer::init %p\n", this));
/* Assign mahine-view: */
/* Cache window ID: */
m_iWinId = (m_pMachineView && m_pMachineView->viewport()) ? (LONG64)m_pMachineView->viewport()->winId() : 0;
/* Initialize critical-section: */
/* Connect handlers: */
if (m_pMachineView)
/* Resize frame-buffer to default size: */
#ifdef Q_OS_WIN
CoCreateFreeThreadedMarshaler(this, &m_pUnkMarshaler.p);
#endif /* Q_OS_WIN */
return S_OK;
}
{
LogRel2(("UIFrameBuffer::~UIFrameBuffer %p\n", this));
/* Disconnect handlers: */
if (m_pMachineView)
/* Deinitialize critical-section: */
}
{
return 0;
}
void UIFrameBuffer::FinalRelease()
{
return;
}
{
/* Disconnect old handlers: */
if (m_pMachineView)
/* Reassign machine-view: */
/* Recache window ID: */
m_iWinId = (m_pMachineView && m_pMachineView->viewport()) ? (LONG64)m_pMachineView->viewport()->winId() : 0;
#ifdef Q_WS_X11
/* Sync Qt and X11 Server. Notify server about newly
* created winId (see xTracker #7547). */
#endif
/* Connect new handlers: */
if (m_pMachineView)
}
{
lock();
unlock();
}
{
if (!puWidth)
return E_POINTER;
return S_OK;
}
{
if (!puHeight)
return E_POINTER;
return S_OK;
}
{
if (!puBitsPerPixel)
return E_POINTER;
*puBitsPerPixel = bitsPerPixel();
return S_OK;
}
{
if (!puBytesPerLine)
return E_POINTER;
*puBytesPerLine = bytesPerLine();
return S_OK;
}
{
if (!puPixelFormat)
return E_POINTER;
*puPixelFormat = pixelFormat();
return S_OK;
}
{
if (!puHeightReduction)
return E_POINTER;
*puHeightReduction = 0;
return S_OK;
}
{
if (!ppOverlay)
return E_POINTER;
*ppOverlay = 0;
return S_OK;
}
{
if (!pWinId)
return E_POINTER;
return S_OK;
}
STDMETHODIMP UIFrameBuffer::COMGETTER(Capabilities)(ComSafeArrayOut(FramebufferCapabilities_T, aCapabilities))
{
return E_POINTER;
if (vboxGlobal().isSeparateProcess())
{
}
else
{
}
return S_OK;
}
STDMETHODIMP UIFrameBuffer::NotifyChange(ULONG uScreenId, ULONG uX, ULONG uY, ULONG uWidth, ULONG uHeight)
{
if (!vboxGlobal().isSeparateProcess())
/* Lock access to frame-buffer: */
lock();
/* Make sure frame-buffer is used: */
if (m_fUnused)
{
LogRel(("UIFrameBuffer::NotifyChange: Screen=%lu, Origin=%lux%lu, Size=%lux%lu, Ignored!\n",
(unsigned long)uScreenId,
/* Unlock access to frame-buffer: */
unlock();
/* Ignore NotifyChange: */
return E_FAIL;
}
/* Disable screen updates: */
m_fUpdatesAllowed = false;
/* While updates are disabled, visible region will be saved: */
if (!vboxGlobal().isSeparateProcess())
{
/* Acquire new pending bitmap: */
}
/* Widget resize is NOT thread-safe and *probably* never will be,
* We have to notify machine-view with the async-signal to perform resize operation. */
LogRel(("UIFrameBuffer::NotifyChange: Screen=%lu, Origin=%lux%lu, Size=%lux%lu, Sending to async-handler\n",
(unsigned long)uScreenId,
/* Unlock access to frame-buffer: */
unlock();
/* Give up control token to other thread: */
/* Confirm NotifyChange: */
return S_OK;
}
{
/* Lock access to frame-buffer: */
lock();
/* Make sure frame-buffer is used: */
if (m_fUnused)
{
LogRel2(("UIFrameBuffer::NotifyUpdate: Origin=%lux%lu, Size=%lux%lu, Ignored!\n",
/* Unlock access to frame-buffer: */
unlock();
/* Ignore NotifyUpdate: */
return E_FAIL;
}
/* Widget update is NOT thread-safe and *seems* never will be,
* We have to notify machine-view with the async-signal to perform update operation. */
LogRel2(("UIFrameBuffer::NotifyUpdate: Origin=%lux%lu, Size=%lux%lu, Sending to async-handler\n",
/* Unlock access to frame-buffer: */
unlock();
/* Confirm NotifyUpdate: */
return S_OK;
}
{
/* Wrapping received data: */
/* Lock access to frame-buffer: */
lock();
/* Make sure frame-buffer is used: */
if (m_fUnused)
{
LogRel2(("UIFrameBuffer::NotifyUpdateImage: Origin=%lux%lu, Size=%lux%lu, Ignored!\n",
/* Unlock access to frame-buffer: */
unlock();
/* Ignore NotifyUpdate: */
return E_FAIL;
}
/* Directly update m_image: */
if (m_fUpdatesAllowed)
{
/* Copy to m_image: */
ULONG h;
for (h = 0; h < uHeight; ++h)
{
}
/* Widget update is NOT thread-safe and *seems* never will be,
* We have to notify machine-view with the async-signal to perform update operation. */
LogRel2(("UIFrameBuffer::NotifyUpdateImage: Origin=%lux%lu, Size=%lux%lu, Sending to async-handler\n",
}
/* Unlock access to frame-buffer: */
unlock();
/* Confirm NotifyUpdateImage: */
return S_OK;
}
STDMETHODIMP UIFrameBuffer::VideoModeSupported(ULONG uWidth, ULONG uHeight, ULONG uBPP, BOOL *pfSupported)
{
/* Make sure result pointer is valid: */
if (!pfSupported)
{
LogRel2(("UIFrameBuffer::IsVideoModeSupported: Mode: BPP=%lu, Size=%lux%lu, Invalid pfSupported pointer!\n",
return E_POINTER;
}
/* Lock access to frame-buffer: */
lock();
/* Make sure frame-buffer is used: */
if (m_fUnused)
{
LogRel2(("UIFrameBuffer::IsVideoModeSupported: Mode: BPP=%lu, Size=%lux%lu, Ignored!\n",
/* Unlock access to frame-buffer: */
unlock();
/* Ignore VideoModeSupported: */
return E_FAIL;
}
/* Determine if supported: */
*pfSupported = TRUE;
if ( (screenSize.width() != 0)
*pfSupported = FALSE;
if ( (screenSize.height() != 0)
*pfSupported = FALSE;
LogRel2(("UIFrameBuffer::IsVideoModeSupported: Mode: BPP=%lu, Size=%lux%lu, Supported=%s\n",
(unsigned long)uBPP, (unsigned long)uWidth, (unsigned long)uHeight, *pfSupported ? "TRUE" : "FALSE"));
/* Unlock access to frame-buffer: */
unlock();
/* Confirm VideoModeSupported: */
return S_OK;
}
{
if (!rects)
return E_POINTER;
return S_OK;
}
{
/* Make sure rectangles were passed: */
if (!pRectangles)
{
LogRel2(("UIFrameBuffer::SetVisibleRegion: Rectangle count=%lu, Invalid pRectangles pointer!\n",
(unsigned long)uCount));
return E_POINTER;
}
/* Lock access to frame-buffer: */
lock();
/* Make sure frame-buffer is used: */
if (m_fUnused)
{
LogRel2(("UIFrameBuffer::SetVisibleRegion: Rectangle count=%lu, Ignored!\n",
(unsigned long)uCount));
/* Unlock access to frame-buffer: */
unlock();
/* Ignore SetVisibleRegion: */
return E_FAIL;
}
/* Compose region: */
{
/* Get current rectangle: */
/* Which is inclusive: */
/* Append region: */
++rects;
}
/* Tune according scale-factor: */
if (m_fUpdatesAllowed)
{
/* We are directly updating synchronous visible-region: */
/* And send async-signal to update asynchronous one: */
LogRel2(("UIFrameBuffer::SetVisibleRegion: Rectangle count=%lu, Sending to async-handler\n",
(unsigned long)uCount));
}
else
{
/* Save the region. */
LogRel2(("UIFrameBuffer::SetVisibleRegion: Rectangle count=%lu, Saved\n",
(unsigned long)uCount));
}
/* Unlock access to frame-buffer: */
unlock();
/* Confirm SetVisibleRegion: */
return S_OK;
}
{
return E_NOTIMPL;
}
{
/* Lock access to frame-buffer: */
lock();
/* Make sure frame-buffer is used: */
if (m_fUnused)
{
LogRel2(("UIFrameBuffer::Notify3DEvent: Ignored!\n"));
/* Unlock access to frame-buffer: */
unlock();
/* Ignore Notify3DEvent: */
return E_FAIL;
}
switch (uType)
{
{
/* Notify machine-view with the async-signal
* about 3D overlay visibility change: */
LogRel2(("UIFrameBuffer::Notify3DEvent: Sending to async-handler: "
"(VBOX3D_NOTIFY_EVENT_TYPE_VISIBLE_3DDATA = %s)\n",
/* Unlock access to frame-buffer: */
unlock();
/* Confirm Notify3DEvent: */
return S_OK;
}
{
unlock();
return hr;
}
default:
break;
}
/* Unlock access to frame-buffer: */
unlock();
/* Ignore Notify3DEvent: */
return E_INVALIDARG;
}
{
/* Make sure machine-view is assigned: */
/* Lock access to frame-buffer: */
lock();
/* If there is NO pending source-bitmap: */
{
/* Do nothing, change-event already processed: */
LogRel2(("UIFrameBuffer::notifyChange: Already processed.\n"));
/* Unlock access to frame-buffer: */
unlock();
/* Return immediately: */
return;
}
/* Release the current bitmap and keep the pending one: */
/* Unlock access to frame-buffer: */
unlock();
/* Perform frame-buffer resize: */
}
{
/* Make sure machine-view is assigned: */
/* Invalidate visible-region (if necessary): */
{
lock();
unlock();
}
/* If source-bitmap invalid: */
if (m_sourceBitmap.isNull())
{
LogRel(("UIFrameBuffer::resizeEvent: "
"Using FALLBACK buffer due to source-bitmap is not provided..\n"));
/* Remember new size came from hint: */
/* And recreate fallback buffer: */
}
/* If source-bitmap valid: */
else
{
LogRel(("UIFrameBuffer::resizeEvent: "
"Directly using source-bitmap content\n"));
/* Acquire source-bitmap attributes: */
ULONG ulBitsPerPixel = 0;
ULONG ulBytesPerLine = 0;
ULONG ulPixelFormat = 0;
/* Remember new actual size: */
/* Recreate QImage on the basis of source-bitmap content: */
/* Check whether guest color depth differs from the bitmap color depth: */
ULONG ulGuestBitsPerPixel = 0;
/* Remind user if necessary, ignore text and VGA modes: */
/* This check (supports graphics) is not quite right due to past mistakes
* in the Guest Additions protocol, but in practice it should be fine. */
if ( ulGuestBitsPerPixel != ulBitsPerPixel
&& ulGuestBitsPerPixel != 0
else
}
lock();
/* Enable screen updates: */
m_fUpdatesAllowed = true;
if (!m_pendingSyncVisibleRegion.isEmpty())
{
/* Directly update synchronous visible-region: */
/* And send async-signal to update asynchronous one: */
LogRel2(("UIFrameBuffer::resizeEvent: Rectangle count=%lu, Sending to async-handler\n",
(unsigned long)m_syncVisibleRegion.rectCount()));
}
unlock();
/* Update scaled-size according scale-factor for modes except the 'Scale' one: */
setScaledSize(scaleFactor() == 1.0 ? QSize() : QSize(m_iWidth * scaleFactor(), m_iHeight * scaleFactor()));
}
{
LogRel2(("UIFrameBuffer::paintEvent: Origin=%lux%lu, Size=%dx%d\n",
/* On mode switch the enqueued paint-event may still come
* while the machine-view is already null (before the new machine-view set),
* ignore paint-event in that case. */
if (!m_pMachineView)
return;
/* Lock access to frame-buffer: */
lock();
/* But if updates disabled: */
if (!m_fUpdatesAllowed)
{
/* Unlock access to frame-buffer: */
unlock();
/* And return immediately: */
return;
}
/* Depending on visual-state type: */
{
break;
default:
break;
}
/* Unlock access to frame-buffer: */
unlock();
}
{
/* Make sure async visible-region has changed: */
if (m_asyncVisibleRegion == region)
return;
/* We are accounting async visible-regions one-by-one
* to keep corresponding viewport area always updated! */
if (!m_asyncVisibleRegion.isEmpty())
/* Remember last visible region: */
#ifdef VBOX_WITH_MASKED_SEAMLESS
/* We have to use async visible-region to apply to [Q]Widget [set]Mask: */
#endif /* VBOX_WITH_MASKED_SEAMLESS */
}
#ifdef VBOX_WITH_VIDEOHWACCEL
{
/* should never be here */
// TODO: Is this required? ^
}
#endif /* VBOX_WITH_VIDEOHWACCEL */
{
/* Remember new scale-factor: */
/* Update scaled-size according scale-factor: */
setScaledSize(scaleFactor() == 1.0 ? QSize() : QSize(m_iWidth * scaleFactor(), m_iHeight * scaleFactor()));
/* Update coordinate-system: */
}
{
/* Remember new backing-scale-factor: */
/* Update coordinate-system: */
}
{
/* Remember new use-unscaled-HiDPI-output value: */
/* Update coordinate-system: */
}
void UIFrameBuffer::prepareConnections()
{
/* EMT connections: */
}
void UIFrameBuffer::cleanupConnections()
{
/* EMT connections: */
}
void UIFrameBuffer::updateCoordinateSystem()
{
/* Reset to default: */
m_transform = QTransform();
/* Apply the scale-factor if necessary: */
if (scaleFactor() != 1.0)
/* Apply the backing-scale-factor if necessary: */
}
{
/* Scaled image is NULL by default: */
/* But if scaled-factor is set and current image is NOT null: */
{
/* We are doing a deep copy of the image to make sure it will not be
* detached during scale process, otherwise we can get a frozen frame-buffer. */
/* And scaling the image to predefined scaled-factor: */
}
/* Finally we are choosing image to paint from: */
/* Prepare the 'paint' rectangle: */
/* Take the backing-scale-factor into account: */
{
}
/* Make sure paint-rectangle is within the image boundary: */
return;
/* Create painter: */
/* Draw image rectangle: */
}
{
/* Scaled image is NULL by default: */
/* But if scaled-factor is set and current image is NOT null: */
{
/* We are doing a deep copy of the image to make sure it will not be
* detached during scale process, otherwise we can get a frozen frame-buffer. */
/* And scaling the image to predefined scaled-factor: */
}
/* Finally we are choosing image to paint from: */
/* Prepare the 'paint' rectangle: */
lock();
unlock();
/* Take the backing-scale-factor into account: */
{
}
/* Make sure paint-rectangle is within the image boundary: */
return;
/* Create painter: */
/* Apply painter clipping for erasing: */
/* Set composition-mode to erase: */
/* Erase rectangle: */
/* Apply painter clipping for painting: */
/* Set composition-mode to paint: */
#if defined(VBOX_WITH_TRANSLUCENT_SEAMLESS)
/* Replace translucent background with black one,
* that is necessary for window with Qt::WA_TranslucentBackground
* and no native backing store support like Mac OS X has: */
# endif /* Q_WS_WIN || Q_WS_X11 */
#endif /* VBOX_WITH_TRANSLUCENT_SEAMLESS */
/* Paint rectangle: */
}
/* static */
bool fUseUnscaledHiDPIOutput,
double dBackingScaleFactor)
{
/* Prepare sub-pixmap: */
/* If HiDPI 'backing-scale-factor' defined: */
if (dBackingScaleFactor > 1.0)
{
/* Should we
* perform logical HiDPI scaling and optimize it for performance? */
{
/* Adjust sub-pixmap: */
}
#ifdef Q_WS_MAC
# ifdef VBOX_GUI_WITH_HIDPI
/* Should we
* do not perform logical HiDPI scaling or
* perform logical HiDPI scaling and optimize it for performance? */
{
/* Mark sub-pixmap as HiDPI: */
}
# endif /* VBOX_GUI_WITH_HIDPI */
#endif /* Q_WS_MAC */
}
/* Which point we should draw corresponding sub-pixmap? */
/* Take the backing-scale-factor into account: */
/* Draw sub-pixmap: */
}
/* static */
int iContentsShiftX, int iContentsShiftY,
bool fUseUnscaledHiDPIOutput,
double dBackingScaleFactor)
{
/* Calculate offset: */
/* Restrain boundaries: */
/* Create sub-image (no copy involved): */
/* Create sub-pixmap on the basis of sub-image above (1st copy involved): */
/* If HiDPI 'backing-scale-factor' defined: */
if (dBackingScaleFactor > 1.0)
{
/* Should we
* perform logical HiDPI scaling and optimize it for performance? */
{
/* Fast scale sub-pixmap (2nd copy involved): */
}
#ifdef Q_WS_MAC
# ifdef VBOX_GUI_WITH_HIDPI
/* Should we
* do not perform logical HiDPI scaling or
* perform logical HiDPI scaling and optimize it for performance? */
{
/* Mark sub-pixmap as HiDPI: */
}
# endif /* VBOX_GUI_WITH_HIDPI */
#endif /* Q_WS_MAC */
}
/* Which point we should draw corresponding sub-pixmap? */
/* Take the backing-scale-factor into account: */
/* Draw sub-pixmap: */
}