diff options
author | Shawn Rutledge <shawn.rutledge@digia.com> | 2012-09-19 11:55:44 +0200 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2012-09-24 00:26:07 +0200 |
commit | 1a31561178d9cb9e5a6f3f986075df24ea5705ff (patch) | |
tree | 0953a9e349b258e00dc86e759338b2a012ee28a8 /src | |
parent | 06c89ae026d5a3579636d8a090cf546b23273bed (diff) |
xcb: dynamic QScreens; primary first; corrected logical DPI
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 <samuel.rodal@digia.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 224 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.h | 6 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbintegration.cpp | 4 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbintegration.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbscreen.cpp | 8 | ||||
-rw-r--r-- | 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 26f304b882..9e394d674f 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 <QtAlgorithms> #include <QSocketNotifier> @@ -100,27 +101,147 @@ static int nullErrorHandler(Display *, XErrorEvent *) } #endif -QXcbScreen* QXcbConnection::createScreenWithFabricatedName(int screenNumber, xcb_screen_t* xcbScreen) +QXcbScreen* QXcbConnection::findOrCreateScreen(QList<QXcbScreen *>& 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<QXcbScreen *> activeScreens; + QList<QXcbScreen *> 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<QPlatformScreen *> 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<QPlatformScreen *> 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 df91ad6e2f..08dd304b3d 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<QXcbScreen *>& 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<QXcbScreen *> 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 0b7f17d321..8784046560 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 2a7bc603da..7f7c52372c 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.h +++ b/src/plugins/platforms/xcb/qxcbintegration.h @@ -106,6 +106,8 @@ private: #endif QScopedPointer<QPlatformServices> 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 3798bf0050..6452186bbf 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 f5439b3a93..d9eee464dc 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; } |