Use a native application for starting plasma

Summary:
At the moment we had several scripts to start the different processes. With this we unify this code into an application that takes care of the whole process.
This allows us to:
- Save on process spawning, we don't need to run a separate process synchronously for every single thing.
- Don't have a redundant configuration file parser but reuse the one we've already optimised in KConfig.
- Issue dbus calls from the process itself instead of spawning qdbus.
- Removes a bunch of duplicated code.

Test Plan: Started different systems on different distros, on wayland and x11.

Reviewers: #plasma

Subscribers: plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D21725
wilder-5.17
Aleix Pol 7 years ago
parent 4feae4be06
commit f0647b712c
  1. 4
      startkde/CMakeLists.txt
  2. 39
      startkde/startplasma-wayland.cpp
  3. 4
      startkde/startplasma-waylandsession.cpp
  4. 10
      startkde/startplasma-x11.cpp
  5. 95
      startkde/startplasma.cpp

@ -2,6 +2,10 @@ add_subdirectory(kcminit)
add_subdirectory(ksyncdbusenv) add_subdirectory(ksyncdbusenv)
add_subdirectory(waitforname) add_subdirectory(waitforname)
add_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_NO_CAST_TO_ASCII)
add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT)
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
qt5_add_dbus_interface( qt5_add_dbus_interface(
startplasma_SRCS startplasma_SRCS
${CMAKE_SOURCE_DIR}/ksplash/ksplashqml/org.kde.KSplash.xml ${CMAKE_SOURCE_DIR}/ksplash/ksplashqml/org.kde.KSplash.xml

@ -29,7 +29,7 @@ int main(int /*argc*/, char** /*argv*/)
setupCursor(true); setupCursor(true);
{ {
KConfig fonts("kcmfonts"); KConfig fonts(QStringLiteral("kcmfonts"));
KConfigGroup group = fonts.group("General"); KConfigGroup group = fonts.group("General");
auto dpiSetting = group.readEntry("forceFontDPIWayland", 96); auto dpiSetting = group.readEntry("forceFontDPIWayland", 96);
auto dpi = dpiSetting == 0 ? 96 : dpiSetting; auto dpi = dpiSetting == 0 ? 96 : dpiSetting;
@ -38,21 +38,36 @@ int main(int /*argc*/, char** /*argv*/)
// Query whether org.freedesktop.locale1 is available. If it is, try to // Query whether org.freedesktop.locale1 is available. If it is, try to
// set XKB_DEFAULT_{MODEL,LAYOUT,VARIANT,OPTIONS} accordingly. // set XKB_DEFAULT_{MODEL,LAYOUT,VARIANT,OPTIONS} accordingly.
if (QDBusConnection::systemBus().interface()->isServiceRegistered("org.freedesktop.locale1")) { {
auto queryAndSet = [](const QByteArray &var, const QByteArray & value) { const QString locale1Service = QStringLiteral("org.freedesktop.locale1");
QDBusInterface iface(QStringLiteral("org.freedesktop.locale1"), QStringLiteral("/org/freedesktop/locale1"), QStringLiteral("org.freedesktop.locale1"), QDBusConnection::systemBus()); const QString locale1Path = QStringLiteral("/org/freedesktop/locale1");
QDBusMessage message = QDBusMessage::createMethodCall(locale1Service,
locale1Path,
QStringLiteral("org.freedesktop.DBus.Properties"),
QLatin1String("GetAll"));
message << locale1Service;
QDBusMessage resultMessage = QDBusConnection::systemBus().call(message);
if (resultMessage.type() == QDBusMessage::ReplyMessage) {
QVariantMap result;
QDBusArgument dbusArgument = resultMessage.arguments().at(0).value<QDBusArgument>();
while (!dbusArgument.atEnd()) {
dbusArgument >> result;
}
QString r = iface.property(value).toString(); auto queryAndSet = [&](const QByteArray &var, const QString & value) {
const auto r = result.value(value).toString();
if (!r.isEmpty()) if (!r.isEmpty())
qputenv(var, r.toLatin1()); qputenv(var, r.toUtf8());
}; };
queryAndSet("X11MODEL", "org.freedesktop.locale1.X11Model"); queryAndSet("X11MODEL", QStringLiteral("X11Model"));
queryAndSet("X11LAYOUT", "org.freedesktop.locale1.X11Layout"); queryAndSet("X11LAYOUT", QStringLiteral("X11Layout"));
queryAndSet("X11VARIANT", "org.freedesktop.locale1.X11Variant"); queryAndSet("X11VARIANT", QStringLiteral("X11Variant"));
queryAndSet("X11OPTIONS", "org.freedesktop.locale1.X11Options"); queryAndSet("X11OPTIONS", QStringLiteral("X11Options"));
} else {
qWarning() << "not a reply org.freedesktop.locale1" << resultMessage;
}
} }
runEnvironmentScripts(); runEnvironmentScripts();
if (!qEnvironmentVariableIsSet("DBUS_SESSION_BUS_ADDRESS")) { if (!qEnvironmentVariableIsSet("DBUS_SESSION_BUS_ADDRESS")) {
@ -67,7 +82,7 @@ int main(int /*argc*/, char** /*argv*/)
return 1; return 1;
} }
runSync(KWIN_WAYLAND_BIN_PATH, { "--xwayland", "--libinput", "--exit-with-session=" CMAKE_INSTALL_FULL_LIBEXECDIR "/startplasma-waylandsession" }); runSync(QStringLiteral(KWIN_WAYLAND_BIN_PATH), { QStringLiteral("--xwayland"), QStringLiteral("--libinput"), QStringLiteral("--exit-with-session=" CMAKE_INSTALL_FULL_LIBEXECDIR "/startplasma-waylandsession") });
out << "startplasmacompositor: Shutting down...\n"; out << "startplasmacompositor: Shutting down...\n";
cleanupPlasmaEnvironment(); cleanupPlasmaEnvironment();

@ -42,7 +42,7 @@ int main(int /*argc*/, char** /*argv*/)
QScopedPointer<QProcess, KillBeforeDeleter> ksplash(setupKSplash()); QScopedPointer<QProcess, KillBeforeDeleter> ksplash(setupKSplash());
qputenv("PLASMA_USE_QT_SCALING", "1"); qputenv("PLASMA_USE_QT_SCALING", "1");
qDebug() << "startplasma-waylandsession: Starting up... DISPLAY=" << qgetenv("DISPLAY"); out << "startplasma-waylandsession: Starting up...";
if (qEnvironmentVariableIsSet("DISPLAY")) { if (qEnvironmentVariableIsSet("DISPLAY")) {
setupX11(); setupX11();
@ -67,7 +67,7 @@ int main(int /*argc*/, char** /*argv*/)
waitForKonqi(); waitForKonqi();
out << "startplasma-waylandsession: Shutting down...\n"; out << "startplasma-waylandsession: Shutting down...\n";
runSync("kdeinit5_shutdown", {}); runSync(QStringLiteral("kdeinit5_shutdown"), {});
cleanupX11(); cleanupX11();
out << "startplasma-waylandsession: Done.\n"; out << "startplasma-waylandsession: Done.\n";

@ -55,7 +55,7 @@ int main(int /*argc*/, char** /*argv*/)
out << "$DISPLAY is not set or cannot connect to the X server.\n"; out << "$DISPLAY is not set or cannot connect to the X server.\n";
return 1; return 1;
case PlasmaRunning: case PlasmaRunning:
messageBox("Plasma seems to be already running on this display.\n"); messageBox(QStringLiteral("Plasma seems to be already running on this display.\n"));
return 1; return 1;
case NoPlasmaRunning: case NoPlasmaRunning:
break; break;
@ -68,14 +68,14 @@ int main(int /*argc*/, char** /*argv*/)
//sent properly over wl_output //sent properly over wl_output
{ {
KConfig cfg("kdeglobals"); KConfig cfg(QStringLiteral("kdeglobals"));
const auto screenScaleFactors = cfg.group("KScreen").readEntry("ScreenScaleFactors", QByteArray()); const auto screenScaleFactors = cfg.group("KScreen").readEntry("ScreenScaleFactors", QByteArray());
if (!screenScaleFactors.isEmpty()) { if (!screenScaleFactors.isEmpty()) {
qputenv("QT_SCREEN_SCALE_FACTORS", screenScaleFactors); qputenv("QT_SCREEN_SCALE_FACTORS", screenScaleFactors);
if (screenScaleFactors == "2" || screenScaleFactors == "3") { if (screenScaleFactors == "2" || screenScaleFactors == "3") {
qputenv("GDK_SCALE", screenScaleFactors); qputenv("GDK_SCALE", screenScaleFactors);
qputenv("GDK_DPI_SCALE", QByteArray::number(1/screenScaleFactors.toInt(), 'g', 3)); qputenv("GDK_DPI_SCALE", QByteArray::number(1./screenScaleFactors.toDouble(), 'g', 3));
} }
} }
} }
@ -93,7 +93,7 @@ int main(int /*argc*/, char** /*argv*/)
if (!syncDBusEnvironment()) { if (!syncDBusEnvironment()) {
// Startup error // Startup error
messageBox("Could not sync environment to dbus.\n"); messageBox(QStringLiteral("Could not sync environment to dbus.\n"));
return 1; return 1;
} }
@ -109,7 +109,7 @@ int main(int /*argc*/, char** /*argv*/)
out << "startkde: Shutting down...\n"; out << "startkde: Shutting down...\n";
runSync("kdeinit5_shutdown", {}); runSync(QStringLiteral("kdeinit5_shutdown"), {});
cleanupPlasmaEnvironment(); cleanupPlasmaEnvironment();
cleanupX11(); cleanupX11();

@ -34,7 +34,7 @@ QTextStream out(stderr);
void messageBox(const QString &text) void messageBox(const QString &text)
{ {
out << text; out << text;
runSync("xmessage", {"-geometry", "500x100", text}); runSync(QStringLiteral("xmessage"), {QStringLiteral("-geometry"), QStringLiteral("500x100"), text});
} }
QStringList allServices(const QLatin1String& prefix) QStringList allServices(const QLatin1String& prefix)
@ -68,10 +68,10 @@ int runSync(const QString& program, const QStringList &args, const QStringList &
p.setEnvironment(QProcess::systemEnvironment() << env); p.setEnvironment(QProcess::systemEnvironment() << env);
p.setProcessChannelMode(QProcess::ForwardedChannels); p.setProcessChannelMode(QProcess::ForwardedChannels);
p.start(program, args); p.start(program, args);
qDebug() << "started..." << program << args; // qDebug() << "started..." << program << args;
p.waitForFinished(-1); p.waitForFinished(-1);
if (p.exitCode()) { if (p.exitCode()) {
qWarning() << program << args << "exited with code" << p.exitCode() << p.readAllStandardError(); qWarning() << program << args << "exited with code" << p.exitCode();
} }
return p.exitCode(); return p.exitCode();
} }
@ -84,10 +84,10 @@ void sourceFiles(const QStringList &files)
if (filteredFiles.isEmpty()) if (filteredFiles.isEmpty())
return; return;
filteredFiles.prepend(CMAKE_INSTALL_FULL_LIBEXECDIR "/plasma-sourceenv.sh"); filteredFiles.prepend(QStringLiteral(CMAKE_INSTALL_FULL_LIBEXECDIR "/plasma-sourceenv.sh"));
QProcess p; QProcess p;
p.start("/bin/sh", filteredFiles); p.start(QStringLiteral("/bin/sh"), filteredFiles);
p.waitForFinished(-1); p.waitForFinished(-1);
const auto fullEnv = p.readAllStandardOutput(); const auto fullEnv = p.readAllStandardOutput();
@ -102,7 +102,7 @@ void sourceFiles(const QStringList &files)
continue; continue;
if (qgetenv(env.left(idx)) != env.mid(idx+1)) { if (qgetenv(env.left(idx)) != env.mid(idx+1)) {
qDebug() << "setting..." << env.left(idx) << env.mid(idx+1) << "was" << qgetenv(env.left(idx)); // qDebug() << "setting..." << env.left(idx) << env.mid(idx+1) << "was" << qgetenv(env.left(idx));
qputenv(env.left(idx), env.mid(idx+1)); qputenv(env.left(idx), env.mid(idx+1));
} }
} }
@ -119,14 +119,22 @@ void runStartupConfig()
{ {
const QString configDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); const QString configDir = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation);
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');
}
//export LC_* variables set by kcmshell5 formats into environment //export LC_* variables set by kcmshell5 formats into environment
//so it can be picked up by QLocale and friends. //so it can be picked up by QLocale and friends.
sourceFiles({configDir + "/plasma-locale-settings.sh"}); sourceFiles({configDir + QStringLiteral("/plasma-locale-settings.sh")});
} }
void setupCursor(bool wayland) void setupCursor(bool wayland)
{ {
const KConfig cfg("kcminputrc"); const KConfig cfg(QStringLiteral("kcminputrc"));
const KConfigGroup inputCfg = cfg.group("Mouse"); const KConfigGroup inputCfg = cfg.group("Mouse");
const auto kcminputrc_mouse_cursorsize = inputCfg.readEntry("cursorSize", QString()); const auto kcminputrc_mouse_cursorsize = inputCfg.readEntry("cursorSize", QString());
@ -140,7 +148,7 @@ void setupCursor(bool wayland)
} }
//TODO: consider linking directly //TODO: consider linking directly
const int applyMouseStatus = wayland ? 0 : runSync("kapplymousetheme", { "kcminputrc_mouse_cursortheme", "kcminputrc_mouse_cursorsize" }); const int applyMouseStatus = wayland ? 0 : runSync(QStringLiteral("kapplymousetheme"), { QStringLiteral("kcminputrc_mouse_cursortheme"), QStringLiteral("kcminputrc_mouse_cursorsize") });
if (applyMouseStatus == 10) { if (applyMouseStatus == 10) {
qputenv("XCURSOR_THEME", "breeze_cursors"); qputenv("XCURSOR_THEME", "breeze_cursors");
} else if (!kcminputrc_mouse_cursortheme.isEmpty()) { } else if (!kcminputrc_mouse_cursortheme.isEmpty()) {
@ -230,15 +238,15 @@ void setupX11()
// If the user has overwritten fonts, the cursor font may be different now // If the user has overwritten fonts, the cursor font may be different now
// so don't move this up. // so don't move this up.
runSync("xsetroot", {"-cursor_name", "left_ptr"}); runSync(QStringLiteral("xsetroot"), {QStringLiteral("-cursor_name"), QStringLiteral("left_ptr")});
runSync("xprop", {"-root", "-f", "KDE_FULL_SESSION", "8t", "-set", "KDE_FULL_SESSION", "true"}); runSync(QStringLiteral("xprop"), {QStringLiteral("-root"), QStringLiteral("-f"), QStringLiteral("KDE_FULL_SESSION"), QStringLiteral("8t"), QStringLiteral("-set"), QStringLiteral("KDE_FULL_SESSION"), QStringLiteral("true")});
runSync("xprop", {"-root", "-f", "KDE_SESSION_VERSION", "32c", "-set", "KDE_SESSION_VERSION", "5"}); runSync(QStringLiteral("xprop"), {QStringLiteral("-root"), QStringLiteral("-f"), QStringLiteral("KDE_SESSION_VERSION"), QStringLiteral("32c"), QStringLiteral("-set"), QStringLiteral("KDE_SESSION_VERSION"), QStringLiteral("5")});
} }
void cleanupX11() void cleanupX11()
{ {
runSync("xprop", { "-root", "-remove", "KDE_FULL_SESSION" }); runSync(QStringLiteral("xprop"), { QStringLiteral("-root"), QStringLiteral("-remove"), QStringLiteral("KDE_FULL_SESSION") });
runSync("xprop", { "-root", "-remove", "KDE_SESSION_VERSION" }); runSync(QStringLiteral("xprop"), { QStringLiteral("-root"), QStringLiteral("-remove"), QStringLiteral("KDE_SESSION_VERSION") });
} }
// TODO: Check if Necessary // TODO: Check if Necessary
@ -255,49 +263,49 @@ bool syncDBusEnvironment()
{ {
int exitCode; int exitCode;
// At this point all environment variables are set, let's send it to the DBus session server to update the activation environment // 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("dbus-update-activation-environment").isEmpty()) if (!QStandardPaths::findExecutable(QStringLiteral("dbus-update-activation-environment")).isEmpty()) {
exitCode = runSync("dbus-update-activation-environment", { "--systemd", "--all" }); exitCode = runSync(QStringLiteral("dbus-update-activation-environment"), { QStringLiteral("--systemd"), QStringLiteral("--all") });
else } else {
exitCode = runSync(CMAKE_INSTALL_FULL_LIBEXECDIR "/ksyncdbusenv", {}); exitCode = runSync(QStringLiteral(CMAKE_INSTALL_FULL_LIBEXECDIR "/ksyncdbusenv"), {});
}
return exitCode == 0; return exitCode == 0;
} }
void setupFontDpi() void setupFontDpi()
{ {
KConfig cfg("kcmfonts"); KConfig cfg(QStringLiteral("kcmfonts"));
KConfigGroup fontsCfg(&cfg, "General"); KConfigGroup fontsCfg(&cfg, "General");
if (!fontsCfg.readEntry("forceFontDPI", false)) { if (!fontsCfg.hasKey("forceFontDPI")) {
return; return;
} }
//TODO port to c++? //TODO port to c++?
const QByteArray input = "Xft.dpi: kcmfonts_general_forcefontdpi"; const QByteArray input = "Xft.dpi: " + QByteArray::number(fontsCfg.readEntry("forceFontDPI", 0));
QProcess p; QProcess p;
p.start("xrdb", { "-quiet", "-merge", "-nocpp" }); p.start(QStringLiteral("xrdb"), { QStringLiteral("-quiet"), QStringLiteral("-merge"), QStringLiteral("-nocpp") });
p.setProcessChannelMode(QProcess::ForwardedChannels); p.setProcessChannelMode(QProcess::ForwardedChannels);
p.write(input); p.write(input);
p.closeWriteChannel(); p.closeWriteChannel();
p.waitForFinished(-1); p.waitForFinished(-1);
} }
static bool dl = false; static bool desktopLockedAtStart = false;
QProcess* setupKSplash() QProcess* setupKSplash()
{ {
const auto dlstr = qgetenv("DESKTOP_LOCKED"); const auto dlstr = qgetenv("DESKTOP_LOCKED");
dl = dlstr == "true" || dlstr == "1"; desktopLockedAtStart = dlstr == "true" || dlstr == "1";
qunsetenv("DESKTOP_LOCKED"); // Don't want it in the environment qunsetenv("DESKTOP_LOCKED"); // Don't want it in the environment
QProcess* p = nullptr; QProcess* p = nullptr;
if (!dl) { if (!desktopLockedAtStart) {
const KConfig cfg("ksplashrc"); const KConfig cfg(QStringLiteral("ksplashrc"));
// the splashscreen and progress indicator // the splashscreen and progress indicator
KConfigGroup ksplashCfg = cfg.group("KSplash"); KConfigGroup ksplashCfg = cfg.group("KSplash");
if (ksplashCfg.readEntry("Engine", QStringLiteral("KSplashQML")) == QLatin1String("KSplashQML")) { if (ksplashCfg.readEntry("Engine", QStringLiteral("KSplashQML")) == QLatin1String("KSplashQML")) {
p = new QProcess; p = new QProcess;
p->start("ksplashqml", { ksplashCfg.readEntry("Theme", QStringLiteral("Breeze")) }); p->start(QStringLiteral("ksplashqml"), { ksplashCfg.readEntry("Theme", QStringLiteral("Breeze")) });
} }
} }
return p; return p;
@ -307,7 +315,7 @@ QProcess* setupKSplash()
void setupGSLib() void setupGSLib()
// Get Ghostscript to look into user's KDE fonts dir for additional Fontmap // Get Ghostscript to look into user's KDE fonts dir for additional Fontmap
{ {
const QByteArray usr_fdir = QFile::encodeName(QDir::home().absoluteFilePath(".fonts")); const QByteArray usr_fdir = QFile::encodeName(QDir::home().absoluteFilePath(QStringLiteral(".fonts")));
if (qEnvironmentVariableIsSet("GS_LIB")) { if (qEnvironmentVariableIsSet("GS_LIB")) {
qputenv("GS_LIB", usr_fdir + ':' + qgetenv("GS_LIB")); qputenv("GS_LIB", usr_fdir + ':' + qgetenv("GS_LIB"));
} else { } else {
@ -319,19 +327,14 @@ bool startKDEInit()
{ {
// We set LD_BIND_NOW to increase the efficiency of kdeinit. // We set LD_BIND_NOW to increase the efficiency of kdeinit.
// kdeinit unsets this variable before loading applications. // kdeinit unsets this variable before loading applications.
const int exitCode = runSync(CMAKE_INSTALL_FULL_LIBEXECDIR_KF5 "/start_kdeinit_wrapper", { "--kded", "+kcminit_startup" }, { "LD_BIND_NOW=true" }); 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) { if (exitCode != 0) {
messageBox("startkde: Could not start kdeinit5. Check your installation."); messageBox(QStringLiteral("startkde: Could not start kdeinit5. Check your installation."));
return false; return false;
} }
OrgKdeKSplashInterface iface("org.kde.KSplash", "/KSplash", QDBusConnection::sessionBus()); OrgKdeKSplashInterface iface(QStringLiteral("org.kde.KSplash"), QStringLiteral("/KSplash"), QDBusConnection::sessionBus());
auto reply = iface.setStage("kinit"); iface.setStage(QStringLiteral("kinit"));
auto watcher = new QDBusPendingCallWatcher(reply);
QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, [] (QDBusPendingCallWatcher* watcher) {
watcher->deleteLater();
qDebug() << "ksplash reply arrived" << watcher->error();
});
return true; return true;
} }
@ -351,15 +354,15 @@ bool startKSMServer()
// lock now and do the rest of the KDE startup underneath the locker. // lock now and do the rest of the KDE startup underneath the locker.
QStringList ksmserverOptions = { CMAKE_INSTALL_FULL_BINDIR "/ksmserver" }; QStringList ksmserverOptions = { QStringLiteral(CMAKE_INSTALL_FULL_BINDIR "/ksmserver") };
if (dl) { if (desktopLockedAtStart) {
ksmserverOptions << "--lockscreen"; ksmserverOptions << QStringLiteral("--lockscreen");
} }
const auto exitCode = runSync("kwrapper5", ksmserverOptions); const auto exitCode = runSync(QStringLiteral("kwrapper5"), ksmserverOptions);
if (exitCode == 255) { if (exitCode == 255) {
// Startup error // Startup error
messageBox("startkde: Could not start ksmserver. Check your installation.\n"); messageBox(QStringLiteral("startkde: Could not start ksmserver. Check your installation.\n"));
return false; return false;
} }
return true; return true;
@ -367,7 +370,7 @@ bool startKSMServer()
void waitForKonqi() void waitForKonqi()
{ {
const KConfig cfg("startkderc"); const KConfig cfg(QStringLiteral("startkderc"));
const KConfigGroup grp = cfg.group("WaitForDrKonqi"); const KConfigGroup grp = cfg.group("WaitForDrKonqi");
bool wait_drkonqi = grp.readEntry("Enabled", true); bool wait_drkonqi = grp.readEntry("Enabled", true);
if (wait_drkonqi) { if (wait_drkonqi) {
@ -382,8 +385,8 @@ void waitForKonqi()
if (wait_drkonqi_counter.elapsed() >= wait_drkonqi_timeout) { if (wait_drkonqi_counter.elapsed() >= wait_drkonqi_timeout) {
// ask remaining drkonqis to die in a graceful way // ask remaining drkonqis to die in a graceful way
for (const auto &service: services) { for (const auto &service: services) {
QDBusInterface iface(service, "/MainApplication"); QDBusInterface iface(service, QStringLiteral("/MainApplication"));
iface.call("quit"); iface.call(QStringLiteral("quit"));
} }
break; break;
} }

Loading…
Cancel
Save