diff options
author | Sean Harmer <sean.harmer@kdab.com> | 2014-11-15 11:40:51 +0000 |
---|---|---|
committer | Sean Harmer <sean.harmer@kdab.com> | 2014-11-17 10:38:22 +0100 |
commit | c5ecabb70c0f0cb25a22bf3742f39648a34d1c3c (patch) | |
tree | e0c788b152d0cb7ec51d0d3e880aa0e286c9fd14 | |
parent | 3d575d4845926bd141ff0c14e57427bba79644d0 (diff) | |
download | qtbase-c5ecabb70c0f0cb25a22bf3742f39648a34d1c3c.tar.gz |
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>
-rw-r--r-- | src/corelib/kernel/qcoreevent.cpp | 1 | ||||
-rw-r--r-- | src/corelib/kernel/qcoreevent.h | 2 | ||||
-rw-r--r-- | src/gui/kernel/qevent.cpp | 49 | ||||
-rw-r--r-- | src/gui/kernel/qevent.h | 17 | ||||
-rw-r--r-- | src/gui/kernel/qoffscreensurface.cpp | 7 | ||||
-rw-r--r-- | src/gui/kernel/qwindow.cpp | 9 | ||||
-rw-r--r-- | tests/auto/gui/kernel/qwindow/tst_qwindow.cpp | 76 |
7 files changed, 160 insertions, 1 deletions
diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp index 191ceaa37b..68d48dbe25 100644 --- a/src/corelib/kernel/qcoreevent.cpp +++ b/src/corelib/kernel/qcoreevent.cpp @@ -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. diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h index 170f319ec9..0807721432 100644 --- a/src/corelib/kernel/qcoreevent.h +++ b/src/corelib/kernel/qcoreevent.h @@ -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 diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index a8539e8013..67c4e3f512 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -1324,6 +1324,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 Returns the window area that has been exposed. The region is given in local coordinates. diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index f91bc0a830..0b6d96a590 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -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: diff --git a/src/gui/kernel/qoffscreensurface.cpp b/src/gui/kernel/qoffscreensurface.cpp index 5cf77de5d8..fb1dfd8df5 100644 --- a/src/gui/kernel/qoffscreensurface.cpp +++ b/src/gui/kernel/qoffscreensurface.cpp @@ -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) { diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index e23e0f8e57..a3b7f38c80 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -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; diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp index fac16ff8a3..7a0683c3bf 100644 --- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp +++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp @@ -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); |