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