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>
35 #include <config-workspace.h>
36 #include <config-unix.h> // HAVE_LIMITS_H
39 #include <sys/types.h>
40 #include <sys/param.h>
42 #ifdef HAVE_SYS_TIME_H
45 #include <sys/socket.h>
60 #include <QPushButton>
62 #include <QtDBus/QtDBus>
67 #include <kstandarddirs.h>
68 #include <kapplication.h>
69 #include <ktemporaryfile.h>
70 #include <knotification.h>
71 #include <kconfiggroup.h>
81 //#include "kdesktop_interface.h"
82 #include "klauncher_interface.h"
83 #include "kcminit_interface.h"
85 /*! Restores the previous session. Ensures the window manager is
86 running (if specified).
88 void KSMServer::restoreSession( const QString
&sessionName
)
94 kDebug( 1218 ) << "KSMServer::restoreSession " << sessionName
;
95 KSharedConfig::Ptr config
= KGlobal::config();
97 sessionGroup
= "Session: " + sessionName
;
98 KConfigGroup
configSessionGroup( config
, sessionGroup
);
100 int count
= configSessionGroup
.readEntry( "count", 0 );
102 publishProgress( appsToStart
, true );
103 upAndRunning( "ksmserver" );
104 connect( klauncherSignals
, SIGNAL( autoStart0Done()), SLOT( autoStart0Done()));
105 connect( klauncherSignals
, SIGNAL( autoStart1Done()), SLOT( autoStart1Done()));
106 connect( klauncherSignals
, SIGNAL( autoStart2Done()), SLOT( autoStart2Done()));
108 // find all commands to launch the wm in the session
109 QList
<QStringList
> wmStartCommands
;
110 if ( !wm
.isEmpty() ) {
111 for ( int i
= 1; i
<= count
; i
++ ) {
112 QString n
= QString::number(i
);
113 if ( wm
== configSessionGroup
.readEntry( QString("program")+n
, QString() ) ) {
114 wmStartCommands
<< configSessionGroup
.readEntry( QString("restartCommand")+n
, QStringList() );
118 if( wmStartCommands
.isEmpty()) // otherwise use the configured default
119 wmStartCommands
<< wmCommands
;
121 launchWM( wmStartCommands
);
125 Starts the default session.
127 Currently, that's the window manager only (if specified).
129 void KSMServer::startDefaultSession()
135 publishProgress( 0, true );
136 upAndRunning( "ksmserver" );
137 connect( klauncherSignals
, SIGNAL( autoStart0Done()), SLOT( autoStart0Done()));
138 connect( klauncherSignals
, SIGNAL( autoStart1Done()), SLOT( autoStart1Done()));
139 connect( klauncherSignals
, SIGNAL( autoStart2Done()), SLOT( autoStart2Done()));
141 launchWM( QList
< QStringList
>() << wmCommands
);
144 void KSMServer::launchWM( const QList
< QStringList
>& wmStartCommands
)
146 assert( state
== LaunchingWM
);
148 // when we have a window manager, we start it first and give
149 // it some time before launching other processes. Results in a
150 // visually more appealing startup.
151 wmProcess
= startApplication( wmStartCommands
[ 0 ] );
152 connect( wmProcess
, SIGNAL( error( QProcess::ProcessError
)), SLOT( wmProcessChange()));
153 connect( wmProcess
, SIGNAL( finished( int, QProcess::ExitStatus
)), SLOT( wmProcessChange()));
154 // there can be possibly more wm's (because of forking for multihead),
155 // but in such case care only about the process of the first one
156 for (int i
= 1; i
< wmStartCommands
.count(); i
++)
157 startApplication( wmStartCommands
[i
] );
158 QTimer::singleShot( 4000, this, SLOT( autoStart0() ) );
161 void KSMServer::clientSetProgram( KSMClient
* client
)
163 if( client
->program() == wm
)
167 void KSMServer::wmProcessChange()
169 if( state
!= LaunchingWM
)
170 { // don't care about the process when not in the wm-launching state anymore
174 if( wmProcess
->state() == QProcess::NotRunning
)
175 { // wm failed to launch for some reason, go with kwin instead
176 kWarning( 1218 ) << "Window manager" << wm
<< "failed to launch";
178 return; // uhoh, kwin itself failed
179 kDebug( 1218 ) << "Launching KWin";
181 wmCommands
= ( QStringList() << "kwin" );
183 launchWM( QList
< QStringList
>() << wmCommands
);
188 void KSMServer::autoStart0()
190 if( state
!= LaunchingWM
)
192 if( !checkStartupSuspend())
195 org::kde::KLauncher
klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
196 klauncher
.autoStart((int)0);
199 void KSMServer::autoStart0Done()
201 if( state
!= AutoStart0
)
203 disconnect( klauncherSignals
, SIGNAL( autoStart0Done()), this, SLOT( autoStart0Done()));
204 if( !checkStartupSuspend())
206 kDebug( 1218 ) << "Autostart 0 done";
207 upAndRunning( "desktop" );
208 kcminitSignals
= new QDBusInterface("org.kde.kcminit", "/kcminit", "org.kde.KCMInit", QDBusConnection::sessionBus(), this );
209 if( !kcminitSignals
->isValid())
210 kWarning() << "kcminit not running?" ;
211 connect( kcminitSignals
, SIGNAL( phase1Done()), SLOT( kcmPhase1Done()));
212 state
= KcmInitPhase1
;
213 QTimer::singleShot( 10000, this, SLOT( kcmPhase1Timeout())); // protection
215 org::kde::KCMInit
kcminit("org.kde.kcminit", "/kcminit" , QDBusConnection::sessionBus());
219 void KSMServer::kcmPhase1Done()
221 if( state
!= KcmInitPhase1
)
223 kDebug( 1218 ) << "Kcminit phase 1 done";
224 disconnect( kcminitSignals
, SIGNAL( phase1Done()), this, SLOT( kcmPhase1Done()));
228 void KSMServer::kcmPhase1Timeout()
230 if( state
!= KcmInitPhase1
)
232 kDebug( 1218 ) << "Kcminit phase 1 timeout";
236 void KSMServer::autoStart1()
238 if( state
!= KcmInitPhase1
)
241 org::kde::KLauncher
klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
242 klauncher
.autoStart((int)1);
245 void KSMServer::autoStart1Done()
247 if( state
!= AutoStart1
)
249 disconnect( klauncherSignals
, SIGNAL( autoStart1Done()), this, SLOT( autoStart1Done()));
250 if( !checkStartupSuspend())
252 kDebug( 1218 ) << "Autostart 1 done";
254 lastIdStarted
.clear();
256 if( defaultSession()) {
263 void KSMServer::clientRegistered( const char* previousId
)
265 if ( previousId
&& lastIdStarted
== previousId
)
269 void KSMServer::tryRestoreNext()
271 if( state
!= Restoring
)
274 startupSuspendTimeoutTimer
.stop();
275 KConfigGroup
config(KGlobal::config(), sessionGroup
);
277 while ( lastAppStarted
< appsToStart
) {
278 publishProgress ( appsToStart
- lastAppStarted
);
280 QString n
= QString::number(lastAppStarted
);
281 QStringList restartCommand
= config
.readEntry( QString("restartCommand")+n
, QStringList() );
282 if ( restartCommand
.isEmpty() ||
283 (config
.readEntry( QString("restartStyleHint")+n
, 0 ) == SmRestartNever
)) {
286 if ( wm
== config
.readEntry( QString("program")+n
, QString() ) )
287 continue; // wm already started
288 if( config
.readEntry( QString( "wasWm" )+n
, false ))
289 continue; // it was wm before, but not now, don't run it (some have --replace in command :( )
290 startApplication( restartCommand
,
291 config
.readEntry( QString("clientMachine")+n
, QString() ),
292 config
.readEntry( QString("userId")+n
, QString() ));
293 lastIdStarted
= config
.readEntry( QString("clientId")+n
, QString() );
294 if ( !lastIdStarted
.isEmpty() ) {
295 restoreTimer
.setSingleShot( true );
296 restoreTimer
.start( 2000 );
297 return; // we get called again from the clientRegistered handler
302 lastIdStarted
.clear();
303 publishProgress( 0 );
308 void KSMServer::autoStart2()
310 if( state
!= Restoring
)
312 if( !checkStartupSuspend())
314 state
= FinishingStartup
;
315 waitAutoStart2
= true;
317 org::kde::KLauncher
klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
318 klauncher
.autoStart((int)2);
320 QDBusInterface
kded( "org.kde.kded", "/kded", "org.kde.kded" );
321 kded
.call( "loadSecondPhase" );
325 connect( kcminitSignals
, SIGNAL( phase2Done()), SLOT( kcmPhase2Done()));
326 QTimer::singleShot( 10000, this, SLOT( kcmPhase2Timeout())); // protection
327 org::kde::KCMInit
kcminit("org.kde.kcminit", "/kcminit" , QDBusConnection::sessionBus());
329 if( !defaultSession())
330 restoreLegacySession(KGlobal::config().data());
331 KNotification::event( "startkde" , QString() , QPixmap() , 0l , KNotification::DefaultEvent
); // this is the time KDE is up, more or less
334 void KSMServer::runUserAutostart()
336 // now let's execute all the stuff in the autostart folder.
337 // the stuff will actually be really executed when the event loop is
338 // entered, since KRun internally uses a QTimer
339 QDir
dir( KGlobalSettings::autostartPath() );
341 const QStringList entries
= dir
.entryList( QDir::Files
);
342 foreach (const QString
& file
, entries
) {
343 // Don't execute backup files
344 if ( !file
.endsWith('~') && !file
.endsWith(".bak") &&
345 ( file
[0] != '%' || !file
.endsWith('%') ) &&
346 ( file
[0] != '#' || !file
.endsWith('#') ) )
348 KUrl
url( dir
.absolutePath() + '/' + file
);
349 (void) new KRun( url
, 0, true );
353 // Create dir so that users can find it :-)
354 dir
.mkpath( KGlobalSettings::autostartPath() );
358 void KSMServer::autoStart2Done()
360 if( state
!= FinishingStartup
)
362 disconnect( klauncherSignals
, SIGNAL( autoStart2Done()), this, SLOT( autoStart2Done()));
363 kDebug( 1218 ) << "Autostart 2 done";
364 waitAutoStart2
= false;
368 void KSMServer::kcmPhase2Done()
370 if( state
!= FinishingStartup
)
372 kDebug( 1218 ) << "Kcminit phase 2 done";
373 disconnect( kcminitSignals
, SIGNAL( phase2Done()), this, SLOT( kcmPhase2Done()));
374 delete kcminitSignals
;
375 kcminitSignals
= NULL
;
376 waitKcmInit2
= false;
380 void KSMServer::kcmPhase2Timeout()
384 kDebug( 1218 ) << "Kcminit phase 2 timeout";
388 void KSMServer::finishStartup()
390 if( state
!= FinishingStartup
)
392 if( waitAutoStart2
|| waitKcmInit2
)
395 upAndRunning( "ready" );
398 setupXIOErrorHandler(); // From now on handle X errors as normal shutdown.
401 bool KSMServer::checkStartupSuspend()
403 if( startupSuspendCount
.isEmpty())
405 // wait for the phase to finish
406 if( !startupSuspendTimeoutTimer
.isActive())
408 startupSuspendTimeoutTimer
.setSingleShot( true );
409 startupSuspendTimeoutTimer
.start( 10000 );
414 void KSMServer::suspendStartup( const QString
&app
)
416 if( !startupSuspendCount
.contains( app
))
417 startupSuspendCount
[ app
] = 0;
418 ++startupSuspendCount
[ app
];
421 void KSMServer::resumeStartup( const QString
&app
)
423 if( !startupSuspendCount
.contains( app
))
425 if( --startupSuspendCount
[ app
] == 0 ) {
426 startupSuspendCount
.remove( app
);
427 if( startupSuspendCount
.isEmpty() && startupSuspendTimeoutTimer
.isActive()) {
428 startupSuspendTimeoutTimer
.stop();
429 resumeStartupInternal();
434 void KSMServer::startupSuspendTimeout()
436 kDebug( 1218 ) << "Startup suspend timeout:" << state
;
437 resumeStartupInternal();
440 void KSMServer::resumeStartupInternal()
442 startupSuspendCount
.clear();
457 kWarning( 1218 ) << "Unknown resume startup state" ;
462 void KSMServer::publishProgress( int progress
, bool max
)
464 Q_UNUSED( progress
);
466 // KSplash now goes away before restoring of session-saved apps starts, in order
467 // to make the startup visually faster, so this is not needed now. Also, there's
468 // no DBUS interface anymore.
470 org::kde::KSplash
ksplash("org.kde.ksplash", "/KSplash", QDBusConnection::sessionBus());
472 ksplash
.setMaxProgress(progress
);
474 ksplash
.setProgress(progress
);
479 void KSMServer::upAndRunning( const QString
& msg
)
482 e
.xclient
.type
= ClientMessage
;
483 e
.xclient
.message_type
= XInternAtom( QX11Info::display(), "_KDE_SPLASH_PROGRESS", False
);
484 e
.xclient
.display
= QX11Info::display();
485 e
.xclient
.window
= QX11Info::appRootWindow();
486 e
.xclient
.format
= 8;
487 assert( strlen( msg
.toLatin1()) < 20 );
488 strcpy( e
.xclient
.data
.b
, msg
.toLatin1());
489 XSendEvent( QX11Info::display(), QX11Info::appRootWindow(), False
, SubstructureNotifyMask
, &e
);