Merge remote-tracking branch 'origin/5.12' into dev

Change-Id: I1d9281750b06f7584e55759994dc6fcbcc5b2455
This commit is contained in:
Qt Forward Merge Bot 2018-10-30 03:03:23 +01:00
commit 572f479787
13 changed files with 155 additions and 21 deletions

View File

@ -179,7 +179,7 @@ void QWaylandWlShellSurface::requestWindowStates(Qt::WindowStates states)
m_window->applyConfigureWhenPossible(); m_window->applyConfigureWhenPossible();
} }
bool isNormal = ~states & (Qt::WindowMaximized | Qt::WindowFullScreen); bool isNormal = !(states & Qt::WindowMaximized) && !(states & Qt::WindowFullScreen);
if (isNormal && (changedStates & (Qt::WindowMaximized | Qt::WindowFullScreen))) { if (isNormal && (changedStates & (Qt::WindowMaximized | Qt::WindowFullScreen))) {
setTopLevel(); // set normal window setTopLevel(); // set normal window
// There's usually no configure event after this, so just clear the rest of the pending // There's usually no configure event after this, so just clear the rest of the pending

View File

@ -261,9 +261,14 @@ void QWaylandXdgSurfaceV6::setAppId(const QString &appId)
m_toplevel->set_app_id(appId); m_toplevel->set_app_id(appId);
} }
bool QWaylandXdgSurfaceV6::isExposed() const
{
return m_configured || m_pendingConfigureSerial;
}
bool QWaylandXdgSurfaceV6::handleExpose(const QRegion &region) bool QWaylandXdgSurfaceV6::handleExpose(const QRegion &region)
{ {
if (!m_configured && !region.isEmpty()) { if (!isExposed() && !region.isEmpty()) {
m_exposeRegion = region; m_exposeRegion = region;
return true; return true;
} }
@ -336,10 +341,18 @@ void QWaylandXdgSurfaceV6::setPopup(QWaylandWindow *parent, QWaylandInputDevice
void QWaylandXdgSurfaceV6::zxdg_surface_v6_configure(uint32_t serial) void QWaylandXdgSurfaceV6::zxdg_surface_v6_configure(uint32_t serial)
{ {
m_window->applyConfigureWhenPossible();
m_pendingConfigureSerial = serial; m_pendingConfigureSerial = serial;
if (!m_configured) {
// We have to do the initial applyConfigure() immediately, since that is the expose.
applyConfigure();
} else {
// Later configures are probably resizes, so we have to queue them up for a time when we
// are not painting to the window.
m_window->applyConfigureWhenPossible();
}
if (!m_exposeRegion.isEmpty()) { if (!m_exposeRegion.isEmpty()) {
QWindowSystemInterface::handleExposeEvent(m_window->window(), m_exposeRegion); m_window->handleExpose(m_exposeRegion);
m_exposeRegion = QRegion(); m_exposeRegion = QRegion();
} }
} }

View File

@ -82,7 +82,7 @@ public:
void setTitle(const QString &title) override; void setTitle(const QString &title) override;
void setAppId(const QString &appId) override; void setAppId(const QString &appId) override;
bool isExposed() const override { return m_configured; } bool isExposed() const override;
bool handleExpose(const QRegion &) override; bool handleExpose(const QRegion &) override;
bool handlesActiveState() const { return m_toplevel; } bool handlesActiveState() const { return m_toplevel; }
void applyConfigure() override; void applyConfigure() override;

View File

@ -295,9 +295,14 @@ void QWaylandXdgSurface::setWindowFlags(Qt::WindowFlags flags)
m_toplevel->requestWindowFlags(flags); m_toplevel->requestWindowFlags(flags);
} }
bool QWaylandXdgSurface::isExposed() const
{
return m_configured || m_pendingConfigureSerial;
}
bool QWaylandXdgSurface::handleExpose(const QRegion &region) bool QWaylandXdgSurface::handleExpose(const QRegion &region)
{ {
if (!m_configured && !region.isEmpty()) { if (!isExposed() && !region.isEmpty()) {
m_exposeRegion = region; m_exposeRegion = region;
return true; return true;
} }
@ -370,10 +375,18 @@ void QWaylandXdgSurface::setPopup(QWaylandWindow *parent, QWaylandInputDevice *d
void QWaylandXdgSurface::xdg_surface_configure(uint32_t serial) void QWaylandXdgSurface::xdg_surface_configure(uint32_t serial)
{ {
m_window->applyConfigureWhenPossible();
m_pendingConfigureSerial = serial; m_pendingConfigureSerial = serial;
if (!m_configured) {
// We have to do the initial applyConfigure() immediately, since that is the expose.
applyConfigure();
} else {
// Later configures are probably resizes, so we have to queue them up for a time when we
// are not painting to the window.
m_window->applyConfigureWhenPossible();
}
if (!m_exposeRegion.isEmpty()) { if (!m_exposeRegion.isEmpty()) {
QWindowSystemInterface::handleExposeEvent(m_window->window(), m_exposeRegion); m_window->handleExpose(m_exposeRegion);
m_exposeRegion = QRegion(); m_exposeRegion = QRegion();
} }
} }

View File

@ -86,7 +86,7 @@ public:
void setAppId(const QString &appId) override; void setAppId(const QString &appId) override;
void setWindowFlags(Qt::WindowFlags flags) override; void setWindowFlags(Qt::WindowFlags flags) override;
bool isExposed() const override { return m_configured; } bool isExposed() const override;
bool handleExpose(const QRegion &) override; bool handleExpose(const QRegion &) override;
bool handlesActiveState() const { return m_toplevel; } bool handlesActiveState() const { return m_toplevel; }
void applyConfigure() override; void applyConfigure() override;

View File

@ -233,8 +233,7 @@ void QWaylandShmBackingStore::flush(QWindow *window, const QRegion &region, cons
mFrontBuffer = mBackBuffer; mFrontBuffer = mBackBuffer;
QMargins margins = windowDecorationMargins(); QMargins margins = windowDecorationMargins();
waylandWindow()->safeCommit(mFrontBuffer, region.translated(margins.left(), margins.top()));
waylandWindow()->commit(mFrontBuffer, region.translated(margins.left(), margins.top()));
} }
void QWaylandShmBackingStore::resize(const QSize &size, const QRegion &) void QWaylandShmBackingStore::resize(const QSize &size, const QRegion &)

View File

@ -246,6 +246,7 @@ void QWaylandWindow::reset(bool sendDestroyEvent)
} }
mMask = QRegion(); mMask = QRegion();
mQueuedBuffer = nullptr;
} }
QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface) QWaylandWindow *QWaylandWindow::fromWlSurface(::wl_surface *surface)
@ -335,7 +336,9 @@ void QWaylandWindow::setGeometry(const QRect &rect)
mSentInitialResize = true; mSentInitialResize = true;
} }
sendExposeEvent(QRect(QPoint(), geometry().size())); QRect exposeGeometry(QPoint(), geometry().size());
if (exposeGeometry != mLastExposeGeometry)
sendExposeEvent(exposeGeometry);
} }
void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset) void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset)
@ -353,6 +356,7 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect)
{ {
if (!(mShellSurface && mShellSurface->handleExpose(rect))) if (!(mShellSurface && mShellSurface->handleExpose(rect)))
QWindowSystemInterface::handleExposeEvent(window(), rect); QWindowSystemInterface::handleExposeEvent(window(), rect);
mLastExposeGeometry = rect;
} }
@ -564,8 +568,29 @@ void QWaylandWindow::damage(const QRect &rect)
damage(rect.x(), rect.y(), rect.width(), rect.height()); damage(rect.x(), rect.y(), rect.width(), rect.height());
} }
void QWaylandWindow::safeCommit(QWaylandBuffer *buffer, const QRegion &damage)
{
if (isExposed()) {
commit(buffer, damage);
} else {
mQueuedBuffer = buffer;
mQueuedBufferDamage = damage;
}
}
void QWaylandWindow::handleExpose(const QRegion &region)
{
QWindowSystemInterface::handleExposeEvent(window(), region);
if (mQueuedBuffer) {
commit(mQueuedBuffer, mQueuedBufferDamage);
mQueuedBuffer = nullptr;
mQueuedBufferDamage = QRegion();
}
}
void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage) void QWaylandWindow::commit(QWaylandBuffer *buffer, const QRegion &damage)
{ {
Q_ASSERT(isExposed());
if (buffer->committed()) { if (buffer->committed()) {
qCDebug(lcWaylandBackingstore) << "Buffer already committed, ignoring."; qCDebug(lcWaylandBackingstore) << "Buffer already committed, ignoring.";
return; return;

View File

@ -116,6 +116,8 @@ public:
using QtWayland::wl_surface::damage; using QtWayland::wl_surface::damage;
void damage(const QRect &rect); void damage(const QRect &rect);
void safeCommit(QWaylandBuffer *buffer, const QRegion &damage);
void handleExpose(const QRegion &region);
void commit(QWaylandBuffer *buffer, const QRegion &damage); void commit(QWaylandBuffer *buffer, const QRegion &damage);
void waitForFrameSync(); void waitForFrameSync();
@ -231,6 +233,8 @@ protected:
Qt::WindowStates mLastReportedWindowStates = Qt::WindowNoState; Qt::WindowStates mLastReportedWindowStates = Qt::WindowNoState;
QWaylandShmBackingStore *mBackingStore = nullptr; QWaylandShmBackingStore *mBackingStore = nullptr;
QWaylandBuffer *mQueuedBuffer = nullptr;
QRegion mQueuedBufferDamage;
private slots: private slots:
void handleScreenRemoved(QScreen *qScreen); void handleScreenRemoved(QScreen *qScreen);
@ -250,6 +254,7 @@ private:
void handleScreenChanged(); void handleScreenChanged();
bool mUpdateRequested = false; bool mUpdateRequested = false;
QRect mLastExposeGeometry;
static const wl_callback_listener callbackListener; static const wl_callback_listener callbackListener;
static void frameCallback(void *data, struct wl_callback *wl_callback, uint32_t time); static void frameCallback(void *data, struct wl_callback *wl_callback, uint32_t time);

View File

@ -74,7 +74,7 @@ void Compositor::sendShellSurfaceConfigure(void *data, const QList<QVariant> &pa
auto statesBytes = QByteArray::fromRawData(reinterpret_cast<const char *>(states.data()), auto statesBytes = QByteArray::fromRawData(reinterpret_cast<const char *>(states.data()),
states.size() * static_cast<int>(sizeof(uint))); states.size() * static_cast<int>(sizeof(uint)));
toplevel->send_configure(size.width(), size.height(), statesBytes); toplevel->send_configure(size.width(), size.height(), statesBytes);
toplevel->xdgSurface()->send_configure(compositor->nextSerial()); toplevel->xdgSurface()->sendConfigure(compositor->nextSerial());
} else if (auto wlShellSurface = surface->wlShellSurface()) { } else if (auto wlShellSurface = surface->wlShellSurface()) {
const uint edges = 0; const uint edges = 0;
wlShellSurface->send_configure(edges, size.width(), size.height()); wlShellSurface->send_configure(edges, size.width(), size.height());
@ -123,13 +123,17 @@ void Surface::surface_destroy(Resource *resource)
if (m_wlShellSurface) // on wl-shell the shell surface is automatically destroyed with the surface if (m_wlShellSurface) // on wl-shell the shell surface is automatically destroyed with the surface
wl_resource_destroy(m_wlShellSurface->resource()->handle); wl_resource_destroy(m_wlShellSurface->resource()->handle);
Q_ASSERT(!m_wlShellSurface); Q_ASSERT(!m_wlShellSurface);
Q_ASSERT(!m_xdgToplevelV6); Q_ASSERT(!m_xdgSurfaceV6);
wl_resource_destroy(resource->handle); wl_resource_destroy(resource->handle);
} }
void Surface::surface_attach(Resource *resource, void Surface::surface_attach(Resource *resource, struct wl_resource *buffer, int x, int y)
struct wl_resource *buffer, int x, int y)
{ {
if (m_xdgSurfaceV6) {
// It's a protocol error to attach a buffer to an xdgSurface that's not configured
Q_ASSERT(xdgSurfaceV6()->configureSent());
}
Q_UNUSED(resource); Q_UNUSED(resource);
Q_UNUSED(x); Q_UNUSED(x);
Q_UNUSED(y); Q_UNUSED(y);

View File

@ -50,7 +50,8 @@ public:
static Surface *fromResource(struct ::wl_resource *resource); static Surface *fromResource(struct ::wl_resource *resource);
void map(); void map();
bool isMapped() const; bool isMapped() const;
XdgToplevelV6 *xdgToplevelV6() const { return m_xdgToplevelV6; } XdgSurfaceV6 *xdgSurfaceV6() const { return m_xdgSurfaceV6; }
XdgToplevelV6 *xdgToplevelV6() const { return m_xdgSurfaceV6 ? m_xdgSurfaceV6->toplevel() : nullptr; }
WlShellSurface *wlShellSurface() const { return m_wlShellSurface; } WlShellSurface *wlShellSurface() const { return m_wlShellSurface; }
QSharedPointer<MockSurface> mockSurface() const { return m_mockSurface; } QSharedPointer<MockSurface> mockSurface() const { return m_mockSurface; }
@ -69,7 +70,7 @@ protected:
void surface_commit(Resource *resource) override; void surface_commit(Resource *resource) override;
private: private:
wl_resource *m_buffer = nullptr; wl_resource *m_buffer = nullptr;
XdgToplevelV6 *m_xdgToplevelV6 = nullptr; XdgSurfaceV6 *m_xdgSurfaceV6 = nullptr;
WlShellSurface *m_wlShellSurface = nullptr; WlShellSurface *m_wlShellSurface = nullptr;
Compositor *m_compositor = nullptr; Compositor *m_compositor = nullptr;
@ -77,7 +78,7 @@ private:
QList<wl_resource *> m_frameCallbackList; QList<wl_resource *> m_frameCallbackList;
bool m_mapped = false; bool m_mapped = false;
friend class XdgToplevelV6; friend class XdgSurfaceV6;
friend class WlShellSurface; friend class WlShellSurface;
}; };

View File

@ -49,6 +49,18 @@ XdgSurfaceV6::XdgSurfaceV6(XdgShellV6 *shell, Surface *surface, wl_client *clien
, m_surface(surface) , m_surface(surface)
, m_shell(shell) , m_shell(shell)
{ {
m_surface->m_xdgSurfaceV6 = this;
}
XdgSurfaceV6::~XdgSurfaceV6()
{
m_surface->m_xdgSurfaceV6 = nullptr;
}
void XdgSurfaceV6::sendConfigure(uint32_t serial)
{
send_configure(serial);
m_configureSent = true;
} }
void XdgSurfaceV6::zxdg_surface_v6_get_toplevel(QtWaylandServer::zxdg_surface_v6::Resource *resource, uint32_t id) void XdgSurfaceV6::zxdg_surface_v6_get_toplevel(QtWaylandServer::zxdg_surface_v6::Resource *resource, uint32_t id)
@ -78,7 +90,6 @@ XdgToplevelV6::XdgToplevelV6(XdgSurfaceV6 *xdgSurface, wl_client *client, uint32
, m_mockToplevel(new MockXdgToplevelV6(this)) , m_mockToplevel(new MockXdgToplevelV6(this))
{ {
auto *surface = m_xdgSurface->surface(); auto *surface = m_xdgSurface->surface();
surface->m_xdgToplevelV6 = this;
m_xdgSurface->shell()->addToplevel(this); m_xdgSurface->shell()->addToplevel(this);
surface->map(); surface->map();
} }
@ -86,7 +97,6 @@ XdgToplevelV6::XdgToplevelV6(XdgSurfaceV6 *xdgSurface, wl_client *client, uint32
XdgToplevelV6::~XdgToplevelV6() XdgToplevelV6::~XdgToplevelV6()
{ {
m_xdgSurface->shell()->removeToplevel(this); m_xdgSurface->shell()->removeToplevel(this);
m_xdgSurface->surface()->m_xdgToplevelV6 = nullptr;
m_mockToplevel->m_toplevel = nullptr; m_mockToplevel->m_toplevel = nullptr;
} }

View File

@ -46,8 +46,13 @@ class XdgSurfaceV6 : public QtWaylandServer::zxdg_surface_v6
{ {
public: public:
XdgSurfaceV6(XdgShellV6 *shell, Surface *surface, wl_client *client, uint32_t id); XdgSurfaceV6(XdgShellV6 *shell, Surface *surface, wl_client *client, uint32_t id);
~XdgSurfaceV6() override;
XdgShellV6 *shell() const { return m_shell; } XdgShellV6 *shell() const { return m_shell; }
Surface *surface() const { return m_surface; } Surface *surface() const { return m_surface; }
XdgToplevelV6 *toplevel() const { return m_toplevel; }
void sendConfigure(uint32_t serial);
bool configureSent() const { return m_configureSent; }
protected: protected:
void zxdg_surface_v6_destroy_resource(Resource *) override { delete this; } void zxdg_surface_v6_destroy_resource(Resource *) override { delete this; }
@ -59,6 +64,7 @@ private:
Surface *m_surface = nullptr; Surface *m_surface = nullptr;
XdgToplevelV6 *m_toplevel = nullptr; XdgToplevelV6 *m_toplevel = nullptr;
XdgShellV6 *m_shell = nullptr; XdgShellV6 *m_shell = nullptr;
bool m_configureSent = false;
friend class XdgToplevelV6; friend class XdgToplevelV6;
}; };

View File

@ -58,6 +58,14 @@ public:
return QWindow::event(event); return QWindow::event(event);
} }
void exposeEvent(QExposeEvent *event) override
{
++exposeEventCount;
QWindow::exposeEvent(event);
}
int exposeEventCount = 0;
signals: signals:
void windowStateChangeEventReceived(uint oldState); void windowStateChangeEventReceived(uint oldState);
}; };
@ -101,6 +109,8 @@ private slots:
void windowStateChangedEvents(); void windowStateChangedEvents();
void windowGeometrySimple(); void windowGeometrySimple();
void windowGeometryFixed(); void windowGeometryFixed();
void flushUnconfiguredXdgSurface();
void dontSpamExposeEvents();
private: private:
MockCompositor *m_compositor = nullptr; MockCompositor *m_compositor = nullptr;
@ -360,6 +370,54 @@ void tst_WaylandClientXdgShellV6::windowGeometryFixed()
QCOMPARE(geometrySpy.takeFirst().at(0).toRect().size(), initialWindowGeometry.size()); QCOMPARE(geometrySpy.takeFirst().at(0).toRect().size(), initialWindowGeometry.size());
} }
void tst_WaylandClientXdgShellV6::flushUnconfiguredXdgSurface()
{
TestWindow window;
window.show();
QSharedPointer<MockSurface> surface;
QTRY_VERIFY(surface = m_compositor->surface());
// Paint and flush some magenta
QBackingStore backingStore(&window);
QRect rect(QPoint(), window.size());
backingStore.resize(rect.size());
backingStore.beginPaint(rect);
QColor color = Qt::magenta;
QPainter p(backingStore.paintDevice());
p.fillRect(rect, color);
p.end();
backingStore.endPaint();
backingStore.flush(rect);
// We're not allowed to send buffer on this surface since it isn't yet configured.
// So, from the compositor's point of view there should be no buffer data yet.
m_compositor->processWaylandEvents();
QVERIFY(surface->image.isNull());
QVERIFY(!window.isExposed());
// Finally sending the configure should trigger an attach and commit with the
// right buffer.
m_compositor->sendShellSurfaceConfigure(surface);
QTRY_COMPARE(surface->image.size(), window.frameGeometry().size());
QTRY_COMPARE(surface->image.pixel(window.frameMargins().left(), window.frameMargins().top()), color.rgba());
QVERIFY(window.isExposed());
}
void tst_WaylandClientXdgShellV6::dontSpamExposeEvents()
{
TestWindow window;
window.show();
QSharedPointer<MockSurface> surface;
QTRY_VERIFY(surface = m_compositor->surface());
QTRY_VERIFY(window.exposeEventCount == 0);
m_compositor->sendShellSurfaceConfigure(surface);
QTRY_VERIFY(window.isExposed());
QTRY_VERIFY(window.exposeEventCount == 1);
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
setenv("XDG_RUNTIME_DIR", ".", 1); setenv("XDG_RUNTIME_DIR", ".", 1);