summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaleb Keithley <kaleb@freedesktop.org>2003-11-14 15:54:53 +0000
committerKaleb Keithley <kaleb@freedesktop.org>2003-11-14 15:54:53 +0000
commitf4d5033f594bf278ff1171091219b3c28dc45446 (patch)
treeac5a9d76006c7b9ef385ac41b5d5d2a6a92903a3
R6.6 is the Xorg base-lineXORG-MAINXORG-STABLE
-rw-r--r--save.c465
-rw-r--r--smproxy.c1314
-rw-r--r--smproxy.h101
-rw-r--r--smproxy.man72
4 files changed, 1952 insertions, 0 deletions
diff --git a/save.c b/save.c
new file mode 100644
index 0000000..5a0b8d1
--- /dev/null
+++ b/save.c
@@ -0,0 +1,465 @@
+/* $Xorg: save.c,v 1.4 2001/02/09 02:05:35 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1994, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+Author: Ralph Mor, X Consortium
+******************************************************************************/
+
+#include "smproxy.h"
+
+typedef struct ProxyFileEntry
+{
+ struct ProxyFileEntry *next;
+ int tag;
+ char *client_id;
+ XClassHint class;
+ char *wm_name;
+ int wm_command_count;
+ char **wm_command;
+} ProxyFileEntry;
+
+ProxyFileEntry *proxyFileHead = NULL;
+
+extern WinInfo *win_head;
+
+
+
+static int
+write_byte (file, b)
+
+FILE *file;
+unsigned char b;
+
+{
+ if (fwrite ((char *) &b, 1, 1, file) != 1)
+ return 0;
+ return 1;
+}
+
+
+static int
+write_short (file, s)
+
+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;
+}
+
+
+static 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;
+}
+
+
+
+static int
+read_byte (file, bp)
+
+FILE *file;
+unsigned char *bp;
+
+{
+ if (fread ((char *) bp, 1, 1, file) != 1)
+ return 0;
+ return 1;
+}
+
+
+static int
+read_short (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;
+}
+
+
+static 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 = (char *) 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;
+}
+
+
+
+/*
+ * An entry in the .smproxy file looks like this:
+ *
+ * FIELD BYTES
+ * ----- ----
+ * client ID len 1
+ * client ID LIST of bytes
+ * WM_CLASS "res name" length 1
+ * WM_CLASS "res name" LIST of bytes
+ * WM_CLASS "res class" length 1
+ * WM_CLASS "res class" LIST of bytes
+ * WM_NAME length 1
+ * WM_NAME LIST of bytes
+ * WM_COMMAND arg count 1
+ * For each arg in WM_COMMAND
+ * arg length 1
+ * arg LIST of bytes
+ */
+
+int
+WriteProxyFileEntry (proxyFile, theWindow)
+
+FILE *proxyFile;
+WinInfo *theWindow;
+
+{
+ int i;
+
+ if (!write_counted_string (proxyFile, theWindow->client_id))
+ return 0;
+ if (!write_counted_string (proxyFile, theWindow->class.res_name))
+ return 0;
+ if (!write_counted_string (proxyFile, theWindow->class.res_class))
+ return 0;
+ if (!write_counted_string (proxyFile, theWindow->wm_name))
+ return 0;
+
+ if (!theWindow->wm_command || theWindow->wm_command_count == 0)
+ {
+ if (!write_byte (proxyFile, 0))
+ return 0;
+ }
+ else
+ {
+ if (!write_byte (proxyFile, (char) theWindow->wm_command_count))
+ return 0;
+ for (i = 0; i < theWindow->wm_command_count; i++)
+ if (!write_counted_string (proxyFile, theWindow->wm_command[i]))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int
+ReadProxyFileEntry (proxyFile, pentry)
+
+FILE *proxyFile;
+ProxyFileEntry **pentry;
+
+{
+ ProxyFileEntry *entry;
+ char byte;
+ int i;
+
+ *pentry = entry = (ProxyFileEntry *) malloc (
+ sizeof (ProxyFileEntry));
+ if (!*pentry)
+ return 0;
+
+ entry->tag = 0;
+ entry->client_id = 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 (proxyFile, &entry->client_id))
+ goto give_up;
+ if (!read_counted_string (proxyFile, &entry->class.res_name))
+ goto give_up;
+ if (!read_counted_string (proxyFile, &entry->class.res_class))
+ goto give_up;
+ if (!read_counted_string (proxyFile, &entry->wm_name))
+ goto give_up;
+
+ if (!read_byte (proxyFile, &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 (proxyFile, &entry->wm_command[i]))
+ goto give_up;
+ }
+
+ return 1;
+
+give_up:
+
+ if (entry->client_id)
+ free (entry->client_id);
+ 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
+ReadProxyFile (filename)
+
+char *filename;
+
+{
+ FILE *proxyFile;
+ ProxyFileEntry *entry;
+ int done = 0;
+ unsigned short version;
+
+ proxyFile = fopen (filename, "rb");
+ if (!proxyFile)
+ return;
+
+ if (!read_short (proxyFile, &version) ||
+ version > SAVEFILE_VERSION)
+ {
+ done = 1;
+ }
+
+ while (!done)
+ {
+ if (ReadProxyFileEntry (proxyFile, &entry))
+ {
+ entry->next = proxyFileHead;
+ proxyFileHead = entry;
+ }
+ else
+ done = 1;
+ }
+
+ fclose (proxyFile);
+}
+
+
+
+static char *
+unique_filename (path, prefix)
+
+char *path;
+char *prefix;
+
+{
+#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
+}
+
+
+
+char *
+WriteProxyFile ()
+
+{
+ FILE *proxyFile = NULL;
+ char *filename = NULL;
+ char *path;
+ WinInfo *winptr;
+ Bool success = False;
+
+ path = getenv ("SM_SAVE_DIR");
+ if (!path)
+ {
+ path = getenv ("HOME");
+ if (!path)
+ path = ".";
+ }
+
+ if ((filename = unique_filename (path, ".prx")) == NULL)
+ goto bad;
+
+ if (!(proxyFile = fopen (filename, "wb")))
+ goto bad;
+
+ if (!write_short (proxyFile, SAVEFILE_VERSION))
+ goto bad;
+
+ success = True;
+ winptr = win_head;
+
+ while (winptr && success)
+ {
+ if (winptr->client_id)
+ if (!WriteProxyFileEntry (proxyFile, winptr))
+ {
+ success = False;
+ break;
+ }
+
+ winptr = winptr->next;
+ }
+
+ bad:
+
+ if (proxyFile)
+ fclose (proxyFile);
+
+ if (success)
+ return (filename);
+ else
+ {
+ if (filename)
+ free (filename);
+ return (NULL);
+ }
+}
+
+
+
+char *
+LookupClientID (theWindow)
+
+WinInfo *theWindow;
+
+{
+ ProxyFileEntry *ptr;
+ int found = 0;
+
+ ptr = proxyFileHead;
+ while (ptr && !found)
+ {
+ if (!ptr->tag &&
+ strcmp (theWindow->class.res_name, ptr->class.res_name) == 0 &&
+ strcmp (theWindow->class.res_class, ptr->class.res_class) == 0 &&
+ strcmp (theWindow->wm_name, ptr->wm_name) == 0)
+ {
+ int i;
+
+ if (theWindow->wm_command_count == ptr->wm_command_count)
+ {
+ for (i = 0; i < theWindow->wm_command_count; i++)
+ if (strcmp (theWindow->wm_command[i],
+ ptr->wm_command[i]) != 0)
+ break;
+
+ if (i == theWindow->wm_command_count)
+ found = 1;
+ }
+ }
+
+ if (!found)
+ ptr = ptr->next;
+ }
+
+ if (found)
+ {
+ ptr->tag = 1;
+ return (ptr->client_id);
+ }
+ else
+ return NULL;
+}
diff --git a/smproxy.c b/smproxy.c
new file mode 100644
index 0000000..41ed45a
--- /dev/null
+++ b/smproxy.c
@@ -0,0 +1,1314 @@
+/* $Xorg: smproxy.c,v 1.6 2001/02/09 02:05:35 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1994, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+Author: Ralph Mor, X Consortium
+******************************************************************************/
+
+#include "smproxy.h"
+
+XtAppContext appContext;
+Display *disp;
+
+Atom wmProtocolsAtom;
+Atom wmSaveYourselfAtom;
+Atom wmStateAtom;
+Atom smClientIdAtom;
+Atom wmClientLeaderAtom;
+
+Bool debug = 0;
+
+SmcConn proxy_smcConn;
+XtInputId proxy_iceInputId;
+char *proxy_clientId = NULL;
+
+WinInfo *win_head = NULL;
+
+int proxy_count = 0;
+int die_count = 0;
+
+Bool ok_to_die = 0;
+
+Bool caught_error = 0;
+
+Bool sent_save_done = 0;
+
+int Argc;
+char **Argv;
+
+
+
+Bool
+HasSaveYourself (window)
+
+Window window;
+
+{
+ Atom *protocols;
+ int numProtocols;
+ int i, found;
+
+ protocols = NULL;
+
+ if (XGetWMProtocols (disp, window, &protocols, &numProtocols) != True)
+ return (False);
+
+ found = 0;
+
+ if (protocols != NULL)
+ {
+ for (i = 0; i < numProtocols; i++)
+ if (protocols[i] == wmSaveYourselfAtom)
+ found = 1;
+
+ XFree (protocols);
+ }
+
+ return (found);
+}
+
+
+
+Bool
+HasXSMPsupport (window)
+
+Window window;
+
+{
+ XTextProperty tp;
+ Bool hasIt = 0;
+
+ if (XGetTextProperty (disp, window, &tp, smClientIdAtom))
+ {
+ if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0)
+ hasIt = 1;
+
+ if (tp.value)
+ XFree ((char *) tp.value);
+ }
+
+ return (hasIt);
+}
+
+
+
+WinInfo *
+GetClientLeader (winptr)
+
+WinInfo *winptr;
+
+{
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytesafter;
+ unsigned long *datap = NULL;
+ WinInfo *leader_winptr = NULL;
+ Bool failure = 0;
+
+ if (XGetWindowProperty (disp, winptr->window, wmClientLeaderAtom,
+ 0L, 1L, False, AnyPropertyType, &actual_type, &actual_format,
+ &nitems, &bytesafter, (unsigned char **) &datap) == Success)
+ {
+ if (actual_type == XA_WINDOW && actual_format == 32 &&
+ nitems == 1 && bytesafter == 0)
+ {
+ Window leader_win = *((Window *) datap);
+
+ if (!LookupWindow (leader_win, &leader_winptr, NULL))
+ failure = 1;
+ }
+
+ if (datap)
+ XFree (datap);
+ }
+
+ if (failure)
+ {
+ /* The client leader was defined, but we couldn't find the window */
+
+ return (NULL);
+ }
+ else if (leader_winptr)
+ {
+ /* We found the real client leader */
+
+ return (leader_winptr);
+ }
+ else
+ {
+ /* There is no client leader defined, return this window */
+
+ return (winptr);
+ }
+}
+
+
+
+char *
+CheckFullyQuantifiedName (name, newstring)
+
+char *name;
+int *newstring;
+
+{
+ /*
+ * Due to a bug in Xlib (for hpux in particular), some clients
+ * will have a WM_CLIENT_MACHINE that is not fully quantified.
+ * For example, we might get "excon" instead of "excon.x.org".
+ * This really stinks. The best we can do is tag on our own
+ * domain name.
+ */
+
+ if (strchr (name, '.') != NULL)
+ {
+ *newstring = 0;
+ return (name);
+ }
+ else
+ {
+ char hostnamebuf[80];
+ char *firstDot;
+
+ gethostname (hostnamebuf, sizeof hostnamebuf);
+ firstDot = strchr (hostnamebuf, '.');
+
+ if (!firstDot)
+ {
+ *newstring = 0;
+ return (name);
+ }
+ else
+ {
+ int bytes = strlen (name) + strlen (firstDot + 1) + 2;
+ char *newptr;
+
+ newptr = (char *) malloc (bytes);
+ sprintf (newptr, "%s.%s", name, firstDot + 1);
+
+ *newstring = 1;
+ return (newptr);
+ }
+ }
+}
+
+
+
+void FinishSaveYourself (winInfo, has_WM_SAVEYOURSELF)
+
+WinInfo *winInfo;
+Bool has_WM_SAVEYOURSELF;
+
+{
+ SmProp prop1, prop2, prop3, *props[3];
+ SmPropValue prop1val, prop2val, prop3val;
+ int i;
+
+ if (!winInfo->got_first_save_yourself)
+ {
+ char userId[20], restartService[80];
+ char *fullyQuantifiedName;
+ int newstring;
+
+ prop1.name = SmProgram;
+ prop1.type = SmARRAY8;
+ prop1.num_vals = 1;
+ prop1.vals = &prop1val;
+ prop1val.value = (SmPointer) winInfo->wm_command[0];
+ prop1val.length = strlen (winInfo->wm_command[0]);
+
+ sprintf (userId, "%d", getuid());
+ prop2.name = SmUserID;
+ prop2.type = SmARRAY8;
+ prop2.num_vals = 1;
+ prop2.vals = &prop2val;
+ prop2val.value = (SmPointer) userId;
+ prop2val.length = strlen (userId);
+
+ fullyQuantifiedName = CheckFullyQuantifiedName (
+ (char *) winInfo->wm_client_machine.value, &newstring);
+ sprintf (restartService, "rstart-rsh/%s", fullyQuantifiedName);
+ if (newstring)
+ free (fullyQuantifiedName);
+
+ prop3.name = "_XC_RestartService";
+ prop3.type = SmLISTofARRAY8;
+ prop3.num_vals = 1;
+ prop3.vals = &prop3val;
+ prop3val.value = (SmPointer) restartService;
+ prop3val.length = strlen (restartService);
+
+ props[0] = &prop1;
+ props[1] = &prop2;
+ props[2] = &prop3;
+
+ SmcSetProperties (winInfo->smc_conn, 3, props);
+
+ winInfo->got_first_save_yourself = 1;
+ }
+
+ prop1.name = SmRestartCommand;
+ prop1.type = SmLISTofARRAY8;
+ prop1.num_vals = winInfo->wm_command_count;
+
+ prop1.vals = (SmPropValue *) malloc (
+ winInfo->wm_command_count * sizeof (SmPropValue));
+
+ if (!prop1.vals)
+ {
+ SmcSaveYourselfDone (winInfo->smc_conn, False);
+ return;
+ }
+
+ for (i = 0; i < winInfo->wm_command_count; i++)
+ {
+ prop1.vals[i].value = (SmPointer) winInfo->wm_command[i];
+ prop1.vals[i].length = strlen (winInfo->wm_command[i]);
+ }
+
+ prop2.name = SmCloneCommand;
+ prop2.type = SmLISTofARRAY8;
+ prop2.num_vals = winInfo->wm_command_count;
+ prop2.vals = prop1.vals;
+
+ props[0] = &prop1;
+ props[1] = &prop2;
+
+ SmcSetProperties (winInfo->smc_conn, 2, props);
+
+ free ((char *) prop1.vals);
+
+ /*
+ * If the client doesn't support WM_SAVE_YOURSELF, we should
+ * return failure for the save, since we really don't know if
+ * the application needed to save state.
+ */
+
+ SmcSaveYourselfDone (winInfo->smc_conn, has_WM_SAVEYOURSELF);
+}
+
+
+
+void
+SaveYourselfCB (smcConn, clientData, saveType, shutdown, interactStyle, fast)
+
+SmcConn smcConn;
+SmPointer clientData;
+int saveType;
+Bool shutdown;
+int interactStyle;
+Bool fast;
+
+{
+ WinInfo *winInfo = (WinInfo *) clientData;
+
+ if (!winInfo->has_save_yourself)
+ {
+ FinishSaveYourself (winInfo, False);
+ }
+ else
+ {
+ XClientMessageEvent saveYourselfMessage;
+
+
+ /* Send WM_SAVE_YOURSELF */
+
+ saveYourselfMessage.type = ClientMessage;
+ saveYourselfMessage.window = winInfo->window;
+ saveYourselfMessage.message_type = wmProtocolsAtom;
+ saveYourselfMessage.format = 32;
+ saveYourselfMessage.data.l[0] = wmSaveYourselfAtom;
+ saveYourselfMessage.data.l[1] = CurrentTime;
+
+ if (XSendEvent (disp, winInfo->window, False, NoEventMask,
+ (XEvent *) &saveYourselfMessage))
+ {
+ winInfo->waiting_for_update = 1;
+
+ if (debug)
+ {
+ printf ("Sent SAVE YOURSELF to 0x%x\n", winInfo->window);
+ printf ("\n");
+ }
+ }
+ else
+ {
+ if (debug)
+ {
+ printf ("Failed to send SAVE YOURSELF to 0x%x\n",
+ winInfo->window);
+ printf ("\n");
+ }
+ }
+ }
+}
+
+
+
+void
+DieCB (smcConn, clientData)
+
+SmcConn smcConn;
+SmPointer clientData;
+
+{
+ WinInfo *winInfo = (WinInfo *) clientData;
+
+ SmcCloseConnection (winInfo->smc_conn, 0, NULL);
+ winInfo->smc_conn = NULL;
+ XtRemoveInput (winInfo->input_id);
+
+ /* Now tell the client to die */
+
+ if (debug)
+ printf ("Trying to kill 0x%x\n", winInfo->window);
+
+ XSync (disp, 0);
+ XKillClient (disp, winInfo->window);
+ XSync (disp, 0);
+
+
+ /*
+ * Proxy must exit when all clients die, and the proxy itself
+ * must have received a Die.
+ */
+
+ die_count++;
+
+ if (die_count == proxy_count && ok_to_die)
+ {
+ exit (0);
+ }
+}
+
+
+
+void
+SaveCompleteCB (smcConn, clientData)
+
+SmcConn smcConn;
+SmPointer clientData;
+
+{
+ /*
+ * Nothing to do here.
+ */
+}
+
+
+
+void
+ShutdownCancelledCB (smcConn, clientData)
+
+SmcConn smcConn;
+SmPointer clientData;
+
+{
+ /*
+ * Since we did not request to interact or request save yourself
+ * phase 2, we know we already sent the save yourself done, so
+ * there is nothing to do here.
+ */
+}
+
+
+
+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
+NullIceErrorHandler (iceConn, swap,
+ offendingMinorOpcode, offendingSequence, errorClass, severity, values)
+
+IceConn iceConn;
+Bool swap;
+int offendingMinorOpcode;
+unsigned long offendingSequence;
+int errorClass;
+int severity;
+IcePointer values;
+
+{
+ return;
+}
+
+
+void
+ConnectClientToSM (winInfo)
+
+WinInfo *winInfo;
+
+{
+ char errorMsg[256];
+ unsigned long mask;
+ SmcCallbacks callbacks;
+ IceConn ice_conn;
+ char *prevId;
+
+ mask = SmcSaveYourselfProcMask | SmcDieProcMask |
+ SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
+
+ callbacks.save_yourself.callback = SaveYourselfCB;
+ callbacks.save_yourself.client_data = (SmPointer) winInfo;
+
+ callbacks.die.callback = DieCB;
+ callbacks.die.client_data = (SmPointer) winInfo;
+
+ callbacks.save_complete.callback = SaveCompleteCB;
+ callbacks.save_complete.client_data = (SmPointer) winInfo;
+
+ callbacks.shutdown_cancelled.callback = ShutdownCancelledCB;
+ callbacks.shutdown_cancelled.client_data = (SmPointer) winInfo;
+
+ prevId = LookupClientID (winInfo);
+
+ /*
+ * In case a protocol error occurs when opening the connection,
+ * (e.g. an authentication error), we set a null error handler
+ * before the open, then restore the default handler after the open.
+ */
+
+ IceSetErrorHandler (NullIceErrorHandler);
+
+ winInfo->smc_conn = SmcOpenConnection (
+ NULL, /* use SESSION_MANAGER env */
+ (SmPointer) winInfo, /* force a new connection */
+ SmProtoMajor,
+ SmProtoMinor,
+ mask,
+ &callbacks,
+ prevId,
+ &winInfo->client_id,
+ 256, errorMsg);
+
+ IceSetErrorHandler (NULL);
+
+ if (winInfo->smc_conn == NULL)
+ return;
+
+ ice_conn = SmcGetIceConnection (winInfo->smc_conn);
+
+ winInfo->input_id = XtAppAddInput (
+ appContext,
+ IceConnectionNumber (ice_conn),
+ (XtPointer) XtInputReadMask,
+ ProcessIceMsgProc,
+ (XtPointer) ice_conn);
+
+ if (debug)
+ {
+ printf ("Connected to SM, window = 0x%x\n", winInfo->window);
+ printf ("\n");
+ }
+
+ proxy_count++;
+}
+
+
+
+MyErrorHandler (display, event)
+
+Display *display;
+XErrorEvent *event;
+
+{
+ caught_error = 1;
+}
+
+
+
+Bool
+LookupWindow (window, ptr_ret, prev_ptr_ret)
+
+Window window;
+WinInfo **ptr_ret;
+WinInfo **prev_ptr_ret;
+
+{
+ WinInfo *ptr, *prev;
+
+ ptr = win_head;
+ prev = NULL;
+
+ while (ptr)
+ {
+ if (ptr->window == window)
+ break;
+ else
+ {
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ }
+
+ if (ptr)
+ {
+ if (ptr_ret)
+ *ptr_ret = ptr;
+ if (prev_ptr_ret)
+ *prev_ptr_ret = prev;
+ return (1);
+ }
+ else
+ return (0);
+}
+
+
+
+WinInfo *
+AddNewWindow (window)
+
+Window window;
+
+{
+ WinInfo *newptr;
+
+ if (LookupWindow (window, NULL, NULL))
+ return (NULL);
+
+ newptr = (WinInfo *) malloc (sizeof (WinInfo));
+
+ if (newptr == NULL)
+ return (NULL);
+
+ newptr->next = win_head;
+ win_head = newptr;
+
+ newptr->window = window;
+ newptr->smc_conn = NULL;
+ newptr->tested_for_sm_client_id = 0;
+ newptr->client_id = NULL;
+ newptr->wm_command = NULL;
+ newptr->wm_command_count = 0;
+ newptr->class.res_name = NULL;
+ newptr->class.res_class = NULL;
+ newptr->wm_name = NULL;
+ newptr->wm_client_machine.value = NULL;
+ newptr->wm_client_machine.nitems = 0;
+ newptr->has_save_yourself = 0;
+ newptr->waiting_for_update = 0;
+ newptr->got_first_save_yourself = 0;
+
+ return (newptr);
+}
+
+
+
+void
+RemoveWindow (winptr)
+
+WinInfo *winptr;
+
+{
+ WinInfo *ptr, *prev;
+
+ if (LookupWindow (winptr->window, &ptr, &prev))
+ {
+ if (prev == NULL)
+ win_head = ptr->next;
+ else
+ prev->next = ptr->next;
+
+ if (ptr->client_id)
+ free (ptr->client_id);
+
+ if (ptr->wm_command)
+ XFreeStringList (ptr->wm_command);
+
+ if (ptr->wm_name)
+ XFree (ptr->wm_name);
+
+ if (ptr->wm_client_machine.value)
+ XFree (ptr->wm_client_machine.value);
+
+ if (ptr->class.res_name)
+ XFree (ptr->class.res_name);
+
+ if (ptr->class.res_class)
+ XFree (ptr->class.res_class);
+
+ free ((char *) ptr);
+ }
+}
+
+
+
+void
+Got_WM_STATE (winptr)
+
+WinInfo *winptr;
+
+{
+ WinInfo *leader_winptr;
+
+ /*
+ * If we already got WM_STATE and tested for SM_CLIENT_ID, we
+ * shouldn't do it again.
+ */
+
+ if (winptr->tested_for_sm_client_id)
+ {
+ return;
+ }
+
+
+ /*
+ * Set a null error handler, in case this window goes away
+ * behind our back.
+ */
+
+ caught_error = 0;
+ XSetErrorHandler (MyErrorHandler);
+
+
+ /*
+ * Get the client leader window.
+ */
+
+ leader_winptr = GetClientLeader (winptr);
+
+ if (caught_error)
+ {
+ caught_error = 0;
+ RemoveWindow (winptr);
+ XSetErrorHandler (NULL);
+ return;
+ }
+
+
+ /*
+ * If we already checked for SM_CLIENT_ID on the client leader
+ * window, don't do it again.
+ */
+
+ if (!leader_winptr || leader_winptr->tested_for_sm_client_id)
+ {
+ caught_error = 0;
+ XSetErrorHandler (NULL);
+ return;
+ }
+
+ leader_winptr->tested_for_sm_client_id = 1;
+
+ if (!HasXSMPsupport (leader_winptr->window))
+ {
+ XFetchName (disp, leader_winptr->window, &leader_winptr->wm_name);
+
+ XGetCommand (disp, leader_winptr->window,
+ &leader_winptr->wm_command,
+ &leader_winptr->wm_command_count);
+
+ XGetClassHint (disp, leader_winptr->window, &leader_winptr->class);
+
+ XGetWMClientMachine (disp, leader_winptr->window,
+ &leader_winptr->wm_client_machine);
+
+ if (leader_winptr->wm_name != NULL &&
+ leader_winptr->wm_command != NULL &&
+ leader_winptr->wm_command_count > 0 &&
+ leader_winptr->class.res_name != NULL &&
+ leader_winptr->class.res_class != NULL &&
+ leader_winptr->wm_client_machine.value != NULL &&
+ leader_winptr->wm_client_machine.nitems != 0)
+ {
+ leader_winptr->has_save_yourself =
+ HasSaveYourself (leader_winptr->window);
+
+ ConnectClientToSM (leader_winptr);
+ }
+ }
+
+ XSync (disp, 0);
+ XSetErrorHandler (NULL);
+
+ if (caught_error)
+ {
+ caught_error = 0;
+ RemoveWindow (leader_winptr);
+ }
+}
+
+
+
+void
+HandleCreate (event)
+
+XCreateWindowEvent *event;
+
+{
+ Atom actual_type;
+ int actual_format;
+ unsigned long nitems, bytesafter;
+ unsigned long *datap = NULL;
+ WinInfo *winptr;
+ Bool got_wm_state = 0;
+
+ /*
+ * We are waiting for all proxy connections to close so we can die.
+ * Don't handle new connections.
+ */
+
+ if (ok_to_die)
+ return;
+
+
+ /*
+ * Add the new window
+ */
+
+ if ((winptr = AddNewWindow (event->window)) == NULL)
+ return;
+
+
+ /*
+ * Right after the window was created, it might have been destroyed,
+ * so the following Xlib calls might fail. Need to catch the error
+ * by installing an error handler.
+ */
+
+ caught_error = 0;
+ XSetErrorHandler (MyErrorHandler);
+
+
+ /*
+ * Select for Property Notify on the window so we can determine
+ * when WM_STATE is defined. To avoid a race condition, we must
+ * do this _before_ we check for WM_STATE right now.
+ *
+ * Select for Substructure Notify so we can determine when the
+ * window is destroyed.
+ */
+
+ XSelectInput (disp, event->window,
+ SubstructureNotifyMask | PropertyChangeMask);
+
+
+ /*
+ * WM_STATE may already be there. Check now.
+ */
+
+ if (XGetWindowProperty (disp, event->window, wmStateAtom,
+ 0L, 2L, False, AnyPropertyType,
+ &actual_type, &actual_format, &nitems, &bytesafter,
+ (unsigned char **) &datap) == Success && datap)
+ {
+ if (nitems > 0)
+ got_wm_state = 1;
+
+ if (datap)
+ XFree ((char *) datap);
+ }
+
+ XSync (disp, 0);
+ XSetErrorHandler (NULL);
+
+ if (caught_error)
+ {
+ caught_error = 0;
+ RemoveWindow (winptr);
+ }
+ else if (got_wm_state)
+ {
+ Got_WM_STATE (winptr);
+ }
+}
+
+
+
+void
+HandleDestroy (event)
+
+XDestroyWindowEvent *event;
+
+{
+ WinInfo *winptr;
+
+ if (LookupWindow (event->window, &winptr, NULL))
+ {
+ if (winptr->smc_conn)
+ {
+ SmcCloseConnection (winptr->smc_conn, 0, NULL);
+ XtRemoveInput (winptr->input_id);
+ proxy_count--;
+ }
+
+ if (debug)
+ {
+ printf ("Removed window (window = 0x%x)\n", winptr->window);
+ printf ("\n");
+ }
+
+ RemoveWindow (winptr);
+ }
+}
+
+
+
+void
+HandleUpdate (event)
+
+XPropertyEvent *event;
+
+{
+ Window window = event->window;
+ WinInfo *winptr;
+
+ if (!LookupWindow (window, &winptr, NULL))
+ return;
+
+ if (event->atom == wmStateAtom)
+ {
+ Got_WM_STATE (winptr);
+ }
+ else if (event->atom == XA_WM_COMMAND && winptr->waiting_for_update)
+ {
+ /* Finish off the Save Yourself */
+
+ if (winptr->wm_command)
+ {
+ XFreeStringList (winptr->wm_command);
+ winptr->wm_command = NULL;
+ winptr->wm_command_count = 0;
+ }
+
+ XGetCommand (disp, window,
+ &winptr->wm_command,
+ &winptr->wm_command_count);
+
+ winptr->waiting_for_update = 0;
+ FinishSaveYourself (winptr, True);
+ }
+}
+
+
+
+void
+ProxySaveYourselfPhase2CB (smcConn, clientData)
+
+SmcConn smcConn;
+SmPointer clientData;
+
+{
+ char *filename;
+ Bool success = True;
+ SmProp prop1, prop2, prop3, *props[3];
+ SmPropValue prop1val, prop2val, prop3val;
+ char discardCommand[80];
+ int numVals, i;
+ static int first_time = 1;
+
+ 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, "%d", 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;
+ }
+
+ if ((filename = WriteProxyFile ()) == NULL)
+ {
+ success = False;
+ goto finishUp;
+ }
+
+ prop1.name = SmRestartCommand;
+ prop1.type = SmLISTofARRAY8;
+
+ prop1.vals = (SmPropValue *) malloc (
+ (Argc + 4) * sizeof (SmPropValue));
+
+ if (!prop1.vals)
+ {
+ success = False;
+ goto finishUp;
+ }
+
+ 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) proxy_clientId;
+ prop1.vals[numVals++].length = strlen (proxy_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);
+
+ finishUp:
+
+ SmcSaveYourselfDone (smcConn, success);
+ sent_save_done = 1;
+
+ if (filename)
+ free (filename);
+}
+
+
+
+void
+ProxySaveYourselfCB (smcConn, clientData, saveType,
+ shutdown, interactStyle, fast)
+
+SmcConn smcConn;
+SmPointer clientData;
+int saveType;
+Bool shutdown;
+int interactStyle;
+Bool fast;
+
+{
+ /*
+ * We want the proxy to respond to the Save Yourself after all
+ * the regular XSMP clients have finished with the save (and possibly
+ * interacted with the user).
+ */
+
+ if (!SmcRequestSaveYourselfPhase2 (smcConn,
+ ProxySaveYourselfPhase2CB, NULL))
+ {
+ SmcSaveYourselfDone (smcConn, False);
+ sent_save_done = 1;
+ }
+ else
+ sent_save_done = 0;
+}
+
+
+
+void
+ProxyDieCB (smcConn, clientData)
+
+SmcConn smcConn;
+SmPointer clientData;
+
+{
+ SmcCloseConnection (proxy_smcConn, 0, NULL);
+ XtRemoveInput (proxy_iceInputId);
+
+ if (die_count == proxy_count)
+ exit (0);
+ else
+ ok_to_die = 1;
+}
+
+
+
+void
+ProxySaveCompleteCB (smcConn, clientData)
+
+SmcConn smcConn;
+SmPointer clientData;
+
+{
+ ;
+}
+
+
+
+void
+ProxyShutdownCancelledCB (smcConn, clientData)
+
+SmcConn smcConn;
+SmPointer clientData;
+
+{
+ if (!sent_save_done)
+ {
+ SmcSaveYourselfDone (smcConn, False);
+ sent_save_done = 1;
+ }
+}
+
+
+
+Status
+ConnectProxyToSM (previous_id)
+
+char *previous_id;
+
+{
+ char errorMsg[256];
+ unsigned long mask;
+ SmcCallbacks callbacks;
+ IceConn iceConn;
+
+ mask = SmcSaveYourselfProcMask | SmcDieProcMask |
+ SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
+
+ callbacks.save_yourself.callback = ProxySaveYourselfCB;
+ callbacks.save_yourself.client_data = (SmPointer) NULL;
+
+ callbacks.die.callback = ProxyDieCB;
+ callbacks.die.client_data = (SmPointer) NULL;
+
+ callbacks.save_complete.callback = ProxySaveCompleteCB;
+ callbacks.save_complete.client_data = (SmPointer) NULL;
+
+ callbacks.shutdown_cancelled.callback = ProxyShutdownCancelledCB;
+ callbacks.shutdown_cancelled.client_data = (SmPointer) NULL;
+
+ proxy_smcConn = SmcOpenConnection (
+ NULL, /* use SESSION_MANAGER env */
+ (SmPointer) appContext,
+ SmProtoMajor,
+ SmProtoMinor,
+ mask,
+ &callbacks,
+ previous_id,
+ &proxy_clientId,
+ 256, errorMsg);
+
+ if (proxy_smcConn == NULL)
+ return (0);
+
+ iceConn = SmcGetIceConnection (proxy_smcConn);
+
+ proxy_iceInputId = XtAppAddInput (
+ appContext,
+ IceConnectionNumber (iceConn),
+ (XtPointer) XtInputReadMask,
+ ProcessIceMsgProc,
+ (XtPointer) iceConn);
+
+ return (1);
+}
+
+
+
+void
+CheckForExistingWindows (root)
+
+Window root;
+
+{
+ Window dontCare1, dontCare2, *children, client_window;
+ unsigned int nchildren, i;
+ XCreateWindowEvent event;
+
+ /*
+ * We query the root tree for all windows created thus far.
+ * Note that at any moment after XQueryTree is called, a
+ * window may be deleted. So we must take extra care to make
+ * sure a window really exists.
+ */
+
+ XQueryTree (disp, root, &dontCare1, &dontCare2, &children, &nchildren);
+
+ for (i = 0; i < nchildren; i++)
+ {
+ event.window = children[i];
+
+ HandleCreate (&event);
+
+ caught_error = 0;
+ XSetErrorHandler (MyErrorHandler);
+
+ client_window = XmuClientWindow (disp, children[i]);
+
+ XSetErrorHandler (NULL);
+
+ if (!caught_error && client_window != children[i])
+ {
+ event.window = client_window;
+ HandleCreate (&event);
+ }
+ }
+}
+
+
+
+main (argc, argv)
+
+int argc;
+char **argv;
+
+{
+ char *restore_filename = NULL;
+ char *client_id = NULL;
+ int i, zero = 0;
+
+ Argc = argc;
+ Argv = argv;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ switch (argv[i][1])
+ {
+ case 'd': /* -debug */
+ debug = 1;
+ continue;
+
+ case 'c': /* -clientId */
+ if (++i >= argc) goto usage;
+ client_id = argv[i];
+ continue;
+
+ case 'r': /* -restore */
+ if (++i >= argc) goto usage;
+ restore_filename = argv[i];
+ continue;
+ }
+ }
+
+ usage:
+
+ fprintf (stderr,
+ "usage: %s [-clientId id] [-restore file] [-debug]\n", argv[0]);
+ exit (1);
+ }
+
+
+ XtToolkitInitialize ();
+ appContext = XtCreateApplicationContext ();
+
+ if (!(disp = XtOpenDisplay (appContext, NULL, "SM-PROXY", "SM-PROXY",
+ NULL, 0, &zero, NULL)))
+ {
+ fprintf (stderr, "smproxy: unable to open display\n");
+ exit (1);
+ }
+
+ if (restore_filename)
+ ReadProxyFile (restore_filename);
+
+ if (!ConnectProxyToSM (client_id))
+ {
+ fprintf (stderr, "smproxy: unable to connect to session manager\n");
+ exit (1);
+ }
+
+ wmProtocolsAtom = XInternAtom (disp, "WM_PROTOCOLS", False);
+ wmSaveYourselfAtom = XInternAtom (disp, "WM_SAVE_YOURSELF", False);
+ wmStateAtom = XInternAtom (disp, "WM_STATE", False);
+ smClientIdAtom = XInternAtom (disp, "SM_CLIENT_ID", False);
+ wmClientLeaderAtom = XInternAtom (disp, "WM_CLIENT_LEADER", False);
+
+ for (i = 0; i < ScreenCount (disp); i++)
+ {
+ Window root = RootWindow (disp, i);
+ XSelectInput (disp, root, SubstructureNotifyMask | PropertyChangeMask);
+ CheckForExistingWindows (root);
+ }
+
+ while (1)
+ {
+ XEvent event;
+
+ XtAppNextEvent (appContext, &event);
+
+ switch (event.type)
+ {
+ case CreateNotify:
+ HandleCreate (&event.xcreatewindow);
+ break;
+
+ case DestroyNotify:
+ HandleDestroy (&event.xdestroywindow);
+ break;
+
+ case PropertyNotify:
+ HandleUpdate (&event.xproperty);
+ break;
+
+ default:
+ XtDispatchEvent (&event);
+ break;
+ }
+ }
+}
diff --git a/smproxy.h b/smproxy.h
new file mode 100644
index 0000000..a6e7522
--- /dev/null
+++ b/smproxy.h
@@ -0,0 +1,101 @@
+/* $Xorg: smproxy.h,v 1.4 2001/02/09 02:05:36 xorgcvs Exp $ */
+/******************************************************************************
+
+Copyright 1994, 1998 The Open Group
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall not be
+used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization from The Open Group.
+
+Author: Ralph Mor, X Consortium
+******************************************************************************/
+
+#include <X11/Xosdefs.h>
+#include <X11/Xfuncs.h>
+
+#include <X11/StringDefs.h>
+#include <X11/Intrinsic.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/SM/SMlib.h>
+
+#ifndef _POSIX_SOURCE
+#define _POSIX_SOURCE
+#include <stdio.h>
+#undef _POSIX_SOURCE
+#else
+#include <stdio.h>
+#endif
+
+#ifndef X_NOT_STDC_ENV
+#include <stdlib.h>
+#else
+extern char *malloc(), *calloc(), *realloc(), *getenv();
+extern void free();
+#endif
+
+#ifndef X_NOT_POSIX
+#ifdef _POSIX_SOURCE
+#include <limits.h>
+#else
+#define _POSIX_SOURCE
+#include <limits.h>
+#undef _POSIX_SOURCE
+#endif
+#endif /* X_NOT_POSIX */
+#ifndef PATH_MAX
+#include <sys/param.h>
+#ifndef PATH_MAX
+#ifdef MAXPATHLEN
+#define PATH_MAX MAXPATHLEN
+#else
+#define PATH_MAX 1024
+#endif
+#endif
+#endif /* PATH_MAX */
+
+
+typedef struct WinInfo {
+ Window window;
+ SmcConn smc_conn;
+ XtInputId input_id;
+ char *client_id;
+ char **wm_command;
+ int wm_command_count;
+ XClassHint class;
+ char *wm_name;
+ XTextProperty wm_client_machine;
+ struct WinInfo *next;
+
+ unsigned int tested_for_sm_client_id : 1;
+ unsigned int has_save_yourself : 1;
+ unsigned int waiting_for_update : 1;
+ unsigned int got_first_save_yourself : 1;
+
+} WinInfo;
+
+
+extern int WriteProxyFileEntry ();
+extern int ReadProxyFileEntry ();
+extern void ReadProxyFile ();
+extern char *WriteProxyFile ();
+extern char *LookupClientID ();
+
+
+#define SAVEFILE_VERSION 1
diff --git a/smproxy.man b/smproxy.man
new file mode 100644
index 0000000..0c1c355
--- /dev/null
+++ b/smproxy.man
@@ -0,0 +1,72 @@
+.\" $Xorg: smproxy.man,v 1.4 2001/02/09 02:05:36 xorgcvs Exp $
+.\" Copyright 1994, 1998 The Open Group
+.\"
+.\" Permission to use, copy, modify, distribute, and sell this software and its
+.\" documentation for any purpose is hereby granted without fee, provided that
+.\" the above copyright notice appear in all copies and that both that
+.\" copyright notice and this permission notice appear in supporting
+.\" documentation.
+.\"
+.\" The above copyright notice and this permission notice shall be included
+.\" in all copies or substantial portions of the Software.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+.\" IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+.\" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+.\" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+.\" OTHER DEALINGS IN THE SOFTWARE.
+.\"
+.\" Except as contained in this notice, the name of The Open Group shall
+.\" not be used in advertising or otherwise to promote the sale, use or
+.\" other dealings in this Software without prior written authorization
+.\" from The Open Group.
+.TH XSM 1 "Release 6.4" "X Version 11"
+.SH NAME
+smproxy \- Session Manager Proxy
+.SH SYNOPSIS
+.B smproxy
+[-clientId id] [-restore saveFile]
+.SH OPTIONS
+.TP 8
+.B \-clientId \fIid\fP
+Specifies the session ID used by \fIsmproxy\fP in the previous session.
+.TP 8
+.B \-restore \fIsaveFile\fP
+Specifies the file used by \fIsmproxy\fP to save state in the previous session.
+.SH DESCRIPTION
+.PP
+\fIsmproxy\fP allows X applications that do not support X11R6 session
+management to participate in an X11R6 session.
+.br
+.sp
+In order for \fIsmproxy\fP to act as a proxy for an X application, one of
+the following must be true:
+.br
+.sp
+- The application maps a top level window containing the
+\fBWM_CLIENT_LEADER\fR property. This property provides a pointer to
+the client leader window which contains the \fBWM_CLASS\fR, \fBWM_NAME\fR,
+\fBWM_COMMAND\fR, and \fBWM_CLIENT_MACHINE\fR properties.
+.br
+.sp
+or ...
+.br
+.sp
+- The application maps a top level window which does not contain the
+\fBWM_CLIENT_LEADER\fR property. However, this top level window
+contains the \fBWM_CLASS\fR, \fBWM_NAME\fR, \fBWM_COMMAND\fR, and
+\fBWM_CLIENT_MACHINE\fR properties.
+.PP
+An application that support the \fBWM_SAVE_YOURSELF\fR protocol will receive
+a \fBWM_SAVE_YOURSELF\fR client message each time the session manager issues
+a checkpoint or shutdown. This allows the application to save state. If
+an application does not support the \fBWM_SAVE_YOURSELF\fR protocol, then
+the proxy will provide enough information to the session manager to restart
+the application (using \fBWM_COMMAND\fR), but no state will be restored.
+.SH SEE ALSO
+xsm(1)
+.SH AUTHOR
+Ralph Mor, X Consortium
+