diff --git a/cmake/Modules/FindWaylandProtocols.cmake b/cmake/Modules/FindWaylandProtocols.cmake index a3879d3..31492a0 100644 --- a/cmake/Modules/FindWaylandProtocols.cmake +++ b/cmake/Modules/FindWaylandProtocols.cmake @@ -1,60 +1,60 @@ #.rst: # FindWaylandProtocols # ------- # # Try to find wayland-protocols on a Unix system. # # This will define the following variables: # # ``WaylandProtocols_FOUND`` # True if (the requested version of) wayland-protocols is available # ``WaylandProtocols_VERSION`` # The version of wayland-protocols # ``WaylandProtocols_DATADIR`` # The wayland protocols data directory #============================================================================= -# Copyright 2019 Vlad Zagorodniy +# Copyright 2019 Vlad Zagorodniy # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #============================================================================= find_package(PkgConfig) pkg_check_modules(PKG_wayland_protocols QUIET wayland-protocols) set(WaylandProtocols_VERSION ${PKG_wayland_protocols_VERSION}) pkg_get_variable(WaylandProtocols_DATADIR wayland-protocols pkgdatadir) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(WaylandProtocols FOUND_VAR WaylandProtocols_FOUND REQUIRED_VARS WaylandProtocols_DATADIR VERSION_VAR WaylandProtocols_VERSION ) include(FeatureSummary) set_package_properties(WaylandProtocols PROPERTIES DESCRIPTION "Specifications of extended Wayland protocols" URL "https://wayland.freedesktop.org/" ) diff --git a/src/server/surfacerole.cpp b/src/server/surfacerole.cpp index d141055..7d6aff1 100644 --- a/src/server/surfacerole.cpp +++ b/src/server/surfacerole.cpp @@ -1,45 +1,45 @@ /**************************************************************************** -Copyright 2019 Vlad Zagorodniy +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 index 32a5046..218352f 100644 --- a/src/server/surfacerole_p.h +++ b/src/server/surfacerole_p.h @@ -1,50 +1,50 @@ /**************************************************************************** -Copyright 2019 Vlad Zagorodniy +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_stable_interface.cpp b/src/server/xdgshell_stable_interface.cpp index 36b1e33..acd0dd0 100644 --- a/src/server/xdgshell_stable_interface.cpp +++ b/src/server/xdgshell_stable_interface.cpp @@ -1,1121 +1,1121 @@ /**************************************************************************** Copyright 2017 David Edmundson -Copyright 2019 Vlad Zagorodniy +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 "xdgshell_stable_interface_p.h" #include "xdgshell_interface_p.h" #include "generic_shell_surface_p.h" #include "display.h" #include "global_p.h" #include "global.h" #include "resource_p.h" #include "output_interface.h" #include "seat_interface.h" #include "surface_interface.h" #include namespace KWayland { namespace Server { class XdgShellStableInterface::Private : public XdgShellInterface::Private { public: Private(XdgShellStableInterface *q, Display *d); QVector surfaces; QVector positioners; private: void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource); void createPositioner(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource); void bind(wl_client *client, uint32_t version, uint32_t id) override; quint32 ping(XdgShellSurfaceInterface * surface) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return static_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void createPositionerCallback(wl_client *client, wl_resource *resource, uint32_t id); static void getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); static void pongCallback(wl_client *client, wl_resource *resource, uint32_t serial); XdgShellStableInterface *q; static const struct xdg_wm_base_interface s_interface; static const quint32 s_version; QHash resources; }; class XdgPopupStableInterface::Private : public XdgShellPopupInterface::Private { public: 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; } while (!configureSerials.isEmpty()) { quint32 i = configureSerials.takeFirst(); emit q_func()->configureAcknowledged(i); if (i == serial) { break; } } } void popupDone() override; quint32 configure(const QRect &rect) override; XdgPopupStableInterface *q_func() { 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 { public: Private(XdgSurfaceStableInterface* q, XdgShellStableInterface* c, SurfaceInterface* surface, wl_resource* parentResource); ~Private() override; XdgSurfaceStableInterface *q_func() { return static_cast(q); } void createTopLevel(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource); void createPopup(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource, wl_resource *parentWindow, wl_resource *positioner); XdgShellStableInterface *m_shell; SurfaceInterface *m_surface; //effectively a union, only one of these should be populated. //a surface cannot have two roles QPointer m_topLevel; QPointer m_popup; private: static void destroyCallback(wl_client *client, wl_resource *resource); static void getTopLevelCallback(wl_client *client, wl_resource *resource, uint32_t id); static void getPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *parent, wl_resource *positioner); static void ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial); static void setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static const struct xdg_surface_interface s_interface; }; class XdgTopLevelStableInterface::Private : public XdgShellSurfaceInterface::Private { public: 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)) { return; } while (!configureSerials.isEmpty()) { quint32 i = configureSerials.takeFirst(); emit q_func()->configureAcknowledged(i); if (i == serial) { break; } } } quint32 configure(States states, const QSize &size) override { if (!resource) { return 0; } const quint32 serial = global->display()->nextSerial(); wl_array configureStates; wl_array_init(&configureStates); if (states.testFlag(State::Maximized)) { uint32_t *s = static_cast(wl_array_add(&configureStates, sizeof(uint32_t))); *s = XDG_TOPLEVEL_STATE_MAXIMIZED; } if (states.testFlag(State::Fullscreen)) { uint32_t *s = static_cast(wl_array_add(&configureStates, sizeof(uint32_t))); *s = XDG_TOPLEVEL_STATE_FULLSCREEN; } if (states.testFlag(State::Resizing)) { uint32_t *s = static_cast(wl_array_add(&configureStates, sizeof(uint32_t))); *s = XDG_TOPLEVEL_STATE_RESIZING; } if (states.testFlag(State::Activated)) { uint32_t *s = static_cast(wl_array_add(&configureStates, sizeof(uint32_t))); *s = XDG_TOPLEVEL_STATE_ACTIVATED; } configureSerials << serial; xdg_toplevel_send_configure(resource, size.width(), size.height(), &configureStates); xdg_surface_send_configure(parentResource, serial); client->flush(); wl_array_release(&configureStates); return serial; }; XdgTopLevelStableInterface *q_func() { return static_cast(q); } 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); static void setMaxSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height); static void setMinSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height); static void setMaximizedCallback(wl_client *client, wl_resource *resource); static void unsetMaximizedCallback(wl_client *client, wl_resource *resource); static void setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource *output); static void unsetFullscreenCallback(wl_client *client, wl_resource *resource); 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; }; const quint32 XdgShellStableInterface::Private::s_version = 1; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct xdg_wm_base_interface XdgShellStableInterface::Private::s_interface = { destroyCallback, createPositionerCallback, getXdgSurfaceCallback, pongCallback }; #endif void XdgShellStableInterface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) auto s = cast(resource); if (!s->surfaces.isEmpty()) { wl_resource_post_error(resource, XDG_WM_BASE_ERROR_DEFUNCT_SURFACES, "WMBase destroyed before surfaces"); } wl_resource_destroy(resource); } void XdgShellStableInterface::Private::createPositionerCallback(wl_client *client, wl_resource *resource, uint32_t id) { auto s = cast(resource); s->createPositioner(client, wl_resource_get_version(resource), id, resource); } void XdgShellStableInterface::Private::getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) { auto s = cast(resource); s->createSurface(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), resource); } void XdgShellStableInterface::Private::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource) { auto it = std::find_if(surfaces.constBegin(), surfaces.constEnd(), [surface](XdgSurfaceStableInterface *s) { return surface == s->surface(); } ); if (it != surfaces.constEnd()) { wl_resource_post_error(surface->resource(), XDG_WM_BASE_ERROR_ROLE, "XDG Surface already created"); return; } XdgSurfaceStableInterface *shellSurface = new XdgSurfaceStableInterface(q, surface, parentResource); surfaces << shellSurface; QObject::connect(shellSurface, &XdgSurfaceStableInterface::destroyed, q, [this, shellSurface] { surfaces.removeAll(shellSurface); } ); shellSurface->d->create(display->getConnection(client), version, id); } void XdgShellStableInterface::Private::createPositioner(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource) { Q_UNUSED(client) XdgPositionerStableInterface *positioner = new XdgPositionerStableInterface(q, parentResource); positioners << positioner; QObject::connect(positioner, &Resource::destroyed, q, [this, positioner] { positioners.removeAll(positioner); } ); positioner->d->create(display->getConnection(client), version, id); } void XdgShellStableInterface::Private::pongCallback(wl_client *client, wl_resource *resource, uint32_t serial) { Q_UNUSED(client) auto s = cast(resource); auto timerIt = s->pingTimers.find(serial); if (timerIt != s->pingTimers.end() && timerIt.value()->isActive()) { delete timerIt.value(); s->pingTimers.erase(timerIt); emit s->q->pongReceived(serial); } } XdgShellStableInterface::Private::Private(XdgShellStableInterface *q, Display *d) : XdgShellInterface::Private(XdgShellInterfaceVersion::Stable, q, d, &xdg_wm_base_interface, 1) , q(q) { } void XdgShellStableInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); auto resource = c->createResource(&xdg_wm_base_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } resources[client] = resource; wl_resource_set_implementation(resource, &s_interface, this, unbind); } void XdgShellStableInterface::Private::unbind(wl_resource *resource) { auto s = cast(resource); auto client = wl_resource_get_client(resource); s->resources.remove(client); } XdgTopLevelStableInterface *XdgShellStableInterface::getSurface(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); for (auto it = d->surfaces.constBegin(); it != d->surfaces.constEnd() ; it++) { auto topLevel = (*it)->topLevel(); if (topLevel && topLevel->resource() == resource) { return topLevel; } } return nullptr; } XdgSurfaceStableInterface *XdgShellStableInterface::realGetSurface(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); for (auto it = d->surfaces.constBegin(); it != d->surfaces.constEnd() ; it++) { if ((*it)->resource() == resource) { return (*it); } } return nullptr; } XdgPositionerStableInterface *XdgShellStableInterface::getPositioner(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); for (auto it = d->positioners.constBegin(); it != d->positioners.constEnd() ; it++) { if ((*it)->resource() == resource) { return *it; } } return nullptr; } quint32 XdgShellStableInterface::Private::ping(XdgShellSurfaceInterface *surface) { 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; } const quint32 pingSerial = display->nextSerial(); xdg_wm_base_send_ping(clientXdgShellResource, pingSerial); setupTimer(pingSerial); return pingSerial; } XdgShellStableInterface::Private *XdgShellStableInterface::d_func() const { return static_cast(d.data()); } namespace { template <> Qt::Edges edgesToQtEdges(xdg_toplevel_resize_edge edges) { Qt::Edges qtEdges; switch (edges) { case XDG_TOPLEVEL_RESIZE_EDGE_TOP: qtEdges = Qt::TopEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM: qtEdges = Qt::BottomEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_LEFT: qtEdges = Qt::LeftEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT: qtEdges = Qt::TopEdge | Qt::LeftEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT: qtEdges = Qt::BottomEdge | Qt::LeftEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_RIGHT: qtEdges = Qt::RightEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT: qtEdges = Qt::TopEdge | Qt::RightEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT: qtEdges = Qt::BottomEdge | Qt::RightEdge; break; case XDG_TOPLEVEL_RESIZE_EDGE_NONE: break; default: Q_UNREACHABLE(); break; } return qtEdges; } } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct xdg_surface_interface XdgSurfaceStableInterface::Private::s_interface = { destroyCallback, getTopLevelCallback, getPopupCallback, setWindowGeometryCallback, ackConfigureCallback }; #endif void XdgSurfaceStableInterface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void XdgSurfaceStableInterface::Private::getTopLevelCallback(wl_client *client, wl_resource *resource, uint32_t id) { auto s = cast(resource); s->createTopLevel(client, wl_resource_get_version(resource), id, resource); } 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; } if (m_popup) { wl_resource_post_error(parentResource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, "Popup already created on this surface"); return; } m_topLevel = new XdgTopLevelStableInterface (m_shell, m_surface, parentResource); m_topLevel->d->create(m_shell->display()->getConnection(client), version, id); emit m_shell->surfaceCreated(m_topLevel); } void XdgSurfaceStableInterface::Private::getPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *parent, wl_resource *positioner) { auto s = cast(resource); s->createPopup(client, wl_resource_get_version(resource), id, resource, parent, positioner); } 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; } if (m_popup) { wl_resource_post_error(parentResource, XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED, "Popup already created on this surface"); return; } auto xdgPositioner = m_shell->getPositioner(positioner); if (!xdgPositioner) { wl_resource_post_error(parentResource, XDG_WM_BASE_ERROR_INVALID_POSITIONER, "Invalid positioner"); return; } m_popup = new XdgPopupStableInterface(m_shell, m_surface, parentResource); auto pd = m_popup->d_func(); pd->create(m_shell->display()->getConnection(client), version, id); auto parentXdgSurface = m_shell->realGetSurface(parentSurface); if (!parentXdgSurface) { wl_resource_post_error(parentResource, XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT, "Invalid popup parent"); return; } pd->parent = parentXdgSurface->surface(); pd->initialSize = xdgPositioner->initialSize(); pd->anchorRect = xdgPositioner->anchorRect(); pd->anchorEdge = xdgPositioner->anchorEdge(); pd->gravity = xdgPositioner->gravity(); pd->constraintAdjustments = xdgPositioner->constraintAdjustments(); pd->anchorOffset = xdgPositioner->anchorOffset(); emit m_shell->xdgPopupCreated(m_popup.data()); } void XdgSurfaceStableInterface::Private::ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (s->m_topLevel) { s->m_topLevel->d_func()->ackConfigure(serial); } else if (s->m_popup) { s->m_popup->d_func()->ackConfigure(serial); } } void XdgSurfaceStableInterface::Private::setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { auto s = cast(resource); Q_ASSERT(client == *s->client); 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()->setWindowGeometryCallback(QRect(x, y, width, height)); } else if (s->m_popup) { s->m_popup->d_func()->setWindowGeometryCallback(QRect(x, y, width, height)); } } XdgSurfaceStableInterface::Private::Private(XdgSurfaceStableInterface *q, XdgShellStableInterface *c, SurfaceInterface *surface, wl_resource *parentResource) : KWayland::Server::Resource::Private(q, c, parentResource, &xdg_surface_interface, &s_interface), m_shell(c), m_surface(surface) { } XdgSurfaceStableInterface::Private::~Private() = default; class XdgPositionerStableInterface::Private : public KWayland::Server::Resource::Private { public: Private(XdgPositionerStableInterface *q, XdgShellStableInterface *c, wl_resource* parentResource); QSize initialSize; QRect anchorRect; Qt::Edges anchorEdge; Qt::Edges gravity; PositionerConstraints constraintAdjustments; QPoint anchorOffset; private: static void setSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height); static void setAnchorRectCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static void setAnchorCallback(wl_client *client, wl_resource *resource, uint32_t anchor); static void setGravityCallback(wl_client *client, wl_resource *resource, uint32_t gravity); static void setConstraintAdjustmentCallback(wl_client *client, wl_resource *resource, uint32_t constraint_adjustment); static void setOffsetCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y); static const struct xdg_positioner_interface s_interface; }; XdgPositionerStableInterface::Private::Private(XdgPositionerStableInterface *q, XdgShellStableInterface *c, wl_resource *parentResource) : KWayland::Server::Resource::Private(q, c, parentResource, &xdg_positioner_interface, &s_interface) { } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct xdg_positioner_interface XdgPositionerStableInterface::Private::s_interface = { resourceDestroyedCallback, setSizeCallback, setAnchorRectCallback, setAnchorCallback, setGravityCallback, setConstraintAdjustmentCallback, setOffsetCallback }; #endif void XdgPositionerStableInterface::Private::setSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height) { Q_UNUSED(client) auto s = cast(resource); s->initialSize = QSize(width, height); } void XdgPositionerStableInterface::Private::setAnchorRectCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { Q_UNUSED(client) auto s = cast(resource); s->anchorRect = QRect(x, y, width, height); } void XdgPositionerStableInterface::Private::setAnchorCallback(wl_client *client, wl_resource *resource, uint32_t anchor) { Q_UNUSED(client) auto s = cast(resource); Qt::Edges qtEdges; switch (anchor) { case XDG_POSITIONER_ANCHOR_TOP: qtEdges = Qt::TopEdge; break; case XDG_POSITIONER_ANCHOR_BOTTOM: qtEdges = Qt::BottomEdge; break; case XDG_POSITIONER_ANCHOR_LEFT: qtEdges = Qt::LeftEdge; break; case XDG_POSITIONER_ANCHOR_TOP_LEFT: qtEdges = Qt::TopEdge | Qt::LeftEdge; break; case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: qtEdges = Qt::BottomEdge | Qt::LeftEdge; break; case XDG_POSITIONER_ANCHOR_RIGHT: qtEdges = Qt::RightEdge; break; case XDG_POSITIONER_ANCHOR_TOP_RIGHT: qtEdges = Qt::TopEdge | Qt::RightEdge; break; case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: qtEdges = Qt::BottomEdge | Qt::RightEdge; break; case XDG_POSITIONER_ANCHOR_NONE: break; default: Q_UNREACHABLE(); break; } s->anchorEdge = qtEdges; } void XdgPositionerStableInterface::Private::setGravityCallback(wl_client *client, wl_resource *resource, uint32_t gravity) { Q_UNUSED(client) auto s = cast(resource); Qt::Edges qtEdges; switch (gravity) { case XDG_POSITIONER_GRAVITY_TOP: qtEdges = Qt::TopEdge; break; case XDG_POSITIONER_GRAVITY_BOTTOM: qtEdges = Qt::BottomEdge; break; case XDG_POSITIONER_GRAVITY_LEFT: qtEdges = Qt::LeftEdge; break; case XDG_POSITIONER_GRAVITY_TOP_LEFT: qtEdges = Qt::TopEdge | Qt::LeftEdge; break; case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT: qtEdges = Qt::BottomEdge | Qt::LeftEdge; break; case XDG_POSITIONER_GRAVITY_RIGHT: qtEdges = Qt::RightEdge; break; case XDG_POSITIONER_GRAVITY_TOP_RIGHT: qtEdges = Qt::TopEdge | Qt::RightEdge; break; case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT: qtEdges = Qt::BottomEdge | Qt::RightEdge; break; case XDG_POSITIONER_GRAVITY_NONE: break; default: Q_UNREACHABLE(); break; } s->gravity = qtEdges; } void XdgPositionerStableInterface::Private::setConstraintAdjustmentCallback(wl_client *client, wl_resource *resource, uint32_t constraint_adjustment) { Q_UNUSED(client) auto s = cast(resource); PositionerConstraints constraints; if (constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X) { constraints |= PositionerConstraint::SlideX; } if (constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y) { constraints |= PositionerConstraint::SlideY; } if (constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X) { constraints |= PositionerConstraint::FlipX; } if (constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y) { constraints |= PositionerConstraint::FlipY; } if (constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X) { constraints |= PositionerConstraint::ResizeX; } if (constraint_adjustment & XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y) { constraints |= PositionerConstraint::ResizeY; } s->constraintAdjustments = constraints; } void XdgPositionerStableInterface::Private::setOffsetCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y) { Q_UNUSED(client) auto s = cast(resource); 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->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->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 = { destroyCallback, setParentCallback, setTitleCallback, setAppIdCallback, showWindowMenuCallback, moveCallback, resizeCallback, setMaxSizeCallback, setMinSizeCallback, setMaximizedCallback, unsetMaximizedCallback, setFullscreenCallback, unsetFullscreenCallback, setMinimizedCallback }; void XdgTopLevelStableInterface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void XdgTopLevelStableInterface::Private::setParentCallback(wl_client *client, wl_resource *resource, wl_resource *parent) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (!parent) { //setting null is valid API. Clear s->parent = nullptr; emit s->q_func()->transientForChanged(); } else { auto parentSurface = static_cast(s->q->global())->getSurface(parent); if (s->parent.data() != parentSurface) { s->parent = QPointer(parentSurface); emit s->q_func()->transientForChanged(); } } } void XdgTopLevelStableInterface::Private::showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, int32_t x, int32_t y) { auto s = cast(resource); Q_ASSERT(client == *s->client); emit s->q_func()->windowMenuRequested(SeatInterface::get(seat), serial, QPoint(x, y)); } XdgTopLevelStableInterface::Private::Private(XdgTopLevelStableInterface *q, XdgShellStableInterface *c, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellSurfaceInterface::Private(XdgShellInterfaceVersion::Stable, q, c, surface, parentResource, &xdg_toplevel_interface, &s_interface) { } void XdgTopLevelStableInterface::Private::setMaximizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->maximizedChanged(true); } void XdgTopLevelStableInterface::Private::unsetMaximizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->maximizedChanged(false); } void XdgTopLevelStableInterface::Private::setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource *output) { auto s = cast(resource); Q_ASSERT(client == *s->client); OutputInterface *o = nullptr; if (output) { o = OutputInterface::get(output); } s->q_func()->fullscreenChanged(true, o); } void XdgTopLevelStableInterface::Private::unsetFullscreenCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->fullscreenChanged(false, nullptr); } void XdgTopLevelStableInterface::Private::setMinimizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->minimizeRequested(); } XdgTopLevelStableInterface::Private::~Private() = default; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct xdg_popup_interface XdgPopupStableInterface::Private::s_interface = { resourceDestroyedCallback, grabCallback }; #endif XdgPopupStableInterface::Private::Private(XdgPopupStableInterface *q, XdgShellStableInterface *c, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellPopupInterface::Private(XdgShellInterfaceVersion::Stable, q, c, surface, parentResource, &xdg_popup_interface, &s_interface) { } 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) auto s = cast(resource); auto seatInterface = SeatInterface::get(seat); s->q_func()->grabRequested(seatInterface, serial); } XdgPopupStableInterface::Private::~Private() = default; quint32 XdgPopupStableInterface::Private::configure(const QRect &rect) { if (!resource) { return 0; } const quint32 serial = global->display()->nextSerial(); configureSerials << serial; xdg_popup_send_configure(resource, rect.x(), rect.y(), rect.width(), rect.height()); xdg_surface_send_configure(parentResource, serial); client->flush(); return serial; } void XdgPopupStableInterface::Private::popupDone() { if (!resource) { return; } // TODO: dismiss all child popups xdg_popup_send_popup_done(resource); client->flush(); } XdgShellStableInterface::XdgShellStableInterface(Display *display, QObject *parent) : XdgShellInterface(new Private(this, display), parent) { } Display* XdgShellStableInterface::display() const { return d->display; } XdgShellStableInterface::~XdgShellStableInterface() = default; XdgSurfaceStableInterface::XdgSurfaceStableInterface(XdgShellStableInterface *parent, SurfaceInterface *surface, wl_resource *parentResource) : KWayland::Server::Resource(new Private(this, parent, surface, parentResource)) { } XdgSurfaceStableInterface::~XdgSurfaceStableInterface() = default; SurfaceInterface* XdgSurfaceStableInterface::surface() const { Q_D(); return d->m_surface; } XdgPositionerStableInterface::XdgPositionerStableInterface(XdgShellStableInterface *parent, wl_resource *parentResource) : KWayland::Server::Resource(new Private(this, parent, parentResource)) { } QSize XdgPositionerStableInterface::initialSize() const { Q_D(); return d->initialSize; } QRect XdgPositionerStableInterface::anchorRect() const { Q_D(); return d->anchorRect; } Qt::Edges XdgPositionerStableInterface::anchorEdge() const { Q_D(); return d->anchorEdge; } Qt::Edges XdgPositionerStableInterface::gravity() const { Q_D(); return d->gravity; } PositionerConstraints XdgPositionerStableInterface::constraintAdjustments() const { Q_D(); return d->constraintAdjustments; } QPoint XdgPositionerStableInterface::anchorOffset() const { Q_D(); return d->anchorOffset; } XdgPositionerStableInterface::Private *XdgPositionerStableInterface::d_func() const { return static_cast(d.data()); } XdgTopLevelStableInterface* XdgSurfaceStableInterface::topLevel() const { Q_D(); return d->m_topLevel.data(); } XdgPopupStableInterface* XdgSurfaceStableInterface::popup() const { Q_D(); return d->m_popup.data(); } XdgSurfaceStableInterface::Private *XdgSurfaceStableInterface::d_func() const { return static_cast(d.data()); } XdgTopLevelStableInterface::XdgTopLevelStableInterface(XdgShellStableInterface *parent, SurfaceInterface *surface, wl_resource *parentResource) : KWayland::Server::XdgShellSurfaceInterface(new Private(this, parent, surface, parentResource)) { } XdgTopLevelStableInterface::~XdgTopLevelStableInterface() = default; XdgTopLevelStableInterface::Private *XdgTopLevelStableInterface::d_func() const { return static_cast(d.data()); } XdgPopupStableInterface::XdgPopupStableInterface(XdgShellStableInterface *parent, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellPopupInterface(new Private(this, parent, surface, parentResource)) { } XdgPopupStableInterface::~XdgPopupStableInterface() = default; XdgPopupStableInterface::Private *XdgPopupStableInterface::d_func() const { return static_cast(d.data()); } } } diff --git a/src/server/xdgshell_v5_interface.cpp b/src/server/xdgshell_v5_interface.cpp index 9e74128..01ea490 100644 --- a/src/server/xdgshell_v5_interface.cpp +++ b/src/server/xdgshell_v5_interface.cpp @@ -1,564 +1,564 @@ /**************************************************************************** Copyright 2016 Martin Gräßlin -Copyright 2019 Vlad Zagorodniy +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 "xdgshell_v5_interface_p.h" #include "xdgshell_interface_p.h" #include "generic_shell_surface_p.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include "output_interface.h" #include "seat_interface.h" #include "surface_interface.h" #include "../compat/wayland-xdg-shell-v5-server-protocol.h" namespace KWayland { namespace Server { class XdgShellV5Interface::Private : public XdgShellInterface::Private { public: Private(XdgShellV5Interface *q, Display *d); QVector surfaces; private: void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource); void createPopup(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, SurfaceInterface *parent, SeatInterface *seat, quint32 serial, const QPoint &pos, wl_resource *parentResource); void bind(wl_client *client, uint32_t version, uint32_t id) override; quint32 ping(XdgShellSurfaceInterface * surface) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } QHash resources; static void destroyCallback(wl_client *client, wl_resource *resource); static void useUnstableVersionCallback(wl_client *client, wl_resource *resource, int32_t version); static void getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); static void getXdgPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface, wl_resource * parent, wl_resource * seat, uint32_t serial, int32_t x, int32_t y); static void pongCallback(wl_client *client, wl_resource *resource, uint32_t serial); XdgShellV5Interface *q; static const struct zxdg_shell_v5_interface s_interface; static const quint32 s_version; }; class XdgPopupV5Interface::Private : public XdgShellPopupInterface::Private { public: Private(XdgPopupV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource); ~Private(); QRect windowGeometry() const override; void commit() override; void popupDone() override; XdgPopupV5Interface *q_func() { return reinterpret_cast(q); } private: static const struct zxdg_popup_v5_interface s_interface; }; const quint32 XdgShellV5Interface::Private::s_version = 1; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct zxdg_shell_v5_interface XdgShellV5Interface::Private::s_interface = { destroyCallback, useUnstableVersionCallback, getXdgSurfaceCallback, getXdgPopupCallback, pongCallback }; #endif void XdgShellV5Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) // TODO: send protocol error if there are still surfaces mapped wl_resource_destroy(resource); } void XdgShellV5Interface::Private::useUnstableVersionCallback(wl_client *client, wl_resource *resource, int32_t version) { Q_UNUSED(client) Q_UNUSED(resource) Q_UNUSED(version) // TODO: implement } void XdgShellV5Interface::Private::getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) { auto s = cast(resource); s->createSurface(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), resource); } void XdgShellV5Interface::Private::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource) { auto it = std::find_if(surfaces.constBegin(), surfaces.constEnd(), [surface](XdgSurfaceV5Interface *s) { return surface == s->surface(); } ); if (it != surfaces.constEnd()) { wl_resource_post_error(surface->resource(), ZXDG_SHELL_V5_ERROR_ROLE, "ShellSurface already created"); return; } XdgSurfaceV5Interface *shellSurface = new XdgSurfaceV5Interface(q, surface, parentResource); surfaces << shellSurface; QObject::connect(shellSurface, &XdgSurfaceV5Interface::destroyed, q, [this, shellSurface] { surfaces.removeAll(shellSurface); } ); shellSurface->d->create(display->getConnection(client), version, id); emit q->surfaceCreated(shellSurface); } void XdgShellV5Interface::Private::getXdgPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface, wl_resource * parent, wl_resource * seat, uint32_t serial, int32_t x, int32_t y) { auto s = cast(resource); s->createPopup(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), SurfaceInterface::get(parent), SeatInterface::get(seat), serial, QPoint(x, y), resource); } void XdgShellV5Interface::Private::createPopup(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, SurfaceInterface *parent, SeatInterface *seat, quint32 serial, const QPoint &pos, wl_resource *parentResource) { XdgPopupV5Interface *popupSurface = new XdgPopupV5Interface(q, surface, parentResource); auto d = popupSurface->d_func(); d->parent = QPointer(parent); d->anchorRect = QRect(pos, QSize(0,0)); //default open like a normal popup d->anchorEdge = Qt::BottomEdge; d->gravity = Qt::TopEdge; d->create(display->getConnection(client), version, id); //compat emit q->popupCreated(popupSurface, seat, serial); //new system emit q->xdgPopupCreated(popupSurface); emit popupSurface->grabRequested(seat, serial); } void XdgShellV5Interface::Private::pongCallback(wl_client *client, wl_resource *resource, uint32_t serial) { Q_UNUSED(client) auto s = cast(resource); auto timerIt = s->pingTimers.find(serial); if (timerIt != s->pingTimers.end() && timerIt.value()->isActive()) { delete timerIt.value(); s->pingTimers.erase(timerIt); emit s->q->pongReceived(serial); } } XdgShellV5Interface::Private::Private(XdgShellV5Interface *q, Display *d) : XdgShellInterface::Private(XdgShellInterfaceVersion::UnstableV5, q, d, &zxdg_shell_v5_interface, s_version) , q(q) { } void XdgShellV5Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); auto resource = c->createResource(&zxdg_shell_v5_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } resources[client] = resource; wl_resource_set_implementation(resource, &s_interface, this, unbind); } void XdgShellV5Interface::Private::unbind(wl_resource *resource) { auto s = cast(resource); auto client = wl_resource_get_client(resource); s->resources.remove(client); } XdgSurfaceV5Interface *XdgShellV5Interface::getSurface(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); auto it = std::find_if(d->surfaces.constBegin(), d->surfaces.constEnd(), [resource] (XdgSurfaceV5Interface *surface) { return surface->resource() == resource; } ); if (it != d->surfaces.constEnd()) { return *it; } return nullptr; } quint32 XdgShellV5Interface::Private::ping(XdgShellSurfaceInterface * surface) { 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; } const quint32 pingSerial = display->nextSerial(); zxdg_shell_v5_send_ping(clientXdgShellResource, pingSerial); setupTimer(pingSerial); return pingSerial; } XdgShellV5Interface::Private *XdgShellV5Interface::d_func() const { return reinterpret_cast(d.data()); } class XdgSurfaceV5Interface::Private : public XdgShellSurfaceInterface::Private { public: 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() { return reinterpret_cast(q); } private: static void setParentCallback(wl_client *client, 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); static void ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial); static void setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static void setMaximizedCallback(wl_client *client, wl_resource *resource); static void unsetMaximizedCallback(wl_client *client, wl_resource *resource); static void setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource * output); static void unsetFullscreenCallback(wl_client *client, wl_resource *resource); 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 { template <> Qt::Edges edgesToQtEdges(zxdg_surface_v5_resize_edge edges) { Qt::Edges qtEdges; switch (edges) { case ZXDG_SURFACE_V5_RESIZE_EDGE_TOP: qtEdges = Qt::TopEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM: qtEdges = Qt::BottomEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_LEFT: qtEdges = Qt::LeftEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_LEFT: qtEdges = Qt::TopEdge | Qt::LeftEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_LEFT: qtEdges = Qt::BottomEdge | Qt::LeftEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_RIGHT: qtEdges = Qt::RightEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_RIGHT: qtEdges = Qt::TopEdge | Qt::RightEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_RIGHT: qtEdges = Qt::BottomEdge | Qt::RightEdge; break; case ZXDG_SURFACE_V5_RESIZE_EDGE_NONE: break; default: Q_UNREACHABLE(); break; } return qtEdges; } } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct zxdg_surface_v5_interface XdgSurfaceV5Interface::Private::s_interface = { resourceDestroyedCallback, setParentCallback, setTitleCallback, setAppIdCallback, showWindowMenuCallback, moveCallback, resizeCallback, ackConfigureCallback, setWindowGeometryCallback, setMaximizedCallback, unsetMaximizedCallback, setFullscreenCallback, unsetFullscreenCallback, setMinimizedCallback }; #endif void XdgSurfaceV5Interface::Private::setParentCallback(wl_client *client, wl_resource *resource, wl_resource *parent) { auto s = cast(resource); Q_ASSERT(client == *s->client); auto parentSurface = static_cast(s->q->global())->getSurface(parent); if (s->parent.data() != parentSurface) { s->parent = QPointer(parentSurface); emit s->q_func()->transientForChanged(); } } void XdgSurfaceV5Interface::Private::showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, int32_t x, int32_t y) { auto s = cast(resource); Q_ASSERT(client == *s->client); emit s->q_func()->windowMenuRequested(SeatInterface::get(seat), serial, QPoint(x, y)); } void XdgSurfaceV5Interface::Private::ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (!s->configureSerials.contains(serial)) { // TODO: send error? return; } while (!s->configureSerials.isEmpty()) { quint32 i = s->configureSerials.takeFirst(); emit s->q_func()->configureAcknowledged(i); if (i == serial) { break; } } } void XdgSurfaceV5Interface::Private::setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t 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) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->maximizedChanged(true); } void XdgSurfaceV5Interface::Private::unsetMaximizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->maximizedChanged(false); } void XdgSurfaceV5Interface::Private::setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource *output) { auto s = cast(resource); Q_ASSERT(client == *s->client); OutputInterface *o = nullptr; if (output) { o = OutputInterface::get(output); } s->q_func()->fullscreenChanged(true, o); } void XdgSurfaceV5Interface::Private::unsetFullscreenCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->fullscreenChanged(false, nullptr); } void XdgSurfaceV5Interface::Private::setMinimizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->minimizeRequested(); } XdgSurfaceV5Interface::Private::Private(XdgSurfaceV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellSurfaceInterface::Private(XdgShellInterfaceVersion::UnstableV5, q, c, surface, parentResource, &zxdg_surface_v5_interface, &s_interface) { } 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) { return 0; } const quint32 serial = global->display()->nextSerial(); wl_array state; wl_array_init(&state); if (states.testFlag(State::Maximized)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_SURFACE_V5_STATE_MAXIMIZED; } if (states.testFlag(State::Fullscreen)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_SURFACE_V5_STATE_FULLSCREEN; } if (states.testFlag(State::Resizing)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_SURFACE_V5_STATE_RESIZING; } if (states.testFlag(State::Activated)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_SURFACE_V5_STATE_ACTIVATED; } configureSerials << serial; zxdg_surface_v5_send_configure(resource, size.width(), size.height(), &state, serial); client->flush(); wl_array_release(&state); return serial; } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct zxdg_popup_v5_interface XdgPopupV5Interface::Private::s_interface = { resourceDestroyedCallback }; #endif XdgPopupV5Interface::Private::Private(XdgPopupV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellPopupInterface::Private(XdgShellInterfaceVersion::UnstableV5, q, c, surface, parentResource, &zxdg_popup_v5_interface, &s_interface) { } XdgPopupV5Interface::Private::~Private() = default; QRect XdgPopupV5Interface::Private::windowGeometry() const { return QRect(); } void XdgPopupV5Interface::Private::commit() { } void XdgPopupV5Interface::Private::popupDone() { if (!resource) { return; } // TODO: dismiss all child popups zxdg_popup_v5_send_popup_done(resource); client->flush(); } XdgShellV5Interface::XdgShellV5Interface(Display *display, QObject *parent) : XdgShellInterface(new Private(this, display), parent) { } XdgShellV5Interface::~XdgShellV5Interface() = default; XdgSurfaceV5Interface::XdgSurfaceV5Interface(XdgShellV5Interface *parent, SurfaceInterface *surface, wl_resource *parentResource) : KWayland::Server::XdgShellSurfaceInterface(new Private(this, parent, surface, parentResource)) { } XdgSurfaceV5Interface::~XdgSurfaceV5Interface() = default; XdgPopupV5Interface::XdgPopupV5Interface(XdgShellV5Interface *parent, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellPopupInterface(new Private(this, parent, surface, parentResource)) { } XdgPopupV5Interface::~XdgPopupV5Interface() = default; XdgPopupV5Interface::Private *XdgPopupV5Interface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/xdgshell_v6_interface.cpp b/src/server/xdgshell_v6_interface.cpp index 5e67ed0..d2991ec 100644 --- a/src/server/xdgshell_v6_interface.cpp +++ b/src/server/xdgshell_v6_interface.cpp @@ -1,1106 +1,1106 @@ /**************************************************************************** Copyright 2017 David Edmundson -Copyright 2019 Vlad Zagorodniy +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 "xdgshell_v6_interface_p.h" #include "xdgshell_interface_p.h" #include "generic_shell_surface_p.h" #include "display.h" #include "global_p.h" #include "global.h" #include "resource_p.h" #include "output_interface.h" #include "seat_interface.h" #include "surface_interface.h" #include namespace KWayland { namespace Server { class XdgShellV6Interface::Private : public XdgShellInterface::Private { public: Private(XdgShellV6Interface *q, Display *d); QVector surfaces; QVector positioners; private: void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource); void createPositioner(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource); void bind(wl_client *client, uint32_t version, uint32_t id) override; quint32 ping(XdgShellSurfaceInterface * surface) override; static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static void destroyCallback(wl_client *client, wl_resource *resource); static void createPositionerCallback(wl_client *client, wl_resource *resource, uint32_t id); static void getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface); static void pongCallback(wl_client *client, wl_resource *resource, uint32_t serial); XdgShellV6Interface *q; static const struct zxdg_shell_v6_interface s_interface; static const quint32 s_version; QHash resources; }; class XdgPopupV6Interface::Private : public XdgShellPopupInterface::Private { public: 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? return; } while (!configureSerials.isEmpty()) { quint32 i = configureSerials.takeFirst(); emit q_func()->configureAcknowledged(i); if (i == serial) { break; } } } void popupDone() override; quint32 configure(const QRect &rect) override; XdgPopupV6Interface *q_func() { 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 { public: Private(XdgSurfaceV6Interface* q, XdgShellV6Interface* c, SurfaceInterface* surface, wl_resource* parentResource); ~Private(); XdgSurfaceV6Interface *q_func() { return reinterpret_cast(q); } void createTopLevel(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource); void createPopup(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource, wl_resource *parentWindow, wl_resource *positioner); XdgShellV6Interface *m_shell; SurfaceInterface *m_surface; //effectively a union, only one of these should be populated. //a surface cannot have two roles QPointer m_topLevel; QPointer m_popup; private: static void destroyCallback(wl_client *client, wl_resource *resource); static void getTopLevelCallback(wl_client *client, wl_resource *resource, uint32_t id); static void getPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *parent, wl_resource *positioner); static void ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial); static void setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static const struct zxdg_surface_v6_interface s_interface; }; class XdgTopLevelV6Interface::Private : public XdgShellSurfaceInterface::Private { public: 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)) { // TODO: send error? return; } while (!configureSerials.isEmpty()) { quint32 i = configureSerials.takeFirst(); emit q_func()->configureAcknowledged(i); if (i == serial) { break; } } } quint32 configure(States states, const QSize &size) override { if (!resource) { return 0; } const quint32 serial = global->display()->nextSerial(); wl_array state; wl_array_init(&state); if (states.testFlag(State::Maximized)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED; } if (states.testFlag(State::Fullscreen)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN; } if (states.testFlag(State::Resizing)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_TOPLEVEL_V6_STATE_RESIZING; } if (states.testFlag(State::Activated)) { uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t))); *s = ZXDG_TOPLEVEL_V6_STATE_ACTIVATED; } configureSerials << serial; zxdg_toplevel_v6_send_configure(resource, size.width(), size.height(), &state); zxdg_surface_v6_send_configure(parentResource, serial); client->flush(); wl_array_release(&state); return serial; }; XdgTopLevelV6Interface *q_func() { return reinterpret_cast(q); } 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); static void setMaxSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height); static void setMinSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height); static void setMaximizedCallback(wl_client *client, wl_resource *resource); static void unsetMaximizedCallback(wl_client *client, wl_resource *resource); static void setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource *output); static void unsetFullscreenCallback(wl_client *client, wl_resource *resource); 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; }; const quint32 XdgShellV6Interface::Private::s_version = 1; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct zxdg_shell_v6_interface XdgShellV6Interface::Private::s_interface = { destroyCallback, createPositionerCallback, getXdgSurfaceCallback, pongCallback }; #endif void XdgShellV6Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) // TODO: send protocol error if there are still surfaces mapped wl_resource_destroy(resource); } void XdgShellV6Interface::Private::createPositionerCallback(wl_client *client, wl_resource *resource, uint32_t id) { auto s = cast(resource); s->createPositioner(client, wl_resource_get_version(resource), id, resource); } void XdgShellV6Interface::Private::getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) { auto s = cast(resource); s->createSurface(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), resource); } void XdgShellV6Interface::Private::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource) { auto it = std::find_if(surfaces.constBegin(), surfaces.constEnd(), [surface](XdgSurfaceV6Interface *s) { return false; return surface == s->surface(); } ); if (it != surfaces.constEnd()) { wl_resource_post_error(surface->resource(), ZXDG_SHELL_V6_ERROR_ROLE, "ShellSurface already created"); return; } XdgSurfaceV6Interface *shellSurface = new XdgSurfaceV6Interface(q, surface, parentResource); surfaces << shellSurface; QObject::connect(shellSurface, &XdgSurfaceV6Interface::destroyed, q, [this, shellSurface] { surfaces.removeAll(shellSurface); } ); shellSurface->d->create(display->getConnection(client), version, id); } void XdgShellV6Interface::Private::createPositioner(wl_client *client, uint32_t version, uint32_t id, wl_resource *parentResource) { Q_UNUSED(client) XdgPositionerV6Interface *positioner = new XdgPositionerV6Interface(q, parentResource); positioners << positioner; QObject::connect(positioner, &Resource::destroyed, q, [this, positioner] { positioners.removeAll(positioner); } ); positioner->d->create(display->getConnection(client), version, id); } void XdgShellV6Interface::Private::pongCallback(wl_client *client, wl_resource *resource, uint32_t serial) { Q_UNUSED(client) auto s = cast(resource); auto timerIt = s->pingTimers.find(serial); if (timerIt != s->pingTimers.end() && timerIt.value()->isActive()) { delete timerIt.value(); s->pingTimers.erase(timerIt); emit s->q->pongReceived(serial); } } XdgShellV6Interface::Private::Private(XdgShellV6Interface *q, Display *d) : XdgShellInterface::Private(XdgShellInterfaceVersion::UnstableV6, q, d, &zxdg_shell_v6_interface, 1) , q(q) { } void XdgShellV6Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); auto resource = c->createResource(&zxdg_shell_v6_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } resources[client] = resource; wl_resource_set_implementation(resource, &s_interface, this, unbind); } void XdgShellV6Interface::Private::unbind(wl_resource *resource) { auto s = cast(resource); auto client = wl_resource_get_client(resource); s->resources.remove(client); } XdgTopLevelV6Interface *XdgShellV6Interface::getSurface(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); for (auto it = d->surfaces.constBegin(); it != d->surfaces.constEnd() ; it++) { auto topLevel = (*it)->topLevel(); if (topLevel && topLevel->resource() == resource) { return topLevel; } } return nullptr; } XdgSurfaceV6Interface *XdgShellV6Interface::realGetSurface(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); for (auto it = d->surfaces.constBegin(); it != d->surfaces.constEnd() ; it++) { if ((*it)->resource() == resource) { return (*it); } } return nullptr; } XdgPositionerV6Interface *XdgShellV6Interface::getPositioner(wl_resource *resource) { if (!resource) { return nullptr; } Q_D(); for (auto it = d->positioners.constBegin(); it != d->positioners.constEnd() ; it++) { if ((*it)->resource() == resource) { return *it; } } return nullptr; } quint32 XdgShellV6Interface::Private::ping(XdgShellSurfaceInterface *surface) { 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; } const quint32 pingSerial = display->nextSerial(); zxdg_shell_v6_send_ping(clientXdgShellResource, pingSerial); setupTimer(pingSerial); return pingSerial; } XdgShellV6Interface::Private *XdgShellV6Interface::d_func() const { return reinterpret_cast(d.data()); } namespace { template <> Qt::Edges edgesToQtEdges(zxdg_toplevel_v6_resize_edge edges) { Qt::Edges qtEdges; switch (edges) { case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP: qtEdges = Qt::TopEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM: qtEdges = Qt::BottomEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT: qtEdges = Qt::LeftEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT: qtEdges = Qt::TopEdge | Qt::LeftEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT: qtEdges = Qt::BottomEdge | Qt::LeftEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT: qtEdges = Qt::RightEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT: qtEdges = Qt::TopEdge | Qt::RightEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT: qtEdges = Qt::BottomEdge | Qt::RightEdge; break; case ZXDG_TOPLEVEL_V6_RESIZE_EDGE_NONE: break; default: Q_UNREACHABLE(); break; } return qtEdges; } } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct zxdg_surface_v6_interface XdgSurfaceV6Interface::Private::s_interface = { destroyCallback, getTopLevelCallback, getPopupCallback, setWindowGeometryCallback, ackConfigureCallback }; #endif void XdgSurfaceV6Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) //FIXME check if we have attached toplevels first and throw an error wl_resource_destroy(resource); } void XdgSurfaceV6Interface::Private::getTopLevelCallback(wl_client *client, wl_resource *resource, uint32_t id) { auto s = cast(resource); s->createTopLevel(client, wl_resource_get_version(resource), id, resource); } 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; } if (m_popup) { wl_resource_post_error(parentResource, ZXDG_SHELL_V6_ERROR_ROLE, "Popup already created on this surface"); return; } m_topLevel = new XdgTopLevelV6Interface (m_shell, m_surface, parentResource); m_topLevel->d->create(m_shell->display()->getConnection(client), version, id); emit m_shell->surfaceCreated(m_topLevel); } void XdgSurfaceV6Interface::Private::getPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *parent, wl_resource *positioner) { auto s = cast(resource); s->createPopup(client, wl_resource_get_version(resource), id, resource, parent, positioner); } 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; } if (m_popup) { wl_resource_post_error(parentResource, ZXDG_SHELL_V6_ERROR_ROLE, "Popup already created on this surface"); return; } auto xdgPositioner = m_shell->getPositioner(positioner); if (!xdgPositioner) { wl_resource_post_error(parentResource, ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER, "Invalid positioner"); return; } m_popup = new XdgPopupV6Interface(m_shell, m_surface, parentResource); auto pd = m_popup->d_func(); pd->create(m_shell->display()->getConnection(client), version, id); auto parentXdgSurface = m_shell->realGetSurface(parentSurface); if (parentXdgSurface) { pd->parent = parentXdgSurface->surface(); } else { wl_resource_post_error(parentResource, ZXDG_SHELL_V6_ERROR_INVALID_POPUP_PARENT, "Invalid popup parent"); return; } pd->initialSize = xdgPositioner->initialSize(); pd->anchorRect = xdgPositioner->anchorRect(); pd->anchorEdge = xdgPositioner->anchorEdge(); pd->gravity = xdgPositioner->gravity(); pd->constraintAdjustments = xdgPositioner->constraintAdjustments(); pd->anchorOffset = xdgPositioner->anchorOffset(); emit m_shell->xdgPopupCreated(m_popup.data()); } void XdgSurfaceV6Interface::Private::ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (s->m_topLevel) { s->m_topLevel->d_func()->ackConfigure(serial); } else if (s->m_popup) { s->m_popup->d_func()->ackConfigure(serial); } } void XdgSurfaceV6Interface::Private::setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t 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) : KWayland::Server::Resource::Private(q, c, parentResource, &zxdg_surface_v6_interface, &s_interface), m_shell(c), m_surface(surface) { } XdgSurfaceV6Interface::Private::~Private() = default; class XdgPositionerV6Interface::Private : public KWayland::Server::Resource::Private { public: Private(XdgPositionerV6Interface *q, XdgShellV6Interface *c, wl_resource* parentResource); QSize initialSize; QRect anchorRect; Qt::Edges anchorEdge; Qt::Edges gravity; PositionerConstraints constraintAdjustments; QPoint anchorOffset; private: static void setSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height); static void setAnchorRectCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); static void setAnchorCallback(wl_client *client, wl_resource *resource, uint32_t anchor); static void setGravityCallback(wl_client *client, wl_resource *resource, uint32_t gravity); static void setConstraintAdjustmentCallback(wl_client *client, wl_resource *resource, uint32_t constraint_adjustment); static void setOffsetCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y); static const struct zxdg_positioner_v6_interface s_interface; }; XdgPositionerV6Interface::Private::Private(XdgPositionerV6Interface *q, XdgShellV6Interface *c, wl_resource *parentResource) : KWayland::Server::Resource::Private(q, c, parentResource, &zxdg_positioner_v6_interface, &s_interface) { } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct zxdg_positioner_v6_interface XdgPositionerV6Interface::Private::s_interface = { resourceDestroyedCallback, setSizeCallback, setAnchorRectCallback, setAnchorCallback, setGravityCallback, setConstraintAdjustmentCallback, setOffsetCallback }; #endif void XdgPositionerV6Interface::Private::setSizeCallback(wl_client *client, wl_resource *resource, int32_t width, int32_t height) { Q_UNUSED(client) auto s = cast(resource); s->initialSize = QSize(width, height); } void XdgPositionerV6Interface::Private::setAnchorRectCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { Q_UNUSED(client) auto s = cast(resource); s->anchorRect = QRect(x, y, width, height); } void XdgPositionerV6Interface::Private::setAnchorCallback(wl_client *client, wl_resource *resource, uint32_t anchor) { Q_UNUSED(client) auto s = cast(resource); //Note - see David E's email to wayland-devel about this being bad API if ((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) && (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT)) { wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid arguments"); return; } if ((anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) && (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM)) { wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid arguments"); return; } Qt::Edges edges; if (anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT) { edges |= Qt::LeftEdge; } if (anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP) { edges |= Qt::TopEdge; } if (anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) { edges |= Qt::RightEdge; } if (anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) { edges |= Qt::BottomEdge; } s->anchorEdge = edges; } void XdgPositionerV6Interface::Private::setGravityCallback(wl_client *client, wl_resource *resource, uint32_t gravity) { Q_UNUSED(client) auto s = cast(resource); if ((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT) && (gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT)) { wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid arguments"); return; } if ((gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP) && (gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)) { wl_resource_post_error(resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid arguments"); return; } Qt::Edges edges; if (gravity & ZXDG_POSITIONER_V6_ANCHOR_LEFT) { edges |= Qt::LeftEdge; } if (gravity & ZXDG_POSITIONER_V6_ANCHOR_TOP) { edges |= Qt::TopEdge; } if (gravity & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) { edges |= Qt::RightEdge; } if (gravity & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM) { edges |= Qt::BottomEdge; } s->gravity = edges; } void XdgPositionerV6Interface::Private::setConstraintAdjustmentCallback(wl_client *client, wl_resource *resource, uint32_t constraint_adjustment) { Q_UNUSED(client) auto s = cast(resource); PositionerConstraints constraints; if (constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X) { constraints |= PositionerConstraint::SlideX; } if (constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y) { constraints |= PositionerConstraint::SlideY; } if (constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X) { constraints |= PositionerConstraint::FlipX; } if (constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y) { constraints |= PositionerConstraint::FlipY; } if (constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X) { constraints |= PositionerConstraint::ResizeX; } if (constraint_adjustment & ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y) { constraints |= PositionerConstraint::ResizeY; } s->constraintAdjustments = constraints; } void XdgPositionerV6Interface::Private::setOffsetCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y) { Q_UNUSED(client) auto s = cast(resource); 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->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->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 = { destroyCallback, setParentCallback, setTitleCallback, setAppIdCallback, showWindowMenuCallback, moveCallback, resizeCallback, setMaxSizeCallback, setMinSizeCallback, setMaximizedCallback, unsetMaximizedCallback, setFullscreenCallback, unsetFullscreenCallback, setMinimizedCallback }; void XdgTopLevelV6Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) wl_resource_destroy(resource); } void XdgTopLevelV6Interface::Private::setParentCallback(wl_client *client, wl_resource *resource, wl_resource *parent) { auto s = cast(resource); Q_ASSERT(client == *s->client); if (!parent) { //setting null is valid API. Clear s->parent = nullptr; emit s->q_func()->transientForChanged(); } else { auto parentSurface = static_cast(s->q->global())->getSurface(parent); if (s->parent.data() != parentSurface) { s->parent = QPointer(parentSurface); emit s->q_func()->transientForChanged(); } } } void XdgTopLevelV6Interface::Private::showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, int32_t x, int32_t y) { auto s = cast(resource); Q_ASSERT(client == *s->client); emit s->q_func()->windowMenuRequested(SeatInterface::get(seat), serial, QPoint(x, y)); } XdgTopLevelV6Interface::Private::Private(XdgTopLevelV6Interface *q, XdgShellV6Interface *c, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellSurfaceInterface::Private(XdgShellInterfaceVersion::UnstableV6, q, c, surface, parentResource, &zxdg_toplevel_v6_interface, &s_interface) { } 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); Q_ASSERT(client == *s->client); s->q_func()->maximizedChanged(true); } void XdgTopLevelV6Interface::Private::unsetMaximizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->maximizedChanged(false); } void XdgTopLevelV6Interface::Private::setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource *output) { auto s = cast(resource); Q_ASSERT(client == *s->client); OutputInterface *o = nullptr; if (output) { o = OutputInterface::get(output); } s->q_func()->fullscreenChanged(true, o); } void XdgTopLevelV6Interface::Private::unsetFullscreenCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->fullscreenChanged(false, nullptr); } void XdgTopLevelV6Interface::Private::setMinimizedCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); Q_ASSERT(client == *s->client); s->q_func()->minimizeRequested(); } XdgTopLevelV6Interface::Private::~Private() = default; #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct zxdg_popup_v6_interface XdgPopupV6Interface::Private::s_interface = { resourceDestroyedCallback, grabCallback }; #endif XdgPopupV6Interface::Private::Private(XdgPopupV6Interface *q, XdgShellV6Interface *c, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellPopupInterface::Private(XdgShellInterfaceVersion::UnstableV6, q, c, surface, parentResource, &zxdg_popup_v6_interface, &s_interface) { } void XdgPopupV6Interface::Private::grabCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial) { Q_UNUSED(client) auto s = cast(resource); auto seatInterface = SeatInterface::get(seat); s->q_func()->grabRequested(seatInterface, serial); } 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) { return 0; } const quint32 serial = global->display()->nextSerial(); configureSerials << serial; zxdg_popup_v6_send_configure(resource, rect.x(), rect.y(), rect.width(), rect.height()); zxdg_surface_v6_send_configure(parentResource, serial); client->flush(); return serial; } void XdgPopupV6Interface::Private::popupDone() { if (!resource) { return; } // TODO: dismiss all child popups zxdg_popup_v6_send_popup_done(resource); client->flush(); } XdgShellV6Interface::XdgShellV6Interface(Display *display, QObject *parent) : XdgShellInterface(new Private(this, display), parent) { } Display* XdgShellV6Interface::display() const { return d->display; } XdgShellV6Interface::~XdgShellV6Interface() = default; XdgSurfaceV6Interface::XdgSurfaceV6Interface(XdgShellV6Interface *parent, SurfaceInterface *surface, wl_resource *parentResource) : KWayland::Server::Resource(new Private(this, parent, surface, parentResource)) { } XdgSurfaceV6Interface::~XdgSurfaceV6Interface() = default; SurfaceInterface* XdgSurfaceV6Interface::surface() const { Q_D(); return d->m_surface; } XdgPositionerV6Interface::XdgPositionerV6Interface(XdgShellV6Interface *parent, wl_resource *parentResource) : KWayland::Server::Resource(new Private(this, parent, parentResource)) { } QSize XdgPositionerV6Interface::initialSize() const { Q_D(); return d->initialSize; } QRect XdgPositionerV6Interface::anchorRect() const { Q_D(); return d->anchorRect; } Qt::Edges XdgPositionerV6Interface::anchorEdge() const { Q_D(); return d->anchorEdge; } Qt::Edges XdgPositionerV6Interface::gravity() const { Q_D(); return d->gravity; } PositionerConstraints XdgPositionerV6Interface::constraintAdjustments() const { Q_D(); return d->constraintAdjustments; } QPoint XdgPositionerV6Interface::anchorOffset() const { Q_D(); return d->anchorOffset; } XdgPositionerV6Interface::Private *XdgPositionerV6Interface::d_func() const { return reinterpret_cast(d.data()); } XdgTopLevelV6Interface* XdgSurfaceV6Interface::topLevel() const { Q_D(); return d->m_topLevel.data(); } XdgPopupV6Interface* XdgSurfaceV6Interface::popup() const { Q_D(); return d->m_popup.data(); } XdgSurfaceV6Interface::Private *XdgSurfaceV6Interface::d_func() const { return reinterpret_cast(d.data()); } XdgTopLevelV6Interface::XdgTopLevelV6Interface(XdgShellV6Interface *parent, SurfaceInterface *surface, wl_resource *parentResource) : KWayland::Server::XdgShellSurfaceInterface(new Private(this, parent, surface, parentResource)) { } XdgTopLevelV6Interface::~XdgTopLevelV6Interface() = default; XdgTopLevelV6Interface::Private *XdgTopLevelV6Interface::d_func() const { return reinterpret_cast(d.data()); } XdgPopupV6Interface::XdgPopupV6Interface(XdgShellV6Interface *parent, SurfaceInterface *surface, wl_resource *parentResource) : XdgShellPopupInterface(new Private(this, parent, surface, parentResource)) { } XdgPopupV6Interface::~XdgPopupV6Interface() = default; XdgPopupV6Interface::Private *XdgPopupV6Interface::d_func() const { return reinterpret_cast(d.data()); } } }