1 /*
2  * Motif
3  *
4  * Copyright (c) 1987-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <ctype.h>
32 #include <sys/param.h>
33 #include <X11/Intrinsic.h>
34 #include <X11/Shell.h>
35 #include <X11/Xatom.h>
36 #include <X11/SM/SM.h>
37 #include <Xm/XmP.h>
38 #include "WmGlobal.h"
39 #include "WmXSMP.h"
40 #ifdef WSM
41 # include "WmWrkspace.h"
42 # include <Dt/Session.h>
43 #endif
44 
45 #define FIX_1193
46 
47 typedef struct _ProxyClientInfo
48 {
49     int screen;
50     char *wmCommand;
51     char *wmClientMachine;
52     char *clientID;
53 } ProxyClientInfo;
54 
55 #define RESTORE_RESOURCE(pCD, resFlag) \
56 	((pCD)->ignoreWMSaveHints || !((pCD)->wmSaveHintFlags & (resFlag)))
57 #define SAVE_RESOURCE(pCD, resFlag) RESTORE_RESOURCE(pCD, resFlag)
58 
59 #define MAX_RESOURCE_LEN 1024
60 
61 #ifdef WSM
62 static char *dtwmFileName = "dtwm.db";
63 #else
64 static char *dtwmFileName = ".mwmclientdb";
65 # define EXTRA_FN_CHARS 20
66 #endif
67 
68 /* Fully-qualified resource names/classes. */
69 static char *xPositionStr = "%s.position.x";
70 static char *yPositionStr = "%s.position.y";
71 static char *widthSizeStr = "%s.size.width";
72 static char *heightSizeStr = "%s.size.height";
73 static char *initialStateStr = "%s.initialState";
74 static char *wmCommandStr = "%s.wmCommand";
75 static char *wmClientMachineStr = "%s.wmClientMachine";
76 static char *screenStr = "%s.screen";
77 #ifdef WSM
78 static char *workspacesStr = "%s.workspaces";
79 static char *iconXPosStr = "%s.iconPos.x.%s";
80 static char *iconYPosStr = "%s.iconPos.y.%s";
81 #else
82 static char *iconXPosStr = "%s.iconPos.x";
83 static char *iconYPosStr = "%s.iconPos.y";
84 #endif
85 
86 /* Header for private database. */
87 static char *dbHeader = "\
88 ! %s\n\
89 !\n\
90 .version: %s\n\
91 .dtwmID: %s\n";
92 
93 /* Format for client entries in database. */
94 static char *dbClientFormat = "\
95 !\n\
96 %s.%s: %s\n\
97 !\n";
98 static char *intArg = ": %d\n";
99 static char *strArg = ": %s\n";
100 static char *normalStateStr = "NormalState";
101 static char *iconicStateStr = "IconicState";
102 
103 static char *XSMPClientStr = "Client";
104 static char *proxyClientStr = "ProxyClient";
105 
106 #ifndef WSM
107 static char *dbFileArgStr = "-session";
108 #endif
109 
110 /* Flag to tell us how to treat ProxyClient info. */
111 static Boolean smClientDBCheckpointed = False;
112 
113 /*
114  *  Prototypes
115  */
116 /* Session mgmt callbacks. */
117 static void smSaveYourselfCallback(Widget, XtPointer, XtPointer);
118 static void smDieCallback(Widget, XtPointer, XtPointer);
119 
120 /* Build client database file name. */
121 static void buildDBFileName(char [MAXPATHLEN], Boolean);
122 #ifndef WSM
123 /*
124  *Get clientDB name according to argv; set according to dbFileName.
125  */
126 static void getClientDBName(void);
127 static void setClientDBName(void);
128 static char **getNewRestartCmd(void);
129 static void freeNewRestartCmd(char **);
130 #endif /* ! WSM */
131 
132 #ifdef WSM
133 /* Get string of client's workspaces. */
134 static char *getClientWorkspaces(ClientData *);
135 #endif
136 
137 /* List-of-clients utilities. */
138 static Boolean addClientToList(ClientData ***, int *, ClientData *);
139 static int clientWorkspaceCompare(const void *, const void *);
140 
141 /* XSMP/Proxy functions to save/restore resources. */
142 static char *getClientResource(char *, char *);
143 static char *getXSMPResource(ClientData *, int, char *);
144 static void getClientGeometry(ClientData *, int *, int *,
145 			      unsigned int *, unsigned int *);
146 static Boolean getProxyClientInfo(ClientData *, ProxyClientInfo *);
147 static Bool cmpProxyClientProc(XrmDatabase *, XrmBindingList,
148 			       XrmQuarkList, XrmRepresentation *,
149 			       XrmValue *, XPointer);
150 static char *findProxyClientID(ClientData *);
151 static Boolean findXSMPClientDBMatch(ClientData *, char **);
152 static Boolean findProxyClientDBMatch(ClientData *, char **);
153 static Boolean saveXSMPClient(FILE *, ClientData *);
154 static Boolean saveProxyClient(FILE *, ClientData *, int);
155 static void dbRemoveProxyClientEntry(char *);
156 
157 static void
smSaveYourselfCallback(Widget w,XtPointer clientData,XtPointer callData)158 smSaveYourselfCallback(Widget w, XtPointer clientData, XtPointer callData)
159 {
160     XtCheckpointToken cpToken = (XtCheckpointToken)callData;
161     XrmDatabase newClientDB;
162     int scr;
163     static Boolean firstTime = True;
164 
165     /*
166      *  This callback will be called on connection to the Session Manager.
167      *  At that time, we don't want to save any state, and we don't
168      *  want to request the second phase.
169      */
170     if (firstTime)
171     {
172 	firstTime = False;
173 	return;
174     }
175 
176     /* Only respond to Local and Both save requests. */
177     if ((cpToken->save_type != SmSaveLocal) &&
178 	(cpToken->save_type != SmSaveBoth))
179 	return;
180 
181     if (cpToken->shutdown &&
182 	(cpToken->cancel_shutdown ||
183 	 cpToken->request_cancel ||
184 	 !cpToken->save_success))
185 	return;  /* Return, maintaining current state */
186 
187     /* If first phase, request notification when all other clients saved. */
188     if (cpToken->phase == 1)
189     {
190 	cpToken->request_next_phase = True;
191 	return;
192     }
193 
194 #ifdef WSM
195     /* Second phase: all other clients saved; now I can save myself. */
196     /* Copied from WmEvent.c. */
197     for (scr = 0; scr < wmGD.numScreens; scr++)
198     {
199 	if (wmGD.Screens[scr].managed)
200 	{
201 	    /*
202 	     * Write out current workspace, frontpanel
203 	     * position and iconbox position and size.
204 	     */
205 	    SaveResources(&wmGD.Screens[scr]);
206 	}
207     }
208 #endif
209 
210     /*
211      *  NEW FOR SESSION MANAGEMENT: Write private client resource database.
212      *  Destroy old client database and save new one.
213      */
214     if ((newClientDB = SaveClientResourceDB())
215 	!= (XrmDatabase)NULL)
216     {
217 	if (wmGD.clientResourceDB != (XrmDatabase)NULL)
218 	    XrmDestroyDatabase(wmGD.clientResourceDB);
219 	wmGD.clientResourceDB = newClientDB;
220 	smClientDBCheckpointed = True;
221 
222 #ifndef WSM
223 	/* Set new session properties if wmGD.dbFileName is valid. */
224 	if (wmGD.dbFileName != (char *)NULL)
225 	{
226 	    char **newRestartCmd, **ptr;
227 	    char *newDiscardCmd[4];
228 	    Arg args[10];
229 	    int nargs;
230 
231 	    newDiscardCmd[0] = "rm";
232 	    newDiscardCmd[1] = "-f";
233 	    newDiscardCmd[2] = wmGD.dbFileName;
234 	    newDiscardCmd[3] = (char *)NULL;
235 
236 	    newRestartCmd = getNewRestartCmd();
237 
238 	    nargs = 0;
239 	    XtSetArg(args[nargs], XtNrestartCommand, newRestartCmd); nargs++;
240 	    XtSetArg(args[nargs], XtNdiscardCommand, newDiscardCmd); nargs++;
241 	    XtSetValues(wmGD.topLevelW, args, nargs);
242 
243 	    freeNewRestartCmd(newRestartCmd);
244 	}
245 #endif /* ! WSM */
246     }
247 }
248 
249 static void
smDieCallback(Widget w,XtPointer clientData,XtPointer callData)250 smDieCallback(Widget w, XtPointer clientData, XtPointer callData)
251 {
252     /* We assume we've saved our state by the time this is called. */
253     ExitWM(0);
254 }
255 
256 static void
buildDBFileName(char fileNameBuf[MAXPATHLEN],Boolean doingSave)257 buildDBFileName(char fileNameBuf[MAXPATHLEN], Boolean doingSave)
258 {
259 #ifdef WSM
260 
261     char *savePath = (char *)NULL;
262 
263     fileNameBuf[0] = '\0';
264     if (doingSave)
265     {
266 	char *saveFile = (char *)NULL;
267 	char *ptr;
268 
269 	if (DtSessionSavePath(wmGD.topLevelW, &savePath, &saveFile))
270 	{
271 	    XtFree(saveFile);
272 
273 	    if ((ptr = strrchr(savePath, '/')) != (char *)NULL)
274 		*ptr = '\0';
275 
276 	    if (strlen(savePath) + strlen(dtwmFileName) + 2 < MAXPATHLEN)
277 		sprintf(fileNameBuf, "%s/%s", savePath, dtwmFileName);
278 
279 	    XtFree(savePath);
280 	}
281     }
282     else
283     {
284 	if (DtSessionRestorePath(wmGD.topLevelW, &savePath, dtwmFileName))
285 	{
286 	    if ((int)strlen(savePath) < MAXPATHLEN)
287 		strcpy(fileNameBuf, savePath);
288 
289 	    XtFree(savePath);
290 	}
291     }
292 
293     if (fileNameBuf[0] == '\0')
294 	strcpy(fileNameBuf, dtwmFileName);
295 
296 #else
297 
298     strcpy(fileNameBuf, (wmGD.dbFileName == (char *)NULL) ?
299 	   dtwmFileName : wmGD.dbFileName);
300 
301 #endif
302 }
303 
304 #ifndef WSM
305 
306 /*
307  *  See if dbFileArgStr specified on command line.  Save subsequent arg;
308  *  if not, see if resource set; if not, put files in user's home directory.
309  *  NOTE: we allocate extra space for the filename so we can append numbers
310  *  without reallocating in setClientDBName.
311  */
312 static void
getClientDBName(void)313 getClientDBName(void)
314 {
315     char **argP;
316 
317     /* See if DB filename specified on command line. */
318     wmGD.dbFileName = (char *)NULL;
319 
320     if (wmGD.argv != (char **)NULL)
321     {
322 	for (argP = wmGD.argv; *argP != (char *)NULL; argP++)
323 	{
324 	    if (strcmp(*argP, dbFileArgStr) == 0)
325 	    {
326 		if (*(++argP) != (char *)NULL)
327 		{
328 		    if ((wmGD.dbFileName =
329 			 (char *)XtMalloc((strlen(*argP) + 1 +
330 					   EXTRA_FN_CHARS) *
331 					  sizeof(char)))
332 			!= (char *)NULL)
333 			strcpy(wmGD.dbFileName, *argP);
334 		}
335 		break;
336 	    }
337 	}
338     }
339 
340     /* Check resource if necessary. */
341     if (wmGD.dbFileName == (char *)NULL)
342     {
343 	if (wmGD.sessionClientDB != (String)NULL)
344 	{
345 	    if ((wmGD.dbFileName =
346 		 (char *)XtMalloc((strlen(wmGD.sessionClientDB) + 1 +
347 				   EXTRA_FN_CHARS) *
348 				  sizeof(char)))
349 		!= (char *)NULL)
350 		strcpy(wmGD.dbFileName, wmGD.sessionClientDB);
351 	}
352     }
353 
354     if (wmGD.dbFileName == (char *)NULL)
355     {
356 	char *homeDir = XmeGetHomeDirName();
357 
358 	if ((wmGD.dbFileName =
359 	     (char *)XtMalloc((strlen(homeDir) + strlen(dtwmFileName) + 2 +
360 			       EXTRA_FN_CHARS) * sizeof(char)))
361 	    != (char *)NULL)
362 	    sprintf(wmGD.dbFileName, "%s/%s", homeDir, dtwmFileName);
363     }
364 }
365 
366 /*
367  *  See comments above in getClientDBName.
368  */
369 static void
setClientDBName(void)370 setClientDBName(void)
371 {
372     char *ptr;
373 
374     if (wmGD.dbFileName == (char *)NULL)
375 	return;
376 
377     /* Change trailing ".<number>" to ".<number+1>" */
378     if ((ptr = strrchr(wmGD.dbFileName, '.')) != (char *)NULL)
379     {
380 	char *p1;
381 
382 	for (p1 = ++ptr; *p1 != '\0'; p1++)
383 	{
384 	    if (!isdigit(*p1))
385 		break;
386 	}
387 
388 	if (*p1 == '\0')
389 	{
390 	    int numSuffix;
391 
392 	    numSuffix = atoi(ptr) + 1;
393 	    sprintf(ptr, "%d", numSuffix);
394 
395 	    /* Success!  We're all done here. */
396 	    return;
397 	}
398     }
399 
400     /* Otherwise, append ".0" to filename. */
401     strcat(wmGD.dbFileName, ".0");
402 }
403 
404 static char **
getNewRestartCmd(void)405 getNewRestartCmd(void)
406 {
407     char **argP;
408     int argc, i;
409     int fileArgIndex = -1;
410     Arg args[10];
411     int nargs;
412     char **restartCmd;
413     char **newRestartCmd;
414 
415     nargs = 0;
416     XtSetArg(args[nargs], XtNrestartCommand, &restartCmd); nargs++;
417     XtGetValues(wmGD.topLevelW, args, nargs);
418 
419     if (restartCmd == (char **)NULL)
420 	return (char **)NULL;
421 
422     for (argc = 0, argP = restartCmd; *argP != (char *)NULL; argP++, argc++)
423     {
424 	if (strcmp(*argP, dbFileArgStr) == 0)
425 	{
426 	    if (*(++argP) == (char *)NULL)
427 		break;
428 
429 	    fileArgIndex = argc++; /* Point at dbFileArgStr, not filename */
430 	}
431     }
432 
433     if (fileArgIndex < 0)
434     {
435 	fileArgIndex = argc;
436 	argc += 2;
437     }
438 
439     if ((newRestartCmd = (char **)XtMalloc((argc + 1) * sizeof(char *)))
440 	== (char **)NULL)
441 	return (char **)NULL;
442 
443     for (i = 0; i < argc; i++)
444     {
445 	if (i != fileArgIndex)
446 	{
447 	    newRestartCmd[i] = XtNewString(restartCmd[i]);
448 	}
449 	else
450 	{
451 	    newRestartCmd[i++] = XtNewString(dbFileArgStr);
452 	    newRestartCmd[i] = XtNewString(wmGD.dbFileName);
453 	}
454     }
455     newRestartCmd[i] = (char *)NULL;
456 
457     return newRestartCmd;
458 }
459 
460 static void
freeNewRestartCmd(char ** restartCmd)461 freeNewRestartCmd(char **restartCmd)
462 {
463 #ifdef FIX_1193
464     if(restartCmd)
465     {
466 	char **tmp = restartCmd;
467 	while (*restartCmd != (char *)NULL)
468 		XtFree(*(restartCmd++));
469 
470 	XtFree((char *)tmp);
471     }
472 #else
473     while (*restartCmd != (char *)NULL)
474 	XtFree(*(restartCmd++));
475 
476     XtFree((char *)restartCmd);
477 #endif
478 }
479 
480 #endif /* ! WSM */
481 
482 #ifdef WSM
483 
484 static char *
getClientWorkspaces(ClientData * pCD)485 getClientWorkspaces(ClientData *pCD)
486 {
487     WmScreenData *pSD = pCD->pSD;
488     WmWorkspaceData *pWS;
489 
490     /* Should we use _DtWmParseMakeQuotedString() when looking at */
491     /* the name of the workspace, as is done in WmWrkspace.c? */
492 
493     /* Easy but slow way to do this would be to use XGetAtomName(). */
494     /* To avoid XServer round trips (and to weed out invalid WS names) */
495     /* we look through workspaces attached to this screen for ID matches. */
496     char *cwsP, *tmpP, *wsNameP;
497     int pLen = 0;
498     int i;
499 
500     for (i = 0; i < pCD->numInhabited; i++)
501     {
502 	if ((pWS = GetWorkspaceData(pSD, pCD->pWsList[i].wsID))
503 	    != (WmWorkspaceData *)NULL)
504 	{
505 	    wsNameP = pWS->name;
506 	    if (pLen == 0)
507 	    {
508 		pLen = strlen(wsNameP) + 1;  /* 1 for null termination */
509 		if ((cwsP = (char *)XtMalloc(pLen * sizeof(char)))
510 		    == (char *)NULL)
511 		    return (char *)NULL;
512 
513 		strcpy(cwsP, wsNameP);
514 	    }
515 	    else
516 	    {
517 		pLen += strlen(wsNameP) + 1;  /* 1 for space */
518 		if ((tmpP = (char *)XtRealloc(cwsP, pLen * sizeof(char)))
519 		    == (char *)NULL)
520 		{
521 		    XtFree((char *)cwsP);
522 		    return (char *)NULL;
523 		}
524 		cwsP = tmpP;
525 		strcat(cwsP, " ");
526 		strcat(cwsP, wsNameP);
527 	    }
528 	}
529     }
530 
531     return cwsP;
532 }
533 
534 #endif /* WSM */
535 
536 static Boolean
addClientToList(ClientData *** cdList,int * nClients,ClientData * pCD)537 addClientToList(ClientData ***cdList, int *nClients, ClientData *pCD)
538 {
539     ClientData **newPtr = (ClientData **)
540 	XtRealloc((char *)*cdList, (*nClients + 1) * sizeof(ClientData *));
541 
542     if (newPtr == (ClientData **)NULL)
543     {
544 	if (*cdList != (ClientData **)NULL)
545 	    XtFree((char *)*cdList);
546 	return False;
547     }
548 
549     *cdList = newPtr;
550     newPtr[*nClients] = pCD;
551     (*nClients)++;
552 
553     return True;
554 }
555 
556 static int
clientWorkspaceCompare(const void * ppCD1,const void * ppCD2)557 clientWorkspaceCompare(const void *ppCD1, const void *ppCD2)
558 {
559     ClientData *pCD1 = *(ClientData **)ppCD1;
560     ClientData *pCD2 = *(ClientData **)ppCD2;
561     int screenDiff;
562 
563     /* Sort first by screen. */
564     if ((screenDiff = pCD1->pSD->screen - pCD2->pSD->screen) != 0)
565 	return screenDiff;
566 
567 #ifdef WSM
568 
569     /* If same screen, sort by workspace id. */
570     /* How do we handle clients that live in more than one workspace? */
571     /* For now, pick the "current" one - if not in active workspace, */
572     /* this will simply be the first one in the client's list. */
573     return (int)(pCD1->pWsList[pCD1->currentWsc].wsID -
574 		 pCD2->pWsList[pCD2->currentWsc].wsID);
575 
576 #else
577 
578     /* If no WSM, must be in same workspace if screen is same! */
579     return 0;
580 
581 #endif
582 }
583 
584 /*
585  *  Assumes: wmGD.clientResourceDB is non-NULL
586  */
587 static char *
getClientResource(char * clientID,char * fmtStr)588 getClientResource(char *clientID, char *fmtStr)
589 {
590     char resourceBuf[MAX_RESOURCE_LEN];
591     char *resourceType;
592     XrmValue resourceValue;
593 
594     sprintf(resourceBuf, fmtStr, clientID);
595     if (XrmGetResource(wmGD.clientResourceDB, resourceBuf, resourceBuf,
596 		       &resourceType, &resourceValue))
597 	return (char *)resourceValue.addr;
598 
599     return (char *)NULL;
600 }
601 
602 /*
603  *  Assumes: pCD has non-NULL smClientID;
604  *           wmGD.clientResourceDB is non-NULL
605  */
606 static char *
getXSMPResource(ClientData * pCD,int resourceFlag,char * fmtStr)607 getXSMPResource(ClientData *pCD, int resourceFlag, char *fmtStr)
608 {
609     if (RESTORE_RESOURCE(pCD, resourceFlag))
610 	return getClientResource(pCD->smClientID, fmtStr);
611 
612     return (char *)NULL;
613 }
614 
615 /*
616  *  Return True if client is XSMP, False otherwise.
617  */
618 static Boolean
findXSMPClientDBMatch(ClientData * pCD,char ** workSpaceNamesP)619 findXSMPClientDBMatch(ClientData *pCD, char **workSpaceNamesP)
620 {
621     if (pCD->smClientID != (String)NULL)
622     {
623 	if (wmGD.clientResourceDB != (XrmDatabase)NULL)
624 	{
625 	    char *resourcePtr;
626 
627 	    if ((resourcePtr = getXSMPResource(pCD, WMSAVE_X, xPositionStr))
628 		!= (char *)NULL)
629 	    {
630 		pCD->clientX = atoi(resourcePtr);
631 		pCD->clientFlags |= SM_X;
632 	    }
633 
634 	    if ((resourcePtr = getXSMPResource(pCD, WMSAVE_Y, yPositionStr))
635 		!= (char *)NULL)
636 	    {
637 		pCD->clientY = atoi(resourcePtr);
638 		pCD->clientFlags |= SM_Y;
639 	    }
640 
641 #ifndef WSM
642 	    if ((resourcePtr =
643 		 getXSMPResource(pCD, WMSAVE_ICON_X, iconXPosStr))
644 		!= (char *)NULL)
645 	    {
646 		ICON_X(pCD) = atoi(resourcePtr);
647 		pCD->clientFlags |= SM_ICON_X;
648 	    }
649 
650 	    if ((resourcePtr =
651 		 getXSMPResource(pCD, WMSAVE_ICON_Y, iconYPosStr))
652 		!= (char *)NULL)
653 	    {
654 		ICON_Y(pCD) = atoi(resourcePtr);
655 		pCD->clientFlags |= SM_ICON_Y;
656 	    }
657 #endif
658 
659 	    if ((resourcePtr = getXSMPResource(pCD, WMSAVE_WIDTH,
660 					       widthSizeStr))
661 		!= (char *)NULL)
662 	    {
663 		pCD->clientWidth = atoi(resourcePtr);
664 		pCD->clientFlags |= SM_WIDTH;
665 	    }
666 
667 	    if ((resourcePtr = getXSMPResource(pCD, WMSAVE_HEIGHT,
668 					       heightSizeStr))
669 		!= (char *)NULL)
670 	    {
671 		pCD->clientHeight = atoi(resourcePtr);
672 		pCD->clientFlags |= SM_HEIGHT;
673 	    }
674 
675 	    if ((resourcePtr = getXSMPResource(pCD, WMSAVE_STATE,
676 					       initialStateStr))
677 		!= (char *)NULL)
678 	    {
679 		pCD->clientState =
680 		    (strcmp(resourcePtr, normalStateStr) == 0) ?
681 			NORMAL_STATE : MINIMIZED_STATE;
682 		pCD->clientFlags |= SM_CLIENT_STATE;
683 	    }
684 
685 #ifdef WSM
686 	    if ((workSpaceNamesP != (char **)NULL) &&
687 		((resourcePtr = getXSMPResource(pCD, WMSAVE_WORKSPACES,
688 						workspacesStr))
689 		 != (char *)NULL))
690 	    {
691 		*workSpaceNamesP = XtNewString(resourcePtr);
692 	    }
693 #endif
694 	}
695 
696 	/* Always return True for XSMP clients. */
697 	return True;
698     }
699 
700     return False;
701 }
702 
703 static Boolean
getProxyClientInfo(ClientData * pCD,ProxyClientInfo * proxyClientInfo)704 getProxyClientInfo(ClientData *pCD, ProxyClientInfo *proxyClientInfo)
705 {
706     XTextProperty textProperty;
707     unsigned long i;
708 
709     /* WM_COMMAND is required; WM_CLIENT_MACHINE is optional. */
710     if (!XGetTextProperty(wmGD.display, pCD->client, &textProperty,
711 			  XA_WM_COMMAND))
712 	return False;
713 
714     if ((textProperty.encoding != XA_STRING) ||
715 	(textProperty.format != 8) ||
716 	(textProperty.value[0] == '\0'))
717     {
718 	if (textProperty.value)
719 	    free((char *)textProperty.value);
720 
721 	return False;
722     }
723 
724     /* Convert embedded NULL characters to space characters. */
725     /* (If last char is NULL, leave it alone) */
726     for (i = 0; i < textProperty.nitems - 1; i++)
727     {
728 	if (textProperty.value[i] == '\0')
729 	    textProperty.value[i] = ' ';
730     }
731 
732     proxyClientInfo->screen = pCD->pSD->screen;
733     proxyClientInfo->wmCommand = (char *)textProperty.value;
734 
735     /* Since WM_CLIENT_MACHINE is optional, don't fail if not found. */
736     if (XGetWMClientMachine(wmGD.display, pCD->client, &textProperty))
737 	proxyClientInfo->wmClientMachine = (char *)textProperty.value;
738     else proxyClientInfo->wmClientMachine = (char *)NULL;
739 
740     proxyClientInfo->clientID = (char *)NULL;
741 
742     return True;
743 }
744 
745 /*
746  *  IMPORTANT: This function is called by XrmEnumerateDatabase().
747  *  It calls other Xrm*() functions - if dtwm is threaded, THIS
748  *  WILL HANG.  For now, dtwm is NOT threaded, so no problem.
749  */
750 static Bool
cmpProxyClientProc(XrmDatabase * clientDB,XrmBindingList bindingList,XrmQuarkList quarkList,XrmRepresentation * reps,XrmValue * value,XPointer uData)751 cmpProxyClientProc(XrmDatabase *clientDB, XrmBindingList bindingList,
752 		   XrmQuarkList quarkList, XrmRepresentation *reps,
753 		   XrmValue *value, XPointer uData)
754 {
755     char *clientScreen;
756     char *wmCommand;
757     char *wmClientMachine;
758     char *clientID = (char *)value->addr;
759     ProxyClientInfo *proxyClientInfo = (ProxyClientInfo *)uData;
760 
761     if (((wmCommand =
762 	  getClientResource(clientID, wmCommandStr)) == (char *)NULL) ||
763 	(strcmp(wmCommand, proxyClientInfo->wmCommand) != 0) ||
764 	((clientScreen =
765 	  getClientResource(clientID, screenStr)) == (char *)NULL) ||
766 	(atoi(clientScreen) != proxyClientInfo->screen))
767 	return FALSE;
768 
769     /* So far so good.  If WM_CLIENT_MACHINE missing from either, */
770     /* or if it is set in both and it's the same, we've got a match! */
771     if (!proxyClientInfo->wmClientMachine ||
772 	((wmClientMachine =
773 	  getClientResource(clientID, wmClientMachineStr)) == (char *)NULL) ||
774 	(strcmp(proxyClientInfo->wmClientMachine, wmClientMachine) == 0))
775     {
776 	proxyClientInfo->clientID = clientID;
777 	return TRUE;
778     }
779 
780     return FALSE;
781 }
782 
783 static char *
findProxyClientID(ClientData * pCD)784 findProxyClientID(ClientData *pCD)
785 {
786     ProxyClientInfo proxyClientInfo;
787     char *clientID = (char *)NULL;
788     static XrmName proxyName[2] = {NULLQUARK, NULLQUARK};
789     static XrmClass proxyClass[2] = {NULLQUARK, NULLQUARK};
790 
791     if (proxyName[0] == NULLQUARK)
792     {
793 	proxyName[0] = XrmStringToName(proxyClientStr);
794 	proxyClass[0] = XrmStringToClass(proxyClientStr);
795     }
796 
797     /*
798      *  We need to match the screen and
799      *  the WM_COMMAND and WM_CLIENT_MACHINE properties.
800      */
801     if (!getProxyClientInfo(pCD, &proxyClientInfo))
802 	return clientID;
803 
804     if (XrmEnumerateDatabase(wmGD.clientResourceDB, proxyName, proxyClass,
805 			     XrmEnumOneLevel, cmpProxyClientProc,
806 			     (XPointer)&proxyClientInfo))
807 	clientID = proxyClientInfo.clientID;
808 
809     if (proxyClientInfo.wmCommand)
810 	free(proxyClientInfo.wmCommand);
811     if (proxyClientInfo.wmClientMachine)
812 	free(proxyClientInfo.wmClientMachine);
813 
814     return clientID;
815 }
816 
817 /*
818  *  Return True if client is *not* XSMP and is listed in the resource DB
819  *  and no checkpoint done yet.  Also remove entry from DB if found.
820  */
821 static Boolean
findProxyClientDBMatch(ClientData * pCD,char ** workSpaceNamesP)822 findProxyClientDBMatch(ClientData *pCD, char **workSpaceNamesP)
823 {
824     if ((pCD->smClientID == (String)NULL) &&
825 	(wmGD.clientResourceDB != (XrmDatabase)NULL) &&
826 	(!smClientDBCheckpointed))
827     {
828 	char *proxyClientID;
829 
830 	if ((proxyClientID = findProxyClientID(pCD)) != (char *)NULL)
831 	{
832 	    char *resourcePtr;
833 
834 	    if ((resourcePtr =
835 		 getClientResource(proxyClientID, xPositionStr))
836 		!= (char *)NULL)
837 	    {
838 		pCD->clientX = atoi(resourcePtr);
839 		pCD->clientFlags |= SM_X;
840 	    }
841 
842 	    if ((resourcePtr =
843 		 getClientResource(proxyClientID, yPositionStr))
844 		!= (char *)NULL)
845 	    {
846 		pCD->clientY = atoi(resourcePtr);
847 		pCD->clientFlags |= SM_Y;
848 	    }
849 
850 #ifndef WSM
851 	    if ((resourcePtr =
852 		 getClientResource(proxyClientID, iconXPosStr))
853 		!= (char *)NULL)
854 	    {
855 		ICON_X(pCD) = atoi(resourcePtr);
856 		pCD->clientFlags |= SM_ICON_X;
857 	    }
858 
859 	    if ((resourcePtr =
860 		 getClientResource(proxyClientID, iconYPosStr))
861 		!= (char *)NULL)
862 	    {
863 		ICON_Y(pCD) = atoi(resourcePtr);
864 		pCD->clientFlags |= SM_ICON_Y;
865 	    }
866 #endif
867 
868 	    if ((resourcePtr =
869 		 getClientResource(proxyClientID, widthSizeStr))
870 		!= (char *)NULL)
871 	    {
872 		pCD->clientWidth = atoi(resourcePtr);
873 		pCD->clientFlags |= SM_WIDTH;
874 	    }
875 
876 	    if ((resourcePtr =
877 		 getClientResource(proxyClientID, heightSizeStr))
878 		!= (char *)NULL)
879 	    {
880 		pCD->clientHeight = atoi(resourcePtr);
881 		pCD->clientFlags |= SM_HEIGHT;
882 	    }
883 
884 	    if ((resourcePtr =
885 		 getClientResource(proxyClientID, initialStateStr))
886 		!= (char *)NULL)
887 	    {
888 		pCD->clientState =
889 		    (strcmp(resourcePtr, normalStateStr) == 0) ?
890 			NORMAL_STATE : MINIMIZED_STATE;
891 		pCD->clientFlags |= SM_CLIENT_STATE;
892 	    }
893 
894 #ifdef WSM
895 	    if ((workSpaceNamesP != (char **)NULL) &&
896 		((resourcePtr =
897 		  getClientResource(proxyClientID, workspacesStr))
898 		 != (char *)NULL))
899 	    {
900 		*workSpaceNamesP = XtNewString(resourcePtr);
901 	    }
902 #endif
903 
904 #ifndef WSM
905 	    /* This is done in LoadClientIconPositions() if WSM defined. */
906 	    dbRemoveProxyClientEntry(proxyClientID);
907 #endif
908 
909 	    return True;
910 	}
911     }
912 
913     return False;
914 }
915 
916 /*
917  *  Translate the client geometry into what's needed on restore.
918  */
919 static void
getClientGeometry(ClientData * pCD,int * clientX,int * clientY,unsigned int * clientWd,unsigned int * clientHt)920 getClientGeometry(ClientData *pCD, int *clientX, int *clientY,
921 		  unsigned int *clientWd, unsigned int *clientHt)
922 {
923     *clientX = pCD->clientX;
924     *clientY = pCD->clientY;
925     *clientWd = (pCD->widthInc != 0) ?
926 	(pCD->clientWidth - pCD->baseWidth) / pCD->widthInc :
927 	    pCD->clientWidth;
928     *clientHt = (pCD->heightInc != 0) ?
929 	(pCD->clientHeight - pCD->baseHeight) / pCD->heightInc :
930 	    pCD->clientHeight;
931 }
932 
933 /*
934  *  Assumes: pCD->smClientID is not NULL
935  */
936 static Boolean
saveXSMPClient(FILE * fp,ClientData * pCD)937 saveXSMPClient(FILE *fp, ClientData *pCD)
938 {
939     int clientX, clientY;
940     unsigned int clientWd, clientHt;
941     char *clientID = pCD->smClientID;
942 
943     fprintf(fp, dbClientFormat, XSMPClientStr, clientID, clientID);
944 
945     getClientGeometry(pCD, &clientX, &clientY, &clientWd, &clientHt);
946 
947     if (SAVE_RESOURCE(pCD, WMSAVE_X))
948     {
949 	fprintf(fp, xPositionStr, clientID);
950 	fprintf(fp, intArg, clientX);
951     }
952 
953     if (SAVE_RESOURCE(pCD, WMSAVE_Y))
954     {
955 	fprintf(fp, yPositionStr, clientID);
956 	fprintf(fp, intArg, clientY);
957     }
958 
959     if (!pCD->pSD->useIconBox)
960     {
961 #ifdef WSM
962 	WmScreenData *pSD = pCD->pSD;
963 	WmWorkspaceData *pWS;
964 	int i;
965 
966 	for (i = 0; i < pCD->numInhabited; i++)
967 	{
968 	    if ((pWS = GetWorkspaceData(pSD, pCD->pWsList[i].wsID))
969 		!= (WmWorkspaceData *)NULL)
970 	    {
971 		if (SAVE_RESOURCE(pCD, WMSAVE_ICON_X))
972 		{
973 		    fprintf(fp, iconXPosStr, clientID, pWS->name);
974 		    fprintf(fp, intArg, pCD->pWsList[i].iconX);
975 		}
976 
977 		if (SAVE_RESOURCE(pCD, WMSAVE_ICON_Y))
978 		{
979 		    fprintf(fp, iconYPosStr, clientID, pWS->name);
980 		    fprintf(fp, intArg, pCD->pWsList[i].iconY);
981 		}
982 	    }
983 	}
984 #else
985 	if (SAVE_RESOURCE(pCD, WMSAVE_ICON_X))
986 	{
987 	    fprintf(fp, iconXPosStr, clientID);
988 	    fprintf(fp, intArg, ICON_X(pCD));
989 	}
990 
991 	if (SAVE_RESOURCE(pCD, WMSAVE_ICON_Y))
992 	{
993 	    fprintf(fp, iconYPosStr, clientID);
994 	    fprintf(fp, intArg, ICON_Y(pCD));
995 	}
996 #endif
997     }
998 
999     if (SAVE_RESOURCE(pCD, WMSAVE_WIDTH))
1000     {
1001 	fprintf(fp, widthSizeStr, clientID);
1002 	fprintf(fp, intArg, clientWd);
1003     }
1004 
1005     if (SAVE_RESOURCE(pCD, WMSAVE_HEIGHT))
1006     {
1007 	fprintf(fp, heightSizeStr, clientID);
1008 	fprintf(fp, intArg, clientHt);
1009     }
1010 
1011     if (SAVE_RESOURCE(pCD, WMSAVE_STATE))
1012     {
1013 	int clientState;
1014 
1015 #ifdef WSM
1016 	clientState = pCD->clientState & ~UNSEEN_STATE;
1017 #else
1018 	clientState = pCD->clientState;
1019 #endif
1020 
1021 	fprintf(fp, initialStateStr, clientID);
1022 	fprintf(fp, strArg, (clientState == NORMAL_STATE) ?
1023 		normalStateStr : iconicStateStr);
1024     }
1025 
1026 #ifdef WSM
1027     if (SAVE_RESOURCE(pCD, WMSAVE_WORKSPACES))
1028     {
1029 	char *clientWorkspaces = getClientWorkspaces(pCD);
1030 
1031 	if (clientWorkspaces != (char *)NULL)
1032 	{
1033 	    fprintf(fp, workspacesStr, clientID);
1034 	    fprintf(fp, strArg, clientWorkspaces);
1035 	    XtFree(clientWorkspaces);
1036 	}
1037     }
1038 #endif
1039 
1040     return True;
1041 }
1042 
1043 /*
1044  *  Assumes: pCD->smClientID is NULL
1045  */
1046 static Boolean
saveProxyClient(FILE * fp,ClientData * pCD,int clientIDNum)1047 saveProxyClient(FILE *fp, ClientData *pCD, int clientIDNum)
1048 {
1049     char clientID[50];
1050     int clientState;
1051     ProxyClientInfo proxyClientInfo;
1052     int clientX, clientY;
1053     unsigned int clientWd, clientHt;
1054 #ifdef WSM
1055     char *clientWorkspaces;
1056 #endif
1057 
1058     if (!getProxyClientInfo(pCD, &proxyClientInfo))
1059 	return False;
1060 
1061     sprintf(clientID, "%d", clientIDNum);
1062     fprintf(fp, dbClientFormat, proxyClientStr, clientID, clientID);
1063 
1064     fprintf(fp, screenStr, clientID);
1065     fprintf(fp, intArg, proxyClientInfo.screen);
1066 
1067     fprintf(fp, wmCommandStr, clientID);
1068     fprintf(fp, strArg, proxyClientInfo.wmCommand);
1069     free(proxyClientInfo.wmCommand);
1070 
1071     if (proxyClientInfo.wmClientMachine != (char *)NULL)
1072     {
1073 	fprintf(fp, wmClientMachineStr, clientID);
1074 	fprintf(fp, strArg, proxyClientInfo.wmClientMachine);
1075 	free(proxyClientInfo.wmClientMachine);
1076     }
1077 
1078     getClientGeometry(pCD, &clientX, &clientY, &clientWd, &clientHt);
1079 
1080     fprintf(fp, xPositionStr, clientID);
1081     fprintf(fp, intArg, clientX);
1082 
1083     fprintf(fp, yPositionStr, clientID);
1084     fprintf(fp, intArg, clientY);
1085 
1086     if (!pCD->pSD->useIconBox)
1087     {
1088 #ifdef WSM
1089 	WmScreenData *pSD = pCD->pSD;
1090 	WmWorkspaceData *pWS;
1091 	int i;
1092 
1093 	for (i = 0; i < pCD->numInhabited; i++)
1094 	{
1095 	    if ((pWS = GetWorkspaceData(pSD, pCD->pWsList[i].wsID))
1096 		!= (WmWorkspaceData *)NULL)
1097 	    {
1098 		fprintf(fp, iconXPosStr, clientID, pWS->name);
1099 		fprintf(fp, intArg, pCD->pWsList[i].iconX);
1100 
1101 		fprintf(fp, iconYPosStr, clientID, pWS->name);
1102 		fprintf(fp, intArg, pCD->pWsList[i].iconY);
1103 	    }
1104 	}
1105 #else
1106 	fprintf(fp, iconXPosStr, clientID);
1107 	fprintf(fp, intArg, ICON_X(pCD));
1108 
1109 	fprintf(fp, iconYPosStr, clientID);
1110 	fprintf(fp, intArg, ICON_Y(pCD));
1111 #endif
1112     }
1113 
1114     fprintf(fp, widthSizeStr, clientID);
1115     fprintf(fp, intArg, clientWd);
1116 
1117     fprintf(fp, heightSizeStr, clientID);
1118     fprintf(fp, intArg, clientHt);
1119 
1120 #ifdef WSM
1121     clientState = pCD->clientState & ~UNSEEN_STATE;
1122 #else
1123     clientState = pCD->clientState;
1124 #endif
1125 
1126     fprintf(fp, initialStateStr, clientID);
1127     fprintf(fp, strArg, (clientState == NORMAL_STATE) ?
1128 	    normalStateStr : iconicStateStr);
1129 
1130 #ifdef WSM
1131     clientWorkspaces = getClientWorkspaces(pCD);
1132     if (clientWorkspaces != (char *)NULL)
1133     {
1134 	fprintf(fp, workspacesStr, clientID);
1135 	fprintf(fp, strArg, clientWorkspaces);
1136 	XtFree(clientWorkspaces);
1137     }
1138 #endif
1139 
1140     return True;
1141 }
1142 
1143 static void
dbRemoveProxyClientEntry(char * proxyClientID)1144 dbRemoveProxyClientEntry(char *proxyClientID)
1145 {
1146     char resourceBuf[MAX_RESOURCE_LEN];
1147 
1148     /* Remove entry from DB.  Since Xrm does not provide a means */
1149     /* of removing something from the DB, we blank out key info. */
1150     sprintf(resourceBuf, wmCommandStr, proxyClientID);
1151     strcat(resourceBuf, ":");
1152     XrmPutLineResource(&wmGD.clientResourceDB, resourceBuf);
1153 }
1154 
1155 /*
1156  *  Add callbacks used in session management.
1157  */
1158 void
AddSMCallbacks(void)1159 AddSMCallbacks(void)
1160 {
1161     XtAddCallback(wmGD.topLevelW, XtNsaveCallback,
1162 		  smSaveYourselfCallback, (XtPointer)NULL);
1163     XtAddCallback(wmGD.topLevelW, XtNdieCallback,
1164 		  smDieCallback, (XtPointer)NULL);
1165 }
1166 
1167 /*
1168  *  Resign from session management, closing any connections made.
1169  */
1170 void
ResignFromSM(void)1171 ResignFromSM(void)
1172 {
1173     if (wmGD.topLevelW)
1174     {
1175 	XtVaSetValues(wmGD.topLevelW,
1176 		      XtNjoinSession, False,
1177 		      NULL);
1178     }
1179 }
1180 
1181 /*
1182  *  Exit the WM, being polite by first resigning from session mgmt.
1183  */
1184 void
ExitWM(int exitCode)1185 ExitWM(int exitCode)
1186 {
1187     ResignFromSM();
1188     exit(exitCode);
1189 }
1190 
1191 /*
1192  *  Read our private database of client resources.
1193  */
1194 XrmDatabase
LoadClientResourceDB(void)1195 LoadClientResourceDB(void)
1196 {
1197     char dbFileName[MAXPATHLEN];
1198 
1199 #ifndef WSM
1200     getClientDBName();
1201 #endif
1202     buildDBFileName(dbFileName, False);
1203 
1204     return XrmGetFileDatabase(dbFileName);
1205 }
1206 
1207 /*
1208  *  Write our private database of client resources.
1209  */
1210 XrmDatabase
SaveClientResourceDB(void)1211 SaveClientResourceDB(void)
1212 {
1213     String mySessionID;
1214     char dbFileName[MAXPATHLEN];
1215     FILE *fp;
1216     int scr;
1217     WmScreenData *pSD;
1218     ClientData *pCD;
1219     int clientIDNum = 0;
1220     ClientListEntry *pCL;
1221 
1222     /* Iterate through client list, saving */
1223     /* appropriate resources for each. */
1224 #ifndef WSM
1225     setClientDBName();
1226 #endif
1227     buildDBFileName(dbFileName, True);
1228     if ((fp = fopen(dbFileName, "w")) == (FILE *)NULL)
1229 	return (XrmDatabase)NULL;
1230 
1231     XtVaGetValues(wmGD.topLevelW,
1232 		  XtNsessionID, &mySessionID,
1233 		  NULL);
1234     fprintf(fp, dbHeader, dtwmFileName, "dtwm Version XSMP1.0",
1235 	    (mySessionID != (String)NULL) ? mySessionID : "");
1236 
1237     for (scr = 0; scr < wmGD.numScreens; scr++)
1238     {
1239 	pSD = &(wmGD.Screens[scr]);
1240 
1241 	for (pCL = pSD->clientList;
1242 	     pCL != (ClientListEntry *)NULL;
1243 	     pCL = pCL->nextSibling)
1244 	{
1245 	    /* Each client may be in list twice: normal & icon */
1246 	    if (pCL->type != NORMAL_STATE)
1247 		continue;
1248 
1249 	    pCD = pCL->pCD;
1250 
1251 	    if (pCD->smClientID != (String)NULL)
1252 	    {
1253 		saveXSMPClient(fp, pCD);
1254 	    }
1255 	    else
1256 	    {
1257 		if (saveProxyClient(fp, pCD, clientIDNum))
1258 		    clientIDNum++;
1259 	    }
1260 	}
1261     }
1262 
1263     fclose(fp);
1264 
1265     /* Retrieve database from file. */
1266     return XrmGetFileDatabase(dbFileName);
1267 }
1268 
1269 /*
1270  *  As with FindDtSessionMatch(), sets properties and then returns
1271  *  an allocated string of workspace names.  This string must be
1272  *  freed by the caller using XtFree().
1273  */
1274 Boolean
FindClientDBMatch(ClientData * pCD,char ** workSpaceNamesP)1275 FindClientDBMatch(ClientData *pCD, char **workSpaceNamesP)
1276 {
1277     return (findXSMPClientDBMatch(pCD, workSpaceNamesP) ||
1278 	    findProxyClientDBMatch(pCD, workSpaceNamesP));
1279 }
1280 
1281 Boolean
GetSmClientIdClientList(ClientData *** clients,int * nClients)1282 GetSmClientIdClientList(ClientData ***clients, int *nClients)
1283 {
1284     int scr;
1285     WmScreenData *pSD;
1286     ClientData *pCD;
1287     ClientListEntry *pCL;
1288 
1289     *nClients = 0;
1290     *clients = (ClientData **)NULL;
1291     for (scr = 0; scr < wmGD.numScreens; scr++)
1292     {
1293 	pSD = &(wmGD.Screens[scr]);
1294 
1295 	for (pCL = pSD->clientList;
1296 	     pCL != (ClientListEntry *)NULL;
1297 	     pCL = pCL->nextSibling)
1298 	{
1299 	    /* Each client may be in list twice: normal & icon */
1300 	    if (pCL->type != NORMAL_STATE)
1301 		continue;
1302 
1303 	    pCD = pCL->pCD;
1304 
1305 	    if (pCD->smClientID != (String)NULL)
1306 	    {
1307 		/* addClientToList() reclaims memory on failure. */
1308 		if (!addClientToList(clients, nClients, pCD))
1309 		    return False;
1310 	    }
1311 	}
1312     }
1313 
1314     return True;
1315 }
1316 
1317 void
SortClientListByWorkspace(ClientData ** clients,int nClients)1318 SortClientListByWorkspace(ClientData **clients, int nClients)
1319 {
1320     if (nClients > 0)
1321     {
1322 	qsort((void *)clients, nClients,
1323 	      sizeof(ClientData *), clientWorkspaceCompare);
1324     }
1325 }
1326 
1327 #ifdef WSM
1328 /* This needs to be called if WSM defined; if WSM not defined, icon */
1329 /* positions are read at the same time as other resources. */
1330 void
LoadClientIconPositions(ClientData * pCD)1331 LoadClientIconPositions(ClientData *pCD)
1332 {
1333     char resourceBuf[MAX_RESOURCE_LEN];
1334     WmScreenData *pSD = pCD->pSD;
1335     WmWorkspaceData *pWS;
1336     int i;
1337     char *resourcePtr;
1338 
1339     if (wmGD.clientResourceDB == (XrmDatabase)NULL)
1340 	return;
1341 
1342     if (pCD->smClientID != (String)NULL)
1343     {
1344 	for (i = 0; i < pCD->numInhabited; i++)
1345 	{
1346 	    if ((pWS = GetWorkspaceData(pSD, pCD->pWsList[i].wsID))
1347 		!= (WmWorkspaceData *)NULL)
1348 	    {
1349 		sprintf(resourceBuf, iconXPosStr, "%s", pWS->name);
1350 		if ((resourcePtr =
1351 		     getXSMPResource(pCD, WMSAVE_ICON_X, resourceBuf))
1352 		    != (char *)NULL)
1353 		{
1354 		    pCD->pWsList[i].iconX = atoi(resourcePtr);
1355 		    pCD->clientFlags |= SM_ICON_X;
1356 		}
1357 
1358 		sprintf(resourceBuf, iconYPosStr, "%s", pWS->name);
1359 		if ((resourcePtr =
1360 		     getXSMPResource(pCD, WMSAVE_ICON_Y, resourceBuf))
1361 		    != (char *)NULL)
1362 		{
1363 		    pCD->pWsList[i].iconY = atoi(resourcePtr);
1364 		    pCD->clientFlags |= SM_ICON_Y;
1365 		}
1366 	    }
1367 	}
1368 	return;
1369     }
1370 
1371     /* Proxy client */
1372     if (!smClientDBCheckpointed)
1373     {
1374 	char *proxyClientID;
1375 
1376 	if ((proxyClientID = findProxyClientID(pCD)) != (char *)NULL)
1377 	{
1378 	    for (i = 0; i < pCD->numInhabited; i++)
1379 	    {
1380 		if ((pWS = GetWorkspaceData(pSD, pCD->pWsList[i].wsID))
1381 		    != (WmWorkspaceData *)NULL)
1382 		{
1383 		    sprintf(resourceBuf, iconXPosStr, "%s", pWS->name);
1384 		    if ((resourcePtr =
1385 			 getClientResource(proxyClientID, resourceBuf))
1386 			!= (char *)NULL)
1387 		    {
1388 			pCD->pWsList[i].iconX = atoi(resourcePtr);
1389 			pCD->clientFlags |= SM_ICON_X;
1390 		    }
1391 
1392 		    sprintf(resourceBuf, iconYPosStr, "%s", pWS->name);
1393 		    if ((resourcePtr =
1394 			 getClientResource(proxyClientID, resourceBuf))
1395 			!= (char *)NULL)
1396 		    {
1397 			pCD->pWsList[i].iconY = atoi(resourcePtr);
1398 			pCD->clientFlags |= SM_ICON_Y;
1399 		    }
1400 		}
1401 	    }
1402 	    dbRemoveProxyClientEntry(proxyClientID);
1403 	}
1404     }
1405 }
1406 #endif /* WSM */
1407