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 <paul.tvete@qt.io>
This commit is contained in:
parent
8184ff6bfb
commit
1e36cce9fe
@ -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());
|
||||
}
|
||||
|
@ -150,6 +150,47 @@ void MockCompositor::sendTouchFrame(const QSharedPointer<MockSurface> &surface)
|
||||
processCommand(command);
|
||||
}
|
||||
|
||||
void MockCompositor::sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface)
|
||||
{
|
||||
Command command = makeCommand(Impl::Compositor::sendDataDeviceDataOffer, m_compositor);
|
||||
command.parameters << QVariant::fromValue(surface);
|
||||
processCommand(command);
|
||||
}
|
||||
|
||||
void MockCompositor::sendDataDeviceEnter(const QSharedPointer<MockSurface> &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<MockSurface> &surface)
|
||||
{
|
||||
Command command = makeCommand(Impl::Compositor::sendDataDeviceDrop, m_compositor);
|
||||
command.parameters << QVariant::fromValue(surface);
|
||||
processCommand(command);
|
||||
}
|
||||
|
||||
void MockCompositor::sendDataDeviceLeave(const QSharedPointer<MockSurface> &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<MockSurface> MockCompositor::surface()
|
||||
{
|
||||
QSharedPointer<MockSurface> 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);
|
||||
|
@ -85,6 +85,15 @@ public:
|
||||
static void sendTouchUp(void *data, const QList<QVariant> ¶meters);
|
||||
static void sendTouchMotion(void *data, const QList<QVariant> ¶meters);
|
||||
static void sendTouchFrame(void *data, const QList<QVariant> ¶meters);
|
||||
static void sendDataDeviceDataOffer(void *data, const QList<QVariant> ¶meters);
|
||||
static void sendDataDeviceEnter(void *data, const QList<QVariant> ¶meters);
|
||||
static void sendDataDeviceMotion(void *data, const QList<QVariant> ¶meters);
|
||||
static void sendDataDeviceDrop(void *data, const QList<QVariant> ¶meters);
|
||||
static void sendDataDeviceLeave(void *data, const QList<QVariant> ¶meters);
|
||||
static void waitForStartDrag(void *data, const QList<QVariant> ¶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<MockSurface> &surface, const QPoint &position, int id);
|
||||
void sendTouchUp(const QSharedPointer<MockSurface> &surface, int id);
|
||||
void sendTouchFrame(const QSharedPointer<MockSurface> &surface);
|
||||
void sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface);
|
||||
void sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint &position);
|
||||
void sendDataDeviceMotion(const QPoint &position);
|
||||
void sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface);
|
||||
void sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface);
|
||||
void waitForStartDrag();
|
||||
|
||||
QSharedPointer<MockSurface> surface();
|
||||
|
||||
|
@ -144,6 +144,70 @@ void Compositor::sendTouchFrame(void *data, const QList<QVariant> ¶meters)
|
||||
compositor->m_touch->sendFrame(surface);
|
||||
}
|
||||
|
||||
void Compositor::sendDataDeviceDataOffer(void *data, const QList<QVariant> ¶meters)
|
||||
{
|
||||
Compositor *compositor = static_cast<Compositor *>(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<QVariant> ¶meters)
|
||||
{
|
||||
Compositor *compositor = static_cast<Compositor *>(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<QVariant> ¶meters)
|
||||
{
|
||||
Compositor *compositor = static_cast<Compositor *>(data);
|
||||
Q_ASSERT(compositor);
|
||||
QPoint position = parameters.first().toPoint();
|
||||
compositor->m_data_device_manager->dataDevice()->sendMotion(position);
|
||||
}
|
||||
|
||||
void Compositor::sendDataDeviceDrop(void *data, const QList<QVariant> ¶meters)
|
||||
{
|
||||
Compositor *compositor = static_cast<Compositor *>(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<QVariant> ¶meters)
|
||||
{
|
||||
Compositor *compositor = static_cast<Compositor *>(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<QVariant> ¶meters)
|
||||
{
|
||||
Compositor *compositor = static_cast<Compositor *>(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,18 +376,65 @@ 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()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -36,6 +36,10 @@
|
||||
#include <QBackingStore>
|
||||
#include <QPainter>
|
||||
#include <QScreen>
|
||||
#include <QWindow>
|
||||
#include <QMimeData>
|
||||
#include <QPixmap>
|
||||
#include <QDrag>
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
|
||||
@ -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<MockSurface> 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<MockSurface> 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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user