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

« back to all changes in this revision

Viewing changes to kwin/sm.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
 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
This program is free software; you can redistribute it and/or modify
 
9
it under the terms of the GNU General Public License as published by
 
10
the Free Software Foundation; either version 2 of the License, or
 
11
(at your option) any later version.
 
12
 
 
13
This program is distributed in the hope that it will be useful,
 
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
GNU General Public License for more details.
 
17
 
 
18
You should have received a copy of the GNU General Public License
 
19
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
*********************************************************************/
 
21
 
 
22
#include "sm.h"
 
23
 
 
24
#include <unistd.h>
 
25
#include <stdlib.h>
 
26
#include <pwd.h>
 
27
#include <fixx11h.h>
 
28
#include <kconfig.h>
 
29
#include <kglobal.h>
 
30
 
 
31
#include "workspace.h"
 
32
#include "client.h"
 
33
#include <QDBusInterface>
 
34
#include <QSocketNotifier>
 
35
#include <QSessionManager>
 
36
#include <kdebug.h>
 
37
 
 
38
namespace KWin
 
39
{
 
40
 
 
41
bool SessionManager::saveState(QSessionManager& sm)
 
42
{
 
43
    // If the session manager is ksmserver, save stacking
 
44
    // order, active window, active desktop etc. in phase 1,
 
45
    // as ksmserver assures no interaction will be done
 
46
    // before the WM finishes phase 1. Saving in phase 2 is
 
47
    // too late, as possible user interaction may change some things.
 
48
    // Phase2 is still needed though (ICCCM 5.2)
 
49
    char* sm_vendor = SmcVendor(static_cast< SmcConn >(sm.handle()));
 
50
    bool ksmserver = qstrcmp(sm_vendor, "KDE") == 0;
 
51
    free(sm_vendor);
 
52
    if (!sm.isPhase2()) {
 
53
        Workspace::self()->sessionSaveStarted();
 
54
        if (ksmserver)   // save stacking order etc. before "save file?" etc. dialogs change it
 
55
            Workspace::self()->storeSession(kapp->sessionConfig(), SMSavePhase0);
 
56
        sm.release(); // Qt doesn't automatically release in this case (bug?)
 
57
        sm.requestPhase2();
 
58
        return true;
 
59
    }
 
60
    Workspace::self()->storeSession(kapp->sessionConfig(), ksmserver ? SMSavePhase2 : SMSavePhase2Full);
 
61
    kapp->sessionConfig()->sync();
 
62
    return true;
 
63
}
 
64
 
 
65
// I bet this is broken, just like everywhere else in KDE
 
66
bool SessionManager::commitData(QSessionManager& sm)
 
67
{
 
68
    if (!sm.isPhase2())
 
69
        Workspace::self()->sessionSaveStarted();
 
70
    return true;
 
71
}
 
72
 
 
73
// Workspace
 
74
 
 
75
/*!
 
76
  Stores the current session in the config file
 
77
 
 
78
  \sa loadSessionInfo()
 
79
 */
 
80
void Workspace::storeSession(KConfig* config, SMSavePhase phase)
 
81
{
 
82
    KConfigGroup cg(config, "Session");
 
83
    int count =  0;
 
84
    int active_client = -1;
 
85
 
 
86
    if (phase == SMSavePhase2 || phase == SMSavePhase2Full) {
 
87
        cg.writeEntry("tiling", tilingEnabled());
 
88
        if (tilingEnabled()) {
 
89
            kDebug(1212) << "Tiling was ON";
 
90
            setTilingEnabled(false);
 
91
        }
 
92
    }
 
93
 
 
94
    for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) {
 
95
        Client* c = (*it);
 
96
        QByteArray sessionId = c->sessionId();
 
97
        QByteArray wmCommand = c->wmCommand();
 
98
        if (sessionId.isEmpty())
 
99
            // remember also applications that are not XSMP capable
 
100
            // and use the obsolete WM_COMMAND / WM_SAVE_YOURSELF
 
101
            if (wmCommand.isEmpty())
 
102
                continue;
 
103
        count++;
 
104
        if (c->isActive())
 
105
            active_client = count;
 
106
        if (phase == SMSavePhase2 || phase == SMSavePhase2Full)
 
107
            storeClient(cg, count, c);
 
108
    }
 
109
    if (phase == SMSavePhase0) {
 
110
        // it would be much simpler to save these values to the config file,
 
111
        // but both Qt and KDE treat phase1 and phase2 separately,
 
112
        // which results in different sessionkey and different config file :(
 
113
        session_active_client = active_client;
 
114
        session_desktop = currentDesktop();
 
115
    } else if (phase == SMSavePhase2) {
 
116
        cg.writeEntry("count", count);
 
117
        cg.writeEntry("active", session_active_client);
 
118
        cg.writeEntry("desktop", session_desktop);
 
119
    } else { // SMSavePhase2Full
 
120
        cg.writeEntry("count", count);
 
121
        cg.writeEntry("active", session_active_client);
 
122
        cg.writeEntry("desktop", currentDesktop());
 
123
    }
 
124
}
 
125
 
 
126
void Workspace::storeClient(KConfigGroup &cg, int num, Client *c)
 
127
{
 
128
    c->setSessionInteract(false); //make sure we get the real values
 
129
    QString n = QString::number(num);
 
130
    cg.writeEntry(QString("sessionId") + n, c->sessionId().constData());
 
131
    cg.writeEntry(QString("windowRole") + n, c->windowRole().constData());
 
132
    cg.writeEntry(QString("wmCommand") + n, c->wmCommand().constData());
 
133
    cg.writeEntry(QString("wmClientMachine") + n, c->wmClientMachine(true).constData());
 
134
    cg.writeEntry(QString("resourceName") + n, c->resourceName().constData());
 
135
    cg.writeEntry(QString("resourceClass") + n, c->resourceClass().constData());
 
136
    cg.writeEntry(QString("geometry") + n, QRect(c->calculateGravitation(true), c->clientSize()));   // FRAME
 
137
    cg.writeEntry(QString("restore") + n, c->geometryRestore());
 
138
    cg.writeEntry(QString("fsrestore") + n, c->geometryFSRestore());
 
139
    cg.writeEntry(QString("maximize") + n, (int) c->maximizeMode());
 
140
    cg.writeEntry(QString("fullscreen") + n, (int) c->fullScreenMode());
 
141
    cg.writeEntry(QString("desktop") + n, c->desktop());
 
142
    // the config entry is called "iconified" for back. comp. reasons
 
143
    // (kconf_update script for updating session files would be too complicated)
 
144
    cg.writeEntry(QString("iconified") + n, c->isMinimized());
 
145
    cg.writeEntry(QString("opacity") + n, c->opacity());
 
146
    // the config entry is called "sticky" for back. comp. reasons
 
147
    cg.writeEntry(QString("sticky") + n, c->isOnAllDesktops());
 
148
    cg.writeEntry(QString("shaded") + n, c->isShade());
 
149
    // the config entry is called "staysOnTop" for back. comp. reasons
 
150
    cg.writeEntry(QString("staysOnTop") + n, c->keepAbove());
 
151
    cg.writeEntry(QString("keepBelow") + n, c->keepBelow());
 
152
    cg.writeEntry(QString("skipTaskbar") + n, c->skipTaskbar(true));
 
153
    cg.writeEntry(QString("skipPager") + n, c->skipPager());
 
154
    cg.writeEntry(QString("skipSwitcher") + n, c->skipSwitcher());
 
155
    // not really just set by user, but name kept for back. comp. reasons
 
156
    cg.writeEntry(QString("userNoBorder") + n, c->noBorder());
 
157
    cg.writeEntry(QString("windowType") + n, windowTypeToTxt(c->windowType()));
 
158
    cg.writeEntry(QString("shortcut") + n, c->shortcut().toString());
 
159
    cg.writeEntry(QString("stackingOrder") + n, unconstrained_stacking_order.indexOf(c));
 
160
    int group = 0;
 
161
    if (c->clientGroup())
 
162
        group = c->clientGroup()->clients().count() > 1 ?
 
163
                // KConfig doesn't support long so we need to live with less precision on 64-bit systems
 
164
                static_cast<int>(reinterpret_cast<long>(c->clientGroup())) : 0;
 
165
    cg.writeEntry(QString("clientGroup") + n, group);
 
166
    cg.writeEntry(QString("activities") + n, c->activities());
 
167
}
 
168
 
 
169
void Workspace::storeSubSession(const QString &name, QSet<QByteArray> sessionIds)
 
170
{
 
171
    //TODO clear it first
 
172
    KConfigGroup cg(KGlobal::config(), QString("SubSession: ") + name);
 
173
    int count =  0;
 
174
    int active_client = -1;
 
175
    for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) {
 
176
        Client* c = (*it);
 
177
        QByteArray sessionId = c->sessionId();
 
178
        QByteArray wmCommand = c->wmCommand();
 
179
        if (sessionId.isEmpty())
 
180
            // remember also applications that are not XSMP capable
 
181
            // and use the obsolete WM_COMMAND / WM_SAVE_YOURSELF
 
182
            if (wmCommand.isEmpty())
 
183
                continue;
 
184
        if (!sessionIds.contains(sessionId))
 
185
            continue;
 
186
 
 
187
        kDebug() << "storing" << sessionId;
 
188
        count++;
 
189
        if (c->isActive())
 
190
            active_client = count;
 
191
        storeClient(cg, count, c);
 
192
    }
 
193
    cg.writeEntry("count", count);
 
194
    cg.writeEntry("active", active_client);
 
195
    //cg.writeEntry( "desktop", currentDesktop());
 
196
}
 
197
 
 
198
bool Workspace::stopActivity(const QString &id)
 
199
{
 
200
    if (sessionSaving()) {
 
201
        return false; //ksmserver doesn't queue requests (yet)
 
202
        //FIXME what about session *loading*?
 
203
    }
 
204
    //ugly hack to avoid dbus deadlocks
 
205
    QMetaObject::invokeMethod(this, "reallyStopActivity", Qt::QueuedConnection, Q_ARG(QString, id));
 
206
    //then lie and assume it worked.
 
207
    return true;
 
208
}
 
209
 
 
210
void Workspace::reallyStopActivity(const QString &id)
 
211
{
 
212
    QStringList openActivities = openActivityList();
 
213
 
 
214
    QSet<QByteArray> saveSessionIds;
 
215
    QSet<QByteArray> dontCloseSessionIds;
 
216
    kDebug() << id;
 
217
    for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) {
 
218
        Client* c = (*it);
 
219
        QByteArray sessionId = c->sessionId();
 
220
        if (sessionId.isEmpty())
 
221
            continue; //TODO support old wm_command apps too?
 
222
 
 
223
        kDebug() << sessionId;
 
224
 
 
225
        //if it's on the activity that's closing, it needs saving
 
226
        //but if a process is on some other open activity, I don't wanna close it yet
 
227
        //this is, of course, complicated by a process having many windows.
 
228
        if (c->isOnAllActivities()) {
 
229
            dontCloseSessionIds << sessionId;
 
230
            continue;
 
231
        }
 
232
        QStringList activities = c->activities();
 
233
        foreach (const QString & activityId, activities) {
 
234
            if (activityId == id)
 
235
                saveSessionIds << sessionId;
 
236
            else if (openActivities.contains(activityId))
 
237
                dontCloseSessionIds << sessionId;
 
238
        }
 
239
    }
 
240
 
 
241
    storeSubSession(id, saveSessionIds);
 
242
 
 
243
    QStringList saveAndClose;
 
244
    QStringList saveOnly;
 
245
    foreach (const QByteArray & sessionId, saveSessionIds) {
 
246
        if (dontCloseSessionIds.contains(sessionId))
 
247
            saveOnly << sessionId;
 
248
        else
 
249
            saveAndClose << sessionId;
 
250
    }
 
251
 
 
252
    kDebug() << "saveActivity" << id << saveAndClose << saveOnly;
 
253
 
 
254
    //pass off to ksmserver
 
255
    QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface");
 
256
    if (ksmserver.isValid()) {
 
257
        QDBusMessage reply = ksmserver.call("saveSubSession", id, saveAndClose, saveOnly);
 
258
        if (reply.type() == QDBusMessage::ErrorMessage)
 
259
            kDebug() << "dbus error:" << reply.errorMessage();
 
260
        else
 
261
            kDebug() << "dbus succeeded";
 
262
    } else
 
263
        kDebug() << "couldn't get ksmserver interface";
 
264
}
 
265
 
 
266
/*!
 
267
  Loads the session information from the config file.
 
268
 
 
269
  \sa storeSession()
 
270
 */
 
271
void Workspace::loadSessionInfo()
 
272
{
 
273
    session.clear();
 
274
    KConfigGroup cg(kapp->sessionConfig(), "Session");
 
275
 
 
276
    setTilingEnabled(cg.readEntry("tiling", false));
 
277
 
 
278
    addSessionInfo(cg);
 
279
}
 
280
 
 
281
void Workspace::addSessionInfo(KConfigGroup &cg)
 
282
{
 
283
    int count =  cg.readEntry("count", 0);
 
284
    int active_client = cg.readEntry("active", 0);
 
285
    for (int i = 1; i <= count; i++) {
 
286
        QString n = QString::number(i);
 
287
        SessionInfo* info = new SessionInfo;
 
288
        session.append(info);
 
289
        info->sessionId = cg.readEntry(QString("sessionId") + n, QString()).toLatin1();
 
290
        info->windowRole = cg.readEntry(QString("windowRole") + n, QString()).toLatin1();
 
291
        info->wmCommand = cg.readEntry(QString("wmCommand") + n, QString()).toLatin1();
 
292
        info->wmClientMachine = cg.readEntry(QString("wmClientMachine") + n, QString()).toLatin1();
 
293
        info->resourceName = cg.readEntry(QString("resourceName") + n, QString()).toLatin1();
 
294
        info->resourceClass = cg.readEntry(QString("resourceClass") + n, QString()).toLower().toLatin1();
 
295
        info->geometry = cg.readEntry(QString("geometry") + n, QRect());
 
296
        info->restore = cg.readEntry(QString("restore") + n, QRect());
 
297
        info->fsrestore = cg.readEntry(QString("fsrestore") + n, QRect());
 
298
        info->maximized = cg.readEntry(QString("maximize") + n, 0);
 
299
        info->fullscreen = cg.readEntry(QString("fullscreen") + n, 0);
 
300
        info->desktop = cg.readEntry(QString("desktop") + n, 0);
 
301
        info->minimized = cg.readEntry(QString("iconified") + n, false);
 
302
        info->opacity = cg.readEntry(QString("opacity") + n, 1.0);
 
303
        info->onAllDesktops = cg.readEntry(QString("sticky") + n, false);
 
304
        info->shaded = cg.readEntry(QString("shaded") + n, false);
 
305
        info->keepAbove = cg.readEntry(QString("staysOnTop") + n, false);
 
306
        info->keepBelow = cg.readEntry(QString("keepBelow") + n, false);
 
307
        info->skipTaskbar = cg.readEntry(QString("skipTaskbar") + n, false);
 
308
        info->skipPager = cg.readEntry(QString("skipPager") + n, false);
 
309
        info->skipSwitcher = cg.readEntry(QString("skipSwitcher") + n, false);
 
310
        info->noBorder = cg.readEntry(QString("userNoBorder") + n, false);
 
311
        info->windowType = txtToWindowType(cg.readEntry(QString("windowType") + n, QString()).toLatin1());
 
312
        info->shortcut = cg.readEntry(QString("shortcut") + n, QString());
 
313
        info->active = (active_client == i);
 
314
        info->stackingOrder = cg.readEntry(QString("stackingOrder") + n, -1);
 
315
        info->clientGroup = cg.readEntry(QString("clientGroup") + n, 0);
 
316
        info->clientGroupClient = NULL;
 
317
        info->activities = cg.readEntry(QString("activities") + n, QStringList());
 
318
    }
 
319
}
 
320
 
 
321
void Workspace::loadSubSessionInfo(const QString &name)
 
322
{
 
323
    KConfigGroup cg(KGlobal::config(), QString("SubSession: ") + name);
 
324
    addSessionInfo(cg);
 
325
}
 
326
 
 
327
bool Workspace::startActivity(const QString &id)
 
328
{
 
329
    if (sessionSaving()) {
 
330
        return false; //ksmserver doesn't queue requests (yet)
 
331
    }
 
332
    if (!allActivities_.contains(id)) {
 
333
        return false; //bogus id
 
334
    }
 
335
 
 
336
    loadSubSessionInfo(id);
 
337
 
 
338
    QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface");
 
339
    if (ksmserver.isValid()) {
 
340
        QDBusMessage reply = ksmserver.call("restoreSubSession", id);
 
341
        if (reply.type() == QDBusMessage::ErrorMessage) {
 
342
            kDebug() << "dbus error:" << reply.errorMessage();
 
343
            return false;
 
344
        }
 
345
    } else {
 
346
        kDebug() << "couldn't get ksmserver interface";
 
347
        return false;
 
348
    }
 
349
    return true;
 
350
}
 
351
 
 
352
/*!
 
353
  Returns a SessionInfo for client \a c. The returned session
 
354
  info is removed from the storage. It's up to the caller to delete it.
 
355
 
 
356
  This function is called when a new window is mapped and must be managed.
 
357
  We try to find a matching entry in the session.
 
358
 
 
359
  May return 0 if there's no session info for the client.
 
360
 */
 
361
SessionInfo* Workspace::takeSessionInfo(Client* c)
 
362
{
 
363
    SessionInfo *realInfo = 0;
 
364
    QByteArray sessionId = c->sessionId();
 
365
    QByteArray windowRole = c->windowRole();
 
366
    QByteArray wmCommand = c->wmCommand();
 
367
    QByteArray wmClientMachine = c->wmClientMachine(true);
 
368
    QByteArray resourceName = c->resourceName();
 
369
    QByteArray resourceClass = c->resourceClass();
 
370
 
 
371
    // First search ``session''
 
372
    if (! sessionId.isEmpty()) {
 
373
        // look for a real session managed client (algorithm suggested by ICCCM)
 
374
        foreach (SessionInfo * info, session) {
 
375
            if (realInfo)
 
376
                break;
 
377
            if (info->sessionId == sessionId && sessionInfoWindowTypeMatch(c, info)) {
 
378
                if (! windowRole.isEmpty()) {
 
379
                    if (info->windowRole == windowRole) {
 
380
                        realInfo = info;
 
381
                        session.removeAll(info);
 
382
                    }
 
383
                } else {
 
384
                    if (info->windowRole.isEmpty()
 
385
                            && info->resourceName == resourceName
 
386
                            && info->resourceClass == resourceClass) {
 
387
                        realInfo = info;
 
388
                        session.removeAll(info);
 
389
                    }
 
390
                }
 
391
            }
 
392
        }
 
393
    } else {
 
394
        // look for a sessioninfo with matching features.
 
395
        foreach (SessionInfo * info, session) {
 
396
            if (realInfo)
 
397
                break;
 
398
            if (info->resourceName == resourceName
 
399
                    && info->resourceClass == resourceClass
 
400
                    && info->wmClientMachine == wmClientMachine
 
401
                    && sessionInfoWindowTypeMatch(c, info)) {
 
402
                if (wmCommand.isEmpty() || info->wmCommand == wmCommand) {
 
403
                    realInfo = info;
 
404
                    session.removeAll(info);
 
405
                }
 
406
            }
 
407
        }
 
408
    }
 
409
 
 
410
    // Set clientGroupClient for other clients in the same group
 
411
    if (realInfo && realInfo->clientGroup)
 
412
        foreach (SessionInfo * info, session)
 
413
        if (!info->clientGroupClient && info->clientGroup == realInfo->clientGroup)
 
414
            info->clientGroupClient = c;
 
415
 
 
416
    return realInfo;
 
417
}
 
418
 
 
419
bool Workspace::sessionInfoWindowTypeMatch(Client* c, SessionInfo* info)
 
420
{
 
421
    if (info->windowType == -2) {
 
422
        // undefined (not really part of NET::WindowType)
 
423
        return !c->isSpecialWindow();
 
424
    }
 
425
    return info->windowType == c->windowType();
 
426
}
 
427
 
 
428
static const char* const window_type_names[] = {
 
429
    "Unknown", "Normal" , "Desktop", "Dock", "Toolbar", "Menu", "Dialog",
 
430
    "Override", "TopMenu", "Utility", "Splash"
 
431
};
 
432
// change also the two functions below when adding new entries
 
433
 
 
434
const char* Workspace::windowTypeToTxt(NET::WindowType type)
 
435
{
 
436
    if (type >= NET::Unknown && type <= NET::Splash)
 
437
        return window_type_names[ type + 1 ]; // +1 (unknown==-1)
 
438
    if (type == -2)   // undefined (not really part of NET::WindowType)
 
439
        return "Undefined";
 
440
    kFatal(1212) << "Unknown Window Type" ;
 
441
    return NULL;
 
442
}
 
443
 
 
444
NET::WindowType Workspace::txtToWindowType(const char* txt)
 
445
{
 
446
    for (int i = NET::Unknown;
 
447
            i <= NET::Splash;
 
448
            ++i)
 
449
        if (qstrcmp(txt, window_type_names[ i + 1 ]) == 0)     // +1
 
450
            return static_cast< NET::WindowType >(i);
 
451
    return static_cast< NET::WindowType >(-2);   // undefined
 
452
}
 
453
 
 
454
 
 
455
 
 
456
 
 
457
// KWin's focus stealing prevention causes problems with user interaction
 
458
// during session save, as it prevents possible dialogs from getting focus.
 
459
// Therefore it's temporarily disabled during session saving. Start of
 
460
// session saving can be detected in SessionManager::saveState() above,
 
461
// but Qt doesn't have API for saying when session saved finished (either
 
462
// successfully, or was canceled). Therefore, create another connection
 
463
// to session manager, that will provide this information.
 
464
// Similarly the remember feature of window-specific settings should be disabled
 
465
// during KDE shutdown when windows may move e.g. because of Kicker going away
 
466
// (struts changing). When session saving starts, it can be cancelled, in which
 
467
// case the shutdown_cancelled callback is invoked, or it's a checkpoint that
 
468
// is immediatelly followed by save_complete, or finally it's a shutdown that
 
469
// is immediatelly followed by die callback. So getting save_yourself with shutdown
 
470
// set disables window-specific settings remembering, getting shutdown_cancelled
 
471
// re-enables, otherwise KWin will go away after die.
 
472
static void save_yourself(SmcConn conn_P, SmPointer ptr, int, Bool shutdown, int, Bool)
 
473
{
 
474
    SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr);
 
475
    if (conn_P != session->connection())
 
476
        return;
 
477
    if (shutdown)
 
478
        Workspace::self()->disableRulesUpdates(true);
 
479
    SmcSaveYourselfDone(conn_P, True);
 
480
}
 
481
 
 
482
static void die(SmcConn conn_P, SmPointer ptr)
 
483
{
 
484
    SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr);
 
485
    if (conn_P != session->connection())
 
486
        return;
 
487
    // session->saveDone(); we will quit anyway
 
488
    session->close();
 
489
}
 
490
 
 
491
static void save_complete(SmcConn conn_P, SmPointer ptr)
 
492
{
 
493
    SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr);
 
494
    if (conn_P != session->connection())
 
495
        return;
 
496
    session->saveDone();
 
497
}
 
498
 
 
499
static void shutdown_cancelled(SmcConn conn_P, SmPointer ptr)
 
500
{
 
501
    SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr);
 
502
    if (conn_P != session->connection())
 
503
        return;
 
504
    Workspace::self()->disableRulesUpdates(false);   // re-enable
 
505
    // no need to differentiate between successful finish and cancel
 
506
    session->saveDone();
 
507
}
 
508
 
 
509
void SessionSaveDoneHelper::saveDone()
 
510
{
 
511
    Workspace::self()->sessionSaveDone();
 
512
}
 
513
 
 
514
SessionSaveDoneHelper::SessionSaveDoneHelper()
 
515
{
 
516
    SmcCallbacks calls;
 
517
    calls.save_yourself.callback = save_yourself;
 
518
    calls.save_yourself.client_data = reinterpret_cast< SmPointer >(this);
 
519
    calls.die.callback = die;
 
520
    calls.die.client_data = reinterpret_cast< SmPointer >(this);
 
521
    calls.save_complete.callback = save_complete;
 
522
    calls.save_complete.client_data = reinterpret_cast< SmPointer >(this);
 
523
    calls.shutdown_cancelled.callback = shutdown_cancelled;
 
524
    calls.shutdown_cancelled.client_data = reinterpret_cast< SmPointer >(this);
 
525
    char* id = NULL;
 
526
    char err[ 11 ];
 
527
    conn = SmcOpenConnection(NULL, 0, 1, 0,
 
528
                             SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask
 
529
                             | SmcShutdownCancelledProcMask, &calls, NULL, &id, 10, err);
 
530
    if (id != NULL)
 
531
        free(id);
 
532
    if (conn == NULL)
 
533
        return; // no SM
 
534
    // set the required properties, mostly dummy values
 
535
    SmPropValue propvalue[ 5 ];
 
536
    SmProp props[ 5 ];
 
537
    propvalue[ 0 ].length = sizeof(unsigned char);
 
538
    unsigned char value0 = SmRestartNever; // so that this extra SM connection doesn't interfere
 
539
    propvalue[ 0 ].value = &value0;
 
540
    props[ 0 ].name = const_cast< char* >(SmRestartStyleHint);
 
541
    props[ 0 ].type = const_cast< char* >(SmCARD8);
 
542
    props[ 0 ].num_vals = 1;
 
543
    props[ 0 ].vals = &propvalue[ 0 ];
 
544
    struct passwd* entry = getpwuid(geteuid());
 
545
    propvalue[ 1 ].length = entry != NULL ? strlen(entry->pw_name) : 0;
 
546
    propvalue[ 1 ].value = (SmPointer)(entry != NULL ? entry->pw_name : "");
 
547
    props[ 1 ].name = const_cast< char* >(SmUserID);
 
548
    props[ 1 ].type = const_cast< char* >(SmARRAY8);
 
549
    props[ 1 ].num_vals = 1;
 
550
    props[ 1 ].vals = &propvalue[ 1 ];
 
551
    propvalue[ 2 ].length = 0;
 
552
    propvalue[ 2 ].value = (SmPointer)("");
 
553
    props[ 2 ].name = const_cast< char* >(SmRestartCommand);
 
554
    props[ 2 ].type = const_cast< char* >(SmLISTofARRAY8);
 
555
    props[ 2 ].num_vals = 1;
 
556
    props[ 2 ].vals = &propvalue[ 2 ];
 
557
    propvalue[ 3 ].length = strlen("kwinsmhelper");
 
558
    propvalue[ 3 ].value = (SmPointer)"kwinsmhelper";
 
559
    props[ 3 ].name = const_cast< char* >(SmProgram);
 
560
    props[ 3 ].type = const_cast< char* >(SmARRAY8);
 
561
    props[ 3 ].num_vals = 1;
 
562
    props[ 3 ].vals = &propvalue[ 3 ];
 
563
    propvalue[ 4 ].length = 0;
 
564
    propvalue[ 4 ].value = (SmPointer)("");
 
565
    props[ 4 ].name = const_cast< char* >(SmCloneCommand);
 
566
    props[ 4 ].type = const_cast< char* >(SmLISTofARRAY8);
 
567
    props[ 4 ].num_vals = 1;
 
568
    props[ 4 ].vals = &propvalue[ 4 ];
 
569
    SmProp* p[ 5 ] = { &props[ 0 ], &props[ 1 ], &props[ 2 ], &props[ 3 ], &props[ 4 ] };
 
570
    SmcSetProperties(conn, 5, p);
 
571
    notifier = new QSocketNotifier(IceConnectionNumber(SmcGetIceConnection(conn)),
 
572
                                   QSocketNotifier::Read, this);
 
573
    connect(notifier, SIGNAL(activated(int)), SLOT(processData()));
 
574
}
 
575
 
 
576
SessionSaveDoneHelper::~SessionSaveDoneHelper()
 
577
{
 
578
    close();
 
579
}
 
580
 
 
581
void SessionSaveDoneHelper::close()
 
582
{
 
583
    if (conn != NULL) {
 
584
        delete notifier;
 
585
        SmcCloseConnection(conn, 0, NULL);
 
586
    }
 
587
    conn = NULL;
 
588
}
 
589
 
 
590
void SessionSaveDoneHelper::processData()
 
591
{
 
592
    if (conn != NULL)
 
593
        IceProcessMessages(SmcGetIceConnection(conn), 0, 0);
 
594
}
 
595
 
 
596
void Workspace::sessionSaveDone()
 
597
{
 
598
    session_saving = false;
 
599
    //remove sessionInteract flag from all clients
 
600
    foreach (Client * c, clients) {
 
601
        c->setSessionInteract(false);
 
602
    }
 
603
}
 
604
 
 
605
} // namespace
 
606
 
 
607
#include "sm.moc"