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>
bb10
Gatis Paeglis 2018-09-22 13:14:23 +02:00
parent 5fe2ed48d6
commit d67214302f
14 changed files with 1317 additions and 1027 deletions

View File

@ -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);
}

View File

@ -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

View File

@ -106,7 +106,7 @@ public:
void put(xcb_drawable_t dst, const QRegion &region, const QPoint &offset);
void preparePaint(const QRegion &region);
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 &region)
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);

View File

@ -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:

View File

@ -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)
void QXcbConnection::selectXRandrEvents()
{
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)
{
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) {

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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,

View File

@ -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)

View File

@ -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);

View File

@ -8,6 +8,7 @@ macos: CONFIG += no_app_extension_api_only
SOURCES = \
qxcbmain.cpp
OTHER_FILES += xcb.json README
PLUGIN_TYPE = platforms

View File

@ -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