summaryrefslogtreecommitdiffstats
path: root/src/plugins
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2018-10-25 07:21:05 +0200
committerLiang Qi <liang.qi@qt.io>2018-10-25 07:21:53 +0200
commite28e91ae99b8c3859899e04cc9370534c7c7b86d (patch)
treecca81b1e745be4f25aab78e8e917c2324594e539 /src/plugins
parent5ea233ca6782eb27adf596515cb66ef3dadc1d5e (diff)
parentebfad73b4e44fe6db8059200da105b4b87888718 (diff)
Merge remote-tracking branch 'origin/5.12' into dev
Conflicts: src/corelib/animation/qpropertyanimation.cpp src/gui/image/qicon.cpp tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp Change-Id: I3698172b7b44ebb487cb38f50fd2c4a9f8a35b21
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/doc/snippets/code/src_plugins_platforms_qnx_qqnxwindow.cpp52
-rw-r--r--src/plugins/platforms/cocoa/qcocoaglcontext.mm3
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuitem.h1
-rw-r--r--src/plugins/platforms/cocoa/qcocoamenuitem.mm18
-rw-r--r--src/plugins/platforms/cocoa/qcocoaprintdevice.h2
-rw-r--r--src/plugins/platforms/cocoa/qcocoaprintdevice.mm4
-rw-r--r--src/plugins/platforms/cocoa/qcocoasystemsettings.mm7
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm8
-rw-r--r--src/plugins/platforms/cocoa/qnsview_keys.mm2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp24
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h2
-rw-r--r--src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.cpp2
-rw-r--r--src/plugins/platforms/qnx/qqnxwindow.cpp14
-rw-r--r--src/plugins/platforms/wasm/qwasmeventtranslator.cpp7
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp43
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.h14
-rw-r--r--src/plugins/platforms/windows/qwindowsdrag.cpp4
-rw-r--r--src/plugins/platforms/windows/qwindowsdrag.h2
-rw-r--r--src/plugins/platforms/windows/qwindowsmousehandler.cpp16
-rw-r--r--src/plugins/platforms/windows/qwindowspointerhandler.cpp175
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.cpp4
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp24
-rw-r--r--src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp6
-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.cpp18
-rw-r--r--src/plugins/platforms/xcb/qxcbbackingstore.h2
-rw-r--r--src/plugins/platforms/xcb/qxcbclipboard.cpp14
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.cpp1397
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection.h443
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.cpp437
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_basic.h175
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_screens.cpp416
-rw-r--r--src/plugins/platforms/xcb/qxcbconnection_xi2.cpp61
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp4
-rw-r--r--src/plugins/platforms/xcb/qxcbeventdispatcher.cpp162
-rw-r--r--src/plugins/platforms/xcb/qxcbeventdispatcher.h117
-rw-r--r--src/plugins/platforms/xcb/qxcbeventqueue.cpp383
-rw-r--r--src/plugins/platforms/xcb/qxcbeventqueue.h162
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.cpp34
-rw-r--r--src/plugins/platforms/xcb/qxcbintegration.h1
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.cpp44
-rw-r--r--src/plugins/platforms/xcb/qxcbkeyboard.h7
-rw-r--r--src/plugins/platforms/xcb/qxcbmain.cpp2
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.cpp10
-rw-r--r--src/plugins/platforms/xcb/qxcbnativeinterface.h4
-rw-r--r--src/plugins/platforms/xcb/qxcbscreen.cpp6
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.cpp203
-rw-r--r--src/plugins/platforms/xcb/qxcbwindow.h5
-rw-r--r--src/plugins/platforms/xcb/xcb-plugin.pro1
-rw-r--r--src/plugins/platforms/xcb/xcb_qpa_lib.pro17
-rw-r--r--src/plugins/platformthemes/platformthemes.pro2
-rw-r--r--src/plugins/printsupport/cups/qppdprintdevice.cpp2
-rw-r--r--src/plugins/printsupport/cups/qppdprintdevice.h2
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac.mm73
-rw-r--r--src/plugins/styles/mac/qmacstyle_mac_p_p.h1
56 files changed, 3131 insertions, 2042 deletions
diff --git a/src/plugins/doc/snippets/code/src_plugins_platforms_qnx_qqnxwindow.cpp b/src/plugins/doc/snippets/code/src_plugins_platforms_qnx_qqnxwindow.cpp
new file mode 100644
index 0000000000..ce82533c3e
--- /dev/null
+++ b/src/plugins/doc/snippets/code/src_plugins_platforms_qnx_qqnxwindow.cpp
@@ -0,0 +1,52 @@
+/***************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+//! [0]
+ QQuickView *view = new QQuickView(parent);
+ view->create();
+ QGuiApplication::platformNativeInterface()->setWindowProperty(view->handle(), "qnxWindowGroup",
+ group);
+//! [0]
+
+//! [1]
+ QQuickView *view = new QQuickView(parent);
+ view->create();
+ QGuiApplication::platformNativeInterface()->setWindowProperty(view->handle(), "qnxWindowGroup",
+ QVariant());
+//! [1]
diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
index 1e1b3907ed..1cd77a22e4 100644
--- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm
+++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm
@@ -414,7 +414,8 @@ bool QCocoaGLContext::setDrawable(QPlatformSurface *surface)
// have the same effect as an update.
// Now we are ready to associate the view with the context
- if ((m_context.view = view) != view) {
+ m_context.view = view;
+ if (m_context.view != view) {
qCInfo(lcQpaOpenGLContext) << "Failed to set" << view << "as drawable for" << m_context;
m_updateObservers.clear();
return false;
diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.h b/src/plugins/platforms/cocoa/qcocoamenuitem.h
index 20fc741fb8..c842b08d52 100644
--- a/src/plugins/platforms/cocoa/qcocoamenuitem.h
+++ b/src/plugins/platforms/cocoa/qcocoamenuitem.h
@@ -119,7 +119,6 @@ private:
QString m_text;
QIcon m_icon;
QPointer<QCocoaMenu> m_menu;
- QFont m_font;
MenuRole m_role;
MenuRole m_detectedRole;
#ifndef QT_NO_SHORTCUT
diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm
index 21faa6d985..e54b6284e5 100644
--- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm
+++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm
@@ -173,7 +173,7 @@ void QCocoaMenuItem::setIsSeparator(bool isSeparator)
void QCocoaMenuItem::setFont(const QFont &font)
{
- m_font = font;
+ Q_UNUSED(font)
}
void QCocoaMenuItem::setRole(MenuRole role)
@@ -319,21 +319,7 @@ NSMenuItem *QCocoaMenuItem::sync()
text += QLatin1String(" (") + accel.toString(QKeySequence::NativeText) + QLatin1String(")");
#endif
- QString finalString = QPlatformTheme::removeMnemonics(text);
- bool useAttributedTitle = false;
- // Cocoa Font and title
- if (m_font.resolve()) {
- NSFont *customMenuFont = [NSFont fontWithName:m_font.family().toNSString()
- size:m_font.pointSize()];
- if (customMenuFont) {
- NSAttributedString *str = [[[NSAttributedString alloc] initWithString:finalString.toNSString()
- attributes:@{NSFontAttributeName: customMenuFont}] autorelease];
- m_native.attributedTitle = str;
- useAttributedTitle = true;
- }
- }
- if (!useAttributedTitle)
- m_native.title = finalString.toNSString();
+ m_native.title = QPlatformTheme::removeMnemonics(text).toNSString();
#ifndef QT_NO_SHORTCUT
if (accel.count() == 1) {
diff --git a/src/plugins/platforms/cocoa/qcocoaprintdevice.h b/src/plugins/platforms/cocoa/qcocoaprintdevice.h
index 20b27f3286..d267343b0e 100644
--- a/src/plugins/platforms/cocoa/qcocoaprintdevice.h
+++ b/src/plugins/platforms/cocoa/qcocoaprintdevice.h
@@ -98,7 +98,9 @@ protected:
void loadOutputBins() const override;
void loadDuplexModes() const override;
void loadColorModes() const override;
+#if QT_CONFIG(mimetype)
void loadMimeTypes() const override;
+#endif
private:
QPageSize createPageSize(const PMPaper &paper) const;
diff --git a/src/plugins/platforms/cocoa/qcocoaprintdevice.mm b/src/plugins/platforms/cocoa/qcocoaprintdevice.mm
index 24ec7ca9a4..7605dc9d1a 100644
--- a/src/plugins/platforms/cocoa/qcocoaprintdevice.mm
+++ b/src/plugins/platforms/cocoa/qcocoaprintdevice.mm
@@ -39,7 +39,9 @@
#include "qcocoaprintdevice.h"
+#if QT_CONFIG(mimetype)
#include <QtCore/qmimedatabase.h>
+#endif
#include <qdebug.h>
QT_BEGIN_NAMESPACE
@@ -417,6 +419,7 @@ QPrint::ColorMode QCocoaPrintDevice::defaultColorMode() const
return QPrint::GrayScale;
}
+#if QT_CONFIG(mimetype)
void QCocoaPrintDevice::loadMimeTypes() const
{
// TODO Check how settings affect returned list
@@ -438,6 +441,7 @@ void QCocoaPrintDevice::loadMimeTypes() const
}
m_haveMimeTypes = true;
}
+#endif // mimetype
bool QCocoaPrintDevice::openPpdFile()
{
diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm
index c1711e7cd4..aef2c6cf48 100644
--- a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm
+++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm
@@ -75,8 +75,9 @@ QPalette * qt_mac_createSystemPalette()
palette->setBrush(QPalette::Disabled, QPalette::Text, dark);
palette->setBrush(QPalette::Disabled, QPalette::ButtonText, dark);
palette->setBrush(QPalette::Disabled, QPalette::Base, backgroundBrush);
- palette->setBrush(QPalette::Active, QPalette::Base, backgroundBrush);
- palette->setBrush(QPalette::Inactive, QPalette::Base, backgroundBrush);
+ QBrush textBackgroundBrush = qt_mac_toQBrush([NSColor textBackgroundColor]);
+ palette->setBrush(QPalette::Active, QPalette::Base, textBackgroundBrush);
+ palette->setBrush(QPalette::Inactive, QPalette::Base, textBackgroundBrush);
palette->setColor(QPalette::Disabled, QPalette::Dark, QColor(191, 191, 191));
palette->setColor(QPalette::Active, QPalette::Dark, QColor(191, 191, 191));
palette->setColor(QPalette::Inactive, QPalette::Dark, QColor(191, 191, 191));
@@ -202,7 +203,7 @@ QHash<QPlatformTheme::Palette, QPalette*> qt_mac_createRolePalettes()
qt_mac_toQBrush([NSColor unemphasizedSelectedTextColor]));
} else {
baseColors = [NSColor controlAlternatingRowBackgroundColors];
- activeHighlightColor = [NSColor selectedControlColor];
+ activeHighlightColor = [NSColor alternateSelectedControlColor];
pal.setBrush(QPalette::Inactive, QPalette::HighlightedText,
pal.brush(QPalette::Active, QPalette::Text));
}
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index b2d1a80097..07c1e8ca3a 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -1720,6 +1720,14 @@ void QCocoaWindow::applyContentBorderThickness(NSWindow *window)
[window setStyleMask:[window styleMask] | NSWindowStyleMaskTexturedBackground];
window.titlebarAppearsTransparent = YES;
+ // Setting titlebarAppearsTransparent to YES means that the border thickness has to account
+ // for the title bar height as well, otherwise sheets will not be presented at the correct
+ // position, which should be (title bar height + top content border size).
+ const NSRect frameRect = window.frame;
+ const NSRect contentRect = [window contentRectForFrameRect:frameRect];
+ const CGFloat titlebarHeight = frameRect.size.height - contentRect.size.height;
+ effectiveTopContentBorderThickness += titlebarHeight;
+
[window setContentBorderThickness:effectiveTopContentBorderThickness forEdge:NSMaxYEdge];
[window setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
diff --git a/src/plugins/platforms/cocoa/qnsview_keys.mm b/src/plugins/platforms/cocoa/qnsview_keys.mm
index 28db532ddc..ad751279bb 100644
--- a/src/plugins/platforms/cocoa/qnsview_keys.mm
+++ b/src/plugins/platforms/cocoa/qnsview_keys.mm
@@ -53,7 +53,7 @@
qtMods |= Qt::AltModifier;
if (modifierFlags & NSEventModifierFlagCommand)
qtMods |= dontSwapCtrlAndMeta ? Qt::MetaModifier : Qt::ControlModifier;
- if (modifierFlags & NSEventModifierFlagCommand)
+ if (modifierFlags & NSEventModifierFlagNumericPad)
qtMods |= Qt::KeypadModifier;
return qtMods;
}
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
index 0cbb494c2f..24f82e7843 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.cpp
@@ -149,16 +149,32 @@ QPlatformCursor *QEglFSKmsGbmScreen::cursor() const
}
}
-gbm_surface *QEglFSKmsGbmScreen::createSurface()
+gbm_surface *QEglFSKmsGbmScreen::createSurface(EGLConfig eglConfig)
{
if (!m_gbm_surface) {
- uint32_t gbmFormat = drmFormatToGbmFormat(m_output.drm_format);
- qCDebug(qLcEglfsKmsDebug, "Creating gbm_surface for screen %s with format 0x%x", qPrintable(name()), gbmFormat);
- m_gbm_surface = gbm_surface_create(static_cast<QEglFSKmsGbmDevice *>(device())->gbmDevice(),
+ qCDebug(qLcEglfsKmsDebug, "Creating gbm_surface for screen %s", qPrintable(name()));
+
+ const auto gbmDevice = static_cast<QEglFSKmsGbmDevice *>(device())->gbmDevice();
+ EGLint native_format = -1;
+ EGLBoolean success = eglGetConfigAttrib(display(), eglConfig, EGL_NATIVE_VISUAL_ID, &native_format);
+ qCDebug(qLcEglfsKmsDebug) << "Got native format" << hex << native_format << dec << "from eglGetConfigAttrib() with return code" << bool(success);
+
+ if (success)
+ m_gbm_surface = gbm_surface_create(gbmDevice,
+ rawGeometry().width(),
+ rawGeometry().height(),
+ native_format,
+ GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+
+ if (!m_gbm_surface) { // fallback for older drivers
+ uint32_t gbmFormat = drmFormatToGbmFormat(m_output.drm_format);
+ qCDebug(qLcEglfsKmsDebug, "Could not create surface with EGL_NATIVE_VISUAL_ID, falling back to format %x", gbmFormat);
+ m_gbm_surface = gbm_surface_create(gbmDevice,
rawGeometry().width(),
rawGeometry().height(),
gbmFormat,
GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+ }
}
return m_gbm_surface; // not owned, gets destroyed in QEglFSKmsGbmIntegration::destroyNativeWindow() via QEglFSKmsGbmWindow::invalidateSurface()
}
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h
index f5a2122723..b94f44b7b1 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmscreen.h
@@ -59,7 +59,7 @@ public:
QPlatformCursor *cursor() const override;
- gbm_surface *createSurface();
+ gbm_surface *createSurface(EGLConfig eglConfig);
void resetSurface();
void initCloning(QPlatformScreen *screenThisScreenClones,
diff --git a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.cpp b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.cpp
index 110a592dec..65a7c4f38a 100644
--- a/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.cpp
+++ b/src/plugins/platforms/eglfs/deviceintegration/eglfs_kms/qeglfskmsgbmwindow.cpp
@@ -53,7 +53,7 @@ void QEglFSKmsGbmWindow::resetSurface()
m_config = QEglFSDeviceIntegration::chooseConfig(display, platformFormat);
m_format = q_glFormatFromConfig(display, m_config, platformFormat);
// One fullscreen window per screen -> the native window is simply the gbm_surface the screen created.
- m_window = reinterpret_cast<EGLNativeWindowType>(gbmScreen->createSurface());
+ m_window = reinterpret_cast<EGLNativeWindowType>(gbmScreen->createSurface(m_config));
PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC createPlatformWindowSurface = nullptr;
const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp
index 7c98b29348..7644e28b44 100644
--- a/src/plugins/platforms/qnx/qqnxwindow.cpp
+++ b/src/plugins/platforms/qnx/qqnxwindow.cpp
@@ -132,22 +132,12 @@ DECLARE_DEBUG_VAR(statistics)
setWindowProperty function of the native interface to set the \e qnxWindowGroup property
to the desired value, for example:
- \code
- QQuickView *view = new QQuickView(parent);
- view->create();
- QGuiApplication::platformNativeInterface()->setWindowProperty(view->handle(), "qnxWindowGroup",
- group);
- \endcode
+ \snippet code/src_plugins_platforms_qnx_qqnxwindow.cpp 0
To leave the current window group, one passes a null value for the property value,
for example:
- \code
- QQuickView *view = new QQuickView(parent);
- view->create();
- QGuiApplication::platformNativeInterface()->setWindowProperty(view->handle(), "qnxWindowGroup",
- QVariant());
- \endcode
+ \snippet code/src_plugins_platforms_qnx_qqnxwindow.cpp 1
\section1 Window Id
diff --git a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
index cc48c15b64..23251fd610 100644
--- a/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
+++ b/src/plugins/platforms/wasm/qwasmeventtranslator.cpp
@@ -50,7 +50,7 @@ using namespace emscripten;
// the existing switching code in QtGui, but for now do it here.
static bool g_usePlatformMacCtrlMetaSwitching = false;
-bool g_useNaturalScrolling = false;
+bool g_useNaturalScrolling = true; // natural scrolling is default on linux/windows
void setNaturalScrolling(bool use) {
g_useNaturalScrolling = use;
@@ -98,7 +98,7 @@ QWasmEventTranslator::QWasmEventTranslator(QObject *parent)
g_usePlatformMacCtrlMetaSwitching = (platform == MacOSPlatform);
if (platform == MacOSPlatform) {
- g_useNaturalScrolling = true; //make this default on macOS
+ g_useNaturalScrolling = false; // make this !default on macOS
EM_ASM(
if (window.safari !== undefined) {//this only works on safari
Module["canvas"].addEventListener('wheel', mouseWheelEvent);
@@ -492,6 +492,9 @@ int QWasmEventTranslator::wheel_cb(int eventType, const EmscriptenWheelEvent *wh
if (wheelEvent->deltaX != 0) pixelDelta.setX(wheelEvent->deltaX * scrollFactor);
QWindowSystemInterface::handleWheelEvent(window2, timestamp, localPoint, globalPoint, QPoint(), pixelDelta, modifiers);
+
+ QWasmEventDispatcher::maintainTimers();
+
return 1;
}
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index 253aeb9d76..1f80ac5872 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -148,7 +148,7 @@ static inline bool sessionManagerInteractionBlocked() { return false; }
static inline int windowDpiAwareness(HWND hwnd)
{
- return QWindowsContext::user32dll.getWindowDpiAwarenessContext && QWindowsContext::user32dll.getWindowDpiAwarenessContext
+ return QWindowsContext::user32dll.getWindowDpiAwarenessContext && QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext
? QWindowsContext::user32dll.getAwarenessFromDpiAwarenessContext(QWindowsContext::user32dll.getWindowDpiAwarenessContext(hwnd))
: -1;
}
@@ -213,6 +213,7 @@ void QWindowsUser32DLL::init()
enableNonClientDpiScaling = (EnableNonClientDpiScaling)library.resolve("EnableNonClientDpiScaling");
getWindowDpiAwarenessContext = (GetWindowDpiAwarenessContext)library.resolve("GetWindowDpiAwarenessContext");
getAwarenessFromDpiAwarenessContext = (GetAwarenessFromDpiAwarenessContext)library.resolve("GetAwarenessFromDpiAwarenessContext");
+ systemParametersInfoForDpi = (SystemParametersInfoForDpi)library.resolve("SystemParametersInfoForDpi");
}
}
@@ -916,6 +917,45 @@ QByteArray QWindowsContext::comErrorString(HRESULT hr)
return result;
}
+bool QWindowsContext::systemParametersInfo(unsigned action, unsigned param, void *out,
+ unsigned dpi)
+{
+ const BOOL result = QWindowsContext::user32dll.systemParametersInfoForDpi != nullptr && dpi != 0
+ ? QWindowsContext::user32dll.systemParametersInfoForDpi(action, param, out, 0, dpi)
+ : SystemParametersInfo(action, param, out, 0);
+ return result == TRUE;
+}
+
+bool QWindowsContext::systemParametersInfoForScreen(unsigned action, unsigned param, void *out,
+ const QPlatformScreen *screen)
+{
+ return systemParametersInfo(action, param, out, screen ? screen->logicalDpi().first : 0);
+}
+
+bool QWindowsContext::systemParametersInfoForWindow(unsigned action, unsigned param, void *out,
+ const QPlatformWindow *win)
+{
+ return systemParametersInfoForScreen(action, param, out, win ? win->screen() : nullptr);
+}
+
+bool QWindowsContext::nonClientMetrics(NONCLIENTMETRICS *ncm, unsigned dpi)
+{
+ memset(ncm, 0, sizeof(NONCLIENTMETRICS));
+ ncm->cbSize = sizeof(NONCLIENTMETRICS);
+ return systemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm->cbSize, ncm, dpi);
+}
+
+bool QWindowsContext::nonClientMetricsForScreen(NONCLIENTMETRICS *ncm,
+ const QPlatformScreen *screen)
+{
+ return nonClientMetrics(ncm, screen ? screen->logicalDpi().first : 0);
+}
+
+bool QWindowsContext::nonClientMetricsForWindow(NONCLIENTMETRICS *ncm, const QPlatformWindow *win)
+{
+ return nonClientMetricsForScreen(ncm, win ? win->screen() : nullptr);
+}
+
static inline QWindowsInputContext *windowsInputContext()
{
return qobject_cast<QWindowsInputContext *>(QWindowsIntegration::instance()->inputContext());
@@ -960,6 +1000,7 @@ static inline bool isInputMessage(UINT m)
case WM_IME_STARTCOMPOSITION:
case WM_IME_ENDCOMPOSITION:
case WM_IME_COMPOSITION:
+ case WM_INPUT:
case WM_TOUCH:
case WM_MOUSEHOVER:
case WM_MOUSELEAVE:
diff --git a/src/plugins/platforms/windows/qwindowscontext.h b/src/plugins/platforms/windows/qwindowscontext.h
index 19e9c26130..fd6c72668c 100644
--- a/src/plugins/platforms/windows/qwindowscontext.h
+++ b/src/plugins/platforms/windows/qwindowscontext.h
@@ -70,6 +70,7 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaTrayIcon)
class QWindow;
class QPlatformScreen;
+class QPlatformWindow;
class QWindowsMenuBar;
class QWindowsScreenManager;
class QWindowsTabletSupport;
@@ -104,6 +105,7 @@ struct QWindowsUser32DLL
typedef BOOL (WINAPI *EnableNonClientDpiScaling)(HWND);
typedef int (WINAPI *GetWindowDpiAwarenessContext)(HWND);
typedef int (WINAPI *GetAwarenessFromDpiAwarenessContext)(int);
+ typedef BOOL (WINAPI *SystemParametersInfoForDpi)(UINT, UINT, PVOID, UINT, UINT);
// Windows pointer functions (Windows 8 or later).
EnableMouseInPointer enableMouseInPointer = nullptr;
@@ -132,6 +134,7 @@ struct QWindowsUser32DLL
EnableNonClientDpiScaling enableNonClientDpiScaling = nullptr;
GetWindowDpiAwarenessContext getWindowDpiAwarenessContext = nullptr;
GetAwarenessFromDpiAwarenessContext getAwarenessFromDpiAwarenessContext = nullptr;
+ SystemParametersInfoForDpi systemParametersInfoForDpi = nullptr;
};
// Shell scaling library (Windows 8.1 onwards)
@@ -237,6 +240,17 @@ public:
bool asyncExpose() const;
void setAsyncExpose(bool value);
+ static bool systemParametersInfo(unsigned action, unsigned param, void *out, unsigned dpi = 0);
+ static bool systemParametersInfoForScreen(unsigned action, unsigned param, void *out,
+ const QPlatformScreen *screen = nullptr);
+ static bool systemParametersInfoForWindow(unsigned action, unsigned param, void *out,
+ const QPlatformWindow *win = nullptr);
+ static bool nonClientMetrics(NONCLIENTMETRICS *ncm, unsigned dpi = 0);
+ static bool nonClientMetricsForScreen(NONCLIENTMETRICS *ncm,
+ const QPlatformScreen *screen = nullptr);
+ static bool nonClientMetricsForWindow(NONCLIENTMETRICS *ncm,
+ const QPlatformWindow *win = nullptr);
+
static DWORD readAdvancedExplorerSettings(const wchar_t *subKey, DWORD defaultValue);
QTouchDevice *touchDevice() const;
diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp
index b7d225cb00..ee82b2f022 100644
--- a/src/plugins/platforms/windows/qwindowsdrag.cpp
+++ b/src/plugins/platforms/windows/qwindowsdrag.cpp
@@ -652,6 +652,7 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState,
*/
bool QWindowsDrag::m_canceled = false;
+bool QWindowsDrag::m_dragging = false;
QWindowsDrag::QWindowsDrag() = default;
@@ -699,7 +700,10 @@ Qt::DropAction QWindowsDrag::drag(QDrag *drag)
const DWORD allowedEffects = translateToWinDragEffects(possibleActions);
qCDebug(lcQpaMime) << '>' << __FUNCTION__ << "possible Actions=0x"
<< hex << int(possibleActions) << "effects=0x" << allowedEffects << dec;
+ // Indicate message handlers we are in DoDragDrop() event loop.
+ QWindowsDrag::m_dragging = true;
const HRESULT r = DoDragDrop(dropDataObject, windowDropSource, allowedEffects, &resultEffect);
+ QWindowsDrag::m_dragging = false;
const DWORD reportedPerformedEffect = dropDataObject->reportedPerformedEffect();
if (r == DRAGDROP_S_DROP) {
if (reportedPerformedEffect == DROPEFFECT_MOVE && resultEffect != DROPEFFECT_MOVE) {
diff --git a/src/plugins/platforms/windows/qwindowsdrag.h b/src/plugins/platforms/windows/qwindowsdrag.h
index f116e50cbf..5f30c59882 100644
--- a/src/plugins/platforms/windows/qwindowsdrag.h
+++ b/src/plugins/platforms/windows/qwindowsdrag.h
@@ -92,6 +92,7 @@ public:
static QWindowsDrag *instance();
void cancelDrag() override { QWindowsDrag::m_canceled = true; }
static bool isCanceled() { return QWindowsDrag::m_canceled; }
+ static bool isDragging() { return QWindowsDrag::m_dragging; }
IDataObject *dropDataObject() const { return m_dropDataObject; }
void setDropDataObject(IDataObject *dataObject) { m_dropDataObject = dataObject; }
@@ -102,6 +103,7 @@ public:
private:
static bool m_canceled;
+ static bool m_dragging;
QWindowsDropMimeData m_dropData;
IDataObject *m_dropDataObject = nullptr;
diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
index c1c275144f..fcdd179a31 100644
--- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp
+++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp
@@ -207,26 +207,26 @@ static inline MouseEvent eventFromMsg(const MSG &msg)
return {QEvent::MouseButtonPress, Qt::LeftButton};
case WM_LBUTTONUP:
return {QEvent::MouseButtonRelease, Qt::LeftButton};
- case WM_LBUTTONDBLCLK:
- return {QEvent::MouseButtonDblClick, Qt::LeftButton};
+ case WM_LBUTTONDBLCLK: // Qt QPA does not handle double clicks, send as press
+ return {QEvent::MouseButtonPress, Qt::LeftButton};
case WM_MBUTTONDOWN:
return {QEvent::MouseButtonPress, Qt::MidButton};
case WM_MBUTTONUP:
return {QEvent::MouseButtonRelease, Qt::MidButton};
case WM_MBUTTONDBLCLK:
- return {QEvent::MouseButtonDblClick, Qt::MidButton};
+ return {QEvent::MouseButtonPress, Qt::MidButton};
case WM_RBUTTONDOWN:
return {QEvent::MouseButtonPress, Qt::RightButton};
case WM_RBUTTONUP:
return {QEvent::MouseButtonRelease, Qt::RightButton};
case WM_RBUTTONDBLCLK:
- return {QEvent::MouseButtonDblClick, Qt::RightButton};
+ return {QEvent::MouseButtonPress, Qt::RightButton};
case WM_XBUTTONDOWN:
return {QEvent::MouseButtonPress, extraButton(msg.wParam)};
case WM_XBUTTONUP:
return {QEvent::MouseButtonRelease, extraButton(msg.wParam)};
case WM_XBUTTONDBLCLK:
- return {QEvent::MouseButtonDblClick, extraButton(msg.wParam)};
+ return {QEvent::MouseButtonPress, extraButton(msg.wParam)};
case WM_NCMOUSEMOVE:
return {QEvent::NonClientAreaMouseMove, Qt::NoButton};
case WM_NCLBUTTONDOWN:
@@ -234,19 +234,19 @@ static inline MouseEvent eventFromMsg(const MSG &msg)
case WM_NCLBUTTONUP:
return {QEvent::NonClientAreaMouseButtonRelease, Qt::LeftButton};
case WM_NCLBUTTONDBLCLK:
- return {QEvent::NonClientAreaMouseButtonDblClick, Qt::LeftButton};
+ return {QEvent::NonClientAreaMouseButtonPress, Qt::LeftButton};
case WM_NCMBUTTONDOWN:
return {QEvent::NonClientAreaMouseButtonPress, Qt::MidButton};
case WM_NCMBUTTONUP:
return {QEvent::NonClientAreaMouseButtonRelease, Qt::MidButton};
case WM_NCMBUTTONDBLCLK:
- return {QEvent::NonClientAreaMouseButtonDblClick, Qt::MidButton};
+ return {QEvent::NonClientAreaMouseButtonPress, Qt::MidButton};
case WM_NCRBUTTONDOWN:
return {QEvent::NonClientAreaMouseButtonPress, Qt::RightButton};
case WM_NCRBUTTONUP:
return {QEvent::NonClientAreaMouseButtonRelease, Qt::RightButton};
case WM_NCRBUTTONDBLCLK:
- return {QEvent::NonClientAreaMouseButtonDblClick, Qt::RightButton};
+ return {QEvent::NonClientAreaMouseButtonPress, Qt::RightButton};
default: // WM_MOUSELEAVE
break;
}
diff --git a/src/plugins/platforms/windows/qwindowspointerhandler.cpp b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
index c5acc38e7c..21dc0cd577 100644
--- a/src/plugins/platforms/windows/qwindowspointerhandler.cpp
+++ b/src/plugins/platforms/windows/qwindowspointerhandler.cpp
@@ -50,6 +50,9 @@
#include "qwindowswindow.h"
#include "qwindowsintegration.h"
#include "qwindowsscreen.h"
+#if QT_CONFIG(draganddrop)
+# include "qwindowsdrag.h"
+#endif
#include <qpa/qwindowsysteminterface.h>
#include <QtGui/qguiapplication.h>
@@ -60,6 +63,7 @@
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qoperatingsystemversion.h>
+#include <QtCore/qqueue.h>
#include <algorithm>
@@ -75,6 +79,111 @@ enum {
QT_PT_TOUCHPAD = 5, // MinGW is missing PT_TOUCHPAD
};
+struct PointerTouchEventInfo {
+ QPointer<QWindow> window;
+ QList<QWindowSystemInterface::TouchPoint> points;
+ Qt::KeyboardModifiers modifiers;
+};
+
+struct PointerTabletEventInfo {
+ QPointer<QWindow> window;
+ QPointF local;
+ QPointF global;
+ int device;
+ int pointerType;
+ Qt::MouseButtons buttons;
+ qreal pressure;
+ int xTilt;
+ int yTilt;
+ qreal tangentialPressure;
+ qreal rotation;
+ int z;
+ qint64 uid;
+ Qt::KeyboardModifiers modifiers;
+};
+
+static QQueue<PointerTouchEventInfo> touchEventQueue;
+static QQueue<PointerTabletEventInfo> tabletEventQueue;
+
+static void enqueueTouchEvent(QWindow *window,
+ const QList<QWindowSystemInterface::TouchPoint> &points,
+ Qt::KeyboardModifiers modifiers)
+{
+ PointerTouchEventInfo eventInfo;
+ eventInfo.window = window;
+ eventInfo.points = points;
+ eventInfo.modifiers = modifiers;
+ touchEventQueue.enqueue(eventInfo);
+}
+
+static void enqueueTabletEvent(QWindow *window, const QPointF &local, const QPointF &global,
+ int device, int pointerType, Qt::MouseButtons buttons, qreal pressure,
+ int xTilt, int yTilt, qreal tangentialPressure, qreal rotation,
+ int z, qint64 uid, Qt::KeyboardModifiers modifiers)
+{
+ PointerTabletEventInfo eventInfo;
+ eventInfo.window = window;
+ eventInfo.local = local;
+ eventInfo.global = global;
+ eventInfo.device = device;
+ eventInfo.pointerType = pointerType;
+ eventInfo.buttons = buttons;
+ eventInfo.pressure = pressure;
+ eventInfo.xTilt = xTilt;
+ eventInfo.yTilt = yTilt;
+ eventInfo.tangentialPressure = tangentialPressure;
+ eventInfo.rotation = rotation;
+ eventInfo.z = z;
+ eventInfo.uid = uid;
+ eventInfo.modifiers = modifiers;
+ tabletEventQueue.enqueue(eventInfo);
+}
+
+static void flushTouchEvents(QTouchDevice *touchDevice)
+{
+ while (!touchEventQueue.isEmpty()) {
+ PointerTouchEventInfo eventInfo = touchEventQueue.dequeue();
+ if (eventInfo.window) {
+ QWindowSystemInterface::handleTouchEvent(eventInfo.window,
+ touchDevice,
+ eventInfo.points,
+ eventInfo.modifiers);
+ }
+ }
+}
+
+static void flushTabletEvents()
+{
+ while (!tabletEventQueue.isEmpty()) {
+ PointerTabletEventInfo eventInfo = tabletEventQueue.dequeue();
+ if (eventInfo.window) {
+ QWindowSystemInterface::handleTabletEvent(eventInfo.window,
+ eventInfo.local,
+ eventInfo.global,
+ eventInfo.device,
+ eventInfo.pointerType,
+ eventInfo.buttons,
+ eventInfo.pressure,
+ eventInfo.xTilt,
+ eventInfo.yTilt,
+ eventInfo.tangentialPressure,
+ eventInfo.rotation,
+ eventInfo.z,
+ eventInfo.uid,
+ eventInfo.modifiers);
+ }
+ }
+}
+
+static bool draggingActive()
+{
+#if QT_CONFIG(draganddrop)
+ return QWindowsDrag::isDragging();
+#else
+ return false;
+#endif
+}
+
bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result)
{
*result = 0;
@@ -171,7 +280,7 @@ bool QWindowsPointerHandler::translatePointerEvent(QWindow *window, HWND hwnd, Q
return false;
}
-static void getMouseEventInfo(UINT message, POINTER_BUTTON_CHANGE_TYPE changeType, QPoint globalPos, QEvent::Type *eventType, Qt::MouseButton *mouseButton)
+static void getMouseEventInfo(UINT message, POINTER_BUTTON_CHANGE_TYPE changeType, QEvent::Type *eventType, Qt::MouseButton *mouseButton)
{
static const QHash<POINTER_BUTTON_CHANGE_TYPE, Qt::MouseButton> buttonMapping {
{POINTER_CHANGE_FIRSTBUTTON_DOWN, Qt::LeftButton},
@@ -225,28 +334,6 @@ static void getMouseEventInfo(UINT message, POINTER_BUTTON_CHANGE_TYPE changeTyp
}
*mouseButton = buttonMapping.value(changeType, Qt::NoButton);
-
- // Pointer messages lack a double click indicator. Check if this is the case here.
- if (*eventType == QEvent::MouseButtonPress ||
- *eventType == QEvent::NonClientAreaMouseButtonPress) {
- static LONG lastTime = 0;
- static Qt::MouseButton lastButton = Qt::NoButton;
- static QEvent::Type lastEvent = QEvent::None;
- static QPoint lastPos;
- LONG messageTime = GetMessageTime();
- if (*mouseButton == lastButton
- && *eventType == lastEvent
- && messageTime - lastTime < (LONG)GetDoubleClickTime()
- && qAbs(globalPos.x() - lastPos.x()) < GetSystemMetrics(SM_CXDOUBLECLK)
- && qAbs(globalPos.y() - lastPos.y()) < GetSystemMetrics(SM_CYDOUBLECLK)) {
- *eventType = nonClient ? QEvent::NonClientAreaMouseButtonDblClick :
- QEvent::MouseButtonDblClick;
- }
- lastTime = messageTime;
- lastButton = *mouseButton;
- lastEvent = *eventType;
- lastPos = globalPos;
- }
}
static QWindow *getWindowUnderPointer(QWindow *window, QPoint globalPos)
@@ -358,7 +445,7 @@ bool QWindowsPointerHandler::translateMouseTouchPadEvent(QWindow *window, HWND h
QEvent::Type eventType;
Qt::MouseButton button;
- getMouseEventInfo(msg.message, pointerInfo->ButtonChangeType, globalPos, &eventType, &button);
+ getMouseEventInfo(msg.message, pointerInfo->ButtonChangeType, &eventType, &button);
if (et & QtWindows::NonClientEventFlag) {
QWindowSystemInterface::handleFrameStrutMouseEvent(window, localPos, globalPos, mouseButtons, button, eventType,
@@ -426,6 +513,9 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
if (et & QtWindows::NonClientEventFlag)
return false; // Let DefWindowProc() handle Non Client messages.
+ if (draggingActive())
+ return false; // Let DoDragDrop() loop handle it.
+
if (count < 1)
return false;
@@ -452,6 +542,8 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
QList<QWindowSystemInterface::TouchPoint> touchPoints;
+ bool primaryPointer = false;
+
if (QWindowsContext::verbose > 1)
qCDebug(lcQpaEvents).noquote().nospace() << showbase
<< __FUNCTION__
@@ -494,16 +586,23 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
touchPoint.state = stationaryTouchPoint ? Qt::TouchPointStationary : Qt::TouchPointMoved;
m_lastTouchPositions.insert(touchPoint.id, touchPoint.normalPosition);
}
+ if (touchInfo[i].pointerInfo.pointerFlags & POINTER_FLAG_PRIMARY)
+ primaryPointer = true;
+
touchPoints.append(touchPoint);
// Avoid getting repeated messages for this frame if there are multiple pointerIds
QWindowsContext::user32dll.skipPointerFrameMessages(touchInfo[i].pointerInfo.pointerId);
}
-
- QWindowSystemInterface::handleTouchEvent(window, m_touchDevice, touchPoints,
- QWindowsKeyMapper::queryKeyboardModifiers());
-
- return true;
+ if (primaryPointer) {
+ // Postpone event delivery to avoid hanging inside DoDragDrop().
+ // Only the primary pointer will generate mouse messages.
+ enqueueTouchEvent(window, touchPoints, QWindowsKeyMapper::queryKeyboardModifiers());
+ } else {
+ QWindowSystemInterface::handleTouchEvent(window, m_touchDevice, touchPoints,
+ QWindowsKeyMapper::queryKeyboardModifiers());
+ }
+ return false; // Allow mouse messages to be generated.
}
bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et,
@@ -512,6 +611,9 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
if (et & QtWindows::NonClientEventFlag)
return false; // Let DefWindowProc() handle Non Client messages.
+ if (draggingActive())
+ return false; // Let DoDragDrop() loop handle it.
+
POINTER_PEN_INFO *penInfo = static_cast<POINTER_PEN_INFO *>(vPenInfo);
RECT pRect, dRect;
@@ -592,20 +694,25 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
}
const Qt::KeyboardModifiers keyModifiers = QWindowsKeyMapper::queryKeyboardModifiers();
- QWindowSystemInterface::handleTabletEvent(target, localPos, hiResGlobalPos, device, type, mouseButtons,
- pressure, xTilt, yTilt, tangentialPressure, rotation, z,
- pointerId, keyModifiers);
- break;
+ // Postpone event delivery to avoid hanging inside DoDragDrop().
+ enqueueTabletEvent(target, localPos, hiResGlobalPos, device, type, mouseButtons,
+ pressure, xTilt, yTilt, tangentialPressure, rotation, z,
+ pointerId, keyModifiers);
+ return false; // Allow mouse messages to be generated.
}
}
return true;
}
-// SetCursorPos()/TrackMouseEvent() will generate old-style WM_MOUSE messages. Handle them here.
+// Process old-style mouse messages here.
bool QWindowsPointerHandler::translateMouseEvent(QWindow *window, HWND hwnd, QtWindows::WindowsEventType et, MSG msg, LRESULT *result)
{
Q_UNUSED(et);
+ // Generate enqueued events.
+ flushTouchEvents(m_touchDevice);
+ flushTabletEvents();
+
*result = 0;
if (msg.message != WM_MOUSELEAVE && msg.message != WM_MOUSEMOVE)
return false;
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index d01a7b0a8a..4b70e915a8 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -507,9 +507,7 @@ void QWindowsTheme::refreshFonts()
if (!QGuiApplication::desktopSettingsAware())
return;
NONCLIENTMETRICS ncm;
- ncm.cbSize = FIELD_OFFSET(NONCLIENTMETRICS, lfMessageFont) + sizeof(LOGFONT);
- SystemParametersInfo(SPI_GETNONCLIENTMETRICS, ncm.cbSize , &ncm, 0);
-
+ QWindowsContext::nonClientMetrics(&ncm);
const QFont menuFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMenuFont);
const QFont messageBoxFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfMessageFont);
const QFont statusFont = QWindowsFontDatabase::LOGFONT_to_QFont(ncm.lfStatusFont);
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index 9c31409644..f340f16679 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -2416,6 +2416,13 @@ void QWindowsWindow::setFrameStrutEventsEnabled(bool enabled)
}
}
+static int getBorderWidth(const QPlatformScreen *screen)
+{
+ NONCLIENTMETRICS ncm;
+ QWindowsContext::nonClientMetricsForScreen(&ncm, screen);
+ return ncm.iBorderWidth + ncm.iPaddedBorderWidth + 2;
+}
+
void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
{
// We don't apply the min/max size hint as we change the dpi, because we did not adjust the
@@ -2425,10 +2432,11 @@ void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
hint.applyToMinMaxInfo(m_data.hwnd, mmi);
}
- if ((testFlag(WithinMaximize) || (window()->windowStates() & Qt::WindowMinimized))
- && (m_data.flags & Qt::FramelessWindowHint)) {
- // This block fixes QTBUG-8361: Frameless windows shouldn't cover the
- // taskbar when maximized
+ // This block fixes QTBUG-8361, QTBUG-4362: Frameless/title-less windows shouldn't cover the
+ // taskbar when maximized
+ if ((testFlag(WithinMaximize) || window()->windowStates().testFlag(Qt::WindowMinimized))
+ && (m_data.flags.testFlag(Qt::FramelessWindowHint)
+ || (m_data.flags.testFlag(Qt::CustomizeWindowHint) && !m_data.flags.testFlag(Qt::WindowTitleHint)))) {
const QScreen *screen = window()->screen();
// Documentation of MINMAXINFO states that it will only work for the primary screen
@@ -2442,6 +2450,14 @@ void QWindowsWindow::getSizeHints(MINMAXINFO *mmi) const
// If you have the taskbar on top, or on the left you don't want it at (0,0):
mmi->ptMaxPosition.x = availableGeometry.x();
mmi->ptMaxPosition.y = availableGeometry.y();
+ if (!m_data.flags.testFlag(Qt::FramelessWindowHint)) {
+ const int borderWidth = getBorderWidth(screen->handle());
+ mmi->ptMaxSize.x += borderWidth * 2;
+ mmi->ptMaxSize.y += borderWidth * 2;
+ mmi->ptMaxTrackSize = mmi->ptMaxSize;
+ mmi->ptMaxPosition.x -= borderWidth;
+ mmi->ptMaxPosition.y -= borderWidth;
+ }
} else if (!screen){
qWarning("window()->screen() returned a null screen");
}
diff --git a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp
index 8851ea59e5..ed482e5dae 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()
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..c8c806749f 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp
@@ -106,7 +106,7 @@ public:
void put(xcb_drawable_t dst, const QRegion &region, const QPoint &offset);
void preparePaint(const QRegion &region);
- static bool createSystemVShmSegment(QXcbConnection *c, size_t segmentSize = 1,
+ static bool createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize = 1,
xcb_shm_segment_info_t *shm_info = nullptr);
private:
@@ -406,12 +406,12 @@ void QXcbBackingStoreImage::createShmSegment(size_t segmentSize)
} else
#endif
{
- if (createSystemVShmSegment(connection(), segmentSize, &m_shm_info))
+ if (createSystemVShmSegment(xcb_connection(), segmentSize, &m_shm_info))
m_segmentSize = segmentSize;
}
}
-bool QXcbBackingStoreImage::createSystemVShmSegment(QXcbConnection *c, size_t segmentSize,
+bool QXcbBackingStoreImage::createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize,
xcb_shm_segment_info_t *shmInfo)
{
const int id = shmget(IPC_PRIVATE, segmentSize, IPC_CREAT | 0600);
@@ -429,17 +429,17 @@ bool QXcbBackingStoreImage::createSystemVShmSegment(QXcbConnection *c, size_t se
if (shmctl(id, IPC_RMID, 0) == -1)
qCWarning(lcQpaXcb, "Error while marking the shared memory segment to be destroyed");
- const auto seg = xcb_generate_id(c->xcb_connection());
- auto cookie = xcb_shm_attach_checked(c->xcb_connection(), seg, id, false);
- auto *error = xcb_request_check(c->xcb_connection(), cookie);
+ const auto seg = xcb_generate_id(c);
+ auto cookie = xcb_shm_attach_checked(c, seg, id, false);
+ auto *error = xcb_request_check(c, cookie);
if (error) {
- c->printXcbError("xcb_shm_attach() failed with error", error);
+ qCWarning(lcQpaXcb(), "xcb_shm_attach() failed");
free(error);
if (shmdt(addr) == -1)
qCWarning(lcQpaXcb, "shmdt() failed (%d: %s) for %p", errno, strerror(errno), addr);
return false;
} else if (!shmInfo) { // this was a test run, free the allocated test segment
- xcb_shm_detach(c->xcb_connection(), seg);
+ xcb_shm_detach(c, seg);
auto shmaddr = static_cast<quint8 *>(addr);
if (shmdt(shmaddr) == -1)
qCWarning(lcQpaXcb, "shmdt() failed (%d: %s) for %p", errno, strerror(errno), shmaddr);
@@ -766,7 +766,7 @@ void QXcbBackingStoreImage::preparePaint(const QRegion &region)
m_pendingFlush |= region;
}
-bool QXcbBackingStore::createSystemVShmSegment(QXcbConnection *c, size_t segmentSize, void *shmInfo)
+bool QXcbBackingStore::createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize, void *shmInfo)
{
auto info = reinterpret_cast<xcb_shm_segment_info_t *>(shmInfo);
return QXcbBackingStoreImage::createSystemVShmSegment(c, segmentSize, info);
diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.h b/src/plugins/platforms/xcb/qxcbbackingstore.h
index 39d023cb9d..b91e5c7dc2 100644
--- a/src/plugins/platforms/xcb/qxcbbackingstore.h
+++ b/src/plugins/platforms/xcb/qxcbbackingstore.h
@@ -74,7 +74,7 @@ public:
void beginPaint(const QRegion &) override;
void endPaint() override;
- static bool createSystemVShmSegment(QXcbConnection *c, size_t segmentSize = 1,
+ static bool createSystemVShmSegment(xcb_connection_t *c, size_t segmentSize = 1,
void *shmInfo = nullptr);
protected:
diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp
index 84831cdbe5..3fd14a659e 100644
--- a/src/plugins/platforms/xcb/qxcbclipboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp
@@ -807,18 +807,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 +833,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;
diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 4e24c970b4..9e857ea2ff 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -38,12 +38,10 @@
****************************************************************************/
#include <QtGui/private/qguiapplication_p.h>
-#include <QtGui/private/qhighdpiscaling_p.h>
#include <QtCore/QDebug>
#include "qxcbconnection.h"
#include "qxcbkeyboard.h"
-#include "qxcbscreen.h"
#include "qxcbwindow.h"
#include "qxcbclipboard.h"
#if QT_CONFIG(draganddrop)
@@ -57,39 +55,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,7 +81,7 @@ Q_LOGGING_CATEGORY(lcQpaXInputDevices, "qt.qpa.input.devices")
Q_LOGGING_CATEGORY(lcQpaXInputEvents, "qt.qpa.input.events")
Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen")
Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events")
-Q_LOGGING_CATEGORY(lcQpaXcb, "qt.qpa.xcb") // for general (uncategorized) XCB logging
+Q_LOGGING_CATEGORY(lcQpaEventReader, "qt.qpa.events.reader")
Q_LOGGING_CATEGORY(lcQpaPeeker, "qt.qpa.peeker")
Q_LOGGING_CATEGORY(lcQpaKeyboard, "qt.qpa.xkeyboard")
Q_LOGGING_CATEGORY(lcQpaXDnd, "qt.qpa.xdnd")
@@ -108,519 +92,27 @@ 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();
-
- 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);
@@ -646,12 +138,8 @@ QXcbConnection::~QXcbConnection()
#if QT_CONFIG(draganddrop)
delete m_drag;
#endif
- if (m_reader && m_reader->isRunning()) {
- sendConnectionEvent(QXcbAtom::_QT_CLOSE_CONNECTION);
- m_reader->wait();
- }
-
- delete m_reader;
+ if (m_eventQueue)
+ delete m_eventQueue;
QXcbIntegration *integration = QXcbIntegration::instance();
// Delete screens in reverse order to avoid crash in case of multiple screens
@@ -663,22 +151,9 @@ QXcbConnection::~QXcbConnection()
delete m_glIntegration;
- if (isConnected()) {
-#if QT_CONFIG(xcb_xlib)
- XCloseDisplay(static_cast<Display *>(m_xlib_display));
-#else
- xcb_disconnect(xcb_connection());
-#endif
- }
-
delete m_keyboard;
}
-bool QXcbConnection::isConnected() const
-{
- return m_connection && !xcb_connection_has_error(m_connection);
-}
-
QXcbScreen *QXcbConnection::primaryScreen() const
{
if (!m_screens.isEmpty()) {
@@ -783,17 +258,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
@@ -1051,11 +526,10 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
printXcbEvent(lcQpaEvents(), "Event", event);
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;
+ if (QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance()) {
+ if (dispatcher->filterNativeEvent(m_nativeInterface->nativeEventType(), event, &result))
+ return;
+ }
uint response_type = event->response_type & ~0x80;
@@ -1187,7 +661,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 +674,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 +682,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) {
@@ -1254,151 +728,6 @@ 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 +745,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 +765,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);
@@ -1564,21 +866,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 +878,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 +900,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,22 +994,17 @@ void QXcbConnection::processXcbEvents()
exit(1);
}
- QXcbEventArray *eventqueue = m_reader->lock();
+ m_eventQueue->flushBufferedEvents();
- for (int i = 0; i < eventqueue->size(); ++i) {
- xcb_generic_event_t *event = eventqueue->at(i);
- if (!event)
- continue;
+ while (xcb_generic_event_t *event = m_eventQueue->takeFirst(flags)) {
QScopedPointer<xcb_generic_event_t, QScopedPointerPodDeleter> eventGuard(event);
- (*eventqueue)[i] = 0;
if (!(event->response_type & ~0x80)) {
handleXcbError(reinterpret_cast<xcb_generic_error_t *>(event));
continue;
}
- if (Q_LIKELY(QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents)) &&
- compressEvent(event, i, eventqueue))
+ if (compressEvent(event))
continue;
#ifndef QT_NO_CLIPBOARD
@@ -1707,285 +1023,28 @@ void QXcbConnection::processXcbEvents()
m_peekFuncs.erase(std::remove_if(m_peekFuncs.begin(), m_peekFuncs.end(),
isWaitingFor),
m_peekFuncs.end());
- m_reader->unlock();
- handleXcbEvent(event);
- m_reader->lock();
- }
- eventqueue->clear();
-
- m_reader->unlock();
+ handleXcbEvent(event);
- m_peekerIndexCacheDirty = m_mainEventLoopFlushedQueue = true;
+ // The lock-based solution used to free the lock inside this loop,
+ // hence allowing for more events to arrive. ### Check if we want
+ // this flush here after QTBUG-70095
+ m_eventQueue->flushBufferedEvents();
+ }
// Indicate with a null event that the event the callbacks are waiting for
// is not in the queue currently.
for (PeekFunc f : qAsConst(m_peekFuncs))
- f(this, 0);
+ f(this, nullptr);
m_peekFuncs.clear();
xcb_flush(xcb_connection());
}
-static const char * xcb_atomnames = {
- // window-manager <-> client protocols
- "WM_PROTOCOLS\0"
- "WM_DELETE_WINDOW\0"
- "WM_TAKE_FOCUS\0"
- "_NET_WM_PING\0"
- "_NET_WM_CONTEXT_HELP\0"
- "_NET_WM_SYNC_REQUEST\0"
- "_NET_WM_SYNC_REQUEST_COUNTER\0"
- "MANAGER\0"
- "_NET_SYSTEM_TRAY_OPCODE\0"
-
- // ICCCM window state
- "WM_STATE\0"
- "WM_CHANGE_STATE\0"
- "WM_CLASS\0"
- "WM_NAME\0"
-
- // Session management
- "WM_CLIENT_LEADER\0"
- "WM_WINDOW_ROLE\0"
- "SM_CLIENT_ID\0"
- "WM_CLIENT_MACHINE\0"
-
- // Clipboard
- "CLIPBOARD\0"
- "INCR\0"
- "TARGETS\0"
- "MULTIPLE\0"
- "TIMESTAMP\0"
- "SAVE_TARGETS\0"
- "CLIP_TEMPORARY\0"
- "_QT_SELECTION\0"
- "_QT_CLIPBOARD_SENTINEL\0"
- "_QT_SELECTION_SENTINEL\0"
- "CLIPBOARD_MANAGER\0"
-
- "RESOURCE_MANAGER\0"
-
- "_XSETROOT_ID\0"
-
- "_QT_SCROLL_DONE\0"
- "_QT_INPUT_ENCODING\0"
-
- "_QT_CLOSE_CONNECTION\0"
-
- "_MOTIF_WM_HINTS\0"
-
- "DTWM_IS_RUNNING\0"
- "ENLIGHTENMENT_DESKTOP\0"
- "_DT_SAVE_MODE\0"
- "_SGI_DESKS_MANAGER\0"
-
- // EWMH (aka NETWM)
- "_NET_SUPPORTED\0"
- "_NET_VIRTUAL_ROOTS\0"
- "_NET_WORKAREA\0"
-
- "_NET_MOVERESIZE_WINDOW\0"
- "_NET_WM_MOVERESIZE\0"
-
- "_NET_WM_NAME\0"
- "_NET_WM_ICON_NAME\0"
- "_NET_WM_ICON\0"
-
- "_NET_WM_PID\0"
-
- "_NET_WM_WINDOW_OPACITY\0"
-
- "_NET_WM_STATE\0"
- "_NET_WM_STATE_ABOVE\0"
- "_NET_WM_STATE_BELOW\0"
- "_NET_WM_STATE_FULLSCREEN\0"
- "_NET_WM_STATE_MAXIMIZED_HORZ\0"
- "_NET_WM_STATE_MAXIMIZED_VERT\0"
- "_NET_WM_STATE_MODAL\0"
- "_NET_WM_STATE_STAYS_ON_TOP\0"
- "_NET_WM_STATE_DEMANDS_ATTENTION\0"
-
- "_NET_WM_USER_TIME\0"
- "_NET_WM_USER_TIME_WINDOW\0"
- "_NET_WM_FULL_PLACEMENT\0"
-
- "_NET_WM_WINDOW_TYPE\0"
- "_NET_WM_WINDOW_TYPE_DESKTOP\0"
- "_NET_WM_WINDOW_TYPE_DOCK\0"
- "_NET_WM_WINDOW_TYPE_TOOLBAR\0"
- "_NET_WM_WINDOW_TYPE_MENU\0"
- "_NET_WM_WINDOW_TYPE_UTILITY\0"
- "_NET_WM_WINDOW_TYPE_SPLASH\0"
- "_NET_WM_WINDOW_TYPE_DIALOG\0"
- "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU\0"
- "_NET_WM_WINDOW_TYPE_POPUP_MENU\0"
- "_NET_WM_WINDOW_TYPE_TOOLTIP\0"
- "_NET_WM_WINDOW_TYPE_NOTIFICATION\0"
- "_NET_WM_WINDOW_TYPE_COMBO\0"
- "_NET_WM_WINDOW_TYPE_DND\0"
- "_NET_WM_WINDOW_TYPE_NORMAL\0"
- "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0"
-
- "_KDE_NET_WM_FRAME_STRUT\0"
- "_NET_FRAME_EXTENTS\0"
-
- "_NET_STARTUP_INFO\0"
- "_NET_STARTUP_INFO_BEGIN\0"
-
- "_NET_SUPPORTING_WM_CHECK\0"
-
- "_NET_WM_CM_S0\0"
-
- "_NET_SYSTEM_TRAY_VISUAL\0"
-
- "_NET_ACTIVE_WINDOW\0"
-
- // Property formats
- "TEXT\0"
- "UTF8_STRING\0"
- "CARDINAL\0"
-
- // xdnd
- "XdndEnter\0"
- "XdndPosition\0"
- "XdndStatus\0"
- "XdndLeave\0"
- "XdndDrop\0"
- "XdndFinished\0"
- "XdndTypeList\0"
- "XdndActionList\0"
-
- "XdndSelection\0"
-
- "XdndAware\0"
- "XdndProxy\0"
-
- "XdndActionCopy\0"
- "XdndActionLink\0"
- "XdndActionMove\0"
- "XdndActionPrivate\0"
-
- // Motif DND
- "_MOTIF_DRAG_AND_DROP_MESSAGE\0"
- "_MOTIF_DRAG_INITIATOR_INFO\0"
- "_MOTIF_DRAG_RECEIVER_INFO\0"
- "_MOTIF_DRAG_WINDOW\0"
- "_MOTIF_DRAG_TARGETS\0"
-
- "XmTRANSFER_SUCCESS\0"
- "XmTRANSFER_FAILURE\0"
-
- // Xkb
- "_XKB_RULES_NAMES\0"
-
- // XEMBED
- "_XEMBED\0"
- "_XEMBED_INFO\0"
-
- // XInput2
- "Button Left\0"
- "Button Middle\0"
- "Button Right\0"
- "Button Wheel Up\0"
- "Button Wheel Down\0"
- "Button Horiz Wheel Left\0"
- "Button Horiz Wheel Right\0"
- "Abs MT Position X\0"
- "Abs MT Position Y\0"
- "Abs MT Touch Major\0"
- "Abs MT Touch Minor\0"
- "Abs MT Orientation\0"
- "Abs MT Pressure\0"
- "Abs MT Tracking ID\0"
- "Max Contacts\0"
- "Rel X\0"
- "Rel Y\0"
- // XInput2 tablet
- "Abs X\0"
- "Abs Y\0"
- "Abs Pressure\0"
- "Abs Tilt X\0"
- "Abs Tilt Y\0"
- "Abs Wheel\0"
- "Abs Distance\0"
- "Wacom Serial IDs\0"
- "INTEGER\0"
- "Rel Horiz Wheel\0"
- "Rel Vert Wheel\0"
- "Rel Horiz Scroll\0"
- "Rel Vert Scroll\0"
- "_XSETTINGS_SETTINGS\0"
- "_COMPIZ_DECOR_PENDING\0"
- "_COMPIZ_DECOR_REQUEST\0"
- "_COMPIZ_DECOR_DELETE_PIXMAP\0"
- "_COMPIZ_TOOLKIT_ACTION\0"
- "_GTK_LOAD_ICONTHEMES\0"
- "AT_SPI_BUS\0"
- "EDID\0"
- "EDID_DATA\0"
- "XFree86_DDC_EDID1_RAWDATA\0"
- // \0\0 terminates loop.
-};
-
-QXcbAtom::Atom QXcbConnection::qatom(xcb_atom_t xatom) const
-{
- return static_cast<QXcbAtom::Atom>(std::find(m_allAtoms, m_allAtoms + QXcbAtom::NAtoms, xatom) - m_allAtoms);
-}
-
-void QXcbConnection::initializeAllAtoms() {
- const char *names[QXcbAtom::NAtoms];
- const char *ptr = xcb_atomnames;
-
- int i = 0;
- while (*ptr) {
- names[i++] = ptr;
- while (*ptr)
- ++ptr;
- ++ptr;
- }
-
- Q_ASSERT(i == QXcbAtom::NAtoms);
-
- xcb_intern_atom_cookie_t cookies[QXcbAtom::NAtoms];
-
- Q_ASSERT(i == QXcbAtom::NAtoms);
- for (i = 0; i < QXcbAtom::NAtoms; ++i)
- cookies[i] = xcb_intern_atom(xcb_connection(), false, strlen(names[i]), names[i]);
-
- for (i = 0; i < QXcbAtom::NAtoms; ++i) {
- xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(xcb_connection(), cookies[i], 0);
- m_allAtoms[i] = reply->atom;
- free(reply);
- }
-}
-
-xcb_atom_t QXcbConnection::internAtom(const char *name)
-{
- if (!name || *name == 0)
- return XCB_NONE;
-
- return Q_XCB_REPLY(xcb_intern_atom, xcb_connection(), false, strlen(name), name)->atom;
-}
-
-QByteArray QXcbConnection::atomName(xcb_atom_t atom)
-{
- if (!atom)
- return QByteArray();
-
- auto reply = Q_XCB_REPLY(xcb_get_atom_name, xcb_connection(), atom);
- if (!reply)
- qWarning() << "QXcbConnection::atomName: bad Atom" << atom;
- else
- return QByteArray(xcb_get_atom_name_name(reply.get()), xcb_get_atom_name_name_length(reply.get()));
-
- return QByteArray();
-}
-
const xcb_format_t *QXcbConnection::formatForDepth(uint8_t depth) const
{
xcb_format_iterator_t iterator =
- xcb_setup_pixmap_formats_iterator(m_setup);
+ xcb_setup_pixmap_formats_iterator(setup());
while (iterator.rem) {
xcb_format_t *format = iterator.data;
@@ -2005,208 +1064,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..49e79ec3fd 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -47,32 +47,18 @@
#include "qxcbexport.h"
#include <QHash>
#include <QList>
-#include <QMutex>
-#include <QObject>
-#include <QThread>
#include <QVector>
-#include <QVarLengthArray>
#include <qpa/qwindowsysteminterface.h>
#include <QtCore/QLoggingCategory>
#include <QtCore/private/qglobal_p.h>
-#include <cstdlib>
-#include <memory>
-
-// This is needed to make Qt compile together with XKB. xkb.h is using a variable
-// which is called 'explicit', this is a reserved keyword in c++
-#if QT_CONFIG(xkb)
-#define explicit dont_use_cxx_explicit
-#include <xcb/xkb.h>
-#undef explicit
-#endif
+#include "qxcbeventqueue.h"
+#include "qxcbconnection_basic.h"
#if QT_CONFIG(tabletevent)
#include <QTabletEvent>
#endif
-struct xcb_randr_get_output_info_reply_t;
-
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcQpaXInput)
@@ -80,10 +66,10 @@ Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputDevices)
Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputEvents)
Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen)
Q_DECLARE_LOGGING_CATEGORY(lcQpaEvents)
-Q_DECLARE_LOGGING_CATEGORY(lcQpaXcb)
Q_DECLARE_LOGGING_CATEGORY(lcQpaPeeker)
Q_DECLARE_LOGGING_CATEGORY(lcQpaKeyboard)
Q_DECLARE_LOGGING_CATEGORY(lcQpaXDnd)
+Q_DECLARE_LOGGING_CATEGORY(lcQpaEventReader)
class QXcbVirtualDesktop;
class QXcbScreen;
@@ -96,244 +82,6 @@ class QXcbNativeInterface;
class QXcbSystemTrayTracker;
class QXcbGlIntegration;
-namespace QXcbAtom {
- enum Atom {
- // window-manager <-> client protocols
- WM_PROTOCOLS,
- WM_DELETE_WINDOW,
- WM_TAKE_FOCUS,
- _NET_WM_PING,
- _NET_WM_CONTEXT_HELP,
- _NET_WM_SYNC_REQUEST,
- _NET_WM_SYNC_REQUEST_COUNTER,
- MANAGER, // System tray notification
- _NET_SYSTEM_TRAY_OPCODE, // System tray operation
-
- // ICCCM window state
- WM_STATE,
- WM_CHANGE_STATE,
- WM_CLASS,
- WM_NAME,
-
- // Session management
- WM_CLIENT_LEADER,
- WM_WINDOW_ROLE,
- SM_CLIENT_ID,
- WM_CLIENT_MACHINE,
-
- // Clipboard
- CLIPBOARD,
- INCR,
- TARGETS,
- MULTIPLE,
- TIMESTAMP,
- SAVE_TARGETS,
- CLIP_TEMPORARY,
- _QT_SELECTION,
- _QT_CLIPBOARD_SENTINEL,
- _QT_SELECTION_SENTINEL,
- CLIPBOARD_MANAGER,
-
- RESOURCE_MANAGER,
-
- _XSETROOT_ID,
-
- _QT_SCROLL_DONE,
- _QT_INPUT_ENCODING,
-
- // Qt/XCB specific
- _QT_CLOSE_CONNECTION,
-
- _MOTIF_WM_HINTS,
-
- DTWM_IS_RUNNING,
- ENLIGHTENMENT_DESKTOP,
- _DT_SAVE_MODE,
- _SGI_DESKS_MANAGER,
-
- // EWMH (aka NETWM)
- _NET_SUPPORTED,
- _NET_VIRTUAL_ROOTS,
- _NET_WORKAREA,
-
- _NET_MOVERESIZE_WINDOW,
- _NET_WM_MOVERESIZE,
-
- _NET_WM_NAME,
- _NET_WM_ICON_NAME,
- _NET_WM_ICON,
-
- _NET_WM_PID,
-
- _NET_WM_WINDOW_OPACITY,
-
- _NET_WM_STATE,
- _NET_WM_STATE_ABOVE,
- _NET_WM_STATE_BELOW,
- _NET_WM_STATE_FULLSCREEN,
- _NET_WM_STATE_MAXIMIZED_HORZ,
- _NET_WM_STATE_MAXIMIZED_VERT,
- _NET_WM_STATE_MODAL,
- _NET_WM_STATE_STAYS_ON_TOP,
- _NET_WM_STATE_DEMANDS_ATTENTION,
-
- _NET_WM_USER_TIME,
- _NET_WM_USER_TIME_WINDOW,
- _NET_WM_FULL_PLACEMENT,
-
- _NET_WM_WINDOW_TYPE,
- _NET_WM_WINDOW_TYPE_DESKTOP,
- _NET_WM_WINDOW_TYPE_DOCK,
- _NET_WM_WINDOW_TYPE_TOOLBAR,
- _NET_WM_WINDOW_TYPE_MENU,
- _NET_WM_WINDOW_TYPE_UTILITY,
- _NET_WM_WINDOW_TYPE_SPLASH,
- _NET_WM_WINDOW_TYPE_DIALOG,
- _NET_WM_WINDOW_TYPE_DROPDOWN_MENU,
- _NET_WM_WINDOW_TYPE_POPUP_MENU,
- _NET_WM_WINDOW_TYPE_TOOLTIP,
- _NET_WM_WINDOW_TYPE_NOTIFICATION,
- _NET_WM_WINDOW_TYPE_COMBO,
- _NET_WM_WINDOW_TYPE_DND,
- _NET_WM_WINDOW_TYPE_NORMAL,
- _KDE_NET_WM_WINDOW_TYPE_OVERRIDE,
-
- _KDE_NET_WM_FRAME_STRUT,
- _NET_FRAME_EXTENTS,
-
- _NET_STARTUP_INFO,
- _NET_STARTUP_INFO_BEGIN,
-
- _NET_SUPPORTING_WM_CHECK,
-
- _NET_WM_CM_S0,
-
- _NET_SYSTEM_TRAY_VISUAL,
-
- _NET_ACTIVE_WINDOW,
-
- // Property formats
- TEXT,
- UTF8_STRING,
- CARDINAL,
-
- // Xdnd
- XdndEnter,
- XdndPosition,
- XdndStatus,
- XdndLeave,
- XdndDrop,
- XdndFinished,
- XdndTypelist,
- XdndActionList,
-
- XdndSelection,
-
- XdndAware,
- XdndProxy,
-
- XdndActionCopy,
- XdndActionLink,
- XdndActionMove,
- XdndActionPrivate,
-
- // Motif DND
- _MOTIF_DRAG_AND_DROP_MESSAGE,
- _MOTIF_DRAG_INITIATOR_INFO,
- _MOTIF_DRAG_RECEIVER_INFO,
- _MOTIF_DRAG_WINDOW,
- _MOTIF_DRAG_TARGETS,
-
- XmTRANSFER_SUCCESS,
- XmTRANSFER_FAILURE,
-
- // Xkb
- _XKB_RULES_NAMES,
-
- // XEMBED
- _XEMBED,
- _XEMBED_INFO,
-
- // XInput2
- ButtonLeft,
- ButtonMiddle,
- ButtonRight,
- ButtonWheelUp,
- ButtonWheelDown,
- ButtonHorizWheelLeft,
- ButtonHorizWheelRight,
- AbsMTPositionX,
- AbsMTPositionY,
- AbsMTTouchMajor,
- AbsMTTouchMinor,
- AbsMTOrientation,
- AbsMTPressure,
- AbsMTTrackingID,
- MaxContacts,
- RelX,
- RelY,
- // XInput2 tablet
- AbsX,
- AbsY,
- AbsPressure,
- AbsTiltX,
- AbsTiltY,
- AbsWheel,
- AbsDistance,
- WacomSerialIDs,
- INTEGER,
- RelHorizWheel,
- RelVertWheel,
- RelHorizScroll,
- RelVertScroll,
-
- _XSETTINGS_SETTINGS,
-
- _COMPIZ_DECOR_PENDING,
- _COMPIZ_DECOR_REQUEST,
- _COMPIZ_DECOR_DELETE_PIXMAP,
- _COMPIZ_TOOLKIT_ACTION,
- _GTK_LOAD_ICONTHEMES,
-
- AT_SPI_BUS,
-
- EDID,
- EDID_DATA,
- XFree86_DDC_EDID1_RAWDATA,
-
- NAtoms
- };
-}
-
-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:
@@ -375,38 +123,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 +147,9 @@ public:
if (!hasShm())
return false; // The non-Shm path does its own swapping
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
- return m_setup->image_byte_order != XCB_IMAGE_ORDER_MSB_FIRST;
+ return setup()->image_byte_order != XCB_IMAGE_ORDER_MSB_FIRST;
#else
- return m_setup->image_byte_order != XCB_IMAGE_ORDER_LSB_FIRST;
+ return setup()->image_byte_order != XCB_IMAGE_ORDER_LSB_FIRST;
#endif
}
@@ -436,9 +169,6 @@ public:
bool hasDefaultVisualId() const { return m_defaultVisualId != UINT_MAX; }
xcb_visualid_t defaultVisualId() const { return m_defaultVisualId; }
-#if QT_CONFIG(xcb_xlib)
- void *xlib_display() const { return m_xlib_display; }
-#endif
void sync();
void handleXcbError(xcb_generic_error_t *error);
@@ -452,44 +182,15 @@ 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 xcb_timestamp_t netWmUserTime() const { return m_netWmUserTime; }
inline void setNetWmUserTime(xcb_timestamp_t t) { if (t > m_netWmUserTime) m_netWmUserTime = t; }
- bool hasXFixes() const { return has_xfixes; }
- bool hasXShape() const { return has_shape_extension; }
- bool hasXRandr() const { return has_randr_extension; }
- bool hasInputShape() const { return has_input_shape; }
- bool hasXKB() const { return has_xkb; }
- bool hasXRender(int major = -1, int minor = -1) const
- {
- if (has_render_extension && major != -1 && minor != -1)
- return m_xrenderVersion >= qMakePair(major, minor);
-
- return has_render_extension;
- }
- bool hasXInput2() const { return m_xi2Enabled; }
- bool hasShm() const { return has_shm; }
- bool hasShmFd() const { return has_shm_fd; }
- bool hasXSync() const { return has_sync_extension; }
-
xcb_timestamp_t getTimestamp();
xcb_window_t getSelectionOwner(xcb_atom_t atom) const;
xcb_window_t getQtSelectionOwner();
@@ -523,46 +224,33 @@ 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;
+ void flush() { xcb_flush(xcb_connection()); }
+ void processXcbEvents(QEventLoop::ProcessEventsFlags flags);
+
protected:
bool event(QEvent *e) override;
-public slots:
- void flush() { xcb_flush(m_connection); }
-
-private slots:
- void processXcbEvents();
-
private:
- void initializeAllAtoms();
- void sendConnectionEvent(QXcbAtom::Atom atom, uint id = 0);
- void initializeShm();
- void initializeXFixes();
- void initializeXRender();
- void initializeXRandr();
- void initializeXinerama();
- void initializeXShape();
- void initializeXKB();
- void initializeXSync();
+ void selectXRandrEvents();
QXcbScreen* findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) const;
QXcbScreen* findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output) const;
QXcbVirtualDesktop* virtualDesktopForRootWindow(xcb_window_t rootWindow) const;
@@ -574,12 +262,9 @@ private:
xcb_randr_get_output_info_reply_t *outputInfo);
void destroyScreen(QXcbScreen *screen);
void initializeScreens();
- bool compressEvent(xcb_generic_event_t *event, int currentIndex, QXcbEventArray *eventqueue) const;
+ bool compressEvent(xcb_generic_event_t *event) const;
- bool m_xi2Enabled = false;
#if QT_CONFIG(xcb_xinput)
- int m_xi2Minor = -1;
- void initializeXInput2();
void xi2SetupDevice(void *info, bool removeExisting = true);
void xi2SetupDevices();
struct TouchDeviceData {
@@ -605,7 +290,6 @@ private:
void xi2HandleEvent(xcb_ge_event_t *event);
void xi2HandleHierarchyEvent(void *event);
void xi2HandleDeviceChangedEvent(void *event);
- int m_xiOpCode;
void xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindow);
#if QT_CONFIG(tabletevent)
struct TabletData {
@@ -646,24 +330,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,44 +359,12 @@ 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::MouseButton m_button = Qt::NoButton;
@@ -729,11 +382,7 @@ 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;
};
@@ -744,24 +393,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 +403,6 @@ private:
QXcbConnection *m_connection;
};
-#define Q_XCB_REPLY_CONNECTION_ARG(connection, ...) connection
-
-struct QStdFreeDeleter {
- void operator()(void *p) const Q_DECL_NOTHROW { return std::free(p); }
-};
-
-#define Q_XCB_REPLY(call, ...) \
- std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \
- call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call(__VA_ARGS__), nullptr) \
- )
-
-#define Q_XCB_REPLY_UNCHECKED(call, ...) \
- std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \
- call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call##_unchecked(__VA_ARGS__), nullptr) \
- )
-
// The xcb_send_event() requires all events to have 32 bytes. It calls memcpy() on the
// passed in event. If the passed in event is less than 32 bytes, memcpy() reaches into
// unrelated memory.
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
new file mode 100644
index 0000000000..7bed3c8937
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp
@@ -0,0 +1,437 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qxcbconnection_basic.h"
+#include "qxcbbackingstore.h" // for createSystemVShmSegment()
+
+#include <xcb/randr.h>
+#include <xcb/shm.h>
+#include <xcb/sync.h>
+#include <xcb/xfixes.h>
+#include <xcb/xinerama.h>
+#if QT_CONFIG(xcb_xinput)
+#include <xcb/xinput.h>
+#endif
+#if QT_CONFIG(xcb_render)
+#include <xcb/render.h>
+#endif
+#if QT_CONFIG(xkb)
+#define explicit dont_use_cxx_explicit
+#include <xcb/xkb.h>
+#undef explicit
+#endif
+
+#if QT_CONFIG(xcb_xlib)
+#define register /* C++17 deprecated register */
+#include <X11/Xlib.h>
+#include <X11/Xlib-xcb.h>
+#include <X11/Xlibint.h>
+#include <X11/Xutil.h>
+#undef register
+#endif
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQpaXcb, "qt.qpa.xcb")
+
+#if QT_CONFIG(xcb_xlib)
+static const char * const xcbConnectionErrors[] = {
+ "No error", /* Error 0 */
+ "I/O error", /* XCB_CONN_ERROR */
+ "Unsupported extension used", /* XCB_CONN_CLOSED_EXT_NOTSUPPORTED */
+ "Out of memory", /* XCB_CONN_CLOSED_MEM_INSUFFICIENT */
+ "Maximum allowed requested length exceeded", /* XCB_CONN_CLOSED_REQ_LEN_EXCEED */
+ "Failed to parse display string", /* XCB_CONN_CLOSED_PARSE_ERR */
+ "No such screen on display", /* XCB_CONN_CLOSED_INVALID_SCREEN */
+ "Error during FD passing" /* XCB_CONN_CLOSED_FDPASSING_FAILED */
+};
+
+static int nullErrorHandler(Display *dpy, XErrorEvent *err)
+{
+#ifndef Q_XCB_DEBUG
+ Q_UNUSED(dpy);
+ Q_UNUSED(err);
+#else
+ const int buflen = 1024;
+ char buf[buflen];
+
+ XGetErrorText(dpy, err->error_code, buf, buflen);
+ fprintf(stderr, "X Error: serial %lu error %d %s\n", err->serial, (int) err->error_code, buf);
+#endif
+ return 0;
+}
+
+static int ioErrorHandler(Display *dpy)
+{
+ xcb_connection_t *conn = XGetXCBConnection(dpy);
+ if (conn != NULL) {
+ /* Print a message with a textual description of the error */
+ int code = xcb_connection_has_error(conn);
+ const char *str = "Unknown error";
+ int arrayLength = sizeof(xcbConnectionErrors) / sizeof(xcbConnectionErrors[0]);
+ if (code >= 0 && code < arrayLength)
+ str = xcbConnectionErrors[code];
+
+ qWarning("The X11 connection broke: %s (code %d)", str, code);
+ }
+ return _XDefaultIOError(dpy);
+}
+#endif
+
+QXcbBasicConnection::QXcbBasicConnection(const char *displayName)
+ : m_displayName(displayName ? QByteArray(displayName) : qgetenv("DISPLAY"))
+{
+#if QT_CONFIG(xcb_xlib)
+ Display *dpy = XOpenDisplay(m_displayName.constData());
+ if (dpy) {
+ m_primaryScreenNumber = DefaultScreen(dpy);
+ m_xcbConnection = XGetXCBConnection(dpy);
+ XSetEventQueueOwner(dpy, XCBOwnsEventQueue);
+ XSetErrorHandler(nullErrorHandler);
+ XSetIOErrorHandler(ioErrorHandler);
+ m_xlibDisplay = dpy;
+ }
+#else
+ m_xcbConnection = xcb_connect(m_displayName.constData(), &m_primaryScreenNumber);
+#endif
+ if (Q_UNLIKELY(!isConnected())) {
+ qCWarning(lcQpaXcb, "could not connect to display %s", m_displayName.constData());
+ return;
+ }
+
+ m_setup = xcb_get_setup(m_xcbConnection);
+ m_xcbAtom.initialize(m_xcbConnection);
+
+ xcb_extension_t *extensions[] = {
+ &xcb_shm_id, &xcb_xfixes_id, &xcb_randr_id, &xcb_shape_id, &xcb_sync_id,
+#if QT_CONFIG(xkb)
+ &xcb_xkb_id,
+#endif
+#if QT_CONFIG(xcb_render)
+ &xcb_render_id,
+#endif
+#if QT_CONFIG(xcb_xinput)
+ &xcb_input_id,
+#endif
+ 0
+ };
+
+ for (xcb_extension_t **ext_it = extensions; *ext_it; ++ext_it)
+ xcb_prefetch_extension_data (m_xcbConnection, *ext_it);
+
+ initializeXSync();
+ if (!qEnvironmentVariableIsSet("QT_XCB_NO_MITSHM"))
+ initializeShm();
+ if (!qEnvironmentVariableIsSet("QT_XCB_NO_XRANDR"))
+ initializeXRandr();
+ if (!m_hasXRandr)
+ initializeXinerama();
+ initializeXFixes();
+ initializeXRender();
+#if QT_CONFIG(xcb_xinput)
+ if (!qEnvironmentVariableIsSet("QT_XCB_NO_XI2"))
+ initializeXInput2();
+#endif
+ initializeXShape();
+ initializeXKB();
+}
+
+QXcbBasicConnection::~QXcbBasicConnection()
+{
+ if (isConnected()) {
+#if QT_CONFIG(xcb_xlib)
+ XCloseDisplay(static_cast<Display *>(m_xlibDisplay));
+#else
+ xcb_disconnect(m_xcbConnection);
+#endif
+ }
+}
+
+xcb_atom_t QXcbBasicConnection::internAtom(const char *name)
+{
+ if (!name || *name == 0)
+ return XCB_NONE;
+
+ return Q_XCB_REPLY(xcb_intern_atom, m_xcbConnection, false, strlen(name), name)->atom;
+}
+
+QByteArray QXcbBasicConnection::atomName(xcb_atom_t atom)
+{
+ if (!atom)
+ return QByteArray();
+
+ auto reply = Q_XCB_REPLY(xcb_get_atom_name, m_xcbConnection, atom);
+ if (reply)
+ return QByteArray(xcb_get_atom_name_name(reply.get()), xcb_get_atom_name_name_length(reply.get()));
+
+ qCWarning(lcQpaXcb) << "atomName: bad atom" << atom;
+ return QByteArray();
+}
+
+#if QT_CONFIG(xcb_xinput)
+// Starting from the xcb version 1.9.3 struct xcb_ge_event_t has changed:
+// - "pad0" became "extension"
+// - "pad1" and "pad" became "pad0"
+// New and old version of this struct share the following fields:
+typedef struct qt_xcb_ge_event_t {
+ uint8_t response_type;
+ uint8_t extension;
+ uint16_t sequence;
+ uint32_t length;
+ uint16_t event_type;
+} qt_xcb_ge_event_t;
+
+bool QXcbBasicConnection::isXIEvent(xcb_generic_event_t *event) const
+{
+ qt_xcb_ge_event_t *e = reinterpret_cast<qt_xcb_ge_event_t *>(event);
+ return e->extension == m_xiOpCode;
+}
+
+bool QXcbBasicConnection::isXIType(xcb_generic_event_t *event, uint16_t type) const
+{
+ if (!isXIEvent(event))
+ return false;
+
+ auto *e = reinterpret_cast<qt_xcb_ge_event_t *>(event);
+ return e->event_type == type;
+}
+#endif // QT_CONFIG(xcb_xinput)
+
+bool QXcbBasicConnection::isXFixesType(uint responseType, int eventType) const
+{
+ return m_hasXFixes && responseType == m_xfixesFirstEvent + eventType;
+}
+
+bool QXcbBasicConnection::isXRandrType(uint responseType, int eventType) const
+{
+ return m_hasXRandr && responseType == m_xrandrFirstEvent + eventType;
+}
+
+bool QXcbBasicConnection::isXkbType(uint responseType) const
+{
+ return m_hasXkb && responseType == m_xkbFirstEvent;
+}
+
+void QXcbBasicConnection::initializeXSync()
+{
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_sync_id);
+ if (!reply || !reply->present)
+ return;
+
+ m_hasXSync = true;
+}
+
+void QXcbBasicConnection::initializeShm()
+{
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_shm_id);
+ if (!reply || !reply->present) {
+ qCDebug(lcQpaXcb, "MIT-SHM extension is not present on the X server");
+ return;
+ }
+
+ auto shmQuery = Q_XCB_REPLY(xcb_shm_query_version, m_xcbConnection);
+ if (!shmQuery) {
+ qCWarning(lcQpaXcb, "failed to request MIT-SHM version");
+ return;
+ }
+
+ m_hasShm = true;
+ m_hasShmFd = (shmQuery->major_version == 1 && shmQuery->minor_version >= 2) ||
+ shmQuery->major_version > 1;
+
+ qCDebug(lcQpaXcb) << "Has MIT-SHM :" << m_hasShm;
+ qCDebug(lcQpaXcb) << "Has MIT-SHM FD :" << m_hasShmFd;
+
+ // Temporary disable warnings (unless running in debug mode).
+ auto logging = const_cast<QLoggingCategory*>(&lcQpaXcb());
+ bool wasEnabled = logging->isEnabled(QtMsgType::QtWarningMsg);
+ if (!logging->isEnabled(QtMsgType::QtDebugMsg))
+ logging->setEnabled(QtMsgType::QtWarningMsg, false);
+ if (!QXcbBackingStore::createSystemVShmSegment(m_xcbConnection)) {
+ qCDebug(lcQpaXcb, "failed to create System V shared memory segment (remote "
+ "X11 connection?), disabling SHM");
+ m_hasShm = m_hasShmFd = false;
+ }
+ if (wasEnabled)
+ logging->setEnabled(QtMsgType::QtWarningMsg, true);
+}
+
+void QXcbBasicConnection::initializeXRandr()
+{
+#if QT_CONFIG(xcb_render)
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_render_id);
+ if (!reply || !reply->present) {
+ qCDebug(lcQpaXcb, "XRender extension not present on the X server");
+ return;
+ }
+
+ auto xrenderQuery = Q_XCB_REPLY(xcb_render_query_version, m_xcbConnection,
+ XCB_RENDER_MAJOR_VERSION,
+ XCB_RENDER_MINOR_VERSION);
+ if (!xrenderQuery) {
+ qCWarning(lcQpaXcb, "xcb_render_query_version failed");
+ return;
+ }
+
+ m_hasXRender = true;
+ m_xrenderVersion.first = xrenderQuery->major_version;
+ m_xrenderVersion.second = xrenderQuery->minor_version;
+#endif
+}
+
+void QXcbBasicConnection::initializeXinerama()
+{
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_xinerama_id);
+ if (!reply || !reply->present)
+ return;
+
+ auto xineramaActive = Q_XCB_REPLY(xcb_xinerama_is_active, m_xcbConnection);
+ if (xineramaActive && xineramaActive->state)
+ m_hasXinerama = true;
+}
+
+void QXcbBasicConnection::initializeXFixes()
+{
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_xfixes_id);
+ if (!reply || !reply->present)
+ return;
+
+ auto xfixesQuery = Q_XCB_REPLY(xcb_xfixes_query_version, m_xcbConnection,
+ XCB_XFIXES_MAJOR_VERSION,
+ XCB_XFIXES_MINOR_VERSION);
+ if (!xfixesQuery || xfixesQuery->major_version < 2) {
+ qCWarning(lcQpaXcb, "failed to initialize XFixes");
+ return;
+ }
+
+ m_hasXFixes = true;
+ m_xfixesFirstEvent = reply->first_event;
+}
+
+void QXcbBasicConnection::initializeXRender()
+{
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_randr_id);
+ if (!reply || !reply->present)
+ return;
+
+ auto xrandrQuery = Q_XCB_REPLY(xcb_randr_query_version, m_xcbConnection,
+ XCB_RANDR_MAJOR_VERSION,
+ XCB_RANDR_MINOR_VERSION);
+ if (!xrandrQuery || (xrandrQuery->major_version < 1 ||
+ (xrandrQuery->major_version == 1 && xrandrQuery->minor_version < 2))) {
+ qCWarning(lcQpaXcb, "failed to initialize XRandr");
+ return;
+ }
+
+ m_hasXRandr = true;
+ m_xrandrFirstEvent = reply->first_event;
+}
+
+#if QT_CONFIG(xcb_xinput)
+void QXcbBasicConnection::initializeXInput2()
+{
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_input_id);
+ if (!reply || !reply->present) {
+ qCDebug(lcQpaXcb, "XInput extension is not present on the X server");
+ return;
+ }
+
+ auto xinputQuery = Q_XCB_REPLY(xcb_input_xi_query_version, m_xcbConnection, 2, 2);
+ if (!xinputQuery || xinputQuery->major_version != 2) {
+ qCWarning(lcQpaXcb, "X server does not support XInput 2");
+ return;
+ }
+
+ qCDebug(lcQpaXcb, "Using XInput version %d.%d",
+ xinputQuery->major_version, xinputQuery->minor_version);
+
+ m_xi2Enabled = true;
+ m_xiOpCode = reply->major_opcode;
+ m_xinputFirstEvent = reply->first_event;
+ m_xi2Minor = xinputQuery->minor_version;
+}
+#endif
+
+void QXcbBasicConnection::initializeXShape()
+{
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_shape_id);
+ if (!reply || !reply->present)
+ return;
+
+ m_hasXhape = true;
+
+ auto shapeQuery = Q_XCB_REPLY(xcb_shape_query_version, m_xcbConnection);
+ if (!shapeQuery) {
+ qCWarning(lcQpaXcb, "failed to initialize XShape extension");
+ return;
+ }
+
+ if (shapeQuery->major_version > 1 || (shapeQuery->major_version == 1 && shapeQuery->minor_version >= 1)) {
+ // The input shape is the only thing added in SHAPE 1.1
+ m_hasInputShape = true;
+ }
+}
+
+void QXcbBasicConnection::initializeXKB()
+{
+#if QT_CONFIG(xkb)
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_xcbConnection, &xcb_xkb_id);
+ if (!reply || !reply->present) {
+ qCWarning(lcQpaXcb, "XKeyboard extension not present on the X server");
+ return;
+ }
+
+ int wantMajor = 1;
+ int wantMinor = 0;
+ auto xkbQuery = Q_XCB_REPLY(xcb_xkb_use_extension, m_xcbConnection, wantMajor, wantMinor);
+ if (!xkbQuery) {
+ qCWarning(lcQpaXcb, "failed to initialize XKeyboard extension");
+ return;
+ }
+ if (!xkbQuery->supported) {
+ qCWarning(lcQpaXcb, "unsupported XKB version (we want %d.%d, but X server has %d.%d)",
+ wantMajor, wantMinor, xkbQuery->serverMajor, xkbQuery->serverMinor);
+ return;
+ }
+
+ m_hasXkb = true;
+ m_xkbFirstEvent = reply->first_event;
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.h b/src/plugins/platforms/xcb/qxcbconnection_basic.h
new file mode 100644
index 0000000000..ca91f7fa45
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbconnection_basic.h
@@ -0,0 +1,175 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QXCBBASICCONNECTION_H
+#define QXCBBASICCONNECTION_H
+
+#include "qxcbatom.h"
+
+#include <QtCore/QPair>
+#include <QtCore/QObject>
+#include <QtCore/QByteArray>
+#include <QtCore/QLoggingCategory>
+#include <QtGui/private/qtguiglobal_p.h>
+
+#include <xcb/xcb.h>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcQpaXcb)
+
+class QXcbBasicConnection : public QObject
+{
+ Q_OBJECT
+public:
+ QXcbBasicConnection(const char *displayName);
+ ~QXcbBasicConnection();
+
+#if QT_CONFIG(xcb_xlib)
+ void *xlib_display() const { return m_xlibDisplay; }
+#endif
+ const char *displayName() const { return m_displayName.constData(); }
+ int primaryScreenNumber() const { return m_primaryScreenNumber; }
+ xcb_connection_t *xcb_connection() const { return m_xcbConnection; }
+ bool isConnected() const {
+ return m_xcbConnection && !xcb_connection_has_error(m_xcbConnection);
+ }
+ const xcb_setup_t *setup() const { return m_setup; }
+
+ inline xcb_atom_t atom(QXcbAtom::Atom qatom) const { return m_xcbAtom.atom(qatom); }
+ QXcbAtom::Atom qatom(xcb_atom_t atom) const { return m_xcbAtom.qatom(atom); }
+ xcb_atom_t internAtom(const char *name);
+ QByteArray atomName(xcb_atom_t atom);
+
+ bool hasXFixes() const { return m_hasXFixes; }
+ bool hasXShape() const { return m_hasXhape; }
+ bool hasXRandr() const { return m_hasXRandr; }
+ bool hasInputShape() const { return m_hasInputShape; }
+ bool hasXKB() const { return m_hasXkb; }
+ bool hasXRender(int major = -1, int minor = -1) const {
+ if (m_hasXRender && major != -1 && minor != -1)
+ return m_xrenderVersion >= qMakePair(major, minor);
+
+ return m_hasXRender;
+ }
+ bool hasXInput2() const { return m_xi2Enabled; }
+ bool hasShm() const { return m_hasShm; }
+ bool hasShmFd() const { return m_hasShmFd; }
+ bool hasXSync() const { return m_hasXSync; }
+ bool hasXinerama() const { return m_hasXinerama; }
+
+#if QT_CONFIG(xcb_xinput)
+ bool isAtLeastXI21() const { return m_xi2Enabled && m_xi2Minor >= 1; }
+ bool isAtLeastXI22() const { return m_xi2Enabled && m_xi2Minor >= 2; }
+ bool isXIEvent(xcb_generic_event_t *event) const;
+ bool isXIType(xcb_generic_event_t *event, uint16_t type) const;
+#endif
+
+ bool isXFixesType(uint responseType, int eventType) const;
+ bool isXRandrType(uint responseType, int eventType) const;
+ bool isXkbType(uint responseType) const; // https://bugs.freedesktop.org/show_bug.cgi?id=51295
+
+protected:
+ void initializeShm();
+ void initializeXFixes();
+ void initializeXRender();
+ void initializeXRandr();
+ void initializeXinerama();
+ void initializeXShape();
+ void initializeXKB();
+ void initializeXSync();
+#if QT_CONFIG(xcb_xinput)
+ void initializeXInput2();
+#endif
+
+private:
+#if QT_CONFIG(xcb_xlib)
+ void *m_xlibDisplay = nullptr;
+#endif
+ QByteArray m_displayName;
+ xcb_connection_t *m_xcbConnection = nullptr;
+ int m_primaryScreenNumber = 0;
+ const xcb_setup_t *m_setup = nullptr;
+ QXcbAtom m_xcbAtom;
+
+ bool m_hasXFixes = false;
+ bool m_hasXinerama = false;
+ bool m_hasXhape = false;
+ bool m_hasInputShape;
+ bool m_hasXRandr = false;
+ bool m_hasXkb = false;
+ bool m_hasXRender = false;
+ bool m_hasShm = false;
+ bool m_hasShmFd = false;
+ bool m_hasXSync = false;
+
+ QPair<int, int> m_xrenderVersion;
+
+ bool m_xi2Enabled = false;
+#if QT_CONFIG(xcb_xinput)
+ int m_xi2Minor = -1;
+ int m_xiOpCode = -1;
+ uint32_t m_xinputFirstEvent = 0;
+#endif
+
+ uint32_t m_xfixesFirstEvent = 0;
+ uint32_t m_xrandrFirstEvent = 0;
+ uint32_t m_xkbFirstEvent = 0;
+};
+
+#define Q_XCB_REPLY_CONNECTION_ARG(connection, ...) connection
+
+struct QStdFreeDeleter {
+ void operator()(void *p) const Q_DECL_NOTHROW { return std::free(p); }
+};
+
+#define Q_XCB_REPLY(call, ...) \
+ std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \
+ call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call(__VA_ARGS__), nullptr) \
+ )
+
+#define Q_XCB_REPLY_UNCHECKED(call, ...) \
+ std::unique_ptr<call##_reply_t, QStdFreeDeleter>( \
+ call##_reply(Q_XCB_REPLY_CONNECTION_ARG(__VA_ARGS__), call##_unchecked(__VA_ARGS__), nullptr) \
+ )
+
+QT_END_NAMESPACE
+
+#endif // QXCBBASICCONNECTION_H
diff --git a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp
new file mode 100644
index 0000000000..4c380bf39f
--- /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 <xcb/xinerama.h>
+
+void QXcbConnection::selectXRandrEvents()
+{
+ xcb_screen_iterator_t rootIter = xcb_setup_roots_iterator(setup());
+ for (; rootIter.rem; xcb_screen_next(&rootIter)) {
+ xcb_randr_select_input(xcb_connection(),
+ rootIter.data->root,
+ XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE |
+ XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE |
+ XCB_RANDR_NOTIFY_MASK_CRTC_CHANGE |
+ XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY
+ );
+ }
+}
+
+QXcbScreen* QXcbConnection::findScreenForCrtc(xcb_window_t rootWindow, xcb_randr_crtc_t crtc) const
+{
+ for (QXcbScreen *screen : m_screens) {
+ if (screen->root() == rootWindow && screen->crtc() == crtc)
+ return screen;
+ }
+
+ return nullptr;
+}
+
+QXcbScreen* QXcbConnection::findScreenForOutput(xcb_window_t rootWindow, xcb_randr_output_t output) const
+{
+ for (QXcbScreen *screen : m_screens) {
+ if (screen->root() == rootWindow && screen->output() == output)
+ return screen;
+ }
+
+ return nullptr;
+}
+
+QXcbVirtualDesktop* QXcbConnection::virtualDesktopForRootWindow(xcb_window_t rootWindow) const
+{
+ for (QXcbVirtualDesktop *virtualDesktop : m_virtualDesktops) {
+ if (virtualDesktop->screen()->root == rootWindow)
+ return virtualDesktop;
+ }
+
+ return nullptr;
+}
+
+/*!
+ \brief Synchronizes the screen list, adds new screens, removes deleted ones
+*/
+void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event)
+{
+ if (event->subCode == XCB_RANDR_NOTIFY_CRTC_CHANGE) {
+ xcb_randr_crtc_change_t crtc = event->u.cc;
+ QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(crtc.window);
+ if (!virtualDesktop)
+ // Not for us
+ return;
+
+ QXcbScreen *screen = findScreenForCrtc(crtc.window, crtc.crtc);
+ qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_CRTC_CHANGE:" << crtc.crtc
+ << "mode" << crtc.mode << "relevant screen" << screen;
+ // Only update geometry when there's a valid mode on the CRTC
+ // CRTC with node mode could mean that output has been disabled, and we'll
+ // get RRNotifyOutputChange notification for that.
+ if (screen && crtc.mode) {
+ if (crtc.rotation == XCB_RANDR_ROTATION_ROTATE_90 ||
+ crtc.rotation == XCB_RANDR_ROTATION_ROTATE_270)
+ std::swap(crtc.width, crtc.height);
+ screen->updateGeometry(QRect(crtc.x, crtc.y, crtc.width, crtc.height), crtc.rotation);
+ if (screen->mode() != crtc.mode)
+ screen->updateRefreshRate(crtc.mode);
+ }
+
+ } else if (event->subCode == XCB_RANDR_NOTIFY_OUTPUT_CHANGE) {
+ xcb_randr_output_change_t output = event->u.oc;
+ QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(output.window);
+ if (!virtualDesktop)
+ // Not for us
+ return;
+
+ QXcbScreen *screen = findScreenForOutput(output.window, output.output);
+ qCDebug(lcQpaScreen) << "QXcbConnection: XCB_RANDR_NOTIFY_OUTPUT_CHANGE:" << output.output;
+
+ if (screen && output.connection == XCB_RANDR_CONNECTION_DISCONNECTED) {
+ qCDebug(lcQpaScreen) << "screen" << screen->name() << "has been disconnected";
+ destroyScreen(screen);
+ } else if (!screen && output.connection == XCB_RANDR_CONNECTION_CONNECTED) {
+ // New XRandR output is available and it's enabled
+ if (output.crtc != XCB_NONE && output.mode != XCB_NONE) {
+ auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
+ output.output, output.config_timestamp);
+ // Find a fake screen
+ const auto scrs = virtualDesktop->screens();
+ for (QPlatformScreen *scr : scrs) {
+ QXcbScreen *xcbScreen = static_cast<QXcbScreen *>(scr);
+ if (xcbScreen->output() == XCB_NONE) {
+ screen = xcbScreen;
+ break;
+ }
+ }
+
+ if (screen) {
+ QString nameWas = screen->name();
+ // Transform the fake screen into a physical screen
+ screen->setOutput(output.output, outputInfo.get());
+ updateScreen(screen, output);
+ qCDebug(lcQpaScreen) << "output" << screen->name()
+ << "is connected and enabled; was fake:" << nameWas;
+ } else {
+ screen = createScreen(virtualDesktop, output, outputInfo.get());
+ qCDebug(lcQpaScreen) << "output" << screen->name() << "is connected and enabled";
+ }
+ QHighDpiScaling::updateHighDpiScaling();
+ }
+ } else if (screen) {
+ if (output.crtc == XCB_NONE && output.mode == XCB_NONE) {
+ // Screen has been disabled
+ auto outputInfo = Q_XCB_REPLY(xcb_randr_get_output_info, xcb_connection(),
+ output.output, output.config_timestamp);
+ if (outputInfo->crtc == XCB_NONE) {
+ qCDebug(lcQpaScreen) << "output" << screen->name() << "has been disabled";
+ destroyScreen(screen);
+ } else {
+ qCDebug(lcQpaScreen) << "output" << screen->name() << "has been temporarily disabled for the mode switch";
+ // Reset crtc to skip RRCrtcChangeNotify events,
+ // because they may be invalid in the middle of the mode switch
+ screen->setCrtc(XCB_NONE);
+ }
+ } else {
+ updateScreen(screen, output);
+ qCDebug(lcQpaScreen) << "output has changed" << screen;
+ }
+ }
+
+ qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name();
+ }
+}
+
+bool QXcbConnection::checkOutputIsPrimary(xcb_window_t rootWindow, xcb_randr_output_t output)
+{
+ auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), rootWindow);
+ if (!primary)
+ qWarning("failed to get the primary output of the screen");
+
+ const bool isPrimary = primary ? (primary->output == output) : false;
+
+ return isPrimary;
+}
+
+void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_change_t &outputChange)
+{
+ screen->setCrtc(outputChange.crtc); // Set the new crtc, because it can be invalid
+ screen->updateGeometry(outputChange.config_timestamp);
+ if (screen->mode() != outputChange.mode)
+ screen->updateRefreshRate(outputChange.mode);
+ // Only screen which belongs to the primary virtual desktop can be a primary screen
+ if (screen->screenNumber() == primaryScreenNumber()) {
+ if (!screen->isPrimary() && checkOutputIsPrimary(outputChange.window, outputChange.output)) {
+ screen->setPrimary(true);
+
+ // If the screen became primary, reshuffle the order in QGuiApplicationPrivate
+ const int idx = m_screens.indexOf(screen);
+ if (idx > 0) {
+ qAsConst(m_screens).first()->setPrimary(false);
+ m_screens.swap(0, idx);
+ }
+ screen->virtualDesktop()->setPrimaryScreen(screen);
+ QXcbIntegration::instance()->setPrimaryScreen(screen);
+ }
+ }
+}
+
+QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop,
+ const xcb_randr_output_change_t &outputChange,
+ xcb_randr_get_output_info_reply_t *outputInfo)
+{
+ QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputChange.output, outputInfo);
+ // Only screen which belongs to the primary virtual desktop can be a primary screen
+ if (screen->screenNumber() == primaryScreenNumber())
+ screen->setPrimary(checkOutputIsPrimary(outputChange.window, outputChange.output));
+
+ if (screen->isPrimary()) {
+ if (!m_screens.isEmpty())
+ qAsConst(m_screens).first()->setPrimary(false);
+
+ m_screens.prepend(screen);
+ } else {
+ m_screens.append(screen);
+ }
+ virtualDesktop->addScreen(screen);
+ QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary());
+
+ return screen;
+}
+
+void QXcbConnection::destroyScreen(QXcbScreen *screen)
+{
+ QXcbVirtualDesktop *virtualDesktop = screen->virtualDesktop();
+ if (virtualDesktop->screens().count() == 1) {
+ // If there are no other screens on the same virtual desktop,
+ // then transform the physical screen into a fake screen.
+ const QString nameWas = screen->name();
+ screen->setOutput(XCB_NONE, nullptr);
+ qCDebug(lcQpaScreen) << "transformed" << nameWas << "to fake" << screen;
+ } else {
+ // There is more than one screen on the same virtual desktop, remove the screen
+ m_screens.removeOne(screen);
+ virtualDesktop->removeScreen(screen);
+
+ // When primary screen is removed, set the new primary screen
+ // which belongs to the primary virtual desktop.
+ if (screen->isPrimary()) {
+ QXcbScreen *newPrimary = static_cast<QXcbScreen *>(virtualDesktop->screens().at(0));
+ newPrimary->setPrimary(true);
+ const int idx = m_screens.indexOf(newPrimary);
+ if (idx > 0)
+ m_screens.swap(0, idx);
+ QXcbIntegration::instance()->setPrimaryScreen(newPrimary);
+ }
+
+ QXcbIntegration::instance()->destroyScreen(screen);
+ }
+}
+
+void QXcbConnection::initializeScreens()
+{
+ selectXRandrEvents();
+
+ 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 (hasXRender()) {
+ // RRGetScreenResourcesCurrent is fast but it may return nothing if the
+ // configuration is not initialized wrt to the hardware. We should call
+ // RRGetScreenResources in this case.
+ auto resources_current = Q_XCB_REPLY(xcb_randr_get_screen_resources_current,
+ xcb_connection(), xcbScreen->root);
+ if (!resources_current) {
+ qWarning("failed to get the current screen resources");
+ } else {
+ xcb_timestamp_t timestamp = 0;
+ xcb_randr_output_t *outputs = nullptr;
+ int outputCount = xcb_randr_get_screen_resources_current_outputs_length(resources_current.get());
+ if (outputCount) {
+ timestamp = resources_current->config_timestamp;
+ outputs = xcb_randr_get_screen_resources_current_outputs(resources_current.get());
+ } else {
+ auto resources = Q_XCB_REPLY(xcb_randr_get_screen_resources,
+ xcb_connection(), xcbScreen->root);
+ if (!resources) {
+ qWarning("failed to get the screen resources");
+ } else {
+ timestamp = resources->config_timestamp;
+ outputCount = xcb_randr_get_screen_resources_outputs_length(resources.get());
+ outputs = xcb_randr_get_screen_resources_outputs(resources.get());
+ }
+ }
+
+ if (outputCount) {
+ auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, xcb_connection(), xcbScreen->root);
+ if (!primary) {
+ qWarning("failed to get the primary output of the screen");
+ } else {
+ for (int i = 0; i < outputCount; i++) {
+ auto output = Q_XCB_REPLY_UNCHECKED(xcb_randr_get_output_info,
+ xcb_connection(), outputs[i], timestamp);
+ // Invalid, disconnected or disabled output
+ if (!output)
+ continue;
+
+ if (output->connection != XCB_RANDR_CONNECTION_CONNECTED) {
+ qCDebug(lcQpaScreen, "Output %s is not connected", qPrintable(
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
+ xcb_randr_get_output_info_name_length(output.get()))));
+ continue;
+ }
+
+ if (output->crtc == XCB_NONE) {
+ qCDebug(lcQpaScreen, "Output %s is not enabled", qPrintable(
+ QString::fromUtf8((const char*)xcb_randr_get_output_info_name(output.get()),
+ xcb_randr_get_output_info_name_length(output.get()))));
+ continue;
+ }
+
+ QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, outputs[i], output.get());
+ siblings << screen;
+ m_screens << screen;
+
+ // There can be multiple outputs per screen, use either
+ // the first or an exact match. An exact match isn't
+ // always available if primary->output is XCB_NONE
+ // or currently disconnected output.
+ if (primaryScreenNumber() == xcbScreenNumber) {
+ if (!primaryScreen || (primary && outputs[i] == primary->output)) {
+ if (primaryScreen)
+ primaryScreen->setPrimary(false);
+ primaryScreen = screen;
+ primaryScreen->setPrimary(true);
+ siblings.prepend(siblings.takeLast());
+ }
+ }
+ }
+ }
+ }
+ }
+ } else if (hasXinerama()) {
+ // Xinerama is available
+ auto screens = Q_XCB_REPLY(xcb_xinerama_query_screens, xcb_connection());
+ if (screens) {
+ xcb_xinerama_screen_info_iterator_t it = xcb_xinerama_query_screens_screen_info_iterator(screens.get());
+ while (it.rem) {
+ xcb_xinerama_screen_info_t *screen_info = it.data;
+ QXcbScreen *screen = new QXcbScreen(this, virtualDesktop,
+ XCB_NONE, nullptr,
+ screen_info, it.index);
+ siblings << screen;
+ m_screens << screen;
+ xcb_xinerama_screen_info_next(&it);
+ }
+ }
+ }
+ if (siblings.isEmpty()) {
+ // If there are no XRandR outputs or XRandR extension is missing,
+ // then create a fake/legacy screen.
+ QXcbScreen *screen = new QXcbScreen(this, virtualDesktop, XCB_NONE, nullptr);
+ qCDebug(lcQpaScreen) << "created fake screen" << screen;
+ m_screens << screen;
+ if (primaryScreenNumber() == xcbScreenNumber) {
+ primaryScreen = screen;
+ primaryScreen->setPrimary(true);
+ }
+ siblings << screen;
+ }
+ virtualDesktop->setScreens(siblings);
+ xcb_screen_next(&it);
+ ++xcbScreenNumber;
+ } // for each xcb screen
+
+ for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops))
+ virtualDesktop->subscribeToXFixesSelectionNotify();
+
+ if (m_virtualDesktops.isEmpty()) {
+ qFatal("QXcbConnection: no screens available");
+ } else {
+ // Ensure the primary screen is first on the list
+ if (primaryScreen) {
+ if (qAsConst(m_screens).first() != primaryScreen) {
+ m_screens.removeOne(primaryScreen);
+ m_screens.prepend(primaryScreen);
+ }
+ }
+
+ // Push the screens to QGuiApplication
+ for (QXcbScreen *screen : qAsConst(m_screens)) {
+ qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")";
+ QXcbIntegration::instance()->screenAdded(screen, screen->isPrimary());
+ }
+
+ qCDebug(lcQpaScreen) << "primary output is" << qAsConst(m_screens).first()->name();
+ }
+}
diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
index a3befc7384..04ddd3c98c 100644
--- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp
@@ -51,31 +51,6 @@
using qt_xcb_input_device_event_t = xcb_input_button_press_event_t;
-void QXcbConnection::initializeXInput2()
-{
- const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_connection, &xcb_input_id);
- if (!reply || !reply->present) {
- qCDebug(lcQpaXInput, "XInput extension is not present on the X server");
- return;
- }
-
- m_xiOpCode = reply->major_opcode;
- xinput_first_event = reply->first_event;
-
- auto xinput_query = Q_XCB_REPLY(xcb_input_xi_query_version, m_connection, 2, 2);
-
- if (!xinput_query || xinput_query->major_version != 2) {
- qCWarning(lcQpaXInput, "X server does not support XInput 2");
- } else {
- qCDebug(lcQpaXInput, "Using XInput version %d.%d",
- xinput_query->major_version, xinput_query->minor_version);
- m_xi2Minor = xinput_query->minor_version;
- m_xi2Enabled = true;
- xi2SetupDevices();
- xi2SelectStateEvents();
- }
-}
-
struct qt_xcb_input_event_mask_t {
xcb_input_event_mask_t header;
uint32_t mask;
@@ -91,7 +66,7 @@ void QXcbConnection::xi2SelectStateEvents()
xiEventMask.mask = XCB_INPUT_XI_EVENT_MASK_HIERARCHY;
xiEventMask.mask |= XCB_INPUT_XI_EVENT_MASK_DEVICE_CHANGED;
xiEventMask.mask |= XCB_INPUT_XI_EVENT_MASK_PROPERTY;
- xcb_input_xi_select_events(m_connection, rootWindow(), 1, &xiEventMask.header);
+ xcb_input_xi_select_events(xcb_connection(), rootWindow(), 1, &xiEventMask.header);
}
void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
@@ -117,8 +92,8 @@ void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
mask.header.mask_len = 1;
mask.mask = bitMask;
xcb_void_cookie_t cookie =
- xcb_input_xi_select_events_checked(m_connection, window, 1, &mask.header);
- xcb_generic_error_t *error = xcb_request_check(m_connection, cookie);
+ xcb_input_xi_select_events_checked(xcb_connection(), window, 1, &mask.header);
+ xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie);
if (error) {
qCDebug(lcQpaXInput, "failed to select events, window %x, error code %d", window, error->error_code);
free(error);
@@ -310,7 +285,7 @@ void QXcbConnection::xi2SetupDevices()
m_touchDevices.clear();
m_xiMasterPointerIds.clear();
- auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, m_connection, XCB_INPUT_DEVICE_ALL);
+ auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), XCB_INPUT_DEVICE_ALL);
if (!reply) {
qCDebug(lcQpaXInputDevices) << "failed to query devices";
return;
@@ -387,8 +362,8 @@ void QXcbConnection::xi2SelectDeviceEventsCompatibility(xcb_window_t window)
xiMask.mask = mask;
xcb_void_cookie_t cookie =
- xcb_input_xi_select_events_checked(m_connection, window, 1, &xiMask.header);
- xcb_generic_error_t *error = xcb_request_check(m_connection, cookie);
+ xcb_input_xi_select_events_checked(xcb_connection(), window, 1, &xiMask.header);
+ xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie);
if (error) {
qCDebug(lcQpaXInput, "failed to select events, window %x, error code %d", window, error->error_code);
free(error);
@@ -413,7 +388,7 @@ void QXcbConnection::xi2SelectDeviceEventsCompatibility(xcb_window_t window)
xiEventMask[i].header.mask_len = 1;
xiEventMask[i].mask = mask;
}
- xcb_input_xi_select_events(m_connection, window, nrTablets, &(xiEventMask.data()->header));
+ xcb_input_xi_select_events(xcb_connection(), window, nrTablets, &(xiEventMask.data()->header));
}
#endif
@@ -430,7 +405,7 @@ void QXcbConnection::xi2SelectDeviceEventsCompatibility(xcb_window_t window)
xiEventMask[i].mask = mask;
i++;
}
- xcb_input_xi_select_events(m_connection, window, i, &(xiEventMask.data()->header));
+ xcb_input_xi_select_events(xcb_connection(), window, i, &(xiEventMask.data()->header));
}
}
@@ -633,7 +608,7 @@ bool QXcbConnection::xi2MouseEventsDisabled() const
static bool xi2MouseDisabled = qEnvironmentVariableIsSet("QT_XCB_NO_XI2_MOUSE");
// FIXME: Don't use XInput2 mouse events when Xinerama extension
// is enabled, because it causes problems with multi-monitor setup.
- return xi2MouseDisabled || has_xinerama_extension;
+ return xi2MouseDisabled || hasXinerama();
}
bool QXcbConnection::isTouchScreen(int id)
@@ -745,7 +720,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
// Touches must be accepted when we are grabbing touch events. Otherwise the entire sequence
// will get replayed when the grab ends.
if (m_xiGrab) {
- xcb_input_xi_allow_events(m_connection, XCB_CURRENT_TIME, xiDeviceEvent->deviceid,
+ xcb_input_xi_allow_events(xcb_connection(), XCB_CURRENT_TIME, xiDeviceEvent->deviceid,
XCB_INPUT_EVENT_MODE_ACCEPT_TOUCH,
xiDeviceEvent->detail, xiDeviceEvent->event);
}
@@ -771,7 +746,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo
xiDeviceEvent->detail == m_startSystemMoveResizeInfo.pointid) {
QXcbWindow *window = platformWindowFromId(m_startSystemMoveResizeInfo.window);
if (window) {
- xcb_input_xi_allow_events(m_connection, XCB_CURRENT_TIME, xiDeviceEvent->deviceid,
+ xcb_input_xi_allow_events(xcb_connection(), XCB_CURRENT_TIME, xiDeviceEvent->deviceid,
XCB_INPUT_EVENT_MODE_REJECT_TOUCH,
xiDeviceEvent->detail, xiDeviceEvent->event);
window->doStartSystemMoveResize(QPoint(x, y), m_startSystemMoveResizeInfo.corner);
@@ -850,10 +825,10 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
for (int id : m_xiMasterPointerIds) {
xcb_generic_error_t *error = nullptr;
- auto cookie = xcb_input_xi_grab_device(m_connection, w, XCB_CURRENT_TIME, XCB_CURSOR_NONE, id,
+ auto cookie = xcb_input_xi_grab_device(xcb_connection(), w, XCB_CURRENT_TIME, XCB_CURSOR_NONE, id,
XCB_INPUT_GRAB_MODE_22_ASYNC, XCB_INPUT_GRAB_MODE_22_ASYNC,
false, 1, &mask);
- auto *reply = xcb_input_xi_grab_device_reply(m_connection, cookie, &error);
+ auto *reply = xcb_input_xi_grab_device_reply(xcb_connection(), cookie, &error);
if (error) {
qCDebug(lcQpaXInput, "failed to grab events for device %d on window %x"
"(error code %d)", id, w, error->error_code);
@@ -867,8 +842,8 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)
}
} else { // ungrab
for (int id : m_xiMasterPointerIds) {
- auto cookie = xcb_input_xi_ungrab_device_checked(m_connection, XCB_CURRENT_TIME, id);
- xcb_generic_error_t *error = xcb_request_check(m_connection, cookie);
+ auto cookie = xcb_input_xi_ungrab_device_checked(xcb_connection(), XCB_CURRENT_TIME, id);
+ xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie);
if (error) {
qCDebug(lcQpaXInput, "XIUngrabDevice failed - id: %d (error code %d)", id, error->error_code);
free(error);
@@ -911,7 +886,7 @@ void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
auto *xiEvent = reinterpret_cast<xcb_input_device_changed_event_t *>(event);
switch (xiEvent->reason) {
case XCB_INPUT_CHANGE_REASON_DEVICE_CHANGE: {
- auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, m_connection, xiEvent->sourceid);
+ auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), xiEvent->sourceid);
if (!reply || reply->num_infos <= 0)
return;
auto it = xcb_input_xi_query_device_infos_iterator(reply.get());
@@ -931,7 +906,7 @@ void QXcbConnection::xi2HandleDeviceChangedEvent(void *event)
void QXcbConnection::xi2UpdateScrollingDevice(ScrollingDevice &scrollingDevice)
{
- auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, m_connection, scrollingDevice.deviceId);
+ auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), scrollingDevice.deviceId);
if (!reply || reply->num_infos <= 0) {
qCDebug(lcQpaXInputDevices, "scrolling device %d no longer present", scrollingDevice.deviceId);
return;
@@ -1184,7 +1159,7 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD
_WACSER_COUNT
};
- auto reply = Q_XCB_REPLY(xcb_input_xi_get_property, m_connection, tabletData->deviceId, 0,
+ auto reply = Q_XCB_REPLY(xcb_input_xi_get_property, xcb_connection(), tabletData->deviceId, 0,
ev->property, XCB_GET_PROPERTY_TYPE_ANY, 0, 100);
if (reply) {
if (reply->type == atom(QXcbAtom::INTEGER) && reply->format == 32 && reply->num_items == _WACSER_COUNT) {
diff --git a/src/plugins/platforms/xcb/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/qxcbeventdispatcher.cpp b/src/plugins/platforms/xcb/qxcbeventdispatcher.cpp
new file mode 100644
index 0000000000..3cb2a5b5ef
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbeventdispatcher.cpp
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qxcbeventdispatcher.h"
+#include "qxcbconnection.h"
+
+#include <QtCore/QCoreApplication>
+
+#include <qpa/qwindowsysteminterface.h>
+
+QT_BEGIN_NAMESPACE
+
+QXcbUnixEventDispatcher::QXcbUnixEventDispatcher(QXcbConnection *connection, QObject *parent)
+ : QEventDispatcherUNIX(parent)
+ , m_connection(connection)
+{
+}
+
+QXcbUnixEventDispatcher::~QXcbUnixEventDispatcher()
+{
+}
+
+bool QXcbUnixEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ const bool didSendEvents = QEventDispatcherUNIX::processEvents(flags);
+ m_connection->processXcbEvents(flags);
+ // The following line should not be necessary after QTBUG-70095
+ return QWindowSystemInterface::sendWindowSystemEvents(flags) || didSendEvents;
+}
+
+bool QXcbUnixEventDispatcher::hasPendingEvents()
+{
+ extern uint qGlobalPostedEventsCount();
+ return qGlobalPostedEventsCount() || QWindowSystemInterface::windowSystemEventsQueued();
+}
+
+void QXcbUnixEventDispatcher::flush()
+{
+ if (qApp)
+ qApp->sendPostedEvents();
+}
+
+#if QT_CONFIG(glib)
+struct XcbEventSource
+{
+ GSource source;
+ QXcbGlibEventDispatcher *dispatcher;
+ QXcbGlibEventDispatcherPrivate *dispatcher_p;
+ QXcbConnection *connection = nullptr;
+};
+
+static gboolean xcbSourcePrepare(GSource *source, gint *timeout)
+{
+ Q_UNUSED(timeout)
+ auto xcbEventSource = reinterpret_cast<XcbEventSource *>(source);
+ return xcbEventSource->dispatcher_p->wakeUpCalled;
+}
+
+static gboolean xcbSourceCheck(GSource *source)
+{
+ return xcbSourcePrepare(source, nullptr);
+}
+
+static gboolean xcbSourceDispatch(GSource *source, GSourceFunc, gpointer)
+{
+ auto xcbEventSource = reinterpret_cast<XcbEventSource *>(source);
+ QEventLoop::ProcessEventsFlags flags = xcbEventSource->dispatcher->flags();
+ xcbEventSource->connection->processXcbEvents(flags);
+ // The following line should not be necessary after QTBUG-70095
+ QWindowSystemInterface::sendWindowSystemEvents(flags);
+ return true;
+}
+
+QXcbGlibEventDispatcher::QXcbGlibEventDispatcher(QXcbConnection *connection, QObject *parent)
+ : QEventDispatcherGlib(*new QXcbGlibEventDispatcherPrivate(), parent)
+{
+ Q_D(QXcbGlibEventDispatcher);
+
+ m_xcbEventSourceFuncs.prepare = xcbSourcePrepare;
+ m_xcbEventSourceFuncs.check = xcbSourceCheck;
+ m_xcbEventSourceFuncs.dispatch = xcbSourceDispatch;
+ m_xcbEventSourceFuncs.finalize = nullptr;
+
+ m_xcbEventSource = reinterpret_cast<XcbEventSource *>(
+ g_source_new(&m_xcbEventSourceFuncs, sizeof(XcbEventSource)));
+
+ m_xcbEventSource->dispatcher = this;
+ m_xcbEventSource->dispatcher_p = d_func();
+ m_xcbEventSource->connection = connection;
+
+ g_source_set_can_recurse(&m_xcbEventSource->source, true);
+ g_source_attach(&m_xcbEventSource->source, d->mainContext);
+}
+
+QXcbGlibEventDispatcherPrivate::QXcbGlibEventDispatcherPrivate()
+{
+}
+
+QXcbGlibEventDispatcher::~QXcbGlibEventDispatcher()
+{
+ g_source_destroy(&m_xcbEventSource->source);
+ g_source_unref(&m_xcbEventSource->source);
+}
+
+bool QXcbGlibEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ m_flags = flags;
+ return QEventDispatcherGlib::processEvents(m_flags);
+}
+
+#endif // QT_CONFIG(glib)
+
+QAbstractEventDispatcher *QXcbEventDispatcher::createEventDispatcher(QXcbConnection *connection)
+{
+#if QT_CONFIG(glib)
+ if (qEnvironmentVariableIsEmpty("QT_NO_GLIB") && QEventDispatcherGlib::versionSupported()) {
+ qCDebug(lcQpaXcb, "using glib dispatcher");
+ return new QXcbGlibEventDispatcher(connection);
+ } else
+#endif
+ {
+ qCDebug(lcQpaXcb, "using unix dispatcher");
+ return new QXcbUnixEventDispatcher(connection);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbeventdispatcher.h b/src/plugins/platforms/xcb/qxcbeventdispatcher.h
new file mode 100644
index 0000000000..6aadd63a70
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbeventdispatcher.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QXCBEVENTDISPATCHER_H
+#define QXCBEVENTDISPATCHER_H
+
+#include <QtCore/QObject>
+#include <QtCore/QEventLoop>
+
+#include <QtCore/private/qeventdispatcher_unix_p.h>
+#if QT_CONFIG(glib)
+#include <QtCore/private/qeventdispatcher_glib_p.h>
+#include <glib.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QXcbConnection;
+
+class QXcbUnixEventDispatcher : public QEventDispatcherUNIX
+{
+ Q_OBJECT
+public:
+ explicit QXcbUnixEventDispatcher(QXcbConnection *connection, QObject *parent = nullptr);
+ ~QXcbUnixEventDispatcher();
+ bool processEvents(QEventLoop::ProcessEventsFlags flags) override;
+
+ // Maybe some user code depends on deprecated QUnixEventDispatcherQPA::
+ // hasPendingEvents() / flush() implementation, so keep it around until
+ // Qt 6. These methods are deprecated in QAbstractEventDispatcher.
+ bool hasPendingEvents() override; // ### Qt 6 remove
+ void flush() override; // ### Qt 6 remove
+
+private:
+ QXcbConnection *m_connection;
+};
+
+#if QT_CONFIG(glib)
+
+struct XcbEventSource;
+class QXcbGlibEventDispatcherPrivate;
+
+class QXcbGlibEventDispatcher : public QEventDispatcherGlib
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QXcbGlibEventDispatcher)
+
+public:
+ explicit QXcbGlibEventDispatcher(QXcbConnection *connection, QObject *parent = nullptr);
+ ~QXcbGlibEventDispatcher();
+
+ bool processEvents(QEventLoop::ProcessEventsFlags flags) override;
+ QEventLoop::ProcessEventsFlags flags() const { return m_flags; }
+
+private:
+ XcbEventSource *m_xcbEventSource;
+ GSourceFuncs m_xcbEventSourceFuncs;
+ QEventLoop::ProcessEventsFlags m_flags;
+};
+
+class QXcbGlibEventDispatcherPrivate : public QEventDispatcherGlibPrivate
+{
+ Q_DECLARE_PUBLIC(QXcbGlibEventDispatcher)
+
+public:
+ QXcbGlibEventDispatcherPrivate();
+};
+
+#endif // QT_CONFIG(glib)
+
+class QXcbEventDispatcher
+{
+public:
+ static QAbstractEventDispatcher *createEventDispatcher(QXcbConnection *connection);
+
+private:
+ QXcbConnection *m_connection;
+};
+
+QT_END_NAMESPACE
+
+#endif // QXCBEVENTDISPATCHER_H
diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.cpp b/src/plugins/platforms/xcb/qxcbeventqueue.cpp
new file mode 100644
index 0000000000..862f68764b
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbeventqueue.cpp
@@ -0,0 +1,383 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "qxcbeventqueue.h"
+#include "qxcbconnection.h"
+
+#include <QtCore/QObject>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QAbstractEventDispatcher>
+#include <QtCore/QMutex>
+#include <QtCore/QDebug>
+
+QT_BEGIN_NAMESPACE
+
+static QBasicMutex qAppExiting;
+static bool dispatcherOwnerDestructing = false;
+
+/*!
+ \class QXcbEventQueue
+ \internal
+
+ Lock-free event passing:
+
+ The lock-free solution uses a singly-linked list to pass events from the
+ reader thread to the main thread. An atomic operation is used to sync the
+ tail node of the list between threads. The reader thread takes special care
+ when accessing the tail node. It does not dequeue the last node and does not
+ access (read or write) the tail node's 'next' member. This lets the reader
+ add more items at the same time as the main thread is dequeuing nodes from
+ the head. A custom linked list implementation is used, because QLinkedList
+ does not have any thread-safety guarantees and the custom list is more
+ lightweight - no reference counting, back links, etc.
+
+ Memory management:
+
+ In a normally functioning application, XCB plugin won't buffer more than few
+ batches of events, couple events per batch. Instead of constantly calling
+ new / delete, we can create a pool of nodes that we reuse. The main thread
+ uses an atomic operation to sync how many nodes have been restored (available
+ for reuse). If at some point a user application will block the main thread
+ for a long time, we might run out of nodes in the pool. Then we create nodes
+ on a heap. These will be automatically "garbage collected" out of the linked
+ list, once the main thread stops blocking.
+*/
+
+QXcbEventQueue::QXcbEventQueue(QXcbConnection *connection)
+ : m_connection(connection)
+{
+ // When running test cases in auto tests, static variables are preserved
+ // between test function runs, even if Q*Application object is destroyed.
+ // Reset to default value to account for this.
+ dispatcherOwnerDestructing = false;
+ qAddPostRoutine([]() {
+ QMutexLocker locker(&qAppExiting);
+ dispatcherOwnerDestructing = true;
+ });
+
+ // Lets init the list with one node, so we don't have to check for
+ // this special case in various places.
+ m_head = m_flushedTail = qXcbEventNodeFactory(nullptr);
+ m_tail.store(m_head, std::memory_order_release);
+
+ start();
+}
+
+QXcbEventQueue::~QXcbEventQueue()
+{
+ if (isRunning()) {
+ sendCloseConnectionEvent();
+ wait();
+ }
+
+ while (xcb_generic_event_t *event = takeFirst())
+ free(event);
+
+ if (m_head && m_head->fromHeap)
+ delete m_head; // the deferred node
+
+ qCDebug(lcQpaEventReader) << "nodes on heap:" << m_nodesOnHeap;
+}
+
+xcb_generic_event_t *QXcbEventQueue::takeFirst(QEventLoop::ProcessEventsFlags flags)
+{
+ // This is the level at which we were moving excluded user input events into
+ // separate queue in Qt 4 (see qeventdispatcher_x11.cpp). In this case
+ // QXcbEventQueue represents Xlib's internal event queue. In Qt 4, Xlib's
+ // event queue peeking APIs would not see these events anymore, the same way
+ // our peeking functions do not consider m_inputEvents. This design is
+ // intentional to keep the same behavior. We could do filtering directly on
+ // QXcbEventQueue, without the m_inputEvents, but it is not clear if it is
+ // needed by anyone who peeks at the native event queue.
+
+ bool excludeUserInputEvents = flags.testFlag(QEventLoop::ExcludeUserInputEvents);
+ if (excludeUserInputEvents) {
+ xcb_generic_event_t *event = nullptr;
+ while ((event = takeFirst())) {
+ if (m_connection->isUserInputEvent(event)) {
+ m_inputEvents << event;
+ continue;
+ }
+ break;
+ }
+ return event;
+ }
+
+ if (!m_inputEvents.isEmpty())
+ return m_inputEvents.takeFirst();
+ return takeFirst();
+}
+
+xcb_generic_event_t *QXcbEventQueue::takeFirst()
+{
+ if (isEmpty())
+ return nullptr;
+
+ xcb_generic_event_t *event = nullptr;
+ do {
+ event = m_head->event;
+ if (m_head == m_flushedTail) {
+ // defer dequeuing until next successful flush of events
+ if (event) // check if not cleared already by some filter
+ m_head->event = nullptr; // if not, clear it
+ } else {
+ dequeueNode();
+ if (!event)
+ continue; // consumed by filter or deferred node
+ }
+ } while (!isEmpty() && !event);
+
+ m_queueModified = m_peekerIndexCacheDirty = true;
+
+ return event;
+}
+
+void QXcbEventQueue::dequeueNode()
+{
+ QXcbEventNode *node = m_head;
+ m_head = m_head->next;
+ if (node->fromHeap)
+ delete node;
+ else
+ m_nodesRestored.fetch_add(1, std::memory_order_release);
+}
+
+void QXcbEventQueue::flushBufferedEvents()
+{
+ m_flushedTail = m_tail.load(std::memory_order_acquire);
+}
+
+QXcbEventNode *QXcbEventQueue::qXcbEventNodeFactory(xcb_generic_event_t *event)
+{
+ static QXcbEventNode qXcbNodePool[PoolSize];
+
+ if (m_freeNodes == 0) // out of nodes, check if the main thread has released any
+ m_freeNodes = m_nodesRestored.exchange(0, std::memory_order_acquire);
+
+ if (m_freeNodes) {
+ m_freeNodes--;
+ if (m_poolIndex == PoolSize) {
+ // wrap back to the beginning, we always take and restore nodes in-order
+ m_poolIndex = 0;
+ }
+ QXcbEventNode *node = &qXcbNodePool[m_poolIndex++];
+ node->event = event;
+ node->next = nullptr;
+ return node;
+ }
+
+ // the main thread is not flushing events and thus the pool has become empty
+ auto node = new QXcbEventNode(event);
+ node->fromHeap = true;
+ qCDebug(lcQpaEventReader) << "[heap] " << m_nodesOnHeap++;
+ return node;
+}
+
+void QXcbEventQueue::run()
+{
+ xcb_generic_event_t *event = nullptr;
+ xcb_connection_t *connection = m_connection->xcb_connection();
+ QXcbEventNode *tail = m_head;
+
+ auto enqueueEvent = [&tail, this](xcb_generic_event_t *event) {
+ if (!isCloseConnectionEvent(event)) {
+ tail->next = qXcbEventNodeFactory(event);
+ tail = tail->next;
+ m_tail.store(tail, std::memory_order_release);
+ }
+ };
+
+ while (!m_closeConnectionDetected && (event = xcb_wait_for_event(connection))) {
+ enqueueEvent(event);
+ while (!m_closeConnectionDetected && (event = xcb_poll_for_queued_event(connection)))
+ enqueueEvent(event);
+ wakeUpDispatcher();
+ }
+
+ if (!m_closeConnectionDetected) {
+ // Connection was terminated not by us. Wake up dispatcher, which will
+ // call processXcbEvents(), where we handle the connection errors via
+ // xcb_connection_has_error().
+ wakeUpDispatcher();
+ }
+}
+
+void QXcbEventQueue::wakeUpDispatcher()
+{
+ QMutexLocker locker(&qAppExiting);
+ if (!dispatcherOwnerDestructing) {
+ // This thread can run before a dispatcher has been created,
+ // so check if it is ready.
+ if (QCoreApplication::eventDispatcher())
+ QCoreApplication::eventDispatcher()->wakeUp();
+ }
+}
+
+qint32 QXcbEventQueue::generatePeekerId()
+{
+ const qint32 peekerId = m_peekerIdSource++;
+ m_peekerToNode.insert(peekerId, nullptr);
+ return peekerId;
+}
+
+bool QXcbEventQueue::removePeekerId(qint32 peekerId)
+{
+ const auto it = m_peekerToNode.find(peekerId);
+ if (it == m_peekerToNode.constEnd()) {
+ qCWarning(lcQpaXcb, "failed to remove unknown peeker id: %d", peekerId);
+ return false;
+ }
+ m_peekerToNode.erase(it);
+ if (m_peekerToNode.isEmpty()) {
+ m_peekerIdSource = 0; // Once the hash becomes empty, we can start reusing IDs
+ m_peekerIndexCacheDirty = false;
+ }
+ return true;
+}
+
+bool QXcbEventQueue::peekEventQueue(PeekerCallback peeker, void *peekerData,
+ PeekOptions option, qint32 peekerId)
+{
+ const bool peekerIdProvided = peekerId != -1;
+ auto peekerToNodeIt = m_peekerToNode.find(peekerId);
+
+ if (peekerIdProvided && peekerToNodeIt == m_peekerToNode.constEnd()) {
+ qCWarning(lcQpaXcb, "failed to find index for unknown peeker id: %d", peekerId);
+ return false;
+ }
+
+ const bool useCache = option.testFlag(PeekOption::PeekFromCachedIndex);
+ if (useCache && !peekerIdProvided) {
+ qCWarning(lcQpaXcb, "PeekOption::PeekFromCachedIndex requires peeker id");
+ return false;
+ }
+
+ if (peekerIdProvided && m_peekerIndexCacheDirty) {
+ for (auto &node : m_peekerToNode) // reset cache
+ node = nullptr;
+ m_peekerIndexCacheDirty = false;
+ }
+
+ flushBufferedEvents();
+ if (isEmpty())
+ return false;
+
+ const auto startNode = [this, useCache, peekerToNodeIt]() -> QXcbEventNode * {
+ if (useCache) {
+ const QXcbEventNode *cachedNode = peekerToNodeIt.value();
+ if (!cachedNode)
+ return m_head; // cache was reset
+ if (cachedNode == m_flushedTail)
+ return nullptr; // no new events since the last call
+ return cachedNode->next;
+ }
+ return m_head;
+ }();
+
+ if (!startNode)
+ return false;
+
+ // A peeker may call QCoreApplication::processEvents(), which will cause
+ // QXcbConnection::processXcbEvents() to modify the queue we are currently
+ // looping through;
+ m_queueModified = false;
+ bool result = false;
+
+ QXcbEventNode *node = startNode;
+ do {
+ xcb_generic_event_t *event = node->event;
+ if (event && peeker(event, peekerData)) {
+ result = true;
+ break;
+ }
+ if (node == m_flushedTail)
+ break;
+ node = node->next;
+ } while (!m_queueModified);
+
+ // Update the cached index if the queue was not modified, and hence the
+ // cache is still valid.
+ if (peekerIdProvided && node != startNode && !m_queueModified) {
+ // Before updating, make sure that a peeker callback did not remove
+ // the peeker id.
+ peekerToNodeIt = m_peekerToNode.find(peekerId);
+ if (peekerToNodeIt != m_peekerToNode.constEnd())
+ *peekerToNodeIt = node; // id still in the cache, update node
+ }
+
+ return result;
+}
+
+void QXcbEventQueue::sendCloseConnectionEvent() const
+{
+ // A hack to close XCB connection. Apparently XCB does not have any APIs for this?
+ xcb_client_message_event_t event;
+ memset(&event, 0, sizeof(event));
+
+ xcb_connection_t *c = m_connection->xcb_connection();
+ const xcb_window_t window = xcb_generate_id(c);
+ xcb_screen_iterator_t it = xcb_setup_roots_iterator(m_connection->setup());
+ xcb_screen_t *screen = it.data;
+ xcb_create_window(c, XCB_COPY_FROM_PARENT,
+ window, screen->root,
+ 0, 0, 1, 1, 0, XCB_WINDOW_CLASS_INPUT_ONLY,
+ screen->root_visual, 0, nullptr);
+
+ event.response_type = XCB_CLIENT_MESSAGE;
+ event.format = 32;
+ event.sequence = 0;
+ event.window = window;
+ event.type = m_connection->atom(QXcbAtom::_QT_CLOSE_CONNECTION);
+ event.data.data32[0] = 0;
+
+ xcb_send_event(c, false, window, XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<const char *>(&event));
+ xcb_destroy_window(c, window);
+ xcb_flush(c);
+}
+
+bool QXcbEventQueue::isCloseConnectionEvent(const xcb_generic_event_t *event)
+{
+ if (event && (event->response_type & ~0x80) == XCB_CLIENT_MESSAGE) {
+ auto clientMessage = reinterpret_cast<const xcb_client_message_event_t *>(event);
+ if (clientMessage->type == m_connection->atom(QXcbAtom::_QT_CLOSE_CONNECTION))
+ m_closeConnectionDetected = true;
+ }
+ return m_closeConnectionDetected;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.h b/src/plugins/platforms/xcb/qxcbeventqueue.h
new file mode 100644
index 0000000000..235f2824be
--- /dev/null
+++ b/src/plugins/platforms/xcb/qxcbeventqueue.h
@@ -0,0 +1,162 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QXCBEVENTQUEUE_H
+#define QXCBEVENTQUEUE_H
+
+#include <QtCore/QThread>
+#include <QtCore/QHash>
+#include <QtCore/QEventLoop>
+#include <QtCore/QVector>
+
+#include <xcb/xcb.h>
+
+#include <atomic>
+
+QT_BEGIN_NAMESPACE
+
+struct QXcbEventNode {
+ QXcbEventNode(xcb_generic_event_t *e = nullptr)
+ : event(e) { }
+
+ xcb_generic_event_t *event;
+ QXcbEventNode *next = nullptr;
+ bool fromHeap = false;
+};
+
+class QXcbConnection;
+class QAbstractEventDispatcher;
+
+class QXcbEventQueue : public QThread
+{
+ Q_OBJECT
+public:
+ QXcbEventQueue(QXcbConnection *connection);
+ ~QXcbEventQueue();
+
+ enum { PoolSize = 100 }; // 2.4 kB with 100 nodes
+
+ enum PeekOption {
+ PeekDefault = 0, // see qx11info_x11.h for docs
+ PeekFromCachedIndex = 1,
+ PeekRetainMatch = 2,
+ PeekRemoveMatch = 3,
+ PeekRemoveMatchContinue = 4
+ };
+ Q_DECLARE_FLAGS(PeekOptions, PeekOption)
+
+ void run() override;
+
+ bool isEmpty() const { return m_head == m_flushedTail && !m_head->event; }
+ xcb_generic_event_t *takeFirst(QEventLoop::ProcessEventsFlags flags);
+ xcb_generic_event_t *takeFirst();
+ void flushBufferedEvents();
+ void wakeUpDispatcher();
+
+ // ### peek() and peekEventQueue() could be unified. Note that peekEventQueue()
+ // is public API exposed via QX11Extras/QX11Info.
+ template<typename Peeker>
+ xcb_generic_event_t *peek(Peeker &&peeker) {
+ return peek(PeekRemoveMatch, std::forward<Peeker>(peeker));
+ }
+ template<typename Peeker>
+ inline xcb_generic_event_t *peek(PeekOption config, Peeker &&peeker);
+
+ qint32 generatePeekerId();
+ bool removePeekerId(qint32 peekerId);
+
+ using PeekerCallback = bool (*)(xcb_generic_event_t *event, void *peekerData);
+ bool peekEventQueue(PeekerCallback peeker, void *peekerData = nullptr,
+ PeekOptions option = PeekDefault, qint32 peekerId = -1);
+
+private:
+ QXcbEventNode *qXcbEventNodeFactory(xcb_generic_event_t *event);
+ void dequeueNode();
+
+ void sendCloseConnectionEvent() const;
+ bool isCloseConnectionEvent(const xcb_generic_event_t *event);
+
+ QXcbEventNode *m_head = nullptr;
+ QXcbEventNode *m_flushedTail = nullptr;
+ std::atomic<QXcbEventNode *> m_tail { nullptr };
+ std::atomic_uint m_nodesRestored { 0 };
+
+ QXcbConnection *m_connection = nullptr;
+ bool m_closeConnectionDetected = false;
+
+ uint m_freeNodes = PoolSize;
+ uint m_poolIndex = 0;
+
+ qint32 m_peekerIdSource = 0;
+ bool m_queueModified = false;
+ bool m_peekerIndexCacheDirty = false;
+ QHash<qint32, QXcbEventNode *> m_peekerToNode;
+
+ QVector<xcb_generic_event_t *> m_inputEvents;
+
+ // debug stats
+ quint64 m_nodesOnHeap = 0;
+};
+
+template<typename Peeker>
+xcb_generic_event_t *QXcbEventQueue::peek(PeekOption option, Peeker &&peeker)
+{
+ flushBufferedEvents();
+ if (isEmpty())
+ return nullptr;
+
+ QXcbEventNode *node = m_head;
+ do {
+ xcb_generic_event_t *event = node->event;
+ if (event && peeker(event, event->response_type & ~0x80)) {
+ if (option == PeekRemoveMatch || option == PeekRemoveMatchContinue)
+ node->event = nullptr;
+ if (option != PeekRemoveMatchContinue)
+ return event;
+ }
+ if (node == m_flushedTail)
+ break;
+ node = node->next;
+ } while (true);
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp
index db8dc09025..ed9e87a036 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.cpp
+++ b/src/plugins/platforms/xcb/qxcbintegration.cpp
@@ -46,6 +46,8 @@
#include "qxcbbackingstore.h"
#include "qxcbnativeinterface.h"
#include "qxcbclipboard.h"
+#include "qxcbeventqueue.h"
+#include "qxcbeventdispatcher.h"
#if QT_CONFIG(draganddrop)
#include "qxcbdrag.h"
#endif
@@ -57,7 +59,6 @@
#include <xcb/xcb.h>
-#include <QtEventDispatcherSupport/private/qgenericunixeventdispatcher_p.h>
#include <QtFontDatabaseSupport/private/qgenericunixfontdatabase_p.h>
#include <QtServiceSupport/private/qgenericunixservices_p.h>
@@ -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()
diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h
index a2de22d53d..f13e232291 100644
--- a/src/plugins/platforms/xcb/qxcbintegration.h
+++ b/src/plugins/platforms/xcb/qxcbintegration.h
@@ -53,7 +53,6 @@ QT_BEGIN_NAMESPACE
class QXcbConnection;
class QAbstractEventDispatcher;
class QXcbNativeInterface;
-class QXcbScreen;
class Q_XCB_EXPORT QXcbIntegration : public QPlatformIntegration
{
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
index 20c169fc53..c5dc7b21ad 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp
@@ -1190,9 +1190,10 @@ QXcbKeyboard::QXcbKeyboard(QXcbConnection *connection)
#if QT_CONFIG(xkb)
core_device_id = 0;
if (connection->hasXKB()) {
+ selectEvents();
core_device_id = xkb_x11_get_core_keyboard_device_id(xcb_connection());
if (core_device_id == -1) {
- qWarning("Qt: couldn't get core keyboard device info");
+ qCWarning(lcQpaXcb, "failed to get core keyboard device info");
return;
}
} else {
@@ -1210,6 +1211,42 @@ QXcbKeyboard::~QXcbKeyboard()
xcb_key_symbols_free(m_key_symbols);
}
+void QXcbKeyboard::selectEvents()
+{
+#if QT_CONFIG(xkb)
+ const uint16_t required_map_parts = (XCB_XKB_MAP_PART_KEY_TYPES |
+ XCB_XKB_MAP_PART_KEY_SYMS |
+ XCB_XKB_MAP_PART_MODIFIER_MAP |
+ XCB_XKB_MAP_PART_EXPLICIT_COMPONENTS |
+ XCB_XKB_MAP_PART_KEY_ACTIONS |
+ XCB_XKB_MAP_PART_KEY_BEHAVIORS |
+ XCB_XKB_MAP_PART_VIRTUAL_MODS |
+ XCB_XKB_MAP_PART_VIRTUAL_MOD_MAP);
+
+ const uint16_t required_events = (XCB_XKB_EVENT_TYPE_NEW_KEYBOARD_NOTIFY |
+ XCB_XKB_EVENT_TYPE_MAP_NOTIFY |
+ XCB_XKB_EVENT_TYPE_STATE_NOTIFY);
+
+ // XKB events are reported to all interested clients without regard
+ // to the current keyboard input focus or grab state
+ xcb_void_cookie_t select = xcb_xkb_select_events_checked(
+ xcb_connection(),
+ XCB_XKB_ID_USE_CORE_KBD,
+ required_events,
+ 0,
+ required_events,
+ required_map_parts,
+ required_map_parts,
+ 0);
+
+ xcb_generic_error_t *error = xcb_request_check(xcb_connection(), select);
+ if (error) {
+ free(error);
+ qCWarning(lcQpaXcb, "failed to select notify events from XKB");
+ }
+#endif
+}
+
void QXcbKeyboard::updateVModMapping()
{
#if QT_CONFIG(xkb)
@@ -1541,7 +1578,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 +1587,7 @@ void QXcbKeyboard::handleKeyEvent(xcb_window_t sourceWindow, QEvent::Type type,
m_autoRepeatCode = code;
}
return true;
- }, false /* removeFromQueue */);
+ });
}
bool filtered = false;
diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h
index 95915fb2e6..f8490592b7 100644
--- a/src/plugins/platforms/xcb/qxcbkeyboard.h
+++ b/src/plugins/platforms/xcb/qxcbkeyboard.h
@@ -43,6 +43,11 @@
#include "qxcbobject.h"
#include <xcb/xcb_keysyms.h>
+#if QT_CONFIG(xkb)
+#define explicit dont_use_cxx_explicit
+#include <xcb/xkb.h>
+#undef explicit
+#endif
#include <xkbcommon/xkbcommon.h>
#if QT_CONFIG(xkb)
@@ -62,6 +67,8 @@ public:
~QXcbKeyboard();
+ void selectEvents();
+
void handleKeyPressEvent(const xcb_key_press_event_t *event);
void handleKeyReleaseEvent(const xcb_key_release_event_t *event);
diff --git a/src/plugins/platforms/xcb/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/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
index 98bedea48a..524af5a2a7 100644
--- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
+++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp
@@ -437,20 +437,20 @@ void QXcbNativeInterface::setAppUserTime(QScreen* screen, xcb_timestamp_t time)
qint32 QXcbNativeInterface::generatePeekerId()
{
QXcbIntegration *integration = QXcbIntegration::instance();
- return integration->defaultConnection()->generatePeekerId();
+ return integration->defaultConnection()->eventQueue()->generatePeekerId();
}
bool QXcbNativeInterface::removePeekerId(qint32 peekerId)
{
QXcbIntegration *integration = QXcbIntegration::instance();
- return integration->defaultConnection()->removePeekerId(peekerId);
+ return integration->defaultConnection()->eventQueue()->removePeekerId(peekerId);
}
-bool QXcbNativeInterface::peekEventQueue(QXcbConnection::PeekerCallback peeker, void *peekerData,
- QXcbConnection::PeekOptions option, qint32 peekerId)
+bool QXcbNativeInterface::peekEventQueue(QXcbEventQueue::PeekerCallback peeker, void *peekerData,
+ QXcbEventQueue::PeekOptions option, qint32 peekerId)
{
QXcbIntegration *integration = QXcbIntegration::instance();
- return integration->defaultConnection()->peekEventQueue(peeker, peekerData, option, peekerId);
+ return integration->defaultConnection()->eventQueue()->peekEventQueue(peeker, peekerData, option, peekerId);
}
void QXcbNativeInterface::setStartupId(const char *data)
diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h
index 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/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp
index 8c0ce8dd7e..35e90e4206 100644
--- a/src/plugins/platforms/xcb/qxcbscreen.cpp
+++ b/src/plugins/platforms/xcb/qxcbscreen.cpp
@@ -747,7 +747,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/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index 69fc6c2951..e56f6b13d8 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -118,8 +118,6 @@ enum {
defaultWindowHeight = 160
};
-//#ifdef NET_WM_STATE_DEBUG
-
QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(xcb_rectangle_t, Q_PRIMITIVE_TYPE);
@@ -499,12 +497,10 @@ void QXcbWindow::create()
clientMachine.size(), clientMachine.constData());
}
+ // Create WM_HINTS property on the window, so we can xcb_get_wm_hints*()
+ // from various setter functions for adjusting the hints.
xcb_wm_hints_t hints;
memset(&hints, 0, sizeof(hints));
- xcb_wm_hints_set_normal(&hints);
-
- xcb_wm_hints_set_input(&hints, !(window()->flags() & Qt::WindowDoesNotAcceptFocus));
-
xcb_set_wm_hints(xcb_connection(), m_window, &hints);
xcb_window_t leader = connection()->clientLeader();
@@ -532,9 +528,6 @@ void QXcbWindow::create()
setWindowFlags(window()->flags());
setWindowTitle(window()->title());
- if (window()->flags() & Qt::WindowTransparentForInput)
- setTransparentForMouseEvents(true);
-
#if QT_CONFIG(xcb_xlib)
// force sync to read outstanding requests - see QTBUG-29106
XSync(static_cast<Display*>(platformScreen->connection()->xlib_display()), false);
@@ -739,20 +732,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,9 +754,6 @@ 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();
}
@@ -928,8 +904,6 @@ void QXcbWindow::doFocusOut()
struct QtMotifWmHints {
quint32 flags, functions, decorations;
- qint32 input_mode;
- quint32 status;
};
enum {
@@ -951,50 +925,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,9 +955,7 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates()
if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)))
result |= NetWmStateDemandsAttention;
} else {
-#ifdef NET_WM_STATE_DEBUG
- printf("getting net wm state (%x), empty\n", m_window);
-#endif
+ qCDebug(lcQpaXcb, "getting net wm state (%x), empty\n", m_window);
}
return result;
@@ -1098,22 +1028,18 @@ void QXcbWindow::setWindowFlags(Qt::WindowFlags flags)
setWmWindowType(wmWindowTypes, flags);
setNetWmStateWindowFlags(flags);
- setMotifWindowFlags(flags);
+ setMotifWmHints(flags);
setTransparentForMouseEvents(flags & Qt::WindowTransparentForInput);
updateDoesNotAcceptFocus(flags & Qt::WindowDoesNotAcceptFocus);
}
-void QXcbWindow::setMotifWindowFlags(Qt::WindowFlags flags)
+void QXcbWindow::setMotifWmHints(Qt::WindowFlags flags)
{
Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
QtMotifWmHints mwmhints;
- mwmhints.flags = 0L;
- mwmhints.functions = 0L;
- mwmhints.decorations = 0;
- mwmhints.input_mode = 0L;
- mwmhints.status = 0L;
+ memset(&mwmhints, 0, sizeof(mwmhints));
if (type != Qt::SplashScreen) {
mwmhints.flags |= MWM_HINTS_DECORATIONS;
@@ -1171,7 +1097,18 @@ void QXcbWindow::setMotifWindowFlags(Qt::WindowFlags flags)
mwmhints.decorations = 0;
}
- setMotifWmHints(connection(), m_window, mwmhints);
+ if (mwmhints.flags) {
+ xcb_change_property(xcb_connection(),
+ XCB_PROP_MODE_REPLACE,
+ m_window,
+ atom(QXcbAtom::_MOTIF_WM_HINTS),
+ atom(QXcbAtom::_MOTIF_WM_HINTS),
+ 32,
+ 5,
+ &mwmhints);
+ } else {
+ xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_MOTIF_WM_HINTS));
+ }
}
void QXcbWindow::changeNetWmState(bool set, xcb_atom_t one, xcb_atom_t two)
@@ -1230,64 +1167,18 @@ void QXcbWindow::setWindowState(Qt::WindowStates state)
changeNetWmState(state & Qt::WindowFullScreen, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN));
}
- connection()->sync();
- m_windowState = state;
-}
-
-void QXcbWindow::updateMotifWmHintsBeforeMap()
-{
- QtMotifWmHints mwmhints = getMotifWmHints(connection(), m_window);
-
- if (window()->modality() != Qt::NonModal) {
- switch (window()->modality()) {
- case Qt::WindowModal:
- mwmhints.input_mode = MWM_INPUT_PRIMARY_APPLICATION_MODAL;
- break;
- case Qt::ApplicationModal:
- default:
- mwmhints.input_mode = MWM_INPUT_FULL_APPLICATION_MODAL;
- break;
- }
- mwmhints.flags |= MWM_HINTS_INPUT_MODE;
- } else {
- mwmhints.input_mode = MWM_INPUT_MODELESS;
- mwmhints.flags &= ~MWM_HINTS_INPUT_MODE;
- }
-
- if (windowMinimumSize() == windowMaximumSize()) {
- // fixed size, remove the resize handle (since mwm/dtwm
- // isn't smart enough to do it itself)
- mwmhints.flags |= MWM_HINTS_FUNCTIONS;
- if (mwmhints.functions == MWM_FUNC_ALL) {
- mwmhints.functions = MWM_FUNC_MOVE;
- } else {
- mwmhints.functions &= ~MWM_FUNC_RESIZE;
- }
-
- if (mwmhints.decorations == MWM_DECOR_ALL) {
- mwmhints.flags |= MWM_HINTS_DECORATIONS;
- mwmhints.decorations = (MWM_DECOR_BORDER
- | MWM_DECOR_TITLE
- | MWM_DECOR_MENU);
- } else {
- mwmhints.decorations &= ~MWM_DECOR_RESIZEH;
- }
- }
-
- if (window()->flags() & Qt::WindowMinimizeButtonHint) {
- mwmhints.flags |= MWM_HINTS_DECORATIONS;
- mwmhints.decorations |= MWM_DECOR_MINIMIZE;
- mwmhints.functions |= MWM_FUNC_MINIMIZE;
- }
- if (window()->flags() & Qt::WindowMaximizeButtonHint) {
- mwmhints.flags |= MWM_HINTS_DECORATIONS;
- mwmhints.decorations |= MWM_DECOR_MAXIMIZE;
- mwmhints.functions |= MWM_FUNC_MAXIMIZE;
+ xcb_get_property_cookie_t cookie = xcb_get_wm_hints_unchecked(xcb_connection(), m_window);
+ xcb_wm_hints_t hints;
+ if (xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, nullptr)) {
+ if (state & Qt::WindowMinimized)
+ xcb_wm_hints_set_iconic(&hints);
+ else
+ xcb_wm_hints_set_normal(&hints);
+ xcb_set_wm_hints(xcb_connection(), m_window, &hints);
}
- if (window()->flags() & Qt::WindowCloseButtonHint)
- mwmhints.functions |= MWM_FUNC_CLOSE;
- setMotifWmHints(connection(), m_window, mwmhints);
+ connection()->sync();
+ m_windowState = state;
}
void QXcbWindow::updateNetWmStateBeforeMap()
@@ -1402,9 +1293,8 @@ void QXcbWindow::updateDoesNotAcceptFocus(bool doesNotAcceptFocus)
xcb_get_property_cookie_t cookie = xcb_get_wm_hints_unchecked(xcb_connection(), m_window);
xcb_wm_hints_t hints;
- if (!xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, NULL)) {
+ if (!xcb_get_wm_hints_reply(xcb_connection(), cookie, &hints, nullptr))
return;
- }
xcb_wm_hints_set_input(&hints, !doesNotAcceptFocus);
xcb_set_wm_hints(xcb_connection(), m_window, &hints);
@@ -1818,21 +1708,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 +2041,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;
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index f7d76ed3b2..6667f45343 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -199,10 +199,9 @@ protected:
NetWmStates netWmStates();
void setNetWmStates(NetWmStates);
- void setMotifWindowFlags(Qt::WindowFlags flags);
- void setNetWmStateWindowFlags(Qt::WindowFlags flags);
+ void setMotifWmHints(Qt::WindowFlags flags);
- void updateMotifWmHintsBeforeMap();
+ void setNetWmStateWindowFlags(Qt::WindowFlags flags);
void updateNetWmStateBeforeMap();
void setTransparentForMouseEvents(bool transparent);
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..c65b145fb6 100644
--- a/src/plugins/platforms/xcb/xcb_qpa_lib.pro
+++ b/src/plugins/platforms/xcb/xcb_qpa_lib.pro
@@ -5,7 +5,7 @@ DEFINES += QT_NO_FOREACH
QT += \
core-private gui-private \
service_support-private theme_support-private \
- eventdispatcher_support-private fontdatabase_support-private \
+ fontdatabase_support-private \
edid_support-private
qtHaveModule(linuxaccessibility_support-private): \
@@ -13,6 +13,8 @@ qtHaveModule(linuxaccessibility_support-private): \
qtConfig(vulkan): QT += vulkan_support-private
+qtConfig(glib) : QMAKE_USE_PRIVATE += glib
+
SOURCES = \
qxcbclipboard.cpp \
qxcbconnection.cpp \
@@ -27,7 +29,12 @@ SOURCES = \
qxcbcursor.cpp \
qxcbimage.cpp \
qxcbxsettings.cpp \
- qxcbsystemtraytracker.cpp
+ qxcbsystemtraytracker.cpp \
+ qxcbeventqueue.cpp \
+ qxcbeventdispatcher.cpp \
+ qxcbconnection_basic.cpp \
+ qxcbconnection_screens.cpp \
+ qxcbatom.cpp
HEADERS = \
qxcbclipboard.h \
@@ -45,7 +52,11 @@ HEADERS = \
qxcbimage.h \
qxcbxsettings.h \
qxcbsystemtraytracker.h \
- qxcbxkbcommon.h
+ qxcbxkbcommon.h \
+ qxcbeventqueue.h \
+ qxcbeventdispatcher.h \
+ qxcbconnection_basic.h \
+ qxcbatom.h
qtConfig(draganddrop) {
SOURCES += qxcbdrag.cpp
diff --git a/src/plugins/platformthemes/platformthemes.pro b/src/plugins/platformthemes/platformthemes.pro
index 06ffc4cc9f..3bcc659199 100644
--- a/src/plugins/platformthemes/platformthemes.pro
+++ b/src/plugins/platformthemes/platformthemes.pro
@@ -1,6 +1,6 @@
TEMPLATE = subdirs
QT_FOR_CONFIG += widgets-private
-qtConfig(dbus):qtConfig(regularexpression): SUBDIRS += xdgdesktopportal
+qtConfig(dbus):qtConfig(regularexpression):qtConfig(mimetype): SUBDIRS += xdgdesktopportal
qtHaveModule(widgets):qtConfig(gtk3): SUBDIRS += gtk3
diff --git a/src/plugins/printsupport/cups/qppdprintdevice.cpp b/src/plugins/printsupport/cups/qppdprintdevice.cpp
index d2ddc4144f..51b93a0016 100644
--- a/src/plugins/printsupport/cups/qppdprintdevice.cpp
+++ b/src/plugins/printsupport/cups/qppdprintdevice.cpp
@@ -473,7 +473,7 @@ bool QPpdPrintDevice::isFeatureAvailable(QPrintDevice::PrintDevicePropertyKey ke
return QPlatformPrintDevice::isFeatureAvailable(key, params);
}
-#ifndef QT_NO_MIMETYPE
+#if QT_CONFIG(mimetype)
void QPpdPrintDevice::loadMimeTypes() const
{
// TODO No CUPS api? Need to manually load CUPS mime.types file?
diff --git a/src/plugins/printsupport/cups/qppdprintdevice.h b/src/plugins/printsupport/cups/qppdprintdevice.h
index 90f90d6788..3baf8b771b 100644
--- a/src/plugins/printsupport/cups/qppdprintdevice.h
+++ b/src/plugins/printsupport/cups/qppdprintdevice.h
@@ -99,7 +99,7 @@ protected:
void loadOutputBins() const override;
void loadDuplexModes() const override;
void loadColorModes() const override;
-#ifndef QT_NO_MIMETYPE
+#if QT_CONFIG(mimetype)
void loadMimeTypes() const override;
#endif
diff --git a/src/plugins/styles/mac/qmacstyle_mac.mm b/src/plugins/styles/mac/qmacstyle_mac.mm
index 2a21673054..94d048ca7e 100644
--- a/src/plugins/styles/mac/qmacstyle_mac.mm
+++ b/src/plugins/styles/mac/qmacstyle_mac.mm
@@ -238,6 +238,33 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QVerticalSplitView);
}
@end
+// See render code in drawPrimitive(PE_FrameTabWidget)
+@interface QT_MANGLE_NAMESPACE(QDarkNSBox) : NSBox
+@end
+
+QT_NAMESPACE_ALIAS_OBJC_CLASS(QDarkNSBox);
+
+@implementation QDarkNSBox
+- (instancetype)init
+{
+ if ((self = [super init])) {
+ self.title = @"";
+ self.titlePosition = NSNoTitle;
+ self.boxType = NSBoxCustom;
+ self.cornerRadius = 3;
+ self.borderColor = [NSColor.controlColor colorWithAlphaComponent:0.1];
+ self.fillColor = [NSColor.darkGrayColor colorWithAlphaComponent:0.2];
+ }
+
+ return self;
+}
+
+- (void)drawRect:(NSRect)rect
+{
+ [super drawRect:rect];
+}
+@end
+
QT_BEGIN_NAMESPACE
// The following constants are used for adjusting the size
@@ -1468,8 +1495,8 @@ QRectF QMacStylePrivate::CocoaControl::adjustedControlFrame(const QRectF &rect)
QRectF frameRect;
const auto frameSize = defaultFrameSize();
if (type == QMacStylePrivate::Button_SquareButton) {
- frameRect = rect.adjusted(3, 1, -3, -5)
- .adjusted(focusRingWidth, focusRingWidth, -focusRingWidth, -focusRingWidth);
+ frameRect = rect.adjusted(3, 1, -3, -1)
+ .adjusted(focusRingWidth, focusRingWidth, -focusRingWidth, -focusRingWidth);
} else if (type == QMacStylePrivate::Button_PushButton) {
// Start from the style option's top-left corner.
frameRect = QRectF(rect.topLeft(),
@@ -1704,18 +1731,28 @@ NSView *QMacStylePrivate::cocoaControl(CocoaControl widget) const
|| widget.size == QStyleHelper::SizeDefault)
return nil;
+ if (widget.type == Box) {
+ if (__builtin_available(macOS 10.14, *)) {
+ if (qt_mac_applicationIsInDarkMode()) {
+ // See render code in drawPrimitive(PE_FrameTabWidget)
+ widget.type = Box_Dark;
+ }
+ }
+ }
+
NSView *bv = cocoaControls.value(widget, nil);
if (!bv) {
switch (widget.type) {
case Box: {
- NSBox *bc = [[NSBox alloc] init];
- bc.title = @"";
- bc.titlePosition = NSNoTitle;
- bc.boxType = NSBoxPrimary;
- bc.borderType = NSBezelBorder;
- bv = bc;
+ NSBox *box = [[NSBox alloc] init];
+ bv = box;
+ box.title = @"";
+ box.titlePosition = NSNoTitle;
break;
}
+ case Box_Dark:
+ bv = [[QDarkNSBox alloc] init];
+ break;
case Button_CheckBox:
case Button_Disclosure:
case Button_PushButton:
@@ -2922,10 +2959,28 @@ void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPai
{
const auto cw = QMacStylePrivate::CocoaControl(QMacStylePrivate::Box, QStyleHelper::SizeLarge);
auto *box = static_cast<NSBox *>(d->cocoaControl(cw));
+ // FIXME Since macOS 10.14, simply calling drawRect: won't display anything anymore.
+ // The AppKit team is aware of this and has proposed a couple of solutions.
+ // The first solution was to call displayRectIgnoringOpacity:inContext: instead.
+ // However, it doesn't seem to work on 10.13. More importantly, dark mode on 10.14
+ // is extremely slow. Light mode works fine.
+ // The second solution is to subclass NSBox and reimplement a trivial drawRect: which
+ // would only call super. This works without any issue on 10.13, but a double border
+ // shows on 10.14 in both light and dark modes.
+ // The code below picks what works on each version and mode. On 10.13 and earlier, we
+ // simply call drawRect: on a regular NSBox. On 10.14, we call displayRectIgnoringOpacity:
+ // inContext:, but only in light mode. In dark mode, we use a custom NSBox subclass,
+ // QDarkNSBox, of type NSBoxCustom. Its appearance is close enough to the real thing so
+ // we can use this for now.
d->drawNSViewInRect(box, opt->rect, p, ^(CGContextRef ctx, const CGRect &rect) {
CGContextTranslateCTM(ctx, 0, rect.origin.y + rect.size.height);
CGContextScaleCTM(ctx, 1, -1);
- [box drawRect:rect];
+ if (QOperatingSystemVersion::current() < QOperatingSystemVersion::MacOSMojave
+ || [box isMemberOfClass:QDarkNSBox.class]) {
+ [box drawRect:rect];
+ } else {
+ [box displayRectIgnoringOpacity:box.bounds inContext:NSGraphicsContext.currentContext];
+ }
});
break;
}
diff --git a/src/plugins/styles/mac/qmacstyle_mac_p_p.h b/src/plugins/styles/mac/qmacstyle_mac_p_p.h
index 8c712e838a..dd99cf4bb5 100644
--- a/src/plugins/styles/mac/qmacstyle_mac_p_p.h
+++ b/src/plugins/styles/mac/qmacstyle_mac_p_p.h
@@ -187,6 +187,7 @@ public:
enum CocoaControlType {
NoControl, // For when there's no such a control in Cocoa
Box, // QGroupBox
+ Box_Dark, // FIXME See render code in drawPrimitive(PE_FrameTabWidget)
Button_CheckBox,
Button_Disclosure, // Disclosure triangle, like in QTreeView
Button_PopupButton, // Non-editable QComboBox