summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r--src/plugins/platforms/xcb/README27
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp4
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp8
-rw-r--r--src/plugins/platforms/xcb/qxcbatom.cpp277
-rw-r--r--src/plugins/platforms/xcb/qxcbatom.h257
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.cpp225
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.h33
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp124
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp1932
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h483
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.cpp437
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.h175
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_screens.cpp414
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp584
-rw-r--r--src/plugins/platforms/xcb/qxcbcursor.cpp6
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp336
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.h15
-rw-r--r--src/plugins/platforms/xcb/qxcbeventdispatcher.cpp162
-rw-r--r--src/plugins/platforms/xcb/qxcbeventdispatcher.h117
-rw-r--r--src/plugins/platforms/xcb/qxcbeventqueue.cpp383
-rw-r--r--src/plugins/platforms/xcb/qxcbeventqueue.h162
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp58
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp908
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.h12
-rw-r--r--src/plugins/platforms/xcb/qxcbmain.cpp12
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.cpp72
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.h15
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp28
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.h6
-rw-r--r--src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp41
-rw-r--r--src/plugins/platforms/xcb/qxcbsystemtraytracker.h4
-rw-r--r--src/plugins/platforms/xcb/qxcbvulkaninstance.cpp29
-rw-r--r--src/plugins/platforms/xcb/qxcbvulkaninstance.h3
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp550
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.h31
-rw-r--r--src/plugins/platforms/xcb/xcb-plugin.pro1
-rw-r--r--src/plugins/platforms/xcb/xcb-static/xcb-static.pro5
-rw-r--r--src/plugins/platforms/xcb/xcb_qpa_lib.pro25
40 files changed, 4269 insertions, 3696 deletions
diff --git a/src/plugins/platforms/xcb/README b/src/plugins/platforms/xcb/README
index 8308db46dc..5f238ab261 100644
--- a/src/plugins/platforms/xcb/README
+++ b/src/plugins/platforms/xcb/README
@@ -1,32 +1,7 @@
-Requires libxcb >= 1.5.
-
-PACKAGE DEPENDENCIES
-
-Required packages:
-libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm1 libxcb-icccm1-dev libxcb-sync0 libxcb-sync0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-glx0-dev libxcb-xinerama0-dev
-
-On Ubuntu 11.10 icccm1 is replaced by icccm4 and xcb-render-util is not available:
-libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-glx0-dev libxcb-xinerama0-dev
-The packages for xcb-render-util can be installed manually from http://packages.ubuntu.com/natty/libxcb-render-util0 and http://packages.ubuntu.com/natty/libxcb-render-util0-dev
-
-On Ubuntu 12.04 icccm1 is replaced by icccm4 and xcb-render-util can be installed automatically:
-libxcb1 libxcb1-dev libx11-xcb1 libx11-xcb-dev libxcb-keysyms1 libxcb-keysyms1-dev libxcb-image0 libxcb-image0-dev libxcb-shm0 libxcb-shm0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-sync0 libxcb-sync0-dev libxcb-xfixes0-dev libxrender-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0 libxcb-render-util0-dev libxcb-glx0-dev libxcb-xinerama0-dev
-
-
-On Fedora, the following packages are required:
-libxcb libxcb-devel libXrender libXrender-devel xcb-util-wm xcb-util-wm-devel xcb-util xcb-util-devel xcb-util-image xcb-util-image-devel xcb-util-keysyms xcb-util-keysyms-devel
+Requires libxcb >= 1.9.1.
REDUCING RUNTIME DEPENDENCIES
The '-qt-xcb' configure option can be used to get rid of most xcb- dependencies. Only libxcb will
still be linked dynamically, since it will be most likely be pulled in via other dependencies anyway.
This should allow for binaries that are portable across most modern Linux distributions.
-
-PACKAGE VERSION REQUIREMENTS
-
-When using touch input via XInput 2.2 or higher, there is a potential issue on systems that ship with
-a libXi older than 1.7.5. This is because XIAllowTouchEvents can deadlock with libXi 1.7.4 and earlier.
-When touch events are never received, this is not an issue, so plain mouse/keyboard systems are not affected.
-Qt versions before 5.8 attempted to recognize this scenario based on the pkg-config package version and skip
-the call. This has been removed starting from 5.8 since relying on pkg-config package versions is unsafe given
-that Qt must also support systems with limited or incomplete pkg-config setups.
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp
index a7641baea1..b751aaf8a3 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp
@@ -163,9 +163,9 @@ bool QXcbGlxIntegration::handleXcbEvent(xcb_generic_event_t *event, uint respons
// Unlock the display before calling the native event filter
XUnlockDisplay(xdisplay);
locked = false;
- QByteArray genericEventFilterType = m_connection->nativeInterface()->genericEventFilterType();
+ auto eventType = m_connection->nativeInterface()->nativeEventType();
long result = 0;
- handled = dispatcher->filterNativeEvent(genericEventFilterType, &ev, &result);
+ handled = dispatcher->filterNativeEvent(eventType, &ev, &result);
}
#endif
}
diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
index 8b63e5431d..a3e6cedecd 100644
--- a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
+++ b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
@@ -226,7 +226,9 @@ public:
QXRenderGlyphCache(QXcbX11Info x, QFontEngine::GlyphFormat format, const QTransform &matrix);
~QXRenderGlyphCache();
- bool addGlyphs(const QTextItemInt &ti, QVarLengthArray<glyph_t> glyphs, QVarLengthArray<QFixedPoint> positions);
+ bool addGlyphs(const QTextItemInt &ti,
+ const QVarLengthArray<glyph_t> &glyphs,
+ const QVarLengthArray<QFixedPoint> &positions);
bool draw(Drawable src, Drawable dst, const QTransform &matrix, const QTextItemInt &ti);
inline GlyphSet glyphSet();
@@ -2608,7 +2610,9 @@ QXRenderGlyphCache::~QXRenderGlyphCache()
XRenderFreeGlyphSet(xinfo.display(), gset);
}
-bool QXRenderGlyphCache::addGlyphs(const QTextItemInt &ti, QVarLengthArray<glyph_t> glyphs, QVarLengthArray<QFixedPoint> positions)
+bool QXRenderGlyphCache::addGlyphs(const QTextItemInt &ti,
+ const QVarLengthArray<glyph_t> &glyphs,
+ const QVarLengthArray<QFixedPoint> &positions)
{
Q_ASSERT(ti.fontEngine->type() == QFontEngine::Freetype);
diff --git a/src/plugins/platforms/xcb/qxcbatom.cpp b/src/plugins/platforms/xcb/qxcbatom.cpp
new file mode 100644
index 0000000000..ecb73cb90b
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbatom.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** 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"
+
+ // 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..233d2eadb7
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbatom.h
@@ -0,0 +1,257 @@
+/****************************************************************************
+**
+** 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,
+
+ // 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 8ae9d9899e..c8c806749f 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
@@ -45,6 +45,16 @@
#include <xcb/shm.h>
#include <xcb/xcb_image.h>
+#if QT_CONFIG(xcb_render)
+#include <xcb/render.h>
+// 'template' is used as a function argument name in xcb_renderutil.h
+#define template template_param
+// extern "C" is missing too
+extern "C" {
+#include <xcb/xcb_renderutil.h>
+}
+#undef template
+#endif
#include <sys/ipc.h>
#include <sys/shm.h>
@@ -76,6 +86,7 @@ class QXcbBackingStoreImage : public QXcbObject
{
public:
QXcbBackingStoreImage(QXcbBackingStore *backingStore, const QSize &size);
+ QXcbBackingStoreImage(QXcbBackingStore *backingStore, const QSize &size, uint depth, QImage::Format format);
~QXcbBackingStoreImage() { destroy(true); }
void resize(const QSize &size);
@@ -95,10 +106,12 @@ 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:
+ void init(const QSize &size, uint depth, QImage::Format format);
+
void createShmSegment(size_t segmentSize);
void destroyShmSegment();
void destroy(bool destroyShm);
@@ -109,8 +122,8 @@ private:
void setClip(const QRegion &region);
xcb_shm_segment_info_t m_shm_info;
- QXcbBackingStore *m_backingStore = nullptr;
size_t m_segmentSize = 0;
+ QXcbBackingStore *m_backingStore = nullptr;
xcb_image_t *m_xcb_image = nullptr;
@@ -185,10 +198,23 @@ QXcbBackingStoreImage::QXcbBackingStoreImage(QXcbBackingStore *backingStore, con
, m_backingStore(backingStore)
{
auto window = static_cast<QXcbWindow *>(m_backingStore->window()->handle());
- m_xcb_format = connection()->formatForDepth(window->depth());
+ init(size, window->depth(), window->imageFormat());
+}
+
+QXcbBackingStoreImage::QXcbBackingStoreImage(QXcbBackingStore *backingStore, const QSize &size,
+ uint depth, QImage::Format format)
+ : QXcbObject(backingStore->connection())
+ , m_backingStore(backingStore)
+{
+ init(size, depth, format);
+}
+
+void QXcbBackingStoreImage::init(const QSize &size, uint depth, QImage::Format format)
+{
+ m_xcb_format = connection()->formatForDepth(depth);
Q_ASSERT(m_xcb_format);
- m_qimage_format = window->imageFormat();
+ m_qimage_format = format;
m_hasAlpha = QImage::toPixelFormat(m_qimage_format).alphaUsage() == QPixelFormat::UsesAlpha;
if (!m_hasAlpha)
m_qimage_format = qt_maybeAlphaVersionWithSameDepth(m_qimage_format);
@@ -380,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);
@@ -403,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);
@@ -740,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);
@@ -843,7 +869,7 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
return;
}
- m_image->put(platformWindow->xcb_window(), clipped, offset);
+ render(platformWindow->xcb_window(), clipped, offset);
if (platformWindow->needsSync())
platformWindow->updateSyncRequestCounter();
@@ -851,6 +877,11 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion &region, const QPoin
xcb_flush(xcb_connection());
}
+void QXcbBackingStore::render(xcb_window_t window, const QRegion &region, const QPoint &offset)
+{
+ m_image->put(window, region, offset);
+}
+
#ifndef QT_NO_OPENGL
void QXcbBackingStore::composeAndFlush(QWindow *window, const QRegion &region, const QPoint &offset,
QPlatformTextureList *textures,
@@ -884,6 +915,11 @@ void QXcbBackingStore::resize(const QSize &size, const QRegion &)
}
QXcbWindow* win = static_cast<QXcbWindow *>(pw);
+ recreateImage(win, size);
+}
+
+void QXcbBackingStore::recreateImage(QXcbWindow *win, const QSize &size)
+{
if (m_image)
m_image->resize(size);
else
@@ -904,4 +940,167 @@ bool QXcbBackingStore::scroll(const QRegion &area, int dx, int dy)
return false;
}
+QXcbSystemTrayBackingStore::QXcbSystemTrayBackingStore(QWindow *window)
+ : QXcbBackingStore(window)
+{
+ // We need three different behaviors depending on whether the X11 visual
+ // for the system tray supports an alpha channel, i.e. is 32 bits, and
+ // whether XRender can be used:
+ // 1) if the visual has an alpha channel, then render the window's buffer
+ // directly to the X11 window as usual
+ // 2) else if XRender can be used, then render the window's buffer to Pixmap,
+ // then render Pixmap's contents to the cleared X11 window with
+ // xcb_render_composite()
+ // 3) else grab the X11 window's content and paint it first each time as a
+ // background before rendering the window's buffer to the X11 window
+
+ auto *platformWindow = static_cast<QXcbWindow *>(window->handle());
+ quint8 depth = connection()->primaryScreen()->depthOfVisual(platformWindow->visualId());
+
+ if (depth != 32) {
+ platformWindow->setParentRelativeBackPixmap();
+#if QT_CONFIG(xcb_render)
+ initXRenderMode();
+#endif
+ m_useGrabbedBackgound = !m_usingXRenderMode;
+ }
+}
+
+QXcbSystemTrayBackingStore::~QXcbSystemTrayBackingStore()
+{
+#if QT_CONFIG(xcb_render)
+ if (m_xrenderPicture) {
+ xcb_render_free_picture(xcb_connection(), m_xrenderPicture);
+ m_xrenderPicture = XCB_NONE;
+ }
+ if (m_xrenderPixmap) {
+ xcb_free_pixmap(xcb_connection(), m_xrenderPixmap);
+ m_xrenderPixmap = XCB_NONE;
+ }
+ if (m_windowPicture) {
+ xcb_render_free_picture(xcb_connection(), m_windowPicture);
+ m_windowPicture = XCB_NONE;
+ }
+#endif // QT_CONFIG(xcb_render)
+}
+
+void QXcbSystemTrayBackingStore::beginPaint(const QRegion &region)
+{
+ QXcbBackingStore::beginPaint(region);
+
+ if (m_useGrabbedBackgound) {
+ QPainter p(paintDevice());
+ p.setCompositionMode(QPainter::CompositionMode_Source);
+ for (const QRect &rect: region)
+ p.drawPixmap(rect, m_grabbedBackground, rect);
+ }
+}
+
+void QXcbSystemTrayBackingStore::render(xcb_window_t window, const QRegion &region, const QPoint &offset)
+{
+ if (!m_usingXRenderMode) {
+ QXcbBackingStore::render(window, region, offset);
+ return;
+ }
+
+#if QT_CONFIG(xcb_render)
+ m_image->put(m_xrenderPixmap, region, offset);
+ const QRect bounds = region.boundingRect();
+ const QPoint target = bounds.topLeft();
+ const QRect source = bounds.translated(offset);
+ xcb_clear_area(xcb_connection(), false, window,
+ target.x(), target.y(), source.width(), source.height());
+ xcb_render_composite(xcb_connection(), XCB_RENDER_PICT_OP_OVER,
+ m_xrenderPicture, 0, m_windowPicture,
+ target.x(), target.y(), 0, 0, target.x(), target.y(),
+ source.width(), source.height());
+#endif // QT_CONFIG(xcb_render)
+}
+
+void QXcbSystemTrayBackingStore::recreateImage(QXcbWindow *win, const QSize &size)
+{
+ if (!m_usingXRenderMode) {
+ QXcbBackingStore::recreateImage(win, size);
+
+ if (m_useGrabbedBackgound) {
+ xcb_clear_area(xcb_connection(), false, win->xcb_window(),
+ 0, 0, size.width(), size.height());
+ m_grabbedBackground = win->xcbScreen()->grabWindow(win->winId(), 0, 0,
+ size.width(), size.height());
+ }
+ return;
+ }
+
+#if QT_CONFIG(xcb_render)
+ if (m_xrenderPicture) {
+ xcb_render_free_picture(xcb_connection(), m_xrenderPicture);
+ m_xrenderPicture = XCB_NONE;
+ }
+ if (m_xrenderPixmap) {
+ xcb_free_pixmap(xcb_connection(), m_xrenderPixmap);
+ m_xrenderPixmap = XCB_NONE;
+ }
+
+ QXcbScreen *screen = win->xcbScreen();
+
+ m_xrenderPixmap = xcb_generate_id(xcb_connection());
+ xcb_create_pixmap(xcb_connection(), 32, m_xrenderPixmap, screen->root(), size.width(), size.height());
+
+ m_xrenderPicture = xcb_generate_id(xcb_connection());
+ xcb_render_create_picture(xcb_connection(), m_xrenderPicture, m_xrenderPixmap, m_xrenderPictFormat, 0, 0);
+
+ // XRender expects premultiplied alpha
+ if (m_image)
+ m_image->resize(size);
+ else
+ m_image = new QXcbBackingStoreImage(this, size, 32, QImage::Format_ARGB32_Premultiplied);
+#endif // QT_CONFIG(xcb_render)
+}
+
+#if QT_CONFIG(xcb_render)
+void QXcbSystemTrayBackingStore::initXRenderMode()
+{
+ if (!connection()->hasXRender())
+ return;
+
+ xcb_connection_t *conn = xcb_connection();
+ auto formatsReply = Q_XCB_REPLY(xcb_render_query_pict_formats, conn);
+
+ if (!formatsReply) {
+ qWarning("QXcbSystemTrayBackingStore: xcb_render_query_pict_formats() failed");
+ return;
+ }
+
+ xcb_render_pictforminfo_t *fmt = xcb_render_util_find_standard_format(formatsReply.get(),
+ XCB_PICT_STANDARD_ARGB_32);
+ if (!fmt) {
+ qWarning("QXcbSystemTrayBackingStore: Failed to find format PICT_STANDARD_ARGB_32");
+ return;
+ }
+
+ m_xrenderPictFormat = fmt->id;
+
+ auto *platformWindow = static_cast<QXcbWindow *>(window()->handle());
+ xcb_render_pictvisual_t *vfmt = xcb_render_util_find_visual_format(formatsReply.get(), platformWindow->visualId());
+
+ if (!vfmt) {
+ qWarning("QXcbSystemTrayBackingStore: Failed to find format for visual %x", platformWindow->visualId());
+ return;
+ }
+
+ m_windowPicture = xcb_generate_id(conn);
+ xcb_void_cookie_t cookie =
+ xcb_render_create_picture_checked(conn, m_windowPicture, platformWindow->xcb_window(), vfmt->format, 0, 0);
+ xcb_generic_error_t *error = xcb_request_check(conn, cookie);
+ if (error) {
+ qWarning("QXcbSystemTrayBackingStore: Failed to create Picture with format %x for window %x, error code %d",
+ vfmt->format, platformWindow->xcb_window(), error->error_code);
+ free(error);
+ return;
+ }
+
+ m_usingXRenderMode = true;
+}
+#endif // QT_CONFIG(xcb_render)
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.h b/src/plugins/platforms/xcb/qxcbbackingstore.h
index 734de1f7d7..b91e5c7dc2 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.h
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.h
@@ -74,15 +74,44 @@ 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);
-private:
+protected:
+ virtual void render(xcb_window_t window, const QRegion &region, const QPoint &offset);
+ virtual void recreateImage(QXcbWindow *win, const QSize &size);
+
QXcbBackingStoreImage *m_image = nullptr;
QStack<QRegion> m_paintRegions;
QImage m_rgbImage;
};
+class QXcbSystemTrayBackingStore : public QXcbBackingStore
+{
+public:
+ QXcbSystemTrayBackingStore(QWindow *window);
+ ~QXcbSystemTrayBackingStore();
+
+ void beginPaint(const QRegion &) override;
+
+protected:
+ void render(xcb_window_t window, const QRegion &region, const QPoint &offset) override;
+ void recreateImage(QXcbWindow *win, const QSize &size) override;
+
+private:
+#if QT_CONFIG(xcb_render)
+ void initXRenderMode();
+
+ xcb_pixmap_t m_xrenderPixmap = XCB_NONE;
+ xcb_render_picture_t m_xrenderPicture = XCB_NONE;
+ xcb_render_pictformat_t m_xrenderPictFormat = XCB_NONE;
+ xcb_render_picture_t m_windowPicture = XCB_NONE;
+#endif
+ bool m_usingXRenderMode = false;
+ bool m_useGrabbedBackgound = false;
+ QPixmap m_grabbedBackground;
+};
+
QT_END_NAMESPACE
#endif
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp
index b091928e8c..3fd14a659e 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp
@@ -42,16 +42,13 @@
#include "qxcbconnection.h"
#include "qxcbscreen.h"
#include "qxcbmime.h"
+#include "qxcbwindow.h"
#include <private/qguiapplication_p.h>
#include <QElapsedTimer>
#include <QtCore/QDebug>
-#define class class_name // Workaround XCB-ICCCM 3.8 breakage
-#include <xcb/xcb_icccm.h>
-#undef class
-
QT_BEGIN_NAMESPACE
#ifndef QT_NO_CLIPBOARD
@@ -156,6 +153,7 @@ private:
QByteArray format_atoms;
};
+namespace {
class INCRTransaction;
typedef QMap<xcb_window_t,INCRTransaction*> TransactionMap;
static TransactionMap *transactions = 0;
@@ -262,6 +260,7 @@ private:
uint offset;
int abort_timer;
};
+} // unnamed namespace
const int QXcbClipboard::clipboard_timeout = 5000;
@@ -276,18 +275,6 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c)
m_timestamp[QClipboard::Selection] = XCB_CURRENT_TIME;
m_owner = connection()->getQtSelectionOwner();
-#ifndef QT_NO_DEBUG
- QByteArray ba("Qt clipboard window");
- xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_owner,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData());
-#endif
-
if (connection()->hasXFixes()) {
const uint32_t mask = XCB_XFIXES_SELECTION_EVENT_MASK_SET_SELECTION_OWNER |
XCB_XFIXES_SELECTION_EVENT_MASK_SELECTION_WINDOW_DESTROY |
@@ -314,7 +301,7 @@ QXcbClipboard::~QXcbClipboard()
connection()->sync();
// waiting until the clipboard manager fetches the content.
- if (!waitForClipboardEvent(m_owner, XCB_SELECTION_NOTIFY, clipboard_timeout, true)) {
+ if (!waitForClipboardEvent(m_owner, XCB_SELECTION_NOTIFY, true)) {
qWarning("QXcbClipboard: Unable to receive an event from the "
"clipboard manager in a reasonable time");
}
@@ -467,17 +454,9 @@ xcb_window_t QXcbClipboard::requestor() const
platformScreen->screen()->root_visual, // visual
0, // value mask
0); // value list
-#ifndef QT_NO_DEBUG
- QByteArray ba("Qt clipboard requestor window");
- xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- window,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData());
-#endif
+
+ QXcbWindow::setWindowTitle(connection(), window,
+ QStringLiteral("Qt Clipboard Requestor Window"));
uint32_t mask = XCB_EVENT_MASK_PROPERTY_CHANGE;
xcb_change_window_attributes(xcb_connection(), window, XCB_CW_EVENT_MASK, &mask);
@@ -600,7 +579,7 @@ void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req)
return;
}
- Q_DECLARE_XCB_EVENT(event, xcb_selection_notify_event_t);
+ q_padded_xcb_event<xcb_selection_notify_event_t> event = {};
event.response_type = XCB_SELECTION_NOTIFY;
event.requestor = req->requestor;
event.selection = req->selection;
@@ -824,73 +803,44 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
return ok;
}
-
-namespace
+xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t window, int type, bool checkManager)
{
- class Notify {
- public:
- Notify(xcb_window_t win, int t)
- : window(win), type(t) {}
- xcb_window_t window;
- int type;
- bool checkEvent(xcb_generic_event_t *event) const {
- if (!event)
- return false;
- int t = event->response_type & 0x7f;
- if (t != type)
+ QElapsedTimer timer;
+ timer.start();
+ QXcbEventQueue *queue = connection()->eventQueue();
+ do {
+ auto e = queue->peek([window, type](xcb_generic_event_t *event, int eventType) {
+ if (eventType != type)
return false;
- if (t == XCB_PROPERTY_NOTIFY) {
- xcb_property_notify_event_t *pn = (xcb_property_notify_event_t *)event;
- if (pn->window == window)
- return true;
- } else if (t == XCB_SELECTION_NOTIFY) {
- xcb_selection_notify_event_t *sn = (xcb_selection_notify_event_t *)event;
- if (sn->requestor == window)
- return true;
+ if (eventType == XCB_PROPERTY_NOTIFY) {
+ auto propertyNotify = reinterpret_cast<xcb_property_notify_event_t *>(event);
+ return propertyNotify->window == window;
}
- return false;
- }
- };
- class ClipboardEvent {
- public:
- ClipboardEvent(QXcbConnection *c)
- { clipboard = c->internAtom("CLIPBOARD"); }
- xcb_atom_t clipboard;
- bool checkEvent(xcb_generic_event_t *e) const {
- if (!e)
- return false;
- int type = e->response_type & 0x7f;
- if (type == XCB_SELECTION_REQUEST) {
- xcb_selection_request_event_t *sr = (xcb_selection_request_event_t *)e;
- return sr->selection == XCB_ATOM_PRIMARY || sr->selection == clipboard;
- } else if (type == XCB_SELECTION_CLEAR) {
- xcb_selection_clear_event_t *sc = (xcb_selection_clear_event_t *)e;
- return sc->selection == XCB_ATOM_PRIMARY || sc->selection == clipboard;
+ if (eventType == XCB_SELECTION_NOTIFY) {
+ auto selectionNotify = reinterpret_cast<xcb_selection_notify_event_t *>(event);
+ return selectionNotify->requestor == window;
}
return false;
- }
- };
-}
-
-xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t win, int type, int timeout, bool checkManager)
-{
- QElapsedTimer timer;
- timer.start();
- do {
- Notify notify(win, type);
- xcb_generic_event_t *e = connection()->checkEvent(notify);
- if (e)
+ });
+ if (e) // found the waited for event
return e;
if (checkManager) {
auto reply = Q_XCB_REPLY(xcb_get_selection_owner, xcb_connection(), atom(QXcbAtom::CLIPBOARD_MANAGER));
if (!reply || reply->owner == XCB_NONE)
- return 0;
+ return nullptr;
}
// process other clipboard events, since someone is probably requesting data from us
- ClipboardEvent clipboard(connection());
- e = connection()->checkEvent(clipboard);
+ auto clipboardAtom = atom(QXcbAtom::CLIPBOARD);
+ e = queue->peek([clipboardAtom](xcb_generic_event_t *event, int type) {
+ xcb_atom_t selection = XCB_ATOM_NONE;
+ if (type == XCB_SELECTION_REQUEST)
+ selection = reinterpret_cast<xcb_selection_request_event_t *>(event)->selection;
+ else if (type == XCB_SELECTION_CLEAR)
+ selection = reinterpret_cast<xcb_selection_clear_event_t *>(event)->selection;
+ return selection == XCB_ATOM_PRIMARY || selection == clipboardAtom;
+ });
if (e) {
connection()->handleXcbEvent(e);
free(e);
@@ -900,9 +850,9 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t win, int
// sleep 50 ms, so we don't use up CPU cycles all the time.
QThread::msleep(50);
- } while (timer.elapsed() < timeout);
+ } while (timer.elapsed() < clipboard_timeout);
- return 0;
+ return nullptr;
}
QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb_atom_t property, int nbytes, bool nullterm)
@@ -924,7 +874,7 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb
for (;;) {
connection()->flush();
- xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_PROPERTY_NOTIFY, clipboard_timeout);
+ xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_PROPERTY_NOTIFY);
if (!ge)
break;
xcb_property_notify_event_t *event = (xcb_property_notify_event_t *)ge;
@@ -987,7 +937,7 @@ QByteArray QXcbClipboard::getSelection(xcb_atom_t selection, xcb_atom_t target,
connection()->sync();
- xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_SELECTION_NOTIFY, clipboard_timeout);
+ xcb_generic_event_t *ge = waitForClipboardEvent(win, XCB_SELECTION_NOTIFY);
bool no_selection = !ge || ((xcb_selection_notify_event_t *)ge)->property == XCB_NONE;
free(ge);
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.h b/src/plugins/platforms/xcb/qxcbclipboard.h
index bfeae13e10..abab42a613 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.h
+++ b/src/plugins/platforms/xcb/qxcbclipboard.h
@@ -89,7 +89,7 @@ public:
QByteArray getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t = 0);
private:
- xcb_generic_event_t *waitForClipboardEvent(xcb_window_t win, int type, int timeout, bool checkManager = false);
+ xcb_generic_event_t *waitForClipboardEvent(xcb_window_t window, int type, bool checkManager = false);
xcb_atom_t sendTargetsSelection(QMimeData *d, xcb_window_t window, xcb_atom_t property);
xcb_atom_t sendSelection(QMimeData *d, xcb_atom_t target, xcb_window_t window, xcb_atom_t property);
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index aca2c347ea..45f096a13a 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -38,12 +38,10 @@
****************************************************************************/
#include <QtGui/private/qguiapplication_p.h>
-#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtCore/QDebug>
#include "qxcbconnection.h"
#include "qxcbkeyboard.h"
-#include "qxcbscreen.h"
#include "qxcbwindow.h"
#include "qxcbclipboard.h"
#if QT_CONFIG(draganddrop)
@@ -55,57 +53,25 @@
#include "qxcbsystemtraytracker.h"
#include "qxcbglintegrationfactory.h"
#include "qxcbglintegration.h"
+#include "qxcbcursor.h"
#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
-#endif
-
-#if QT_CONFIG(xinput2)
-#include <X11/extensions/XI2proto.h>
-#endif
-
-#if QT_CONFIG(xcb_render)
-#include <xcb/render.h>
-#endif
-
-#if defined(Q_CC_GNU) && defined(Q_OF_ELF)
-static xcb_generic_event_t *local_xcb_poll_for_queued_event(xcb_connection_t *c)
- __attribute__((weakref("xcb_poll_for_queued_event")));
-static inline void checkXcbPollForQueuedEvent()
-{ }
-#else
-#include <dlfcn.h>
-typedef xcb_generic_event_t * (*XcbPollForQueuedEventFunctionPointer)(xcb_connection_t *c);
-static XcbPollForQueuedEventFunctionPointer local_xcb_poll_for_queued_event;
-
-static inline void checkXcbPollForQueuedEvent()
-{
-#ifdef RTLD_DEFAULT
- local_xcb_poll_for_queued_event = (XcbPollForQueuedEventFunctionPointer)dlsym(RTLD_DEFAULT, "xcb_poll_for_queued_event");
+#include <xcb/xfixes.h>
+#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
QT_BEGIN_NAMESPACE
@@ -115,9 +81,10 @@ Q_LOGGING_CATEGORY(lcQpaXInputDevices, "qt.qpa.input.devices")
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(lcQpaXcb, "qt.qpa.xcb") // for general (uncategorized) XCB logging
+Q_LOGGING_CATEGORY(lcQpaEventReader, "qt.qpa.events.reader")
Q_LOGGING_CATEGORY(lcQpaPeeker, "qt.qpa.peeker")
Q_LOGGING_CATEGORY(lcQpaKeyboard, "qt.qpa.xkeyboard")
+Q_LOGGING_CATEGORY(lcQpaXDnd, "qt.qpa.xdnd")
// this event type was added in libxcb 1.10,
// but we support also older version
@@ -125,487 +92,30 @@ Q_LOGGING_CATEGORY(lcQpaKeyboard, "qt.qpa.xkeyboard")
#define XCB_GE_GENERIC 35
#endif
-#if QT_CONFIG(xinput2)
-// 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(xinput2)
-
-#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
-
-QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) const
-{
- for (QXcbScreen *screen : m_screens) {
- if (screen->root() == rootWindow && screen->crtc() == crtc)
- return screen;
- }
-
- return 0;
-}
-
-QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output) const
-{
- for (QXcbScreen *screen : m_screens) {
- if (screen->root() == rootWindow && screen->output() == output)
- return screen;
- }
-
- return 0;
-}
-
-QXcbVirtualDesktop* QXcbConnection::virtualDesktopForRootWindow(xcb_window_t rootWindow) const
-{
- for (QXcbVirtualDesktop *virtualDesktop : m_virtualDesktops) {
- if (virtualDesktop->screen()->root == rootWindow)
- return virtualDesktop;
- }
-
- return 0;
-}
-
-/*!
- \brief Synchronizes the screen list, adds new screens, removes deleted ones
-*/
-void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
-{
- if (event->subCode == XCB_RANDR_NOTIFY_CRTC_CHANGE) {
- xcb_randr_crtc_change_t crtc = event->u.cc;
- QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(crtc.window);
- if (!virtualDesktop)
- // Not for us
- return;
-
- QXcbScreen *screen = findScreenForCrtc(crtc.window, crtc.crtc);
- qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc
- << "mode" << crtc.mode << "relevant screen" << screen;
- // Only update geometry when there's a valid mode on the CRTC
- // CRTC with node mode could mean that output has been disabled, and we'll
- // get RRNotifyOutputChange notification for that.
- if (screen && crtc.mode) {
- if (crtc.rotation == XCB_RANDR_ROTATION_ROTATE_90 ||
- crtc.rotation == XCB_RANDR_ROTATION_ROTATE_270)
- std::swap(crtc.width, crtc.height);
- screen->updateGeometry(QRect(crtc.x, crtc.y, crtc.width, crtc.height), crtc.rotation);
- if (screen->mode() != crtc.mode)
- screen->updateRefreshRate(crtc.mode);
- }
-
- } else if (event->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) {
- xcb_randr_output_change_t output = event->u.oc;
- QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(output.window);
- if (!virtualDesktop)
- // Not for us
- return;
-
- QXcbScreen *screen = findScreenForOutput(output.window, output.output);
- qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_OUTPUT_CHANGE:" << output.output;
-
- if (screen && output.connection == XCB_RANDR_CONNECTION_DISCONNECTED) {
- qCDebug(lcQpaScreen) << "screen" << screen->name() << "has been disconnected";
- destroyScreen(screen);
- } else if (!screen && output.connection == XCB_RANDR_CONNECTION_CONNECTED) {
- // New XRandR output is available and it's enabled
- if (output.crtc != XCB_NONE && output.mode != XCB_NONE) {
- auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
- output.output, output.config_timestamp);
- // Find a fake screen
- const auto scrs = virtualDesktop->screens();
- for (QPlatformScreen *scr : scrs) {
- QXcbScreen *xcbScreen = static_cast<QXcbScreen *>(scr);
- if (xcbScreen->output() == XCB_NONE) {
- screen = xcbScreen;
- break;
- }
- }
-
- if (screen) {
- QString nameWas = screen->name();
- // Transform the fake screen into a physical screen
- screen->setOutput(output.output, outputInfo.get());
- updateScreen(screen, output);
- qCDebug(lcQpaScreen) << "output" << screen->name()
- << "is connected and enabled; was fake:" << nameWas;
- } else {
- screen = createScreen(virtualDesktop, output, outputInfo.get());
- qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled";
- }
- QHighDpiScaling::updateHighDpiScaling();
- }
- } else if (screen) {
- if (output.crtc == XCB_NONE && output.mode == XCB_NONE) {
- // Screen has been disabled
- auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
- output.output, output.config_timestamp);
- if (outputInfo->crtc == XCB_NONE) {
- qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled";
- destroyScreen(screen);
- } else {
- qCDebug(lcQpaScreen) << "output" << screen->name() << "has been temporarily disabled for the mode switch";
- // Reset crtc to skip RRCrtcChangeNotify events,
- // because they may be invalid in the middle of the mode switch
- screen->setCrtc(XCB_NONE);
- }
- } else {
- updateScreen(screen, output);
- qCDebug(lcQpaScreen) << "output has changed" << screen;
- }
- }
-
- qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name();
- }
-}
-
-bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output)
-{
- auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), rootWindow);
- if (!primary)
- qWarning("failed to get the primary output of the screen");
-
- const bool isPrimary = primary ? (primary->output == output) : false;
-
- return isPrimary;
-}
-
-void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange)
-{
- screen->setCrtc(outputChange.crtc); // Set the new crtc, because it can be invalid
- screen->updateGeometry(outputChange.config_timestamp);
- 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->isPrimary() && checkOutputIsPrimary(outputChange.window, outputChange.output)) {
- screen->setPrimary(true);
-
- // If the screen became primary, reshuffle the order in QGuiApplicationPrivate
- const int idx = m_screens.indexOf(screen);
- if (idx > 0) {
- qAsConst(m_screens).first()->setPrimary(false);
- m_screens.swap(0, idx);
- }
- screen->virtualDesktop()->setPrimaryScreen(screen);
- QXcbIntegration::instance()->setPrimaryScreen(screen);
- }
- }
-}
-
-QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop,
- const xcb_randr_output_change_t &outputChange,
- xcb_randr_get_output_info_reply_t *outputInfo)
-{
- 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)
- screen->setPrimary(checkOutputIsPrimary(outputChange.window, outputChange.output));
-
- if (screen->isPrimary()) {
- if (!m_screens.isEmpty())
- qAsConst(m_screens).first()->setPrimary(false);
-
- m_screens.prepend(screen);
- } else {
- m_screens.append(screen);
- }
- virtualDesktop->addScreen(screen);
- QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary());
-
- return screen;
-}
-
-void QXcbConnection::destroyScreen(QXcbScreen *screen)
-{
- QXcbVirtualDesktop *virtualDesktop = screen->virtualDesktop();
- if (virtualDesktop->screens().count() == 1) {
- // If there are no other screens on the same virtual desktop,
- // then transform the physical screen into a fake screen.
- const QString nameWas = screen->name();
- screen->setOutput(XCB_NONE, nullptr);
- qCDebug(lcQpaScreen) << "transformed" << nameWas << "to fake" << screen;
- } else {
- // There is more than one screen on the same virtual desktop, remove the screen
- m_screens.removeOne(screen);
- virtualDesktop->removeScreen(screen);
-
- // When primary screen is removed, set the new primary screen
- // which belongs to the primary virtual desktop.
- if (screen->isPrimary()) {
- QXcbScreen *newPrimary = static_cast<QXcbScreen *>(virtualDesktop->screens().at(0));
- newPrimary->setPrimary(true);
- const int idx = m_screens.indexOf(newPrimary);
- if (idx > 0)
- m_screens.swap(0, idx);
- QXcbIntegration::instance()->setPrimaryScreen(newPrimary);
- }
-
- QXcbIntegration::instance()->destroyScreen(screen);
- }
-}
-
-void QXcbConnection::initializeScreens()
-{
- xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup);
- int xcbScreenNumber = 0; // screen number in the xcb sense
- QXcbScreen *primaryScreen = nullptr;
- while (it.rem) {
- // Each "screen" in xcb terminology is a virtual desktop,
- // potentially a collection of separate juxtaposed monitors.
- // But we want a separate QScreen for each output (e.g. DVI-I-1, VGA-1, etc.)
- // which will become virtual siblings.
- xcb_screen_t *xcbScreen = it.data;
- QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber);
- m_virtualDesktops.append(virtualDesktop);
- QList<QPlatformScreen *> siblings;
- if (has_randr_extension) {
- // 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.
- auto resources_current = Q_XCB_REPLY(xcb_randr_get_screen_resources_current,
- xcb_connection(), xcbScreen->root);
- if (!resources_current) {
- qWarning("failed to get the current screen resources");
- } else {
- xcb_timestamp_t timestamp = 0;
- xcb_randr_output_t *outputs = nullptr;
- int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.get());
- if (outputCount) {
- timestamp = resources_current->config_timestamp;
- outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.get());
- } else {
- auto resources = Q_XCB_REPLY(xcb_randr_get_screen_resources,
- xcb_connection(), xcbScreen->root);
- if (!resources) {
- qWarning("failed to get the screen resources");
- } else {
- timestamp = resources->config_timestamp;
- outputCount = xcb_randr_get_screen_resources_outputs_length(resources.get());
- outputs = xcb_randr_get_screen_resources_outputs(resources.get());
- }
- }
-
- if (outputCount) {
- auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), xcbScreen->root);
- if (!primary) {
- qWarning("failed to get the primary output of the screen");
- } else {
- for (int i = 0; i < outputCount; i++) {
- auto output = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_output_info,
- xcb_connection(), outputs[i], timestamp);
- // Invalid, disconnected or disabled output
- if (!output)
- continue;
-
- if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
- qCDebug(lcQpaScreen, "Output %s is not connected", qPrintable(
- QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
- xcb_randr_get_output_info_name_length(output.get()))));
- continue;
- }
-
- if (output->crtc == XCB_NONE) {
- qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable(
- QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
- xcb_randr_get_output_info_name_length(output.get()))));
- continue;
- }
-
- QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.get());
- siblings << screen;
- m_screens << screen;
-
- // There can be multiple outputs per screen, use either
- // the first or an exact match. An exact match isn't
- // always available if primary->output is XCB_NONE
- // or currently disconnected output.
- if (m_primaryScreenNumber == xcbScreenNumber) {
- if (!primaryScreen || (primary && outputs[i] == primary->output)) {
- if (primaryScreen)
- primaryScreen->setPrimary(false);
- primaryScreen = screen;
- primaryScreen->setPrimary(true);
- siblings.prepend(siblings.takeLast());
- }
- }
- }
- }
- }
- }
- } else if (has_xinerama_extension) {
- // Xinerama is available
- auto screens = Q_XCB_REPLY(xcb_xinerama_query_screens, m_connection);
- if (screens) {
- xcb_xinerama_screen_info_iterator_t it = xcb_xinerama_query_screens_screen_info_iterator(screens.get());
- while (it.rem) {
- xcb_xinerama_screen_info_t *screen_info = it.data;
- QXcbScreen *screen = new QXcbScreen(this, virtualDesktop,
- XCB_NONE, nullptr,
- screen_info, it.index);
- siblings << screen;
- m_screens << screen;
- xcb_xinerama_screen_info_next(&it);
- }
- }
- }
- if (siblings.isEmpty()) {
- // If there are no XRandR outputs or XRandR extension is missing,
- // then create a fake/legacy screen.
- QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, nullptr);
- qCDebug(lcQpaScreen) << "created fake screen" << screen;
- m_screens << screen;
- if (m_primaryScreenNumber == xcbScreenNumber) {
- primaryScreen = screen;
- primaryScreen->setPrimary(true);
- }
- siblings << screen;
- }
- virtualDesktop->setScreens(siblings);
- xcb_screen_next(&it);
- ++xcbScreenNumber;
- } // for each xcb screen
-
- for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops))
- virtualDesktop->subscribeToXFixesSelectionNotify();
-
- if (m_virtualDesktops.isEmpty()) {
- qFatal("QXcbConnection: no screens available");
- } else {
- // Ensure the primary screen is first on the list
- if (primaryScreen) {
- if (qAsConst(m_screens).first() != primaryScreen) {
- m_screens.removeOne(primaryScreen);
- m_screens.prepend(primaryScreen);
- }
- }
-
- // Push the screens to QGuiApplication
- for (QXcbScreen *screen : qAsConst(m_screens)) {
- qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")";
- QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary());
- }
-
- qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name();
- }
-}
-
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_reader = new QXcbEventReader(this);
- m_reader->start();
- 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
- 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_eventQueue = new QXcbEventQueue(this);
m_xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP").toLower();
- initializeAllAtoms();
+ if (hasXRandr())
+ xrandrSelectEvents();
- 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(xinput2)
- if (!qEnvironmentVariableIsSet("QT_XCB_NO_XI2"))
- initializeXInput2();
+#if QT_CONFIG(xcb_xinput)
+ if (hasXInput2()) {
+ xi2SetupDevices();
+ xi2SelectStateEvents();
+ }
#endif
- initializeXShape();
- initializeXKB();
m_wmSupport.reset(new QXcbWMSupport(this));
m_keyboard = new QXcbKeyboard(this);
@@ -620,34 +130,6 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
if (!m_startupId.isNull())
qunsetenv("DESKTOP_STARTUP_ID");
-
- QStringList glIntegrationNames;
- glIntegrationNames << QStringLiteral("xcb_glx") << QStringLiteral("xcb_egl");
- QString glIntegrationName = QString::fromLocal8Bit(qgetenv("QT_XCB_GL_INTEGRATION"));
- if (!glIntegrationName.isEmpty()) {
- qCDebug(lcQpaGl) << "QT_XCB_GL_INTEGRATION is set to" << glIntegrationName;
- if (glIntegrationName != QLatin1String("none")) {
- glIntegrationNames.removeAll(glIntegrationName);
- glIntegrationNames.prepend(glIntegrationName);
- } else {
- glIntegrationNames.clear();
- }
- }
-
- if (!glIntegrationNames.isEmpty()) {
- qCDebug(lcQpaGl) << "Choosing xcb gl-integration based on following priority\n" << glIntegrationNames;
- for (int i = 0; i < glIntegrationNames.size() && !m_glIntegration; i++) {
- m_glIntegration = QXcbGlIntegrationFactory::create(glIntegrationNames.at(i));
- if (m_glIntegration && !m_glIntegration->initialize(this)) {
- qCDebug(lcQpaGl) << "Failed to initialize xcb gl-integration" << glIntegrationNames.at(i);
- delete m_glIntegration;
- m_glIntegration = nullptr;
- }
- }
- if (!m_glIntegration)
- qCDebug(lcQpaGl) << "Failed to create xcb gl-integration";
- }
-
sync();
}
@@ -659,12 +141,8 @@ QXcbConnection::~QXcbConnection()
#if QT_CONFIG(draganddrop)
delete m_drag;
#endif
- if (m_reader && m_reader->isRunning()) {
- sendConnectionEvent(QXcbAtom::_QT_CLOSE_CONNECTION);
- m_reader->wait();
- }
-
- delete m_reader;
+ if (m_eventQueue)
+ delete m_eventQueue;
QXcbIntegration *integration = QXcbIntegration::instance();
// Delete screens in reverse order to avoid crash in case of multiple screens
@@ -676,22 +154,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()) {
@@ -727,22 +192,22 @@ QXcbWindow *QXcbConnection::platformWindowFromId(xcb_window_t id)
#define HANDLE_PLATFORM_WINDOW_EVENT(event_t, windowMember, handler) \
{ \
- event_t *e = reinterpret_cast<event_t *>(event); \
+ auto e = reinterpret_cast<event_t *>(event); \
if (QXcbWindowEventListener *eventListener = windowEventListenerFromId(e->windowMember)) { \
- handled = eventListener->handleGenericEvent(event, &result); \
- if (!handled) \
- eventListener->handler(e); \
+ if (eventListener->handleNativeEvent(event)) \
+ return; \
+ eventListener->handler(e); \
} \
} \
break;
#define HANDLE_KEYBOARD_EVENT(event_t, handler) \
{ \
- event_t *e = reinterpret_cast<event_t *>(event); \
+ auto e = reinterpret_cast<event_t *>(event); \
if (QXcbWindowEventListener *eventListener = windowEventListenerFromId(e->event)) { \
- handled = eventListener->handleGenericEvent(event, &result); \
- if (!handled) \
- m_keyboard->handler(e); \
+ if (eventListener->handleNativeEvent(event)) \
+ return; \
+ m_keyboard->handler(e); \
} \
} \
break;
@@ -796,17 +261,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
@@ -968,7 +433,7 @@ void QXcbConnection::handleXcbError(xcb_generic_error_t *error)
{
long result = 0;
QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
- if (dispatcher && dispatcher->filterNativeEvent(m_nativeInterface->genericEventFilterType(), error, &result))
+ if (dispatcher && dispatcher->filterNativeEvent(m_nativeInterface->nativeEventType(), error, &result))
return;
printXcbError("QXcbConnection: XCB error", error);
@@ -1060,371 +525,210 @@ namespace {
void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
{
- long result = 0;
- QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
- bool handled = dispatcher && dispatcher->filterNativeEvent(m_nativeInterface->genericEventFilterType(), event, &result);
+ if (Q_UNLIKELY(lcQpaEvents().isDebugEnabled()))
+ printXcbEvent(lcQpaEvents(), "Event", event);
- uint response_type = event->response_type & ~0x80;
+ long result = 0; // Used only by MS Windows
+ if (QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance()) {
+ if (dispatcher->filterNativeEvent(m_nativeInterface->nativeEventType(), event, &result))
+ return;
+ }
- if (!handled) {
- switch (response_type) {
- case XCB_EXPOSE:
- HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent);
-
- case XCB_BUTTON_PRESS: {
- xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event;
- m_keyboard->updateXKBStateFromCore(ev->state);
- // the event explicitly contains the state of the three first buttons,
- // the rest we need to manage ourselves
- m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
- setButtonState(translateMouseButton(ev->detail), true);
- if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
- qCDebug(lcQpaXInputEvents, "legacy mouse press, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttonState));
- HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent);
- }
- case XCB_BUTTON_RELEASE: {
- xcb_button_release_event_t *ev = (xcb_button_release_event_t *)event;
- m_keyboard->updateXKBStateFromCore(ev->state);
- m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
- setButtonState(translateMouseButton(ev->detail), false);
- if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
- qCDebug(lcQpaXInputEvents, "legacy mouse release, button %d state %X", ev->detail, static_cast<unsigned int>(m_buttonState));
- HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent);
- }
- case XCB_MOTION_NOTIFY: {
- xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event;
- m_keyboard->updateXKBStateFromCore(ev->state);
- m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
- if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
- qCDebug(lcQpaXInputEvents, "legacy mouse move %d,%d button %d state %X", ev->event_x, ev->event_y,
- ev->detail, static_cast<unsigned int>(m_buttonState));
- HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent);
- }
+ uint response_type = event->response_type & ~0x80;
- case XCB_CONFIGURE_NOTIFY:
- HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent);
- case XCB_MAP_NOTIFY:
- HANDLE_PLATFORM_WINDOW_EVENT(xcb_map_notify_event_t, event, handleMapNotifyEvent);
- case XCB_UNMAP_NOTIFY:
- HANDLE_PLATFORM_WINDOW_EVENT(xcb_unmap_notify_event_t, event, handleUnmapNotifyEvent);
- case XCB_DESTROY_NOTIFY:
- HANDLE_PLATFORM_WINDOW_EVENT(xcb_destroy_notify_event_t, event, handleDestroyNotifyEvent);
- case XCB_CLIENT_MESSAGE:
- handleClientMessageEvent((xcb_client_message_event_t *)event);
- break;
- case XCB_ENTER_NOTIFY:
-#if QT_CONFIG(xinput2)
- if (hasXInput2() && !xi2MouseEventsDisabled())
- break;
+ bool handled = true;
+ switch (response_type) {
+ case XCB_EXPOSE:
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_expose_event_t, window, handleExposeEvent);
+ case XCB_BUTTON_PRESS: {
+ auto ev = reinterpret_cast<xcb_button_press_event_t *>(event);
+ m_keyboard->updateXKBStateFromCore(ev->state);
+ // the event explicitly contains the state of the three first buttons,
+ // the rest we need to manage ourselves
+ m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
+ setButtonState(translateMouseButton(ev->detail), true);
+ if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
+ qCDebug(lcQpaXInputEvents, "legacy mouse press, button %d state %X",
+ ev->detail, static_cast<unsigned int>(m_buttonState));
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_press_event_t, event, handleButtonPressEvent);
+ }
+ case XCB_BUTTON_RELEASE: {
+ auto ev = reinterpret_cast<xcb_button_release_event_t *>(event);
+ m_keyboard->updateXKBStateFromCore(ev->state);
+ m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
+ setButtonState(translateMouseButton(ev->detail), false);
+ if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
+ qCDebug(lcQpaXInputEvents, "legacy mouse release, button %d state %X",
+ ev->detail, static_cast<unsigned int>(m_buttonState));
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent);
+ }
+ case XCB_MOTION_NOTIFY: {
+ auto ev = reinterpret_cast<xcb_motion_notify_event_t *>(event);
+ m_keyboard->updateXKBStateFromCore(ev->state);
+ m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
+ if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
+ qCDebug(lcQpaXInputEvents, "legacy mouse move %d,%d button %d state %X",
+ ev->event_x, ev->event_y, ev->detail, static_cast<unsigned int>(m_buttonState));
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_motion_notify_event_t, event, handleMotionNotifyEvent);
+ }
+ case XCB_CONFIGURE_NOTIFY:
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_configure_notify_event_t, event, handleConfigureNotifyEvent);
+ case XCB_MAP_NOTIFY:
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_map_notify_event_t, event, handleMapNotifyEvent);
+ case XCB_UNMAP_NOTIFY:
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_unmap_notify_event_t, event, handleUnmapNotifyEvent);
+ case XCB_DESTROY_NOTIFY:
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_destroy_notify_event_t, event, handleDestroyNotifyEvent);
+ case XCB_CLIENT_MESSAGE: {
+ auto clientMessage = reinterpret_cast<xcb_client_message_event_t *>(event);
+ if (clientMessage->format != 32)
+ return;
+#if QT_CONFIG(draganddrop)
+ if (clientMessage->type == atom(QXcbAtom::XdndStatus))
+ drag()->handleStatus(clientMessage);
+ else if (clientMessage->type == atom(QXcbAtom::XdndFinished))
+ drag()->handleFinished(clientMessage);
#endif
- HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent);
- case XCB_LEAVE_NOTIFY:
-#if QT_CONFIG(xinput2)
- if (hasXInput2() && !xi2MouseEventsDisabled())
- break;
+ if (m_systemTrayTracker && clientMessage->type == atom(QXcbAtom::MANAGER))
+ m_systemTrayTracker->notifyManagerClientMessageEvent(clientMessage);
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_client_message_event_t, window, handleClientMessageEvent);
+ }
+ case XCB_ENTER_NOTIFY:
+#if QT_CONFIG(xcb_xinput)
+ if (hasXInput2() && !xi2MouseEventsDisabled())
+ break;
#endif
- m_keyboard->updateXKBStateFromCore(((xcb_leave_notify_event_t *)event)->state);
- HANDLE_PLATFORM_WINDOW_EVENT(xcb_leave_notify_event_t, event, handleLeaveNotifyEvent);
- case XCB_FOCUS_IN:
- HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_in_event_t, event, handleFocusInEvent);
- case XCB_FOCUS_OUT:
- HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_out_event_t, event, handleFocusOutEvent);
- case XCB_KEY_PRESS:
- {
- xcb_key_press_event_t *kp = (xcb_key_press_event_t *)event;
- m_keyboard->updateXKBStateFromCore(kp->state);
- setTime(kp->time);
- HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent);
- }
- case XCB_KEY_RELEASE:
- m_keyboard->updateXKBStateFromCore(((xcb_key_release_event_t *)event)->state);
- HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent);
- case XCB_MAPPING_NOTIFY:
- m_keyboard->updateKeymap(reinterpret_cast<xcb_mapping_notify_event_t *>(event));
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_enter_notify_event_t, event, handleEnterNotifyEvent);
+ case XCB_LEAVE_NOTIFY:
+#if QT_CONFIG(xcb_xinput)
+ if (hasXInput2() && !xi2MouseEventsDisabled())
break;
- case XCB_SELECTION_REQUEST:
- {
+#endif
+ m_keyboard->updateXKBStateFromCore(reinterpret_cast<xcb_leave_notify_event_t *>(event)->state);
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_leave_notify_event_t, event, handleLeaveNotifyEvent);
+ case XCB_FOCUS_IN:
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_in_event_t, event, handleFocusInEvent);
+ case XCB_FOCUS_OUT:
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_focus_out_event_t, event, handleFocusOutEvent);
+ case XCB_KEY_PRESS:
+ {
+ auto keyPress = reinterpret_cast<xcb_key_press_event_t *>(event);
+ m_keyboard->updateXKBStateFromCore(keyPress->state);
+ setTime(keyPress->time);
+ HANDLE_KEYBOARD_EVENT(xcb_key_press_event_t, handleKeyPressEvent);
+ }
+ case XCB_KEY_RELEASE:
+ m_keyboard->updateXKBStateFromCore(reinterpret_cast<xcb_key_release_event_t *>(event)->state);
+ HANDLE_KEYBOARD_EVENT(xcb_key_release_event_t, handleKeyReleaseEvent);
+ case XCB_MAPPING_NOTIFY:
+ m_keyboard->updateKeymap(reinterpret_cast<xcb_mapping_notify_event_t *>(event));
+ break;
+ case XCB_SELECTION_REQUEST:
+ {
#if QT_CONFIG(draganddrop) || QT_CONFIG(clipboard)
- xcb_selection_request_event_t *sr = reinterpret_cast<xcb_selection_request_event_t *>(event);
+ auto selectionRequest = reinterpret_cast<xcb_selection_request_event_t *>(event);
#endif
#if QT_CONFIG(draganddrop)
- if (sr->selection == atom(QXcbAtom::XdndSelection))
- m_drag->handleSelectionRequest(sr);
- else
+ if (selectionRequest->selection == atom(QXcbAtom::XdndSelection))
+ m_drag->handleSelectionRequest(selectionRequest);
+ else
#endif
- {
+ {
#ifndef QT_NO_CLIPBOARD
- m_clipboard->handleSelectionRequest(sr);
+ m_clipboard->handleSelectionRequest(selectionRequest);
#endif
- }
- break;
}
- case XCB_SELECTION_CLEAR:
- setTime((reinterpret_cast<xcb_selection_clear_event_t *>(event))->time);
+ break;
+ }
+ case XCB_SELECTION_CLEAR:
+ setTime((reinterpret_cast<xcb_selection_clear_event_t *>(event))->time);
#ifndef QT_NO_CLIPBOARD
- m_clipboard->handleSelectionClearRequest(reinterpret_cast<xcb_selection_clear_event_t *>(event));
+ m_clipboard->handleSelectionClearRequest(reinterpret_cast<xcb_selection_clear_event_t *>(event));
#endif
- handled = true;
- break;
- case XCB_SELECTION_NOTIFY:
- setTime((reinterpret_cast<xcb_selection_notify_event_t *>(event))->time);
- handled = false;
- break;
- case XCB_PROPERTY_NOTIFY:
- {
- xcb_property_notify_event_t *pn = reinterpret_cast<xcb_property_notify_event_t *>(event);
- if (pn->atom == atom(QXcbAtom::_NET_WORKAREA)) {
- QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(pn->window);
- if (virtualDesktop)
- virtualDesktop->updateWorkArea();
- } else {
- HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent);
- }
- break;
+ break;
+ case XCB_SELECTION_NOTIFY:
+ setTime((reinterpret_cast<xcb_selection_notify_event_t *>(event))->time);
+ break;
+ case XCB_PROPERTY_NOTIFY:
+ {
+ auto propertyNotify = reinterpret_cast<xcb_property_notify_event_t *>(event);
+ if (propertyNotify->atom == atom(QXcbAtom::_NET_WORKAREA)) {
+ QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(propertyNotify->window);
+ if (virtualDesktop)
+ virtualDesktop->updateWorkArea();
+ } else {
+ HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent);
}
-#if QT_CONFIG(xinput2)
- case XCB_GE_GENERIC:
- // Here the windowEventListener is invoked from xi2HandleEvent()
- if (hasXInput2() && isXIEvent(event, m_xiOpCode))
- xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event));
- break;
+ break;
+ }
+#if QT_CONFIG(xcb_xinput)
+ case XCB_GE_GENERIC:
+ // Here the windowEventListener is invoked from xi2HandleEvent()
+ if (hasXInput2() && isXIEvent(event))
+ xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event));
+ break;
#endif
- default:
- handled = false;
- break;
- }
+ default:
+ handled = false; // event type not recognized
+ break;
}
- if (!handled) {
- if (has_xfixes && response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) {
- xcb_xfixes_selection_notify_event_t *notify_event = reinterpret_cast<xcb_xfixes_selection_notify_event_t *>(event);
- setTime(notify_event->timestamp);
+ if (handled)
+ return;
+
+ handled = true;
+ 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
- m_clipboard->handleXFixesSelectionRequest(notify_event);
+ m_clipboard->handleXFixesSelectionRequest(notify_event);
#endif
- for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops))
- virtualDesktop->handleXFixesSelectionNotify(notify_event);
-
- handled = true;
- } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_NOTIFY) {
- updateScreens(reinterpret_cast<xcb_randr_notify_event_t *>(event));
- handled = true;
- } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
- xcb_randr_screen_change_notify_event_t *change_event = reinterpret_cast<xcb_randr_screen_change_notify_event_t *>(event);
- if (auto *virtualDesktop = virtualDesktopForRootWindow(change_event->root))
- virtualDesktop->handleScreenChange(change_event);
-
- handled = true;
+ for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops))
+ virtualDesktop->handleXFixesSelectionNotify(notify_event);
+ } else if (isXRandrType(response_type, XCB_RANDR_NOTIFY)) {
+ updateScreens(reinterpret_cast<xcb_randr_notify_event_t *>(event));
+ } 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
- _xkb_event *xkb_event = reinterpret_cast<_xkb_event *>(event);
- if (xkb_event->any.deviceID == m_keyboard->coreDeviceId()) {
- switch (xkb_event->any.xkbType) {
- // XkbNewKkdNotify and XkbMapNotify together capture all sorts of keymap
- // updates (e.g. xmodmap, xkbcomp, setxkbmap), with minimal redundent recompilations.
- case XCB_XKB_STATE_NOTIFY:
- m_keyboard->updateXKBState(&xkb_event->state_notify);
- handled = true;
- break;
- case XCB_XKB_MAP_NOTIFY:
+ } 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) {
+ // XkbNewKkdNotify and XkbMapNotify together capture all sorts of keymap
+ // updates (e.g. xmodmap, xkbcomp, setxkbmap), with minimal redundent recompilations.
+ case XCB_XKB_STATE_NOTIFY:
+ m_keyboard->updateXKBState(&xkb_event->state_notify);
+ break;
+ case XCB_XKB_MAP_NOTIFY:
+ m_keyboard->updateKeymap();
+ break;
+ case XCB_XKB_NEW_KEYBOARD_NOTIFY: {
+ xcb_xkb_new_keyboard_notify_event_t *ev = &xkb_event->new_keyboard_notify;
+ if (ev->changed & XCB_XKB_NKN_DETAIL_KEYCODES)
m_keyboard->updateKeymap();
- handled = true;
- break;
- case XCB_XKB_NEW_KEYBOARD_NOTIFY: {
- xcb_xkb_new_keyboard_notify_event_t *ev = &xkb_event->new_keyboard_notify;
- if (ev->changed & XCB_XKB_NKN_DETAIL_KEYCODES)
- m_keyboard->updateKeymap();
- break;
- }
- default:
- break;
+ break;
}
+ default:
+ break;
}
-#endif
}
- }
-
- if (!handled && m_glIntegration)
- handled = m_glIntegration->handleXcbEvent(event, response_type);
-
-#if 0
- if (Q_UNLIKELY(lcQpaEvents().isDebugEnabled()))
- printXcbEvent(lcQpaEvents(), handled ? "Handled" : "Unhandled", event);
#endif
-}
-
-void QXcbConnection::addPeekFunc(PeekFunc f)
-{
- m_peekFuncs.append(f);
-}
-
-qint32 QXcbConnection::generatePeekerId()
-{
- qint32 peekerId = m_peekerIdSource++;
- m_peekerToCachedIndex.insert(peekerId, 0);
- return peekerId;
-}
-
-bool QXcbConnection::removePeekerId(qint32 peekerId)
-{
- if (!m_peekerToCachedIndex.contains(peekerId)) {
- qCWarning(lcQpaXcb, "failed to remove unknown peeker id: %d", peekerId);
- return false;
- }
- m_peekerToCachedIndex.remove(peekerId);
- if (m_peekerToCachedIndex.isEmpty()) {
- m_peekerIdSource = 0; // Once the hash becomes empty, we can start reusing IDs
- m_peekerIndexCacheDirty = false;
- }
- return true;
-}
-
-bool QXcbConnection::peekEventQueue(PeekerCallback peeker, void *peekerData,
- PeekOptions option, qint32 peekerId)
-{
- bool peekerIdProvided = peekerId != -1;
- if (peekerIdProvided && !m_peekerToCachedIndex.contains(peekerId)) {
- qCWarning(lcQpaXcb, "failed to find index for unknown peeker id: %d", peekerId);
- return false;
- }
-
- bool peekFromCachedIndex = option.testFlag(PeekOption::PeekFromCachedIndex);
- if (peekFromCachedIndex && !peekerIdProvided) {
- qCWarning(lcQpaXcb, "PeekOption::PeekFromCachedIndex requires peeker id");
- return false;
- }
-
- if (peekerIdProvided && m_peekerIndexCacheDirty) {
- // When the main event loop has flushed the buffered XCB events into the window
- // system event queue, the cached indices are not valid anymore and need reset.
- auto it = m_peekerToCachedIndex.begin();
- while (it != m_peekerToCachedIndex.constEnd()) {
- (*it) = 0;
- ++it;
- }
- m_peekerIndexCacheDirty = false;
- }
-
- qint32 peekerIndex = peekFromCachedIndex ? m_peekerToCachedIndex.value(peekerId) : 0;
- qint32 startingIndex = peekerIndex;
- bool result = false;
- m_mainEventLoopFlushedQueue = false;
-
- QXcbEventArray *eventqueue = m_reader->lock();
-
- if (Q_UNLIKELY(lcQpaPeeker().isDebugEnabled())) {
- qCDebug(lcQpaPeeker, "[%d] peeker index: %d | mode: %s | queue size: %d", peekerId,
- peekerIndex, peekFromCachedIndex ? "cache" : "start", eventqueue->size());
- }
- while (peekerIndex < eventqueue->size() && !result && !m_mainEventLoopFlushedQueue) {
- xcb_generic_event_t *event = eventqueue->at(peekerIndex++);
- if (!event)
- continue;
- if (Q_UNLIKELY(lcQpaPeeker().isDebugEnabled())) {
- QString debug = QString((QLatin1String("[%1] peeking at index: %2")))
- .arg(peekerId).arg(peekerIndex - 1);
- printXcbEvent(lcQpaPeeker(), debug.toLatin1(), event);
- }
- // A peeker may call QCoreApplication::processEvents(), which has two implications:
- // 1) We need to make the lock available for QXcbConnection::processXcbEvents(),
- // otherwise we will deadlock;
- // 2) QXcbConnection::processXcbEvents() will flush the queue we are currently
- // looping through;
- m_reader->unlock();
- result = peeker(event, peekerData);
- m_reader->lock();
- }
-
- m_reader->unlock();
-
- if (peekerIdProvided && peekerIndex != startingIndex && !m_mainEventLoopFlushedQueue) {
- auto it = m_peekerToCachedIndex.find(peekerId);
- // Make sure that a peeker callback did not remove the peeker id
- if (it != m_peekerToCachedIndex.constEnd())
- (*it) = peekerIndex;
- }
-
- return result;
-}
-
-QXcbEventReader::QXcbEventReader(QXcbConnection *connection)
- : m_connection(connection)
-{
- checkXcbPollForQueuedEvent();
-}
-
-void QXcbEventReader::start()
-{
- if (local_xcb_poll_for_queued_event) {
- connect(this, SIGNAL(eventPending()), m_connection, SLOT(processXcbEvents()), Qt::QueuedConnection);
- connect(this, SIGNAL(finished()), m_connection, SLOT(processXcbEvents()));
- QThread::start();
} else {
- // Must be done after we have an event-dispatcher. By posting a method invocation
- // we are sure that by the time the method is called we have an event-dispatcher.
- QMetaObject::invokeMethod(this, "registerForEvents", Qt::QueuedConnection);
- }
-}
-
-void QXcbEventReader::registerForEvents()
-{
- QSocketNotifier *notifier = new QSocketNotifier(xcb_get_file_descriptor(m_connection->xcb_connection()), QSocketNotifier::Read, this);
- connect(notifier, SIGNAL(activated(int)), m_connection, SLOT(processXcbEvents()));
-
- QAbstractEventDispatcher *dispatcher = QGuiApplicationPrivate::eventDispatcher;
- connect(dispatcher, SIGNAL(aboutToBlock()), m_connection, SLOT(processXcbEvents()));
- connect(dispatcher, SIGNAL(awake()), m_connection, SLOT(processXcbEvents()));
-}
-
-void QXcbEventReader::registerEventDispatcher(QAbstractEventDispatcher *dispatcher)
-{
- // flush the xcb connection before the EventDispatcher is going to block
- // In the non-threaded case processXcbEvents is called before going to block,
- // which flushes the connection.
- if (local_xcb_poll_for_queued_event)
- connect(dispatcher, SIGNAL(aboutToBlock()), m_connection, SLOT(flush()));
-}
-
-void QXcbEventReader::run()
-{
- xcb_generic_event_t *event;
- while (m_connection && (event = xcb_wait_for_event(m_connection->xcb_connection()))) {
- m_mutex.lock();
- addEvent(event);
- while (m_connection && (event = local_xcb_poll_for_queued_event(m_connection->xcb_connection())))
- addEvent(event);
- m_mutex.unlock();
- emit eventPending();
+ handled = false; // event type still not recognized
}
- m_mutex.lock();
- for (int i = 0; i < m_events.size(); ++i)
- free(m_events.at(i));
- m_events.clear();
- m_mutex.unlock();
-}
-
-void QXcbEventReader::addEvent(xcb_generic_event_t *event)
-{
- if ((event->response_type & ~0x80) == XCB_CLIENT_MESSAGE
- && (reinterpret_cast<xcb_client_message_event_t *>(event))->type == m_connection->atom(QXcbAtom::_QT_CLOSE_CONNECTION))
- m_connection = 0;
- m_events << event;
-}
+ if (handled)
+ return;
-QXcbEventArray *QXcbEventReader::lock()
-{
- m_mutex.lock();
- if (!local_xcb_poll_for_queued_event) {
- while (xcb_generic_event_t *event = xcb_poll_for_event(m_connection->xcb_connection()))
- m_events << event;
- }
- return &m_events;
+ if (m_glIntegration)
+ m_glIntegration->handleXcbEvent(event, response_type);
}
-void QXcbEventReader::unlock()
+void QXcbConnection::addPeekFunc(PeekFunc f)
{
- m_mutex.unlock();
+ m_peekFuncs.append(f);
}
void QXcbConnection::setFocusWindow(QWindow *w)
@@ -1444,88 +748,42 @@ 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);
-}
-
-void QXcbConnection::sendConnectionEvent(QXcbAtom::Atom a, uint id)
-{
- xcb_client_message_event_t event;
- memset(&event, 0, sizeof(event));
-
- const xcb_window_t eventListener = xcb_generate_id(m_connection);
- xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_setup);
- xcb_screen_t *screen = it.data;
- xcb_create_window(m_connection, XCB_COPY_FROM_PARENT,
- eventListener, screen->root,
- 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
- screen->root_visual, 0, 0);
-
- event.response_type = XCB_CLIENT_MESSAGE;
- event.format = 32;
- event.sequence = 0;
- event.window = eventListener;
- event.type = atom(a);
- event.data.data32[0] = id;
-
- xcb_send_event(xcb_connection(), false, eventListener, XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<const char *>(&event));
- xcb_destroy_window(m_connection, eventListener);
- xcb_flush(xcb_connection());
-}
-
-namespace
-{
- class PropertyNotifyEvent {
- public:
- PropertyNotifyEvent(xcb_window_t win, xcb_atom_t property)
- : window(win), type(XCB_PROPERTY_NOTIFY), atom(property) {}
- xcb_window_t window;
- int type;
- xcb_atom_t atom;
- bool checkEvent(xcb_generic_event_t *event) const {
- if (!event)
- return false;
- if ((event->response_type & ~0x80) != type) {
- return false;
- } else {
- xcb_property_notify_event_t *pn = reinterpret_cast<xcb_property_notify_event_t *>(event);
- if ((pn->window == window) && (pn->atom == atom))
- return true;
- }
- return false;
- }
- };
+ xcb_ungrab_server(xcb_connection());
}
xcb_timestamp_t QXcbConnection::getTimestamp()
{
// send a dummy event to myself to get the timestamp from X server.
- xcb_window_t root_win = rootWindow();
- xcb_change_property(xcb_connection(), XCB_PROP_MODE_APPEND, root_win, atom(QXcbAtom::CLIP_TEMPORARY),
- XCB_ATOM_INTEGER, 32, 0, NULL);
+ xcb_window_t window = rootWindow();
+ xcb_atom_t dummyAtom = atom(QXcbAtom::CLIP_TEMPORARY);
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_APPEND, window, dummyAtom,
+ XCB_ATOM_INTEGER, 32, 0, nullptr);
connection()->flush();
- PropertyNotifyEvent checker(root_win, atom(QXcbAtom::CLIP_TEMPORARY));
- xcb_generic_event_t *event = 0;
- // lets keep this inside a loop to avoid a possible race condition, where
- // reader thread has not yet had the time to acquire the mutex in order
- // to add the new set of events to its event queue
+ xcb_generic_event_t *event = nullptr;
+
while (!event) {
connection()->sync();
- event = checkEvent(checker);
+ event = eventQueue()->peek([window, dummyAtom](xcb_generic_event_t *event, int type) {
+ if (type != XCB_PROPERTY_NOTIFY)
+ return false;
+ auto propertyNotify = reinterpret_cast<xcb_property_notify_event_t *>(event);
+ return propertyNotify->window == window && propertyNotify->atom == dummyAtom;
+ });
}
xcb_property_notify_event_t *pn = reinterpret_cast<xcb_property_notify_event_t *>(event);
xcb_timestamp_t timestamp = pn->time;
free(event);
- xcb_delete_property(xcb_connection(), root_win, atom(QXcbAtom::CLIP_TEMPORARY));
+ xcb_delete_property(xcb_connection(), window, dummyAtom);
return timestamp;
}
@@ -1552,6 +810,9 @@ xcb_window_t QXcbConnection::getQtSelectionOwner()
xcbScreen->root_visual, // visual
0, // value mask
0); // value list
+
+ QXcbWindow::setWindowTitle(connection(), m_qtSelectionOwner,
+ QStringLiteral("Qt Selection Window"));
}
return m_qtSelectionOwner;
}
@@ -1576,17 +837,11 @@ xcb_window_t QXcbConnection::clientLeader()
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->screen()->root_visual,
0, 0);
-#ifndef QT_NO_DEBUG
- QByteArray ba("Qt client leader window");
- xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_clientLeader,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData());
-#endif
+
+
+ QXcbWindow::setWindowTitle(connection(), m_clientLeader,
+ QStringLiteral("Qt Client Leader Window"));
+
xcb_change_property(xcb_connection(),
XCB_PROP_MODE_REPLACE,
m_clientLeader,
@@ -1614,46 +869,6 @@ xcb_window_t QXcbConnection::clientLeader()
return m_clientLeader;
}
-#if QT_CONFIG(xcb_xlib)
-void *QXcbConnection::xlib_display() const
-{
- return m_xlib_display;
-}
-
-void *QXcbConnection::createVisualInfoForDefaultVisualId() const
-{
- if (m_defaultVisualId == UINT_MAX)
- return 0;
- XVisualInfo info;
- memset(&info, 0, sizeof info);
- info.visualid = m_defaultVisualId;
-
- int count = 0;
- Display *dpy = static_cast<Display *>(connection()->xlib_display());
- XVisualInfo *retVisual = XGetVisualInfo(dpy, VisualIDMask, &info, &count);
- Q_ASSERT(count < 2);
- return retVisual;
-}
-
-#endif
-
-#if QT_CONFIG(xinput2)
-// it is safe to cast XI_* events here as long as we are only touching the first 32 bytes,
-// after that position event needs memmove, see xi2PrepareXIGenericDeviceEvent
-static inline bool isXIType(xcb_generic_event_t *event, int opCode, uint16_t type)
-{
- if (!isXIEvent(event, opCode))
- return false;
-
- xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
- return xiEvent->evtype == type;
-}
-#endif
-static inline bool isValid(xcb_generic_event_t *event)
-{
- return event && (event->response_type & ~0x80);
-}
-
/*! \internal
Compresses events of the same type to avoid swamping the event queue.
@@ -1666,85 +881,115 @@ static inline bool isValid(xcb_generic_event_t *event)
3) Or add public API to Qt for disabling event compression QTBUG-44964
*/
-bool QXcbConnection::compressEvent(xcb_generic_event_t *event, int currentIndex, QXcbEventArray *eventqueue) const
+bool QXcbConnection::compressEvent(xcb_generic_event_t *event) const
{
+ if (!QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents))
+ return false;
+
uint responseType = event->response_type & ~0x80;
- int nextIndex = currentIndex + 1;
if (responseType == XCB_MOTION_NOTIFY) {
// compress XCB_MOTION_NOTIFY notify events
- for (int j = nextIndex; j < eventqueue->size(); ++j) {
- xcb_generic_event_t *next = eventqueue->at(j);
- if (!isValid(next))
- continue;
- if (next->response_type == XCB_MOTION_NOTIFY)
- return true;
- }
- return false;
+ return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch,
+ [](xcb_generic_event_t *, int type) {
+ return type == XCB_MOTION_NOTIFY;
+ });
}
-#if QT_CONFIG(xinput2)
+
+#if QT_CONFIG(xcb_xinput)
// compress XI_* events
if (responseType == XCB_GE_GENERIC) {
if (!hasXInput2())
return false;
// compress XI_Motion
- if (isXIType(event, m_xiOpCode, XI_Motion)) {
+ if (isXIType(event, XCB_INPUT_MOTION)) {
#if QT_CONFIG(tabletevent)
- xXIDeviceEvent *xdev = reinterpret_cast<xXIDeviceEvent *>(event);
- // Xlib's XI2 events need memmove, see xi2PrepareXIGenericDeviceEvent()
- auto sourceId = *reinterpret_cast<uint16_t *>(reinterpret_cast<char *>(&xdev->sourceid) + 4);
+ auto xdev = reinterpret_cast<xcb_input_motion_event_t *>(event);
if (!QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents) &&
- const_cast<QXcbConnection *>(this)->tabletDataForDevice(sourceId))
+ const_cast<QXcbConnection *>(this)->tabletDataForDevice(xdev->sourceid))
return false;
#endif // QT_CONFIG(tabletevent)
- for (int j = nextIndex; j < eventqueue->size(); ++j) {
- xcb_generic_event_t *next = eventqueue->at(j);
- if (!isValid(next))
- continue;
- if (isXIType(next, m_xiOpCode, XI_Motion))
- return true;
- }
- return false;
+ return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch,
+ [this](xcb_generic_event_t *next, int) {
+ return isXIType(next, XCB_INPUT_MOTION);
+ });
}
-#ifdef XCB_USE_XINPUT22
+
// compress XI_TouchUpdate for the same touch point id
- if (isXIType(event, m_xiOpCode, XI_TouchUpdate)) {
- xXIDeviceEvent *xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
- uint32_t id = xiDeviceEvent->detail % INT_MAX;
- for (int j = nextIndex; j < eventqueue->size(); ++j) {
- xcb_generic_event_t *next = eventqueue->at(j);
- if (!isValid(next))
- continue;
- if (isXIType(next, m_xiOpCode, XI_TouchUpdate)) {
- xXIDeviceEvent *xiDeviceNextEvent = reinterpret_cast<xXIDeviceEvent *>(next);
- if (id == xiDeviceNextEvent->detail % INT_MAX)
- return true;
- }
- }
- return false;
+ 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, XCB_INPUT_TOUCH_UPDATE))
+ return false;
+ auto touchUpdateNextEvent = reinterpret_cast<xcb_input_touch_update_event_t *>(next);
+ return id == touchUpdateNextEvent->detail % INT_MAX;
+ });
}
-#endif
+
return false;
}
#endif
+
if (responseType == XCB_CONFIGURE_NOTIFY) {
// compress multiple configure notify events for the same window
- for (int j = nextIndex; j < eventqueue->size(); ++j) {
- xcb_generic_event_t *next = eventqueue->at(j);
- if (isValid(next) && next->response_type == XCB_CONFIGURE_NOTIFY
- && reinterpret_cast<xcb_configure_notify_event_t *>(next)->event == reinterpret_cast<xcb_configure_notify_event_t *>(event)->event)
- {
- return true;
- }
- }
- return false;
+ return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch,
+ [event](xcb_generic_event_t *next, int type) {
+ if (type != XCB_CONFIGURE_NOTIFY)
+ return false;
+ auto currentEvent = reinterpret_cast<xcb_configure_notify_event_t *>(event);
+ auto nextEvent = reinterpret_cast<xcb_configure_notify_event_t *>(next);
+ return currentEvent->event == nextEvent->event;
+ });
}
return false;
}
-void QXcbConnection::processXcbEvents()
+bool QXcbConnection::isUserInputEvent(xcb_generic_event_t *event) const
+{
+ auto eventType = event->response_type & ~0x80;
+ bool isInputEvent = eventType == XCB_BUTTON_PRESS ||
+ eventType == XCB_BUTTON_RELEASE ||
+ eventType == XCB_KEY_PRESS ||
+ eventType == XCB_KEY_RELEASE ||
+ eventType == XCB_MOTION_NOTIFY ||
+ eventType == XCB_ENTER_NOTIFY ||
+ eventType == XCB_LEAVE_NOTIFY;
+ if (isInputEvent)
+ return true;
+
+#if QT_CONFIG(xcb_xinput)
+ if (connection()->hasXInput2()) {
+ 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, XCB_INPUT_PROPERTY);
+ }
+ if (isInputEvent)
+ return true;
+#endif
+
+ if (eventType == XCB_CLIENT_MESSAGE) {
+ auto clientMessage = reinterpret_cast<const xcb_client_message_event_t *>(event);
+ if (clientMessage->format == 32 && clientMessage->type == atom(QXcbAtom::WM_PROTOCOLS))
+ if (clientMessage->data.data32[0] == atom(QXcbAtom::WM_DELETE_WINDOW))
+ isInputEvent = true;
+ }
+
+ return isInputEvent;
+}
+
+void QXcbConnection::processXcbEvents(QEventLoop::ProcessEventsFlags flags)
{
int connection_error = xcb_connection_has_error(xcb_connection());
if (connection_error) {
@@ -1752,22 +997,17 @@ void QXcbConnection::processXcbEvents()
exit(1);
}
- QXcbEventArray *eventqueue = m_reader->lock();
+ m_eventQueue->flushBufferedEvents();
- for (int i = 0; i < eventqueue->size(); ++i) {
- xcb_generic_event_t *event = eventqueue->at(i);
- if (!event)
- continue;
+ while (xcb_generic_event_t *event = m_eventQueue->takeFirst(flags)) {
QScopedPointer<xcb_generic_event_t, QScopedPointerPodDeleter> eventGuard(event);
- (*eventqueue)[i] = 0;
if (!(event->response_type & ~0x80)) {
handleXcbError(reinterpret_cast<xcb_generic_error_t *>(event));
continue;
}
- if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents)) &&
- compressEvent(event, i, eventqueue))
+ if (compressEvent(event))
continue;
#ifndef QT_NO_CLIPBOARD
@@ -1786,305 +1026,28 @@ void QXcbConnection::processXcbEvents()
m_peekFuncs.erase(std::remove_if(m_peekFuncs.begin(), m_peekFuncs.end(),
isWaitingFor),
m_peekFuncs.end());
- m_reader->unlock();
- handleXcbEvent(event);
- m_reader->lock();
- }
-
- eventqueue->clear();
- m_reader->unlock();
+ handleXcbEvent(event);
- m_peekerIndexCacheDirty = m_mainEventLoopFlushedQueue = true;
+ // The lock-based solution used to free the lock inside this loop,
+ // hence allowing for more events to arrive. ### Check if we want
+ // this flush here after QTBUG-70095
+ m_eventQueue->flushBufferedEvents();
+ }
// Indicate with a null event that the event the callbacks are waiting for
// is not in the queue currently.
for (PeekFunc f : qAsConst(m_peekFuncs))
- f(this, 0);
+ f(this, nullptr);
m_peekFuncs.clear();
xcb_flush(xcb_connection());
}
-void QXcbConnection::handleClientMessageEvent(const xcb_client_message_event_t *event)
-{
- if (event->format != 32)
- return;
-
-#if QT_CONFIG(draganddrop)
- if (event->type == atom(QXcbAtom::XdndStatus)) {
- drag()->handleStatus(event);
- } else if (event->type == atom(QXcbAtom::XdndFinished)) {
- drag()->handleFinished(event);
- }
-#endif
- if (m_systemTrayTracker && event->type == atom(QXcbAtom::MANAGER))
- m_systemTrayTracker->notifyManagerClientMessageEvent(event);
-
- QXcbWindow *window = platformWindowFromId(event->window);
- if (!window)
- return;
-
- window->handleClientMessageEvent(event);
-}
-
-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"
-
- // 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"
- // \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::NPredefinedAtoms);
-
- const QByteArray settings_atom_name = "_QT_SETTINGS_TIMESTAMP_" + m_displayName;
- names[i++] = settings_atom_name;
-
- 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;
@@ -2104,225 +1067,66 @@ 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()
+QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() const
{
- 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
- );
+ if (!m_systemTrayTracker) {
+ QXcbConnection *self = const_cast<QXcbConnection *>(this);
+ if ((self->m_systemTrayTracker = QXcbSystemTrayTracker::create(self))) {
+ connect(m_systemTrayTracker, SIGNAL(systemTrayWindowChanged(QScreen*)),
+ QGuiApplication::platformNativeInterface(), SIGNAL(systemTrayWindowChanged(QScreen*)));
+ }
}
+ return m_systemTrayTracker;
}
-void QXcbConnection::initializeXinerama()
+Qt::MouseButtons QXcbConnection::queryMouseButtons() const
{
- 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;
+ int stateMask = 0;
+ QXcbCursor::queryPointer(connection(), 0, 0, &stateMask);
+ return translateMouseButtons(stateMask);
}
-void QXcbConnection::initializeXShape()
+Qt::KeyboardModifiers QXcbConnection::queryKeyboardModifiers() const
{
- 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;
- }
+ int stateMask = 0;
+ QXcbCursor::queryPointer(connection(), 0, 0, &stateMask);
+ return keyboard()->translateModifiers(stateMask);
}
-void QXcbConnection::initializeXKB()
+QXcbGlIntegration *QXcbConnection::glIntegration() const
{
-#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 (m_glIntegrationInitialized)
+ return m_glIntegration;
- 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;
+ QStringList glIntegrationNames;
+ glIntegrationNames << QStringLiteral("xcb_glx") << QStringLiteral("xcb_egl");
+ QString glIntegrationName = QString::fromLocal8Bit(qgetenv("QT_XCB_GL_INTEGRATION"));
+ if (!glIntegrationName.isEmpty()) {
+ qCDebug(lcQpaGl) << "QT_XCB_GL_INTEGRATION is set to" << glIntegrationName;
+ if (glIntegrationName != QLatin1String("none")) {
+ glIntegrationNames.removeAll(glIntegrationName);
+ glIntegrationNames.prepend(glIntegrationName);
+ } else {
+ glIntegrationNames.clear();
+ }
}
-#endif
-}
-QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() const
-{
- if (!m_systemTrayTracker) {
- QXcbConnection *self = const_cast<QXcbConnection *>(this);
- if ((self->m_systemTrayTracker = QXcbSystemTrayTracker::create(self))) {
- connect(m_systemTrayTracker, SIGNAL(systemTrayWindowChanged(QScreen*)),
- QGuiApplication::platformNativeInterface(), SIGNAL(systemTrayWindowChanged(QScreen*)));
+ if (!glIntegrationNames.isEmpty()) {
+ qCDebug(lcQpaGl) << "Choosing xcb gl-integration based on following priority\n" << glIntegrationNames;
+ for (int i = 0; i < glIntegrationNames.size() && !m_glIntegration; i++) {
+ m_glIntegration = QXcbGlIntegrationFactory::create(glIntegrationNames.at(i));
+ if (m_glIntegration && !m_glIntegration->initialize(const_cast<QXcbConnection *>(this))) {
+ qCDebug(lcQpaGl) << "Failed to initialize xcb gl-integration" << glIntegrationNames.at(i);
+ delete m_glIntegration;
+ m_glIntegration = nullptr;
+ }
}
+ if (!m_glIntegration)
+ qCDebug(lcQpaGl) << "Failed to create xcb gl-integration";
}
- return m_systemTrayTracker;
-}
-bool QXcbConnection::xEmbedSystemTrayAvailable()
-{
- if (!QGuiApplicationPrivate::platformIntegration())
- return false;
- QXcbConnection *connection = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration())->defaultConnection();
- return connection->systemTrayTracker();
-}
-
-bool QXcbConnection::xEmbedSystemTrayVisualHasAlphaChannel()
-{
- if (!QGuiApplicationPrivate::platformIntegration())
- return false;
- QXcbConnection *connection = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration())->defaultConnection();
- return connection->systemTrayTracker() && connection->systemTrayTracker()->visualHasAlphaChannel();
+ m_glIntegrationInitialized = true;
+ return m_glIntegration;
}
bool QXcbConnection::event(QEvent *e)
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index c9dde35558..5d53b97d37 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -47,42 +47,18 @@
#include "qxcbexport.h"
#include <QHash>
#include <QList>
-#include <QMutex>
-#include <QObject>
-#include <QThread>
#include <QVector>
-#include <QVarLengthArray>
#include <qpa/qwindowsysteminterface.h>
#include <QtCore/QLoggingCategory>
#include <QtCore/private/qglobal_p.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 "qxcbeventqueue.h"
+#include "qxcbconnection_basic.h"
#if QT_CONFIG(tabletevent)
#include <QTabletEvent>
#endif
-#if QT_CONFIG(xinput2)
-#include <X11/extensions/XI2.h>
-#ifdef XIScrollClass
-#define XCB_USE_XINPUT21 // XI 2.1 adds smooth scrolling support
-#ifdef XI_TouchBeginMask
-#define XCB_USE_XINPUT22 // XI 2.2 adds multi-point touch support
-#endif
-#endif
-#endif // QT_CONFIG(xinput2)
-
-struct xcb_randr_get_output_info_reply_t;
-
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcQpaXInput)
@@ -90,9 +66,10 @@ 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)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaEventReader)
class QXcbVirtualDesktop;
class QXcbScreen;
@@ -105,248 +82,11 @@ 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,
-
- // 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,
-
- NPredefinedAtoms,
-
- _QT_SETTINGS_TIMESTAMP = NPredefinedAtoms,
- NAtoms
- };
-}
-
-typedef QVarLengthArray<xcb_generic_event_t *, 64> QXcbEventArray;
-
-class QXcbConnection;
-class QXcbEventReader : public QThread
-{
- Q_OBJECT
-public:
- QXcbEventReader(QXcbConnection *connection);
-
- void run() override;
-
- QXcbEventArray *lock();
- void unlock();
-
- void start();
-
- void registerEventDispatcher(QAbstractEventDispatcher *dispatcher);
-
-signals:
- void eventPending();
-
-private slots:
- void registerForEvents();
-
-private:
- void addEvent(xcb_generic_event_t *event);
-
- QMutex m_mutex;
- QXcbEventArray m_events;
- QXcbConnection *m_connection;
-};
-
class QXcbWindowEventListener
{
public:
virtual ~QXcbWindowEventListener() {}
- virtual bool handleGenericEvent(xcb_generic_event_t *, long *) { return false; }
+ virtual bool handleNativeEvent(xcb_generic_event_t *) { return false; }
virtual void handleExposeEvent(const xcb_expose_event_t *) {}
virtual void handleClientMessageEvent(const xcb_client_message_event_t *) {}
@@ -362,7 +102,7 @@ public:
virtual void handleFocusInEvent(const xcb_focus_in_event_t *) {}
virtual void handleFocusOutEvent(const xcb_focus_out_event_t *) {}
virtual void handlePropertyNotifyEvent(const xcb_property_notify_event_t *) {}
-#if QT_CONFIG(xinput2)
+#if QT_CONFIG(xcb_xinput)
virtual void handleXIMouseEvent(xcb_ge_event_t *, Qt::MouseEventSource = Qt::MouseEventNotSynthesized) {}
virtual void handleXIEnterLeave(xcb_ge_event_t *) {}
#endif
@@ -383,8 +123,7 @@ private:
QXcbWindow *m_window;
};
-class QAbstractEventDispatcher;
-class Q_XCB_EXPORT QXcbConnection : public QObject
+class Q_XCB_EXPORT QXcbConnection : public QXcbBasicConnection
{
Q_OBJECT
public:
@@ -392,22 +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
@@ -415,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
}
@@ -437,10 +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;
- void *createVisualInfoForDefaultVisualId() const;
-#endif
void sync();
void handleXcbError(xcb_generic_error_t *error);
@@ -454,45 +182,15 @@ public:
QXcbWindowEventListener *windowEventListenerFromId(xcb_window_t id);
QXcbWindow *platformWindowFromId(xcb_window_t id);
- template<typename T>
- inline xcb_generic_event_t *checkEvent(T &checker);
-
typedef bool (*PeekFunc)(QXcbConnection *, xcb_generic_event_t *);
void addPeekFunc(PeekFunc f);
- // Peek at all queued events
- qint32 generatePeekerId();
- bool removePeekerId(qint32 peekerId);
- enum PeekOption { PeekDefault = 0, PeekFromCachedIndex = 1 }; // see qx11info_x11.h
- Q_DECLARE_FLAGS(PeekOptions, PeekOption)
- typedef bool (*PeekerCallback)(xcb_generic_event_t *event, void *peekerData);
- bool peekEventQueue(PeekerCallback peeker, void *peekerData = nullptr,
- PeekOptions option = PeekDefault, qint32 peekerId = -1);
-
inline xcb_timestamp_t time() const { return m_time; }
inline void setTime(xcb_timestamp_t t) { if (t > m_time) m_time = t; }
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 threadedEventHandling() const { return m_reader->isRunning(); }
-
xcb_timestamp_t getTimestamp();
xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
xcb_window_t getQtSelectionOwner();
@@ -522,53 +220,37 @@ public:
QXcbNativeInterface *nativeInterface() const { return m_nativeInterface; }
QXcbSystemTrayTracker *systemTrayTracker() const;
- static bool xEmbedSystemTrayAvailable();
- static bool xEmbedSystemTrayVisualHasAlphaChannel();
-#if QT_CONFIG(xinput2)
+ Qt::MouseButtons queryMouseButtons() const;
+ Qt::KeyboardModifiers queryKeyboardModifiers() const;
+
+ bool isUserInputEvent(xcb_generic_event_t *event) const;
+
+#if QT_CONFIG(xcb_xinput)
void xi2SelectStateEvents();
void xi2SelectDeviceEvents(xcb_window_t window);
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);
-#ifdef XCB_USE_XINPUT21
void xi2UpdateScrollingDevices();
-#endif
-#ifdef XCB_USE_XINPUT22
bool startSystemMoveResizeForTouchBegin(xcb_window_t window, const QPoint &point, int corner);
void abortSystemMoveResizeForTouch();
bool isTouchScreen(int id);
#endif
-#endif
- QXcbEventReader *eventReader() const { return m_reader; }
bool canGrab() const { return m_canGrabServer; }
- QXcbGlIntegration *glIntegration() const { return m_glIntegration; }
+ QXcbGlIntegration *glIntegration() const;
+
+ void flush() { xcb_flush(xcb_connection()); }
+ void processXcbEvents(QEventLoop::ProcessEventsFlags flags);
protected:
bool event(QEvent *e) override;
-public slots:
- void flush() { xcb_flush(m_connection); }
-
-private slots:
- void processXcbEvents();
-
private:
- void initializeAllAtoms();
- void sendConnectionEvent(QXcbAtom::Atom atom, uint id = 0);
- void initializeShm();
- void initializeXFixes();
- void initializeXRender();
- void initializeXRandr();
- void initializeXinerama();
- void initializeXShape();
- void initializeXKB();
- void handleClientMessageEvent(const xcb_client_message_event_t *event);
+ void xrandrSelectEvents();
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;
@@ -580,12 +262,9 @@ private:
xcb_randr_get_output_info_reply_t *outputInfo);
void destroyScreen(QXcbScreen *screen);
void initializeScreens();
- bool compressEvent(xcb_generic_event_t *event, int currentIndex, QXcbEventArray *eventqueue) const;
+ bool compressEvent(xcb_generic_event_t *event) const;
- bool m_xi2Enabled = false;
-#if QT_CONFIG(xinput2)
- int m_xi2Minor = -1;
- void initializeXInput2();
+#if QT_CONFIG(xcb_xinput)
void xi2SetupDevice(void *info, bool removeExisting = true);
void xi2SetupDevices();
struct TouchDeviceData {
@@ -611,10 +290,7 @@ private:
void xi2HandleEvent(xcb_ge_event_t *event);
void xi2HandleHierarchyEvent(void *event);
void xi2HandleDeviceChangedEvent(void *event);
- int m_xiOpCode, m_xiEventBase, m_xiErrorBase;
-#ifdef XCB_USE_XINPUT22
void xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow);
-#endif // XCB_USE_XINPUT22
#if QT_CONFIG(tabletevent)
struct TabletData {
int deviceId = 0;
@@ -649,32 +325,30 @@ private:
QPointF lastScrollPosition;
};
QHash<int, ScrollingDevice> m_scrollingDevices;
-#ifdef XCB_USE_XINPUT21
void xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice);
void xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice);
ScrollingDevice *scrollingDeviceForId(int id);
-#endif
static bool xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value);
- static void xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event);
-#endif
- xcb_connection_t *m_connection = nullptr;
- const xcb_setup_t *m_setup = nullptr;
+ QHash<int, TouchDeviceData> m_touchDevices;
+ struct StartSystemMoveResizeInfo {
+ xcb_window_t window = XCB_NONE;
+ uint16_t deviceid;
+ uint32_t pointid;
+ int corner;
+ } m_startSystemMoveResizeInfo;
+#endif // QT_CONFIG(xcb_xinput)
+
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;
@@ -685,42 +359,12 @@ private:
QScopedPointer<QXcbWMSupport> m_wmSupport;
QXcbNativeInterface *m_nativeInterface = nullptr;
-#if QT_CONFIG(xcb_xlib)
- void *m_xlib_display = nullptr;
-#endif
- QXcbEventReader *m_reader = nullptr;
+ QXcbEventQueue *m_eventQueue = nullptr;
-#if QT_CONFIG(xinput2)
- QHash<int, TouchDeviceData> m_touchDevices;
-#ifdef XCB_USE_XINPUT22
- struct StartSystemMoveResizeInfo {
- xcb_window_t window = XCB_NONE;
- uint16_t deviceid;
- uint32_t pointid;
- int corner;
- } m_startSystemMoveResizeInfo;
-#endif
-#endif
WindowMapper m_mapper;
QVector<PeekFunc> m_peekFuncs;
- uint32_t xfixes_first_event = 0;
- uint32_t xrandr_first_event = 0;
- uint32_t xkb_first_event = 0;
-
- 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;
-
- QPair<int, int> m_xrenderVersion;
-
Qt::MouseButtons m_buttonState = 0;
Qt::MouseButton m_button = Qt::NoButton;
@@ -731,44 +375,24 @@ private:
xcb_window_t m_clientLeader = 0;
QByteArray m_startupId;
QXcbSystemTrayTracker *m_systemTrayTracker = nullptr;
- QXcbGlIntegration *m_glIntegration = nullptr;
+ mutable QXcbGlIntegration *m_glIntegration = nullptr;
+ mutable bool m_glIntegrationInitialized = false;
bool m_xiGrab = false;
QVector<int> m_xiMasterPointerIds;
xcb_window_t m_qtSelectionOwner = 0;
- bool m_mainEventLoopFlushedQueue = false;
- qint32 m_peekerIdSource = 0;
- bool m_peekerIndexCacheDirty = false;
- QHash<qint32, qint32> m_peekerToCachedIndex;
- friend class QXcbEventReader;
+ friend class QXcbEventQueue;
QByteArray m_xdgCurrentDesktop;
};
-#if QT_CONFIG(xinput2)
+#if QT_CONFIG(xcb_xinput)
#if QT_CONFIG(tabletevent)
Q_DECLARE_TYPEINFO(QXcbConnection::TabletData::ValuatorClassInfo, Q_PRIMITIVE_TYPE);
Q_DECLARE_TYPEINFO(QXcbConnection::TabletData, Q_MOVABLE_TYPE);
#endif
#endif
-template<typename T>
-xcb_generic_event_t *QXcbConnection::checkEvent(T &checker)
-{
- QXcbEventArray *eventqueue = m_reader->lock();
-
- for (int i = 0; i < eventqueue->size(); ++i) {
- xcb_generic_event_t *event = eventqueue->at(i);
- if (checker.checkEvent(event)) {
- (*eventqueue)[i] = 0;
- m_reader->unlock();
- return event;
- }
- }
- m_reader->unlock();
- return 0;
-}
-
class QXcbConnectionGrabber
{
public:
@@ -779,34 +403,11 @@ 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) \
- )
-
-template <typename T>
-union q_padded_xcb_event {
- T event;
- char padding[32];
-};
-
// 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.
-#define Q_DECLARE_XCB_EVENT(event_var, event_type) \
- q_padded_xcb_event<event_type> store = {}; \
- auto &event_var = store.event;
+template <typename T>
+struct alignas(32) q_padded_xcb_event : T { };
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
new file mode 100644
index 0000000000..b8335d1240
--- /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_hasXRandr = 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_hasXRender = 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_screens.cpp b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp
new file mode 100644
index 0000000000..fe9e0be86d
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp
@@ -0,0 +1,414 @@
+/****************************************************************************
+**
+** 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.h"
+#include "qxcbscreen.h"
+#include "qxcbintegration.h"
+
+#include <QtGui/private/qhighdpiscaling_p.h>
+#include <QtCore/QString>
+#include <QtCore/QList>
+
+#include <xcb/xinerama.h>
+
+void QXcbConnection::xrandrSelectEvents()
+{
+ 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
+ );
+ }
+}
+
+QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) const
+{
+ for (QXcbScreen *screen : m_screens) {
+ if (screen->root() == rootWindow && screen->crtc() == crtc)
+ return screen;
+ }
+
+ return nullptr;
+}
+
+QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output) const
+{
+ for (QXcbScreen *screen : m_screens) {
+ if (screen->root() == rootWindow && screen->output() == output)
+ return screen;
+ }
+
+ return nullptr;
+}
+
+QXcbVirtualDesktop* QXcbConnection::virtualDesktopForRootWindow(xcb_window_t rootWindow) const
+{
+ for (QXcbVirtualDesktop *virtualDesktop : m_virtualDesktops) {
+ if (virtualDesktop->screen()->root == rootWindow)
+ return virtualDesktop;
+ }
+
+ return nullptr;
+}
+
+/*!
+ \brief Synchronizes the screen list, adds new screens, removes deleted ones
+*/
+void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
+{
+ if (event->subCode == XCB_RANDR_NOTIFY_CRTC_CHANGE) {
+ xcb_randr_crtc_change_t crtc = event->u.cc;
+ QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(crtc.window);
+ if (!virtualDesktop)
+ // Not for us
+ return;
+
+ QXcbScreen *screen = findScreenForCrtc(crtc.window, crtc.crtc);
+ qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc
+ << "mode" << crtc.mode << "relevant screen" << screen;
+ // Only update geometry when there's a valid mode on the CRTC
+ // CRTC with node mode could mean that output has been disabled, and we'll
+ // get RRNotifyOutputChange notification for that.
+ if (screen && crtc.mode) {
+ if (crtc.rotation == XCB_RANDR_ROTATION_ROTATE_90 ||
+ crtc.rotation == XCB_RANDR_ROTATION_ROTATE_270)
+ std::swap(crtc.width, crtc.height);
+ screen->updateGeometry(QRect(crtc.x, crtc.y, crtc.width, crtc.height), crtc.rotation);
+ if (screen->mode() != crtc.mode)
+ screen->updateRefreshRate(crtc.mode);
+ }
+
+ } else if (event->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) {
+ xcb_randr_output_change_t output = event->u.oc;
+ QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(output.window);
+ if (!virtualDesktop)
+ // Not for us
+ return;
+
+ QXcbScreen *screen = findScreenForOutput(output.window, output.output);
+ qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_OUTPUT_CHANGE:" << output.output;
+
+ if (screen && output.connection == XCB_RANDR_CONNECTION_DISCONNECTED) {
+ qCDebug(lcQpaScreen) << "screen" << screen->name() << "has been disconnected";
+ destroyScreen(screen);
+ } else if (!screen && output.connection == XCB_RANDR_CONNECTION_CONNECTED) {
+ // New XRandR output is available and it's enabled
+ if (output.crtc != XCB_NONE && output.mode != XCB_NONE) {
+ auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
+ output.output, output.config_timestamp);
+ // Find a fake screen
+ const auto scrs = virtualDesktop->screens();
+ for (QPlatformScreen *scr : scrs) {
+ QXcbScreen *xcbScreen = static_cast<QXcbScreen *>(scr);
+ if (xcbScreen->output() == XCB_NONE) {
+ screen = xcbScreen;
+ break;
+ }
+ }
+
+ if (screen) {
+ QString nameWas = screen->name();
+ // Transform the fake screen into a physical screen
+ screen->setOutput(output.output, outputInfo.get());
+ updateScreen(screen, output);
+ qCDebug(lcQpaScreen) << "output" << screen->name()
+ << "is connected and enabled; was fake:" << nameWas;
+ } else {
+ screen = createScreen(virtualDesktop, output, outputInfo.get());
+ qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled";
+ }
+ QHighDpiScaling::updateHighDpiScaling();
+ }
+ } else if (screen) {
+ if (output.crtc == XCB_NONE && output.mode == XCB_NONE) {
+ // Screen has been disabled
+ auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
+ output.output, output.config_timestamp);
+ if (outputInfo->crtc == XCB_NONE) {
+ qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled";
+ destroyScreen(screen);
+ } else {
+ qCDebug(lcQpaScreen) << "output" << screen->name() << "has been temporarily disabled for the mode switch";
+ // Reset crtc to skip RRCrtcChangeNotify events,
+ // because they may be invalid in the middle of the mode switch
+ screen->setCrtc(XCB_NONE);
+ }
+ } else {
+ updateScreen(screen, output);
+ qCDebug(lcQpaScreen) << "output has changed" << screen;
+ }
+ }
+
+ qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name();
+ }
+}
+
+bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output)
+{
+ auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), rootWindow);
+ if (!primary)
+ qWarning("failed to get the primary output of the screen");
+
+ const bool isPrimary = primary ? (primary->output == output) : false;
+
+ return isPrimary;
+}
+
+void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange)
+{
+ screen->setCrtc(outputChange.crtc); // Set the new crtc, because it can be invalid
+ screen->updateGeometry(outputChange.config_timestamp);
+ 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() == primaryScreenNumber()) {
+ if (!screen->isPrimary() && checkOutputIsPrimary(outputChange.window, outputChange.output)) {
+ screen->setPrimary(true);
+
+ // If the screen became primary, reshuffle the order in QGuiApplicationPrivate
+ const int idx = m_screens.indexOf(screen);
+ if (idx > 0) {
+ qAsConst(m_screens).first()->setPrimary(false);
+ m_screens.swap(0, idx);
+ }
+ screen->virtualDesktop()->setPrimaryScreen(screen);
+ QXcbIntegration::instance()->setPrimaryScreen(screen);
+ }
+ }
+}
+
+QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop,
+ const xcb_randr_output_change_t &outputChange,
+ xcb_randr_get_output_info_reply_t *outputInfo)
+{
+ 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() == primaryScreenNumber())
+ screen->setPrimary(checkOutputIsPrimary(outputChange.window, outputChange.output));
+
+ if (screen->isPrimary()) {
+ if (!m_screens.isEmpty())
+ qAsConst(m_screens).first()->setPrimary(false);
+
+ m_screens.prepend(screen);
+ } else {
+ m_screens.append(screen);
+ }
+ virtualDesktop->addScreen(screen);
+ QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary());
+
+ return screen;
+}
+
+void QXcbConnection::destroyScreen(QXcbScreen *screen)
+{
+ QXcbVirtualDesktop *virtualDesktop = screen->virtualDesktop();
+ if (virtualDesktop->screens().count() == 1) {
+ // If there are no other screens on the same virtual desktop,
+ // then transform the physical screen into a fake screen.
+ const QString nameWas = screen->name();
+ screen->setOutput(XCB_NONE, nullptr);
+ qCDebug(lcQpaScreen) << "transformed" << nameWas << "to fake" << screen;
+ } else {
+ // There is more than one screen on the same virtual desktop, remove the screen
+ m_screens.removeOne(screen);
+ virtualDesktop->removeScreen(screen);
+
+ // When primary screen is removed, set the new primary screen
+ // which belongs to the primary virtual desktop.
+ if (screen->isPrimary()) {
+ QXcbScreen *newPrimary = static_cast<QXcbScreen *>(virtualDesktop->screens().at(0));
+ newPrimary->setPrimary(true);
+ const int idx = m_screens.indexOf(newPrimary);
+ if (idx > 0)
+ m_screens.swap(0, idx);
+ QXcbIntegration::instance()->setPrimaryScreen(newPrimary);
+ }
+
+ QXcbIntegration::instance()->destroyScreen(screen);
+ }
+}
+
+void QXcbConnection::initializeScreens()
+{
+ 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) {
+ // Each "screen" in xcb terminology is a virtual desktop,
+ // potentially a collection of separate juxtaposed monitors.
+ // But we want a separate QScreen for each output (e.g. DVI-I-1, VGA-1, etc.)
+ // which will become virtual siblings.
+ xcb_screen_t *xcbScreen = it.data;
+ QXcbVirtualDesktop *virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber);
+ m_virtualDesktops.append(virtualDesktop);
+ QList<QPlatformScreen *> siblings;
+ if (hasXRandr()) {
+ // 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.
+ auto resources_current = Q_XCB_REPLY(xcb_randr_get_screen_resources_current,
+ xcb_connection(), xcbScreen->root);
+ if (!resources_current) {
+ qWarning("failed to get the current screen resources");
+ } else {
+ xcb_timestamp_t timestamp = 0;
+ xcb_randr_output_t *outputs = nullptr;
+ int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.get());
+ if (outputCount) {
+ timestamp = resources_current->config_timestamp;
+ outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.get());
+ } else {
+ auto resources = Q_XCB_REPLY(xcb_randr_get_screen_resources,
+ xcb_connection(), xcbScreen->root);
+ if (!resources) {
+ qWarning("failed to get the screen resources");
+ } else {
+ timestamp = resources->config_timestamp;
+ outputCount = xcb_randr_get_screen_resources_outputs_length(resources.get());
+ outputs = xcb_randr_get_screen_resources_outputs(resources.get());
+ }
+ }
+
+ if (outputCount) {
+ auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), xcbScreen->root);
+ if (!primary) {
+ qWarning("failed to get the primary output of the screen");
+ } else {
+ for (int i = 0; i < outputCount; i++) {
+ auto output = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_output_info,
+ xcb_connection(), outputs[i], timestamp);
+ // Invalid, disconnected or disabled output
+ if (!output)
+ continue;
+
+ if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
+ qCDebug(lcQpaScreen, "Output %s is not connected", qPrintable(
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
+ xcb_randr_get_output_info_name_length(output.get()))));
+ continue;
+ }
+
+ if (output->crtc == XCB_NONE) {
+ qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable(
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
+ xcb_randr_get_output_info_name_length(output.get()))));
+ continue;
+ }
+
+ QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.get());
+ siblings << screen;
+ m_screens << screen;
+
+ // There can be multiple outputs per screen, use either
+ // the first or an exact match. An exact match isn't
+ // always available if primary->output is XCB_NONE
+ // or currently disconnected output.
+ if (primaryScreenNumber() == xcbScreenNumber) {
+ if (!primaryScreen || (primary && outputs[i] == primary->output)) {
+ if (primaryScreen)
+ primaryScreen->setPrimary(false);
+ primaryScreen = screen;
+ primaryScreen->setPrimary(true);
+ siblings.prepend(siblings.takeLast());
+ }
+ }
+ }
+ }
+ }
+ }
+ } else if (hasXinerama()) {
+ // Xinerama is available
+ 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) {
+ xcb_xinerama_screen_info_t *screen_info = it.data;
+ QXcbScreen *screen = new QXcbScreen(this, virtualDesktop,
+ XCB_NONE, nullptr,
+ screen_info, it.index);
+ siblings << screen;
+ m_screens << screen;
+ xcb_xinerama_screen_info_next(&it);
+ }
+ }
+ }
+ if (siblings.isEmpty()) {
+ // If there are no XRandR outputs or XRandR extension is missing,
+ // then create a fake/legacy screen.
+ QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, nullptr);
+ qCDebug(lcQpaScreen) << "created fake screen" << screen;
+ m_screens << screen;
+ if (primaryScreenNumber() == xcbScreenNumber) {
+ primaryScreen = screen;
+ primaryScreen->setPrimary(true);
+ }
+ siblings << screen;
+ }
+ virtualDesktop->setScreens(siblings);
+ xcb_screen_next(&it);
+ ++xcbScreenNumber;
+ } // for each xcb screen
+
+ for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops))
+ virtualDesktop->subscribeToXFixesSelectionNotify();
+
+ if (m_virtualDesktops.isEmpty()) {
+ qFatal("QXcbConnection: no screens available");
+ } else {
+ // Ensure the primary screen is first on the list
+ if (primaryScreen) {
+ if (qAsConst(m_screens).first() != primaryScreen) {
+ m_screens.removeOne(primaryScreen);
+ m_screens.prepend(primaryScreen);
+ }
+ }
+
+ // Push the screens to QGuiApplication
+ for (QXcbScreen *screen : qAsConst(m_screens)) {
+ qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")";
+ QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary());
+ }
+
+ qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name();
+ }
+}
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index 6a5248b8f1..04ddd3c98c 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -47,59 +47,26 @@
#include <QDebug>
#include <cmath>
-#include <X11/extensions/XInput2.h>
-#include <X11/extensions/XI2proto.h>
+#include <xcb/xinput.h>
-void QXcbConnection::initializeXInput2()
-{
- Display *xDisplay = static_cast<Display *>(m_xlib_display);
- if (XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) {
- int xiMajor = 2;
-#if defined(XCB_USE_XINPUT22)
- m_xi2Minor = 2; // for touch support 2.2 is enough
-#elif defined(XCB_USE_XINPUT21)
- m_xi2Minor = 1; // for smooth scrolling 2.1 is enough
-#else
- m_xi2Minor = 0; // for tablet support 2.0 is enough
-#endif
- qCDebug(lcQpaXInput, "Plugin build with support for XInput 2 version up "
- "to %d.%d", xiMajor, m_xi2Minor);
-
- switch (XIQueryVersion(xDisplay, &xiMajor, &m_xi2Minor)) {
- case Success:
- // Server's supported version can be lower than the version we have
- // announced to support. In this case Qt client will be limited by
- // X server's supported version.
- qCDebug(lcQpaXInput, "Using XInput version %d.%d", xiMajor, m_xi2Minor);
- m_xi2Enabled = true;
- xi2SetupDevices();
- xi2SelectStateEvents();
- break;
- case BadRequest: // Must be an X server with XInput 1
- qCDebug(lcQpaXInput, "X server does not support XInput 2");
- break;
- default: // BadValue
- qCDebug(lcQpaXInput, "Internal error");
- break;
- }
- }
-}
+using qt_xcb_input_device_event_t = xcb_input_button_press_event_t;
+
+struct qt_xcb_input_event_mask_t {
+ xcb_input_event_mask_t header;
+ uint32_t mask;
+};
void QXcbConnection::xi2SelectStateEvents()
{
// These state events do not depend on a specific X window, but are global
// for the X client's (application's) state.
- unsigned int bitMask = 0;
- unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
- XIEventMask xiEventMask;
- bitMask = XI_HierarchyChangedMask;
- bitMask |= XI_DeviceChangedMask;
- bitMask |= XI_PropertyEventMask;
- xiEventMask.deviceid = XIAllDevices;
- xiEventMask.mask_len = sizeof(bitMask);
- xiEventMask.mask = xiBitMask;
- Display *dpy = static_cast<Display *>(m_xlib_display);
- XISelectEvents(dpy, DefaultRootWindow(dpy), &xiEventMask, 1);
+ qt_xcb_input_event_mask_t xiEventMask;
+ xiEventMask.header.deviceid = XCB_INPUT_DEVICE_ALL;
+ xiEventMask.header.mask_len = 1;
+ 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(xcb_connection(), rootWindow(), 1, &xiEventMask.header);
}
void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
@@ -107,38 +74,42 @@ void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
if (window == rootWindow())
return;
- unsigned int bitMask = 0;
- unsigned char *xiBitMask = reinterpret_cast<unsigned char *>(&bitMask);
- bitMask |= XI_ButtonPressMask;
- bitMask |= XI_ButtonReleaseMask;
- bitMask |= XI_MotionMask;
+ uint32_t bitMask = XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS;
+ bitMask |= XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE;
+ bitMask |= XCB_INPUT_XI_EVENT_MASK_MOTION;
// There is a check for enter/leave events in plain xcb enter/leave event handler,
// core enter/leave events will be ignored in this case.
- bitMask |= XI_EnterMask;
- bitMask |= XI_LeaveMask;
-#ifdef XCB_USE_XINPUT22
+ bitMask |= XCB_INPUT_XI_EVENT_MASK_ENTER;
+ bitMask |= XCB_INPUT_XI_EVENT_MASK_LEAVE;
if (isAtLeastXI22()) {
- bitMask |= XI_TouchBeginMask;
- bitMask |= XI_TouchUpdateMask;
- bitMask |= XI_TouchEndMask;
+ bitMask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN;
+ bitMask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE;
+ bitMask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_END;
}
-#endif
- XIEventMask mask;
- mask.mask_len = sizeof(bitMask);
- mask.mask = xiBitMask;
- mask.deviceid = XIAllMasterDevices;
- Display *dpy = static_cast<Display *>(m_xlib_display);
- Status result = XISelectEvents(dpy, window, &mask, 1);
- if (result == Success)
+ qt_xcb_input_event_mask_t mask;
+ mask.header.deviceid = XCB_INPUT_DEVICE_ALL_MASTER;
+ mask.header.mask_len = 1;
+ mask.mask = bitMask;
+ xcb_void_cookie_t 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);
+ } else {
QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
- else
- qCDebug(lcQpaXInput, "failed to select events, window %x, result %d", window, result);
+ }
+}
+
+static inline qreal fixed3232ToReal(xcb_input_fp3232_t val)
+{
+ return qreal(val.integral) + qreal(val.frac) / (1ULL << 32);
}
void QXcbConnection::xi2SetupDevice(void *info, bool removeExisting)
{
- XIDeviceInfo *deviceInfo = reinterpret_cast<XIDeviceInfo *>(info);
+ auto *deviceInfo = reinterpret_cast<xcb_input_xi_device_info_t *>(info);
if (removeExisting) {
#if QT_CONFIG(tabletevent)
for (int i = 0; i < m_tabletData.count(); ++i) {
@@ -152,53 +123,54 @@ void QXcbConnection::xi2SetupDevice(void *info, bool removeExisting)
m_touchDevices.remove(deviceInfo->deviceid);
}
- qCDebug(lcQpaXInputDevices) << "input device " << deviceInfo->name << "ID" << deviceInfo->deviceid;
+ qCDebug(lcQpaXInputDevices) << "input device " << xcb_input_xi_device_info_name(deviceInfo) << "ID" << deviceInfo->deviceid;
#if QT_CONFIG(tabletevent)
TabletData tabletData;
#endif
ScrollingDevice scrollingDevice;
- for (int c = 0; c < deviceInfo->num_classes; ++c) {
- XIAnyClassInfo *classinfo = deviceInfo->classes[c];
+ auto classes_it = xcb_input_xi_device_info_classes_iterator(deviceInfo);
+ for (; classes_it.rem; xcb_input_device_class_next(&classes_it)) {
+ xcb_input_device_class_t *classinfo = classes_it.data;
switch (classinfo->type) {
- case XIValuatorClass: {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
+ case XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR: {
+ auto *vci = reinterpret_cast<xcb_input_valuator_class_t *>(classinfo);
const int valuatorAtom = qatom(vci->label);
qCDebug(lcQpaXInputDevices) << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms);
#if QT_CONFIG(tabletevent)
if (valuatorAtom < QXcbAtom::NAtoms) {
TabletData::ValuatorClassInfo info;
- info.minVal = vci->min;
- info.maxVal = vci->max;
+ info.minVal = fixed3232ToReal(vci->min);
+ info.maxVal = fixed3232ToReal(vci->max);
info.number = vci->number;
tabletData.valuatorInfo[valuatorAtom] = info;
}
#endif // QT_CONFIG(tabletevent)
if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
- scrollingDevice.lastScrollPosition.setX(vci->value);
+ scrollingDevice.lastScrollPosition.setX(fixed3232ToReal(vci->value));
else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel)
- scrollingDevice.lastScrollPosition.setY(vci->value);
+ scrollingDevice.lastScrollPosition.setY(fixed3232ToReal(vci->value));
break;
}
-#ifdef XCB_USE_XINPUT21
- case XIScrollClass: {
- XIScrollClassInfo *sci = reinterpret_cast<XIScrollClassInfo *>(classinfo);
- if (sci->scroll_type == XIScrollTypeVertical) {
+ case XCB_INPUT_DEVICE_CLASS_TYPE_SCROLL: {
+ auto *sci = reinterpret_cast<xcb_input_scroll_class_t *>(classinfo);
+ if (sci->scroll_type == XCB_INPUT_SCROLL_TYPE_VERTICAL) {
scrollingDevice.orientations |= Qt::Vertical;
scrollingDevice.verticalIndex = sci->number;
- scrollingDevice.verticalIncrement = sci->increment;
- }
- else if (sci->scroll_type == XIScrollTypeHorizontal) {
+ scrollingDevice.verticalIncrement = fixed3232ToReal(sci->increment);
+ } else if (sci->scroll_type == XCB_INPUT_SCROLL_TYPE_HORIZONTAL) {
scrollingDevice.orientations |= Qt::Horizontal;
scrollingDevice.horizontalIndex = sci->number;
- scrollingDevice.horizontalIncrement = sci->increment;
+ scrollingDevice.horizontalIncrement = fixed3232ToReal(sci->increment);
}
break;
}
- case XIButtonClass: {
- XIButtonClassInfo *bci = reinterpret_cast<XIButtonClassInfo *>(classinfo);
+ case XCB_INPUT_DEVICE_CLASS_TYPE_BUTTON: {
+ auto *bci = reinterpret_cast<xcb_input_button_class_t *>(classinfo);
+ xcb_atom_t *labels = 0;
if (bci->num_buttons >= 5) {
- Atom label4 = bci->labels[3];
- Atom label5 = bci->labels[4];
+ labels = xcb_input_button_class_labels(bci);
+ xcb_atom_t label4 = labels[3];
+ xcb_atom_t label5 = labels[4];
// Some drivers have no labels on the wheel buttons, some have no label on just one and some have no label on
// button 4 and the wrong one on button 5. So we just check that they are not labelled with unrelated buttons.
if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp || qatom(label4) == QXcbAtom::ButtonWheelDown) &&
@@ -206,23 +178,20 @@ void QXcbConnection::xi2SetupDevice(void *info, bool removeExisting)
scrollingDevice.legacyOrientations |= Qt::Vertical;
}
if (bci->num_buttons >= 7) {
- Atom label6 = bci->labels[5];
- Atom label7 = bci->labels[6];
+ xcb_atom_t label6 = labels[5];
+ xcb_atom_t label7 = labels[6];
if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight))
scrollingDevice.legacyOrientations |= Qt::Horizontal;
}
qCDebug(lcQpaXInputDevices, " has %d buttons", bci->num_buttons);
break;
}
-#endif
- case XIKeyClass:
+ case XCB_INPUT_DEVICE_CLASS_TYPE_KEY:
qCDebug(lcQpaXInputDevices) << " it's a keyboard";
break;
-#ifdef XCB_USE_XINPUT22
- case XITouchClass:
+ case XCB_INPUT_DEVICE_CLASS_TYPE_TOUCH:
// will be handled in populateTouchDevices()
break;
-#endif
default:
qCDebug(lcQpaXInputDevices) << " has class" << classinfo->type;
break;
@@ -237,7 +206,8 @@ void QXcbConnection::xi2SetupDevice(void *info, bool removeExisting)
isTablet = true;
// But we need to be careful not to take the touch and tablet-button devices as tablets.
- QByteArray name = QByteArray(deviceInfo->name).toLower();
+ QByteArray name = QByteArray(xcb_input_xi_device_info_name(deviceInfo),
+ xcb_input_xi_device_info_name_length(deviceInfo)).toLower();
QString dbgType = QLatin1String("UNKNOWN");
if (name.contains("eraser")) {
isTablet = true;
@@ -281,7 +251,6 @@ void QXcbConnection::xi2SetupDevice(void *info, bool removeExisting)
}
#endif // QT_CONFIG(tabletevent)
-#ifdef XCB_USE_XINPUT21
if (scrollingDevice.orientations || scrollingDevice.legacyOrientations) {
scrollingDevice.deviceId = deviceInfo->deviceid;
// Only use legacy wheel button events when we don't have real scroll valuators.
@@ -289,7 +258,6 @@ void QXcbConnection::xi2SetupDevice(void *info, bool removeExisting)
m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice);
qCDebug(lcQpaXInputDevices) << " it's a scrolling device";
}
-#endif
if (!isTablet) {
TouchDeviceData *dev = populateTouchDevices(deviceInfo);
@@ -315,23 +283,28 @@ void QXcbConnection::xi2SetupDevices()
#endif
m_scrollingDevices.clear();
m_touchDevices.clear();
-
- Display *xDisplay = static_cast<Display *>(m_xlib_display);
- int deviceCount = 0;
- XIDeviceInfo *devices = XIQueryDevice(xDisplay, XIAllDevices, &deviceCount);
m_xiMasterPointerIds.clear();
- for (int i = 0; i < deviceCount; ++i) {
- XIDeviceInfo deviceInfo = devices[i];
- if (deviceInfo.use == XIMasterPointer) {
- m_xiMasterPointerIds.append(deviceInfo.deviceid);
+
+ 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;
+ }
+
+ auto it = xcb_input_xi_query_device_infos_iterator(reply.get());
+ for (; it.rem; xcb_input_xi_device_info_next(&it)) {
+ xcb_input_xi_device_info_t *deviceInfo = it.data;
+ if (deviceInfo->type == XCB_INPUT_DEVICE_TYPE_MASTER_POINTER) {
+ m_xiMasterPointerIds.append(deviceInfo->deviceid);
continue;
}
- if (deviceInfo.use == XISlavePointer) // only slave pointer devices are relevant here
- xi2SetupDevice(&deviceInfo, false);
+ // only slave pointer devices are relevant here
+ if (deviceInfo->type == XCB_INPUT_DEVICE_TYPE_SLAVE_POINTER)
+ xi2SetupDevice(deviceInfo, false);
}
+
if (m_xiMasterPointerIds.size() > 1)
qCDebug(lcQpaXInputDevices) << "multi-pointer X detected";
- XIFreeDeviceInfo(devices);
}
/*! \internal
@@ -376,70 +349,64 @@ void QXcbConnection::xi2SelectDeviceEventsCompatibility(xcb_window_t window)
if (window == rootWindow())
return;
- unsigned int mask = 0;
- unsigned char *bitMask = reinterpret_cast<unsigned char *>(&mask);
- Display *dpy = static_cast<Display *>(m_xlib_display);
+ uint32_t mask = 0;
-#ifdef XCB_USE_XINPUT22
if (isAtLeastXI22()) {
- mask |= XI_TouchBeginMask;
- mask |= XI_TouchUpdateMask;
- mask |= XI_TouchEndMask;
-
- XIEventMask xiMask;
- xiMask.mask_len = sizeof(mask);
- xiMask.mask = bitMask;
- xiMask.deviceid = XIAllMasterDevices;
- Status result = XISelectEvents(dpy, window, &xiMask, 1);
- if (result == Success)
+ mask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN;
+ mask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE;
+ mask |= XCB_INPUT_XI_EVENT_MASK_TOUCH_END;
+
+ qt_xcb_input_event_mask_t xiMask;
+ xiMask.header.deviceid = XCB_INPUT_DEVICE_ALL_MASTER;
+ xiMask.header.mask_len = 1;
+ xiMask.mask = mask;
+
+ xcb_void_cookie_t 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);
+ } else {
QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
- else
- qCDebug(lcQpaXInput, "failed to select events, window %x, result %d", window, result);
+ }
}
-#endif
- mask = XI_ButtonPressMask;
- mask |= XI_ButtonReleaseMask;
- mask |= XI_MotionMask;
+ mask = XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS;
+ mask |= XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE;
+ mask |= XCB_INPUT_XI_EVENT_MASK_MOTION;
#if QT_CONFIG(tabletevent)
QSet<int> tabletDevices;
if (!m_tabletData.isEmpty()) {
const int nrTablets = m_tabletData.count();
- QVector<XIEventMask> xiEventMask(nrTablets);
+ QVector<qt_xcb_input_event_mask_t> xiEventMask(nrTablets);
for (int i = 0; i < nrTablets; ++i) {
int deviceId = m_tabletData.at(i).deviceId;
tabletDevices.insert(deviceId);
- xiEventMask[i].deviceid = deviceId;
- xiEventMask[i].mask_len = sizeof(mask);
- xiEventMask[i].mask = bitMask;
+ xiEventMask[i].header.deviceid = deviceId;
+ xiEventMask[i].header.mask_len = 1;
+ xiEventMask[i].mask = mask;
}
- XISelectEvents(dpy, window, xiEventMask.data(), nrTablets);
+ xcb_input_xi_select_events(xcb_connection(), window, nrTablets, &(xiEventMask.data()->header));
}
#endif
-#ifdef XCB_USE_XINPUT21
if (!m_scrollingDevices.isEmpty()) {
- QVector<XIEventMask> xiEventMask(m_scrollingDevices.size());
+ QVector<qt_xcb_input_event_mask_t> xiEventMask(m_scrollingDevices.size());
int i = 0;
for (const ScrollingDevice& scrollingDevice : qAsConst(m_scrollingDevices)) {
#if QT_CONFIG(tabletevent)
if (tabletDevices.contains(scrollingDevice.deviceId))
continue; // All necessary events are already captured.
#endif
- xiEventMask[i].deviceid = scrollingDevice.deviceId;
- xiEventMask[i].mask_len = sizeof(mask);
- xiEventMask[i].mask = bitMask;
+ xiEventMask[i].header.deviceid = scrollingDevice.deviceId;
+ xiEventMask[i].header.mask_len = 1;
+ xiEventMask[i].mask = mask;
i++;
}
- XISelectEvents(dpy, window, xiEventMask.data(), i);
+ xcb_input_xi_select_events(xcb_connection(), window, i, &(xiEventMask.data()->header));
}
-#endif
-
-#if !QT_CONFIG(tabletevent) && !defined(XCB_USE_XINPUT21)
- Q_UNUSED(bitMask);
- Q_UNUSED(dpy);
-#endif
}
QXcbConnection::TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
@@ -452,39 +419,38 @@ QXcbConnection::TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info)
{
- XIDeviceInfo *deviceinfo = reinterpret_cast<XIDeviceInfo *>(info);
+ auto *deviceinfo = reinterpret_cast<xcb_input_xi_device_info_t *>(info);
QTouchDevice::Capabilities caps = 0;
int type = -1;
int maxTouchPoints = 1;
bool isTouchDevice = false;
bool hasRelativeCoords = false;
TouchDeviceData dev;
- for (int i = 0; i < deviceinfo->num_classes; ++i) {
- XIAnyClassInfo *classinfo = deviceinfo->classes[i];
+ auto classes_it = xcb_input_xi_device_info_classes_iterator(deviceinfo);
+ for (; classes_it.rem; xcb_input_device_class_next(&classes_it)) {
+ xcb_input_device_class_t *classinfo = classes_it.data;
switch (classinfo->type) {
-#ifdef XCB_USE_XINPUT22
- case XITouchClass: {
- XITouchClassInfo *tci = reinterpret_cast<XITouchClassInfo *>(classinfo);
+ case XCB_INPUT_DEVICE_CLASS_TYPE_TOUCH: {
+ auto *tci = reinterpret_cast<xcb_input_touch_class_t *>(classinfo);
maxTouchPoints = tci->num_touches;
qCDebug(lcQpaXInputDevices, " has touch class with mode %d", tci->mode);
switch (tci->mode) {
- case XIDependentTouch:
+ case XCB_INPUT_TOUCH_MODE_DEPENDENT:
type = QTouchDevice::TouchPad;
break;
- case XIDirectTouch:
+ case XCB_INPUT_TOUCH_MODE_DIRECT:
type = QTouchDevice::TouchScreen;
break;
}
break;
}
-#endif // XCB_USE_XINPUT22
- case XIValuatorClass: {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classinfo);
+ case XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR: {
+ auto *vci = reinterpret_cast<xcb_input_valuator_class_t *>(classinfo);
const QXcbAtom::Atom valuatorAtom = qatom(vci->label);
if (valuatorAtom < QXcbAtom::NAtoms) {
TouchDeviceData::ValuatorClassInfo info;
- info.min = vci->min;
- info.max = vci->max;
+ info.min = fixed3232ToReal(vci->min);
+ info.max = fixed3232ToReal(vci->max);
info.number = vci->number;
info.label = valuatorAtom;
dev.valuatorInfo.append(info);
@@ -502,16 +468,16 @@ QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info
caps |= QTouchDevice::Pressure;
else if (valuatorAtom == QXcbAtom::RelX) {
hasRelativeCoords = true;
- dev.size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
+ dev.size.setWidth((fixed3232ToReal(vci->max) - fixed3232ToReal(vci->min)) * 1000.0 / vciResolution);
} else if (valuatorAtom == QXcbAtom::RelY) {
hasRelativeCoords = true;
- dev.size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
+ dev.size.setHeight((fixed3232ToReal(vci->max) - fixed3232ToReal(vci->min)) * 1000.0 / vciResolution);
} else if (valuatorAtom == QXcbAtom::AbsX) {
caps |= QTouchDevice::Position;
- dev.size.setWidth((vci->max - vci->min) * 1000.0 / vciResolution);
+ dev.size.setWidth((fixed3232ToReal(vci->max) - fixed3232ToReal(vci->min)) * 1000.0 / vciResolution);
} else if (valuatorAtom == QXcbAtom::AbsY) {
caps |= QTouchDevice::Position;
- dev.size.setHeight((vci->max - vci->min) * 1000.0 / vciResolution);
+ dev.size.setHeight((fixed3232ToReal(vci->max) - fixed3232ToReal(vci->min)) * 1000.0 / vciResolution);
}
break;
}
@@ -530,7 +496,8 @@ QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info
if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
dev.qtTouchDevice = new QTouchDevice;
- dev.qtTouchDevice->setName(QString::fromUtf8(deviceinfo->name));
+ dev.qtTouchDevice->setName(QString::fromUtf8(xcb_input_xi_device_info_name(deviceinfo),
+ xcb_input_xi_device_info_name_length(deviceinfo)));
dev.qtTouchDevice->setType((QTouchDevice::DeviceType)type);
dev.qtTouchDevice->setCapabilities(caps);
dev.qtTouchDevice->setMaximumTouchPoints(maxTouchPoints);
@@ -543,89 +510,83 @@ QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info
return isTouchDevice ? &m_touchDevices[deviceinfo->deviceid] : nullptr;
}
-#if defined(XCB_USE_XINPUT21) || QT_CONFIG(tabletevent)
-static inline qreal fixed1616ToReal(FP1616 val)
+#if QT_CONFIG(tabletevent)
+static inline qreal fixed1616ToReal(xcb_input_fp1616_t val)
{
return qreal(val) / 0x10000;
}
-#endif // defined(XCB_USE_XINPUT21) || QT_CONFIG(tabletevent)
+#endif // QT_CONFIG(tabletevent)
void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
{
- xi2PrepareXIGenericDeviceEvent(event);
- xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
+ auto *xiEvent = reinterpret_cast<qt_xcb_input_device_event_t *>(event);
int sourceDeviceId = xiEvent->deviceid; // may be the master id
- xXIDeviceEvent *xiDeviceEvent = 0;
- xXIEnterEvent *xiEnterEvent = 0;
+ qt_xcb_input_device_event_t *xiDeviceEvent = nullptr;
+ xcb_input_enter_event_t *xiEnterEvent = nullptr;
QXcbWindowEventListener *eventListener = 0;
- switch (xiEvent->evtype) {
- case XI_ButtonPress:
- case XI_ButtonRelease:
- case XI_Motion:
-#ifdef XCB_USE_XINPUT22
- case XI_TouchBegin:
- case XI_TouchUpdate:
- case XI_TouchEnd:
-#endif
+ switch (xiEvent->event_type) {
+ case XCB_INPUT_BUTTON_PRESS:
+ case XCB_INPUT_BUTTON_RELEASE:
+ case XCB_INPUT_MOTION:
+ case XCB_INPUT_TOUCH_BEGIN:
+ case XCB_INPUT_TOUCH_UPDATE:
+ case XCB_INPUT_TOUCH_END:
{
- xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
+ xiDeviceEvent = xiEvent;
eventListener = windowEventListenerFromId(xiDeviceEvent->event);
sourceDeviceId = xiDeviceEvent->sourceid; // use the actual device id instead of the master
break;
}
- case XI_Enter:
- case XI_Leave: {
- xiEnterEvent = reinterpret_cast<xXIEnterEvent *>(event);
+ case XCB_INPUT_ENTER:
+ case XCB_INPUT_LEAVE: {
+ xiEnterEvent = reinterpret_cast<xcb_input_enter_event_t *>(event);
eventListener = windowEventListenerFromId(xiEnterEvent->event);
sourceDeviceId = xiEnterEvent->sourceid; // use the actual device id instead of the master
break;
}
- case XI_HierarchyChanged:
- xi2HandleHierarchyEvent(xiEvent);
+ case XCB_INPUT_HIERARCHY:
+ xi2HandleHierarchyEvent(event);
return;
- case XI_DeviceChanged:
- xi2HandleDeviceChangedEvent(xiEvent);
+ case XCB_INPUT_DEVICE_CHANGED:
+ xi2HandleDeviceChangedEvent(event);
return;
default:
break;
}
if (eventListener) {
- long result = 0;
- if (eventListener->handleGenericEvent(reinterpret_cast<xcb_generic_event_t *>(event), &result))
+ if (eventListener->handleNativeEvent(reinterpret_cast<xcb_generic_event_t *>(event)))
return;
}
#if QT_CONFIG(tabletevent)
if (!xiEnterEvent) {
QXcbConnection::TabletData *tablet = tabletDataForDevice(sourceDeviceId);
- if (tablet && xi2HandleTabletEvent(xiEvent, tablet))
+ if (tablet && xi2HandleTabletEvent(event, tablet))
return;
}
#endif // QT_CONFIG(tabletevent)
-#ifdef XCB_USE_XINPUT21
if (ScrollingDevice *device = scrollingDeviceForId(sourceDeviceId))
- xi2HandleScrollEvent(xiEvent, *device);
-#endif // XCB_USE_XINPUT21
+ xi2HandleScrollEvent(event, *device);
-#ifdef XCB_USE_XINPUT22
if (xiDeviceEvent) {
- switch (xiDeviceEvent->evtype) {
- case XI_ButtonPress:
- case XI_ButtonRelease:
- case XI_Motion:
- if (!xi2MouseEventsDisabled() && eventListener && !(xiDeviceEvent->flags & XIPointerEmulated))
+ switch (xiDeviceEvent->event_type) {
+ case XCB_INPUT_BUTTON_PRESS:
+ case XCB_INPUT_BUTTON_RELEASE:
+ case XCB_INPUT_MOTION:
+ if (!xi2MouseEventsDisabled() && eventListener &&
+ !(xiDeviceEvent->flags & XCB_INPUT_POINTER_EVENT_FLAGS_POINTER_EMULATED))
eventListener->handleXIMouseEvent(event);
break;
- case XI_TouchBegin:
- case XI_TouchUpdate:
- case XI_TouchEnd:
+ case XCB_INPUT_TOUCH_BEGIN:
+ case XCB_INPUT_TOUCH_UPDATE:
+ case XCB_INPUT_TOUCH_END:
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f on window %x",
- event->event_type, xiDeviceEvent->sequenceNumber, xiDeviceEvent->detail,
+ event->event_type, xiDeviceEvent->sequence, xiDeviceEvent->detail,
fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y),
fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y),xiDeviceEvent->event);
if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event))
@@ -633,14 +594,13 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
break;
}
} else if (xiEnterEvent && !xi2MouseEventsDisabled() && eventListener) {
- switch (xiEnterEvent->evtype) {
- case XI_Enter:
- case XI_Leave:
+ switch (xiEnterEvent->event_type) {
+ case XCB_INPUT_ENTER:
+ case XCB_INPUT_LEAVE:
eventListener->handleXIEnterLeave(event);
break;
}
}
-#endif // XCB_USE_XINPUT22
}
bool QXcbConnection::xi2MouseEventsDisabled() const
@@ -648,10 +608,9 @@ 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();
}
-#ifdef XCB_USE_XINPUT22
bool QXcbConnection::isTouchScreen(int id)
{
auto device = touchDeviceForId(id);
@@ -660,11 +619,11 @@ bool QXcbConnection::isTouchScreen(int id)
void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow)
{
- xXIDeviceEvent *xiDeviceEvent = static_cast<xXIDeviceEvent *>(xiDevEvent);
+ auto *xiDeviceEvent = reinterpret_cast<xcb_input_touch_begin_event_t *>(xiDevEvent);
TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid);
Q_ASSERT(dev);
const bool firstTouch = dev->touchPoints.isEmpty();
- if (xiDeviceEvent->evtype == XI_TouchBegin) {
+ if (xiDeviceEvent->event_type == XCB_INPUT_TOUCH_BEGIN) {
QWindowSystemInterface::TouchPoint tp;
tp.id = xiDeviceEvent->detail % INT_MAX;
tp.state = Qt::TouchPointPressed;
@@ -735,7 +694,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
y = touchPoint.area.center().y();
ny = y / screen->geometry().height();
}
- if (xiDeviceEvent->evtype != XI_TouchEnd) {
+ if (xiDeviceEvent->event_type != XCB_INPUT_TOUCH_END) {
if (!dev->providesTouchOrientation) {
if (w == 0.0)
w = touchPoint.area.width();
@@ -750,8 +709,8 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
}
}
- switch (xiDeviceEvent->evtype) {
- case XI_TouchBegin:
+ switch (xiDeviceEvent->event_type) {
+ case XCB_INPUT_TOUCH_BEGIN:
if (firstTouch) {
dev->firstPressedPosition = QPointF(x, y);
dev->firstPressedNormalPosition = QPointF(nx, ny);
@@ -761,14 +720,12 @@ 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) {
- // Note that XIAllowTouchEvents is known to deadlock with older libXi versions,
- // for details see qtbase/src/plugins/platforms/xcb/README. This has nothing to
- // do with the XInput protocol version, but is a bug in libXi implementation instead.
- XIAllowTouchEvents(static_cast<Display *>(m_xlib_display), xiDeviceEvent->deviceid,
- xiDeviceEvent->detail, xiDeviceEvent->event, XIAcceptTouch);
+ xcb_input_xi_allow_events(xcb_connection(), XCB_CURRENT_TIME, xiDeviceEvent->deviceid,
+ XCB_INPUT_EVENT_MODE_ACCEPT_TOUCH,
+ xiDeviceEvent->detail, xiDeviceEvent->event);
}
break;
- case XI_TouchUpdate:
+ case XCB_INPUT_TOUCH_UPDATE:
if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
dev->size.width() * screen->geometry().width() / screen->physicalSize().width();
@@ -789,14 +746,15 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
xiDeviceEvent->detail == m_startSystemMoveResizeInfo.pointid) {
QXcbWindow *window = platformWindowFromId(m_startSystemMoveResizeInfo.window);
if (window) {
- XIAllowTouchEvents(static_cast<Display *>(m_xlib_display), xiDeviceEvent->deviceid,
- xiDeviceEvent->detail, xiDeviceEvent->event, XIRejectTouch);
+ 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);
m_startSystemMoveResizeInfo.window = XCB_NONE;
}
}
break;
- case XI_TouchEnd:
+ case XCB_INPUT_TOUCH_END:
touchPoint.state = Qt::TouchPointReleased;
if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad && dev->pointPressedPosition.value(touchPoint.id) == QPointF(x, y)) {
qreal dx = (nx - dev->firstPressedNormalPosition.x()) *
@@ -814,7 +772,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents) << " touchpoint " << touchPoint.id << " state " << touchPoint.state << " pos norm " << touchPoint.normalPosition <<
" area " << touchPoint.area << " pressure " << touchPoint.pressure;
- Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective_mods);
+ Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective);
QWindowSystemInterface::handleTouchEvent(platformWindow->window(), xiDeviceEvent->time, dev->qtTouchDevice, dev->touchPoints.values(), modifiers);
if (touchPoint.state == Qt::TouchPointReleased)
// If a touchpoint was released, we can forget it, because the ID won't be reused.
@@ -850,46 +808,46 @@ void QXcbConnection::abortSystemMoveResizeForTouch()
{
m_startSystemMoveResizeInfo.window = XCB_NONE;
}
-#endif // XCB_USE_XINPUT22
bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
{
- Display *xDisplay = static_cast<Display *>(xlib_display());
bool ok = false;
if (grab) { // grab
- XIEventMask evmask;
- unsigned char mask[XIMaskLen(XI_LASTEVENT)];
- evmask.mask = mask;
- evmask.mask_len = sizeof(mask);
- memset(mask, 0, sizeof(mask));
- XISetMask(mask, XI_ButtonPress);
- XISetMask(mask, XI_ButtonRelease);
- XISetMask(mask, XI_Motion);
- XISetMask(mask, XI_Enter);
- XISetMask(mask, XI_Leave);
- XISetMask(mask, XI_TouchBegin);
- XISetMask(mask, XI_TouchUpdate);
- XISetMask(mask, XI_TouchEnd);
+ uint32_t mask = XCB_INPUT_XI_EVENT_MASK_BUTTON_PRESS
+ | XCB_INPUT_XI_EVENT_MASK_BUTTON_RELEASE
+ | XCB_INPUT_XI_EVENT_MASK_MOTION
+ | XCB_INPUT_XI_EVENT_MASK_ENTER
+ | XCB_INPUT_XI_EVENT_MASK_LEAVE
+ | XCB_INPUT_XI_EVENT_MASK_TOUCH_BEGIN
+ | XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE
+ | XCB_INPUT_XI_EVENT_MASK_TOUCH_END;
for (int id : m_xiMasterPointerIds) {
- evmask.deviceid = id;
- Status result = XIGrabDevice(xDisplay, id, w, CurrentTime, None,
- XIGrabModeAsync, XIGrabModeAsync, False, &evmask);
- if (result != Success) {
+ xcb_generic_error_t *error = nullptr;
+ 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(xcb_connection(), cookie, &error);
+ if (error) {
qCDebug(lcQpaXInput, "failed to grab events for device %d on window %x"
- "(result %d)", id, w, result);
+ "(error code %d)", id, w, error->error_code);
+ free(error);
} else {
// Managed to grab at least one of master pointers, that should be enough
// to properly dismiss windows that rely on mouse grabbing.
ok = true;
}
+ free(reply);
}
} else { // ungrab
for (int id : m_xiMasterPointerIds) {
- Status result = XIUngrabDevice(xDisplay, id, CurrentTime);
- if (result != Success)
- qCDebug(lcQpaXInput, "XIUngrabDevice failed - id: %d (result %d)", id, result);
+ 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);
+ }
}
// XIUngrabDevice does not seem to wait for a reply from X server (similar to
// xcb_ungrab_pointer). Ungrabbing won't fail, unless NoSuchExtension error
@@ -906,9 +864,9 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
void QXcbConnection::xi2HandleHierarchyEvent(void *event)
{
- xXIHierarchyEvent *xiEvent = reinterpret_cast<xXIHierarchyEvent *>(event);
+ auto *xiEvent = reinterpret_cast<xcb_input_hierarchy_event_t *>(event);
// We only care about hotplugged devices
- if (!(xiEvent->flags & (XISlaveRemoved | XISlaveAdded)))
+ if (!(xiEvent->flags & (XCB_INPUT_HIERARCHY_MASK_SLAVE_REMOVED | XCB_INPUT_HIERARCHY_MASK_SLAVE_ADDED)))
return;
xi2SetupDevices();
@@ -925,23 +883,19 @@ void QXcbConnection::xi2HandleHierarchyEvent(void *event)
void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
{
- xXIDeviceChangedEvent *xiEvent = reinterpret_cast<xXIDeviceChangedEvent *>(event);
+ auto *xiEvent = reinterpret_cast<xcb_input_device_changed_event_t *>(event);
switch (xiEvent->reason) {
- case XIDeviceChange: {
- int nrDevices = 0;
- Display *dpy = static_cast<Display *>(m_xlib_display);
- XIDeviceInfo* deviceInfo = XIQueryDevice(dpy, xiEvent->sourceid, &nrDevices);
- if (nrDevices <= 0)
+ case XCB_INPUT_CHANGE_REASON_DEVICE_CHANGE: {
+ auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), xiEvent->sourceid);
+ if (!reply || reply->num_infos <= 0)
return;
- xi2SetupDevice(deviceInfo);
- XIFreeDeviceInfo(deviceInfo);
+ auto it = xcb_input_xi_query_device_infos_iterator(reply.get());
+ xi2SetupDevice(it.data);
break;
}
- case XISlaveSwitch: {
-#ifdef XCB_USE_XINPUT21
+ case XCB_INPUT_CHANGE_REASON_SLAVE_SWITCH: {
if (ScrollingDevice *scrollingDevice = scrollingDeviceForId(xiEvent->sourceid))
xi2UpdateScrollingDevice(*scrollingDevice);
-#endif
break;
}
default:
@@ -950,28 +904,28 @@ void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
}
}
-#ifdef XCB_USE_XINPUT21
void QXcbConnection::xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice)
{
- int nrDevices = 0;
- Display *dpy = static_cast<Display *>(m_xlib_display);
- XIDeviceInfo* deviceInfo = XIQueryDevice(dpy, scrollingDevice.deviceId, &nrDevices);
- if (nrDevices <= 0) {
+ 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;
}
QPointF lastScrollPosition;
if (lcQpaXInputEvents().isDebugEnabled())
lastScrollPosition = scrollingDevice.lastScrollPosition;
- for (int c = 0; c < deviceInfo->num_classes; ++c) {
- XIAnyClassInfo *classInfo = deviceInfo->classes[c];
- if (classInfo->type == XIValuatorClass) {
- XIValuatorClassInfo *vci = reinterpret_cast<XIValuatorClassInfo *>(classInfo);
+
+ xcb_input_xi_device_info_t *deviceInfo = xcb_input_xi_query_device_infos_iterator(reply.get()).data;
+ auto classes_it = xcb_input_xi_device_info_classes_iterator(deviceInfo);
+ for (; classes_it.rem; xcb_input_device_class_next(&classes_it)) {
+ xcb_input_device_class_t *classInfo = classes_it.data;
+ if (classInfo->type == XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR) {
+ auto *vci = reinterpret_cast<xcb_input_valuator_class_t *>(classInfo);
const int valuatorAtom = qatom(vci->label);
if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel)
- scrollingDevice.lastScrollPosition.setX(vci->value);
+ scrollingDevice.lastScrollPosition.setX(fixed3232ToReal(vci->value));
else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel)
- scrollingDevice.lastScrollPosition.setY(vci->value);
+ scrollingDevice.lastScrollPosition.setY(fixed3232ToReal(vci->value));
}
}
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled() && lastScrollPosition != scrollingDevice.lastScrollPosition))
@@ -979,8 +933,6 @@ void QXcbConnection::xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice)
lastScrollPosition.x(), lastScrollPosition.y(),
scrollingDevice.lastScrollPosition.x(),
scrollingDevice.lastScrollPosition.y());
-
- XIFreeDeviceInfo(deviceInfo);
}
void QXcbConnection::xi2UpdateScrollingDevices()
@@ -1003,10 +955,9 @@ QXcbConnection::ScrollingDevice *QXcbConnection::scrollingDeviceForId(int id)
void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollingDevice)
{
- xXIGenericDeviceEvent *xiEvent = reinterpret_cast<xXIGenericDeviceEvent *>(event);
+ auto *xiDeviceEvent = reinterpret_cast<qt_xcb_input_device_event_t *>(event);
- if (xiEvent->evtype == XI_Motion && scrollingDevice.orientations) {
- xXIDeviceEvent* xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
+ if (xiDeviceEvent->event_type == XCB_INPUT_MOTION && scrollingDevice.orientations) {
if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) {
QPoint rawDelta;
QPoint angleDelta;
@@ -1040,17 +991,16 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin
if (!angleDelta.isNull()) {
QPoint local(fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y));
QPoint global(fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y));
- Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective_mods);
+ Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective);
if (modifiers & Qt::AltModifier) {
std::swap(angleDelta.rx(), angleDelta.ry());
std::swap(rawDelta.rx(), rawDelta.ry());
}
qCDebug(lcQpaXInputEvents) << "scroll wheel @ window pos" << local << "delta px" << rawDelta << "angle" << angleDelta;
- QWindowSystemInterface::handleWheelEvent(platformWindow->window(), xiEvent->time, local, global, rawDelta, angleDelta, modifiers);
+ QWindowSystemInterface::handleWheelEvent(platformWindow->window(), xiDeviceEvent->time, local, global, rawDelta, angleDelta, modifiers);
}
}
- } else if (xiEvent->evtype == XI_ButtonRelease && scrollingDevice.legacyOrientations) {
- xXIDeviceEvent* xiDeviceEvent = reinterpret_cast<xXIDeviceEvent *>(event);
+ } else if (xiDeviceEvent->event_type == XCB_INPUT_BUTTON_RELEASE && scrollingDevice.legacyOrientations) {
if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) {
QPoint angleDelta;
if (scrollingDevice.legacyOrientations & Qt::Vertical) {
@@ -1068,16 +1018,15 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin
if (!angleDelta.isNull()) {
QPoint local(fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y));
QPoint global(fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y));
- Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective_mods);
+ Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective);
if (modifiers & Qt::AltModifier)
std::swap(angleDelta.rx(), angleDelta.ry());
qCDebug(lcQpaXInputEvents) << "scroll wheel (button" << xiDeviceEvent->detail << ") @ window pos" << local << "delta angle" << angleDelta;
- QWindowSystemInterface::handleWheelEvent(platformWindow->window(), xiEvent->time, local, global, QPoint(), angleDelta, modifiers);
+ QWindowSystemInterface::handleWheelEvent(platformWindow->window(), xiDeviceEvent->time, local, global, QPoint(), angleDelta, modifiers);
}
}
}
}
-#endif // XCB_USE_XINPUT21
static int xi2ValuatorOffset(const unsigned char *maskPtr, int maskLen, int number)
{
@@ -1100,10 +1049,10 @@ static int xi2ValuatorOffset(const unsigned char *maskPtr, int maskLen, int numb
bool QXcbConnection::xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value)
{
- const xXIDeviceEvent *xideviceevent = static_cast<const xXIDeviceEvent *>(event);
- const unsigned char *buttonsMaskAddr = (const unsigned char*)&xideviceevent[1];
- const unsigned char *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4;
- FP3232 *valuatorsValuesAddr = (FP3232*)(valuatorsMaskAddr + xideviceevent->valuators_len * 4);
+ auto *xideviceevent = static_cast<const qt_xcb_input_device_event_t *>(event);
+ auto *buttonsMaskAddr = reinterpret_cast<const unsigned char *>(&xideviceevent[1]);
+ auto *valuatorsMaskAddr = buttonsMaskAddr + xideviceevent->buttons_len * 4;
+ auto *valuatorsValuesAddr = reinterpret_cast<const xcb_input_fp3232_t *>(valuatorsMaskAddr + xideviceevent->valuators_len * 4);
int valuatorOffset = xi2ValuatorOffset(valuatorsMaskAddr, xideviceevent->valuators_len, valuatorNum);
if (valuatorOffset < 0)
@@ -1114,15 +1063,6 @@ bool QXcbConnection::xi2GetValuatorValueIfSet(const void *event, int valuatorNum
return true;
}
-void QXcbConnection::xi2PrepareXIGenericDeviceEvent(xcb_ge_event_t *event)
-{
- // xcb event structs contain stuff that wasn't on the wire, the full_sequence field
- // adds an extra 4 bytes and generic events cookie data is on the wire right after the standard 32 bytes.
- // Move this data back to have the same layout in memory as it was on the wire
- // and allow casting, overwriting the full_sequence field.
- memmove((char*) event + 32, (char*) event + 36, event->length * 4);
-}
-
Qt::MouseButton QXcbConnection::xiToQtMouseButton(uint32_t b)
{
switch (b) {
@@ -1186,31 +1126,29 @@ static const char *pointerTypeName(QTabletEvent::PointerType ptype) {
bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletData)
{
bool handled = true;
- Display *xDisplay = static_cast<Display *>(m_xlib_display);
- const xXIGenericDeviceEvent *xiEvent = static_cast<const xXIGenericDeviceEvent *>(event);
- const xXIDeviceEvent *xiDeviceEvent = reinterpret_cast<const xXIDeviceEvent *>(xiEvent);
+ const auto *xiDeviceEvent = reinterpret_cast<const qt_xcb_input_device_event_t *>(event);
- switch (xiEvent->evtype) {
- case XI_ButtonPress: {
+ switch (xiDeviceEvent->event_type) {
+ case XCB_INPUT_BUTTON_PRESS: {
Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail);
tabletData->buttons |= b;
- xi2ReportTabletEvent(xiEvent, tabletData);
+ xi2ReportTabletEvent(event, tabletData);
break;
}
- case XI_ButtonRelease: {
+ case XCB_INPUT_BUTTON_RELEASE: {
Qt::MouseButton b = xiToQtMouseButton(xiDeviceEvent->detail);
tabletData->buttons ^= b;
- xi2ReportTabletEvent(xiEvent, tabletData);
+ xi2ReportTabletEvent(event, tabletData);
break;
}
- case XI_Motion:
- xi2ReportTabletEvent(xiEvent, tabletData);
+ case XCB_INPUT_MOTION:
+ xi2ReportTabletEvent(event, tabletData);
break;
- case XI_PropertyEvent: {
+ case XCB_INPUT_PROPERTY: {
// This is the wacom driver's way of reporting tool proximity.
// The evdev driver doesn't do it this way.
- const xXIPropertyEvent *ev = reinterpret_cast<const xXIPropertyEvent *>(event);
- if (ev->what == XIPropertyModified) {
+ const auto *ev = reinterpret_cast<const xcb_input_property_event_t *>(event);
+ if (ev->what == XCB_INPUT_PROPERTY_FLAG_MODIFIED) {
if (ev->property == atom(QXcbAtom::WacomSerialIDs)) {
enum WacomSerialIndex {
_WACSER_USB_ID = 0,
@@ -1220,15 +1158,12 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD
_WACSER_TOOL_ID,
_WACSER_COUNT
};
- Atom propType;
- int propFormat;
- unsigned long numItems, bytesAfter;
- unsigned char *data;
- if (XIGetProperty(xDisplay, tabletData->deviceId, ev->property, 0, 100,
- 0, AnyPropertyType, &propType, &propFormat,
- &numItems, &bytesAfter, &data) == Success) {
- if (propType == atom(QXcbAtom::INTEGER) && propFormat == 32 && numItems == _WACSER_COUNT) {
- quint32 *ptr = reinterpret_cast<quint32 *>(data);
+
+ 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) {
+ quint32 *ptr = reinterpret_cast<quint32 *>(xcb_input_xi_get_property_items(reply.get()));
quint32 tool = ptr[_WACSER_TOOL_ID];
// Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/
// e.g. on Thinkpad Helix, tool ID will be 0 and serial will be 1
@@ -1260,7 +1195,6 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD
tabletData->deviceId, ptr[_WACSER_USB_ID], ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID],
ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], toolName(tabletData->tool));
}
- XFree(data);
}
}
}
@@ -1276,12 +1210,12 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD
void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletData)
{
- const xXIDeviceEvent *ev = reinterpret_cast<const xXIDeviceEvent *>(event);
+ auto *ev = reinterpret_cast<const qt_xcb_input_device_event_t *>(event);
QXcbWindow *xcbWindow = platformWindowFromId(ev->event);
if (!xcbWindow)
return;
QWindow *window = xcbWindow->window();
- const Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(ev->mods.effective_mods);
+ const Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(ev->mods.effective);
QPointF local(fixed1616ToReal(ev->event_x), fixed1616ToReal(ev->event_y));
QPointF global(fixed1616ToReal(ev->root_x), fixed1616ToReal(ev->root_y));
double pressure = 0, rotation = 0, tangentialPressure = 0;
@@ -1324,7 +1258,7 @@ void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletD
qCDebug(lcQpaXInputEvents, "XI2 event on tablet %d with tool %s type %s seq %d detail %d time %d "
"pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf modifiers 0x%x",
tabletData->deviceId, toolName(tabletData->tool), pointerTypeName(tabletData->pointerType),
- ev->sequenceNumber, ev->detail, ev->time,
+ ev->sequence, ev->detail, ev->time,
local.x(), local.y(), global.x(), global.y(),
(int)tabletData->buttons, pressure, xTilt, yTilt, rotation, (int)modifiers);
diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp
index b401100dd4..57629ac03a 100644
--- a/src/plugins/platforms/xcb/qxcbcursor.cpp
+++ b/src/plugins/platforms/xcb/qxcbcursor.cpp
@@ -628,6 +628,12 @@ xcb_cursor_t QXcbCursor::createBitmapCursor(QCursor *cursor)
}
#endif
+/*! \internal
+
+ Note that the logical state of a device (as seen by means of the protocol) may
+ lag the physical state if device event processing is frozen. See QueryPointer
+ in X11 protocol specification.
+*/
void QXcbCursor::queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDesktop, QPoint *pos, int *keybMask)
{
if (pos)
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index c8ba33edf5..aa329d8cb7 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -63,19 +63,6 @@
QT_BEGIN_NAMESPACE
-//#define DND_DEBUG
-#ifdef DND_DEBUG
-#define DEBUG qDebug
-#else
-#define DEBUG if(0) qDebug
-#endif
-
-#ifdef DND_DEBUG
-#define DNDDEBUG qDebug()
-#else
-#define DNDDEBUG if(0) qDebug()
-#endif
-
const int xdnd_version = 5;
static inline xcb_window_t xcb_window(QPlatformWindow *w)
@@ -164,8 +151,12 @@ void QXcbDrag::init()
QXcbCursor::queryPointer(connection(), &current_virtual_desktop, 0);
drag_types.clear();
+ //current_embedding_widget = 0;
+
dropped = false;
canceled = false;
+
+ source_sameanswer = QRect();
}
bool QXcbDrag::eventFilter(QObject *o, QEvent *e)
@@ -181,11 +172,10 @@ bool QXcbDrag::eventFilter(QObject *o, QEvent *e)
void QXcbDrag::startDrag()
{
- // #fixme enableEventFilter();
-
init();
#ifndef QT_NO_CLIPBOARD
+ qCDebug(lcQpaXDnd) << "starting drag where source:" << connection()->clipboard()->owner();
xcb_set_selection_owner(xcb_connection(), connection()->clipboard()->owner(),
atom(QXcbAtom::XdndSelection), connection()->time());
#endif
@@ -211,6 +201,9 @@ void QXcbDrag::startDrag()
QBasicDrag::startDrag();
if (connection()->mouseGrabber() == nullptr)
shapedPixmapWindow()->setMouseGrabEnabled(true);
+
+ auto nativePixelPos = QHighDpi::toNativePixels(QCursor::pos(), initiatorWindow);
+ move(nativePixelPos, QGuiApplication::mouseButtons(), QGuiApplication::keyboardModifiers());
}
void QXcbDrag::endDrag()
@@ -307,34 +300,13 @@ xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md
return 0;
}
-void QXcbDrag::move(const QPoint &globalPos)
+bool QXcbDrag::findXdndAwareTarget(const QPoint &globalPos, xcb_window_t *target_out)
{
-
- if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid())
- return;
-
- QXcbVirtualDesktop *virtualDesktop = nullptr;
- QPoint cursorPos;
- QXcbCursor::queryPointer(connection(), &virtualDesktop, &cursorPos);
- QXcbScreen *screen = virtualDesktop->screenAt(cursorPos);
- QPoint deviceIndependentPos = QHighDpiScaling::mapPositionFromNative(globalPos, screen);
-
- if (virtualDesktop != current_virtual_desktop) {
- setUseCompositing(virtualDesktop->compositingActive());
- recreateShapedPixmapWindow(static_cast<QPlatformScreen*>(screen)->screen(), deviceIndependentPos);
- if (connection()->mouseGrabber() == nullptr)
- shapedPixmapWindow()->setMouseGrabEnabled(true);
-
- current_virtual_desktop = virtualDesktop;
- } else {
- QBasicDrag::moveShapedPixmapWindow(deviceIndependentPos);
- }
-
xcb_window_t rootwin = current_virtual_desktop->root();
- auto translate = Q_XCB_REPLY(xcb_translate_coordinates, connection()->xcb_connection(),
+ auto translate = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
rootwin, rootwin, globalPos.x(), globalPos.y());
if (!translate)
- return;
+ return false;
xcb_window_t target = translate->child;
int lx = translate->dst_x;
@@ -343,10 +315,9 @@ void QXcbDrag::move(const QPoint &globalPos)
if (target && target != rootwin) {
xcb_window_t src = rootwin;
while (target != 0) {
- DNDDEBUG << "checking target for XdndAware" << target << lx << ly;
+ qCDebug(lcQpaXDnd) << "checking target for XdndAware" << target;
- // translate coordinates
- auto translate = Q_XCB_REPLY(xcb_translate_coordinates, connection()->xcb_connection(),
+ auto translate = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(),
src, target, lx, ly);
if (!translate) {
target = 0;
@@ -357,12 +328,11 @@ void QXcbDrag::move(const QPoint &globalPos)
src = target;
xcb_window_t child = translate->child;
- // check if it has XdndAware
auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, target,
atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0);
bool aware = reply && reply->type != XCB_NONE;
if (aware) {
- DNDDEBUG << "Found XdndAware on " << target;
+ qCDebug(lcQpaXDnd) << "found XdndAware on" << target;
break;
}
@@ -370,14 +340,45 @@ void QXcbDrag::move(const QPoint &globalPos)
}
if (!target || target == shapedPixmapWindow()->handle()->winId()) {
- DNDDEBUG << "need to find real window";
+ qCDebug(lcQpaXDnd) << "need to find real window";
target = findRealWindow(globalPos, rootwin, 6, true);
if (target == 0)
target = findRealWindow(globalPos, rootwin, 6, false);
- DNDDEBUG << "real window found" << target;
+ qCDebug(lcQpaXDnd) << "real window found" << target;
}
}
+ *target_out = target;
+ return true;
+}
+
+void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
+{
+ // The source sends XdndEnter and XdndPosition to the target.
+ if (source_sameanswer.contains(globalPos) && source_sameanswer.isValid())
+ return;
+
+ QXcbVirtualDesktop *virtualDesktop = nullptr;
+ QPoint cursorPos;
+ QXcbCursor::queryPointer(connection(), &virtualDesktop, &cursorPos);
+ QXcbScreen *screen = virtualDesktop->screenAt(cursorPos);
+ QPoint deviceIndependentPos = QHighDpiScaling::mapPositionFromNative(globalPos, screen);
+
+ if (virtualDesktop != current_virtual_desktop) {
+ setUseCompositing(virtualDesktop->compositingActive());
+ recreateShapedPixmapWindow(static_cast<QPlatformScreen*>(screen)->screen(), deviceIndependentPos);
+ if (connection()->mouseGrabber() == nullptr)
+ shapedPixmapWindow()->setMouseGrabEnabled(true);
+
+ current_virtual_desktop = virtualDesktop;
+ } else {
+ QBasicDrag::moveShapedPixmapWindow(deviceIndependentPos);
+ }
+
+ xcb_window_t target;
+ if (!findXdndAwareTarget(globalPos, &target))
+ return;
+
QXcbWindow *w = 0;
if (target) {
w = connection()->platformWindowFromId(target);
@@ -385,7 +386,7 @@ void QXcbDrag::move(const QPoint &globalPos)
w = 0;
} else {
w = 0;
- target = rootwin;
+ target = current_virtual_desktop->root();
}
xcb_window_t proxy_target = xdndProxy(connection(), target);
@@ -427,13 +428,14 @@ void QXcbDrag::move(const QPoint &globalPos)
enter.data.data32[0] = 0;
#endif
enter.data.data32[1] = flags;
- enter.data.data32[2] = drag_types.size()>0 ? drag_types.at(0) : 0;
- enter.data.data32[3] = drag_types.size()>1 ? drag_types.at(1) : 0;
- enter.data.data32[4] = drag_types.size()>2 ? drag_types.at(2) : 0;
+ enter.data.data32[2] = drag_types.size() > 0 ? drag_types.at(0) : 0;
+ enter.data.data32[3] = drag_types.size() > 1 ? drag_types.at(1) : 0;
+ enter.data.data32[4] = drag_types.size() > 2 ? drag_types.at(2) : 0;
// provisionally set the rectangle to 5x5 pixels...
- source_sameanswer = QRect(globalPos.x() - 2, globalPos.y() -2 , 5, 5);
+ source_sameanswer = QRect(globalPos.x() - 2, globalPos.y() - 2 , 5, 5);
+
+ qCDebug(lcQpaXDnd) << "sending XdndEnter to target:" << target;
- DEBUG() << "sending Xdnd enter source=" << enter.data.data32[0];
if (w)
handleEnter(w, &enter, current_proxy_target);
else if (target)
@@ -447,7 +449,8 @@ void QXcbDrag::move(const QPoint &globalPos)
if (target) {
waiting_for_status = true;
-
+ // The source sends a ClientMessage of type XdndPosition. This tells the target the
+ // position of the mouse and the action that the user requested.
xcb_client_message_event_t move;
move.response_type = XCB_CLIENT_MESSAGE;
move.sequence = 0;
@@ -462,21 +465,34 @@ void QXcbDrag::move(const QPoint &globalPos)
move.data.data32[1] = 0; // flags
move.data.data32[2] = (globalPos.x() << 16) + globalPos.y();
move.data.data32[3] = connection()->time();
- move.data.data32[4] = toXdndAction(defaultAction(currentDrag()->supportedActions(), QGuiApplication::keyboardModifiers()));
- DEBUG() << "sending Xdnd position source=" << move.data.data32[0] << "target=" << move.window;
+ move.data.data32[4] = toXdndAction(defaultAction(currentDrag()->supportedActions(), mods));
+
+ qCDebug(lcQpaXDnd) << "sending XdndPosition to target:" << target;
source_time = connection()->time();
if (w)
- handle_xdnd_position(w, &move);
+ handle_xdnd_position(w, &move, b, mods);
else
xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&move);
}
+
+ static const bool isUnity = qgetenv("XDG_CURRENT_DESKTOP").toLower() == "unity";
+ if (isUnity && xdndCollectionWindow == XCB_NONE) {
+ QString name = QXcbWindow::windowTitle(connection(), target);
+ if (name == QStringLiteral("XdndCollectionWindowImp"))
+ xdndCollectionWindow = target;
+ }
+ if (target == xdndCollectionWindow) {
+ setCanDrop(false);
+ updateCursor(Qt::IgnoreAction);
+ }
}
-void QXcbDrag::drop(const QPoint &globalPos)
+void QXcbDrag::drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
{
- QBasicDrag::drop(globalPos);
+ // XdndDrop is sent from source to target to complete the drop.
+ QBasicDrag::drop(globalPos, b, mods);
if (!current_target)
return;
@@ -500,7 +516,7 @@ void QXcbDrag::drop(const QPoint &globalPos)
QXcbWindow *w = connection()->platformWindowFromId(current_proxy_target);
- if (w && (w->window()->type() == Qt::Desktop) /*&& !w->acceptDrops()*/)
+ if (w && w->window()->type() == Qt::Desktop) // && !w->acceptDrops()
w = 0;
Transaction t = {
@@ -519,16 +535,13 @@ void QXcbDrag::drop(const QPoint &globalPos)
cleanup_timer = startTimer(XdndDropTransactionTimeout);
}
+ qCDebug(lcQpaXDnd) << "sending drop to target:" << current_target;
+
if (w) {
- handleDrop(w, &drop);
+ handleDrop(w, &drop, b, mods);
} else {
xcb_send_event(xcb_connection(), false, current_proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&drop);
}
-
- current_target = 0;
- current_proxy_target = 0;
- source_time = 0;
-// current_embedding_widget = 0;
}
Qt::DropAction QXcbDrag::toDropAction(xcb_atom_t a) const
@@ -586,44 +599,6 @@ int QXcbDrag::findTransactionByTime(xcb_timestamp_t timestamp)
}
#if 0
-
-// find an ancestor with XdndAware on it
-static Window findXdndAwareParent(Window window)
-{
- Window target = 0;
- forever {
- // check if window has XdndAware
- Atom type = 0;
- int f;
- unsigned long n, a;
- unsigned char *data = 0;
- if (XGetWindowProperty(X11->display, window, ATOM(XdndAware), 0, 0, False,
- AnyPropertyType, &type, &f,&n,&a,&data) == Success) {
- if (data)
- XFree(data);
- if (type) {
- target = window;
- break;
- }
- }
-
- // try window's parent
- Window root;
- Window parent;
- Window *children;
- uint unused;
- if (!XQueryTree(X11->display, window, &root, &parent, &children, &unused))
- break;
- if (children)
- XFree(children);
- if (window == root)
- break;
- window = parent;
- }
- return target;
-}
-
-
// for embedding only
static QWidget* current_embedding_widget = 0;
static xcb_client_message_event_t last_enter_event;
@@ -665,11 +640,10 @@ static bool checkEmbedded(QWidget* w, const XEvent* xe)
}
#endif
-
-void QXcbDrag::handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event, xcb_window_t proxy)
+void QXcbDrag::handleEnter(QPlatformWindow *, const xcb_client_message_event_t *event, xcb_window_t proxy)
{
- Q_UNUSED(window);
- DEBUG() << "handleEnter" << window;
+ // The target receives XdndEnter.
+ qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndEnter";
xdnd_types.clear();
@@ -705,11 +679,16 @@ void QXcbDrag::handleEnter(QPlatformWindow *window, const xcb_client_message_eve
}
}
for(int i = 0; i < xdnd_types.length(); ++i)
- DEBUG() << " " << connection()->atomName(xdnd_types.at(i));
+ qCDebug(lcQpaXDnd) << " " << connection()->atomName(xdnd_types.at(i));
}
-void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message_event_t *e)
+void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message_event_t *e,
+ Qt::MouseButtons b, Qt::KeyboardModifiers mods)
{
+ // The target receives XdndPosition. The target window must determine which widget the mouse
+ // is in and ask it whether or not it will accept the drop.
+ qCDebug(lcQpaXDnd) << "target:" << e->window << "received XdndPosition";
+
QPoint p((e->data.data32[2] & 0xffff0000) >> 16, e->data.data32[2] & 0x0000ffff);
Q_ASSERT(w);
QRect geometry = w->geometry();
@@ -718,8 +697,9 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message
if (!w || !w->window() || (w->window()->type() == Qt::Desktop))
return;
- if (e->data.data32[0] != xdnd_dragsource) {
- DEBUG("xdnd drag position from unexpected source (%x not %x)", e->data.data32[0], xdnd_dragsource);
+ if (Q_UNLIKELY(e->data.data32[0] != xdnd_dragsource)) {
+ qCDebug(lcQpaXDnd, "xdnd drag position from unexpected source (%x not %x)",
+ e->data.data32[0], xdnd_dragsource);
return;
}
@@ -741,10 +721,19 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message
supported_actions = Qt::DropActions(toDropAction(e->data.data32[4]));
}
- QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(w->window(),dropData,p,supported_actions);
+ auto buttons = currentDrag() ? b : connection()->queryMouseButtons();
+ auto modifiers = currentDrag() ? mods : connection()->queryKeyboardModifiers();
+
+ QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag(
+ w->window(), dropData, p, supported_actions, buttons, modifiers);
+
+ // ### FIXME ? - answerRect appears to be unused.
QRect answerRect(p + geometry.topLeft(), QSize(1,1));
answerRect = qt_response.answerRect().translated(geometry.topLeft()).intersected(geometry);
+ // The target sends a ClientMessage of type XdndStatus. This tells the source whether or not
+ // it will accept the drop, and, if so, what action will be taken. It also includes a rectangle
+ // that means "don't send another XdndPosition message until the mouse moves out of here".
xcb_client_message_event_t response;
response.response_type = XCB_CLIENT_MESSAGE;
response.sequence = 0;
@@ -775,13 +764,15 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message
// reset
target_time = XCB_CURRENT_TIME;
+ qCDebug(lcQpaXDnd) << "sending XdndStatus to source:" << xdnd_dragsource;
+
#ifndef QT_NO_CLIPBOARD
if (xdnd_dragsource == connection()->clipboard()->owner())
handle_xdnd_status(&response);
else
#endif
- xcb_send_event(xcb_connection(), false, current_proxy_target,
- XCB_EVENT_MASK_NO_EVENT, (const char *)&response);
+ xcb_send_event(xcb_connection(), false, current_proxy_target,
+ XCB_EVENT_MASK_NO_EVENT, (const char *)&response);
}
namespace
@@ -790,12 +781,11 @@ namespace
public:
ClientMessageScanner(xcb_atom_t a) : atom(a) {}
xcb_atom_t atom;
- bool checkEvent(xcb_generic_event_t *event) const {
- if (!event)
+ bool operator() (xcb_generic_event_t *event, int type) const {
+ if (type != XCB_CLIENT_MESSAGE)
return false;
- if ((event->response_type & 0x7f) != XCB_CLIENT_MESSAGE)
- return false;
- return ((xcb_client_message_event_t *)event)->type == atom;
+ auto clientMessage = reinterpret_cast<xcb_client_message_event_t *>(event);
+ return clientMessage->type == atom;
}
};
}
@@ -803,12 +793,11 @@ namespace
void QXcbDrag::handlePosition(QPlatformWindow * w, const xcb_client_message_event_t *event)
{
xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event);
- xcb_generic_event_t *nextEvent;
ClientMessageScanner scanner(atom(QXcbAtom::XdndPosition));
- while ((nextEvent = connection()->checkEvent(scanner))) {
+ while (auto nextEvent = connection()->eventQueue()->peek(scanner)) {
if (lastEvent != event)
free(lastEvent);
- lastEvent = (xcb_client_message_event_t *)nextEvent;
+ lastEvent = reinterpret_cast<xcb_client_message_event_t *>(nextEvent);
}
handle_xdnd_position(w, lastEvent);
@@ -818,7 +807,9 @@ void QXcbDrag::handlePosition(QPlatformWindow * w, const xcb_client_message_even
void QXcbDrag::handle_xdnd_status(const xcb_client_message_event_t *event)
{
- DEBUG("xdndHandleStatus");
+ // The source receives XdndStatus. It can use the action to change the cursor to indicate
+ // whether or not the user's requested action will be performed.
+ qCDebug(lcQpaXDnd) << "source:" << event->window << "received XdndStatus";
waiting_for_status = false;
// ignore late status messages
if (event->data.data32[0] && event->data.data32[0] != current_target)
@@ -855,7 +846,7 @@ void QXcbDrag::handleStatus(const xcb_client_message_event_t *event)
xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event);
xcb_generic_event_t *nextEvent;
ClientMessageScanner scanner(atom(QXcbAtom::XdndStatus));
- while ((nextEvent = connection()->checkEvent(scanner))) {
+ while ((nextEvent = connection()->eventQueue()->peek(scanner))) {
if (lastEvent != event)
free(lastEvent);
lastEvent = (xcb_client_message_event_t *)nextEvent;
@@ -864,12 +855,13 @@ void QXcbDrag::handleStatus(const xcb_client_message_event_t *event)
handle_xdnd_status(lastEvent);
if (lastEvent != event)
free(lastEvent);
- DEBUG("xdndHandleStatus end");
}
void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event)
{
- DEBUG("xdnd leave");
+ // If the target receives XdndLeave, it frees any cached data and forgets the whole incident.
+ qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndLeave";
+
if (!currentWindow || w != currentWindow.data()->handle())
return; // sanity
@@ -882,22 +874,19 @@ void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t
if (event->data.data32[0] != xdnd_dragsource) {
// This often happens - leave other-process window quickly
- DEBUG("xdnd drag leave from unexpected source (%x not %x", event->data.data32[0], xdnd_dragsource);
+ qCDebug(lcQpaXDnd, "xdnd drag leave from unexpected source (%x not %x",
+ event->data.data32[0], xdnd_dragsource);
}
- QWindowSystemInterface::handleDrag(w->window(),0,QPoint(),Qt::IgnoreAction);
-
- xdnd_dragsource = 0;
- xdnd_types.clear();
- currentWindow.clear();
+ QWindowSystemInterface::handleDrag(w->window(), nullptr, QPoint(), Qt::IgnoreAction, 0, 0);
}
void QXcbDrag::send_leave()
{
+ // XdndLeave is sent from the source to the target to cancel the drop.
if (!current_target)
return;
-
xcb_client_message_event_t leave;
leave.response_type = XCB_CLIENT_MESSAGE;
leave.sequence = 0;
@@ -919,21 +908,21 @@ void QXcbDrag::send_leave()
if (w && (w->window()->type() == Qt::Desktop) /*&& !w->acceptDrops()*/)
w = 0;
+ qCDebug(lcQpaXDnd) << "sending XdndLeave to target:" << current_target;
+
if (w)
handleLeave(w, (const xcb_client_message_event_t *)&leave);
else
xcb_send_event(xcb_connection(), false,current_proxy_target,
XCB_EVENT_MASK_NO_EVENT, (const char *)&leave);
-
- current_target = 0;
- current_proxy_target = 0;
- source_time = XCB_CURRENT_TIME;
- waiting_for_status = false;
}
-void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *event)
+void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *event,
+ Qt::MouseButtons b, Qt::KeyboardModifiers mods)
{
- DEBUG("xdndHandleDrop");
+ // Target receives XdndDrop. Once it is finished processing the drop, it sends XdndFinished.
+ qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndDrop";
+
if (!currentWindow) {
xdnd_dragsource = 0;
return; // sanity
@@ -941,16 +930,14 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
const uint32_t *l = event->data.data32;
- DEBUG("xdnd drop");
-
if (l[0] != xdnd_dragsource) {
- DEBUG("xdnd drop from unexpected source (%x not %x", l[0], xdnd_dragsource);
+ qCDebug(lcQpaXDnd, "xdnd drop from unexpected source (%x not %x", l[0], xdnd_dragsource);
return;
}
// update the "user time" from the timestamp in the event.
if (l[2] != 0)
- target_time = /*X11->userTime =*/ l[2];
+ target_time = l[2];
Qt::DropActions supported_drop_actions;
QMimeData *dropData = 0;
@@ -960,9 +947,6 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
} else {
dropData = m_dropData;
supported_drop_actions = accepted_drop_action;
-
- // Drop coming from another app? Update keyboard modifiers.
- QGuiApplicationPrivate::modifier_buttons = QGuiApplication::queryKeyboardModifiers();
}
if (!dropData)
@@ -973,7 +957,13 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
// dropData = QDragManager::dragPrivate(X11->dndDropTransactions.at(at).object)->data;
// if we can't find it, then use the data in the drag manager
- QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(currentWindow.data(),dropData,currentPosition,supported_drop_actions);
+ auto buttons = currentDrag() ? b : connection()->queryMouseButtons();
+ auto modifiers = currentDrag() ? mods : connection()->queryKeyboardModifiers();
+
+ QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop(
+ currentWindow.data(), dropData, currentPosition, supported_drop_actions,
+ buttons, modifiers);
+
setExecutedDropAction(response.acceptedAction());
xcb_client_message_event_t finished;
@@ -985,34 +975,26 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
finished.data.data32[0] = currentWindow ? xcb_window(currentWindow.data()) : XCB_NONE;
finished.data.data32[1] = response.isAccepted(); // flags
finished.data.data32[2] = toXdndAction(response.acceptedAction());
- xcb_send_event(xcb_connection(), false, current_proxy_target,
- XCB_EVENT_MASK_NO_EVENT, (char *)&finished);
- xdnd_dragsource = 0;
- currentWindow.clear();
- waiting_for_status = false;
+ qCDebug(lcQpaXDnd) << "sending XdndFinished to source:" << xdnd_dragsource;
- // reset
- target_time = XCB_CURRENT_TIME;
+ xcb_send_event(xcb_connection(), false, current_proxy_target,
+ XCB_EVENT_MASK_NO_EVENT, (char *)&finished);
dropped = true;
}
-
void QXcbDrag::handleFinished(const xcb_client_message_event_t *event)
{
- DEBUG("xdndHandleFinished");
+ // Source receives XdndFinished when target is done processing the drop data.
+ qCDebug(lcQpaXDnd) << "source:" << event->window << "received XdndFinished";
+
#ifndef QT_NO_CLIPBOARD
if (event->window != connection()->clipboard()->owner())
return;
#endif
const unsigned long *l = (const unsigned long *)event->data.data32;
-
- DNDDEBUG << "xdndHandleFinished, l[0]" << l[0]
- << "current_target" << current_target
- << "qt_xdnd_current_proxy_targe" << current_proxy_target;
-
if (l[0]) {
int at = findTransactionByWindow(l[0]);
if (at != -1) {
@@ -1087,7 +1069,8 @@ void QXcbDrag::timerEvent(QTimerEvent* e)
void QXcbDrag::cancel()
{
- DEBUG("QXcbDrag::cancel");
+ qCDebug(lcQpaXDnd) << "dnd was canceled";
+
QBasicDrag::cancel();
if (current_target)
send_leave();
@@ -1098,7 +1081,6 @@ void QXcbDrag::cancel()
canceled = true;
}
-// find an ancestor with XdndAware on it
static xcb_window_t findXdndAwareParent(QXcbConnection *c, xcb_window_t window)
{
xcb_window_t target = 0;
@@ -1127,7 +1109,8 @@ static xcb_window_t findXdndAwareParent(QXcbConnection *c, xcb_window_t window)
void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event)
{
- Q_DECLARE_XCB_EVENT(notify, xcb_selection_notify_event_t);
+ qCDebug(lcQpaXDnd) << "handle selection request from target:" << event->requestor;
+ q_padded_xcb_event<xcb_selection_notify_event_t> notify = {};
notify.response_type = XCB_SELECTION_NOTIFY;
notify.requestor = event->requestor;
notify.selection = event->selection;
@@ -1194,10 +1177,10 @@ void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event
bool QXcbDrag::dndEnable(QXcbWindow *w, bool on)
{
- DNDDEBUG << "xdndEnable" << w << on;
+ // Windows announce that they support the XDND protocol by creating a window property XdndAware.
if (on) {
- QXcbWindow *xdnd_widget = 0;
- if ((w->window()->type() == Qt::Desktop)) {
+ QXcbWindow *window = nullptr;
+ if (w->window()->type() == Qt::Desktop) {
if (desktop_proxy) // *WE* already have one.
return false;
@@ -1208,8 +1191,8 @@ bool QXcbDrag::dndEnable(QXcbWindow *w, bool on)
if (!proxy_id) {
desktop_proxy = new QWindow;
- xdnd_widget = static_cast<QXcbWindow *>(desktop_proxy->handle());
- proxy_id = xdnd_widget->xcb_window();
+ window = static_cast<QXcbWindow *>(desktop_proxy->handle());
+ proxy_id = window->xcb_window();
xcb_atom_t xdnd_proxy = atom(QXcbAtom::XdndProxy);
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, w->xcb_window(), xdnd_proxy,
XCB_ATOM_WINDOW, 32, 1, &proxy_id);
@@ -1218,24 +1201,24 @@ bool QXcbDrag::dndEnable(QXcbWindow *w, bool on)
}
} else {
- xdnd_widget = w;
+ window = w;
}
- if (xdnd_widget) {
- DNDDEBUG << "setting XdndAware for" << xdnd_widget << xdnd_widget->xcb_window();
+ if (window) {
+ qCDebug(lcQpaXDnd) << "setting XdndAware for" << window->xcb_window();
xcb_atom_t atm = xdnd_version;
- xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, xdnd_widget->xcb_window(),
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window->xcb_window(),
atom(QXcbAtom::XdndAware), XCB_ATOM_ATOM, 32, 1, &atm);
return true;
} else {
return false;
}
} else {
- if ((w->window()->type() == Qt::Desktop)) {
+ if (w->window()->type() == Qt::Desktop) {
xcb_delete_property(xcb_connection(), w->xcb_window(), atom(QXcbAtom::XdndProxy));
delete desktop_proxy;
desktop_proxy = 0;
} else {
- DNDDEBUG << "not deleting XDndAware";
+ qCDebug(lcQpaXDnd) << "not deleting XDndAware";
}
return true;
}
@@ -1293,7 +1276,6 @@ QVariant QXcbDropData::xdndObtainData(const QByteArray &format, QVariant::Type r
return mimeConvertToFormat(c, a, result, QLatin1String(format), requestedType, encoding);
}
-
bool QXcbDropData::hasFormat_sys(const QString &format) const
{
return formats().contains(format);
diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h
index 60287b717b..c19008c04b 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.h
+++ b/src/plugins/platforms/xcb/qxcbdrag.h
@@ -78,14 +78,15 @@ public:
void startDrag() override;
void cancel() override;
- void move(const QPoint &globalPos) override;
- void drop(const QPoint &globalPos) override;
+ void move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override;
+ void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override;
void endDrag() override;
void handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event, xcb_window_t proxy = 0);
void handlePosition(QPlatformWindow *w, const xcb_client_message_event_t *event);
void handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event);
- void handleDrop(QPlatformWindow *, const xcb_client_message_event_t *event);
+ void handleDrop(QPlatformWindow *, const xcb_client_message_event_t *event,
+ Qt::MouseButtons b = 0, Qt::KeyboardModifiers mods = 0);
void handleStatus(const xcb_client_message_event_t *event);
void handleSelectionRequest(const xcb_selection_request_event_t *event);
@@ -100,12 +101,15 @@ public:
protected:
void timerEvent(QTimerEvent* e) override;
+ bool findXdndAwareTarget(const QPoint &globalPos, xcb_window_t *target_out);
+
private:
friend class QXcbDropData;
void init();
- void handle_xdnd_position(QPlatformWindow *w, const xcb_client_message_event_t *event);
+ void handle_xdnd_position(QPlatformWindow *w, const xcb_client_message_event_t *event,
+ Qt::MouseButtons b = 0, Qt::KeyboardModifiers mods = 0);
void handle_xdnd_status(const xcb_client_message_event_t *event);
void send_leave();
@@ -139,6 +143,9 @@ private:
bool dropped;
bool canceled;
+ // A window from Unity DnD Manager, which does not respect the XDnD spec
+ xcb_window_t xdndCollectionWindow = XCB_NONE;
+
// top-level window we sent position to last.
xcb_window_t current_target;
// window to send events to (always valid if current_target)
diff --git a/src/plugins/platforms/xcb/qxcbeventdispatcher.cpp b/src/plugins/platforms/xcb/qxcbeventdispatcher.cpp
new file mode 100644
index 0000000000..3cb2a5b5ef
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbeventdispatcher.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** 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 "qxcbeventdispatcher.h"
+#include "qxcbconnection.h"
+
+#include <QtCore/QCoreApplication>
+
+#include <qpa/qwindowsysteminterface.h>
+
+QT_BEGIN_NAMESPACE
+
+QXcbUnixEventDispatcher::QXcbUnixEventDispatcher(QXcbConnection *connection, QObject *parent)
+ : QEventDispatcherUNIX(parent)
+ , m_connection(connection)
+{
+}
+
+QXcbUnixEventDispatcher::~QXcbUnixEventDispatcher()
+{
+}
+
+bool QXcbUnixEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ const bool didSendEvents = QEventDispatcherUNIX::processEvents(flags);
+ m_connection->processXcbEvents(flags);
+ // The following line should not be necessary after QTBUG-70095
+ return QWindowSystemInterface::sendWindowSystemEvents(flags) || didSendEvents;
+}
+
+bool QXcbUnixEventDispatcher::hasPendingEvents()
+{
+ extern uint qGlobalPostedEventsCount();
+ return qGlobalPostedEventsCount() || QWindowSystemInterface::windowSystemEventsQueued();
+}
+
+void QXcbUnixEventDispatcher::flush()
+{
+ if (qApp)
+ qApp->sendPostedEvents();
+}
+
+#if QT_CONFIG(glib)
+struct XcbEventSource
+{
+ GSource source;
+ QXcbGlibEventDispatcher *dispatcher;
+ QXcbGlibEventDispatcherPrivate *dispatcher_p;
+ QXcbConnection *connection = nullptr;
+};
+
+static gboolean xcbSourcePrepare(GSource *source, gint *timeout)
+{
+ Q_UNUSED(timeout)
+ auto xcbEventSource = reinterpret_cast<XcbEventSource *>(source);
+ return xcbEventSource->dispatcher_p->wakeUpCalled;
+}
+
+static gboolean xcbSourceCheck(GSource *source)
+{
+ return xcbSourcePrepare(source, nullptr);
+}
+
+static gboolean xcbSourceDispatch(GSource *source, GSourceFunc, gpointer)
+{
+ auto xcbEventSource = reinterpret_cast<XcbEventSource *>(source);
+ QEventLoop::ProcessEventsFlags flags = xcbEventSource->dispatcher->flags();
+ xcbEventSource->connection->processXcbEvents(flags);
+ // The following line should not be necessary after QTBUG-70095
+ QWindowSystemInterface::sendWindowSystemEvents(flags);
+ return true;
+}
+
+QXcbGlibEventDispatcher::QXcbGlibEventDispatcher(QXcbConnection *connection, QObject *parent)
+ : QEventDispatcherGlib(*new QXcbGlibEventDispatcherPrivate(), parent)
+{
+ Q_D(QXcbGlibEventDispatcher);
+
+ m_xcbEventSourceFuncs.prepare = xcbSourcePrepare;
+ m_xcbEventSourceFuncs.check = xcbSourceCheck;
+ m_xcbEventSourceFuncs.dispatch = xcbSourceDispatch;
+ m_xcbEventSourceFuncs.finalize = nullptr;
+
+ m_xcbEventSource = reinterpret_cast<XcbEventSource *>(
+ g_source_new(&m_xcbEventSourceFuncs, sizeof(XcbEventSource)));
+
+ m_xcbEventSource->dispatcher = this;
+ m_xcbEventSource->dispatcher_p = d_func();
+ m_xcbEventSource->connection = connection;
+
+ g_source_set_can_recurse(&m_xcbEventSource->source, true);
+ g_source_attach(&m_xcbEventSource->source, d->mainContext);
+}
+
+QXcbGlibEventDispatcherPrivate::QXcbGlibEventDispatcherPrivate()
+{
+}
+
+QXcbGlibEventDispatcher::~QXcbGlibEventDispatcher()
+{
+ g_source_destroy(&m_xcbEventSource->source);
+ g_source_unref(&m_xcbEventSource->source);
+}
+
+bool QXcbGlibEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ m_flags = flags;
+ return QEventDispatcherGlib::processEvents(m_flags);
+}
+
+#endif // QT_CONFIG(glib)
+
+QAbstractEventDispatcher *QXcbEventDispatcher::createEventDispatcher(QXcbConnection *connection)
+{
+#if QT_CONFIG(glib)
+ if (qEnvironmentVariableIsEmpty("QT_NO_GLIB") && QEventDispatcherGlib::versionSupported()) {
+ qCDebug(lcQpaXcb, "using glib dispatcher");
+ return new QXcbGlibEventDispatcher(connection);
+ } else
+#endif
+ {
+ qCDebug(lcQpaXcb, "using unix dispatcher");
+ return new QXcbUnixEventDispatcher(connection);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbeventdispatcher.h b/src/plugins/platforms/xcb/qxcbeventdispatcher.h
new file mode 100644
index 0000000000..6aadd63a70
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbeventdispatcher.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** 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 QXCBEVENTDISPATCHER_H
+#define QXCBEVENTDISPATCHER_H
+
+#include <QtCore/QObject>
+#include <QtCore/QEventLoop>
+
+#include <QtCore/private/qeventdispatcher_unix_p.h>
+#if QT_CONFIG(glib)
+#include <QtCore/private/qeventdispatcher_glib_p.h>
+#include <glib.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QXcbConnection;
+
+class QXcbUnixEventDispatcher : public QEventDispatcherUNIX
+{
+ Q_OBJECT
+public:
+ explicit QXcbUnixEventDispatcher(QXcbConnection *connection, QObject *parent = nullptr);
+ ~QXcbUnixEventDispatcher();
+ bool processEvents(QEventLoop::ProcessEventsFlags flags) override;
+
+ // Maybe some user code depends on deprecated QUnixEventDispatcherQPA::
+ // hasPendingEvents() / flush() implementation, so keep it around until
+ // Qt 6. These methods are deprecated in QAbstractEventDispatcher.
+ bool hasPendingEvents() override; // ### Qt 6 remove
+ void flush() override; // ### Qt 6 remove
+
+private:
+ QXcbConnection *m_connection;
+};
+
+#if QT_CONFIG(glib)
+
+struct XcbEventSource;
+class QXcbGlibEventDispatcherPrivate;
+
+class QXcbGlibEventDispatcher : public QEventDispatcherGlib
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QXcbGlibEventDispatcher)
+
+public:
+ explicit QXcbGlibEventDispatcher(QXcbConnection *connection, QObject *parent = nullptr);
+ ~QXcbGlibEventDispatcher();
+
+ bool processEvents(QEventLoop::ProcessEventsFlags flags) override;
+ QEventLoop::ProcessEventsFlags flags() const { return m_flags; }
+
+private:
+ XcbEventSource *m_xcbEventSource;
+ GSourceFuncs m_xcbEventSourceFuncs;
+ QEventLoop::ProcessEventsFlags m_flags;
+};
+
+class QXcbGlibEventDispatcherPrivate : public QEventDispatcherGlibPrivate
+{
+ Q_DECLARE_PUBLIC(QXcbGlibEventDispatcher)
+
+public:
+ QXcbGlibEventDispatcherPrivate();
+};
+
+#endif // QT_CONFIG(glib)
+
+class QXcbEventDispatcher
+{
+public:
+ static QAbstractEventDispatcher *createEventDispatcher(QXcbConnection *connection);
+
+private:
+ QXcbConnection *m_connection;
+};
+
+QT_END_NAMESPACE
+
+#endif // QXCBEVENTDISPATCHER_H
diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.cpp b/src/plugins/platforms/xcb/qxcbeventqueue.cpp
new file mode 100644
index 0000000000..862f68764b
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbeventqueue.cpp
@@ -0,0 +1,383 @@
+/****************************************************************************
+**
+** 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 "qxcbeventqueue.h"
+#include "qxcbconnection.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QAbstractEventDispatcher>
+#include <QtCore/QMutex>
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+static QBasicMutex qAppExiting;
+static bool dispatcherOwnerDestructing = false;
+
+/*!
+ \class QXcbEventQueue
+ \internal
+
+ Lock-free event passing:
+
+ The lock-free solution uses a singly-linked list to pass events from the
+ reader thread to the main thread. An atomic operation is used to sync the
+ tail node of the list between threads. The reader thread takes special care
+ when accessing the tail node. It does not dequeue the last node and does not
+ access (read or write) the tail node's 'next' member. This lets the reader
+ add more items at the same time as the main thread is dequeuing nodes from
+ the head. A custom linked list implementation is used, because QLinkedList
+ does not have any thread-safety guarantees and the custom list is more
+ lightweight - no reference counting, back links, etc.
+
+ Memory management:
+
+ In a normally functioning application, XCB plugin won't buffer more than few
+ batches of events, couple events per batch. Instead of constantly calling
+ new / delete, we can create a pool of nodes that we reuse. The main thread
+ uses an atomic operation to sync how many nodes have been restored (available
+ for reuse). If at some point a user application will block the main thread
+ for a long time, we might run out of nodes in the pool. Then we create nodes
+ on a heap. These will be automatically "garbage collected" out of the linked
+ list, once the main thread stops blocking.
+*/
+
+QXcbEventQueue::QXcbEventQueue(QXcbConnection *connection)
+ : m_connection(connection)
+{
+ // When running test cases in auto tests, static variables are preserved
+ // between test function runs, even if Q*Application object is destroyed.
+ // Reset to default value to account for this.
+ dispatcherOwnerDestructing = false;
+ qAddPostRoutine([]() {
+ QMutexLocker locker(&qAppExiting);
+ dispatcherOwnerDestructing = true;
+ });
+
+ // Lets init the list with one node, so we don't have to check for
+ // this special case in various places.
+ m_head = m_flushedTail = qXcbEventNodeFactory(nullptr);
+ m_tail.store(m_head, std::memory_order_release);
+
+ start();
+}
+
+QXcbEventQueue::~QXcbEventQueue()
+{
+ if (isRunning()) {
+ sendCloseConnectionEvent();
+ wait();
+ }
+
+ while (xcb_generic_event_t *event = takeFirst())
+ free(event);
+
+ if (m_head && m_head->fromHeap)
+ delete m_head; // the deferred node
+
+ qCDebug(lcQpaEventReader) << "nodes on heap:" << m_nodesOnHeap;
+}
+
+xcb_generic_event_t *QXcbEventQueue::takeFirst(QEventLoop::ProcessEventsFlags flags)
+{
+ // This is the level at which we were moving excluded user input events into
+ // separate queue in Qt 4 (see qeventdispatcher_x11.cpp). In this case
+ // QXcbEventQueue represents Xlib's internal event queue. In Qt 4, Xlib's
+ // event queue peeking APIs would not see these events anymore, the same way
+ // our peeking functions do not consider m_inputEvents. This design is
+ // intentional to keep the same behavior. We could do filtering directly on
+ // QXcbEventQueue, without the m_inputEvents, but it is not clear if it is
+ // needed by anyone who peeks at the native event queue.
+
+ bool excludeUserInputEvents = flags.testFlag(QEventLoop::ExcludeUserInputEvents);
+ if (excludeUserInputEvents) {
+ xcb_generic_event_t *event = nullptr;
+ while ((event = takeFirst())) {
+ if (m_connection->isUserInputEvent(event)) {
+ m_inputEvents << event;
+ continue;
+ }
+ break;
+ }
+ return event;
+ }
+
+ if (!m_inputEvents.isEmpty())
+ return m_inputEvents.takeFirst();
+ return takeFirst();
+}
+
+xcb_generic_event_t *QXcbEventQueue::takeFirst()
+{
+ if (isEmpty())
+ return nullptr;
+
+ xcb_generic_event_t *event = nullptr;
+ do {
+ event = m_head->event;
+ if (m_head == m_flushedTail) {
+ // defer dequeuing until next successful flush of events
+ if (event) // check if not cleared already by some filter
+ m_head->event = nullptr; // if not, clear it
+ } else {
+ dequeueNode();
+ if (!event)
+ continue; // consumed by filter or deferred node
+ }
+ } while (!isEmpty() && !event);
+
+ m_queueModified = m_peekerIndexCacheDirty = true;
+
+ return event;
+}
+
+void QXcbEventQueue::dequeueNode()
+{
+ QXcbEventNode *node = m_head;
+ m_head = m_head->next;
+ if (node->fromHeap)
+ delete node;
+ else
+ m_nodesRestored.fetch_add(1, std::memory_order_release);
+}
+
+void QXcbEventQueue::flushBufferedEvents()
+{
+ m_flushedTail = m_tail.load(std::memory_order_acquire);
+}
+
+QXcbEventNode *QXcbEventQueue::qXcbEventNodeFactory(xcb_generic_event_t *event)
+{
+ static QXcbEventNode qXcbNodePool[PoolSize];
+
+ if (m_freeNodes == 0) // out of nodes, check if the main thread has released any
+ m_freeNodes = m_nodesRestored.exchange(0, std::memory_order_acquire);
+
+ if (m_freeNodes) {
+ m_freeNodes--;
+ if (m_poolIndex == PoolSize) {
+ // wrap back to the beginning, we always take and restore nodes in-order
+ m_poolIndex = 0;
+ }
+ QXcbEventNode *node = &qXcbNodePool[m_poolIndex++];
+ node->event = event;
+ node->next = nullptr;
+ return node;
+ }
+
+ // the main thread is not flushing events and thus the pool has become empty
+ auto node = new QXcbEventNode(event);
+ node->fromHeap = true;
+ qCDebug(lcQpaEventReader) << "[heap] " << m_nodesOnHeap++;
+ return node;
+}
+
+void QXcbEventQueue::run()
+{
+ xcb_generic_event_t *event = nullptr;
+ xcb_connection_t *connection = m_connection->xcb_connection();
+ QXcbEventNode *tail = m_head;
+
+ auto enqueueEvent = [&tail, this](xcb_generic_event_t *event) {
+ if (!isCloseConnectionEvent(event)) {
+ tail->next = qXcbEventNodeFactory(event);
+ tail = tail->next;
+ m_tail.store(tail, std::memory_order_release);
+ }
+ };
+
+ while (!m_closeConnectionDetected && (event = xcb_wait_for_event(connection))) {
+ enqueueEvent(event);
+ while (!m_closeConnectionDetected && (event = xcb_poll_for_queued_event(connection)))
+ enqueueEvent(event);
+ wakeUpDispatcher();
+ }
+
+ if (!m_closeConnectionDetected) {
+ // Connection was terminated not by us. Wake up dispatcher, which will
+ // call processXcbEvents(), where we handle the connection errors via
+ // xcb_connection_has_error().
+ wakeUpDispatcher();
+ }
+}
+
+void QXcbEventQueue::wakeUpDispatcher()
+{
+ QMutexLocker locker(&qAppExiting);
+ if (!dispatcherOwnerDestructing) {
+ // This thread can run before a dispatcher has been created,
+ // so check if it is ready.
+ if (QCoreApplication::eventDispatcher())
+ QCoreApplication::eventDispatcher()->wakeUp();
+ }
+}
+
+qint32 QXcbEventQueue::generatePeekerId()
+{
+ const qint32 peekerId = m_peekerIdSource++;
+ m_peekerToNode.insert(peekerId, nullptr);
+ return peekerId;
+}
+
+bool QXcbEventQueue::removePeekerId(qint32 peekerId)
+{
+ const auto it = m_peekerToNode.find(peekerId);
+ if (it == m_peekerToNode.constEnd()) {
+ qCWarning(lcQpaXcb, "failed to remove unknown peeker id: %d", peekerId);
+ return false;
+ }
+ m_peekerToNode.erase(it);
+ if (m_peekerToNode.isEmpty()) {
+ m_peekerIdSource = 0; // Once the hash becomes empty, we can start reusing IDs
+ m_peekerIndexCacheDirty = false;
+ }
+ return true;
+}
+
+bool QXcbEventQueue::peekEventQueue(PeekerCallback peeker, void *peekerData,
+ PeekOptions option, qint32 peekerId)
+{
+ const bool peekerIdProvided = peekerId != -1;
+ auto peekerToNodeIt = m_peekerToNode.find(peekerId);
+
+ if (peekerIdProvided && peekerToNodeIt == m_peekerToNode.constEnd()) {
+ qCWarning(lcQpaXcb, "failed to find index for unknown peeker id: %d", peekerId);
+ return false;
+ }
+
+ const bool useCache = option.testFlag(PeekOption::PeekFromCachedIndex);
+ if (useCache && !peekerIdProvided) {
+ qCWarning(lcQpaXcb, "PeekOption::PeekFromCachedIndex requires peeker id");
+ return false;
+ }
+
+ if (peekerIdProvided && m_peekerIndexCacheDirty) {
+ for (auto &node : m_peekerToNode) // reset cache
+ node = nullptr;
+ m_peekerIndexCacheDirty = false;
+ }
+
+ flushBufferedEvents();
+ if (isEmpty())
+ return false;
+
+ const auto startNode = [this, useCache, peekerToNodeIt]() -> QXcbEventNode * {
+ if (useCache) {
+ const QXcbEventNode *cachedNode = peekerToNodeIt.value();
+ if (!cachedNode)
+ return m_head; // cache was reset
+ if (cachedNode == m_flushedTail)
+ return nullptr; // no new events since the last call
+ return cachedNode->next;
+ }
+ return m_head;
+ }();
+
+ if (!startNode)
+ return false;
+
+ // A peeker may call QCoreApplication::processEvents(), which will cause
+ // QXcbConnection::processXcbEvents() to modify the queue we are currently
+ // looping through;
+ m_queueModified = false;
+ bool result = false;
+
+ QXcbEventNode *node = startNode;
+ do {
+ xcb_generic_event_t *event = node->event;
+ if (event && peeker(event, peekerData)) {
+ result = true;
+ break;
+ }
+ if (node == m_flushedTail)
+ break;
+ node = node->next;
+ } while (!m_queueModified);
+
+ // Update the cached index if the queue was not modified, and hence the
+ // cache is still valid.
+ if (peekerIdProvided && node != startNode && !m_queueModified) {
+ // Before updating, make sure that a peeker callback did not remove
+ // the peeker id.
+ peekerToNodeIt = m_peekerToNode.find(peekerId);
+ if (peekerToNodeIt != m_peekerToNode.constEnd())
+ *peekerToNodeIt = node; // id still in the cache, update node
+ }
+
+ return result;
+}
+
+void QXcbEventQueue::sendCloseConnectionEvent() const
+{
+ // A hack to close XCB connection. Apparently XCB does not have any APIs for this?
+ xcb_client_message_event_t event;
+ memset(&event, 0, sizeof(event));
+
+ 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->setup());
+ xcb_screen_t *screen = it.data;
+ xcb_create_window(c, XCB_COPY_FROM_PARENT,
+ window, screen->root,
+ 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
+ screen->root_visual, 0, nullptr);
+
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.sequence = 0;
+ event.window = window;
+ event.type = m_connection->atom(QXcbAtom::_QT_CLOSE_CONNECTION);
+ event.data.data32[0] = 0;
+
+ xcb_send_event(c, false, window, XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<const char *>(&event));
+ xcb_destroy_window(c, window);
+ xcb_flush(c);
+}
+
+bool QXcbEventQueue::isCloseConnectionEvent(const xcb_generic_event_t *event)
+{
+ if (event && (event->response_type & ~0x80) == XCB_CLIENT_MESSAGE) {
+ auto clientMessage = reinterpret_cast<const xcb_client_message_event_t *>(event);
+ if (clientMessage->type == m_connection->atom(QXcbAtom::_QT_CLOSE_CONNECTION))
+ m_closeConnectionDetected = true;
+ }
+ return m_closeConnectionDetected;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.h b/src/plugins/platforms/xcb/qxcbeventqueue.h
new file mode 100644
index 0000000000..235f2824be
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbeventqueue.h
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** 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 QXCBEVENTQUEUE_H
+#define QXCBEVENTQUEUE_H
+
+#include <QtCore/QThread>
+#include <QtCore/QHash>
+#include <QtCore/QEventLoop>
+#include <QtCore/QVector>
+
+#include <xcb/xcb.h>
+
+#include <atomic>
+
+QT_BEGIN_NAMESPACE
+
+struct QXcbEventNode {
+ QXcbEventNode(xcb_generic_event_t *e = nullptr)
+ : event(e) { }
+
+ xcb_generic_event_t *event;
+ QXcbEventNode *next = nullptr;
+ bool fromHeap = false;
+};
+
+class QXcbConnection;
+class QAbstractEventDispatcher;
+
+class QXcbEventQueue : public QThread
+{
+ Q_OBJECT
+public:
+ QXcbEventQueue(QXcbConnection *connection);
+ ~QXcbEventQueue();
+
+ enum { PoolSize = 100 }; // 2.4 kB with 100 nodes
+
+ enum PeekOption {
+ PeekDefault = 0, // see qx11info_x11.h for docs
+ PeekFromCachedIndex = 1,
+ PeekRetainMatch = 2,
+ PeekRemoveMatch = 3,
+ PeekRemoveMatchContinue = 4
+ };
+ Q_DECLARE_FLAGS(PeekOptions, PeekOption)
+
+ void run() override;
+
+ bool isEmpty() const { return m_head == m_flushedTail && !m_head->event; }
+ xcb_generic_event_t *takeFirst(QEventLoop::ProcessEventsFlags flags);
+ xcb_generic_event_t *takeFirst();
+ void flushBufferedEvents();
+ void wakeUpDispatcher();
+
+ // ### peek() and peekEventQueue() could be unified. Note that peekEventQueue()
+ // is public API exposed via QX11Extras/QX11Info.
+ template<typename Peeker>
+ xcb_generic_event_t *peek(Peeker &&peeker) {
+ return peek(PeekRemoveMatch, std::forward<Peeker>(peeker));
+ }
+ template<typename Peeker>
+ inline xcb_generic_event_t *peek(PeekOption config, Peeker &&peeker);
+
+ qint32 generatePeekerId();
+ bool removePeekerId(qint32 peekerId);
+
+ using PeekerCallback = bool (*)(xcb_generic_event_t *event, void *peekerData);
+ bool peekEventQueue(PeekerCallback peeker, void *peekerData = nullptr,
+ PeekOptions option = PeekDefault, qint32 peekerId = -1);
+
+private:
+ QXcbEventNode *qXcbEventNodeFactory(xcb_generic_event_t *event);
+ void dequeueNode();
+
+ void sendCloseConnectionEvent() const;
+ bool isCloseConnectionEvent(const xcb_generic_event_t *event);
+
+ QXcbEventNode *m_head = nullptr;
+ QXcbEventNode *m_flushedTail = nullptr;
+ std::atomic<QXcbEventNode *> m_tail { nullptr };
+ std::atomic_uint m_nodesRestored { 0 };
+
+ QXcbConnection *m_connection = nullptr;
+ bool m_closeConnectionDetected = false;
+
+ uint m_freeNodes = PoolSize;
+ uint m_poolIndex = 0;
+
+ qint32 m_peekerIdSource = 0;
+ bool m_queueModified = false;
+ bool m_peekerIndexCacheDirty = false;
+ QHash<qint32, QXcbEventNode *> m_peekerToNode;
+
+ QVector<xcb_generic_event_t *> m_inputEvents;
+
+ // debug stats
+ quint64 m_nodesOnHeap = 0;
+};
+
+template<typename Peeker>
+xcb_generic_event_t *QXcbEventQueue::peek(PeekOption option, Peeker &&peeker)
+{
+ flushBufferedEvents();
+ if (isEmpty())
+ return nullptr;
+
+ QXcbEventNode *node = m_head;
+ do {
+ xcb_generic_event_t *event = node->event;
+ if (event && peeker(event, event->response_type & ~0x80)) {
+ if (option == PeekRemoveMatch || option == PeekRemoveMatchContinue)
+ node->event = nullptr;
+ if (option != PeekRemoveMatchContinue)
+ return event;
+ }
+ if (node == m_flushedTail)
+ break;
+ node = node->next;
+ } while (true);
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp
index bf9eaacbb8..ed9e87a036 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.cpp
+++ b/src/plugins/platforms/xcb/qxcbintegration.cpp
@@ -46,6 +46,8 @@
#include "qxcbbackingstore.h"
#include "qxcbnativeinterface.h"
#include "qxcbclipboard.h"
+#include "qxcbeventqueue.h"
+#include "qxcbeventdispatcher.h"
#if QT_CONFIG(draganddrop)
#include "qxcbdrag.h"
#endif
@@ -57,7 +59,6 @@
#include <xcb/xcb.h>
-#include <QtEventDispatcherSupport/private/qgenericunixeventdispatcher_p.h>
#include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h>
#include <QtServiceSupport/private/qgenericunixservices_p.h>
@@ -69,12 +70,12 @@
#define register /* C++17 deprecated register */
#include <X11/Xlib.h>
#undef register
+#endif
#if QT_CONFIG(xcb_native_painting)
#include "qxcbnativepainting.h"
#include "qpixmap_x11_p.h"
#include "qbackingstore_x11_p.h"
#endif
-#endif
#include <qpa/qplatforminputcontextfactory_p.h>
#include <private/qgenericunixthemes_p.h>
@@ -136,6 +137,8 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters, int &argc, char
m_instance = this;
qApp->setAttribute(Qt::AA_CompressHighFrequencyEvents, true);
+ QWindowSystemInterface::setPlatformFiltersEvents(true);
+
qRegisterMetaType<QXcbWindow*>();
#if QT_CONFIG(xcb_xlib)
XInitThreads();
@@ -192,14 +195,17 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters, int &argc, char
const int numParameters = parameters.size();
m_connections.reserve(1 + numParameters / 2);
+
auto conn = new QXcbConnection(m_nativeInterface.data(), m_canGrab, m_defaultVisualId, displayName);
- if (conn->isConnected())
- m_connections << conn;
- else
+ if (!conn->isConnected()) {
delete conn;
+ return;
+ }
+ m_connections << conn;
+ // ### Qt 6 (QTBUG-52408) remove this multi-connection code path
for (int i = 0; i < numParameters - 1; i += 2) {
- qCDebug(lcQpaScreen) << "connecting to additional display: " << parameters.at(i) << parameters.at(i+1);
+ qCDebug(lcQpaXcb) << "connecting to additional display: " << parameters.at(i) << parameters.at(i+1);
QString display = parameters.at(i) + QLatin1Char(':') + parameters.at(i+1);
conn = new QXcbConnection(m_nativeInterface.data(), m_canGrab, m_defaultVisualId, display.toLatin1().constData());
if (conn->isConnected())
@@ -208,11 +214,6 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters, int &argc, char
delete conn;
}
- if (m_connections.isEmpty()) {
- qCritical("Could not connect to any X display.");
- exit(1);
- }
-
m_fontDatabase.reset(new QGenericUnixFontDatabase());
#if QT_CONFIG(xcb_native_painting)
@@ -241,10 +242,11 @@ QPlatformPixmap *QXcbIntegration::createPlatformPixmap(QPlatformPixmap::PixelTyp
QPlatformWindow *QXcbIntegration::createPlatformWindow(QWindow *window) const
{
- QXcbScreen *screen = static_cast<QXcbScreen *>(window->screen()->handle());
- QXcbGlIntegration *glIntegration = screen->connection()->glIntegration();
- if (window->type() != Qt::Desktop) {
+ QXcbGlIntegration *glIntegration = nullptr;
+ const bool isTrayIconWindow = QXcbWindow::isTrayIconWindow(window);;
+ if (window->type() != Qt::Desktop && !isTrayIconWindow) {
if (window->supportsOpenGL()) {
+ glIntegration = defaultConnection()->glIntegration();
if (glIntegration) {
QXcbWindow *xcbWindow = glIntegration->createWindow(window);
xcbWindow->create();
@@ -259,7 +261,7 @@ QPlatformWindow *QXcbIntegration::createPlatformWindow(QWindow *window) const
}
}
- Q_ASSERT(window->type() == Qt::Desktop || !window->supportsOpenGL()
+ Q_ASSERT(window->type() == Qt::Desktop || isTrayIconWindow || !window->supportsOpenGL()
|| (!glIntegration && window->surfaceType() == QSurface::RasterGLSurface)); // for VNC
QXcbWindow *xcbWindow = new QXcbWindow(window);
xcbWindow->create();
@@ -286,6 +288,10 @@ QPlatformOpenGLContext *QXcbIntegration::createPlatformOpenGLContext(QOpenGLCont
QPlatformBackingStore *QXcbIntegration::createPlatformBackingStore(QWindow *window) const
{
+ const bool isTrayIconWindow = QXcbWindow::isTrayIconWindow(window);
+ if (isTrayIconWindow)
+ return new QXcbSystemTrayBackingStore(window);
+
#if QT_CONFIG(xcb_native_painting)
if (nativePaintingEnabled())
return new QXcbNativeBackingStore(window);
@@ -313,8 +319,7 @@ bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const
{
const auto *connection = qAsConst(m_connections).first();
if (const auto *integration = connection->glIntegration())
- return cap != ThreadedOpenGL
- || (connection->threadedEventHandling() && integration->supportsThreadedOpenGL());
+ return cap != ThreadedOpenGL || integration->supportsThreadedOpenGL();
return false;
}
@@ -338,10 +343,7 @@ bool QXcbIntegration::hasCapability(QPlatformIntegration::Capability cap) const
QAbstractEventDispatcher *QXcbIntegration::createEventDispatcher() const
{
- QAbstractEventDispatcher *dispatcher = createUnixEventDispatcher();
- for (int i = 0; i < m_connections.size(); i++)
- m_connections[i]->eventReader()->registerEventDispatcher(dispatcher);
- return dispatcher;
+ return QXcbEventDispatcher::createEventDispatcher(defaultConnection());
}
void QXcbIntegration::initialize()
@@ -381,8 +383,17 @@ QPlatformClipboard *QXcbIntegration::clipboard() const
#endif
#if QT_CONFIG(draganddrop)
+#include <private/qsimpledrag_p.h>
QPlatformDrag *QXcbIntegration::drag() const
{
+ static const bool useSimpleDrag = qEnvironmentVariableIsSet("QT_XCB_USE_SIMPLE_DRAG");
+ if (Q_UNLIKELY(useSimpleDrag)) { // This is useful for testing purposes
+ static QSimpleDrag *simpleDrag = nullptr;
+ if (!simpleDrag)
+ simpleDrag = new QSimpleDrag();
+ return simpleDrag;
+ }
+
return m_connections.at(0)->drag();
}
#endif
@@ -414,10 +425,7 @@ QPlatformServices *QXcbIntegration::services() const
Qt::KeyboardModifiers QXcbIntegration::queryKeyboardModifiers() const
{
- int keybMask = 0;
- QXcbConnection *conn = m_connections.at(0);
- QXcbCursor::queryPointer(conn, 0, 0, &keybMask);
- return conn->keyboard()->translateModifiers(keybMask);
+ return m_connections.at(0)->queryKeyboardModifiers();
}
QList<int> QXcbIntegration::possibleKeys(const QKeyEvent *e) const
diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h
index 69e49cb7f6..f13e232291 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.h
+++ b/src/plugins/platforms/xcb/qxcbintegration.h
@@ -53,7 +53,6 @@ QT_BEGIN_NAMESPACE
class QXcbConnection;
class QAbstractEventDispatcher;
class QXcbNativeInterface;
-class QXcbScreen;
class Q_XCB_EXPORT QXcbIntegration : public QPlatformIntegration
{
@@ -103,6 +102,7 @@ public:
QPlatformTheme *createPlatformTheme(const QString &name) const override;
QVariant styleHint(StyleHint hint) const override;
+ bool hasDefaultConnection() const { return !m_connections.isEmpty(); }
QXcbConnection *defaultConnection() const { return m_connections.first(); }
QByteArray wmClass() const;
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index 3733995a0d..c5dc7b21ad 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -49,366 +49,388 @@
#include <QtCore/QMetaEnum>
#include <private/qguiapplication_p.h>
+#include <private/qmakearray_p.h>
#include <xkbcommon/xkbcommon-keysyms.h>
-#if QT_CONFIG(xinput2)
-#include <X11/extensions/XI2proto.h>
-#undef KeyPress
-#undef KeyRelease
+#if QT_CONFIG(xcb_xinput)
+#include <xcb/xinput.h>
#endif
QT_BEGIN_NAMESPACE
-static const unsigned int KeyTbl[] = {
- // misc keys
-
- XKB_KEY_Escape, Qt::Key_Escape,
- XKB_KEY_Tab, Qt::Key_Tab,
- XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab,
- XKB_KEY_BackSpace, Qt::Key_Backspace,
- XKB_KEY_Return, Qt::Key_Return,
- XKB_KEY_Insert, Qt::Key_Insert,
- XKB_KEY_Delete, Qt::Key_Delete,
- XKB_KEY_Clear, Qt::Key_Delete,
- XKB_KEY_Pause, Qt::Key_Pause,
- XKB_KEY_Print, Qt::Key_Print,
- 0x1005FF60, Qt::Key_SysReq, // hardcoded Sun SysReq
- 0x1007ff00, Qt::Key_SysReq, // hardcoded X386 SysReq
-
- // cursor movement
-
- XKB_KEY_Home, Qt::Key_Home,
- XKB_KEY_End, Qt::Key_End,
- XKB_KEY_Left, Qt::Key_Left,
- XKB_KEY_Up, Qt::Key_Up,
- XKB_KEY_Right, Qt::Key_Right,
- XKB_KEY_Down, Qt::Key_Down,
- XKB_KEY_Prior, Qt::Key_PageUp,
- XKB_KEY_Next, Qt::Key_PageDown,
-
- // modifiers
-
- XKB_KEY_Shift_L, Qt::Key_Shift,
- XKB_KEY_Shift_R, Qt::Key_Shift,
- XKB_KEY_Shift_Lock, Qt::Key_Shift,
- XKB_KEY_Control_L, Qt::Key_Control,
- XKB_KEY_Control_R, Qt::Key_Control,
- XKB_KEY_Meta_L, Qt::Key_Meta,
- XKB_KEY_Meta_R, Qt::Key_Meta,
- XKB_KEY_Alt_L, Qt::Key_Alt,
- XKB_KEY_Alt_R, Qt::Key_Alt,
- XKB_KEY_Caps_Lock, Qt::Key_CapsLock,
- XKB_KEY_Num_Lock, Qt::Key_NumLock,
- XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock,
- XKB_KEY_Super_L, Qt::Key_Super_L,
- XKB_KEY_Super_R, Qt::Key_Super_R,
- XKB_KEY_Menu, Qt::Key_Menu,
- XKB_KEY_Hyper_L, Qt::Key_Hyper_L,
- XKB_KEY_Hyper_R, Qt::Key_Hyper_R,
- XKB_KEY_Help, Qt::Key_Help,
- 0x1000FF74, Qt::Key_Backtab, // hardcoded HP backtab
- 0x1005FF10, Qt::Key_F11, // hardcoded Sun F36 (labeled F11)
- 0x1005FF11, Qt::Key_F12, // hardcoded Sun F37 (labeled F12)
-
- // numeric and function keypad keys
-
- XKB_KEY_KP_Space, Qt::Key_Space,
- XKB_KEY_KP_Tab, Qt::Key_Tab,
- XKB_KEY_KP_Enter, Qt::Key_Enter,
- //XKB_KEY_KP_F1, Qt::Key_F1,
- //XKB_KEY_KP_F2, Qt::Key_F2,
- //XKB_KEY_KP_F3, Qt::Key_F3,
- //XKB_KEY_KP_F4, Qt::Key_F4,
- XKB_KEY_KP_Home, Qt::Key_Home,
- XKB_KEY_KP_Left, Qt::Key_Left,
- XKB_KEY_KP_Up, Qt::Key_Up,
- XKB_KEY_KP_Right, Qt::Key_Right,
- XKB_KEY_KP_Down, Qt::Key_Down,
- XKB_KEY_KP_Prior, Qt::Key_PageUp,
- XKB_KEY_KP_Next, Qt::Key_PageDown,
- XKB_KEY_KP_End, Qt::Key_End,
- XKB_KEY_KP_Begin, Qt::Key_Clear,
- XKB_KEY_KP_Insert, Qt::Key_Insert,
- XKB_KEY_KP_Delete, Qt::Key_Delete,
- XKB_KEY_KP_Equal, Qt::Key_Equal,
- XKB_KEY_KP_Multiply, Qt::Key_Asterisk,
- XKB_KEY_KP_Add, Qt::Key_Plus,
- XKB_KEY_KP_Separator, Qt::Key_Comma,
- XKB_KEY_KP_Subtract, Qt::Key_Minus,
- XKB_KEY_KP_Decimal, Qt::Key_Period,
- XKB_KEY_KP_Divide, Qt::Key_Slash,
-
- // special non-XF86 function keys
-
- XKB_KEY_Undo, Qt::Key_Undo,
- XKB_KEY_Redo, Qt::Key_Redo,
- XKB_KEY_Find, Qt::Key_Find,
- XKB_KEY_Cancel, Qt::Key_Cancel,
-
- // International input method support keys
-
- // International & multi-key character composition
- XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr,
- XKB_KEY_Multi_key, Qt::Key_Multi_key,
- XKB_KEY_Codeinput, Qt::Key_Codeinput,
- XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate,
- XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate,
- XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate,
-
- // Misc Functions
- XKB_KEY_Mode_switch, Qt::Key_Mode_switch,
- XKB_KEY_script_switch, Qt::Key_Mode_switch,
-
- // Japanese keyboard support
- XKB_KEY_Kanji, Qt::Key_Kanji,
- XKB_KEY_Muhenkan, Qt::Key_Muhenkan,
- //XKB_KEY_Henkan_Mode, Qt::Key_Henkan_Mode,
- XKB_KEY_Henkan_Mode, Qt::Key_Henkan,
- XKB_KEY_Henkan, Qt::Key_Henkan,
- XKB_KEY_Romaji, Qt::Key_Romaji,
- XKB_KEY_Hiragana, Qt::Key_Hiragana,
- XKB_KEY_Katakana, Qt::Key_Katakana,
- XKB_KEY_Hiragana_Katakana, Qt::Key_Hiragana_Katakana,
- XKB_KEY_Zenkaku, Qt::Key_Zenkaku,
- XKB_KEY_Hankaku, Qt::Key_Hankaku,
- XKB_KEY_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku,
- XKB_KEY_Touroku, Qt::Key_Touroku,
- XKB_KEY_Massyo, Qt::Key_Massyo,
- XKB_KEY_Kana_Lock, Qt::Key_Kana_Lock,
- XKB_KEY_Kana_Shift, Qt::Key_Kana_Shift,
- XKB_KEY_Eisu_Shift, Qt::Key_Eisu_Shift,
- XKB_KEY_Eisu_toggle, Qt::Key_Eisu_toggle,
- //XKB_KEY_Kanji_Bangou, Qt::Key_Kanji_Bangou,
- //XKB_KEY_Zen_Koho, Qt::Key_Zen_Koho,
- //XKB_KEY_Mae_Koho, Qt::Key_Mae_Koho,
- XKB_KEY_Kanji_Bangou, Qt::Key_Codeinput,
- XKB_KEY_Zen_Koho, Qt::Key_MultipleCandidate,
- XKB_KEY_Mae_Koho, Qt::Key_PreviousCandidate,
-
- // Korean keyboard support
- XKB_KEY_Hangul, Qt::Key_Hangul,
- XKB_KEY_Hangul_Start, Qt::Key_Hangul_Start,
- XKB_KEY_Hangul_End, Qt::Key_Hangul_End,
- XKB_KEY_Hangul_Hanja, Qt::Key_Hangul_Hanja,
- XKB_KEY_Hangul_Jamo, Qt::Key_Hangul_Jamo,
- XKB_KEY_Hangul_Romaja, Qt::Key_Hangul_Romaja,
- //XKB_KEY_Hangul_Codeinput, Qt::Key_Hangul_Codeinput,
- XKB_KEY_Hangul_Codeinput, Qt::Key_Codeinput,
- XKB_KEY_Hangul_Jeonja, Qt::Key_Hangul_Jeonja,
- XKB_KEY_Hangul_Banja, Qt::Key_Hangul_Banja,
- XKB_KEY_Hangul_PreHanja, Qt::Key_Hangul_PreHanja,
- XKB_KEY_Hangul_PostHanja, Qt::Key_Hangul_PostHanja,
- //XKB_KEY_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate,
- //XKB_KEY_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate,
- //XKB_KEY_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate,
- XKB_KEY_Hangul_SingleCandidate, Qt::Key_SingleCandidate,
- XKB_KEY_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate,
- XKB_KEY_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate,
- XKB_KEY_Hangul_Special, Qt::Key_Hangul_Special,
- //XKB_KEY_Hangul_switch, Qt::Key_Hangul_switch,
- XKB_KEY_Hangul_switch, Qt::Key_Mode_switch,
-
- // dead keys
- XKB_KEY_dead_grave, Qt::Key_Dead_Grave,
- XKB_KEY_dead_acute, Qt::Key_Dead_Acute,
- XKB_KEY_dead_circumflex, Qt::Key_Dead_Circumflex,
- XKB_KEY_dead_tilde, Qt::Key_Dead_Tilde,
- XKB_KEY_dead_macron, Qt::Key_Dead_Macron,
- XKB_KEY_dead_breve, Qt::Key_Dead_Breve,
- XKB_KEY_dead_abovedot, Qt::Key_Dead_Abovedot,
- XKB_KEY_dead_diaeresis, Qt::Key_Dead_Diaeresis,
- XKB_KEY_dead_abovering, Qt::Key_Dead_Abovering,
- XKB_KEY_dead_doubleacute, Qt::Key_Dead_Doubleacute,
- XKB_KEY_dead_caron, Qt::Key_Dead_Caron,
- XKB_KEY_dead_cedilla, Qt::Key_Dead_Cedilla,
- XKB_KEY_dead_ogonek, Qt::Key_Dead_Ogonek,
- XKB_KEY_dead_iota, Qt::Key_Dead_Iota,
- XKB_KEY_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound,
- XKB_KEY_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound,
- XKB_KEY_dead_belowdot, Qt::Key_Dead_Belowdot,
- XKB_KEY_dead_hook, Qt::Key_Dead_Hook,
- XKB_KEY_dead_horn, Qt::Key_Dead_Horn,
- XKB_KEY_dead_stroke, Qt::Key_Dead_Stroke,
- XKB_KEY_dead_abovecomma, Qt::Key_Dead_Abovecomma,
- XKB_KEY_dead_abovereversedcomma, Qt::Key_Dead_Abovereversedcomma,
- XKB_KEY_dead_doublegrave, Qt::Key_Dead_Doublegrave,
- XKB_KEY_dead_belowring, Qt::Key_Dead_Belowring,
- XKB_KEY_dead_belowmacron, Qt::Key_Dead_Belowmacron,
- XKB_KEY_dead_belowcircumflex, Qt::Key_Dead_Belowcircumflex,
- XKB_KEY_dead_belowtilde, Qt::Key_Dead_Belowtilde,
- XKB_KEY_dead_belowbreve, Qt::Key_Dead_Belowbreve,
- XKB_KEY_dead_belowdiaeresis, Qt::Key_Dead_Belowdiaeresis,
- XKB_KEY_dead_invertedbreve, Qt::Key_Dead_Invertedbreve,
- XKB_KEY_dead_belowcomma, Qt::Key_Dead_Belowcomma,
- XKB_KEY_dead_currency, Qt::Key_Dead_Currency,
- XKB_KEY_dead_a, Qt::Key_Dead_a,
- XKB_KEY_dead_A, Qt::Key_Dead_A,
- XKB_KEY_dead_e, Qt::Key_Dead_e,
- XKB_KEY_dead_E, Qt::Key_Dead_E,
- XKB_KEY_dead_i, Qt::Key_Dead_i,
- XKB_KEY_dead_I, Qt::Key_Dead_I,
- XKB_KEY_dead_o, Qt::Key_Dead_o,
- XKB_KEY_dead_O, Qt::Key_Dead_O,
- XKB_KEY_dead_u, Qt::Key_Dead_u,
- XKB_KEY_dead_U, Qt::Key_Dead_U,
- XKB_KEY_dead_small_schwa, Qt::Key_Dead_Small_Schwa,
- XKB_KEY_dead_capital_schwa, Qt::Key_Dead_Capital_Schwa,
- XKB_KEY_dead_greek, Qt::Key_Dead_Greek,
- XKB_KEY_dead_lowline, Qt::Key_Dead_Lowline,
- XKB_KEY_dead_aboveverticalline, Qt::Key_Dead_Aboveverticalline,
- XKB_KEY_dead_belowverticalline, Qt::Key_Dead_Belowverticalline,
- XKB_KEY_dead_longsolidusoverlay, Qt::Key_Dead_Longsolidusoverlay,
-
- // Special keys from X.org - This include multimedia keys,
- // wireless/bluetooth/uwb keys, special launcher keys, etc.
- XKB_KEY_XF86Back, Qt::Key_Back,
- XKB_KEY_XF86Forward, Qt::Key_Forward,
- XKB_KEY_XF86Stop, Qt::Key_Stop,
- XKB_KEY_XF86Refresh, Qt::Key_Refresh,
- XKB_KEY_XF86Favorites, Qt::Key_Favorites,
- XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia,
- XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl,
- XKB_KEY_XF86HomePage, Qt::Key_HomePage,
- XKB_KEY_XF86Search, Qt::Key_Search,
- XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown,
- XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute,
- XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp,
- XKB_KEY_XF86AudioPlay, Qt::Key_MediaPlay,
- XKB_KEY_XF86AudioStop, Qt::Key_MediaStop,
- XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious,
- XKB_KEY_XF86AudioNext, Qt::Key_MediaNext,
- XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord,
- XKB_KEY_XF86AudioPause, Qt::Key_MediaPause,
- XKB_KEY_XF86Mail, Qt::Key_LaunchMail,
- XKB_KEY_XF86MyComputer, Qt::Key_Launch0, // ### Qt 6: remap properly
- XKB_KEY_XF86Calculator, Qt::Key_Launch1,
- XKB_KEY_XF86Memo, Qt::Key_Memo,
- XKB_KEY_XF86ToDoList, Qt::Key_ToDoList,
- XKB_KEY_XF86Calendar, Qt::Key_Calendar,
- XKB_KEY_XF86PowerDown, Qt::Key_PowerDown,
- XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust,
- XKB_KEY_XF86Standby, Qt::Key_Standby,
- XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp,
- XKB_KEY_XF86MonBrightnessDown, Qt::Key_MonBrightnessDown,
- XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff,
- XKB_KEY_XF86KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp,
- XKB_KEY_XF86KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown,
- XKB_KEY_XF86PowerOff, Qt::Key_PowerOff,
- XKB_KEY_XF86WakeUp, Qt::Key_WakeUp,
- XKB_KEY_XF86Eject, Qt::Key_Eject,
- XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver,
- XKB_KEY_XF86WWW, Qt::Key_WWW,
- XKB_KEY_XF86Sleep, Qt::Key_Sleep,
- XKB_KEY_XF86LightBulb, Qt::Key_LightBulb,
- XKB_KEY_XF86Shop, Qt::Key_Shop,
- XKB_KEY_XF86History, Qt::Key_History,
- XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite,
- XKB_KEY_XF86HotLinks, Qt::Key_HotLinks,
- XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust,
- XKB_KEY_XF86Finance, Qt::Key_Finance,
- XKB_KEY_XF86Community, Qt::Key_Community,
- XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind,
- XKB_KEY_XF86BackForward, Qt::Key_BackForward,
- XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft,
- XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight,
- XKB_KEY_XF86Book, Qt::Key_Book,
- XKB_KEY_XF86CD, Qt::Key_CD,
- XKB_KEY_XF86Calculater, Qt::Key_Calculator,
- XKB_KEY_XF86Clear, Qt::Key_Clear,
- XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab,
- XKB_KEY_XF86Close, Qt::Key_Close,
- XKB_KEY_XF86Copy, Qt::Key_Copy,
- XKB_KEY_XF86Cut, Qt::Key_Cut,
- XKB_KEY_XF86Display, Qt::Key_Display,
- XKB_KEY_XF86DOS, Qt::Key_DOS,
- XKB_KEY_XF86Documents, Qt::Key_Documents,
- XKB_KEY_XF86Excel, Qt::Key_Excel,
- XKB_KEY_XF86Explorer, Qt::Key_Explorer,
- XKB_KEY_XF86Game, Qt::Key_Game,
- XKB_KEY_XF86Go, Qt::Key_Go,
- XKB_KEY_XF86iTouch, Qt::Key_iTouch,
- XKB_KEY_XF86LogOff, Qt::Key_LogOff,
- XKB_KEY_XF86Market, Qt::Key_Market,
- XKB_KEY_XF86Meeting, Qt::Key_Meeting,
- XKB_KEY_XF86MenuKB, Qt::Key_MenuKB,
- XKB_KEY_XF86MenuPB, Qt::Key_MenuPB,
- XKB_KEY_XF86MySites, Qt::Key_MySites,
- XKB_KEY_XF86New, Qt::Key_New,
- XKB_KEY_XF86News, Qt::Key_News,
- XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome,
- XKB_KEY_XF86Open, Qt::Key_Open,
- XKB_KEY_XF86Option, Qt::Key_Option,
- XKB_KEY_XF86Paste, Qt::Key_Paste,
- XKB_KEY_XF86Phone, Qt::Key_Phone,
- XKB_KEY_XF86Reply, Qt::Key_Reply,
- XKB_KEY_XF86Reload, Qt::Key_Reload,
- XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows,
- XKB_KEY_XF86RotationPB, Qt::Key_RotationPB,
- XKB_KEY_XF86RotationKB, Qt::Key_RotationKB,
- XKB_KEY_XF86Save, Qt::Key_Save,
- XKB_KEY_XF86Send, Qt::Key_Send,
- XKB_KEY_XF86Spell, Qt::Key_Spell,
- XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen,
- XKB_KEY_XF86Support, Qt::Key_Support,
- XKB_KEY_XF86TaskPane, Qt::Key_TaskPane,
- XKB_KEY_XF86Terminal, Qt::Key_Terminal,
- XKB_KEY_XF86Tools, Qt::Key_Tools,
- XKB_KEY_XF86Travel, Qt::Key_Travel,
- XKB_KEY_XF86Video, Qt::Key_Video,
- XKB_KEY_XF86Word, Qt::Key_Word,
- XKB_KEY_XF86Xfer, Qt::Key_Xfer,
- XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn,
- XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut,
- XKB_KEY_XF86Away, Qt::Key_Away,
- XKB_KEY_XF86Messenger, Qt::Key_Messenger,
- XKB_KEY_XF86WebCam, Qt::Key_WebCam,
- XKB_KEY_XF86MailForward, Qt::Key_MailForward,
- XKB_KEY_XF86Pictures, Qt::Key_Pictures,
- XKB_KEY_XF86Music, Qt::Key_Music,
- XKB_KEY_XF86Battery, Qt::Key_Battery,
- XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth,
- XKB_KEY_XF86WLAN, Qt::Key_WLAN,
- XKB_KEY_XF86UWB, Qt::Key_UWB,
- XKB_KEY_XF86AudioForward, Qt::Key_AudioForward,
- XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat,
- XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay,
- XKB_KEY_XF86Subtitle, Qt::Key_Subtitle,
- XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack,
- XKB_KEY_XF86Time, Qt::Key_Time,
- XKB_KEY_XF86Select, Qt::Key_Select,
- XKB_KEY_XF86View, Qt::Key_View,
- XKB_KEY_XF86TopMenu, Qt::Key_TopMenu,
- XKB_KEY_XF86Red, Qt::Key_Red,
- XKB_KEY_XF86Green, Qt::Key_Green,
- XKB_KEY_XF86Yellow, Qt::Key_Yellow,
- XKB_KEY_XF86Blue, Qt::Key_Blue,
- XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth,
- XKB_KEY_XF86Suspend, Qt::Key_Suspend,
- XKB_KEY_XF86Hibernate, Qt::Key_Hibernate,
- XKB_KEY_XF86TouchpadToggle, Qt::Key_TouchpadToggle,
- XKB_KEY_XF86TouchpadOn, Qt::Key_TouchpadOn,
- XKB_KEY_XF86TouchpadOff, Qt::Key_TouchpadOff,
- XKB_KEY_XF86AudioMicMute, Qt::Key_MicMute,
- XKB_KEY_XF86Launch0, Qt::Key_Launch2, // ### Qt 6: remap properly
- XKB_KEY_XF86Launch1, Qt::Key_Launch3,
- XKB_KEY_XF86Launch2, Qt::Key_Launch4,
- XKB_KEY_XF86Launch3, Qt::Key_Launch5,
- XKB_KEY_XF86Launch4, Qt::Key_Launch6,
- XKB_KEY_XF86Launch5, Qt::Key_Launch7,
- XKB_KEY_XF86Launch6, Qt::Key_Launch8,
- XKB_KEY_XF86Launch7, Qt::Key_Launch9,
- XKB_KEY_XF86Launch8, Qt::Key_LaunchA,
- XKB_KEY_XF86Launch9, Qt::Key_LaunchB,
- XKB_KEY_XF86LaunchA, Qt::Key_LaunchC,
- XKB_KEY_XF86LaunchB, Qt::Key_LaunchD,
- XKB_KEY_XF86LaunchC, Qt::Key_LaunchE,
- XKB_KEY_XF86LaunchD, Qt::Key_LaunchF,
- XKB_KEY_XF86LaunchE, Qt::Key_LaunchG,
- XKB_KEY_XF86LaunchF, Qt::Key_LaunchH,
-
- 0, 0
+typedef struct xkb2qt
+{
+ unsigned int xkb;
+ unsigned int qt;
+
+ constexpr bool operator <=(const xkb2qt &that) const noexcept
+ {
+ return xkb <= that.xkb;
+ }
+
+ constexpr bool operator <(const xkb2qt &that) const noexcept
+ {
+ return xkb < that.xkb;
+ }
+} xkb2qt_t;
+
+template<std::size_t Xkb, std::size_t Qt>
+struct Xkb2Qt
+{
+ using Type = xkb2qt_t;
+ static constexpr Type data() noexcept { return Type{Xkb, Qt}; }
};
+static constexpr const auto KeyTbl = qMakeArray(
+ QSortedData<
+ // misc keys
+
+ Xkb2Qt<XKB_KEY_Escape, Qt::Key_Escape>,
+ Xkb2Qt<XKB_KEY_Tab, Qt::Key_Tab>,
+ Xkb2Qt<XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab>,
+ Xkb2Qt<XKB_KEY_BackSpace, Qt::Key_Backspace>,
+ Xkb2Qt<XKB_KEY_Return, Qt::Key_Return>,
+ Xkb2Qt<XKB_KEY_Insert, Qt::Key_Insert>,
+ Xkb2Qt<XKB_KEY_Delete, Qt::Key_Delete>,
+ Xkb2Qt<XKB_KEY_Clear, Qt::Key_Delete>,
+ Xkb2Qt<XKB_KEY_Pause, Qt::Key_Pause>,
+ Xkb2Qt<XKB_KEY_Print, Qt::Key_Print>,
+ Xkb2Qt<0x1005FF60, Qt::Key_SysReq>, // hardcoded Sun SysReq
+ Xkb2Qt<0x1007ff00, Qt::Key_SysReq>, // hardcoded X386 SysReq
+
+ // cursor movement
+
+ Xkb2Qt<XKB_KEY_Home, Qt::Key_Home>,
+ Xkb2Qt<XKB_KEY_End, Qt::Key_End>,
+ Xkb2Qt<XKB_KEY_Left, Qt::Key_Left>,
+ Xkb2Qt<XKB_KEY_Up, Qt::Key_Up>,
+ Xkb2Qt<XKB_KEY_Right, Qt::Key_Right>,
+ Xkb2Qt<XKB_KEY_Down, Qt::Key_Down>,
+ Xkb2Qt<XKB_KEY_Prior, Qt::Key_PageUp>,
+ Xkb2Qt<XKB_KEY_Next, Qt::Key_PageDown>,
+
+ // modifiers
+
+ Xkb2Qt<XKB_KEY_Shift_L, Qt::Key_Shift>,
+ Xkb2Qt<XKB_KEY_Shift_R, Qt::Key_Shift>,
+ Xkb2Qt<XKB_KEY_Shift_Lock, Qt::Key_Shift>,
+ Xkb2Qt<XKB_KEY_Control_L, Qt::Key_Control>,
+ Xkb2Qt<XKB_KEY_Control_R, Qt::Key_Control>,
+ Xkb2Qt<XKB_KEY_Meta_L, Qt::Key_Meta>,
+ Xkb2Qt<XKB_KEY_Meta_R, Qt::Key_Meta>,
+ Xkb2Qt<XKB_KEY_Alt_L, Qt::Key_Alt>,
+ Xkb2Qt<XKB_KEY_Alt_R, Qt::Key_Alt>,
+ Xkb2Qt<XKB_KEY_Caps_Lock, Qt::Key_CapsLock>,
+ Xkb2Qt<XKB_KEY_Num_Lock, Qt::Key_NumLock>,
+ Xkb2Qt<XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock>,
+ Xkb2Qt<XKB_KEY_Super_L, Qt::Key_Super_L>,
+ Xkb2Qt<XKB_KEY_Super_R, Qt::Key_Super_R>,
+ Xkb2Qt<XKB_KEY_Menu, Qt::Key_Menu>,
+ Xkb2Qt<XKB_KEY_Hyper_L, Qt::Key_Hyper_L>,
+ Xkb2Qt<XKB_KEY_Hyper_R, Qt::Key_Hyper_R>,
+ Xkb2Qt<XKB_KEY_Help, Qt::Key_Help>,
+ Xkb2Qt<0x1000FF74, Qt::Key_Backtab>, // hardcoded HP backtab
+ Xkb2Qt<0x1005FF10, Qt::Key_F11>, // hardcoded Sun F36 (labeled F11)
+ Xkb2Qt<0x1005FF11, Qt::Key_F12>, // hardcoded Sun F37 (labeled F12)
+
+ // numeric and function keypad keys
+
+ Xkb2Qt<XKB_KEY_KP_Space, Qt::Key_Space>,
+ Xkb2Qt<XKB_KEY_KP_Tab, Qt::Key_Tab>,
+ Xkb2Qt<XKB_KEY_KP_Enter, Qt::Key_Enter>,
+ //Xkb2Qt<XKB_KEY_KP_F1, Qt::Key_F1>,
+ //Xkb2Qt<XKB_KEY_KP_F2, Qt::Key_F2>,
+ //Xkb2Qt<XKB_KEY_KP_F3, Qt::Key_F3>,
+ //Xkb2Qt<XKB_KEY_KP_F4, Qt::Key_F4>,
+ Xkb2Qt<XKB_KEY_KP_Home, Qt::Key_Home>,
+ Xkb2Qt<XKB_KEY_KP_Left, Qt::Key_Left>,
+ Xkb2Qt<XKB_KEY_KP_Up, Qt::Key_Up>,
+ Xkb2Qt<XKB_KEY_KP_Right, Qt::Key_Right>,
+ Xkb2Qt<XKB_KEY_KP_Down, Qt::Key_Down>,
+ Xkb2Qt<XKB_KEY_KP_Prior, Qt::Key_PageUp>,
+ Xkb2Qt<XKB_KEY_KP_Next, Qt::Key_PageDown>,
+ Xkb2Qt<XKB_KEY_KP_End, Qt::Key_End>,
+ Xkb2Qt<XKB_KEY_KP_Begin, Qt::Key_Clear>,
+ Xkb2Qt<XKB_KEY_KP_Insert, Qt::Key_Insert>,
+ Xkb2Qt<XKB_KEY_KP_Delete, Qt::Key_Delete>,
+ Xkb2Qt<XKB_KEY_KP_Equal, Qt::Key_Equal>,
+ Xkb2Qt<XKB_KEY_KP_Multiply, Qt::Key_Asterisk>,
+ Xkb2Qt<XKB_KEY_KP_Add, Qt::Key_Plus>,
+ Xkb2Qt<XKB_KEY_KP_Separator, Qt::Key_Comma>,
+ Xkb2Qt<XKB_KEY_KP_Subtract, Qt::Key_Minus>,
+ Xkb2Qt<XKB_KEY_KP_Decimal, Qt::Key_Period>,
+ Xkb2Qt<XKB_KEY_KP_Divide, Qt::Key_Slash>,
+
+ // special non-XF86 function keys
+
+ Xkb2Qt<XKB_KEY_Undo, Qt::Key_Undo>,
+ Xkb2Qt<XKB_KEY_Redo, Qt::Key_Redo>,
+ Xkb2Qt<XKB_KEY_Find, Qt::Key_Find>,
+ Xkb2Qt<XKB_KEY_Cancel, Qt::Key_Cancel>,
+
+ // International input method support keys
+
+ // International & multi-key character composition
+ Xkb2Qt<XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr>,
+ Xkb2Qt<XKB_KEY_Multi_key, Qt::Key_Multi_key>,
+ Xkb2Qt<XKB_KEY_Codeinput, Qt::Key_Codeinput>,
+ Xkb2Qt<XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate>,
+ Xkb2Qt<XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate>,
+ Xkb2Qt<XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate>,
+
+ // Misc Functions
+ Xkb2Qt<XKB_KEY_Mode_switch, Qt::Key_Mode_switch>,
+ Xkb2Qt<XKB_KEY_script_switch, Qt::Key_Mode_switch>,
+
+ // Japanese keyboard support
+ Xkb2Qt<XKB_KEY_Kanji, Qt::Key_Kanji>,
+ Xkb2Qt<XKB_KEY_Muhenkan, Qt::Key_Muhenkan>,
+ //Xkb2Qt<XKB_KEY_Henkan_Mode, Qt::Key_Henkan_Mode>,
+ Xkb2Qt<XKB_KEY_Henkan_Mode, Qt::Key_Henkan>,
+ Xkb2Qt<XKB_KEY_Henkan, Qt::Key_Henkan>,
+ Xkb2Qt<XKB_KEY_Romaji, Qt::Key_Romaji>,
+ Xkb2Qt<XKB_KEY_Hiragana, Qt::Key_Hiragana>,
+ Xkb2Qt<XKB_KEY_Katakana, Qt::Key_Katakana>,
+ Xkb2Qt<XKB_KEY_Hiragana_Katakana, Qt::Key_Hiragana_Katakana>,
+ Xkb2Qt<XKB_KEY_Zenkaku, Qt::Key_Zenkaku>,
+ Xkb2Qt<XKB_KEY_Hankaku, Qt::Key_Hankaku>,
+ Xkb2Qt<XKB_KEY_Zenkaku_Hankaku, Qt::Key_Zenkaku_Hankaku>,
+ Xkb2Qt<XKB_KEY_Touroku, Qt::Key_Touroku>,
+ Xkb2Qt<XKB_KEY_Massyo, Qt::Key_Massyo>,
+ Xkb2Qt<XKB_KEY_Kana_Lock, Qt::Key_Kana_Lock>,
+ Xkb2Qt<XKB_KEY_Kana_Shift, Qt::Key_Kana_Shift>,
+ Xkb2Qt<XKB_KEY_Eisu_Shift, Qt::Key_Eisu_Shift>,
+ Xkb2Qt<XKB_KEY_Eisu_toggle, Qt::Key_Eisu_toggle>,
+ //Xkb2Qt<XKB_KEY_Kanji_Bangou, Qt::Key_Kanji_Bangou>,
+ //Xkb2Qt<XKB_KEY_Zen_Koho, Qt::Key_Zen_Koho>,
+ //Xkb2Qt<XKB_KEY_Mae_Koho, Qt::Key_Mae_Koho>,
+ Xkb2Qt<XKB_KEY_Kanji_Bangou, Qt::Key_Codeinput>,
+ Xkb2Qt<XKB_KEY_Zen_Koho, Qt::Key_MultipleCandidate>,
+ Xkb2Qt<XKB_KEY_Mae_Koho, Qt::Key_PreviousCandidate>,
+
+ // Korean keyboard support
+ Xkb2Qt<XKB_KEY_Hangul, Qt::Key_Hangul>,
+ Xkb2Qt<XKB_KEY_Hangul_Start, Qt::Key_Hangul_Start>,
+ Xkb2Qt<XKB_KEY_Hangul_End, Qt::Key_Hangul_End>,
+ Xkb2Qt<XKB_KEY_Hangul_Hanja, Qt::Key_Hangul_Hanja>,
+ Xkb2Qt<XKB_KEY_Hangul_Jamo, Qt::Key_Hangul_Jamo>,
+ Xkb2Qt<XKB_KEY_Hangul_Romaja, Qt::Key_Hangul_Romaja>,
+ //Xkb2Qt<XKB_KEY_Hangul_Codeinput, Qt::Key_Hangul_Codeinput>,
+ Xkb2Qt<XKB_KEY_Hangul_Codeinput, Qt::Key_Codeinput>,
+ Xkb2Qt<XKB_KEY_Hangul_Jeonja, Qt::Key_Hangul_Jeonja>,
+ Xkb2Qt<XKB_KEY_Hangul_Banja, Qt::Key_Hangul_Banja>,
+ Xkb2Qt<XKB_KEY_Hangul_PreHanja, Qt::Key_Hangul_PreHanja>,
+ Xkb2Qt<XKB_KEY_Hangul_PostHanja, Qt::Key_Hangul_PostHanja>,
+ //Xkb2Qt<XKB_KEY_Hangul_SingleCandidate,Qt::Key_Hangul_SingleCandidate>,
+ //Xkb2Qt<XKB_KEY_Hangul_MultipleCandidate,Qt::Key_Hangul_MultipleCandidate>,
+ //Xkb2Qt<XKB_KEY_Hangul_PreviousCandidate,Qt::Key_Hangul_PreviousCandidate>,
+ Xkb2Qt<XKB_KEY_Hangul_SingleCandidate, Qt::Key_SingleCandidate>,
+ Xkb2Qt<XKB_KEY_Hangul_MultipleCandidate,Qt::Key_MultipleCandidate>,
+ Xkb2Qt<XKB_KEY_Hangul_PreviousCandidate,Qt::Key_PreviousCandidate>,
+ Xkb2Qt<XKB_KEY_Hangul_Special, Qt::Key_Hangul_Special>,
+ //Xkb2Qt<XKB_KEY_Hangul_switch, Qt::Key_Hangul_switch>,
+ Xkb2Qt<XKB_KEY_Hangul_switch, Qt::Key_Mode_switch>,
+
+ // dead keys
+ Xkb2Qt<XKB_KEY_dead_grave, Qt::Key_Dead_Grave>,
+ Xkb2Qt<XKB_KEY_dead_acute, Qt::Key_Dead_Acute>,
+ Xkb2Qt<XKB_KEY_dead_circumflex, Qt::Key_Dead_Circumflex>,
+ Xkb2Qt<XKB_KEY_dead_tilde, Qt::Key_Dead_Tilde>,
+ Xkb2Qt<XKB_KEY_dead_macron, Qt::Key_Dead_Macron>,
+ Xkb2Qt<XKB_KEY_dead_breve, Qt::Key_Dead_Breve>,
+ Xkb2Qt<XKB_KEY_dead_abovedot, Qt::Key_Dead_Abovedot>,
+ Xkb2Qt<XKB_KEY_dead_diaeresis, Qt::Key_Dead_Diaeresis>,
+ Xkb2Qt<XKB_KEY_dead_abovering, Qt::Key_Dead_Abovering>,
+ Xkb2Qt<XKB_KEY_dead_doubleacute, Qt::Key_Dead_Doubleacute>,
+ Xkb2Qt<XKB_KEY_dead_caron, Qt::Key_Dead_Caron>,
+ Xkb2Qt<XKB_KEY_dead_cedilla, Qt::Key_Dead_Cedilla>,
+ Xkb2Qt<XKB_KEY_dead_ogonek, Qt::Key_Dead_Ogonek>,
+ Xkb2Qt<XKB_KEY_dead_iota, Qt::Key_Dead_Iota>,
+ Xkb2Qt<XKB_KEY_dead_voiced_sound, Qt::Key_Dead_Voiced_Sound>,
+ Xkb2Qt<XKB_KEY_dead_semivoiced_sound, Qt::Key_Dead_Semivoiced_Sound>,
+ Xkb2Qt<XKB_KEY_dead_belowdot, Qt::Key_Dead_Belowdot>,
+ Xkb2Qt<XKB_KEY_dead_hook, Qt::Key_Dead_Hook>,
+ Xkb2Qt<XKB_KEY_dead_horn, Qt::Key_Dead_Horn>,
+ Xkb2Qt<XKB_KEY_dead_stroke, Qt::Key_Dead_Stroke>,
+ Xkb2Qt<XKB_KEY_dead_abovecomma, Qt::Key_Dead_Abovecomma>,
+ Xkb2Qt<XKB_KEY_dead_abovereversedcomma, Qt::Key_Dead_Abovereversedcomma>,
+ Xkb2Qt<XKB_KEY_dead_doublegrave, Qt::Key_Dead_Doublegrave>,
+ Xkb2Qt<XKB_KEY_dead_belowring, Qt::Key_Dead_Belowring>,
+ Xkb2Qt<XKB_KEY_dead_belowmacron, Qt::Key_Dead_Belowmacron>,
+ Xkb2Qt<XKB_KEY_dead_belowcircumflex, Qt::Key_Dead_Belowcircumflex>,
+ Xkb2Qt<XKB_KEY_dead_belowtilde, Qt::Key_Dead_Belowtilde>,
+ Xkb2Qt<XKB_KEY_dead_belowbreve, Qt::Key_Dead_Belowbreve>,
+ Xkb2Qt<XKB_KEY_dead_belowdiaeresis, Qt::Key_Dead_Belowdiaeresis>,
+ Xkb2Qt<XKB_KEY_dead_invertedbreve, Qt::Key_Dead_Invertedbreve>,
+ Xkb2Qt<XKB_KEY_dead_belowcomma, Qt::Key_Dead_Belowcomma>,
+ Xkb2Qt<XKB_KEY_dead_currency, Qt::Key_Dead_Currency>,
+ Xkb2Qt<XKB_KEY_dead_a, Qt::Key_Dead_a>,
+ Xkb2Qt<XKB_KEY_dead_A, Qt::Key_Dead_A>,
+ Xkb2Qt<XKB_KEY_dead_e, Qt::Key_Dead_e>,
+ Xkb2Qt<XKB_KEY_dead_E, Qt::Key_Dead_E>,
+ Xkb2Qt<XKB_KEY_dead_i, Qt::Key_Dead_i>,
+ Xkb2Qt<XKB_KEY_dead_I, Qt::Key_Dead_I>,
+ Xkb2Qt<XKB_KEY_dead_o, Qt::Key_Dead_o>,
+ Xkb2Qt<XKB_KEY_dead_O, Qt::Key_Dead_O>,
+ Xkb2Qt<XKB_KEY_dead_u, Qt::Key_Dead_u>,
+ Xkb2Qt<XKB_KEY_dead_U, Qt::Key_Dead_U>,
+ Xkb2Qt<XKB_KEY_dead_small_schwa, Qt::Key_Dead_Small_Schwa>,
+ Xkb2Qt<XKB_KEY_dead_capital_schwa, Qt::Key_Dead_Capital_Schwa>,
+ Xkb2Qt<XKB_KEY_dead_greek, Qt::Key_Dead_Greek>,
+ Xkb2Qt<XKB_KEY_dead_lowline, Qt::Key_Dead_Lowline>,
+ Xkb2Qt<XKB_KEY_dead_aboveverticalline, Qt::Key_Dead_Aboveverticalline>,
+ Xkb2Qt<XKB_KEY_dead_belowverticalline, Qt::Key_Dead_Belowverticalline>,
+ Xkb2Qt<XKB_KEY_dead_longsolidusoverlay, Qt::Key_Dead_Longsolidusoverlay>,
+
+ // Special keys from X.org - This include multimedia keys,
+ // wireless/bluetooth/uwb keys, special launcher keys, etc.
+ Xkb2Qt<XKB_KEY_XF86Back, Qt::Key_Back>,
+ Xkb2Qt<XKB_KEY_XF86Forward, Qt::Key_Forward>,
+ Xkb2Qt<XKB_KEY_XF86Stop, Qt::Key_Stop>,
+ Xkb2Qt<XKB_KEY_XF86Refresh, Qt::Key_Refresh>,
+ Xkb2Qt<XKB_KEY_XF86Favorites, Qt::Key_Favorites>,
+ Xkb2Qt<XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia>,
+ Xkb2Qt<XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl>,
+ Xkb2Qt<XKB_KEY_XF86HomePage, Qt::Key_HomePage>,
+ Xkb2Qt<XKB_KEY_XF86Search, Qt::Key_Search>,
+ Xkb2Qt<XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown>,
+ Xkb2Qt<XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute>,
+ Xkb2Qt<XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp>,
+ Xkb2Qt<XKB_KEY_XF86AudioPlay, Qt::Key_MediaPlay>,
+ Xkb2Qt<XKB_KEY_XF86AudioStop, Qt::Key_MediaStop>,
+ Xkb2Qt<XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious>,
+ Xkb2Qt<XKB_KEY_XF86AudioNext, Qt::Key_MediaNext>,
+ Xkb2Qt<XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord>,
+ Xkb2Qt<XKB_KEY_XF86AudioPause, Qt::Key_MediaPause>,
+ Xkb2Qt<XKB_KEY_XF86Mail, Qt::Key_LaunchMail>,
+ Xkb2Qt<XKB_KEY_XF86MyComputer, Qt::Key_Launch0>, // ### Qt 6: remap properly
+ Xkb2Qt<XKB_KEY_XF86Calculator, Qt::Key_Launch1>,
+ Xkb2Qt<XKB_KEY_XF86Memo, Qt::Key_Memo>,
+ Xkb2Qt<XKB_KEY_XF86ToDoList, Qt::Key_ToDoList>,
+ Xkb2Qt<XKB_KEY_XF86Calendar, Qt::Key_Calendar>,
+ Xkb2Qt<XKB_KEY_XF86PowerDown, Qt::Key_PowerDown>,
+ Xkb2Qt<XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust>,
+ Xkb2Qt<XKB_KEY_XF86Standby, Qt::Key_Standby>,
+ Xkb2Qt<XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp>,
+ Xkb2Qt<XKB_KEY_XF86MonBrightnessDown, Qt::Key_MonBrightnessDown>,
+ Xkb2Qt<XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff>,
+ Xkb2Qt<XKB_KEY_XF86KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp>,
+ Xkb2Qt<XKB_KEY_XF86KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown>,
+ Xkb2Qt<XKB_KEY_XF86PowerOff, Qt::Key_PowerOff>,
+ Xkb2Qt<XKB_KEY_XF86WakeUp, Qt::Key_WakeUp>,
+ Xkb2Qt<XKB_KEY_XF86Eject, Qt::Key_Eject>,
+ Xkb2Qt<XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver>,
+ Xkb2Qt<XKB_KEY_XF86WWW, Qt::Key_WWW>,
+ Xkb2Qt<XKB_KEY_XF86Sleep, Qt::Key_Sleep>,
+ Xkb2Qt<XKB_KEY_XF86LightBulb, Qt::Key_LightBulb>,
+ Xkb2Qt<XKB_KEY_XF86Shop, Qt::Key_Shop>,
+ Xkb2Qt<XKB_KEY_XF86History, Qt::Key_History>,
+ Xkb2Qt<XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite>,
+ Xkb2Qt<XKB_KEY_XF86HotLinks, Qt::Key_HotLinks>,
+ Xkb2Qt<XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust>,
+ Xkb2Qt<XKB_KEY_XF86Finance, Qt::Key_Finance>,
+ Xkb2Qt<XKB_KEY_XF86Community, Qt::Key_Community>,
+ Xkb2Qt<XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind>,
+ Xkb2Qt<XKB_KEY_XF86BackForward, Qt::Key_BackForward>,
+ Xkb2Qt<XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft>,
+ Xkb2Qt<XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight>,
+ Xkb2Qt<XKB_KEY_XF86Book, Qt::Key_Book>,
+ Xkb2Qt<XKB_KEY_XF86CD, Qt::Key_CD>,
+ Xkb2Qt<XKB_KEY_XF86Calculater, Qt::Key_Calculator>,
+ Xkb2Qt<XKB_KEY_XF86Clear, Qt::Key_Clear>,
+ Xkb2Qt<XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab>,
+ Xkb2Qt<XKB_KEY_XF86Close, Qt::Key_Close>,
+ Xkb2Qt<XKB_KEY_XF86Copy, Qt::Key_Copy>,
+ Xkb2Qt<XKB_KEY_XF86Cut, Qt::Key_Cut>,
+ Xkb2Qt<XKB_KEY_XF86Display, Qt::Key_Display>,
+ Xkb2Qt<XKB_KEY_XF86DOS, Qt::Key_DOS>,
+ Xkb2Qt<XKB_KEY_XF86Documents, Qt::Key_Documents>,
+ Xkb2Qt<XKB_KEY_XF86Excel, Qt::Key_Excel>,
+ Xkb2Qt<XKB_KEY_XF86Explorer, Qt::Key_Explorer>,
+ Xkb2Qt<XKB_KEY_XF86Game, Qt::Key_Game>,
+ Xkb2Qt<XKB_KEY_XF86Go, Qt::Key_Go>,
+ Xkb2Qt<XKB_KEY_XF86iTouch, Qt::Key_iTouch>,
+ Xkb2Qt<XKB_KEY_XF86LogOff, Qt::Key_LogOff>,
+ Xkb2Qt<XKB_KEY_XF86Market, Qt::Key_Market>,
+ Xkb2Qt<XKB_KEY_XF86Meeting, Qt::Key_Meeting>,
+ Xkb2Qt<XKB_KEY_XF86MenuKB, Qt::Key_MenuKB>,
+ Xkb2Qt<XKB_KEY_XF86MenuPB, Qt::Key_MenuPB>,
+ Xkb2Qt<XKB_KEY_XF86MySites, Qt::Key_MySites>,
+ Xkb2Qt<XKB_KEY_XF86New, Qt::Key_New>,
+ Xkb2Qt<XKB_KEY_XF86News, Qt::Key_News>,
+ Xkb2Qt<XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome>,
+ Xkb2Qt<XKB_KEY_XF86Open, Qt::Key_Open>,
+ Xkb2Qt<XKB_KEY_XF86Option, Qt::Key_Option>,
+ Xkb2Qt<XKB_KEY_XF86Paste, Qt::Key_Paste>,
+ Xkb2Qt<XKB_KEY_XF86Phone, Qt::Key_Phone>,
+ Xkb2Qt<XKB_KEY_XF86Reply, Qt::Key_Reply>,
+ Xkb2Qt<XKB_KEY_XF86Reload, Qt::Key_Reload>,
+ Xkb2Qt<XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows>,
+ Xkb2Qt<XKB_KEY_XF86RotationPB, Qt::Key_RotationPB>,
+ Xkb2Qt<XKB_KEY_XF86RotationKB, Qt::Key_RotationKB>,
+ Xkb2Qt<XKB_KEY_XF86Save, Qt::Key_Save>,
+ Xkb2Qt<XKB_KEY_XF86Send, Qt::Key_Send>,
+ Xkb2Qt<XKB_KEY_XF86Spell, Qt::Key_Spell>,
+ Xkb2Qt<XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen>,
+ Xkb2Qt<XKB_KEY_XF86Support, Qt::Key_Support>,
+ Xkb2Qt<XKB_KEY_XF86TaskPane, Qt::Key_TaskPane>,
+ Xkb2Qt<XKB_KEY_XF86Terminal, Qt::Key_Terminal>,
+ Xkb2Qt<XKB_KEY_XF86Tools, Qt::Key_Tools>,
+ Xkb2Qt<XKB_KEY_XF86Travel, Qt::Key_Travel>,
+ Xkb2Qt<XKB_KEY_XF86Video, Qt::Key_Video>,
+ Xkb2Qt<XKB_KEY_XF86Word, Qt::Key_Word>,
+ Xkb2Qt<XKB_KEY_XF86Xfer, Qt::Key_Xfer>,
+ Xkb2Qt<XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn>,
+ Xkb2Qt<XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut>,
+ Xkb2Qt<XKB_KEY_XF86Away, Qt::Key_Away>,
+ Xkb2Qt<XKB_KEY_XF86Messenger, Qt::Key_Messenger>,
+ Xkb2Qt<XKB_KEY_XF86WebCam, Qt::Key_WebCam>,
+ Xkb2Qt<XKB_KEY_XF86MailForward, Qt::Key_MailForward>,
+ Xkb2Qt<XKB_KEY_XF86Pictures, Qt::Key_Pictures>,
+ Xkb2Qt<XKB_KEY_XF86Music, Qt::Key_Music>,
+ Xkb2Qt<XKB_KEY_XF86Battery, Qt::Key_Battery>,
+ Xkb2Qt<XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth>,
+ Xkb2Qt<XKB_KEY_XF86WLAN, Qt::Key_WLAN>,
+ Xkb2Qt<XKB_KEY_XF86UWB, Qt::Key_UWB>,
+ Xkb2Qt<XKB_KEY_XF86AudioForward, Qt::Key_AudioForward>,
+ Xkb2Qt<XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat>,
+ Xkb2Qt<XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay>,
+ Xkb2Qt<XKB_KEY_XF86Subtitle, Qt::Key_Subtitle>,
+ Xkb2Qt<XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack>,
+ Xkb2Qt<XKB_KEY_XF86Time, Qt::Key_Time>,
+ Xkb2Qt<XKB_KEY_XF86Select, Qt::Key_Select>,
+ Xkb2Qt<XKB_KEY_XF86View, Qt::Key_View>,
+ Xkb2Qt<XKB_KEY_XF86TopMenu, Qt::Key_TopMenu>,
+ Xkb2Qt<XKB_KEY_XF86Red, Qt::Key_Red>,
+ Xkb2Qt<XKB_KEY_XF86Green, Qt::Key_Green>,
+ Xkb2Qt<XKB_KEY_XF86Yellow, Qt::Key_Yellow>,
+ Xkb2Qt<XKB_KEY_XF86Blue, Qt::Key_Blue>,
+ Xkb2Qt<XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth>,
+ Xkb2Qt<XKB_KEY_XF86Suspend, Qt::Key_Suspend>,
+ Xkb2Qt<XKB_KEY_XF86Hibernate, Qt::Key_Hibernate>,
+ Xkb2Qt<XKB_KEY_XF86TouchpadToggle, Qt::Key_TouchpadToggle>,
+ Xkb2Qt<XKB_KEY_XF86TouchpadOn, Qt::Key_TouchpadOn>,
+ Xkb2Qt<XKB_KEY_XF86TouchpadOff, Qt::Key_TouchpadOff>,
+ Xkb2Qt<XKB_KEY_XF86AudioMicMute, Qt::Key_MicMute>,
+ Xkb2Qt<XKB_KEY_XF86Launch0, Qt::Key_Launch2>, // ### Qt 6: remap properly
+ Xkb2Qt<XKB_KEY_XF86Launch1, Qt::Key_Launch3>,
+ Xkb2Qt<XKB_KEY_XF86Launch2, Qt::Key_Launch4>,
+ Xkb2Qt<XKB_KEY_XF86Launch3, Qt::Key_Launch5>,
+ Xkb2Qt<XKB_KEY_XF86Launch4, Qt::Key_Launch6>,
+ Xkb2Qt<XKB_KEY_XF86Launch5, Qt::Key_Launch7>,
+ Xkb2Qt<XKB_KEY_XF86Launch6, Qt::Key_Launch8>,
+ Xkb2Qt<XKB_KEY_XF86Launch7, Qt::Key_Launch9>,
+ Xkb2Qt<XKB_KEY_XF86Launch8, Qt::Key_LaunchA>,
+ Xkb2Qt<XKB_KEY_XF86Launch9, Qt::Key_LaunchB>,
+ Xkb2Qt<XKB_KEY_XF86LaunchA, Qt::Key_LaunchC>,
+ Xkb2Qt<XKB_KEY_XF86LaunchB, Qt::Key_LaunchD>,
+ Xkb2Qt<XKB_KEY_XF86LaunchC, Qt::Key_LaunchE>,
+ Xkb2Qt<XKB_KEY_XF86LaunchD, Qt::Key_LaunchF>,
+ Xkb2Qt<XKB_KEY_XF86LaunchE, Qt::Key_LaunchG>,
+ Xkb2Qt<XKB_KEY_XF86LaunchF, Qt::Key_LaunchH>
+ >::Data{}
+);
+
// Possible modifier states.
static const Qt::KeyboardModifiers ModsTbl[] = {
Qt::NoModifier, // 0
@@ -831,20 +853,20 @@ void QXcbKeyboard::updateXKBStateFromCore(quint16 state)
}
}
-#if QT_CONFIG(xinput2)
+#if QT_CONFIG(xcb_xinput)
void QXcbKeyboard::updateXKBStateFromXI(void *modInfo, void *groupInfo)
{
if (m_config && !connection()->hasXKB()) {
- xXIModifierInfo *mods = static_cast<xXIModifierInfo *>(modInfo);
- xXIGroupInfo *group = static_cast<xXIGroupInfo *>(groupInfo);
+ auto *mods = static_cast<xcb_input_modifier_info_t *>(modInfo);
+ auto *group = static_cast<xcb_input_group_info_t *>(groupInfo);
const xkb_state_component changedComponents
= xkb_state_update_mask(m_xkbState.get(),
- mods->base_mods,
- mods->latched_mods,
- mods->locked_mods,
- group->base_group,
- group->latched_group,
- group->locked_group);
+ mods->base,
+ mods->latched,
+ mods->locked,
+ group->base,
+ group->latched,
+ group->locked);
handleStateChanges(changedComponents);
}
@@ -1105,14 +1127,10 @@ int QXcbKeyboard::keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers modif
qtKey = xkbcommon_xkb_keysym_to_upper(keysym);
} else {
// check if we have a direct mapping
- int i = 0;
- while (KeyTbl[i]) {
- if (keysym == KeyTbl[i]) {
- qtKey = KeyTbl[i + 1];
- break;
- }
- i += 2;
- }
+ xkb2qt_t searchKey{keysym, 0};
+ auto it = std::lower_bound(KeyTbl.cbegin(), KeyTbl.cend(), searchKey);
+ if (it != KeyTbl.end() && !(searchKey < *it))
+ qtKey = it->qt;
}
QString text;
@@ -1172,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 {
@@ -1192,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)
@@ -1461,64 +1516,6 @@ void QXcbKeyboard::resolveMaskConflicts()
}
}
-class KeyChecker
-{
-public:
- KeyChecker(xcb_window_t window, xcb_keycode_t code, xcb_timestamp_t time, quint16 state)
- : m_window(window)
- , m_code(code)
- , m_time(time)
- , m_state(state)
- , m_error(false)
- , m_release(true)
- {
- }
-
- bool checkEvent(xcb_generic_event_t *ev)
- {
- if (m_error || !ev)
- return false;
-
- int type = ev->response_type & ~0x80;
- if (type != XCB_KEY_PRESS && type != XCB_KEY_RELEASE)
- return false;
-
- xcb_key_press_event_t *event = (xcb_key_press_event_t *)ev;
-
- if (event->event != m_window || event->detail != m_code || event->state != m_state) {
- m_error = true;
- return false;
- }
-
- if (type == XCB_KEY_PRESS) {
- m_error = !m_release || event->time - m_time > 10;
- return !m_error;
- }
-
- if (m_release) {
- m_error = true;
- return false;
- }
-
- m_release = true;
- m_time = event->time;
-
- return false;
- }
-
- bool release() const { return m_release; }
- xcb_timestamp_t time() const { return m_time; }
-
-private:
- xcb_window_t m_window;
- xcb_keycode_t m_code;
- xcb_timestamp_t m_time;
- quint16 m_state;
-
- bool m_error;
- bool m_release;
-};
-
void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, xcb_keycode_t code,
quint16 state, xcb_timestamp_t time, bool fromSendEvent)
{
@@ -1532,7 +1529,6 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
if (type == QEvent::KeyPress)
targetWindow->updateNetWmUserTime(time);
-
ScopedXKBState sendEventState;
if (fromSendEvent) {
// Have a temporary keyboard state filled in from state
@@ -1550,7 +1546,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
struct xkb_state *xkbState = fromSendEvent ? sendEventState.get() : m_xkbState.get();
xcb_keysym_t sym = xkb_state_key_get_one_sym(xkbState, code);
- QString string = lookupString(xkbState, code);
+ QString text = lookupString(xkbState, code);
Qt::KeyboardModifiers modifiers = translateModifiers(state);
if (sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9)
@@ -1575,55 +1571,43 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
int qtcode = keysymToQtKey(latinKeysym != XKB_KEY_NoSymbol ? latinKeysym : sym,
modifiers, xkbState, code);
- bool isAutoRepeat = false;
if (type == QEvent::KeyPress) {
- if (m_autorepeat_code == code) {
- isAutoRepeat = true;
- m_autorepeat_code = 0;
- }
+ if (m_isAutoRepeat && m_autoRepeatCode != code)
+ // Some other key was pressed while we are auto-repeating on a different key.
+ m_isAutoRepeat = false;
} else {
- // look ahead for auto-repeat
- KeyChecker checker(source->xcb_window(), code, time, state);
- xcb_generic_event_t *event = connection()->checkEvent(checker);
- if (event) {
- isAutoRepeat = true;
- free(event);
- }
- m_autorepeat_code = isAutoRepeat ? code : 0;
+ m_isAutoRepeat = false;
+ // Look at the next event in the queue to see if we are auto-repeating.
+ connection()->eventQueue()->peek(QXcbEventQueue::PeekRetainMatch,
+ [this, time, code](xcb_generic_event_t *event, int type) {
+ if (type == XCB_KEY_PRESS) {
+ auto keyPress = reinterpret_cast<xcb_key_press_event_t *>(event);
+ m_isAutoRepeat = keyPress->time == time && keyPress->detail == code;
+ if (m_isAutoRepeat)
+ m_autoRepeatCode = code;
+ }
+ return true;
+ });
}
bool filtered = false;
- QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
- if (inputContext) {
- QKeyEvent event(type, qtcode, modifiers, code, sym, state, string, isAutoRepeat, string.length());
+ if (auto inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext()) {
+ QKeyEvent event(type, qtcode, modifiers, code, sym, state, text, m_isAutoRepeat, text.size());
event.setTimestamp(time);
filtered = inputContext->filterEvent(&event);
}
- QWindow *window = targetWindow->window();
if (!filtered) {
+ QWindow *window = targetWindow->window();
#ifndef QT_NO_CONTEXTMENU
if (type == QEvent::KeyPress && qtcode == Qt::Key_Menu) {
const QPoint globalPos = window->screen()->handle()->cursor()->pos();
const QPoint pos = window->mapFromGlobal(globalPos);
QWindowSystemInterface::handleContextMenuEvent(window, false, pos, globalPos, modifiers);
}
-#endif // QT_NO_CONTEXTMENU
+#endif
QWindowSystemInterface::handleExtendedKeyEvent(window, time, type, qtcode, modifiers,
- code, sym, state, string, isAutoRepeat);
- }
-
- if (isAutoRepeat && type == QEvent::KeyRelease) {
- // since we removed it from the event queue using checkEvent we need to send the key press here
- filtered = false;
- if (inputContext) {
- QKeyEvent event(QEvent::KeyPress, qtcode, modifiers, code, sym, state, string, isAutoRepeat, string.length());
- event.setTimestamp(time);
- filtered = inputContext->filterEvent(&event);
- }
- if (!filtered)
- QWindowSystemInterface::handleExtendedKeyEvent(window, time, QEvent::KeyPress, qtcode, modifiers,
- code, sym, state, string, isAutoRepeat);
+ code, sym, state, text, m_isAutoRepeat);
}
}
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h
index c131d69267..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);
@@ -74,7 +81,7 @@ public:
void updateXKBMods();
xkb_mod_mask_t xkbModMask(quint16 state);
void updateXKBStateFromCore(quint16 state);
-#if QT_CONFIG(xinput2)
+#if QT_CONFIG(xcb_xinput)
void updateXKBStateFromXI(void *modInfo, void *groupInfo);
#endif
#if QT_CONFIG(xkb)
@@ -109,7 +116,8 @@ protected:
private:
bool m_config = false;
- xcb_keycode_t m_autorepeat_code = 0;
+ bool m_isAutoRepeat = false;
+ xcb_keycode_t m_autoRepeatCode = 0;
struct _mod_masks {
uint alt;
diff --git a/src/plugins/platforms/xcb/qxcbmain.cpp b/src/plugins/platforms/xcb/qxcbmain.cpp
index f8cb9a9269..c1e37f3704 100644
--- a/src/plugins/platforms/xcb/qxcbmain.cpp
+++ b/src/plugins/platforms/xcb/qxcbmain.cpp
@@ -52,10 +52,16 @@ public:
QPlatformIntegration* QXcbIntegrationPlugin::create(const QString& system, const QStringList& parameters, int &argc, char **argv)
{
- if (!system.compare(QLatin1String("xcb"), Qt::CaseInsensitive))
- return new QXcbIntegration(parameters, argc, argv);
+ if (!system.compare(QLatin1String("xcb"), Qt::CaseInsensitive)) {
+ auto xcbIntegration = new QXcbIntegration(parameters, argc, argv);
+ if (!xcbIntegration->hasDefaultConnection()) {
+ delete xcbIntegration;
+ return nullptr;
+ }
+ return xcbIntegration;
+ }
- return 0;
+ return nullptr;
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
index db44e58cbb..524af5a2a7 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
@@ -54,7 +54,6 @@
#include <QtGui/qscreen.h>
#include <QtPlatformHeaders/qxcbwindowfunctions.h>
-#include <QtPlatformHeaders/qxcbintegrationfunctions.h>
#include <QtPlatformHeaders/qxcbscreenfunctions.h>
#include <stdio.h>
@@ -93,8 +92,7 @@ static int resourceType(const QByteArray &key)
return int(result - names);
}
-QXcbNativeInterface::QXcbNativeInterface() :
- m_genericEventFilterType(QByteArrayLiteral("xcb_generic_event_t"))
+QXcbNativeInterface::QXcbNativeInterface()
{
}
@@ -106,50 +104,6 @@ static inline QXcbSystemTrayTracker *systemTrayTracker(const QScreen *s)
return static_cast<const QXcbScreen *>(s->handle())->connection()->systemTrayTracker();
}
-bool QXcbNativeInterface::systemTrayAvailable(const QScreen *screen) const
-{
- return systemTrayTracker(screen);
-}
-
-bool QXcbNativeInterface::requestSystemTrayWindowDock(const QWindow *window)
-{
- return QXcbWindow::requestSystemTrayWindowDockStatic(window);
-}
-
-QRect QXcbNativeInterface::systemTrayWindowGlobalGeometry(const QWindow *window)
-{
- return QXcbWindow::systemTrayWindowGlobalGeometryStatic(window);
-}
-
-xcb_window_t QXcbNativeInterface::locateSystemTray(xcb_connection_t *conn, const QXcbScreen *screen)
-{
- if (m_sysTraySelectionAtom == XCB_ATOM_NONE) {
- const QByteArray net_sys_tray = QString::fromLatin1("_NET_SYSTEM_TRAY_S%1").arg(screen->screenNumber()).toLatin1();
- auto intern_r = Q_XCB_REPLY_UNCHECKED(xcb_intern_atom, conn,
- true, net_sys_tray.length(), net_sys_tray);
- if (!intern_r)
- return XCB_WINDOW_NONE;
-
- m_sysTraySelectionAtom = intern_r->atom;
- }
-
- auto sel_owner_r = Q_XCB_REPLY_UNCHECKED(xcb_get_selection_owner, conn, m_sysTraySelectionAtom);
- if (!sel_owner_r)
- return XCB_WINDOW_NONE;
-
- return sel_owner_r->owner;
-}
-
-bool QXcbNativeInterface::systrayVisualHasAlphaChannel()
-{
- return QXcbConnection::xEmbedSystemTrayVisualHasAlphaChannel();
-}
-
-void QXcbNativeInterface::setParentRelativeBackPixmap(QWindow *window)
-{
- QXcbWindow::setParentRelativeBackPixmapStatic(window);
-}
-
void *QXcbNativeInterface::nativeResourceForIntegration(const QByteArray &resourceString)
{
QByteArray lowerCaseResource = resourceString.toLower();
@@ -371,18 +325,6 @@ QFunctionPointer QXcbNativeInterface::platformFunction(const QByteArray &functio
if (function == QXcbWindowFunctions::setWmWindowIconTextIdentifier())
return QFunctionPointer(QXcbWindowFunctions::SetWmWindowIconText(QXcbWindow::setWindowIconTextStatic));
- if (function == QXcbWindowFunctions::setParentRelativeBackPixmapIdentifier())
- return QFunctionPointer(QXcbWindowFunctions::SetParentRelativeBackPixmap(QXcbWindow::setParentRelativeBackPixmapStatic));
-
- if (function == QXcbWindowFunctions::requestSystemTrayWindowDockIdentifier())
- return QFunctionPointer(QXcbWindowFunctions::RequestSystemTrayWindowDock(QXcbWindow::requestSystemTrayWindowDockStatic));
-
- if (function == QXcbWindowFunctions::systemTrayWindowGlobalGeometryIdentifier())
- return QFunctionPointer(QXcbWindowFunctions::SystemTrayWindowGlobalGeometry(QXcbWindow::systemTrayWindowGlobalGeometryStatic));
-
- if (function == QXcbIntegrationFunctions::xEmbedSystemTrayVisualHasAlphaChannelIdentifier())
- return QFunctionPointer(QXcbIntegrationFunctions::XEmbedSystemTrayVisualHasAlphaChannel(QXcbConnection::xEmbedSystemTrayVisualHasAlphaChannel));
-
if (function == QXcbWindowFunctions::visualIdIdentifier()) {
return QFunctionPointer(QXcbWindowFunctions::VisualId(QXcbWindow::visualIdStatic));
}
@@ -466,7 +408,7 @@ void *QXcbNativeInterface::atspiBus()
QXcbIntegration *integration = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration());
QXcbConnection *defaultConnection = integration->defaultConnection();
if (defaultConnection) {
- xcb_atom_t atspiBusAtom = defaultConnection->internAtom("AT_SPI_BUS");
+ auto atspiBusAtom = defaultConnection->atom(QXcbAtom::AT_SPI_BUS);
auto reply = Q_XCB_REPLY(xcb_get_property, defaultConnection->xcb_connection(),
false, defaultConnection->rootWindow(),
atspiBusAtom, XCB_ATOM_STRING, 0, 128);
@@ -495,20 +437,20 @@ void QXcbNativeInterface::setAppUserTime(QScreen* screen, xcb_timestamp_t time)
qint32 QXcbNativeInterface::generatePeekerId()
{
QXcbIntegration *integration = QXcbIntegration::instance();
- return integration->defaultConnection()->generatePeekerId();
+ return integration->defaultConnection()->eventQueue()->generatePeekerId();
}
bool QXcbNativeInterface::removePeekerId(qint32 peekerId)
{
QXcbIntegration *integration = QXcbIntegration::instance();
- return integration->defaultConnection()->removePeekerId(peekerId);
+ return integration->defaultConnection()->eventQueue()->removePeekerId(peekerId);
}
-bool QXcbNativeInterface::peekEventQueue(QXcbConnection::PeekerCallback peeker, void *peekerData,
- QXcbConnection::PeekOptions option, qint32 peekerId)
+bool QXcbNativeInterface::peekEventQueue(QXcbEventQueue::PeekerCallback peeker, void *peekerData,
+ QXcbEventQueue::PeekOptions option, qint32 peekerId)
{
QXcbIntegration *integration = QXcbIntegration::instance();
- return integration->defaultConnection()->peekEventQueue(peeker, peekerData, option, peekerId);
+ return integration->defaultConnection()->eventQueue()->peekEventQueue(peeker, peekerData, option, peekerId);
}
void QXcbNativeInterface::setStartupId(const char *data)
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h
index 6a752c68ca..4656f46be5 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.h
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h
@@ -98,7 +98,7 @@ public:
QFunctionPointer platformFunction(const QByteArray &function) const override;
- inline const QByteArray &genericEventFilterType() const { return m_genericEventFilterType; }
+ inline const QByteArray &nativeEventType() const { return m_nativeEventType; }
void *displayForWindow(QWindow *window);
void *connectionForWindow(QWindow *window);
@@ -118,15 +118,10 @@ public:
static qint32 generatePeekerId();
static bool removePeekerId(qint32 peekerId);
- static bool peekEventQueue(QXcbConnection::PeekerCallback peeker, void *peekerData = nullptr,
- QXcbConnection::PeekOptions option = QXcbConnection::PeekDefault,
+ static bool peekEventQueue(QXcbEventQueue::PeekerCallback peeker, void *peekerData = nullptr,
+ QXcbEventQueue::PeekOptions option = QXcbEventQueue::PeekDefault,
qint32 peekerId = -1);
- Q_INVOKABLE bool systemTrayAvailable(const QScreen *screen) const;
- Q_INVOKABLE void setParentRelativeBackPixmap(QWindow *window);
- Q_INVOKABLE bool systrayVisualHasAlphaChannel();
- Q_INVOKABLE bool requestSystemTrayWindowDock(const QWindow *window);
- Q_INVOKABLE QRect systemTrayWindowGlobalGeometry(const QWindow *window);
Q_INVOKABLE QString dumpConnectionNativeWindows(const QXcbConnection *connection, WId root) const;
Q_INVOKABLE QString dumpNativeWindows(WId root = 0) const;
@@ -136,9 +131,7 @@ signals:
void systemTrayWindowChanged(QScreen *screen);
private:
- xcb_window_t locateSystemTray(xcb_connection_t *conn, const QXcbScreen *screen);
-
- const QByteArray m_genericEventFilterType;
+ const QByteArray m_nativeEventType = QByteArrayLiteral("xcb_generic_event_t");
xcb_atom_t m_sysTraySelectionAtom = XCB_ATOM_NONE;
diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index a696e2a311..57dbdc9bec 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -95,12 +95,6 @@ QXcbVirtualDesktop::QXcbVirtualDesktop(QXcbConnection *connection, xcb_screen_t
m_windowManagerName = QXcbWindow::windowTitle(connection, windowManager);
}
- const xcb_query_extension_reply_t *sync_reply = xcb_get_extension_data(xcb_connection(), &xcb_sync_id);
- if (!sync_reply || !sync_reply->present)
- m_syncRequestSupported = false;
- else
- m_syncRequestSupported = true;
-
xcb_depth_iterator_t depth_iterator =
xcb_screen_allowed_depths_iterator(screen);
@@ -746,7 +740,11 @@ void QXcbScreen::updateGeometry(const QRect &geometry, uint8_t rotation)
m_sizeMillimeters = sizeInMillimeters(geometry.size(), m_virtualDesktop->dpi());
qreal dpi = geometry.width() / physicalSize().width() * qreal(25.4);
- m_pixelDensity = qMax(1, qRound(dpi/96));
+
+ // Use 128 as a reference DPI on small screens. This favors "small UI" over "large UI".
+ qreal referenceDpi = physicalSize().width() <= 320 ? 128 : 96;
+
+ m_pixelDensity = qMax(1, qRound(dpi/referenceDpi));
m_geometry = geometry;
m_availableGeometry = geometry & m_virtualDesktop->workArea();
QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), m_geometry, m_availableGeometry);
@@ -905,16 +903,12 @@ QByteArray QXcbScreen::getEdid() const
return result;
// Try a bunch of atoms
- xcb_atom_t atom = connection()->internAtom("EDID");
- result = getOutputProperty(atom);
- if (result.isEmpty()) {
- atom = connection()->internAtom("EDID_DATA");
- result = getOutputProperty(atom);
- }
- if (result.isEmpty()) {
- atom = connection()->internAtom("XFree86_DDC_EDID1_RAWDATA");
- result = getOutputProperty(atom);
- }
+ result = getOutputProperty(atom(QXcbAtom::EDID));
+ if (result.isEmpty())
+ result = getOutputProperty(atom(QXcbAtom::EDID_DATA));
+ if (result.isEmpty())
+ result = getOutputProperty(atom(QXcbAtom::XFree86_DDC_EDID1_RAWDATA));
+
return result;
}
diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h
index 792aca4b06..7f22a8e4db 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.h
+++ b/src/plugins/platforms/xcb/qxcbscreen.h
@@ -102,7 +102,6 @@ public:
int antialiasingEnabled() const { return m_antialiasingEnabled; }
QString windowManagerName() const { return m_windowManagerName; }
- bool syncRequestSupported() const { return m_syncRequestSupported; }
QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &format) const;
@@ -133,10 +132,9 @@ private:
QFontEngine::SubpixelAntialiasingType m_subpixelType = QFontEngine::SubpixelAntialiasingType(-1);
int m_antialiasingEnabled = -1;
QString m_windowManagerName;
- bool m_syncRequestSupported = false;
QMap<xcb_visualid_t, xcb_visualtype_t> m_visuals;
QMap<xcb_visualid_t, quint8> m_visualDepths;
- uint16_t m_rotation = XCB_RANDR_ROTATION_ROTATE_0;
+ uint16_t m_rotation = 0;
};
class Q_XCB_EXPORT QXcbScreen : public QXcbObject, public QPlatformScreen
@@ -188,7 +186,6 @@ public:
void windowShown(QXcbWindow *window);
QString windowManagerName() const { return m_virtualDesktop->windowManagerName(); }
- bool syncRequestSupported() const { return m_virtualDesktop->syncRequestSupported(); }
QSurfaceFormat surfaceFormatFor(const QSurfaceFormat &format) const;
@@ -220,7 +217,6 @@ private:
xcb_randr_crtc_t m_crtc;
xcb_randr_mode_t m_mode = XCB_NONE;
bool m_primary = false;
- uint8_t m_rotation = 0;
QString m_outputName;
QSizeF m_outputSizeMillimeters;
diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
index c98879c7df..684e603fab 100644
--- a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
+++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp
@@ -91,8 +91,7 @@ xcb_window_t QXcbSystemTrayTracker::locateTrayWindow(const QXcbConnection *conne
return reply->owner;
}
-// API for QPlatformNativeInterface/QPlatformSystemTrayIcon: Request a window
-// to be docked on the tray.
+// Request a window to be docked on the tray.
void QXcbSystemTrayTracker::requestSystemTrayWindowDock(xcb_window_t window) const
{
xcb_client_message_event_t trayRequest;
@@ -122,23 +121,6 @@ xcb_window_t QXcbSystemTrayTracker::trayWindow()
return m_trayWindow;
}
-// API for QPlatformNativeInterface/QPlatformSystemTrayIcon: Return the geometry of a
-// a window parented on the tray. Determines the global geometry via XCB since mapToGlobal
-// does not work for the QWindow parented on the tray.
-QRect QXcbSystemTrayTracker::systemTrayWindowGlobalGeometry(xcb_window_t window) const
-{
- xcb_connection_t *conn = m_connection->xcb_connection();
- auto geomReply = Q_XCB_REPLY(xcb_get_geometry, conn, window);
- if (!geomReply)
- return QRect();
-
- auto translateReply = Q_XCB_REPLY(xcb_translate_coordinates, conn, window, m_connection->rootWindow(), 0, 0);
- if (!translateReply)
- return QRect();
-
- return QRect(QPoint(translateReply->dst_x, translateReply->dst_y), QSize(geomReply->width, geomReply->height));
-}
-
inline void QXcbSystemTrayTracker::emitSystemTrayWindowChanged()
{
if (const QPlatformScreen *ps = m_connection->primaryScreen())
@@ -162,10 +144,18 @@ void QXcbSystemTrayTracker::handleDestroyNotifyEvent(const xcb_destroy_notify_ev
}
}
-bool QXcbSystemTrayTracker::visualHasAlphaChannel()
+xcb_visualid_t QXcbSystemTrayTracker::visualId()
+{
+ xcb_visualid_t visual = netSystemTrayVisual();
+ if (visual == XCB_NONE)
+ visual = m_connection->primaryScreen()->screen()->root_visual;
+ return visual;
+}
+
+xcb_visualid_t QXcbSystemTrayTracker::netSystemTrayVisual()
{
if (m_trayWindow == XCB_WINDOW_NONE)
- return false;
+ return XCB_NONE;
xcb_atom_t tray_atom = m_connection->atom(QXcbAtom::_NET_SYSTEM_TRAY_VISUAL);
@@ -174,7 +164,7 @@ bool QXcbSystemTrayTracker::visualHasAlphaChannel()
false, m_trayWindow,
tray_atom, XCB_ATOM_VISUALID, 0, 1);
if (!systray_atom_reply)
- return false;
+ return XCB_NONE;
xcb_visualid_t systrayVisualId = XCB_NONE;
if (systray_atom_reply->value_len > 0 && xcb_get_property_value_length(systray_atom_reply.get()) > 0) {
@@ -182,12 +172,7 @@ bool QXcbSystemTrayTracker::visualHasAlphaChannel()
systrayVisualId = vids[0];
}
- if (systrayVisualId != XCB_NONE) {
- quint8 depth = m_connection->primaryScreen()->depthOfVisual(systrayVisualId);
- return depth == 32;
- }
-
- return false;
+ return systrayVisualId;
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.h b/src/plugins/platforms/xcb/qxcbsystemtraytracker.h
index a95b9374e9..d2fc24c957 100644
--- a/src/plugins/platforms/xcb/qxcbsystemtraytracker.h
+++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.h
@@ -57,13 +57,12 @@ public:
xcb_window_t trayWindow();
void requestSystemTrayWindowDock(xcb_window_t window) const;
- QRect systemTrayWindowGlobalGeometry(xcb_window_t window) const;
void notifyManagerClientMessageEvent(const xcb_client_message_event_t *);
void handleDestroyNotifyEvent(const xcb_destroy_notify_event_t *) override;
- bool visualHasAlphaChannel();
+ xcb_visualid_t visualId();
signals:
void systemTrayWindowChanged(QScreen *screen);
@@ -73,6 +72,7 @@ private:
xcb_atom_t selection);
static xcb_window_t locateTrayWindow(const QXcbConnection *connection, xcb_atom_t selection);
void emitSystemTrayWindowChanged();
+ xcb_visualid_t netSystemTrayVisual();
const xcb_atom_t m_selection;
const xcb_atom_t m_trayAtom;
diff --git a/src/plugins/platforms/xcb/qxcbvulkaninstance.cpp b/src/plugins/platforms/xcb/qxcbvulkaninstance.cpp
index 4d540defa9..b3f8a5832d 100644
--- a/src/plugins/platforms/xcb/qxcbvulkaninstance.cpp
+++ b/src/plugins/platforms/xcb/qxcbvulkaninstance.cpp
@@ -46,20 +46,9 @@ QT_BEGIN_NAMESPACE
QXcbVulkanInstance::QXcbVulkanInstance(QVulkanInstance *instance)
: m_instance(instance),
m_getPhysDevPresSupport(nullptr),
- m_createSurface(nullptr),
- m_destroySurface(nullptr)
+ m_createSurface(nullptr)
{
- if (qEnvironmentVariableIsSet("QT_VULKAN_LIB"))
- m_lib.setFileName(QString::fromUtf8(qgetenv("QT_VULKAN_LIB")));
- else
- m_lib.setFileName(QStringLiteral("vulkan"));
-
- if (!m_lib.load()) {
- qWarning("Failed to load %s: %s", qPrintable(m_lib.fileName()), qPrintable(m_lib.errorString()));
- return;
- }
-
- init(&m_lib);
+ loadVulkanLibrary(QStringLiteral("vulkan"));
}
QXcbVulkanInstance::~QXcbVulkanInstance()
@@ -114,14 +103,6 @@ VkSurfaceKHR QXcbVulkanInstance::createSurface(QXcbWindow *window)
qWarning("Failed to find vkCreateXcbSurfaceKHR");
return surface;
}
- if (!m_destroySurface) {
- m_destroySurface = reinterpret_cast<PFN_vkDestroySurfaceKHR>(
- m_vkGetInstanceProcAddr(m_vkInst, "vkDestroySurfaceKHR"));
- }
- if (!m_destroySurface) {
- qWarning("Failed to find vkDestroySurfaceKHR");
- return surface;
- }
VkXcbSurfaceCreateInfoKHR surfaceInfo;
memset(&surfaceInfo, 0, sizeof(surfaceInfo));
@@ -135,12 +116,6 @@ VkSurfaceKHR QXcbVulkanInstance::createSurface(QXcbWindow *window)
return surface;
}
-void QXcbVulkanInstance::destroySurface(VkSurfaceKHR surface)
-{
- if (m_destroySurface && surface)
- m_destroySurface(m_vkInst, surface, nullptr);
-}
-
void QXcbVulkanInstance::presentQueued(QWindow *window)
{
QXcbWindow *w = static_cast<QXcbWindow *>(window->handle());
diff --git a/src/plugins/platforms/xcb/qxcbvulkaninstance.h b/src/plugins/platforms/xcb/qxcbvulkaninstance.h
index dbe057d944..53f7345254 100644
--- a/src/plugins/platforms/xcb/qxcbvulkaninstance.h
+++ b/src/plugins/platforms/xcb/qxcbvulkaninstance.h
@@ -64,14 +64,11 @@ public:
void presentQueued(QWindow *window) override;
VkSurfaceKHR createSurface(QXcbWindow *window);
- void destroySurface(VkSurfaceKHR surface);
private:
QVulkanInstance *m_instance;
- QLibrary m_lib;
PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR m_getPhysDevPresSupport;
PFN_vkCreateXcbSurfaceKHR m_createSurface;
- PFN_vkDestroySurfaceKHR m_destroySurface;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index 59b06d543e..70aab77a51 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -70,6 +70,9 @@
#undef class
#include <xcb/xfixes.h>
#include <xcb/shape.h>
+#if QT_CONFIG(xcb_xinput)
+#include <xcb/xinput.h>
+#endif
// xcb-icccm 3.8 support
#ifdef XCB_ICCCM_NUM_WM_SIZE_HINTS_ELEMENTS
@@ -109,19 +112,12 @@
#undef register
#endif
-#if QT_CONFIG(xinput2)
-#include <X11/extensions/XInput2.h>
-#include <X11/extensions/XI2proto.h>
-#endif
-
#define XCOORD_MAX 16383
enum {
defaultWindowWidth = 160,
defaultWindowHeight = 160
};
-//#ifdef NET_WM_STATE_DEBUG
-
QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(xcb_rectangle_t, Q_PRIMITIVE_TYPE);
@@ -200,11 +196,6 @@ void QXcbWindow::setImageFormatForVisual(const xcb_visualtype_t *visual)
}
}
-static inline bool positionIncludesFrame(QWindow *w)
-{
- return qt_window_private(w)->positionPolicy == QWindowPrivate::WindowFrameInclusive;
-}
-
#if QT_CONFIG(xcb_xlib)
static inline XTextProperty* qstringToXTP(Display *dpy, const QString& s)
{
@@ -282,11 +273,7 @@ QXcbWindow::QXcbWindow(QWindow *window)
setConnection(xcbScreen()->connection());
}
-#ifdef Q_COMPILER_CLASS_ENUM
enum : quint32 {
-#else
-enum {
-#endif
baseEventMask
= XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY
| XCB_EVENT_MASK_PROPERTY_CHANGE | XCB_EVENT_MASK_FOCUS_CHANGE,
@@ -308,6 +295,7 @@ void QXcbWindow::create()
destroy();
m_windowState = Qt::WindowNoState;
+ m_trayIconWindow = isTrayIconWindow(window());
Qt::WindowType type = window()->type();
@@ -334,9 +322,6 @@ void QXcbWindow::create()
return;
}
- // Parameters to XCreateWindow() are frame corner + inner size.
- // This fits in case position policy is frame inclusive. There is
- // currently no way to implement it for frame-exclusive geometries.
QPlatformWindow::setGeometry(rect);
if (platformScreen != currentScreen)
@@ -368,7 +353,9 @@ void QXcbWindow::create()
const xcb_visualtype_t *visual = nullptr;
- if (connection()->hasDefaultVisualId()) {
+ if (m_trayIconWindow && connection()->systemTrayTracker()) {
+ visual = platformScreen->visualForId(connection()->systemTrayTracker()->visualId());
+ } else if (connection()->hasDefaultVisualId()) {
visual = platformScreen->visualForId(connection()->defaultVisualId());
if (!visual)
qWarning() << "Failed to use requested visual id.";
@@ -408,9 +395,12 @@ void QXcbWindow::create()
| XCB_CW_SAVE_UNDER
| XCB_CW_EVENT_MASK;
- static const bool haveOpenGL = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL);
+ static auto haveOpenGL = []() {
+ static const bool result = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL);
+ return result;
+ };
- if ((window()->supportsOpenGL() && haveOpenGL) || m_format.hasAlpha()) {
+ if ((window()->supportsOpenGL() && haveOpenGL()) || m_format.hasAlpha()) {
m_cmap = xcb_generate_id(xcb_connection());
xcb_create_colormap(xcb_connection(),
XCB_COLORMAP_ALLOC_NONE,
@@ -448,8 +438,6 @@ void QXcbWindow::create()
connection()->addWindowEventListener(m_window, this);
- xcb_change_window_attributes(xcb_connection(), m_window, mask, values);
-
propagateSizeHints();
xcb_atom_t properties[5];
@@ -458,9 +446,7 @@ void QXcbWindow::create()
properties[propertyCount++] = atom(QXcbAtom::WM_TAKE_FOCUS);
properties[propertyCount++] = atom(QXcbAtom::_NET_WM_PING);
- m_usingSyncProtocol = platformScreen->syncRequestSupported();
-
- if (m_usingSyncProtocol)
+ if (connection()->hasXSync())
properties[propertyCount++] = atom(QXcbAtom::_NET_WM_SYNC_REQUEST);
if (window()->flags() & Qt::WindowContextHelpButtonHint)
@@ -484,7 +470,7 @@ void QXcbWindow::create()
XCB_ATOM_STRING, 8, wmClass.size(), wmClass.constData());
}
- if (m_usingSyncProtocol) {
+ if (connection()->hasXSync()) {
m_syncCounter = xcb_generate_id(xcb_connection());
xcb_sync_create_counter(xcb_connection(), m_syncCounter, m_syncValue);
@@ -504,12 +490,17 @@ void QXcbWindow::create()
atom(QXcbAtom::_NET_WM_PID), XCB_ATOM_CARDINAL, 32,
1, &pid);
+ const QByteArray clientMachine = QSysInfo::machineHostName().toLocal8Bit();
+ if (!clientMachine.isEmpty()) {
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::WM_CLIENT_MACHINE), XCB_ATOM_STRING, 8,
+ clientMachine.size(), clientMachine.constData());
+ }
+
+ // Create WM_HINTS property on the window, so we can xcb_get_wm_hints*()
+ // from various setter functions for adjusting the hints.
xcb_wm_hints_t hints;
memset(&hints, 0, sizeof(hints));
- xcb_wm_hints_set_normal(&hints);
-
- xcb_wm_hints_set_input(&hints, !(window()->flags() & Qt::WindowDoesNotAcceptFocus));
-
xcb_set_wm_hints(xcb_connection(), m_window, &hints);
xcb_window_t leader = connection()->clientLeader();
@@ -524,7 +515,7 @@ void QXcbWindow::create()
atom(QXcbAtom::_XEMBED_INFO),
32, 2, (void *)data);
-#if QT_CONFIG(xinput2)
+#if QT_CONFIG(xcb_xinput)
if (connection()->hasXInput2()) {
if (connection()->xi2MouseEventsDisabled())
connection()->xi2SelectDeviceEventsCompatibility(m_window);
@@ -537,9 +528,6 @@ void QXcbWindow::create()
setWindowFlags(window()->flags());
setWindowTitle(window()->title());
- if (window()->flags() & Qt::WindowTransparentForInput)
- setTransparentForMouseEvents(true);
-
#if QT_CONFIG(xcb_xlib)
// force sync to read outstanding requests - see QTBUG-29106
XSync(static_cast<Display*>(platformScreen->connection()->xlib_display()), false);
@@ -562,6 +550,9 @@ void QXcbWindow::create()
QByteArray wmWindowRole = window()->property(wm_window_role_property_id).toByteArray();
setWmWindowRole(wmWindowRole);
}
+
+ if (m_trayIconWindow)
+ m_embedded = requestSystemTrayWindowDock();
}
QXcbWindow::~QXcbWindow()
@@ -587,7 +578,7 @@ void QXcbWindow::destroy()
if (connection()->mouseGrabber() == this)
connection()->setMouseGrabber(nullptr);
- if (m_syncCounter && m_usingSyncProtocol)
+ if (m_syncCounter && connection()->hasXSync())
xcb_sync_destroy_counter(xcb_connection(), m_syncCounter);
if (m_window) {
if (m_netWmUserTimeWindow) {
@@ -623,25 +614,23 @@ void QXcbWindow::setGeometry(const QRect &rect)
if (!newScreen)
newScreen = xcbScreen();
- const QRect wmGeometry = windowToWmGeometry(rect);
-
if (newScreen != currentScreen)
QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());
if (qt_window_private(window())->positionAutomatic) {
const quint32 mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
const qint32 values[] = {
- qBound<qint32>(1, wmGeometry.width(), XCOORD_MAX),
- qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX),
+ qBound<qint32>(1, rect.width(), XCOORD_MAX),
+ qBound<qint32>(1, rect.height(), XCOORD_MAX),
};
xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values));
} else {
const quint32 mask = XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y | XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT;
const qint32 values[] = {
- qBound<qint32>(-XCOORD_MAX, wmGeometry.x(), XCOORD_MAX),
- qBound<qint32>(-XCOORD_MAX, wmGeometry.y(), XCOORD_MAX),
- qBound<qint32>(1, wmGeometry.width(), XCOORD_MAX),
- qBound<qint32>(1, wmGeometry.height(), XCOORD_MAX),
+ qBound<qint32>(-XCOORD_MAX, rect.x(), XCOORD_MAX),
+ qBound<qint32>(-XCOORD_MAX, rect.y(), XCOORD_MAX),
+ qBound<qint32>(1, rect.width(), XCOORD_MAX),
+ qBound<qint32>(1, rect.height(), XCOORD_MAX),
};
xcb_configure_window(xcb_connection(), m_window, mask, reinterpret_cast<const quint32*>(values));
if (window()->parent() && !window()->transientParent()) {
@@ -739,34 +728,10 @@ void QXcbWindow::setVisible(bool visible)
hide();
}
-static inline bool testShowWithoutActivating(const QWindow *window)
-{
- // QWidget-attribute Qt::WA_ShowWithoutActivating.
- const QVariant showWithoutActivating = window->property("_q_showWithoutActivating");
- return showWithoutActivating.isValid() && showWithoutActivating.toBool();
-}
-
void QXcbWindow::show()
{
if (window()->isTopLevel()) {
- xcb_get_property_cookie_t cookie = xcb_get_wm_hints_unchecked(xcb_connection(), m_window);
-
- xcb_wm_hints_t hints;
- xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, NULL);
-
- if (window()->windowStates() & Qt::WindowMinimized)
- xcb_wm_hints_set_iconic(&hints);
- else
- xcb_wm_hints_set_normal(&hints);
-
- xcb_wm_hints_set_input(&hints, !(window()->flags() & Qt::WindowDoesNotAcceptFocus));
-
- xcb_set_wm_hints(xcb_connection(), m_window, &hints);
-
- m_gravity = positionIncludesFrame(window()) ?
- XCB_GRAVITY_NORTH_WEST : XCB_GRAVITY_STATIC;
-
// update WM_NORMAL_HINTS
propagateSizeHints();
@@ -789,19 +754,18 @@ void QXcbWindow::show()
if (!transientXcbParent)
xcb_delete_property(xcb_connection(), m_window, XCB_ATOM_WM_TRANSIENT_FOR);
- // update _MOTIF_WM_HINTS
- updateMotifWmHintsBeforeMap();
-
// update _NET_WM_STATE
updateNetWmStateBeforeMap();
}
- if (testShowWithoutActivating(window()))
+ // QWidget-attribute Qt::WA_ShowWithoutActivating.
+ const auto showWithoutActivating = window()->property("_q_showWithoutActivating");
+ if (showWithoutActivating.isValid() && showWithoutActivating.toBool())
updateNetWmUserTime(0);
else if (connection()->time() != XCB_TIME_CURRENT_TIME)
updateNetWmUserTime(connection()->time());
- if (window()->objectName() == QLatin1String("QSystemTrayIconSysWindow"))
+ if (m_trayIconWindow)
return; // defer showing until XEMBED_EMBEDDED_NOTIFY
xcb_map_window(xcb_connection(), m_window);
@@ -819,7 +783,7 @@ void QXcbWindow::hide()
xcb_unmap_window(xcb_connection(), m_window);
// send synthetic UnmapNotify event according to icccm 4.1.4
- Q_DECLARE_XCB_EVENT(event, xcb_unmap_notify_event_t);
+ q_padded_xcb_event<xcb_unmap_notify_event_t> event = {};
event.response_type = XCB_UNMAP_NOTIFY;
event.event = xcbScreen()->root();
event.window = m_window;
@@ -940,8 +904,8 @@ void QXcbWindow::doFocusOut()
struct QtMotifWmHints {
quint32 flags, functions, decorations;
- qint32 input_mode;
- quint32 status;
+ qint32 input_mode; // unused
+ quint32 status; // unused
};
enum {
@@ -963,50 +927,8 @@ enum {
MWM_DECOR_MENU = (1L << 4),
MWM_DECOR_MINIMIZE = (1L << 5),
MWM_DECOR_MAXIMIZE = (1L << 6),
-
- MWM_HINTS_INPUT_MODE = (1L << 2),
-
- MWM_INPUT_MODELESS = 0L,
- MWM_INPUT_PRIMARY_APPLICATION_MODAL = 1L,
- MWM_INPUT_FULL_APPLICATION_MODAL = 3L
};
-static QtMotifWmHints getMotifWmHints(QXcbConnection *c, xcb_window_t window)
-{
- QtMotifWmHints hints;
-
- auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, c->xcb_connection(), 0, window,
- c->atom(QXcbAtom::_MOTIF_WM_HINTS), c->atom(QXcbAtom::_MOTIF_WM_HINTS), 0, 20);
-
- if (reply && reply->format == 32 && reply->type == c->atom(QXcbAtom::_MOTIF_WM_HINTS)) {
- hints = *((QtMotifWmHints *)xcb_get_property_value(reply.get()));
- } else {
- hints.flags = 0L;
- hints.functions = MWM_FUNC_ALL;
- hints.decorations = MWM_DECOR_ALL;
- hints.input_mode = 0L;
- hints.status = 0L;
- }
-
- return hints;
-}
-
-static void setMotifWmHints(QXcbConnection *c, xcb_window_t window, const QtMotifWmHints &hints)
-{
- if (hints.flags != 0l) {
- xcb_change_property(c->xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- window,
- c->atom(QXcbAtom::_MOTIF_WM_HINTS),
- c->atom(QXcbAtom::_MOTIF_WM_HINTS),
- 32,
- 5,
- &hints);
- } else {
- xcb_delete_property(c->xcb_connection(), window, c->atom(QXcbAtom::_MOTIF_WM_HINTS));
- }
-}
-
QXcbWindow::NetWmStates QXcbWindow::netWmStates()
{
NetWmStates result(0);
@@ -1035,9 +957,7 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates()
if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)))
result |= NetWmStateDemandsAttention;
} else {
-#ifdef NET_WM_STATE_DEBUG
- printf("getting net wm state (%x), empty\n", m_window);
-#endif
+ qCDebug(lcQpaXcb, "getting net wm state (%x), empty\n", m_window);
}
return result;
@@ -1110,22 +1030,18 @@ void QXcbWindow::setWindowFlags(Qt::WindowFlags flags)
setWmWindowType(wmWindowTypes, flags);
setNetWmStateWindowFlags(flags);
- setMotifWindowFlags(flags);
+ setMotifWmHints(flags);
setTransparentForMouseEvents(flags & Qt::WindowTransparentForInput);
updateDoesNotAcceptFocus(flags & Qt::WindowDoesNotAcceptFocus);
}
-void QXcbWindow::setMotifWindowFlags(Qt::WindowFlags flags)
+void QXcbWindow::setMotifWmHints(Qt::WindowFlags flags)
{
Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
QtMotifWmHints mwmhints;
- mwmhints.flags = 0L;
- mwmhints.functions = 0L;
- mwmhints.decorations = 0;
- mwmhints.input_mode = 0L;
- mwmhints.status = 0L;
+ memset(&mwmhints, 0, sizeof(mwmhints));
if (type != Qt::SplashScreen) {
mwmhints.flags |= MWM_HINTS_DECORATIONS;
@@ -1183,7 +1099,18 @@ void QXcbWindow::setMotifWindowFlags(Qt::WindowFlags flags)
mwmhints.decorations = 0;
}
- setMotifWmHints(connection(), m_window, mwmhints);
+ if (mwmhints.flags) {
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ atom(QXcbAtom::_MOTIF_WM_HINTS),
+ atom(QXcbAtom::_MOTIF_WM_HINTS),
+ 32,
+ 5,
+ &mwmhints);
+ } else {
+ xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_MOTIF_WM_HINTS));
+ }
}
void QXcbWindow::changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two)
@@ -1242,64 +1169,18 @@ void QXcbWindow::setWindowState(Qt::WindowStates state)
changeNetWmState(state & Qt::WindowFullScreen, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
}
- connection()->sync();
- m_windowState = state;
-}
-
-void QXcbWindow::updateMotifWmHintsBeforeMap()
-{
- QtMotifWmHints mwmhints = getMotifWmHints(connection(), m_window);
-
- if (window()->modality() != Qt::NonModal) {
- switch (window()->modality()) {
- case Qt::WindowModal:
- mwmhints.input_mode = MWM_INPUT_PRIMARY_APPLICATION_MODAL;
- break;
- case Qt::ApplicationModal:
- default:
- mwmhints.input_mode = MWM_INPUT_FULL_APPLICATION_MODAL;
- break;
- }
- mwmhints.flags |= MWM_HINTS_INPUT_MODE;
- } else {
- mwmhints.input_mode = MWM_INPUT_MODELESS;
- mwmhints.flags &= ~MWM_HINTS_INPUT_MODE;
- }
-
- if (windowMinimumSize() == windowMaximumSize()) {
- // fixed size, remove the resize handle (since mwm/dtwm
- // isn't smart enough to do it itself)
- mwmhints.flags |= MWM_HINTS_FUNCTIONS;
- if (mwmhints.functions == MWM_FUNC_ALL) {
- mwmhints.functions = MWM_FUNC_MOVE;
- } else {
- mwmhints.functions &= ~MWM_FUNC_RESIZE;
- }
-
- if (mwmhints.decorations == MWM_DECOR_ALL) {
- mwmhints.flags |= MWM_HINTS_DECORATIONS;
- mwmhints.decorations = (MWM_DECOR_BORDER
- | MWM_DECOR_TITLE
- | MWM_DECOR_MENU);
- } else {
- mwmhints.decorations &= ~MWM_DECOR_RESIZEH;
- }
- }
-
- if (window()->flags() & Qt::WindowMinimizeButtonHint) {
- mwmhints.flags |= MWM_HINTS_DECORATIONS;
- mwmhints.decorations |= MWM_DECOR_MINIMIZE;
- mwmhints.functions |= MWM_FUNC_MINIMIZE;
- }
- if (window()->flags() & Qt::WindowMaximizeButtonHint) {
- mwmhints.flags |= MWM_HINTS_DECORATIONS;
- mwmhints.decorations |= MWM_DECOR_MAXIMIZE;
- mwmhints.functions |= MWM_FUNC_MAXIMIZE;
+ xcb_get_property_cookie_t cookie = xcb_get_wm_hints_unchecked(xcb_connection(), m_window);
+ xcb_wm_hints_t hints;
+ if (xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, nullptr)) {
+ if (state & Qt::WindowMinimized)
+ xcb_wm_hints_set_iconic(&hints);
+ else
+ xcb_wm_hints_set_normal(&hints);
+ xcb_set_wm_hints(xcb_connection(), m_window, &hints);
}
- if (window()->flags() & Qt::WindowCloseButtonHint)
- mwmhints.functions |= MWM_FUNC_CLOSE;
- setMotifWmHints(connection(), m_window, mwmhints);
+ connection()->sync();
+ m_windowState = state;
}
void QXcbWindow::updateNetWmStateBeforeMap()
@@ -1364,17 +1245,10 @@ void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp)
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW),
XCB_ATOM_WINDOW, 32, 1, &m_netWmUserTimeWindow);
xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME));
-#ifndef QT_NO_DEBUG
- QByteArray ba("Qt NET_WM user time window");
- xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_netWmUserTimeWindow,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData());
-#endif
+
+ QXcbWindow::setWindowTitle(connection(), m_netWmUserTimeWindow,
+ QStringLiteral("Qt NET_WM User Time Window"));
+
} else if (!isSupportedByWM) {
// WM no longer supports it, then we should remove the
// _NET_WM_USER_TIME_WINDOW atom.
@@ -1421,9 +1295,8 @@ void QXcbWindow::updateDoesNotAcceptFocus(bool doesNotAcceptFocus)
xcb_get_property_cookie_t cookie = xcb_get_wm_hints_unchecked(xcb_connection(), m_window);
xcb_wm_hints_t hints;
- if (!xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, NULL)) {
+ if (!xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, nullptr))
return;
- }
xcb_wm_hints_set_input(&hints, !doesNotAcceptFocus);
xcb_set_wm_hints(xcb_connection(), m_window, &hints);
@@ -1452,24 +1325,7 @@ void QXcbWindow::setParent(const QPlatformWindow *parent)
void QXcbWindow::setWindowTitle(const QString &title)
{
- QString fullTitle = formatWindowTitle(title, QString::fromUtf8(" \xe2\x80\x94 ")); // unicode character U+2014, EM DASH
- const QByteArray ba = std::move(fullTitle).toUtf8();
- xcb_change_property(xcb_connection(),
- XCB_PROP_MODE_REPLACE,
- m_window,
- atom(QXcbAtom::_NET_WM_NAME),
- atom(QXcbAtom::UTF8_STRING),
- 8,
- ba.length(),
- ba.constData());
-
-#if QT_CONFIG(xcb_xlib)
- Display *dpy = static_cast<Display *>(connection()->xlib_display());
- XTextProperty *text = qstringToXTP(dpy, title);
- if (text)
- XSetWMName(dpy, m_window, text);
-#endif
- xcb_flush(xcb_connection());
+ setWindowTitle(connection(), m_window, title);
}
void QXcbWindow::setWindowIconText(const QString &title)
@@ -1541,38 +1397,28 @@ void QXcbWindow::lower()
xcb_configure_window(xcb_connection(), m_window, mask, values);
}
-// Adapt the geometry to match the WM expection with regards
-// to gravity.
-QRect QXcbWindow::windowToWmGeometry(QRect r) const
-{
- if (m_dirtyFrameMargins || m_frameMargins.isNull())
- return r;
- const bool frameInclusive = positionIncludesFrame(window());
- // XCB_GRAVITY_STATIC requires the inner geometry, whereas
- // XCB_GRAVITY_NORTH_WEST requires the frame geometry
- if (frameInclusive && m_gravity == XCB_GRAVITY_STATIC) {
- r.translate(m_frameMargins.left(), m_frameMargins.top());
- } else if (!frameInclusive && m_gravity == XCB_GRAVITY_NORTH_WEST) {
- r.translate(-m_frameMargins.left(), -m_frameMargins.top());
- }
- return r;
-}
-
void QXcbWindow::propagateSizeHints()
{
// update WM_NORMAL_HINTS
xcb_size_hints_t hints;
memset(&hints, 0, sizeof(hints));
- const QRect xRect = windowToWmGeometry(geometry());
+ const QRect rect = geometry();
+ QWindowPrivate *win = qt_window_private(window());
- QWindow *win = window();
+ if (!win->positionAutomatic)
+ xcb_size_hints_set_position(&hints, true, rect.x(), rect.y());
+ if (rect.width() < QWINDOWSIZE_MAX || rect.height() < QWINDOWSIZE_MAX)
+ xcb_size_hints_set_size(&hints, true, rect.width(), rect.height());
- if (!qt_window_private(win)->positionAutomatic)
- xcb_size_hints_set_position(&hints, true, xRect.x(), xRect.y());
- if (xRect.width() < QWINDOWSIZE_MAX || xRect.height() < QWINDOWSIZE_MAX)
- xcb_size_hints_set_size(&hints, true, xRect.width(), xRect.height());
- xcb_size_hints_set_win_gravity(&hints, m_gravity);
+ /* Gravity describes how to interpret x and y values the next time
+ window needs to be positioned on a screen.
+ XCB_GRAVITY_STATIC : the left top corner of the client window
+ XCB_GRAVITY_NORTH_WEST : the left top corner of the frame window */
+ auto gravity = win->positionPolicy == QWindowPrivate::WindowFrameInclusive
+ ? XCB_GRAVITY_NORTH_WEST : XCB_GRAVITY_STATIC;
+
+ xcb_size_hints_set_win_gravity(&hints, gravity);
QSize minimumSize = windowMinimumSize();
QSize maximumSize = windowMaximumSize();
@@ -1836,12 +1682,6 @@ void QXcbWindow::setWmWindowRole(const QByteArray &role)
role.size(), role.constData());
}
-void QXcbWindow::setParentRelativeBackPixmapStatic(QWindow *window)
-{
- if (window->handle())
- static_cast<QXcbWindow *>(window->handle())->setParentRelativeBackPixmap();
-}
-
void QXcbWindow::setParentRelativeBackPixmap()
{
const quint32 mask = XCB_CW_BACK_PIXMAP;
@@ -1849,14 +1689,7 @@ void QXcbWindow::setParentRelativeBackPixmap()
xcb_change_window_attributes(xcb_connection(), m_window, mask, values);
}
-bool QXcbWindow::requestSystemTrayWindowDockStatic(const QWindow *window)
-{
- if (window->handle())
- return static_cast<QXcbWindow *>(window->handle())->requestSystemTrayWindowDock();
- return false;
-}
-
-bool QXcbWindow::requestSystemTrayWindowDock() const
+bool QXcbWindow::requestSystemTrayWindowDock()
{
if (!connection()->systemTrayTracker())
return false;
@@ -1864,81 +1697,33 @@ bool QXcbWindow::requestSystemTrayWindowDock() const
return true;
}
-QRect QXcbWindow::systemTrayWindowGlobalGeometryStatic(const QWindow *window)
+bool QXcbWindow::handleNativeEvent(xcb_generic_event_t *event)
{
- if (window->handle())
- return static_cast<QXcbWindow *>(window->handle())->systemTrayWindowGlobalGeometry();
- return QRect();
+ auto eventType = connection()->nativeInterface()->nativeEventType();
+ long result = 0; // Used only by MS Windows
+ return QWindowSystemInterface::handleNativeEvent(window(), eventType, event, &result);
}
-QRect QXcbWindow::systemTrayWindowGlobalGeometry() const
+void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event)
{
- if (!connection()->systemTrayTracker())
- return QRect();
- return connection()->systemTrayTracker()->systemTrayWindowGlobalGeometry(m_window);
-}
+ QRect rect(event->x, event->y, event->width, event->height);
+ m_exposeRegion |= rect;
-class ExposeCompressor
-{
-public:
- ExposeCompressor(xcb_window_t window, QRegion *region)
- : m_window(window)
- , m_region(region)
- , m_pending(true)
- {
- }
+ bool pending = true;
- bool checkEvent(xcb_generic_event_t *event)
- {
- if (!event)
- return false;
- if ((event->response_type & ~0x80) != XCB_EXPOSE)
+ connection()->eventQueue()->peek(QXcbEventQueue::PeekRemoveMatchContinue,
+ [this, &pending](xcb_generic_event_t *event, int type) {
+ if (type != XCB_EXPOSE)
return false;
- xcb_expose_event_t *expose = (xcb_expose_event_t *)event;
+ auto expose = reinterpret_cast<xcb_expose_event_t *>(event);
if (expose->window != m_window)
return false;
if (expose->count == 0)
- m_pending = false;
- *m_region |= QRect(expose->x, expose->y, expose->width, expose->height);
+ pending = false;
+ m_exposeRegion |= QRect(expose->x, expose->y, expose->width, expose->height);
+ free(expose);
return true;
- }
-
- bool pending() const
- {
- return m_pending;
- }
-
-private:
- xcb_window_t m_window;
- QRegion *m_region;
- bool m_pending;
-};
-
-bool QXcbWindow::compressExposeEvent(QRegion &exposeRegion)
-{
- ExposeCompressor compressor(m_window, &exposeRegion);
- xcb_generic_event_t *filter = 0;
- do {
- filter = connection()->checkEvent(compressor);
- free(filter);
- } while (filter);
- return compressor.pending();
-}
-
-bool QXcbWindow::handleGenericEvent(xcb_generic_event_t *event, long *result)
-{
- return QWindowSystemInterface::handleNativeEvent(window(),
- connection()->nativeInterface()->genericEventFilterType(),
- event,
- result);
-}
-
-void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event)
-{
- QRect rect(event->x, event->y, event->width, event->height);
-
- m_exposeRegion |= rect;
- bool pending = compressExposeEvent(m_exposeRegion);
+ });
// if count is non-zero there are more expose events pending
if (event->count == 0 || !pending) {
@@ -1977,7 +1762,7 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even
connection()->setTime(event->data.data32[1]);
m_syncValue.lo = event->data.data32[2];
m_syncValue.hi = event->data.data32[3];
- if (m_usingSyncProtocol)
+ if (connection()->hasXSync())
m_syncState = SyncReceived;
#ifndef QT_NO_WHATSTHIS
} else if (protocolAtom == atom(QXcbAtom::_NET_WM_CONTEXT_HELP)) {
@@ -2052,7 +1837,7 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t *
}
m_oldWindowSize = actualGeometry.size();
- if (m_usingSyncProtocol && m_syncState == SyncReceived)
+ if (connection()->hasXSync() && m_syncState == SyncReceived)
m_syncState = SyncAndConfigureReceived;
m_dirtyFrameMargins = true;
@@ -2137,7 +1922,7 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in
updateNetWmUserTime(timestamp);
- if (m_embedded) {
+ if (m_embedded && !m_trayIconWindow) {
if (window() != QGuiApplication::focusWindow()) {
const QXcbWindow *container = static_cast<const QXcbWindow *>(parent());
Q_ASSERT(container != 0);
@@ -2149,7 +1934,7 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in
QPoint global(root_x, root_y);
if (isWheel) {
-#if QT_CONFIG(xinput2)
+#if QT_CONFIG(xcb_xinput)
if (!connection()->isAtLeastXI21()) {
#endif
QPoint angleDelta;
@@ -2164,7 +1949,7 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in
if (modifiers & Qt::AltModifier)
std::swap(angleDelta.rx(), angleDelta.ry());
QWindowSystemInterface::handleWheelEvent(window(), timestamp, local, global, QPoint(), angleDelta, modifiers);
-#if QT_CONFIG(xinput2)
+#if QT_CONFIG(xcb_xinput)
}
#endif
return;
@@ -2204,7 +1989,7 @@ static inline bool doCheckUnGrabAncestor(QXcbConnection *conn)
if (conn) {
const bool mouseButtonsPressed = (conn->buttonState() != Qt::NoButton);
-#if QT_CONFIG(xinput2)
+#if QT_CONFIG(xcb_xinput)
return mouseButtonsPressed || (conn->hasXInput2() && !conn->xi2MouseEventsDisabled());
#else
return mouseButtonsPressed;
@@ -2231,24 +2016,6 @@ static bool ignoreEnterEvent(quint8 mode, quint8 detail, QXcbConnection *conn =
|| detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL);
}
-class EnterEventChecker
-{
-public:
- bool checkEvent(xcb_generic_event_t *event)
- {
- if (!event)
- return false;
- if ((event->response_type & ~0x80) != XCB_ENTER_NOTIFY)
- return false;
-
- xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *)event;
- if (ignoreEnterEvent(enter->mode, enter->detail))
- return false;
-
- return true;
- }
-};
-
void QXcbWindow::handleEnterNotifyEvent(int event_x, int event_y, int root_x, int root_y,
quint8 mode, quint8 detail, xcb_timestamp_t timestamp)
{
@@ -2258,7 +2025,7 @@ void QXcbWindow::handleEnterNotifyEvent(int event_x, int event_y, int root_x, in
if (ignoreEnterEvent(mode, detail, connection()) || connection()->mousePressWindow())
return;
-#ifdef XCB_USE_XINPUT21
+#if QT_CONFIG(xcb_xinput)
// Updates scroll valuators, as user might have done some scrolling outside our X client.
connection()->xi2UpdateScrollingDevices();
#endif
@@ -2275,9 +2042,15 @@ void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y,
if (ignoreLeaveEvent(mode, detail, connection()) || connection()->mousePressWindow())
return;
- EnterEventChecker checker;
- xcb_enter_notify_event_t *enter = (xcb_enter_notify_event_t *)connection()->checkEvent(checker);
- QXcbWindow *enterWindow = enter ? connection()->platformWindowFromId(enter->event) : 0;
+ // check if enter event is buffered
+ auto event = connection()->eventQueue()->peek([](xcb_generic_event_t *event, int type) {
+ if (type != XCB_ENTER_NOTIFY)
+ return false;
+ auto enter = reinterpret_cast<xcb_enter_notify_event_t *>(event);
+ return !ignoreEnterEvent(enter->mode, enter->detail);
+ });
+ auto enter = reinterpret_cast<xcb_enter_notify_event_t *>(event);
+ QXcbWindow *enterWindow = enter ? connection()->platformWindowFromId(enter->event) : nullptr;
if (enterWindow) {
QPoint local(enter->event_x, enter->event_y);
@@ -2330,16 +2103,18 @@ void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event)
event->time, QEvent::MouseMove);
}
-#if QT_CONFIG(xinput2)
-static inline int fixed1616ToInt(FP1616 val)
+#if QT_CONFIG(xcb_xinput)
+static inline int fixed1616ToInt(xcb_input_fp1616_t val)
{
return int(qreal(val) / 0x10000);
}
+#define qt_xcb_mask_is_set(ptr, event) (((unsigned char*)(ptr))[(event)>>3] & (1 << ((event) & 7)))
+
void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource source)
{
QXcbConnection *conn = connection();
- xXIDeviceEvent *ev = reinterpret_cast<xXIDeviceEvent *>(event);
+ auto *ev = reinterpret_cast<xcb_input_button_press_event_t *>(event);
if (ev->buttons_len > 0) {
unsigned char *buttonMask = (unsigned char *) &ev[1];
@@ -2347,16 +2122,16 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource
// XIPointerEmulated being set: https://bugs.freedesktop.org/show_bug.cgi?id=98188
// Filter them out by other attributes: when their source device is a touch screen
// and the LMB is pressed.
- if (XIMaskIsSet(buttonMask, 1) && conn->isTouchScreen(ev->sourceid)) {
+ if (qt_xcb_mask_is_set(buttonMask, 1) && conn->isTouchScreen(ev->sourceid)) {
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInput, "XI2 mouse event from touch device %d was ignored", ev->sourceid);
return;
}
for (int i = 1; i <= 15; ++i)
- conn->setButtonState(conn->translateMouseButton(i), XIMaskIsSet(buttonMask, i));
+ conn->setButtonState(conn->translateMouseButton(i), qt_xcb_mask_is_set(buttonMask, i));
}
- const Qt::KeyboardModifiers modifiers = conn->keyboard()->translateModifiers(ev->mods.effective_mods);
+ const Qt::KeyboardModifiers modifiers = conn->keyboard()->translateModifiers(ev->mods.effective);
const int event_x = fixed1616ToInt(ev->event_x);
const int event_y = fixed1616ToInt(ev->event_y);
const int root_x = fixed1616ToInt(ev->root_x);
@@ -2373,47 +2148,47 @@ void QXcbWindow::handleXIMouseEvent(xcb_ge_event_t *event, Qt::MouseEventSource
sourceName = me.valueToKey(source);
}
- switch (ev->evtype) {
- case XI_ButtonPress:
+ switch (ev->event_type) {
+ case XCB_INPUT_BUTTON_PRESS:
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "XI2 mouse press, button %d, time %d, source %s", button, ev->time, sourceName);
conn->setButtonState(button, true);
handleButtonPressEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, QEvent::MouseButtonPress, source);
break;
- case XI_ButtonRelease:
+ case XCB_INPUT_BUTTON_RELEASE:
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "XI2 mouse release, button %d, time %d, source %s", button, ev->time, sourceName);
conn->setButtonState(button, false);
handleButtonReleaseEvent(event_x, event_y, root_x, root_y, ev->detail, modifiers, ev->time, QEvent::MouseButtonRelease, source);
break;
- case XI_Motion:
+ case XCB_INPUT_MOTION:
if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled()))
qCDebug(lcQpaXInputEvents, "XI2 mouse motion %d,%d, time %d, source %s", event_x, event_y, ev->time, sourceName);
handleMotionNotifyEvent(event_x, event_y, root_x, root_y, modifiers, ev->time, QEvent::MouseMove, source);
break;
default:
- qWarning() << "Unrecognized XI2 mouse event" << ev->evtype;
+ qWarning() << "Unrecognized XI2 mouse event" << ev->event_type;
break;
}
}
void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event)
{
- xXIEnterEvent *ev = reinterpret_cast<xXIEnterEvent *>(event);
+ auto *ev = reinterpret_cast<xcb_input_enter_event_t *>(event);
// Compare the window with current mouse grabber to prevent deliver events to any other windows.
// If leave event occurs and the window is under mouse - allow to deliver the leave event.
QXcbWindow *mouseGrabber = connection()->mouseGrabber();
if (mouseGrabber && mouseGrabber != this
- && (ev->evtype != XI_Leave || QGuiApplicationPrivate::currentMouseWindow != window())) {
+ && (ev->event_type != XCB_INPUT_LEAVE || QGuiApplicationPrivate::currentMouseWindow != window())) {
return;
}
const int root_x = fixed1616ToInt(ev->root_x);
const int root_y = fixed1616ToInt(ev->root_y);
- switch (ev->evtype) {
- case XI_Enter: {
+ switch (ev->event_type) {
+ case XCB_INPUT_ENTER: {
const int event_x = fixed1616ToInt(ev->event_x);
const int event_y = fixed1616ToInt(ev->event_y);
qCDebug(lcQpaXInputEvents, "XI2 mouse enter %d,%d, mode %d, detail %d, time %d",
@@ -2421,7 +2196,7 @@ void QXcbWindow::handleXIEnterLeave(xcb_ge_event_t *event)
handleEnterNotifyEvent(event_x, event_y, root_x, root_y, ev->mode, ev->detail, ev->time);
break;
}
- case XI_Leave:
+ case XCB_INPUT_LEAVE:
qCDebug(lcQpaXInputEvents, "XI2 mouse leave, mode %d, detail %d, time %d",
ev->mode, ev->detail, ev->time);
connection()->keyboard()->updateXKBStateFromXI(&ev->mods, &ev->group);
@@ -2523,7 +2298,7 @@ void QXcbWindow::updateSyncRequestCounter()
// window manager does not expect a sync event yet.
return;
}
- if (m_usingSyncProtocol && (m_syncValue.lo != 0 || m_syncValue.hi != 0)) {
+ if (connection()->hasXSync() && (m_syncValue.lo != 0 || m_syncValue.hi != 0)) {
xcb_sync_set_counter(xcb_connection(), m_syncCounter, m_syncValue);
xcb_flush(xcb_connection());
@@ -2563,7 +2338,7 @@ bool QXcbWindow::setMouseGrabEnabled(bool grab)
if (grab && !connection()->canGrab())
return false;
-#if QT_CONFIG(xinput2)
+#if QT_CONFIG(xcb_xinput)
if (connection()->hasXInput2() && !connection()->xi2MouseEventsDisabled()) {
bool result = connection()->xi2SetMouseGrabEnabled(m_window, grab);
if (grab && result)
@@ -2591,11 +2366,11 @@ bool QXcbWindow::setMouseGrabEnabled(bool grab)
return result;
}
-void QXcbWindow::windowEvent(QEvent *event)
+bool QXcbWindow::windowEvent(QEvent *event)
{
switch (event->type()) {
case QEvent::FocusIn:
- if (m_embedded && !event->spontaneous()) {
+ if (m_embedded && !m_trayIconWindow && !event->spontaneous()) {
QFocusEvent *focusEvent = static_cast<QFocusEvent *>(event);
switch (focusEvent->reason()) {
case Qt::TabFocusReason:
@@ -2617,7 +2392,7 @@ void QXcbWindow::windowEvent(QEvent *event)
default:
break;
}
- QPlatformWindow::windowEvent(event);
+ return QPlatformWindow::windowEvent(event);
}
bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner)
@@ -2638,7 +2413,7 @@ bool QXcbWindow::startSystemMoveResize(const QPoint &pos, int corner)
return false;
const QPoint globalPos = QHighDpi::toNativePixels(window()->mapToGlobal(pos), window()->screen());
-#ifdef XCB_USE_XINPUT22
+#if QT_CONFIG(xcb_xinput)
// ### FIXME QTBUG-53389
bool startedByTouch = connection()->startSystemMoveResizeForTouchBegin(m_window, globalPos, corner);
if (startedByTouch) {
@@ -2659,6 +2434,7 @@ bool QXcbWindow::startSystemMoveResize(const QPoint &pos, int corner)
return true;
}
+
void QXcbWindow::doStartSystemMoveResize(const QPoint &globalPos, int corner)
{
const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE);
@@ -2728,11 +2504,6 @@ void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event)
case XEMBED_EMBEDDED_NOTIFY:
xcb_map_window(xcb_connection(), m_window);
xcbScreen()->windowShown(this);
- // Without Qt::WA_TranslucentBackground, we use a ParentRelative BackPixmap.
- // Clear the whole tray icon window to its background color as early as possible
- // so that we can get a clean result from grabWindow() later.
- xcb_clear_area(xcb_connection(), false, m_window, 0, 0, geometry().width(), geometry().height());
- xcb_flush(xcb_connection());
break;
case XEMBED_FOCUS_IN:
Qt::FocusReason reason;
@@ -2846,6 +2617,28 @@ QXcbScreen *QXcbWindow::xcbScreen() const
return static_cast<QXcbScreen *>(screen());
}
+void QXcbWindow::setWindowTitle(const QXcbConnection *conn, xcb_window_t window, const QString &title)
+{
+ QString fullTitle = formatWindowTitle(title, QString::fromUtf8(" \xe2\x80\x94 ")); // unicode character U+2014, EM DASH
+ const QByteArray ba = std::move(fullTitle).toUtf8();
+ xcb_change_property(conn->xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ window,
+ conn->atom(QXcbAtom::_NET_WM_NAME),
+ conn->atom(QXcbAtom::UTF8_STRING),
+ 8,
+ ba.length(),
+ ba.constData());
+
+#if QT_CONFIG(xcb_xlib)
+ Display *dpy = static_cast<Display *>(conn->xlib_display());
+ XTextProperty *text = qstringToXTP(dpy, title);
+ if (text)
+ XSetWMName(dpy, window, text);
+#endif
+ xcb_flush(conn->xcb_connection());
+}
+
QString QXcbWindow::windowTitle(const QXcbConnection *conn, xcb_window_t window)
{
const xcb_atom_t utf8Atom = conn->atom(QXcbAtom::UTF8_STRING);
@@ -2856,6 +2649,15 @@ QString QXcbWindow::windowTitle(const QXcbConnection *conn, xcb_window_t window)
const char *name = reinterpret_cast<const char *>(xcb_get_property_value(reply.get()));
return QString::fromUtf8(name, xcb_get_property_value_length(reply.get()));
}
+
+ reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, conn->xcb_connection(),
+ false, window, conn->atom(QXcbAtom::WM_NAME),
+ XCB_ATOM_STRING, 0, 1024);
+ if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) {
+ const char *name = reinterpret_cast<const char *>(xcb_get_property_value(reply.get()));
+ return QString::fromLatin1(name, xcb_get_property_value_length(reply.get()));
+ }
+
return QString();
}
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index c4f49c593c..6667f45343 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -105,7 +105,7 @@ public:
QSurfaceFormat format() const override;
- void windowEvent(QEvent *event) override;
+ bool windowEvent(QEvent *event) override;
bool startSystemResize(const QPoint &pos, Qt::Corner corner) override;
bool startSystemMove(const QPoint &pos) override;
@@ -121,7 +121,7 @@ public:
QImage::Format imageFormat() const { return m_imageFormat; }
bool imageNeedsRgbSwap() const { return m_imageRgbSwap; }
- bool handleGenericEvent(xcb_generic_event_t *event, long *result) override;
+ bool handleNativeEvent(xcb_generic_event_t *event) override;
void handleExposeEvent(const xcb_expose_event_t *event) override;
void handleClientMessageEvent(const xcb_client_message_event_t *event) override;
@@ -137,7 +137,7 @@ public:
void handleFocusInEvent(const xcb_focus_in_event_t *event) override;
void handleFocusOutEvent(const xcb_focus_out_event_t *event) override;
void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) override;
-#if QT_CONFIG(xinput2)
+#if QT_CONFIG(xcb_xinput)
void handleXIMouseEvent(xcb_ge_event_t *, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized) override;
void handleXIEnterLeave(xcb_ge_event_t *) override;
#endif
@@ -159,14 +159,8 @@ public:
static void setWindowIconTextStatic(QWindow *window, const QString &text);
- static void setParentRelativeBackPixmapStatic(QWindow *window);
void setParentRelativeBackPixmap();
-
- static bool requestSystemTrayWindowDockStatic(const QWindow *window);
- bool requestSystemTrayWindowDock() const;
-
- static QRect systemTrayWindowGlobalGeometryStatic(const QWindow *window);
- QRect systemTrayWindowGlobalGeometry() const;
+ bool requestSystemTrayWindowDock();
uint visualId() const;
bool needsSync() const;
@@ -179,9 +173,15 @@ public:
bool startSystemMoveResize(const QPoint &pos, int corner);
void doStartSystemMoveResize(const QPoint &globalPos, int corner);
+ static bool isTrayIconWindow(QWindow *window)
+ {
+ return window->objectName() == QLatin1String("QSystemTrayIconSysWindow");
+ }
+
virtual void create();
virtual void destroy();
+ static void setWindowTitle(const QXcbConnection *conn, xcb_window_t window, const QString &title);
static QString windowTitle(const QXcbConnection *conn, xcb_window_t window);
public Q_SLOTS:
@@ -199,10 +199,9 @@ protected:
NetWmStates netWmStates();
void setNetWmStates(NetWmStates);
- void setMotifWindowFlags(Qt::WindowFlags flags);
- void setNetWmStateWindowFlags(Qt::WindowFlags flags);
+ void setMotifWmHints(Qt::WindowFlags flags);
- void updateMotifWmHintsBeforeMap();
+ void setNetWmStateWindowFlags(Qt::WindowFlags flags);
void updateNetWmStateBeforeMap();
void setTransparentForMouseEvents(bool transparent);
@@ -220,8 +219,6 @@ protected:
void doFocusIn();
void doFocusOut();
- bool compressExposeEvent(QRegion &exposeRegion);
-
void handleButtonPressEvent(int event_x, int event_y, int root_x, int root_y,
int detail, Qt::KeyboardModifiers modifiers, xcb_timestamp_t timestamp,
QEvent::Type type, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized);
@@ -252,15 +249,13 @@ protected:
Qt::WindowStates m_windowState = Qt::WindowNoState;
- xcb_gravity_t m_gravity = XCB_GRAVITY_STATIC;
-
bool m_mapped = false;
bool m_transparent = false;
- bool m_usingSyncProtocol = false;
bool m_deferredActivation = false;
bool m_embedded = false;
bool m_alertState = false;
bool m_minimized = false;
+ bool m_trayIconWindow = false;
xcb_window_t m_netWmUserTimeWindow = XCB_NONE;
QSurfaceFormat m_format;
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-static/xcb-static.pro b/src/plugins/platforms/xcb/xcb-static/xcb-static.pro
index 20d8b83e7c..078b275381 100644
--- a/src/plugins/platforms/xcb/xcb-static/xcb-static.pro
+++ b/src/plugins/platforms/xcb/xcb-static/xcb-static.pro
@@ -2,7 +2,7 @@
# Statically compile in code for
# libxcb-fixes, libxcb-randr, libxcb-shm, libxcb-sync, libxcb-image,
# libxcb-keysyms, libxcb-icccm, libxcb-renderutil, libxcb-xkb,
-# libxcb-xinerama
+# libxcb-xinerama, libxcb-xinput
#
CONFIG += static
@@ -29,7 +29,8 @@ SOURCES += \
$$LIBXCB_DIR/render.c \
$$LIBXCB_DIR/shape.c \
$$LIBXCB_DIR/xkb.c \
- $$LIBXCB_DIR/xinerama.c
+ $$LIBXCB_DIR/xinerama.c \
+ $$LIBXCB_DIR/xinput.c
#
# xcb-util
diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
index 9c4797ac26..c65b145fb6 100644
--- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro
+++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
@@ -5,7 +5,7 @@ DEFINES += QT_NO_FOREACH
QT += \
core-private gui-private \
service_support-private theme_support-private \
- eventdispatcher_support-private fontdatabase_support-private \
+ fontdatabase_support-private \
edid_support-private
qtHaveModule(linuxaccessibility_support-private): \
@@ -13,6 +13,8 @@ qtHaveModule(linuxaccessibility_support-private): \
qtConfig(vulkan): QT += vulkan_support-private
+qtConfig(glib) : QMAKE_USE_PRIVATE += glib
+
SOURCES = \
qxcbclipboard.cpp \
qxcbconnection.cpp \
@@ -27,7 +29,12 @@ SOURCES = \
qxcbcursor.cpp \
qxcbimage.cpp \
qxcbxsettings.cpp \
- qxcbsystemtraytracker.cpp
+ qxcbsystemtraytracker.cpp \
+ qxcbeventqueue.cpp \
+ qxcbeventdispatcher.cpp \
+ qxcbconnection_basic.cpp \
+ qxcbconnection_screens.cpp \
+ qxcbatom.cpp
HEADERS = \
qxcbclipboard.h \
@@ -45,7 +52,11 @@ HEADERS = \
qxcbimage.h \
qxcbxsettings.h \
qxcbsystemtraytracker.h \
- qxcbxkbcommon.h
+ qxcbxkbcommon.h \
+ qxcbeventqueue.h \
+ qxcbeventdispatcher.h \
+ qxcbconnection_basic.h \
+ qxcbatom.h
qtConfig(draganddrop) {
SOURCES += qxcbdrag.cpp
@@ -58,11 +69,10 @@ DEFINES += QT_BUILD_XCB_PLUGIN
qtConfig(xcb-xlib) {
QMAKE_USE += xcb_xlib
+}
- qtConfig(xinput2) {
- SOURCES += qxcbconnection_xi2.cpp
- QMAKE_USE += xinput2
- }
+qtConfig(xcb-xinput) {
+ SOURCES += qxcbconnection_xi2.cpp
}
qtConfig(xcb-sm) {
@@ -89,6 +99,7 @@ qtConfig(vulkan) {
} else {
qtConfig(xkb): QMAKE_USE += xcb_xkb
qtConfig(xcb-render): QMAKE_USE += xcb_render
+ qtConfig(xcb-xinput): QMAKE_USE += xcb_xinput
QMAKE_USE += xcb_syslibs
}