diff --git a/src/run-metacity.sh b/src/run-metacity.sh index b2266dc97..a1aa05019 100755 --- a/src/run-metacity.sh +++ b/src/run-metacity.sh @@ -14,6 +14,10 @@ if test -z "$CLIENTS"; then CLIENTS=0 fi +if test -z "$SM_CLIENTS"; then + SM_CLIENTS=0 +fi + if test -z "$ONLY_WM"; then Xnest -ac :1 -scrns $SCREENS -geometry 640x480 -bw 15 & usleep 500000 @@ -23,6 +27,12 @@ if test -z "$ONLY_WM"; then DISPLAY=:1 xterm -geometry 25x15 & done fi + + if test $SM_CLIENTS != 0; then + for I in `seq 1 $SM_CLIENTS`; do + DISPLAY=:1 gnome-terminal --geometry 25x15 & + done + fi usleep 50000 diff --git a/src/session.c b/src/session.c index 9f5c1cd8c..bb9b72672 100644 --- a/src/session.c +++ b/src/session.c @@ -533,6 +533,58 @@ set_clone_restart_commands (void) * session manager. */ +static const char* +window_type_to_string (MetaWindowType type) +{ + switch (type) + { + case META_WINDOW_NORMAL: + return "normal"; + break; + case META_WINDOW_DESKTOP: + return "desktop"; + break; + case META_WINDOW_DOCK: + return "dock"; + break; + case META_WINDOW_DIALOG: + return "dialog"; + break; + case META_WINDOW_MODAL_DIALOG: + return "modal_dialog"; + break; + case META_WINDOW_TOOLBAR: + return "toolbar"; + break; + case META_WINDOW_MENU: + return "menu"; + break; + } + + return ""; +} + +static MetaWindowType +window_type_from_string (const char *str) +{ + if (strcmp (str, "normal") == 0) + return META_WINDOW_NORMAL; + else if (strcmp (str, "desktop") == 0) + return META_WINDOW_DESKTOP; + else if (strcmp (str, "dock") == 0) + return META_WINDOW_DOCK; + else if (strcmp (str, "dialog") == 0) + return META_WINDOW_DIALOG; + else if (strcmp (str, "modal_dialog") == 0) + return META_WINDOW_MODAL_DIALOG; + else if (strcmp (str, "toolbar") == 0) + return META_WINDOW_TOOLBAR; + else if (strcmp (str, "menu") == 0) + return META_WINDOW_MENU; + else + return META_WINDOW_NORMAL; +} + static void save_state (void) { @@ -586,16 +638,23 @@ save_state (void) /* The file format is: * - * - * 2 - * 4 + * + * + * * * * * + * + * Note that attributes on are the match info we use to + * see if the saved state applies to a restored window, and + * child elements are the saved state to be applied. + * */ /* FIXME we are putting non-UTF-8 in here. */ + + meta_warning ("FIXME Saving session ID, class, name, etc. pretending that they are valid UTF-8, but no such thing is necessarily true"); fprintf (outfile, "\n", client_id); @@ -621,12 +680,13 @@ save_state (void) window->desc, window->sm_client_id); fprintf (outfile, - " \n", + " \n", window->sm_client_id, window->res_class ? window->res_class : "", window->res_name ? window->res_name : "", window->title ? window->title : "", - window->role ? window->role : ""); + window->role ? window->role : "", + window_type_to_string (window->type)); /* Sticky */ if (window->on_all_workspaces) @@ -634,14 +694,14 @@ save_state (void) /* Workspaces we're on */ { - GSList *w; + GList *w; w = window->workspaces; while (w != NULL) { int n; n = meta_workspace_screen_index (w->data); fprintf (outfile, - "%d\n", n); + "\n", n); w = w->next; } @@ -682,19 +742,442 @@ save_state (void) g_free (session_file); } +typedef enum +{ + WINDOW_TAG_NONE, + WINDOW_TAG_DESKTOP, + WINDOW_TAG_STICKY, + WINDOW_TAG_GEOMETRY +} WindowTag; + +typedef struct +{ + MetaWindowSessionInfo *info; + +} ParseData; + +static void session_info_free (MetaWindowSessionInfo *info); +static MetaWindowSessionInfo* session_info_new (void); + +static void start_element_handler (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error); +static void end_element_handler (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error); +static void text_handler (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error); + +static GMarkupParser metacity_session_parser = { + start_element_handler, + end_element_handler, + text_handler, + NULL, + NULL +}; + +static GSList *window_info_list = NULL; + static void load_state (const char *previous_id) { + GMarkupParseContext *context; + GError *error; + ParseData parse_data; + char *text; + int length; + char *session_file; + + session_file = g_strconcat (g_get_home_dir (), + ".metacity/sessions/", + client_id, + NULL); + + error = NULL; + if (!g_file_get_contents (session_file, + &text, + &length, + &error)) + { + meta_warning (_("Failed to read saved session file %s: %s\n"), + session_file, error->message); + g_error_free (error); + g_free (session_file); + return; + } + + parse_data.info = NULL; + + context = g_markup_parse_context_new (&metacity_session_parser, + 0, &parse_data, NULL); + + error = NULL; + if (!g_markup_parse_context_parse (context, + text, + length, + &error)) + goto error; + + + error = NULL; + if (!g_markup_parse_context_end_parse (context, &error)) + goto error; + + g_markup_parse_context_free (context); + + goto out; + + error: + + meta_warning (_("Failed to parse saved session file: %s\n"), + error->message); + g_error_free (error); + + out: + + g_free (text); +} +static void +start_element_handler (GMarkupParseContext *context, + const gchar *element_name, + const gchar **attribute_names, + const gchar **attribute_values, + gpointer user_data, + GError **error) +{ + ParseData *pd; + + pd = user_data; + + if (strcmp (element_name, "window") == 0) + { + int i; + + if (pd->info) + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_PARSE, + _("nested tag")); + return; + } + + pd->info = session_info_new (); + + i = 0; + while (attribute_names[i]) + { + const char *name; + const char *val; + + name = attribute_names[i]; + val = attribute_values[i]; + + if (strcmp (name, "id") == 0) + { + if (*val) + pd->info->id = g_strdup (val); + } + else if (strcmp (name, "class") == 0) + { + if (*val) + pd->info->res_class = g_strdup (val); + } + else if (strcmp (name, "name") == 0) + { + if (*val) + pd->info->res_name = g_strdup (val); + } + else if (strcmp (name, "title") == 0) + { + if (*val) + pd->info->title = g_strdup (val); + } + else if (strcmp (name, "role") == 0) + { + if (*val) + pd->info->role = g_strdup (val); + } + else if (strcmp (name, "type") == 0) + { + if (*val) + pd->info->type = window_type_from_string (val); + } + else + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, + _("Unknown attribute %s on element"), + name); + session_info_free (pd->info); + pd->info = NULL; + return; + } + + ++i; + } + } + else if (strcmp (element_name, "workspace") == 0) + { + int i; + + i = 0; + while (attribute_names[i]) + { + const char *name; + + name = attribute_names[i]; + + if (strcmp (name, "index") == 0) + { + pd->info->workspace_indices = + g_slist_prepend (pd->info->workspace_indices, + GINT_TO_POINTER (atoi (attribute_values[i]))); + } + else + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, + _("Unknown attribute %s on element"), + name); + session_info_free (pd->info); + pd->info = NULL; + return; + } + + ++i; + } + } + else if (strcmp (element_name, "sticky") == 0) + { + pd->info->on_all_workspaces = TRUE; + pd->info->on_all_workspaces_set = TRUE; + } + else if (strcmp (element_name, "geometry") == 0) + { + + + } + else + { + g_set_error (error, + G_MARKUP_ERROR, + G_MARKUP_ERROR_UNKNOWN_ELEMENT, + _("Unknown element %s"), + element_name); + return; + } +} + +static void +end_element_handler (GMarkupParseContext *context, + const gchar *element_name, + gpointer user_data, + GError **error) +{ + ParseData *pd; + + pd = user_data; + + if (strcmp (element_name, "window") == 0) + { + g_assert (pd->info); + + window_info_list = g_slist_prepend (window_info_list, + pd->info); + + meta_verbose ("Loaded window info from session with class: %s name: %s role: %s\n", + pd->info->res_class ? pd->info->res_class : "(none)", + pd->info->res_name ? pd->info->res_name : "(none)", + pd->info->role ? pd->info->role : "(none)"); + + pd->info = NULL; + } +} + +static void +text_handler (GMarkupParseContext *context, + const gchar *text, + gsize text_len, + gpointer user_data, + GError **error) +{ + ParseData *pd; + + pd = user_data; + + /* Right now we don't have any elements where we care about their + * content + */ +} + + +static gboolean +both_null_or_matching (const char *a, + const char *b) +{ + if (a == NULL && b == NULL) + return TRUE; + else if (a && b && strcmp (a, b) == 0) + return TRUE; + else + return FALSE; +} + +static GSList* +get_possible_matches (MetaWindow *window) +{ + /* Get all windows with this client ID */ + GSList *retval; + GSList *tmp; + + retval = NULL; + + tmp = window_info_list; + while (tmp != NULL) + { + MetaWindowSessionInfo *info; + + info = tmp->data; + + if (both_null_or_matching (info->id, + window->sm_client_id) && + both_null_or_matching (info->res_class, window->res_class) && + both_null_or_matching (info->res_name, window->res_name) && + both_null_or_matching (info->role, window->role)) + { + meta_verbose ("Window %s may match saved window with class: %s name: %s role: %s\n", + window->desc, + info->res_class ? info->res_class : "(none)", + info->res_name ? info->res_name : "(none)", + info->role ? info->role : "(none)"); + + retval = g_slist_prepend (retval, info); + } + + tmp = tmp->next; + } + + return retval; +} + +const MetaWindowSessionInfo* +find_best_match (GSList *infos, + MetaWindow *window) +{ + GSList *tmp; + const MetaWindowSessionInfo *matching_title; + const MetaWindowSessionInfo *matching_type; + + matching_title = NULL; + matching_type = NULL; + + tmp = infos; + while (tmp != NULL) + { + MetaWindowSessionInfo *info; + + info = tmp->data; + + if (matching_title == NULL && + both_null_or_matching (info->title, window->title)) + matching_title = info; + + if (matching_type == NULL && + info->type == window->type) + matching_type = info; + + tmp = tmp->next; + } + + /* Prefer same title, then same type of window, then + * just pick something. Eventually we could enhance this + * to e.g. break ties by geometry hint similarity, + * or other window features. + */ + + if (matching_title) + return matching_title; + else if (matching_type) + return matching_type; + else + return infos->data; + + return NULL; +} + +const MetaWindowSessionInfo* +meta_window_lookup_saved_state (MetaWindow *window) +{ + GSList *possibles; + const MetaWindowSessionInfo *info; + + /* Window is not session managed. + * I haven't yet figured out how to deal with these + * in a way that doesn't cause broken side effects in + * situations other than on session restore. + */ + if (window->sm_client_id == NULL) + return NULL; + + possibles = get_possible_matches (window); + + if (possibles == NULL) + return NULL; + + info = find_best_match (possibles, window); + + g_slist_free (possibles); + + return info; } void -meta_window_lookup_saved_state (MetaWindow *window, - MetaWindowSessionInfo *info) +meta_window_release_saved_state (const MetaWindowSessionInfo *info) { - + /* We don't want to use the same saved state again for another + * window. + */ + window_info_list = g_slist_remove (window_info_list, info); + session_info_free ((MetaWindowSessionInfo*) info); +} + +static void +session_info_free (MetaWindowSessionInfo *info) +{ + g_free (info->id); + g_free (info->res_class); + g_free (info->res_name); + g_free (info->title); + g_free (info->role); + + g_slist_free (info->workspace_indices); + + g_free (info); +} + +static MetaWindowSessionInfo* +session_info_new (void) +{ + MetaWindowSessionInfo *info; + + info = g_new0 (MetaWindowSessionInfo, 1); + + info->type = META_WINDOW_NORMAL; + + return info; } #endif /* HAVE_SM */