From da364484aa51ce7c73d39ce0ca444169ce69588e Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Mon, 1 Aug 2016 09:43:59 +0200 Subject: Fix crash when dragging with touch without first having pointer focus Also add drag-and-drop tests for both touch and mouse. Task-number: QTBUG-54756 Change-Id: Ibfff48b1f2377022a8624e28e9f638076187ddca Reviewed-by: Paul Olav Tvete --- src/client/qwaylanddatadevice.cpp | 3 + tests/auto/client/mockcompositor.cpp | 42 ++++++++++++ tests/auto/client/mockcompositor.h | 15 +++++ tests/auto/client/mockinput.cpp | 121 +++++++++++++++++++++++++++++++++++ tests/auto/client/mockinput.h | 18 ++++++ tests/auto/client/tst_client.cpp | 86 +++++++++++++++++++++++++ 6 files changed, 285 insertions(+) diff --git a/src/client/qwaylanddatadevice.cpp b/src/client/qwaylanddatadevice.cpp index 8130578b5..255b13f4c 100644 --- a/src/client/qwaylanddatadevice.cpp +++ b/src/client/qwaylanddatadevice.cpp @@ -107,7 +107,10 @@ void QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon) { m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData)); connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled); + QWaylandWindow *origin = m_display->currentInputDevice()->pointerFocus(); + if (!origin) + origin = m_display->currentInputDevice()->touchFocus(); start_drag(m_dragSource->object(), origin->object(), icon->object(), m_display->currentInputDevice()->serial()); } diff --git a/tests/auto/client/mockcompositor.cpp b/tests/auto/client/mockcompositor.cpp index 7eb683191..83dd52ba2 100644 --- a/tests/auto/client/mockcompositor.cpp +++ b/tests/auto/client/mockcompositor.cpp @@ -150,6 +150,47 @@ void MockCompositor::sendTouchFrame(const QSharedPointer &surface) processCommand(command); } +void MockCompositor::sendDataDeviceDataOffer(const QSharedPointer &surface) +{ + Command command = makeCommand(Impl::Compositor::sendDataDeviceDataOffer, m_compositor); + command.parameters << QVariant::fromValue(surface); + processCommand(command); +} + +void MockCompositor::sendDataDeviceEnter(const QSharedPointer &surface, const QPoint& position) +{ + Command command = makeCommand(Impl::Compositor::sendDataDeviceEnter, m_compositor); + command.parameters << QVariant::fromValue(surface) << QVariant::fromValue(position); + processCommand(command); +} + +void MockCompositor::sendDataDeviceMotion(const QPoint &position) +{ + Command command = makeCommand(Impl::Compositor::sendDataDeviceMotion, m_compositor); + command.parameters << QVariant::fromValue(position); + processCommand(command); +} + +void MockCompositor::sendDataDeviceDrop(const QSharedPointer &surface) +{ + Command command = makeCommand(Impl::Compositor::sendDataDeviceDrop, m_compositor); + command.parameters << QVariant::fromValue(surface); + processCommand(command); +} + +void MockCompositor::sendDataDeviceLeave(const QSharedPointer &surface) +{ + Command command = makeCommand(Impl::Compositor::sendDataDeviceLeave, m_compositor); + command.parameters << QVariant::fromValue(surface); + processCommand(command); +} + +void MockCompositor::waitForStartDrag() +{ + Command command = makeCommand(Impl::Compositor::waitForStartDrag, m_compositor); + processCommand(command); +} + QSharedPointer MockCompositor::surface() { QSharedPointer result; @@ -228,6 +269,7 @@ namespace Impl { Compositor::Compositor() : m_display(wl_display_create()) + , m_startDragSeen(false) , m_time(0) { wl_list_init(&m_outputResources); diff --git a/tests/auto/client/mockcompositor.h b/tests/auto/client/mockcompositor.h index 8731949c0..2f0df4a0f 100644 --- a/tests/auto/client/mockcompositor.h +++ b/tests/auto/client/mockcompositor.h @@ -85,6 +85,15 @@ public: static void sendTouchUp(void *data, const QList ¶meters); static void sendTouchMotion(void *data, const QList ¶meters); static void sendTouchFrame(void *data, const QList ¶meters); + static void sendDataDeviceDataOffer(void *data, const QList ¶meters); + static void sendDataDeviceEnter(void *data, const QList ¶meters); + static void sendDataDeviceMotion(void *data, const QList ¶meters); + static void sendDataDeviceDrop(void *data, const QList ¶meters); + static void sendDataDeviceLeave(void *data, const QList ¶meters); + static void waitForStartDrag(void *data, const QList ¶meters); + +public: + bool m_startDragSeen; private: static void bindCompositor(wl_client *client, void *data, uint32_t version, uint32_t id); @@ -156,6 +165,12 @@ public: void sendTouchMotion(const QSharedPointer &surface, const QPoint &position, int id); void sendTouchUp(const QSharedPointer &surface, int id); void sendTouchFrame(const QSharedPointer &surface); + void sendDataDeviceDataOffer(const QSharedPointer &surface); + void sendDataDeviceEnter(const QSharedPointer &surface, const QPoint &position); + void sendDataDeviceMotion(const QPoint &position); + void sendDataDeviceDrop(const QSharedPointer &surface); + void sendDataDeviceLeave(const QSharedPointer &surface); + void waitForStartDrag(); QSharedPointer surface(); diff --git a/tests/auto/client/mockinput.cpp b/tests/auto/client/mockinput.cpp index dd0512e76..05df8604b 100644 --- a/tests/auto/client/mockinput.cpp +++ b/tests/auto/client/mockinput.cpp @@ -144,6 +144,70 @@ void Compositor::sendTouchFrame(void *data, const QList ¶meters) compositor->m_touch->sendFrame(surface); } +void Compositor::sendDataDeviceDataOffer(void *data, const QList ¶meters) +{ + Compositor *compositor = static_cast(data); + Surface *surface = resolveSurface(parameters.first()); + + Q_ASSERT(compositor); + Q_ASSERT(surface); + + compositor->m_data_device_manager->dataDevice()->sendDataOffer(surface->resource()->client()); +} + +void Compositor::sendDataDeviceEnter(void *data, const QList ¶meters) +{ + Compositor *compositor = static_cast(data); + Surface *surface = resolveSurface(parameters.first()); + QPoint position = parameters.at(1).toPoint(); + + Q_ASSERT(compositor); + Q_ASSERT(surface); + + compositor->m_data_device_manager->dataDevice()->sendEnter(surface, position); +} + +void Compositor::sendDataDeviceMotion(void *data, const QList ¶meters) +{ + Compositor *compositor = static_cast(data); + Q_ASSERT(compositor); + QPoint position = parameters.first().toPoint(); + compositor->m_data_device_manager->dataDevice()->sendMotion(position); +} + +void Compositor::sendDataDeviceDrop(void *data, const QList ¶meters) +{ + Compositor *compositor = static_cast(data); + Surface *surface = resolveSurface(parameters.first()); + + Q_ASSERT(compositor); + Q_ASSERT(surface); + + compositor->m_data_device_manager->dataDevice()->sendDrop(surface); +} + +void Compositor::sendDataDeviceLeave(void *data, const QList ¶meters) +{ + Compositor *compositor = static_cast(data); + Surface *surface = resolveSurface(parameters.first()); + + Q_ASSERT(compositor); + Q_ASSERT(surface); + + compositor->m_data_device_manager->dataDevice()->sendLeave(surface); +} + +void Compositor::waitForStartDrag(void *data, const QList ¶meters) +{ + Compositor *compositor = static_cast(data); + Q_ASSERT(compositor); + while (!compositor->m_startDragSeen) { + wl_display_flush_clients(compositor->m_display); + wl_event_loop_dispatch(compositor->m_loop, 100); + } + compositor->m_startDragSeen = false; +} + Seat::Seat(Compositor *compositor, struct ::wl_display *display) : wl_seat(display, 2) , m_compositor(compositor) @@ -312,11 +376,53 @@ void Touch::sendFrame(Surface *surface) wl_touch_send_frame(resource->handle); } +DataOffer::DataOffer() + : wl_data_offer() +{ + +} + DataDevice::DataDevice(Compositor *compositor) : wl_data_device() , m_compositor(compositor) + , m_dataOffer(nullptr) + , m_focus(nullptr) +{ + +} + +void DataDevice::sendDataOffer(wl_client *client) +{ + m_dataOffer = new QtWaylandServer::wl_data_offer(client, 0, 1); + Resource *resource = resourceMap().value(client); + send_data_offer(resource->handle, m_dataOffer->resource()->handle); +} + +void DataDevice::sendEnter(Surface *surface, const QPoint& position) +{ + uint serial = m_compositor->nextSerial(); + m_focus = surface; + Resource *resource = resourceMap().value(surface->resource()->client()); + send_enter(resource->handle, serial, surface->resource()->handle, position.x(), position.y(), m_dataOffer->resource()->handle); +} + +void DataDevice::sendMotion(const QPoint &position) +{ + uint32_t time = m_compositor->time(); + Resource *resource = resourceMap().value(m_focus->resource()->client()); + send_motion(resource->handle, time, position.x(), position.y()); +} + +void DataDevice::sendDrop(Surface *surface) { + Resource *resource = resourceMap().value(surface->resource()->client()); + send_drop(resource->handle); +} +void DataDevice::sendLeave(Surface *surface) +{ + Resource *resource = resourceMap().value(surface->resource()->client()); + send_leave(resource->handle); } DataDevice::~DataDevice() @@ -324,6 +430,11 @@ DataDevice::~DataDevice() } +void DataDevice::data_device_start_drag(QtWaylandServer::wl_data_device::Resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial) +{ + m_compositor->m_startDragSeen = true; +} + DataDeviceManager::DataDeviceManager(Compositor *compositor, wl_display *display) : wl_data_device_manager(display, 1) , m_compositor(compositor) @@ -336,6 +447,11 @@ DataDeviceManager::~DataDeviceManager() } +DataDevice *DataDeviceManager::dataDevice() const +{ + return m_data_device.data(); +} + void DataDeviceManager::data_device_manager_get_data_device(Resource *resource, uint32_t id, struct ::wl_resource *seat) { if (!m_data_device) @@ -343,4 +459,9 @@ void DataDeviceManager::data_device_manager_get_data_device(Resource *resource, m_data_device->add(resource->client(), id, 1); } +void DataDeviceManager::data_device_manager_create_data_source(QtWaylandServer::wl_data_device_manager::Resource *resource, uint32_t id) +{ + new QtWaylandServer::wl_data_source(resource->client(), id, 1); +} + } diff --git a/tests/auto/client/mockinput.h b/tests/auto/client/mockinput.h index 61c507077..d98328a66 100644 --- a/tests/auto/client/mockinput.h +++ b/tests/auto/client/mockinput.h @@ -127,14 +127,30 @@ private: Compositor *m_compositor; }; +class DataOffer : public QtWaylandServer::wl_data_offer +{ +public: + DataOffer(); +}; + class DataDevice : public QtWaylandServer::wl_data_device { public: DataDevice(Compositor *compositor); + void sendDataOffer(wl_client *client); + void sendEnter(Surface *surface, const QPoint &position); + void sendMotion(const QPoint &position); + void sendDrop(Surface *surface); + void sendLeave(Surface *surface); ~DataDevice(); +protected: + void data_device_start_drag(Resource *resource, struct ::wl_resource *source, struct ::wl_resource *origin, struct ::wl_resource *icon, uint32_t serial) override; + private: Compositor *m_compositor; + QtWaylandServer::wl_data_offer *m_dataOffer; + Surface* m_focus; }; class DataDeviceManager : public QtWaylandServer::wl_data_device_manager @@ -142,9 +158,11 @@ class DataDeviceManager : public QtWaylandServer::wl_data_device_manager public: DataDeviceManager(Compositor *compositor, struct ::wl_display *display); ~DataDeviceManager(); + DataDevice *dataDevice() const; protected: void data_device_manager_get_data_device(Resource *resource, uint32_t id, struct ::wl_resource *seat) Q_DECL_OVERRIDE; + void data_device_manager_create_data_source(Resource *resource, uint32_t id) override; private: Compositor *m_compositor; diff --git a/tests/auto/client/tst_client.cpp b/tests/auto/client/tst_client.cpp index 3fde258d1..5a1143220 100644 --- a/tests/auto/client/tst_client.cpp +++ b/tests/auto/client/tst_client.cpp @@ -36,6 +36,10 @@ #include #include #include +#include +#include +#include +#include #include @@ -141,6 +145,8 @@ private slots: void createDestroyWindow(); void events(); void backingStore(); + void touchDrag(); + void mouseDrag(); private: MockCompositor *compositor; @@ -251,6 +257,86 @@ void tst_WaylandClient::backingStore() QTRY_VERIFY(surface->image.isNull()); } +class DndWindow : public QWindow +{ + Q_OBJECT + +public: + DndWindow(QWindow *parent = 0) + : QWindow(parent) + , dragStarted(false) + { + QImage cursorImage(64,64,QImage::Format_ARGB32); + cursorImage.fill(Qt::blue); + m_dragIcon = QPixmap::fromImage(cursorImage); + } + ~DndWindow(){} + bool dragStarted; + +protected: + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE + { + if (dragStarted) + return; + dragStarted = true; + + QByteArray dataBytes; + QMimeData *mimeData = new QMimeData; + mimeData->setData("application/x-dnditemdata", dataBytes); + QDrag *drag = new QDrag(this); + drag->setMimeData(mimeData); + drag->setPixmap(m_dragIcon); + drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction); + } +private: + QPixmap m_dragIcon; +}; + +void tst_WaylandClient::touchDrag() +{ + DndWindow window; + window.show(); + + QSharedPointer surface; + QTRY_VERIFY(surface = compositor->surface()); + + compositor->setKeyboardFocus(surface); + QTRY_COMPARE(QGuiApplication::focusWindow(), &window); + + const int id = 0; + compositor->sendTouchDown(surface, QPoint(10, 10), id); + compositor->sendTouchMotion(surface, QPoint(20, 20), id); + compositor->sendTouchFrame(surface); + compositor->waitForStartDrag(); + compositor->sendDataDeviceDataOffer(surface); + compositor->sendDataDeviceEnter(surface, QPoint(20, 20)); + compositor->sendDataDeviceMotion( QPoint(21, 21)); + compositor->sendDataDeviceDrop(surface); + compositor->sendDataDeviceLeave(surface); + QTRY_VERIFY(window.dragStarted); +} + +void tst_WaylandClient::mouseDrag() +{ + DndWindow window; + window.show(); + + QSharedPointer surface; + QTRY_VERIFY(surface = compositor->surface()); + + compositor->setKeyboardFocus(surface); + QTRY_COMPARE(QGuiApplication::focusWindow(), &window); + + compositor->sendMousePress(surface, QPoint(10, 10)); + compositor->sendDataDeviceDataOffer(surface); + compositor->sendDataDeviceEnter(surface, QPoint(20, 20)); + compositor->sendDataDeviceMotion( QPoint(21, 21)); + compositor->waitForStartDrag(); + compositor->sendDataDeviceDrop(surface); + compositor->sendDataDeviceLeave(surface); + QTRY_VERIFY(window.dragStarted); +} + int main(int argc, char **argv) { setenv("XDG_RUNTIME_DIR", ".", 1); -- cgit v1.2.3