Client: Don't freeze in QDrag::exec if there was no drag focus

002fade6 fixed a crash when starting a drag without a valid focus, but
there is still a problem, because QDrag::exec will never return because
it's waiting indefinitely in an event loop.

- QWaylandDataDevice::startDrag can now fail by returning false.
- When starting a drag fails, we cancel the drag through
QWaylandDrag::cancelDrag wrapped in invokeMethod.
- Also, don't unnecessarily create a data_source if we cannot start a
drag.

[ChangeLog][QPA plugin] Fixed a freeze that happened when starting a
drag-and-drop operation without a valid source surface.

Change-Id: Iea19b0c92c196a44d1274a966bee4ff519632d34
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
Johan Klokkhammer Helsing 2019-08-21 13:44:05 +02:00 committed by Johan Helsing
parent d87b6d8231
commit 59486fdf3b
3 changed files with 18 additions and 10 deletions

View File

@ -102,19 +102,22 @@ QWaylandDataOffer *QWaylandDataDevice::dragOffer() const
return m_dragOffer.data();
}
void QWaylandDataDevice::startDrag(QMimeData *mimeData, QWaylandWindow *icon)
bool 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();
if (origin)
start_drag(m_dragSource->object(), origin->object(), icon->object(), m_display->currentInputDevice()->serial());
else
if (!origin) {
qCDebug(lcQpaWayland) << "Couldn't start a drag because the origin window could not be found.";
return false;
}
m_dragSource.reset(new QWaylandDataSource(m_display->dndSelectionHandler(), mimeData));
connect(m_dragSource.data(), &QWaylandDataSource::cancelled, this, &QWaylandDataDevice::dragSourceCancelled);
start_drag(m_dragSource->object(), origin->object(), icon->object(), m_display->currentInputDevice()->serial());
return true;
}
void QWaylandDataDevice::cancelDrag()

View File

@ -89,7 +89,7 @@ public:
#if QT_CONFIG(draganddrop)
QWaylandDataOffer *dragOffer() const;
void startDrag(QMimeData *mimeData, QWaylandWindow *icon);
bool startDrag(QMimeData *mimeData, QWaylandWindow *icon);
void cancelDrag();
#endif

View File

@ -66,8 +66,13 @@ void QWaylandDrag::startDrag()
{
QBasicDrag::startDrag();
QWaylandWindow *icon = static_cast<QWaylandWindow *>(shapedPixmapWindow()->handle());
m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), icon);
icon->addAttachOffset(-drag()->hotSpot());
if (m_display->currentInputDevice()->dataDevice()->startDrag(drag()->mimeData(), icon)) {
icon->addAttachOffset(-drag()->hotSpot());
} else {
// Cancelling immediately does not work, since the event loop for QDrag::exec is started
// after this function returns.
QMetaObject::invokeMethod(this, [this](){ cancelDrag(); }, Qt::QueuedConnection);
}
}
void QWaylandDrag::cancel()