legacy session management support (WM_COMMAND) for emacs-lovers.

Many thanks to Leon Bottou <leonb@research.att.com> for this one.

svn path=/trunk/kdebase/kwin/; revision=87737
This commit is contained in:
Matthias Ettrich 2001-03-19 15:35:07 +00:00
parent 857e657e3f
commit 9b24d884bf
6 changed files with 453 additions and 119 deletions

View file

@ -30,6 +30,12 @@ Atoms::Atoms()
atoms[n] = &wm_change_state;
names[n++] = (char *) "WM_CHANGE_STATE";
atoms[n] = &wm_client_leader;
names[n++] = (char *) "WM_CLIENT_LEADER";
atoms[n] = &wm_save_yourself;
names[n++] = (char *) "WM_SAVE_YOURSELF";
atoms[n] = &motif_wm_hints;
names[n++] = (char *) "_MOTIF_WM_HINTS";

View file

@ -17,6 +17,8 @@ public:
Atom wm_delete_window;
Atom wm_take_focus;
Atom wm_change_state;
Atom wm_client_leader;
Atom wm_save_yourself;
Atom motif_wm_hints;
Atom net_wm_context_help;

View file

@ -518,6 +518,7 @@ Client::Client( Workspace *ws, WId w, QWidget *parent, const char *name, WFlags
getWMHints();
getWindowProtocols();
getWmNormalHints(); // get xSizeHint
getWmClientLeader();
fetchName();
if ( mainClient()->isSticky() )
@ -1150,6 +1151,8 @@ bool Client::propertyNotify( XPropertyEvent& e )
default:
if ( e.atom == atoms->wm_protocols )
getWindowProtocols();
else if (e.atom == atoms->wm_client_leader )
getWmClientLeader();
break;
}
return TRUE;
@ -2603,81 +2606,174 @@ void Client::keyPressEvent( QKeyEvent * e )
QCursor::setPos( pos );
}
static QCString getStringProperty(WId w, Atom prop, char separator=0)
{
Atom type;
int format;
unsigned long nitems = 0;
unsigned long extra = 0;
unsigned char *data = 0;
QCString result = "";
if ( XGetWindowProperty( qt_xdisplay(), w, prop, 0, 10000,
FALSE, XA_STRING, &type, &format,
&nitems, &extra, &data ) == Success ) {
if (data && separator) {
for (int i=0; i<(int)nitems; i++)
if (!data[i] && i+1<(int)nitems)
data[i] = separator;
}
if (data)
result = (const char*) data;
XFree(data);
}
return result;
}
/*!
Returns WINDOW_ROLE property for a given window.
*/
QCString Client::staticWindowRole(WId w)
{
return getStringProperty(w, qt_window_role);
}
/*!
Returns SM_CLIENT_ID property for a given window.
*/
QCString Client::staticSessionId(WId w)
{
return getStringProperty(w, qt_sm_client_id);
}
/*!
Returns WM_COMMAND property for a given window.
*/
QCString Client::staticWmCommand(WId w)
{
return getStringProperty(w, XA_WM_COMMAND, ' ');
}
/*!
Returns WM_CLIENT_MACHINE property for a given window.
Local machine is always returned as "localhost".
*/
QCString Client::staticWmClientMachine(WId w)
{
QCString result = getStringProperty(w, XA_WM_CLIENT_MACHINE);
if (result.isEmpty()) {
result = "localhost";
} else {
// special name for the local machine (localhost)
char hostnamebuf[80];
if (gethostname (hostnamebuf, sizeof hostnamebuf) >= 0) {
hostnamebuf[sizeof(hostnamebuf)-1] = 0;
if (result == hostnamebuf)
result = "localhost";
char *dot = strchr(hostnamebuf, '.');
if (dot && !(*dot = 0) && result == hostnamebuf)
result = "localhost";
}
}
return result;
}
/*!
Returns WM_CLIENT_LEADER property for a given window.
*/
Window Client::staticWmClientLeader(WId w)
{
Atom type;
int format;
unsigned long nitems = 0;
unsigned long extra = 0;
unsigned char *data = 0;
Window result = w;
if ( XGetWindowProperty( qt_xdisplay(), w, atoms->wm_client_leader, 0, 10000,
FALSE, XA_WINDOW, &type, &format,
&nitems, &extra, &data ) == Success ) {
if (data && nitems > 0)
result = *((Window*) data);
XFree(data);
}
return result;
}
void Client::getWmClientLeader()
{
wmClientLeaderWin = staticWmClientLeader(win);
}
/*!
Returns WINDOW_ROLE for this client
*/
QCString Client::windowRole()
{
Atom type;
int format;
unsigned long length, after;
unsigned char *data;
QCString result;
if ( XGetWindowProperty( qt_xdisplay(), win, qt_window_role, 0, 1024,
FALSE, XA_STRING, &type, &format,
&length, &after, &data ) == Success ) {
if ( data )
result = (const char*) data;
XFree( data );
}
return result;
return staticWindowRole(win);
}
/*!
Returns sessionId for this client,
taken either from its window or from the leader window.
*/
QCString Client::sessionId()
{
Atom type;
int format;
unsigned long length, after;
unsigned char *data;
QCString result;
if ( XGetWindowProperty( qt_xdisplay(), win, qt_sm_client_id, 0, 1024,
FALSE, XA_STRING, &type, &format,
&length, &after, &data ) == Success ) {
if ( data )
result = (const char*) data;
XFree( data );
}
QCString result = staticSessionId(win);
if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=win)
result = staticSessionId(wmClientLeaderWin);
return result;
}
static int getprop(Window w, Atom a, Atom type, long len, unsigned char **p)
{
Atom real_type;
int format;
unsigned long n, extra;
int status;
status = XGetWindowProperty(qt_xdisplay(), w, a, 0L, len, False, type, &real_type, &format, &n, &extra, p);
if (status != Success || *p == 0)
return -1;
if (n == 0)
XFree((void*) *p);
return n;
}
QCString Client::wmCommand()
{
QCString result;
char *p;
int i,n;
if ((n = getprop(win, XA_WM_COMMAND, XA_STRING, 100L, (unsigned char **)&p)) > 0){
result = p;
for ( i = 0; (i += strlen(p+i)+1) < n; result.append(p+i) )
result.append(" ");
XFree((char *) p);
}
return result;
}
/*!
Returns the classhint resource name for this client,
*/
QCString Client::resourceName()
{
return resource_name;
}
/*!
Returns the classhint resource class for this client,
*/
QCString Client::resourceClass()
{
return resource_class;
}
/*!
Returns command property for this client,
taken either from its window or from the leader window.
*/
QCString Client::wmCommand()
{
QCString result = staticWmCommand(win);
if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=win)
result = staticWmCommand(wmClientLeaderWin);
return result;
}
/*!
Returns client machine for this client,
taken either from its window or from the leader window.
*/
QCString Client::wmClientMachine()
{
QCString result = staticWmClientMachine(win);
if (result.isEmpty() && wmClientLeaderWin && wmClientLeaderWin!=win)
result = staticWmClientMachine(wmClientLeaderWin);
return result;
}
/*!
Returns client leader window for this client.
Returns the client window itself if no leader window is defined.
*/
Window Client::wmClientLeader()
{
if (wmClientLeaderWin)
return wmClientLeaderWin;
return win;
}
void Client::activateLayout()
{

View file

@ -181,9 +181,11 @@ public:
QCString windowRole();
QCString sessionId();
QCString wmCommand();
QCString resourceName();
QCString resourceClass();
QCString wmCommand();
QCString wmClientMachine();
Window wmClientLeader();
QRect adjustedClientArea( const QRect& area ) const;
@ -317,6 +319,16 @@ private:
void verifyTransientFor();
friend class WindowWrapper;
QString cap;
WId wmClientLeaderWin;
void getWmClientLeader();
public:
static QCString staticWindowRole(WId);
static QCString staticSessionId(WId);
static QCString staticWmCommand(WId);
static QCString staticWmClientMachine(WId);
static Window staticWmClientLeader(WId);
};
inline WId Client::window() const

View file

@ -3,7 +3,10 @@ kwin - the KDE window manager
Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
******************************************************************/
//#define QT_CLEAN_NAMESPACE
#define select kwin_hide_select
#include <kconfig.h>
#include <kglobal.h>
#include <kglobalsettings.h>
@ -42,6 +45,15 @@ const int XIconicState = IconicState;
#include <kwin.h>
#include <kdebug.h>
// Possible protoypes for select() were hidden as `kwin_hide_select.
// Undo the hiding definition and defines an acceptable prototype.
// This is how QT does this. It should work where QT works.
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#undef select
extern "C" int select(int,void*,void*,void*,struct timeval*);
namespace KWinInternal {
// NET WM Protocol handler class
@ -280,15 +292,8 @@ Workspace::Workspace( bool restore )
init();
if ( restore ) { // pseudo session management with wmCommand
for (SessionInfo* info = session.first(); info; info = session.next() ) {
if ( info->sessionId.isEmpty() && !info->wmCommand.isEmpty() ) {
KShellProcess proc;
proc << QString::fromLatin1( info->wmCommand );
proc.start(KShellProcess::DontCare);
}
}
}
if ( restore )
restoreLegacySession(kapp->sessionConfig());
}
void Workspace::init()
@ -2924,6 +2929,180 @@ void Workspace::slotResetAllClients()
requestFocus( active );
}
/*
* Legacy session management
*/
#ifndef NO_LEGACY_SESSION_MANAGEMENT
#define WM_SAVE_YOURSELF_TIMEOUT 2000
enum WinState { W_INVALID, W_VALID, W_SAVE_YOURSELF, W_SAVE_YOURSELF_OK };
typedef QMap<WId,WinState> WinMap;
static WinMap *winsPtr = 0;
static int winsErrorHandler(Display *, XErrorEvent *ev)
{
if (winsPtr) {
WinMap::Iterator it = winsPtr->find(ev->resourceid);
if (it != winsPtr->end())
it.data() = W_INVALID;
}
return 0;
}
/*!
Stores legacy session management data
*/
void Workspace::storeLegacySession( KConfig* config )
{
// Compute set of leader windows that
// might require legacy session management
WinMap wins;
for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) {
Client* c = (*it);
WId leader = c->wmClientLeader();
if (!wins.contains(leader) && c->sessionId().isEmpty())
wins.insert(leader, W_VALID);
}
// Open a new display for safely doing the WM_SAVE_YOURSELF protocol
XSync(qt_xdisplay(), False);
Display *newdisplay = XOpenDisplay(DisplayString(qt_xdisplay()));
if (!newdisplay) return;
WId root = DefaultRootWindow(newdisplay);
XGrabKeyboard(newdisplay, root, False,
GrabModeAsync, GrabModeAsync, CurrentTime);
XGrabPointer(newdisplay, root, False, Button1Mask|Button2Mask|Button3Mask,
GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
winsPtr = &wins;
XErrorHandler oldHandler = XSetErrorHandler(winsErrorHandler);
// Send WM_SAVE_YOURSELF messages to relevant windows
XEvent ev;
int awaiting_replies = 0;
for (WinMap::Iterator it = wins.begin(); it != wins.end(); ++it) {
if ( atoms->wm_protocols == None || atoms->wm_save_yourself == None)
break;
if ( it.data() == W_VALID ) {
WId w = it.key();
Atom *protocols = 0;
int nprotocols = 0;
XGetWMProtocols(newdisplay, w, &protocols, &nprotocols);
for (int i=0; i<nprotocols; i++)
if (protocols[i] == atoms->wm_save_yourself) {
it.data() = W_SAVE_YOURSELF;
break;
}
XFree((void*) protocols);
}
if ( it.data() == W_SAVE_YOURSELF ) {
WId w = it.key();
awaiting_replies += 1;
memset(&ev, 0, sizeof(ev));
ev.xclient.type = ClientMessage;
ev.xclient.window = w;
ev.xclient.message_type = atoms->wm_protocols;
ev.xclient.format = 32;
ev.xclient.data.l[0] = atoms->wm_save_yourself;
ev.xclient.data.l[1] = CurrentTime;
XSelectInput(newdisplay, w, PropertyChangeMask|StructureNotifyMask);
XSendEvent(newdisplay, w, False, 0, &ev);
}
}
// Wait for change in WM_COMMAND (with timeout)
XFlush(newdisplay);
QTime start = QTime::currentTime();
while (awaiting_replies > 0) {
if (XPending(newdisplay)) {
/* Process pending event */
XNextEvent(newdisplay, &ev);
WinMap::Iterator it = wins.find( ev.xany.window );
if (it == wins.end() || it.data() == W_SAVE_YOURSELF_OK)
continue;
if ( ev.type == UnmapNotify ||
( ev.xany.type == PropertyNotify
&& ev.xproperty.atom == XA_WM_COMMAND ) ) {
awaiting_replies -= 1;
it.data() = W_SAVE_YOURSELF_OK;
}
} else {
/* Check timeout */
int msecs = start.elapsed();
if (msecs >= WM_SAVE_YOURSELF_TIMEOUT)
break;
/* Wait for more events */
struct timeval tmwait;
int fd = ConnectionNumber(newdisplay);
fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
tmwait.tv_sec = (WM_SAVE_YOURSELF_TIMEOUT - msecs) / 1000;
tmwait.tv_usec = ((WM_SAVE_YOURSELF_TIMEOUT - msecs) % 1000) * 1000;
::select(fd+1, &fds, NULL, &fds, &tmwait);
}
}
// Terminate work in new display
XAllowEvents(newdisplay, ReplayPointer, CurrentTime);
XAllowEvents(newdisplay, ReplayKeyboard, CurrentTime);
XSync(newdisplay, False);
XCloseDisplay(newdisplay);
// Write LegacySession data
config->setGroup("LegacySession" );
int count = 0;
for (WinMap::Iterator it = wins.begin(); it != wins.end(); ++it) {
if (it.data() != W_INVALID) {
WId w = it.key();
QCString wmCommand = Client::staticWmCommand(w);
QCString wmClientMachine = Client::staticWmClientMachine(w);
if ( !wmCommand.isEmpty() && !wmClientMachine.isEmpty() ) {
count++;
QString n = QString::number(count);
config->writeEntry( QString("command")+n, wmCommand.data() );
config->writeEntry( QString("clientMachine")+n, wmClientMachine.data() );
}
}
}
config->writeEntry( "count", count );
// Restore old error handler
XSync(qt_xdisplay(), False);
XSetErrorHandler(oldHandler);
// Process a few events to update the client list.
// All events should be there because of the XSync above.
kapp->processEvents(10);
}
#endif
/*!
Restores legacy session management data (i.e. restart applications)
*/
void Workspace::restoreLegacySession( KConfig* config )
{
if (config) {
config->setGroup("LegacySession" );
int count = config->readNumEntry( "count" );
for ( int i = 1; i <= count; i++ ) {
QString n = QString::number(i);
QCString wmCommand = config->readEntry( QString("command")+n ).latin1();
QCString wmClientMachine = config->readEntry( QString("clientMachine")+n ).latin1();
if ( !wmCommand.isEmpty() && !wmClientMachine.isEmpty() ) {
KShellProcess proc;
if ( wmClientMachine != "localhost" )
proc << "xon" << wmClientMachine;
proc << QString::fromLatin1( wmCommand );
proc.start(KShellProcess::DontCare);
}
}
}
}
/*!
Stores the current session in the config file
@ -2931,29 +3110,40 @@ void Workspace::slotResetAllClients()
*/
void Workspace::storeSession( KConfig* config )
{
#ifndef NO_LEGACY_SESSION_MANAGEMENT
storeLegacySession(config);
#endif
config->setGroup("Session" );
int count = 0;
for (ClientList::Iterator it = clients.begin(); it != clients.end(); ++it) {
Client* c = (*it);
QCString sessionId = c->sessionId();
QCString windowRole = c->windowRole();
QCString wmCommand = c->wmCommand();
if ( !sessionId.isEmpty() || !wmCommand.isEmpty() ) {
count++;
QString n = QString::number(count);
config->writeEntry( QString("sessionId")+n, sessionId.data() );
config->writeEntry( QString("windowRole")+n, windowRole.data() );
config->writeEntry( QString("wmCommand")+n, wmCommand.data() );
config->writeEntry( QString("geometry")+n, QRect( c->pos(), c->windowWrapper()->size() ) );
config->writeEntry( QString("restore")+n, c->geometryRestore() );
config->writeEntry( QString("maximize")+n, (int) c->maximizeMode() );
config->writeEntry( QString("desktop")+n, c->desktop() );
config->writeEntry( QString("iconified")+n, c->isIconified() );
config->writeEntry( QString("sticky")+n, c->isSticky() );
config->writeEntry( QString("shaded")+n, c->isShade() );
config->writeEntry( QString("staysOnTop")+n, c->staysOnTop() );
config->writeEntry( QString("skipTaskbar")+n, c->skipTaskbar() );
}
if ( sessionId.isEmpty() )
#ifndef NO_LEGACY_SESSION_MANAGEMENT
// This is the only connection between the determination of legacy
// session managed applications (storeLegacySession) and the
// recollection of the window geometries (this function).
if ( wmCommand.isEmpty() )
#endif
continue;
count++;
QString n = QString::number(count);
config->writeEntry( QString("sessionId")+n, sessionId.data() );
config->writeEntry( QString("windowRole")+n, c->windowRole().data() );
config->writeEntry( QString("wmCommand")+n, wmCommand.data() );
config->writeEntry( QString("wmClientMachine")+n, c->wmClientMachine().data() );
config->writeEntry( QString("resourceName")+n, c->resourceName().data() );
config->writeEntry( QString("resourceClass")+n, c->resourceClass().data() );
config->writeEntry( QString("geometry")+n, QRect( c->pos(), c->windowWrapper()->size() ) );
config->writeEntry( QString("restore")+n, c->geometryRestore() );
config->writeEntry( QString("maximize")+n, (int) c->maximizeMode() );
config->writeEntry( QString("desktop")+n, c->desktop() );
config->writeEntry( QString("iconified")+n, c->isIconified() );
config->writeEntry( QString("sticky")+n, c->isSticky() );
config->writeEntry( QString("shaded")+n, c->isShade() );
config->writeEntry( QString("staysOnTop")+n, c->staysOnTop() );
config->writeEntry( QString("skipTaskbar")+n, c->skipTaskbar() );
}
config->writeEntry( "count", count );
}
@ -2977,6 +3167,9 @@ void Workspace::loadSessionInfo()
info->sessionId = config->readEntry( QString("sessionId")+n ).latin1();
info->windowRole = config->readEntry( QString("windowRole")+n ).latin1();
info->wmCommand = config->readEntry( QString("wmCommand")+n ).latin1();
info->wmClientMachine = config->readEntry( QString("wmClientMachine")+n ).latin1();
info->resourceName = config->readEntry( QString("resourceName")+n ).latin1();
info->resourceClass = config->readEntry( QString("resourceClass")+n ).latin1();
info->geometry = config->readRectEntry( QString("geometry")+n );
info->restore = config->readRectEntry( QString("restore")+n );
info->maximize = config->readNumEntry( QString("maximize")+n, 0 );
@ -3001,6 +3194,7 @@ void Workspace::loadFakeSessionInfo()
fakeSession.append( info );
info->resourceName = config->readEntry( QString("resourceName")+n ).latin1();
info->resourceClass = config->readEntry( QString("resourceClass")+n ).latin1();
info->wmClientMachine = config->readEntry( QString("clientMachine")+n ).latin1();
info->geometry = config->readRectEntry( QString("geometry")+n );
info->restore = config->readRectEntry( QString("restore")+n );
info->maximize = config->readNumEntry( QString("maximize")+n, 0 );
@ -3021,6 +3215,7 @@ void Workspace::storeFakeSessionInfo( Client* c )
fakeSession.append( info );
info->resourceName = c->resourceName();
info->resourceClass = c->resourceClass();
info->wmClientMachine = c->wmClientMachine();
info->geometry = QRect( c->pos(), c->windowWrapper()->size() ) ;
info->restore = c->geometryRestore();
info->maximize = (int)c->maximizeMode();
@ -3042,6 +3237,7 @@ void Workspace::writeFakeSessionInfo()
QString n = QString::number(count);
config->writeEntry( QString("resourceName")+n, info->resourceName.data() );
config->writeEntry( QString("resourceClass")+n, info->resourceClass.data() );
config->writeEntry( QString("clientMachine")+n, info->wmClientMachine.data() );
config->writeEntry( QString("geometry")+n, info->geometry );
config->writeEntry( QString("restore")+n, info->restore );
config->writeEntry( QString("maximize")+n, info->maximize );
@ -3056,47 +3252,68 @@ void Workspace::writeFakeSessionInfo()
}
/*!
Returns the SessionInfo for client \a c. The returned session
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. We also try to find
a matching entry in the fakeSession to see if the user had seclected the
``store settings'' menu entry.
May return 0 if there's no session info for the client.
*/
SessionInfo* Workspace::takeSessionInfo( Client* c )
{
if ( !session.isEmpty() ) {
QCString sessionId = c->sessionId();
QCString windowRole = c->windowRole();
QCString wmCommand = c->wmCommand();
for (SessionInfo* info = session.first(); info; info = session.next() ) {
// a real session managed client
if ( info->sessionId == sessionId &&
( ( info->windowRole.isEmpty() && windowRole.isEmpty() )
|| (info->windowRole == windowRole ) ) )
return session.take();
// pseudo session management
if ( info->sessionId.isEmpty() && !info->wmCommand.isEmpty() &&
info->wmCommand == wmCommand &&
( ( info->windowRole.isEmpty() && windowRole.isEmpty() )
|| (info->windowRole == windowRole ) ) )
return session.take();
}
}
// fakeSession, the "Store Settings" option in the window operation popup menu
if ( !fakeSession.isEmpty() ) {
QCString resourceName = c->resourceName();
QCString resourceClass = c->resourceClass();
for (SessionInfo* info = fakeSession.first(); info; info = fakeSession.next() ) {
if ( info->resourceName == resourceName && info->resourceClass == resourceClass ) {
c->setStoreSettings( TRUE );
return fakeSession.take();
SessionInfo *realInfo = 0;
SessionInfo *fakeInfo = 0;
QCString sessionId = c->sessionId();
QCString windowRole = c->windowRole();
QCString wmCommand = c->wmCommand();
QCString wmClientMachine = c->wmClientMachine();
QCString resourceName = c->resourceName();
QCString resourceClass = c->resourceClass();
// First search ``session''
if (! sessionId.isEmpty() ) {
// look for a real session managed client (algorithm suggested by ICCCM)
for (SessionInfo* info = session.first(); info && !realInfo; info = session.next() )
if ( info->sessionId == sessionId ) {
if ( ! windowRole.isEmpty() ) {
if ( info->windowRole == windowRole )
realInfo = session.take();
} else {
if ( info->windowRole.isEmpty() &&
info->resourceName == resourceName &&
info->resourceClass == resourceClass )
realInfo = session.take();
}
}
}
} else {
// look for a sessioninfo with matching features.
for (SessionInfo* info = session.first(); info && !realInfo; info = session.next() )
if ( info->resourceName == resourceName &&
info->resourceClass == resourceClass &&
info->wmClientMachine == wmClientMachine )
if ( wmCommand.isEmpty() || info->wmCommand == wmCommand )
realInfo = session.take();
}
// Now search ``fakeSession''
for (SessionInfo* info = fakeSession.first(); info && !fakeInfo; info = fakeSession.next() )
if ( info->resourceName == resourceName &&
info->resourceClass == resourceClass &&
info->wmClientMachine == wmClientMachine )
fakeInfo = fakeSession.take();
// Reconciliate
if (fakeInfo)
c->setStoreSettings( TRUE );
if (fakeInfo && realInfo)
delete fakeInfo;
if (realInfo)
return realInfo;
if (fakeInfo)
return fakeInfo;
return 0;
}

View file

@ -56,10 +56,9 @@ struct SessionInfo
{
QCString sessionId;
QCString windowRole;
QCString wmCommand; // compatibility
QCString resourceName; // for faked session info
QCString wmCommand;
QCString wmClientMachine;
QCString resourceName;
QCString resourceClass;
QRect geometry;
@ -180,6 +179,8 @@ public:
void performWindowOperation( Client* c, Options::WindowOperation op );
void restoreLegacySession( KConfig* config );
void storeLegacySession( KConfig* config );
void storeSession( KConfig* config );
SessionInfo* takeSessionInfo( Client* );