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 <updatelaunchenvjob.h> 0052 0053 #include "krdb.h" 0054 #if HAVE_X11 0055 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0056 #include <private/qtx11extras_p.h> 0057 #else 0058 #include <QX11Info> 0059 #endif 0060 #include <X11/Xlib.h> 0061 #endif 0062 0063 #include <filesystem> 0064 0065 inline const char *gtkEnvVar(int version) 0066 { 0067 return 2 == version ? "GTK2_RC_FILES" : "GTK_RC_FILES"; 0068 } 0069 0070 inline QLatin1String sysGtkrc(int version) 0071 { 0072 std::error_code error; 0073 if (version == 2) { 0074 if (std::filesystem::exists("/etc/opt/gnome/gtk-2.0", error) && !error) { 0075 return QLatin1String("/etc/opt/gnome/gtk-2.0/gtkrc"); 0076 } 0077 return QLatin1String("/etc/gtk-2.0/gtkrc"); 0078 } 0079 if (std::filesystem::exists("/etc/opt/gnome/gtk", error) && !error) { 0080 return QLatin1String("/etc/opt/gnome/gtk/gtkrc"); 0081 } 0082 return QLatin1String("/etc/gtk/gtkrc"); 0083 } 0084 0085 inline QLatin1String userGtkrc(int version) 0086 { 0087 return version == 2 ? QLatin1String("/.gtkrc-2.0") : QLatin1String("/.gtkrc"); 0088 } 0089 0090 // ----------------------------------------------------------------------------- 0091 static QString writableGtkrc(int version) 0092 { 0093 QString gtkrc = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); 0094 QDir dir; 0095 dir.mkpath(gtkrc); 0096 gtkrc += 2 == version ? "/gtkrc-2.0" : "/gtkrc"; 0097 return gtkrc; 0098 } 0099 0100 // ----------------------------------------------------------------------------- 0101 static void applyGtkStyles(int version) 0102 { 0103 const char *varName = gtkEnvVar(version); 0104 const char *envVar = getenv(varName); 0105 if (envVar) { // Already set by user, don't override it 0106 return; 0107 } 0108 0109 const QByteArray gtkrc(envVar); 0110 const QString userHomeGtkrc = QDir::homePath() + userGtkrc(version); 0111 QStringList list = QFile::decodeName(gtkrc).split(QLatin1Char(':')); 0112 if (!list.contains(userHomeGtkrc)) { 0113 list.prepend(userHomeGtkrc); 0114 } 0115 0116 const QLatin1String systemGtkrc = sysGtkrc(version); 0117 if (!list.contains(systemGtkrc)) { 0118 list.prepend(systemGtkrc); 0119 } 0120 0121 list.removeAll(QLatin1String("")); 0122 0123 const QString gtkkde = writableGtkrc(version); 0124 list.removeAll(gtkkde); 0125 list.append(gtkkde); 0126 0127 // Pass env. var to kdeinit. 0128 const QString value = list.join(QLatin1Char(':')); 0129 UpdateLaunchEnvJob(varName, value); 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, "WM"); 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, "KDE"); 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(), QStringLiteral("General")); 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, "General"); 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 KConfigGroup fontsCfg(&cfg, "General"); 0330 0331 int defaultDpi = 0; 0332 const bool isWayland = KWindowSystem::isPlatformWayland(); 0333 0334 if (isWayland) { 0335 KConfig cfg(QStringLiteral("kwinrc")); 0336 KConfigGroup xwaylandGroup = cfg.group("Xwayland"); 0337 qreal scale = xwaylandGroup.readEntry("Scale", 1.0); 0338 defaultDpi = scale * 96; 0339 } 0340 0341 QString fontDpiKey = isWayland ? QStringLiteral("forceFontDPIWayland") : QStringLiteral("forceFontDPI"); 0342 return fontsCfg.readEntry(fontDpiKey, defaultDpi); 0343 } 0344 0345 void runRdb(unsigned int flags) 0346 { 0347 // Obtain the application palette that is about to be set. 0348 bool exportQtColors = flags & KRdbExportQtColors; 0349 bool exportQtSettings = flags & KRdbExportQtSettings; 0350 bool exportXftSettings = flags & KRdbExportXftSettings; 0351 bool exportGtkTheme = flags & KRdbExportGtkTheme; 0352 0353 KSharedConfigPtr kglobalcfg = KSharedConfig::openConfig(QStringLiteral("kdeglobals")); 0354 KConfigGroup kglobals(kglobalcfg, "KDE"); 0355 QPalette newPal = KColorScheme::createApplicationPalette(kglobalcfg); 0356 0357 QTemporaryFile tmpFile; 0358 if (!tmpFile.open()) { 0359 qDebug() << "Couldn't open temp file"; 0360 exit(0); 0361 } 0362 0363 KConfigGroup generalCfgGroup(kglobalcfg, "General"); 0364 0365 QString gtkTheme; 0366 if (kglobals.hasKey("widgetStyle")) 0367 gtkTheme = kglobals.readEntry("widgetStyle"); 0368 else 0369 gtkTheme = QStringLiteral("oxygen"); 0370 0371 createGtkrc(newPal, exportGtkTheme, gtkTheme, 1); 0372 createGtkrc(newPal, exportGtkTheme, gtkTheme, 2); 0373 0374 // Merge ~/.Xresources or fallback to ~/.Xdefaults 0375 QString homeDir = QDir::homePath(); 0376 QString xResources = homeDir + "/.Xresources"; 0377 0378 // very primitive support for ~/.Xresources by appending it 0379 if (QFile::exists(xResources)) 0380 copyFile(tmpFile, xResources, true); 0381 else 0382 copyFile(tmpFile, homeDir + "/.Xdefaults", true); 0383 0384 // Export the Xcursor theme & size settings 0385 KConfigGroup mousecfg(KSharedConfig::openConfig(QStringLiteral("kcminputrc")), "Mouse"); 0386 QString theme = mousecfg.readEntry("cursorTheme", QStringLiteral("breeze_cursors")); 0387 QString size = mousecfg.readEntry("cursorSize", QStringLiteral("24")); 0388 QString contents; 0389 0390 if (!theme.isNull()) 0391 contents = "Xcursor.theme: " + theme + '\n'; 0392 0393 if (!size.isNull()) 0394 contents += "Xcursor.size: " + size + '\n'; 0395 0396 if (exportXftSettings) { 0397 contents += QLatin1String("Xft.antialias: "); 0398 if (generalCfgGroup.readEntry("XftAntialias", true)) 0399 contents += QLatin1String("1\n"); 0400 else 0401 contents += QLatin1String("0\n"); 0402 0403 QString hintStyle = generalCfgGroup.readEntry("XftHintStyle", "hintslight"); 0404 contents += QLatin1String("Xft.hinting: "); 0405 if (hintStyle.isEmpty()) 0406 contents += QLatin1String("-1\n"); 0407 else { 0408 if (hintStyle != QLatin1String("hintnone")) 0409 contents += QLatin1String("1\n"); 0410 else 0411 contents += QLatin1String("0\n"); 0412 contents += "Xft.hintstyle: " + hintStyle + '\n'; 0413 } 0414 0415 QString subPixel = generalCfgGroup.readEntry("XftSubPixel", "rgb"); 0416 if (!subPixel.isEmpty()) 0417 contents += "Xft.rgba: " + subPixel + '\n'; 0418 0419 int dpi = xftDpi(); 0420 if (dpi > 0) 0421 contents += "Xft.dpi: " + QString::number(dpi) + '\n'; 0422 else { 0423 KProcess queryProc; 0424 queryProc << QStringLiteral("xrdb") << QStringLiteral("-query"); 0425 queryProc.setOutputChannelMode(KProcess::OnlyStdoutChannel); 0426 queryProc.start(); 0427 if (queryProc.waitForFinished()) { 0428 QByteArray db = queryProc.readAllStandardOutput(); 0429 int idx1 = 0; 0430 while (idx1 < db.size()) { 0431 int idx2 = db.indexOf('\n', idx1); 0432 if (idx2 == -1) { 0433 idx2 = db.size() - 1; 0434 } 0435 const auto entry = QByteArray::fromRawData(db.constData() + idx1, idx2 - idx1 + 1); 0436 if (entry.startsWith("Xft.dpi:")) { 0437 db.remove(idx1, entry.size()); 0438 } else { 0439 idx1 = idx2 + 1; 0440 } 0441 } 0442 0443 KProcess loadProc; 0444 loadProc << QStringLiteral("xrdb") << QStringLiteral("-quiet") << QStringLiteral("-load") << QStringLiteral("-nocpp"); 0445 loadProc.start(); 0446 if (loadProc.waitForStarted()) { 0447 loadProc.write(db); 0448 loadProc.closeWriteChannel(); 0449 loadProc.waitForFinished(); 0450 } 0451 } 0452 } 0453 } 0454 0455 if (contents.length() > 0) 0456 tmpFile.write(contents.toLatin1(), contents.length()); 0457 0458 tmpFile.flush(); 0459 0460 KProcess proc; 0461 #ifndef NDEBUG 0462 proc << QStringLiteral("xrdb") << QStringLiteral("-merge") << tmpFile.fileName(); 0463 #else 0464 proc << "xrdb" 0465 << "-quiet" 0466 << "-merge" << tmpFile.fileName(); 0467 #endif 0468 proc.execute(); 0469 0470 // Needed for applications that don't set their own cursor. 0471 QProcess::execute(QStringLiteral("xsetroot"), {QStringLiteral("-cursor_name"), QStringLiteral("left_ptr")}); 0472 0473 applyGtkStyles(1); 0474 applyGtkStyles(2); 0475 0476 /* Qt exports */ 0477 if (exportQtColors || exportQtSettings) { 0478 QSettings *settings = new QSettings(QStringLiteral("Trolltech")); 0479 0480 if (exportQtColors) 0481 applyQtColors(kglobalcfg, *settings, newPal); // For kcmcolors 0482 0483 if (exportQtSettings) 0484 applyQtSettings(kglobalcfg, *settings); // For kcmstyle 0485 0486 delete settings; 0487 QCoreApplication::processEvents(); 0488 #if HAVE_X11 0489 if (qApp->platformName() == QLatin1String("xcb")) { 0490 // We let KIPC take care of ourselves, as we are in a KDE app with 0491 // QApp::setDesktopSettingsAware(false); 0492 // Instead of calling QApp::x11_apply_settings() directly, we instead 0493 // modify the timestamp which propagates the settings changes onto 0494 // Qt-only apps without adversely affecting ourselves. 0495 0496 // Cheat and use the current timestamp, since we just saved to qtrc. 0497 QDateTime settingsstamp = QDateTime::currentDateTime(); 0498 0499 static Atom qt_settings_timestamp = 0; 0500 if (!qt_settings_timestamp) { 0501 QString atomname(QStringLiteral("_QT_SETTINGS_TIMESTAMP_")); 0502 atomname += XDisplayName(nullptr); // Use the $DISPLAY envvar. 0503 qt_settings_timestamp = XInternAtom(QX11Info::display(), atomname.toLatin1(), False); 0504 } 0505 0506 QBuffer stamp; 0507 QDataStream s(&stamp.buffer(), QIODevice::WriteOnly); 0508 s << settingsstamp; 0509 XChangeProperty(QX11Info::display(), 0510 QX11Info::appRootWindow(), 0511 qt_settings_timestamp, 0512 qt_settings_timestamp, 0513 8, 0514 PropModeReplace, 0515 (unsigned char *)stamp.buffer().data(), 0516 stamp.buffer().size()); 0517 qApp->processEvents(); 0518 } 0519 #endif 0520 } 0521 }