1 | /***************************************************************** |
2 | ksmserver - the KDE session management server |
3 | |
4 | Copyright 2000 Matthias Ettrich <ettrich@kde.org> |
5 | |
6 | relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de> |
7 | |
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> |
11 | |
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: |
18 | |
19 | The above copyright notice and this permission notice shall be included in |
20 | all copies or substantial portions of the Software. |
21 | |
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. |
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 | |
84 | void 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 | |
95 | bool 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 | |
104 | void 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 | |
232 | void KSMServer::pendingShutdownTimeout() |
233 | { |
234 | shutdown( pendingShutdown_confirm, pendingShutdown_sdtype, pendingShutdown_sdmode ); |
235 | } |
236 | |
237 | void 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 | |
270 | void KSMServer::saveCurrentSessionAs( const QString &session ) |
271 | { |
272 | if ( state != Idle || dialogActive ) |
273 | return; |
274 | sessionGroup = "Session: " + session; |
275 | saveCurrentSession(); |
276 | } |
277 | |
278 | // callbacks |
279 | void 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 | |
315 | void 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 | |
325 | void 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 | |
337 | void 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 | |
355 | void 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 | |
376 | void 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 | |
403 | void 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 | |
415 | void KSMServer::endProtection() |
416 | { |
417 | protectionTimer.stop(); |
418 | } |
419 | |
420 | /* |
421 | Internal protection slot, invoked when clients do not react during |
422 | shutdown. |
423 | */ |
424 | void 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 | |
439 | void 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 | |
496 | void KSMServer::logoutSoundTimeout() |
497 | { |
498 | if (state != WaitingForKNotify) { |
499 | return; |
500 | } |
501 | kDebug( 1218 ) << "logout sound timeout" ; |
502 | logoutSoundFinished(); |
503 | } |
504 | |
505 | void 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 | |
523 | void 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 | |
540 | void 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 | |
567 | void 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 |
578 | void KSMServer::killingCompleted() |
579 | { |
580 | kapp->quit(); |
581 | } |
582 | |
583 | void KSMServer::logoutSoundFinished( ) |
584 | { |
585 | if( state != WaitingForKNotify ) |
586 | return; |
587 | kDebug( 1218 ) << "Logout event finished" ; |
588 | startKilling(); |
589 | } |
590 | |
591 | void KSMServer::timeoutQuit() |
592 | { |
593 | foreach( KSMClient* c, clients ) { |
594 | kWarning( 1218 ) << "SmsDie timeout, client " << c->program() << "(" << c->clientId() << ")" ; |
595 | } |
596 | killWM(); |
597 | } |
598 | |
599 | void KSMServer::timeoutWMQuit() |
600 | { |
601 | if( state == KillingWM ) { |
602 | kWarning( 1218 ) << "SmsDie WM timeout" ; |
603 | } |
604 | killingCompleted(); |
605 | } |
606 | |
607 | void 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 | |
631 | void 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 | |
665 | void 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 | |
681 | void 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 | |
698 | void 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 | |