From 65f168e1447cc7e56077fb85a7b7511a47494650 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Fri, 3 Sep 2021 12:48:09 +0100 Subject: [PATCH] Use a direct connection for X to wayland drags We already use a direct DataSource object for transfer. Use of the new public SeatInterface::startDrag allows us to start our drag directly. --- src/xwl/databridge.cpp | 35 +---------- src/xwl/databridge.h | 19 ------ src/xwl/datasource.cpp | 26 ++++++++ src/xwl/datasource.h | 24 ++++++++ src/xwl/dnd.cpp | 83 +++++++++++-------------- src/xwl/dnd.h | 1 + src/xwl/drag_x.cpp | 113 ++++++++--------------------------- src/xwl/drag_x.h | 25 +++----- src/xwl/selection_source.cpp | 28 ++------- src/xwl/selection_source.h | 18 ------ src/xwl/transfer.cpp | 5 -- 11 files changed, 123 insertions(+), 254 deletions(-) diff --git a/src/xwl/databridge.cpp b/src/xwl/databridge.cpp index 9d75a43e04..d156ac4546 100644 --- a/src/xwl/databridge.cpp +++ b/src/xwl/databridge.cpp @@ -18,16 +18,11 @@ #include "wayland_server.h" #include "workspace.h" -#include -#include -#include - #include #include #include #include -using namespace KWayland::Client; using namespace KWaylandServer; namespace KWin @@ -46,31 +41,7 @@ DataBridge::DataBridge(QObject *parent) : QObject(parent) { s_self = this; - - DataDeviceManager *dataDeviceManager = waylandServer()->internalDataDeviceManager(); - Seat *seat = waylandServer()->internalSeat(); - m_dataDevice = dataDeviceManager->getDataDevice(seat, this); - - const DataDeviceManagerInterface *dataDeviceManagerInterface = - waylandServer()->dataDeviceManager(); - - auto *dc = new QMetaObject::Connection(); - *dc = connect(dataDeviceManagerInterface, &DataDeviceManagerInterface::dataDeviceCreated, this, - [this, dc](DataDeviceInterface *dataDeviceInterface) { - if (m_dataDeviceInterface) { - return; - } - if (dataDeviceInterface->client() != *waylandServer()->internalConnection()) { - return; - } - QObject::disconnect(*dc); - delete dc; - m_dataDeviceInterface = dataDeviceInterface; - init(); - } - ); - - waylandServer()->dispatch(); + init(); } DataBridge::~DataBridge() @@ -83,7 +54,6 @@ void DataBridge::init() m_clipboard = new Clipboard(atoms->clipboard, this); m_dnd = new Dnd(atoms->xdnd_selection, this); m_primary = new Primary(atoms->primary, this); - waylandServer()->dispatch(); kwinApp()->installNativeEventFilter(this); } @@ -98,9 +68,6 @@ bool DataBridge::nativeEventFilter(const QByteArray &eventType, void *message, l DragEventReply DataBridge::dragMoveFilter(Toplevel *target, const QPoint &pos) { - if (!m_dnd) { - return DragEventReply::Wayland; - } return m_dnd->dragMoveFilter(target, pos); } diff --git a/src/xwl/databridge.h b/src/xwl/databridge.h index 4bb115a2cc..541384eb61 100644 --- a/src/xwl/databridge.h +++ b/src/xwl/databridge.h @@ -15,13 +15,6 @@ #include #include -namespace KWayland -{ -namespace Client -{ -class DataDevice; -} -} namespace KWaylandServer { class DataDeviceInterface; @@ -56,14 +49,6 @@ public: DragEventReply dragMoveFilter(Toplevel *target, const QPoint &pos); - KWayland::Client::DataDevice *dataDevice() const - { - return m_dataDevice; - } - KWaylandServer::DataDeviceInterface *dataDeviceIface() const - { - return m_dataDeviceInterface; - } Dnd *dnd() const { return m_dnd; @@ -78,10 +63,6 @@ private: Dnd *m_dnd = nullptr; Primary *m_primary = nullptr; - /* Internal data device interface */ - KWayland::Client::DataDevice *m_dataDevice = nullptr; - KWaylandServer::DataDeviceInterface *m_dataDeviceInterface = nullptr; - KWIN_SINGLETON(DataBridge) }; diff --git a/src/xwl/datasource.cpp b/src/xwl/datasource.cpp index d4880e0e88..68d594545e 100644 --- a/src/xwl/datasource.cpp +++ b/src/xwl/datasource.cpp @@ -26,5 +26,31 @@ void XwlDataSource::setMimeTypes(const QStringList &mimeTypes) { m_mimeTypes = mimeTypes; } + +void XwlDataSource::accept(const QString &mimeType) +{ + m_accepted = !mimeType.isEmpty(); +} + +KWaylandServer::DataDeviceManagerInterface::DnDActions XwlDataSource::supportedDragAndDropActions() const +{ + return m_supportedDndActions; +} + +void XwlDataSource::setSupportedDndActions(KWaylandServer::DataDeviceManagerInterface::DnDActions dndActions) +{ + m_supportedDndActions = dndActions; + Q_EMIT supportedDragAndDropActionsChanged(); +} + +void XwlDataSource::dndAction(KWaylandServer::DataDeviceManagerInterface::DnDAction action) +{ + m_dndAction = action; +} + +bool XwlDataSource::isAccepted() const +{ + return m_accepted; +} } } diff --git a/src/xwl/datasource.h b/src/xwl/datasource.h index 95cfd78a60..e60c95cd8b 100644 --- a/src/xwl/datasource.h +++ b/src/xwl/datasource.h @@ -19,11 +19,35 @@ public: void cancel() override; QStringList mimeTypes() const override; void setMimeTypes(const QStringList &mimeTypes); + + void accept(const QString &mimeType) override; + KWaylandServer::DataDeviceManagerInterface::DnDActions supportedDragAndDropActions() const override; + void setSupportedDndActions(KWaylandServer::DataDeviceManagerInterface::DnDActions dndActions); + + void dndAction(KWaylandServer::DataDeviceManagerInterface::DnDAction action) override; + + KWaylandServer::DataDeviceManagerInterface::DnDAction selectedDragAndDropAction() { + return m_dndAction; + } + + void dropPerformed() override { + Q_EMIT dropped(); + } + void dndFinished() override { + Q_EMIT finished(); + } + bool isAccepted() const override; + Q_SIGNALS: void dataRequested(const QString &mimeType, qint32 fd); + void dropped(); + void finished(); private: QStringList m_mimeTypes; + KWaylandServer::DataDeviceManagerInterface::DnDActions m_supportedDndActions; + KWaylandServer::DataDeviceManagerInterface::DnDAction m_dndAction = KWaylandServer::DataDeviceManagerInterface::DnDAction::None; + bool m_accepted = false; }; } } diff --git a/src/xwl/dnd.cpp b/src/xwl/dnd.cpp index 05801fdceb..75672d7316 100644 --- a/src/xwl/dnd.cpp +++ b/src/xwl/dnd.cpp @@ -21,9 +21,6 @@ #include "xwayland.h" #include "xwldrophandler.h" -#include -#include - #include #include #include @@ -80,44 +77,6 @@ Dnd::Dnd(xcb_atom_t atom, QObject *parent) connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::dragStarted, this, &Dnd::startDrag); connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::dragEnded, this, &Dnd::endDrag); - - const auto *comp = waylandServer()->compositor(); - m_surface = waylandServer()->internalCompositor()->createSurface(this); - m_surface->setInputRegion(nullptr); - m_surface->commit(KWayland::Client::Surface::CommitFlag::None); - auto *dc = new QMetaObject::Connection(); - *dc = connect(comp, &KWaylandServer::CompositorInterface::surfaceCreated, this, - [this, dc](KWaylandServer::SurfaceInterface *si) { - // TODO: how to make sure that it is the iface of m_surface? - if (m_surfaceIface || si->client() != waylandServer()->internalConnection()) { - return; - } - QObject::disconnect(*dc); - delete dc; - m_surfaceIface = si; - connect(workspace(), &Workspace::clientActivated, this, - [this](AbstractClient *ac) { - if (!ac || !ac->inherits("KWin::X11Client")) { - return; - } - auto *surface = ac->surface(); - if (surface) { - surface->setDataProxy(m_surfaceIface); - } else { - auto *dc = new QMetaObject::Connection(); - *dc = connect(ac, &AbstractClient::surfaceChanged, this, [this, ac, dc] { - if (auto *surface = ac->surface()) { - surface->setDataProxy(m_surfaceIface); - QObject::disconnect(*dc); - delete dc; - } - } - ); - } - }); - } - ); - waylandServer()->dispatch(); } void Dnd::doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) @@ -153,7 +112,6 @@ void Dnd::doHandleXfixesNotify(xcb_xfixes_selection_notify_event_t *event) if (!source) { return; } - DataBridge::self()->dataDeviceIface()->updateProxy(originSurface); m_currentDrag = new XToWlDrag(source); } @@ -161,7 +119,6 @@ void Dnd::x11OffersChanged(const QStringList &added, const QStringList &removed) { Q_UNUSED(added); Q_UNUSED(removed); - // TODO: handled internally } bool Dnd::handleClientMessage(xcb_client_message_event_t *event) @@ -179,17 +136,14 @@ bool Dnd::handleClientMessage(xcb_client_message_event_t *event) DragEventReply Dnd::dragMoveFilter(Toplevel *target, const QPoint &pos) { - // This filter only is used when a drag is in process. Q_ASSERT(m_currentDrag); return m_currentDrag->moveFilter(target, pos); } void Dnd::startDrag() { - auto *ddi = waylandServer()->seat()->dragSource(); - if (ddi == DataBridge::self()->dataDeviceIface()) { - // X to Wl drag, started by us, is in progress. - Q_ASSERT(m_currentDrag); + auto dragSource = waylandServer()->seat()->dragSource(); + if (qobject_cast(dragSource)) { return; } @@ -199,7 +153,7 @@ void Dnd::startDrag() // New Wl to X drag, init drag and Wl source. m_currentDrag = new WlToXDrag(); auto source = new WlSource(this); - source->setDataSourceIface(ddi->dragSource()); + source->setDataSourceIface(dragSource); setWlSource(source); ownSelection(true); } @@ -223,5 +177,36 @@ void Dnd::clearOldDrag(Drag *drag) delete drag; } +using DnDAction = KWaylandServer::DataDeviceManagerInterface::DnDAction; +using DnDActions = KWaylandServer::DataDeviceManagerInterface::DnDActions; + +DnDAction Dnd::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; +} + +xcb_atom_t Dnd::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; +} + } // namespace Xwl } // namespace KWin diff --git a/src/xwl/dnd.h b/src/xwl/dnd.h index 22309f225b..6ea44dec5c 100644 --- a/src/xwl/dnd.h +++ b/src/xwl/dnd.h @@ -11,6 +11,7 @@ #include "selection.h" +#include #include namespace KWaylandServer diff --git a/src/xwl/drag_x.cpp b/src/xwl/drag_x.cpp index 91096f8447..6811fd6bdd 100644 --- a/src/xwl/drag_x.cpp +++ b/src/xwl/drag_x.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 */ @@ -12,18 +14,17 @@ #include "dnd.h" #include "selection_source.h" #include "xwayland.h" +#include "datasource.h" #include "abstract_client.h" #include "atoms.h" #include "wayland_server.h" #include "workspace.h" -#include -#include - #include #include #include +#include #include #include @@ -33,36 +34,8 @@ namespace KWin namespace Xwl { -using DnDAction = KWayland::Client::DataDeviceManager::DnDAction; -using DnDActions = KWayland::Client::DataDeviceManager::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; -} - -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; -} +using DnDAction = KWaylandServer::DataDeviceManagerInterface::DnDAction; +using DnDActions = KWaylandServer::DataDeviceManagerInterface::DnDActions; static QStringList atomToMimeTypes(xcb_atom_t atom) { @@ -103,9 +76,7 @@ XToWlDrag::XToWlDrag(X11Source *source) Q_UNUSED(fd); m_dataRequests << QPair(m_source->timestamp(), false); }); - auto *ddm = waylandServer()->internalDataDeviceManager(); - m_dataSource = ddm->createDataSource(this); - connect(m_dataSource, &KWayland::Client::DataSource::dragAndDropPerformed, this, [this] { + connect(&m_selectionSource, &XwlDataSource::dropped, this, [this] { m_performed = true; if (m_visit) { connect(m_visit, &WlVisit::finish, this, [this](WlVisit *visit) { @@ -124,43 +95,22 @@ XToWlDrag::XToWlDrag(X11Source *source) } }); } + // Dave do we need this async finish check anymore? checkForFinished(); }); - connect(m_dataSource, &KWayland::Client::DataSource::dragAndDropFinished, this, [this] { - // this call is not reliably initiated by Wayland clients + connect(&m_selectionSource, &XwlDataSource::finished, this, [this] { checkForFinished(); }); + connect(&m_selectionSource, &XwlDataSource::dataRequested, source, &X11Source::startTransfer); - // source does _not_ take ownership of m_dataSource - source->setDataSource(m_dataSource); - - auto *dc = new QMetaObject::Connection(); - *dc = connect(waylandServer()->dataDeviceManager(), &KWaylandServer::DataDeviceManagerInterface::dataSourceCreated, this, - [this, dc](KWaylandServer::DataSourceInterface *dsi) { - Q_ASSERT(dsi); - if (dsi->client() != waylandServer()->internalConnection()->client()) { - return; - } - QObject::disconnect(*dc); - delete dc; - connect(dsi, &KWaylandServer::DataSourceInterface::mimeTypeOffered, this, &XToWlDrag::offerCallback); - } - ); - // Start drag with serial of last left pointer button press. - // This means X to Wl drags can only be executed with the left pointer button being pressed. - // For touch and (maybe) other pointer button drags we have to revisit this. - // - // Until then we accept the restriction for Xwayland clients. - DataBridge::self()->dataDevice()->startDrag(waylandServer()->seat()->pointerButtonSerial(Qt::LeftButton), - m_dataSource, - DataBridge::self()->dnd()->surface()); - waylandServer()->dispatch(); + auto *seat = waylandServer()->seat(); + int serial = waylandServer()->seat()->pointerButtonSerial(Qt::LeftButton); + // we know we are the focussed surface as dnd checks + seat->startDrag(&m_selectionSource, seat->focusedPointerSurface(), serial); } XToWlDrag::~XToWlDrag() { - delete m_dataSource; - m_dataSource = nullptr; } DragEventReply XToWlDrag::moveFilter(Toplevel *target, const QPoint &pos) @@ -194,7 +144,7 @@ DragEventReply XToWlDrag::moveFilter(Toplevel *target, const QPoint &pos) if (hasCurrent) { // last received enter event is now void, // wait for the next one - seat->setDragTarget(nullptr); + seat->setDragTarget(nullptr, nullptr); } return DragEventReply::Ignore; } @@ -220,18 +170,12 @@ bool XToWlDrag::handleClientMessage(xcb_client_message_event_t *event) void XToWlDrag::setDragAndDropAction(DnDAction action) { - m_dataSource->setDragAndDropActions(action); + m_selectionSource.setSupportedDndActions(action); } DnDAction XToWlDrag::selectedDragAndDropAction() { - // Take the last received action only from before the drag was performed, - // because the action gets reset as soon as the drag is performed - // (this seems to be a bug in KWayland -> TODO). - if (!m_performed) { - m_lastSelectedDragAndDropAction = m_dataSource->selectedDragAndDropAction(); - } - return m_lastSelectedDragAndDropAction; + return m_selectionSource.selectedDragAndDropAction(); } void XToWlDrag::setOffers(const Mimes &offers) @@ -250,32 +194,25 @@ void XToWlDrag::setOffers(const Mimes &offers) return; } - // TODO: make sure that offers are not changed in between visits - - m_offersPending = m_offers = offers; + m_offers = offers; + QStringList mimeTypes; + mimeTypes.reserve(offers.size()); for (const auto &mimePair : offers) { - m_dataSource->offer(mimePair.first); + mimeTypes.append(mimePair.first); } + m_selectionSource.setMimeTypes(mimeTypes); + setDragTarget(); } using Mime = QPair; -void XToWlDrag::offerCallback(const QString &mime) -{ - m_offersPending.erase(std::remove_if(m_offersPending.begin(), m_offersPending.end(), - [mime](const Mime &m) { return m.first == mime; })); - if (m_offersPending.isEmpty() && m_visit && m_visit->entered()) { - setDragTarget(); - } -} - void XToWlDrag::setDragTarget() { auto *ac = m_visit->target(); - workspace()->activateClient(ac); auto seat = waylandServer()->seat(); auto dropTarget = seat->dropHandlerForSurface(ac->surface()); + if (!dropTarget || !ac->surface()) { return; } @@ -470,7 +407,7 @@ bool WlVisit::handlePosition(xcb_client_message_event_t *event) xcb_atom_t actionAtom = m_version > 1 ? data->data32[4] : atoms->xdnd_action_copy; - auto action = atomToClientAction(actionAtom); + auto action = Dnd::atomToClientAction(actionAtom); if (action == DnDAction::None) { // copy action is always possible in XDND diff --git a/src/xwl/drag_x.h b/src/xwl/drag_x.h index ccaa3f5bf1..af01e2e257 100644 --- a/src/xwl/drag_x.h +++ b/src/xwl/drag_x.h @@ -10,21 +10,14 @@ #define KWIN_XWL_DRAG_X #include "drag.h" +#include "datasource.h" -#include +#include #include #include #include -namespace KWayland -{ -namespace Client -{ -class DataSource; -} -} - namespace KWin { class Toplevel; @@ -49,8 +42,8 @@ public: DragEventReply moveFilter(Toplevel *target, const QPoint &pos) override; bool handleClientMessage(xcb_client_message_event_t *event) override; - void setDragAndDropAction(KWayland::Client::DataDeviceManager::DnDAction action); - KWayland::Client::DataDeviceManager::DnDAction selectedDragAndDropAction(); + void setDragAndDropAction(KWaylandServer::DataDeviceManagerInterface::DnDAction action); + KWaylandServer::DataDeviceManagerInterface::DnDAction selectedDragAndDropAction(); bool end() override { return false; @@ -61,15 +54,13 @@ public: private: void setOffers(const Mimes &offers); - void offerCallback(const QString &mime); void setDragTarget(); bool checkForFinished(); - KWayland::Client::DataSource *m_dataSource; - Mimes m_offers; - Mimes m_offersPending; + + XwlDataSource m_selectionSource; X11Source *m_source; QVector > m_dataRequests; @@ -78,7 +69,7 @@ private: QVector m_oldVisits; bool m_performed = false; - KWayland::Client::DataDeviceManager::DnDAction m_lastSelectedDragAndDropAction = KWayland::Client::DataDeviceManager::DnDAction::None; + KWaylandServer::DataDeviceManagerInterface::DnDAction m_lastSelectedDragAndDropAction = KWaylandServer::DataDeviceManagerInterface::DnDAction::None; Q_DISABLE_COPY(XToWlDrag) }; @@ -139,7 +130,7 @@ private: uint32_t m_version = 0; xcb_atom_t m_actionAtom; - KWayland::Client::DataDeviceManager::DnDAction m_action = KWayland::Client::DataDeviceManager::DnDAction::None; + KWaylandServer::DataDeviceManagerInterface::DnDAction m_action = KWaylandServer::DataDeviceManagerInterface::DnDAction::None; bool m_mapped = false; bool m_entered = false; diff --git a/src/xwl/selection_source.cpp b/src/xwl/selection_source.cpp index 1473921bac..c4aa2651e4 100644 --- a/src/xwl/selection_source.cpp +++ b/src/xwl/selection_source.cpp @@ -13,11 +13,6 @@ #include "atoms.h" #include "wayland_server.h" -#include -#include -#include -#include - #include #include #include @@ -51,9 +46,13 @@ void WlSource::setDataSourceIface(KWaylandServer::AbstractDataSource *dsi) for (const auto &mime : dsi->mimeTypes()) { m_offers << mime; } + + // TODO, this can probably be removed after some testing + // all mime types should be constant after a data source is set m_offerConnection = connect(dsi, &KWaylandServer::DataSourceInterface::mimeTypeOffered, this, &WlSource::receiveOffer); + m_dsi = dsi; } @@ -155,7 +154,6 @@ bool WlSource::checkStartTransfer(xcb_selection_request_event_t *event) } m_dsi->requestData(*mimeIt, p[1]); - waylandServer()->dispatch(); Q_EMIT transferReady(new xcb_selection_request_event_t(*event), p[0]); return true; @@ -250,26 +248,8 @@ void X11Source::handleTargets() free(reply); } -void X11Source::setDataSource(KWayland::Client::DataSource *dataSource) -{ - Q_ASSERT(dataSource); - if (m_dataSource) { - delete m_dataSource; - } - - m_dataSource = dataSource; - - for (const Mime &offer : qAsConst(m_offers)) { - dataSource->offer(offer.first); - } - - connect(dataSource, &KWayland::Client::DataSource::sendDataRequested, - this, &X11Source::startTransfer); -} - void X11Source::setOffers(const Mimes &offers) { - // TODO: share code with handleTargets and emit signals accordingly? m_offers = offers; } diff --git a/src/xwl/selection_source.h b/src/xwl/selection_source.h index 8514679bd4..838d588bd9 100644 --- a/src/xwl/selection_source.h +++ b/src/xwl/selection_source.h @@ -19,13 +19,6 @@ class QSocketNotifier; struct xcb_selection_request_event_t; struct xcb_xfixes_selection_notify_event_t; -namespace KWayland -{ -namespace Client -{ -class DataSource; -} -} namespace KWaylandServer { class DataDeviceInterface; @@ -119,16 +112,6 @@ class X11Source : public SelectionSource public: X11Source(Selection *selection, xcb_xfixes_selection_notify_event_t *event); - /** - * @param ds must exist. - * - * X11Source does not take ownership of it in general, but if the function - * is called again, it will delete the previous data source. - */ - void setDataSource(KWayland::Client::DataSource *dataSource); - KWayland::Client::DataSource *dataSource() const { - return m_dataSource; - } void getTargets(); Mimes offers() const { @@ -151,7 +134,6 @@ private: void handleTargets(); xcb_window_t m_owner; - KWayland::Client::DataSource *m_dataSource = nullptr; Mimes m_offers; diff --git a/src/xwl/transfer.cpp b/src/xwl/transfer.cpp index 6ba4c7b234..8d74f60174 100644 --- a/src/xwl/transfer.cpp +++ b/src/xwl/transfer.cpp @@ -16,11 +16,6 @@ #include "wayland_server.h" #include "workspace.h" -#include -#include -#include -#include - #include #include #include