• 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 
5 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
7 
8 You can Freely distribute this program under the GNU General Public
9 License. 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 
27 namespace KWinInternal
28 {
29 
30 bool 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
56 bool SessionManaged::commitData( TQSessionManager& sm )
57  {
58  if ( !sm.isPhase2() )
59  Workspace::self()->sessionSaveStarted();
60  return true;
61  }
62 
63 // Workspace
64 
70 void 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 
150 void 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 
199 SessionInfo* 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 
245 bool 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
260 bool 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 
276 static 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 
283 const 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 
293 NET::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.
321 static 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 
331 static 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 
340 static 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 
348 static 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 
358 void SessionSaveDoneHelper::saveDone()
359  {
360  Workspace::self()->sessionSaveDone();
361  }
362 
363 SessionSaveDoneHelper::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 
425 SessionSaveDoneHelper::~SessionSaveDoneHelper()
426  {
427  close();
428  }
429 
430 void SessionSaveDoneHelper::close()
431  {
432  if( conn != NULL )
433  {
434  delete notifier;
435  SmcCloseConnection( conn, 0, NULL );
436  }
437  conn = NULL;
438  }
439 
440 void 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
  • Modules
  • Alphabetical List
  • Class List
  • File List
  • Class Members

twin

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