summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/xcb
diff options
context:
space:
mode:
authorTobias Hunger <tobias.hunger@qt.io>2019-04-16 16:32:08 +0200
committerTobias Hunger <tobias.hunger@qt.io>2019-04-16 16:32:08 +0200
commit6630937e63ae5797487b86743a7733c8ae5cc42c (patch)
tree3d53dacf6430f9099e1fb20835881205de674961 /src/plugins/platforms/xcb
parent37ed6dae00640f9cc980ffda05347c12a7eb5d7e (diff)
parentc7af193d2e49e9f10b86262e63d8d13abf72b5cf (diff)
Merge commit 'dev' into 'wip/cmake-merge'
Diffstat (limited to 'src/plugins/platforms/xcb')
-rw-r--r--src/plugins/platforms/xcb/CMakeLists.txt138
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/gl_integrations.pro4
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationplugin.h2
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp117
-rw-r--r--src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp4
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp10
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp19
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h2
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp14
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h5
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qt_x11_p.h2
-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.cpp56
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.h5
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp231
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.h41
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp1447
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h464
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.cpp445
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.h181
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_screens.cpp416
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp97
-rw-r--r--src/plugins/platforms/xcb/qxcbcursor.cpp7
-rw-r--r--src/plugins/platforms/xcb/qxcbcursor.h4
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp4
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.h4
-rw-r--r--src/plugins/platforms/xcb/qxcbeventdispatcher.cpp162
-rw-r--r--src/plugins/platforms/xcb/qxcbeventdispatcher.h114
-rw-r--r--src/plugins/platforms/xcb/qxcbeventqueue.cpp395
-rw-r--r--src/plugins/platforms/xcb/qxcbeventqueue.h169
-rw-r--r--src/plugins/platforms/xcb/qxcbimage.cpp17
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp36
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.h3
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp773
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.h43
-rw-r--r--src/plugins/platforms/xcb/qxcbmain.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbmime.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.cpp19
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.h4
-rw-r--r--src/plugins/platforms/xcb/qxcbobject.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp15
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.h4
-rw-r--r--src/plugins/platforms/xcb/qxcbsessionmanager.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp485
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.h17
-rw-r--r--src/plugins/platforms/xcb/qxcbxkbcommon.h233
-rw-r--r--src/plugins/platforms/xcb/xcb-plugin.pro1
-rw-r--r--src/plugins/platforms/xcb/xcb_qpa_lib.pro45
49 files changed, 3319 insertions, 3477 deletions
diff --git a/src/plugins/platforms/xcb/CMakeLists.txt b/src/plugins/platforms/xcb/CMakeLists.txt
index 434fc5e3ca..646d9f1976 100644
--- a/src/plugins/platforms/xcb/CMakeLists.txt
+++ b/src/plugins/platforms/xcb/CMakeLists.txt
@@ -1,16 +1,19 @@
# Generated from xcb_qpa_lib.pro.
-#####################################################################
-## QtXcbQpa Module:
-#####################################################################
+# special case:
find_package(X11_XCB)
find_package(X11)
find_package(XCB)
find_package(XKB)
find_package(PkgConfig)
find_package(Freetype)
+find_package(GLIB2)
-pkg_check_modules(XKB_COMMON_X11 xkbcommon-x11>=0.4.1 IMPORTED_TARGET)
+pkg_check_modules(XKB_COMMON_X11 xkbcommon-x11>=0.4.1 IMPORTED_TARGET) # special case
+
+#####################################################################
+## XcbQpa Module:
+#####################################################################
add_qt_module(XcbQpa
NO_MODULE_HEADERS
@@ -19,10 +22,15 @@ add_qt_module(XcbQpa
gl_integrations/qxcbglintegrationfactory.cpp gl_integrations/qxcbglintegrationfactory.h
gl_integrations/qxcbglintegrationplugin.h
gl_integrations/qxcbnativeinterfacehandler.cpp gl_integrations/qxcbnativeinterfacehandler.h
+ qxcbatom.cpp qxcbatom.h
qxcbbackingstore.cpp qxcbbackingstore.h
qxcbclipboard.cpp qxcbclipboard.h
qxcbconnection.cpp qxcbconnection.h
+ qxcbconnection_basic.cpp qxcbconnection_basic.h
+ qxcbconnection_screens.cpp
qxcbcursor.cpp qxcbcursor.h
+ qxcbeventdispatcher.cpp qxcbeventdispatcher.h
+ qxcbeventqueue.cpp qxcbeventqueue.h
qxcbimage.cpp qxcbimage.h
qxcbintegration.cpp qxcbintegration.h
qxcbkeyboard.cpp qxcbkeyboard.h
@@ -33,102 +41,107 @@ add_qt_module(XcbQpa
qxcbsystemtraytracker.cpp qxcbsystemtraytracker.h
qxcbwindow.cpp qxcbwindow.h
qxcbwmsupport.cpp qxcbwmsupport.h
- qxcbxkbcommon.h
qxcbxsettings.cpp qxcbxsettings.h
DEFINES
QT_NO_FOREACH
QT_BUILD_XCB_PLUGIN
INCLUDE_DIRECTORIES
- gl_integrations/
+ gl_integrations
LIBRARIES
Qt::CorePrivate
+ Qt::EdidSupportPrivate
+ Qt::FontDatabaseSupportPrivate
Qt::GuiPrivate
Qt::ServiceSupportPrivate
Qt::ThemeSupportPrivate
- Qt::EventDispatcherSupportPrivate
- Qt::FontDatabaseSupportPrivate
- Qt::EdidSupportPrivate
- X11::XCB
- XCB::XCB
- XCB::SHAPE
+ Qt::XkbCommonSupportPrivate
+ PUBLIC_LIBRARIES
+ ${CMAKE_DL_LIBS}
+ Qt::Core
+ Qt::EdidSupport
+ Qt::FontDatabaseSupport
+ Qt::Gui
+ Qt::ServiceSupport
+ Qt::ThemeSupport
+ Qt::XkbCommonSupport
XCB::ICCCM
+ XCB::IMAGE
+ XCB::KEYSYMS
XCB::RANDR
- XCB::XKB
+ XCB::RENDER
+ XCB::RENDERUTIL
+ XCB::SHAPE
+ XCB::SHM
XCB::SYNC
+ XCB::XCB
XCB::XFIXES
XCB::XINERAMA
- XCB::SHM
- XCB::IMAGE
- XCB::RENDER
- XCB::RENDERUTIL
- XCB::KEYSYMS
XKB::XKB
- PkgConfig::XKB_COMMON_X11
)
+#### Keys ignored in scope 1:.:.:xcb_qpa_lib.pro:<TRUE>:
+# CONFIG = "no_module_headers" "internal_module"
+# _LOADED = "qt_build_paths" "qt_module"
+
## Scopes:
#####################################################################
extend_target(XcbQpa CONDITION TARGET Qt::LinuxAccessibilitySupportPrivate
LIBRARIES
Qt::LinuxAccessibilitySupportPrivate
+ PUBLIC_LIBRARIES
+ Qt::LinuxAccessibilitySupport
)
extend_target(XcbQpa CONDITION QT_FEATURE_vulkan
+ SOURCES
+ qxcbvulkaninstance.cpp qxcbvulkaninstance.h
+ qxcbvulkanwindow.cpp qxcbvulkanwindow.h
+ LIBRARIES
+ Qt::VulkanSupportPrivate
+ PUBLIC_LIBRARIES
+ Qt::VulkanSupport
+)
+
+extend_target(XcbQpa CONDITION QT_FEATURE_glib
LIBRARIES
- VulkanSupportPrivate
+ GLIB2::GLIB2
)
extend_target(XcbQpa CONDITION QT_FEATURE_draganddrop
SOURCES
qxcbdrag.cpp qxcbdrag.h
)
-#
-#extend_target(XcbQpa CONDITION QT_FEATURE_xcb_xlib
-#)
+
+extend_target(XcbQpa CONDITION QT_FEATURE_xcb_xlib
+ PUBLIC_LIBRARIES
+ X11::XCB
+)
extend_target(XcbQpa CONDITION QT_FEATURE_xcb_xinput
SOURCES
qxcbconnection_xi2.cpp
+ PUBLIC_LIBRARIES
+ XCB::XINPUT
)
extend_target(XcbQpa CONDITION QT_FEATURE_xcb_sm
SOURCES
qxcbsessionmanager.cpp qxcbsessionmanager.h
- LIBRARIES
+ PUBLIC_LIBRARIES
${X11_SM_LIB} ${X11_ICE_LIB}
)
-extend_target(XcbQpa CONDITION QT_FEATURE_vulkan
- SOURCES
- qxcbvulkaninstance.cpp qxcbvulkaninstance.h
- qxcbvulkanwindow.cpp qxcbvulkanwindow.h
+extend_target(XcbQpa CONDITION QT_FEATURE_xkb
+ PUBLIC_LIBRARIES
+ XCB::XKB
+ XKB::XKB
+)
+
+extend_target(XcbQpa CONDITION CLANG AND NOT ICC
+ COMPILE_OPTIONS
+ "-ftemplate-depth=1024"
)
-#
-#extend_target(XcbQpa CONDITION NOT QT_FEATURE_system_xcb
-#)
-#
-#extend_target(XcbQpa CONDITION NOT NOT QT_FEATURE_system_xcb
-#)
-#
-#extend_target(XcbQpa CONDITION (NOT NOT QT_FEATURE_system_xcb) AND (QT_FEATURE_xkb)
-#)
-#
-#extend_target(XcbQpa CONDITION (NOT NOT QT_FEATURE_system_xcb) AND (QT_FEATURE_xcb_render)
-#)
-#
-#extend_target(XcbQpa CONDITION (NOT NOT QT_FEATURE_system_xcb) AND (QT_FEATURE_xcb_xinput)
-#)
-#
-#extend_target(XcbQpa CONDITION NOT QT_FEATURE_xkbcommon_system
-#)
-
-#
-#extend_target(XcbQpa CONDITION NOT NOT QT_FEATURE_xkbcommon_system
-#)
-#
-#extend_target(XcbQpa CONDITION QT_FEATURE_dlopen
-#)
extend_target(XcbQpa CONDITION QT_FEATURE_xcb_native_painting
SOURCES
@@ -140,24 +153,15 @@ extend_target(XcbQpa CONDITION QT_FEATURE_xcb_native_painting
nativepainting/qtessellator.cpp nativepainting/qtessellator_p.h
nativepainting/qxcbnativepainting.cpp nativepainting/qxcbnativepainting.h
INCLUDE_DIRECTORIES
- nativepainting/
+ nativepainting
)
-extend_target(XcbQpa CONDITION (QT_FEATURE_xcb_native_painting) AND (QT_FEATURE_xrender)
- LIBRARIES
- X11::Xrender
+extend_target(XcbQpa CONDITION QT_FEATURE_xcb_native_painting AND QT_FEATURE_xrender
+ PUBLIC_LIBRARIES
+ X11::Xrender # special case
)
-extend_target(XcbQpa CONDITION (QT_FEATURE_xcb_native_painting) AND (QT_FEATURE_fontconfig)
+extend_target(XcbQpa CONDITION QT_FEATURE_fontconfig AND QT_FEATURE_xcb_native_painting
LIBRARIES
Freetype::Freetype
)
-
-add_qt_plugin(qxcb
- TYPE platforms
- SOURCES
- qxcbmain.cpp
- LIBRARIES
- Qt::XcbQpa
- Qt::GuiPrivate
-)
diff --git a/src/plugins/platforms/xcb/gl_integrations/gl_integrations.pro b/src/plugins/platforms/xcb/gl_integrations/gl_integrations.pro
index b8f878ffe8..dde176433c 100644
--- a/src/plugins/platforms/xcb/gl_integrations/gl_integrations.pro
+++ b/src/plugins/platforms/xcb/gl_integrations/gl_integrations.pro
@@ -1,10 +1,10 @@
TEMPLATE = subdirs
QT_FOR_CONFIG += gui-private
-qtConfig(egl):qtConfig(egl_x11):qtConfig(opengl) {
+qtConfig(xcb-egl-plugin) {
SUBDIRS += xcb_egl
}
-qtConfig(xcb-xlib):qtConfig(opengl):!qtConfig(opengles2) {
+qtConfig(xcb-glx-plugin) {
SUBDIRS += xcb_glx
}
diff --git a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationplugin.h b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationplugin.h
index d5a0af97b0..b18248570f 100644
--- a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationplugin.h
+++ b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegrationplugin.h
@@ -54,7 +54,7 @@ class Q_XCB_EXPORT QXcbGlIntegrationPlugin : public QObject
{
Q_OBJECT
public:
- explicit QXcbGlIntegrationPlugin(QObject *parent = 0)
+ explicit QXcbGlIntegrationPlugin(QObject *parent = nullptr)
: QObject(parent)
{ }
diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
index 741885e321..476de6d1e5 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp
@@ -38,9 +38,6 @@
****************************************************************************/
#include <QDebug>
-#if QT_CONFIG(library)
-#include <QLibrary>
-#endif
#include "qxcbwindow.h"
#include "qxcbscreen.h"
@@ -51,6 +48,9 @@
#undef register
#include <GL/glx.h>
+#if QT_CONFIG(regularexpression)
+# include <QtCore/QRegularExpression>
+#endif
#include <QtGui/QOpenGLContext>
#include <QtGui/QOffscreenSurface>
@@ -60,10 +60,6 @@
#include "qxcbglintegration.h"
-#if !defined(QT_STATIC) && QT_CONFIG(dlopen)
-#include <dlfcn.h>
-#endif
-
QT_BEGIN_NAMESPACE
typedef GLXContext (*glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*);
@@ -626,43 +622,7 @@ void QGLXContext::swapBuffers(QPlatformSurface *surface)
QFunctionPointer QGLXContext::getProcAddress(const char *procName)
{
-#ifdef QT_STATIC
- return glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(procName));
-#else
- typedef void *(*qt_glXGetProcAddressARB)(const GLubyte *);
- static qt_glXGetProcAddressARB glXGetProcAddressARB = 0;
- static bool resolved = false;
-
- if (resolved && !glXGetProcAddressARB)
- return 0;
- if (!glXGetProcAddressARB) {
- QList<QByteArray> glxExt = QByteArray(glXGetClientString(m_display, GLX_EXTENSIONS)).split(' ');
- if (glxExt.contains("GLX_ARB_get_proc_address")) {
-#if QT_CONFIG(dlopen)
- void *handle = dlopen(NULL, RTLD_LAZY);
- if (handle) {
- glXGetProcAddressARB = (qt_glXGetProcAddressARB) dlsym(handle, "glXGetProcAddressARB");
- dlclose(handle);
- }
- if (!glXGetProcAddressARB)
-#endif
- {
-#if QT_CONFIG(library)
- extern const QString qt_gl_library_name();
-// QLibrary lib(qt_gl_library_name());
- QLibrary lib(QLatin1String("GL"));
- if (!lib.load())
- lib.setFileNameAndVersion(QLatin1String("GL"), 1);
- glXGetProcAddressARB = (qt_glXGetProcAddressARB) lib.resolve("glXGetProcAddressARB");
-#endif
- }
- }
- resolved = true;
- }
- if (!glXGetProcAddressARB)
- return 0;
- return (void (*)())glXGetProcAddressARB(reinterpret_cast<const GLubyte *>(procName));
-#endif
+ return glXGetProcAddress(reinterpret_cast<const GLubyte *>(procName));
}
QSurfaceFormat QGLXContext::format() const
@@ -692,30 +652,10 @@ static const char *qglx_threadedgl_blacklist_renderer[] = {
0
};
-// This disables threaded rendering on anything using mesa, e.g.
-// - nvidia/nouveau
-// - amd/gallium
-// - intel
-// - some software opengl implementations
-//
-// The client glx vendor string is used to identify those setups as that seems to show the least
-// variance between the bad configurations. It's always "Mesa Project and SGI". There are some
-// configurations which don't use mesa and which can do threaded rendering (amd and nvidia chips
-// with their own proprietary drivers).
-//
-// This, of course, is very broad and disables threaded rendering on a lot of devices which would
-// be able to use it. However, the bugs listed below don't follow any easily recognizable pattern
-// and we should rather be safe.
-//
-// http://cgit.freedesktop.org/xcb/libxcb/commit/?id=be0fe56c3bcad5124dcc6c47a2fad01acd16f71a will
-// fix some of the issues. Basically, the proprietary drivers seem to have a way of working around
-// a fundamental flaw with multithreaded access to xcb, but mesa doesn't. The blacklist should be
-// reevaluated once that patch is released in some version of xcb.
static const char *qglx_threadedgl_blacklist_vendor[] = {
- "Mesa Project and SGI", // QTCREATORBUG-10875 (crash in creator)
- // QTBUG-34492 (flickering in fullscreen)
- // QTBUG-38221
- 0
+ "llvmpipe", // QTCREATORBUG-10666
+ "nouveau", // https://bugs.freedesktop.org/show_bug.cgi?id=91632
+ nullptr
};
void QGLXContext::queryDummyContext()
@@ -776,21 +716,50 @@ void QGLXContext::queryDummyContext()
}
}
}
-
- if (glxvendor) {
+ if (const char *vendor = (const char *) glGetString(GL_VENDOR)) {
for (int i = 0; qglx_threadedgl_blacklist_vendor[i]; ++i) {
- if (strstr(glxvendor, qglx_threadedgl_blacklist_vendor[i]) != 0) {
+ if (strstr(vendor, qglx_threadedgl_blacklist_vendor[i]) != 0) {
qCDebug(lcQpaGl).nospace() << "Multithreaded OpenGL disabled: "
- "blacklisted vendor \""
- << qglx_threadedgl_blacklist_vendor[i]
- << "\"";
-
+ "blacklisted vendor \""
+ << qglx_threadedgl_blacklist_vendor[i]
+ << "\"";
m_supportsThreading = false;
break;
}
}
}
+ if (glxvendor && m_supportsThreading) {
+ // Blacklist Mesa drivers due to QTCREATORBUG-10875 (crash in creator),
+ // QTBUG-34492 (flickering in fullscreen) and QTBUG-38221
+ const char *mesaVersionStr = nullptr;
+ if (strstr(glxvendor, "Mesa Project") != 0) {
+ mesaVersionStr = (const char *) glGetString(GL_VERSION);
+ m_supportsThreading = false;
+ }
+
+ if (mesaVersionStr) {
+ // The issue was fixed in Xcb 1.11, but we can't check for that
+ // at runtime, so instead assume it fixed with recent Mesa versions
+ // released several years after the Xcb fix.
+#if QT_CONFIG(regularexpression)
+ QRegularExpression versionTest(QStringLiteral("Mesa (\\d+)"));
+ QRegularExpressionMatch result = versionTest.match(QString::fromLatin1(mesaVersionStr));
+ int versionNr = 0;
+ if (result.hasMatch())
+ versionNr = result.captured(1).toInt();
+ if (versionNr >= 17) {
+ // White-listed
+ m_supportsThreading = true;
+ }
+#endif
+ }
+ if (!m_supportsThreading) {
+ qCDebug(lcQpaGl).nospace() << "Multithreaded OpenGL disabled: "
+ "blacklisted vendor \"Mesa Project\"";
+ }
+ }
+
context.doneCurrent();
if (oldContext && oldSurface)
oldContext->makeCurrent(oldSurface);
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 b751aaf8a3..34895caaa2 100644
--- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp
+++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qxcbglxintegration.cpp
@@ -164,7 +164,11 @@ bool QXcbGlxIntegration::handleXcbEvent(xcb_generic_event_t *event, uint respons
XUnlockDisplay(xdisplay);
locked = false;
auto eventType = m_connection->nativeInterface()->nativeEventType();
+# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ qintptr result = 0;
+# else
long result = 0;
+# endif
handled = dispatcher->filterNativeEvent(eventType, &ev, &result);
}
#endif
diff --git a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp
index 8851ea59e5..bbc156fc53 100644
--- a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp
+++ b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp
@@ -62,8 +62,10 @@ QXcbNativeBackingStore::QXcbNativeBackingStore(QWindow *window)
: QPlatformBackingStore(window)
, m_translucentBackground(false)
{
- if (QXcbWindow *w = static_cast<QXcbWindow *>(window->handle()))
- m_translucentBackground = w->connection()->hasXRender() && QImage::toPixelFormat(w->imageFormat()).alphaSize() > 0;
+ if (QXcbWindow *w = static_cast<QXcbWindow *>(window->handle())) {
+ m_translucentBackground = w->connection()->hasXRender() &&
+ QImage::toPixelFormat(w->imageFormat()).alphaUsage() == QPixelFormat::UsesAlpha;
+ }
}
QXcbNativeBackingStore::~QXcbNativeBackingStore()
@@ -190,6 +192,10 @@ bool QXcbNativeBackingStore::scroll(const QRegion &area, int dx, int dy)
void QXcbNativeBackingStore::beginPaint(const QRegion &region)
{
+ QX11PlatformPixmap *x11pm = qt_x11Pixmap(m_pixmap);
+ if (x11pm)
+ x11pm->setIsBackingStore(true);
+
#if QT_CONFIG(xrender)
if (m_translucentBackground) {
const QVector<XRectangle> xrects = qt_region_to_xrectangles(region);
diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
index a3e6cedecd..8d958aae94 100644
--- a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
+++ b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp
@@ -200,6 +200,7 @@ public:
uint has_pattern : 1;
uint has_alpha_pen : 1;
uint has_alpha_brush : 1;
+ uint use_sysclip : 1;
uint render_hints;
const QXcbX11Info *xinfo;
@@ -458,7 +459,7 @@ static QPixmap qt_patternForAlpha(uchar alpha, int screen)
% HexString<uchar>(alpha)
% HexString<int>(screen);
- if (!QPixmapCache::find(key, pm)) {
+ if (!QPixmapCache::find(key, &pm)) {
// #### why not use a mono image here????
QImage pattern(DITHER_SIZE, DITHER_SIZE, QImage::Format_ARGB32);
pattern.fill(0xffffffff);
@@ -701,6 +702,9 @@ bool QX11PaintEngine::begin(QPaintDevice *pdev)
d->xlibMaxLinePoints = 32762; // a safe number used to avoid, call to XMaxRequestSize(d->dpy) - 3;
d->opacity = 1;
+ QX11PlatformPixmap *x11pm = paintDevice()->devType() == QInternal::Pixmap ? qt_x11Pixmap(*static_cast<QPixmap *>(paintDevice())) : nullptr;
+ d->use_sysclip = paintDevice()->devType() == QInternal::Widget || (x11pm ? x11pm->isBackingStore() : false);
+
// Set up the polygon clipper. Note: This will only work in
// polyline mode as long as we have a buffer zone, since a
// polyline may be clipped into several non-connected polylines.
@@ -1472,7 +1476,7 @@ void QX11PaintEngine::updatePen(const QPen &pen)
}
if (!d->has_clipping) { // if clipping is set the paintevent clip region is merged with the clip region
- QRegion sysClip = systemClip();
+ QRegion sysClip = d->use_sysclip ? systemClip() : QRegion();
if (!sysClip.isEmpty())
x11SetClipRegion(d->dpy, d->gc, 0, d->picture, sysClip);
else
@@ -1603,7 +1607,7 @@ void QX11PaintEngine::updateBrush(const QBrush &brush, const QPointF &origin)
vals.fill_style = s;
XChangeGC(d->dpy, d->gc_brush, mask, &vals);
if (!d->has_clipping) {
- QRegion sysClip = systemClip();
+ QRegion sysClip = d->use_sysclip ? systemClip() : QRegion();
if (!sysClip.isEmpty())
x11SetClipRegion(d->dpy, d->gc_brush, 0, d->picture, sysClip);
else
@@ -2223,7 +2227,7 @@ void QX11PaintEngine::updateMatrix(const QTransform &mtx)
void QX11PaintEngine::updateClipRegion_dev(const QRegion &clipRegion, Qt::ClipOperation op)
{
Q_D(QX11PaintEngine);
- QRegion sysClip = systemClip();
+ QRegion sysClip = d->use_sysclip ? systemClip() : QRegion();
if (op == Qt::NoClip) {
d->has_clipping = false;
d->crgn = sysClip;
@@ -2641,6 +2645,13 @@ bool QXRenderGlyphCache::addGlyphs(const QTextItemInt &ti,
if (glyph == 0 || glyph->format != glyphFormat())
return false;
+ if (glyph->format == QFontEngine::Format_Mono) {
+ // Must convert bitmap from msb to lsb bit order
+ QImage img(glyph->data, glyph->width, glyph->height, QImage::Format_Mono);
+ img = img.convertToFormat(QImage::Format_MonoLSB);
+ memcpy(glyph->data, img.constBits(), static_cast<size_t>(img.sizeInBytes()));
+ }
+
set->setGlyph(glyphs[i], spp, glyph);
Q_ASSERT(glyph->data || glyph->width == 0 || glyph->height == 0);
diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h
index 9b01c0a3fc..bc82351283 100644
--- a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h
+++ b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11_p.h
@@ -116,7 +116,7 @@ protected:
friend GC qt_x11_get_brush_gc(QPainter *);
private:
- Q_DISABLE_COPY(QX11PaintEngine)
+ Q_DISABLE_COPY_MOVE(QX11PaintEngine)
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp
index 86c87e5e30..b1ce39f363 100644
--- a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp
+++ b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp
@@ -1772,6 +1772,20 @@ XID QX11PlatformPixmap::createBitmapFromImage(const QImage &image)
return hd;
}
+bool QX11PlatformPixmap::isBackingStore() const
+{
+ return (flags & IsBackingStore);
+}
+
+void QX11PlatformPixmap::setIsBackingStore(bool on)
+{
+ if (on)
+ flags |= IsBackingStore;
+ else {
+ flags &= ~IsBackingStore;
+ }
+}
+
#if QT_CONFIG(xrender)
void QX11PlatformPixmap::convertToARGB32(bool preserveContents)
{
diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h
index 7392cbfccf..9c0ba98300 100644
--- a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h
+++ b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11_p.h
@@ -90,6 +90,8 @@ public:
void convertToARGB32(bool preserveContents = true);
#endif
+ bool isBackingStore() const;
+ void setIsBackingStore(bool on);
private:
friend class QX11PaintEngine;
friend const QXcbX11Info &qt_x11Info(const QPixmap &pixmap);
@@ -110,7 +112,8 @@ private:
Uninitialized = 0x1,
Readonly = 0x2,
InvertedWhenBoundToTexture = 0x4,
- GlSurfaceCreatedWithAlpha = 0x8
+ GlSurfaceCreatedWithAlpha = 0x8,
+ IsBackingStore = 0x10
};
uint flags;
diff --git a/src/plugins/platforms/xcb/nativepainting/qt_x11_p.h b/src/plugins/platforms/xcb/nativepainting/qt_x11_p.h
index a13a8f0483..1f7e7cf49b 100644
--- a/src/plugins/platforms/xcb/nativepainting/qt_x11_p.h
+++ b/src/plugins/platforms/xcb/nativepainting/qt_x11_p.h
@@ -190,7 +190,7 @@ struct QX11InfoData {
};
template <class T>
-Q_DECL_RELAXED_CONSTEXPR inline int lowest_bit(T v) Q_DECL_NOTHROW
+Q_DECL_RELAXED_CONSTEXPR inline int lowest_bit(T v) noexcept
{
int result = qCountTrailingZeroBits(v);
return ((result >> 3) == sizeof(T)) ? -1 : result;
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 c29eb29b7e..741317d766 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
@@ -45,16 +45,8 @@
#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>
@@ -106,7 +98,7 @@ public:
void put(xcb_drawable_t dst, const QRegion &region, const QPoint &offset);
void preparePaint(const QRegion &region);
- static bool createSystemVShmSegment(QXcbConnection *c, size_t segmentSize = 1,
+ static bool createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize = 1,
xcb_shm_segment_info_t *shm_info = nullptr);
private:
@@ -406,12 +398,12 @@ void QXcbBackingStoreImage::createShmSegment(size_t segmentSize)
} else
#endif
{
- if (createSystemVShmSegment(connection(), segmentSize, &m_shm_info))
+ if (createSystemVShmSegment(xcb_connection(), segmentSize, &m_shm_info))
m_segmentSize = segmentSize;
}
}
-bool QXcbBackingStoreImage::createSystemVShmSegment(QXcbConnection *c, size_t segmentSize,
+bool QXcbBackingStoreImage::createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize,
xcb_shm_segment_info_t *shmInfo)
{
const int id = shmget(IPC_PRIVATE, segmentSize, IPC_CREAT | 0600);
@@ -429,17 +421,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);
@@ -649,17 +641,17 @@ void QXcbBackingStoreImage::flushPixmap(const QRegion &region, bool fullRegion)
xcb_subimage.bit_order = m_xcb_image->bit_order;
const bool needsByteSwap = xcb_subimage.byte_order != m_xcb_image->byte_order;
+ // Ensure that we don't send more than maxPutImageRequestDataBytes per request.
+ const auto maxPutImageRequestDataBytes = connection()->maxRequestDataBytes(sizeof(xcb_put_image_request_t));
for (const QRect &rect : region) {
- // We must make sure that each request is not larger than max_req_size.
- // Each request takes req_size + m_xcb_image->stride * height bytes.
- static const uint32_t req_size = sizeof(xcb_put_image_request_t);
- const uint32_t max_req_size = xcb_get_maximum_request_length(xcb_connection());
- const int rows_per_put = (max_req_size - req_size) / m_xcb_image->stride;
+ const quint32 stride = round_up_scanline(rect.width() * m_qimage.depth(), xcb_subimage.scanline_pad) >> 3;
+ const int rows_per_put = maxPutImageRequestDataBytes / stride;
// This assert could trigger if a single row has more pixels than fit in
- // a single PutImage request. However, max_req_size is guaranteed to be
- // at least 16384 bytes. That should be enough for quite large images.
+ // a single PutImage request. In the absence of the BIG-REQUESTS extension
+ // the theoretical maximum lengths of maxPutImageRequestDataBytes can be
+ // roughly 256kB.
Q_ASSERT(rows_per_put > 0);
// If we upload the whole image in a single chunk, the result might be
@@ -674,9 +666,10 @@ void QXcbBackingStoreImage::flushPixmap(const QRegion &region, bool fullRegion)
while (height > 0) {
const int rows = std::min(height, rows_per_put);
const QRect subRect(x, y, width, rows);
- const quint32 stride = round_up_scanline(width * m_qimage.depth(), xcb_subimage.scanline_pad) >> 3;
const QImage subImage = native_sub_image(&m_flushBuffer, stride, m_qimage, subRect, needsByteSwap);
+ Q_ASSERT(static_cast<size_t>(subImage.sizeInBytes()) <= maxPutImageRequestDataBytes);
+
xcb_subimage.width = width;
xcb_subimage.height = rows;
xcb_subimage.data = const_cast<uint8_t *>(subImage.constBits());
@@ -766,7 +759,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);
@@ -837,6 +830,9 @@ void QXcbBackingStore::endPaint()
QImage QXcbBackingStore::toImage() const
{
+ // If the backingstore is rgbSwapped, return the internal image type here.
+ if (!m_rgbImage.isNull())
+ return m_rgbImage;
return m_image && m_image->image() ? *m_image->image() : QImage();
}
@@ -959,16 +955,13 @@ QXcbSystemTrayBackingStore::QXcbSystemTrayBackingStore(QWindow *window)
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;
@@ -981,7 +974,6 @@ QXcbSystemTrayBackingStore::~QXcbSystemTrayBackingStore()
xcb_render_free_picture(xcb_connection(), m_windowPicture);
m_windowPicture = XCB_NONE;
}
-#endif // QT_CONFIG(xcb_render)
}
void QXcbSystemTrayBackingStore::beginPaint(const QRegion &region)
@@ -1003,7 +995,6 @@ void QXcbSystemTrayBackingStore::render(xcb_window_t window, const QRegion &regi
return;
}
-#if QT_CONFIG(xcb_render)
m_image->put(m_xrenderPixmap, region, offset);
const QRect bounds = region.boundingRect();
const QPoint target = bounds.topLeft();
@@ -1014,7 +1005,6 @@ void QXcbSystemTrayBackingStore::render(xcb_window_t window, const QRegion &regi
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)
@@ -1031,7 +1021,6 @@ void QXcbSystemTrayBackingStore::recreateImage(QXcbWindow *win, const QSize &siz
return;
}
-#if QT_CONFIG(xcb_render)
if (m_xrenderPicture) {
xcb_render_free_picture(xcb_connection(), m_xrenderPicture);
m_xrenderPicture = XCB_NONE;
@@ -1054,10 +1043,8 @@ void QXcbSystemTrayBackingStore::recreateImage(QXcbWindow *win, const QSize &siz
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())
@@ -1101,6 +1088,5 @@ void QXcbSystemTrayBackingStore::initXRenderMode()
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 39d023cb9d..0c30929d4e 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.h
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.h
@@ -74,7 +74,7 @@ public:
void beginPaint(const QRegion &) override;
void endPaint() override;
- static bool createSystemVShmSegment(QXcbConnection *c, size_t segmentSize = 1,
+ static bool createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize = 1,
void *shmInfo = nullptr);
protected:
@@ -99,14 +99,13 @@ protected:
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;
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp
index 84831cdbe5..2cb6720d40 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp
@@ -153,114 +153,71 @@ private:
QByteArray format_atoms;
};
-namespace {
-class INCRTransaction;
-typedef QMap<xcb_window_t,INCRTransaction*> TransactionMap;
-static TransactionMap *transactions = 0;
+QXcbClipboardTransaction::QXcbClipboardTransaction(QXcbClipboard *clipboard, xcb_window_t w,
+ xcb_atom_t p, QByteArray d, xcb_atom_t t, int f)
+ : m_clipboard(clipboard), m_window(w), m_property(p), m_data(d), m_target(t), m_format(f)
+{
+ const quint32 values[] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
+ xcb_change_window_attributes(m_clipboard->xcb_connection(), m_window,
+ XCB_CW_EVENT_MASK, values);
-//#define INCR_DEBUG
+ m_abortTimerId = startTimer(m_clipboard->clipboardTimeout());
+}
-class INCRTransaction : public QObject
+QXcbClipboardTransaction::~QXcbClipboardTransaction()
{
- Q_OBJECT
-public:
- INCRTransaction(QXcbConnection *c, xcb_window_t w, xcb_atom_t p,
- QByteArray d, uint i, xcb_atom_t t, int f, int to) :
- conn(c), win(w), property(p), data(d), increment(i),
- target(t), format(f), timeout(to), offset(0)
- {
- const quint32 values[] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
- xcb_change_window_attributes(conn->xcb_connection(), win,
- XCB_CW_EVENT_MASK, values);
- if (!transactions) {
-#ifdef INCR_DEBUG
- qDebug("INCRTransaction: creating the TransactionMap");
-#endif
- transactions = new TransactionMap;
- conn->clipboard()->setProcessIncr(true);
- }
- transactions->insert(win, this);
- abort_timer = startTimer(timeout);
- }
+ if (m_abortTimerId)
+ killTimer(m_abortTimerId);
+ m_abortTimerId = 0;
+ m_clipboard->removeTransaction(m_window);
+}
- ~INCRTransaction()
- {
- if (abort_timer)
- killTimer(abort_timer);
- abort_timer = 0;
- transactions->remove(win);
- if (transactions->isEmpty()) {
-#ifdef INCR_DEBUG
- qDebug("INCRTransaction: no more INCR transactions left in the TransactionMap");
-#endif
- delete transactions;
- transactions = 0;
- conn->clipboard()->setProcessIncr(false);
- }
- }
+bool QXcbClipboardTransaction::updateIncrementalProperty(const xcb_property_notify_event_t *event)
+{
+ if (event->atom != m_property || event->state != XCB_PROPERTY_DELETE)
+ return false;
- void updateIncrProperty(xcb_property_notify_event_t *event, bool &accepted)
- {
- xcb_connection_t *c = conn->xcb_connection();
- if (event->atom == property && event->state == XCB_PROPERTY_DELETE) {
- accepted = true;
- // restart the timer
- if (abort_timer)
- killTimer(abort_timer);
- abort_timer = startTimer(timeout);
-
- unsigned int bytes_left = data.size() - offset;
- if (bytes_left > 0) {
- unsigned int bytes_to_send = qMin(increment, bytes_left);
-#ifdef INCR_DEBUG
- qDebug("INCRTransaction: sending %d bytes, %d remaining (INCR transaction %p)",
- bytes_to_send, bytes_left - bytes_to_send, this);
-#endif
- int dataSize = bytes_to_send / (format / 8);
- xcb_change_property(c, XCB_PROP_MODE_REPLACE, win, property,
- target, format, dataSize, data.constData() + offset);
- offset += bytes_to_send;
- } else {
-#ifdef INCR_DEBUG
- qDebug("INCRTransaction: INCR transaction %p completed", this);
-#endif
- xcb_change_property(c, XCB_PROP_MODE_REPLACE, win, property,
- target, format, 0, (const void *)0);
- const quint32 values[] = { XCB_EVENT_MASK_NO_EVENT };
- xcb_change_window_attributes(conn->xcb_connection(), win,
- XCB_CW_EVENT_MASK, values);
- // self destroy
- delete this;
- }
- }
- }
+ // restart the timer
+ if (m_abortTimerId)
+ killTimer(m_abortTimerId);
+ m_abortTimerId = startTimer(m_clipboard->clipboardTimeout());
-protected:
- void timerEvent(QTimerEvent *ev) override
- {
- if (ev->timerId() == abort_timer) {
- // this can happen when the X client we are sending data
- // to decides to exit (normally or abnormally)
-#ifdef INCR_DEBUG
- qDebug("INCRTransaction: Timed out while sending data to %p", this);
-#endif
- delete this;
- }
+ uint bytes_left = uint(m_data.size()) - m_offset;
+ if (bytes_left > 0) {
+ int increment = m_clipboard->increment();
+ uint bytes_to_send = qMin(uint(increment), bytes_left);
+
+ qCDebug(lcQpaClipboard, "sending %d bytes, %d remaining, transaction: %p)",
+ bytes_to_send, bytes_left - bytes_to_send, this);
+
+ uint32_t dataSize = bytes_to_send / (m_format / 8);
+ xcb_change_property(m_clipboard->xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ m_property, m_target, m_format, dataSize, m_data.constData() + m_offset);
+ m_offset += bytes_to_send;
+ } else {
+ qCDebug(lcQpaClipboard, "transaction %p completed", this);
+
+ xcb_change_property(m_clipboard->xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ m_property, m_target, m_format, 0, nullptr);
+
+ const quint32 values[] = { XCB_EVENT_MASK_NO_EVENT };
+ xcb_change_window_attributes(m_clipboard->xcb_connection(), m_window,
+ XCB_CW_EVENT_MASK, values);
+ delete this; // self destroy
}
+ return true;
+}
-private:
- QXcbConnection *conn;
- xcb_window_t win;
- xcb_atom_t property;
- QByteArray data;
- uint increment;
- xcb_atom_t target;
- int format;
- int timeout;
- uint offset;
- int abort_timer;
-};
-} // unnamed namespace
+
+void QXcbClipboardTransaction::timerEvent(QTimerEvent *ev)
+{
+ if (ev->timerId() == m_abortTimerId) {
+ // this can happen when the X client we are sending data
+ // to decides to exit (normally or abnormally)
+ qCDebug(lcQpaClipboard, "timed out while sending data to %p", this);
+ delete this; // self destroy
+ }
+}
const int QXcbClipboard::clipboard_timeout = 5000;
@@ -282,6 +239,9 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c)
xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, XCB_ATOM_PRIMARY, mask);
xcb_xfixes_select_selection_input_checked(xcb_connection(), m_owner, atom(QXcbAtom::CLIPBOARD), mask);
}
+
+ // xcb_change_property_request_t and xcb_get_property_request_t are the same size
+ m_maxPropertyRequestDataBytes = connection()->maxRequestDataBytes(sizeof(xcb_change_property_request_t));
}
QXcbClipboard::~QXcbClipboard()
@@ -301,7 +261,9 @@ QXcbClipboard::~QXcbClipboard()
connection()->sync();
// waiting until the clipboard manager fetches the content.
- if (!waitForClipboardEvent(m_owner, XCB_SELECTION_NOTIFY, true)) {
+ if (auto event = waitForClipboardEvent(m_owner, XCB_SELECTION_NOTIFY, true)) {
+ free(event);
+ } else {
qWarning("QXcbClipboard: Unable to receive an event from the "
"clipboard manager in a reasonable time");
}
@@ -313,16 +275,17 @@ QXcbClipboard::~QXcbClipboard()
delete m_clientClipboard[QClipboard::Selection];
}
-void QXcbClipboard::incrTransactionPeeker(xcb_generic_event_t *ge, bool &accepted)
+bool QXcbClipboard::handlePropertyNotify(const xcb_generic_event_t *event)
{
- uint response_type = ge->response_type & ~0x80;
- if (response_type == XCB_PROPERTY_NOTIFY) {
- xcb_property_notify_event_t *event = (xcb_property_notify_event_t *)ge;
- TransactionMap::Iterator it = transactions->find(event->window);
- if (it != transactions->end()) {
- (*it)->updateIncrProperty(event, accepted);
- }
- }
+ if (m_transactions.isEmpty() || event->response_type != XCB_PROPERTY_NOTIFY)
+ return false;
+
+ auto propertyNotify = reinterpret_cast<const xcb_property_notify_event_t *>(event);
+ TransactionMap::Iterator it = m_transactions.find(propertyNotify->window);
+ if (it == m_transactions.constEnd())
+ return false;
+
+ return (*it)->updateIncrementalProperty(propertyNotify);
}
xcb_window_t QXcbClipboard::getSelectionOwner(xcb_atom_t atom) const
@@ -522,19 +485,18 @@ xcb_atom_t QXcbClipboard::sendSelection(QMimeData *d, xcb_atom_t target, xcb_win
// This 'bool' can be removed once there is a proper fix for QTBUG-32853
if (m_clipboard_closing)
allow_incr = false;
- // X_ChangeProperty protocol request is 24 bytes
- const int increment = (xcb_get_maximum_request_length(xcb_connection()) * 4) - 24;
- if (data.size() > increment && allow_incr) {
+
+ if (data.size() > m_maxPropertyRequestDataBytes && allow_incr) {
long bytes = data.size();
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, property,
atom(QXcbAtom::INCR), 32, 1, (const void *)&bytes);
- new INCRTransaction(connection(), window, property, data, increment,
- atomFormat, dataFormat, clipboard_timeout);
+ auto transaction = new QXcbClipboardTransaction(this, window, property, data, atomFormat, dataFormat);
+ m_transactions.insert(window, transaction);
return property;
}
// make sure we can perform the XChangeProperty in a single request
- if (data.size() > increment)
+ if (data.size() > m_maxPropertyRequestDataBytes)
return XCB_NONE; // ### perhaps use several XChangeProperty calls w/ PropModeAppend?
int dataSize = data.size() / (dataFormat / 8);
// use a single request to transfer data
@@ -716,17 +678,8 @@ void QXcbClipboard::handleXFixesSelectionRequest(xcb_xfixes_selection_notify_eve
emitChanged(mode);
}
-
-static inline int maxSelectionIncr(xcb_connection_t *c)
-{
- int l = xcb_get_maximum_request_length(c);
- return (l > 65536 ? 65536*4 : l*4) - 100;
-}
-
bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, bool deleteProperty, QByteArray *buffer, int *size, xcb_atom_t *type, int *format)
{
- int maxsize = maxSelectionIncr(xcb_connection());
- ulong bytes_left; // bytes_after
xcb_atom_t dummy_type;
int dummy_format;
@@ -743,7 +696,8 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
}
*type = reply->type;
*format = reply->format;
- bytes_left = reply->bytes_after;
+
+ auto bytes_left = reply->bytes_after;
int offset = 0, buffer_offset = 0;
@@ -758,7 +712,8 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property,
while (bytes_left) {
// more to read...
- reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, win, property, XCB_GET_PROPERTY_TYPE_ANY, offset, maxsize/4);
+ reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, win, property,
+ XCB_GET_PROPERTY_TYPE_ANY, offset, m_maxPropertyRequestDataBytes / 4);
if (!reply || reply->type == XCB_NONE)
break;
@@ -807,18 +762,18 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t window, i
{
QElapsedTimer timer;
timer.start();
+ QXcbEventQueue *queue = connection()->eventQueue();
do {
- auto e = connection()->checkEvent([window, type](xcb_generic_event_t *event, int eventType) {
+ auto e = queue->peek([window, type](xcb_generic_event_t *event, int eventType) {
if (eventType != type)
return false;
if (eventType == XCB_PROPERTY_NOTIFY) {
auto propertyNotify = reinterpret_cast<xcb_property_notify_event_t *>(event);
- if (propertyNotify->window == window)
- return true;
- } else if (eventType == XCB_SELECTION_NOTIFY) {
+ return propertyNotify->window == window;
+ }
+ if (eventType == XCB_SELECTION_NOTIFY) {
auto selectionNotify = reinterpret_cast<xcb_selection_notify_event_t *>(event);
- if (selectionNotify->requestor == window)
- return true;
+ return selectionNotify->requestor == window;
}
return false;
});
@@ -833,7 +788,7 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t window, i
// process other clipboard events, since someone is probably requesting data from us
auto clipboardAtom = atom(QXcbAtom::CLIPBOARD);
- e = connection()->checkEvent([clipboardAtom](xcb_generic_event_t *event, int type) {
+ 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;
@@ -848,8 +803,9 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t window, i
connection()->flush();
- // sleep 50 ms, so we don't use up CPU cycles all the time.
- QThread::msleep(50);
+ const auto elapsed = timer.elapsed();
+ if (elapsed < clipboard_timeout)
+ queue->waitForNewEvents(clipboard_timeout - elapsed);
} while (timer.elapsed() < clipboard_timeout);
return nullptr;
@@ -878,6 +834,7 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb
if (!ge)
break;
xcb_property_notify_event_t *event = (xcb_property_notify_event_t *)ge;
+ QScopedPointer<xcb_property_notify_event_t, QScopedPointerPodDeleter> guard(event);
if (event->atom != property
|| event->state != XCB_PROPERTY_NEW_VALUE
@@ -909,8 +866,6 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb
} else {
break;
}
-
- free(ge);
}
// timed out ... create a new requestor window, otherwise the requestor
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.h b/src/plugins/platforms/xcb/qxcbclipboard.h
index abab42a613..51ae0dc1ee 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.h
+++ b/src/plugins/platforms/xcb/qxcbclipboard.h
@@ -45,14 +45,41 @@
#include <xcb/xcb.h>
#include <xcb/xfixes.h>
+#include <QtCore/QObject>
+
QT_BEGIN_NAMESPACE
#ifndef QT_NO_CLIPBOARD
class QXcbConnection;
class QXcbScreen;
+class QXcbClipboard;
class QXcbClipboardMime;
+class QXcbClipboardTransaction : public QObject
+{
+ Q_OBJECT
+public:
+ QXcbClipboardTransaction(QXcbClipboard *clipboard, xcb_window_t w, xcb_atom_t p,
+ QByteArray d, xcb_atom_t t, int f);
+ ~QXcbClipboardTransaction();
+
+ bool updateIncrementalProperty(const xcb_property_notify_event_t *event);
+
+protected:
+ void timerEvent(QTimerEvent *ev) override;
+
+private:
+ QXcbClipboard *m_clipboard;
+ xcb_window_t m_window;
+ xcb_atom_t m_property;
+ QByteArray m_data;
+ xcb_atom_t m_target;
+ uint8_t m_format;
+ uint m_offset = 0;
+ int m_abortTimerId = 0;
+};
+
class QXcbClipboard : public QXcbObject, public QPlatformClipboard
{
public:
@@ -81,13 +108,16 @@ public:
QByteArray getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtatom);
- void setProcessIncr(bool process) { m_incr_active = process; }
- bool processIncr() { return m_incr_active; }
- void incrTransactionPeeker(xcb_generic_event_t *ge, bool &accepted);
+ bool handlePropertyNotify(const xcb_generic_event_t *event);
xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
QByteArray getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t t = 0);
+ int increment() const { return m_maxPropertyRequestDataBytes; }
+ int clipboardTimeout() const { return clipboard_timeout; }
+
+ void removeTransaction(xcb_window_t window) { m_transactions.remove(window); }
+
private:
xcb_generic_event_t *waitForClipboardEvent(xcb_window_t window, int type, bool checkManager = false);
@@ -107,9 +137,12 @@ private:
static const int clipboard_timeout;
- bool m_incr_active = false;
+ int m_maxPropertyRequestDataBytes = 0;
bool m_clipboard_closing = false;
xcb_timestamp_t m_incr_receive_time = 0;
+
+ using TransactionMap = QMap<xcb_window_t, QXcbClipboardTransaction *>;
+ TransactionMap m_transactions;
};
#endif // QT_NO_CLIPBOARD
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 4e24c970b4..d27a288feb 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -38,12 +38,11 @@
****************************************************************************/
#include <QtGui/private/qguiapplication_p.h>
-#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtCore/QDebug>
+#include <QtCore/QCoreApplication>
#include "qxcbconnection.h"
#include "qxcbkeyboard.h"
-#include "qxcbscreen.h"
#include "qxcbwindow.h"
#include "qxcbclipboard.h"
#if QT_CONFIG(draganddrop)
@@ -57,39 +56,25 @@
#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
+#if QT_CONFIG(xkb)
+#define explicit dont_use_cxx_explicit
+#include <xcb/xkb.h>
+#undef explicit
#endif
-
#if QT_CONFIG(xcb_xinput)
#include <xcb/xinput.h>
#endif
-#if QT_CONFIG(xcb_render)
-#include <xcb/render.h>
-#endif
-
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaXInput, "qt.qpa.input")
@@ -97,9 +82,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(lcQpaClipboard, "qt.qpa.clipboard")
Q_LOGGING_CATEGORY(lcQpaXDnd, "qt.qpa.xdnd")
// this event type was added in libxcb 1.10,
@@ -108,519 +94,30 @@ Q_LOGGING_CATEGORY(lcQpaXDnd, "qt.qpa.xdnd")
#define XCB_GE_GENERIC 35
#endif
-#if QT_CONFIG(xcb_xinput)
-// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed:
-// - "pad0" became "extension"
-// - "pad1" and "pad" became "pad0"
-// New and old version of this struct share the following fields:
-typedef struct qt_xcb_ge_event_t {
- uint8_t response_type;
- uint8_t extension;
- uint16_t sequence;
- uint32_t length;
- uint16_t event_type;
-} qt_xcb_ge_event_t;
-
-static inline bool isXIEvent(xcb_generic_event_t *event, int opCode)
-{
- qt_xcb_ge_event_t *e = reinterpret_cast<qt_xcb_ge_event_t *>(event);
- return e->extension == opCode;
-}
-#endif // QT_CONFIG(xcb_xinput)
-
-#if QT_CONFIG(xcb_xlib)
-static const char * const xcbConnectionErrors[] = {
- "No error", /* Error 0 */
- "I/O error", /* XCB_CONN_ERROR */
- "Unsupported extension used", /* XCB_CONN_CLOSED_EXT_NOTSUPPORTED */
- "Out of memory", /* XCB_CONN_CLOSED_MEM_INSUFFICIENT */
- "Maximum allowed requested length exceeded", /* XCB_CONN_CLOSED_REQ_LEN_EXCEED */
- "Failed to parse display string", /* XCB_CONN_CLOSED_PARSE_ERR */
- "No such screen on display", /* XCB_CONN_CLOSED_INVALID_SCREEN */
- "Error during FD passing" /* XCB_CONN_CLOSED_FDPASSING_FAILED */
-};
-
-static int nullErrorHandler(Display *dpy, XErrorEvent *err)
-{
-#ifndef Q_XCB_DEBUG
- Q_UNUSED(dpy);
- Q_UNUSED(err);
-#else
- const int buflen = 1024;
- char buf[buflen];
-
- XGetErrorText(dpy, err->error_code, buf, buflen);
- fprintf(stderr, "X Error: serial %lu error %d %s\n", err->serial, (int) err->error_code, buf);
-#endif
- return 0;
-}
-
-static int ioErrorHandler(Display *dpy)
-{
- 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::create(QXcbNativeInterface *nativeInterface, bool canGrabServer,
- xcb_visualid_t defaultVisualId,
- const char *displayNameIn)
-{
- const QByteArray displayName = displayNameIn ? QByteArray(displayNameIn) : qgetenv("DISPLAY");
- int primaryScreenNumber = 0;
- void *xlibDisplay = nullptr;
- xcb_connection_t *connection = nullptr;
-#if QT_CONFIG(xcb_xlib)
- Display *dpy = XOpenDisplay(displayName.constData());
- if (dpy) {
- primaryScreenNumber = DefaultScreen(dpy);
- connection = XGetXCBConnection(dpy);
- XSetEventQueueOwner(dpy, XCBOwnsEventQueue);
- XSetErrorHandler(nullErrorHandler);
- XSetIOErrorHandler(ioErrorHandler);
- xlibDisplay = dpy;
- }
-#else
- connection = xcb_connect(displayName.constData(), &primaryScreenNumber);
-#endif // QT_CONFIG(xcb_xlib)
- if (Q_UNLIKELY(connection == nullptr)) {
- qWarning("QXcbConnection: Could not connect to display \"%s\"", displayName.constData());
- return nullptr;
- }
- if (Q_UNLIKELY(xcb_connection_has_error(connection))) {
-#if QT_CONFIG(xcb_xlib)
- XCloseDisplay(static_cast<Display *>(xlibDisplay));
-#else
- xcb_disconnect(connection);
-#endif
- qWarning("QXcbConnection: Errors occurred connecting to display \"%s\"", displayName.constData());
- return nullptr;
- }
- return new QXcbConnection(connection, primaryScreenNumber, nativeInterface,
- canGrabServer, defaultVisualId, displayName, xlibDisplay);
-}
-
-
-QXcbConnection::QXcbConnection(xcb_connection_t *c, int primaryScreenNumber,
- QXcbNativeInterface *nativeInterface, bool canGrabServer,
- xcb_visualid_t defaultVisualId, const QByteArray &displayName,
- void *xlibDisplay)
- : m_connection(c)
+QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName)
+ : QXcbBasicConnection(displayName)
, m_canGrabServer(canGrabServer)
, m_defaultVisualId(defaultVisualId)
- , m_primaryScreenNumber(primaryScreenNumber)
- , m_displayName(displayName)
, m_nativeInterface(nativeInterface)
-#if QT_CONFIG(xcb_xlib)
- , m_xlib_display(xlibDisplay)
-#endif
{
- 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
-#if QT_CONFIG(xcb_xinput)
- &xcb_input_id,
-#endif
- 0
- };
-
- for (xcb_extension_t **ext_it = extensions; *ext_it; ++ext_it)
- xcb_prefetch_extension_data (m_connection, *ext_it);
+ if (!isConnected())
+ return;
- m_setup = xcb_get_setup(xcb_connection());
+ m_eventQueue = new QXcbEventQueue(this);
m_xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP").toLower();
- initializeAllAtoms();
+ if (hasXRandr())
+ xrandrSelectEvents();
- initializeXSync();
- if (!qEnvironmentVariableIsSet("QT_XCB_NO_MITSHM"))
- initializeShm();
- if (!qEnvironmentVariableIsSet("QT_XCB_NO_XRANDR"))
- initializeXRandr();
- if (!has_randr_extension)
- initializeXinerama();
- initializeXFixes();
initializeScreens();
- initializeXRender();
#if QT_CONFIG(xcb_xinput)
- if (!qEnvironmentVariableIsSet("QT_XCB_NO_XI2"))
- initializeXInput2();
+ if (hasXInput2()) {
+ xi2SetupDevices();
+ xi2SelectStateEvents();
+ }
#endif
- initializeXShape();
- initializeXKB();
m_wmSupport.reset(new QXcbWMSupport(this));
m_keyboard = new QXcbKeyboard(this);
@@ -635,6 +132,12 @@ QXcbConnection::QXcbConnection(xcb_connection_t *c, int primaryScreenNumber,
if (!m_startupId.isNull())
qunsetenv("DESKTOP_STARTUP_ID");
+ m_focusInTimer.setSingleShot(true);
+ m_focusInTimer.callOnTimeout([]() {
+ // No FocusIn events for us, proceed with FocusOut normally.
+ QWindowSystemInterface::handleWindowActivated(nullptr, Qt::ActiveWindowFocusReason);
+ });
+
sync();
}
@@ -646,39 +149,21 @@ 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
while (!m_screens.isEmpty())
- integration->destroyScreen(m_screens.takeLast());
+ QWindowSystemInterface::handleScreenRemoved(m_screens.takeLast());
while (!m_virtualDesktops.isEmpty())
delete m_virtualDesktops.takeLast();
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()) {
@@ -783,17 +268,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
@@ -953,7 +438,11 @@ const char *xcb_protocol_request_codes[] =
void QXcbConnection::handleXcbError(xcb_generic_error_t *error)
{
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ qintptr result = 0;
+#else
long result = 0;
+#endif
QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
if (dispatcher && dispatcher->filterNativeEvent(m_nativeInterface->nativeEventType(), error, &result))
return;
@@ -1050,12 +539,15 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
if (Q_UNLIKELY(lcQpaEvents().isDebugEnabled()))
printXcbEvent(lcQpaEvents(), "Event", event);
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ qintptr result = 0; // Used only by MS Windows
+#else
long result = 0; // Used only by MS Windows
- QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance();
- bool handledByNativeEventFilter = dispatcher && dispatcher->filterNativeEvent(
- m_nativeInterface->nativeEventType(), event, &result);
- if (handledByNativeEventFilter)
- return;
+#endif
+ if (QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance()) {
+ if (dispatcher->filterNativeEvent(m_nativeInterface->nativeEventType(), event, &result))
+ return;
+ }
uint response_type = event->response_type & ~0x80;
@@ -1174,6 +666,10 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
break;
case XCB_PROPERTY_NOTIFY:
{
+#ifndef QT_NO_CLIPBOARD
+ if (m_clipboard->handlePropertyNotify(event))
+ break;
+#endif
auto propertyNotify = reinterpret_cast<xcb_property_notify_event_t *>(event);
if (propertyNotify->atom == atom(QXcbAtom::_NET_WORKAREA)) {
QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(propertyNotify->window);
@@ -1187,7 +683,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
#if QT_CONFIG(xcb_xinput)
case XCB_GE_GENERIC:
// Here the windowEventListener is invoked from xi2HandleEvent()
- if (hasXInput2() && isXIEvent(event, m_xiOpCode))
+ if (hasXInput2() && isXIEvent(event))
xi2HandleEvent(reinterpret_cast<xcb_ge_event_t *>(event));
break;
#endif
@@ -1200,7 +696,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
return;
handled = true;
- if (has_xfixes && response_type == xfixes_first_event + XCB_XFIXES_SELECTION_NOTIFY) {
+ if (isXFixesType(response_type, XCB_XFIXES_SELECTION_NOTIFY)) {
auto notify_event = reinterpret_cast<xcb_xfixes_selection_notify_event_t *>(event);
setTime(notify_event->timestamp);
#ifndef QT_NO_CLIPBOARD
@@ -1208,14 +704,14 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
#endif
for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops))
virtualDesktop->handleXFixesSelectionNotify(notify_event);
- } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_NOTIFY) {
+ } else if (isXRandrType(response_type, XCB_RANDR_NOTIFY)) {
updateScreens(reinterpret_cast<xcb_randr_notify_event_t *>(event));
- } else if (has_randr_extension && response_type == xrandr_first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY) {
+ } else if (isXRandrType(response_type, XCB_RANDR_SCREEN_CHANGE_NOTIFY)) {
auto change_event = reinterpret_cast<xcb_randr_screen_change_notify_event_t *>(event);
if (auto virtualDesktop = virtualDesktopForRootWindow(change_event->root))
virtualDesktop->handleScreenChange(change_event);
#if QT_CONFIG(xkb)
- } else if (response_type == xkb_first_event) { // https://bugs.freedesktop.org/show_bug.cgi?id=51295
+ } else if (isXkbType(response_type)) {
auto xkb_event = reinterpret_cast<_xkb_event *>(event);
if (xkb_event->any.deviceID == m_keyboard->coreDeviceId()) {
switch (xkb_event->any.xkbType) {
@@ -1249,156 +745,6 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
m_glIntegration->handleXcbEvent(event, response_type);
}
-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)
-{
-}
-
-void QXcbEventReader::start()
-{
- connect(this, &QXcbEventReader::eventPending, m_connection, &QXcbConnection::processXcbEvents, Qt::QueuedConnection);
- connect(this, &QXcbEventReader::finished, m_connection, &QXcbConnection::processXcbEvents);
- QThread::start();
-}
-
-void QXcbEventReader::registerEventDispatcher(QAbstractEventDispatcher *dispatcher)
-{
- // Flush the xcb connection before the event dispatcher is going to block.
- connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, m_connection, &QXcbConnection::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 = xcb_poll_for_queued_event(m_connection->xcb_connection())))
- addEvent(event);
- m_mutex.unlock();
- emit eventPending();
- }
-
- 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;
-}
-
-QXcbEventArray *QXcbEventReader::lock()
-{
- m_mutex.lock();
- return &m_events;
-}
-
-void QXcbEventReader::unlock()
-{
- m_mutex.unlock();
-}
-
void QXcbConnection::setFocusWindow(QWindow *w)
{
m_focusWindow = w ? static_cast<QXcbWindow *>(w->handle()) : nullptr;
@@ -1416,38 +762,13 @@ void QXcbConnection::setMousePressWindow(QXcbWindow *w)
void QXcbConnection::grabServer()
{
if (m_canGrabServer)
- xcb_grab_server(m_connection);
+ xcb_grab_server(xcb_connection());
}
void QXcbConnection::ungrabServer()
{
if (m_canGrabServer)
- xcb_ungrab_server(m_connection);
-}
-
-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());
+ xcb_ungrab_server(xcb_connection());
}
xcb_timestamp_t QXcbConnection::getTimestamp()
@@ -1461,12 +782,10 @@ xcb_timestamp_t QXcbConnection::getTimestamp()
connection()->flush();
xcb_generic_event_t *event = nullptr;
- // 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
+
while (!event) {
connection()->sync();
- event = checkEvent([window, dummyAtom](xcb_generic_event_t *event, int type) {
+ 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);
@@ -1507,7 +826,7 @@ xcb_window_t QXcbConnection::getQtSelectionOwner()
0); // value list
QXcbWindow::setWindowTitle(connection(), m_qtSelectionOwner,
- QStringLiteral("Qt Selection Window"));
+ QLatin1String("Qt Selection Owner for ") + QCoreApplication::applicationName());
}
return m_qtSelectionOwner;
}
@@ -1564,21 +883,6 @@ xcb_window_t QXcbConnection::clientLeader()
return m_clientLeader;
}
-#if QT_CONFIG(xcb_xinput)
-static inline bool isXIType(xcb_generic_event_t *event, int opCode, uint16_t type)
-{
- if (!isXIEvent(event, opCode))
- return false;
-
- auto *e = reinterpret_cast<qt_xcb_ge_event_t *>(event);
- return e->event_type == type;
-}
-#endif
-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.
@@ -1591,22 +895,21 @@ 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(xcb_xinput)
// compress XI_* events
if (responseType == XCB_GE_GENERIC) {
@@ -1614,58 +917,93 @@ bool QXcbConnection::compressEvent(xcb_generic_event_t *event, int currentIndex,
return false;
// compress XI_Motion
- if (isXIType(event, m_xiOpCode, XCB_INPUT_MOTION)) {
+ if (isXIType(event, XCB_INPUT_MOTION)) {
#if QT_CONFIG(tabletevent)
- auto *xdev = reinterpret_cast<xcb_input_motion_event_t *>(event);
+ auto xdev = reinterpret_cast<xcb_input_motion_event_t *>(event);
if (!QCoreApplication::testAttribute(Qt::AA_CompressTabletEvents) &&
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, XCB_INPUT_MOTION))
- return true;
- }
- return false;
+ return m_eventQueue->peek(QXcbEventQueue::PeekRetainMatch,
+ [this](xcb_generic_event_t *next, int) {
+ return isXIType(next, XCB_INPUT_MOTION);
+ });
}
+
// compress XI_TouchUpdate for the same touch point id
- if (isXIType(event, m_xiOpCode, XCB_INPUT_TOUCH_UPDATE)) {
- auto *touchUpdateEvent = reinterpret_cast<xcb_input_touch_update_event_t *>(event);
+ 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;
- 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, XCB_INPUT_TOUCH_UPDATE)) {
- auto *touchUpdateNextEvent = reinterpret_cast<xcb_input_touch_update_event_t *>(next);
- if (id == touchUpdateNextEvent->detail % INT_MAX)
- return true;
- }
- }
- return false;
+
+ 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;
+ });
}
+
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) {
@@ -1673,319 +1011,34 @@ 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))
- continue;
-
-#ifndef QT_NO_CLIPBOARD
- bool accepted = false;
- if (clipboard()->processIncr())
- clipboard()->incrTransactionPeeker(event, accepted);
- if (accepted)
+ if (compressEvent(event))
continue;
-#endif
- auto isWaitingFor = [=](PeekFunc peekFunc) {
- // These callbacks return true if the event is what they were
- // waiting for, remove them from the list in that case.
- return peekFunc(this, event);
- };
- 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();
-
- m_peekerIndexCacheDirty = m_mainEventLoopFlushedQueue = true;
-
- // 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);
- m_peekFuncs.clear();
-
- xcb_flush(xcb_connection());
-}
-
-static const char * xcb_atomnames = {
- // window-manager <-> client protocols
- "WM_PROTOCOLS\0"
- "WM_DELETE_WINDOW\0"
- "WM_TAKE_FOCUS\0"
- "_NET_WM_PING\0"
- "_NET_WM_CONTEXT_HELP\0"
- "_NET_WM_SYNC_REQUEST\0"
- "_NET_WM_SYNC_REQUEST_COUNTER\0"
- "MANAGER\0"
- "_NET_SYSTEM_TRAY_OPCODE\0"
-
- // ICCCM window state
- "WM_STATE\0"
- "WM_CHANGE_STATE\0"
- "WM_CLASS\0"
- "WM_NAME\0"
-
- // Session management
- "WM_CLIENT_LEADER\0"
- "WM_WINDOW_ROLE\0"
- "SM_CLIENT_ID\0"
- "WM_CLIENT_MACHINE\0"
-
- // Clipboard
- "CLIPBOARD\0"
- "INCR\0"
- "TARGETS\0"
- "MULTIPLE\0"
- "TIMESTAMP\0"
- "SAVE_TARGETS\0"
- "CLIP_TEMPORARY\0"
- "_QT_SELECTION\0"
- "_QT_CLIPBOARD_SENTINEL\0"
- "_QT_SELECTION_SENTINEL\0"
- "CLIPBOARD_MANAGER\0"
-
- "RESOURCE_MANAGER\0"
-
- "_XSETROOT_ID\0"
-
- "_QT_SCROLL_DONE\0"
- "_QT_INPUT_ENCODING\0"
-
- "_QT_CLOSE_CONNECTION\0"
-
- "_MOTIF_WM_HINTS\0"
-
- "DTWM_IS_RUNNING\0"
- "ENLIGHTENMENT_DESKTOP\0"
- "_DT_SAVE_MODE\0"
- "_SGI_DESKS_MANAGER\0"
-
- // EWMH (aka NETWM)
- "_NET_SUPPORTED\0"
- "_NET_VIRTUAL_ROOTS\0"
- "_NET_WORKAREA\0"
-
- "_NET_MOVERESIZE_WINDOW\0"
- "_NET_WM_MOVERESIZE\0"
-
- "_NET_WM_NAME\0"
- "_NET_WM_ICON_NAME\0"
- "_NET_WM_ICON\0"
-
- "_NET_WM_PID\0"
-
- "_NET_WM_WINDOW_OPACITY\0"
-
- "_NET_WM_STATE\0"
- "_NET_WM_STATE_ABOVE\0"
- "_NET_WM_STATE_BELOW\0"
- "_NET_WM_STATE_FULLSCREEN\0"
- "_NET_WM_STATE_MAXIMIZED_HORZ\0"
- "_NET_WM_STATE_MAXIMIZED_VERT\0"
- "_NET_WM_STATE_MODAL\0"
- "_NET_WM_STATE_STAYS_ON_TOP\0"
- "_NET_WM_STATE_DEMANDS_ATTENTION\0"
-
- "_NET_WM_USER_TIME\0"
- "_NET_WM_USER_TIME_WINDOW\0"
- "_NET_WM_FULL_PLACEMENT\0"
-
- "_NET_WM_WINDOW_TYPE\0"
- "_NET_WM_WINDOW_TYPE_DESKTOP\0"
- "_NET_WM_WINDOW_TYPE_DOCK\0"
- "_NET_WM_WINDOW_TYPE_TOOLBAR\0"
- "_NET_WM_WINDOW_TYPE_MENU\0"
- "_NET_WM_WINDOW_TYPE_UTILITY\0"
- "_NET_WM_WINDOW_TYPE_SPLASH\0"
- "_NET_WM_WINDOW_TYPE_DIALOG\0"
- "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0"
- "_NET_WM_WINDOW_TYPE_POPUP_MENU\0"
- "_NET_WM_WINDOW_TYPE_TOOLTIP\0"
- "_NET_WM_WINDOW_TYPE_NOTIFICATION\0"
- "_NET_WM_WINDOW_TYPE_COMBO\0"
- "_NET_WM_WINDOW_TYPE_DND\0"
- "_NET_WM_WINDOW_TYPE_NORMAL\0"
- "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0"
-
- "_KDE_NET_WM_FRAME_STRUT\0"
- "_NET_FRAME_EXTENTS\0"
-
- "_NET_STARTUP_INFO\0"
- "_NET_STARTUP_INFO_BEGIN\0"
-
- "_NET_SUPPORTING_WM_CHECK\0"
-
- "_NET_WM_CM_S0\0"
-
- "_NET_SYSTEM_TRAY_VISUAL\0"
-
- "_NET_ACTIVE_WINDOW\0"
-
- // Property formats
- "TEXT\0"
- "UTF8_STRING\0"
- "CARDINAL\0"
-
- // xdnd
- "XdndEnter\0"
- "XdndPosition\0"
- "XdndStatus\0"
- "XdndLeave\0"
- "XdndDrop\0"
- "XdndFinished\0"
- "XdndTypeList\0"
- "XdndActionList\0"
-
- "XdndSelection\0"
-
- "XdndAware\0"
- "XdndProxy\0"
-
- "XdndActionCopy\0"
- "XdndActionLink\0"
- "XdndActionMove\0"
- "XdndActionPrivate\0"
-
- // Motif DND
- "_MOTIF_DRAG_AND_DROP_MESSAGE\0"
- "_MOTIF_DRAG_INITIATOR_INFO\0"
- "_MOTIF_DRAG_RECEIVER_INFO\0"
- "_MOTIF_DRAG_WINDOW\0"
- "_MOTIF_DRAG_TARGETS\0"
-
- "XmTRANSFER_SUCCESS\0"
- "XmTRANSFER_FAILURE\0"
-
- // Xkb
- "_XKB_RULES_NAMES\0"
-
- // XEMBED
- "_XEMBED\0"
- "_XEMBED_INFO\0"
-
- // XInput2
- "Button Left\0"
- "Button Middle\0"
- "Button Right\0"
- "Button Wheel Up\0"
- "Button Wheel Down\0"
- "Button Horiz Wheel Left\0"
- "Button Horiz Wheel Right\0"
- "Abs MT Position X\0"
- "Abs MT Position Y\0"
- "Abs MT Touch Major\0"
- "Abs MT Touch Minor\0"
- "Abs MT Orientation\0"
- "Abs MT Pressure\0"
- "Abs MT Tracking ID\0"
- "Max Contacts\0"
- "Rel X\0"
- "Rel Y\0"
- // XInput2 tablet
- "Abs X\0"
- "Abs Y\0"
- "Abs Pressure\0"
- "Abs Tilt X\0"
- "Abs Tilt Y\0"
- "Abs Wheel\0"
- "Abs Distance\0"
- "Wacom Serial IDs\0"
- "INTEGER\0"
- "Rel Horiz Wheel\0"
- "Rel Vert Wheel\0"
- "Rel Horiz Scroll\0"
- "Rel Vert Scroll\0"
- "_XSETTINGS_SETTINGS\0"
- "_COMPIZ_DECOR_PENDING\0"
- "_COMPIZ_DECOR_REQUEST\0"
- "_COMPIZ_DECOR_DELETE_PIXMAP\0"
- "_COMPIZ_TOOLKIT_ACTION\0"
- "_GTK_LOAD_ICONTHEMES\0"
- "AT_SPI_BUS\0"
- "EDID\0"
- "EDID_DATA\0"
- "XFree86_DDC_EDID1_RAWDATA\0"
- // \0\0 terminates loop.
-};
-
-QXcbAtom::Atom QXcbConnection::qatom(xcb_atom_t xatom) const
-{
- return static_cast<QXcbAtom::Atom>(std::find(m_allAtoms, m_allAtoms + QXcbAtom::NAtoms, xatom) - m_allAtoms);
-}
-
-void QXcbConnection::initializeAllAtoms() {
- const char *names[QXcbAtom::NAtoms];
- const char *ptr = xcb_atomnames;
-
- int i = 0;
- while (*ptr) {
- names[i++] = ptr;
- while (*ptr)
- ++ptr;
- ++ptr;
- }
-
- Q_ASSERT(i == QXcbAtom::NAtoms);
-
- xcb_intern_atom_cookie_t cookies[QXcbAtom::NAtoms];
- Q_ASSERT(i == QXcbAtom::NAtoms);
- for (i = 0; i < QXcbAtom::NAtoms; ++i)
- cookies[i] = xcb_intern_atom(xcb_connection(), false, strlen(names[i]), names[i]);
-
- for (i = 0; i < QXcbAtom::NAtoms; ++i) {
- xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0);
- m_allAtoms[i] = reply->atom;
- free(reply);
+ // 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();
}
-}
-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();
+ xcb_flush(xcb_connection());
}
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;
@@ -2005,208 +1058,6 @@ void QXcbConnection::sync()
free(xcb_get_input_focus_reply(xcb_connection(), cookie, 0));
}
-void QXcbConnection::initializeShm()
-{
- const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_shm_id);
- if (!reply || !reply->present) {
- qCDebug(lcQpaXcb, "MIT-SHM extension is not present on the X server");
- return;
- }
- has_shm = true;
-
- auto shm_query = Q_XCB_REPLY(xcb_shm_query_version, m_connection);
- if (shm_query) {
- has_shm_fd = (shm_query->major_version == 1 && shm_query->minor_version >= 2) ||
- shm_query->major_version > 1;
- } else {
- qCWarning(lcQpaXcb, "QXcbConnection: Failed to request MIT-SHM version");
- }
-
- qCDebug(lcQpaXcb) << "Has MIT-SHM :" << has_shm;
- qCDebug(lcQpaXcb) << "Has MIT-SHM FD :" << has_shm_fd;
-
- // Temporary disable warnings (unless running in debug mode).
- auto logging = const_cast<QLoggingCategory*>(&lcQpaXcb());
- bool wasEnabled = logging->isEnabled(QtMsgType::QtWarningMsg);
- if (!logging->isEnabled(QtMsgType::QtDebugMsg))
- logging->setEnabled(QtMsgType::QtWarningMsg, false);
- if (!QXcbBackingStore::createSystemVShmSegment(this)) {
- qCDebug(lcQpaXcb, "failed to create System V shared memory segment (remote "
- "X11 connection?), disabling SHM");
- has_shm = has_shm_fd = false;
- }
- if (wasEnabled)
- logging->setEnabled(QtMsgType::QtWarningMsg, true);
-}
-
-void QXcbConnection::initializeXFixes()
-{
- const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xfixes_id);
- if (!reply || !reply->present)
- return;
-
- auto xfixes_query = Q_XCB_REPLY(xcb_xfixes_query_version, m_connection,
- XCB_XFIXES_MAJOR_VERSION,
- XCB_XFIXES_MINOR_VERSION);
- if (!xfixes_query || xfixes_query->major_version < 2) {
- qWarning("QXcbConnection: Failed to initialize XFixes");
- return;
- }
- xfixes_first_event = reply->first_event;
- has_xfixes = true;
-}
-
-void QXcbConnection::initializeXRender()
-{
-#if QT_CONFIG(xcb_render)
- const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_render_id);
- if (!reply || !reply->present) {
- qCDebug(lcQpaXcb, "XRender extension not present on the X server");
- return;
- }
-
- auto xrender_query = Q_XCB_REPLY(xcb_render_query_version, m_connection,
- XCB_RENDER_MAJOR_VERSION,
- XCB_RENDER_MINOR_VERSION);
- if (!xrender_query) {
- qCWarning(lcQpaXcb, "xcb_render_query_version failed");
- return;
- }
-
- has_render_extension = true;
- m_xrenderVersion.first = xrender_query->major_version;
- m_xrenderVersion.second = xrender_query->minor_version;
-#endif
-}
-
-void QXcbConnection::initializeXRandr()
-{
- const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_randr_id);
- if (!reply || !reply->present)
- return;
-
- xrandr_first_event = reply->first_event;
-
- auto xrandr_query = Q_XCB_REPLY(xcb_randr_query_version, m_connection,
- XCB_RANDR_MAJOR_VERSION,
- XCB_RANDR_MINOR_VERSION);
-
- has_randr_extension = true;
-
- if (!xrandr_query || (xrandr_query->major_version < 1 || (xrandr_query->major_version == 1 && xrandr_query->minor_version < 2))) {
- qWarning("QXcbConnection: Failed to initialize XRandr");
- has_randr_extension = false;
- }
-
- xcb_screen_iterator_t rootIter = xcb_setup_roots_iterator(m_setup);
- for (; rootIter.rem; xcb_screen_next(&rootIter)) {
- xcb_randr_select_input(xcb_connection(),
- rootIter.data->root,
- XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
- XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
- XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
- XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY
- );
- }
-}
-
-void QXcbConnection::initializeXinerama()
-{
- const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xinerama_id);
- if (!reply || !reply->present)
- return;
-
- auto xinerama_is_active = Q_XCB_REPLY(xcb_xinerama_is_active, m_connection);
- has_xinerama_extension = xinerama_is_active && xinerama_is_active->state;
-}
-
-void QXcbConnection::initializeXShape()
-{
- const xcb_query_extension_reply_t *xshape_reply = xcb_get_extension_data(m_connection, &xcb_shape_id);
- if (!xshape_reply || !xshape_reply->present)
- return;
-
- has_shape_extension = true;
- auto shape_query = Q_XCB_REPLY(xcb_shape_query_version, m_connection);
- if (!shape_query) {
- qWarning("QXcbConnection: Failed to initialize SHAPE extension");
- } else if (shape_query->major_version > 1 || (shape_query->major_version == 1 && shape_query->minor_version >= 1)) {
- // The input shape is the only thing added in SHAPE 1.1
- has_input_shape = true;
- }
-}
-
-void QXcbConnection::initializeXKB()
-{
-#if QT_CONFIG(xkb)
- const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_xkb_id);
- if (!reply || !reply->present) {
- qWarning("Qt: XKEYBOARD extension not present on the X server.");
- xkb_first_event = 0;
- return;
- }
- xkb_first_event = reply->first_event;
-
- xcb_connection_t *c = connection()->xcb_connection();
-
- auto xkb_query = Q_XCB_REPLY(xcb_xkb_use_extension, c,
- XKB_X11_MIN_MAJOR_XKB_VERSION,
- XKB_X11_MIN_MINOR_XKB_VERSION);
-
- if (!xkb_query) {
- qWarning("Qt: Failed to initialize XKB extension");
- return;
- } else if (!xkb_query->supported) {
- qWarning("Qt: Unsupported XKB version (We want %d %d, but X server has %d %d)",
- XCB_XKB_MAJOR_VERSION, XCB_XKB_MINOR_VERSION,
- xkb_query->serverMajor, xkb_query->serverMinor);
- return;
- }
-
- has_xkb = true;
-
- const uint16_t required_map_parts = (XCB_XKB_MAP_PART_KEY_TYPES |
- XCB_XKB_MAP_PART_KEY_SYMS |
- XCB_XKB_MAP_PART_MODIFIER_MAP |
- XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
- XCB_XKB_MAP_PART_KEY_ACTIONS |
- XCB_XKB_MAP_PART_KEY_BEHAVIORS |
- XCB_XKB_MAP_PART_VIRTUAL_MODS |
- XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP);
-
- const uint16_t required_events = (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
- XCB_XKB_EVENT_TYPE_MAP_NOTIFY |
- XCB_XKB_EVENT_TYPE_STATE_NOTIFY);
-
- // XKB events are reported to all interested clients without regard
- // to the current keyboard input focus or grab state
- xcb_void_cookie_t select = xcb_xkb_select_events_checked(c,
- XCB_XKB_ID_USE_CORE_KBD,
- required_events,
- 0,
- required_events,
- required_map_parts,
- required_map_parts,
- 0);
-
- xcb_generic_error_t *error = xcb_request_check(c, select);
- if (error) {
- free(error);
- qWarning("Qt: failed to select notify events from xcb-xkb");
- return;
- }
-#endif
-}
-
-void QXcbConnection::initializeXSync()
-{
- const xcb_query_extension_reply_t *reply = xcb_get_extension_data(xcb_connection(), &xcb_sync_id);
- if (!reply || !reply->present)
- return;
-
- has_sync_extension = true;
-}
-
QXcbSystemTrayTracker *QXcbConnection::systemTrayTracker() const
{
if (!m_systemTrayTracker) {
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index db45031cf4..7cf25d41a6 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -43,36 +43,23 @@
#include <xcb/xcb.h>
#include <xcb/randr.h>
+#include <QtCore/QTimer>
#include <QtGui/private/qtguiglobal_p.h>
#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
-struct xcb_randr_get_output_info_reply_t;
-
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcQpaXInput)
@@ -80,10 +67,11 @@ 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(lcQpaClipboard)
Q_DECLARE_LOGGING_CATEGORY(lcQpaXDnd)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaEventReader)
class QXcbVirtualDesktop;
class QXcbScreen;
@@ -96,244 +84,6 @@ class QXcbNativeInterface;
class QXcbSystemTrayTracker;
class QXcbGlIntegration;
-namespace QXcbAtom {
- enum Atom {
- // window-manager <-> client protocols
- WM_PROTOCOLS,
- WM_DELETE_WINDOW,
- WM_TAKE_FOCUS,
- _NET_WM_PING,
- _NET_WM_CONTEXT_HELP,
- _NET_WM_SYNC_REQUEST,
- _NET_WM_SYNC_REQUEST_COUNTER,
- MANAGER, // System tray notification
- _NET_SYSTEM_TRAY_OPCODE, // System tray operation
-
- // ICCCM window state
- WM_STATE,
- WM_CHANGE_STATE,
- WM_CLASS,
- WM_NAME,
-
- // Session management
- WM_CLIENT_LEADER,
- WM_WINDOW_ROLE,
- SM_CLIENT_ID,
- WM_CLIENT_MACHINE,
-
- // Clipboard
- CLIPBOARD,
- INCR,
- TARGETS,
- MULTIPLE,
- TIMESTAMP,
- SAVE_TARGETS,
- CLIP_TEMPORARY,
- _QT_SELECTION,
- _QT_CLIPBOARD_SENTINEL,
- _QT_SELECTION_SENTINEL,
- CLIPBOARD_MANAGER,
-
- RESOURCE_MANAGER,
-
- _XSETROOT_ID,
-
- _QT_SCROLL_DONE,
- _QT_INPUT_ENCODING,
-
- // Qt/XCB specific
- _QT_CLOSE_CONNECTION,
-
- _MOTIF_WM_HINTS,
-
- DTWM_IS_RUNNING,
- ENLIGHTENMENT_DESKTOP,
- _DT_SAVE_MODE,
- _SGI_DESKS_MANAGER,
-
- // EWMH (aka NETWM)
- _NET_SUPPORTED,
- _NET_VIRTUAL_ROOTS,
- _NET_WORKAREA,
-
- _NET_MOVERESIZE_WINDOW,
- _NET_WM_MOVERESIZE,
-
- _NET_WM_NAME,
- _NET_WM_ICON_NAME,
- _NET_WM_ICON,
-
- _NET_WM_PID,
-
- _NET_WM_WINDOW_OPACITY,
-
- _NET_WM_STATE,
- _NET_WM_STATE_ABOVE,
- _NET_WM_STATE_BELOW,
- _NET_WM_STATE_FULLSCREEN,
- _NET_WM_STATE_MAXIMIZED_HORZ,
- _NET_WM_STATE_MAXIMIZED_VERT,
- _NET_WM_STATE_MODAL,
- _NET_WM_STATE_STAYS_ON_TOP,
- _NET_WM_STATE_DEMANDS_ATTENTION,
-
- _NET_WM_USER_TIME,
- _NET_WM_USER_TIME_WINDOW,
- _NET_WM_FULL_PLACEMENT,
-
- _NET_WM_WINDOW_TYPE,
- _NET_WM_WINDOW_TYPE_DESKTOP,
- _NET_WM_WINDOW_TYPE_DOCK,
- _NET_WM_WINDOW_TYPE_TOOLBAR,
- _NET_WM_WINDOW_TYPE_MENU,
- _NET_WM_WINDOW_TYPE_UTILITY,
- _NET_WM_WINDOW_TYPE_SPLASH,
- _NET_WM_WINDOW_TYPE_DIALOG,
- _NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
- _NET_WM_WINDOW_TYPE_POPUP_MENU,
- _NET_WM_WINDOW_TYPE_TOOLTIP,
- _NET_WM_WINDOW_TYPE_NOTIFICATION,
- _NET_WM_WINDOW_TYPE_COMBO,
- _NET_WM_WINDOW_TYPE_DND,
- _NET_WM_WINDOW_TYPE_NORMAL,
- _KDE_NET_WM_WINDOW_TYPE_OVERRIDE,
-
- _KDE_NET_WM_FRAME_STRUT,
- _NET_FRAME_EXTENTS,
-
- _NET_STARTUP_INFO,
- _NET_STARTUP_INFO_BEGIN,
-
- _NET_SUPPORTING_WM_CHECK,
-
- _NET_WM_CM_S0,
-
- _NET_SYSTEM_TRAY_VISUAL,
-
- _NET_ACTIVE_WINDOW,
-
- // Property formats
- TEXT,
- UTF8_STRING,
- CARDINAL,
-
- // Xdnd
- XdndEnter,
- XdndPosition,
- XdndStatus,
- XdndLeave,
- XdndDrop,
- XdndFinished,
- XdndTypelist,
- XdndActionList,
-
- XdndSelection,
-
- XdndAware,
- XdndProxy,
-
- XdndActionCopy,
- XdndActionLink,
- XdndActionMove,
- XdndActionPrivate,
-
- // Motif DND
- _MOTIF_DRAG_AND_DROP_MESSAGE,
- _MOTIF_DRAG_INITIATOR_INFO,
- _MOTIF_DRAG_RECEIVER_INFO,
- _MOTIF_DRAG_WINDOW,
- _MOTIF_DRAG_TARGETS,
-
- XmTRANSFER_SUCCESS,
- XmTRANSFER_FAILURE,
-
- // Xkb
- _XKB_RULES_NAMES,
-
- // XEMBED
- _XEMBED,
- _XEMBED_INFO,
-
- // XInput2
- ButtonLeft,
- ButtonMiddle,
- ButtonRight,
- ButtonWheelUp,
- ButtonWheelDown,
- ButtonHorizWheelLeft,
- ButtonHorizWheelRight,
- AbsMTPositionX,
- AbsMTPositionY,
- AbsMTTouchMajor,
- AbsMTTouchMinor,
- AbsMTOrientation,
- AbsMTPressure,
- AbsMTTrackingID,
- MaxContacts,
- RelX,
- RelY,
- // XInput2 tablet
- AbsX,
- AbsY,
- AbsPressure,
- AbsTiltX,
- AbsTiltY,
- AbsWheel,
- AbsDistance,
- WacomSerialIDs,
- INTEGER,
- RelHorizWheel,
- RelVertWheel,
- RelHorizScroll,
- RelVertScroll,
-
- _XSETTINGS_SETTINGS,
-
- _COMPIZ_DECOR_PENDING,
- _COMPIZ_DECOR_REQUEST,
- _COMPIZ_DECOR_DELETE_PIXMAP,
- _COMPIZ_TOOLKIT_ACTION,
- _GTK_LOAD_ICONTHEMES,
-
- AT_SPI_BUS,
-
- EDID,
- EDID_DATA,
- XFree86_DDC_EDID1_RAWDATA,
-
- NAtoms
- };
-}
-
-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:
- void addEvent(xcb_generic_event_t *event);
-
- QMutex m_mutex;
- QXcbEventArray m_events;
- QXcbConnection *m_connection;
-};
-
class QXcbWindowEventListener
{
public:
@@ -358,7 +108,7 @@ public:
virtual void handleXIMouseEvent(xcb_ge_event_t *, Qt::MouseEventSource = Qt::MouseEventNotSynthesized) {}
virtual void handleXIEnterLeave(xcb_ge_event_t *) {}
#endif
- virtual QXcbWindow *toWindow() { return 0; }
+ virtual QXcbWindow *toWindow() { return nullptr; }
};
typedef QHash<xcb_window_t, QXcbWindowEventListener *> WindowMapper;
@@ -375,38 +125,23 @@ private:
QXcbWindow *m_window;
};
-class QAbstractEventDispatcher;
-class Q_XCB_EXPORT QXcbConnection : public QObject
+class Q_XCB_EXPORT QXcbConnection : public QXcbBasicConnection
{
Q_OBJECT
public:
- explicit QXcbConnection(xcb_connection_t *c, int primaryScreenNumber,
- QXcbNativeInterface *nativeInterface, bool canGrabServer,
- xcb_visualid_t defaultVisualId, const QByteArray &displayName,
- void *xlibDisplay = nullptr);
-
+ QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName = 0);
~QXcbConnection();
- static QXcbConnection *create(QXcbNativeInterface *nativeInterface, bool canGrabServer,
- xcb_visualid_t defaultVisualId, const char *displayName = nullptr);
-
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
@@ -414,9 +149,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
}
@@ -436,9 +171,6 @@ public:
bool hasDefaultVisualId() const { return m_defaultVisualId != UINT_MAX; }
xcb_visualid_t defaultVisualId() const { return m_defaultVisualId; }
-#if QT_CONFIG(xcb_xlib)
- void *xlib_display() const { return m_xlib_display; }
-#endif
void sync();
void handleXcbError(xcb_generic_error_t *error);
@@ -452,43 +184,11 @@ public:
QXcbWindowEventListener *windowEventListenerFromId(xcb_window_t id);
QXcbWindow *platformWindowFromId(xcb_window_t id);
- template<typename Functor>
- inline xcb_generic_event_t *checkEvent(Functor &&filter, bool removeFromQueue = true);
-
- 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 void setTime(xcb_timestamp_t t) { if (timeGreaterThan(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 hasXSync() const { return has_sync_extension; }
+ inline void setNetWmUserTime(xcb_timestamp_t t) { if (timeGreaterThan(t, m_netWmUserTime)) m_netWmUserTime = t; }
xcb_timestamp_t getTimestamp();
xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
@@ -523,46 +223,35 @@ public:
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);
void xi2UpdateScrollingDevices();
bool startSystemMoveResizeForTouchBegin(xcb_window_t window, const QPoint &point, int corner);
void abortSystemMoveResizeForTouch();
bool isTouchScreen(int id);
#endif
- QXcbEventReader *eventReader() const { return m_reader; }
bool canGrab() const { return m_canGrabServer; }
QXcbGlIntegration *glIntegration() const;
-protected:
- bool event(QEvent *e) override;
+ void flush() { xcb_flush(xcb_connection()); }
+ void processXcbEvents(QEventLoop::ProcessEventsFlags flags);
-public slots:
- void flush() { xcb_flush(m_connection); }
+ QTimer &focusInTimer() { return m_focusInTimer; }
-private slots:
- void processXcbEvents();
+protected:
+ bool event(QEvent *e) override;
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 initializeXSync();
+ 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;
@@ -574,12 +263,11 @@ 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;
+ inline bool timeGreaterThan(xcb_timestamp_t a, xcb_timestamp_t b) const
+ { return static_cast<int32_t>(a - b) > 0 || b == XCB_CURRENT_TIME; }
- bool m_xi2Enabled = false;
#if QT_CONFIG(xcb_xinput)
- int m_xi2Minor = -1;
- void initializeXInput2();
void xi2SetupDevice(void *info, bool removeExisting = true);
void xi2SetupDevices();
struct TouchDeviceData {
@@ -605,7 +293,6 @@ private:
void xi2HandleEvent(xcb_ge_event_t *event);
void xi2HandleHierarchyEvent(void *event);
void xi2HandleDeviceChangedEvent(void *event);
- int m_xiOpCode;
void xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow);
#if QT_CONFIG(tabletevent)
struct TabletData {
@@ -646,24 +333,25 @@ private:
ScrollingDevice *scrollingDeviceForId(int id);
static bool xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value);
-#endif
- xcb_connection_t *const m_connection;
- 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;
- const int m_primaryScreenNumber;
-
- xcb_atom_t m_allAtoms[QXcbAtom::NAtoms];
xcb_timestamp_t m_time = XCB_CURRENT_TIME;
xcb_timestamp_t m_netWmUserTime = XCB_CURRENT_TIME;
- const QByteArray m_displayName;
-
QXcbKeyboard *m_keyboard = nullptr;
#ifndef QT_NO_CLIPBOARD
QXcbClipboard *m_clipboard = nullptr;
@@ -674,45 +362,11 @@ private:
QScopedPointer<QXcbWMSupport> m_wmSupport;
QXcbNativeInterface *m_nativeInterface = nullptr;
-#if QT_CONFIG(xcb_xlib)
- void *const m_xlib_display;
-#endif
- QXcbEventReader *m_reader = nullptr;
+ QXcbEventQueue *m_eventQueue = nullptr;
-#if QT_CONFIG(xcb_xinput)
- QHash<int, TouchDeviceData> m_touchDevices;
- struct StartSystemMoveResizeInfo {
- xcb_window_t window = XCB_NONE;
- uint16_t deviceid;
- uint32_t pointid;
- int corner;
- } m_startSystemMoveResizeInfo;
-#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;
-#if QT_CONFIG(xcb_xinput)
- uint32_t xinput_first_event = 0;
-#endif
-
- bool has_xfixes = false;
- bool has_xinerama_extension = false;
- bool has_shape_extension = false;
- bool has_randr_extension = false;
- bool has_input_shape;
- bool has_xkb = false;
- bool has_render_extension = false;
- bool has_shm = false;
- bool has_shm_fd = false;
- bool has_sync_extension = false;
-
- QPair<int, int> m_xrenderVersion;
-
- Qt::MouseButtons m_buttonState = 0;
+ Qt::MouseButtons m_buttonState = nullptr;
Qt::MouseButton m_button = Qt::NoButton;
QXcbWindow *m_focusWindow = nullptr;
@@ -729,13 +383,11 @@ private:
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;
+ QTimer m_focusInTimer;
+
};
#if QT_CONFIG(xcb_xinput)
#if QT_CONFIG(tabletevent)
@@ -744,24 +396,6 @@ Q_DECLARE_TYPEINFO(QXcbConnection::TabletData, Q_MOVABLE_TYPE);
#endif
#endif
-template<typename Functor>
-xcb_generic_event_t *QXcbConnection::checkEvent(Functor &&filter, bool removeFromQueue)
-{
- QXcbEventArray *eventqueue = m_reader->lock();
-
- for (int i = 0; i < eventqueue->size(); ++i) {
- xcb_generic_event_t *event = eventqueue->at(i);
- if (event && filter(event, event->response_type & ~0x80)) {
- if (removeFromQueue)
- (*eventqueue)[i] = nullptr;
- m_reader->unlock();
- return event;
- }
- }
- m_reader->unlock();
- return nullptr;
-}
-
class QXcbConnectionGrabber
{
public:
@@ -772,22 +406,6 @@ private:
QXcbConnection *m_connection;
};
-#define Q_XCB_REPLY_CONNECTION_ARG(connection, ...) connection
-
-struct QStdFreeDeleter {
- void operator()(void *p) const Q_DECL_NOTHROW { return std::free(p); }
-};
-
-#define Q_XCB_REPLY(call, ...) \
- std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \
- call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call(__VA_ARGS__), nullptr) \
- )
-
-#define Q_XCB_REPLY_UNCHECKED(call, ...) \
- std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \
- call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call##_unchecked(__VA_ARGS__), nullptr) \
- )
-
// The xcb_send_event() requires all events to have 32 bytes. It calls memcpy() on the
// passed in event. If the passed in event is less than 32 bytes, memcpy() reaches into
// unrelated memory.
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
new file mode 100644
index 0000000000..9a028e5a7e
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
@@ -0,0 +1,445 @@
+/****************************************************************************
+**
+** 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>
+#include <xcb/render.h>
+#if QT_CONFIG(xcb_xinput)
+#include <xcb/xinput.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);
+ m_maximumRequestLength = xcb_get_maximum_request_length(m_xcbConnection);
+
+ xcb_extension_t *extensions[] = {
+ &xcb_shm_id, &xcb_xfixes_id, &xcb_randr_id, &xcb_shape_id, &xcb_sync_id,
+ &xcb_render_id,
+#if QT_CONFIG(xkb)
+ &xcb_xkb_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
+ }
+}
+
+size_t QXcbBasicConnection::maxRequestDataBytes(size_t requestSize) const
+{
+ if (hasBigRequest())
+ requestSize += 4; // big-request encoding adds 4 bytes
+
+ return m_maximumRequestLength * 4 - requestSize;
+}
+
+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();
+}
+
+bool QXcbBasicConnection::hasBigRequest() const
+{
+ return m_maximumRequestLength > m_setup->maximum_request_length;
+}
+
+#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::initializeXRender()
+{
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_render_id);
+ if (!reply || !reply->present) {
+ qCDebug(lcQpaXcb, "XRender extension not present on the X server");
+ return;
+ }
+
+ auto xrenderQuery = Q_XCB_REPLY(xcb_render_query_version, m_xcbConnection,
+ XCB_RENDER_MAJOR_VERSION,
+ XCB_RENDER_MINOR_VERSION);
+ if (!xrenderQuery) {
+ qCWarning(lcQpaXcb, "xcb_render_query_version failed");
+ return;
+ }
+
+ m_hasXRender = true;
+ m_xrenderVersion.first = xrenderQuery->major_version;
+ m_xrenderVersion.second = xrenderQuery->minor_version;
+}
+
+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::initializeXRandr()
+{
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_randr_id);
+ if (!reply || !reply->present)
+ return;
+
+ auto xrandrQuery = Q_XCB_REPLY(xcb_randr_query_version, m_xcbConnection,
+ XCB_RANDR_MAJOR_VERSION,
+ XCB_RANDR_MINOR_VERSION);
+ if (!xrandrQuery || (xrandrQuery->major_version < 1 ||
+ (xrandrQuery->major_version == 1 && xrandrQuery->minor_version < 2))) {
+ qCWarning(lcQpaXcb, "failed to initialize XRandr");
+ return;
+ }
+
+ m_hasXRandr = true;
+ m_xrandrFirstEvent = reply->first_event;
+}
+
+#if QT_CONFIG(xcb_xinput)
+void QXcbBasicConnection::initializeXInput2()
+{
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_input_id);
+ if (!reply || !reply->present) {
+ qCDebug(lcQpaXcb, "XInput extension is not present on the X server");
+ return;
+ }
+
+ auto xinputQuery = Q_XCB_REPLY(xcb_input_xi_query_version, m_xcbConnection, 2, 2);
+ if (!xinputQuery || xinputQuery->major_version != 2) {
+ qCWarning(lcQpaXcb, "X server does not support XInput 2");
+ return;
+ }
+
+ qCDebug(lcQpaXcb, "Using XInput version %d.%d",
+ xinputQuery->major_version, xinputQuery->minor_version);
+
+ m_xi2Enabled = true;
+ m_xiOpCode = reply->major_opcode;
+ m_xinputFirstEvent = reply->first_event;
+ m_xi2Minor = xinputQuery->minor_version;
+}
+#endif
+
+void QXcbBasicConnection::initializeXShape()
+{
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_shape_id);
+ if (!reply || !reply->present)
+ return;
+
+ m_hasXhape = true;
+
+ auto shapeQuery = Q_XCB_REPLY(xcb_shape_query_version, m_xcbConnection);
+ if (!shapeQuery) {
+ qCWarning(lcQpaXcb, "failed to initialize XShape extension");
+ return;
+ }
+
+ if (shapeQuery->major_version > 1 || (shapeQuery->major_version == 1 && shapeQuery->minor_version >= 1)) {
+ // The input shape is the only thing added in SHAPE 1.1
+ m_hasInputShape = true;
+ }
+}
+
+void QXcbBasicConnection::initializeXKB()
+{
+#if QT_CONFIG(xkb)
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_xkb_id);
+ if (!reply || !reply->present) {
+ qCWarning(lcQpaXcb, "XKeyboard extension not present on the X server");
+ return;
+ }
+
+ int wantMajor = 1;
+ int wantMinor = 0;
+ auto xkbQuery = Q_XCB_REPLY(xcb_xkb_use_extension, m_xcbConnection, wantMajor, wantMinor);
+ if (!xkbQuery) {
+ qCWarning(lcQpaXcb, "failed to initialize XKeyboard extension");
+ return;
+ }
+ if (!xkbQuery->supported) {
+ qCWarning(lcQpaXcb, "unsupported XKB version (we want %d.%d, but X server has %d.%d)",
+ wantMajor, wantMinor, xkbQuery->serverMajor, xkbQuery->serverMinor);
+ return;
+ }
+
+ m_hasXkb = true;
+ m_xkbFirstEvent = reply->first_event;
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.h b/src/plugins/platforms/xcb/qxcbconnection_basic.h
new file mode 100644
index 0000000000..1bd4310562
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbconnection_basic.h
@@ -0,0 +1,181 @@
+/****************************************************************************
+**
+** 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 "qxcbexport.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 Q_XCB_EXPORT 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; }
+
+ size_t maxRequestDataBytes(size_t requestSize) const;
+
+ 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; }
+ bool hasBigRequest() const;
+
+#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;
+
+ uint32_t m_maximumRequestLength = 0;
+};
+
+#define Q_XCB_REPLY_CONNECTION_ARG(connection, ...) connection
+
+struct QStdFreeDeleter {
+ void operator()(void *p) const noexcept { 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..9ba71ada37
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp
@@ -0,0 +1,416 @@
+/****************************************************************************
+**
+** 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 <qpa/qwindowsysteminterface.h>
+
+#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.swapItemsAt(0, idx);
+ }
+ screen->virtualDesktop()->setPrimaryScreen(screen);
+ QWindowSystemInterface::handlePrimaryScreenChanged(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);
+ QWindowSystemInterface::handleScreenAdded(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.swapItemsAt(0, idx);
+ QWindowSystemInterface::handlePrimaryScreenChanged(newPrimary);
+ }
+
+ QWindowSystemInterface::handleScreenRemoved(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(std::move(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() << ")";
+ QWindowSystemInterface::handleScreenAdded(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 a3befc7384..e4da207b00 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -51,31 +51,6 @@
using qt_xcb_input_device_event_t = xcb_input_button_press_event_t;
-void QXcbConnection::initializeXInput2()
-{
- const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_input_id);
- if (!reply || !reply->present) {
- qCDebug(lcQpaXInput, "XInput extension is not present on the X server");
- return;
- }
-
- m_xiOpCode = reply->major_opcode;
- xinput_first_event = reply->first_event;
-
- auto xinput_query = Q_XCB_REPLY(xcb_input_xi_query_version, m_connection, 2, 2);
-
- if (!xinput_query || xinput_query->major_version != 2) {
- qCWarning(lcQpaXInput, "X server does not support XInput 2");
- } else {
- qCDebug(lcQpaXInput, "Using XInput version %d.%d",
- xinput_query->major_version, xinput_query->minor_version);
- m_xi2Minor = xinput_query->minor_version;
- m_xi2Enabled = true;
- xi2SetupDevices();
- xi2SelectStateEvents();
- }
-}
-
struct qt_xcb_input_event_mask_t {
xcb_input_event_mask_t header;
uint32_t mask;
@@ -91,7 +66,7 @@ void QXcbConnection::xi2SelectStateEvents()
xiEventMask.mask = XCB_INPUT_XI_EVENT_MASK_HIERARCHY;
xiEventMask.mask |= XCB_INPUT_XI_EVENT_MASK_DEVICE_CHANGED;
xiEventMask.mask |= XCB_INPUT_XI_EVENT_MASK_PROPERTY;
- xcb_input_xi_select_events(m_connection, rootWindow(), 1, &xiEventMask.header);
+ xcb_input_xi_select_events(xcb_connection(), rootWindow(), 1, &xiEventMask.header);
}
void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
@@ -117,8 +92,8 @@ void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
mask.header.mask_len = 1;
mask.mask = bitMask;
xcb_void_cookie_t cookie =
- xcb_input_xi_select_events_checked(m_connection, window, 1, &mask.header);
- xcb_generic_error_t *error = xcb_request_check(m_connection, cookie);
+ xcb_input_xi_select_events_checked(xcb_connection(), window, 1, &mask.header);
+ xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie);
if (error) {
qCDebug(lcQpaXInput, "failed to select events, window %x, error code %d", window, error->error_code);
free(error);
@@ -310,7 +285,7 @@ void QXcbConnection::xi2SetupDevices()
m_touchDevices.clear();
m_xiMasterPointerIds.clear();
- auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, m_connection, XCB_INPUT_DEVICE_ALL);
+ auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), XCB_INPUT_DEVICE_ALL);
if (!reply) {
qCDebug(lcQpaXInputDevices) << "failed to query devices";
return;
@@ -387,8 +362,8 @@ void QXcbConnection::xi2SelectDeviceEventsCompatibility(xcb_window_t window)
xiMask.mask = mask;
xcb_void_cookie_t cookie =
- xcb_input_xi_select_events_checked(m_connection, window, 1, &xiMask.header);
- xcb_generic_error_t *error = xcb_request_check(m_connection, cookie);
+ xcb_input_xi_select_events_checked(xcb_connection(), window, 1, &xiMask.header);
+ xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie);
if (error) {
qCDebug(lcQpaXInput, "failed to select events, window %x, error code %d", window, error->error_code);
free(error);
@@ -413,7 +388,7 @@ void QXcbConnection::xi2SelectDeviceEventsCompatibility(xcb_window_t window)
xiEventMask[i].header.mask_len = 1;
xiEventMask[i].mask = mask;
}
- xcb_input_xi_select_events(m_connection, window, nrTablets, &(xiEventMask.data()->header));
+ xcb_input_xi_select_events(xcb_connection(), window, nrTablets, &(xiEventMask.data()->header));
}
#endif
@@ -430,7 +405,7 @@ void QXcbConnection::xi2SelectDeviceEventsCompatibility(xcb_window_t window)
xiEventMask[i].mask = mask;
i++;
}
- xcb_input_xi_select_events(m_connection, window, i, &(xiEventMask.data()->header));
+ xcb_input_xi_select_events(xcb_connection(), window, i, &(xiEventMask.data()->header));
}
}
@@ -633,7 +608,7 @@ bool QXcbConnection::xi2MouseEventsDisabled() const
static bool xi2MouseDisabled = qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE");
// FIXME: Don't use XInput2 mouse events when Xinerama extension
// is enabled, because it causes problems with multi-monitor setup.
- return xi2MouseDisabled || has_xinerama_extension;
+ return xi2MouseDisabled || hasXinerama();
}
bool QXcbConnection::isTouchScreen(int id)
@@ -662,7 +637,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
qreal nx = -1.0, ny = -1.0;
qreal w = 0.0, h = 0.0;
bool majorAxisIsY = touchPoint.area.height() > touchPoint.area.width();
- for (const TouchDeviceData::ValuatorClassInfo vci : dev->valuatorInfo) {
+ for (const TouchDeviceData::ValuatorClassInfo &vci : qAsConst(dev->valuatorInfo)) {
double value;
if (!xi2GetValuatorValueIfSet(xiDeviceEvent, vci.number, &value))
continue;
@@ -745,7 +720,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
// Touches must be accepted when we are grabbing touch events. Otherwise the entire sequence
// will get replayed when the grab ends.
if (m_xiGrab) {
- xcb_input_xi_allow_events(m_connection, XCB_CURRENT_TIME, xiDeviceEvent->deviceid,
+ xcb_input_xi_allow_events(xcb_connection(), XCB_CURRENT_TIME, xiDeviceEvent->deviceid,
XCB_INPUT_EVENT_MODE_ACCEPT_TOUCH,
xiDeviceEvent->detail, xiDeviceEvent->event);
}
@@ -771,7 +746,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
xiDeviceEvent->detail == m_startSystemMoveResizeInfo.pointid) {
QXcbWindow *window = platformWindowFromId(m_startSystemMoveResizeInfo.window);
if (window) {
- xcb_input_xi_allow_events(m_connection, XCB_CURRENT_TIME, xiDeviceEvent->deviceid,
+ xcb_input_xi_allow_events(xcb_connection(), XCB_CURRENT_TIME, xiDeviceEvent->deviceid,
XCB_INPUT_EVENT_MODE_REJECT_TOUCH,
xiDeviceEvent->detail, xiDeviceEvent->event);
window->doStartSystemMoveResize(QPoint(x, y), m_startSystemMoveResizeInfo.corner);
@@ -848,12 +823,12 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
| XCB_INPUT_XI_EVENT_MASK_TOUCH_UPDATE
| XCB_INPUT_XI_EVENT_MASK_TOUCH_END;
- for (int id : m_xiMasterPointerIds) {
+ for (int id : qAsConst(m_xiMasterPointerIds)) {
xcb_generic_error_t *error = nullptr;
- auto cookie = xcb_input_xi_grab_device(m_connection, w, XCB_CURRENT_TIME, XCB_CURSOR_NONE, id,
+ auto cookie = xcb_input_xi_grab_device(xcb_connection(), w, XCB_CURRENT_TIME, XCB_CURSOR_NONE, id,
XCB_INPUT_GRAB_MODE_22_ASYNC, XCB_INPUT_GRAB_MODE_22_ASYNC,
false, 1, &mask);
- auto *reply = xcb_input_xi_grab_device_reply(m_connection, cookie, &error);
+ auto *reply = xcb_input_xi_grab_device_reply(xcb_connection(), cookie, &error);
if (error) {
qCDebug(lcQpaXInput, "failed to grab events for device %d on window %x"
"(error code %d)", id, w, error->error_code);
@@ -866,9 +841,9 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
free(reply);
}
} else { // ungrab
- for (int id : m_xiMasterPointerIds) {
- auto cookie = xcb_input_xi_ungrab_device_checked(m_connection, XCB_CURRENT_TIME, id);
- xcb_generic_error_t *error = xcb_request_check(m_connection, cookie);
+ for (int id : qAsConst(m_xiMasterPointerIds)) {
+ auto cookie = xcb_input_xi_ungrab_device_checked(xcb_connection(), XCB_CURRENT_TIME, id);
+ xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie);
if (error) {
qCDebug(lcQpaXInput, "XIUngrabDevice failed - id: %d (error code %d)", id, error->error_code);
free(error);
@@ -911,7 +886,7 @@ void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
auto *xiEvent = reinterpret_cast<xcb_input_device_changed_event_t *>(event);
switch (xiEvent->reason) {
case XCB_INPUT_CHANGE_REASON_DEVICE_CHANGE: {
- auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, m_connection, xiEvent->sourceid);
+ auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), xiEvent->sourceid);
if (!reply || reply->num_infos <= 0)
return;
auto it = xcb_input_xi_query_device_infos_iterator(reply.get());
@@ -931,7 +906,7 @@ void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
void QXcbConnection::xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice)
{
- auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, m_connection, scrollingDevice.deviceId);
+ auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), scrollingDevice.deviceId);
if (!reply || reply->num_infos <= 0) {
qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", scrollingDevice.deviceId);
return;
@@ -1184,7 +1159,7 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD
_WACSER_COUNT
};
- auto reply = Q_XCB_REPLY(xcb_input_xi_get_property, m_connection, tabletData->deviceId, 0,
+ auto reply = Q_XCB_REPLY(xcb_input_xi_get_property, xcb_connection(), tabletData->deviceId, 0,
ev->property, XCB_GET_PROPERTY_TYPE_ANY, 0, 100);
if (reply) {
if (reply->type == atom(QXcbAtom::INTEGER) && reply->format == 32 && reply->num_items == _WACSER_COUNT) {
@@ -1233,6 +1208,11 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD
return handled;
}
+inline qreal scaleOneValuator(qreal normValue, qreal screenMin, qreal screenSize)
+{
+ return screenMin + normValue * screenSize;
+}
+
void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletData)
{
auto *ev = reinterpret_cast<const qt_xcb_input_device_event_t *>(event);
@@ -1245,6 +1225,17 @@ void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletD
QPointF global(fixed1616ToReal(ev->root_x), fixed1616ToReal(ev->root_y));
double pressure = 0, rotation = 0, tangentialPressure = 0;
int xTilt = 0, yTilt = 0;
+ static const bool useValuators = !qEnvironmentVariableIsSet("QT_XCB_TABLET_LEGACY_COORDINATES");
+
+ // Valuators' values are relative to the physical size of the current virtual
+ // screen. Therefore we cannot use QScreen/QWindow geometry and should use
+ // QPlatformWindow/QPlatformScreen instead.
+ QRect physicalScreenArea;
+ if (Q_LIKELY(useValuators)) {
+ const QList<QPlatformScreen *> siblings = window->screen()->handle()->virtualSiblings();
+ for (const QPlatformScreen *screen : siblings)
+ physicalScreenArea |= screen->geometry();
+ }
for (QHash<int, TabletData::ValuatorClassInfo>::iterator it = tabletData->valuatorInfo.begin(),
ite = tabletData->valuatorInfo.end(); it != ite; ++it) {
@@ -1253,6 +1244,20 @@ void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletD
xi2GetValuatorValueIfSet(event, classInfo.number, &classInfo.curVal);
double normalizedValue = (classInfo.curVal - classInfo.minVal) / (classInfo.maxVal - classInfo.minVal);
switch (valuator) {
+ case QXcbAtom::AbsX:
+ if (Q_LIKELY(useValuators)) {
+ const qreal value = scaleOneValuator(normalizedValue, physicalScreenArea.x(), physicalScreenArea.width());
+ global.setX(value);
+ local.setX(value - window->handle()->geometry().x());
+ }
+ break;
+ case QXcbAtom::AbsY:
+ if (Q_LIKELY(useValuators)) {
+ qreal value = scaleOneValuator(normalizedValue, physicalScreenArea.y(), physicalScreenArea.height());
+ global.setY(value);
+ local.setY(value - window->handle()->geometry().y());
+ }
+ break;
case QXcbAtom::AbsPressure:
pressure = normalizedValue;
break;
diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp
index 57629ac03a..fbadab4d50 100644
--- a/src/plugins/platforms/xcb/qxcbcursor.cpp
+++ b/src/plugins/platforms/xcb/qxcbcursor.cpp
@@ -255,6 +255,7 @@ static const uint8_t * const cursor_bits20[] = {
forbidden_bits, forbiddenm_bits
};
+// ### FIXME This mapping is incomplete - QTBUG-71423
static const std::vector<const char *> cursorNames[] = {
{ "left_ptr", "default", "top_left_arrow", "left_arrow" },
{ "up_arrow" },
@@ -273,7 +274,7 @@ static const std::vector<const char *> cursorNames[] = {
{ "forbidden", "not-allowed", "crossed_circle", "circle", "03b6e0fcb3499374a867c041f52298f0" },
{ "whats_this", "help", "question_arrow", "5c6cd98b3f3ebcb1f9c7f1c204630408", "d9ce0ab605698f320427677b458ad60b" },
{ "left_ptr_watch", "half-busy", "progress", "00000000000000020006000e7e9ffc3f", "08e8e1c95fe2fc01f976f1e063a24ccd" },
- { "openhand", "fleur", "5aca4d189052212118709018842178c0", "9d800788f1b08800ae810202380a0822" },
+ { "openhand", "grab", "fleur", "5aca4d189052212118709018842178c0", "9d800788f1b08800ae810202380a0822" },
{ "closedhand", "grabbing", "208530c400c041818281048008011002" },
{ "dnd-copy", "copy" },
{ "dnd-move", "move" },
@@ -606,14 +607,10 @@ xcb_cursor_t QXcbCursor::createBitmapCursor(QCursor *cursor)
QPoint spot = cursor->hotSpot();
xcb_cursor_t c = XCB_NONE;
if (cursor->pixmap().depth() > 1) {
-#if QT_CONFIG(xcb_render)
if (connection()->hasXRender(0, 5))
c = qt_xcb_createCursorXRender(m_screen, cursor->pixmap().toImage(), spot);
else
qCWarning(lcQpaXcb, "xrender >= 0.5 required to create pixmap cursors");
-#else
- qCWarning(lcQpaXcb, "This build of Qt does not support pixmap cursors");
-#endif
} else {
xcb_connection_t *conn = xcb_connection();
xcb_pixmap_t cp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->bitmap()->toImage());
diff --git a/src/plugins/platforms/xcb/qxcbcursor.h b/src/plugins/platforms/xcb/qxcbcursor.h
index 5bc806381c..0b238823f0 100644
--- a/src/plugins/platforms/xcb/qxcbcursor.h
+++ b/src/plugins/platforms/xcb/qxcbcursor.h
@@ -65,7 +65,7 @@ inline bool operator==(const QXcbCursorCacheKey &k1, const QXcbCursorCacheKey &k
return k1.shape == k2.shape && k1.bitmapCacheKey == k2.bitmapCacheKey && k1.maskCacheKey == k2.maskCacheKey;
}
-inline uint qHash(const QXcbCursorCacheKey &k, uint seed) Q_DECL_NOTHROW
+inline uint qHash(const QXcbCursorCacheKey &k, uint seed) noexcept
{
return (uint(k.shape) + uint(k.bitmapCacheKey) + uint(k.maskCacheKey)) ^ seed;
}
@@ -83,7 +83,7 @@ public:
QPoint pos() const override;
void setPos(const QPoint &pos) override;
- static void queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDesktop, QPoint *pos, int *keybMask = 0);
+ static void queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDesktop, QPoint *pos, int *keybMask = nullptr);
#ifndef QT_NO_CURSOR
xcb_cursor_t xcbCursor(const QCursor &c) const
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index 2b8e507f30..aa329d8cb7 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -794,7 +794,7 @@ void QXcbDrag::handlePosition(QPlatformWindow * w, const xcb_client_message_even
{
xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event);
ClientMessageScanner scanner(atom(QXcbAtom::XdndPosition));
- while (auto nextEvent = connection()->checkEvent(scanner)) {
+ while (auto nextEvent = connection()->eventQueue()->peek(scanner)) {
if (lastEvent != event)
free(lastEvent);
lastEvent = reinterpret_cast<xcb_client_message_event_t *>(nextEvent);
@@ -846,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;
diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h
index c19008c04b..1388e68acc 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.h
+++ b/src/plugins/platforms/xcb/qxcbdrag.h
@@ -86,7 +86,7 @@ public:
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,
- Qt::MouseButtons b = 0, Qt::KeyboardModifiers mods = 0);
+ Qt::MouseButtons b = nullptr, Qt::KeyboardModifiers mods = nullptr);
void handleStatus(const xcb_client_message_event_t *event);
void handleSelectionRequest(const xcb_selection_request_event_t *event);
@@ -109,7 +109,7 @@ private:
void init();
void handle_xdnd_position(QPlatformWindow *w, const xcb_client_message_event_t *event,
- Qt::MouseButtons b = 0, Qt::KeyboardModifiers mods = 0);
+ Qt::MouseButtons b = nullptr, Qt::KeyboardModifiers mods = nullptr);
void handle_xdnd_status(const xcb_client_message_event_t *event);
void send_leave();
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..ddf448cf87
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbeventdispatcher.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** 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);
+};
+
+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..82a36c0727
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbeventqueue.cpp
@@ -0,0 +1,395 @@
+/****************************************************************************
+**
+** 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();
+ }
+
+ flushBufferedEvents();
+ while (xcb_generic_event_t *event = takeFirst(QEventLoop::AllEvents))
+ 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);
+ } else {
+ free(event);
+ }
+ };
+
+ while (!m_closeConnectionDetected && (event = xcb_wait_for_event(connection))) {
+ enqueueEvent(event);
+ while (!m_closeConnectionDetected && (event = xcb_poll_for_queued_event(connection)))
+ enqueueEvent(event);
+
+ m_newEventsCondition.wakeOne();
+ 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.constFind(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.end()) {
+ 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.end())
+ *peekerToNodeIt = node; // id still in the cache, update node
+ }
+
+ return result;
+}
+
+void QXcbEventQueue::waitForNewEvents(unsigned long time)
+{
+ m_newEventsMutex.lock();
+ m_newEventsCondition.wait(&m_newEventsMutex, time);
+ m_newEventsMutex.unlock();
+}
+
+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..11d0b8e963
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbeventqueue.h
@@ -0,0 +1,169 @@
+/****************************************************************************
+**
+** 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 <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+
+#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);
+
+ void waitForNewEvents(unsigned long time = ULONG_MAX);
+
+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;
+
+ QMutex m_newEventsMutex;
+ QWaitCondition m_newEventsCondition;
+};
+
+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/qxcbimage.cpp b/src/plugins/platforms/xcb/qxcbimage.cpp
index 44c7d22344..8f33e6ed31 100644
--- a/src/plugins/platforms/xcb/qxcbimage.cpp
+++ b/src/plugins/platforms/xcb/qxcbimage.cpp
@@ -42,16 +42,9 @@
#include <QtGui/QColor>
#include <QtGui/private/qimage_p.h>
#include <QtGui/private/qdrawhelper_p.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 "qxcbconnection.h"
#include "qxcbintegration.h"
@@ -236,7 +229,6 @@ xcb_pixmap_t qt_xcb_XPixmapFromBitmap(QXcbScreen *screen, const QImage &image)
xcb_cursor_t qt_xcb_createCursorXRender(QXcbScreen *screen, const QImage &image,
const QPoint &spot)
{
-#if QT_CONFIG(xcb_render)
xcb_connection_t *conn = screen->xcb_connection();
const int w = image.width();
const int h = image.height();
@@ -289,13 +281,6 @@ xcb_cursor_t qt_xcb_createCursorXRender(QXcbScreen *screen, const QImage &image,
xcb_render_free_picture(conn, pic);
xcb_free_pixmap(conn, pix);
return cursor;
-
-#else
- Q_UNUSED(screen);
- Q_UNUSED(image);
- Q_UNUSED(spot);
- return XCB_NONE;
-#endif
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp
index db8dc09025..a70c7db923 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>
@@ -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();
@@ -193,14 +196,22 @@ QXcbIntegration::QXcbIntegration(const QStringList &parameters, int &argc, char
const int numParameters = parameters.size();
m_connections.reserve(1 + numParameters / 2);
- if (QXcbConnection *defaultConnection = QXcbConnection::create(m_nativeInterface.data(), m_canGrab, m_defaultVisualId, displayName)) {
- m_connections.append(defaultConnection);
- for (int i = 0; i < numParameters - 1; i += 2) {
- qCDebug(lcQpaScreen) << "connecting to additional display: " << parameters.at(i) << parameters.at(i+1);
- QString display = parameters.at(i) + QLatin1Char(':') + parameters.at(i+1);
- if (QXcbConnection *connection = QXcbConnection::create(m_nativeInterface.data(), m_canGrab, m_defaultVisualId, display.toLatin1().constData()))
- m_connections.append(connection);
- }
+ auto conn = new QXcbConnection(m_nativeInterface.data(), m_canGrab, m_defaultVisualId, displayName);
+ 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(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())
+ m_connections << conn;
+ else
+ delete conn;
}
m_fontDatabase.reset(new QGenericUnixFontDatabase());
@@ -332,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()
@@ -349,6 +357,8 @@ void QXcbIntegration::initialize()
m_inputContext.reset(QPlatformInputContextFactory::create(icStr));
if (!m_inputContext && icStr != defaultInputContext && icStr != QLatin1String("none"))
m_inputContext.reset(QPlatformInputContextFactory::create(defaultInputContext));
+
+ defaultConnection()->keyboard()->initialize();
}
void QXcbIntegration::moveToScreen(QWindow *window, int screen)
diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h
index a2de22d53d..571726c354 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
{
@@ -138,8 +137,6 @@ private:
QScopedPointer<QPlatformServices> m_services;
- friend class QXcbConnection; // access QPlatformIntegration::screenAdded()
-
mutable QByteArray m_wmClass;
const char *m_instanceName;
bool m_canGrab;
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index 20c169fc53..d0e02ecdd1 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -39,7 +39,6 @@
#include "qxcbkeyboard.h"
#include "qxcbwindow.h"
#include "qxcbscreen.h"
-#include "qxcbxkbcommon.h"
#include <qpa/qwindowsysteminterface.h>
#include <qpa/qplatforminputcontext.h>
@@ -49,404 +48,20 @@
#include <QtCore/QMetaEnum>
#include <private/qguiapplication_p.h>
-#include <private/qmakearray_p.h>
-#include <xkbcommon/xkbcommon-keysyms.h>
+#if QT_CONFIG(xkb)
+#include <xkbcommon/xkbcommon-x11.h>
+#endif
#if QT_CONFIG(xcb_xinput)
#include <xcb/xinput.h>
#endif
-QT_BEGIN_NAMESPACE
-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
- Qt::ShiftModifier, // 1
- Qt::ControlModifier, // 2
- Qt::ControlModifier | Qt::ShiftModifier, // 3
- Qt::AltModifier, // 4
- Qt::AltModifier | Qt::ShiftModifier, // 5
- Qt::AltModifier | Qt::ControlModifier, // 6
- Qt::AltModifier | Qt::ShiftModifier | Qt::ControlModifier, // 7
- Qt::NoModifier // Fall-back to raw Key_*, for non-latin1 kb layouts
-};
+QT_BEGIN_NAMESPACE
Qt::KeyboardModifiers QXcbKeyboard::translateModifiers(int s) const
{
- Qt::KeyboardModifiers ret = 0;
+ Qt::KeyboardModifiers ret = Qt::NoModifier;
if (s & XCB_MOD_MASK_SHIFT)
ret |= Qt::ShiftModifier;
if (s & XCB_MOD_MASK_CONTROL)
@@ -473,7 +88,7 @@ static xcb_keysym_t getUnshiftedXKey(xcb_keysym_t unshifted, xcb_keysym_t shifte
xcb_keysym_t xlower;
xcb_keysym_t xupper;
- xkbcommon_XConvertCase(unshifted, &xlower, &xupper);
+ QXkbCommon::xkbcommon_XConvertCase(unshifted, &xlower, &xupper);
if (xlower != xupper // Check if symbol is cased
&& unshifted == xupper) { // Unshifted must be upper case
@@ -805,7 +420,12 @@ void QXcbKeyboard::updateKeymap()
updateXKBMods();
- checkForLatinLayout();
+ QXkbCommon::verifyHasLatinLayout(m_xkbKeymap.get());
+}
+
+QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const
+{
+ return QXkbCommon::possibleKeys(m_xkbState.get(), event, m_superAsMeta, m_hyperAsMeta);
}
#if QT_CONFIG(xkb)
@@ -918,281 +538,16 @@ void QXcbKeyboard::updateXKBMods()
xkb_mods.mod5 = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Mod5");
}
-static bool isLatin(xkb_keysym_t sym)
-{
- return ((sym >= 'a' && sym <= 'z') || (sym >= 'A' && sym <= 'Z'));
-}
-
-void QXcbKeyboard::checkForLatinLayout() const
-{
- const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts(m_xkbKeymap.get());
- const xcb_keycode_t minKeycode = xkb_keymap_min_keycode(m_xkbKeymap.get());
- const xcb_keycode_t maxKeycode = xkb_keymap_max_keycode(m_xkbKeymap.get());
-
- const xkb_keysym_t *keysyms = nullptr;
- int nrLatinKeys = 0;
- for (xkb_layout_index_t layout = 0; layout < layoutCount; ++layout) {
- for (xcb_keycode_t code = minKeycode; code < maxKeycode; ++code) {
- xkb_keymap_key_get_syms_by_level(m_xkbKeymap.get(), code, layout, 0, &keysyms);
- if (keysyms && isLatin(keysyms[0]))
- nrLatinKeys++;
- if (nrLatinKeys > 10) // arbitrarily chosen threshold
- return;
- }
- }
- // This means that lookupLatinKeysym() will not find anything and latin
- // key shortcuts might not work. This is a bug in the affected desktop
- // environment. Usually can be solved via system settings by adding e.g. 'us'
- // layout to the list of seleced layouts, or by using command line, "setxkbmap
- // -layout rus,en". The position of latin key based layout in the list of the
- // selected layouts is irrelevant. Properly functioning desktop environments
- // handle this behind the scenes, even if no latin key based layout has been
- // explicitly listed in the selected layouts.
- qCWarning(lcQpaKeyboard, "no keyboard layouts with latin keys present");
-}
-
-xkb_keysym_t QXcbKeyboard::lookupLatinKeysym(xkb_keycode_t keycode) const
-{
- xkb_layout_index_t layout;
- xkb_keysym_t sym = XKB_KEY_NoSymbol;
- const xkb_layout_index_t layoutCount = xkb_keymap_num_layouts_for_key(m_xkbKeymap.get(), keycode);
- const xkb_layout_index_t currentLayout = xkb_state_key_get_layout(m_xkbState.get(), keycode);
- // Look at user layouts in the order in which they are defined in system
- // settings to find a latin keysym.
- for (layout = 0; layout < layoutCount; ++layout) {
- if (layout == currentLayout)
- continue;
- const xkb_keysym_t *syms;
- xkb_level_index_t level = xkb_state_key_get_level(m_xkbState.get(), keycode, layout);
- if (xkb_keymap_key_get_syms_by_level(m_xkbKeymap.get(), keycode, layout, level, &syms) != 1)
- continue;
- if (isLatin(syms[0])) {
- sym = syms[0];
- break;
- }
- }
-
- if (sym == XKB_KEY_NoSymbol)
- return sym;
-
- xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LATCHED);
- xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LOCKED);
-
- // Check for uniqueness, consider the following setup:
- // setxkbmap -layout us,ru,us -variant dvorak,, -option 'grp:ctrl_alt_toggle' (set 'ru' as active).
- // In this setup, the user would expect to trigger a ctrl+q shortcut by pressing ctrl+<physical x key>,
- // because "US dvorak" is higher up in the layout settings list. This check verifies that an obtained
- // 'sym' can not be acquired by any other layout higher up in the user's layout list. If it can be acquired
- // then the obtained key is not unique. This prevents ctrl+<physical q key> from generating a ctrl+q
- // shortcut in the above described setup. We don't want ctrl+<physical x key> and ctrl+<physical q key> to
- // generate the same shortcut event in this case.
- const xcb_keycode_t minKeycode = xkb_keymap_min_keycode(m_xkbKeymap.get());
- const xcb_keycode_t maxKeycode = xkb_keymap_max_keycode(m_xkbKeymap.get());
- ScopedXKBState state(xkb_state_new(m_xkbKeymap.get()));
- for (xkb_layout_index_t prevLayout = 0; prevLayout < layout; ++prevLayout) {
- xkb_state_update_mask(state.get(), 0, latchedMods, lockedMods, 0, 0, prevLayout);
- for (xcb_keycode_t code = minKeycode; code < maxKeycode; ++code) {
- xkb_keysym_t prevSym = xkb_state_key_get_one_sym(state.get(), code);
- if (prevSym == sym) {
- sym = XKB_KEY_NoSymbol;
- break;
- }
- }
- }
-
- return sym;
-}
-
-static const char *qtKeyName(int qtKey)
-{
- int keyEnumIndex = qt_getQtMetaObject()->indexOfEnumerator("Key");
- QMetaEnum keyEnum = qt_getQtMetaObject()->enumerator(keyEnumIndex);
- return keyEnum.valueToKey(qtKey);
-}
-
-QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const
-{
- // turn off the modifier bits which doesn't participate in shortcuts
- Qt::KeyboardModifiers notNeeded = Qt::KeypadModifier | Qt::GroupSwitchModifier;
- Qt::KeyboardModifiers modifiers = event->modifiers() &= ~notNeeded;
- // create a fresh kb state and test against the relevant modifier combinations
- struct xkb_state *kb_state = xkb_state_new(m_xkbKeymap.get());
- if (!kb_state) {
- qWarning("QXcbKeyboard: failed to compile xkb keymap!");
- return QList<int>();
- }
- // get kb state from the master xkb_state and update the temporary kb_state
- xkb_layout_index_t lockedLayout = xkb_state_serialize_layout(m_xkbState.get(), XKB_STATE_LAYOUT_LOCKED);
- xkb_mod_mask_t latchedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LATCHED);
- xkb_mod_mask_t lockedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_LOCKED);
- xkb_mod_mask_t depressedMods = xkb_state_serialize_mods(m_xkbState.get(), XKB_STATE_MODS_DEPRESSED);
-
- xkb_state_update_mask(kb_state, depressedMods, latchedMods, lockedMods, 0, 0, lockedLayout);
- quint32 keycode = event->nativeScanCode();
- // handle shortcuts for level three and above
- xkb_layout_index_t layoutIndex = xkb_state_key_get_layout(kb_state, keycode);
- xkb_level_index_t levelIndex = 0;
- if (layoutIndex != XKB_LAYOUT_INVALID) {
- levelIndex = xkb_state_key_get_level(kb_state, keycode, layoutIndex);
- if (levelIndex == XKB_LEVEL_INVALID)
- levelIndex = 0;
- }
- if (levelIndex <= 1)
- xkb_state_update_mask(kb_state, 0, latchedMods, lockedMods, 0, 0, lockedLayout);
-
- xkb_keysym_t sym = xkb_state_key_get_one_sym(kb_state, keycode);
- if (sym == XKB_KEY_NoSymbol) {
- xkb_state_unref(kb_state);
- return QList<int>();
- }
-
- QList<int> result;
- int baseQtKey = keysymToQtKey(sym, modifiers, kb_state, keycode);
- if (baseQtKey)
- result += (baseQtKey + modifiers);
-
- xkb_mod_index_t shiftMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Shift");
- xkb_mod_index_t altMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Alt");
- xkb_mod_index_t controlMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Control");
- xkb_mod_index_t metaMod = xkb_keymap_mod_get_index(m_xkbKeymap.get(), "Meta");
-
- Q_ASSERT(shiftMod < 32);
- Q_ASSERT(altMod < 32);
- Q_ASSERT(controlMod < 32);
-
- xkb_mod_mask_t depressed;
- int qtKey = 0;
- // obtain a list of possible shortcuts for the given key event
- for (uint i = 1; i < sizeof(ModsTbl) / sizeof(*ModsTbl) ; ++i) {
- Qt::KeyboardModifiers neededMods = ModsTbl[i];
- if ((modifiers & neededMods) == neededMods) {
- if (i == 8) {
- if (isLatin(baseQtKey))
- continue;
- // add a latin key as a fall back key
- sym = lookupLatinKeysym(keycode);
- } else {
- depressed = 0;
- if (neededMods & Qt::AltModifier)
- depressed |= (1 << altMod);
- if (neededMods & Qt::ShiftModifier)
- depressed |= (1 << shiftMod);
- if (neededMods & Qt::ControlModifier)
- depressed |= (1 << controlMod);
- if (metaMod < 32 && neededMods & Qt::MetaModifier)
- depressed |= (1 << metaMod);
- xkb_state_update_mask(kb_state, depressed, latchedMods, lockedMods, 0, 0, lockedLayout);
- sym = xkb_state_key_get_one_sym(kb_state, keycode);
- }
- if (sym == XKB_KEY_NoSymbol)
- continue;
-
- Qt::KeyboardModifiers mods = modifiers & ~neededMods;
- qtKey = keysymToQtKey(sym, mods, kb_state, keycode);
- if (!qtKey || qtKey == baseQtKey)
- continue;
-
- // catch only more specific shortcuts, i.e. Ctrl+Shift+= also generates Ctrl++ and +,
- // but Ctrl++ is more specific than +, so we should skip the last one
- bool ambiguous = false;
- for (int shortcut : qAsConst(result)) {
- if (int(shortcut & ~Qt::KeyboardModifierMask) == qtKey && (shortcut & mods) == mods) {
- ambiguous = true;
- break;
- }
- }
- if (ambiguous)
- continue;
-
- result += (qtKey + mods);
- }
- }
- xkb_state_unref(kb_state);
- return result;
-}
-
-int QXcbKeyboard::keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
- struct xkb_state *state, xcb_keycode_t code) const
-{
- int qtKey = 0;
-
- // lookup from direct mapping
- if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) {
- // function keys
- qtKey = Qt::Key_F1 + (keysym - XKB_KEY_F1);
- } else if (keysym >= XKB_KEY_KP_0 && keysym <= XKB_KEY_KP_9) {
- // numeric keypad keys
- qtKey = Qt::Key_0 + (keysym - XKB_KEY_KP_0);
- } else if (isLatin(keysym)) {
- qtKey = xkbcommon_xkb_keysym_to_upper(keysym);
- } else {
- // check if we have a direct mapping
- 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;
- bool fromUnicode = qtKey == 0;
- if (fromUnicode) { // lookup from unicode
- if (modifiers & Qt::ControlModifier) {
- // Control modifier changes the text to ASCII control character, therefore we
- // can't use this text to map keysym to a qt key. We can use the same keysym
- // (it is not affectd by transformation) to obtain untransformed text. For details
- // see "Appendix A. Default Symbol Transformations" in the XKB specification.
- text = lookupStringNoKeysymTransformations(keysym);
- } else {
- text = lookupString(state, code);
- }
- if (!text.isEmpty()) {
- if (text.unicode()->isDigit()) {
- // Ensures that also non-latin digits are mapped to corresponding qt keys,
- // e.g CTRL + ۲ (arabic two), is mapped to CTRL + Qt::Key_2.
- qtKey = Qt::Key_0 + text.unicode()->digitValue();
- } else {
- qtKey = text.unicode()->toUpper().unicode();
- }
- }
- }
-
- if (rmod_masks.meta) {
- // translate Super/Hyper keys to Meta if we're using them as the MetaModifier
- if (rmod_masks.meta == rmod_masks.super && (qtKey == Qt::Key_Super_L
- || qtKey == Qt::Key_Super_R)) {
- qtKey = Qt::Key_Meta;
- } else if (rmod_masks.meta == rmod_masks.hyper && (qtKey == Qt::Key_Hyper_L
- || qtKey == Qt::Key_Hyper_R)) {
- qtKey = Qt::Key_Meta;
- }
- }
-
- if (Q_UNLIKELY(lcQpaKeyboard().isDebugEnabled())) {
- char keysymName[64];
- xkb_keysym_get_name(keysym, keysymName, sizeof(keysymName));
- QString keysymInHex = QString(QStringLiteral("0x%1")).arg(keysym, 0, 16);
- if (qtKeyName(qtKey)) {
- qCDebug(lcQpaKeyboard).nospace() << "keysym: " << keysymName << "("
- << keysymInHex << ") mapped to Qt::" << qtKeyName(qtKey) << " | text: " << text
- << " | qt key: " << qtKey << " mapped from unicode number: " << fromUnicode;
- } else {
- qCDebug(lcQpaKeyboard).nospace() << "no Qt::Key for keysym: " << keysymName
- << "(" << keysymInHex << ") | text: " << text << " | qt key: " << qtKey;
- }
- }
-
- return qtKey;
-}
-
QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection)
: QXcbObject(connection)
{
#if QT_CONFIG(xkb)
core_device_id = 0;
if (connection->hasXKB()) {
+ selectEvents();
core_device_id = xkb_x11_get_core_keyboard_device_id(xcb_connection());
if (core_device_id == -1) {
- qWarning("Qt: couldn't get core keyboard device info");
+ qCWarning(lcQpaXcb, "failed to get core keyboard device info");
return;
}
} else {
@@ -1210,6 +565,48 @@ QXcbKeyboard::~QXcbKeyboard()
xcb_key_symbols_free(m_key_symbols);
}
+void QXcbKeyboard::initialize()
+{
+ auto inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
+ QXkbCommon::setXkbContext(inputContext, m_xkbContext.get());
+}
+
+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)
@@ -1477,6 +874,12 @@ void QXcbKeyboard::resolveMaskConflicts()
rmod_masks.meta = rmod_masks.hyper;
}
}
+
+ // translate Super/Hyper keys to Meta if we're using them as the MetaModifier
+ if (rmod_masks.meta && rmod_masks.meta == rmod_masks.super)
+ m_superAsMeta = true;
+ if (rmod_masks.meta && rmod_masks.meta == rmod_masks.hyper)
+ m_hyperAsMeta = true;
}
void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type, xcb_keycode_t code,
@@ -1492,7 +895,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
if (type == QEvent::KeyPress)
targetWindow->updateNetWmUserTime(time);
- ScopedXKBState sendEventState;
+ QXkbCommon::ScopedXKBState sendEventState;
if (fromSendEvent) {
// Have a temporary keyboard state filled in from state
// this way we allow for synthetic events to have different state
@@ -1509,30 +912,13 @@ 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 text = lookupString(xkbState, code);
+ QString text = QXkbCommon::lookupString(xkbState, code);
Qt::KeyboardModifiers modifiers = translateModifiers(state);
- if (sym >= XKB_KEY_KP_Space && sym <= XKB_KEY_KP_9)
+ if (QXkbCommon::isKeypad(sym))
modifiers |= Qt::KeypadModifier;
- // Note 1: All standard key sequences on linux (as defined in platform theme)
- // that use a latin character also contain a control modifier, which is why
- // checking for Qt::ControlModifier is sufficient here. It is possible to
- // override QPlatformTheme::keyBindings() and provide custom sequences for
- // QKeySequence::StandardKey. Custom sequences probably should respect this
- // convention (alternatively, we could test against other modifiers here).
- // Note 2: The possibleKeys() shorcut mechanism is not affected by this value
- // adjustment and does its own thing.
- xcb_keysym_t latinKeysym = XKB_KEY_NoSymbol;
- if (modifiers & Qt::ControlModifier) {
- // With standard shortcuts we should prefer a latin character, this is
- // in checks like "event == QKeySequence::Copy".
- if (!isLatin(sym))
- latinKeysym = lookupLatinKeysym(code);
- }
-
- int qtcode = keysymToQtKey(latinKeysym != XKB_KEY_NoSymbol ? latinKeysym : sym,
- modifiers, xkbState, code);
+ int qtcode = QXkbCommon::keysymToQtKey(sym, modifiers, xkbState, code, m_superAsMeta, m_hyperAsMeta);
if (type == QEvent::KeyPress) {
if (m_isAutoRepeat && m_autoRepeatCode != code)
@@ -1541,7 +927,8 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
} else {
m_isAutoRepeat = false;
// Look at the next event in the queue to see if we are auto-repeating.
- connection()->checkEvent([this, time, code](xcb_generic_event_t *event, int type) {
+ 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;
@@ -1549,7 +936,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
m_autoRepeatCode = code;
}
return true;
- }, false /* removeFromQueue */);
+ });
}
bool filtered = false;
@@ -1573,28 +960,6 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
}
}
-QString QXcbKeyboard::lookupString(struct xkb_state *state, xcb_keycode_t code) const
-{
- QVarLengthArray<char, 32> chars(32);
- const int size = xkb_state_key_get_utf8(state, code, chars.data(), chars.size());
- if (Q_UNLIKELY(size + 1 > chars.size())) { // +1 for NUL
- chars.resize(size + 1);
- xkb_state_key_get_utf8(state, code, chars.data(), chars.size());
- }
- return QString::fromUtf8(chars.constData(), size);
-}
-
-QString QXcbKeyboard::lookupStringNoKeysymTransformations(xkb_keysym_t keysym) const
-{
- QVarLengthArray<char, 32> chars(32);
- const int size = xkb_keysym_to_utf8(keysym, chars.data(), chars.size());
- if (Q_UNLIKELY(size > chars.size())) {
- chars.resize(size);
- xkb_keysym_to_utf8(keysym, chars.data(), chars.size());
- }
- return QString::fromUtf8(chars.constData(), size);
-}
-
static bool fromSendEvent(const void *event)
{
// From X11 protocol: Every event contains an 8-bit type code. The most
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h
index 95915fb2e6..e35c82ad24 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.h
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.h
@@ -43,18 +43,19 @@
#include "qxcbobject.h"
#include <xcb/xcb_keysyms.h>
-
-#include <xkbcommon/xkbcommon.h>
#if QT_CONFIG(xkb)
-#include <xkbcommon/xkbcommon-x11.h>
+#define explicit dont_use_cxx_explicit
+#include <xcb/xkb.h>
+#undef explicit
#endif
+#include <xkbcommon/xkbcommon.h>
+#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
+
#include <QEvent>
QT_BEGIN_NAMESPACE
-class QWindow;
-
class QXcbKeyboard : public QXcbObject
{
public:
@@ -62,13 +63,16 @@ public:
~QXcbKeyboard();
+ void initialize();
+ void selectEvents();
+
void handleKeyPressEvent(const xcb_key_press_event_t *event);
void handleKeyReleaseEvent(const xcb_key_release_event_t *event);
Qt::KeyboardModifiers translateModifiers(int s) const;
void updateKeymap(xcb_mapping_notify_event_t *event);
void updateKeymap();
- QList<int> possibleKeys(const QKeyEvent *e) const;
+ QList<int> possibleKeys(const QKeyEvent *event) const;
// when XKEYBOARD not present on the X server
void updateXKBMods();
@@ -89,10 +93,6 @@ protected:
quint16 state, xcb_timestamp_t time, bool fromSendEvent);
void resolveMaskConflicts();
- QString lookupString(struct xkb_state *state, xcb_keycode_t code) const;
- QString lookupStringNoKeysymTransformations(xkb_keysym_t keysym) const;
- int keysymToQtKey(xcb_keysym_t keysym, Qt::KeyboardModifiers modifiers,
- struct xkb_state *state, xcb_keycode_t code) const;
typedef QMap<xcb_keysym_t, int> KeysymModifierMap;
struct xkb_keymap *keymapFromCore(const KeysymModifierMap &keysymMods);
@@ -104,9 +104,6 @@ protected:
void updateVModMapping();
void updateVModToRModMapping();
- xkb_keysym_t lookupLatinKeysym(xkb_keycode_t keycode) const;
- void checkForLatinLayout() const;
-
private:
bool m_config = false;
bool m_isAutoRepeat = false;
@@ -141,22 +138,12 @@ private:
int core_device_id;
#endif
- struct XKBStateDeleter {
- void operator()(struct xkb_state *state) const { return xkb_state_unref(state); }
- };
- struct XKBKeymapDeleter {
- void operator()(struct xkb_keymap *keymap) const { return xkb_keymap_unref(keymap); }
- };
- struct XKBContextDeleter {
- void operator()(struct xkb_context *context) const { return xkb_context_unref(context); }
- };
- using ScopedXKBState = std::unique_ptr<struct xkb_state, XKBStateDeleter>;
- using ScopedXKBKeymap = std::unique_ptr<struct xkb_keymap, XKBKeymapDeleter>;
- using ScopedXKBContext = std::unique_ptr<struct xkb_context, XKBContextDeleter>;
+ QXkbCommon::ScopedXKBState m_xkbState;
+ QXkbCommon::ScopedXKBKeymap m_xkbKeymap;
+ QXkbCommon::ScopedXKBContext m_xkbContext;
- ScopedXKBState m_xkbState;
- ScopedXKBKeymap m_xkbKeymap;
- ScopedXKBContext m_xkbContext;
+ bool m_superAsMeta = false;
+ bool m_hyperAsMeta = false;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbmain.cpp b/src/plugins/platforms/xcb/qxcbmain.cpp
index 539d033ca9..c1e37f3704 100644
--- a/src/plugins/platforms/xcb/qxcbmain.cpp
+++ b/src/plugins/platforms/xcb/qxcbmain.cpp
@@ -53,7 +53,7 @@ public:
QPlatformIntegration* QXcbIntegrationPlugin::create(const QString& system, const QStringList& parameters, int &argc, char **argv)
{
if (!system.compare(QLatin1String("xcb"), Qt::CaseInsensitive)) {
- QXcbIntegration *xcbIntegration = new QXcbIntegration(parameters, argc, argv);
+ auto xcbIntegration = new QXcbIntegration(parameters, argc, argv);
if (!xcbIntegration->hasDefaultConnection()) {
delete xcbIntegration;
return nullptr;
diff --git a/src/plugins/platforms/xcb/qxcbmime.cpp b/src/plugins/platforms/xcb/qxcbmime.cpp
index 7170d259fd..d611f86a9c 100644
--- a/src/plugins/platforms/xcb/qxcbmime.cpp
+++ b/src/plugins/platforms/xcb/qxcbmime.cpp
@@ -168,7 +168,7 @@ QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a,
if (!encoding.isEmpty()
&& atomName == format + QLatin1String(";charset=") + QLatin1String(encoding)) {
-#ifndef QT_NO_TEXTCODEC
+#if QT_CONFIG(textcodec)
if (requestedType == QVariant::String) {
QTextCodec *codec = QTextCodec::codecForName(encoding);
if (codec)
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
index 98bedea48a..899081e752 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
@@ -304,7 +304,7 @@ QPlatformNativeInterface::NativeResourceForWindowFunction QXcbNativeInterface::n
QPlatformNativeInterface::NativeResourceForBackingStoreFunction QXcbNativeInterface::nativeResourceFunctionForBackingStore(const QByteArray &resource)
{
const QByteArray lowerCaseResource = resource.toLower();
- NativeResourceForBackingStoreFunction func = handlerNativeResourceFunctionForBackingStore(resource);
+ NativeResourceForBackingStoreFunction func = handlerNativeResourceFunctionForBackingStore(lowerCaseResource);
return func;
}
@@ -412,12 +412,15 @@ void *QXcbNativeInterface::atspiBus()
auto reply = Q_XCB_REPLY(xcb_get_property, defaultConnection->xcb_connection(),
false, defaultConnection->rootWindow(),
atspiBusAtom, XCB_ATOM_STRING, 0, 128);
- Q_ASSERT(!reply->bytes_after);
+ if (!reply)
+ return nullptr;
+
char *data = (char *)xcb_get_property_value(reply.get());
int length = xcb_get_property_value_length(reply.get());
return new QByteArray(data, length);
}
- return 0;
+
+ return nullptr;
}
void QXcbNativeInterface::setAppTime(QScreen* screen, xcb_timestamp_t time)
@@ -437,20 +440,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 d1f2747bea..4656f46be5 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.h
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h
@@ -118,8 +118,8 @@ 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 QString dumpConnectionNativeWindows(const QXcbConnection *connection, WId root) const;
diff --git a/src/plugins/platforms/xcb/qxcbobject.h b/src/plugins/platforms/xcb/qxcbobject.h
index 1b98d9346b..931bed9ec1 100644
--- a/src/plugins/platforms/xcb/qxcbobject.h
+++ b/src/plugins/platforms/xcb/qxcbobject.h
@@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE
class QXcbObject
{
public:
- QXcbObject(QXcbConnection *connection = 0) : m_connection(connection) {}
+ QXcbObject(QXcbConnection *connection = nullptr) : m_connection(connection) {}
void setConnection(QXcbConnection *connection) { m_connection = connection; }
QXcbConnection *connection() const { return m_connection; }
diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index 8c0ce8dd7e..0fa0e8cd7b 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -112,13 +112,6 @@ QXcbVirtualDesktop::QXcbVirtualDesktop(QXcbConnection *connection, xcb_screen_t
xcb_depth_next(&depth_iterator);
}
-
- if (connection->hasXRandr()) {
- xcb_connection_t *conn = connection->xcb_connection();
- auto screen_info = Q_XCB_REPLY(xcb_randr_get_screen_info, conn, screen->root);
- if (screen_info)
- m_rotation = screen_info->rotation;
- }
}
QXcbVirtualDesktop::~QXcbVirtualDesktop()
@@ -154,7 +147,7 @@ void QXcbVirtualDesktop::setPrimaryScreen(QPlatformScreen *s)
{
const int idx = m_screens.indexOf(s);
Q_ASSERT(idx > -1);
- m_screens.swap(0, idx);
+ m_screens.swapItemsAt(0, idx);
}
QXcbXSettings *QXcbVirtualDesktop::xSettings() const
@@ -747,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);
diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h
index 4404a04f49..be6c45e415 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.h
+++ b/src/plugins/platforms/xcb/qxcbscreen.h
@@ -79,7 +79,7 @@ public:
QXcbScreen *screenAt(const QPoint &pos) const;
QList<QPlatformScreen *> screens() const { return m_screens; }
- void setScreens(QList<QPlatformScreen *> sl) { m_screens = sl; }
+ void setScreens(QList<QPlatformScreen *> &&sl) { m_screens = std::move(sl); }
void removeScreen(QPlatformScreen *s) { m_screens.removeOne(s); }
void addScreen(QPlatformScreen *s);
void setPrimaryScreen(QPlatformScreen *s);
@@ -134,7 +134,7 @@ private:
QString m_windowManagerName;
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
diff --git a/src/plugins/platforms/xcb/qxcbsessionmanager.h b/src/plugins/platforms/xcb/qxcbsessionmanager.h
index 0ad9445361..79c587b38d 100644
--- a/src/plugins/platforms/xcb/qxcbsessionmanager.h
+++ b/src/plugins/platforms/xcb/qxcbsessionmanager.h
@@ -85,8 +85,6 @@ public:
private:
QEventLoop *m_eventLoop;
-
- Q_DISABLE_COPY(QXcbSessionManager)
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index 69fc6c2951..396b47001d 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -64,38 +64,13 @@
#include <algorithm>
-// FIXME This workaround can be removed for xcb-icccm > 3.8
-#define class class_name
#include <xcb/xcb_icccm.h>
-#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
-#define xcb_get_wm_hints_reply xcb_icccm_get_wm_hints_reply
-#define xcb_get_wm_hints xcb_icccm_get_wm_hints
-#define xcb_get_wm_hints_unchecked xcb_icccm_get_wm_hints_unchecked
-#define xcb_set_wm_hints xcb_icccm_set_wm_hints
-#define xcb_set_wm_normal_hints xcb_icccm_set_wm_normal_hints
-#define xcb_size_hints_set_base_size xcb_icccm_size_hints_set_base_size
-#define xcb_size_hints_set_max_size xcb_icccm_size_hints_set_max_size
-#define xcb_size_hints_set_min_size xcb_icccm_size_hints_set_min_size
-#define xcb_size_hints_set_position xcb_icccm_size_hints_set_position
-#define xcb_size_hints_set_resize_inc xcb_icccm_size_hints_set_resize_inc
-#define xcb_size_hints_set_size xcb_icccm_size_hints_set_size
-#define xcb_size_hints_set_win_gravity xcb_icccm_size_hints_set_win_gravity
-#define xcb_wm_hints_set_iconic xcb_icccm_wm_hints_set_iconic
-#define xcb_wm_hints_set_normal xcb_icccm_wm_hints_set_normal
-#define xcb_wm_hints_set_input xcb_icccm_wm_hints_set_input
-#define xcb_wm_hints_t xcb_icccm_wm_hints_t
-#define XCB_WM_STATE_ICONIC XCB_ICCCM_WM_STATE_ICONIC
-#define XCB_WM_STATE_WITHDRAWN XCB_ICCCM_WM_STATE_WITHDRAWN
-#endif
-
#include <private/qguiapplication_p.h>
#include <private/qwindow_p.h>
@@ -118,8 +93,6 @@ enum {
defaultWindowHeight = 160
};
-//#ifdef NET_WM_STATE_DEBUG
-
QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(xcb_rectangle_t, Q_PRIMITIVE_TYPE);
@@ -499,13 +472,13 @@ void QXcbWindow::create()
clientMachine.size(), clientMachine.constData());
}
- xcb_wm_hints_t hints;
+ // Create WM_HINTS property on the window, so we can xcb_icccm_get_wm_hints*()
+ // from various setter functions for adjusting the hints.
+ xcb_icccm_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);
+ hints.flags = XCB_ICCCM_WM_HINT_WINDOW_GROUP;
+ hints.window_group = connection()->clientLeader();
+ xcb_icccm_set_wm_hints(xcb_connection(), m_window, &hints);
xcb_window_t leader = connection()->clientLeader();
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
@@ -532,9 +505,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);
@@ -739,20 +709,6 @@ 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);
-
// update WM_NORMAL_HINTS
propagateSizeHints();
@@ -775,11 +731,8 @@ 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();
+ setNetWmStateOnUnmappedWindow();
}
// QWidget-attribute Qt::WA_ShowWithoutActivating.
@@ -890,46 +843,18 @@ void QXcbWindow::doFocusIn()
QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
}
-static bool focusInPeeker(QXcbConnection *connection, xcb_generic_event_t *event)
-{
- if (!event) {
- // FocusIn event is not in the queue, proceed with FocusOut normally.
- QWindowSystemInterface::handleWindowActivated(nullptr, Qt::ActiveWindowFocusReason);
- return true;
- }
- uint response_type = event->response_type & ~0x80;
- if (response_type == XCB_FOCUS_IN) {
- // Ignore focus events that are being sent only because the pointer is over
- // our window, even if the input focus is in a different window.
- xcb_focus_in_event_t *e = (xcb_focus_in_event_t *) event;
- if (e->detail != XCB_NOTIFY_DETAIL_POINTER)
- return true;
- }
-
- /* We are also interested in XEMBED_FOCUS_IN events */
- if (response_type == XCB_CLIENT_MESSAGE) {
- xcb_client_message_event_t *cme = (xcb_client_message_event_t *)event;
- if (cme->type == connection->atom(QXcbAtom::_XEMBED)
- && cme->data.data32[1] == XEMBED_FOCUS_IN)
- return true;
- }
-
- return false;
-}
-
void QXcbWindow::doFocusOut()
{
connection()->setFocusWindow(nullptr);
relayFocusToModalWindow();
// Do not set the active window to nullptr if there is a FocusIn coming.
- // The FocusIn handler will update QXcbConnection::setFocusWindow() accordingly.
- connection()->addPeekFunc(focusInPeeker);
+ connection()->focusInTimer().start(400);
}
struct QtMotifWmHints {
quint32 flags, functions, decorations;
- qint32 input_mode;
- quint32 status;
+ qint32 input_mode; // unused
+ quint32 status; // unused
};
enum {
@@ -951,50 +876,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);
@@ -1023,54 +906,12 @@ 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;
}
-void QXcbWindow::setNetWmStates(NetWmStates states)
-{
- QVector<xcb_atom_t> atoms;
-
- auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
- 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
- XCB_ATOM_ATOM, 0, 1024);
- if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM && reply->value_len > 0) {
- const xcb_atom_t *data = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get()));
- atoms.resize(reply->value_len);
- memcpy((void *)&atoms.first(), (void *)data, reply->value_len * sizeof(xcb_atom_t));
- }
-
- if (states & NetWmStateAbove && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_ABOVE)))
- atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_ABOVE));
- if (states & NetWmStateBelow && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_BELOW)))
- atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_BELOW));
- if (states & NetWmStateFullScreen && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)))
- atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
- if (states & NetWmStateMaximizedHorz && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ)))
- atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ));
- if (states & NetWmStateMaximizedVert && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)))
- atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
- if (states & NetWmStateModal && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MODAL)))
- atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MODAL));
- if (states & NetWmStateStaysOnTop && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP)))
- atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP));
- if (states & NetWmStateDemandsAttention && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)))
- atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION));
-
- if (atoms.isEmpty()) {
- xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_STATE));
- } else {
- xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
- atom(QXcbAtom::_NET_WM_STATE), XCB_ATOM_ATOM, 32,
- atoms.count(), atoms.constData());
- }
- xcb_flush(xcb_connection());
-}
-
void QXcbWindow::setWindowFlags(Qt::WindowFlags flags)
{
Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
@@ -1097,23 +938,19 @@ void QXcbWindow::setWindowFlags(Qt::WindowFlags flags)
}
setWmWindowType(wmWindowTypes, flags);
- setNetWmStateWindowFlags(flags);
- setMotifWindowFlags(flags);
+ setNetWmState(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;
@@ -1171,10 +1008,21 @@ 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)
+void QXcbWindow::setNetWmState(bool set, xcb_atom_t one, xcb_atom_t two)
{
xcb_client_message_event_t event;
@@ -1194,106 +1042,32 @@ void QXcbWindow::changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two)
(const char *)&event);
}
-void QXcbWindow::setWindowState(Qt::WindowStates state)
+void QXcbWindow::setNetWmState(Qt::WindowStates state)
{
- if (state == m_windowState)
- return;
-
- if ((m_windowState & Qt::WindowMinimized) && !(state & Qt::WindowMinimized)) {
- xcb_map_window(xcb_connection(), m_window);
- } else if (!(m_windowState & Qt::WindowMinimized) && (state & Qt::WindowMinimized)) {
- xcb_client_message_event_t event;
-
- event.response_type = XCB_CLIENT_MESSAGE;
- event.format = 32;
- event.sequence = 0;
- event.window = m_window;
- event.type = atom(QXcbAtom::WM_CHANGE_STATE);
- event.data.data32[0] = XCB_WM_STATE_ICONIC;
- event.data.data32[1] = 0;
- event.data.data32[2] = 0;
- event.data.data32[3] = 0;
- event.data.data32[4] = 0;
-
- xcb_send_event(xcb_connection(), 0, xcbScreen()->root(),
- XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
- (const char *)&event);
- m_minimized = true;
- }
-
if ((m_windowState ^ state) & Qt::WindowMaximized) {
- changeNetWmState(state & Qt::WindowMaximized, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ),
- atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
+ setNetWmState(state & Qt::WindowMaximized,
+ atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ),
+ atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
}
- if ((m_windowState ^ state) & Qt::WindowFullScreen) {
- changeNetWmState(state & Qt::WindowFullScreen, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
- }
-
- connection()->sync();
- m_windowState = state;
+ if ((m_windowState ^ state) & Qt::WindowFullScreen)
+ setNetWmState(state & Qt::WindowFullScreen, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
}
-void QXcbWindow::updateMotifWmHintsBeforeMap()
+void QXcbWindow::setNetWmState(Qt::WindowFlags flags)
{
- 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;
- }
- if (window()->flags() & Qt::WindowCloseButtonHint)
- mwmhints.functions |= MWM_FUNC_CLOSE;
-
- setMotifWmHints(connection(), m_window, mwmhints);
+ setNetWmState(flags & Qt::WindowStaysOnTopHint,
+ atom(QXcbAtom::_NET_WM_STATE_ABOVE),
+ atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP));
+ setNetWmState(flags & Qt::WindowStaysOnBottomHint, atom(QXcbAtom::_NET_WM_STATE_BELOW));
}
-void QXcbWindow::updateNetWmStateBeforeMap()
+void QXcbWindow::setNetWmStateOnUnmappedWindow()
{
- NetWmStates states(0);
+ if (Q_UNLIKELY(m_mapped))
+ qCWarning(lcQpaXcb()) << "internal error: " << Q_FUNC_INFO << "called on mapped window";
+ NetWmStates states(0);
const Qt::WindowFlags flags = window()->flags();
if (flags & Qt::WindowStaysOnTopHint) {
states |= NetWmStateAbove;
@@ -1313,16 +1087,92 @@ void QXcbWindow::updateNetWmStateBeforeMap()
if (window()->modality() != Qt::NonModal)
states |= NetWmStateModal;
- setNetWmStates(states);
+ // According to EWMH:
+ // "The Window Manager should remove _NET_WM_STATE whenever a window is withdrawn".
+ // Which means that we don't have to read this property before changing it on a withdrawn
+ // window. But there are situations where users want to adjust this property as well
+ // (e4cea305ed2ba3c9f580bf9d16c59a1048af0e8a), so instead of overwriting the property
+ // we first read it and then merge our hints with the existing values, allowing a user
+ // to set custom hints.
+
+ QVector<xcb_atom_t> atoms;
+ auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(),
+ 0, m_window, atom(QXcbAtom::_NET_WM_STATE),
+ XCB_ATOM_ATOM, 0, 1024);
+ if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM && reply->value_len > 0) {
+ const xcb_atom_t *data = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get()));
+ atoms.resize(reply->value_len);
+ memcpy((void *)&atoms.first(), (void *)data, reply->value_len * sizeof(xcb_atom_t));
+ }
+
+ if (states & NetWmStateAbove && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_ABOVE)))
+ atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_ABOVE));
+ if (states & NetWmStateBelow && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_BELOW)))
+ atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_BELOW));
+ if (states & NetWmStateFullScreen && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)))
+ atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
+ if (states & NetWmStateMaximizedHorz && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ)))
+ atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ));
+ if (states & NetWmStateMaximizedVert && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)))
+ atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT));
+ if (states & NetWmStateModal && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MODAL)))
+ atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MODAL));
+ if (states & NetWmStateStaysOnTop && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP)))
+ atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP));
+ if (states & NetWmStateDemandsAttention && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)))
+ atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION));
+
+ if (atoms.isEmpty()) {
+ xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_STATE));
+ } else {
+ xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window,
+ atom(QXcbAtom::_NET_WM_STATE), XCB_ATOM_ATOM, 32,
+ atoms.count(), atoms.constData());
+ }
+ xcb_flush(xcb_connection());
}
-void QXcbWindow::setNetWmStateWindowFlags(Qt::WindowFlags flags)
+void QXcbWindow::setWindowState(Qt::WindowStates state)
{
- changeNetWmState(flags & Qt::WindowStaysOnTopHint,
- atom(QXcbAtom::_NET_WM_STATE_ABOVE),
- atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP));
- changeNetWmState(flags & Qt::WindowStaysOnBottomHint,
- atom(QXcbAtom::_NET_WM_STATE_BELOW));
+ if (state == m_windowState)
+ return;
+
+ if ((m_windowState & Qt::WindowMinimized) && !(state & Qt::WindowMinimized)) {
+ xcb_map_window(xcb_connection(), m_window);
+ } else if (!(m_windowState & Qt::WindowMinimized) && (state & Qt::WindowMinimized)) {
+ xcb_client_message_event_t event;
+
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.sequence = 0;
+ event.window = m_window;
+ event.type = atom(QXcbAtom::WM_CHANGE_STATE);
+ event.data.data32[0] = XCB_ICCCM_WM_STATE_ICONIC;
+ event.data.data32[1] = 0;
+ event.data.data32[2] = 0;
+ event.data.data32[3] = 0;
+ event.data.data32[4] = 0;
+
+ xcb_send_event(xcb_connection(), 0, xcbScreen()->root(),
+ XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT,
+ (const char *)&event);
+ m_minimized = true;
+ }
+
+ setNetWmState(state);
+
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints_unchecked(xcb_connection(), m_window);
+ xcb_icccm_wm_hints_t hints;
+ if (xcb_icccm_get_wm_hints_reply(xcb_connection(), cookie, &hints, nullptr)) {
+ if (state & Qt::WindowMinimized)
+ xcb_icccm_wm_hints_set_iconic(&hints);
+ else
+ xcb_icccm_wm_hints_set_normal(&hints);
+ xcb_icccm_set_wm_hints(xcb_connection(), m_window, &hints);
+ }
+
+ connection()->sync();
+ m_windowState = state;
}
void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp)
@@ -1399,15 +1249,14 @@ void QXcbWindow::setTransparentForMouseEvents(bool transparent)
void QXcbWindow::updateDoesNotAcceptFocus(bool doesNotAcceptFocus)
{
- xcb_get_property_cookie_t cookie = xcb_get_wm_hints_unchecked(xcb_connection(), m_window);
+ xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints_unchecked(xcb_connection(), m_window);
- xcb_wm_hints_t hints;
- if (!xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, NULL)) {
+ xcb_icccm_wm_hints_t hints;
+ if (!xcb_icccm_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);
+ xcb_icccm_wm_hints_set_input(&hints, !doesNotAcceptFocus);
+ xcb_icccm_set_wm_hints(xcb_connection(), m_window, &hints);
}
WId QXcbWindow::winId() const
@@ -1515,9 +1364,9 @@ void QXcbWindow::propagateSizeHints()
QWindowPrivate *win = qt_window_private(window());
if (!win->positionAutomatic)
- xcb_size_hints_set_position(&hints, true, rect.x(), rect.y());
+ xcb_icccm_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());
+ xcb_icccm_size_hints_set_size(&hints, true, rect.width(), rect.height());
/* Gravity describes how to interpret x and y values the next time
window needs to be positioned on a screen.
@@ -1526,7 +1375,7 @@ void QXcbWindow::propagateSizeHints()
auto gravity = win->positionPolicy == QWindowPrivate::WindowFrameInclusive
? XCB_GRAVITY_NORTH_WEST : XCB_GRAVITY_STATIC;
- xcb_size_hints_set_win_gravity(&hints, gravity);
+ xcb_icccm_size_hints_set_win_gravity(&hints, gravity);
QSize minimumSize = windowMinimumSize();
QSize maximumSize = windowMaximumSize();
@@ -1534,21 +1383,21 @@ void QXcbWindow::propagateSizeHints()
QSize sizeIncrement = windowSizeIncrement();
if (minimumSize.width() > 0 || minimumSize.height() > 0)
- xcb_size_hints_set_min_size(&hints,
- qMin(XCOORD_MAX,minimumSize.width()),
- qMin(XCOORD_MAX,minimumSize.height()));
+ xcb_icccm_size_hints_set_min_size(&hints,
+ qMin(XCOORD_MAX,minimumSize.width()),
+ qMin(XCOORD_MAX,minimumSize.height()));
if (maximumSize.width() < QWINDOWSIZE_MAX || maximumSize.height() < QWINDOWSIZE_MAX)
- xcb_size_hints_set_max_size(&hints,
- qMin(XCOORD_MAX, maximumSize.width()),
- qMin(XCOORD_MAX, maximumSize.height()));
+ xcb_icccm_size_hints_set_max_size(&hints,
+ qMin(XCOORD_MAX, maximumSize.width()),
+ qMin(XCOORD_MAX, maximumSize.height()));
if (sizeIncrement.width() > 0 || sizeIncrement.height() > 0) {
- xcb_size_hints_set_base_size(&hints, baseSize.width(), baseSize.height());
- xcb_size_hints_set_resize_inc(&hints, sizeIncrement.width(), sizeIncrement.height());
+ xcb_icccm_size_hints_set_base_size(&hints, baseSize.width(), baseSize.height());
+ xcb_icccm_size_hints_set_resize_inc(&hints, sizeIncrement.width(), sizeIncrement.height());
}
- xcb_set_wm_normal_hints(xcb_connection(), m_window, &hints);
+ xcb_icccm_set_wm_normal_hints(xcb_connection(), m_window, &hints);
}
void QXcbWindow::requestActivateWindow()
@@ -1808,7 +1657,11 @@ bool QXcbWindow::requestSystemTrayWindowDock()
bool QXcbWindow::handleNativeEvent(xcb_generic_event_t *event)
{
auto eventType = connection()->nativeInterface()->nativeEventType();
+#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+ qintptr result = 0; // Used only by MS Windows
+#else
long result = 0; // Used only by MS Windows
+#endif
return QWindowSystemInterface::handleNativeEvent(window(), eventType, event, &result);
}
@@ -1818,21 +1671,20 @@ void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event)
m_exposeRegion |= rect;
bool pending = true;
- xcb_generic_event_t *e = nullptr;
- do { // compress expose events
- e = connection()->checkEvent([this, &pending](xcb_generic_event_t *event, int type) {
- if (type != XCB_EXPOSE)
- return false;
- auto expose = reinterpret_cast<xcb_expose_event_t *>(event);
- if (expose->window != m_window)
- return false;
- if (expose->count == 0)
- pending = false;
- m_exposeRegion |= QRect(expose->x, expose->y, expose->width, expose->height);
- return true;
- });
- free(e);
- } while (e);
+
+ connection()->eventQueue()->peek(QXcbEventQueue::PeekRemoveMatchContinue,
+ [this, &pending](xcb_generic_event_t *event, int type) {
+ if (type != XCB_EXPOSE)
+ return false;
+ auto expose = reinterpret_cast<xcb_expose_event_t *>(event);
+ if (expose->window != m_window)
+ return false;
+ if (expose->count == 0)
+ pending = false;
+ m_exposeRegion |= QRect(expose->x, expose->y, expose->width, expose->height);
+ free(expose);
+ return true;
+ });
// if count is non-zero there are more expose events pending
if (event->count == 0 || !pending) {
@@ -2152,13 +2004,11 @@ void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y,
return;
// check if enter event is buffered
- auto event = connection()->checkEvent([](xcb_generic_event_t *event, int type) {
+ 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);
- if (ignoreEnterEvent(enter->mode, enter->detail))
- return false;
- return true;
+ return !ignoreEnterEvent(enter->mode, enter->detail);
});
auto enter = reinterpret_cast<xcb_enter_notify_event_t *>(event);
QXcbWindow *enterWindow = enter ? connection()->platformWindowFromId(enter->event) : nullptr;
@@ -2358,8 +2208,8 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev
if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::WM_STATE)) {
const quint32 *data = (const quint32 *)xcb_get_property_value(reply.get());
if (reply->length != 0)
- m_minimized = (data[0] == XCB_WM_STATE_ICONIC
- || (data[0] == XCB_WM_STATE_WITHDRAWN && m_minimized));
+ m_minimized = (data[0] == XCB_ICCCM_WM_STATE_ICONIC
+ || (data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN && m_minimized));
}
}
if (m_minimized)
@@ -2390,6 +2240,8 @@ void QXcbWindow::handleFocusInEvent(const xcb_focus_in_event_t *event)
// our window, even if the input focus is in a different window.
if (event->detail == XCB_NOTIFY_DETAIL_POINTER)
return;
+
+ connection()->focusInTimer().stop();
doFocusIn();
}
@@ -2617,6 +2469,7 @@ void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event)
xcbScreen()->windowShown(this);
break;
case XEMBED_FOCUS_IN:
+ connection()->focusInTimer().stop();
Qt::FocusReason reason;
switch (event->data.data32[2]) {
case XEMBED_FOCUS_FIRST:
@@ -2701,7 +2554,7 @@ void QXcbWindow::setAlertState(bool enabled)
m_alertState = enabled;
- changeNetWmState(enabled, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION));
+ setNetWmState(enabled, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION));
}
uint QXcbWindow::visualId() const
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index f7d76ed3b2..e4a04f5308 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -166,7 +166,7 @@ public:
bool needsSync() const;
void postSyncWindowRequest();
- void clearSyncWindowRequest() { m_pendingSyncRequest = 0; }
+ void clearSyncWindowRequest() { m_pendingSyncRequest = nullptr; }
QXcbScreen *xcbScreen() const;
@@ -193,22 +193,19 @@ protected:
void setImageFormatForVisual(const xcb_visualtype_t *visual);
QXcbScreen *parentScreen();
-
QXcbScreen *initialScreen() const;
- void changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two = 0);
- NetWmStates netWmStates();
- void setNetWmStates(NetWmStates);
- void setMotifWindowFlags(Qt::WindowFlags flags);
- void setNetWmStateWindowFlags(Qt::WindowFlags flags);
+ void setNetWmState(bool set, xcb_atom_t one, xcb_atom_t two = 0);
+ void setNetWmState(Qt::WindowFlags flags);
+ void setNetWmState(Qt::WindowStates state);
+ void setNetWmStateOnUnmappedWindow();
+ NetWmStates netWmStates();
- void updateMotifWmHintsBeforeMap();
- void updateNetWmStateBeforeMap();
+ void setMotifWmHints(Qt::WindowFlags flags);
void setTransparentForMouseEvents(bool transparent);
void updateDoesNotAcceptFocus(bool doesNotAcceptFocus);
- QRect windowToWmGeometry(QRect r) const;
void sendXEmbedMessage(xcb_window_t window, quint32 message,
quint32 detail = 0, quint32 data1 = 0, quint32 data2 = 0);
void handleXEmbedMessage(const xcb_client_message_event_t *event);
diff --git a/src/plugins/platforms/xcb/qxcbxkbcommon.h b/src/plugins/platforms/xcb/qxcbxkbcommon.h
deleted file mode 100644
index 422c0c0f12..0000000000
--- a/src/plugins/platforms/xcb/qxcbxkbcommon.h
+++ /dev/null
@@ -1,233 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins 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$
-**
-****************************************************************************/
-
-/* XConvertCase was copied from src/3rdparty/xkbcommon/src/keysym.c,
- which contains the following license information:
-
- Copyright 1985, 1987, 1990, 1998 The Open Group
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
- Except as contained in this notice, the names of the authors or their
- institutions shall not be used in advertising or otherwise to promote the
- sale, use or other dealings in this Software without prior written
- authorization from the authors.
-
-
-
- Copyright © 2009 Dan Nicholson
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of this software and associated documentation files (the "Software"),
- to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense,
- and/or sell copies of the Software, and to permit persons to whom the
- Software is furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice (including the next
- paragraph) shall be included in all copies or substantial portions of the
- Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
-*/
-
-/*
- The following code modifications were applied:
-
- XConvertCase() was renamed to xkbcommon_XConvertCase(), to not confuse it
- with Xlib's XConvertCase().
-
- UCSConvertCase() was renamed to qt_UCSConvertCase() and function's body was
- replaced to use Qt APIs for doing case conversion, which should give us better
- results instead of using the less complete version from keysym.c
-*/
-
-#include <xkbcommon/xkbcommon.h>
-#include <QtCore/QChar>
-
-QT_BEGIN_NAMESPACE
-
-static void qt_UCSConvertCase(uint32_t code, xkb_keysym_t *lower, xkb_keysym_t *upper)
-{
- *lower = QChar::toLower(code);
- *upper = QChar::toUpper(code);
-}
-
-void xkbcommon_XConvertCase(xkb_keysym_t sym, xkb_keysym_t *lower, xkb_keysym_t *upper)
-{
- /* Latin 1 keysym */
- if (sym < 0x100) {
- qt_UCSConvertCase(sym, lower, upper);
- return;
- }
-
- /* Unicode keysym */
- if ((sym & 0xff000000) == 0x01000000) {
- qt_UCSConvertCase((sym & 0x00ffffff), lower, upper);
- *upper |= 0x01000000;
- *lower |= 0x01000000;
- return;
- }
-
- /* Legacy keysym */
-
- *lower = sym;
- *upper = sym;
-
- switch (sym >> 8) {
- case 1: /* Latin 2 */
- /* Assume the KeySym is a legal value (ignore discontinuities) */
- if (sym == XKB_KEY_Aogonek)
- *lower = XKB_KEY_aogonek;
- else if (sym >= XKB_KEY_Lstroke && sym <= XKB_KEY_Sacute)
- *lower += (XKB_KEY_lstroke - XKB_KEY_Lstroke);
- else if (sym >= XKB_KEY_Scaron && sym <= XKB_KEY_Zacute)
- *lower += (XKB_KEY_scaron - XKB_KEY_Scaron);
- else if (sym >= XKB_KEY_Zcaron && sym <= XKB_KEY_Zabovedot)
- *lower += (XKB_KEY_zcaron - XKB_KEY_Zcaron);
- else if (sym == XKB_KEY_aogonek)
- *upper = XKB_KEY_Aogonek;
- else if (sym >= XKB_KEY_lstroke && sym <= XKB_KEY_sacute)
- *upper -= (XKB_KEY_lstroke - XKB_KEY_Lstroke);
- else if (sym >= XKB_KEY_scaron && sym <= XKB_KEY_zacute)
- *upper -= (XKB_KEY_scaron - XKB_KEY_Scaron);
- else if (sym >= XKB_KEY_zcaron && sym <= XKB_KEY_zabovedot)
- *upper -= (XKB_KEY_zcaron - XKB_KEY_Zcaron);
- else if (sym >= XKB_KEY_Racute && sym <= XKB_KEY_Tcedilla)
- *lower += (XKB_KEY_racute - XKB_KEY_Racute);
- else if (sym >= XKB_KEY_racute && sym <= XKB_KEY_tcedilla)
- *upper -= (XKB_KEY_racute - XKB_KEY_Racute);
- break;
- case 2: /* Latin 3 */
- /* Assume the KeySym is a legal value (ignore discontinuities) */
- if (sym >= XKB_KEY_Hstroke && sym <= XKB_KEY_Hcircumflex)
- *lower += (XKB_KEY_hstroke - XKB_KEY_Hstroke);
- else if (sym >= XKB_KEY_Gbreve && sym <= XKB_KEY_Jcircumflex)
- *lower += (XKB_KEY_gbreve - XKB_KEY_Gbreve);
- else if (sym >= XKB_KEY_hstroke && sym <= XKB_KEY_hcircumflex)
- *upper -= (XKB_KEY_hstroke - XKB_KEY_Hstroke);
- else if (sym >= XKB_KEY_gbreve && sym <= XKB_KEY_jcircumflex)
- *upper -= (XKB_KEY_gbreve - XKB_KEY_Gbreve);
- else if (sym >= XKB_KEY_Cabovedot && sym <= XKB_KEY_Scircumflex)
- *lower += (XKB_KEY_cabovedot - XKB_KEY_Cabovedot);
- else if (sym >= XKB_KEY_cabovedot && sym <= XKB_KEY_scircumflex)
- *upper -= (XKB_KEY_cabovedot - XKB_KEY_Cabovedot);
- break;
- case 3: /* Latin 4 */
- /* Assume the KeySym is a legal value (ignore discontinuities) */
- if (sym >= XKB_KEY_Rcedilla && sym <= XKB_KEY_Tslash)
- *lower += (XKB_KEY_rcedilla - XKB_KEY_Rcedilla);
- else if (sym >= XKB_KEY_rcedilla && sym <= XKB_KEY_tslash)
- *upper -= (XKB_KEY_rcedilla - XKB_KEY_Rcedilla);
- else if (sym == XKB_KEY_ENG)
- *lower = XKB_KEY_eng;
- else if (sym == XKB_KEY_eng)
- *upper = XKB_KEY_ENG;
- else if (sym >= XKB_KEY_Amacron && sym <= XKB_KEY_Umacron)
- *lower += (XKB_KEY_amacron - XKB_KEY_Amacron);
- else if (sym >= XKB_KEY_amacron && sym <= XKB_KEY_umacron)
- *upper -= (XKB_KEY_amacron - XKB_KEY_Amacron);
- break;
- case 6: /* Cyrillic */
- /* Assume the KeySym is a legal value (ignore discontinuities) */
- if (sym >= XKB_KEY_Serbian_DJE && sym <= XKB_KEY_Serbian_DZE)
- *lower -= (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje);
- else if (sym >= XKB_KEY_Serbian_dje && sym <= XKB_KEY_Serbian_dze)
- *upper += (XKB_KEY_Serbian_DJE - XKB_KEY_Serbian_dje);
- else if (sym >= XKB_KEY_Cyrillic_YU && sym <= XKB_KEY_Cyrillic_HARDSIGN)
- *lower -= (XKB_KEY_Cyrillic_YU - XKB_KEY_Cyrillic_yu);
- else if (sym >= XKB_KEY_Cyrillic_yu && sym <= XKB_KEY_Cyrillic_hardsign)
- *upper += (XKB_KEY_Cyrillic_YU - XKB_KEY_Cyrillic_yu);
- break;
- case 7: /* Greek */
- /* Assume the KeySym is a legal value (ignore discontinuities) */
- if (sym >= XKB_KEY_Greek_ALPHAaccent && sym <= XKB_KEY_Greek_OMEGAaccent)
- *lower += (XKB_KEY_Greek_alphaaccent - XKB_KEY_Greek_ALPHAaccent);
- else if (sym >= XKB_KEY_Greek_alphaaccent && sym <= XKB_KEY_Greek_omegaaccent &&
- sym != XKB_KEY_Greek_iotaaccentdieresis &&
- sym != XKB_KEY_Greek_upsilonaccentdieresis)
- *upper -= (XKB_KEY_Greek_alphaaccent - XKB_KEY_Greek_ALPHAaccent);
- else if (sym >= XKB_KEY_Greek_ALPHA && sym <= XKB_KEY_Greek_OMEGA)
- *lower += (XKB_KEY_Greek_alpha - XKB_KEY_Greek_ALPHA);
- else if (sym >= XKB_KEY_Greek_alpha && sym <= XKB_KEY_Greek_omega &&
- sym != XKB_KEY_Greek_finalsmallsigma)
- *upper -= (XKB_KEY_Greek_alpha - XKB_KEY_Greek_ALPHA);
- break;
- case 0x13: /* Latin 9 */
- if (sym == XKB_KEY_OE)
- *lower = XKB_KEY_oe;
- else if (sym == XKB_KEY_oe)
- *upper = XKB_KEY_OE;
- else if (sym == XKB_KEY_Ydiaeresis)
- *lower = XKB_KEY_ydiaeresis;
- break;
- }
-}
-
-xkb_keysym_t xkbcommon_xkb_keysym_to_upper(xkb_keysym_t ks)
-{
- xkb_keysym_t lower, upper;
-
- xkbcommon_XConvertCase(ks, &lower, &upper);
-
- return upper;
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/xcb-plugin.pro b/src/plugins/platforms/xcb/xcb-plugin.pro
index a2c56a3dcf..4c646d42c6 100644
--- a/src/plugins/platforms/xcb/xcb-plugin.pro
+++ b/src/plugins/platforms/xcb/xcb-plugin.pro
@@ -8,6 +8,7 @@ macos: CONFIG += no_app_extension_api_only
SOURCES = \
qxcbmain.cpp
+
OTHER_FILES += xcb.json README
PLUGIN_TYPE = platforms
diff --git a/src/plugins/platforms/xcb/xcb_qpa_lib.pro b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
index 9390d04983..34c671c8c7 100644
--- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro
+++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
@@ -5,14 +5,17 @@ DEFINES += QT_NO_FOREACH
QT += \
core-private gui-private \
service_support-private theme_support-private \
- eventdispatcher_support-private fontdatabase_support-private \
- edid_support-private
+ fontdatabase_support-private \
+ edid_support-private \
+ xkbcommon_support-private
qtHaveModule(linuxaccessibility_support-private): \
QT += linuxaccessibility_support-private
qtConfig(vulkan): QT += vulkan_support-private
+qtConfig(glib) : QMAKE_USE_PRIVATE += glib
+
SOURCES = \
qxcbclipboard.cpp \
qxcbconnection.cpp \
@@ -27,7 +30,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 +53,10 @@ HEADERS = \
qxcbimage.h \
qxcbxsettings.h \
qxcbsystemtraytracker.h \
- qxcbxkbcommon.h
+ qxcbeventqueue.h \
+ qxcbeventdispatcher.h \
+ qxcbconnection_basic.h \
+ qxcbatom.h
qtConfig(draganddrop) {
SOURCES += qxcbdrag.cpp
@@ -84,25 +95,25 @@ qtConfig(vulkan) {
}
!qtConfig(system-xcb) {
- QMAKE_USE += xcb-static xcb
+ QMAKE_USE += xcb-static
} 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
+ QMAKE_USE += \
+ xcb_icccm xcb_image xcb_keysyms xcb_randr xcb_render xcb_renderutil \
+ xcb_shape xcb_shm xcb_sync xcb_xfixes xcb_xinerama
}
+QMAKE_USE += xcb
-# libxkbcommon
-!qtConfig(xkbcommon-system) {
- qtConfig(xkb) {
- include(../../../3rdparty/xkbcommon-x11.pri)
- } else {
- include(../../../3rdparty/xkbcommon.pri)
- }
-} else {
- QMAKE_USE += xkbcommon
+QMAKE_USE += xkbcommon
+qtConfig(xkb) {
+ QMAKE_USE += xkbcommon_x11
+ qtConfig(system-xcb): QMAKE_USE += xcb_xkb
}
qtConfig(dlopen): QMAKE_USE += libdl
+# qxcbkeyboard.cpp's KeyTbl has more than 256 levels of expansion and older
+# Clang uses that as a limit (it's 1024 in current versions).
+clang:!intel_icc: QMAKE_CXXFLAGS += -ftemplate-depth=1024
+
load(qt_module)