Send events when platform surfaces are created/about to be destroyed

These synchronously delivered events allow applications to correctly
and conveniently handle native platform surfaces being destroyed. This
is particularly useful when doing rendering on a non-gui thread as it
allows to shutdown rendering before the native surface gets destroyed
from under us.

Task-number: QTBUG-42476
Task-number: QTBUG-42483
Change-Id: I63f41bbdb32f281d0f3b8ec2537eb2b0361f3bb3
Reviewed-by: Laszlo Agocs <laszlo.agocs@digia.com>
This commit is contained in:
Sean Harmer 2014-11-15 11:40:51 +00:00
parent 3d575d4845
commit c5ecabb70c
7 changed files with 160 additions and 1 deletions

View File

@ -183,6 +183,7 @@ QT_BEGIN_NAMESPACE
\value ParentAboutToChange The widget parent is about to change.
\value ParentChange The widget parent has changed.
\value PlatformPanel A platform specific panel has been requested.
\value PlatformSurface A native platform surface has been created or is about to be destroyed.
\value Polish The widget is polished.
\value PolishRequest The widget should be polished.
\value QueryWhatsThis The widget should accept the event if it has "What's This?" help.

View File

@ -275,6 +275,8 @@ public:
WindowChangeInternal = 215, // internal for QQuickWidget
ScreenChangeInternal = 216,
PlatformSurface = 217, // Platform surface created or about to be destroyed
// 512 reserved for Qt Jambi's MetaCall event
// 513 reserved for Qt Jambi's DeleteOnMainThread event

View File

@ -1323,6 +1323,55 @@ QExposeEvent::~QExposeEvent()
{
}
/*!
\class QPlatformSurfaceEvent
\since 5.5
\brief The QPlatformSurfaceEvent class is used to notify about native platform surface events.
\inmodule QtGui
\ingroup events
Platform window events are synchronously sent to windows and offscreen surfaces when their
underlying native surfaces are created or are about to be destroyed.
Applications can respond to these events to know when the underlying platform
surface exists.
*/
/*!
\enum QPlatformSurfaceEvent::SurfaceEventType
This enum describes the type of platform surface event. The possible types are:
\value SurfaceCreated The underlying native surface has been created
\value SurfaceAboutToBeDestroyed The underlying native surface will be destroyed immediately after this event
The \c SurfaceAboutToBeDestroyed event type is useful as a means of stopping rendering to
a platform window before it is destroyed.
*/
/*!
\fn QPlatformSurfaceEvent::SurfaceEventType QPlatformSurfaceEvent::surfaceEventType() const
Returns the specific type of platform surface event.
*/
/*!
Constructs a platform surface event for the given \a surfaceEventType.
*/
QPlatformSurfaceEvent::QPlatformSurfaceEvent(SurfaceEventType surfaceEventType)
: QEvent(PlatformSurface)
, m_surfaceEventType(surfaceEventType)
{
}
/*!
\internal
*/
QPlatformSurfaceEvent::~QPlatformSurfaceEvent()
{
}
/*!
\fn const QRegion &QExposeEvent::region() const

View File

@ -410,6 +410,23 @@ protected:
QRegion rgn;
};
class Q_GUI_EXPORT QPlatformSurfaceEvent : public QEvent
{
public:
enum SurfaceEventType {
SurfaceCreated,
SurfaceAboutToBeDestroyed
};
explicit QPlatformSurfaceEvent(SurfaceEventType surfaceEventType);
~QPlatformSurfaceEvent();
inline SurfaceEventType surfaceEventType() const { return m_surfaceEventType; }
protected:
SurfaceEventType m_surfaceEventType;
};
class Q_GUI_EXPORT QResizeEvent : public QEvent
{
public:

View File

@ -180,6 +180,9 @@ void QOffscreenSurface::create()
d->offscreenWindow->setGeometry(0, 0, d->size.width(), d->size.height());
d->offscreenWindow->create();
}
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
QGuiApplication::sendEvent(this, &e);
}
}
@ -191,6 +194,10 @@ void QOffscreenSurface::create()
void QOffscreenSurface::destroy()
{
Q_D(QOffscreenSurface);
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
QGuiApplication::sendEvent(this, &e);
delete d->platformOffscreenSurface;
d->platformOffscreenSurface = 0;
if (d->offscreenWindow) {

View File

@ -404,6 +404,11 @@ void QWindowPrivate::create(bool recursive)
window->d_func()->platformWindow->setParent(platformWindow);
}
}
if (platformWindow) {
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceCreated);
QGuiApplication::sendEvent(q, &e);
}
}
}
@ -1590,6 +1595,10 @@ void QWindow::destroy()
bool wasVisible = isVisible();
setVisible(false);
QPlatformSurfaceEvent e(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
QGuiApplication::sendEvent(this, &e);
delete d->platformWindow;
d->resizeEventPending = true;
d->receivedExpose = false;

View File

@ -63,6 +63,7 @@ private slots:
void positioning_data();
void positioning();
void positioningDuringMinimized();
void platformSurface();
void isExposed();
void isActive();
void testInputEvents();
@ -160,8 +161,18 @@ public:
{
m_received[event->type()]++;
m_order << event->type();
if (event->type() == QEvent::Expose)
switch (event->type()) {
case QEvent::Expose:
m_exposeRegion = static_cast<QExposeEvent *>(event)->region();
break;
case QEvent::PlatformSurface:
m_surfaceventType = static_cast<QPlatformSurfaceEvent *>(event)->surfaceEventType();
break;
default:
break;
}
return QWindow::event(event);
}
@ -181,10 +192,16 @@ public:
return m_exposeRegion;
}
QPlatformSurfaceEvent::SurfaceEventType surfaceEventType() const
{
return m_surfaceventType;
}
private:
QHash<QEvent::Type, int> m_received;
QVector<QEvent::Type> m_order;
QRegion m_exposeRegion;
QPlatformSurfaceEvent::SurfaceEventType m_surfaceventType;
};
void tst_QWindow::eventOrderOnShow()
@ -352,6 +369,63 @@ void tst_QWindow::positioningDuringMinimized()
QTRY_COMPARE(window.geometry(), newGeometry);
}
class PlatformWindowFilter : public QObject
{
Q_OBJECT
public:
PlatformWindowFilter(QObject *parent = 0)
: QObject(parent)
, m_window(Q_NULLPTR)
, m_alwaysExisted(true)
{}
void setWindow(Window *window) { m_window = window; }
bool eventFilter(QObject *o, QEvent *e)
{
// Check that the platform surface events are delivered synchronously.
// If they are, the native platform surface should always exist when we
// receive a QPlatformSurfaceEvent
if (e->type() == QEvent::PlatformSurface && o == m_window) {
m_alwaysExisted &= (m_window->handle() != Q_NULLPTR);
}
return false;
}
bool surfaceExisted() const { return m_alwaysExisted; }
private:
Window *m_window;
bool m_alwaysExisted;
};
void tst_QWindow::platformSurface()
{
QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);
Window window;
PlatformWindowFilter filter;
filter.setWindow(&window);
window.installEventFilter(&filter);
window.setGeometry(geometry);
QCOMPARE(window.geometry(), geometry);
window.create();
QTRY_VERIFY(window.received(QEvent::PlatformSurface) == 1);
QTRY_VERIFY(window.surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated);
QTRY_VERIFY(window.handle() != Q_NULLPTR);
window.destroy();
QTRY_VERIFY(window.received(QEvent::PlatformSurface) == 2);
QTRY_VERIFY(window.surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
QTRY_VERIFY(window.handle() == Q_NULLPTR);
// Check for synchronous delivery of platform surface events and that the platform
// surface always existed upon event delivery
QTRY_VERIFY(filter.surfaceExisted());
}
void tst_QWindow::isExposed()
{
QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize);