From 400dd31db616b9f374b5629bfe3035be37661316 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Fri, 3 Sep 2021 12:47:57 +0100 Subject: [PATCH] Drop the internal connection for wayland to X drags An AbstractDragTarget is introduced. This contains either the DataDevice we are dragging to or an Xwl bridge. We set this on Seat along with the active surface. In future this also allows getting rid of the move filter. --- src/input.cpp | 25 +++++-- src/xwl/CMakeLists.txt | 1 + src/xwl/dnd.cpp | 10 ++- src/xwl/dnd.h | 23 +++--- src/xwl/drag.cpp | 3 +- src/xwl/drag_wl.cpp | 134 +++++++---------------------------- src/xwl/drag_wl.h | 20 ++---- src/xwl/drag_x.cpp | 9 ++- src/xwl/xwayland.cpp | 11 +++ src/xwl/xwayland.h | 1 + src/xwl/xwayland_interface.h | 6 ++ src/xwl/xwldrophandler.cpp | 60 ++++++++++++++++ src/xwl/xwldrophandler.h | 35 +++++++++ 13 files changed, 193 insertions(+), 145 deletions(-) create mode 100644 src/xwl/xwldrophandler.cpp create mode 100644 src/xwl/xwldrophandler.h diff --git a/src/input.cpp b/src/input.cpp index dfc5ed14f9..1a6d40e3be 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -1924,6 +1924,23 @@ public: QHash m_cursorByTool; }; +static KWaylandServer::AbstractDropHandler *dropHandler(Toplevel *toplevel) +{ + auto surface = toplevel->surface(); + if (!surface) { + return nullptr; + } + auto seat = waylandServer()->seat(); + auto dropTarget = seat->dropHandlerForSurface(surface); + if (dropTarget) {return dropTarget;} + + if (qobject_cast(toplevel) && xwayland()) { + return xwayland()->xwlDropHandler(); + } + + return nullptr; +} + class DragAndDropInputFilter : public QObject, public InputEventFilter { Q_OBJECT @@ -1979,11 +1996,11 @@ public: if (t) { // TODO: consider decorations if (t->surface() != seat->dragSurface()) { - seat->setDragTarget(t->surface(), t->inputTransformation()); + seat->setDragTarget(dropHandler(t), t->surface(), t->inputTransformation()); } } else { // no window at that place, if we have a surface we need to reset - seat->setDragTarget(nullptr); + seat->setDragTarget(nullptr, nullptr); m_dragTarget = nullptr; } break; @@ -2048,7 +2065,7 @@ public: workspace()->takeActivity(m_dragTarget, Workspace::ActivityFlag::ActivityFocus); m_raiseTimer.start(); } - seat->setDragTarget(t->surface(), pos, t->inputTransformation()); + seat->setDragTarget(dropHandler(t), t->surface(), pos, t->inputTransformation()); } if ((pos - m_lastPos).manhattanLength() > 10) { m_lastPos = pos; @@ -2057,7 +2074,7 @@ public: } } else { // no window at that place, if we have a surface we need to reset - seat->setDragTarget(nullptr); + seat->setDragTarget(nullptr, nullptr); m_dragTarget = nullptr; } return true; diff --git a/src/xwl/CMakeLists.txt b/src/xwl/CMakeLists.txt index 09760f88ef..56a168c91e 100644 --- a/src/xwl/CMakeLists.txt +++ b/src/xwl/CMakeLists.txt @@ -13,5 +13,6 @@ add_library(KWinXwaylandServerModule OBJECT selection_source.cpp transfer.cpp xwayland.cpp + xwldrophandler.cpp ) target_link_libraries(KWinXwaylandServerModule PUBLIC kwin KWinXwaylandCommon) diff --git a/src/xwl/dnd.cpp b/src/xwl/dnd.cpp index 643f641913..05801fdceb 100644 --- a/src/xwl/dnd.cpp +++ b/src/xwl/dnd.cpp @@ -3,7 +3,8 @@ This file is part of the KDE project. SPDX-FileCopyrightText: 2019 Roman Gilg - + SPDX-FileCopyrightText: 2021 David Edmundson + SPDX-FileCopyrightText: 2021 David Redondo SPDX-License-Identifier: GPL-2.0-or-later */ #include "dnd.h" @@ -18,6 +19,7 @@ #include "wayland_server.h" #include "workspace.h" #include "xwayland.h" +#include "xwldrophandler.h" #include #include @@ -42,8 +44,14 @@ uint32_t Dnd::version() return s_version; } +XwlDropHandler *Dnd::dropHandler() const +{ + return m_dropHandler; +} + Dnd::Dnd(xcb_atom_t atom, QObject *parent) : Selection(atom, parent) + , m_dropHandler(new XwlDropHandler) { xcb_connection_t *xcbConn = kwinApp()->x11Connection(); diff --git a/src/xwl/dnd.h b/src/xwl/dnd.h index e82e987c3e..22309f225b 100644 --- a/src/xwl/dnd.h +++ b/src/xwl/dnd.h @@ -13,13 +13,6 @@ #include -namespace KWayland -{ -namespace Client -{ -class Surface; -} -} namespace KWaylandServer { class SurfaceInterface; @@ -34,6 +27,8 @@ namespace Xwl class Drag; enum class DragEventReply; +class XwlDropHandler; + /** * Represents the drag and drop mechanism, on X side this is the XDND protocol. * For more information on XDND see: https://johnlindal.wixsite.com/xdnd @@ -46,6 +41,7 @@ public: explicit Dnd(xcb_atom_t atom, QObject *parent); static uint32_t version(); + XwlDropHandler* dropHandler() const; void doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) override; void x11OffersChanged(const QStringList &added, const QStringList &removed) override; @@ -53,12 +49,10 @@ public: DragEventReply dragMoveFilter(Toplevel *target, const QPoint &pos); - KWaylandServer::SurfaceInterface *surfaceIface() const { - return m_surfaceIface; - } - KWayland::Client::Surface *surface() const { - return m_surface; - } + using DnDAction = KWaylandServer::DataDeviceManagerInterface::DnDAction; + using DnDActions = KWaylandServer::DataDeviceManagerInterface::DnDActions; + static DnDAction atomToClientAction(xcb_atom_t atom); + static xcb_atom_t clientActionToAtom(DnDAction action); private: // start and end Wl native client drags (Wl -> Xwl) @@ -70,8 +64,7 @@ private: Drag *m_currentDrag = nullptr; QVector m_oldDrags; - KWayland::Client::Surface *m_surface; - KWaylandServer::SurfaceInterface *m_surfaceIface = nullptr; + XwlDropHandler *m_dropHandler; Q_DISABLE_COPY(Dnd) }; diff --git a/src/xwl/drag.cpp b/src/xwl/drag.cpp index 0c1493a8e6..74c6091acf 100644 --- a/src/xwl/drag.cpp +++ b/src/xwl/drag.cpp @@ -3,7 +3,8 @@ This file is part of the KDE project. SPDX-FileCopyrightText: 2019 Roman Gilg - + SPDX-FileCopyrightText: 2021 David Edmundson + SPDX-FileCopyrightText: 2021 David Redondo SPDX-License-Identifier: GPL-2.0-or-later */ #include "drag.h" diff --git a/src/xwl/drag_wl.cpp b/src/xwl/drag_wl.cpp index 147923c990..2eabec973c 100644 --- a/src/xwl/drag_wl.cpp +++ b/src/xwl/drag_wl.cpp @@ -3,6 +3,8 @@ This file is part of the KDE project. SPDX-FileCopyrightText: 2019 Roman Gilg + SPDX-FileCopyrightText: 2021 David Edmundson + SPDX-FileCopyrightText: 2021 David Redondo SPDX-License-Identifier: GPL-2.0-or-later */ @@ -11,6 +13,7 @@ #include "databridge.h" #include "dnd.h" #include "xwayland.h" +#include "xwldrophandler.h" #include "atoms.h" #include "x11client.h" @@ -25,108 +28,36 @@ #include #include +using DnDAction = KWaylandServer::DataDeviceManagerInterface::DnDAction; +using DnDActions = KWaylandServer::DataDeviceManagerInterface::DnDActions; + namespace KWin { namespace Xwl { -using DnDAction = KWaylandServer::DataDeviceManagerInterface::DnDAction; -using DnDActions = KWaylandServer::DataDeviceManagerInterface::DnDActions; - -static DnDAction atomToClientAction(xcb_atom_t atom) -{ - if (atom == atoms->xdnd_action_copy) { - return DnDAction::Copy; - } else if (atom == atoms->xdnd_action_move) { - return DnDAction::Move; - } else if (atom == atoms->xdnd_action_ask) { - // we currently do not support it - need some test client first - return DnDAction::None; -// return DnDAction::Ask; - } - return DnDAction::None; -} - -static xcb_atom_t clientActionToAtom(DnDAction action) -{ - if (action == DnDAction::Copy) { - return atoms->xdnd_action_copy; - } else if (action == DnDAction::Move) { - return atoms->xdnd_action_move; - } else if (action == DnDAction::Ask) { - // we currently do not support it - need some test client first - return XCB_ATOM_NONE; -// return atoms->xdnd_action_ask; - } - return XCB_ATOM_NONE; -} - -WlToXDrag::WlToXDrag() -{ - m_dsi = waylandServer()->seat()->dragSource()->dragSource(); -} - DragEventReply WlToXDrag::moveFilter(Toplevel *target, const QPoint &pos) { - AbstractClient *ac = qobject_cast(target); - auto *seat = waylandServer()->seat(); - if (m_visit && m_visit->target() == ac) { - // no target change - return DragEventReply::Take; - } - // leave current target - if (m_visit) { - seat->setDragTarget(nullptr); - m_visit->leave(); - delete m_visit; - m_visit = nullptr; - } - if (!qobject_cast(ac)) { - // no target or wayland native target, - // handled by input code directly - return DragEventReply::Wayland; - } - // new target - seat->setDragTarget(DataBridge::self()->dnd()->surfaceIface(), pos, ac->inputTransformation()); - m_visit = new Xvisit(this, ac); - return DragEventReply::Take; + Q_UNUSED(target) + Q_UNUSED(pos) + return DragEventReply::Wayland; } bool WlToXDrag::handleClientMessage(xcb_client_message_event_t *event) { - if (m_visit && m_visit->handleClientMessage(event)) { - return true; - } - return false; + return DataBridge::self()->dnd()->dropHandler()->handleClientMessage(event); } bool WlToXDrag::end() { - if (!m_visit || m_visit->finished()) { - delete m_visit; - m_visit = nullptr; - return true; - } - connect(m_visit, &Xvisit::finish, this, [this](Xvisit *visit) { - Q_ASSERT(m_visit == visit); - delete visit; - m_visit = nullptr; - // we direclty allow to delete previous visits - Q_EMIT finish(this); - }); - m_visit->leave(); - return false; + return true; } -KWaylandServer::DataSourceInterface *WlToXDrag::dataSourceIface() const -{ - return m_dsi.data(); -} -Xvisit::Xvisit(WlToXDrag *drag, AbstractClient *target) - : QObject(drag), - m_drag(drag), - m_target(target) +Xvisit::Xvisit(AbstractClient *target, KWaylandServer::AbstractDataSource *dataSource, QObject *parent) + : QObject(parent), + m_target(target), + m_dataSource(dataSource) { // first check supported DND version xcb_connection_t *xcbConn = kwinApp()->x11Connection(); @@ -156,7 +87,6 @@ Xvisit::Xvisit(WlToXDrag *drag, AbstractClient *target) } free(reply); - m_dropConnection = connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::dragDropped, this, &Xvisit::drop); receiveOffer(); } @@ -181,9 +111,8 @@ bool Xvisit::handleStatus(xcb_client_message_event_t *event) m_accepts = data->data32[1] & 1; xcb_atom_t actionAtom = data->data32[4]; - auto dataSource = m_drag->dataSourceIface(); - if (dataSource && !dataSource->mimeTypes().isEmpty()) { - dataSource->accept(m_accepts ? dataSource->mimeTypes().constFirst() : QString()); + if (m_dataSource && !m_dataSource->mimeTypes().isEmpty()) { + m_dataSource->accept(m_accepts ? m_dataSource->mimeTypes().constFirst() : QString()); } // TODO: we could optimize via rectangle in data32[2] and data32[3] @@ -192,7 +121,7 @@ bool Xvisit::handleStatus(xcb_client_message_event_t *event) if (!m_state.dropped) { // as long as the drop is not yet done determine requested action - m_preferredAction = atomToClientAction(actionAtom); + m_preferredAction = Dnd::atomToClientAction(actionAtom); determineProposedAction(); requestDragAndDropAction(); } @@ -229,8 +158,8 @@ bool Xvisit::handleFinished(xcb_client_message_event_t *event) Q_UNUSED(success); Q_UNUSED(usedActionAtom); - if (auto dataSource = m_drag->dataSourceIface()) { - dataSource->dndFinished(); + if (m_dataSource) { + m_dataSource->dndFinished(); } doFinish(); return true; @@ -252,7 +181,7 @@ void Xvisit::sendPosition(const QPointF &globalPos) data.data32[0] = DataBridge::self()->dnd()->window(); data.data32[2] = (x << 16) | y; data.data32[3] = XCB_CURRENT_TIME; - data.data32[4] = clientActionToAtom(m_proposedAction); + data.data32[4] = Dnd::clientActionToAtom(m_proposedAction); Drag::sendClientMessage(m_target->window(), atoms->xdnd_position, &data); } @@ -277,8 +206,7 @@ void Xvisit::leave() void Xvisit::receiveOffer() { retrieveSupportedActions(); - auto dragSource = m_drag->dataSourceIface(); - connect(dragSource, &KWaylandServer::AbstractDataSource::supportedDragAndDropActionsChanged, + connect(m_dataSource, &KWaylandServer::AbstractDataSource::supportedDragAndDropActionsChanged, this, &Xvisit::retrieveSupportedActions); enter(); } @@ -298,8 +226,7 @@ void Xvisit::enter() void Xvisit::sendEnter() { - auto dataSource = m_drag->dataSourceIface(); - if (!dataSource) { + if (!m_dataSource) { return; } @@ -307,7 +234,7 @@ void Xvisit::sendEnter() data.data32[0] = DataBridge::self()->dnd()->window(); data.data32[1] = m_version << 24; - const auto mimeTypesNames = dataSource->mimeTypes(); + const auto mimeTypesNames = m_dataSource->mimeTypes(); const int mimesCount = mimeTypesNames.size(); size_t cnt = 0; size_t totalCnt = 0; @@ -376,7 +303,7 @@ void Xvisit::sendLeave() void Xvisit::retrieveSupportedActions() { - m_supportedActions = m_drag->dataSourceIface()->supportedDragAndDropActions(); + m_supportedActions = m_dataSource->supportedDragAndDropActions(); determineProposedAction(); requestDragAndDropAction(); } @@ -412,8 +339,8 @@ void Xvisit::requestDragAndDropAction() } else if (m_supportedActions.testFlag(DnDAction::Move)) { action = DnDAction::Move; } - if (auto dataSource = m_drag->dataSourceIface()) { - dataSource->dndAction(action); + if (m_dataSource) { + m_dataSource->dndAction(action); } } @@ -454,15 +381,8 @@ void Xvisit::stopConnections() { // final outcome has been determined from Wayland side // no more updates needed - disconnect(m_enterConnection); - m_enterConnection = QMetaObject::Connection(); - disconnect(m_dropConnection); - m_dropConnection = QMetaObject::Connection(); - disconnect(m_motionConnection); m_motionConnection = QMetaObject::Connection(); - disconnect(m_actionConnection); - m_actionConnection = QMetaObject::Connection(); } } // namespace Xwl diff --git a/src/xwl/drag_wl.h b/src/xwl/drag_wl.h index 9e702e94c3..c25a0bee33 100644 --- a/src/xwl/drag_wl.h +++ b/src/xwl/drag_wl.h @@ -38,20 +38,13 @@ class Xvisit; class WlToXDrag : public Drag { Q_OBJECT - + using Drag::Drag; public: - explicit WlToXDrag(); - DragEventReply moveFilter(Toplevel *target, const QPoint &pos) override; bool handleClientMessage(xcb_client_message_event_t *event) override; bool end() override; - - KWaylandServer::DataSourceInterface *dataSourceIface() const; - private: - QPointer m_dsi; - Xvisit *m_visit = nullptr; Q_DISABLE_COPY(WlToXDrag) }; @@ -64,7 +57,7 @@ class Xvisit : public QObject public: // TODO: handle ask action - Xvisit(WlToXDrag *drag, AbstractClient *target); + Xvisit(AbstractClient *target, KWaylandServer::AbstractDataSource *dataSource, QObject *parent); bool handleClientMessage(xcb_client_message_event_t *event); bool handleStatus(xcb_client_message_event_t *event); @@ -79,7 +72,7 @@ public: AbstractClient *target() const { return m_target; } - + void drop(); Q_SIGNALS: void finish(Xvisit *self); @@ -96,19 +89,14 @@ private: void requestDragAndDropAction(); void setProposedAction(); - void drop(); - void doFinish(); void stopConnections(); - WlToXDrag *m_drag; AbstractClient *m_target; + QPointer m_dataSource; uint32_t m_version = 0; - QMetaObject::Connection m_enterConnection; QMetaObject::Connection m_motionConnection; - QMetaObject::Connection m_actionConnection; - QMetaObject::Connection m_dropConnection; struct { bool pending = false; diff --git a/src/xwl/drag_x.cpp b/src/xwl/drag_x.cpp index fcca9321ca..91096f8447 100644 --- a/src/xwl/drag_x.cpp +++ b/src/xwl/drag_x.cpp @@ -272,7 +272,14 @@ void XToWlDrag::offerCallback(const QString &mime) void XToWlDrag::setDragTarget() { auto *ac = m_visit->target(); - waylandServer()->seat()->setDragTarget(ac->surface(), ac->inputTransformation()); + + workspace()->activateClient(ac); + auto seat = waylandServer()->seat(); + auto dropTarget = seat->dropHandlerForSurface(ac->surface()); + if (!dropTarget || !ac->surface()) { + return; + } + seat->setDragTarget(dropTarget, ac->surface(), ac->inputTransformation()); } bool XToWlDrag::checkForFinished() diff --git a/src/xwl/xwayland.cpp b/src/xwl/xwayland.cpp index a406397d32..6a74986a7a 100644 --- a/src/xwl/xwayland.cpp +++ b/src/xwl/xwayland.cpp @@ -10,6 +10,8 @@ */ #include "xwayland.h" #include "databridge.h" +#include "dnd.h" +#include "xwldrophandler.h" #include "main_wayland.h" #include "options.h" @@ -484,5 +486,14 @@ DragEventReply Xwayland::dragMoveFilter(Toplevel *target, const QPoint &pos) return bridge->dragMoveFilter(target, pos); } +KWaylandServer::AbstractDropHandler *Xwayland::xwlDropHandler() +{ + DataBridge *bridge = DataBridge::self(); + if (bridge) { + return bridge->dnd()->dropHandler(); + } + return nullptr; +} + } // namespace Xwl } // namespace KWin diff --git a/src/xwl/xwayland.h b/src/xwl/xwayland.h index b16338f597..fc6f81131e 100644 --- a/src/xwl/xwayland.h +++ b/src/xwl/xwayland.h @@ -124,6 +124,7 @@ private: void destroyX11Connection(); DragEventReply dragMoveFilter(Toplevel *target, const QPoint &pos) override; + KWaylandServer::AbstractDropHandler *xwlDropHandler() override; int m_xcbConnectionFd = -1; QProcess *m_xwaylandProcess = nullptr; diff --git a/src/xwl/xwayland_interface.h b/src/xwl/xwayland_interface.h index ec6d47b1b2..6fb62080bc 100644 --- a/src/xwl/xwayland_interface.h +++ b/src/xwl/xwayland_interface.h @@ -16,6 +16,11 @@ class QProcess; +namespace KWaylandServer +{ +class AbstractDropHandler; +} + namespace KWin { class Toplevel; @@ -41,6 +46,7 @@ public: virtual Xwl::DragEventReply dragMoveFilter(Toplevel *target, const QPoint &pos) = 0; virtual QProcess *process() const = 0; + virtual KWaylandServer::AbstractDropHandler *xwlDropHandler() = 0; protected: explicit XwaylandInterface(QObject *parent = nullptr); diff --git a/src/xwl/xwldrophandler.cpp b/src/xwl/xwldrophandler.cpp new file mode 100644 index 0000000000..f42b6d93e9 --- /dev/null +++ b/src/xwl/xwldrophandler.cpp @@ -0,0 +1,60 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2021 David Edmundson + SPDX-FileCopyrightText: 2021 David Redondo + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include "databridge.h" +#include "dnd.h" +#include "drag_wl.h" +#include "wayland_server.h" +#include "workspace.h" +#include "x11client.h" +#include "xwldrophandler.h" + +#include + +namespace KWin::Xwl { + +XwlDropHandler::XwlDropHandler() + : KWaylandServer::AbstractDropHandler(nullptr) +{ +} + +void XwlDropHandler::drop() +{ + if (m_xvisit) { + m_xvisit->drop(); + } +} + +bool XwlDropHandler::handleClientMessage(xcb_client_message_event_t *event) +{ + if (m_xvisit && m_xvisit->handleClientMessage(event)) { + return true; + } + return false; +} + +void XwlDropHandler::updateDragTarget(KWaylandServer::SurfaceInterface *surface, quint32 serial) +{ + auto client = workspace()->findClient([surface](const X11Client *c) {return c->surface() == surface;}); + if (m_xvisit && client == m_xvisit->target()) { + return; + } + // leave current target + if (m_xvisit) { + m_xvisit->leave(); + delete m_xvisit; + m_xvisit = nullptr; + } + if (client) { + m_xvisit = new Xvisit(client, waylandServer()->seat()->dragSource(), this); + } + +} +} diff --git a/src/xwl/xwldrophandler.h b/src/xwl/xwldrophandler.h new file mode 100644 index 0000000000..df47a04c7d --- /dev/null +++ b/src/xwl/xwldrophandler.h @@ -0,0 +1,35 @@ +/* + KWin - the KDE window manager + This file is part of the KDE project. + + SPDX-FileCopyrightText: 2021 David Edmundson + SPDX-FileCopyrightText: 2021 David Redondo + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#ifndef XWLDROPHANDLER_H +#define XWLDROPHANDLER_H + +#include + +namespace KWin { +namespace Xwl { + +class Xvisit; + +class XwlDropHandler : public KWaylandServer::AbstractDropHandler +{ + Q_OBJECT +public: + XwlDropHandler(); + + void updateDragTarget(KWaylandServer::SurfaceInterface *surface, quint32 serial) override; + bool handleClientMessage(xcb_client_message_event_t *event); +private: + void drop() override; + Xvisit *m_xvisit = nullptr; +}; +} +} +#endif // XWLDROPHANDLER_H