Changeset View
Changeset View
Standalone View
Standalone View
ksmserver/startup.cpp
1 | /***************************************************************** | 1 | /***************************************************************** | ||
---|---|---|---|---|---|
2 | ksmserver - the KDE session management server | 2 | ksmserver - the KDE session management server | ||
3 | 3 | | |||
4 | Copyright 2000 Matthias Ettrich <ettrich@kde.org> | 4 | Copyright 2000 Matthias Ettrich <ettrich@kde.org> | ||
5 | Copyright 2005 Lubos Lunak <l.lunak@kde.org> | 5 | Copyright 2005 Lubos Lunak <l.lunak@kde.org> | ||
6 | Copyright 2018 David Edmundson <davidedmundson@kde.org> | ||||
7 | | ||||
6 | 8 | | |||
7 | relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de> | 9 | relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de> | ||
8 | 10 | | |||
9 | some code taken from the dcopserver (part of the KDE libraries), which is | 11 | some code taken from the dcopserver (part of the KDE libraries), which is | ||
10 | Copyright 1999 Matthias Ettrich <ettrich@kde.org> | 12 | Copyright 1999 Matthias Ettrich <ettrich@kde.org> | ||
11 | Copyright 1999 Preston Brown <pbrown@kde.org> | 13 | Copyright 1999 Preston Brown <pbrown@kde.org> | ||
12 | 14 | | |||
13 | Permission is hereby granted, free of charge, to any person obtaining a copy | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy | ||
Show All 13 Lines | |||||
27 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | 29 | AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
28 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 30 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
29 | 31 | | |||
30 | ******************************************************************/ | 32 | ******************************************************************/ | ||
31 | 33 | | |||
32 | #include "startup.h" | 34 | #include "startup.h" | ||
33 | #include "server.h" | 35 | #include "server.h" | ||
34 | 36 | | |||
35 | #include <QDir> | | |||
36 | #include <Kdelibs4Migration> | | |||
37 | #include <config-workspace.h> | 37 | #include <config-workspace.h> | ||
38 | #include <config-unix.h> // HAVE_LIMITS_H | 38 | #include <config-unix.h> // HAVE_LIMITS_H | ||
39 | #include <config-ksmserver.h> | 39 | #include <config-ksmserver.h> | ||
40 | 40 | | |||
41 | #include <ksmserver_debug.h> | 41 | #include <ksmserver_debug.h> | ||
42 | 42 | | |||
43 | #include <pwd.h> | 43 | #include "kcminit_interface.h" | ||
44 | #include <sys/types.h> | 44 | #include <klauncher_interface.h> | ||
45 | #include <sys/param.h> | | |||
46 | #include <sys/stat.h> | | |||
47 | #ifdef HAVE_SYS_TIME_H | | |||
48 | #include <sys/time.h> | | |||
49 | #endif | | |||
50 | #include <sys/socket.h> | | |||
51 | #include <sys/un.h> | | |||
52 | | ||||
53 | #include <unistd.h> | | |||
54 | #include <stdlib.h> | | |||
55 | #include <signal.h> | | |||
56 | #include <time.h> | | |||
57 | #include <errno.h> | | |||
58 | #include <string.h> | | |||
59 | #include <assert.h> | | |||
60 | | ||||
61 | #ifdef HAVE_LIMITS_H | | |||
62 | #include <limits.h> | | |||
63 | #endif | | |||
64 | | ||||
65 | #include <QTimer> | | |||
66 | #include <QDBusConnection> | | |||
67 | #include <QDBusMessage> | | |||
68 | #include <QDBusPendingCall> | | |||
69 | #include <phonon/audiooutput.h> | | |||
70 | #include <phonon/mediaobject.h> | | |||
71 | #include <phonon/mediasource.h> | | |||
72 | #include <QStandardPaths> | | |||
73 | 45 | | |||
74 | #include <kconfig.h> | 46 | #include <KCompositeJob> | ||
75 | #include <kconfiggroup.h> | 47 | #include <Kdelibs4Migration> | ||
76 | #include <kio/desktopexecparser.h> | 48 | #include <KIO/DesktopExecParser> | ||
77 | #include <KSharedConfig> | 49 | #include <KJob> | ||
78 | #include <kprocess.h> | | |||
79 | #include <KNotifyConfig> | 50 | #include <KNotifyConfig> | ||
51 | #include <KProcess> | ||||
80 | #include <KService> | 52 | #include <KService> | ||
81 | 53 | | |||
82 | #include "global.h" | 54 | #include <phonon/audiooutput.h> | ||
83 | #include "server.h" | 55 | #include <phonon/mediaobject.h> | ||
84 | #include "client.h" | 56 | #include <phonon/mediasource.h> | ||
85 | 57 | | |||
58 | #include <QDBusConnection> | ||||
59 | #include <QDBusMessage> | ||||
60 | #include <QDBusPendingCall> | ||||
61 | #include <QDir> | ||||
62 | #include <QStandardPaths> | ||||
63 | #include <QTimer> | ||||
86 | #include <QX11Info> | 64 | #include <QX11Info> | ||
87 | 65 | | |||
88 | //#include "kdesktop_interface.h" | 66 | class Phase: public KCompositeJob | ||
89 | #include <klauncher_interface.h> | 67 | { | ||
90 | #include <qstandardpaths.h> | 68 | public: | ||
91 | #include "kcminit_interface.h" | 69 | Phase(QObject *parent): | ||
70 | KCompositeJob(parent) | ||||
71 | {} | ||||
72 | | ||||
73 | bool addSubjob(KJob *job) override { | ||||
74 | bool rc = KCompositeJob::addSubjob(job); | ||||
75 | job->start(); | ||||
76 | return rc; | ||||
77 | } | ||||
78 | | ||||
79 | void slotResult(KJob *job) override { | ||||
80 | KCompositeJob::slotResult(job); | ||||
81 | if (!hasSubjobs()) { | ||||
82 | emitResult(); | ||||
83 | } | ||||
84 | } | ||||
85 | }; | ||||
92 | 86 | | |||
93 | //#define KSMSERVER_STARTUP_DEBUG1 | 87 | class StartupPhase0: public Phase | ||
88 | { | ||||
89 | public: | ||||
90 | StartupPhase0(QObject *parent) : Phase(parent) | ||||
91 | {} | ||||
92 | void start() override { | ||||
93 | qCDebug(KSMSERVER) << "Phase 0"; | ||||
94 | addSubjob(new AutoStartAppsJob(0)); | ||||
95 | addSubjob(new KCMInitJob(1)); | ||||
broulik: Can these happen in parallel? In the old code (as far as I can understand it) it did auto start… | |||||
Well read! It was deliberate, but I should have commented it in the description. Autostart apps job only launches, it doesn't (currently) block waiting for them to start. Therefore it's effectively in parallel already. davidedmundson: Well read!
It was deliberate, but I should have commented it in the description.
Autostart… | |||||
96 | } | ||||
97 | }; | ||||
98 | | ||||
99 | class StartupPhase1: public Phase | ||||
100 | { | ||||
101 | public: | ||||
102 | StartupPhase1(QObject *parent) : Phase(parent) | ||||
103 | {} | ||||
104 | void start() override { | ||||
105 | qCDebug(KSMSERVER) << "Phase 1"; | ||||
106 | addSubjob(new AutoStartAppsJob(1)); | ||||
107 | } | ||||
108 | }; | ||||
94 | 109 | | |||
95 | #ifdef KSMSERVER_STARTUP_DEBUG1 | 110 | class StartupPhase2: public Phase | ||
96 | static QTime t; | 111 | { | ||
97 | #endif | 112 | public: | ||
113 | StartupPhase2(QObject *parent) : Phase(parent) | ||||
114 | {} | ||||
115 | void runUserAutostart(); | ||||
116 | bool migrateKDE4Autostart(const QString &folder); | ||||
117 | | ||||
118 | void start() override { | ||||
119 | qCDebug(KSMSERVER) << "Phase 2"; | ||||
120 | addSubjob(new AutoStartAppsJob(2)); | ||||
121 | addSubjob(new KDEDInitJob()); | ||||
122 | addSubjob(new KCMInitJob(2)); | ||||
123 | runUserAutostart(); | ||||
124 | } | ||||
125 | }; | ||||
98 | 126 | | |||
99 | // Put the notification in its own thread as it can happen that | 127 | // Put the notification in its own thread as it can happen that | ||
100 | // PulseAudio will start initializing with this, so let's not | 128 | // PulseAudio will start initializing with this, so let's not | ||
101 | // block the main thread with waiting for PulseAudio to start | 129 | // block the main thread with waiting for PulseAudio to start | ||
102 | class NotificationThread : public QThread | 130 | class NotificationThread : public QThread | ||
103 | { | 131 | { | ||
104 | Q_OBJECT | 132 | Q_OBJECT | ||
105 | void run() override { | 133 | void run() override { | ||
106 | // We cannot parent to the thread itself so let's create | 134 | // We cannot parent to the thread itself so let's create | ||
107 | // a QObject on the stack and parent everythign to it | 135 | // a QObject on the stack and parent everythign to it | ||
108 | QObject parent; | 136 | QObject parent; | ||
109 | KNotifyConfig notifyConfig(QStringLiteral("plasma_workspace"), QList< QPair<QString,QString> >(), QStringLiteral("startkde")); | 137 | KNotifyConfig notifyConfig(QStringLiteral("plasma_workspace"), QList< QPair<QString,QString> >(), QStringLiteral("startkde")); | ||
110 | const QString action = notifyConfig.readEntry(QStringLiteral("Action")); | 138 | const QString action = notifyConfig.readEntry(QStringLiteral("Action")); | ||
111 | if (action.isEmpty() || !action.split(QLatin1Char('|')).contains(QStringLiteral("Sound"))) { | 139 | if (action.isEmpty() || !action.split(QLatin1Char('|')).contains(QStringLiteral("Sound"))) { | ||
112 | // no startup sound configured | 140 | // no startup sound configured | ||
113 | return; | 141 | return; | ||
114 | } | 142 | } | ||
115 | Phonon::AudioOutput *m_audioOutput = new Phonon::AudioOutput(Phonon::NotificationCategory, &parent); | 143 | Phonon::AudioOutput *m_audioOutput = new Phonon::AudioOutput(Phonon::NotificationCategory, &parent); | ||
broulik: As a next step I'd like to see this ported to libcanberra :P | |||||
Canberra has a systemd unit that plays startup/shutdown sounds automatically. It could be entirely someone elses problem. \o/ davidedmundson: Canberra has a systemd unit that plays startup/shutdown sounds automatically. It could be… | |||||
116 | 144 | | |||
117 | QString soundFilename = notifyConfig.readEntry(QStringLiteral("Sound")); | 145 | QString soundFilename = notifyConfig.readEntry(QStringLiteral("Sound")); | ||
118 | if (soundFilename.isEmpty()) { | 146 | if (soundFilename.isEmpty()) { | ||
119 | qCWarning(KSMSERVER) << "Audio notification requested, but no sound file provided in notifyrc file, aborting audio notification"; | 147 | qCWarning(KSMSERVER) << "Audio notification requested, but no sound file provided in notifyrc file, aborting audio notification"; | ||
120 | return; | 148 | return; | ||
121 | } | 149 | } | ||
122 | 150 | | |||
123 | QUrl soundURL; | 151 | QUrl soundURL; | ||
Show All 23 Lines | |||||
147 | m->play(); | 175 | m->play(); | ||
148 | exec(); | 176 | exec(); | ||
149 | } | 177 | } | ||
150 | 178 | | |||
151 | }; | 179 | }; | ||
152 | 180 | | |||
153 | Startup::Startup(KSMServer *parent): | 181 | Startup::Startup(KSMServer *parent): | ||
154 | QObject(parent), | 182 | QObject(parent), | ||
155 | ksmserver(parent), | 183 | ksmserver(parent) | ||
156 | state(Waiting) | | |||
157 | { | 184 | { | ||
158 | connect(ksmserver, &KSMServer::windowManagerLoaded, this, &Startup::autoStart0); | 185 | auto phase0 = new StartupPhase0(this); | ||
186 | auto phase1 = new StartupPhase1(this); | ||||
187 | auto phase2 = new StartupPhase2(this); | ||||
188 | auto restoreSession = new RestoreSessionJob(ksmserver); | ||||
189 | | ||||
190 | connect(ksmserver, &KSMServer::windowManagerLoaded, phase0, &KJob::start); | ||||
191 | connect(phase0, &KJob::finished, phase1, &KJob::start); | ||||
192 | | ||||
193 | connect(phase1, &KJob::finished, this, [=]() { | ||||
194 | ksmserver->setupShortcuts(); // done only here, because it needs kglobalaccel :-/ | ||||
anthonyfieroni: Can we delay it to finishStartup? | |||||
Probably, but for now I'm trying to be as close to 1:1 with the old code. Long term plan ideally I want these just in a .desktop file that kglobalaccel can use directly. davidedmundson: Probably, but for now I'm trying to be as close to 1:1 with the old code.
Long term plan… | |||||
195 | }); | ||||
196 | | ||||
197 | if (ksmserver->defaultSession()) { | ||||
198 | connect(phase1, &KJob::finished, phase2, &KJob::start); | ||||
199 | } else { | ||||
200 | connect(phase1, &KJob::finished, restoreSession, &KJob::start); | ||||
201 | connect(restoreSession, &KJob::finished, phase2, &KJob::start); | ||||
202 | } | ||||
203 | connect(phase1, &KJob::finished, this, [this]() { | ||||
204 | NotificationThread *loginSound = new NotificationThread(); | ||||
205 | connect(loginSound, &NotificationThread::finished, loginSound, &NotificationThread::deleteLater); | ||||
206 | loginSound->start();}); | ||||
broulik: Coding style, `});` on new line | |||||
207 | connect(phase2, &KJob::finished, this, &Startup::finishStartup); | ||||
159 | } | 208 | } | ||
160 | 209 | | |||
161 | void Startup::autoStart0() | 210 | void Startup::upAndRunning( const QString& msg ) | ||
anthonyfieroni: It's unused, can we remove it? | |||||
davidedmundson: it's called from main | |||||
162 | { | 211 | { | ||
163 | disconnect(ksmserver, &KSMServer::windowManagerLoaded, this, &Startup::autoStart0); | 212 | QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"), | ||
164 | state = AutoStart0; | 213 | QStringLiteral("/KSplash"), | ||
165 | #ifdef KSMSERVER_STARTUP_DEBUG1 | 214 | QStringLiteral("org.kde.KSplash"), | ||
166 | qCDebug(KSMSERVER) << t.elapsed(); | 215 | QStringLiteral("setStage")); | ||
167 | #endif | 216 | ksplashProgressMessage.setArguments(QList<QVariant>() << msg); | ||
217 | QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage); | ||||
218 | } | ||||
168 | 219 | | |||
169 | autoStart(0); | 220 | void Startup::finishStartup() | ||
221 | { | ||||
222 | qCDebug(KSMSERVER) << "Finished"; | ||||
223 | ksmserver->state = KSMServer::Idle; | ||||
224 | ksmserver->setupXIOErrorHandler(); | ||||
170 | } | 225 | } | ||
171 | 226 | | |||
172 | void Startup::autoStart0Done() | 227 | KCMInitJob::KCMInitJob(int phase) | ||
228 | :m_phase(phase) | ||||
173 | { | 229 | { | ||
174 | if( state != AutoStart0 ) | 230 | } | ||
175 | return; | 231 | | ||
176 | qCDebug(KSMSERVER) << "Autostart 0 done"; | 232 | void KCMInitJob::start() { | ||
177 | #ifdef KSMSERVER_STARTUP_DEBUG1 | 233 | //FIXME - replace all this with just a DBus call with a timeout and make kcminit delay the reply till it's done | ||
178 | qCDebug(KSMSERVER) << t.elapsed(); | | |||
179 | #endif | | |||
180 | 234 | | |||
181 | state = KcmInitPhase1; | 235 | auto kcminitSignals = new QDBusInterface( QStringLiteral( "org.kde.kcminit"), | ||
182 | kcminitSignals = new QDBusInterface( QStringLiteral( "org.kde.kcminit"), | | |||
183 | QStringLiteral( "/kcminit" ), | 236 | QStringLiteral( "/kcminit" ), | ||
184 | QStringLiteral( "org.kde.KCMInit" ), | 237 | QStringLiteral( "org.kde.KCMInit" ), | ||
185 | QDBusConnection::sessionBus(), this ); | 238 | QDBusConnection::sessionBus(), this ); | ||
186 | if( !kcminitSignals->isValid()) { | 239 | if( !kcminitSignals->isValid()) { | ||
187 | qCWarning(KSMSERVER) << "kcminit not running? If we are running with mobile profile or in another platform other than X11 this is normal."; | 240 | qCWarning(KSMSERVER) << "kcminit not running? If we are running with mobile profile or in another platform other than X11 this is normal."; | ||
188 | delete kcminitSignals; | 241 | QTimer::singleShot(0, this, [this]() { | ||
189 | kcminitSignals = nullptr; | 242 | emitResult();}); | ||
190 | QTimer::singleShot(0, this, &Startup::kcmPhase1Done); | | |||
191 | return; | 243 | return; | ||
192 | } | 244 | } | ||
193 | connect( kcminitSignals, SIGNAL(phase1Done()), SLOT(kcmPhase1Done())); | 245 | if (m_phase == 1) { | ||
194 | QTimer::singleShot( 10000, this, &Startup::kcmPhase1Timeout); // protection | 246 | connect( kcminitSignals, SIGNAL(phase1Done()), this, SLOT(emitResult())); | ||
247 | } else { | ||||
248 | connect( kcminitSignals, SIGNAL(phase2Done()), this, SLOT(emitResult())); | ||||
249 | } | ||||
250 | QTimer::singleShot( 10000, this, [this]() {emitResult();}); // protection | ||||
195 | 251 | | |||
196 | org::kde::KCMInit kcminit(QStringLiteral("org.kde.kcminit"), | 252 | org::kde::KCMInit kcminit(QStringLiteral("org.kde.kcminit"), | ||
197 | QStringLiteral("/kcminit"), | 253 | QStringLiteral("/kcminit"), | ||
198 | QDBusConnection::sessionBus()); | 254 | QDBusConnection::sessionBus()); | ||
199 | kcminit.runPhase1(); | | |||
200 | } | | |||
201 | | ||||
202 | void Startup::kcmPhase1Done() | | |||
203 | { | | |||
204 | if( state != KcmInitPhase1 ) | | |||
205 | return; | | |||
206 | qCDebug(KSMSERVER) << "Kcminit phase 1 done"; | | |||
207 | if (kcminitSignals) { | | |||
208 | disconnect( kcminitSignals, SIGNAL(phase1Done()), this, SLOT(kcmPhase1Done())); | | |||
209 | } | | |||
210 | autoStart1(); | | |||
211 | } | | |||
212 | 255 | | |||
213 | void Startup::kcmPhase1Timeout() | 256 | if (m_phase == 1) { | ||
214 | { | 257 | kcminit.runPhase1(); | ||
215 | if( state != KcmInitPhase1 ) | 258 | } else { | ||
216 | return; | 259 | kcminit.runPhase2(); | ||
217 | qCDebug(KSMSERVER) << "Kcminit phase 1 timeout"; | | |||
218 | kcmPhase1Done(); | | |||
219 | } | 260 | } | ||
220 | | ||||
221 | void Startup::autoStart1() | | |||
222 | { | | |||
223 | if( state != KcmInitPhase1 ) | | |||
224 | return; | | |||
225 | state = AutoStart1; | | |||
226 | #ifdef KSMSERVER_STARTUP_DEBUG1 | | |||
227 | qCDebug(KSMSERVER)<< t.elapsed(); | | |||
228 | #endif | | |||
229 | autoStart(1); | | |||
230 | } | 261 | } | ||
231 | 262 | | |||
232 | void Startup::autoStart1Done() | 263 | KDEDInitJob::KDEDInitJob() | ||
233 | { | 264 | { | ||
234 | if( state != AutoStart1 ) | | |||
235 | return; | | |||
236 | qCDebug(KSMSERVER) << "Autostart 1 done"; | | |||
237 | ksmserver->setupShortcuts(); // done only here, because it needs kglobalaccel :-/ | | |||
238 | ksmserver->lastAppStarted = 0; | | |||
239 | ksmserver->lastIdStarted.clear(); | | |||
240 | ksmserver->state = KSMServer::Restoring; | | |||
241 | #ifdef KSMSERVER_STARTUP_DEBUG1 | | |||
242 | qCDebug(KSMSERVER)<< t.elapsed(); | | |||
243 | #endif | | |||
244 | if( ksmserver->defaultSession()) { | | |||
245 | autoStart2(); | | |||
246 | return; | | |||
247 | } | | |||
248 | ksmserver->tryRestoreNext(); | | |||
249 | connect(ksmserver, &KSMServer::sessionRestored, this, &Startup::autoStart2); | | |||
250 | } | 265 | } | ||
251 | 266 | | |||
252 | void Startup::autoStart2() | 267 | void KDEDInitJob::start() { | ||
253 | { | 268 | qCDebug(KSMSERVER()); | ||
254 | if( ksmserver->state != KSMServer::Restoring ) | | |||
255 | return; | | |||
256 | ksmserver->startupDone(); | | |||
257 | | ||||
258 | state = FinishingStartup; | | |||
259 | #ifdef KSMSERVER_STARTUP_DEBUG1 | | |||
260 | qCDebug(KSMSERVER)<< t.elapsed(); | | |||
261 | #endif | | |||
262 | waitAutoStart2 = true; | | |||
263 | waitKcmInit2 = true; | | |||
264 | autoStart(2); | | |||
265 | | ||||
266 | QDBusInterface kded( QStringLiteral( "org.kde.kded5" ), | 269 | QDBusInterface kded( QStringLiteral( "org.kde.kded5" ), | ||
267 | QStringLiteral( "/kded" ), | 270 | QStringLiteral( "/kded" ), | ||
268 | QStringLiteral( "org.kde.kded5" ) ); | 271 | QStringLiteral( "org.kde.kded5" ) ); | ||
269 | auto pending = kded.asyncCall( QStringLiteral( "loadSecondPhase" ) ); | 272 | auto pending = kded.asyncCall( QStringLiteral( "loadSecondPhase" ) ); | ||
270 | 273 | | |||
271 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending, this); | 274 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending, this); | ||
272 | QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &Startup::secondKDEDPhaseLoaded); | 275 | QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this]() {emitResult();}); | ||
apol: can't this be `&KJob::emitResult`? | |||||
It's stupid: I can do connect(watch, SIGNAL(finished()), this, SLOT(emitResult())); but not with function pointers because: error: 'emitResult' is a protected member of 'KJob' connect(watcher, &QDBusPendingCallWatcher::finished, this, &KJob::emitResult); davidedmundson: It's stupid:
I can do
connect(watch, SIGNAL(finished()), this, SLOT(emitResult()));
but not… | |||||
273 | QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, &QObject::deleteLater); | 276 | QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, &QObject::deleteLater); | ||
274 | runUserAutostart(); | | |||
275 | | ||||
276 | if (kcminitSignals) { | | |||
277 | connect( kcminitSignals, SIGNAL(phase2Done()), SLOT(kcmPhase2Done())); | | |||
278 | QTimer::singleShot( 10000, this, &Startup::kcmPhase2Timeout); // protection | | |||
279 | org::kde::KCMInit kcminit(QStringLiteral("org.kde.kcminit"), | | |||
280 | QStringLiteral("/kcminit"), | | |||
281 | QDBusConnection::sessionBus()); | | |||
282 | kcminit.runPhase2(); | | |||
283 | } else { | | |||
284 | QTimer::singleShot(0, this, &Startup::kcmPhase2Done); | | |||
285 | } | 277 | } | ||
286 | } | | |||
287 | | ||||
288 | void Startup::secondKDEDPhaseLoaded() | | |||
289 | { | | |||
290 | | ||||
291 | #ifdef KSMSERVER_STARTUP_DEBUG1 | | |||
292 | qCDebug(KSMSERVER)<< "kded" << t.elapsed(); | | |||
293 | #endif | | |||
294 | 278 | | |||
295 | if( !ksmserver->defaultSession()) | 279 | RestoreSessionJob::RestoreSessionJob(KSMServer *server): KJob(), | ||
296 | ksmserver->restoreLegacySession(KSharedConfig::openConfig().data()); | 280 | m_ksmserver(server) | ||
281 | {} | ||||
297 | 282 | | |||
298 | qCDebug(KSMSERVER) << "Starting notification thread"; | 283 | void RestoreSessionJob::start() | ||
299 | NotificationThread *loginSound = new NotificationThread(); | 284 | { | ||
300 | // Delete the thread when finished | 285 | m_ksmserver->lastAppStarted = 0; | ||
301 | connect(loginSound, &NotificationThread::finished, loginSound, &NotificationThread::deleteLater); | 286 | m_ksmserver->lastIdStarted.clear(); | ||
302 | loginSound->start(); | 287 | m_ksmserver->state = KSMServer::Restoring; | ||
288 | connect(m_ksmserver, &KSMServer::sessionRestored, this, [this]() {emitResult();}); | ||||
289 | m_ksmserver->tryRestoreNext(); | ||||
303 | } | 290 | } | ||
304 | 291 | | |||
305 | void Startup::runUserAutostart() | 292 | void StartupPhase2::runUserAutostart() | ||
306 | { | 293 | { | ||
307 | // Now let's execute the scripts in the KDE-specific autostart-scripts folder. | 294 | // Now let's execute the scripts in the KDE-specific autostart-scripts folder. | ||
308 | const QString autostartFolder = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QDir::separator() + QStringLiteral("autostart-scripts"); | 295 | const QString autostartFolder = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QDir::separator() + QStringLiteral("autostart-scripts"); | ||
309 | 296 | | |||
310 | QDir dir(autostartFolder); | 297 | QDir dir(autostartFolder); | ||
311 | if (!dir.exists()) { | 298 | if (!dir.exists()) { | ||
312 | // Create dir in all cases, so that users can find it :-) | 299 | // Create dir in all cases, so that users can find it :-) | ||
313 | dir.mkpath(QStringLiteral(".")); | 300 | dir.mkpath(QStringLiteral(".")); | ||
Show All 19 Lines | 313 | { | |||
333 | connect(p, static_cast<void (QProcess::*)(int)>(&QProcess::finished), [p](int exitCode) { | 320 | connect(p, static_cast<void (QProcess::*)(int)>(&QProcess::finished), [p](int exitCode) { | ||
334 | qCInfo(KSMSERVER) << "autostart script" << p->program() << "finished with exit code " << exitCode; | 321 | qCInfo(KSMSERVER) << "autostart script" << p->program() << "finished with exit code " << exitCode; | ||
335 | p->deleteLater(); | 322 | p->deleteLater(); | ||
336 | }); | 323 | }); | ||
337 | } | 324 | } | ||
338 | } | 325 | } | ||
339 | } | 326 | } | ||
340 | 327 | | |||
341 | bool Startup::migrateKDE4Autostart(const QString &autostartFolder) | 328 | bool StartupPhase2::migrateKDE4Autostart(const QString &autostartFolder) | ||
342 | { | 329 | { | ||
343 | // Migrate user autostart from kde4 | 330 | // Migrate user autostart from kde4 | ||
344 | Kdelibs4Migration migration; | 331 | Kdelibs4Migration migration; | ||
345 | if (!migration.kdeHomeFound()) { | 332 | if (!migration.kdeHomeFound()) { | ||
346 | return false; | 333 | return false; | ||
347 | } | 334 | } | ||
348 | // KDEHOME/Autostart was the default value for KGlobalSettings::autostart() | 335 | // KDEHOME/Autostart was the default value for KGlobalSettings::autostart() | ||
349 | QString oldAutostart = migration.kdeHome() + QStringLiteral("/Autostart"); | 336 | QString oldAutostart = migration.kdeHome() + QStringLiteral("/Autostart"); | ||
Show All 19 Lines | 346 | foreach (const QString &file, entries) { | |||
369 | } | 356 | } | ||
370 | if (!success) { | 357 | if (!success) { | ||
371 | qCWarning(KSMSERVER) << "Error copying" << src << "to" << dest; | 358 | qCWarning(KSMSERVER) << "Error copying" << src << "to" << dest; | ||
372 | } | 359 | } | ||
373 | } | 360 | } | ||
374 | return true; | 361 | return true; | ||
375 | } | 362 | } | ||
376 | 363 | | |||
377 | void Startup::autoStart2Done() | 364 | AutoStartAppsJob::AutoStartAppsJob(int phase) | ||
378 | { | 365 | { | ||
379 | if( state != FinishingStartup ) | 366 | m_autoStart.loadAutoStartList(); //FIXME, share this between jobs | ||
380 | return; | | |||
381 | qCDebug(KSMSERVER) << "Autostart 2 done"; | | |||
382 | waitAutoStart2 = false; | | |||
383 | finishStartup(); | | |||
384 | } | | |||
385 | | ||||
386 | void Startup::kcmPhase2Done() | | |||
387 | { | | |||
388 | if( state != FinishingStartup ) | | |||
389 | return; | | |||
390 | qCDebug(KSMSERVER) << "Kcminit phase 2 done"; | | |||
391 | if (kcminitSignals) { | | |||
392 | disconnect( kcminitSignals, SIGNAL(phase2Done()), this, SLOT(kcmPhase2Done())); | | |||
393 | delete kcminitSignals; | | |||
394 | kcminitSignals = nullptr; | | |||
395 | } | | |||
396 | waitKcmInit2 = false; | | |||
397 | finishStartup(); | | |||
398 | } | | |||
399 | | ||||
400 | void Startup::kcmPhase2Timeout() | | |||
401 | { | | |||
402 | if( !waitKcmInit2 ) | | |||
403 | return; | | |||
404 | qCDebug(KSMSERVER) << "Kcminit phase 2 timeout"; | | |||
405 | kcmPhase2Done(); | | |||
406 | } | | |||
407 | | ||||
408 | void Startup::finishStartup() | | |||
409 | { | | |||
410 | if( state != FinishingStartup ) | | |||
411 | return; | | |||
412 | if( waitAutoStart2 || waitKcmInit2 ) | | |||
413 | return; | | |||
414 | | ||||
415 | upAndRunning( QStringLiteral( "ready" ) ); | | |||
416 | #ifdef KSMSERVER_STARTUP_DEBUG1 | | |||
417 | qCDebug(KSMSERVER)<< t.elapsed(); | | |||
418 | #endif | | |||
419 | | ||||
420 | state = Waiting; | | |||
421 | ksmserver->setupXIOErrorHandler(); // From now on handle X errors as normal shutdown. | | |||
422 | } | | |||
423 | | ||||
424 | void Startup::upAndRunning( const QString& msg ) | | |||
425 | { | | |||
426 | QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"), | | |||
427 | QStringLiteral("/KSplash"), | | |||
428 | QStringLiteral("org.kde.KSplash"), | | |||
429 | QStringLiteral("setStage")); | | |||
430 | ksplashProgressMessage.setArguments(QList<QVariant>() << msg); | | |||
431 | QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage); | | |||
432 | } | | |||
433 | | ||||
434 | | ||||
435 | void Startup::autoStart(int phase) | | |||
436 | { | | |||
437 | if (m_autoStart.phase() >= phase) { | | |||
438 | return; | | |||
439 | } | | |||
440 | m_autoStart.setPhase(phase); | 367 | m_autoStart.setPhase(phase); | ||
441 | if (phase == 0) { | | |||
442 | m_autoStart.loadAutoStartList(); | | |||
443 | } | | |||
444 | QTimer::singleShot(0, this, &Startup::slotAutoStart); | | |||
445 | } | 368 | } | ||
446 | 369 | | |||
447 | void Startup::slotAutoStart() | 370 | void AutoStartAppsJob::start() { | ||
448 | { | 371 | qCDebug(KSMSERVER()); | ||
372 | | ||||
373 | QTimer::singleShot(0, this, [=]() { | ||||
449 | do { | 374 | do { | ||
450 | QString serviceName = m_autoStart.startService(); | 375 | QString serviceName = m_autoStart.startService(); | ||
451 | if (serviceName.isEmpty()) { | 376 | if (serviceName.isEmpty()) { | ||
452 | // Done | 377 | // Done | ||
453 | if (!m_autoStart.phaseDone()) { | 378 | if (!m_autoStart.phaseDone()) { | ||
454 | m_autoStart.setPhaseDone(); | 379 | m_autoStart.setPhaseDone(); | ||
455 | switch (m_autoStart.phase()) { | | |||
456 | case 0: | | |||
457 | autoStart0Done(); | | |||
458 | break; | | |||
459 | case 1: | | |||
460 | autoStart1Done(); | | |||
461 | break; | | |||
462 | case 2: | | |||
463 | autoStart2Done(); | | |||
464 | break; | | |||
465 | } | | |||
466 | } | 380 | } | ||
381 | emitResult(); | ||||
467 | return; | 382 | return; | ||
468 | } | 383 | } | ||
469 | KService service(serviceName); | 384 | KService service(serviceName); | ||
470 | auto arguments = KIO::DesktopExecParser(service, QList<QUrl>()).resultingArguments(); | 385 | auto arguments = KIO::DesktopExecParser(service, QList<QUrl>()).resultingArguments(); | ||
471 | if (arguments.isEmpty()) { | 386 | if (arguments.isEmpty()) { | ||
472 | qCWarning(KSMSERVER) << "failed to parse" << serviceName << "for autostart"; | 387 | qCWarning(KSMSERVER) << "failed to parse" << serviceName << "for autostart"; | ||
473 | continue; | 388 | continue; | ||
474 | } | 389 | } | ||
475 | qCInfo(KSMSERVER) << "Starting autostart service " << serviceName << arguments; | 390 | qCInfo(KSMSERVER) << "Starting autostart service " << serviceName << arguments; | ||
476 | auto program = arguments.takeFirst(); | 391 | auto program = arguments.takeFirst(); | ||
477 | if (!QProcess::startDetached(program, arguments)) | 392 | if (!QProcess::startDetached(program, arguments)) | ||
478 | qCWarning(KSMSERVER) << "could not start" << serviceName << ":" << program << arguments; | 393 | qCWarning(KSMSERVER) << "could not start" << serviceName << ":" << program << arguments; | ||
479 | } while (true); | 394 | } while (true); | ||
480 | // Loop till we find a service that we can start. | 395 | }); | ||
481 | } | 396 | } | ||
482 | 397 | | |||
483 | #include "startup.moc" | 398 | #include "startup.moc" |
Can these happen in parallel? In the old code (as far as I can understand it) it did auto start first and then kcminit phase 1? or does addSubjob queue them one after the other (doesn't look like it)?