Details
-
Bug
-
Resolution: Unresolved
-
P2: Important
-
None
-
5.14.1, 5.15.2
-
None
Description
QWindowPrivate::setVisible emits a visibleChanged signal before actually applying the changes on the window. This means that if an application has a signal handler that calls QWindow::setVisible, it will be called recursively.
void QWindowPrivate::setVisible(bool visible) { Q_Q(QWindow); if (this->visible != visible) { this->visible = visible; emit q->visibleChanged(visible); // <--- may cause recursive calls updateVisibility(); } else if (platformWindow) { // Visibility hasn't changed, and the platform window is in sync return; } // ... lots of code that uses the stack variable "visible", which may be different from "this->visible"
This means that if the platformWindow is not yet created, or if the value of visible is different from the outer call, the recursive call will do the actual work, and then it will be redone or even partially reverted (if called with different values) once the stack is popped and the initial call resumes.
Qml app to trigger some of the possible issues:
import QtQuick 2.6 import QtQuick.Window 2.2 import QtQuick.Controls 2.14 Window { id: window visible: true property int signalsReceived: 0 property bool buttonClicked: false onVisibleChanged: { // We want to skip the initial events when the app is just starting up if (!buttonClicked) { return; } ++signalsReceived; console.log(`Signal ${signalsReceived}: visible changed to ${visible}`); if (signalsReceived === 1) { // only do it on the first signal console.log("setting visible to true in signal handler"); visible = true; } } Column { Button { text: "Trigger bugs?" onClicked: { buttonClicked = true; window.visible = false; } } Text { text: `Visible: ${visible}` } } }
On xcb, this causes a crash:
qml: Signal 1: visible changed to false qml: setting visible to true in signal handler qml: Signal 2: visible changed to true qt.qpa.xcb: internal error: void QXcbWindow::setNetWmStateOnUnmappedWindow() called on mapped window
And on Wayland as well:
QSocketNotifier: Can only be used with threads started with QThread qml: Signal 1: visible changed to false qml: setting visible to true in signal handler qml: Signal 2: visible changed to true [destroyed object]: error 0: xdg_wm_base::get_xdg_surface already requested The Wayland connection experienced a fatal error: Protocol error fish: “qml visibility-bug.qml -platfor…” terminated by signal SIGABRT (Abort)
There are less obscure cases that are affected by this as well, i.e. the following causes a crash on startup on Wayland:
import QtQuick 2.6 import QtQuick.Window 2.2 import QtQuick.Controls 2.14 Window { id: window visible: true CheckBox { text: "Window visible" checked: window.visible onCheckedChanged: window.visible = checked } }
QSocketNotifier: Can only be used with threads started with QThread wl_surface@20: error 0: xdg_wm_base::get_xdg_surface already requested The Wayland connection experienced a fatal error: Protocol error fish: “qml visibility-bug-2.qml -platf…” terminated by signal SIGABRT (Abort)
Attachments
Issue Links
- relates to
-
QTBUG-120031 xcb: Modal state may get lost due to a race
- Reported