diff --git a/ksmserver/server.h b/ksmserver/server.h index 3ed05ad20..e9bc7b5e8 100644 --- a/ksmserver/server.h +++ b/ksmserver/server.h @@ -1,273 +1,274 @@ /***************************************************************** ksmserver - the KDE session management server Copyright 2000 Matthias Ettrich Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************/ #ifndef SERVER_H #define SERVER_H #define INT32 QINT32 #include #include #include extern "C" { #include #include #include #include #include } #include // needed to avoid clash with INT8 defined in X11/Xmd.h on solaris #define QT_CLEAN_NAMESPACE 1 #include #include #include #include #include #include #include #include #define SESSION_PREVIOUS_LOGOUT "saved at previous logout" #define SESSION_BY_USER "saved by user" class KProcess; class KSMListener; class KSMConnection; class KSMClient; class OrgKdeKLauncherInterface; class QDBusInterface; enum SMType { SM_ERROR, SM_WMCOMMAND, SM_WMSAVEYOURSELF }; struct SMData { SMType type; QStringList wmCommand; QString wmClientMachine; QString wmclass1, wmclass2; }; typedef QMap WindowMap; class KSMServer : public QObject { Q_OBJECT public: enum class InitFlag { None = 0, OnlyLocal = 1 << 0, ImmediateLockScreen = 1 << 1, NoLockScreen = 1 << 2 }; Q_DECLARE_FLAGS(InitFlags, InitFlag) KSMServer( const QString& windowManager, InitFlags flags ); ~KSMServer() override; static KSMServer* self(); void* watchConnection( IceConn iceConn ); void removeConnection( KSMConnection* conn ); KSMClient* newClient( SmsConn ); void deleteClient( KSMClient* client ); // callbacks void saveYourselfDone( KSMClient* client, bool success ); void interactRequest( KSMClient* client, int dialogType ); void interactDone( KSMClient* client, bool cancelShutdown ); void phase2Request( KSMClient* client ); // error handling void ioError( IceConn iceConn ); // notification void clientSetProgram( KSMClient* client ); void clientRegistered( const char* previousId ); // public API void restoreSession( const QString &sessionName ); void startDefaultSession(); void shutdown( KWorkSpace::ShutdownConfirm confirm, KWorkSpace::ShutdownType sdtype, KWorkSpace::ShutdownMode sdmode ); Q_SIGNALS: void windowManagerLoaded(); public Q_SLOTS: void cleanUp(); private Q_SLOTS: void newConnection( int socket ); void processData( int socket ); void protectionTimeout(); void timeoutQuit(); void timeoutWMQuit(); void pendingShutdownTimeout(); void wmProcessChange(); void defaultLogout(); void logoutWithoutConfirmation(); void haltWithoutConfirmation(); void rebootWithoutConfirmation(); private: void handlePendingInteractions(); void completeShutdownOrCheckpoint(); void startKilling(); void startKillingSubSession(); void performStandardKilling(); void completeKilling(); void completeKillingSubSession(); void killWM(); void signalSubSessionClosed(); void completeKillingWM(); void cancelShutdown( KSMClient* c ); void killingCompleted(); void createLogoutEffectWidget(); void discardSession(); void storeSession(); void startProtection(); void endProtection(); void launchWM( const QList< QStringList >& wmStartCommands ); KProcess* startApplication( const QStringList& command, const QString& clientMachine = QString(), const QString& userId = QString(), bool wm = false ); void executeCommand( const QStringList& command ); bool isWM( const KSMClient* client ) const; bool isWM( const QString& program ) const; void selectWm( const QString& kdewm ); bool defaultSession() const; // empty session void setupXIOErrorHandler(); void performLegacySessionSave(); void storeLegacySession( KConfig* config ); void restoreLegacySession( KConfig* config ); void restoreLegacySessionInternal( KConfigGroup* config, char sep = ',' ); QStringList windowWmCommand(WId w); QString windowWmClientMachine(WId w); WId windowWmClientLeader(WId w); QByteArray windowSessionId(WId w, WId leader); void setupShortcuts(); void tryRestoreNext(); void startupDone(); void runShutdownScripts(); void performLogout(); // public dcop interface public Q_SLOTS: //public dcop interface void logout( int, int, int ); bool canShutdown(); bool isShuttingDown() const; QString currentSession(); void saveCurrentSession(); void saveCurrentSessionAs( const QString & ); QStringList sessionList(); void wmChanged(); void saveSubSession( const QString &name, QStringList saveAndClose, QStringList saveOnly = QStringList() ); void restoreSubSession( const QString &name ); void openSwitchUserDialog(); Q_SIGNALS: void subSessionClosed(); void subSessionCloseCanceled(); void subSessionOpened(); void sessionRestored(); private: QList listener; QList clients; enum State { Idle, LaunchingWM, Restoring, Shutdown, Checkpoint, Killing, KillingWM, WaitingForKNotify, // shutdown ClosingSubSession, KillingSubSession, RestoringSubSession }; State state; bool saveSession; int wmPhase1WaitingCount; int saveType; KWorkSpace::ShutdownType shutdownType; KWorkSpace::ShutdownMode shutdownMode; QString bootOption; bool clean; KSMClient* clientInteracting; QString wm; QStringList wmCommands; KProcess* wmProcess; QString sessionGroup; QString sessionName; QTimer protectionTimer; QTimer restoreTimer; QString xonCommand; QTimer pendingShutdown; QWidget* logoutEffectWidget; KWorkSpace::ShutdownConfirm pendingShutdown_confirm; KWorkSpace::ShutdownType pendingShutdown_sdtype; KWorkSpace::ShutdownMode pendingShutdown_sdmode; // sequential startup int appsToStart; int lastAppStarted; QString lastIdStarted; QStringList excludeApps; WindowMap legacyWindows; int inhibitCookie; //subSession stuff QList clientsToKill; QList clientsToSave; int sockets[2]; friend bool readFromPipe(int pipe); + friend class RestoreSessionJob; friend class Startup; }; #endif diff --git a/ksmserver/startup.cpp b/ksmserver/startup.cpp index 3eaeae104..10f6677da 100644 --- a/ksmserver/startup.cpp +++ b/ksmserver/startup.cpp @@ -1,483 +1,407 @@ /***************************************************************** ksmserver - the KDE session management server Copyright 2000 Matthias Ettrich Copyright 2005 Lubos Lunak +Copyright 2018 David Edmundson + relatively small extensions by Oswald Buddenhagen some code taken from the dcopserver (part of the KDE libraries), which is Copyright 1999 Matthias Ettrich Copyright 1999 Preston Brown Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ******************************************************************/ #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 // block the main thread with waiting for PulseAudio to start class NotificationThread : public QThread { Q_OBJECT void run() override { // We cannot parent to the thread itself so let's create // a QObject on the stack and parent everythign to it QObject parent; KNotifyConfig notifyConfig(QStringLiteral("plasma_workspace"), QList< QPair >(), QStringLiteral("startkde")); const QString action = notifyConfig.readEntry(QStringLiteral("Action")); if (action.isEmpty() || !action.split(QLatin1Char('|')).contains(QStringLiteral("Sound"))) { // no startup sound configured return; } Phonon::AudioOutput *m_audioOutput = new Phonon::AudioOutput(Phonon::NotificationCategory, &parent); QString soundFilename = notifyConfig.readEntry(QStringLiteral("Sound")); if (soundFilename.isEmpty()) { qCWarning(KSMSERVER) << "Audio notification requested, but no sound file provided in notifyrc file, aborting audio notification"; return; } QUrl soundURL; const auto dataLocations = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); for (const QString &dataLocation: dataLocations) { soundURL = QUrl::fromUserInput(soundFilename, dataLocation + QStringLiteral("/sounds"), QUrl::AssumeLocalFile); if (soundURL.isLocalFile() && QFile::exists(soundURL.toLocalFile())) { break; } else if (!soundURL.isLocalFile() && soundURL.isValid()) { break; } soundURL.clear(); } if (soundURL.isEmpty()) { qCWarning(KSMSERVER) << "Audio notification requested, but sound file from notifyrc file was not found, aborting audio notification"; return; } Phonon::MediaObject *m = new Phonon::MediaObject(&parent); connect(m, &Phonon::MediaObject::finished, this, &NotificationThread::quit); Phonon::createPath(m, m_audioOutput); m->setCurrentSource(soundURL); m->play(); exec(); } }; 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"); QDir dir(autostartFolder); if (!dir.exists()) { // Create dir in all cases, so that users can find it :-) dir.mkpath(QStringLiteral(".")); if (!migrateKDE4Autostart(autostartFolder)) { return; } } const QStringList entries = dir.entryList(QDir::Files); foreach (const QString &file, entries) { // Don't execute backup files if (!file.endsWith(QLatin1Char('~')) && !file.endsWith(QStringLiteral(".bak")) && (file[0] != QLatin1Char('%') || !file.endsWith(QLatin1Char('%'))) && (file[0] != QLatin1Char('#') || !file.endsWith(QLatin1Char('#')))) { const QString fullPath = dir.absolutePath() + QLatin1Char('/') + file; qCInfo(KSMSERVER) << "Starting autostart script " << fullPath; auto p = new KProcess; //deleted in onFinished lambda p->setProgram(fullPath); p->start(); connect(p, static_cast(&QProcess::finished), [p](int exitCode) { qCInfo(KSMSERVER) << "autostart script" << p->program() << "finished with exit code " << exitCode; p->deleteLater(); }); } } } -bool Startup::migrateKDE4Autostart(const QString &autostartFolder) +bool StartupPhase2::migrateKDE4Autostart(const QString &autostartFolder) { // Migrate user autostart from kde4 Kdelibs4Migration migration; if (!migration.kdeHomeFound()) { return false; } // KDEHOME/Autostart was the default value for KGlobalSettings::autostart() QString oldAutostart = migration.kdeHome() + QStringLiteral("/Autostart"); // That path could be customized in kdeglobals const QString oldKdeGlobals = migration.locateLocal("config", QStringLiteral("kdeglobals")); if (!oldKdeGlobals.isEmpty()) { oldAutostart = KConfig(oldKdeGlobals).group("Paths").readEntry("Autostart", oldAutostart); } const QDir oldFolder(oldAutostart); qCDebug(KSMSERVER) << "Copying autostart files from" << oldFolder.path(); const QStringList entries = oldFolder.entryList(QDir::Files); foreach (const QString &file, entries) { const QString src = oldFolder.absolutePath() + QLatin1Char('/') + file; const QString dest = autostartFolder + QLatin1Char('/') + file; QFileInfo info(src); bool success; if (info.isSymLink()) { // This will only work with absolute symlink targets success = QFile::link(info.symLinkTarget(), dest); } else { success = QFile::copy(src, dest); } if (!success) { qCWarning(KSMSERVER) << "Error copying" << src << "to" << dest; } } 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" diff --git a/ksmserver/startup.h b/ksmserver/startup.h index ce11446ae..17cd398bf 100644 --- 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; }; +