diff options
author | Gatis Paeglis <gatis.paeglis@qt.io> | 2018-09-22 13:14:23 +0200 |
---|---|---|
committer | Gatis Paeglis <gatis.paeglis@qt.io> | 2018-10-17 10:02:45 +0000 |
commit | d67214302f269242ae3d8d2b962fd91ec42c979e (patch) | |
tree | 797359110b434799337ae7f76c97c89b7b6e8e1d /src/plugins/platforms/xcb/qxcbconnection_basic.cpp | |
parent | 5fe2ed48d60f383706274fa087f08a041c79dc73 (diff) |
xcb: qxcbconnection_basic
A basic base class that creates a connection and initializes
extensions. The goal was to reduce the size of qxcbconnection.h/cpp.
Made QXcbAtom into a class that handles atom initialization and
exposes the relevant APIs. Before this patch, all of that logic
was inside of qxcbconnection.h/cpp.
Change-Id: Ia893c3b31e2343dfbe62fe2aa6bfd0017abf46ea
Reviewed-by: Gatis Paeglis <gatis.paeglis@qt.io>
Diffstat (limited to 'src/plugins/platforms/xcb/qxcbconnection_basic.cpp')
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection_basic.cpp | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp new file mode 100644 index 0000000000..7bed3c8937 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp @@ -0,0 +1,437 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** 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 The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qxcbconnection_basic.h" +#include "qxcbbackingstore.h" // for createSystemVShmSegment() + +#include <xcb/randr.h> +#include <xcb/shm.h> +#include <xcb/sync.h> +#include <xcb/xfixes.h> +#include <xcb/xinerama.h> +#if QT_CONFIG(xcb_xinput) +#include <xcb/xinput.h> +#endif +#if QT_CONFIG(xcb_render) +#include <xcb/render.h> +#endif +#if QT_CONFIG(xkb) +#define explicit dont_use_cxx_explicit +#include <xcb/xkb.h> +#undef explicit +#endif + +#if QT_CONFIG(xcb_xlib) +#define register /* C++17 deprecated register */ +#include <X11/Xlib.h> +#include <X11/Xlib-xcb.h> +#include <X11/Xlibint.h> +#include <X11/Xutil.h> +#undef register +#endif + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcQpaXcb, "qt.qpa.xcb") + +#if QT_CONFIG(xcb_xlib) +static const char * const xcbConnectionErrors[] = { + "No error", /* Error 0 */ + "I/O error", /* XCB_CONN_ERROR */ + "Unsupported extension used", /* XCB_CONN_CLOSED_EXT_NOTSUPPORTED */ + "Out of memory", /* XCB_CONN_CLOSED_MEM_INSUFFICIENT */ + "Maximum allowed requested length exceeded", /* XCB_CONN_CLOSED_REQ_LEN_EXCEED */ + "Failed to parse display string", /* XCB_CONN_CLOSED_PARSE_ERR */ + "No such screen on display", /* XCB_CONN_CLOSED_INVALID_SCREEN */ + "Error during FD passing" /* XCB_CONN_CLOSED_FDPASSING_FAILED */ +}; + +static int nullErrorHandler(Display *dpy, XErrorEvent *err) +{ +#ifndef Q_XCB_DEBUG + Q_UNUSED(dpy); + Q_UNUSED(err); +#else + const int buflen = 1024; + char buf[buflen]; + + XGetErrorText(dpy, err->error_code, buf, buflen); + fprintf(stderr, "X Error: serial %lu error %d %s\n", err->serial, (int) err->error_code, buf); +#endif + return 0; +} + +static int ioErrorHandler(Display *dpy) +{ + xcb_connection_t *conn = XGetXCBConnection(dpy); + if (conn != NULL) { + /* Print a message with a textual description of the error */ + int code = xcb_connection_has_error(conn); + const char *str = "Unknown error"; + int arrayLength = sizeof(xcbConnectionErrors) / sizeof(xcbConnectionErrors[0]); + if (code >= 0 && code < arrayLength) + str = xcbConnectionErrors[code]; + + qWarning("The X11 connection broke: %s (code %d)", str, code); + } + return _XDefaultIOError(dpy); +} +#endif + +QXcbBasicConnection::QXcbBasicConnection(const char *displayName) + : m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY")) +{ +#if QT_CONFIG(xcb_xlib) + Display *dpy = XOpenDisplay(m_displayName.constData()); + if (dpy) { + m_primaryScreenNumber = DefaultScreen(dpy); + m_xcbConnection = XGetXCBConnection(dpy); + XSetEventQueueOwner(dpy, XCBOwnsEventQueue); + XSetErrorHandler(nullErrorHandler); + XSetIOErrorHandler(ioErrorHandler); + m_xlibDisplay = dpy; + } +#else + m_xcbConnection = xcb_connect(m_displayName.constData(), &m_primaryScreenNumber); +#endif + if (Q_UNLIKELY(!isConnected())) { + qCWarning(lcQpaXcb, "could not connect to display %s", m_displayName.constData()); + return; + } + + m_setup = xcb_get_setup(m_xcbConnection); + m_xcbAtom.initialize(m_xcbConnection); + + xcb_extension_t *extensions[] = { + &xcb_shm_id, &xcb_xfixes_id, &xcb_randr_id, &xcb_shape_id, &xcb_sync_id, +#if QT_CONFIG(xkb) + &xcb_xkb_id, +#endif +#if QT_CONFIG(xcb_render) + &xcb_render_id, +#endif +#if QT_CONFIG(xcb_xinput) + &xcb_input_id, +#endif + 0 + }; + + for (xcb_extension_t **ext_it = extensions; *ext_it; ++ext_it) + xcb_prefetch_extension_data (m_xcbConnection, *ext_it); + + initializeXSync(); + if (!qEnvironmentVariableIsSet("QT_XCB_NO_MITSHM")) + initializeShm(); + if (!qEnvironmentVariableIsSet("QT_XCB_NO_XRANDR")) + initializeXRandr(); + if (!m_hasXRandr) + initializeXinerama(); + initializeXFixes(); + initializeXRender(); +#if QT_CONFIG(xcb_xinput) + if (!qEnvironmentVariableIsSet("QT_XCB_NO_XI2")) + initializeXInput2(); +#endif + initializeXShape(); + initializeXKB(); +} + +QXcbBasicConnection::~QXcbBasicConnection() +{ + if (isConnected()) { +#if QT_CONFIG(xcb_xlib) + XCloseDisplay(static_cast<Display *>(m_xlibDisplay)); +#else + xcb_disconnect(m_xcbConnection); +#endif + } +} + +xcb_atom_t QXcbBasicConnection::internAtom(const char *name) +{ + if (!name || *name == 0) + return XCB_NONE; + + return Q_XCB_REPLY(xcb_intern_atom, m_xcbConnection, false, strlen(name), name)->atom; +} + +QByteArray QXcbBasicConnection::atomName(xcb_atom_t atom) +{ + if (!atom) + return QByteArray(); + + auto reply = Q_XCB_REPLY(xcb_get_atom_name, m_xcbConnection, atom); + if (reply) + return QByteArray(xcb_get_atom_name_name(reply.get()), xcb_get_atom_name_name_length(reply.get())); + + qCWarning(lcQpaXcb) << "atomName: bad atom" << atom; + return QByteArray(); +} + +#if QT_CONFIG(xcb_xinput) +// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed: +// - "pad0" became "extension" +// - "pad1" and "pad" became "pad0" +// New and old version of this struct share the following fields: +typedef struct qt_xcb_ge_event_t { + uint8_t response_type; + uint8_t extension; + uint16_t sequence; + uint32_t length; + uint16_t event_type; +} qt_xcb_ge_event_t; + +bool QXcbBasicConnection::isXIEvent(xcb_generic_event_t *event) const +{ + qt_xcb_ge_event_t *e = reinterpret_cast<qt_xcb_ge_event_t *>(event); + return e->extension == m_xiOpCode; +} + +bool QXcbBasicConnection::isXIType(xcb_generic_event_t *event, uint16_t type) const +{ + if (!isXIEvent(event)) + return false; + + auto *e = reinterpret_cast<qt_xcb_ge_event_t *>(event); + return e->event_type == type; +} +#endif // QT_CONFIG(xcb_xinput) + +bool QXcbBasicConnection::isXFixesType(uint responseType, int eventType) const +{ + return m_hasXFixes && responseType == m_xfixesFirstEvent + eventType; +} + +bool QXcbBasicConnection::isXRandrType(uint responseType, int eventType) const +{ + return m_hasXRandr && responseType == m_xrandrFirstEvent + eventType; +} + +bool QXcbBasicConnection::isXkbType(uint responseType) const +{ + return m_hasXkb && responseType == m_xkbFirstEvent; +} + +void QXcbBasicConnection::initializeXSync() +{ + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_sync_id); + if (!reply || !reply->present) + return; + + m_hasXSync = true; +} + +void QXcbBasicConnection::initializeShm() +{ + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_shm_id); + if (!reply || !reply->present) { + qCDebug(lcQpaXcb, "MIT-SHM extension is not present on the X server"); + return; + } + + auto shmQuery = Q_XCB_REPLY(xcb_shm_query_version, m_xcbConnection); + if (!shmQuery) { + qCWarning(lcQpaXcb, "failed to request MIT-SHM version"); + return; + } + + m_hasShm = true; + m_hasShmFd = (shmQuery->major_version == 1 && shmQuery->minor_version >= 2) || + shmQuery->major_version > 1; + + qCDebug(lcQpaXcb) << "Has MIT-SHM :" << m_hasShm; + qCDebug(lcQpaXcb) << "Has MIT-SHM FD :" << m_hasShmFd; + + // Temporary disable warnings (unless running in debug mode). + auto logging = const_cast<QLoggingCategory*>(&lcQpaXcb()); + bool wasEnabled = logging->isEnabled(QtMsgType::QtWarningMsg); + if (!logging->isEnabled(QtMsgType::QtDebugMsg)) + logging->setEnabled(QtMsgType::QtWarningMsg, false); + if (!QXcbBackingStore::createSystemVShmSegment(m_xcbConnection)) { + qCDebug(lcQpaXcb, "failed to create System V shared memory segment (remote " + "X11 connection?), disabling SHM"); + m_hasShm = m_hasShmFd = false; + } + if (wasEnabled) + logging->setEnabled(QtMsgType::QtWarningMsg, true); +} + +void QXcbBasicConnection::initializeXRandr() +{ +#if QT_CONFIG(xcb_render) + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_render_id); + if (!reply || !reply->present) { + qCDebug(lcQpaXcb, "XRender extension not present on the X server"); + return; + } + + auto xrenderQuery = Q_XCB_REPLY(xcb_render_query_version, m_xcbConnection, + XCB_RENDER_MAJOR_VERSION, + XCB_RENDER_MINOR_VERSION); + if (!xrenderQuery) { + qCWarning(lcQpaXcb, "xcb_render_query_version failed"); + return; + } + + m_hasXRender = true; + m_xrenderVersion.first = xrenderQuery->major_version; + m_xrenderVersion.second = xrenderQuery->minor_version; +#endif +} + +void QXcbBasicConnection::initializeXinerama() +{ + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_xinerama_id); + if (!reply || !reply->present) + return; + + auto xineramaActive = Q_XCB_REPLY(xcb_xinerama_is_active, m_xcbConnection); + if (xineramaActive && xineramaActive->state) + m_hasXinerama = true; +} + +void QXcbBasicConnection::initializeXFixes() +{ + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_xfixes_id); + if (!reply || !reply->present) + return; + + auto xfixesQuery = Q_XCB_REPLY(xcb_xfixes_query_version, m_xcbConnection, + XCB_XFIXES_MAJOR_VERSION, + XCB_XFIXES_MINOR_VERSION); + if (!xfixesQuery || xfixesQuery->major_version < 2) { + qCWarning(lcQpaXcb, "failed to initialize XFixes"); + return; + } + + m_hasXFixes = true; + m_xfixesFirstEvent = reply->first_event; +} + +void QXcbBasicConnection::initializeXRender() +{ + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_randr_id); + if (!reply || !reply->present) + return; + + auto xrandrQuery = Q_XCB_REPLY(xcb_randr_query_version, m_xcbConnection, + XCB_RANDR_MAJOR_VERSION, + XCB_RANDR_MINOR_VERSION); + if (!xrandrQuery || (xrandrQuery->major_version < 1 || + (xrandrQuery->major_version == 1 && xrandrQuery->minor_version < 2))) { + qCWarning(lcQpaXcb, "failed to initialize XRandr"); + return; + } + + m_hasXRandr = true; + m_xrandrFirstEvent = reply->first_event; +} + +#if QT_CONFIG(xcb_xinput) +void QXcbBasicConnection::initializeXInput2() +{ + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_input_id); + if (!reply || !reply->present) { + qCDebug(lcQpaXcb, "XInput extension is not present on the X server"); + return; + } + + auto xinputQuery = Q_XCB_REPLY(xcb_input_xi_query_version, m_xcbConnection, 2, 2); + if (!xinputQuery || xinputQuery->major_version != 2) { + qCWarning(lcQpaXcb, "X server does not support XInput 2"); + return; + } + + qCDebug(lcQpaXcb, "Using XInput version %d.%d", + xinputQuery->major_version, xinputQuery->minor_version); + + m_xi2Enabled = true; + m_xiOpCode = reply->major_opcode; + m_xinputFirstEvent = reply->first_event; + m_xi2Minor = xinputQuery->minor_version; +} +#endif + +void QXcbBasicConnection::initializeXShape() +{ + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_shape_id); + if (!reply || !reply->present) + return; + + m_hasXhape = true; + + auto shapeQuery = Q_XCB_REPLY(xcb_shape_query_version, m_xcbConnection); + if (!shapeQuery) { + qCWarning(lcQpaXcb, "failed to initialize XShape extension"); + return; + } + + if (shapeQuery->major_version > 1 || (shapeQuery->major_version == 1 && shapeQuery->minor_version >= 1)) { + // The input shape is the only thing added in SHAPE 1.1 + m_hasInputShape = true; + } +} + +void QXcbBasicConnection::initializeXKB() +{ +#if QT_CONFIG(xkb) + const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_xkb_id); + if (!reply || !reply->present) { + qCWarning(lcQpaXcb, "XKeyboard extension not present on the X server"); + return; + } + + int wantMajor = 1; + int wantMinor = 0; + auto xkbQuery = Q_XCB_REPLY(xcb_xkb_use_extension, m_xcbConnection, wantMajor, wantMinor); + if (!xkbQuery) { + qCWarning(lcQpaXcb, "failed to initialize XKeyboard extension"); + return; + } + if (!xkbQuery->supported) { + qCWarning(lcQpaXcb, "unsupported XKB version (we want %d.%d, but X server has %d.%d)", + wantMajor, wantMinor, xkbQuery->serverMajor, xkbQuery->serverMinor); + return; + } + + m_hasXkb = true; + m_xkbFirstEvent = reply->first_event; +#endif +} + +QT_END_NAMESPACE |