From 1bf9de380991206d4afa104cd31c567583f288c2 Mon Sep 17 00:00:00 2001 From: Dana Jansens Date: Sun, 6 May 2007 05:19:19 +0000 Subject: [PATCH] refactor all the session stuff. yay, it works properly now. make sessions save the desktop being displayed. can we get rid of config_firstdesk now? refactor startup a bit. focus the window that was focused when we were restarted. have clients properly restore their session state. add undecorated to the saved session state for clients. --- openbox/client.c | 152 ++++++++----- openbox/debug.c | 3 + openbox/debug.h | 1 + openbox/focus.c | 17 +- openbox/openbox.c | 123 +++++++++-- openbox/openbox.h | 5 +- openbox/screen.c | 6 +- openbox/session.c | 534 +++++++++++++++++++++++++--------------------- openbox/session.h | 10 +- 9 files changed, 525 insertions(+), 326 deletions(-) diff --git a/openbox/client.c b/openbox/client.c index 9327cbfd..17d4c4a1 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -83,12 +83,13 @@ static void client_change_state(ObClient *self); static void client_change_wm_state(ObClient *self); static void client_apply_startup_state(ObClient *self, gint x, gint y); static void client_restore_session_state(ObClient *self); -static void client_restore_session_stacking(ObClient *self); +static gboolean client_restore_session_stacking(ObClient *self); static ObAppSettings *client_get_settings_state(ObClient *self); static void client_update_transient_tree(ObClient *self, ObGroup *oldgroup, ObGroup *newgroup, ObClient* oldparent, ObClient *newparent); +static void client_present(ObClient *self, gboolean here, gboolean raise); void client_startup(gboolean reconfig) { @@ -321,10 +322,10 @@ void client_manage(Window window) grab_server(FALSE); stacking_add_nonintrusive(CLIENT_AS_WINDOW(self)); - client_restore_session_stacking(self); /* focus the new window? */ if (ob_state() != OB_STATE_STARTING && + (!self->session || self->session->focused) && !self->iconic && /* this means focus=true for window is same as config_focus_new=true */ ((config_focus_new || (settings && settings->focus == 1)) || @@ -377,6 +378,10 @@ void client_manage(Window window) */ ob_debug("placing window 0x%x at %d, %d with size %d x %d\n", self->window, newx, newy, self->area.width, self->area.height); + if (self->session) + ob_debug("session requested %d %d\n", + self->session->x, self->session->y); + client_apply_startup_state(self, newx, newy); mouse_grab_for_client(self, TRUE); @@ -443,7 +448,8 @@ void client_manage(Window window) Also if you don't have focus_new enabled, then it's going to get raised to the top. Legacy begets legacy I guess? */ - client_raise(self); + if (!client_restore_session_stacking(self)) + client_raise(self); } /* this has to happen before we try focus the window, but we want it to @@ -456,8 +462,10 @@ void client_manage(Window window) a window maps since its not based on an action from the user like clicking a window to activate it. so keep the new window out of the way but do focus it. */ - if (activate) - client_activate(self, FALSE, TRUE); + if (activate) { + gboolean stacked = client_restore_session_stacking(self); + client_present(self, FALSE, !stacked); + } /* add to client list/map */ client_list = g_list_append(client_list, self); @@ -685,13 +693,22 @@ static void client_restore_session_state(ObClient *self) { GList *it; - if (!(it = session_state_find(self))) + ob_debug_type(OB_DEBUG_SM, + "Restore session for client %s\n", self->title); + + if (!(it = session_state_find(self))) { + ob_debug_type(OB_DEBUG_SM, + "Session data not found for client %s\n", self->title); return; + } self->session = it->data; + ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s\n", + self->title); + RECT_SET_POINT(self->area, self->session->x, self->session->y); - self->positioned = PPosition; + self->positioned = USPosition; if (self->session->w > 0) self->area.width = self->session->w; if (self->session->h > 0) @@ -713,28 +730,33 @@ static void client_restore_session_state(ObClient *self) self->below = self->session->below; self->max_horz = self->session->max_horz; self->max_vert = self->session->max_vert; + self->undecorated = self->session->undecorated; } -static void client_restore_session_stacking(ObClient *self) +static gboolean client_restore_session_stacking(ObClient *self) { - GList *it; + GList *it, *mypos; + + if (!self->session) return FALSE; - if (!self->session) return; + mypos = g_list_find(session_saved_state, self->session); + if (!mypos) return FALSE; - it = g_list_find(session_saved_state, self->session); - for (it = g_list_previous(it); it; it = g_list_previous(it)) { + /* start above me and look for the first client */ + for (it = g_list_previous(mypos); it; it = g_list_previous(it)) { GList *cit; - for (cit = client_list; cit; cit = g_list_next(cit)) - if (session_state_cmp(it->data, cit->data)) - break; - if (cit) { - client_calc_layer(self); - stacking_below(CLIENT_AS_WINDOW(self), - CLIENT_AS_WINDOW(cit->data)); - break; + for (cit = client_list; cit; cit = g_list_next(cit)) { + ObClient *c = cit->data; + /* found a client that was in the session, so go below it */ + if (c->session == it->data) { + stacking_below(CLIENT_AS_WINDOW(self), + CLIENT_AS_WINDOW(cit->data)); + return TRUE; + } } } + return FALSE; } void client_move_onscreen(ObClient *self, gboolean rude) @@ -3252,6 +3274,47 @@ gboolean client_focus(ObClient *self) return TRUE; } +/*! Present the client to the user. + @param raise If the client should be raised or not. You should only set + raise to false if you don't care if the window is completely + hidden. +*/ +static void client_present(ObClient *self, gboolean here, gboolean raise) +{ + /* if using focus_delay, stop the timer now so that focus doesn't + go moving on us */ + event_halt_focus_delay(); + + if (client_normal(self) && screen_showing_desktop) + screen_show_desktop(FALSE, FALSE); + if (self->iconic) + client_iconify(self, FALSE, here); + if (self->desktop != DESKTOP_ALL && + self->desktop != screen_desktop) + { + if (here) + client_set_desktop(self, screen_desktop, FALSE); + else + screen_set_desktop(self->desktop); + } else if (!self->frame->visible) + /* if its not visible for other reasons, then don't mess + with it */ + return; + if (self->shaded) + client_shade(self, FALSE); + + client_focus(self); + + if (raise) { + /* we do this as an action here. this is rather important. this is + because we want the results from the focus change to take place + BEFORE we go about raising the window. when a fullscreen window + loses focus, we need this or else the raise wont be able to raise + above the to-lose-focus fullscreen window. */ + client_raise(self); + } +} + void client_activate(ObClient *self, gboolean here, gboolean user) { guint32 last_time = focus_client ? focus_client->user_time : CurrentTime; @@ -3274,36 +3337,7 @@ void client_activate(ObClient *self, gboolean here, gboolean user) if (event_curtime != CurrentTime) self->user_time = event_curtime; - /* if using focus_delay, stop the timer now so that focus doesn't - go moving on us */ - event_halt_focus_delay(); - - if (client_normal(self) && screen_showing_desktop) - screen_show_desktop(FALSE, FALSE); - if (self->iconic) - client_iconify(self, FALSE, here); - if (self->desktop != DESKTOP_ALL && - self->desktop != screen_desktop) - { - if (here) - client_set_desktop(self, screen_desktop, FALSE); - else - screen_set_desktop(self->desktop); - } else if (!self->frame->visible) - /* if its not visible for other reasons, then don't mess - with it */ - return; - if (self->shaded) - client_shade(self, FALSE); - - client_focus(self); - - /* we do this as an action here. this is rather important. this is - because we want the results from the focus change to take place - BEFORE we go about raising the window. when a fullscreen window - loses focus, we need this or else the raise wont be able to raise - above the to-lose-focus fullscreen window. */ - client_raise(self); + client_present(self, here, TRUE); } } @@ -3517,15 +3551,25 @@ void client_update_sm_client_id(ObClient *self) self->sm_client_id = NULL; if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) && - self->group) - PROP_GETS(self->group->leader, sm_client_id, locale, - &self->sm_client_id); + self->group) { + ob_debug_type(OB_DEBUG_SM, "Client %s does not have session id\n", + self->title); + if (!PROP_GETS(self->group->leader, sm_client_id, locale, + &self->sm_client_id)) { + ob_debug_type(OB_DEBUG_SM, "Client %s does not have session id on " + "group window\n", self->title); + } else + ob_debug_type(OB_DEBUG_SM, "Client %s has session id on " + "group window\n", self->title); + } else + ob_debug_type(OB_DEBUG_SM, "Client %s has session id\n", + self->title); } #define WANT_EDGE(cur, c) \ if(cur == c) \ continue; \ - if(!client_normal(cur)) \ + if(!client_normal(cur)) \ continue; \ if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \ continue; \ diff --git a/openbox/debug.c b/openbox/debug.c index 3a765dc5..0fceba48 100644 --- a/openbox/debug.c +++ b/openbox/debug.c @@ -64,6 +64,9 @@ void ob_debug_type(ObDebugType type, const gchar *a, ...) case OB_DEBUG_APP_BUGS: fprintf(stderr, "APPLICATION BUG: "); break; + case OB_DEBUG_SM: + fprintf(stderr, "SESSION: "); + break; default: g_assert_not_reached(); } diff --git a/openbox/debug.h b/openbox/debug.h index a38ae75b..3c6bedf5 100644 --- a/openbox/debug.h +++ b/openbox/debug.h @@ -28,6 +28,7 @@ void ob_debug(const gchar *a, ...); typedef enum { OB_DEBUG_FOCUS, OB_DEBUG_APP_BUGS, + OB_DEBUG_SM, OB_DEBUG_TYPE_NUM } ObDebugType; diff --git a/openbox/focus.c b/openbox/focus.c index 94257205..35a2e027 100644 --- a/openbox/focus.c +++ b/openbox/focus.c @@ -232,24 +232,19 @@ ObClient* focus_fallback_target(gboolean allow_refocus, ObClient *old) ObClient *c = it->data; /* fallback focus to a window if: 1. it is actually focusable, cuz if it's not then we're sending - focus off to nothing - 2. it is validated. if the window is about to disappear, then - don't try focus it. - 3. it is visible on the current desktop. this ignores - omnipresent windows, which are problematic in their own rite. - 4. it's not iconic - 5. it is a normal type window, don't fall back onto a dock or + focus off to nothing. this includes if it is visible right now + 2. it is on the current desktop. this ignores omnipresent + windows, which are problematic in their own rite. + 3. it is a normal type window, don't fall back onto a dock or a splashscreen or a desktop window (save the desktop as a backup fallback though) */ - if (client_can_focus(c) && !c->iconic) + if (client_can_focus(c)) { if (c->desktop == screen_desktop && client_normal(c)) { ob_debug_type(OB_DEBUG_FOCUS, "found in focus order\n"); return it->data; - } else if ((c->desktop == screen_desktop || - c->desktop == DESKTOP_ALL) && - c->type == OB_CLIENT_TYPE_DESKTOP && + } else if (c->type == OB_CLIENT_TYPE_DESKTOP && desktop == NULL) desktop = c; } diff --git a/openbox/openbox.c b/openbox/openbox.c index 934afa41..63379641 100644 --- a/openbox/openbox.c +++ b/openbox/openbox.c @@ -81,6 +81,9 @@ ObMainLoop *ob_main_loop; Display *ob_display; gint ob_screen; gboolean ob_replace_wm = FALSE; +gboolean ob_sm_use = TRUE; +gchar *ob_sm_id = NULL; +gchar *ob_sm_save_file = NULL; static ObState state; static gboolean xsync = FALSE; @@ -95,7 +98,9 @@ static gboolean being_replaced = FALSE; static gchar *config_type = NULL; static void signal_handler(gint signal, gpointer data); -static void parse_args(gint argc, gchar **argv); +static void parse_env(char **argv0); +static void remove_args(gint *argc, gchar **argv, gint index, gint num); +static void parse_args(gint *argc, gchar **argv); static Cursor load_cursor(const gchar *name, guint fontval); gint main(gint argc, gchar **argv) @@ -115,8 +120,9 @@ gint main(gint argc, gchar **argv) g_message(_("Unable to change to home directory '%s': %s"), g_get_home_dir(), g_strerror(errno)); - /* parse out command line args */ - parse_args(argc, argv); + /* parse out environment and command line args */ + parse_env(&argv[0]); + parse_args(&argc, argv); if (!remote_control) { parse_paths_startup(); @@ -124,6 +130,7 @@ gint main(gint argc, gchar **argv) session_startup(argc, argv); } + ob_display = XOpenDisplay(NULL); if (ob_display == NULL) ob_exit_with_error("Failed to open the display."); @@ -284,9 +291,21 @@ gint main(gint argc, gchar **argv) menu_frame_startup(reconfigure); if (!reconfigure) { + guint32 xid; + ObWindow *w; + /* get all the existing windows */ client_manage_all(); - focus_fallback(TRUE); + focus_nothing(); + + /* focus what was focused if a wm was already running */ + if (PROP_GET32(RootWindow(ob_display, ob_screen), + net_active_window, window, &xid) && + (w = g_hash_table_lookup(window_map, &xid)) && + WINDOW_IS_CLIENT(w)) + { + client_focus(WINDOW_AS_CLIENT(w)); + } } else { GList *it; @@ -359,11 +378,32 @@ gint main(gint argc, gchar **argv) } } + /* we remove the session arguments from argv, so put them back */ + if (ob_sm_save_file != NULL) { + guint l = g_strv_length(argv); + argv = g_renew(gchar*, argv, l+2); + argv[l] = g_strdup("--sm-save-file"); + argv[l+1] = ob_sm_save_file; + argv[l+2] = NULL; + } + if (ob_sm_id != NULL) { + guint l = g_strv_length(argv); + argv = g_renew(gchar*, argv, l+2); + argv[l] = g_strdup("--sm-client-id"); + argv[l+1] = ob_sm_id; + argv[l+2] = NULL; + } + /* re-run me */ execvp(argv[0], argv); /* try how we were run */ execlp(argv[0], g_path_get_basename(argv[0]), (char *)NULL); /* last resort */ } + + /* free stuff passed in from the command line or environment */ + g_free(ob_sm_save_file); + g_free(ob_sm_id); + g_free(config_type); return exitcode; } @@ -405,30 +445,52 @@ static void print_version() static void print_help() { g_print(_("Syntax: openbox [options]\n")); - g_print(_("\nOptions:\n\n")); + g_print(_("\nOptions:\n")); g_print(_(" --config TYPE Specify the configuration profile to use\n")); #ifdef USE_SM - g_print(_(" --sm-disable Disable connection to session manager\n")); - g_print(_(" --sm-client-id ID Specify session management ID\n")); - g_print(_(" --sm-save-file FILE Specify file to load a saved session from\n")); + g_print(_(" --sm-disable Disable connection to the session manager\n")); #endif g_print(_(" --replace Replace the currently running window manager\n")); g_print(_(" --help Display this help and exit\n")); g_print(_(" --version Display the version and exit\n")); - g_print(_("\nPassing messages to a running Openbox instance:\n\n")); + g_print(_("\nPassing messages to a running Openbox instance:\n")); g_print(_(" --reconfigure Reload Openbox's configuration\n")); - g_print(_("\nDebugging options:\n\n")); + g_print(_("\nOptions for internal use:\n")); + g_print(_(" --sm-save-file FILE Specify file to load a saved session from\n")); + g_print(_(" --sm-client-id ID Specify session management ID\n")); + g_print(_("\nDebugging options:\n")); g_print(_(" --sync Run in synchronous mode\n")); g_print(_(" --debug Display debugging output\n")); g_print(_(" --debug-focus Display debugging output for focus handling\n")); - g_print(_("\nPlease report bugs at %s\n\n"), PACKAGE_BUGREPORT); + g_print(_("\nPlease report bugs at %s\n"), PACKAGE_BUGREPORT); } -static void parse_args(gint argc, gchar **argv) +static void parse_env(gchar **argv0) +{ + const char *c; + + /* pretend we are this other application */ + if ((c = getenv("OPENBOX_RESTART_BINARY"))) + *argv0 = g_strdup(c); + unsetenv("OPENBOX_RESTART_BINARY"); +} + +static void remove_args(gint *argc, gchar **argv, gint index, gint num) +{ + gint i; + + for (i = index; i < index + num; ++i) + argv[i] = argv[i+num]; + for (; i < *argc; ++i) + argv[i] = NULL; + *argc -= num; +} + +static void parse_args(gint *argc, gchar **argv) { gint i; - for (i = 1; i < argc; ++i) { + for (i = 1; i < *argc; ++i) { if (!strcmp(argv[i], "--version")) { print_version(); exit(0); @@ -443,23 +505,54 @@ static void parse_args(gint argc, gchar **argv) xsync = TRUE; } else if (!strcmp(argv[i], "--debug")) { ob_debug_show_output(TRUE); + ob_debug_enable(OB_DEBUG_SM, TRUE); ob_debug_enable(OB_DEBUG_APP_BUGS, TRUE); } else if (!strcmp(argv[i], "--debug-focus")) { ob_debug_show_output(TRUE); + ob_debug_enable(OB_DEBUG_SM, TRUE); ob_debug_enable(OB_DEBUG_APP_BUGS, TRUE); ob_debug_enable(OB_DEBUG_FOCUS, TRUE); } else if (!strcmp(argv[i], "--reconfigure")) { remote_control = 1; +/* don't make this do anything if it's not in --help .. } else if (!strcmp(argv[i], "--restart")) { remote_control = 2; +*/ } else if (!strcmp(argv[i], "--config")) { - if (i == argc - 1) /* no args left */ - g_printerr(_("--config-file requires an argument\n")); + if (i == *argc - 1) /* no args left */ + g_printerr(_("--config requires an argument\n")); else { config_type = g_strdup(argv[i+1]); ++i; } } +#ifdef USE_SM + else if (!strcmp(argv[i], "--sm-save-file")) { + if (i == *argc - 1) /* no args left */ + g_printerr(_("--sm-save-file requires an argument\n")); + else { + ob_sm_save_file = g_strdup(argv[i+1]); + remove_args(argc, argv, i, 2); + --i; /* this arg was removed so go back */ + } + } else if (!strcmp(argv[i], "--sm-client-id")) { + if (i == *argc - 1) /* no args left */ + g_printerr(_("--sm-client-id requires an argument\n")); + else { + ob_sm_id = g_strdup(argv[i+1]); + remove_args(argc, argv, i, 2); + --i; /* this arg was removed so go back */ + } + } else if (!strcmp(argv[i], "--sm-disable")) { + ob_sm_use = FALSE; + } +#endif + else { + /* this is a memleak.. oh well.. heh */ + gchar *err = g_strdup_printf + ("Invalid command line argument '%s'\n", argv[i]); + ob_exit_with_error(err); + } } } diff --git a/openbox/openbox.h b/openbox/openbox.h index 03dcdab5..fadb6981 100644 --- a/openbox/openbox.h +++ b/openbox/openbox.h @@ -40,8 +40,11 @@ extern Display *ob_display; /*! The number of the screen on which we're running */ extern gint ob_screen; -extern gchar *ob_sm_id; extern gboolean ob_sm_use; +extern gchar *ob_sm_id; +/* This save_file will get pass to ourselves if we restart too! So we won't + make a new file every time, yay. */ +extern gchar *ob_sm_save_file; extern gboolean ob_replace_wm; /* The state of execution of the window manager */ diff --git a/openbox/screen.c b/openbox/screen.c index a413411a..a32f649f 100644 --- a/openbox/screen.c +++ b/openbox/screen.c @@ -28,6 +28,7 @@ #include "config.h" #include "screen.h" #include "client.h" +#include "session.h" #include "frame.h" #include "event.h" #include "focus.h" @@ -328,7 +329,10 @@ void screen_startup(gboolean reconfig) d < screen_num_desktops) { screen_set_desktop(d); - } else + } else if (session_desktop >= 0) + screen_set_desktop(MIN((guint)session_desktop, + screen_num_desktops)); + else screen_set_desktop(MIN(config_screen_firstdesk, screen_num_desktops) - 1); diff --git a/openbox/session.c b/openbox/session.c index 62cff4b7..124ff8bc 100644 --- a/openbox/session.c +++ b/openbox/session.c @@ -18,26 +18,24 @@ /* This session code is largely inspired by metacity code. */ -#ifndef USE_SM - #include "session.h" -#include "client.h" -GList *session_saved_state; +struct _ObClient; + +GList *session_saved_state = NULL; +gint session_desktop = -1; +#ifndef USE_SM void session_startup(gint argc, gchar **argv) {} void session_shutdown(gboolean permanent) {} -GList* session_state_find(ObClient *c) { return NULL; } -gboolean session_state_cmp(ObSessionState *s, ObClient *c) { return FALSE; } -void session_state_free(ObSessionState *state) {} - +GList* session_state_find(struct _ObClient *c) { return NULL; } #else #include "debug.h" #include "openbox.h" -#include "session.h" #include "client.h" #include "prop.h" +#include "screen.h" #include "gettext.h" #include "parser/parse.h" @@ -52,18 +50,24 @@ void session_state_free(ObSessionState *state) {} #include -GList *session_saved_state; +#define SM_ERR_LEN 1024 -static gboolean sm_disable; -static SmcConn sm_conn; -static gchar *save_file; -static gchar *sm_id; -static gint sm_argc; -static gchar **sm_argv; -static gchar *sm_sessions_path; +static SmcConn sm_conn; +static gint sm_argc; +static gchar **sm_argv; -static void session_load(gchar *path); -static gboolean session_save(); +static gboolean session_connect(); + +static void session_load_file(gchar *path); +static gboolean session_save_to_file(); + +static void session_setup_program(); +static void session_setup_user(); +static void session_setup_restart_style(gboolean restart); +static void session_setup_pid(); +static void session_setup_priority(); +static void session_setup_clone_command(); +static void session_setup_restart_command(); static void sm_save_yourself(SmcConn conn, SmPointer data, gint save_type, Bool shutdown, gint interact_style, Bool fast); @@ -71,283 +75,329 @@ static void sm_die(SmcConn conn, SmPointer data); static void sm_save_complete(SmcConn conn, SmPointer data); static void sm_shutdown_cancelled(SmcConn conn, SmPointer data); -static void save_commands() +static gboolean session_state_cmp(ObSessionState *s, ObClient *c); +static void session_state_free(ObSessionState *state); + +void session_startup(gint argc, gchar **argv) { - SmProp *props[2]; - SmProp prop_cmd = { SmCloneCommand, SmLISTofARRAY8, 1, }; - SmProp prop_res = { SmRestartCommand, SmLISTofARRAY8 }; - gint i; + gchar *dir; - prop_cmd.vals = g_new(SmPropValue, sm_argc); - prop_cmd.num_vals = sm_argc; - for (i = 0; i < sm_argc; ++i) { - prop_cmd.vals[i].value = sm_argv[i]; - prop_cmd.vals[i].length = strlen(sm_argv[i]); - } + if (!ob_sm_use) return; - prop_res.vals = g_new(SmPropValue, sm_argc + 2); - prop_res.num_vals = sm_argc + 2; - for (i = 0; i < sm_argc; ++i) { - prop_res.vals[i].value = sm_argv[i]; - prop_res.vals[i].length = strlen(sm_argv[i]); + sm_argc = argc; + sm_argv = argv; + + dir = g_build_filename(parse_xdg_data_home_path(), + "openbox", "sessions", NULL); + if (!parse_mkdir_path(dir, 0700)) { + g_message(_("Unable to make directory '%s': %s"), + dir, g_strerror(errno)); } - prop_res.vals[i].value = "--sm-save-file"; - prop_res.vals[i++].length = strlen("--sm-save-file"); - prop_res.vals[i].value = save_file; - prop_res.vals[i++].length = strlen(save_file); + if (ob_sm_save_file != NULL) { + ob_debug_type(OB_DEBUG_SM, "Loading from session file %s\n", + ob_sm_save_file); + session_load_file(ob_sm_save_file); + } else { + gchar *filename; + + /* this algo is from metacity */ + filename = g_strdup_printf("%u-%u-%u.obs", + (guint)time(NULL), + (guint)getpid(), + g_random_int()); + ob_sm_save_file = g_build_filename(dir, filename, NULL); + g_free(filename); + } - props[0] = &prop_res; - props[1] = &prop_cmd; - SmcSetProperties(sm_conn, 2, props); + if (session_connect()) { + session_setup_program(); + session_setup_user(); + session_setup_restart_style(TRUE); + session_setup_pid(); + session_setup_priority(); + session_setup_clone_command(); + } - g_free(prop_res.vals); - g_free(prop_cmd.vals); + g_free(dir); } -static void remove_args(gint *argc, gchar ***argv, gint index, gint num) +void session_shutdown(gboolean permanent) { - gint i; + if (!ob_sm_use) return; - for (i = index; i < index + num; ++i) - (*argv)[i] = (*argv)[i+num]; - *argc -= num; -} + if (sm_conn) { + /* if permanent is true then we will change our session state so that + the SM won't run us again */ + session_setup_restart_style(!permanent); -static void parse_args(gint *argc, gchar ***argv) -{ - gint i; + SmcCloseConnection(sm_conn, 0, NULL); - for (i = 1; i < *argc; ++i) { - if (!strcmp((*argv)[i], "--sm-client-id")) { - if (i == *argc - 1) /* no args left */ - g_printerr(_("--sm-client-id requires an argument\n")); - else { - sm_id = g_strdup((*argv)[i+1]); - remove_args(argc, argv, i, 2); - ++i; - } - } else if (!strcmp((*argv)[i], "--sm-save-file")) { - if (i == *argc - 1) /* no args left */ - g_printerr(_("--sm-save-file requires an argument\n")); - else { - save_file = g_strdup((*argv)[i+1]); - remove_args(argc, argv, i, 2); - ++i; - } - } else if (!strcmp((*argv)[i], "--sm-disable")) { - sm_disable = TRUE; - remove_args(argc, argv, i, 1); + while (session_saved_state) { + session_state_free(session_saved_state->data); + session_saved_state = g_list_delete_link(session_saved_state, + session_saved_state); } } } -void session_startup(gint argc, gchar **argv) +/*! Connect to the session manager and set up our callback functions */ +static gboolean session_connect() { -#define SM_ERR_LEN 1024 - SmcCallbacks cb; + gchar *oldid; gchar sm_err[SM_ERR_LEN]; - gint i; - - sm_argc = argc; - sm_argv = g_new(gchar*, argc); - for (i = 0; i < argc; ++i) - sm_argv[i] = argv[i]; - - parse_args(&sm_argc, &sm_argv); - - if (sm_disable) { - g_free(sm_argv); - return; - } - - sm_sessions_path = g_build_filename(parse_xdg_data_home_path(), - "openbox", "sessions", NULL); - if (!parse_mkdir_path(sm_sessions_path, 0700)) { - g_message(_("Unable to make directory '%s': %s"), - sm_sessions_path, g_strerror(errno)); - } - - if (save_file) - session_load(save_file); - else { - gchar *filename; - - /* this algo is from metacity */ - filename = g_strdup_printf("%d-%d-%u.obs", - (gint) time(NULL), - (gint) getpid(), - g_random_int()); - save_file = g_build_filename(sm_sessions_path, filename, NULL); - g_free(filename); - } + /* set up our callback functions */ cb.save_yourself.callback = sm_save_yourself; cb.save_yourself.client_data = NULL; - cb.die.callback = sm_die; cb.die.client_data = NULL; - cb.save_complete.callback = sm_save_complete; cb.save_complete.client_data = NULL; - cb.shutdown_cancelled.callback = sm_shutdown_cancelled; cb.shutdown_cancelled.client_data = NULL; + /* connect to the server */ + oldid = ob_sm_id; + ob_debug_type(OB_DEBUG_SM, "Connecting to SM with id: %s\n", + oldid ? oldid : "(null)"); sm_conn = SmcOpenConnection(NULL, NULL, 1, 0, SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask, - &cb, sm_id, &sm_id, + &cb, oldid, &ob_sm_id, SM_ERR_LEN, sm_err); + g_free(oldid); if (sm_conn == NULL) ob_debug("Failed to connect to session manager: %s\n", sm_err); - else { - SmPropValue val_prog; - SmPropValue val_uid; - SmPropValue val_hint; - SmPropValue val_pri; - SmPropValue val_pid; - SmProp prop_prog = { SmProgram, SmARRAY8, 1, }; - SmProp prop_uid = { SmUserID, SmARRAY8, 1, }; - SmProp prop_hint = { SmRestartStyleHint, SmCARD8, 1, }; - SmProp prop_pid = { SmProcessID, SmARRAY8, 1, }; - SmProp prop_pri = { "_GSM_Priority", SmCARD8, 1, }; - SmProp *props[6]; - gchar hint, pri; - gchar pid[32]; - - val_prog.value = sm_argv[0]; - val_prog.length = strlen(sm_argv[0]); - - val_uid.value = g_strdup(g_get_user_name()); - val_uid.length = strlen(val_uid.value); - - hint = SmRestartImmediately; - val_hint.value = &hint; - val_hint.length = 1; - - g_snprintf(pid, sizeof(pid), "%ld", (glong) getpid()); - val_pid.value = pid; - val_pid.length = strlen(pid); - - /* priority with gnome-session-manager, low to run before other apps */ - pri = 20; - val_pri.value = &pri; - val_pri.length = 1; - - prop_prog.vals = &val_prog; - prop_uid.vals = &val_uid; - prop_hint.vals = &val_hint; - prop_pid.vals = &val_pid; - prop_pri.vals = &val_pri; - - props[0] = &prop_prog; - props[1] = &prop_uid; - props[2] = &prop_hint; - props[3] = &prop_pid; - props[4] = &prop_pri; - - SmcSetProperties(sm_conn, 5, props); - - g_free(val_uid.value); - - save_commands(); - } + return sm_conn != NULL; } -void session_shutdown(gboolean permanent) +static void session_setup_program() { - if (sm_disable) - return; + SmPropValue vals = { + .value = sm_argv[0], + .length = strlen(sm_argv[0]) + 1 + }; + SmProp prop = { + .name = g_strdup(SmProgram), + .type = g_strdup(SmARRAY8), + .num_vals = 1, + .vals = &vals + }; + SmProp *list = ∝ + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); +} - g_free(sm_sessions_path); - g_free(save_file); - g_free(sm_id); - g_free(sm_argv); +static void session_setup_user() +{ + char *user = g_strdup(g_get_user_name()); + + SmPropValue vals = { + .value = user, + .length = strlen(user) + 1 + }; + SmProp prop = { + .name = g_strdup(SmUserID), + .type = g_strdup(SmARRAY8), + .num_vals = 1, + .vals = &vals + }; + SmProp *list = ∝ + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); + g_free(user); +} - if (sm_conn) { - /* if permanent is true then we will change our session state so that - the SM won't run us again */ - if (permanent) { - SmPropValue val_hint; - SmProp prop_hint = { SmRestartStyleHint, SmCARD8, 1, }; - SmProp *props[1]; - gulong hint; +static void session_setup_restart_style(gboolean restart) +{ + char restart_hint = restart ? SmRestartImmediately : SmRestartIfRunning; + + SmPropValue vals = { + .value = &restart_hint, + .length = 1 + }; + SmProp prop = { + .name = g_strdup(SmRestartStyleHint), + .type = g_strdup(SmCARD8), + .num_vals = 1, + .vals = &vals + }; + SmProp *list = ∝ + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); +} - /* when we exit, we want to reset this to a more friendly state */ - hint = SmRestartIfRunning; - val_hint.value = &hint; - val_hint.length = 1; +static void session_setup_pid() +{ + gchar *pid = g_strdup_printf("%ld", (glong) getpid()); + + SmPropValue vals = { + .value = pid, + .length = strlen(pid) + 1 + }; + SmProp prop = { + .name = g_strdup(SmProcessID), + .type = g_strdup(SmARRAY8), + .num_vals = 1, + .vals = &vals + }; + SmProp *list = ∝ + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); + g_free(pid); +} - prop_hint.vals = &val_hint; +/*! This is a gnome-session-manager extension */ +static void session_setup_priority() +{ + gchar priority = 20; /* 20 is a lower prioity to run before other apps */ + + SmPropValue vals = { + .value = &priority, + .length = 1 + }; + SmProp prop = { + .name = g_strdup("_GSM_Priority"), + .type = g_strdup(SmCARD8), + .num_vals = 1, + .vals = &vals + }; + SmProp *list = ∝ + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); +} - props[0] = &prop_hint; +static void session_setup_clone_command() +{ + gint i; - SmcSetProperties(sm_conn, 1, props); - } + SmPropValue *vals = g_new(SmPropValue, sm_argc); + SmProp prop = { + .name = g_strdup(SmCloneCommand), + .type = g_strdup(SmLISTofARRAY8), + .num_vals = sm_argc, + .vals = vals + }; - SmcCloseConnection(sm_conn, 0, NULL); + for (i = 0; i < sm_argc; ++i) { + vals[i].value = sm_argv[i]; + vals[i].length = strlen(sm_argv[i]) + 1; + } - while (session_saved_state) { - session_state_free(session_saved_state->data); - session_saved_state = g_list_delete_link(session_saved_state, - session_saved_state); - } + SmProp *list = ∝ + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); + g_free(vals); +} + +static void session_setup_restart_command() +{ + gint i; + + SmPropValue *vals = g_new(SmPropValue, sm_argc + 4); + SmProp prop = { + .name = g_strdup(SmRestartCommand), + .type = g_strdup(SmLISTofARRAY8), + .num_vals = sm_argc + 4, + .vals = vals + }; + + for (i = 0; i < sm_argc; ++i) { + vals[i].value = sm_argv[i]; + vals[i].length = strlen(sm_argv[i]) + 1; } + + vals[i].value = g_strdup("--sm-save-file"); + vals[i].length = strlen("--sm-save-file") + 1; + vals[i+1].value = ob_sm_save_file; + vals[i+1].length = strlen(ob_sm_save_file) + 1; + + vals[i+2].value = g_strdup("--sm-client-id"); + vals[i+2].length = strlen("--sm-client-id") + 1; + vals[i+3].value = ob_sm_id; + vals[i+3].length = strlen(ob_sm_id) + 1; + + SmProp *list = ∝ + SmcSetProperties(sm_conn, 1, &list); + g_free(prop.name); + g_free(prop.type); + g_free(vals[i].value); + g_free(vals[i+2].value); + g_free(vals); } -static void sm_save_yourself_phase2(SmcConn conn, SmPointer data) +static void sm_save_yourself_2(SmcConn conn, SmPointer data) { gboolean success; - success = session_save(); - save_commands(); + /* save the current state */ + ob_debug_type(OB_DEBUG_SM, "Session save phase 2 requested\n"); + ob_debug_type(OB_DEBUG_SM, + " Saving session to file '%s'\n", ob_sm_save_file); + success = session_save_to_file(); + + /* tell the session manager how to restore this state */ + if (success) session_setup_restart_command(); + ob_debug_type(OB_DEBUG_SM, "Saving is done (success = %d)\n", success); SmcSaveYourselfDone(conn, success); } + static void sm_save_yourself(SmcConn conn, SmPointer data, gint save_type, Bool shutdown, gint interact_style, Bool fast) { - if (!SmcRequestSaveYourselfPhase2(conn, sm_save_yourself_phase2, data)) { - ob_debug("SAVE YOURSELF PHASE 2 failed\n"); + ob_debug_type(OB_DEBUG_SM, "Session save requested\n"); + if (!SmcRequestSaveYourselfPhase2(conn, sm_save_yourself_2, data)) { + ob_debug_type(OB_DEBUG_SM, "Requst for phase 2 failed\n"); SmcSaveYourselfDone(conn, FALSE); } } static void sm_die(SmcConn conn, SmPointer data) { + ob_debug_type(OB_DEBUG_SM, "Die requested\n"); ob_exit(0); } static void sm_save_complete(SmcConn conn, SmPointer data) { + ob_debug_type(OB_DEBUG_SM, "Save complete\n"); } static void sm_shutdown_cancelled(SmcConn conn, SmPointer data) { + ob_debug_type(OB_DEBUG_SM, "Shutdown cancelled\n"); } -static gboolean session_save() +static gboolean session_save_to_file() { FILE *f; GList *it; gboolean success = TRUE; - f = fopen(save_file, "w"); + f = fopen(ob_sm_save_file, "w"); if (!f) { success = FALSE; g_message(_("Unable to save the session to '%s': %s"), - save_file, g_strerror(errno)); + ob_sm_save_file, g_strerror(errno)); } else { - guint stack_pos = 0; - fprintf(f, "\n\n"); - fprintf(f, "\n\n", sm_id); + fprintf(f, "\n\n"); + fprintf(f, "%d\n", screen_desktop); + + /* they are ordered top to bottom in stacking order */ for (it = stacking_list; it; it = g_list_next(it)) { gint prex, prey, prew, preh; ObClient *c; @@ -361,8 +411,15 @@ static gboolean session_save() if (!client_normal(c)) continue; - if (!c->sm_client_id) + if (!c->sm_client_id) { + ob_debug_type(OB_DEBUG_SM, "Client %s does not have a client " + "id set, so we can't save its state\n", + c->title); continue; + } + + ob_debug_type(OB_DEBUG_SM, "Saving state for client %s\n", + c->title); prex = c->area.x; prey = c->area.y; @@ -398,7 +455,6 @@ static gboolean session_save() g_free(t); fprintf(f, "\t%d\n", c->desktop); - fprintf(f, "\t%d\n", stack_pos); fprintf(f, "\t%d\n", prex); fprintf(f, "\t%d\n", prey); fprintf(f, "\t%d\n", prew); @@ -421,9 +477,11 @@ static gboolean session_save() fprintf(f, "\t\n"); if (c->max_vert) fprintf(f, "\t\n"); + if (c->undecorated) + fprintf(f, "\t\n"); + if (client_focused(c)) + fprintf(f, "\t\n"); fprintf(f, "\n\n"); - - ++stack_pos; } fprintf(f, "\n"); @@ -431,7 +489,7 @@ static gboolean session_save() if (fflush(f)) { success = FALSE; g_message(_("Error while saving the session to '%s': %s"), - save_file, g_strerror(errno)); + ob_sm_save_file, g_strerror(errno)); } fclose(f); } @@ -439,7 +497,7 @@ static gboolean session_save() return success; } -void session_state_free(ObSessionState *state) +static void session_state_free(ObSessionState *state) { if (state) { g_free(state->id); @@ -451,8 +509,17 @@ void session_state_free(ObSessionState *state) } } -gboolean session_state_cmp(ObSessionState *s, ObClient *c) +static gboolean session_state_cmp(ObSessionState *s, ObClient *c) { + ob_debug_type(OB_DEBUG_SM, "Comparing client against saved state: \n"); + ob_debug_type(OB_DEBUG_SM, " client id: %s \n", c->sm_client_id); + ob_debug_type(OB_DEBUG_SM, " client name: %s \n", c->name); + ob_debug_type(OB_DEBUG_SM, " client class: %s \n", c->class); + ob_debug_type(OB_DEBUG_SM, " client role: %s \n", c->role); + ob_debug_type(OB_DEBUG_SM, " state id: %s \n", s->id); + ob_debug_type(OB_DEBUG_SM, " state name: %s \n", s->name); + ob_debug_type(OB_DEBUG_SM, " state class: %s \n", s->class); + ob_debug_type(OB_DEBUG_SM, " state role: %s \n", s->role); return (c->sm_client_id && !strcmp(s->id, c->sm_client_id) && !strcmp(s->name, c->name) && @@ -474,27 +541,20 @@ GList* session_state_find(ObClient *c) return it; } -static gint stack_sort(const ObSessionState *s1, const ObSessionState *s2) -{ - return s1->stacking - s2->stacking; -} - -static void session_load(gchar *path) +static void session_load_file(gchar *path) { xmlDocPtr doc; xmlNodePtr node, n; - gchar *id; if (!parse_load(path, "openbox_session", &doc, &node)) return; - if (!parse_attr_string("id", node, &id)) - return; - g_free(sm_id); - sm_id = id; + if ((n = parse_find_node("desktop", node->children))) + session_desktop = parse_int(doc, n); - node = parse_find_node("window", node->children); - while (node) { + for (node = parse_find_node("window", node->children); node != NULL; + node = parse_find_node("window", node->next)) + { ObSessionState *state; state = g_new0(ObSessionState, 1); @@ -510,9 +570,6 @@ static void session_load(gchar *path) if (!(n = parse_find_node("role", node->children))) goto session_load_bail; state->role = parse_string(doc, n); - if (!(n = parse_find_node("stacking", node->children))) - goto session_load_bail; - state->stacking = parse_int(doc, n); if (!(n = parse_find_node("desktop", node->children))) goto session_load_bail; state->desktop = parse_int(doc, n); @@ -547,23 +604,20 @@ static void session_load(gchar *path) parse_find_node("max_horz", node->children) != NULL; state->max_vert = parse_find_node("max_vert", node->children) != NULL; + state->undecorated = + parse_find_node("undecorated", node->children) != NULL; + state->focused = + parse_find_node("focused", node->children) != NULL; - /* save this */ - session_saved_state = g_list_prepend(session_saved_state, state); - goto session_load_ok; + /* save this. they are in the file in stacking order, so preserve + that order here */ + session_saved_state = g_list_append(session_saved_state, state); + continue; session_load_bail: session_state_free(state); - - session_load_ok: - - node = parse_find_node("window", node->next); } - /* sort them by their stacking order */ - session_saved_state = g_list_sort(session_saved_state, - (GCompareFunc)stack_sort); - xmlFreeDoc(doc); } diff --git a/openbox/session.h b/openbox/session.h index b2c18a1f..cbfd5177 100644 --- a/openbox/session.h +++ b/openbox/session.h @@ -27,22 +27,24 @@ typedef struct _ObSessionState ObSessionState; struct _ObSessionState { gchar *id, *name, *class, *role; - guint stacking; guint desktop; gint x, y, w, h; gboolean shaded, iconic, skip_pager, skip_taskbar, fullscreen; - gboolean above, below, max_horz, max_vert; + gboolean above, below, max_horz, max_vert, undecorated; + gboolean focused; gboolean matched; }; +/*! The desktop being viewed when the session was saved. A valud of -1 means + it was not saved */ +extern gint session_desktop; + extern GList *session_saved_state; void session_startup(gint argc, gchar **argv); void session_shutdown(gboolean permanent); GList* session_state_find(struct _ObClient *c); -gboolean session_state_cmp(ObSessionState *s, struct _ObClient *c); -void session_state_free(ObSessionState *state); #endif -- 2.39.2