session.c revision aa7a011c
1/*****************************************************************************/
2/**       Copyright 1988 by Evans & Sutherland Computer Corporation,        **/
3/**                          Salt Lake City, Utah                           **/
4/**  Portions Copyright 1989 by the Massachusetts Institute of Technology   **/
5/**                        Cambridge, Massachusetts                         **/
6/**                                                                         **/
7/**                           All Rights Reserved                           **/
8/**                                                                         **/
9/**    Permission to use, copy, modify, and distribute this software and    **/
10/**    its documentation  for  any  purpose  and  without  fee is hereby    **/
11/**    granted, provided that the above copyright notice appear  in  all    **/
12/**    copies and that both  that  copyright  notice  and  this  permis-    **/
13/**    sion  notice appear in supporting  documentation,  and  that  the    **/
14/**    names of Evans & Sutherland and M.I.T. not be used in advertising    **/
15/**    in publicity pertaining to distribution of the  software  without    **/
16/**    specific, written prior permission.                                  **/
17/**                                                                         **/
18/**    EVANS & SUTHERLAND AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD    **/
19/**    TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES  OF  MERCHANT-    **/
20/**    ABILITY  AND  FITNESS,  IN  NO  EVENT SHALL EVANS & SUTHERLAND OR    **/
21/**    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAM-    **/
22/**    AGES OR  ANY DAMAGES WHATSOEVER  RESULTING FROM LOSS OF USE, DATA    **/
23/**    OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER    **/
24/**    TORTIOUS ACTION, ARISING OUT OF OR IN  CONNECTION  WITH  THE  USE    **/
25/**    OR PERFORMANCE OF THIS SOFTWARE.                                     **/
26/*****************************************************************************/
27/*
28 *  [ ctwm ]
29 *
30 *  Copyright 1992 Claude Lecommandeur.
31 *
32 * Permission to use, copy, modify  and distribute this software  [ctwm] and
33 * its documentation for any purpose is hereby granted without fee, provided
34 * that the above  copyright notice appear  in all copies and that both that
35 * copyright notice and this permission notice appear in supporting documen-
36 * tation, and that the name of  Claude Lecommandeur not be used in adverti-
37 * sing or  publicity  pertaining to  distribution of  the software  without
38 * specific, written prior permission. Claude Lecommandeur make no represen-
39 * tations  about the suitability  of this software  for any purpose.  It is
40 * provided "as is" without express or implied warranty.
41 *
42 * Claude Lecommandeur DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
43 * INCLUDING ALL  IMPLIED WARRANTIES OF  MERCHANTABILITY AND FITNESS.  IN NO
44 * EVENT SHALL  Claude Lecommandeur  BE LIABLE FOR ANY SPECIAL,  INDIRECT OR
45 * CONSEQUENTIAL  DAMAGES OR ANY  DAMAGES WHATSOEVER  RESULTING FROM LOSS OF
46 * USE, DATA  OR PROFITS,  WHETHER IN AN ACTION  OF CONTRACT,  NEGLIGENCE OR
47 * OTHER  TORTIOUS ACTION,  ARISING OUT OF OR IN  CONNECTION WITH THE USE OR
48 * PERFORMANCE OF THIS SOFTWARE.
49 *
50 * Author:  Claude Lecommandeur [ lecom@sic.epfl.ch ][ April 1992 ]
51 */
52
53/*********************************************************************
54 *
55 * This module has been modified by
56 * Matthew McNeill (21 Mar 1997) - University of Durham (UK)
57 * for the support of the X Session Management Protocol
58 *
59 *********************************************************************
60 *
61 * Copyright (c) 1996-1997 The University of Durham, UK -
62 * Department of Computer Science.
63 * All rights reserved.
64 *
65 * Permission is hereby granted, without written agreement and without
66 * licence or royalty fees, to use, copy, modify, and distribute this
67 * software and its documentation, provided that usage, copying, modification
68 * or distribution is not for direct commercial advantage and that the
69 * above copyright notice and the following two paragraphs appear in
70 * all copies of this software. To use, copy, modify or distribute this
71 * software and its documentation otherwise requires a fee and/or specific
72 * permission.
73 *
74 * IN NO EVENT SHALL THE UNIVERSITY OF DURHAM BE LIABLE TO ANY PARTY FOR
75 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
76 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
77 * DURHAM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
78 *
79 * THE UNIVERSITY OF DURHAM SPECIFICALLY DISCLAIMS ANY WARRANTIES,
80 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
81 * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
82 * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF DURHAM HAS NO OBLIGATION TO
83 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
84 *
85 ********************************************************************/
86
87/**********************************************************************
88 *
89 * $XConsortium: session.c,v 1.18 95/01/04 22:28:37 mor Exp $
90 *
91 * Session support for the X11R6 XSMP (X Session Management Protocol)
92 *
93 * 95/01/04 Ralph Mor, X Consortium        Initial Version.
94 *
95 * Do the necessary modification to be integrated in ctwm.
96 * Can no longer be used for the standard twm.
97 *
98 * 21 Mar 1997  Matthew McNeill, Durham University  Modified Version.
99 *
100 **********************************************************************/
101
102#include <X11/Xos.h>
103
104#ifndef X_NOT_POSIX
105#ifdef _POSIX_SOURCE
106#include <limits.h>
107#else
108#define _POSIX_SOURCE
109#include <limits.h>
110#undef _POSIX_SOURCE
111#endif
112#endif /* X_NOT_POSIX */
113#ifndef PATH_MAX
114#include <sys/param.h>
115#ifndef PATH_MAX
116#ifdef MAXPATHLEN
117#define PATH_MAX MAXPATHLEN
118#else
119#define PATH_MAX 1024
120#endif
121#endif
122#endif /* PATH_MAX */
123
124#include <X11/Xlib.h>
125#include <X11/Xatom.h>
126#include <stdio.h>
127#include "twm.h"
128#include "icons.h"
129#include "screen.h"
130#include "session.h"
131
132SmcConn smcConn = NULL;
133XtInputId iceInputId;
134char *twm_clientId;
135TWMWinConfigEntry *winConfigHead = NULL;
136Bool gotFirstSave = 0;
137Bool sent_save_done = 0;
138
139#define SAVEFILE_VERSION 2
140
141
142/*===[ Get Client SM_CLIENT_ID ]=============================================*/
143
144char *GetClientID (Window window)
145/* This function returns the value of the session manager client ID property
146 * given a valid window handle. If no such property exists on a window then
147 * null is returned
148 */
149{
150    char *client_id = NULL;
151    Window client_leader;
152    XTextProperty tp;
153    Atom actual_type;
154    int actual_format;
155    unsigned long nitems;
156    unsigned long bytes_after;
157    Window *prop = NULL;
158
159    if (XGetWindowProperty (dpy, window, _XA_WM_CLIENT_LEADER,
160	0L, 1L, False, AnyPropertyType,	&actual_type, &actual_format,
161	&nitems, &bytes_after, (unsigned char **)&prop) == Success)
162    {
163	if (actual_type == XA_WINDOW && actual_format == 32 &&
164	    nitems == 1 && bytes_after == 0)
165	{
166	    client_leader = *prop;
167
168	    if (XGetTextProperty (dpy, client_leader, &tp, _XA_SM_CLIENT_ID))
169	    {
170		if (tp.encoding == XA_STRING &&
171		    tp.format == 8 && tp.nitems != 0)
172		    client_id = (char *) tp.value;
173	    }
174	}
175
176	if (prop)
177	    XFree (prop);
178    }
179
180    return client_id;
181}
182
183/*===[ Get Window Role ]=====================================================*/
184
185char *GetWindowRole (Window window)
186/* this function returns the WM_WINDOW_ROLE property of a window
187 */
188{
189    XTextProperty tp;
190
191    if (XGetTextProperty (dpy, window, &tp, _XA_WM_WINDOW_ROLE))
192    {
193	if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0)
194	    return ((char *) tp.value);
195    }
196
197    return NULL;
198}
199
200/*===[ Various file write procedures ]=======================================*/
201
202static int write_byte (FILE *file, unsigned char b)
203{
204    if (fwrite ((char *) &b, 1, 1, file) != 1)
205	return 0;
206    return 1;
207}
208
209/*---------------------------------------------------------------------------*/
210
211static int write_ushort (FILE *file, unsigned short s)
212{
213    unsigned char   file_short[2];
214
215    file_short[0] = (s & (unsigned)0xff00) >> 8;
216    file_short[1] = s & 0xff;
217    if (fwrite ((char *) file_short, (int) sizeof (file_short), 1, file) != 1)
218	return 0;
219    return 1;
220}
221
222/*---------------------------------------------------------------------------*/
223
224static int write_short (FILE *file, short s)
225{
226    unsigned char   file_short[2];
227
228    file_short[0] = (s & (unsigned)0xff00) >> 8;
229    file_short[1] = s & 0xff;
230    if (fwrite ((char *) file_short, (int) sizeof (file_short), 1, file) != 1)
231	return 0;
232    return 1;
233}
234
235/*---------------------------------------------------------------------------*
236 * Matthew McNeill Feb 1997 - required to save the occupation state as an
237 *                            integer.
238 */
239
240static int write_int (FILE *file, int i)
241{
242    unsigned char   file_int[4];
243
244    file_int[0] = (i & (unsigned)0xff000000) >> 24;
245    file_int[1] = (i & (unsigned)0x00ff0000) >> 16;
246    file_int[2] = (i & (unsigned)0x0000ff00) >> 8;
247    file_int[3] = (i & (unsigned)0x000000ff);
248    if (fwrite ((char *) file_int, (int) sizeof (file_int), 1, file) != 1)
249	return 0;
250    return 1;
251}
252
253/*---------------------------------------------------------------------------*/
254
255static int write_counted_string (FILE *file, char *string)
256{
257    if (string)
258    {
259	unsigned char count = strlen (string);
260
261	if (write_byte (file, count) == 0)
262	    return 0;
263	if (fwrite (string, (int) sizeof (char), (int) count, file) != count)
264	    return 0;
265    }
266    else
267    {
268	if (write_byte (file, 0) == 0)
269	    return 0;
270    }
271
272    return 1;
273}
274
275/*===[ various file read procedures ]========================================*/
276
277static int read_byte (FILE *file, unsigned char *bp)
278{
279    if (fread ((char *) bp, 1, 1, file) != 1)
280	return 0;
281    return 1;
282}
283
284/*---------------------------------------------------------------------------*/
285
286static int read_ushort (FILE *file, unsigned short *shortp)
287{
288    unsigned char   file_short[2];
289
290    if (fread ((char *) file_short, (int) sizeof (file_short), 1, file) != 1)
291	return 0;
292    *shortp = file_short[0] * 256 + file_short[1];
293    return 1;
294}
295
296/*---------------------------------------------------------------------------*/
297
298static int read_short (FILE *file, short *shortp)
299{
300    unsigned char   file_short[2];
301
302    if (fread ((char *) file_short, (int) sizeof (file_short), 1, file) != 1)
303	return 0;
304    *shortp = file_short[0] * 256 + file_short[1];
305    return 1;
306}
307
308/*---------------------------------------------------------------------------*
309 * Matthew McNeill Feb 1997 - required to save the occupation state as an
310 *                            integer.
311 */
312
313static int read_int (FILE *file, int *intp)
314{
315    unsigned char   file_int[4];
316
317    if (fread ((char *) file_int, (int) sizeof (file_int), 1, file) != 1)
318	return 0;
319    *intp =  (((int) file_int[0]) << 24) & 0xff000000;
320    *intp += (((int) file_int[1]) << 16) & 0x00ff0000;
321    *intp += (((int) file_int[2]) << 8)  & 0x0000ff00;
322    *intp += ((int) file_int[3]);
323    return 1;
324}
325
326/*---------------------------------------------------------------------------*/
327
328static int read_counted_string (FILE *file, char **stringp)
329{
330    unsigned char  len;
331    char	   *data;
332
333    if (read_byte (file, &len) == 0)
334	return 0;
335    if (len == 0) {
336	data = 0;
337    } else {
338	data = malloc ((unsigned) len + 1);
339	if (!data)
340	    return 0;
341	if (fread (data, (int) sizeof (char), (int) len, file) != len) {
342	    free (data);
343	    return 0;
344	}
345	data[len] = '\0';
346    }
347    *stringp = data;
348    return 1;
349}
350
351/*===[ Definition of a window config entry ]===================================
352 *
353 * An entry in the saved window config file looks like this:
354 *
355 * FIELD				BYTES
356 * -----                                ----
357 * SM_CLIENT_ID ID len			1	       (may be 0)
358 * SM_CLIENT_ID				LIST of bytes  (may be NULL)
359 *
360 * WM_WINDOW_ROLE length		1	       (may be 0)
361 * WM_WINDOW_ROLE			LIST of bytes  (may be NULL)
362 *
363 * if no WM_WINDOW_ROLE (length = 0)
364 *
365 *   WM_CLASS "res name" length		1
366 *   WM_CLASS "res name"		LIST of bytes
367 *   WM_CLASS "res class" length        1
368 *   WM_CLASS "res class"               LIST of bytes
369 *   WM_NAME length			1		(0 if name changed)
370 *   WM_NAME				LIST of bytes
371 *   WM_COMMAND arg count		1      		(0 if no SM_CLIENT_ID)
372 *   For each arg in WM_COMMAND
373 *      arg length			1
374 *      arg				LIST of bytes
375 *
376 * Iconified bool			1
377 * Icon info present bool		1
378 *
379 * if icon info present
380 *	icon x				2
381 *	icon y				2
382 *
383 * Geom x				2
384 * Geom y				2
385 * Geom width				2
386 * Geom height				2
387 *
388 * Width ever changed by user		1
389 * Height ever changed by user		1
390 *
391 * ------------------[ Matthew McNeill Feb 1997 ]----------------------------
392 *
393 * Workspace Occupation                 4
394 *
395 */
396
397
398/*===[ Write Window Config Entry to file ]===================================*/
399
400int WriteWinConfigEntry (FILE *configFile, TwmWindow *theWindow,
401			 char *clientId, char *windowRole)
402/* this function writes a window configuration entry of a given window to
403 * the given configuration file
404 */
405{
406    char **wm_command;
407    int wm_command_count, i;
408
409    /* ...unless the config file says otherwise. */
410    if (LookInList (Scr == NULL ? ScreenList [0]->DontSave : Scr->DontSave,
411		    theWindow->full_name, &theWindow->class))
412	return 1;
413
414    if (!write_counted_string (configFile, clientId))
415	return 0;
416
417    if (!write_counted_string (configFile, windowRole))
418	return 0;
419
420    if (!windowRole)
421    {
422	if (!write_counted_string (configFile, theWindow->class.res_name))
423	    return 0;
424	if (!write_counted_string (configFile, theWindow->class.res_class))
425	    return 0;
426	if (theWindow->nameChanged)
427	{
428	    /*
429	     * If WM_NAME changed on this window, we can't use it as
430	     * a criteria for looking up window configurations.  See the
431	     * longer explanation in the GetWindowConfig() function below.
432	     */
433
434	    if (!write_counted_string (configFile, NULL))
435		return 0;
436	}
437	else
438	{
439	    if (!write_counted_string (configFile, theWindow->name))
440		return 0;
441	}
442
443	wm_command = NULL;
444	wm_command_count = 0;
445	XGetCommand (dpy, theWindow->w, &wm_command, &wm_command_count);
446
447	if (clientId || !wm_command || wm_command_count == 0)
448	{
449	    if (!write_byte (configFile, 0))
450		return 0;
451	}
452	else
453	{
454	    if (!write_byte (configFile, (char) wm_command_count))
455	        return 0;
456	    for (i = 0; i < wm_command_count; i++)
457	        if (!write_counted_string (configFile, wm_command[i]))
458	            return 0;
459	    XFreeStringList (wm_command);
460	}
461    }
462
463    /* ===================[ Matthew McNeill Feb 1997 ]========================= *
464     * there has been a structural change to TwmWindow in ctwm. The Icon information
465     * is in a sub-structure now. The presence of icon information is not indicative
466     * of its current state. There is a new boolean condition for this (isicon)
467     */
468
469    if (!write_byte (configFile, theWindow->isicon ? 1 : 0)) return 0;    /* iconified */
470
471    /* ===================[ Matthew McNeill Feb 1997 ]========================= *
472     * there has been a structural change to TwmWindow in ctwm. The Icon information
473     * is in a sub-structure now, if there is no icon, this sub-structure does
474     * not exist and the attempted access (below) causes a core dump.
475     * we need to check that the structure exists before trying to access it
476     */
477    if (theWindow->icon)
478      {
479	if (!write_byte (configFile, theWindow->icon->w ? 1 : 0)) return 0; /* icon info exists */
480	if (theWindow->icon->w)
481	  {
482	    int icon_x, icon_y;
483	    XGetGeometry (dpy, theWindow->icon->w, &JunkRoot, &icon_x,
484			  &icon_y, &JunkWidth, &JunkHeight, &JunkBW, &JunkDepth);
485	    if (!write_short (configFile, (short) icon_x)) return 0;
486	    if (!write_short (configFile, (short) icon_y)) return 0;
487	  }
488      }
489    else
490      {
491	if (!write_byte (configFile, 0)) return 0; /* icon does not have any information */
492      }
493    /* ======================================================================= */
494
495    if (!write_short (configFile, (short) theWindow->frame_x))
496	return 0;
497    if (!write_short (configFile, (short) theWindow->frame_y))
498	return 0;
499    if (!write_ushort (configFile, (unsigned short) theWindow->attr.width))
500	return 0;
501    if (!write_ushort (configFile, (unsigned short) theWindow->attr.height))
502	return 0;
503    if (!write_byte (configFile, theWindow->widthEverChangedByUser ? 1 : 0))
504	return 0;
505    if (!write_byte (configFile, theWindow->heightEverChangedByUser ? 1 : 0))
506	return 0;
507
508    /* ===================[ Matthew McNeill Feb 1997 ]=======================*
509     * write an extra piece of information to the file, this is the occupation
510     * number and is a bit field of the workspaces occupied by the client.
511     */
512
513    if (!write_int (configFile, theWindow->occupation))
514	return 0;
515
516    /* ======================================================================*/
517
518    return 1;
519}
520
521/*===[ Read Window Configuration Entry ]=====================================*/
522
523int ReadWinConfigEntry (FILE *configFile, unsigned short version,
524			TWMWinConfigEntry **pentry)
525/* this function reads the next window configuration entry from the given file
526 * else it returns FALSE if none exists or there is a problem
527 */
528{
529    TWMWinConfigEntry *entry;
530    unsigned char byte;
531    int i;
532
533    *pentry = entry = (TWMWinConfigEntry *) malloc (
534	sizeof (TWMWinConfigEntry));
535    if (!*pentry)
536	return 0;
537
538    entry->tag = 0;
539    entry->client_id = NULL;
540    entry->window_role = NULL;
541    entry->class.res_name = NULL;
542    entry->class.res_class = NULL;
543    entry->wm_name = NULL;
544    entry->wm_command = NULL;
545    entry->wm_command_count = 0;
546
547    if (!read_counted_string (configFile, &entry->client_id))
548	goto give_up;
549
550    if (!read_counted_string (configFile, &entry->window_role))
551	goto give_up;
552
553    if (!entry->window_role)
554    {
555	if (!read_counted_string (configFile, &entry->class.res_name))
556	    goto give_up;
557	if (!read_counted_string (configFile, &entry->class.res_class))
558	    goto give_up;
559	if (!read_counted_string (configFile, &entry->wm_name))
560	    goto give_up;
561
562	if (!read_byte (configFile, &byte))
563	    goto give_up;
564	entry->wm_command_count = byte;
565
566	if (entry->wm_command_count == 0)
567	    entry->wm_command = NULL;
568	else
569	{
570	    entry->wm_command = (char **) malloc (entry->wm_command_count *
571	        sizeof (char *));
572
573	    if (!entry->wm_command)
574		goto give_up;
575
576	    for (i = 0; i < entry->wm_command_count; i++)
577		if (!read_counted_string (configFile, &entry->wm_command[i]))
578		    goto give_up;
579	}
580    }
581
582    if (!read_byte (configFile, &byte))
583	goto give_up;
584
585    entry->iconified = byte;
586
587    if (!read_byte (configFile, &byte))
588	goto give_up;
589
590    entry->icon_info_present = byte;
591
592    if (entry->icon_info_present)
593    {
594	if (!read_short (configFile, (short *) &entry->icon_x))
595	    goto give_up;
596	if (!read_short (configFile, (short *) &entry->icon_y))
597	    goto give_up;
598    }
599
600    if (!read_short (configFile, (short *) &entry->x))
601	goto give_up;
602    if (!read_short (configFile, (short *) &entry->y))
603	goto give_up;
604    if (!read_ushort (configFile, &entry->width))
605	goto give_up;
606    if (!read_ushort (configFile, &entry->height))
607	goto give_up;
608
609    if (version > 1)
610    {
611	if (!read_byte (configFile, &byte))
612	    goto give_up;
613	entry->width_ever_changed_by_user = byte;
614
615	if (!read_byte (configFile, &byte))
616	    goto give_up;
617	entry->height_ever_changed_by_user = byte;
618    }
619    else
620    {
621	entry->width_ever_changed_by_user = False;
622	entry->height_ever_changed_by_user = False;
623    }
624
625    /* ===================[ Matthew McNeill Feb 1997 ]======================= *
626     * read in the occupation information to restore the windows to the
627     * correct workspaces.
628     */
629
630    if (!read_int (configFile, &entry->occupation))
631	goto give_up;
632
633    /* ====================================================================== */
634
635    return 1;
636
637give_up:
638
639    if (entry->client_id)
640	free (entry->client_id);
641    if (entry->window_role)
642	free (entry->window_role);
643    if (entry->class.res_name)
644	free (entry->class.res_name);
645    if (entry->class.res_class)
646	free (entry->class.res_class);
647    if (entry->wm_name)
648	free (entry->wm_name);
649    if (entry->wm_command_count)
650    {
651	for (i = 0; i < entry->wm_command_count; i++)
652	    if (entry->wm_command[i])
653		free (entry->wm_command[i]);
654    }
655    if (entry->wm_command)
656	free ((char *) entry->wm_command);
657
658    free ((char *) entry);
659    *pentry = NULL;
660
661    return 0;
662}
663
664/*===[ Read In Win Config File ]=============================================*/
665
666void ReadWinConfigFile (char *filename)
667/* this function reads the window configuration file and stores the information
668 * in a data structure which is returned
669 */
670{
671    FILE *configFile;
672    TWMWinConfigEntry *entry;
673    int done = 0;
674    unsigned short version;
675
676    configFile = fopen (filename, "rb");
677    if (!configFile)
678	return;
679
680    if (!read_ushort (configFile, &version) ||
681	version > SAVEFILE_VERSION)
682    {
683	done = 1;
684    }
685
686    while (!done)
687    {
688	if (ReadWinConfigEntry (configFile, version, &entry))
689	{
690	    entry->next = winConfigHead;
691	    winConfigHead = entry;
692	}
693	else
694	    done = 1;
695    }
696
697    fclose (configFile);
698}
699
700/*===[ Get Window Configuration ]============================================*
701 * Matthew McNeill Feb 1997 - added extra parameter (occupation) to return
702 *                            restored occupation of the window
703 */
704
705int GetWindowConfig (TwmWindow *theWindow, short *x, short *y,
706		     unsigned short *width, unsigned short *height,
707		     Bool *iconified, Bool *icon_info_present,
708		     short *icon_x, short *icon_y,
709		     Bool *width_ever_changed_by_user,
710		     Bool *height_ever_changed_by_user,
711		     int *occupation) /* <== [ Matthew McNeill Feb 1997 ] == */
712/* This function attempts to extract all the relevant information from the
713 * given window and return values via the rest of the parameters to the
714 * function
715 */
716{
717    char *clientId, *windowRole;
718    TWMWinConfigEntry *ptr;
719    int found = 0;
720
721    ptr = winConfigHead;
722
723    if (!ptr)
724	return 0;
725
726    clientId = GetClientID (theWindow->w);
727    windowRole = GetWindowRole (theWindow->w);
728
729    while (ptr && !found)
730    {
731	int client_id_match = (!clientId && !ptr->client_id) ||
732	    (clientId && ptr->client_id &&
733	    strcmp (clientId, ptr->client_id) == 0);
734
735	if (!ptr->tag && client_id_match)
736	{
737	    if (windowRole || ptr->window_role)
738	    {
739		found = (windowRole && ptr->window_role &&
740		    strcmp (windowRole, ptr->window_role) == 0);
741	    }
742	    else
743	    {
744		/*
745		 * Compare WM_CLASS + only compare WM_NAME if the
746		 * WM_NAME in the saved file is non-NULL.  If the
747		 * WM_NAME in the saved file is NULL, this means that
748		 * the client changed the value of WM_NAME during the
749		 * session, and we can not use it as a criteria for
750		 * our search.  For example, with xmh, at save time
751		 * the window name might be "xmh: folderY".  However,
752		 * if xmh does not properly restore state when it is
753		 * restarted, the initial window name might be
754		 * "xmh: folderX".  This would cause the window manager
755		 * to fail in finding the saved window configuration.
756		 * The best we can do is ignore WM_NAME if its value
757		 * changed in the previous session.
758		 */
759
760		if (strcmp (theWindow->class.res_name,
761			ptr->class.res_name) == 0 &&
762		    strcmp (theWindow->class.res_class,
763			ptr->class.res_class) == 0 &&
764		   (ptr->wm_name == NULL ||
765		    strcmp (theWindow->name, ptr->wm_name) == 0))
766		{
767		    if (clientId)
768		    {
769			/*
770			 * If a client ID was present, we should not check
771			 * WM_COMMAND because Xt will put a -xtsessionID arg
772			 * on the command line.
773			 */
774
775			found = 1;
776		    }
777		    else
778		    {
779			/*
780			 * For non-XSMP clients, also check WM_COMMAND.
781			 */
782
783			char **wm_command = NULL;
784			int wm_command_count = 0, i;
785
786			XGetCommand (dpy, theWindow->w,
787			    &wm_command, &wm_command_count);
788
789			if (wm_command_count == ptr->wm_command_count)
790			{
791			    for (i = 0; i < wm_command_count; i++)
792				if (strcmp (wm_command[i],
793				    ptr->wm_command[i]) != 0)
794				    break;
795
796			    if (i == wm_command_count)
797				found = 1;
798			}
799		    }
800		}
801	    }
802	}
803
804	if (!found)
805	    ptr = ptr->next;
806    }
807
808    if (found)
809    {
810	*x = ptr->x;
811	*y = ptr->y;
812	*width = ptr->width;
813	*height = ptr->height;
814	*iconified = ptr->iconified;
815	*icon_info_present = ptr->icon_info_present;
816	*width_ever_changed_by_user = ptr->width_ever_changed_by_user;
817	*height_ever_changed_by_user = ptr->height_ever_changed_by_user;
818
819	if (*icon_info_present)
820	{
821	    *icon_x = ptr->icon_x;
822	    *icon_y = ptr->icon_y;
823	}
824
825	*occupation = ptr->occupation; /* <== [ Matthew McNeill Feb 1997 ] == */
826
827	ptr->tag = 1;
828    }
829    else
830	*iconified = 0;
831
832    if (clientId)
833	XFree (clientId);
834
835    if (windowRole)
836	XFree (windowRole);
837
838    return found;
839}
840
841/*===[ Unique Filename Generator ]===========================================*/
842
843static FILE *unique_file (char **filename, char *path, char *prefix)
844/* this function attempts to allocate a temporary file to store the
845 * information of the windows
846 */
847{
848    int fd;
849    char tmp[PATH_MAX], template[PATH_MAX];
850    FILE *fp;
851
852    snprintf(tmp, sizeof(tmp), "%s/%sXXXXXX", path, prefix);
853#ifndef HAVE_MKSTEMP
854    do {
855        if (fd == -1)
856            strcpy(template, tmp);
857        if ((mktemp(template) == NULL) || (template[0] == '\0'))
858            return NULL;
859        fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0600);
860    } while ((fd == -1) && (errno == EEXIST || errno == EINTR));
861#else
862    {
863	int omask = umask(077);
864	fd = mkstemp(tmp);
865	umask(omask);
866    }
867    if (fd == -1)
868	return NULL;
869#endif
870    if ((fp = fdopen(fd, "wb")) == NULL)
871	close(fd);
872    *filename = strdup(template);
873    return fp;
874}
875
876/*===[ SAVE WINDOW INFORMATION ]=============================================*/
877
878#ifndef PATH_MAX
879#  define PATH_MAX 1023
880#endif
881
882void SaveYourselfPhase2CB (SmcConn smcCon, SmPointer clientData)
883/* this is where all the work is done in saving the state of the windows.
884 * it is not done in Phase One because phase one is used for the other clients
885 * to make sure that all the property information on their windows is correct
886 * and up to date
887 */
888{
889    int scrnum;
890    ScreenInfo *theScreen;
891    TwmWindow *theWindow;
892    char *clientId, *windowRole;
893    FILE *configFile = NULL;
894    char *path;
895    char *filename = NULL;
896    Bool success = False;
897    SmProp prop1, prop2, prop3, *props[3];
898    SmPropValue prop1val, prop2val, prop3val;
899    char discardCommand[PATH_MAX + 4];
900    int numVals, i;
901    static int first_time = 1;
902
903    if (first_time)
904    {
905	char userId[20];
906	char hint = SmRestartIfRunning;
907
908	prop1.name = SmProgram;
909	prop1.type = SmARRAY8;
910	prop1.num_vals = 1;
911	prop1.vals = &prop1val;
912	prop1val.value = Argv[0];
913	prop1val.length = strlen (Argv[0]);
914
915	sprintf (userId, "%d", getuid());
916	prop2.name = SmUserID;
917	prop2.type = SmARRAY8;
918	prop2.num_vals = 1;
919	prop2.vals = &prop2val;
920	prop2val.value = (SmPointer) userId;
921	prop2val.length = strlen (userId);
922
923	prop3.name = SmRestartStyleHint;
924	prop3.type = SmCARD8;
925	prop3.num_vals = 1;
926	prop3.vals = &prop3val;
927	prop3val.value = (SmPointer) &hint;
928	prop3val.length = 1;
929
930	props[0] = &prop1;
931	props[1] = &prop2;
932	props[2] = &prop3;
933
934	SmcSetProperties (smcCon, 3, props);
935
936	first_time = 0;
937    }
938
939    path = getenv ("SM_SAVE_DIR");
940    if (!path)
941    {
942	path = getenv ("HOME");
943	if (!path)
944	    path = ".";
945    }
946    /*==============[ Matthew McNeill Feb 1997 ]==============*
947     *        changed the unique name to CTWM rather than TWM
948     *        this is tidier and more functional and prevents
949     *        TWM picking up CTWM config files. The format is
950     *        no longer the same since the new format supports
951     *        virtaul workspaces.
952     *========================================================*/
953    if ((configFile = unique_file (&filename, path, ".ctwm")) == NULL)
954	goto bad;
955
956    if (!write_ushort (configFile, SAVEFILE_VERSION))
957	goto bad;
958
959    success = True;
960
961    for (scrnum = 0; scrnum < NumScreens && success; scrnum++)
962    {
963	if (ScreenList[scrnum] != NULL)
964	{
965	    theScreen = ScreenList[scrnum];
966	    theWindow = theScreen->FirstWindow;
967
968	    while (theWindow && success)
969	    {
970		clientId = GetClientID (theWindow->w);
971		windowRole = GetWindowRole (theWindow->w);
972
973		if (!WriteWinConfigEntry (configFile, theWindow,
974		    clientId, windowRole))
975		    success = False;
976
977		if (clientId)
978		    XFree (clientId);
979
980		if (windowRole)
981		    XFree (windowRole);
982
983		theWindow = theWindow->next;
984	    }
985	}
986    }
987
988    prop1.name = SmRestartCommand;
989    prop1.type = SmLISTofARRAY8;
990
991    prop1.vals = (SmPropValue *) malloc (
992	(Argc + 4) * sizeof (SmPropValue));
993
994    if (!prop1.vals)
995    {
996	success = False;
997	goto bad;
998    }
999
1000    numVals = 0;
1001
1002    for (i = 0; i < Argc; i++)
1003    {
1004	if (strcmp (Argv[i], "-clientId") == 0 ||
1005	    strcmp (Argv[i], "-restore") == 0)
1006	{
1007	    i++;
1008	}
1009	else
1010	{
1011	    prop1.vals[numVals].value = (SmPointer) Argv[i];
1012	    prop1.vals[numVals++].length = strlen (Argv[i]);
1013	}
1014    }
1015
1016    prop1.vals[numVals].value = (SmPointer) "-clientId";
1017    prop1.vals[numVals++].length = 9;
1018
1019    prop1.vals[numVals].value = (SmPointer) twm_clientId;
1020    prop1.vals[numVals++].length = strlen (twm_clientId);
1021
1022    prop1.vals[numVals].value = (SmPointer) "-restore";
1023    prop1.vals[numVals++].length = 8;
1024
1025    prop1.vals[numVals].value = (SmPointer) filename;
1026    prop1.vals[numVals++].length = strlen (filename);
1027
1028    prop1.num_vals = numVals;
1029
1030    sprintf (discardCommand, "rm %s", filename);
1031    prop2.name = SmDiscardCommand;
1032    prop2.type = SmARRAY8;
1033    prop2.num_vals = 1;
1034    prop2.vals = &prop2val;
1035    prop2val.value = (SmPointer) discardCommand;
1036    prop2val.length = strlen (discardCommand);
1037
1038    props[0] = &prop1;
1039    props[1] = &prop2;
1040
1041    SmcSetProperties (smcCon, 2, props);
1042    free ((char *) prop1.vals);
1043
1044 bad:
1045    SmcSaveYourselfDone (smcCon, success);
1046    sent_save_done = 1;
1047
1048    if (configFile)
1049	fclose (configFile);
1050
1051    if (filename)
1052	free (filename);
1053}
1054
1055/*===[ Save Yourself SM CallBack ]===========================================*/
1056
1057void SaveYourselfCB (SmcConn smcCon, SmPointer clientData,
1058		     int saveType, Bool shutdown, int interactStyle, Bool fast)
1059/* this procedure is called by the session manager when requesting the
1060 * window manager to save its status, ie all the window configurations
1061 */
1062{
1063    if (!SmcRequestSaveYourselfPhase2 (smcCon, SaveYourselfPhase2CB, NULL))
1064    {
1065        SmcSaveYourselfDone (smcCon, False);
1066        sent_save_done = 1;
1067    }
1068    else
1069	sent_save_done = 0;
1070}
1071
1072/*===[ Die SM Call Back ]====================================================*/
1073
1074void DieCB (SmcConn smcCon, SmPointer clientData)
1075/* this procedure is called by the session manager when requesting that the
1076 * application shut istelf down
1077 */
1078{
1079    SmcCloseConnection (smcCon, 0, NULL);
1080    XtRemoveInput (iceInputId);
1081    Done(0);
1082}
1083
1084/*===[ Save Complete SM Call Back ]==========================================*/
1085
1086void SaveCompleteCB (SmcConn smcCon, SmPointer clientData)
1087/* This function is called to say that the save has been completed and that
1088 * the program can continue its operation
1089 */
1090{
1091    ;
1092}
1093
1094/*===[ Shutdown Cancelled SM Call Back ]=====================================*/
1095
1096void ShutdownCancelledCB (SmcConn smcCon, SmPointer clientData)
1097
1098{
1099    if (!sent_save_done)
1100    {
1101	SmcSaveYourselfDone (smcCon, False);
1102	sent_save_done = 1;
1103    }
1104}
1105
1106/*===[ Process ICE Message ]=================================================*/
1107
1108void ProcessIceMsgProc (XtPointer client_data, int *source, XtInputId *id)
1109
1110{
1111    IceConn	ice_conn = (IceConn) client_data;
1112    IceProcessMessages (ice_conn, NULL, NULL);
1113}
1114
1115
1116/*===[ Connect To Session Manager ]==========================================*/
1117
1118void ConnectToSessionManager (char *previous_id)
1119/* This procedure attempts to connect to the session manager and setup the
1120 * interclent exchange via the Call Backs
1121 */
1122{
1123    char errorMsg[256];
1124    unsigned long mask;
1125    SmcCallbacks callbacks;
1126    IceConn iceConn;
1127
1128    mask = SmcSaveYourselfProcMask | SmcDieProcMask |
1129	SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
1130
1131    callbacks.save_yourself.callback = SaveYourselfCB;
1132    callbacks.save_yourself.client_data = (SmPointer) NULL;
1133
1134    callbacks.die.callback = DieCB;
1135    callbacks.die.client_data = (SmPointer) NULL;
1136
1137    callbacks.save_complete.callback = SaveCompleteCB;
1138    callbacks.save_complete.client_data = (SmPointer) NULL;
1139
1140    callbacks.shutdown_cancelled.callback = ShutdownCancelledCB;
1141    callbacks.shutdown_cancelled.client_data = (SmPointer) NULL;
1142
1143    smcConn = SmcOpenConnection (
1144	NULL, 			/* use SESSION_MANAGER env */
1145	(SmPointer) appContext,
1146	SmProtoMajor,
1147	SmProtoMinor,
1148	mask,
1149	&callbacks,
1150	previous_id,
1151	&twm_clientId,
1152	256, errorMsg);
1153
1154    if (smcConn == NULL)
1155      {
1156#ifdef DEBUG
1157       /* == [ Matthew McNeill Feb 1997 ] == */
1158       fprintf (stderr, "%s : Connection to session manager failed. (%s)",ProgramName, errorMsg);
1159#endif
1160       return;
1161      }
1162
1163    iceConn = SmcGetIceConnection (smcConn);
1164
1165    iceInputId = XtAppAddInput (
1166	    appContext,
1167	    IceConnectionNumber (iceConn),
1168            (XtPointer) XtInputReadMask,
1169	    ProcessIceMsgProc,
1170	    (XtPointer) iceConn);
1171}
1172