summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorShawn Rutledge <shawn.rutledge@digia.com>2012-09-19 11:55:44 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2012-09-24 00:26:07 +0200
commit1a31561178d9cb9e5a6f3f986075df24ea5705ff (patch)
tree0953a9e349b258e00dc86e759338b2a012ee28a8 /src
parent06c89ae026d5a3579636d8a090cf546b23273bed (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.cpp224
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h6
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp4
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp8
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.h1
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 &parameters)
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; }