From 4c855a9f9ff523e2753157897100393d14bf2f9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 17 Jul 2017 12:13:58 +0200 Subject: Add QPlatformWindow::initialize() for two-step window creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QWindow::create method calls createPlatformWindow, and assigns the result to d->platformWindow. If the platform sends any sort of events synchronously during the creation, the event will be delivered to a QWindow that doesn't have a handle() yet, resulting in noop handling of the event, or crashes. To mitigate this situations, platforms should do as little a possible in the QPlatformWindow constructor, and leave initialization to the new method, where the QWindow will have a handle(). The macOS platform plugin still has a m_initialized guard, to prevent sending geometry changes during initialization, as this will result in a resize event before a show event. This forced behavior seems dubious, but is left for a followup patch. Task-number: QTBUG-61977 Change-Id: I04d32d93391e89d068752b719270438e7024ad46 Reviewed-by: Morten Johan Sørvig Reviewed-by: Simon Hausmann --- src/gui/kernel/qplatformwindow.cpp | 11 ++++++++ src/gui/kernel/qplatformwindow.h | 2 ++ src/gui/kernel/qwindow.cpp | 2 ++ src/plugins/platforms/cocoa/qcocoawindow.h | 4 ++- src/plugins/platforms/cocoa/qcocoawindow.mm | 40 ++++++++++++++++++----------- 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/src/gui/kernel/qplatformwindow.cpp b/src/gui/kernel/qplatformwindow.cpp index a26a23243d..5ece421ea5 100644 --- a/src/gui/kernel/qplatformwindow.cpp +++ b/src/gui/kernel/qplatformwindow.cpp @@ -70,6 +70,17 @@ QPlatformWindow::~QPlatformWindow() { } +/*! + Called as part of QWindow::create(), after constructing + the window. Platforms should prefer to do initialization + here instead of in the constructor, as the platform window + object will be fully constructed, and associated to the + corresponding QWindow, allowing synchronous event delivery. +*/ +void QPlatformWindow::initialize() +{ +} + /*! Returns the window which belongs to the QPlatformWindow */ diff --git a/src/gui/kernel/qplatformwindow.h b/src/gui/kernel/qplatformwindow.h index eead96f2d1..95d8af760c 100644 --- a/src/gui/kernel/qplatformwindow.h +++ b/src/gui/kernel/qplatformwindow.h @@ -74,6 +74,8 @@ public: explicit QPlatformWindow(QWindow *window); virtual ~QPlatformWindow(); + virtual void initialize(); + QWindow *window() const; QPlatformWindow *parent() const; diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index bd52113762..1d8d6dfc95 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -509,6 +509,8 @@ void QWindowPrivate::create(bool recursive, WId nativeHandle) return; } + platformWindow->initialize(); + QObjectList childObjects = q->children(); for (int i = 0; i < childObjects.size(); i ++) { QObject *object = childObjects.at(i); diff --git a/src/plugins/platforms/cocoa/qcocoawindow.h b/src/plugins/platforms/cocoa/qcocoawindow.h index 4b616665f7..e2fb372dae 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.h +++ b/src/plugins/platforms/cocoa/qcocoawindow.h @@ -95,6 +95,8 @@ public: QCocoaWindow(QWindow *tlw, WId nativeHandle = 0); ~QCocoaWindow(); + void initialize() override; + void setGeometry(const QRect &rect) Q_DECL_OVERRIDE; QRect geometry() const Q_DECL_OVERRIDE; void setCocoaGeometry(const QRect &rect); @@ -250,7 +252,7 @@ public: // for QNSView QPointer m_enterLeaveTargetWindow; bool m_windowUnderMouse; - bool m_inConstructor; + bool m_initialized; bool m_inSetVisible; bool m_inSetGeometry; bool m_inSetStyleMask; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 46da26ce35..e6b1a981e6 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -132,8 +132,8 @@ Q_CONSTRUCTOR_FUNCTION(qRegisterNotificationCallbacks) const int QCocoaWindow::NoAlertRequest = -1; -QCocoaWindow::QCocoaWindow(QWindow *tlw, WId nativeHandle) - : QPlatformWindow(tlw) +QCocoaWindow::QCocoaWindow(QWindow *win, WId nativeHandle) + : QPlatformWindow(win) , m_view(nil) , m_nsWindow(0) , m_viewIsEmbedded(false) @@ -141,7 +141,7 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw, WId nativeHandle) , m_lastReportedWindowState(Qt::WindowNoState) , m_windowModality(Qt::NonModal) , m_windowUnderMouse(false) - , m_inConstructor(true) + , m_initialized(false) , m_inSetVisible(false) , m_inSetGeometry(false) , m_inSetStyleMask(false) @@ -165,24 +165,31 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw, WId nativeHandle) { qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::QCocoaWindow" << window(); - QMacAutoReleasePool pool; - if (nativeHandle) { m_view = reinterpret_cast(nativeHandle); [m_view retain]; - } else { + } +} + +void QCocoaWindow::initialize() +{ + qCDebug(lcQpaCocoaWindow) << "QCocoaWindow::initialize" << window(); + + QMacAutoReleasePool pool; + + if (!m_view) { m_view = [[QNSView alloc] initWithCocoaWindow:this]; // Enable high-dpi OpenGL for retina displays. Enabling has the side // effect that Cocoa will start calling glViewport(0, 0, width, height), // overriding any glViewport calls in application code. This is usually not a // problem, except if the appilcation wants to have a "custom" viewport. // (like the hellogl example) - if (tlw->supportsOpenGL()) { - BOOL enable = qt_mac_resolveOption(YES, tlw, "_q_mac_wantsBestResolutionOpenGLSurface", + if (window()->supportsOpenGL()) { + BOOL enable = qt_mac_resolveOption(YES, window(), "_q_mac_wantsBestResolutionOpenGLSurface", "QT_MAC_WANTS_BEST_RESOLUTION_OPENGL_SURFACE"); [m_view setWantsBestResolutionOpenGLSurface:enable]; } - BOOL enable = qt_mac_resolveOption(NO, tlw, "_q_mac_wantsLayer", + BOOL enable = qt_mac_resolveOption(NO, window(), "_q_mac_wantsLayer", "QT_MAC_WANTS_LAYER"); [m_view setWantsLayer:enable]; } @@ -190,10 +197,11 @@ QCocoaWindow::QCocoaWindow(QWindow *tlw, WId nativeHandle) setGeometry(initialGeometry(window(), windowGeometry(), defaultWindowWidth, defaultWindowHeight)); recreateWindowIfNeeded(); - tlw->setGeometry(geometry()); - if (tlw->isTopLevel()) - setWindowIcon(tlw->icon()); - m_inConstructor = false; + window()->setGeometry(geometry()); + if (window()->isTopLevel()) + setWindowIcon(window()->icon()); + + m_initialized = true; } QCocoaWindow::~QCocoaWindow() @@ -1021,8 +1029,10 @@ bool QCocoaWindow::windowShouldClose() void QCocoaWindow::handleGeometryChange() { - // Don't send geometry change event to Qt unless it's ready to handle events - if (m_inConstructor) + // Prevent geometry change during initialization, as that will result + // in a resize event, and Qt expects those to come after the show event. + // FIXME: Remove once we've clarified the Qt behavior for this. + if (!m_initialized) return; // Don't send the geometry change if the QWindow is designated to be -- cgit v1.2.3