diff options
| author | nyx <nnyyxxxx@protonmail.com> | 2025-02-23 21:52:10 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-02-24 03:52:10 +0100 |
| commit | 3458d7ac93a101ea534ef6d0bcbc32725e267045 (patch) | |
| tree | 0f1f5d39c59a152e7b01610010d9d0a3005fcc05 /src/xwayland/Dnd.cpp | |
| parent | e59464629fd1b0bdc4505786b8186d759933933c (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.cpp | 157 |
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 } |
