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