From 07bdf14dab07d4c7eddbfaff2a67865335236741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 26 Aug 2014 16:07:39 +0200 Subject: [PATCH 1/9] [kwin_wayland] Initial addition of the WaylandServer module So far this new module contains: * Display * OutputInterface Display manages the server socket and server event loop. In general it's the entry point to any part of the server. OutputInterface is the abstraction for the wl_output interface on server side. An OutputInterface is created through the Display. The auto tests for ConnectionThread and Output are adjusted to use the internal server instead of starting Weston. Especially the Output test could be extended to test much more as we have absolute control over the server now. --- src/wayland/display.cpp | 133 +++++++++++ src/wayland/display.h | 85 +++++++ src/wayland/output_interface.cpp | 371 +++++++++++++++++++++++++++++++ src/wayland/output_interface.h | 179 +++++++++++++++ 4 files changed, 768 insertions(+) create mode 100644 src/wayland/display.cpp create mode 100644 src/wayland/display.h create mode 100644 src/wayland/output_interface.cpp create mode 100644 src/wayland/output_interface.h diff --git a/src/wayland/display.cpp b/src/wayland/display.cpp new file mode 100644 index 0000000000..af4b410ffe --- /dev/null +++ b/src/wayland/display.cpp @@ -0,0 +1,133 @@ +/******************************************************************** + 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 "display.h" +#include "output_interface.h" + +#include +#include +#include +#include + +#include + +namespace KWin +{ +namespace WaylandServer +{ + +Display::Display(QObject *parent) + : QObject(parent) + , m_display(nullptr) + , m_loop(nullptr) + , m_socketName(QStringLiteral("wayland-0")) + , m_running(false) +{ + connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, &Display::flush); +} + +Display::~Display() +{ + terminate(); +} + +void Display::flush() +{ + if (!m_display || !m_loop) { + return; + } + if (wl_event_loop_dispatch(m_loop, 0) != 0) { + qWarning() << "Error on dispatching Wayland event loop"; + } + wl_display_flush_clients(m_display); +} + +void Display::setSocketName(const QString &name) +{ + if (m_socketName == name) { + return; + } + m_socketName = name; + emit socketNameChanged(m_socketName); +} + +QString Display::socketName() const +{ + return m_socketName; +} + +void Display::start() +{ + Q_ASSERT(!m_running); + Q_ASSERT(!m_display); + m_display = wl_display_create(); + if (wl_display_add_socket(m_display, qPrintable(m_socketName)) != 0) { + return; + } + + m_loop = wl_display_get_event_loop(m_display); + int fd = wl_event_loop_get_fd(m_loop); + if (fd == -1) { + qWarning() << "Did not get the file descriptor for the event loop"; + return; + } + QSocketNotifier *m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read, this); + connect(m_notifier, &QSocketNotifier::activated, this, &Display::flush); + setRunning(true); +} + +void Display::terminate() +{ + if (!m_running) { + return; + } + emit aboutToTerminate(); + wl_display_terminate(m_display); + wl_display_destroy(m_display); + m_display = nullptr; + m_loop = nullptr; + setRunning(false); +} + +void Display::setRunning(bool running) +{ + if (m_running == running) { + return; + } + m_running = running; + emit runningChanged(m_running); +} + +OutputInterface *Display::createOutput(QObject *parent) +{ + OutputInterface *output = new OutputInterface(this, parent); + connect(output, &QObject::destroyed, this, [this,output] { m_outputs.removeAll(output); }); + connect(this, &Display::aboutToTerminate, output, [this,output] { removeOutput(output); }); + m_outputs << output; + return output; +} + +void Display::removeOutput(OutputInterface *output) +{ + m_outputs.removeAll(output); + delete output; +} + +} +} diff --git a/src/wayland/display.h b/src/wayland/display.h new file mode 100644 index 0000000000..c63dbe2a18 --- /dev/null +++ b/src/wayland/display.h @@ -0,0 +1,85 @@ +/******************************************************************** + 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_DISPLAY_H +#define KWIN_WAYLAND_SERVER_DISPLAY_H + +#include +#include + +struct wl_display; +struct wl_event_loop; + +namespace KWin +{ +namespace WaylandServer +{ + +class OutputInterface; + +class Display : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString socketName READ socketName WRITE setSocketName NOTIFY socketNameChanged) + Q_PROPERTY(bool running READ isRunning NOTIFY runningChanged) +public: + explicit Display(QObject *parent = nullptr); + virtual ~Display(); + + void setSocketName(const QString &name); + QString socketName() const; + + void start(); + void terminate(); + + operator wl_display*() { + return m_display; + } + operator wl_display*() const { + return m_display; + } + bool isRunning() const { + return m_running; + } + + OutputInterface *createOutput(QObject *parent = nullptr); + void removeOutput(OutputInterface *output); + const QList outputs() const { + return m_outputs; + } + +Q_SIGNALS: + void socketNameChanged(const QString&); + void runningChanged(bool); + void aboutToTerminate(); + +private: + void flush(); + void setRunning(bool running); + wl_display *m_display; + wl_event_loop *m_loop; + QString m_socketName; + bool m_running; + QList m_outputs; +}; + +} +} + +#endif diff --git a/src/wayland/output_interface.cpp b/src/wayland/output_interface.cpp new file mode 100644 index 0000000000..4614503e6c --- /dev/null +++ b/src/wayland/output_interface.cpp @@ -0,0 +1,371 @@ +/******************************************************************** + 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 "output_interface.h" +#include "display.h" + +#include + +namespace KWin +{ +namespace WaylandServer +{ + +static const quint32 s_version = 2; + +OutputInterface::OutputInterface(Display *display, QObject *parent) + : QObject(parent) + , m_display(display) + , m_output(nullptr) + , m_physicalSize(QSize()) + , m_globalPosition(QPoint()) + , m_manufacturer(QStringLiteral("org.kde.kwin")) + , m_model(QStringLiteral("none")) + , m_scale(1) + , m_subPixel(SubPixel::Unknown) + , m_transform(Transform::Normal) +{ + connect(this, &OutputInterface::currentModeChanged, this, + [this] { + auto currentModeIt = std::find_if(m_modes.constBegin(), m_modes.constEnd(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); }); + if (currentModeIt == m_modes.constEnd()) { + return; + } + for (auto it = m_resources.constBegin(); it != m_resources.constEnd(); ++it) { + sendMode((*it).resource, *currentModeIt); + sendDone(*it); + } + } + ); + connect(this, &OutputInterface::subPixelChanged, this, &OutputInterface::updateGeometry); + connect(this, &OutputInterface::transformChanged, this, &OutputInterface::updateGeometry); + connect(this, &OutputInterface::globalPositionChanged, this, &OutputInterface::updateGeometry); + connect(this, &OutputInterface::modelChanged, this, &OutputInterface::updateGeometry); + connect(this, &OutputInterface::manufacturerChanged, this, &OutputInterface::updateGeometry); + connect(this, &OutputInterface::scaleChanged, this, &OutputInterface::updateScale); +} + +OutputInterface::~OutputInterface() +{ + destroy(); +} + +void OutputInterface::create() +{ + Q_ASSERT(!m_output); + m_output = wl_global_create(*m_display, &wl_output_interface, s_version, this, OutputInterface::bind); +} + +void OutputInterface::destroy() +{ + if (!m_output) { + return; + } + wl_global_destroy(m_output); + m_output = nullptr; +} + +QSize OutputInterface::pixelSize() const +{ + auto it = std::find_if(m_modes.begin(), m_modes.end(), + [](const Mode &mode) { + return mode.flags.testFlag(ModeFlag::Current); + } + ); + if (it == m_modes.end()) { + return QSize(); + } + return (*it).size; +} + +int OutputInterface::refreshRate() const +{ + auto it = std::find_if(m_modes.begin(), m_modes.end(), + [](const Mode &mode) { + return mode.flags.testFlag(ModeFlag::Current); + } + ); + if (it == m_modes.end()) { + return 60000; + } + return (*it).refreshRate; +} + +void OutputInterface::addMode(const QSize &size, OutputInterface::ModeFlags flags, int refreshRate) +{ + Q_ASSERT(!isValid()); + + auto currentModeIt = std::find_if(m_modes.begin(), m_modes.end(), + [](const Mode &mode) { + return mode.flags.testFlag(ModeFlag::Current); + } + ); + if (currentModeIt == m_modes.end() && !flags.testFlag(ModeFlag::Current)) { + // no mode with current flag - enforce + flags |= ModeFlag::Current; + } + if (currentModeIt != m_modes.end() && flags.testFlag(ModeFlag::Current)) { + // another mode has the current flag - remove + (*currentModeIt).flags &= ~uint(ModeFlag::Current); + } + + if (flags.testFlag(ModeFlag::Preferred)) { + // remove from existing Preferred mode + auto preferredIt = std::find_if(m_modes.begin(), m_modes.end(), + [](const Mode &mode) { + return mode.flags.testFlag(ModeFlag::Preferred); + } + ); + if (preferredIt != m_modes.end()) { + (*preferredIt).flags &= ~uint(ModeFlag::Preferred); + } + } + + auto existingModeIt = std::find_if(m_modes.begin(), m_modes.end(), + [size,refreshRate](const Mode &mode) { + return mode.size == size && mode.refreshRate == refreshRate; + } + ); + auto emitChanges = [this,flags,size,refreshRate] { + emit modesChanged(); + if (flags.testFlag(ModeFlag::Current)) { + emit refreshRateChanged(refreshRate); + emit pixelSizeChanged(size); + emit currentModeChanged(); + } + }; + if (existingModeIt != m_modes.end()) { + if ((*existingModeIt).flags == flags) { + // nothing to do + return; + } + (*existingModeIt).flags = flags; + emitChanges(); + return; + } + Mode mode; + mode.size = size; + mode.refreshRate = refreshRate; + mode.flags = flags; + m_modes << mode; + emitChanges(); +} + +void OutputInterface::setCurrentMode(const QSize &size, int refreshRate) +{ + auto currentModeIt = std::find_if(m_modes.begin(), m_modes.end(), + [](const Mode &mode) { + return mode.flags.testFlag(ModeFlag::Current); + } + ); + if (currentModeIt != m_modes.end()) { + // another mode has the current flag - remove + (*currentModeIt).flags &= ~uint(ModeFlag::Current); + } + + auto existingModeIt = std::find_if(m_modes.begin(), m_modes.end(), + [size,refreshRate](const Mode &mode) { + return mode.size == size && mode.refreshRate == refreshRate; + } + ); + + Q_ASSERT(existingModeIt != m_modes.end()); + (*existingModeIt).flags |= ModeFlag::Current; + emit modesChanged(); + emit refreshRateChanged((*existingModeIt).refreshRate); + emit pixelSizeChanged((*existingModeIt).size); + emit currentModeChanged(); +} + +void OutputInterface::bind(wl_client *client, void *data, uint32_t version, uint32_t id) +{ + OutputInterface *output = reinterpret_cast(data); + output->bind(client, version, id); +} + +int32_t OutputInterface::toTransform() const +{ + switch (m_transform) { + case Transform::Normal: + return WL_OUTPUT_TRANSFORM_NORMAL; + case Transform::Rotated90: + return WL_OUTPUT_TRANSFORM_90; + case Transform::Rotated180: + return WL_OUTPUT_TRANSFORM_180; + case Transform::Rotated270: + return WL_OUTPUT_TRANSFORM_270; + case Transform::Flipped: + return WL_OUTPUT_TRANSFORM_FLIPPED; + case Transform::Flipped90: + return WL_OUTPUT_TRANSFORM_FLIPPED_90; + case Transform::Flipped180: + return WL_OUTPUT_TRANSFORM_FLIPPED_180; + case Transform::Flipped270: + return WL_OUTPUT_TRANSFORM_FLIPPED_270; + } + abort(); +} + +int32_t OutputInterface::toSubPixel() const +{ + switch (m_subPixel) { + case SubPixel::Unknown: + return WL_OUTPUT_SUBPIXEL_UNKNOWN; + case SubPixel::None: + return WL_OUTPUT_SUBPIXEL_NONE; + case SubPixel::HorizontalRGB: + return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB; + case SubPixel::HorizontalBGR: + return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR; + case SubPixel::VerticalRGB: + return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB; + case SubPixel::VerticalBGR: + return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR; + } + abort(); +} + +void OutputInterface::bind(wl_client *client, uint32_t version, uint32_t id) +{ + wl_resource *resource = wl_resource_create(client, &wl_output_interface, qMin(version, s_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_user_data(resource, this); + wl_resource_set_destructor(resource, OutputInterface::unbind); + ResourceData r; + r.resource = resource; + r.version = version; + m_resources << r; + + sendGeometry(resource); + sendScale(r); + + auto currentModeIt = m_modes.constEnd(); + for (auto it = m_modes.constBegin(); it != m_modes.constEnd(); ++it) { + const Mode &mode = *it; + if (mode.flags.testFlag(ModeFlag::Current)) { + // needs to be sent as last mode + currentModeIt = it; + continue; + } + sendMode(resource, mode); + } + + if (currentModeIt != m_modes.constEnd()) { + sendMode(resource, *currentModeIt); + } + + sendDone(r); +} + +void OutputInterface::unbind(wl_resource *resource) +{ + OutputInterface *o = reinterpret_cast(wl_resource_get_user_data(resource)); + auto it = std::find_if(o->m_resources.begin(), o->m_resources.end(), [resource](const ResourceData &r) { return r.resource == resource; }); + if (it != o->m_resources.end()) { + o->m_resources.erase(it); + } +} + +void OutputInterface::sendMode(wl_resource *resource, const Mode &mode) +{ + int32_t flags = 0; + if (mode.flags.testFlag(ModeFlag::Current)) { + flags |= WL_OUTPUT_MODE_CURRENT; + } + if (mode.flags.testFlag(ModeFlag::Preferred)) { + flags |= WL_OUTPUT_MODE_PREFERRED; + } + wl_output_send_mode(resource, + flags, + mode.size.width(), + mode.size.height(), + mode.refreshRate); + +} + +void OutputInterface::sendGeometry(wl_resource *resource) +{ + wl_output_send_geometry(resource, + m_globalPosition.x(), + m_globalPosition.y(), + m_physicalSize.width(), + m_physicalSize.height(), + toSubPixel(), + qPrintable(m_manufacturer), + qPrintable(m_model), + toTransform()); +} + +void OutputInterface::sendScale(const OutputInterface::ResourceData &data) +{ + if (data.version < 2) { + return; + } + wl_output_send_scale(data.resource, m_scale); +} + +void OutputInterface::sendDone(const OutputInterface::ResourceData &data) +{ + if (data.version < 2) { + return; + } + wl_output_send_done(data.resource); +} + +void OutputInterface::updateGeometry() +{ + for (auto it = m_resources.constBegin(); it != m_resources.constEnd(); ++it) { + sendGeometry((*it).resource); + sendDone(*it); + } +} + +void OutputInterface::updateScale() +{ + for (auto it = m_resources.constBegin(); it != m_resources.constEnd(); ++it) { + sendScale(*it); + sendDone(*it); + } +} + +#define SETTER(setterName, type, argumentName) \ + void OutputInterface::setterName(type arg) \ + { \ + if (m_##argumentName == arg) { \ + return; \ + } \ + m_##argumentName = arg; \ + emit argumentName##Changed(m_##argumentName); \ + } + +SETTER(setPhysicalSize, const QSize&, physicalSize) +SETTER(setGlobalPosition, const QPoint&, globalPosition) +SETTER(setManufacturer, const QString&, manufacturer) +SETTER(setModel, const QString&, model) +SETTER(setScale, int, scale) +SETTER(setSubPixel, SubPixel, subPixel) +SETTER(setTransform, Transform, transform) + +#undef SETTER + +} +} diff --git a/src/wayland/output_interface.h b/src/wayland/output_interface.h new file mode 100644 index 0000000000..b13410de51 --- /dev/null +++ b/src/wayland/output_interface.h @@ -0,0 +1,179 @@ +/******************************************************************** + 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_OUTPUT_INTERFACE_H +#define KWIN_WAYLAND_SERVER_OUTPUT_INTERFACE_H + +#include +#include +#include + +struct wl_global; +struct wl_client; +struct wl_resource; + +namespace KWin +{ +namespace WaylandServer +{ + +class Display; + +class OutputInterface : public QObject +{ + Q_OBJECT + Q_PROPERTY(QSize physicalSize READ physicalSize WRITE setPhysicalSize NOTIFY physicalSizeChanged) + Q_PROPERTY(QPoint globalPosition READ globalPosition WRITE setGlobalPosition NOTIFY globalPositionChanged) + Q_PROPERTY(QString manufacturer READ manufacturer WRITE setManufacturer NOTIFY manufacturerChanged) + Q_PROPERTY(QString model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QSize pixelSize READ pixelSize NOTIFY pixelSizeChanged) + Q_PROPERTY(int refreshRate READ refreshRate NOTIFY refreshRateChanged) + Q_PROPERTY(int scale READ scale WRITE setScale NOTIFY scaleChanged) +public: + enum class SubPixel { + Unknown, + None, + HorizontalRGB, + HorizontalBGR, + VerticalRGB, + VerticalBGR + }; + enum class Transform { + Normal, + Rotated90, + Rotated180, + Rotated270, + Flipped, + Flipped90, + Flipped180, + Flipped270 + }; + enum class ModeFlag { + Current = 1, + Preferred = 2 + }; + Q_DECLARE_FLAGS(ModeFlags, ModeFlag) + struct Mode { + QSize size = QSize(); + int refreshRate = 60000; + ModeFlags flags; + }; + virtual ~OutputInterface(); + void create(); + void destroy(); + bool isValid() const { + return m_output != nullptr; + } + + const QSize &physicalSize() const { + return m_physicalSize; + } + const QPoint &globalPosition() const { + return m_globalPosition; + } + const QString &manufacturer() const { + return m_manufacturer; + } + const QString &model() const { + return m_model; + } + QSize pixelSize() const; + int refreshRate() const; + int scale() const { + return m_scale; + } + SubPixel subPixel() const { + return m_subPixel; + } + Transform transform() const { + return m_transform; + } + QList modes() const { + return m_modes; + } + + void setPhysicalSize(const QSize &size); + void setGlobalPosition(const QPoint &pos); + void setManufacturer(const QString &manufacturer); + void setModel(const QString &model); + void setScale(int scale); + void setSubPixel(SubPixel subPixel); + void setTransform(Transform transform); + void addMode(const QSize &size, ModeFlags flags = ModeFlags(), int refreshRate = 60000); + void setCurrentMode(const QSize &size, int refreshRate = 60000); + + operator wl_global*() { + return m_output; + } + operator wl_global*() const { + return m_output; + } + +Q_SIGNALS: + void physicalSizeChanged(const QSize&); + void globalPositionChanged(const QPoint&); + void manufacturerChanged(const QString&); + void modelChanged(const QString&); + void pixelSizeChanged(const QSize&); + void refreshRateChanged(int); + void scaleChanged(int); + void subPixelChanged(SubPixel); + void transformChanged(Transform); + void modesChanged(); + void currentModeChanged(); + +private: + struct ResourceData { + wl_resource *resource; + uint32_t version; + }; + friend class Display; + explicit OutputInterface(Display *display, QObject *parent = nullptr); + static void bind(wl_client *client, void *data, uint32_t version, uint32_t id); + static void unbind(wl_resource *resource); + void bind(wl_client *client, uint32_t version, uint32_t id); + int32_t toTransform() const; + int32_t toSubPixel() const; + void sendMode(wl_resource *resource, const Mode &mode); + void sendDone(const ResourceData &data); + void updateGeometry(); + void sendGeometry(wl_resource *resource); + void sendScale(const ResourceData &data); + void updateScale(); + Display *m_display; + wl_global *m_output; + QSize m_physicalSize; + QPoint m_globalPosition; + QString m_manufacturer; + QString m_model; + int m_scale; + SubPixel m_subPixel; + Transform m_transform; + QList m_modes; + QList m_resources; +}; + +} +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(KWin::WaylandServer::OutputInterface::ModeFlags) +Q_DECLARE_METATYPE(KWin::WaylandServer::OutputInterface::SubPixel) +Q_DECLARE_METATYPE(KWin::WaylandServer::OutputInterface::Transform) + +#endif From 992d210e86bd615edff2b69e36fb14ad141d6604 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Thu, 28 Aug 2014 09:52:35 +0200 Subject: [PATCH 2/9] [kwin_wayland] Add CompositorInterface and SurfaceInterface As far as it's currently possible to implement. CompositorInterface is able to create a surface and emits the created SurfaceInterface. It does not yet support regions. The SurfaceInterface is already more complete. It keeps track of the double buffered states and emits signals when one of the values are changed after the committing. It supports frame callbacks and has a hook to mark the frame as rendered. What's still missing are the regions (as it's not implemented in CompositorInterface) and attaching the buffer as we do not yet support creating shm buffers and pools. The client side test is changed to use our own server and extended to test damage and frame callback. The test needs to be extended for scale and transform, but that is still missing in the client side implementation. --- src/wayland/compositor_interface.cpp | 117 ++++++++++++++ src/wayland/compositor_interface.h | 73 +++++++++ src/wayland/display.cpp | 8 + src/wayland/display.h | 3 + src/wayland/surface_interface.cpp | 223 +++++++++++++++++++++++++++ src/wayland/surface_interface.h | 127 +++++++++++++++ 6 files changed, 551 insertions(+) create mode 100644 src/wayland/compositor_interface.cpp create mode 100644 src/wayland/compositor_interface.h create mode 100644 src/wayland/surface_interface.cpp create mode 100644 src/wayland/surface_interface.h diff --git a/src/wayland/compositor_interface.cpp b/src/wayland/compositor_interface.cpp new file mode 100644 index 0000000000..b6e098d5d3 --- /dev/null +++ b/src/wayland/compositor_interface.cpp @@ -0,0 +1,117 @@ +/******************************************************************** + 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 "compositor_interface.h" +#include "display.h" +#include "surface_interface.h" + +namespace KWin +{ +namespace WaylandServer +{ + +static const quint32 s_version = 3; + +const struct wl_compositor_interface CompositorInterface::s_interface = { + CompositorInterface::createSurfaceCallback, + CompositorInterface::createRegionCallback +}; + +CompositorInterface::CompositorInterface(Display *display, QObject *parent) + : QObject(parent) + , m_display(display) + , m_compositor(nullptr) +{ +} + +CompositorInterface::~CompositorInterface() +{ + destroy(); +} + +void CompositorInterface::create() +{ + Q_ASSERT(!m_compositor); + m_compositor = wl_global_create(*m_display, &wl_compositor_interface, s_version, this, CompositorInterface::bind); +} + +void CompositorInterface::destroy() +{ + if (!m_compositor) { + return; + } + wl_global_destroy(m_compositor); + m_compositor = nullptr; +} + +void CompositorInterface::bind(wl_client *client, void *data, uint32_t version, uint32_t id) +{ + CompositorInterface *compositor = reinterpret_cast(data); + compositor->bind(client, version, id); +} + +void CompositorInterface::bind(wl_client *client, uint32_t version, uint32_t id) +{ + wl_resource *resource = wl_resource_create(client, &wl_compositor_interface, qMin(version, s_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &CompositorInterface::s_interface, this, CompositorInterface::unbind); + // TODO: should we track? +} + +void CompositorInterface::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) + // TODO: implement? +} + +void CompositorInterface::createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id) +{ + CompositorInterface::cast(resource)->createSurface(client, resource, id); +} + +void CompositorInterface::createSurface(wl_client *client, wl_resource *resource, uint32_t id) +{ + SurfaceInterface *surface = new SurfaceInterface(this); + surface->create(client, wl_resource_get_version(resource), id); + if (!surface->surface()) { + wl_resource_post_no_memory(resource); + delete surface; + return; + } + emit surfaceCreated(surface); +} + +void CompositorInterface::createRegionCallback(wl_client *client, wl_resource *resource, uint32_t id) +{ + CompositorInterface::cast(resource)->createRegion(client, resource, id); +} + +void CompositorInterface::createRegion(wl_client *client, wl_resource *resource, uint32_t id) +{ + Q_UNUSED(client) + Q_UNUSED(resource) + Q_UNUSED(id) + // TODO: implement +} + +} +} diff --git a/src/wayland/compositor_interface.h b/src/wayland/compositor_interface.h new file mode 100644 index 0000000000..476883c12e --- /dev/null +++ b/src/wayland/compositor_interface.h @@ -0,0 +1,73 @@ +/******************************************************************** + 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_COMPOSITOR_INTERFACE_H +#define KWIN_WAYLAND_SERVER_COMPOSITOR_INTERFACE_H + +#include "surface_interface.h" + +#include + +#include + +namespace KWin +{ +namespace WaylandServer +{ + +class Display; +class SurfaceInterface; + +class CompositorInterface : public QObject +{ + Q_OBJECT +public: + virtual ~CompositorInterface(); + + void create(); + void destroy(); + bool isValid() const { + return m_compositor != nullptr; + } + +Q_SIGNALS: + void surfaceCreated(KWin::WaylandServer::SurfaceInterface*); + +private: + explicit CompositorInterface(Display *display, QObject *parent = nullptr); + friend class Display; + static void bind(wl_client *client, void *data, uint32_t version, uint32_t id); + static void unbind(wl_resource *resource); + static void createSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id); + static void createRegionCallback(wl_client *client, wl_resource *resource, uint32_t id); + void bind(wl_client *client, uint32_t version, uint32_t id); + void createSurface(wl_client *client, wl_resource *resource, uint32_t id); + void createRegion(wl_client *client, wl_resource *resource, uint32_t id); + static CompositorInterface *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + static const struct wl_compositor_interface s_interface; + Display *m_display; + wl_global *m_compositor; +}; + +} +} + +#endif diff --git a/src/wayland/display.cpp b/src/wayland/display.cpp index af4b410ffe..06210ee33c 100644 --- a/src/wayland/display.cpp +++ b/src/wayland/display.cpp @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "display.h" +#include "compositor_interface.h" #include "output_interface.h" #include @@ -123,6 +124,13 @@ OutputInterface *Display::createOutput(QObject *parent) return output; } +CompositorInterface *Display::createCompositor(QObject *parent) +{ + CompositorInterface *compositor = new CompositorInterface(this, parent); + connect(this, &Display::aboutToTerminate, compositor, [this,compositor] { delete compositor; }); + return compositor; +} + void Display::removeOutput(OutputInterface *output) { m_outputs.removeAll(output); diff --git a/src/wayland/display.h b/src/wayland/display.h index c63dbe2a18..d18d9c09ad 100644 --- a/src/wayland/display.h +++ b/src/wayland/display.h @@ -31,6 +31,7 @@ namespace KWin namespace WaylandServer { +class CompositorInterface; class OutputInterface; class Display : public QObject @@ -64,6 +65,8 @@ public: return m_outputs; } + CompositorInterface *createCompositor(QObject *parent = nullptr); + Q_SIGNALS: void socketNameChanged(const QString&); void runningChanged(bool); diff --git a/src/wayland/surface_interface.cpp b/src/wayland/surface_interface.cpp new file mode 100644 index 0000000000..ea0343c95e --- /dev/null +++ b/src/wayland/surface_interface.cpp @@ -0,0 +1,223 @@ +/******************************************************************** + 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 "surface_interface.h" +#include "compositor_interface.h" + +namespace KWin +{ +namespace WaylandServer +{ + +const struct wl_surface_interface SurfaceInterface::s_interface = { + SurfaceInterface::destroyCallback, + SurfaceInterface::attachCallback, + SurfaceInterface::damageCallback, + SurfaceInterface::frameCallaback, + SurfaceInterface::opaqueRegionCallback, + SurfaceInterface::inputRegionCallback, + SurfaceInterface::commitCallback, + SurfaceInterface::bufferTransformCallback, + SurfaceInterface::bufferScaleCallback +}; + +SurfaceInterface::SurfaceInterface(CompositorInterface *parent) + : QObject(parent) + , m_compositor(parent) + , m_surface(nullptr) + , m_client(nullptr) +{ +} + +SurfaceInterface::~SurfaceInterface() +{ + destroy(); +} + +void SurfaceInterface::create(wl_client *client, quint32 version, quint32 id) +{ + Q_ASSERT(!m_surface); + Q_ASSERT(!m_client); + m_client = client; + m_surface = wl_resource_create(client, &wl_surface_interface, version, id); + if (!m_surface) { + return; + } + wl_resource_set_implementation(m_surface, &SurfaceInterface::s_interface, this, SurfaceInterface::unbind); +} + +void SurfaceInterface::frameRendered(quint32 msec) +{ + // notify all callbacks + while (!m_current.callbacks.isEmpty()) { + wl_resource *r = m_current.callbacks.takeFirst(); + wl_callback_send_done(r, msec); + wl_resource_destroy(r); + } +} + +void SurfaceInterface::unbind(wl_resource *r) +{ + SurfaceInterface *s = SurfaceInterface::cast(r); + s->m_surface = nullptr; + s->deleteLater(); +} + +void SurfaceInterface::destroy() +{ + for (wl_resource *c : m_current.callbacks) { + wl_resource_destroy(c); + } + for (wl_resource *c : m_pending.callbacks) { + wl_resource_destroy(c); + } + if (m_surface) { + wl_resource_destroy(m_surface); + m_surface = nullptr; + } +} + +void SurfaceInterface::commit() +{ + for (wl_resource *c : m_current.callbacks) { + wl_resource_destroy(c); + } + const bool opaqueRegionChanged = m_current.opaque != m_pending.opaque; + const bool inputRegionChanged = m_current.input != m_pending.input; + const bool scaleFactorChanged = m_current.scale != m_pending.scale; + const bool transformFactorChanged = m_current.transform != m_pending.transform; + // copy values + m_current = m_pending; + m_pending = State{}; + if (opaqueRegionChanged) { + emit opaqueChanged(m_current.opaque); + } + if (inputRegionChanged) { + emit inputChanged(m_current.input); + } + if (scaleFactorChanged) { + emit scaleChanged(m_current.scale); + } + if (transformFactorChanged) { + emit transformChanged(m_current.transform); + } + if (!m_current.damage.isEmpty()) { + emit damaged(m_current.damage); + } +} + +void SurfaceInterface::damage(const QRect &rect) +{ + // TODO: documentation says we need to remove the parts outside of the surface + m_pending.damage = m_pending.damage.united(rect); +} + +void SurfaceInterface::setScale(qint32 scale) +{ + m_pending.scale = scale; +} + +void SurfaceInterface::setTransform(OutputInterface::Transform transform) +{ + m_pending.transform = transform; +} + +void SurfaceInterface::addFrameCallback(uint32_t callback) +{ + wl_resource *r = wl_resource_create(m_client, &wl_callback_interface, 1, callback); + if (!r) { + wl_resource_post_no_memory(m_surface); + return; + } + wl_resource_set_implementation(r, nullptr, this, destroyFrameCallback); + m_pending.callbacks << r; +} + +void SurfaceInterface::destroyFrameCallback(wl_resource *r) +{ + SurfaceInterface *s = SurfaceInterface::cast(r); + s->m_current.callbacks.removeAll(r); + s->m_pending.callbacks.removeAll(r); +} + +void SurfaceInterface::destroyCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + SurfaceInterface::cast(resource)->deleteLater(); +} + +void SurfaceInterface::attachCallback(wl_client *client, wl_resource *resource, wl_resource *buffer, int32_t sx, int32_t sy) +{ + Q_UNUSED(client) + Q_UNUSED(resource) + Q_UNUSED(buffer) + Q_UNUSED(sx) + Q_UNUSED(sy) + // TODO: implement me +} + +void SurfaceInterface::damageCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) +{ + Q_UNUSED(client) + SurfaceInterface::cast(resource)->damage(QRect(x, y, width, height)); +} + +void SurfaceInterface::frameCallaback(wl_client *client, wl_resource *resource, uint32_t callback) +{ + SurfaceInterface *s = SurfaceInterface::cast(resource); + Q_ASSERT(client == s->m_client); + s->addFrameCallback(callback); +} + +void SurfaceInterface::opaqueRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region) +{ + Q_UNUSED(client) + Q_UNUSED(resource) + Q_UNUSED(region) + // TODO: implement me +} + +void SurfaceInterface::inputRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region) +{ + Q_UNUSED(client) + Q_UNUSED(resource) + Q_UNUSED(region) + // TODO: implement me +} + +void SurfaceInterface::commitCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + SurfaceInterface::cast(resource)->commit(); +} + +void SurfaceInterface::bufferTransformCallback(wl_client *client, wl_resource *resource, int32_t transform) +{ + Q_UNUSED(client) + SurfaceInterface::cast(resource)->setTransform(OutputInterface::Transform(transform)); +} + +void SurfaceInterface::bufferScaleCallback(wl_client *client, wl_resource *resource, int32_t scale) +{ + Q_UNUSED(client) + SurfaceInterface::cast(resource)->setScale(scale); +} + +} +} diff --git a/src/wayland/surface_interface.h b/src/wayland/surface_interface.h new file mode 100644 index 0000000000..659105e4e7 --- /dev/null +++ b/src/wayland/surface_interface.h @@ -0,0 +1,127 @@ +/******************************************************************** + 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_SURFACE_INTERFACE_H +#define KWIN_WAYLAND_SERVER_SURFACE_INTERFACE_H + +#include "output_interface.h" + +#include +#include + +#include + +namespace KWin +{ +namespace WaylandServer +{ +class CompositorInterface; + +class SurfaceInterface : public QObject +{ + Q_OBJECT + Q_PROPERTY(QRegion damage READ damage NOTIFY damaged) + Q_PROPERTY(QRegion opaque READ opaque NOTIFY opaqueChanged) + Q_PROPERTY(QRegion input READ input NOTIFY inputChanged) + Q_PROPERTY(qint32 scale READ scale NOTIFY scaleChanged) + Q_PROPERTY(KWin::WaylandServer::OutputInterface::Transform transform READ transform NOTIFY transformChanged) +public: + virtual ~SurfaceInterface(); + + void create(wl_client *client, quint32 version, quint32 id); + + void frameRendered(quint32 msec); + + wl_resource *surface() const { + return m_surface; + } + wl_client *client() const { + return m_client; + } + + QRegion damage() const { + return m_current.damage; + } + QRegion opaque() const { + return m_current.opaque; + } + QRegion input() const { + return m_current.input; + } + qint32 scale() const { + return m_current.scale; + } + OutputInterface::Transform transform() const { + return m_current.transform; + } + +Q_SIGNALS: + void damaged(const QRegion&); + void opaqueChanged(const QRegion&); + void inputChanged(const QRegion&); + void scaleChanged(qint32); + void transformChanged(KWin::WaylandServer::OutputInterface::Transform); + +private: + struct State { + QRegion damage = QRegion(); + QRegion opaque = QRegion(); + QRegion input = QRegion(); + qint32 scale = 1; + OutputInterface::Transform transform = OutputInterface::Transform::Normal; + QList callbacks = QList(); + }; + friend class CompositorInterface; + explicit SurfaceInterface(CompositorInterface *parent); + static void unbind(wl_resource *r); + static void destroyFrameCallback(wl_resource *r); + + static const struct wl_surface_interface s_interface; + static void destroyCallback(wl_client *client, wl_resource *resource); + static void attachCallback(wl_client *client, wl_resource *resource, wl_resource *buffer, int32_t sx, int32_t sy); + static void damageCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height); + static void frameCallaback(wl_client *client, wl_resource *resource, uint32_t callback); + static void opaqueRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region); + static void inputRegionCallback(wl_client *client, wl_resource *resource, wl_resource *region); + static void commitCallback(wl_client *client, wl_resource *resource); + // since version 2 + static void bufferTransformCallback(wl_client *client, wl_resource *resource, int32_t transform); + // since version 3 + static void bufferScaleCallback(wl_client *client, wl_resource *resource, int32_t scale); + + static SurfaceInterface *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + void destroy(); + void commit(); + void damage(const QRect &rect); + void setScale(qint32 scale); + void setTransform(OutputInterface::Transform transform); + void addFrameCallback(uint32_t callback); + CompositorInterface *m_compositor; + wl_resource *m_surface; + wl_client *m_client; + State m_current; + State m_pending; +}; + +} +} + +#endif From 76cb898c7f3beb71f3b8f1875c1ff45c34d91cc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Thu, 28 Aug 2014 14:22:53 +0200 Subject: [PATCH 3/9] [kwin_wayland] Add support for shm buffers in server module The Display provides a method to create the shm pool and a BufferInterface class is added to the server module. It is created from the SurfaceInterface when a buffer gets attached to the surface. The BufferInterface can be referenced and once its unreferenced it sends a buffer release to the client and destroys itself. For the case that the buffer is a shm buffer the BufferInterface provides a convenience method to turn it into a QImage. The auto test for Surface is extended by attaching buffers to the surface and verifying that the content is correct. --- src/wayland/buffer_interface.cpp | 107 ++++++++++++++++++++++++++++++ src/wayland/buffer_interface.h | 72 ++++++++++++++++++++ src/wayland/display.cpp | 6 ++ src/wayland/display.h | 1 + src/wayland/surface_interface.cpp | 25 +++++-- src/wayland/surface_interface.h | 10 +++ 6 files changed, 216 insertions(+), 5 deletions(-) create mode 100644 src/wayland/buffer_interface.cpp create mode 100644 src/wayland/buffer_interface.h diff --git a/src/wayland/buffer_interface.cpp b/src/wayland/buffer_interface.cpp new file mode 100644 index 0000000000..fb8e7501e2 --- /dev/null +++ b/src/wayland/buffer_interface.cpp @@ -0,0 +1,107 @@ +/******************************************************************** + 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 "buffer_interface.h" +#include "surface_interface.h" + +namespace KWin +{ +namespace WaylandServer +{ + +BufferInterface::BufferInterface(wl_resource *resource, SurfaceInterface *parent) + : QObject(parent) + , m_buffer(resource) + , m_shmBuffer(wl_shm_buffer_get(m_buffer)) + , m_surface(parent) + , m_refCount(0) +{ +} + +BufferInterface::~BufferInterface() +{ + Q_ASSERT(m_refCount == 0); + releaseImage(); +} + +void BufferInterface::releaseImage() +{ + if (m_image.isNull()) { + return; + } + // first destroy it + m_image = QImage(); + wl_shm_buffer_end_access(m_shmBuffer); +} + +void BufferInterface::ref() +{ + m_refCount++; +} + +void BufferInterface::unref() +{ + Q_ASSERT(m_refCount > 0); + m_refCount--; + if (m_refCount == 0) { + releaseImage(); + wl_buffer_send_release(m_buffer); + deleteLater(); + } +} + +QImage::Format BufferInterface::format() const +{ + switch (wl_shm_buffer_get_format(m_shmBuffer)) { + case WL_SHM_FORMAT_ARGB8888: + return QImage::Format_ARGB32; + case WL_SHM_FORMAT_XRGB8888: + return QImage::Format_RGB32; + default: + return QImage::Format_Invalid; + } +} + +QImage BufferInterface::data() +{ + if (m_image.isNull()) { + createImage(); + } + return m_image; +} + +void BufferInterface::createImage() +{ + if (!m_shmBuffer) { + return; + } + const QImage::Format imageFormat = format(); + if (imageFormat == QImage::Format_Invalid) { + return; + } + wl_shm_buffer_begin_access(m_shmBuffer); + m_image = QImage((const uchar*)wl_shm_buffer_get_data(m_shmBuffer), + wl_shm_buffer_get_width(m_shmBuffer), + wl_shm_buffer_get_height(m_shmBuffer), + wl_shm_buffer_get_stride(m_shmBuffer), + imageFormat); +} + +} +} diff --git a/src/wayland/buffer_interface.h b/src/wayland/buffer_interface.h new file mode 100644 index 0000000000..41531c4c93 --- /dev/null +++ b/src/wayland/buffer_interface.h @@ -0,0 +1,72 @@ +/******************************************************************** + 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_BUFFER_INTERFACE_H +#define KWIN_WAYLAND_SERVER_BUFFER_INTERFACE_H + +#include +#include + +struct wl_resource; +struct wl_shm_buffer; + +namespace KWin +{ +namespace WaylandServer +{ +class SurfaceInterface; + + +class BufferInterface : public QObject +{ + Q_OBJECT +public: + virtual ~BufferInterface(); + void ref(); + void unref(); + bool isReferenced() const { + return m_refCount > 0; + } + + SurfaceInterface *surface() const { + return m_surface; + } + wl_shm_buffer *shmBuffer() { + return m_shmBuffer; + } + + QImage data(); + +private: + friend class SurfaceInterface; + explicit BufferInterface(wl_resource *resource, SurfaceInterface *parent); + QImage::Format format() const; + void createImage(); + void releaseImage(); + wl_resource *m_buffer; + wl_shm_buffer *m_shmBuffer; + SurfaceInterface *m_surface; + int m_refCount; + QImage m_image; +}; + +} +} + +#endif diff --git a/src/wayland/display.cpp b/src/wayland/display.cpp index 06210ee33c..6778128704 100644 --- a/src/wayland/display.cpp +++ b/src/wayland/display.cpp @@ -131,6 +131,12 @@ CompositorInterface *Display::createCompositor(QObject *parent) return compositor; } +void Display::createShm() +{ + Q_ASSERT(m_running); + wl_display_init_shm(m_display); +} + void Display::removeOutput(OutputInterface *output) { m_outputs.removeAll(output); diff --git a/src/wayland/display.h b/src/wayland/display.h index d18d9c09ad..dc9633c7d0 100644 --- a/src/wayland/display.h +++ b/src/wayland/display.h @@ -66,6 +66,7 @@ public: } CompositorInterface *createCompositor(QObject *parent = nullptr); + void createShm(); Q_SIGNALS: void socketNameChanged(const QString&); diff --git a/src/wayland/surface_interface.cpp b/src/wayland/surface_interface.cpp index ea0343c95e..8ce1d2a93f 100644 --- a/src/wayland/surface_interface.cpp +++ b/src/wayland/surface_interface.cpp @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "surface_interface.h" +#include "buffer_interface.h" #include "compositor_interface.h" namespace KWin @@ -87,6 +88,9 @@ void SurfaceInterface::destroy() for (wl_resource *c : m_pending.callbacks) { wl_resource_destroy(c); } + if (m_current.buffer) { + m_current.buffer->unref(); + } if (m_surface) { wl_resource_destroy(m_surface); m_surface = nullptr; @@ -102,6 +106,12 @@ void SurfaceInterface::commit() const bool inputRegionChanged = m_current.input != m_pending.input; const bool scaleFactorChanged = m_current.scale != m_pending.scale; const bool transformFactorChanged = m_current.transform != m_pending.transform; + if (m_current.buffer) { + m_current.buffer->unref(); + } + if (m_pending.buffer) { + m_pending.buffer->ref(); + } // copy values m_current = m_pending; m_pending = State{}; @@ -149,6 +159,15 @@ void SurfaceInterface::addFrameCallback(uint32_t callback) m_pending.callbacks << r; } +void SurfaceInterface::attachBuffer(wl_resource *buffer, const QPoint &offset) +{ + m_pending.offset = offset; + if (m_pending.buffer) { + delete m_pending.buffer; + } + m_pending.buffer = new BufferInterface(buffer, this); +} + void SurfaceInterface::destroyFrameCallback(wl_resource *r) { SurfaceInterface *s = SurfaceInterface::cast(r); @@ -165,11 +184,7 @@ void SurfaceInterface::destroyCallback(wl_client *client, wl_resource *resource) void SurfaceInterface::attachCallback(wl_client *client, wl_resource *resource, wl_resource *buffer, int32_t sx, int32_t sy) { Q_UNUSED(client) - Q_UNUSED(resource) - Q_UNUSED(buffer) - Q_UNUSED(sx) - Q_UNUSED(sy) - // TODO: implement me + SurfaceInterface::cast(resource)->attachBuffer(buffer, QPoint(sx, sy)); } void SurfaceInterface::damageCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) diff --git a/src/wayland/surface_interface.h b/src/wayland/surface_interface.h index 659105e4e7..75e5cdc82c 100644 --- a/src/wayland/surface_interface.h +++ b/src/wayland/surface_interface.h @@ -31,6 +31,7 @@ namespace KWin { namespace WaylandServer { +class BufferInterface; class CompositorInterface; class SurfaceInterface : public QObject @@ -70,6 +71,12 @@ public: OutputInterface::Transform transform() const { return m_current.transform; } + BufferInterface *buffer() { + return m_current.buffer; + } + QPoint offset() const { + return m_current.offset; + } Q_SIGNALS: void damaged(const QRegion&); @@ -86,6 +93,8 @@ private: qint32 scale = 1; OutputInterface::Transform transform = OutputInterface::Transform::Normal; QList callbacks = QList(); + QPoint offset = QPoint(); + BufferInterface *buffer = nullptr; }; friend class CompositorInterface; explicit SurfaceInterface(CompositorInterface *parent); @@ -114,6 +123,7 @@ private: void setScale(qint32 scale); void setTransform(OutputInterface::Transform transform); void addFrameCallback(uint32_t callback); + void attachBuffer(wl_resource *buffer, const QPoint &offset); CompositorInterface *m_compositor; wl_resource *m_surface; wl_client *m_client; 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 4/9] [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 From 9d780deeb26d7584b3476ced3b01fd49f9cc04b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 2 Sep 2014 09:34:31 +0200 Subject: [PATCH 5/9] [kwin_wayland] Add SeatInterface to server module So far the Seat interface is provided together with pointer and keyboard. As always touch is not yet implemented. The pointer interface is still lacking the set cursor callback. Keyboard on the other hand is complete. Both Keyboard and Pointer have the concept of a focused surface and only to the bound interface belonging to the same client as the focused surface events are sent. The change comes with a set of new auto tests also verifying the client side which wasn't possible before as we couldn't fake events. --- src/wayland/display.cpp | 8 + src/wayland/display.h | 2 + src/wayland/seat_interface.cpp | 601 +++++++++++++++++++++++++++++++++ src/wayland/seat_interface.h | 266 +++++++++++++++ 4 files changed, 877 insertions(+) create mode 100644 src/wayland/seat_interface.cpp create mode 100644 src/wayland/seat_interface.h diff --git a/src/wayland/display.cpp b/src/wayland/display.cpp index 7a0bf161e7..46394f417a 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 "seat_interface.h" #include "shell_interface.h" #include @@ -139,6 +140,13 @@ ShellInterface *Display::createShell(QObject *parent) return shell; } +SeatInterface *Display::createSeat(QObject *parent) +{ + SeatInterface *seat = new SeatInterface(this, parent); + connect(this, &Display::aboutToTerminate, seat, [this,seat] { delete seat; }); + return seat; +} + void Display::createShm() { Q_ASSERT(m_running); diff --git a/src/wayland/display.h b/src/wayland/display.h index 961455ef0c..7298288679 100644 --- a/src/wayland/display.h +++ b/src/wayland/display.h @@ -33,6 +33,7 @@ namespace WaylandServer class CompositorInterface; class OutputInterface; +class SeatInterface; class ShellInterface; class Display : public QObject @@ -72,6 +73,7 @@ public: CompositorInterface *createCompositor(QObject *parent = nullptr); void createShm(); ShellInterface *createShell(QObject *parent = nullptr); + SeatInterface *createSeat(QObject *parent = nullptr); Q_SIGNALS: void socketNameChanged(const QString&); diff --git a/src/wayland/seat_interface.cpp b/src/wayland/seat_interface.cpp new file mode 100644 index 0000000000..fef594f4c0 --- /dev/null +++ b/src/wayland/seat_interface.cpp @@ -0,0 +1,601 @@ +/******************************************************************** + 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 "seat_interface.h" +#include "display.h" +#include "surface_interface.h" + +#include +#include + +namespace KWin +{ + +namespace WaylandServer +{ + +static const quint32 s_version = 3; + +const struct wl_seat_interface SeatInterface::s_interface = { + SeatInterface::getPointerCallback, + SeatInterface::getKeyboardCallback, + SeatInterface::getTouchCallback +}; + +SeatInterface::SeatInterface(Display *display, QObject *parent) + : QObject(parent) + , m_display(display) + , m_seat(nullptr) + , m_name() + , m_pointer(false) + , m_keyboard(false) + , m_touch(false) + , m_pointerInterface(new PointerInterface(display, this)) + , m_keyboardInterface(new KeyboardInterface(display, this)) +{ + connect(this, &SeatInterface::nameChanged, this, + [this] { + for (auto it = m_resources.constBegin(); it != m_resources.constEnd(); ++it) { + sendName(*it); + } + } + ); + auto sendCapabilitiesAll = [this] { + for (auto it = m_resources.constBegin(); it != m_resources.constEnd(); ++it) { + sendCapabilities(*it); + } + }; + connect(this, &SeatInterface::hasPointerChanged, this, sendCapabilitiesAll); + connect(this, &SeatInterface::hasKeyboardChanged, this, sendCapabilitiesAll); + connect(this, &SeatInterface::hasTouchChanged, this, sendCapabilitiesAll); +} + +SeatInterface::~SeatInterface() +{ + destroy(); +} + +void SeatInterface::create() +{ + Q_ASSERT(!m_seat); + m_seat = wl_global_create(*m_display, &wl_seat_interface, s_version, this, &SeatInterface::bind); +} + +void SeatInterface::destroy() +{ + while (!m_resources.isEmpty()) { + wl_resource_destroy(m_resources.takeLast()); + } + if (m_seat) { + wl_global_destroy(m_seat); + m_seat = nullptr; + } +} + +void SeatInterface::bind(wl_client *client, void *data, uint32_t version, uint32_t id) +{ + reinterpret_cast(data)->bind(client, version, id); +} + +void SeatInterface::bind(wl_client *client, uint32_t version, uint32_t id) +{ + wl_resource *r = wl_resource_create(client, &wl_seat_interface, qMin(s_version, version), id); + if (!r) { + wl_client_post_no_memory(client); + return; + } + m_resources << r; + + wl_resource_set_implementation(r, &SeatInterface::s_interface, this, SeatInterface::unbind); + + sendCapabilities(r); + sendName(r); +} + +void SeatInterface::unbind(wl_resource *r) +{ + SeatInterface::cast(r)->m_resources.removeAll(r); +} + +void SeatInterface::sendName(wl_resource *r) +{ + if (wl_resource_get_version(r) < WL_SEAT_NAME_SINCE_VERSION) { + return; + } + wl_seat_send_name(r, m_name.toUtf8().constData()); +} + +void SeatInterface::sendCapabilities(wl_resource *r) +{ + uint32_t capabilities = 0; + if (m_pointer) { + capabilities |= WL_SEAT_CAPABILITY_POINTER; + } + if (m_keyboard) { + capabilities |= WL_SEAT_CAPABILITY_KEYBOARD; + } + if (m_touch) { + capabilities |= WL_SEAT_CAPABILITY_TOUCH; + } + wl_seat_send_capabilities(r, capabilities); +} + +SeatInterface *SeatInterface::cast(wl_resource *r) +{ + return reinterpret_cast(wl_resource_get_user_data(r)); +} + +void SeatInterface::setHasKeyboard(bool has) +{ + if (m_keyboard == has) { + return; + } + m_keyboard = has; + emit hasKeyboardChanged(m_keyboard); +} + +void SeatInterface::setHasPointer(bool has) +{ + if (m_pointer == has) { + return; + } + m_pointer = has; + emit hasPointerChanged(m_pointer); +} + +void SeatInterface::setHasTouch(bool has) +{ + if (m_touch == has) { + return; + } + m_touch = has; + emit hasTouchChanged(m_touch); +} + +void SeatInterface::setName(const QString &name) +{ + if (m_name == name) { + return; + } + m_name = name; + emit nameChanged(m_name); +} + +void SeatInterface::getPointerCallback(wl_client *client, wl_resource *resource, uint32_t id) +{ + SeatInterface::cast(resource)->m_pointerInterface->createInterface(client, resource, id); +} + +void SeatInterface::getKeyboardCallback(wl_client *client, wl_resource *resource, uint32_t id) +{ + SeatInterface::cast(resource)->m_keyboardInterface->createInterfae(client, resource, id); +} + +void SeatInterface::getTouchCallback(wl_client *client, wl_resource *resource, uint32_t id) +{ + Q_UNUSED(client) + Q_UNUSED(resource) + Q_UNUSED(id) +} + +/**************************************** + * PointerInterface + ***************************************/ + +const struct wl_pointer_interface PointerInterface::s_interface = { + PointerInterface::setCursorCallback, + PointerInterface::releaseCallback +}; + +PointerInterface::PointerInterface(Display *display, SeatInterface *parent) + : QObject(parent) + , m_display(display) + , m_seat(parent) + , m_eventTime(0) +{ +} + +PointerInterface::~PointerInterface() +{ + while (!m_resources.isEmpty()) { + ResourceData data = m_resources.takeLast(); + wl_resource_destroy(data.pointer); + } +} + +void PointerInterface::createInterface(wl_client *client, wl_resource *parentResource, uint32_t id) +{ + wl_resource *p = wl_resource_create(client, &wl_pointer_interface, wl_resource_get_version(parentResource), id); + if (!p) { + wl_resource_post_no_memory(parentResource); + return; + } + ResourceData data; + data.client = client; + data.pointer = p; + m_resources << data; + + wl_resource_set_implementation(p, &PointerInterface::s_interface, this, PointerInterface::unbind); +} + +wl_resource *PointerInterface::pointerForSurface(SurfaceInterface *surface) const +{ + if (!surface) { + return nullptr; + } + for (auto it = m_resources.constBegin(); it != m_resources.constEnd(); ++it) { + if ((*it).client == surface->client()) { + return (*it).pointer; + } + } + return nullptr; +} + +void PointerInterface::setFocusedSurface(SurfaceInterface *surface, const QPoint &surfacePosition) +{ + const quint32 serial = m_display->nextSerial(); + if (m_focusedSurface.surface && m_focusedSurface.pointer) { + wl_pointer_send_leave(m_focusedSurface.pointer, serial, m_focusedSurface.surface->surface()); + disconnect(m_focusedSurface.surface, &QObject::destroyed, this, &PointerInterface::surfaceDeleted); + } + m_focusedSurface.pointer = pointerForSurface(surface); + if (!m_focusedSurface.pointer) { + m_focusedSurface = FocusedSurface(); + return; + } + m_focusedSurface.surface = surface; + m_focusedSurface.offset = surfacePosition; + m_focusedSurface.serial = serial; + connect(m_focusedSurface.surface, &QObject::destroyed, this, &PointerInterface::surfaceDeleted); + + const QPoint pos = m_globalPos - surfacePosition; + wl_pointer_send_enter(m_focusedSurface.pointer, m_focusedSurface.serial, + m_focusedSurface.surface->surface(), + wl_fixed_from_int(pos.x()), wl_fixed_from_int(pos.y())); +} + +void PointerInterface::surfaceDeleted() +{ + m_focusedSurface = FocusedSurface(); +} + +void PointerInterface::setFocusedSurfacePosition(const QPoint &surfacePosition) +{ + if (!m_focusedSurface.surface) { + return; + } + m_focusedSurface.offset = surfacePosition; +} + +void PointerInterface::setGlobalPos(const QPoint &pos) +{ + if (m_globalPos == pos) { + return; + } + m_globalPos = pos; + if (m_focusedSurface.surface && m_focusedSurface.pointer) { + const QPoint pos = m_globalPos - m_focusedSurface.offset; + wl_pointer_send_motion(m_focusedSurface.pointer, m_eventTime, + wl_fixed_from_int(pos.x()), wl_fixed_from_int(pos.y())); + } + emit globalPosChanged(m_globalPos); +} + +void PointerInterface::updateTimestamp(quint32 time) +{ + m_eventTime = time; +} + +void PointerInterface::buttonPressed(quint32 button) +{ + const quint32 serial = m_display->nextSerial(); + updateButtonSerial(button, serial); + updateButtonState(button, ButtonState::Pressed); + if (!m_focusedSurface.surface || !m_focusedSurface.pointer) { + return; + } + wl_pointer_send_button(m_focusedSurface.pointer, serial, m_eventTime, button, WL_POINTER_BUTTON_STATE_PRESSED); +} + +void PointerInterface::buttonReleased(quint32 button) +{ + const quint32 serial = m_display->nextSerial(); + updateButtonSerial(button, serial); + updateButtonState(button, ButtonState::Released); + if (!m_focusedSurface.surface || !m_focusedSurface.pointer) { + return; + } + wl_pointer_send_button(m_focusedSurface.pointer, serial, m_eventTime, button, WL_POINTER_BUTTON_STATE_RELEASED); +} + +void PointerInterface::updateButtonSerial(quint32 button, quint32 serial) +{ + auto it = m_buttonSerials.find(button); + if (it == m_buttonSerials.end()) { + m_buttonSerials.insert(button, serial); + return; + } + it.value() = serial; +} + +quint32 PointerInterface::buttonSerial(quint32 button) const +{ + auto it = m_buttonSerials.constFind(button); + if (it == m_buttonSerials.constEnd()) { + return 0; + } + return it.value(); +} + +void PointerInterface::updateButtonState(quint32 button, PointerInterface::ButtonState state) +{ + auto it = m_buttonStates.find(button); + if (it == m_buttonStates.end()) { + m_buttonStates.insert(button, state); + return; + } + it.value() = state; +} + +bool PointerInterface::isButtonPressed(quint32 button) const +{ + auto it = m_buttonStates.constFind(button); + if (it == m_buttonStates.constEnd()) { + return false; + } + return it.value() == ButtonState::Pressed ? true : false; +} + +void PointerInterface::axis(Qt::Orientation orientation, quint32 delta) +{ + if (!m_focusedSurface.surface || !m_focusedSurface.pointer) { + return; + } + wl_pointer_send_axis(m_focusedSurface.pointer, m_eventTime, + (orientation == Qt::Vertical) ? WL_POINTER_AXIS_VERTICAL_SCROLL : WL_POINTER_AXIS_HORIZONTAL_SCROLL, + wl_fixed_from_int(delta)); +} + +void PointerInterface::unbind(wl_resource *resource) +{ + PointerInterface *p = PointerInterface::cast(resource); + auto it = std::find_if(p->m_resources.begin(), p->m_resources.end(), + [resource](const ResourceData &data) { + return data.pointer == resource; + } + ); + if (it == p->m_resources.end()) { + return; + } + if ((*it).pointer == p->m_focusedSurface.pointer) { + disconnect(p->m_focusedSurface.surface, &QObject::destroyed, p, &PointerInterface::surfaceDeleted); + p->m_focusedSurface = FocusedSurface(); + } + p->m_resources.erase(it); +} + +void PointerInterface::setCursorCallback(wl_client *client, wl_resource *resource, uint32_t serial, + wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y) +{ + Q_UNUSED(client) + Q_UNUSED(resource) + Q_UNUSED(serial) + Q_UNUSED(surface) + Q_UNUSED(hotspot_x) + Q_UNUSED(hotspot_y) + // TODO: implement +} + +void PointerInterface::releaseCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + unbind(resource); +} + +/**************************************** + * KeyboardInterface + ***************************************/ + +const struct wl_keyboard_interface KeyboardInterface::s_interface { + KeyboardInterface::releaseCallback +}; + +KeyboardInterface::KeyboardInterface(Display *display, SeatInterface *parent) + : QObject(parent) + , m_display(display) + , m_seat(parent) + , m_eventTime(0) +{ +} + +KeyboardInterface::~KeyboardInterface() +{ + while (!m_resources.isEmpty()) { + ResourceData data = m_resources.takeLast(); + wl_resource_destroy(data.keyboard); + } +} + +void KeyboardInterface::createInterfae(wl_client *client, wl_resource *parentResource, uint32_t id) +{ + wl_resource *k = wl_resource_create(client, &wl_keyboard_interface, wl_resource_get_version(parentResource), id); + if (!k) { + wl_resource_post_no_memory(parentResource); + return; + } + ResourceData data; + data.client = client; + data.keyboard = k; + m_resources << data; + + wl_resource_set_implementation(k, &KeyboardInterface::s_interface, this, KeyboardInterface::unbind); + + sendKeymap(k); +} + +void KeyboardInterface::unbind(wl_resource *resource) +{ + KeyboardInterface *k = KeyboardInterface::cast(resource); + auto it = std::find_if(k->m_resources.begin(), k->m_resources.end(), + [resource](const ResourceData &data) { + return data.keyboard == resource; + } + ); + if (it == k->m_resources.end()) { + return; + } + if ((*it).keyboard == k->m_focusedSurface.keyboard) { + disconnect(k->m_focusedSurface.surface, &QObject::destroyed, k, &KeyboardInterface::surfaceDeleted); + k->m_focusedSurface = FocusedSurface(); + } + k->m_resources.erase(it); +} + +void KeyboardInterface::surfaceDeleted() +{ + m_focusedSurface = FocusedSurface(); +} + +void KeyboardInterface::releaseCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + unbind(resource); +} + +void KeyboardInterface::setKeymap(int fd, quint32 size) +{ + m_keymap.xkbcommonCompatible = true; + m_keymap.fd = fd; + m_keymap.size = size; + sendKeymapToAll(); +} + +void KeyboardInterface::sendKeymap(wl_resource *r) +{ + if (m_keymap.xkbcommonCompatible) { + wl_keyboard_send_keymap(r, + WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, + m_keymap.fd, + m_keymap.size); + } else { + int nullFd = open("/dev/null", O_RDONLY); + wl_keyboard_send_keymap(r, WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP, nullFd, 0); + close(nullFd); + } +} + +void KeyboardInterface::sendKeymapToAll() +{ + for (auto it = m_resources.constBegin(); it != m_resources.constEnd(); ++it) { + sendKeymap((*it).keyboard); + } +} + +void KeyboardInterface::sendModifiers(wl_resource* r) +{ + wl_keyboard_send_modifiers(r, m_display->nextSerial(), m_modifiers.depressed, m_modifiers.latched, m_modifiers.locked, m_modifiers.group); +} + +void KeyboardInterface::setFocusedSurface(SurfaceInterface *surface) +{ + const quint32 serial = m_display->nextSerial(); + if (m_focusedSurface.surface && m_focusedSurface.keyboard) { + wl_keyboard_send_leave(m_focusedSurface.keyboard, serial, m_focusedSurface.surface->surface()); + disconnect(m_focusedSurface.surface, &QObject::destroyed, this, &KeyboardInterface::surfaceDeleted); + } + m_focusedSurface.keyboard = keyboardForSurface(surface); + if (!m_focusedSurface.keyboard) { + m_focusedSurface = FocusedSurface(); + return; + } + m_focusedSurface.surface = surface; + connect(m_focusedSurface.surface, &QObject::destroyed, this, &KeyboardInterface::surfaceDeleted); + + wl_array keys; + wl_array_init(&keys); + for (auto it = m_keyStates.constBegin(); it != m_keyStates.constEnd(); ++it) { + if (it.value() == KeyState::Pressed) { + continue; + } + uint32_t *k = reinterpret_cast(wl_array_add(&keys, sizeof(uint32_t))); + *k = it.key(); + } + wl_keyboard_send_enter(m_focusedSurface.keyboard, serial, m_focusedSurface.surface->surface(), &keys); + wl_array_release(&keys); + + sendModifiers(m_focusedSurface.keyboard); +} + +wl_resource *KeyboardInterface::keyboardForSurface(SurfaceInterface *surface) const +{ + if (!surface) { + return nullptr; + } + for (auto it = m_resources.constBegin(); it != m_resources.constEnd(); ++it) { + if ((*it).client == surface->client()) { + return (*it).keyboard; + } + } + return nullptr; +} + +void KeyboardInterface::keyPressed(quint32 key) +{ + updateKey(key, KeyState::Pressed); + if (m_focusedSurface.surface && m_focusedSurface.keyboard) { + wl_keyboard_send_key(m_focusedSurface.keyboard, m_display->nextSerial(), m_eventTime, key, WL_KEYBOARD_KEY_STATE_PRESSED); + } +} + +void KeyboardInterface::keyReleased(quint32 key) +{ + updateKey(key, KeyState::Released); + if (m_focusedSurface.surface && m_focusedSurface.keyboard) { + wl_keyboard_send_key(m_focusedSurface.keyboard, m_display->nextSerial(), m_eventTime, key, WL_KEYBOARD_KEY_STATE_RELEASED); + } +} + +void KeyboardInterface::updateKey(quint32 key, KeyboardInterface::KeyState state) +{ + auto it = m_keyStates.find(key); + if (it == m_keyStates.end()) { + m_keyStates.insert(key, state); + return; + } + it.value() = state; +} + +void KeyboardInterface::updateTimestamp(quint32 time) +{ + m_eventTime = time; +} + +void KeyboardInterface::updateModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group) +{ + m_modifiers.depressed = depressed; + m_modifiers.latched = latched; + m_modifiers.locked = locked; + m_modifiers.group = group; + if (m_focusedSurface.surface && m_focusedSurface.keyboard) { + sendModifiers(m_focusedSurface.keyboard); + } +} + +} +} diff --git a/src/wayland/seat_interface.h b/src/wayland/seat_interface.h new file mode 100644 index 0000000000..256163336a --- /dev/null +++ b/src/wayland/seat_interface.h @@ -0,0 +1,266 @@ +/******************************************************************** + 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_SEAT_INTERFACE_H +#define KWIN_WAYLAND_SERVER_SEAT_INTERFACE_H + +#include +#include +#include + +#include + +namespace KWin +{ +namespace WaylandServer +{ + +class Display; +class KeyboardInterface; +class PointerInterface; +class SurfaceInterface; + +class SeatInterface : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(bool pointer READ hasPointer WRITE setHasPointer NOTIFY hasPointerChanged) + Q_PROPERTY(bool keyboard READ hasKeyboard WRITE setHasKeyboard NOTIFY hasKeyboardChanged) + Q_PROPERTY(bool tourch READ hasTouch WRITE setHasTouch NOTIFY hasTouchChanged) +public: + virtual ~SeatInterface(); + + void create(); + void destroy(); + bool isValid() const { + return m_seat != nullptr; + } + + const QString &name() const { + return m_name; + } + bool hasPointer() const { + return m_pointer; + } + bool hasKeyboard() const { + return m_keyboard; + } + bool hasTouch() const { + return m_touch; + } + PointerInterface *pointer() { + return m_pointerInterface; + } + KeyboardInterface *keyboard() { + return m_keyboardInterface; + } + + void setName(const QString &name); + void setHasPointer(bool has); + void setHasKeyboard(bool has); + void setHasTouch(bool has); + +Q_SIGNALS: + void nameChanged(const QString&); + void hasPointerChanged(bool); + void hasKeyboardChanged(bool); + void hasTouchChanged(bool); + +private: + friend class Display; + explicit SeatInterface(Display *display, QObject *parent); + + static void bind(wl_client *client, void *data, uint32_t version, uint32_t id); + static void unbind(wl_resource *r); + + // interface + static void getPointerCallback(wl_client *client, wl_resource *resource, uint32_t id); + static void getKeyboardCallback(wl_client *client, wl_resource *resource, uint32_t id); + static void getTouchCallback(wl_client *client, wl_resource *resource, uint32_t id); + + static SeatInterface *cast(wl_resource *r); + void bind(wl_client *client, uint32_t version, uint32_t id); + void sendCapabilities(wl_resource *r); + void sendName(wl_resource *r); + + Display *m_display; + wl_global *m_seat; + QString m_name; + bool m_pointer; + bool m_keyboard; + bool m_touch; + QList m_resources; + PointerInterface *m_pointerInterface; + KeyboardInterface *m_keyboardInterface; + static const struct wl_seat_interface s_interface; +}; + +class PointerInterface : public QObject +{ + Q_OBJECT + Q_PROPERTY(QPoint globalPos READ globalPos WRITE setGlobalPos NOTIFY globalPosChanged) +public: + virtual ~PointerInterface(); + + void createInterface(wl_client *client, wl_resource *parentResource, uint32_t id); + + void updateTimestamp(quint32 time); + void setGlobalPos(const QPoint &pos); + const QPoint &globalPos() const { + return m_globalPos; + } + void buttonPressed(quint32 button); + void buttonReleased(quint32 button); + bool isButtonPressed(quint32 button) const; + quint32 buttonSerial(quint32 button) const; + void axis(Qt::Orientation orientation, quint32 delta); + + void setFocusedSurface(SurfaceInterface *surface, const QPoint &surfacePosition = QPoint()); + void setFocusedSurfacePosition(const QPoint &surfacePosition); + SurfaceInterface *focusedSurface() const { + return m_focusedSurface.surface; + } + const QPoint &focusedSurfacePosition() const { + return m_focusedSurface.offset; + } + +Q_SIGNALS: + void globalPosChanged(const QPoint &pos); + +private: + friend class SeatInterface; + explicit PointerInterface(Display *display, SeatInterface *parent); + wl_resource *pointerForSurface(SurfaceInterface *surface) const; + void surfaceDeleted(); + void updateButtonSerial(quint32 button, quint32 serial); + enum class ButtonState { + Released, + Pressed + }; + void updateButtonState(quint32 button, ButtonState state); + + static PointerInterface *cast(wl_resource *resource) { + return reinterpret_cast(wl_resource_get_user_data(resource)); + } + + static void unbind(wl_resource *resource); + // interface + static void setCursorCallback(wl_client *client, wl_resource *resource, uint32_t serial, + wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y); + // since version 3 + static void releaseCallback(wl_client *client, wl_resource *resource); + + Display *m_display; + SeatInterface *m_seat; + struct ResourceData { + wl_client *client = nullptr; + wl_resource *pointer = nullptr; + }; + QList m_resources; + quint32 m_eventTime; + QPoint m_globalPos; + struct FocusedSurface { + SurfaceInterface *surface = nullptr; + QPoint offset = QPoint(); + wl_resource *pointer = nullptr; + quint32 serial = 0; + }; + FocusedSurface m_focusedSurface; + QHash m_buttonSerials; + QHash m_buttonStates; + + static const struct wl_pointer_interface s_interface; +}; + +class KeyboardInterface : public QObject +{ + Q_OBJECT +public: + virtual ~KeyboardInterface(); + + void createInterfae(wl_client *client, wl_resource *parentResource, uint32_t id); + + void updateTimestamp(quint32 time); + void setKeymap(int fd, quint32 size); + void keyPressed(quint32 key); + void keyReleased(quint32 key); + void updateModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group); + + void setFocusedSurface(SurfaceInterface *surface); + SurfaceInterface *focusedSurface() const { + return m_focusedSurface.surface; + } + +private: + friend class SeatInterface; + explicit KeyboardInterface(Display *display, SeatInterface *parent); + void surfaceDeleted(); + wl_resource *keyboardForSurface(SurfaceInterface *surface) const; + void sendKeymap(wl_resource *r); + void sendKeymapToAll(); + void sendModifiers(wl_resource *r); + enum class KeyState { + Released, + Pressed + }; + void updateKey(quint32 key, KeyState state); + + static KeyboardInterface *cast(wl_resource *resource) { + return reinterpret_cast(wl_resource_get_user_data(resource)); + } + + static void unbind(wl_resource *resource); + // since version 3 + static void releaseCallback(wl_client *client, wl_resource *resource); + + Display *m_display; + SeatInterface *m_seat; + struct ResourceData { + wl_client *client = nullptr; + wl_resource *keyboard = nullptr; + }; + QList m_resources; + struct Keymap { + int fd = -1; + quint32 size = 0; + bool xkbcommonCompatible = false; + }; + Keymap m_keymap; + struct Modifiers { + quint32 depressed = 0; + quint32 latched = 0; + quint32 locked = 0; + quint32 group = 0; + }; + Modifiers m_modifiers; + struct FocusedSurface { + SurfaceInterface *surface = nullptr; + wl_resource *keyboard = nullptr; + }; + FocusedSurface m_focusedSurface; + QHash m_keyStates; + quint32 m_eventTime; + + static const struct wl_keyboard_interface s_interface; +}; + +} +} + +#endif From b51fb1f8b953dbb3aec2c784aa22f322858e58b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Tue, 2 Sep 2014 11:11:26 +0200 Subject: [PATCH 6/9] [kwin_wayland] define WL_SEAT_NAME_SINCE_VERSION if not defined Apparently not yet in wayland server library on build.kde.org. --- src/wayland/seat_interface.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/wayland/seat_interface.cpp b/src/wayland/seat_interface.cpp index fef594f4c0..a8a880b072 100644 --- a/src/wayland/seat_interface.cpp +++ b/src/wayland/seat_interface.cpp @@ -24,6 +24,10 @@ along with this program. If not, see . #include #include +#ifndef WL_SEAT_NAME_SINCE_VERSION +#define WL_SEAT_NAME_SINCE_VERSION 2 +#endif + namespace KWin { From 2ccaa4342a60b37d7cf530834bacdb966c719547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 3 Sep 2014 20:03:13 +0200 Subject: [PATCH 7/9] [kwin_wayland] Fix Display::outputs Should return a const-ref not a const copy. --- src/wayland/display.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wayland/display.h b/src/wayland/display.h index 7298288679..28ac0bcbea 100644 --- a/src/wayland/display.h +++ b/src/wayland/display.h @@ -66,7 +66,7 @@ public: OutputInterface *createOutput(QObject *parent = nullptr); void removeOutput(OutputInterface *output); - const QList outputs() const { + const QList &outputs() const { return m_outputs; } From 7003d80173c14747aac9eda6c5e270d47578460f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 3 Sep 2014 20:04:31 +0200 Subject: [PATCH 8/9] [kwin_wayland] Add assert in Display::setRunning Making the impossible condition an assert instead of a return. --- src/wayland/display.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/wayland/display.cpp b/src/wayland/display.cpp index 46394f417a..3fef02c0c1 100644 --- a/src/wayland/display.cpp +++ b/src/wayland/display.cpp @@ -110,9 +110,7 @@ void Display::terminate() void Display::setRunning(bool running) { - if (m_running == running) { - return; - } + Q_ASSERT(m_running != running); m_running = running; emit runningChanged(m_running); } From 37c5318375e8dcf669ef3d24c4d34c4446444831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Wed, 17 Sep 2014 13:20:55 +0200 Subject: [PATCH 9/9] Move all files to src/server --- src/wayland/{ => server}/buffer_interface.cpp | 0 src/wayland/{ => server}/buffer_interface.h | 0 src/wayland/{ => server}/shell_interface.cpp | 0 src/wayland/{ => server}/shell_interface.h | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename src/wayland/{ => server}/buffer_interface.cpp (100%) rename src/wayland/{ => server}/buffer_interface.h (100%) rename src/wayland/{ => server}/shell_interface.cpp (100%) rename src/wayland/{ => server}/shell_interface.h (100%) diff --git a/src/wayland/buffer_interface.cpp b/src/wayland/server/buffer_interface.cpp similarity index 100% rename from src/wayland/buffer_interface.cpp rename to src/wayland/server/buffer_interface.cpp diff --git a/src/wayland/buffer_interface.h b/src/wayland/server/buffer_interface.h similarity index 100% rename from src/wayland/buffer_interface.h rename to src/wayland/server/buffer_interface.h diff --git a/src/wayland/shell_interface.cpp b/src/wayland/server/shell_interface.cpp similarity index 100% rename from src/wayland/shell_interface.cpp rename to src/wayland/server/shell_interface.cpp diff --git a/src/wayland/shell_interface.h b/src/wayland/server/shell_interface.h similarity index 100% rename from src/wayland/shell_interface.h rename to src/wayland/server/shell_interface.h