diff options
Diffstat (limited to 'ksmserver/startup.cpp')
-rw-r--r-- | ksmserver/startup.cpp | 489 |
1 files changed, 489 insertions, 0 deletions
diff --git a/ksmserver/startup.cpp b/ksmserver/startup.cpp new file mode 100644 index 000000000..b1ed9992e --- /dev/null +++ b/ksmserver/startup.cpp @@ -0,0 +1,489 @@ +/***************************************************************** +ksmserver - the KDE session management server + +Copyright (C) 2000 Matthias Ettrich <ettrich@kde.org> +Copyright (C) 2005 Lubos Lunak <l.lunak@kde.org> + +relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de> + +some code taken from the dcopserver (part of the KDE libraries), which is +Copyright (c) 1999 Matthias Ettrich <ettrich@kde.org> +Copyright (c) 1999 Preston Brown <pbrown@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +******************************************************************/ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pwd.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif +#include <sys/socket.h> +#include <sys/un.h> + +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <time.h> +#include <errno.h> +#include <string.h> +#include <assert.h> + +#ifdef HAVE_LIMITS_H +#include <limits.h> +#endif + +#include <tqfile.h> +#include <tqtextstream.h> +#include <tqdatastream.h> +#include <tqptrstack.h> +#include <tqpushbutton.h> +#include <tqmessagebox.h> +#include <tqguardedptr.h> +#include <tqtimer.h> + +#include <tdelocale.h> +#include <tdeglobal.h> +#include <tdeconfig.h> +#include <kstandarddirs.h> +#include <unistd.h> +#include <tdeapplication.h> +#include <kstaticdeleter.h> +#include <tdetempfile.h> +#include <kprocess.h> +#include <dcopclient.h> +#include <dcopref.h> +#include <twinmodule.h> +#include <knotifyclient.h> + +#include "server.h" +#include "global.h" +#include "startupdlg.h" +#include "client.h" + +#include <kdebug.h> + +// shall we show a nice fancy login screen? +bool showFancyLogin = FALSE; +bool trinity_startup_main_sequence_done = FALSE; + +/*! Restores the previous session. Ensures the window manager is + running (if specified). + */ +void KSMServer::restoreSession( TQString sessionName ) +{ + showFancyLogin = TDEConfigGroup(TDEGlobal::config(), "Login").readBoolEntry("showFancyLogin", true); + TDEConfig ksplashcfg( "ksplashrc", true ); + ksplashcfg.setGroup( "KSplash" ); + if ( ksplashcfg.readEntry( "Theme", "Default" ) != TQString("Unified") ) + showFancyLogin = false; + + if( state != Idle ) + return; + state = LaunchingWM; + + kdDebug( 1218 ) << "KSMServer::restoreSession " << sessionName << endl; + upAndRunning( "restore session"); + TDEConfig* config = TDEGlobal::config(); + + sessionGroup = "Session: " + sessionName; + + config->setGroup( sessionGroup ); + int count = config->readNumEntry( "count" ); + appsToStart = count; + + TQValueList<TQStringList> wmCommands; + if ( !wm.isEmpty() ) { + for ( int i = 1; i <= count; i++ ) { + TQString n = TQString::number(i); + if ( wm == config->readEntry( TQString("program")+n ) ) { + wmCommands << config->readListEntry( TQString("restartCommand")+n ); + } + } + } + if ( wmCommands.isEmpty() ) + wmCommands << ( TQStringList() << wm ); + + publishProgress( appsToStart, true ); + connectDCOPSignal( launcher, launcher, "autoStart0Done()", + "autoStart0Done()", true); + connectDCOPSignal( launcher, launcher, "autoStart1Done()", + "autoStart1Done()", true); + connectDCOPSignal( launcher, launcher, "autoStart2Done()", + "autoStart2Done()", true); + upAndRunning( "ksmserver" ); + + if ( !wmCommands.isEmpty() ) { + // when we have a window manager, we start it first and give + // it some time before launching other processes. Results in a + // visually more appealing startup. + for (uint i = 0; i < wmCommands.count(); i++) + startApplication( wmCommands[i] ); + if ((showFancyLogin) && (!startupNotifierIPDlg)) { + startupNotifierIPDlg = KSMStartupIPDlg::showStartupIP(); + } + TQTimer::singleShot( 4000, this, TQT_SLOT( autoStart0() ) ); + } else { + if ((showFancyLogin) && (!startupNotifierIPDlg)) { + startupNotifierIPDlg = KSMStartupIPDlg::showStartupIP(); + } + autoStart0(); + } +} + +/*! + Starts the default session. + + Currently, that's the window manager only (if specified). + */ +void KSMServer::startDefaultSession() +{ + showFancyLogin = TDEConfigGroup(TDEGlobal::config(), "Login").readBoolEntry("showFancyLogin", true); + TDEConfig ksplashcfg( "ksplashrc", true ); + ksplashcfg.setGroup( "KSplash" ); + if ( ksplashcfg.readEntry( "Theme", "Default" ) != TQString("None") ) + showFancyLogin = false; + + if( state != Idle ) + return; + + state = LaunchingWM; + sessionGroup = ""; + publishProgress( 0, true ); + upAndRunning( "ksmserver" ); + connectDCOPSignal( launcher, launcher, "autoStart0Done()", + "autoStart0Done()", true); + connectDCOPSignal( launcher, launcher, "autoStart1Done()", + "autoStart1Done()", true); + connectDCOPSignal( launcher, launcher, "autoStart2Done()", + "autoStart2Done()", true); + if (!wmAddArgs.isEmpty()) { + TQStringList wmstartupcommand; + wmstartupcommand.split(" ", wmAddArgs); + wmstartupcommand.prepend(wm); + startApplication( wmstartupcommand ); + } + else { + startApplication( wm ); + } + if ((showFancyLogin) && (!startupNotifierIPDlg)) { + startupNotifierIPDlg = KSMStartupIPDlg::showStartupIP(); + } + TQTimer::singleShot( 4000, this, TQT_SLOT( autoStart0() ) ); +} + + +void KSMServer::clientSetProgram( KSMClient* client ) +{ + if ( !wm.isEmpty() && client->program() == wm ) + autoStart0(); +} + +void KSMServer::autoStart0() +{ + if( state != LaunchingWM ) + return; + if( !checkStartupSuspend()) + return; + state = AutoStart0; + DCOPRef( launcher ).send( "autoStart", (int) 0 ); +} + +void KSMServer::autoStart0Done() +{ + if( state != AutoStart0 ) + return; + disconnectDCOPSignal( launcher, launcher, "autoStart0Done()", + "autoStart0Done()"); + if( !checkStartupSuspend()) + return; + kdDebug( 1218 ) << "Autostart 0 done" << endl; + upAndRunning( "kdesktop" ); + upAndRunning( "kicker" ); + connectDCOPSignal( "kcminit", "kcminit", "phase1Done()", + "kcmPhase1Done()", true); + state = KcmInitPhase1; + TQTimer::singleShot( 10000, this, TQT_SLOT( kcmPhase1Timeout())); // protection + DCOPRef( "kcminit", "kcminit" ).send( "runPhase1" ); +} + +void KSMServer::kcmPhase1Done() +{ + if( state != KcmInitPhase1 ) + return; + kdDebug( 1218 ) << "Kcminit phase 1 done" << endl; + disconnectDCOPSignal( "kcminit", "kcminit", "phase1Done()", + "kcmPhase1Done()" ); + autoStart1(); +} + +void KSMServer::kcmPhase1Timeout() +{ + if( state != KcmInitPhase1 ) + return; + kdDebug( 1218 ) << "Kcminit phase 1 timeout" << endl; + kcmPhase1Done(); +} + +void KSMServer::autoStart1() +{ + if( state != KcmInitPhase1 ) + return; + state = AutoStart1; + DCOPRef( launcher ).send( "autoStart", (int) 1 ); +} + +void KSMServer::autoStart1Done() +{ + if( state != AutoStart1 ) + return; + disconnectDCOPSignal( launcher, launcher, "autoStart1Done()", + "autoStart1Done()"); + if( !checkStartupSuspend()) + return; + kdDebug( 1218 ) << "Autostart 1 done" << endl; + lastAppStarted = 0; + lastIdStarted = TQString::null; + state = Restoring; + if( defaultSession()) { + autoStart2(); + return; + } + tryRestoreNext(); +} + +void KSMServer::clientRegistered( const char* previousId ) +{ + if ( previousId && lastIdStarted == previousId ) + tryRestoreNext(); +} + +void KSMServer::tryRestoreNext() +{ + if( state != Restoring ) + return; + restoreTimer.stop(); + TDEConfig* config = TDEGlobal::config(); + config->setGroup( sessionGroup ); + + while ( lastAppStarted < appsToStart ) { + publishProgress ( appsToStart - lastAppStarted ); + lastAppStarted++; + TQString n = TQString::number(lastAppStarted); + TQStringList restartCommand = config->readListEntry( TQString("restartCommand")+n ); + if ( restartCommand.isEmpty() || + (config->readNumEntry( TQString("restartStyleHint")+n ) == SmRestartNever)) { + continue; + } + if ( wm == config->readEntry( TQString("program")+n ) ) + continue; // wm already started + if( config->readBoolEntry( TQString( "wasWm" )+n, false )) + continue; // it was wm before, but not now, don't run it (some have --replace in command :( ) + startApplication( restartCommand, + config->readEntry( TQString("clientMachine")+n ), + config->readEntry( TQString("userId")+n )); + lastIdStarted = config->readEntry( TQString("clientId")+n ); + if ( !lastIdStarted.isEmpty() ) { + restoreTimer.start( 2000, TRUE ); + return; // we get called again from the clientRegistered handler + } + } + + appsToStart = 0; + lastIdStarted = TQString::null; + publishProgress( 0 ); + + autoStart2(); +} + +void KSMServer::autoStart2() +{ + if( state != Restoring ) + return; + if( !checkStartupSuspend()) + return; + state = FinishingStartup; + waitAutoStart2 = true; + waitKcmInit2 = true; + DCOPRef( launcher ).send( "autoStart", (int) 2 ); + DCOPRef( "kded", "kded" ).send( "loadSecondPhase" ); + DCOPRef( "kdesktop", "KDesktopIface" ).send( "runAutoStart" ); + connectDCOPSignal( "kcminit", "kcminit", "phase2Done()", + "kcmPhase2Done()", true); + TQTimer::singleShot( 10000, this, TQT_SLOT( kcmPhase2Timeout())); // protection + DCOPRef( "kcminit", "kcminit" ).send( "runPhase2" ); + if( !defaultSession()) + restoreLegacySession( TDEGlobal::config()); + KNotifyClient::event( 0, "starttde" ); // this is the time KDE is up, more or less +} + +void KSMServer::autoStart2Done() +{ + if( state != FinishingStartup ) + return; + disconnectDCOPSignal( launcher, launcher, "autoStart2Done()", + "autoStart2Done()"); + kdDebug( 1218 ) << "Autostart 2 done" << endl; + waitAutoStart2 = false; + finishStartup(); +} + +void KSMServer::kcmPhase2Done() +{ + if( state != FinishingStartup ) + return; + kdDebug( 1218 ) << "Kcminit phase 2 done" << endl; + disconnectDCOPSignal( "kcminit", "kcminit", "phase2Done()", + "kcmPhase2Done()"); + waitKcmInit2 = false; + finishStartup(); +} + +void KSMServer::kcmPhase2Timeout() +{ + if( !waitKcmInit2 ) + return; + kdDebug( 1218 ) << "Kcminit phase 2 timeout" << endl; + kcmPhase2Done(); +} + +void KSMServer::finishStartup() +{ + if( state != FinishingStartup ) + return; + if( waitAutoStart2 || waitKcmInit2 ) + return; + + upAndRunning( "session ready" ); + DCOPRef( "knotify" ).send( "sessionReady" ); // knotify startup optimization + + state = Idle; + + // [FIXME] When this fires applications are still being loaded, especially the task tray apps + // See if there is a way to detect when all session managed applications have been fully started and wait to fire this until that point! + if (startupNotifierIPDlg) { + static_cast<KSMStartupIPDlg*>(startupNotifierIPDlg)->closeSMDialog(); + startupNotifierIPDlg=0; + } + + setupXIOErrorHandler(); // From now on handle X errors as normal shutdown. +} + +bool KSMServer::checkStartupSuspend() +{ + if( startupSuspendCount.isEmpty()) + return true; + // wait for the phase to finish + if( !startupSuspendTimeoutTimer.isActive()) + startupSuspendTimeoutTimer.start( 10000, true ); + return false; +} + +void KSMServer::suspendStartup( TQCString app ) +{ + if( !startupSuspendCount.contains( app )) + startupSuspendCount[ app ] = 0; + ++startupSuspendCount[ app ]; +} + +void KSMServer::resumeStartup( TQCString app ) +{ + if( !startupSuspendCount.contains( app )) + return; + if( --startupSuspendCount[ app ] == 0 ) { + startupSuspendCount.remove( app ); + if( startupSuspendCount.isEmpty() && startupSuspendTimeoutTimer.isActive()) { + startupSuspendTimeoutTimer.stop(); + resumeStartupInternal(); + } + } +} + +void KSMServer::startupSuspendTimeout() +{ + kdDebug( 1218 ) << "Startup suspend timeout:" << state << endl; + resumeStartupInternal(); +} + +void KSMServer::resumeStartupInternal() +{ + startupSuspendCount.clear(); + switch( state ) { + case LaunchingWM: + autoStart0(); + break; + case AutoStart0: + autoStart0Done(); + break; + case AutoStart1: + autoStart1Done(); + break; + case Restoring: + autoStart2(); + break; + default: + kdWarning( 1218 ) << "Unknown resume startup state" << endl; + break; + } +} + +void KSMServer::publishProgress( int progress, bool max ) +{ + DCOPRef( "ksplash" ).send( max ? "setMaxProgress" : "setProgress", progress ); +} + + +void KSMServer::upAndRunning( const TQString& msg ) +{ + if (startupNotifierIPDlg) { + static_cast<KSMStartupIPDlg*>(startupNotifierIPDlg)->setStartupPhase(msg); + if (msg == TQString("session ready")) { + trinity_startup_main_sequence_done = TRUE; + } + } + DCOPRef( "ksplash" ).send( "upAndRunning", msg ); + XEvent e; + e.xclient.type = ClientMessage; + e.xclient.message_type = XInternAtom( tqt_xdisplay(), "_KDE_SPLASH_PROGRESS", False ); + e.xclient.display = tqt_xdisplay(); + e.xclient.window = tqt_xrootwin(); + e.xclient.format = 8; + assert( strlen( msg.latin1()) < 20 ); + strcpy( e.xclient.data.b, msg.latin1()); + XSendEvent( tqt_xdisplay(), tqt_xrootwin(), False, SubstructureNotifyMask, &e ); +} + +// these two are in the DCOP interface but I have no idea what uses them +// Remove for KDE4 +void KSMServer::restoreSessionInternal() +{ + autoStart1Done(); +} + +void KSMServer::restoreSessionDoneInternal() +{ + autoStart2Done(); +} |