From e712c5db2bd00f4be59331e39ef42312d5cf3b1c Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Mon, 1 Jun 2020 23:29:53 +0100 Subject: [PATCH] Introduce API round primary-selection-unstable-v1.xml AKA middle-click paste It's mostly a copy-paste of DataDevice. --- src/wayland/CMakeLists.txt | 10 ++ src/wayland/display.cpp | 8 + src/wayland/display.h | 6 + .../primaryselectiondevice_v1_interface.cpp | 143 ++++++++++++++++++ .../primaryselectiondevice_v1_interface.h | 64 ++++++++ ...aryselectiondevicemanager_v1_interface.cpp | 77 ++++++++++ ...imaryselectiondevicemanager_v1_interface.h | 43 ++++++ .../primaryselectionoffer_v1_interface.cpp | 87 +++++++++++ .../primaryselectionoffer_v1_interface.h | 48 ++++++ .../primaryselectionsource_v1_interface.cpp | 92 +++++++++++ .../primaryselectionsource_v1_interface.h | 49 ++++++ src/wayland/seat_interface.cpp | 90 +++++++++++ src/wayland/seat_interface.h | 9 ++ src/wayland/seat_interface_p.h | 6 + 14 files changed, 732 insertions(+) create mode 100644 src/wayland/primaryselectiondevice_v1_interface.cpp create mode 100644 src/wayland/primaryselectiondevice_v1_interface.h create mode 100644 src/wayland/primaryselectiondevicemanager_v1_interface.cpp create mode 100644 src/wayland/primaryselectiondevicemanager_v1_interface.h create mode 100644 src/wayland/primaryselectionoffer_v1_interface.cpp create mode 100644 src/wayland/primaryselectionoffer_v1_interface.h create mode 100644 src/wayland/primaryselectionsource_v1_interface.cpp create mode 100644 src/wayland/primaryselectionsource_v1_interface.h diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt index 3a9625108e..2d44d5cce6 100644 --- a/src/wayland/CMakeLists.txt +++ b/src/wayland/CMakeLists.txt @@ -40,6 +40,10 @@ set(SERVER_LIB_SRCS pointerconstraints_interface_v1.cpp pointergestures_interface.cpp pointergestures_interface_v1.cpp + primaryselectiondevice_v1_interface.cpp + primaryselectiondevicemanager_v1_interface.cpp + primaryselectionoffer_v1_interface.cpp + primaryselectionsource_v1_interface.cpp region_interface.cpp relativepointer_interface.cpp relativepointer_interface_v1.cpp @@ -244,6 +248,11 @@ ecm_add_qtwayland_server_protocol(SERVER_LIB_SRCS BASENAME viewporter ) +ecm_add_qtwayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${WaylandProtocols_DATADIR}/unstable/primary-selection/primary-selection-unstable-v1.xml + BASENAME wp-primary-selection-unstable-v1 +) + set(SERVER_GENERATED_SRCS ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-blur-server-protocol.h @@ -378,6 +387,7 @@ set(SERVER_LIB_HEADERS pointer_interface.h pointerconstraints_interface.h pointergestures_interface.h + primaryselectiondevicemanager_v1_interface.h region_interface.h relativepointer_interface.h remote_access_interface.h diff --git a/src/wayland/display.cpp b/src/wayland/display.cpp index 23edc0a608..f8ebdf14bd 100644 --- a/src/wayland/display.cpp +++ b/src/wayland/display.cpp @@ -29,6 +29,7 @@ #include "plasmawindowmanagement_interface.h" #include "pointerconstraints_interface_p.h" #include "pointergestures_interface_p.h" +#include "primaryselectiondevicemanager_v1_interface.h" #include "relativepointer_interface_p.h" #include "remote_access_interface.h" #include "seat_interface.h" @@ -524,6 +525,13 @@ ViewporterInterface *Display::createViewporter(QObject *parent) return viewporter; } +PrimarySelectionDeviceManagerV1Interface *Display::createPrimarySelectionDeviceManagerV1(QObject *parent) +{ + auto primarySelection = new PrimarySelectionDeviceManagerV1Interface(this, parent); + connect(this, &Display::aboutToTerminate, primarySelection, [primarySelection] { delete primarySelection; }); + return primarySelection; +} + void Display::createShm() { Q_ASSERT(d->display); diff --git a/src/wayland/display.h b/src/wayland/display.h index 8849f7bf17..56df57c48f 100644 --- a/src/wayland/display.h +++ b/src/wayland/display.h @@ -78,6 +78,7 @@ class KeyStateInterface; class LinuxDmabufUnstableV1Interface; class TabletManagerInterface; class DataControlDeviceManagerV1Interface; +class PrimarySelectionDeviceManagerV1Interface; class KeyboardShortcutsInhibitManagerV1Interface; class ViewporterInterface; @@ -333,6 +334,11 @@ public: */ ViewporterInterface *createViewporter(QObject *parent = nullptr); + /** + * Creates the PrimarySelectionDeviceManagerV1Interface + */ + PrimarySelectionDeviceManagerV1Interface *createPrimarySelectionDeviceManagerV1(QObject *parent = nullptr); + /** * Gets the ClientConnection for the given @p client. * If there is no ClientConnection yet for the given @p client, it will be created. diff --git a/src/wayland/primaryselectiondevice_v1_interface.cpp b/src/wayland/primaryselectiondevice_v1_interface.cpp new file mode 100644 index 0000000000..7d70a2b417 --- /dev/null +++ b/src/wayland/primaryselectiondevice_v1_interface.cpp @@ -0,0 +1,143 @@ +/* + SPDX-FileCopyrightText: 2020 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#include "primaryselectiondevice_v1_interface.h" +#include "primaryselectiondevicemanager_v1_interface.h" +#include "primaryselectionoffer_v1_interface.h" +#include "primaryselectionsource_v1_interface.h" +#include "display.h" +#include "seat_interface.h" +#include "seat_interface_p.h" + +// Wayland +#include + +namespace KWaylandServer +{ + +class PrimarySelectionDeviceV1InterfacePrivate: public QtWaylandServer::zwp_primary_selection_device_v1 +{ +public: + PrimarySelectionDeviceV1InterfacePrivate(PrimarySelectionDeviceV1Interface *q, SeatInterface *seat, wl_resource *resource); + + PrimarySelectionOfferV1Interface *createDataOffer(AbstractDataSource *source); + + PrimarySelectionDeviceV1Interface *q; + SeatInterface *seat; + QPointer selection; + +private: + void setSelection(PrimarySelectionSourceV1Interface *dataSource); + +protected: + void zwp_primary_selection_device_v1_destroy_resource(Resource *resource) override; + void zwp_primary_selection_device_v1_set_selection(Resource *resource, wl_resource *source, uint32_t serial) override; + void zwp_primary_selection_device_v1_destroy(Resource *resource) override; +}; + + +PrimarySelectionDeviceV1InterfacePrivate::PrimarySelectionDeviceV1InterfacePrivate(PrimarySelectionDeviceV1Interface *_q, SeatInterface *seat, wl_resource *resource) + : QtWaylandServer::zwp_primary_selection_device_v1(resource) + , q(_q) + , seat(seat) +{ +} + +void PrimarySelectionDeviceV1InterfacePrivate::zwp_primary_selection_device_v1_set_selection(Resource *resource, wl_resource *source, uint32_t serial) +{ + Q_UNUSED(resource) + Q_UNUSED(serial) + PrimarySelectionSourceV1Interface *dataSource = nullptr; + + if (source) { + dataSource = PrimarySelectionSourceV1Interface::get(source); + Q_ASSERT(dataSource); + } + + if (selection == dataSource) { + return; + } + if (selection) { + selection->cancel(); + } + selection = dataSource; + if (selection) { + emit q->selectionChanged(selection); + } +} + +void PrimarySelectionDeviceV1InterfacePrivate::zwp_primary_selection_device_v1_destroy(QtWaylandServer::zwp_primary_selection_device_v1::Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +PrimarySelectionOfferV1Interface *PrimarySelectionDeviceV1InterfacePrivate::createDataOffer(AbstractDataSource *source) +{ + if (!source) { + // a data offer can only exist together with a source + return nullptr; + } + + wl_resource *data_offer_resource = wl_resource_create(resource()->client(), &zwp_primary_selection_offer_v1_interface, resource()->version(), 0); + if (!data_offer_resource) { + wl_resource_post_no_memory(resource()->handle); + return nullptr; + } + + PrimarySelectionOfferV1Interface *offer = new PrimarySelectionOfferV1Interface(source, data_offer_resource); + send_data_offer(offer->resource()); + offer->sendAllOffers(); + return offer; +} + +void PrimarySelectionDeviceV1InterfacePrivate::zwp_primary_selection_device_v1_destroy_resource(QtWaylandServer::zwp_primary_selection_device_v1::Resource *resource) +{ + Q_UNUSED(resource) + delete q; +} + +PrimarySelectionDeviceV1Interface::PrimarySelectionDeviceV1Interface(SeatInterface *seat, wl_resource *resource) + : QObject() + , d(new PrimarySelectionDeviceV1InterfacePrivate(this, seat, resource)) +{ + seat->d_func()->registerPrimarySelectionDevice(this); +} + +PrimarySelectionDeviceV1Interface::~PrimarySelectionDeviceV1Interface() = default; + +SeatInterface *PrimarySelectionDeviceV1Interface::seat() const +{ + return d->seat; +} + +PrimarySelectionSourceV1Interface *PrimarySelectionDeviceV1Interface::selection() const +{ + return d->selection; +} + +void PrimarySelectionDeviceV1Interface::sendSelection(AbstractDataSource *other) +{ + if (!other) { + sendClearSelection(); + return; + } + PrimarySelectionOfferV1Interface *offer = d->createDataOffer(other); + if (!offer) { + return; + } + d->send_selection(offer->resource()); +} + +void PrimarySelectionDeviceV1Interface::sendClearSelection() +{ + d->send_selection(nullptr); +} + +wl_client *PrimarySelectionDeviceV1Interface::client() const +{ + return d->resource()->client(); +} + +} diff --git a/src/wayland/primaryselectiondevice_v1_interface.h b/src/wayland/primaryselectiondevice_v1_interface.h new file mode 100644 index 0000000000..5fb2d5d645 --- /dev/null +++ b/src/wayland/primaryselectiondevice_v1_interface.h @@ -0,0 +1,64 @@ +/* + SPDX-FileCopyrightText: 2020 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#ifndef WAYLAND_SERVER_PRIMARY_SELECTION_DEVICE_INTERFACE_H +#define WAYLAND_SERVER_PRIMARY_SELECTION_DEVICE_INTERFACE_H + +#include + +#include + +struct wl_resource; +struct wl_client; + +namespace KWaylandServer +{ + +class AbstractDataSource; +class PrimarySelectionDeviceManagerV1Interface; +class PrimarySelectionOfferV1Interface; +class PrimarySelectionSourceV1Interface; +class SeatInterface; +class SurfaceInterface; +class PrimarySelectionDeviceV1InterfacePrivate; + +/** + * @brief Represents the Resource for the wl_data_device interface. + * + * @see SeatInterface + * @see PrimarySelectionSourceInterface + * Lifespan is mapped to the underlying object + **/ +class KWAYLANDSERVER_EXPORT PrimarySelectionDeviceV1Interface : public QObject +{ + Q_OBJECT +public: + virtual ~PrimarySelectionDeviceV1Interface(); + + SeatInterface *seat() const; + + PrimarySelectionSourceV1Interface *selection() const; + + void sendSelection(AbstractDataSource *other); + void sendClearSelection(); + + wl_client *client() const; + +Q_SIGNALS: + void selectionChanged(KWaylandServer::PrimarySelectionSourceV1Interface*); + void selectionCleared(); + +private: + friend class PrimarySelectionDeviceManagerV1InterfacePrivate; + explicit PrimarySelectionDeviceV1Interface(SeatInterface *seat, wl_resource *resource); + + QScopedPointer d; +}; + +} + +Q_DECLARE_METATYPE(KWaylandServer::PrimarySelectionDeviceV1Interface*) + +#endif diff --git a/src/wayland/primaryselectiondevicemanager_v1_interface.cpp b/src/wayland/primaryselectiondevicemanager_v1_interface.cpp new file mode 100644 index 0000000000..a6a90d2b51 --- /dev/null +++ b/src/wayland/primaryselectiondevicemanager_v1_interface.cpp @@ -0,0 +1,77 @@ +/* + SPDX-FileCopyrightText: 2020 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ + +#include "primaryselectiondevicemanager_v1_interface.h" +#include "primaryselectiondevice_v1_interface.h" +#include "primaryselectionsource_v1_interface.h" +#include "display.h" +#include "seat_interface_p.h" +// Wayland +#include + +static const int s_version = 1; +namespace KWaylandServer +{ + +class PrimarySelectionDeviceManagerV1InterfacePrivate : public QtWaylandServer::zwp_primary_selection_device_manager_v1 +{ +public: + PrimarySelectionDeviceManagerV1InterfacePrivate(PrimarySelectionDeviceManagerV1Interface *q, Display *d); + + PrimarySelectionDeviceManagerV1Interface *q; +protected: + void zwp_primary_selection_device_manager_v1_create_source(Resource *resource, uint32_t id) override; + void zwp_primary_selection_device_manager_v1_get_device(Resource *resource, uint32_t id, wl_resource *seat) override; + void zwp_primary_selection_device_manager_v1_destroy(Resource *resource) override; +}; + +PrimarySelectionDeviceManagerV1InterfacePrivate::PrimarySelectionDeviceManagerV1InterfacePrivate(PrimarySelectionDeviceManagerV1Interface *q, Display *d) + : QtWaylandServer::zwp_primary_selection_device_manager_v1(*d, s_version) + , q(q) +{ +} + +void PrimarySelectionDeviceManagerV1InterfacePrivate::zwp_primary_selection_device_manager_v1_create_source(Resource *resource, uint32_t id) +{ + wl_resource *data_source_resource = wl_resource_create(resource->client(), &zwp_primary_selection_source_v1_interface, resource->version(), id); + if (!data_source_resource) { + wl_resource_post_no_memory(resource->handle); + return; + } + PrimarySelectionSourceV1Interface *dataSource = new PrimarySelectionSourceV1Interface(q, data_source_resource); + emit q->dataSourceCreated(dataSource); +} + +void PrimarySelectionDeviceManagerV1InterfacePrivate::zwp_primary_selection_device_manager_v1_get_device(Resource *resource, uint32_t id, wl_resource *seat) +{ + SeatInterface *s = SeatInterface::get(seat); + Q_ASSERT(s); + if (!s) { + return; + } + + wl_resource *data_device_resource = wl_resource_create(resource->client(), &zwp_primary_selection_device_v1_interface, resource->version(), id); + if (!data_device_resource) { + wl_resource_post_no_memory(resource->handle); + return; + } + PrimarySelectionDeviceV1Interface *dataDevice = new PrimarySelectionDeviceV1Interface(s, data_device_resource); + emit q->dataDeviceCreated(dataDevice); +} + +void PrimarySelectionDeviceManagerV1InterfacePrivate::zwp_primary_selection_device_manager_v1_destroy(Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +PrimarySelectionDeviceManagerV1Interface::PrimarySelectionDeviceManagerV1Interface(Display *display, QObject *parent) + : QObject(parent) + , d(new PrimarySelectionDeviceManagerV1InterfacePrivate(this, display)) +{ +} + +PrimarySelectionDeviceManagerV1Interface::~PrimarySelectionDeviceManagerV1Interface() = default; +} diff --git a/src/wayland/primaryselectiondevicemanager_v1_interface.h b/src/wayland/primaryselectiondevicemanager_v1_interface.h new file mode 100644 index 0000000000..685e44da33 --- /dev/null +++ b/src/wayland/primaryselectiondevicemanager_v1_interface.h @@ -0,0 +1,43 @@ +/* + SPDX-FileCopyrightText: 2020 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#ifndef WAYLAND_SERVER_PRIMARY_SELECTION_DEVICE_MANAGER_INTERFACE_H +#define WAYLAND_SERVER_PRIMARY_SELECTION_DEVICE_MANAGER_INTERFACE_H + +#include + +#include + +namespace KWaylandServer +{ + +class Display; +class PrimarySelectionSourceV1Interface; +class PrimarySelectionDeviceManagerV1InterfacePrivate; +class PrimarySelectionDeviceV1Interface; + +/** + * @brief Represents the Global for zwp_primary_selection_manager_v1 interface. + * + **/ +class KWAYLANDSERVER_EXPORT PrimarySelectionDeviceManagerV1Interface : public QObject +{ + Q_OBJECT +public: + ~PrimarySelectionDeviceManagerV1Interface(); + +Q_SIGNALS: + void dataSourceCreated(KWaylandServer::PrimarySelectionSourceV1Interface *dataSource); + void dataDeviceCreated(KWaylandServer::PrimarySelectionDeviceV1Interface *dataDevice); + +private: + explicit PrimarySelectionDeviceManagerV1Interface(Display *display, QObject *parent = nullptr); + friend class Display; + QScopedPointer d; +}; + +} + +#endif diff --git a/src/wayland/primaryselectionoffer_v1_interface.cpp b/src/wayland/primaryselectionoffer_v1_interface.cpp new file mode 100644 index 0000000000..2136021d9a --- /dev/null +++ b/src/wayland/primaryselectionoffer_v1_interface.cpp @@ -0,0 +1,87 @@ +/* + SPDX-FileCopyrightText: 2020 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#include "primaryselectionoffer_v1_interface.h" +#include "primaryselectiondevice_v1_interface.h" +#include "primaryselectionsource_v1_interface.h" +// Qt +#include +#include +// Wayland +#include +// system +#include + +namespace KWaylandServer +{ + +class PrimarySelectionOfferV1InterfacePrivate : public QtWaylandServer::zwp_primary_selection_offer_v1 +{ +public: + PrimarySelectionOfferV1InterfacePrivate(PrimarySelectionOfferV1Interface *q, AbstractDataSource *source, wl_resource *resource); + PrimarySelectionOfferV1Interface *q; + QPointer source; + +protected: + void zwp_primary_selection_offer_v1_receive(Resource *resource, const QString &mime_type, int32_t fd) override; + void zwp_primary_selection_offer_v1_destroy(Resource *resource) override; + void zwp_primary_selection_offer_v1_destroy_resource(Resource *resource) override; +}; + +PrimarySelectionOfferV1InterfacePrivate::PrimarySelectionOfferV1InterfacePrivate(PrimarySelectionOfferV1Interface *_q, AbstractDataSource *source, wl_resource *resource) + : QtWaylandServer::zwp_primary_selection_offer_v1(resource) + , q(_q) + , source(source) +{ +} + +void PrimarySelectionOfferV1InterfacePrivate::zwp_primary_selection_offer_v1_destroy(QtWaylandServer::zwp_primary_selection_offer_v1::Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +void PrimarySelectionOfferV1InterfacePrivate::zwp_primary_selection_offer_v1_destroy_resource(QtWaylandServer::zwp_primary_selection_offer_v1::Resource *resource) +{ + Q_UNUSED(resource) + delete q; +} + +void PrimarySelectionOfferV1InterfacePrivate::zwp_primary_selection_offer_v1_receive(Resource *resource, const QString &mimeType, qint32 fd) +{ + Q_UNUSED(resource) + if (!source) { + close(fd); + return; + } + source->requestData(mimeType, fd); +} + +PrimarySelectionOfferV1Interface::PrimarySelectionOfferV1Interface(AbstractDataSource *source, wl_resource *resource) + : QObject() + , d(new PrimarySelectionOfferV1InterfacePrivate(this, source, resource)) +{ + Q_ASSERT(source); + connect(source, &AbstractDataSource::mimeTypeOffered, this, + [this](const QString &mimeType) { + d->send_offer(mimeType); + } + ); +} + +PrimarySelectionOfferV1Interface::~PrimarySelectionOfferV1Interface() = default; + +void PrimarySelectionOfferV1Interface::sendAllOffers() +{ + for (const QString &mimeType : d->source->mimeTypes()) { + d->send_offer(mimeType); + } +} + +wl_resource *PrimarySelectionOfferV1Interface::resource() const +{ + return d->resource()->handle; +} + +} diff --git a/src/wayland/primaryselectionoffer_v1_interface.h b/src/wayland/primaryselectionoffer_v1_interface.h new file mode 100644 index 0000000000..4000e2b996 --- /dev/null +++ b/src/wayland/primaryselectionoffer_v1_interface.h @@ -0,0 +1,48 @@ +/* + SPDX-FileCopyrightText: 2020 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#ifndef WAYLAND_SERVER_PRIMARY_SELECTION_OFFER_INTERFACE_H +#define WAYLAND_SERVER_PRIMARY_SELECTION_OFFER_INTERFACE_H + +#include + +#include + +#include "primaryselectiondevicemanager_v1_interface.h" + +struct wl_resource; +namespace KWaylandServer +{ + +class AbstractDataSource; +class PrimarySelectionDeviceV1Interface; +class PrimarySelectionSourceV1Interface; +class PrimarySelectionOfferV1InterfacePrivate; + +/** + * @brief Represents the Resource for the wl_data_offer interface. + * Lifespan is mapped to the underlying object + **/ +class KWAYLANDSERVER_EXPORT PrimarySelectionOfferV1Interface : public QObject +{ + Q_OBJECT +public: + ~PrimarySelectionOfferV1Interface() override; + + void sendAllOffers(); + wl_resource *resource() const; + +private: + friend class PrimarySelectionDeviceV1InterfacePrivate; + explicit PrimarySelectionOfferV1Interface(AbstractDataSource *source, wl_resource *resource); + + QScopedPointer d; +}; + +} + +Q_DECLARE_METATYPE(KWaylandServer::PrimarySelectionOfferV1Interface*) + +#endif diff --git a/src/wayland/primaryselectionsource_v1_interface.cpp b/src/wayland/primaryselectionsource_v1_interface.cpp new file mode 100644 index 0000000000..7c2c520631 --- /dev/null +++ b/src/wayland/primaryselectionsource_v1_interface.cpp @@ -0,0 +1,92 @@ +/* + SPDX-FileCopyrightText: 2020 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#include "primaryselectionsource_v1_interface.h" +#include "primaryselectiondevicemanager_v1_interface.h" +#include "clientconnection.h" +// Qt +#include +// Wayland +#include +// system +#include + +namespace KWaylandServer +{ + +class PrimarySelectionSourceV1InterfacePrivate : public QtWaylandServer::zwp_primary_selection_source_v1 +{ +public: + PrimarySelectionSourceV1InterfacePrivate(PrimarySelectionSourceV1Interface *q, ::wl_resource *resource); + + QStringList mimeTypes; + PrimarySelectionSourceV1Interface *q; +protected: + void zwp_primary_selection_source_v1_destroy_resource(Resource *resource) override; + void zwp_primary_selection_source_v1_offer(Resource *resource, const QString &mime_type) override; + void zwp_primary_selection_source_v1_destroy(Resource *resource) override; +}; + +PrimarySelectionSourceV1InterfacePrivate::PrimarySelectionSourceV1InterfacePrivate(PrimarySelectionSourceV1Interface *_q, ::wl_resource *resource) + : QtWaylandServer::zwp_primary_selection_source_v1(resource) + , q(_q) +{ +} + +void PrimarySelectionSourceV1InterfacePrivate::zwp_primary_selection_source_v1_destroy_resource(QtWaylandServer::zwp_primary_selection_source_v1::Resource *resource) +{ + Q_UNUSED(resource) + emit q->aboutToBeUnbound(); + emit q->unbound(); + delete q; +} + +void PrimarySelectionSourceV1InterfacePrivate::zwp_primary_selection_source_v1_offer(Resource *, const QString &mimeType) +{ + mimeTypes << mimeType; + emit q->mimeTypeOffered(mimeType); +} + +void PrimarySelectionSourceV1InterfacePrivate::zwp_primary_selection_source_v1_destroy(QtWaylandServer::zwp_primary_selection_source_v1::Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +PrimarySelectionSourceV1Interface::PrimarySelectionSourceV1Interface(PrimarySelectionDeviceManagerV1Interface *parent, ::wl_resource *resource) + : AbstractDataSource(nullptr, parent) + , d(new PrimarySelectionSourceV1InterfacePrivate(this, resource)) +{ +} + +PrimarySelectionSourceV1Interface::~PrimarySelectionSourceV1Interface() = default; + +void PrimarySelectionSourceV1Interface::requestData(const QString &mimeType, qint32 fd) +{ + d->send_send(mimeType, fd); + close(fd); +} + +void PrimarySelectionSourceV1Interface::cancel() +{ + d->send_cancelled(); +} + +QStringList PrimarySelectionSourceV1Interface::mimeTypes() const +{ + return d->mimeTypes; +} + +wl_client *PrimarySelectionSourceV1Interface::client() +{ + return d->resource()->client(); +} + +PrimarySelectionSourceV1Interface *PrimarySelectionSourceV1Interface::get(wl_resource *native) +{ + auto priv = static_cast(QtWaylandServer::zwp_primary_selection_source_v1::Resource::fromResource(native)->object()); + return priv->q; +} + +} diff --git a/src/wayland/primaryselectionsource_v1_interface.h b/src/wayland/primaryselectionsource_v1_interface.h new file mode 100644 index 0000000000..11bd9f6151 --- /dev/null +++ b/src/wayland/primaryselectionsource_v1_interface.h @@ -0,0 +1,49 @@ +/* + SPDX-FileCopyrightText: 2020 David Edmundson + + SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL +*/ +#ifndef WAYLAND_SERVER_PRIMARY_SELECTION_SOURCE_INTERFACE_H +#define WAYLAND_SERVER_PRIMARY_SELECTION_SOURCE_INTERFACE_H + +#include "abstract_data_source.h" + +#include + +#include "primaryselectiondevicemanager_v1_interface.h" + +namespace KWaylandServer +{ + +class PrimarySelectionSourceV1InterfacePrivate; + +/** + * @brief Represents the Resource for the zwp_primary_selection_source_v1 interface. + * Lifespan is mapped to the underlying object + **/ +class KWAYLANDSERVER_EXPORT PrimarySelectionSourceV1Interface : public AbstractDataSource +{ + Q_OBJECT +public: + ~PrimarySelectionSourceV1Interface() override; + + void requestData(const QString &mimeType, qint32 fd) override; + void cancel() override; + + QStringList mimeTypes() const override; + + static PrimarySelectionSourceV1Interface *get(wl_resource *native); + wl_client *client() override; + +private: + friend class PrimarySelectionDeviceManagerV1InterfacePrivate; + explicit PrimarySelectionSourceV1Interface(PrimarySelectionDeviceManagerV1Interface *parent, ::wl_resource *resource); + + QScopedPointer d; +}; + +} + +Q_DECLARE_METATYPE(KWaylandServer::PrimarySelectionSourceV1Interface*) + +#endif diff --git a/src/wayland/seat_interface.cpp b/src/wayland/seat_interface.cpp index 1424148497..912ea5f24a 100644 --- a/src/wayland/seat_interface.cpp +++ b/src/wayland/seat_interface.cpp @@ -16,6 +16,8 @@ #include "keyboard_interface_p.h" #include "pointer_interface.h" #include "pointer_interface_p.h" +#include "primaryselectiondevice_v1_interface.h" +#include "primaryselectionsource_v1_interface.h" #include "surface_interface.h" #include "textinput_interface_p.h" // Qt @@ -359,6 +361,37 @@ void SeatInterface::Private::registerDataControlDevice(DataControlDeviceV1Interf } } +void SeatInterface::Private::registerPrimarySelectionDevice(PrimarySelectionDeviceV1Interface *primarySelectionDevice) +{ + Q_ASSERT(primarySelectionDevice->seat() == q); + + primarySelectionDevices << primarySelectionDevice; + auto dataDeviceCleanup = [this, primarySelectionDevice] { + primarySelectionDevices.removeOne(primarySelectionDevice); + keys.focus.primarySelections.removeOne(primarySelectionDevice); + }; + QObject::connect(primarySelectionDevice, &QObject::destroyed, q, dataDeviceCleanup); + QObject::connect(primarySelectionDevice, &PrimarySelectionDeviceV1Interface::selectionChanged, q, + [this, primarySelectionDevice] { + updatePrimarySelection(primarySelectionDevice); + } + ); + QObject::connect(primarySelectionDevice, &PrimarySelectionDeviceV1Interface::selectionCleared, q, + [this, primarySelectionDevice] { + updatePrimarySelection(primarySelectionDevice); + } + ); + // is the new DataDevice for the current keyoard focus? + if (keys.focus.surface) { + // same client? + if (*keys.focus.surface->client() == primarySelectionDevice->client()) { + keys.focus.primarySelections.append(primarySelectionDevice); + if (currentPrimarySelection) { + primarySelectionDevice->sendSelection(currentPrimarySelection); + } + } + } +} void SeatInterface::Private::registerTextInput(TextInputInterface *ti) { @@ -412,6 +445,15 @@ void SeatInterface::Private::updateSelection(DataDeviceInterface *dataDevice) q->setSelection(dataDevice->selection()); } +void SeatInterface::Private::updatePrimarySelection(PrimarySelectionDeviceV1Interface *primarySelectionDevice) +{ + // if the update is from the focussed window we should inform the active client + if (!(keys.focus.surface && (*keys.focus.surface->client() == primarySelectionDevice->client()))) { + return; + } + q->setPrimarySelection(primarySelectionDevice->selection()); +} + void SeatInterface::setHasKeyboard(bool has) { Q_D(); @@ -1134,6 +1176,22 @@ void SeatInterface::setFocusedKeyboardSurface(SurfaceInterface *surface) dataDevice->sendClearSelection(); } } + // primary selection + QVector primarySelectionDevices; + for (auto it = d->primarySelectionDevices.constBegin(); it != d->primarySelectionDevices.constEnd(); ++it) { + if ((*it)->client() == *surface->client()) { + primarySelectionDevices << *it; + } + } + + d->keys.focus.primarySelections = primarySelectionDevices; + for (auto primaryDataDevice : primarySelectionDevices) { + if (d->currentSelection) { + primaryDataDevice->sendSelection(d->currentPrimarySelection); + } else { + primaryDataDevice->sendClearSelection(); + } + } } for (auto it = d->keys.focus.keyboards.constBegin(), end = d->keys.focus.keyboards.constEnd(); it != end; ++it) { (*it)->setFocusedSurface(surface, serial); @@ -1632,4 +1690,36 @@ void SeatInterface::setSelection(AbstractDataSource *selection) emit selectionChanged(selection); } +void SeatInterface::setPrimarySelection(AbstractDataSource *selection) +{ + Q_D(); + if (d->currentPrimarySelection == selection) { + return; + } + + if (d->currentPrimarySelection) { + d->currentPrimarySelection->cancel(); + disconnect(d->currentPrimarySelection, nullptr, this, nullptr); + } + + if (selection) { + auto cleanup = [this]() { + setPrimarySelection(nullptr); + }; + connect(selection, &DataSourceInterface::unbound, this, cleanup); + } + + d->currentPrimarySelection = selection; + + for (auto focussedSelection: qAsConst(d->keys.focus.primarySelections)) { + if (selection) { + focussedSelection->sendSelection(selection); + } else { + focussedSelection->sendClearSelection(); + } + } + + emit primarySelectionChanged(selection); +} + } diff --git a/src/wayland/seat_interface.h b/src/wayland/seat_interface.h index 1fe2f38d52..6f0afde673 100644 --- a/src/wayland/seat_interface.h +++ b/src/wayland/seat_interface.h @@ -712,6 +712,8 @@ public: **/ void setSelection(AbstractDataSource *selection); + void setPrimarySelection(AbstractDataSource *selection); + static SeatInterface *get(wl_resource *native); Q_SIGNALS: @@ -741,6 +743,12 @@ Q_SIGNALS: **/ void selectionChanged(KWaylandServer::AbstractDataSource*); + /** + * Emitted whenever the primary selection changes + * @see primarySelection + **/ + void primarySelectionChanged(KWaylandServer::AbstractDataSource*); + /** * Emitted when a drag'n'drop operation is started * @since 5.6 @@ -769,6 +777,7 @@ Q_SIGNALS: private: friend class Display; friend class DataControlDeviceV1Interface; + friend class PrimarySelectionDeviceV1Interface; friend class DataDeviceManagerInterface; friend class TextInputManagerUnstableV0Interface; friend class TextInputManagerUnstableV2Interface; diff --git a/src/wayland/seat_interface_p.h b/src/wayland/seat_interface_p.h index b632f4af76..3d4653a23b 100644 --- a/src/wayland/seat_interface_p.h +++ b/src/wayland/seat_interface_p.h @@ -24,6 +24,7 @@ class DataDeviceInterface; class DataSourceInterface; class DataControlDeviceV1Interface; class TextInputInterface; +class PrimarySelectionDeviceV1Interface; class SeatInterface::Private : public Global::Private { @@ -37,6 +38,7 @@ public: QVector touchsForSurface(SurfaceInterface *surface) const; QVector dataDevicesForSurface(SurfaceInterface *surface) const; TextInputInterface *textInputForSurface(SurfaceInterface *surface) const; + void registerPrimarySelectionDevice(PrimarySelectionDeviceV1Interface *primarySelectionDevice); void registerDataDevice(DataDeviceInterface *dataDevice); void registerDataControlDevice(DataControlDeviceV1Interface *dataDevice); void registerTextInput(TextInputInterface *textInput); @@ -52,12 +54,14 @@ public: QVector keyboards; QVector touchs; QVector dataDevices; + QVector primarySelectionDevices; QVector dataControlDevices; QVector textInputs; // the last thing copied into the clipboard content AbstractDataSource *currentSelection = nullptr; + AbstractDataSource *currentPrimarySelection = nullptr; // Pointer related members struct Pointer { @@ -109,6 +113,7 @@ public: QMetaObject::Connection destroyConnection; quint32 serial = 0; QVector selections; + QVector primarySelections; }; Focus focus; quint32 lastStateSerial = 0; @@ -173,6 +178,7 @@ private: void getKeyboard(wl_client *client, wl_resource *resource, uint32_t id); void getTouch(wl_client *client, wl_resource *resource, uint32_t id); void updateSelection(DataDeviceInterface *dataDevice); + void updatePrimarySelection(PrimarySelectionDeviceV1Interface *primarySelectionDevice); static Private *cast(wl_resource *r); static void unbind(wl_resource *r);