colorSchemeChanged under Gnome
-
Hi @KejPi,
Which Operating System and Qt version are you using?
Are you using GTK3?
Is DBus running on your OS?
If so, please launch dbus-monitor, change the Gnome theme and post the output afterwards.
Cheers
Axel -
Qt's reaction to platform or color themes changes in Linux depends on DBus signals.
This is regularly being tested on all supported Linux platforms.
Other systems might just work as well, if they emit the same DBus signals.
Here is an example of a system where it doesn't work.Here are the DBus property change signals Qt is listening to (Qt 6.5):
org.kde.kdeglobals.KDE / widgetStyle => KDE application style org.kde.kdeglobals.General /ColorScheme => KDE global theme org.gnome.desktop.interface / gtk-theme => gtk theme
When an environment doesn't react,
dbus-monitor
is likely to tell you why:- if it doesn't exist, install DBus
- if it exsits and none of the above is emitted during a change, check your installation. In a Gnome environment, make sure to have the latest GTK3 up and running.
-
I am running Gnome 43.5 on ArchLinux - that is practically clean Gnome without adjustments.
Following messages appear in dbus-monitor when I enable dark mode:
signal time=1683913100.742518 sender=:1.70 -> destination=(null destination) serial=66 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.impl.portal.Settings; member=SettingChanged string "org.gnome.desktop.interface" string "color-scheme" variant string "prefer-dark" signal time=1683913100.742525 sender=:1.70 -> destination=(null destination) serial=67 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.impl.portal.Settings; member=SettingChanged string "org.freedesktop.appearance" string "color-scheme" variant uint32 1 signal time=1683913100.743353 sender=:1.67 -> destination=(null destination) serial=163 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.portal.Settings; member=SettingChanged string "org.gnome.desktop.interface" string "color-scheme" variant string "prefer-dark" signal time=1683913100.743373 sender=:1.67 -> destination=(null destination) serial=164 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.portal.Settings; member=SettingChanged string "org.freedesktop.appearance" string "color-scheme" variant uint32 1 signal time=1683913100.757986 sender=:1.69 -> destination=(null destination) serial=106 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.impl.portal.Settings; member=SettingChanged string "org.gnome.desktop.interface" string "color-scheme" variant string "prefer-dark" signal time=1683913100.758199 sender=:1.69 -> destination=(null destination) serial=107 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.impl.portal.Settings; member=SettingChanged string "org.freedesktop.appearance" string "color-scheme" variant uint32 1 signal time=1683913100.758544 sender=:1.67 -> destination=(null destination) serial=165 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.portal.Settings; member=SettingChanged string "org.gnome.desktop.interface" string "color-scheme" variant string "prefer-dark" signal time=1683913100.758564 sender=:1.67 -> destination=(null destination) serial=166 path=/org/freedesktop/portal/desktop; interface=org.freedesktop.portal.Settings; member=SettingChanged string "org.freedesktop.appearance" string "color-scheme" variant uint32 1
This how it looks like (there is dark window title bar in dark mode, the rest is the same):
It is somehow better on "supported"Ubuntu 22.04 but still not good - white titlebar and glitches when window is moved
When the theme is changed to dark or when I move the window
After moving the window:
Conclusions:
- it does not work using default Gnome 43.5 installation
- it somehow works with Ubuntu 22.04 but it has graphical glitches when windows moves and windows titlebar stays white that practically makes it unusable.
EDIT: it seems that titlebar simply does not change - if application starts in dark mode, then it stays dark.
-
@KejPi
Could you please export the environment variable
QT_LOGGING_RULES="qt.qpa.plugin=true;qt.qpa.theme=true"
...and start your application again?
I'd like to know which platform theme is loaded. There will be a debugging outputplatformThemeName
which in your case should be either "gnome" or "gtk3".Let's hope that it's "gnome", in which case the last signal emitted in the list is compliant with this blog post.
IMO that would justify a fix to make our gnome theme reactive to runtime appearance changes.EDIT: I agree, the glitches look unfortunate...
-
Arch linux with Gnome 43.5
$ QT_LOGGING_RULES="qt.qpa.plugin=true;qt.qpa.theme=true" ./gallery qt.qpa.plugin: init_platform called with pluginNamesWithArguments "wayland;xcb" platformPluginPath "" platformThemeName "" qt.qpa.plugin: Attempting to load Qt platform plugin "wayland" with arguments QList() qt.qpa.plugin: Could not find the Qt platform plugin "wayland" in "" qt.qpa.plugin: Attempting to load Qt platform plugin "xcb" with arguments QList() qt.qpa.plugin: Successfully loaded Qt platform plugin "xcb" qt.qpa.theme: Adding platform integration's theme names to list of theme names: QList("gtk3", "gnome", "generic") qt.qpa.theme: Attempting to create platform theme "gtk3" via QPlatformThemeFactory::create qt.qpa.theme: Successfully created platform theme "gtk3"
Ubuntu 22.04
$ QT_LOGGING_RULES="qt.qpa.plugin=true;qt.qpa.theme=true" ./gallery qt.qpa.plugin: init_platform called with pluginNamesWithArguments "wayland;xcb" platformPluginPath "" platformThemeName "" qt.qpa.plugin: Attempting to load Qt platform plugin "wayland" with arguments QList() qt.qpa.plugin: Successfully loaded Qt platform plugin "wayland" qt.qpa.theme: Adding platform integration's theme names to list of theme names: QList("ubuntu", "gtk3", "gnome", "generic") qt.qpa.theme: Attempting to create platform theme "ubuntu" via QPlatformThemeFactory::create qt.qpa.theme: Attempting to create platform theme "gtk3" via QPlatformThemeFactory::create qt.qpa.theme: Successfully created platform theme "gtk3" qt.dbus.integration: Could not connect "org.freedesktop.IBus" to globalEngineChanged(QString)
-
@KejPi
Thanks for sharing!Two things puzzle me:
-
You said your arch Linux is a plain Gnome desktop. It seems to have gtk3 running, which is why Qt loads the GTK3 platform theme. The GTK3 theme (under Qt 6.5) subscribes to GTK changes (under more) of the GTK settings gtk-application-prefer-dark-theme, gtk-theme-name. Our theme change signal not getting fired, can only caused by the GTK3 theme changes not getting announced. They do get announced, however, on all the test systems I have available.
-
You also shared the log output of Ubuntu 22.04. This is a fully supported platform. Does it work there or does it not?
-
-
@Axel-Spoerl said in colorSchemeChanged under Gnome:
Two things puzzle me:
- You said your arch Linux is a plain Gnome desktop. It seems to have gtk3 running, which is why Qt loads the GTK3 platform theme. The GTK3 theme (under Qt 6.5) subscribes to GTK changes (under more) of the GTK settings gtk-application-prefer-dark-theme, gtk-theme-name. Our theme change signal not getting fired, can only caused by the GTK3 theme changes not getting announced. They do get announced, however, on all the test systems I have available.
Qt signal is not coming on Arch Linux. I have shared dbus signals that appear when I switch from Light to Dark theme - does it mean that any of these messages is not triggering Qt signal?
- You also shared the log output of Ubuntu 22.04. This is a fully supported platform. Does it work there or does it not?
There are graphical glitches shared by screenshots above on Ubuntu which make the implementation as non working IMO.
-
Can confirm that
colorSchemeChanged
signal literally never emits under Fedora Rawhide (qt 6.7.0) under Gnome Shell or KDE Plasma 6 no matter how you debug. Changing system style to dark/white never triggerscolorSchemeChanged
. It's a Qt bug for sure.More than that
/src/plugins/platformthemes/gtk3/qgtk3theme.cpp
reads~/.config/gtk3/settings.ini
but in modern distributives with Gnome that file doesn't exist, and never creates. So withQT_QPA_PLATFORMTHEME=gtk3
you always get white Qt widgets style. -
@krab4t said in colorSchemeChanged under Gnome:
Can confirm that colorSchemeChanged signal literally never emits under Fedora Rawhide
See here for supported Linux platforms. Fedora Rawhide is not one of them.
It's a Qt bug for sure.
Qt subscribes to the following
settingChanged
signals on DBus:org.kde.kdeglobals widgetStyle org.kde.kdeglobals.General ColorScheme org.gnome.desktop.interface gtk-theme org.freedesktop.appearance color-scheme
=> check on
dbusmon
, if one of them is emitted when your system's color scheme changes. -
Signal emitted on unsupported Ubuntu 24.04 (installed qt 6.7.0 via online installer), but not on unsupported Fedora Rawhide (installed qt 6.7.0 via fedora repositories). There are some changes in dev branch to how/when
colorSchemeChanged
emitted logic, andhandleThemeChanged
method code. I hope in 6.8 everything will be fully fixed. -
@krab4t said in colorSchemeChanged under Gnome:
I hope in 6.8 everything will be fully fixed
Ubuntu 24 is likely to become a supported platform. Some more support for XDG has been added by Jan Grulich in 6.8. However, all supported platforms work as a charm. So there is no bug and nothing to be fixed.
To make Qt aware of the DBus signals emitted on an unsupported platform's theme changes, you can put a JSON filename in a writable path in the environment variable
QT_QPA_DBUS_SIGNALS_SAVE
, e.g./tmp/signals.json
.
Then start a Qt application and inspect the file.
You can modify it and add your platform's signal (preferably without modifying the existing signals).
Then put the file in a safe place and ship it with your application.
MakeQT_QPA_DBUS_SIGNALS
point to the edited file.
Qt will now subscribe to the specified signal and trigger a theme change when it is fired. -
@Axel-Spoerl said in colorSchemeChanged under Gnome:
work as a charm.
It works like a bloated mess, with the same functionality implemented multiple times across multiple files/classes.
QGtk3PortalInterface
class exist only in dev branch, and not in any qt6 release. So in qt 6.7 and older theme changes detection works viaQGtk3Interface
and GSettings https://github.com/qt/qtbase/blob/dev/src/plugins/platformthemes/gtk3/qgtk3interface.cpp#L46Older Gnome 40 versions support dark style only via manually changing GTK theme to
Adwaita-Dark
(or any *-dark theme). Via gnome-tweaks or via dconf-editor in path/org/gnome/desktop/interface/
settinggtk-theme
. So reacting to this change works fine in Qt6 and signalcolorSchemeChanged
emitted as expected.However, since Gnome 45 GTK enabling dark mode via "Quick Settings menu" does NOT change
gtk-theme
value but onlycolor-scheme
value affected according to dconf-editor.Then even if Qt reacts to dbus changes it still inside QGtk3Storage tries
populateMap()
https://github.com/qt/qtbase/blob/6.7.0/src/plugins/platformthemes/gtk3/qgtk3storage.cpp#L335 and gets oldgtk-theme
unchanged valueAdwaita
, and returns. Nothing more happens, no new pallete applies, no signal emitted either.The only reason it works under Ubuntu 24.04 on newer Gnome 46 it's either because Cannonical patched it, or GTK keeps
gtk-theme
value for dark mode only for Adwaita style and not for other styles like Yaru / Yaru-dark on Ubuntu. But that not the case for Fedora 40, or upcoming openSUSE 15.6 default installations.On unsupported platforms dark style can be actually applied in Qt6 applications and signal emitts when you manually change via gnome-tweaks "Legacy Applications" option to
Adwaita-dark
, this setsgtk-theme
toAdwaita-dark
according to dconf-editor.So. Even
QGtk3PortalInterface
from dev gets into 6.8 release, it changes nothing for Gnome45+ support. Because https://github.com/qt/qtbase/blob/dev/src/plugins/platformthemes/gtk3/qgtk3storage.cpp#L337themeName()
still reads https://github.com/qt/qtbase/blob/dev/src/plugins/platformthemes/gtk3/qgtk3interface.cpp#L480"gtk-theme-name"
viagsettings
gtk, and gets oldAdwaita
value and notAdwaita-dark
when you enable "Dark Style" via Quick Settings Gnome menu.I'm done debugging by installing multiple old and new linux distros in VirtualBox. QGtk3 has the worst qCDebug usage for logging useful information, literally not reliable for users to debug/log.
-
Thanks for diving into various operating systems.
Unfortunately, theQGtk3Theme
has limitations, under more because there is no API to actually parse a GTK3 theme.
The implementation focuses on RHEL 8/9, Ubuntu 18/22 and openSuSE 15.5.It works like a bloated mess
QGtk3 has the worst qCDebug usage for logging useful information, literally not reliable for users to debug/log.
Please write in a polite manner. Bad language is not welcome here.
If you see room for improvement, please share your observations here, or file a bug report. -
I am having similar problems with my Qt application Sigil on Manjaro Testing with Qt 6.7.0 and KDE 6 Plasma (X11) set up. I ran through the debugging options you posted about earlier:
/ExtSSD/repos/build > export QT_LOGGING_RULES="qt.qpa.plugin=true;qt.qpa.theme=true"
/ExtSSD/repos/build > ./bin/sigil
Debug: init_platform called with pluginNamesWithArguments "xcb" platformPluginPath "" platformThemeName ""
Debug: Attempting to load Qt platform plugin "xcb" with arguments QList()
Debug: Successfully loaded Qt platform plugin "xcb"
Debug: Adding platform integration's theme names to list of theme names: QList("kde", "generic")
Debug: Attempting to create platform theme "kde" via QPlatformThemeFactory::create
Debug: Successfully created platform theme "kde"And running dbus-monitor I see the following snippet when I tried to change my default theme to BreathLight from BreathDark:
signal time=1715438356.470501 sender=:1.23 -> destination=(null destination) serial=188 path=/org/freedesktop/portal/desktop; i
nterface=org.freedesktop.impl.portal.Settings; member=SettingChanged
string "org.kde.kdeglobals.General"
string "ColorScheme"
variant string "BreathLight"
signal time=1715438356.470518 sender=:1.7 -> destination=(null destination) serial=298 path=/org/freedesktop/portal/desktop; in
terface=org.freedesktop.portal.Settings; member=SettingChanged
string "org.kde.kdeglobals.General"
string "ColorScheme"
variant string "BreathLight"I do have xdg-desktop-portal and xdg-desktop-portal-kde installed.
I tried doing the following to try to see what signals were being watched for but /tmp/signals.json was never created:
/ExtSSD/repos/build > export QT_QPA_DBUS_SIGNALS_SAVE=/tmp/signals.json
/ExtSSD/repos/build > ./bin/sigil
/ExtSSD/repos/build > cat /tmp/signals.json 12s
cat: /tmp/signals.json: No such file or directorySo I am at a loss to explain why styleHints() never sends out the changed signal.
If I instead add a QEvent handling routine to our MainApplication.cpp I can easily see the QEvent ApplicationPaletteChanged event and part of Sigil responds to that but the styleHints signal itself is never sent out.. We tested the exact same code in Sigil on Windows and MacOS and the styleHints() changed signal is in fact sent out properly. It is just with Linux (KDE 6 Plasma) that no signal is being produced.
If I query styleHints()->colorScheme() upon app startup I get Qt::ColorScheme::Unknown under Linux only. Never on macOS or Windows. This is with standard BreathLight and BreathDark theme changed System settings.
From everything I have read here this should work. But it does not. If I create my own signal based on the MainApplication ApplicationPaletteChanged signal, this all works, but that kind of defeats the purpose.
Any ideas of where or how to proceed from here in tracking down why this does not work.
-
I think you could make your unix theme code much more robust to where different distributions store their config files for kde and what they store as ColorScheme by simply swapping the order of the lines following:
https://github.com/qt/qtbase/blob/dev/src/gui/platform/unix/qgenericunixthemes.cpp#L664
So that */ColorScheme is looked for and not just root level ColorScheme and then if nothing found still call updateColorScheme with an empty string so that your code that checks palette text vs base colour lightness has a chance to work to classify as light or dark instead of defaulting to Qt::ColorScheme::Unknown which in turn effectively prevents any theme change signal from being produced.
The exact name of the ColorScheme is truly meaningless to this process, isn't it?
Perhaps use the General/ColorSchemeHashValue to determine if any theme change has occurred would be better leaving the user to determine if they want to call it light or dark.
-
Which Qt version are you building against? Either it’s the wrong Qt version, or it draws a platform theme, that doesn’t use the DBus listener. Hard to say. It works for me on openSuSE Tumbleweed with Qt 6.7.1 and later.
On a general note: It feels bad, to let you down but all supported Linux distributions work out of the box. Mechanisms are provided for unsupported versions, to implement workarounds. If you hold a commercial license, contact your account manager for professional support. If you use Qt open source, feel free to submit a patch with sufficient testing against regressions for supported platforms. I’ll gladly review it.
-
I am using Qt 6.7.0 from Manjaro Testing. I understand you do not think it is a bug since it passes your "supported" platforms tests.
But in all honesty, this code if very fragile. Under kde if no ColorScheme value is found in the local kdeglobals file (say the stringliteral is key is actually "General/ColorScheme" in this case) then an empty QVariant (invalid) is returned. That makes it default to Qt::ColorScheme::Unknown so even though new palette colors are loaded, and the dbus signals are actually received, no Qt signal is actually sent out because Qt::ColorScheme::Unknown is never changed.
I would think that improving the robustness of this code would be important to Qt. Especially for KDE Plasma 6 users on Arch and Manjaro.
Again one way to make this robust in the KDE platform code is to look for an invalid QVariant returned from searching kdeglobals for "ColorScheme" and replace it with a null string to pass to updateColorScheme so that the actual new palette can be used to detect light vs dark.
ie. here ...
const QVariant colorScheme = readKdeSetting(QStringLiteral("ColorScheme"), kdeDirs, kdeVersion, kdeSettings); if (colorScheme.isValid()) updateColorScheme(colorScheme.toString()); else m_colorScheme = Qt::ColorScheme::Unknown;
Additionally for KDE you can walk all the keys in a setting file to identify that you need to look for General/ColorScheme or just ColorScheme.
With some added flexibility you can make things much more robust to different versions of KDE.
-
But in all honesty, this code if very fragile. Under kde if no ColorScheme value is found in the local kdeglobals file (say the stringliteral is key is actually "General/ColorScheme" in this case) then an empty QVariant (invalid) is returned.
All KDE settins read in
QKdeThemePrivate::refresh()
are (sadly hard wired in the code, and) updated when found in the expected place.In other words, they aren't updated if the respective
readKdeSetting()
returns a default constructedQVariant
.
I'd call that strict, which means it is fragile on purpose.
There's something wrong, if a setting isn't in the expected place.There is room for improvement as regards logging and flexibility. The settings we need from KDE could be put in an enum, and mapped to their KDE settings key. That could be exported to / imported from a JSON file. Logging could be added for settings which couldn't be read. I'll look into this!