kwin Library API Documentation

sm.cpp

00001 /***************************************************************** 00002 KWin - the KDE window manager 00003 This file is part of the KDE project. 00004 00005 Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> 00006 Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> 00007 00008 You can Freely distribute this program under the GNU General Public 00009 License. See the file "COPYING" for the exact licensing terms. 00010 ******************************************************************/ 00011 00012 #include "sm.h" 00013 00014 #include <qsocketnotifier.h> 00015 #include <qsessionmanager.h> 00016 #include <kdebug.h> 00017 #include <unistd.h> 00018 #include <stdlib.h> 00019 #include <pwd.h> 00020 #include <fixx11h.h> 00021 #include <kconfig.h> 00022 #include <kglobal.h> 00023 00024 #include "workspace.h" 00025 #include "client.h" 00026 00027 namespace KWinInternal 00028 { 00029 00030 bool SessionManaged::saveState( QSessionManager& sm ) 00031 { 00032 // If the session manager is ksmserver, save stacking 00033 // order, active window, active desktop etc. in phase 1, 00034 // as ksmserver assures no interaction will be done 00035 // before the WM finishes phase 1. Saving in phase 2 is 00036 // too late, as possible user interaction may change some things. 00037 // Phase2 is still needed though (ICCCM 5.2) 00038 char* sm_vendor = SmcVendor( static_cast< SmcConn >( sm.handle())); 00039 bool ksmserver = qstrcmp( sm_vendor, "KDE" ) == 0; 00040 free( sm_vendor ); 00041 if ( !sm.isPhase2() ) 00042 { 00043 Workspace::self()->sessionSaveStarted(); 00044 if( ksmserver ) // save stacking order etc. before "save file?" etc. dialogs change it 00045 Workspace::self()->storeSession( kapp->sessionConfig(), SMSavePhase0 ); 00046 sm.release(); // Qt doesn't automatically release in this case (bug?) 00047 sm.requestPhase2(); 00048 return true; 00049 } 00050 Workspace::self()->storeSession( kapp->sessionConfig(), ksmserver ? SMSavePhase2 : SMSavePhase2Full ); 00051 kapp->sessionConfig()->sync(); 00052 return true; 00053 } 00054 00055 // I bet this is broken, just like everywhere else in KDE 00056 bool SessionManaged::commitData( QSessionManager& sm ) 00057 { 00058 if ( !sm.isPhase2() ) 00059 Workspace::self()->sessionSaveStarted(); 00060 return true; 00061 } 00062 00063 // Workspace 00064 00070 void Workspace::storeSession( KConfig* config, SMSavePhase phase ) 00071 { 00072 config->setGroup("Session" ); 00073 int count = 0; 00074 int active_client = -1; 00075 for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) 00076 { 00077 Client* c = (*it); 00078 QCString sessionId = c->sessionId(); 00079 QCString wmCommand = c->wmCommand(); 00080 if ( sessionId.isEmpty() ) 00081 // remember also applications that are not XSMP capable 00082 // and use the obsolete WM_COMMAND / WM_SAVE_YOURSELF 00083 if ( wmCommand.isEmpty() ) 00084 continue; 00085 count++; 00086 if( c->isActive()) 00087 active_client = count; 00088 QString n = QString::number(count); 00089 if( phase == SMSavePhase2 || phase == SMSavePhase2Full ) 00090 { 00091 config->writeEntry( QString("sessionId")+n, sessionId.data() ); 00092 config->writeEntry( QString("windowRole")+n, c->windowRole().data() ); 00093 config->writeEntry( QString("wmCommand")+n, wmCommand.data() ); 00094 config->writeEntry( QString("wmClientMachine")+n, c->wmClientMachine( true ).data() ); 00095 config->writeEntry( QString("resourceName")+n, c->resourceName().data() ); 00096 config->writeEntry( QString("resourceClass")+n, c->resourceClass().data() ); 00097 config->writeEntry( QString("geometry")+n, QRect( c->calculateGravitation(TRUE), c->clientSize() ) ); // FRAME 00098 config->writeEntry( QString("restore")+n, c->geometryRestore() ); 00099 config->writeEntry( QString("fsrestore")+n, c->geometryFSRestore() ); 00100 config->writeEntry( QString("maximize")+n, (int) c->maximizeMode() ); 00101 config->writeEntry( QString("fullscreen")+n, (int) c->fullScreenMode() ); 00102 config->writeEntry( QString("desktop")+n, c->desktop() ); 00103 // the config entry is called "iconified" for back. comp. reasons 00104 // (kconf_update script for updating session files would be too complicated) 00105 config->writeEntry( QString("iconified")+n, c->isMinimized() ); 00106 // the config entry is called "sticky" for back. comp. reasons 00107 config->writeEntry( QString("sticky")+n, c->isOnAllDesktops() ); 00108 config->writeEntry( QString("shaded")+n, c->isShade() ); 00109 // the config entry is called "staysOnTop" for back. comp. reasons 00110 config->writeEntry( QString("staysOnTop")+n, c->keepAbove() ); 00111 config->writeEntry( QString("keepBelow")+n, c->keepBelow() ); 00112 config->writeEntry( QString("skipTaskbar")+n, c->skipTaskbar( true ) ); 00113 config->writeEntry( QString("skipPager")+n, c->skipPager() ); 00114 config->writeEntry( QString("userNoBorder")+n, c->isUserNoBorder() ); 00115 config->writeEntry( QString("windowType")+n, windowTypeToTxt( c->windowType())); 00116 } 00117 } 00118 // TODO store also stacking order 00119 if( phase == SMSavePhase0 ) 00120 { 00121 // it would be much simpler to save these values to the config file, 00122 // but both Qt and KDE treat phase1 and phase2 separately, 00123 // which results in different sessionkey and different config file :( 00124 session_active_client = active_client; 00125 session_desktop = currentDesktop(); 00126 } 00127 else if( phase == SMSavePhase2 ) 00128 { 00129 config->writeEntry( "count", count ); 00130 config->writeEntry( "active", session_active_client ); 00131 config->writeEntry( "desktop", session_desktop ); 00132 } 00133 else // SMSavePhase2Full 00134 { 00135 config->writeEntry( "count", count ); 00136 config->writeEntry( "active", session_active_client ); 00137 config->writeEntry( "desktop", currentDesktop()); 00138 } 00139 } 00140 00141 00147 void Workspace::loadSessionInfo() 00148 { 00149 session.clear(); 00150 KConfig* config = kapp->sessionConfig(); 00151 config->setGroup("Session" ); 00152 int count = config->readNumEntry( "count" ); 00153 int active_client = config->readNumEntry( "active" ); 00154 for ( int i = 1; i <= count; i++ ) 00155 { 00156 QString n = QString::number(i); 00157 SessionInfo* info = new SessionInfo; 00158 session.append( info ); 00159 info->sessionId = config->readEntry( QString("sessionId")+n ).latin1(); 00160 info->windowRole = config->readEntry( QString("windowRole")+n ).latin1(); 00161 info->wmCommand = config->readEntry( QString("wmCommand")+n ).latin1(); 00162 info->wmClientMachine = config->readEntry( QString("wmClientMachine")+n ).latin1(); 00163 info->resourceName = config->readEntry( QString("resourceName")+n ).latin1(); 00164 info->resourceClass = config->readEntry( QString("resourceClass")+n ).lower().latin1(); 00165 info->geometry = config->readRectEntry( QString("geometry")+n ); 00166 info->restore = config->readRectEntry( QString("restore")+n ); 00167 info->fsrestore = config->readRectEntry( QString("fsrestore")+n ); 00168 info->maximized = config->readNumEntry( QString("maximize")+n, 0 ); 00169 info->fullscreen = config->readNumEntry( QString("fullscreen")+n, 0 ); 00170 info->desktop = config->readNumEntry( QString("desktop")+n, 0 ); 00171 info->minimized = config->readBoolEntry( QString("iconified")+n, FALSE ); 00172 info->onAllDesktops = config->readBoolEntry( QString("sticky")+n, FALSE ); 00173 info->shaded = config->readBoolEntry( QString("shaded")+n, FALSE ); 00174 info->keepAbove = config->readBoolEntry( QString("staysOnTop")+n, FALSE ); 00175 info->keepBelow = config->readBoolEntry( QString("keepBelow")+n, FALSE ); 00176 info->skipTaskbar = config->readBoolEntry( QString("skipTaskbar")+n, FALSE ); 00177 info->skipPager = config->readBoolEntry( QString("skipPager")+n, FALSE ); 00178 info->userNoBorder = config->readBoolEntry( QString("userNoBorder")+n, FALSE ); 00179 info->windowType = txtToWindowType( config->readEntry( QString("windowType")+n ).latin1()); 00180 info->active = ( active_client == i ); 00181 } 00182 } 00183 00193 SessionInfo* Workspace::takeSessionInfo( Client* c ) 00194 { 00195 SessionInfo *realInfo = 0; 00196 QCString sessionId = c->sessionId(); 00197 QCString windowRole = c->windowRole(); 00198 QCString wmCommand = c->wmCommand(); 00199 QCString wmClientMachine = c->wmClientMachine( true ); 00200 QCString resourceName = c->resourceName(); 00201 QCString resourceClass = c->resourceClass(); 00202 00203 // First search ``session'' 00204 if (! sessionId.isEmpty() ) 00205 { 00206 // look for a real session managed client (algorithm suggested by ICCCM) 00207 for (SessionInfo* info = session.first(); info && !realInfo; info = session.next() ) 00208 if ( info->sessionId == sessionId && sessionInfoWindowTypeMatch( c, info )) 00209 { 00210 if ( ! windowRole.isEmpty() ) 00211 { 00212 if ( info->windowRole == windowRole ) 00213 realInfo = session.take(); 00214 } 00215 else 00216 { 00217 if ( info->windowRole.isEmpty() && 00218 info->resourceName == resourceName && 00219 info->resourceClass == resourceClass ) 00220 realInfo = session.take(); 00221 } 00222 } 00223 } 00224 else 00225 { 00226 // look for a sessioninfo with matching features. 00227 for (SessionInfo* info = session.first(); info && !realInfo; info = session.next() ) 00228 if ( info->resourceName == resourceName && 00229 info->resourceClass == resourceClass && 00230 info->wmClientMachine == wmClientMachine && 00231 sessionInfoWindowTypeMatch( c, info )) 00232 if ( wmCommand.isEmpty() || info->wmCommand == wmCommand ) 00233 realInfo = session.take(); 00234 } 00235 00236 return realInfo; 00237 } 00238 00239 bool Workspace::sessionInfoWindowTypeMatch( Client* c, SessionInfo* info ) 00240 { 00241 if( info->windowType == -2 ) 00242 { // undefined (not really part of NET::WindowType) 00243 return !c->isSpecialWindow() || c->isOverride(); 00244 } 00245 return info->windowType == c->windowType(); 00246 } 00247 00248 // maybe needed later 00249 #if 0 00250 // KMainWindow's without name() given have WM_WINDOW_ROLE in the form 00251 // of <appname>-mainwindow#<number> 00252 // when comparing them for fake session info, it's probably better to check 00253 // them without the trailing number 00254 bool Workspace::windowRoleMatch( const QCString& role1, const QCString& role2 ) 00255 { 00256 if( role1.isEmpty() && role2.isEmpty()) 00257 return true; 00258 int pos1 = role1.find( '#' ); 00259 int pos2 = role2.find( '#' ); 00260 bool ret; 00261 if( pos1 < 0 || pos2 < 0 || pos1 != pos2 ) 00262 ret = role1 == role2; 00263 else 00264 ret = qstrncmp( role1, role2, pos1 ) == 0; 00265 kdDebug() << "WR:" << role1 << ":" << pos1 << ":" << role2 << ":" << pos2 << ":::" << ret << endl; 00266 return ret; 00267 } 00268 #endif 00269 00270 static const char* const window_type_names[] = 00271 { 00272 "Unknown", "Normal" , "Desktop", "Dock", "Toolbar", "Menu", "Dialog", 00273 "Override", "TopMenu", "Utility", "Splash" 00274 }; 00275 // change also the two functions below when adding new entries 00276 00277 const char* Workspace::windowTypeToTxt( NET::WindowType type ) 00278 { 00279 if( type >= NET::Unknown && type <= NET::Splash ) 00280 return window_type_names[ type + 1 ]; // +1 (unknown==-1) 00281 if( type == -2 ) // undefined (not really part of NET::WindowType) 00282 return "Undefined"; 00283 kdFatal() << "Unknown Window Type" << endl; 00284 return NULL; 00285 } 00286 00287 NET::WindowType Workspace::txtToWindowType( const char* txt ) 00288 { 00289 for( int i = NET::Unknown; 00290 i <= NET::Splash; 00291 ++i ) 00292 if( qstrcmp( txt, window_type_names[ i + 1 ] ) == 0 ) // +1 00293 return static_cast< NET::WindowType >( i ); 00294 return static_cast< NET::WindowType >( -2 ); // undefined 00295 } 00296 00297 00298 00299 00300 // KWin's focus stealing prevention causes problems with user interaction 00301 // during session save, as it prevents possible dialogs from getting focus. 00302 // Therefore it's temporarily disabled during session saving. Start of 00303 // session saving can be detected in SessionManaged::saveState() above, 00304 // but Qt doesn't have API for saying when session saved finished (either 00305 // successfully, or was cancelled). Therefore, create another connection 00306 // to session manager, that will provide this information. 00307 static void save_yourself( SmcConn conn_P, SmPointer ptr, int, Bool, int, Bool ) 00308 { 00309 SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr ); 00310 if( conn_P != session->connection()) 00311 return; 00312 SmcSaveYourselfDone( conn_P, True ); 00313 } 00314 00315 static void die( SmcConn conn_P, SmPointer ptr ) 00316 { 00317 SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr ); 00318 if( conn_P != session->connection()) 00319 return; 00320 // session->saveDone(); we will quit anyway 00321 session->close(); 00322 } 00323 00324 static void save_complete( SmcConn conn_P, SmPointer ptr ) 00325 { 00326 SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr ); 00327 if( conn_P != session->connection()) 00328 return; 00329 session->saveDone(); 00330 } 00331 00332 static void shutdown_cancelled( SmcConn conn_P, SmPointer ptr ) 00333 { 00334 SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr ); 00335 if( conn_P != session->connection()) 00336 return; 00337 // no need to differentiate between successful finish and cancel 00338 session->saveDone(); 00339 } 00340 00341 void SessionSaveDoneHelper::saveDone() 00342 { 00343 Workspace::self()->sessionSaveDone(); 00344 } 00345 00346 SessionSaveDoneHelper::SessionSaveDoneHelper() 00347 { 00348 SmcCallbacks calls; 00349 calls.save_yourself.callback = save_yourself; 00350 calls.save_yourself.client_data = reinterpret_cast< SmPointer >(this); 00351 calls.die.callback = die; 00352 calls.die.client_data = reinterpret_cast< SmPointer >(this); 00353 calls.save_complete.callback = save_complete; 00354 calls.save_complete.client_data = reinterpret_cast< SmPointer >(this); 00355 calls.shutdown_cancelled.callback = shutdown_cancelled; 00356 calls.shutdown_cancelled.client_data = reinterpret_cast< SmPointer >(this); 00357 char* id = NULL; 00358 char err[ 11 ]; 00359 conn = SmcOpenConnection( NULL, 0, 1, 0, 00360 SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask 00361 | SmcShutdownCancelledProcMask, &calls, NULL, &id, 10, err ); 00362 if( id != NULL ) 00363 free( id ); 00364 if( conn == NULL ) 00365 return; // no SM 00366 // set the required properties, mostly dummy values 00367 SmPropValue propvalue[ 5 ]; 00368 SmProp props[ 5 ]; 00369 propvalue[ 0 ].length = sizeof( int ); 00370 int value0 = SmRestartNever; // so that this extra SM connection doesn't interfere 00371 propvalue[ 0 ].value = &value0; 00372 props[ 0 ].name = const_cast< char* >( SmRestartStyleHint ); 00373 props[ 0 ].type = const_cast< char* >( SmCARD8 ); 00374 props[ 0 ].num_vals = 1; 00375 props[ 0 ].vals = &propvalue[ 0 ]; 00376 struct passwd* entry = getpwuid( geteuid() ); 00377 propvalue[ 1 ].length = entry != NULL ? strlen( entry->pw_name ) : 0; 00378 propvalue[ 1 ].value = (SmPointer)( entry != NULL ? entry->pw_name : "" ); 00379 props[ 1 ].name = const_cast< char* >( SmUserID ); 00380 props[ 1 ].type = const_cast< char* >( SmARRAY8 ); 00381 props[ 1 ].num_vals = 1; 00382 props[ 1 ].vals = &propvalue[ 1 ]; 00383 propvalue[ 2 ].length = 0; 00384 propvalue[ 2 ].value = (SmPointer)( "" ); 00385 props[ 2 ].name = const_cast< char* >( SmRestartCommand ); 00386 props[ 2 ].type = const_cast< char* >( SmLISTofARRAY8 ); 00387 props[ 2 ].num_vals = 1; 00388 props[ 2 ].vals = &propvalue[ 2 ]; 00389 propvalue[ 3 ].length = 0; 00390 propvalue[ 3 ].value = qApp->argv()[ 0 ]; 00391 props[ 3 ].name = const_cast< char* >( SmProgram ); 00392 props[ 3 ].type = const_cast< char* >( SmARRAY8 ); 00393 props[ 3 ].num_vals = 1; 00394 props[ 3 ].vals = &propvalue[ 3 ]; 00395 propvalue[ 4 ].length = 0; 00396 propvalue[ 4 ].value = (SmPointer)( "" ); 00397 props[ 4 ].name = const_cast< char* >( SmCloneCommand ); 00398 props[ 4 ].type = const_cast< char* >( SmLISTofARRAY8 ); 00399 props[ 4 ].num_vals = 1; 00400 props[ 4 ].vals = &propvalue[ 4 ]; 00401 SmProp* p[ 5 ] = { &props[ 0 ], &props[ 1 ], &props[ 2 ], &props[ 3 ], &props[ 4 ] }; 00402 SmcSetProperties( conn, 5, p ); 00403 notifier = new QSocketNotifier( IceConnectionNumber( SmcGetIceConnection( conn )), 00404 QSocketNotifier::Read, this ); 00405 connect( notifier, SIGNAL( activated( int )), SLOT( processData())); 00406 } 00407 00408 SessionSaveDoneHelper::~SessionSaveDoneHelper() 00409 { 00410 close(); 00411 } 00412 00413 void SessionSaveDoneHelper::close() 00414 { 00415 if( conn != NULL ) 00416 { 00417 delete notifier; 00418 SmcCloseConnection( conn, 0, NULL ); 00419 } 00420 conn = NULL; 00421 } 00422 00423 void SessionSaveDoneHelper::processData() 00424 { 00425 if( conn != NULL ) 00426 IceProcessMessages( SmcGetIceConnection( conn ), 0, 0 ); 00427 } 00428 00429 } // namespace 00430 00431 #include "sm.moc"
KDE Logo
This file is part of the documentation for kwin Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Aug 13 21:47:06 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003