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