From 8e506fdd299d2fa18172209bea316f484e234e19 Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Wed, 26 May 2021 10:32:04 +0200 Subject: [PATCH] xcb: fix QWindow::startSystemMove()/Resize() triggered by mouse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can't get mouse release event from master pointers after QXcbWindow::doStartSystemMoveResize() which calls xcb_ungrab_pointer(), it looks like most X11 WMs work as that. So we try to get mouse release event from slave pointers. Based on https://specifications.freedesktop.org/wm-spec/1.4/ar01s04.html , we need to send _NET_WM_MOVERESIZE_CANCEL when we get mouse release event. Task-number: QTBUG-91077 Change-Id: I01e74a01c87b381ee7cd6f20d51a1fa61c0e98fc Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/xcb/qxcbconnection.cpp | 3 ++ src/plugins/platforms/xcb/qxcbconnection.h | 10 +++- .../platforms/xcb/qxcbconnection_xi2.cpp | 47 ++++++++++++++++++- src/plugins/platforms/xcb/qxcbwindow.cpp | 8 ++-- 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index b19b070a4d..58a4a8ea04 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -86,6 +86,7 @@ Q_LOGGING_CATEGORY(lcQpaXDnd, "qt.qpa.xdnd") QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName) : QXcbBasicConnection(displayName) + , m_duringSystemMoveResize(false) , m_canGrabServer(canGrabServer) , m_defaultVisualId(defaultVisualId) , m_nativeInterface(nativeInterface) @@ -592,6 +593,8 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) } case XCB_BUTTON_RELEASE: { auto ev = reinterpret_cast(event); + if (m_duringSystemMoveResize && ev->root != XCB_NONE) + abortSystemMoveResize(ev->root); m_keyboard->updateXKBStateFromCore(ev->state); m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state); setButtonState(translateMouseButton(ev->detail), false); diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 8938d28939..556e6b1002 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -231,10 +231,13 @@ public: Qt::MouseButton xiToQtMouseButton(uint32_t b); void xi2UpdateScrollingDevices(); - bool startSystemMoveResizeForTouch(xcb_window_t window, int edges); - void abortSystemMoveResizeForTouch(); bool isTouchScreen(int id); + bool startSystemMoveResizeForTouch(xcb_window_t window, int edges); + void abortSystemMoveResize(xcb_window_t window); + bool isDuringSystemMoveResize() const; + void setDuringSystemMoveResize(bool during); + bool canGrab() const { return m_canGrabServer; } QXcbGlIntegration *glIntegration() const; @@ -323,12 +326,14 @@ private: static bool xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value); QHash m_touchDevices; + struct StartSystemMoveResizeInfo { xcb_window_t window = XCB_NONE; uint16_t deviceid; uint32_t pointid; int edges; } m_startSystemMoveResizeInfo; + bool m_duringSystemMoveResize; const bool m_canGrabServer; const xcb_visualid_t m_defaultVisualId; @@ -367,6 +372,7 @@ private: mutable bool m_glIntegrationInitialized = false; bool m_xiGrab = false; QList m_xiMasterPointerIds; + QList m_xiSlavePointerIds; xcb_window_t m_qtSelectionOwner = 0; diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index 9abdfd422b..0e79f35d60 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -90,7 +90,7 @@ void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window) } qt_xcb_input_event_mask_t mask; - mask.header.deviceid = XCB_INPUT_DEVICE_ALL_MASTER; + mask.header.deviceid = XCB_INPUT_DEVICE_ALL; mask.header.mask_len = 1; mask.mask = bitMask; xcb_void_cookie_t cookie = @@ -493,6 +493,7 @@ void QXcbConnection::xi2SetupDevices() // already registered break; case XCB_INPUT_DEVICE_TYPE_SLAVE_POINTER: { + m_xiSlavePointerIds.append(deviceInfo->deviceid); QInputDevice *master = const_cast(QInputDevicePrivate::fromId(deviceInfo->attachment)); Q_ASSERT(master); xi2SetupSlavePointerDevice(deviceInfo, false, qobject_cast(master)); @@ -638,6 +639,19 @@ static inline qreal fixed1616ToReal(xcb_input_fp1616_t val) void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) { auto *xiEvent = reinterpret_cast(event); + if (m_xiSlavePointerIds.contains(xiEvent->deviceid)) { + if (!m_duringSystemMoveResize) + return; + if (xiEvent->event == XCB_NONE) + return; + + if (xiEvent->event_type == XCB_INPUT_BUTTON_RELEASE + && xiEvent->detail == XCB_BUTTON_INDEX_1 ) { + abortSystemMoveResize(xiEvent->event); + } else { + return; + } + } int sourceDeviceId = xiEvent->deviceid; // may be the master id qt_xcb_input_device_event_t *xiDeviceEvent = nullptr; xcb_input_enter_event_t *xiEnterEvent = nullptr; @@ -915,9 +929,38 @@ bool QXcbConnection::startSystemMoveResizeForTouch(xcb_window_t window, int edge return false; } -void QXcbConnection::abortSystemMoveResizeForTouch() +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); + xcb_client_message_event_t xev; + xev.response_type = XCB_CLIENT_MESSAGE; + xev.type = moveResize; + xev.sequence = 0; + xev.window = window; + xev.format = 32; + xev.data.data32[0] = 0; + xev.data.data32[1] = 0; + xev.data.data32[2] = 11; // _NET_WM_MOVERESIZE_CANCEL + xev.data.data32[3] = 0; + xev.data.data32[4] = 0; + xcb_send_event(xcb_connection(), false, primaryScreen()->root(), + XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, + (const char *)&xev); + + m_duringSystemMoveResize = false; +} + +bool QXcbConnection::isDuringSystemMoveResize() const +{ + return m_duringSystemMoveResize; +} + +void QXcbConnection::setDuringSystemMoveResize(bool during) +{ + m_duringSystemMoveResize = during; } bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab) diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 2ee5dc300c..122e89f22e 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -2334,14 +2334,11 @@ bool QXcbWindow::startSystemMoveResize(const QPoint &pos, int edges) if (startedByTouch) { if (connection()->isUnity()) { // Unity fails to move/resize via _NET_WM_MOVERESIZE (WM bug?). - connection()->abortSystemMoveResizeForTouch(); + connection()->abortSystemMoveResize(m_window); return false; } // KWin, Openbox, AwesomeWM and Gnome have been tested to work with _NET_WM_MOVERESIZE. } else { // Started by mouse press. - if (connection()->isUnity()) - return false; // _NET_WM_MOVERESIZE on this WM is bouncy (WM bug?). - doStartSystemMoveResize(mapToGlobal(pos), edges); } @@ -2373,6 +2370,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); xcb_client_message_event_t xev; xev.response_type = XCB_CLIENT_MESSAGE; @@ -2392,6 +2390,8 @@ void QXcbWindow::doStartSystemMoveResize(const QPoint &globalPos, int edges) xcb_send_event(connection()->xcb_connection(), false, xcbScreen()->root(), XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, (const char *)&xev); + + connection()->setDuringSystemMoveResize(true); } // Sends an XEmbed message.