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.
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.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 This file is strongly based on the corresponding files from
18 twm and enlightenment.
21 /* ---------------------------- included header files ---------------------- */
31 #include <X11/Xatom.h>
33 #include "libs/fvwmlib.h"
34 #include "libs/FSMlib.h"
35 #include "libs/Strings.h"
36 #include "libs/System.h"
39 #include "execcontext.h"
40 #include "add_window.h"
44 #include "module_list.h"
45 #include "module_interface.h"
50 #include "move_resize.h"
52 /* ---------------------------- local definitions -------------------------- */
54 /*#define FVWM_SM_DEBUG_PROTO*/
55 /*#define FVWM_SM_DEBUG_WINMATCH*/
56 /*#define FVWM_SM_DEBUG_FILES*/
58 /* ---------------------------- local macros ------------------------------- */
60 #define xstreq(a,b) ((!a && !b) || (a && b && (strcmp(a,b)==0)))
62 /* ---------------------------- imports ------------------------------------ */
64 extern Bool Restarting
;
65 extern int master_pid
;
69 /* ---------------------------- included code files ------------------------ */
71 /* ---------------------------- local types -------------------------------- */
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
;
93 unsigned long ewmh_hint_desktop
;
97 /* ---------------------------- forward declarations ----------------------- */
99 /* ---------------------------- local variables ---------------------------- */
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
;
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
;
114 /* ---------------------------- exported variables (globals) --------------- */
118 /* ---------------------------- local functions ---------------------------- */
121 char *duplicate(const char *s
)
128 r
= (char *) safemalloc (sizeof(char)*(l
+1));
134 static char *get_version_string(void)
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"; */
142 static char *unspace_string(const char *str
)
144 static const char *spaces
= " \t\n";
145 char *tr_str
= CatString2(str
, NULL
);
152 for (i
= 0; i
< strlen(spaces
); i
++)
155 while ((ptr
= strchr(ptr
, spaces
[i
])) != NULL
)
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".
173 SaveGlobalState(FILE *f
)
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
);
185 f
, " [STYLE] %i %i\n", Scr
.gs
.do_emulate_mwm
,
186 Scr
.gs
.do_emulate_win
);
191 static void set_real_state_filename(char *filename
)
197 if (real_state_filename
)
199 free(real_state_filename
);
201 real_state_filename
= safestrdup(filename
);
206 static char *get_unique_state_filename(void)
208 const char *path
= getenv("SM_SAVE_DIR");
219 path
= getenv ("HOME");
227 pwd
= getpwuid(getuid());
238 filename
= safestrdup(CatString2(path
, "/.fs-XXXXXX"));
239 fd
= fvwm_mkstemp(filename
);
254 GetWindowRole(Window window
)
258 if (XGetTextProperty (dpy
, window
, &tp
, _XA_WM_WINDOW_ROLE
))
260 if (tp
.encoding
== XA_STRING
&& tp
.format
== 8 &&
263 return ((char *) tp
.value
);
266 if (XGetTextProperty (dpy
, window
, &tp
, _XA_WINDOW_ROLE
))
268 if (tp
.encoding
== XA_STRING
&& tp
.format
== 8 &&
271 return ((char *) tp
.value
);
279 GetClientID(FvwmWindow
*fw
)
281 char *client_id
= NULL
;
282 Window client_leader
= None
;
287 unsigned long nitems
;
288 unsigned long bytes_after
;
289 unsigned char *prop
= NULL
;
293 if (XGetWindowProperty(
294 dpy
, window
, _XA_WM_CLIENT_LEADER
, 0L, 1L, False
,
295 AnyPropertyType
, &actual_type
, &actual_format
, &nitems
,
296 &bytes_after
, &prop
) == Success
)
298 if (actual_type
== XA_WINDOW
&& actual_format
== 32 &&
299 nitems
== 1 && bytes_after
== 0)
301 client_leader
= (Window
)(*(long *)prop
);
305 if (!client_leader
&& fw
->wmhints
&&
306 (fw
->wmhints
->flags
& WindowGroupHint
))
308 client_leader
= fw
->wmhints
->window_group
;
315 dpy
, client_leader
, &tp
, _XA_SM_CLIENT_ID
))
317 if (tp
.encoding
== XA_STRING
&& tp
.format
== 8 &&
320 client_id
= (char *) tp
.value
;
334 ** Verify the current fvwm version with the version that stroed the state file.
335 ** No state will be restored if versions don't match.
337 static Bool
VerifyVersionInfo(char *filename
)
340 char s
[4096], s1
[4096];
342 if (!filename
|| !*filename
)
346 if ((f
= fopen(filename
, "r")) == NULL
)
351 while (fgets(s
, sizeof(s
), f
))
353 sscanf(s
, "%4000s", s1
);
354 if (!strcmp(s1
, "[FVWM_VERSION]"))
356 char *current_v
= get_version_string();
357 sscanf(s
, "%*s %[^\n]", s1
);
358 if (strcmp(s1
, current_v
) == 0)
360 does_file_version_match
= True
;
365 ERR
, "VerifyVersionInfo",
366 "State file version (%s) does not"
367 " match the current version (%s), "
368 "state file is ignored.", s1
,
376 return does_file_version_match
;
380 SaveVersionInfo(FILE *f
)
382 fprintf(f
, "[FVWM_VERSION] %s\n", get_version_string());
388 SaveWindowStates(FILE *f
)
393 int wm_command_count
;
400 for (ewin
= get_next_window_in_stack_ring(&Scr
.FvwmRoot
);
401 ewin
!= &Scr
.FvwmRoot
;
402 ewin
= get_next_window_in_stack_ring(ewin
))
404 Bool is_icon_sticky_across_pages
;
407 dpy
, FW_W(ewin
), &JunkRoot
, &JunkX
, &JunkY
,
408 (unsigned int*)&JunkWidth
,
409 (unsigned int*)&JunkHeight
,
410 (unsigned int*)&JunkBW
,
411 (unsigned int*)&JunkDepth
))
413 /* Don't save the state of windows that already died
417 is_icon_sticky_across_pages
=
418 is_window_sticky_across_pages(ewin
);
421 wm_command_count
= 0;
423 client_id
= GetClientID(ewin
);
426 /* no client id, some session manager do not manage
427 * such client ... this can cause problem */
429 dpy
, FW_W(ewin
), &wm_command
,
430 &wm_command_count
) &&
431 wm_command
&& wm_command_count
> 0)
437 /* No client id and no WM_COMMAND, the client
438 * cannot be managed by the sessiom manager
444 XFreeStringList(wm_command
);
452 fprintf(f
, "[CLIENT] %lx\n", FW_W(ewin
));
455 fprintf(f
, " [CLIENT_ID] %s\n", client_id
);
459 window_role
= GetWindowRole(FW_W(ewin
));
462 fprintf(f
, " [WINDOW_ROLE] %s\n", window_role
);
465 if (client_id
&& window_role
)
467 /* we have enough information */
471 if (ewin
->class.res_class
)
473 fprintf(f
, " [RES_NAME] %s\n",
474 ewin
->class.res_name
);
476 if (ewin
->class.res_name
)
478 fprintf(f
, " [RES_CLASS] %s\n",
479 ewin
->class.res_class
);
483 fprintf(f
, " [WM_NAME] %s\n",
487 if (wm_command
&& wm_command_count
> 0)
489 fprintf(f
, " [WM_COMMAND] %i",
491 for (i
= 0; i
< wm_command_count
; i
++)
494 unspace_string(wm_command
[i
]));
502 XFreeStringList(wm_command
);
506 gravity_get_naked_geometry(
507 ewin
->hints
.win_gravity
, ewin
, &save_g
,
509 if (IS_STICKY_ACROSS_PAGES(ewin
))
514 get_visible_icon_geometry(ewin
, &ig
);
516 f
, " [GEOMETRY] %i %i %i %i %i %i %i %i %i %i %i %i"
518 save_g
.x
, save_g
.y
, save_g
.width
, save_g
.height
,
519 ewin
->g
.max
.x
, ewin
->g
.max
.y
, ewin
->g
.max
.width
,
520 ewin
->g
.max
.height
, ewin
->g
.max_defect
.width
,
521 ewin
->g
.max_defect
.height
,
522 ig
.x
+ ((!is_icon_sticky_across_pages
) ? Scr
.Vx
: 0),
523 ig
.y
+ ((!is_icon_sticky_across_pages
) ? Scr
.Vy
: 0),
524 ewin
->hints
.win_gravity
,
525 ewin
->g
.max_offset
.x
, ewin
->g
.max_offset
.y
);
526 fprintf(f
, " [DESK] %i\n", ewin
->Desk
);
527 /* set the layer to the default layer if the layer has been
528 * set by an ewmh hint */
529 layer
= get_layer(ewin
);
530 if (layer
== ewin
->ewmh_hint_layer
&& layer
> 0)
532 layer
= Scr
.DefaultLayer
;
534 fprintf(f
, " [LAYER] %i %i\n", layer
, ewin
->default_layer
);
535 fprintf(f
, " [PLACED_BY_BUTTON] %i\n", ewin
->placed_by_button
);
536 fprintf(f
, " [EWMH_DESKTOP] %lu\n", ewin
->ewmh_hint_desktop
);
537 fprintf(f
, " [FLAGS] ");
538 for (i
= 0; i
< sizeof(window_flags
); i
++)
541 (int)(((unsigned char *)&(ewin
->flags
))[i
]));
548 /* This complicated logic is from twm, where it is explained */
549 static Bool
matchWin(FvwmWindow
*w
, Match
*m
)
551 char *client_id
= NULL
;
552 char *window_role
= NULL
;
553 char **wm_command
= NULL
;
554 int wm_command_count
= 0, i
;
558 client_id
= GetClientID(w
);
562 if (FW_W(w
) == m
->win
)
567 else if (xstreq(client_id
, m
->client_id
))
570 /* client_id's match */
572 window_role
= GetWindowRole(FW_W(w
));
574 if (client_id
&& (window_role
|| m
->window_role
))
576 /* We have or had a window role, base decision on it */
577 found
= xstreq(window_role
, m
->window_role
);
579 else if (xstreq(w
->class.res_name
, m
->res_name
) &&
580 xstreq(w
->class.res_class
, m
->res_class
) &&
581 (IS_NAME_CHANGED(w
) || IS_NAME_CHANGED(m
) ||
582 xstreq(w
->name
.name
, m
->wm_name
)))
586 /* If we have a client_id, we don't
587 * compare WM_COMMAND, since it will be
593 /* for non-SM-aware clients we also
594 * compare WM_COMMAND */
596 dpy
, FW_W(w
), &wm_command
,
600 wm_command_count
= 0;
602 if (wm_command_count
== m
->wm_command_count
)
604 for (i
= 0; i
< wm_command_count
; i
++)
606 if (strcmp(unspace_string(
608 m
->wm_command
[i
])!=0)
614 if (i
== wm_command_count
)
616 /* migo (21/Oct/1999):
617 * on restarts compare
619 /* But if we restart we only need
620 * to compare window ids
621 * olicha (2005-01-06) */
624 } /* if (wm_command_count ==... */
625 } /* else if res_class, res_name and wm_name agree */
626 } /* else no window roles */
627 } /* if client_id's agree */
629 #ifdef FVWM_SM_DEBUG_WINMATCH
631 "\twin(%s, %s, %s, %s, %s,",
632 w
->class.res_name
, w
->class.res_class
, w
->name
.name
,
633 (client_id
)? client_id
:"(null)",
634 (window_role
)? window_role
:"(null)");
637 for (i
= 0; i
< wm_command_count
; i
++)
639 fprintf(stderr
," %s", wm_command
[i
]);
645 fprintf(stderr
," no_wmc,");
647 fprintf(stderr
," %d)", IS_NAME_CHANGED(w
));
648 fprintf(stderr
,"\n[%d]", found
);
650 "\tmat(%s, %s, %s, %s, %s,",
651 m
->res_name
, m
->res_class
, m
->wm_name
,
652 (m
->client_id
)?m
->client_id
:"(null)",
653 (m
->window_role
)?m
->window_role
:"(null)");
656 for (i
= 0; i
< m
->wm_command_count
; i
++)
658 fprintf(stderr
," %s", m
->wm_command
[i
]);
664 fprintf(stderr
," no_wmc,");
666 fprintf(stderr
," %d)\n\n", IS_NAME_CHANGED(m
));
679 XFreeStringList (wm_command
);
685 static int save_state_file(char *filename
)
690 if (!filename
|| !*filename
)
694 if ((f
= fopen(filename
, "w")) == NULL
)
699 fprintf(f
, "# This file is generated by fvwm."
700 " It stores global and window states.\n");
701 fprintf(f
, "# Normally, you must never delete this file,"
702 " it will be auto-deleted.\n\n");
704 if (SessionSupport
&& going_to_restart
)
706 fprintf(f
, "[REAL_STATE_FILENAME] %s\n", real_state_filename
);
707 going_to_restart
= False
; /* not needed */
710 success
= do_preserve_state
711 ? SaveVersionInfo(f
) && SaveWindowStates(f
) && SaveGlobalState(f
)
713 do_preserve_state
= True
;
717 #ifdef FVWM_SM_DEBUG_FILES
719 "mkdir -p /tmp/fs-save; cp ", filename
,
722 #if defined(FVWM_SM_DEBUG_PROTO) || defined(FVWM_SM_DEBUG_FILES)
723 fprintf(stderr
, "[FVWM_SMDEBUG] Saving %s\n", filename
);
730 set_sm_properties(FSmcConn sm_conn
, char *filename
, char hint
)
732 FSmProp prop1
, prop2
, prop3
, prop4
, prop5
, prop6
, prop7
, *props
[7];
733 FSmPropValue prop1val
, prop2val
, prop3val
, prop4val
, prop7val
;
737 int numVals
, i
, priority
= 30;
738 Bool is_xsm_detected
= False
;
745 #ifdef FVWM_SM_DEBUG_PROTO
746 fprintf(stderr
, "[FVWM_SMDEBUG][set_sm_properties] state filename: %s%s\n",
747 filename
? filename
: "(null)", sm_conn
? "" : " - not connected");
755 pwd
= getpwuid (getuid());
756 user_id
= pwd
->pw_name
;
758 prop1
.name
= FSmProgram
;
759 prop1
.type
= FSmARRAY8
;
761 prop1
.vals
= &prop1val
;
762 prop1val
.value
= g_argv
[0];
763 prop1val
.length
= strlen (g_argv
[0]);
765 prop2
.name
= FSmUserID
;
766 prop2
.type
= FSmARRAY8
;
768 prop2
.vals
= &prop2val
;
769 prop2val
.value
= (FSmPointer
) user_id
;
770 prop2val
.length
= strlen (user_id
);
772 prop3
.name
= FSmRestartStyleHint
;
773 prop3
.type
= FSmCARD8
;
775 prop3
.vals
= &prop3val
;
776 prop3val
.value
= (FSmPointer
) &hint
;
779 prop4
.name
= "_GSM_Priority";
780 prop4
.type
= FSmCARD8
;
782 prop4
.vals
= &prop4val
;
783 prop4val
.value
= (FSmPointer
) &priority
;
786 sprintf(screen_num
, "%d", (int)Scr
.screen
);
788 prop5
.name
= FSmCloneCommand
;
789 prop5
.type
= FSmLISTofARRAY8
;
790 prop5
.vals
= (FSmPropValue
*)malloc((g_argc
+ 2) * sizeof (FSmPropValue
));
792 for (i
= 0; i
< g_argc
; i
++)
794 if (strcmp (g_argv
[i
], "-clientId") == 0 ||
795 strcmp (g_argv
[i
], "-restore") == 0 ||
796 strcmp (g_argv
[i
], "-d") == 0 ||
797 (strcmp (g_argv
[i
], "-s") == 0 && i
+1 < g_argc
&&
798 g_argv
[i
+1][0] != '-'))
802 else if (strcmp (g_argv
[i
], "-s") != 0)
804 prop5
.vals
[numVals
].value
= (FSmPointer
) g_argv
[i
];
805 prop5
.vals
[numVals
++].length
= strlen (g_argv
[i
]);
809 prop5
.vals
[numVals
].value
= (FSmPointer
) "-s";
810 prop5
.vals
[numVals
++].length
= 2;
812 prop5
.vals
[numVals
].value
= (FSmPointer
) screen_num
;
813 prop5
.vals
[numVals
++].length
= strlen (screen_num
);
816 prop5
.num_vals
= numVals
;
820 prop6
.name
= FSmRestartCommand
;
821 prop6
.type
= FSmLISTofARRAY8
;
823 prop6
.vals
= (FSmPropValue
*)malloc(
824 (g_argc
+ 6) * sizeof (FSmPropValue
));
828 for (i
= 0; i
< g_argc
; i
++)
830 if (strcmp (g_argv
[i
], "-clientId") == 0 ||
831 strcmp (g_argv
[i
], "-restore") == 0 ||
832 strcmp (g_argv
[i
], "-d") == 0 ||
833 (strcmp (g_argv
[i
], "-s") == 0 && i
+1 < g_argc
&&
834 g_argv
[i
+1][0] != '-'))
838 else if (strcmp (g_argv
[i
], "-s") != 0)
840 prop6
.vals
[numVals
].value
=
841 (FSmPointer
) g_argv
[i
];
842 prop6
.vals
[numVals
++].length
=
847 prop6
.vals
[numVals
].value
= (FSmPointer
) "-s";
848 prop6
.vals
[numVals
++].length
= 2;
850 prop6
.vals
[numVals
].value
= (FSmPointer
) screen_num
;
851 prop6
.vals
[numVals
++].length
= strlen (screen_num
);
853 prop6
.vals
[numVals
].value
= (FSmPointer
) "-clientId";
854 prop6
.vals
[numVals
++].length
= 9;
856 prop6
.vals
[numVals
].value
= (FSmPointer
) sm_client_id
;
857 prop6
.vals
[numVals
++].length
= strlen (sm_client_id
);
859 prop6
.vals
[numVals
].value
= (FSmPointer
) "-restore";
860 prop6
.vals
[numVals
++].length
= 8;
862 prop6
.vals
[numVals
].value
= (FSmPointer
) filename
;
863 prop6
.vals
[numVals
++].length
= strlen (filename
);
865 prop6
.num_vals
= numVals
;
867 prop7
.name
= FSmDiscardCommand
;
869 is_xsm_detected
= StrEquals(getenv("SESSION_MANAGER_NAME"), "xsm");
873 /* the protocol spec says that the discard command
874 should be LISTofARRAY8 on posix systems, but xsm
875 demands that it be ARRAY8.
877 char *discardCommand
= alloca(
878 (10 + strlen(filename
)) * sizeof(char));
879 sprintf (discardCommand
, "rm -f '%s'", filename
);
880 prop7
.type
= FSmARRAY8
;
882 prop7
.vals
= &prop7val
;
883 prop7val
.value
= (FSmPointer
) discardCommand
;
884 prop7val
.length
= strlen (discardCommand
);
888 prop7
.type
= FSmLISTofARRAY8
;
891 (FSmPropValue
*) malloc (
892 3 * sizeof (FSmPropValue
));
893 prop7
.vals
[0].value
= "rm";
894 prop7
.vals
[0].length
= 2;
895 prop7
.vals
[1].value
= "-f";
896 prop7
.vals
[1].length
= 2;
897 prop7
.vals
[2].value
= filename
;
898 prop7
.vals
[2].length
= strlen (filename
);
912 FSmcSetProperties (sm_conn
, 7, props
);
914 free ((char *) prop6
.vals
);
915 if (!is_xsm_detected
)
917 free ((char *) prop7
.vals
);
922 FSmcSetProperties (sm_conn
, 5, props
);
924 free ((char *) prop5
.vals
);
928 callback_save_yourself2(FSmcConn sm_conn
, FSmPointer client_data
)
939 filename
= get_unique_state_filename();
940 #ifdef FVWM_SM_DEBUG_PROTO
941 fprintf(stderr
, "[FVWM_SMDEBUG][callback_save_yourself2]\n");
944 success
= save_state_file(filename
);
947 set_sm_properties(sm_conn
, filename
, FSmRestartIfRunning
);
948 set_real_state_filename(filename
);
952 FSmcSaveYourselfDone (sm_conn
, success
);
957 callback_save_yourself(FSmcConn sm_conn
, FSmPointer client_data
,
958 int save_style
, Bool shutdown
, int interact_style
,
967 #ifdef FVWM_SM_DEBUG_PROTO
968 fprintf(stderr
, "[FVWM_SMDEBUG][callback_save_yourself] "
969 "(save=%d, shut=%d, intr=%d, fast=%d)\n",
970 save_style
, shutdown
, interact_style
, fast
);
973 if (save_style
== FSmSaveGlobal
)
976 #ifdef FVWM_SM_DEBUG_PROTO
977 fprintf(stderr
, "[FVWM_SMDEBUG][callback_save_yourself] "
978 "Global Save type ... do nothing\n");
980 FSmcSaveYourselfDone (sm_conn
, True
);
985 #ifdef FVWM_SM_DEBUG_PROTO
986 fprintf(stderr
, "[FVWM_SMDEBUG][callback_save_yourself] "
987 "Both or Local save type, going to phase 2 ...");
989 if (!FSmcRequestSaveYourselfPhase2(
990 sm_conn
, callback_save_yourself2
, NULL
))
992 FSmcSaveYourselfDone (sm_conn
, False
);
994 #ifdef FVWM_SM_DEBUG_PROTO
995 fprintf(stderr
, " failed!\n");
1000 #ifdef FVWM_SM_DEBUG_PROTO
1001 fprintf(stderr
, " OK\n");
1010 callback_die(FSmcConn sm_conn
, FSmPointer client_data
)
1013 if (!SessionSupport
)
1018 #ifdef FVWM_SM_DEBUG_PROTO
1019 fprintf(stderr
, "[FVWM_SMDEBUG][callback_die]\n");
1022 if (FSmcCloseConnection(sm_conn
, 0, NULL
) != FSmcClosedNow
)
1024 /* go a head any way ? */
1028 if (master_pid
!= getpid())
1030 kill(master_pid
, SIGTERM
);
1036 callback_save_complete(FSmcConn sm_conn
, FSmPointer client_data
)
1038 if (!SessionSupport
)
1042 #ifdef FVWM_SM_DEBUG_PROTO
1043 fprintf(stderr
, "[FVWM_SMDEBUG][callback_save_complete]\n");
1050 callback_shutdown_cancelled(FSmcConn sm_conn
, FSmPointer client_data
)
1052 if (!SessionSupport
)
1057 #ifdef FVWM_SM_DEBUG_PROTO
1058 fprintf(stderr
, "[FVWM_SMDEBUG][callback_shutdown_cancelled]\n");
1061 if (!sent_save_done
)
1063 FSmcSaveYourselfDone(sm_conn
, False
);
1070 /* the following is taken from xsm */
1072 MyIoErrorHandler(FIceConn ice_conn
)
1074 if (!SessionSupport
)
1081 (*prev_handler
) (ice_conn
);
1088 InstallIOErrorHandler(void)
1090 FIceIOErrorHandler default_handler
;
1092 if (!SessionSupport
)
1097 prev_handler
= FIceSetIOErrorHandler (NULL
);
1098 default_handler
= FIceSetIOErrorHandler (MyIoErrorHandler
);
1099 if (prev_handler
== default_handler
)
1101 prev_handler
= NULL
;
1107 /* ---------------------------- interface functions ------------------------ */
1110 LoadGlobalState(char *filename
)
1113 char s
[4096], s1
[4096];
1115 int i1
, i2
, i3
, i4
, i5
;
1117 if (!does_file_version_match
)
1121 if (!filename
|| !*filename
)
1125 if ((f
= fopen(filename
, "r")) == NULL
)
1130 while (fgets(s
, sizeof(s
), f
))
1137 sscanf(s
, "%4000s", s1
);
1138 /* If we are restarting, [REAL_STATE_FILENAME] points
1139 * to the file containing the true session state. */
1140 if (SessionSupport
&& !strcmp(s1
, "[REAL_STATE_FILENAME]"))
1142 /* migo: temporarily (?) moved to
1143 LoadWindowStates (trick for gnome-session)
1144 sscanf(s, "%*s %s", s2);
1145 set_sm_properties(sm_conn, s2, FSmRestartIfRunning);
1146 set_real_state_filename(s2);
1149 else if (!strcmp(s1
, "[DESKTOP]"))
1151 sscanf(s
, "%*s %i", &i1
);
1154 else if (!strcmp(s1
, "[VIEWPORT]"))
1156 sscanf(s
, "%*s %i %i %i %i", &i1
, &i2
, &i3
,
1158 /* migo: we don't want to lose DeskTopSize in
1159 * configurations, and it does not work well
1160 * anyways - Gnome is not updated
1164 MoveViewport(i1
, i2
, True
);
1167 /* migo (08-Dec-1999): we don't want to eliminate config yet */
1168 else if (/*!Restarting*/ 0)
1170 /* Matthias: We don't want to restore too much
1171 * state if we are restarting, since that
1172 * would make restarting useless for rereading
1173 * changed rc files. */
1174 if (!strcmp(s1
, "[SCROLL]"))
1176 sscanf(s
, "%*s %i %i %i %i %i", &i1
,
1177 &i2
, &i3
, &i4
, &i5
);
1178 Scr
.EdgeScrollX
= i1
;
1179 Scr
.EdgeScrollY
= i2
;
1180 Scr
.ScrollDelay
= i3
;
1183 Scr
.flags
.edge_wrap_x
= 1;
1187 Scr
.flags
.edge_wrap_x
= 0;
1191 Scr
.flags
.edge_wrap_y
= 1;
1195 Scr
.flags
.edge_wrap_y
= 0;
1198 else if (!strcmp(s1
, "[MISC]"))
1200 sscanf(s
, "%*s %i %i %i", &i1
, &i2
,
1203 Scr
.ColormapFocus
= i2
;
1204 Scr
.ColorLimit
= i3
;
1206 else if (!strcmp(s1
, "[STYLE]"))
1208 sscanf(s
, "%*s %i %i", &i1
, &i2
);
1209 Scr
.gs
.EmulateMWM
= i1
;
1210 Scr
.gs
.EmulateWIN
= i2
;
1221 DisableRestoringState(void)
1229 LoadWindowStates(char *filename
)
1232 char s
[4096], s1
[4096];
1238 if (!VerifyVersionInfo(filename
))
1242 if (!filename
|| !*filename
)
1246 set_real_state_filename(filename
);
1247 if ((f
= fopen(filename
, "r")) == NULL
)
1252 #if defined(FVWM_SM_DEBUG_PROTO) || defined(FVWM_SM_DEBUG_FILES)
1253 fprintf(stderr
, "[FVWM_SMDEBUG] Loading %s\n", filename
);
1255 #ifdef FVWM_SM_DEBUG_FILES
1257 "mkdir -p /tmp/fs-load; cp ", filename
,
1261 while (fgets(s
, sizeof(s
), f
))
1264 sscanf(s
, "%4000s%n", s1
, &n
);
1265 if (!SessionSupport
/* migo: temporarily */ &&
1266 !strcmp(s1
, "[REAL_STATE_FILENAME]"))
1268 sscanf(s
, "%*s %s", s1
);
1269 set_sm_properties(sm_conn
, s1
, FSmRestartIfRunning
);
1270 set_real_state_filename(s1
);
1272 else if (!strcmp(s1
, "[CLIENT]"))
1274 sscanf(s
, "%*s %lx", &w
);
1276 matches
= (Match
*)saferealloc(
1277 (void *)matches
, sizeof(Match
) * num_match
);
1278 matches
[num_match
- 1].win
= w
;
1279 matches
[num_match
- 1].client_id
= NULL
;
1280 matches
[num_match
- 1].res_name
= NULL
;
1281 matches
[num_match
- 1].res_class
= NULL
;
1282 matches
[num_match
- 1].window_role
= NULL
;
1283 matches
[num_match
- 1].wm_name
= NULL
;
1284 matches
[num_match
- 1].wm_command_count
= 0;
1285 matches
[num_match
- 1].wm_command
= NULL
;
1286 matches
[num_match
- 1].x
= 0;
1287 matches
[num_match
- 1].y
= 0;
1288 matches
[num_match
- 1].w
= 100;
1289 matches
[num_match
- 1].h
= 100;
1290 matches
[num_match
- 1].x_max
= 0;
1291 matches
[num_match
- 1].y_max
= 0;
1292 matches
[num_match
- 1].w_max
= Scr
.MyDisplayWidth
;
1293 matches
[num_match
- 1].h_max
= Scr
.MyDisplayHeight
;
1294 matches
[num_match
- 1].width_defect_max
= 0;
1295 matches
[num_match
- 1].height_defect_max
= 0;
1296 matches
[num_match
- 1].icon_x
= 0;
1297 matches
[num_match
- 1].icon_y
= 0;
1298 matches
[num_match
- 1].desktop
= 0;
1299 matches
[num_match
- 1].layer
= 0;
1300 matches
[num_match
- 1].default_layer
= 0;
1301 memset(&(matches
[num_match
- 1].flags
), 0,
1302 sizeof(window_flags
));
1303 matches
[num_match
- 1].used
= 0;
1305 else if (!strcmp(s1
, "[GEOMETRY]"))
1307 sscanf(s
, "%*s %i %i %i %i %i %i %i %i %i %i %i %i"
1309 &(matches
[num_match
- 1].x
),
1310 &(matches
[num_match
- 1].y
),
1311 &(matches
[num_match
- 1].w
),
1312 &(matches
[num_match
- 1].h
),
1313 &(matches
[num_match
- 1].x_max
),
1314 &(matches
[num_match
- 1].y_max
),
1315 &(matches
[num_match
- 1].w_max
),
1316 &(matches
[num_match
- 1].h_max
),
1317 &(matches
[num_match
- 1].width_defect_max
),
1318 &(matches
[num_match
- 1].height_defect_max
),
1319 &(matches
[num_match
- 1].icon_x
),
1320 &(matches
[num_match
- 1].icon_y
),
1321 &(matches
[num_match
- 1].gravity
),
1322 &(matches
[num_match
- 1].max_x_offset
),
1323 &(matches
[num_match
- 1].max_y_offset
));
1325 else if (!strcmp(s1
, "[DESK]"))
1328 &(matches
[num_match
- 1].desktop
));
1330 else if (!strcmp(s1
, "[LAYER]"))
1332 sscanf(s
, "%*s %i %i",
1333 &(matches
[num_match
- 1].layer
),
1334 &(matches
[num_match
- 1].default_layer
));
1336 else if (!strcmp(s1
, "[PLACED_BY_BUTTON]"))
1339 &(matches
[num_match
- 1].placed_by_button
));
1341 else if (!strcmp(s1
, "[EWMH_DESKTOP]"))
1343 sscanf(s
, "%*s %lu",
1344 &(matches
[num_match
- 1].ewmh_hint_desktop
));
1346 else if (!strcmp(s1
, "[FLAGS]"))
1357 for (i
= 0; i
< sizeof(window_flags
); i
++)
1360 sscanf(ts
, "%02x ", &f
);
1362 (matches
[num_match
-1].flags
))[i
] = f
;
1366 else if (!strcmp(s1
, "[CLIENT_ID]"))
1373 sscanf(s2
, "%[^\n]", s1
);
1374 matches
[num_match
- 1].client_id
= duplicate(s1
);
1376 else if (!strcmp(s1
, "[WINDOW_ROLE]"))
1383 sscanf(s2
, "%[^\n]", s1
);
1384 matches
[num_match
- 1].window_role
= duplicate(s1
);
1386 else if (!strcmp(s1
, "[RES_NAME]"))
1393 sscanf(s2
, "%[^\n]", s1
);
1394 matches
[num_match
- 1].res_name
= duplicate(s1
);
1396 else if (!strcmp(s1
, "[RES_CLASS]"))
1403 sscanf(s2
, "%[^\n]", s1
);
1404 matches
[num_match
- 1].res_class
= duplicate(s1
);
1406 else if (!strcmp(s1
, "[WM_NAME]"))
1413 sscanf(s2
, "%[^\n]", s1
);
1414 matches
[num_match
- 1].wm_name
= duplicate(s1
);
1416 else if (!strcmp(s1
, "[WM_COMMAND]"))
1418 sscanf(s
, "%*s %i%n",
1419 &matches
[num_match
- 1].wm_command_count
, &pos
);
1420 matches
[num_match
- 1].wm_command
= (char **)
1422 matches
[num_match
- 1].
1423 wm_command_count
* sizeof (char *));
1425 i
< matches
[num_match
- 1].wm_command_count
; i
++)
1427 sscanf (s
+pos
, "%s%n", s1
, &pos1
);
1429 matches
[num_match
- 1].wm_command
[i
] =
1440 This routine (potentially) changes the flags STARTICONIC,
1441 MAXIMIZED, WSHADE and STICKY and the Desk and
1442 attr.x, .y, .width, .height entries. It also changes the
1443 stack_before pointer to return information about the
1444 desired stacking order. It expects the stacking order
1445 to be set up correctly beforehand!
1449 FvwmWindow
*ewin
, mwtsm_state_args
*ret_state_args
,
1450 initial_window_options_t
*win_opts
)
1454 if (!does_file_version_match
)
1458 for (i
= 0; i
< num_match
; i
++)
1460 if (!matches
[i
].used
&& matchWin(ewin
, &matches
[i
]))
1462 matches
[i
].used
= 1;
1466 /* We don't want to restore too much state if
1467 we are restarting, since that would make
1468 * restarting useless for rereading changed
1470 SET_DO_SKIP_WINDOW_LIST(
1472 DO_SKIP_WINDOW_LIST(&(matches
[i
])));
1473 SET_ICON_SUPPRESSED(
1475 IS_ICON_SUPPRESSED(&(matches
[i
])));
1476 SET_HAS_NO_ICON_TITLE(
1477 ewin
, HAS_NO_ICON_TITLE(&(matches
[i
])));
1479 FW_FOCUS_POLICY(ewin
),
1480 FP_IS_LENIENT(FW_FOCUS_POLICY(
1482 SET_ICON_STICKY_ACROSS_PAGES(
1483 ewin
, IS_ICON_STICKY_ACROSS_PAGES(
1485 SET_ICON_STICKY_ACROSS_DESKS(
1486 ewin
, IS_ICON_STICKY_ACROSS_DESKS(
1488 SET_DO_SKIP_ICON_CIRCULATE(
1489 ewin
, DO_SKIP_ICON_CIRCULATE(
1491 SET_DO_SKIP_SHADED_CIRCULATE(
1492 ewin
, DO_SKIP_SHADED_CIRCULATE(
1494 SET_DO_SKIP_CIRCULATE(
1495 ewin
, DO_SKIP_CIRCULATE(&(matches
[i
])));
1497 &FW_FOCUS_POLICY(ewin
),
1498 &FW_FOCUS_POLICY(&matches
[i
]),
1499 sizeof(focus_policy_t
));
1500 if (matches
[i
].wm_name
)
1502 free_window_names(ewin
, True
, False
);
1503 ewin
->name
.name
= matches
[i
].wm_name
;
1504 setup_visible_name(ewin
, False
);
1507 SET_NAME_CHANGED(ewin
,IS_NAME_CHANGED(&(matches
[i
])));
1509 ewin
, IS_PLACED_BY_FVWM(&(matches
[i
])));
1510 ret_state_args
->do_shade
= IS_SHADED(&(matches
[i
]));
1511 ret_state_args
->used_title_dir_for_shading
=
1512 USED_TITLE_DIR_FOR_SHADING(&(matches
[i
]));
1513 ret_state_args
->shade_dir
= SHADED_DIR(&(matches
[i
]));
1514 ret_state_args
->do_max
= IS_MAXIMIZED(&(matches
[i
]));
1515 SET_USER_STATES(ewin
, GET_USER_STATES(&(matches
[i
])));
1516 SET_ICON_MOVED(ewin
, IS_ICON_MOVED(&(matches
[i
])));
1517 if (IS_ICONIFIED(&(matches
[i
])))
1520 ICON_MOVED is necessary to make fvwm use
1521 icon_[xy]_loc for icon placement
1523 win_opts
->initial_state
= IconicState
;
1524 win_opts
->flags
.use_initial_icon_xy
= 1;
1525 win_opts
->initial_icon_x
= matches
[i
].icon_x
;
1526 win_opts
->initial_icon_y
= matches
[i
].icon_y
;
1527 if (!IS_STICKY_ACROSS_PAGES(&(matches
[i
])) &&
1528 !(IS_ICONIFIED(&(matches
[i
])) &&
1529 IS_ICON_STICKY_ACROSS_PAGES(
1532 win_opts
->initial_icon_x
-= Scr
.Vx
;
1533 win_opts
->initial_icon_y
-= Scr
.Vy
;
1536 ewin
->g
.normal
.x
= matches
[i
].x
;
1537 ewin
->g
.normal
.y
= matches
[i
].y
;
1538 ewin
->g
.normal
.width
= matches
[i
].w
;
1539 ewin
->g
.normal
.height
= matches
[i
].h
;
1540 ewin
->g
.max
.x
= matches
[i
].x_max
;
1541 ewin
->g
.max
.y
= matches
[i
].y_max
;
1542 ewin
->g
.max
.width
= matches
[i
].w_max
;
1543 ewin
->g
.max
.height
= matches
[i
].h_max
;
1544 ewin
->g
.max_defect
.width
= matches
[i
].width_defect_max
;
1545 ewin
->g
.max_defect
.height
=
1546 matches
[i
].height_defect_max
;
1547 ewin
->g
.max_offset
.x
= matches
[i
].max_x_offset
;
1548 ewin
->g
.max_offset
.y
= matches
[i
].max_y_offset
;
1549 SET_STICKY_ACROSS_PAGES(
1550 ewin
, IS_STICKY_ACROSS_PAGES(&(matches
[i
])));
1551 SET_STICKY_ACROSS_DESKS(
1552 ewin
, IS_STICKY_ACROSS_DESKS(&(matches
[i
])));
1553 ewin
->Desk
= (IS_STICKY_ACROSS_DESKS(ewin
)) ?
1554 Scr
.CurrentDesk
: matches
[i
].desktop
;
1555 set_layer(ewin
, matches
[i
].layer
);
1556 set_default_layer(ewin
, matches
[i
].default_layer
);
1557 ewin
->placed_by_button
= matches
[i
].placed_by_button
;
1558 /* Note: the Modal, skip pager, skip taskbar and
1559 * "stacking order" state are not restored here: there
1560 * are restored in EWMH_ExitStuff */
1561 ewin
->ewmh_hint_desktop
= matches
[i
].ewmh_hint_desktop
;
1562 SET_HAS_EWMH_INIT_WM_DESKTOP(
1563 ewin
, HAS_EWMH_INIT_WM_DESKTOP(&(matches
[i
])));
1564 SET_HAS_EWMH_INIT_HIDDEN_STATE(
1565 ewin
, HAS_EWMH_INIT_HIDDEN_STATE(
1567 SET_HAS_EWMH_INIT_MAXHORIZ_STATE(
1568 ewin
, HAS_EWMH_INIT_MAXHORIZ_STATE(
1570 SET_HAS_EWMH_INIT_MAXVERT_STATE(
1571 ewin
, HAS_EWMH_INIT_MAXVERT_STATE(
1573 SET_HAS_EWMH_INIT_SHADED_STATE(
1574 ewin
, HAS_EWMH_INIT_SHADED_STATE(
1576 SET_HAS_EWMH_INIT_STICKY_STATE(
1577 ewin
, HAS_EWMH_INIT_STICKY_STATE(
1587 RestartInSession (char *filename
, Bool is_native
, Bool _do_preserve_state
)
1589 do_preserve_state
= _do_preserve_state
;
1591 if (SessionSupport
&& sm_conn
&& is_native
)
1593 going_to_restart
= True
;
1595 save_state_file(filename
);
1596 set_sm_properties(sm_conn
, filename
, FSmRestartImmediately
);
1598 MoveViewport(0, 0, False
);
1604 if (!FSmcCloseConnection(sm_conn
, 0, NULL
) != FSmcClosedNow
)
1606 /* go a head any way ? */
1609 #ifdef FVWM_SM_DEBUG_PROTO
1610 fprintf(stderr
, "[FVWM_SMDEBUG]: Exiting, now SM must "
1613 /* Close all my pipes */
1616 exit(0); /* let the SM restart us */
1619 save_state_file(filename
);
1620 /* return and let Done restart us */
1625 void SetClientID(char *client_id
)
1627 if (!SessionSupport
)
1631 previous_sm_client_id
= client_id
;
1639 char error_string_ret
[4096] = "";
1641 FSmcCallbacks callbacks
;
1643 if (!SessionSupport
)
1646 MyIoErrorHandler(NULL
);
1647 callback_save_yourself2(NULL
, NULL
);
1652 InstallIOErrorHandler();
1654 callbacks
.save_yourself
.callback
= callback_save_yourself
;
1655 callbacks
.die
.callback
= callback_die
;
1656 callbacks
.save_complete
.callback
= callback_save_complete
;
1657 callbacks
.shutdown_cancelled
.callback
= callback_shutdown_cancelled
;
1658 callbacks
.save_yourself
.client_data
=
1659 callbacks
.die
.client_data
=
1660 callbacks
.save_complete
.client_data
=
1661 callbacks
.shutdown_cancelled
.client_data
= (FSmPointer
) NULL
;
1662 sm_conn
= FSmcOpenConnection(
1663 NULL
, &context
, FSmProtoMajor
, FSmProtoMinor
,
1664 FSmcSaveYourselfProcMask
| FSmcDieProcMask
|
1665 FSmcSaveCompleteProcMask
| FSmcShutdownCancelledProcMask
,
1666 &callbacks
, previous_sm_client_id
, &sm_client_id
, 4096,
1671 Don't annoy users which don't use a session manager
1673 if (previous_sm_client_id
)
1677 "While connecting to session manager:\n%s.",
1681 #ifdef FVWM_SM_DEBUG_PROTO
1682 fprintf(stderr
,"[FVWM_SMDEBUG] No SM connection\n");
1687 sm_fd
= FIceConnectionNumber(FSmcGetIceConnection(sm_conn
));
1688 #ifdef FVWM_SM_DEBUG_PROTO
1689 fprintf(stderr
,"[FVWM_SMDEBUG] Connectecd to a SM\n");
1691 set_init_function_name(0, "SessionInitFunction");
1692 set_init_function_name(1, "SessionRestartFunction");
1693 set_init_function_name(2, "SessionExitFunction");
1694 /* basically to restet our restart style hint after a
1696 set_sm_properties(sm_conn
, NULL
, FSmRestartIfRunning
);
1703 ProcessICEMsgs(void)
1705 FIceProcessMessagesStatus status
;
1707 if (!SessionSupport
)
1712 #ifdef FVWM_SM_DEBUG_PROTO
1713 fprintf(stderr
,"[FVWM_SMDEBUG][ProcessICEMsgs] %i\n", (int)sm_fd
);
1719 status
= FIceProcessMessages(FSmcGetIceConnection(sm_conn
), NULL
, NULL
);
1720 if (status
== FIceProcessMessagesIOError
)
1722 fvwm_msg(ERR
, "ProcessICEMSGS",
1723 "Connection to session manager lost\n");
1732 * Fvwm Function implementation: QuitSession, SaveSession, SaveQuitSession.
1733 * migo (15-Jun-1999): I am not sure this is right.
1734 * The session mabager must implement SmsSaveYourselfRequest (xsm doesn't?).
1735 * Alternative implementation may use unix signals, but this does not work
1736 * with all session managers (also must suppose that SM runs locally).
1738 int get_sm_pid(void)
1740 const char *session_manager_var
= getenv("SESSION_MANAGER");
1741 const char *sm_pid_str_ptr
;
1744 if (!SessionSupport
)
1749 if (!session_manager_var
)
1753 sm_pid_str_ptr
= strchr(session_manager_var
, ',');
1754 if (!sm_pid_str_ptr
)
1758 while (sm_pid_str_ptr
> session_manager_var
&& isdigit(*(--sm_pid_str_ptr
)))
1762 while (isdigit(*(++sm_pid_str_ptr
)))
1764 sm_pid
= sm_pid
* 10 + *sm_pid_str_ptr
- '0';
1771 * quit_session - hopefully shutdowns the session
1773 Bool
quit_session(void)
1775 if (!SessionSupport
)
1784 FSmcRequestSaveYourself(
1785 sm_conn
, FSmSaveLocal
, True
/* shutdown */, FSmInteractStyleAny
,
1786 False
/* fast */, True
/* global */);
1789 /* migo: xsm does not support RequestSaveYourself, but supports
1792 int sm_pid = get_sm_pid();
1793 if (!sm_pid) return False;
1794 return kill(sm_pid, SIGTERM) == 0 ? True : False;
1799 * save_session - hopefully saves the session
1801 Bool
save_session(void)
1803 if (!SessionSupport
)
1812 FSmcRequestSaveYourself(
1813 sm_conn
, FSmSaveBoth
, False
/* shutdown */, FSmInteractStyleAny
,
1814 False
/* fast */, True
/* global */);
1817 /* migo: xsm does not support RequestSaveYourself, but supports
1820 int sm_pid = get_sm_pid();
1821 if (!sm_pid) return False;
1822 return kill(sm_pid, SIGUSR1) == 0 ? True : False;
1827 * save_quit_session - hopefully saves and shutdowns the session
1829 Bool
save_quit_session(void)
1831 if (!SessionSupport
)
1841 FSmcRequestSaveYourself(
1842 sm_conn
, FSmSaveBoth
, True
/* shutdown */, FSmInteractStyleAny
,
1843 False
/* fast */, True
/* global */);
1846 /* migo: xsm does not support RequestSaveYourself, but supports
1849 if (save_session() == False) return False;
1850 sleep(3); / * doesn't work anyway * /
1851 if (quit_session() == False) return False;
1856 /* ---------------------------- builtin commands --------------------------- */
1858 void CMD_QuitSession(F_CMD_ARGS
)
1865 void CMD_SaveSession(F_CMD_ARGS
)
1872 void CMD_SaveQuitSession(F_CMD_ARGS
)
1874 save_quit_session();