1/*****************************************************************
2ksmserver - the KDE session management server
3
4Copyright 2000 Matthias Ettrich <ettrich@kde.org>
5
6relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de>
7
8some code taken from the dcopserver (part of the KDE libraries), which is
9Copyright 1999 Matthias Ettrich <ettrich@kde.org>
10Copyright 1999 Preston Brown <pbrown@kde.org>
11
12Permission is hereby granted, free of charge, to any person obtaining a copy
13of this software and associated documentation files (the "Software"), to deal
14in the Software without restriction, including without limitation the rights
15to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16copies of the Software, and to permit persons to whom the Software is
17furnished to do so, subject to the following conditions:
18
19The above copyright notice and this permission notice shall be included in
20all copies or substantial portions of the Software.
21
22THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28
29******************************************************************/
30
31
32#include <config-workspace.h>
33#include <config-unix.h> // HAVE_LIMITS_H
34
35#include <pwd.h>
36#include <sys/types.h>
37#include <sys/param.h>
38#include <sys/stat.h>
39#ifdef HAVE_SYS_TIME_H
40#include <sys/time.h>
41#endif
42#include <sys/socket.h>
43#include <sys/un.h>
44
45#include <unistd.h>
46#include <stdlib.h>
47#include <signal.h>
48#include <time.h>
49#include <errno.h>
50#include <string.h>
51#include <assert.h>
52
53#ifdef HAVE_LIMITS_H
54#include <limits.h>
55#endif
56
57#include <QPushButton>
58#include <QTimer>
59#include <QtDBus/QtDBus>
60
61#include <KApplication>
62#include <KConfig>
63#include <KConfigGroup>
64#include <KGlobal>
65#include <KLocale>
66#include <KNotification>
67#include <KStandardDirs>
68#include <KTemporaryFile>
69#include <kworkspace/kdisplaymanager.h>
70#include "server.h"
71#include "global.h"
72#include "client.h"
73#include "shutdowndlg.h"
74
75#include <solid/powermanagement.h>
76
77#include <kdebug.h>
78
79#include <QDesktopWidget>
80#include <QX11Info>
81#include <X11/Xutil.h>
82#include <X11/Xatom.h>
83
84void KSMServer::logout( int confirm, int sdtype, int sdmode )
85{
86 // KDE5: remove me
87 if (sdtype == KWorkSpace::ShutdownTypeLogout)
88 sdtype = KWorkSpace::ShutdownTypeNone;
89
90 shutdown( (KWorkSpace::ShutdownConfirm)confirm,
91 (KWorkSpace::ShutdownType)sdtype,
92 (KWorkSpace::ShutdownMode)sdmode );
93}
94
95bool KSMServer::canShutdown()
96{
97 KSharedConfig::Ptr config = KGlobal::config();
98 config->reparseConfiguration(); // config may have changed in the KControl module
99 KConfigGroup cg( config, "General");
100
101 return cg.readEntry( "offerShutdown", true ) && KDisplayManager().canShutdown();
102}
103
104void KSMServer::shutdown( KWorkSpace::ShutdownConfirm confirm,
105 KWorkSpace::ShutdownType sdtype, KWorkSpace::ShutdownMode sdmode )
106{
107 pendingShutdown.stop();
108 if( dialogActive )
109 return;
110 if( state >= Shutdown ) // already performing shutdown
111 return;
112 if( state != Idle ) // performing startup
113 {
114 // perform shutdown as soon as startup is finished, in order to avoid saving partial session
115 if( !pendingShutdown.isActive())
116 {
117 pendingShutdown.start( 1000 );
118 pendingShutdown_confirm = confirm;
119 pendingShutdown_sdtype = sdtype;
120 pendingShutdown_sdmode = sdmode;
121 }
122 return;
123 }
124
125 KSharedConfig::Ptr config = KGlobal::config();
126 config->reparseConfiguration(); // config may have changed in the KControl module
127
128 KConfigGroup cg( config, "General");
129
130 bool logoutConfirmed =
131 (confirm == KWorkSpace::ShutdownConfirmYes) ? false :
132 (confirm == KWorkSpace::ShutdownConfirmNo) ? true :
133 !cg.readEntry( "confirmLogout", true );
134 bool choose = false;
135 bool maysd = false;
136 if (cg.readEntry( "offerShutdown", true ) && KDisplayManager().canShutdown())
137 maysd = true;
138 if (!maysd) {
139 if (sdtype != KWorkSpace::ShutdownTypeNone &&
140 sdtype != KWorkSpace::ShutdownTypeDefault &&
141 logoutConfirmed)
142 return; /* unsupported fast shutdown */
143 sdtype = KWorkSpace::ShutdownTypeNone;
144 } else if (sdtype == KWorkSpace::ShutdownTypeDefault) {
145 sdtype = (KWorkSpace::ShutdownType)
146 cg.readEntry( "shutdownType", (int)KWorkSpace::ShutdownTypeNone );
147 choose = true;
148 }
149 if (sdmode == KWorkSpace::ShutdownModeDefault)
150 sdmode = KWorkSpace::ShutdownModeInteractive;
151
152 dialogActive = true;
153 QString bopt;
154 if ( !logoutConfirmed ) {
155 KApplication::kApplication()->updateUserTimestamp();
156 KSMShutdownFeedback::start(); // make the screen gray
157 QString theme = cg.readEntry( "theme", "default" );
158 logoutConfirmed = KSMShutdownDlg::confirmShutdown( maysd, choose, sdtype, bopt, theme);
159 // ###### We can't make the screen remain gray while talking to the apps,
160 // because this prevents interaction ("do you want to save", etc.)
161 // TODO: turn the feedback widget into a list of apps to be closed,
162 // with an indicator of the current status for each.
163 KSMShutdownFeedback::stop(); // make the screen become normal again
164 }
165
166 if ( logoutConfirmed ) {
167
168 // If the logout was confirmed, let's start a powermanagement inhibition.
169 // We store the cookie so we can interrupt it if the logout will be canceled
170 inhibitCookie = Solid::PowerManagement::beginSuppressingSleep();
171
172 shutdownType = sdtype;
173 shutdownMode = sdmode;
174 bootOption = bopt;
175
176 // shall we save the session on logout?
177 saveSession = ( cg.readEntry( "loginMode", "restorePreviousLogout" ) == "restorePreviousLogout" );
178
179 if ( saveSession )
180 sessionGroup = QString("Session: ") + SESSION_PREVIOUS_LOGOUT;
181
182 // Set the real desktop background to black so that exit looks
183 // clean regardless of what was on "our" desktop.
184 QPalette palette;
185 palette.setColor( kapp->desktop()->backgroundRole(), Qt::black );
186 kapp->desktop()->setPalette(palette);
187 state = Shutdown;
188 wmPhase1WaitingCount = 0;
189 saveType = saveSession?SmSaveBoth:SmSaveGlobal;
190#ifndef NO_LEGACY_SESSION_MANAGEMENT
191 performLegacySessionSave();
192#endif
193 startProtection();
194 foreach( KSMClient* c, clients ) {
195 c->resetState();
196 // Whoever came with the idea of phase 2 got it backwards
197 // unfortunately. Window manager should be the very first
198 // one saving session data, not the last one, as possible
199 // user interaction during session save may alter
200 // window positions etc.
201 // Moreover, KWin's focus stealing prevention would lead
202 // to undesired effects while session saving (dialogs
203 // wouldn't be activated), so it needs be assured that
204 // KWin will turn it off temporarily before any other
205 // user interaction takes place.
206 // Therefore, make sure the WM finishes its phase 1
207 // before others a chance to change anything.
208 // KWin will check if the session manager is ksmserver,
209 // and if yes it will save in phase 1 instead of phase 2.
210 if( isWM( c ) )
211 ++wmPhase1WaitingCount;
212 }
213 if (wmPhase1WaitingCount > 0) {
214 foreach( KSMClient* c, clients ) {
215 if( isWM( c ) )
216 SmsSaveYourself( c->connection(), saveType,
217 true, SmInteractStyleAny, false );
218 }
219 } else { // no WM, simply start them all
220 foreach( KSMClient* c, clients )
221 SmsSaveYourself( c->connection(), saveType,
222 true, SmInteractStyleAny, false );
223 }
224 if ( clients.isEmpty() )
225 completeShutdownOrCheckpoint();
226 } else {
227 KSMShutdownFeedback::logoutCanceled(); // make the screen become normal again
228 }
229 dialogActive = false;
230}
231
232void KSMServer::pendingShutdownTimeout()
233{
234 shutdown( pendingShutdown_confirm, pendingShutdown_sdtype, pendingShutdown_sdmode );
235}
236
237void KSMServer::saveCurrentSession()
238{
239 if ( state != Idle || dialogActive )
240 return;
241
242 if ( currentSession().isEmpty() || currentSession() == SESSION_PREVIOUS_LOGOUT )
243 sessionGroup = QString("Session: ") + SESSION_BY_USER;
244
245 state = Checkpoint;
246 wmPhase1WaitingCount = 0;
247 saveType = SmSaveLocal;
248 saveSession = true;
249#ifndef NO_LEGACY_SESSION_MANAGEMENT
250 performLegacySessionSave();
251#endif
252 foreach( KSMClient* c, clients ) {
253 c->resetState();
254 if( isWM( c ) )
255 ++wmPhase1WaitingCount;
256 }
257 if (wmPhase1WaitingCount > 0) {
258 foreach( KSMClient* c, clients ) {
259 if( isWM( c ) )
260 SmsSaveYourself( c->connection(), saveType, false, SmInteractStyleNone, false );
261 }
262 } else {
263 foreach( KSMClient* c, clients )
264 SmsSaveYourself( c->connection(), saveType, false, SmInteractStyleNone, false );
265 }
266 if ( clients.isEmpty() )
267 completeShutdownOrCheckpoint();
268}
269
270void KSMServer::saveCurrentSessionAs( const QString &session )
271{
272 if ( state != Idle || dialogActive )
273 return;
274 sessionGroup = "Session: " + session;
275 saveCurrentSession();
276}
277
278// callbacks
279void KSMServer::saveYourselfDone( KSMClient* client, bool success )
280{
281 if ( state == Idle ) {
282 // State saving when it's not shutdown or checkpoint. Probably
283 // a shutdown was canceled and the client is finished saving
284 // only now. Discard the saved state in order to avoid
285 // the saved data building up.
286 QStringList discard = client->discardCommand();
287 if( !discard.isEmpty())
288 executeCommand( discard );
289 return;
290 }
291 if ( success ) {
292 client->saveYourselfDone = true;
293 completeShutdownOrCheckpoint();
294 } else {
295 // fake success to make KDE's logout not block with broken
296 // apps. A perfect ksmserver would display a warning box at
297 // the very end.
298 client->saveYourselfDone = true;
299 completeShutdownOrCheckpoint();
300 }
301 startProtection();
302 if( isWM( client ) && !client->wasPhase2 && wmPhase1WaitingCount > 0 ) {
303 --wmPhase1WaitingCount;
304 // WM finished its phase1, save the rest
305 if( wmPhase1WaitingCount == 0 ) {
306 foreach( KSMClient* c, clients )
307 if( !isWM( c ))
308 SmsSaveYourself( c->connection(), saveType, saveType != SmSaveLocal,
309 saveType != SmSaveLocal ? SmInteractStyleAny : SmInteractStyleNone,
310 false );
311 }
312 }
313}
314
315void KSMServer::interactRequest( KSMClient* client, int /*dialogType*/ )
316{
317 if ( state == Shutdown || state == ClosingSubSession )
318 client->pendingInteraction = true;
319 else
320 SmsInteract( client->connection() );
321
322 handlePendingInteractions();
323}
324
325void KSMServer::interactDone( KSMClient* client, bool cancelShutdown_ )
326{
327 if ( client != clientInteracting )
328 return; // should not happen
329 clientInteracting = 0;
330 if ( cancelShutdown_ )
331 cancelShutdown( client );
332 else
333 handlePendingInteractions();
334}
335
336
337void KSMServer::phase2Request( KSMClient* client )
338{
339 client->waitForPhase2 = true;
340 client->wasPhase2 = true;
341 completeShutdownOrCheckpoint();
342 if( isWM( client ) && wmPhase1WaitingCount > 0 ) {
343 --wmPhase1WaitingCount;
344 // WM finished its phase1 and requests phase2, save the rest
345 if( wmPhase1WaitingCount == 0 ) {
346 foreach( KSMClient* c, clients )
347 if( !isWM( c ))
348 SmsSaveYourself( c->connection(), saveType, saveType != SmSaveLocal,
349 saveType != SmSaveLocal ? SmInteractStyleAny : SmInteractStyleNone,
350 false );
351 }
352 }
353}
354
355void KSMServer::handlePendingInteractions()
356{
357 if ( clientInteracting )
358 return;
359
360 foreach( KSMClient* c, clients ) {
361 if ( c->pendingInteraction ) {
362 clientInteracting = c;
363 c->pendingInteraction = false;
364 break;
365 }
366 }
367 if ( clientInteracting ) {
368 endProtection();
369 SmsInteract( clientInteracting->connection() );
370 } else {
371 startProtection();
372 }
373}
374
375
376void KSMServer::cancelShutdown( KSMClient* c )
377{
378 clientInteracting = 0;
379 kDebug() << state;
380 if ( state == ClosingSubSession ) {
381 clientsToKill.clear();
382 clientsToSave.clear();
383 emit subSessionCloseCanceled();
384 } else {
385 Solid::PowerManagement::stopSuppressingSleep(inhibitCookie);
386 kDebug( 1218 ) << "Client " << c->program() << " (" << c->clientId() << ") canceled shutdown.";
387 KSMShutdownFeedback::logoutCanceled(); // make the screen become normal again
388 KNotification::event( "cancellogout" , i18n( "Logout canceled by '%1'", c->program()),
389 QPixmap() , 0l , KNotification::DefaultEvent );
390 foreach( KSMClient* c, clients ) {
391 SmsShutdownCancelled( c->connection() );
392 if( c->saveYourselfDone ) {
393 // Discard also saved state.
394 QStringList discard = c->discardCommand();
395 if( !discard.isEmpty())
396 executeCommand( discard );
397 }
398 }
399 }
400 state = Idle;
401}
402
403void KSMServer::startProtection()
404{
405 KSharedConfig::Ptr config = KGlobal::config();
406 config->reparseConfiguration(); // config may have changed in the KControl module
407 KConfigGroup cg( config, "General" );
408
409 int timeout = cg.readEntry( "clientShutdownTimeoutSecs", 15 ) * 1000;
410
411 protectionTimer.setSingleShot( true );
412 protectionTimer.start( timeout );
413}
414
415void KSMServer::endProtection()
416{
417 protectionTimer.stop();
418}
419
420/*
421Internal protection slot, invoked when clients do not react during
422shutdown.
423*/
424void KSMServer::protectionTimeout()
425{
426 if ( ( state != Shutdown && state != Checkpoint && state != ClosingSubSession ) || clientInteracting )
427 return;
428
429 foreach( KSMClient* c, clients ) {
430 if ( !c->saveYourselfDone && !c->waitForPhase2 ) {
431 kDebug( 1218 ) << "protectionTimeout: client " << c->program() << "(" << c->clientId() << ")";
432 c->saveYourselfDone = true;
433 }
434 }
435 completeShutdownOrCheckpoint();
436 startProtection();
437}
438
439void KSMServer::completeShutdownOrCheckpoint()
440{
441 if ( state != Shutdown && state != Checkpoint && state != ClosingSubSession )
442 return;
443
444 QList<KSMClient*> pendingClients;
445 if (state == ClosingSubSession)
446 pendingClients = clientsToSave;
447 else
448 pendingClients = clients;
449
450 foreach( KSMClient* c, pendingClients ) {
451 if ( !c->saveYourselfDone && !c->waitForPhase2 )
452 return; // not done yet
453 }
454
455 // do phase 2
456 bool waitForPhase2 = false;
457 foreach( KSMClient* c, pendingClients ) {
458 if ( !c->saveYourselfDone && c->waitForPhase2 ) {
459 c->waitForPhase2 = false;
460 SmsSaveYourselfPhase2( c->connection() );
461 waitForPhase2 = true;
462 }
463 }
464 if ( waitForPhase2 )
465 return;
466
467 if ( saveSession )
468 storeSession();
469 else
470 discardSession();
471
472 if ( state == Shutdown ) {
473 KNotification *n = KNotification::event( "exitkde" , QString() , QPixmap() , 0l , KNotification::DefaultEvent ); // KDE says good bye
474 connect(n, SIGNAL(closed()) , this, SLOT(logoutSoundFinished()) );
475 // https://bugs.kde.org/show_bug.cgi?id=228005
476 // if sound is not working for some reason (e.g. no phonon
477 // backends are installed) the closed() signal never happens
478 // and logoutSoundFinished() never gets called. Add this timer to make
479 // sure the shutdown procedure continues even if sound system is broken.
480 QTimer::singleShot(5000, this, SLOT(logoutSoundTimeout()));
481 kDebug( 1218 ) << "Starting logout event";
482 state = WaitingForKNotify;
483 createLogoutEffectWidget();
484
485 } else if ( state == Checkpoint ) {
486 foreach( KSMClient* c, clients ) {
487 SmsSaveComplete( c->connection());
488 }
489 state = Idle;
490 } else { //ClosingSubSession
491 startKillingSubSession();
492 }
493
494}
495
496void KSMServer::logoutSoundTimeout()
497{
498 if (state != WaitingForKNotify) {
499 return;
500 }
501 kDebug( 1218 ) << "logout sound timeout";
502 logoutSoundFinished();
503}
504
505void KSMServer::startKilling()
506{
507 kDebug( 1218 ) << "Starting killing clients";
508 // kill all clients
509 state = Killing;
510 foreach( KSMClient* c, clients ) {
511 if( isWM( c )) // kill the WM as the last one in order to reduce flicker
512 continue;
513 kDebug( 1218 ) << "completeShutdown: client " << c->program() << "(" << c->clientId() << ")";
514 SmsDie( c->connection() );
515 }
516
517 kDebug( 1218 ) << " We killed all clients. We have now clients.count()=" <<
518 clients.count() << endl;
519 completeKilling();
520 QTimer::singleShot( 10000, this, SLOT(timeoutQuit()) );
521}
522
523void KSMServer::completeKilling()
524{
525 kDebug( 1218 ) << "KSMServer::completeKilling clients.count()=" <<
526 clients.count() << endl;
527 if( state == Killing ) {
528 bool wait = false;
529 foreach( KSMClient* c, clients ) {
530 if( isWM( c ))
531 continue;
532 wait = true; // still waiting for clients to go away
533 }
534 if( wait )
535 return;
536 killWM();
537 }
538}
539
540void KSMServer::killWM()
541{
542 if( state != Killing )
543 return;
544 delete logoutEffectWidget;
545#ifdef COMPILE_SCREEN_LOCKER
546 // To prevent kwin from becoming "defunct".
547 ScreenLocker::KSldApp::self()->cleanUp();
548#endif
549 kDebug( 1218 ) << "Starting killing WM";
550 state = KillingWM;
551 bool iswm = false;
552 foreach( KSMClient* c, clients ) {
553 if( isWM( c )) {
554 iswm = true;
555 kDebug( 1218 ) << "killWM: client " << c->program() << "(" << c->clientId() << ")";
556 SmsDie( c->connection() );
557 }
558 }
559 if( iswm ) {
560 completeKillingWM();
561 QTimer::singleShot( 5000, this, SLOT(timeoutWMQuit()) );
562 }
563 else
564 killingCompleted();
565}
566
567void KSMServer::completeKillingWM()
568{
569 kDebug( 1218 ) << "KSMServer::completeKillingWM clients.count()=" <<
570 clients.count() << endl;
571 if( state == KillingWM ) {
572 if( clients.isEmpty())
573 killingCompleted();
574 }
575}
576
577// shutdown is fully complete
578void KSMServer::killingCompleted()
579{
580 kapp->quit();
581}
582
583void KSMServer::logoutSoundFinished( )
584{
585 if( state != WaitingForKNotify )
586 return;
587 kDebug( 1218 ) << "Logout event finished";
588 startKilling();
589}
590
591void KSMServer::timeoutQuit()
592{
593 foreach( KSMClient* c, clients ) {
594 kWarning( 1218 ) << "SmsDie timeout, client " << c->program() << "(" << c->clientId() << ")" ;
595 }
596 killWM();
597}
598
599void KSMServer::timeoutWMQuit()
600{
601 if( state == KillingWM ) {
602 kWarning( 1218 ) << "SmsDie WM timeout" ;
603 }
604 killingCompleted();
605}
606
607void KSMServer::createLogoutEffectWidget()
608{
609// Ok, this is rather a hack. In order to fade the whole desktop when playing the logout
610// sound, killing applications and leaving KDE, create a dummy window that triggers
611// the logout fade effect again.
612 logoutEffectWidget = new QWidget( NULL, Qt::X11BypassWindowManagerHint );
613 logoutEffectWidget->winId(); // workaround for Qt4.3 setWindowRole() assert
614 logoutEffectWidget->setWindowRole( "logouteffect" );
615//#if !(QT_VERSION >= QT_VERSION_CHECK(4, 3, 3) || defined(QT_KDE_QT_COPY))
616// Qt doesn't set this on unmanaged windows
617 QByteArray appName = qAppName().toLatin1();
618 XClassHint class_hint;
619 class_hint.res_name = appName.data(); // application name
620 class_hint.res_class = const_cast<char *>(QX11Info::appClass()); // application class
621 XSetWMProperties( QX11Info::display(), logoutEffectWidget->winId(),
622 NULL, NULL, NULL, 0, NULL, NULL, &class_hint );
623 XChangeProperty( QX11Info::display(), logoutEffectWidget->winId(),
624 XInternAtom( QX11Info::display(), "WM_WINDOW_ROLE", False ), XA_STRING, 8, PropModeReplace,
625 (unsigned char *)"logouteffect", strlen( "logouteffect" ));
626//#endif
627 logoutEffectWidget->setGeometry( -100, -100, 1, 1 );
628 logoutEffectWidget->show();
629}
630
631void KSMServer::saveSubSession(const QString &name, QStringList saveAndClose, QStringList saveOnly)
632{
633 if( state != Idle ) { // performing startup
634 kDebug() << "not idle!" << state;
635 return;
636 }
637 kDebug() << name << saveAndClose << saveOnly;
638 state = ClosingSubSession;
639 saveType = SmSaveBoth; //both or local? what oes it mean?
640 saveSession = true;
641 sessionGroup = "SubSession: " + name;
642
643#ifndef NO_LEGACY_SESSION_MANAGEMENT
644 //performLegacySessionSave(); FIXME
645#endif
646
647 startProtection();
648 foreach( KSMClient* c, clients ) {
649 if (saveAndClose.contains(c->clientId())) {
650 c->resetState();
651 SmsSaveYourself( c->connection(), saveType,
652 true, SmInteractStyleAny, false );
653 clientsToSave << c;
654 clientsToKill << c;
655 } else if (saveOnly.contains(c->clientId())) {
656 c->resetState();
657 SmsSaveYourself( c->connection(), saveType,
658 true, SmInteractStyleAny, false );
659 clientsToSave << c;
660 }
661 }
662 completeShutdownOrCheckpoint();
663}
664
665void KSMServer::startKillingSubSession()
666{
667 kDebug( 1218 ) << "Starting killing clients";
668 // kill all clients
669 state = KillingSubSession;
670 foreach( KSMClient* c, clientsToKill ) {
671 kDebug( 1218 ) << "completeShutdown: client " << c->program() << "(" << c->clientId() << ")";
672 SmsDie( c->connection() );
673 }
674
675 kDebug( 1218 ) << " We killed some clients. We have now clients.count()=" <<
676 clients.count() << endl;
677 completeKillingSubSession();
678 QTimer::singleShot( 10000, this, SLOT(signalSubSessionClosed()) );
679}
680
681void KSMServer::completeKillingSubSession()
682{
683 kDebug( 1218 ) << "KSMServer::completeKillingSubSession clients.count()=" <<
684 clients.count() << endl;
685 if( state == KillingSubSession ) {
686 bool wait = false;
687 foreach( KSMClient* c, clientsToKill ) {
688 if( isWM( c ))
689 continue;
690 wait = true; // still waiting for clients to go away
691 }
692 if( wait )
693 return;
694 signalSubSessionClosed();
695 }
696}
697
698void KSMServer::signalSubSessionClosed()
699{
700 if( state != KillingSubSession )
701 return;
702 clientsToKill.clear();
703 clientsToSave.clear();
704 //TODO tell the subSession manager the close request was carried out
705 //so that plasma can close its stuff
706 state = Idle;
707 kDebug() << state;
708 emit subSessionClosed();
709}
710