diff -rupN qtbase-opensource-src-5.5.1/src/plugins/platforms/xcb/qxcbconnection.cpp qtbase-opensource-src-5.5.1-new/src/plugins/platforms/xcb/qxcbconnection.cpp --- qtbase-opensource-src-5.5.1/src/plugins/platforms/xcb/qxcbconnection.cpp 2015-10-13 06:35:27.000000000 +0200 +++ qtbase-opensource-src-5.5.1-new/src/plugins/platforms/xcb/qxcbconnection.cpp 2015-10-21 21:02:53.056198256 +0200 @@ -242,20 +241,18 @@ 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); - foreach (QXcbScreen *otherScreen, m_screens) - otherScreen->removeVirtualSibling((QPlatformScreen *) 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, true); } 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) { + // QTBUG-40174, QTBUG-42985: If virtual screen exists, + // remove it and next add a physical screen. + if (m_onlyVirtualScreen) { + qCDebug(lcQpaScreen) << "default screen" << screen->name() << "has been removed"; + destroyScreen(m_screens.at(0), false); + m_onlyVirtualScreen = false; + } + xcb_randr_get_output_info_cookie_t outputInfoCookie = xcb_randr_get_output_info(xcb_connection(), output.output, output.config_timestamp); QScopedPointer outputInfo( @@ -270,34 +267,25 @@ void QXcbConnection::updateScreens(const otherScreen->addVirtualSibling(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(window->handle()); - if (xcbWin) - xcbWin->maybeSetScreen(screen); - } + maybeSetScreenForTopLevelWindows(screen); } - // 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 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); - foreach (QXcbScreen *otherScreen, m_screens) - otherScreen->removeVirtualSibling((QPlatformScreen *) screen); - QXcbIntegration::instance()->destroyScreen(screen); + destroyScreen(screen, true); } else { qCDebug(lcQpaScreen) << "output" << screen->name() << "has been temporarily disabled for the mode switch"; + screen->setCrtc(XCB_NONE); //Invalidate crtc } } else { // Just update existing screen + screen->setCrtc(output.crtc); //Set the new crtc, because it may be invalidated screen->updateGeometry(output.config_timestamp); const bool wasPrimary = screen->isPrimary(); screen->setPrimary(checkOutputIsPrimary(output.window, output.output)); @@ -316,19 +304,61 @@ void QXcbConnection::updateScreens(const qCDebug(lcQpaScreen) << "output has changed" << screen; } } + if (!m_screens.isEmpty()) qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name(); else qCDebug(lcQpaScreen) << "no outputs"; } } +void QXcbConnection::destroyScreen(QXcbScreen *screen, bool canCreateVirtualScreen) +{ + // Known screen removed -> delete it + m_screens.removeOne(screen); + foreach (QXcbScreen *otherScreen, m_screens) + otherScreen->removeVirtualSibling((QPlatformScreen *)screen); + QXcbIntegration::instance()->destroyScreen(screen); + + // QTBUG-40174, QTBUG-42985: If all screens are removed, add a virtual + // screen and remove it later if a physical screen becomes available. + if (canCreateVirtualScreen && m_screens.isEmpty()) + createVirtualScreen(); +} +void QXcbConnection::createVirtualScreen() +{ + QXcbVirtualDesktop *virtualDesktop = m_virtualDesktops.value(0); + if (virtualDesktop && !virtualDesktop->size().isEmpty()) { + Q_ASSERT(m_screens.isEmpty()); + QXcbScreen *screen = createScreen(virtualDesktop, 0, Q_NULLPTR); + screen->setVirtualSiblings(QList() << screen); + screen->setPrimary(true); + m_onlyVirtualScreen = true; + m_screens << screen; + QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); + maybeSetScreenForTopLevelWindows(screen); + qCDebug(lcQpaScreen) << "virtual screen was created" << screen; + } +} +void QXcbConnection::maybeSetScreenForTopLevelWindows(QXcbScreen *screen) +{ + // 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. + bool doFlush = false; + foreach (QWindow *window, QGuiApplication::topLevelWindows()) { + QXcbWindow *xcbWin = static_cast(window->handle()); + if (xcbWin) + doFlush |= xcbWin->maybeSetScreen(screen); + } + // Flush Window System Events to prevent disappearing windows + if (doFlush) + QWindowSystemInterface::flushWindowSystemEvents(); +} 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; while (it.rem) { // Each "screen" in xcb terminology is a virtual desktop, // potentially a collection of separate juxtaposed monitors. @@ -407,7 +437,6 @@ void QXcbConnection::initializeScreens() QXcbScreen *screen = createScreen(virtualDesktop, outputs[i], output.data()); siblings << screen; - hasOutputs = true; m_screens << screen; // There can be multiple outputs per screen, use either @@ -434,39 +463,23 @@ void QXcbConnection::initializeScreens() ++xcbScreenNumber; } // for each xcb screen - // 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); - screen->setVirtualSiblings(QList() << 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_screens.isEmpty()) { + createVirtualScreen(); + } else { + // Ensure the primary screen is first in 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()); - } - - if (!m_screens.isEmpty()) - qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name(); + // Push the screens to QGuiApplication + foreach (QXcbScreen *screen, m_screens) { + qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")"; + QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary()); + } + } } QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName) @@ -474,6 +487,7 @@ QXcbConnection::QXcbConnection(QXcbNativ , m_canGrabServer(canGrabServer) , m_defaultVisualId(defaultVisualId) , m_primaryScreenNumber(0) + , m_onlyVirtualScreen(false) , m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY")) , m_nativeInterface(nativeInterface) #ifdef XCB_USE_XLIB @@ -1110,8 +1124,19 @@ void QXcbConnection::handleXcbEvent(xcb_ handled = false; break; case XCB_PROPERTY_NOTIFY: - HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent); + { + // Update geometry for all screens, because availableGeometry must be changed + const xcb_property_notify_event_t *propertyNotifyEvent = (const xcb_property_notify_event_t *)event; + if (propertyNotifyEvent->atom == atom(QXcbAtom::_NET_WORKAREA)) { + foreach (QXcbScreen *screen, m_screens) { + if (propertyNotifyEvent->window == screen->root()) + screen->updateGeometry(propertyNotifyEvent->time); + } + } else { + HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent); + } break; + } #if defined(XCB_USE_XINPUT2) case XCB_GE_GENERIC: // Here the windowEventListener is invoked from xi2HandleEvent() diff -rupN qtbase-opensource-src-5.5.1/src/plugins/platforms/xcb/qxcbconnection.h qtbase-opensource-src-5.5.1-new/src/plugins/platforms/xcb/qxcbconnection.h --- qtbase-opensource-src-5.5.1/src/plugins/platforms/xcb/qxcbconnection.h 2015-10-13 06:35:27.000000000 +0200 +++ qtbase-opensource-src-5.5.1-new/src/plugins/platforms/xcb/qxcbconnection.h 2015-10-21 21:00:21.767613360 +0200 @@ -519,6 +519,9 @@ private: QXcbVirtualDesktop* virtualDesktopForRootWindow(xcb_window_t rootWindow); bool checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output); void initializeScreens(); + void destroyScreen(QXcbScreen *screen, bool canCreateVirtualScreen); + void createVirtualScreen(); + void maybeSetScreenForTopLevelWindows(QXcbScreen *screen); void updateScreens(const xcb_randr_notify_event_t *event); bool m_xi2Enabled; @@ -583,6 +586,7 @@ private: QList m_virtualDesktops; QList m_screens; int m_primaryScreenNumber; + bool m_onlyVirtualScreen; xcb_atom_t m_allAtoms[QXcbAtom::NAtoms]; diff -rupN qtbase-opensource-src-5.5.1/src/plugins/platforms/xcb/qxcbscreen.cpp qtbase-opensource-src-5.5.1-new/src/plugins/platforms/xcb/qxcbscreen.cpp --- qtbase-opensource-src-5.5.1/src/plugins/platforms/xcb/qxcbscreen.cpp 2015-10-13 06:35:27.000000000 +0200 +++ qtbase-opensource-src-5.5.1-new/src/plugins/platforms/xcb/qxcbscreen.cpp 2015-10-21 21:00:21.768613377 +0200 @@ -438,14 +438,6 @@ void QXcbScreen::handleScreenChange(xcb_ 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(window->handle()); - if (xcbWin) - xcbWin->maybeSetScreen(this); - } } void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp) diff -rupN qtbase-opensource-src-5.5.1/src/plugins/platforms/xcb/qxcbscreen.h qtbase-opensource-src-5.5.1-new/src/plugins/platforms/xcb/qxcbscreen.h --- qtbase-opensource-src-5.5.1/src/plugins/platforms/xcb/qxcbscreen.h 2015-10-13 06:35:27.000000000 +0200 +++ qtbase-opensource-src-5.5.1-new/src/plugins/platforms/xcb/qxcbscreen.h 2015-10-21 21:00:21.768613377 +0200 @@ -116,6 +116,8 @@ public: xcb_randr_crtc_t crtc() const { return m_crtc; } xcb_randr_mode_t mode() const { return m_mode; } + 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; } diff -rupN qtbase-opensource-src-5.5.1/src/plugins/platforms/xcb/qxcbwindow.cpp qtbase-opensource-src-5.5.1-new/src/plugins/platforms/xcb/qxcbwindow.cpp --- qtbase-opensource-src-5.5.1/src/plugins/platforms/xcb/qxcbwindow.cpp 2015-10-13 06:35:27.000000000 +0200 +++ qtbase-opensource-src-5.5.1-new/src/plugins/platforms/xcb/qxcbwindow.cpp 2015-10-21 21:01:17.324562601 +0200 @@ -694,12 +694,17 @@ void QXcbWindow::destroy() m_pendingSyncRequest->invalidate(); } -void QXcbWindow::maybeSetScreen(QXcbScreen *screen) +bool QXcbWindow::maybeSetScreen(QXcbScreen *screen) { - if (!window()->screen() && screen->geometry().contains(geometry().topLeft())) { + // Every window must have a screen. Otherwise application can + // crash and the window contents are invisible e.g. in x11vnc. + if (!window()->screen()) { QWindowSystemInterface::handleWindowScreenChanged(window(), static_cast(screen)->screen()); - QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(0, 0), window()->size()))); + if (screen->geometry().contains(geometry().topLeft())) + QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(0, 0), window()->size()))); + return true; } + return false; } void QXcbWindow::setGeometry(const QRect &rect) @@ -1243,8 +1248,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)); } @@ -2323,8 +2324,6 @@ void QXcbWindow::handleEnterNotifyEvent( const int dpr = int(devicePixelRatio()); const QPoint local(event->event_x/dpr, event->event_y/dpr); - if (!xcbScreen()) - return; QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y)); QWindowSystemInterface::handleEnterEvent(window(), local, global); } @@ -2343,8 +2342,6 @@ void QXcbWindow::handleLeaveNotifyEvent( if (enterWindow) { const int dpr = int(devicePixelRatio()); QPoint local(enter->event_x/dpr, enter->event_y/dpr); - if (!xcbScreen()) - return; QPoint global = xcbScreen()->mapFromNative(QPoint(event->root_x, event->root_y)); QWindowSystemInterface::handleEnterLeaveEvent(enterWindow->window(), window(), local, global); @@ -2360,8 +2357,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) @@ -2403,8 +2398,6 @@ void QXcbWindow::handlePropertyNotifyEve return; } else if (event->atom == atom(QXcbAtom::_NET_FRAME_EXTENTS)) { m_dirtyFrameMargins = true; - } else if (event->atom == atom(QXcbAtom::_NET_WORKAREA) && xcbScreen() && event->window == xcbScreen()->root()) { - xcbScreen()->updateGeometry(event->time); } } @@ -2682,8 +2675,6 @@ bool QXcbWindow::needsSync() const void QXcbWindow::postSyncWindowRequest() { - if (!xcbScreen()) - return; if (!m_pendingSyncRequest) { QXcbSyncWindowRequest *e = new QXcbSyncWindowRequest(this); m_pendingSyncRequest = e; diff -rupN qtbase-opensource-src-5.5.1/src/plugins/platforms/xcb/qxcbwindow.h qtbase-opensource-src-5.5.1-new/src/plugins/platforms/xcb/qxcbwindow.h --- qtbase-opensource-src-5.5.1/src/plugins/platforms/xcb/qxcbwindow.h 2015-10-13 06:35:27.000000000 +0200 +++ qtbase-opensource-src-5.5.1-new/src/plugins/platforms/xcb/qxcbwindow.h 2015-10-21 21:00:21.769613394 +0200 @@ -158,7 +158,7 @@ public: virtual void create(); virtual void destroy(); - void maybeSetScreen(QXcbScreen *screen); + bool maybeSetScreen(QXcbScreen *screen); QXcbScreen *screenForNativeGeometry(const QRect &newGeometry) const; public Q_SLOTS: