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.
This commit is contained in:
David Edmundson 2021-09-03 12:47:57 +01:00
parent 7f976199b7
commit 400dd31db6
13 changed files with 193 additions and 145 deletions

View file

@ -1924,6 +1924,23 @@ public:
QHash<KWaylandServer::TabletToolV2Interface*, Cursor*> 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<X11Client*>(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;

View file

@ -13,5 +13,6 @@ add_library(KWinXwaylandServerModule OBJECT
selection_source.cpp
transfer.cpp
xwayland.cpp
xwldrophandler.cpp
)
target_link_libraries(KWinXwaylandServerModule PUBLIC kwin KWinXwaylandCommon)

View file

@ -3,7 +3,8 @@
This file is part of the KDE project.
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
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 <KWayland/Client/compositor.h>
#include <KWayland/Client/surface.h>
@ -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();

View file

@ -13,13 +13,6 @@
#include <QPoint>
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<Drag *> m_oldDrags;
KWayland::Client::Surface *m_surface;
KWaylandServer::SurfaceInterface *m_surfaceIface = nullptr;
XwlDropHandler *m_dropHandler;
Q_DISABLE_COPY(Dnd)
};

View file

@ -3,7 +3,8 @@
This file is part of the KDE project.
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "drag.h"

View file

@ -3,6 +3,8 @@
This file is part of the KDE project.
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
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 <QMouseEvent>
#include <QTimer>
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<AbstractClient *>(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<X11Client *>(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

View file

@ -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<KWaylandServer::DataSourceInterface> 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<KWaylandServer::AbstractDataSource> 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;

View file

@ -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()

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -0,0 +1,60 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
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 <KWaylandServer/seat_interface.h>
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);
}
}
}

35
src/xwl/xwldrophandler.h Normal file
View file

@ -0,0 +1,35 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef XWLDROPHANDLER_H
#define XWLDROPHANDLER_H
#include <KWaylandServer/datadevice_interface.h>
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