From 71b125e9676dce577ef5d76111351cdef46502ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 29 Aug 2014 11:42:57 +0200 Subject: [PATCH] [kwin_wayland] Add Shell and ShellSurface to server module ShellSurfaceInterface is not yet completely implemented. Several parts are still TODO, e.g. move/resize is missing, setting to maximized is missing and also flags for fullscreen are missing. The surface test is extended as far as possible. --- src/wayland/display.cpp | 18 ++ src/wayland/display.h | 5 + src/wayland/shell_interface.cpp | 356 ++++++++++++++++++++++++++++++++ src/wayland/shell_interface.h | 181 ++++++++++++++++ 4 files changed, 560 insertions(+) create mode 100644 src/wayland/shell_interface.cpp create mode 100644 src/wayland/shell_interface.h diff --git a/src/wayland/display.cpp b/src/wayland/display.cpp index 6778128704..7a0bf161e7 100644 --- a/src/wayland/display.cpp +++ b/src/wayland/display.cpp @@ -20,6 +20,7 @@ along with this program. If not, see . #include "display.h" #include "compositor_interface.h" #include "output_interface.h" +#include "shell_interface.h" #include #include @@ -131,6 +132,13 @@ CompositorInterface *Display::createCompositor(QObject *parent) return compositor; } +ShellInterface *Display::createShell(QObject *parent) +{ + ShellInterface *shell = new ShellInterface(this, parent); + connect(this, &Display::aboutToTerminate, shell, [this,shell] { delete shell; }); + return shell; +} + void Display::createShm() { Q_ASSERT(m_running); @@ -143,5 +151,15 @@ void Display::removeOutput(OutputInterface *output) delete output; } +quint32 Display::nextSerial() +{ + return wl_display_next_serial(m_display); +} + +quint32 Display::serial() +{ + return wl_display_get_serial(m_display); +} + } } diff --git a/src/wayland/display.h b/src/wayland/display.h index dc9633c7d0..961455ef0c 100644 --- a/src/wayland/display.h +++ b/src/wayland/display.h @@ -33,6 +33,7 @@ namespace WaylandServer class CompositorInterface; class OutputInterface; +class ShellInterface; class Display : public QObject { @@ -46,6 +47,9 @@ public: void setSocketName(const QString &name); QString socketName() const; + quint32 serial(); + quint32 nextSerial(); + void start(); void terminate(); @@ -67,6 +71,7 @@ public: CompositorInterface *createCompositor(QObject *parent = nullptr); void createShm(); + ShellInterface *createShell(QObject *parent = nullptr); Q_SIGNALS: void socketNameChanged(const QString&); diff --git a/src/wayland/shell_interface.cpp b/src/wayland/shell_interface.cpp new file mode 100644 index 0000000000..b2dc5e790c --- /dev/null +++ b/src/wayland/shell_interface.cpp @@ -0,0 +1,356 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 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_interface.h" +#include "display.h" +#include "surface_interface.h" + +#include + +namespace KWin +{ +namespace WaylandServer +{ + +static const quint32 s_version = 1; + +const struct wl_shell_interface ShellInterface::s_interface = { + ShellInterface::createSurfaceCallback +}; + +ShellInterface::ShellInterface(Display *display, QObject *parent) + : QObject(parent) + , m_display(display) + , m_shell(nullptr) +{ +} + +ShellInterface::~ShellInterface() +{ + destroy(); +} + +void ShellInterface::create() +{ + Q_ASSERT(!m_shell); + m_shell = wl_global_create(*m_display, &wl_shell_interface, s_version, this, &ShellInterface::bind); +} + +void ShellInterface::destroy() +{ + if (!m_shell) { + return; + } + wl_global_destroy(m_shell); + m_shell = nullptr; +} + +void ShellInterface::bind(wl_client *client, void *data, uint32_t version, uint32_t id) +{ + reinterpret_cast(data)->bind(client, version, id); +} + +void ShellInterface::bind(wl_client *client, uint32_t version, uint32_t id) +{ + wl_resource *shell = wl_resource_create(client, &wl_shell_interface, qMin(version, s_version), id); + if (!shell) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(shell, &ShellInterface::s_interface, this, nullptr); +} + +void ShellInterface::createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface) +{ + ShellInterface *s = reinterpret_cast(wl_resource_get_user_data(resource)); + s->createSurface(client, wl_resource_get_version(resource), id, + reinterpret_cast(wl_resource_get_user_data(surface))); +} + +void ShellInterface::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface) +{ + auto it = std::find_if(m_surfaces.constBegin(), m_surfaces.constEnd(), + [surface](ShellSurfaceInterface *s) { + return surface == s->surface(); + } + ); + if (it != m_surfaces.constBegin()) { + wl_resource_post_error(surface->surface(), WL_DISPLAY_ERROR_INVALID_OBJECT, "ShellSurface already created"); + return; + } + ShellSurfaceInterface *shellSurface = new ShellSurfaceInterface(this, surface); + m_surfaces << shellSurface; + connect(shellSurface, &ShellSurfaceInterface::destroyed, this, + [this, shellSurface] { + m_surfaces.removeAll(shellSurface); + } + ); + shellSurface->create(client, version, id); + emit surfaceCreated(shellSurface); +} + +/********************************* + * ShellSurfaceInterface + *********************************/ + +const struct wl_shell_surface_interface ShellSurfaceInterface::s_interface = { + ShellSurfaceInterface::pongCallback, + ShellSurfaceInterface::moveCallback, + ShellSurfaceInterface::resizeCallback, + ShellSurfaceInterface::setToplevelCallback, + ShellSurfaceInterface::setTransientCallback, + ShellSurfaceInterface::setFullscreenCallback, + ShellSurfaceInterface::setPopupCalback, + ShellSurfaceInterface::setMaximizedCallback, + ShellSurfaceInterface::setTitleCallback, + ShellSurfaceInterface::setClassCallback +}; + +ShellSurfaceInterface::ShellSurfaceInterface(ShellInterface *shell, SurfaceInterface *parent) + : QObject(parent) + , m_surface(parent) + , m_shell(shell) + , m_shellSurface(nullptr) + , m_client(nullptr) + , m_clientPid(0) + , m_clientUser(0) + , m_clientGroup(0) + , m_title() + , m_windowClass(QByteArray()) + , m_pingTimer(new QTimer(this)) + , m_fullscreen(false) + , m_toplevel(false) +{ + m_pingTimer->setSingleShot(true); + m_pingTimer->setInterval(1000); + connect(m_pingTimer, &QTimer::timeout, this, &ShellSurfaceInterface::pingTimeout); + connect(this, &ShellSurfaceInterface::fullscreenChanged, this, + [this] (bool fullscreen) { + if (!fullscreen) { + return; + } + setToplevel(false); + } + ); + connect(this, &ShellSurfaceInterface::toplevelChanged, this, + [this] (bool toplevel) { + if (!toplevel) { + return; + } + setFullscreen(false); + } + ); +} + +ShellSurfaceInterface::~ShellSurfaceInterface() +{ + if (m_shellSurface) { + wl_resource_destroy(m_shellSurface); + } +} + +void ShellSurfaceInterface::create(wl_client *client, quint32 version, quint32 id) +{ + Q_ASSERT(!m_client); + Q_ASSERT(!m_shellSurface); + m_shellSurface = wl_resource_create(client, &wl_shell_surface_interface, version, id); + if (!m_shellSurface) { + wl_client_post_no_memory(client); + return; + } + m_client = client; + wl_client_get_credentials(m_client, &m_clientPid, &m_clientUser, &m_clientGroup); + + wl_resource_set_implementation(m_shellSurface, &ShellSurfaceInterface::s_interface, this, ShellSurfaceInterface::unbind); +} + +void ShellSurfaceInterface::unbind(wl_resource *r) +{ + ShellSurfaceInterface *s = cast(r); + s->m_shellSurface = nullptr; + s->deleteLater(); +} + +void ShellSurfaceInterface::pongCallback(wl_client *client, wl_resource *resource, uint32_t serial) +{ + ShellSurfaceInterface *s = cast(resource); + Q_ASSERT(client == s->m_client); + s->pong(serial); +} + +void ShellSurfaceInterface::pong(quint32 serial) +{ + if (m_pingTimer->isActive() && serial == m_pingSerial) { + m_pingTimer->stop(); + emit pongReceived(); + } +} + +void ShellSurfaceInterface::ping() +{ + if (m_pingTimer->isActive()) { + return; + } + m_pingSerial = m_shell->display()->nextSerial(); + wl_shell_surface_send_ping(m_shellSurface, m_pingSerial); + wl_client_flush(m_client); + m_pingTimer->start(); +} + +void ShellSurfaceInterface::setPingTimeout(uint msec) +{ + m_pingTimer->setInterval(msec); +} + +bool ShellSurfaceInterface::isPinged() const +{ + return m_pingTimer->isActive(); +} + +void ShellSurfaceInterface::requestSize(const QSize &size) +{ + // TODO: what about the edges? + wl_shell_surface_send_configure(m_shellSurface, 0, size.width(), size.height()); + wl_client_flush(m_client); +} + +void ShellSurfaceInterface::moveCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial) +{ + Q_UNUSED(seat) + Q_UNUSED(serial) + ShellSurfaceInterface *s = cast(resource); + Q_ASSERT(client == s->m_client); + // TODO: implement +} + +void ShellSurfaceInterface::resizeCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, uint32_t edges) +{ + Q_UNUSED(seat) + Q_UNUSED(serial) + Q_UNUSED(edges) + ShellSurfaceInterface *s = cast(resource); + Q_ASSERT(client == s->m_client); + // TODO: implement +} + +void ShellSurfaceInterface::setToplevelCallback(wl_client *client, wl_resource *resource) +{ + ShellSurfaceInterface *s = cast(resource); + Q_ASSERT(client == s->m_client); + s->setToplevel(true); +} + +void ShellSurfaceInterface::setToplevel(bool toplevel) +{ + if (m_toplevel == toplevel) { + return; + } + m_toplevel = toplevel; + emit toplevelChanged(m_toplevel); +} + +void ShellSurfaceInterface::setTransientCallback(wl_client *client, wl_resource *resource, wl_resource *parent, + int32_t x, int32_t y, uint32_t flags) +{ + Q_UNUSED(parent) + Q_UNUSED(x) + Q_UNUSED(y) + Q_UNUSED(flags) + ShellSurfaceInterface *s = cast(resource); + Q_ASSERT(client == s->m_client); + // TODO: implement +} + +void ShellSurfaceInterface::setFullscreenCallback(wl_client *client, wl_resource *resource, uint32_t method, + uint32_t framerate, wl_resource *output) +{ + Q_UNUSED(method) + Q_UNUSED(framerate) + Q_UNUSED(output) + ShellSurfaceInterface *s = cast(resource); + Q_ASSERT(client == s->m_client); + // TODO: add method, framerate and output + s->setFullscreen(true); +} + +void ShellSurfaceInterface::setFullscreen(bool fullscreen) +{ + if (m_fullscreen == fullscreen) { + return; + } + m_fullscreen = fullscreen; + emit fullscreenChanged(m_fullscreen); +} + +void ShellSurfaceInterface::setPopupCalback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, + wl_resource *parent, int32_t x, int32_t y, uint32_t flags) +{ + Q_UNUSED(seat) + Q_UNUSED(serial) + Q_UNUSED(parent) + Q_UNUSED(x) + Q_UNUSED(y) + Q_UNUSED(flags) + ShellSurfaceInterface *s = cast(resource); + Q_ASSERT(client == s->m_client); + // TODO: implement +} + +void ShellSurfaceInterface::setMaximizedCallback(wl_client *client, wl_resource *resource, wl_resource *output) +{ + Q_UNUSED(output) + ShellSurfaceInterface *s = cast(resource); + Q_ASSERT(client == s->m_client); + // TODO: implement +} + +void ShellSurfaceInterface::setTitleCallback(wl_client *client, wl_resource *resource, const char *title) +{ + ShellSurfaceInterface *s = cast(resource); + Q_ASSERT(client == s->m_client); + s->setTitle(QString::fromUtf8(title)); +} + +void ShellSurfaceInterface::setTitle(const QString &title) +{ + if (m_title == title) { + return; + } + m_title = title; + emit titleChanged(m_title); +} + +void ShellSurfaceInterface::setClassCallback(wl_client *client, wl_resource *resource, const char *class_) +{ + ShellSurfaceInterface *s = cast(resource); + Q_ASSERT(client == s->m_client); + s->setWindowClass(QByteArray(class_)); +} + +void ShellSurfaceInterface::setWindowClass(const QByteArray &windowClass) +{ + if (m_windowClass == windowClass) { + return; + } + m_windowClass = windowClass; + emit windowClassChanged(m_windowClass); +} + +} +} diff --git a/src/wayland/shell_interface.h b/src/wayland/shell_interface.h new file mode 100644 index 0000000000..82b6221332 --- /dev/null +++ b/src/wayland/shell_interface.h @@ -0,0 +1,181 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright (C) 2014 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_WAYLAND_SERVER_SHELL_INTERFACE_H +#define KWIN_WAYLAND_SERVER_SHELL_INTERFACE_H + +#include + +#include + +class QSize; +class QTimer; +struct wl_global; + +namespace KWin +{ +namespace WaylandServer +{ + +class Display; +class SurfaceInterface; +class ShellSurfaceInterface; + +class ShellInterface : public QObject +{ + Q_OBJECT +public: + virtual ~ShellInterface(); + + void create(); + void destroy(); + + bool isValid() const { + return m_shell != nullptr; + } + + Display *display() const { + return m_display; + } + +Q_SIGNALS: + void surfaceCreated(KWin::WaylandServer::ShellSurfaceInterface*); + +private: + friend class Display; + explicit ShellInterface(Display *display, QObject *parent); + static void bind(wl_client *client, void *data, uint32_t version, uint32_t id); + static void createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource *surface); + void bind(wl_client *client, uint32_t version, uint32_t id); + void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface); + Display *m_display; + wl_global *m_shell; + static const struct wl_shell_interface s_interface; + QList m_surfaces; +}; + +class ShellSurfaceInterface : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString title READ title NOTIFY titleChanged) + Q_PROPERTY(QByteArray windowClass READ windowClass NOTIFY windowClassChanged) + Q_PROPERTY(bool fullscreen READ isFullscreen NOTIFY fullscreenChanged) + Q_PROPERTY(bool toplevel READ isToplevel NOTIFY toplevelChanged) +public: + virtual ~ShellSurfaceInterface(); + + void ping(); + void setPingTimeout(uint msec); + bool isPinged() const; + void requestSize(const QSize &size); + + SurfaceInterface *surface() const { + return m_surface; + } + ShellInterface *shell() const { + return m_shell; + } + wl_resource *shellSurface() const { + return m_shellSurface; + } + + const QString &title() const { + return m_title; + } + const QByteArray &windowClass() const { + return m_windowClass; + } + bool isFullscreen() const { + return m_fullscreen; + } + bool isToplevel() const { + return m_toplevel; + } + + // TODO: keep them here or add a better encapsulation? + pid_t clientPid() const { + return m_clientPid; + } + uid_t clientUser() const { + return m_clientUser; + } + gid_t clientGroup() const { + return m_clientGroup; + } + +Q_SIGNALS: + void titleChanged(const QString&); + void windowClassChanged(const QByteArray&); + void pingTimeout(); + void pongReceived(); + void fullscreenChanged(bool); + void toplevelChanged(bool); + +private: + friend class ShellInterface; + explicit ShellSurfaceInterface(ShellInterface *shell, SurfaceInterface *parent); + void create(wl_client *client, quint32 version, quint32 id); + void setTitle(const QString &title); + void setWindowClass(const QByteArray &windowClass); + void pong(quint32 serial); + void setFullscreen(bool fullscreen); + void setToplevel(bool toplevel); + + static ShellSurfaceInterface *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + static void unbind(wl_resource *r); + // interface callbacks + static void pongCallback(wl_client *client, wl_resource *resource, uint32_t serial); + static void moveCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial); + static void resizeCallback(wl_client *client, wl_resource *resource, wl_resource *seat, + uint32_t serial, uint32_t edges); + static void setToplevelCallback(wl_client *client, wl_resource *resource); + static void setTransientCallback(wl_client *client, wl_resource *resource, wl_resource *parent, + int32_t x, int32_t y, uint32_t flags); + static void setFullscreenCallback(wl_client *client, wl_resource *resource, uint32_t method, + uint32_t framerate, wl_resource *output); + static void setPopupCalback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, + wl_resource *parent, int32_t x, int32_t y, uint32_t flags); + static void setMaximizedCallback(wl_client *client, wl_resource *resource, wl_resource *output); + static void setTitleCallback(wl_client *client, wl_resource *resource, const char *title); + static void setClassCallback(wl_client *client, wl_resource *resource, const char *class_); + + SurfaceInterface *m_surface; + ShellInterface *m_shell; + wl_resource *m_shellSurface; + wl_client *m_client; + pid_t m_clientPid; + uid_t m_clientUser; + gid_t m_clientGroup; + QString m_title; + QByteArray m_windowClass; + QTimer *m_pingTimer; + quint32 m_pingSerial; + bool m_fullscreen; + bool m_toplevel; + + static const struct wl_shell_surface_interface s_interface; +}; + +} +} + +#endif