diff --git a/ksmserver/server.h b/ksmserver/server.h --- a/ksmserver/server.h +++ b/ksmserver/server.h @@ -267,6 +267,7 @@ int sockets[2]; friend bool readFromPipe(int pipe); + friend class RestoreSessionJob; friend class Startup; }; diff --git a/ksmserver/startup.h b/ksmserver/startup.h --- a/ksmserver/startup.h +++ b/ksmserver/startup.h @@ -1,51 +1,65 @@ #pragma once #include -#include +#include + #include "autostart.h" class KProcess; class KSMServer; +class KCompositeJob; class Startup : public QObject { +Q_OBJECT public: Startup(KSMServer *parent); void upAndRunning( const QString& msg ); -private Q_SLOTS: - - void runUserAutostart(); - bool migrateKDE4Autostart(const QString &autostartFolder); - - void autoStart0(); - void autoStart1(); - void autoStart2(); - void autoStart0Done(); - void autoStart1Done(); - void autoStart2Done(); - void kcmPhase1Done(); - void kcmPhase2Done(); - // ksplash interface void finishStartup(); - void slotAutoStart(); - void secondKDEDPhaseLoaded(); - void kcmPhase1Timeout(); - void kcmPhase2Timeout(); - private: void autoStart(int phase); private: - AutoStart m_autoStart; KSMServer *ksmserver = nullptr; - enum State - { - Waiting, AutoStart0, KcmInitPhase1, AutoStart1, FinishingStartup, // startup - }; - State state; +}; + +class KCMInitJob: public KJob +{ +Q_OBJECT +public: + KCMInitJob(int phase); + void start() override; +public Q_SLOTS: + void done(); +private: + int m_phase; +}; + +class KDEDInitJob: public KJob +{ +Q_OBJECT +public: + KDEDInitJob(); + void start() override; +}; - bool waitAutoStart2 = true; - bool waitKcmInit2 = true; - QDBusInterface* kcminitSignals = nullptr; +class AutoStartAppsJob: public KJob +{ +Q_OBJECT +public: + AutoStartAppsJob(int phase); + void start() override; +private: + AutoStart m_autoStart; +}; +class RestoreSessionJob: public KJob +{ +Q_OBJECT +public: + RestoreSessionJob(KSMServer *ksmserver); + void start() override; +private: + KSMServer *m_ksmserver; }; + diff --git a/ksmserver/startup.cpp b/ksmserver/startup.cpp --- a/ksmserver/startup.cpp +++ b/ksmserver/startup.cpp @@ -3,6 +3,8 @@ Copyright 2000 Matthias Ettrich Copyright 2005 Lubos Lunak +Copyright 2018 David Edmundson + relatively small extensions by Oswald Buddenhagen @@ -32,69 +34,99 @@ #include "startup.h" #include "server.h" -#include -#include #include #include // HAVE_LIMITS_H #include #include -#include -#include -#include -#include -#ifdef HAVE_SYS_TIME_H -#include -#endif -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_LIMITS_H -#include -#endif +#include "kcminit_interface.h" +#include + +#include +#include +#include +#include +#include +#include +#include -#include -#include -#include -#include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include -#include "global.h" -#include "server.h" -#include "client.h" +class Phase: public KCompositeJob +{ +Q_OBJECT +public: + Phase(QObject *parent): + KCompositeJob(parent) + {} + + bool addSubjob(KJob *job) override { + bool rc = KCompositeJob::addSubjob(job); + job->start(); + return rc; + } -#include + void slotResult(KJob *job) override { + KCompositeJob::slotResult(job); + if (!hasSubjobs()) { + emitResult(); + } + } +}; -//#include "kdesktop_interface.h" -#include -#include -#include "kcminit_interface.h" +class StartupPhase0: public Phase +{ +Q_OBJECT +public: + StartupPhase0(QObject *parent) : Phase(parent) + {} + void start() override { + qCDebug(KSMSERVER) << "Phase 0"; + addSubjob(new AutoStartAppsJob(0)); + addSubjob(new KCMInitJob(1)); + } +}; -//#define KSMSERVER_STARTUP_DEBUG1 +class StartupPhase1: public Phase +{ +Q_OBJECT +public: + StartupPhase1(QObject *parent) : Phase(parent) + {} + void start() override { + qCDebug(KSMSERVER) << "Phase 1"; + addSubjob(new AutoStartAppsJob(1)); + } +}; -#ifdef KSMSERVER_STARTUP_DEBUG1 -static QTime t; -#endif +class StartupPhase2: public Phase +{ +Q_OBJECT +public: + StartupPhase2(QObject *parent) : Phase(parent) + {} + void runUserAutostart(); + bool migrateKDE4Autostart(const QString &folder); + + void start() override { + qCDebug(KSMSERVER) << "Phase 2"; + addSubjob(new AutoStartAppsJob(2)); + addSubjob(new KDEDInitJob()); + addSubjob(new KCMInitJob(2)); + runUserAutostart(); + } +}; // Put the notification in its own thread as it can happen that // PulseAudio will start initializing with this, so let's not @@ -152,157 +184,121 @@ Startup::Startup(KSMServer *parent): QObject(parent), - ksmserver(parent), - state(Waiting) + ksmserver(parent) { - connect(ksmserver, &KSMServer::windowManagerLoaded, this, &Startup::autoStart0); + auto phase0 = new StartupPhase0(this); + auto phase1 = new StartupPhase1(this); + auto phase2 = new StartupPhase2(this); + auto restoreSession = new RestoreSessionJob(ksmserver); + + connect(ksmserver, &KSMServer::windowManagerLoaded, phase0, &KJob::start); + connect(phase0, &KJob::finished, phase1, &KJob::start); + + connect(phase1, &KJob::finished, this, [=]() { + ksmserver->setupShortcuts(); // done only here, because it needs kglobalaccel :-/ + }); + + if (ksmserver->defaultSession()) { + connect(phase1, &KJob::finished, phase2, &KJob::start); + } else { + connect(phase1, &KJob::finished, restoreSession, &KJob::start); + connect(restoreSession, &KJob::finished, phase2, &KJob::start); + } + connect(phase1, &KJob::finished, this, []() { + NotificationThread *loginSound = new NotificationThread(); + connect(loginSound, &NotificationThread::finished, loginSound, &NotificationThread::deleteLater); + loginSound->start();}); + connect(phase2, &KJob::finished, this, &Startup::finishStartup); } -void Startup::autoStart0() +void Startup::upAndRunning( const QString& msg ) { - disconnect(ksmserver, &KSMServer::windowManagerLoaded, this, &Startup::autoStart0); - state = AutoStart0; -#ifdef KSMSERVER_STARTUP_DEBUG1 - qCDebug(KSMSERVER) << t.elapsed(); -#endif + QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"), + QStringLiteral("/KSplash"), + QStringLiteral("org.kde.KSplash"), + QStringLiteral("setStage")); + ksplashProgressMessage.setArguments(QList() << msg); + QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage); +} - autoStart(0); +void Startup::finishStartup() +{ + qCDebug(KSMSERVER) << "Finished"; + ksmserver->state = KSMServer::Idle; + ksmserver->setupXIOErrorHandler(); + upAndRunning(QStringLiteral("ready")); } -void Startup::autoStart0Done() +KCMInitJob::KCMInitJob(int phase) + :m_phase(phase) { - if( state != AutoStart0 ) - return; - qCDebug(KSMSERVER) << "Autostart 0 done"; -#ifdef KSMSERVER_STARTUP_DEBUG1 - qCDebug(KSMSERVER) << t.elapsed(); -#endif +} - state = KcmInitPhase1; - kcminitSignals = new QDBusInterface( QStringLiteral( "org.kde.kcminit"), +void KCMInitJob::start() { + //FIXME - replace all this with just a DBus call with a timeout and make kcminit delay the reply till it's done + + auto kcminitSignals = new QDBusInterface( QStringLiteral( "org.kde.kcminit"), QStringLiteral( "/kcminit" ), QStringLiteral( "org.kde.KCMInit" ), QDBusConnection::sessionBus(), this ); if( !kcminitSignals->isValid()) { qCWarning(KSMSERVER) << "kcminit not running? If we are running with mobile profile or in another platform other than X11 this is normal."; - delete kcminitSignals; - kcminitSignals = nullptr; - QTimer::singleShot(0, this, &Startup::kcmPhase1Done); + QTimer::singleShot(0, this, &KCMInitJob::done); return; } - connect( kcminitSignals, SIGNAL(phase1Done()), SLOT(kcmPhase1Done())); - QTimer::singleShot( 10000, this, &Startup::kcmPhase1Timeout); // protection + if (m_phase == 1) { + connect( kcminitSignals, SIGNAL(phase1Done()), this, SLOT(done())); + } else { + connect( kcminitSignals, SIGNAL(phase2Done()), this, SLOT(done())); + } + QTimer::singleShot( 10000, this, &KCMInitJob::done); // protection org::kde::KCMInit kcminit(QStringLiteral("org.kde.kcminit"), QStringLiteral("/kcminit"), QDBusConnection::sessionBus()); - kcminit.runPhase1(); -} -void Startup::kcmPhase1Done() -{ - if( state != KcmInitPhase1 ) - return; - qCDebug(KSMSERVER) << "Kcminit phase 1 done"; - if (kcminitSignals) { - disconnect( kcminitSignals, SIGNAL(phase1Done()), this, SLOT(kcmPhase1Done())); + if (m_phase == 1) { + kcminit.runPhase1(); + } else { + kcminit.runPhase2(); } - autoStart1(); -} - -void Startup::kcmPhase1Timeout() -{ - if( state != KcmInitPhase1 ) - return; - qCDebug(KSMSERVER) << "Kcminit phase 1 timeout"; - kcmPhase1Done(); } -void Startup::autoStart1() +void KCMInitJob::done() { - if( state != KcmInitPhase1 ) - return; - state = AutoStart1; -#ifdef KSMSERVER_STARTUP_DEBUG1 - qCDebug(KSMSERVER)<< t.elapsed(); -#endif - autoStart(1); + emitResult(); } -void Startup::autoStart1Done() +KDEDInitJob::KDEDInitJob() { - if( state != AutoStart1 ) - return; - qCDebug(KSMSERVER) << "Autostart 1 done"; - ksmserver->setupShortcuts(); // done only here, because it needs kglobalaccel :-/ - ksmserver->lastAppStarted = 0; - ksmserver->lastIdStarted.clear(); - ksmserver->state = KSMServer::Restoring; -#ifdef KSMSERVER_STARTUP_DEBUG1 - qCDebug(KSMSERVER)<< t.elapsed(); -#endif - if( ksmserver->defaultSession()) { - autoStart2(); - return; - } - ksmserver->tryRestoreNext(); - connect(ksmserver, &KSMServer::sessionRestored, this, &Startup::autoStart2); } -void Startup::autoStart2() -{ - if( ksmserver->state != KSMServer::Restoring ) - return; - ksmserver->startupDone(); - - state = FinishingStartup; -#ifdef KSMSERVER_STARTUP_DEBUG1 - qCDebug(KSMSERVER)<< t.elapsed(); -#endif - waitAutoStart2 = true; - waitKcmInit2 = true; - autoStart(2); - +void KDEDInitJob::start() { + qCDebug(KSMSERVER()); QDBusInterface kded( QStringLiteral( "org.kde.kded5" ), QStringLiteral( "/kded" ), QStringLiteral( "org.kde.kded5" ) ); auto pending = kded.asyncCall( QStringLiteral( "loadSecondPhase" ) ); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending, this); - QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &Startup::secondKDEDPhaseLoaded); - QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, &QObject::deleteLater); - runUserAutostart(); - - if (kcminitSignals) { - connect( kcminitSignals, SIGNAL(phase2Done()), SLOT(kcmPhase2Done())); - QTimer::singleShot( 10000, this, &Startup::kcmPhase2Timeout); // protection - org::kde::KCMInit kcminit(QStringLiteral("org.kde.kcminit"), - QStringLiteral("/kcminit"), - QDBusConnection::sessionBus()); - kcminit.runPhase2(); - } else { - QTimer::singleShot(0, this, &Startup::kcmPhase2Done); - } + connect(watcher, &QDBusPendingCallWatcher::finished, this, [this]() {emitResult();}); + connect(watcher, &QDBusPendingCallWatcher::finished, watcher, &QObject::deleteLater); } -void Startup::secondKDEDPhaseLoaded() -{ - -#ifdef KSMSERVER_STARTUP_DEBUG1 - qCDebug(KSMSERVER)<< "kded" << t.elapsed(); -#endif +RestoreSessionJob::RestoreSessionJob(KSMServer *server): KJob(), + m_ksmserver(server) +{} - if( !ksmserver->defaultSession()) - ksmserver->restoreLegacySession(KSharedConfig::openConfig().data()); - - qCDebug(KSMSERVER) << "Starting notification thread"; - NotificationThread *loginSound = new NotificationThread(); - // Delete the thread when finished - connect(loginSound, &NotificationThread::finished, loginSound, &NotificationThread::deleteLater); - loginSound->start(); +void RestoreSessionJob::start() +{ + m_ksmserver->lastAppStarted = 0; + m_ksmserver->lastIdStarted.clear(); + m_ksmserver->state = KSMServer::Restoring; + connect(m_ksmserver, &KSMServer::sessionRestored, this, [this]() {emitResult();}); + m_ksmserver->tryRestoreNext(); } -void Startup::runUserAutostart() +void StartupPhase2::runUserAutostart() { // Now let's execute the scripts in the KDE-specific autostart-scripts folder. const QString autostartFolder = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QDir::separator() + QStringLiteral("autostart-scripts"); @@ -338,7 +334,7 @@ } } -bool Startup::migrateKDE4Autostart(const QString &autostartFolder) +bool StartupPhase2::migrateKDE4Autostart(const QString &autostartFolder) { // Migrate user autostart from kde4 Kdelibs4Migration migration; @@ -374,110 +370,38 @@ return true; } -void Startup::autoStart2Done() -{ - if( state != FinishingStartup ) - return; - qCDebug(KSMSERVER) << "Autostart 2 done"; - waitAutoStart2 = false; - finishStartup(); -} - -void Startup::kcmPhase2Done() -{ - if( state != FinishingStartup ) - return; - qCDebug(KSMSERVER) << "Kcminit phase 2 done"; - if (kcminitSignals) { - disconnect( kcminitSignals, SIGNAL(phase2Done()), this, SLOT(kcmPhase2Done())); - delete kcminitSignals; - kcminitSignals = nullptr; - } - waitKcmInit2 = false; - finishStartup(); -} - -void Startup::kcmPhase2Timeout() +AutoStartAppsJob::AutoStartAppsJob(int phase) { - if( !waitKcmInit2 ) - return; - qCDebug(KSMSERVER) << "Kcminit phase 2 timeout"; - kcmPhase2Done(); -} - -void Startup::finishStartup() -{ - if( state != FinishingStartup ) - return; - if( waitAutoStart2 || waitKcmInit2 ) - return; - - upAndRunning( QStringLiteral( "ready" ) ); -#ifdef KSMSERVER_STARTUP_DEBUG1 - qCDebug(KSMSERVER)<< t.elapsed(); -#endif - - state = Waiting; - ksmserver->setupXIOErrorHandler(); // From now on handle X errors as normal shutdown. -} - -void Startup::upAndRunning( const QString& msg ) -{ - QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"), - QStringLiteral("/KSplash"), - QStringLiteral("org.kde.KSplash"), - QStringLiteral("setStage")); - ksplashProgressMessage.setArguments(QList() << msg); - QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage); -} - - -void Startup::autoStart(int phase) -{ - if (m_autoStart.phase() >= phase) { - return; - } + m_autoStart.loadAutoStartList(); //FIXME, share this between jobs m_autoStart.setPhase(phase); - if (phase == 0) { - m_autoStart.loadAutoStartList(); - } - QTimer::singleShot(0, this, &Startup::slotAutoStart); } -void Startup::slotAutoStart() -{ - do { - QString serviceName = m_autoStart.startService(); - if (serviceName.isEmpty()) { - // Done - if (!m_autoStart.phaseDone()) { - m_autoStart.setPhaseDone(); - switch (m_autoStart.phase()) { - case 0: - autoStart0Done(); - break; - case 1: - autoStart1Done(); - break; - case 2: - autoStart2Done(); - break; +void AutoStartAppsJob::start() { + qCDebug(KSMSERVER()); + + QTimer::singleShot(0, this, [=]() { + do { + QString serviceName = m_autoStart.startService(); + if (serviceName.isEmpty()) { + // Done + if (!m_autoStart.phaseDone()) { + m_autoStart.setPhaseDone(); } + emitResult(); + return; } - return; - } - KService service(serviceName); - auto arguments = KIO::DesktopExecParser(service, QList()).resultingArguments(); - if (arguments.isEmpty()) { - qCWarning(KSMSERVER) << "failed to parse" << serviceName << "for autostart"; - continue; - } - qCInfo(KSMSERVER) << "Starting autostart service " << serviceName << arguments; - auto program = arguments.takeFirst(); - if (!QProcess::startDetached(program, arguments)) - qCWarning(KSMSERVER) << "could not start" << serviceName << ":" << program << arguments; - } while (true); - // Loop till we find a service that we can start. + KService service(serviceName); + auto arguments = KIO::DesktopExecParser(service, QList()).resultingArguments(); + if (arguments.isEmpty()) { + qCWarning(KSMSERVER) << "failed to parse" << serviceName << "for autostart"; + continue; + } + qCInfo(KSMSERVER) << "Starting autostart service " << serviceName << arguments; + auto program = arguments.takeFirst(); + if (!QProcess::startDetached(program, arguments)) + qCWarning(KSMSERVER) << "could not start" << serviceName << ":" << program << arguments; + } while (true); + }); } #include "startup.moc"