diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -22,6 +22,7 @@ fullscreen_shell.cpp idle.cpp keyboard.cpp + remote_access.cpp outputconfiguration.cpp outputmanagement.cpp outputdevice.cpp @@ -117,6 +118,10 @@ PROTOCOL ${KWAYLAND_SOURCE_DIR}/src/client/protocols/text-input-unstable-v2.xml BASENAME text-input-v2 ) +ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS + PROTOCOL ${KWAYLAND_SOURCE_DIR}/src/client/protocols/remote-access.xml + BASENAME remote-access +) add_library(KF5WaylandClient ${CLIENT_LIB_SRCS}) generate_export_header(KF5WaylandClient @@ -164,6 +169,7 @@ fullscreen_shell.h idle.h keyboard.h + remote_access.h outputconfiguration.h outputmanagement.h outputdevice.h diff --git a/src/client/protocols/remote-access.xml b/src/client/protocols/remote-access.xml new file mode 100644 --- /dev/null +++ b/src/client/protocols/remote-access.xml @@ -0,0 +1,48 @@ + + + . + ]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/client/registry.h b/src/client/registry.h --- a/src/client/registry.h +++ b/src/client/registry.h @@ -41,6 +41,7 @@ struct org_kde_kwin_outputdevice; struct org_kde_kwin_fake_input; struct org_kde_kwin_idle; +struct org_kde_kwin_remote_access_manager; struct org_kde_kwin_dpms_manager; struct org_kde_kwin_shadow_manager; struct org_kde_kwin_blur_manager; @@ -65,6 +66,7 @@ class OutputManagement; class OutputDevice; class Idle; +class RemoteAccessManager; class Output; class PlasmaShell; class PlasmaWindowManagement; @@ -138,7 +140,8 @@ OutputDevice, ///< Refers to the org_kde_kwin_outputdevice interface ServerSideDecorationManager, ///< Refers to org_kde_kwin_server_decoration_manager TextInputManagerUnstableV0, ///< Refers to wl_text_input_manager, @since 5.23 - TextInputManagerUnstableV2 ///< Refers to zwp_text_input_manager_v2, @since 5.23 + TextInputManagerUnstableV2, ///< Refers to zwp_text_input_manager_v2, @since 5.23 + RemoteAccessManager ///< Refers to org_kde_kwin_remote_access_manager interface, @since 5.23 }; explicit Registry(QObject *parent = nullptr); virtual ~Registry(); @@ -374,6 +377,16 @@ **/ org_kde_kwin_idle *bindIdle(uint32_t name, uint32_t version) const; /** + * Binds the org_kde_kwin_remote_access_manager with @p name and @p version. + * If the @p name does not exist or is not for the idle interface, + * @c null will be returned. + * + * Prefer using createRemoteAccessManager instead. + * @see createRemoteAccessManager + * @since 5.23 + **/ + org_kde_kwin_remote_access_manager *bindRemoteAccessManager(uint32_t name, uint32_t version) const; + /** * Binds the org_kde_kwin_fake_input with @p name and @p version. * If the @p name does not exist or is not for the fake input interface, * @c null will be returned. @@ -671,6 +684,22 @@ **/ Idle *createIdle(quint32 name, quint32 version, QObject *parent = nullptr); /** + * Creates a RemoteAccessManager and sets it up to manage the interface identified by + * @p name and @p version. + * + * Note: in case @p name is invalid or isn't for the org_kde_kwin_remote_access_manager interface, + * the returned RemoteAccessManager will not be valid. Therefore it's recommended to call + * isValid on the created instance. + * + * @param name The name of the org_kde_kwin_remote_access_manager interface to bind + * @param version The version or the org_kde_kwin_remote_access_manager interface to use + * @param parent The parent for RemoteAccessManager + * + * @returns The created RemoteAccessManager. + * @since 5.23 + **/ + RemoteAccessManager *createRemoteAccessManager(quint32 name, quint32 version, QObject *parent = nullptr); + /** * Creates a FakeInput and sets it up to manage the interface identified by * @p name and @p version. * @@ -900,6 +929,13 @@ **/ void idleAnnounced(quint32 name, quint32 version); /** + * Emitted whenever a org_kde_kwin_remote_access_manager interface gets announced. + * @param name The name for the announced interface + * @param version The maximum supported version of the announced interface + * @since 5.23 + **/ + void remoteAccessManagerAnnounced(quint32 name, quint32 version); + /** * Emitted whenever a org_kde_kwin_fake_input interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface @@ -1038,6 +1074,12 @@ **/ void idleRemoved(quint32 name); /** + * Emitted whenever a org_kde_kwin_remote_access_manager interface gets removed. + * @param name The name for the removed interface + * @since 5.23 + **/ + void remoteAccessManagerRemoved(quint32 name); + /** * Emitted whenever a org_kde_kwin_fake_input interface gets removed. * @param name The name for the removed interface * @since 5.4 diff --git a/src/client/registry.cpp b/src/client/registry.cpp --- a/src/client/registry.cpp +++ b/src/client/registry.cpp @@ -26,6 +26,7 @@ #include "fakeinput.h" #include "fullscreen_shell.h" #include "idle.h" +#include "remote_access.h" #include "logging_p.h" #include "outputconfiguration.h" #include "outputmanagement.h" @@ -52,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -161,6 +163,13 @@ &Registry::idleAnnounced, &Registry::idleRemoved }}, + {Registry::Interface::RemoteAccessManager, { + 1, + QByteArrayLiteral("org_kde_kwin_remote_access_manager"), + &org_kde_kwin_remote_access_manager_interface, + &Registry::remoteAccessManagerAnnounced, + &Registry::remoteAccessManagerRemoved + }}, {Registry::Interface::FakeInput, { 2, QByteArrayLiteral("org_kde_kwin_fake_input"), @@ -532,6 +541,7 @@ BIND(PlasmaShell, org_kde_plasma_shell) BIND(PlasmaWindowManagement, org_kde_plasma_window_management) BIND(Idle, org_kde_kwin_idle) +BIND(RemoteAccessManager, org_kde_kwin_remote_access_manager) BIND(FakeInput, org_kde_kwin_fake_input) BIND(OutputManagement, org_kde_kwin_outputmanagement) BIND(OutputDevice, org_kde_kwin_outputdevice) @@ -581,6 +591,7 @@ CREATE(PlasmaShell) CREATE(PlasmaWindowManagement) CREATE(Idle) +CREATE(RemoteAccessManager) CREATE(FakeInput) CREATE(OutputManagement) CREATE(OutputDevice) diff --git a/src/client/remote_access.h b/src/client/remote_access.h new file mode 100644 --- /dev/null +++ b/src/client/remote_access.h @@ -0,0 +1,207 @@ +/**************************************************************************** +Copyright 2016 Oleg Chernovskiy + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#ifndef KWAYLAND_CLIENT_REMOTE_ACCESS_H +#define KWAYLAND_CLIENT_REMOTE_ACCESS_H + +#include + +#include + +struct org_kde_kwin_remote_access_manager; +struct org_kde_kwin_remote_buffer; + +namespace KWayland +{ +namespace Client +{ + +class EventQueue; +class RemoteBuffer; + +/** + * @short Wrapper for the org_kde_kwin_remote_access_manager interface. + * + * This class provides a convenient wrapper for the org_kde_kwin_remote_access_manager interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the RemoteAccessManager interface: + * @code + * RemoteAccessManager *c = registry->createRemoteAccessManager(name, version); + * @endcode + * + * This creates the RemoteAccessManager and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * RemoteAccessManager *c = new RemoteAccessManager; + * c->setup(registry->bindRemoteAccessManager(name, version)); + * @endcode + * + * The RemoteAccessManager can be used as a drop-in replacement for any org_kde_kwin_remote_access_manager + * pointer as it provides matching cast operators. + * + * @see Registry + **/ +class KWAYLANDCLIENT_EXPORT RemoteAccessManager : public QObject +{ + Q_OBJECT +public: + /** + * Creates a new RemoteAccessManager. + * Note: after constructing the RemoteAccessManager it is not yet valid and one needs + * to call setup. In order to get a ready to use RemoteAccessManager prefer using + * Registry::createRemoteAccessManager. + **/ + explicit RemoteAccessManager(QObject *parent = nullptr); + virtual ~RemoteAccessManager(); + + /** + * Setup this RemoteAccessManager to manage the @p remoteaccessmanager. + * When using Registry::createRemoteAccessManager there is no need to call this + * method. + **/ + void setup(org_kde_kwin_remote_access_manager *remoteaccessmanager); + /** + * @returns @c true if managing a org_kde_kwin_remote_access_manager. + **/ + bool isValid() const; + /** + * Releases the org_kde_kwin_remote_access_manager interface. + * After the interface has been released the RemoteAccessManager instance is no + * longer valid and can be setup with another org_kde_kwin_remote_access_manager interface. + **/ + void release(); + /** + * Destroys the data held by this RemoteAccessManager. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new org_kde_kwin_remote_access_manager interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, remoteaccessmanager, &RemoteAccessManager::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Sets the @p queue to use for creating objects with this RemoteAccessManager. + **/ + void setEventQueue(EventQueue *queue); + /** + * @returns The event queue to use for creating objects with this RemoteAccessManager. + **/ + EventQueue *eventQueue(); + + operator org_kde_kwin_remote_access_manager*(); + operator org_kde_kwin_remote_access_manager*() const; + +Q_SIGNALS: + /** + * The corresponding global for this interface on the Registry got removed. + * + * This signal gets only emitted if the RemoteAccessManager got created by + * Registry::createRemoteAccessManager + **/ + void removed(); + + /** + * Buffer from server is ready to be delivered to this client + * @param buffer_id internal buffer id to be created + **/ + void bufferReady(RemoteBuffer *rbuf); + +private: + class Private; + QScopedPointer d; +}; + +/** + * @short Wrapper for org_kde_kwin_remote_buffer interface. + * The instances of this class are created by parent RemoteAccessManager. + * Deletion (by noLongerNeeded call) is in responsibility of underlying system. + */ +class KWAYLANDCLIENT_EXPORT RemoteBuffer : public QObject +{ + Q_OBJECT +public: + virtual ~RemoteBuffer(); + /** + * Setup this RemoteBuffer to manage the @p remotebuffer. + **/ + void setup(org_kde_kwin_remote_buffer *remotebuffer); + /** + * @returns @c true if managing a org_kde_kwin_remote_buffer. + **/ + bool isValid() const; + /** + * Releases the org_kde_kwin_remote_buffer interface. + * After the interface has been released the RemoteBuffer instance is no + * longer valid and can be setup with another org_kde_kwin_remote_buffer interface. + **/ + void release(); + /** + * Destroys the data held by this RemoteBuffer. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new org_kde_kwin_remote_buffer interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, remotebuffer, &RemoteBuffer::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + operator org_kde_kwin_remote_buffer*(); + operator org_kde_kwin_remote_buffer*() const; + + qint32 fd(); + quint32 width(); + quint32 height(); + quint32 stride(); + quint32 format(); + + +Q_SIGNALS: + void paramsObtained(); + +private: + + friend class RemoteAccessManager; + explicit RemoteBuffer(QObject *parent = nullptr); + class Private; + QScopedPointer d; +}; + + +} +} + +#endif diff --git a/src/client/remote_access.cpp b/src/client/remote_access.cpp new file mode 100644 --- /dev/null +++ b/src/client/remote_access.cpp @@ -0,0 +1,247 @@ +/**************************************************************************** +Copyright 2016 Oleg Chernovskiy + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#include "remote_access.h" +#include "event_queue.h" +#include "wayland_pointer_p.h" +// Wayland +#include +// Qt +#include + +namespace KWayland +{ +namespace Client +{ + +class RemoteAccessManager::Private +{ +public: + explicit Private(RemoteAccessManager *ram); + void setup(org_kde_kwin_remote_access_manager *k); + + WaylandPointer ram; + EventQueue *queue = nullptr; +private: + static const struct org_kde_kwin_remote_access_manager_listener s_listener; + static void bufferReadyCallback(void *data, org_kde_kwin_remote_access_manager *org_kde_kwin_remoteaccess, quint32 buffer_id); + + RemoteAccessManager *q; +}; + +RemoteAccessManager::Private::Private(RemoteAccessManager *q) + : q(q) +{ +} + +const org_kde_kwin_remote_access_manager_listener RemoteAccessManager::Private::s_listener = { + bufferReadyCallback +}; + +void RemoteAccessManager::Private::bufferReadyCallback(void *data, org_kde_kwin_remote_access_manager *interface, quint32 buffer_id) +{ + auto ramp = reinterpret_cast(data); + Q_ASSERT(ramp->ram == interface); + + // handle it fully internally, get the buffer immediately + auto requested = org_kde_kwin_remote_access_manager_get_buffer(ramp->ram, buffer_id); + auto rbuf = new RemoteBuffer(ramp->q); + rbuf->setup(requested); + qDebug() << "Got buffer from server, fd:" << buffer_id; + + emit ramp->q->bufferReady(rbuf); +} + +void RemoteAccessManager::Private::setup(org_kde_kwin_remote_access_manager *k) +{ + Q_ASSERT(k); + Q_ASSERT(!ram); + ram.setup(k); + org_kde_kwin_remote_access_manager_add_listener(k, &s_listener, this); +} + +RemoteAccessManager::RemoteAccessManager(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ +} + +RemoteAccessManager::~RemoteAccessManager() +{ + release(); +} + +void RemoteAccessManager::setup(org_kde_kwin_remote_access_manager *ram) +{ + d->setup(ram); +} + +void RemoteAccessManager::release() +{ + d->ram.release(); +} + +void RemoteAccessManager::destroy() +{ + d->ram.destroy(); +} + +void RemoteAccessManager::setEventQueue(EventQueue *queue) +{ + d->queue = queue; +} + +EventQueue *RemoteAccessManager::eventQueue() +{ + return d->queue; +} + +RemoteAccessManager::operator org_kde_kwin_remote_access_manager*() { + return d->ram; +} + +RemoteAccessManager::operator org_kde_kwin_remote_access_manager*() const { + return d->ram; +} + +bool RemoteAccessManager::isValid() const +{ + return d->ram.isValid(); +} + +class RemoteBuffer::Private +{ +public: + Private(RemoteBuffer *q); + void setup(org_kde_kwin_remote_buffer *buffer); + + static struct org_kde_kwin_remote_buffer_listener s_listener; + static void paramsCallback(void *data, org_kde_kwin_remote_buffer *rbuf, + qint32 fd, quint32 width, quint32 height, quint32 stride, quint32 format); + + WaylandPointer remotebuffer; + RemoteBuffer *q; + + qint32 fd = 0; + quint32 width = 0; + quint32 height = 0; + quint32 stride = 0; + quint32 format = 0; +}; + +RemoteBuffer::Private::Private(RemoteBuffer *q) + : q(q) +{ +} + +void RemoteBuffer::Private::paramsCallback(void *data, org_kde_kwin_remote_buffer *rbuf, + qint32 fd, quint32 width, quint32 height, quint32 stride, quint32 format) +{ + Q_UNUSED(rbuf) + Private *p = reinterpret_cast(data); + p->fd = fd; + p->width = width; + p->height = height; + p->stride = stride; + p->format = format; + emit p->q->paramsObtained(); +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +org_kde_kwin_remote_buffer_listener RemoteBuffer::Private::s_listener = { + paramsCallback +}; +#endif + +void RemoteBuffer::Private::setup(org_kde_kwin_remote_buffer *rbuffer) +{ + remotebuffer.setup(rbuffer); + org_kde_kwin_remote_buffer_add_listener(rbuffer, &s_listener, this); +} + +RemoteBuffer::RemoteBuffer(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ +} + +RemoteBuffer::~RemoteBuffer() +{ + release(); + qDebug() << "Buffer released"; +} + +void RemoteBuffer::setup(org_kde_kwin_remote_buffer *remotebuffer) +{ + Q_ASSERT(remotebuffer); + Q_ASSERT(!d->remotebuffer); + d->setup(remotebuffer); +} + +void RemoteBuffer::release() +{ + d->remotebuffer.release(); +} + +void RemoteBuffer::destroy() +{ + d->remotebuffer.destroy(); +} + +RemoteBuffer::operator org_kde_kwin_remote_buffer*() { + return d->remotebuffer; +} + +RemoteBuffer::operator org_kde_kwin_remote_buffer*() const { + return d->remotebuffer; +} + +bool RemoteBuffer::isValid() const +{ + return d->remotebuffer.isValid(); +} + +qint32 RemoteBuffer::fd() +{ + return d->fd; +} + +quint32 RemoteBuffer::width() +{ + return d->width; +} + +quint32 RemoteBuffer::height() +{ + return d->height; +} + +quint32 RemoteBuffer::stride() +{ + return d->stride; +} + +quint32 RemoteBuffer::format() +{ + return d->format; +} + + +} +} \ No newline at end of file diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -12,6 +12,7 @@ idle_interface.cpp fakeinput_interface.cpp keyboard_interface.cpp + remote_access_interface.cpp outputconfiguration_interface.cpp outputchangeset.cpp outputmanagement_interface.cpp @@ -113,6 +114,11 @@ BASENAME text-input-unstable-v2 ) +ecm_add_wayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${KWAYLAND_SOURCE_DIR}/src/client/protocols/remote-access.xml + BASENAME remote-access +) + add_library(KF5WaylandServer ${SERVER_LIB_SRCS}) generate_export_header(KF5WaylandServer BASE_NAME @@ -163,6 +169,7 @@ global.h idle_interface.h keyboard_interface.h + remote_access_interface.h outputdevice_interface.h outputchangeset.h outputconfiguration_interface.h diff --git a/src/server/display.h b/src/server/display.h --- a/src/server/display.h +++ b/src/server/display.h @@ -53,6 +53,7 @@ class DataDeviceManagerInterface; class DpmsManagerInterface; class IdleInterface; +class RemoteAccessManagerInterface; class FakeInputInterface; class OutputInterface; class OutputDeviceInterface; @@ -162,6 +163,7 @@ PlasmaWindowManagementInterface *createPlasmaWindowManagement(QObject *parent = nullptr); QtSurfaceExtensionInterface *createQtSurfaceExtension(QObject *parent = nullptr); IdleInterface *createIdle(QObject *parent = nullptr); + RemoteAccessManagerInterface *createRemoteAccessManager(QObject *parent = nullptr); FakeInputInterface *createFakeInput(QObject *parent = nullptr); ShadowManagerInterface *createShadowManager(QObject *parent = nullptr); BlurManagerInterface *createBlurManager(QObject *parent = nullptr); diff --git a/src/server/display.cpp b/src/server/display.cpp --- a/src/server/display.cpp +++ b/src/server/display.cpp @@ -25,6 +25,7 @@ #include "outputmanagement_interface.h" #include "outputdevice_interface.h" #include "idle_interface.h" +#include "remote_access_interface.h" #include "fakeinput_interface.h" #include "logging_p.h" #include "output_interface.h" @@ -279,6 +280,13 @@ return s; } +RemoteAccessManagerInterface *Display::createRemoteAccessManager(QObject *parent) +{ + auto i = new RemoteAccessManagerInterface(this, parent); + connect(this, &Display::aboutToTerminate, i, [this, i] { delete i; }); + return i; +} + IdleInterface *Display::createIdle(QObject *parent) { auto i = new IdleInterface(this, parent); diff --git a/src/server/remote_access_interface.h b/src/server/remote_access_interface.h new file mode 100644 --- /dev/null +++ b/src/server/remote_access_interface.h @@ -0,0 +1,118 @@ +/**************************************************************************** +Copyright 2016 Oleg Chernovskiy + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#ifndef KWAYLAND_SERVER_REMOTE_ACCESS_H +#define KWAYLAND_SERVER_REMOTE_ACCESS_H + +#include "global.h" +#include "resource.h" + +#include + +#include + +/** + * The structure server should fill to use this interface. + * Lifecycle: + * 1. GbmBuffer is filled and passed to RemoteAccessManager + * (stored in manager's sent list) + * 2. Client confirms that it wants this buffer, the RemoteBuffer + * interface is then created and wrapped around GbmBuffer. + * Manager purges its reference from sent list. + * 3. Once client is done with buffer (or disconnected), + * RemoteBuffer notifies manager and release signal is emitted. + * It's the responsibility of process to delete this GbmBuffer + * and release its' fd and gbm_bo afterwards. + **/ +struct GbmBuffer +{ + // relevant for server + qint32 fd = 0; //< also buffer_id + + // Note that on client side received fd number will be different + // and meaningful only for client process! + // Thus we can use server-side fd as an implicit unique id + + // relevant for client + quint32 width = 0; + quint32 height = 0; + quint32 stride = 0; + quint32 format = 0; +}; + +namespace KWayland +{ +namespace Server +{ + +class Display; + +class KWAYLANDSERVER_EXPORT RemoteAccessManagerInterface : public Global +{ + Q_OBJECT +public: + virtual ~RemoteAccessManagerInterface() = default; + + /** + * Store buffer in sent list and notify client that we have a buffer for it + **/ + void sendBufferReady(const GbmBuffer *buf); + /** + * Check whether interface has been bound + **/ + bool isBound() const; + +Q_SIGNALS: + /** + * Previously sent buffer has been released by client + */ + void bufferReleased(const GbmBuffer *buf); + +private: + explicit RemoteAccessManagerInterface(Display *display, QObject *parent = nullptr); + friend class Display; + class Private; + +}; + +class KWAYLANDSERVER_EXPORT RemoteBufferInterface : public Resource +{ + Q_OBJECT +public: + virtual ~RemoteBufferInterface() = default; + + /** + * Sends GBM fd to the client. + * Note that server still has to close mirror fd from its side. + **/ + void passFd(); + +private: + explicit RemoteBufferInterface(RemoteAccessManagerInterface *ram, wl_resource *pResource, const GbmBuffer *buf); + friend class RemoteAccessManagerInterface; + + class Private; + Private *d_func() const; +}; + + +} +} + +#endif diff --git a/src/server/remote_access_interface.cpp b/src/server/remote_access_interface.cpp new file mode 100644 --- /dev/null +++ b/src/server/remote_access_interface.cpp @@ -0,0 +1,267 @@ +/**************************************************************************** +Copyright 2016 Oleg Chernovskiy + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#include "remote_access_interface.h" +#include "display.h" +#include "global_p.h" +#include "resource_p.h" + +#include + +#include + +namespace KWayland +{ +namespace Server +{ + +class RemoteAccessManagerInterface::Private : public Global::Private +{ +public: + Private(RemoteAccessManagerInterface *q, Display *d); + virtual ~Private(); + + void sendBufferReady(const GbmBuffer *buf); + void releaseAll(); + + bool bound = false; + +private: + // methods + static void unbind(wl_resource *resource); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + static void getBufferCallback(wl_client *client, wl_resource *resource, uint32_t buffer, uint32_t internalBufId); + void bind(wl_client *client, uint32_t version, uint32_t id) override; + + // fields + static const struct org_kde_kwin_remote_access_manager_interface s_interface; + static const quint32 s_version; + + RemoteAccessManagerInterface *q; + wl_resource *m_resource = nullptr; + + /** + * Buffers that were acked by client + * with RemoteBuffer wrapped around them + **/ + QList ackedBuffers; + + /** + * Buffers that were sent but still not acked by server + * Keys are fd numbers as they are unique + **/ + QHash sentBuffers; +}; + +const quint32 RemoteAccessManagerInterface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct org_kde_kwin_remote_access_manager_interface RemoteAccessManagerInterface::Private::s_interface = { + getBufferCallback +}; +#endif + +void RemoteAccessManagerInterface::Private::getBufferCallback(wl_client *client, wl_resource *resource, uint32_t buffer, uint32_t internalBufId) +{ + Private *p = cast(resource); + + // client asks for buffer we earlier announced, we must have it + const GbmBuffer *buf = p->sentBuffers[internalBufId]; + if(Q_UNLIKELY(!buf)) { // no such buffer (?) + wl_resource_post_no_memory(resource); + return; + } + + auto rbuf = new RemoteBufferInterface(p->q, resource, buf); + rbuf->create(p->display->getConnection(client), wl_resource_get_version(resource), buffer); + if (!rbuf->resource()) { + wl_resource_post_no_memory(resource); + delete rbuf; + return; + } + + // move buffer to acked buffers from sent + p->sentBuffers.remove(internalBufId); + p->ackedBuffers << rbuf; + + QObject::connect(rbuf, &QObject::destroyed, [p, rbuf, buffer, internalBufId] { + p->ackedBuffers.removeOne(rbuf); + qDebug() << "Server buffer released: id" << buffer << ", fd" << internalBufId; + }); + + // send buffer params + rbuf->passFd(); +} + +RemoteAccessManagerInterface::Private::Private(RemoteAccessManagerInterface *q, Display *d) + : Global::Private(d, &org_kde_kwin_remote_access_manager_interface, s_version) + , q(q) +{ +} + +void RemoteAccessManagerInterface::Private::sendBufferReady(const GbmBuffer *buf) +{ + // store buffer locally, client will ask it later + sentBuffers[buf->fd] = buf; + // notify client + qDebug() << "Server buffer sent: fd" << buf->fd; + org_kde_kwin_remote_access_manager_send_buffer_ready(m_resource, buf->fd); +} + +void RemoteAccessManagerInterface::Private::releaseAll() +{ + // remote buffers are in our responsibility, delete them + // wrapped gbm buffers will be released anyway + for (RemoteBufferInterface *acked : ackedBuffers) { + acked->deleteLater(); + } + + // non-acked buffers should be released manually + for (const GbmBuffer *buf : sentBuffers) { + emit q->bufferReleased(buf); + } +} + +void RemoteAccessManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + // only 1 client permitted + if (bound) { + wl_client_post_no_memory(client); + return; + } + + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&org_kde_kwin_remote_access_manager_interface, qMin(version, s_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &s_interface, this, unbind); + m_resource = resource; + bound = true; +} + +void RemoteAccessManagerInterface::Private::unbind(wl_resource *resource) +{ + Private *p = cast(resource); + + // we're unbinding, hence all acked and sent buffers are now effectively invalid + // client won't come for them + p->releaseAll(); + p->m_resource = nullptr; + p->bound = false; +} + +RemoteAccessManagerInterface::Private::~Private() +{ + // server deletes created interfaces, release all held buffers + releaseAll(); +} + +RemoteAccessManagerInterface::RemoteAccessManagerInterface(Display *display, QObject *parent) + : Global(new Private(this, display), parent) +{ +} + +void RemoteAccessManagerInterface::sendBufferReady(const GbmBuffer *buf) +{ + Private *priv = reinterpret_cast(d.data()); + priv->sendBufferReady(buf); +} + +bool RemoteAccessManagerInterface::isBound() const +{ + Private *priv = reinterpret_cast(d.data()); + return priv->bound; +} + +class RemoteBufferInterface::Private : public Resource::Private +{ +public: + Private(RemoteAccessManagerInterface *ram, RemoteBufferInterface *q, wl_resource *pResource, const GbmBuffer *buf); + ~Private(); + + void passFd(); + +private: + static void releaseCallback(wl_client *client, wl_resource *resource); + + static const struct org_kde_kwin_remote_buffer_interface s_interface; + + const GbmBuffer *const wrapped; +}; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct org_kde_kwin_remote_buffer_interface RemoteBufferInterface::Private::s_interface = { + releaseCallback +}; +#endif + +void RemoteBufferInterface::Private::releaseCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + Private *p = cast(resource); + // client wants to release this buffer, notify manager + + p->q->deleteLater(); // also purges it from manager's list +} + +RemoteBufferInterface::Private::Private(RemoteAccessManagerInterface *ram, RemoteBufferInterface *q, wl_resource *pResource, const GbmBuffer *buf) + : Resource::Private(q, ram, pResource, &org_kde_kwin_remote_buffer_interface, &s_interface), wrapped(buf) +{ +} + +RemoteBufferInterface::Private::~Private() +{ + if (resource) { + wl_resource_destroy(resource); + resource = nullptr; + } + + auto ram = reinterpret_cast(global); + emit ram->bufferReleased(wrapped); +} + +void RemoteBufferInterface::Private::passFd() +{ + org_kde_kwin_remote_buffer_send_gbm_handle(resource, wrapped->fd, + wrapped->width, wrapped->height, wrapped->stride, wrapped->format); +} + +RemoteBufferInterface::RemoteBufferInterface(RemoteAccessManagerInterface *ram, wl_resource *pResource, const GbmBuffer *buf) + : Resource(new Private(ram, this, pResource, buf), ram) +{ +} + +RemoteBufferInterface::Private *RemoteBufferInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + + +void RemoteBufferInterface::passFd() +{ + d_func()->passFd(); +} + +} +} + diff --git a/src/tools/mapping.txt b/src/tools/mapping.txt --- a/src/tools/mapping.txt +++ b/src/tools/mapping.txt @@ -44,3 +44,6 @@ wl_text_input_manager;TextInputManagerUnstableV0 zwp_text_input_v2;TextInputUnstableV2 zwp_text_input_manager_v2;TextInputManagerUnstableV2 +org_kde_kwin_remote_access_manager;RemoteAccessManager +org_kde_kwin_remote_buffer;RemoteBuffer +