• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KWin

sm.cpp

Go to the documentation of this file.
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 This program is free software; you can redistribute it and/or modify
00009 it under the terms of the GNU General Public License as published by
00010 the Free Software Foundation; either version 2 of the License, or
00011 (at your option) any later version.
00012 
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 GNU General Public License for more details.
00017 
00018 You should have received a copy of the GNU General Public License
00019 along with this program.  If not, see <http://www.gnu.org/licenses/>.
00020 *********************************************************************/
00021 
00022 #include "sm.h"
00023 
00024 #include <unistd.h>
00025 #include <stdlib.h>
00026 #include <pwd.h>
00027 #include <fixx11h.h>
00028 #include <kconfig.h>
00029 #include <kglobal.h>
00030 
00031 #include "workspace.h"
00032 #include "client.h"
00033 #include <QSocketNotifier>
00034 #include <QSessionManager>
00035 #include <kdebug.h>
00036 
00037 namespace KWin
00038 {
00039 
00040 bool SessionManager::saveState( QSessionManager& sm )
00041     {
00042     // If the session manager is ksmserver, save stacking
00043     // order, active window, active desktop etc. in phase 1,
00044     // as ksmserver assures no interaction will be done
00045     // before the WM finishes phase 1. Saving in phase 2 is
00046     // too late, as possible user interaction may change some things.
00047     // Phase2 is still needed though (ICCCM 5.2)
00048     char* sm_vendor = SmcVendor( static_cast< SmcConn >( sm.handle()));
00049     bool ksmserver = qstrcmp( sm_vendor, "KDE" ) == 0;
00050     free( sm_vendor );
00051     if ( !sm.isPhase2() )
00052         {
00053         Workspace::self()->sessionSaveStarted();
00054         if( ksmserver ) // save stacking order etc. before "save file?" etc. dialogs change it
00055             Workspace::self()->storeSession( kapp->sessionConfig(), SMSavePhase0 );
00056         sm.release(); // Qt doesn't automatically release in this case (bug?)
00057         sm.requestPhase2();
00058         return true;
00059         }
00060     Workspace::self()->storeSession( kapp->sessionConfig(), ksmserver ? SMSavePhase2 : SMSavePhase2Full );
00061     kapp->sessionConfig()->sync();
00062     return true;
00063     }
00064 
00065 // I bet this is broken, just like everywhere else in KDE
00066 bool SessionManager::commitData( QSessionManager& sm )
00067     {
00068     if ( !sm.isPhase2() )
00069         Workspace::self()->sessionSaveStarted();
00070     return true;
00071     }
00072 
00073 // Workspace
00074 
00080 void Workspace::storeSession( KConfig* config, SMSavePhase phase )
00081     {
00082     KConfigGroup cg(config, "Session");
00083     int count =  0;
00084     int active_client = -1;
00085     for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) 
00086         {
00087         Client* c = (*it);
00088         QByteArray sessionId = c->sessionId();
00089         QByteArray wmCommand = c->wmCommand();
00090         if ( sessionId.isEmpty() )
00091         // remember also applications that are not XSMP capable
00092         // and use the obsolete WM_COMMAND / WM_SAVE_YOURSELF
00093             if ( wmCommand.isEmpty() )
00094                 continue;
00095         count++;
00096         if( c->isActive())
00097             active_client = count;
00098         QString n = QString::number(count);
00099         if( phase == SMSavePhase2 || phase == SMSavePhase2Full )
00100             {
00101             cg.writeEntry( QString("sessionId")+n, sessionId.constData() );
00102             cg.writeEntry( QString("windowRole")+n, c->windowRole().constData() );
00103             cg.writeEntry( QString("wmCommand")+n, wmCommand.constData() );
00104             cg.writeEntry( QString("wmClientMachine")+n, c->wmClientMachine( true ).constData() );
00105             cg.writeEntry( QString("resourceName")+n, c->resourceName().constData() );
00106             cg.writeEntry( QString("resourceClass")+n, c->resourceClass().constData() );
00107             cg.writeEntry( QString("geometry")+n, QRect( c->calculateGravitation(true), c->clientSize() ) ); // FRAME
00108             cg.writeEntry( QString("restore")+n, c->geometryRestore() );
00109             cg.writeEntry( QString("fsrestore")+n, c->geometryFSRestore() );
00110             cg.writeEntry( QString("maximize")+n, (int) c->maximizeMode() );
00111             cg.writeEntry( QString("fullscreen")+n, (int) c->fullScreenMode() );
00112             cg.writeEntry( QString("desktop")+n, c->desktop() );
00113             // the config entry is called "iconified" for back. comp. reasons
00114             // (kconf_update script for updating session files would be too complicated)
00115             cg.writeEntry( QString("iconified")+n, c->isMinimized() );
00116             // the config entry is called "sticky" for back. comp. reasons
00117             cg.writeEntry( QString("sticky")+n, c->isOnAllDesktops() );
00118             cg.writeEntry( QString("shaded")+n, c->isShade() );
00119             // the config entry is called "staysOnTop" for back. comp. reasons
00120             cg.writeEntry( QString("staysOnTop")+n, c->keepAbove() );
00121             cg.writeEntry( QString("keepBelow")+n, c->keepBelow() );
00122             cg.writeEntry( QString("skipTaskbar")+n, c->skipTaskbar( true ) );
00123             cg.writeEntry( QString("skipPager")+n, c->skipPager() );
00124             // not really just set by user, but name kept for back. comp. reasons
00125             cg.writeEntry( QString("userNoBorder")+n, c->noBorder() );
00126             cg.writeEntry( QString("windowType")+n, windowTypeToTxt( c->windowType()));
00127             cg.writeEntry( QString("shortcut")+n, c->shortcut().toString());
00128             cg.writeEntry( QString("stackingOrder")+n, unconstrained_stacking_order.indexOf( c ));
00129             }
00130         }
00131     if( phase == SMSavePhase0 )
00132         {
00133         // it would be much simpler to save these values to the config file,
00134         // but both Qt and KDE treat phase1 and phase2 separately,
00135         // which results in different sessionkey and different config file :(
00136         session_active_client = active_client;
00137         session_desktop = currentDesktop();
00138         }
00139     else if( phase == SMSavePhase2 )
00140         {
00141         cg.writeEntry( "count", count );
00142         cg.writeEntry( "active", session_active_client );
00143         cg.writeEntry( "desktop", session_desktop );
00144         }
00145     else // SMSavePhase2Full
00146         {
00147         cg.writeEntry( "count", count );
00148         cg.writeEntry( "active", session_active_client );
00149         cg.writeEntry( "desktop", currentDesktop());
00150         }
00151     }
00152 
00153 
00159 void Workspace::loadSessionInfo()
00160     {
00161     session.clear();
00162     KConfigGroup cg(kapp->sessionConfig(), "Session");
00163     int count =  cg.readEntry( "count",0 );
00164     int active_client = cg.readEntry( "active",0 );
00165     for ( int i = 1; i <= count; i++ ) 
00166         {
00167         QString n = QString::number(i);
00168         SessionInfo* info = new SessionInfo;
00169         session.append( info );
00170         info->sessionId = cg.readEntry( QString("sessionId")+n, QString() ).toLatin1();
00171         info->windowRole = cg.readEntry( QString("windowRole")+n, QString() ).toLatin1();
00172         info->wmCommand = cg.readEntry( QString("wmCommand")+n, QString() ).toLatin1();
00173         info->wmClientMachine = cg.readEntry( QString("wmClientMachine")+n, QString() ).toLatin1();
00174         info->resourceName = cg.readEntry( QString("resourceName")+n, QString() ).toLatin1();
00175         info->resourceClass = cg.readEntry( QString("resourceClass")+n, QString() ).toLower().toLatin1();
00176         info->geometry = cg.readEntry( QString("geometry")+n,QRect() );
00177         info->restore = cg.readEntry( QString("restore")+n,QRect() );
00178         info->fsrestore = cg.readEntry( QString("fsrestore")+n,QRect() );
00179         info->maximized = cg.readEntry( QString("maximize")+n, 0 );
00180         info->fullscreen = cg.readEntry( QString("fullscreen")+n, 0 );
00181         info->desktop = cg.readEntry( QString("desktop")+n, 0 );
00182         info->minimized = cg.readEntry( QString("iconified")+n, false );
00183         info->onAllDesktops = cg.readEntry( QString("sticky")+n, false );
00184         info->shaded = cg.readEntry( QString("shaded")+n, false );
00185         info->keepAbove = cg.readEntry( QString("staysOnTop")+n, false  );
00186         info->keepBelow = cg.readEntry( QString("keepBelow")+n, false  );
00187         info->skipTaskbar = cg.readEntry( QString("skipTaskbar")+n, false  );
00188         info->skipPager = cg.readEntry( QString("skipPager")+n, false  );
00189         info->noBorder = cg.readEntry( QString("userNoBorder")+n, false  );
00190         info->windowType = txtToWindowType( cg.readEntry( QString("windowType")+n, QString() ).toLatin1());
00191         info->shortcut = cg.readEntry( QString("shortcut")+n, QString() );
00192         info->active = ( active_client == i );
00193         info->stackingOrder = cg.readEntry( QString("stackingOrder")+n, -1 );
00194         }
00195     }
00196 
00206 SessionInfo* Workspace::takeSessionInfo( Client* c )
00207     {
00208     SessionInfo *realInfo = 0;
00209     QByteArray sessionId = c->sessionId();
00210     QByteArray windowRole = c->windowRole();
00211     QByteArray wmCommand = c->wmCommand();
00212     QByteArray wmClientMachine = c->wmClientMachine( true );
00213     QByteArray resourceName = c->resourceName();
00214     QByteArray resourceClass = c->resourceClass();
00215 
00216     // First search ``session''
00217     if (! sessionId.isEmpty() ) 
00218         {
00219         // look for a real session managed client (algorithm suggested by ICCCM)
00220         foreach( SessionInfo* info, session )
00221             {
00222             if( realInfo )
00223                 break;
00224             if( info->sessionId == sessionId && sessionInfoWindowTypeMatch( c, info )) 
00225                 {
00226                 if( ! windowRole.isEmpty() ) 
00227                     {
00228                     if( info->windowRole == windowRole )
00229                         {
00230                         realInfo = info;
00231                         session.removeAll(info);
00232                         }
00233                     }
00234                 else 
00235                     {
00236                     if( info->windowRole.isEmpty()
00237                         && info->resourceName == resourceName
00238                         && info->resourceClass == resourceClass )
00239                         {
00240                         realInfo = info;
00241                         session.removeAll(info);
00242                         }
00243                     }
00244                 }
00245             }
00246         }
00247     else 
00248         {
00249         // look for a sessioninfo with matching features.
00250         foreach( SessionInfo* info, session )
00251             {
00252             if( realInfo )
00253                 break;
00254             if( info->resourceName == resourceName
00255                 && info->resourceClass == resourceClass
00256                 && info->wmClientMachine == wmClientMachine
00257                 && sessionInfoWindowTypeMatch( c, info ))
00258                 {
00259                 if ( wmCommand.isEmpty() || info->wmCommand == wmCommand )
00260                     {
00261                     realInfo = info;
00262                     session.removeAll( info );
00263                     }
00264                 }
00265             }
00266         }
00267     return realInfo;
00268     }
00269 
00270 bool Workspace::sessionInfoWindowTypeMatch( Client* c, SessionInfo* info )
00271     {
00272     if( info->windowType == -2 ) 
00273         { // undefined (not really part of NET::WindowType)
00274         return !c->isSpecialWindow();
00275         }
00276     return info->windowType == c->windowType();
00277     }
00278 
00279 static const char* const window_type_names[] = 
00280     {
00281     "Unknown", "Normal" , "Desktop", "Dock", "Toolbar", "Menu", "Dialog",
00282     "Override", "TopMenu", "Utility", "Splash"
00283     };
00284     // change also the two functions below when adding new entries
00285 
00286 const char* Workspace::windowTypeToTxt( NET::WindowType type )
00287     {
00288     if( type >= NET::Unknown && type <= NET::Splash )
00289         return window_type_names[ type + 1 ]; // +1 (unknown==-1)
00290     if( type == -2 ) // undefined (not really part of NET::WindowType)
00291         return "Undefined";
00292     kFatal() << "Unknown Window Type" ;
00293     return NULL;
00294     }
00295 
00296 NET::WindowType Workspace::txtToWindowType( const char* txt )
00297     {
00298     for( int i = NET::Unknown;
00299          i <= NET::Splash;
00300          ++i )
00301         if( qstrcmp( txt, window_type_names[ i + 1 ] ) == 0 ) // +1
00302             return static_cast< NET::WindowType >( i );
00303     return static_cast< NET::WindowType >( -2 ); // undefined
00304     }
00305 
00306 
00307 
00308 
00309 // KWin's focus stealing prevention causes problems with user interaction
00310 // during session save, as it prevents possible dialogs from getting focus.
00311 // Therefore it's temporarily disabled during session saving. Start of
00312 // session saving can be detected in SessionManager::saveState() above,
00313 // but Qt doesn't have API for saying when session saved finished (either
00314 // successfully, or was canceled). Therefore, create another connection
00315 // to session manager, that will provide this information.
00316 // Similarly the remember feature of window-specific settings should be disabled
00317 // during KDE shutdown when windows may move e.g. because of Kicker going away
00318 // (struts changing). When session saving starts, it can be cancelled, in which
00319 // case the shutdown_cancelled callback is invoked, or it's a checkpoint that
00320 // is immediatelly followed by save_complete, or finally it's a shutdown that
00321 // is immediatelly followed by die callback. So getting save_yourself with shutdown
00322 // set disables window-specific settings remembering, getting shutdown_cancelled
00323 // re-enables, otherwise KWin will go away after die.
00324 static void save_yourself( SmcConn conn_P, SmPointer ptr, int, Bool shutdown, int, Bool )
00325     {
00326     SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr );
00327     if( conn_P != session->connection())
00328         return;
00329     if( shutdown )
00330         Workspace::self()->disableRulesUpdates( true );
00331     SmcSaveYourselfDone( conn_P, True );
00332     }
00333 
00334 static void die( SmcConn conn_P, SmPointer ptr )
00335     {
00336     SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr );
00337     if( conn_P != session->connection())
00338         return;
00339     // session->saveDone(); we will quit anyway
00340     session->close();
00341     }
00342 
00343 static void save_complete( SmcConn conn_P, SmPointer ptr )
00344     {
00345     SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr );
00346     if( conn_P != session->connection())
00347         return;
00348     session->saveDone();
00349     }
00350 
00351 static void shutdown_cancelled( SmcConn conn_P, SmPointer ptr )
00352     {
00353     SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >( ptr );
00354     if( conn_P != session->connection())
00355         return;
00356     Workspace::self()->disableRulesUpdates( false ); // re-enable
00357     // no need to differentiate between successful finish and cancel
00358     session->saveDone();
00359     }
00360 
00361 void SessionSaveDoneHelper::saveDone()
00362     {
00363     Workspace::self()->sessionSaveDone();
00364     }
00365 
00366 SessionSaveDoneHelper::SessionSaveDoneHelper()
00367     {
00368     SmcCallbacks calls;
00369     calls.save_yourself.callback = save_yourself;
00370     calls.save_yourself.client_data = reinterpret_cast< SmPointer >(this);
00371     calls.die.callback = die;
00372     calls.die.client_data = reinterpret_cast< SmPointer >(this);
00373     calls.save_complete.callback = save_complete;
00374     calls.save_complete.client_data = reinterpret_cast< SmPointer >(this);
00375     calls.shutdown_cancelled.callback = shutdown_cancelled;
00376     calls.shutdown_cancelled.client_data = reinterpret_cast< SmPointer >(this);
00377     char* id = NULL;
00378     char err[ 11 ];
00379     conn = SmcOpenConnection( NULL, 0, 1, 0,
00380         SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask
00381         | SmcShutdownCancelledProcMask, &calls, NULL, &id, 10, err );
00382     if( id != NULL )
00383         free( id );
00384     if( conn == NULL )
00385         return; // no SM
00386     // set the required properties, mostly dummy values
00387     SmPropValue propvalue[ 5 ];
00388     SmProp props[ 5 ];
00389     propvalue[ 0 ].length = sizeof( int );
00390     int value0 = SmRestartNever; // so that this extra SM connection doesn't interfere
00391     propvalue[ 0 ].value = &value0;
00392     props[ 0 ].name = const_cast< char* >( SmRestartStyleHint );
00393     props[ 0 ].type = const_cast< char* >( SmCARD8 );
00394     props[ 0 ].num_vals = 1;
00395     props[ 0 ].vals = &propvalue[ 0 ];
00396     struct passwd* entry = getpwuid( geteuid() );
00397     propvalue[ 1 ].length = entry != NULL ? strlen( entry->pw_name ) : 0;
00398     propvalue[ 1 ].value = (SmPointer)( entry != NULL ? entry->pw_name : "" );
00399     props[ 1 ].name = const_cast< char* >( SmUserID );
00400     props[ 1 ].type = const_cast< char* >( SmARRAY8 );
00401     props[ 1 ].num_vals = 1;
00402     props[ 1 ].vals = &propvalue[ 1 ];
00403     propvalue[ 2 ].length = 0;
00404     propvalue[ 2 ].value = (SmPointer)( "" );
00405     props[ 2 ].name = const_cast< char* >( SmRestartCommand );
00406     props[ 2 ].type = const_cast< char* >( SmLISTofARRAY8 );
00407     props[ 2 ].num_vals = 1;
00408     props[ 2 ].vals = &propvalue[ 2 ];
00409     propvalue[ 3 ].length = strlen( "kwinsmhelper" );
00410     propvalue[ 3 ].value = (SmPointer)"kwinsmhelper";
00411     props[ 3 ].name = const_cast< char* >( SmProgram );
00412     props[ 3 ].type = const_cast< char* >( SmARRAY8 );
00413     props[ 3 ].num_vals = 1;
00414     props[ 3 ].vals = &propvalue[ 3 ];
00415     propvalue[ 4 ].length = 0;
00416     propvalue[ 4 ].value = (SmPointer)( "" );
00417     props[ 4 ].name = const_cast< char* >( SmCloneCommand );
00418     props[ 4 ].type = const_cast< char* >( SmLISTofARRAY8 );
00419     props[ 4 ].num_vals = 1;
00420     props[ 4 ].vals = &propvalue[ 4 ];
00421     SmProp* p[ 5 ] = { &props[ 0 ], &props[ 1 ], &props[ 2 ], &props[ 3 ], &props[ 4 ] };
00422     SmcSetProperties( conn, 5, p );
00423     notifier = new QSocketNotifier( IceConnectionNumber( SmcGetIceConnection( conn )),
00424         QSocketNotifier::Read, this );
00425     connect( notifier, SIGNAL( activated( int )), SLOT( processData()));
00426     }
00427 
00428 SessionSaveDoneHelper::~SessionSaveDoneHelper()
00429     {
00430     close();
00431     }
00432 
00433 void SessionSaveDoneHelper::close()
00434     {
00435     if( conn != NULL )
00436         {
00437         delete notifier;
00438         SmcCloseConnection( conn, 0, NULL );
00439         }
00440     conn = NULL;
00441     }
00442 
00443 void SessionSaveDoneHelper::processData()
00444     {
00445     if( conn != NULL )
00446         IceProcessMessages( SmcGetIceConnection( conn ), 0, 0 );
00447     }
00448 
00449 } // namespace
00450 
00451 #include "sm.moc"

KWin

Skip menu "KWin"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal