diff options
Diffstat (limited to 'src/plugins/platforms/xcb')
24 files changed, 332 insertions, 153 deletions
diff --git a/src/plugins/platforms/xcb/CMakeLists.txt b/src/plugins/platforms/xcb/CMakeLists.txt index 17e723d607..96758e7181 100644 --- a/src/plugins/platforms/xcb/CMakeLists.txt +++ b/src/plugins/platforms/xcb/CMakeLists.txt @@ -5,6 +5,11 @@ ## 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 @@ -57,7 +62,6 @@ 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 @@ -68,11 +72,6 @@ qt_disable_apple_app_extension_api_only(XcbQpaPrivate) ## 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 @@ -165,7 +164,7 @@ endif() qt_internal_add_plugin(QXcbIntegrationPlugin OUTPUT_NAME qxcb PLUGIN_TYPE platforms - DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES xcb + DEFAULT_IF "xcb" IN_LIST QT_QPA_PLATFORMS SOURCES qxcbmain.cpp DEFINES @@ -178,5 +177,5 @@ qt_internal_add_plugin(QXcbIntegrationPlugin 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/xcb_egl/qxcbeglintegration.cpp b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp index 1e93ea6805..133b992cd9 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.cpp @@ -58,6 +58,7 @@ std::optional<VisualInfo> getVisualInfo(xcb_screen_t *screen, QXcbEglIntegration::QXcbEglIntegration() : m_connection(nullptr) , m_egl_display(EGL_NO_DISPLAY) + , m_using_platform_display(false) { qCDebug(lcQpaGl) << "Xcb EGL gl-integration created"; } @@ -80,6 +81,7 @@ bool QXcbEglIntegration::initialize(QXcbConnection *connection) m_egl_display = streamFuncs.get_platform_display(EGL_PLATFORM_X11_KHR, m_connection->xlib_display(), nullptr); + m_using_platform_display = true; } #if QT_CONFIG(egl_x11) @@ -92,16 +94,19 @@ bool QXcbEglIntegration::initialize(QXcbConnection *connection) 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())); 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 ff0804678f..7caf4304bc 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.h +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglintegration.h @@ -39,10 +39,13 @@ public: EGLDisplay eglDisplay() const { return m_egl_display; } + 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 4d87b08db8..bf2ceb96f4 100644 --- a/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp +++ b/src/plugins/platforms/xcb/gl_integrations/xcb_egl/qxcbeglwindow.cpp @@ -7,6 +7,10 @@ #include <QtGui/private/qeglconvenience_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 QXcbEglWindow::QXcbEglWindow(QWindow *window, QXcbEglIntegration *glIntegration) @@ -42,7 +46,20 @@ 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/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 186a3cf9d7..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()); } diff --git a/src/plugins/platforms/xcb/qxcbatom.cpp b/src/plugins/platforms/xcb/qxcbatom.cpp index 09b1fe8a9d..dd5596653c 100644 --- a/src/plugins/platforms/xcb/qxcbatom.cpp +++ b/src/plugins/platforms/xcb/qxcbatom.cpp @@ -111,6 +111,7 @@ 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" @@ -196,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" @@ -230,8 +232,10 @@ void QXcbAtom::initializeAllAtoms(xcb_connection_t *connection) { 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 07dad31d45..bc677eaf3e 100644 --- a/src/plugins/platforms/xcb/qxcbatom.h +++ b/src/plugins/platforms/xcb/qxcbatom.h @@ -112,6 +112,7 @@ public: 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, @@ -199,6 +200,7 @@ public: Atom_COMPIZ_DECOR_REQUEST, Atom_COMPIZ_DECOR_DELETE_PIXMAP, Atom_COMPIZ_TOOLKIT_ACTION, + Atom_GTK_APPLICATION_ID, Atom_GTK_LOAD_ICONTHEMES, AtomAT_SPI_BUS, diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp index 0659cf9fc4..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); diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index 2e103b0eb2..1056c6408f 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -92,9 +92,9 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra 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(); @@ -1141,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 24e684866d..527744fe81 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -183,7 +183,6 @@ public: QXcbSystemTrayTracker *systemTrayTracker() const; Qt::MouseButtons queryMouseButtons() const; - Qt::KeyboardModifiers queryKeyboardModifiers() const; bool isUserInputEvent(xcb_generic_event_t *event) const; @@ -367,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_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index efcea4f93d..4f62a1880b 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -241,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; @@ -453,10 +454,6 @@ void QXcbConnection::xi2SetupSlavePointerDevice(void *info, bool removeExisting, */ 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); @@ -468,14 +465,13 @@ void QXcbConnection::xi2SetupDevices() // 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) { - for (auto it = previousDevices.constBegin(); it != previousDevices.constEnd(); ++it) { - if ((*it)->systemId() == systemId) { - previousDevices.erase(it); // it's a keeper - return false; // not new - } - } - return true; // it's really new + // 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. @@ -540,6 +536,11 @@ void QXcbConnection::xi2SetupDevices() // 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) @@ -765,7 +766,7 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) if (auto device = QPointingDevicePrivate::pointingDeviceById(sourceDeviceId)) xi2HandleScrollEvent(event, device); else - qCWarning(lcQpaXInputEvents) << "scroll event from unregistered device" << Qt::hex << sourceDeviceId; + qCDebug(lcQpaXInputEvents) << "scroll event from unregistered device" << sourceDeviceId; if (xiDeviceEvent) { switch (xiDeviceEvent->event_type) { @@ -812,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; @@ -1274,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; @@ -1525,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, @@ -1533,22 +1547,19 @@ 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 diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index 8f087edc9b..dc9ed46956 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -10,6 +10,7 @@ #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> @@ -288,6 +289,13 @@ QXcbCursor::~QXcbCursor() 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) diff --git a/src/plugins/platforms/xcb/qxcbcursor.h b/src/plugins/platforms/xcb/qxcbcursor.h index 14958f824b..bf26861e8f 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.h +++ b/src/plugins/platforms/xcb/qxcbcursor.h @@ -48,6 +48,8 @@ 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); diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index 3ef37e85c5..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) @@ -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) { @@ -766,7 +769,7 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message } 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); @@ -1006,7 +1009,7 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e return; 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, @@ -1231,7 +1234,7 @@ void QXcbDrag::handleSelectionRequest(const xcb_selection_request_event_t *event bool QXcbDrag::dndEnable(QXcbWindow *w, bool on) { - qCDebug(lcQpaXDnd) << "dndEnable" << w << 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; 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 e32891e814..4dafae31e3 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.cpp +++ b/src/plugins/platforms/xcb/qxcbintegration.cpp @@ -93,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) @@ -197,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(); @@ -337,11 +344,12 @@ 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(); @@ -422,14 +430,9 @@ QPlatformServices *QXcbIntegration::services() const return m_services.data(); } -Qt::KeyboardModifiers QXcbIntegration::queryKeyboardModifiers() const -{ - return m_connection->queryKeyboardModifiers(); -} - -QList<int> QXcbIntegration::possibleKeys(const QKeyEvent *e) const +QPlatformKeyMapper *QXcbIntegration::keyMapper() const { - return m_connection->keyboard()->possibleKeys(e); + return m_connection->keyboard(); } QStringList QXcbIntegration::themeNames() const @@ -472,21 +475,23 @@ QVariant QXcbIntegration::styleHint(QPlatformIntegration::StyleHint hint) const 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 @@ -585,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 abdfe7112c..a1e0c3f3e1 100644 --- a/src/plugins/platforms/xcb/qxcbintegration.h +++ b/src/plugins/platforms/xcb/qxcbintegration.h @@ -74,8 +74,7 @@ public: 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; diff --git a/src/plugins/platforms/xcb/qxcbkeyboard.cpp b/src/plugins/platforms/xcb/qxcbkeyboard.cpp index 0666fac735..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) 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 9d62423071..860d195d13 100644 --- a/src/plugins/platforms/xcb/qxcbmime.cpp +++ b/src/plugins/platforms/xcb/qxcbmime.cpp @@ -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.size() * 2); + data->assign({reinterpret_cast<const char *>(mozUri.data()), mozUri.size() * 2}); } else if (atomName == "application/x-color"_L1) *dataFormat = 16; ret = true; @@ -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(); } } } diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 584f1e5406..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,7 +96,7 @@ 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(); } QXcbScreen *QXcbWindow::initialScreen() const @@ -133,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; } @@ -223,6 +226,7 @@ enum : quint32 { void QXcbWindow::create() { + xcb_window_t old_m_window = m_window; destroy(); m_windowState = Qt::WindowNoState; @@ -231,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); @@ -272,11 +276,11 @@ void QXcbWindow::create() 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); } @@ -294,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()); } } @@ -388,6 +392,31 @@ void QXcbWindow::create() 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); @@ -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,6 +545,8 @@ 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); @@ -522,7 +580,7 @@ void QXcbWindow::setGeometry(const QRect &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(); @@ -641,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()) { @@ -654,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(); @@ -781,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() @@ -1014,7 +1094,7 @@ void QXcbWindow::setNetWmState(Qt::WindowFlags flags) 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(); @@ -1091,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::Atom_NET_WM_STATE_MAXIMIZED_HORZ), atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT)); - if (m_windowState & Qt::WindowFullScreen) + 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; @@ -1123,13 +1206,8 @@ void QXcbWindow::setWindowState(Qt::WindowStates state) } m_minimized = true; } - if (state & Qt::WindowMaximized) - setNetWmState(true, - atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_HORZ), - atom(QXcbAtom::Atom_NET_WM_STATE_MAXIMIZED_VERT)); - if (state & Qt::WindowFullScreen) - setNetWmState(true, atom(QXcbAtom::Atom_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); @@ -1371,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()); } @@ -1389,14 +1468,22 @@ 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) @@ -1411,7 +1498,7 @@ void QXcbWindow::requestActivateWindow() 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; @@ -1715,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); @@ -1726,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; @@ -1802,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())); @@ -1813,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()); } } @@ -1836,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); @@ -1880,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); } @@ -1901,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); @@ -1924,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); } @@ -1941,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) { @@ -1960,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); @@ -2285,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); @@ -2405,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) @@ -2443,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; } 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; } |