Warning, file /plasma/plasma-workspace/kcms/krdb/krdb.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 KRDB - puts current KDE color scheme into preprocessor statements 0003 cats specially written application default files and uses xrdb -merge to 0004 write to RESOURCE_MANAGER. Thus it gives a simple way to make non-KDE 0005 applications fit in with the desktop 0006 0007 SPDX-FileCopyrightText: 1998 Mark Donohoe 0008 SPDX-FileCopyrightText: 2001 Waldo Bastian <bastian@kde.org> 0009 SPDX-FileCopyrightText: 2002 Karol Szwed <gallium@kde.org> 0010 SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org> 0011 0012 reworked for KDE 2.0: 0013 SPDX-FileCopyrightText: 1999 Dirk A. Mueller 0014 0015 add support for GTK applications: 0016 SPDX-FileCopyrightText: 2001 Matthias Ettrich 0017 0018 SPDX-License-Identifier: GPL-2.0-or-later 0019 */ 0020 0021 #include <config-X11.h> 0022 #include <config-workspace.h> 0023 #include <limits.h> 0024 #include <stdlib.h> 0025 #include <string.h> 0026 #include <unistd.h> 0027 0028 #undef Unsorted 0029 #include <QApplication> 0030 #include <QBuffer> 0031 #include <QDir> 0032 #include <QFontDatabase> 0033 #include <QSettings> 0034 0035 #include <QByteArray> 0036 #include <QDBusConnection> 0037 #include <QDateTime> 0038 #include <QDebug> 0039 #include <QPixmap> 0040 #include <QSaveFile> 0041 #include <QTemporaryFile> 0042 #include <QTextStream> 0043 0044 #include <KColorScheme> 0045 #include <KConfig> 0046 #include <KConfigGroup> 0047 #include <KLocalizedString> 0048 #include <KProcess> 0049 #include <KWindowSystem> 0050 0051 #include <KUpdateLaunchEnvironmentJob> 0052 0053 #include "krdb.h" 0054 #if HAVE_X11 0055 #include <X11/Xlib.h> 0056 #include <private/qtx11extras_p.h> 0057 #endif 0058 0059 #include <filesystem> 0060 0061 using namespace Qt::StringLiterals; 0062 0063 inline const char *gtkEnvVar(int version) 0064 { 0065 return 2 == version ? "GTK2_RC_FILES" : "GTK_RC_FILES"; 0066 } 0067 0068 inline QLatin1String sysGtkrc(int version) 0069 { 0070 std::error_code error; 0071 if (version == 2) { 0072 if (std::filesystem::exists("/etc/opt/gnome/gtk-2.0", error) && !error) { 0073 return QLatin1String("/etc/opt/gnome/gtk-2.0/gtkrc"); 0074 } 0075 return QLatin1String("/etc/gtk-2.0/gtkrc"); 0076 } 0077 if (std::filesystem::exists("/etc/opt/gnome/gtk", error) && !error) { 0078 return QLatin1String("/etc/opt/gnome/gtk/gtkrc"); 0079 } 0080 return QLatin1String("/etc/gtk/gtkrc"); 0081 } 0082 0083 inline QLatin1String userGtkrc(int version) 0084 { 0085 return version == 2 ? QLatin1String("/.gtkrc-2.0") : QLatin1String("/.gtkrc"); 0086 } 0087 0088 // ----------------------------------------------------------------------------- 0089 static QString writableGtkrc(int version) 0090 { 0091 QString gtkrc = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); 0092 QDir dir; 0093 dir.mkpath(gtkrc); 0094 gtkrc += 2 == version ? "/gtkrc-2.0" : "/gtkrc"; 0095 return gtkrc; 0096 } 0097 0098 // ----------------------------------------------------------------------------- 0099 static void applyGtkStyles(int version) 0100 { 0101 const char *varName = gtkEnvVar(version); 0102 const char *envVar = getenv(varName); 0103 if (envVar) { // Already set by user, don't override it 0104 return; 0105 } 0106 0107 const QByteArray gtkrc(envVar); 0108 const QString userHomeGtkrc = QDir::homePath() + userGtkrc(version); 0109 QStringList list = QFile::decodeName(gtkrc).split(QLatin1Char(':')); 0110 if (!list.contains(userHomeGtkrc)) { 0111 list.prepend(userHomeGtkrc); 0112 } 0113 0114 const QLatin1String systemGtkrc = sysGtkrc(version); 0115 if (!list.contains(systemGtkrc)) { 0116 list.prepend(systemGtkrc); 0117 } 0118 0119 list.removeAll(QLatin1String("")); 0120 0121 const QString gtkkde = writableGtkrc(version); 0122 list.removeAll(gtkkde); 0123 list.append(gtkkde); 0124 0125 // Pass env. var to kdeinit. 0126 const QString value = list.join(QLatin1Char(':')); 0127 QProcessEnvironment newEnv; 0128 newEnv.insert(varName, value); 0129 new KUpdateLaunchEnvironmentJob(newEnv); 0130 } 0131 0132 // ----------------------------------------------------------------------------- 0133 0134 static void applyQtColors(KSharedConfigPtr kglobalcfg, QSettings &settings, QPalette &newPal) 0135 { 0136 QStringList actcg, inactcg, discg; 0137 /* export kde color settings */ 0138 int i; 0139 for (i = 0; i < QPalette::NColorRoles; i++) 0140 actcg << newPal.color(QPalette::Active, (QPalette::ColorRole)i).name(); 0141 for (i = 0; i < QPalette::NColorRoles; i++) 0142 inactcg << newPal.color(QPalette::Inactive, (QPalette::ColorRole)i).name(); 0143 for (i = 0; i < QPalette::NColorRoles; i++) 0144 discg << newPal.color(QPalette::Disabled, (QPalette::ColorRole)i).name(); 0145 0146 settings.setValue(QStringLiteral("/qt/Palette/active"), actcg); 0147 settings.setValue(QStringLiteral("/qt/Palette/inactive"), inactcg); 0148 settings.setValue(QStringLiteral("/qt/Palette/disabled"), discg); 0149 0150 // export kwin's colors to qtrc for kstyle to use 0151 KConfigGroup wmCfgGroup(kglobalcfg, u"WM"_s); 0152 0153 // active colors 0154 QColor clr = newPal.color(QPalette::Active, QPalette::Window); 0155 clr = wmCfgGroup.readEntry("activeBackground", clr); 0156 settings.setValue(QStringLiteral("/qt/KWinPalette/activeBackground"), clr.name()); 0157 if (QPixmap::defaultDepth() > 8) 0158 clr = clr.darker(110); 0159 clr = wmCfgGroup.readEntry("activeBlend", clr); 0160 settings.setValue(QStringLiteral("/qt/KWinPalette/activeBlend"), clr.name()); 0161 clr = newPal.color(QPalette::Active, QPalette::HighlightedText); 0162 clr = wmCfgGroup.readEntry("activeForeground", clr); 0163 settings.setValue(QStringLiteral("/qt/KWinPalette/activeForeground"), clr.name()); 0164 clr = newPal.color(QPalette::Active, QPalette::Window); 0165 clr = wmCfgGroup.readEntry("frame", clr); 0166 settings.setValue(QStringLiteral("/qt/KWinPalette/frame"), clr.name()); 0167 clr = wmCfgGroup.readEntry("activeTitleBtnBg", clr); 0168 settings.setValue(QStringLiteral("/qt/KWinPalette/activeTitleBtnBg"), clr.name()); 0169 0170 // inactive colors 0171 clr = newPal.color(QPalette::Inactive, QPalette::Window); 0172 clr = wmCfgGroup.readEntry("inactiveBackground", clr); 0173 settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveBackground"), clr.name()); 0174 if (QPixmap::defaultDepth() > 8) 0175 clr = clr.darker(110); 0176 clr = wmCfgGroup.readEntry("inactiveBlend", clr); 0177 settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveBlend"), clr.name()); 0178 clr = newPal.color(QPalette::Inactive, QPalette::Window).darker(); 0179 clr = wmCfgGroup.readEntry("inactiveForeground", clr); 0180 settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveForeground"), clr.name()); 0181 clr = newPal.color(QPalette::Inactive, QPalette::Window); 0182 clr = wmCfgGroup.readEntry("inactiveFrame", clr); 0183 settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveFrame"), clr.name()); 0184 clr = wmCfgGroup.readEntry("inactiveTitleBtnBg", clr); 0185 settings.setValue(QStringLiteral("/qt/KWinPalette/inactiveTitleBtnBg"), clr.name()); 0186 0187 KConfigGroup kdeCfgGroup(kglobalcfg, u"KDE"_s); 0188 settings.setValue(QStringLiteral("/qt/KDE/contrast"), kdeCfgGroup.readEntry("contrast", 7)); 0189 } 0190 0191 // ----------------------------------------------------------------------------- 0192 0193 static void applyQtSettings(KSharedConfigPtr kglobalcfg, QSettings &settings) 0194 { 0195 /* export font settings */ 0196 0197 // NOTE keep this in sync with kfontsettingsdata in plasma-integration (cf. also Bug 378262) 0198 QFont defaultFont(QStringLiteral("Noto Sans"), 10, -1); 0199 defaultFont.setStyleHint(QFont::SansSerif); 0200 0201 const KConfigGroup configGroup(KSharedConfig::openConfig(), u"General"_s); 0202 const QString fontInfo = configGroup.readEntry(QStringLiteral("font"), QString()); 0203 if (!fontInfo.isEmpty()) { 0204 defaultFont.fromString(fontInfo); 0205 } 0206 0207 settings.setValue(QStringLiteral("/qt/font"), defaultFont.toString()); 0208 0209 /* export effects settings */ 0210 KConfigGroup kdeCfgGroup(kglobalcfg, u"General"_s); 0211 bool effectsEnabled = kdeCfgGroup.readEntry("EffectsEnabled", false); 0212 bool fadeMenus = kdeCfgGroup.readEntry("EffectFadeMenu", false); 0213 bool fadeTooltips = kdeCfgGroup.readEntry("EffectFadeTooltip", false); 0214 bool animateCombobox = kdeCfgGroup.readEntry("EffectAnimateCombo", false); 0215 0216 QStringList guieffects; 0217 if (effectsEnabled) { 0218 guieffects << QStringLiteral("general"); 0219 if (fadeMenus) 0220 guieffects << QStringLiteral("fademenu"); 0221 if (animateCombobox) 0222 guieffects << QStringLiteral("animatecombo"); 0223 if (fadeTooltips) 0224 guieffects << QStringLiteral("fadetooltip"); 0225 } else 0226 guieffects << QStringLiteral("none"); 0227 0228 settings.setValue(QStringLiteral("/qt/GUIEffects"), guieffects); 0229 } 0230 0231 // ----------------------------------------------------------------------------- 0232 0233 static void copyFile(QFile &tmp, QString const &filename, bool) 0234 { 0235 QFile f(filename); 0236 if (f.open(QIODevice::ReadOnly)) { 0237 QByteArray buf(8192, ' '); 0238 while (!f.atEnd()) { 0239 int read = f.read(buf.data(), buf.size()); 0240 if (read > 0) 0241 tmp.write(buf.data(), read); 0242 } 0243 } 0244 } 0245 0246 // ----------------------------------------------------------------------------- 0247 0248 static void createGtkrc(const QPalette &cg, bool exportGtkTheme, const QString >kTheme, int version) 0249 { 0250 Q_UNUSED(cg); 0251 // lukas: why does it create in ~/.kde/share/config ??? 0252 // pfeiffer: so that we don't overwrite the user's gtkrc. 0253 // it is found via the GTK_RC_FILES environment variable. 0254 QSaveFile saveFile(writableGtkrc(version)); 0255 if (!saveFile.open(QIODevice::WriteOnly)) 0256 return; 0257 0258 QTextStream t(&saveFile); 0259 t << i18n( 0260 "# created by KDE Plasma, %1\n" 0261 "#\n", 0262 QDateTime::currentDateTime().toString()); 0263 0264 if (2 == version) { // we should maybe check for MacOS settings here 0265 using Qt::endl; 0266 t << endl; 0267 t << "gtk-alternative-button-order = 1" << endl; 0268 t << endl; 0269 } 0270 0271 if (exportGtkTheme) { 0272 QString gtkStyle; 0273 if (gtkTheme.toLower() == QLatin1String("oxygen")) 0274 gtkStyle = QStringLiteral("oxygen-gtk"); 0275 else 0276 gtkStyle = gtkTheme; 0277 0278 bool exist_gtkrc = false; 0279 QByteArray gtkrc = getenv(gtkEnvVar(version)); 0280 QStringList listGtkrc = QFile::decodeName(gtkrc).split(QLatin1Char(':')); 0281 if (listGtkrc.contains(saveFile.fileName())) 0282 listGtkrc.removeAll(saveFile.fileName()); 0283 listGtkrc.append(QDir::homePath() + userGtkrc(version)); 0284 listGtkrc.append(QDir::homePath() + "/.gtkrc-2.0-kde"); 0285 listGtkrc.append(QDir::homePath() + "/.gtkrc-2.0-kde4"); 0286 listGtkrc.removeAll(QLatin1String("")); 0287 listGtkrc.removeDuplicates(); 0288 for (int i = 0; i < listGtkrc.size(); ++i) { 0289 if ((exist_gtkrc = QFile::exists(listGtkrc.at(i)))) 0290 break; 0291 } 0292 0293 if (!exist_gtkrc) { 0294 QString gtk2ThemeFilename; 0295 gtk2ThemeFilename = QStringLiteral("%1/.themes/%2/gtk-2.0/gtkrc").arg(QDir::homePath(), gtkStyle); 0296 if (!QFile::exists(gtk2ThemeFilename)) { 0297 QStringList gtk2ThemePath; 0298 gtk2ThemeFilename.clear(); 0299 QByteArray xdgDataDirs = getenv("XDG_DATA_DIRS"); 0300 gtk2ThemePath.append(QDir::homePath() + "/.local"); 0301 gtk2ThemePath.append(QFile::decodeName(xdgDataDirs).split(QLatin1Char(':'))); 0302 gtk2ThemePath.removeDuplicates(); 0303 for (int i = 0; i < gtk2ThemePath.size(); ++i) { 0304 gtk2ThemeFilename = QStringLiteral("%1/themes/%2/gtk-2.0/gtkrc").arg(gtk2ThemePath.at(i), gtkStyle); 0305 if (QFile::exists(gtk2ThemeFilename)) 0306 break; 0307 else 0308 gtk2ThemeFilename.clear(); 0309 } 0310 } 0311 0312 if (!gtk2ThemeFilename.isEmpty()) { 0313 t << "include \"" << gtk2ThemeFilename << "\"" << Qt::endl; 0314 t << Qt::endl; 0315 t << "gtk-theme-name=\"" << gtkStyle << "\"" << Qt::endl; 0316 t << Qt::endl; 0317 } 0318 } 0319 } 0320 0321 saveFile.commit(); 0322 } 0323 0324 // ----------------------------------------------------------------------------- 0325 0326 int xftDpi() 0327 { 0328 KConfig cfg(QStringLiteral("kcmfonts")); 0329 int dpi = 0; 0330 0331 if (KWindowSystem::isPlatformWayland()) { 0332 KConfig cfg(QStringLiteral("kwinrc")); 0333 KConfigGroup xwaylandGroup = cfg.group(QStringLiteral("Xwayland")); 0334 qreal scale = xwaylandGroup.readEntry("Scale", 1.0); 0335 dpi = scale * 96; 0336 } else { 0337 KConfigGroup fontsCfg(&cfg, u"General"_s); 0338 dpi = fontsCfg.readEntry(QStringLiteral("forceFontDPI"), 96); 0339 } 0340 0341 return dpi; 0342 } 0343 0344 void runRdb(unsigned int flags) 0345 { 0346 // Obtain the application palette that is about to be set. 0347 bool exportQtColors = flags & KRdbExportQtColors; 0348 bool exportQtSettings = flags & KRdbExportQtSettings; 0349 bool exportXftSettings = flags & KRdbExportXftSettings; 0350 bool exportGtkTheme = flags & KRdbExportGtkTheme; 0351 0352 KSharedConfigPtr kglobalcfg = KSharedConfig::openConfig(QStringLiteral("kdeglobals")); 0353 KConfigGroup kglobals(kglobalcfg, u"KDE"_s); 0354 QPalette newPal = KColorScheme::createApplicationPalette(kglobalcfg); 0355 0356 QTemporaryFile tmpFile; 0357 if (!tmpFile.open()) { 0358 qDebug() << "Couldn't open temp file"; 0359 exit(0); 0360 } 0361 0362 KConfigGroup generalCfgGroup(kglobalcfg, u"General"_s); 0363 0364 QString gtkTheme; 0365 if (kglobals.hasKey("widgetStyle")) 0366 gtkTheme = kglobals.readEntry("widgetStyle"); 0367 else 0368 gtkTheme = QStringLiteral("oxygen"); 0369 0370 createGtkrc(newPal, exportGtkTheme, gtkTheme, 1); 0371 createGtkrc(newPal, exportGtkTheme, gtkTheme, 2); 0372 0373 // Merge ~/.Xresources or fallback to ~/.Xdefaults 0374 QString homeDir = QDir::homePath(); 0375 QString xResources = homeDir + "/.Xresources"; 0376 0377 // very primitive support for ~/.Xresources by appending it 0378 if (QFile::exists(xResources)) 0379 copyFile(tmpFile, xResources, true); 0380 else 0381 copyFile(tmpFile, homeDir + "/.Xdefaults", true); 0382 0383 // Export the Xcursor theme & size settings 0384 KConfigGroup mousecfg(KSharedConfig::openConfig(QStringLiteral("kcminputrc")), u"Mouse"_s); 0385 QString theme = mousecfg.readEntry("cursorTheme", QStringLiteral("breeze_cursors")); 0386 int cursorSize = mousecfg.readEntry("cursorSize", 24); 0387 0388 if (KWindowSystem::isPlatformWayland()) { 0389 KConfig kwinConfig(QStringLiteral("kwinrc")); 0390 KConfigGroup xwaylandGroup(&kwinConfig, u"Xwayland"_s); 0391 cursorSize *= xwaylandGroup.readEntry("Scale", 1.0); 0392 } 0393 0394 QString contents; 0395 contents += "Xcursor.theme: " + theme + '\n'; 0396 contents += "Xcursor.size: " + QString::number(cursorSize) + '\n'; 0397 0398 if (exportXftSettings) { 0399 contents += QLatin1String("Xft.antialias: "); 0400 if (generalCfgGroup.readEntry("XftAntialias", true)) 0401 contents += QLatin1String("1\n"); 0402 else 0403 contents += QLatin1String("0\n"); 0404 0405 QString hintStyle = generalCfgGroup.readEntry("XftHintStyle", "hintslight"); 0406 contents += QLatin1String("Xft.hinting: "); 0407 if (hintStyle.isEmpty()) 0408 contents += QLatin1String("-1\n"); 0409 else { 0410 if (hintStyle != QLatin1String("hintnone")) 0411 contents += QLatin1String("1\n"); 0412 else 0413 contents += QLatin1String("0\n"); 0414 contents += "Xft.hintstyle: " + hintStyle + '\n'; 0415 } 0416 0417 QString subPixel = generalCfgGroup.readEntry("XftSubPixel", "rgb"); 0418 if (!subPixel.isEmpty()) 0419 contents += "Xft.rgba: " + subPixel + '\n'; 0420 0421 int dpi = xftDpi(); 0422 if (dpi > 0) 0423 contents += "Xft.dpi: " + QString::number(dpi) + '\n'; 0424 else { 0425 KProcess queryProc; 0426 queryProc << QStringLiteral("xrdb") << QStringLiteral("-query"); 0427 queryProc.setOutputChannelMode(KProcess::OnlyStdoutChannel); 0428 queryProc.start(); 0429 if (queryProc.waitForFinished()) { 0430 QByteArray db = queryProc.readAllStandardOutput(); 0431 int idx1 = 0; 0432 while (idx1 < db.size()) { 0433 int idx2 = db.indexOf('\n', idx1); 0434 if (idx2 == -1) { 0435 idx2 = db.size() - 1; 0436 } 0437 const auto entry = QByteArray::fromRawData(db.constData() + idx1, idx2 - idx1 + 1); 0438 if (entry.startsWith("Xft.dpi:")) { 0439 db.remove(idx1, entry.size()); 0440 } else { 0441 idx1 = idx2 + 1; 0442 } 0443 } 0444 0445 KProcess loadProc; 0446 loadProc << QStringLiteral("xrdb") << QStringLiteral("-quiet") << QStringLiteral("-load") << QStringLiteral("-nocpp"); 0447 loadProc.start(); 0448 if (loadProc.waitForStarted()) { 0449 loadProc.write(db); 0450 loadProc.closeWriteChannel(); 0451 loadProc.waitForFinished(); 0452 } 0453 } 0454 } 0455 } 0456 0457 if (contents.length() > 0) 0458 tmpFile.write(contents.toLatin1(), contents.length()); 0459 0460 tmpFile.flush(); 0461 0462 KProcess proc; 0463 #ifndef NDEBUG 0464 proc << QStringLiteral("xrdb") << QStringLiteral("-merge") << tmpFile.fileName(); 0465 #else 0466 proc << "xrdb" 0467 << "-quiet" 0468 << "-merge" << tmpFile.fileName(); 0469 #endif 0470 proc.execute(); 0471 0472 // Needed for applications that don't set their own cursor. 0473 QProcess::execute(QStringLiteral("xsetroot"), {QStringLiteral("-cursor_name"), QStringLiteral("left_ptr")}); 0474 0475 applyGtkStyles(1); 0476 applyGtkStyles(2); 0477 0478 /* Qt exports */ 0479 if (exportQtColors || exportQtSettings) { 0480 QSettings *settings = new QSettings(QStringLiteral("Trolltech")); 0481 0482 if (exportQtColors) 0483 applyQtColors(kglobalcfg, *settings, newPal); // For kcmcolors 0484 0485 if (exportQtSettings) 0486 applyQtSettings(kglobalcfg, *settings); // For kcmstyle 0487 0488 delete settings; 0489 QCoreApplication::processEvents(); 0490 #if HAVE_X11 0491 if (qApp->platformName() == QLatin1String("xcb")) { 0492 // We let KIPC take care of ourselves, as we are in a KDE app with 0493 // QApp::setDesktopSettingsAware(false); 0494 // Instead of calling QApp::x11_apply_settings() directly, we instead 0495 // modify the timestamp which propagates the settings changes onto 0496 // Qt-only apps without adversely affecting ourselves. 0497 0498 // Cheat and use the current timestamp, since we just saved to qtrc. 0499 QDateTime settingsstamp = QDateTime::currentDateTime(); 0500 0501 static Atom qt_settings_timestamp = 0; 0502 if (!qt_settings_timestamp) { 0503 QString atomname(QStringLiteral("_QT_SETTINGS_TIMESTAMP_")); 0504 atomname += XDisplayName(nullptr); // Use the $DISPLAY envvar. 0505 qt_settings_timestamp = XInternAtom(QX11Info::display(), atomname.toLatin1(), False); 0506 } 0507 0508 QBuffer stamp; 0509 QDataStream s(&stamp.buffer(), QIODevice::WriteOnly); 0510 s << settingsstamp; 0511 XChangeProperty(QX11Info::display(), 0512 QX11Info::appRootWindow(), 0513 qt_settings_timestamp, 0514 qt_settings_timestamp, 0515 8, 0516 PropModeReplace, 0517 (unsigned char *)stamp.buffer().data(), 0518 stamp.buffer().size()); 0519 qApp->processEvents(); 0520 } 0521 #endif 0522 } 0523 }