Add QPlatformWindow::initialize() for two-step window creation

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 <morten.sorvig@qt.io>
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
bb10
Tor Arne Vestbø 2017-07-17 12:13:58 +02:00
parent 1e8a687a3a
commit 4c855a9f9f
5 changed files with 43 additions and 16 deletions

View File

@ -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
*/

View File

@ -74,6 +74,8 @@ public:
explicit QPlatformWindow(QWindow *window);
virtual ~QPlatformWindow();
virtual void initialize();
QWindow *window() const;
QPlatformWindow *parent() const;

View File

@ -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);

View File

@ -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<QWindow> m_enterLeaveTargetWindow;
bool m_windowUnderMouse;
bool m_inConstructor;
bool m_initialized;
bool m_inSetVisible;
bool m_inSetGeometry;
bool m_inSetStyleMask;

View File

@ -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<NSView *>(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