not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kwin / sm.cpp
blob2e90923526aa836864a357722fffe9992e7bc5b5
1 /********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
5 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *********************************************************************/
22 #include "sm.h"
24 #include <unistd.h>
25 #include <stdlib.h>
26 #include <pwd.h>
27 #include <fixx11h.h>
28 #include <kconfig.h>
29 #include <kglobal.h>
31 #include "workspace.h"
32 #include "client.h"
33 #include <QSocketNotifier>
34 #include <QSessionManager>
35 #include <kdebug.h>
37 namespace KWin
40 bool SessionManager::saveState( QSessionManager& sm )
42 // If the session manager is ksmserver, save stacking
43 // order, active window, active desktop etc. in phase 1,
44 // as ksmserver assures no interaction will be done
45 // before the WM finishes phase 1. Saving in phase 2 is
46 // too late, as possible user interaction may change some things.
47 // Phase2 is still needed though (ICCCM 5.2)
48 char* sm_vendor = SmcVendor( static_cast< SmcConn >( sm.handle()));
49 bool ksmserver = qstrcmp( sm_vendor, "KDE" ) == 0;
50 free( sm_vendor );
51 if ( !sm.isPhase2() )
53 Workspace::self()->sessionSaveStarted();
54 if( ksmserver ) // save stacking order etc. before "save file?" etc. dialogs change it
55 Workspace::self()->storeSession( kapp->sessionConfig(), SMSavePhase0 );
56 sm.release(); // Qt doesn't automatically release in this case (bug?)
57 sm.requestPhase2();
58 return true;
60 Workspace::self()->storeSession( kapp->sessionConfig(), ksmserver ? SMSavePhase2 : SMSavePhase2Full );
61 kapp->sessionConfig()->sync();
62 return true;
65 // I bet this is broken, just like everywhere else in KDE
66 bool SessionManager::commitData( QSessionManager& sm )
68 if ( !sm.isPhase2() )
69 Workspace::self()->sessionSaveStarted();
70 return true;
73 // Workspace
75 /*!
76 Stores the current session in the config file
78 \sa loadSessionInfo()
80 void Workspace::storeSession( KConfig* config, SMSavePhase phase )
82 KConfigGroup cg(config, "Session");
83 int count = 0;
84 int active_client = -1;
85 for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it)
87 Client* c = (*it);
88 QByteArray sessionId = c->sessionId();
89 QByteArray wmCommand = c->wmCommand();
90 if ( sessionId.isEmpty() )
91 // remember also applications that are not XSMP capable
92 // and use the obsolete WM_COMMAND / WM_SAVE_YOURSELF
93 if ( wmCommand.isEmpty() )
94 continue;
95 count++;
96 if( c->isActive())
97 active_client = count;
98 QString n = QString::number(count);
99 if( phase == SMSavePhase2 || phase == SMSavePhase2Full )
101 cg.writeEntry( QString("sessionId")+n, sessionId.constData() );
102 cg.writeEntry( QString("windowRole")+n, c->windowRole().constData() );
103 cg.writeEntry( QString("wmCommand")+n, wmCommand.constData() );
104 cg.writeEntry( QString("wmClientMachine")+n, c->wmClientMachine( true ).constData() );
105 cg.writeEntry( QString("resourceName")+n, c->resourceName().constData() );
106 cg.writeEntry( QString("resourceClass")+n, c->resourceClass().constData() );
107 cg.writeEntry( QString("geometry")+n, QRect( c->calculateGravitation(true), c->clientSize() ) ); // FRAME
108 cg.writeEntry( QString("restore")+n, c->geometryRestore() );
109 cg.writeEntry( QString("fsrestore")+n, c->geometryFSRestore() );
110 cg.writeEntry( QString("maximize")+n, (int) c->maximizeMode() );
111 cg.writeEntry( QString("fullscreen")+n, (int) c->fullScreenMode() );
112 cg.writeEntry( QString("desktop")+n, c->desktop() );
113 // the config entry is called "iconified" for back. comp. reasons
114 // (kconf_update script for updating session files would be too complicated)
115 cg.writeEntry( QString("iconified")+n, c->isMinimized() );
116 // the config entry is called "sticky" for back. comp. reasons
117 cg.writeEntry( QString("sticky")+n, c->isOnAllDesktops() );
118 cg.writeEntry( QString("shaded")+n, c->isShade() );
119 // the config entry is called "staysOnTop" for back. comp. reasons
120 cg.writeEntry( QString("staysOnTop")+n, c->keepAbove() );
121 cg.writeEntry( QString("keepBelow")+n, c->keepBelow() );
122 cg.writeEntry( QString("skipTaskbar")+n, c->skipTaskbar( true ) );
123 cg.writeEntry( QString("skipPager")+n, c->skipPager() );
124 // not really just set by user, but name kept for back. comp. reasons
125 cg.writeEntry( QString("userNoBorder")+n, c->noBorder() );
126 cg.writeEntry( QString("windowType")+n, windowTypeToTxt( c->windowType()));
127 cg.writeEntry( QString("shortcut")+n, c->shortcut().toString());
128 cg.writeEntry( QString("stackingOrder")+n, unconstrained_stacking_order.indexOf( c ));
131 if( phase == SMSavePhase0 )
133 // it would be much simpler to save these values to the config file,
134 // but both Qt and KDE treat phase1 and phase2 separately,
135 // which results in different sessionkey and different config file :(
136 session_active_client = active_client;
137 session_desktop = currentDesktop();
139 else if( phase == SMSavePhase2 )
141 cg.writeEntry( "count", count );
142 cg.writeEntry( "active", session_active_client );
143 cg.writeEntry( "desktop", session_desktop );
145 else // SMSavePhase2Full
147 cg.writeEntry( "count", count );
148 cg.writeEntry( "active", session_active_client );
149 cg.writeEntry( "desktop", currentDesktop());
155 Loads the session information from the config file.
157 \sa storeSession()
159 void Workspace::loadSessionInfo()
161 session.clear();
162 KConfigGroup cg(kapp->sessionConfig(), "Session");
163 int count = cg.readEntry( "count",0 );
164 int active_client = cg.readEntry( "active",0 );
165 for ( int i = 1; i <= count; i++ )
167 QString n = QString::number(i);
168 SessionInfo* info = new SessionInfo;
169 session.append( info );
170 info->sessionId = cg.readEntry( QString("sessionId")+n, QString() ).toLatin1();
171 info->windowRole = cg.readEntry( QString("windowRole")+n, QString() ).toLatin1();
172 info->wmCommand = cg.readEntry( QString("wmCommand")+n, QString() ).toLatin1();
173 info->wmClientMachine = cg.readEntry( QString("wmClientMachine")+n, QString() ).toLatin1();
174 info->resourceName = cg.readEntry( QString("resourceName")+n, QString() ).toLatin1();
175 info->resourceClass = cg.readEntry( QString("resourceClass")+n, QString() ).toLower().toLatin1();
176 info->geometry = cg.readEntry( QString("geometry")+n,QRect() );
177 info->restore = cg.readEntry( QString("restore")+n,QRect() );
178 info->fsrestore = cg.readEntry( QString("fsrestore")+n,QRect() );
179 info->maximized = cg.readEntry( QString("maximize")+n, 0 );
180 info->fullscreen = cg.readEntry( QString("fullscreen")+n, 0 );
181 info->desktop = cg.readEntry( QString("desktop")+n, 0 );
182 info->minimized = cg.readEntry( QString("iconified")+n, false );
183 info->onAllDesktops = cg.readEntry( QString("sticky")+n, false );
184 info->shaded = cg.readEntry( QString("shaded")+n, false );
185 info->keepAbove = cg.readEntry( QString("staysOnTop")+n, false );
186 info->keepBelow = cg.readEntry( QString("keepBelow")+n, false );
187 info->skipTaskbar = cg.readEntry( QString("skipTaskbar")+n, false );
188 info->skipPager = cg.readEntry( QString("skipPager")+n, false );
189 info->noBorder = cg.readEntry( QString("userNoBorder")+n, false );
190 info->windowType = txtToWindowType( cg.readEntry( QString("windowType")+n, QString() ).toLatin1());
191 info->shortcut = cg.readEntry( QString("shortcut")+n, QString() );
192 info->active = ( active_client == i );
193 info->stackingOrder = cg.readEntry( QString("stackingOrder")+n, -1 );
198 Returns a SessionInfo for client \a c. The returned session
199 info is removed from the storage. It's up to the caller to delete it.
201 This function is called when a new window is mapped and must be managed.
202 We try to find a matching entry in the session.
204 May return 0 if there's no session info for the client.
206 SessionInfo* Workspace::takeSessionInfo( Client* c )
208 SessionInfo *realInfo = 0;
209 QByteArray sessionId = c->sessionId();
210 QByteArray windowRole = c->windowRole();
211 QByteArray wmCommand = c->wmCommand();
212 QByteArray wmClientMachine = c->wmClientMachine( true );
213 QByteArray resourceName = c->resourceName();
214 QByteArray resourceClass = c->resourceClass();
216 // First search ``session''
217 if (! sessionId.isEmpty() )
219 // look for a real session managed client (algorithm suggested by ICCCM)
220 foreach( SessionInfo* info, session )
222 if( realInfo )
223 break;
224 if( info->sessionId == sessionId && sessionInfoWindowTypeMatch( c, info ))
226 if( ! windowRole.isEmpty() )
228 if( info->windowRole == windowRole )
230 realInfo = info;
231 session.removeAll(info);
234 else
236 if( info->windowRole.isEmpty()
237 && info->resourceName == resourceName
238 && info->resourceClass == resourceClass )
240 realInfo = info;
241 session.removeAll(info);
247 else
249 // look for a sessioninfo with matching features.
250 foreach( SessionInfo* info, session )
252 if( realInfo )
253 break;
254 if( info->resourceName == resourceName
255 && info->resourceClass == resourceClass
256 && info->wmClientMachine == wmClientMachine
257 && sessionInfoWindowTypeMatch( c, info ))
259 if ( wmCommand.isEmpty() || info->wmCommand == wmCommand )
261 realInfo = info;
262 session.removeAll( info );
267 return realInfo;
270 bool Workspace::sessionInfoWindowTypeMatch( Client* c, SessionInfo* info )
272 if( info->windowType == -2 )
273 { // undefined (not really part of NET::WindowType)
274 return !c->isSpecialWindow();
276 return info->windowType == c->windowType();
279 static const char* const window_type_names[] =
281 "Unknown", "Normal" , "Desktop", "Dock", "Toolbar", "Menu", "Dialog",
282 "Override", "TopMenu", "Utility", "Splash"
284 // change also the two functions below when adding new entries
286 const char* Workspace::windowTypeToTxt( NET::WindowType type )
288 if( type >= NET::Unknown && type <= NET::Splash )
289 return window_type_names[ type + 1 ]; // +1 (unknown==-1)
290 if( type == -2 ) // undefined (not really part of NET::WindowType)
291 return "Undefined";
292 kFatal(1212) << "Unknown Window Type" ;
293 return NULL;
296 NET::WindowType Workspace::txtToWindowType( const char* txt )
298 for( int i = NET::Unknown;
299 i <= NET::Splash;
300 ++i )
301 if( qstrcmp( txt, window_type_names[ i + 1 ] ) == 0 ) // +1
302 return static_cast< NET::WindowType >( i );
303 return static_cast< NET::WindowType >( -2 ); // undefined
309 // KWin's focus stealing prevention causes problems with user interaction
310 // during session save, as it prevents possible dialogs from getting focus.
311 // Therefore it's temporarily disabled during session saving. Start of
312 // session saving can be detected in SessionManager::saveState() above,
313 // but Qt doesn't have API for saying when session saved finished (either
314 // successfully, or was canceled). Therefore, create another connection
315 // to session manager, that will provide this information.
316 // Similarly the remember feature of window-specific settings should be disabled
317 // during KDE shutdown when windows may move e.g. because of Kicker going away
318 // (struts changing). When session saving starts, it can be cancelled, in which
319 // case the shutdown_cancelled callback is invoked, or it's a checkpoint that
320 // is immediatelly followed by save_complete, or finally it's a shutdown that
321 // is immediatelly followed by die callback. So getting save_yourself with shutdown
322 // set disables window-specific settings remembering, getting shutdown_cancelled
323 // re-enables, otherwise KWin will go away after die.
324 static void save_yourself( SmcConn conn_P, SmPointer ptr, int, Bool shutdown, int, Bool )
326 SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr );
327 if( conn_P != session->connection())
328 return;
329 if( shutdown )
330 Workspace::self()->disableRulesUpdates( true );
331 SmcSaveYourselfDone( conn_P, True );
334 static void die( SmcConn conn_P, SmPointer ptr )
336 SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr );
337 if( conn_P != session->connection())
338 return;
339 // session->saveDone(); we will quit anyway
340 session->close();
343 static void save_complete( SmcConn conn_P, SmPointer ptr )
345 SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr );
346 if( conn_P != session->connection())
347 return;
348 session->saveDone();
351 static void shutdown_cancelled( SmcConn conn_P, SmPointer ptr )
353 SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr );
354 if( conn_P != session->connection())
355 return;
356 Workspace::self()->disableRulesUpdates( false ); // re-enable
357 // no need to differentiate between successful finish and cancel
358 session->saveDone();
361 void SessionSaveDoneHelper::saveDone()
363 Workspace::self()->sessionSaveDone();
366 SessionSaveDoneHelper::SessionSaveDoneHelper()
368 SmcCallbacks calls;
369 calls.save_yourself.callback = save_yourself;
370 calls.save_yourself.client_data = reinterpret_cast< SmPointer >(this);
371 calls.die.callback = die;
372 calls.die.client_data = reinterpret_cast< SmPointer >(this);
373 calls.save_complete.callback = save_complete;
374 calls.save_complete.client_data = reinterpret_cast< SmPointer >(this);
375 calls.shutdown_cancelled.callback = shutdown_cancelled;
376 calls.shutdown_cancelled.client_data = reinterpret_cast< SmPointer >(this);
377 char* id = NULL;
378 char err[ 11 ];
379 conn = SmcOpenConnection( NULL, 0, 1, 0,
380 SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask
381 | SmcShutdownCancelledProcMask, &calls, NULL, &id, 10, err );
382 if( id != NULL )
383 free( id );
384 if( conn == NULL )
385 return; // no SM
386 // set the required properties, mostly dummy values
387 SmPropValue propvalue[ 5 ];
388 SmProp props[ 5 ];
389 propvalue[ 0 ].length = sizeof( int );
390 int value0 = SmRestartNever; // so that this extra SM connection doesn't interfere
391 propvalue[ 0 ].value = &value0;
392 props[ 0 ].name = const_cast< char* >( SmRestartStyleHint );
393 props[ 0 ].type = const_cast< char* >( SmCARD8 );
394 props[ 0 ].num_vals = 1;
395 props[ 0 ].vals = &propvalue[ 0 ];
396 struct passwd* entry = getpwuid( geteuid() );
397 propvalue[ 1 ].length = entry != NULL ? strlen( entry->pw_name ) : 0;
398 propvalue[ 1 ].value = (SmPointer)( entry != NULL ? entry->pw_name : "" );
399 props[ 1 ].name = const_cast< char* >( SmUserID );
400 props[ 1 ].type = const_cast< char* >( SmARRAY8 );
401 props[ 1 ].num_vals = 1;
402 props[ 1 ].vals = &propvalue[ 1 ];
403 propvalue[ 2 ].length = 0;
404 propvalue[ 2 ].value = (SmPointer)( "" );
405 props[ 2 ].name = const_cast< char* >( SmRestartCommand );
406 props[ 2 ].type = const_cast< char* >( SmLISTofARRAY8 );
407 props[ 2 ].num_vals = 1;
408 props[ 2 ].vals = &propvalue[ 2 ];
409 propvalue[ 3 ].length = strlen( "kwinsmhelper" );
410 propvalue[ 3 ].value = (SmPointer)"kwinsmhelper";
411 props[ 3 ].name = const_cast< char* >( SmProgram );
412 props[ 3 ].type = const_cast< char* >( SmARRAY8 );
413 props[ 3 ].num_vals = 1;
414 props[ 3 ].vals = &propvalue[ 3 ];
415 propvalue[ 4 ].length = 0;
416 propvalue[ 4 ].value = (SmPointer)( "" );
417 props[ 4 ].name = const_cast< char* >( SmCloneCommand );
418 props[ 4 ].type = const_cast< char* >( SmLISTofARRAY8 );
419 props[ 4 ].num_vals = 1;
420 props[ 4 ].vals = &propvalue[ 4 ];
421 SmProp* p[ 5 ] = { &props[ 0 ], &props[ 1 ], &props[ 2 ], &props[ 3 ], &props[ 4 ] };
422 SmcSetProperties( conn, 5, p );
423 notifier = new QSocketNotifier( IceConnectionNumber( SmcGetIceConnection( conn )),
424 QSocketNotifier::Read, this );
425 connect( notifier, SIGNAL( activated( int )), SLOT( processData()));
428 SessionSaveDoneHelper::~SessionSaveDoneHelper()
430 close();
433 void SessionSaveDoneHelper::close()
435 if( conn != NULL )
437 delete notifier;
438 SmcCloseConnection( conn, 0, NULL );
440 conn = NULL;
443 void SessionSaveDoneHelper::processData()
445 if( conn != NULL )
446 IceProcessMessages( SmcGetIceConnection( conn ), 0, 0 );
449 } // namespace
451 #include "sm.moc"