From f48170b479df359f47af12b03a501d9d0c386e66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82a=C5=BCej=20Szczygie=C5=82?= Date: Mon, 2 Nov 2015 00:26:39 +0100 Subject: xcb: Add Xinerama support This patch makes possible to use Xinerama screens in XCB platform plugin. Task-number: QTBUG-48615 Change-Id: Ib4dbfcdfadc46d2875a2fc09e8b852181edfbed2 Reviewed-by: Shawn Rutledge --- src/plugins/platforms/xcb/README | 6 ++-- src/plugins/platforms/xcb/qxcbconnection.cpp | 54 ++++++++++++++++++++++++---- src/plugins/platforms/xcb/qxcbconnection.h | 2 ++ src/plugins/platforms/xcb/qxcbscreen.cpp | 26 +++++++++----- src/plugins/platforms/xcb/qxcbscreen.h | 4 ++- src/plugins/platforms/xcb/xcb_qpa_lib.pro | 2 +- 6 files changed, 75 insertions(+), 19 deletions(-) (limited to 'src/plugins/platforms') diff --git a/src/plugins/platforms/xcb/README b/src/plugins/platforms/xcb/README index 2f666bebfd..15cf4cf241 100644 --- a/src/plugins/platforms/xcb/README +++ b/src/plugins/platforms/xcb/README @@ -3,14 +3,14 @@ Requires libxcb >= 1.5. PACKAGE DEPENDENCIES Required packages: -libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm1 libxcb-icccm1-dev libxcb-sync0 libxcb-sync0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-glx0-dev +libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm1 libxcb-icccm1-dev libxcb-sync0 libxcb-sync0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-glx0-dev libxcb-xinerama0-dev On Ubuntu 11.10 icccm1 is replaced by icccm4 and xcb-render-util is not available: -libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-glx0-dev +libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-glx0-dev libxcb-xinerama0-dev The packages for xcb-render-util can be installed manually from http://packages.ubuntu.com/natty/libxcb-render-util0 and http://packages.ubuntu.com/natty/libxcb-render-util0-dev On Ubuntu 12.04 icccm1 is replaced by icccm4 and xcb-render-util can be installed automatically: -libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-glx0-dev +libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-glx0-dev libxcb-xinerama0-dev On Fedora, the following packages are required: diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 231fe9af3f..f93cfde4a5 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #ifdef XCB_USE_XLIB #include @@ -386,6 +387,7 @@ void QXcbConnection::initializeScreens() xcb_screen_t *xcbScreen = it.data; QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber); m_virtualDesktops.append(virtualDesktop); + QList siblings; if (has_randr_extension) { xcb_generic_error_t *error = NULL; // RRGetScreenResourcesCurrent is fast but it may return nothing if the @@ -429,7 +431,6 @@ void QXcbConnection::initializeScreens() qWarning("failed to get the primary output of the screen"); free(error); } else { - QList siblings; for (int i = 0; i < outputCount; i++) { QScopedPointer output( xcb_randr_get_output_info_reply(xcb_connection(), @@ -471,12 +472,30 @@ void QXcbConnection::initializeScreens() } } } - virtualDesktop->setScreens(siblings); } } } + } else if (has_xinerama_extension) { + // Xinerama is available + xcb_xinerama_query_screens_cookie_t cookie = xcb_xinerama_query_screens(m_connection); + xcb_xinerama_query_screens_reply_t *screens = xcb_xinerama_query_screens_reply(m_connection, + cookie, + Q_NULLPTR); + if (screens) { + xcb_xinerama_screen_info_iterator_t it = xcb_xinerama_query_screens_screen_info_iterator(screens); + while (it.rem) { + xcb_xinerama_screen_info_t *screen_info = it.data; + QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, + XCB_NONE, Q_NULLPTR, + screen_info, it.index); + siblings << screen; + m_screens << screen; + xcb_xinerama_screen_info_next(&it); + } + free(screens); + } } - if (virtualDesktop->screens().isEmpty()) { + if (siblings.isEmpty()) { // If there are no XRandR outputs or XRandR extension is missing, // then create a fake/legacy screen. QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, Q_NULLPTR); @@ -486,8 +505,9 @@ void QXcbConnection::initializeScreens() primaryScreen = screen; primaryScreen->setPrimary(true); } - virtualDesktop->addScreen(screen); + siblings << screen; } + virtualDesktop->setScreens(siblings); xcb_screen_next(&it); ++xcbScreenNumber; } // for each xcb screen @@ -529,6 +549,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra , xfixes_first_event(0) , xrandr_first_event(0) , xkb_first_event(0) + , has_xinerama_extension(false) , has_shape_extension(false) , has_randr_extension(false) , has_input_shape(false) @@ -583,7 +604,10 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra m_time = XCB_CURRENT_TIME; m_netWmUserTime = XCB_CURRENT_TIME; - initializeXRandr(); + if (!qEnvironmentVariableIsSet("QT_XCB_NO_XRANDR")) + initializeXRandr(); + if (!has_randr_extension) + initializeXinerama(); initializeXFixes(); initializeScreens(); @@ -2087,6 +2111,22 @@ void QXcbConnection::initializeXRandr() } } +void QXcbConnection::initializeXinerama() +{ + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xinerama_id); + if (!reply || !reply->present) + return; + + xcb_generic_error_t *error = Q_NULLPTR; + xcb_xinerama_is_active_cookie_t xinerama_query_cookie = xcb_xinerama_is_active(m_connection); + xcb_xinerama_is_active_reply_t *xinerama_is_active = xcb_xinerama_is_active_reply(m_connection, + xinerama_query_cookie, + &error); + has_xinerama_extension = xinerama_is_active && !error && xinerama_is_active->state; + free(error); + free(xinerama_is_active); +} + void QXcbConnection::initializeXShape() { const xcb_query_extension_reply_t *xshape_reply = xcb_get_extension_data(m_connection, &xcb_shape_id); @@ -2174,7 +2214,9 @@ void QXcbConnection::initializeXKB() bool QXcbConnection::xi2MouseEvents() const { static bool mouseViaXI2 = !qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE"); - return mouseViaXI2; + // Don't use XInput2 when Xinerama extension is enabled, + // because it causes problems with multi-monitor setup. + return mouseViaXI2 && !has_xinerama_extension; } #if defined(XCB_USE_XINPUT2) diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index fb7cc137b9..6d26e88fa2 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -515,6 +515,7 @@ private: void initializeXFixes(); void initializeXRender(); void initializeXRandr(); + void initializeXinerama(); void initializeXShape(); void initializeXKB(); void handleClientMessageEvent(const xcb_client_message_event_t *event); @@ -639,6 +640,7 @@ private: uint32_t xrandr_first_event; uint32_t xkb_first_event; + bool has_xinerama_extension; bool has_shape_extension; bool has_randr_extension; bool has_input_shape; diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index f05432ef68..caddd2b2a5 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -155,8 +155,15 @@ void QXcbVirtualDesktop::updateWorkArea() } } +static inline QSizeF sizeInMillimeters(const QSize &size, const QDpi &dpi) +{ + return QSizeF(Q_MM_PER_INCH * size.width() / dpi.first, + Q_MM_PER_INCH * size.height() / dpi.second); +} + QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop, - xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output) + xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *output, + const xcb_xinerama_screen_info_t *xineramaScreenInfo, int xineramaScreenIdx) : QXcbObject(connection) , m_virtualDesktop(virtualDesktop) , m_output(outputId) @@ -188,8 +195,14 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe updateRefreshRate(crtc->mode); free(crtc); } - } else { - updateGeometry(output ? output->timestamp : 0); + } else if (xineramaScreenInfo) { + m_geometry = QRect(xineramaScreenInfo->x_org, xineramaScreenInfo->y_org, + xineramaScreenInfo->width, xineramaScreenInfo->height); + m_nativeGeometry = m_geometry; + m_availableGeometry = m_geometry & m_virtualDesktop->workArea(); + m_sizeMillimeters = sizeInMillimeters(m_geometry.size(), virtualDpi()); + if (xineramaScreenIdx > -1) + m_outputName += QLatin1Char('-') + QString::number(xineramaScreenIdx); } if (m_geometry.isEmpty()) { @@ -538,11 +551,8 @@ void QXcbScreen::updateGeometry(const QRect &geom, uint8_t rotation) // It can be that physical size is unknown while virtual size // is known (probably back-calculated from DPI and resolution), // e.g. on VNC or with some hardware. - if (m_sizeMillimeters.isEmpty()) { - QDpi dpi = virtualDpi(); - m_sizeMillimeters = QSizeF(Q_MM_PER_INCH * xGeometry.width() / dpi.first, - Q_MM_PER_INCH * xGeometry.width() / dpi.second); - } + if (m_sizeMillimeters.isEmpty()) + m_sizeMillimeters = sizeInMillimeters(xGeometry.size(), virtualDpi()); qreal dpi = xGeometry.width() / physicalSize().width() * qreal(25.4); m_pixelDensity = qRound(dpi/96); diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index 79620f40ec..dd7396aca2 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -40,6 +40,7 @@ #include #include #include +#include #include "qxcbobject.h" #include "qxcbscreen.h" @@ -102,7 +103,8 @@ class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen { public: QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDesktop, - xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *outputInfo); + xcb_randr_output_t outputId, xcb_randr_get_output_info_reply_t *outputInfo, + const xcb_xinerama_screen_info_t *xineramaScreenInfo = Q_NULLPTR, int xineramaScreenIdx = -1); ~QXcbScreen(); QString getOutputName(xcb_randr_get_output_info_reply_t *outputInfo); diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro index 60eb8a02e3..302d87e007 100644 --- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro +++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro @@ -92,7 +92,7 @@ contains(QT_CONFIG, xcb-qt) { INCLUDEPATH += $$XCB_DIR/include $$XCB_DIR/sysinclude LIBS += -lxcb -L$$OUT_PWD/xcb-static -lxcb-static } else { - LIBS += -lxcb -lxcb-image -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr -lxcb-shape -lxcb-keysyms + LIBS += -lxcb -lxcb-image -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr -lxcb-shape -lxcb-keysyms -lxcb-xinerama !contains(DEFINES, QT_NO_XKB):LIBS += -lxcb-xkb } -- cgit v1.2.3