Add (and install) svg for the new krunner interface.
[kdebase/uwolfer.git] / workspace / ksmserver / startup.cpp
blobbb16fadceb5a6db3583c60ac6ebe7afb0b3b468b
1 /*****************************************************************
2 ksmserver - the KDE session management server
4 Copyright 2000 Matthias Ettrich <ettrich@kde.org>
5 Copyright 2005 Lubos Lunak <l.lunak@kde.org>
7 relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de>
9 some code taken from the dcopserver (part of the KDE libraries), which is
10 Copyright 1999 Matthias Ettrich <ettrich@kde.org>
11 Copyright 1999 Preston Brown <pbrown@kde.org>
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
20 The above copyright notice and this permission notice shall be included in
21 all copies or substantial portions of the Software.
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
27 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 ******************************************************************/
32 #include <kglobalsettings.h>
33 #include <QDir>
34 #include <krun.h>
35 #include <config-workspace.h>
36 #include <config-unix.h> // HAVE_LIMITS_H
38 #include <pwd.h>
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/stat.h>
42 #ifdef HAVE_SYS_TIME_H
43 #include <sys/time.h>
44 #endif
45 #include <sys/socket.h>
46 #include <sys/un.h>
48 #include <unistd.h>
49 #include <stdlib.h>
50 #include <signal.h>
51 #include <time.h>
52 #include <errno.h>
53 #include <string.h>
54 #include <assert.h>
56 #ifdef HAVE_LIMITS_H
57 #include <limits.h>
58 #endif
60 #include <QPushButton>
61 #include <QTimer>
62 #include <QtDBus/QtDBus>
64 #include <klocale.h>
65 #include <kglobal.h>
66 #include <kconfig.h>
67 #include <kstandarddirs.h>
68 #include <kapplication.h>
69 #include <ktemporaryfile.h>
70 #include <knotification.h>
71 #include <kconfiggroup.h>
73 #include "global.h"
74 #include "server.h"
75 #include "client.h"
76 #include <kdebug.h>
78 #include <QX11Info>
80 //#include "kdesktop_interface.h"
81 #include "klauncher_interface.h"
82 #include "kcminit_interface.h"
84 /*! Restores the previous session. Ensures the window manager is
85 running (if specified).
87 void KSMServer::restoreSession( const QString &sessionName )
89 if( state != Idle )
90 return;
91 state = LaunchingWM;
93 kDebug( 1218 ) << "KSMServer::restoreSession " << sessionName;
94 KSharedConfig::Ptr config = KGlobal::config();
96 sessionGroup = "Session: " + sessionName;
97 KConfigGroup configSessionGroup( config, sessionGroup);
99 int count = configSessionGroup.readEntry( "count", 0 );
100 appsToStart = count;
102 QList<QStringList> wmCommands;
103 if ( !wm.isEmpty() ) {
104 for ( int i = 1; i <= count; i++ ) {
105 QString n = QString::number(i);
106 if ( wm == configSessionGroup.readEntry( QString("program")+n, QString() ) ) {
107 wmCommands << configSessionGroup.readEntry( QString("restartCommand")+n, QStringList() );
111 if ( wmCommands.isEmpty() )
112 wmCommands << ( QStringList() << wm );
114 publishProgress( appsToStart, true );
115 connect( klauncherSignals, SIGNAL( autoStart0Done()), SLOT( autoStart0Done()));
116 connect( klauncherSignals, SIGNAL( autoStart1Done()), SLOT( autoStart1Done()));
117 connect( klauncherSignals, SIGNAL( autoStart2Done()), SLOT( autoStart2Done()));
118 upAndRunning( "ksmserver" );
120 if ( !wmCommands.isEmpty() ) {
121 // when we have a window manager, we start it first and give
122 // it some time before launching other processes. Results in a
123 // visually more appealing startup.
124 for (int i = 0; i < wmCommands.count(); i++)
125 startApplication( wmCommands[i] );
126 QTimer::singleShot( 4000, this, SLOT( autoStart0() ) );
127 } else {
128 autoStart0();
133 Starts the default session.
135 Currently, that's the window manager only (if specified).
137 void KSMServer::startDefaultSession()
139 if( state != Idle )
140 return;
142 state = LaunchingWM;
143 sessionGroup = "";
144 publishProgress( 0, true );
145 upAndRunning( "ksmserver" );
146 connect( klauncherSignals, SIGNAL( autoStart0Done()), SLOT( autoStart0Done()));
147 connect( klauncherSignals, SIGNAL( autoStart1Done()), SLOT( autoStart1Done()));
148 connect( klauncherSignals, SIGNAL( autoStart2Done()), SLOT( autoStart2Done()));
149 startApplication( QStringList() << wm );
150 QTimer::singleShot( 4000, this, SLOT( autoStart0() ) );
153 void KSMServer::clientSetProgram( KSMClient* client )
155 if ( !wm.isEmpty() && client->program() == wm )
156 autoStart0();
159 void KSMServer::autoStart0()
161 if( state != LaunchingWM )
162 return;
163 if( !checkStartupSuspend())
164 return;
165 state = AutoStart0;
166 org::kde::KLauncher klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
167 klauncher.autoStart((int)0);
170 void KSMServer::autoStart0Done()
172 if( state != AutoStart0 )
173 return;
174 disconnect( klauncherSignals, SIGNAL( autoStart0Done()), this, SLOT( autoStart0Done()));
175 if( !checkStartupSuspend())
176 return;
177 kDebug( 1218 ) << "Autostart 0 done";
178 upAndRunning( "desktop" );
179 kcminitSignals = new QDBusInterface("org.kde.kcminit", "/kcminit", "org.kde.KCMInit", QDBusConnection::sessionBus(), this );
180 if( !kcminitSignals->isValid())
181 kWarning() << "kcminit not running?" ;
182 connect( kcminitSignals, SIGNAL( phase1Done()), SLOT( kcmPhase1Done()));
183 state = KcmInitPhase1;
184 QTimer::singleShot( 10000, this, SLOT( kcmPhase1Timeout())); // protection
186 org::kde::KCMInit kcminit("org.kde.kcminit", "/kcminit" , QDBusConnection::sessionBus());
187 kcminit.runPhase1();
190 void KSMServer::kcmPhase1Done()
192 if( state != KcmInitPhase1 )
193 return;
194 kDebug( 1218 ) << "Kcminit phase 1 done";
195 disconnect( kcminitSignals, SIGNAL( phase1Done()), this, SLOT( kcmPhase1Done()));
196 autoStart1();
199 void KSMServer::kcmPhase1Timeout()
201 if( state != KcmInitPhase1 )
202 return;
203 kDebug( 1218 ) << "Kcminit phase 1 timeout";
204 kcmPhase1Done();
207 void KSMServer::autoStart1()
209 if( state != KcmInitPhase1 )
210 return;
211 state = AutoStart1;
212 org::kde::KLauncher klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
213 klauncher.autoStart((int)1);
216 void KSMServer::autoStart1Done()
218 if( state != AutoStart1 )
219 return;
220 disconnect( klauncherSignals, SIGNAL( autoStart1Done()), this, SLOT( autoStart1Done()));
221 if( !checkStartupSuspend())
222 return;
223 kDebug( 1218 ) << "Autostart 1 done";
224 lastAppStarted = 0;
225 lastIdStarted.clear();
226 state = Restoring;
227 if( defaultSession()) {
228 autoStart2();
229 return;
231 tryRestoreNext();
234 void KSMServer::clientRegistered( const char* previousId )
236 if ( previousId && lastIdStarted == previousId )
237 tryRestoreNext();
240 void KSMServer::tryRestoreNext()
242 if( state != Restoring )
243 return;
244 restoreTimer.stop();
245 startupSuspendTimeoutTimer.stop();
246 KConfigGroup config(KGlobal::config(), sessionGroup );
248 while ( lastAppStarted < appsToStart ) {
249 publishProgress ( appsToStart - lastAppStarted );
250 lastAppStarted++;
251 QString n = QString::number(lastAppStarted);
252 QStringList restartCommand = config.readEntry( QString("restartCommand")+n, QStringList() );
253 if ( restartCommand.isEmpty() ||
254 (config.readEntry( QString("restartStyleHint")+n, 0 ) == SmRestartNever)) {
255 continue;
257 if ( wm == config.readEntry( QString("program")+n, QString() ) )
258 continue;
259 startApplication( restartCommand,
260 config.readEntry( QString("clientMachine")+n, QString() ),
261 config.readEntry( QString("userId")+n, QString() ));
262 lastIdStarted = config.readEntry( QString("clientId")+n, QString() );
263 if ( !lastIdStarted.isEmpty() ) {
264 restoreTimer.setSingleShot( true );
265 restoreTimer.start( 2000 );
266 return; // we get called again from the clientRegistered handler
270 appsToStart = 0;
271 lastIdStarted.clear();
272 publishProgress( 0 );
274 autoStart2();
277 void KSMServer::autoStart2()
279 if( state != Restoring )
280 return;
281 if( !checkStartupSuspend())
282 return;
283 state = FinishingStartup;
284 waitAutoStart2 = true;
285 waitKcmInit2 = true;
286 org::kde::KLauncher klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
287 klauncher.autoStart((int)2);
289 QDBusInterface kded( "org.kde.kded", "/kded", "org.kde.kded" );
290 kded.call( "loadSecondPhase" );
292 runUserAutostart();
294 connect( kcminitSignals, SIGNAL( phase2Done()), SLOT( kcmPhase2Done()));
295 QTimer::singleShot( 10000, this, SLOT( kcmPhase2Timeout())); // protection
296 org::kde::KCMInit kcminit("org.kde.kcminit", "/kcminit" , QDBusConnection::sessionBus());
297 kcminit.runPhase2();
298 if( !defaultSession())
299 restoreLegacySession(KGlobal::config().data());
300 KNotification::event( "startkde" , QString() , QPixmap() , 0l , KNotification::DefaultEvent ); // this is the time KDE is up, more or less
303 void KSMServer::runUserAutostart()
305 // now let's execute all the stuff in the autostart folder.
306 // the stuff will actually be really executed when the event loop is
307 // entered, since KRun internally uses a QTimer
308 QDir dir( KGlobalSettings::autostartPath() );
309 if (dir.exists()) {
310 const QStringList entries = dir.entryList( QDir::Files );
311 foreach (const QString& file, entries) {
312 // Don't execute backup files
313 if ( !file.endsWith('~') && !file.endsWith(".bak") &&
314 ( file[0] != '%' || !file.endsWith('%') ) &&
315 ( file[0] != '#' || !file.endsWith('#') ) )
317 KUrl url( dir.absolutePath() + '/' + file );
318 (void) new KRun( url, 0, true );
321 } else {
322 // Create dir so that users can find it :-)
323 dir.mkpath( KGlobalSettings::autostartPath() );
327 void KSMServer::autoStart2Done()
329 if( state != FinishingStartup )
330 return;
331 disconnect( klauncherSignals, SIGNAL( autoStart2Done()), this, SLOT( autoStart2Done()));
332 kDebug( 1218 ) << "Autostart 2 done";
333 waitAutoStart2 = false;
334 finishStartup();
337 void KSMServer::kcmPhase2Done()
339 if( state != FinishingStartup )
340 return;
341 kDebug( 1218 ) << "Kcminit phase 2 done";
342 disconnect( kcminitSignals, SIGNAL( phase2Done()), this, SLOT( kcmPhase2Done()));
343 delete kcminitSignals;
344 kcminitSignals = NULL;
345 waitKcmInit2 = false;
346 finishStartup();
349 void KSMServer::kcmPhase2Timeout()
351 if( !waitKcmInit2 )
352 return;
353 kDebug( 1218 ) << "Kcminit phase 2 timeout";
354 kcmPhase2Done();
357 void KSMServer::finishStartup()
359 if( state != FinishingStartup )
360 return;
361 if( waitAutoStart2 || waitKcmInit2 )
362 return;
364 upAndRunning( "ready" );
366 state = Idle;
367 setupXIOErrorHandler(); // From now on handle X errors as normal shutdown.
370 bool KSMServer::checkStartupSuspend()
372 if( startupSuspendCount.isEmpty())
373 return true;
374 // wait for the phase to finish
375 if( !startupSuspendTimeoutTimer.isActive())
377 startupSuspendTimeoutTimer.setSingleShot( true );
378 startupSuspendTimeoutTimer.start( 10000 );
380 return false;
383 void KSMServer::suspendStartup( const QString &app )
385 #if KDE_IS_VERSION( 3, 90, 0 )
386 #ifdef __GNUC__
387 #warning Re-enable suspend/resume startup and check it works properly.
388 #endif
389 #endif
390 return;
391 if( !startupSuspendCount.contains( app ))
392 startupSuspendCount[ app ] = 0;
393 ++startupSuspendCount[ app ];
396 void KSMServer::resumeStartup( const QString &app )
398 return;
399 if( !startupSuspendCount.contains( app ))
400 return;
401 if( --startupSuspendCount[ app ] == 0 ) {
402 startupSuspendCount.remove( app );
403 if( startupSuspendCount.isEmpty() && startupSuspendTimeoutTimer.isActive()) {
404 startupSuspendTimeoutTimer.stop();
405 resumeStartupInternal();
410 void KSMServer::startupSuspendTimeout()
412 kDebug( 1218 ) << "Startup suspend timeout:" << state;
413 resumeStartupInternal();
416 void KSMServer::resumeStartupInternal()
418 startupSuspendCount.clear();
419 switch( state ) {
420 case LaunchingWM:
421 autoStart0();
422 break;
423 case AutoStart0:
424 autoStart0Done();
425 break;
426 case AutoStart1:
427 autoStart1Done();
428 break;
429 case Restoring:
430 autoStart2();
431 break;
432 default:
433 kWarning( 1218 ) << "Unknown resume startup state" ;
434 break;
438 void KSMServer::publishProgress( int progress, bool max )
440 Q_UNUSED( progress );
441 Q_UNUSED( max );
442 // KSplash now goes away before restoring of session-saved apps starts, in order
443 // to make the startup visually faster, so this is not needed now. Also, there's
444 // no DBUS interface anymore.
445 #if 0
446 org::kde::KSplash ksplash("org.kde.ksplash", "/KSplash", QDBusConnection::sessionBus());
447 if(max)
448 ksplash.setMaxProgress(progress);
449 else
450 ksplash.setProgress(progress);
451 #endif
455 void KSMServer::upAndRunning( const QString& msg )
457 XEvent e;
458 e.xclient.type = ClientMessage;
459 e.xclient.message_type = XInternAtom( QX11Info::display(), "_KDE_SPLASH_PROGRESS", False );
460 e.xclient.display = QX11Info::display();
461 e.xclient.window = QX11Info::appRootWindow();
462 e.xclient.format = 8;
463 assert( strlen( msg.toLatin1()) < 20 );
464 strcpy( e.xclient.data.b, msg.toLatin1());
465 XSendEvent( QX11Info::display(), QX11Info::appRootWindow(), False, SubstructureNotifyMask, &e );