From c511466d747d99ee76465cfe90ce594fa1f27469 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?B=C5=82a=C5=BCej=20Szczygie=C5=82?= <spaz16@wp.pl>
Date: Thu, 14 Apr 2016 17:00:23 +0200
Subject: [PATCH] xcb: Properly process enter/leave events

Ignore enter/leave events when there is a window under mouse button.
Unset window under mouse button if other window is grabbed.
Smarter ignoring (un)grab ancestor enter/leave event.
Ignore ungrab inferior leave event.

Amends: b9f76db30d261421e4da58f29053181af04ceb4d

Task-number: QTBUG-46576
Task-number: QTBUG-51573
Task-number: QTBUG-52332
Task-number: QTBUG-52488
Change-Id: I8d926309aa60bb8929728691c31ecf93d1e299ad
Reviewed-by: Dmitry Shachnev <mitya57@gmail.com>
Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com>
Reviewed-by: Shawn Rutledge <shawn.rutledge@theqtcompany.com>
---
 src/plugins/platforms/xcb/qxcbconnection.cpp |  6 ++
 src/plugins/platforms/xcb/qxcbconnection.h   |  3 +
 src/plugins/platforms/xcb/qxcbwindow.cpp     | 70 +++++++++++++++-----
 3 files changed, 64 insertions(+), 15 deletions(-)

diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp
index 135c763dc11..edfaf2b0156 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.cpp
+++ b/src/plugins/platforms/xcb/qxcbconnection.cpp
@@ -560,6 +560,7 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
     , m_buttons(0)
     , m_focusWindow(0)
     , m_mouseGrabber(0)
+    , m_mousePressWindow(0)
     , m_clientLeader(0)
     , m_systemTrayTracker(0)
     , m_glIntegration(Q_NULLPTR)
@@ -1367,6 +1368,11 @@ void QXcbConnection::setFocusWindow(QXcbWindow *w)
 void QXcbConnection::setMouseGrabber(QXcbWindow *w)
 {
     m_mouseGrabber = w;
+    m_mousePressWindow = Q_NULLPTR;
+}
+void QXcbConnection::setMousePressWindow(QXcbWindow *w)
+{
+    m_mousePressWindow = w;
 }
 
 void QXcbConnection::grabServer()
diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h
index 86af5dc9c57..7ba95887ffe 100644
--- a/src/plugins/platforms/xcb/qxcbconnection.h
+++ b/src/plugins/platforms/xcb/qxcbconnection.h
@@ -475,6 +475,8 @@ public:
     void setFocusWindow(QXcbWindow *);
     QXcbWindow *mouseGrabber() const { return m_mouseGrabber; }
     void setMouseGrabber(QXcbWindow *);
+    QXcbWindow *mousePressWindow() const { return m_mousePressWindow; }
+    void setMousePressWindow(QXcbWindow *);
 
     QByteArray startupId() const { return m_startupId; }
     void setStartupId(const QByteArray &nextId) { m_startupId = nextId; }
@@ -658,6 +660,7 @@ private:
 
     QXcbWindow *m_focusWindow;
     QXcbWindow *m_mouseGrabber;
+    QXcbWindow *m_mousePressWindow;
 
     xcb_window_t m_clientLeader;
     QByteArray m_startupId;
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index d1c3d7039f1..226507f7a01 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -616,8 +616,12 @@ QXcbWindow::~QXcbWindow()
 {
     if (window()->type() != Qt::ForeignWindow)
         destroy();
-    else if (connection()->mouseGrabber() == this)
-        connection()->setMouseGrabber(Q_NULLPTR);
+    else {
+        if (connection()->mouseGrabber() == this)
+            connection()->setMouseGrabber(Q_NULLPTR);
+        if (connection()->mousePressWindow() == this)
+            connection()->setMousePressWindow(Q_NULLPTR);
+    }
 }
 
 void QXcbWindow::destroy()
@@ -875,6 +879,16 @@ void QXcbWindow::hide()
 
     if (connection()->mouseGrabber() == this)
         connection()->setMouseGrabber(Q_NULLPTR);
+    if (QPlatformWindow *w = connection()->mousePressWindow()) {
+        // Unset mousePressWindow when it (or one of its parents) is unmapped
+        while (w) {
+            if (w == this) {
+                connection()->setMousePressWindow(Q_NULLPTR);
+                break;
+            }
+            w = w->parent();
+        }
+    }
 
     m_mapped = false;
 
@@ -2214,6 +2228,8 @@ void QXcbWindow::handleButtonPressEvent(int event_x, int event_y, int root_x, in
         return;
     }
 
+    connection()->setMousePressWindow(this);
+
     handleMouseEvent(timestamp, local, global, modifiers, source);
 }
 
@@ -2228,19 +2244,44 @@ void QXcbWindow::handleButtonReleaseEvent(int event_x, int event_y, int root_x,
         return;
     }
 
+    if (connection()->buttons() == Qt::NoButton)
+        connection()->setMousePressWindow(Q_NULLPTR);
+
     handleMouseEvent(timestamp, local, global, modifiers, source);
 }
 
-static bool ignoreLeaveEvent(quint8 mode, quint8 detail)
+static inline bool doCheckUnGrabAncestor(QXcbConnection *conn)
+{
+    /* Checking for XCB_NOTIFY_MODE_GRAB and XCB_NOTIFY_DETAIL_ANCESTOR prevents unwanted
+     * enter/leave events on AwesomeWM on mouse button press. It also ignores duplicated
+     * enter/leave events on Alt+Tab switching on some WMs with XInput2 events.
+     * Without XInput2 events the (Un)grabAncestor cannot be checked when mouse button is
+     * not pressed, otherwise (e.g. on Alt+Tab) it can igonre important enter/leave events.
+    */
+    if (conn) {
+        const bool mouseButtonsPressed = (conn->buttons() != Qt::NoButton);
+#ifdef XCB_USE_XINPUT22
+        return mouseButtonsPressed || (conn->isAtLeastXI22() && conn->xi2MouseEvents());
+#else
+        return mouseButtonsPressed;
+#endif
+    }
+    return true;
+}
+
+static bool ignoreLeaveEvent(quint8 mode, quint8 detail, QXcbConnection *conn = Q_NULLPTR)
 {
-    return (mode == XCB_NOTIFY_MODE_GRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) // Check for AwesomeWM
+    return ((doCheckUnGrabAncestor(conn)
+             && 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;
+            || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL);
 }
 
-static bool ignoreEnterEvent(quint8 mode, quint8 detail)
+static bool ignoreEnterEvent(quint8 mode, quint8 detail, QXcbConnection *conn = Q_NULLPTR)
 {
-    return ((mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR) // Check for AwesomeWM
+    return ((doCheckUnGrabAncestor(conn)
+             && mode == XCB_NOTIFY_MODE_UNGRAB && detail == XCB_NOTIFY_DETAIL_ANCESTOR)
             || (mode != XCB_NOTIFY_MODE_NORMAL && mode != XCB_NOTIFY_MODE_UNGRAB)
             || detail == XCB_NOTIFY_DETAIL_VIRTUAL
             || detail == XCB_NOTIFY_DETAIL_NONLINEAR_VIRTUAL);
@@ -2274,9 +2315,7 @@ void QXcbWindow::handleEnterNotifyEvent(int event_x, int event_y, int root_x, in
 
     const QPoint global = QPoint(root_x, root_y);
 
-    if (ignoreEnterEvent(mode, detail)
-            || (connection()->buttons() != Qt::NoButton
-                && QGuiApplicationPrivate::lastCursorPosition != global))
+    if (ignoreEnterEvent(mode, detail, connection()) || connection()->mousePressWindow())
         return;
 
     const QPoint local(event_x, event_y);
@@ -2288,11 +2327,7 @@ void QXcbWindow::handleLeaveNotifyEvent(int root_x, int root_y,
 {
     connection()->setTime(timestamp);
 
-    const QPoint global(root_x, root_y);
-
-    if (ignoreLeaveEvent(mode, detail)
-            || (connection()->buttons() != Qt::NoButton
-                && QGuiApplicationPrivate::lastCursorPosition != global))
+    if (ignoreLeaveEvent(mode, detail, connection()) || connection()->mousePressWindow())
         return;
 
     EnterEventChecker checker;
@@ -2315,6 +2350,11 @@ void QXcbWindow::handleMotionNotifyEvent(int event_x, int event_y, int root_x, i
 {
     QPoint local(event_x, event_y);
     QPoint global(root_x, root_y);
+
+    // "mousePressWindow" can be NULL i.e. if a window will be grabbed or umnapped, so set it again here
+    if (connection()->buttons() != Qt::NoButton && connection()->mousePressWindow() == Q_NULLPTR)
+        connection()->setMousePressWindow(this);
+
     handleMouseEvent(timestamp, local, global, modifiers, source);
 }
 
-- 
GitLab