summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb/qxcbconnection.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbconnection.cpp')
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp590
1 files changed, 336 insertions, 254 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 77e4601485..2084d7ea5d 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
-** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
@@ -10,9 +10,9 @@
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
@@ -23,8 +23,8 @@
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
@@ -44,11 +44,14 @@
#include "qxcbnativeinterface.h"
#include "qxcbintegration.h"
#include "qxcbsystemtraytracker.h"
+#include "qxcbglintegrationfactory.h"
+#include "qxcbglintegration.h"
#include <QSocketNotifier>
#include <QAbstractEventDispatcher>
#include <QTimer>
#include <QByteArray>
+#include <QScopedPointer>
#include <algorithm>
@@ -74,18 +77,11 @@
#include <xcb/render.h>
#endif
-#if defined(XCB_HAS_XCB_GLX)
-#include <xcb/glx.h>
-#endif
-
-#ifdef XCB_USE_EGL //don't pull in eglext prototypes
-#include <EGL/egl.h>
-#endif
-
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaXInput, "qt.qpa.input")
Q_LOGGING_CATEGORY(lcQpaXInputDevices, "qt.qpa.input.devices")
+Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen")
#ifdef XCB_USE_XLIB
static const char * const xcbConnectionErrors[] = {
@@ -121,41 +117,29 @@ static int ioErrorHandler(Display *dpy)
}
#endif
-#if defined(XCB_HAS_XCB_GLX) && XCB_GLX_MAJOR_VERSION == 1 && XCB_GLX_MINOR_VERSION < 4
-
-#define XCB_GLX_BUFFER_SWAP_COMPLETE 1
-
-typedef struct xcb_glx_buffer_swap_complete_event_t {
- uint8_t response_type;
- uint8_t pad0;
- uint16_t sequence;
- uint16_t event_type;
- uint8_t pad1[2];
- xcb_glx_drawable_t drawable;
- uint32_t ust_hi;
- uint32_t ust_lo;
- uint32_t msc_hi;
- uint32_t msc_lo;
- uint32_t sbc;
-} xcb_glx_buffer_swap_complete_event_t;
-#endif
+QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc)
+{
+ foreach (QXcbScreen *screen, m_screens) {
+ if (screen->root() == rootWindow && screen->crtc() == crtc)
+ return screen;
+ }
-#if defined(XCB_USE_XLIB) && defined(XCB_USE_GLX)
-typedef struct {
- int type;
- unsigned long serial; /* # of last request processed by server */
- Bool send_event; /* true if this came from a SendEvent request */
- Display *display; /* Display the event was read from */
- Drawable drawable; /* drawable on which event was requested in event mask */
- int event_type;
- int64_t ust;
- int64_t msc;
- int64_t sbc;
-} QGLXBufferSwapComplete;
-#endif
+ return 0;
+}
-QXcbScreen* QXcbConnection::findOrCreateScreen(QList<QXcbScreen *>& newScreens,
- int screenNumber, xcb_screen_t* xcbScreen, xcb_randr_get_output_info_reply_t *output)
+QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output)
+{
+ foreach (QXcbScreen *screen, m_screens) {
+ if (screen->root() == rootWindow && screen->output() == output)
+ return screen;
+ }
+
+ return 0;
+}
+
+QXcbScreen* QXcbConnection::createScreen(int screenNumber, xcb_screen_t* xcbScreen,
+ xcb_randr_output_t outputId,
+ xcb_randr_get_output_info_reply_t *output)
{
QString name;
if (output)
@@ -166,112 +150,249 @@ QXcbScreen* QXcbConnection::findOrCreateScreen(QList<QXcbScreen *>& newScreens,
int dotPos = displayName.lastIndexOf('.');
if (dotPos != -1)
displayName.truncate(dotPos);
- name = displayName + QLatin1Char('.') + QString::number(screenNumber);
+ name = QString::fromLocal8Bit(displayName) + QLatin1Char('.') + QString::number(screenNumber);
}
- 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;
+
+ return new QXcbScreen(this, xcbScreen, outputId, output, name, screenNumber);
+}
+
+bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output)
+{
+ xcb_generic_error_t *error = 0;
+ xcb_randr_get_output_primary_cookie_t primaryCookie =
+ xcb_randr_get_output_primary(xcb_connection(), rootWindow);
+ QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary (
+ xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error));
+ if (!primary || error) {
+ qWarning("failed to get the primary output of the screen");
+ free(error);
+ error = NULL;
+ }
+ const bool isPrimary = primary ? (primary->output == output) : false;
+
+ return isPrimary;
+}
+
+xcb_screen_t* QXcbConnection::xcbScreenForRootWindow(xcb_window_t rootWindow, int *xcbScreenNumber)
+{
+ xcb_screen_iterator_t xcbScreenIter = xcb_setup_roots_iterator(m_setup);
+ for (; xcbScreenIter.rem; xcb_screen_next(&xcbScreenIter)) {
+ if (xcbScreenIter.data->root == rootWindow) {
+ if (xcbScreenNumber)
+ *xcbScreenNumber = xcb_setup_roots_length(m_setup) - xcbScreenIter.rem;
+ return xcbScreenIter.data;
+ }
+ }
+
+ return 0;
}
/*!
\brief Synchronizes the screen list, adds new screens, removes deleted ones
*/
-void QXcbConnection::updateScreens()
+void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
+{
+ if (event->subCode == XCB_RANDR_NOTIFY_CRTC_CHANGE) {
+ xcb_randr_crtc_change_t crtc = event->u.cc;
+ xcb_screen_t *xcbScreen = xcbScreenForRootWindow(crtc.window);
+ if (!xcbScreen)
+ // Not for us
+ return;
+
+ qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc;
+ QXcbScreen *screen = findScreenForCrtc(crtc.window, crtc.crtc);
+ // Only update geometry when there's a valid mode on the CRTC
+ // CRTC with node mode could mean that output has been disabled, and we'll
+ // get RRNotifyOutputChange notification for that.
+ if (screen && crtc.mode) {
+ screen->updateGeometry(QRect(crtc.x, crtc.y, crtc.width, crtc.height), crtc.rotation);
+ if (screen->mode() != crtc.mode)
+ screen->updateRefreshRate(crtc.mode);
+ }
+
+ } else if (event->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) {
+ xcb_randr_output_change_t output = event->u.oc;
+ int xcbScreenNumber = 0;
+ xcb_screen_t *xcbScreen = xcbScreenForRootWindow(output.window, &xcbScreenNumber);
+ if (!xcbScreen)
+ // Not for us
+ return;
+
+ QXcbScreen *screen = findScreenForOutput(output.window, output.output);
+ qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_OUTPUT_CHANGE:" << output.output;
+
+ if (screen && output.connection == XCB_RANDR_CONNECTION_DISCONNECTED) {
+ qCDebug(lcQpaScreen) << "screen" << screen->name() << "has been disconnected";
+
+ // Known screen removed -> delete it
+ m_screens.removeOne(screen);
+ foreach (QXcbScreen *otherScreen, m_screens)
+ otherScreen->removeVirtualSibling((QPlatformScreen *) screen);
+
+ QXcbIntegration::instance()->destroyScreen(screen);
+
+ // QTBUG-40174, QTBUG-42985: If all screens are removed, wait
+ // and start rendering again later if a screen becomes available.
+
+ } else if (!screen && output.connection == XCB_RANDR_CONNECTION_CONNECTED) {
+ // New XRandR output is available and it's enabled
+ if (output.crtc != XCB_NONE && output.mode != XCB_NONE) {
+ xcb_randr_get_output_info_cookie_t outputInfoCookie =
+ xcb_randr_get_output_info(xcb_connection(), output.output, output.config_timestamp);
+ QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> outputInfo(
+ xcb_randr_get_output_info_reply(xcb_connection(), outputInfoCookie, NULL));
+
+ screen = createScreen(xcbScreenNumber, xcbScreen, output.output, outputInfo.data());
+ qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled";
+
+ screen->setPrimary(checkOutputIsPrimary(output.window, output.output));
+ foreach (QXcbScreen *otherScreen, m_screens)
+ if (otherScreen->root() == output.window)
+ otherScreen->addVirtualSibling(screen);
+ m_screens << screen;
+ QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary());
+ }
+ // else ignore disabled screens
+ } else if (screen) {
+ // Screen has been disabled -> remove
+ if (output.crtc == XCB_NONE && output.mode == XCB_NONE) {
+ qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled";
+ m_screens.removeOne(screen);
+ foreach (QXcbScreen *otherScreen, m_screens)
+ otherScreen->removeVirtualSibling((QPlatformScreen *) screen);
+ QXcbIntegration::instance()->destroyScreen(screen);
+ } else {
+ // Just update existing screen
+ screen->updateGeometry(output.config_timestamp);
+ const bool wasPrimary = screen->isPrimary();
+ screen->setPrimary(checkOutputIsPrimary(output.window, output.output));
+ if (screen->mode() != output.mode)
+ screen->updateRefreshRate(output.mode);
+
+ // If the screen became primary, reshuffle the order in QGuiApplicationPrivate
+ // TODO: add a proper mechanism for updating primary screen
+ if (!wasPrimary && screen->isPrimary()) {
+ QScreen *realScreen = static_cast<QPlatformScreen*>(screen)->screen();
+ QGuiApplicationPrivate::screen_list.removeOne(realScreen);
+ QGuiApplicationPrivate::screen_list.prepend(realScreen);
+ m_screens.removeOne(screen);
+ m_screens.prepend(screen);
+ }
+ qCDebug(lcQpaScreen) << "output has changed" << screen;
+ }
+ }
+ if (!m_screens.isEmpty())
+ qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name();
+ else
+ qCDebug(lcQpaScreen) << "no outputs";
+ }
+}
+
+void QXcbConnection::initializeScreens()
{
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;
+ QXcbScreen* primaryScreen = Q_NULLPTR;
+ xcb_screen_t *xcbScreen = Q_NULLPTR;
+ bool hasOutputs = false;
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;
+ xcbScreen = it.data;
QList<QPlatformScreen *> 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 =
- xcb_randr_get_output_primary(xcb_connection(), xcbScreen->root);
- xcb_randr_get_screen_resources_cookie_t resourcesCookie =
- xcb_randr_get_screen_resources(xcb_connection(), xcbScreen->root);
- xcb_randr_get_output_primary_reply_t *primary =
- xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error);
- if (!primary || error) {
- qWarning("QXcbConnection: Failed to get the primary output of the screen");
+ // RRGetScreenResourcesCurrent is fast but it may return nothing if the
+ // configuration is not initialized wrt to the hardware. We should call
+ // RRGetScreenResources in this case.
+ QScopedPointer<xcb_randr_get_screen_resources_reply_t, QScopedPointerPodDeleter> resources;
+ xcb_randr_get_screen_resources_current_cookie_t resourcesCookie =
+ xcb_randr_get_screen_resources_current(xcb_connection(), xcbScreen->root);
+ QScopedPointer<xcb_randr_get_screen_resources_current_reply_t, QScopedPointerPodDeleter> resources_current(
+ xcb_randr_get_screen_resources_current_reply(xcb_connection(), resourcesCookie, &error));
+ if (!resources_current || error) {
+ qWarning("failed to get the current screen resources");
free(error);
} else {
- xcb_randr_get_screen_resources_reply_t *resources =
- xcb_randr_get_screen_resources_reply(xcb_connection(), resourcesCookie, &error);
- if (!resources || error) {
- qWarning("QXcbConnection: Failed to get the screen resources");
- free(error);
+ xcb_timestamp_t timestamp;
+ xcb_randr_output_t *outputs = Q_NULLPTR;
+ outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.data());
+ if (outputCount) {
+ timestamp = resources_current->config_timestamp;
+ outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.data());
} else {
- xcb_timestamp_t timestamp = resources->config_timestamp;
- outputCount = xcb_randr_get_screen_resources_outputs_length(resources);
- xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(resources);
-
- 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;
+ xcb_randr_get_screen_resources_cookie_t resourcesCookie =
+ xcb_randr_get_screen_resources(xcb_connection(), xcbScreen->root);
+ resources.reset(xcb_randr_get_screen_resources_reply(xcb_connection(), resourcesCookie, &error));
+ if (!resources || error) {
+ qWarning("failed to get the screen resources");
+ free(error);
+ } else {
+ timestamp = resources->config_timestamp;
+ outputCount = xcb_randr_get_screen_resources_outputs_length(resources.data());
+ outputs = xcb_randr_get_screen_resources_outputs(resources.data());
+ }
+ }
-#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 (outputCount) {
+ xcb_randr_get_output_primary_cookie_t primaryCookie =
+ xcb_randr_get_output_primary(xcb_connection(), xcbScreen->root);
+ QScopedPointer<xcb_randr_get_output_primary_reply_t, QScopedPointerPodDeleter> primary(
+ xcb_randr_get_output_primary_reply(xcb_connection(), primaryCookie, &error));
+ if (!primary || error) {
+ qWarning("failed to get the primary output of the screen");
+ free(error);
+ } else {
+ for (int i = 0; i < outputCount; i++) {
+ QScopedPointer<xcb_randr_get_output_info_reply_t, QScopedPointerPodDeleter> output(
+ xcb_randr_get_output_info_reply(xcb_connection(),
+ xcb_randr_get_output_info_unchecked(xcb_connection(), outputs[i], timestamp), NULL));
+
+ // Invalid, disconnected or disabled output
+ if (output == NULL)
+ continue;
+
+ if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
+ qCDebug(lcQpaScreen, "Output %s is not connected", qPrintable(
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.data()),
+ xcb_randr_get_output_info_name_length(output.data()))));
+ continue;
+ }
- if (output->crtc == XCB_NONE) {
-#ifdef Q_XCB_DEBUG
- qDebug("Screen output %s is not connected", qPrintable(outputName));
-#endif
- continue;
- }
+ if (output->crtc == XCB_NONE) {
+ qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable(
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.data()),
+ xcb_randr_get_output_info_name_length(output.data()))));
+ continue;
+ }
- QXcbScreen *screen = findOrCreateScreen(newScreens, xcbScreenNumber, xcbScreen, output);
- siblings << screen;
- activeScreens << screen;
- ++screenNumber;
- // 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
- // or currently disconnected output.
- if (m_primaryScreenNumber == xcbScreenNumber) {
- if (!primaryScreen || (primary && 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
+ QXcbScreen *screen = createScreen(xcbScreenNumber, xcbScreen, outputs[i], output.data());
+ siblings << screen;
+ ++connectedOutputCount;
+ hasOutputs = true;
+ m_screens << screen;
+
+ // 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
+ // or currently disconnected output.
+ if (m_primaryScreenNumber == xcbScreenNumber) {
+ if (!primaryScreen || (primary && outputs[i] == primary->output)) {
+ if (primaryScreen)
+ primaryScreen->setPrimary(false);
+ primaryScreen = screen;
+ primaryScreen->setPrimary(true);
+ siblings.prepend(siblings.takeLast());
+ }
}
}
- free(output);
}
}
- free(resources);
}
- free(primary);
- }
- // 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) {
-#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;
}
foreach (QPlatformScreen* s, siblings)
((QXcbScreen*)s)->setVirtualSiblings(siblings);
@@ -279,28 +400,38 @@ void QXcbConnection::updateScreens()
++xcbScreenNumber;
} // for each xcb screen
- // 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);
+ // If there's no randr extension, or there was some error above, or we found a
+ // screen which doesn't have outputs for some other reason (e.g. on VNC or ssh -X),
+ // but the dimensions are known anyway, and we don't already have any lingering
+ // (possibly disconnected) screens, then showing windows should be possible,
+ // so create one screen. (QTBUG-31389)
+ if (xcbScreen && !hasOutputs && xcbScreen->width_in_pixels > 0 && xcbScreen->height_in_pixels > 0 && m_screens.isEmpty()) {
+ QXcbScreen *screen = createScreen(0, xcbScreen, 0, Q_NULLPTR);
+ screen->setVirtualSiblings(QList<QPlatformScreen *>() << screen);
+ m_screens << screen;
+ primaryScreen = screen;
+ primaryScreen->setPrimary(true);
+ qCDebug(lcQpaScreen) << "found a screen with zero outputs" << screen;
+ }
+
+ // Ensure the primary screen is first in the list
+ if (primaryScreen) {
+ Q_ASSERT(!m_screens.isEmpty());
+ if (m_screens.first() != primaryScreen) {
+ m_screens.removeOne(primaryScreen);
+ m_screens.prepend(primaryScreen);
}
}
- // 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);
+ // Push the screens to QApplication
+ QXcbIntegration *integration = QXcbIntegration::instance();
+ foreach (QXcbScreen* screen, m_screens) {
+ qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")";
+ integration->screenAdded(screen, screen->isPrimary());
}
- // 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);
+ if (!m_screens.isEmpty())
+ qCDebug(lcQpaScreen) << "primary output is" << m_screens.first()->name();
}
QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, const char *displayName)
@@ -309,27 +440,23 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
, m_primaryScreenNumber(0)
, m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY"))
, m_nativeInterface(nativeInterface)
+#ifdef XCB_USE_XLIB
+ , m_xlib_display(0)
+#endif
, xfixes_first_event(0)
, xrandr_first_event(0)
, xkb_first_event(0)
- , glx_first_event(0)
- , has_glx_extension(false)
, has_shape_extension(false)
, has_randr_extension(false)
, has_input_shape(false)
- , has_touch_without_mouse_emulation(false)
, has_xkb(false)
, m_buttons(0)
, m_focusWindow(0)
, m_systemTrayTracker(0)
+ , m_glIntegration(Q_NULLPTR)
{
-#ifdef XCB_USE_EGL
- EGLNativeDisplayType dpy = EGL_DEFAULT_DISPLAY;
-#elif defined(XCB_USE_XLIB)
- Display *dpy;
-#endif
#ifdef XCB_USE_XLIB
- dpy = XOpenDisplay(m_displayName.constData());
+ Display *dpy = XOpenDisplay(m_displayName.constData());
if (dpy) {
m_primaryScreenNumber = DefaultScreen(dpy);
m_connection = XGetXCBConnection(dpy);
@@ -345,12 +472,6 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
if (!m_connection || xcb_connection_has_error(m_connection))
qFatal("QXcbConnection: Could not connect to display %s", m_displayName.constData());
-#ifdef XCB_USE_EGL
- EGLDisplay eglDisplay = eglGetDisplay(dpy);
- m_egl_display = eglDisplay;
- EGLint major, minor;
- m_has_egl = eglInitialize(eglDisplay, &major, &minor);
-#endif //XCB_USE_EGL
m_reader = new QXcbEventReader(this);
m_reader->start();
@@ -363,9 +484,6 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
#ifdef XCB_USE_RENDER
&xcb_render_id,
#endif
-#ifdef XCB_HAS_XCB_GLX
- &xcb_glx_id,
-#endif
0
};
@@ -380,9 +498,11 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
m_netWmUserTime = XCB_CURRENT_TIME;
initializeXRandr();
- updateScreens();
+ initializeScreens();
+
+ if (m_screens.isEmpty())
+ qFatal("QXcbConnection: no screens available");
- initializeGLX();
initializeXFixes();
initializeXRender();
m_xi2Enabled = false;
@@ -405,6 +525,27 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
if (!m_startupId.isNull())
qunsetenv("DESKTOP_STARTUP_ID");
+
+ QStringList glIntegrationNames;
+ glIntegrationNames << QStringLiteral("xcb_glx") << QStringLiteral("xcb_egl");
+ QString glIntegrationName = QString::fromLocal8Bit(qgetenv("QT_XCB_GL_INTEGRATION"));
+ if (glIntegrationName.size()) {
+ glIntegrationNames.removeAll(glIntegrationName);
+ glIntegrationNames.prepend(glIntegrationName);
+ }
+
+ qCDebug(QT_XCB_GLINTEGRATION) << "Choosing xcb gl-integration based on following priority\n" << glIntegrationNames;
+ for (int i = 0; i < glIntegrationNames.size() && !m_glIntegration; i++) {
+ m_glIntegration = QXcbGlIntegrationFactory::create(glIntegrationNames.at(i));
+ if (m_glIntegration && !m_glIntegration->initialize(this)) {
+ qCDebug(QT_XCB_GLINTEGRATION) << "Failed to initialize xcb gl-integration" << glIntegrationNames.at(i);
+ delete m_glIntegration;
+ m_glIntegration = Q_NULLPTR;
+ }
+ }
+ if (!m_glIntegration)
+ qCDebug(QT_XCB_GLINTEGRATION) << "Failed to create xcb gl-integration";
+
sync();
if (qEnvironmentVariableIsEmpty("QT_IM_MODULE"))
@@ -431,14 +572,12 @@ QXcbConnection::~QXcbConnection()
delete m_reader;
+ QXcbIntegration *integration = QXcbIntegration::instance();
// Delete screens in reverse order to avoid crash in case of multiple screens
while (!m_screens.isEmpty())
- delete m_screens.takeLast();
+ integration->destroyScreen(m_screens.takeLast());
-#ifdef XCB_USE_EGL
- if (m_has_egl)
- eglTerminate(m_egl_display);
-#endif //XCB_USE_EGL
+ delete m_glIntegration;
#ifdef XCB_USE_XLIB
XCloseDisplay((Display *)m_xlib_display);
@@ -823,8 +962,10 @@ void QXcbConnection::handleMotionNotify(xcb_generic_event_t *ev)
xcb_motion_notify_event_t *event = (xcb_motion_notify_event_t *)ev;
m_buttons = (m_buttons & ~0x7) | translateMouseButtons(event->state);
- if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled()))
- qDebug("xcb: moved mouse to %4d, %4d; button state %X", event->event_x, event->event_y, static_cast<unsigned int>(m_buttons));
+#ifdef Q_XCB_DEBUG
+ qCDebug(lcQpaXInput, "xcb: moved mouse to %4d, %4d; button state %X",
+ event->event_x, event->event_y, static_cast<unsigned int>(m_buttons));
+#endif
}
#ifndef QT_NO_XKB
@@ -957,14 +1098,14 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
m_clipboard->handleXFixesSelectionRequest((xcb_xfixes_selection_notify_event_t *)event);
#endif
handled = true;
+ } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_NOTIFY) {
+ updateScreens((xcb_randr_notify_event_t *)event);
+ 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 ) {
+ if (s->root() == change_event->root )
s->handleScreenChange(change_event);
- s->updateRefreshRate();
- }
}
handled = true;
#ifndef QT_NO_XKB
@@ -996,51 +1137,8 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
}
}
-#ifdef XCB_USE_XLIB
- if (!handled) {
- // Check if a custom XEvent constructor was registered in xlib for this event type, and call it discarding the constructed XEvent if any.
- // XESetWireToEvent might be used by libraries to intercept messages from the X server e.g. the OpenGL lib waiting for DRI2 events.
- Display *xdisplay = (Display *)m_xlib_display;
- XLockDisplay(xdisplay);
- bool locked = true;
- Bool (*proc)(Display*, XEvent*, xEvent*) = XESetWireToEvent(xdisplay, response_type, 0);
- if (proc) {
- XESetWireToEvent(xdisplay, response_type, proc);
- XEvent dummy;
- event->sequence = LastKnownRequestProcessed(m_xlib_display);
- if (proc(xdisplay, &dummy, (xEvent*)event)) {
-#if defined(XCB_USE_GLX) && defined(XCB_HAS_XCB_GLX)
- // DRI2 clients don't receive GLXBufferSwapComplete events on the wire.
- // Instead the GLX event is synthesized from the DRI2BufferSwapComplete event
- // by DRI2WireToEvent(). For an application to be able to see the event
- // we have to convert it to an xcb_glx_buffer_swap_complete_event_t and
- // pass it to the native event filter.
- const uint swap_complete = glx_first_event + XCB_GLX_BUFFER_SWAP_COMPLETE;
- if (dispatcher && has_glx_extension && uint(dummy.type) == swap_complete && response_type != swap_complete) {
- QGLXBufferSwapComplete *xev = reinterpret_cast<QGLXBufferSwapComplete *>(&dummy);
- xcb_glx_buffer_swap_complete_event_t ev;
- memset(&ev, 0, sizeof(xcb_glx_buffer_swap_complete_event_t));
- ev.response_type = xev->type;
- ev.sequence = xev->serial;
- ev.event_type = xev->event_type;
- ev.drawable = xev->drawable;
- ev.ust_hi = xev->ust >> 32;
- ev.ust_lo = xev->ust & 0xffffffff;
- ev.msc_hi = xev->msc >> 32;
- ev.msc_lo = xev->msc & 0xffffffff;
- ev.sbc = xev->sbc & 0xffffffff;
- // Unlock the display before calling the native event filter
- XUnlockDisplay(xdisplay);
- locked = false;
- handled = dispatcher->filterNativeEvent(m_nativeInterface->genericEventFilterType(), &ev, &result);
- }
-#endif
- }
- }
- if (locked)
- XUnlockDisplay(xdisplay);
- }
-#endif
+ if (!handled && m_glIntegration)
+ handled = m_glIntegration->handleXcbEvent(event, response_type);
if (handled)
printXcbEvent("Handled XCB event", event);
@@ -1235,9 +1333,17 @@ xcb_timestamp_t QXcbConnection::getTimestamp()
xcb_window_t QXcbConnection::rootWindow()
{
- return primaryScreen()->root();
+ QXcbScreen *s = primaryScreen();
+ return s ? s->root() : 0;
}
+#ifdef XCB_USE_XLIB
+void *QXcbConnection::xlib_display() const
+{
+ return m_xlib_display;
+}
+#endif
+
void QXcbConnection::processXcbEvents()
{
int connection_error = xcb_connection_has_error(xcb_connection());
@@ -1680,34 +1786,6 @@ void QXcbConnection::initializeXRender()
#endif
}
-void QXcbConnection::initializeGLX()
-{
-#ifdef XCB_HAS_XCB_GLX
- const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_glx_id);
- if (!reply || !reply->present)
- return;
-
- has_glx_extension = true;
- glx_first_event = reply->first_event;
-
- xcb_generic_error_t *error = 0;
- xcb_glx_query_version_cookie_t xglx_query_cookie = xcb_glx_query_version(m_connection,
- XCB_GLX_MAJOR_VERSION,
- XCB_GLX_MINOR_VERSION);
- xcb_glx_query_version_reply_t *xglx_query = xcb_glx_query_version_reply(m_connection,
- xglx_query_cookie, &error);
- if (!xglx_query || error) {
- qWarning("QXcbConnection: Failed to initialize GLX");
- free(error);
- has_glx_extension = false;
- }
- free(xglx_query);
-#else
- // no way to check, assume GLX is present
- has_glx_extension = true;
-#endif
-}
-
void QXcbConnection::initializeXRandr()
{
const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_randr_id);
@@ -1731,6 +1809,17 @@ void QXcbConnection::initializeXRandr()
has_randr_extension = false;
}
free(xrandr_query);
+
+ xcb_screen_iterator_t rootIter = xcb_setup_roots_iterator(m_setup);
+ for (; rootIter.rem; xcb_screen_next(&rootIter)) {
+ xcb_randr_select_input(xcb_connection(),
+ rootIter.data->root,
+ XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
+ XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
+ XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
+ XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY
+ );
+ }
}
void QXcbConnection::initializeXShape()
@@ -1817,13 +1906,6 @@ void QXcbConnection::initializeXKB()
#endif
}
-#if defined(XCB_USE_EGL)
-bool QXcbConnection::hasEgl() const
-{
- return m_has_egl;
-}
-#endif // defined(XCB_USE_EGL)
-
#if defined(XCB_USE_XINPUT2)
static int xi2ValuatorOffset(unsigned char *maskPtr, int maskLen, int number)
{