https://bugs.kde.org/show_bug.cgi?id=515889
Leart Turely <[email protected]> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |[email protected] --- Comment #21 from Leart Turely <[email protected]> --- `AT_SPI_BUS_ADDRESS=1 krita` can show a working global menu. Root cause found. Analysis and the minimal reproducer by DeepSeek V4.1 Flash, with the reporter. Full analysis: https://github.com/worksoup/appmenu_test/blob/main/BUGREPORT.md Reproducer (Qt 5 + Qt 6, CMake, windowed): https://github.com/worksoup/appmenu_test What happens ------------ Before Krita's real application object exists, KisOpenGLModeProber creates a throw-away QGuiApplication (plus a QWindow and a QOpenGLContext) to probe the OpenGL renderers, and destroys them again. On Qt 6, destroying that probe QWindow calls QAccessible::isActive() - Qt 5.15's QWindow destructor has no such call - and that makes the xcb/wayland QPA plugin instantiate its accessibility implementation (the at-spi bridge), which creates the process-wide session-bus connection while the throw-away application is still the current qApp. Why that breaks the real application ------------------------------------ Qt's D-Bus connection is a process-level resource. When it is created on the main thread while a qApp already exists, delivery starts suspended (dispatchEnabled == false) and a QMetaObject::invokeMethod call is queued on that qApp to set it to true once its event loop runs. The throw-away application is destroyed before any event loop runs, so that queued call disappears with it; the cached connection (global QDBusConnectionManager, defaultBuses[]) is handed to the real application without ever being re-armed. Every incoming D-Bus method call is therefore queued and never answered, and the Plasma applet can only time out. Note that the code that builds the throw-away application already excludes most D-Bus producers (QT_NO_XDG_DESKTOP_PORTAL=1, setDesktopSettingsAware(false)); the Qt 6 destructor-time accessibility query is the one that no existing guard covers. The menu is exported correctly (/MenuBar/N is registered and Plasma writes _KDE_NET_WM_APPMENU_*), and only the connection-level Peer.Ping still answers - which makes the failure easy to misdiagnose. Measurements (Qt 6.11.2; Introspect/AboutToShow from another process; 124 = timeout) ----------------------------------------------------------------------------------- - throw-away QGuiApplication + QWindow: 124 / 124 (bug reproduced) - same, but the probe application does not create a QWindow: 0 / 0 - same + AT_SPI_BUS_ADDRESS set while the probe is alive: 0 / 0 (fixed) - same + blocking DBUS_SESSION_BUS_ADDRESS while the probe is alive: no usable session bus at all - Qt 5.15.19 with the same code: 0 / 0 Workaround ---------- Set AT_SPI_BUS_ADDRESS to any non-empty value while the throw-away probe application is alive (Krita's EnvironmentSetter RAII fits), and optionally call QDBusConnection::disconnectFromBus("a11y") before the real application is created. The at-spi bridge then takes its early-return path and never calls QDBusConnection::sessionBus(), so the process' first connection is created by the real application and delivery resumes normally. Measured: AT_SPI_BUS_ADDRESS=1 krita shows a working global menu. Keep it scoped to the probe - set process-wide it also hides the symptom but silently disables accessibility for the whole process. Why this is reported to KDE rather than to Qt --------------------------------------------- The code that constructs the throw-away application already excludes most D-Bus-producing functionality, so Krita appears to have taken Qt's D-Bus connection-manager behaviour into account; hence this report goes to KDE first (the Krita-side mitigation above is enough to remove the symptom). Mechanically, however, this looks like a Qt defect: the resume step is bound to a qApp that is later destroyed (enableDispatchDelayed(qApp)), and the cached connection (defaultBuses[]) is handed out without being re-armed - and QAccessible::isActive(), which is meant to be a cheap guard, ends up instantiating the at-spi bridge and creating a D-Bus connection. I have not checked whether Qt already has a report for this. If someone familiar with Qt can confirm that this is a Qt bug, help forwarding it to Qt (bugreports.qt.io, project QTBUG, component DBus) would be appreciated. -- You are receiving this mail because: You are watching all bug changes.
