1 /*****************************************************************
2 ksmserver - the KDE session management server
4 Copyright 2000 Matthias Ettrich <ettrich@kde.org>
6 relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de>
8 some code taken from the dcopserver (part of the KDE libraries), which is
9 Copyright 1999 Matthias Ettrich <ettrich@kde.org>
10 Copyright 1999 Preston Brown <pbrown@kde.org>
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
19 The above copyright notice and this permission notice shall be included in
20 all copies or substantial portions of the Software.
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 ******************************************************************/
32 #include <config-workspace.h>
33 #include <config-unix.h> // HAVE_LIMITS_H
36 #include <sys/types.h>
37 #include <sys/param.h>
39 #ifdef HAVE_SYS_TIME_H
42 #include <sys/socket.h>
57 #include <QPushButton>
59 #include <QtDBus/QtDBus>
64 #include <kstandarddirs.h>
65 #include <kapplication.h>
66 #include <ktemporaryfile.h>
67 #include <kconfiggroup.h>
68 #include <knotification.h>
69 #include <kdisplaymanager.h>
73 #include "shutdowndlg.h"
78 #include <QDesktopWidget>
80 #include <X11/Xutil.h>
81 #include <X11/Xatom.h>
83 void KSMServer::logout( int confirm
, int sdtype
, int sdmode
)
85 shutdown( (KWorkSpace::ShutdownConfirm
)confirm
,
86 (KWorkSpace::ShutdownType
)sdtype
,
87 (KWorkSpace::ShutdownMode
)sdmode
);
90 bool KSMServer::canShutdown()
92 KSharedConfig::Ptr config
= KGlobal::config();
93 config
->reparseConfiguration(); // config may have changed in the KControl module
94 KConfigGroup
cg( config
, "General");
96 return cg
.readEntry( "offerShutdown", true ) && KDisplayManager().canShutdown();
99 void KSMServer::shutdown( KWorkSpace::ShutdownConfirm confirm
,
100 KWorkSpace::ShutdownType sdtype
, KWorkSpace::ShutdownMode sdmode
)
102 pendingShutdown
.stop();
105 if( state
>= Shutdown
) // already performing shutdown
107 if( state
!= Idle
) // performing startup
109 // perform shutdown as soon as startup is finished, in order to avoid saving partial session
110 if( !pendingShutdown
.isActive())
112 pendingShutdown
.start( 1000 );
113 pendingShutdown_confirm
= confirm
;
114 pendingShutdown_sdtype
= sdtype
;
115 pendingShutdown_sdmode
= sdmode
;
120 KSharedConfig::Ptr config
= KGlobal::config();
121 config
->reparseConfiguration(); // config may have changed in the KControl module
123 KConfigGroup
cg( config
, "General");
125 bool logoutConfirmed
=
126 (confirm
== KWorkSpace::ShutdownConfirmYes
) ? false :
127 (confirm
== KWorkSpace::ShutdownConfirmNo
) ? true :
128 !cg
.readEntry( "confirmLogout", true );
130 if (cg
.readEntry( "offerShutdown", true ) && KDisplayManager().canShutdown())
133 if (sdtype
!= KWorkSpace::ShutdownTypeNone
&&
134 sdtype
!= KWorkSpace::ShutdownTypeDefault
&&
136 return; /* unsupported fast shutdown */
137 sdtype
= KWorkSpace::ShutdownTypeNone
;
138 } else if (sdtype
== KWorkSpace::ShutdownTypeDefault
)
139 sdtype
= (KWorkSpace::ShutdownType
)
140 cg
.readEntry( "shutdownType", (int)KWorkSpace::ShutdownTypeNone
);
141 if (sdmode
== KWorkSpace::ShutdownModeDefault
)
142 sdmode
= KWorkSpace::ShutdownModeInteractive
;
146 if ( !logoutConfirmed
) {
147 KSMShutdownFeedback::start(); // make the screen gray
149 KSMShutdownDlg::confirmShutdown( maysd
, sdtype
, bopt
);
150 // ###### We can't make the screen remain gray while talking to the apps,
151 // because this prevents interaction ("do you want to save", etc.)
152 // TODO: turn the feedback widget into a list of apps to be closed,
153 // with an indicator of the current status for each.
154 KSMShutdownFeedback::stop(); // make the screen become normal again
157 if ( logoutConfirmed
) {
159 shutdownType
= sdtype
;
160 shutdownMode
= sdmode
;
163 // shall we save the session on logout?
164 saveSession
= ( cg
.readEntry( "loginMode", "restorePreviousLogout" ) == "restorePreviousLogout" );
167 sessionGroup
= QString("Session: ") + SESSION_PREVIOUS_LOGOUT
;
169 // Set the real desktop background to black so that exit looks
170 // clean regardless of what was on "our" desktop.
172 palette
.setColor( kapp
->desktop()->backgroundRole(), Qt::black
);
173 kapp
->desktop()->setPalette(palette
);
175 wmPhase1WaitingCount
= 0;
176 saveType
= saveSession
?SmSaveBoth
:SmSaveGlobal
;
177 #ifndef NO_LEGACY_SESSION_MANAGEMENT
178 performLegacySessionSave();
181 foreach( KSMClient
* c
, clients
) {
183 // Whoever came with the idea of phase 2 got it backwards
184 // unfortunately. Window manager should be the very first
185 // one saving session data, not the last one, as possible
186 // user interaction during session save may alter
187 // window positions etc.
188 // Moreover, KWin's focus stealing prevention would lead
189 // to undesired effects while session saving (dialogs
190 // wouldn't be activated), so it needs be assured that
191 // KWin will turn it off temporarily before any other
192 // user interaction takes place.
193 // Therefore, make sure the WM finishes its phase 1
194 // before others a chance to change anything.
195 // KWin will check if the session manager is ksmserver,
196 // and if yes it will save in phase 1 instead of phase 2.
198 ++wmPhase1WaitingCount
;
199 SmsSaveYourself( c
->connection(), saveType
,
200 true, SmInteractStyleAny
, false );
204 if( wmPhase1WaitingCount
== 0 ) { // no WM, simply start them all
205 foreach( KSMClient
* c
, clients
)
206 SmsSaveYourself( c
->connection(), saveType
,
207 true, SmInteractStyleAny
, false );
209 if ( clients
.isEmpty() )
210 completeShutdownOrCheckpoint();
212 dialogActive
= false;
215 void KSMServer::pendingShutdownTimeout()
217 shutdown( pendingShutdown_confirm
, pendingShutdown_sdtype
, pendingShutdown_sdmode
);
220 void KSMServer::saveCurrentSession()
222 if ( state
!= Idle
|| dialogActive
)
225 if ( currentSession().isEmpty() || currentSession() == SESSION_PREVIOUS_LOGOUT
)
226 sessionGroup
= QString("Session: ") + SESSION_BY_USER
;
229 wmPhase1WaitingCount
= 0;
230 saveType
= SmSaveLocal
;
232 #ifndef NO_LEGACY_SESSION_MANAGEMENT
233 performLegacySessionSave();
235 foreach( KSMClient
* c
, clients
) {
238 ++wmPhase1WaitingCount
;
239 SmsSaveYourself( c
->connection(), saveType
, false, SmInteractStyleNone
, false );
242 if( wmPhase1WaitingCount
== 0 ) {
243 foreach( KSMClient
* c
, clients
)
244 SmsSaveYourself( c
->connection(), saveType
, false, SmInteractStyleNone
, false );
246 if ( clients
.isEmpty() )
247 completeShutdownOrCheckpoint();
250 void KSMServer::saveCurrentSessionAs( const QString
&session
)
252 if ( state
!= Idle
|| dialogActive
)
254 sessionGroup
= "Session: " + session
;
255 saveCurrentSession();
259 void KSMServer::saveYourselfDone( KSMClient
* client
, bool success
)
261 if ( state
== Idle
) {
262 // State saving when it's not shutdown or checkpoint. Probably
263 // a shutdown was canceled and the client is finished saving
264 // only now. Discard the saved state in order to avoid
265 // the saved data building up.
266 QStringList discard
= client
->discardCommand();
267 if( !discard
.isEmpty())
268 executeCommand( discard
);
272 client
->saveYourselfDone
= true;
273 completeShutdownOrCheckpoint();
275 // fake success to make KDE's logout not block with broken
276 // apps. A perfect ksmserver would display a warning box at
278 client
->saveYourselfDone
= true;
279 completeShutdownOrCheckpoint();
282 if( isWM( client
) && !client
->wasPhase2
&& wmPhase1WaitingCount
> 0 ) {
283 --wmPhase1WaitingCount
;
284 // WM finished its phase1, save the rest
285 if( wmPhase1WaitingCount
== 0 ) {
286 foreach( KSMClient
* c
, clients
)
288 SmsSaveYourself( c
->connection(), saveType
, saveType
!= SmSaveLocal
,
289 saveType
!= SmSaveLocal
? SmInteractStyleAny
: SmInteractStyleNone
,
295 void KSMServer::interactRequest( KSMClient
* client
, int /*dialogType*/ )
297 if ( state
== Shutdown
)
298 client
->pendingInteraction
= true;
300 SmsInteract( client
->connection() );
302 handlePendingInteractions();
305 void KSMServer::interactDone( KSMClient
* client
, bool cancelShutdown_
)
307 if ( client
!= clientInteracting
)
308 return; // should not happen
309 clientInteracting
= 0;
310 if ( cancelShutdown_
)
311 cancelShutdown( client
);
313 handlePendingInteractions();
317 void KSMServer::phase2Request( KSMClient
* client
)
319 client
->waitForPhase2
= true;
320 client
->wasPhase2
= true;
321 completeShutdownOrCheckpoint();
322 if( isWM( client
) && wmPhase1WaitingCount
> 0 ) {
323 --wmPhase1WaitingCount
;
324 // WM finished its phase1 and requests phase2, save the rest
325 if( wmPhase1WaitingCount
== 0 ) {
326 foreach( KSMClient
* c
, clients
)
328 SmsSaveYourself( c
->connection(), saveType
, saveType
!= SmSaveLocal
,
329 saveType
!= SmSaveLocal
? SmInteractStyleAny
: SmInteractStyleNone
,
335 void KSMServer::handlePendingInteractions()
337 if ( clientInteracting
)
340 foreach( KSMClient
* c
, clients
) {
341 if ( c
->pendingInteraction
) {
342 clientInteracting
= c
;
343 c
->pendingInteraction
= false;
347 if ( clientInteracting
) {
349 SmsInteract( clientInteracting
->connection() );
356 void KSMServer::cancelShutdown( KSMClient
* c
)
358 kDebug( 1218 ) << "Client " << c
->program() << " (" << c
->clientId() << ") canceled shutdown.";
359 KNotification::event( "cancellogout" , i18n( "Logout canceled by '%1'", c
->program()),
360 QPixmap() , 0l , KNotification::DefaultEvent
);
361 clientInteracting
= 0;
362 foreach( KSMClient
* c
, clients
) {
363 SmsShutdownCancelled( c
->connection() );
364 if( c
->saveYourselfDone
) {
365 // Discard also saved state.
366 QStringList discard
= c
->discardCommand();
367 if( !discard
.isEmpty())
368 executeCommand( discard
);
374 void KSMServer::startProtection()
376 protectionTimer
.setSingleShot( true );
377 protectionTimer
.start( 10000 );
380 void KSMServer::endProtection()
382 protectionTimer
.stop();
386 Internal protection slot, invoked when clients do not react during
389 void KSMServer::protectionTimeout()
391 if ( ( state
!= Shutdown
&& state
!= Checkpoint
) || clientInteracting
)
394 foreach( KSMClient
* c
, clients
) {
395 if ( !c
->saveYourselfDone
&& !c
->waitForPhase2
) {
396 kDebug( 1218 ) << "protectionTimeout: client " << c
->program() << "(" << c
->clientId() << ")";
397 c
->saveYourselfDone
= true;
400 completeShutdownOrCheckpoint();
404 void KSMServer::completeShutdownOrCheckpoint()
406 if ( state
!= Shutdown
&& state
!= Checkpoint
)
409 foreach( KSMClient
* c
, clients
) {
410 if ( !c
->saveYourselfDone
&& !c
->waitForPhase2
)
411 return; // not done yet
415 bool waitForPhase2
= false;
416 foreach( KSMClient
* c
, clients
) {
417 if ( !c
->saveYourselfDone
&& c
->waitForPhase2
) {
418 c
->waitForPhase2
= false;
419 SmsSaveYourselfPhase2( c
->connection() );
420 waitForPhase2
= true;
431 if ( state
== Shutdown
) {
433 #warning KNotify TODO
435 /* How to check if the daemon is still running. We will not start the knotify daemon just for playing a sound before shutdown. or do wa want that ?
437 knotifySignals = QDBus::sessionBus().findInterface("org.kde.knotify",
438 "/knotify", "org.kde.KNotify" );
439 if( !knotifySignals->isValid())
440 kWarning() << "knotify not running?" ;
443 KNotification
*n
= KNotification::event( "exitkde" , QString() , QPixmap() , 0l , KNotification::DefaultEvent
); // KDE says good bye
444 connect(n
, SIGNAL( closed() ) , this, SLOT(logoutSoundFinished()) );
445 kDebug( 1218 ) << "Starting logout event";
446 state
= WaitingForKNotify
;
447 createLogoutEffectWidget();
449 } else if ( state
== Checkpoint
) {
450 foreach( KSMClient
* c
, clients
) {
451 SmsSaveComplete( c
->connection());
457 void KSMServer::startKilling()
459 kDebug( 1218 ) << "Starting killing clients";
462 foreach( KSMClient
* c
, clients
) {
463 if( isWM( c
)) // kill the WM as the last one in order to reduce flicker
465 kDebug( 1218 ) << "completeShutdown: client " << c
->program() << "(" << c
->clientId() << ")";
466 SmsDie( c
->connection() );
469 kDebug( 1218 ) << " We killed all clients. We have now clients.count()=" <<
470 clients
.count() << endl
;
472 QTimer::singleShot( 10000, this, SLOT( timeoutQuit() ) );
475 void KSMServer::completeKilling()
477 kDebug( 1218 ) << "KSMServer::completeKilling clients.count()=" <<
478 clients
.count() << endl
;
479 if( state
== Killing
) {
481 foreach( KSMClient
* c
, clients
) {
484 wait
= true; // still waiting for clients to go away
492 void KSMServer::killWM()
494 if( state
!= Killing
)
496 delete logoutEffectWidget
;
497 kDebug( 1218 ) << "Starting killing WM";
500 foreach( KSMClient
* c
, clients
) {
503 kDebug( 1218 ) << "killWM: client " << c
->program() << "(" << c
->clientId() << ")";
504 SmsDie( c
->connection() );
509 QTimer::singleShot( 5000, this, SLOT( timeoutWMQuit() ) );
515 void KSMServer::completeKillingWM()
517 kDebug( 1218 ) << "KSMServer::completeKillingWM clients.count()=" <<
518 clients
.count() << endl
;
519 if( state
== KillingWM
) {
520 if( clients
.isEmpty())
525 // shutdown is fully complete
526 void KSMServer::killingCompleted()
531 void KSMServer::logoutSoundFinished( )
533 if( state
!= WaitingForKNotify
)
535 kDebug( 1218 ) << "Logout event finished";
539 void KSMServer::timeoutQuit()
541 foreach( KSMClient
* c
, clients
) {
542 kWarning( 1218 ) << "SmsDie timeout, client " << c
->program() << "(" << c
->clientId() << ")" ;
547 void KSMServer::timeoutWMQuit()
549 if( state
== KillingWM
) {
550 kWarning( 1218 ) << "SmsDie WM timeout" ;
555 void KSMServer::createLogoutEffectWidget()
557 // Ok, this is rather a hack. In order to fade the whole desktop when playing the logout
558 // sound, killing applications and leaving KDE, create a dummy window that triggers
559 // the logout fade effect again.
560 logoutEffectWidget
= new QWidget( NULL
, Qt::X11BypassWindowManagerHint
);
561 logoutEffectWidget
->winId(); // workaround for Qt4.3 setWindowRole() assert
562 logoutEffectWidget
->setWindowRole( "logouteffect" );
563 //#if !(QT_VERSION >= QT_VERSION_CHECK(4, 3, 3) || defined(QT_KDE_QT_COPY))
564 // Qt doesn't set this on unmanaged windows
565 QByteArray appName
= qAppName().toLatin1();
566 XClassHint class_hint
;
567 class_hint
.res_name
= appName
.data(); // application name
568 class_hint
.res_class
= const_cast<char *>(QX11Info::appClass()); // application class
569 XSetWMProperties( QX11Info::display(), logoutEffectWidget
->winId(),
570 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &class_hint
);
571 XChangeProperty( QX11Info::display(), logoutEffectWidget
->winId(),
572 XInternAtom( QX11Info::display(), "WM_WINDOW_ROLE", False
), XA_STRING
, 8, PropModeReplace
,
573 (unsigned char *)"logouteffect", strlen( "logouteffect" ));
575 logoutEffectWidget
->setGeometry( -100, -100, 1, 1 );
576 logoutEffectWidget
->show();