xcb: fix QWindow::startSystemMove()/Resize() triggered by mouse

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ø <tor.arne.vestbo@qt.io>
This commit is contained in:
Liang Qi 2021-05-26 10:32:04 +02:00
parent 3ca5b46e5d
commit 8e506fdd29
4 changed files with 60 additions and 8 deletions

View File

@ -86,6 +86,7 @@ Q_LOGGING_CATEGORY(lcQpaXDnd, "qt.qpa.xdnd")
QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName) QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGrabServer, xcb_visualid_t defaultVisualId, const char *displayName)
: QXcbBasicConnection(displayName) : QXcbBasicConnection(displayName)
, m_duringSystemMoveResize(false)
, m_canGrabServer(canGrabServer) , m_canGrabServer(canGrabServer)
, m_defaultVisualId(defaultVisualId) , m_defaultVisualId(defaultVisualId)
, m_nativeInterface(nativeInterface) , m_nativeInterface(nativeInterface)
@ -592,6 +593,8 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event)
} }
case XCB_BUTTON_RELEASE: { case XCB_BUTTON_RELEASE: {
auto ev = reinterpret_cast<xcb_button_release_event_t *>(event); auto ev = reinterpret_cast<xcb_button_release_event_t *>(event);
if (m_duringSystemMoveResize && ev->root != XCB_NONE)
abortSystemMoveResize(ev->root);
m_keyboard->updateXKBStateFromCore(ev->state); m_keyboard->updateXKBStateFromCore(ev->state);
m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state); m_buttonState = (m_buttonState & ~0x7) | translateMouseButtons(ev->state);
setButtonState(translateMouseButton(ev->detail), false); setButtonState(translateMouseButton(ev->detail), false);

View File

@ -231,10 +231,13 @@ public:
Qt::MouseButton xiToQtMouseButton(uint32_t b); Qt::MouseButton xiToQtMouseButton(uint32_t b);
void xi2UpdateScrollingDevices(); void xi2UpdateScrollingDevices();
bool startSystemMoveResizeForTouch(xcb_window_t window, int edges);
void abortSystemMoveResizeForTouch();
bool isTouchScreen(int id); 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; } bool canGrab() const { return m_canGrabServer; }
QXcbGlIntegration *glIntegration() const; QXcbGlIntegration *glIntegration() const;
@ -323,12 +326,14 @@ private:
static bool xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value); static bool xi2GetValuatorValueIfSet(const void *event, int valuatorNum, double *value);
QHash<int, TouchDeviceData> m_touchDevices; QHash<int, TouchDeviceData> m_touchDevices;
struct StartSystemMoveResizeInfo { struct StartSystemMoveResizeInfo {
xcb_window_t window = XCB_NONE; xcb_window_t window = XCB_NONE;
uint16_t deviceid; uint16_t deviceid;
uint32_t pointid; uint32_t pointid;
int edges; int edges;
} m_startSystemMoveResizeInfo; } m_startSystemMoveResizeInfo;
bool m_duringSystemMoveResize;
const bool m_canGrabServer; const bool m_canGrabServer;
const xcb_visualid_t m_defaultVisualId; const xcb_visualid_t m_defaultVisualId;
@ -367,6 +372,7 @@ private:
mutable bool m_glIntegrationInitialized = false; mutable bool m_glIntegrationInitialized = false;
bool m_xiGrab = false; bool m_xiGrab = false;
QList<int> m_xiMasterPointerIds; QList<int> m_xiMasterPointerIds;
QList<int> m_xiSlavePointerIds;
xcb_window_t m_qtSelectionOwner = 0; xcb_window_t m_qtSelectionOwner = 0;

View File

@ -90,7 +90,7 @@ void QXcbConnection::xi2SelectDeviceEvents(xcb_window_t window)
} }
qt_xcb_input_event_mask_t mask; 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.header.mask_len = 1;
mask.mask = bitMask; mask.mask = bitMask;
xcb_void_cookie_t cookie = xcb_void_cookie_t cookie =
@ -493,6 +493,7 @@ void QXcbConnection::xi2SetupDevices()
// already registered // already registered
break; break;
case XCB_INPUT_DEVICE_TYPE_SLAVE_POINTER: { case XCB_INPUT_DEVICE_TYPE_SLAVE_POINTER: {
m_xiSlavePointerIds.append(deviceInfo->deviceid);
QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment)); QInputDevice *master = const_cast<QInputDevice *>(QInputDevicePrivate::fromId(deviceInfo->attachment));
Q_ASSERT(master); Q_ASSERT(master);
xi2SetupSlavePointerDevice(deviceInfo, false, qobject_cast<QPointingDevice *>(master)); xi2SetupSlavePointerDevice(deviceInfo, false, qobject_cast<QPointingDevice *>(master));
@ -638,6 +639,19 @@ static inline qreal fixed1616ToReal(xcb_input_fp1616_t val)
void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event)
{ {
auto *xiEvent = reinterpret_cast<qt_xcb_input_device_event_t *>(event); auto *xiEvent = reinterpret_cast<qt_xcb_input_device_event_t *>(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 int sourceDeviceId = xiEvent->deviceid; // may be the master id
qt_xcb_input_device_event_t *xiDeviceEvent = nullptr; qt_xcb_input_device_event_t *xiDeviceEvent = nullptr;
xcb_input_enter_event_t *xiEnterEvent = nullptr; xcb_input_enter_event_t *xiEnterEvent = nullptr;
@ -915,9 +929,38 @@ bool QXcbConnection::startSystemMoveResizeForTouch(xcb_window_t window, int edge
return false; 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; 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) bool QXcbConnection::xi2SetMouseGrabEnabled(xcb_window_t w, bool grab)

View File

@ -2334,14 +2334,11 @@ bool QXcbWindow::startSystemMoveResize(const QPoint &pos, int edges)
if (startedByTouch) { if (startedByTouch) {
if (connection()->isUnity()) { if (connection()->isUnity()) {
// Unity fails to move/resize via _NET_WM_MOVERESIZE (WM bug?). // Unity fails to move/resize via _NET_WM_MOVERESIZE (WM bug?).
connection()->abortSystemMoveResizeForTouch(); connection()->abortSystemMoveResize(m_window);
return false; return false;
} }
// KWin, Openbox, AwesomeWM and Gnome have been tested to work with _NET_WM_MOVERESIZE. // KWin, Openbox, AwesomeWM and Gnome have been tested to work with _NET_WM_MOVERESIZE.
} else { // Started by mouse press. } else { // Started by mouse press.
if (connection()->isUnity())
return false; // _NET_WM_MOVERESIZE on this WM is bouncy (WM bug?).
doStartSystemMoveResize(mapToGlobal(pos), edges); doStartSystemMoveResize(mapToGlobal(pos), edges);
} }
@ -2373,6 +2370,7 @@ static uint qtEdgesToXcbMoveResizeDirection(Qt::Edges edges)
void QXcbWindow::doStartSystemMoveResize(const QPoint &globalPos, int 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::_NET_WM_MOVERESIZE);
xcb_client_message_event_t xev; xcb_client_message_event_t xev;
xev.response_type = XCB_CLIENT_MESSAGE; 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_send_event(connection()->xcb_connection(), false, xcbScreen()->root(),
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY, XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY,
(const char *)&xev); (const char *)&xev);
connection()->setDuringSystemMoveResize(true);
} }
// Sends an XEmbed message. // Sends an XEmbed message.