From f3edd534bab6c89e09c0fa6f2a0fad35d5744058 Mon Sep 17 00:00:00 2001 From: Chani Armitage Date: Wed, 20 Oct 2010 12:41:51 +0000 Subject: [PATCH] Activity session support this implements dbus methods in kwin for saving & restoring activities. internally, kwin creates "sub-sessions" in kwin and ksmserver to represent the saved activities. ksmserver doesn't need to know what activities are :) at the moment the code includes some fake information; it will be changed to use the real stuff once the activities service code is in. svn path=/trunk/KDE/kdebase/workspace/; revision=1187793 --- org.kde.KWin.xml | 6 ++ sm.cpp | 210 ++++++++++++++++++++++++++++++++++++++--------- workspace.h | 8 ++ 3 files changed, 184 insertions(+), 40 deletions(-) diff --git a/org.kde.KWin.xml b/org.kde.KWin.xml index 9c66c29b71..47ed80f91f 100644 --- a/org.kde.KWin.xml +++ b/org.kde.KWin.xml @@ -73,5 +73,11 @@ + + + + + + diff --git a/sm.cpp b/sm.cpp index 944ac4b0cb..bbf25f82cf 100644 --- a/sm.cpp +++ b/sm.cpp @@ -30,6 +30,7 @@ along with this program. If not, see . #include "workspace.h" #include "client.h" +#include #include #include #include @@ -106,47 +107,8 @@ void Workspace::storeSession( KConfig* config, SMSavePhase phase ) count++; if( c->isActive()) active_client = count; - QString n = QString::number(count); if( phase == SMSavePhase2 || phase == SMSavePhase2Full ) - { - cg.writeEntry( QString("sessionId")+n, sessionId.constData() ); - cg.writeEntry( QString("windowRole")+n, c->windowRole().constData() ); - cg.writeEntry( QString("wmCommand")+n, wmCommand.constData() ); - cg.writeEntry( QString("wmClientMachine")+n, c->wmClientMachine( true ).constData() ); - cg.writeEntry( QString("resourceName")+n, c->resourceName().constData() ); - cg.writeEntry( QString("resourceClass")+n, c->resourceClass().constData() ); - cg.writeEntry( QString("geometry")+n, QRect( c->calculateGravitation(true), c->clientSize() ) ); // FRAME - cg.writeEntry( QString("restore")+n, c->geometryRestore() ); - cg.writeEntry( QString("fsrestore")+n, c->geometryFSRestore() ); - cg.writeEntry( QString("maximize")+n, (int) c->maximizeMode() ); - cg.writeEntry( QString("fullscreen")+n, (int) c->fullScreenMode() ); - cg.writeEntry( QString("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( QString("iconified")+n, c->isMinimized() ); - cg.writeEntry( QString("opacity")+n, c->opacity() ); - // the config entry is called "sticky" for back. comp. reasons - cg.writeEntry( QString("sticky")+n, c->isOnAllDesktops() ); - cg.writeEntry( QString("shaded")+n, c->isShade() ); - // the config entry is called "staysOnTop" for back. comp. reasons - cg.writeEntry( QString("staysOnTop")+n, c->keepAbove() ); - cg.writeEntry( QString("keepBelow")+n, c->keepBelow() ); - cg.writeEntry( QString("skipTaskbar")+n, c->skipTaskbar( true ) ); - cg.writeEntry( QString("skipPager")+n, c->skipPager() ); - cg.writeEntry( QString("skipSwitcher")+n, c->skipSwitcher() ); - // not really just set by user, but name kept for back. comp. reasons - cg.writeEntry( QString("userNoBorder")+n, c->noBorder() ); - cg.writeEntry( QString("windowType")+n, windowTypeToTxt( c->windowType())); - cg.writeEntry( QString("shortcut")+n, c->shortcut().toString()); - cg.writeEntry( QString("stackingOrder")+n, unconstrained_stacking_order.indexOf( c )); - int group = 0; - if( c->clientGroup() ) - group = c->clientGroup()->clients().count() > 1 ? - // KConfig doesn't support long so we need to live with less precision on 64-bit systems - static_cast( reinterpret_cast( c->clientGroup() )) : 0; - cg.writeEntry( QString("clientGroup")+n, group ); - cg.writeEntry( QString("activities")+n, c->activities() ); - } + storeClient(cg, count, c); } if( phase == SMSavePhase0 ) { @@ -170,6 +132,140 @@ void Workspace::storeSession( KConfig* config, SMSavePhase phase ) } } +void Workspace::storeClient( KConfigGroup &cg, int num, Client *c ) + { + QString n = QString::number(num); + cg.writeEntry( QString("sessionId")+n, c->sessionId().constData() ); + cg.writeEntry( QString("windowRole")+n, c->windowRole().constData() ); + cg.writeEntry( QString("wmCommand")+n, c->wmCommand().constData() ); + cg.writeEntry( QString("wmClientMachine")+n, c->wmClientMachine( true ).constData() ); + cg.writeEntry( QString("resourceName")+n, c->resourceName().constData() ); + cg.writeEntry( QString("resourceClass")+n, c->resourceClass().constData() ); + cg.writeEntry( QString("geometry")+n, QRect( c->calculateGravitation(true), c->clientSize() ) ); // FRAME + cg.writeEntry( QString("restore")+n, c->geometryRestore() ); + cg.writeEntry( QString("fsrestore")+n, c->geometryFSRestore() ); + cg.writeEntry( QString("maximize")+n, (int) c->maximizeMode() ); + cg.writeEntry( QString("fullscreen")+n, (int) c->fullScreenMode() ); + cg.writeEntry( QString("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( QString("iconified")+n, c->isMinimized() ); + cg.writeEntry( QString("opacity")+n, c->opacity() ); + // the config entry is called "sticky" for back. comp. reasons + cg.writeEntry( QString("sticky")+n, c->isOnAllDesktops() ); + cg.writeEntry( QString("shaded")+n, c->isShade() ); + // the config entry is called "staysOnTop" for back. comp. reasons + cg.writeEntry( QString("staysOnTop")+n, c->keepAbove() ); + cg.writeEntry( QString("keepBelow")+n, c->keepBelow() ); + cg.writeEntry( QString("skipTaskbar")+n, c->skipTaskbar( true ) ); + cg.writeEntry( QString("skipPager")+n, c->skipPager() ); + cg.writeEntry( QString("skipSwitcher")+n, c->skipSwitcher() ); + // not really just set by user, but name kept for back. comp. reasons + cg.writeEntry( QString("userNoBorder")+n, c->noBorder() ); + cg.writeEntry( QString("windowType")+n, windowTypeToTxt( c->windowType())); + cg.writeEntry( QString("shortcut")+n, c->shortcut().toString()); + cg.writeEntry( QString("stackingOrder")+n, unconstrained_stacking_order.indexOf( c )); + int group = 0; + if( c->clientGroup() ) + group = c->clientGroup()->clients().count() > 1 ? + // KConfig doesn't support long so we need to live with less precision on 64-bit systems + static_cast( reinterpret_cast( c->clientGroup() )) : 0; + cg.writeEntry( QString("clientGroup")+n, group ); + cg.writeEntry( QString("activities")+n, c->activities() ); + } + +void Workspace::storeSubSession(const QString &name, QSet sessionIds) + { + //TODO clear it first + KConfigGroup cg(KGlobal::config(), QString("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; + + kDebug() << "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()); + } + +void Workspace::storeActivity(const QString &id) + { + //TODO check if it's already closed + QSet saveSessionIds; + QSet dontCloseSessionIds; + kDebug() << id; + for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) + { + Client* c = (*it); + QByteArray sessionId = c->sessionId(); + if ( sessionId.isEmpty() ) + continue; //TODO support old wm_command apps too? + + kDebug() << sessionId; + + //if it's on the activity that's closing, it needs saving + //but if a process is on some other open activity, I don't wanna close it yet + //this is, of course, complicated by a process having many windows. + if (c->isOnAllActivities()) + { + dontCloseSessionIds << sessionId; + continue; + } + QStringList activities = c->activities(); + foreach (const QString &activityId, activities) + { + if (activityId == id) + saveSessionIds << sessionId; + else if (openActivities_.contains(activityId)) + dontCloseSessionIds << sessionId; + } + } + + storeSubSession(id, saveSessionIds); + + QStringList saveAndClose; + QStringList saveOnly; + foreach (const QByteArray &sessionId, saveSessionIds) + { + if (dontCloseSessionIds.contains(sessionId)) + saveOnly << sessionId; + else + saveAndClose << sessionId; + } + + kDebug() << "saveActivity" << id << saveAndClose << saveOnly; + + openActivities_.removeOne(id); //FIXME it's not closed until ksmserver says it's closed + + //pass off to ksmserver + QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface"); + if (ksmserver.isValid()) + { + QDBusMessage reply = ksmserver.call("saveSubSession", id, saveAndClose, saveOnly); + if (reply.type() == QDBusMessage::ErrorMessage) + kDebug() << "dbus error:" << reply.errorMessage(); + else + kDebug() << "dbus succeeded"; + } + else + kDebug() << "couldn't get ksmserver interface"; + } /*! Loads the session information from the config file. @@ -183,6 +279,11 @@ void Workspace::loadSessionInfo() setTilingEnabled( cg.readEntry( "tiling", false ) ); + addSessionInfo(cg); + } + +void Workspace::addSessionInfo(KConfigGroup &cg) + { int count = cg.readEntry( "count",0 ); int active_client = cg.readEntry( "active",0 ); for ( int i = 1; i <= count; i++ ) @@ -222,6 +323,35 @@ void Workspace::loadSessionInfo() } } +void Workspace::loadSubSessionInfo(const QString &name) + { + KConfigGroup cg(KGlobal::config(), QString("SubSession: ") + name); + addSessionInfo(cg); + } + +void Workspace::loadActivity(const QString &id) + { + if (!allActivities_.contains(id)) + return; //bogus id + if (openActivities_.contains(id)) + return; //already open + + openActivities_ << id; + loadSubSessionInfo(id); + + QDBusInterface ksmserver("org.kde.ksmserver", "/KSMServer", "org.kde.KSMServerInterface"); + if (ksmserver.isValid()) + { + QDBusMessage reply = ksmserver.call("restoreSubSession", id); + if (reply.type() == QDBusMessage::ErrorMessage) + kDebug() << "dbus error:" << reply.errorMessage(); + else + kDebug() << "dbus succeeded"; + } + else + kDebug() << "couldn't get ksmserver interface"; + } + /*! 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. diff --git a/workspace.h b/workspace.h index 931745cf22..136a323ad0 100644 --- a/workspace.h +++ b/workspace.h @@ -318,6 +318,7 @@ class Workspace : public QObject, public KDecorationDefines int currentDesktop_; QString activity_; QStringList allActivities_; + QStringList openActivities_; bool desktopLayoutDynamicity_; KActivityController activityController_; @@ -420,6 +421,8 @@ class Workspace : public QObject, public KDecorationDefines void performWindowOperation( Client* c, WindowOperation op ); void storeSession( KConfig* config, SMSavePhase phase ); + void storeClient( KConfigGroup &cg, int num, Client *c ); + void storeSubSession( const QString &name, QSet sessionIds ); SessionInfo* takeSessionInfo( Client* ); WindowRules findWindowRules( const Client*, bool ); @@ -446,6 +449,8 @@ class Workspace : public QObject, public KDecorationDefines void toggleTiling(); void nextTileLayout(); void previousTileLayout(); + void storeActivity( const QString &id ); + void loadActivity( const QString &id ); void setCurrentScreen( int new_screen ); @@ -875,6 +880,9 @@ class Workspace : public QObject, public KDecorationDefines Client* active_popup_client; void loadSessionInfo(); + void addSessionInfo( KConfigGroup &cg ); + void loadSubSessionInfo( const QString &name ); + void loadWindowRules(); void editWindowRules( Client* c, bool whole_app );