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 void KSMServer::shutdown( KWorkSpace::ShutdownConfirm confirm
,
91 KWorkSpace::ShutdownType sdtype
, KWorkSpace::ShutdownMode sdmode
)
93 pendingShutdown
.stop();
96 if( state
>= Shutdown
) // already performing shutdown
98 if( state
!= Idle
) // performing startup
100 // perform shutdown as soon as startup is finished, in order to avoid saving partial session
101 if( !pendingShutdown
.isActive())
103 pendingShutdown
.start( 1000 );
104 pendingShutdown_confirm
= confirm
;
105 pendingShutdown_sdtype
= sdtype
;
106 pendingShutdown_sdmode
= sdmode
;
111 KSharedConfig::Ptr config
= KGlobal::config();
112 config
->reparseConfiguration(); // config may have changed in the KControl module
114 KConfigGroup
cg( config
, "General");
116 bool logoutConfirmed
=
117 (confirm
== KWorkSpace::ShutdownConfirmYes
) ? false :
118 (confirm
== KWorkSpace::ShutdownConfirmNo
) ? true :
119 !cg
.readEntry( "confirmLogout", true );
121 if (cg
.readEntry( "offerShutdown", true ) && KDisplayManager().canShutdown())
124 if (sdtype
!= KWorkSpace::ShutdownTypeNone
&&
125 sdtype
!= KWorkSpace::ShutdownTypeDefault
&&
127 return; /* unsupported fast shutdown */
128 sdtype
= KWorkSpace::ShutdownTypeNone
;
129 } else if (sdtype
== KWorkSpace::ShutdownTypeDefault
)
130 sdtype
= (KWorkSpace::ShutdownType
)
131 cg
.readEntry( "shutdownType", (int)KWorkSpace::ShutdownTypeNone
);
132 if (sdmode
== KWorkSpace::ShutdownModeDefault
)
133 sdmode
= KWorkSpace::ShutdownModeInteractive
;
137 if ( !logoutConfirmed
) {
138 KSMShutdownFeedback::start(); // make the screen gray
140 KSMShutdownDlg::confirmShutdown( maysd
, sdtype
, bopt
);
141 // ###### We can't make the screen remain gray while talking to the apps,
142 // because this prevents interaction ("do you want to save", etc.)
143 // TODO: turn the feedback widget into a list of apps to be closed,
144 // with an indicator of the current status for each.
145 KSMShutdownFeedback::stop(); // make the screen become normal again
148 if ( logoutConfirmed
) {
150 shutdownType
= sdtype
;
151 shutdownMode
= sdmode
;
154 // shall we save the session on logout?
155 saveSession
= ( cg
.readEntry( "loginMode", "restorePreviousLogout" ) == "restorePreviousLogout" );
158 sessionGroup
= QString("Session: ") + SESSION_PREVIOUS_LOGOUT
;
160 // Set the real desktop background to black so that exit looks
161 // clean regardless of what was on "our" desktop.
163 palette
.setColor( kapp
->desktop()->backgroundRole(), Qt::black
);
164 kapp
->desktop()->setPalette(palette
);
166 wmPhase1WaitingCount
= 0;
167 saveType
= saveSession
?SmSaveBoth
:SmSaveGlobal
;
168 #ifndef NO_LEGACY_SESSION_MANAGEMENT
169 performLegacySessionSave();
172 foreach( KSMClient
* c
, clients
) {
174 // Whoever came with the idea of phase 2 got it backwards
175 // unfortunately. Window manager should be the very first
176 // one saving session data, not the last one, as possible
177 // user interaction during session save may alter
178 // window positions etc.
179 // Moreover, KWin's focus stealing prevention would lead
180 // to undesired effects while session saving (dialogs
181 // wouldn't be activated), so it needs be assured that
182 // KWin will turn it off temporarily before any other
183 // user interaction takes place.
184 // Therefore, make sure the WM finishes its phase 1
185 // before others a chance to change anything.
186 // KWin will check if the session manager is ksmserver,
187 // and if yes it will save in phase 1 instead of phase 2.
189 ++wmPhase1WaitingCount
;
190 SmsSaveYourself( c
->connection(), saveType
,
191 true, SmInteractStyleAny
, false );
195 if( wmPhase1WaitingCount
== 0 ) { // no WM, simply start them all
196 foreach( KSMClient
* c
, clients
)
197 SmsSaveYourself( c
->connection(), saveType
,
198 true, SmInteractStyleAny
, false );
200 if ( clients
.isEmpty() )
201 completeShutdownOrCheckpoint();
203 dialogActive
= false;
206 void KSMServer::pendingShutdownTimeout()
208 shutdown( pendingShutdown_confirm
, pendingShutdown_sdtype
, pendingShutdown_sdmode
);
211 void KSMServer::saveCurrentSession()
213 if ( state
!= Idle
|| dialogActive
)
216 if ( currentSession().isEmpty() || currentSession() == SESSION_PREVIOUS_LOGOUT
)
217 sessionGroup
= QString("Session: ") + SESSION_BY_USER
;
220 wmPhase1WaitingCount
= 0;
221 saveType
= SmSaveLocal
;
223 #ifndef NO_LEGACY_SESSION_MANAGEMENT
224 performLegacySessionSave();
226 foreach( KSMClient
* c
, clients
) {
229 ++wmPhase1WaitingCount
;
230 SmsSaveYourself( c
->connection(), saveType
, false, SmInteractStyleNone
, false );
233 if( wmPhase1WaitingCount
== 0 ) {
234 foreach( KSMClient
* c
, clients
)
235 SmsSaveYourself( c
->connection(), saveType
, false, SmInteractStyleNone
, false );
237 if ( clients
.isEmpty() )
238 completeShutdownOrCheckpoint();
241 void KSMServer::saveCurrentSessionAs( const QString
&session
)
243 if ( state
!= Idle
|| dialogActive
)
245 sessionGroup
= "Session: " + session
;
246 saveCurrentSession();
250 void KSMServer::saveYourselfDone( KSMClient
* client
, bool success
)
252 if ( state
== Idle
) {
253 // State saving when it's not shutdown or checkpoint. Probably
254 // a shutdown was canceled and the client is finished saving
255 // only now. Discard the saved state in order to avoid
256 // the saved data building up.
257 QStringList discard
= client
->discardCommand();
258 if( !discard
.isEmpty())
259 executeCommand( discard
);
263 client
->saveYourselfDone
= true;
264 completeShutdownOrCheckpoint();
266 // fake success to make KDE's logout not block with broken
267 // apps. A perfect ksmserver would display a warning box at
269 client
->saveYourselfDone
= true;
270 completeShutdownOrCheckpoint();
273 if( isWM( client
) && !client
->wasPhase2
&& wmPhase1WaitingCount
> 0 ) {
274 --wmPhase1WaitingCount
;
275 // WM finished its phase1, save the rest
276 if( wmPhase1WaitingCount
== 0 ) {
277 foreach( KSMClient
* c
, clients
)
279 SmsSaveYourself( c
->connection(), saveType
, saveType
!= SmSaveLocal
,
280 saveType
!= SmSaveLocal
? SmInteractStyleAny
: SmInteractStyleNone
,
286 void KSMServer::interactRequest( KSMClient
* client
, int /*dialogType*/ )
288 if ( state
== Shutdown
)
289 client
->pendingInteraction
= true;
291 SmsInteract( client
->connection() );
293 handlePendingInteractions();
296 void KSMServer::interactDone( KSMClient
* client
, bool cancelShutdown_
)
298 if ( client
!= clientInteracting
)
299 return; // should not happen
300 clientInteracting
= 0;
301 if ( cancelShutdown_
)
302 cancelShutdown( client
);
304 handlePendingInteractions();
308 void KSMServer::phase2Request( KSMClient
* client
)
310 client
->waitForPhase2
= true;
311 client
->wasPhase2
= true;
312 completeShutdownOrCheckpoint();
313 if( isWM( client
) && wmPhase1WaitingCount
> 0 ) {
314 --wmPhase1WaitingCount
;
315 // WM finished its phase1 and requests phase2, save the rest
316 if( wmPhase1WaitingCount
== 0 ) {
317 foreach( KSMClient
* c
, clients
)
319 SmsSaveYourself( c
->connection(), saveType
, saveType
!= SmSaveLocal
,
320 saveType
!= SmSaveLocal
? SmInteractStyleAny
: SmInteractStyleNone
,
326 void KSMServer::handlePendingInteractions()
328 if ( clientInteracting
)
331 foreach( KSMClient
* c
, clients
) {
332 if ( c
->pendingInteraction
) {
333 clientInteracting
= c
;
334 c
->pendingInteraction
= false;
338 if ( clientInteracting
) {
340 SmsInteract( clientInteracting
->connection() );
347 void KSMServer::cancelShutdown( KSMClient
* c
)
349 kDebug( 1218 ) << "Client " << c
->program() << " (" << c
->clientId() << ") canceled shutdown.";
350 KNotification::event( "cancellogout" , i18n( "Logout canceled by '%1'", c
->program()),
351 QPixmap() , 0l , KNotification::DefaultEvent
);
352 clientInteracting
= 0;
353 foreach( KSMClient
* c
, clients
) {
354 SmsShutdownCancelled( c
->connection() );
355 if( c
->saveYourselfDone
) {
356 // Discard also saved state.
357 QStringList discard
= c
->discardCommand();
358 if( !discard
.isEmpty())
359 executeCommand( discard
);
365 void KSMServer::startProtection()
367 protectionTimer
.setSingleShot( true );
368 protectionTimer
.start( 10000 );
371 void KSMServer::endProtection()
373 protectionTimer
.stop();
377 Internal protection slot, invoked when clients do not react during
380 void KSMServer::protectionTimeout()
382 if ( ( state
!= Shutdown
&& state
!= Checkpoint
) || clientInteracting
)
385 foreach( KSMClient
* c
, clients
) {
386 if ( !c
->saveYourselfDone
&& !c
->waitForPhase2
) {
387 kDebug( 1218 ) << "protectionTimeout: client " << c
->program() << "(" << c
->clientId() << ")";
388 c
->saveYourselfDone
= true;
391 completeShutdownOrCheckpoint();
395 void KSMServer::completeShutdownOrCheckpoint()
397 if ( state
!= Shutdown
&& state
!= Checkpoint
)
400 foreach( KSMClient
* c
, clients
) {
401 if ( !c
->saveYourselfDone
&& !c
->waitForPhase2
)
402 return; // not done yet
406 bool waitForPhase2
= false;
407 foreach( KSMClient
* c
, clients
) {
408 if ( !c
->saveYourselfDone
&& c
->waitForPhase2
) {
409 c
->waitForPhase2
= false;
410 SmsSaveYourselfPhase2( c
->connection() );
411 waitForPhase2
= true;
422 if ( state
== Shutdown
) {
424 #warning KNotify TODO
426 /* 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 ?
428 knotifySignals = QDBus::sessionBus().findInterface("org.kde.knotify",
429 "/knotify", "org.kde.KNotify" );
430 if( !knotifySignals->isValid())
431 kWarning() << "knotify not running?" ;
434 KNotification
*n
= KNotification::event( "exitkde" , QString() , QPixmap() , 0l , KNotification::DefaultEvent
); // KDE says good bye
435 connect(n
, SIGNAL( closed() ) , this, SLOT(logoutSoundFinished()) );
436 kDebug( 1218 ) << "Starting logout event";
437 state
= WaitingForKNotify
;
438 createLogoutEffectWidget();
440 } else if ( state
== Checkpoint
) {
441 foreach( KSMClient
* c
, clients
) {
442 SmsSaveComplete( c
->connection());
448 void KSMServer::startKilling()
450 kDebug( 1218 ) << "Starting killing clients";
453 foreach( KSMClient
* c
, clients
) {
454 if( isWM( c
)) // kill the WM as the last one in order to reduce flicker
456 kDebug( 1218 ) << "completeShutdown: client " << c
->program() << "(" << c
->clientId() << ")";
457 SmsDie( c
->connection() );
460 kDebug( 1218 ) << " We killed all clients. We have now clients.count()=" <<
461 clients
.count() << endl
;
463 QTimer::singleShot( 10000, this, SLOT( timeoutQuit() ) );
466 void KSMServer::completeKilling()
468 kDebug( 1218 ) << "KSMServer::completeKilling clients.count()=" <<
469 clients
.count() << endl
;
470 if( state
== Killing
) {
472 foreach( KSMClient
* c
, clients
) {
475 wait
= true; // still waiting for clients to go away
483 void KSMServer::killWM()
485 if( state
!= Killing
)
487 delete logoutEffectWidget
;
488 kDebug( 1218 ) << "Starting killing WM";
491 foreach( KSMClient
* c
, clients
) {
494 kDebug( 1218 ) << "killWM: client " << c
->program() << "(" << c
->clientId() << ")";
495 SmsDie( c
->connection() );
500 QTimer::singleShot( 5000, this, SLOT( timeoutWMQuit() ) );
506 void KSMServer::completeKillingWM()
508 kDebug( 1218 ) << "KSMServer::completeKillingWM clients.count()=" <<
509 clients
.count() << endl
;
510 if( state
== KillingWM
) {
511 if( clients
.isEmpty())
516 // shutdown is fully complete
517 void KSMServer::killingCompleted()
522 void KSMServer::logoutSoundFinished( )
524 if( state
!= WaitingForKNotify
)
526 kDebug( 1218 ) << "Logout event finished";
530 void KSMServer::timeoutQuit()
532 foreach( KSMClient
* c
, clients
) {
533 kWarning( 1218 ) << "SmsDie timeout, client " << c
->program() << "(" << c
->clientId() << ")" ;
538 void KSMServer::timeoutWMQuit()
540 if( state
== KillingWM
) {
541 kWarning( 1218 ) << "SmsDie WM timeout" ;
546 void KSMServer::createLogoutEffectWidget()
548 // Ok, this is rather a hack. In order to fade the whole desktop when playing the logout
549 // sound, killing applications and leaving KDE, create a dummy window that triggers
550 // the logout fade effect again.
551 logoutEffectWidget
= new QWidget( NULL
, Qt::X11BypassWindowManagerHint
);
552 logoutEffectWidget
->winId(); // workaround for Qt4.3 setWindowRole() assert
553 logoutEffectWidget
->setWindowRole( "logouteffect" );
554 //#if !(QT_VERSION >= QT_VERSION_CHECK(4, 3, 3) || defined(QT_KDE_QT_COPY))
555 // Qt doesn't set this on unmanaged windows
556 QByteArray appName
= qAppName().toLatin1();
557 XClassHint class_hint
;
558 class_hint
.res_name
= appName
.data(); // application name
559 class_hint
.res_class
= const_cast<char *>(QX11Info::appClass()); // application class
560 XSetWMProperties( QX11Info::display(), logoutEffectWidget
->winId(),
561 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &class_hint
);
562 XChangeProperty( QX11Info::display(), logoutEffectWidget
->winId(),
563 XInternAtom( QX11Info::display(), "WM_WINDOW_ROLE", False
), XA_STRING
, 8, PropModeReplace
,
564 (unsigned char *)"logouteffect", strlen( "logouteffect" ));
566 logoutEffectWidget
->setGeometry( -100, -100, 1, 1 );
567 logoutEffectWidget
->show();