~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to ksmserver/legacy.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*****************************************************************
 
2
ksmserver - the KDE session management server
 
3
 
 
4
Copyright 2000 Matthias Ettrich <ettrich@kde.org>
 
5
Copyright 2005 Lubos Lunak <l.lunak@kde.org>
 
6
 
 
7
relatively small extensions by Oswald Buddenhagen <ob6@inf.tu-dresden.de>
 
8
 
 
9
some code taken from the dcopserver (part of the KDE libraries), which is
 
10
Copyright 1999 Matthias Ettrich <ettrich@kde.org>
 
11
Copyright 1999 Preston Brown <pbrown@kde.org>
 
12
 
 
13
Permission is hereby granted, free of charge, to any person obtaining a copy
 
14
of this software and associated documentation files (the "Software"), to deal
 
15
in the Software without restriction, including without limitation the rights
 
16
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 
17
copies of the Software, and to permit persons to whom the Software is
 
18
furnished to do so, subject to the following conditions:
 
19
 
 
20
The above copyright notice and this permission notice shall be included in
 
21
all copies or substantial portions of the Software.
 
22
 
 
23
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
24
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
25
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 
26
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 
27
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 
28
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
29
 
 
30
******************************************************************/
 
31
 
 
32
#include <config-workspace.h>
 
33
 
 
34
#ifdef HAVE_SYS_TIME_H
 
35
#include <sys/time.h>
 
36
#endif
 
37
 
 
38
#include "server.h"
 
39
 
 
40
#include <unistd.h>
 
41
 
 
42
 
 
43
#include <kconfig.h>
 
44
#include <kconfiggroup.h>
 
45
#include <kshell.h>
 
46
#include <kdebug.h>
 
47
#include <kwindowsystem.h>
 
48
 
 
49
#include <X11/Xlib.h>
 
50
#include <X11/Xutil.h>
 
51
#include <X11/Xatom.h>
 
52
 
 
53
/*
 
54
* Legacy session management
 
55
*/
 
56
 
 
57
#ifndef NO_LEGACY_SESSION_MANAGEMENT
 
58
const int WM_SAVE_YOURSELF_TIMEOUT = 4000;
 
59
 
 
60
static WindowMap* windowMapPtr = 0;
 
61
 
 
62
static Atom wm_save_yourself = XNone;
 
63
static Atom wm_protocols = XNone;
 
64
static Atom wm_client_leader = XNone;
 
65
static Atom sm_client_id = XNone;
 
66
 
 
67
static int winsErrorHandler(Display *, XErrorEvent *ev)
 
68
{
 
69
    if (windowMapPtr) {
 
70
        WindowMap::Iterator it = windowMapPtr->find(ev->resourceid);
 
71
        if (it != windowMapPtr->end())
 
72
            (*it).type = SM_ERROR;
 
73
    }
 
74
    return 0;
 
75
}
 
76
 
 
77
void KSMServer::performLegacySessionSave()
 
78
{
 
79
    kDebug( 1218 ) << "Saving legacy session apps";
 
80
    if (state == ClosingSubSession)
 
81
        return; //FIXME implement later
 
82
    // Setup error handler
 
83
    legacyWindows.clear();
 
84
    windowMapPtr = &legacyWindows;
 
85
    XErrorHandler oldHandler = XSetErrorHandler(winsErrorHandler);
 
86
    // Compute set of leader windows that need legacy session management
 
87
    // and determine which style (WM_COMMAND or WM_SAVE_YOURSELF)
 
88
    if( wm_save_yourself == (Atom)XNone ) {
 
89
        Atom atoms[ 4 ];
 
90
        const char* const names[]
 
91
            = { "WM_SAVE_YOURSELF", "WM_PROTOCOLS", "WM_CLIENT_LEADER", "SM_CLIENT_ID" };
 
92
        XInternAtoms( QX11Info::display(), const_cast< char** >( names ), 4,
 
93
            False, atoms );
 
94
        wm_save_yourself = atoms[ 0 ];
 
95
        wm_protocols = atoms[ 1 ];
 
96
        wm_client_leader = atoms[ 2 ];
 
97
        sm_client_id = atoms[ 3 ];
 
98
    }
 
99
    for ( QList<WId>::ConstIterator it = KWindowSystem::windows().begin();
 
100
        it != KWindowSystem::windows().end(); ++it) {
 
101
        WId leader = windowWmClientLeader( *it );
 
102
        if (!legacyWindows.contains(leader) && windowSessionId( *it, leader ).isEmpty()) {
 
103
            SMType wtype = SM_WMCOMMAND;
 
104
            int nprotocols = 0;
 
105
            Atom *protocols = 0;
 
106
            if( XGetWMProtocols(QX11Info::display(), leader, &protocols, &nprotocols)) {
 
107
                for (int i=0; i<nprotocols; i++)
 
108
                    if (protocols[i] == wm_save_yourself) {
 
109
                        wtype = SM_WMSAVEYOURSELF;
 
110
                        break;
 
111
                    }
 
112
                XFree((void*) protocols);
 
113
            }
 
114
            SMData data;
 
115
            data.type = wtype;
 
116
            XClassHint classHint;
 
117
            if( XGetClassHint( QX11Info::display(), leader, &classHint ) ) {
 
118
                data.wmclass1 = classHint.res_name;
 
119
                data.wmclass2 = classHint.res_class;
 
120
                XFree( classHint.res_name );
 
121
                XFree( classHint.res_class );
 
122
            }
 
123
            legacyWindows.insert(leader, data);
 
124
        }
 
125
    }
 
126
    // Open fresh display for sending WM_SAVE_YOURSELF
 
127
    XSync(QX11Info::display(), False);
 
128
    Display *newdisplay = XOpenDisplay(DisplayString(QX11Info::display()));
 
129
    if (!newdisplay) {
 
130
        windowMapPtr = NULL;
 
131
        XSetErrorHandler(oldHandler);
 
132
        return;
 
133
    }
 
134
    WId root = DefaultRootWindow(newdisplay);
 
135
    XGrabKeyboard(newdisplay, root, False,
 
136
                GrabModeAsync, GrabModeAsync, CurrentTime);
 
137
    XGrabPointer(newdisplay, root, False, Button1Mask|Button2Mask|Button3Mask,
 
138
                GrabModeAsync, GrabModeAsync, XNone, XNone, CurrentTime);
 
139
    // Send WM_SAVE_YOURSELF messages
 
140
    XEvent ev;
 
141
    int awaiting_replies = 0;
 
142
    for (WindowMap::Iterator it = legacyWindows.begin(); it != legacyWindows.end(); ++it) {
 
143
        if ( (*it).type == SM_WMSAVEYOURSELF ) {
 
144
            WId w = it.key();
 
145
            awaiting_replies += 1;
 
146
            memset(&ev, 0, sizeof(ev));
 
147
            ev.xclient.type = ClientMessage;
 
148
            ev.xclient.window = w;
 
149
            ev.xclient.message_type = wm_protocols;
 
150
            ev.xclient.format = 32;
 
151
            ev.xclient.data.l[0] = wm_save_yourself;
 
152
            ev.xclient.data.l[1] = QX11Info::appTime();
 
153
            XSelectInput(newdisplay, w, PropertyChangeMask|StructureNotifyMask);
 
154
            XSendEvent(newdisplay, w, False, 0, &ev);
 
155
        }
 
156
    }
 
157
    // Wait for change in WM_COMMAND with timeout
 
158
    XFlush(newdisplay);
 
159
    QTime start = QTime::currentTime();
 
160
    while (awaiting_replies > 0) {
 
161
        if (XPending(newdisplay)) {
 
162
            /* Process pending event */
 
163
            XNextEvent(newdisplay, &ev);
 
164
            if ( ( ev.xany.type == UnmapNotify ) ||
 
165
                ( ev.xany.type == PropertyNotify && ev.xproperty.atom == XA_WM_COMMAND ) ) {
 
166
                WindowMap::Iterator it = legacyWindows.find( ev.xany.window );
 
167
                if ( it != legacyWindows.end() && (*it).type != SM_WMCOMMAND ) {
 
168
                    awaiting_replies -= 1;
 
169
                    if ( (*it).type != SM_ERROR )
 
170
                        (*it).type = SM_WMCOMMAND;
 
171
                }
 
172
            }
 
173
        } else {
 
174
            /* Check timeout */
 
175
            int msecs = start.elapsed();
 
176
            if (msecs >= WM_SAVE_YOURSELF_TIMEOUT)
 
177
                break;
 
178
            /* Wait for more events */
 
179
            fd_set fds;
 
180
            FD_ZERO(&fds);
 
181
            int fd = ConnectionNumber(newdisplay);
 
182
            FD_SET(fd, &fds);
 
183
            struct timeval tmwait;
 
184
            tmwait.tv_sec = (WM_SAVE_YOURSELF_TIMEOUT - msecs) / 1000;
 
185
            tmwait.tv_usec = ((WM_SAVE_YOURSELF_TIMEOUT - msecs) % 1000) * 1000;
 
186
            ::select(fd+1, &fds, NULL, &fds, &tmwait);
 
187
        }
 
188
    }
 
189
    // Terminate work in new display
 
190
    XAllowEvents(newdisplay, ReplayPointer, CurrentTime);
 
191
    XAllowEvents(newdisplay, ReplayKeyboard, CurrentTime);
 
192
    XSync(newdisplay, False);
 
193
    XCloseDisplay(newdisplay);
 
194
    // Restore old error handler
 
195
    XSync(QX11Info::display(), False);
 
196
    XSetErrorHandler(oldHandler);
 
197
    for (WindowMap::Iterator it = legacyWindows.begin(); it != legacyWindows.end(); ++it) {
 
198
        if ( (*it).type != SM_ERROR) {
 
199
            WId w = it.key();
 
200
            (*it).wmCommand = windowWmCommand(w);
 
201
            (*it).wmClientMachine = windowWmClientMachine(w);
 
202
        }
 
203
    }
 
204
    kDebug( 1218 ) << "Done saving " << legacyWindows.count() << " legacy session apps";
 
205
}
 
206
 
 
207
/*!
 
208
Stores legacy session management data
 
209
*/
 
210
void KSMServer::storeLegacySession( KConfig* config )
 
211
{
 
212
    if (state == ClosingSubSession)
 
213
        return; //FIXME implement later
 
214
    // Write LegacySession data
 
215
    config->deleteGroup( "Legacy" + sessionGroup );
 
216
    KConfigGroup group( config, "Legacy" + sessionGroup );
 
217
    int count = 0;
 
218
    for (WindowMap::ConstIterator it = legacyWindows.constBegin(); it != legacyWindows.constEnd(); ++it) {
 
219
        if ( (*it).type != SM_ERROR) {
 
220
            if( excludeApps.contains( (*it).wmclass1.toLower())
 
221
                || excludeApps.contains( (*it).wmclass2.toLower()))
 
222
                continue;
 
223
            if ( !(*it).wmCommand.isEmpty() && !(*it).wmClientMachine.isEmpty() ) {
 
224
                count++;
 
225
                QString n = QString::number(count);
 
226
                group.writeEntry( QString("command")+n, (*it).wmCommand );
 
227
                group.writeEntry( QString("clientMachine")+n, (*it).wmClientMachine );
 
228
            }
 
229
        }
 
230
    }
 
231
    group.writeEntry( "count", count );
 
232
}
 
233
 
 
234
/*!
 
235
Restores legacy session management data (i.e. restart applications)
 
236
*/
 
237
void KSMServer::restoreLegacySession( KConfig* config )
 
238
{
 
239
    if( config->hasGroup( "Legacy" + sessionGroup )) {
 
240
        KConfigGroup group( config, "Legacy" + sessionGroup );
 
241
        restoreLegacySessionInternal( &group );
 
242
    } else if( wm == "kwin" ) { // backwards comp. - get it from kwinrc
 
243
        KConfigGroup group( config, sessionGroup );
 
244
        int count =  group.readEntry( "count", 0 );
 
245
        for ( int i = 1; i <= count; i++ ) {
 
246
            QString n = QString::number(i);
 
247
            if ( group.readEntry( QString("program")+n, QString() ) != wm )
 
248
                continue;
 
249
            QStringList restartCommand =
 
250
                group.readEntry( QString("restartCommand")+n, QStringList() );
 
251
            for( QStringList::ConstIterator it = restartCommand.constBegin();
 
252
                it != restartCommand.constEnd();
 
253
                ++it ) {
 
254
                if( (*it) == "-session" ) {
 
255
                    ++it;
 
256
                    if( it != restartCommand.constEnd()) {
 
257
                        KConfig cfg( "session/" + wm + '_' + (*it) );
 
258
                        KConfigGroup group(&cfg, "LegacySession");
 
259
                        restoreLegacySessionInternal( &group, ' ' );
 
260
                    }
 
261
                }
 
262
            }
 
263
        }
 
264
    }
 
265
}
 
266
 
 
267
void KSMServer::restoreLegacySessionInternal( KConfigGroup* config, char sep )
 
268
{
 
269
    int count = config->readEntry( "count",0 );
 
270
    for ( int i = 1; i <= count; i++ ) {
 
271
        QString n = QString::number(i);
 
272
        QStringList wmCommand = (sep == ',') ?
 
273
                config->readEntry( QString("command")+n, QStringList() ) :
 
274
                KShell::splitArgs( config->readEntry( QString("command")+n, QString() ) ); // close enough(?)
 
275
        if( wmCommand.isEmpty())
 
276
            continue;
 
277
        if( isWM( wmCommand.first()))
 
278
            continue;
 
279
        startApplication( wmCommand,
 
280
                        config->readEntry( QString("clientMachine")+n, QString() ),
 
281
                        config->readEntry( QString("userId")+n, QString() ));
 
282
    }
 
283
}
 
284
 
 
285
static QByteArray getQCStringProperty(WId w, Atom prop)
 
286
{
 
287
    Atom type;
 
288
    int format, status;
 
289
    unsigned long nitems = 0;
 
290
    unsigned long extra = 0;
 
291
    unsigned char *data = 0;
 
292
    QByteArray result = "";
 
293
    status = XGetWindowProperty( QX11Info::display(), w, prop, 0, 10000,
 
294
                                false, XA_STRING, &type, &format,
 
295
                                &nitems, &extra, &data );
 
296
    if ( status == Success) {
 
297
        if( data )
 
298
            result = (char*)data;
 
299
        XFree(data);
 
300
    }
 
301
    return result;
 
302
}
 
303
 
 
304
static QStringList getQStringListProperty(WId w, Atom prop)
 
305
{
 
306
    Atom type;
 
307
    int format, status;
 
308
    unsigned long nitems = 0;
 
309
    unsigned long extra = 0;
 
310
    unsigned char *data = 0;
 
311
    QStringList result;
 
312
 
 
313
    status = XGetWindowProperty( QX11Info::display(), w, prop, 0, 10000,
 
314
                                false, XA_STRING, &type, &format,
 
315
                                &nitems, &extra, &data );
 
316
    if ( status == Success) {
 
317
        if (!data)
 
318
            return result;
 
319
        for (int i=0; i<(int)nitems; i++) {
 
320
            result << QLatin1String( (const char*)data + i );
 
321
            while(data[i]) i++;
 
322
        }
 
323
        XFree(data);
 
324
    }
 
325
    return result;
 
326
}
 
327
 
 
328
QStringList KSMServer::windowWmCommand(WId w)
 
329
{
 
330
    QStringList ret = getQStringListProperty(w, XA_WM_COMMAND);
 
331
    // hacks here
 
332
    if( ret.count() == 1 ) {
 
333
        QString command = ret.first();
 
334
        // Mozilla is launched using wrapper scripts, so it's launched using "mozilla",
 
335
        // but the actual binary is "mozilla-bin" or "<path>/mozilla-bin", and that's what
 
336
        // will be also in WM_COMMAND - using this "mozilla-bin" doesn't work at all though
 
337
        if( command.endsWith( "mozilla-bin" ))
 
338
            return QStringList() << "mozilla";
 
339
        if( command.endsWith( "firefox-bin" ))
 
340
            return QStringList() << "firefox";
 
341
        if( command.endsWith( "thunderbird-bin" ))
 
342
            return QStringList() << "thunderbird";
 
343
        if( command.endsWith( "sunbird-bin" ))
 
344
            return QStringList() << "sunbird";
 
345
        if( command.endsWith( "seamonkey-bin" ))
 
346
            return QStringList() << "seamonkey";
 
347
    }
 
348
    return ret;
 
349
}
 
350
 
 
351
QString KSMServer::windowWmClientMachine(WId w)
 
352
{
 
353
    QByteArray result = getQCStringProperty(w, XA_WM_CLIENT_MACHINE);
 
354
    if (result.isEmpty()) {
 
355
        result = "localhost";
 
356
    } else {
 
357
        // special name for the local machine (localhost)
 
358
        char hostnamebuf[80];
 
359
        if (gethostname (hostnamebuf, sizeof hostnamebuf) >= 0) {
 
360
            hostnamebuf[sizeof(hostnamebuf)-1] = 0;
 
361
            if (result == hostnamebuf)
 
362
                result = "localhost";
 
363
            if(char *dot = strchr(hostnamebuf, '.')) {
 
364
                *dot = '\0';
 
365
                if(result == hostnamebuf)
 
366
                    result = "localhost";
 
367
            }
 
368
        }
 
369
    }
 
370
    return QLatin1String(result);
 
371
}
 
372
 
 
373
WId KSMServer::windowWmClientLeader(WId w)
 
374
{
 
375
    Atom type;
 
376
    int format, status;
 
377
    unsigned long nitems = 0;
 
378
    unsigned long extra = 0;
 
379
    unsigned char *data = 0;
 
380
    Window result = w;
 
381
    status = XGetWindowProperty( QX11Info::display(), w, wm_client_leader, 0, 10000,
 
382
                                false, XA_WINDOW, &type, &format,
 
383
                                &nitems, &extra, &data );
 
384
    if (status  == Success ) {
 
385
        if (data && nitems > 0)
 
386
            result = *((Window*) data);
 
387
        XFree(data);
 
388
    }
 
389
    return result;
 
390
}
 
391
 
 
392
 
 
393
/*
 
394
Returns sessionId for this client,
 
395
taken either from its window or from the leader window.
 
396
*/
 
397
QByteArray KSMServer::windowSessionId(WId w, WId leader)
 
398
{
 
399
    QByteArray result = getQCStringProperty(w, sm_client_id);
 
400
    if (result.isEmpty() && leader != (WId)None && leader != w)
 
401
        result = getQCStringProperty(leader, sm_client_id);
 
402
    return result;
 
403
}
 
404
#endif