smproxy.c revision 24047306
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>
3114c0a534Smrg
32bf2eeab3Smrgstatic XtAppContext appContext;
33bf2eeab3Smrgstatic Display *disp;
3414c0a534Smrg
35bf2eeab3Smrgstatic Atom wmProtocolsAtom;
36bf2eeab3Smrgstatic Atom wmSaveYourselfAtom;
37bf2eeab3Smrgstatic Atom wmStateAtom;
38bf2eeab3Smrgstatic Atom smClientIdAtom;
39bf2eeab3Smrgstatic Atom wmClientLeaderAtom;
4014c0a534Smrg
41bf2eeab3Smrgstatic Bool debug = 0;
4214c0a534Smrg
43bf2eeab3Smrgstatic SmcConn proxy_smcConn;
44bf2eeab3Smrgstatic XtInputId proxy_iceInputId;
45bf2eeab3Smrgstatic char *proxy_clientId = NULL;
4614c0a534Smrg
4714c0a534SmrgWinInfo *win_head = NULL;
4814c0a534Smrg
49bf2eeab3Smrgstatic int proxy_count = 0;
50bf2eeab3Smrgstatic int die_count = 0;
5114c0a534Smrg
52bf2eeab3Smrgstatic Bool ok_to_die = 0;
5314c0a534Smrg
54bf2eeab3Smrgstatic Bool caught_error = 0;
5514c0a534Smrg
56bf2eeab3Smrgstatic Bool sent_save_done = 0;
5714c0a534Smrg
58bf2eeab3Smrgstatic int Argc;
59bf2eeab3Smrgstatic char **Argv;
6014c0a534Smrg
61bf2eeab3Smrgstatic Bool HasSaveYourself ( Window window );
62bf2eeab3Smrgstatic Bool HasXSMPsupport ( Window window );
63bf2eeab3Smrgstatic WinInfo * GetClientLeader ( WinInfo *winptr );
64bf2eeab3Smrgstatic char * CheckFullyQuantifiedName ( char *name, int *newstring );
65bf2eeab3Smrgstatic void FinishSaveYourself ( WinInfo *winInfo, Bool has_WM_SAVEYOURSELF );
66bf2eeab3Smrgstatic void SaveYourselfCB ( SmcConn smcConn, SmPointer clientData, int saveType,
67bf2eeab3Smrg			    Bool shutdown, int interactStyle, Bool fast );
68bf2eeab3Smrgstatic void DieCB ( SmcConn smcConn, SmPointer clientData );
69bf2eeab3Smrgstatic void SaveCompleteCB ( SmcConn smcConn, SmPointer clientData );
70bf2eeab3Smrgstatic void ShutdownCancelledCB ( SmcConn smcConn, SmPointer clientData );
71bf2eeab3Smrgstatic void ProcessIceMsgProc ( XtPointer client_data, int *source, XtInputId *id );
72bf2eeab3Smrgstatic void NullIceErrorHandler ( IceConn iceConn, Bool swap,
7314c0a534Smrg			   int offendingMinorOpCode,
7414c0a534Smrg			   unsigned long offendingSequence,
7514c0a534Smrg			   int errorClass, int severity, IcePointer values );
76bf2eeab3Smrgstatic void ConnectClientToSM ( WinInfo *winInfo );
77bf2eeab3Smrgstatic int MyErrorHandler ( Display *display, XErrorEvent *event );
78bf2eeab3Smrgstatic Bool LookupWindow ( Window window, WinInfo **ptr_ret, WinInfo **prev_ptr_ret );
79bf2eeab3Smrgstatic WinInfo * AddNewWindow ( Window window );
80bf2eeab3Smrgstatic void RemoveWindow ( WinInfo *winptr );
81bf2eeab3Smrgstatic void Got_WM_STATE ( WinInfo *winptr );
82bf2eeab3Smrgstatic void HandleCreate ( XCreateWindowEvent *event );
83bf2eeab3Smrgstatic void HandleDestroy ( XDestroyWindowEvent *event );
84bf2eeab3Smrgstatic void HandleUpdate ( XPropertyEvent *event );
85bf2eeab3Smrgstatic void ProxySaveYourselfPhase2CB ( SmcConn smcConn, SmPointer clientData );
86bf2eeab3Smrgstatic void ProxySaveYourselfCB ( SmcConn smcConn, SmPointer clientData,
8714c0a534Smrg			   int saveType, Bool shutdown, int interactStyle,
8814c0a534Smrg			   Bool fast );
89bf2eeab3Smrgstatic void ProxyDieCB ( SmcConn smcConn, SmPointer clientData );
90bf2eeab3Smrgstatic void ProxySaveCompleteCB ( SmcConn smcConn, SmPointer clientData );
91bf2eeab3Smrgstatic void ProxyShutdownCancelledCB ( SmcConn smcConn, SmPointer clientData );
92bf2eeab3Smrgstatic Status ConnectProxyToSM ( char *previous_id );
93bf2eeab3Smrgstatic void CheckForExistingWindows ( Window root );
9414c0a534Smrg
9514c0a534Smrg
96bf2eeab3Smrgstatic Bool
97bf2eeab3SmrgHasSaveYourself(Window window)
9814c0a534Smrg{
9914c0a534Smrg    Atom *protocols;
10014c0a534Smrg    int numProtocols;
10114c0a534Smrg    int i, found;
10214c0a534Smrg
10314c0a534Smrg    protocols = NULL;
10414c0a534Smrg
10514c0a534Smrg    if (XGetWMProtocols (disp, window, &protocols, &numProtocols) != True)
10614c0a534Smrg	return (False);
10714c0a534Smrg
10814c0a534Smrg    found = 0;
10914c0a534Smrg
11014c0a534Smrg    if (protocols != NULL)
11114c0a534Smrg    {
11214c0a534Smrg	for (i = 0; i < numProtocols; i++)
11314c0a534Smrg	    if (protocols[i] == wmSaveYourselfAtom)
11414c0a534Smrg		found = 1;
11514c0a534Smrg
11614c0a534Smrg	XFree (protocols);
11714c0a534Smrg    }
11814c0a534Smrg
11914c0a534Smrg    return (found);
12014c0a534Smrg}
12114c0a534Smrg
12214c0a534Smrg
12314c0a534Smrg
124bf2eeab3Smrgstatic Bool
125bf2eeab3SmrgHasXSMPsupport(Window window)
12614c0a534Smrg{
12714c0a534Smrg    XTextProperty tp;
12814c0a534Smrg    Bool hasIt = 0;
12914c0a534Smrg
13014c0a534Smrg    if (XGetTextProperty (disp, window, &tp, smClientIdAtom))
13114c0a534Smrg    {
13214c0a534Smrg	if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0)
13314c0a534Smrg	    hasIt = 1;
13414c0a534Smrg
13514c0a534Smrg	if (tp.value)
13614c0a534Smrg	    XFree ((char *) tp.value);
13714c0a534Smrg    }
13814c0a534Smrg
13914c0a534Smrg    return (hasIt);
14014c0a534Smrg}
14114c0a534Smrg
14214c0a534Smrg
14314c0a534Smrg
144bf2eeab3Smrgstatic WinInfo *
145bf2eeab3SmrgGetClientLeader(WinInfo *winptr)
14614c0a534Smrg{
14714c0a534Smrg    Atom actual_type;
14814c0a534Smrg    int actual_format;
14914c0a534Smrg    unsigned long nitems, bytesafter;
15014c0a534Smrg    unsigned long *datap = NULL;
15114c0a534Smrg    WinInfo *leader_winptr = NULL;
15214c0a534Smrg    Bool failure = 0;
15314c0a534Smrg
15414c0a534Smrg    if (XGetWindowProperty (disp, winptr->window, wmClientLeaderAtom,
15514c0a534Smrg	0L, 1L, False, AnyPropertyType,	&actual_type, &actual_format,
15614c0a534Smrg	&nitems, &bytesafter, (unsigned char **) &datap) == Success)
15714c0a534Smrg    {
15814c0a534Smrg	if (actual_type == XA_WINDOW && actual_format == 32 &&
15914c0a534Smrg	    nitems == 1 && bytesafter == 0)
16014c0a534Smrg	{
16114c0a534Smrg	    Window leader_win = *((Window *) datap);
16214c0a534Smrg
16314c0a534Smrg	    if (!LookupWindow (leader_win, &leader_winptr, NULL))
16414c0a534Smrg		failure = 1;
16514c0a534Smrg	}
16614c0a534Smrg
16714c0a534Smrg	if (datap)
16814c0a534Smrg	    XFree (datap);
16914c0a534Smrg    }
17014c0a534Smrg
17114c0a534Smrg    if (failure)
17214c0a534Smrg    {
17314c0a534Smrg	/* The client leader was defined, but we couldn't find the window */
17414c0a534Smrg
17514c0a534Smrg	return (NULL);
17614c0a534Smrg    }
17714c0a534Smrg    else if (leader_winptr)
17814c0a534Smrg    {
17914c0a534Smrg	/* We found the real client leader */
18014c0a534Smrg
18114c0a534Smrg	return (leader_winptr);
18214c0a534Smrg    }
18314c0a534Smrg    else
18414c0a534Smrg    {
18514c0a534Smrg	/* There is no client leader defined, return this window */
18614c0a534Smrg
18714c0a534Smrg	return (winptr);
18814c0a534Smrg    }
18914c0a534Smrg}
19014c0a534Smrg
19114c0a534Smrg
19214c0a534Smrg
193bf2eeab3Smrgstatic char *
194bf2eeab3SmrgCheckFullyQuantifiedName(char *name, int *newstring)
19514c0a534Smrg{
19614c0a534Smrg    /*
19714c0a534Smrg     * Due to a bug in Xlib (for hpux in particular), some clients
19814c0a534Smrg     * will have a WM_CLIENT_MACHINE that is not fully quantified.
19914c0a534Smrg     * For example, we might get "excon" instead of "excon.x.org".
20014c0a534Smrg     * This really stinks.  The best we can do is tag on our own
20114c0a534Smrg     * domain name.
20214c0a534Smrg     */
20314c0a534Smrg
20414c0a534Smrg    if (strchr (name, '.') != NULL)
20514c0a534Smrg    {
20614c0a534Smrg	*newstring = 0;
20714c0a534Smrg	return (name);
20814c0a534Smrg    }
20914c0a534Smrg    else
21014c0a534Smrg    {
21114c0a534Smrg	char hostnamebuf[80];
21214c0a534Smrg	char *firstDot;
21314c0a534Smrg
21414c0a534Smrg	gethostname (hostnamebuf, sizeof hostnamebuf);
21514c0a534Smrg	firstDot = strchr (hostnamebuf, '.');
21614c0a534Smrg
21714c0a534Smrg	if (!firstDot)
21814c0a534Smrg	{
21914c0a534Smrg	    *newstring = 0;
22014c0a534Smrg	    return (name);
22114c0a534Smrg	}
22214c0a534Smrg	else
22314c0a534Smrg	{
22414c0a534Smrg	    char *newptr;
22514c0a534Smrg
22624047306Smrg	    if (asprintf (&newptr, "%s.%s", name, firstDot + 1) == -1) {
22724047306Smrg                *newstring = 0;
22824047306Smrg                return NULL;
22924047306Smrg            }
23014c0a534Smrg	    *newstring = 1;
23114c0a534Smrg	    return (newptr);
23214c0a534Smrg	}
23314c0a534Smrg    }
23414c0a534Smrg}
23514c0a534Smrg
23614c0a534Smrg
23714c0a534Smrg
238bf2eeab3Smrgstatic void
239bf2eeab3SmrgFinishSaveYourself(WinInfo *winInfo, Bool has_WM_SAVEYOURSELF)
24014c0a534Smrg{
24114c0a534Smrg    SmProp prop1, prop2, prop3, *props[3];
24214c0a534Smrg    SmPropValue prop1val, prop2val, prop3val;
24314c0a534Smrg    int i;
24414c0a534Smrg
24514c0a534Smrg    if (!winInfo->got_first_save_yourself)
24614c0a534Smrg    {
24714c0a534Smrg	char userId[20], restartService[80];
24814c0a534Smrg	char *fullyQuantifiedName;
24914c0a534Smrg	int newstring;
25014c0a534Smrg
25114c0a534Smrg	prop1.name = SmProgram;
25214c0a534Smrg	prop1.type = SmARRAY8;
25314c0a534Smrg	prop1.num_vals = 1;
25414c0a534Smrg	prop1.vals = &prop1val;
25514c0a534Smrg	prop1val.value = (SmPointer) winInfo->wm_command[0];
25614c0a534Smrg	prop1val.length = strlen (winInfo->wm_command[0]);
25714c0a534Smrg
25824047306Smrg	snprintf (userId, sizeof(userId), "%ld", (long)getuid());
25914c0a534Smrg	prop2.name = SmUserID;
26014c0a534Smrg	prop2.type = SmARRAY8;
26114c0a534Smrg	prop2.num_vals = 1;
26214c0a534Smrg	prop2.vals = &prop2val;
26314c0a534Smrg	prop2val.value = (SmPointer) userId;
26414c0a534Smrg	prop2val.length = strlen (userId);
26514c0a534Smrg
26614c0a534Smrg	fullyQuantifiedName = CheckFullyQuantifiedName (
26714c0a534Smrg	    (char *) winInfo->wm_client_machine.value, &newstring);
26824047306Smrg	snprintf (restartService, sizeof(restartService),
26924047306Smrg                  "rstart-rsh/%s", fullyQuantifiedName);
27014c0a534Smrg	if (newstring)
27114c0a534Smrg	    free (fullyQuantifiedName);
27214c0a534Smrg
27314c0a534Smrg	prop3.name = "_XC_RestartService";
27414c0a534Smrg	prop3.type = SmLISTofARRAY8;
27514c0a534Smrg	prop3.num_vals = 1;
27614c0a534Smrg	prop3.vals = &prop3val;
27714c0a534Smrg	prop3val.value = (SmPointer) restartService;
27814c0a534Smrg	prop3val.length = strlen (restartService);
27914c0a534Smrg
28014c0a534Smrg	props[0] = &prop1;
28114c0a534Smrg	props[1] = &prop2;
28214c0a534Smrg	props[2] = &prop3;
28314c0a534Smrg
28414c0a534Smrg	SmcSetProperties (winInfo->smc_conn, 3, props);
28514c0a534Smrg
28614c0a534Smrg	winInfo->got_first_save_yourself = 1;
28714c0a534Smrg    }
28814c0a534Smrg
28914c0a534Smrg    prop1.name = SmRestartCommand;
29014c0a534Smrg    prop1.type = SmLISTofARRAY8;
29114c0a534Smrg    prop1.num_vals = winInfo->wm_command_count;
29214c0a534Smrg
29314c0a534Smrg    prop1.vals = (SmPropValue *) malloc (
29414c0a534Smrg	winInfo->wm_command_count * sizeof (SmPropValue));
29514c0a534Smrg
29614c0a534Smrg    if (!prop1.vals)
29714c0a534Smrg    {
29814c0a534Smrg	SmcSaveYourselfDone (winInfo->smc_conn, False);
29914c0a534Smrg	return;
30014c0a534Smrg    }
30114c0a534Smrg
30214c0a534Smrg    for (i = 0; i < winInfo->wm_command_count; i++)
30314c0a534Smrg    {
30414c0a534Smrg	prop1.vals[i].value = (SmPointer) winInfo->wm_command[i];
30514c0a534Smrg	prop1.vals[i].length = strlen (winInfo->wm_command[i]);
30614c0a534Smrg    }
30714c0a534Smrg
30814c0a534Smrg    prop2.name = SmCloneCommand;
30914c0a534Smrg    prop2.type = SmLISTofARRAY8;
31014c0a534Smrg    prop2.num_vals = winInfo->wm_command_count;
31114c0a534Smrg    prop2.vals = prop1.vals;
31214c0a534Smrg
31314c0a534Smrg    props[0] = &prop1;
31414c0a534Smrg    props[1] = &prop2;
31514c0a534Smrg
31614c0a534Smrg    SmcSetProperties (winInfo->smc_conn, 2, props);
31714c0a534Smrg
31814c0a534Smrg    free ((char *) prop1.vals);
31914c0a534Smrg
32014c0a534Smrg    /*
32114c0a534Smrg     * If the client doesn't support WM_SAVE_YOURSELF, we should
32214c0a534Smrg     * return failure for the save, since we really don't know if
32314c0a534Smrg     * the application needed to save state.
32414c0a534Smrg     */
32514c0a534Smrg
32614c0a534Smrg    SmcSaveYourselfDone (winInfo->smc_conn, has_WM_SAVEYOURSELF);
32714c0a534Smrg}
32814c0a534Smrg
32914c0a534Smrg
33014c0a534Smrg
331bf2eeab3Smrgstatic void
332bf2eeab3SmrgSaveYourselfCB(SmcConn smcConn, SmPointer clientData, int saveType,
333bf2eeab3Smrg	       Bool shutdown, int interactStyle, Bool fast)
33414c0a534Smrg{
33514c0a534Smrg    WinInfo *winInfo = (WinInfo *) clientData;
33614c0a534Smrg
33714c0a534Smrg    if (!winInfo->has_save_yourself)
33814c0a534Smrg    {
33914c0a534Smrg	FinishSaveYourself (winInfo, False);
34014c0a534Smrg    }
34114c0a534Smrg    else
34214c0a534Smrg    {
34314c0a534Smrg	XClientMessageEvent saveYourselfMessage;
34414c0a534Smrg
34514c0a534Smrg
34614c0a534Smrg	/* Send WM_SAVE_YOURSELF */
34714c0a534Smrg
34814c0a534Smrg	saveYourselfMessage.type = ClientMessage;
34914c0a534Smrg	saveYourselfMessage.window = winInfo->window;
35014c0a534Smrg	saveYourselfMessage.message_type = wmProtocolsAtom;
35114c0a534Smrg	saveYourselfMessage.format = 32;
35214c0a534Smrg	saveYourselfMessage.data.l[0] = wmSaveYourselfAtom;
35314c0a534Smrg	saveYourselfMessage.data.l[1] = CurrentTime;
35414c0a534Smrg
35514c0a534Smrg	if (XSendEvent (disp, winInfo->window, False, NoEventMask,
35614c0a534Smrg	    (XEvent *) &saveYourselfMessage))
35714c0a534Smrg	{
35814c0a534Smrg	    winInfo->waiting_for_update = 1;
35914c0a534Smrg
36014c0a534Smrg	    if (debug)
36114c0a534Smrg	    {
36214c0a534Smrg		printf ("Sent SAVE YOURSELF to 0x%x\n",
36314c0a534Smrg			(unsigned int)winInfo->window);
36414c0a534Smrg		printf ("\n");
36514c0a534Smrg	    }
36614c0a534Smrg	}
36714c0a534Smrg	else
36814c0a534Smrg	{
36914c0a534Smrg	    if (debug)
37014c0a534Smrg	    {
37114c0a534Smrg		printf ("Failed to send SAVE YOURSELF to 0x%x\n",
37214c0a534Smrg		    (unsigned int)winInfo->window);
37314c0a534Smrg		printf ("\n");
37414c0a534Smrg	    }
37514c0a534Smrg	}
37614c0a534Smrg    }
37714c0a534Smrg}
37814c0a534Smrg
37914c0a534Smrg
38014c0a534Smrg
381bf2eeab3Smrgstatic void
382bf2eeab3SmrgDieCB(SmcConn smcConn, SmPointer clientData)
38314c0a534Smrg{
38414c0a534Smrg    WinInfo *winInfo = (WinInfo *) clientData;
38514c0a534Smrg
38614c0a534Smrg    SmcCloseConnection (winInfo->smc_conn, 0, NULL);
38714c0a534Smrg    winInfo->smc_conn = NULL;
38814c0a534Smrg    XtRemoveInput (winInfo->input_id);
38914c0a534Smrg
39014c0a534Smrg    /* Now tell the client to die */
39114c0a534Smrg
39214c0a534Smrg    if (debug)
39314c0a534Smrg	printf ("Trying to kill 0x%x\n", (unsigned int)winInfo->window);
39414c0a534Smrg
39514c0a534Smrg    XSync (disp, 0);
39614c0a534Smrg    XKillClient (disp, winInfo->window);
39714c0a534Smrg    XSync (disp, 0);
39814c0a534Smrg
39914c0a534Smrg
40014c0a534Smrg    /*
40114c0a534Smrg     * Proxy must exit when all clients die, and the proxy itself
40214c0a534Smrg     * must have received a Die.
40314c0a534Smrg     */
40414c0a534Smrg
40514c0a534Smrg    die_count++;
40614c0a534Smrg
40714c0a534Smrg    if (die_count == proxy_count && ok_to_die)
40814c0a534Smrg    {
40914c0a534Smrg	exit (0);
41014c0a534Smrg    }
41114c0a534Smrg}
41214c0a534Smrg
41314c0a534Smrg
41414c0a534Smrg
415bf2eeab3Smrgstatic void
416bf2eeab3SmrgSaveCompleteCB(SmcConn smcConn, SmPointer clientData)
41714c0a534Smrg{
41814c0a534Smrg    /*
41914c0a534Smrg     * Nothing to do here.
42014c0a534Smrg     */
42114c0a534Smrg}
42214c0a534Smrg
42314c0a534Smrg
42414c0a534Smrg
425bf2eeab3Smrgstatic void
426bf2eeab3SmrgShutdownCancelledCB(SmcConn smcConn, SmPointer clientData)
42714c0a534Smrg{
42814c0a534Smrg    /*
42914c0a534Smrg     * Since we did not request to interact or request save yourself
43014c0a534Smrg     * phase 2, we know we already sent the save yourself done, so
43114c0a534Smrg     * there is nothing to do here.
43214c0a534Smrg     */
43314c0a534Smrg}
43414c0a534Smrg
43514c0a534Smrg
43614c0a534Smrg
437bf2eeab3Smrgstatic void
438bf2eeab3SmrgProcessIceMsgProc(XtPointer client_data, int *source, XtInputId *id)
43914c0a534Smrg{
44014c0a534Smrg    IceConn	ice_conn = (IceConn) client_data;
44114c0a534Smrg
44214c0a534Smrg    IceProcessMessages (ice_conn, NULL, NULL);
44314c0a534Smrg}
44414c0a534Smrg
44514c0a534Smrg
44614c0a534Smrg
447bf2eeab3Smrgstatic void
448bf2eeab3SmrgNullIceErrorHandler(IceConn iceConn, Bool swap, int offendingMinorOpcode,
449bf2eeab3Smrg		    unsigned long offendingSequence, int errorClass,
450bf2eeab3Smrg		    int severity, IcePointer values)
45114c0a534Smrg{
45214c0a534Smrg    return;
45314c0a534Smrg}
45414c0a534Smrg
45514c0a534Smrg
456bf2eeab3Smrgstatic void
457bf2eeab3SmrgConnectClientToSM(WinInfo *winInfo)
45814c0a534Smrg{
45914c0a534Smrg    char errorMsg[256];
46014c0a534Smrg    unsigned long mask;
46114c0a534Smrg    SmcCallbacks callbacks;
46214c0a534Smrg    IceConn ice_conn;
46314c0a534Smrg    char *prevId;
46414c0a534Smrg
46514c0a534Smrg    mask = SmcSaveYourselfProcMask | SmcDieProcMask |
46614c0a534Smrg	SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
46714c0a534Smrg
46814c0a534Smrg    callbacks.save_yourself.callback = SaveYourselfCB;
46914c0a534Smrg    callbacks.save_yourself.client_data = (SmPointer) winInfo;
47014c0a534Smrg
47114c0a534Smrg    callbacks.die.callback = DieCB;
47214c0a534Smrg    callbacks.die.client_data = (SmPointer) winInfo;
47314c0a534Smrg
47414c0a534Smrg    callbacks.save_complete.callback = SaveCompleteCB;
47514c0a534Smrg    callbacks.save_complete.client_data = (SmPointer) winInfo;
47614c0a534Smrg
47714c0a534Smrg    callbacks.shutdown_cancelled.callback = ShutdownCancelledCB;
47814c0a534Smrg    callbacks.shutdown_cancelled.client_data = (SmPointer) winInfo;
47914c0a534Smrg
48014c0a534Smrg    prevId = LookupClientID (winInfo);
48114c0a534Smrg
48214c0a534Smrg    /*
48314c0a534Smrg     * In case a protocol error occurs when opening the connection,
48414c0a534Smrg     * (e.g. an authentication error), we set a null error handler
48514c0a534Smrg     * before the open, then restore the default handler after the open.
48614c0a534Smrg     */
48714c0a534Smrg
48814c0a534Smrg    IceSetErrorHandler (NullIceErrorHandler);
48914c0a534Smrg
49014c0a534Smrg    winInfo->smc_conn = SmcOpenConnection (
49114c0a534Smrg	NULL, 			/* use SESSION_MANAGER env */
49214c0a534Smrg	(SmPointer) winInfo,	/* force a new connection */
49314c0a534Smrg	SmProtoMajor,
49414c0a534Smrg	SmProtoMinor,
49514c0a534Smrg	mask,
49614c0a534Smrg	&callbacks,
49714c0a534Smrg	prevId,
49814c0a534Smrg	&winInfo->client_id,
49914c0a534Smrg	256, errorMsg);
50014c0a534Smrg
50114c0a534Smrg    IceSetErrorHandler (NULL);
50214c0a534Smrg
50314c0a534Smrg    if (winInfo->smc_conn == NULL)
50414c0a534Smrg	return;
50514c0a534Smrg
50614c0a534Smrg    ice_conn = SmcGetIceConnection (winInfo->smc_conn);
50714c0a534Smrg
50814c0a534Smrg    winInfo->input_id = XtAppAddInput (
50914c0a534Smrg	    appContext,
51014c0a534Smrg	    IceConnectionNumber (ice_conn),
51114c0a534Smrg            (XtPointer) XtInputReadMask,
51214c0a534Smrg	    ProcessIceMsgProc,
51314c0a534Smrg	    (XtPointer) ice_conn);
51414c0a534Smrg
51514c0a534Smrg    if (debug)
51614c0a534Smrg    {
51714c0a534Smrg	printf ("Connected to SM, window = 0x%x\n",
51814c0a534Smrg		(unsigned int)winInfo->window);
51914c0a534Smrg	printf ("\n");
52014c0a534Smrg    }
52114c0a534Smrg
52214c0a534Smrg    proxy_count++;
52314c0a534Smrg}
52414c0a534Smrg
52514c0a534Smrg
52614c0a534Smrg
527bf2eeab3Smrgstatic int
528bf2eeab3SmrgMyErrorHandler(Display *display, XErrorEvent *event)
52914c0a534Smrg{
53014c0a534Smrg    caught_error = 1;
53114c0a534Smrg    return 0;
53214c0a534Smrg}
53314c0a534Smrg
53414c0a534Smrg
53514c0a534Smrg
536bf2eeab3Smrgstatic Bool
537bf2eeab3SmrgLookupWindow(Window window, WinInfo **ptr_ret, WinInfo **prev_ptr_ret)
53814c0a534Smrg{
53914c0a534Smrg    WinInfo *ptr, *prev;
54014c0a534Smrg
54114c0a534Smrg    ptr = win_head;
54214c0a534Smrg    prev = NULL;
54314c0a534Smrg
54414c0a534Smrg    while (ptr)
54514c0a534Smrg    {
54614c0a534Smrg	if (ptr->window == window)
54714c0a534Smrg	    break;
54814c0a534Smrg	else
54914c0a534Smrg	{
55014c0a534Smrg	    prev = ptr;
55114c0a534Smrg	    ptr = ptr->next;
55214c0a534Smrg	}
55314c0a534Smrg    }
55414c0a534Smrg
55514c0a534Smrg    if (ptr)
55614c0a534Smrg    {
55714c0a534Smrg	if (ptr_ret)
55814c0a534Smrg	    *ptr_ret = ptr;
55914c0a534Smrg	if (prev_ptr_ret)
56014c0a534Smrg	    *prev_ptr_ret = prev;
56114c0a534Smrg	return (1);
56214c0a534Smrg    }
56314c0a534Smrg    else
56414c0a534Smrg	return (0);
56514c0a534Smrg}
56614c0a534Smrg
56714c0a534Smrg
56814c0a534Smrg
569bf2eeab3Smrgstatic WinInfo *
570bf2eeab3SmrgAddNewWindow(Window window)
57114c0a534Smrg{
57214c0a534Smrg    WinInfo *newptr;
57314c0a534Smrg
57414c0a534Smrg    if (LookupWindow (window, NULL, NULL))
57514c0a534Smrg	return (NULL);
57614c0a534Smrg
57714c0a534Smrg    newptr = (WinInfo *) malloc (sizeof (WinInfo));
57814c0a534Smrg
57914c0a534Smrg    if (newptr == NULL)
58014c0a534Smrg	return (NULL);
58114c0a534Smrg
58214c0a534Smrg    newptr->next = win_head;
58314c0a534Smrg    win_head = newptr;
58414c0a534Smrg
58514c0a534Smrg    newptr->window = window;
58614c0a534Smrg    newptr->smc_conn = NULL;
58714c0a534Smrg    newptr->tested_for_sm_client_id = 0;
58814c0a534Smrg    newptr->client_id = NULL;
58914c0a534Smrg    newptr->wm_command = NULL;
59014c0a534Smrg    newptr->wm_command_count = 0;
59114c0a534Smrg    newptr->class.res_name = NULL;
59214c0a534Smrg    newptr->class.res_class = NULL;
59314c0a534Smrg    newptr->wm_name = NULL;
59414c0a534Smrg    newptr->wm_client_machine.value = NULL;
59514c0a534Smrg    newptr->wm_client_machine.nitems = 0;
59614c0a534Smrg    newptr->has_save_yourself = 0;
59714c0a534Smrg    newptr->waiting_for_update = 0;
59814c0a534Smrg    newptr->got_first_save_yourself = 0;
59914c0a534Smrg
60014c0a534Smrg    return (newptr);
60114c0a534Smrg}
60214c0a534Smrg
60314c0a534Smrg
60414c0a534Smrg
605bf2eeab3Smrgstatic void
606bf2eeab3SmrgRemoveWindow(WinInfo *winptr)
60714c0a534Smrg{
60814c0a534Smrg    WinInfo *ptr, *prev;
60914c0a534Smrg
61014c0a534Smrg    if (LookupWindow (winptr->window, &ptr, &prev))
61114c0a534Smrg    {
61214c0a534Smrg	if (prev == NULL)
61314c0a534Smrg	    win_head = ptr->next;
61414c0a534Smrg	else
61514c0a534Smrg	    prev->next = ptr->next;
61614c0a534Smrg
61714c0a534Smrg	if (ptr->client_id)
61814c0a534Smrg	    free (ptr->client_id);
61914c0a534Smrg
62014c0a534Smrg	if (ptr->wm_command)
62114c0a534Smrg	    XFreeStringList (ptr->wm_command);
62214c0a534Smrg
62314c0a534Smrg	if (ptr->wm_name)
62414c0a534Smrg	    XFree (ptr->wm_name);
62514c0a534Smrg
62614c0a534Smrg	if (ptr->wm_client_machine.value)
62714c0a534Smrg	    XFree (ptr->wm_client_machine.value);
62814c0a534Smrg
62914c0a534Smrg	if (ptr->class.res_name)
63014c0a534Smrg	    XFree (ptr->class.res_name);
63114c0a534Smrg
63214c0a534Smrg	if (ptr->class.res_class)
63314c0a534Smrg	    XFree (ptr->class.res_class);
63414c0a534Smrg
63514c0a534Smrg	free ((char *) ptr);
63614c0a534Smrg    }
63714c0a534Smrg}
63814c0a534Smrg
63914c0a534Smrg
64014c0a534Smrg
641bf2eeab3Smrgstatic void
642bf2eeab3SmrgGot_WM_STATE(WinInfo *winptr)
64314c0a534Smrg{
64414c0a534Smrg    WinInfo *leader_winptr;
64514c0a534Smrg
64614c0a534Smrg    /*
64714c0a534Smrg     * If we already got WM_STATE and tested for SM_CLIENT_ID, we
64814c0a534Smrg     * shouldn't do it again.
64914c0a534Smrg     */
65014c0a534Smrg
65114c0a534Smrg    if (winptr->tested_for_sm_client_id)
65214c0a534Smrg    {
65314c0a534Smrg	return;
65414c0a534Smrg    }
65514c0a534Smrg
65614c0a534Smrg
65714c0a534Smrg    /*
65814c0a534Smrg     * Set a null error handler, in case this window goes away
65914c0a534Smrg     * behind our back.
66014c0a534Smrg     */
66114c0a534Smrg
66214c0a534Smrg    caught_error = 0;
66314c0a534Smrg    XSetErrorHandler (MyErrorHandler);
66414c0a534Smrg
66514c0a534Smrg
66614c0a534Smrg    /*
66714c0a534Smrg     * Get the client leader window.
66814c0a534Smrg     */
66914c0a534Smrg
67014c0a534Smrg    leader_winptr = GetClientLeader (winptr);
67114c0a534Smrg
67214c0a534Smrg    if (caught_error)
67314c0a534Smrg    {
67414c0a534Smrg	caught_error = 0;
67514c0a534Smrg	RemoveWindow (winptr);
67614c0a534Smrg	XSetErrorHandler (NULL);
67714c0a534Smrg	return;
67814c0a534Smrg    }
67914c0a534Smrg
68014c0a534Smrg
68114c0a534Smrg    /*
68214c0a534Smrg     * If we already checked for SM_CLIENT_ID on the client leader
68314c0a534Smrg     * window, don't do it again.
68414c0a534Smrg     */
68514c0a534Smrg
68614c0a534Smrg    if (!leader_winptr || leader_winptr->tested_for_sm_client_id)
68714c0a534Smrg    {
68814c0a534Smrg	caught_error = 0;
68914c0a534Smrg	XSetErrorHandler (NULL);
69014c0a534Smrg	return;
69114c0a534Smrg    }
69214c0a534Smrg
69314c0a534Smrg    leader_winptr->tested_for_sm_client_id = 1;
69414c0a534Smrg
69514c0a534Smrg    if (!HasXSMPsupport (leader_winptr->window))
69614c0a534Smrg    {
69714c0a534Smrg	XFetchName (disp, leader_winptr->window, &leader_winptr->wm_name);
69814c0a534Smrg
69914c0a534Smrg	XGetCommand (disp, leader_winptr->window,
70014c0a534Smrg	    &leader_winptr->wm_command,
70114c0a534Smrg	    &leader_winptr->wm_command_count);
70214c0a534Smrg
70314c0a534Smrg	XGetClassHint (disp, leader_winptr->window, &leader_winptr->class);
70414c0a534Smrg
70514c0a534Smrg	XGetWMClientMachine (disp, leader_winptr->window,
70614c0a534Smrg	    &leader_winptr->wm_client_machine);
70714c0a534Smrg
70814c0a534Smrg	if (leader_winptr->wm_name != NULL &&
70914c0a534Smrg	    leader_winptr->wm_command != NULL &&
71014c0a534Smrg	    leader_winptr->wm_command_count > 0 &&
71114c0a534Smrg	    leader_winptr->class.res_name != NULL &&
71214c0a534Smrg	    leader_winptr->class.res_class != NULL &&
71314c0a534Smrg	    leader_winptr->wm_client_machine.value != NULL &&
71414c0a534Smrg	    leader_winptr->wm_client_machine.nitems != 0)
71514c0a534Smrg	{
71614c0a534Smrg	    leader_winptr->has_save_yourself =
71714c0a534Smrg		HasSaveYourself (leader_winptr->window);
71814c0a534Smrg
71914c0a534Smrg	    ConnectClientToSM (leader_winptr);
72014c0a534Smrg	}
72114c0a534Smrg    }
72214c0a534Smrg
72314c0a534Smrg    XSync (disp, 0);
72414c0a534Smrg    XSetErrorHandler (NULL);
72514c0a534Smrg
72614c0a534Smrg    if (caught_error)
72714c0a534Smrg    {
72814c0a534Smrg	caught_error = 0;
72914c0a534Smrg	RemoveWindow (leader_winptr);
73014c0a534Smrg    }
73114c0a534Smrg}
73214c0a534Smrg
73314c0a534Smrg
73414c0a534Smrg
735bf2eeab3Smrgstatic void
736bf2eeab3SmrgHandleCreate(XCreateWindowEvent *event)
73714c0a534Smrg{
73814c0a534Smrg    Atom actual_type;
73914c0a534Smrg    int actual_format;
74014c0a534Smrg    unsigned long nitems, bytesafter;
74114c0a534Smrg    unsigned long *datap = NULL;
74214c0a534Smrg    WinInfo *winptr;
74314c0a534Smrg    Bool got_wm_state = 0;
74414c0a534Smrg
74514c0a534Smrg    /*
74614c0a534Smrg     * We are waiting for all proxy connections to close so we can die.
74714c0a534Smrg     * Don't handle new connections.
74814c0a534Smrg     */
74914c0a534Smrg
75014c0a534Smrg    if (ok_to_die)
75114c0a534Smrg	return;
75214c0a534Smrg
75314c0a534Smrg
75414c0a534Smrg    /*
75514c0a534Smrg     * Add the new window
75614c0a534Smrg     */
75714c0a534Smrg
75814c0a534Smrg    if ((winptr = AddNewWindow (event->window)) == NULL)
75914c0a534Smrg	return;
76014c0a534Smrg
76114c0a534Smrg
76214c0a534Smrg    /*
76314c0a534Smrg     * Right after the window was created, it might have been destroyed,
76414c0a534Smrg     * so the following Xlib calls might fail.  Need to catch the error
76514c0a534Smrg     * by installing an error handler.
76614c0a534Smrg     */
76714c0a534Smrg
76814c0a534Smrg    caught_error = 0;
76914c0a534Smrg    XSetErrorHandler (MyErrorHandler);
77014c0a534Smrg
77114c0a534Smrg
77214c0a534Smrg    /*
77314c0a534Smrg     * Select for Property Notify on the window so we can determine
77414c0a534Smrg     * when WM_STATE is defined.  To avoid a race condition, we must
77514c0a534Smrg     * do this _before_ we check for WM_STATE right now.
77614c0a534Smrg     *
77714c0a534Smrg     * Select for Substructure Notify so we can determine when the
77814c0a534Smrg     * window is destroyed.
77914c0a534Smrg     */
78014c0a534Smrg
78114c0a534Smrg    XSelectInput (disp, event->window,
78214c0a534Smrg	SubstructureNotifyMask | PropertyChangeMask);
78314c0a534Smrg
78414c0a534Smrg
78514c0a534Smrg    /*
78614c0a534Smrg     * WM_STATE may already be there.  Check now.
78714c0a534Smrg     */
78814c0a534Smrg
78914c0a534Smrg    if (XGetWindowProperty (disp, event->window, wmStateAtom,
79014c0a534Smrg	0L, 2L, False, AnyPropertyType,
79114c0a534Smrg	&actual_type, &actual_format, &nitems, &bytesafter,
79214c0a534Smrg	(unsigned char **) &datap) == Success && datap)
79314c0a534Smrg    {
79414c0a534Smrg	if (nitems > 0)
79514c0a534Smrg	    got_wm_state = 1;
79614c0a534Smrg
79714c0a534Smrg	if (datap)
79814c0a534Smrg	    XFree ((char *) datap);
79914c0a534Smrg    }
80014c0a534Smrg
80114c0a534Smrg    XSync (disp, 0);
80214c0a534Smrg    XSetErrorHandler (NULL);
80314c0a534Smrg
80414c0a534Smrg    if (caught_error)
80514c0a534Smrg    {
80614c0a534Smrg	caught_error = 0;
80714c0a534Smrg	RemoveWindow (winptr);
80814c0a534Smrg    }
80914c0a534Smrg    else if (got_wm_state)
81014c0a534Smrg    {
81114c0a534Smrg	Got_WM_STATE (winptr);
81214c0a534Smrg    }
81314c0a534Smrg}
81414c0a534Smrg
81514c0a534Smrg
81614c0a534Smrg
817bf2eeab3Smrgstatic void
818bf2eeab3SmrgHandleDestroy(XDestroyWindowEvent *event)
81914c0a534Smrg{
82014c0a534Smrg    WinInfo *winptr;
82114c0a534Smrg
82214c0a534Smrg    if (LookupWindow (event->window, &winptr, NULL))
82314c0a534Smrg    {
82414c0a534Smrg	if (winptr->smc_conn)
82514c0a534Smrg	{
82614c0a534Smrg	    SmcCloseConnection (winptr->smc_conn, 0, NULL);
82714c0a534Smrg	    XtRemoveInput (winptr->input_id);
82814c0a534Smrg	    proxy_count--;
82914c0a534Smrg	}
83014c0a534Smrg
83114c0a534Smrg	if (debug)
83214c0a534Smrg	{
83314c0a534Smrg	    printf ("Removed window (window = 0x%x)\n",
83414c0a534Smrg		    (unsigned int)winptr->window);
83514c0a534Smrg	    printf ("\n");
83614c0a534Smrg	}
83714c0a534Smrg
83814c0a534Smrg	RemoveWindow (winptr);
83914c0a534Smrg    }
84014c0a534Smrg}
84114c0a534Smrg
84214c0a534Smrg
84314c0a534Smrg
844bf2eeab3Smrgstatic void
845bf2eeab3SmrgHandleUpdate(XPropertyEvent *event)
84614c0a534Smrg{
84714c0a534Smrg    Window window = event->window;
84814c0a534Smrg    WinInfo *winptr;
84914c0a534Smrg
85014c0a534Smrg    if (!LookupWindow (window, &winptr, NULL))
85114c0a534Smrg	return;
85214c0a534Smrg
85314c0a534Smrg    if (event->atom == wmStateAtom)
85414c0a534Smrg    {
85514c0a534Smrg	Got_WM_STATE (winptr);
85614c0a534Smrg    }
85714c0a534Smrg    else if (event->atom == XA_WM_COMMAND && winptr->waiting_for_update)
85814c0a534Smrg    {
85914c0a534Smrg	/* Finish off the Save Yourself */
86014c0a534Smrg
86114c0a534Smrg	if (winptr->wm_command)
86214c0a534Smrg	{
86314c0a534Smrg	    XFreeStringList (winptr->wm_command);
86414c0a534Smrg	    winptr->wm_command = NULL;
86514c0a534Smrg	    winptr->wm_command_count = 0;
86614c0a534Smrg	}
86714c0a534Smrg
86814c0a534Smrg	XGetCommand (disp, window,
86914c0a534Smrg	    &winptr->wm_command,
87014c0a534Smrg	    &winptr->wm_command_count);
87114c0a534Smrg
87214c0a534Smrg	winptr->waiting_for_update = 0;
87314c0a534Smrg	FinishSaveYourself (winptr, True);
87414c0a534Smrg    }
87514c0a534Smrg}
87614c0a534Smrg
87714c0a534Smrg
87814c0a534Smrg
879bf2eeab3Smrgstatic void
880bf2eeab3SmrgProxySaveYourselfPhase2CB(SmcConn smcConn, SmPointer clientData)
88114c0a534Smrg{
88214c0a534Smrg    char *filename;
88314c0a534Smrg    Bool success = True;
88414c0a534Smrg    SmProp prop1, prop2, prop3, *props[3];
88514c0a534Smrg    SmPropValue prop1val, prop2val, prop3val;
88624047306Smrg    char *discardCommand;
88714c0a534Smrg    int numVals, i;
88814c0a534Smrg    static int first_time = 1;
88914c0a534Smrg
89014c0a534Smrg    if (first_time)
89114c0a534Smrg    {
89214c0a534Smrg	char userId[20];
89314c0a534Smrg	char hint = SmRestartIfRunning;
89414c0a534Smrg
89514c0a534Smrg	prop1.name = SmProgram;
89614c0a534Smrg	prop1.type = SmARRAY8;
89714c0a534Smrg	prop1.num_vals = 1;
89814c0a534Smrg	prop1.vals = &prop1val;
89914c0a534Smrg	prop1val.value = Argv[0];
90014c0a534Smrg	prop1val.length = strlen (Argv[0]);
90114c0a534Smrg
90224047306Smrg	snprintf (userId, sizeof(userId), "%ld", (long)getuid());
90314c0a534Smrg	prop2.name = SmUserID;
90414c0a534Smrg	prop2.type = SmARRAY8;
90514c0a534Smrg	prop2.num_vals = 1;
90614c0a534Smrg	prop2.vals = &prop2val;
90714c0a534Smrg	prop2val.value = (SmPointer) userId;
90814c0a534Smrg	prop2val.length = strlen (userId);
90914c0a534Smrg
91014c0a534Smrg	prop3.name = SmRestartStyleHint;
91114c0a534Smrg	prop3.type = SmCARD8;
91214c0a534Smrg	prop3.num_vals = 1;
91314c0a534Smrg	prop3.vals = &prop3val;
91414c0a534Smrg	prop3val.value = (SmPointer) &hint;
91514c0a534Smrg	prop3val.length = 1;
91614c0a534Smrg
91714c0a534Smrg	props[0] = &prop1;
91814c0a534Smrg	props[1] = &prop2;
91914c0a534Smrg	props[2] = &prop3;
92014c0a534Smrg
92114c0a534Smrg	SmcSetProperties (smcConn, 3, props);
92214c0a534Smrg
92314c0a534Smrg	first_time = 0;
92414c0a534Smrg    }
92514c0a534Smrg
92614c0a534Smrg    if ((filename = WriteProxyFile ()) == NULL)
92714c0a534Smrg    {
92814c0a534Smrg	success = False;
92914c0a534Smrg	goto finishUp;
93014c0a534Smrg    }
93114c0a534Smrg
93214c0a534Smrg    prop1.name = SmRestartCommand;
93314c0a534Smrg    prop1.type = SmLISTofARRAY8;
93414c0a534Smrg
93514c0a534Smrg    prop1.vals = (SmPropValue *) malloc (
93614c0a534Smrg	(Argc + 4) * sizeof (SmPropValue));
93714c0a534Smrg
93814c0a534Smrg    if (!prop1.vals)
93914c0a534Smrg    {
94014c0a534Smrg	success = False;
94114c0a534Smrg	goto finishUp;
94214c0a534Smrg    }
94314c0a534Smrg
94414c0a534Smrg    numVals = 0;
94514c0a534Smrg
94614c0a534Smrg    for (i = 0; i < Argc; i++)
94714c0a534Smrg    {
94814c0a534Smrg	if (strcmp (Argv[i], "-clientId") == 0 ||
94914c0a534Smrg	    strcmp (Argv[i], "-restore") == 0)
95014c0a534Smrg	{
95114c0a534Smrg	    i++;
95214c0a534Smrg	}
95314c0a534Smrg	else
95414c0a534Smrg	{
95514c0a534Smrg	    prop1.vals[numVals].value = (SmPointer) Argv[i];
95614c0a534Smrg	    prop1.vals[numVals++].length = strlen (Argv[i]);
95714c0a534Smrg	}
95814c0a534Smrg    }
95914c0a534Smrg
96014c0a534Smrg    prop1.vals[numVals].value = (SmPointer) "-clientId";
96114c0a534Smrg    prop1.vals[numVals++].length = 9;
96214c0a534Smrg
96314c0a534Smrg    prop1.vals[numVals].value = (SmPointer) proxy_clientId;
96414c0a534Smrg    prop1.vals[numVals++].length = strlen (proxy_clientId);
96514c0a534Smrg
96614c0a534Smrg    prop1.vals[numVals].value = (SmPointer) "-restore";
96714c0a534Smrg    prop1.vals[numVals++].length = 8;
96814c0a534Smrg
96914c0a534Smrg    prop1.vals[numVals].value = (SmPointer) filename;
97014c0a534Smrg    prop1.vals[numVals++].length = strlen (filename);
97114c0a534Smrg
97214c0a534Smrg    prop1.num_vals = numVals;
97314c0a534Smrg
97414c0a534Smrg
97524047306Smrg    if (asprintf (&discardCommand, "rm %s", filename) == -1) {
97624047306Smrg	success = False;
97724047306Smrg	goto finishUp;
97824047306Smrg    }
97914c0a534Smrg    prop2.name = SmDiscardCommand;
98014c0a534Smrg    prop2.type = SmARRAY8;
98114c0a534Smrg    prop2.num_vals = 1;
98214c0a534Smrg    prop2.vals = &prop2val;
98314c0a534Smrg    prop2val.value = (SmPointer) discardCommand;
98414c0a534Smrg    prop2val.length = strlen (discardCommand);
98514c0a534Smrg
98614c0a534Smrg    props[0] = &prop1;
98714c0a534Smrg    props[1] = &prop2;
98814c0a534Smrg
98914c0a534Smrg    SmcSetProperties (smcConn, 2, props);
99014c0a534Smrg    free ((char *) prop1.vals);
99124047306Smrg    free (discardCommand);
99214c0a534Smrg
99314c0a534Smrg finishUp:
99414c0a534Smrg
99514c0a534Smrg    SmcSaveYourselfDone (smcConn, success);
99614c0a534Smrg    sent_save_done = 1;
99714c0a534Smrg
99814c0a534Smrg    if (filename)
99914c0a534Smrg	free (filename);
100014c0a534Smrg}
100114c0a534Smrg
100214c0a534Smrg
100314c0a534Smrg
1004bf2eeab3Smrgstatic void
1005bf2eeab3SmrgProxySaveYourselfCB(SmcConn smcConn, SmPointer clientData, int saveType,
1006bf2eeab3Smrg		    Bool shutdown, int interactStyle, Bool fast)
100714c0a534Smrg{
100814c0a534Smrg    /*
100914c0a534Smrg     * We want the proxy to respond to the Save Yourself after all
101014c0a534Smrg     * the regular XSMP clients have finished with the save (and possibly
101114c0a534Smrg     * interacted with the user).
101214c0a534Smrg     */
101314c0a534Smrg
101414c0a534Smrg    if (!SmcRequestSaveYourselfPhase2 (smcConn,
101514c0a534Smrg	ProxySaveYourselfPhase2CB, NULL))
101614c0a534Smrg    {
101714c0a534Smrg	SmcSaveYourselfDone (smcConn, False);
101814c0a534Smrg	sent_save_done = 1;
101914c0a534Smrg    }
102014c0a534Smrg    else
102114c0a534Smrg	sent_save_done = 0;
102214c0a534Smrg}
102314c0a534Smrg
102414c0a534Smrg
102514c0a534Smrg
1026bf2eeab3Smrgstatic void
1027bf2eeab3SmrgProxyDieCB(SmcConn smcConn, SmPointer clientData)
102814c0a534Smrg{
102914c0a534Smrg    SmcCloseConnection (proxy_smcConn, 0, NULL);
103014c0a534Smrg    XtRemoveInput (proxy_iceInputId);
103114c0a534Smrg
103214c0a534Smrg    if (die_count == proxy_count)
103314c0a534Smrg	exit (0);
103414c0a534Smrg    else
103514c0a534Smrg	ok_to_die = 1;
103614c0a534Smrg}
103714c0a534Smrg
103814c0a534Smrg
103914c0a534Smrg
1040bf2eeab3Smrgstatic void
1041bf2eeab3SmrgProxySaveCompleteCB(SmcConn smcConn, SmPointer clientData)
104214c0a534Smrg{
104314c0a534Smrg    ;
104414c0a534Smrg}
104514c0a534Smrg
104614c0a534Smrg
104714c0a534Smrg
1048bf2eeab3Smrgstatic void
1049bf2eeab3SmrgProxyShutdownCancelledCB(SmcConn smcConn, SmPointer clientData)
105014c0a534Smrg{
105114c0a534Smrg    if (!sent_save_done)
105214c0a534Smrg    {
105314c0a534Smrg	SmcSaveYourselfDone (smcConn, False);
105414c0a534Smrg	sent_save_done = 1;
105514c0a534Smrg    }
105614c0a534Smrg}
105714c0a534Smrg
105814c0a534Smrg
105914c0a534Smrg
1060bf2eeab3Smrgstatic Status
1061bf2eeab3SmrgConnectProxyToSM(char *previous_id)
106214c0a534Smrg{
106314c0a534Smrg    char errorMsg[256];
106414c0a534Smrg    unsigned long mask;
106514c0a534Smrg    SmcCallbacks callbacks;
106614c0a534Smrg    IceConn iceConn;
106714c0a534Smrg
106814c0a534Smrg    mask = SmcSaveYourselfProcMask | SmcDieProcMask |
106914c0a534Smrg	SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
107014c0a534Smrg
107114c0a534Smrg    callbacks.save_yourself.callback = ProxySaveYourselfCB;
107214c0a534Smrg    callbacks.save_yourself.client_data = (SmPointer) NULL;
107314c0a534Smrg
107414c0a534Smrg    callbacks.die.callback = ProxyDieCB;
107514c0a534Smrg    callbacks.die.client_data = (SmPointer) NULL;
107614c0a534Smrg
107714c0a534Smrg    callbacks.save_complete.callback = ProxySaveCompleteCB;
107814c0a534Smrg    callbacks.save_complete.client_data = (SmPointer) NULL;
107914c0a534Smrg
108014c0a534Smrg    callbacks.shutdown_cancelled.callback = ProxyShutdownCancelledCB;
108114c0a534Smrg    callbacks.shutdown_cancelled.client_data = (SmPointer) NULL;
108214c0a534Smrg
108314c0a534Smrg    proxy_smcConn = SmcOpenConnection (
108414c0a534Smrg	NULL, 			/* use SESSION_MANAGER env */
108514c0a534Smrg	(SmPointer) appContext,
108614c0a534Smrg	SmProtoMajor,
108714c0a534Smrg	SmProtoMinor,
108814c0a534Smrg	mask,
108914c0a534Smrg	&callbacks,
109014c0a534Smrg	previous_id,
109114c0a534Smrg	&proxy_clientId,
109214c0a534Smrg	256, errorMsg);
109314c0a534Smrg
109414c0a534Smrg    if (proxy_smcConn == NULL)
109514c0a534Smrg	return (0);
109614c0a534Smrg
109714c0a534Smrg    iceConn = SmcGetIceConnection (proxy_smcConn);
109814c0a534Smrg
109914c0a534Smrg    proxy_iceInputId = XtAppAddInput (
110014c0a534Smrg	    appContext,
110114c0a534Smrg	    IceConnectionNumber (iceConn),
110214c0a534Smrg            (XtPointer) XtInputReadMask,
110314c0a534Smrg	    ProcessIceMsgProc,
110414c0a534Smrg	    (XtPointer) iceConn);
110514c0a534Smrg
110614c0a534Smrg    return (1);
110714c0a534Smrg}
110814c0a534Smrg
110914c0a534Smrg
111014c0a534Smrg
1111bf2eeab3Smrgstatic void
1112bf2eeab3SmrgCheckForExistingWindows(Window root)
111314c0a534Smrg{
111414c0a534Smrg    Window dontCare1, dontCare2, *children, client_window;
111514c0a534Smrg    unsigned int nchildren, i;
111614c0a534Smrg    XCreateWindowEvent event;
111714c0a534Smrg
111814c0a534Smrg    /*
111914c0a534Smrg     * We query the root tree for all windows created thus far.
112014c0a534Smrg     * Note that at any moment after XQueryTree is called, a
112114c0a534Smrg     * window may be deleted.  So we must take extra care to make
112214c0a534Smrg     * sure a window really exists.
112314c0a534Smrg     */
112414c0a534Smrg
112514c0a534Smrg    XQueryTree (disp, root, &dontCare1, &dontCare2, &children, &nchildren);
112614c0a534Smrg
112714c0a534Smrg    for (i = 0; i < nchildren; i++)
112814c0a534Smrg    {
112914c0a534Smrg	event.window = children[i];
113014c0a534Smrg
113114c0a534Smrg	HandleCreate (&event);
113214c0a534Smrg
113314c0a534Smrg	caught_error = 0;
113414c0a534Smrg	XSetErrorHandler (MyErrorHandler);
113514c0a534Smrg
113614c0a534Smrg	client_window = XmuClientWindow (disp, children[i]);
113714c0a534Smrg
113814c0a534Smrg	XSetErrorHandler (NULL);
113914c0a534Smrg
114014c0a534Smrg	if (!caught_error && client_window != children[i])
114114c0a534Smrg	{
114214c0a534Smrg	    event.window = client_window;
114314c0a534Smrg	    HandleCreate (&event);
114414c0a534Smrg	}
114514c0a534Smrg    }
114614c0a534Smrg}
114714c0a534Smrg
114814c0a534Smrg
114914c0a534Smrg
115014c0a534Smrgint
115114c0a534Smrgmain (int argc, char *argv[])
115214c0a534Smrg{
115314c0a534Smrg    char *restore_filename = NULL;
115414c0a534Smrg    char *client_id = NULL;
115514c0a534Smrg    int i, zero = 0;
115614c0a534Smrg
115714c0a534Smrg    Argc = argc;
115814c0a534Smrg    Argv = argv;
115914c0a534Smrg
116014c0a534Smrg    for (i = 1; i < argc; i++)
116114c0a534Smrg    {
116214c0a534Smrg	if (argv[i][0] == '-')
116314c0a534Smrg	{
116414c0a534Smrg	    switch (argv[i][1])
116514c0a534Smrg	    {
116614c0a534Smrg	      case 'd':				/* -debug */
116714c0a534Smrg		debug = 1;
116814c0a534Smrg		continue;
116914c0a534Smrg
117014c0a534Smrg	      case 'c':				/* -clientId */
117124047306Smrg		if (++i >= argc) {
117224047306Smrg		    fprintf (stderr, "%s: -clientId requires an argument\n",
117324047306Smrg			     argv[0]);
117424047306Smrg		    goto usage;
117524047306Smrg		}
117614c0a534Smrg		client_id = argv[i];
117714c0a534Smrg		continue;
117814c0a534Smrg
117914c0a534Smrg	      case 'r':				/* -restore */
118024047306Smrg		if (++i >= argc) {
118124047306Smrg		    fprintf (stderr, "%s: -restore requires an argument\n",
118224047306Smrg			     argv[0]);
118324047306Smrg		    goto usage;
118424047306Smrg		}
118514c0a534Smrg		restore_filename = argv[i];
118614c0a534Smrg		continue;
118724047306Smrg
118824047306Smrg	      case 'v':
118924047306Smrg		puts (PACKAGE_STRING);
119024047306Smrg		exit (0);
119114c0a534Smrg	    }
119214c0a534Smrg	}
119314c0a534Smrg
119424047306Smrg	fprintf (stderr, "%s: unrecognized argument: %s\n", argv[0], argv[i]);
119524047306Smrg
119614c0a534Smrg    usage:
119714c0a534Smrg
119814c0a534Smrg	fprintf (stderr,
119924047306Smrg	    "usage:  %s [-clientId id] [-restore file] [-debug] [-version]\n",
120024047306Smrg		 argv[0]);
120114c0a534Smrg	exit (1);
120214c0a534Smrg    }
120314c0a534Smrg
120414c0a534Smrg
120514c0a534Smrg    XtToolkitInitialize ();
120614c0a534Smrg    appContext = XtCreateApplicationContext ();
120714c0a534Smrg
120814c0a534Smrg    if (!(disp = XtOpenDisplay (appContext, NULL, "SM-PROXY", "SM-PROXY",
120914c0a534Smrg	NULL, 0, &zero, NULL)))
121014c0a534Smrg    {
121114c0a534Smrg	fprintf (stderr, "smproxy: unable to open display\n");
121214c0a534Smrg	exit (1);
121314c0a534Smrg    }
121414c0a534Smrg
121514c0a534Smrg    if (restore_filename)
121614c0a534Smrg	ReadProxyFile (restore_filename);
121714c0a534Smrg
121814c0a534Smrg    if (!ConnectProxyToSM (client_id))
121914c0a534Smrg    {
122014c0a534Smrg	fprintf (stderr, "smproxy: unable to connect to session manager\n");
122114c0a534Smrg	exit (1);
122214c0a534Smrg    }
122314c0a534Smrg
122414c0a534Smrg    wmProtocolsAtom = XInternAtom (disp, "WM_PROTOCOLS", False);
122514c0a534Smrg    wmSaveYourselfAtom = XInternAtom (disp, "WM_SAVE_YOURSELF", False);
122614c0a534Smrg    wmStateAtom = XInternAtom (disp, "WM_STATE", False);
122714c0a534Smrg    smClientIdAtom = XInternAtom (disp, "SM_CLIENT_ID", False);
122814c0a534Smrg    wmClientLeaderAtom = XInternAtom (disp, "WM_CLIENT_LEADER", False);
122914c0a534Smrg
123014c0a534Smrg    for (i = 0; i < ScreenCount (disp); i++)
123114c0a534Smrg    {
123214c0a534Smrg	Window root = RootWindow (disp, i);
123314c0a534Smrg	XSelectInput (disp, root, SubstructureNotifyMask | PropertyChangeMask);
123414c0a534Smrg	CheckForExistingWindows (root);
123514c0a534Smrg    }
123614c0a534Smrg
123714c0a534Smrg    while (1)
123814c0a534Smrg    {
123914c0a534Smrg	XEvent event;
124014c0a534Smrg
124114c0a534Smrg	XtAppNextEvent (appContext, &event);
124214c0a534Smrg
124314c0a534Smrg	switch (event.type)
124414c0a534Smrg	{
124514c0a534Smrg	case CreateNotify:
124614c0a534Smrg	    HandleCreate (&event.xcreatewindow);
124714c0a534Smrg	    break;
124814c0a534Smrg
124914c0a534Smrg	case DestroyNotify:
125014c0a534Smrg	    HandleDestroy (&event.xdestroywindow);
125114c0a534Smrg	    break;
125214c0a534Smrg
125314c0a534Smrg	case PropertyNotify:
125414c0a534Smrg	    HandleUpdate (&event.xproperty);
125514c0a534Smrg	    break;
125614c0a534Smrg
125714c0a534Smrg	default:
125814c0a534Smrg	    XtDispatchEvent (&event);
125914c0a534Smrg	    break;
126014c0a534Smrg	}
126114c0a534Smrg    }
126214c0a534Smrg    exit(0);
126314c0a534Smrg}
1264