diff options
-rw-r--r-- | src/plugins/platforms/xcb/qxcbatom.cpp | 287 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbatom.h | 267 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbbackingstore.cpp | 18 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbbackingstore.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.cpp | 711 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection.h | 329 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection_basic.cpp | 437 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection_basic.h | 175 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbconnection_xi2.cpp | 61 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbeventqueue.cpp | 2 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbkeyboard.cpp | 39 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/qxcbkeyboard.h | 7 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/xcb-plugin.pro | 1 | ||||
-rw-r--r-- | src/plugins/platforms/xcb/xcb_qpa_lib.pro | 8 |
14 files changed, 1317 insertions, 1027 deletions
diff --git a/src/plugins/platforms/xcb/qxcbatom.cpp b/src/plugins/platforms/xcb/qxcbatom.cpp new file mode 100644 index 0000000000..9255a57a4f --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbatom.cpp @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** 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 "qxcbatom.h" + +#include <QtCore/qglobal.h> + +#include <string.h> + +#include <algorithm> + +static const char *xcb_atomnames = { + // window-manager <-> client protocols + "WM_PROTOCOLS\0" + "WM_DELETE_WINDOW\0" + "WM_TAKE_FOCUS\0" + "_NET_WM_PING\0" + "_NET_WM_CONTEXT_HELP\0" + "_NET_WM_SYNC_REQUEST\0" + "_NET_WM_SYNC_REQUEST_COUNTER\0" + "MANAGER\0" + "_NET_SYSTEM_TRAY_OPCODE\0" + + // ICCCM window state + "WM_STATE\0" + "WM_CHANGE_STATE\0" + "WM_CLASS\0" + "WM_NAME\0" + + // Session management + "WM_CLIENT_LEADER\0" + "WM_WINDOW_ROLE\0" + "SM_CLIENT_ID\0" + "WM_CLIENT_MACHINE\0" + + // Clipboard + "CLIPBOARD\0" + "INCR\0" + "TARGETS\0" + "MULTIPLE\0" + "TIMESTAMP\0" + "SAVE_TARGETS\0" + "CLIP_TEMPORARY\0" + "_QT_SELECTION\0" + "_QT_CLIPBOARD_SENTINEL\0" + "_QT_SELECTION_SENTINEL\0" + "CLIPBOARD_MANAGER\0" + + "RESOURCE_MANAGER\0" + + "_XSETROOT_ID\0" + + "_QT_SCROLL_DONE\0" + "_QT_INPUT_ENCODING\0" + + "_QT_CLOSE_CONNECTION\0" + + "_MOTIF_WM_HINTS\0" + + "DTWM_IS_RUNNING\0" + "ENLIGHTENMENT_DESKTOP\0" + "_DT_SAVE_MODE\0" + "_SGI_DESKS_MANAGER\0" + + // EWMH (aka NETWM) + "_NET_SUPPORTED\0" + "_NET_VIRTUAL_ROOTS\0" + "_NET_WORKAREA\0" + + "_NET_MOVERESIZE_WINDOW\0" + "_NET_WM_MOVERESIZE\0" + + "_NET_WM_NAME\0" + "_NET_WM_ICON_NAME\0" + "_NET_WM_ICON\0" + + "_NET_WM_PID\0" + + "_NET_WM_WINDOW_OPACITY\0" + + "_NET_WM_STATE\0" + "_NET_WM_STATE_ABOVE\0" + "_NET_WM_STATE_BELOW\0" + "_NET_WM_STATE_FULLSCREEN\0" + "_NET_WM_STATE_MAXIMIZED_HORZ\0" + "_NET_WM_STATE_MAXIMIZED_VERT\0" + "_NET_WM_STATE_MODAL\0" + "_NET_WM_STATE_STAYS_ON_TOP\0" + "_NET_WM_STATE_DEMANDS_ATTENTION\0" + + "_NET_WM_USER_TIME\0" + "_NET_WM_USER_TIME_WINDOW\0" + "_NET_WM_FULL_PLACEMENT\0" + + "_NET_WM_WINDOW_TYPE\0" + "_NET_WM_WINDOW_TYPE_DESKTOP\0" + "_NET_WM_WINDOW_TYPE_DOCK\0" + "_NET_WM_WINDOW_TYPE_TOOLBAR\0" + "_NET_WM_WINDOW_TYPE_MENU\0" + "_NET_WM_WINDOW_TYPE_UTILITY\0" + "_NET_WM_WINDOW_TYPE_SPLASH\0" + "_NET_WM_WINDOW_TYPE_DIALOG\0" + "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0" + "_NET_WM_WINDOW_TYPE_POPUP_MENU\0" + "_NET_WM_WINDOW_TYPE_TOOLTIP\0" + "_NET_WM_WINDOW_TYPE_NOTIFICATION\0" + "_NET_WM_WINDOW_TYPE_COMBO\0" + "_NET_WM_WINDOW_TYPE_DND\0" + "_NET_WM_WINDOW_TYPE_NORMAL\0" + "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0" + + "_KDE_NET_WM_FRAME_STRUT\0" + "_NET_FRAME_EXTENTS\0" + + "_NET_STARTUP_INFO\0" + "_NET_STARTUP_INFO_BEGIN\0" + + "_NET_SUPPORTING_WM_CHECK\0" + + "_NET_WM_CM_S0\0" + + "_NET_SYSTEM_TRAY_VISUAL\0" + + "_NET_ACTIVE_WINDOW\0" + + // Property formats + "TEXT\0" + "UTF8_STRING\0" + "CARDINAL\0" + + // xdnd + "XdndEnter\0" + "XdndPosition\0" + "XdndStatus\0" + "XdndLeave\0" + "XdndDrop\0" + "XdndFinished\0" + "XdndTypeList\0" + "XdndActionList\0" + + "XdndSelection\0" + + "XdndAware\0" + "XdndProxy\0" + + "XdndActionCopy\0" + "XdndActionLink\0" + "XdndActionMove\0" + "XdndActionPrivate\0" + + // Motif DND + "_MOTIF_DRAG_AND_DROP_MESSAGE\0" + "_MOTIF_DRAG_INITIATOR_INFO\0" + "_MOTIF_DRAG_RECEIVER_INFO\0" + "_MOTIF_DRAG_WINDOW\0" + "_MOTIF_DRAG_TARGETS\0" + + "XmTRANSFER_SUCCESS\0" + "XmTRANSFER_FAILURE\0" + + // Xkb + "_XKB_RULES_NAMES\0" + + // XEMBED + "_XEMBED\0" + "_XEMBED_INFO\0" + + // XInput2 + "Button Left\0" + "Button Middle\0" + "Button Right\0" + "Button Wheel Up\0" + "Button Wheel Down\0" + "Button Horiz Wheel Left\0" + "Button Horiz Wheel Right\0" + "Abs MT Position X\0" + "Abs MT Position Y\0" + "Abs MT Touch Major\0" + "Abs MT Touch Minor\0" + "Abs MT Orientation\0" + "Abs MT Pressure\0" + "Abs MT Tracking ID\0" + "Max Contacts\0" + "Rel X\0" + "Rel Y\0" + // XInput2 tablet + "Abs X\0" + "Abs Y\0" + "Abs Pressure\0" + "Abs Tilt X\0" + "Abs Tilt Y\0" + "Abs Wheel\0" + "Abs Distance\0" + "Wacom Serial IDs\0" + "INTEGER\0" + "Rel Horiz Wheel\0" + "Rel Vert Wheel\0" + "Rel Horiz Scroll\0" + "Rel Vert Scroll\0" + "_XSETTINGS_SETTINGS\0" + "_COMPIZ_DECOR_PENDING\0" + "_COMPIZ_DECOR_REQUEST\0" + "_COMPIZ_DECOR_DELETE_PIXMAP\0" + "_COMPIZ_TOOLKIT_ACTION\0" + "_GTK_LOAD_ICONTHEMES\0" + "AT_SPI_BUS\0" + "EDID\0" + "EDID_DATA\0" + "XFree86_DDC_EDID1_RAWDATA\0" + // \0\0 terminates loop. +}; + +QXcbAtom::QXcbAtom() +{ +} + +void QXcbAtom::initialize(xcb_connection_t *connection) +{ + initializeAllAtoms(connection); +} + +void QXcbAtom::initializeAllAtoms(xcb_connection_t *connection) { + const char *names[QXcbAtom::NAtoms]; + const char *ptr = xcb_atomnames; + + int i = 0; + while (*ptr) { + names[i++] = ptr; + while (*ptr) + ++ptr; + ++ptr; + } + + Q_ASSERT(i == QXcbAtom::NAtoms); + + xcb_intern_atom_cookie_t cookies[QXcbAtom::NAtoms]; + + Q_ASSERT(i == QXcbAtom::NAtoms); + for (i = 0; i < QXcbAtom::NAtoms; ++i) + cookies[i] = xcb_intern_atom(connection, false, strlen(names[i]), names[i]); + + for (i = 0; i < QXcbAtom::NAtoms; ++i) { + xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(connection, cookies[i], 0); + m_allAtoms[i] = reply->atom; + free(reply); + } +} + +QXcbAtom::Atom QXcbAtom::qatom(xcb_atom_t xatom) const +{ + return static_cast<QXcbAtom::Atom>(std::find(m_allAtoms, m_allAtoms + QXcbAtom::NAtoms, xatom) - m_allAtoms); +} diff --git a/src/plugins/platforms/xcb/qxcbatom.h b/src/plugins/platforms/xcb/qxcbatom.h new file mode 100644 index 0000000000..b6cc159035 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbatom.h @@ -0,0 +1,267 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#ifndef QXCBATOM_H +#define QXCBATOM_H + +#include <xcb/xcb.h> + +class QXcbAtom +{ +public: + enum Atom { + // window-manager <-> client protocols + WM_PROTOCOLS, + WM_DELETE_WINDOW, + WM_TAKE_FOCUS, + _NET_WM_PING, + _NET_WM_CONTEXT_HELP, + _NET_WM_SYNC_REQUEST, + _NET_WM_SYNC_REQUEST_COUNTER, + MANAGER, // System tray notification + _NET_SYSTEM_TRAY_OPCODE, // System tray operation + + // ICCCM window state + WM_STATE, + WM_CHANGE_STATE, + WM_CLASS, + WM_NAME, + + // Session management + WM_CLIENT_LEADER, + WM_WINDOW_ROLE, + SM_CLIENT_ID, + WM_CLIENT_MACHINE, + + // Clipboard + CLIPBOARD, + INCR, + TARGETS, + MULTIPLE, + TIMESTAMP, + SAVE_TARGETS, + CLIP_TEMPORARY, + _QT_SELECTION, + _QT_CLIPBOARD_SENTINEL, + _QT_SELECTION_SENTINEL, + CLIPBOARD_MANAGER, + + RESOURCE_MANAGER, + + _XSETROOT_ID, + + _QT_SCROLL_DONE, + _QT_INPUT_ENCODING, + + // Qt/XCB specific + _QT_CLOSE_CONNECTION, + + _MOTIF_WM_HINTS, + + DTWM_IS_RUNNING, + ENLIGHTENMENT_DESKTOP, + _DT_SAVE_MODE, + _SGI_DESKS_MANAGER, + + // EWMH (aka NETWM) + _NET_SUPPORTED, + _NET_VIRTUAL_ROOTS, + _NET_WORKAREA, + + _NET_MOVERESIZE_WINDOW, + _NET_WM_MOVERESIZE, + + _NET_WM_NAME, + _NET_WM_ICON_NAME, + _NET_WM_ICON, + + _NET_WM_PID, + + _NET_WM_WINDOW_OPACITY, + + _NET_WM_STATE, + _NET_WM_STATE_ABOVE, + _NET_WM_STATE_BELOW, + _NET_WM_STATE_FULLSCREEN, + _NET_WM_STATE_MAXIMIZED_HORZ, + _NET_WM_STATE_MAXIMIZED_VERT, + _NET_WM_STATE_MODAL, + _NET_WM_STATE_STAYS_ON_TOP, + _NET_WM_STATE_DEMANDS_ATTENTION, + + _NET_WM_USER_TIME, + _NET_WM_USER_TIME_WINDOW, + _NET_WM_FULL_PLACEMENT, + + _NET_WM_WINDOW_TYPE, + _NET_WM_WINDOW_TYPE_DESKTOP, + _NET_WM_WINDOW_TYPE_DOCK, + _NET_WM_WINDOW_TYPE_TOOLBAR, + _NET_WM_WINDOW_TYPE_MENU, + _NET_WM_WINDOW_TYPE_UTILITY, + _NET_WM_WINDOW_TYPE_SPLASH, + _NET_WM_WINDOW_TYPE_DIALOG, + _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + _NET_WM_WINDOW_TYPE_POPUP_MENU, + _NET_WM_WINDOW_TYPE_TOOLTIP, + _NET_WM_WINDOW_TYPE_NOTIFICATION, + _NET_WM_WINDOW_TYPE_COMBO, + _NET_WM_WINDOW_TYPE_DND, + _NET_WM_WINDOW_TYPE_NORMAL, + _KDE_NET_WM_WINDOW_TYPE_OVERRIDE, + + _KDE_NET_WM_FRAME_STRUT, + _NET_FRAME_EXTENTS, + + _NET_STARTUP_INFO, + _NET_STARTUP_INFO_BEGIN, + + _NET_SUPPORTING_WM_CHECK, + + _NET_WM_CM_S0, + + _NET_SYSTEM_TRAY_VISUAL, + + _NET_ACTIVE_WINDOW, + + // Property formats + TEXT, + UTF8_STRING, + CARDINAL, + + // Xdnd + XdndEnter, + XdndPosition, + XdndStatus, + XdndLeave, + XdndDrop, + XdndFinished, + XdndTypelist, + XdndActionList, + + XdndSelection, + + XdndAware, + XdndProxy, + + XdndActionCopy, + XdndActionLink, + XdndActionMove, + XdndActionPrivate, + + // Motif DND + _MOTIF_DRAG_AND_DROP_MESSAGE, + _MOTIF_DRAG_INITIATOR_INFO, + _MOTIF_DRAG_RECEIVER_INFO, + _MOTIF_DRAG_WINDOW, + _MOTIF_DRAG_TARGETS, + + XmTRANSFER_SUCCESS, + XmTRANSFER_FAILURE, + + // Xkb + _XKB_RULES_NAMES, + + // XEMBED + _XEMBED, + _XEMBED_INFO, + + // XInput2 + ButtonLeft, + ButtonMiddle, + ButtonRight, + ButtonWheelUp, + ButtonWheelDown, + ButtonHorizWheelLeft, + ButtonHorizWheelRight, + AbsMTPositionX, + AbsMTPositionY, + AbsMTTouchMajor, + AbsMTTouchMinor, + AbsMTOrientation, + AbsMTPressure, + AbsMTTrackingID, + MaxContacts, + RelX, + RelY, + // XInput2 tablet + AbsX, + AbsY, + AbsPressure, + AbsTiltX, + AbsTiltY, + AbsWheel, + AbsDistance, + WacomSerialIDs, + INTEGER, + RelHorizWheel, + RelVertWheel, + RelHorizScroll, + RelVertScroll, + + _XSETTINGS_SETTINGS, + + _COMPIZ_DECOR_PENDING, + _COMPIZ_DECOR_REQUEST, + _COMPIZ_DECOR_DELETE_PIXMAP, + _COMPIZ_TOOLKIT_ACTION, + _GTK_LOAD_ICONTHEMES, + + AT_SPI_BUS, + + EDID, + EDID_DATA, + XFree86_DDC_EDID1_RAWDATA, + + NAtoms + }; + + QXcbAtom(); + void initialize(xcb_connection_t *connection); + + inline xcb_atom_t atom(QXcbAtom::Atom atom) const { return m_allAtoms[atom]; } + QXcbAtom::Atom qatom(xcb_atom_t atom) const; + +protected: + void initializeAllAtoms(xcb_connection_t *connection); + +private: + xcb_atom_t m_allAtoms[QXcbAtom::NAtoms]; +}; + +#endif // QXCBATOM_H diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp index c29eb29b7e..c8c806749f 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp +++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp @@ -106,7 +106,7 @@ public: void put(xcb_drawable_t dst, const QRegion ®ion, const QPoint &offset); void preparePaint(const QRegion ®ion); - static bool createSystemVShmSegment(QXcbConnection *c, size_t segmentSize = 1, + static bool createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize = 1, xcb_shm_segment_info_t *shm_info = nullptr); private: @@ -406,12 +406,12 @@ void QXcbBackingStoreImage::createShmSegment(size_t segmentSize) } else #endif { - if (createSystemVShmSegment(connection(), segmentSize, &m_shm_info)) + if (createSystemVShmSegment(xcb_connection(), segmentSize, &m_shm_info)) m_segmentSize = segmentSize; } } -bool QXcbBackingStoreImage::createSystemVShmSegment(QXcbConnection *c, size_t segmentSize, +bool QXcbBackingStoreImage::createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize, xcb_shm_segment_info_t *shmInfo) { const int id = shmget(IPC_PRIVATE, segmentSize, IPC_CREAT | 0600); @@ -429,17 +429,17 @@ bool QXcbBackingStoreImage::createSystemVShmSegment(QXcbConnection *c, size_t se if (shmctl(id, IPC_RMID, 0) == -1) qCWarning(lcQpaXcb, "Error while marking the shared memory segment to be destroyed"); - const auto seg = xcb_generate_id(c->xcb_connection()); - auto cookie = xcb_shm_attach_checked(c->xcb_connection(), seg, id, false); - auto *error = xcb_request_check(c->xcb_connection(), cookie); + const auto seg = xcb_generate_id(c); + auto cookie = xcb_shm_attach_checked(c, seg, id, false); + auto *error = xcb_request_check(c, cookie); if (error) { - c->printXcbError("xcb_shm_attach() failed with error", error); + qCWarning(lcQpaXcb(), "xcb_shm_attach() failed"); free(error); if (shmdt(addr) == -1) qCWarning(lcQpaXcb, "shmdt() failed (%d: %s) for %p", errno, strerror(errno), addr); return false; } else if (!shmInfo) { // this was a test run, free the allocated test segment - xcb_shm_detach(c->xcb_connection(), seg); + xcb_shm_detach(c, seg); auto shmaddr = static_cast<quint8 *>(addr); if (shmdt(shmaddr) == -1) qCWarning(lcQpaXcb, "shmdt() failed (%d: %s) for %p", errno, strerror(errno), shmaddr); @@ -766,7 +766,7 @@ void QXcbBackingStoreImage::preparePaint(const QRegion ®ion) m_pendingFlush |= region; } -bool QXcbBackingStore::createSystemVShmSegment(QXcbConnection *c, size_t segmentSize, void *shmInfo) +bool QXcbBackingStore::createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize, void *shmInfo) { auto info = reinterpret_cast<xcb_shm_segment_info_t *>(shmInfo); return QXcbBackingStoreImage::createSystemVShmSegment(c, segmentSize, info); diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.h b/src/plugins/platforms/xcb/qxcbbackingstore.h index 39d023cb9d..b91e5c7dc2 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.h +++ b/src/plugins/platforms/xcb/qxcbbackingstore.h @@ -74,7 +74,7 @@ public: void beginPaint(const QRegion &) override; void endPaint() override; - static bool createSystemVShmSegment(QXcbConnection *c, size_t segmentSize = 1, + static bool createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize = 1, void *shmInfo = nullptr); protected: diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index a4294956c1..10d07c37bb 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -59,38 +59,24 @@ #include "qxcbbackingstore.h" #include "qxcbeventqueue.h" -#include <QSocketNotifier> #include <QAbstractEventDispatcher> -#include <QTimer> #include <QByteArray> #include <QScopedPointer> -#include <algorithm> - #include <stdio.h> #include <errno.h> -#include <xcb/shm.h> -#include <xcb/sync.h> + #include <xcb/xfixes.h> #include <xcb/xinerama.h> - -#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 +#if QT_CONFIG(xkb) +#define explicit dont_use_cxx_explicit +#include <xcb/xkb.h> +#undef explicit #endif - #if QT_CONFIG(xcb_xinput) #include <xcb/xinput.h> #endif -#if QT_CONFIG(xcb_render) -#include <xcb/render.h> -#endif - QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaXInput, "qt.qpa.input") @@ -99,7 +85,6 @@ Q_LOGGING_CATEGORY(lcQpaXInputEvents, "qt.qpa.input.events") Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen") Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events") Q_LOGGING_CATEGORY(lcQpaEventReader, "qt.qpa.events.reader") -Q_LOGGING_CATEGORY(lcQpaXcb, "qt.qpa.xcb") // for general (uncategorized) XCB logging Q_LOGGING_CATEGORY(lcQpaPeeker, "qt.qpa.peeker") Q_LOGGING_CATEGORY(lcQpaKeyboard, "qt.qpa.xkeyboard") Q_LOGGING_CATEGORY(lcQpaXDnd, "qt.qpa.xdnd") @@ -110,69 +95,19 @@ Q_LOGGING_CATEGORY(lcQpaXDnd, "qt.qpa.xdnd") #define XCB_GE_GENERIC 35 #endif -#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; - -static inline bool isXIEvent(xcb_generic_event_t *event, int opCode) -{ - qt_xcb_ge_event_t *e = reinterpret_cast<qt_xcb_ge_event_t *>(event); - return e->extension == opCode; -} -#endif // QT_CONFIG(xcb_xinput) - -#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) +void QXcbConnection::selectXRandrEvents() { - 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); + xcb_screen_iterator_t rootIter = xcb_setup_roots_iterator(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 + ); } - return _XDefaultIOError(dpy); } -#endif QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) const { @@ -314,7 +249,7 @@ void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_cha if (screen->mode() != outputChange.mode) screen->updateRefreshRate(outputChange.mode); // Only screen which belongs to the primary virtual desktop can be a primary screen - if (screen->screenNumber() == m_primaryScreenNumber) { + if (screen->screenNumber() == primaryScreenNumber()) { if (!screen->isPrimary() && checkOutputIsPrimary(outputChange.window, outputChange.output)) { screen->setPrimary(true); @@ -336,7 +271,7 @@ QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop, { QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputChange.output, outputInfo); // Only screen which belongs to the primary virtual desktop can be a primary screen - if (screen->screenNumber() == m_primaryScreenNumber) + if (screen->screenNumber() == primaryScreenNumber()) screen->setPrimary(checkOutputIsPrimary(outputChange.window, outputChange.output)); if (screen->isPrimary()) { @@ -384,7 +319,7 @@ void QXcbConnection::destroyScreen(QXcbScreen *screen) void QXcbConnection::initializeScreens() { - xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup); + xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup()); int xcbScreenNumber = 0; // screen number in the xcb sense QXcbScreen *primaryScreen = nullptr; while (it.rem) { @@ -396,7 +331,7 @@ void QXcbConnection::initializeScreens() QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber); m_virtualDesktops.append(virtualDesktop); QList<QPlatformScreen *> siblings; - if (has_randr_extension) { + if (hasXRender()) { // 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. @@ -457,7 +392,7 @@ void QXcbConnection::initializeScreens() // 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 (primaryScreenNumber() == xcbScreenNumber) { if (!primaryScreen || (primary && outputs[i] == primary->output)) { if (primaryScreen) primaryScreen->setPrimary(false); @@ -470,9 +405,9 @@ void QXcbConnection::initializeScreens() } } } - } else if (has_xinerama_extension) { + } else if (hasXinerama()) { // Xinerama is available - auto screens = Q_XCB_REPLY(xcb_xinerama_query_screens, m_connection); + auto screens = Q_XCB_REPLY(xcb_xinerama_query_screens, xcb_connection()); if (screens) { xcb_xinerama_screen_info_iterator_t it = xcb_xinerama_query_screens_screen_info_iterator(screens.get()); while (it.rem) { @@ -492,7 +427,7 @@ void QXcbConnection::initializeScreens() QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, nullptr); qCDebug(lcQpaScreen) << "created fake screen" << screen; m_screens << screen; - if (m_primaryScreenNumber == xcbScreenNumber) { + if (primaryScreenNumber() == xcbScreenNumber) { primaryScreen = screen; primaryScreen->setPrimary(true); } @@ -528,72 +463,28 @@ void QXcbConnection::initializeScreens() } QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName) - : m_canGrabServer(canGrabServer) + : QXcbBasicConnection(displayName) + , m_canGrabServer(canGrabServer) , m_defaultVisualId(defaultVisualId) - , m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY")) , m_nativeInterface(nativeInterface) { -#if QT_CONFIG(xcb_xlib) - Display *dpy = XOpenDisplay(m_displayName.constData()); - if (dpy) { - m_primaryScreenNumber = DefaultScreen(dpy); - m_connection = XGetXCBConnection(dpy); - XSetEventQueueOwner(dpy, XCBOwnsEventQueue); - XSetErrorHandler(nullErrorHandler); - XSetIOErrorHandler(ioErrorHandler); - m_xlib_display = dpy; - } -#else - m_connection = xcb_connect(m_displayName.constData(), &m_primaryScreenNumber); -#endif // QT_CONFIG(xcb_xlib) - - if (Q_UNLIKELY(!m_connection || xcb_connection_has_error(m_connection))) { - qCWarning(lcQpaScreen, "QXcbConnection: Could not connect to display %s", m_displayName.constData()); + if (!isConnected()) return; - } m_eventQueue = new QXcbEventQueue(this); - 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_connection, *ext_it); - - m_setup = xcb_get_setup(xcb_connection()); - m_xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP").toLower(); - initializeAllAtoms(); + selectXRandrEvents(); - initializeXSync(); - if (!qEnvironmentVariableIsSet("QT_XCB_NO_MITSHM")) - initializeShm(); - if (!qEnvironmentVariableIsSet("QT_XCB_NO_XRANDR")) - initializeXRandr(); - if (!has_randr_extension) - initializeXinerama(); - initializeXFixes(); initializeScreens(); - initializeXRender(); #if QT_CONFIG(xcb_xinput) - if (!qEnvironmentVariableIsSet("QT_XCB_NO_XI2")) - initializeXInput2(); + if (hasXInput2()) { + xi2SetupDevices(); + xi2SelectStateEvents(); + } #endif - initializeXShape(); - initializeXKB(); m_wmSupport.reset(new QXcbWMSupport(this)); m_keyboard = new QXcbKeyboard(this); @@ -632,22 +523,9 @@ QXcbConnection::~QXcbConnection() delete m_glIntegration; - if (isConnected()) { -#if QT_CONFIG(xcb_xlib) - XCloseDisplay(static_cast<Display *>(m_xlib_display)); -#else - xcb_disconnect(xcb_connection()); -#endif - } - delete m_keyboard; } -bool QXcbConnection::isConnected() const -{ - return m_connection && !xcb_connection_has_error(m_connection); -} - QXcbScreen *QXcbConnection::primaryScreen() const { if (!m_screens.isEmpty()) { @@ -752,17 +630,17 @@ void QXcbConnection::printXcbEvent(const QLoggingCategory &log, const char *mess CASE_PRINT_AND_RETURN( XCB_GE_GENERIC ); } // XFixes - if (has_xfixes && response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) + if (isXFixesType(response_type, XCB_XFIXES_SELECTION_NOTIFY)) PRINT_AND_RETURN("XCB_XFIXES_SELECTION_NOTIFY"); + // XRandR - if (has_randr_extension) { - if (response_type == xrandr_first_event + XCB_RANDR_NOTIFY) - PRINT_AND_RETURN("XCB_RANDR_NOTIFY"); - if (response_type == xrandr_first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY) - PRINT_AND_RETURN("XCB_RANDR_SCREEN_CHANGE_NOTIFY"); - } + if (isXRandrType(response_type, XCB_RANDR_NOTIFY)) + PRINT_AND_RETURN("XCB_RANDR_NOTIFY"); + if (isXRandrType(response_type, XCB_RANDR_SCREEN_CHANGE_NOTIFY)) + PRINT_AND_RETURN("XCB_RANDR_SCREEN_CHANGE_NOTIFY"); + // XKB - if (response_type == xkb_first_event) + if (isXkbType(response_type)) PRINT_AND_RETURN("XCB_XKB_* event"); // UNKNOWN @@ -1155,7 +1033,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) #if QT_CONFIG(xcb_xinput) case XCB_GE_GENERIC: // Here the windowEventListener is invoked from xi2HandleEvent() - if (hasXInput2() && isXIEvent(event, m_xiOpCode)) + if (hasXInput2() && isXIEvent(event)) xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event)); break; #endif @@ -1168,7 +1046,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) return; handled = true; - if (has_xfixes && response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) { + if (isXFixesType(response_type, XCB_XFIXES_SELECTION_NOTIFY)) { auto notify_event = reinterpret_cast<xcb_xfixes_selection_notify_event_t *>(event); setTime(notify_event->timestamp); #ifndef QT_NO_CLIPBOARD @@ -1176,14 +1054,14 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) #endif for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops)) virtualDesktop->handleXFixesSelectionNotify(notify_event); - } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_NOTIFY) { + } else if (isXRandrType(response_type, XCB_RANDR_NOTIFY)) { updateScreens(reinterpret_cast<xcb_randr_notify_event_t *>(event)); - } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY) { + } else if (isXRandrType(response_type, XCB_RANDR_SCREEN_CHANGE_NOTIFY)) { auto change_event = reinterpret_cast<xcb_randr_screen_change_notify_event_t *>(event); if (auto virtualDesktop = virtualDesktopForRootWindow(change_event->root)) virtualDesktop->handleScreenChange(change_event); #if QT_CONFIG(xkb) - } else if (response_type == xkb_first_event) { // https://bugs.freedesktop.org/show_bug.cgi?id=51295 + } else if (isXkbType(response_type)) { auto xkb_event = reinterpret_cast<_xkb_event *>(event); if (xkb_event->any.deviceID == m_keyboard->coreDeviceId()) { switch (xkb_event->any.xkbType) { @@ -1239,13 +1117,13 @@ void QXcbConnection::setMousePressWindow(QXcbWindow *w) void QXcbConnection::grabServer() { if (m_canGrabServer) - xcb_grab_server(m_connection); + xcb_grab_server(xcb_connection()); } void QXcbConnection::ungrabServer() { if (m_canGrabServer) - xcb_ungrab_server(m_connection); + xcb_ungrab_server(xcb_connection()); } xcb_timestamp_t QXcbConnection::getTimestamp() @@ -1360,17 +1238,6 @@ xcb_window_t QXcbConnection::clientLeader() return m_clientLeader; } -#if QT_CONFIG(xcb_xinput) -static inline bool isXIType(xcb_generic_event_t *event, int opCode, uint16_t type) -{ - if (!isXIEvent(event, opCode)) - return false; - - auto *e = reinterpret_cast<qt_xcb_ge_event_t *>(event); - return e->event_type == type; -} -#endif - /*! \internal Compresses events of the same type to avoid swamping the event queue. @@ -1405,7 +1272,7 @@ bool QXcbConnection::compressEvent(xcb_generic_event_t *event) const return false; // compress XI_Motion - if (isXIType(event, m_xiOpCode, XCB_INPUT_MOTION)) { + if (isXIType(event, XCB_INPUT_MOTION)) { #if QT_CONFIG(tabletevent) auto xdev = reinterpret_cast<xcb_input_motion_event_t *>(event); if (!QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents) && @@ -1414,17 +1281,18 @@ bool QXcbConnection::compressEvent(xcb_generic_event_t *event) const #endif // QT_CONFIG(tabletevent) return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch, [this](xcb_generic_event_t *next, int) { - return isXIType(next, m_xiOpCode, XCB_INPUT_MOTION); + return isXIType(next, XCB_INPUT_MOTION); }); } // compress XI_TouchUpdate for the same touch point id - if (isXIType(event, m_xiOpCode, XCB_INPUT_TOUCH_UPDATE)) { + if (isXIType(event, XCB_INPUT_TOUCH_UPDATE)) { auto touchUpdateEvent = reinterpret_cast<xcb_input_touch_update_event_t *>(event); uint32_t id = touchUpdateEvent->detail % INT_MAX; + return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch, [this, &id](xcb_generic_event_t *next, int) { - if (!isXIType(next, m_xiOpCode, XCB_INPUT_TOUCH_UPDATE)) + if (!isXIType(next, XCB_INPUT_TOUCH_UPDATE)) return false; auto touchUpdateNextEvent = reinterpret_cast<xcb_input_touch_update_event_t *>(next); return id == touchUpdateNextEvent->detail % INT_MAX; @@ -1465,16 +1333,16 @@ bool QXcbConnection::isUserInputEvent(xcb_generic_event_t *event) const #if QT_CONFIG(xcb_xinput) if (connection()->hasXInput2()) { - isInputEvent = isXIType(event, m_xiOpCode, XCB_INPUT_BUTTON_PRESS) || - isXIType(event, m_xiOpCode, XCB_INPUT_BUTTON_RELEASE) || - isXIType(event, m_xiOpCode, XCB_INPUT_MOTION) || - isXIType(event, m_xiOpCode, XCB_INPUT_TOUCH_BEGIN) || - isXIType(event, m_xiOpCode, XCB_INPUT_TOUCH_UPDATE) || - isXIType(event, m_xiOpCode, XCB_INPUT_TOUCH_END) || - isXIType(event, m_xiOpCode, XCB_INPUT_ENTER) || - isXIType(event, m_xiOpCode, XCB_INPUT_LEAVE) || + isInputEvent = isXIType(event, XCB_INPUT_BUTTON_PRESS) || + isXIType(event, XCB_INPUT_BUTTON_RELEASE) || + isXIType(event, XCB_INPUT_MOTION) || + isXIType(event, XCB_INPUT_TOUCH_BEGIN) || + isXIType(event, XCB_INPUT_TOUCH_UPDATE) || + isXIType(event, XCB_INPUT_TOUCH_END) || + isXIType(event, XCB_INPUT_ENTER) || + isXIType(event, XCB_INPUT_LEAVE) || // wacom driver's way of reporting tool proximity - isXIType(event, m_xiOpCode, XCB_INPUT_PROPERTY); + isXIType(event, XCB_INPUT_PROPERTY); } if (isInputEvent) return true; @@ -1545,265 +1413,10 @@ void QXcbConnection::processXcbEvents(QEventLoop::ProcessEventsFlags flags) xcb_flush(xcb_connection()); } -static const char * xcb_atomnames = { - // window-manager <-> client protocols - "WM_PROTOCOLS\0" - "WM_DELETE_WINDOW\0" - "WM_TAKE_FOCUS\0" - "_NET_WM_PING\0" - "_NET_WM_CONTEXT_HELP\0" - "_NET_WM_SYNC_REQUEST\0" - "_NET_WM_SYNC_REQUEST_COUNTER\0" - "MANAGER\0" - "_NET_SYSTEM_TRAY_OPCODE\0" - - // ICCCM window state - "WM_STATE\0" - "WM_CHANGE_STATE\0" - "WM_CLASS\0" - "WM_NAME\0" - - // Session management - "WM_CLIENT_LEADER\0" - "WM_WINDOW_ROLE\0" - "SM_CLIENT_ID\0" - "WM_CLIENT_MACHINE\0" - - // Clipboard - "CLIPBOARD\0" - "INCR\0" - "TARGETS\0" - "MULTIPLE\0" - "TIMESTAMP\0" - "SAVE_TARGETS\0" - "CLIP_TEMPORARY\0" - "_QT_SELECTION\0" - "_QT_CLIPBOARD_SENTINEL\0" - "_QT_SELECTION_SENTINEL\0" - "CLIPBOARD_MANAGER\0" - - "RESOURCE_MANAGER\0" - - "_XSETROOT_ID\0" - - "_QT_SCROLL_DONE\0" - "_QT_INPUT_ENCODING\0" - - "_QT_CLOSE_CONNECTION\0" - - "_MOTIF_WM_HINTS\0" - - "DTWM_IS_RUNNING\0" - "ENLIGHTENMENT_DESKTOP\0" - "_DT_SAVE_MODE\0" - "_SGI_DESKS_MANAGER\0" - - // EWMH (aka NETWM) - "_NET_SUPPORTED\0" - "_NET_VIRTUAL_ROOTS\0" - "_NET_WORKAREA\0" - - "_NET_MOVERESIZE_WINDOW\0" - "_NET_WM_MOVERESIZE\0" - - "_NET_WM_NAME\0" - "_NET_WM_ICON_NAME\0" - "_NET_WM_ICON\0" - - "_NET_WM_PID\0" - - "_NET_WM_WINDOW_OPACITY\0" - - "_NET_WM_STATE\0" - "_NET_WM_STATE_ABOVE\0" - "_NET_WM_STATE_BELOW\0" - "_NET_WM_STATE_FULLSCREEN\0" - "_NET_WM_STATE_MAXIMIZED_HORZ\0" - "_NET_WM_STATE_MAXIMIZED_VERT\0" - "_NET_WM_STATE_MODAL\0" - "_NET_WM_STATE_STAYS_ON_TOP\0" - "_NET_WM_STATE_DEMANDS_ATTENTION\0" - - "_NET_WM_USER_TIME\0" - "_NET_WM_USER_TIME_WINDOW\0" - "_NET_WM_FULL_PLACEMENT\0" - - "_NET_WM_WINDOW_TYPE\0" - "_NET_WM_WINDOW_TYPE_DESKTOP\0" - "_NET_WM_WINDOW_TYPE_DOCK\0" - "_NET_WM_WINDOW_TYPE_TOOLBAR\0" - "_NET_WM_WINDOW_TYPE_MENU\0" - "_NET_WM_WINDOW_TYPE_UTILITY\0" - "_NET_WM_WINDOW_TYPE_SPLASH\0" - "_NET_WM_WINDOW_TYPE_DIALOG\0" - "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0" - "_NET_WM_WINDOW_TYPE_POPUP_MENU\0" - "_NET_WM_WINDOW_TYPE_TOOLTIP\0" - "_NET_WM_WINDOW_TYPE_NOTIFICATION\0" - "_NET_WM_WINDOW_TYPE_COMBO\0" - "_NET_WM_WINDOW_TYPE_DND\0" - "_NET_WM_WINDOW_TYPE_NORMAL\0" - "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0" - - "_KDE_NET_WM_FRAME_STRUT\0" - "_NET_FRAME_EXTENTS\0" - - "_NET_STARTUP_INFO\0" - "_NET_STARTUP_INFO_BEGIN\0" - - "_NET_SUPPORTING_WM_CHECK\0" - - "_NET_WM_CM_S0\0" - - "_NET_SYSTEM_TRAY_VISUAL\0" - - "_NET_ACTIVE_WINDOW\0" - - // Property formats - "TEXT\0" - "UTF8_STRING\0" - "CARDINAL\0" - - // xdnd - "XdndEnter\0" - "XdndPosition\0" - "XdndStatus\0" - "XdndLeave\0" - "XdndDrop\0" - "XdndFinished\0" - "XdndTypeList\0" - "XdndActionList\0" - - "XdndSelection\0" - - "XdndAware\0" - "XdndProxy\0" - - "XdndActionCopy\0" - "XdndActionLink\0" - "XdndActionMove\0" - "XdndActionPrivate\0" - - // Motif DND - "_MOTIF_DRAG_AND_DROP_MESSAGE\0" - "_MOTIF_DRAG_INITIATOR_INFO\0" - "_MOTIF_DRAG_RECEIVER_INFO\0" - "_MOTIF_DRAG_WINDOW\0" - "_MOTIF_DRAG_TARGETS\0" - - "XmTRANSFER_SUCCESS\0" - "XmTRANSFER_FAILURE\0" - - // Xkb - "_XKB_RULES_NAMES\0" - - // XEMBED - "_XEMBED\0" - "_XEMBED_INFO\0" - - // XInput2 - "Button Left\0" - "Button Middle\0" - "Button Right\0" - "Button Wheel Up\0" - "Button Wheel Down\0" - "Button Horiz Wheel Left\0" - "Button Horiz Wheel Right\0" - "Abs MT Position X\0" - "Abs MT Position Y\0" - "Abs MT Touch Major\0" - "Abs MT Touch Minor\0" - "Abs MT Orientation\0" - "Abs MT Pressure\0" - "Abs MT Tracking ID\0" - "Max Contacts\0" - "Rel X\0" - "Rel Y\0" - // XInput2 tablet - "Abs X\0" - "Abs Y\0" - "Abs Pressure\0" - "Abs Tilt X\0" - "Abs Tilt Y\0" - "Abs Wheel\0" - "Abs Distance\0" - "Wacom Serial IDs\0" - "INTEGER\0" - "Rel Horiz Wheel\0" - "Rel Vert Wheel\0" - "Rel Horiz Scroll\0" - "Rel Vert Scroll\0" - "_XSETTINGS_SETTINGS\0" - "_COMPIZ_DECOR_PENDING\0" - "_COMPIZ_DECOR_REQUEST\0" - "_COMPIZ_DECOR_DELETE_PIXMAP\0" - "_COMPIZ_TOOLKIT_ACTION\0" - "_GTK_LOAD_ICONTHEMES\0" - "AT_SPI_BUS\0" - "EDID\0" - "EDID_DATA\0" - "XFree86_DDC_EDID1_RAWDATA\0" - // \0\0 terminates loop. -}; - -QXcbAtom::Atom QXcbConnection::qatom(xcb_atom_t xatom) const -{ - return static_cast<QXcbAtom::Atom>(std::find(m_allAtoms, m_allAtoms + QXcbAtom::NAtoms, xatom) - m_allAtoms); -} - -void QXcbConnection::initializeAllAtoms() { - const char *names[QXcbAtom::NAtoms]; - const char *ptr = xcb_atomnames; - - int i = 0; - while (*ptr) { - names[i++] = ptr; - while (*ptr) - ++ptr; - ++ptr; - } - - Q_ASSERT(i == QXcbAtom::NAtoms); - - xcb_intern_atom_cookie_t cookies[QXcbAtom::NAtoms]; - - Q_ASSERT(i == QXcbAtom::NAtoms); - for (i = 0; i < QXcbAtom::NAtoms; ++i) - cookies[i] = xcb_intern_atom(xcb_connection(), false, strlen(names[i]), names[i]); - - for (i = 0; i < QXcbAtom::NAtoms; ++i) { - xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0); - m_allAtoms[i] = reply->atom; - free(reply); - } -} - -xcb_atom_t QXcbConnection::internAtom(const char *name) -{ - if (!name || *name == 0) - return XCB_NONE; - - return Q_XCB_REPLY(xcb_intern_atom, xcb_connection(), false, strlen(name), name)->atom; -} - -QByteArray QXcbConnection::atomName(xcb_atom_t atom) -{ - if (!atom) - return QByteArray(); - - auto reply = Q_XCB_REPLY(xcb_get_atom_name, xcb_connection(), atom); - if (!reply) - qWarning() << "QXcbConnection::atomName: bad Atom" << atom; - else - return QByteArray(xcb_get_atom_name_name(reply.get()), xcb_get_atom_name_name_length(reply.get())); - - return QByteArray(); -} - const xcb_format_t *QXcbConnection::formatForDepth(uint8_t depth) const { xcb_format_iterator_t iterator = - xcb_setup_pixmap_formats_iterator(m_setup); + xcb_setup_pixmap_formats_iterator(setup()); while (iterator.rem) { xcb_format_t *format = iterator.data; @@ -1823,208 +1436,6 @@ void QXcbConnection::sync() free(xcb_get_input_focus_reply(xcb_connection(), cookie, 0)); } -void QXcbConnection::initializeShm() -{ - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_shm_id); - if (!reply || !reply->present) { - qCDebug(lcQpaXcb, "MIT-SHM extension is not present on the X server"); - return; - } - has_shm = true; - - auto shm_query = Q_XCB_REPLY(xcb_shm_query_version, m_connection); - if (shm_query) { - has_shm_fd = (shm_query->major_version == 1 && shm_query->minor_version >= 2) || - shm_query->major_version > 1; - } else { - qCWarning(lcQpaXcb, "QXcbConnection: Failed to request MIT-SHM version"); - } - - qCDebug(lcQpaXcb) << "Has MIT-SHM :" << has_shm; - qCDebug(lcQpaXcb) << "Has MIT-SHM FD :" << has_shm_fd; - - // 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(this)) { - qCDebug(lcQpaXcb, "failed to create System V shared memory segment (remote " - "X11 connection?), disabling SHM"); - has_shm = has_shm_fd = false; - } - if (wasEnabled) - logging->setEnabled(QtMsgType::QtWarningMsg, true); -} - -void QXcbConnection::initializeXFixes() -{ - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xfixes_id); - if (!reply || !reply->present) - return; - - auto xfixes_query = Q_XCB_REPLY(xcb_xfixes_query_version, m_connection, - XCB_XFIXES_MAJOR_VERSION, - XCB_XFIXES_MINOR_VERSION); - if (!xfixes_query || xfixes_query->major_version < 2) { - qWarning("QXcbConnection: Failed to initialize XFixes"); - return; - } - xfixes_first_event = reply->first_event; - has_xfixes = true; -} - -void QXcbConnection::initializeXRender() -{ -#if QT_CONFIG(xcb_render) - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_render_id); - if (!reply || !reply->present) { - qCDebug(lcQpaXcb, "XRender extension not present on the X server"); - return; - } - - auto xrender_query = Q_XCB_REPLY(xcb_render_query_version, m_connection, - XCB_RENDER_MAJOR_VERSION, - XCB_RENDER_MINOR_VERSION); - if (!xrender_query) { - qCWarning(lcQpaXcb, "xcb_render_query_version failed"); - return; - } - - has_render_extension = true; - m_xrenderVersion.first = xrender_query->major_version; - m_xrenderVersion.second = xrender_query->minor_version; -#endif -} - -void QXcbConnection::initializeXRandr() -{ - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_randr_id); - if (!reply || !reply->present) - return; - - xrandr_first_event = reply->first_event; - - auto xrandr_query = Q_XCB_REPLY(xcb_randr_query_version, m_connection, - XCB_RANDR_MAJOR_VERSION, - XCB_RANDR_MINOR_VERSION); - - has_randr_extension = true; - - if (!xrandr_query || (xrandr_query->major_version < 1 || (xrandr_query->major_version == 1 && xrandr_query->minor_version < 2))) { - qWarning("QXcbConnection: Failed to initialize XRandr"); - has_randr_extension = false; - } - - 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::initializeXinerama() -{ - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xinerama_id); - if (!reply || !reply->present) - return; - - auto xinerama_is_active = Q_XCB_REPLY(xcb_xinerama_is_active, m_connection); - has_xinerama_extension = xinerama_is_active && xinerama_is_active->state; -} - -void QXcbConnection::initializeXShape() -{ - const xcb_query_extension_reply_t *xshape_reply = xcb_get_extension_data(m_connection, &xcb_shape_id); - if (!xshape_reply || !xshape_reply->present) - return; - - has_shape_extension = true; - auto shape_query = Q_XCB_REPLY(xcb_shape_query_version, m_connection); - if (!shape_query) { - qWarning("QXcbConnection: Failed to initialize SHAPE extension"); - } else if (shape_query->major_version > 1 || (shape_query->major_version == 1 && shape_query->minor_version >= 1)) { - // The input shape is the only thing added in SHAPE 1.1 - has_input_shape = true; - } -} - -void QXcbConnection::initializeXKB() -{ -#if QT_CONFIG(xkb) - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xkb_id); - if (!reply || !reply->present) { - qWarning("Qt: XKEYBOARD extension not present on the X server."); - xkb_first_event = 0; - return; - } - xkb_first_event = reply->first_event; - - xcb_connection_t *c = connection()->xcb_connection(); - - auto xkb_query = Q_XCB_REPLY(xcb_xkb_use_extension, c, - XKB_X11_MIN_MAJOR_XKB_VERSION, - XKB_X11_MIN_MINOR_XKB_VERSION); - - if (!xkb_query) { - qWarning("Qt: Failed to initialize XKB extension"); - return; - } else if (!xkb_query->supported) { - qWarning("Qt: Unsupported XKB version (We want %d %d, but X server has %d %d)", - XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION, - xkb_query->serverMajor, xkb_query->serverMinor); - return; - } - - has_xkb = true; - - const uint16_t required_map_parts = (XCB_XKB_MAP_PART_KEY_TYPES | - XCB_XKB_MAP_PART_KEY_SYMS | - XCB_XKB_MAP_PART_MODIFIER_MAP | - XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | - XCB_XKB_MAP_PART_KEY_ACTIONS | - XCB_XKB_MAP_PART_KEY_BEHAVIORS | - XCB_XKB_MAP_PART_VIRTUAL_MODS | - XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP); - - const uint16_t required_events = (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | - XCB_XKB_EVENT_TYPE_MAP_NOTIFY | - XCB_XKB_EVENT_TYPE_STATE_NOTIFY); - - // XKB events are reported to all interested clients without regard - // to the current keyboard input focus or grab state - xcb_void_cookie_t select = xcb_xkb_select_events_checked(c, - XCB_XKB_ID_USE_CORE_KBD, - required_events, - 0, - required_events, - required_map_parts, - required_map_parts, - 0); - - xcb_generic_error_t *error = xcb_request_check(c, select); - if (error) { - free(error); - qWarning("Qt: failed to select notify events from xcb-xkb"); - return; - } -#endif -} - -void QXcbConnection::initializeXSync() -{ - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(xcb_connection(), &xcb_sync_id); - if (!reply || !reply->present) - return; - - has_sync_extension = true; -} - QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() const { if (!m_systemTrayTracker) { diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 96591c7994..fcc1309afe 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -47,31 +47,18 @@ #include "qxcbexport.h" #include <QHash> #include <QList> -#include <QObject> #include <QVector> #include <qpa/qwindowsysteminterface.h> #include <QtCore/QLoggingCategory> #include <QtCore/private/qglobal_p.h> #include "qxcbeventqueue.h" - -#include <cstdlib> -#include <memory> - -// This is needed to make Qt compile together with XKB. xkb.h is using a variable -// which is called 'explicit', this is a reserved keyword in c++ -#if QT_CONFIG(xkb) -#define explicit dont_use_cxx_explicit -#include <xcb/xkb.h> -#undef explicit -#endif +#include "qxcbconnection_basic.h" #if QT_CONFIG(tabletevent) #include <QTabletEvent> #endif -struct xcb_randr_get_output_info_reply_t; - QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQpaXInput) @@ -79,7 +66,6 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputDevices) Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputEvents) Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen) Q_DECLARE_LOGGING_CATEGORY(lcQpaEvents) -Q_DECLARE_LOGGING_CATEGORY(lcQpaXcb) Q_DECLARE_LOGGING_CATEGORY(lcQpaPeeker) Q_DECLARE_LOGGING_CATEGORY(lcQpaKeyboard) Q_DECLARE_LOGGING_CATEGORY(lcQpaXDnd) @@ -96,215 +82,6 @@ class QXcbNativeInterface; class QXcbSystemTrayTracker; class QXcbGlIntegration; -namespace QXcbAtom { - enum Atom { - // window-manager <-> client protocols - WM_PROTOCOLS, - WM_DELETE_WINDOW, - WM_TAKE_FOCUS, - _NET_WM_PING, - _NET_WM_CONTEXT_HELP, - _NET_WM_SYNC_REQUEST, - _NET_WM_SYNC_REQUEST_COUNTER, - MANAGER, // System tray notification - _NET_SYSTEM_TRAY_OPCODE, // System tray operation - - // ICCCM window state - WM_STATE, - WM_CHANGE_STATE, - WM_CLASS, - WM_NAME, - - // Session management - WM_CLIENT_LEADER, - WM_WINDOW_ROLE, - SM_CLIENT_ID, - WM_CLIENT_MACHINE, - - // Clipboard - CLIPBOARD, - INCR, - TARGETS, - MULTIPLE, - TIMESTAMP, - SAVE_TARGETS, - CLIP_TEMPORARY, - _QT_SELECTION, - _QT_CLIPBOARD_SENTINEL, - _QT_SELECTION_SENTINEL, - CLIPBOARD_MANAGER, - - RESOURCE_MANAGER, - - _XSETROOT_ID, - - _QT_SCROLL_DONE, - _QT_INPUT_ENCODING, - - // Qt/XCB specific - _QT_CLOSE_CONNECTION, - - _MOTIF_WM_HINTS, - - DTWM_IS_RUNNING, - ENLIGHTENMENT_DESKTOP, - _DT_SAVE_MODE, - _SGI_DESKS_MANAGER, - - // EWMH (aka NETWM) - _NET_SUPPORTED, - _NET_VIRTUAL_ROOTS, - _NET_WORKAREA, - - _NET_MOVERESIZE_WINDOW, - _NET_WM_MOVERESIZE, - - _NET_WM_NAME, - _NET_WM_ICON_NAME, - _NET_WM_ICON, - - _NET_WM_PID, - - _NET_WM_WINDOW_OPACITY, - - _NET_WM_STATE, - _NET_WM_STATE_ABOVE, - _NET_WM_STATE_BELOW, - _NET_WM_STATE_FULLSCREEN, - _NET_WM_STATE_MAXIMIZED_HORZ, - _NET_WM_STATE_MAXIMIZED_VERT, - _NET_WM_STATE_MODAL, - _NET_WM_STATE_STAYS_ON_TOP, - _NET_WM_STATE_DEMANDS_ATTENTION, - - _NET_WM_USER_TIME, - _NET_WM_USER_TIME_WINDOW, - _NET_WM_FULL_PLACEMENT, - - _NET_WM_WINDOW_TYPE, - _NET_WM_WINDOW_TYPE_DESKTOP, - _NET_WM_WINDOW_TYPE_DOCK, - _NET_WM_WINDOW_TYPE_TOOLBAR, - _NET_WM_WINDOW_TYPE_MENU, - _NET_WM_WINDOW_TYPE_UTILITY, - _NET_WM_WINDOW_TYPE_SPLASH, - _NET_WM_WINDOW_TYPE_DIALOG, - _NET_WM_WINDOW_TYPE_DROPDOWN_MENU, - _NET_WM_WINDOW_TYPE_POPUP_MENU, - _NET_WM_WINDOW_TYPE_TOOLTIP, - _NET_WM_WINDOW_TYPE_NOTIFICATION, - _NET_WM_WINDOW_TYPE_COMBO, - _NET_WM_WINDOW_TYPE_DND, - _NET_WM_WINDOW_TYPE_NORMAL, - _KDE_NET_WM_WINDOW_TYPE_OVERRIDE, - - _KDE_NET_WM_FRAME_STRUT, - _NET_FRAME_EXTENTS, - - _NET_STARTUP_INFO, - _NET_STARTUP_INFO_BEGIN, - - _NET_SUPPORTING_WM_CHECK, - - _NET_WM_CM_S0, - - _NET_SYSTEM_TRAY_VISUAL, - - _NET_ACTIVE_WINDOW, - - // Property formats - TEXT, - UTF8_STRING, - CARDINAL, - - // Xdnd - XdndEnter, - XdndPosition, - XdndStatus, - XdndLeave, - XdndDrop, - XdndFinished, - XdndTypelist, - XdndActionList, - - XdndSelection, - - XdndAware, - XdndProxy, - - XdndActionCopy, - XdndActionLink, - XdndActionMove, - XdndActionPrivate, - - // Motif DND - _MOTIF_DRAG_AND_DROP_MESSAGE, - _MOTIF_DRAG_INITIATOR_INFO, - _MOTIF_DRAG_RECEIVER_INFO, - _MOTIF_DRAG_WINDOW, - _MOTIF_DRAG_TARGETS, - - XmTRANSFER_SUCCESS, - XmTRANSFER_FAILURE, - - // Xkb - _XKB_RULES_NAMES, - - // XEMBED - _XEMBED, - _XEMBED_INFO, - - // XInput2 - ButtonLeft, - ButtonMiddle, - ButtonRight, - ButtonWheelUp, - ButtonWheelDown, - ButtonHorizWheelLeft, - ButtonHorizWheelRight, - AbsMTPositionX, - AbsMTPositionY, - AbsMTTouchMajor, - AbsMTTouchMinor, - AbsMTOrientation, - AbsMTPressure, - AbsMTTrackingID, - MaxContacts, - RelX, - RelY, - // XInput2 tablet - AbsX, - AbsY, - AbsPressure, - AbsTiltX, - AbsTiltY, - AbsWheel, - AbsDistance, - WacomSerialIDs, - INTEGER, - RelHorizWheel, - RelVertWheel, - RelHorizScroll, - RelVertScroll, - - _XSETTINGS_SETTINGS, - - _COMPIZ_DECOR_PENDING, - _COMPIZ_DECOR_REQUEST, - _COMPIZ_DECOR_DELETE_PIXMAP, - _COMPIZ_TOOLKIT_ACTION, - _GTK_LOAD_ICONTHEMES, - - AT_SPI_BUS, - - EDID, - EDID_DATA, - XFree86_DDC_EDID1_RAWDATA, - - NAtoms - }; -} - class QXcbWindowEventListener { public: @@ -346,7 +123,7 @@ private: QXcbWindow *m_window; }; -class Q_XCB_EXPORT QXcbConnection : public QObject +class Q_XCB_EXPORT QXcbConnection : public QXcbBasicConnection { Q_OBJECT public: @@ -354,23 +131,15 @@ public: ~QXcbConnection(); QXcbConnection *connection() const { return const_cast<QXcbConnection *>(this); } - bool isConnected() const; QXcbEventQueue *eventQueue() const { return m_eventQueue; } const QList<QXcbVirtualDesktop *> &virtualDesktops() const { return m_virtualDesktops; } const QList<QXcbScreen *> &screens() const { return m_screens; } - int primaryScreenNumber() const { return m_primaryScreenNumber; } - QXcbVirtualDesktop *primaryVirtualDesktop() const { return m_virtualDesktops.value(m_primaryScreenNumber); } + QXcbVirtualDesktop *primaryVirtualDesktop() const { + return m_virtualDesktops.value(primaryScreenNumber()); + } QXcbScreen *primaryScreen() const; - inline xcb_atom_t atom(QXcbAtom::Atom atom) const { return m_allAtoms[atom]; } - QXcbAtom::Atom qatom(xcb_atom_t atom) const; - xcb_atom_t internAtom(const char *name); - QByteArray atomName(xcb_atom_t atom); - - const char *displayName() const { return m_displayName.constData(); } - xcb_connection_t *xcb_connection() const { return m_connection; } - const xcb_setup_t *setup() const { return m_setup; } const xcb_format_t *formatForDepth(uint8_t depth) const; bool imageNeedsEndianSwap() const @@ -378,9 +147,9 @@ public: if (!hasShm()) return false; // The non-Shm path does its own swapping #if Q_BYTE_ORDER == Q_BIG_ENDIAN - return m_setup->image_byte_order != XCB_IMAGE_ORDER_MSB_FIRST; + return setup()->image_byte_order != XCB_IMAGE_ORDER_MSB_FIRST; #else - return m_setup->image_byte_order != XCB_IMAGE_ORDER_LSB_FIRST; + return setup()->image_byte_order != XCB_IMAGE_ORDER_LSB_FIRST; #endif } @@ -400,9 +169,6 @@ public: bool hasDefaultVisualId() const { return m_defaultVisualId != UINT_MAX; } xcb_visualid_t defaultVisualId() const { return m_defaultVisualId; } -#if QT_CONFIG(xcb_xlib) - void *xlib_display() const { return m_xlib_display; } -#endif void sync(); void handleXcbError(xcb_generic_error_t *error); @@ -425,23 +191,6 @@ public: inline xcb_timestamp_t netWmUserTime() const { return m_netWmUserTime; } inline void setNetWmUserTime(xcb_timestamp_t t) { if (t > m_netWmUserTime) m_netWmUserTime = t; } - bool hasXFixes() const { return has_xfixes; } - bool hasXShape() const { return has_shape_extension; } - bool hasXRandr() const { return has_randr_extension; } - bool hasInputShape() const { return has_input_shape; } - bool hasXKB() const { return has_xkb; } - bool hasXRender(int major = -1, int minor = -1) const - { - if (has_render_extension && major != -1 && minor != -1) - return m_xrenderVersion >= qMakePair(major, minor); - - return has_render_extension; - } - bool hasXInput2() const { return m_xi2Enabled; } - bool hasShm() const { return has_shm; } - bool hasShmFd() const { return has_shm_fd; } - bool hasXSync() const { return has_sync_extension; } - xcb_timestamp_t getTimestamp(); xcb_window_t getSelectionOwner(xcb_atom_t atom) const; xcb_window_t getQtSelectionOwner(); @@ -483,8 +232,6 @@ public: void xi2SelectDeviceEventsCompatibility(xcb_window_t window); bool xi2SetMouseGrabEnabled(xcb_window_t w, bool grab); bool xi2MouseEventsDisabled() const; - bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; } - bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; } Qt::MouseButton xiToQtMouseButton(uint32_t b); void xi2UpdateScrollingDevices(); bool startSystemMoveResizeForTouchBegin(xcb_window_t window, const QPoint &point, int corner); @@ -496,22 +243,14 @@ public: QXcbGlIntegration *glIntegration() const; - void flush() { xcb_flush(m_connection); } + void flush() { xcb_flush(xcb_connection()); } void processXcbEvents(QEventLoop::ProcessEventsFlags flags); protected: bool event(QEvent *e) override; private: - void initializeAllAtoms(); - void initializeShm(); - void initializeXFixes(); - void initializeXRender(); - void initializeXRandr(); - void initializeXinerama(); - void initializeXShape(); - void initializeXKB(); - void initializeXSync(); + void selectXRandrEvents(); QXcbScreen* findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) const; QXcbScreen* findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output) const; QXcbVirtualDesktop* virtualDesktopForRootWindow(xcb_window_t rootWindow) const; @@ -525,10 +264,7 @@ private: void initializeScreens(); bool compressEvent(xcb_generic_event_t *event) const; - bool m_xi2Enabled = false; #if QT_CONFIG(xcb_xinput) - int m_xi2Minor = -1; - void initializeXInput2(); void xi2SetupDevice(void *info, bool removeExisting = true); void xi2SetupDevices(); struct TouchDeviceData { @@ -554,7 +290,6 @@ private: void xi2HandleEvent(xcb_ge_event_t *event); void xi2HandleHierarchyEvent(void *event); void xi2HandleDeviceChangedEvent(void *event); - int m_xiOpCode; void xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow); #if QT_CONFIG(tabletevent) struct TabletData { @@ -597,22 +332,15 @@ private: static bool xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value); #endif - xcb_connection_t *m_connection = nullptr; - const xcb_setup_t *m_setup = nullptr; const bool m_canGrabServer; const xcb_visualid_t m_defaultVisualId; QList<QXcbVirtualDesktop *> m_virtualDesktops; QList<QXcbScreen *> m_screens; - int m_primaryScreenNumber = 0; - - xcb_atom_t m_allAtoms[QXcbAtom::NAtoms]; xcb_timestamp_t m_time = XCB_CURRENT_TIME; xcb_timestamp_t m_netWmUserTime = XCB_CURRENT_TIME; - QByteArray m_displayName; - QXcbKeyboard *m_keyboard = nullptr; #ifndef QT_NO_CLIPBOARD QXcbClipboard *m_clipboard = nullptr; @@ -623,9 +351,6 @@ private: QScopedPointer<QXcbWMSupport> m_wmSupport; QXcbNativeInterface *m_nativeInterface = nullptr; -#if QT_CONFIG(xcb_xlib) - void *m_xlib_display = nullptr; -#endif QXcbEventQueue *m_eventQueue = nullptr; #if QT_CONFIG(xcb_xinput) @@ -641,26 +366,6 @@ private: QVector<PeekFunc> m_peekFuncs; - uint32_t xfixes_first_event = 0; - uint32_t xrandr_first_event = 0; - uint32_t xkb_first_event = 0; -#if QT_CONFIG(xcb_xinput) - uint32_t xinput_first_event = 0; -#endif - - bool has_xfixes = false; - bool has_xinerama_extension = false; - bool has_shape_extension = false; - bool has_randr_extension = false; - bool has_input_shape; - bool has_xkb = false; - bool has_render_extension = false; - bool has_shm = false; - bool has_shm_fd = false; - bool has_sync_extension = false; - - QPair<int, int> m_xrenderVersion; - Qt::MouseButtons m_buttonState = 0; Qt::MouseButton m_button = Qt::NoButton; @@ -699,22 +404,6 @@ private: QXcbConnection *m_connection; }; -#define Q_XCB_REPLY_CONNECTION_ARG(connection, ...) connection - -struct QStdFreeDeleter { - void operator()(void *p) const Q_DECL_NOTHROW { return std::free(p); } -}; - -#define Q_XCB_REPLY(call, ...) \ - std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \ - call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call(__VA_ARGS__), nullptr) \ - ) - -#define Q_XCB_REPLY_UNCHECKED(call, ...) \ - std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \ - call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call##_unchecked(__VA_ARGS__), nullptr) \ - ) - // The xcb_send_event() requires all events to have 32 bytes. It calls memcpy() on the // passed in event. If the passed in event is less than 32 bytes, memcpy() reaches into // unrelated memory. 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 diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.h b/src/plugins/platforms/xcb/qxcbconnection_basic.h new file mode 100644 index 0000000000..ca91f7fa45 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbconnection_basic.h @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ +#ifndef QXCBBASICCONNECTION_H +#define QXCBBASICCONNECTION_H + +#include "qxcbatom.h" + +#include <QtCore/QPair> +#include <QtCore/QObject> +#include <QtCore/QByteArray> +#include <QtCore/QLoggingCategory> +#include <QtGui/private/qtguiglobal_p.h> + +#include <xcb/xcb.h> + +#include <memory> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcQpaXcb) + +class QXcbBasicConnection : public QObject +{ + Q_OBJECT +public: + QXcbBasicConnection(const char *displayName); + ~QXcbBasicConnection(); + +#if QT_CONFIG(xcb_xlib) + void *xlib_display() const { return m_xlibDisplay; } +#endif + const char *displayName() const { return m_displayName.constData(); } + int primaryScreenNumber() const { return m_primaryScreenNumber; } + xcb_connection_t *xcb_connection() const { return m_xcbConnection; } + bool isConnected() const { + return m_xcbConnection && !xcb_connection_has_error(m_xcbConnection); + } + const xcb_setup_t *setup() const { return m_setup; } + + inline xcb_atom_t atom(QXcbAtom::Atom qatom) const { return m_xcbAtom.atom(qatom); } + QXcbAtom::Atom qatom(xcb_atom_t atom) const { return m_xcbAtom.qatom(atom); } + xcb_atom_t internAtom(const char *name); + QByteArray atomName(xcb_atom_t atom); + + bool hasXFixes() const { return m_hasXFixes; } + bool hasXShape() const { return m_hasXhape; } + bool hasXRandr() const { return m_hasXRandr; } + bool hasInputShape() const { return m_hasInputShape; } + bool hasXKB() const { return m_hasXkb; } + bool hasXRender(int major = -1, int minor = -1) const { + if (m_hasXRender && major != -1 && minor != -1) + return m_xrenderVersion >= qMakePair(major, minor); + + return m_hasXRender; + } + bool hasXInput2() const { return m_xi2Enabled; } + bool hasShm() const { return m_hasShm; } + bool hasShmFd() const { return m_hasShmFd; } + bool hasXSync() const { return m_hasXSync; } + bool hasXinerama() const { return m_hasXinerama; } + +#if QT_CONFIG(xcb_xinput) + bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; } + bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; } + bool isXIEvent(xcb_generic_event_t *event) const; + bool isXIType(xcb_generic_event_t *event, uint16_t type) const; +#endif + + bool isXFixesType(uint responseType, int eventType) const; + bool isXRandrType(uint responseType, int eventType) const; + bool isXkbType(uint responseType) const; // https://bugs.freedesktop.org/show_bug.cgi?id=51295 + +protected: + void initializeShm(); + void initializeXFixes(); + void initializeXRender(); + void initializeXRandr(); + void initializeXinerama(); + void initializeXShape(); + void initializeXKB(); + void initializeXSync(); +#if QT_CONFIG(xcb_xinput) + void initializeXInput2(); +#endif + +private: +#if QT_CONFIG(xcb_xlib) + void *m_xlibDisplay = nullptr; +#endif + QByteArray m_displayName; + xcb_connection_t *m_xcbConnection = nullptr; + int m_primaryScreenNumber = 0; + const xcb_setup_t *m_setup = nullptr; + QXcbAtom m_xcbAtom; + + bool m_hasXFixes = false; + bool m_hasXinerama = false; + bool m_hasXhape = false; + bool m_hasInputShape; + bool m_hasXRandr = false; + bool m_hasXkb = false; + bool m_hasXRender = false; + bool m_hasShm = false; + bool m_hasShmFd = false; + bool m_hasXSync = false; + + QPair<int, int> m_xrenderVersion; + + bool m_xi2Enabled = false; +#if QT_CONFIG(xcb_xinput) + int m_xi2Minor = -1; + int m_xiOpCode = -1; + uint32_t m_xinputFirstEvent = 0; +#endif + + uint32_t m_xfixesFirstEvent = 0; + uint32_t m_xrandrFirstEvent = 0; + uint32_t m_xkbFirstEvent = 0; +}; + +#define Q_XCB_REPLY_CONNECTION_ARG(connection, ...) connection + +struct QStdFreeDeleter { + void operator()(void *p) const Q_DECL_NOTHROW { return std::free(p); } +}; + +#define Q_XCB_REPLY(call, ...) \ + std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \ + call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call(__VA_ARGS__), nullptr) \ + ) + +#define Q_XCB_REPLY_UNCHECKED(call, ...) \ + std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \ + call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call##_unchecked(__VA_ARGS__), nullptr) \ + ) + +QT_END_NAMESPACE + +#endif // QXCBBASICCONNECTION_H diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index a3befc7384..04ddd3c98c 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -51,31 +51,6 @@ using qt_xcb_input_device_event_t = xcb_input_button_press_event_t; -void QXcbConnection::initializeXInput2() -{ - const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_input_id); - if (!reply || !reply->present) { - qCDebug(lcQpaXInput, "XInput extension is not present on the X server"); - return; - } - - m_xiOpCode = reply->major_opcode; - xinput_first_event = reply->first_event; - - auto xinput_query = Q_XCB_REPLY(xcb_input_xi_query_version, m_connection, 2, 2); - - if (!xinput_query || xinput_query->major_version != 2) { - qCWarning(lcQpaXInput, "X server does not support XInput 2"); - } else { - qCDebug(lcQpaXInput, "Using XInput version %d.%d", - xinput_query->major_version, xinput_query->minor_version); - m_xi2Minor = xinput_query->minor_version; - m_xi2Enabled = true; - xi2SetupDevices(); - xi2SelectStateEvents(); - } -} - struct qt_xcb_input_event_mask_t { xcb_input_event_mask_t header; uint32_t mask; @@ -91,7 +66,7 @@ void QXcbConnection::xi2SelectStateEvents() xiEventMask.mask = XCB_INPUT_XI_EVENT_MASK_HIERARCHY; xiEventMask.mask |= XCB_INPUT_XI_EVENT_MASK_DEVICE_CHANGED; xiEventMask.mask |= XCB_INPUT_XI_EVENT_MASK_PROPERTY; - xcb_input_xi_select_events(m_connection, rootWindow(), 1, &xiEventMask.header); + xcb_input_xi_select_events(xcb_connection(), rootWindow(), 1, &xiEventMask.header); } void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window) @@ -117,8 +92,8 @@ void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window) mask.header.mask_len = 1; mask.mask = bitMask; xcb_void_cookie_t cookie = - xcb_input_xi_select_events_checked(m_connection, window, 1, &mask.header); - xcb_generic_error_t *error = xcb_request_check(m_connection, cookie); + xcb_input_xi_select_events_checked(xcb_connection(), window, 1, &mask.header); + xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie); if (error) { qCDebug(lcQpaXInput, "failed to select events, window %x, error code %d", window, error->error_code); free(error); @@ -310,7 +285,7 @@ void QXcbConnection::xi2SetupDevices() m_touchDevices.clear(); m_xiMasterPointerIds.clear(); - auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, m_connection, XCB_INPUT_DEVICE_ALL); + auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), XCB_INPUT_DEVICE_ALL); if (!reply) { qCDebug(lcQpaXInputDevices) << "failed to query devices"; return; @@ -387,8 +362,8 @@ void QXcbConnection::xi2SelectDeviceEventsCompatibility(xcb_window_t window) xiMask.mask = mask; xcb_void_cookie_t cookie = - xcb_input_xi_select_events_checked(m_connection, window, 1, &xiMask.header); - xcb_generic_error_t *error = xcb_request_check(m_connection, cookie); + xcb_input_xi_select_events_checked(xcb_connection(), window, 1, &xiMask.header); + xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie); if (error) { qCDebug(lcQpaXInput, "failed to select events, window %x, error code %d", window, error->error_code); free(error); @@ -413,7 +388,7 @@ void QXcbConnection::xi2SelectDeviceEventsCompatibility(xcb_window_t window) xiEventMask[i].header.mask_len = 1; xiEventMask[i].mask = mask; } - xcb_input_xi_select_events(m_connection, window, nrTablets, &(xiEventMask.data()->header)); + xcb_input_xi_select_events(xcb_connection(), window, nrTablets, &(xiEventMask.data()->header)); } #endif @@ -430,7 +405,7 @@ void QXcbConnection::xi2SelectDeviceEventsCompatibility(xcb_window_t window) xiEventMask[i].mask = mask; i++; } - xcb_input_xi_select_events(m_connection, window, i, &(xiEventMask.data()->header)); + xcb_input_xi_select_events(xcb_connection(), window, i, &(xiEventMask.data()->header)); } } @@ -633,7 +608,7 @@ bool QXcbConnection::xi2MouseEventsDisabled() const static bool xi2MouseDisabled = qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE"); // FIXME: Don't use XInput2 mouse events when Xinerama extension // is enabled, because it causes problems with multi-monitor setup. - return xi2MouseDisabled || has_xinerama_extension; + return xi2MouseDisabled || hasXinerama(); } bool QXcbConnection::isTouchScreen(int id) @@ -745,7 +720,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo // Touches must be accepted when we are grabbing touch events. Otherwise the entire sequence // will get replayed when the grab ends. if (m_xiGrab) { - xcb_input_xi_allow_events(m_connection, XCB_CURRENT_TIME, xiDeviceEvent->deviceid, + xcb_input_xi_allow_events(xcb_connection(), XCB_CURRENT_TIME, xiDeviceEvent->deviceid, XCB_INPUT_EVENT_MODE_ACCEPT_TOUCH, xiDeviceEvent->detail, xiDeviceEvent->event); } @@ -771,7 +746,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo xiDeviceEvent->detail == m_startSystemMoveResizeInfo.pointid) { QXcbWindow *window = platformWindowFromId(m_startSystemMoveResizeInfo.window); if (window) { - xcb_input_xi_allow_events(m_connection, XCB_CURRENT_TIME, xiDeviceEvent->deviceid, + xcb_input_xi_allow_events(xcb_connection(), XCB_CURRENT_TIME, xiDeviceEvent->deviceid, XCB_INPUT_EVENT_MODE_REJECT_TOUCH, xiDeviceEvent->detail, xiDeviceEvent->event); window->doStartSystemMoveResize(QPoint(x, y), m_startSystemMoveResizeInfo.corner); @@ -850,10 +825,10 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab) for (int id : m_xiMasterPointerIds) { xcb_generic_error_t *error = nullptr; - auto cookie = xcb_input_xi_grab_device(m_connection, w, XCB_CURRENT_TIME, XCB_CURSOR_NONE, id, + auto cookie = xcb_input_xi_grab_device(xcb_connection(), w, XCB_CURRENT_TIME, XCB_CURSOR_NONE, id, XCB_INPUT_GRAB_MODE_22_ASYNC, XCB_INPUT_GRAB_MODE_22_ASYNC, false, 1, &mask); - auto *reply = xcb_input_xi_grab_device_reply(m_connection, cookie, &error); + auto *reply = xcb_input_xi_grab_device_reply(xcb_connection(), cookie, &error); if (error) { qCDebug(lcQpaXInput, "failed to grab events for device %d on window %x" "(error code %d)", id, w, error->error_code); @@ -867,8 +842,8 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab) } } else { // ungrab for (int id : m_xiMasterPointerIds) { - auto cookie = xcb_input_xi_ungrab_device_checked(m_connection, XCB_CURRENT_TIME, id); - xcb_generic_error_t *error = xcb_request_check(m_connection, cookie); + auto cookie = xcb_input_xi_ungrab_device_checked(xcb_connection(), XCB_CURRENT_TIME, id); + xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie); if (error) { qCDebug(lcQpaXInput, "XIUngrabDevice failed - id: %d (error code %d)", id, error->error_code); free(error); @@ -911,7 +886,7 @@ void QXcbConnection::xi2HandleDeviceChangedEvent(void *event) auto *xiEvent = reinterpret_cast<xcb_input_device_changed_event_t *>(event); switch (xiEvent->reason) { case XCB_INPUT_CHANGE_REASON_DEVICE_CHANGE: { - auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, m_connection, xiEvent->sourceid); + auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), xiEvent->sourceid); if (!reply || reply->num_infos <= 0) return; auto it = xcb_input_xi_query_device_infos_iterator(reply.get()); @@ -931,7 +906,7 @@ void QXcbConnection::xi2HandleDeviceChangedEvent(void *event) void QXcbConnection::xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice) { - auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, m_connection, scrollingDevice.deviceId); + auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), scrollingDevice.deviceId); if (!reply || reply->num_infos <= 0) { qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", scrollingDevice.deviceId); return; @@ -1184,7 +1159,7 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD _WACSER_COUNT }; - auto reply = Q_XCB_REPLY(xcb_input_xi_get_property, m_connection, tabletData->deviceId, 0, + auto reply = Q_XCB_REPLY(xcb_input_xi_get_property, xcb_connection(), tabletData->deviceId, 0, ev->property, XCB_GET_PROPERTY_TYPE_ANY, 0, 100); if (reply) { if (reply->type == atom(QXcbAtom::INTEGER) && reply->format == 32 && reply->num_items == _WACSER_COUNT) { diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.cpp b/src/plugins/platforms/xcb/qxcbeventqueue.cpp index 20589fc550..862f68764b 100644 --- a/src/plugins/platforms/xcb/qxcbeventqueue.cpp +++ b/src/plugins/platforms/xcb/qxcbeventqueue.cpp @@ -351,7 +351,7 @@ void QXcbEventQueue::sendCloseConnectionEvent() const xcb_connection_t *c = m_connection->xcb_connection(); const xcb_window_t window = xcb_generate_id(c); - xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_connection->m_setup); + xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_connection->setup()); xcb_screen_t *screen = it.data; xcb_create_window(c, XCB_COPY_FROM_PARENT, window, screen->root, diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index ad06f24c14..c5dc7b21ad 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -1190,9 +1190,10 @@ QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection) #if QT_CONFIG(xkb) core_device_id = 0; if (connection->hasXKB()) { + selectEvents(); core_device_id = xkb_x11_get_core_keyboard_device_id(xcb_connection()); if (core_device_id == -1) { - qWarning("Qt: couldn't get core keyboard device info"); + qCWarning(lcQpaXcb, "failed to get core keyboard device info"); return; } } else { @@ -1210,6 +1211,42 @@ QXcbKeyboard::~QXcbKeyboard() xcb_key_symbols_free(m_key_symbols); } +void QXcbKeyboard::selectEvents() +{ +#if QT_CONFIG(xkb) + const uint16_t required_map_parts = (XCB_XKB_MAP_PART_KEY_TYPES | + XCB_XKB_MAP_PART_KEY_SYMS | + XCB_XKB_MAP_PART_MODIFIER_MAP | + XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS | + XCB_XKB_MAP_PART_KEY_ACTIONS | + XCB_XKB_MAP_PART_KEY_BEHAVIORS | + XCB_XKB_MAP_PART_VIRTUAL_MODS | + XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP); + + const uint16_t required_events = (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY | + XCB_XKB_EVENT_TYPE_MAP_NOTIFY | + XCB_XKB_EVENT_TYPE_STATE_NOTIFY); + + // XKB events are reported to all interested clients without regard + // to the current keyboard input focus or grab state + xcb_void_cookie_t select = xcb_xkb_select_events_checked( + xcb_connection(), + XCB_XKB_ID_USE_CORE_KBD, + required_events, + 0, + required_events, + required_map_parts, + required_map_parts, + 0); + + xcb_generic_error_t *error = xcb_request_check(xcb_connection(), select); + if (error) { + free(error); + qCWarning(lcQpaXcb, "failed to select notify events from XKB"); + } +#endif +} + void QXcbKeyboard::updateVModMapping() { #if QT_CONFIG(xkb) diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h index 95915fb2e6..f8490592b7 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.h +++ b/src/plugins/platforms/xcb/qxcbkeyboard.h @@ -43,6 +43,11 @@ #include "qxcbobject.h" #include <xcb/xcb_keysyms.h> +#if QT_CONFIG(xkb) +#define explicit dont_use_cxx_explicit +#include <xcb/xkb.h> +#undef explicit +#endif #include <xkbcommon/xkbcommon.h> #if QT_CONFIG(xkb) @@ -62,6 +67,8 @@ public: ~QXcbKeyboard(); + void selectEvents(); + void handleKeyPressEvent(const xcb_key_press_event_t *event); void handleKeyReleaseEvent(const xcb_key_release_event_t *event); diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro index a2c56a3dcf..4c646d42c6 100644 --- a/src/plugins/platforms/xcb/xcb-plugin.pro +++ b/src/plugins/platforms/xcb/xcb-plugin.pro @@ -8,6 +8,7 @@ macos: CONFIG += no_app_extension_api_only SOURCES = \ qxcbmain.cpp + OTHER_FILES += xcb.json README PLUGIN_TYPE = platforms diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro index a747b25c88..c0704e4c18 100644 --- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro +++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro @@ -31,7 +31,9 @@ SOURCES = \ qxcbxsettings.cpp \ qxcbsystemtraytracker.cpp \ qxcbeventqueue.cpp \ - qxcbeventdispatcher.cpp + qxcbeventdispatcher.cpp \ + qxcbconnection_basic.cpp \ + qxcbatom.cpp HEADERS = \ qxcbclipboard.h \ @@ -51,7 +53,9 @@ HEADERS = \ qxcbsystemtraytracker.h \ qxcbxkbcommon.h \ qxcbeventqueue.h \ - qxcbeventdispatcher.h + qxcbeventdispatcher.h \ + qxcbconnection_basic.h \ + qxcbatom.h qtConfig(draganddrop) { SOURCES += qxcbdrag.cpp |