You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
533 lines
21 KiB
533 lines
21 KiB
/******************************************************************** |
|
KWin - the KDE window manager |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org> |
|
Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*********************************************************************/ |
|
|
|
#include "sm.h" |
|
|
|
#include <unistd.h> |
|
#include <stdlib.h> |
|
#include <pwd.h> |
|
#include <fixx11h.h> |
|
#include <kconfig.h> |
|
|
|
#include "workspace.h" |
|
#include "client.h" |
|
#include <QDebug> |
|
#include <QFile> |
|
#include <QSocketNotifier> |
|
#include <QSessionManager> |
|
|
|
namespace KWin |
|
{ |
|
|
|
static bool gs_sessionManagerIsKSMServer = false; |
|
static KConfig *sessionConfig(QString id, QString key) |
|
{ |
|
static KConfig *config = nullptr; |
|
static QString lastId; |
|
static QString lastKey; |
|
static QString pattern = QString(QLatin1String("session/%1_%2_%3")).arg(qApp->applicationName()); |
|
if (id != lastId || key != lastKey) { |
|
delete config; |
|
config = nullptr; |
|
} |
|
lastId = id; |
|
lastKey = key; |
|
if (!config) { |
|
config = new KConfig(pattern.arg(id).arg(key), KConfig::SimpleConfig); |
|
} |
|
return config; |
|
} |
|
|
|
void Workspace::saveState(QSessionManager &sm) |
|
{ |
|
// If the session manager is ksmserver, save stacking |
|
// order, active window, active desktop etc. in phase 1, |
|
// as ksmserver assures no interaction will be done |
|
// before the WM finishes phase 1. Saving in phase 2 is |
|
// too late, as possible user interaction may change some things. |
|
// Phase2 is still needed though (ICCCM 5.2) |
|
KConfig *config = sessionConfig(sm.sessionId(), sm.sessionKey()); |
|
if (!sm.isPhase2()) { |
|
KConfigGroup cg(config, "Session"); |
|
cg.writeEntry("AllowsInteraction", sm.allowsInteraction()); |
|
sessionSaveStarted(); |
|
if (gs_sessionManagerIsKSMServer) // save stacking order etc. before "save file?" etc. dialogs change it |
|
storeSession(config, SMSavePhase0); |
|
config->markAsClean(); // don't write Phase #1 data to disk |
|
sm.release(); // Qt doesn't automatically release in this case (bug?) |
|
sm.requestPhase2(); |
|
return; |
|
} |
|
storeSession(config, gs_sessionManagerIsKSMServer ? SMSavePhase2 : SMSavePhase2Full); |
|
config->sync(); |
|
|
|
// inform the smserver on how to clean-up after us |
|
const QString localFilePath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + config->name(); |
|
if (QFile::exists(localFilePath)) { // expectable for the sync |
|
sm.setDiscardCommand(QStringList() << QStringLiteral("rm") << localFilePath); |
|
} |
|
} |
|
|
|
// I bet this is broken, just like everywhere else in KDE |
|
void Workspace::commitData(QSessionManager &sm) |
|
{ |
|
if (!sm.isPhase2()) |
|
sessionSaveStarted(); |
|
} |
|
|
|
// Workspace |
|
|
|
/*! |
|
Stores the current session in the config file |
|
|
|
\sa loadSessionInfo() |
|
*/ |
|
void Workspace::storeSession(KConfig* config, SMSavePhase phase) |
|
{ |
|
KConfigGroup cg(config, "Session"); |
|
int count = 0; |
|
int active_client = -1; |
|
|
|
for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { |
|
Client* c = (*it); |
|
QByteArray sessionId = c->sessionId(); |
|
QByteArray wmCommand = c->wmCommand(); |
|
if (sessionId.isEmpty()) |
|
// remember also applications that are not XSMP capable |
|
// and use the obsolete WM_COMMAND / WM_SAVE_YOURSELF |
|
if (wmCommand.isEmpty()) |
|
continue; |
|
count++; |
|
if (c->isActive()) |
|
active_client = count; |
|
if (phase == SMSavePhase2 || phase == SMSavePhase2Full) |
|
storeClient(cg, count, c); |
|
} |
|
if (phase == SMSavePhase0) { |
|
// it would be much simpler to save these values to the config file, |
|
// but both Qt and KDE treat phase1 and phase2 separately, |
|
// which results in different sessionkey and different config file :( |
|
session_active_client = active_client; |
|
session_desktop = VirtualDesktopManager::self()->current(); |
|
} else if (phase == SMSavePhase2) { |
|
cg.writeEntry("count", count); |
|
cg.writeEntry("active", session_active_client); |
|
cg.writeEntry("desktop", session_desktop); |
|
} else { // SMSavePhase2Full |
|
cg.writeEntry("count", count); |
|
cg.writeEntry("active", session_active_client); |
|
cg.writeEntry("desktop", VirtualDesktopManager::self()->current()); |
|
} |
|
} |
|
|
|
void Workspace::storeClient(KConfigGroup &cg, int num, Client *c) |
|
{ |
|
c->setSessionInteract(false); //make sure we get the real values |
|
QString n = QString::number(num); |
|
cg.writeEntry(QLatin1String("sessionId") + n, c->sessionId().constData()); |
|
cg.writeEntry(QLatin1String("windowRole") + n, c->windowRole().constData()); |
|
cg.writeEntry(QLatin1String("wmCommand") + n, c->wmCommand().constData()); |
|
cg.writeEntry(QLatin1String("resourceName") + n, c->resourceName().constData()); |
|
cg.writeEntry(QLatin1String("resourceClass") + n, c->resourceClass().constData()); |
|
cg.writeEntry(QLatin1String("geometry") + n, QRect(c->calculateGravitation(true), c->clientSize())); // FRAME |
|
cg.writeEntry(QLatin1String("restore") + n, c->geometryRestore()); |
|
cg.writeEntry(QLatin1String("fsrestore") + n, c->geometryFSRestore()); |
|
cg.writeEntry(QLatin1String("maximize") + n, (int) c->maximizeMode()); |
|
cg.writeEntry(QLatin1String("fullscreen") + n, (int) c->fullScreenMode()); |
|
cg.writeEntry(QLatin1String("desktop") + n, c->desktop()); |
|
// the config entry is called "iconified" for back. comp. reasons |
|
// (kconf_update script for updating session files would be too complicated) |
|
cg.writeEntry(QLatin1String("iconified") + n, c->isMinimized()); |
|
cg.writeEntry(QLatin1String("opacity") + n, c->opacity()); |
|
// the config entry is called "sticky" for back. comp. reasons |
|
cg.writeEntry(QLatin1String("sticky") + n, c->isOnAllDesktops()); |
|
cg.writeEntry(QLatin1String("shaded") + n, c->isShade()); |
|
// the config entry is called "staysOnTop" for back. comp. reasons |
|
cg.writeEntry(QLatin1String("staysOnTop") + n, c->keepAbove()); |
|
cg.writeEntry(QLatin1String("keepBelow") + n, c->keepBelow()); |
|
cg.writeEntry(QLatin1String("skipTaskbar") + n, c->originalSkipTaskbar()); |
|
cg.writeEntry(QLatin1String("skipPager") + n, c->skipPager()); |
|
cg.writeEntry(QLatin1String("skipSwitcher") + n, c->skipSwitcher()); |
|
// not really just set by user, but name kept for back. comp. reasons |
|
cg.writeEntry(QLatin1String("userNoBorder") + n, c->noBorder()); |
|
cg.writeEntry(QLatin1String("windowType") + n, windowTypeToTxt(c->windowType())); |
|
cg.writeEntry(QLatin1String("shortcut") + n, c->shortcut().toString()); |
|
cg.writeEntry(QLatin1String("stackingOrder") + n, unconstrained_stacking_order.indexOf(c)); |
|
// KConfig doesn't support long so we need to live with less precision on 64-bit systems |
|
cg.writeEntry(QLatin1String("tabGroup") + n, static_cast<int>(reinterpret_cast<long>(c->tabGroup()))); |
|
cg.writeEntry(QLatin1String("activities") + n, c->activities()); |
|
} |
|
|
|
void Workspace::storeSubSession(const QString &name, QSet<QByteArray> sessionIds) |
|
{ |
|
//TODO clear it first |
|
KConfigGroup cg(KSharedConfig::openConfig(), QLatin1String("SubSession: ") + name); |
|
int count = 0; |
|
int active_client = -1; |
|
for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) { |
|
Client* c = (*it); |
|
QByteArray sessionId = c->sessionId(); |
|
QByteArray wmCommand = c->wmCommand(); |
|
if (sessionId.isEmpty()) |
|
// remember also applications that are not XSMP capable |
|
// and use the obsolete WM_COMMAND / WM_SAVE_YOURSELF |
|
if (wmCommand.isEmpty()) |
|
continue; |
|
if (!sessionIds.contains(sessionId)) |
|
continue; |
|
|
|
qCDebug(KWIN_CORE) << "storing" << sessionId; |
|
count++; |
|
if (c->isActive()) |
|
active_client = count; |
|
storeClient(cg, count, c); |
|
} |
|
cg.writeEntry("count", count); |
|
cg.writeEntry("active", active_client); |
|
//cg.writeEntry( "desktop", currentDesktop()); |
|
} |
|
|
|
/*! |
|
Loads the session information from the config file. |
|
|
|
\sa storeSession() |
|
*/ |
|
void Workspace::loadSessionInfo(const QString &key) |
|
{ |
|
// NOTICE: qApp->sessionKey() is outdated when this gets invoked |
|
// the key parameter is cached from the application constructor. |
|
session.clear(); |
|
KConfigGroup cg(sessionConfig(qApp->sessionId(), key), "Session"); |
|
addSessionInfo(cg); |
|
} |
|
|
|
void Workspace::addSessionInfo(KConfigGroup &cg) |
|
{ |
|
m_initialDesktop = cg.readEntry("desktop", 1); |
|
int count = cg.readEntry("count", 0); |
|
int active_client = cg.readEntry("active", 0); |
|
for (int i = 1; i <= count; i++) { |
|
QString n = QString::number(i); |
|
SessionInfo* info = new SessionInfo; |
|
session.append(info); |
|
info->sessionId = cg.readEntry(QLatin1String("sessionId") + n, QString()).toLatin1(); |
|
info->windowRole = cg.readEntry(QLatin1String("windowRole") + n, QString()).toLatin1(); |
|
info->wmCommand = cg.readEntry(QLatin1String("wmCommand") + n, QString()).toLatin1(); |
|
info->resourceName = cg.readEntry(QLatin1String("resourceName") + n, QString()).toLatin1(); |
|
info->resourceClass = cg.readEntry(QLatin1String("resourceClass") + n, QString()).toLower().toLatin1(); |
|
info->geometry = cg.readEntry(QLatin1String("geometry") + n, QRect()); |
|
info->restore = cg.readEntry(QLatin1String("restore") + n, QRect()); |
|
info->fsrestore = cg.readEntry(QLatin1String("fsrestore") + n, QRect()); |
|
info->maximized = cg.readEntry(QLatin1String("maximize") + n, 0); |
|
info->fullscreen = cg.readEntry(QLatin1String("fullscreen") + n, 0); |
|
info->desktop = cg.readEntry(QLatin1String("desktop") + n, 0); |
|
info->minimized = cg.readEntry(QLatin1String("iconified") + n, false); |
|
info->opacity = cg.readEntry(QLatin1String("opacity") + n, 1.0); |
|
info->onAllDesktops = cg.readEntry(QLatin1String("sticky") + n, false); |
|
info->shaded = cg.readEntry(QLatin1String("shaded") + n, false); |
|
info->keepAbove = cg.readEntry(QLatin1String("staysOnTop") + n, false); |
|
info->keepBelow = cg.readEntry(QLatin1String("keepBelow") + n, false); |
|
info->skipTaskbar = cg.readEntry(QLatin1String("skipTaskbar") + n, false); |
|
info->skipPager = cg.readEntry(QLatin1String("skipPager") + n, false); |
|
info->skipSwitcher = cg.readEntry(QLatin1String("skipSwitcher") + n, false); |
|
info->noBorder = cg.readEntry(QLatin1String("userNoBorder") + n, false); |
|
info->windowType = txtToWindowType(cg.readEntry(QLatin1String("windowType") + n, QString()).toLatin1().constData()); |
|
info->shortcut = cg.readEntry(QLatin1String("shortcut") + n, QString()); |
|
info->active = (active_client == i); |
|
info->stackingOrder = cg.readEntry(QLatin1String("stackingOrder") + n, -1); |
|
info->tabGroup = cg.readEntry(QLatin1String("tabGroup") + n, 0); |
|
info->tabGroupClient = NULL; |
|
info->activities = cg.readEntry(QLatin1String("activities") + n, QStringList()); |
|
} |
|
} |
|
|
|
void Workspace::loadSubSessionInfo(const QString &name) |
|
{ |
|
KConfigGroup cg(KSharedConfig::openConfig(), QLatin1String("SubSession: ") + name); |
|
addSessionInfo(cg); |
|
} |
|
|
|
/*! |
|
Returns a SessionInfo for client \a c. The returned session |
|
info is removed from the storage. It's up to the caller to delete it. |
|
|
|
This function is called when a new window is mapped and must be managed. |
|
We try to find a matching entry in the session. |
|
|
|
May return 0 if there's no session info for the client. |
|
*/ |
|
SessionInfo* Workspace::takeSessionInfo(Client* c) |
|
{ |
|
SessionInfo *realInfo = 0; |
|
QByteArray sessionId = c->sessionId(); |
|
QByteArray windowRole = c->windowRole(); |
|
QByteArray wmCommand = c->wmCommand(); |
|
QByteArray resourceName = c->resourceName(); |
|
QByteArray resourceClass = c->resourceClass(); |
|
|
|
// First search ``session'' |
|
if (! sessionId.isEmpty()) { |
|
// look for a real session managed client (algorithm suggested by ICCCM) |
|
foreach (SessionInfo * info, session) { |
|
if (realInfo) |
|
break; |
|
if (info->sessionId == sessionId && sessionInfoWindowTypeMatch(c, info)) { |
|
if (! windowRole.isEmpty()) { |
|
if (info->windowRole == windowRole) { |
|
realInfo = info; |
|
session.removeAll(info); |
|
} |
|
} else { |
|
if (info->windowRole.isEmpty() |
|
&& info->resourceName == resourceName |
|
&& info->resourceClass == resourceClass) { |
|
realInfo = info; |
|
session.removeAll(info); |
|
} |
|
} |
|
} |
|
} |
|
} else { |
|
// look for a sessioninfo with matching features. |
|
foreach (SessionInfo * info, session) { |
|
if (realInfo) |
|
break; |
|
if (info->resourceName == resourceName |
|
&& info->resourceClass == resourceClass |
|
&& sessionInfoWindowTypeMatch(c, info)) { |
|
if (wmCommand.isEmpty() || info->wmCommand == wmCommand) { |
|
realInfo = info; |
|
session.removeAll(info); |
|
} |
|
} |
|
} |
|
} |
|
|
|
// Set tabGroupClient for other clients in the same group |
|
if (realInfo && realInfo->tabGroup) { |
|
foreach (SessionInfo * info, session) { |
|
if (!info->tabGroupClient && info->tabGroup == realInfo->tabGroup) |
|
info->tabGroupClient = c; |
|
} |
|
} |
|
|
|
return realInfo; |
|
} |
|
|
|
bool Workspace::sessionInfoWindowTypeMatch(Client* c, SessionInfo* info) |
|
{ |
|
if (info->windowType == -2) { |
|
// undefined (not really part of NET::WindowType) |
|
return !c->isSpecialWindow(); |
|
} |
|
return info->windowType == c->windowType(); |
|
} |
|
|
|
static const char* const window_type_names[] = { |
|
"Unknown", "Normal" , "Desktop", "Dock", "Toolbar", "Menu", "Dialog", |
|
"Override", "TopMenu", "Utility", "Splash" |
|
}; |
|
// change also the two functions below when adding new entries |
|
|
|
const char* Workspace::windowTypeToTxt(NET::WindowType type) |
|
{ |
|
if (type >= NET::Unknown && type <= NET::Splash) |
|
return window_type_names[ type + 1 ]; // +1 (unknown==-1) |
|
if (type == -2) // undefined (not really part of NET::WindowType) |
|
return "Undefined"; |
|
qFatal("Unknown Window Type"); |
|
return NULL; |
|
} |
|
|
|
NET::WindowType Workspace::txtToWindowType(const char* txt) |
|
{ |
|
for (int i = NET::Unknown; |
|
i <= NET::Splash; |
|
++i) |
|
if (qstrcmp(txt, window_type_names[ i + 1 ]) == 0) // +1 |
|
return static_cast< NET::WindowType >(i); |
|
return static_cast< NET::WindowType >(-2); // undefined |
|
} |
|
|
|
|
|
|
|
|
|
// KWin's focus stealing prevention causes problems with user interaction |
|
// during session save, as it prevents possible dialogs from getting focus. |
|
// Therefore it's temporarily disabled during session saving. Start of |
|
// session saving can be detected in SessionManager::saveState() above, |
|
// but Qt doesn't have API for saying when session saved finished (either |
|
// successfully, or was canceled). Therefore, create another connection |
|
// to session manager, that will provide this information. |
|
// Similarly the remember feature of window-specific settings should be disabled |
|
// during KDE shutdown when windows may move e.g. because of Kicker going away |
|
// (struts changing). When session saving starts, it can be cancelled, in which |
|
// case the shutdown_cancelled callback is invoked, or it's a checkpoint that |
|
// is immediatelly followed by save_complete, or finally it's a shutdown that |
|
// is immediatelly followed by die callback. So getting save_yourself with shutdown |
|
// set disables window-specific settings remembering, getting shutdown_cancelled |
|
// re-enables, otherwise KWin will go away after die. |
|
static void save_yourself(SmcConn conn_P, SmPointer ptr, int, Bool shutdown, int, Bool) |
|
{ |
|
SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr); |
|
if (conn_P != session->connection()) |
|
return; |
|
if (shutdown) |
|
RuleBook::self()->setUpdatesDisabled(true); |
|
SmcSaveYourselfDone(conn_P, True); |
|
} |
|
|
|
static void die(SmcConn conn_P, SmPointer ptr) |
|
{ |
|
SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr); |
|
if (conn_P != session->connection()) |
|
return; |
|
// session->saveDone(); we will quit anyway |
|
session->close(); |
|
} |
|
|
|
static void save_complete(SmcConn conn_P, SmPointer ptr) |
|
{ |
|
SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr); |
|
if (conn_P != session->connection()) |
|
return; |
|
session->saveDone(); |
|
} |
|
|
|
static void shutdown_cancelled(SmcConn conn_P, SmPointer ptr) |
|
{ |
|
SessionSaveDoneHelper* session = reinterpret_cast< SessionSaveDoneHelper* >(ptr); |
|
if (conn_P != session->connection()) |
|
return; |
|
RuleBook::self()->setUpdatesDisabled(false); // re-enable |
|
// no need to differentiate between successful finish and cancel |
|
session->saveDone(); |
|
} |
|
|
|
void SessionSaveDoneHelper::saveDone() |
|
{ |
|
if (Workspace::self()) |
|
Workspace::self()->sessionSaveDone(); |
|
} |
|
|
|
SessionSaveDoneHelper::SessionSaveDoneHelper() |
|
{ |
|
SmcCallbacks calls; |
|
calls.save_yourself.callback = save_yourself; |
|
calls.save_yourself.client_data = reinterpret_cast< SmPointer >(this); |
|
calls.die.callback = die; |
|
calls.die.client_data = reinterpret_cast< SmPointer >(this); |
|
calls.save_complete.callback = save_complete; |
|
calls.save_complete.client_data = reinterpret_cast< SmPointer >(this); |
|
calls.shutdown_cancelled.callback = shutdown_cancelled; |
|
calls.shutdown_cancelled.client_data = reinterpret_cast< SmPointer >(this); |
|
char* id = NULL; |
|
char err[ 11 ]; |
|
conn = SmcOpenConnection(NULL, 0, 1, 0, |
|
SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask |
|
| SmcShutdownCancelledProcMask, &calls, NULL, &id, 10, err); |
|
if (id != NULL) |
|
free(id); |
|
if (conn == NULL) |
|
return; // no SM |
|
|
|
// detect ksmserver |
|
// NOTICE: no idea whether the assumptions about it in Workspace::saveState() |
|
// still hold for KF5/Plasma 5 |
|
#warning assuming special behavior in ksmserver to be still present in KF5 |
|
char* vendor = SmcVendor(conn); |
|
gs_sessionManagerIsKSMServer = qstrcmp(vendor, "KDE") == 0; |
|
free(vendor); |
|
|
|
// set the required properties, mostly dummy values |
|
SmPropValue propvalue[ 5 ]; |
|
SmProp props[ 5 ]; |
|
propvalue[ 0 ].length = sizeof(unsigned char); |
|
unsigned char value0 = SmRestartNever; // so that this extra SM connection doesn't interfere |
|
propvalue[ 0 ].value = &value0; |
|
props[ 0 ].name = const_cast< char* >(SmRestartStyleHint); |
|
props[ 0 ].type = const_cast< char* >(SmCARD8); |
|
props[ 0 ].num_vals = 1; |
|
props[ 0 ].vals = &propvalue[ 0 ]; |
|
struct passwd* entry = getpwuid(geteuid()); |
|
propvalue[ 1 ].length = entry != NULL ? strlen(entry->pw_name) : 0; |
|
propvalue[ 1 ].value = (SmPointer)(entry != NULL ? entry->pw_name : ""); |
|
props[ 1 ].name = const_cast< char* >(SmUserID); |
|
props[ 1 ].type = const_cast< char* >(SmARRAY8); |
|
props[ 1 ].num_vals = 1; |
|
props[ 1 ].vals = &propvalue[ 1 ]; |
|
propvalue[ 2 ].length = 0; |
|
propvalue[ 2 ].value = (SmPointer)(""); |
|
props[ 2 ].name = const_cast< char* >(SmRestartCommand); |
|
props[ 2 ].type = const_cast< char* >(SmLISTofARRAY8); |
|
props[ 2 ].num_vals = 1; |
|
props[ 2 ].vals = &propvalue[ 2 ]; |
|
propvalue[ 3 ].length = strlen("kwinsmhelper"); |
|
propvalue[ 3 ].value = (SmPointer)"kwinsmhelper"; |
|
props[ 3 ].name = const_cast< char* >(SmProgram); |
|
props[ 3 ].type = const_cast< char* >(SmARRAY8); |
|
props[ 3 ].num_vals = 1; |
|
props[ 3 ].vals = &propvalue[ 3 ]; |
|
propvalue[ 4 ].length = 0; |
|
propvalue[ 4 ].value = (SmPointer)(""); |
|
props[ 4 ].name = const_cast< char* >(SmCloneCommand); |
|
props[ 4 ].type = const_cast< char* >(SmLISTofARRAY8); |
|
props[ 4 ].num_vals = 1; |
|
props[ 4 ].vals = &propvalue[ 4 ]; |
|
SmProp* p[ 5 ] = { &props[ 0 ], &props[ 1 ], &props[ 2 ], &props[ 3 ], &props[ 4 ] }; |
|
SmcSetProperties(conn, 5, p); |
|
notifier = new QSocketNotifier(IceConnectionNumber(SmcGetIceConnection(conn)), |
|
QSocketNotifier::Read, this); |
|
connect(notifier, SIGNAL(activated(int)), SLOT(processData())); |
|
} |
|
|
|
SessionSaveDoneHelper::~SessionSaveDoneHelper() |
|
{ |
|
close(); |
|
} |
|
|
|
void SessionSaveDoneHelper::close() |
|
{ |
|
if (conn != NULL) { |
|
delete notifier; |
|
SmcCloseConnection(conn, 0, NULL); |
|
} |
|
conn = NULL; |
|
} |
|
|
|
void SessionSaveDoneHelper::processData() |
|
{ |
|
if (conn != NULL) |
|
IceProcessMessages(SmcGetIceConnection(conn), 0, 0); |
|
} |
|
|
|
void Workspace::sessionSaveDone() |
|
{ |
|
session_saving = false; |
|
//remove sessionInteract flag from all clients |
|
foreach (Client * c, clients) { |
|
c->setSessionInteract(false); |
|
} |
|
} |
|
|
|
} // namespace |
|
|
|
#include "sm.moc"
|
|
|