#include <X11/Xos.h>
#ifndef X_NOT_POSIX
#ifdef _POSIX_SOURCE
#include <limits.h>
#else
#define _POSIX_SOURCE
#include <limits.h>
#undef _POSIX_SOURCE
#endif
#endif
#ifndef PATH_MAX
#include <sys/param.h>
#ifndef PATH_MAX
#ifdef MAXPATHLEN
#define PATH_MAX MAXPATHLEN
#else
#define PATH_MAX 1024
#endif
#endif
#endif
#ifdef HAS_MKSTEMP
#include <unistd.h>
#endif
#include <X11/Xlib.h>
#include <X11/SM/SMlib.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include "twm.h"
#include "screen.h"
#include "session.h"
SmcConn smcConn = NULL;
XtInputId iceInputId;
char *twm_clientId;
TWMWinConfigEntry *winConfigHead = NULL;
Bool gotFirstSave = 0;
Bool sent_save_done = 0;
#define SAVEFILE_VERSION 2
#ifndef HAS_MKSTEMP
static char *unique_filename ( char *path, char *prefix );
#else
static char *unique_filename ( char *path, char *prefix, int *pFd );
#endif
char *
GetClientID (window)
Window window;
{
char *client_id = NULL;
Window client_leader;
XTextProperty tp;
Atom actual_type;
int actual_format;
unsigned long nitems;
unsigned long bytes_after;
unsigned char *prop = NULL;
if (XGetWindowProperty (dpy, window, _XA_WM_CLIENT_LEADER,
0L, 1L, False, AnyPropertyType, &actual_type, &actual_format,
&nitems, &bytes_after, &prop) == Success)
{
if (actual_type == XA_WINDOW && actual_format == 32 &&
nitems == 1 && bytes_after == 0)
{
client_leader = *((Window *) prop);
if (XGetTextProperty (dpy, client_leader, &tp, _XA_SM_CLIENT_ID))
{
if (tp.encoding == XA_STRING &&
tp.format == 8 && tp.nitems != 0)
client_id = (char *) tp.value;
}
}
if (prop)
XFree (prop);
}
return client_id;
}
char *
GetWindowRole (window)
Window window;
{
XTextProperty tp;
if (XGetTextProperty (dpy, window, &tp, _XA_WM_WINDOW_ROLE))
{
if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0)
return ((char *) tp.value);
}
return NULL;
}
int
write_byte (FILE *file, unsigned char b)
{
if (fwrite ((char *) &b, 1, 1, file) != 1)
return 0;
return 1;
}
int
write_ushort (FILE *file, unsigned short s)
{
unsigned char file_short[2];
file_short[0] = (s & (unsigned)0xff00) >> 8;
file_short[1] = s & 0xff;
if (fwrite ((char *) file_short, (int) sizeof (file_short), 1, file) != 1)
return 0;
return 1;
}
int
write_short (FILE *file, short s)
{
unsigned char file_short[2];
file_short[0] = (s & (unsigned)0xff00) >> 8;
file_short[1] = s & 0xff;
if (fwrite ((char *) file_short, (int) sizeof (file_short), 1, file) != 1)
return 0;
return 1;
}
int
write_counted_string (file, string)
FILE *file;
char *string;
{
if (string)
{
unsigned char count = strlen (string);
if (write_byte (file, count) == 0)
return 0;
if (fwrite (string, (int) sizeof (char), (int) count, file) != count)
return 0;
}
else
{
if (write_byte (file, 0) == 0)
return 0;
}
return 1;
}
int
read_byte (file, bp)
FILE *file;
unsigned char *bp;
{
if (fread ((char *) bp, 1, 1, file) != 1)
return 0;
return 1;
}
int
read_ushort (file, shortp)
FILE *file;
unsigned short *shortp;
{
unsigned char file_short[2];
if (fread ((char *) file_short, (int) sizeof (file_short), 1, file) != 1)
return 0;
*shortp = file_short[0] * 256 + file_short[1];
return 1;
}
int
read_short (file, shortp)
FILE *file;
short *shortp;
{
unsigned char file_short[2];
if (fread ((char *) file_short, (int) sizeof (file_short), 1, file) != 1)
return 0;
*shortp = file_short[0] * 256 + file_short[1];
return 1;
}
int
read_counted_string (file, stringp)
FILE *file;
char **stringp;
{
unsigned char len;
char *data;
if (read_byte (file, &len) == 0)
return 0;
if (len == 0) {
data = 0;
} else {
data = malloc ((unsigned) len + 1);
if (!data)
return 0;
if (fread (data, (int) sizeof (char), (int) len, file) != len) {
free (data);
return 0;
}
data[len] = '\0';
}
*stringp = data;
return 1;
}
int
WriteWinConfigEntry (configFile, theWindow, clientId, windowRole)
FILE *configFile;
TwmWindow *theWindow;
char *clientId;
char *windowRole;
{
char **wm_command;
int wm_command_count, i;
if (!write_counted_string (configFile, clientId))
return 0;
if (!write_counted_string (configFile, windowRole))
return 0;
if (!windowRole)
{
if (!write_counted_string (configFile, theWindow->class.res_name))
return 0;
if (!write_counted_string (configFile, theWindow->class.res_class))
return 0;
if (theWindow->nameChanged)
{
if (!write_counted_string (configFile, NULL))
return 0;
}
else
{
if (!write_counted_string (configFile, theWindow->name))
return 0;
}
wm_command = NULL;
wm_command_count = 0;
XGetCommand (dpy, theWindow->w, &wm_command, &wm_command_count);
if (clientId || !wm_command || wm_command_count == 0)
{
if (!write_byte (configFile, 0))
return 0;
}
else
{
if (!write_byte (configFile, (char) wm_command_count))
return 0;
for (i = 0; i < wm_command_count; i++)
if (!write_counted_string (configFile, wm_command[i]))
return 0;
XFreeStringList (wm_command);
}
}
if (!write_byte (configFile, theWindow->icon ? 1 : 0))
return 0;
if (!write_byte (configFile, theWindow->icon_w ? 1 : 0))
return 0;
if (theWindow->icon_w)
{
int icon_x, icon_y;
XGetGeometry (dpy, theWindow->icon_w, &JunkRoot, &icon_x,
&icon_y, &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth);
if (!write_short (configFile, (short) icon_x))
return 0;
if (!write_short (configFile, (short) icon_y))
return 0;
}
if (!write_short (configFile, (short) theWindow->frame_x))
return 0;
if (!write_short (configFile, (short) theWindow->frame_y))
return 0;
if (!write_ushort (configFile, (unsigned short) theWindow->attr.width))
return 0;
if (!write_ushort (configFile, (unsigned short) theWindow->attr.height))
return 0;
if (!write_byte (configFile, theWindow->widthEverChangedByUser ? 1 : 0))
return 0;
if (!write_byte (configFile, theWindow->heightEverChangedByUser ? 1 : 0))
return 0;
return 1;
}
int
ReadWinConfigEntry (FILE *configFile, unsigned short version,
TWMWinConfigEntry **pentry)
{
TWMWinConfigEntry *entry;
unsigned char byte;
int i;
*pentry = entry = (TWMWinConfigEntry *) malloc (
sizeof (TWMWinConfigEntry));
if (!*pentry)
return 0;
entry->tag = 0;
entry->client_id = NULL;
entry->window_role = NULL;
entry->class.res_name = NULL;
entry->class.res_class = NULL;
entry->wm_name = NULL;
entry->wm_command = NULL;
entry->wm_command_count = 0;
if (!read_counted_string (configFile, &entry->client_id))
goto give_up;
if (!read_counted_string (configFile, &entry->window_role))
goto give_up;
if (!entry->window_role)
{
if (!read_counted_string (configFile, &entry->class.res_name))
goto give_up;
if (!read_counted_string (configFile, &entry->class.res_class))
goto give_up;
if (!read_counted_string (configFile, &entry->wm_name))
goto give_up;
if (!read_byte (configFile, &byte))
goto give_up;
entry->wm_command_count = byte;
if (entry->wm_command_count == 0)
entry->wm_command = NULL;
else
{
entry->wm_command = (char **) malloc (entry->wm_command_count *
sizeof (char *));
if (!entry->wm_command)
goto give_up;
for (i = 0; i < entry->wm_command_count; i++)
if (!read_counted_string (configFile, &entry->wm_command[i]))
goto give_up;
}
}
if (!read_byte (configFile, &byte))
goto give_up;
entry->iconified = byte;
if (!read_byte (configFile, &byte))
goto give_up;
entry->icon_info_present = byte;
if (entry->icon_info_present)
{
if (!read_short (configFile, (short *) &entry->icon_x))
goto give_up;
if (!read_short (configFile, (short *) &entry->icon_y))
goto give_up;
}
if (!read_short (configFile, (short *) &entry->x))
goto give_up;
if (!read_short (configFile, (short *) &entry->y))
goto give_up;
if (!read_ushort (configFile, &entry->width))
goto give_up;
if (!read_ushort (configFile, &entry->height))
goto give_up;
if (version > 1)
{
if (!read_byte (configFile, &byte))
goto give_up;
entry->width_ever_changed_by_user = byte;
if (!read_byte (configFile, &byte))
goto give_up;
entry->height_ever_changed_by_user = byte;
}
else
{
entry->width_ever_changed_by_user = False;
entry->height_ever_changed_by_user = False;
}
return 1;
give_up:
if (entry->client_id)
free (entry->client_id);
if (entry->window_role)
free (entry->window_role);
if (entry->class.res_name)
free (entry->class.res_name);
if (entry->class.res_class)
free (entry->class.res_class);
if (entry->wm_name)
free (entry->wm_name);
if (entry->wm_command_count)
{
for (i = 0; i < entry->wm_command_count; i++)
if (entry->wm_command[i])
free (entry->wm_command[i]);
}
if (entry->wm_command)
free ((char *) entry->wm_command);
free ((char *) entry);
*pentry = NULL;
return 0;
}
void
ReadWinConfigFile (filename)
char *filename;
{
FILE *configFile;
TWMWinConfigEntry *entry;
int done = 0;
unsigned short version;
configFile = fopen (filename, "rb");
if (!configFile)
return;
if (!read_ushort (configFile, &version) ||
version > SAVEFILE_VERSION)
{
done = 1;
}
while (!done)
{
if (ReadWinConfigEntry (configFile, version, &entry))
{
entry->next = winConfigHead;
winConfigHead = entry;
}
else
done = 1;
}
fclose (configFile);
}
int
GetWindowConfig (theWindow, x, y, width, height,
iconified, icon_info_present, icon_x, icon_y,
width_ever_changed_by_user, height_ever_changed_by_user)
TwmWindow *theWindow;
short *x, *y;
unsigned short *width, *height;
Bool *iconified;
Bool *icon_info_present;
short *icon_x, *icon_y;
Bool *width_ever_changed_by_user;
Bool *height_ever_changed_by_user;
{
char *clientId, *windowRole;
TWMWinConfigEntry *ptr;
int found = 0;
ptr = winConfigHead;
if (!ptr)
return 0;
clientId = GetClientID (theWindow->w);
windowRole = GetWindowRole (theWindow->w);
while (ptr && !found)
{
int client_id_match = (!clientId && !ptr->client_id) ||
(clientId && ptr->client_id &&
strcmp (clientId, ptr->client_id) == 0);
if (!ptr->tag && client_id_match)
{
if (windowRole || ptr->window_role)
{
found = (windowRole && ptr->window_role &&
strcmp (windowRole, ptr->window_role) == 0);
}
else
{
if (strcmp (theWindow->class.res_name,
ptr->class.res_name) == 0 &&
strcmp (theWindow->class.res_class,
ptr->class.res_class) == 0 &&
(ptr->wm_name == NULL ||
strcmp (theWindow->name, ptr->wm_name) == 0))
{
if (clientId)
{
found = 1;
}
else
{
char **wm_command = NULL;
int wm_command_count = 0, i;
XGetCommand (dpy, theWindow->w,
&wm_command, &wm_command_count);
if (wm_command_count == ptr->wm_command_count)
{
for (i = 0; i < wm_command_count; i++)
if (strcmp (wm_command[i],
ptr->wm_command[i]) != 0)
break;
if (i == wm_command_count)
found = 1;
}
}
}
}
}
if (!found)
ptr = ptr->next;
}
if (found)
{
*x = ptr->x;
*y = ptr->y;
*width = ptr->width;
*height = ptr->height;
*iconified = ptr->iconified;
*icon_info_present = ptr->icon_info_present;
*width_ever_changed_by_user = ptr->width_ever_changed_by_user;
*height_ever_changed_by_user = ptr->height_ever_changed_by_user;
if (*icon_info_present)
{
*icon_x = ptr->icon_x;
*icon_y = ptr->icon_y;
}
ptr->tag = 1;
}
else
*iconified = 0;
if (clientId)
XFree (clientId);
if (windowRole)
XFree (windowRole);
return found;
}
#ifndef HAS_MKSTEMP
static char *
unique_filename (path, prefix)
char *path;
char *prefix;
#else
static char *
unique_filename (path, prefix, pFd)
char *path;
char *prefix;
int *pFd;
#endif
{
#ifndef HAS_MKSTEMP
#ifndef X_NOT_POSIX
return ((char *) tempnam (path, prefix));
#else
char tempFile[PATH_MAX];
char *tmp;
sprintf (tempFile, "%s/%sXXXXXX", path, prefix);
tmp = (char *) mktemp (tempFile);
if (tmp)
{
char *ptr = (char *) malloc (strlen (tmp) + 1);
strcpy (ptr, tmp);
return (ptr);
}
else
return (NULL);
#endif
#else
char tempFile[PATH_MAX];
char *ptr;
sprintf (tempFile, "%s/%sXXXXXX", path, prefix);
ptr = (char *)malloc(strlen(tempFile) + 1);
if (ptr != NULL)
{
strcpy(ptr, tempFile);
*pFd = mkstemp(ptr);
}
return ptr;
#endif
}
void
SaveYourselfPhase2CB (smcConn, clientData)
SmcConn smcConn;
SmPointer clientData;
{
int scrnum;
ScreenInfo *theScreen;
TwmWindow *theWindow;
char *clientId, *windowRole;
FILE *configFile = NULL;
char *path;
char *filename = NULL;
Bool success = False;
SmProp prop1, prop2, prop3, *props[3];
SmPropValue prop1val, prop2val, prop3val;
char discardCommand[80];
int numVals, i;
static int first_time = 1;
#ifdef HAS_MKSTEMP
int fd;
#endif
if (first_time)
{
char userId[20];
char hint = SmRestartIfRunning;
prop1.name = SmProgram;
prop1.type = SmARRAY8;
prop1.num_vals = 1;
prop1.vals = &prop1val;
prop1val.value = Argv[0];
prop1val.length = strlen (Argv[0]);
sprintf (userId, "%ld", (long)getuid());
prop2.name = SmUserID;
prop2.type = SmARRAY8;
prop2.num_vals = 1;
prop2.vals = &prop2val;
prop2val.value = (SmPointer) userId;
prop2val.length = strlen (userId);
prop3.name = SmRestartStyleHint;
prop3.type = SmCARD8;
prop3.num_vals = 1;
prop3.vals = &prop3val;
prop3val.value = (SmPointer) &hint;
prop3val.length = 1;
props[0] = &prop1;
props[1] = &prop2;
props[2] = &prop3;
SmcSetProperties (smcConn, 3, props);
first_time = 0;
}
path = getenv ("SM_SAVE_DIR");
if (!path)
{
path = getenv ("HOME");
if (!path)
path = ".";
}
#ifndef HAS_MKSTEMP
if ((filename = unique_filename (path, ".twm")) == NULL)
goto bad;
if (!(configFile = fopen (filename, "wb")))
goto bad;
#else
if ((filename = unique_filename (path, ".twm", &fd)) == NULL)
goto bad;
if (!(configFile = fdopen(fd, "wb")))
goto bad;
#endif
if (!write_ushort (configFile, SAVEFILE_VERSION))
goto bad;
success = True;
for (scrnum = 0; scrnum < NumScreens && success; scrnum++)
{
if (ScreenList[scrnum] != NULL)
{
theScreen = ScreenList[scrnum];
theWindow = theScreen->TwmRoot.next;
while (theWindow && success)
{
clientId = GetClientID (theWindow->w);
windowRole = GetWindowRole (theWindow->w);
if (!WriteWinConfigEntry (configFile, theWindow,
clientId, windowRole))
success = False;
if (clientId)
XFree (clientId);
if (windowRole)
XFree (windowRole);
theWindow = theWindow->next;
}
}
}
prop1.name = SmRestartCommand;
prop1.type = SmLISTofARRAY8;
prop1.vals = (SmPropValue *) malloc (
(Argc + 4) * sizeof (SmPropValue));
if (!prop1.vals)
{
success = False;
goto bad;
}
numVals = 0;
for (i = 0; i < Argc; i++)
{
if (strcmp (Argv[i], "-clientId") == 0 ||
strcmp (Argv[i], "-restore") == 0)
{
i++;
}
else
{
prop1.vals[numVals].value = (SmPointer) Argv[i];
prop1.vals[numVals++].length = strlen (Argv[i]);
}
}
prop1.vals[numVals].value = (SmPointer) "-clientId";
prop1.vals[numVals++].length = 9;
prop1.vals[numVals].value = (SmPointer) twm_clientId;
prop1.vals[numVals++].length = strlen (twm_clientId);
prop1.vals[numVals].value = (SmPointer) "-restore";
prop1.vals[numVals++].length = 8;
prop1.vals[numVals].value = (SmPointer) filename;
prop1.vals[numVals++].length = strlen (filename);
prop1.num_vals = numVals;
sprintf (discardCommand, "rm %s", filename);
prop2.name = SmDiscardCommand;
prop2.type = SmARRAY8;
prop2.num_vals = 1;
prop2.vals = &prop2val;
prop2val.value = (SmPointer) discardCommand;
prop2val.length = strlen (discardCommand);
props[0] = &prop1;
props[1] = &prop2;
SmcSetProperties (smcConn, 2, props);
free ((char *) prop1.vals);
bad:
SmcSaveYourselfDone (smcConn, success);
sent_save_done = 1;
if (configFile)
fclose (configFile);
if (filename)
free (filename);
}
void
SaveYourselfCB (smcConn, clientData, saveType, shutdown, interactStyle, fast)
SmcConn smcConn;
SmPointer clientData;
int saveType;
Bool shutdown;
int interactStyle;
Bool fast;
{
if (!SmcRequestSaveYourselfPhase2 (smcConn, SaveYourselfPhase2CB, NULL))
{
SmcSaveYourselfDone (smcConn, False);
sent_save_done = 1;
}
else
sent_save_done = 0;
}
void
DieCB (smcConn, clientData)
SmcConn smcConn;
SmPointer clientData;
{
SmcCloseConnection (smcConn, 0, NULL);
XtRemoveInput (iceInputId);
Done(NULL, NULL);
}
void
SaveCompleteCB (smcConn, clientData)
SmcConn smcConn;
SmPointer clientData;
{
;
}
void
ShutdownCancelledCB (smcConn, clientData)
SmcConn smcConn;
SmPointer clientData;
{
if (!sent_save_done)
{
SmcSaveYourselfDone (smcConn, False);
sent_save_done = 1;
}
}
void
ProcessIceMsgProc (client_data, source, id)
XtPointer client_data;
int *source;
XtInputId *id;
{
IceConn ice_conn = (IceConn) client_data;
IceProcessMessages (ice_conn, NULL, NULL);
}
void
ConnectToSessionManager (previous_id)
char *previous_id;
{
char errorMsg[256];
unsigned long mask;
SmcCallbacks callbacks;
IceConn iceConn;
mask = SmcSaveYourselfProcMask | SmcDieProcMask |
SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
callbacks.save_yourself.callback = SaveYourselfCB;
callbacks.save_yourself.client_data = (SmPointer) NULL;
callbacks.die.callback = DieCB;
callbacks.die.client_data = (SmPointer) NULL;
callbacks.save_complete.callback = SaveCompleteCB;
callbacks.save_complete.client_data = (SmPointer) NULL;
callbacks.shutdown_cancelled.callback = ShutdownCancelledCB;
callbacks.shutdown_cancelled.client_data = (SmPointer) NULL;
smcConn = SmcOpenConnection (
NULL,
(SmPointer) appContext,
SmProtoMajor,
SmProtoMinor,
mask,
&callbacks,
previous_id,
&twm_clientId,
256, errorMsg);
if (smcConn == NULL)
return;
iceConn = SmcGetIceConnection (smcConn);
iceInputId = XtAppAddInput (
appContext,
IceConnectionNumber (iceConn),
(XtPointer) XtInputReadMask,
ProcessIceMsgProc,
(XtPointer) iceConn);
}