~ubuntu-branches/ubuntu/lucid/kdebase/lucid

« back to all changes in this revision

Viewing changes to kwin/sm.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2008-05-27 12:09:48 UTC
  • mfrom: (1.1.13 upstream)
  • Revision ID: james.westby@ubuntu.com-20080527120948-dottsyd5rcwhzd36
Tags: 4:4.0.80-1ubuntu1
* Merge with Debian
 - remove 97_fix_target_link_libraries.diff
 - Add replaces/conflicts on -kde4 packages

Show diffs side-by-side

added added

removed removed

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