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_iface.h>
83
#include "kcminit_interface.h"
85
//#define KSMSERVER_STARTUP_DEBUG1
87
#ifdef KSMSERVER_STARTUP_DEBUG1
91
/*! Restores the previous session. Ensures the window manager is
92
running (if specified).
94
void KSMServer::restoreSession( const QString &sessionName )
98
#ifdef KSMSERVER_STARTUP_DEBUG1
103
kDebug( 1218 ) << "KSMServer::restoreSession " << sessionName;
104
KSharedConfig::Ptr config = KGlobal::config();
106
sessionGroup = "Session: " + sessionName;
107
KConfigGroup configSessionGroup( config, sessionGroup);
109
int count = configSessionGroup.readEntry( "count", 0 );
111
upAndRunning( "ksmserver" );
112
connect( klauncherSignals, SIGNAL( autoStart0Done()), SLOT( autoStart0Done()));
113
connect( klauncherSignals, SIGNAL( autoStart1Done()), SLOT( autoStart1Done()));
114
connect( klauncherSignals, SIGNAL( autoStart2Done()), SLOT( autoStart2Done()));
116
// find all commands to launch the wm in the session
117
QList<QStringList> wmStartCommands;
118
if ( !wm.isEmpty() ) {
119
for ( int i = 1; i <= count; i++ ) {
120
QString n = QString::number(i);
121
if ( wm == configSessionGroup.readEntry( QString("program")+n, QString() ) ) {
122
wmStartCommands << configSessionGroup.readEntry( QString("restartCommand")+n, QStringList() );
126
if( wmStartCommands.isEmpty()) // otherwise use the configured default
127
wmStartCommands << wmCommands;
129
launchWM( wmStartCommands );
133
Starts the default session.
135
Currently, that's the window manager only (if specified).
137
void KSMServer::startDefaultSession()
142
#ifdef KSMSERVER_STARTUP_DEBUG1
146
upAndRunning( "ksmserver" );
147
connect( klauncherSignals, SIGNAL( autoStart0Done()), SLOT( autoStart0Done()));
148
connect( klauncherSignals, SIGNAL( autoStart1Done()), SLOT( autoStart1Done()));
149
connect( klauncherSignals, SIGNAL( autoStart2Done()), SLOT( autoStart2Done()));
151
launchWM( QList< QStringList >() << wmCommands );
154
void KSMServer::launchWM( const QList< QStringList >& wmStartCommands )
156
assert( state == LaunchingWM );
158
// when we have a window manager, we start it first and give
159
// it some time before launching other processes. Results in a
160
// visually more appealing startup.
161
wmProcess = startApplication( wmStartCommands[ 0 ], QString(), QString(), true );
162
connect( wmProcess, SIGNAL( error( QProcess::ProcessError )), SLOT( wmProcessChange()));
163
connect( wmProcess, SIGNAL( finished( int, QProcess::ExitStatus )), SLOT( wmProcessChange()));
164
QTimer::singleShot( 4000, this, SLOT( autoStart0() ) );
167
void KSMServer::clientSetProgram( KSMClient* client )
169
if( client->program() == wm )
171
#ifndef KSMSERVER_STARTUP_DEBUGl
175
if( client->program() == "gedit" && ( cnt == 0 ))
177
else if( client->program() == "konqueror" && ( cnt == 1 ))
179
else if( client->program() == "kspaceduel" && ( cnt == 2 ))
181
else if( client->program() == "gedit" && ( cnt == 3 ))
186
KMessageBox::information( NULL, "drat" );
191
void KSMServer::wmProcessChange()
193
if( state != LaunchingWM )
194
{ // don't care about the process when not in the wm-launching state anymore
198
if( wmProcess->state() == QProcess::NotRunning )
199
{ // wm failed to launch for some reason, go with kwin instead
200
kWarning( 1218 ) << "Window manager" << wm << "failed to launch";
202
return; // uhoh, kwin itself failed
203
kDebug( 1218 ) << "Launching KWin";
205
wmCommands = ( QStringList() << "kwin" );
207
launchWM( QList< QStringList >() << wmCommands );
212
void KSMServer::autoStart0()
214
if( state != LaunchingWM )
216
if( !checkStartupSuspend())
219
#ifdef KSMSERVER_STARTUP_DEBUG1
220
kDebug() << t.elapsed();
222
org::kde::KLauncher klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
223
klauncher.autoStart((int)0);
226
void KSMServer::autoStart0Done()
228
if( state != AutoStart0 )
230
disconnect( klauncherSignals, SIGNAL( autoStart0Done()), this, SLOT( autoStart0Done()));
231
if( !checkStartupSuspend())
233
kDebug( 1218 ) << "Autostart 0 done";
234
#ifdef KSMSERVER_STARTUP_DEBUG1
235
kDebug() << t.elapsed();
237
upAndRunning( "desktop" );
238
kcminitSignals = new QDBusInterface("org.kde.kcminit", "/kcminit", "org.kde.KCMInit", QDBusConnection::sessionBus(), this );
239
if( !kcminitSignals->isValid())
240
kWarning() << "kcminit not running?" ;
241
connect( kcminitSignals, SIGNAL( phase1Done()), SLOT( kcmPhase1Done()));
242
state = KcmInitPhase1;
243
QTimer::singleShot( 10000, this, SLOT( kcmPhase1Timeout())); // protection
245
org::kde::KCMInit kcminit("org.kde.kcminit", "/kcminit" , QDBusConnection::sessionBus());
249
void KSMServer::kcmPhase1Done()
251
if( state != KcmInitPhase1 )
253
kDebug( 1218 ) << "Kcminit phase 1 done";
254
disconnect( kcminitSignals, SIGNAL( phase1Done()), this, SLOT( kcmPhase1Done()));
258
void KSMServer::kcmPhase1Timeout()
260
if( state != KcmInitPhase1 )
262
kDebug( 1218 ) << "Kcminit phase 1 timeout";
266
void KSMServer::autoStart1()
268
if( state != KcmInitPhase1 )
271
#ifdef KSMSERVER_STARTUP_DEBUG1
272
kDebug() << t.elapsed();
274
org::kde::KLauncher klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
275
klauncher.autoStart((int)1);
278
void KSMServer::autoStart1Done()
280
if( state != AutoStart1 )
282
disconnect( klauncherSignals, SIGNAL( autoStart1Done()), this, SLOT( autoStart1Done()));
283
if( !checkStartupSuspend())
285
kDebug( 1218 ) << "Autostart 1 done";
286
setupShortcuts(); // done only here, because it needs kglobalaccel :-/
288
lastIdStarted.clear();
290
#ifdef KSMSERVER_STARTUP_DEBUG1
291
kDebug() << t.elapsed();
293
if( defaultSession()) {
300
void KSMServer::clientRegistered( const char* previousId )
302
if ( previousId && lastIdStarted == previousId )
306
void KSMServer::tryRestoreNext()
308
if( state != Restoring && state != RestoringSubSession )
311
startupSuspendTimeoutTimer.stop();
312
KConfigGroup config(KGlobal::config(), sessionGroup );
314
while ( lastAppStarted < appsToStart ) {
316
QString n = QString::number(lastAppStarted);
317
QString clientId = config.readEntry( QString("clientId")+n, QString() );
318
bool alreadyStarted = false;
319
foreach ( KSMClient *c, clients ) {
320
if ( c->clientId() == clientId ) {
321
alreadyStarted = true;
325
if ( alreadyStarted )
328
QStringList restartCommand = config.readEntry( QString("restartCommand")+n, QStringList() );
329
if ( restartCommand.isEmpty() ||
330
(config.readEntry( QString("restartStyleHint")+n, 0 ) == SmRestartNever)) {
333
if ( wm == config.readEntry( QString("program")+n, QString() ) )
334
continue; // wm already started
335
if( config.readEntry( QString( "wasWm" )+n, false ))
336
continue; // it was wm before, but not now, don't run it (some have --replace in command :( )
337
startApplication( restartCommand,
338
config.readEntry( QString("clientMachine")+n, QString() ),
339
config.readEntry( QString("userId")+n, QString() ));
340
lastIdStarted = clientId;
341
if ( !lastIdStarted.isEmpty() ) {
342
restoreTimer.setSingleShot( true );
343
restoreTimer.start( 2000 );
344
return; // we get called again from the clientRegistered handler
350
lastIdStarted.clear();
352
if (state == Restoring)
356
emit subSessionOpened();
360
void KSMServer::autoStart2()
362
if( state != Restoring )
364
if( !checkStartupSuspend())
366
state = FinishingStartup;
367
#ifdef KSMSERVER_STARTUP_DEBUG1
368
kDebug() << t.elapsed();
370
waitAutoStart2 = true;
372
org::kde::KLauncher klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
373
klauncher.autoStart((int)2);
375
QDBusInterface kded( "org.kde.kded", "/kded", "org.kde.kded" );
376
kded.call( "loadSecondPhase" );
380
connect( kcminitSignals, SIGNAL( phase2Done()), SLOT( kcmPhase2Done()));
381
QTimer::singleShot( 10000, this, SLOT( kcmPhase2Timeout())); // protection
382
org::kde::KCMInit kcminit("org.kde.kcminit", "/kcminit" , QDBusConnection::sessionBus());
384
if( !defaultSession())
385
restoreLegacySession(KGlobal::config().data());
386
KNotification::event( "startkde" , QString() , QPixmap() , 0l , KNotification::DefaultEvent ); // this is the time KDE is up, more or less
389
void KSMServer::runUserAutostart()
391
// now let's execute all the stuff in the autostart folder.
392
// the stuff will actually be really executed when the event loop is
393
// entered, since KRun internally uses a QTimer
394
QDir dir( KGlobalSettings::autostartPath() );
396
const QStringList entries = dir.entryList( QDir::Files );
397
foreach (const QString& file, entries) {
398
// Don't execute backup files
399
if ( !file.endsWith('~') && !file.endsWith(".bak") &&
400
( file[0] != '%' || !file.endsWith('%') ) &&
401
( file[0] != '#' || !file.endsWith('#') ) )
403
KUrl url( dir.absolutePath() + '/' + file );
404
(void) new KRun( url, 0, true );
408
// Create dir so that users can find it :-)
409
dir.mkpath( KGlobalSettings::autostartPath() );
413
void KSMServer::autoStart2Done()
415
if( state != FinishingStartup )
417
disconnect( klauncherSignals, SIGNAL( autoStart2Done()), this, SLOT( autoStart2Done()));
418
kDebug( 1218 ) << "Autostart 2 done";
419
waitAutoStart2 = false;
423
void KSMServer::kcmPhase2Done()
425
if( state != FinishingStartup )
427
kDebug( 1218 ) << "Kcminit phase 2 done";
428
disconnect( kcminitSignals, SIGNAL( phase2Done()), this, SLOT( kcmPhase2Done()));
429
delete kcminitSignals;
430
kcminitSignals = NULL;
431
waitKcmInit2 = false;
435
void KSMServer::kcmPhase2Timeout()
439
kDebug( 1218 ) << "Kcminit phase 2 timeout";
443
void KSMServer::finishStartup()
445
if( state != FinishingStartup )
447
if( waitAutoStart2 || waitKcmInit2 )
450
upAndRunning( "ready" );
451
#ifdef KSMSERVER_STARTUP_DEBUG1
452
kDebug() << t.elapsed();
456
setupXIOErrorHandler(); // From now on handle X errors as normal shutdown.
459
bool KSMServer::checkStartupSuspend()
461
if( startupSuspendCount.isEmpty())
463
// wait for the phase to finish
464
if( !startupSuspendTimeoutTimer.isActive())
466
startupSuspendTimeoutTimer.setSingleShot( true );
467
startupSuspendTimeoutTimer.start( 10000 );
472
void KSMServer::suspendStartup( const QString &app )
474
if( !startupSuspendCount.contains( app ))
475
startupSuspendCount[ app ] = 0;
476
++startupSuspendCount[ app ];
479
void KSMServer::resumeStartup( const QString &app )
481
if( !startupSuspendCount.contains( app ))
483
if( --startupSuspendCount[ app ] == 0 ) {
484
startupSuspendCount.remove( app );
485
if( startupSuspendCount.isEmpty() && startupSuspendTimeoutTimer.isActive()) {
486
startupSuspendTimeoutTimer.stop();
487
resumeStartupInternal();
492
void KSMServer::startupSuspendTimeout()
494
kDebug( 1218 ) << "Startup suspend timeout:" << state;
495
resumeStartupInternal();
498
void KSMServer::resumeStartupInternal()
500
startupSuspendCount.clear();
515
kWarning( 1218 ) << "Unknown resume startup state" ;
520
void KSMServer::upAndRunning( const QString& msg )
523
e.xclient.type = ClientMessage;
524
e.xclient.message_type = XInternAtom( QX11Info::display(), "_KDE_SPLASH_PROGRESS", False );
525
e.xclient.display = QX11Info::display();
526
e.xclient.window = QX11Info::appRootWindow();
527
e.xclient.format = 8;
528
assert( strlen( msg.toLatin1()) < 20 );
529
strcpy( e.xclient.data.b, msg.toLatin1());
530
XSendEvent( QX11Info::display(), QX11Info::appRootWindow(), False, SubstructureNotifyMask, &e );
533
void KSMServer::restoreSubSession( const QString& name )
535
sessionGroup = "SubSession: " + name;
537
KConfigGroup configSessionGroup( KGlobal::config(), sessionGroup);
538
int count = configSessionGroup.readEntry( "count", 0 );
541
lastIdStarted.clear();
543
state = RestoringSubSession;