QGuiApplication: fix crash caused by posting fake mouse event

Since Qt expects a mouse press/release event to not change
mouse position in the same event, QGuiApplicationPrivate::processMouseEvent
will detect if a QWindowSystemInterfacePrivate::MouseEvent
tries to do this and convert it to two events; one move
and one press/release.

The problem is that the extra mouse event gets posted. So if
delivering the first event causes a flush in the event queue
(which can easily happen if e.g calling processEvents), the
second event will be processed before the first returns.

On iOS we see a crash with DnD as result of this, since drag
data gets deleted on mouse release, and returning back to a mouse
move after that will cause dangling pointers.

This patch will instead of posting the event, call the
event handler recursively with the faked event as argument.
That way a flush will not cause the "pending" event to be
delivered.

Change-Id: Id9d88053b4859083fedd666584815016d67ac51b
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@digia.com>
Reviewed-by: Laszlo Agocs <laszlo.agocs@digia.com>
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
bb10
Richard Moe Gustavsen 2014-04-04 12:01:03 +02:00 committed by The Qt Project
parent e30ae1268f
commit b8d0fac5a9
1 changed files with 10 additions and 6 deletions

View File

@ -1609,14 +1609,17 @@ void QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePriv
void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *e)
{
QEvent::Type type;
// move first
Qt::MouseButtons stateChange = e->buttons ^ buttons;
const bool frameStrut = e->type == QWindowSystemInterfacePrivate::FrameStrutMouse;
if (e->globalPos != QGuiApplicationPrivate::lastCursorPosition && (stateChange != Qt::NoButton)) {
QWindowSystemInterfacePrivate::MouseEvent * newMouseEvent =
new QWindowSystemInterfacePrivate::MouseEvent(e->window.data(), e->timestamp, e->type, e->localPos, e->globalPos, e->buttons, e->modifiers);
QWindowSystemInterfacePrivate::windowSystemEventQueue.prepend(newMouseEvent); // just in case the move triggers a new event loop
stateChange = Qt::NoButton;
// A mouse event should not change both position and buttons at the same time. Instead we
// should first send a move event followed by a button changed event. Since this is not the case
// with the current event, we fake a move-only event that we recurse and process first. This
// will update the global mouse position and cause the second event to be a button only event.
QWindowSystemInterfacePrivate::MouseEvent moveEvent(e->window.data(),
e->timestamp, e->type, e->localPos, e->globalPos, buttons, e->modifiers);
processMouseEvent(&moveEvent);
Q_ASSERT(e->globalPos == QGuiApplicationPrivate::lastCursorPosition);
// continue with processing mouse button change event
}
QWindow *window = e->window.data();
@ -1635,6 +1638,7 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo
Qt::MouseButton button = Qt::NoButton;
bool doubleClick = false;
const bool frameStrut = e->type == QWindowSystemInterfacePrivate::FrameStrutMouse;
if (QGuiApplicationPrivate::lastCursorPosition != globalPoint) {
type = frameStrut ? QEvent::NonClientAreaMouseMove : QEvent::MouseMove;