summaryrefslogtreecommitdiffstats
path: root/ksmserver/shutdown.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ksmserver/shutdown.cpp')
-rw-r--r--ksmserver/shutdown.cpp1062
1 files changed, 1062 insertions, 0 deletions
diff --git a/ksmserver/shutdown.cpp b/ksmserver/shutdown.cpp
new file mode 100644
index 000000000..a40bffc3b
--- /dev/null
+++ b/ksmserver/shutdown.cpp
@@ -0,0 +1,1062 @@
+/*****************************************************************
+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 <tqregexp.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 <dmctl.h>
+#include <kdebug.h>
+#include <knotifyclient.h>
+
+#include <libtdersync/tdersync.h>
+
+#include "server.h"
+#include "global.h"
+#include "shutdowndlg.h"
+#include "client.h"
+
+#ifdef BUILD_PROFILE_SHUTDOWN
+#define PROFILE_SHUTDOWN 1
+#endif
+
+#ifdef PROFILE_SHUTDOWN
+ #define SHUTDOWN_MARKER(x) printf("[ksmserver] '%s' [%s]\n", x, TQTime::currentTime().toString("hh:mm:ss:zzz").ascii()); fflush(stdout);
+#else // PROFILE_SHUTDOWN
+ #define SHUTDOWN_MARKER(x)
+#endif // PROFILE_SHUTDOWN
+
+// Time to wait after close request for graceful application termination
+// If set too high running applications may be ungracefully terminated on slow machines or when many X11 applications are running
+#define KSMSERVER_SHUTDOWN_CLIENT_UNRESPONSIVE_TIMEOUT 20000
+
+// Time to wait before showing manual termination options
+// If set too low the user may be confused by buttons briefly flashing up on the screen during an otherwise normal logout process
+#define KSMSERVER_NOTIFICATION_MANUAL_OPTIONS_TIMEOUT 3000
+
+void KSMServer::logout( int confirm, int sdtype, int sdmode )
+{
+ shutdown( (TDEApplication::ShutdownConfirm)confirm,
+ (TDEApplication::ShutdownType)sdtype,
+ (TDEApplication::ShutdownMode)sdmode );
+}
+
+bool KSMServer::checkStatus( bool &logoutConfirmed, bool &maysd, bool &mayrb,
+ TDEApplication::ShutdownConfirm confirm,
+ TDEApplication::ShutdownType sdtype,
+ TDEApplication::ShutdownMode sdmode )
+{
+ pendingShutdown.stop();
+ if( dialogActive ) {
+ return false;
+ }
+ if( state >= Shutdown ) { // already performing shutdown
+ return false;
+ }
+ if( state != Idle ) { // performing startup
+ // perform shutdown as soon as startup is finished, in order to avoid saving partial session
+ if( !pendingShutdown.isActive()) {
+ pendingShutdown.start( 1000 );
+ pendingShutdown_confirm = confirm;
+ pendingShutdown_sdtype = sdtype;
+ pendingShutdown_sdmode = sdmode;
+ }
+ return false;
+ }
+
+ TDEConfig *config = TDEGlobal::config();
+ config->reparseConfiguration(); // config may have changed in the KControl module
+
+ config->setGroup("General" );
+ logoutConfirmed =
+ (confirm == TDEApplication::ShutdownConfirmYes) ? false :
+ (confirm == TDEApplication::ShutdownConfirmNo) ? true :
+ !config->readBoolEntry( "confirmLogout", true );
+ maysd = false;
+ mayrb = false;
+ if (config->readBoolEntry( "offerShutdown", true )) {
+ if (DM().canShutdown()) {
+ maysd = true;
+ mayrb = true;
+ }
+ else {
+#ifdef __TDE_HAVE_TDEHWLIB
+ TDERootSystemDevice* rootDevice = hwDevices->rootSystemDevice();
+ if (rootDevice) {
+ if (rootDevice->canPowerOff()) {
+ maysd = true;
+ }
+ if (rootDevice->canReboot()) {
+ mayrb = true;
+ }
+ }
+#endif
+ }
+ }
+ if (!maysd) {
+ if (sdtype != TDEApplication::ShutdownTypeNone &&
+ sdtype != TDEApplication::ShutdownTypeDefault &&
+ sdtype != TDEApplication::ShutdownTypeReboot &&
+ logoutConfirmed)
+ return false; /* unsupported fast shutdown */
+ }
+ if (!mayrb) {
+ if (sdtype != TDEApplication::ShutdownTypeNone &&
+ sdtype != TDEApplication::ShutdownTypeDefault &&
+ sdtype != TDEApplication::ShutdownTypeHalt &&
+ logoutConfirmed)
+ return false; /* unsupported fast shutdown */
+ }
+
+ return true;
+}
+
+void KSMServer::shutdownInternal( TDEApplication::ShutdownConfirm confirm,
+ TDEApplication::ShutdownType sdtype,
+ TDEApplication::ShutdownMode sdmode,
+ TQString bopt )
+{
+ bool maysd = false;
+ bool mayrb = false;
+ bool logoutConfirmed = false;
+ if ( !checkStatus( logoutConfirmed, maysd, mayrb, confirm, sdtype, sdmode ) ) {
+ return;
+ }
+
+ TDEConfig *config = TDEGlobal::config();
+
+ config->setGroup("General" );
+ if ((!maysd) && (sdtype != TDEApplication::ShutdownTypeReboot)) {
+ sdtype = TDEApplication::ShutdownTypeNone;
+ }
+ if ((!mayrb) && (sdtype != TDEApplication::ShutdownTypeHalt)) {
+ sdtype = TDEApplication::ShutdownTypeNone;
+ }
+ if (sdtype == TDEApplication::ShutdownTypeDefault) {
+ sdtype = (TDEApplication::ShutdownType) config->readNumEntry( "shutdownType", (int)TDEApplication::ShutdownTypeNone );
+ }
+ if (sdmode == TDEApplication::ShutdownModeDefault) {
+ sdmode = TDEApplication::ShutdownModeInteractive;
+ }
+
+ // shall we show a logout status dialog box?
+ bool showLogoutStatusDlg = TDEConfigGroup(TDEGlobal::config(), "Logout").readBoolEntry("showLogoutStatusDlg", true);
+
+ if (showLogoutStatusDlg) {
+ KSMShutdownIPFeedback::start();
+ }
+
+ dialogActive = true;
+ if ( !logoutConfirmed ) {
+ int selection;
+ KSMShutdownFeedback::start(); // make the screen gray
+ logoutConfirmed =
+ KSMShutdownDlg::confirmShutdown( maysd, mayrb, sdtype, bopt, &selection );
+ // ###### We can't make the screen remain gray while talking to the apps,
+ // because this prevents interaction ("do you want to save", etc.)
+ // TODO: turn the feedback widget into a list of apps to be closed,
+ // with an indicator of the current status for each.
+ KSMShutdownFeedback::stop(); // make the screen become normal again
+ if (selection != 0) {
+ // respect lock on resume & disable suspend/hibernate settings
+ // from power-manager
+ TDEConfig config("power-managerrc");
+ bool lockOnResume = config.readBoolEntry("lockOnResume", true);
+ if (lockOnResume) {
+ TQCString replyType;
+ TQByteArray replyData;
+ // Block here until lock is complete
+ // If this is not done the desktop of the locked session will be shown after suspend/hibernate until the lock fully engages!
+ kapp->dcopClient()->call("kdesktop", "KScreensaverIface", "lock()", TQCString(""), replyType, replyData);
+ }
+#ifdef __TDE_HAVE_TDEHWLIB
+ TDERootSystemDevice* rootDevice = hwDevices->rootSystemDevice();
+ if (rootDevice) {
+ if (selection == 1) { // Suspend
+ rootDevice->setPowerState(TDESystemPowerState::Suspend);
+ }
+ if (selection == 2) { // Hibernate
+ rootDevice->setPowerState(TDESystemPowerState::Hibernate);
+ }
+ if (selection == 3) { // Freeze
+ rootDevice->setPowerState(TDESystemPowerState::Freeze);
+ }
+ }
+#endif
+ }
+ }
+
+ if ( logoutConfirmed ) {
+ SHUTDOWN_MARKER("Shutdown initiated");
+ shutdownType = sdtype;
+ shutdownMode = sdmode;
+ bootOption = bopt;
+ shutdownNotifierIPDlg = 0;
+ if (showLogoutStatusDlg) {
+ shutdownNotifierIPDlg = KSMShutdownIPDlg::showShutdownIP();
+ if (shutdownNotifierIPDlg) {
+ connect(shutdownNotifierIPDlg, SIGNAL(abortLogoutClicked()), this, SLOT(cancelShutdown()));
+ connect(shutdownNotifierIPDlg, SIGNAL(skipNotificationClicked()), this, SLOT(forceSkipSaveYourself()));
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Notifying applications of logout request..."));
+ notificationTimer.start( KSMSERVER_NOTIFICATION_MANUAL_OPTIONS_TIMEOUT, true );
+ }
+ }
+
+ // shall we save the session on logout?
+ saveSession = ( config->readEntry( "loginMode", "restorePreviousLogout" ) == "restorePreviousLogout" );
+
+ if ( saveSession ) {
+ sessionGroup = TQString("Session: ") + SESSION_PREVIOUS_LOGOUT;
+ }
+
+ // Set the real desktop background to black so that exit looks
+ // clean regardless of what was on "our" desktop.
+ if (!showLogoutStatusDlg) {
+ TQT_TQWIDGET(kapp->desktop())->setBackgroundColor( Qt::black );
+ }
+ state = Shutdown;
+ wmPhase1WaitingCount = 0;
+ saveType = saveSession?SmSaveBoth:SmSaveGlobal;
+ performLegacySessionSave();
+ SHUTDOWN_MARKER("Legacy save complete");
+ startProtection();
+ for ( KSMClient* c = clients.first(); c; c = clients.next() ) {
+ c->resetState();
+ // Whoever came with the idea of phase 2 got it backwards
+ // unfortunately. Window manager should be the very first
+ // one saving session data, not the last one, as possible
+ // user interaction during session save may alter
+ // window positions etc.
+ // Moreover, KWin's focus stealing prevention would lead
+ // to undesired effects while session saving (dialogs
+ // wouldn't be activated), so it needs be assured that
+ // KWin will turn it off temporarily before any other
+ // user interaction takes place.
+ // Therefore, make sure the WM finishes its phase 1
+ // before others a chance to change anything.
+ // KWin will check if the session manager is ksmserver,
+ // and if yes it will save in phase 1 instead of phase 2.
+ if( isWM( c )) {
+ ++wmPhase1WaitingCount;
+ SmsSaveYourself( c->connection(), saveType,
+ true, SmInteractStyleAny, false );
+ }
+
+ }
+ if( wmPhase1WaitingCount == 0 ) { // no WM, simply start them all
+ for ( KSMClient* c = clients.first(); c; c = clients.next() )
+ SmsSaveYourself( c->connection(), saveType,
+ true, SmInteractStyleAny, false );
+ }
+ if ( clients.isEmpty() ) {
+ completeShutdownOrCheckpoint();
+ }
+ }
+ else {
+ if (showLogoutStatusDlg) {
+ KSMShutdownIPFeedback::stop();
+ }
+ }
+ dialogActive = false;
+}
+
+void KSMServer::shutdown( TDEApplication::ShutdownConfirm confirm,
+ TDEApplication::ShutdownType sdtype, TDEApplication::ShutdownMode sdmode )
+{
+ shutdownInternal( confirm, sdtype, sdmode );
+}
+
+#include <tdemessagebox.h>
+
+void KSMServer::logoutTimed( int sdtype, int sdmode, TQString bootOption )
+{
+ int confirmDelay = 0;
+
+ TDEConfig* config = TDEGlobal::config();
+ config->reparseConfiguration(); // config may have changed in the KControl module
+ config->setGroup( "General" );
+
+ if ( sdtype == TDEApplication::ShutdownTypeHalt ) {
+ confirmDelay = config->readNumEntry( "confirmShutdownDelay", 31 );
+ }
+ else if ( sdtype == TDEApplication::ShutdownTypeReboot ) {
+ confirmDelay = config->readNumEntry( "confirmRebootDelay", 31 );
+ }
+ else {
+ if(config->readBoolEntry("confirmLogout", true)) {
+ confirmDelay = config->readNumEntry( "confirmLogoutDelay", 31 );
+ }
+ }
+
+ bool result = true;
+ if (confirmDelay > 0) {
+ if(config->readBoolEntry("doFancyLogout", true)) {
+ KSMShutdownFeedback::start(); // make the screen gray
+ }
+ result = KSMDelayedMessageBox::showTicker( (TDEApplication::ShutdownType)sdtype, bootOption, confirmDelay );
+ if(config->readBoolEntry("doFancyLogout", true)) {
+ KSMShutdownFeedback::stop(); // make the screen become normal again
+ }
+ }
+
+ if ( result )
+ shutdownInternal( TDEApplication::ShutdownConfirmNo,
+ (TDEApplication::ShutdownType)sdtype,
+ (TDEApplication::ShutdownMode)sdmode,
+ bootOption );
+}
+
+void KSMServer::pendingShutdownTimeout()
+{
+ shutdown( pendingShutdown_confirm, pendingShutdown_sdtype, pendingShutdown_sdmode );
+}
+
+void KSMServer::saveCurrentSession()
+{
+ if ( state != Idle || dialogActive )
+ return;
+
+ if ( currentSession().isEmpty() || currentSession() == SESSION_PREVIOUS_LOGOUT )
+ sessionGroup = TQString("Session: ") + SESSION_BY_USER;
+
+ state = Checkpoint;
+ wmPhase1WaitingCount = 0;
+ saveType = SmSaveLocal;
+ saveSession = true;
+ performLegacySessionSave();
+ for ( KSMClient* c = clients.first(); c; c = clients.next() ) {
+ c->resetState();
+ if( isWM( c )) {
+ ++wmPhase1WaitingCount;
+ SmsSaveYourself( c->connection(), saveType, false, SmInteractStyleNone, false );
+ }
+ }
+ if( wmPhase1WaitingCount == 0 ) {
+ for ( KSMClient* c = clients.first(); c; c = clients.next() )
+ SmsSaveYourself( c->connection(), saveType, false, SmInteractStyleNone, false );
+ }
+ if ( clients.isEmpty() )
+ completeShutdownOrCheckpoint();
+}
+
+void KSMServer::saveCurrentSessionAs( TQString session )
+{
+ if ( state != Idle || dialogActive )
+ return;
+ sessionGroup = "Session: " + session;
+ saveCurrentSession();
+}
+
+// callbacks
+void KSMServer::saveYourselfDone( KSMClient* client, bool success )
+{
+ if ( state == Idle ) {
+ // State saving when it's not shutdown or checkpoint. Probably
+ // a shutdown was cancelled and the client is finished saving
+ // only now. Discard the saved state in order to avoid
+ // the saved data building up.
+ TQStringList discard = client->discardCommand();
+ if( !discard.isEmpty())
+ executeCommand( discard );
+ return;
+ }
+ if ( success ) {
+ client->saveYourselfDone = true;
+ completeShutdownOrCheckpoint();
+ } else {
+ // fake success to make KDE's logout not block with broken
+ // apps. A perfect ksmserver would display a warning box at
+ // the very end.
+ client->saveYourselfDone = true;
+ completeShutdownOrCheckpoint();
+ }
+ startProtection();
+ if( isWM( client ) && !client->wasPhase2 && wmPhase1WaitingCount > 0 ) {
+ --wmPhase1WaitingCount;
+ // WM finished its phase1, save the rest
+ if( wmPhase1WaitingCount == 0 ) {
+ for ( KSMClient* c = clients.first(); c; c = clients.next() )
+ if( !isWM( c ))
+ SmsSaveYourself( c->connection(), saveType, saveType != SmSaveLocal,
+ saveType != SmSaveLocal ? SmInteractStyleAny : SmInteractStyleNone,
+ false );
+ }
+ }
+
+ notificationTimer.stop();
+ if (shutdownNotifierIPDlg) {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->hideNotificationActionButtons();
+ }
+
+ updateLogoutStatusDialog();
+}
+
+void KSMServer::updateLogoutStatusDialog()
+{
+ bool inPhase2 = true;
+ bool pendingInteraction = false;
+ for( KSMClient* c = clients.first(); c; c = clients.next()) {
+ if ( !c->saveYourselfDone && !c->waitForPhase2 ) {
+ inPhase2 = false;
+ }
+ if ( c->pendingInteraction ) {
+ pendingInteraction = true;
+ }
+ }
+ if (clientInteracting) {
+ pendingInteraction = true;
+ }
+
+ if (shutdownNotifierIPDlg) {
+ int waitingClients = 0;
+ TQString nextClientToKill;
+ TQDateTime currentDateTime = TQDateTime::currentDateTime();
+ TQDateTime oldestFoundDateTime = currentDateTime;
+ for( KSMClient* c = clients.first(); c; c = clients.next()) {
+ if (c->saveYourselfDone) {
+ continue;
+ }
+ if( isWM( c ) || isCM( c ) || isNotifier( c ) || isDesktop( c ) ) {
+ continue;
+ }
+ waitingClients++;
+ if (c->program() != "") {
+ if (c->terminationRequestTimeStamp < oldestFoundDateTime) {
+ nextClientToKill = c->program();
+ oldestFoundDateTime = c->terminationRequestTimeStamp;
+ }
+ }
+ }
+ if (inPhase2) {
+ if (phase2ClientCount > 0) {
+ if (!notificationTimer.isActive()) {
+ notificationTimer.start( KSMSERVER_NOTIFICATION_MANUAL_OPTIONS_TIMEOUT, true );
+ }
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show();
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setNotificationActionButtonsSkipText(i18n("Skip Notification (%1)").arg(((KSMSERVER_SHUTDOWN_CLIENT_UNRESPONSIVE_TIMEOUT - (protectionTimerCounter*1000))/1000)+1));
+ if (nextClientToKill == "") {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Notifying remaining applications of logout request (%1/%2)...").arg(phase2ClientCount-waitingClients).arg(phase2ClientCount));
+ }
+ else {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Notifying remaining applications of logout request (%1/%2, %3)...").arg(phase2ClientCount-waitingClients).arg(phase2ClientCount).arg(nextClientToKill));
+ }
+ }
+ }
+ else {
+ if (pendingInteraction) {
+#if 0
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setNotificationActionButtonsSkipText(i18n("Ignore and Resume Logout"));
+#else
+ // Hide dialog and buttons
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->hide();
+ notificationTimer.stop();
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->hideNotificationActionButtons();
+#endif
+ if (nextClientToKill == "") {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("An application is requesting attention, logout paused..."));
+ }
+ else {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("%3 is requesting attention, logout paused...").arg(nextClientToKill));
+ }
+ }
+ else {
+ if (!notificationTimer.isActive()) {
+ notificationTimer.start( KSMSERVER_NOTIFICATION_MANUAL_OPTIONS_TIMEOUT, true );
+ }
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show();
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setNotificationActionButtonsSkipText(i18n("Skip Notification (%1)").arg(((KSMSERVER_SHUTDOWN_CLIENT_UNRESPONSIVE_TIMEOUT - (protectionTimerCounter*1000))/1000)+1));
+ if (nextClientToKill == "") {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Notifying applications of logout request (%1/%2)...").arg(clients.count()-waitingClients).arg(clients.count()));
+ }
+ else {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Notifying applications of logout request (%1/%2, %3)...").arg(clients.count()-waitingClients).arg(clients.count()).arg(nextClientToKill));
+ }
+ }
+ }
+ }
+}
+
+void KSMServer::interactRequest( KSMClient* client, int /*dialogType*/ )
+{
+ if ( state == Shutdown )
+ client->pendingInteraction = true;
+ else
+ SmsInteract( client->connection() );
+
+ handlePendingInteractions();
+}
+
+void KSMServer::interactDone( KSMClient* client, bool cancelShutdown_ )
+{
+ if ( client != clientInteracting )
+ return; // should not happen
+ clientInteracting = 0;
+ if ( cancelShutdown_ )
+ cancelShutdown( client );
+ else
+ handlePendingInteractions();
+}
+
+
+void KSMServer::phase2Request( KSMClient* client )
+{
+ client->waitForPhase2 = true;
+ client->wasPhase2 = true;
+ completeShutdownOrCheckpoint();
+ if( isWM( client ) && wmPhase1WaitingCount > 0 ) {
+ --wmPhase1WaitingCount;
+ // WM finished its phase1 and requests phase2, save the rest
+ if( wmPhase1WaitingCount == 0 ) {
+ for ( KSMClient* c = clients.first(); c; c = clients.next() )
+ if( !isWM( c ))
+ SmsSaveYourself( c->connection(), saveType, saveType != SmSaveLocal,
+ saveType != SmSaveLocal ? SmInteractStyleAny : SmInteractStyleNone,
+ false );
+ }
+ }
+}
+
+void KSMServer::handlePendingInteractions()
+{
+ if ( clientInteracting )
+ return;
+
+ for ( KSMClient* c = clients.first(); c; c = clients.next() ) {
+ if ( c->pendingInteraction ) {
+ clientInteracting = c;
+ c->pendingInteraction = false;
+ break;
+ }
+ }
+ if ( clientInteracting ) {
+ endProtection();
+ SmsInteract( clientInteracting->connection() );
+ } else {
+ startProtection();
+ }
+}
+
+void KSMServer::cancelShutdown( TQString cancellationText )
+{
+ if (shutdownNotifierIPDlg) {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->closeSMDialog();
+ shutdownNotifierIPDlg=0;
+ }
+ KNotifyClient::event( 0, "cancellogout", cancellationText);
+ clientInteracting = 0;
+ for ( KSMClient* c = clients.first(); c; c = clients.next() ) {
+ SmsShutdownCancelled( c->connection() );
+ if( c->saveYourselfDone ) {
+ // Discard also saved state.
+ TQStringList discard = c->discardCommand();
+ if( !discard.isEmpty())
+ executeCommand( discard );
+ }
+ }
+ state = Idle;
+}
+
+void KSMServer::cancelShutdown( KSMClient* c )
+{
+ kdDebug( 1218 ) << "Client " << c->program() << " (" << c->clientId() << ") canceled shutdown." << endl;
+ cancelShutdown(i18n( "Logout canceled by '%1'" ).arg( c->program()));
+}
+
+void KSMServer::cancelShutdown()
+{
+ kdDebug( 1218 ) << "User canceled shutdown." << endl;
+ cancelShutdown(i18n( "Logout canceled by user" ));
+}
+
+void KSMServer::startProtection()
+{
+ protectionTimerCounter = 0;
+ protectionTimer.start( 1000, true );
+}
+
+void KSMServer::endProtection()
+{
+ protectionTimerCounter = 0;
+ protectionTimer.stop();
+}
+
+void KSMServer::protectionTimerTick()
+{
+ protectionTimerCounter++;
+ if ((protectionTimerCounter*1000) > KSMSERVER_SHUTDOWN_CLIENT_UNRESPONSIVE_TIMEOUT) {
+ protectionTimerCounter = 0;
+ protectionTimeout();
+ }
+ else {
+ protectionTimer.start( 1000, true );
+ }
+ updateLogoutStatusDialog();
+}
+
+/*
+ Internal protection slot, invoked when clients do not react during
+ shutdown.
+ */
+void KSMServer::protectionTimeout()
+{
+ if ( ( state != Shutdown && state != Checkpoint ) || clientInteracting )
+ return;
+
+ handleProtectionTimeout();
+
+ startProtection();
+}
+
+void KSMServer::forceSkipSaveYourself()
+{
+ SHUTDOWN_MARKER("forceSkipSaveYourself");
+
+ handleProtectionTimeout();
+
+ startProtection();
+}
+
+void KSMServer::handleProtectionTimeout()
+{
+ SHUTDOWN_MARKER("handleProtectionTimeout");
+
+ notificationTimer.stop();
+ if (shutdownNotifierIPDlg) {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->hideNotificationActionButtons();
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show();
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Forcing interacting application termination").append("..."));
+ }
+
+ for ( KSMClient* c = clients.first(); c; c = clients.next() ) {
+ if ( !c->saveYourselfDone && !c->waitForPhase2 ) {
+ kdDebug( 1218 ) << "protectionTimeout: client " << c->program() << "(" << c->clientId() << ")" << endl;
+ c->saveYourselfDone = true;
+ }
+ }
+ completeShutdownOrCheckpoint();
+}
+
+void KSMServer::notificationTimeout()
+{
+ if (shutdownNotifierIPDlg) {
+ // Display the buttons in the logout dialog
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->showNotificationActionButtons();
+ }
+}
+
+void KSMServer::completeShutdownOrCheckpoint()
+{
+ SHUTDOWN_MARKER("completeShutdownOrCheckpoint");
+ if ( state != Shutdown && state != Checkpoint ) {
+ SHUTDOWN_MARKER("completeShutdownOrCheckpoint state not Shutdown or Checkpoint");
+ return;
+ }
+
+ for ( KSMClient* c = clients.first(); c; c = clients.next() ) {
+ if ( !c->saveYourselfDone && !c->waitForPhase2 ) {
+ SHUTDOWN_MARKER("completeShutdownOrCheckpoint state not done yet");
+ return; // not done yet
+ }
+ }
+
+ // do phase 2
+ phase2ClientCount = 0;
+ bool waitForPhase2 = false;
+ for ( KSMClient* c = clients.first(); c; c = clients.next() ) {
+ if ( !c->saveYourselfDone && c->waitForPhase2 ) {
+ c->waitForPhase2 = false;
+ phase2ClientCount++;
+ SmsSaveYourselfPhase2( c->connection() );
+ waitForPhase2 = true;
+ }
+ }
+ if ( waitForPhase2 ) {
+ SHUTDOWN_MARKER("completeShutdownOrCheckpoint state still waiting for Phase 2");
+ if (shutdownNotifierIPDlg) {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show();
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Notifying remaining applications of logout request..."));
+ notificationTimer.start( KSMSERVER_NOTIFICATION_MANUAL_OPTIONS_TIMEOUT, true );
+ }
+ return;
+ }
+ SHUTDOWN_MARKER("Phase 2 complete");
+
+ bool showLogoutStatusDlg = TDEConfigGroup(TDEGlobal::config(), "Logout").readBoolEntry("showLogoutStatusDlg", true);
+ if (showLogoutStatusDlg && state != Checkpoint) {
+ KSMShutdownIPFeedback::showit(); // hide the UGLY logout process from the user
+ if (!shutdownNotifierIPDlg) {
+ shutdownNotifierIPDlg = KSMShutdownIPDlg::showShutdownIP();
+ if (shutdownNotifierIPDlg) {
+ connect(shutdownNotifierIPDlg, SIGNAL(abortLogoutClicked()), this, SLOT(cancelShutdown()));
+ connect(shutdownNotifierIPDlg, SIGNAL(skipNotificationClicked()), this, SLOT(forceSkipSaveYourself()));
+ }
+ }
+ while (!KSMShutdownIPFeedback::ispainted()) {
+ tqApp->processEvents();
+ }
+ }
+
+ notificationTimer.stop();
+ if (shutdownNotifierIPDlg) {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->hideNotificationActionButtons();
+ }
+
+ // synchronize any folders that were requested for shutdown sync
+ if (shutdownNotifierIPDlg) {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show();
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Synchronizing remote folders").append("..."));
+ }
+ KRsync krs(this, "");
+ krs.executeLogoutAutoSync();
+ if (shutdownNotifierIPDlg) {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show();
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Saving your settings..."));
+ }
+
+ if ( saveSession ) {
+ storeSession();
+ SHUTDOWN_MARKER("Session stored");
+ }
+ else {
+ discardSession();
+ SHUTDOWN_MARKER("Session discarded");
+ }
+
+ if ( state == Shutdown ) {
+ bool waitForKNotify = true;
+ if( !kapp->dcopClient()->connectDCOPSignal( "knotify", "",
+ "notifySignal(TQString,TQString,TQString,TQString,TQString,int,int,int,int)",
+ "ksmserver", "notifySlot(TQString,TQString,TQString,TQString,TQString,int,int,int,int)", false )) {
+ waitForKNotify = false;
+ }
+ if( !kapp->dcopClient()->connectDCOPSignal( "knotify", "",
+ "playingFinished(int,int)",
+ "ksmserver", "logoutSoundFinished(int,int)", false )) {
+ waitForKNotify = false;
+ }
+ // event() can return -1 if KNotifyClient short-circuits and avoids KNotify
+ logoutSoundEvent = KNotifyClient::event( 0, "exitkde" ); // KDE says good bye
+ if( logoutSoundEvent <= 0 ) {
+ waitForKNotify = false;
+ }
+ initialClientCount = clients.count();
+ if (shutdownNotifierIPDlg) {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show();
+ TQString nextClientToKill;
+ TQDateTime currentDateTime = TQDateTime::currentDateTime();
+ TQDateTime oldestFoundDateTime = currentDateTime;
+ for( KSMClient* c = clients.first(); c; c = clients.next()) {
+ if( isWM( c ) || isCM( c ) || isNotifier( c ) || isDesktop( c ) ) {
+ continue;
+ }
+ if (c->program() != "") {
+ if (c->terminationRequestTimeStamp < oldestFoundDateTime) {
+ nextClientToKill = c->program();
+ oldestFoundDateTime = c->terminationRequestTimeStamp;
+ }
+ }
+ }
+ KSMShutdownIPDlg *shutdownNotifierDlg=static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg);
+ shutdownNotifierDlg->setProgressBarTotalSteps(initialClientCount);
+ shutdownNotifierDlg->setProgressBarProgress(initialClientCount-clients.count());
+ if (nextClientToKill == "") {
+ shutdownNotifierDlg->setStatusMessage(i18n("Closing applications (%1/%2)...").arg(initialClientCount-clients.count()).arg(initialClientCount));
+ }
+ else {
+ shutdownNotifierDlg->setStatusMessage(i18n("Closing applications (%1/%2, %3)...").arg(initialClientCount-clients.count()).arg(initialClientCount).arg(nextClientToKill));
+ }
+ }
+ if( waitForKNotify ) {
+ state = WaitingForKNotify;
+ knotifyTimeoutTimer.start( 20000, true );
+ return;
+ }
+ startKilling();
+ }
+ else if ( state == Checkpoint ) {
+ for ( KSMClient* c = clients.first(); c; c = clients.next() ) {
+ SmsSaveComplete( c->connection());
+ }
+ state = Idle;
+ }
+ SHUTDOWN_MARKER("Fully shutdown");
+}
+
+void KSMServer::startKilling()
+{
+ SHUTDOWN_MARKER("startKilling");
+ knotifyTimeoutTimer.stop();
+ // kill all clients
+ state = Killing;
+ for ( KSMClient* c = clients.first(); c; c = clients.next() ) {
+ if( isWM( c ) || isCM( c ) || isNotifier( c ) || isDesktop( c ) ) { // kill the WM and CM as the last one in order to reduce flicker. Also wait to kill knotify to avoid logout delays
+ continue;
+ }
+ kdDebug( 1218 ) << "completeShutdown: client " << c->program() << "(" << c->clientId() << ")" << endl;
+ c->terminationRequestTimeStamp = TQDateTime::currentDateTime();
+ SmsDie( c->connection() );
+ }
+
+ kdDebug( 1218 ) << " We killed all clients. We have now clients.count()=" << clients.count() << endl;
+ completeKilling();
+ shutdownTimer.start( KSMSERVER_SHUTDOWN_CLIENT_UNRESPONSIVE_TIMEOUT, TRUE );
+}
+
+void KSMServer::completeKilling()
+{
+ // Activity detected; reset forcible shutdown timer...
+ if (shutdownTimer.isActive()) {
+ shutdownTimer.start( KSMSERVER_SHUTDOWN_CLIENT_UNRESPONSIVE_TIMEOUT, TRUE );
+ }
+ SHUTDOWN_MARKER("completeKilling");
+ kdDebug( 1218 ) << "KSMServer::completeKilling clients.count()=" << clients.count() << endl;
+ if( state == Killing ) {
+ bool wait = false;
+ TQString nextClientToKill;
+ TQDateTime currentDateTime = TQDateTime::currentDateTime();
+ TQDateTime oldestFoundDateTime = currentDateTime;
+ for( KSMClient* c = clients.first(); c; c = clients.next()) {
+ if( isWM( c ) || isCM( c ) || isNotifier( c ) || isDesktop( c ) ) {
+ continue;
+ }
+ if (c->program() != "") {
+ if (c->terminationRequestTimeStamp < oldestFoundDateTime) {
+ nextClientToKill = c->program();
+ oldestFoundDateTime = c->terminationRequestTimeStamp;
+ }
+ wait = true; // still waiting for clients to go away
+ }
+ }
+ if( wait ) {
+ if (shutdownNotifierIPDlg) {
+ KSMShutdownIPDlg *shutdownNotifierDlg=static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg);
+ shutdownNotifierDlg->setProgressBarTotalSteps(initialClientCount);
+ shutdownNotifierDlg->setProgressBarProgress(initialClientCount-clients.count());
+ shutdownNotifierDlg->show();
+ if (nextClientToKill == "") {
+ shutdownNotifierDlg->setStatusMessage(i18n("Closing applications (%1/%2)...").arg(initialClientCount-clients.count()).arg(initialClientCount));
+ }
+ else {
+ shutdownNotifierDlg->setStatusMessage(i18n("Closing applications (%1/%2, %3)...").arg(initialClientCount-clients.count()).arg(initialClientCount).arg(nextClientToKill));
+ }
+ }
+ return;
+ }
+ else {
+ if (shutdownNotifierIPDlg) {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->show();
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->setStatusMessage(i18n("Terminating services..."));
+ }
+ }
+ killWM();
+ }
+}
+
+void KSMServer::killWM()
+{
+ SHUTDOWN_MARKER("killWM");
+ state = KillingWM;
+ bool iswm = false;
+ if (shutdownNotifierIPDlg) {
+ static_cast<KSMShutdownIPDlg*>(shutdownNotifierIPDlg)->closeSMDialog();
+ shutdownNotifierIPDlg=0;
+ }
+ for ( KSMClient* c = clients.first(); c; c = clients.next() ) {
+ if( isDesktop( c )) {
+ iswm = true;
+ c->terminationRequestTimeStamp = TQDateTime::currentDateTime();
+ SmsDie( c->connection() );
+ }
+ if( isNotifier( c )) {
+ iswm = true;
+ c->terminationRequestTimeStamp = TQDateTime::currentDateTime();
+ SmsDie( c->connection() );
+ }
+ if( isCM( c )) {
+ iswm = true;
+ c->terminationRequestTimeStamp = TQDateTime::currentDateTime();
+ SmsDie( c->connection() );
+ }
+ if( isWM( c )) {
+ iswm = true;
+ kdDebug( 1218 ) << "killWM: client " << c->program() << "(" << c->clientId() << ")" << endl;
+ c->terminationRequestTimeStamp = TQDateTime::currentDateTime();
+ SmsDie( c->connection() );
+ }
+ }
+ if( iswm ) {
+ completeKillingWM();
+ TQTimer::singleShot( 5000, this, TQT_SLOT( timeoutWMQuit() ) );
+ }
+ else {
+ killingCompleted();
+ }
+}
+
+void KSMServer::completeKillingWM()
+{
+ SHUTDOWN_MARKER("completeKillingWM");
+ kdDebug( 1218 ) << "KSMServer::completeKillingWM clients.count()=" << clients.count() << endl;
+ if( state == KillingWM ) {
+ if( clients.isEmpty()) {
+ killingCompleted();
+ }
+ }
+}
+
+// shutdown is fully complete
+void KSMServer::killingCompleted()
+{
+ SHUTDOWN_MARKER("killingCompleted");
+ DM dmObject;
+ int dmType = dmObject.type();
+ if ((dmType == DM::NewTDM) || (dmType == DM::OldTDM) || (dmType == DM::GDM)) {
+ // Hide any remaining windows until the X server is terminated by the display manager
+ pid_t child;
+ child = fork();
+ if (child != 0) {
+ kapp->quit();
+ }
+ else if (child == 0) {
+ // If any remaining client(s) do not exit quickly (e.g. drkonqui) terminate so that they can be seen and interacted with
+ sleep(30);
+ exit(0);
+ }
+ }
+ else {
+ kapp->quit();
+ }
+}
+
+// called when KNotify performs notification for logout (not when sound is finished though)
+void KSMServer::notifySlot(TQString event ,TQString app,TQString,TQString,TQString,int present,int,int,int)
+{
+ SHUTDOWN_MARKER("notifySlot");
+ if( state != WaitingForKNotify ) {
+ SHUTDOWN_MARKER("notifySlot state != WaitingForKNotify");
+ return;
+ }
+ if( event != "exitkde" || app != "ksmserver" ) {
+ SHUTDOWN_MARKER("notifySlot event != \"exitkde\" || app != \"ksmserver\"");
+ return;
+ }
+ if( present & KNotifyClient::Sound ) { // logoutSoundFinished() will be called
+ SHUTDOWN_MARKER("notifySlot present & KNotifyClient::Sound");
+ return;
+ }
+ startKilling();
+}
+
+// This is stupid. The normal DCOP signal connected to notifySlot() above should be simply
+// emitted in KNotify only after the sound is finished playing.
+void KSMServer::logoutSoundFinished( int event, int )
+{
+ SHUTDOWN_MARKER("logoutSoundFinished");
+ if( state != WaitingForKNotify ) {
+ return;
+ }
+ if( event != logoutSoundEvent ) {
+ return;
+ }
+ startKilling();
+}
+
+void KSMServer::knotifyTimeout()
+{
+ SHUTDOWN_MARKER("knotifyTimeout");
+ if( state != WaitingForKNotify ) {
+ return;
+ }
+ startKilling();
+}
+
+void KSMServer::timeoutQuit()
+{
+ SHUTDOWN_MARKER("timeoutQuit");
+ for (KSMClient *c = clients.first(); c; c = clients.next()) {
+ SHUTDOWN_MARKER(TQString("SmsDie timeout, client %1 (%2)").arg(c->program()).arg(c->clientId()).ascii());
+ kdWarning( 1218 ) << "SmsDie timeout, client " << c->program() << "(" << c->clientId() << ")" << endl;
+ }
+ killWM();
+}
+
+void KSMServer::timeoutWMQuit()
+{
+ SHUTDOWN_MARKER("timeoutWMQuit");
+ if( state == KillingWM ) {
+ kdWarning( 1218 ) << "SmsDie WM timeout" << endl;
+ }
+ killingCompleted();
+}