1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, see: <http://www.gnu.org/licenses/>
14  */
15 /*
16   This file is strongly based on the corresponding files from
17   twm and enlightenment.
18 */
19 
20 /* ---------------------------- included header files ---------------------- */
21 
22 #include "config.h"
23 
24 #include <stdio.h>
25 #ifdef HAVE_GETPWUID
26 #include <pwd.h>
27 #endif
28 #include <signal.h>
29 #include <fcntl.h>
30 #include <X11/Xatom.h>
31 
32 #include "libs/fvwmlib.h"
33 #include "libs/FSMlib.h"
34 #include "libs/Strings.h"
35 #include "libs/System.h"
36 #include "fvwm.h"
37 #include "externs.h"
38 #include "execcontext.h"
39 #include "add_window.h"
40 #include "misc.h"
41 #include "screen.h"
42 #include "session.h"
43 #include "module_list.h"
44 #include "module_interface.h"
45 #include "stack.h"
46 #include "icccm2.h"
47 #include "virtual.h"
48 #include "geometry.h"
49 #include "move_resize.h"
50 #include "infostore.h"
51 
52 /* ---------------------------- local definitions -------------------------- */
53 
54 /*#define FVWM_SM_DEBUG_PROTO*/
55 /*#define FVWM_SM_DEBUG_WINMATCH*/
56 #define FVWM_SM_DEBUG_FILES
57 
58 /* ---------------------------- local macros ------------------------------- */
59 
60 #define xstreq(a,b) ((!a && !b) || (a && b && (strcmp(a,b)==0)))
61 
62 /* ---------------------------- imports ------------------------------------ */
63 
64 extern Bool Restarting;
65 extern int master_pid;
66 extern char **g_argv;
67 extern int g_argc;
68 
69 /* ---------------------------- included code files ------------------------ */
70 
71 /* ---------------------------- local types -------------------------------- */
72 
73 typedef struct _match
74 {
75 	unsigned long win;
76 	char *client_id;
77 	char *res_name;
78 	char *res_class;
79 	char *window_role;
80 	char *wm_name;
81 	int wm_command_count;
82 	char **wm_command;
83 	int x, y, w, h, icon_x, icon_y;
84 	int x_max, y_max, w_max, h_max;
85 	int width_defect_max, height_defect_max;
86 	int max_x_offset, max_y_offset;
87 	int desktop;
88 	int layer;
89 	int default_layer;
90 	int placed_by_button;
91 	int used;
92 	int gravity;
93 	unsigned long ewmh_hint_desktop;
94 	window_flags flags;
95 } Match;
96 
97 /* ---------------------------- forward declarations ----------------------- */
98 
99 /* ---------------------------- local variables ---------------------------- */
100 
101 static char *previous_sm_client_id = NULL;
102 static char *sm_client_id = NULL;
103 static Bool sent_save_done = 0;
104 static char *real_state_filename = NULL;
105 static Bool going_to_restart = False;
106 static FIceIOErrorHandler prev_handler;
107 static FSmcConn sm_conn = NULL;
108 
109 static int num_match = 0;
110 static Match *matches = NULL;
111 static Bool does_file_version_match = False;
112 static Bool do_preserve_state = True;
113 
114 /* ---------------------------- exported variables (globals) --------------- */
115 
116 int sm_fd = -1;
117 
118 /* ---------------------------- local functions ---------------------------- */
119 
120 static
duplicate(const char * s)121 char *duplicate(const char *s)
122 {
123 	int l;
124 	char *r;
125 
126 	if (!s) return NULL;
127 	l = strlen(s);
128 	r = (char *) safemalloc (sizeof(char)*(l+1));
129 	strncpy(r, s, l+1);
130 
131 	return r;
132 }
133 
get_version_string(void)134 static char *get_version_string(void)
135 {
136 	/* migo (14-Mar-2001): it is better to manually update a version string
137 	 * in the stable branch, otherwise saving sessions becomes useless */
138 	return CatString3(VERSION, ", ", __DATE__);
139 	/* return "2.6-0"; */
140 }
141 
unspace_string(const char * str)142 static char *unspace_string(const char *str)
143 {
144 	static const char *spaces = " \t\n";
145 	char *tr_str = CatString2(str, NULL);
146 	int i;
147 
148 	if (!tr_str)
149 	{
150 		return NULL;
151 	}
152 	for (i = 0; i < strlen(spaces); i++)
153 	{
154 		char *ptr = tr_str;
155 		while ((ptr = strchr(ptr, spaces[i])) != NULL)
156 		{
157 			*(ptr++) = '_';
158 		}
159 	}
160 
161 	return tr_str;
162 }
163 
164 /*
165  * It is a bit ugly to have a separate file format for
166  * config files and session save files. The proper way
167  * to do this may be to extend the config file format
168  * to allow the specification of everything we need
169  * to save here. Then the option "-restore xyz" could
170  * be replaced by "-f xyz".
171  */
172 static int
SaveGlobalState(FILE * f)173 SaveGlobalState(FILE *f)
174 {
175 	fprintf(f, "[GLOBAL]\n");
176 	fprintf(f, "  [DESKTOP] %i\n", Scr.CurrentDesk);
177 	fprintf(f, "  [VIEWPORT] %i %i %i %i\n",
178 		Scr.Vx, Scr.Vy, Scr.VxMax, Scr.VyMax);
179 	fprintf(f, "  [SCROLL] %i %i %i %i %i\n",
180 		Scr.EdgeScrollX, Scr.EdgeScrollY, Scr.ScrollDelay,
181 		!!(Scr.flags.do_edge_wrap_x), !!(Scr.flags.do_edge_wrap_y));
182 	fprintf(f, "  [MISC] %i %i %i\n",
183 		Scr.ClickTime, Scr.ColormapFocus, Scr.ColorLimit);
184 	fprintf(
185 		f, "  [STYLE] %i %i\n", Scr.gs.do_emulate_mwm,
186 		Scr.gs.do_emulate_win);
187 
188 	if (get_metainfo_length() > 0) {
189 		MetaInfo *mi = get_metainfo(), *mi_i;
190 
191 		fprintf(f, "  [INFOSTORE]\n");
192 		for (mi_i = mi; mi_i; mi_i = mi_i->next) {
193 			fprintf(f, "    [KEY] %s\n", mi_i->key);
194 			fprintf(f, "    [VALUE] %s\n", mi_i->value);
195 		}
196 	}
197 
198 
199 	return 1;
200 }
201 
set_real_state_filename(char * filename)202 static void set_real_state_filename(char *filename)
203 {
204 	if (!SessionSupport)
205 	{
206 		return;
207 	}
208 	if (real_state_filename)
209 	{
210 		free(real_state_filename);
211 	}
212 	real_state_filename = safestrdup(filename);
213 
214 	return;
215 }
216 
get_unique_state_filename(void)217 static char *get_unique_state_filename(void)
218 {
219 	const char *path = getenv("SM_SAVE_DIR");
220 	char *filename;
221 	int fd;
222 
223 	if (!SessionSupport)
224 	{
225 		return NULL;
226 	}
227 
228 	if (!path)
229 	{
230 		path = getenv ("HOME");
231 	}
232 
233 #ifdef HAVE_GETPWUID
234 	if (!path)
235 	{
236 		struct passwd *pwd;
237 
238 		pwd = getpwuid(getuid());
239 		if (pwd)
240 		{
241 			path = pwd->pw_dir;
242 		}
243 	}
244 #endif
245 	if (!path)
246 	{
247 		return NULL;
248 	}
249 	filename = safestrdup(CatString2(path, "/.fs-XXXXXX"));
250 	fd = fvwm_mkstemp(filename);
251 	if (fd == -1)
252 	{
253 		free (filename);
254 		filename = NULL;
255 	}
256 	else
257 	{
258 		close (fd);
259 	}
260 
261 	return filename;
262 }
263 
264 static char *
GetWindowRole(Window window)265 GetWindowRole(Window window)
266 {
267 	XTextProperty tp;
268 
269 	if (XGetTextProperty (dpy, window, &tp, _XA_WM_WINDOW_ROLE))
270 	{
271 		if (tp.encoding == XA_STRING && tp.format == 8 &&
272 		    tp.nitems != 0)
273 		{
274 			return ((char *) tp.value);
275 		}
276 	}
277 	if (XGetTextProperty (dpy, window, &tp, _XA_WINDOW_ROLE))
278 	{
279 		if (tp.encoding == XA_STRING && tp.format == 8 &&
280 		    tp.nitems != 0)
281 		{
282 			return ((char *) tp.value);
283 		}
284 	}
285 
286 	return NULL;
287 }
288 
289 static char *
GetClientID(FvwmWindow * fw)290 GetClientID(FvwmWindow *fw)
291 {
292 	char *client_id = NULL;
293 	Window client_leader = None;
294 	Window window;
295 	XTextProperty tp;
296 	Atom actual_type;
297 	int actual_format;
298 	unsigned long nitems;
299 	unsigned long bytes_after;
300 	unsigned char *prop = NULL;
301 
302 	window = FW_W(fw);
303 
304 	if (XGetWindowProperty(
305 		    dpy, window, _XA_WM_CLIENT_LEADER, 0L, 1L, False,
306 		    AnyPropertyType, &actual_type, &actual_format, &nitems,
307 		    &bytes_after, &prop) == Success)
308 	{
309 		if (actual_type == XA_WINDOW && actual_format == 32 &&
310 		    nitems == 1 && bytes_after == 0)
311 		{
312 			client_leader = (Window)(*(long *)prop);
313 		}
314 	}
315 
316 	if (!client_leader && fw->wmhints &&
317 	    (fw->wmhints->flags & WindowGroupHint))
318 	{
319 		client_leader = fw->wmhints->window_group;
320 	}
321 
322 	if (client_leader)
323 	{
324 		if (
325 			XGetTextProperty(
326 				dpy, client_leader, &tp, _XA_SM_CLIENT_ID))
327 		{
328 			if (tp.encoding == XA_STRING && tp.format == 8 &&
329 			    tp.nitems != 0)
330 			{
331 				client_id = (char *) tp.value;
332 			}
333 		}
334 	}
335 
336 	if (prop)
337 	{
338 		XFree (prop);
339 	}
340 
341 	return client_id;
342 }
343 
344 /*
345 **  Verify the current fvwm version with the version that stroed the state file.
346 **  No state will be restored if versions don't match.
347 */
VerifyVersionInfo(char * filename)348 static Bool VerifyVersionInfo(char *filename)
349 {
350 	FILE *f;
351 	char s[4096], s1[4096];
352 
353 	if (!filename || !*filename)
354 	{
355 		return False;
356 	}
357 	if ((f = fopen(filename, "r")) == NULL)
358 	{
359 		return False;
360 	}
361 
362 	while (fgets(s, sizeof(s), f))
363 	{
364 		sscanf(s, "%4000s", s1);
365 		if (!strcmp(s1, "[FVWM_VERSION]"))
366 		{
367 			char *current_v = get_version_string();
368 			sscanf(s, "%*s %[^\n]", s1);
369 			if (strcmp(s1, current_v) == 0)
370 			{
371 				does_file_version_match = True;
372 			}
373 			else
374 			{
375 				fvwm_msg(
376 					ERR, "VerifyVersionInfo",
377 					"State file version (%s) does not"
378 					" match the current version (%s), "
379 					"state file is ignored.", s1,
380 					current_v);
381 				break;
382 			}
383 		}
384 	}
385 	fclose(f);
386 
387 	return does_file_version_match;
388 }
389 
390 static int
SaveVersionInfo(FILE * f)391 SaveVersionInfo(FILE *f)
392 {
393 	fprintf(f, "[FVWM_VERSION] %s\n", get_version_string());
394 
395 	return 1;
396 }
397 
398 static int
SaveWindowStates(FILE * f)399 SaveWindowStates(FILE *f)
400 {
401 	char *client_id;
402 	char *window_role;
403 	char **wm_command;
404 	int wm_command_count;
405 	FvwmWindow *ewin;
406 	rectangle save_g;
407 	rectangle ig;
408 	int i;
409 	int layer;
410 
411 	for (ewin = get_next_window_in_stack_ring(&Scr.FvwmRoot);
412 	     ewin != &Scr.FvwmRoot;
413 	     ewin = get_next_window_in_stack_ring(ewin))
414 	{
415 		Bool is_icon_sticky_across_pages;
416 
417 		if (!XGetGeometry(
418 			    dpy, FW_W(ewin), &JunkRoot, &JunkX, &JunkY,
419 			    (unsigned int*)&JunkWidth,
420 			    (unsigned int*)&JunkHeight,
421 			    (unsigned int*)&JunkBW,
422 			    (unsigned int*)&JunkDepth))
423 		{
424 			/* Don't save the state of windows that already died
425 			 * (i.e. modules)! */
426 			continue;
427 		}
428 		is_icon_sticky_across_pages =
429 			is_window_sticky_across_pages(ewin);
430 
431 		wm_command = NULL;
432 		wm_command_count = 0;
433 
434 		client_id = GetClientID(ewin);
435 		if (!client_id)
436 		{
437 			/* no client id, some session manager do not manage
438 			 * such client ... this can cause problem */
439 			if (XGetCommand(
440 				    dpy, FW_W(ewin), &wm_command,
441 				    &wm_command_count) &&
442 			    wm_command && wm_command_count > 0)
443 			{
444 				/* ok */
445 			}
446 			else
447 			{
448 				/* No client id and no WM_COMMAND, the client
449 				 * cannot be managed by the sessiom manager
450 				 * skip it! */
451 				/* TA:  20110611 - But actually, this breaks
452 				 * those applications which don't set the
453 				 * WM_COMMAND XAtom anymore.  The ICCCM
454 				 * deprecated this at version 2.0 -- and its
455 				 * lack of existence here shouldn't be a
456 				 * problem.  Let newer session managers handle
457 				 * the error if it even matters.
458 				 */
459 				if (!Restarting)
460 				{
461 					if (wm_command)
462 					{
463 						XFreeStringList(wm_command);
464 						wm_command = NULL;
465 					}
466 					/* TA: 20110611 - But see above.  We
467 					 * no longer skip clients who don't
468 					 * set this legacy field.
469 					 */
470 					/* continue; */
471 				}
472 			}
473 		}
474 
475 		fprintf(f, "[CLIENT] %lx\n", FW_W(ewin));
476 		if (client_id)
477 		{
478 			fprintf(f, "  [CLIENT_ID] %s\n", client_id);
479 			XFree(client_id);
480 		}
481 
482 		window_role = GetWindowRole(FW_W(ewin));
483 		if (window_role)
484 		{
485 			fprintf(f, "  [WINDOW_ROLE] %s\n", window_role);
486 			XFree(window_role);
487 		}
488 		if (client_id && window_role)
489 		{
490 			/* we have enough information */
491 		}
492 		else
493 		{
494 			if (ewin->class.res_class)
495 			{
496 				fprintf(f, "  [RES_NAME] %s\n",
497 					ewin->class.res_name);
498 			}
499 			if (ewin->class.res_name)
500 			{
501 				fprintf(f, "  [RES_CLASS] %s\n",
502 					ewin->class.res_class);
503 			}
504 			if (ewin->name.name)
505 			{
506 				fprintf(f, "  [WM_NAME] %s\n",
507 					ewin->name.name);
508 			}
509 
510 			if (wm_command && wm_command_count > 0)
511 			{
512 				fprintf(f, "  [WM_COMMAND] %i",
513 					wm_command_count);
514 				for (i = 0; i < wm_command_count; i++)
515 				{
516 					fprintf(f, " %s",
517 						unspace_string(wm_command[i]));
518 				}
519 				fprintf(f, "\n");
520 			}
521 		} /* !window_role */
522 
523 		if (wm_command)
524 		{
525 			XFreeStringList(wm_command);
526 			wm_command = NULL;
527 		}
528 
529 		gravity_get_naked_geometry(
530 			ewin->hints.win_gravity, ewin, &save_g,
531 			&ewin->g.normal);
532 		if (IS_STICKY_ACROSS_PAGES(ewin))
533 		{
534 			save_g.x -= Scr.Vx;
535 			save_g.y -= Scr.Vy;
536 		}
537 		get_visible_icon_geometry(ewin, &ig);
538 		fprintf(
539 			f, "  [GEOMETRY] %i %i %i %i %i %i %i %i %i %i %i %i"
540 			" %i %i %i\n",
541 			save_g.x, save_g.y, save_g.width, save_g.height,
542 			ewin->g.max.x, ewin->g.max.y, ewin->g.max.width,
543 			ewin->g.max.height, ewin->g.max_defect.width,
544 			ewin->g.max_defect.height,
545 			ig.x + ((!is_icon_sticky_across_pages) ? Scr.Vx : 0),
546 			ig.y + ((!is_icon_sticky_across_pages) ? Scr.Vy : 0),
547 			ewin->hints.win_gravity,
548 			ewin->g.max_offset.x, ewin->g.max_offset.y);
549 		fprintf(f, "  [DESK] %i\n", ewin->Desk);
550 		/* set the layer to the default layer if the layer has been
551 		 * set by an ewmh hint */
552 		layer = get_layer(ewin);
553 		if (layer == ewin->ewmh_hint_layer && layer > 0)
554 		{
555 			layer = Scr.DefaultLayer;
556 		}
557 		fprintf(f, "  [LAYER] %i %i\n", layer, ewin->default_layer);
558 		fprintf(f, "  [PLACED_BY_BUTTON] %i\n", ewin->placed_by_button);
559 		fprintf(f, "  [EWMH_DESKTOP] %lu\n", ewin->ewmh_hint_desktop);
560 		fprintf(f, "  [FLAGS] ");
561 		for (i = 0; i < sizeof(window_flags); i++)
562 		{
563 			fprintf(f, "%02x ",
564 				(int)(((unsigned char *)&(ewin->flags))[i]));
565 		}
566 		fprintf(f, "\n");
567 	}
568 	return 1;
569 }
570 
571 /* This complicated logic is from twm, where it is explained */
matchWin(FvwmWindow * w,Match * m)572 static Bool matchWin(FvwmWindow *w, Match *m)
573 {
574 	char *client_id = NULL;
575 	char *window_role = NULL;
576 	char **wm_command = NULL;
577 	int wm_command_count = 0, i;
578 	int found;
579 
580 	found = 0;
581 	client_id = GetClientID(w);
582 
583 	if (Restarting)
584 	{
585 		if (FW_W(w) == m->win)
586 		{
587 			found = 1;
588 		}
589 	}
590 	else if (xstreq(client_id, m->client_id))
591 	{
592 
593 		/* client_id's match */
594 
595 		window_role = GetWindowRole(FW_W(w));
596 
597 		if (client_id && (window_role || m->window_role))
598 		{
599 			/* We have or had a window role, base decision on it */
600 			found = xstreq(window_role, m->window_role);
601 		}
602 		else if (xstreq(w->class.res_name, m->res_name) &&
603 			 xstreq(w->class.res_class, m->res_class) &&
604 			 (IS_NAME_CHANGED(w) || IS_NAME_CHANGED(m) ||
605 			  xstreq(w->name.name, m->wm_name)))
606 		{
607 			if (client_id)
608 			{
609 				/* If we have a client_id, we don't
610 				 * compare WM_COMMAND, since it will be
611 				 * different. */
612 				found = 1;
613 			}
614 			else
615 			{
616 				/* for non-SM-aware clients we also
617 				 * compare WM_COMMAND */
618 				if (!XGetCommand(
619 					    dpy, FW_W(w), &wm_command,
620 					    &wm_command_count))
621 				{
622 					wm_command = NULL;
623 					wm_command_count = 0;
624 				}
625 				if (wm_command_count == m->wm_command_count)
626 				{
627 					for (i = 0; i < wm_command_count; i++)
628 					{
629 						if (strcmp(unspace_string(
630 								   wm_command[i]),
631 							   m->wm_command[i])!=0)
632 						{
633 							break;
634 						}
635 					}
636 
637 					if (i == wm_command_count)
638 					{
639 						/* migo (21/Oct/1999):
640 						 * on restarts compare
641 						 * window ids too */
642 						/* But if we restart we only need
643 						 * to compare window ids
644 						 * olicha (2005-01-06) */
645 						found = 1;
646 					}
647 				} /* if (wm_command_count ==... */
648 			} /* else if res_class, res_name and wm_name agree */
649 		} /* else no window roles */
650 	} /* if client_id's agree */
651 
652 #ifdef FVWM_SM_DEBUG_WINMATCH
653 	fprintf(stderr,
654 		"\twin(%s, %s, %s, %s, %s,",
655 		w->class.res_name, w->class.res_class, w->name.name,
656 		(client_id)? client_id:"(null)",
657 		(window_role)? window_role:"(null)");
658 	if (wm_command)
659 	{
660 		for (i = 0; i < wm_command_count; i++)
661 		{
662 			fprintf(stderr," %s", wm_command[i]);
663 		}
664 		fprintf(stderr,",");
665 	}
666 	else
667 	{
668 		fprintf(stderr," no_wmc,");
669 	}
670 	fprintf(stderr," %d)", IS_NAME_CHANGED(w));
671 	fprintf(stderr,"\n[%d]", found);
672 	fprintf(stderr,
673 		"\tmat(%s, %s, %s, %s, %s,",
674 		m->res_name, m->res_class, m->wm_name,
675 		(m->client_id)?m->client_id:"(null)",
676 		(m->window_role)?m->window_role:"(null)");
677 	if (m->wm_command)
678 	{
679 		for (i = 0; i < m->wm_command_count; i++)
680 		{
681 			fprintf(stderr," %s", m->wm_command[i]);
682 		}
683 		fprintf(stderr,",");
684 	}
685 	else
686 	{
687 		fprintf(stderr," no_wmc,");
688 	}
689 	fprintf(stderr," %d)\n\n", IS_NAME_CHANGED(m));
690 #endif
691 
692 	if (client_id)
693 	{
694 		XFree(client_id);
695 	}
696 	if (window_role)
697 	{
698 		XFree(window_role);
699 	}
700 	if (wm_command)
701 	{
702 		XFreeStringList (wm_command);
703 	}
704 
705 	return found;
706 }
707 
save_state_file(char * filename)708 static int save_state_file(char *filename)
709 {
710 	FILE *f;
711 	int success;
712 
713 	if (!filename || !*filename)
714 	{
715 		return 0;
716 	}
717 	if ((f = fopen(filename, "w")) == NULL)
718 	{
719 		return 0;
720 	}
721 
722 	fprintf(f, "# This file is generated by fvwm."
723 		" It stores global and window states.\n");
724 	fprintf(f, "# Normally, you must never delete this file,"
725 		" it will be auto-deleted.\n\n");
726 
727 	if (SessionSupport && going_to_restart)
728 	{
729 		fprintf(f, "[REAL_STATE_FILENAME] %s\n", real_state_filename);
730 		going_to_restart = False;  /* not needed */
731 	}
732 
733 	success = do_preserve_state
734 		? SaveVersionInfo(f) && SaveWindowStates(f) && SaveGlobalState(f)
735 		: 1;
736 	do_preserve_state = True;
737 	if (fclose(f) != 0)
738 		return 0;
739 
740 #ifdef FVWM_SM_DEBUG_FILES
741 	system(CatString3(
742 		       "mkdir -p /tmp/fs-save; cp ", filename,
743 		       " /tmp/fs-save"));
744 #endif
745 #if defined(FVWM_SM_DEBUG_PROTO) || defined(FVWM_SM_DEBUG_FILES)
746 	fprintf(stderr, "[FVWM_SMDEBUG] Saving %s\n", filename);
747 #endif
748 
749 	return success;
750 }
751 
752 static void
set_sm_properties(FSmcConn sm_conn,char * filename,char hint)753 set_sm_properties(FSmcConn sm_conn, char *filename, char hint)
754 {
755 	FSmProp prop1, prop2, prop3, prop4, prop5, prop6, prop7, *props[7];
756 	FSmPropValue prop1val, prop2val, prop3val, prop4val, prop7val;
757 	struct passwd *pwd;
758 	char *user_id;
759 	char screen_num[32];
760 	int numVals, i, priority = 30;
761 	Bool is_xsm_detected = False;
762 
763 	if (!SessionSupport)
764 	{
765 		return;
766 	}
767 
768 #ifdef FVWM_SM_DEBUG_PROTO
769 	fprintf(stderr, "[FVWM_SMDEBUG][set_sm_properties] state filename: %s%s\n",
770 		filename ? filename : "(null)", sm_conn ? "" : " - not connected");
771 #endif
772 
773 	if (!sm_conn)
774 	{
775 		return;
776 	}
777 
778 	pwd = getpwuid (getuid());
779 	user_id = pwd->pw_name;
780 
781 	prop1.name = FSmProgram;
782 	prop1.type = FSmARRAY8;
783 	prop1.num_vals = 1;
784 	prop1.vals = &prop1val;
785 	prop1val.value = g_argv[0];
786 	prop1val.length = strlen (g_argv[0]);
787 
788 	prop2.name = FSmUserID;
789 	prop2.type = FSmARRAY8;
790 	prop2.num_vals = 1;
791 	prop2.vals = &prop2val;
792 	prop2val.value = (FSmPointer) user_id;
793 	prop2val.length = strlen (user_id);
794 
795 	prop3.name = FSmRestartStyleHint;
796 	prop3.type = FSmCARD8;
797 	prop3.num_vals = 1;
798 	prop3.vals = &prop3val;
799 	prop3val.value = (FSmPointer) &hint;
800 	prop3val.length = 1;
801 
802 	prop4.name = "_GSM_Priority";
803 	prop4.type = FSmCARD8;
804 	prop4.num_vals = 1;
805 	prop4.vals = &prop4val;
806 	prop4val.value = (FSmPointer) &priority;
807 	prop4val.length = 1;
808 
809 	sprintf(screen_num, "%d", (int)Scr.screen);
810 
811 	prop5.name = FSmCloneCommand;
812 	prop5.type = FSmLISTofARRAY8;
813 	prop5.vals = (FSmPropValue *)malloc((g_argc + 2) * sizeof (FSmPropValue));
814 	numVals = 0;
815 	for (i = 0; i < g_argc; i++)
816 	{
817 		if (strcmp (g_argv[i], "-clientId") == 0 ||
818 		    strcmp (g_argv[i], "-restore") == 0 ||
819 		    strcmp (g_argv[i], "-d") == 0 ||
820 		    (strcmp (g_argv[i], "-s") == 0 && i+1 < g_argc &&
821 		     g_argv[i+1][0] != '-'))
822 		{
823 			i++;
824 		}
825 		else if (strcmp (g_argv[i], "-s") != 0)
826 		{
827 			prop5.vals[numVals].value = (FSmPointer) g_argv[i];
828 			prop5.vals[numVals++].length = strlen (g_argv[i]);
829 		}
830 	}
831 
832 	prop5.vals[numVals].value = (FSmPointer) "-s";
833 	prop5.vals[numVals++].length = 2;
834 
835 	prop5.vals[numVals].value = (FSmPointer) screen_num;
836 	prop5.vals[numVals++].length = strlen (screen_num);
837 
838 
839 	prop5.num_vals = numVals;
840 
841 	if (filename)
842 	{
843 		prop6.name = FSmRestartCommand;
844 		prop6.type = FSmLISTofARRAY8;
845 
846 		prop6.vals = (FSmPropValue *)malloc(
847 			(g_argc + 6) * sizeof (FSmPropValue));
848 
849 		numVals = 0;
850 
851 		for (i = 0; i < g_argc; i++)
852 		{
853 			if (strcmp (g_argv[i], "-clientId") == 0 ||
854 			    strcmp (g_argv[i], "-restore") == 0 ||
855 			    strcmp (g_argv[i], "-d") == 0 ||
856 			    (strcmp (g_argv[i], "-s") == 0 && i+1 < g_argc &&
857 			     g_argv[i+1][0] != '-'))
858 			{
859 				i++;
860 			}
861 			else if (strcmp (g_argv[i], "-s") != 0)
862 			{
863 				prop6.vals[numVals].value =
864 					(FSmPointer) g_argv[i];
865 				prop6.vals[numVals++].length =
866 					strlen (g_argv[i]);
867 			}
868 		}
869 
870 		prop6.vals[numVals].value = (FSmPointer) "-s";
871 		prop6.vals[numVals++].length = 2;
872 
873 		prop6.vals[numVals].value = (FSmPointer) screen_num;
874 		prop6.vals[numVals++].length = strlen (screen_num);
875 
876 		prop6.vals[numVals].value = (FSmPointer) "-clientId";
877 		prop6.vals[numVals++].length = 9;
878 
879 		prop6.vals[numVals].value = (FSmPointer) sm_client_id;
880 		prop6.vals[numVals++].length = strlen (sm_client_id);
881 
882 		prop6.vals[numVals].value = (FSmPointer) "-restore";
883 		prop6.vals[numVals++].length = 8;
884 
885 		prop6.vals[numVals].value = (FSmPointer) filename;
886 		prop6.vals[numVals++].length = strlen (filename);
887 
888 		prop6.num_vals = numVals;
889 
890 		prop7.name = FSmDiscardCommand;
891 
892 		is_xsm_detected = StrEquals(getenv("SESSION_MANAGER_NAME"), "xsm");
893 
894 		if (is_xsm_detected)
895 		{
896 			/* the protocol spec says that the discard command
897 			   should be LISTofARRAY8 on posix systems, but xsm
898 			   demands that it be ARRAY8.
899 			*/
900 			char *discardCommand = alloca(
901 				(10 + strlen(filename)) * sizeof(char));
902 			sprintf (discardCommand, "rm -f '%s'", filename);
903 			prop7.type = FSmARRAY8;
904 			prop7.num_vals = 1;
905 			prop7.vals = &prop7val;
906 			prop7val.value = (FSmPointer) discardCommand;
907 			prop7val.length = strlen (discardCommand);
908 		}
909 		else
910 		{
911 			prop7.type = FSmLISTofARRAY8;
912 			prop7.num_vals = 3;
913 			prop7.vals =
914 				(FSmPropValue *) malloc (
915 					3 * sizeof (FSmPropValue));
916 			prop7.vals[0].value = "rm";
917 			prop7.vals[0].length = 2;
918 			prop7.vals[1].value = "-f";
919 			prop7.vals[1].length = 2;
920 			prop7.vals[2].value = filename;
921 			prop7.vals[2].length = strlen (filename);
922 		}
923 	}
924 
925 	props[0] = &prop1;
926 	props[1] = &prop2;
927 	props[2] = &prop3;
928 	props[3] = &prop4;
929 	props[4] = &prop5;
930 	SUPPRESS_UNUSED_VAR_WARNING(props);
931 	if (filename)
932 	{
933 		props[5] = &prop6;
934 		props[6] = &prop7;
935 		FSmcSetProperties (sm_conn, 7, props);
936 
937 		free ((char *) prop6.vals);
938 		if (!is_xsm_detected)
939 		{
940 			free ((char *) prop7.vals);
941 		}
942 	}
943 	else
944 	{
945 		FSmcSetProperties (sm_conn, 5, props);
946 	}
947 	free ((char *) prop5.vals);
948 }
949 
950 static void
callback_save_yourself2(FSmcConn sm_conn,FSmPointer client_data)951 callback_save_yourself2(FSmcConn sm_conn, FSmPointer client_data)
952 {
953 	Bool success = 0;
954 	char *filename;
955 
956 
957 	if (!SessionSupport)
958 	{
959 		return;
960 	}
961 
962 	filename = get_unique_state_filename();
963 #ifdef FVWM_SM_DEBUG_PROTO
964 	fprintf(stderr, "[FVWM_SMDEBUG][callback_save_yourself2]\n");
965 #endif
966 
967 	success = save_state_file(filename);
968 	if (success)
969 	{
970 		set_sm_properties(sm_conn, filename, FSmRestartIfRunning);
971 		set_real_state_filename(filename);
972 	}
973 	free(filename);
974 
975 	FSmcSaveYourselfDone (sm_conn, success);
976 	sent_save_done = 1;
977 }
978 
979 static void
callback_save_yourself(FSmcConn sm_conn,FSmPointer client_data,int save_style,Bool shutdown,int interact_style,Bool fast)980 callback_save_yourself(FSmcConn sm_conn, FSmPointer client_data,
981 		       int save_style, Bool shutdown, int interact_style,
982 		       Bool fast)
983 {
984 
985 	if (!SessionSupport)
986 	{
987 		return;
988 	}
989 
990 #ifdef FVWM_SM_DEBUG_PROTO
991 	fprintf(stderr, "[FVWM_SMDEBUG][callback_save_yourself] "
992 		"(save=%d, shut=%d, intr=%d, fast=%d)\n",
993 		save_style, shutdown, interact_style, fast);
994 #endif
995 
996 	if (save_style == FSmSaveGlobal)
997 	{
998 		/* nothing to do */
999 #ifdef FVWM_SM_DEBUG_PROTO
1000 		fprintf(stderr, "[FVWM_SMDEBUG][callback_save_yourself] "
1001 		"Global Save type ... do nothing\n");
1002 #endif
1003 		FSmcSaveYourselfDone (sm_conn, True);
1004 		sent_save_done = 1;
1005 		return;
1006 
1007 	}
1008 #ifdef FVWM_SM_DEBUG_PROTO
1009 	fprintf(stderr, "[FVWM_SMDEBUG][callback_save_yourself] "
1010 		"Both or Local save type, going to phase 2 ...");
1011 #endif
1012 	if (!FSmcRequestSaveYourselfPhase2(
1013 		    sm_conn, callback_save_yourself2, NULL))
1014 	{
1015 		FSmcSaveYourselfDone (sm_conn, False);
1016 		sent_save_done = 1;
1017 #ifdef FVWM_SM_DEBUG_PROTO
1018 		fprintf(stderr, " failed!\n");
1019 #endif
1020 	}
1021 	else
1022 	{
1023 #ifdef FVWM_SM_DEBUG_PROTO
1024 		fprintf(stderr, " OK\n");
1025 #endif
1026 		sent_save_done = 0;
1027 	}
1028 
1029 	return;
1030 }
1031 
1032 static void
callback_die(FSmcConn sm_conn,FSmPointer client_data)1033 callback_die(FSmcConn sm_conn, FSmPointer client_data)
1034 {
1035 
1036 	if (!SessionSupport)
1037 	{
1038 		return;
1039 	}
1040 
1041 #ifdef FVWM_SM_DEBUG_PROTO
1042 	fprintf(stderr, "[FVWM_SMDEBUG][callback_die]\n");
1043 #endif
1044 
1045 	if (FSmcCloseConnection(sm_conn, 0, NULL) != FSmcClosedNow)
1046 	{
1047 		/* go a head any way ? */
1048 	}
1049 	sm_fd = -1;
1050 
1051 	if (master_pid != getpid())
1052 	{
1053 		kill(master_pid, SIGTERM);
1054 	}
1055 	Done(0, NULL);
1056 }
1057 
1058 static void
callback_save_complete(FSmcConn sm_conn,FSmPointer client_data)1059 callback_save_complete(FSmcConn sm_conn, FSmPointer client_data)
1060 {
1061 	if (!SessionSupport)
1062 	{
1063 		return;
1064 	}
1065 #ifdef FVWM_SM_DEBUG_PROTO
1066 	fprintf(stderr, "[FVWM_SMDEBUG][callback_save_complete]\n");
1067 #endif
1068 
1069 	return;
1070 }
1071 
1072 static void
callback_shutdown_cancelled(FSmcConn sm_conn,FSmPointer client_data)1073 callback_shutdown_cancelled(FSmcConn sm_conn, FSmPointer client_data)
1074 {
1075 	if (!SessionSupport)
1076 	{
1077 		return;
1078 	}
1079 
1080 #ifdef FVWM_SM_DEBUG_PROTO
1081 	fprintf(stderr, "[FVWM_SMDEBUG][callback_shutdown_cancelled]\n");
1082 #endif
1083 
1084 	if (!sent_save_done)
1085 	{
1086 		FSmcSaveYourselfDone(sm_conn, False);
1087 		sent_save_done = 1;
1088 	}
1089 
1090 	return;
1091 }
1092 
1093 /* the following is taken from xsm */
1094 static void
MyIoErrorHandler(FIceConn ice_conn)1095 MyIoErrorHandler(FIceConn ice_conn)
1096 {
1097 	if (!SessionSupport)
1098 	{
1099 		return;
1100 	}
1101 
1102 	if (prev_handler)
1103 	{
1104 		(*prev_handler) (ice_conn);
1105 	}
1106 
1107 	return;
1108 }
1109 
1110 static void
InstallIOErrorHandler(void)1111 InstallIOErrorHandler(void)
1112 {
1113 	FIceIOErrorHandler default_handler;
1114 
1115 	if (!SessionSupport)
1116 	{
1117 		return;
1118 	}
1119 
1120 	prev_handler = FIceSetIOErrorHandler (NULL);
1121 	default_handler = FIceSetIOErrorHandler (MyIoErrorHandler);
1122 	if (prev_handler == default_handler)
1123 	{
1124 		prev_handler = NULL;
1125 	}
1126 
1127 	return;
1128 }
1129 
1130 /* ---------------------------- interface functions ------------------------ */
1131 
1132 void
LoadGlobalState(char * filename)1133 LoadGlobalState(char *filename)
1134 {
1135 	FILE *f;
1136 	char s[4096], s1[4096];
1137 	/* char s2[256]; */
1138 	char *is_key = NULL, *is_value = NULL;
1139 	int n, i1, i2, i3, i4;
1140 
1141 	if (!does_file_version_match)
1142 	{
1143 		return;
1144 	}
1145 	if (!filename || !*filename)
1146 	{
1147 		return;
1148 	}
1149 	if ((f = fopen(filename, "r")) == NULL)
1150 	{
1151 		return;
1152 	}
1153 
1154 	while (fgets(s, sizeof(s), f))
1155 	{
1156 		n = 0;
1157 
1158 		i1 = 0;
1159 		i2 = 0;
1160 		i3 = 0;
1161 		i4 = 0;
1162 
1163 		sscanf(s, "%4000s%n", s1, &n);
1164 		/* If we are restarting, [REAL_STATE_FILENAME] points
1165 		 * to the file containing the true session state. */
1166 		if (SessionSupport && !strcmp(s1, "[REAL_STATE_FILENAME]"))
1167 		{
1168 			/* migo: temporarily (?) moved to
1169 			   LoadWindowStates (trick for gnome-session)
1170 			   sscanf(s, "%*s %s", s2);
1171 			   set_sm_properties(sm_conn, s2, FSmRestartIfRunning);
1172 			   set_real_state_filename(s2);
1173 			*/
1174 		}
1175 		else if (!strcmp(s1, "[DESKTOP]"))
1176 		{
1177 			sscanf(s, "%*s %i", &i1);
1178 			goto_desk(i1);
1179 		}
1180 		else if (!strcmp(s1, "[VIEWPORT]"))
1181 		{
1182 			sscanf(s, "%*s %i %i %i %i", &i1, &i2, &i3,
1183 			       &i4);
1184 			/* migo: we don't want to lose DeskTopSize in
1185 			 * configurations, and it does not work well
1186 			 * anyways - Gnome is not updated
1187 			 Scr.VxMax = i3;
1188 			 Scr.VyMax = i4;
1189 			*/
1190 			MoveViewport(i1, i2, True);
1191 		}
1192 		else if (!strcmp(s1, "[KEY]"))
1193 		{
1194 			char *s2;
1195 			s2 = s + n;
1196 			if (*s2 != 0)
1197 			{
1198 				s2++;
1199 			}
1200 			sscanf(s2, "%[^\n]", s1);
1201 			is_key = safestrdup(s1);
1202 		}
1203 		else if (!strcmp(s1, "[VALUE]"))
1204 		{
1205 			char *s2;
1206 			s2 = s + n;
1207 			if (*s2 != 0)
1208 			{
1209 				s2++;
1210 			}
1211 			sscanf(s2, "%[^\n]", s1);
1212 			is_value = safestrdup(s1);
1213 
1214 			fprintf(stderr, "GOT: %s -> %s\n", is_key, is_value);
1215 
1216 			if (is_key != NULL && is_value != NULL) {
1217 				fprintf(stderr, "INSERTING: %s -> %s\n",
1218 					is_key, is_value);
1219 				insert_metainfo(is_key, is_value);
1220 			}
1221 
1222 			free(is_key);
1223 			free(is_value);
1224 			is_key = is_value = NULL;
1225 		}
1226 #if 0
1227 		/* migo (08-Dec-1999): we don't want to eliminate config yet */
1228 		else if (/*!Restarting*/ 0)
1229 		{
1230 			/* Matthias: We don't want to restore too much
1231 			 * state if we are restarting, since that
1232 			 * would make restarting useless for rereading
1233 			 * changed rc files. */
1234 			if (!strcmp(s1, "[SCROLL]"))
1235 			{
1236 				sscanf(s, "%*s %i %i %i %i ", &i1,
1237 				       &i2, &i3, &i4);
1238 				Scr.EdgeScrollX = i1;
1239 				Scr.EdgeScrollY = i2;
1240 				Scr.ScrollDelay = i3;
1241 				if (i4)
1242 				{
1243 					Scr.flags.edge_wrap_x = 1;
1244 				}
1245 				else
1246 				{
1247 					Scr.flags.edge_wrap_x = 0;
1248 				}
1249 				if (i3)
1250 				{
1251 					Scr.flags.edge_wrap_y = 1;
1252 				}
1253 				else
1254 				{
1255 					Scr.flags.edge_wrap_y = 0;
1256 				}
1257 			}
1258 			else if (!strcmp(s1, "[MISC]"))
1259 			{
1260 				sscanf(s, "%*s %i %i %i", &i1, &i2,
1261 				       &i3);
1262 				Scr.ClickTime = i1;
1263 				Scr.ColormapFocus = i2;
1264 				Scr.ColorLimit = i3;
1265 			}
1266 			else if (!strcmp(s1, "[STYLE]"))
1267 			{
1268 				sscanf(s, "%*s %i %i", &i1, &i2);
1269 				Scr.gs.EmulateMWM = i1;
1270 				Scr.gs.EmulateWIN = i2;
1271 			}
1272 		}
1273 #endif
1274 	}
1275 	fclose(f);
1276 
1277 	return;
1278 }
1279 
1280 void
DisableRestoringState(void)1281 DisableRestoringState(void)
1282 {
1283 	num_match = 0;
1284 
1285 	return;
1286 }
1287 
1288 void
LoadWindowStates(char * filename)1289 LoadWindowStates(char *filename)
1290 {
1291 	FILE *f;
1292 	char s[4096], s1[4096];
1293 	char *s2;
1294 	int i, pos, pos1;
1295 	unsigned long w;
1296 	int n;
1297 
1298 	if (!VerifyVersionInfo(filename))
1299 	{
1300 		return;
1301 	}
1302 	if (!filename || !*filename)
1303 	{
1304 		return;
1305 	}
1306 	set_real_state_filename(filename);
1307 	if ((f = fopen(filename, "r")) == NULL)
1308 	{
1309 		return;
1310 	}
1311 
1312 #if defined(FVWM_SM_DEBUG_PROTO) ||  defined(FVWM_SM_DEBUG_FILES)
1313 	fprintf(stderr, "[FVWM_SMDEBUG] Loading %s\n", filename);
1314 #endif
1315 #ifdef FVWM_SM_DEBUG_FILES
1316 	system(CatString3(
1317 		       "mkdir -p /tmp/fs-load; cp ", filename,
1318 		       " /tmp/fs-load"));
1319 #endif
1320 
1321 	while (fgets(s, sizeof(s), f))
1322 	{
1323 		n = 0;
1324 		sscanf(s, "%4000s%n", s1, &n);
1325 		if (!SessionSupport /* migo: temporarily */ &&
1326 		    !strcmp(s1, "[REAL_STATE_FILENAME]"))
1327 		{
1328 			sscanf(s, "%*s %s", s1);
1329 			set_sm_properties(sm_conn, s1, FSmRestartIfRunning);
1330 			set_real_state_filename(s1);
1331 		}
1332 		else if (!strcmp(s1, "[CLIENT]"))
1333 		{
1334 			sscanf(s, "%*s %lx", &w);
1335 			num_match++;
1336 			matches = (Match *)saferealloc(
1337 				(void *)matches, sizeof(Match) * num_match);
1338 			matches[num_match - 1].win = w;
1339 			matches[num_match - 1].client_id = NULL;
1340 			matches[num_match - 1].res_name = NULL;
1341 			matches[num_match - 1].res_class = NULL;
1342 			matches[num_match - 1].window_role = NULL;
1343 			matches[num_match - 1].wm_name = NULL;
1344 			matches[num_match - 1].wm_command_count = 0;
1345 			matches[num_match - 1].wm_command = NULL;
1346 			matches[num_match - 1].x = 0;
1347 			matches[num_match - 1].y = 0;
1348 			matches[num_match - 1].w = 100;
1349 			matches[num_match - 1].h = 100;
1350 			matches[num_match - 1].x_max = 0;
1351 			matches[num_match - 1].y_max = 0;
1352 			matches[num_match - 1].w_max = Scr.MyDisplayWidth;
1353 			matches[num_match - 1].h_max = Scr.MyDisplayHeight;
1354 			matches[num_match - 1].width_defect_max = 0;
1355 			matches[num_match - 1].height_defect_max = 0;
1356 			matches[num_match - 1].icon_x = 0;
1357 			matches[num_match - 1].icon_y = 0;
1358 			matches[num_match - 1].desktop = 0;
1359 			matches[num_match - 1].layer = 0;
1360 			matches[num_match - 1].default_layer = 0;
1361 			memset(&(matches[num_match - 1].flags), 0,
1362 			       sizeof(window_flags));
1363 			matches[num_match - 1].used = 0;
1364 		}
1365 		else if (!strcmp(s1, "[GEOMETRY]"))
1366 		{
1367 			sscanf(s, "%*s %i %i %i %i %i %i %i %i %i %i %i %i"
1368 			       " %i %i %i",
1369 			       &(matches[num_match - 1].x),
1370 			       &(matches[num_match - 1].y),
1371 			       &(matches[num_match - 1].w),
1372 			       &(matches[num_match - 1].h),
1373 			       &(matches[num_match - 1].x_max),
1374 			       &(matches[num_match - 1].y_max),
1375 			       &(matches[num_match - 1].w_max),
1376 			       &(matches[num_match - 1].h_max),
1377 			       &(matches[num_match - 1].width_defect_max),
1378 			       &(matches[num_match - 1].height_defect_max),
1379 			       &(matches[num_match - 1].icon_x),
1380 			       &(matches[num_match - 1].icon_y),
1381 			       &(matches[num_match - 1].gravity),
1382 			       &(matches[num_match - 1].max_x_offset),
1383 			       &(matches[num_match - 1].max_y_offset));
1384 		}
1385 		else if (!strcmp(s1, "[DESK]"))
1386 		{
1387 			sscanf(s, "%*s %i",
1388 			       &(matches[num_match - 1].desktop));
1389 		}
1390 		else if (!strcmp(s1, "[LAYER]"))
1391 		{
1392 			sscanf(s, "%*s %i %i",
1393 			       &(matches[num_match - 1].layer),
1394 			       &(matches[num_match - 1].default_layer));
1395 		}
1396 		else if (!strcmp(s1, "[PLACED_BY_BUTTON]"))
1397 		{
1398 			sscanf(s, "%*s %i",
1399 			       &(matches[num_match - 1].placed_by_button));
1400 		}
1401 		else if (!strcmp(s1, "[EWMH_DESKTOP]"))
1402 		{
1403 			sscanf(s, "%*s %lu",
1404 			       &(matches[num_match - 1].ewmh_hint_desktop));
1405 		}
1406 		else if (!strcmp(s1, "[FLAGS]"))
1407 		{
1408 			char *ts = s;
1409 
1410 			/* skip [FLAGS] */
1411 			while (*ts != ']')
1412 			{
1413 				ts++;
1414 			}
1415 			ts++;
1416 
1417 			for (i = 0; i < sizeof(window_flags); i++)
1418 			{
1419 				unsigned int f;
1420 				sscanf(ts, "%02x ", &f);
1421 				((unsigned char *)&
1422 				 (matches[num_match-1].flags))[i] = f;
1423 				ts += 3;
1424 			}
1425 		}
1426 		else if (!strcmp(s1, "[CLIENT_ID]"))
1427 		{
1428 			s2 = s + n;
1429 			if (*s2 != 0)
1430 			{
1431 				s2++;
1432 			}
1433 			sscanf(s2, "%[^\n]", s1);
1434 			matches[num_match - 1].client_id = duplicate(s1);
1435 		}
1436 		else if (!strcmp(s1, "[WINDOW_ROLE]"))
1437 		{
1438 			s2 = s + n;
1439 			if (*s2 != 0)
1440 			{
1441 				s2++;
1442 			}
1443 			sscanf(s2, "%[^\n]", s1);
1444 			matches[num_match - 1].window_role = duplicate(s1);
1445 		}
1446 		else if (!strcmp(s1, "[RES_NAME]"))
1447 		{
1448 			s2 = s + n;
1449 			if (*s2 != 0)
1450 			{
1451 				s2++;
1452 			}
1453 			sscanf(s2, "%[^\n]", s1);
1454 			matches[num_match - 1].res_name = duplicate(s1);
1455 		}
1456 		else if (!strcmp(s1, "[RES_CLASS]"))
1457 		{
1458 			s2 = s + n;
1459 			if (*s2 != 0)
1460 			{
1461 				s2++;
1462 			}
1463 			sscanf(s2, "%[^\n]", s1);
1464 			matches[num_match - 1].res_class = duplicate(s1);
1465 		}
1466 		else if (!strcmp(s1, "[WM_NAME]"))
1467 		{
1468 			s2 = s + n;
1469 			if (*s2 != 0)
1470 			{
1471 				s2++;
1472 			}
1473 			sscanf(s2, "%[^\n]", s1);
1474 			matches[num_match - 1].wm_name = duplicate(s1);
1475 		}
1476 		else if (!strcmp(s1, "[WM_COMMAND]"))
1477 		{
1478 			sscanf(s, "%*s %i%n",
1479 			       &matches[num_match - 1].wm_command_count, &pos);
1480 			matches[num_match - 1].wm_command = (char **)
1481 				safemalloc(
1482 					matches[num_match - 1].
1483 					wm_command_count * sizeof (char *));
1484 			for (i = 0;
1485 			     i < matches[num_match - 1].wm_command_count; i++)
1486 			{
1487 				sscanf (s+pos, "%s%n", s1, &pos1);
1488 				pos += pos1;
1489 				matches[num_match - 1].wm_command[i] =
1490 					duplicate (s1);
1491 			}
1492 		}
1493 	}
1494 	fclose(f);
1495 
1496 	return;
1497 }
1498 
1499 /*
1500   This routine (potentially) changes the flags STARTICONIC,
1501   MAXIMIZED, WSHADE and STICKY and the Desk and
1502   attr.x, .y, .width, .height entries. It also changes the
1503   stack_before pointer to return information about the
1504   desired stacking order. It expects the stacking order
1505   to be set up correctly beforehand!
1506 */
1507 Bool
MatchWinToSM(FvwmWindow * ewin,mwtsm_state_args * ret_state_args,initial_window_options_t * win_opts)1508 MatchWinToSM(
1509 	FvwmWindow *ewin, mwtsm_state_args *ret_state_args,
1510 	initial_window_options_t *win_opts)
1511 {
1512 	int i;
1513 
1514 	if (!does_file_version_match)
1515 	{
1516 		return False;
1517 	}
1518 	for (i = 0; i < num_match; i++)
1519 	{
1520 		if (!matches[i].used && matchWin(ewin, &matches[i]))
1521 		{
1522 			matches[i].used = 1;
1523 
1524 			if (!Restarting)
1525 			{
1526 				/* We don't want to restore too much state if
1527 				   we are restarting, since that would make
1528 				   * restarting useless for rereading changed
1529 				   * rc files. */
1530 				SET_DO_SKIP_WINDOW_LIST(
1531 					ewin,
1532 					DO_SKIP_WINDOW_LIST(&(matches[i])));
1533 				SET_ICON_SUPPRESSED(
1534 					ewin,
1535 					IS_ICON_SUPPRESSED(&(matches[i])));
1536 				SET_HAS_NO_ICON_TITLE(
1537 					ewin, HAS_NO_ICON_TITLE(&(matches[i])));
1538 				FPS_LENIENT(
1539 					FW_FOCUS_POLICY(ewin),
1540 					FP_IS_LENIENT(FW_FOCUS_POLICY(
1541 							      &(matches[i]))));
1542 				SET_ICON_STICKY_ACROSS_PAGES(
1543 					ewin, IS_ICON_STICKY_ACROSS_PAGES(
1544 						&(matches[i])));
1545 				SET_ICON_STICKY_ACROSS_DESKS(
1546 					ewin, IS_ICON_STICKY_ACROSS_DESKS(
1547 						&(matches[i])));
1548 				SET_DO_SKIP_ICON_CIRCULATE(
1549 					ewin, DO_SKIP_ICON_CIRCULATE(
1550 						&(matches[i])));
1551 				SET_DO_SKIP_SHADED_CIRCULATE(
1552 					ewin, DO_SKIP_SHADED_CIRCULATE(
1553 						&(matches[i])));
1554 				SET_DO_SKIP_CIRCULATE(
1555 					ewin, DO_SKIP_CIRCULATE(&(matches[i])));
1556 				memcpy(
1557 					&FW_FOCUS_POLICY(ewin),
1558 					&FW_FOCUS_POLICY(&matches[i]),
1559 					sizeof(focus_policy_t));
1560 				if (matches[i].wm_name)
1561 				{
1562 					free_window_names(ewin, True, False);
1563 					ewin->name.name = matches[i].wm_name;
1564 					setup_visible_names(ewin, 1);
1565 				}
1566 			}
1567 			SET_NAME_CHANGED(ewin,IS_NAME_CHANGED(&(matches[i])));
1568 			SET_PLACED_BY_FVWM(
1569 				ewin, IS_PLACED_BY_FVWM(&(matches[i])));
1570 			ret_state_args->do_shade = IS_SHADED(&(matches[i]));
1571 			ret_state_args->used_title_dir_for_shading =
1572 				USED_TITLE_DIR_FOR_SHADING(&(matches[i]));
1573 			ret_state_args->shade_dir = SHADED_DIR(&(matches[i]));
1574 			ret_state_args->do_max = IS_MAXIMIZED(&(matches[i]));
1575 			SET_USER_STATES(ewin, GET_USER_STATES(&(matches[i])));
1576 			SET_ICON_MOVED(ewin, IS_ICON_MOVED(&(matches[i])));
1577 			if (IS_ICONIFIED(&(matches[i])))
1578 			{
1579 				/*
1580 				  ICON_MOVED is necessary to make fvwm use
1581 				  icon_[xy]_loc for icon placement
1582 				*/
1583 				win_opts->initial_state = IconicState;
1584 				win_opts->flags.use_initial_icon_xy = 1;
1585 				win_opts->initial_icon_x = matches[i].icon_x;
1586 				win_opts->initial_icon_y = matches[i].icon_y;
1587 				if (!IS_STICKY_ACROSS_PAGES(&(matches[i])) &&
1588 				    !(IS_ICONIFIED(&(matches[i])) &&
1589 				      IS_ICON_STICKY_ACROSS_PAGES(
1590 					      &(matches[i]))))
1591 				{
1592 					win_opts->initial_icon_x -= Scr.Vx;
1593 					win_opts->initial_icon_y -= Scr.Vy;
1594 				}
1595 			}
1596 			ewin->g.normal.x = matches[i].x;
1597 			ewin->g.normal.y = matches[i].y;
1598 			ewin->g.normal.width = matches[i].w;
1599 			ewin->g.normal.height = matches[i].h;
1600 			ewin->g.max.x = matches[i].x_max;
1601 			ewin->g.max.y = matches[i].y_max;
1602 			ewin->g.max.width = matches[i].w_max;
1603 			ewin->g.max.height = matches[i].h_max;
1604 			ewin->g.max_defect.width = matches[i].width_defect_max;
1605 			ewin->g.max_defect.height =
1606 				matches[i].height_defect_max;
1607 			ewin->g.max_offset.x = matches[i].max_x_offset;
1608 			ewin->g.max_offset.y = matches[i].max_y_offset;
1609 			SET_STICKY_ACROSS_PAGES(
1610 				ewin, IS_STICKY_ACROSS_PAGES(&(matches[i])));
1611 			SET_STICKY_ACROSS_DESKS(
1612 				ewin, IS_STICKY_ACROSS_DESKS(&(matches[i])));
1613 			ewin->Desk = (IS_STICKY_ACROSS_DESKS(ewin)) ?
1614 				Scr.CurrentDesk : matches[i].desktop;
1615 			set_layer(ewin, matches[i].layer);
1616 			set_default_layer(ewin, matches[i].default_layer);
1617 			ewin->placed_by_button = matches[i].placed_by_button;
1618 			/* Note: the Modal, skip pager, skip taskbar and
1619 			 * "stacking order" state are not restored here: there
1620 			 * are restored in EWMH_ExitStuff */
1621 			ewin->ewmh_hint_desktop = matches[i].ewmh_hint_desktop;
1622 			SET_HAS_EWMH_INIT_WM_DESKTOP(
1623 				ewin, HAS_EWMH_INIT_WM_DESKTOP(&(matches[i])));
1624 			SET_HAS_EWMH_INIT_HIDDEN_STATE(
1625 				ewin, HAS_EWMH_INIT_HIDDEN_STATE(
1626 					&(matches[i])));
1627 			SET_HAS_EWMH_INIT_MAXHORIZ_STATE(
1628 				ewin, HAS_EWMH_INIT_MAXHORIZ_STATE(
1629 					&(matches[i])));
1630 			SET_HAS_EWMH_INIT_MAXVERT_STATE(
1631 				ewin, HAS_EWMH_INIT_MAXVERT_STATE(
1632 					&(matches[i])));
1633 			SET_HAS_EWMH_INIT_SHADED_STATE(
1634 				ewin, HAS_EWMH_INIT_SHADED_STATE(
1635 					&(matches[i])));
1636 			SET_HAS_EWMH_INIT_STICKY_STATE(
1637 				ewin, HAS_EWMH_INIT_STICKY_STATE(
1638 					&(matches[i])));
1639 			return True;
1640 		}
1641 	}
1642 
1643 	return False;
1644 }
1645 
1646 void
RestartInSession(char * filename,Bool is_native,Bool _do_preserve_state)1647 RestartInSession (char *filename, Bool is_native, Bool _do_preserve_state)
1648 {
1649 	do_preserve_state = _do_preserve_state;
1650 
1651 	if (SessionSupport && sm_conn && is_native)
1652 	{
1653 		going_to_restart = True;
1654 
1655 		save_state_file(filename);
1656 		set_sm_properties(sm_conn, filename, FSmRestartImmediately);
1657 
1658 		MoveViewport(0, 0, False);
1659 		Reborder();
1660 
1661 		CloseICCCM2();
1662 		XCloseDisplay(dpy);
1663 
1664 		if ((!FSmcCloseConnection(sm_conn, 0, NULL)) != FSmcClosedNow)
1665 		{
1666 			/* go a head any way ? */
1667 		}
1668 
1669 #ifdef  FVWM_SM_DEBUG_PROTO
1670 		fprintf(stderr, "[FVWM_SMDEBUG]: Exiting, now SM must "
1671 			"restart us.\n");
1672 #endif
1673 		/* Close all my pipes */
1674 		module_kill_all();
1675 
1676 		exit(0); /* let the SM restart us */
1677 	}
1678 
1679 	save_state_file(filename);
1680 	/* return and let Done restart us */
1681 
1682 	return;
1683 }
1684 
SetClientID(char * client_id)1685 void SetClientID(char *client_id)
1686 {
1687 	if (!SessionSupport)
1688 	{
1689 		return;
1690 	}
1691 	previous_sm_client_id = client_id;
1692 
1693 	return;
1694 }
1695 
1696 void
SessionInit(void)1697 SessionInit(void)
1698 {
1699 	char error_string_ret[4096] = "";
1700 	FSmPointer context;
1701 	FSmcCallbacks callbacks;
1702 
1703 	if (!SessionSupport)
1704 	{
1705 		/* -Wall fixes */
1706 		MyIoErrorHandler(NULL);
1707 		callback_save_yourself2(NULL, NULL);
1708 		return;
1709 	}
1710 
1711 	InstallIOErrorHandler();
1712 
1713 	callbacks.save_yourself.callback = callback_save_yourself;
1714 	callbacks.die.callback = callback_die;
1715 	callbacks.save_complete.callback = callback_save_complete;
1716 	callbacks.shutdown_cancelled.callback = callback_shutdown_cancelled;
1717 	callbacks.save_yourself.client_data =
1718 		callbacks.die.client_data =
1719 		callbacks.save_complete.client_data =
1720 		callbacks.shutdown_cancelled.client_data = (FSmPointer) NULL;
1721 	SUPPRESS_UNUSED_VAR_WARNING(context);
1722 	sm_conn = FSmcOpenConnection(
1723 		NULL, &context, FSmProtoMajor, FSmProtoMinor,
1724 		FSmcSaveYourselfProcMask | FSmcDieProcMask |
1725 		FSmcSaveCompleteProcMask | FSmcShutdownCancelledProcMask,
1726 		&callbacks, previous_sm_client_id, &sm_client_id, 4096,
1727 		error_string_ret);
1728 	if (!sm_conn)
1729 	{
1730 		/*
1731 		  Don't annoy users which don't use a session manager
1732 		*/
1733 		if (previous_sm_client_id)
1734 		{
1735 			fvwm_msg(
1736 				ERR, "SessionInit",
1737 				"While connecting to session manager:\n%s.",
1738 				error_string_ret);
1739 		}
1740 		sm_fd = -1;
1741 #ifdef FVWM_SM_DEBUG_PROTO
1742 		fprintf(stderr,"[FVWM_SMDEBUG] No SM connection\n");
1743 #endif
1744 	}
1745 	else
1746 	{
1747 		sm_fd = FIceConnectionNumber(FSmcGetIceConnection(sm_conn));
1748 #ifdef FVWM_SM_DEBUG_PROTO
1749 		fprintf(stderr,"[FVWM_SMDEBUG] Connectecd to a SM\n");
1750 #endif
1751 		set_init_function_name(0, "SessionInitFunction");
1752 		set_init_function_name(1, "SessionRestartFunction");
1753 		set_init_function_name(2, "SessionExitFunction");
1754 		/* basically to restet our restart style hint after a
1755 		 * restart */
1756 		set_sm_properties(sm_conn, NULL, FSmRestartIfRunning);
1757 	}
1758 
1759 	return;
1760 }
1761 
1762 void
ProcessICEMsgs(void)1763 ProcessICEMsgs(void)
1764 {
1765 	FIceProcessMessagesStatus status;
1766 
1767 	if (!SessionSupport)
1768 	{
1769 		return;
1770 	}
1771 
1772 #ifdef FVWM_SM_DEBUG_PROTO
1773 	fprintf(stderr,"[FVWM_SMDEBUG][ProcessICEMsgs] %i\n", (int)sm_fd);
1774 #endif
1775 	if (sm_fd < 0)
1776 	{
1777 		return;
1778 	}
1779 	status = FIceProcessMessages(FSmcGetIceConnection(sm_conn), NULL, NULL);
1780 	if (status == FIceProcessMessagesIOError)
1781 	{
1782 		fvwm_msg(ERR, "ProcessICEMSGS",
1783 			 "Connection to session manager lost\n");
1784 		sm_conn = NULL;
1785 		sm_fd = -1;
1786 	}
1787 
1788 	return;
1789 }
1790 
1791 /*
1792  * Fvwm Function implementation: QuitSession, SaveSession, SaveQuitSession.
1793  * migo (15-Jun-1999): I am not sure this is right.
1794  * The session mabager must implement SmsSaveYourselfRequest (xsm doesn't?).
1795  * Alternative implementation may use unix signals, but this does not work
1796  * with all session managers (also must suppose that SM runs locally).
1797  */
get_sm_pid(void)1798 int get_sm_pid(void)
1799 {
1800 	const char *session_manager_var = getenv("SESSION_MANAGER");
1801 	const char *sm_pid_str_ptr;
1802 	int sm_pid = 0;
1803 
1804 	if (!SessionSupport)
1805 	{
1806 		return 0;
1807 	}
1808 
1809 	if (!session_manager_var)
1810 	{
1811 		return 0;
1812 	}
1813 	sm_pid_str_ptr = strchr(session_manager_var, ',');
1814 	if (!sm_pid_str_ptr)
1815 	{
1816 		return 0;
1817 	}
1818 	while (sm_pid_str_ptr > session_manager_var && isdigit(*(--sm_pid_str_ptr)))
1819 	{
1820 		/* nothing */
1821 	}
1822 	while (isdigit(*(++sm_pid_str_ptr)))
1823 	{
1824 		sm_pid = sm_pid * 10 + *sm_pid_str_ptr - '0';
1825 	}
1826 
1827 	return sm_pid;
1828 }
1829 
1830 /*
1831  * quit_session - hopefully shutdowns the session
1832  */
quit_session(void)1833 Bool quit_session(void)
1834 {
1835 	if (!SessionSupport)
1836 	{
1837 		return False;
1838 	}
1839 	if (!sm_conn)
1840 	{
1841 		return False;
1842 	}
1843 
1844 	FSmcRequestSaveYourself(
1845 		sm_conn, FSmSaveLocal, True /* shutdown */, FSmInteractStyleAny,
1846 		False /* fast */, True /* global */);
1847 	return True;
1848 
1849 	/* migo: xsm does not support RequestSaveYourself, but supports
1850 	 * signals: */
1851 	/*
1852 	  int sm_pid = get_sm_pid();
1853 	  if (!sm_pid) return False;
1854 	  return kill(sm_pid, SIGTERM) == 0 ? True : False;
1855 	*/
1856 }
1857 
1858 /*
1859  * save_session - hopefully saves the session
1860  */
save_session(void)1861 Bool save_session(void)
1862 {
1863 	if (!SessionSupport)
1864 	{
1865 		return False;
1866 	}
1867 	if (!sm_conn)
1868 	{
1869 		return False;
1870 	}
1871 
1872 	FSmcRequestSaveYourself(
1873 		sm_conn, FSmSaveBoth, False /* shutdown */, FSmInteractStyleAny,
1874 		False /* fast */, True /* global */);
1875 	return True;
1876 
1877 	/* migo: xsm does not support RequestSaveYourself, but supports
1878 	 * signals: */
1879 	/*
1880 	  int sm_pid = get_sm_pid();
1881 	  if (!sm_pid) return False;
1882 	  return kill(sm_pid, SIGUSR1) == 0 ? True : False;
1883 	*/
1884 }
1885 
1886 /*
1887  * save_quit_session - hopefully saves and shutdowns the session
1888  */
save_quit_session(void)1889 Bool save_quit_session(void)
1890 {
1891 	if (!SessionSupport)
1892 	{
1893 		return False;
1894 	}
1895 
1896 	if (!sm_conn)
1897 	{
1898 		return False;
1899 	}
1900 
1901 	FSmcRequestSaveYourself(
1902 		sm_conn, FSmSaveBoth, True /* shutdown */, FSmInteractStyleAny,
1903 		False /* fast */, True /* global */);
1904 	return True;
1905 
1906 	/* migo: xsm does not support RequestSaveYourself, but supports
1907 	 * signals: */
1908 	/*
1909 	  if (save_session() == False) return False;
1910 	  sleep(3);  / * doesn't work anyway * /
1911 	  if (quit_session() == False) return False;
1912 	  return True;
1913 	*/
1914 }
1915 
1916 /* ---------------------------- builtin commands --------------------------- */
1917 
CMD_QuitSession(F_CMD_ARGS)1918 void CMD_QuitSession(F_CMD_ARGS)
1919 {
1920 	quit_session();
1921 
1922 	return;
1923 }
1924 
CMD_SaveSession(F_CMD_ARGS)1925 void CMD_SaveSession(F_CMD_ARGS)
1926 {
1927 	save_session();
1928 
1929 	return;
1930 }
1931 
CMD_SaveQuitSession(F_CMD_ARGS)1932 void CMD_SaveQuitSession(F_CMD_ARGS)
1933 {
1934 	save_quit_session();
1935 
1936 	return;
1937 }
1938