commit 52f5e50f11a3ba82e32dc2efc656e4021a3fa4f5 Author: Sandro Mani Date: Mon Feb 2 17:14:40 2015 +0100 Ensure QGuiApplicationPrivate::screen_list is correctly populated Ensure QGuiApplicationPrivate::screen_list always contains at least one screen, and that the first item (returned by QGuiApplication::primaryScreen) is always the current primary screen Task-number: QTBUG-42985 Change-Id: I08b880b3e94387f28230ed5fc738bceea943bad3 Reviewed-by: Jørgen Lind diff --git a/src/gui/kernel/qplatformintegration.cpp b/src/gui/kernel/qplatformintegration.cpp index 93b1359..13f2880 100644 --- a/src/gui/kernel/qplatformintegration.cpp +++ b/src/gui/kernel/qplatformintegration.cpp @@ -431,11 +431,15 @@ QList QPlatformIntegration::possibleKeys(const QKeyEvent *) const The screen should be deleted by calling QPlatformIntegration::destroyScreen(). */ -void QPlatformIntegration::screenAdded(QPlatformScreen *ps) +void QPlatformIntegration::screenAdded(QPlatformScreen *ps, bool isPrimary) { QScreen *screen = new QScreen(ps); ps->d_func()->screen = screen; - QGuiApplicationPrivate::screen_list << screen; + if (isPrimary) { + QGuiApplicationPrivate::screen_list.prepend(screen); + } else { + QGuiApplicationPrivate::screen_list.append(screen); + } emit qGuiApp->screenAdded(screen); } diff --git a/src/gui/kernel/qplatformintegration.h b/src/gui/kernel/qplatformintegration.h index 9b7e2df..067d8d9 100644 --- a/src/gui/kernel/qplatformintegration.h +++ b/src/gui/kernel/qplatformintegration.h @@ -169,7 +169,7 @@ public: #endif protected: - void screenAdded(QPlatformScreen *screen); + void screenAdded(QPlatformScreen *screen, bool isPrimary = false); void destroyScreen(QPlatformScreen *screen); }; diff --git a/src/plugins/platforms/windows/qwindowsintegration.h b/src/plugins/platforms/windows/qwindowsintegration.h index ff26342..bfde2bd 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.h +++ b/src/plugins/platforms/windows/qwindowsintegration.h @@ -93,7 +93,7 @@ public: static QWindowsIntegration *instance() { return m_instance; } - inline void emitScreenAdded(QPlatformScreen *s) { screenAdded(s); } + inline void emitScreenAdded(QPlatformScreen *s, bool isPrimary = false) { screenAdded(s, isPrimary); } inline void emitDestroyScreen(QPlatformScreen *s) { destroyScreen(s); } unsigned options() const; diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 79219e3..a401175 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -482,7 +482,8 @@ bool QWindowsScreenManager::handleScreenChanges() } else { QWindowsScreen *newScreen = new QWindowsScreen(newData); m_screens.push_back(newScreen); - QWindowsIntegration::instance()->emitScreenAdded(newScreen); + QWindowsIntegration::instance()->emitScreenAdded(newScreen, + newData.flags & QWindowsScreenData::PrimaryScreen); qCDebug(lcQpaWindows) << "New Monitor: " << newData; } // exists } // for new screens. diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 0091736..e416865 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -182,9 +182,8 @@ QXcbScreen* QXcbConnection::findOrCreateScreen(QList& newScreens, void QXcbConnection::updateScreens() { xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); - int screenNumber = 0; // index of this QScreen in QGuiApplication::screens() int xcbScreenNumber = 0; // screen number in the xcb sense - QSet activeScreens; + QList activeScreens; QList newScreens; QXcbScreen* primaryScreen = NULL; while (it.rem) { @@ -195,6 +194,7 @@ void QXcbConnection::updateScreens() xcb_screen_t *xcbScreen = it.data; QList siblings; int outputCount = 0; + int connectedOutputCount = 0; if (has_randr_extension) { xcb_generic_error_t *error = NULL; xcb_randr_get_output_primary_cookie_t primaryCookie = @@ -239,7 +239,7 @@ void QXcbConnection::updateScreens() QXcbScreen *screen = findOrCreateScreen(newScreens, xcbScreenNumber, xcbScreen, output); siblings << screen; activeScreens << screen; - ++screenNumber; + ++connectedOutputCount; // There can be multiple outputs per screen, use either // the first or an exact match. An exact match isn't // always available if primary->output is XCB_NONE @@ -262,7 +262,7 @@ void QXcbConnection::updateScreens() } // If there's no randr extension, or there was some error above, or the screen // doesn't have outputs for some other reason (e.g. on VNC or ssh -X), just assume there is one screen. - if (outputCount == 0) { + if (connectedOutputCount == 0) { #ifdef Q_XCB_DEBUG qDebug("Found a screen with zero outputs"); #endif @@ -271,7 +271,6 @@ void QXcbConnection::updateScreens() activeScreens << screen; if (!primaryScreen) primaryScreen = screen; - ++screenNumber; } foreach (QPlatformScreen* s, siblings) ((QXcbScreen*)s)->setVirtualSiblings(siblings); @@ -280,28 +279,50 @@ void QXcbConnection::updateScreens() } // for each xcb screen QXcbIntegration *integration = QXcbIntegration::instance(); - // Now activeScreens is the complete set of screens which are active at this time. - // Delete any existing screens which are not in activeScreens + + // Rebuild screen list, ensuring primary screen is always in front, + // both in the QXcbConnection::m_screens list as well as in the + // QGuiApplicationPrivate::screen_list list, which gets updated via + // - screen added: integration->screenAdded() + // - screen removed: integration->destroyScreen + + // Gather screens to delete + QList screensToDelete; for (int i = m_screens.count() - 1; i >= 0; --i) { if (!activeScreens.contains(m_screens[i])) { - integration->destroyScreen(m_screens.at(i)); - m_screens.removeAt(i); + screensToDelete.append(m_screens.takeAt(i)); } } - // Add any new screens, and make sure the primary screen comes first - // since it is used by QGuiApplication::primaryScreen() + // If there is a new primary screen, add that one first + if (newScreens.contains(primaryScreen)) { + newScreens.removeOne(primaryScreen); + m_screens.prepend(primaryScreen); + integration->screenAdded(primaryScreen, true); + } + + // Add the remaining new screens foreach (QXcbScreen* screen, newScreens) { - if (screen == primaryScreen) - m_screens.prepend(screen); - else - m_screens.append(screen); + m_screens.append(screen); + integration->screenAdded(screen); } - // Now that they are in the right order, emit the added signals for new screens only - foreach (QXcbScreen* screen, m_screens) - if (newScreens.contains(screen)) - integration->screenAdded(screen); + // Delete the old screens, now that the new ones were added + // and we are sure that there is at least one screen available + foreach (QXcbScreen* screen, screensToDelete) { + integration->destroyScreen(screen); + } + + // Ensure that the primary screen is first in m_screens too + // (in case the assignment of primary was the only change, + // without adding or removing screens) + if (primaryScreen) { + Q_ASSERT(!m_screens.isEmpty()); + if (m_screens.first() != primaryScreen) { + m_screens.removeOne(primaryScreen); + m_screens.prepend(primaryScreen); + } + } } QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, const char *displayName)