File indexing completed on 2024-10-13 13:11:20
0001 /* 0002 * SPDX-FileCopyrightText: 2019 Mikhail Zolotukhin <zomial@protonmail.com> 0003 * SPDX-FileCopyrightText: 2019 Nicolas Fella <nicolas.fella@gmx.de> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #include "gtkconfig.h" 0009 0010 #include <KColorScheme> 0011 #include <KPluginFactory> 0012 #include <KWindowSystem> 0013 0014 #include <QDBusConnection> 0015 #include <QDBusMessage> 0016 #include <QFont> 0017 #include <QGuiApplication> 0018 #include <QTimer> 0019 0020 #include <algorithm> 0021 #include <cmath> 0022 0023 #include "config_editor/custom_css.h" 0024 #include "config_editor/gsettings.h" 0025 #include "config_editor/gtk2.h" 0026 #include "config_editor/settings_ini.h" 0027 #include "config_editor/xsettings.h" 0028 #include "gsd-xsettings-manager/gsd-xsettings-manager.h" 0029 0030 K_PLUGIN_CLASS_WITH_JSON(GtkConfig, "gtkconfig.json") 0031 0032 GtkConfig::GtkConfig(QObject *parent, const QVariantList &) 0033 : KDEDModule(parent) 0034 , configValueProvider(new ConfigValueProvider()) 0035 , themePreviewer(new ThemePreviewer(this)) 0036 , kdeglobalsConfigWatcher(KConfigWatcher::create(KSharedConfig::openConfig())) 0037 , kwinConfigWatcher(KConfigWatcher::create(KSharedConfig::openConfig(QStringLiteral("kwinrc")))) 0038 , kcmfontsConfigWatcher(KConfigWatcher::create(KSharedConfig::openConfig(QStringLiteral("kcmfonts")))) 0039 , kcminputConfigWatcher(KConfigWatcher::create(KSharedConfig::openConfig(QStringLiteral("kcminputrc")))) 0040 , breezeConfigWatcher(KConfigWatcher::create(KSharedConfig::openConfig(QStringLiteral("breezerc")))) 0041 { 0042 QDBusConnection dbus = QDBusConnection::sessionBus(); 0043 dbus.registerService(QStringLiteral("org.kde.GtkConfig")); 0044 dbus.registerObject(QStringLiteral("/GtkConfig"), this, QDBusConnection::ExportScriptableSlots); 0045 0046 if (qgetenv("GTK_USE_PORTAL") != "1" && KWindowSystem::isPlatformWayland()) { 0047 m_gsdXsettingsManager = new GSDXSettingsManager(this); 0048 } 0049 0050 connect(kdeglobalsConfigWatcher.data(), &KConfigWatcher::configChanged, this, &GtkConfig::onKdeglobalsSettingsChange); 0051 connect(kwinConfigWatcher.data(), &KConfigWatcher::configChanged, this, &GtkConfig::onKWinSettingsChange); 0052 connect(kcmfontsConfigWatcher.data(), &KConfigWatcher::configChanged, this, &GtkConfig::onKCMFontsSettingsChange); 0053 connect(kcminputConfigWatcher.data(), &KConfigWatcher::configChanged, this, &GtkConfig::onKCMInputSettingsChange); 0054 connect(breezeConfigWatcher.data(), &KConfigWatcher::configChanged, this, &GtkConfig::onBreezeSettingsChange); 0055 0056 Gtk2ConfigEditor::removeLegacyStrings(); 0057 applyAllSettings(); 0058 } 0059 0060 GtkConfig::~GtkConfig() 0061 { 0062 QDBusConnection dbus = QDBusConnection::sessionBus(); 0063 dbus.unregisterService(QStringLiteral("org.kde.GtkConfig")); 0064 dbus.unregisterObject(QStringLiteral("/GtkConfig")); 0065 } 0066 0067 void GtkConfig::setGtkTheme(const QString &themeName) const 0068 { 0069 Gtk2ConfigEditor::setValue(QStringLiteral("gtk-theme-name"), themeName); 0070 GSettingsEditor::setValue("gtk-theme", themeName); 0071 SettingsIniEditor::setValue(QStringLiteral("gtk-theme-name"), themeName); 0072 XSettingsEditor::setValue(QStringLiteral("Net/ThemeName"), themeName); 0073 0074 // Window decorations are part of the theme, in case of Breeze we inject custom ones from KWin 0075 setWindowDecorationsAppearance(); 0076 } 0077 0078 QString GtkConfig::gtkTheme() const 0079 { 0080 return SettingsIniEditor::value(QStringLiteral("gtk-theme-name")); 0081 } 0082 0083 void GtkConfig::showGtkThemePreview(const QString &themeName) const 0084 { 0085 themePreviewer->showGtk3App(themeName); 0086 } 0087 0088 void GtkConfig::setFont() const 0089 { 0090 const QString configFontName = configValueProvider->fontName(); 0091 Gtk2ConfigEditor::setValue(QStringLiteral("gtk-font-name"), configFontName); 0092 GSettingsEditor::setValue("font-name", configFontName); 0093 SettingsIniEditor::setValue(QStringLiteral("gtk-font-name"), configFontName); 0094 XSettingsEditor::setValue(QStringLiteral("Gtk/FontName"), configFontName); 0095 } 0096 0097 void GtkConfig::setIconTheme() const 0098 { 0099 const QString iconThemeName = configValueProvider->iconThemeName(); 0100 Gtk2ConfigEditor::setValue(QStringLiteral("gtk-icon-theme-name"), iconThemeName); 0101 GSettingsEditor::setValue("icon-theme", iconThemeName); 0102 SettingsIniEditor::setValue(QStringLiteral("gtk-icon-theme-name"), iconThemeName); 0103 XSettingsEditor::setValue(QStringLiteral("Net/IconThemeName"), iconThemeName); 0104 } 0105 0106 void GtkConfig::setCursorTheme() const 0107 { 0108 const QString cursorThemeName = configValueProvider->cursorThemeName(); 0109 Gtk2ConfigEditor::setValue(QStringLiteral("gtk-cursor-theme-name"), cursorThemeName); 0110 GSettingsEditor::setValue("cursor-theme", cursorThemeName); 0111 SettingsIniEditor::setValue(QStringLiteral("gtk-cursor-theme-name"), cursorThemeName); 0112 XSettingsEditor::setValue(QStringLiteral("Gtk/CursorThemeName"), cursorThemeName); 0113 } 0114 0115 void GtkConfig::setCursorSize() const 0116 { 0117 const int cursorSize = configValueProvider->cursorSize(); 0118 Gtk2ConfigEditor::setValue(QStringLiteral("gtk-cursor-theme-size"), cursorSize); 0119 GSettingsEditor::setValue("cursor-size", cursorSize); 0120 SettingsIniEditor::setValue(QStringLiteral("gtk-cursor-theme-size"), cursorSize); 0121 XSettingsEditor::setValue(QStringLiteral("Gtk/CursorThemeSize"), cursorSize); 0122 } 0123 0124 void GtkConfig::setIconsOnButtons() const 0125 { 0126 const bool iconsOnButtonsConfigValue = configValueProvider->iconsOnButtons(); 0127 Gtk2ConfigEditor::setValue(QStringLiteral("gtk-button-images"), iconsOnButtonsConfigValue); 0128 // Deprecated in GTK 4 0129 SettingsIniEditor::setValue(QStringLiteral("gtk-button-images"), iconsOnButtonsConfigValue, 3); 0130 XSettingsEditor::setValue(QStringLiteral("Gtk/ButtonImages"), iconsOnButtonsConfigValue); 0131 } 0132 0133 void GtkConfig::setIconsInMenus() const 0134 { 0135 const bool iconsInMenusConfigValue = configValueProvider->iconsInMenus(); 0136 Gtk2ConfigEditor::setValue(QStringLiteral("gtk-menu-images"), iconsInMenusConfigValue); 0137 // Deprecated in GTK 4 0138 SettingsIniEditor::setValue(QStringLiteral("gtk-menu-images"), iconsInMenusConfigValue, 3); 0139 XSettingsEditor::setValue(QStringLiteral("Gtk/MenuImages"), iconsInMenusConfigValue); 0140 } 0141 0142 void GtkConfig::setToolbarStyle() const 0143 { 0144 const int toolbarStyle = configValueProvider->toolbarStyle(); 0145 Gtk2ConfigEditor::setValue(QStringLiteral("gtk-toolbar-style"), toolbarStyle); 0146 GSettingsEditor::setValueAsEnum("toolbar-style", toolbarStyle); 0147 // Deprecated in GTK 4 0148 SettingsIniEditor::setValue(QStringLiteral("gtk-toolbar-style"), toolbarStyle, 3); 0149 XSettingsEditor::setValue(QStringLiteral("Gtk/ToolbarStyle"), toolbarStyle); 0150 } 0151 0152 void GtkConfig::setScrollbarBehavior() const 0153 { 0154 const bool scrollbarBehavior = configValueProvider->scrollbarBehavior(); 0155 Gtk2ConfigEditor::setValue(QStringLiteral("gtk-primary-button-warps-slider"), scrollbarBehavior); 0156 SettingsIniEditor::setValue(QStringLiteral("gtk-primary-button-warps-slider"), scrollbarBehavior); 0157 XSettingsEditor::setValue(QStringLiteral("Gtk/PrimaryButtonWarpsSlider"), scrollbarBehavior); 0158 } 0159 0160 void GtkConfig::setDarkThemePreference() const 0161 { 0162 const bool preferDarkTheme = configValueProvider->preferDarkTheme(); 0163 SettingsIniEditor::setValue(QStringLiteral("gtk-application-prefer-dark-theme"), preferDarkTheme); 0164 // https://gitlab.gnome.org/GNOME/gsettings-desktop-schemas/-/blob/master/headers/gdesktop-enums.h 0165 GSettingsEditor::setValueAsEnum("color-scheme", 0166 preferDarkTheme ? 1 /*G_DESKTOP_COLOR_SCHEME_PREFER_DARK*/ : 0 /*G_DESKTOP_COLOR_SCHEME_DEFAULT*/, 0167 "org.gnome.desktop.interface"); 0168 } 0169 0170 void GtkConfig::setWindowDecorationsAppearance() const 0171 { 0172 if (gtkTheme() == QStringLiteral("Breeze")) { // Only Breeze GTK supports custom decoration buttons 0173 const auto windowDecorationsButtonsImages = configValueProvider->windowDecorationsButtonsImages(); 0174 CustomCssEditor::setCustomClientSideDecorations(windowDecorationsButtonsImages); 0175 } else { 0176 CustomCssEditor::disableCustomClientSideDecorations(); 0177 } 0178 } 0179 0180 void GtkConfig::setWindowDecorationsButtonsOrder() const 0181 { 0182 const QString windowDecorationsButtonOrder = configValueProvider->windowDecorationsButtonsOrder(); 0183 GSettingsEditor::setValue("button-layout", windowDecorationsButtonOrder, "org.gnome.desktop.wm.preferences"); 0184 SettingsIniEditor::setValue(QStringLiteral("gtk-decoration-layout"), windowDecorationsButtonOrder); 0185 XSettingsEditor::setValue(QStringLiteral("Gtk/DecorationLayout"), windowDecorationsButtonOrder); 0186 } 0187 0188 void GtkConfig::setEnableAnimations() const 0189 { 0190 const bool enableAnimations = configValueProvider->enableAnimations(); 0191 Gtk2ConfigEditor::setValue(QStringLiteral("gtk-enable-animations"), enableAnimations); 0192 GSettingsEditor::setValue("enable-animations", enableAnimations); 0193 SettingsIniEditor::setValue(QStringLiteral("gtk-enable-animations"), enableAnimations); 0194 XSettingsEditor::setValue(QStringLiteral("Gtk/EnableAnimations"), enableAnimations); 0195 if (m_gsdXsettingsManager) { 0196 m_gsdXsettingsManager->enableAnimationsChanged(); 0197 } 0198 } 0199 0200 void GtkConfig::setGlobalScale() const 0201 { 0202 const unsigned scaleFactor = configValueProvider->x11GlobalScaleFactor(); 0203 XSettingsEditor::setValue(QStringLiteral("Gdk/WindowScalingFactor"), scaleFactor); 0204 GSettingsEditor::setValue("scaling-factor", scaleFactor); // For IntelliJ IDEA 0205 } 0206 0207 void GtkConfig::setTextScale() const 0208 { 0209 const double x11Scale = configValueProvider->x11GlobalScaleFactor(); 0210 const int x11ScaleIntegerPart = int(x11Scale); 0211 0212 const int forceFontDpi = configValueProvider->fontDpi(); 0213 0214 int x11TextDpiAbsolute = 96 * 1024; 0215 double waylandTextScaleFactor = 1.0; 0216 0217 if (forceFontDpi == 0) { 0218 x11TextDpiAbsolute = (96 * 1024) * x11Scale; 0219 } else { 0220 x11TextDpiAbsolute = (forceFontDpi * 1024); 0221 0222 if (!KWindowSystem::isPlatformX11()) { 0223 x11TextDpiAbsolute *= x11Scale; 0224 } 0225 0226 waylandTextScaleFactor = double(forceFontDpi) / 96.0; 0227 waylandTextScaleFactor = std::clamp(waylandTextScaleFactor, 0.5, 3.0); 0228 } 0229 0230 XSettingsEditor::unsetValue(QStringLiteral("Xft/DPI")); 0231 SettingsIniEditor::setValue(QStringLiteral("gtk-xft-dpi"), x11TextDpiAbsolute); 0232 XSettingsEditor::setValue(QStringLiteral("Gdk/UnscaledDPI"), x11TextDpiAbsolute / x11ScaleIntegerPart); 0233 GSettingsEditor::setValue("text-scaling-factor", waylandTextScaleFactor); 0234 } 0235 0236 void GtkConfig::setColors() const 0237 { 0238 CustomCssEditor::addGtkModule(QStringLiteral("colorreload-gtk-module")); 0239 if (m_gsdXsettingsManager) { 0240 m_gsdXsettingsManager->modulesChanged(); 0241 } 0242 // modulesChanged signal will take some time to reach a GTK app, so explicitly wait a moment 0243 QTimer::singleShot(200, this, [this] { 0244 const QMap<QString, QColor> colors = configValueProvider->colors(); 0245 CustomCssEditor::setColors(colors); 0246 }); 0247 } 0248 0249 void GtkConfig::applyAllSettings() const 0250 { 0251 setFont(); 0252 setIconTheme(); 0253 setCursorTheme(); 0254 setCursorSize(); 0255 setIconsOnButtons(); 0256 setIconsInMenus(); 0257 setToolbarStyle(); 0258 setScrollbarBehavior(); 0259 setDarkThemePreference(); 0260 setWindowDecorationsAppearance(); 0261 setWindowDecorationsButtonsOrder(); 0262 setEnableAnimations(); 0263 setGlobalScale(); 0264 setTextScale(); 0265 setColors(); 0266 } 0267 0268 void GtkConfig::onKdeglobalsSettingsChange(const KConfigGroup &group, const QByteArrayList &names) const 0269 { 0270 if (group.name() == QStringLiteral("KDE")) { 0271 if (names.contains(QByteArrayLiteral("AnimationDurationFactor"))) { 0272 setEnableAnimations(); 0273 } 0274 if (names.contains(QByteArrayLiteral("ShowIconsInMenuItems"))) { 0275 setIconsInMenus(); 0276 } 0277 if (names.contains(QByteArrayLiteral("ShowIconsOnPushButtons"))) { 0278 setIconsOnButtons(); 0279 } 0280 if (names.contains(QByteArrayLiteral("ScrollbarLeftClickNavigatesByPage"))) { 0281 setScrollbarBehavior(); 0282 } 0283 } else if (group.name() == QStringLiteral("Icons")) { 0284 if (names.contains(QByteArrayLiteral("Theme"))) { 0285 setIconTheme(); 0286 } 0287 } else if (group.name() == QStringLiteral("General")) { 0288 if (names.contains(QByteArrayLiteral("font"))) { 0289 setFont(); 0290 } 0291 if (names.contains(QByteArrayLiteral("ColorScheme")) || names.contains(QByteArrayLiteral("AccentColor"))) { 0292 setColors(); 0293 setDarkThemePreference(); 0294 setWindowDecorationsAppearance(); // Decorations' color can depend on the current color scheme 0295 } 0296 } else if (group.name() == QStringLiteral("KScreen")) { 0297 if (names.contains(QByteArrayLiteral("ScaleFactor"))) { 0298 setGlobalScale(); 0299 // setTextScale() will be called in onKCMFontsSettingsChange 0300 } 0301 } else if (group.name() == QStringLiteral("Toolbar style")) { 0302 if (names.contains(QByteArrayLiteral("ToolButtonStyle"))) { 0303 setToolbarStyle(); 0304 } 0305 } 0306 } 0307 0308 void GtkConfig::onKWinSettingsChange(const KConfigGroup &group, const QByteArrayList &names) const 0309 { 0310 if (group.name() == QStringLiteral("org.kde.kdecoration2")) { 0311 if (names.contains(QByteArrayLiteral("ButtonsOnRight")) // 0312 || names.contains(QByteArrayLiteral("ButtonsOnLeft"))) { 0313 setWindowDecorationsButtonsOrder(); 0314 } 0315 if (names.contains(QByteArrayLiteral("theme"))) { 0316 setWindowDecorationsAppearance(); 0317 } 0318 } else if (group.name() == QStringLiteral("Xwayland")) { 0319 if (names.contains(QByteArrayLiteral("Scale"))) { 0320 setGlobalScale(); 0321 setTextScale(); 0322 } 0323 } 0324 } 0325 0326 void GtkConfig::onKCMFontsSettingsChange(const KConfigGroup &group, const QByteArrayList &names) const 0327 { 0328 if (group.name() == QStringLiteral("General")) { 0329 if (names.contains("forceFontDPI") || names.contains("forceFontDPIWayland")) { 0330 setTextScale(); 0331 } 0332 } 0333 } 0334 0335 void GtkConfig::onKCMInputSettingsChange(const KConfigGroup &group, const QByteArrayList &names) const 0336 { 0337 if (group.name() == QStringLiteral("Mouse")) { 0338 if (names.contains("cursorTheme")) { 0339 setCursorTheme(); 0340 } 0341 if (names.contains("cursorSize")) { 0342 setCursorSize(); 0343 } 0344 } 0345 } 0346 0347 void GtkConfig::onBreezeSettingsChange(const KConfigGroup &group, const QByteArrayList &names) const 0348 { 0349 if (group.name() == QStringLiteral("Common") // 0350 && names.contains("OutlineCloseButton")) { 0351 setWindowDecorationsAppearance(); 0352 } 0353 } 0354 0355 #include "gtkconfig.moc"