From 04efbda0dff42dad2aa8f1f0cf6552ec1477e3a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 21 Aug 2023 17:24:40 +0200 Subject: [PATCH] macOS: Use enumerateWindowsWithOptions to implement QCocoaScreen::topLevelAt() The [NSWidow windowNumberAtPoint:belowWindowWithWindowNumber] API has issues with sometimes being out of sync with the window server, resulting in failing to hit test windows that we know are there. This has manifested in flakeyness in our tests, for example in tst_QWindow's testsInputEvents: http://testresults.qt.io/grafana/goto/YNGj7TgIg A workaround is to call [NSWindow windowNumbersWithOptions:0] to force a sync, but we might as well use the more modern block based API to iterate our own windows in Z-order. This API seems to do the required sync on our behalf, or at least doesn't operate on stale data. The logic has been otherwise kept as is, including treating non-top-level windows as candidates for hit testing, which seems strange for a function named topLevelAt(). This is to be investigated further. Task-number: QTBUG-108402 Task-number: QTBUG-115945 Change-Id: I5599881c381a0a673d262e4b9585e2c6798c9810 Reviewed-by: Timur Pocheptsov (cherry picked from commit 108d2e44867acfa98c3b0c211d9b48f39d10efa9) Reviewed-by: Qt Cherry-pick Bot --- src/plugins/platforms/cocoa/qcocoascreen.mm | 52 ++++++++++----------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index f12f3f8ecc3..7f1995761a7 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -523,39 +524,36 @@ QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingType QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const { - NSPoint screenPoint = mapToNative(point); + __block QWindow *window = nullptr; + [NSApp enumerateWindowsWithOptions:NSWindowListOrderedFrontToBack + usingBlock:^(NSWindow *nsWindow, BOOL *stop) { + if (!nsWindow) + return; - // Search (hit test) for the top-level window. [NSWidow windowNumberAtPoint: - // belowWindowWithWindowNumber] may return windows that are not interesting - // to Qt. The search iterates until a suitable window or no window is found. - NSInteger topWindowNumber = 0; - QWindow *window = nullptr; - do { - // Get the top-most window, below any previously rejected window. - topWindowNumber = [NSWindow windowNumberAtPoint:screenPoint - belowWindowWithWindowNumber:topWindowNumber]; + // Continue the search if the window does not belong to Qt + if (![nsWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) + return; - // Continue the search if the window does not belong to this process. - NSWindow *nsWindow = [NSApp windowWithWindowNumber:topWindowNumber]; - if (!nsWindow) - continue; + QCocoaWindow *cocoaWindow = qnsview_cast(nsWindow.contentView).platformWindow; + if (!cocoaWindow) + return; - // Continue the search if the window does not belong to Qt. - if (![nsWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) - continue; + QWindow *w = cocoaWindow->window(); + if (!w->isVisible()) + return; - QCocoaWindow *cocoaWindow = qnsview_cast(nsWindow.contentView).platformWindow; - if (!cocoaWindow) - continue; - window = cocoaWindow->window(); + if (!QHighDpi::toNativePixels(w->geometry(), w).contains(point)) + return; - // Continue the search if the window is not a top-level window. - if (!window->isTopLevel()) - continue; + window = w; - // Stop searching. The current window is the correct window. - break; - } while (topWindowNumber > 0); + // Continue the search if the window is not a top-level window + if (!window->isTopLevel()) + return; + + *stop = true; + } + ]; return window; }