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> | 45 | | ||
46 | #include <sys/stat.h> | 46 | #include <KCompositeJob> | ||
47 | #ifdef HAVE_SYS_TIME_H | 47 | #include <Kdelibs4Migration> | ||
48 | #include <sys/time.h> | 48 | #include <KIO/DesktopExecParser> | ||
49 | #endif | 49 | #include <KJob> | ||
50 | #include <sys/socket.h> | 50 | #include <KNotifyConfig> | ||
51 | #include <sys/un.h> | 51 | #include <KProcess> | ||
52 | 52 | #include <KService> | |||
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 | 53 | | |||
65 | #include <QTimer> | | |||
66 | #include <QDBusConnection> | | |||
67 | #include <QDBusMessage> | | |||
68 | #include <QDBusPendingCall> | | |||
69 | #include <phonon/audiooutput.h> | 54 | #include <phonon/audiooutput.h> | ||
70 | #include <phonon/mediaobject.h> | 55 | #include <phonon/mediaobject.h> | ||
71 | #include <phonon/mediasource.h> | 56 | #include <phonon/mediasource.h> | ||
72 | #include <QStandardPaths> | | |||
73 | 57 | | |||
74 | #include <kconfig.h> | 58 | #include <QDBusConnection> | ||
75 | #include <kconfiggroup.h> | 59 | #include <QDBusMessage> | ||
76 | #include <kio/desktopexecparser.h> | 60 | #include <QDBusPendingCall> | ||
77 | #include <KSharedConfig> | 61 | #include <QDir> | ||
78 | #include <kprocess.h> | 62 | #include <QStandardPaths> | ||
79 | #include <KNotifyConfig> | 63 | #include <QTimer> | ||
80 | #include <KService> | 64 | #include <QX11Info> | ||
81 | 65 | | |||
82 | #include "global.h" | 66 | class Phase: public KCompositeJob | ||
83 | #include "server.h" | 67 | { | ||
84 | #include "client.h" | 68 | Q_OBJECT | ||
69 | public: | ||||
70 | Phase(QObject *parent): | ||||
71 | KCompositeJob(parent) | ||||
72 | {} | ||||
73 | | ||||
74 | bool addSubjob(KJob *job) override { | ||||
75 | bool rc = KCompositeJob::addSubjob(job); | ||||
76 | job->start(); | ||||
77 | return rc; | ||||
78 | } | ||||
85 | 79 | | |||
86 | #include <QX11Info> | 80 | void slotResult(KJob *job) override { | ||
81 | KCompositeJob::slotResult(job); | ||||
82 | if (!hasSubjobs()) { | ||||
83 | emitResult(); | ||||
84 | } | ||||
85 | } | ||||
86 | }; | ||||
87 | 87 | | |||
88 | //#include "kdesktop_interface.h" | 88 | class StartupPhase0: public Phase | ||
89 | #include <klauncher_interface.h> | 89 | { | ||
90 | #include <qstandardpaths.h> | 90 | Q_OBJECT | ||
91 | #include "kcminit_interface.h" | 91 | public: | ||
92 | StartupPhase0(QObject *parent) : Phase(parent) | ||||
93 | {} | ||||
94 | void start() override { | ||||
95 | qCDebug(KSMSERVER) << "Phase 0"; | ||||
96 | addSubjob(new AutoStartAppsJob(0)); | ||||
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… | |||||
97 | addSubjob(new KCMInitJob(1)); | ||||
98 | } | ||||
99 | }; | ||||
92 | 100 | | |||
93 | //#define KSMSERVER_STARTUP_DEBUG1 | 101 | class StartupPhase1: public Phase | ||
102 | { | ||||
103 | Q_OBJECT | ||||
104 | public: | ||||
105 | StartupPhase1(QObject *parent) : Phase(parent) | ||||
106 | {} | ||||
107 | void start() override { | ||||
108 | qCDebug(KSMSERVER) << "Phase 1"; | ||||
109 | addSubjob(new AutoStartAppsJob(1)); | ||||
110 | } | ||||
111 | }; | ||||
94 | 112 | | |||
95 | #ifdef KSMSERVER_STARTUP_DEBUG1 | 113 | class StartupPhase2: public Phase | ||
96 | static QTime t; | 114 | { | ||
97 | #endif | 115 | Q_OBJECT | ||
116 | public: | ||||
117 | StartupPhase2(QObject *parent) : Phase(parent) | ||||
118 | {} | ||||
119 | void runUserAutostart(); | ||||
120 | bool migrateKDE4Autostart(const QString &folder); | ||||
121 | | ||||
122 | void start() override { | ||||
123 | qCDebug(KSMSERVER) << "Phase 2"; | ||||
124 | addSubjob(new AutoStartAppsJob(2)); | ||||
125 | addSubjob(new KDEDInitJob()); | ||||
126 | addSubjob(new KCMInitJob(2)); | ||||
127 | runUserAutostart(); | ||||
128 | } | ||||
129 | }; | ||||
98 | 130 | | |||
99 | // Put the notification in its own thread as it can happen that | 131 | // Put the notification in its own thread as it can happen that | ||
100 | // PulseAudio will start initializing with this, so let's not | 132 | // PulseAudio will start initializing with this, so let's not | ||
101 | // block the main thread with waiting for PulseAudio to start | 133 | // block the main thread with waiting for PulseAudio to start | ||
102 | class NotificationThread : public QThread | 134 | class NotificationThread : public QThread | ||
103 | { | 135 | { | ||
104 | Q_OBJECT | 136 | Q_OBJECT | ||
105 | void run() override { | 137 | void run() override { | ||
106 | // We cannot parent to the thread itself so let's create | 138 | // We cannot parent to the thread itself so let's create | ||
107 | // a QObject on the stack and parent everythign to it | 139 | // a QObject on the stack and parent everythign to it | ||
108 | QObject parent; | 140 | QObject parent; | ||
109 | KNotifyConfig notifyConfig(QStringLiteral("plasma_workspace"), QList< QPair<QString,QString> >(), QStringLiteral("startkde")); | 141 | KNotifyConfig notifyConfig(QStringLiteral("plasma_workspace"), QList< QPair<QString,QString> >(), QStringLiteral("startkde")); | ||
110 | const QString action = notifyConfig.readEntry(QStringLiteral("Action")); | 142 | const QString action = notifyConfig.readEntry(QStringLiteral("Action")); | ||
111 | if (action.isEmpty() || !action.split(QLatin1Char('|')).contains(QStringLiteral("Sound"))) { | 143 | if (action.isEmpty() || !action.split(QLatin1Char('|')).contains(QStringLiteral("Sound"))) { | ||
112 | // no startup sound configured | 144 | // no startup sound configured | ||
113 | return; | 145 | return; | ||
114 | } | 146 | } | ||
115 | Phonon::AudioOutput *m_audioOutput = new Phonon::AudioOutput(Phonon::NotificationCategory, &parent); | 147 | 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 | 148 | | |||
117 | QString soundFilename = notifyConfig.readEntry(QStringLiteral("Sound")); | 149 | QString soundFilename = notifyConfig.readEntry(QStringLiteral("Sound")); | ||
118 | if (soundFilename.isEmpty()) { | 150 | if (soundFilename.isEmpty()) { | ||
119 | qCWarning(KSMSERVER) << "Audio notification requested, but no sound file provided in notifyrc file, aborting audio notification"; | 151 | qCWarning(KSMSERVER) << "Audio notification requested, but no sound file provided in notifyrc file, aborting audio notification"; | ||
120 | return; | 152 | return; | ||
121 | } | 153 | } | ||
122 | 154 | | |||
123 | QUrl soundURL; | 155 | QUrl soundURL; | ||
Show All 23 Lines | |||||
147 | m->play(); | 179 | m->play(); | ||
148 | exec(); | 180 | exec(); | ||
149 | } | 181 | } | ||
150 | 182 | | |||
151 | }; | 183 | }; | ||
152 | 184 | | |||
153 | Startup::Startup(KSMServer *parent): | 185 | Startup::Startup(KSMServer *parent): | ||
154 | QObject(parent), | 186 | QObject(parent), | ||
155 | ksmserver(parent), | 187 | ksmserver(parent) | ||
156 | state(Waiting) | | |||
157 | { | 188 | { | ||
158 | connect(ksmserver, &KSMServer::windowManagerLoaded, this, &Startup::autoStart0); | 189 | auto phase0 = new StartupPhase0(this); | ||
190 | auto phase1 = new StartupPhase1(this); | ||||
191 | auto phase2 = new StartupPhase2(this); | ||||
192 | auto restoreSession = new RestoreSessionJob(ksmserver); | ||||
193 | | ||||
194 | connect(ksmserver, &KSMServer::windowManagerLoaded, phase0, &KJob::start); | ||||
195 | connect(phase0, &KJob::finished, phase1, &KJob::start); | ||||
196 | | ||||
197 | connect(phase1, &KJob::finished, this, [=]() { | ||||
198 | 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… | |||||
199 | }); | ||||
200 | | ||||
201 | if (ksmserver->defaultSession()) { | ||||
202 | connect(phase1, &KJob::finished, phase2, &KJob::start); | ||||
203 | } else { | ||||
204 | connect(phase1, &KJob::finished, restoreSession, &KJob::start); | ||||
205 | connect(restoreSession, &KJob::finished, phase2, &KJob::start); | ||||
206 | } | ||||
207 | connect(phase1, &KJob::finished, this, []() { | ||||
208 | NotificationThread *loginSound = new NotificationThread(); | ||||
209 | connect(loginSound, &NotificationThread::finished, loginSound, &NotificationThread::deleteLater); | ||||
210 | loginSound->start();}); | ||||
broulik: Coding style, `});` on new line | |||||
211 | connect(phase2, &KJob::finished, this, &Startup::finishStartup); | ||||
159 | } | 212 | } | ||
160 | 213 | | |||
161 | void Startup::autoStart0() | 214 | void Startup::upAndRunning( const QString& msg ) | ||
anthonyfieroni: It's unused, can we remove it? | |||||
davidedmundson: it's called from main | |||||
162 | { | 215 | { | ||
163 | disconnect(ksmserver, &KSMServer::windowManagerLoaded, this, &Startup::autoStart0); | 216 | QDBusMessage ksplashProgressMessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KSplash"), | ||
164 | state = AutoStart0; | 217 | QStringLiteral("/KSplash"), | ||
165 | #ifdef KSMSERVER_STARTUP_DEBUG1 | 218 | QStringLiteral("org.kde.KSplash"), | ||
166 | qCDebug(KSMSERVER) << t.elapsed(); | 219 | QStringLiteral("setStage")); | ||
167 | #endif | 220 | ksplashProgressMessage.setArguments(QList<QVariant>() << msg); | ||
221 | QDBusConnection::sessionBus().asyncCall(ksplashProgressMessage); | ||||
222 | } | ||||
168 | 223 | | |||
169 | autoStart(0); | 224 | void Startup::finishStartup() | ||
225 | { | ||||
226 | qCDebug(KSMSERVER) << "Finished"; | ||||
227 | ksmserver->state = KSMServer::Idle; | ||||
228 | ksmserver->setupXIOErrorHandler(); | ||||
229 | upAndRunning(QStringLiteral("ready")); | ||||
170 | } | 230 | } | ||
171 | 231 | | |||
172 | void Startup::autoStart0Done() | 232 | KCMInitJob::KCMInitJob(int phase) | ||
233 | :m_phase(phase) | ||||
173 | { | 234 | { | ||
174 | if( state != AutoStart0 ) | 235 | } | ||
175 | return; | | |||
176 | qCDebug(KSMSERVER) << "Autostart 0 done"; | | |||
177 | #ifdef KSMSERVER_STARTUP_DEBUG1 | | |||
178 | qCDebug(KSMSERVER) << t.elapsed(); | | |||
179 | #endif | | |||
180 | 236 | | |||
181 | state = KcmInitPhase1; | 237 | void KCMInitJob::start() { | ||
182 | kcminitSignals = new QDBusInterface( QStringLiteral( "org.kde.kcminit"), | 238 | //FIXME - replace all this with just a DBus call with a timeout and make kcminit delay the reply till it's done | ||
239 | | ||||
240 | auto kcminitSignals = new QDBusInterface( QStringLiteral( "org.kde.kcminit"), | ||||
183 | QStringLiteral( "/kcminit" ), | 241 | QStringLiteral( "/kcminit" ), | ||
184 | QStringLiteral( "org.kde.KCMInit" ), | 242 | QStringLiteral( "org.kde.KCMInit" ), | ||
185 | QDBusConnection::sessionBus(), this ); | 243 | QDBusConnection::sessionBus(), this ); | ||
186 | if( !kcminitSignals->isValid()) { | 244 | 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."; | 245 | 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; | 246 | QTimer::singleShot(0, this, &KCMInitJob::done); | ||
189 | kcminitSignals = nullptr; | | |||
190 | QTimer::singleShot(0, this, &Startup::kcmPhase1Done); | | |||
191 | return; | 247 | return; | ||
192 | } | 248 | } | ||
193 | connect( kcminitSignals, SIGNAL(phase1Done()), SLOT(kcmPhase1Done())); | 249 | if (m_phase == 1) { | ||
194 | QTimer::singleShot( 10000, this, &Startup::kcmPhase1Timeout); // protection | 250 | connect( kcminitSignals, SIGNAL(phase1Done()), this, SLOT(done())); | ||
251 | } else { | ||||
252 | connect( kcminitSignals, SIGNAL(phase2Done()), this, SLOT(done())); | ||||
253 | } | ||||
254 | QTimer::singleShot( 10000, this, &KCMInitJob::done); // protection | ||||
195 | 255 | | |||
196 | org::kde::KCMInit kcminit(QStringLiteral("org.kde.kcminit"), | 256 | org::kde::KCMInit kcminit(QStringLiteral("org.kde.kcminit"), | ||
197 | QStringLiteral("/kcminit"), | 257 | QStringLiteral("/kcminit"), | ||
198 | QDBusConnection::sessionBus()); | 258 | QDBusConnection::sessionBus()); | ||
199 | kcminit.runPhase1(); | | |||
200 | } | | |||
201 | 259 | | |||
202 | void Startup::kcmPhase1Done() | 260 | if (m_phase == 1) { | ||
203 | { | 261 | kcminit.runPhase1(); | ||
204 | if( state != KcmInitPhase1 ) | 262 | } else { | ||
205 | return; | 263 | kcminit.runPhase2(); | ||
206 | qCDebug(KSMSERVER) << "Kcminit phase 1 done"; | | |||
207 | if (kcminitSignals) { | | |||
208 | disconnect( kcminitSignals, SIGNAL(phase1Done()), this, SLOT(kcmPhase1Done())); | | |||
209 | } | 264 | } | ||
210 | autoStart1(); | | |||
211 | } | 265 | } | ||
212 | 266 | | |||
213 | void Startup::kcmPhase1Timeout() | 267 | void KCMInitJob::done() | ||
214 | { | 268 | { | ||
215 | if( state != KcmInitPhase1 ) | 269 | emitResult(); | ||
216 | return; | | |||
217 | qCDebug(KSMSERVER) << "Kcminit phase 1 timeout"; | | |||
218 | kcmPhase1Done(); | | |||
219 | } | 270 | } | ||
220 | 271 | | |||
221 | void Startup::autoStart1() | 272 | KDEDInitJob::KDEDInitJob() | ||
222 | { | 273 | { | ||
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 | } | 274 | } | ||
231 | 275 | | |||
232 | void Startup::autoStart1Done() | 276 | void KDEDInitJob::start() { | ||
233 | { | 277 | qCDebug(KSMSERVER()); | ||
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 | } | | |||
251 | | ||||
252 | void Startup::autoStart2() | | |||
253 | { | | |||
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" ), | 278 | QDBusInterface kded( QStringLiteral( "org.kde.kded5" ), | ||
267 | QStringLiteral( "/kded" ), | 279 | QStringLiteral( "/kded" ), | ||
268 | QStringLiteral( "org.kde.kded5" ) ); | 280 | QStringLiteral( "org.kde.kded5" ) ); | ||
269 | auto pending = kded.asyncCall( QStringLiteral( "loadSecondPhase" ) ); | 281 | auto pending = kded.asyncCall( QStringLiteral( "loadSecondPhase" ) ); | ||
270 | 282 | | |||
271 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending, this); | 283 | QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pending, this); | ||
272 | QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, &Startup::secondKDEDPhaseLoaded); | 284 | 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); | 285 | 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 | } | | |||
286 | } | 286 | } | ||
287 | 287 | | |||
288 | void Startup::secondKDEDPhaseLoaded() | 288 | RestoreSessionJob::RestoreSessionJob(KSMServer *server): KJob(), | ||
289 | { | 289 | m_ksmserver(server) | ||
290 | 290 | {} | |||
291 | #ifdef KSMSERVER_STARTUP_DEBUG1 | | |||
292 | qCDebug(KSMSERVER)<< "kded" << t.elapsed(); | | |||
293 | #endif | | |||
294 | | ||||
295 | if( !ksmserver->defaultSession()) | | |||
296 | ksmserver->restoreLegacySession(KSharedConfig::openConfig().data()); | | |||
297 | 291 | | |||
298 | qCDebug(KSMSERVER) << "Starting notification thread"; | 292 | void RestoreSessionJob::start() | ||
299 | NotificationThread *loginSound = new NotificationThread(); | 293 | { | ||
300 | // Delete the thread when finished | 294 | m_ksmserver->lastAppStarted = 0; | ||
301 | connect(loginSound, &NotificationThread::finished, loginSound, &NotificationThread::deleteLater); | 295 | m_ksmserver->lastIdStarted.clear(); | ||
302 | loginSound->start(); | 296 | m_ksmserver->state = KSMServer::Restoring; | ||
297 | connect(m_ksmserver, &KSMServer::sessionRestored, this, [this]() {emitResult();}); | ||||
298 | m_ksmserver->tryRestoreNext(); | ||||
303 | } | 299 | } | ||
304 | 300 | | |||
305 | void Startup::runUserAutostart() | 301 | void StartupPhase2::runUserAutostart() | ||
306 | { | 302 | { | ||
307 | // Now let's execute the scripts in the KDE-specific autostart-scripts folder. | 303 | // 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"); | 304 | const QString autostartFolder = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QDir::separator() + QStringLiteral("autostart-scripts"); | ||
309 | 305 | | |||
310 | QDir dir(autostartFolder); | 306 | QDir dir(autostartFolder); | ||
311 | if (!dir.exists()) { | 307 | if (!dir.exists()) { | ||
312 | // Create dir in all cases, so that users can find it :-) | 308 | // Create dir in all cases, so that users can find it :-) | ||
313 | dir.mkpath(QStringLiteral(".")); | 309 | dir.mkpath(QStringLiteral(".")); | ||
Show All 19 Lines | 322 | { | |||
333 | connect(p, static_cast<void (QProcess::*)(int)>(&QProcess::finished), [p](int exitCode) { | 329 | connect(p, static_cast<void (QProcess::*)(int)>(&QProcess::finished), [p](int exitCode) { | ||
334 | qCInfo(KSMSERVER) << "autostart script" << p->program() << "finished with exit code " << exitCode; | 330 | qCInfo(KSMSERVER) << "autostart script" << p->program() << "finished with exit code " << exitCode; | ||
335 | p->deleteLater(); | 331 | p->deleteLater(); | ||
336 | }); | 332 | }); | ||
337 | } | 333 | } | ||
338 | } | 334 | } | ||
339 | } | 335 | } | ||
340 | 336 | | |||
341 | bool Startup::migrateKDE4Autostart(const QString &autostartFolder) | 337 | bool StartupPhase2::migrateKDE4Autostart(const QString &autostartFolder) | ||
342 | { | 338 | { | ||
343 | // Migrate user autostart from kde4 | 339 | // Migrate user autostart from kde4 | ||
344 | Kdelibs4Migration migration; | 340 | Kdelibs4Migration migration; | ||
345 | if (!migration.kdeHomeFound()) { | 341 | if (!migration.kdeHomeFound()) { | ||
346 | return false; | 342 | return false; | ||
347 | } | 343 | } | ||
348 | // KDEHOME/Autostart was the default value for KGlobalSettings::autostart() | 344 | // KDEHOME/Autostart was the default value for KGlobalSettings::autostart() | ||
349 | QString oldAutostart = migration.kdeHome() + QStringLiteral("/Autostart"); | 345 | QString oldAutostart = migration.kdeHome() + QStringLiteral("/Autostart"); | ||
Show All 19 Lines | 355 | foreach (const QString &file, entries) { | |||
369 | } | 365 | } | ||
370 | if (!success) { | 366 | if (!success) { | ||
371 | qCWarning(KSMSERVER) << "Error copying" << src << "to" << dest; | 367 | qCWarning(KSMSERVER) << "Error copying" << src << "to" << dest; | ||
372 | } | 368 | } | ||
373 | } | 369 | } | ||
374 | return true; | 370 | return true; | ||
375 | } | 371 | } | ||
376 | 372 | | |||
377 | void Startup::autoStart2Done() | 373 | AutoStartAppsJob::AutoStartAppsJob(int phase) | ||
378 | { | 374 | { | ||
379 | if( state != FinishingStartup ) | 375 | 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); | 376 | m_autoStart.setPhase(phase); | ||
441 | if (phase == 0) { | | |||
442 | m_autoStart.loadAutoStartList(); | | |||
443 | } | | |||
444 | QTimer::singleShot(0, this, &Startup::slotAutoStart); | | |||
445 | } | 377 | } | ||
446 | 378 | | |||
447 | void Startup::slotAutoStart() | 379 | void AutoStartAppsJob::start() { | ||
448 | { | 380 | qCDebug(KSMSERVER()); | ||
381 | | ||||
382 | QTimer::singleShot(0, this, [=]() { | ||||
449 | do { | 383 | do { | ||
450 | QString serviceName = m_autoStart.startService(); | 384 | QString serviceName = m_autoStart.startService(); | ||
451 | if (serviceName.isEmpty()) { | 385 | if (serviceName.isEmpty()) { | ||
452 | // Done | 386 | // Done | ||
453 | if (!m_autoStart.phaseDone()) { | 387 | if (!m_autoStart.phaseDone()) { | ||
454 | m_autoStart.setPhaseDone(); | 388 | 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 | } | 389 | } | ||
390 | emitResult(); | ||||
467 | return; | 391 | return; | ||
468 | } | 392 | } | ||
469 | KService service(serviceName); | 393 | KService service(serviceName); | ||
470 | auto arguments = KIO::DesktopExecParser(service, QList<QUrl>()).resultingArguments(); | 394 | auto arguments = KIO::DesktopExecParser(service, QList<QUrl>()).resultingArguments(); | ||
471 | if (arguments.isEmpty()) { | 395 | if (arguments.isEmpty()) { | ||
472 | qCWarning(KSMSERVER) << "failed to parse" << serviceName << "for autostart"; | 396 | qCWarning(KSMSERVER) << "failed to parse" << serviceName << "for autostart"; | ||
473 | continue; | 397 | continue; | ||
474 | } | 398 | } | ||
475 | qCInfo(KSMSERVER) << "Starting autostart service " << serviceName << arguments; | 399 | qCInfo(KSMSERVER) << "Starting autostart service " << serviceName << arguments; | ||
476 | auto program = arguments.takeFirst(); | 400 | auto program = arguments.takeFirst(); | ||
477 | if (!QProcess::startDetached(program, arguments)) | 401 | if (!QProcess::startDetached(program, arguments)) | ||
478 | qCWarning(KSMSERVER) << "could not start" << serviceName << ":" << program << arguments; | 402 | qCWarning(KSMSERVER) << "could not start" << serviceName << ":" << program << arguments; | ||
479 | } while (true); | 403 | } while (true); | ||
480 | // Loop till we find a service that we can start. | 404 | }); | ||
481 | } | 405 | } | ||
482 | 406 | | |||
483 | #include "startup.moc" | 407 | #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)?