mirror of
https://github.com/hyprwm/Hyprland.git
synced 2025-08-19 22:13:49 -07:00
xwayland: improve dnd and cleanup (#9405)
Minor changes to xwayland dnd / regular dnd to fix various issues --------- Co-authored-by: Vaxry <vaxry@vaxry.net>
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
#include "../../managers/HookSystemManager.hpp"
|
#include "../../managers/HookSystemManager.hpp"
|
||||||
#include "../../helpers/Monitor.hpp"
|
#include "../../helpers/Monitor.hpp"
|
||||||
#include "../../render/Renderer.hpp"
|
#include "../../render/Renderer.hpp"
|
||||||
|
#include "../../xwayland/Dnd.hpp"
|
||||||
using namespace Hyprutils::OS;
|
using namespace Hyprutils::OS;
|
||||||
|
|
||||||
CWLDataOfferResource::CWLDataOfferResource(SP<CWlDataOffer> resource_, SP<IDataSource> source_) : source(source_), resource(resource_) {
|
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);
|
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.dndSurface.reset();
|
||||||
dnd.dndSurfaceCommit.reset();
|
dnd.dndSurfaceCommit.reset();
|
||||||
dnd.dndSurfaceDestroy.reset();
|
dnd.dndSurfaceDestroy.reset();
|
||||||
@@ -697,6 +698,16 @@ void CWLDataDeviceProtocol::resetDndState() {
|
|||||||
dnd.mouseMove.reset();
|
dnd.mouseMove.reset();
|
||||||
dnd.touchUp.reset();
|
dnd.touchUp.reset();
|
||||||
dnd.touchMove.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() {
|
void CWLDataDeviceProtocol::dropDrag() {
|
||||||
@@ -712,21 +723,31 @@ void CWLDataDeviceProtocol::dropDrag() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dnd.focusedDevice->sendDrop();
|
dnd.focusedDevice->sendDrop();
|
||||||
|
|
||||||
|
#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();
|
dnd.focusedDevice->sendLeave();
|
||||||
|
|
||||||
resetDndState();
|
|
||||||
|
|
||||||
if (dnd.overriddenCursor)
|
if (dnd.overriddenCursor)
|
||||||
g_pInputManager->unsetCursorImage();
|
g_pInputManager->unsetCursorImage();
|
||||||
dnd.overriddenCursor = false;
|
dnd.overriddenCursor = false;
|
||||||
|
cleanupDndState(false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWLDataDeviceProtocol::wasDragSuccessful() {
|
bool CWLDataDeviceProtocol::wasDragSuccessful() {
|
||||||
if (!dnd.focusedDevice || !dnd.currentSource)
|
if (!dnd.currentSource)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (auto const& o : m_vOffers) {
|
for (auto const& o : m_vOffers) {
|
||||||
if (o->dead || !o->source || !o->source->hasDnd())
|
if (o->dead || o->source != dnd.currentSource)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (o->recvd || o->accepted)
|
if (o->recvd || o->accepted)
|
||||||
@@ -734,25 +755,14 @@ bool CWLDataDeviceProtocol::wasDragSuccessful() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef NO_XWAYLAND
|
#ifndef NO_XWAYLAND
|
||||||
if (g_pXWayland->pWM) {
|
if (dnd.focusedDevice->getX11())
|
||||||
for (auto const& o : g_pXWayland->pWM->dndDataOffers) {
|
return true;
|
||||||
if (o->dead || !o->source || !o->source->hasDnd())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (o->source != dnd.currentSource)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWLDataDeviceProtocol::completeDrag() {
|
void CWLDataDeviceProtocol::completeDrag() {
|
||||||
resetDndState();
|
|
||||||
|
|
||||||
if (!dnd.focusedDevice && !dnd.currentSource)
|
if (!dnd.focusedDevice && !dnd.currentSource)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -761,15 +771,11 @@ void CWLDataDeviceProtocol::completeDrag() {
|
|||||||
dnd.currentSource->sendDndFinished();
|
dnd.currentSource->sendDndFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
dnd.focusedDevice.reset();
|
cleanupDndState(true, true, true);
|
||||||
dnd.currentSource.reset();
|
|
||||||
|
|
||||||
g_pInputManager->simulateMouseMovement();
|
|
||||||
g_pSeatManager->resendEnterEvents();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWLDataDeviceProtocol::abortDrag() {
|
void CWLDataDeviceProtocol::abortDrag() {
|
||||||
resetDndState();
|
cleanupDndState(false, false, false);
|
||||||
|
|
||||||
if (dnd.overriddenCursor)
|
if (dnd.overriddenCursor)
|
||||||
g_pInputManager->unsetCursorImage();
|
g_pInputManager->unsetCursorImage();
|
||||||
@@ -778,8 +784,14 @@ void CWLDataDeviceProtocol::abortDrag() {
|
|||||||
if (!dnd.focusedDevice && !dnd.currentSource)
|
if (!dnd.focusedDevice && !dnd.currentSource)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (dnd.focusedDevice)
|
if (dnd.focusedDevice) {
|
||||||
|
#ifndef NO_XWAYLAND
|
||||||
|
if (auto x11Device = dnd.focusedDevice->getX11(); x11Device)
|
||||||
|
x11Device->forceCleanupDnd();
|
||||||
|
#endif
|
||||||
dnd.focusedDevice->sendLeave();
|
dnd.focusedDevice->sendLeave();
|
||||||
|
}
|
||||||
|
|
||||||
if (dnd.currentSource)
|
if (dnd.currentSource)
|
||||||
dnd.currentSource->cancelled();
|
dnd.currentSource->cancelled();
|
||||||
|
|
||||||
|
@@ -190,7 +190,7 @@ class CWLDataDeviceProtocol : public IWaylandProtocol {
|
|||||||
void updateDrag();
|
void updateDrag();
|
||||||
void dropDrag();
|
void dropDrag();
|
||||||
void completeDrag();
|
void completeDrag();
|
||||||
void resetDndState();
|
void cleanupDndState(bool resetDevice, bool resetSource, bool simulateInput);
|
||||||
bool wasDragSuccessful();
|
bool wasDragSuccessful();
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@@ -6,9 +6,14 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "../managers/XWaylandManager.hpp"
|
#include "../managers/XWaylandManager.hpp"
|
||||||
#include "../desktop/WLSurface.hpp"
|
#include "../desktop/WLSurface.hpp"
|
||||||
|
#include "../protocols/core/Compositor.hpp"
|
||||||
|
|
||||||
using namespace Hyprutils::OS;
|
using namespace Hyprutils::OS;
|
||||||
|
|
||||||
|
#define PROPERTY_FORMAT_32BIT 32
|
||||||
|
#define PROPERTY_LENGTH 1
|
||||||
|
#define PROPERTY_OFFSET 0
|
||||||
|
|
||||||
#ifndef NO_XWAYLAND
|
#ifndef NO_XWAYLAND
|
||||||
static xcb_atom_t dndActionToAtom(uint32_t actions) {
|
static xcb_atom_t dndActionToAtom(uint32_t actions) {
|
||||||
if (actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
|
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;
|
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
|
#endif
|
||||||
|
|
||||||
eDataSourceType CX11DataOffer::type() {
|
eDataSourceType CX11DataOffer::type() {
|
||||||
@@ -52,9 +102,6 @@ void CX11DataDevice::sendEnter(uint32_t serial, SP<CWLSurfaceResource> surf, con
|
|||||||
#ifndef NO_XWAYLAND
|
#ifndef NO_XWAYLAND
|
||||||
auto XSURF = g_pXWayland->pWM->windowForWayland(surf);
|
auto XSURF = g_pXWayland->pWM->windowForWayland(surf);
|
||||||
|
|
||||||
if (offer == lastOffer)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!XSURF) {
|
if (!XSURF) {
|
||||||
Debug::log(ERR, "CX11DataDevice::sendEnter: No xwayland surface for destination");
|
Debug::log(ERR, "CX11DataDevice::sendEnter: No xwayland surface for destination");
|
||||||
return;
|
return;
|
||||||
@@ -67,56 +114,62 @@ void CX11DataDevice::sendEnter(uint32_t serial, SP<CWLSurfaceResource> surf, con
|
|||||||
return;
|
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;
|
std::vector<xcb_atom_t> targets;
|
||||||
// reserve to avoid reallocations
|
// reserve to avoid reallocations
|
||||||
targets.reserve(SOURCE->mimes().size());
|
targets.reserve(SOURCE->mimes().size());
|
||||||
|
for (auto const& m : SOURCE->mimes()) {
|
||||||
for (auto& mime : SOURCE->mimes()) {
|
targets.push_back(g_pXWayland->pWM->mimeToAtom(m));
|
||||||
targets.emplace_back(g_pXWayland->pWM->mimeToAtom(mime));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xcb_change_property(g_pXWayland->pWM->connection, XCB_PROP_MODE_REPLACE, g_pXWayland->pWM->dndSelection.window, HYPRATOMS["XdndTypeList"], XCB_ATOM_ATOM, 32, targets.size(),
|
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());
|
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;
|
lastSurface = XSURF;
|
||||||
lastOffer = offer;
|
lastOffer = offer;
|
||||||
|
|
||||||
auto hlSurface = CWLSurface::fromResource(surf);
|
auto hlSurface = XSURF->surface.lock();
|
||||||
if (!hlSurface) {
|
if (!hlSurface) {
|
||||||
Debug::log(ERR, "CX11DataDevice::sendEnter: Non desktop x surface?!");
|
Debug::log(ERR, "CX11DataDevice::sendEnter: Non desktop x surface?!");
|
||||||
lastSurfaceCoords = {};
|
lastSurfaceCoords = {};
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSurfaceCoords = hlSurface->getSurfaceBoxGlobal().value_or(CBox{}).pos();
|
lastSurfaceCoords = g_pXWaylandManager->xwaylandToWaylandCoords(XSURF->geometry.pos());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CX11DataDevice::cleanupState() {
|
||||||
|
lastSurface.reset();
|
||||||
|
lastOffer.reset();
|
||||||
|
lastSurfaceCoords = {};
|
||||||
|
lastTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void CX11DataDevice::sendLeave() {
|
void CX11DataDevice::sendLeave() {
|
||||||
#ifndef NO_XWAYLAND
|
#ifndef NO_XWAYLAND
|
||||||
if (!lastSurface)
|
if (!lastSurface)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
xcb_window_t targetWindow = getProxyWindow(lastSurface->xID);
|
||||||
|
|
||||||
xcb_client_message_data_t data = {0};
|
xcb_client_message_data_t data = {0};
|
||||||
data.data32[0] = g_pXWayland->pWM->dndSelection.window;
|
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();
|
cleanupState();
|
||||||
lastOffer.reset();
|
|
||||||
|
|
||||||
xcb_set_selection_owner(g_pXWayland->pWM->connection, g_pXWayland->pWM->dndSelection.window, XCB_ATOM_NONE, XCB_TIME_CURRENT_TIME);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,32 +178,39 @@ void CX11DataDevice::sendMotion(uint32_t timeMs, const Vector2D& local) {
|
|||||||
if (!lastSurface || !lastOffer || !lastOffer->getSource())
|
if (!lastSurface || !lastOffer || !lastOffer->getSource())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
xcb_window_t targetWindow = getProxyWindow(lastSurface->xID);
|
||||||
|
|
||||||
const auto XCOORDS = g_pXWaylandManager->waylandToXWaylandCoords(lastSurfaceCoords + local);
|
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};
|
xcb_client_message_data_t data = {0};
|
||||||
data.data32[0] = g_pXWayland->pWM->dndSelection.window;
|
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[3] = timeMs;
|
||||||
data.data32[4] = dndActionToAtom(lastOffer->getSource()->actions());
|
data.data32[4] = dndActionToAtom(lastOffer->getSource()->actions());
|
||||||
|
|
||||||
g_pXWayland->pWM->sendDndEvent(lastSurface->surface.lock(), HYPRATOMS["XdndPosition"], data);
|
sendDndEvent(targetWindow, HYPRATOMS["XdndPosition"], data);
|
||||||
|
|
||||||
lastTime = timeMs;
|
lastTime = timeMs;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void CX11DataDevice::sendDrop() {
|
void CX11DataDevice::sendDrop() {
|
||||||
#ifndef NO_XWAYLAND
|
#ifndef NO_XWAYLAND
|
||||||
if (!lastSurface || !lastOffer)
|
if (!lastSurface || !lastOffer) {
|
||||||
|
Debug::log(ERR, "CX11DataDevice::sendDrop: No surface or offer");
|
||||||
return;
|
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};
|
xcb_client_message_data_t data = {0};
|
||||||
data.data32[0] = g_pXWayland->pWM->dndSelection.window;
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,15 +235,16 @@ std::vector<std::string> CX11DataSource::mimes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CX11DataSource::send(const std::string& mime, CFileDescriptor fd) {
|
void CX11DataSource::send(const std::string& mime, CFileDescriptor fd) {
|
||||||
;
|
; // no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
void CX11DataSource::accepted(const std::string& mime) {
|
void CX11DataSource::accepted(const std::string& mime) {
|
||||||
;
|
; // no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
void CX11DataSource::cancelled() {
|
void CX11DataSource::cancelled() {
|
||||||
;
|
dndSuccess = false;
|
||||||
|
dropped = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CX11DataSource::hasDnd() {
|
bool CX11DataSource::hasDnd() {
|
||||||
@@ -195,11 +256,13 @@ bool CX11DataSource::dndDone() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CX11DataSource::error(uint32_t code, const std::string& msg) {
|
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() {
|
void CX11DataSource::sendDndFinished() {
|
||||||
;
|
dndSuccess = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t CX11DataSource::actions() {
|
uint32_t CX11DataSource::actions() {
|
||||||
@@ -211,9 +274,29 @@ eDataSourceType CX11DataSource::type() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CX11DataSource::sendDndDropPerformed() {
|
void CX11DataSource::sendDndDropPerformed() {
|
||||||
;
|
dropped = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CX11DataSource::sendDndAction(wl_data_device_manager_dnd_action a) {
|
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
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../protocols/types/DataDevice.hpp"
|
#include "../protocols/types/DataDevice.hpp"
|
||||||
|
#include "../managers/SeatManager.hpp"
|
||||||
|
#include "../managers/input/InputManager.hpp"
|
||||||
#include <wayland-server-protocol.h>
|
#include <wayland-server-protocol.h>
|
||||||
#include <hyprutils/os/FileDescriptor.hpp>
|
#include <hyprutils/os/FileDescriptor.hpp>
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
#define XDND_VERSION 5
|
#define XDND_VERSION 5
|
||||||
|
|
||||||
@@ -72,10 +75,16 @@ class CX11DataDevice : public IDataDevice {
|
|||||||
virtual void sendDrop();
|
virtual void sendDrop();
|
||||||
virtual void sendSelection(SP<IDataOffer> offer);
|
virtual void sendSelection(SP<IDataOffer> offer);
|
||||||
virtual eDataSourceType type();
|
virtual eDataSourceType type();
|
||||||
|
void forceCleanupDnd();
|
||||||
|
|
||||||
WP<CX11DataDevice> self;
|
WP<CX11DataDevice> self;
|
||||||
|
|
||||||
private:
|
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<CXWaylandSurface> lastSurface;
|
||||||
WP<IDataOffer> lastOffer;
|
WP<IDataOffer> lastOffer;
|
||||||
Vector2D lastSurfaceCoords;
|
Vector2D lastSurfaceCoords;
|
||||||
|
@@ -1125,20 +1125,22 @@ void CXWM::dissociate(SP<CXWaylandSurface> surf) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CXWM::updateClientList() {
|
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;
|
std::vector<xcb_window_t> windows;
|
||||||
for (auto const& m : mappedSurfaces) {
|
windows.reserve(mappedSurfaces.size());
|
||||||
windows.push_back(m->xID);
|
|
||||||
|
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());
|
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.clear();
|
||||||
|
windows.reserve(mappedSurfacesStacking.size());
|
||||||
|
|
||||||
for (auto const& m : mappedSurfacesStacking) {
|
for (auto const& s : mappedSurfacesStacking) {
|
||||||
windows.push_back(m->xID);
|
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());
|
xcb_change_property(connection, XCB_PROP_MODE_REPLACE, screen->root, HYPRATOMS["_NET_CLIENT_LIST_STACKING"], XCB_ATOM_WINDOW, 32, windows.size(), windows.data());
|
||||||
@@ -1279,29 +1281,6 @@ void CXWM::setCursor(unsigned char* pixData, uint32_t stride, const Vector2D& si
|
|||||||
xcb_flush(connection);
|
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() {
|
SP<CX11DataDevice> CXWM::getDataDevice() {
|
||||||
return dndDataDevice;
|
return dndDataDevice;
|
||||||
}
|
}
|
||||||
|
@@ -143,8 +143,6 @@ class CXWM {
|
|||||||
|
|
||||||
void updateClientList();
|
void updateClientList();
|
||||||
|
|
||||||
void sendDndEvent(SP<CWLSurfaceResource> destination, xcb_atom_t type, xcb_client_message_data_t& data);
|
|
||||||
|
|
||||||
// event handlers
|
// event handlers
|
||||||
void handleCreate(xcb_create_notify_event_t* e);
|
void handleCreate(xcb_create_notify_event_t* e);
|
||||||
void handleDestroy(xcb_destroy_notify_event_t* e);
|
void handleDestroy(xcb_destroy_notify_event_t* e);
|
||||||
|
Reference in New Issue
Block a user