Move drag logic to seat and introduce SeatInterface::startDrag

A dataDevice has a request to start a drag with multiple parameters.
As kwayland's goal is to turn an event-driven API into a property cache API we
store this data. This patch moves that storage to the Seat as properties of the
active drag, rather than a property of the data device that happened to
initialise it.

This both helps keep a lot of other logic together, but also allows us to expose
a public startDrag method that can be invoked from Kwin's internal surfaces or
xwayland.

Any properties in DataDevice now relate to data being dropped on the device.
This commit is contained in:
David Redondo 2021-09-03 14:27:16 +02:00 committed by David Edmundson
parent ea65068fe0
commit 0aaed33c66
14 changed files with 119 additions and 183 deletions

View file

@ -29,6 +29,11 @@ class KWAYLANDSERVER_EXPORT AbstractDataSource : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
virtual bool isAccepted() const
{
return false;
}
virtual void accept(const QString &mimeType) virtual void accept(const QString &mimeType)
{ {
Q_UNUSED(mimeType); Q_UNUSED(mimeType);

View file

@ -171,9 +171,6 @@ void TestDataDevice::testCreate()
auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>(); auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
QVERIFY(deviceInterface); QVERIFY(deviceInterface);
QCOMPARE(deviceInterface->seat(), m_seatInterface); QCOMPARE(deviceInterface->seat(), m_seatInterface);
QVERIFY(!deviceInterface->dragSource());
QVERIFY(!deviceInterface->origin());
QVERIFY(!deviceInterface->icon());
QVERIFY(!deviceInterface->selection()); QVERIFY(!deviceInterface->selection());
// this will probably fail, we need to make a selection client side // this will probably fail, we need to make a selection client side
@ -269,9 +266,9 @@ void TestDataDevice::testDrag()
dataDevice->startDrag(pointerButtonSerial, dataSource.data(), surface.data()); dataDevice->startDrag(pointerButtonSerial, dataSource.data(), surface.data());
QCOMPARE(dragStartedSpy.wait(500), success); QCOMPARE(dragStartedSpy.wait(500), success);
QCOMPARE(!dragStartedSpy.isEmpty(), success); QCOMPARE(!dragStartedSpy.isEmpty(), success);
QCOMPARE(deviceInterface->dragSource(), success ? sourceInterface : nullptr); QCOMPARE(m_seatInterface->dragSource(), success ? sourceInterface : nullptr);
QCOMPARE(deviceInterface->origin(), success ? surfaceInterface : nullptr); QCOMPARE(m_seatInterface->dragSurface(), success ? surfaceInterface : nullptr);
QVERIFY(!deviceInterface->icon()); QVERIFY(!m_seatInterface->dragIcon());
} }
void TestDataDevice::testDragInternally_data() void TestDataDevice::testDragInternally_data()
@ -350,13 +347,13 @@ void TestDataDevice::testDragInternally()
dataDevice->startDragInternally(pointerButtonSerial, surface.data(), iconSurface.data()); dataDevice->startDragInternally(pointerButtonSerial, surface.data(), iconSurface.data());
QCOMPARE(dragStartedSpy.wait(500), success); QCOMPARE(dragStartedSpy.wait(500), success);
QCOMPARE(!dragStartedSpy.isEmpty(), success); QCOMPARE(!dragStartedSpy.isEmpty(), success);
QVERIFY(!deviceInterface->dragSource()); QVERIFY(!m_seatInterface->dragSource());
QCOMPARE(deviceInterface->origin(), success ? surfaceInterface : nullptr); QCOMPARE(m_seatInterface->dragSurface(), success ? surfaceInterface : nullptr);
if (success) { if (success) {
QCOMPARE(deviceInterface->icon()->surface(), iconSurfaceInterface); QCOMPARE(m_seatInterface->dragIcon()->surface(), iconSurfaceInterface);
} else { } else {
QCOMPARE(deviceInterface->icon(), nullptr); QCOMPARE(m_seatInterface->dragIcon(), nullptr);
} }
} }

View file

@ -11,6 +11,7 @@
#include "../../src/server/datasource_interface.h" #include "../../src/server/datasource_interface.h"
#include "../../src/server/display.h" #include "../../src/server/display.h"
#include "../../src/server/seat_interface.h" #include "../../src/server/seat_interface.h"
#include "../../src/server/seat_interface_p.h"
#include "KWayland/Client/compositor.h" #include "KWayland/Client/compositor.h"
#include "KWayland/Client/connection_thread.h" #include "KWayland/Client/connection_thread.h"
#include "KWayland/Client/datadevice.h" #include "KWayland/Client/datadevice.h"
@ -230,8 +231,8 @@ void TestDragAndDrop::testPointerDragAndDrop()
QVERIFY(dragStartedSpy.wait()); QVERIFY(dragStartedSpy.wait());
QCOMPARE(m_seatInterface->dragSurface(), serverSurface); QCOMPARE(m_seatInterface->dragSurface(), serverSurface);
QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4()); QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4());
QVERIFY(!m_seatInterface->dragSource()->icon()); QVERIFY(!m_seatInterface->dragIcon());
QCOMPARE(m_seatInterface->dragSource()->dragImplicitGrabSerial(), buttonPressSpy.first().first().value<quint32>()); QCOMPARE(SeatInterfacePrivate::get(m_seatInterface)->drag.dragImplicitGrabSerial, buttonPressSpy.first().first().value<quint32>());
QVERIFY(dragEnteredSpy.wait()); QVERIFY(dragEnteredSpy.wait());
QCOMPARE(dragEnteredSpy.count(), 1); QCOMPARE(dragEnteredSpy.count(), 1);
QCOMPARE(dragEnteredSpy.first().first().value<quint32>(), m_display->serial()); QCOMPARE(dragEnteredSpy.first().first().value<quint32>(), m_display->serial());
@ -333,8 +334,8 @@ void TestDragAndDrop::testTouchDragAndDrop()
QVERIFY(dragStartedSpy.wait()); QVERIFY(dragStartedSpy.wait());
QCOMPARE(m_seatInterface->dragSurface(), serverSurface); QCOMPARE(m_seatInterface->dragSurface(), serverSurface);
QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4()); QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4());
QVERIFY(!m_seatInterface->dragSource()->icon()); QVERIFY(!m_seatInterface->dragIcon());
QCOMPARE(m_seatInterface->dragSource()->dragImplicitGrabSerial(), tp->downSerial()); QCOMPARE(SeatInterfacePrivate::get(m_seatInterface)->drag.dragImplicitGrabSerial, tp->downSerial());
QVERIFY(dragEnteredSpy.wait()); QVERIFY(dragEnteredSpy.wait());
QCOMPARE(dragEnteredSpy.count(), 1); QCOMPARE(dragEnteredSpy.count(), 1);
QCOMPARE(dragEnteredSpy.first().first().value<quint32>(), m_display->serial()); QCOMPARE(dragEnteredSpy.first().first().value<quint32>(), m_display->serial());
@ -428,8 +429,8 @@ void TestDragAndDrop::testDragAndDropWithCancelByDestroyDataSource()
QVERIFY(dragStartedSpy.wait()); QVERIFY(dragStartedSpy.wait());
QCOMPARE(m_seatInterface->dragSurface(), serverSurface); QCOMPARE(m_seatInterface->dragSurface(), serverSurface);
QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4()); QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4());
QVERIFY(!m_seatInterface->dragSource()->icon()); QVERIFY(!m_seatInterface->dragIcon());
QCOMPARE(m_seatInterface->dragSource()->dragImplicitGrabSerial(), buttonPressSpy.first().first().value<quint32>()); QCOMPARE(SeatInterfacePrivate::get(m_seatInterface)->drag.dragImplicitGrabSerial, buttonPressSpy.first().first().value<quint32>());
QVERIFY(dragEnteredSpy.wait()); QVERIFY(dragEnteredSpy.wait());
QCOMPARE(dragEnteredSpy.count(), 1); QCOMPARE(dragEnteredSpy.count(), 1);
QCOMPARE(dragEnteredSpy.first().first().value<quint32>(), m_display->serial()); QCOMPARE(dragEnteredSpy.first().first().value<quint32>(), m_display->serial());

View file

@ -39,8 +39,8 @@ void DragAndDropIconPrivate::commit()
position += surface()->offset(); position += surface()->offset();
} }
DragAndDropIcon::DragAndDropIcon(SurfaceInterface *surface, QObject *parent) DragAndDropIcon::DragAndDropIcon(SurfaceInterface *surface)
: QObject(parent) : QObject(surface)
, d(new DragAndDropIconPrivate(surface)) , d(new DragAndDropIconPrivate(surface))
{ {
} }
@ -71,11 +71,6 @@ DataDeviceInterfacePrivate::DataDeviceInterfacePrivate(SeatInterface *seat, Data
{ {
} }
void DataDeviceInterfacePrivate::endDrag()
{
icon.reset();
}
void DataDeviceInterfacePrivate::data_device_start_drag(Resource *resource, void DataDeviceInterfacePrivate::data_device_start_drag(Resource *resource,
wl_resource *sourceResource, wl_resource *sourceResource,
wl_resource *originResource, wl_resource *originResource,
@ -109,22 +104,14 @@ void DataDeviceInterfacePrivate::data_device_start_drag(Resource *resource,
return; return;
} }
} }
// TODO: source is allowed to be null, handled client internally!
source = dataSource; DragAndDropIcon *dragIcon = nullptr;
if (dataSource) {
QObject::connect(dataSource, &AbstractDataSource::aboutToBeDestroyed, q, [this] {
source = nullptr;
});
}
if (iconSurface) { if (iconSurface) {
icon.reset(new DragAndDropIcon(iconSurface)); // drag icon lifespan is mapped to surface lifespan
QObject::connect(iconSurface, &SurfaceInterface::aboutToBeDestroyed, icon.data(), [this] { dragIcon = new DragAndDropIcon(iconSurface);
icon.reset();
});
} }
surface = focusSurface;
drag.serial = serial; drag.serial = serial;
Q_EMIT q->dragStarted(); Q_EMIT q->dragStarted(dataSource, focusSurface, serial, dragIcon);
} }
void DataDeviceInterfacePrivate::data_device_set_selection(Resource *resource, wl_resource *source, uint32_t serial) void DataDeviceInterfacePrivate::data_device_set_selection(Resource *resource, wl_resource *source, uint32_t serial)
@ -198,21 +185,6 @@ SeatInterface *DataDeviceInterface::seat() const
return d->seat; return d->seat;
} }
DataSourceInterface *DataDeviceInterface::dragSource() const
{
return d->source;
}
DragAndDropIcon *DataDeviceInterface::icon() const
{
return d->icon.data();
}
SurfaceInterface *DataDeviceInterface::origin() const
{
return d->proxyRemoteSurface ? d->proxyRemoteSurface.data() : d->surface;
}
DataSourceInterface *DataDeviceInterface::selection() const DataSourceInterface *DataDeviceInterface::selection() const
{ {
return d->selection; return d->selection;
@ -267,23 +239,18 @@ void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 se
} }
// don't update serial, we need it // don't update serial, we need it
} }
auto dragSourceDevice = d->seat->dragSource(); auto dragSource = d->seat->dragSource();
if (!surface || !dragSourceDevice) { if (!surface || !dragSource) {
if (auto s = dragSourceDevice->dragSource()) { if (auto s = dragSource) {
s->dndAction(DataDeviceManagerInterface::DnDAction::None); s->dndAction(DataDeviceManagerInterface::DnDAction::None);
} }
return; return;
} }
if (d->proxyRemoteSurface && d->proxyRemoteSurface == surface) {
// A proxy can not have the remote surface as target. if (dragSource) {
// TODO: do this for all client's surfaces? dragSource->accept(QString());
return;
} }
auto *source = dragSourceDevice->dragSource(); DataOfferInterface *offer = d->createDataOffer(dragSource);
if (source) {
source->setAccepted(false);
}
DataOfferInterface *offer = d->createDataOffer(source);
d->drag.surface = surface; d->drag.surface = surface;
if (d->seat->isDragPointer()) { if (d->seat->isDragPointer()) {
d->drag.posConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this, [this] { d->drag.posConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this, [this] {
@ -314,35 +281,30 @@ void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 se
d->send_enter(serial, surface->resource(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr); d->send_enter(serial, surface->resource(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr);
if (offer) { if (offer) {
offer->sendSourceActions(); offer->sendSourceActions();
auto matchOffers = [source, offer] { auto matchOffers = [dragSource, offer] {
DataDeviceManagerInterface::DnDAction action{DataDeviceManagerInterface::DnDAction::None}; DataDeviceManagerInterface::DnDAction action{DataDeviceManagerInterface::DnDAction::None};
if (source->supportedDragAndDropActions().testFlag(offer->preferredDragAndDropAction())) { if (dragSource->supportedDragAndDropActions().testFlag(offer->preferredDragAndDropAction())) {
action = offer->preferredDragAndDropAction(); action = offer->preferredDragAndDropAction();
} else { } else {
if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy) if (dragSource->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy)
&& offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy)) { && offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Copy)) {
action = DataDeviceManagerInterface::DnDAction::Copy; action = DataDeviceManagerInterface::DnDAction::Copy;
} else if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move) } else if (dragSource->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move)
&& offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move)) { && offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Move)) {
action = DataDeviceManagerInterface::DnDAction::Move; action = DataDeviceManagerInterface::DnDAction::Move;
} else if (source->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask) } else if (dragSource->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask)
&& offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask)) { && offer->supportedDragAndDropActions().testFlag(DataDeviceManagerInterface::DnDAction::Ask)) {
action = DataDeviceManagerInterface::DnDAction::Ask; action = DataDeviceManagerInterface::DnDAction::Ask;
} }
} }
offer->dndAction(action); offer->dndAction(action);
source->dndAction(action); dragSource->dndAction(action);
}; };
d->drag.targetActionConnection = connect(offer, &DataOfferInterface::dragAndDropActionsChanged, source, matchOffers); d->drag.targetActionConnection = connect(offer, &DataOfferInterface::dragAndDropActionsChanged, dragSource, matchOffers);
d->drag.sourceActionConnection = connect(source, &DataSourceInterface::supportedDragAndDropActionsChanged, source, matchOffers); d->drag.sourceActionConnection = connect(dragSource, &AbstractDataSource::supportedDragAndDropActionsChanged, dragSource, matchOffers);
} }
} }
quint32 DataDeviceInterface::dragImplicitGrabSerial() const
{
return d->drag.serial;
}
void DataDeviceInterface::updateProxy(SurfaceInterface *remote) void DataDeviceInterface::updateProxy(SurfaceInterface *remote)
{ {
// TODO: connect destroy signal? // TODO: connect destroy signal?

View file

@ -49,7 +49,7 @@ public:
SurfaceInterface *surface() const; SurfaceInterface *surface() const;
private: private:
explicit DragAndDropIcon(SurfaceInterface *surface, QObject *parent = nullptr); explicit DragAndDropIcon(SurfaceInterface *surface);
friend class DataDeviceInterfacePrivate; friend class DataDeviceInterfacePrivate;
QScopedPointer<DragAndDropIconPrivate> d; QScopedPointer<DragAndDropIconPrivate> d;
}; };
@ -72,18 +72,6 @@ public:
virtual ~DataDeviceInterface(); virtual ~DataDeviceInterface();
SeatInterface *seat() const; SeatInterface *seat() const;
DataSourceInterface *dragSource() const;
SurfaceInterface *origin() const;
/**
* Returns the additional icon attached to the cursor during a drag-and-drop operation.
* This function returns @c null if no drag-and-drop icon has been attached.
*/
DragAndDropIcon *icon() const;
/**
* @returns the serial of the implicit grab which started the drag
*/
quint32 dragImplicitGrabSerial() const;
DataSourceInterface *selection() const; DataSourceInterface *selection() const;
@ -105,16 +93,13 @@ public:
* @param serial The serial to be used for enter/leave * @param serial The serial to be used for enter/leave
*/ */
void updateDragTarget(SurfaceInterface *surface, quint32 serial); void updateDragTarget(SurfaceInterface *surface, quint32 serial);
/**
* Mark this DataDeviceInterface as being a proxy device for @p remote.
*/
void updateProxy(SurfaceInterface *remote); void updateProxy(SurfaceInterface *remote);
wl_client *client(); wl_client *client();
Q_SIGNALS: Q_SIGNALS:
void aboutToBeDestroyed(); void aboutToBeDestroyed();
void dragStarted(); void dragStarted(AbstractDataSource *source, SurfaceInterface *originSurface, quint32 serial, DragAndDropIcon *dragIcon);
void selectionChanged(KWaylandServer::DataSourceInterface *); void selectionChanged(KWaylandServer::DataSourceInterface *);
void selectionCleared(); void selectionCleared();

View file

@ -29,13 +29,9 @@ public:
DataDeviceInterfacePrivate(SeatInterface *seat, DataDeviceInterface *_q, wl_resource *resource); DataDeviceInterfacePrivate(SeatInterface *seat, DataDeviceInterface *_q, wl_resource *resource);
DataOfferInterface *createDataOffer(AbstractDataSource *source); DataOfferInterface *createDataOffer(AbstractDataSource *source);
void endDrag();
SeatInterface *seat; SeatInterface *seat;
DataDeviceInterface *q; DataDeviceInterface *q;
DataSourceInterface *source = nullptr;
SurfaceInterface *surface = nullptr;
QScopedPointer<DragAndDropIcon> icon;
QPointer<DataSourceInterface> selection; QPointer<DataSourceInterface> selection;
QPointer<SurfaceInterface> proxyRemoteSurface; QPointer<SurfaceInterface> proxyRemoteSurface;

View file

@ -118,7 +118,9 @@ void DataSourceInterface::requestData(const QString &mimeType, qint32 fd)
void DataSourceInterface::cancel() void DataSourceInterface::cancel()
{ {
d->send_cancelled(); if (wl_resource_get_version(resource()) >= WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
d->send_cancelled();
}
} }
QStringList DataSourceInterface::mimeTypes() const QStringList DataSourceInterface::mimeTypes() const

View file

@ -46,7 +46,7 @@ public:
wl_client *client() const override; wl_client *client() const override;
bool isAccepted() const; bool isAccepted() const override;
void setAccepted(bool accepted); void setAccepted(bool accepted);
private: private:

View file

@ -171,42 +171,12 @@ void SeatInterfacePrivate::registerDataDevice(DataDeviceInterface *dataDevice)
QObject::connect(dataDevice, &DataDeviceInterface::selectionCleared, q, [this, dataDevice] { QObject::connect(dataDevice, &DataDeviceInterface::selectionCleared, q, [this, dataDevice] {
updateSelection(dataDevice); updateSelection(dataDevice);
}); });
QObject::connect(dataDevice, &DataDeviceInterface::dragStarted, q, [this, dataDevice] { QObject::connect(dataDevice,
const auto dragSerial = dataDevice->dragImplicitGrabSerial(); &DataDeviceInterface::dragStarted,
if (q->hasImplicitPointerGrab(dragSerial)) { q,
drag.mode = Drag::Mode::Pointer; [this](AbstractDataSource *source, SurfaceInterface *origin, quint32 serial, DragAndDropIcon *dragIcon) {
drag.transformation = globalPointer.focus.transformation; q->startDrag(source, origin, serial, dragIcon);
} else if (q->hasImplicitTouchGrab(dragSerial)) { });
drag.mode = Drag::Mode::Touch;
// TODO: touch transformation
} else {
// no implicit grab, abort drag
return;
}
auto *originSurface = dataDevice->origin();
const bool proxied = originSurface->dataProxy();
if (!proxied) {
// origin surface
drag.target = dataDevice;
drag.surface = originSurface;
// TODO: transformation needs to be either pointer or touch
drag.transformation = globalPointer.focus.transformation;
}
drag.source = dataDevice;
drag.destroyConnection = QObject::connect(dataDevice, &DataDeviceInterface::aboutToBeDestroyed, q, [this] {
cancelDrag(display->nextSerial());
});
if (dataDevice->dragSource()) {
drag.dragSourceDestroyConnection = QObject::connect(dataDevice->dragSource(), &AbstractDataSource::aboutToBeDestroyed, q, [this] {
cancelDrag(display->nextSerial());
});
} else {
drag.dragSourceDestroyConnection = QMetaObject::Connection();
}
dataDevice->updateDragTarget(proxied ? nullptr : originSurface, dataDevice->dragImplicitGrabSerial());
Q_EMIT q->dragStarted();
Q_EMIT q->dragSurfaceChanged();
});
// is the new DataDevice for the current keyoard focus? // is the new DataDevice for the current keyoard focus?
if (globalKeyboard.focus.surface) { if (globalKeyboard.focus.surface) {
// same client? // same client?
@ -303,12 +273,10 @@ void SeatInterfacePrivate::cancelDrag(quint32 serial)
void SeatInterfacePrivate::endDrag(quint32 serial) void SeatInterfacePrivate::endDrag(quint32 serial)
{ {
QObject::disconnect(drag.destroyConnection);
QObject::disconnect(drag.dragSourceDestroyConnection); QObject::disconnect(drag.dragSourceDestroyConnection);
DataDeviceInterface *dragTargetDevice = drag.target; DataDeviceInterface *dragTargetDevice = drag.target;
DataDeviceInterface *dragSourceDevice = drag.source; AbstractDataSource *dragSource = drag.source;
DataSourceInterface *dragSource = dragSourceDevice ? dragSourceDevice->dragSource() : nullptr;
if (dragSource) { if (dragSource) {
// TODO: Also check the current drag-and-drop action. // TODO: Also check the current drag-and-drop action.
if (dragTargetDevice && dragSource->isAccepted()) { if (dragTargetDevice && dragSource->isAccepted()) {
@ -316,19 +284,13 @@ void SeatInterfacePrivate::endDrag(quint32 serial)
dragTargetDevice->drop(); dragTargetDevice->drop();
dragSource->dropPerformed(); dragSource->dropPerformed();
} else { } else {
if (wl_resource_get_version(dragSource->resource()) >= WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { dragSource->cancel();
dragSource->cancel();
}
} }
} }
if (dragTargetDevice) { if (dragTargetDevice) {
dragTargetDevice->updateDragTarget(nullptr, serial); dragTargetDevice->updateDragTarget(nullptr, serial);
} }
if (dragSourceDevice) {
auto devicePrivate = DataDeviceInterfacePrivate::get(dragSourceDevice);
devicePrivate->endDrag();
}
drag = Drag(); drag = Drag();
Q_EMIT q->dragSurfaceChanged(); Q_EMIT q->dragSurfaceChanged();
@ -486,12 +448,10 @@ void SeatInterface::notifyPointerMotion(const QPointF &pos)
return; return;
} }
if (isDragPointer()) { if (isDragPointer()) {
const auto *originSurface = dragSource()->origin(); // data device will handle it directly
const bool proxyRemoteFocused = originSurface->dataProxy() && originSurface == focusedSurface; // for xwayland cases we still want to send pointer events
if (!proxyRemoteFocused) { if (!d->dataDevicesForSurface(focusedSurface).isEmpty())
// handled by DataDevice
return; return;
}
} }
if (focusedSurface->lockedPointer() && focusedSurface->lockedPointer()->isLocked()) { if (focusedSurface->lockedPointer() && focusedSurface->lockedPointer()->isLocked()) {
return; return;
@ -743,7 +703,7 @@ void SeatInterface::notifyPointerButton(quint32 button, PointerButtonState state
d->updatePointerButtonSerial(button, serial); d->updatePointerButtonSerial(button, serial);
d->updatePointerButtonState(button, SeatInterfacePrivate::Pointer::State::Released); d->updatePointerButtonState(button, SeatInterfacePrivate::Pointer::State::Released);
if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Pointer) { if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Pointer) {
if (d->drag.source->dragImplicitGrabSerial() != currentButtonSerial) { if (d->drag.dragImplicitGrabSerial != currentButtonSerial) {
// not our drag button - ignore // not our drag button - ignore
return; return;
} }
@ -1100,7 +1060,7 @@ void SeatInterface::notifyTouchUp(qint32 id)
} }
Q_ASSERT(d->globalTouch.ids.contains(id)); Q_ASSERT(d->globalTouch.ids.contains(id));
const qint32 serial = d->display->nextSerial(); const qint32 serial = d->display->nextSerial();
if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Touch && d->drag.source->dragImplicitGrabSerial() == d->globalTouch.ids.value(id)) { if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Touch && d->drag.dragImplicitGrabSerial == d->globalTouch.ids.value(id)) {
// the implicitly grabbing touch point has been upped // the implicitly grabbing touch point has been upped
d->endDrag(serial); d->endDrag(serial);
} }
@ -1174,7 +1134,7 @@ SurfaceInterface *SeatInterface::dragSurface() const
return d->drag.surface; return d->drag.surface;
} }
DataDeviceInterface *SeatInterface::dragSource() const AbstractDataSource *SeatInterface::dragSource() const
{ {
return d->drag.source; return d->drag.source;
} }
@ -1305,4 +1265,45 @@ void SeatInterface::setPrimarySelection(AbstractDataSource *selection)
Q_EMIT primarySelectionChanged(selection); Q_EMIT primarySelectionChanged(selection);
} }
void SeatInterface::startDrag(AbstractDataSource *dragSource, SurfaceInterface *originSurface, int dragSerial, DragAndDropIcon *dragIcon)
{
if (hasImplicitPointerGrab(dragSerial)) {
d->drag.mode = SeatInterfacePrivate::Drag::Mode::Pointer;
d->drag.transformation = d->globalPointer.focus.transformation;
} else if (hasImplicitTouchGrab(dragSerial)) {
d->drag.mode = SeatInterfacePrivate::Drag::Mode::Touch;
// TODO: touch transformation
} else {
// no implicit grab, abort drag
return;
}
d->drag.dragImplicitGrabSerial = dragSerial;
// set initial drag target to ourself
d->drag.surface = originSurface;
// TODO: transformation needs to be either pointer or touch
d->drag.transformation = d->globalPointer.focus.transformation;
d->drag.source = dragSource;
if (dragSource) {
d->drag.dragSourceDestroyConnection = QObject::connect(dragSource, &AbstractDataSource::aboutToBeDestroyed, this, [this] {
d->cancelDrag(d->display->nextSerial());
});
}
d->drag.dragIcon = dragIcon;
if (!d->dataDevicesForSurface(originSurface).isEmpty()) {
d->drag.target = d->dataDevicesForSurface(originSurface)[0];
}
if (d->drag.target) {
d->drag.target->updateDragTarget(originSurface, dragSerial);
}
Q_EMIT dragStarted();
Q_EMIT dragSurfaceChanged();
}
DragAndDropIcon *SeatInterface::dragIcon() const
{
return d->drag.dragIcon;
}
} }

View file

@ -18,6 +18,7 @@ struct wl_resource;
namespace KWaylandServer namespace KWaylandServer
{ {
class AbstractDataSource; class AbstractDataSource;
class DragAndDropIcon;
class DataDeviceInterface; class DataDeviceInterface;
class Display; class Display;
class KeyboardInterface; class KeyboardInterface;
@ -208,7 +209,7 @@ public:
* @returns The DataDeviceInterface which started the drag and drop operation. * @returns The DataDeviceInterface which started the drag and drop operation.
* @see isDrag * @see isDrag
*/ */
DataDeviceInterface *dragSource() const; KWaylandServer::AbstractDataSource *dragSource() const;
/** /**
* Sets the current drag target to @p surface. * Sets the current drag target to @p surface.
* *
@ -618,6 +619,14 @@ public:
KWaylandServer::AbstractDataSource *primarySelection() const; KWaylandServer::AbstractDataSource *primarySelection() const;
void setPrimarySelection(AbstractDataSource *selection); void setPrimarySelection(AbstractDataSource *selection);
void startDrag(AbstractDataSource *source, SurfaceInterface *sourceSurface, int dragSerial = -1, DragAndDropIcon *dragIcon = nullptr);
/**
* Returns the additional icon attached to the cursor during a drag-and-drop operation.
* This function returns @c null if no drag-and-drop is active or no icon has been attached.
*/
DragAndDropIcon *dragIcon() const;
static SeatInterface *get(wl_resource *native); static SeatInterface *get(wl_resource *native);
Q_SIGNALS: Q_SIGNALS:

View file

@ -25,11 +25,13 @@ class DataControlDeviceV1Interface;
class TextInputV2Interface; class TextInputV2Interface;
class TextInputV3Interface; class TextInputV3Interface;
class PrimarySelectionDeviceV1Interface; class PrimarySelectionDeviceV1Interface;
class DragAndDropIcon;
class SeatInterfacePrivate : public QtWaylandServer::wl_seat class SeatInterfacePrivate : public QtWaylandServer::wl_seat
{ {
public: public:
static SeatInterfacePrivate *get(SeatInterface *seat); // exported for unit tests
KWAYLANDSERVER_EXPORT static SeatInterfacePrivate *get(SeatInterface *seat);
SeatInterfacePrivate(SeatInterface *q, Display *display); SeatInterfacePrivate(SeatInterface *q, Display *display);
void sendCapabilities(); void sendCapabilities();
@ -119,11 +121,12 @@ public:
Touch, Touch,
}; };
Mode mode = Mode::None; Mode mode = Mode::None;
DataDeviceInterface *source = nullptr; AbstractDataSource *source = nullptr;
QPointer<SurfaceInterface> surface;
QPointer<DataDeviceInterface> target; QPointer<DataDeviceInterface> target;
SurfaceInterface *surface = nullptr; QPointer<DragAndDropIcon> dragIcon;
QMatrix4x4 transformation; QMatrix4x4 transformation;
QMetaObject::Connection destroyConnection; quint32 dragImplicitGrabSerial = -1;
QMetaObject::Connection dragSourceDestroyConnection; QMetaObject::Connection dragSourceDestroyConnection;
}; };
Drag drag; Drag drag;

View file

@ -936,16 +936,6 @@ bool SurfaceInterface::inhibitsIdle() const
return !d->idleInhibitors.isEmpty(); return !d->idleInhibitors.isEmpty();
} }
void SurfaceInterface::setDataProxy(SurfaceInterface *surface)
{
d->dataProxy = surface;
}
SurfaceInterface *SurfaceInterface::dataProxy() const
{
return d->dataProxy;
}
QPointF SurfaceInterface::mapToBuffer(const QPointF &point) const QPointF SurfaceInterface::mapToBuffer(const QPointF &point) const
{ {
return d->surfaceToBufferMatrix.map(point); return d->surfaceToBufferMatrix.map(point);

View file

@ -310,20 +310,6 @@ public:
*/ */
static SurfaceInterface *get(quint32 id, const ClientConnection *client); static SurfaceInterface *get(quint32 id, const ClientConnection *client);
/**
* Set @p surface as a data proxy for this SurfaceInterface. This enables
* the proxy to conduct drags on the surface's client behalf.
*
* Setting a data proxy is only allowed when the client owning this surface
* has not created a data device itself.
*/
void setDataProxy(SurfaceInterface *surface);
/**
* Returns the data proxy of this SurfaceInterface or null if there
* is none set.
*/
SurfaceInterface *dataProxy() const;
Q_SIGNALS: Q_SIGNALS:
/** /**
* This signal is emitted when the underlying wl_surface resource is about to be freed. * This signal is emitted when the underlying wl_surface resource is about to be freed.

View file

@ -119,7 +119,6 @@ public:
QVector<IdleInhibitorV1Interface *> idleInhibitors; QVector<IdleInhibitorV1Interface *> idleInhibitors;
ViewportInterface *viewportExtension = nullptr; ViewportInterface *viewportExtension = nullptr;
SurfaceInterface *dataProxy = nullptr;
ClientConnection *client = nullptr; ClientConnection *client = nullptr;
protected: protected: