~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to ksmserver/startup.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************
 
2
ksmserver - the KDE session management server
 
3
 
 
4
Copyright 2000 Matthias Ettrich <ettrich@kde.org>
 
5
Copyright 2005 Lubos Lunak <l.lunak@kde.org>
 
6
 
 
7
relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de>
 
8
 
 
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>
 
12
 
 
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:
 
19
 
 
20
The above copyright notice and this permission notice shall be included in
 
21
all copies or substantial portions of the Software.
 
22
 
 
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.
 
29
 
 
30
******************************************************************/
 
31
 
 
32
#include <kglobalsettings.h>
 
33
#include <QDir>
 
34
#include <krun.h>
 
35
#include <config-workspace.h>
 
36
#include <config-unix.h> // HAVE_LIMITS_H
 
37
 
 
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>
 
47
 
 
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>
 
55
 
 
56
#ifdef HAVE_LIMITS_H
 
57
#include <limits.h>
 
58
#endif
 
59
 
 
60
#include <QPushButton>
 
61
#include <QTimer>
 
62
#include <QtDBus/QtDBus>
 
63
 
 
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>
 
72
#include <kprocess.h>
 
73
 
 
74
#include "global.h"
 
75
#include "server.h"
 
76
#include "client.h"
 
77
#include <kdebug.h>
 
78
 
 
79
#include <QX11Info>
 
80
 
 
81
//#include "kdesktop_interface.h"
 
82
#include <klauncher_iface.h>
 
83
#include "kcminit_interface.h"
 
84
 
 
85
//#define KSMSERVER_STARTUP_DEBUG1
 
86
 
 
87
#ifdef KSMSERVER_STARTUP_DEBUG1
 
88
static QTime t;
 
89
#endif
 
90
 
 
91
/*!  Restores the previous session. Ensures the window manager is
 
92
  running (if specified).
 
93
 */
 
94
void KSMServer::restoreSession( const QString &sessionName )
 
95
{
 
96
    if( state != Idle )
 
97
        return;
 
98
#ifdef KSMSERVER_STARTUP_DEBUG1
 
99
    t.start();
 
100
#endif
 
101
    state = LaunchingWM;
 
102
 
 
103
    kDebug( 1218 ) << "KSMServer::restoreSession " << sessionName;
 
104
    KSharedConfig::Ptr config = KGlobal::config();
 
105
 
 
106
    sessionGroup = "Session: " + sessionName;
 
107
    KConfigGroup configSessionGroup( config, sessionGroup);
 
108
 
 
109
    int count =  configSessionGroup.readEntry( "count", 0 );
 
110
    appsToStart = count;
 
111
    upAndRunning( "ksmserver" );
 
112
    connect( klauncherSignals, SIGNAL( autoStart0Done()), SLOT( autoStart0Done()));
 
113
    connect( klauncherSignals, SIGNAL( autoStart1Done()), SLOT( autoStart1Done()));
 
114
    connect( klauncherSignals, SIGNAL( autoStart2Done()), SLOT( autoStart2Done()));
 
115
 
 
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() );
 
123
            }
 
124
        }
 
125
    } 
 
126
    if( wmStartCommands.isEmpty()) // otherwise use the configured default
 
127
        wmStartCommands << wmCommands;
 
128
 
 
129
    launchWM( wmStartCommands );
 
130
}
 
131
 
 
132
/*!
 
133
  Starts the default session.
 
134
 
 
135
  Currently, that's the window manager only (if specified).
 
136
 */
 
137
void KSMServer::startDefaultSession()
 
138
{
 
139
    if( state != Idle )
 
140
        return;
 
141
    state = LaunchingWM;
 
142
#ifdef KSMSERVER_STARTUP_DEBUG1
 
143
    t.start();
 
144
#endif
 
145
    sessionGroup = "";
 
146
    upAndRunning( "ksmserver" );
 
147
    connect( klauncherSignals, SIGNAL( autoStart0Done()), SLOT( autoStart0Done()));
 
148
    connect( klauncherSignals, SIGNAL( autoStart1Done()), SLOT( autoStart1Done()));
 
149
    connect( klauncherSignals, SIGNAL( autoStart2Done()), SLOT( autoStart2Done()));
 
150
 
 
151
    launchWM( QList< QStringList >() << wmCommands );
 
152
}
 
153
 
 
154
void KSMServer::launchWM( const QList< QStringList >& wmStartCommands )
 
155
{
 
156
    assert( state == LaunchingWM );
 
157
 
 
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() ) );
 
165
}
 
166
 
 
167
void KSMServer::clientSetProgram( KSMClient* client )
 
168
{
 
169
    if( client->program() == wm )
 
170
        autoStart0();
 
171
#ifndef KSMSERVER_STARTUP_DEBUGl
 
172
    if( state == Idle )
 
173
        {
 
174
        static int cnt = 0;
 
175
        if( client->program() == "gedit" && ( cnt == 0 ))
 
176
            ++cnt;
 
177
        else if( client->program() == "konqueror" && ( cnt == 1 ))
 
178
            ++cnt;
 
179
        else if( client->program() == "kspaceduel" && ( cnt == 2 ))
 
180
            ++cnt;
 
181
        else if( client->program() == "gedit" && ( cnt == 3 ))
 
182
            ++cnt;
 
183
        else
 
184
            cnt = 0;
 
185
        if( cnt == 4 )
 
186
            KMessageBox::information( NULL, "drat" );
 
187
        }
 
188
#endif
 
189
}
 
190
 
 
191
void KSMServer::wmProcessChange()
 
192
{
 
193
    if( state != LaunchingWM )
 
194
    { // don't care about the process when not in the wm-launching state anymore
 
195
        wmProcess = NULL;
 
196
        return;
 
197
    }
 
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";
 
201
        if( wm == "kwin" )
 
202
            return; // uhoh, kwin itself failed
 
203
        kDebug( 1218 ) << "Launching KWin";
 
204
        wm = "kwin";
 
205
        wmCommands = ( QStringList() << "kwin" ); 
 
206
        // launch it
 
207
        launchWM( QList< QStringList >() << wmCommands );
 
208
        return;
 
209
    }
 
210
}
 
211
 
 
212
void KSMServer::autoStart0()
 
213
{
 
214
    if( state != LaunchingWM )
 
215
        return;
 
216
    if( !checkStartupSuspend())
 
217
        return;
 
218
    state = AutoStart0;
 
219
#ifdef KSMSERVER_STARTUP_DEBUG1
 
220
    kDebug() << t.elapsed();
 
221
#endif
 
222
    org::kde::KLauncher klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
 
223
    klauncher.autoStart((int)0);
 
224
}
 
225
 
 
226
void KSMServer::autoStart0Done()
 
227
{
 
228
    if( state != AutoStart0 )
 
229
        return;
 
230
    disconnect( klauncherSignals, SIGNAL( autoStart0Done()), this, SLOT( autoStart0Done()));
 
231
    if( !checkStartupSuspend())
 
232
        return;
 
233
    kDebug( 1218 ) << "Autostart 0 done";
 
234
#ifdef KSMSERVER_STARTUP_DEBUG1
 
235
    kDebug() << t.elapsed();
 
236
#endif
 
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
 
244
 
 
245
    org::kde::KCMInit kcminit("org.kde.kcminit", "/kcminit" , QDBusConnection::sessionBus());
 
246
    kcminit.runPhase1();
 
247
}
 
248
 
 
249
void KSMServer::kcmPhase1Done()
 
250
{
 
251
    if( state != KcmInitPhase1 )
 
252
        return;
 
253
    kDebug( 1218 ) << "Kcminit phase 1 done";
 
254
    disconnect( kcminitSignals, SIGNAL( phase1Done()), this, SLOT( kcmPhase1Done()));
 
255
    autoStart1();
 
256
}
 
257
 
 
258
void KSMServer::kcmPhase1Timeout()
 
259
{
 
260
    if( state != KcmInitPhase1 )
 
261
        return;
 
262
    kDebug( 1218 ) << "Kcminit phase 1 timeout";
 
263
    kcmPhase1Done();
 
264
}
 
265
 
 
266
void KSMServer::autoStart1()
 
267
{
 
268
    if( state != KcmInitPhase1 )
 
269
        return;
 
270
    state = AutoStart1;
 
271
#ifdef KSMSERVER_STARTUP_DEBUG1
 
272
    kDebug() << t.elapsed();
 
273
#endif
 
274
    org::kde::KLauncher klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
 
275
    klauncher.autoStart((int)1);
 
276
}
 
277
 
 
278
void KSMServer::autoStart1Done()
 
279
{
 
280
    if( state != AutoStart1 )
 
281
        return;
 
282
    disconnect( klauncherSignals, SIGNAL( autoStart1Done()), this, SLOT( autoStart1Done()));
 
283
    if( !checkStartupSuspend())
 
284
        return;
 
285
    kDebug( 1218 ) << "Autostart 1 done";
 
286
    setupShortcuts(); // done only here, because it needs kglobalaccel :-/
 
287
    lastAppStarted = 0;
 
288
    lastIdStarted.clear();
 
289
    state = Restoring;
 
290
#ifdef KSMSERVER_STARTUP_DEBUG1
 
291
    kDebug() << t.elapsed();
 
292
#endif
 
293
    if( defaultSession()) {
 
294
        autoStart2();
 
295
        return;
 
296
    }
 
297
    tryRestoreNext();
 
298
}
 
299
 
 
300
void KSMServer::clientRegistered( const char* previousId )
 
301
{
 
302
    if ( previousId && lastIdStarted == previousId )
 
303
        tryRestoreNext();
 
304
}
 
305
 
 
306
void KSMServer::tryRestoreNext()
 
307
{
 
308
    if( state != Restoring && state != RestoringSubSession )
 
309
        return;
 
310
    restoreTimer.stop();
 
311
    startupSuspendTimeoutTimer.stop();
 
312
    KConfigGroup config(KGlobal::config(), sessionGroup );
 
313
 
 
314
    while ( lastAppStarted < appsToStart ) {
 
315
        lastAppStarted++;
 
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;
 
322
                break;
 
323
            }
 
324
        }
 
325
        if ( alreadyStarted )
 
326
            continue;
 
327
 
 
328
        QStringList restartCommand = config.readEntry( QString("restartCommand")+n, QStringList() );
 
329
        if ( restartCommand.isEmpty() ||
 
330
             (config.readEntry( QString("restartStyleHint")+n, 0 ) == SmRestartNever)) {
 
331
            continue;
 
332
        }
 
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
 
345
        }
 
346
    }
 
347
 
 
348
    //all done
 
349
    appsToStart = 0;
 
350
    lastIdStarted.clear();
 
351
 
 
352
    if (state == Restoring)
 
353
        autoStart2();
 
354
    else { //subsession
 
355
        state = Idle;
 
356
        emit subSessionOpened();
 
357
    }
 
358
}
 
359
 
 
360
void KSMServer::autoStart2()
 
361
{
 
362
    if( state != Restoring )
 
363
        return;
 
364
    if( !checkStartupSuspend())
 
365
        return;
 
366
    state = FinishingStartup;
 
367
#ifdef KSMSERVER_STARTUP_DEBUG1
 
368
    kDebug() << t.elapsed();
 
369
#endif
 
370
    waitAutoStart2 = true;
 
371
    waitKcmInit2 = true;
 
372
    org::kde::KLauncher klauncher("org.kde.klauncher", "/KLauncher", QDBusConnection::sessionBus());
 
373
    klauncher.autoStart((int)2);
 
374
 
 
375
    QDBusInterface kded( "org.kde.kded", "/kded", "org.kde.kded" );
 
376
    kded.call( "loadSecondPhase" );
 
377
 
 
378
    runUserAutostart();
 
379
 
 
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());
 
383
    kcminit.runPhase2();
 
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
 
387
}
 
388
 
 
389
void KSMServer::runUserAutostart()
 
390
{
 
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() );
 
395
    if (dir.exists()) {
 
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('#') ) )
 
402
            {
 
403
                KUrl url( dir.absolutePath() + '/' + file );
 
404
                (void) new KRun( url, 0, true );
 
405
            }
 
406
        }
 
407
    } else {
 
408
        // Create dir so that users can find it :-)
 
409
        dir.mkpath( KGlobalSettings::autostartPath() );
 
410
    }
 
411
}
 
412
 
 
413
void KSMServer::autoStart2Done()
 
414
{
 
415
    if( state != FinishingStartup )
 
416
        return;
 
417
    disconnect( klauncherSignals, SIGNAL( autoStart2Done()), this, SLOT( autoStart2Done()));
 
418
    kDebug( 1218 ) << "Autostart 2 done";
 
419
    waitAutoStart2 = false;
 
420
    finishStartup();
 
421
}
 
422
 
 
423
void KSMServer::kcmPhase2Done()
 
424
{
 
425
    if( state != FinishingStartup )
 
426
        return;
 
427
    kDebug( 1218 ) << "Kcminit phase 2 done";
 
428
    disconnect( kcminitSignals, SIGNAL( phase2Done()), this, SLOT( kcmPhase2Done()));
 
429
    delete kcminitSignals;
 
430
    kcminitSignals = NULL;
 
431
    waitKcmInit2 = false;
 
432
    finishStartup();
 
433
}
 
434
 
 
435
void KSMServer::kcmPhase2Timeout()
 
436
{
 
437
    if( !waitKcmInit2 )
 
438
        return;
 
439
    kDebug( 1218 ) << "Kcminit phase 2 timeout";
 
440
    kcmPhase2Done();
 
441
}
 
442
 
 
443
void KSMServer::finishStartup()
 
444
{
 
445
    if( state != FinishingStartup )
 
446
        return;
 
447
    if( waitAutoStart2 || waitKcmInit2 )
 
448
        return;
 
449
 
 
450
    upAndRunning( "ready" );
 
451
#ifdef KSMSERVER_STARTUP_DEBUG1
 
452
    kDebug() << t.elapsed();
 
453
#endif
 
454
 
 
455
    state = Idle;
 
456
    setupXIOErrorHandler(); // From now on handle X errors as normal shutdown.
 
457
}
 
458
 
 
459
bool KSMServer::checkStartupSuspend()
 
460
{
 
461
    if( startupSuspendCount.isEmpty())
 
462
        return true;
 
463
    // wait for the phase to finish
 
464
    if( !startupSuspendTimeoutTimer.isActive())
 
465
    {
 
466
        startupSuspendTimeoutTimer.setSingleShot( true );
 
467
        startupSuspendTimeoutTimer.start( 10000 );
 
468
    }
 
469
    return false;
 
470
}
 
471
 
 
472
void KSMServer::suspendStartup( const QString &app )
 
473
{
 
474
    if( !startupSuspendCount.contains( app ))
 
475
        startupSuspendCount[ app ] = 0;
 
476
    ++startupSuspendCount[ app ];
 
477
}
 
478
 
 
479
void KSMServer::resumeStartup( const QString &app )
 
480
{
 
481
    if( !startupSuspendCount.contains( app ))
 
482
        return;
 
483
    if( --startupSuspendCount[ app ] == 0 ) {
 
484
        startupSuspendCount.remove( app );
 
485
        if( startupSuspendCount.isEmpty() && startupSuspendTimeoutTimer.isActive()) {
 
486
            startupSuspendTimeoutTimer.stop();
 
487
            resumeStartupInternal();
 
488
        }
 
489
    }
 
490
}
 
491
 
 
492
void KSMServer::startupSuspendTimeout()
 
493
{
 
494
    kDebug( 1218 ) << "Startup suspend timeout:" << state;
 
495
    resumeStartupInternal();
 
496
}
 
497
 
 
498
void KSMServer::resumeStartupInternal()
 
499
{
 
500
    startupSuspendCount.clear();
 
501
    switch( state ) {
 
502
        case LaunchingWM:
 
503
            autoStart0();
 
504
          break;
 
505
        case AutoStart0:
 
506
            autoStart0Done();
 
507
          break;
 
508
        case AutoStart1:
 
509
            autoStart1Done();
 
510
          break;
 
511
        case Restoring:
 
512
            autoStart2();
 
513
          break;
 
514
        default:
 
515
            kWarning( 1218 ) << "Unknown resume startup state" ;
 
516
          break;
 
517
    }
 
518
}
 
519
 
 
520
void KSMServer::upAndRunning( const QString& msg )
 
521
{
 
522
    XEvent e;
 
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 );
 
531
}
 
532
 
 
533
void KSMServer::restoreSubSession( const QString& name )
 
534
{
 
535
    sessionGroup = "SubSession: " + name;
 
536
 
 
537
    KConfigGroup configSessionGroup( KGlobal::config(), sessionGroup);
 
538
    int count =  configSessionGroup.readEntry( "count", 0 );
 
539
    appsToStart = count;
 
540
    lastAppStarted = 0;
 
541
    lastIdStarted.clear();
 
542
 
 
543
    state = RestoringSubSession;
 
544
    tryRestoreNext();
 
545
}