summaryrefslogtreecommitdiff
path: root/src/xwayland/Dnd.cpp
diff options
context:
space:
mode:
authornyx <nnyyxxxx@protonmail.com>2025-02-23 21:52:10 -0500
committerGitHub <noreply@github.com>2025-02-24 03:52:10 +0100
commit3458d7ac93a101ea534ef6d0bcbc32725e267045 (patch)
tree0f1f5d39c59a152e7b01610010d9d0a3005fcc05 /src/xwayland/Dnd.cpp
parente59464629fd1b0bdc4505786b8186d759933933c (diff)
xwayland: improve dnd and cleanup (#9405)
Minor changes to xwayland dnd / regular dnd to fix various issues --------- Co-authored-by: Vaxry <vaxry@vaxry.net>
Diffstat (limited to 'src/xwayland/Dnd.cpp')
-rw-r--r--src/xwayland/Dnd.cpp157
1 files changed, 120 insertions, 37 deletions
diff --git a/src/xwayland/Dnd.cpp b/src/xwayland/Dnd.cpp
index 16d166ce..d5e555a4 100644
--- a/src/xwayland/Dnd.cpp
+++ b/src/xwayland/Dnd.cpp
@@ -6,9 +6,14 @@
#endif
#include "../managers/XWaylandManager.hpp"
#include "../desktop/WLSurface.hpp"
+#include "../protocols/core/Compositor.hpp"
using namespace Hyprutils::OS;
+#define PROPERTY_FORMAT_32BIT 32
+#define PROPERTY_LENGTH 1
+#define PROPERTY_OFFSET 0
+
#ifndef NO_XWAYLAND
static xcb_atom_t dndActionToAtom(uint32_t actions) {
if (actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
@@ -20,6 +25,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, PROPERTY_OFFSET, window, HYPRATOMS["XdndProxy"], XCB_ATOM_WINDOW, PROPERTY_OFFSET, 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 == 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, PROPERTY_OFFSET, proxyWindow, HYPRATOMS["XdndProxy"], XCB_ATOM_WINDOW, PROPERTY_OFFSET, 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 +102,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 +114,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);
-
- lastSurface.reset();
- lastOffer.reset();
+ sendDndEvent(targetWindow, HYPRATOMS["XdndLeave"], data);
- xcb_set_selection_owner(g_pXWayland->pWM->connection, g_pXWayland->pWM->dndSelection.window, XCB_ATOM_NONE, XCB_TIME_CURRENT_TIME);
+ cleanupState();
#endif
}
@@ -125,32 +178,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 +235,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 +256,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 +274,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
}