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>
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
)
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 );
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() ) );
133 Starts the default session.
135 Currently, that's the window manager only (if specified).
137 void KSMServer::startDefaultSession()
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
)
159 void KSMServer::autoStart0()
161 if( state
!= LaunchingWM
)
163 if( !checkStartupSuspend())
166 org::kde::KLauncher
klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
167 klauncher
.autoStart((int)0);
170 void KSMServer::autoStart0Done()
172 if( state
!= AutoStart0
)
174 disconnect( klauncherSignals
, SIGNAL( autoStart0Done()), this, SLOT( autoStart0Done()));
175 if( !checkStartupSuspend())
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());
190 void KSMServer::kcmPhase1Done()
192 if( state
!= KcmInitPhase1
)
194 kDebug( 1218 ) << "Kcminit phase 1 done";
195 disconnect( kcminitSignals
, SIGNAL( phase1Done()), this, SLOT( kcmPhase1Done()));
199 void KSMServer::kcmPhase1Timeout()
201 if( state
!= KcmInitPhase1
)
203 kDebug( 1218 ) << "Kcminit phase 1 timeout";
207 void KSMServer::autoStart1()
209 if( state
!= KcmInitPhase1
)
212 org::kde::KLauncher
klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
213 klauncher
.autoStart((int)1);
216 void KSMServer::autoStart1Done()
218 if( state
!= AutoStart1
)
220 disconnect( klauncherSignals
, SIGNAL( autoStart1Done()), this, SLOT( autoStart1Done()));
221 if( !checkStartupSuspend())
223 kDebug( 1218 ) << "Autostart 1 done";
225 lastIdStarted
.clear();
227 if( defaultSession()) {
234 void KSMServer::clientRegistered( const char* previousId
)
236 if ( previousId
&& lastIdStarted
== previousId
)
240 void KSMServer::tryRestoreNext()
242 if( state
!= Restoring
)
245 startupSuspendTimeoutTimer
.stop();
246 KConfigGroup
config(KGlobal::config(), sessionGroup
);
248 while ( lastAppStarted
< appsToStart
) {
249 publishProgress ( appsToStart
- 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
)) {
257 if ( wm
== config
.readEntry( QString("program")+n
, QString() ) )
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
271 lastIdStarted
.clear();
272 publishProgress( 0 );
277 void KSMServer::autoStart2()
279 if( state
!= Restoring
)
281 if( !checkStartupSuspend())
283 state
= FinishingStartup
;
284 waitAutoStart2
= 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" );
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());
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() );
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 );
322 // Create dir so that users can find it :-)
323 dir
.mkpath( KGlobalSettings::autostartPath() );
327 void KSMServer::autoStart2Done()
329 if( state
!= FinishingStartup
)
331 disconnect( klauncherSignals
, SIGNAL( autoStart2Done()), this, SLOT( autoStart2Done()));
332 kDebug( 1218 ) << "Autostart 2 done";
333 waitAutoStart2
= false;
337 void KSMServer::kcmPhase2Done()
339 if( state
!= FinishingStartup
)
341 kDebug( 1218 ) << "Kcminit phase 2 done";
342 disconnect( kcminitSignals
, SIGNAL( phase2Done()), this, SLOT( kcmPhase2Done()));
343 delete kcminitSignals
;
344 kcminitSignals
= NULL
;
345 waitKcmInit2
= false;
349 void KSMServer::kcmPhase2Timeout()
353 kDebug( 1218 ) << "Kcminit phase 2 timeout";
357 void KSMServer::finishStartup()
359 if( state
!= FinishingStartup
)
361 if( waitAutoStart2
|| waitKcmInit2
)
364 upAndRunning( "ready" );
367 setupXIOErrorHandler(); // From now on handle X errors as normal shutdown.
370 bool KSMServer::checkStartupSuspend()
372 if( startupSuspendCount
.isEmpty())
374 // wait for the phase to finish
375 if( !startupSuspendTimeoutTimer
.isActive())
377 startupSuspendTimeoutTimer
.setSingleShot( true );
378 startupSuspendTimeoutTimer
.start( 10000 );
383 void KSMServer::suspendStartup( const QString
&app
)
385 #if KDE_IS_VERSION( 3, 90, 0 )
387 #warning Re-enable suspend/resume startup and check it works properly.
391 if( !startupSuspendCount
.contains( app
))
392 startupSuspendCount
[ app
] = 0;
393 ++startupSuspendCount
[ app
];
396 void KSMServer::resumeStartup( const QString
&app
)
399 if( !startupSuspendCount
.contains( app
))
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();
433 kWarning( 1218 ) << "Unknown resume startup state" ;
438 void KSMServer::publishProgress( int progress
, bool max
)
440 Q_UNUSED( progress
);
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.
446 org::kde::KSplash
ksplash("org.kde.ksplash", "/KSplash", QDBusConnection::sessionBus());
448 ksplash
.setMaxProgress(progress
);
450 ksplash
.setProgress(progress
);
455 void KSMServer::upAndRunning( const QString
& msg
)
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
);