Fix UITouch event handling on tvOS

On tvOS touchesEnded: occasionally gets called with touches that have
not been passed via the touchesBegan:. When this happens previously
cached touch event (that HAVE been passed to touchesBegan:) are no
longer valid.
This causes a crash when testing if new touches contain old ones (since
NSSet dereferences the needle which is no longer valid).

Fix uses the unique (unsigned int) hash that UIKIT assigns to the
UITouch instance so cached copies are never accessed.

Furthermore, tvOS only supports single touch so now just clearing cache
when touch has ended.

Task-number: QTBUG-84383
Pick-to: 5.15
Pick-to: 5.12
Change-Id: I7592cdde74ce834285e7b14196171f6b57736cc8
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Mike Krus 2020-05-22 15:38:24 +01:00
parent 1f3af0f35c
commit 428115340b

View File

@ -57,7 +57,7 @@
Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
@implementation QUIView {
QHash<UITouch *, QWindowSystemInterface::TouchPoint> m_activeTouches;
QHash<NSUInteger, QWindowSystemInterface::TouchPoint> m_activeTouches;
UITouch *m_activePencilTouch;
int m_nextTouchId;
NSMutableArray<UIAccessibilityElement *> *m_accessibleElements;
@ -404,9 +404,19 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
}
#endif
for (UITouch *uiTouch : m_activeTouches.keys()) {
QWindowSystemInterface::TouchPoint &touchPoint = m_activeTouches[uiTouch];
if (![touches containsObject:uiTouch]) {
if (m_activeTouches.isEmpty())
return;
for (auto it = m_activeTouches.begin(); it != m_activeTouches.end(); ++it) {
auto hash = it.key();
QWindowSystemInterface::TouchPoint &touchPoint = it.value();
UITouch *uiTouch = nil;
for (UITouch *touch in touches) {
if (touch.hash == hash) {
uiTouch = touch;
break;
}
}
if (!uiTouch) {
touchPoint.state = Qt::TouchPointStationary;
} else {
touchPoint.state = state;
@ -438,8 +448,6 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
}
}
}
if (m_activeTouches.isEmpty())
return;
if ([self.window isKindOfClass:[QUIWindow class]] &&
!static_cast<QUIWindow *>(self.window).sendingEvent) {
@ -475,9 +483,9 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
m_activePencilTouch = touch;
} else
{
Q_ASSERT(!m_activeTouches.contains(touch));
Q_ASSERT(!m_activeTouches.contains(touch.hash));
#endif
m_activeTouches[touch].id = m_nextTouchId++;
m_activeTouches[touch.hash].id = m_nextTouchId++;
#if QT_CONFIG(tabletevent)
}
#endif
@ -504,6 +512,7 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
[self handleTouches:touches withEvent:event withState:Qt::TouchPointReleased withTimestamp:ulong(event.timestamp * 1000)];
// Remove ended touch points from the active set:
#ifndef Q_OS_TVOS
for (UITouch *touch in touches) {
#if QT_CONFIG(tabletevent)
if (touch.type == UITouchTypeStylus) {
@ -511,9 +520,14 @@ Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet")
} else
#endif
{
m_activeTouches.remove(touch);
m_activeTouches.remove(touch.hash);
}
}
#else
// tvOS only supports single touch
m_activeTouches.clear();
#endif
if (m_activeTouches.isEmpty() && !m_activePencilTouch)
m_nextTouchId = 0;
}