From a094af001795c9651b299d700a992150d1aba33a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?B=C5=82a=C5=BCej=20Szczygie=C5=82?= <spaz16@wp.pl>
Date: Fri, 16 Oct 2015 22:51:59 +0200
Subject: [PATCH] xcb: Use a placeholder QScreen when there are no outputs
 connected

If no screens are available, windows could disappear, could stop rendering
graphics, or the application could crash.  This is a real use case in several
scenarios: with x11vnc, when all monitors are physically disconnected from a
desktop machine, or in some cases even when the monitor sleeps.  Now when the
last screen is disconnected, it is transformed into a fake screen.  When a
physical screen appears, the fake QScreen is transformed into a representation
of the physical screen.  Every virtual desktop has its own fake screen, and
primary screens must belong to the primary virtual desktop. It fixes updating
screen geometry on temporarily disabled screens in the middle of the mode
switch.

Expected results: Windows don't disappear, the application doesn't
crash, and QMenu is displayed on the appropriate screen.

This reverts patch 51ada7734ad780178ecced11e0dff454dfc2e5f2

Change-Id: I6e8eb682b0c8425d08ffdaecbd4c6c7700c914b4
Task-number: QTBUG-42985
Reviewed-by: Shawn Rutledge <shawn.rutledge@theqtcompany.com>
---
 src/gui/kernel/qscreen.cpp                   |   4 +-
 src/plugins/platforms/xcb/qxcbconnection.cpp | 273 +++++++++++++++------------
 src/plugins/platforms/xcb/qxcbconnection.h   |  10 +-
 src/plugins/platforms/xcb/qxcbscreen.cpp     |  49 +++--
 src/plugins/platforms/xcb/qxcbscreen.h       |  10 +-
 src/plugins/platforms/xcb/qxcbwindow.cpp     |  38 +---
 src/plugins/platforms/xcb/qxcbwindow.h       |   1 -
 7 files changed, 212 insertions(+), 173 deletions(-)

Index: b/src/gui/kernel/qscreen.cpp
===================================================================
--- a/src/gui/kernel/qscreen.cpp
+++ b/src/gui/kernel/qscreen.cpp
@@ -115,8 +115,8 @@ QScreen::~QScreen()
     bool movingFromVirtualSibling = primaryScreen && primaryScreen->handle()->virtualSiblings().contains(handle());
 
     // Move any leftover windows to the primary screen
-    foreach (QWindow *window, QGuiApplication::topLevelWindows()) {
-        if (window->screen() != this)
+    foreach (QWindow *window, QGuiApplication::allWindows()) {
+        if (!window->isTopLevel() || window->screen() != this)
             continue;
 
         const bool wasVisible = window->isVisible();
Index: b/src/plugins/platforms/xcb/qxcbconnection.cpp
===================================================================
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -161,42 +161,6 @@ QXcbScreen* QXcbConnection::findScreenFo
     return 0;
 }
 
-QXcbScreen* QXcbConnection::createScreen(QXcbVirtualDesktop* virtualDesktop,
-                                         xcb_randr_output_t outputId,
-                                         xcb_randr_get_output_info_reply_t *output)
-{
-    QString name;
-    if (output)
-        name = QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output),
-                xcb_randr_get_output_info_name_length(output));
-    else {
-        QByteArray displayName = m_displayName;
-        int dotPos = displayName.lastIndexOf('.');
-        if (dotPos != -1)
-            displayName.truncate(dotPos);
-        name = QString::fromLocal8Bit(displayName) + QLatin1Char('.') + QString::number(virtualDesktop->number());
-    }
-
-    return new QXcbScreen(this, virtualDesktop, outputId, output, name);
-}
-
-bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output)
-{
-    xcb_generic_error_t *error = 0;
-    xcb_randr_get_output_primary_cookie_t primaryCookie =
-        xcb_randr_get_output_primary(xcb_connection(), rootWindow);
-    QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary (
-        xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error));
-    if (!primary || error) {
-        qWarning("failed to get the primary output of the screen");
-        free(error);
-        error = NULL;
-    }
-    const bool isPrimary = primary ? (primary->output == output) : false;
-
-    return isPrimary;
-}
-
 QXcbVirtualDesktop* QXcbConnection::virtualDesktopForRootWindow(xcb_window_t rootWindow)
 {
     foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops) {
@@ -219,8 +183,9 @@ void QXcbConnection::updateScreens(const
             // Not for us
             return;
 
-        qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc;
         QXcbScreen *screen = findScreenForCrtc(crtc.window, crtc.crtc);
+        qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc
+                             << "mode" << crtc.mode << "relevant screen" << screen;
         // Only update geometry when there's a valid mode on the CRTC
         // CRTC with node mode could mean that output has been disabled, and we'll
         // get RRNotifyOutputChange notification for that.
@@ -242,16 +207,7 @@ void QXcbConnection::updateScreens(const
 
         if (screen && output.connection == XCB_RANDR_CONNECTION_DISCONNECTED) {
             qCDebug(lcQpaScreen) << "screen" << screen->name() << "has been disconnected";
-
-            // Known screen removed -> delete it
-            m_screens.removeOne(screen);
-            virtualDesktop->removeScreen(screen);
-
-            QXcbIntegration::instance()->destroyScreen(screen);
-
-            // QTBUG-40174, QTBUG-42985: If all screens are removed, wait
-            // and start rendering again later if a screen becomes available.
-
+            destroyScreen(screen);
         } else if (!screen && output.connection == XCB_RANDR_CONNECTION_CONNECTED) {
             // New XRandR output is available and it's enabled
             if (output.crtc != XCB_NONE && output.mode != XCB_NONE) {
@@ -260,59 +216,142 @@ void QXcbConnection::updateScreens(const
                 QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> outputInfo(
                     xcb_randr_get_output_info_reply(xcb_connection(), outputInfoCookie, NULL));
 
-                screen = createScreen(virtualDesktop, output.output, outputInfo.data());
-                qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled";
+                // Find a fake screen
+                foreach (QPlatformScreen *scr, virtualDesktop->screens()) {
+                    QXcbScreen *xcbScreen = (QXcbScreen *)scr;
+                    if (xcbScreen->output() == XCB_NONE) {
+                        screen = xcbScreen;
+                        break;
+                    }
+                }
 
-                screen->setPrimary(checkOutputIsPrimary(output.window, output.output));
-                virtualDesktop->addScreen(screen);
-                m_screens << screen;
-                QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary());
-
-                // Windows which had null screens have already had expose events by now.
-                // They need to be told the screen is back, it's OK to render.
-                foreach (QWindow *window, QGuiApplication::topLevelWindows()) {
-                    QXcbWindow *xcbWin = static_cast<QXcbWindow*>(window->handle());
-                    if (xcbWin)
-                        xcbWin->maybeSetScreen(screen);
+                if (screen) {
+                    QString nameWas = screen->name();
+                    // Transform the fake screen into a physical screen
+                    screen->setOutput(output.output, outputInfo.data());
+                    updateScreen(screen, output);
+                    qCDebug(lcQpaScreen) << "output" << screen->name()
+                                         << "is connected and enabled; was fake:" << nameWas;
+                } else {
+                    screen = createScreen(virtualDesktop, output, outputInfo.data());
+                    qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled";
                 }
             }
-            // else ignore disabled screens
         } else if (screen) {
-            // Screen has been disabled -> remove
             if (output.crtc == XCB_NONE && output.mode == XCB_NONE) {
+                // Screen has been disabled
                 xcb_randr_get_output_info_cookie_t outputInfoCookie =
                     xcb_randr_get_output_info(xcb_connection(), output.output, output.config_timestamp);
                 QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> outputInfo(
                     xcb_randr_get_output_info_reply(xcb_connection(), outputInfoCookie, NULL));
                 if (outputInfo->crtc == XCB_NONE) {
                     qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled";
-                    m_screens.removeOne(screen);
-                    virtualDesktop->removeScreen(screen);
-                    QXcbIntegration::instance()->destroyScreen(screen);
+                    destroyScreen(screen);
                 } else {
                     qCDebug(lcQpaScreen) << "output" << screen->name() << "has been temporarily disabled for the mode switch";
+                    // Reset crtc to skip RRCrtcChangeNotify events,
+                    // because they may be invalid in the middle of the mode switch
+                    screen->setCrtc(XCB_NONE);
                 }
             } else {
-                // Just update existing screen
-                screen->updateGeometry(output.config_timestamp);
-                const bool wasPrimary = screen->isPrimary();
-                screen->setPrimary(checkOutputIsPrimary(output.window, output.output));
-                if (screen->mode() != output.mode)
-                    screen->updateRefreshRate(output.mode);
-
-                // If the screen became primary, reshuffle the order in QGuiApplicationPrivate
-                if (!wasPrimary && screen->isPrimary()) {
-                    const int idx = m_screens.indexOf(screen);
-                    m_screens.swap(0, idx);
-                    QXcbIntegration::instance()->setPrimaryScreen(screen);
-                }
+                updateScreen(screen, output);
                 qCDebug(lcQpaScreen) << "output has changed" << screen;
             }
         }
+
+        qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name();
+    }
+}
+
+bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output)
+{
+    xcb_generic_error_t *error = 0;
+    xcb_randr_get_output_primary_cookie_t primaryCookie =
+        xcb_randr_get_output_primary(xcb_connection(), rootWindow);
+    QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary (
+        xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error));
+    if (!primary || error) {
+        qWarning("failed to get the primary output of the screen");
+        free(error);
+        error = NULL;
+    }
+    const bool isPrimary = primary ? (primary->output == output) : false;
+
+    return isPrimary;
+}
+
+void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange)
+{
+    screen->setCrtc(outputChange.crtc); // Set the new crtc, because it can be invalid
+    screen->updateGeometry(outputChange.config_timestamp);
+    if (screen->mode() != outputChange.mode)
+        screen->updateRefreshRate(outputChange.mode);
+    // Only screen which belongs to the primary virtual desktop can be a primary screen
+    if (screen->screenNumber() == m_primaryScreenNumber) {
+        if (!screen->isPrimary() && checkOutputIsPrimary(outputChange.window, outputChange.output)) {
+            screen->setPrimary(true);
+
+            // If the screen became primary, reshuffle the order in QGuiApplicationPrivate
+            const int idx = m_screens.indexOf(screen);
+            if (idx > 0) {
+                m_screens.first()->setPrimary(false);
+                m_screens.swap(0, idx);
+            }
+            screen->virtualDesktop()->setPrimaryScreen(screen);
+            QXcbIntegration::instance()->setPrimaryScreen(screen);
+        }
+    }
+}
+
+QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop,
+                                         const xcb_randr_output_change_t &outputChange,
+                                         xcb_randr_get_output_info_reply_t *outputInfo)
+{
+    QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputChange.output, outputInfo);
+    // Only screen which belongs to the primary virtual desktop can be a primary screen
+    if (screen->screenNumber() == m_primaryScreenNumber)
+        screen->setPrimary(checkOutputIsPrimary(outputChange.window, outputChange.output));
+
+    if (screen->isPrimary()) {
         if (!m_screens.isEmpty())
-            qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name();
-        else
-            qCDebug(lcQpaScreen) << "no outputs";
+            m_screens.first()->setPrimary(false);
+
+        m_screens.prepend(screen);
+    } else {
+        m_screens.append(screen);
+    }
+    virtualDesktop->addScreen(screen);
+    QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary());
+
+    return screen;
+}
+
+void QXcbConnection::destroyScreen(QXcbScreen *screen)
+{
+    QXcbVirtualDesktop *virtualDesktop = screen->virtualDesktop();
+    if (virtualDesktop->screens().count() == 1) {
+        // If there are no other screens on the same virtual desktop,
+        // then transform the physical screen into a fake screen.
+        const QString nameWas = screen->name();
+        screen->setOutput(XCB_NONE, Q_NULLPTR);
+        qCDebug(lcQpaScreen) << "transformed" << nameWas << "to fake" << screen;
+    } else {
+        // There is more than one screen on the same virtual desktop, remove the screen
+        m_screens.removeOne(screen);
+        virtualDesktop->removeScreen(screen);
+
+        // When primary screen is removed, set the new primary screen
+        // which belongs to the primary virtual desktop.
+        if (screen->isPrimary()) {
+            QXcbScreen *newPrimary = (QXcbScreen *)virtualDesktop->screens().at(0);
+            newPrimary->setPrimary(true);
+            const int idx = m_screens.indexOf(newPrimary);
+            if (idx > 0)
+                m_screens.swap(0, idx);
+            QXcbIntegration::instance()->setPrimaryScreen(newPrimary);
+        }
+
+        QXcbIntegration::instance()->destroyScreen(screen);
     }
 }
 
@@ -320,8 +359,7 @@ void QXcbConnection::initializeScreens()
 {
     xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup);
     int xcbScreenNumber = 0;    // screen number in the xcb sense
-    QXcbScreen* primaryScreen = Q_NULLPTR;
-    bool hasOutputs = false;
+    QXcbScreen *primaryScreen = Q_NULLPTR;
     while (it.rem) {
         // Each "screen" in xcb terminology is a virtual desktop,
         // potentially a collection of separate juxtaposed monitors.
@@ -330,8 +368,6 @@ void QXcbConnection::initializeScreens()
         xcb_screen_t *xcbScreen = it.data;
         QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber);
         m_virtualDesktops.append(virtualDesktop);
-        QList<QPlatformScreen *> siblings;
-        int outputCount = 0;
         if (has_randr_extension) {
             xcb_generic_error_t *error = NULL;
             // RRGetScreenResourcesCurrent is fast but it may return nothing if the
@@ -348,7 +384,7 @@ void QXcbConnection::initializeScreens()
             } else {
                 xcb_timestamp_t timestamp;
                 xcb_randr_output_t *outputs = Q_NULLPTR;
-                outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.data());
+                int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.data());
                 if (outputCount) {
                     timestamp = resources_current->config_timestamp;
                     outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.data());
@@ -375,6 +411,7 @@ void QXcbConnection::initializeScreens()
                         qWarning("failed to get the primary output of the screen");
                         free(error);
                     } else {
+                        QList<QPlatformScreen *> siblings;
                         for (int i = 0; i < outputCount; i++) {
                             QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> output(
                                     xcb_randr_get_output_info_reply(xcb_connection(),
@@ -398,9 +435,8 @@ void QXcbConnection::initializeScreens()
                                 continue;
                             }
 
-                            QXcbScreen *screen = createScreen(virtualDesktop, outputs[i], output.data());
+                            QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.data());
                             siblings << screen;
-                            hasOutputs = true;
                             m_screens << screen;
 
                             // There can be multiple outputs per screen, use either
@@ -417,11 +453,23 @@ void QXcbConnection::initializeScreens()
                                 }
                             }
                         }
+                        virtualDesktop->setScreens(siblings);
                     }
                 }
             }
         }
-        virtualDesktop->setScreens(siblings);
+        if (virtualDesktop->screens().isEmpty()) {
+            // If there are no XRandR outputs or XRandR extension is missing,
+            // then create a fake/legacy screen.
+            QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, Q_NULLPTR);
+            qCDebug(lcQpaScreen) << "created fake screen" << screen;
+            m_screens << screen;
+            if (m_primaryScreenNumber == xcbScreenNumber) {
+                primaryScreen = screen;
+                primaryScreen->setPrimary(true);
+            }
+            virtualDesktop->addScreen(screen);
+        }
         xcb_screen_next(&it);
         ++xcbScreenNumber;
     } // for each xcb screen
@@ -429,39 +477,25 @@ void QXcbConnection::initializeScreens()
     foreach (QXcbVirtualDesktop *virtualDesktop, m_virtualDesktops)
         virtualDesktop->subscribeToXFixesSelectionNotify();
 
-    // If there's no randr extension, or there was some error above, or we found a
-    // screen which doesn't have outputs for some other reason (e.g. on VNC or ssh -X),
-    // but the dimensions are known anyway, and we don't already have any lingering
-    // (possibly disconnected) screens, then showing windows should be possible,
-    // so create one screen. (QTBUG-31389)
-    QXcbVirtualDesktop *virtualDesktop = m_virtualDesktops.value(0);
-    if (virtualDesktop && !hasOutputs && !virtualDesktop->size().isEmpty() && m_screens.isEmpty()) {
-        QXcbScreen *screen = createScreen(virtualDesktop, 0, Q_NULLPTR);
-        virtualDesktop->setScreens(QList<QPlatformScreen *>() << screen);
-        m_screens << screen;
-        primaryScreen = screen;
-        primaryScreen->setPrimary(true);
-        qCDebug(lcQpaScreen) << "found a screen with zero outputs" << screen;
-    }
-
-    // Ensure the primary screen is first in the list
-    if (primaryScreen) {
-        Q_ASSERT(!m_screens.isEmpty());
-        if (m_screens.first() != primaryScreen) {
-            m_screens.removeOne(primaryScreen);
-            m_screens.prepend(primaryScreen);
+	 if (m_virtualDesktops.isEmpty()) {
+        qFatal("QXcbConnection: no screens available");
+    } else {
+        // Ensure the primary screen is first on the list
+        if (primaryScreen) {
+            if (m_screens.first() != primaryScreen) {
+                m_screens.removeOne(primaryScreen);
+                m_screens.prepend(primaryScreen);
+            }
         }
-    }
 
-    // Push the screens to QApplication
-    QXcbIntegration *integration = QXcbIntegration::instance();
-    foreach (QXcbScreen* screen, m_screens) {
-        qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")";
-        integration->screenAdded(screen, screen->isPrimary());
-    }
+        // Push the screens to QGuiApplication
+        foreach (QXcbScreen *screen, m_screens) {
+            qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")";
+            QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary());
+        }
 
-    if (!m_screens.isEmpty())
         qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name();
+	 }
 }
 
 QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName)
@@ -535,9 +569,6 @@ QXcbConnection::QXcbConnection(QXcbNativ
     initializeXFixes();
     initializeScreens();
 
-    if (m_screens.isEmpty())
-        qFatal("QXcbConnection: no screens available");
-
     initializeXRender();
     m_xi2Enabled = false;
 #if defined(XCB_USE_XINPUT2)
Index: b/src/plugins/platforms/xcb/qxcbscreen.cpp
===================================================================
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -79,6 +79,13 @@ void QXcbVirtualDesktop::addScreen(QPlat
     ((QXcbScreen *) s)->isPrimary() ? m_screens.prepend(s) : m_screens.append(s);
 }
 
+void QXcbVirtualDesktop::setPrimaryScreen(QPlatformScreen *s)
+{
+    const int idx = m_screens.indexOf(s);
+    Q_ASSERT(idx > -1);
+    m_screens.swap(0, idx);
+}
+
 QXcbXSettings *QXcbVirtualDesktop::xSettings() const
 {
     if (!m_xSettings) {
@@ -113,16 +120,15 @@ void QXcbVirtualDesktop::subscribeToXFix
 }
 
 QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop,
-                       xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output,
-                       QString outputName)
+                       xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output)
     : QXcbObject(connection)
     , m_virtualDesktop(virtualDesktop)
     , m_output(outputId)
-    , m_crtc(output ? output->crtc : 0)
+    , m_crtc(output ? output->crtc : XCB_NONE)
     , m_mode(XCB_NONE)
     , m_primary(false)
     , m_rotation(XCB_RANDR_ROTATION_ROTATE_0)
-    , m_outputName(outputName)
+    , m_outputName(getOutputName(output))
     , m_outputSizeMillimeters(output ? QSize(output->mm_width, output->mm_height) : QSize())
     , m_virtualSize(virtualDesktop->size())
     , m_virtualSizeMillimeters(virtualDesktop->physicalSize())
@@ -232,6 +238,22 @@ QXcbScreen::~QXcbScreen()
     delete m_cursor;
 }
 
+QString QXcbScreen::getOutputName(xcb_randr_get_output_info_reply_t *outputInfo)
+{
+    QString name;
+    if (outputInfo) {
+        name = QString::fromUtf8((const char*)xcb_randr_get_output_info_name(outputInfo),
+                                 xcb_randr_get_output_info_name_length(outputInfo));
+    } else {
+        QByteArray displayName = connection()->displayName();
+        int dotPos = displayName.lastIndexOf('.');
+        if (dotPos != -1)
+            displayName.truncate(dotPos);
+        name = QString::fromLocal8Bit(displayName) + QLatin1Char('.')
+                + QString::number(m_virtualDesktop->number());
+    }
+    return name;
+}
 
 QWindow *QXcbScreen::topLevelAt(const QPoint &p) const
 {
@@ -356,6 +378,16 @@ QPlatformCursor *QXcbScreen::cursor() co
     return m_cursor;
 }
 
+void QXcbScreen::setOutput(xcb_randr_output_t outputId,
+                           xcb_randr_get_output_info_reply_t *outputInfo)
+{
+    m_output = outputId;
+    m_crtc = outputInfo ? outputInfo->crtc : XCB_NONE;
+    m_mode = XCB_NONE;
+    m_outputName = getOutputName(outputInfo);
+    // TODO: Send an event to the QScreen instance that the screen changed its name
+}
+
 /*!
     \brief handle the XCB screen change event and update properties
 
@@ -424,19 +456,10 @@ void QXcbScreen::handleScreenChange(xcb_
 
     updateGeometry(change_event->timestamp);
 
-    QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
     QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), m_orientation);
 
     QDpi ldpi = logicalDpi();
     QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(QPlatformScreen::screen(), ldpi.first, ldpi.second);
-
-    // Windows which had null screens have already had expose events by now.
-    // They need to be told the screen is back, it's OK to render.
-    foreach (QWindow *window, QGuiApplication::topLevelWindows()) {
-        QXcbWindow *xcbWin = static_cast<QXcbWindow*>(window->handle());
-        if (xcbWin)
-            xcbWin->maybeSetScreen(this);
-    }
 }
 
 void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp)
Index: b/src/plugins/platforms/xcb/qxcbscreen.h
===================================================================
--- a/src/plugins/platforms/xcb/qxcbscreen.h
+++ b/src/plugins/platforms/xcb/qxcbscreen.h
@@ -72,6 +72,7 @@ public:
     void setScreens(QList<QPlatformScreen *> sl) { m_screens = sl; }
     void removeScreen(QPlatformScreen *s) { m_screens.removeOne(s); }
     void addScreen(QPlatformScreen *s);
+    void setPrimaryScreen(QPlatformScreen *s);
 
     QXcbXSettings *xSettings() const;
 
@@ -94,10 +95,11 @@ class Q_XCB_EXPORT QXcbScreen : public Q
 {
 public:
     QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop,
-               xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output,
-               QString outputName);
+               xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *outputInfo);
     ~QXcbScreen();
 
+    QString getOutputName(xcb_randr_get_output_info_reply_t *outputInfo);
+
     QPixmap grabWindow(WId window, int x, int y, int width, int height) const Q_DECL_OVERRIDE;
 
     QWindow *topLevelAt(const QPoint &point) const Q_DECL_OVERRIDE;
@@ -130,6 +132,10 @@ public:
     xcb_randr_crtc_t crtc() const { return m_crtc; }
     xcb_randr_mode_t mode() const { return m_mode; }
 
+    void setOutput(xcb_randr_output_t outputId,
+                   xcb_randr_get_output_info_reply_t *outputInfo);
+    void setCrtc(xcb_randr_crtc_t crtc) { m_crtc = crtc; }
+
     void windowShown(QXcbWindow *window);
     QString windowManagerName() const { return m_windowManagerName; }
     bool syncRequestSupported() const { return m_syncRequestSupported; }
Index: b/src/plugins/platforms/xcb/qxcbwindow.cpp
===================================================================
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -629,14 +629,6 @@ void QXcbWindow::destroy()
         m_pendingSyncRequest->invalidate();
 }
 
-void QXcbWindow::maybeSetScreen(QXcbScreen *screen)
-{
-    if (!window()->screen() && screen->geometry().contains(geometry().topLeft())) {
-        QWindowSystemInterface::handleWindowScreenChanged(window(), static_cast<QPlatformScreen *>(screen)->screen());
-        QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(0, 0), window()->size())));
-    }
-}
-
 void QXcbWindow::setGeometry(const QRect &rect)
 {
     QPlatformWindow::setGeometry(rect);
@@ -843,15 +835,13 @@ void QXcbWindow::hide()
     Q_XCB_CALL(xcb_unmap_window(xcb_connection(), m_window));
 
     // send synthetic UnmapNotify event according to icccm 4.1.4
-    if (xcbScreen()) {
-        xcb_unmap_notify_event_t event;
-        event.response_type = XCB_UNMAP_NOTIFY;
-        event.event = xcbScreen()->root();
-        event.window = m_window;
-        event.from_configure = false;
-        Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xcbScreen()->root(),
-                                  XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
-    }
+    xcb_unmap_notify_event_t event;
+    event.response_type = XCB_UNMAP_NOTIFY;
+    event.event = xcbScreen()->root();
+    event.window = m_window;
+    event.from_configure = false;
+    Q_XCB_CALL(xcb_send_event(xcb_connection(), false, xcbScreen()->root(),
+                              XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
 
     xcb_flush(xcb_connection());
 
@@ -1176,8 +1166,6 @@ void QXcbWindow::changeNetWmState(bool s
     event.data.data32[3] = 0;
     event.data.data32[4] = 0;
 
-    if (!xcbScreen())
-        return;
     Q_XCB_CALL(xcb_send_event(xcb_connection(), 0, xcbScreen()->root(), XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&event));
 }
 
@@ -1426,8 +1414,6 @@ void QXcbWindow::setParent(const QPlatfo
         xcb_parent_id = qXcbParent->xcb_window();
         m_embedded = qXcbParent->window()->type() == Qt::ForeignWindow;
     } else {
-        if (!xcbScreen())
-            return;
         xcb_parent_id = xcbScreen()->root();
         m_embedded = false;
     }
@@ -1937,7 +1923,7 @@ void QXcbWindow::handleConfigureNotifyEv
 {
     bool fromSendEvent = (event->response_type & 0x80);
     QPoint pos(event->x, event->y);
-    if (!parent() && !fromSendEvent && xcbScreen()) {
+    if (!parent() && !fromSendEvent) {
         // Do not trust the position, query it instead.
         xcb_translate_coordinates_cookie_t cookie = xcb_translate_coordinates(xcb_connection(), xcb_window(),
                                                                               xcbScreen()->root(), 0, 0);
@@ -2237,8 +2223,6 @@ void QXcbWindow::handleEnterNotifyEvent(
         return;
 
     const QPoint local(event->event_x, event->event_y);
-    if (!xcbScreen())
-        return;
     QPoint global = QPoint(event->root_x, event->root_y);
     QWindowSystemInterface::handleEnterEvent(window(), local, global);
 }
@@ -2256,8 +2240,6 @@ void QXcbWindow::handleLeaveNotifyEvent(
 
     if (enterWindow) {
         QPoint local(enter->event_x, enter->event_y);
-        if (!xcbScreen())
-            return;
         QPoint global = QPoint(event->root_x, event->root_y);
 
         QWindowSystemInterface::handleEnterLeaveEvent(enterWindow->window(), window(), local, global);
@@ -2273,8 +2255,6 @@ void QXcbWindow::handlePropertyNotifyEve
     connection()->setTime(event->time);
 
     const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE;
-    if (!xcbScreen())
-        return;
 
     if (event->atom == atom(QXcbAtom::_NET_WM_STATE) || event->atom == atom(QXcbAtom::WM_STATE)) {
         if (propertyDeleted)
@@ -2594,8 +2574,6 @@ bool QXcbWindow::needsSync() const
 
 void QXcbWindow::postSyncWindowRequest()
 {
-    if (!xcbScreen())
-        return;
     if (!m_pendingSyncRequest) {
         QXcbSyncWindowRequest *e = new QXcbSyncWindowRequest(this);
         m_pendingSyncRequest = e;
Index: b/src/plugins/platforms/xcb/qxcbwindow.h
===================================================================
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -158,7 +158,6 @@ public:
 
     virtual void create();
     virtual void destroy();
-    void maybeSetScreen(QXcbScreen *screen);
     QXcbScreen *screenForNativeGeometry(const QRect &newGeometry) const;
 
 public Q_SLOTS:
Index: b/src/plugins/platforms/xcb/qxcbconnection.h
===================================================================
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -515,15 +515,17 @@ private:
     void initializeXShape();
     void initializeXKB();
     void handleClientMessageEvent(const xcb_client_message_event_t *event);
-    QXcbScreen* createScreen(QXcbVirtualDesktop *virtualDesktop,
-                             xcb_randr_output_t outputId = XCB_NONE,
-                             xcb_randr_get_output_info_reply_t *output = 0);
     QXcbScreen* findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc);
     QXcbScreen* findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output);
     QXcbVirtualDesktop* virtualDesktopForRootWindow(xcb_window_t rootWindow);
+	 void updateScreens(const xcb_randr_notify_event_t *event);
     bool checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output);
+	 void updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange);
+    QXcbScreen *createScreen(QXcbVirtualDesktop *virtualDesktop,
+                             const xcb_randr_output_change_t &outputChange,
+                             xcb_randr_get_output_info_reply_t *outputInfo);
+    void destroyScreen(QXcbScreen *screen);
     void initializeScreens();
-    void updateScreens(const xcb_randr_notify_event_t *event);
 
     bool m_xi2Enabled;
     int m_xi2Minor;
