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