diff --git a/kcms/translations/translations.cpp b/kcms/translations/translations.cpp index 7c6e5d17d..cf1551b24 100644 --- a/kcms/translations/translations.cpp +++ b/kcms/translations/translations.cpp @@ -1,144 +1,143 @@ /* * Copyright (C) 2014 John Layt * Copyright (C) 2018 Eike Hein * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "translations.h" #include "translationsmodel.h" -#include "writeexports.h" - #include #include #include #include K_PLUGIN_CLASS_WITH_JSON(Translations, "kcm_translations.json") +static const QString configFile = QStringLiteral("plasma-localerc"); +static const QString lcLanguage = QStringLiteral("LANGUAGE"); + Translations::Translations(QObject *parent, const QVariantList &args) : KQuickAddons::ConfigModule(parent, args) , m_translationsModel(new TranslationsModel(this)) , m_selectedTranslationsModel(new SelectedTranslationsModel(this)) , m_availableTranslationsModel(new AvailableTranslationsModel(this)) , m_everSaved(false) { KAboutData *about = new KAboutData(QStringLiteral("kcm_translations"), i18n("Configure Plasma translations"), QStringLiteral("2.0"), QString(), KAboutLicense::LGPL); setAboutData(about); setButtons(Apply | Default); m_config = KConfigGroup(KSharedConfig::openConfig(configFile), "Translations"); connect(m_selectedTranslationsModel, &SelectedTranslationsModel::selectedLanguagesChanged, this, &Translations::selectedLanguagesChanged); connect(m_selectedTranslationsModel, &SelectedTranslationsModel::missingLanguagesChanged, this, &Translations::missingLanguagesChanged); connect(m_selectedTranslationsModel, &SelectedTranslationsModel::selectedLanguagesChanged, m_availableTranslationsModel, &AvailableTranslationsModel::setSelectedLanguages); } Translations::~Translations() { } QAbstractItemModel* Translations::translationsModel() const { return m_translationsModel; } QAbstractItemModel* Translations::selectedTranslationsModel() const { return m_selectedTranslationsModel; } QAbstractItemModel* Translations::availableTranslationsModel() const { return m_availableTranslationsModel; } bool Translations::everSaved() const { return m_everSaved; } void Translations::load() { m_configuredLanguages = m_config.readEntry(lcLanguage, QString()).split(QLatin1Char(':'), QString::SkipEmptyParts); m_availableTranslationsModel->setSelectedLanguages(m_configuredLanguages); m_selectedTranslationsModel->setSelectedLanguages(m_configuredLanguages); } void Translations::save() { m_everSaved = true; emit everSavedChanged(); m_configuredLanguages = m_selectedTranslationsModel->selectedLanguages(); const auto missingLanguages = m_selectedTranslationsModel->missingLanguages(); for (const QString& lang : missingLanguages) { m_configuredLanguages.removeOne(lang); } m_config.writeEntry(lcLanguage, m_configuredLanguages.join(QLatin1Char(':')), KConfig::Persistent); m_config.sync(); - writeExports(); - m_selectedTranslationsModel->setSelectedLanguages(m_configuredLanguages); } void Translations::defaults() { KConfigGroup formatsConfig = KConfigGroup(KSharedConfig::openConfig(configFile), "Formats"); QString lang = formatsConfig.readEntry("LANG", QString()); if (lang.isEmpty() || !KLocalizedString::availableDomainTranslations("plasmashell").contains(lang)) { lang = QLocale::system().name(); } if (!KLocalizedString::availableDomainTranslations("plasmashell").contains(lang)) { lang = QStringLiteral("en_US"); } QStringList languages; languages << lang; m_selectedTranslationsModel->setSelectedLanguages(languages); } void Translations::selectedLanguagesChanged() { setNeedsSave(m_configuredLanguages != m_selectedTranslationsModel->selectedLanguages()); } void Translations::missingLanguagesChanged() { if (!m_selectedTranslationsModel->missingLanguages().isEmpty()) { setNeedsSave(true); } } #include "translations.moc" diff --git a/kcms/translations/writeexports.h b/kcms/translations/writeexports.h deleted file mode 100644 index 3faceccd9..000000000 --- a/kcms/translations/writeexports.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2014 Sebastian Kügler - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - */ - -#ifndef WRITEEXPORTS_H -#define WRITEEXPORTS_H - -#include -#include -#include - -#include - -const static QString configFile = QStringLiteral("plasma-localerc"); -const static QString exportFile = QStringLiteral("plasma-locale-settings.sh"); - -const static QString lcLang = QStringLiteral("LANG"); - -const static QString lcNumeric = QStringLiteral("LC_NUMERIC"); -const static QString lcTime = QStringLiteral("LC_TIME"); -const static QString lcMonetary = QStringLiteral("LC_MONETARY"); -const static QString lcMeasurement = QStringLiteral("LC_MEASUREMENT"); -const static QString lcCollate = QStringLiteral("LC_COLLATE"); -const static QString lcCtype = QStringLiteral("LC_CTYPE"); - -const static QString lcLanguage = QStringLiteral("LANGUAGE"); - - -void writeExports() -{ - const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + exportFile; - - QString script(QStringLiteral("# Generated script, do not edit\n")); - script.append(QLatin1String("# Exports language-format specific env vars from startkde.\n")); - script.append(QLatin1String("# This script has been generated from kcmshell5 formats.\n")); - script.append(QLatin1String("# It will automatically be overwritten from there.\n")); - KConfigGroup formatsConfig = KConfigGroup(KSharedConfig::openConfig(configFile), "Formats"); - KConfigGroup languageConfig = KConfigGroup(KSharedConfig::openConfig(configFile), "Translations"); - - const QString _export = QStringLiteral("export "); - - // Formats, uses LC_* and LANG variables - const QString lang = formatsConfig.readEntry(lcLang, QString()); - if (!lang.isEmpty()) { - script.append(_export + lcLang + QLatin1Char('=') + lang + QLatin1Char('\n')); - } - - const QString numeric = formatsConfig.readEntry(lcNumeric, QString()); - if (!numeric.isEmpty()) { - script.append(_export + lcNumeric + QLatin1Char('=') + numeric + QLatin1Char('\n')); - } - - const QString time = formatsConfig.readEntry(lcTime, QString()); - if (!time.isEmpty()) { - script.append(_export + lcTime + QLatin1Char('=') + time + QLatin1Char('\n')); - } - - const QString monetary = formatsConfig.readEntry(lcMonetary, QString()); - if (!monetary.isEmpty()) { - script.append(_export + lcMonetary + QLatin1Char('=') + monetary + QLatin1Char('\n')); - } - - const QString measurement = formatsConfig.readEntry(lcMeasurement, QString()); - if (!measurement.isEmpty()) { - script.append(_export + lcMeasurement + QLatin1Char('=') + measurement + QLatin1Char('\n')); - } - - const QString collate = formatsConfig.readEntry(lcCollate, QString()); - if (!collate.isEmpty()) { - script.append(_export + lcCollate + QLatin1Char('=') + collate + QLatin1Char('\n')); - } - - const QString ctype = formatsConfig.readEntry(lcCtype, QString()); - if (!ctype.isEmpty()) { - script.append(_export + lcCtype + QLatin1Char('=') + ctype + QLatin1Char('\n')); - } - - // Translations, uses LANGUAGE variable - const QString language = languageConfig.readEntry(lcLanguage, QString()); - if (!language.isEmpty()) { - script.append(_export + lcLanguage + QLatin1Char('=') + language + QLatin1Char('\n')); - } - - - - QFile file(configPath); - file.open(QIODevice::WriteOnly | QIODevice::Text); - QTextStream out(&file); - - qDebug() << "Wrote script: " << configPath << "\n" << script; - out << script; - file.close(); -} - -#endif diff --git a/startkde/startplasma.cpp b/startkde/startplasma.cpp index ffb67ffb2..394ad3c40 100644 --- a/startkde/startplasma.cpp +++ b/startkde/startplasma.cpp @@ -1,405 +1,417 @@ /* This file is part of the KDE project Copyright (C) 2019 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include "startplasma.h" QTextStream out(stderr); void messageBox(const QString &text) { out << text; runSync(QStringLiteral("xmessage"), {QStringLiteral("-geometry"), QStringLiteral("500x100"), text}); } QStringList allServices(const QLatin1String& prefix) { QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface(); const QStringList services = bus->registeredServiceNames(); QMap servicesWithAliases; for (const QString &serviceName : services) { QDBusReply reply = bus->serviceOwner(serviceName); QString owner = reply; if (owner.isEmpty()) owner = serviceName; servicesWithAliases[owner].append(serviceName); } QStringList names; for (auto it = servicesWithAliases.constBegin(); it != servicesWithAliases.constEnd(); ++it) { if (it.value().startsWith(prefix)) names << it.value(); } names.removeDuplicates(); names.sort(); return names; } int runSync(const QString& program, const QStringList &args, const QStringList &env) { QProcess p; if (!env.isEmpty()) p.setEnvironment(QProcess::systemEnvironment() << env); p.setProcessChannelMode(QProcess::ForwardedChannels); p.start(program, args); // qDebug() << "started..." << program << args; p.waitForFinished(-1); if (p.exitCode()) { qWarning() << program << args << "exited with code" << p.exitCode(); } return p.exitCode(); } void sourceFiles(const QStringList &files) { QStringList filteredFiles; std::copy_if(files.begin(), files.end(), std::back_inserter(filteredFiles), [](const QString& i){ return QFileInfo(i).isReadable(); } ); if (filteredFiles.isEmpty()) return; filteredFiles.prepend(QStringLiteral(CMAKE_INSTALL_FULL_LIBEXECDIR "/plasma-sourceenv.sh")); QProcess p; p.start(QStringLiteral("/bin/sh"), filteredFiles); p.waitForFinished(-1); const auto fullEnv = p.readAllStandardOutput(); auto envs = fullEnv.split('\0'); for (auto &env: envs) { if (env.startsWith("_=") || env.startsWith("SHLVL")) continue; const int idx = env.indexOf('='); if (Q_UNLIKELY(idx <= 0)) continue; if (qgetenv(env.left(idx)) != env.mid(idx+1)) { // qDebug() << "setting..." << env.left(idx) << env.mid(idx+1) << "was" << qgetenv(env.left(idx)); qputenv(env.left(idx), env.mid(idx+1)); } } } void createConfigDirectory() { const QString configDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); if (!QDir().mkpath(configDir)) out << "Could not create config directory XDG_CONFIG_HOME: " << configDir << '\n'; } void runStartupConfig() { - const QString configDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); + //export LC_* variables set by kcmshell5 formats into environment + //so it can be picked up by QLocale and friends. + KConfig config(QStringLiteral("plasma-localerc")); + KConfigGroup formatsConfig = KConfigGroup(&config, "Formats"); + + const auto lcValues = { + "LANG", "LC_NUMERIC", "LC_MONETARY", "LC_MEASUREMENT", "LC_COLLATE", "LC_CTYPE" + }; + for (auto lc : lcValues) { + const QString value = formatsConfig.readEntry(lc, QString()); + if (!value.isEmpty()) { + qputenv(lc, value.toUtf8()); + } + } - const QString localerc(configDir + QLatin1String("/plasma-localerc")); - if (!QFile::exists(localerc)) { - QFile f(localerc); - f.open(QFile::WriteOnly); - f.write("[Formats]\n" - "LANG=" + qgetenv("LANG") + '\n'); + KConfigGroup languageConfig = KConfigGroup(&config, "Translations"); + const QString value = formatsConfig.readEntry("LANGUAGE", QString()); + if (!value.isEmpty()) { + qputenv("LANGUAGE", value.toUtf8()); } - //export LC_* variables set by kcmshell5 formats into environment - //so it can be picked up by QLocale and friends. - sourceFiles({configDir + QStringLiteral("/plasma-locale-settings.sh")}); + if (!formatsConfig.hasKey("LANG") && !qEnvironmentVariableIsEmpty("LANG")) { + formatsConfig.writeEntry("LANG", qgetenv("LANG")); + formatsConfig.sync(); + } } void setupCursor(bool wayland) { const KConfig cfg(QStringLiteral("kcminputrc")); const KConfigGroup inputCfg = cfg.group("Mouse"); const auto kcminputrc_mouse_cursorsize = inputCfg.readEntry("cursorSize", QString()); const auto kcminputrc_mouse_cursortheme = inputCfg.readEntry("cursorTheme", QStringLiteral("breeze_cursors")); if (!kcminputrc_mouse_cursortheme.isEmpty() || !kcminputrc_mouse_cursorsize.isEmpty()) { #ifdef XCURSOR_PATH QByteArray path(XCURSOR_PATH); path.replace("$XCURSOR_PATH", qgetenv("XCURSOR_PATH")); qputenv("XCURSOR_PATH", path); #endif } //TODO: consider linking directly const int applyMouseStatus = wayland ? 0 : runSync(QStringLiteral("kapplymousetheme"), { kcminputrc_mouse_cursortheme, kcminputrc_mouse_cursorsize }); if (applyMouseStatus == 10) { qputenv("XCURSOR_THEME", "breeze_cursors"); } else if (!kcminputrc_mouse_cursortheme.isEmpty()) { qputenv("XCURSOR_THEME", kcminputrc_mouse_cursortheme.toUtf8()); } if (!kcminputrc_mouse_cursorsize.isEmpty()) { qputenv("XCURSOR_SIZE", kcminputrc_mouse_cursorsize.toUtf8()); } } // Source scripts found in /plasma-workspace/env/*.sh // (where correspond to the system and user's configuration // directory. // // This is where you can define environment variables that will be available to // all KDE programs, so this is where you can run agents using e.g. eval `ssh-agent` // or eval `gpg-agent --daemon`. // Note: if you do that, you should also put "ssh-agent -k" as a shutdown script // // (see end of this file). // For anything else (that doesn't set env vars, or that needs a window manager), // better use the Autostart folder. void runEnvironmentScripts() { QStringList scripts; const auto locations = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("plasma-workspace/env"), QStandardPaths::LocateDirectory); for (const QString & location : locations) { QDir dir(location); const auto dirScripts = dir.entryInfoList({QStringLiteral("*.sh")}); for (const auto script : dirScripts) { scripts << script.absoluteFilePath(); } } sourceFiles(scripts); // Make sure that the KDE prefix is first in XDG_DATA_DIRS and that it's set at all. // The spec allows XDG_DATA_DIRS to be not set, but X session startup scripts tend // to set it to a list of paths *not* including the KDE prefix if it's not /usr or // /usr/local. if (!qEnvironmentVariableIsSet("XDG_DATA_DIRS")) { qputenv("XDG_DATA_DIRS", KDE_INSTALL_FULL_DATAROOTDIR ":/usr/share:/usr/local/share"); } } // Mark that full KDE session is running (e.g. Konqueror preloading works only // with full KDE running). The KDE_FULL_SESSION property can be detected by // any X client connected to the same X session, even if not launched // directly from the KDE session but e.g. using "ssh -X", kdesu. $KDE_FULL_SESSION // however guarantees that the application is launched in the same environment // like the KDE session and that e.g. KDE utilities/libraries are available. // KDE_FULL_SESSION property is also only available since KDE 3.5.5. // The matching tests are: // For $KDE_FULL_SESSION: // if test -n "$KDE_FULL_SESSION"; then ... whatever // For KDE_FULL_SESSION property (on X11): // xprop -root | grep "^KDE_FULL_SESSION" >/dev/null 2>/dev/null // if test $? -eq 0; then ... whatever // // Additionally there is $KDE_SESSION_UID with the uid // of the user running the KDE session. It should be rarely needed (e.g. // after sudo to prevent desktop-wide functionality in the new user's kded). // // Since KDE4 there is also KDE_SESSION_VERSION, containing the major version number. // void setupPlasmaEnvironment() { #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) //Manually disable auto scaling because we are scaling above //otherwise apps that manually opt in for high DPI get auto scaled by the developer AND manually scaled by us qputenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0"); #endif qputenv("KDE_FULL_SESSION", "true"); qputenv("KDE_SESSION_VERSION", "5"); qputenv("KDE_SESSION_UID", QByteArray::number(getuid())); qputenv("XDG_CURRENT_DESKTOP", "KDE"); } void setupX11() { // Set a left cursor instead of the standard X11 "X" cursor, since I've heard // from some users that they're confused and don't know what to do. This is // especially necessary on slow machines, where starting KDE takes one or two // minutes until anything appears on the screen. // // If the user has overwritten fonts, the cursor font may be different now // so don't move this up. runSync(QStringLiteral("xsetroot"), {QStringLiteral("-cursor_name"), QStringLiteral("left_ptr")}); runSync(QStringLiteral("xprop"), {QStringLiteral("-root"), QStringLiteral("-f"), QStringLiteral("KDE_FULL_SESSION"), QStringLiteral("8t"), QStringLiteral("-set"), QStringLiteral("KDE_FULL_SESSION"), QStringLiteral("true")}); runSync(QStringLiteral("xprop"), {QStringLiteral("-root"), QStringLiteral("-f"), QStringLiteral("KDE_SESSION_VERSION"), QStringLiteral("32c"), QStringLiteral("-set"), QStringLiteral("KDE_SESSION_VERSION"), QStringLiteral("5")}); } void cleanupX11() { runSync(QStringLiteral("xprop"), { QStringLiteral("-root"), QStringLiteral("-remove"), QStringLiteral("KDE_FULL_SESSION") }); runSync(QStringLiteral("xprop"), { QStringLiteral("-root"), QStringLiteral("-remove"), QStringLiteral("KDE_SESSION_VERSION") }); } // TODO: Check if Necessary void cleanupPlasmaEnvironment() { qunsetenv("KDE_FULL_SESSION"); qunsetenv("KDE_SESSION_VERSION"); qunsetenv("KDE_SESSION_UID"); } // kwin_wayland can possibly also start dbus-activated services which need env variables. // In that case, the update in startplasma might be too late. bool syncDBusEnvironment() { int exitCode; // At this point all environment variables are set, let's send it to the DBus session server to update the activation environment if (!QStandardPaths::findExecutable(QStringLiteral("dbus-update-activation-environment")).isEmpty()) { exitCode = runSync(QStringLiteral("dbus-update-activation-environment"), { QStringLiteral("--systemd"), QStringLiteral("--all") }); } else { exitCode = runSync(QStringLiteral(CMAKE_INSTALL_FULL_LIBEXECDIR "/ksyncdbusenv"), {}); } return exitCode == 0; } void setupFontDpi() { KConfig cfg(QStringLiteral("kcmfonts")); KConfigGroup fontsCfg(&cfg, "General"); if (!fontsCfg.hasKey("forceFontDPI")) { return; } //TODO port to c++? const QByteArray input = "Xft.dpi: " + QByteArray::number(fontsCfg.readEntry("forceFontDPI", 0)); QProcess p; p.start(QStringLiteral("xrdb"), { QStringLiteral("-quiet"), QStringLiteral("-merge"), QStringLiteral("-nocpp") }); p.setProcessChannelMode(QProcess::ForwardedChannels); p.write(input); p.closeWriteChannel(); p.waitForFinished(-1); } static bool desktopLockedAtStart = false; QProcess* setupKSplash() { const auto dlstr = qgetenv("DESKTOP_LOCKED"); desktopLockedAtStart = dlstr == "true" || dlstr == "1"; qunsetenv("DESKTOP_LOCKED"); // Don't want it in the environment QProcess* p = nullptr; if (!desktopLockedAtStart) { const KConfig cfg(QStringLiteral("ksplashrc")); // the splashscreen and progress indicator KConfigGroup ksplashCfg = cfg.group("KSplash"); if (ksplashCfg.readEntry("Engine", QStringLiteral("KSplashQML")) == QLatin1String("KSplashQML")) { p = new QProcess; p->start(QStringLiteral("ksplashqml"), { ksplashCfg.readEntry("Theme", QStringLiteral("Breeze")) }); } } return p; } void setupGSLib() // Get Ghostscript to look into user's KDE fonts dir for additional Fontmap { const QByteArray usr_fdir = QFile::encodeName(QDir::home().absoluteFilePath(QStringLiteral(".fonts"))); if (qEnvironmentVariableIsSet("GS_LIB")) { qputenv("GS_LIB", usr_fdir + ':' + qgetenv("GS_LIB")); } else { qputenv("GS_LIB", usr_fdir); } } bool startKDEInit() { // We set LD_BIND_NOW to increase the efficiency of kdeinit. // kdeinit unsets this variable before loading applications. const int exitCode = runSync(QStringLiteral(CMAKE_INSTALL_FULL_LIBEXECDIR_KF5 "/start_kdeinit_wrapper"), { QStringLiteral("--kded"), QStringLiteral("+kcminit_startup") }, { QStringLiteral("LD_BIND_NOW=true") }); if (exitCode != 0) { messageBox(QStringLiteral("startkde: Could not start kdeinit5. Check your installation.")); return false; } OrgKdeKSplashInterface iface(QStringLiteral("org.kde.KSplash"), QStringLiteral("/KSplash"), QDBusConnection::sessionBus()); iface.setStage(QStringLiteral("kinit")); return true; } bool startKSMServer(bool wayland) { // finally, give the session control to the session manager // see kdebase/ksmserver for the description of the rest of the startup sequence // if the KDEWM environment variable has been set, then it will be used as KDE's // window manager instead of kwin. // if KDEWM is not set, ksmserver will ensure kwin is started. // kwrapper5 is used to reduce startup time and memory usage // kwrapper5 does not return useful error codes such as the exit code of ksmserver. // We only check for 255 which means that the ksmserver process could not be // started, any problems thereafter, e.g. ksmserver failing to initialize, // will remain undetected. // If the session should be locked from the start (locked autologin), // lock now and do the rest of the KDE startup underneath the locker. QStringList ksmserverOptions; if (wayland) { ksmserverOptions << QStringLiteral("--no-lockscreen"); } else { if (qEnvironmentVariableIsSet("KDEWM")) { ksmserverOptions << QStringLiteral("--windowmanager") << qEnvironmentVariable("KDEWM"); } if (desktopLockedAtStart) { ksmserverOptions << QStringLiteral("--lockscreen"); } } const auto exitCode = runSync(QStringLiteral(CMAKE_INSTALL_FULL_BINDIR "/plasma_session"), ksmserverOptions); if (exitCode == 255) { // Startup error messageBox(QStringLiteral("startkde: Could not start ksmserver. Check your installation.\n")); return false; } return true; } void waitForKonqi() { const KConfig cfg(QStringLiteral("startkderc")); const KConfigGroup grp = cfg.group("WaitForDrKonqi"); bool wait_drkonqi = grp.readEntry("Enabled", true); if (wait_drkonqi) { // wait for remaining drkonqi instances with timeout (in seconds) const int wait_drkonqi_timeout = grp.readEntry("Timeout", 900) * 1000; QElapsedTimer wait_drkonqi_counter; wait_drkonqi_counter.start(); QStringList services = allServices(QLatin1String("org.kde.drkonqi-")); while (!services.isEmpty()) { sleep(5); services = allServices(QLatin1String("org.kde.drkonqi-")); if (wait_drkonqi_counter.elapsed() >= wait_drkonqi_timeout) { // ask remaining drkonqis to die in a graceful way for (const auto &service: services) { QDBusInterface iface(service, QStringLiteral("/MainApplication")); iface.call(QStringLiteral("quit")); } break; } } } }