hyprland-git/debian/patches/xwayland.patch

554 lines
19 KiB
Diff

From cc67873b712ad0b45957b34be2db6b8095420603 Mon Sep 17 00:00:00 2001
From: nnyyxxxx <nnyyxxxx@protonmail.com>
Date: Thu, 13 Feb 2025 22:14:18 -0500
Subject: [PATCH] refactor(xwayland): improve dnd and cleanup
---
src/protocols/core/DataDevice.cpp | 62 +++++++-----
src/protocols/core/DataDevice.hpp | 2 +-
src/xwayland/Dnd.cpp | 153 ++++++++++++++++++++++--------
src/xwayland/Dnd.hpp | 13 +++
src/xwayland/XWM.cpp | 39 ++------
src/xwayland/XWM.hpp | 2 -
6 files changed, 176 insertions(+), 95 deletions(-)
diff --git a/src/protocols/core/DataDevice.cpp b/src/protocols/core/DataDevice.cpp
index 25117265d11..f4044ec5baf 100644
--- a/src/protocols/core/DataDevice.cpp
+++ b/src/protocols/core/DataDevice.cpp
@@ -13,6 +13,7 @@
#include "../../managers/HookSystemManager.hpp"
#include "../../helpers/Monitor.hpp"
#include "../../render/Renderer.hpp"
+#include "../../xwayland/Dnd.hpp"
using namespace Hyprutils::OS;
CWLDataOfferResource::CWLDataOfferResource(SP<CWlDataOffer> resource_, SP<IDataSource> source_) : source(source_), resource(resource_) {
@@ -689,7 +690,7 @@ void CWLDataDeviceProtocol::updateDrag() {
g_pSeatManager->state.dndPointerFocus->current.size / 2.F, offer);
}
-void CWLDataDeviceProtocol::resetDndState() {
+void CWLDataDeviceProtocol::cleanupDndState(bool resetDevice, bool resetSource, bool simulateInput) {
dnd.dndSurface.reset();
dnd.dndSurfaceCommit.reset();
dnd.dndSurfaceDestroy.reset();
@@ -697,6 +698,16 @@ void CWLDataDeviceProtocol::resetDndState() {
dnd.mouseMove.reset();
dnd.touchUp.reset();
dnd.touchMove.reset();
+
+ if (resetDevice)
+ dnd.focusedDevice.reset();
+ if (resetSource)
+ dnd.currentSource.reset();
+
+ if (simulateInput) {
+ g_pInputManager->simulateMouseMovement();
+ g_pSeatManager->resendEnterEvents();
+ }
}
void CWLDataDeviceProtocol::dropDrag() {
@@ -712,21 +723,31 @@ void CWLDataDeviceProtocol::dropDrag() {
}
dnd.focusedDevice->sendDrop();
- dnd.focusedDevice->sendLeave();
- resetDndState();
+#ifndef NO_XWAYLAND
+ if (dnd.focusedDevice->getX11()) {
+ dnd.focusedDevice->sendLeave();
+ if (dnd.overriddenCursor)
+ g_pInputManager->unsetCursorImage();
+ dnd.overriddenCursor = false;
+ cleanupDndState(true, true, true);
+ return;
+ }
+#endif
+ dnd.focusedDevice->sendLeave();
if (dnd.overriddenCursor)
g_pInputManager->unsetCursorImage();
dnd.overriddenCursor = false;
+ cleanupDndState(false, false, false);
}
bool CWLDataDeviceProtocol::wasDragSuccessful() {
- if (!dnd.focusedDevice || !dnd.currentSource)
+ if (!dnd.currentSource)
return false;
for (auto const& o : m_vOffers) {
- if (o->dead || !o->source || !o->source->hasDnd())
+ if (o->dead || o->source != dnd.currentSource)
continue;
if (o->recvd || o->accepted)
@@ -734,25 +755,14 @@ bool CWLDataDeviceProtocol::wasDragSuccessful() {
}
#ifndef NO_XWAYLAND
- if (g_pXWayland->pWM) {
- 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;
- }
- }
+ if (dnd.focusedDevice->getX11())
+ return true;
#endif
return false;
}
void CWLDataDeviceProtocol::completeDrag() {
- resetDndState();
-
if (!dnd.focusedDevice && !dnd.currentSource)
return;
@@ -761,15 +771,11 @@ void CWLDataDeviceProtocol::completeDrag() {
dnd.currentSource->sendDndFinished();
}
- dnd.focusedDevice.reset();
- dnd.currentSource.reset();
-
- g_pInputManager->simulateMouseMovement();
- g_pSeatManager->resendEnterEvents();
+ cleanupDndState(true, true, true);
}
void CWLDataDeviceProtocol::abortDrag() {
- resetDndState();
+ cleanupDndState(false, false, false);
if (dnd.overriddenCursor)
g_pInputManager->unsetCursorImage();
@@ -778,8 +784,14 @@ void CWLDataDeviceProtocol::abortDrag() {
if (!dnd.focusedDevice && !dnd.currentSource)
return;
- if (dnd.focusedDevice)
+ if (dnd.focusedDevice) {
+#ifndef NO_XWAYLAND
+ if (auto x11Device = dnd.focusedDevice->getX11(); x11Device)
+ x11Device->forceCleanupDnd();
+#endif
dnd.focusedDevice->sendLeave();
+ }
+
if (dnd.currentSource)
dnd.currentSource->cancelled();
diff --git a/src/protocols/core/DataDevice.hpp b/src/protocols/core/DataDevice.hpp
index dfea4a71efd..be0b68b3cd9 100644
--- a/src/protocols/core/DataDevice.hpp
+++ b/src/protocols/core/DataDevice.hpp
@@ -190,7 +190,7 @@ class CWLDataDeviceProtocol : public IWaylandProtocol {
void updateDrag();
void dropDrag();
void completeDrag();
- void resetDndState();
+ void cleanupDndState(bool resetDevice, bool resetSource, bool simulateInput);
bool wasDragSuccessful();
//
diff --git a/src/xwayland/Dnd.cpp b/src/xwayland/Dnd.cpp
index 16d166ce2f9..dd02e6e2608 100644
--- a/src/xwayland/Dnd.cpp
+++ b/src/xwayland/Dnd.cpp
@@ -6,6 +6,7 @@
#endif
#include "../managers/XWaylandManager.hpp"
#include "../desktop/WLSurface.hpp"
+#include "../protocols/core/Compositor.hpp"
using namespace Hyprutils::OS;
@@ -20,6 +21,51 @@ static xcb_atom_t dndActionToAtom(uint32_t actions) {
return XCB_ATOM_NONE;
}
+
+void CX11DataDevice::sendDndEvent(xcb_window_t targetWindow, xcb_atom_t type, xcb_client_message_data_t& data) {
+ xcb_client_message_event_t event = {
+ .response_type = XCB_CLIENT_MESSAGE,
+ .format = 32,
+ .sequence = 0,
+ .window = targetWindow,
+ .type = type,
+ .data = data,
+ };
+
+ xcb_send_event(g_pXWayland->pWM->connection, 0, targetWindow, XCB_EVENT_MASK_NO_EVENT, (const char*)&event);
+ xcb_flush(g_pXWayland->pWM->connection);
+}
+
+xcb_window_t CX11DataDevice::getProxyWindow(xcb_window_t window) {
+ xcb_window_t targetWindow = window;
+ xcb_get_property_cookie_t proxyCookie =
+ xcb_get_property(g_pXWayland->pWM->connection, XCB_PROPERTY_OFFSET, window, HYPRATOMS["XdndProxy"], XCB_ATOM_WINDOW, XCB_PROPERTY_OFFSET, XCB_PROPERTY_LENGTH);
+ xcb_get_property_reply_t* proxyReply = xcb_get_property_reply(g_pXWayland->pWM->connection, proxyCookie, nullptr);
+
+ const auto isValidPropertyReply = [](xcb_get_property_reply_t* reply) {
+ return reply && reply->type == XCB_ATOM_WINDOW && reply->format == XCB_PROPERTY_FORMAT_32BIT && xcb_get_property_value_length(reply) == sizeof(xcb_window_t);
+ };
+
+ if (isValidPropertyReply(proxyReply)) {
+ xcb_window_t proxyWindow = *(xcb_window_t*)xcb_get_property_value(proxyReply);
+
+ xcb_get_property_cookie_t proxyVerifyCookie =
+ xcb_get_property(g_pXWayland->pWM->connection, XCB_PROPERTY_OFFSET, proxyWindow, HYPRATOMS["XdndProxy"], XCB_ATOM_WINDOW, XCB_PROPERTY_OFFSET, XCB_PROPERTY_LENGTH);
+ xcb_get_property_reply_t* proxyVerifyReply = xcb_get_property_reply(g_pXWayland->pWM->connection, proxyVerifyCookie, nullptr);
+
+ if (isValidPropertyReply(proxyVerifyReply)) {
+ xcb_window_t verifyWindow = *(xcb_window_t*)xcb_get_property_value(proxyVerifyReply);
+ if (verifyWindow == proxyWindow) {
+ targetWindow = proxyWindow;
+ Debug::log(LOG, "Using XdndProxy window {:x} for window {:x}", proxyWindow, window);
+ }
+ }
+ free(proxyVerifyReply);
+ }
+ free(proxyReply);
+
+ return targetWindow;
+}
#endif
eDataSourceType CX11DataOffer::type() {
@@ -52,9 +98,6 @@ void CX11DataDevice::sendEnter(uint32_t serial, SP<CWLSurfaceResource> surf, con
#ifndef NO_XWAYLAND
auto XSURF = g_pXWayland->pWM->windowForWayland(surf);
- if (offer == lastOffer)
- return;
-
if (!XSURF) {
Debug::log(ERR, "CX11DataDevice::sendEnter: No xwayland surface for destination");
return;
@@ -67,56 +110,62 @@ void CX11DataDevice::sendEnter(uint32_t serial, SP<CWLSurfaceResource> surf, con
return;
}
- xcb_set_selection_owner(g_pXWayland->pWM->connection, g_pXWayland->pWM->dndSelection.window, HYPRATOMS["XdndSelection"], XCB_TIME_CURRENT_TIME);
-
- xcb_client_message_data_t data = {0};
- data.data32[0] = g_pXWayland->pWM->dndSelection.window;
- data.data32[1] = XDND_VERSION << 24;
-
- // let the client know it needs to check for DND_TYPE_LIST
- data.data32[1] |= 1;
-
std::vector<xcb_atom_t> targets;
// reserve to avoid reallocations
targets.reserve(SOURCE->mimes().size());
-
- for (auto& mime : SOURCE->mimes()) {
- targets.emplace_back(g_pXWayland->pWM->mimeToAtom(mime));
+ for (auto const& m : SOURCE->mimes()) {
+ targets.push_back(g_pXWayland->pWM->mimeToAtom(m));
}
xcb_change_property(g_pXWayland->pWM->connection, XCB_PROP_MODE_REPLACE, g_pXWayland->pWM->dndSelection.window, HYPRATOMS["XdndTypeList"], XCB_ATOM_ATOM, 32, targets.size(),
targets.data());
- g_pXWayland->pWM->sendDndEvent(surf, HYPRATOMS["XdndEnter"], data);
+ xcb_set_selection_owner(g_pXWayland->pWM->connection, g_pXWayland->pWM->dndSelection.window, HYPRATOMS["XdndSelection"], XCB_TIME_CURRENT_TIME);
+ xcb_flush(g_pXWayland->pWM->connection);
+
+ xcb_window_t targetWindow = getProxyWindow(XSURF->xID);
+
+ xcb_client_message_data_t data = {0};
+ data.data32[0] = g_pXWayland->pWM->dndSelection.window;
+ data.data32[1] = XDND_VERSION << 24;
+ data.data32[1] |= 1;
+
+ sendDndEvent(targetWindow, HYPRATOMS["XdndEnter"], data);
lastSurface = XSURF;
lastOffer = offer;
- auto hlSurface = CWLSurface::fromResource(surf);
+ auto hlSurface = XSURF->surface.lock();
if (!hlSurface) {
Debug::log(ERR, "CX11DataDevice::sendEnter: Non desktop x surface?!");
lastSurfaceCoords = {};
return;
}
- lastSurfaceCoords = hlSurface->getSurfaceBoxGlobal().value_or(CBox{}).pos();
+ lastSurfaceCoords = g_pXWaylandManager->xwaylandToWaylandCoords(XSURF->geometry.pos());
#endif
}
+void CX11DataDevice::cleanupState() {
+ lastSurface.reset();
+ lastOffer.reset();
+ lastSurfaceCoords = {};
+ lastTime = 0;
+}
+
void CX11DataDevice::sendLeave() {
#ifndef NO_XWAYLAND
if (!lastSurface)
return;
+ xcb_window_t targetWindow = getProxyWindow(lastSurface->xID);
+
xcb_client_message_data_t data = {0};
data.data32[0] = g_pXWayland->pWM->dndSelection.window;
- g_pXWayland->pWM->sendDndEvent(lastSurface->surface.lock(), HYPRATOMS["XdndLeave"], data);
+ sendDndEvent(targetWindow, HYPRATOMS["XdndLeave"], data);
- lastSurface.reset();
- lastOffer.reset();
-
- xcb_set_selection_owner(g_pXWayland->pWM->connection, g_pXWayland->pWM->dndSelection.window, XCB_ATOM_NONE, XCB_TIME_CURRENT_TIME);
+ cleanupState();
#endif
}
@@ -125,32 +174,39 @@ void CX11DataDevice::sendMotion(uint32_t timeMs, const Vector2D& local) {
if (!lastSurface || !lastOffer || !lastOffer->getSource())
return;
+ xcb_window_t targetWindow = getProxyWindow(lastSurface->xID);
+
const auto XCOORDS = g_pXWaylandManager->waylandToXWaylandCoords(lastSurfaceCoords + local);
+ const uint32_t coords = ((uint32_t)XCOORDS.x << 16) | (uint32_t)XCOORDS.y;
xcb_client_message_data_t data = {0};
data.data32[0] = g_pXWayland->pWM->dndSelection.window;
- data.data32[2] = (((int32_t)XCOORDS.x) << 16) | (int32_t)XCOORDS.y;
+ data.data32[2] = coords;
data.data32[3] = timeMs;
data.data32[4] = dndActionToAtom(lastOffer->getSource()->actions());
- g_pXWayland->pWM->sendDndEvent(lastSurface->surface.lock(), HYPRATOMS["XdndPosition"], data);
+ sendDndEvent(targetWindow, HYPRATOMS["XdndPosition"], data);
+
lastTime = timeMs;
#endif
}
void CX11DataDevice::sendDrop() {
#ifndef NO_XWAYLAND
- if (!lastSurface || !lastOffer)
+ if (!lastSurface || !lastOffer) {
+ Debug::log(ERR, "CX11DataDevice::sendDrop: No surface or offer");
return;
+ }
+
+ xcb_window_t targetWindow = getProxyWindow(lastSurface->xID);
- // we don't have timeMs here, just send last time + 1
xcb_client_message_data_t data = {0};
data.data32[0] = g_pXWayland->pWM->dndSelection.window;
- data.data32[2] = lastTime + 1;
+ data.data32[2] = lastTime;
- g_pXWayland->pWM->sendDndEvent(lastSurface->surface.lock(), HYPRATOMS["XdndDrop"], data);
+ sendDndEvent(targetWindow, HYPRATOMS["XdndDrop"], data);
- sendLeave();
+ cleanupState();
#endif
}
@@ -175,15 +231,16 @@ std::vector<std::string> CX11DataSource::mimes() {
}
void CX11DataSource::send(const std::string& mime, CFileDescriptor fd) {
- ;
+ ; // no-op
}
void CX11DataSource::accepted(const std::string& mime) {
- ;
+ ; // no-op
}
void CX11DataSource::cancelled() {
- ;
+ dndSuccess = false;
+ dropped = false;
}
bool CX11DataSource::hasDnd() {
@@ -195,11 +252,13 @@ bool CX11DataSource::dndDone() {
}
void CX11DataSource::error(uint32_t code, const std::string& msg) {
- Debug::log(ERR, "CX11DataSource::error: this fn is a stub: code {} msg {}", code, msg);
+ Debug::log(ERR, "CX11DataSource error: code {} msg {}", code, msg);
+ dndSuccess = false;
+ dropped = false;
}
void CX11DataSource::sendDndFinished() {
- ;
+ dndSuccess = true;
}
uint32_t CX11DataSource::actions() {
@@ -211,9 +270,29 @@ eDataSourceType CX11DataSource::type() {
}
void CX11DataSource::sendDndDropPerformed() {
- ;
+ dropped = true;
}
void CX11DataSource::sendDndAction(wl_data_device_manager_dnd_action a) {
- ;
+ ; // no-op
+}
+
+void CX11DataDevice::forceCleanupDnd() {
+#ifndef NO_XWAYLAND
+ if (lastOffer) {
+ auto source = lastOffer->getSource();
+ if (source) {
+ source->cancelled();
+ source->sendDndFinished();
+ }
+ }
+
+ xcb_set_selection_owner(g_pXWayland->pWM->connection, XCB_ATOM_NONE, HYPRATOMS["XdndSelection"], XCB_TIME_CURRENT_TIME);
+ xcb_flush(g_pXWayland->pWM->connection);
+
+ cleanupState();
+
+ g_pSeatManager->setPointerFocus(nullptr, {});
+ g_pInputManager->simulateMouseMovement();
+#endif
}
diff --git a/src/xwayland/Dnd.hpp b/src/xwayland/Dnd.hpp
index fb0307963f4..a23f4c004f7 100644
--- a/src/xwayland/Dnd.hpp
+++ b/src/xwayland/Dnd.hpp
@@ -1,11 +1,18 @@
#pragma once
#include "../protocols/types/DataDevice.hpp"
+#include "../managers/SeatManager.hpp"
+#include "../managers/input/InputManager.hpp"
#include <wayland-server-protocol.h>
#include <hyprutils/os/FileDescriptor.hpp>
+#include <xcb/xcb.h>
#define XDND_VERSION 5
+#define XCB_PROPERTY_FORMAT_32BIT 32
+#define XCB_PROPERTY_LENGTH 1
+#define XCB_PROPERTY_OFFSET 0
+
class CXWaylandSurface;
class CX11DataOffer : public IDataOffer {
@@ -72,10 +79,16 @@ class CX11DataDevice : public IDataDevice {
virtual void sendDrop();
virtual void sendSelection(SP<IDataOffer> offer);
virtual eDataSourceType type();
+ void forceCleanupDnd();
WP<CX11DataDevice> self;
private:
+ void cleanupState();
+#ifndef NO_XWAYLAND
+ xcb_window_t getProxyWindow(xcb_window_t window);
+ void sendDndEvent(xcb_window_t targetWindow, xcb_atom_t type, xcb_client_message_data_t& data);
+#endif
WP<CXWaylandSurface> lastSurface;
WP<IDataOffer> lastOffer;
Vector2D lastSurfaceCoords;
diff --git a/src/xwayland/XWM.cpp b/src/xwayland/XWM.cpp
index 68552dd6580..6bdef8181b3 100644
--- a/src/xwayland/XWM.cpp
+++ b/src/xwayland/XWM.cpp
@@ -1104,20 +1104,22 @@ void CXWM::dissociate(SP<CXWaylandSurface> surf) {
}
void CXWM::updateClientList() {
- std::erase_if(mappedSurfaces, [](const auto& e) { return e.expired() || !e->mapped; });
- std::erase_if(mappedSurfacesStacking, [](const auto& e) { return e.expired() || !e->mapped; });
-
std::vector<xcb_window_t> windows;
- for (auto const& m : mappedSurfaces) {
- windows.push_back(m->xID);
+ windows.reserve(mappedSurfaces.size());
+
+ for (auto const& s : mappedSurfaces) {
+ if (auto surf = s.lock(); surf)
+ windows.push_back(surf->xID);
}
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, screen->root, HYPRATOMS["_NET_CLIENT_LIST"], XCB_ATOM_WINDOW, 32, windows.size(), windows.data());
windows.clear();
+ windows.reserve(mappedSurfacesStacking.size());
- for (auto const& m : mappedSurfacesStacking) {
- windows.push_back(m->xID);
+ for (auto const& s : mappedSurfacesStacking) {
+ if (auto surf = s.lock(); surf)
+ windows.push_back(surf->xID);
}
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, screen->root, HYPRATOMS["_NET_CLIENT_LIST_STACKING"], XCB_ATOM_WINDOW, 32, windows.size(), windows.data());
@@ -1223,29 +1225,6 @@ void CXWM::setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& si
xcb_flush(connection);
}
-void CXWM::sendDndEvent(SP<CWLSurfaceResource> destination, xcb_atom_t type, xcb_client_message_data_t& data) {
- auto XSURF = windowForWayland(destination);
-
- if (!XSURF) {
- Debug::log(ERR, "[xwm] No xwayland surface for destination in sendDndEvent");
- return;
- }
-
- xcb_client_message_event_t event = {
- .response_type = XCB_CLIENT_MESSAGE,
- .format = 32,
- .sequence = 0,
- .window = XSURF->xID,
- .type = type,
- .data = data,
- };
-
- xcb_send_event(g_pXWayland->pWM->connection,
- 0, // propagate
- XSURF->xID, XCB_EVENT_MASK_NO_EVENT, (const char*)&event);
- xcb_flush(g_pXWayland->pWM->connection);
-}
-
SP<CX11DataDevice> CXWM::getDataDevice() {
return dndDataDevice;
}
diff --git a/src/xwayland/XWM.hpp b/src/xwayland/XWM.hpp
index 73a02a861a9..cdf51c4e342 100644
--- a/src/xwayland/XWM.hpp
+++ b/src/xwayland/XWM.hpp
@@ -143,8 +143,6 @@ class CXWM {
void updateClientList();
- void sendDndEvent(SP<CWLSurfaceResource> destination, xcb_atom_t type, xcb_client_message_data_t& data);
-
// event handlers
void handleCreate(xcb_create_notify_event_t* e);
void handleDestroy(xcb_destroy_notify_event_t* e);