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