iOS: Send touch events asynchronously to avoid deadlocking UIKit event loop

Although CFRunLoop is documented to support nesting, the UIKit event
delivery machinery is not prepared to handle nested event loops. If the
user starts a nested event loop in response to e.g. a button press/release,
it will deadlock the entire UIKit event machinery, stopping processing
of both screen updates (CATransactions) as well as other events.

This became an issue on iPhone hardware device in iOS 15, but can not be
reproduces on iPads or in the simulator.

To be on the safe side, we deliver all touch events asynchronously,
even if that means the application code will always be one step
behind the event delivered by the operating system.

Fixes: QTBUG-98651
Pick-to: 6.4 6.3 6.2 5.15
Change-Id: Id0a9fa60b7bb7aa98606d46257e99eac144a1080
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Tor Arne Vestbø 2022-06-17 13:29:55 +02:00
parent ca11725659
commit e56b2d7a9e

View File

@ -430,7 +430,10 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::AsynchronousDelivery>(
self.platformWindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values());
} else {
QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::SynchronousDelivery>(
// Send the touch event asynchronously, as the application might spin a recursive
// event loop in response to the touch event (a dialog e.g.), which will deadlock
// the UIKit event delivery system (QTBUG-98651).
QWindowSystemInterface::handleTouchEvent<QWindowSystemInterface::AsynchronousDelivery>(
self.platformWindow->window(), timeStamp, iosIntegration->touchDevice(), m_activeTouches.values());
}
}
@ -536,7 +539,12 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
NSTimeInterval timestamp = event ? event.timestamp : [[NSProcessInfo processInfo] systemUptime];
QIOSIntegration *iosIntegration = static_cast<QIOSIntegration *>(QGuiApplicationPrivate::platformIntegration());
QWindowSystemInterface::handleTouchCancelEvent(self.platformWindow->window(), ulong(timestamp * 1000), iosIntegration->touchDevice());
// Send the touch event asynchronously, as the application might spin a recursive
// event loop in response to the touch event (a dialog e.g.), which will deadlock
// the UIKit event delivery system (QTBUG-98651).
QWindowSystemInterface::handleTouchCancelEvent<QWindowSystemInterface::AsynchronousDelivery>(
self.platformWindow->window(), ulong(timestamp * 1000), iosIntegration->touchDevice());
}
- (int)mapPressTypeToKey:(UIPress*)press withModifiers:(Qt::KeyboardModifiers)qtModifiers text:(QString &)text