mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-08-17 21:13:48 -07:00
xwayland: Support cross DnD from Wayland (#8708)
Adds support for drag-and-drop from Wayland clients to XWayland ones
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
#include "../../Compositor.hpp"
|
||||
#include "Seat.hpp"
|
||||
#include "Compositor.hpp"
|
||||
#include "../../xwayland/XWayland.hpp"
|
||||
#include "../../xwayland/Server.hpp"
|
||||
|
||||
CWLDataOfferResource::CWLDataOfferResource(SP<CWlDataOffer> resource_, SP<IDataSource> source_) : source(source_), resource(resource_) {
|
||||
if (!good())
|
||||
@@ -103,6 +105,22 @@ void CWLDataOfferResource::sendData() {
|
||||
}
|
||||
}
|
||||
|
||||
eDataSourceType CWLDataOfferResource::type() {
|
||||
return DATA_SOURCE_TYPE_WAYLAND;
|
||||
}
|
||||
|
||||
SP<CWLDataOfferResource> CWLDataOfferResource::getWayland() {
|
||||
return self.lock();
|
||||
}
|
||||
|
||||
SP<CX11DataOffer> CWLDataOfferResource::getX11() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SP<IDataSource> CWLDataOfferResource::getSource() {
|
||||
return source.lock();
|
||||
}
|
||||
|
||||
CWLDataSourceResource::CWLDataSourceResource(SP<CWlDataSource> resource_, SP<CWLDataDeviceResource> device_) : device(device_), resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
@@ -209,6 +227,10 @@ uint32_t CWLDataSourceResource::actions() {
|
||||
return supportedActions;
|
||||
}
|
||||
|
||||
eDataSourceType CWLDataSourceResource::type() {
|
||||
return DATA_SOURCE_TYPE_WAYLAND;
|
||||
}
|
||||
|
||||
CWLDataDeviceResource::CWLDataDeviceResource(SP<CWlDataDevice> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
@@ -260,15 +282,18 @@ wl_client* CWLDataDeviceResource::client() {
|
||||
return pClient;
|
||||
}
|
||||
|
||||
void CWLDataDeviceResource::sendDataOffer(SP<CWLDataOfferResource> offer) {
|
||||
if (offer)
|
||||
resource->sendDataOffer(offer->resource.get());
|
||||
else
|
||||
void CWLDataDeviceResource::sendDataOffer(SP<IDataOffer> offer) {
|
||||
if (!offer)
|
||||
resource->sendDataOfferRaw(nullptr);
|
||||
else if (const auto WL = offer->getWayland(); WL)
|
||||
resource->sendDataOffer(WL->resource.get());
|
||||
//FIXME: X11
|
||||
}
|
||||
|
||||
void CWLDataDeviceResource::sendEnter(uint32_t serial, SP<CWLSurfaceResource> surf, const Vector2D& local, SP<CWLDataOfferResource> offer) {
|
||||
resource->sendEnterRaw(serial, surf->getResource()->resource(), wl_fixed_from_double(local.x), wl_fixed_from_double(local.y), offer->resource->resource());
|
||||
void CWLDataDeviceResource::sendEnter(uint32_t serial, SP<CWLSurfaceResource> surf, const Vector2D& local, SP<IDataOffer> offer) {
|
||||
if (const auto WL = offer->getWayland(); WL)
|
||||
resource->sendEnterRaw(serial, surf->getResource()->resource(), wl_fixed_from_double(local.x), wl_fixed_from_double(local.y), WL->resource->resource());
|
||||
// FIXME: X11
|
||||
}
|
||||
|
||||
void CWLDataDeviceResource::sendLeave() {
|
||||
@@ -283,11 +308,23 @@ void CWLDataDeviceResource::sendDrop() {
|
||||
resource->sendDrop();
|
||||
}
|
||||
|
||||
void CWLDataDeviceResource::sendSelection(SP<CWLDataOfferResource> offer) {
|
||||
void CWLDataDeviceResource::sendSelection(SP<IDataOffer> offer) {
|
||||
if (!offer)
|
||||
resource->sendSelectionRaw(nullptr);
|
||||
else
|
||||
resource->sendSelection(offer->resource.get());
|
||||
else if (const auto WL = offer->getWayland(); WL)
|
||||
resource->sendSelection(WL->resource.get());
|
||||
}
|
||||
|
||||
eDataSourceType CWLDataDeviceResource::type() {
|
||||
return DATA_SOURCE_TYPE_WAYLAND;
|
||||
}
|
||||
|
||||
SP<CWLDataDeviceResource> CWLDataDeviceResource::getWayland() {
|
||||
return self.lock();
|
||||
}
|
||||
|
||||
SP<CX11DataDevice> CWLDataDeviceResource::getX11() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CWLDataDeviceManagerResource::CWLDataDeviceManagerResource(SP<CWlDataDeviceManager> resource_) : resource(resource_) {
|
||||
@@ -377,32 +414,53 @@ void CWLDataDeviceProtocol::destroyResource(CWLDataOfferResource* resource) {
|
||||
std::erase_if(m_vOffers, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
|
||||
SP<CWLDataDeviceResource> CWLDataDeviceProtocol::dataDeviceForClient(wl_client* c) {
|
||||
SP<IDataDevice> CWLDataDeviceProtocol::dataDeviceForClient(wl_client* c) {
|
||||
#ifndef NO_XWAYLAND
|
||||
if (c == g_pXWayland->pServer->xwaylandClient)
|
||||
return g_pXWayland->pWM->getDataDevice();
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void CWLDataDeviceProtocol::sendSelectionToDevice(SP<CWLDataDeviceResource> dev, SP<IDataSource> sel) {
|
||||
void CWLDataDeviceProtocol::sendSelectionToDevice(SP<IDataDevice> dev, SP<IDataSource> sel) {
|
||||
if (!sel) {
|
||||
dev->sendSelection(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto OFFER = m_vOffers.emplace_back(makeShared<CWLDataOfferResource>(makeShared<CWlDataOffer>(dev->resource->client(), dev->resource->version(), 0), sel));
|
||||
SP<IDataOffer> offer;
|
||||
|
||||
if (!OFFER->good()) {
|
||||
dev->resource->noMemory();
|
||||
m_vOffers.pop_back();
|
||||
if (const auto WL = dev->getWayland(); WL) {
|
||||
const auto OFFER = m_vOffers.emplace_back(makeShared<CWLDataOfferResource>(makeShared<CWlDataOffer>(WL->resource->client(), WL->resource->version(), 0), sel));
|
||||
if (!OFFER->good()) {
|
||||
WL->resource->noMemory();
|
||||
m_vOffers.pop_back();
|
||||
return;
|
||||
}
|
||||
OFFER->source = sel;
|
||||
OFFER->self = OFFER;
|
||||
offer = OFFER;
|
||||
}
|
||||
#ifndef NO_XWAYLAND
|
||||
else if (const auto X11 = dev->getX11(); X11)
|
||||
offer = g_pXWayland->pWM->createX11DataOffer(g_pSeatManager->state.keyboardFocus.lock(), sel);
|
||||
#endif
|
||||
|
||||
if (!offer) {
|
||||
LOGM(ERR, "No offer could be created in sendSelectionToDevice");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGM(LOG, "New offer {:x} for data source {:x}", (uintptr_t)OFFER.get(), (uintptr_t)sel.get());
|
||||
LOGM(LOG, "New {} offer {:x} for data source {:x}", offer->type() == DATA_SOURCE_TYPE_WAYLAND ? "wayland" : "X11", (uintptr_t)offer.get(), (uintptr_t)sel.get());
|
||||
|
||||
dev->sendDataOffer(OFFER);
|
||||
OFFER->sendData();
|
||||
dev->sendSelection(OFFER);
|
||||
dev->sendDataOffer(offer);
|
||||
if (const auto WL = offer->getWayland(); WL)
|
||||
WL->sendData();
|
||||
dev->sendSelection(offer);
|
||||
}
|
||||
|
||||
void CWLDataDeviceProtocol::onDestroyDataSource(WP<CWLDataSourceResource> source) {
|
||||
@@ -424,7 +482,7 @@ void CWLDataDeviceProtocol::setSelection(SP<IDataSource> source) {
|
||||
return;
|
||||
|
||||
auto DESTDEVICE = dataDeviceForClient(g_pSeatManager->state.keyboardFocusResource->client());
|
||||
if (DESTDEVICE)
|
||||
if (DESTDEVICE && DESTDEVICE->type() == DATA_SOURCE_TYPE_WAYLAND)
|
||||
sendSelectionToDevice(DESTDEVICE, nullptr);
|
||||
|
||||
return;
|
||||
@@ -442,6 +500,11 @@ void CWLDataDeviceProtocol::setSelection(SP<IDataSource> source) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (DESTDEVICE->type() != DATA_SOURCE_TYPE_WAYLAND) {
|
||||
LOGM(LOG, "CWLDataDeviceProtocol::setSelection: ignoring X11 data device");
|
||||
return;
|
||||
}
|
||||
|
||||
sendSelectionToDevice(DESTDEVICE, source);
|
||||
}
|
||||
|
||||
@@ -589,22 +652,38 @@ void CWLDataDeviceProtocol::updateDrag() {
|
||||
if (!dnd.focusedDevice)
|
||||
return;
|
||||
|
||||
// make a new offer
|
||||
const auto OFFER = m_vOffers.emplace_back(
|
||||
makeShared<CWLDataOfferResource>(makeShared<CWlDataOffer>(dnd.focusedDevice->resource->client(), dnd.focusedDevice->resource->version(), 0), dnd.currentSource.lock()));
|
||||
SP<IDataOffer> offer;
|
||||
|
||||
if (!OFFER->good()) {
|
||||
dnd.currentSource->resource->noMemory();
|
||||
m_vOffers.pop_back();
|
||||
if (const auto WL = dnd.focusedDevice->getWayland(); WL) {
|
||||
const auto OFFER =
|
||||
m_vOffers.emplace_back(makeShared<CWLDataOfferResource>(makeShared<CWlDataOffer>(WL->resource->client(), WL->resource->version(), 0), dnd.currentSource.lock()));
|
||||
if (!OFFER->good()) {
|
||||
WL->resource->noMemory();
|
||||
m_vOffers.pop_back();
|
||||
return;
|
||||
}
|
||||
OFFER->source = dnd.currentSource;
|
||||
OFFER->self = OFFER;
|
||||
offer = OFFER;
|
||||
}
|
||||
#ifndef NO_XWAYLAND
|
||||
else if (const auto X11 = dnd.focusedDevice->getX11(); X11)
|
||||
offer = g_pXWayland->pWM->createX11DataOffer(g_pSeatManager->state.keyboardFocus.lock(), dnd.currentSource.lock());
|
||||
#endif
|
||||
|
||||
if (!offer) {
|
||||
LOGM(ERR, "No offer could be created in updateDrag");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGM(LOG, "New dnd offer {:x} for data source {:x}", (uintptr_t)OFFER.get(), (uintptr_t)dnd.currentSource.get());
|
||||
LOGM(LOG, "New {} dnd offer {:x} for data source {:x}", offer->type() == DATA_SOURCE_TYPE_WAYLAND ? "wayland" : "X11", (uintptr_t)offer.get(),
|
||||
(uintptr_t)dnd.currentSource.get());
|
||||
|
||||
dnd.focusedDevice->sendDataOffer(OFFER);
|
||||
OFFER->sendData();
|
||||
dnd.focusedDevice->sendDataOffer(offer);
|
||||
if (const auto WL = offer->getWayland(); WL)
|
||||
WL->sendData();
|
||||
dnd.focusedDevice->sendEnter(wl_display_next_serial(g_pCompositor->m_sWLDisplay), g_pSeatManager->state.dndPointerFocus.lock(),
|
||||
g_pSeatManager->state.dndPointerFocus->current.size / 2.F, OFFER);
|
||||
g_pSeatManager->state.dndPointerFocus->current.size / 2.F, offer);
|
||||
}
|
||||
|
||||
void CWLDataDeviceProtocol::resetDndState() {
|
||||
@@ -651,6 +730,18 @@ bool CWLDataDeviceProtocol::wasDragSuccessful() {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef NO_XWAYLAND
|
||||
for (auto const& o : g_pXWayland->pWM->dndDataOffers) {
|
||||
if (o->dead || !o->source || !o->source->hasDnd())
|
||||
continue;
|
||||
|
||||
if (o->source != dnd.currentSource)
|
||||
continue;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -26,21 +26,27 @@ class CWLDataOfferResource;
|
||||
class CWLSurfaceResource;
|
||||
class CMonitor;
|
||||
|
||||
class CWLDataOfferResource {
|
||||
class CWLDataOfferResource : public IDataOffer {
|
||||
public:
|
||||
CWLDataOfferResource(SP<CWlDataOffer> resource_, SP<IDataSource> source_);
|
||||
~CWLDataOfferResource();
|
||||
|
||||
bool good();
|
||||
void sendData();
|
||||
bool good();
|
||||
void sendData();
|
||||
|
||||
WP<IDataSource> source;
|
||||
virtual eDataSourceType type();
|
||||
virtual SP<CWLDataOfferResource> getWayland();
|
||||
virtual SP<CX11DataOffer> getX11();
|
||||
virtual SP<IDataSource> getSource();
|
||||
|
||||
bool dead = false;
|
||||
bool accepted = false;
|
||||
bool recvd = false;
|
||||
WP<IDataSource> source;
|
||||
WP<CWLDataOfferResource> self;
|
||||
|
||||
uint32_t actions = 0;
|
||||
bool dead = false;
|
||||
bool accepted = false;
|
||||
bool recvd = false;
|
||||
|
||||
uint32_t actions = 0;
|
||||
|
||||
private:
|
||||
SP<CWlDataOffer> resource;
|
||||
@@ -66,9 +72,9 @@ class CWLDataSourceResource : public IDataSource {
|
||||
virtual void error(uint32_t code, const std::string& msg);
|
||||
virtual void sendDndFinished();
|
||||
virtual uint32_t actions(); // wl_data_device_manager.dnd_action
|
||||
|
||||
void sendDndDropPerformed();
|
||||
void sendDndAction(wl_data_device_manager_dnd_action a);
|
||||
virtual eDataSourceType type();
|
||||
virtual void sendDndDropPerformed();
|
||||
virtual void sendDndAction(wl_data_device_manager_dnd_action a);
|
||||
|
||||
bool used = false;
|
||||
bool dnd = false;
|
||||
@@ -88,21 +94,24 @@ class CWLDataSourceResource : public IDataSource {
|
||||
friend class CWLDataDeviceProtocol;
|
||||
};
|
||||
|
||||
class CWLDataDeviceResource {
|
||||
class CWLDataDeviceResource : public IDataDevice {
|
||||
public:
|
||||
CWLDataDeviceResource(SP<CWlDataDevice> resource_);
|
||||
|
||||
bool good();
|
||||
wl_client* client();
|
||||
bool good();
|
||||
wl_client* client();
|
||||
|
||||
void sendDataOffer(SP<CWLDataOfferResource> offer);
|
||||
void sendEnter(uint32_t serial, SP<CWLSurfaceResource> surf, const Vector2D& local, SP<CWLDataOfferResource> offer);
|
||||
void sendLeave();
|
||||
void sendMotion(uint32_t timeMs, const Vector2D& local);
|
||||
void sendDrop();
|
||||
void sendSelection(SP<CWLDataOfferResource> offer);
|
||||
virtual SP<CWLDataDeviceResource> getWayland();
|
||||
virtual SP<CX11DataDevice> getX11();
|
||||
virtual void sendDataOffer(SP<IDataOffer> offer);
|
||||
virtual void sendEnter(uint32_t serial, SP<CWLSurfaceResource> surf, const Vector2D& local, SP<IDataOffer> offer);
|
||||
virtual void sendLeave();
|
||||
virtual void sendMotion(uint32_t timeMs, const Vector2D& local);
|
||||
virtual void sendDrop();
|
||||
virtual void sendSelection(SP<IDataOffer> offer);
|
||||
virtual eDataSourceType type();
|
||||
|
||||
WP<CWLDataDeviceResource> self;
|
||||
WP<CWLDataDeviceResource> self;
|
||||
|
||||
private:
|
||||
SP<CWlDataDevice> resource;
|
||||
@@ -152,19 +161,19 @@ class CWLDataDeviceProtocol : public IWaylandProtocol {
|
||||
|
||||
void onDestroyDataSource(WP<CWLDataSourceResource> source);
|
||||
void setSelection(SP<IDataSource> source);
|
||||
void sendSelectionToDevice(SP<CWLDataDeviceResource> dev, SP<IDataSource> sel);
|
||||
void sendSelectionToDevice(SP<IDataDevice> dev, SP<IDataSource> sel);
|
||||
void updateSelection();
|
||||
void onKeyboardFocus();
|
||||
void onDndPointerFocus();
|
||||
|
||||
struct {
|
||||
WP<CWLDataDeviceResource> focusedDevice;
|
||||
WP<CWLDataSourceResource> currentSource;
|
||||
WP<CWLSurfaceResource> dndSurface;
|
||||
WP<CWLSurfaceResource> originSurface;
|
||||
bool overriddenCursor = false;
|
||||
CHyprSignalListener dndSurfaceDestroy;
|
||||
CHyprSignalListener dndSurfaceCommit;
|
||||
WP<IDataDevice> focusedDevice;
|
||||
WP<IDataSource> currentSource;
|
||||
WP<CWLSurfaceResource> dndSurface;
|
||||
WP<CWLSurfaceResource> originSurface;
|
||||
bool overriddenCursor = false;
|
||||
CHyprSignalListener dndSurfaceDestroy;
|
||||
CHyprSignalListener dndSurfaceCommit;
|
||||
|
||||
// for ending a dnd
|
||||
SP<HOOK_CALLBACK_FN> mouseMove;
|
||||
@@ -182,7 +191,7 @@ class CWLDataDeviceProtocol : public IWaylandProtocol {
|
||||
bool wasDragSuccessful();
|
||||
|
||||
//
|
||||
SP<CWLDataDeviceResource> dataDeviceForClient(wl_client*);
|
||||
SP<IDataDevice> dataDeviceForClient(wl_client*);
|
||||
|
||||
friend class CSeatManager;
|
||||
friend class CWLDataDeviceManagerResource;
|
||||
|
@@ -27,3 +27,15 @@ void IDataSource::sendDndFinished() {
|
||||
uint32_t IDataSource::actions() {
|
||||
return 7; // all
|
||||
}
|
||||
|
||||
void IDataSource::sendDndDropPerformed() {
|
||||
;
|
||||
}
|
||||
|
||||
void IDataSource::sendDndAction(wl_data_device_manager_dnd_action a) {
|
||||
;
|
||||
}
|
||||
|
||||
void IDataOffer::markDead() {
|
||||
;
|
||||
}
|
||||
|
@@ -4,6 +4,15 @@
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include "../../helpers/signal/Signal.hpp"
|
||||
#include <wayland-server-protocol.h>
|
||||
#include "../../helpers/memory/Memory.hpp"
|
||||
#include "../../helpers/math/Math.hpp"
|
||||
|
||||
class CWLDataOfferResource;
|
||||
class CX11DataOffer;
|
||||
class CX11DataDevice;
|
||||
class CWLDataDeviceResource;
|
||||
class CWLSurfaceResource;
|
||||
|
||||
enum eDataSourceType : uint8_t {
|
||||
DATA_SOURCE_TYPE_WAYLAND = 0,
|
||||
@@ -27,6 +36,8 @@ class IDataSource {
|
||||
virtual void error(uint32_t code, const std::string& msg) = 0;
|
||||
virtual eDataSourceType type();
|
||||
virtual uint32_t actions(); // wl_data_device_manager.dnd_action
|
||||
virtual void sendDndDropPerformed();
|
||||
virtual void sendDndAction(wl_data_device_manager_dnd_action a);
|
||||
|
||||
struct {
|
||||
CSignal destroy;
|
||||
@@ -35,3 +46,31 @@ class IDataSource {
|
||||
private:
|
||||
bool wasUsed = false;
|
||||
};
|
||||
|
||||
class IDataOffer {
|
||||
public:
|
||||
IDataOffer() = default;
|
||||
virtual ~IDataOffer() = default;
|
||||
|
||||
virtual eDataSourceType type() = 0;
|
||||
virtual SP<CWLDataOfferResource> getWayland() = 0;
|
||||
virtual SP<CX11DataOffer> getX11() = 0;
|
||||
virtual SP<IDataSource> getSource() = 0;
|
||||
virtual void markDead();
|
||||
};
|
||||
|
||||
class IDataDevice {
|
||||
public:
|
||||
IDataDevice() = default;
|
||||
virtual ~IDataDevice() = default;
|
||||
|
||||
virtual SP<CWLDataDeviceResource> getWayland() = 0;
|
||||
virtual SP<CX11DataDevice> getX11() = 0;
|
||||
virtual void sendDataOffer(SP<IDataOffer> offer) = 0;
|
||||
virtual void sendEnter(uint32_t serial, SP<CWLSurfaceResource> surf, const Vector2D& local, SP<IDataOffer> offer) = 0;
|
||||
virtual void sendLeave() = 0;
|
||||
virtual void sendMotion(uint32_t timeMs, const Vector2D& local) = 0;
|
||||
virtual void sendDrop() = 0;
|
||||
virtual void sendSelection(SP<IDataOffer> offer) = 0;
|
||||
virtual eDataSourceType type() = 0;
|
||||
};
|
||||
|
Reference in New Issue
Block a user