aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorVaxry <[email protected]>2024-05-12 15:55:46 +0100
committerVaxry <[email protected]>2024-05-14 22:05:49 +0100
commit8c5f2a3de72d35403c9bf0334e0c3ff1641a7f0b (patch)
treeec5d623164f2f2b3e3ccb2ce3f71064babc3f4c1
parent7e7b7bddb2d05b57fe64f334a8a20df33bccb456 (diff)
downloadHyprland-8c5f2a3de72d35403c9bf0334e0c3ff1641a7f0b.tar.gz
Hyprland-8c5f2a3de72d35403c9bf0334e0c3ff1641a7f0b.zip
wlr-data-device: move to hyprland impl
-rw-r--r--CMakeLists.txt1
-rw-r--r--protocols/meson.build1
-rw-r--r--protocols/wlr-data-control-unstable-v1.xml278
-rw-r--r--src/managers/ProtocolManager.cpp2
-rw-r--r--src/managers/SeatManager.cpp2
-rw-r--r--src/protocols/DataDeviceWlr.cpp310
-rw-r--r--src/protocols/DataDeviceWlr.hpp121
-rw-r--r--src/protocols/core/DataDevice.cpp2
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());
});