114c0a534Smrg/******************************************************************************
214c0a534Smrg
314c0a534SmrgCopyright 1994, 1998  The Open Group
414c0a534Smrg
514c0a534SmrgPermission to use, copy, modify, distribute, and sell this software and its
614c0a534Smrgdocumentation for any purpose is hereby granted without fee, provided that
714c0a534Smrgthe above copyright notice appear in all copies and that both that
814c0a534Smrgcopyright notice and this permission notice appear in supporting
914c0a534Smrgdocumentation.
1014c0a534Smrg
1114c0a534SmrgThe above copyright notice and this permission notice shall be included in
1214c0a534Smrgall copies or substantial portions of the Software.
1314c0a534Smrg
1414c0a534SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1514c0a534SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1614c0a534SmrgFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
1714c0a534SmrgOPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
1814c0a534SmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
1914c0a534SmrgCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2014c0a534Smrg
2114c0a534SmrgExcept as contained in this notice, the name of The Open Group shall not be
2214c0a534Smrgused in advertising or otherwise to promote the sale, use or other dealings
2314c0a534Smrgin this Software without prior written authorization from The Open Group.
2414c0a534Smrg
2514c0a534SmrgAuthor:  Ralph Mor, X Consortium
2614c0a534Smrg******************************************************************************/
2714c0a534Smrg
2814c0a534Smrg#include "smproxy.h"
2914c0a534Smrg#include <unistd.h>
3014c0a534Smrg#include <X11/Xmu/WinUtil.h>
31bdc460c5Smrg#include <limits.h>
32bdc460c5Smrg
33bdc460c5Smrg#ifndef HOST_NAME_MAX
34bdc460c5Smrg#define HOST_NAME_MAX 256
35bdc460c5Smrg#endif
3614c0a534Smrg
37bf2eeab3Smrgstatic XtAppContext appContext;
38bf2eeab3Smrgstatic Display *disp;
3914c0a534Smrg
40bf2eeab3Smrgstatic Atom wmProtocolsAtom;
41bf2eeab3Smrgstatic Atom wmSaveYourselfAtom;
42bf2eeab3Smrgstatic Atom wmStateAtom;
43bf2eeab3Smrgstatic Atom smClientIdAtom;
44bf2eeab3Smrgstatic Atom wmClientLeaderAtom;
4514c0a534Smrg
46bf2eeab3Smrgstatic Bool debug = 0;
4714c0a534Smrg
48bf2eeab3Smrgstatic SmcConn proxy_smcConn;
49bf2eeab3Smrgstatic XtInputId proxy_iceInputId;
50bf2eeab3Smrgstatic char *proxy_clientId = NULL;
5114c0a534Smrg
5214c0a534SmrgWinInfo *win_head = NULL;
5314c0a534Smrg
54bf2eeab3Smrgstatic int proxy_count = 0;
55bf2eeab3Smrgstatic int die_count = 0;
5614c0a534Smrg
57bf2eeab3Smrgstatic Bool ok_to_die = 0;
5814c0a534Smrg
59bf2eeab3Smrgstatic Bool caught_error = 0;
6014c0a534Smrg
61bf2eeab3Smrgstatic Bool sent_save_done = 0;
6214c0a534Smrg
63bf2eeab3Smrgstatic int Argc;
64bf2eeab3Smrgstatic char **Argv;
6514c0a534Smrg
66bf2eeab3Smrgstatic Bool HasSaveYourself ( Window window );
67bf2eeab3Smrgstatic Bool HasXSMPsupport ( Window window );
68bf2eeab3Smrgstatic WinInfo * GetClientLeader ( WinInfo *winptr );
69bf2eeab3Smrgstatic char * CheckFullyQuantifiedName ( char *name, int *newstring );
70bf2eeab3Smrgstatic void FinishSaveYourself ( WinInfo *winInfo, Bool has_WM_SAVEYOURSELF );
71bf2eeab3Smrgstatic void SaveYourselfCB ( SmcConn smcConn, SmPointer clientData, int saveType,
72bf2eeab3Smrg			    Bool shutdown, int interactStyle, Bool fast );
73bf2eeab3Smrgstatic void DieCB ( SmcConn smcConn, SmPointer clientData );
74bf2eeab3Smrgstatic void SaveCompleteCB ( SmcConn smcConn, SmPointer clientData );
75bf2eeab3Smrgstatic void ShutdownCancelledCB ( SmcConn smcConn, SmPointer clientData );
76bf2eeab3Smrgstatic void ProcessIceMsgProc ( XtPointer client_data, int *source, XtInputId *id );
77bf2eeab3Smrgstatic void NullIceErrorHandler ( IceConn iceConn, Bool swap,
78bdc460c5Smrg			   int offendingMinorOpcode,
7914c0a534Smrg			   unsigned long offendingSequence,
8014c0a534Smrg			   int errorClass, int severity, IcePointer values );
81bf2eeab3Smrgstatic void ConnectClientToSM ( WinInfo *winInfo );
82bf2eeab3Smrgstatic int MyErrorHandler ( Display *display, XErrorEvent *event );
83bf2eeab3Smrgstatic Bool LookupWindow ( Window window, WinInfo **ptr_ret, WinInfo **prev_ptr_ret );
84bf2eeab3Smrgstatic WinInfo * AddNewWindow ( Window window );
85bf2eeab3Smrgstatic void RemoveWindow ( WinInfo *winptr );
86bf2eeab3Smrgstatic void Got_WM_STATE ( WinInfo *winptr );
87bf2eeab3Smrgstatic void HandleCreate ( XCreateWindowEvent *event );
88bf2eeab3Smrgstatic void HandleDestroy ( XDestroyWindowEvent *event );
89bf2eeab3Smrgstatic void HandleUpdate ( XPropertyEvent *event );
90bf2eeab3Smrgstatic void ProxySaveYourselfPhase2CB ( SmcConn smcConn, SmPointer clientData );
91bf2eeab3Smrgstatic void ProxySaveYourselfCB ( SmcConn smcConn, SmPointer clientData,
9214c0a534Smrg			   int saveType, Bool shutdown, int interactStyle,
9314c0a534Smrg			   Bool fast );
94bf2eeab3Smrgstatic void ProxyDieCB ( SmcConn smcConn, SmPointer clientData );
95bf2eeab3Smrgstatic void ProxySaveCompleteCB ( SmcConn smcConn, SmPointer clientData );
96bf2eeab3Smrgstatic void ProxyShutdownCancelledCB ( SmcConn smcConn, SmPointer clientData );
97bf2eeab3Smrgstatic Status ConnectProxyToSM ( char *previous_id );
98bf2eeab3Smrgstatic void CheckForExistingWindows ( Window root );
9914c0a534Smrg
10014c0a534Smrg
101bf2eeab3Smrgstatic Bool
102bf2eeab3SmrgHasSaveYourself(Window window)
10314c0a534Smrg{
10414c0a534Smrg    Atom *protocols;
10514c0a534Smrg    int numProtocols;
106bdc460c5Smrg    int found;
10714c0a534Smrg
10814c0a534Smrg    protocols = NULL;
10914c0a534Smrg
11014c0a534Smrg    if (XGetWMProtocols (disp, window, &protocols, &numProtocols) != True)
11114c0a534Smrg	return (False);
11214c0a534Smrg
11314c0a534Smrg    found = 0;
11414c0a534Smrg
11514c0a534Smrg    if (protocols != NULL)
11614c0a534Smrg    {
117bdc460c5Smrg	for (int i = 0; i < numProtocols; i++) {
11814c0a534Smrg	    if (protocols[i] == wmSaveYourselfAtom)
11914c0a534Smrg		found = 1;
120bdc460c5Smrg	}
12114c0a534Smrg
12214c0a534Smrg	XFree (protocols);
12314c0a534Smrg    }
12414c0a534Smrg
12514c0a534Smrg    return (found);
12614c0a534Smrg}
12714c0a534Smrg
12814c0a534Smrg
12914c0a534Smrg
130bf2eeab3Smrgstatic Bool
131bf2eeab3SmrgHasXSMPsupport(Window window)
13214c0a534Smrg{
13314c0a534Smrg    XTextProperty tp;
13414c0a534Smrg    Bool hasIt = 0;
13514c0a534Smrg
13614c0a534Smrg    if (XGetTextProperty (disp, window, &tp, smClientIdAtom))
13714c0a534Smrg    {
13814c0a534Smrg	if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0)
13914c0a534Smrg	    hasIt = 1;
14014c0a534Smrg
14114c0a534Smrg	if (tp.value)
14214c0a534Smrg	    XFree ((char *) tp.value);
14314c0a534Smrg    }
14414c0a534Smrg
14514c0a534Smrg    return (hasIt);
14614c0a534Smrg}
14714c0a534Smrg
14814c0a534Smrg
14914c0a534Smrg
150bf2eeab3Smrgstatic WinInfo *
151bf2eeab3SmrgGetClientLeader(WinInfo *winptr)
15214c0a534Smrg{
15314c0a534Smrg    Atom actual_type;
15414c0a534Smrg    int actual_format;
15514c0a534Smrg    unsigned long nitems, bytesafter;
15614c0a534Smrg    unsigned long *datap = NULL;
15714c0a534Smrg    WinInfo *leader_winptr = NULL;
15814c0a534Smrg    Bool failure = 0;
15914c0a534Smrg
16014c0a534Smrg    if (XGetWindowProperty (disp, winptr->window, wmClientLeaderAtom,
16114c0a534Smrg	0L, 1L, False, AnyPropertyType,	&actual_type, &actual_format,
16214c0a534Smrg	&nitems, &bytesafter, (unsigned char **) &datap) == Success)
16314c0a534Smrg    {
16414c0a534Smrg	if (actual_type == XA_WINDOW && actual_format == 32 &&
16514c0a534Smrg	    nitems == 1 && bytesafter == 0)
16614c0a534Smrg	{
16714c0a534Smrg	    Window leader_win = *((Window *) datap);
16814c0a534Smrg
16914c0a534Smrg	    if (!LookupWindow (leader_win, &leader_winptr, NULL))
17014c0a534Smrg		failure = 1;
17114c0a534Smrg	}
17214c0a534Smrg
17314c0a534Smrg	if (datap)
17414c0a534Smrg	    XFree (datap);
17514c0a534Smrg    }
17614c0a534Smrg
17714c0a534Smrg    if (failure)
17814c0a534Smrg    {
17914c0a534Smrg	/* The client leader was defined, but we couldn't find the window */
18014c0a534Smrg
18114c0a534Smrg	return (NULL);
18214c0a534Smrg    }
18314c0a534Smrg    else if (leader_winptr)
18414c0a534Smrg    {
18514c0a534Smrg	/* We found the real client leader */
18614c0a534Smrg
18714c0a534Smrg	return (leader_winptr);
18814c0a534Smrg    }
18914c0a534Smrg    else
19014c0a534Smrg    {
19114c0a534Smrg	/* There is no client leader defined, return this window */
19214c0a534Smrg
19314c0a534Smrg	return (winptr);
19414c0a534Smrg    }
19514c0a534Smrg}
19614c0a534Smrg
19714c0a534Smrg
19814c0a534Smrg
199bf2eeab3Smrgstatic char *
200bf2eeab3SmrgCheckFullyQuantifiedName(char *name, int *newstring)
20114c0a534Smrg{
20214c0a534Smrg    /*
20314c0a534Smrg     * Due to a bug in Xlib (for hpux in particular), some clients
20414c0a534Smrg     * will have a WM_CLIENT_MACHINE that is not fully quantified.
20514c0a534Smrg     * For example, we might get "excon" instead of "excon.x.org".
20614c0a534Smrg     * This really stinks.  The best we can do is tag on our own
207bdc460c5Smrg     * domain name, if we can - if not, the unquantified name is
208bdc460c5Smrg     * better than nothing.
20914c0a534Smrg     */
21014c0a534Smrg
21114c0a534Smrg    if (strchr (name, '.') != NULL)
21214c0a534Smrg    {
21314c0a534Smrg	*newstring = 0;
21414c0a534Smrg	return (name);
21514c0a534Smrg    }
21614c0a534Smrg    else
21714c0a534Smrg    {
218bdc460c5Smrg	char hostnamebuf[HOST_NAME_MAX + 1] = { 0 };
21914c0a534Smrg	char *firstDot;
22014c0a534Smrg
221bdc460c5Smrg	if (gethostname (hostnamebuf, sizeof hostnamebuf) == 0)
222bdc460c5Smrg	    firstDot = strchr (hostnamebuf, '.');
223bdc460c5Smrg	else
224bdc460c5Smrg	    firstDot = NULL;
22514c0a534Smrg
22614c0a534Smrg	if (!firstDot)
22714c0a534Smrg	{
22814c0a534Smrg	    *newstring = 0;
22914c0a534Smrg	    return (name);
23014c0a534Smrg	}
23114c0a534Smrg	else
23214c0a534Smrg	{
23314c0a534Smrg	    char *newptr;
23414c0a534Smrg
23524047306Smrg	    if (asprintf (&newptr, "%s.%s", name, firstDot + 1) == -1) {
23624047306Smrg                *newstring = 0;
237bdc460c5Smrg                return (name);
23824047306Smrg            }
23914c0a534Smrg	    *newstring = 1;
24014c0a534Smrg	    return (newptr);
24114c0a534Smrg	}
24214c0a534Smrg    }
24314c0a534Smrg}
24414c0a534Smrg
24514c0a534Smrg
24614c0a534Smrg
247bf2eeab3Smrgstatic void
248bf2eeab3SmrgFinishSaveYourself(WinInfo *winInfo, Bool has_WM_SAVEYOURSELF)
24914c0a534Smrg{
25014c0a534Smrg    SmProp prop1, prop2, prop3, *props[3];
25114c0a534Smrg    SmPropValue prop1val, prop2val, prop3val;
25214c0a534Smrg
25314c0a534Smrg    if (!winInfo->got_first_save_yourself)
25414c0a534Smrg    {
25514c0a534Smrg	char userId[20], restartService[80];
25614c0a534Smrg	char *fullyQuantifiedName;
25714c0a534Smrg	int newstring;
25814c0a534Smrg
25914c0a534Smrg	prop1.name = SmProgram;
26014c0a534Smrg	prop1.type = SmARRAY8;
26114c0a534Smrg	prop1.num_vals = 1;
26214c0a534Smrg	prop1.vals = &prop1val;
26314c0a534Smrg	prop1val.value = (SmPointer) winInfo->wm_command[0];
26414c0a534Smrg	prop1val.length = strlen (winInfo->wm_command[0]);
26514c0a534Smrg
26624047306Smrg	snprintf (userId, sizeof(userId), "%ld", (long)getuid());
26714c0a534Smrg	prop2.name = SmUserID;
26814c0a534Smrg	prop2.type = SmARRAY8;
26914c0a534Smrg	prop2.num_vals = 1;
27014c0a534Smrg	prop2.vals = &prop2val;
27114c0a534Smrg	prop2val.value = (SmPointer) userId;
27214c0a534Smrg	prop2val.length = strlen (userId);
27314c0a534Smrg
27414c0a534Smrg	fullyQuantifiedName = CheckFullyQuantifiedName (
27514c0a534Smrg	    (char *) winInfo->wm_client_machine.value, &newstring);
27624047306Smrg	snprintf (restartService, sizeof(restartService),
27724047306Smrg                  "rstart-rsh/%s", fullyQuantifiedName);
27814c0a534Smrg	if (newstring)
27914c0a534Smrg	    free (fullyQuantifiedName);
28014c0a534Smrg
28114c0a534Smrg	prop3.name = "_XC_RestartService";
28214c0a534Smrg	prop3.type = SmLISTofARRAY8;
28314c0a534Smrg	prop3.num_vals = 1;
28414c0a534Smrg	prop3.vals = &prop3val;
28514c0a534Smrg	prop3val.value = (SmPointer) restartService;
28614c0a534Smrg	prop3val.length = strlen (restartService);
28714c0a534Smrg
28814c0a534Smrg	props[0] = &prop1;
28914c0a534Smrg	props[1] = &prop2;
29014c0a534Smrg	props[2] = &prop3;
29114c0a534Smrg
29214c0a534Smrg	SmcSetProperties (winInfo->smc_conn, 3, props);
29314c0a534Smrg
29414c0a534Smrg	winInfo->got_first_save_yourself = 1;
29514c0a534Smrg    }
29614c0a534Smrg
29714c0a534Smrg    prop1.name = SmRestartCommand;
29814c0a534Smrg    prop1.type = SmLISTofARRAY8;
29914c0a534Smrg    prop1.num_vals = winInfo->wm_command_count;
30014c0a534Smrg
301bdc460c5Smrg    prop1.vals = calloc (winInfo->wm_command_count, sizeof (SmPropValue));
30214c0a534Smrg
30314c0a534Smrg    if (!prop1.vals)
30414c0a534Smrg    {
30514c0a534Smrg	SmcSaveYourselfDone (winInfo->smc_conn, False);
30614c0a534Smrg	return;
30714c0a534Smrg    }
30814c0a534Smrg
309bdc460c5Smrg    for (int i = 0; i < winInfo->wm_command_count; i++)
31014c0a534Smrg    {
31114c0a534Smrg	prop1.vals[i].value = (SmPointer) winInfo->wm_command[i];
31214c0a534Smrg	prop1.vals[i].length = strlen (winInfo->wm_command[i]);
31314c0a534Smrg    }
31414c0a534Smrg
31514c0a534Smrg    prop2.name = SmCloneCommand;
31614c0a534Smrg    prop2.type = SmLISTofARRAY8;
31714c0a534Smrg    prop2.num_vals = winInfo->wm_command_count;
31814c0a534Smrg    prop2.vals = prop1.vals;
31914c0a534Smrg
32014c0a534Smrg    props[0] = &prop1;
32114c0a534Smrg    props[1] = &prop2;
32214c0a534Smrg
32314c0a534Smrg    SmcSetProperties (winInfo->smc_conn, 2, props);
32414c0a534Smrg
325bdc460c5Smrg    free (prop1.vals);
32614c0a534Smrg
32714c0a534Smrg    /*
32814c0a534Smrg     * If the client doesn't support WM_SAVE_YOURSELF, we should
32914c0a534Smrg     * return failure for the save, since we really don't know if
33014c0a534Smrg     * the application needed to save state.
33114c0a534Smrg     */
33214c0a534Smrg
33314c0a534Smrg    SmcSaveYourselfDone (winInfo->smc_conn, has_WM_SAVEYOURSELF);
33414c0a534Smrg}
33514c0a534Smrg
33614c0a534Smrg
33714c0a534Smrg
338bf2eeab3Smrgstatic void
339bf2eeab3SmrgSaveYourselfCB(SmcConn smcConn, SmPointer clientData, int saveType,
340bf2eeab3Smrg	       Bool shutdown, int interactStyle, Bool fast)
34114c0a534Smrg{
34214c0a534Smrg    WinInfo *winInfo = (WinInfo *) clientData;
34314c0a534Smrg
34414c0a534Smrg    if (!winInfo->has_save_yourself)
34514c0a534Smrg    {
34614c0a534Smrg	FinishSaveYourself (winInfo, False);
34714c0a534Smrg    }
34814c0a534Smrg    else
34914c0a534Smrg    {
35014c0a534Smrg	XClientMessageEvent saveYourselfMessage;
35114c0a534Smrg
35214c0a534Smrg
35314c0a534Smrg	/* Send WM_SAVE_YOURSELF */
35414c0a534Smrg
35514c0a534Smrg	saveYourselfMessage.type = ClientMessage;
35614c0a534Smrg	saveYourselfMessage.window = winInfo->window;
35714c0a534Smrg	saveYourselfMessage.message_type = wmProtocolsAtom;
35814c0a534Smrg	saveYourselfMessage.format = 32;
35914c0a534Smrg	saveYourselfMessage.data.l[0] = wmSaveYourselfAtom;
36014c0a534Smrg	saveYourselfMessage.data.l[1] = CurrentTime;
36114c0a534Smrg
36214c0a534Smrg	if (XSendEvent (disp, winInfo->window, False, NoEventMask,
36314c0a534Smrg	    (XEvent *) &saveYourselfMessage))
36414c0a534Smrg	{
36514c0a534Smrg	    winInfo->waiting_for_update = 1;
36614c0a534Smrg
36714c0a534Smrg	    if (debug)
36814c0a534Smrg	    {
36914c0a534Smrg		printf ("Sent SAVE YOURSELF to 0x%x\n",
37014c0a534Smrg			(unsigned int)winInfo->window);
37114c0a534Smrg		printf ("\n");
37214c0a534Smrg	    }
37314c0a534Smrg	}
37414c0a534Smrg	else
37514c0a534Smrg	{
37614c0a534Smrg	    if (debug)
37714c0a534Smrg	    {
37814c0a534Smrg		printf ("Failed to send SAVE YOURSELF to 0x%x\n",
37914c0a534Smrg		    (unsigned int)winInfo->window);
38014c0a534Smrg		printf ("\n");
38114c0a534Smrg	    }
38214c0a534Smrg	}
38314c0a534Smrg    }
38414c0a534Smrg}
38514c0a534Smrg
38614c0a534Smrg
38714c0a534Smrg
388bf2eeab3Smrgstatic void
389bf2eeab3SmrgDieCB(SmcConn smcConn, SmPointer clientData)
39014c0a534Smrg{
39114c0a534Smrg    WinInfo *winInfo = (WinInfo *) clientData;
39214c0a534Smrg
39314c0a534Smrg    SmcCloseConnection (winInfo->smc_conn, 0, NULL);
39414c0a534Smrg    winInfo->smc_conn = NULL;
39514c0a534Smrg    XtRemoveInput (winInfo->input_id);
39614c0a534Smrg
39714c0a534Smrg    /* Now tell the client to die */
39814c0a534Smrg
39914c0a534Smrg    if (debug)
40014c0a534Smrg	printf ("Trying to kill 0x%x\n", (unsigned int)winInfo->window);
40114c0a534Smrg
40214c0a534Smrg    XSync (disp, 0);
40314c0a534Smrg    XKillClient (disp, winInfo->window);
40414c0a534Smrg    XSync (disp, 0);
40514c0a534Smrg
40614c0a534Smrg
40714c0a534Smrg    /*
40814c0a534Smrg     * Proxy must exit when all clients die, and the proxy itself
40914c0a534Smrg     * must have received a Die.
41014c0a534Smrg     */
41114c0a534Smrg
41214c0a534Smrg    die_count++;
41314c0a534Smrg
41414c0a534Smrg    if (die_count == proxy_count && ok_to_die)
41514c0a534Smrg    {
41614c0a534Smrg	exit (0);
41714c0a534Smrg    }
41814c0a534Smrg}
41914c0a534Smrg
42014c0a534Smrg
42114c0a534Smrg
422bf2eeab3Smrgstatic void
423bf2eeab3SmrgSaveCompleteCB(SmcConn smcConn, SmPointer clientData)
42414c0a534Smrg{
42514c0a534Smrg    /*
42614c0a534Smrg     * Nothing to do here.
42714c0a534Smrg     */
42814c0a534Smrg}
42914c0a534Smrg
43014c0a534Smrg
43114c0a534Smrg
432bf2eeab3Smrgstatic void
433bf2eeab3SmrgShutdownCancelledCB(SmcConn smcConn, SmPointer clientData)
43414c0a534Smrg{
43514c0a534Smrg    /*
43614c0a534Smrg     * Since we did not request to interact or request save yourself
43714c0a534Smrg     * phase 2, we know we already sent the save yourself done, so
43814c0a534Smrg     * there is nothing to do here.
43914c0a534Smrg     */
44014c0a534Smrg}
44114c0a534Smrg
44214c0a534Smrg
44314c0a534Smrg
444bf2eeab3Smrgstatic void
445bf2eeab3SmrgProcessIceMsgProc(XtPointer client_data, int *source, XtInputId *id)
44614c0a534Smrg{
44714c0a534Smrg    IceConn	ice_conn = (IceConn) client_data;
44814c0a534Smrg
44914c0a534Smrg    IceProcessMessages (ice_conn, NULL, NULL);
45014c0a534Smrg}
45114c0a534Smrg
45214c0a534Smrg
45314c0a534Smrg
454bf2eeab3Smrgstatic void
455bf2eeab3SmrgNullIceErrorHandler(IceConn iceConn, Bool swap, int offendingMinorOpcode,
456bf2eeab3Smrg		    unsigned long offendingSequence, int errorClass,
457bf2eeab3Smrg		    int severity, IcePointer values)
45814c0a534Smrg{
45914c0a534Smrg    return;
46014c0a534Smrg}
46114c0a534Smrg
46214c0a534Smrg
463bf2eeab3Smrgstatic void
464bf2eeab3SmrgConnectClientToSM(WinInfo *winInfo)
46514c0a534Smrg{
46614c0a534Smrg    char errorMsg[256];
46714c0a534Smrg    unsigned long mask;
46814c0a534Smrg    SmcCallbacks callbacks;
46914c0a534Smrg    IceConn ice_conn;
47014c0a534Smrg    char *prevId;
47114c0a534Smrg
47214c0a534Smrg    mask = SmcSaveYourselfProcMask | SmcDieProcMask |
47314c0a534Smrg	SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
47414c0a534Smrg
47514c0a534Smrg    callbacks.save_yourself.callback = SaveYourselfCB;
47614c0a534Smrg    callbacks.save_yourself.client_data = (SmPointer) winInfo;
47714c0a534Smrg
47814c0a534Smrg    callbacks.die.callback = DieCB;
47914c0a534Smrg    callbacks.die.client_data = (SmPointer) winInfo;
48014c0a534Smrg
48114c0a534Smrg    callbacks.save_complete.callback = SaveCompleteCB;
48214c0a534Smrg    callbacks.save_complete.client_data = (SmPointer) winInfo;
48314c0a534Smrg
48414c0a534Smrg    callbacks.shutdown_cancelled.callback = ShutdownCancelledCB;
48514c0a534Smrg    callbacks.shutdown_cancelled.client_data = (SmPointer) winInfo;
48614c0a534Smrg
48714c0a534Smrg    prevId = LookupClientID (winInfo);
48814c0a534Smrg
48914c0a534Smrg    /*
49014c0a534Smrg     * In case a protocol error occurs when opening the connection,
49114c0a534Smrg     * (e.g. an authentication error), we set a null error handler
49214c0a534Smrg     * before the open, then restore the default handler after the open.
49314c0a534Smrg     */
49414c0a534Smrg
49514c0a534Smrg    IceSetErrorHandler (NullIceErrorHandler);
49614c0a534Smrg
49714c0a534Smrg    winInfo->smc_conn = SmcOpenConnection (
49814c0a534Smrg	NULL, 			/* use SESSION_MANAGER env */
49914c0a534Smrg	(SmPointer) winInfo,	/* force a new connection */
50014c0a534Smrg	SmProtoMajor,
50114c0a534Smrg	SmProtoMinor,
50214c0a534Smrg	mask,
50314c0a534Smrg	&callbacks,
50414c0a534Smrg	prevId,
50514c0a534Smrg	&winInfo->client_id,
50614c0a534Smrg	256, errorMsg);
50714c0a534Smrg
50814c0a534Smrg    IceSetErrorHandler (NULL);
50914c0a534Smrg
51014c0a534Smrg    if (winInfo->smc_conn == NULL)
51114c0a534Smrg	return;
51214c0a534Smrg
51314c0a534Smrg    ice_conn = SmcGetIceConnection (winInfo->smc_conn);
51414c0a534Smrg
51514c0a534Smrg    winInfo->input_id = XtAppAddInput (
51614c0a534Smrg	    appContext,
51714c0a534Smrg	    IceConnectionNumber (ice_conn),
51814c0a534Smrg            (XtPointer) XtInputReadMask,
51914c0a534Smrg	    ProcessIceMsgProc,
52014c0a534Smrg	    (XtPointer) ice_conn);
52114c0a534Smrg
52214c0a534Smrg    if (debug)
52314c0a534Smrg    {
52414c0a534Smrg	printf ("Connected to SM, window = 0x%x\n",
52514c0a534Smrg		(unsigned int)winInfo->window);
52614c0a534Smrg	printf ("\n");
52714c0a534Smrg    }
52814c0a534Smrg
52914c0a534Smrg    proxy_count++;
53014c0a534Smrg}
53114c0a534Smrg
53214c0a534Smrg
53314c0a534Smrg
534bf2eeab3Smrgstatic int
535bf2eeab3SmrgMyErrorHandler(Display *display, XErrorEvent *event)
53614c0a534Smrg{
53714c0a534Smrg    caught_error = 1;
53814c0a534Smrg    return 0;
53914c0a534Smrg}
54014c0a534Smrg
54114c0a534Smrg
54214c0a534Smrg
543bf2eeab3Smrgstatic Bool
544bf2eeab3SmrgLookupWindow(Window window, WinInfo **ptr_ret, WinInfo **prev_ptr_ret)
54514c0a534Smrg{
54614c0a534Smrg    WinInfo *ptr, *prev;
54714c0a534Smrg
54814c0a534Smrg    ptr = win_head;
54914c0a534Smrg    prev = NULL;
55014c0a534Smrg
55114c0a534Smrg    while (ptr)
55214c0a534Smrg    {
55314c0a534Smrg	if (ptr->window == window)
55414c0a534Smrg	    break;
55514c0a534Smrg	else
55614c0a534Smrg	{
55714c0a534Smrg	    prev = ptr;
55814c0a534Smrg	    ptr = ptr->next;
55914c0a534Smrg	}
56014c0a534Smrg    }
56114c0a534Smrg
56214c0a534Smrg    if (ptr)
56314c0a534Smrg    {
56414c0a534Smrg	if (ptr_ret)
56514c0a534Smrg	    *ptr_ret = ptr;
56614c0a534Smrg	if (prev_ptr_ret)
56714c0a534Smrg	    *prev_ptr_ret = prev;
56814c0a534Smrg	return (1);
56914c0a534Smrg    }
57014c0a534Smrg    else
57114c0a534Smrg	return (0);
57214c0a534Smrg}
57314c0a534Smrg
57414c0a534Smrg
57514c0a534Smrg
576bf2eeab3Smrgstatic WinInfo *
577bf2eeab3SmrgAddNewWindow(Window window)
57814c0a534Smrg{
57914c0a534Smrg    WinInfo *newptr;
58014c0a534Smrg
58114c0a534Smrg    if (LookupWindow (window, NULL, NULL))
58214c0a534Smrg	return (NULL);
58314c0a534Smrg
584bdc460c5Smrg    newptr = malloc (sizeof (WinInfo));
58514c0a534Smrg
58614c0a534Smrg    if (newptr == NULL)
58714c0a534Smrg	return (NULL);
58814c0a534Smrg
58914c0a534Smrg    newptr->next = win_head;
59014c0a534Smrg    win_head = newptr;
59114c0a534Smrg
59214c0a534Smrg    newptr->window = window;
59314c0a534Smrg    newptr->smc_conn = NULL;
59414c0a534Smrg    newptr->tested_for_sm_client_id = 0;
59514c0a534Smrg    newptr->client_id = NULL;
59614c0a534Smrg    newptr->wm_command = NULL;
59714c0a534Smrg    newptr->wm_command_count = 0;
59814c0a534Smrg    newptr->class.res_name = NULL;
59914c0a534Smrg    newptr->class.res_class = NULL;
60014c0a534Smrg    newptr->wm_name = NULL;
60114c0a534Smrg    newptr->wm_client_machine.value = NULL;
60214c0a534Smrg    newptr->wm_client_machine.nitems = 0;
60314c0a534Smrg    newptr->has_save_yourself = 0;
60414c0a534Smrg    newptr->waiting_for_update = 0;
60514c0a534Smrg    newptr->got_first_save_yourself = 0;
60614c0a534Smrg
60714c0a534Smrg    return (newptr);
60814c0a534Smrg}
60914c0a534Smrg
61014c0a534Smrg
61114c0a534Smrg
612bf2eeab3Smrgstatic void
613bf2eeab3SmrgRemoveWindow(WinInfo *winptr)
61414c0a534Smrg{
61514c0a534Smrg    WinInfo *ptr, *prev;
61614c0a534Smrg
61714c0a534Smrg    if (LookupWindow (winptr->window, &ptr, &prev))
61814c0a534Smrg    {
61914c0a534Smrg	if (prev == NULL)
62014c0a534Smrg	    win_head = ptr->next;
62114c0a534Smrg	else
62214c0a534Smrg	    prev->next = ptr->next;
62314c0a534Smrg
62414c0a534Smrg	if (ptr->client_id)
62514c0a534Smrg	    free (ptr->client_id);
62614c0a534Smrg
62714c0a534Smrg	if (ptr->wm_command)
62814c0a534Smrg	    XFreeStringList (ptr->wm_command);
62914c0a534Smrg
63014c0a534Smrg	if (ptr->wm_name)
63114c0a534Smrg	    XFree (ptr->wm_name);
63214c0a534Smrg
63314c0a534Smrg	if (ptr->wm_client_machine.value)
63414c0a534Smrg	    XFree (ptr->wm_client_machine.value);
63514c0a534Smrg
63614c0a534Smrg	if (ptr->class.res_name)
63714c0a534Smrg	    XFree (ptr->class.res_name);
63814c0a534Smrg
63914c0a534Smrg	if (ptr->class.res_class)
64014c0a534Smrg	    XFree (ptr->class.res_class);
64114c0a534Smrg
64214c0a534Smrg	free ((char *) ptr);
64314c0a534Smrg    }
64414c0a534Smrg}
64514c0a534Smrg
64614c0a534Smrg
64714c0a534Smrg
648bf2eeab3Smrgstatic void
649bf2eeab3SmrgGot_WM_STATE(WinInfo *winptr)
65014c0a534Smrg{
65114c0a534Smrg    WinInfo *leader_winptr;
65214c0a534Smrg
65314c0a534Smrg    /*
65414c0a534Smrg     * If we already got WM_STATE and tested for SM_CLIENT_ID, we
65514c0a534Smrg     * shouldn't do it again.
65614c0a534Smrg     */
65714c0a534Smrg
65814c0a534Smrg    if (winptr->tested_for_sm_client_id)
65914c0a534Smrg    {
66014c0a534Smrg	return;
66114c0a534Smrg    }
66214c0a534Smrg
66314c0a534Smrg
66414c0a534Smrg    /*
66514c0a534Smrg     * Set a null error handler, in case this window goes away
66614c0a534Smrg     * behind our back.
66714c0a534Smrg     */
66814c0a534Smrg
66914c0a534Smrg    caught_error = 0;
67014c0a534Smrg    XSetErrorHandler (MyErrorHandler);
67114c0a534Smrg
67214c0a534Smrg
67314c0a534Smrg    /*
67414c0a534Smrg     * Get the client leader window.
67514c0a534Smrg     */
67614c0a534Smrg
67714c0a534Smrg    leader_winptr = GetClientLeader (winptr);
67814c0a534Smrg
67914c0a534Smrg    if (caught_error)
68014c0a534Smrg    {
68114c0a534Smrg	caught_error = 0;
68214c0a534Smrg	RemoveWindow (winptr);
68314c0a534Smrg	XSetErrorHandler (NULL);
68414c0a534Smrg	return;
68514c0a534Smrg    }
68614c0a534Smrg
68714c0a534Smrg
68814c0a534Smrg    /*
68914c0a534Smrg     * If we already checked for SM_CLIENT_ID on the client leader
69014c0a534Smrg     * window, don't do it again.
69114c0a534Smrg     */
69214c0a534Smrg
69314c0a534Smrg    if (!leader_winptr || leader_winptr->tested_for_sm_client_id)
69414c0a534Smrg    {
69514c0a534Smrg	caught_error = 0;
69614c0a534Smrg	XSetErrorHandler (NULL);
69714c0a534Smrg	return;
69814c0a534Smrg    }
69914c0a534Smrg
70014c0a534Smrg    leader_winptr->tested_for_sm_client_id = 1;
70114c0a534Smrg
70214c0a534Smrg    if (!HasXSMPsupport (leader_winptr->window))
70314c0a534Smrg    {
70414c0a534Smrg	XFetchName (disp, leader_winptr->window, &leader_winptr->wm_name);
70514c0a534Smrg
70614c0a534Smrg	XGetCommand (disp, leader_winptr->window,
70714c0a534Smrg	    &leader_winptr->wm_command,
70814c0a534Smrg	    &leader_winptr->wm_command_count);
70914c0a534Smrg
71014c0a534Smrg	XGetClassHint (disp, leader_winptr->window, &leader_winptr->class);
71114c0a534Smrg
71214c0a534Smrg	XGetWMClientMachine (disp, leader_winptr->window,
71314c0a534Smrg	    &leader_winptr->wm_client_machine);
71414c0a534Smrg
71514c0a534Smrg	if (leader_winptr->wm_name != NULL &&
71614c0a534Smrg	    leader_winptr->wm_command != NULL &&
71714c0a534Smrg	    leader_winptr->wm_command_count > 0 &&
71814c0a534Smrg	    leader_winptr->class.res_name != NULL &&
71914c0a534Smrg	    leader_winptr->class.res_class != NULL &&
72014c0a534Smrg	    leader_winptr->wm_client_machine.value != NULL &&
72114c0a534Smrg	    leader_winptr->wm_client_machine.nitems != 0)
72214c0a534Smrg	{
72314c0a534Smrg	    leader_winptr->has_save_yourself =
72414c0a534Smrg		HasSaveYourself (leader_winptr->window);
72514c0a534Smrg
72614c0a534Smrg	    ConnectClientToSM (leader_winptr);
72714c0a534Smrg	}
72814c0a534Smrg    }
72914c0a534Smrg
73014c0a534Smrg    XSync (disp, 0);
73114c0a534Smrg    XSetErrorHandler (NULL);
73214c0a534Smrg
73314c0a534Smrg    if (caught_error)
73414c0a534Smrg    {
73514c0a534Smrg	caught_error = 0;
73614c0a534Smrg	RemoveWindow (leader_winptr);
73714c0a534Smrg    }
73814c0a534Smrg}
73914c0a534Smrg
74014c0a534Smrg
74114c0a534Smrg
742bf2eeab3Smrgstatic void
743bf2eeab3SmrgHandleCreate(XCreateWindowEvent *event)
74414c0a534Smrg{
74514c0a534Smrg    Atom actual_type;
74614c0a534Smrg    int actual_format;
74714c0a534Smrg    unsigned long nitems, bytesafter;
74814c0a534Smrg    unsigned long *datap = NULL;
74914c0a534Smrg    WinInfo *winptr;
75014c0a534Smrg    Bool got_wm_state = 0;
75114c0a534Smrg
75214c0a534Smrg    /*
75314c0a534Smrg     * We are waiting for all proxy connections to close so we can die.
75414c0a534Smrg     * Don't handle new connections.
75514c0a534Smrg     */
75614c0a534Smrg
75714c0a534Smrg    if (ok_to_die)
75814c0a534Smrg	return;
75914c0a534Smrg
76014c0a534Smrg
76114c0a534Smrg    /*
76214c0a534Smrg     * Add the new window
76314c0a534Smrg     */
76414c0a534Smrg
76514c0a534Smrg    if ((winptr = AddNewWindow (event->window)) == NULL)
76614c0a534Smrg	return;
76714c0a534Smrg
76814c0a534Smrg
76914c0a534Smrg    /*
77014c0a534Smrg     * Right after the window was created, it might have been destroyed,
77114c0a534Smrg     * so the following Xlib calls might fail.  Need to catch the error
77214c0a534Smrg     * by installing an error handler.
77314c0a534Smrg     */
77414c0a534Smrg
77514c0a534Smrg    caught_error = 0;
77614c0a534Smrg    XSetErrorHandler (MyErrorHandler);
77714c0a534Smrg
77814c0a534Smrg
77914c0a534Smrg    /*
78014c0a534Smrg     * Select for Property Notify on the window so we can determine
78114c0a534Smrg     * when WM_STATE is defined.  To avoid a race condition, we must
78214c0a534Smrg     * do this _before_ we check for WM_STATE right now.
78314c0a534Smrg     *
78414c0a534Smrg     * Select for Substructure Notify so we can determine when the
78514c0a534Smrg     * window is destroyed.
78614c0a534Smrg     */
78714c0a534Smrg
78814c0a534Smrg    XSelectInput (disp, event->window,
78914c0a534Smrg	SubstructureNotifyMask | PropertyChangeMask);
79014c0a534Smrg
79114c0a534Smrg
79214c0a534Smrg    /*
79314c0a534Smrg     * WM_STATE may already be there.  Check now.
79414c0a534Smrg     */
79514c0a534Smrg
79614c0a534Smrg    if (XGetWindowProperty (disp, event->window, wmStateAtom,
79714c0a534Smrg	0L, 2L, False, AnyPropertyType,
79814c0a534Smrg	&actual_type, &actual_format, &nitems, &bytesafter,
79914c0a534Smrg	(unsigned char **) &datap) == Success && datap)
80014c0a534Smrg    {
80114c0a534Smrg	if (nitems > 0)
80214c0a534Smrg	    got_wm_state = 1;
80314c0a534Smrg
80414c0a534Smrg	if (datap)
80514c0a534Smrg	    XFree ((char *) datap);
80614c0a534Smrg    }
80714c0a534Smrg
80814c0a534Smrg    XSync (disp, 0);
80914c0a534Smrg    XSetErrorHandler (NULL);
81014c0a534Smrg
81114c0a534Smrg    if (caught_error)
81214c0a534Smrg    {
81314c0a534Smrg	caught_error = 0;
81414c0a534Smrg	RemoveWindow (winptr);
81514c0a534Smrg    }
81614c0a534Smrg    else if (got_wm_state)
81714c0a534Smrg    {
81814c0a534Smrg	Got_WM_STATE (winptr);
81914c0a534Smrg    }
82014c0a534Smrg}
82114c0a534Smrg
82214c0a534Smrg
82314c0a534Smrg
824bf2eeab3Smrgstatic void
825bf2eeab3SmrgHandleDestroy(XDestroyWindowEvent *event)
82614c0a534Smrg{
82714c0a534Smrg    WinInfo *winptr;
82814c0a534Smrg
82914c0a534Smrg    if (LookupWindow (event->window, &winptr, NULL))
83014c0a534Smrg    {
83114c0a534Smrg	if (winptr->smc_conn)
83214c0a534Smrg	{
83314c0a534Smrg	    SmcCloseConnection (winptr->smc_conn, 0, NULL);
83414c0a534Smrg	    XtRemoveInput (winptr->input_id);
83514c0a534Smrg	    proxy_count--;
83614c0a534Smrg	}
83714c0a534Smrg
83814c0a534Smrg	if (debug)
83914c0a534Smrg	{
84014c0a534Smrg	    printf ("Removed window (window = 0x%x)\n",
84114c0a534Smrg		    (unsigned int)winptr->window);
84214c0a534Smrg	    printf ("\n");
84314c0a534Smrg	}
84414c0a534Smrg
84514c0a534Smrg	RemoveWindow (winptr);
84614c0a534Smrg    }
84714c0a534Smrg}
84814c0a534Smrg
84914c0a534Smrg
85014c0a534Smrg
851bf2eeab3Smrgstatic void
852bf2eeab3SmrgHandleUpdate(XPropertyEvent *event)
85314c0a534Smrg{
85414c0a534Smrg    Window window = event->window;
85514c0a534Smrg    WinInfo *winptr;
85614c0a534Smrg
85714c0a534Smrg    if (!LookupWindow (window, &winptr, NULL))
85814c0a534Smrg	return;
85914c0a534Smrg
86014c0a534Smrg    if (event->atom == wmStateAtom)
86114c0a534Smrg    {
86214c0a534Smrg	Got_WM_STATE (winptr);
86314c0a534Smrg    }
86414c0a534Smrg    else if (event->atom == XA_WM_COMMAND && winptr->waiting_for_update)
86514c0a534Smrg    {
86614c0a534Smrg	/* Finish off the Save Yourself */
86714c0a534Smrg
86814c0a534Smrg	if (winptr->wm_command)
86914c0a534Smrg	{
87014c0a534Smrg	    XFreeStringList (winptr->wm_command);
87114c0a534Smrg	    winptr->wm_command = NULL;
87214c0a534Smrg	    winptr->wm_command_count = 0;
87314c0a534Smrg	}
87414c0a534Smrg
87514c0a534Smrg	XGetCommand (disp, window,
87614c0a534Smrg	    &winptr->wm_command,
87714c0a534Smrg	    &winptr->wm_command_count);
87814c0a534Smrg
87914c0a534Smrg	winptr->waiting_for_update = 0;
88014c0a534Smrg	FinishSaveYourself (winptr, True);
88114c0a534Smrg    }
88214c0a534Smrg}
88314c0a534Smrg
88414c0a534Smrg
88514c0a534Smrg
886bf2eeab3Smrgstatic void
887bf2eeab3SmrgProxySaveYourselfPhase2CB(SmcConn smcConn, SmPointer clientData)
88814c0a534Smrg{
88914c0a534Smrg    char *filename;
89014c0a534Smrg    Bool success = True;
89114c0a534Smrg    SmProp prop1, prop2, prop3, *props[3];
89214c0a534Smrg    SmPropValue prop1val, prop2val, prop3val;
89324047306Smrg    char *discardCommand;
894bdc460c5Smrg    int numVals;
89514c0a534Smrg    static int first_time = 1;
89614c0a534Smrg
89714c0a534Smrg    if (first_time)
89814c0a534Smrg    {
89914c0a534Smrg	char userId[20];
90014c0a534Smrg	char hint = SmRestartIfRunning;
90114c0a534Smrg
90214c0a534Smrg	prop1.name = SmProgram;
90314c0a534Smrg	prop1.type = SmARRAY8;
90414c0a534Smrg	prop1.num_vals = 1;
90514c0a534Smrg	prop1.vals = &prop1val;
90614c0a534Smrg	prop1val.value = Argv[0];
90714c0a534Smrg	prop1val.length = strlen (Argv[0]);
90814c0a534Smrg
90924047306Smrg	snprintf (userId, sizeof(userId), "%ld", (long)getuid());
91014c0a534Smrg	prop2.name = SmUserID;
91114c0a534Smrg	prop2.type = SmARRAY8;
91214c0a534Smrg	prop2.num_vals = 1;
91314c0a534Smrg	prop2.vals = &prop2val;
91414c0a534Smrg	prop2val.value = (SmPointer) userId;
91514c0a534Smrg	prop2val.length = strlen (userId);
91614c0a534Smrg
91714c0a534Smrg	prop3.name = SmRestartStyleHint;
91814c0a534Smrg	prop3.type = SmCARD8;
91914c0a534Smrg	prop3.num_vals = 1;
92014c0a534Smrg	prop3.vals = &prop3val;
92114c0a534Smrg	prop3val.value = (SmPointer) &hint;
92214c0a534Smrg	prop3val.length = 1;
92314c0a534Smrg
92414c0a534Smrg	props[0] = &prop1;
92514c0a534Smrg	props[1] = &prop2;
92614c0a534Smrg	props[2] = &prop3;
92714c0a534Smrg
92814c0a534Smrg	SmcSetProperties (smcConn, 3, props);
92914c0a534Smrg
93014c0a534Smrg	first_time = 0;
93114c0a534Smrg    }
93214c0a534Smrg
93314c0a534Smrg    if ((filename = WriteProxyFile ()) == NULL)
93414c0a534Smrg    {
93514c0a534Smrg	success = False;
93614c0a534Smrg	goto finishUp;
93714c0a534Smrg    }
93814c0a534Smrg
93914c0a534Smrg    prop1.name = SmRestartCommand;
94014c0a534Smrg    prop1.type = SmLISTofARRAY8;
94114c0a534Smrg
942bdc460c5Smrg    prop1.vals = calloc ((Argc + 4), sizeof (SmPropValue));
94314c0a534Smrg
94414c0a534Smrg    if (!prop1.vals)
94514c0a534Smrg    {
94614c0a534Smrg	success = False;
94714c0a534Smrg	goto finishUp;
94814c0a534Smrg    }
94914c0a534Smrg
95014c0a534Smrg    numVals = 0;
95114c0a534Smrg
952bdc460c5Smrg    for (int i = 0; i < Argc; i++)
95314c0a534Smrg    {
95414c0a534Smrg	if (strcmp (Argv[i], "-clientId") == 0 ||
95514c0a534Smrg	    strcmp (Argv[i], "-restore") == 0)
95614c0a534Smrg	{
95714c0a534Smrg	    i++;
95814c0a534Smrg	}
95914c0a534Smrg	else
96014c0a534Smrg	{
96114c0a534Smrg	    prop1.vals[numVals].value = (SmPointer) Argv[i];
96214c0a534Smrg	    prop1.vals[numVals++].length = strlen (Argv[i]);
96314c0a534Smrg	}
96414c0a534Smrg    }
96514c0a534Smrg
96614c0a534Smrg    prop1.vals[numVals].value = (SmPointer) "-clientId";
96714c0a534Smrg    prop1.vals[numVals++].length = 9;
96814c0a534Smrg
96914c0a534Smrg    prop1.vals[numVals].value = (SmPointer) proxy_clientId;
97014c0a534Smrg    prop1.vals[numVals++].length = strlen (proxy_clientId);
97114c0a534Smrg
97214c0a534Smrg    prop1.vals[numVals].value = (SmPointer) "-restore";
97314c0a534Smrg    prop1.vals[numVals++].length = 8;
97414c0a534Smrg
97514c0a534Smrg    prop1.vals[numVals].value = (SmPointer) filename;
97614c0a534Smrg    prop1.vals[numVals++].length = strlen (filename);
97714c0a534Smrg
97814c0a534Smrg    prop1.num_vals = numVals;
97914c0a534Smrg
98014c0a534Smrg
98124047306Smrg    if (asprintf (&discardCommand, "rm %s", filename) == -1) {
98224047306Smrg	success = False;
98324047306Smrg	goto finishUp;
98424047306Smrg    }
98514c0a534Smrg    prop2.name = SmDiscardCommand;
98614c0a534Smrg    prop2.type = SmARRAY8;
98714c0a534Smrg    prop2.num_vals = 1;
98814c0a534Smrg    prop2.vals = &prop2val;
98914c0a534Smrg    prop2val.value = (SmPointer) discardCommand;
99014c0a534Smrg    prop2val.length = strlen (discardCommand);
99114c0a534Smrg
99214c0a534Smrg    props[0] = &prop1;
99314c0a534Smrg    props[1] = &prop2;
99414c0a534Smrg
99514c0a534Smrg    SmcSetProperties (smcConn, 2, props);
996bdc460c5Smrg    free (prop1.vals);
99724047306Smrg    free (discardCommand);
99814c0a534Smrg
99914c0a534Smrg finishUp:
100014c0a534Smrg
100114c0a534Smrg    SmcSaveYourselfDone (smcConn, success);
100214c0a534Smrg    sent_save_done = 1;
100314c0a534Smrg
100414c0a534Smrg    if (filename)
100514c0a534Smrg	free (filename);
100614c0a534Smrg}
100714c0a534Smrg
100814c0a534Smrg
100914c0a534Smrg
1010bf2eeab3Smrgstatic void
1011bf2eeab3SmrgProxySaveYourselfCB(SmcConn smcConn, SmPointer clientData, int saveType,
1012bf2eeab3Smrg		    Bool shutdown, int interactStyle, Bool fast)
101314c0a534Smrg{
101414c0a534Smrg    /*
101514c0a534Smrg     * We want the proxy to respond to the Save Yourself after all
101614c0a534Smrg     * the regular XSMP clients have finished with the save (and possibly
101714c0a534Smrg     * interacted with the user).
101814c0a534Smrg     */
101914c0a534Smrg
102014c0a534Smrg    if (!SmcRequestSaveYourselfPhase2 (smcConn,
102114c0a534Smrg	ProxySaveYourselfPhase2CB, NULL))
102214c0a534Smrg    {
102314c0a534Smrg	SmcSaveYourselfDone (smcConn, False);
102414c0a534Smrg	sent_save_done = 1;
102514c0a534Smrg    }
102614c0a534Smrg    else
102714c0a534Smrg	sent_save_done = 0;
102814c0a534Smrg}
102914c0a534Smrg
103014c0a534Smrg
103114c0a534Smrg
1032bf2eeab3Smrgstatic void
1033bf2eeab3SmrgProxyDieCB(SmcConn smcConn, SmPointer clientData)
103414c0a534Smrg{
103514c0a534Smrg    SmcCloseConnection (proxy_smcConn, 0, NULL);
103614c0a534Smrg    XtRemoveInput (proxy_iceInputId);
103714c0a534Smrg
103814c0a534Smrg    if (die_count == proxy_count)
103914c0a534Smrg	exit (0);
104014c0a534Smrg    else
104114c0a534Smrg	ok_to_die = 1;
104214c0a534Smrg}
104314c0a534Smrg
104414c0a534Smrg
104514c0a534Smrg
1046bf2eeab3Smrgstatic void
1047bf2eeab3SmrgProxySaveCompleteCB(SmcConn smcConn, SmPointer clientData)
104814c0a534Smrg{
104914c0a534Smrg    ;
105014c0a534Smrg}
105114c0a534Smrg
105214c0a534Smrg
105314c0a534Smrg
1054bf2eeab3Smrgstatic void
1055bf2eeab3SmrgProxyShutdownCancelledCB(SmcConn smcConn, SmPointer clientData)
105614c0a534Smrg{
105714c0a534Smrg    if (!sent_save_done)
105814c0a534Smrg    {
105914c0a534Smrg	SmcSaveYourselfDone (smcConn, False);
106014c0a534Smrg	sent_save_done = 1;
106114c0a534Smrg    }
106214c0a534Smrg}
106314c0a534Smrg
106414c0a534Smrg
106514c0a534Smrg
1066bf2eeab3Smrgstatic Status
1067bf2eeab3SmrgConnectProxyToSM(char *previous_id)
106814c0a534Smrg{
106914c0a534Smrg    char errorMsg[256];
107014c0a534Smrg    unsigned long mask;
107114c0a534Smrg    SmcCallbacks callbacks;
107214c0a534Smrg    IceConn iceConn;
107314c0a534Smrg
107414c0a534Smrg    mask = SmcSaveYourselfProcMask | SmcDieProcMask |
107514c0a534Smrg	SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
107614c0a534Smrg
107714c0a534Smrg    callbacks.save_yourself.callback = ProxySaveYourselfCB;
107814c0a534Smrg    callbacks.save_yourself.client_data = (SmPointer) NULL;
107914c0a534Smrg
108014c0a534Smrg    callbacks.die.callback = ProxyDieCB;
108114c0a534Smrg    callbacks.die.client_data = (SmPointer) NULL;
108214c0a534Smrg
108314c0a534Smrg    callbacks.save_complete.callback = ProxySaveCompleteCB;
108414c0a534Smrg    callbacks.save_complete.client_data = (SmPointer) NULL;
108514c0a534Smrg
108614c0a534Smrg    callbacks.shutdown_cancelled.callback = ProxyShutdownCancelledCB;
108714c0a534Smrg    callbacks.shutdown_cancelled.client_data = (SmPointer) NULL;
108814c0a534Smrg
108914c0a534Smrg    proxy_smcConn = SmcOpenConnection (
109014c0a534Smrg	NULL, 			/* use SESSION_MANAGER env */
109114c0a534Smrg	(SmPointer) appContext,
109214c0a534Smrg	SmProtoMajor,
109314c0a534Smrg	SmProtoMinor,
109414c0a534Smrg	mask,
109514c0a534Smrg	&callbacks,
109614c0a534Smrg	previous_id,
109714c0a534Smrg	&proxy_clientId,
109814c0a534Smrg	256, errorMsg);
109914c0a534Smrg
110014c0a534Smrg    if (proxy_smcConn == NULL)
110114c0a534Smrg	return (0);
110214c0a534Smrg
110314c0a534Smrg    iceConn = SmcGetIceConnection (proxy_smcConn);
110414c0a534Smrg
110514c0a534Smrg    proxy_iceInputId = XtAppAddInput (
110614c0a534Smrg	    appContext,
110714c0a534Smrg	    IceConnectionNumber (iceConn),
110814c0a534Smrg            (XtPointer) XtInputReadMask,
110914c0a534Smrg	    ProcessIceMsgProc,
111014c0a534Smrg	    (XtPointer) iceConn);
111114c0a534Smrg
111214c0a534Smrg    return (1);
111314c0a534Smrg}
111414c0a534Smrg
111514c0a534Smrg
111614c0a534Smrg
1117bf2eeab3Smrgstatic void
1118bf2eeab3SmrgCheckForExistingWindows(Window root)
111914c0a534Smrg{
1120bdc460c5Smrg    Window dontCare1, dontCare2, *children;
112114c0a534Smrg    unsigned int nchildren, i;
112214c0a534Smrg
112314c0a534Smrg    /*
112414c0a534Smrg     * We query the root tree for all windows created thus far.
112514c0a534Smrg     * Note that at any moment after XQueryTree is called, a
112614c0a534Smrg     * window may be deleted.  So we must take extra care to make
112714c0a534Smrg     * sure a window really exists.
112814c0a534Smrg     */
112914c0a534Smrg
113014c0a534Smrg    XQueryTree (disp, root, &dontCare1, &dontCare2, &children, &nchildren);
113114c0a534Smrg
113214c0a534Smrg    for (i = 0; i < nchildren; i++)
113314c0a534Smrg    {
1134bdc460c5Smrg	Window client_window;
1135bdc460c5Smrg	XCreateWindowEvent event = { .window = children[i] };
113614c0a534Smrg
113714c0a534Smrg	HandleCreate (&event);
113814c0a534Smrg
113914c0a534Smrg	caught_error = 0;
114014c0a534Smrg	XSetErrorHandler (MyErrorHandler);
114114c0a534Smrg
114214c0a534Smrg	client_window = XmuClientWindow (disp, children[i]);
114314c0a534Smrg
114414c0a534Smrg	XSetErrorHandler (NULL);
114514c0a534Smrg
114614c0a534Smrg	if (!caught_error && client_window != children[i])
114714c0a534Smrg	{
114814c0a534Smrg	    event.window = client_window;
114914c0a534Smrg	    HandleCreate (&event);
115014c0a534Smrg	}
115114c0a534Smrg    }
115214c0a534Smrg}
115314c0a534Smrg
115414c0a534Smrg
115514c0a534Smrg
115614c0a534Smrgint
115714c0a534Smrgmain (int argc, char *argv[])
115814c0a534Smrg{
115914c0a534Smrg    char *restore_filename = NULL;
116014c0a534Smrg    char *client_id = NULL;
116114c0a534Smrg    int i, zero = 0;
11627015785aSmrg    const char * const usage =
11637015785aSmrg        "usage:  %s [-clientId id] [-restore file] [-debug] [-version]\n";
116414c0a534Smrg
116514c0a534Smrg    Argc = argc;
116614c0a534Smrg    Argv = argv;
116714c0a534Smrg
116814c0a534Smrg    for (i = 1; i < argc; i++)
116914c0a534Smrg    {
117014c0a534Smrg	if (argv[i][0] == '-')
117114c0a534Smrg	{
117214c0a534Smrg	    switch (argv[i][1])
117314c0a534Smrg	    {
117414c0a534Smrg	      case 'd':				/* -debug */
117514c0a534Smrg		debug = 1;
117614c0a534Smrg		continue;
117714c0a534Smrg
117814c0a534Smrg	      case 'c':				/* -clientId */
117924047306Smrg		if (++i >= argc) {
118024047306Smrg		    fprintf (stderr, "%s: -clientId requires an argument\n",
118124047306Smrg			     argv[0]);
118224047306Smrg		    goto usage;
118324047306Smrg		}
118414c0a534Smrg		client_id = argv[i];
118514c0a534Smrg		continue;
118614c0a534Smrg
118714c0a534Smrg	      case 'r':				/* -restore */
118824047306Smrg		if (++i >= argc) {
118924047306Smrg		    fprintf (stderr, "%s: -restore requires an argument\n",
119024047306Smrg			     argv[0]);
119124047306Smrg		    goto usage;
119224047306Smrg		}
119314c0a534Smrg		restore_filename = argv[i];
119414c0a534Smrg		continue;
119524047306Smrg
119624047306Smrg	      case 'v':
119724047306Smrg		puts (PACKAGE_STRING);
119824047306Smrg		exit (0);
11997015785aSmrg
12007015785aSmrg            case '-':
12017015785aSmrg                if (strcmp(argv[i], "--help") == 0) {
12027015785aSmrg                    fprintf (stdout, usage, argv[0]);
12037015785aSmrg                    exit (0);
12047015785aSmrg                }
12057015785aSmrg                if (strcmp(argv[i], "--version") == 0) {
12067015785aSmrg                    puts (PACKAGE_STRING);
12077015785aSmrg                    exit (0);
12087015785aSmrg                }
12097015785aSmrg                break;
121014c0a534Smrg	    }
121114c0a534Smrg	}
121214c0a534Smrg
121324047306Smrg	fprintf (stderr, "%s: unrecognized argument: %s\n", argv[0], argv[i]);
121424047306Smrg
121514c0a534Smrg    usage:
121614c0a534Smrg
12177015785aSmrg	fprintf (stderr, usage, argv[0]);
121814c0a534Smrg	exit (1);
121914c0a534Smrg    }
122014c0a534Smrg
122114c0a534Smrg
122214c0a534Smrg    XtToolkitInitialize ();
122314c0a534Smrg    appContext = XtCreateApplicationContext ();
122414c0a534Smrg
122514c0a534Smrg    if (!(disp = XtOpenDisplay (appContext, NULL, "SM-PROXY", "SM-PROXY",
122614c0a534Smrg	NULL, 0, &zero, NULL)))
122714c0a534Smrg    {
122814c0a534Smrg	fprintf (stderr, "smproxy: unable to open display\n");
122914c0a534Smrg	exit (1);
123014c0a534Smrg    }
123114c0a534Smrg
123214c0a534Smrg    if (restore_filename)
123314c0a534Smrg	ReadProxyFile (restore_filename);
123414c0a534Smrg
123514c0a534Smrg    if (!ConnectProxyToSM (client_id))
123614c0a534Smrg    {
123714c0a534Smrg	fprintf (stderr, "smproxy: unable to connect to session manager\n");
123814c0a534Smrg	exit (1);
123914c0a534Smrg    }
124014c0a534Smrg
124114c0a534Smrg    wmProtocolsAtom = XInternAtom (disp, "WM_PROTOCOLS", False);
124214c0a534Smrg    wmSaveYourselfAtom = XInternAtom (disp, "WM_SAVE_YOURSELF", False);
124314c0a534Smrg    wmStateAtom = XInternAtom (disp, "WM_STATE", False);
124414c0a534Smrg    smClientIdAtom = XInternAtom (disp, "SM_CLIENT_ID", False);
124514c0a534Smrg    wmClientLeaderAtom = XInternAtom (disp, "WM_CLIENT_LEADER", False);
124614c0a534Smrg
124714c0a534Smrg    for (i = 0; i < ScreenCount (disp); i++)
124814c0a534Smrg    {
124914c0a534Smrg	Window root = RootWindow (disp, i);
125014c0a534Smrg	XSelectInput (disp, root, SubstructureNotifyMask | PropertyChangeMask);
125114c0a534Smrg	CheckForExistingWindows (root);
125214c0a534Smrg    }
125314c0a534Smrg
125414c0a534Smrg    while (1)
125514c0a534Smrg    {
125614c0a534Smrg	XEvent event;
125714c0a534Smrg
125814c0a534Smrg	XtAppNextEvent (appContext, &event);
125914c0a534Smrg
126014c0a534Smrg	switch (event.type)
126114c0a534Smrg	{
126214c0a534Smrg	case CreateNotify:
126314c0a534Smrg	    HandleCreate (&event.xcreatewindow);
126414c0a534Smrg	    break;
126514c0a534Smrg
126614c0a534Smrg	case DestroyNotify:
126714c0a534Smrg	    HandleDestroy (&event.xdestroywindow);
126814c0a534Smrg	    break;
126914c0a534Smrg
127014c0a534Smrg	case PropertyNotify:
127114c0a534Smrg	    HandleUpdate (&event.xproperty);
127214c0a534Smrg	    break;
127314c0a534Smrg
127414c0a534Smrg	default:
127514c0a534Smrg	    XtDispatchEvent (&event);
127614c0a534Smrg	    break;
127714c0a534Smrg	}
127814c0a534Smrg    }
127914c0a534Smrg    exit(0);
128014c0a534Smrg}
1281