> After some testing, I determined that the bug is not present in
> 5.15.5+dfsg-3 but is present in 5.15.6+dfsg-1.

Control: tags -1 patch fixed-upstream
Control: found -1 5.15.6+dfsg-1
Control: notfound -1 5.15.5+dfsg-3

Okay, I have finally found the bug. It was introduced in the following commit:

https://github.com/qt/qtbase/commit/290b405872602de931646fe4f769eff208f9bbef

The fix is as follows:

https://github.com/qt/qtbase/commit/d27a6235246764bef1d61905ef96feeeddc65cd8
https://github.com/qt/qtbase/commit/f9e4402ffeef791e66b7b2f2cc332000df7f5cd4

Please consider adding this patch into Debian.

For convenience, I am attaching the cherry-picked patch that is needed to 
apply.

-- 
Alexander Kernozhitsky
diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp
index da179591e9..9acef9b5e9 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.cpp
+++ b/src/plugins/platforms/xcb/qxcbwindow.cpp
@@ -93,6 +93,8 @@ enum {
 
 QT_BEGIN_NAMESPACE
 
+Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
+
 Q_DECLARE_TYPEINFO(xcb_rectangle_t, Q_PRIMITIVE_TYPE);
 
 #undef FocusIn
@@ -555,6 +557,7 @@ void QXcbWindow::destroy()
     }
 
     m_mapped = false;
+    m_recreationReasons = RecreationNotNeeded;
 
     if (m_pendingSyncRequest)
         m_pendingSyncRequest->invalidate();
@@ -564,11 +567,6 @@ void QXcbWindow::setGeometry(const QRect &rect)
 {
     QPlatformWindow::setGeometry(rect);
 
-    if (shouldDeferTask(Task::SetGeometry)) {
-        m_deferredGeometry = rect;
-        return;
-    }
-
     propagateSizeHints();
 
     QXcbScreen *currentScreen = xcbScreen();
@@ -693,10 +691,12 @@ void QXcbWindow::setVisible(bool visible)
 
 void QXcbWindow::show()
 {
-    if (shouldDeferTask(Task::Map))
-        return;
-
     if (window()->isTopLevel()) {
+        if (m_recreationReasons != RecreationNotNeeded) {
+            qCDebug(lcQpaWindow) << "QXcbWindow: need to recreate window" << window() << m_recreationReasons;
+            create();
+            m_recreationReasons = RecreationNotNeeded;
+        }
 
         // update WM_NORMAL_HINTS
         propagateSizeHints();
@@ -746,10 +746,6 @@ void QXcbWindow::show()
 
 void QXcbWindow::hide()
 {
-    if (shouldDeferTask(Task::Unmap))
-        return;
-
-    m_wmStateValid = false;
     xcb_unmap_window(xcb_connection(), m_window);
 
     // send synthetic UnmapNotify event according to icccm 4.1.4
@@ -909,9 +905,6 @@ QXcbWindow::NetWmStates QXcbWindow::netWmStates()
 
 void QXcbWindow::setWindowFlags(Qt::WindowFlags flags)
 {
-    if (shouldDeferTask(Task::SetWindowFlags))
-        return;
-
     Qt::WindowType type = static_cast<Qt::WindowType>(int(flags & Qt::WindowType_Mask));
 
     if (type == Qt::ToolTip)
@@ -919,6 +912,12 @@ void QXcbWindow::setWindowFlags(Qt::WindowFlags flags)
     if (type == Qt::Popup)
         flags |= Qt::X11BypassWindowManagerHint;
 
+    Qt::WindowFlags oldflags = window()->flags();
+    if ((oldflags & Qt::WindowStaysOnTopHint) != (flags & Qt::WindowStaysOnTopHint))
+        m_recreationReasons |= WindowStaysOnTopHintChanged;
+    if ((oldflags & Qt::WindowStaysOnBottomHint) != (flags & Qt::WindowStaysOnBottomHint))
+        m_recreationReasons |= WindowStaysOnBottomHintChanged;
+
     const quint32 mask = XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK;
     const quint32 values[] = {
          // XCB_CW_OVERRIDE_REDIRECT
@@ -941,8 +940,6 @@ void QXcbWindow::setWindowFlags(Qt::WindowFlags flags)
 
     setTransparentForMouseEvents(flags & Qt::WindowTransparentForInput);
     updateDoesNotAcceptFocus(flags & Qt::WindowDoesNotAcceptFocus);
-
-    m_isWmManagedWindow = !(flags & Qt::X11BypassWindowManagerHint);
 }
 
 void QXcbWindow::setMotifWmHints(Qt::WindowFlags flags)
@@ -1142,9 +1139,6 @@ void QXcbWindow::setWindowState(Qt::WindowStates state)
     if (state == m_windowState)
         return;
 
-    if (shouldDeferTask(Task::SetWindowState))
-        return;
-
     // unset old state
     if (m_windowState & Qt::WindowMinimized)
         xcb_map_window(xcb_connection(), m_window);
@@ -1894,10 +1888,6 @@ void QXcbWindow::handleUnmapNotifyEvent(const xcb_unmap_notify_event_t *event)
     if (event->window == m_window) {
         m_mapped = false;
         QWindowSystemInterface::handleExposeEvent(window(), QRegion());
-        if (!m_isWmManagedWindow) {
-            m_wmStateValid = true;
-            handleDeferredTasks();
-        }
     }
 }
 
@@ -2212,98 +2202,30 @@ void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event)
     handleLeaveNotifyEvent(event->root_x, event->root_y, event->mode, event->detail, event->time);
 }
 
-bool QXcbWindow::shouldDeferTask(Task task)
-{
-    if (m_wmStateValid)
-        return false;
-
-    m_deferredTasks.append(task);
-    return true;
-}
-
-void QXcbWindow::handleDeferredTasks()
-{
-    Q_ASSERT(m_wmStateValid == true);
-    if (m_deferredTasks.isEmpty())
-        return;
-
-    bool map = false;
-    bool unmap = false;
-
-    QVector<Task> tasks;
-    for (auto taskIt = m_deferredTasks.rbegin(); taskIt != m_deferredTasks.rend(); ++taskIt) {
-        if (!tasks.contains(*taskIt))
-            tasks.prepend(*taskIt);
-    }
-
-    for (Task task : tasks) {
-        switch (task) {
-        case Task::Map:
-            map = true;
-            unmap = false;
-            break;
-        case Task::Unmap:
-            unmap = true;
-            map = false;
-            break;
-        case Task::SetGeometry:
-            setGeometry(m_deferredGeometry);
-            break;
-        case Task::SetWindowFlags:
-            setWindowFlags(window()->flags());
-            break;
-        case Task::SetWindowState:
-            setWindowState(window()->windowState());
-            break;
-        }
-    }
-    m_deferredTasks.clear();
-
-    if (map) {
-        Q_ASSERT(unmap == false);
-        show();
-    }
-    if (unmap) {
-        Q_ASSERT(map == false);
-        hide();
-    }
-}
-
 void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event)
 {
     connection()->setTime(event->time);
 
-    const bool wmStateChanged = event->atom == atom(QXcbAtom::WM_STATE);
-    const bool netWmStateChanged = event->atom == atom(QXcbAtom::_NET_WM_STATE);
-    if (netWmStateChanged || wmStateChanged) {
-        if (wmStateChanged && !m_wmStateValid && m_isWmManagedWindow) {
-            // ICCCM 4.1.4
-            // Clients that want to re-use a client window (e.g. by mapping it again)
-            // after withdrawing it must wait for the withdrawal to be complete before
-            // proceeding. The preferred method for doing this is for clients to wait for
-            // a window manager to update or remove the WM_STATE property.
-            m_wmStateValid = true;
-            handleDeferredTasks();
-        }
-        if (event->state == XCB_PROPERTY_DELETE)
+    const bool propertyDeleted = event->state == XCB_PROPERTY_DELETE;
+
+    if (event->atom == atom(QXcbAtom::_NET_WM_STATE) || event->atom == atom(QXcbAtom::WM_STATE)) {
+        if (propertyDeleted)
             return;
 
-        if (wmStateChanged) {
+        Qt::WindowStates newState = Qt::WindowNoState;
+
+        if (event->atom == atom(QXcbAtom::WM_STATE)) { // WM_STATE: Quick check for 'Minimize'.
             auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(),
                                      0, m_window, atom(QXcbAtom::WM_STATE),
                                      XCB_ATOM_ANY, 0, 1024);
             if (reply && reply->format == 32 && reply->type == atom(QXcbAtom::WM_STATE)) {
-                auto data = static_cast<const quint32 *>(xcb_get_property_value(reply.get()));
-                if (reply->length != 0) {
-                    const bool changedToWithdrawn = data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN;
-                    const bool changedToIconic = data[0] == XCB_ICCCM_WM_STATE_ICONIC;
-                    m_minimized = changedToIconic || (changedToWithdrawn && m_minimized);
-                }
+                const quint32 *data = (const quint32 *)xcb_get_property_value(reply.get());
+                if (reply->length != 0)
+                    m_minimized = (data[0] == XCB_ICCCM_WM_STATE_ICONIC
+                                   || (data[0] == XCB_ICCCM_WM_STATE_WITHDRAWN && m_minimized));
             }
         }
 
-        // _NET_WM_STATE handling
-        Qt::WindowStates newState = Qt::WindowNoState;
         const NetWmStates states = netWmStates();
         // _NET_WM_STATE_HIDDEN should be set by the Window Manager to indicate that a window would
         // not be visible on the screen if its desktop/viewport were active and its coordinates were
@@ -2325,6 +2247,7 @@ void QXcbWindow::handlePropertyNotifyEvent(const xcb_property_notify_event_t *ev
             if ((m_windowState & Qt::WindowMinimized) && connection()->mouseGrabber() == this)
                 connection()->setMouseGrabber(nullptr);
         }
+        return;
     } else if (event->atom == atom(QXcbAtom::_NET_FRAME_EXTENTS)) {
         m_dirtyFrameMargins = true;
     }
diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h
index 55af9279b1..8de486c6d2 100644
--- a/src/plugins/platforms/xcb/qxcbwindow.h
+++ b/src/plugins/platforms/xcb/qxcbwindow.h
@@ -74,13 +74,12 @@ public:
 
     Q_DECLARE_FLAGS(NetWmStates, NetWmState)
 
-    enum Task {
-        Map,
-        Unmap,
-        SetGeometry,
-        SetWindowFlags,
-        SetWindowState
+    enum RecreationReason {
+        RecreationNotNeeded = 0,
+        WindowStaysOnTopHintChanged = 0x1,
+        WindowStaysOnBottomHintChanged = 0x2
     };
+    Q_DECLARE_FLAGS(RecreationReasons, RecreationReason)
 
     QXcbWindow(QWindow *window);
     ~QXcbWindow();
@@ -151,9 +150,6 @@ public:
 
     QXcbWindow *toWindow() override;
 
-    bool shouldDeferTask(Task task);
-    void handleDeferredTasks();
-
     void handleMouseEvent(xcb_timestamp_t time, const QPoint &local, const QPoint &global,
                           Qt::KeyboardModifiers modifiers, QEvent::Type type, Qt::MouseEventSource source);
 
@@ -293,10 +289,7 @@ protected:
 
     qreal m_sizeHintsScaleFactor = 1.0;
 
-    bool m_wmStateValid = true;
-    QVector<Task> m_deferredTasks;
-    bool m_isWmManagedWindow = true;
-    QRect m_deferredGeometry;
+    RecreationReasons m_recreationReasons = RecreationNotNeeded;
 };
 
 class QXcbForeignWindow : public QXcbWindow

Reply via email to