diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | protocols/meson.build | 1 | ||||
-rw-r--r-- | protocols/wlr-data-control-unstable-v1.xml | 278 | ||||
-rw-r--r-- | src/managers/ProtocolManager.cpp | 2 | ||||
-rw-r--r-- | src/managers/SeatManager.cpp | 2 | ||||
-rw-r--r-- | src/protocols/DataDeviceWlr.cpp | 310 | ||||
-rw-r--r-- | src/protocols/DataDeviceWlr.hpp | 121 | ||||
-rw-r--r-- | src/protocols/core/DataDevice.cpp | 2 |
8 files changed, 717 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 80483c4f..3cdae161 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -271,6 +271,7 @@ protocolNew("protocols/wlr-virtual-pointer-unstable-v1.xml" "wlr-virtual-pointer protocolNew("protocols/input-method-unstable-v2.xml" "input-method-unstable-v2" true) protocolNew("protocols/wlr-output-management-unstable-v1.xml" "wlr-output-management-unstable-v1" true) protocolNew("protocols/kde-server-decoration.xml" "kde-server-decoration" true) +protocolNew("protocols/wlr-data-control-unstable-v1.xml" "wlr-data-control-unstable-v1" true) protocolNew("subprojects/hyprland-protocols/protocols/hyprland-focus-grab-v1.xml" "hyprland-focus-grab-v1" true) protocolNew("protocols/wlr-layer-shell-unstable-v1.xml" "wlr-layer-shell-unstable-v1" true) protocolNew("staging/tearing-control/tearing-control-v1.xml" "tearing-control-v1" false) diff --git a/protocols/meson.build b/protocols/meson.build index 4b7aa200..adedbf8e 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -42,6 +42,7 @@ new_protocols = [ ['wlr-output-management-unstable-v1.xml'], ['kde-server-decoration.xml'], ['wlr-layer-shell-unstable-v1.xml'], + ['wlr-data-control-unstable-v1.xml'], [hl_protocol_dir, 'protocols/hyprland-focus-grab-v1.xml'], [wl_protocol_dir, 'staging/tearing-control/tearing-control-v1.xml'], [wl_protocol_dir, 'staging/fractional-scale/fractional-scale-v1.xml'], diff --git a/protocols/wlr-data-control-unstable-v1.xml b/protocols/wlr-data-control-unstable-v1.xml new file mode 100644 index 00000000..75e8671b --- /dev/null +++ b/protocols/wlr-data-control-unstable-v1.xml @@ -0,0 +1,278 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="wlr_data_control_unstable_v1"> + <copyright> + Copyright © 2018 Simon Ser + Copyright © 2019 Ivan Molodetskikh + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + </copyright> + + <description summary="control data devices"> + This protocol allows a privileged client to control data devices. In + particular, the client will be able to manage the current selection and take + the role of a clipboard manager. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + </description> + + <interface name="zwlr_data_control_manager_v1" version="2"> + <description summary="manager to control data devices"> + This interface is a manager that allows creating per-seat data device + controls. + </description> + + <request name="create_data_source"> + <description summary="create a new data source"> + Create a new data source. + </description> + <arg name="id" type="new_id" interface="zwlr_data_control_source_v1" + summary="data source to create"/> + </request> + + <request name="get_data_device"> + <description summary="get a data device for a seat"> + Create a data device that can be used to manage a seat's selection. + </description> + <arg name="id" type="new_id" interface="zwlr_data_control_device_v1"/> + <arg name="seat" type="object" interface="wl_seat"/> + </request> + + <request name="destroy" type="destructor"> + <description summary="destroy the manager"> + All objects created by the manager will still remain valid, until their + appropriate destroy request has been called. + </description> + </request> + </interface> + + <interface name="zwlr_data_control_device_v1" version="2"> + <description summary="manage a data device for a seat"> + This interface allows a client to manage a seat's selection. + + When the seat is destroyed, this object becomes inert. + </description> + + <request name="set_selection"> + <description summary="copy data to the selection"> + This request asks the compositor to set the selection to the data from + the source on behalf of the client. + + The given source may not be used in any further set_selection or + set_primary_selection requests. Attempting to use a previously used + source is a protocol error. + + To unset the selection, set the source to NULL. + </description> + <arg name="source" type="object" interface="zwlr_data_control_source_v1" + allow-null="true"/> + </request> + + <request name="destroy" type="destructor"> + <description summary="destroy this data device"> + Destroys the data device object. + </description> + </request> + + <event name="data_offer"> + <description summary="introduce a new wlr_data_control_offer"> + The data_offer event introduces a new wlr_data_control_offer object, + which will subsequently be used in either the + wlr_data_control_device.selection event (for the regular clipboard + selections) or the wlr_data_control_device.primary_selection event (for + the primary clipboard selections). Immediately following the + wlr_data_control_device.data_offer event, the new data_offer object + will send out wlr_data_control_offer.offer events to describe the MIME + types it offers. + </description> + <arg name="id" type="new_id" interface="zwlr_data_control_offer_v1"/> + </event> + + <event name="selection"> + <description summary="advertise new selection"> + The selection event is sent out to notify the client of a new + wlr_data_control_offer for the selection for this device. The + wlr_data_control_device.data_offer and the wlr_data_control_offer.offer + events are sent out immediately before this event to introduce the data + offer object. The selection event is sent to a client when a new + selection is set. The wlr_data_control_offer is valid until a new + wlr_data_control_offer or NULL is received. The client must destroy the + previous selection wlr_data_control_offer, if any, upon receiving this + event. + + The first selection event is sent upon binding the + wlr_data_control_device object. + </description> + <arg name="id" type="object" interface="zwlr_data_control_offer_v1" + allow-null="true"/> + </event> + + <event name="finished"> + <description summary="this data control is no longer valid"> + This data control object is no longer valid and should be destroyed by + the client. + </description> + </event> + + <!-- Version 2 additions --> + + <event name="primary_selection" since="2"> + <description summary="advertise new primary selection"> + The primary_selection event is sent out to notify the client of a new + wlr_data_control_offer for the primary selection for this device. The + wlr_data_control_device.data_offer and the wlr_data_control_offer.offer + events are sent out immediately before this event to introduce the data + offer object. The primary_selection event is sent to a client when a + new primary selection is set. The wlr_data_control_offer is valid until + a new wlr_data_control_offer or NULL is received. The client must + destroy the previous primary selection wlr_data_control_offer, if any, + upon receiving this event. + + If the compositor supports primary selection, the first + primary_selection event is sent upon binding the + wlr_data_control_device object. + </description> + <arg name="id" type="object" interface="zwlr_data_control_offer_v1" + allow-null="true"/> + </event> + + <request name="set_primary_selection" since="2"> + <description summary="copy data to the primary selection"> + This request asks the compositor to set the primary selection to the + data from the source on behalf of the client. + + The given source may not be used in any further set_selection or + set_primary_selection requests. Attempting to use a previously used + source is a protocol error. + + To unset the primary selection, set the source to NULL. + + The compositor will ignore this request if it does not support primary + selection. + </description> + <arg name="source" type="object" interface="zwlr_data_control_source_v1" + allow-null="true"/> + </request> + + <enum name="error" since="2"> + <entry name="used_source" value="1" + summary="source given to set_selection or set_primary_selection was already used before"/> + </enum> + </interface> + + <interface name="zwlr_data_control_source_v1" version="1"> + <description summary="offer to transfer data"> + The wlr_data_control_source object is the source side of a + wlr_data_control_offer. It is created by the source client in a data + transfer and provides a way to describe the offered data and a way to + respond to requests to transfer the data. + </description> + + <enum name="error"> + <entry name="invalid_offer" value="1" + summary="offer sent after wlr_data_control_device.set_selection"/> + </enum> + + <request name="offer"> + <description summary="add an offered MIME type"> + This request adds a MIME type to the set of MIME types advertised to + targets. Can be called several times to offer multiple types. + + Calling this after wlr_data_control_device.set_selection is a protocol + error. + </description> + <arg name="mime_type" type="string" + summary="MIME type offered by the data source"/> + </request> + + <request name="destroy" type="destructor"> + <description summary="destroy this source"> + Destroys the data source object. + </description> + </request> + + <event name="send"> + <description summary="send the data"> + Request for data from the client. Send the data as the specified MIME + type over the passed file descriptor, then close it. + </description> + <arg name="mime_type" type="string" summary="MIME type for the data"/> + <arg name="fd" type="fd" summary="file descriptor for the data"/> + </event> + + <event name="cancelled"> + <description summary="selection was cancelled"> + This data source is no longer valid. The data source has been replaced + by another data source. + + The client should clean up and destroy this data source. + </description> + </event> + </interface> + + <interface name="zwlr_data_control_offer_v1" version="1"> + <description summary="offer to transfer data"> + A wlr_data_control_offer represents a piece of data offered for transfer + by another client (the source client). The offer describes the different + MIME types that the data can be converted to and provides the mechanism + for transferring the data directly from the source client. + </description> + + <request name="receive"> + <description summary="request that the data is transferred"> + To transfer the offered data, the client issues this request and + indicates the MIME type it wants to receive. The transfer happens + through the passed file descriptor (typically created with the pipe + system call). The source client writes the data in the MIME type + representation requested and then closes the file descriptor. + + The receiving client reads from the read end of the pipe until EOF and + then closes its end, at which point the transfer is complete. + + This request may happen multiple times for different MIME types. + </description> + <arg name="mime_type" type="string" + summary="MIME type desired by receiver"/> + <arg name="fd" type="fd" summary="file descriptor for data transfer"/> + </request> + + <request name="destroy" type="destructor"> + <description summary="destroy this offer"> + Destroys the data offer object. + </description> + </request> + + <event name="offer"> + <description summary="advertise offered MIME type"> + Sent immediately after creating the wlr_data_control_offer object. + One event per offered MIME type. + </description> + <arg name="mime_type" type="string" summary="offered MIME type"/> + </event> + </interface> +</protocol> diff --git a/src/managers/ProtocolManager.cpp b/src/managers/ProtocolManager.cpp index 8167103f..4b03263b 100644 --- a/src/managers/ProtocolManager.cpp +++ b/src/managers/ProtocolManager.cpp @@ -29,6 +29,7 @@ #include "../protocols/LayerShell.hpp" #include "../protocols/PresentationTime.hpp" #include "../protocols/XDGShell.hpp" +#include "../protocols/DataDeviceWlr.hpp" #include "../protocols/core/Seat.hpp" #include "../protocols/core/DataDevice.hpp" @@ -69,6 +70,7 @@ CProtocolManager::CProtocolManager() { PROTO::layerShell = std::make_unique<CLayerShellProtocol>(&zwlr_layer_shell_v1_interface, 5, "LayerShell"); PROTO::presentation = std::make_unique<CPresentationProtocol>(&wp_presentation_interface, 1, "Presentation"); PROTO::xdgShell = std::make_unique<CXDGShellProtocol>(&xdg_wm_base_interface, 6, "XDGShell"); + PROTO::dataWlr = std::make_unique<CDataDeviceWLRProtocol>(&zwlr_data_control_manager_v1_interface, 2, "DataDeviceWlr"); // Old protocol implementations. // TODO: rewrite them to use hyprwayland-scanner. diff --git a/src/managers/SeatManager.cpp b/src/managers/SeatManager.cpp index 76840bd3..a8505610 100644 --- a/src/managers/SeatManager.cpp +++ b/src/managers/SeatManager.cpp @@ -1,6 +1,7 @@ #include "SeatManager.hpp" #include "../protocols/core/Seat.hpp" #include "../protocols/core/DataDevice.hpp" +#include "../protocols/DataDeviceWlr.hpp" #include "../Compositor.hpp" #include "../devices/IKeyboard.hpp" #include <algorithm> @@ -446,6 +447,7 @@ void CSeatManager::setCurrentSelection(SP<IDataSource> source) { if (source) { selection.destroySelection = source->events.destroy.registerListener([this](std::any d) { setCurrentSelection(nullptr); }); PROTO::data->setSelection(source); + PROTO::dataWlr->setSelection(source); } } diff --git a/src/protocols/DataDeviceWlr.cpp b/src/protocols/DataDeviceWlr.cpp new file mode 100644 index 00000000..a518b0ae --- /dev/null +++ b/src/protocols/DataDeviceWlr.cpp @@ -0,0 +1,310 @@ +#include "DataDeviceWlr.hpp" +#include <algorithm> +#include "../managers/SeatManager.hpp" +#include "core/Seat.hpp" + +#define LOGM PROTO::dataWlr->protoLog + +CWLRDataOffer::CWLRDataOffer(SP<CZwlrDataControlOfferV1> resource_, SP<IDataSource> source_) : source(source_), resource(resource_) { + if (!good()) + return; + + resource->setDestroy([this](CZwlrDataControlOfferV1* r) { PROTO::dataWlr->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrDataControlOfferV1* r) { PROTO::dataWlr->destroyResource(this); }); + + resource->setReceive([this](CZwlrDataControlOfferV1* r, const char* mime, int32_t fd) { + if (!source) { + LOGM(WARN, "Possible bug: Receive on an offer w/o a source"); + close(fd); + return; + } + + if (dead) { + LOGM(WARN, "Possible bug: Receive on an offer that's dead"); + close(fd); + return; + } + + LOGM(LOG, "Offer {:x} asks to send data from source {:x}", (uintptr_t)this, (uintptr_t)source.get()); + + source->send(mime, fd); + }); +} + +bool CWLRDataOffer::good() { + return resource->resource(); +} + +void CWLRDataOffer::sendData() { + if (!source) + return; + + for (auto& m : source->mimes()) { + resource->sendOffer(m.c_str()); + } +} + +CWLRDataSource::CWLRDataSource(SP<CZwlrDataControlSourceV1> resource_, SP<CWLRDataDevice> device_) : device(device_), resource(resource_) { + if (!good()) + return; + + resource->setData(this); + + resource->setDestroy([this](CZwlrDataControlSourceV1* r) { + events.destroy.emit(); + PROTO::dataWlr->destroyResource(this); + }); + resource->setOnDestroy([this](CZwlrDataControlSourceV1* r) { + events.destroy.emit(); + PROTO::dataWlr->destroyResource(this); + }); + + resource->setOffer([this](CZwlrDataControlSourceV1* r, const char* mime) { mimeTypes.push_back(mime); }); +} + +CWLRDataSource::~CWLRDataSource() { + events.destroy.emit(); +} + +SP<CWLRDataSource> CWLRDataSource::fromResource(wl_resource* res) { + auto data = (CWLRDataSource*)(((CZwlrDataControlSourceV1*)wl_resource_get_user_data(res))->data()); + return data ? data->self.lock() : nullptr; +} + +bool CWLRDataSource::good() { + return resource->resource(); +} + +std::vector<std::string> CWLRDataSource::mimes() { + return mimeTypes; +} + +void CWLRDataSource::send(const std::string& mime, uint32_t fd) { + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) { + LOGM(ERR, "Compositor/App bug: CWLRDataSource::sendAskSend with non-existent mime"); + close(fd); + return; + } + + resource->sendSend(mime.c_str(), fd); + close(fd); +} + +void CWLRDataSource::accepted(const std::string& mime) { + if (std::find(mimeTypes.begin(), mimeTypes.end(), mime) == mimeTypes.end()) + LOGM(ERR, "Compositor/App bug: CWLRDataSource::sendAccepted with non-existent mime"); + + // wlr has no accepted +} + +void CWLRDataSource::cancelled() { + resource->sendCancelled(); +} + +void CWLRDataSource::error(uint32_t code, const std::string& msg) { + resource->error(code, msg); +} + +CWLRDataDevice::CWLRDataDevice(SP<CZwlrDataControlDeviceV1> resource_) : resource(resource_) { + if (!good()) + return; + + pClient = resource->client(); + + resource->setDestroy([this](CZwlrDataControlDeviceV1* r) { PROTO::dataWlr->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrDataControlDeviceV1* r) { PROTO::dataWlr->destroyResource(this); }); + + resource->setSetSelection([this](CZwlrDataControlDeviceV1* r, wl_resource* sourceR) { + auto source = sourceR ? CWLRDataSource::fromResource(sourceR) : CSharedPointer<CWLRDataSource>{}; + if (!source) { + LOGM(LOG, "wlr reset selection received"); + g_pSeatManager->setCurrentSelection(nullptr); + return; + } + + if (source && source->used()) + LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); + + source->markUsed(); + + LOGM(LOG, "wlr manager requests selection to {:x}", (uintptr_t)source.get()); + g_pSeatManager->setCurrentSelection(source); + }); + + resource->setSetPrimarySelection([this](CZwlrDataControlDeviceV1* r, wl_resource* sourceR) { + auto source = sourceR ? CWLRDataSource::fromResource(sourceR) : CSharedPointer<CWLRDataSource>{}; + if (!source) { + LOGM(LOG, "wlr reset primary selection received"); + g_pSeatManager->setCurrentSelection(nullptr); + return; + } + + if (source && source->used()) + LOGM(WARN, "setSelection on a used resource. By protocol, this is a violation, but firefox et al insist on doing this."); + + source->markUsed(); + + LOGM(LOG, "wlr manager requests primary selection to {:x}", (uintptr_t)source.get()); + g_pSeatManager->setCurrentSelection(source); + }); +} + +bool CWLRDataDevice::good() { + return resource->resource(); +} + +wl_client* CWLRDataDevice::client() { + return pClient; +} + +void CWLRDataDevice::sendInitialSelections() { + PROTO::dataWlr->sendSelectionToDevice(self.lock(), g_pSeatManager->selection.currentSelection.lock(), false); + PROTO::dataWlr->sendSelectionToDevice(self.lock(), g_pSeatManager->selection.currentPrimarySelection.lock(), true); +} + +void CWLRDataDevice::sendDataOffer(SP<CWLRDataOffer> offer) { + resource->sendDataOffer(offer->resource.get()); +} + +void CWLRDataDevice::sendSelection(SP<CWLRDataOffer> selection) { + resource->sendSelection(selection->resource.get()); +} + +CWLRDataControlManagerResource::CWLRDataControlManagerResource(SP<CZwlrDataControlManagerV1> resource_) : resource(resource_) { + if (!good()) + return; + + resource->setDestroy([this](CZwlrDataControlManagerV1* r) { PROTO::dataWlr->destroyResource(this); }); + resource->setOnDestroy([this](CZwlrDataControlManagerV1* r) { PROTO::dataWlr->destroyResource(this); }); + + resource->setGetDataDevice([this](CZwlrDataControlManagerV1* r, uint32_t id, wl_resource* seat) { + const auto RESOURCE = PROTO::dataWlr->m_vDevices.emplace_back(makeShared<CWLRDataDevice>(makeShared<CZwlrDataControlDeviceV1>(r->client(), r->version(), id))); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::dataWlr->m_vDevices.pop_back(); + return; + } + + RESOURCE->self = RESOURCE; + device = RESOURCE; + + for (auto& s : sources) { + if (!s) + continue; + s->device = RESOURCE; + } + + RESOURCE->sendInitialSelections(); + + LOGM(LOG, "New wlr data device bound at {:x}", (uintptr_t)RESOURCE.get()); + }); + + resource->setCreateDataSource([this](CZwlrDataControlManagerV1* r, uint32_t id) { + std::erase_if(sources, [](const auto& e) { return e.expired(); }); + + const auto RESOURCE = + PROTO::dataWlr->m_vSources.emplace_back(makeShared<CWLRDataSource>(makeShared<CZwlrDataControlSourceV1>(r->client(), r->version(), id), device.lock())); + + if (!RESOURCE->good()) { + r->noMemory(); + PROTO::dataWlr->m_vSources.pop_back(); + return; + } + + if (!device) + LOGM(WARN, "New data source before a device was created"); + + RESOURCE->self = RESOURCE; + + sources.push_back(RESOURCE); + + LOGM(LOG, "New wlr data source bound at {:x}", (uintptr_t)RESOURCE.get()); + }); +} + +bool CWLRDataControlManagerResource::good() { + return resource->resource(); +} + +CDataDeviceWLRProtocol::CDataDeviceWLRProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) { + ; +} + +void CDataDeviceWLRProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) { + const auto RESOURCE = m_vManagers.emplace_back(makeShared<CWLRDataControlManagerResource>(makeShared<CZwlrDataControlManagerV1>(client, ver, id))); + + if (!RESOURCE->good()) { + wl_client_post_no_memory(client); + m_vManagers.pop_back(); + return; + } + + LOGM(LOG, "New wlr_data_control_manager at {:x}", (uintptr_t)RESOURCE.get()); +} + +void CDataDeviceWLRProtocol::destroyResource(CWLRDataControlManagerResource* resource) { + std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; }); +} + +void CDataDeviceWLRProtocol::destroyResource(CWLRDataSource* resource) { + std::erase_if(m_vSources, [&](const auto& other) { return other.get() == resource; }); +} + +void CDataDeviceWLRProtocol::destroyResource(CWLRDataDevice* resource) { + std::erase_if(m_vDevices, [&](const auto& other) { return other.get() == resource; }); +} + +void CDataDeviceWLRProtocol::destroyResource(CWLRDataOffer* resource) { + std::erase_if(m_vOffers, [&](const auto& other) { return other.get() == resource; }); +} + +void CDataDeviceWLRProtocol::sendSelectionToDevice(SP<CWLRDataDevice> dev, SP<IDataSource> sel) { + if (!sel) + return; + + const auto OFFER = m_vOffers.emplace_back(makeShared<CWLRDataOffer>(makeShared<CZwlrDataControlOfferV1>(dev->resource->client(), dev->resource->version(), 0), sel)); + + if (!OFFER->good()) { + dev->resource->noMemory(); + m_vOffers.pop_back(); + return; + } + + LOGM(LOG, "New offer {:x} for data source {:x}", (uintptr_t)OFFER.get(), (uintptr_t)sel.get()); + + dev->sendDataOffer(OFFER); + OFFER->sendData(); + dev->sendSelection(OFFER); +} + +void CDataDeviceWLRProtocol::setSelection(SP<IDataSource> source) { + for (auto& o : m_vOffers) { + if (o->source && o->source->hasDnd()) + continue; + o->dead = true; + } + + if (!source) { + LOGM(LOG, "resetting selection"); + + for (auto& d : m_vDevices) { + sendSelectionToDevice(d, nullptr); + } + + return; + } + + LOGM(LOG, "New selection for data source {:x}", (uintptr_t)source.get()); + + for (auto& d : m_vDevices) { + sendSelectionToDevice(d, source); + } +} + +SP<CWLRDataDevice> CDataDeviceWLRProtocol::dataDeviceForClient(wl_client* c) { + auto it = std::find_if(m_vDevices.begin(), m_vDevices.end(), [c](const auto& e) { return e->client() == c; }); + if (it == m_vDevices.end()) + return nullptr; + return *it; +} diff --git a/src/protocols/DataDeviceWlr.hpp b/src/protocols/DataDeviceWlr.hpp new file mode 100644 index 00000000..0b703347 --- /dev/null +++ b/src/protocols/DataDeviceWlr.hpp @@ -0,0 +1,121 @@ +#pragma once + +#include <memory> +#include <vector> +#include <cstdint> +#include "WaylandProtocol.hpp" +#include "wlr-data-control-unstable-v1.hpp" +#include "types/DataDevice.hpp" + +class CWLRDataControlManagerResource; +class CWLRDataSource; +class CWLRDataDevice; +class CWLRDataOffer; + +class CWLRDataOffer { + public: + CWLRDataOffer(SP<CZwlrDataControlOfferV1> resource_, SP<IDataSource> source); + + bool good(); + void sendData(); + + bool dead = false; + + WP<IDataSource> source; + + private: + SP<CZwlrDataControlOfferV1> resource; + + friend class CWLRDataDevice; +}; + +class CWLRDataSource : public IDataSource { + public: + CWLRDataSource(SP<CZwlrDataControlSourceV1> resource_, SP<CWLRDataDevice> device_); + ~CWLRDataSource(); + static SP<CWLRDataSource> fromResource(wl_resource*); + + bool good(); + + virtual std::vector<std::string> mimes(); + virtual void send(const std::string& mime, uint32_t fd); + virtual void accepted(const std::string& mime); + virtual void cancelled(); + virtual void error(uint32_t code, const std::string& msg); + + std::vector<std::string> mimeTypes; + WP<CWLRDataSource> self; + WP<CWLRDataDevice> device; + + private: + SP<CZwlrDataControlSourceV1> resource; +}; + +class CWLRDataDevice { + public: + CWLRDataDevice(SP<CZwlrDataControlDeviceV1> resource_); + + bool good(); + wl_client* client(); + void sendInitialSelections(); + + void sendDataOffer(SP<CWLRDataOffer> offer); + void sendSelection(SP<CWLRDataOffer> selection); + + WP<CWLRDataDevice> self; + + private: + SP<CZwlrDataControlDeviceV1> resource; + wl_client* pClient = nullptr; + + friend class CDataDeviceWLRProtocol; +}; + +class CWLRDataControlManagerResource { + public: + CWLRDataControlManagerResource(SP<CZwlrDataControlManagerV1> resource_); + + bool good(); + + WP<CWLRDataDevice> device; + std::vector<WP<CWLRDataSource>> sources; + + private: + SP<CZwlrDataControlManagerV1> resource; +}; + +class CDataDeviceWLRProtocol : public IWaylandProtocol { + public: + CDataDeviceWLRProtocol(const wl_interface* iface, const int& ver, const std::string& name); + + virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id); + + private: + void destroyResource(CWLRDataControlManagerResource* resource); + void destroyResource(CWLRDataSource* resource); + void destroyResource(CWLRDataDevice* resource); + void destroyResource(CWLRDataOffer* resource); + + // + std::vector<SP<CWLRDataControlManagerResource>> m_vManagers; + std::vector<SP<CWLRDataSource>> m_vSources; + std::vector<SP<CWLRDataDevice>> m_vDevices; + std::vector<SP<CWLRDataOffer>> m_vOffers; + + // + void setSelection(SP<IDataSource> source); + void sendSelectionToDevice(SP<CWLRDataDevice> dev, SP<IDataSource> sel); + + // + SP<CWLRDataDevice> dataDeviceForClient(wl_client*); + + friend class CSeatManager; + friend class CWLRDataControlManagerResource; + friend class CWLRDataSource; + friend class CWLRDataDevice; + friend class CWLRDataOffer; +}; + +namespace PROTO { + inline UP<CDataDeviceWLRProtocol> dataWlr; +}; diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp index d332a0be..2b9503b1 100644 --- a/src/protocols/core/DataDevice.cpp +++ b/src/protocols/core/DataDevice.cpp @@ -297,6 +297,8 @@ CWLDataDeviceManagerResource::CWLDataDeviceManagerResource(SP<CWlDataDeviceManag RESOURCE->self = RESOURCE; + sources.push_back(RESOURCE); + LOGM(LOG, "New data source bound at {:x}", (uintptr_t)RESOURCE.get()); }); |