• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • twin
 

twin

  • twin
sm.cpp
1/*****************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
7
8You can Freely distribute this program under the GNU General Public
9License. See the file "COPYING" for the exact licensing terms.
10******************************************************************/
11
12#include "sm.h"
13
14#include <tqsocketnotifier.h>
15#include <tqsessionmanager.h>
16#include <kdebug.h>
17#include <unistd.h>
18#include <stdlib.h>
19#include <pwd.h>
20#include <fixx11h.h>
21#include <tdeconfig.h>
22#include <tdeglobal.h>
23
24#include "workspace.h"
25#include "client.h"
26
27namespace KWinInternal
28{
29
30bool SessionManaged::saveState( TQSessionManager& sm )
31 {
32 // If the session manager is ksmserver, save stacking
33 // order, active window, active desktop etc. in phase 1,
34 // as ksmserver assures no interaction will be done
35 // before the WM finishes phase 1. Saving in phase 2 is
36 // too late, as possible user interaction may change some things.
37 // Phase2 is still needed though (ICCCM 5.2)
38 char* sm_vendor = SmcVendor( static_cast< SmcConn >( sm.handle()));
39 bool ksmserver = qstrcmp( sm_vendor, "KDE" ) == 0;
40 free( sm_vendor );
41 if ( !sm.isPhase2() )
42 {
43 Workspace::self()->sessionSaveStarted();
44 if( ksmserver ) // save stacking order etc. before "save file?" etc. dialogs change it
45 Workspace::self()->storeSession( tdeApp->sessionConfig(), SMSavePhase0 );
46 sm.release(); // Qt doesn't automatically release in this case (bug?)
47 sm.requestPhase2();
48 return true;
49 }
50 Workspace::self()->storeSession( tdeApp->sessionConfig(), ksmserver ? SMSavePhase2 : SMSavePhase2Full );
51 tdeApp->sessionConfig()->sync();
52 return true;
53 }
54
55// I bet this is broken, just like everywhere else in KDE
56bool SessionManaged::commitData( TQSessionManager& sm )
57 {
58 if ( !sm.isPhase2() )
59 Workspace::self()->sessionSaveStarted();
60 return true;
61 }
62
63// Workspace
64
70void Workspace::storeSession( TDEConfig* config, SMSavePhase phase )
71 {
72 config->setGroup("Session" );
73 int count = 0;
74 int active_client = -1;
75 for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it)
76 {
77 Client* c = (*it);
78 TQCString sessionId = c->sessionId();
79 TQCString wmCommand = c->wmCommand();
80 if ( sessionId.isEmpty() )
81 // remember also applications that are not XSMP capable
82 // and use the obsolete WM_COMMAND / WM_SAVE_YOURSELF
83 if ( wmCommand.isEmpty() )
84 continue;
85 count++;
86 if( c->isActive())
87 active_client = count;
88 TQString n = TQString::number(count);
89 if( phase == SMSavePhase2 || phase == SMSavePhase2Full )
90 {
91 config->writeEntry( TQString("sessionId")+n, sessionId.data() );
92 config->writeEntry( TQString("windowRole")+n, c->windowRole().data() );
93 config->writeEntry( TQString("wmCommand")+n, wmCommand.data() );
94 config->writeEntry( TQString("wmClientMachine")+n, c->wmClientMachine( true ).data() );
95 config->writeEntry( TQString("resourceName")+n, c->resourceName().data() );
96 config->writeEntry( TQString("resourceClass")+n, c->resourceClass().data() );
97 config->writeEntry( TQString("geometry")+n, TQRect( c->calculateGravitation(true), c->clientSize() ) ); // FRAME
98 config->writeEntry( TQString("restore")+n, c->geometryRestore() );
99 config->writeEntry( TQString("fsrestore")+n, c->geometryFSRestore() );
100 config->writeEntry( TQString("maximize")+n, (int) c->maximizeMode() );
101 config->writeEntry( TQString("fullscreen")+n, (int) c->fullScreenMode() );
102 config->writeEntry( TQString("desktop")+n, c->desktop() );
103 // the config entry is called "iconified" for back. comp. reasons
104 // (tdeconf_update script for updating session files would be too complicated)
105 config->writeEntry( TQString("iconified")+n, c->isMinimized() );
106 // the config entry is called "sticky" for back. comp. reasons
107 config->writeEntry( TQString("sticky")+n, c->isOnAllDesktops() );
108 config->writeEntry( TQString("shaded")+n, c->isShade() );
109 config->writeEntry( TQString("shadowed")+n, c->isShadowed() );
110 // the config entry is called "staysOnTop" for back. comp. reasons
111 config->writeEntry( TQString("staysOnTop")+n, c->keepAbove() );
112 config->writeEntry( TQString("keepBelow")+n, c->keepBelow() );
113 config->writeEntry( TQString("skipTaskbar")+n, c->skipTaskbar( true ) );
114 config->writeEntry( TQString("skipPager")+n, c->skipPager() );
115 config->writeEntry( TQString("userNoBorder")+n, c->isUserNoBorder() );
116 config->writeEntry( TQString("userNoBorderForced")+n, c->isUserNoBorderForced() );
117 config->writeEntry( TQString("windowType")+n, windowTypeToTxt( c->windowType()));
118 config->writeEntry( TQString("shortcut")+n, c->shortcut().toStringInternal());
119 }
120 }
121 // TODO store also stacking order
122 if( phase == SMSavePhase0 )
123 {
124 // it would be much simpler to save these values to the config file,
125 // but both Qt and KDE treat phase1 and phase2 separately,
126 // which results in different sessionkey and different config file :(
127 session_active_client = active_client;
128 session_desktop = currentDesktop();
129 }
130 else if( phase == SMSavePhase2 )
131 {
132 config->writeEntry( "count", count );
133 config->writeEntry( "active", session_active_client );
134 config->writeEntry( "desktop", session_desktop );
135 }
136 else // SMSavePhase2Full
137 {
138 config->writeEntry( "count", count );
139 config->writeEntry( "active", session_active_client );
140 config->writeEntry( "desktop", currentDesktop());
141 }
142 }
143
144
150void Workspace::loadSessionInfo()
151 {
152 session.clear();
153 TDEConfig* config = tdeApp->sessionConfig();
154 config->setGroup("Session" );
155 int count = config->readNumEntry( "count" );
156 int active_client = config->readNumEntry( "active" );
157 for ( int i = 1; i <= count; i++ )
158 {
159 TQString n = TQString::number(i);
160 SessionInfo* info = new SessionInfo;
161 session.append( info );
162 info->sessionId = config->readEntry( TQString("sessionId")+n ).latin1();
163 info->windowRole = config->readEntry( TQString("windowRole")+n ).latin1();
164 info->wmCommand = config->readEntry( TQString("wmCommand")+n ).latin1();
165 info->wmClientMachine = config->readEntry( TQString("wmClientMachine")+n ).latin1();
166 info->resourceName = config->readEntry( TQString("resourceName")+n ).latin1();
167 info->resourceClass = config->readEntry( TQString("resourceClass")+n ).lower().latin1();
168 info->geometry = config->readRectEntry( TQString("geometry")+n );
169 info->restore = config->readRectEntry( TQString("restore")+n );
170 info->fsrestore = config->readRectEntry( TQString("fsrestore")+n );
171 info->maximized = config->readNumEntry( TQString("maximize")+n, 0 );
172 info->fullscreen = config->readNumEntry( TQString("fullscreen")+n, 0 );
173 info->desktop = config->readNumEntry( TQString("desktop")+n, 0 );
174 info->minimized = config->readBoolEntry( TQString("iconified")+n, false );
175 info->onAllDesktops = config->readBoolEntry( TQString("sticky")+n, false );
176 info->shaded = config->readBoolEntry( TQString("shaded")+n, false );
177 info->shadowed = config->readBoolEntry( TQString("shadowed")+n, true );
178 info->keepAbove = config->readBoolEntry( TQString("staysOnTop")+n, false );
179 info->keepBelow = config->readBoolEntry( TQString("keepBelow")+n, false );
180 info->skipTaskbar = config->readBoolEntry( TQString("skipTaskbar")+n, false );
181 info->skipPager = config->readBoolEntry( TQString("skipPager")+n, false );
182 info->userNoBorder = config->readBoolEntry( TQString("userNoBorder")+n, false );
183 info->userNoBorderForced = config->readBoolEntry( TQString("userNoBorderForced")+n, false );
184 info->windowType = txtToWindowType( config->readEntry( TQString("windowType")+n ).latin1());
185 info->shortcut = config->readEntry( TQString("shortcut")+n );
186 info->active = ( active_client == i );
187 }
188 }
189
199SessionInfo* Workspace::takeSessionInfo( Client* c )
200 {
201 SessionInfo *realInfo = 0;
202 TQCString sessionId = c->sessionId();
203 TQCString windowRole = c->windowRole();
204 TQCString wmCommand = c->wmCommand();
205 TQCString wmClientMachine = c->wmClientMachine( true );
206 TQCString resourceName = c->resourceName();
207 TQCString resourceClass = c->resourceClass();
208
209 // First search ``session''
210 if (! sessionId.isEmpty() )
211 {
212 // look for a real session managed client (algorithm suggested by ICCCM)
213 for (SessionInfo* info = session.first(); info && !realInfo; info = session.next() )
214 if ( info->sessionId == sessionId && sessionInfoWindowTypeMatch( c, info ))
215 {
216 if ( ! windowRole.isEmpty() )
217 {
218 if ( info->windowRole == windowRole )
219 realInfo = session.take();
220 }
221 else
222 {
223 if ( info->windowRole.isEmpty() &&
224 info->resourceName == resourceName &&
225 info->resourceClass == resourceClass )
226 realInfo = session.take();
227 }
228 }
229 }
230 else
231 {
232 // look for a sessioninfo with matching features.
233 for (SessionInfo* info = session.first(); info && !realInfo; info = session.next() )
234 if ( info->resourceName == resourceName &&
235 info->resourceClass == resourceClass &&
236 info->wmClientMachine == wmClientMachine &&
237 sessionInfoWindowTypeMatch( c, info ))
238 if ( wmCommand.isEmpty() || info->wmCommand == wmCommand )
239 realInfo = session.take();
240 }
241
242 return realInfo;
243 }
244
245bool Workspace::sessionInfoWindowTypeMatch( Client* c, SessionInfo* info )
246 {
247 if( info->windowType == -2 )
248 { // undefined (not really part of NET::WindowType)
249 return !c->isSpecialWindow();
250 }
251 return info->windowType == c->windowType();
252 }
253
254// maybe needed later
255#if 0
256// TDEMainWindow's without name() given have WM_WINDOW_ROLE in the form
257// of <appname>-mainwindow#<number>
258// when comparing them for fake session info, it's probably better to check
259// them without the trailing number
260bool Workspace::windowRoleMatch( const TQCString& role1, const TQCString& role2 )
261 {
262 if( role1.isEmpty() && role2.isEmpty())
263 return true;
264 int pos1 = role1.find( '#' );
265 int pos2 = role2.find( '#' );
266 bool ret;
267 if( pos1 < 0 || pos2 < 0 || pos1 != pos2 )
268 ret = role1 == role2;
269 else
270 ret = tqstrncmp( role1, role2, pos1 ) == 0;
271 kdDebug() << "WR:" << role1 << ":" << pos1 << ":" << role2 << ":" << pos2 << ":::" << ret << endl;
272 return ret;
273 }
274#endif
275
276static const char* const window_type_names[] =
277 {
278 "Unknown", "Normal" , "Desktop", "Dock", "Toolbar", "Menu", "Dialog",
279 "Override", "TopMenu", "Utility", "Splash"
280 };
281 // change also the two functions below when adding new entries
282
283const char* Workspace::windowTypeToTxt( NET::WindowType type )
284 {
285 if( type >= NET::Unknown && type <= NET::Splash )
286 return window_type_names[ type + 1 ]; // +1 (unknown==-1)
287 if( type == -2 ) // undefined (not really part of NET::WindowType)
288 return "Undefined";
289 kdFatal() << "Unknown Window Type" << endl;
290 return NULL;
291 }
292
293NET::WindowType Workspace::txtToWindowType( const char* txt )
294 {
295 for( int i = NET::Unknown;
296 i <= NET::Splash;
297 ++i )
298 if( qstrcmp( txt, window_type_names[ i + 1 ] ) == 0 ) // +1
299 return static_cast< NET::WindowType >( i );
300 return static_cast< NET::WindowType >( -2 ); // undefined
301 }
302
303
304
305
306// KWin's focus stealing prevention causes problems with user interaction
307// during session save, as it prevents possible dialogs from getting focus.
308// Therefore it's temporarily disabled during session saving. Start of
309// session saving can be detected in SessionManaged::saveState() above,
310// but Qt doesn't have API for saying when session saved finished (either
311// successfully, or was cancelled). Therefore, create another connection
312// to session manager, that will provide this information.
313// Similarly the remember feature of window-specific settings should be disabled
314// during KDE shutdown when windows may move e.g. because of Kicker going away
315// (struts changing). When session saving starts, it can be cancelled, in which
316// case the shutdown_cancelled callback is invoked, or it's a checkpoint that
317// is immediatelly followed by save_complete, or finally it's a shutdown that
318// is immediatelly followed by die callback. So getting save_yourself with shutdown
319// set disables window-specific settings remembering, getting shutdown_cancelled
320// re-enables, otherwise KWin will go away after die.
321static void save_yourself( SmcConn conn_P, SmPointer ptr, int, Bool shutdown, int, Bool )
322 {
323 SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr );
324 if( conn_P != session->connection())
325 return;
326 if( shutdown )
327 Workspace::self()->disableRulesUpdates( true );
328 SmcSaveYourselfDone( conn_P, True );
329 }
330
331static void die( SmcConn conn_P, SmPointer ptr )
332 {
333 SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr );
334 if( conn_P != session->connection())
335 return;
336 // session->saveDone(); we will quit anyway
337 session->close();
338 }
339
340static void save_complete( SmcConn conn_P, SmPointer ptr )
341 {
342 SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr );
343 if( conn_P != session->connection())
344 return;
345 session->saveDone();
346 }
347
348static void shutdown_cancelled( SmcConn conn_P, SmPointer ptr )
349 {
350 SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr );
351 if( conn_P != session->connection())
352 return;
353 Workspace::self()->disableRulesUpdates( false ); // re-enable
354 // no need to differentiate between successful finish and cancel
355 session->saveDone();
356 }
357
358void SessionSaveDoneHelper::saveDone()
359 {
360 Workspace::self()->sessionSaveDone();
361 }
362
363SessionSaveDoneHelper::SessionSaveDoneHelper()
364 {
365 SmcCallbacks calls;
366 calls.save_yourself.callback = save_yourself;
367 calls.save_yourself.client_data = reinterpret_cast< SmPointer >(this);
368 calls.die.callback = die;
369 calls.die.client_data = reinterpret_cast< SmPointer >(this);
370 calls.save_complete.callback = save_complete;
371 calls.save_complete.client_data = reinterpret_cast< SmPointer >(this);
372 calls.shutdown_cancelled.callback = shutdown_cancelled;
373 calls.shutdown_cancelled.client_data = reinterpret_cast< SmPointer >(this);
374 char* id = NULL;
375 char err[ 11 ];
376 conn = SmcOpenConnection( NULL, 0, 1, 0,
377 SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask
378 | SmcShutdownCancelledProcMask, &calls, NULL, &id, 10, err );
379 if( id != NULL )
380 free( id );
381 if( conn == NULL )
382 return; // no SM
383 // set the required properties, mostly dummy values
384 SmPropValue propvalue[ 5 ];
385 SmProp props[ 5 ];
386 propvalue[ 0 ].length = sizeof( int );
387 int value0 = SmRestartNever; // so that this extra SM connection doesn't interfere
388 propvalue[ 0 ].value = &value0;
389 props[ 0 ].name = const_cast< char* >( SmRestartStyleHint );
390 props[ 0 ].type = const_cast< char* >( SmCARD8 );
391 props[ 0 ].num_vals = 1;
392 props[ 0 ].vals = &propvalue[ 0 ];
393 struct passwd* entry = getpwuid( geteuid() );
394 propvalue[ 1 ].length = entry != NULL ? strlen( entry->pw_name ) : 0;
395 propvalue[ 1 ].value = (SmPointer)( entry != NULL ? entry->pw_name : "" );
396 props[ 1 ].name = const_cast< char* >( SmUserID );
397 props[ 1 ].type = const_cast< char* >( SmARRAY8 );
398 props[ 1 ].num_vals = 1;
399 props[ 1 ].vals = &propvalue[ 1 ];
400 propvalue[ 2 ].length = 0;
401 propvalue[ 2 ].value = (SmPointer)( "" );
402 props[ 2 ].name = const_cast< char* >( SmRestartCommand );
403 props[ 2 ].type = const_cast< char* >( SmLISTofARRAY8 );
404 props[ 2 ].num_vals = 1;
405 props[ 2 ].vals = &propvalue[ 2 ];
406 propvalue[ 3 ].length = 0;
407 propvalue[ 3 ].value = tqApp->argv()[ 0 ];
408 props[ 3 ].name = const_cast< char* >( SmProgram );
409 props[ 3 ].type = const_cast< char* >( SmARRAY8 );
410 props[ 3 ].num_vals = 1;
411 props[ 3 ].vals = &propvalue[ 3 ];
412 propvalue[ 4 ].length = 0;
413 propvalue[ 4 ].value = (SmPointer)( "" );
414 props[ 4 ].name = const_cast< char* >( SmCloneCommand );
415 props[ 4 ].type = const_cast< char* >( SmLISTofARRAY8 );
416 props[ 4 ].num_vals = 1;
417 props[ 4 ].vals = &propvalue[ 4 ];
418 SmProp* p[ 5 ] = { &props[ 0 ], &props[ 1 ], &props[ 2 ], &props[ 3 ], &props[ 4 ] };
419 SmcSetProperties( conn, 5, p );
420 notifier = new TQSocketNotifier( IceConnectionNumber( SmcGetIceConnection( conn )),
421 TQSocketNotifier::Read, this );
422 connect( notifier, TQ_SIGNAL( activated( int )), TQ_SLOT( processData()));
423 }
424
425SessionSaveDoneHelper::~SessionSaveDoneHelper()
426 {
427 close();
428 }
429
430void SessionSaveDoneHelper::close()
431 {
432 if( conn != NULL )
433 {
434 delete notifier;
435 SmcCloseConnection( conn, 0, NULL );
436 }
437 conn = NULL;
438 }
439
440void SessionSaveDoneHelper::processData()
441 {
442 if( conn != NULL )
443 IceProcessMessages( SmcGetIceConnection( conn ), 0, 0 );
444 }
445
446} // namespace
447
448#include "sm.moc"

twin

Skip menu "twin"
  • Main Page
  • Alphabetical List
  • Class List
  • File List
  • Class Members

twin

Skip menu "twin"
  • kate
  • libkonq
  • twin
  •   lib
Generated for twin by doxygen 1.9.8
This website is maintained by Timothy Pearson.