diff --git a/CMakeLists.txt b/CMakeLists.txt index e9752dd016..fc7dfd0241 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -442,6 +442,7 @@ if(HAVE_WAYLAND) ${kwin_KDEINIT_SRCS} abstract_backend.cpp virtual_terminal.cpp + shell_client.cpp wayland_server.cpp ) if(HAVE_WAYLAND_CURSOR) diff --git a/composite.cpp b/composite.cpp index be88b1d5e0..ad413b6c27 100644 --- a/composite.cpp +++ b/composite.cpp @@ -39,6 +39,7 @@ along with this program. If not, see . #include "xcbutils.h" #if HAVE_WAYLAND #include "abstract_backend.h" +#include "shell_client.h" #include "wayland_server.h" #endif #include "decorations/decoratedclient.h" @@ -743,6 +744,16 @@ bool Compositor::windowRepaintsPending() const foreach (Toplevel * c, Workspace::self()->deletedList()) if (!c->repaints().isEmpty()) return true; +#if HAVE_WAYLAND + if (auto w = waylandServer()) { + const auto &clients = w->clients(); + for (auto c : clients) { + if (!c->repaints().isEmpty()) { + return true; + } + } + } +#endif return false; } diff --git a/effects.cpp b/effects.cpp index 02286dc1eb..13865dc952 100644 --- a/effects.cpp +++ b/effects.cpp @@ -57,6 +57,7 @@ along with this program. If not, see . #include "xcbutils.h" #if HAVE_WAYLAND #include "abstract_backend.h" +#include "shell_client.h" #include "wayland_server.h" #endif @@ -297,6 +298,18 @@ EffectsHandlerImpl::EffectsHandlerImpl(Compositor *compositor, Scene *scene) for (Unmanaged *u : ws->unmanagedList()) { setupUnmanagedConnections(u); } +#if HAVE_WAYLAND + if (auto w = waylandServer()) { + connect(w, &WaylandServer::shellClientAdded, this, + [this](ShellClient *c) { + if (c->readyForPainting()) + slotShellClientShown(c); + else + connect(c, &Toplevel::windowShown, this, &EffectsHandlerImpl::slotShellClientShown); + } + ); + } +#endif reconfigure(); } @@ -565,6 +578,16 @@ void EffectsHandlerImpl::slotClientShown(KWin::Toplevel *t) emit windowAdded(c->effectWindow()); } +void EffectsHandlerImpl::slotShellClientShown(Toplevel *t) +{ +#if HAVE_WAYLAND + ShellClient *c = static_cast(t); + connect(c, &ShellClient::windowClosed, this, &EffectsHandlerImpl::slotWindowClosed); + connect(c, &ShellClient::geometryShapeChanged, this, &EffectsHandlerImpl::slotGeometryShapeChanged); + emit windowAdded(t->effectWindow()); +#endif +} + void EffectsHandlerImpl::slotUnmanagedShown(KWin::Toplevel *t) { // regardless, unmanaged windows are -yet?- not synced anyway Q_ASSERT(dynamic_cast(t)); diff --git a/effects.h b/effects.h index 59339ae5de..d13d379f83 100644 --- a/effects.h +++ b/effects.h @@ -234,6 +234,7 @@ public Q_SLOTS: protected Q_SLOTS: void slotClientShown(KWin::Toplevel*); + void slotShellClientShown(KWin::Toplevel*); void slotUnmanagedShown(KWin::Toplevel*); void slotWindowClosed(KWin::Toplevel *c); void slotClientMaximized(KWin::Client *c, MaximizeMode maxMode); diff --git a/shell_client.cpp b/shell_client.cpp new file mode 100644 index 0000000000..7e81e5305c --- /dev/null +++ b/shell_client.cpp @@ -0,0 +1,161 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2015 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#include "shell_client.h" +#include "deleted.h" +#include "wayland_server.h" + +#include +#include +#include + +using namespace KWayland::Server; + +namespace KWin +{ + +ShellClient::ShellClient(ShellSurfaceInterface *surface) + : Toplevel() + , m_shellSurface(surface) +{ + setSurface(surface->surface()); + setupCompositing(); + if (surface->surface()->buffer()) { + setReadyForPainting(); + m_clientSize = surface->surface()->buffer()->size(); + } else { + ready_for_painting = false; + } + setGeometry(QRect(QPoint(0, 0), m_clientSize)); + + connect(surface->surface(), &SurfaceInterface::sizeChanged, this, + [this] { + m_clientSize = m_shellSurface->surface()->buffer()->size(); + setGeometry(QRect(QPoint(0, 0), m_clientSize)); + } + ); + connect(surface, &ShellSurfaceInterface::destroyed, this, &ShellClient::destroyClient); + connect(surface->surface(), &SurfaceInterface::unmapped, this, &ShellClient::destroyClient); +} + +ShellClient::~ShellClient() = default; + +void ShellClient::destroyClient() +{ + Deleted *del = Deleted::create(this); + emit windowClosed(this, del); + waylandServer()->removeClient(this); + + del->unrefWindow(); + m_shellSurface = nullptr; + deleteClient(this); +} + +void ShellClient::deleteClient(ShellClient *c) +{ + delete c; +} + +QStringList ShellClient::activities() const +{ + // TODO: implement + return QStringList(); +} + +QPoint ShellClient::clientPos() const +{ + return QPoint(0, 0); +} + +QSize ShellClient::clientSize() const +{ + // TODO: connect for changes + return m_clientSize; +} + +void ShellClient::debug(QDebug &stream) const +{ + // TODO: implement + Q_UNUSED(stream) +} + +int ShellClient::desktop() const +{ + // TODO: implement + return -1; +} + +Layer ShellClient::layer() const +{ + // TODO: implement + return KWin::NormalLayer; +} + +bool ShellClient::shouldUnredirect() const +{ + // TODO: unredirect for fullscreen + return false; +} + +QRect ShellClient::transparentRect() const +{ + // TODO: implement + return QRect(); +} + +NET::WindowType ShellClient::windowType(bool direct, int supported_types) const +{ + // TODO: implement + Q_UNUSED(direct) + Q_UNUSED(supported_types) + return NET::Normal; +} + +double ShellClient::opacity() const +{ + return 1.0; +} + +void ShellClient::addDamage(const QRegion &damage) +{ + setReadyForPainting(); + if (m_shellSurface->surface()->buffer()->size().isValid()) { + m_clientSize = m_shellSurface->surface()->buffer()->size(); + setGeometry(QRect(QPoint(0, 0), m_clientSize)); + } + Toplevel::addDamage(damage); +} + +void ShellClient::setGeometry(const QRect &rect) +{ + if (geom == rect) { + return; + } + const QRect old = geom; + geom = rect; + emit geometryChanged(); + emit geometryShapeChanged(this, old); +} + +QByteArray ShellClient::windowRole() const +{ + return QByteArray(); +} + +} diff --git a/shell_client.h b/shell_client.h new file mode 100644 index 0000000000..9742caf647 --- /dev/null +++ b/shell_client.h @@ -0,0 +1,73 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2015 Martin Gräßlin + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program 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 General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*********************************************************************/ +#ifndef KWIN_SHELL_CLIENT_H +#define KWIN_SHELL_CLIENT_H + +#include "toplevel.h" + +namespace KWayland +{ +namespace Server +{ +class ShellSurfaceInterface; +} +} + +namespace KWin +{ + +class ShellClient : public Toplevel +{ + Q_OBJECT +public: + ShellClient(KWayland::Server::ShellSurfaceInterface *surface); + virtual ~ShellClient(); + + QStringList activities() const override; + QPoint clientPos() const override; + QSize clientSize() const override; + int desktop() const override; + Layer layer() const override; + QRect transparentRect() const override; + bool shouldUnredirect() const override; + NET::WindowType windowType(bool direct = false, int supported_types = 0) const override; + void debug(QDebug &stream) const override; + double opacity() const override; + QByteArray windowRole() const override; + + KWayland::Server::ShellSurfaceInterface *shellSurface() const { + return m_shellSurface; + } + +protected: + void addDamage(const QRegion &damage) override; + +private: + void setGeometry(const QRect &rect); + void destroyClient(); + static void deleteClient(ShellClient *c); + + KWayland::Server::ShellSurfaceInterface *m_shellSurface; + QSize m_clientSize; +}; + +} + +#endif diff --git a/wayland_server.cpp b/wayland_server.cpp index d8a9ab4344..97144d54dc 100644 --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -19,8 +19,9 @@ along with this program. If not, see . *********************************************************************/ #include "wayland_server.h" #include "abstract_backend.h" +#include "composite.h" #include "screens.h" -#include "toplevel.h" +#include "shell_client.h" #include "workspace.h" // Client @@ -78,6 +79,20 @@ void WaylandServer::init(const QByteArray &socketName) ); m_shell = m_display->createShell(m_display); m_shell->create(); + connect(m_shell, &ShellInterface::surfaceCreated, this, + [this] (ShellSurfaceInterface *surface) { + if (surface->client() == m_xwaylandConnection) { + // skip Xwayland clients, those are created using standard X11 way + return; + } + auto client = new ShellClient(surface); + if (auto c = Compositor::self()) { + connect(client, &Toplevel::needsRepaint, c, &Compositor::scheduleRepaint); + } + m_clients << client; + emit shellClientAdded(client); + } + ); m_display->createShm(); m_seat = m_display->createSeat(m_display); m_seat->create(); @@ -163,4 +178,10 @@ void WaylandServer::uninstallBackend(AbstractBackend *backend) m_backend = nullptr; } +void WaylandServer::removeClient(ShellClient *c) +{ + m_clients.removeAll(c); + emit shellClientRemoved(c); +} + } diff --git a/wayland_server.h b/wayland_server.h index d9091f4e60..e408496959 100644 --- a/wayland_server.h +++ b/wayland_server.h @@ -44,6 +44,7 @@ class OutputInterface; namespace KWin { +class ShellClient; class AbstractBackend; @@ -67,6 +68,10 @@ public: KWayland::Server::ShellInterface *shell() { return m_shell; } + QList clients() const { + return m_clients; + } + void removeClient(ShellClient *c); AbstractBackend *backend() const { return m_backend; @@ -98,6 +103,10 @@ public: return m_internalConnection.client; } +Q_SIGNALS: + void shellClientAdded(ShellClient*); + void shellClientRemoved(ShellClient*); + private: KWayland::Server::Display *m_display = nullptr; KWayland::Server::CompositorInterface *m_compositor = nullptr; @@ -112,6 +121,7 @@ private: } m_internalConnection; AbstractBackend *m_backend = nullptr; + QList m_clients; KWIN_SINGLETON(WaylandServer) }; diff --git a/workspace.cpp b/workspace.cpp index aefb7740ea..7309ca5239 100644 --- a/workspace.cpp +++ b/workspace.cpp @@ -52,6 +52,10 @@ along with this program. If not, see . #include "unmanaged.h" #include "useractions.h" #include "virtualdesktops.h" +#if HAVE_WAYLAND +#include "shell_client.h" +#include "wayland_server.h" +#endif #include "xcbutils.h" #include "main.h" #include "decorations/decorationbridge.h" @@ -370,6 +374,27 @@ void Workspace::init() Scripting::create(this); +#if HAVE_WAYLAND + if (auto w = waylandServer()) { + connect(w, &WaylandServer::shellClientAdded, this, + [this] (ShellClient *c) { + if (!unconstrained_stacking_order.contains(c)) + unconstrained_stacking_order.append(c); // Raise if it hasn't got any stacking position yet + if (!stacking_order.contains(c)) // It'll be updated later, and updateToolWindows() requires + stacking_order.append(c); // c to be in stacking_order + x_stacking_dirty = true; + updateStackingOrder(true); + } + ); + connect(w, &WaylandServer::shellClientRemoved, this, + [this] { + x_stacking_dirty = true; + updateStackingOrder(true); + } + ); + } +#endif + // SELI TODO: This won't work with unreasonable focus policies, // and maybe in rare cases also if the selected client doesn't // want focus