1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-29 09:52:29 +01:00
Files
wmaker/src/session.c
dan 33cc542e85 - Finished moving to the new proplist handling code in WINGs.
- Also tested the backward compatibility ability of the WINGs proplist code
  which seems to work quite well.

Starting with this moment, Window Maker no longer needs libPropList and is
now using the better and much more robust proplist code from WINGs. Also the
WINGs based proplist code is actively maintained while the old libPropList
code is practically dead and flawed by the fact that it borrowed concepts
from the UserDefaults which conflicted with the retain/release mechanism,
making some problems that libPropList had, practically unsolvable without a
complete redesign (which can be found in the more robust WINGs code).
2001-10-04 03:07:34 +00:00

1382 lines
33 KiB
C

/* session.c - session state handling and R6 style session management
*
* Copyright (c) 1998 Dan Pascu
* Copyright (c) 1998, 1999 Alfredo Kojima
*
* Window Maker window manager
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
/*
*
* If defined(XSMP_ENABLED) and session manager is running then
* do normal stuff
* else
* do pre-R6 session management stuff (save window state and relaunch)
*
* When doing a checkpoint:
*
* = Without XSMP
* Open "Stop"/status Dialog
* Send SAVE_YOURSELF to clients and wait for reply
* Save restart info
* Save state of clients
*
* = With XSMP
* Send checkpoint request to sm
*
* When exiting:
* -------------
*
* = Without XSMP
*
* Open "Exit Now"/status Dialog
* Send SAVE_YOURSELF to clients and wait for reply
* Save restart info
* Save state of clients
* Send DELETE to all clients
* When no more clients are left or user hit "Exit Now", exit
*
* = With XSMP
*
* Send Shutdown request to session manager
* if SaveYourself message received, save state of clients
* if the Die message is received, exit.
*/
#include "wconfig.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef XSMP_ENABLED
#include <X11/SM/SMlib.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include "WindowMaker.h"
#include "screen.h"
#include "window.h"
#include "client.h"
#include "session.h"
#include "wcore.h"
#include "framewin.h"
#include "workspace.h"
#include "funcs.h"
#include "properties.h"
#include "application.h"
#include "appicon.h"
#include "dock.h"
#include <WINGs/WUtil.h>
/** Global **/
extern Atom _XA_WM_SAVE_YOURSELF;
extern Time LastTimestamp;
#ifdef XSMP_ENABLED
extern int wScreenCount;
/* requested for SaveYourselfPhase2 */
static Bool sWaitingPhase2 = False;
static SmcConn sSMCConn = NULL;
static WMHandlerID sSMInputHandler = NULL;
/* our SM client ID */
static char *sClientID = NULL;
#endif
static WMPropList *sApplications = NULL;
static WMPropList *sCommand;
static WMPropList *sName;
static WMPropList *sHost;
static WMPropList *sWorkspace;
static WMPropList *sShaded;
static WMPropList *sMiniaturized;
static WMPropList *sHidden;
static WMPropList *sGeometry;
static WMPropList *sShortcutMask;
static WMPropList *sDock;
static WMPropList *sYes, *sNo;
static void
make_keys()
{
if (sApplications!=NULL)
return;
sApplications = WMCreatePLString("Applications");
sCommand = WMCreatePLString("Command");
sName = WMCreatePLString("Name");
sHost = WMCreatePLString("Host");
sWorkspace = WMCreatePLString("Workspace");
sShaded = WMCreatePLString("Shaded");
sMiniaturized = WMCreatePLString("Miniaturized");
sHidden = WMCreatePLString("Hidden");
sGeometry = WMCreatePLString("Geometry");
sDock = WMCreatePLString("Dock");
sShortcutMask = WMCreatePLString("ShortcutMask");
sYes = WMCreatePLString("Yes");
sNo = WMCreatePLString("No");
}
static int
getBool(WMPropList *value)
{
char *val;
if (!WMIsPLString(value)) {
return 0;
}
if (!(val = WMGetFromPLString(value))) {
return 0;
}
if ((val[1]=='\0' && (val[0]=='y' || val[0]=='Y'))
|| strcasecmp(val, "YES")==0) {
return 1;
} else if ((val[1]=='\0' && (val[0]=='n' || val[0]=='N'))
|| strcasecmp(val, "NO")==0) {
return 0;
} else {
int i;
if (sscanf(val, "%i", &i)==1) {
return (i!=0);
} else {
wwarning(_("can't convert \"%s\" to boolean"), val);
return 0;
}
}
}
static unsigned
getInt(WMPropList *value)
{
char *val;
unsigned n;
if (!WMIsPLString(value))
return 0;
val = WMGetFromPLString(value);
if (!val)
return 0;
if (sscanf(val, "%u", &n) != 1)
return 0;
return n;
}
static WMPropList*
makeWindowState(WWindow *wwin, WApplication *wapp)
{
WScreen *scr = wwin->screen_ptr;
Window win;
int argc;
char **argv;
int i;
unsigned mask;
char *class, *instance, *command=NULL, buffer[512];
WMPropList *win_state, *cmd, *name, *workspace;
WMPropList *shaded, *miniaturized, *hidden, *geometry;
WMPropList *dock, *shortcut;
if (wwin->main_window!=None && wwin->main_window!=wwin->client_win)
win = wwin->main_window;
else
win = wwin->client_win;
if (XGetCommand(dpy, win, &argv, &argc) && argc>0) {
command = wtokenjoin(argv, argc);
XFreeStringList(argv);
}
if (!command)
return NULL;
if (PropGetWMClass(win, &class, &instance)) {
if (class && instance)
snprintf(buffer, sizeof(buffer), "%s.%s", instance, class);
else if (instance)
snprintf(buffer, sizeof(buffer), "%s", instance);
else if (class)
snprintf(buffer, sizeof(buffer), ".%s", class);
else
snprintf(buffer, sizeof(buffer), ".");
name = WMCreatePLString(buffer);
cmd = WMCreatePLString(command);
/*sprintf(buffer, "%d", wwin->frame->workspace+1);
workspace = WMCreatePLString(buffer);*/
workspace = WMCreatePLString(scr->workspaces[wwin->frame->workspace]->name);
shaded = wwin->flags.shaded ? sYes : sNo;
miniaturized = wwin->flags.miniaturized ? sYes : sNo;
hidden = wwin->flags.hidden ? sYes : sNo;
snprintf(buffer, sizeof(buffer), "%ix%i+%i+%i",
wwin->client.width, wwin->client.height,
wwin->frame_x, wwin->frame_y);
geometry = WMCreatePLString(buffer);
for (mask = 0, i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
if (scr->shortcutWindows[i] != NULL &&
WMGetFirstInArray(scr->shortcutWindows[i], wwin) != WANotFound) {
mask |= 1<<i;
}
}
snprintf(buffer, sizeof(buffer), "%u", mask);
shortcut = WMCreatePLString(buffer);
win_state = WMCreatePLDictionary(sName, name,
sCommand, cmd,
sWorkspace, workspace,
sShaded, shaded,
sMiniaturized, miniaturized,
sHidden, hidden,
sShortcutMask, shortcut,
sGeometry, geometry,
NULL);
WMReleasePropList(name);
WMReleasePropList(cmd);
WMReleasePropList(workspace);
WMReleasePropList(geometry);
WMReleasePropList(shortcut);
if (wapp && wapp->app_icon && wapp->app_icon->dock) {
int i;
char *name;
if (wapp->app_icon->dock == scr->dock) {
name="Dock";
} else {
for(i=0; i<scr->workspace_count; i++)
if(scr->workspaces[i]->clip == wapp->app_icon->dock)
break;
assert( i < scr->workspace_count);
/*n = i+1;*/
name = scr->workspaces[i]->name;
}
dock = WMCreatePLString(name);
WMPutInPLDictionary(win_state, sDock, dock);
WMReleasePropList(dock);
}
} else {
win_state = NULL;
}
if (instance) XFree(instance);
if (class) XFree(class);
if (command) wfree(command);
return win_state;
}
void
wSessionSaveState(WScreen *scr)
{
WWindow *wwin = scr->focused_window;
WMPropList *win_info, *wks;
WMPropList *list=NULL;
WMArray *wapp_list=NULL;
make_keys();
if (!scr->session_state) {
scr->session_state = WMCreatePLDictionary(NULL, NULL, NULL);
if (!scr->session_state)
return;
}
list = WMCreatePLArray(NULL);
wapp_list = WMCreateArray(16);
while (wwin) {
WApplication *wapp=wApplicationOf(wwin->main_window);
if (wwin->transient_for==None
&& WMGetFirstInArray(wapp_list, wapp)==WANotFound
&& !WFLAGP(wwin, dont_save_session)) {
/* A entry for this application was not yet saved. Save one. */
if ((win_info = makeWindowState(wwin, wapp))!=NULL) {
WMAddToPLArray(list, win_info);
WMReleasePropList(win_info);
/* If we were succesful in saving the info for this window
* add the application the window belongs to, to the
* application list, so no multiple entries for the same
* application are saved.
*/
WMAddToArray(wapp_list, wapp);
}
}
wwin = wwin->prev;
}
WMRemoveFromPLDictionary(scr->session_state, sApplications);
WMPutInPLDictionary(scr->session_state, sApplications, list);
WMReleasePropList(list);
wks = WMCreatePLString(scr->workspaces[scr->current_workspace]->name);
WMPutInPLDictionary(scr->session_state, sWorkspace, wks);
WMReleasePropList(wks);
WMFreeArray(wapp_list);
}
void
wSessionClearState(WScreen *scr)
{
make_keys();
if (!scr->session_state)
return;
WMRemoveFromPLDictionary(scr->session_state, sApplications);
WMRemoveFromPLDictionary(scr->session_state, sWorkspace);
}
static pid_t
execCommand(WScreen *scr, char *command, char *host)
{
pid_t pid;
char **argv;
int argc;
wtokensplit(command, &argv, &argc);
if (argv==NULL) {
return 0;
}
if ((pid=fork())==0) {
char **args;
int i;
SetupEnvironment(scr);
args = malloc(sizeof(char*)*(argc+1));
if (!args)
exit(111);
for (i=0; i<argc; i++) {
args[i] = argv[i];
}
args[argc] = NULL;
execvp(argv[0], args);
exit(111);
}
while (argc > 0)
wfree(argv[--argc]);
wfree(argv);
return pid;
}
static WSavedState*
getWindowState(WScreen *scr, WMPropList *win_state)
{
WSavedState *state = wmalloc(sizeof(WSavedState));
WMPropList *value;
char *tmp;
unsigned mask;
int i;
memset(state, 0, sizeof(WSavedState));
state->workspace = -1;
value = WMGetFromPLDictionary(win_state, sWorkspace);
if (value && WMIsPLString(value)) {
tmp = WMGetFromPLString(value);
if (sscanf(tmp, "%i", &state->workspace)!=1) {
state->workspace = -1;
for (i=0; i < scr->workspace_count; i++) {
if (strcmp(scr->workspaces[i]->name, tmp)==0) {
state->workspace = i;
break;
}
}
} else {
state->workspace--;
}
}
if ((value = WMGetFromPLDictionary(win_state, sShaded))!=NULL)
state->shaded = getBool(value);
if ((value = WMGetFromPLDictionary(win_state, sMiniaturized))!=NULL)
state->miniaturized = getBool(value);
if ((value = WMGetFromPLDictionary(win_state, sHidden))!=NULL)
state->hidden = getBool(value);
if ((value = WMGetFromPLDictionary(win_state, sShortcutMask))!=NULL) {
mask = getInt(value);
state->window_shortcuts = mask;
}
value = WMGetFromPLDictionary(win_state, sGeometry);
if (value && WMIsPLString(value)) {
if (!(sscanf(WMGetFromPLString(value), "%ix%i+%i+%i",
&state->w, &state->h, &state->x, &state->y)==4 &&
(state->w>0 && state->h>0))) {
state->w = 0;
state->h = 0;
}
}
return state;
}
#define SAME(x, y) (((x) && (y) && !strcmp((x), (y))) || (!(x) && !(y)))
void
wSessionRestoreState(WScreen *scr)
{
WSavedState *state;
char *instance, *class, *command, *host;
WMPropList *win_info, *apps, *cmd, *value;
pid_t pid;
int i, count;
WDock *dock;
WAppIcon *btn=NULL;
int j, n, found;
char *tmp;
make_keys();
if (!scr->session_state)
return;
WMPLSetCaseSensitive(True);
apps = WMGetFromPLDictionary(scr->session_state, sApplications);
if (!apps)
return;
count = WMGetPropListItemCount(apps);
if (count==0)
return;
for (i=0; i<count; i++) {
win_info = WMGetFromPLArray(apps, i);
cmd = WMGetFromPLDictionary(win_info, sCommand);
if (!cmd || !WMIsPLString(cmd) || !(command = WMGetFromPLString(cmd))) {
continue;
}
value = WMGetFromPLDictionary(win_info, sName);
if (!value)
continue;
ParseWindowName(value, &instance, &class, "session");
if (!instance && !class)
continue;
value = WMGetFromPLDictionary(win_info, sHost);
if (value && WMIsPLString(value))
host = WMGetFromPLString(value);
else
host = NULL;
state = getWindowState(scr, win_info);
dock = NULL;
value = WMGetFromPLDictionary(win_info, sDock);
if (value && WMIsPLString(value) && (tmp = WMGetFromPLString(value))!=NULL) {
if (sscanf(tmp, "%i", &n)!=1) {
if (!strcasecmp(tmp, "DOCK")) {
dock = scr->dock;
} else {
for (j=0; j < scr->workspace_count; j++) {
if (strcmp(scr->workspaces[j]->name, tmp)==0) {
dock = scr->workspaces[j]->clip;
break;
}
}
}
} else {
if (n == 0) {
dock = scr->dock;
} else if (n>0 && n<=scr->workspace_count) {
dock = scr->workspaces[n-1]->clip;
}
}
}
found = 0;
if (dock!=NULL) {
for (j=0; j<dock->max_icons; j++) {
btn = dock->icon_array[j];
if (btn && SAME(instance, btn->wm_instance) &&
SAME(class, btn->wm_class) &&
SAME(command, btn->command) &&
!btn->launching) {
found = 1;
break;
}
}
}
if (found) {
wDockLaunchWithState(dock, btn, state);
} else if ((pid = execCommand(scr, command, host)) > 0) {
wWindowAddSavedState(instance, class, command, pid, state);
} else {
wfree(state);
}
if (instance) wfree(instance);
if (class) wfree(class);
}
/* clean up */
WMPLSetCaseSensitive(False);
}
void
wSessionRestoreLastWorkspace(WScreen *scr)
{
WMPropList *wks;
int w, i;
char *tmp;
make_keys();
if (!scr->session_state)
return;
WMPLSetCaseSensitive(True);
wks = WMGetFromPLDictionary(scr->session_state, sWorkspace);
if (!wks || !WMIsPLString(wks))
return;
tmp = WMGetFromPLString(wks);
/* clean up */
WMPLSetCaseSensitive(False);
if (sscanf(tmp, "%i", &w)!=1) {
w = -1;
for (i=0; i < scr->workspace_count; i++) {
if (strcmp(scr->workspaces[i]->name, tmp)==0) {
w = i;
break;
}
}
} else {
w--;
}
if (w!=scr->current_workspace && w<scr->workspace_count) {
wWorkspaceChange(scr, w);
}
}
static void
clearWaitingAckState(WScreen *scr)
{
WWindow *wwin;
WApplication *wapp;
for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
wwin->flags.waiting_save_ack = 0;
if (wwin->main_window != None) {
wapp = wApplicationOf(wwin->main_window);
if (wapp)
wapp->main_window_desc->flags.waiting_save_ack = 0;
}
}
}
void
wSessionSaveClients(WScreen *scr)
{
}
/*
* With XSMP, this job is done by smproxy
*/
void
wSessionSendSaveYourself(WScreen *scr)
{
WWindow *wwin;
int count;
/* freeze client interaction with clients */
XGrabKeyboard(dpy, scr->root_win, False, GrabModeAsync, GrabModeAsync,
CurrentTime);
XGrabPointer(dpy, scr->root_win, False, ButtonPressMask|ButtonReleaseMask,
GrabModeAsync, GrabModeAsync, scr->root_win, None,
CurrentTime);
clearWaitingAckState(scr);
count = 0;
/* first send SAVE_YOURSELF for everybody */
for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
WWindow *mainWin;
mainWin = wWindowFor(wwin->main_window);
if (mainWin) {
/* if the client is a multi-window client, only send message
* to the main window */
wwin = mainWin;
}
/* make sure the SAVE_YOURSELF flag is up-to-date */
PropGetProtocols(wwin->client_win, &wwin->protocols);
if (wwin->protocols.SAVE_YOURSELF) {
if (!wwin->flags.waiting_save_ack) {
wClientSendProtocol(wwin, _XA_WM_SAVE_YOURSELF, LastTimestamp);
wwin->flags.waiting_save_ack = 1;
count++;
}
} else {
wwin->flags.waiting_save_ack = 0;
}
}
/* then wait for acknowledge */
while (count > 0) {
}
XUngrabPointer(dpy, CurrentTime);
XUngrabKeyboard(dpy, CurrentTime);
XFlush(dpy);
}
#ifdef XSMP_ENABLED
/*
* With full session management support, the part of WMState
* that store client window state will become obsolete (maybe we can reuse
* the old code too),
* but we still need to store state info like the dock and workspaces.
* It is better to keep dock/wspace info in WMState because the user
* might want to keep the dock configuration while not wanting to
* resume a previously saved session.
* So, wmaker specific state info can be saved in
* ~/GNUstep/.AppInfo/WindowMaker/statename.state
* Its better to not put it in the defaults directory because:
* - its not a defaults file (having domain names like wmaker0089504baa
* in the defaults directory wouldn't be very neat)
* - this state file is not meant to be edited by users
*
* The old session code will become obsolete. When wmaker is
* compiled with R6 sm support compiled in, it'll be better to
* use a totally rewritten state saving code, but we can keep
* the current code for when XSMP_ENABLED is not compiled in.
*
* This will be confusing to old users (well get lots of "SAVE_SESSION broke!"
* messages), but it'll be better.
*
* -readme
*/
static char*
getWindowRole(Window window)
{
XTextProperty prop;
static Atom atom = 0;
if (!atom)
atom = XInternAtom(dpy, "WM_WINDOW_ROLE", False);
if (XGetTextProperty(dpy, window, &prop, atom)) {
if (prop.encoding == XA_STRING && prop.format == 8 && prop.nitems > 0)
return prop.value;
}
return NULL;
}
/*
*
* Saved Info:
*
* WM_WINDOW_ROLE
*
* WM_CLASS.instance
* WM_CLASS.class
* WM_NAME
* WM_COMMAND
*
* geometry
* state = (miniaturized, shaded, etc)
* attribute
* workspace #
* app state = (which dock, hidden)
* window shortcut #
*/
static WMPropList*
makeAppState(WWindow *wwin)
{
WApplication *wapp;
WMPropList *state;
WScreen *scr = wwin->screen_ptr;
state = WMCreatePLArray(NULL, NULL);
wapp = wApplicationOf(wwin->main_window);
if (wapp) {
if (wapp->app_icon && wapp->app_icon->dock) {
if (wapp->app_icon->dock == scr->dock) {
WMAddToPLArray(state, WMCreatePLString("Dock"));
} else {
int i;
for(i=0; i<scr->workspace_count; i++)
if(scr->workspaces[i]->clip == wapp->app_icon->dock)
break;
assert(i < scr->workspace_count);
WMAddToPLArray(state,
WMCreatePLString(scr->workspaces[i]->name));
}
}
WMAddToPLArray(state, WMCreatePLString(wapp->hidden ? "1" : "0"));
}
return state;
}
Bool
wSessionGetStateFor(WWindow *wwin, WSessionData *state)
{
char *str;
WMPropList *slist;
WMPropList *elem;
WMPropList *value;
int index = 0;
index = 3;
/* geometry */
value = WMGetFromPLArray(slist, index++);
str = WMGetFromPLString(value);
sscanf(str, "%i %i %i %i %i %i", &state->x, &state->y,
&state->width, &state->height,
&state->user_changed_width, &state->user_changed_height);
/* state */
value = WMGetFromPLArray(slist, index++);
str = WMGetFromPLString(value);
sscanf(str, "%i %i %i", &state->miniaturized, &state->shaded,
&state->maximized);
/* attributes */
value = WMGetFromPLArray(slist, index++);
str = WMGetFromPLString(value);
getAttributeState(str, &state->mflags, &state->flags);
/* workspace */
value = WMGetFromPLArray(slist, index++);
str = WMGetFromPLString(value);
sscanf(str, "%i", &state->workspace);
/* app state (repeated for all windows of the app) */
value = WMGetFromPLArray(slist, index++);
str = WMGetFromPLString(value);
/* ???? */
/* shortcuts */
value = WMGetFromPLArray(slist, index++);
str = WMGetFromPLString(value);
sscanf(str, "%i", &state->shortcuts);
}
static WMPropList*
makeAttributeState(WWindow *wwin)
{
unsigned int data1, data2;
char buffer[256];
#define W_FLAG(wwin, FLAG) ((wwin)->defined_user_flags.FLAG \
? (wwin)->user_flags.FLAG : -1)
snprintf(buffer, sizeof(buffer),
"%i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i",
W_FLAG(no_titlebar),
W_FLAG(no_resizable),
W_FLAG(no_closable),
W_FLAG(no_miniaturizable),
W_FLAG(no_resizebar),
W_FLAG(no_close_button),
W_FLAG(no_miniaturize_button),
/*
W_FLAG(broken_close),
W_FLAG(kill_close),
*/
W_FLAG(no_shadeable),
W_FLAG(omnipresent),
W_FLAG(skip_window_list),
W_FLAG(floating),
W_FLAG(sunken),
W_FLAG(no_bind_keys),
W_FLAG(no_bind_mouse),
W_FLAG(no_hide_others),
W_FLAG(no_appicon),
W_FLAG(dont_move_off),
W_FLAG(no_focusable),
W_FLAG(always_user_icon),
W_FLAG(start_miniaturized),
W_FLAG(start_hidden),
W_FLAG(start_maximized),
W_FLAG(dont_save_session),
W_FLAG(emulate_appicon));
return WMCreatePLString(buffer);
}
static void
appendStringInArray(WMPropList *array, char *str)
{
WMPropList *val;
val = WMCreatePLString(str);
WMAddToPLArray(array, val);
WMReleasePropList(val);
}
static WMPropList*
makeClientState(WWindow *wwin)
{
WMPropList *state;
WMPropList *tmp;
char *str;
char buffer[512];
int i;
unsigned shortcuts;
state = WMCreatePLArray(NULL, NULL);
/* WM_WINDOW_ROLE */
str = getWindowRole(wwin->client_win);
if (!str)
appendStringInArray(state, "");
else {
appendStringInArray(state, str);
XFree(str);
}
/* WM_CLASS.instance */
appendStringInArray(state, wwin->wm_instance);
/* WM_CLASS.class */
appendStringInArray(state, wwin->wm_class);
/* WM_NAME */
if (wwin->flags.wm_name_changed)
appendStringInArray(state, "");
else
appendStringInArray(state, wwin->frame->name);
/* geometry */
snprintf(buffer, sizeof(buffer), "%i %i %i %i %i %i", wwin->frame_x, wwin->frame_y,
wwin->client.width, wwin->client.height,
wwin->flags.user_changed_width, wwin->flags.user_changed_height);
appendStringInArray(state, buffer);
/* state */
snprintf(buffer, sizeof(buffer), "%i %i %i", wwin->flags.miniaturized,
wwin->flags.shaded, wwin->flags.maximized);
appendStringInArray(state, buffer);
/* attributes */
tmp = makeAttributeState(wwin);
WMAddToPLArray(state, tmp);
WMReleasePropList(tmp);
/* workspace */
snprintf(buffer, sizeof(buffer), "%i", wwin->frame->workspace);
appendStringInArray(state, buffer);
/* app state (repeated for all windows of the app) */
tmp = makeAppState(wwin);
WMAddToPLArray(state, tmp);
WMReleasePropList(tmp);
/* shortcuts */
shortcuts = 0;
for (i = 0; i < MAX_WINDOW_SHORTCUTS; i++) {
if (scr->shortcutWindow[i] == wwin) {
shortcuts |= 1 << i;
}
}
snprintf(buffer, sizeof(buffer), "%ui", shortcuts);
appendStringInArray(tmp, buffer);
return state;
}
static void
smSaveYourselfPhase2Proc(SmcConn smc_conn, SmPointer client_data)
{
SmProp props[4];
SmPropValue prop1val, prop2val, prop3val, prop4val;
char **argv = (char**)client_data;
int argc;
int i, j;
Bool ok = False;
char *statefile = NULL;
char *prefix;
Bool gsPrefix = False;
char *discardCmd = NULL;
time_t t;
WMPropList *state, *plState;
int len;
#ifdef DEBUG1
puts("received SaveYourselfPhase2 SM message");
#endif
/* save session state */
/* the file that will contain the state */
prefix = getenv("SM_SAVE_DIR");
if (!prefix) {
prefix = wusergnusteppath();
if (prefix)
gsPrefix = True;
}
if (!prefix) {
prefix = getenv("HOME");
}
if (!prefix)
prefix = ".";
len = strlen(prefix)+64;
statefile = malloc(len);
if (!statefile) {
wwarning(_("out of memory while saving session state"));
goto fail;
}
t = time();
i = 0;
do {
if (gsPrefix)
snprintf(statefile, len, "%s/.AppInfo/WindowMaker/wmaker.%l%i.state",
prefix, t, i);
else
snprintf(statefile, len, "%s/wmaker.%l%i.state", prefix, t, i);
i++;
} while (access(F_OK, statefile)!=-1);
/* save the states of all windows we're managing */
state = WMCreatePLArray(NULL, NULL);
/*
* Format:
*
* state_file ::= dictionary with version_info ; state
* version_info ::= 'version' = '1';
* state ::= 'state' = array of screen_info
* screen_info ::= array of (screen number, window_info, window_info, ...)
* window_info ::=
*/
for (i=0; i<wScreenCount; i++) {
WScreen *scr;
WWindow *wwin;
char buf[32];
WMPropList *pscreen;
scr = wScreenWithNumber(i);
snprintf(buf, sizeof(buf), "%i", scr->screen);
pscreen = WMCreatePLArray(WMCreatePLString(buf), NULL);
wwin = scr->focused_window;
while (wwin) {
WMPropList *pwindow;
pwindow = makeClientState(wwin);
WMAddToPLArray(pscreen, pwindow);
wwin = wwin->prev;
}
WMAddToPLArray(state, pscreen);
}
plState = WMCreatePLDictionary(WMCreatePLString("Version"),
WMCreatePLString("1.0"),
WMCreatePLString("Screens"),
state, NULL);
WMWritePropListToFile(plState, statefile, False);
WMReleasePropList(plState);
/* set the remaining properties that we didn't set at
* startup time */
for (argc=0, i=0; argv[i]!=NULL; i++) {
if (strcmp(argv[i], "-clientid")==0
|| strcmp(argv[i], "-restore")==0) {
i++;
} else {
argc++;
}
}
prop[0].name = SmRestartCommand;
prop[0].type = SmLISTofARRAY8;
prop[0].vals = malloc(sizeof(SmPropValue)*(argc+4));
prop[0].num_vals = argc+4;
prop[1].name = SmCloneCommand;
prop[1].type = SmLISTofARRAY8;
prop[1].vals = malloc(sizeof(SmPropValue)*(argc));
prop[1].num_vals = argc;
if (!prop[0].vals || !prop[1].vals) {
wwarning(_("end of memory while saving session state"));
goto fail;
}
for (j=0, i=0; i<argc+4; i++) {
if (strcmp(argv[i], "-clientid")==0
|| strcmp(argv[i], "-restore")==0) {
i++;
} else {
prop[0].vals[j].value = argv[i];
prop[0].vals[j].length = strlen(argv[i]);
prop[1].vals[j].value = argv[i];
prop[1].vals[j].length = strlen(argv[i]);
j++;
}
}
prop[0].vals[j].value = "-clientid";
prop[0].vals[j].length = 9;
j++;
prop[0].vals[j].value = sClientID;
prop[0].vals[j].length = strlen(sClientID);
j++;
prop[0].vals[j].value = "-restore";
prop[0].vals[j].length = 11;
j++;
prop[0].vals[j].value = statefile;
prop[0].vals[j].length = strlen(statefile);
{
int len = strlen(statefile)+8;
discardCmd = malloc(len);
if (!discardCmd)
goto fail;
snprintf(discardCmd, len, "rm %s", statefile);
}
prop[2].name = SmDiscardCommand;
prop[2].type = SmARRAY8;
prop[2].vals[0] = discardCmd;
prop[2].num_vals = 1;
SmcSetProperties(sSMCConn, 3, prop);
ok = True;
fail:
SmcSaveYourselfDone(smc_conn, ok);
if (prop[0].vals)
wfree(prop[0].vals);
if (prop[1].vals)
wfree(prop[1].vals);
if (discardCmd)
wfree(discardCmd);
if (!ok) {
remove(statefile);
}
if (statefile)
wfree(statefile);
}
static void
smSaveYourselfProc(SmcConn smc_conn, SmPointer client_data, int save_type,
Bool shutdown, int interact_style, Bool fast)
{
#ifdef DEBUG1
puts("received SaveYourself SM message");
#endif
if (!SmcRequestSaveYourselfPhase2(smc_conn, smSaveYourselfPhase2Proc,
client_data)) {
SmcSaveYourselfDone(smc_conn, False);
sWaitingPhase2 = False;
} else {
#ifdef DEBUG1
puts("successfull request of SYS phase 2");
#endif
sWaitingPhase2 = True;
}
}
static void
smDieProc(SmcConn smc_conn, SmPointer client_data)
{
#ifdef DEBUG1
puts("received Die SM message");
#endif
wSessionDisconnectManager();
Shutdown(WSExitMode, True);
}
static void
smSaveCompleteProc(SmcConn smc_conn)
{
/* it means that we can resume doing things that can change our state */
#ifdef DEBUG1
puts("received SaveComplete SM message");
#endif
}
static void
smShutdownCancelledProc(SmcConn smc_conn, SmPointer client_data)
{
if (sWaitingPhase2) {
sWaitingPhase2 = False;
SmcSaveYourselfDone(smc_conn, False);
}
}
static void
iceMessageProc(int fd, int mask, void *clientData)
{
IceConn iceConn = (IceConn)clientData;
IceProcessMessages(iceConn, NULL, NULL);
}
static void
iceIOErrorHandler(IceConnection ice_conn)
{
/* This is not fatal but can mean the session manager exited.
* If the session manager exited normally we would get a
* Die message, so this probably means an abnormal exit.
* If the sm was the last client of session, then we'll die
* anyway, otherwise we can continue doing our stuff.
*/
wwarning(_("connection to the session manager was lost"));
wSessionDisconnectManager();
}
void
wSessionConnectManager(char **argv, int argc)
{
IceConn iceConn;
char *previous_id = NULL;
char buffer[256];
SmcCallbacks callbacks;
unsigned long mask;
char uid[32];
char pid[32];
SmProp props[4];
SmPropValue prop1val, prop2val, prop3val, prop4val;
char restartStyle;
int i;
mask = SmcSaveYourselfProcMask|SmcDieProcMask|SmcSaveCompleteProcMask
|SmcShutdownCancelledProcMask;
callbacks.save_yourself.callback = smSaveYourselfProc;
callbacks.save_yourself.client_data = argv;
callbacks.die.callback = smDieProc;
callbacks.die.client_data = NULL;
callbacks.save_complete.callback = smSaveCompleteProc;
callbacks.save_complete.client_data = NULL;
callbacks.shutdown_cancelled.callback = smShutdownCancelledProc;
callbacks.shutdown_cancelled.client_data = NULL;
for (i=0; i<argc; i++) {
if (strcmp(argv[i], "-clientid")==0) {
previous_id = argv[i+1];
break;
}
}
/* connect to the session manager */
sSMCConn = SmcOpenConnection(NULL, NULL, SmProtoMajor, SmProtoMinor,
mask, &callbacks, previous_id,
&sClientID, 255, buffer);
if (!sSMCConn) {
return;
}
#ifdef DEBUG1
puts("connected to the session manager");
#endif
/* IceSetIOErrorHandler(iceIOErrorHandler);*/
/* check for session manager clients */
iceConn = SmcGetIceConnection(smcConn);
if (fcntl(IceConnectionNumber(iceConn), F_SETFD, FD_CLOEXEC) < 0) {
wsyserror("error setting close-on-exec flag for ICE connection");
}
sSMInputHandler = WMAddInputHandler(IceConnectionNumber(iceConn),
WIReadMask, iceMessageProc, iceConn);
/* setup information about ourselves */
/* program name */
prop1val.value = argv[0];
prop1val.length = strlen(argv[0]);
prop[0].name = SmProgram;
prop[0].type = SmARRAY8;
prop[0].num_vals = 1;
prop[0].vals = &prop1val;
/* The XSMP doc from X11R6.1 says it contains the user name,
* but every client implementation I saw places the uid # */
snprintf(uid, sizeof(uid), "%i", getuid());
prop2val.value = uid;
prop2val.length = strlen(uid);
prop[1].name = SmUserID;
prop[1].type = SmARRAY8;
prop[1].num_vals = 1;
prop[1].vals = &prop2val;
/* Restart style. We should restart only if we were running when
* the previous session finished. */
restartStyle = SmRestartIfRunning;
prop3val.value = &restartStyle;
prop3val.length = 1;
prop[2].name = SmRestartStyleHint;
prop[2].type = SmCARD8;
prop[2].num_vals = 1;
prop[2].vals = &prop3val;
/* Our PID. Not required but might be usefull */
snprintf(pid, sizeof(pid), "%i", getpid());
prop4val.value = pid;
prop4val.length = strlen(pid);
prop[3].name = SmProcessID;
prop[3].type = SmARRAY8;
prop[3].num_vals = 1;
prop[3].vals = &prop4val;
/* we'll set the rest of the hints later */
SmcSetProperties(sSMCConn, 4, props);
}
void
wSessionDisconnectManager(void)
{
if (sSMCConn) {
WMDeleteInputHandler(sSMInputHandler);
sSMInputHandler = NULL;
SmcCloseConnection(sSMCConn, 0, NULL);
sSMCConn = NULL;
}
}
void
wSessionRequestShutdown(void)
{
/* request a shutdown to the session manager */
if (sSMCConn)
SmcRequestSaveYourself(sSMCConn, SmSaveBoth, True, SmInteractStyleAny,
False, True);
}
Bool
wSessionIsManaged(void)
{
return sSMCConn!=NULL;
}
#endif /* !XSMP_ENABLED */