diff --git a/autotests/client/test_xdg_shell_stable.cpp b/autotests/client/test_xdg_shell_stable.cpp --- a/autotests/client/test_xdg_shell_stable.cpp +++ b/autotests/client/test_xdg_shell_stable.cpp @@ -57,14 +57,18 @@ QVERIFY(maxSizeSpy.isValid()); xdgSurface->setMaxSize(QSize(100, 100)); + surface->commit(Surface::CommitFlag::None); QVERIFY(maxSizeSpy.wait()); QCOMPARE(maxSizeSpy.count(), 1); QCOMPARE(maxSizeSpy.last().at(0).value(), QSize(100,100)); + QCOMPARE(serverXdgSurface->maximumSize(), QSize(100, 100)); xdgSurface->setMaxSize(QSize(200, 200)); + surface->commit(Surface::CommitFlag::None); QVERIFY(maxSizeSpy.wait()); QCOMPARE(maxSizeSpy.count(), 2); QCOMPARE(maxSizeSpy.last().at(0).value(), QSize(200,200)); + QCOMPARE(serverXdgSurface->maximumSize(), QSize(200, 200)); } @@ -141,14 +145,18 @@ QVERIFY(minSizeSpy.isValid()); xdgSurface->setMinSize(QSize(200, 200)); + surface->commit(Surface::CommitFlag::None); QVERIFY(minSizeSpy.wait()); QCOMPARE(minSizeSpy.count(), 1); QCOMPARE(minSizeSpy.last().at(0).value(), QSize(200,200)); + QCOMPARE(serverXdgSurface->minimumSize(), QSize(200, 200)); xdgSurface->setMinSize(QSize(100, 100)); + surface->commit(Surface::CommitFlag::None); QVERIFY(minSizeSpy.wait()); QCOMPARE(minSizeSpy.count(), 2); QCOMPARE(minSizeSpy.last().at(0).value(), QSize(100,100)); + QCOMPARE(serverXdgSurface->minimumSize(), QSize(100, 100)); } //top level then toplevel @@ -224,11 +232,10 @@ SURFACE QSignalSpy windowGeometryChangedSpy(serverXdgSurface, &XdgShellSurfaceInterface::windowGeometryChanged); xdgSurface->setWindowGeometry(QRect(50, 50, 400, 400)); - - windowGeometryChangedSpy.wait(); + surface->commit(Surface::CommitFlag::None); + QVERIFY(windowGeometryChangedSpy.wait()); QCOMPARE(serverXdgSurface->windowGeometry(), QRect(50, 50, 400, 400)); - //add a popup to this surface XdgPositioner positioner(QSize(10,10), QRect(100,100,50,50)); QSignalSpy xdgPopupCreatedSpy(m_xdgShellInterface, &XdgShellInterface::xdgPopupCreated); @@ -240,7 +247,8 @@ QSignalSpy popupWindowGeometryChangedSpy(serverXdgPopup, &XdgShellPopupInterface::windowGeometryChanged); xdgPopupSurface->setWindowGeometry(QRect(60, 60, 300, 300)); - popupWindowGeometryChangedSpy.wait(); + popupSurface->commit(Surface::CommitFlag::None); + QVERIFY(popupWindowGeometryChangedSpy.wait()); QCOMPARE(serverXdgPopup->windowGeometry(), QRect(60, 60, 300, 300)); } diff --git a/autotests/client/test_xdg_shell_v6.cpp b/autotests/client/test_xdg_shell_v6.cpp --- a/autotests/client/test_xdg_shell_v6.cpp +++ b/autotests/client/test_xdg_shell_v6.cpp @@ -55,14 +55,18 @@ QVERIFY(maxSizeSpy.isValid()); xdgSurface->setMaxSize(QSize(100, 100)); + surface->commit(Surface::CommitFlag::None); QVERIFY(maxSizeSpy.wait()); QCOMPARE(maxSizeSpy.count(), 1); QCOMPARE(maxSizeSpy.last().at(0).value(), QSize(100,100)); + QCOMPARE(serverXdgSurface->maximumSize(), QSize(100, 100)); xdgSurface->setMaxSize(QSize(200, 200)); + surface->commit(Surface::CommitFlag::None); QVERIFY(maxSizeSpy.wait()); QCOMPARE(maxSizeSpy.count(), 2); QCOMPARE(maxSizeSpy.last().at(0).value(), QSize(200,200)); + QCOMPARE(serverXdgSurface->maximumSize(), QSize(200, 200)); } @@ -139,14 +143,18 @@ QVERIFY(minSizeSpy.isValid()); xdgSurface->setMinSize(QSize(200, 200)); + surface->commit(Surface::CommitFlag::None); QVERIFY(minSizeSpy.wait()); QCOMPARE(minSizeSpy.count(), 1); QCOMPARE(minSizeSpy.last().at(0).value(), QSize(200,200)); + QCOMPARE(serverXdgSurface->minimumSize(), QSize(200, 200)); xdgSurface->setMinSize(QSize(100, 100)); + surface->commit(Surface::CommitFlag::None); QVERIFY(minSizeSpy.wait()); QCOMPARE(minSizeSpy.count(), 2); QCOMPARE(minSizeSpy.last().at(0).value(), QSize(100,100)); + QCOMPARE(serverXdgSurface->minimumSize(), QSize(100, 100)); } //top level then toplevel diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -49,6 +49,7 @@ slide_interface.cpp subcompositor_interface.cpp surface_interface.cpp + surfacerole.cpp textinput_interface.cpp textinput_interface_v0.cpp textinput_interface_v2.cpp diff --git a/src/server/generic_shell_surface_p.h b/src/server/generic_shell_surface_p.h --- a/src/server/generic_shell_surface_p.h +++ b/src/server/generic_shell_surface_p.h @@ -22,6 +22,7 @@ #include "seat_interface.h" #include "surface_interface.h" +#include "surfacerole_p.h" #include namespace KWayland @@ -31,11 +32,12 @@ { template -class GenericShellSurface +class GenericShellSurface : public SurfaceRole { public: GenericShellSurface(T *shellSurface, SurfaceInterface *surface) - : surface(surface) + : SurfaceRole(surface) + , surface(surface) , shellSurface(shellSurface) {} diff --git a/src/server/shell_interface.cpp b/src/server/shell_interface.cpp --- a/src/server/shell_interface.cpp +++ b/src/server/shell_interface.cpp @@ -71,6 +71,8 @@ Private(ShellSurfaceInterface *q, ShellInterface *shell, SurfaceInterface *surface, wl_resource *parentResource); void ping(); + void commit() override; + QScopedPointer pingTimer; quint32 pingSerial = 0; enum class WindowMode { @@ -195,6 +197,10 @@ ShellSurfaceInterface::~ShellSurfaceInterface() = default; +void ShellSurfaceInterface::Private::commit() +{ +} + void ShellSurfaceInterface::Private::pongCallback(wl_client *client, wl_resource *resource, uint32_t serial) { auto s = cast(resource); diff --git a/src/server/surface_interface.h b/src/server/surface_interface.h --- a/src/server/surface_interface.h +++ b/src/server/surface_interface.h @@ -374,6 +374,7 @@ friend class ContrastManagerInterface; friend class IdleInhibitManagerUnstableV1Interface; friend class PointerConstraintsUnstableV1Interface; + friend class SurfaceRole; explicit SurfaceInterface(CompositorInterface *parent, wl_resource *parentResource); class Private; diff --git a/src/server/surface_interface.cpp b/src/server/surface_interface.cpp --- a/src/server/surface_interface.cpp +++ b/src/server/surface_interface.cpp @@ -27,6 +27,7 @@ #include "region_interface.h" #include "subcompositor_interface.h" #include "subsurface_interface_p.h" +#include "surfacerole_p.h" // Qt #include // Wayland @@ -529,6 +530,9 @@ subSurface->d_func()->commit(); } } + if (role) { + role->commit(); + } emit q->committed(); } diff --git a/src/server/surface_interface_p.h b/src/server/surface_interface_p.h --- a/src/server/surface_interface_p.h +++ b/src/server/surface_interface_p.h @@ -34,6 +34,7 @@ { class IdleInhibitorInterface; +class SurfaceRole; class SurfaceInterface::Private : public Resource::Private { @@ -86,6 +87,8 @@ void commitSubSurface(); void commit(); + SurfaceRole *role = nullptr; + State current; State pending; State subSurfacePending; diff --git a/src/server/surfacerole.cpp b/src/server/surfacerole.cpp new file mode 100644 --- /dev/null +++ b/src/server/surfacerole.cpp @@ -0,0 +1,45 @@ +/**************************************************************************** +Copyright 2019 Vlad Zagorodniy + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ + +#include "surfacerole_p.h" +#include "surface_interface_p.h" +#include "surface_interface.h" + +namespace KWayland +{ +namespace Server +{ + +SurfaceRole::SurfaceRole(SurfaceInterface *surface) + : m_surface(surface) +{ + m_surface->d_func()->role = this; +} + +SurfaceRole::~SurfaceRole() +{ + // Lifetime of the surface role is not bounded to the associated surface. + if (m_surface) { + m_surface->d_func()->role = nullptr; + } +} + +} +} diff --git a/src/server/surfacerole_p.h b/src/server/surfacerole_p.h new file mode 100644 --- /dev/null +++ b/src/server/surfacerole_p.h @@ -0,0 +1,50 @@ +/**************************************************************************** +Copyright 2019 Vlad Zagorodniy + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ + +#ifndef KWAYLAND_SERVER_SURFACEROLE_P_H +#define KWAYLAND_SERVER_SURFACEROLE_P_H + +#include + +namespace KWayland +{ +namespace Server +{ + +class SurfaceInterface; + +class SurfaceRole +{ +public: + explicit SurfaceRole(SurfaceInterface *surface); + virtual ~SurfaceRole(); + + virtual void commit() = 0; + +private: + QPointer m_surface; + + Q_DISABLE_COPY(SurfaceRole) +}; + +} +} + +#endif // KWAYLAND_SERVER_SURFACEROLE_P_H diff --git a/src/server/xdgshell_interface.h b/src/server/xdgshell_interface.h --- a/src/server/xdgshell_interface.h +++ b/src/server/xdgshell_interface.h @@ -287,6 +287,18 @@ */ QRect windowGeometry() const; + /** + * @returns The minimum size for the window specified by the client. + * @since 5.65 + */ + QSize minimumSize() const; + + /** + * @returns The maximum size for the window specified by the client. + * @since 5.65 + */ + QSize maximumSize() const; + Q_SIGNALS: /** * Emitted whenever the title changes. diff --git a/src/server/xdgshell_interface.cpp b/src/server/xdgshell_interface.cpp --- a/src/server/xdgshell_interface.cpp +++ b/src/server/xdgshell_interface.cpp @@ -157,7 +157,19 @@ QRect XdgShellSurfaceInterface::windowGeometry() const { Q_D(); - return d->windowGeometry; + return d->windowGeometry(); +} + +QSize XdgShellSurfaceInterface::minimumSize() const +{ + Q_D(); + return d->minimumSize(); +} + +QSize XdgShellSurfaceInterface::maximumSize() const +{ + Q_D(); + return d->maximumSize(); } XdgShellSurfaceInterface::Private *XdgShellSurfaceInterface::d_func() const @@ -261,7 +273,7 @@ QRect XdgShellPopupInterface::windowGeometry() const { Q_D(); - return d->windowGeometry; + return d->windowGeometry(); } void XdgShellPopupInterface::popupDone() diff --git a/src/server/xdgshell_interface_p.h b/src/server/xdgshell_interface_p.h --- a/src/server/xdgshell_interface_p.h +++ b/src/server/xdgshell_interface_p.h @@ -53,14 +53,16 @@ virtual void close() = 0; virtual quint32 configure(States states, const QSize &size) = 0; + virtual QRect windowGeometry() const = 0; + virtual QSize minimumSize() const = 0; + virtual QSize maximumSize() const = 0; XdgShellSurfaceInterface *q_func() { return reinterpret_cast(q); } QVector configureSerials; QPointer parent; - QRect windowGeometry; XdgShellInterfaceVersion interfaceVersion; protected: @@ -72,6 +74,7 @@ public: virtual ~Private(); virtual void popupDone() = 0; + virtual QRect windowGeometry() const = 0; XdgShellPopupInterface *q_func() { return reinterpret_cast(q); @@ -94,7 +97,6 @@ Qt::Edges gravity; PositionerConstraints constraintAdjustments; QPoint anchorOffset; - QRect windowGeometry; XdgShellInterfaceVersion interfaceVersion; diff --git a/src/server/xdgshell_stable_interface.cpp b/src/server/xdgshell_stable_interface.cpp --- a/src/server/xdgshell_stable_interface.cpp +++ b/src/server/xdgshell_stable_interface.cpp @@ -1,5 +1,6 @@ /**************************************************************************** Copyright 2017 David Edmundson +Copyright 2019 Vlad Zagorodniy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -74,6 +75,9 @@ Private(XdgPopupStableInterface *q, XdgShellStableInterface *c, SurfaceInterface *surface, wl_resource *parentResource); ~Private() override; + QRect windowGeometry() const override; + void commit() override; + void ackConfigure(quint32 serial) { if (!configureSerials.contains(serial)) { return; @@ -94,9 +98,23 @@ return static_cast(q); } private: + void setWindowGeometryCallback(const QRect &rect); + static void grabCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial); static const struct xdg_popup_interface s_interface; + + struct ShellSurfaceState + { + QRect windowGeometry; + + bool windowGeometryIsSet = false; + }; + + ShellSurfaceState m_currentState; + ShellSurfaceState m_pendingState; + + friend class XdgSurfaceStableInterface; }; class XdgSurfaceStableInterface::Private : public KWayland::Server::Resource::Private @@ -135,7 +153,11 @@ Private(XdgTopLevelStableInterface* q, XdgShellStableInterface* c, SurfaceInterface* surface, wl_resource* parentResource); ~Private() override; + QRect windowGeometry() const override; + QSize minimumSize() const override; + QSize maximumSize() const override; void close() override; + void commit() override; void ackConfigure(quint32 serial) { if (!configureSerials.contains(serial)) { @@ -188,6 +210,8 @@ } private: + void setWindowGeometryCallback(const QRect &rect); + static void destroyCallback(wl_client *client, wl_resource *resource); static void setParentCallback(struct wl_client *client, struct wl_resource *resource, wl_resource *parent); static void showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, int32_t x, int32_t y); @@ -200,6 +224,22 @@ static void setMinimizedCallback(wl_client *client, wl_resource *resource); static const struct xdg_toplevel_interface s_interface; + + struct ShellSurfaceState + { + QRect windowGeometry; + QSize minimumSize = QSize(0, 0); + QSize maximiumSize = QSize(INT32_MAX, INT32_MAX); + + bool windowGeometryIsSet = false; + bool minimumSizeIsSet = false; + bool maximumSizeIsSet = false; + }; + + ShellSurfaceState m_currentState; + ShellSurfaceState m_pendingState; + + friend class XdgSurfaceStableInterface; }; @@ -441,6 +481,8 @@ void XdgSurfaceStableInterface::Private::createTopLevel(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource) { + // FIXME: That's incorrect! The client may have asked us to create an xdg-toplevel + // for a pointer surface or a subsurface. We have to post an error in that case. if (m_topLevel) { wl_resource_post_error(parentResource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, "Toplevel already created on this surface"); return; @@ -464,7 +506,8 @@ void XdgSurfaceStableInterface::Private::createPopup(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource, wl_resource *parentSurface, wl_resource *positioner) { - + // FIXME: That's incorrect! The client may have asked us to create an xdg-popup + // for a pointer surface or a subsurface. We have to post an error in that case. if (m_topLevel) { wl_resource_post_error(parentResource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, "Toplevel already created on this surface"); return; @@ -518,14 +561,15 @@ auto s = cast(resource); Q_ASSERT(client == *s->client); - const QRect windowRect(x, y, width, height); + if (width < 0 || height < 0) { + wl_resource_post_error(resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE , "Tried to set invalid xdg-surface geometry"); + return; + } if (s->m_topLevel) { - s->m_topLevel->d_func()->windowGeometry = windowRect; - emit s->m_topLevel->windowGeometryChanged(windowRect); + s->m_topLevel->d_func()->setWindowGeometryCallback(QRect(x, y, width, height)); } else if (s->m_popup) { - s->m_popup->d_func()->windowGeometry = windowRect; - emit s->m_popup->windowGeometryChanged(windowRect); + s->m_popup->d_func()->setWindowGeometryCallback(QRect(x, y, width, height)); } } @@ -706,24 +750,90 @@ s->anchorOffset = QPoint(x,y); } +QRect XdgTopLevelStableInterface::Private::windowGeometry() const +{ + return m_currentState.windowGeometry; +} + +QSize XdgTopLevelStableInterface::Private::minimumSize() const +{ + return m_currentState.minimumSize; +} + +QSize XdgTopLevelStableInterface::Private::maximumSize() const +{ + return m_currentState.maximiumSize; +} + void XdgTopLevelStableInterface::Private::close() { xdg_toplevel_send_close(resource); client->flush(); } +void XdgTopLevelStableInterface::Private::commit() +{ + const bool windowGeometryChanged = m_pendingState.windowGeometryIsSet; + const bool minimumSizeChanged = m_pendingState.minimumSizeIsSet; + const bool maximumSizeChanged = m_pendingState.maximumSizeIsSet; + + if (windowGeometryChanged) { + m_currentState.windowGeometry = m_pendingState.windowGeometry; + } + if (minimumSizeChanged) { + m_currentState.minimumSize = m_pendingState.minimumSize; + } + if (maximumSizeChanged) { + m_currentState.maximiumSize = m_pendingState.maximiumSize; + } + + m_pendingState = ShellSurfaceState{}; + + if (windowGeometryChanged) { + emit q_func()->windowGeometryChanged(m_currentState.windowGeometry); + } + if (minimumSizeChanged) { + emit q_func()->minSizeChanged(m_currentState.minimumSize); + } + if (maximumSizeChanged) { + emit q_func()->maxSizeChanged(m_currentState.maximiumSize); + } +} + void XdgTopLevelStableInterface::Private::setMaxSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height) { + if (width < 0 || height < 0) { + wl_resource_post_error(resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, "Tried to set invalid xdg-toplevel maximum size"); + return; + } + if (width == 0) { + width = INT32_MAX; + } + if (height == 0) { + height = INT32_MAX; + } auto s = cast(resource); Q_ASSERT(client == *s->client); - s->q_func()->maxSizeChanged(QSize(width, height)); + s->m_pendingState.maximiumSize = QSize(width, height); + s->m_pendingState.maximumSizeIsSet = true; } void XdgTopLevelStableInterface::Private::setMinSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height) { + if (width < 0 || height < 0) { + wl_resource_post_error(resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, "Tried to set invalid xdg-toplevel minimum size"); + return; + } auto s = cast(resource); Q_ASSERT(client == *s->client); - s->q_func()->minSizeChanged(QSize(width, height)); + s->m_pendingState.minimumSize = QSize(width, height); + s->m_pendingState.minimumSizeIsSet = true; +} + +void XdgTopLevelStableInterface::Private::setWindowGeometryCallback(const QRect &rect) +{ + m_pendingState.windowGeometry = rect; + m_pendingState.windowGeometryIsSet = true; } const struct xdg_toplevel_interface XdgTopLevelStableInterface::Private::s_interface = { @@ -831,6 +941,32 @@ { } +QRect XdgPopupStableInterface::Private::windowGeometry() const +{ + return m_currentState.windowGeometry; +} + +void XdgPopupStableInterface::Private::commit() +{ + const bool windowGeometryChanged = m_pendingState.windowGeometryIsSet; + + if (windowGeometryChanged) { + m_currentState.windowGeometry = m_pendingState.windowGeometry; + } + + m_pendingState = ShellSurfaceState{}; + + if (windowGeometryChanged) { + emit q_func()->windowGeometryChanged(m_currentState.windowGeometry); + } +} + +void XdgPopupStableInterface::Private::setWindowGeometryCallback(const QRect &rect) +{ + m_pendingState.windowGeometry = rect; + m_pendingState.windowGeometryIsSet = true; +} + void XdgPopupStableInterface::Private::grabCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial) { Q_UNUSED(client) diff --git a/src/server/xdgshell_v5_interface.cpp b/src/server/xdgshell_v5_interface.cpp --- a/src/server/xdgshell_v5_interface.cpp +++ b/src/server/xdgshell_v5_interface.cpp @@ -1,5 +1,6 @@ /**************************************************************************** Copyright 2016 Martin Gräßlin +Copyright 2019 Vlad Zagorodniy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -71,6 +72,8 @@ Private(XdgPopupV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource); ~Private(); + QRect windowGeometry() const override; + void commit() override; void popupDone() override; XdgPopupV5Interface *q_func() { @@ -220,7 +223,7 @@ { auto client = surface->client()->client(); //from here we can get the resource bound to our global. - + auto clientXdgShellResource = resources.value(client); if (!clientXdgShellResource) { return 0; @@ -243,7 +246,11 @@ Private(XdgSurfaceV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource); ~Private(); + QRect windowGeometry() const override; + QSize minimumSize() const override; + QSize maximumSize() const override; void close() override; + void commit() override; quint32 configure(States states, const QSize &size) override; XdgSurfaceV5Interface *q_func() { @@ -262,6 +269,16 @@ static void setMinimizedCallback(wl_client *client, wl_resource *resource); static const struct zxdg_surface_v5_interface s_interface; + + struct ShellSurfaceState + { + QRect windowGeometry; + + bool windowGeometryIsSet = false; + }; + + ShellSurfaceState m_currentState; + ShellSurfaceState m_pendingState; }; namespace { @@ -360,13 +377,14 @@ void XdgSurfaceV5Interface::Private::setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { - // TODO: implement - Q_UNUSED(client) - Q_UNUSED(resource) - Q_UNUSED(x) - Q_UNUSED(y) - Q_UNUSED(width) - Q_UNUSED(height) + if (width < 0 || height < 0) { + wl_resource_post_error(resource, -1, "Tried to set invalid xdg-surface geometry"); + return; + } + auto s = cast(resource); + Q_ASSERT(client == *s->client); + s->m_pendingState.windowGeometry = QRect(x, y, width, height); + s->m_pendingState.windowGeometryIsSet = true; } void XdgSurfaceV5Interface::Private::setMaximizedCallback(wl_client *client, wl_resource *resource) @@ -415,12 +433,42 @@ XdgSurfaceV5Interface::Private::~Private() = default; +QRect XdgSurfaceV5Interface::Private::windowGeometry() const +{ + return m_currentState.windowGeometry; +} + +QSize XdgSurfaceV5Interface::Private::minimumSize() const +{ + return QSize(0, 0); +} + +QSize XdgSurfaceV5Interface::Private::maximumSize() const +{ + return QSize(INT_MAX, INT_MAX); +} + void XdgSurfaceV5Interface::Private::close() { zxdg_surface_v5_send_close(resource); client->flush(); } +void XdgSurfaceV5Interface::Private::commit() +{ + const bool windowGeometryChanged = m_pendingState.windowGeometryIsSet; + + if (windowGeometryChanged) { + m_currentState.windowGeometry = m_pendingState.windowGeometry; + } + + m_pendingState = ShellSurfaceState{}; + + if (windowGeometryChanged) { + emit q_func()->windowGeometryChanged(m_currentState.windowGeometry); + } +} + quint32 XdgSurfaceV5Interface::Private::configure(States states, const QSize &size) { if (!resource) { @@ -466,6 +514,14 @@ XdgPopupV5Interface::Private::~Private() = default; +QRect XdgPopupV5Interface::Private::windowGeometry() const +{ + return QRect(); +} + +void XdgPopupV5Interface::Private::commit() +{ +} void XdgPopupV5Interface::Private::popupDone() { diff --git a/src/server/xdgshell_v6_interface.cpp b/src/server/xdgshell_v6_interface.cpp --- a/src/server/xdgshell_v6_interface.cpp +++ b/src/server/xdgshell_v6_interface.cpp @@ -1,5 +1,6 @@ /**************************************************************************** Copyright 2017 David Edmundson +Copyright 2019 Vlad Zagorodniy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -74,6 +75,9 @@ Private(XdgPopupV6Interface *q, XdgShellV6Interface *c, SurfaceInterface *surface, wl_resource *parentResource); ~Private(); + QRect windowGeometry() const override; + void commit() override; + void ackConfigure(quint32 serial) { if (!configureSerials.contains(serial)) { // TODO: send error? @@ -95,9 +99,23 @@ return reinterpret_cast(q); } private: + void setWindowGeometryCallback(const QRect &rect); + static void grabCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial); static const struct zxdg_popup_v6_interface s_interface; + + struct ShellSurfaceState + { + QRect windowGeometry; + + bool windowGeometryIsSet = false; + }; + + ShellSurfaceState m_currentState; + ShellSurfaceState m_pendingState; + + friend class XdgSurfaceV6Interface; }; class XdgSurfaceV6Interface::Private : public KWayland::Server::Resource::Private @@ -136,7 +154,11 @@ Private(XdgTopLevelV6Interface* q, XdgShellV6Interface* c, SurfaceInterface* surface, wl_resource* parentResource); ~Private(); + QRect windowGeometry() const override; + QSize minimumSize() const override; + QSize maximumSize() const override; void close() override; + void commit() override; void ackConfigure(quint32 serial) { if (!configureSerials.contains(serial)) { @@ -190,6 +212,8 @@ } private: + void setWindowGeometryCallback(const QRect &rect); + static void destroyCallback(wl_client *client, wl_resource *resource); static void setParentCallback(struct wl_client *client, struct wl_resource *resource, wl_resource *parent); static void showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, int32_t x, int32_t y); @@ -202,6 +226,22 @@ static void setMinimizedCallback(wl_client *client, wl_resource *resource); static const struct zxdg_toplevel_v6_interface s_interface; + + struct ShellSurfaceState + { + QRect windowGeometry; + QSize minimumSize = QSize(0, 0); + QSize maximiumSize = QSize(INT32_MAX, INT32_MAX); + + bool windowGeometryIsSet = false; + bool minimumSizeIsSet = false; + bool maximumSizeIsSet = false; + }; + + ShellSurfaceState m_currentState; + ShellSurfaceState m_pendingState; + + friend class XdgSurfaceV6Interface; }; @@ -441,6 +481,8 @@ void XdgSurfaceV6Interface::Private::createTopLevel(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource) { + // FIXME: That's incorrect! The client may have asked us to create an xdg-toplevel + // for a pointer surface or a subsurface. We have to post an error in that case. if (m_topLevel) { wl_resource_post_error(parentResource, ZXDG_SHELL_V6_ERROR_ROLE, "Toplevel already created on this surface"); return; @@ -464,7 +506,8 @@ void XdgSurfaceV6Interface::Private::createPopup(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource, wl_resource *parentSurface, wl_resource *positioner) { - + // FIXME: That's incorrect! The client may have asked us to create an xdg-popup + // for a pointer surface or a subsurface. We have to post an error in that case. if (m_topLevel) { wl_resource_post_error(parentResource, ZXDG_SHELL_V6_ERROR_ROLE, "Toplevel already created on this surface"); return; @@ -517,13 +560,19 @@ void XdgSurfaceV6Interface::Private::setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { - // TODO: implement - not done for v5 either - Q_UNUSED(client) - Q_UNUSED(resource) - Q_UNUSED(x) - Q_UNUSED(y) - Q_UNUSED(width) - Q_UNUSED(height) + auto s = cast(resource); + Q_ASSERT(client == *s->client); + + if (width < 0 || height < 0) { + wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, "Tried to set invalid xdg-surface geometry"); + return; + } + + if (s->m_topLevel) { + s->m_topLevel->d_func()->setWindowGeometryCallback(QRect(x, y, width, height)); + } else if (s->m_popup) { + s->m_popup->d_func()->setWindowGeometryCallback(QRect(x, y, width, height)); + } } XdgSurfaceV6Interface::Private::Private(XdgSurfaceV6Interface *q, XdgShellV6Interface *c, SurfaceInterface *surface, wl_resource *parentResource) @@ -686,24 +735,61 @@ s->anchorOffset = QPoint(x,y); } +QRect XdgTopLevelV6Interface::Private::windowGeometry() const +{ + return m_currentState.windowGeometry; +} + +QSize XdgTopLevelV6Interface::Private::minimumSize() const +{ + return m_currentState.minimumSize; +} + +QSize XdgTopLevelV6Interface::Private::maximumSize() const +{ + return m_currentState.maximiumSize; +} + void XdgTopLevelV6Interface::Private::close() { zxdg_toplevel_v6_send_close(resource); client->flush(); } void XdgTopLevelV6Interface::Private::setMaxSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height) { + if (width < 0 || height < 0) { + wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, "Tried to set invalid xdg-toplevel maximum size"); + return; + } + if (width == 0) { + width = INT32_MAX; + } + if (height == 0) { + height = INT32_MAX; + } auto s = cast(resource); Q_ASSERT(client == *s->client); - s->q_func()->maxSizeChanged(QSize(width, height)); + s->m_pendingState.maximiumSize = QSize(width, height); + s->m_pendingState.maximumSizeIsSet = true; } void XdgTopLevelV6Interface::Private::setMinSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height) { + if (width < 0 || height < 0) { + wl_resource_post_error(resource, ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, "Tried to set invalid xdg-toplevel minimum size"); + return; + } auto s = cast(resource); Q_ASSERT(client == *s->client); - s->q_func()->minSizeChanged(QSize(width, height)); + s->m_pendingState.minimumSize = QSize(width, height); + s->m_pendingState.minimumSizeIsSet = true; +} + +void XdgTopLevelV6Interface::Private::setWindowGeometryCallback(const QRect &rect) +{ + m_pendingState.windowGeometry = rect; + m_pendingState.windowGeometryIsSet = true; } const struct zxdg_toplevel_v6_interface XdgTopLevelV6Interface::Private::s_interface = { @@ -758,6 +844,35 @@ { } +void XdgTopLevelV6Interface::Private::commit() +{ + const bool windowGeometryChanged = m_pendingState.windowGeometryIsSet; + const bool minimumSizeChanged = m_pendingState.minimumSizeIsSet; + const bool maximumSizeChanged = m_pendingState.maximumSizeIsSet; + + if (windowGeometryChanged) { + m_currentState.windowGeometry = m_pendingState.windowGeometry; + } + if (minimumSizeChanged) { + m_currentState.minimumSize = m_pendingState.minimumSize; + } + if (maximumSizeChanged) { + m_currentState.maximiumSize = m_pendingState.maximiumSize; + } + + m_pendingState = ShellSurfaceState{}; + + if (windowGeometryChanged) { + emit q_func()->windowGeometryChanged(m_currentState.windowGeometry); + } + if (minimumSizeChanged) { + emit q_func()->minSizeChanged(m_currentState.minimumSize); + } + if (maximumSizeChanged) { + emit q_func()->maxSizeChanged(m_currentState.maximiumSize); + } +} + void XdgTopLevelV6Interface::Private::setMaximizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); @@ -821,6 +936,32 @@ XdgPopupV6Interface::Private::~Private() = default; +QRect XdgPopupV6Interface::Private::windowGeometry() const +{ + return m_currentState.windowGeometry; +} + +void XdgPopupV6Interface::Private::commit() +{ + const bool windowGeometryChanged = m_pendingState.windowGeometryIsSet; + + if (windowGeometryChanged) { + m_currentState.windowGeometry = m_pendingState.windowGeometry; + } + + m_pendingState = ShellSurfaceState{}; + + if (windowGeometryChanged) { + emit q_func()->windowGeometryChanged(m_currentState.windowGeometry); + } +} + +void XdgPopupV6Interface::Private::setWindowGeometryCallback(const QRect &rect) +{ + m_pendingState.windowGeometry = rect; + m_pendingState.windowGeometryIsSet = true; +} + quint32 XdgPopupV6Interface::Private::configure(const QRect &rect) { if (!resource) {