From f4b8697c40bd476ef6bf83418e144adce7c7d4a3 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 3 Feb 2015 13:05:20 +0100 Subject: Handle gracefully the removal and re-attachment of all QScreens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't crash; restore windows when all screens are removed and re-added. xcb: on configure notify, check for screen change: it may be that a window belonging to a screen which was removed has now gotten mapped to the new screen. On screen change, send a synthetic expose event, because the real expose events already happened. Task-number: QTBUG-38326 Task-number: QTBUG-32973 Task-number: QTBUG-42985 Change-Id: If334f55c248468ad3c95e7066bb14eca377d2050 Reviewed-by: Jørgen Lind --- src/gui/kernel/qguiapplication.cpp | 4 ++- src/gui/kernel/qplatformwindow.cpp | 5 ++-- src/gui/kernel/qscreen.cpp | 2 +- src/gui/kernel/qwindow.cpp | 14 +++++---- src/gui/kernel/qwindow_p.h | 5 +++- src/plugins/platforms/xcb/qxcbconnection.cpp | 3 ++ src/plugins/platforms/xcb/qxcbnativeinterface.cpp | 7 ++++- src/plugins/platforms/xcb/qxcbscreen.cpp | 7 +++++ src/plugins/platforms/xcb/qxcbwindow.cpp | 36 +++++++++++++++-------- src/plugins/platforms/xcb/qxcbwindow.h | 1 + src/widgets/kernel/qwidget.cpp | 2 +- src/widgets/kernel/qwidgetwindow.cpp | 3 +- 12 files changed, 64 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index d44a79b37e..92be903b41 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -2583,6 +2583,8 @@ void QGuiApplicationPrivate::processExposeEvent(QWindowSystemInterfacePrivate::E return; QWindow *window = e->exposed.data(); + if (!window) + return; QWindowPrivate *p = qt_window_private(window); if (!p->receivedExpose) { @@ -2598,7 +2600,7 @@ void QGuiApplicationPrivate::processExposeEvent(QWindowSystemInterfacePrivate::E p->receivedExpose = true; } - p->exposed = e->isExposed; + p->exposed = e->isExposed && window->screen(); QExposeEvent exposeEvent(e->region); QCoreApplication::sendSpontaneousEvent(window, &exposeEvent); diff --git a/src/gui/kernel/qplatformwindow.cpp b/src/gui/kernel/qplatformwindow.cpp index 167bd44f0e..763ab68868 100644 --- a/src/gui/kernel/qplatformwindow.cpp +++ b/src/gui/kernel/qplatformwindow.cpp @@ -83,7 +83,8 @@ QPlatformWindow *QPlatformWindow::parent() const */ QPlatformScreen *QPlatformWindow::screen() const { - return window()->screen()->handle(); + QScreen *scr = window()->screen(); + return scr ? scr->handle() : Q_NULLPTR; } /*! @@ -479,7 +480,7 @@ QString QPlatformWindow::formatWindowTitle(const QString &title, const QString & QPlatformScreen *QPlatformWindow::screenForGeometry(const QRect &newGeometry) const { QPlatformScreen *currentScreen = screen(); - if (!parent() && !currentScreen->geometry().intersects(newGeometry)) { + if (!parent() && currentScreen && !currentScreen->geometry().intersects(newGeometry)) { Q_FOREACH (QPlatformScreen* screen, currentScreen->virtualSiblings()) { if (screen->geometry().intersects(newGeometry)) return screen; diff --git a/src/gui/kernel/qscreen.cpp b/src/gui/kernel/qscreen.cpp index 8357e14354..0cc8f1082a 100644 --- a/src/gui/kernel/qscreen.cpp +++ b/src/gui/kernel/qscreen.cpp @@ -86,7 +86,7 @@ QScreen::~QScreen() if (this == primaryScreen) return; - bool movingFromVirtualSibling = primaryScreen->handle()->virtualSiblings().contains(handle()); + bool movingFromVirtualSibling = primaryScreen && primaryScreen->handle()->virtualSiblings().contains(handle()); // Move any leftover windows to the primary screen foreach (QWindow *window, QGuiApplication::topLevelWindows()) { diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index b679dd9bfc..773cce7a7f 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -338,7 +338,7 @@ inline bool QWindowPrivate::windowRecreationRequired(QScreen *newScreen) const { Q_Q(const QWindow); const QScreen *oldScreen = q->screen(); - return oldScreen != newScreen && platformWindow + return oldScreen != newScreen && (platformWindow || !oldScreen) && !(oldScreen && oldScreen->virtualSiblings().contains(newScreen)); } @@ -373,10 +373,13 @@ void QWindowPrivate::setTopLevelScreen(QScreen *newScreen, bool recreate) } if (newScreen != topLevelScreen) { const bool shouldRecreate = recreate && windowRecreationRequired(newScreen); - if (shouldRecreate) + const bool shouldShow = visibilityOnDestroy && !topLevelScreen; + if (shouldRecreate && platformWindow) q->destroy(); connectToScreen(newScreen); - if (newScreen && shouldRecreate) + if (shouldShow) + q->setVisible(true); + else if (newScreen && shouldRecreate) create(true); emitScreenChangedRecursion(newScreen); } @@ -1600,6 +1603,7 @@ void QWindow::destroy() QGuiApplicationPrivate::tabletPressTarget = parent(); bool wasVisible = isVisible(); + d->visibilityOnDestroy = wasVisible && d->platformWindow; setVisible(false); @@ -1683,7 +1687,7 @@ bool QWindow::setMouseGrabEnabled(bool grab) QScreen *QWindow::screen() const { Q_D(const QWindow); - return d->parentWindow ? d->parentWindow->screen() : d->topLevelScreen; + return d->parentWindow ? d->parentWindow->screen() : d->topLevelScreen.data(); } /*! @@ -1703,7 +1707,7 @@ void QWindow::setScreen(QScreen *newScreen) Q_D(QWindow); if (!newScreen) newScreen = QGuiApplication::primaryScreen(); - d->setTopLevelScreen(newScreen, true /* recreate */); + d->setTopLevelScreen(newScreen, newScreen != 0); } /*! diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h index c496d7716b..9151e76ea4 100644 --- a/src/gui/kernel/qwindow_p.h +++ b/src/gui/kernel/qwindow_p.h @@ -45,6 +45,7 @@ // We mean it. // +#include #include #include @@ -73,6 +74,7 @@ public: , parentWindow(0) , platformWindow(0) , visible(false) + , visibilityOnDestroy(false) , exposed(false) , windowState(Qt::WindowNoState) , visibility(QWindow::Hidden) @@ -147,6 +149,7 @@ public: QWindow *parentWindow; QPlatformWindow *platformWindow; bool visible; + bool visibilityOnDestroy; bool exposed; QSurfaceFormat requestedFormat; QString windowTitle; @@ -175,7 +178,7 @@ public: int updateTimer; QPointer transientParent; - QScreen *topLevelScreen; + QPointer topLevelScreen; #ifndef QT_NO_CURSOR QCursor cursor; diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 93914c0cba..48cf366d28 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -332,6 +332,9 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra initializeXRandr(); updateScreens(); + if (m_screens.isEmpty()) + qFatal("QXcbConnection: no screens available"); + initializeXFixes(); initializeXRender(); m_xi2Enabled = false; diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index 8974ab549f..915287d93e 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -249,8 +249,13 @@ void *QXcbNativeInterface::nativeResourceForContext(const QByteArray &resourceSt void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resourceString, QScreen *screen) { + if (!screen) { + qWarning() << "nativeResourceForScreen: null screen"; + return Q_NULLPTR; + } + QByteArray lowerCaseResource = resourceString.toLower(); - void *result = handlerNativeResourceForScreen(lowerCaseResource ,screen); + void *result = handlerNativeResourceForScreen(lowerCaseResource, screen); if (result) return result; diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 0f25d10aac..b5eed4f4fe 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -417,6 +417,13 @@ void QXcbScreen::handleScreenChange(xcb_randr_screen_change_notify_event_t *chan 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()); + xcbWin->maybeSetScreen(this); + } } void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp) diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index c407caae2f..0e28cf2698 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -556,6 +556,14 @@ void QXcbWindow::destroy() m_pendingSyncRequest->invalidate(); } +void QXcbWindow::maybeSetScreen(QXcbScreen *screen) +{ + if (!window()->screen() && screen->geometry().contains(geometry().topLeft() * int(devicePixelRatio()))) { + QWindowSystemInterface::handleWindowScreenChanged(window(), static_cast(screen)->screen()); + QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(0, 0), window()->size()))); + } +} + void QXcbWindow::setGeometry(const QRect &rect) { QPlatformWindow::setGeometry(rect); @@ -737,13 +745,15 @@ void QXcbWindow::hide() Q_XCB_CALL(xcb_unmap_window(xcb_connection(), m_window)); // send synthetic UnmapNotify event according to icccm 4.1.4 - 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)); + 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_flush(xcb_connection()); @@ -1794,7 +1804,9 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even QPlatformScreen *QXcbWindow::screenForNativeGeometry(const QRect &newGeometry) const { QXcbScreen *currentScreen = static_cast(screen()); - if (!parent() && !currentScreen->nativeGeometry().intersects(newGeometry)) { + if (!currentScreen && QGuiApplication::primaryScreen()) + currentScreen = static_cast(QGuiApplication::primaryScreen()->handle()); + if (currentScreen && !parent() && !currentScreen->nativeGeometry().intersects(newGeometry)) { Q_FOREACH (QPlatformScreen* screen, currentScreen->virtualSiblings()) { if (static_cast(screen)->nativeGeometry().intersects(newGeometry)) return screen; @@ -1807,7 +1819,7 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t * { bool fromSendEvent = (event->response_type & 0x80); QPoint pos(event->x, event->y); - if (!parent() && !fromSendEvent) { + if (!parent() && !fromSendEvent && xcbScreen()) { // 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); @@ -1825,10 +1837,10 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t * QPlatformWindow::setGeometry(rect); QWindowSystemInterface::handleGeometryChange(window(), rect); - QPlatformScreen *newScreen = screenForNativeGeometry(nativeRect); if (newScreen != screen()) { - QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); + if (newScreen) + QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->screen()); int newDpr = devicePixelRatio(); if (newDpr != dpr) { QRect newRect = mapGeometryFromNative(nativeRect, newDpr); @@ -2116,7 +2128,7 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev m_windowState = newState; } return; - } else if (event->atom == atom(QXcbAtom::_NET_WORKAREA) && event->window == xcbScreen()->root()) { + } else if (event->atom == atom(QXcbAtom::_NET_WORKAREA) && xcbScreen() && event->window == xcbScreen()->root()) { xcbScreen()->updateGeometry(event->time); } } diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index 63fdb250da..981f4e7496 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -153,6 +153,7 @@ public: virtual void create(); virtual void destroy(); + void maybeSetScreen(QXcbScreen *screen); QPlatformScreen *screenForNativeGeometry(const QRect &newGeometry) const; public Q_SLOTS: diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index d6a106cef5..ca82dadabb 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -5536,7 +5536,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP } // Native widgets need to be marked dirty on screen so painting will be done in correct context - if (backingStore && !onScreen && !asRoot && (q->internalWinId() || !q->nativeParentWidget()->isWindow())) + if (backingStore && !onScreen && !asRoot && (q->internalWinId() || (q->nativeParentWidget() && !q->nativeParentWidget()->isWindow()))) backingStore->markDirtyOnScreen(toBePainted, q, offset); //restore diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index ed5d56c689..5724df6c55 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -635,7 +635,8 @@ void QWidgetWindow::handleScreenChange() sendScreenChangeRecursively(m_widget); // Invalidate the backing store buffer and repaint immediately. - repaintWindow(); + if (screen()) + repaintWindow(); } void QWidgetWindow::repaintWindow() -- cgit v1.2.3