Details
-
Bug
-
Resolution: Done
-
P2: Important
-
None
-
5.10
-
None
Description
QWindow calls QPlatformIntegration::createPlatformWindow() from create(), and assigns the result to d->platformWindow.
There's currently nothing preventing the platform from delivering synchronous QPA events during that createPlatformWindow call, in which the QWindow will not have a valid handle() yet.
This can typically happen when creating the native window handle results in native callbacks for geometry or expose changes, as seen on e.g. macOS and Windows:
* frame #0: 0x00007fff892b891a libsystem_kernel.dylib`__wait4_nocancel + 10 frame #1: 0x00007fff8c48921c libsystem_c.dylib`system + 459 frame #2: 0x000000010695ada4 QtTest`stackTrace() + 164 frame #3: 0x000000010695acb5 QtTest`QTest::FatalSignalHandler::signal(int) + 197 frame #4: 0x00007fff94fe0f1a libsystem_platform.dylib`_sigtramp + 26 frame #5: 0x0000000108c35a35 libqcocoa.dylib`QRasterBackingStore::beginPaint(QRegion const&) + 37 frame #6: 0x00000001060438cc QtGui`QBackingStore::beginPaint(QRegion const&) + 364 frame #7: 0x0000000105e8a739 tst_qbackingstore`Window::exposeEvent(QExposeEvent*) + 89 frame #8: 0x0000000105ed0d4e QtGui`QWindow::event(QEvent*) + 254 frame #9: 0x00000001065b30cf QtCore`QCoreApplicationPrivate::notify_helper(QObject*, QEvent*) + 303 frame #10: 0x00000001065b2d2d QtCore`QCoreApplication::notify(QObject*, QEvent*) + 45 frame #11: 0x00000001065b2c84 QtCore`QCoreApplication::notifyInternal2(QObject*, QEvent*) + 164 frame #12: 0x0000000105ec54d4 QtGui`QGuiApplicationPrivate::processExposeEvent(QWindowSystemInterfacePrivate::ExposeEvent*) + 308 frame #13: 0x0000000105ea58f3 QtGui`bool QWindowSystemInterfacePrivate::handleWindowSystemEvent<QWindowSystemInterface::SynchronousDelivery>(QWindowSystemInterfacePrivate::WindowSystemEvent*) + 99 frame #14: 0x0000000105eab903 QtGui`void QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(QWindow*, QRegion const&) + 467 frame #15: 0x0000000108b4edfa libqcocoa.dylib`QCocoaWindow::handleExposeEvent(QRegion const&) + 618 frame #16: 0x0000000108b556ea libqcocoa.dylib`-[QNSView drawRect:] + 922 frame #17: 0x00007fff8fc6c689 AppKit`-[NSView _drawRect:clip:] + 4238 frame #18: 0x00007fff8fc6acfa AppKit`-[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 1875 frame #19: 0x00007fff8fc6b0fe AppKit`-[NSView _recursiveDisplayAllDirtyWithLockFocus:visRect:] + 2903 frame #20: 0x00007fff8fc68b96 AppKit`-[NSView _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 913 frame #21: 0x00007fff8fc682f3 AppKit`-[NSThemeFrame _recursiveDisplayRectIfNeededIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:topView:] + 333 frame #22: 0x00007fff8fc6468b AppKit`-[NSView _displayRectIgnoringOpacity:isVisibleRect:rectIsVisibleRectForView:] + 2761 frame #23: 0x00007fff8fc1c1ae AppKit`-[NSView displayIfNeeded] + 1876 frame #24: 0x00007fff8fc612af AppKit`-[NSThemeFrame handleSetFrameCommonRedisplay] + 251 frame #25: 0x00007fff8fc5990d AppKit`-[NSWindow _setFrameCommon:display:stashSize:] + 3570 frame #26: 0x00007fff8fd16895 AppKit`-[NSWindow setFrame:display:animate:] + 147 frame #27: 0x00007fff8fe0d6e3 AppKit`-[NSWindow zoom:] + 808 frame #28: 0x0000000108b4b3e0 libqcocoa.dylib`QCocoaWindow::applyWindowState(QFlags<Qt::WindowState>) + 848 frame #29: 0x0000000108b492fa libqcocoa.dylib`QCocoaWindow::recreateWindowIfNeeded() + 1946 frame #30: 0x0000000108b488b4 libqcocoa.dylib`QCocoaWindow::QCocoaWindow(QWindow*, unsigned long long) + 1140 frame #31: 0x0000000108b44b64 libqcocoa.dylib`QCocoaIntegration::createPlatformWindow(QWindow*) const + 36 frame #32: 0x0000000105ecd71c QtGui`QWindowPrivate::create(bool, unsigned long long) + 108 frame #33: 0x0000000105ecbc7e QtGui`QWindowPrivate::setVisible(bool) + 382
00000081`9dafda90 00007ff9`3cc5a74e Qt5Guid!QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::DefaultDelivery>(class QWindow * window = 0x000001d7`adb08cf0, class QRect * newRect = 0x00000081`9dafdc64, class QRect * oldRect = 0x00000081`9dafdd80)+0x13 [d:\projects\qt5-dev\qtbase\src\gui\kernel\qwindowsysteminterface.cpp @ 281] 00000081`9dafdb00 00007ff9`33acf2bd qwindowsd!QWindowsIntegration::createPlatformWindow(class QWindow * window = 0x000001d7`adb08cf0)+0x7ee [d:\projects\qt5-dev\qtbase\src\plugins\platforms\windows\qwindowsintegration.cpp @ 357] 00000081`9dafde80 00007ff9`33ac9c07 Qt5Guid!QWindowPrivate::create(bool recursive = false, unsigned int64 nativeHandle = 0)+0xad [d:\projects\qt5-dev\qtbase\src\gui\kernel\qwindow.cpp @ 503] 00000081`9dafdf80 00000000`5a5d1d4d Qt5Guid!QWindow::create(void)+0x27 [d:\projects\qt5-dev\qtbase\src\gui\kernel\qwindow.cpp @ 622] 00000081`9dafdfc0 00000000`5a5cf366 Qt5Widgetsd!QWidgetPrivate::create_sys(unsigned int64 window = 0, bool initializeWindow = true, bool destroyOldWindow = true)+0x57d [d:\projects\qt5-dev\qtbase\src\widgets\kernel\qwidget.cpp @ 1479] 00000081`9dafe100 00000000`5a5c6deb Qt5Widgetsd!QWidget::create(unsigned int64 window = 0, bool initializeWindow = true, bool destroyOldWindow = true)+0x2e6 [d:\projects\qt5-dev\qtbase\src\widgets\kernel\qwidget.cpp @ 1340] 00000081`9dafe170 00000000`5a5c73f9 Qt5Widgetsd!QWidget::setVisible(bool visible = true)+0x15b [d:\projects\qt5-dev\qtbase\src\widgets\kernel\qwidget.cpp @ 8169] 00000081`9dafe230 00007ff6`076081b0 Qt5Widgetsd!QWidget::show(void)+0x79 [d:\projects\qt5-dev\qtbase\src\widgets\kernel\qwidget.cpp @ 7770]
All user and Qt code should of course ideally guard against this, checking for a handle(), but we have a few cases that don't, e.g. QBackingStore, resulting in crashes.
The bigger issue however is that those events are sent from the platform thinking they will be handled, so we may end up sending only one expose event, which gets ignored (during painting, by QBackingStore once that's fixed), and then the window never sees another expose event.
One way to fix this would be to tag QWindows during create, and teach QPA to que up any events for window in this state, even if sent synchronously or flushed explicitly, and then have QWindowPrivate::create flush those events once done creating the window.
The downside is that events that the native platform expected synchronous delivery of, are now delivered async, so we should probably couple this with adding a QPlatformWindow::initialize(), that QWindowPrivate::create() calls after createPlatformWindow().
Ideally platforms would do as little as possible in the QPlatformWindow constructor, and move all init logic to initialize(). The former solution with QPA having logic to defer events, would be a safe-guard for platform that still use the constructor for init, or for the cases where things in the constructor accidentally trigger events.
Attachments
Issue Links
- resulted from
-
QTBUG-50414 OS X: Move expose event handling to [QNSView drawRect]
- Closed
- resulted in
-
QTBUG-62177 dev branch/(5.10): tst_qquickwindow::attachedProperty() fails
- Closed