/* -*-c-*- */ /* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* This file is strongly based on the corresponding files from twm and enlightenment. */ /* ---------------------------- included header files ---------------------- */ #include "config.h" #include #ifdef HAVE_GETPWUID #include #endif #include #include #include #include "libs/fvwmlib.h" #include "libs/FSMlib.h" #include "libs/Strings.h" #include "libs/System.h" #include "fvwm.h" #include "externs.h" #include "execcontext.h" #include "add_window.h" #include "misc.h" #include "screen.h" #include "session.h" #include "module_list.h" #include "module_interface.h" #include "stack.h" #include "icccm2.h" #include "virtual.h" #include "geometry.h" #include "move_resize.h" /* ---------------------------- local definitions -------------------------- */ /*#define FVWM_SM_DEBUG_PROTO*/ /*#define FVWM_SM_DEBUG_WINMATCH*/ /*#define FVWM_SM_DEBUG_FILES*/ /* ---------------------------- local macros ------------------------------- */ #define xstreq(a,b) ((!a && !b) || (a && b && (strcmp(a,b)==0))) /* ---------------------------- imports ------------------------------------ */ extern Bool Restarting; extern int master_pid; extern char **g_argv; extern int g_argc; /* ---------------------------- included code files ------------------------ */ /* ---------------------------- local types -------------------------------- */ typedef struct _match { unsigned long win; char *client_id; char *res_name; char *res_class; char *window_role; char *wm_name; int wm_command_count; char **wm_command; int x, y, w, h, icon_x, icon_y; int x_max, y_max, w_max, h_max; int width_defect_max, height_defect_max; int max_x_offset, max_y_offset; int desktop; int layer; int default_layer; int placed_by_button; int used; int gravity; unsigned long ewmh_hint_desktop; window_flags flags; } Match; /* ---------------------------- forward declarations ----------------------- */ /* ---------------------------- local variables ---------------------------- */ static char *previous_sm_client_id = NULL; static char *sm_client_id = NULL; static Bool sent_save_done = 0; static char *realStateFilename = NULL; static Bool goingToRestart = False; static FIceIOErrorHandler prev_handler; static FSmcConn sm_conn = NULL; static int num_match = 0; static Match *matches = NULL; static Bool does_file_version_match = False; static Bool doPreserveState = True; /* ---------------------------- exported variables (globals) --------------- */ int sm_fd = -1; /* ---------------------------- local functions ---------------------------- */ static char *duplicate(const char *s) { int l; char *r; if (!s) return NULL; l = strlen(s); r = (char *) safemalloc (sizeof(char)*(l+1)); strncpy(r, s, l+1); return r; } static char *get_version_string(void) { /* migo (14-Mar-2001): it is better to manually update a version string * in the stable branch, otherwise saving sessions becomes useless */ return CatString3(VERSION, ", ", __DATE__); /* return "2.6-0"; */ } static char *unspace_string(const char *str) { static const char *spaces = " \t\n"; char *tr_str = CatString2(str, NULL); int i; if (!tr_str) { return NULL; } for (i = 0; i < strlen(spaces); i++) { char *ptr = tr_str; while ((ptr = strchr(ptr, spaces[i])) != NULL) { *(ptr++) = '_'; } } return tr_str; } /* * It is a bit ugly to have a separate file format for * config files and session save files. The proper way * to do this may be to extend the config file format * to allow the specification of everything we need * to save here. Then the option "-restore xyz" could * be replaced by "-f xyz". */ static int SaveGlobalState(FILE *f) { fprintf(f, "[GLOBAL]\n"); fprintf(f, " [DESKTOP] %i\n", Scr.CurrentDesk); fprintf(f, " [VIEWPORT] %i %i %i %i\n", Scr.Vx, Scr.Vy, Scr.VxMax, Scr.VyMax); fprintf(f, " [SCROLL] %i %i %i %i %i %i\n", Scr.EdgeScrollX, Scr.EdgeScrollY, Scr.ScrollResistance, Scr.MoveResistance, !!(Scr.flags.do_edge_wrap_x), !!(Scr.flags.do_edge_wrap_y)); fprintf(f, " [SNAP] %i %i %i %i\n", Scr.SnapAttraction, Scr.SnapMode, Scr.SnapGridX, Scr.SnapGridY); fprintf(f, " [MISC] %i %i %i\n", Scr.ClickTime, Scr.ColormapFocus, Scr.ColorLimit); fprintf(f, " [STYLE] %i %i\n", Scr.gs.do_emulate_mwm, Scr.gs.do_emulate_win); return 1; } static void setRealStateFilename(char *filename) { if (!SessionSupport) { return; } if (realStateFilename) { free(realStateFilename); } realStateFilename = safestrdup(filename); return; } static char *getUniqueStateFilename(void) { const char *path = getenv("SM_SAVE_DIR"); char *filename; int fd; if (!SessionSupport) { return NULL; } if (!path) { path = getenv ("HOME"); } #ifdef HAVE_GETPWUID if (!path) { struct passwd *pwd; pwd = getpwuid(getuid()); if (pwd) { path = pwd->pw_dir; } } #endif if (!path) { return NULL; } filename = safestrdup(CatString2(path, "/.fs-XXXXXX")); fd = fvwm_mkstemp(filename); if (fd == -1) { free (filename); filename = NULL; } else { close (fd); } return filename; } static char * GetWindowRole(Window window) { XTextProperty tp; if (XGetTextProperty (dpy, window, &tp, _XA_WM_WINDOW_ROLE)) { if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0) { return ((char *) tp.value); } } if (XGetTextProperty (dpy, window, &tp, _XA_WINDOW_ROLE)) { if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0) { return ((char *) tp.value); } } return NULL; } static char * GetClientID(FvwmWindow *fw) { char *client_id = NULL; Window client_leader = None; Window window; XTextProperty tp; Atom actual_type; int actual_format; unsigned long nitems; unsigned long bytes_after; unsigned char *prop = NULL; window = FW_W(fw); if (XGetWindowProperty( dpy, window, _XA_WM_CLIENT_LEADER, 0L, 1L, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop) == Success) { if (actual_type == XA_WINDOW && actual_format == 32 && nitems == 1 && bytes_after == 0) { client_leader = (Window)(*(long *)prop); } } if (!client_leader && fw->wmhints && (fw->wmhints->flags & WindowGroupHint)) { client_leader = fw->wmhints->window_group; } if (client_leader) { if ( XGetTextProperty( dpy, client_leader, &tp, _XA_SM_CLIENT_ID)) { if (tp.encoding == XA_STRING && tp.format == 8 && tp.nitems != 0) { client_id = (char *) tp.value; } } } if (prop) { XFree (prop); } return client_id; } /* ** Verify the current fvwm version with the version that stroed the state file. ** No state will be restored if versions don't match. */ static Bool VerifyVersionInfo(char *filename) { FILE *f; char s[4096], s1[4096]; if (!filename || !*filename) { return False; } if ((f = fopen(filename, "r")) == NULL) { return False; } while (fgets(s, sizeof(s), f)) { sscanf(s, "%4000s", s1); if (!strcmp(s1, "[FVWM_VERSION]")) { char *current_v = get_version_string(); sscanf(s, "%*s %[^\n]", s1); if (strcmp(s1, current_v) == 0) { does_file_version_match = True; } else { fvwm_msg( ERR, "VerifyVersionInfo", "State file version (%s) does not" " match the current version (%s), " "state file is ignored.", s1, current_v); break; } } } fclose(f); return does_file_version_match; } static int SaveVersionInfo(FILE *f) { fprintf(f, "[FVWM_VERSION] %s\n", get_version_string()); return 1; } static int SaveWindowStates(FILE *f) { char *client_id; char *window_role; char **wm_command; int wm_command_count; FvwmWindow *ewin; rectangle save_g; rectangle ig; int i; int layer; for (ewin = get_next_window_in_stack_ring(&Scr.FvwmRoot); ewin != &Scr.FvwmRoot; ewin = get_next_window_in_stack_ring(ewin)) { Bool is_icon_sticky_across_pages; if (!XGetGeometry( dpy, FW_W(ewin), &JunkRoot, &JunkX, &JunkY, (unsigned int*)&JunkWidth, (unsigned int*)&JunkHeight, (unsigned int*)&JunkBW, (unsigned int*)&JunkDepth)) { /* Don't save the state of windows that already died * (i.e. modules)! */ continue; } is_icon_sticky_across_pages = is_window_sticky_across_pages(ewin); wm_command = NULL; wm_command_count = 0; client_id = GetClientID(ewin); if (!client_id) { /* no client id, some session manager do not manage * such client ... this can cause problem */ if (XGetCommand( dpy, FW_W(ewin), &wm_command, &wm_command_count) && wm_command && wm_command_count > 0) { /* ok */ } else { /* No client id and no WM_COMMAND, the client * cannot be managed by the sessiom manager * skip it! */ if (!Restarting) { if (wm_command) { XFreeStringList(wm_command); wm_command = NULL; } continue; } } } fprintf(f, "[CLIENT] %lx\n", FW_W(ewin)); if (client_id) { fprintf(f, " [CLIENT_ID] %s\n", client_id); XFree(client_id); } window_role = GetWindowRole(FW_W(ewin)); if (window_role) { fprintf(f, " [WINDOW_ROLE] %s\n", window_role); XFree(window_role); } if (client_id && window_role) { /* we have enough information */ } else { if (ewin->class.res_class) { fprintf(f, " [RES_NAME] %s\n", ewin->class.res_name); } if (ewin->class.res_name) { fprintf(f, " [RES_CLASS] %s\n", ewin->class.res_class); } if (ewin->name.name) { fprintf(f, " [WM_NAME] %s\n", ewin->name.name); } if (wm_command && wm_command_count > 0) { fprintf(f, " [WM_COMMAND] %i", wm_command_count); for (i = 0; i < wm_command_count; i++) { fprintf(f, " %s", unspace_string(wm_command[i])); } fprintf(f, "\n"); } } /* !window_role */ if (wm_command) { XFreeStringList(wm_command); wm_command = NULL; } gravity_get_naked_geometry( ewin->hints.win_gravity, ewin, &save_g, &ewin->g.normal); if (IS_STICKY_ACROSS_PAGES(ewin)) { save_g.x -= Scr.Vx; save_g.y -= Scr.Vy; } get_visible_icon_geometry(ewin, &ig); fprintf( f, " [GEOMETRY] %i %i %i %i %i %i %i %i %i %i %i %i" " %i %i %i\n", save_g.x, save_g.y, save_g.width, save_g.height, ewin->g.max.x, ewin->g.max.y, ewin->g.max.width, ewin->g.max.height, ewin->g.max_defect.width, ewin->g.max_defect.height, ig.x + ((!is_icon_sticky_across_pages) ? Scr.Vx : 0), ig.y + ((!is_icon_sticky_across_pages) ? Scr.Vy : 0), ewin->hints.win_gravity, ewin->g.max_offset.x, ewin->g.max_offset.y); fprintf(f, " [DESK] %i\n", ewin->Desk); /* set the layer to the default layer if the layer has been * set by an ewmh hint */ layer = get_layer(ewin); if (layer == ewin->ewmh_hint_layer && layer > 0) { layer = Scr.DefaultLayer; } fprintf(f, " [LAYER] %i %i\n", layer, ewin->default_layer); fprintf(f, " [PLACED_BY_BUTTON] %i\n", ewin->placed_by_button); fprintf(f, " [EWMH_DESKTOP] %lu\n", ewin->ewmh_hint_desktop); fprintf(f, " [FLAGS] "); for (i = 0; i < sizeof(window_flags); i++) { fprintf(f, "%02x ", (int)(((unsigned char *)&(ewin->flags))[i])); } fprintf(f, "\n"); } return 1; } /* This complicated logic is from twm, where it is explained */ static Bool matchWin(FvwmWindow *w, Match *m) { char *client_id = NULL; char *window_role = NULL; char **wm_command = NULL; int wm_command_count = 0, i; int found; found = 0; client_id = GetClientID(w); if (Restarting) { if (FW_W(w) == m->win) { found = 1; } } else if (xstreq(client_id, m->client_id)) { /* client_id's match */ window_role = GetWindowRole(FW_W(w)); if (client_id && (window_role || m->window_role)) { /* We have or had a window role, base decision on it */ found = xstreq(window_role, m->window_role); } else if (xstreq(w->class.res_name, m->res_name) && xstreq(w->class.res_class, m->res_class) && (IS_NAME_CHANGED(w) || IS_NAME_CHANGED(m) || xstreq(w->name.name, m->wm_name))) { if (client_id) { /* If we have a client_id, we don't * compare WM_COMMAND, since it will be * different. */ found = 1; } else { /* for non-SM-aware clients we also * compare WM_COMMAND */ if (!XGetCommand( dpy, FW_W(w), &wm_command, &wm_command_count)) { wm_command = NULL; wm_command_count = 0; } if (wm_command_count == m->wm_command_count) { for (i = 0; i < wm_command_count; i++) { if (strcmp(unspace_string( wm_command[i]), m->wm_command[i])!=0) { break; } } if (i == wm_command_count) { /* migo (21/Oct/1999): * on restarts compare * window ids too */ /* But if we restart we only need * to compare window ids * olicha (2005-01-06) */ found = 1; } } /* if (wm_command_count ==... */ } /* else if res_class, res_name and wm_name agree */ } /* else no window roles */ } /* if client_id's agree */ #ifdef FVWM_SM_DEBUG_WINMATCH fprintf(stderr, "\twin(%s, %s, %s, %s, %s,", w->class.res_name, w->class.res_class, w->name.name, (client_id)? client_id:"(null)", (window_role)? window_role:"(null)"); if (wm_command) { for (i = 0; i < wm_command_count; i++) { fprintf(stderr," %s", wm_command[i]); } fprintf(stderr,","); } else { fprintf(stderr," no_wmc,"); } fprintf(stderr," %d)", IS_NAME_CHANGED(w)); fprintf(stderr,"\n[%d]", found); fprintf(stderr, "\tmat(%s, %s, %s, %s, %s,", m->res_name, m->res_class, m->wm_name, (m->client_id)?m->client_id:"(null)", (m->window_role)?m->window_role:"(null)"); if (m->wm_command) { for (i = 0; i < m->wm_command_count; i++) { fprintf(stderr," %s", m->wm_command[i]); } fprintf(stderr,","); } else { fprintf(stderr," no_wmc,"); } fprintf(stderr," %d)\n\n", IS_NAME_CHANGED(m)); #endif if (client_id) { XFree(client_id); } if (window_role) { XFree(window_role); } if (wm_command) { XFreeStringList (wm_command); } return found; } static int saveStateFile(char *filename) { FILE *f; int success; if (!filename || !*filename) { return 0; } if ((f = fopen(filename, "w")) == NULL) { return 0; } fprintf(f, "# This file is generated by fvwm." " It stores global and window states.\n"); fprintf(f, "# Normally, you must never delete this file," " it will be auto-deleted.\n\n"); if (SessionSupport && goingToRestart) { fprintf(f, "[REAL_STATE_FILENAME] %s\n", realStateFilename); goingToRestart = False; /* not needed */ } success = doPreserveState? SaveVersionInfo(f) && SaveWindowStates(f) && SaveGlobalState(f): 1; doPreserveState = True; if (fclose(f) != 0) return 0; #ifdef FVWM_SM_DEBUG_FILES system(CatString3( "mkdir -p /tmp/fs-save; cp ", filename, " /tmp/fs-save")); #endif #if defined(FVWM_SM_DEBUG_PROTO) || defined(FVWM_SM_DEBUG_FILES) fprintf(stderr, "[FVWM_SMDEBUG] Saving %s\n", filename); #endif return success; } static void setSmProperties(FSmcConn sm_conn, char *filename, char hint) { FSmProp prop1, prop2, prop3, prop4, prop5, prop6, prop7, *props[7]; FSmPropValue prop1val, prop2val, prop3val, prop4val, prop7val; struct passwd *pwd; char *user_id; char screen_num[32]; int numVals, i, priority = 30; Bool xsmDetected = False; if (!SessionSupport) { return; } #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr, "[FVWM_SMDEBUG][setSmProperties] state filename: %s%s\n", (filename)? filename:"(null)", sm_conn? "": " - not connected"); #endif if (!sm_conn) { return; } pwd = getpwuid (getuid()); user_id = pwd->pw_name; prop1.name = FSmProgram; prop1.type = FSmARRAY8; prop1.num_vals = 1; prop1.vals = &prop1val; prop1val.value = g_argv[0]; prop1val.length = strlen (g_argv[0]); prop2.name = FSmUserID; prop2.type = FSmARRAY8; prop2.num_vals = 1; prop2.vals = &prop2val; prop2val.value = (FSmPointer) user_id; prop2val.length = strlen (user_id); prop3.name = FSmRestartStyleHint; prop3.type = FSmCARD8; prop3.num_vals = 1; prop3.vals = &prop3val; prop3val.value = (FSmPointer) &hint; prop3val.length = 1; prop4.name = "_GSM_Priority"; prop4.type = FSmCARD8; prop4.num_vals = 1; prop4.vals = &prop4val; prop4val.value = (FSmPointer) &priority; prop4val.length = 1; sprintf(screen_num, "%d", (int)Scr.screen); prop5.name = FSmCloneCommand; prop5.type = FSmLISTofARRAY8; prop5.vals = (FSmPropValue *)malloc((g_argc + 2) * sizeof (FSmPropValue)); numVals = 0; for (i = 0; i < g_argc; i++) { if (strcmp (g_argv[i], "-clientId") == 0 || strcmp (g_argv[i], "-restore") == 0 || strcmp (g_argv[i], "-d") == 0 || (strcmp (g_argv[i], "-s") == 0 && i+1 < g_argc && g_argv[i+1][0] != '-')) { i++; } else if (strcmp (g_argv[i], "-s") != 0) { prop5.vals[numVals].value = (FSmPointer) g_argv[i]; prop5.vals[numVals++].length = strlen (g_argv[i]); } } prop5.vals[numVals].value = (FSmPointer) "-s"; prop5.vals[numVals++].length = 2; prop5.vals[numVals].value = (FSmPointer) screen_num; prop5.vals[numVals++].length = strlen (screen_num); prop5.num_vals = numVals; if (filename) { prop6.name = FSmRestartCommand; prop6.type = FSmLISTofARRAY8; prop6.vals = (FSmPropValue *)malloc( (g_argc + 6) * sizeof (FSmPropValue)); numVals = 0; for (i = 0; i < g_argc; i++) { if (strcmp (g_argv[i], "-clientId") == 0 || strcmp (g_argv[i], "-restore") == 0 || strcmp (g_argv[i], "-d") == 0 || (strcmp (g_argv[i], "-s") == 0 && i+1 < g_argc && g_argv[i+1][0] != '-')) { i++; } else if (strcmp (g_argv[i], "-s") != 0) { prop6.vals[numVals].value = (FSmPointer) g_argv[i]; prop6.vals[numVals++].length = strlen (g_argv[i]); } } prop6.vals[numVals].value = (FSmPointer) "-s"; prop6.vals[numVals++].length = 2; prop6.vals[numVals].value = (FSmPointer) screen_num; prop6.vals[numVals++].length = strlen (screen_num); prop6.vals[numVals].value = (FSmPointer) "-clientId"; prop6.vals[numVals++].length = 9; prop6.vals[numVals].value = (FSmPointer) sm_client_id; prop6.vals[numVals++].length = strlen (sm_client_id); prop6.vals[numVals].value = (FSmPointer) "-restore"; prop6.vals[numVals++].length = 8; prop6.vals[numVals].value = (FSmPointer) filename; prop6.vals[numVals++].length = strlen (filename); prop6.num_vals = numVals; prop7.name = FSmDiscardCommand; xsmDetected = StrEquals(getenv("SESSION_MANAGER_NAME"), "xsm"); if (xsmDetected) { /* the protocol spec says that the discard command should be LISTofARRAY8 on posix systems, but xsm demands that it be ARRAY8. */ char *discardCommand = alloca( (10 + strlen(filename)) * sizeof(char)); sprintf (discardCommand, "rm -f '%s'", filename); prop7.type = FSmARRAY8; prop7.num_vals = 1; prop7.vals = &prop7val; prop7val.value = (FSmPointer) discardCommand; prop7val.length = strlen (discardCommand); } else { prop7.type = FSmLISTofARRAY8; prop7.num_vals = 3; prop7.vals = (FSmPropValue *) malloc ( 3 * sizeof (FSmPropValue)); prop7.vals[0].value = "rm"; prop7.vals[0].length = 2; prop7.vals[1].value = "-f"; prop7.vals[1].length = 2; prop7.vals[2].value = filename; prop7.vals[2].length = strlen (filename); } } props[0] = &prop1; props[1] = &prop2; props[2] = &prop3; props[3] = &prop4; props[4] = &prop5; if (filename) { props[5] = &prop6; props[6] = &prop7; FSmcSetProperties (sm_conn, 7, props); free ((char *) prop6.vals); if (!xsmDetected) { free ((char *) prop7.vals); } } else { FSmcSetProperties (sm_conn, 5, props); } free ((char *) prop5.vals); } static void callback_save_yourself2(FSmcConn sm_conn, FSmPointer client_data) { Bool success = 0; char *filename; if (!SessionSupport) { return; } filename = getUniqueStateFilename(); #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr, "[FVWM_SMDEBUG][callback_save_yourself2]\n"); #endif success = saveStateFile(filename); if (success) { setSmProperties(sm_conn, filename, FSmRestartIfRunning); setRealStateFilename(filename); } free(filename); FSmcSaveYourselfDone (sm_conn, success); sent_save_done = 1; } static void callback_save_yourself(FSmcConn sm_conn, FSmPointer client_data, int save_style, Bool shutdown, int interact_style, Bool fast) { if (!SessionSupport) { return; } #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr, "[FVWM_SMDEBUG][callback_save_yourself] " "(save=%d, shut=%d, intr=%d, fast=%d)\n", save_style, shutdown, interact_style, fast); #endif if (save_style == FSmSaveGlobal) { /* nothing to do */ #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr, "[FVWM_SMDEBUG][callback_save_yourself] " "Global Save type ... do nothing\n"); #endif FSmcSaveYourselfDone (sm_conn, True); sent_save_done = 1; return; } #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr, "[FVWM_SMDEBUG][callback_save_yourself] " "Both or Local save type, going to phase 2 ..."); #endif if (!FSmcRequestSaveYourselfPhase2( sm_conn, callback_save_yourself2, NULL)) { FSmcSaveYourselfDone (sm_conn, False); sent_save_done = 1; #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr, " failed!\n"); #endif } else { #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr, " OK\n"); #endif sent_save_done = 0; } return; } static void callback_die(FSmcConn sm_conn, FSmPointer client_data) { if (!SessionSupport) { return; } #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr, "[FVWM_SMDEBUG][callback_die]\n"); #endif if (FSmcCloseConnection(sm_conn, 0, NULL) != FSmcClosedNow) { /* go a head any way ? */ } sm_fd = -1; if (master_pid != getpid()) { kill(master_pid, SIGTERM); } Done(0, NULL); } static void callback_save_complete(FSmcConn sm_conn, FSmPointer client_data) { if (!SessionSupport) { return; } #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr, "[FVWM_SMDEBUG][callback_save_complete]\n"); #endif return; } static void callback_shutdown_cancelled(FSmcConn sm_conn, FSmPointer client_data) { if (!SessionSupport) { return; } #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr, "[FVWM_SMDEBUG][callback_shutdown_cancelled]\n"); #endif if (!sent_save_done) { FSmcSaveYourselfDone(sm_conn, False); sent_save_done = 1; } return; } /* the following is taken from xsm */ static void MyIoErrorHandler(FIceConn ice_conn) { if (!SessionSupport) { return; } if (prev_handler) { (*prev_handler) (ice_conn); } return; } static void InstallIOErrorHandler(void) { FIceIOErrorHandler default_handler; if (!SessionSupport) { return; } prev_handler = FIceSetIOErrorHandler (NULL); default_handler = FIceSetIOErrorHandler (MyIoErrorHandler); if (prev_handler == default_handler) { prev_handler = NULL; } return; } /* ---------------------------- interface functions ------------------------ */ void LoadGlobalState(char *filename) { FILE *f; char s[4096], s1[4096]; /* char s2[256]; */ int i1, i2, i3, i4, i5, i6; if (!does_file_version_match) { return; } if (!filename || !*filename) { return; } if ((f = fopen(filename, "r")) == NULL) { return; } while (fgets(s, sizeof(s), f)) { i1 = 0; i2 = 0; i3 = 0; i4 = 0; i5 = 0; i6 = 0; sscanf(s, "%4000s", s1); /* If we are restarting, [REAL_STATE_FILENAME] points * to the file containing the true session state. */ if (SessionSupport && !strcmp(s1, "[REAL_STATE_FILENAME]")) { /* migo: temporarily (?) moved to LoadWindowStates (trick for gnome-session) sscanf(s, "%*s %s", s2); setFSmProperties (sm_conn, s2, FSmRestartIfRunning); setRealStateFilename(s2); */ } else if (!strcmp(s1, "[DESKTOP]")) { sscanf(s, "%*s %i", &i1); goto_desk(i1); } else if (!strcmp(s1, "[VIEWPORT]")) { sscanf(s, "%*s %i %i %i %i", &i1, &i2, &i3, &i4); /* migo: we don't want to lose DeskTopSize in * configurations, and it does not work well * anyways - Gnome is not updated Scr.VxMax = i3; Scr.VyMax = i4; */ MoveViewport(i1, i2, True); } #if 0 /* migo (08-Dec-1999): we don't want to eliminate config yet */ else if (/*!Restarting*/ 0) { /* Matthias: We don't want to restore too much * state if we are restarting, since that * would make restarting useless for rereading * changed rc files. */ if (!strcmp(s1, "[SCROLL]")) { sscanf(s, "%*s %i %i %i %i %i %i", &i1, &i2, &i3, &i4, &i5, &i6); Scr.EdgeScrollX = i1; Scr.EdgeScrollY = i2; Scr.ScrollResistance = i3; Scr.MoveResistance = i4; if (i5) { Scr.flags.edge_wrap_x = 1; } else { Scr.flags.edge_wrap_x = 0; } if (i6) { Scr.flags.edge_wrap_y = 1; } else { Scr.flags.edge_wrap_y = 0; } } else if (!strcmp(s1, "[SNAP]")) { sscanf(s, "%*s %i %i %i %i", &i1, &i2, &i3, &i4); Scr.SnapAttraction = i1; Scr.SnapMode = i2; Scr.SnapGridX = i3; Scr.SnapGridY = i4; } else if (!strcmp(s1, "[MISC]")) { sscanf(s, "%*s %i %i %i", &i1, &i2, &i3); Scr.ClickTime = i1; Scr.ColormapFocus = i2; Scr.ColorLimit = i3; } else if (!strcmp(s1, "[STYLE]")) { sscanf(s, "%*s %i %i", &i1, &i2); Scr.gs.EmulateMWM = i1; Scr.gs.EmulateWIN = i2; } } #endif } fclose(f); return; } void DisableRestoringState(void) { num_match = 0; return; } void LoadWindowStates(char *filename) { FILE *f; char s[4096], s1[4096]; char *s2; int i, pos, pos1; unsigned long w; int n; if (!VerifyVersionInfo(filename)) { return; } if (!filename || !*filename) { return; } setRealStateFilename(filename); if ((f = fopen(filename, "r")) == NULL) { return; } #if defined(FVWM_SM_DEBUG_PROTO) || defined(FVWM_SM_DEBUG_FILES) fprintf(stderr, "[FVWM_SMDEBUG] Loading %s\n", filename); #endif #ifdef FVWM_SM_DEBUG_FILES system(CatString3( "mkdir -p /tmp/fs-load; cp ", filename, " /tmp/fs-load")); #endif while (fgets(s, sizeof(s), f)) { n = 0; sscanf(s, "%4000s%n", s1, &n); if (!SessionSupport /* migo: temporarily */ && !strcmp(s1, "[REAL_STATE_FILENAME]")) { sscanf(s, "%*s %s", s1); setSmProperties (sm_conn, s1, FSmRestartIfRunning); setRealStateFilename(s1); } else if (!strcmp(s1, "[CLIENT]")) { sscanf(s, "%*s %lx", &w); num_match++; matches = (Match *)saferealloc( (void *)matches, sizeof(Match) * num_match); matches[num_match - 1].win = w; matches[num_match - 1].client_id = NULL; matches[num_match - 1].res_name = NULL; matches[num_match - 1].res_class = NULL; matches[num_match - 1].window_role = NULL; matches[num_match - 1].wm_name = NULL; matches[num_match - 1].wm_command_count = 0; matches[num_match - 1].wm_command = NULL; matches[num_match - 1].x = 0; matches[num_match - 1].y = 0; matches[num_match - 1].w = 100; matches[num_match - 1].h = 100; matches[num_match - 1].x_max = 0; matches[num_match - 1].y_max = 0; matches[num_match - 1].w_max = Scr.MyDisplayWidth; matches[num_match - 1].h_max = Scr.MyDisplayHeight; matches[num_match - 1].width_defect_max = 0; matches[num_match - 1].height_defect_max = 0; matches[num_match - 1].icon_x = 0; matches[num_match - 1].icon_y = 0; matches[num_match - 1].desktop = 0; matches[num_match - 1].layer = 0; matches[num_match - 1].default_layer = 0; memset(&(matches[num_match - 1].flags), 0, sizeof(window_flags)); matches[num_match - 1].used = 0; } else if (!strcmp(s1, "[GEOMETRY]")) { sscanf(s, "%*s %i %i %i %i %i %i %i %i %i %i %i %i" " %i %i %i", &(matches[num_match - 1].x), &(matches[num_match - 1].y), &(matches[num_match - 1].w), &(matches[num_match - 1].h), &(matches[num_match - 1].x_max), &(matches[num_match - 1].y_max), &(matches[num_match - 1].w_max), &(matches[num_match - 1].h_max), &(matches[num_match - 1].width_defect_max), &(matches[num_match - 1].height_defect_max), &(matches[num_match - 1].icon_x), &(matches[num_match - 1].icon_y), &(matches[num_match - 1].gravity), &(matches[num_match - 1].max_x_offset), &(matches[num_match - 1].max_y_offset)); } else if (!strcmp(s1, "[DESK]")) { sscanf(s, "%*s %i", &(matches[num_match - 1].desktop)); } else if (!strcmp(s1, "[LAYER]")) { sscanf(s, "%*s %i %i", &(matches[num_match - 1].layer), &(matches[num_match - 1].default_layer)); } else if (!strcmp(s1, "[PLACED_BY_BUTTON]")) { sscanf(s, "%*s %i", &(matches[num_match - 1].placed_by_button)); } else if (!strcmp(s1, "[EWMH_DESKTOP]")) { sscanf(s, "%*s %lu", &(matches[num_match - 1].ewmh_hint_desktop)); } else if (!strcmp(s1, "[FLAGS]")) { char *ts = s; /* skip [FLAGS] */ while (*ts != ']') { ts++; } ts++; for (i = 0; i < sizeof(window_flags); i++) { unsigned int f; sscanf(ts, "%02x ", &f); ((unsigned char *)& (matches[num_match-1].flags))[i] = f; ts += 3; } } else if (!strcmp(s1, "[CLIENT_ID]")) { s2 = s + n; if (*s2 != 0) { s2++; } sscanf(s2, "%[^\n]", s1); matches[num_match - 1].client_id = duplicate(s1); } else if (!strcmp(s1, "[WINDOW_ROLE]")) { s2 = s + n; if (*s2 != 0) { s2++; } sscanf(s2, "%[^\n]", s1); matches[num_match - 1].window_role = duplicate(s1); } else if (!strcmp(s1, "[RES_NAME]")) { s2 = s + n; if (*s2 != 0) { s2++; } sscanf(s2, "%[^\n]", s1); matches[num_match - 1].res_name = duplicate(s1); } else if (!strcmp(s1, "[RES_CLASS]")) { s2 = s + n; if (*s2 != 0) { s2++; } sscanf(s2, "%[^\n]", s1); matches[num_match - 1].res_class = duplicate(s1); } else if (!strcmp(s1, "[WM_NAME]")) { s2 = s + n; if (*s2 != 0) { s2++; } sscanf(s2, "%[^\n]", s1); matches[num_match - 1].wm_name = duplicate(s1); } else if (!strcmp(s1, "[WM_COMMAND]")) { sscanf(s, "%*s %i%n", &matches[num_match - 1].wm_command_count, &pos); matches[num_match - 1].wm_command = (char **) safemalloc( matches[num_match - 1]. wm_command_count * sizeof (char *)); for (i = 0; i < matches[num_match - 1].wm_command_count; i++) { sscanf (s+pos, "%s%n", s1, &pos1); pos += pos1; matches[num_match - 1].wm_command[i] = duplicate (s1); } } } fclose(f); return; } /* This routine (potentially) changes the flags STARTICONIC, MAXIMIZED, WSHADE and STICKY and the Desk and attr.x, .y, .width, .height entries. It also changes the stack_before pointer to return information about the desired stacking order. It expects the stacking order to be set up correctly beforehand! */ Bool MatchWinToSM( FvwmWindow *ewin, mwtsm_state_args *ret_state_args, initial_window_options_t *win_opts) { int i; if (!does_file_version_match) { return False; } for (i = 0; i < num_match; i++) { if (!matches[i].used && matchWin(ewin, &matches[i])) { matches[i].used = 1; if (!Restarting) { /* We don't want to restore too much state if we are restarting, since that would make * restarting useless for rereading changed * rc files. */ SET_DO_SKIP_WINDOW_LIST( ewin, DO_SKIP_WINDOW_LIST(&(matches[i]))); SET_ICON_SUPPRESSED( ewin, IS_ICON_SUPPRESSED(&(matches[i]))); SET_HAS_NO_ICON_TITLE( ewin, HAS_NO_ICON_TITLE(&(matches[i]))); FPS_LENIENT( FW_FOCUS_POLICY(ewin), FP_IS_LENIENT(FW_FOCUS_POLICY( &(matches[i])))); SET_ICON_STICKY_ACROSS_PAGES( ewin, IS_ICON_STICKY_ACROSS_PAGES( &(matches[i]))); SET_ICON_STICKY_ACROSS_DESKS( ewin, IS_ICON_STICKY_ACROSS_DESKS( &(matches[i]))); SET_DO_SKIP_ICON_CIRCULATE( ewin, DO_SKIP_ICON_CIRCULATE( &(matches[i]))); SET_DO_SKIP_SHADED_CIRCULATE( ewin, DO_SKIP_SHADED_CIRCULATE( &(matches[i]))); SET_DO_SKIP_CIRCULATE( ewin, DO_SKIP_CIRCULATE(&(matches[i]))); memcpy( &FW_FOCUS_POLICY(ewin), &FW_FOCUS_POLICY(&matches[i]), sizeof(focus_policy_t)); if (matches[i].wm_name) { free_window_names(ewin, True, False); ewin->name.name = matches[i].wm_name; setup_visible_name(ewin, False); } } SET_NAME_CHANGED(ewin,IS_NAME_CHANGED(&(matches[i]))); SET_PLACED_BY_FVWM( ewin, IS_PLACED_BY_FVWM(&(matches[i]))); ret_state_args->do_shade = IS_SHADED(&(matches[i])); ret_state_args->used_title_dir_for_shading = USED_TITLE_DIR_FOR_SHADING(&(matches[i])); ret_state_args->shade_dir = SHADED_DIR(&(matches[i])); ret_state_args->do_max = IS_MAXIMIZED(&(matches[i])); SET_USER_STATES(ewin, GET_USER_STATES(&(matches[i]))); SET_ICON_MOVED(ewin, IS_ICON_MOVED(&(matches[i]))); if (IS_ICONIFIED(&(matches[i]))) { /* ICON_MOVED is necessary to make fvwm use icon_[xy]_loc for icon placement */ win_opts->initial_state = IconicState; win_opts->flags.use_initial_icon_xy = 1; win_opts->initial_icon_x = matches[i].icon_x; win_opts->initial_icon_y = matches[i].icon_y; if (!IS_STICKY_ACROSS_PAGES(&(matches[i])) && !(IS_ICONIFIED(&(matches[i])) && IS_ICON_STICKY_ACROSS_PAGES( &(matches[i])))) { win_opts->initial_icon_x -= Scr.Vx; win_opts->initial_icon_y -= Scr.Vy; } } ewin->g.normal.x = matches[i].x; ewin->g.normal.y = matches[i].y; ewin->g.normal.width = matches[i].w; ewin->g.normal.height = matches[i].h; ewin->g.max.x = matches[i].x_max; ewin->g.max.y = matches[i].y_max; ewin->g.max.width = matches[i].w_max; ewin->g.max.height = matches[i].h_max; ewin->g.max_defect.width = matches[i].width_defect_max; ewin->g.max_defect.height = matches[i].height_defect_max; ewin->g.max_offset.x = matches[i].max_x_offset; ewin->g.max_offset.y = matches[i].max_y_offset; SET_STICKY_ACROSS_PAGES( ewin, IS_STICKY_ACROSS_PAGES(&(matches[i]))); SET_STICKY_ACROSS_DESKS( ewin, IS_STICKY_ACROSS_DESKS(&(matches[i]))); ewin->Desk = (IS_STICKY_ACROSS_DESKS(ewin)) ? Scr.CurrentDesk : matches[i].desktop; set_layer(ewin, matches[i].layer); set_default_layer(ewin, matches[i].default_layer); ewin->placed_by_button = matches[i].placed_by_button; /* Note: the Modal, skip pager, skip taskbar and * "stacking order" state are not restored here: there * are restored in EWMH_ExitStuff */ ewin->ewmh_hint_desktop = matches[i].ewmh_hint_desktop; SET_HAS_EWMH_INIT_WM_DESKTOP( ewin, HAS_EWMH_INIT_WM_DESKTOP(&(matches[i]))); SET_HAS_EWMH_INIT_FULLSCREEN_STATE( ewin, HAS_EWMH_INIT_FULLSCREEN_STATE( &(matches[i]))); SET_HAS_EWMH_INIT_HIDDEN_STATE( ewin, HAS_EWMH_INIT_HIDDEN_STATE( &(matches[i]))); SET_HAS_EWMH_INIT_MAXHORIZ_STATE( ewin, HAS_EWMH_INIT_MAXHORIZ_STATE( &(matches[i]))); SET_HAS_EWMH_INIT_MAXVERT_STATE( ewin, HAS_EWMH_INIT_MAXVERT_STATE( &(matches[i]))); SET_HAS_EWMH_INIT_SHADED_STATE( ewin, HAS_EWMH_INIT_SHADED_STATE( &(matches[i]))); SET_HAS_EWMH_INIT_STICKY_STATE( ewin, HAS_EWMH_INIT_STICKY_STATE( &(matches[i]))); return True; } } return False; } void RestartInSession (char *filename, Bool isNative, Bool _doPreserveState) { doPreserveState = _doPreserveState; if (SessionSupport && sm_conn && isNative) { goingToRestart = True; saveStateFile(filename); setSmProperties(sm_conn, filename, FSmRestartImmediately); MoveViewport(0, 0, False); Reborder(); CloseICCCM2(); XCloseDisplay(dpy); if (!FSmcCloseConnection(sm_conn, 0, NULL) != FSmcClosedNow) { /* go a head any way ? */ } #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr, "[FVWM_SMDEBUG]: Exiting, now SM must " "restart us.\n"); #endif /* Close all my pipes */ module_kill_all(); exit(0); /* let the SM restart us */ } saveStateFile(filename); /* return and let Done restart us */ return; } void SetClientID(char *client_id) { if (!SessionSupport) { return; } previous_sm_client_id = client_id; return; } void SessionInit(void) { char error_string_ret[4096] = ""; FSmPointer context; FSmcCallbacks callbacks; if (!SessionSupport) { /* -Wall fixes */ MyIoErrorHandler(NULL); callback_save_yourself2(NULL, NULL); return; } context = NULL; InstallIOErrorHandler(); callbacks.save_yourself.callback = callback_save_yourself; callbacks.die.callback = callback_die; callbacks.save_complete.callback = callback_save_complete; callbacks.shutdown_cancelled.callback = callback_shutdown_cancelled; callbacks.save_yourself.client_data = callbacks.die.client_data = callbacks.save_complete.client_data = callbacks.shutdown_cancelled.client_data = (FSmPointer) NULL; sm_conn = FSmcOpenConnection( NULL, &context, FSmProtoMajor, FSmProtoMinor, FSmcSaveYourselfProcMask | FSmcDieProcMask | FSmcSaveCompleteProcMask | FSmcShutdownCancelledProcMask, &callbacks, previous_sm_client_id, &sm_client_id, 4096, error_string_ret); if (!sm_conn) { /* Don't annoy users which don't use a session manager */ if (previous_sm_client_id) { fvwm_msg( ERR, "SessionInit", "While connecting to session manager:\n%s.", error_string_ret); } sm_fd = -1; #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr,"[FVWM_SMDEBUG] No SM connection\n"); #endif } else { sm_fd = FIceConnectionNumber(FSmcGetIceConnection(sm_conn)); #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr,"[FVWM_SMDEBUG] Connectecd to a SM\n"); #endif setInitFunctionName(0, "SessionInitFunction"); setInitFunctionName(1, "SessionRestartFunction"); setInitFunctionName(2, "SessionExitFunction"); /* basically to restet our restart style hint after a * restart */ setSmProperties(sm_conn, NULL, FSmRestartIfRunning); } return; } void ProcessICEMsgs(void) { FIceProcessMessagesStatus status; if (!SessionSupport) { return; } #ifdef FVWM_SM_DEBUG_PROTO fprintf(stderr,"[FVWM_SMDEBUG][ProcessICEMsgs] %i\n", (int)sm_fd); #endif if (sm_fd < 0) { return; } status = FIceProcessMessages(FSmcGetIceConnection(sm_conn), NULL, NULL); if (status == FIceProcessMessagesIOError) { fvwm_msg(ERR, "ProcessICEMSGS", "Connection to session manager lost\n"); sm_conn = NULL; sm_fd = -1; } return; } /* * Fvwm Function implementation: QuitSession, SaveSession, SaveQuitSession. * migo (15-Jun-1999): I am not sure this is right. * The session mabager must implement SmsSaveYourselfRequest (xsm doesn't?). * Alternative implementation may use unix signals, but this does not work * with all session managers (also must suppose that SM runs locally). */ int getSmPid(void) { const char *sessionManagerVar = getenv("SESSION_MANAGER"); const char *smPidStrPtr; int smPid = 0; if (!SessionSupport) { return 0; } if (!sessionManagerVar) { return 0; } smPidStrPtr = strchr(sessionManagerVar, ','); if (!smPidStrPtr) { return 0; } while (smPidStrPtr > sessionManagerVar && isdigit(*(--smPidStrPtr))) { /* nothing */ } while (isdigit(*(++smPidStrPtr))) { smPid = smPid * 10 + *smPidStrPtr - '0'; } return smPid; } /* * quitSession - hopefully shutdowns the session */ Bool quitSession(void) { if (!SessionSupport) { return False; } if (!sm_conn) { return False; } FSmcRequestSaveYourself( sm_conn, FSmSaveLocal, True /* shutdown */, FSmInteractStyleAny, False /* fast */, True /* global */); return True; /* migo: xsm does not support RequestSaveYourself, but supports * signals: */ /* int smPid = getSmPid(); if (!smPid) return False; return kill(smPid, SIGTERM) == 0? True: False; */ } /* * saveSession - hopefully saves the session */ Bool saveSession(void) { if (!SessionSupport) { return False; } if (!sm_conn) { return False; } FSmcRequestSaveYourself( sm_conn, FSmSaveBoth, False /* shutdown */, FSmInteractStyleAny, False /* fast */, True /* global */); return True; /* migo: xsm does not support RequestSaveYourself, but supports * signals: */ /* int smPid = getSmPid(); if (!smPid) return False; return kill(smPid, SIGUSR1) == 0? True: False; */ } /* * saveQuitSession - hopefully saves and shutdowns the session */ Bool saveQuitSession(void) { if (!SessionSupport) { return False; } if (!sm_conn) { return False; } FSmcRequestSaveYourself( sm_conn, FSmSaveBoth, True /* shutdown */, FSmInteractStyleAny, False /* fast */, True /* global */); return True; /* migo: xsm does not support RequestSaveYourself, but supports * signals: */ /* if (saveSession() == False) return False; sleep(3); / * doesn't work anyway * / if (quitSession() == False) return False; return True; */ } /* ---------------------------- builtin commands --------------------------- */ void CMD_QuitSession(F_CMD_ARGS) { quitSession(); return; } void CMD_SaveSession(F_CMD_ARGS) { saveSession(); return; } void CMD_SaveQuitSession(F_CMD_ARGS) { saveQuitSession(); return; }