xcb: recreate xcb window under some conditions
authorDebian Qt/KDE Maintainers <debian-qt-kde@lists.debian.org>
Wed, 12 Apr 2023 23:24:34 +0000 (00:24 +0100)
committerLisandro Damián Nicanor Pérez Meyer <lisandro@debian.org>
Wed, 12 Apr 2023 23:24:34 +0000 (00:24 +0100)
Origin: upstream, https://code.qt.io/cgit/qt/qtbase.git/commit/?id=f9e4402ffeef791e
Last-Update: 2022-11-24

Some netWmState needs to be set during unmap/hide(), which is too
difficult to follow, and causes m_mapped status out of sync very easily
sometimes, which we had tried in
e946e6895a8517a887ac246905e0769edd766fcc .

Destroy the xcb window and recreate new could make the thing
much easier. This practice is also used in other platforms, such
as cocoa plugin.

In Qt 4, the platform window was destroyed and re-created in this
situation on all platforms, which was not ported into Qt5.

See also the code between setWinId(0) and createWinId() in
QWidgetPrivate::setParent_sys() in qwidget_x11.cpp/qwidget_win.cpp/
qwidget_mac.mm.

Gbp-Pq: Name recreate_xcb_window.diff

src/plugins/platforms/xcb/qxcbwindow.cpp
src/plugins/platforms/xcb/qxcbwindow.h

index 1c947f348bfb741eade48a981670004b5d62fcf0..609e8ac562bad86d8f52421c0acd3ebc1e45be83 100644 (file)
@@ -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();
@@ -689,6 +692,11 @@ void QXcbWindow::setVisible(bool visible)
 void QXcbWindow::show()
 {
     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();
@@ -904,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
index 6f5c1f5ed9387b8632b4daa283bc858a9d8ad947..8de486c6d2a72a6d0a2603b2c9ea982d77d4d2d6 100644 (file)
@@ -74,6 +74,13 @@ public:
 
     Q_DECLARE_FLAGS(NetWmStates, NetWmState)
 
+    enum RecreationReason {
+        RecreationNotNeeded = 0,
+        WindowStaysOnTopHintChanged = 0x1,
+        WindowStaysOnBottomHintChanged = 0x2
+    };
+    Q_DECLARE_FLAGS(RecreationReasons, RecreationReason)
+
     QXcbWindow(QWindow *window);
     ~QXcbWindow();
 
@@ -281,6 +288,8 @@ protected:
     int m_swapInterval = -1;
 
     qreal m_sizeHintsScaleFactor = 1.0;
+
+    RecreationReasons m_recreationReasons = RecreationNotNeeded;
 };
 
 class QXcbForeignWindow : public QXcbWindow