From 1a31561178d9cb9e5a6f3f986075df24ea5705ff Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 19 Sep 2012 11:55:44 +0200 Subject: [PATCH] xcb: dynamic QScreens; primary first; corrected logical DPI MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit A new QScreen is created when an output is activated (monitor or projector is added, for example), and destroyed when the output is turned off. Ensures that screens and siblings are always in the right order: primary comes first. Logical DPI is derived from virtual geom / virtual size, which will be different than output geom / physical size if X was started with --dpi override. This is a good thing: when X gets wrong EDID info for physical size and you need to override it to get reasonable font sizes, Qt will heed the logical DPI for font sizing. Change-Id: I5e3de34013c1b6b21067243de56f3f1eb72787fa Reviewed-by: Samuel Rødal --- src/plugins/platforms/xcb/qxcbconnection.cpp | 224 ++++++++++++++++---------- src/plugins/platforms/xcb/qxcbconnection.h | 6 +- src/plugins/platforms/xcb/qxcbintegration.cpp | 4 - src/plugins/platforms/xcb/qxcbintegration.h | 2 + src/plugins/platforms/xcb/qxcbscreen.cpp | 8 +- src/plugins/platforms/xcb/qxcbscreen.h | 1 + 6 files changed, 151 insertions(+), 94 deletions(-) diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 26f304b..9e394d6 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -50,6 +50,7 @@ #include "qxcbdrag.h" #include "qxcbwmsupport.h" #include "qxcbnativeinterface.h" +#include "qxcbintegration.h" #include #include @@ -100,27 +101,147 @@ static int nullErrorHandler(Display *, XErrorEvent *) } #endif -QXcbScreen* QXcbConnection::createScreenWithFabricatedName(int screenNumber, xcb_screen_t* xcbScreen) +QXcbScreen* QXcbConnection::findOrCreateScreen(QList& newScreens, + int screenNumber, xcb_screen_t* xcbScreen, xcb_randr_get_output_info_reply_t *output) { - QByteArray displayName = m_displayName; - int dotPos = displayName.lastIndexOf('.'); - if (dotPos != -1) - displayName.truncate(dotPos); - QString name = displayName + QLatin1Char('.') + QString::number(screenNumber); - QXcbScreen *screen = new QXcbScreen(this, xcbScreen, NULL, name, screenNumber); - // make sure the primary screen appears first since it is used by QGuiApplication::primaryScreen() - if (m_primaryScreen == screenNumber) { - m_screens.prepend(screen); - } else { - m_screens.append(screen); + 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 = displayName + QLatin1Char('.') + QString::number(screenNumber); } - return screen; + foreach (QXcbScreen* scr, m_screens) + if (scr->name() == name && scr->root() == xcbScreen->root) + return scr; + QXcbScreen *ret = new QXcbScreen(this, xcbScreen, output, name, screenNumber); + newScreens << ret; + return ret; +} + +/*! + \brief Synchronizes the screen list, adds new screens, removes deleted ones +*/ +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 newScreens; + QXcbScreen* primaryScreen = NULL; + while (it.rem) { + // Each "screen" in xcb terminology is a virtual desktop, + // potentially a collection of separate juxtaposed monitors. + // But we want a separate QScreen for each output (e.g. DVI-I-1, VGA-1, etc.) + // which will become virtual siblings. + xcb_screen_t *xcbScreen = it.data; + QList siblings; + if (has_randr_extension) { + xcb_randr_get_output_primary_cookie_t primaryCookie = + xcb_randr_get_output_primary_unchecked(xcb_connection(), xcbScreen->root); + xcb_randr_get_screen_resources_current_cookie_t resourcesCookie = + xcb_randr_get_screen_resources_current_unchecked(xcb_connection(), xcbScreen->root); + xcb_randr_get_output_primary_reply_t *primary = + xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, NULL); + xcb_randr_get_screen_resources_current_reply_t *resources = + xcb_randr_get_screen_resources_current_reply(xcb_connection(), resourcesCookie, NULL); + xcb_timestamp_t timestamp = resources->config_timestamp; + int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources); + xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(resources); + + if (outputCount == 0) { + // This happens on VNC for example. But there is actually a screen anyway. +#ifdef Q_XCB_DEBUG + qDebug("Found a screen with zero outputs"); +#endif + QXcbScreen* screen = findOrCreateScreen(newScreens, xcbScreenNumber, xcbScreen); + siblings << screen; + activeScreens << screen; + if (!primaryScreen) + primaryScreen = screen; + ++screenNumber; + } + for (int i = 0; i < outputCount; i++) { + xcb_randr_get_output_info_reply_t *output = + xcb_randr_get_output_info_reply(xcb_connection(), + xcb_randr_get_output_info_unchecked(xcb_connection(), outputs[i], timestamp), NULL); + if (output == NULL) + continue; + +#ifdef Q_XCB_DEBUG + QString outputName = QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output), + xcb_randr_get_output_info_name_length(output)); +#endif + + if (output->crtc == XCB_NONE) { +#ifdef Q_XCB_DEBUG + qDebug("Screen output %s is not connected", qPrintable(outputName)); +#endif + continue; + } + + QXcbScreen *screen = findOrCreateScreen(newScreens, xcbScreenNumber, xcbScreen, output); + siblings << screen; + activeScreens << screen; + ++screenNumber; + if (!primaryScreen && primary) { + if (primary->output == XCB_NONE || outputs[i] == primary->output) { + primaryScreen = screen; + siblings.prepend(siblings.takeLast()); +#ifdef Q_XCB_DEBUG + qDebug("Primary output is %d: %s", primary->output, qPrintable(outputName)); +#endif + } + } + free(output); + } + free(primary); + free(resources); + } else { + QXcbScreen *screen = findOrCreateScreen(newScreens, xcbScreenNumber, xcbScreen); + siblings << screen; + activeScreens << screen; + if (!primaryScreen) + primaryScreen = screen; + ++screenNumber; + } + foreach (QPlatformScreen* s, siblings) + ((QXcbScreen*)s)->setVirtualSiblings(siblings); + xcb_screen_next(&it); + ++xcbScreenNumber; + } + + // Now activeScreens is the complete set of screens which are active at this time. + // Delete any existing screens which are not in activeScreens + for (int i = m_screens.count() - 1; i >= 0; --i) + if (!activeScreens.contains(m_screens[i])) { + delete m_screens[i]; + m_screens.removeAt(i); + } + + // Add any new screens, and make sure the primary screen comes first + // since it is used by QGuiApplication::primaryScreen() + foreach (QXcbScreen* screen, newScreens) { + if (screen == primaryScreen) + m_screens.prepend(screen); + else + m_screens.append(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)) + ((QXcbIntegration*)QGuiApplicationPrivate::platformIntegration())->screenAdded(screen); } QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, const char *displayName) : m_connection(0) , m_primaryScreen(0) - , m_primaryOutput(-1) , m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY")) , m_nativeInterface(nativeInterface) #ifdef XCB_USE_XINPUT2_MAEMO @@ -182,80 +303,8 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, const char m_time = XCB_CURRENT_TIME; - xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); - initializeXRandr(); - - int screenNumber = 0; - while (it.rem) { - // Each "screen" in xcb terminology is a virtual desktop, - // potentially a collection of separate juxtaposed monitors. - // Now iterate the individual outputs (e.g. DVI-I-1, VGA-1, etc.) - // and make a QScreen instance for each. - xcb_screen_t *xcbScreen = it.data; - QList siblings; - if (has_randr_extension) { - xcb_randr_get_output_primary_cookie_t primaryCookie = - xcb_randr_get_output_primary_unchecked(xcb_connection(), xcbScreen->root); - xcb_randr_get_screen_resources_current_cookie_t resourcesCookie = - xcb_randr_get_screen_resources_current_unchecked(xcb_connection(), xcbScreen->root); - xcb_randr_get_output_primary_reply_t *primary = - xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, NULL); - xcb_randr_get_screen_resources_current_reply_t *resources = - xcb_randr_get_screen_resources_current_reply(xcb_connection(), resourcesCookie, NULL); - xcb_timestamp_t timestamp = resources->config_timestamp; - int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources); - xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(resources); - - if (outputCount == 0) { - // This happens on VNC for example. But there is actually a screen anyway. -#ifdef Q_XCB_DEBUG - qDebug("Found a screen with zero outputs"); -#endif - QXcbScreen *screen = createScreenWithFabricatedName(screenNumber, it.data); - siblings << screen; - ++screenNumber; - } - for (int i = 0; i < outputCount; i++) { - xcb_randr_get_output_info_reply_t *output = - xcb_randr_get_output_info_reply(xcb_connection(), - xcb_randr_get_output_info_unchecked(xcb_connection(), outputs[i], timestamp), NULL); - if (output == NULL) - continue; - QString outputName = QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output), - xcb_randr_get_output_info_name_length(output)); - - if (output->crtc == XCB_NONE) { -#ifdef Q_XCB_DEBUG - qDebug("Screen output %s is not connected", qPrintable(outputName)); -#endif - continue; - } - - QXcbScreen *screen = new QXcbScreen(this, xcbScreen, output, outputName, screenNumber); - siblings << screen; - // make sure the primary screen appears first since it is used by QGuiApplication::primaryScreen() - if (outputs[i] == primary->output && m_primaryOutput < 0) { - m_primaryOutput = screenNumber; - m_screens.prepend(screen); - } else { - m_screens.append(screen); - } - ++screenNumber; - free(output); - } - - free(primary); - free(resources); - } else { - QXcbScreen *screen = createScreenWithFabricatedName(screenNumber, it.data); - siblings << screen; - ++screenNumber; - } - foreach (QPlatformScreen* s, siblings) - ((QXcbScreen*)s)->setVirtualSiblings(siblings); - xcb_screen_next(&it); - } + updateScreens(); m_connectionEventListener = xcb_generate_id(m_connection); xcb_create_window(m_connection, XCB_COPY_FROM_PARENT, @@ -709,6 +758,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) #endif handled = true; } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { + updateScreens(); xcb_randr_screen_change_notify_event_t *change_event = (xcb_randr_screen_change_notify_event_t *)event; foreach (QXcbScreen *s, m_screens) { if (s->root() == change_event->root ) { diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index df91ad6..08dd304 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -66,6 +66,7 @@ struct XInput2MaemoData; #endif struct XInput2DeviceData; #endif +struct xcb_randr_get_output_info_reply_t; //#define Q_XCB_DEBUG @@ -398,7 +399,9 @@ private: void handleGenericEventMaemo(xcb_ge_event_t *event); #endif void handleClientMessageEvent(const xcb_client_message_event_t *event); - QXcbScreen* createScreenWithFabricatedName(int screenNumber, xcb_screen_t* xcbScreen); + QXcbScreen* findOrCreateScreen(QList& newScreens, int screenNumber, + xcb_screen_t* xcbScreen, xcb_randr_get_output_info_reply_t *output = NULL); + void updateScreens(); bool m_xi2Enabled; int m_xi2Minor; @@ -443,7 +446,6 @@ private: QList m_screens; int m_primaryScreen; - int m_primaryOutput; xcb_atom_t m_allAtoms[QXcbAtom::NAtoms]; diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index 0b7f17d..8784046 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -110,10 +110,6 @@ QXcbIntegration::QXcbIntegration(const QStringList ¶meters) m_connections << new QXcbConnection(m_nativeInterface.data(), display.toLatin1().constData()); } - foreach (QXcbConnection *connection, m_connections) - foreach (QXcbScreen *screen, connection->screens()) - screenAdded(screen); - m_fontDatabase.reset(new QGenericUnixFontDatabase()); m_inputContext.reset(QPlatformInputContextFactory::create()); #ifndef QT_NO_ACCESSIBILITY diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h index 2a7bc60..7f7c523 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.h +++ b/src/plugins/platforms/xcb/qxcbintegration.h @@ -106,6 +106,8 @@ private: #endif QScopedPointer m_services; + + friend class QXcbConnection; // access QPlatformIntegration::screenAdded() }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 3798bf0..6452186 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -83,7 +83,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr, #ifdef Q_XCB_DEBUG qDebug(); - qDebug("Screen %s:", m_outputName.toUtf8().constData()); + qDebug("Screen output %s of xcb screen %d:", m_outputName.toUtf8().constData(), m_number); qDebug(" width..........: %lf", m_sizeMillimeters.width()); qDebug(" height.........: %lf", m_sizeMillimeters.height()); qDebug(" geometry.......: %d x %d +%d +%d", m_geometry.width(), m_geometry.height(), m_geometry.x(), m_geometry.y()); @@ -240,6 +240,12 @@ QImage::Format QXcbScreen::format() const return QImage::Format_RGB32; } +QDpi QXcbScreen::logicalDpi() const +{ + return QDpi(25.4 * m_virtualSize.width() / m_virtualSizeMillimeters.width(), + 25.4 * m_virtualSize.height() / m_virtualSizeMillimeters.height()); +} + QPlatformCursor *QXcbScreen::cursor() const { return m_cursor; diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index f5439b3..d9eee46 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -71,6 +71,7 @@ public: int depth() const { return m_screen->root_depth; } QImage::Format format() const; QSizeF physicalSize() const { return m_sizeMillimeters; } + QDpi logicalDpi() const; QPlatformCursor *cursor() const; qreal refreshRate() const { return m_refreshRate; } Qt::ScreenOrientation orientation() const { return m_orientation; } -- 2.7.4