diff options
Diffstat (limited to 'src/plugins/platforms/xcb')
48 files changed, 1450 insertions, 950 deletions
diff --git a/src/plugins/platforms/xcb/CMakeLists.txt b/src/plugins/platforms/xcb/CMakeLists.txt index c73b2de73f..e8fb442dd4 100644 --- a/src/plugins/platforms/xcb/CMakeLists.txt +++ b/src/plugins/platforms/xcb/CMakeLists.txt @@ -1,9 +1,15 @@ -# Generated from xcb.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## XcbQpaPrivate Module: ##################################################################### +if(GCC) + # Work around GCC ABI issues + add_compile_options(-Wno-psabi) +endif() + qt_internal_add_module(XcbQpaPrivate CONFIG_MODULE_NAME xcb_qpa_lib INTERNAL_MODULE @@ -44,6 +50,7 @@ qt_internal_add_module(XcbQpaPrivate PkgConfig::XKB_COMMON_X11 Qt::CorePrivate Qt::GuiPrivate + XCB::CURSOR XCB::ICCCM XCB::IMAGE XCB::KEYSYMS @@ -55,23 +62,16 @@ qt_internal_add_module(XcbQpaPrivate XCB::SYNC XCB::XCB XCB::XFIXES - # XCB::XINPUT # special case remove handled below XCB::XKB XKB::XKB + NO_UNITY_BUILD # X11 define clashes ) -# special case begin qt_disable_apple_app_extension_api_only(XcbQpaPrivate) -# special case end ## Scopes: ##################################################################### -qt_internal_extend_target(XcbQpaPrivate CONDITION QT_FEATURE_opengl - PUBLIC_LIBRARIES - Qt::OpenGLPrivate -) - qt_internal_extend_target(XcbQpaPrivate CONDITION QT_FEATURE_glib LIBRARIES GLIB2::GLIB2 @@ -83,21 +83,24 @@ qt_internal_extend_target(XcbQpaPrivate CONDITION QT_FEATURE_draganddrop ) qt_internal_extend_target(XcbQpaPrivate CONDITION QT_FEATURE_xcb_xlib + SOURCES + qt_xlib_wrapper.c qt_xlib_wrapper.h PUBLIC_LIBRARIES X11::XCB - # special case begin - # 'QMAKE_USE += xcb_xlib' in qmake implies also += xlib (aka X11) - # due to "use": "xcb xlib" in src/gui/configure.json. - # That's not yet handled by the conversion scripts unfortunately. X11::X11 - # special case end +) + +qt_internal_extend_target(XcbQpaPrivate CONDITION NOT QT_FEATURE_xcb_xlib + SOURCES + qxcbcursorfont.h ) qt_internal_extend_target(XcbQpaPrivate CONDITION QT_FEATURE_xcb_sm SOURCES qxcbsessionmanager.cpp qxcbsessionmanager.h PUBLIC_LIBRARIES - ${X11_SM_LIB} ${X11_ICE_LIB} + X11::SM + X11::ICE ) qt_internal_extend_target(XcbQpaPrivate CONDITION QT_FEATURE_vulkan @@ -140,7 +143,6 @@ qt_internal_extend_target(XcbQpaPrivate CONDITION QT_FEATURE_fontconfig AND QT_F WrapFreetype::WrapFreetype ) -# special case begin if(QT_FEATURE_system_xcb_xinput) qt_internal_extend_target(XcbQpaPrivate LIBRARIES XCB::XINPUT) else() @@ -154,7 +156,6 @@ else() "${PROJECT_SOURCE_DIR}/src/3rdparty/xcb/include" ) endif() -# special case end ##################################################################### ## QXcbIntegrationPlugin Plugin: @@ -163,7 +164,7 @@ endif() qt_internal_add_plugin(QXcbIntegrationPlugin OUTPUT_NAME qxcb PLUGIN_TYPE platforms - DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES xcb # special case + DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES xcb SOURCES qxcbmain.cpp DEFINES @@ -174,16 +175,7 @@ qt_internal_add_plugin(QXcbIntegrationPlugin Qt::XcbQpaPrivate ) -#### Keys ignored in scope 18:.:.:xcb-plugin.pro:<TRUE>: -# OTHER_FILES = "xcb.json" "README" - -## Scopes: -##################################################################### - -#### Keys ignored in scope 20:.:.:xcb-plugin.pro:NOT TARGET___equals____ss_QT_DEFAULT_QPA_PLUGIN: -# PLUGIN_EXTENDS = "-" add_subdirectory(gl_integrations) if(OFF) - add_subdirectory(xcb-static) # special case TODO: xcb-static sub folder + add_subdirectory(xcb-static) endif() - diff --git a/src/plugins/platforms/xcb/gl_integrations/CMakeLists.txt b/src/plugins/platforms/xcb/gl_integrations/CMakeLists.txt index 210a924550..957beb9ed4 100644 --- a/src/plugins/platforms/xcb/gl_integrations/CMakeLists.txt +++ b/src/plugins/platforms/xcb/gl_integrations/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from gl_integrations.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause if(QT_FEATURE_xcb_egl_plugin) add_subdirectory(xcb_egl) diff --git a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h index 6e841370d8..c6492f02ae 100644 --- a/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h +++ b/src/plugins/platforms/xcb/gl_integrations/qxcbglintegration.h @@ -14,7 +14,7 @@ class QPlatformOffscreenSurface; class QOffscreenSurface; class QXcbNativeInterfaceHandler; -Q_XCB_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcQpaGl) +Q_DECLARE_EXPORTED_LOGGING_CATEGORY(lcQpaGl, Q_XCB_EXPORT) class Q_XCB_EXPORT QXcbGlIntegration { diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/CMakeLists.txt b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/CMakeLists.txt index 4a62c8ad46..12938c159a 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/CMakeLists.txt +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/CMakeLists.txt @@ -1,10 +1,11 @@ -# Generated from xcb_egl.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## QXcbEglIntegrationPlugin Plugin: ##################################################################### -qt_find_package(EGL) # special case +qt_find_package(EGL) qt_internal_add_plugin(QXcbEglIntegrationPlugin OUTPUT_NAME qxcb-egl-integration @@ -26,5 +27,6 @@ qt_internal_add_plugin(QXcbEglIntegrationPlugin Qt::Gui Qt::GuiPrivate Qt::XcbQpaPrivate - EGL::EGL # special case + EGL::EGL + NO_UNITY_BUILD # X11 define clashes ) diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglinclude.h b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglinclude.h index 17428f778f..d12501bd90 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglinclude.h +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglinclude.h @@ -7,7 +7,6 @@ #include <QtGui/QPalette> #include <QtCore/QTextStream> #include <QtGui/private/qmath_p.h> -#include <QtGui/private/qcssparser_p.h> #include <QtGui/private/qtextengine_p.h> #include <QtGui/private/qt_egl_p.h> diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp index 2ab89f07d6..133b992cd9 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp @@ -6,15 +6,59 @@ #include "qxcbeglcontext.h" #include <QtGui/QOffscreenSurface> +#include <QtGui/private/qeglconvenience_p.h> #include <QtGui/private/qeglstreamconvenience_p.h> +#include <optional> #include "qxcbeglnativeinterfacehandler.h" QT_BEGIN_NAMESPACE +namespace { + +struct VisualInfo +{ + xcb_visualtype_t visualType; + uint8_t depth; +}; + +std::optional<VisualInfo> getVisualInfo(xcb_screen_t *screen, + std::optional<xcb_visualid_t> requestedVisualId, + std::optional<uint8_t> requestedDepth = std::nullopt) +{ + xcb_depth_iterator_t depthIterator = xcb_screen_allowed_depths_iterator(screen); + + while (depthIterator.rem) { + xcb_depth_t *depth = depthIterator.data; + xcb_visualtype_iterator_t visualTypeIterator = xcb_depth_visuals_iterator(depth); + + while (visualTypeIterator.rem) { + xcb_visualtype_t *visualType = visualTypeIterator.data; + if (requestedVisualId && visualType->visual_id != *requestedVisualId) { + xcb_visualtype_next(&visualTypeIterator); + continue; + } + + if (requestedDepth && depth->depth != *requestedDepth) { + xcb_visualtype_next(&visualTypeIterator); + continue; + } + + return VisualInfo{ *visualType, depth->depth }; + } + + xcb_depth_next(&depthIterator); + } + + return std::nullopt; +} + +} // namespace + QXcbEglIntegration::QXcbEglIntegration() : m_connection(nullptr) , m_egl_display(EGL_NO_DISPLAY) + , m_using_platform_display(false) { qCDebug(lcQpaGl) << "Xcb EGL gl-integration created"; } @@ -31,23 +75,38 @@ bool QXcbEglIntegration::initialize(QXcbConnection *connection) const char *extensions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); +#if QT_CONFIG(xcb_xlib) if (extensions && strstr(extensions, "EGL_EXT_platform_x11")) { QEGLStreamConvenience streamFuncs; m_egl_display = streamFuncs.get_platform_display(EGL_PLATFORM_X11_KHR, - xlib_display(), + m_connection->xlib_display(), nullptr); + m_using_platform_display = true; } +#if QT_CONFIG(egl_x11) if (!m_egl_display) - m_egl_display = eglGetDisplay(reinterpret_cast<EGLNativeDisplayType>(xlib_display())); + m_egl_display = eglGetDisplay(reinterpret_cast<EGLNativeDisplayType>(m_connection->xlib_display())); +#endif +#else + if (extensions && (strstr(extensions, "EGL_EXT_platform_xcb") || strstr(extensions, "EGL_MESA_platform_xcb"))) { + QEGLStreamConvenience streamFuncs; + m_egl_display = streamFuncs.get_platform_display(EGL_PLATFORM_XCB_KHR, + reinterpret_cast<void *>(connection->xcb_connection()), + nullptr); + m_using_platform_display = true; + } +#endif EGLint major, minor; bool success = eglInitialize(m_egl_display, &major, &minor); +#if QT_CONFIG(egl_x11) if (!success) { m_egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); qCDebug(lcQpaGl) << "Xcb EGL gl-integration retrying with display" << m_egl_display; success = eglInitialize(m_egl_display, &major, &minor); } +#endif m_native_interface_handler.reset(new QXcbEglNativeInterfaceHandler(connection->nativeInterface())); @@ -84,13 +143,99 @@ QPlatformOffscreenSurface *QXcbEglIntegration::createPlatformOffscreenSurface(QO return new QEGLPbuffer(eglDisplay(), screen->surfaceFormatFor(surface->requestedFormat()), surface); } -void *QXcbEglIntegration::xlib_display() const +xcb_visualid_t QXcbEglIntegration::getCompatibleVisualId(xcb_screen_t *screen, EGLConfig config) const { -#if QT_CONFIG(xcb_xlib) - return m_connection->xlib_display(); -#else - return EGL_DEFAULT_DISPLAY; -#endif + xcb_visualid_t visualId = 0; + EGLint eglValue = 0; + + EGLint configRedSize = 0; + eglGetConfigAttrib(eglDisplay(), config, EGL_RED_SIZE, &configRedSize); + + EGLint configGreenSize = 0; + eglGetConfigAttrib(eglDisplay(), config, EGL_GREEN_SIZE, &configGreenSize); + + EGLint configBlueSize = 0; + eglGetConfigAttrib(eglDisplay(), config, EGL_BLUE_SIZE, &configBlueSize); + + EGLint configAlphaSize = 0; + eglGetConfigAttrib(eglDisplay(), config, EGL_ALPHA_SIZE, &configAlphaSize); + + eglGetConfigAttrib(eglDisplay(), config, EGL_CONFIG_ID, &eglValue); + int configId = eglValue; + + // See if EGL provided a valid VisualID: + eglGetConfigAttrib(eglDisplay(), config, EGL_NATIVE_VISUAL_ID, &eglValue); + visualId = eglValue; + if (visualId) { + // EGL has suggested a visual id, so get the rest of the visual info for that id: + std::optional<VisualInfo> chosenVisualInfo = getVisualInfo(screen, visualId); + if (chosenVisualInfo) { + // Skip size checks if implementation supports non-matching visual + // and config (QTBUG-9444). + if (q_hasEglExtension(eglDisplay(), "EGL_NV_post_convert_rounding")) + return visualId; + // Skip also for i.MX6 where 565 visuals are suggested for the default 444 configs and it works just fine. + const char *vendor = eglQueryString(eglDisplay(), EGL_VENDOR); + if (vendor && strstr(vendor, "Vivante")) + return visualId; + + int visualRedSize = qPopulationCount(chosenVisualInfo->visualType.red_mask); + int visualGreenSize = qPopulationCount(chosenVisualInfo->visualType.green_mask); + int visualBlueSize = qPopulationCount(chosenVisualInfo->visualType.blue_mask); + int visualAlphaSize = chosenVisualInfo->depth - visualRedSize - visualBlueSize - visualGreenSize; + + const bool visualMatchesConfig = visualRedSize >= configRedSize + && visualGreenSize >= configGreenSize + && visualBlueSize >= configBlueSize + && visualAlphaSize >= configAlphaSize; + + // In some cases EGL tends to suggest a 24-bit visual for 8888 + // configs. In such a case we have to fall back to getVisualInfo. + if (!visualMatchesConfig) { + visualId = 0; + qCDebug(lcQpaGl, + "EGL suggested using X Visual ID %d (%d %d %d %d depth %d) for EGL config %d" + "(%d %d %d %d), but this is incompatible", + visualId, visualRedSize, visualGreenSize, visualBlueSize, visualAlphaSize, chosenVisualInfo->depth, + configId, configRedSize, configGreenSize, configBlueSize, configAlphaSize); + } + } else { + qCDebug(lcQpaGl, "EGL suggested using X Visual ID %d for EGL config %d, but that isn't a valid ID", + visualId, configId); + visualId = 0; + } + } + else + qCDebug(lcQpaGl, "EGL did not suggest a VisualID (EGL_NATIVE_VISUAL_ID was zero) for EGLConfig %d", configId); + + if (visualId) { + qCDebug(lcQpaGl, configAlphaSize > 0 + ? "Using ARGB Visual ID %d provided by EGL for config %d" + : "Using Opaque Visual ID %d provided by EGL for config %d", visualId, configId); + return visualId; + } + + // Finally, try to use getVisualInfo and only use the bit depths to match on: + if (!visualId) { + uint8_t depth = configRedSize + configGreenSize + configBlueSize + configAlphaSize; + std::optional<VisualInfo> matchingVisual = getVisualInfo(screen, std::nullopt, depth); + if (!matchingVisual) { + // Try again without taking the alpha channel into account: + depth = configRedSize + configGreenSize + configBlueSize; + matchingVisual = getVisualInfo(screen, std::nullopt, depth); + } + + if (matchingVisual) + visualId = matchingVisual->visualType.visual_id; + } + + if (visualId) { + qCDebug(lcQpaGl, "Using Visual ID %d provided by getVisualInfo for EGL config %d", visualId, configId); + return visualId; + } + + qWarning("Unable to find an X11 visual which matches EGL config %d", configId); + return 0; } QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.h b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.h index 5ea37c0924..7caf4304bc 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.h +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.h @@ -38,10 +38,14 @@ public: bool supportsThreadedOpenGL() const override { return true; } EGLDisplay eglDisplay() const { return m_egl_display; } - void *xlib_display() const; + + bool usingPlatformDisplay() const { return m_using_platform_display; } + + xcb_visualid_t getCompatibleVisualId(xcb_screen_t *screen, EGLConfig config) const; private: QXcbConnection *m_connection; EGLDisplay m_egl_display; + bool m_using_platform_display; QScopedPointer<QXcbEglNativeInterfaceHandler> m_native_interface_handler; }; diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp index e5ca2b4864..bf2ceb96f4 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp @@ -6,7 +6,10 @@ #include "qxcbeglintegration.h" #include <QtGui/private/qeglconvenience_p.h> -#include <QtGui/private/qxlibeglintegration_p.h> + +#ifndef EGL_EXT_platform_base +typedef EGLSurface (EGLAPIENTRYP PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC) (EGLDisplay dpy, EGLConfig config, void *native_window, const EGLint *attrib_list); +#endif QT_BEGIN_NAMESPACE @@ -29,35 +32,34 @@ void QXcbEglWindow::resolveFormat(const QSurfaceFormat &format) m_format = q_glFormatFromConfig(m_glIntegration->eglDisplay(), m_config, format); } -#if QT_CONFIG(xcb_xlib) const xcb_visualtype_t *QXcbEglWindow::createVisual() { QXcbScreen *scr = xcbScreen(); if (!scr) return QXcbWindow::createVisual(); - Display *xdpy = static_cast<Display *>(m_glIntegration->xlib_display()); - VisualID id = QXlibEglIntegration::getCompatibleVisualId(xdpy, m_glIntegration->eglDisplay(), m_config); - - XVisualInfo visualInfoTemplate; - memset(&visualInfoTemplate, 0, sizeof(XVisualInfo)); - visualInfoTemplate.visualid = id; - - XVisualInfo *visualInfo; - int matchingCount = 0; - visualInfo = XGetVisualInfo(xdpy, VisualIDMask, &visualInfoTemplate, &matchingCount); - const xcb_visualtype_t *xcb_visualtype = scr->visualForId(visualInfo->visualid); - XFree(visualInfo); - - return xcb_visualtype; + xcb_visualid_t id = m_glIntegration->getCompatibleVisualId(scr->screen(), m_config); + return scr->visualForId(id); } -#endif void QXcbEglWindow::create() { QXcbWindow::create(); + // this is always true without egl_x11 + if (m_glIntegration->usingPlatformDisplay()) { + auto createPlatformWindowSurface = reinterpret_cast<PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC>( + eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT")); + m_surface = createPlatformWindowSurface(m_glIntegration->eglDisplay(), + m_config, + reinterpret_cast<void *>(&m_window), + nullptr); + return; + } + +#if QT_CONFIG(egl_x11) m_surface = eglCreateWindowSurface(m_glIntegration->eglDisplay(), m_config, m_window, nullptr); +#endif } QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.h b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.h index 79f1df0493..935a0c6d43 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.h +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.h @@ -25,10 +25,7 @@ public: protected: void create() override; void resolveFormat(const QSurfaceFormat &format) override; - -#if QT_CONFIG(xcb_xlib) const xcb_visualtype_t *createVisual() override; -#endif private: QXcbEglIntegration *m_glIntegration; diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/CMakeLists.txt b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/CMakeLists.txt index ae81eba545..f9f78ad1eb 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/CMakeLists.txt +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from xcb_glx.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## QXcbGlxIntegrationPlugin Plugin: @@ -24,6 +25,7 @@ qt_internal_add_plugin(QXcbGlxIntegrationPlugin Qt::Gui Qt::GuiPrivate Qt::XcbQpaPrivate + NO_UNITY_BUILD # X11 define clashes ) ## Scopes: diff --git a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp index 5992ebd74e..04eac027cd 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_glx/qglxintegration.cpp @@ -242,9 +242,9 @@ QGLXContext::QGLXContext(Display *display, QXcbScreen *screen, const QSurfaceFor // Robustness must match that of the shared context. if (share && share->format().testOption(QSurfaceFormat::ResetNotification)) m_format.setOption(QSurfaceFormat::ResetNotification); - Q_ASSERT(glVersions.count() > 0); + Q_ASSERT(glVersions.size() > 0); - for (int i = 0; !m_context && i < glVersions.count(); i++) { + for (int i = 0; !m_context && i < glVersions.size(); i++) { const int version = glVersions[i]; if (version > requestedVersion) continue; @@ -395,14 +395,19 @@ QGLXContext::QGLXContext(Display *display, GLXContext context, void *visualInfo, int numConfigs = 0; static const int attribs[] = { GLX_FBCONFIG_ID, configId, None }; configs = glXChooseFBConfig(m_display, screenNumber, attribs, &numConfigs); - if (!configs || numConfigs < 1) { + if (!configs) { + qWarning("QGLXContext: Failed to find config(invalid arguments for glXChooseFBConfig)"); + return; + } else if (numConfigs < 1) { qWarning("QGLXContext: Failed to find config"); + XFree(configs); return; } if (configs && numConfigs > 1) // this is suspicious so warn but let it continue qWarning("QGLXContext: Multiple configs for FBConfig ID %d", configId); m_config = configs[0]; + XFree(configs); } Q_ASSERT(vinfo || m_config); diff --git a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp index 96a19552a5..d523eecc5a 100644 --- a/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp +++ b/src/plugins/platforms/xcb/nativepainting/qbackingstore_x11.cpp @@ -16,10 +16,6 @@ #include <X11/Xlib.h> #undef register -#ifndef None -#define None 0L -#endif - QT_BEGIN_NAMESPACE QXcbNativeBackingStore::QXcbNativeBackingStore(QWindow *window) @@ -78,11 +74,8 @@ void QXcbNativeBackingStore::flush(QWindow *window, const QRegion ®ion, const XRenderSetPictureClipRectangles(display(), dstPic, 0, 0, clipRects.constData(), clipRects.size()); - XRenderComposite(display(), PictOpSrc, srcPic, None, dstPic, - br.x() + offset.x(), br.y() + offset.y(), - 0, 0, - br.x(), br.y(), - br.width(), br.height()); + XRenderComposite(display(), PictOpSrc, srcPic, 0L /*None*/, dstPic, br.x() + offset.x(), + br.y() + offset.y(), 0, 0, br.x(), br.y(), br.width(), br.height()); XRenderFreePicture(display(), dstPic); } diff --git a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp index f630f538aa..743ff92654 100644 --- a/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp +++ b/src/plugins/platforms/xcb/nativepainting/qpaintengine_x11.cpp @@ -2134,7 +2134,7 @@ void QX11PaintEngine::drawPixmap(const QRectF &r, const QPixmap &px, const QRect XFillRectangle(d->dpy, d->hd, d->gc, x, y, sw, sh); restore_clip = true; } else if (mono_dst && !mono_src) { - QBitmap bitmap(pixmap); + QBitmap bitmap = QBitmap::fromPixmap(pixmap); XCopyArea(d->dpy, qt_x11PixmapHandle(bitmap), d->hd, d->gc, sx, sy, sw, sh, x, y); } else { XCopyArea(d->dpy, qt_x11PixmapHandle(pixmap), d->hd, d->gc, sx, sy, sw, sh, x, y); diff --git a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp index 2a84431cde..a4e820ea92 100644 --- a/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp +++ b/src/plugins/platforms/xcb/nativepainting/qpixmap_x11.cpp @@ -1314,7 +1314,7 @@ QBitmap QX11PlatformPixmap::mask() const #endif if (d == 1) { QX11PlatformPixmap *that = const_cast<QX11PlatformPixmap*>(this); - mask = QPixmap(that); + mask = QBitmap::fromPixmap(QPixmap(that)); } else { mask = mask_to_bitmap(xinfo.screen()); } @@ -1437,22 +1437,16 @@ QPixmap QX11PlatformPixmap::transformed(const QTransform &transform, Qt::Transfo transform.m21(), transform.m22(), transform.m23(), 0., 0., 1); bool complex_xform = false; - qreal scaledWidth; - qreal scaledHeight; if (mat.type() <= QTransform::TxScale) { - scaledHeight = qAbs(mat.m22()) * hs + 0.9999; - scaledWidth = qAbs(mat.m11()) * ws + 0.9999; - h = qAbs(int(scaledHeight)); - w = qAbs(int(scaledWidth)); + h = qRound(qAbs(mat.m22()) * hs); + w = qRound(qAbs(mat.m11()) * ws); } else { // rotation or shearing QPolygonF a(QRectF(0, 0, ws, hs)); a = mat.map(a); QRect r = a.boundingRect().toAlignedRect(); w = r.width(); h = r.height(); - scaledWidth = w; - scaledHeight = h; complex_xform = true; } mat = QPixmap::trueMatrix(mat, ws, hs); // true matrix @@ -1461,7 +1455,7 @@ QPixmap QX11PlatformPixmap::transformed(const QTransform &transform, Qt::Transfo mat = mat.inverted(&invertible); // invert matrix if (h == 0 || w == 0 || !invertible - || qAbs(scaledWidth) >= 32768 || qAbs(scaledHeight) >= 32768 ) + || qAbs(h) >= 32768 || qAbs(w) >= 32768 ) // error, return null pixmap return QPixmap(); diff --git a/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp b/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp index ecc21eb1f5..7b0094044b 100644 --- a/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp +++ b/src/plugins/platforms/xcb/nativepainting/qtessellator.cpp @@ -5,6 +5,7 @@ #include <QRect> #include <QList> +#include <QMap> #include <QDebug> #include <qmath.h> diff --git a/src/plugins/platforms/xcb/qt_xlib_wrapper.c b/src/plugins/platforms/xcb/qt_xlib_wrapper.c new file mode 100644 index 0000000000..f4614e5504 --- /dev/null +++ b/src/plugins/platforms/xcb/qt_xlib_wrapper.c @@ -0,0 +1,7 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include "qt_xlib_wrapper.h" + +#include <X11/Xlib.h> + +void qt_XFlush(Display *dpy) { XFlush(dpy); } diff --git a/src/plugins/platforms/xcb/qt_xlib_wrapper.h b/src/plugins/platforms/xcb/qt_xlib_wrapper.h new file mode 100644 index 0000000000..642dbdeadd --- /dev/null +++ b/src/plugins/platforms/xcb/qt_xlib_wrapper.h @@ -0,0 +1,17 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#ifndef QT_XLIB_WRAPPER_H +#define QT_XLIB_WRAPPER_H + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct _XDisplay Display; + void qt_XFlush(Display *dpy); + +#ifdef __cplusplus +} +#endif + +#endif // QT_XLIB_WRAPPER_H diff --git a/src/plugins/platforms/xcb/qxcbatom.cpp b/src/plugins/platforms/xcb/qxcbatom.cpp index 8f984c7280..dd5596653c 100644 --- a/src/plugins/platforms/xcb/qxcbatom.cpp +++ b/src/plugins/platforms/xcb/qxcbatom.cpp @@ -54,6 +54,8 @@ static const char *xcb_atomnames = { "_QT_CLOSE_CONNECTION\0" + "_QT_GET_TIMESTAMP\0" + "_MOTIF_WM_HINTS\0" "DTWM_IS_RUNNING\0" @@ -109,11 +111,13 @@ static const char *xcb_atomnames = { "_NET_WM_WINDOW_TYPE_NORMAL\0" "_KDE_NET_WM_WINDOW_TYPE_OVERRIDE\0" + "_KDE_NET_WM_DESKTOP_FILE\0" "_KDE_NET_WM_FRAME_STRUT\0" "_NET_FRAME_EXTENTS\0" "_NET_STARTUP_INFO\0" "_NET_STARTUP_INFO_BEGIN\0" + "_NET_STARTUP_ID\0" "_NET_SUPPORTING_WM_CHECK\0" @@ -193,6 +197,7 @@ static const char *xcb_atomnames = { "_COMPIZ_DECOR_REQUEST\0" "_COMPIZ_DECOR_DELETE_PIXMAP\0" "_COMPIZ_TOOLKIT_ACTION\0" + "_GTK_APPLICATION_ID\0" "_GTK_LOAD_ICONTHEMES\0" "AT_SPI_BUS\0" "EDID\0" @@ -212,29 +217,25 @@ void QXcbAtom::initialize(xcb_connection_t *connection) } void QXcbAtom::initializeAllAtoms(xcb_connection_t *connection) { - const char *names[QXcbAtom::NAtoms]; - const char *ptr = xcb_atomnames; - + const char *name = xcb_atomnames; + size_t name_len; 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]; + while ((name_len = strlen(name)) != 0) { + cookies[i] = xcb_intern_atom(connection, false, name_len, name); + ++i; + name += name_len + 1; // jump over the \0 + } + 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], nullptr); - m_allAtoms[i] = reply->atom; - free(reply); + if (reply) { + m_allAtoms[i] = reply->atom; + free(reply); + } } } diff --git a/src/plugins/platforms/xcb/qxcbatom.h b/src/plugins/platforms/xcb/qxcbatom.h index 48924dd626..bc677eaf3e 100644 --- a/src/plugins/platforms/xcb/qxcbatom.h +++ b/src/plugins/platforms/xcb/qxcbatom.h @@ -10,201 +10,206 @@ 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 + AtomWM_PROTOCOLS, + AtomWM_DELETE_WINDOW, + AtomWM_TAKE_FOCUS, + Atom_NET_WM_PING, + Atom_NET_WM_CONTEXT_HELP, + Atom_NET_WM_SYNC_REQUEST, + Atom_NET_WM_SYNC_REQUEST_COUNTER, + AtomMANAGER, // System tray notification + Atom_NET_SYSTEM_TRAY_OPCODE, // System tray operation // ICCCM window state - WM_STATE, - WM_CHANGE_STATE, - WM_CLASS, - WM_NAME, + AtomWM_STATE, + AtomWM_CHANGE_STATE, + AtomWM_CLASS, + AtomWM_NAME, // Session management - WM_CLIENT_LEADER, - WM_WINDOW_ROLE, - SM_CLIENT_ID, - WM_CLIENT_MACHINE, + AtomWM_CLIENT_LEADER, + AtomWM_WINDOW_ROLE, + AtomSM_CLIENT_ID, + AtomWM_CLIENT_MACHINE, // Clipboard - CLIPBOARD, - INCR, - TARGETS, - MULTIPLE, - TIMESTAMP, - SAVE_TARGETS, - CLIP_TEMPORARY, - _QT_SELECTION, - _QT_CLIPBOARD_SENTINEL, - _QT_SELECTION_SENTINEL, - CLIPBOARD_MANAGER, + AtomCLIPBOARD, + AtomINCR, + AtomTARGETS, + AtomMULTIPLE, + AtomTIMESTAMP, + AtomSAVE_TARGETS, + AtomCLIP_TEMPORARY, + Atom_QT_SELECTION, + Atom_QT_CLIPBOARD_SENTINEL, + Atom_QT_SELECTION_SENTINEL, + AtomCLIPBOARD_MANAGER, - RESOURCE_MANAGER, + AtomRESOURCE_MANAGER, - _XSETROOT_ID, + Atom_XSETROOT_ID, - _QT_SCROLL_DONE, - _QT_INPUT_ENCODING, + Atom_QT_SCROLL_DONE, + Atom_QT_INPUT_ENCODING, // Qt/XCB specific - _QT_CLOSE_CONNECTION, + Atom_QT_CLOSE_CONNECTION, - _MOTIF_WM_HINTS, + Atom_QT_GET_TIMESTAMP, - DTWM_IS_RUNNING, - ENLIGHTENMENT_DESKTOP, - _DT_SAVE_MODE, - _SGI_DESKS_MANAGER, + Atom_MOTIF_WM_HINTS, + + AtomDTWM_IS_RUNNING, + AtomENLIGHTENMENT_DESKTOP, + Atom_DT_SAVE_MODE, + Atom_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_STATE_HIDDEN, - - _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, + Atom_NET_SUPPORTED, + Atom_NET_VIRTUAL_ROOTS, + Atom_NET_WORKAREA, + + Atom_NET_MOVERESIZE_WINDOW, + Atom_NET_WM_MOVERESIZE, + + Atom_NET_WM_NAME, + Atom_NET_WM_ICON_NAME, + Atom_NET_WM_ICON, + + Atom_NET_WM_PID, + + Atom_NET_WM_WINDOW_OPACITY, + + Atom_NET_WM_STATE, + Atom_NET_WM_STATE_ABOVE, + Atom_NET_WM_STATE_BELOW, + Atom_NET_WM_STATE_FULLSCREEN, + Atom_NET_WM_STATE_MAXIMIZED_HORZ, + Atom_NET_WM_STATE_MAXIMIZED_VERT, + Atom_NET_WM_STATE_MODAL, + Atom_NET_WM_STATE_STAYS_ON_TOP, + Atom_NET_WM_STATE_DEMANDS_ATTENTION, + Atom_NET_WM_STATE_HIDDEN, + + Atom_NET_WM_USER_TIME, + Atom_NET_WM_USER_TIME_WINDOW, + Atom_NET_WM_FULL_PLACEMENT, + + Atom_NET_WM_WINDOW_TYPE, + Atom_NET_WM_WINDOW_TYPE_DESKTOP, + Atom_NET_WM_WINDOW_TYPE_DOCK, + Atom_NET_WM_WINDOW_TYPE_TOOLBAR, + Atom_NET_WM_WINDOW_TYPE_MENU, + Atom_NET_WM_WINDOW_TYPE_UTILITY, + Atom_NET_WM_WINDOW_TYPE_SPLASH, + Atom_NET_WM_WINDOW_TYPE_DIALOG, + Atom_NET_WM_WINDOW_TYPE_DROPDOWN_MENU, + Atom_NET_WM_WINDOW_TYPE_POPUP_MENU, + Atom_NET_WM_WINDOW_TYPE_TOOLTIP, + Atom_NET_WM_WINDOW_TYPE_NOTIFICATION, + Atom_NET_WM_WINDOW_TYPE_COMBO, + Atom_NET_WM_WINDOW_TYPE_DND, + Atom_NET_WM_WINDOW_TYPE_NORMAL, + Atom_KDE_NET_WM_WINDOW_TYPE_OVERRIDE, + + Atom_KDE_NET_WM_DESKTOP_FILE, + Atom_KDE_NET_WM_FRAME_STRUT, + Atom_NET_FRAME_EXTENTS, + + Atom_NET_STARTUP_INFO, + Atom_NET_STARTUP_INFO_BEGIN, + Atom_NET_STARTUP_ID, + + Atom_NET_SUPPORTING_WM_CHECK, + + Atom_NET_WM_CM_S0, + + Atom_NET_SYSTEM_TRAY_VISUAL, + + Atom_NET_ACTIVE_WINDOW, // Property formats - TEXT, - UTF8_STRING, - CARDINAL, + AtomTEXT, + AtomUTF8_STRING, + AtomCARDINAL, // Xdnd - XdndEnter, - XdndPosition, - XdndStatus, - XdndLeave, - XdndDrop, - XdndFinished, - XdndTypelist, - XdndActionList, - - XdndSelection, - - XdndAware, - XdndProxy, - - XdndActionCopy, - XdndActionLink, - XdndActionMove, - XdndActionAsk, - XdndActionPrivate, + AtomXdndEnter, + AtomXdndPosition, + AtomXdndStatus, + AtomXdndLeave, + AtomXdndDrop, + AtomXdndFinished, + AtomXdndTypelist, + AtomXdndActionList, + + AtomXdndSelection, + + AtomXdndAware, + AtomXdndProxy, + + AtomXdndActionCopy, + AtomXdndActionLink, + AtomXdndActionMove, + AtomXdndActionAsk, + AtomXdndActionPrivate, // Xkb - _XKB_RULES_NAMES, + Atom_XKB_RULES_NAMES, // XEMBED - _XEMBED, - _XEMBED_INFO, + Atom_XEMBED, + Atom_XEMBED_INFO, // XInput2 - ButtonLeft, - ButtonMiddle, - ButtonRight, - ButtonWheelUp, - ButtonWheelDown, - ButtonHorizWheelLeft, - ButtonHorizWheelRight, - AbsMTPositionX, - AbsMTPositionY, - AbsMTTouchMajor, - AbsMTTouchMinor, - AbsMTOrientation, - AbsMTPressure, - AbsMTTrackingID, - MaxContacts, - RelX, - RelY, + AtomButtonLeft, + AtomButtonMiddle, + AtomButtonRight, + AtomButtonWheelUp, + AtomButtonWheelDown, + AtomButtonHorizWheelLeft, + AtomButtonHorizWheelRight, + AtomAbsMTPositionX, + AtomAbsMTPositionY, + AtomAbsMTTouchMajor, + AtomAbsMTTouchMinor, + AtomAbsMTOrientation, + AtomAbsMTPressure, + AtomAbsMTTrackingID, + AtomMaxContacts, + AtomRelX, + AtomRelY, // 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, - - _ICC_PROFILE, + AtomAbsX, + AtomAbsY, + AtomAbsPressure, + AtomAbsTiltX, + AtomAbsTiltY, + AtomAbsWheel, + AtomAbsDistance, + AtomWacomSerialIDs, + AtomINTEGER, + AtomRelHorizWheel, + AtomRelVertWheel, + AtomRelHorizScroll, + AtomRelVertScroll, + + Atom_XSETTINGS_SETTINGS, + + Atom_COMPIZ_DECOR_PENDING, + Atom_COMPIZ_DECOR_REQUEST, + Atom_COMPIZ_DECOR_DELETE_PIXMAP, + Atom_COMPIZ_TOOLKIT_ACTION, + Atom_GTK_APPLICATION_ID, + Atom_GTK_LOAD_ICONTHEMES, + + AtomAT_SPI_BUS, + + AtomEDID, + AtomEDID_DATA, + AtomXFree86_DDC_EDID1_RAWDATA, + + Atom_ICC_PROFILE, NAtoms }; diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp index d6cc0bc2af..8353fac6a9 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp +++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp @@ -173,7 +173,7 @@ void QXcbBackingStoreImage::init(const QSize &size, uint depth, QImage::Format f m_qimage_format = format; m_hasAlpha = QImage::toPixelFormat(m_qimage_format).alphaUsage() == QPixelFormat::UsesAlpha; if (!m_hasAlpha) - m_qimage_format = qt_maybeAlphaVersionWithSameDepth(m_qimage_format); + m_qimage_format = qt_maybeDataCompatibleAlphaVersion(m_qimage_format); memset(&m_shm_info, 0, sizeof m_shm_info); @@ -866,6 +866,7 @@ void QXcbBackingStore::render(xcb_window_t window, const QRegion ®ion, const } QPlatformBackingStore::FlushResult QXcbBackingStore::rhiFlush(QWindow *window, + qreal sourceDevicePixelRatio, const QRegion ®ion, const QPoint &offset, QPlatformTextureList *textures, @@ -876,8 +877,10 @@ QPlatformBackingStore::FlushResult QXcbBackingStore::rhiFlush(QWindow *window, m_image->flushScrolledRegion(true); - QPlatformBackingStore::rhiFlush(window, region, offset, textures, translucentBackground); - + auto result = QPlatformBackingStore::rhiFlush(window, sourceDevicePixelRatio, region, offset, + textures, translucentBackground); + if (result != FlushSuccess) + return result; QXcbWindow *platformWindow = static_cast<QXcbWindow *>(window->handle()); if (platformWindow->needsSync()) { platformWindow->updateSyncRequestCounter(); diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.h b/src/plugins/platforms/xcb/qxcbbackingstore.h index c3ee4a9e31..979b254326 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.h +++ b/src/plugins/platforms/xcb/qxcbbackingstore.h @@ -24,6 +24,7 @@ public: QPaintDevice *paintDevice() override; void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) override; FlushResult rhiFlush(QWindow *window, + qreal sourceDevicePixelRatio, const QRegion ®ion, const QPoint &offset, QPlatformTextureList *textures, diff --git a/src/plugins/platforms/xcb/qxcbclipboard.cpp b/src/plugins/platforms/xcb/qxcbclipboard.cpp index 6c15d7c142..40e2f47354 100644 --- a/src/plugins/platforms/xcb/qxcbclipboard.cpp +++ b/src/plugins/platforms/xcb/qxcbclipboard.cpp @@ -31,7 +31,7 @@ public: break; case QClipboard::Clipboard: - modeAtom = m_clipboard->atom(QXcbAtom::CLIPBOARD); + modeAtom = m_clipboard->atom(QXcbAtom::AtomCLIPBOARD); break; default: @@ -56,12 +56,12 @@ protected: if (isEmpty()) return QStringList(); - if (!formatList.count()) { + if (!formatList.size()) { QXcbClipboardMime *that = const_cast<QXcbClipboardMime *>(this); // get the list of targets from the current clipboard owner - we do this // once so that multiple calls to this function don't require multiple // server round trips... - that->format_atoms = m_clipboard->getDataInFormat(modeAtom, m_clipboard->atom(QXcbAtom::TARGETS)); + that->format_atoms = m_clipboard->getDataInFormat(modeAtom, m_clipboard->atom(QXcbAtom::AtomTARGETS)); if (format_atoms.size() > 0) { const xcb_atom_t *targets = (const xcb_atom_t *) format_atoms.data(); @@ -126,7 +126,7 @@ QXcbClipboardTransaction::QXcbClipboardTransaction(QXcbClipboard *clipboard, xcb xcb_change_window_attributes(m_clipboard->xcb_connection(), m_window, XCB_CW_EVENT_MASK, values); - m_abortTimerId = startTimer(m_clipboard->clipboardTimeout()); + m_abortTimerId = startTimer(std::chrono::milliseconds{m_clipboard->clipboardTimeout()}); } QXcbClipboardTransaction::~QXcbClipboardTransaction() @@ -145,7 +145,7 @@ bool QXcbClipboardTransaction::updateIncrementalProperty(const xcb_property_noti // restart the timer if (m_abortTimerId) killTimer(m_abortTimerId); - m_abortTimerId = startTimer(m_clipboard->clipboardTimeout()); + m_abortTimerId = startTimer(std::chrono::milliseconds{m_clipboard->clipboardTimeout()}); uint bytes_left = uint(m_data.size()) - m_offset; if (bytes_left > 0) { @@ -203,7 +203,7 @@ QXcbClipboard::QXcbClipboard(QXcbConnection *c) xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->qtSelectionOwner(), XCB_ATOM_PRIMARY, mask); xcb_xfixes_select_selection_input_checked(xcb_connection(), connection()->qtSelectionOwner(), - atom(QXcbAtom::CLIPBOARD), mask); + atom(QXcbAtom::AtomCLIPBOARD), mask); } // xcb_change_property_request_t and xcb_get_property_request_t are the same size @@ -218,13 +218,13 @@ QXcbClipboard::~QXcbClipboard() m_timestamp[QClipboard::Selection] != XCB_CURRENT_TIME) { // First we check if there is a clipboard manager. - if (connection()->selectionOwner(atom(QXcbAtom::CLIPBOARD_MANAGER)) != XCB_NONE) { + if (connection()->selectionOwner(atom(QXcbAtom::AtomCLIPBOARD_MANAGER)) != XCB_NONE) { // we delete the property so the manager saves all TARGETS. xcb_delete_property(xcb_connection(), connection()->qtSelectionOwner(), - atom(QXcbAtom::_QT_SELECTION)); + atom(QXcbAtom::Atom_QT_SELECTION)); xcb_convert_selection(xcb_connection(), connection()->qtSelectionOwner(), - atom(QXcbAtom::CLIPBOARD_MANAGER), atom(QXcbAtom::SAVE_TARGETS), - atom(QXcbAtom::_QT_SELECTION), connection()->time()); + atom(QXcbAtom::AtomCLIPBOARD_MANAGER), atom(QXcbAtom::AtomSAVE_TARGETS), + atom(QXcbAtom::Atom_QT_SELECTION), connection()->time()); connection()->sync(); // waiting until the clipboard manager fetches the content. @@ -259,7 +259,7 @@ bool QXcbClipboard::handlePropertyNotify(const xcb_generic_event_t *event) xcb_atom_t QXcbClipboard::atomForMode(QClipboard::Mode mode) const { if (mode == QClipboard::Clipboard) - return atom(QXcbAtom::CLIPBOARD); + return atom(QXcbAtom::AtomCLIPBOARD); if (mode == QClipboard::Selection) return XCB_ATOM_PRIMARY; return XCB_NONE; @@ -269,7 +269,7 @@ QClipboard::Mode QXcbClipboard::modeForAtom(xcb_atom_t a) const { if (a == XCB_ATOM_PRIMARY) return QClipboard::Selection; - if (a == atom(QXcbAtom::CLIPBOARD)) + if (a == atom(QXcbAtom::AtomCLIPBOARD)) return QClipboard::Clipboard; // not supported enum value, used to detect errors return QClipboard::FindBuffer; @@ -412,10 +412,10 @@ xcb_atom_t QXcbClipboard::sendTargetsSelection(QMimeData *d, xcb_window_t window types.append(atoms.at(j)); } } - types.append(atom(QXcbAtom::TARGETS)); - types.append(atom(QXcbAtom::MULTIPLE)); - types.append(atom(QXcbAtom::TIMESTAMP)); - types.append(atom(QXcbAtom::SAVE_TARGETS)); + types.append(atom(QXcbAtom::AtomTARGETS)); + types.append(atom(QXcbAtom::AtomMULTIPLE)); + types.append(atom(QXcbAtom::AtomTIMESTAMP)); + types.append(atom(QXcbAtom::AtomSAVE_TARGETS)); xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, property, XCB_ATOM_ATOM, 32, types.size(), (const void *)types.constData()); @@ -439,7 +439,7 @@ xcb_atom_t QXcbClipboard::sendSelection(QMimeData *d, xcb_atom_t target, xcb_win // don't allow INCR transfers when using MULTIPLE or to // Motif clients (since Motif doesn't support INCR) - static xcb_atom_t motif_clip_temporary = atom(QXcbAtom::CLIP_TEMPORARY); + static xcb_atom_t motif_clip_temporary = atom(QXcbAtom::AtomCLIP_TEMPORARY); bool allow_incr = property != motif_clip_temporary; // This 'bool' can be removed once there is a proper fix for QTBUG-32853 if (m_clipboard_closing) @@ -448,7 +448,7 @@ xcb_atom_t QXcbClipboard::sendSelection(QMimeData *d, xcb_atom_t target, xcb_win if (data.size() > m_maxPropertyRequestDataBytes && allow_incr) { long bytes = data.size(); xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, property, - atom(QXcbAtom::INCR), 32, 1, (const void *)&bytes); + atom(QXcbAtom::AtomINCR), 32, 1, (const void *)&bytes); auto transaction = new QXcbClipboardTransaction(this, window, property, data, atomFormat, dataFormat); m_transactions.insert(window, transaction); return property; @@ -532,9 +532,9 @@ void QXcbClipboard::handleSelectionRequest(xcb_selection_request_event_t *req) return; } - xcb_atom_t targetsAtom = atom(QXcbAtom::TARGETS); - xcb_atom_t multipleAtom = atom(QXcbAtom::MULTIPLE); - xcb_atom_t timestampAtom = atom(QXcbAtom::TIMESTAMP); + xcb_atom_t targetsAtom = atom(QXcbAtom::AtomTARGETS); + xcb_atom_t multipleAtom = atom(QXcbAtom::AtomMULTIPLE); + xcb_atom_t timestampAtom = atom(QXcbAtom::AtomTIMESTAMP); struct AtomPair { xcb_atom_t target; xcb_atom_t property; } *multi = nullptr; xcb_atom_t multi_type = XCB_NONE; @@ -708,7 +708,7 @@ bool QXcbClipboard::clipboardReadProperty(xcb_window_t win, xcb_atom_t property, // correct size, not 0-term. if (size) *size = buffer_offset; - if (*type == atom(QXcbAtom::INCR)) + if (*type == atom(QXcbAtom::AtomINCR)) m_incr_receive_time = connection()->getTimestamp(); if (deleteProperty) xcb_delete_property(xcb_connection(), win, property); @@ -747,12 +747,12 @@ xcb_generic_event_t *QXcbClipboard::waitForClipboardEvent(xcb_window_t window, i const QXcbEventNode *flushedTailNode = queue->flushedTail(); if (checkManager) { - if (connection()->selectionOwner(atom(QXcbAtom::CLIPBOARD_MANAGER)) == XCB_NONE) + if (connection()->selectionOwner(atom(QXcbAtom::AtomCLIPBOARD_MANAGER)) == XCB_NONE) return nullptr; } // process other clipboard events, since someone is probably requesting data from us - auto clipboardAtom = atom(QXcbAtom::CLIPBOARD); + auto clipboardAtom = atom(QXcbAtom::AtomCLIPBOARD); e = queue->peek([clipboardAtom](xcb_generic_event_t *event, int type) { xcb_atom_t selection = XCB_ATOM_NONE; if (type == XCB_SELECTION_REQUEST) @@ -846,7 +846,7 @@ QByteArray QXcbClipboard::clipboardReadIncrementalProperty(xcb_window_t win, xcb QByteArray QXcbClipboard::getDataInFormat(xcb_atom_t modeAtom, xcb_atom_t fmtAtom) { - return getSelection(modeAtom, fmtAtom, atom(QXcbAtom::_QT_SELECTION)); + return getSelection(modeAtom, fmtAtom, atom(QXcbAtom::Atom_QT_SELECTION)); } QByteArray QXcbClipboard::getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t property, xcb_timestamp_t time) @@ -870,7 +870,7 @@ QByteArray QXcbClipboard::getSelection(xcb_atom_t selection, xcb_atom_t target, xcb_atom_t type; if (clipboardReadProperty(win, property, true, &buf, nullptr, &type, nullptr)) { - if (type == atom(QXcbAtom::INCR)) { + if (type == atom(QXcbAtom::AtomINCR)) { int nbytes = buf.size() >= 4 ? *((int*)buf.data()) : 0; buf = clipboardReadIncrementalProperty(win, property, nbytes, false); } diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index cf2a2af457..1056c6408f 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -35,6 +35,10 @@ #undef explicit #include <xcb/xinput.h> +#if QT_CONFIG(xcb_xlib) +#include "qt_xlib_wrapper.h" +#endif + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; @@ -81,16 +85,16 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra m_drag = new QXcbDrag(this); #endif - m_startupId = qgetenv("DESKTOP_STARTUP_ID"); - if (!m_startupId.isNull()) + setStartupId(qgetenv("DESKTOP_STARTUP_ID")); + if (!startupId().isNull()) qunsetenv("DESKTOP_STARTUP_ID"); const int focusInDelay = 100; m_focusInTimer.setSingleShot(true); m_focusInTimer.setInterval(focusInDelay); - m_focusInTimer.callOnTimeout([]() { + m_focusInTimer.callOnTimeout(this, []() { // No FocusIn events for us, proceed with FocusOut normally. - QWindowSystemInterface::handleWindowActivated(nullptr, Qt::ActiveWindowFocusReason); + QWindowSystemInterface::handleFocusWindowChanged(nullptr, Qt::ActiveWindowFocusReason); }); sync(); @@ -141,12 +145,12 @@ void QXcbConnection::removeWindowEventListener(xcb_window_t id) QXcbWindowEventListener *QXcbConnection::windowEventListenerFromId(xcb_window_t id) { - return m_mapper.value(id, 0); + return m_mapper.value(id, nullptr); } QXcbWindow *QXcbConnection::platformWindowFromId(xcb_window_t id) { - QXcbWindowEventListener *listener = m_mapper.value(id, 0); + QXcbWindowEventListener *listener = m_mapper.value(id, nullptr); if (listener) return listener->toWindow(); return nullptr; @@ -449,12 +453,12 @@ void QXcbConnection::printXcbError(const char *message, xcb_generic_error_t *err uint clamped_error_code = qMin<uint>(error->error_code, (sizeof(xcb_errors) / sizeof(xcb_errors[0])) - 1); uint clamped_major_code = qMin<uint>(error->major_code, (sizeof(xcb_protocol_request_codes) / sizeof(xcb_protocol_request_codes[0])) - 1); - qCWarning(lcQpaXcb, "%s: %d (%s), sequence: %d, resource id: %d, major code: %d (%s), minor code: %d", - message, - int(error->error_code), xcb_errors[clamped_error_code], - int(error->sequence), int(error->resource_id), - int(error->major_code), xcb_protocol_request_codes[clamped_major_code], - int(error->minor_code)); + qCDebug(lcQpaXcb, "%s: %d (%s), sequence: %d, resource id: %d, major code: %d (%s), minor code: %d", + message, + int(error->error_code), xcb_errors[clamped_error_code], + int(error->sequence), int(error->resource_id), + int(error->major_code), xcb_protocol_request_codes[clamped_major_code], + int(error->minor_code)); } static Qt::MouseButtons translateMouseButtons(int s) @@ -598,12 +602,12 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) if (clientMessage->format != 32) return; #if QT_CONFIG(draganddrop) - if (clientMessage->type == atom(QXcbAtom::XdndStatus)) + if (clientMessage->type == atom(QXcbAtom::AtomXdndStatus)) drag()->handleStatus(clientMessage); - else if (clientMessage->type == atom(QXcbAtom::XdndFinished)) + else if (clientMessage->type == atom(QXcbAtom::AtomXdndFinished)) drag()->handleFinished(clientMessage); #endif - if (m_systemTrayTracker && clientMessage->type == atom(QXcbAtom::MANAGER)) + if (m_systemTrayTracker && clientMessage->type == atom(QXcbAtom::AtomMANAGER)) m_systemTrayTracker->notifyManagerClientMessageEvent(clientMessage); HANDLE_PLATFORM_WINDOW_EVENT(xcb_client_message_event_t, window, handleClientMessageEvent); } @@ -654,7 +658,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) setTime(selectionRequest->time); #endif #if QT_CONFIG(draganddrop) - if (selectionRequest->selection == atom(QXcbAtom::XdndSelection)) + if (selectionRequest->selection == atom(QXcbAtom::AtomXdndSelection)) m_drag->handleSelectionRequest(selectionRequest); else #endif @@ -682,11 +686,11 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) if (m_clipboard->handlePropertyNotify(event)) break; #endif - if (propertyNotify->atom == atom(QXcbAtom::_NET_WORKAREA)) { + if (propertyNotify->atom == atom(QXcbAtom::Atom_NET_WORKAREA)) { QXcbVirtualDesktop *virtualDesktop = virtualDesktopForRootWindow(propertyNotify->window); if (virtualDesktop) virtualDesktop->updateWorkArea(); - } else if (propertyNotify->atom == atom(QXcbAtom::_NET_SUPPORTED)) { + } else if (propertyNotify->atom == atom(QXcbAtom::Atom_NET_SUPPORTED)) { m_wmSupport->updateNetWMAtoms(); } else { HANDLE_PLATFORM_WINDOW_EVENT(xcb_property_notify_event_t, window, handlePropertyNotifyEvent); @@ -713,7 +717,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) #ifndef QT_NO_CLIPBOARD m_clipboard->handleXFixesSelectionRequest(notify_event); #endif - for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops)) + for (QXcbVirtualDesktop *virtualDesktop : std::as_const(m_virtualDesktops)) virtualDesktop->handleXFixesSelectionNotify(notify_event); } else if (isXRandrType(response_type, XCB_RANDR_NOTIFY)) { if (!isAtLeastXRandR15()) @@ -771,6 +775,28 @@ void QXcbConnection::setMousePressWindow(QXcbWindow *w) m_mousePressWindow = w; } +QByteArray QXcbConnection::startupId() const +{ + return m_startupId; +} +void QXcbConnection::setStartupId(const QByteArray &nextId) +{ + m_startupId = nextId; + if (m_clientLeader) { + if (!nextId.isEmpty()) + xcb_change_property(xcb_connection(), + XCB_PROP_MODE_REPLACE, + clientLeader(), + atom(QXcbAtom::Atom_NET_STARTUP_ID), + atom(QXcbAtom::AtomUTF8_STRING), + 8, + nextId.size(), + nextId.constData()); + else + xcb_delete_property(xcb_connection(), clientLeader(), atom(QXcbAtom::Atom_NET_STARTUP_ID)); + } +} + void QXcbConnection::grabServer() { if (m_canGrabServer) @@ -796,8 +822,8 @@ xcb_timestamp_t QXcbConnection::getTimestamp() { // send a dummy event to myself to get the timestamp from X server. xcb_window_t window = rootWindow(); - xcb_atom_t dummyAtom = atom(QXcbAtom::CLIP_TEMPORARY); - xcb_change_property(xcb_connection(), XCB_PROP_MODE_APPEND, window, dummyAtom, + xcb_atom_t dummyAtom = atom(QXcbAtom::Atom_QT_GET_TIMESTAMP); + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window, dummyAtom, XCB_ATOM_INTEGER, 32, 0, nullptr); connection()->flush(); @@ -829,8 +855,6 @@ xcb_timestamp_t QXcbConnection::getTimestamp() xcb_timestamp_t timestamp = pn->time; free(event); - xcb_delete_property(xcb_connection(), window, dummyAtom); - return timestamp; } @@ -897,7 +921,7 @@ xcb_window_t QXcbConnection::clientLeader() xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_clientLeader, - atom(QXcbAtom::WM_CLIENT_LEADER), + atom(QXcbAtom::AtomWM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32, 1, @@ -910,13 +934,15 @@ xcb_window_t QXcbConnection::clientLeader() xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_clientLeader, - atom(QXcbAtom::SM_CLIENT_ID), + atom(QXcbAtom::AtomSM_CLIENT_ID), XCB_ATOM_STRING, 8, - session.length(), + session.size(), session.constData()); } #endif + + setStartupId(startupId()); } return m_clientLeader; } @@ -1029,8 +1055,8 @@ bool QXcbConnection::isUserInputEvent(xcb_generic_event_t *event) const 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)) + if (clientMessage->format == 32 && clientMessage->type == atom(QXcbAtom::AtomWM_PROTOCOLS)) + if (clientMessage->data.data32[0] == atom(QXcbAtom::AtomWM_DELETE_WINDOW)) isInputEvent = true; } @@ -1066,6 +1092,10 @@ void QXcbConnection::processXcbEvents(QEventLoop::ProcessEventsFlags flags) m_eventQueue->flushBufferedEvents(); } +#if QT_CONFIG(xcb_xlib) + qt_XFlush(static_cast<Display *>(xlib_display())); +#endif + xcb_flush(xcb_connection()); } @@ -1111,13 +1141,6 @@ Qt::MouseButtons QXcbConnection::queryMouseButtons() const return translateMouseButtons(stateMask); } -Qt::KeyboardModifiers QXcbConnection::queryKeyboardModifiers() const -{ - int stateMask = 0; - QXcbCursor::queryPointer(connection(), nullptr, nullptr, &stateMask); - return keyboard()->translateModifiers(stateMask); -} - QXcbGlIntegration *QXcbConnection::glIntegration() const { if (m_glIntegrationInitialized) diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 2d55524f6f..527744fe81 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -170,9 +170,8 @@ public: QXcbWindow *mousePressWindow() const { return m_mousePressWindow; } void setMousePressWindow(QXcbWindow *); - QByteArray startupId() const { return m_startupId; } - void setStartupId(const QByteArray &nextId) { m_startupId = nextId; } - void clearStartupId() { m_startupId.clear(); } + QByteArray startupId() const; + void setStartupId(const QByteArray &nextId); void grabServer(); void ungrabServer(); @@ -184,7 +183,6 @@ public: QXcbSystemTrayTracker *systemTrayTracker() const; Qt::MouseButtons queryMouseButtons() const; - Qt::KeyboardModifiers queryKeyboardModifiers() const; bool isUserInputEvent(xcb_generic_event_t *event) const; @@ -368,7 +366,7 @@ Q_DECLARE_TYPEINFO(QXcbConnection::TabletData, Q_RELOCATABLE_TYPE); class QXcbConnectionGrabber { public: - QXcbConnectionGrabber(QXcbConnection *connection); + Q_NODISCARD_CTOR QXcbConnectionGrabber(QXcbConnection *connection); ~QXcbConnectionGrabber(); void release(); private: diff --git a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp index 5f1b964afa..6416003bea 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_basic.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_basic.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qxcbconnection_basic.h" #include "qxcbbackingstore.h" // for createSystemVShmSegment() +#include "private/qoffsetstringarray_p.h" #include <xcb/randr.h> #include <xcb/shm.h> @@ -27,7 +28,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaXcb, "qt.qpa.xcb") #if QT_CONFIG(xcb_xlib) -static const char * const xcbConnectionErrors[] = { +static constexpr auto xcbConnectionErrors = qOffsetStringArray( "No error", /* Error 0 */ "I/O error", /* XCB_CONN_ERROR */ "Unsupported extension used", /* XCB_CONN_CLOSED_EXT_NOTSUPPORTED */ @@ -36,7 +37,7 @@ static const char * const xcbConnectionErrors[] = { "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) { @@ -60,8 +61,7 @@ static int ioErrorHandler(Display *dpy) /* 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) + if (code >= 0 && code < xcbConnectionErrors.count()) str = xcbConnectionErrors[code]; qWarning("The X11 connection broke: %s (code %d)", str, code); diff --git a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp index aa0f2fef65..c31e9b1039 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_screens.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_screens.cpp @@ -155,7 +155,7 @@ void QXcbConnection::updateScreens(const xcb_randr_notify_event_t *event) } } - qCDebug(lcQpaScreen) << "updateScreens: primary output is" << qAsConst(m_screens).first()->name(); + qCDebug(lcQpaScreen) << "updateScreens: primary output is" << std::as_const(m_screens).first()->name(); } } @@ -184,7 +184,7 @@ void QXcbConnection::updateScreen(QXcbScreen *screen, const xcb_randr_output_cha // 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); + std::as_const(m_screens).first()->setPrimary(false); m_screens.swapItemsAt(0, idx); } screen->virtualDesktop()->setPrimaryScreen(screen); @@ -204,7 +204,7 @@ QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop, if (screen->isPrimary()) { if (!m_screens.isEmpty()) - qAsConst(m_screens).first()->setPrimary(false); + std::as_const(m_screens).first()->setPrimary(false); m_screens.prepend(screen); } else { @@ -219,7 +219,7 @@ QXcbScreen *QXcbConnection::createScreen(QXcbVirtualDesktop *virtualDesktop, void QXcbConnection::destroyScreen(QXcbScreen *screen) { QXcbVirtualDesktop *virtualDesktop = screen->virtualDesktop(); - if (virtualDesktop->screens().count() == 1) { + if (virtualDesktop->screens().size() == 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(); @@ -253,7 +253,7 @@ void QXcbConnection::updateScreen_monitor(QXcbScreen *screen, xcb_randr_monitor_ if (screen->isPrimary()) { const int idx = m_screens.indexOf(screen); if (idx > 0) { - qAsConst(m_screens).first()->setPrimary(false); + std::as_const(m_screens).first()->setPrimary(false); m_screens.swapItemsAt(0, idx); } screen->virtualDesktop()->setPrimaryScreen(screen); @@ -268,7 +268,7 @@ QXcbScreen *QXcbConnection::createScreen_monitor(QXcbVirtualDesktop *virtualDesk if (screen->isPrimary()) { if (!m_screens.isEmpty()) - qAsConst(m_screens).first()->setPrimary(false); + std::as_const(m_screens).first()->setPrimary(false); m_screens.prepend(screen); } else { @@ -326,7 +326,7 @@ void QXcbConnection::initializeScreens(bool initialized) ++xcbScreenNumber; } - for (QXcbVirtualDesktop *virtualDesktop : qAsConst(m_virtualDesktops)) + for (QXcbVirtualDesktop *virtualDesktop : std::as_const(m_virtualDesktops)) virtualDesktop->subscribeToXFixesSelectionNotify(); if (m_virtualDesktops.isEmpty()) { @@ -334,7 +334,7 @@ void QXcbConnection::initializeScreens(bool initialized) } else { // Ensure the primary screen is first on the list if (primaryScreen) { - if (qAsConst(m_screens).first() != primaryScreen) { + if (std::as_const(m_screens).first() != primaryScreen) { m_screens.removeOne(primaryScreen); m_screens.prepend(primaryScreen); } @@ -342,14 +342,14 @@ void QXcbConnection::initializeScreens(bool initialized) // Push the screens to QGuiApplication if (!initialized) { - for (QXcbScreen *screen : qAsConst(m_screens)) { + for (QXcbScreen *screen : std::as_const(m_screens)) { qCDebug(lcQpaScreen) << "adding" << screen << "(Primary:" << screen->isPrimary() << ")"; QWindowSystemInterface::handleScreenAdded(screen, screen->isPrimary()); } } if (!m_screens.isEmpty()) - qCDebug(lcQpaScreen) << "initializeScreens: primary output is" << qAsConst(m_screens).first()->name(); + qCDebug(lcQpaScreen) << "initializeScreens: primary output is" << std::as_const(m_screens).first()->name(); } } @@ -489,6 +489,10 @@ void QXcbConnection::initializeScreensFromMonitor(xcb_screen_iterator_t *it, int virtualDesktop = new QXcbVirtualDesktop(this, xcbScreen, xcbScreenNumber); m_virtualDesktops.append(virtualDesktop); } + + if (xcbScreenNumber != primaryScreenNumber()) + return; + QList<QPlatformScreen*> old = virtualDesktop->m_screens; QList<QPlatformScreen *> siblings; @@ -511,24 +515,22 @@ void QXcbConnection::initializeScreensFromMonitor(xcb_screen_iterator_t *it, int screen = findScreenForMonitorInfo(old, monitor_info); if (!screen) { screen = createScreen_monitor(virtualDesktop, monitor_info, monitors_r->timestamp); - QHighDpiScaling::updateHighDpiScaling(); } else { updateScreen_monitor(screen, monitor_info, monitors_r->timestamp); old.removeAll(screen); } } - m_screens << screen; + if (!m_screens.contains(screen)) + m_screens << screen; siblings << screen; // similar logic with QXcbConnection::initializeScreensFromOutput() - if (primaryScreenNumber() == xcbScreenNumber) { - if (!(*primaryScreen) || monitor_info->primary) { - if (*primaryScreen) - (*primaryScreen)->setPrimary(false); - *primaryScreen = screen; - (*primaryScreen)->setPrimary(true); - siblings.prepend(siblings.takeLast()); - } + if (!(*primaryScreen) || monitor_info->primary) { + if (*primaryScreen) + (*primaryScreen)->setPrimary(false); + *primaryScreen = screen; + (*primaryScreen)->setPrimary(true); + siblings.prepend(siblings.takeLast()); } xcb_randr_monitor_info_next(&monitor_iter); @@ -551,10 +553,8 @@ void QXcbConnection::initializeScreensFromMonitor(xcb_screen_iterator_t *it, int qCDebug(lcQpaScreen) << "create a fake screen: " << screen; } - if (primaryScreenNumber() == xcbScreenNumber) { - *primaryScreen = screen; - (*primaryScreen)->setPrimary(true); - } + *primaryScreen = screen; + (*primaryScreen)->setPrimary(true); siblings << screen; m_screens << screen; diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index 8cb4e5d603..4f62a1880b 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -15,7 +15,9 @@ #include <xcb/xinput.h> +#if QT_CONFIG(gestures) #define QT_XCB_HAS_TOUCHPAD_GESTURES (XCB_INPUT_MINOR_VERSION >= 4) +#endif using namespace Qt::StringLiterals; @@ -226,7 +228,7 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting, auto *deviceInfo = reinterpret_cast<xcb_input_xi_device_info_t *>(info); if (removeExisting) { #if QT_CONFIG(tabletevent) - for (int i = 0; i < m_tabletData.count(); ++i) { + for (int i = 0; i < m_tabletData.size(); ++i) { if (m_tabletData.at(i).deviceId == deviceInfo->deviceid) { m_tabletData.remove(i); break; @@ -239,6 +241,7 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting, const QByteArray nameRaw = QByteArray(xcb_input_xi_device_info_name(deviceInfo), xcb_input_xi_device_info_name_length(deviceInfo)); const QString name = QString::fromUtf8(nameRaw); + m_xiSlavePointerIds.append(deviceInfo->deviceid); qCDebug(lcQpaXInputDevices) << "input device " << name << "ID" << deviceInfo->deviceid; #if QT_CONFIG(tabletevent) TabletData tabletData; @@ -270,9 +273,9 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting, tabletData.valuatorInfo[valuatorAtom] = info; } #endif // QT_CONFIG(tabletevent) - if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel) + if (valuatorAtom == QXcbAtom::AtomRelHorizScroll || valuatorAtom == QXcbAtom::AtomRelHorizWheel) scrollingDevice()->lastScrollPosition.setX(fixed3232ToReal(vci->value)); - else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel) + else if (valuatorAtom == QXcbAtom::AtomRelVertScroll || valuatorAtom == QXcbAtom::AtomRelVertWheel) scrollingDevice()->lastScrollPosition.setY(fixed3232ToReal(vci->value)); break; } @@ -300,14 +303,14 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting, xcb_atom_t label5 = labels[4]; // Some drivers have no labels on the wheel buttons, some have no label on just one and some have no label on // button 4 and the wrong one on button 5. So we just check that they are not labelled with unrelated buttons. - if ((!label4 || qatom(label4) == QXcbAtom::ButtonWheelUp || qatom(label4) == QXcbAtom::ButtonWheelDown) && - (!label5 || qatom(label5) == QXcbAtom::ButtonWheelUp || qatom(label5) == QXcbAtom::ButtonWheelDown)) + if ((!label4 || qatom(label4) == QXcbAtom::AtomButtonWheelUp || qatom(label4) == QXcbAtom::AtomButtonWheelDown) && + (!label5 || qatom(label5) == QXcbAtom::AtomButtonWheelUp || qatom(label5) == QXcbAtom::AtomButtonWheelDown)) scrollingDevice()->legacyOrientations |= Qt::Vertical; } if (bci->num_buttons >= 7) { xcb_atom_t label6 = labels[5]; xcb_atom_t label7 = labels[6]; - if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight)) + if ((!label6 || qatom(label6) == QXcbAtom::AtomButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::AtomButtonHorizWheelRight)) scrollingDevice()->legacyOrientations |= Qt::Horizontal; } buttonCount = bci->num_buttons; @@ -331,9 +334,9 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting, bool isTablet = false; #if QT_CONFIG(tabletevent) // If we have found the valuators which we expect a tablet to have, it might be a tablet. - if (tabletData.valuatorInfo.contains(QXcbAtom::AbsX) && - tabletData.valuatorInfo.contains(QXcbAtom::AbsY) && - tabletData.valuatorInfo.contains(QXcbAtom::AbsPressure)) + if (tabletData.valuatorInfo.contains(QXcbAtom::AtomAbsX) && + tabletData.valuatorInfo.contains(QXcbAtom::AtomAbsY) && + tabletData.valuatorInfo.contains(QXcbAtom::AtomAbsPressure)) isTablet = true; // But we need to be careful not to take the touch and tablet-button devices as tablets. @@ -356,7 +359,7 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting, // combined device (evdev) rather than separate pen/eraser (wacom driver) tabletData.pointerType = QPointingDevice::PointerType::Pen; dbgType = "pen"_L1; - } else if (nameLower.contains("aiptek") /* && device == QXcbAtom::KEYBOARD */) { + } else if (nameLower.contains("aiptek") /* && device == QXcbAtom::AtomKEYBOARD */) { // some "Genius" tablets isTablet = true; tabletData.pointerType = QPointingDevice::PointerType::Pen; @@ -384,9 +387,9 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting, m_tabletData.append(tabletData); qCDebug(lcQpaXInputDevices) << " it's a tablet with pointer type" << dbgType; QPointingDevice::Capabilities capsOverride = QInputDevice::Capability::None; - if (tabletData.valuatorInfo.contains(QXcbAtom::AbsTiltX)) + if (tabletData.valuatorInfo.contains(QXcbAtom::AtomAbsTiltX)) capsOverride.setFlag(QInputDevice::Capability::XTilt); - if (tabletData.valuatorInfo.contains(QXcbAtom::AbsTiltY)) + if (tabletData.valuatorInfo.contains(QXcbAtom::AtomAbsTiltY)) capsOverride.setFlag(QInputDevice::Capability::YTilt); // TODO can we get USB ID? Q_ASSERT(deviceInfo->deviceid == tabletData.deviceId); @@ -442,12 +445,15 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting, } } +/*! + Find all X11 input devices at startup, or react to a device hierarchy event, + and create/delete the corresponding QInputDevice instances as necessary. + Afterwards, we expect QInputDevice::devices() to contain only the + Qt-relevant devices that \c {xinput list} reports. The parent of each master + device is this QXcbConnection object; the parent of each slave is its master. +*/ void QXcbConnection::xi2SetupDevices() { -#if QT_CONFIG(tabletevent) - m_tabletData.clear(); -#endif - m_touchDevices.clear(); m_xiMasterPointerIds.clear(); auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), XCB_INPUT_DEVICE_ALL); @@ -456,6 +462,18 @@ void QXcbConnection::xi2SetupDevices() return; } + // Start with all known devices; remove the ones that still exist. + // Afterwards, previousDevices will be the list of those that we should delete. + QList<const QInputDevice *> previousDevices = QInputDevice::devices(); + // Return true if the device with the given systemId is new; + // otherwise remove it from previousDevices and return false. + auto newOrKeep = [&previousDevices](qint64 systemId) { + // if nothing is removed from previousDevices, it's a new device + return !previousDevices.removeIf([systemId](const QInputDevice *dev) { + return dev->systemId() == systemId; + }); + }; + // XInput doesn't provide a way to identify "seats"; but each device has an attachment to another device. // So we make up a seatId: master-keyboard-id << 16 | master-pointer-id. @@ -464,17 +482,21 @@ void QXcbConnection::xi2SetupDevices() xcb_input_xi_device_info_t *deviceInfo = it.data; switch (deviceInfo->type) { case XCB_INPUT_DEVICE_TYPE_MASTER_KEYBOARD: { - auto dev = new QInputDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), - deviceInfo->deviceid, QInputDevice::DeviceType::Keyboard, - QString::number(deviceInfo->deviceid << 16 | deviceInfo->attachment, 16), this); - QWindowSystemInterface::registerInputDevice(dev); + if (newOrKeep(deviceInfo->deviceid)) { + auto dev = new QInputDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), + deviceInfo->deviceid, QInputDevice::DeviceType::Keyboard, + QString::number(deviceInfo->deviceid << 16 | deviceInfo->attachment, 16), this); + QWindowSystemInterface::registerInputDevice(dev); + } } break; case XCB_INPUT_DEVICE_TYPE_MASTER_POINTER: { m_xiMasterPointerIds.append(deviceInfo->deviceid); - auto dev = new QXcbScrollingDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid, - QInputDevice::Capability::Position | QInputDevice::Capability::Scroll | QInputDevice::Capability::Hover, - 32, QString::number(deviceInfo->attachment << 16 | deviceInfo->deviceid, 16), this); - QWindowSystemInterface::registerInputDevice(dev); + if (newOrKeep(deviceInfo->deviceid)) { + auto dev = new QXcbScrollingDevice(QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid, + QInputDevice::Capability::Position | QInputDevice::Capability::Scroll | QInputDevice::Capability::Hover, + 32, QString::number(deviceInfo->attachment << 16 | deviceInfo->deviceid, 16), this); + QWindowSystemInterface::registerInputDevice(dev); + } continue; } break; default: @@ -491,23 +513,36 @@ void QXcbConnection::xi2SetupDevices() // already registered break; case XCB_INPUT_DEVICE_TYPE_SLAVE_POINTER: { - m_xiSlavePointerIds.append(deviceInfo->deviceid); - QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment)); - Q_ASSERT(master); - xi2SetupSlavePointerDevice(deviceInfo, false, qobject_cast<QPointingDevice *>(master)); + if (newOrKeep(deviceInfo->deviceid)) { + m_xiSlavePointerIds.append(deviceInfo->deviceid); + QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment)); + Q_ASSERT(master); + xi2SetupSlavePointerDevice(deviceInfo, false, qobject_cast<QPointingDevice *>(master)); + } } break; case XCB_INPUT_DEVICE_TYPE_SLAVE_KEYBOARD: { - QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment)); - Q_ASSERT(master); - QWindowSystemInterface::registerInputDevice(new QInputDevice( - QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid, - QInputDevice::DeviceType::Keyboard, master->seatName(), master)); + if (newOrKeep(deviceInfo->deviceid)) { + QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment)); + Q_ASSERT(master); + QWindowSystemInterface::registerInputDevice(new QInputDevice( + QString::fromUtf8(xcb_input_xi_device_info_name(deviceInfo)), deviceInfo->deviceid, + QInputDevice::DeviceType::Keyboard, master->seatName(), master)); + } } break; case XCB_INPUT_DEVICE_TYPE_FLOATING_SLAVE: break; } } + // previousDevices is now the list of those that are no longer found + qCDebug(lcQpaXInputDevices) << "removed" << previousDevices; + for (auto it = previousDevices.constBegin(); it != previousDevices.constEnd(); ++it) { + const auto id = (*it)->systemId(); + m_xiSlavePointerIds.removeAll(id); + m_touchDevices.remove(id); + } + qDeleteAll(previousDevices); + if (m_xiMasterPointerIds.size() > 1) qCDebug(lcQpaXInputDevices) << "multi-pointer X detected"; } @@ -573,27 +608,27 @@ QXcbConnection::TouchDeviceData *QXcbConnection::populateTouchDevices(void *info // Some devices (mice) report a resolution of 0; they will be excluded later, // for now just prevent a division by zero const int vciResolution = vci->resolution ? vci->resolution : 1; - if (valuatorAtom == QXcbAtom::AbsMTPositionX) + if (valuatorAtom == QXcbAtom::AtomAbsMTPositionX) caps |= QInputDevice::Capability::Position | QInputDevice::Capability::NormalizedPosition; - else if (valuatorAtom == QXcbAtom::AbsMTTouchMajor) + else if (valuatorAtom == QXcbAtom::AtomAbsMTTouchMajor) caps |= QInputDevice::Capability::Area; - else if (valuatorAtom == QXcbAtom::AbsMTOrientation) + else if (valuatorAtom == QXcbAtom::AtomAbsMTOrientation) dev.providesTouchOrientation = true; - else if (valuatorAtom == QXcbAtom::AbsMTPressure || valuatorAtom == QXcbAtom::AbsPressure) + else if (valuatorAtom == QXcbAtom::AtomAbsMTPressure || valuatorAtom == QXcbAtom::AtomAbsPressure) caps |= QInputDevice::Capability::Pressure; - else if (valuatorAtom == QXcbAtom::RelX) { + else if (valuatorAtom == QXcbAtom::AtomRelX) { hasRelativeCoords = true; dev.size.setWidth((fixed3232ToReal(vci->max) - fixed3232ToReal(vci->min)) * 1000.0 / vciResolution); - } else if (valuatorAtom == QXcbAtom::RelY) { + } else if (valuatorAtom == QXcbAtom::AtomRelY) { hasRelativeCoords = true; dev.size.setHeight((fixed3232ToReal(vci->max) - fixed3232ToReal(vci->min)) * 1000.0 / vciResolution); - } else if (valuatorAtom == QXcbAtom::AbsX) { + } else if (valuatorAtom == QXcbAtom::AtomAbsX) { caps |= QInputDevice::Capability::Position; dev.size.setWidth((fixed3232ToReal(vci->max) - fixed3232ToReal(vci->min)) * 1000.0 / vciResolution); - } else if (valuatorAtom == QXcbAtom::AbsY) { + } else if (valuatorAtom == QXcbAtom::AtomAbsY) { caps |= QInputDevice::Capability::Position; dev.size.setHeight((fixed3232ToReal(vci->max) - fixed3232ToReal(vci->min)) * 1000.0 / vciResolution); - } else if (valuatorAtom == QXcbAtom::RelVertWheel || valuatorAtom == QXcbAtom::RelHorizWheel) { + } else if (valuatorAtom == QXcbAtom::AtomRelVertWheel || valuatorAtom == QXcbAtom::AtomRelHorizWheel) { caps |= QInputDevice::Capability::Scroll; } break; @@ -651,7 +686,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) { auto *xiEvent = reinterpret_cast<qt_xcb_input_device_event_t *>(event); setTime(xiEvent->time); - if (m_xiSlavePointerIds.contains(xiEvent->deviceid)) { + if (m_xiSlavePointerIds.contains(xiEvent->deviceid) && xiEvent->event_type != XCB_INPUT_PROPERTY) { if (!m_duringSystemMoveResize) return; if (xiEvent->event == XCB_NONE) @@ -730,6 +765,8 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) if (auto device = QPointingDevicePrivate::pointingDeviceById(sourceDeviceId)) xi2HandleScrollEvent(event, device); + else + qCDebug(lcQpaXInputEvents) << "scroll event from unregistered device" << sourceDeviceId; if (xiDeviceEvent) { switch (xiDeviceEvent->event_type) { @@ -776,7 +813,16 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo { auto *xiDeviceEvent = reinterpret_cast<xcb_input_touch_begin_event_t *>(xiDevEvent); TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid); - Q_ASSERT(dev); + if (!dev) { + qCDebug(lcQpaXInputEvents) << "didn't find the dev for given sourceid - " << xiDeviceEvent->sourceid + << ", try to repopulate xi2 devices"; + xi2SetupDevices(); + dev = touchDeviceForId(xiDeviceEvent->sourceid); + if (!dev) { + qCDebug(lcQpaXInputEvents) << "still can't find the dev for it, give up."; + return; + } + } const bool firstTouch = dev->touchPoints.isEmpty(); if (xiDeviceEvent->event_type == XCB_INPUT_TOUCH_BEGIN) { QWindowSystemInterface::TouchPoint tp; @@ -792,7 +838,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo qreal nx = -1.0, ny = -1.0; qreal w = 0.0, h = 0.0; bool majorAxisIsY = touchPoint.area.height() > touchPoint.area.width(); - for (const TouchDeviceData::ValuatorClassInfo &vci : qAsConst(dev->valuatorInfo)) { + for (const TouchDeviceData::ValuatorClassInfo &vci : std::as_const(dev->valuatorInfo)) { double value; if (!xi2GetValuatorValueIfSet(xiDeviceEvent, vci.number, &value)) continue; @@ -804,27 +850,27 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo if (value < vci.min) value = vci.min; qreal valuatorNormalized = (value - vci.min) / (vci.max - vci.min); - if (vci.label == QXcbAtom::RelX) { + if (vci.label == QXcbAtom::AtomRelX) { nx = valuatorNormalized; - } else if (vci.label == QXcbAtom::RelY) { + } else if (vci.label == QXcbAtom::AtomRelY) { ny = valuatorNormalized; - } else if (vci.label == QXcbAtom::AbsX) { + } else if (vci.label == QXcbAtom::AtomAbsX) { nx = valuatorNormalized; - } else if (vci.label == QXcbAtom::AbsY) { + } else if (vci.label == QXcbAtom::AtomAbsY) { ny = valuatorNormalized; - } else if (vci.label == QXcbAtom::AbsMTPositionX) { + } else if (vci.label == QXcbAtom::AtomAbsMTPositionX) { nx = valuatorNormalized; - } else if (vci.label == QXcbAtom::AbsMTPositionY) { + } else if (vci.label == QXcbAtom::AtomAbsMTPositionY) { ny = valuatorNormalized; - } else if (vci.label == QXcbAtom::AbsMTTouchMajor) { + } else if (vci.label == QXcbAtom::AtomAbsMTTouchMajor) { const qreal sw = screen->geometry().width(); const qreal sh = screen->geometry().height(); w = valuatorNormalized * qHypot(sw, sh); - } else if (vci.label == QXcbAtom::AbsMTTouchMinor) { + } else if (vci.label == QXcbAtom::AtomAbsMTTouchMinor) { const qreal sw = screen->geometry().width(); const qreal sh = screen->geometry().height(); h = valuatorNormalized * qHypot(sw, sh); - } else if (vci.label == QXcbAtom::AbsMTOrientation) { + } else if (vci.label == QXcbAtom::AtomAbsMTOrientation) { // Find the closest axis. // 0 corresponds to the Y axis, vci.max to the X axis. // Flipping over the Y axis and rotating by 180 degrees @@ -835,7 +881,7 @@ void QXcbConnection::xi2ProcessTouch(void *xiDevEvent, QXcbWindow *platformWindo value -= 2 * vci.max; value = qAbs(value); majorAxisIsY = value < vci.max - value; - } else if (vci.label == QXcbAtom::AbsMTPressure || vci.label == QXcbAtom::AbsPressure) { + } else if (vci.label == QXcbAtom::AtomAbsMTPressure || vci.label == QXcbAtom::AtomAbsPressure) { touchPoint.pressure = valuatorNormalized; } @@ -967,7 +1013,7 @@ void QXcbConnection::abortSystemMoveResize(xcb_window_t window) qCDebug(lcQpaXInputDevices) << "sending client message NET_WM_MOVERESIZE_CANCEL to window: " << window; m_startSystemMoveResizeInfo.window = XCB_NONE; - const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE); + const xcb_atom_t moveResize = connection()->atom(QXcbAtom::Atom_NET_WM_MOVERESIZE); xcb_client_message_event_t xev; xev.response_type = XCB_CLIENT_MESSAGE; xev.type = moveResize; @@ -1023,7 +1069,7 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab) } #endif // QT_CONFIG(gestures) && QT_XCB_HAS_TOUCHPAD_GESTURES - for (int id : qAsConst(m_xiMasterPointerIds)) { + for (int id : std::as_const(m_xiMasterPointerIds)) { xcb_generic_error_t *error = nullptr; auto cookie = xcb_input_xi_grab_device(xcb_connection(), w, XCB_CURRENT_TIME, XCB_CURSOR_NONE, id, XCB_INPUT_GRAB_MODE_22_ASYNC, XCB_INPUT_GRAB_MODE_22_ASYNC, @@ -1041,7 +1087,7 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab) free(reply); } } else { // ungrab - for (int id : qAsConst(m_xiMasterPointerIds)) { + for (int id : std::as_const(m_xiMasterPointerIds)) { auto cookie = xcb_input_xi_ungrab_device_checked(xcb_connection(), XCB_CURRENT_TIME, id); xcb_generic_error_t *error = xcb_request_check(xcb_connection(), cookie); if (error) { @@ -1065,11 +1111,15 @@ bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab) void QXcbConnection::xi2HandleHierarchyEvent(void *event) { auto *xiEvent = reinterpret_cast<xcb_input_hierarchy_event_t *>(event); - // We only care about hotplugged devices - if (!(xiEvent->flags & (XCB_INPUT_HIERARCHY_MASK_SLAVE_REMOVED | XCB_INPUT_HIERARCHY_MASK_SLAVE_ADDED))) - return; - - xi2SetupDevices(); + // We care about hotplugged devices (slaves) and master devices. + // We don't report anything for DEVICE_ENABLED or DEVICE_DISABLED + // (but often that goes with adding or removal anyway). + // We don't react to SLAVE_ATTACHED or SLAVE_DETACHED either. + if (xiEvent->flags & (XCB_INPUT_HIERARCHY_MASK_MASTER_ADDED | + XCB_INPUT_HIERARCHY_MASK_MASTER_REMOVED | + XCB_INPUT_HIERARCHY_MASK_SLAVE_REMOVED | + XCB_INPUT_HIERARCHY_MASK_SLAVE_ADDED)) + xi2SetupDevices(); } #if QT_XCB_HAS_TOUCHPAD_GESTURES @@ -1234,6 +1284,9 @@ 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: { + // Don't call xi2SetupSlavePointerDevice() again for an already-known device, and never for a master. + if (m_xiMasterPointerIds.contains(xiEvent->deviceid) || m_xiSlavePointerIds.contains(xiEvent->deviceid)) + return; auto reply = Q_XCB_REPLY(xcb_input_xi_query_device, xcb_connection(), xiEvent->sourceid); if (!reply || reply->num_infos <= 0) return; @@ -1275,9 +1328,9 @@ void QXcbConnection::xi2UpdateScrollingDevice(QInputDevice *dev) if (classInfo->type == XCB_INPUT_DEVICE_CLASS_TYPE_VALUATOR) { auto *vci = reinterpret_cast<xcb_input_valuator_class_t *>(classInfo); const int valuatorAtom = qatom(vci->label); - if (valuatorAtom == QXcbAtom::RelHorizScroll || valuatorAtom == QXcbAtom::RelHorizWheel) + if (valuatorAtom == QXcbAtom::AtomRelHorizScroll || valuatorAtom == QXcbAtom::AtomRelHorizWheel) scrollingDevice->lastScrollPosition.setX(fixed3232ToReal(vci->value)); - else if (valuatorAtom == QXcbAtom::RelVertScroll || valuatorAtom == QXcbAtom::RelVertWheel) + else if (valuatorAtom == QXcbAtom::AtomRelVertScroll || valuatorAtom == QXcbAtom::AtomRelVertWheel) scrollingDevice->lastScrollPosition.setY(fixed3232ToReal(vci->value)); } } @@ -1464,7 +1517,7 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD // The evdev driver doesn't do it this way. const auto *ev = reinterpret_cast<const xcb_input_property_event_t *>(event); if (ev->what == XCB_INPUT_PROPERTY_FLAG_MODIFIED) { - if (ev->property == atom(QXcbAtom::WacomSerialIDs)) { + if (ev->property == atom(QXcbAtom::AtomWacomSerialIDs)) { enum WacomSerialIndex { _WACSER_USB_ID = 0, _WACSER_LAST_TOOL_SERIAL, @@ -1477,7 +1530,7 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD 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) { + if (reply->type == atom(QXcbAtom::AtomINTEGER) && reply->format == 32 && reply->num_items == _WACSER_COUNT) { quint32 *ptr = reinterpret_cast<quint32 *>(xcb_input_xi_get_property_items(reply.get())); quint32 tool = ptr[_WACSER_TOOL_ID]; // Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/ @@ -1485,6 +1538,7 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD if (!tool && ptr[_WACSER_TOOL_SERIAL]) tool = ptr[_WACSER_TOOL_SERIAL]; + QWindow *win = nullptr; // TODO QTBUG-111400 get the position somehow, then the window // The property change event informs us which tool is in proximity or which one left proximity. if (tool) { const QPointingDevice *dev = tabletToolInstance(nullptr, tabletData->name, @@ -1493,30 +1547,26 @@ bool QXcbConnection::xi2HandleTabletEvent(const void *event, TabletData *tabletD tabletData->inProximity = true; tabletData->tool = dev->type(); tabletData->serialId = qint64(ptr[_WACSER_TOOL_SERIAL]); - QWindowSystemInterface::handleTabletEnterProximityEvent(ev->time, - int(tabletData->tool), int(tabletData->pointerType), tabletData->serialId); + QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(win, ev->time, dev, true); // enter } else { tool = ptr[_WACSER_LAST_TOOL_ID]; // Workaround for http://sourceforge.net/p/linuxwacom/bugs/246/ // e.g. on Thinkpad Helix, tool ID will be 0 and serial will be 1 if (!tool) tool = ptr[_WACSER_LAST_TOOL_SERIAL]; - const QInputDevice *dev = QInputDevicePrivate::fromId(tabletData->deviceId); + auto *dev = qobject_cast<const QPointingDevice *>(QInputDevicePrivate::fromId(tabletData->deviceId)); Q_ASSERT(dev); tabletData->tool = dev->type(); tabletData->inProximity = false; tabletData->serialId = qint64(ptr[_WACSER_LAST_TOOL_SERIAL]); - // TODO why doesn't it just take QPointingDevice* - QWindowSystemInterface::handleTabletLeaveProximityEvent(ev->time, - int(tabletData->tool), int(tabletData->pointerType), tabletData->serialId); + QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(win, ev->time, dev, false); // leave } // TODO maybe have a hash of tabletData->deviceId to device data so we can // look up the tablet name here, and distinguish multiple tablets - if (Q_UNLIKELY(lcQpaXInputEvents().isDebugEnabled())) - qCDebug(lcQpaXInputDevices, "XI2 proximity change on tablet %d %s (USB %x): last tool: %x id %x current tool: %x id %x %s", - tabletData->deviceId, qPrintable(tabletData->name), ptr[_WACSER_USB_ID], - ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID], - ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], toolName(tabletData->tool)); + qCDebug(lcQpaXInputDevices, "XI2 proximity change on tablet %d %s (USB %x): last tool: %x id %x current tool: %x id %x %s", + tabletData->deviceId, qPrintable(tabletData->name), ptr[_WACSER_USB_ID], + ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID], + ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], toolName(tabletData->tool)); } } } @@ -1550,6 +1600,9 @@ void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletD double pressure = 0, rotation = 0, tangentialPressure = 0; int xTilt = 0, yTilt = 0; static const bool useValuators = !qEnvironmentVariableIsSet("QT_XCB_TABLET_LEGACY_COORDINATES"); + const QPointingDevice *dev = QPointingDevicePrivate::tabletDevice(QInputDevice::DeviceType(tabletData->tool), + QPointingDevice::PointerType(tabletData->pointerType), + QPointingDeviceUniqueId::fromNumericId(tabletData->serialId)); // Valuators' values are relative to the physical size of the current virtual // screen. Therefore we cannot use QScreen/QWindow geometry and should use @@ -1568,36 +1621,37 @@ void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletD xi2GetValuatorValueIfSet(event, classInfo.number, &classInfo.curVal); double normalizedValue = (classInfo.curVal - classInfo.minVal) / (classInfo.maxVal - classInfo.minVal); switch (valuator) { - case QXcbAtom::AbsX: + case QXcbAtom::AtomAbsX: if (Q_LIKELY(useValuators)) { const qreal value = scaleOneValuator(normalizedValue, physicalScreenArea.x(), physicalScreenArea.width()); global.setX(value); local.setX(xcbWindow->mapFromGlobalF(global).x()); } break; - case QXcbAtom::AbsY: + case QXcbAtom::AtomAbsY: if (Q_LIKELY(useValuators)) { qreal value = scaleOneValuator(normalizedValue, physicalScreenArea.y(), physicalScreenArea.height()); global.setY(value); local.setY(xcbWindow->mapFromGlobalF(global).y()); } break; - case QXcbAtom::AbsPressure: + case QXcbAtom::AtomAbsPressure: pressure = normalizedValue; break; - case QXcbAtom::AbsTiltX: + case QXcbAtom::AtomAbsTiltX: xTilt = classInfo.curVal; break; - case QXcbAtom::AbsTiltY: + case QXcbAtom::AtomAbsTiltY: yTilt = classInfo.curVal; break; - case QXcbAtom::AbsWheel: + case QXcbAtom::AtomAbsWheel: switch (tabletData->tool) { case QInputDevice::DeviceType::Airbrush: tangentialPressure = normalizedValue * 2.0 - 1.0; // Convert 0..1 range to -1..+1 range break; case QInputDevice::DeviceType::Stylus: - rotation = normalizedValue * 360.0 - 180.0; // Convert 0..1 range to -180..+180 degrees + if (dev->capabilities().testFlag(QInputDevice::Capability::Rotation)) + rotation = normalizedValue * 360.0 - 180.0; // Convert 0..1 range to -180..+180 degrees break; default: // Other types of styli do not use this valuator break; @@ -1616,16 +1670,15 @@ void QXcbConnection::xi2ReportTabletEvent(const void *event, TabletData *tabletD local.x(), local.y(), global.x(), global.y(), (int)tabletData->buttons, pressure, xTilt, yTilt, rotation, (int)modifiers); - QWindowSystemInterface::handleTabletEvent(window, ev->time, local, global, - int(tabletData->tool), int(tabletData->pointerType), + QWindowSystemInterface::handleTabletEvent(window, ev->time, dev, local, global, tabletData->buttons, pressure, xTilt, yTilt, tangentialPressure, - rotation, 0, tabletData->serialId, modifiers); + rotation, 0, modifiers); } QXcbConnection::TabletData *QXcbConnection::tabletDataForDevice(int id) { - for (int i = 0; i < m_tabletData.count(); ++i) { + for (int i = 0; i < m_tabletData.size(); ++i) { if (m_tabletData.at(i).deviceId == id) return &m_tabletData[i]; } diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index abda9c9d0a..dc9ed46956 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qxcbcursor.h" @@ -7,13 +7,17 @@ #include "qxcbimage.h" #include "qxcbxsettings.h" -#if QT_CONFIG(library) -#include <QtCore/QLibrary> -#endif #include <QtGui/QWindow> #include <QtGui/QBitmap> #include <QtGui/private/qguiapplication_p.h> +#include <qpa/qplatformtheme.h> + +#if QT_CONFIG(xcb_xlib) #include <X11/cursorfont.h> +#else +#include "qxcbcursorfont.h" +#endif + #include <xcb/xfixes.h> #include <xcb/xcb_image.h> @@ -21,24 +25,6 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -typedef int (*PtrXcursorLibraryLoadCursor)(void *, const char *); -typedef char *(*PtrXcursorLibraryGetTheme)(void *); -typedef int (*PtrXcursorLibrarySetTheme)(void *, const char *); -typedef int (*PtrXcursorLibraryGetDefaultSize)(void *); - -#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library) -#include <X11/Xlib.h> -enum { - XCursorShape = CursorShape -}; -#undef CursorShape - -static PtrXcursorLibraryLoadCursor ptrXcursorLibraryLoadCursor = nullptr; -static PtrXcursorLibraryGetTheme ptrXcursorLibraryGetTheme = nullptr; -static PtrXcursorLibrarySetTheme ptrXcursorLibrarySetTheme = nullptr; -static PtrXcursorLibraryGetDefaultSize ptrXcursorLibraryGetDefaultSize = nullptr; -#endif - static xcb_font_t cursorFont = 0; static int cursorCount = 0; @@ -255,10 +241,10 @@ QXcbCursorCacheKey::QXcbCursorCacheKey(const QCursor &c) if (pixmapCacheKey) { bitmapCacheKey = pixmapCacheKey; } else { - Q_ASSERT(!c.bitmap(Qt::ReturnByValue).isNull()); - Q_ASSERT(!c.mask(Qt::ReturnByValue).isNull()); - bitmapCacheKey = c.bitmap(Qt::ReturnByValue).cacheKey(); - maskCacheKey = c.mask(Qt::ReturnByValue).cacheKey(); + Q_ASSERT(!c.bitmap().isNull()); + Q_ASSERT(!c.mask().isNull()); + bitmapCacheKey = c.bitmap().cacheKey(); + maskCacheKey = c.mask().cacheKey(); } } } @@ -266,50 +252,28 @@ QXcbCursorCacheKey::QXcbCursorCacheKey(const QCursor &c) #endif // !QT_NO_CURSOR QXcbCursor::QXcbCursor(QXcbConnection *conn, QXcbScreen *screen) - : QXcbObject(conn), m_screen(screen), m_gtkCursorThemeInitialized(false) + : QXcbObject(conn), m_screen(screen), m_cursorContext(nullptr), m_callbackForPropertyRegistered(false) { #if QT_CONFIG(cursor) // see NUM_BITMAPS in libXcursor/src/xcursorint.h m_bitmapCache.setMaxCost(8); #endif + updateContext(); + if (cursorCount++) return; cursorFont = xcb_generate_id(xcb_connection()); const char *cursorStr = "cursor"; xcb_open_font(xcb_connection(), cursorFont, strlen(cursorStr), cursorStr); - -#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library) - static bool function_ptrs_not_initialized = true; - if (function_ptrs_not_initialized) { - QLibrary xcursorLib("Xcursor"_L1, 1); - bool xcursorFound = xcursorLib.load(); - if (!xcursorFound) { // try without the version number - xcursorLib.setFileName("Xcursor"_L1); - xcursorFound = xcursorLib.load(); - } - if (xcursorFound) { - ptrXcursorLibraryLoadCursor = - (PtrXcursorLibraryLoadCursor) xcursorLib.resolve("XcursorLibraryLoadCursor"); - ptrXcursorLibraryGetTheme = - (PtrXcursorLibraryGetTheme) xcursorLib.resolve("XcursorGetTheme"); - ptrXcursorLibrarySetTheme = - (PtrXcursorLibrarySetTheme) xcursorLib.resolve("XcursorSetTheme"); - ptrXcursorLibraryGetDefaultSize = - (PtrXcursorLibraryGetDefaultSize) xcursorLib.resolve("XcursorGetDefaultSize"); - } - function_ptrs_not_initialized = false; - } - -#endif } QXcbCursor::~QXcbCursor() { xcb_connection_t *conn = xcb_connection(); - if (m_gtkCursorThemeInitialized) { + if (m_callbackForPropertyRegistered) { m_screen->xSettings()->removeCallbackForHandle(this); } @@ -317,9 +281,33 @@ QXcbCursor::~QXcbCursor() xcb_close_font(conn, cursorFont); #ifndef QT_NO_CURSOR - for (xcb_cursor_t cursor : qAsConst(m_cursorHash)) + for (xcb_cursor_t cursor : std::as_const(m_cursorHash)) xcb_free_cursor(conn, cursor); #endif + + if (m_cursorContext) + xcb_cursor_context_free(m_cursorContext); +} + +QSize QXcbCursor::size() const +{ + if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) + return theme->themeHint(QPlatformTheme::MouseCursorSize).toSize(); + return QSize(24, 24); +} + +void QXcbCursor::updateContext() +{ + if (m_cursorContext) + xcb_cursor_context_free(m_cursorContext); + + m_cursorContext = nullptr; + + xcb_connection_t *conn = xcb_connection(); + if (xcb_cursor_context_new(conn, m_screen->screen(), &m_cursorContext) < 0) { + qWarning() << "xcb: Could not initialize xcb-cursor"; + m_cursorContext = nullptr; + } } #ifndef QT_NO_CURSOR @@ -482,76 +470,39 @@ xcb_cursor_t QXcbCursor::createNonStandardCursor(int cshape) return cursor; } -#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library) -bool updateCursorTheme(void *dpy, const QByteArray &theme) { - if (!ptrXcursorLibraryGetTheme - || !ptrXcursorLibrarySetTheme) - return false; - QByteArray oldTheme = ptrXcursorLibraryGetTheme(dpy); - if (oldTheme == theme) - return false; - - int setTheme = ptrXcursorLibrarySetTheme(dpy,theme.constData()); - return setTheme; -} - - void QXcbCursor::cursorThemePropertyChanged(QXcbVirtualDesktop *screen, const QByteArray &name, const QVariant &property, void *handle) +void QXcbCursor::cursorThemePropertyChanged(QXcbVirtualDesktop *screen, const QByteArray &name, const QVariant &property, void *handle) { Q_UNUSED(screen); Q_UNUSED(name); + Q_UNUSED(property); QXcbCursor *self = static_cast<QXcbCursor *>(handle); self->m_cursorHash.clear(); - - updateCursorTheme(self->connection()->xlib_display(),property.toByteArray()); + self->updateContext(); } -static xcb_cursor_t loadCursor(void *dpy, int cshape) -{ - xcb_cursor_t cursor = XCB_NONE; - if (!ptrXcursorLibraryLoadCursor || !dpy) - return cursor; - - for (const char *cursorName: cursorNames[cshape]) { - cursor = ptrXcursorLibraryLoadCursor(dpy, cursorName); - if (cursor != XCB_NONE) - break; - } - - return cursor; -} -#endif // QT_CONFIG(xcb_xlib) / QT_CONFIG(library) - xcb_cursor_t QXcbCursor::createFontCursor(int cshape) { + if (!m_cursorContext) + return XCB_NONE; + xcb_connection_t *conn = xcb_connection(); int cursorId = cursorIdForShape(cshape); xcb_cursor_t cursor = XCB_NONE; -#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library) - if (m_screen->xSettings()->initialized()) - m_screen->xSettings()->registerCallbackForProperty("Gtk/CursorThemeName",cursorThemePropertyChanged,this); + if (!m_callbackForPropertyRegistered && m_screen->xSettings()->initialized()) { + m_screen->xSettings()->registerCallbackForProperty("Gtk/CursorThemeName", cursorThemePropertyChanged, this); + + m_callbackForPropertyRegistered = true; + } - // Try Xcursor first + // Try xcb-cursor first if (cshape >= 0 && cshape <= Qt::LastCursor) { - void *dpy = connection()->xlib_display(); - cursor = loadCursor(dpy, cshape); - if (!cursor && !m_gtkCursorThemeInitialized && m_screen->xSettings()->initialized()) { - QByteArray gtkCursorTheme = m_screen->xSettings()->setting("Gtk/CursorThemeName").toByteArray(); - if (updateCursorTheme(dpy,gtkCursorTheme)) { - cursor = loadCursor(dpy, cshape); - } - m_gtkCursorThemeInitialized = true; + for (const char *cursorName : cursorNames[cshape]) { + cursor = xcb_cursor_load_cursor(m_cursorContext, cursorName); + if (cursor != XCB_NONE) + return cursor; } } - if (cursor) - return cursor; - if (!cursor && cursorId) { - cursor = XCreateFontCursor(static_cast<Display *>(connection()->xlib_display()), cursorId); - if (cursor) - return cursor; - } - -#endif // Non-standard X11 cursors are created from bitmaps cursor = createNonStandardCursor(cshape); @@ -583,8 +534,8 @@ xcb_cursor_t QXcbCursor::createBitmapCursor(QCursor *cursor) qCWarning(lcQpaXcb, "xrender >= 0.5 required to create pixmap cursors"); } else { xcb_connection_t *conn = xcb_connection(); - xcb_pixmap_t cp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->bitmap(Qt::ReturnByValue).toImage()); - xcb_pixmap_t mp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->mask(Qt::ReturnByValue).toImage()); + xcb_pixmap_t cp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->bitmap().toImage()); + xcb_pixmap_t mp = qt_xcb_XPixmapFromBitmap(m_screen, cursor->mask().toImage()); c = xcb_generate_id(conn); xcb_create_cursor(conn, c, cp, mp, 0, 0, 0, 0xFFFF, 0xFFFF, 0xFFFF, spot.x(), spot.y()); diff --git a/src/plugins/platforms/xcb/qxcbcursor.h b/src/plugins/platforms/xcb/qxcbcursor.h index 83438321ef..bf26861e8f 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.h +++ b/src/plugins/platforms/xcb/qxcbcursor.h @@ -1,4 +1,4 @@ -// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QXCBCURSOR_H @@ -6,6 +6,7 @@ #include <qpa/qplatformcursor.h> #include "qxcbscreen.h" +#include <xcb/xcb_cursor.h> #include <QtCore/QCache> @@ -47,6 +48,10 @@ public: QPoint pos() const override; void setPos(const QPoint &pos) override; + QSize size() const override; + + void updateContext(); + static void queryPointer(QXcbConnection *c, QXcbVirtualDesktop **virtualDesktop, QPoint *pos, int *keybMask = nullptr); #ifndef QT_NO_CURSOR @@ -75,17 +80,16 @@ private: #endif QXcbScreen *m_screen; + xcb_cursor_context_t *m_cursorContext; #ifndef QT_NO_CURSOR CursorHash m_cursorHash; BitmapCursorCache m_bitmapCache; #endif -#if QT_CONFIG(xcb_xlib) && QT_CONFIG(library) static void cursorThemePropertyChanged(QXcbVirtualDesktop *screen, const QByteArray &name, const QVariant &property, void *handle); -#endif - bool m_gtkCursorThemeInitialized; + bool m_callbackForPropertyRegistered; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbcursorfont.h b/src/plugins/platforms/xcb/qxcbcursorfont.h new file mode 100644 index 0000000000..fe74e27691 --- /dev/null +++ b/src/plugins/platforms/xcb/qxcbcursorfont.h @@ -0,0 +1,88 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// copied from <X11/cursorfont.h> + +#ifndef QXCBCURSORFONT_H +#define QXCBCURSORFONT_H + +#define XC_num_glyphs 154 +#define XC_X_cursor 0 +#define XC_arrow 2 +#define XC_based_arrow_down 4 +#define XC_based_arrow_up 6 +#define XC_boat 8 +#define XC_bogosity 10 +#define XC_bottom_left_corner 12 +#define XC_bottom_right_corner 14 +#define XC_bottom_side 16 +#define XC_bottom_tee 18 +#define XC_box_spiral 20 +#define XC_center_ptr 22 +#define XC_circle 24 +#define XC_clock 26 +#define XC_coffee_mug 28 +#define XC_cross 30 +#define XC_cross_reverse 32 +#define XC_crosshair 34 +#define XC_diamond_cross 36 +#define XC_dot 38 +#define XC_dotbox 40 +#define XC_double_arrow 42 +#define XC_draft_large 44 +#define XC_draft_small 46 +#define XC_draped_box 48 +#define XC_exchange 50 +#define XC_fleur 52 +#define XC_gobbler 54 +#define XC_gumby 56 +#define XC_hand1 58 +#define XC_hand2 60 +#define XC_heart 62 +#define XC_icon 64 +#define XC_iron_cross 66 +#define XC_left_ptr 68 +#define XC_left_side 70 +#define XC_left_tee 72 +#define XC_leftbutton 74 +#define XC_ll_angle 76 +#define XC_lr_angle 78 +#define XC_man 80 +#define XC_middlebutton 82 +#define XC_mouse 84 +#define XC_pencil 86 +#define XC_pirate 88 +#define XC_plus 90 +#define XC_question_arrow 92 +#define XC_right_ptr 94 +#define XC_right_side 96 +#define XC_right_tee 98 +#define XC_rightbutton 100 +#define XC_rtl_logo 102 +#define XC_sailboat 104 +#define XC_sb_down_arrow 106 +#define XC_sb_h_double_arrow 108 +#define XC_sb_left_arrow 110 +#define XC_sb_right_arrow 112 +#define XC_sb_up_arrow 114 +#define XC_sb_v_double_arrow 116 +#define XC_shuttle 118 +#define XC_sizing 120 +#define XC_spider 122 +#define XC_spraycan 124 +#define XC_star 126 +#define XC_target 128 +#define XC_tcross 130 +#define XC_top_left_arrow 132 +#define XC_top_left_corner 134 +#define XC_top_right_corner 136 +#define XC_top_side 138 +#define XC_top_tee 140 +#define XC_trek 142 +#define XC_ul_angle 144 +#define XC_umbrella 146 +#define XC_ur_angle 148 +#define XC_watch 150 +#define XC_xterm 152 + +#endif /* QXCBCURSORFONT_H */ diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index f5cb50d9c6..6e5dd770b9 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -5,6 +5,7 @@ #include <xcb/xcb.h> #include "qxcbconnection.h" #include "qxcbclipboard.h" +#include "qxcbkeyboard.h" #include "qxcbmime.h" #include "qxcbwindow.h" #include "qxcbscreen.h" @@ -27,6 +28,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::Literals::StringLiterals; + const int xdnd_version = 5; static inline xcb_window_t xcb_window(QPlatformWindow *w) @@ -44,7 +47,7 @@ static xcb_window_t xdndProxy(QXcbConnection *c, xcb_window_t w) xcb_window_t proxy = XCB_NONE; auto reply = Q_XCB_REPLY(xcb_get_property, c->xcb_connection(), - false, w, c->atom(QXcbAtom::XdndProxy), XCB_ATOM_WINDOW, 0, 1); + false, w, c->atom(QXcbAtom::AtomXdndProxy), XCB_ATOM_WINDOW, 0, 1); if (reply && reply->type == XCB_ATOM_WINDOW) proxy = *((xcb_window_t *)xcb_get_property_value(reply.get())); @@ -54,7 +57,7 @@ static xcb_window_t xdndProxy(QXcbConnection *c, xcb_window_t w) // exists and is real? reply = Q_XCB_REPLY(xcb_get_property, c->xcb_connection(), - false, proxy, c->atom(QXcbAtom::XdndProxy), XCB_ATOM_WINDOW, 0, 1); + false, proxy, c->atom(QXcbAtom::AtomXdndProxy), XCB_ATOM_WINDOW, 0, 1); if (reply && reply->type == XCB_ATOM_WINDOW) { xcb_window_t p = *((xcb_window_t *)xcb_get_property_value(reply.get())); @@ -140,7 +143,7 @@ void QXcbDrag::startDrag() qCDebug(lcQpaXDnd) << "starting drag where source:" << connection()->qtSelectionOwner(); xcb_set_selection_owner(xcb_connection(), connection()->qtSelectionOwner(), - atom(QXcbAtom::XdndSelection), connection()->time()); + atom(QXcbAtom::AtomXdndSelection), connection()->time()); QStringList fmts = QXcbMime::formatsHelper(drag()->mimeData()); for (int i = 0; i < fmts.size(); ++i) { @@ -153,7 +156,7 @@ void QXcbDrag::startDrag() if (drag_types.size() > 3) xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->qtSelectionOwner(), - atom(QXcbAtom::XdndTypelist), + atom(QXcbAtom::AtomXdndTypelist), XCB_ATOM_ATOM, 32, drag_types.size(), (const void *)drag_types.constData()); setUseCompositing(current_virtual_desktop->compositingActive()); @@ -187,7 +190,7 @@ Qt::DropAction QXcbDrag::defaultAction(Qt::DropActions possibleActions, Qt::Keyb void QXcbDrag::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) { - if (event->window != xdnd_dragsource || event->atom != atom(QXcbAtom::XdndActionList)) + if (event->window != xdnd_dragsource || event->atom != atom(QXcbAtom::AtomXdndActionList)) return; readActionList(); @@ -233,7 +236,7 @@ xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md bool windowContainsMouse = !ignoreNonXdndAwareWindows; { auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), - false, w, connection()->atom(QXcbAtom::XdndAware), + false, w, connection()->atom(QXcbAtom::AtomXdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0); bool isAware = reply && reply->type != XCB_NONE; if (isAware) { @@ -306,7 +309,7 @@ bool QXcbDrag::findXdndAwareTarget(const QPoint &globalPos, xcb_window_t *target xcb_window_t child = translate->child; auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, target, - atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0); + atom(QXcbAtom::AtomXdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0); bool aware = reply && reply->type != XCB_NONE; if (aware) { qCDebug(lcQpaXDnd) << "found XdndAware on" << target; @@ -379,7 +382,7 @@ void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod if (proxy_target) { auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, proxy_target, - atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 1); + atom(QXcbAtom::AtomXdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 1); if (!reply || reply->type == XCB_NONE) { target = 0; } else { @@ -404,7 +407,7 @@ void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod enter.sequence = 0; enter.window = target; enter.format = 32; - enter.type = atom(QXcbAtom::XdndEnter); + enter.type = atom(QXcbAtom::AtomXdndEnter); enter.data.data32[0] = connection()->qtSelectionOwner(); enter.data.data32[1] = flags; enter.data.data32[2] = drag_types.size() > 0 ? drag_types.at(0) : 0; @@ -435,7 +438,7 @@ void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod move.sequence = 0; move.window = target; move.format = 32; - move.type = atom(QXcbAtom::XdndPosition); + move.type = atom(QXcbAtom::AtomXdndPosition); move.data.data32[0] = connection()->qtSelectionOwner(); move.data.data32[1] = 0; // flags move.data.data32[2] = (globalPos.x() << 16) + globalPos.y(); @@ -459,7 +462,7 @@ void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod static const bool isUnity = qgetenv("XDG_CURRENT_DESKTOP").toLower() == "unity"; if (isUnity && xdndCollectionWindow == XCB_NONE) { QString name = QXcbWindow::windowTitle(connection(), target); - if (name == QStringLiteral("XdndCollectionWindowImp")) + if (name == "XdndCollectionWindowImp"_L1) xdndCollectionWindow = target; } if (target == xdndCollectionWindow) { @@ -481,7 +484,7 @@ void QXcbDrag::drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod drop.sequence = 0; drop.window = current_target; drop.format = 32; - drop.type = atom(QXcbAtom::XdndDrop); + drop.type = atom(QXcbAtom::AtomXdndDrop); drop.data.data32[0] = connection()->qtSelectionOwner(); drop.data.data32[1] = 0; // flags drop.data.data32[2] = connection()->time(); @@ -521,11 +524,11 @@ void QXcbDrag::drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod Qt::DropAction QXcbDrag::toDropAction(xcb_atom_t a) const { - if (a == atom(QXcbAtom::XdndActionCopy) || a == 0) + if (a == atom(QXcbAtom::AtomXdndActionCopy) || a == 0) return Qt::CopyAction; - if (a == atom(QXcbAtom::XdndActionLink)) + if (a == atom(QXcbAtom::AtomXdndActionLink)) return Qt::LinkAction; - if (a == atom(QXcbAtom::XdndActionMove)) + if (a == atom(QXcbAtom::AtomXdndActionMove)) return Qt::MoveAction; return Qt::CopyAction; } @@ -534,7 +537,7 @@ Qt::DropActions QXcbDrag::toDropActions(const QList<xcb_atom_t> &atoms) const { Qt::DropActions actions; for (const auto actionAtom : atoms) { - if (actionAtom != atom(QXcbAtom::XdndActionAsk)) + if (actionAtom != atom(QXcbAtom::AtomXdndActionAsk)) actions |= toDropAction(actionAtom); } return actions; @@ -544,16 +547,16 @@ xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const { switch (a) { case Qt::CopyAction: - return atom(QXcbAtom::XdndActionCopy); + return atom(QXcbAtom::AtomXdndActionCopy); case Qt::LinkAction: - return atom(QXcbAtom::XdndActionLink); + return atom(QXcbAtom::AtomXdndActionLink); case Qt::MoveAction: case Qt::TargetMoveAction: - return atom(QXcbAtom::XdndActionMove); + return atom(QXcbAtom::AtomXdndActionMove); case Qt::IgnoreAction: return XCB_NONE; default: - return atom(QXcbAtom::XdndActionCopy); + return atom(QXcbAtom::AtomXdndActionCopy); } } @@ -561,7 +564,7 @@ void QXcbDrag::readActionList() { drop_actions.clear(); auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, xdnd_dragsource, - atom(QXcbAtom::XdndActionList), XCB_ATOM_ATOM, + atom(QXcbAtom::AtomXdndActionList), XCB_ATOM_ATOM, 0, 1024); if (reply && reply->type != XCB_NONE && reply->format == 32) { int length = xcb_get_property_value_length(reply.get()) / 4; @@ -590,7 +593,7 @@ void QXcbDrag::setActionList(Qt::DropAction requestedAction, Qt::DropActions sup if (current_actions != actions) { xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->qtSelectionOwner(), - atom(QXcbAtom::XdndActionList), + atom(QXcbAtom::AtomXdndActionList), XCB_ATOM_ATOM, 32, actions.size(), actions.constData()); current_actions = actions; } @@ -614,7 +617,7 @@ void QXcbDrag::stopListeningForActionListChanges() int QXcbDrag::findTransactionByWindow(xcb_window_t window) { int at = -1; - for (int i = 0; i < transactions.count(); ++i) { + for (int i = 0; i < transactions.size(); ++i) { const Transaction &t = transactions.at(i); if (t.target == window || t.proxy_target == window) { at = i; @@ -627,7 +630,7 @@ int QXcbDrag::findTransactionByWindow(xcb_window_t window) int QXcbDrag::findTransactionByTime(xcb_timestamp_t timestamp) { int at = -1; - for (int i = 0; i < transactions.count(); ++i) { + for (int i = 0; i < transactions.size(); ++i) { const Transaction &t = transactions.at(i); if (t.timestamp == timestamp) { at = i; @@ -701,7 +704,7 @@ void QXcbDrag::handleEnter(QPlatformWindow *, const xcb_client_message_event_t * if (event->data.data32[1] & 1) { // get the types from XdndTypeList auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, xdnd_dragsource, - atom(QXcbAtom::XdndTypelist), XCB_ATOM_ATOM, + atom(QXcbAtom::AtomXdndTypelist), XCB_ATOM_ATOM, 0, xdnd_max_type); if (reply && reply->type != XCB_NONE && reply->format == 32) { int length = xcb_get_property_value_length(reply.get()) / 4; @@ -720,7 +723,7 @@ void QXcbDrag::handleEnter(QPlatformWindow *, const xcb_client_message_event_t * xdnd_types.append(event->data.data32[i]); } } - for(int i = 0; i < xdnd_types.length(); ++i) + for(int i = 0; i < xdnd_types.size(); ++i) qCDebug(lcQpaXDnd) << " " << connection()->atomName(xdnd_types.at(i)); } @@ -734,7 +737,7 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message QPoint p((e->data.data32[2] & 0xffff0000) >> 16, e->data.data32[2] & 0x0000ffff); Q_ASSERT(w); QRect geometry = w->geometry(); - p -= geometry.topLeft(); + p -= w->isEmbedded() ? w->mapToGlobal(geometry.topLeft()) : geometry.topLeft(); if (!w || !w->window() || (w->window()->type() == Qt::Desktop)) return; @@ -761,12 +764,12 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message } else { dropData = m_dropData; supported_actions = toDropActions(drop_actions); - if (e->data.data32[4] != atom(QXcbAtom::XdndActionAsk)) + if (e->data.data32[4] != atom(QXcbAtom::AtomXdndActionAsk)) supported_actions |= Qt::DropActions(toDropAction(e->data.data32[4])); } auto buttons = currentDrag() ? b : connection()->queryMouseButtons(); - auto modifiers = currentDrag() ? mods : connection()->queryKeyboardModifiers(); + auto modifiers = currentDrag() ? mods : connection()->keyboard()->queryKeyboardModifiers(); QPlatformDragQtResponse qt_response = QWindowSystemInterface::handleDrag( w->window(), dropData, p, supported_actions, buttons, modifiers); @@ -783,7 +786,7 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message response.sequence = 0; response.window = xdnd_dragsource; response.format = 32; - response.type = atom(QXcbAtom::XdndStatus); + response.type = atom(QXcbAtom::AtomXdndStatus); response.data.data32[0] = xcb_window(w); response.data.data32[1] = qt_response.isAccepted(); // flags response.data.data32[2] = 0; // x, y @@ -835,7 +838,7 @@ namespace void QXcbDrag::handlePosition(QPlatformWindow * w, const xcb_client_message_event_t *event) { xcb_client_message_event_t *lastEvent = const_cast<xcb_client_message_event_t *>(event); - ClientMessageScanner scanner(atom(QXcbAtom::XdndPosition)); + ClientMessageScanner scanner(atom(QXcbAtom::AtomXdndPosition)); while (auto nextEvent = connection()->eventQueue()->peek(scanner)) { if (lastEvent != event) free(lastEvent); @@ -883,7 +886,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)); + ClientMessageScanner scanner(atom(QXcbAtom::AtomXdndStatus)); while ((nextEvent = connection()->eventQueue()->peek(scanner))) { if (lastEvent != event) free(lastEvent); @@ -934,7 +937,7 @@ void QXcbDrag::send_leave() leave.sequence = 0; leave.window = current_target; leave.format = 32; - leave.type = atom(QXcbAtom::XdndLeave); + leave.type = atom(QXcbAtom::AtomXdndLeave); leave.data.data32[0] = connection()->qtSelectionOwner(); leave.data.data32[1] = 0; // flags leave.data.data32[2] = 0; // x, y @@ -980,40 +983,54 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e Qt::DropActions supported_drop_actions; QMimeData *dropData = nullptr; + // this could be a same-application drop, just proxied due to + // some XEMBEDding, so try to find the real QMimeData used + // based on the timestamp for this drop. + int at = findTransactionByTime(target_time); + if (at != -1) { + qCDebug(lcQpaXDnd) << "found one transaction via findTransactionByTime()"; + dropData = transactions.at(at).drag->mimeData(); + // Can't use the source QMimeData if we need the image conversion code from xdndObtainData + if (dropData && dropData->hasImage()) + dropData = 0; + } + // if we can't find it, then use the data in the drag manager if (currentDrag()) { - dropData = currentDrag()->mimeData(); + if (!dropData) + dropData = currentDrag()->mimeData(); supported_drop_actions = Qt::DropActions(l[4]); } else { - dropData = m_dropData; + if (!dropData) + dropData = m_dropData; supported_drop_actions = accepted_drop_action | toDropActions(drop_actions); } if (!dropData) return; - // ### - // int at = findXdndDropTransactionByTime(target_time); - // if (at != -1) - // dropData = QDragManager::dragPrivate(X11->dndDropTransactions.at(at).object)->data; - // if we can't find it, then use the data in the drag manager auto buttons = currentDrag() ? b : connection()->queryMouseButtons(); - auto modifiers = currentDrag() ? mods : connection()->queryKeyboardModifiers(); + auto modifiers = currentDrag() ? mods : connection()->keyboard()->queryKeyboardModifiers(); QPlatformDropQtResponse response = QWindowSystemInterface::handleDrop( currentWindow.data(), dropData, currentPosition, supported_drop_actions, buttons, modifiers); - setExecutedDropAction(response.acceptedAction()); + Qt::DropAction acceptedAaction = response.acceptedAction(); + if (!response.isAccepted()) { + // Ignore a failed drag + acceptedAaction = Qt::IgnoreAction; + } + setExecutedDropAction(acceptedAaction); xcb_client_message_event_t finished = {}; finished.response_type = XCB_CLIENT_MESSAGE; finished.sequence = 0; finished.window = xdnd_dragsource; finished.format = 32; - finished.type = atom(QXcbAtom::XdndFinished); + finished.type = atom(QXcbAtom::AtomXdndFinished); finished.data.data32[0] = currentWindow ? xcb_window(currentWindow.data()) : XCB_NONE; finished.data.data32[1] = response.isAccepted(); // flags - finished.data.data32[2] = toXdndAction(response.acceptedAction()); + finished.data.data32[2] = toXdndAction(acceptedAaction); qCDebug(lcQpaXDnd) << "sending XdndFinished to source:" << xdnd_dragsource; @@ -1075,7 +1092,7 @@ void QXcbDrag::timerEvent(QTimerEvent* e) { if (e->timerId() == cleanup_timer) { bool stopTimer = true; - for (int i = 0; i < transactions.count(); ++i) { + for (int i = 0; i < transactions.size(); ++i) { const Transaction &t = transactions.at(i); if (t.targetWindow) { // dnd within the same process, don't delete, these are taken care of @@ -1083,7 +1100,7 @@ void QXcbDrag::timerEvent(QTimerEvent* e) continue; } QTime currentTime = QTime::currentTime(); - int delta = t.time.msecsTo(currentTime); + std::chrono::milliseconds delta{t.time.msecsTo(currentTime)}; if (delta > XdndDropTransactionTimeout) { /* delete transactions which are older than XdndDropTransactionTimeout. It could mean one of these: @@ -1127,7 +1144,7 @@ static xcb_window_t findXdndAwareParent(QXcbConnection *c, xcb_window_t window) forever { // check if window has XdndAware auto gpReply = Q_XCB_REPLY(xcb_get_property, c->xcb_connection(), false, window, - c->atom(QXcbAtom::XdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0); + c->atom(QXcbAtom::AtomXdndAware), XCB_GET_PROPERTY_TYPE_ANY, 0, 0); bool aware = gpReply && gpReply->type != XCB_NONE; if (aware) { target = window; @@ -1217,6 +1234,7 @@ void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event bool QXcbDrag::dndEnable(QXcbWindow *w, bool on) { + qCDebug(lcQpaXDnd) << "dndEnable" << static_cast<QPlatformWindow *>(w) << on; // Windows announce that they support the XDND protocol by creating a window property XdndAware. if (on) { QXcbWindow *window = nullptr; @@ -1233,7 +1251,7 @@ bool QXcbDrag::dndEnable(QXcbWindow *w, bool on) desktop_proxy = new QWindow; window = static_cast<QXcbWindow *>(desktop_proxy->handle()); proxy_id = window->xcb_window(); - xcb_atom_t xdnd_proxy = atom(QXcbAtom::XdndProxy); + xcb_atom_t xdnd_proxy = atom(QXcbAtom::AtomXdndProxy); xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, w->xcb_window(), xdnd_proxy, XCB_ATOM_WINDOW, 32, 1, &proxy_id); xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, proxy_id, xdnd_proxy, @@ -1247,14 +1265,14 @@ bool QXcbDrag::dndEnable(QXcbWindow *w, bool on) qCDebug(lcQpaXDnd) << "setting XdndAware for" << window->xcb_window(); xcb_atom_t atm = xdnd_version; xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, window->xcb_window(), - atom(QXcbAtom::XdndAware), XCB_ATOM_ATOM, 32, 1, &atm); + atom(QXcbAtom::AtomXdndAware), XCB_ATOM_ATOM, 32, 1, &atm); return true; } else { return false; } } else { if (w->window()->type() == Qt::Desktop) { - xcb_delete_property(xcb_connection(), w->xcb_window(), atom(QXcbAtom::XdndProxy)); + xcb_delete_property(xcb_connection(), w->xcb_window(), atom(QXcbAtom::AtomXdndProxy)); delete desktop_proxy; desktop_proxy = nullptr; } else { @@ -1306,10 +1324,10 @@ QVariant QXcbDropData::xdndObtainData(const QByteArray &format, QMetaType reques return result; #ifndef QT_NO_CLIPBOARD - if (c->selectionOwner(c->atom(QXcbAtom::XdndSelection)) == XCB_NONE) + if (c->selectionOwner(c->atom(QXcbAtom::AtomXdndSelection)) == XCB_NONE) return result; // should never happen? - xcb_atom_t xdnd_selection = c->atom(QXcbAtom::XdndSelection); + xcb_atom_t xdnd_selection = c->atom(QXcbAtom::AtomXdndSelection); result = c->clipboard()->getSelection(xdnd_selection, a, xdnd_selection, drag->targetTime()); #endif diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h index a58b7e850e..ae7cc915c8 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.h +++ b/src/plugins/platforms/xcb/qxcbdrag.h @@ -14,7 +14,6 @@ #include <qpoint.h> #include <qpointer.h> #include <qrect.h> -#include <qsharedpointer.h> #include <qxcbobject.h> #include <QtCore/QDebug> @@ -128,7 +127,7 @@ private: QXcbVirtualDesktop *current_virtual_desktop; // 10 minute timer used to discard old XdndDrop transactions - enum { XdndDropTransactionTimeout = 600000 }; + static constexpr std::chrono::minutes XdndDropTransactionTimeout{10}; int cleanup_timer; QList<xcb_atom_t> drag_types; diff --git a/src/plugins/platforms/xcb/qxcbeventqueue.cpp b/src/plugins/platforms/xcb/qxcbeventqueue.cpp index 5c87cba80d..33795d63cf 100644 --- a/src/plugins/platforms/xcb/qxcbeventqueue.cpp +++ b/src/plugins/platforms/xcb/qxcbeventqueue.cpp @@ -345,7 +345,7 @@ void QXcbEventQueue::sendCloseConnectionEvent() const event.format = 32; event.sequence = 0; event.window = window; - event.type = m_connection->atom(QXcbAtom::_QT_CLOSE_CONNECTION); + event.type = m_connection->atom(QXcbAtom::Atom_QT_CLOSE_CONNECTION); event.data.data32[0] = 0; xcb_send_event(c, false, window, XCB_EVENT_MASK_NO_EVENT, reinterpret_cast<const char *>(&event)); @@ -357,7 +357,7 @@ 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)) + if (clientMessage->type == m_connection->atom(QXcbAtom::Atom_QT_CLOSE_CONNECTION)) m_closeConnectionDetected = true; } return m_closeConnectionDetected; diff --git a/src/plugins/platforms/xcb/qxcbimage.h b/src/plugins/platforms/xcb/qxcbimage.h index e550102881..c022fae639 100644 --- a/src/plugins/platforms/xcb/qxcbimage.h +++ b/src/plugins/platforms/xcb/qxcbimage.h @@ -5,7 +5,6 @@ #define QXCBIMAGE_H #include "qxcbscreen.h" -#include <QtCore/QPair> #include <QtGui/QImage> #include <QtGui/QPixmap> #include <xcb/xcb_image.h> diff --git a/src/plugins/platforms/xcb/qxcbintegration.cpp b/src/plugins/platforms/xcb/qxcbintegration.cpp index 893b11c8c3..4dafae31e3 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -20,6 +20,7 @@ #ifndef QT_NO_SESSIONMANAGER #include "qxcbsessionmanager.h" #endif +#include "qxcbxsettings.h" #include <xcb/xcb.h> @@ -48,9 +49,9 @@ #include <QtGui/QOpenGLContext> #include <QtGui/QScreen> #include <QtGui/QOffscreenSurface> -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) #include <qpa/qplatformaccessibility.h> -#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE +#if QT_CONFIG(accessibility_atspi_bridge) #include <QtGui/private/qspiaccessiblebridge_p.h> #endif #endif @@ -92,10 +93,17 @@ static bool runningUnderDebugger() #endif } +class QXcbUnixServices : public QGenericUnixServices +{ +public: + QString portalWindowIdentifier(QWindow *window) override; +}; + + QXcbIntegration *QXcbIntegration::m_instance = nullptr; QXcbIntegration::QXcbIntegration(const QStringList ¶meters, int &argc, char **argv) - : m_services(new QGenericUnixServices) + : m_services(new QXcbUnixServices) , m_instanceName(nullptr) , m_canGrab(true) , m_defaultVisualId(UINT_MAX) @@ -196,7 +204,7 @@ QPlatformPixmap *QXcbIntegration::createPlatformPixmap(QPlatformPixmap::PixelTyp QPlatformWindow *QXcbIntegration::createPlatformWindow(QWindow *window) const { QXcbGlIntegration *glIntegration = nullptr; - const bool isTrayIconWindow = QXcbWindow::isTrayIconWindow(window);; + const bool isTrayIconWindow = QXcbWindow::isTrayIconWindow(window); if (window->type() != Qt::Desktop && !isTrayIconWindow) { if (window->supportsOpenGL()) { glIntegration = connection()->glIntegration(); @@ -324,19 +332,38 @@ QAbstractEventDispatcher *QXcbIntegration::createEventDispatcher() const return QXcbEventDispatcher::createEventDispatcher(connection()); } +using namespace Qt::Literals::StringLiterals; +static const auto xsNetCursorBlink = "Net/CursorBlink"_ba; +static const auto xsNetCursorBlinkTime = "Net/CursorBlinkTime"_ba; +static const auto xsNetDoubleClickTime = "Net/DoubleClickTime"_ba; +static const auto xsNetDoubleClickDistance = "Net/DoubleClickDistance"_ba; +static const auto xsNetDndDragThreshold = "Net/DndDragThreshold"_ba; + void QXcbIntegration::initialize() { const auto defaultInputContext = "compose"_L1; // Perform everything that may potentially need the event dispatcher (timers, socket // notifiers) here instead of the constructor. - QString icStr = QPlatformInputContextFactory::requested(); - if (icStr.isNull()) - icStr = defaultInputContext; - m_inputContext.reset(QPlatformInputContextFactory::create(icStr)); - if (!m_inputContext && icStr != defaultInputContext && icStr != "none"_L1) + auto icStrs = QPlatformInputContextFactory::requested(); + if (icStrs.isEmpty()) + icStrs = { defaultInputContext }; + m_inputContext.reset(QPlatformInputContextFactory::create(icStrs)); + if (!m_inputContext && !icStrs.contains(defaultInputContext) + && icStrs != QStringList{"none"_L1}) m_inputContext.reset(QPlatformInputContextFactory::create(defaultInputContext)); connection()->keyboard()->initialize(); + + auto notifyThemeChanged = [](QXcbVirtualDesktop *, const QByteArray &, const QVariant &, void *) { + QWindowSystemInterface::handleThemeChange(); + }; + + auto *xsettings = connection()->primaryScreen()->xSettings(); + xsettings->registerCallbackForProperty(xsNetCursorBlink, notifyThemeChanged, this); + xsettings->registerCallbackForProperty(xsNetCursorBlinkTime, notifyThemeChanged, this); + xsettings->registerCallbackForProperty(xsNetDoubleClickTime, notifyThemeChanged, this); + xsettings->registerCallbackForProperty(xsNetDoubleClickDistance, notifyThemeChanged, this); + xsettings->registerCallbackForProperty(xsNetDndDragThreshold, notifyThemeChanged, this); } void QXcbIntegration::moveToScreen(QWindow *window, int screen) @@ -383,7 +410,7 @@ QPlatformInputContext *QXcbIntegration::inputContext() const return m_inputContext.data(); } -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) QPlatformAccessibility *QXcbIntegration::accessibility() const { #if !defined(QT_NO_ACCESSIBILITY_ATSPI_BRIDGE) @@ -403,14 +430,9 @@ QPlatformServices *QXcbIntegration::services() const return m_services.data(); } -Qt::KeyboardModifiers QXcbIntegration::queryKeyboardModifiers() const +QPlatformKeyMapper *QXcbIntegration::keyMapper() const { - return m_connection->queryKeyboardModifiers(); -} - -QList<int> QXcbIntegration::possibleKeys(const QKeyEvent *e) const -{ - return m_connection->keyboard()->possibleKeys(e); + return m_connection->keyboard(); } QStringList QXcbIntegration::themeNames() const @@ -423,31 +445,53 @@ QPlatformTheme *QXcbIntegration::createPlatformTheme(const QString &name) const return QGenericUnixTheme::createUnixTheme(name); } +#define RETURN_VALID_XSETTINGS(key) { \ + auto value = connection()->primaryScreen()->xSettings()->setting(key); \ + if (value.isValid()) return value; \ +} + QVariant QXcbIntegration::styleHint(QPlatformIntegration::StyleHint hint) const { switch (hint) { - case QPlatformIntegration::CursorFlashTime: - case QPlatformIntegration::KeyboardInputInterval: + case QPlatformIntegration::CursorFlashTime: { + bool ok = false; + // If cursor blinking is off, returns 0 to keep the cursor awlays display. + if (connection()->primaryScreen()->xSettings()->setting(xsNetCursorBlink).toInt(&ok) == 0 && ok) + return 0; + + RETURN_VALID_XSETTINGS(xsNetCursorBlinkTime); + break; + } case QPlatformIntegration::MouseDoubleClickInterval: + RETURN_VALID_XSETTINGS(xsNetDoubleClickTime); + break; + case QPlatformIntegration::MouseDoubleClickDistance: + RETURN_VALID_XSETTINGS(xsNetDoubleClickDistance); + break; + case QPlatformIntegration::KeyboardInputInterval: case QPlatformIntegration::StartDragTime: case QPlatformIntegration::KeyboardAutoRepeatRate: case QPlatformIntegration::PasswordMaskDelay: case QPlatformIntegration::StartDragVelocity: case QPlatformIntegration::UseRtlExtensions: case QPlatformIntegration::PasswordMaskCharacter: + case QPlatformIntegration::FlickMaximumVelocity: + case QPlatformIntegration::FlickDeceleration: // TODO using various xcb, gnome or KDE settings break; // Not implemented, use defaults + case QPlatformIntegration::FlickStartDistance: case QPlatformIntegration::StartDragDistance: { + RETURN_VALID_XSETTINGS(xsNetDndDragThreshold); // The default (in QPlatformTheme::defaultThemeHint) is 10 pixels, but // on a high-resolution screen it makes sense to increase it. - qreal dpi = 100.0; + qreal dpi = 100; if (const QXcbScreen *screen = connection()->primaryScreen()) { if (screen->logicalDpi().first > dpi) dpi = screen->logicalDpi().first; if (screen->logicalDpi().second > dpi) dpi = screen->logicalDpi().second; } - return 10.0 * dpi / 100.0; + return (hint == QPlatformIntegration::FlickStartDistance ? qreal(15) : qreal(10)) * dpi / qreal(100); } case QPlatformIntegration::ShowIsFullScreen: // X11 always has support for windows, but the @@ -546,4 +590,15 @@ QPlatformVulkanInstance *QXcbIntegration::createPlatformVulkanInstance(QVulkanIn } #endif +void QXcbIntegration::setApplicationBadge(qint64 number) +{ + auto unixServices = dynamic_cast<QGenericUnixServices *>(services()); + unixServices->setApplicationBadge(number); +} + +QString QXcbUnixServices::portalWindowIdentifier(QWindow *window) +{ + return "x11:"_L1 + QString::number(window->winId(), 16); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbintegration.h b/src/plugins/platforms/xcb/qxcbintegration.h index 4553bbe700..a1e0c3f3e1 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.h +++ b/src/plugins/platforms/xcb/qxcbintegration.h @@ -68,14 +68,13 @@ public: QPlatformInputContext *inputContext() const override; -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) QPlatformAccessibility *accessibility() const override; #endif QPlatformServices *services() const override; - Qt::KeyboardModifiers queryKeyboardModifiers() const override; - QList<int> possibleKeys(const QKeyEvent *e) const override; + QPlatformKeyMapper *keyMapper() const override; QStringList themeNames() const override; QPlatformTheme *createPlatformTheme(const QString &name) const override; @@ -102,6 +101,8 @@ public: static QXcbIntegration *instance() { return m_instance; } + void setApplicationBadge(qint64 number) override; + private: QXcbConnection *m_connection = nullptr; @@ -110,7 +111,7 @@ private: QScopedPointer<QPlatformInputContext> m_inputContext; -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) mutable QScopedPointer<QPlatformAccessibility> m_accessibility; #endif diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index 79fef496b4..17da54bc7a 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.cpp +++ b/src/plugins/platforms/xcb/qxcbkeyboard.cpp @@ -3,6 +3,7 @@ #include "qxcbkeyboard.h" #include "qxcbwindow.h" #include "qxcbscreen.h" +#include "qxcbcursor.h" #include <qpa/qwindowsysteminterface.h> #include <qpa/qplatforminputcontext.h> @@ -377,9 +378,18 @@ void QXcbKeyboard::updateKeymap() QXkbCommon::verifyHasLatinLayout(m_xkbKeymap.get()); } -QList<int> QXcbKeyboard::possibleKeys(const QKeyEvent *event) const +QList<QKeyCombination> QXcbKeyboard::possibleKeyCombinations(const QKeyEvent *event) const { - return QXkbCommon::possibleKeys(m_xkbState.get(), event, m_superAsMeta, m_hyperAsMeta); + return QXkbCommon::possibleKeyCombinations( + m_xkbState.get(), event, m_superAsMeta, m_hyperAsMeta); +} + +Qt::KeyboardModifiers QXcbKeyboard::queryKeyboardModifiers() const +{ + // FIXME: Should we base this on m_xkbState? + int stateMask = 0; + QXcbCursor::queryPointer(connection(), nullptr, nullptr, &stateMask); + return translateModifiers(stateMask); } void QXcbKeyboard::updateXKBState(xcb_xkb_state_notify_event_t *state) @@ -405,7 +415,7 @@ static xkb_layout_index_t lockedGroup(quint16 state) void QXcbKeyboard::updateXKBStateFromCore(quint16 state) { - if (m_config && !connection()->hasXKB()) { + if (m_config) { struct xkb_state *xkbState = m_xkbState.get(); xkb_mod_mask_t modsDepressed = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_DEPRESSED); xkb_mod_mask_t modsLatched = xkb_state_serialize_mods(xkbState, XKB_STATE_MODS_LATCHED); @@ -427,7 +437,7 @@ void QXcbKeyboard::updateXKBStateFromCore(quint16 state) void QXcbKeyboard::updateXKBStateFromXI(void *modInfo, void *groupInfo) { - if (m_config && !connection()->hasXKB()) { + if (m_config) { auto *mods = static_cast<xcb_input_modifier_info_t *>(modInfo); auto *group = static_cast<xcb_input_group_info_t *>(groupInfo); const xkb_state_component changedComponents diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.h b/src/plugins/platforms/xcb/qxcbkeyboard.h index 15b08fbead..62d9268c64 100644 --- a/src/plugins/platforms/xcb/qxcbkeyboard.h +++ b/src/plugins/platforms/xcb/qxcbkeyboard.h @@ -14,11 +14,13 @@ #include <QtGui/private/qxkbcommon_p.h> #include <xkbcommon/xkbcommon-x11.h> +#include <qpa/qplatformkeymapper.h> + #include <QEvent> QT_BEGIN_NAMESPACE -class QXcbKeyboard : public QXcbObject +class QXcbKeyboard : public QXcbObject, public QPlatformKeyMapper { public: QXcbKeyboard(QXcbConnection *connection); @@ -34,7 +36,9 @@ public: Qt::KeyboardModifiers translateModifiers(int s) const; void updateKeymap(xcb_mapping_notify_event_t *event); void updateKeymap(); - QList<int> possibleKeys(const QKeyEvent *event) const; + + QList<QKeyCombination> possibleKeyCombinations(const QKeyEvent *event) const override; + Qt::KeyboardModifiers queryKeyboardModifiers() const override; void updateXKBMods(); xkb_mod_mask_t xkbModMask(quint16 state); diff --git a/src/plugins/platforms/xcb/qxcbmime.cpp b/src/plugins/platforms/xcb/qxcbmime.cpp index 7161af279c..860d195d13 100644 --- a/src/plugins/platforms/xcb/qxcbmime.cpp +++ b/src/plugins/platforms/xcb/qxcbmime.cpp @@ -27,8 +27,8 @@ QString QXcbMime::mimeAtomToString(QXcbConnection *connection, xcb_atom_t a) // special cases for string type if (a == XCB_ATOM_STRING - || a == connection->atom(QXcbAtom::UTF8_STRING) - || a == connection->atom(QXcbAtom::TEXT)) + || a == connection->atom(QXcbAtom::AtomUTF8_STRING) + || a == connection->atom(QXcbAtom::AtomTEXT)) return "text/plain"_L1; // special case for images @@ -54,15 +54,15 @@ bool QXcbMime::mimeDataForAtom(QXcbConnection *connection, xcb_atom_t a, QMimeDa *atomFormat = a; *dataFormat = 8; - if ((a == connection->atom(QXcbAtom::UTF8_STRING) + if ((a == connection->atom(QXcbAtom::AtomUTF8_STRING) || a == XCB_ATOM_STRING - || a == connection->atom(QXcbAtom::TEXT)) + || a == connection->atom(QXcbAtom::AtomTEXT)) && QInternalMimeData::hasFormatHelper("text/plain"_L1, mimeData)) { - if (a == connection->atom(QXcbAtom::UTF8_STRING)) { + if (a == connection->atom(QXcbAtom::AtomUTF8_STRING)) { *data = QInternalMimeData::renderDataHelper("text/plain"_L1, mimeData); ret = true; } else if (a == XCB_ATOM_STRING || - a == connection->atom(QXcbAtom::TEXT)) { + a == connection->atom(QXcbAtom::AtomTEXT)) { // ICCCM says STRING is latin1 *data = QString::fromUtf8(QInternalMimeData::renderDataHelper( "text/plain"_L1, mimeData)).toLatin1(); @@ -79,8 +79,7 @@ bool QXcbMime::mimeDataForAtom(QXcbConnection *connection, xcb_atom_t a, QMimeDa if (atomName == "text/uri-list"_L1 && connection->atomName(a) == "text/x-moz-url") { const QString mozUri = QLatin1StringView(data->split('\n').constFirst()) + u'\n'; - *data = QByteArray(reinterpret_cast<const char *>(mozUri.data()), - mozUri.length() * 2); + data->assign({reinterpret_cast<const char *>(mozUri.data()), mozUri.size() * 2}); } else if (atomName == "application/x-color"_L1) *dataFormat = 16; ret = true; @@ -102,9 +101,9 @@ QList<xcb_atom_t> QXcbMime::mimeAtomsForFormat(QXcbConnection *connection, const // special cases for strings if (format == "text/plain"_L1) { - atoms.append(connection->atom(QXcbAtom::UTF8_STRING)); + atoms.append(connection->atom(QXcbAtom::AtomUTF8_STRING)); atoms.append(XCB_ATOM_STRING); - atoms.append(connection->atom(QXcbAtom::TEXT)); + atoms.append(connection->atom(QXcbAtom::AtomTEXT)); } // special cases for uris @@ -139,11 +138,11 @@ QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, if (format == "text/plain"_L1) { if (data.endsWith('\0')) data.chop(1); - if (a == connection->atom(QXcbAtom::UTF8_STRING)) { + if (a == connection->atom(QXcbAtom::AtomUTF8_STRING)) { return QString::fromUtf8(data); } if (a == XCB_ATOM_STRING || - a == connection->atom(QXcbAtom::TEXT)) + a == connection->atom(QXcbAtom::AtomTEXT)) return QString::fromLatin1(data); } // If data contains UTF16 text, convert it to a string. @@ -156,7 +155,7 @@ QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, const quint8 byte1 = data.at(1); if ((byte0 == 0xff && byte1 == 0xfe) || (byte0 == 0xfe && byte1 == 0xff) || (byte0 != 0 && byte1 == 0) || (byte0 == 0 && byte1 != 0)) { - const QString str = QString::fromUtf16( + const QStringView str( reinterpret_cast<const char16_t *>(data.constData()), data.size() / 2); if (!str.isNull()) { if (format == "text/uri-list"_L1) { @@ -175,7 +174,7 @@ QVariant QXcbMime::mimeConvertToFormat(QXcbConnection *connection, xcb_atom_t a, return list.constFirst(); return list; } else { - return str; + return str.toString(); } } } @@ -227,12 +226,12 @@ xcb_atom_t QXcbMime::mimeAtomForFormat(QXcbConnection *connection, const QString // find matches for string types if (format == "text/plain"_L1) { - if (atoms.contains(connection->atom(QXcbAtom::UTF8_STRING))) - return connection->atom(QXcbAtom::UTF8_STRING); + if (atoms.contains(connection->atom(QXcbAtom::AtomUTF8_STRING))) + return connection->atom(QXcbAtom::AtomUTF8_STRING); if (atoms.contains(XCB_ATOM_STRING)) return XCB_ATOM_STRING; - if (atoms.contains(connection->atom(QXcbAtom::TEXT))) - return connection->atom(QXcbAtom::TEXT); + if (atoms.contains(connection->atom(QXcbAtom::AtomTEXT))) + return connection->atom(QXcbAtom::AtomTEXT); } // find matches for uri types diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index cd6ff40df9..06f5241d8c 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -350,7 +350,7 @@ void *QXcbNativeInterface::atspiBus() QXcbIntegration *integration = static_cast<QXcbIntegration *>(QGuiApplicationPrivate::platformIntegration()); QXcbConnection *connection = integration->connection(); if (connection) { - auto atspiBusAtom = connection->atom(QXcbAtom::AT_SPI_BUS); + auto atspiBusAtom = connection->atom(QXcbAtom::AtomAT_SPI_BUS); auto reply = Q_XCB_REPLY(xcb_get_property, connection->xcb_connection(), false, connection->rootWindow(), atspiBusAtom, XCB_ATOM_STRING, 0, 128); diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index bb33f393ec..06f4b66edb 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -50,7 +50,7 @@ QXcbVirtualDesktop::QXcbVirtualDesktop(QXcbConnection *connection, xcb_screen_t auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(), false, screen->root, - atom(QXcbAtom::_NET_SUPPORTING_WM_CHECK), + atom(QXcbAtom::Atom_NET_SUPPORTING_WM_CHECK), XCB_ATOM_WINDOW, 0, 1024); if (reply && reply->format == 32 && reply->type == XCB_ATOM_WINDOW) { xcb_window_t windowManager = *((xcb_window_t *)xcb_get_property_value(reply.get())); @@ -92,7 +92,7 @@ QXcbVirtualDesktop::~QXcbVirtualDesktop() { delete m_xSettings; - for (auto cmap : qAsConst(m_visualColormaps)) + for (auto cmap : std::as_const(m_visualColormaps)) xcb_free_colormap(xcb_connection(), cmap); } @@ -222,7 +222,7 @@ void QXcbVirtualDesktop::handleScreenChange(xcb_randr_screen_change_notify_event case XCB_RANDR_ROTATION_REFLECT_Y: break; } - for (QPlatformScreen *platformScreen : qAsConst(m_screens)) { + for (QPlatformScreen *platformScreen : std::as_const(m_screens)) { QDpi ldpi = platformScreen->logicalDpi(); QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(platformScreen->screen(), ldpi.first, ldpi.second); } @@ -249,7 +249,7 @@ QRect QXcbVirtualDesktop::getWorkArea() const { QRect r; auto workArea = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(), false, screen()->root, - atom(QXcbAtom::_NET_WORKAREA), + atom(QXcbAtom::Atom_NET_WORKAREA), XCB_ATOM_CARDINAL, 0, 1024); if (workArea && workArea->type == XCB_ATOM_CARDINAL && workArea->format == 32 && workArea->value_len >= 4) { // If workArea->value_len > 4, the remaining ones seem to be for WM's virtual desktops @@ -271,7 +271,7 @@ void QXcbVirtualDesktop::updateWorkArea() QRect workArea = getWorkArea(); if (m_workArea != workArea) { m_workArea = workArea; - for (QPlatformScreen *screen : qAsConst(m_screens)) + for (QPlatformScreen *screen : std::as_const(m_screens)) ((QXcbScreen *)screen)->updateAvailableGeometry(); } } @@ -499,6 +499,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe , m_crtc(output ? output->crtc : XCB_NONE) , m_outputName(getOutputName(output)) , m_outputSizeMillimeters(output ? QSize(output->mm_width, output->mm_height) : QSize()) + , m_cursor(std::make_unique<QXcbCursor>(connection, this)) { if (connection->isAtLeastXRandR12()) { xcb_randr_select_input(xcb_connection(), screen()->root, true); @@ -519,8 +520,6 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe if (m_sizeMillimeters.isEmpty()) m_sizeMillimeters = virtualDesktop->physicalSize(); - m_cursor = new QXcbCursor(connection, this); - updateColorSpaceAndEdid(); } @@ -530,7 +529,7 @@ void QXcbScreen::updateColorSpaceAndEdid() // Read colord ICC data (from GNOME settings) auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(), false, screen()->root, - connection()->atom(QXcbAtom::_ICC_PROFILE), + connection()->atom(QXcbAtom::Atom_ICC_PROFILE), XCB_ATOM_CARDINAL, 0, 8192); if (reply->format == 8 && reply->type == XCB_ATOM_CARDINAL) { QByteArray data(reinterpret_cast<const char *>(xcb_get_property_value(reply.get())), reply->value_len); @@ -557,11 +556,11 @@ void QXcbScreen::updateColorSpaceAndEdid() m_edid.greenChromaticity, m_edid.blueChromaticity, QColorSpace::TransferFunction::Gamma, m_edid.gamma); } else { - if (m_edid.tables.length() == 1) { + if (m_edid.tables.size() == 1) { m_colorSpace = QColorSpace(m_edid.whiteChromaticity, m_edid.redChromaticity, m_edid.greenChromaticity, m_edid.blueChromaticity, m_edid.tables[0]); - } else if (m_edid.tables.length() == 3) { + } else if (m_edid.tables.size() == 3) { m_colorSpace = QColorSpace(m_edid.whiteChromaticity, m_edid.redChromaticity, m_edid.greenChromaticity, m_edid.blueChromaticity, m_edid.tables[0], m_edid.tables[1], m_edid.tables[2]); @@ -585,6 +584,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, QXcbVirtualDesktop *virtualDe : QXcbObject(connection) , m_virtualDesktop(virtualDesktop) , m_monitor(monitorInfo) + , m_cursor(std::make_unique<QXcbCursor>(connection, this)) { setMonitor(monitorInfo, timestamp); } @@ -598,6 +598,7 @@ void QXcbScreen::setMonitor(xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp m_crtcs.clear(); m_output = XCB_NONE; m_crtc = XCB_NONE; + m_singlescreen = false; if (!monitorInfo) { m_monitor = nullptr; @@ -678,12 +679,11 @@ void QXcbScreen::setMonitor(xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp m_sizeMillimeters = virtualDesktop()->physicalSize(); m_outputName = getName(monitorInfo); - if (connection()->primaryScreenNumber() == virtualDesktop()->number() && monitorInfo->primary) - m_primary = true; - else - m_primary = false; - - m_cursor = new QXcbCursor(connection(), this); + m_primary = false; + if (connection()->primaryScreenNumber() == virtualDesktop()->number()) { + if (monitorInfo->primary || isPrimaryInXScreen()) + m_primary = true; + } updateColorSpaceAndEdid(); } @@ -700,9 +700,19 @@ QString QXcbScreen::defaultName() return name; } +bool QXcbScreen::isPrimaryInXScreen() +{ + auto primary = Q_XCB_REPLY(xcb_randr_get_output_primary, connection()->xcb_connection(), root()); + if (!primary) + qWarning("failed to get the primary output of the screen"); + + const bool isPrimary = primary ? (m_monitor ? m_outputs.contains(primary->output) : m_output == primary->output) : false; + + return isPrimary; +} + QXcbScreen::~QXcbScreen() { - delete m_cursor; } QString QXcbScreen::getOutputName(xcb_randr_get_output_info_reply_t *outputInfo) @@ -786,7 +796,7 @@ void QXcbScreen::windowShown(QXcbWindow *window) // Freedesktop.org Startup Notification if (!connection()->startupId().isEmpty() && window->window()->isTopLevel()) { sendStartupMessage(QByteArrayLiteral("remove: ID=") + connection()->startupId()); - connection()->clearStartupId(); + connection()->setStartupId({}); } } @@ -807,15 +817,15 @@ void QXcbScreen::sendStartupMessage(const QByteArray &message) const xcb_client_message_event_t ev; ev.response_type = XCB_CLIENT_MESSAGE; ev.format = 8; - ev.type = connection()->atom(QXcbAtom::_NET_STARTUP_INFO_BEGIN); + ev.type = connection()->atom(QXcbAtom::Atom_NET_STARTUP_INFO_BEGIN); ev.sequence = 0; ev.window = rootWindow; int sent = 0; - int length = message.length() + 1; // include NUL byte + int length = message.size() + 1; // include NUL byte const char *data = message.constData(); do { if (sent == 20) - ev.type = connection()->atom(QXcbAtom::_NET_STARTUP_INFO); + ev.type = connection()->atom(QXcbAtom::Atom_NET_STARTUP_INFO); const int start = sent; const int numBytes = qMin(length - start, 20); @@ -829,7 +839,7 @@ void QXcbScreen::sendStartupMessage(const QByteArray &message) const QRect QXcbScreen::availableGeometry() const { static bool enforceNetWorkarea = !qEnvironmentVariableIsEmpty("QT_RELY_ON_NET_WORKAREA_ATOM"); - bool isMultiHeadSystem = virtualSiblings().length() > 1; + bool isMultiHeadSystem = virtualSiblings().size() > 1; bool useScreenGeometry = isMultiHeadSystem && !enforceNetWorkarea; return useScreenGeometry ? m_geometry : m_availableGeometry; } @@ -869,7 +879,7 @@ QDpi QXcbScreen::logicalDpi() const QPlatformCursor *QXcbScreen::cursor() const { - return m_cursor; + return m_cursor.get(); } void QXcbScreen::setOutput(xcb_randr_output_t outputId, @@ -1085,11 +1095,11 @@ QByteArray QXcbScreen::getEdid() const return result; // Try a bunch of atoms - result = getOutputProperty(atom(QXcbAtom::EDID)); + result = getOutputProperty(atom(QXcbAtom::AtomEDID)); if (result.isEmpty()) - result = getOutputProperty(atom(QXcbAtom::EDID_DATA)); + result = getOutputProperty(atom(QXcbAtom::AtomEDID_DATA)); if (result.isEmpty()) - result = getOutputProperty(atom(QXcbAtom::XFree86_DDC_EDID1_RAWDATA)); + result = getOutputProperty(atom(QXcbAtom::AtomXFree86_DDC_EDID1_RAWDATA)); return result; } diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index 12c13ef80e..49165d3ba4 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -18,6 +18,8 @@ #include <QtGui/private/qedidparser_p.h> +#include <memory> + QT_BEGIN_NAMESPACE class QXcbConnection; @@ -161,6 +163,7 @@ public: void setCrtc(xcb_randr_crtc_t crtc) { m_crtc = crtc; } void setMonitor(xcb_randr_monitor_info_t *monitorInfo, xcb_timestamp_t timestamp = XCB_NONE); QString defaultName(); + bool isPrimaryInXScreen(); void windowShown(QXcbWindow *window); QString windowManagerName() const { return m_virtualDesktop->windowManagerName(); } @@ -212,7 +215,7 @@ private: QRect m_availableGeometry; QColorSpace m_colorSpace; Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation; - QXcbCursor *m_cursor; + std::unique_ptr<QXcbCursor> m_cursor; qreal m_refreshRate = 60.0; QEdidParser m_edid; diff --git a/src/plugins/platforms/xcb/qxcbsessionmanager.cpp b/src/plugins/platforms/xcb/qxcbsessionmanager.cpp index ec305d5030..b4e28ab831 100644 --- a/src/plugins/platforms/xcb/qxcbsessionmanager.cpp +++ b/src/plugins/platforms/xcb/qxcbsessionmanager.cpp @@ -101,19 +101,19 @@ static void sm_setProperty(const QString &name, const QString &value) { QByteArray v = value.toUtf8(); SmPropValue prop; - prop.length = v.length(); + prop.length = v.size(); prop.value = (SmPointer) const_cast<char *>(v.constData()); sm_setProperty(name.toLatin1().data(), SmARRAY8, 1, &prop); } static void sm_setProperty(const QString &name, const QStringList &value) { - SmPropValue *prop = new SmPropValue[value.count()]; + SmPropValue *prop = new SmPropValue[value.size()]; int count = 0; QList<QByteArray> vl; vl.reserve(value.size()); for (QStringList::ConstIterator it = value.begin(); it != value.end(); ++it) { - prop[count].length = (*it).length(); + prop[count].length = (*it).size(); vl.append((*it).toUtf8()); prop[count].value = (char*)vl.constLast().data(); ++count; diff --git a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp index e184c07128..84d2d73f91 100644 --- a/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp +++ b/src/plugins/platforms/xcb/qxcbsystemtraytracker.cpp @@ -26,7 +26,7 @@ enum { QXcbSystemTrayTracker *QXcbSystemTrayTracker::create(QXcbConnection *connection) { // Selection, tray atoms for GNOME, NET WM Specification - const xcb_atom_t trayAtom = connection->atom(QXcbAtom::_NET_SYSTEM_TRAY_OPCODE); + const xcb_atom_t trayAtom = connection->atom(QXcbAtom::Atom_NET_SYSTEM_TRAY_OPCODE); if (!trayAtom) return nullptr; const QByteArray netSysTray = QByteArrayLiteral("_NET_SYSTEM_TRAY_S") + QByteArray::number(connection->primaryScreenNumber()); @@ -113,7 +113,7 @@ xcb_visualid_t QXcbSystemTrayTracker::netSystemTrayVisual() if (m_trayWindow == XCB_WINDOW_NONE) return XCB_NONE; - xcb_atom_t tray_atom = m_connection->atom(QXcbAtom::_NET_SYSTEM_TRAY_VISUAL); + xcb_atom_t tray_atom = m_connection->atom(QXcbAtom::Atom_NET_SYSTEM_TRAY_VISUAL); // Get the xcb property for the _NET_SYSTEM_TRAY_VISUAL atom auto systray_atom_reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, m_connection->xcb_connection(), diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 0162d058a6..d3e4fa9548 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -6,6 +6,7 @@ #include <QtDebug> #include <QMetaEnum> #include <QScreen> +#include <QtCore/QFileInfo> #include <QtGui/QIcon> #include <QtGui/QRegion> #include <QtGui/private/qhighdpiscaling_p.h> @@ -59,6 +60,7 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window"); +Q_LOGGING_CATEGORY(lcQpaXcbWindow, "qt.qpa.xcb.window"); Q_DECLARE_TYPEINFO(xcb_rectangle_t, Q_PRIMITIVE_TYPE); @@ -94,12 +96,14 @@ const quint32 XEMBED_VERSION = 0; QXcbScreen *QXcbWindow::parentScreen() { - return parent() ? static_cast<QXcbWindow*>(parent())->parentScreen() : xcbScreen(); + return QPlatformWindow::parent() ? static_cast<QXcbWindow*>(QPlatformWindow::parent())->parentScreen() : xcbScreen(); } -//QPlatformWindow::screenForGeometry version that uses deviceIndependentGeometry QXcbScreen *QXcbWindow::initialScreen() const { + // Resolve initial screen via QWindowPrivate::screenForGeometry(), + // which works in platform independent coordinates, as opposed to + // QPlatformWindow::screenForGeometry() that uses native coordinates. QWindowPrivate *windowPrivate = qt_window_private(window()); QScreen *screen = windowPrivate->screenForGeometry(window()->geometry()); return static_cast<QXcbScreen*>(screen->handle()); @@ -131,6 +135,7 @@ void QXcbWindow::setImageFormatForVisual(const xcb_visualtype_t *visual) case 16: qWarning("Using RGB16 fallback, if this works your X11 server is reporting a bad screen format."); m_imageFormat = QImage::Format_RGB16; + break; default: break; } @@ -166,7 +171,7 @@ static inline XTextProperty* qstringToXTP(Display *dpy, const QString& s) tp.value = (uchar*)qcs.data(); tp.encoding = XA_STRING; tp.format = 8; - tp.nitems = qcs.length(); + tp.nitems = qcs.size(); free_prop = false; } return &tp; @@ -221,6 +226,7 @@ enum : quint32 { void QXcbWindow::create() { + xcb_window_t old_m_window = m_window; destroy(); m_windowState = Qt::WindowNoState; @@ -229,8 +235,8 @@ void QXcbWindow::create() Qt::WindowType type = window()->type(); QXcbScreen *currentScreen = xcbScreen(); - QXcbScreen *platformScreen = parent() ? parentScreen() : initialScreen(); - QRect rect = parent() + QXcbScreen *platformScreen = QPlatformWindow::parent() ? parentScreen() : initialScreen(); + QRect rect = QPlatformWindow::parent() ? QHighDpi::toNativeLocalPosition(window()->geometry(), platformScreen) : QHighDpi::toNativePixels(window()->geometry(), platformScreen); @@ -253,11 +259,6 @@ void QXcbWindow::create() return; } - QPlatformWindow::setGeometry(rect); - - if (platformScreen != currentScreen) - QWindowSystemInterface::handleWindowScreenChanged(window(), platformScreen->QPlatformScreen::screen()); - const QSize minimumSize = windowMinimumSize(); if (rect.width() > 0 || rect.height() > 0) { rect.setWidth(qBound(1, rect.width(), XCOORD_MAX)); @@ -269,12 +270,17 @@ void QXcbWindow::create() rect.setHeight(QHighDpi::toNativePixels(int(defaultWindowHeight), platformScreen->QPlatformScreen::screen())); } + QPlatformWindow::setGeometry(rect); + + if (platformScreen != currentScreen) + QWindowSystemInterface::handleWindowScreenChanged(window(), platformScreen->QPlatformScreen::screen()); + xcb_window_t xcb_parent_id = platformScreen->root(); - if (parent()) { - xcb_parent_id = static_cast<QXcbWindow *>(parent())->xcb_window(); - m_embedded = parent()->isForeignWindow(); + if (QPlatformWindow::parent()) { + xcb_parent_id = static_cast<QXcbWindow *>(QPlatformWindow::parent())->xcb_window(); + m_embedded = QPlatformWindow::parent()->isForeignWindow(); - QSurfaceFormat parentFormat = parent()->window()->requestedFormat(); + QSurfaceFormat parentFormat = QPlatformWindow::parent()->window()->requestedFormat(); if (window()->surfaceType() != QSurface::OpenGLSurface && parentFormat.hasAlpha()) { window()->setFormat(parentFormat); } @@ -292,16 +298,16 @@ void QXcbWindow::create() qWarning() << "Failed to use requested visual id."; } - if (parent()) { + if (QPlatformWindow::parent()) { // When using a Vulkan QWindow via QWidget::createWindowContainer() we // must make sure the visuals are compatible. Now, the parent will be // of RasterGLSurface which typically chooses a GLX/EGL compatible // visual which may not be what the Vulkan window would choose. // Therefore, take the parent's visual. if (window()->surfaceType() == QSurface::VulkanSurface - && parent()->window()->surfaceType() != QSurface::VulkanSurface) + && QPlatformWindow::parent()->window()->surfaceType() != QSurface::VulkanSurface) { - visual = platformScreen->visualForId(static_cast<QXcbWindow *>(parent())->visualId()); + visual = platformScreen->visualForId(static_cast<QXcbWindow *>(QPlatformWindow::parent())->visualId()); } } @@ -358,20 +364,20 @@ void QXcbWindow::create() xcb_atom_t properties[5]; int propertyCount = 0; - properties[propertyCount++] = atom(QXcbAtom::WM_DELETE_WINDOW); - properties[propertyCount++] = atom(QXcbAtom::WM_TAKE_FOCUS); - properties[propertyCount++] = atom(QXcbAtom::_NET_WM_PING); + properties[propertyCount++] = atom(QXcbAtom::AtomWM_DELETE_WINDOW); + properties[propertyCount++] = atom(QXcbAtom::AtomWM_TAKE_FOCUS); + properties[propertyCount++] = atom(QXcbAtom::Atom_NET_WM_PING); if (connection()->hasXSync()) - properties[propertyCount++] = atom(QXcbAtom::_NET_WM_SYNC_REQUEST); + properties[propertyCount++] = atom(QXcbAtom::Atom_NET_WM_SYNC_REQUEST); if (window()->flags() & Qt::WindowContextHelpButtonHint) - properties[propertyCount++] = atom(QXcbAtom::_NET_WM_CONTEXT_HELP); + properties[propertyCount++] = atom(QXcbAtom::Atom_NET_WM_CONTEXT_HELP); xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - atom(QXcbAtom::WM_PROTOCOLS), + atom(QXcbAtom::AtomWM_PROTOCOLS), XCB_ATOM_ATOM, 32, propertyCount, @@ -382,10 +388,35 @@ void QXcbWindow::create() const QByteArray wmClass = QXcbIntegration::instance()->wmClass(); if (!wmClass.isEmpty()) { xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, - m_window, atom(QXcbAtom::WM_CLASS), + m_window, atom(QXcbAtom::AtomWM_CLASS), XCB_ATOM_STRING, 8, wmClass.size(), wmClass.constData()); } + QString desktopFileName = QGuiApplication::desktopFileName(); + if (QGuiApplication::desktopFileName().isEmpty()) { + QFileInfo fi = QFileInfo(QCoreApplication::instance()->applicationFilePath()); + QStringList domainName = + QCoreApplication::instance()->organizationDomain().split(QLatin1Char('.'), + Qt::SkipEmptyParts); + + if (domainName.isEmpty()) { + desktopFileName = fi.baseName(); + } else { + for (int i = 0; i < domainName.size(); ++i) + desktopFileName.prepend(QLatin1Char('.')).prepend(domainName.at(i)); + desktopFileName.append(fi.baseName()); + } + } + if (!desktopFileName.isEmpty()) { + const QByteArray dfName = desktopFileName.toUtf8(); + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, + m_window, atom(QXcbAtom::Atom_KDE_NET_WM_DESKTOP_FILE), + atom(QXcbAtom::AtomUTF8_STRING), 8, dfName.size(), dfName.constData()); + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, + m_window, atom(QXcbAtom::Atom_GTK_APPLICATION_ID), + atom(QXcbAtom::AtomUTF8_STRING), 8, dfName.size(), dfName.constData()); + } + if (connection()->hasXSync()) { m_syncCounter = xcb_generate_id(xcb_connection()); xcb_sync_create_counter(xcb_connection(), m_syncCounter, m_syncValue); @@ -393,7 +424,7 @@ void QXcbWindow::create() xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - atom(QXcbAtom::_NET_WM_SYNC_REQUEST_COUNTER), + atom(QXcbAtom::Atom_NET_WM_SYNC_REQUEST_COUNTER), XCB_ATOM_CARDINAL, 32, 1, @@ -403,13 +434,13 @@ void QXcbWindow::create() // set the PID to let the WM kill the application if unresponsive quint32 pid = getpid(); xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - atom(QXcbAtom::_NET_WM_PID), XCB_ATOM_CARDINAL, 32, + atom(QXcbAtom::Atom_NET_WM_PID), XCB_ATOM_CARDINAL, 32, 1, &pid); const QByteArray clientMachine = QSysInfo::machineHostName().toLocal8Bit(); if (!clientMachine.isEmpty()) { xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - atom(QXcbAtom::WM_CLIENT_MACHINE), XCB_ATOM_STRING, 8, + atom(QXcbAtom::AtomWM_CLIENT_MACHINE), XCB_ATOM_STRING, 8, clientMachine.size(), clientMachine.constData()); } @@ -423,14 +454,14 @@ void QXcbWindow::create() xcb_window_t leader = connection()->clientLeader(); xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - atom(QXcbAtom::WM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32, + atom(QXcbAtom::AtomWM_CLIENT_LEADER), XCB_ATOM_WINDOW, 32, 1, &leader); /* Add XEMBED info; this operation doesn't initiate the embedding. */ quint32 data[] = { XEMBED_VERSION, XEMBED_MAPPED }; xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - atom(QXcbAtom::_XEMBED_INFO), - atom(QXcbAtom::_XEMBED_INFO), + atom(QXcbAtom::Atom_XEMBED_INFO), + atom(QXcbAtom::Atom_XEMBED_INFO), 32, 2, (void *)data); if (connection()->hasXInput2()) @@ -440,10 +471,8 @@ void QXcbWindow::create() setWindowFlags(window()->flags()); setWindowTitle(window()->title()); -#if QT_CONFIG(xcb_xlib) // force sync to read outstanding requests - see QTBUG-29106 - XSync(static_cast<Display*>(platformScreen->connection()->xlib_display()), false); -#endif + connection()->sync(); #if QT_CONFIG(draganddrop) connection()->drag()->dndEnable(this, true); @@ -465,6 +494,17 @@ void QXcbWindow::create() if (m_trayIconWindow) m_embedded = requestSystemTrayWindowDock(); + + if (m_window != old_m_window) { + if (!m_wmTransientForChildren.isEmpty()) { + QList<QPointer<QXcbWindow>> transientChildren = m_wmTransientForChildren; + m_wmTransientForChildren.clear(); + for (auto transientChild : transientChildren) { + if (transientChild) + transientChild->updateWmTransientFor(); + } + } + } } QXcbWindow::~QXcbWindow() @@ -472,6 +512,22 @@ QXcbWindow::~QXcbWindow() destroy(); } +QXcbForeignWindow::QXcbForeignWindow(QWindow *window, WId nativeHandle) + : QXcbWindow(window) +{ + m_window = nativeHandle; + + // Reflect the foreign window's geometry as our own + if (auto geometry = Q_XCB_REPLY(xcb_get_geometry, xcb_connection(), m_window)) { + QRect nativeGeometry(geometry->x, geometry->y, geometry->width, geometry->height); + QPlatformWindow::setGeometry(nativeGeometry); + } + + // And reparent, if we have a parent already + if (QPlatformWindow::parent()) + setParent(QPlatformWindow::parent()); +} + QXcbForeignWindow::~QXcbForeignWindow() { // Clear window so that destroy() does not affect it @@ -489,12 +545,14 @@ void QXcbWindow::destroy() doFocusOut(); if (connection()->mouseGrabber() == this) connection()->setMouseGrabber(nullptr); + if (connection()->mousePressWindow() == this) + connection()->setMousePressWindow(nullptr); if (m_syncCounter && connection()->hasXSync()) xcb_sync_destroy_counter(xcb_connection(), m_syncCounter); if (m_window) { if (m_netWmUserTimeWindow) { - xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW)); + xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::Atom_NET_WM_USER_TIME_WINDOW)); // Some window managers, like metacity, do XSelectInput on the _NET_WM_USER_TIME_WINDOW window, // without trapping BadWindow (which crashes when the user time window is destroyed). connection()->sync(); @@ -515,12 +573,14 @@ void QXcbWindow::destroy() void QXcbWindow::setGeometry(const QRect &rect) { + setWindowState(Qt::WindowNoState); + QPlatformWindow::setGeometry(rect); propagateSizeHints(); QXcbScreen *currentScreen = xcbScreen(); - QXcbScreen *newScreen = parent() ? parentScreen() : static_cast<QXcbScreen*>(screenForGeometry(rect)); + QXcbScreen *newScreen = QPlatformWindow::parent() ? parentScreen() : static_cast<QXcbScreen*>(screenForGeometry(rect)); if (!newScreen) newScreen = xcbScreen(); @@ -561,9 +621,9 @@ void QXcbWindow::setGeometry(const QRect &rect) QMargins QXcbWindow::frameMargins() const { if (m_dirtyFrameMargins) { - if (connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_FRAME_EXTENTS))) { + if (connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::Atom_NET_FRAME_EXTENTS))) { auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, m_window, - atom(QXcbAtom::_NET_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 0, 4); + atom(QXcbAtom::Atom_NET_FRAME_EXTENTS), XCB_ATOM_CARDINAL, 0, 4); if (reply && reply->type == XCB_ATOM_CARDINAL && reply->format == 32 && reply->value_len == 4) { quint32 *data = (quint32 *)xcb_get_property_value(reply.get()); // _NET_FRAME_EXTENTS format is left, right, top, bottom @@ -639,6 +699,44 @@ void QXcbWindow::setVisible(bool visible) hide(); } +void QXcbWindow::updateWmTransientFor() +{ + xcb_window_t transientXcbParent = XCB_NONE; + if (isTransient(window())) { + QWindow *tp = window()->transientParent(); + if (tp && tp->handle()) { + QXcbWindow *handle = static_cast<QXcbWindow *>(tp->handle()); + transientXcbParent = tp->handle()->winId(); + if (transientXcbParent) { + handle->registerWmTransientForChild(this); + qCDebug(lcQpaXcbWindow) << Q_FUNC_INFO << static_cast<QPlatformWindow *>(handle) + << " registerWmTransientForChild " << static_cast<QPlatformWindow *>(this); + } + } + // Default to client leader if there is no transient parent, else modal dialogs can + // be hidden by their parents. + if (!transientXcbParent) + transientXcbParent = connection()->clientLeader(); + if (transientXcbParent) { // ICCCM 4.1.2.6 + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, + XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32, + 1, &transientXcbParent); + qCDebug(lcQpaXcbWindow, "0x%x added XCB_ATOM_WM_TRANSIENT_FOR 0x%x", m_window, transientXcbParent); + } + } + if (!transientXcbParent) + xcb_delete_property(xcb_connection(), m_window, XCB_ATOM_WM_TRANSIENT_FOR); +} + +void QXcbWindow::registerWmTransientForChild(QXcbWindow *child) +{ + if (!child) + return; + + if (!m_wmTransientForChildren.contains(child)) + m_wmTransientForChildren.append(child); +} + void QXcbWindow::show() { if (window()->isTopLevel()) { @@ -652,23 +750,7 @@ void QXcbWindow::show() propagateSizeHints(); // update WM_TRANSIENT_FOR - xcb_window_t transientXcbParent = 0; - if (isTransient(window())) { - const QWindow *tp = window()->transientParent(); - if (tp && tp->handle()) - transientXcbParent = tp->handle()->winId(); - // Default to client leader if there is no transient parent, else modal dialogs can - // be hidden by their parents. - if (!transientXcbParent) - transientXcbParent = connection()->clientLeader(); - if (transientXcbParent) { // ICCCM 4.1.2.6 - xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - XCB_ATOM_WM_TRANSIENT_FOR, XCB_ATOM_WINDOW, 32, - 1, &transientXcbParent); - } - } - if (!transientXcbParent) - xcb_delete_property(xcb_connection(), m_window, XCB_ATOM_WM_TRANSIENT_FOR); + updateWmTransientFor(); // update _NET_WM_STATE setNetWmStateOnUnmappedWindow(); @@ -779,7 +861,7 @@ void QXcbWindow::doFocusIn() return; QWindow *w = static_cast<QWindowPrivate *>(QObjectPrivate::get(window()))->eventReceiver(); connection()->setFocusWindow(w); - QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason); + QWindowSystemInterface::handleFocusWindowChanged(w, Qt::ActiveWindowFocusReason); } void QXcbWindow::doFocusOut() @@ -822,29 +904,29 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates() NetWmStates result; auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(), - 0, m_window, atom(QXcbAtom::_NET_WM_STATE), + 0, m_window, atom(QXcbAtom::Atom_NET_WM_STATE), XCB_ATOM_ATOM, 0, 1024); if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) { const xcb_atom_t *states = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get())); const xcb_atom_t *statesEnd = states + reply->length; - if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_ABOVE))) + if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::Atom_NET_WM_STATE_ABOVE))) result |= NetWmStateAbove; - if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_BELOW))) + if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::Atom_NET_WM_STATE_BELOW))) result |= NetWmStateBelow; - if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN))) + if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN))) result |= NetWmStateFullScreen; - if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ))) + if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ))) result |= NetWmStateMaximizedHorz; - if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT))) + if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT))) result |= NetWmStateMaximizedVert; - if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_MODAL))) + if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::Atom_NET_WM_STATE_MODAL))) result |= NetWmStateModal; - if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP))) + if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::Atom_NET_WM_STATE_STAYS_ON_TOP))) result |= NetWmStateStaysOnTop; - if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION))) + if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::Atom_NET_WM_STATE_DEMANDS_ATTENTION))) result |= NetWmStateDemandsAttention; - if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::_NET_WM_STATE_HIDDEN))) + if (statesEnd != std::find(states, statesEnd, atom(QXcbAtom::Atom_NET_WM_STATE_HIDDEN))) result |= NetWmStateHidden; } else { qCDebug(lcQpaXcb, "getting net wm state (%x), empty\n", m_window); @@ -959,13 +1041,13 @@ void QXcbWindow::setMotifWmHints(Qt::WindowFlags flags) xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - atom(QXcbAtom::_MOTIF_WM_HINTS), - atom(QXcbAtom::_MOTIF_WM_HINTS), + atom(QXcbAtom::Atom_MOTIF_WM_HINTS), + atom(QXcbAtom::Atom_MOTIF_WM_HINTS), 32, 5, &mwmhints); } else { - xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_MOTIF_WM_HINTS)); + xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::Atom_MOTIF_WM_HINTS)); } } @@ -977,7 +1059,7 @@ void QXcbWindow::setNetWmState(bool set, xcb_atom_t one, xcb_atom_t two) event.format = 32; event.sequence = 0; event.window = m_window; - event.type = atom(QXcbAtom::_NET_WM_STATE); + event.type = atom(QXcbAtom::Atom_NET_WM_STATE); event.data.data32[0] = set ? 1 : 0; event.data.data32[1] = one; event.data.data32[2] = two; @@ -993,26 +1075,26 @@ void QXcbWindow::setNetWmState(Qt::WindowStates state) { if ((m_windowState ^ state) & Qt::WindowMaximized) { setNetWmState(state & Qt::WindowMaximized, - atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ), - atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)); + atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ), + atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT)); } if ((m_windowState ^ state) & Qt::WindowFullScreen) - setNetWmState(state & Qt::WindowFullScreen, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)); + setNetWmState(state & Qt::WindowFullScreen, atom(QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN)); } void QXcbWindow::setNetWmState(Qt::WindowFlags flags) { setNetWmState(flags & Qt::WindowStaysOnTopHint, - atom(QXcbAtom::_NET_WM_STATE_ABOVE), - atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP)); - setNetWmState(flags & Qt::WindowStaysOnBottomHint, atom(QXcbAtom::_NET_WM_STATE_BELOW)); + atom(QXcbAtom::Atom_NET_WM_STATE_ABOVE), + atom(QXcbAtom::Atom_NET_WM_STATE_STAYS_ON_TOP)); + setNetWmState(flags & Qt::WindowStaysOnBottomHint, atom(QXcbAtom::Atom_NET_WM_STATE_BELOW)); } void QXcbWindow::setNetWmStateOnUnmappedWindow() { if (Q_UNLIKELY(m_mapped)) - qCWarning(lcQpaXcb()) << "internal error: " << Q_FUNC_INFO << "called on mapped window"; + qCDebug(lcQpaXcb()) << "internal info: " << Q_FUNC_INFO << "called on mapped window"; NetWmStates states; const Qt::WindowFlags flags = window()->flags(); @@ -1047,7 +1129,7 @@ void QXcbWindow::setNetWmStateOnUnmappedWindow() QList<xcb_atom_t> atoms; auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(), - 0, m_window, atom(QXcbAtom::_NET_WM_STATE), + 0, m_window, atom(QXcbAtom::Atom_NET_WM_STATE), XCB_ATOM_ATOM, 0, 1024); if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM && reply->value_len > 0) { const xcb_atom_t *data = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get())); @@ -1055,31 +1137,31 @@ void QXcbWindow::setNetWmStateOnUnmappedWindow() memcpy((void *)&atoms.first(), (void *)data, reply->value_len * sizeof(xcb_atom_t)); } - if (states & NetWmStateAbove && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_ABOVE))) - atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_ABOVE)); - if (states & NetWmStateBelow && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_BELOW))) - atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_BELOW)); - if (states & NetWmStateHidden && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_HIDDEN))) - atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_HIDDEN)); - if (states & NetWmStateFullScreen && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN))) - atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)); - if (states & NetWmStateMaximizedHorz && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ))) - atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ)); - if (states & NetWmStateMaximizedVert && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT))) - atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)); - if (states & NetWmStateModal && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_MODAL))) - atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_MODAL)); - if (states & NetWmStateStaysOnTop && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP))) - atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_STAYS_ON_TOP)); - if (states & NetWmStateDemandsAttention && !atoms.contains(atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION))) - atoms.push_back(atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)); + if (states & NetWmStateAbove && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_ABOVE))) + atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_ABOVE)); + if (states & NetWmStateBelow && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_BELOW))) + atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_BELOW)); + if (states & NetWmStateHidden && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_HIDDEN))) + atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_HIDDEN)); + if (states & NetWmStateFullScreen && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN))) + atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN)); + if (states & NetWmStateMaximizedHorz && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ))) + atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ)); + if (states & NetWmStateMaximizedVert && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT))) + atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT)); + if (states & NetWmStateModal && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_MODAL))) + atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_MODAL)); + if (states & NetWmStateStaysOnTop && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_STAYS_ON_TOP))) + atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_STAYS_ON_TOP)); + if (states & NetWmStateDemandsAttention && !atoms.contains(atom(QXcbAtom::Atom_NET_WM_STATE_DEMANDS_ATTENTION))) + atoms.push_back(atom(QXcbAtom::Atom_NET_WM_STATE_DEMANDS_ATTENTION)); if (atoms.isEmpty()) { - xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_STATE)); + xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::Atom_NET_WM_STATE)); } else { xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - atom(QXcbAtom::_NET_WM_STATE), XCB_ATOM_ATOM, 32, - atoms.count(), atoms.constData()); + atom(QXcbAtom::Atom_NET_WM_STATE), XCB_ATOM_ATOM, 32, + atoms.size(), atoms.constData()); } xcb_flush(xcb_connection()); } @@ -1089,18 +1171,21 @@ void QXcbWindow::setWindowState(Qt::WindowStates state) if (state == m_windowState) return; + Qt::WindowStates unsetState = m_windowState & ~state; + Qt::WindowStates newState = state & ~m_windowState; + // unset old state - if (m_windowState & Qt::WindowMinimized) + if (unsetState & Qt::WindowMinimized) xcb_map_window(xcb_connection(), m_window); - if (m_windowState & Qt::WindowMaximized) + if (unsetState & Qt::WindowMaximized) setNetWmState(false, - atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ), - atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)); - if (m_windowState & Qt::WindowFullScreen) - setNetWmState(false, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)); + atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ), + atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT)); + if (unsetState & Qt::WindowFullScreen) + setNetWmState(false, atom(QXcbAtom::Atom_NET_WM_STATE_FULLSCREEN)); // set new state - if (state & Qt::WindowMinimized) { + if (newState & Qt::WindowMinimized) { { xcb_client_message_event_t event; @@ -1108,7 +1193,7 @@ void QXcbWindow::setWindowState(Qt::WindowStates state) event.format = 32; event.sequence = 0; event.window = m_window; - event.type = atom(QXcbAtom::WM_CHANGE_STATE); + event.type = atom(QXcbAtom::AtomWM_CHANGE_STATE); event.data.data32[0] = XCB_ICCCM_WM_STATE_ICONIC; event.data.data32[1] = 0; event.data.data32[2] = 0; @@ -1121,13 +1206,8 @@ void QXcbWindow::setWindowState(Qt::WindowStates state) } m_minimized = true; } - if (state & Qt::WindowMaximized) - setNetWmState(true, - atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_HORZ), - atom(QXcbAtom::_NET_WM_STATE_MAXIMIZED_VERT)); - if (state & Qt::WindowFullScreen) - setNetWmState(true, atom(QXcbAtom::_NET_WM_STATE_FULLSCREEN)); + // set Maximized && FullScreen state if need setNetWmState(state); xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints_unchecked(xcb_connection(), m_window); @@ -1153,7 +1233,7 @@ void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp) if (timestamp != 0) connection()->setNetWmUserTime(timestamp); - const bool isSupportedByWM = connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW)); + const bool isSupportedByWM = connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::Atom_NET_WM_USER_TIME_WINDOW)); if (m_netWmUserTimeWindow || isSupportedByWM) { if (!m_netWmUserTimeWindow) { m_netWmUserTimeWindow = xcb_generate_id(xcb_connection()); @@ -1168,9 +1248,9 @@ void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp) 0, // value mask nullptr); // value list wid = m_netWmUserTimeWindow; - xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW), + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, atom(QXcbAtom::Atom_NET_WM_USER_TIME_WINDOW), XCB_ATOM_WINDOW, 32, 1, &m_netWmUserTimeWindow); - xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME)); + xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::Atom_NET_WM_USER_TIME)); QXcbWindow::setWindowTitle(connection(), m_netWmUserTimeWindow, QStringLiteral("Qt NET_WM User Time Window")); @@ -1178,14 +1258,14 @@ void QXcbWindow::updateNetWmUserTime(xcb_timestamp_t timestamp) } else if (!isSupportedByWM) { // WM no longer supports it, then we should remove the // _NET_WM_USER_TIME_WINDOW atom. - xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_USER_TIME_WINDOW)); + xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::Atom_NET_WM_USER_TIME_WINDOW)); xcb_destroy_window(xcb_connection(), m_netWmUserTimeWindow); m_netWmUserTimeWindow = XCB_NONE; } else { wid = m_netWmUserTimeWindow; } } - xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, wid, atom(QXcbAtom::_NET_WM_USER_TIME), + xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, wid, atom(QXcbAtom::Atom_NET_WM_USER_TIME), XCB_ATOM_CARDINAL, 32, 1, ×tamp); } @@ -1260,10 +1340,10 @@ void QXcbWindow::setWindowIconText(const QString &title) xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - atom(QXcbAtom::_NET_WM_ICON_NAME), - atom(QXcbAtom::UTF8_STRING), + atom(QXcbAtom::Atom_NET_WM_ICON_NAME), + atom(QXcbAtom::AtomUTF8_STRING), 8, - ba.length(), + ba.size(), ba.constData()); } @@ -1295,23 +1375,24 @@ void QXcbWindow::setWindowIcon(const QIcon &icon) if (!icon_data.isEmpty()) { // Ignore icon exceeding maximum xcb request length - if (icon_data.size() > xcb_get_maximum_request_length(xcb_connection())) { - qWarning("Ignoring window icon: Size %llu exceeds maximum xcb request length %u.", - icon_data.size(), xcb_get_maximum_request_length(xcb_connection())); + if (quint64(icon_data.size()) > quint64(xcb_get_maximum_request_length(xcb_connection()))) { + qWarning() << "Ignoring window icon" << icon_data.size() + << "exceeds maximum xcb request length" + << xcb_get_maximum_request_length(xcb_connection()); return; } xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - atom(QXcbAtom::_NET_WM_ICON), - atom(QXcbAtom::CARDINAL), + atom(QXcbAtom::Atom_NET_WM_ICON), + atom(QXcbAtom::AtomCARDINAL), 32, icon_data.size(), (unsigned char *) icon_data.data()); } else { xcb_delete_property(xcb_connection(), m_window, - atom(QXcbAtom::_NET_WM_ICON)); + atom(QXcbAtom::Atom_NET_WM_ICON)); } } @@ -1368,7 +1449,8 @@ void QXcbWindow::propagateSizeHints() qMin(XCOORD_MAX, maximumSize.height())); if (sizeIncrement.width() > 0 || sizeIncrement.height() > 0) { - xcb_icccm_size_hints_set_base_size(&hints, baseSize.width(), baseSize.height()); + if (!baseSize.isNull() && baseSize.isValid()) + xcb_icccm_size_hints_set_base_size(&hints, baseSize.width(), baseSize.height()); xcb_icccm_size_hints_set_resize_inc(&hints, sizeIncrement.width(), sizeIncrement.height()); } @@ -1386,29 +1468,37 @@ void QXcbWindow::requestActivateWindow() return; } - if (!m_mapped) { - m_deferredActivation = true; - return; + { + QMutexLocker locker(&m_mappedMutex); + if (!m_mapped) { + m_deferredActivation = true; + return; + } + m_deferredActivation = false; } - m_deferredActivation = false; updateNetWmUserTime(connection()->time()); QWindow *focusWindow = QGuiApplication::focusWindow(); + xcb_window_t current = XCB_NONE; + if (focusWindow) { + if (QPlatformWindow *pw = focusWindow->handle()) + current = pw->winId(); + } if (window()->isTopLevel() && !(window()->flags() & Qt::X11BypassWindowManagerHint) && (!focusWindow || !window()->isAncestorOf(focusWindow)) - && connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::_NET_ACTIVE_WINDOW))) { + && connection()->wmSupport()->isSupportedByWM(atom(QXcbAtom::Atom_NET_ACTIVE_WINDOW))) { xcb_client_message_event_t event; event.response_type = XCB_CLIENT_MESSAGE; event.format = 32; event.sequence = 0; event.window = m_window; - event.type = atom(QXcbAtom::_NET_ACTIVE_WINDOW); + event.type = atom(QXcbAtom::Atom_NET_ACTIVE_WINDOW); event.data.data32[0] = 1; event.data.data32[1] = connection()->time(); - event.data.data32[2] = focusWindow ? focusWindow->winId() : XCB_NONE; + event.data.data32[2] = current; event.data.data32[3] = 0; event.data.data32[4] = 0; @@ -1432,7 +1522,7 @@ QXcbWindow::WindowTypes QXcbWindow::wmWindowTypes() const WindowTypes result; auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, xcb_connection(), - 0, m_window, atom(QXcbAtom::_NET_WM_WINDOW_TYPE), + 0, m_window, atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM, 0, 1024); if (reply && reply->format == 32 && reply->type == XCB_ATOM_ATOM) { const xcb_atom_t *types = static_cast<const xcb_atom_t *>(xcb_get_property_value(reply.get())); @@ -1440,49 +1530,49 @@ QXcbWindow::WindowTypes QXcbWindow::wmWindowTypes() const for (; types != types_end; types++) { QXcbAtom::Atom type = connection()->qatom(*types); switch (type) { - case QXcbAtom::_NET_WM_WINDOW_TYPE_NORMAL: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NORMAL: result |= WindowType::Normal; break; - case QXcbAtom::_NET_WM_WINDOW_TYPE_DESKTOP: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DESKTOP: result |= WindowType::Desktop; break; - case QXcbAtom::_NET_WM_WINDOW_TYPE_DOCK: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DOCK: result |= WindowType::Dock; break; - case QXcbAtom::_NET_WM_WINDOW_TYPE_TOOLBAR: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLBAR: result |= WindowType::Toolbar; break; - case QXcbAtom::_NET_WM_WINDOW_TYPE_MENU: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_MENU: result |= WindowType::Menu; break; - case QXcbAtom::_NET_WM_WINDOW_TYPE_UTILITY: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_UTILITY: result |= WindowType::Utility; break; - case QXcbAtom::_NET_WM_WINDOW_TYPE_SPLASH: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_SPLASH: result |= WindowType::Splash; break; - case QXcbAtom::_NET_WM_WINDOW_TYPE_DIALOG: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DIALOG: result |= WindowType::Dialog; break; - case QXcbAtom::_NET_WM_WINDOW_TYPE_DROPDOWN_MENU: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DROPDOWN_MENU: result |= WindowType::DropDownMenu; break; - case QXcbAtom::_NET_WM_WINDOW_TYPE_POPUP_MENU: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_POPUP_MENU: result |= WindowType::PopupMenu; break; - case QXcbAtom::_NET_WM_WINDOW_TYPE_TOOLTIP: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLTIP: result |= WindowType::Tooltip; break; - case QXcbAtom::_NET_WM_WINDOW_TYPE_NOTIFICATION: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NOTIFICATION: result |= WindowType::Notification; break; - case QXcbAtom::_NET_WM_WINDOW_TYPE_COMBO: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_COMBO: result |= WindowType::Combo; break; - case QXcbAtom::_NET_WM_WINDOW_TYPE_DND: + case QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DND: result |= WindowType::Dnd; break; - case QXcbAtom::_KDE_NET_WM_WINDOW_TYPE_OVERRIDE: + case QXcbAtom::Atom_KDE_NET_WM_WINDOW_TYPE_OVERRIDE: result |= WindowType::KdeOverride; break; default: @@ -1499,41 +1589,41 @@ void QXcbWindow::setWmWindowType(WindowTypes types, Qt::WindowFlags flags) // manual selection 1 (these are never set by Qt and take precedence) if (types & WindowType::Normal) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_NORMAL)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NORMAL)); if (types & WindowType::Desktop) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_DESKTOP)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DESKTOP)); if (types & WindowType::Dock) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_DOCK)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DOCK)); if (types & WindowType::Notification) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_NOTIFICATION)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NOTIFICATION)); // manual selection 2 (Qt uses these during auto selection); if (types & WindowType::Utility) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_UTILITY)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_UTILITY)); if (types & WindowType::Splash) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_SPLASH)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_SPLASH)); if (types & WindowType::Dialog) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_DIALOG)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DIALOG)); if (types & WindowType::Tooltip) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_TOOLTIP)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLTIP)); if (types & WindowType::KdeOverride) - atoms.append(atom(QXcbAtom::_KDE_NET_WM_WINDOW_TYPE_OVERRIDE)); + atoms.append(atom(QXcbAtom::Atom_KDE_NET_WM_WINDOW_TYPE_OVERRIDE)); // manual selection 3 (these can be set by Qt, but don't have a // corresponding Qt::WindowType). note that order of the *MENU // atoms is important if (types & WindowType::Menu) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_MENU)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_MENU)); if (types & WindowType::DropDownMenu) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DROPDOWN_MENU)); if (types & WindowType::PopupMenu) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_POPUP_MENU)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_POPUP_MENU)); if (types & WindowType::Toolbar) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_TOOLBAR)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLBAR)); if (types & WindowType::Combo) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_COMBO)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_COMBO)); if (types & WindowType::Dnd) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_DND)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DND)); // automatic selection Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask)); @@ -1541,20 +1631,20 @@ void QXcbWindow::setWmWindowType(WindowTypes types, Qt::WindowFlags flags) case Qt::Dialog: case Qt::Sheet: if (!(types & WindowType::Dialog)) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_DIALOG)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_DIALOG)); break; case Qt::Tool: case Qt::Drawer: if (!(types & WindowType::Utility)) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_UTILITY)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_UTILITY)); break; case Qt::ToolTip: if (!(types & WindowType::Tooltip)) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_TOOLTIP)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_TOOLTIP)); break; case Qt::SplashScreen: if (!(types & WindowType::Splash)) - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_SPLASH)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_SPLASH)); break; default: break; @@ -1562,20 +1652,20 @@ void QXcbWindow::setWmWindowType(WindowTypes types, Qt::WindowFlags flags) if ((flags & Qt::FramelessWindowHint) && !(types & WindowType::KdeOverride)) { // override netwm type - quick and easy for KDE noborder - atoms.append(atom(QXcbAtom::_KDE_NET_WM_WINDOW_TYPE_OVERRIDE)); + atoms.append(atom(QXcbAtom::Atom_KDE_NET_WM_WINDOW_TYPE_OVERRIDE)); } - if (atoms.size() == 1 && atoms.first() == atom(QXcbAtom::_NET_WM_WINDOW_TYPE_NORMAL)) + if (atoms.size() == 1 && atoms.first() == atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NORMAL)) atoms.clear(); else - atoms.append(atom(QXcbAtom::_NET_WM_WINDOW_TYPE_NORMAL)); + atoms.append(atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE_NORMAL)); if (atoms.isEmpty()) { - xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::_NET_WM_WINDOW_TYPE)); + xcb_delete_property(xcb_connection(), m_window, atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE)); } else { xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - atom(QXcbAtom::_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM, 32, - atoms.count(), atoms.constData()); + atom(QXcbAtom::Atom_NET_WM_WINDOW_TYPE), XCB_ATOM_ATOM, 32, + atoms.size(), atoms.constData()); } xcb_flush(xcb_connection()); } @@ -1584,7 +1674,7 @@ void QXcbWindow::setWindowRole(const QString &role) { QByteArray roleData = role.toLatin1(); xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - atom(QXcbAtom::WM_WINDOW_ROLE), XCB_ATOM_STRING, 8, + atom(QXcbAtom::AtomWM_WINDOW_ROLE), XCB_ATOM_STRING, 8, roleData.size(), roleData.constData()); } @@ -1643,15 +1733,15 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even if (event->format != 32) return; - if (event->type == atom(QXcbAtom::WM_PROTOCOLS)) { + if (event->type == atom(QXcbAtom::AtomWM_PROTOCOLS)) { xcb_atom_t protocolAtom = event->data.data32[0]; - if (protocolAtom == atom(QXcbAtom::WM_DELETE_WINDOW)) { + if (protocolAtom == atom(QXcbAtom::AtomWM_DELETE_WINDOW)) { QWindowSystemInterface::handleCloseEvent(window()); - } else if (protocolAtom == atom(QXcbAtom::WM_TAKE_FOCUS)) { + } else if (protocolAtom == atom(QXcbAtom::AtomWM_TAKE_FOCUS)) { connection()->setTime(event->data.data32[1]); relayFocusToModalWindow(); return; - } else if (protocolAtom == atom(QXcbAtom::_NET_WM_PING)) { + } else if (protocolAtom == atom(QXcbAtom::Atom_NET_WM_PING)) { if (event->window == xcbScreen()->root()) return; @@ -1664,14 +1754,14 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even XCB_EVENT_MASK_STRUCTURE_NOTIFY | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT, (const char *)&reply); xcb_flush(xcb_connection()); - } else if (protocolAtom == atom(QXcbAtom::_NET_WM_SYNC_REQUEST)) { + } else if (protocolAtom == atom(QXcbAtom::Atom_NET_WM_SYNC_REQUEST)) { connection()->setTime(event->data.data32[1]); m_syncValue.lo = event->data.data32[2]; m_syncValue.hi = event->data.data32[3]; if (connection()->hasXSync()) m_syncState = SyncReceived; #ifndef QT_NO_WHATSTHIS - } else if (protocolAtom == atom(QXcbAtom::_NET_WM_CONTEXT_HELP)) { + } else if (protocolAtom == atom(QXcbAtom::Atom_NET_WM_CONTEXT_HELP)) { QWindowSystemInterface::handleEnterWhatsThisEvent(); #endif } else { @@ -1679,29 +1769,29 @@ void QXcbWindow::handleClientMessageEvent(const xcb_client_message_event_t *even connection()->atomName(protocolAtom).constData()); } #if QT_CONFIG(draganddrop) - } else if (event->type == atom(QXcbAtom::XdndEnter)) { + } else if (event->type == atom(QXcbAtom::AtomXdndEnter)) { connection()->drag()->handleEnter(this, event); - } else if (event->type == atom(QXcbAtom::XdndPosition)) { + } else if (event->type == atom(QXcbAtom::AtomXdndPosition)) { connection()->drag()->handlePosition(this, event); - } else if (event->type == atom(QXcbAtom::XdndLeave)) { + } else if (event->type == atom(QXcbAtom::AtomXdndLeave)) { connection()->drag()->handleLeave(this, event); - } else if (event->type == atom(QXcbAtom::XdndDrop)) { + } else if (event->type == atom(QXcbAtom::AtomXdndDrop)) { connection()->drag()->handleDrop(this, event); #endif - } else if (event->type == atom(QXcbAtom::_XEMBED)) { + } else if (event->type == atom(QXcbAtom::Atom_XEMBED)) { handleXEmbedMessage(event); - } else if (event->type == atom(QXcbAtom::_NET_ACTIVE_WINDOW)) { + } else if (event->type == atom(QXcbAtom::Atom_NET_ACTIVE_WINDOW)) { doFocusIn(); - } else if (event->type == atom(QXcbAtom::MANAGER) - || event->type == atom(QXcbAtom::_NET_WM_STATE) - || event->type == atom(QXcbAtom::WM_CHANGE_STATE)) { + } else if (event->type == atom(QXcbAtom::AtomMANAGER) + || event->type == atom(QXcbAtom::Atom_NET_WM_STATE) + || event->type == atom(QXcbAtom::AtomWM_CHANGE_STATE)) { // Ignore _NET_WM_STATE, MANAGER which are relate to tray icons // and other messages. - } else if (event->type == atom(QXcbAtom::_COMPIZ_DECOR_PENDING) - || event->type == atom(QXcbAtom::_COMPIZ_DECOR_REQUEST) - || event->type == atom(QXcbAtom::_COMPIZ_DECOR_DELETE_PIXMAP) - || event->type == atom(QXcbAtom::_COMPIZ_TOOLKIT_ACTION) - || event->type == atom(QXcbAtom::_GTK_LOAD_ICONTHEMES)) { + } else if (event->type == atom(QXcbAtom::Atom_COMPIZ_DECOR_PENDING) + || event->type == atom(QXcbAtom::Atom_COMPIZ_DECOR_REQUEST) + || event->type == atom(QXcbAtom::Atom_COMPIZ_DECOR_DELETE_PIXMAP) + || event->type == atom(QXcbAtom::Atom_COMPIZ_TOOLKIT_ACTION) + || event->type == atom(QXcbAtom::Atom_GTK_LOAD_ICONTHEMES)) { //silence the _COMPIZ and _GTK messages for now } else { qCWarning(lcQpaXcb) << "Unhandled client message: " << connection()->atomName(event->type); @@ -1712,7 +1802,7 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t * { bool fromSendEvent = (event->response_type & 0x80); QPoint pos(event->x, event->y); - if (!parent() && !fromSendEvent) { + if (!QPlatformWindow::parent() && !fromSendEvent) { // Do not trust the position, query it instead. auto reply = Q_XCB_REPLY(xcb_translate_coordinates, xcb_connection(), xcb_window(), xcbScreen()->root(), 0, 0); @@ -1723,7 +1813,7 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t * } const QRect actualGeometry = QRect(pos, QSize(event->width, event->height)); - QPlatformScreen *newScreen = parent() ? parent()->screen() : screenForGeometry(actualGeometry); + QPlatformScreen *newScreen = QPlatformWindow::parent() ? QPlatformWindow::parent()->screen() : screenForGeometry(actualGeometry); if (!newScreen) return; @@ -1799,8 +1889,11 @@ QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event) { if (event->window == m_window) { + m_mappedMutex.lock(); m_mapped = true; - if (m_deferredActivation) + const bool deferredActivation = m_deferredActivation; + m_mappedMutex.unlock(); + if (deferredActivation) requestActivateWindow(); QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size())); @@ -1810,7 +1903,9 @@ void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event) void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event) { if (event->window == m_window) { + m_mappedMutex.lock(); m_mapped = false; + m_mappedMutex.unlock(); QWindowSystemInterface::handleExposeEvent(window(), QRegion()); } } @@ -1833,7 +1928,7 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in if (m_embedded && !m_trayIconWindow) { if (window() != QGuiApplication::focusWindow()) { - const QXcbWindow *container = static_cast<const QXcbWindow *>(parent()); + const QXcbWindow *container = static_cast<const QXcbWindow *>(QPlatformWindow::parent()); Q_ASSERT(container != nullptr); sendXEmbedMessage(container->xcb_window(), XEMBED_REQUEST_FOCUS); @@ -1877,8 +1972,10 @@ void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x, return; } - if (connection()->buttonState() == Qt::NoButton) + if (connection()->buttonState() == Qt::NoButton) { connection()->setMousePressWindow(nullptr); + m_ignorePressedWindowOnMouseLeave = false; + } handleMouseEvent(timestamp, local, global, modifiers, type, source); } @@ -1898,10 +1995,10 @@ static inline bool doCheckUnGrabAncestor(QXcbConnection *conn) return true; } -static bool ignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn = nullptr) +static bool ignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn) { return ((doCheckUnGrabAncestor(conn) - && mode == XCB_NOTIFY_MODE_GRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) + && mode == XCB_NOTIFY_MODE_GRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) || (mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_INFERIOR) || detail == XCB_NOTIFY_DETAIL_VIRTUAL || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL); @@ -1921,14 +2018,18 @@ void QXcbWindow::handleEnterNotifyEvent(int event_x, int event_y, int root_x, in { connection()->setTime(timestamp); - const QPoint global = QPoint(root_x, root_y); - - if (ignoreEnterEvent(mode, detail, connection()) || connection()->mousePressWindow()) + if (ignoreEnterEvent(mode, detail, connection()) + || (connection()->mousePressWindow() && !m_ignorePressedWindowOnMouseLeave)) { return; + } // Updates scroll valuators, as user might have done some scrolling outside our X client. connection()->xi2UpdateScrollingDevices(); + if (mode == XCB_NOTIFY_MODE_UNGRAB && connection()->queryMouseButtons() != Qt::NoButton) + m_ignorePressedWindowOnMouseLeave = true; + + const QPoint global = QPoint(root_x, root_y); const QPoint local(event_x, event_y); QWindowSystemInterface::handleEnterEvent(window(), local, global); } @@ -1938,8 +2039,11 @@ void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y, { connection()->setTime(timestamp); - if (ignoreLeaveEvent(mode, detail, connection()) || connection()->mousePressWindow()) + QXcbWindow *mousePressWindow = connection()->mousePressWindow(); + if (ignoreLeaveEvent(mode, detail, connection()) + || (mousePressWindow && !m_ignorePressedWindowOnMouseLeave)) { return; + } // check if enter event is buffered auto event = connection()->eventQueue()->peek([](xcb_generic_event_t *event, int type) { @@ -1957,6 +2061,8 @@ void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y, QWindowSystemInterface::handleEnterLeaveEvent(enterWindow->window(), window(), local, global); } else { QWindowSystemInterface::handleLeaveEvent(window()); + if (m_ignorePressedWindowOnMouseLeave) + connection()->setMousePressWindow(nullptr); } free(enter); @@ -2133,17 +2239,17 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE; - if (event->atom == atom(QXcbAtom::_NET_WM_STATE) || event->atom == atom(QXcbAtom::WM_STATE)) { + if (event->atom == atom(QXcbAtom::Atom_NET_WM_STATE) || event->atom == atom(QXcbAtom::AtomWM_STATE)) { if (propertyDeleted) return; Qt::WindowStates newState = Qt::WindowNoState; - if (event->atom == atom(QXcbAtom::WM_STATE)) { // WM_STATE: Quick check for 'Minimize'. + if (event->atom == atom(QXcbAtom::AtomWM_STATE)) { // WM_STATE: Quick check for 'Minimize'. auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), - 0, m_window, atom(QXcbAtom::WM_STATE), + 0, m_window, atom(QXcbAtom::AtomWM_STATE), XCB_ATOM_ANY, 0, 1024); - if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::WM_STATE)) { + if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::AtomWM_STATE)) { const quint32 *data = (const quint32 *)xcb_get_property_value(reply.get()); if (reply->length != 0) m_minimized = (data[0] == XCB_ICCCM_WM_STATE_ICONIC @@ -2173,7 +2279,7 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev connection()->setMouseGrabber(nullptr); } return; - } else if (event->atom == atom(QXcbAtom::_NET_FRAME_EXTENTS)) { + } else if (event->atom == atom(QXcbAtom::Atom_NET_FRAME_EXTENTS)) { m_dirtyFrameMargins = true; } } @@ -2282,7 +2388,7 @@ bool QXcbWindow::windowEvent(QEvent *event) case Qt::BacktabFocusReason: { const QXcbWindow *container = - static_cast<const QXcbWindow *>(parent()); + static_cast<const QXcbWindow *>(QPlatformWindow::parent()); sendXEmbedMessage(container->xcb_window(), focusEvent->reason() == Qt::TabFocusReason ? XEMBED_FOCUS_NEXT : XEMBED_FOCUS_PREV); @@ -2312,7 +2418,7 @@ bool QXcbWindow::startSystemMove() bool QXcbWindow::startSystemMoveResize(const QPoint &pos, int edges) { - const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE); + const xcb_atom_t moveResize = connection()->atom(QXcbAtom::Atom_NET_WM_MOVERESIZE); if (!connection()->wmSupport()->isSupportedByWM(moveResize)) return false; @@ -2360,7 +2466,7 @@ static uint qtEdgesToXcbMoveResizeDirection(Qt::Edges edges) void QXcbWindow::doStartSystemMoveResize(const QPoint &globalPos, int edges) { qCDebug(lcQpaXInputDevices) << "triggered system move or resize via sending _NET_WM_MOVERESIZE client message"; - const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE); + const xcb_atom_t moveResize = connection()->atom(QXcbAtom::Atom_NET_WM_MOVERESIZE); xcb_client_message_event_t xev; xev.response_type = XCB_CLIENT_MESSAGE; xev.type = moveResize; @@ -2393,7 +2499,7 @@ void QXcbWindow::sendXEmbedMessage(xcb_window_t window, quint32 message, event.format = 32; event.sequence = 0; event.window = window; - event.type = atom(QXcbAtom::_XEMBED); + event.type = atom(QXcbAtom::Atom_XEMBED); event.data.data32[0] = connection()->time(); event.data.data32[1] = message; event.data.data32[2] = detail; @@ -2402,15 +2508,15 @@ void QXcbWindow::sendXEmbedMessage(xcb_window_t window, quint32 message, xcb_send_event(xcb_connection(), false, window, XCB_EVENT_MASK_NO_EVENT, (const char *)&event); } -static bool activeWindowChangeQueued(const QWindow *window) +static bool focusWindowChangeQueued(const QWindow *window) { /* Check from window system event queue if the next queued activation * targets a window other than @window. */ - QWindowSystemInterfacePrivate::ActivatedWindowEvent *systemEvent = - static_cast<QWindowSystemInterfacePrivate::ActivatedWindowEvent *> - (QWindowSystemInterfacePrivate::peekWindowSystemEvent(QWindowSystemInterfacePrivate::ActivatedWindow)); - return systemEvent && systemEvent->activated != window; + QWindowSystemInterfacePrivate::FocusWindowEvent *systemEvent = + static_cast<QWindowSystemInterfacePrivate::FocusWindowEvent *> + (QWindowSystemInterfacePrivate::peekWindowSystemEvent(QWindowSystemInterfacePrivate::FocusWindow)); + return systemEvent && systemEvent->focused != window; } void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event) @@ -2440,13 +2546,13 @@ void QXcbWindow::handleXEmbedMessage(const xcb_client_message_event_t *event) break; } connection()->setFocusWindow(window()); - QWindowSystemInterface::handleWindowActivated(window(), reason); + QWindowSystemInterface::handleFocusWindowChanged(window(), reason); break; case XEMBED_FOCUS_OUT: if (window() == QGuiApplication::focusWindow() - && !activeWindowChangeQueued(window())) { + && !focusWindowChangeQueued(window())) { connection()->setFocusWindow(nullptr); - QWindowSystemInterface::handleWindowActivated(nullptr); + QWindowSystemInterface::handleFocusWindowChanged(nullptr); } break; } @@ -2472,7 +2578,7 @@ void QXcbWindow::setOpacity(qreal level) xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, m_window, - atom(QXcbAtom::_NET_WM_WINDOW_OPACITY), + atom(QXcbAtom::Atom_NET_WM_WINDOW_OPACITY), XCB_ATOM_CARDINAL, 32, 1, @@ -2510,7 +2616,7 @@ void QXcbWindow::setAlertState(bool enabled) m_alertState = enabled; - setNetWmState(enabled, atom(QXcbAtom::_NET_WM_STATE_DEMANDS_ATTENTION)); + setNetWmState(enabled, atom(QXcbAtom::Atom_NET_WM_STATE_DEMANDS_ATTENTION)); } uint QXcbWindow::visualId() const @@ -2544,10 +2650,10 @@ void QXcbWindow::setWindowTitle(const QXcbConnection *conn, xcb_window_t window, xcb_change_property(conn->xcb_connection(), XCB_PROP_MODE_REPLACE, window, - conn->atom(QXcbAtom::_NET_WM_NAME), - conn->atom(QXcbAtom::UTF8_STRING), + conn->atom(QXcbAtom::Atom_NET_WM_NAME), + conn->atom(QXcbAtom::AtomUTF8_STRING), 8, - ba.length(), + ba.size(), ba.constData()); #if QT_CONFIG(xcb_xlib) @@ -2561,9 +2667,9 @@ void QXcbWindow::setWindowTitle(const QXcbConnection *conn, xcb_window_t window, QString QXcbWindow::windowTitle(const QXcbConnection *conn, xcb_window_t window) { - const xcb_atom_t utf8Atom = conn->atom(QXcbAtom::UTF8_STRING); + const xcb_atom_t utf8Atom = conn->atom(QXcbAtom::AtomUTF8_STRING); auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, conn->xcb_connection(), - false, window, conn->atom(QXcbAtom::_NET_WM_NAME), + false, window, conn->atom(QXcbAtom::Atom_NET_WM_NAME), utf8Atom, 0, 1024); if (reply && reply->format == 8 && reply->type == utf8Atom) { const char *name = reinterpret_cast<const char *>(xcb_get_property_value(reply.get())); @@ -2571,7 +2677,7 @@ QString QXcbWindow::windowTitle(const QXcbConnection *conn, xcb_window_t window) } reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, conn->xcb_connection(), - false, window, conn->atom(QXcbAtom::WM_NAME), + false, window, conn->atom(QXcbAtom::AtomWM_NAME), XCB_ATOM_STRING, 0, 1024); if (reply && reply->format == 8 && reply->type == XCB_ATOM_STRING) { const char *name = reinterpret_cast<const char *>(xcb_get_property_value(reply.get())); diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index 54a96a7a0a..0c047d569b 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -6,6 +6,8 @@ #include <qpa/qplatformwindow.h> #include <qpa/qplatformwindow_p.h> +#include <QtCore/QObject> +#include <QtCore/QPointer> #include <QtGui/QSurfaceFormat> #include <QtGui/QImage> @@ -20,9 +22,10 @@ class QXcbScreen; class QXcbSyncWindowRequest; class QIcon; -class Q_XCB_EXPORT QXcbWindow : public QXcbObject, public QXcbWindowEventListener, public QPlatformWindow +class Q_XCB_EXPORT QXcbWindow : public QObject, public QXcbObject, public QXcbWindowEventListener, public QPlatformWindow , public QNativeInterface::Private::QXcbWindow { + Q_OBJECT public: enum NetWmState { NetWmStateAbove = 0x1, @@ -118,6 +121,8 @@ public: Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source); void updateNetWmUserTime(xcb_timestamp_t timestamp); + void updateWmTransientFor(); + void registerWmTransientForChild(QXcbWindow *); WindowTypes wmWindowTypes() const; void setWmWindowType(WindowTypes types, Qt::WindowFlags flags); @@ -217,6 +222,7 @@ protected: Qt::WindowStates m_windowState = Qt::WindowNoState; + QMutex m_mappedMutex; bool m_mapped = false; bool m_transparent = false; bool m_deferredActivation = false; @@ -224,6 +230,7 @@ protected: bool m_alertState = false; bool m_minimized = false; bool m_trayIconWindow = false; + bool m_ignorePressedWindowOnMouseLeave = false; xcb_window_t m_netWmUserTimeWindow = XCB_NONE; QSurfaceFormat m_format; @@ -253,13 +260,14 @@ protected: qreal m_sizeHintsScaleFactor = 1.0; RecreationReasons m_recreationReasons = RecreationNotNeeded; + + QList<QPointer<QXcbWindow>> m_wmTransientForChildren; }; class QXcbForeignWindow : public QXcbWindow { public: - QXcbForeignWindow(QWindow *window, WId nativeHandle) - : QXcbWindow(window) { m_window = nativeHandle; } + QXcbForeignWindow(QWindow *window, WId nativeHandle); ~QXcbForeignWindow(); bool isForeignWindow() const override { return true; } diff --git a/src/plugins/platforms/xcb/qxcbwmsupport.cpp b/src/plugins/platforms/xcb/qxcbwmsupport.cpp index 50e85c0aa5..0e3c470c89 100644 --- a/src/plugins/platforms/xcb/qxcbwmsupport.cpp +++ b/src/plugins/platforms/xcb/qxcbwmsupport.cpp @@ -30,7 +30,7 @@ void QXcbWMSupport::updateNetWMAtoms() int offset = 0; int remaining = 0; do { - auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, root, atom(QXcbAtom::_NET_SUPPORTED), XCB_ATOM_ATOM, offset, 1024); + auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, root, atom(QXcbAtom::Atom_NET_SUPPORTED), XCB_ATOM_ATOM, offset, 1024); if (!reply) break; @@ -54,7 +54,7 @@ void QXcbWMSupport::updateVirtualRoots() { net_virtual_roots.clear(); - if (!isSupportedByWM(atom(QXcbAtom::_NET_VIRTUAL_ROOTS))) + if (!isSupportedByWM(atom(QXcbAtom::Atom_NET_VIRTUAL_ROOTS))) return; xcb_window_t root = connection()->primaryScreen()->root(); @@ -62,7 +62,7 @@ void QXcbWMSupport::updateVirtualRoots() int remaining = 0; do { auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), - false, root, atom(QXcbAtom::_NET_VIRTUAL_ROOTS), XCB_ATOM_WINDOW, offset, 1024); + false, root, atom(QXcbAtom::Atom_NET_VIRTUAL_ROOTS), XCB_ATOM_WINDOW, offset, 1024); if (!reply) break; diff --git a/src/plugins/platforms/xcb/qxcbxsettings.cpp b/src/plugins/platforms/xcb/qxcbxsettings.cpp index 62fd13e5b9..6b62864add 100644 --- a/src/plugins/platforms/xcb/qxcbxsettings.cpp +++ b/src/plugins/platforms/xcb/qxcbxsettings.cpp @@ -68,7 +68,7 @@ public: int offset = 0; QByteArray settings; - xcb_atom_t _xsettings_atom = screen->connection()->atom(QXcbAtom::_XSETTINGS_SETTINGS); + xcb_atom_t _xsettings_atom = screen->connection()->atom(QXcbAtom::Atom_XSETTINGS_SETTINGS); while (1) { auto reply = Q_XCB_REPLY_UNCHECKED(xcb_get_property, screen->xcb_connection(), @@ -104,7 +104,7 @@ public: void populateSettings(const QByteArray &xSettings) { - if (xSettings.length() < 12) + if (xSettings.size() < 12) return; char byteOrder = xSettings.at(0); if (byteOrder != XCB_IMAGE_ORDER_LSB_FIRST && byteOrder != XCB_IMAGE_ORDER_MSB_FIRST) { @@ -192,7 +192,7 @@ QXcbXSettings::QXcbXSettings(QXcbVirtualDesktop *screen) auto atom_reply = Q_XCB_REPLY(xcb_intern_atom, screen->xcb_connection(), true, - settings_atom_for_screen.length(), + settings_atom_for_screen.size(), settings_atom_for_screen.constData()); if (!atom_reply) return; |