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