From 9c3a58a913a7e59359146264ee59d40d703d4db2 Mon Sep 17 00:00:00 2001
From: Dyami Caliri <dyami@dragonframe.com>
Date: Wed, 27 Aug 2014 09:27:50 -0700
Subject: [PATCH] Recreate child windows when changing screens

When setting a new screen, the code calls QWindow::destroy(), which
recursively destroys all child windows. It then calls create() on the
top-level window, leaving child windows destroyed. This causes crashes
if you have embedded native widgets.

Task-number: QTBUG-40817
Change-Id: Iaace2589f48bbfd5faaf5ff95357ff43b310504a
Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
Reviewed-by: Laszlo Agocs <laszlo.agocs@digia.com>
---
 src/gui/kernel/qwindow.cpp                    | 34 ++++++++++-------
 src/gui/kernel/qwindow_p.h                    |  1 +
 .../kernel/qwidget_window/qwidget_window.pro  |  2 +-
 .../qwidget_window/tst_qwidget_window.cpp     | 37 +++++++++++++++++++
 4 files changed, 60 insertions(+), 14 deletions(-)
 mode change 100644 => 100755 tests/auto/widgets/kernel/qwidget_window/qwidget_window.pro
 mode change 100644 => 100755 tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp

diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index d6f9fad0707..6724d68c95b 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -367,12 +367,31 @@ void QWindowPrivate::setScreen(QScreen *newScreen, bool recreate)
         if (newScreen) {
             q->connect(screen, SIGNAL(destroyed(QObject*)), q, SLOT(screenDestroyed(QObject*)));
             if (shouldRecreate)
-                q->create();
+                create(true);
         }
         emit q->screenChanged(newScreen);
     }
 }
 
+void QWindowPrivate::create(bool recursive)
+{
+    Q_Q(QWindow);
+    if (!platformWindow) {
+        platformWindow = QGuiApplicationPrivate::platformIntegration()->createPlatformWindow(q);
+        QObjectList childObjects = q->children();
+        for (int i = 0; i < childObjects.size(); i ++) {
+            QObject *object = childObjects.at(i);
+            if (object->isWindowType()) {
+                QWindow *window = static_cast<QWindow *>(object);
+                if (recursive)
+                    window->d_func()->create(true);
+                if (window->d_func()->platformWindow)
+                    window->d_func()->platformWindow->setParent(platformWindow);
+            }
+        }
+    }
+}
+
 void QWindowPrivate::clearFocusObject()
 {
 }
@@ -490,18 +509,7 @@ bool QWindow::isVisible() const
 void QWindow::create()
 {
     Q_D(QWindow);
-    if (!d->platformWindow) {
-        d->platformWindow = QGuiApplicationPrivate::platformIntegration()->createPlatformWindow(this);
-        QObjectList childObjects = children();
-        for (int i = 0; i < childObjects.size(); i ++) {
-            QObject *object = childObjects.at(i);
-            if(object->isWindowType()) {
-                QWindow *window = static_cast<QWindow *>(object);
-                if (window->d_func()->platformWindow)
-                    window->d_func()->platformWindow->setParent(d->platformWindow);
-            }
-        }
-    }
+    d->create(false);
 }
 
 /*!
diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h
index 4305edea51f..51d78bb9cd1 100644
--- a/src/gui/kernel/qwindow_p.h
+++ b/src/gui/kernel/qwindow_p.h
@@ -132,6 +132,7 @@ public:
     void _q_clearAlert();
 
     void setScreen(QScreen *newScreen, bool recreate);
+    void create(bool recursive);
 
     virtual void clearFocusObject();
 
diff --git a/tests/auto/widgets/kernel/qwidget_window/qwidget_window.pro b/tests/auto/widgets/kernel/qwidget_window/qwidget_window.pro
old mode 100644
new mode 100755
index baa2823f9a4..d61681d5cb8
--- a/tests/auto/widgets/kernel/qwidget_window/qwidget_window.pro
+++ b/tests/auto/widgets/kernel/qwidget_window/qwidget_window.pro
@@ -1,7 +1,7 @@
 CONFIG += testcase
 CONFIG += parallel_test
 TARGET = tst_qwidget_window
-QT += widgets testlib
+QT += widgets testlib core-private gui-private
 SOURCES  += tst_qwidget_window.cpp
 
 x11 {
diff --git a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp
old mode 100644
new mode 100755
index d6b7fc20edc..87e1c7a4f3b
--- a/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp
+++ b/tests/auto/widgets/kernel/qwidget_window/tst_qwidget_window.cpp
@@ -52,6 +52,9 @@
 #include <qlistwidget.h>
 #include <qpushbutton.h>
 #include <qboxlayout.h>
+#include <qtabwidget.h>
+#include <qlabel.h>
+#include <private/qwindow_p.h>
 
 static inline void setFrameless(QWidget *w)
 {
@@ -96,6 +99,8 @@ private slots:
 #endif
 
     void tst_qtbug35600();
+    void tst_recreateWindow_QTBUG40817();
+
 };
 
 void tst_QWidget_window::initTestCase()
@@ -598,5 +603,37 @@ void tst_QWidget_window::tst_qtbug35600()
     // QTBUG-35600: program may crash here or on exit
 }
 
+void tst_QWidget_window::tst_recreateWindow_QTBUG40817()
+{
+    QTabWidget tab;
+
+    QWidget *w = new QWidget;
+    tab.addTab(w, "Tab1");
+    QVBoxLayout *vl = new QVBoxLayout(w);
+    QLabel *lbl = new QLabel("HELLO1");
+    lbl->setObjectName("label1");
+    vl->addWidget(lbl);
+    w = new QWidget;
+    tab.addTab(w, "Tab2");
+    vl = new QVBoxLayout(w);
+    lbl = new QLabel("HELLO2");
+    lbl->setAttribute(Qt::WA_NativeWindow);
+    lbl->setObjectName("label2");
+    vl->addWidget(lbl);
+
+    tab.show();
+
+    QVERIFY(QTest::qWaitForWindowExposed(&tab));
+
+    QWindow *win = tab.windowHandle();
+    win->destroy();
+    QWindowPrivate *p = qt_window_private(win);
+    p->create(true);
+    win->show();
+
+    tab.setCurrentIndex(1);
+}
+
+
 QTEST_MAIN(tst_QWidget_window)
 #include "tst_qwidget_window.moc"
-- 
GitLab