Handle gracefully the removal and re-attachment of all QScreens
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 <jorgen.lind@theqtcompany.com>bb10
parent
45b60c01ef
commit
f4b8697c40
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtGui/qscreen.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
#include <qpa/qplatformwindow.h>
|
||||
|
||||
|
|
@ -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<QWindow> transientParent;
|
||||
QScreen *topLevelScreen;
|
||||
QPointer<QScreen> topLevelScreen;
|
||||
|
||||
#ifndef QT_NO_CURSOR
|
||||
QCursor cursor;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<QXcbWindow*>(window->handle());
|
||||
xcbWin->maybeSetScreen(this);
|
||||
}
|
||||
}
|
||||
|
||||
void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp)
|
||||
|
|
|
|||
|
|
@ -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<QPlatformScreen *>(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<QXcbScreen*>(screen());
|
||||
if (!parent() && !currentScreen->nativeGeometry().intersects(newGeometry)) {
|
||||
if (!currentScreen && QGuiApplication::primaryScreen())
|
||||
currentScreen = static_cast<QXcbScreen*>(QGuiApplication::primaryScreen()->handle());
|
||||
if (currentScreen && !parent() && !currentScreen->nativeGeometry().intersects(newGeometry)) {
|
||||
Q_FOREACH (QPlatformScreen* screen, currentScreen->virtualSiblings()) {
|
||||
if (static_cast<QXcbScreen*>(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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ public:
|
|||
|
||||
virtual void create();
|
||||
virtual void destroy();
|
||||
void maybeSetScreen(QXcbScreen *screen);
|
||||
QPlatformScreen *screenForNativeGeometry(const QRect &newGeometry) const;
|
||||
|
||||
public Q_SLOTS:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue