diff options
Diffstat (limited to 'src/core/session.c')
-rw-r--r-- | src/core/session.c | 1900 |
1 files changed, 0 insertions, 1900 deletions
diff --git a/src/core/session.c b/src/core/session.c deleted file mode 100644 index fc29e13d..00000000 --- a/src/core/session.c +++ /dev/null @@ -1,1900 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* Metacity Session Management */ - -/* - * Copyright (C) 2001 Havoc Pennington (some code in here from - * libgnomeui, (C) Tom Tromey, Carsten Schaar) - * Copyright (C) 2004, 2005 Elijah Newren - * - * 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. - */ - -#include <config.h> - -#include "session.h" -#include <X11/Xatom.h> - -#include <time.h> - -#ifndef HAVE_SM -void -meta_session_init (const char *client_id, - const char *save_file) -{ - meta_topic (META_DEBUG_SM, "Compiled without session management support\n"); -} - -void -meta_session_shutdown (void) -{ - /* nothing */ -} - -const MetaWindowSessionInfo* -meta_window_lookup_saved_state (MetaWindow *window) -{ - return NULL; -} - -void -meta_window_release_saved_state (const MetaWindowSessionInfo *info) -{ - ; -} -#else /* HAVE_SM */ - -#include <X11/ICE/ICElib.h> -#include <X11/SM/SMlib.h> -#include <unistd.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <fcntl.h> -#include <errno.h> -#include <glib.h> -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include "main.h" -#include "util.h" -#include "display-private.h" -#include "workspace.h" - -static void ice_io_error_handler (IceConn connection); - -static void new_ice_connection (IceConn connection, IcePointer client_data, - Bool opening, IcePointer *watch_data); - -static void save_state (void); -static char* load_state (const char *previous_save_file); -static void regenerate_save_file (void); -static const char* full_save_file (void); -static void warn_about_lame_clients_and_finish_interact (gboolean shutdown); - -/* This is called when data is available on an ICE connection. */ -static gboolean -process_ice_messages (GIOChannel *channel, - GIOCondition condition, - gpointer client_data) -{ - IceConn connection = (IceConn) client_data; - IceProcessMessagesStatus status; - - /* This blocks infinitely sometimes. I don't know what - * to do about it. Checking "condition" just breaks - * session management. - */ - status = IceProcessMessages (connection, NULL, NULL); - - if (status == IceProcessMessagesIOError) - { -#if 0 - IcePointer context = IceGetConnectionContext (connection); -#endif - - /* We were disconnected */ - IceSetShutdownNegotiation (connection, False); - IceCloseConnection (connection); - - return FALSE; - } - - return TRUE; -} - -/* This is called when a new ICE connection is made. It arranges for - the ICE connection to be handled via the event loop. */ -static void -new_ice_connection (IceConn connection, IcePointer client_data, Bool opening, - IcePointer *watch_data) -{ - guint input_id; - - if (opening) - { - /* Make sure we don't pass on these file descriptors to any - * exec'ed children - */ - GIOChannel *channel; - - fcntl (IceConnectionNumber (connection), F_SETFD, - fcntl (IceConnectionNumber (connection), F_GETFD, 0) | FD_CLOEXEC); - - channel = g_io_channel_unix_new (IceConnectionNumber (connection)); - - input_id = g_io_add_watch (channel, - G_IO_IN | G_IO_ERR, - process_ice_messages, - connection); - - g_io_channel_unref (channel); - - *watch_data = (IcePointer) GUINT_TO_POINTER (input_id); - } - else - { - input_id = GPOINTER_TO_UINT ((gpointer) *watch_data); - - g_source_remove (input_id); - } -} - -static IceIOErrorHandler ice_installed_handler; - -/* We call any handler installed before (or after) gnome_ice_init but - avoid calling the default libICE handler which does an exit() */ -static void -ice_io_error_handler (IceConn connection) -{ - if (ice_installed_handler) - (*ice_installed_handler) (connection); -} - -static void -ice_init (void) -{ - static gboolean ice_initted = FALSE; - - if (! ice_initted) - { - IceIOErrorHandler default_handler; - - ice_installed_handler = IceSetIOErrorHandler (NULL); - default_handler = IceSetIOErrorHandler (ice_io_error_handler); - - if (ice_installed_handler == default_handler) - ice_installed_handler = NULL; - - IceAddConnectionWatch (new_ice_connection, NULL); - - ice_initted = TRUE; - } -} - -typedef enum -{ - STATE_DISCONNECTED, - STATE_IDLE, - STATE_SAVING_PHASE_1, - STATE_WAITING_FOR_PHASE_2, - STATE_SAVING_PHASE_2, - STATE_WAITING_FOR_INTERACT, - STATE_DONE_WITH_INTERACT, - STATE_SKIPPING_GLOBAL_SAVE, - STATE_FROZEN, - STATE_REGISTERING -} ClientState; - -static void save_phase_2_callback (SmcConn smc_conn, - SmPointer client_data); -static void interact_callback (SmcConn smc_conn, - SmPointer client_data); -static void shutdown_cancelled_callback (SmcConn smc_conn, - SmPointer client_data); -static void save_complete_callback (SmcConn smc_conn, - SmPointer client_data); -static void die_callback (SmcConn smc_conn, - SmPointer client_data); -static void save_yourself_callback (SmcConn smc_conn, - SmPointer client_data, - int save_style, - Bool shutdown, - int interact_style, - Bool fast); -static void set_clone_restart_commands (void); - -static char *client_id = NULL; -static gpointer session_connection = NULL; -static ClientState current_state = STATE_DISCONNECTED; -static gboolean interaction_allowed = FALSE; - -void -meta_session_init (const char *previous_client_id, - const char *previous_save_file) -{ - /* Some code here from twm */ - char buf[256]; - unsigned long mask; - SmcCallbacks callbacks; - char *saved_client_id; - - meta_topic (META_DEBUG_SM, "Initializing session with save file '%s'\n", - previous_save_file ? previous_save_file : "(none)"); - - if (previous_save_file) - { - saved_client_id = load_state (previous_save_file); - previous_client_id = saved_client_id; - } - else if (previous_client_id) - { - char *save_file = g_strconcat (previous_client_id, ".ms", NULL); - saved_client_id = load_state (save_file); - g_free (save_file); - } - else - { - saved_client_id = NULL; - } - - ice_init (); - - mask = SmcSaveYourselfProcMask | SmcDieProcMask | - SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask; - - callbacks.save_yourself.callback = save_yourself_callback; - callbacks.save_yourself.client_data = NULL; - - callbacks.die.callback = die_callback; - callbacks.die.client_data = NULL; - - callbacks.save_complete.callback = save_complete_callback; - callbacks.save_complete.client_data = NULL; - - callbacks.shutdown_cancelled.callback = shutdown_cancelled_callback; - callbacks.shutdown_cancelled.client_data = NULL; - - session_connection = - SmcOpenConnection (NULL, /* use SESSION_MANAGER env */ - NULL, /* means use existing ICE connection */ - SmProtoMajor, - SmProtoMinor, - mask, - &callbacks, - (char*) previous_client_id, - &client_id, - 255, buf); - - if (session_connection == NULL) - { - meta_topic (META_DEBUG_SM, - "Failed to a open connection to a session manager, so window positions will not be saved: %s\n", - buf); - - goto out; - } - else - { - if (client_id == NULL) - meta_bug ("Session manager gave us a NULL client ID?"); - meta_topic (META_DEBUG_SM, "Obtained session ID '%s'\n", client_id); - } - - if (previous_client_id && strcmp (previous_client_id, client_id) == 0) - current_state = STATE_IDLE; - else - current_state = STATE_REGISTERING; - - { - SmProp prop1, prop2, prop3, prop4, prop5, prop6, *props[6]; - SmPropValue prop1val, prop2val, prop3val, prop4val, prop5val, prop6val; - char pid[32]; - char hint = SmRestartImmediately; - char priority = 20; /* low to run before other apps */ - - prop1.name = SmProgram; - prop1.type = SmARRAY8; - prop1.num_vals = 1; - prop1.vals = &prop1val; - prop1val.value = "metacity"; - prop1val.length = strlen ("metacity"); - - /* twm sets getuid() for this, but the SM spec plainly - * says pw_name, twm is on crack - */ - prop2.name = SmUserID; - prop2.type = SmARRAY8; - prop2.num_vals = 1; - prop2.vals = &prop2val; - prop2val.value = (char*) g_get_user_name (); - prop2val.length = strlen (prop2val.value); - - prop3.name = SmRestartStyleHint; - prop3.type = SmCARD8; - prop3.num_vals = 1; - prop3.vals = &prop3val; - prop3val.value = &hint; - prop3val.length = 1; - - sprintf (pid, "%d", getpid ()); - prop4.name = SmProcessID; - prop4.type = SmARRAY8; - prop4.num_vals = 1; - prop4.vals = &prop4val; - prop4val.value = pid; - prop4val.length = strlen (prop4val.value); - - /* Always start in home directory */ - prop5.name = SmCurrentDirectory; - prop5.type = SmARRAY8; - prop5.num_vals = 1; - prop5.vals = &prop5val; - prop5val.value = (char*) g_get_home_dir (); - prop5val.length = strlen (prop5val.value); - - prop6.name = "_GSM_Priority"; - prop6.type = SmCARD8; - prop6.num_vals = 1; - prop6.vals = &prop6val; - prop6val.value = &priority; - prop6val.length = 1; - - props[0] = &prop1; - props[1] = &prop2; - props[2] = &prop3; - props[3] = &prop4; - props[4] = &prop5; - props[5] = &prop6; - - SmcSetProperties (session_connection, 6, props); - } - - out: - g_free (saved_client_id); -} - -void -meta_session_shutdown (void) -{ - /* Change our restart mode to IfRunning */ - - SmProp prop1; - SmPropValue prop1val; - SmProp *props[1]; - char hint = SmRestartIfRunning; - - if (session_connection == NULL) - return; - - prop1.name = SmRestartStyleHint; - prop1.type = SmCARD8; - prop1.num_vals = 1; - prop1.vals = &prop1val; - prop1val.value = &hint; - prop1val.length = 1; - - props[0] = &prop1; - - SmcSetProperties (session_connection, 1, props); -} - -static void -disconnect (void) -{ - SmcCloseConnection (session_connection, 0, NULL); - session_connection = NULL; - current_state = STATE_DISCONNECTED; -} - -static void -save_yourself_possibly_done (gboolean shutdown, - gboolean successful) -{ - meta_topic (META_DEBUG_SM, - "save possibly done shutdown = %d success = %d\n", - shutdown, successful); - - if (current_state == STATE_SAVING_PHASE_1) - { - Status status; - - status = SmcRequestSaveYourselfPhase2 (session_connection, - save_phase_2_callback, - GINT_TO_POINTER (shutdown)); - - if (status) - current_state = STATE_WAITING_FOR_PHASE_2; - - meta_topic (META_DEBUG_SM, - "Requested phase 2, status = %d\n", status); - } - - if (current_state == STATE_SAVING_PHASE_2 && - interaction_allowed) - { - Status status; - - status = SmcInteractRequest (session_connection, - /* ignore this feature of the protocol by always - * claiming normal - */ - SmDialogNormal, - interact_callback, - GINT_TO_POINTER (shutdown)); - - if (status) - current_state = STATE_WAITING_FOR_INTERACT; - - meta_topic (META_DEBUG_SM, - "Requested interact, status = %d\n", status); - } - - if (current_state == STATE_SAVING_PHASE_1 || - current_state == STATE_SAVING_PHASE_2 || - current_state == STATE_DONE_WITH_INTERACT || - current_state == STATE_SKIPPING_GLOBAL_SAVE) - { - meta_topic (META_DEBUG_SM, "Sending SaveYourselfDone\n"); - - SmcSaveYourselfDone (session_connection, - successful); - - if (shutdown) - current_state = STATE_FROZEN; - else - current_state = STATE_IDLE; - } -} - -static void -save_phase_2_callback (SmcConn smc_conn, SmPointer client_data) -{ - gboolean shutdown; - - meta_topic (META_DEBUG_SM, "Phase 2 save"); - - shutdown = GPOINTER_TO_INT (client_data); - - current_state = STATE_SAVING_PHASE_2; - - save_state (); - - save_yourself_possibly_done (shutdown, TRUE); -} - -static void -save_yourself_callback (SmcConn smc_conn, - SmPointer client_data, - int save_style, - Bool shutdown, - int interact_style, - Bool fast) -{ - gboolean successful; - - meta_topic (META_DEBUG_SM, "SaveYourself received"); - - successful = TRUE; - - /* The first SaveYourself after registering for the first time - * is a special case (SM specs 7.2). - */ - -#if 0 /* I think the GnomeClient rationale for this doesn't apply */ - if (current_state == STATE_REGISTERING) - { - current_state = STATE_IDLE; - /* Double check that this is a section 7.2 SaveYourself: */ - - if (save_style == SmSaveLocal && - interact_style == SmInteractStyleNone && - !shutdown && !fast) - { - /* The protocol requires this even if xsm ignores it. */ - SmcSaveYourselfDone (session_connection, successful); - return; - } - } -#endif - - /* ignore Global style saves - * - * This interpretaion of the Local/Global/Both styles - * was discussed extensively on the xdg-list. See: - * - * https://listman.redhat.com/pipermail/xdg-list/2002-July/000615.html - */ - if (save_style == SmSaveGlobal) - { - current_state = STATE_SKIPPING_GLOBAL_SAVE; - save_yourself_possibly_done (shutdown, successful); - return; - } - - interaction_allowed = interact_style != SmInteractStyleNone; - - current_state = STATE_SAVING_PHASE_1; - - regenerate_save_file (); - - set_clone_restart_commands (); - - save_yourself_possibly_done (shutdown, successful); -} - - -static void -die_callback (SmcConn smc_conn, SmPointer client_data) -{ - meta_topic (META_DEBUG_SM, "Exiting at request of session manager\n"); - disconnect (); - meta_quit (META_EXIT_SUCCESS); -} - -static void -save_complete_callback (SmcConn smc_conn, SmPointer client_data) -{ - /* nothing */ - meta_topic (META_DEBUG_SM, "SaveComplete received\n"); -} - -static void -shutdown_cancelled_callback (SmcConn smc_conn, SmPointer client_data) -{ - meta_topic (META_DEBUG_SM, "Shutdown cancelled received\n"); - - if (session_connection != NULL && - (current_state != STATE_IDLE && current_state != STATE_FROZEN)) - { - SmcSaveYourselfDone (session_connection, True); - current_state = STATE_IDLE; - } -} - -static void -interact_callback (SmcConn smc_conn, SmPointer client_data) -{ - /* nothing */ - gboolean shutdown; - - meta_topic (META_DEBUG_SM, "Interaction permission received\n"); - - shutdown = GPOINTER_TO_INT (client_data); - - current_state = STATE_DONE_WITH_INTERACT; - - warn_about_lame_clients_and_finish_interact (shutdown); -} - -static void -set_clone_restart_commands (void) -{ - char *restartv[10]; - char *clonev[10]; - char *discardv[10]; - int i; - SmProp prop1, prop2, prop3, *props[3]; - - /* Restart (use same client ID) */ - - prop1.name = SmRestartCommand; - prop1.type = SmLISTofARRAY8; - - g_return_if_fail (client_id); - - i = 0; - restartv[i] = "metacity"; - ++i; - restartv[i] = "--sm-client-id"; - ++i; - restartv[i] = client_id; - ++i; - restartv[i] = NULL; - - prop1.vals = g_new (SmPropValue, i); - i = 0; - while (restartv[i]) - { - prop1.vals[i].value = restartv[i]; - prop1.vals[i].length = strlen (restartv[i]); - ++i; - } - prop1.num_vals = i; - - /* Clone (no client ID) */ - - i = 0; - clonev[i] = "metacity"; - ++i; - clonev[i] = NULL; - - prop2.name = SmCloneCommand; - prop2.type = SmLISTofARRAY8; - - prop2.vals = g_new (SmPropValue, i); - i = 0; - while (clonev[i]) - { - prop2.vals[i].value = clonev[i]; - prop2.vals[i].length = strlen (clonev[i]); - ++i; - } - prop2.num_vals = i; - - /* Discard */ - - i = 0; - discardv[i] = "rm"; - ++i; - discardv[i] = "-f"; - ++i; - discardv[i] = (char*) full_save_file (); - ++i; - discardv[i] = NULL; - - prop3.name = SmDiscardCommand; - prop3.type = SmLISTofARRAY8; - - prop3.vals = g_new (SmPropValue, i); - i = 0; - while (discardv[i]) - { - prop3.vals[i].value = discardv[i]; - prop3.vals[i].length = strlen (discardv[i]); - ++i; - } - prop3.num_vals = i; - - - props[0] = &prop1; - props[1] = &prop2; - props[2] = &prop3; - - SmcSetProperties (session_connection, 3, props); - - g_free (prop1.vals); - g_free (prop2.vals); - g_free (prop3.vals); -} - -/* The remaining code in this file actually loads/saves the session, - * while the code above this comment handles chatting with the - * session manager. - */ - -static const char* -window_type_to_string (MetaWindowType type) -{ - switch (type) - { - case META_WINDOW_NORMAL: - return "normal"; - case META_WINDOW_DESKTOP: - return "desktop"; - case META_WINDOW_DOCK: - return "dock"; - case META_WINDOW_DIALOG: - return "dialog"; - case META_WINDOW_MODAL_DIALOG: - return "modal_dialog"; - case META_WINDOW_TOOLBAR: - return "toolbar"; - case META_WINDOW_MENU: - return "menu"; - case META_WINDOW_SPLASHSCREEN: - return "splashscreen"; - case META_WINDOW_UTILITY: - return "utility"; - } - - 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 if (strcmp (str, "utility") == 0) - return META_WINDOW_UTILITY; - else if (strcmp (str, "splashscreen") == 0) - return META_WINDOW_SPLASHSCREEN; - else - return META_WINDOW_NORMAL; -} - -static int -window_gravity_from_string (const char *str) -{ - if (strcmp (str, "NorthWestGravity") == 0) - return NorthWestGravity; - else if (strcmp (str, "NorthGravity") == 0) - return NorthGravity; - else if (strcmp (str, "NorthEastGravity") == 0) - return NorthEastGravity; - else if (strcmp (str, "WestGravity") == 0) - return WestGravity; - else if (strcmp (str, "CenterGravity") == 0) - return CenterGravity; - else if (strcmp (str, "EastGravity") == 0) - return EastGravity; - else if (strcmp (str, "SouthWestGravity") == 0) - return SouthWestGravity; - else if (strcmp (str, "SouthGravity") == 0) - return SouthGravity; - else if (strcmp (str, "SouthEastGravity") == 0) - return SouthEastGravity; - else if (strcmp (str, "StaticGravity") == 0) - return StaticGravity; - else - return NorthWestGravity; -} - -static char* -encode_text_as_utf8_markup (const char *text) -{ - /* text can be any encoding, and is nul-terminated. - * we pretend it's Latin-1 and encode as UTF-8 - */ - GString *str; - const char *p; - char *escaped; - - str = g_string_new (""); - - p = text; - while (*p) - { - g_string_append_unichar (str, *p); - ++p; - } - - escaped = g_markup_escape_text (str->str, str->len); - g_string_free (str, TRUE); - - return escaped; -} - -static char* -decode_text_from_utf8 (const char *text) -{ - /* Convert back from the encoded (but not escaped) UTF-8 */ - GString *str; - const char *p; - - str = g_string_new (""); - - p = text; - while (*p) - { - /* obviously this barfs if the UTF-8 contains chars > 255 */ - g_string_append_c (str, g_utf8_get_char (p)); - - p = g_utf8_next_char (p); - } - - return g_string_free (str, FALSE); -} - -static void -save_state (void) -{ - char *metacity_dir; - char *session_dir; - FILE *outfile; - GSList *windows; - GSList *tmp; - int stack_position; - - g_assert (client_id); - - outfile = NULL; - - /* - * g_get_user_config_dir() is guaranteed to return an existing directory. - * Eventually, if SM stays with the WM, I'd like to make this - * something like <config>/window_placement in a standard format. - * Future optimisers should note also that by the time we get here - * we probably already have full_save_path figured out and therefore - * can just use the directory name from that. - */ - metacity_dir = g_strconcat (g_get_user_config_dir (), - G_DIR_SEPARATOR_S "metacity", - NULL); - - session_dir = g_strconcat (metacity_dir, - G_DIR_SEPARATOR_S "sessions", - NULL); - - if (mkdir (metacity_dir, 0700) < 0 && - errno != EEXIST) - { - meta_warning (_("Could not create directory '%s': %s\n"), - metacity_dir, g_strerror (errno)); - } - - if (mkdir (session_dir, 0700) < 0 && - errno != EEXIST) - { - meta_warning (_("Could not create directory '%s': %s\n"), - session_dir, g_strerror (errno)); - } - - meta_topic (META_DEBUG_SM, "Saving session to '%s'\n", full_save_file ()); - - outfile = fopen (full_save_file (), "w"); - - if (outfile == NULL) - { - meta_warning (_("Could not open session file '%s' for writing: %s\n"), - full_save_file (), g_strerror (errno)); - goto out; - } - - /* The file format is: - * <metacity_session id="foo"> - * <window id="bar" class="XTerm" name="xterm" title="/foo/bar" role="blah" type="normal" stacking="5"> - * <workspace index="2"/> - * <workspace index="4"/> - * <sticky/> <minimized/> <maximized/> - * <geometry x="100" y="100" width="200" height="200" gravity="northwest"/> - * </window> - * </metacity_session> - * - * Note that attributes on <window> 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. - * - */ - - fprintf (outfile, "<metacity_session id=\"%s\">\n", - client_id); - - windows = meta_display_list_windows (meta_get_display ()); - stack_position = 0; - - windows = g_slist_sort (windows, meta_display_stack_cmp); - tmp = windows; - stack_position = 0; - - while (tmp != NULL) - { - MetaWindow *window; - - window = tmp->data; - - if (window->sm_client_id) - { - char *sm_client_id; - char *res_class; - char *res_name; - char *role; - char *title; - - /* client id, class, name, role are not expected to be - * in UTF-8 (I think they are in XPCS which is Latin-1? - * in practice they are always ascii though.) - */ - - sm_client_id = encode_text_as_utf8_markup (window->sm_client_id); - res_class = window->res_class ? - encode_text_as_utf8_markup (window->res_class) : NULL; - res_name = window->res_name ? - encode_text_as_utf8_markup (window->res_name) : NULL; - role = window->role ? - encode_text_as_utf8_markup (window->role) : NULL; - if (window->title) - title = g_markup_escape_text (window->title, -1); - else - title = NULL; - - meta_topic (META_DEBUG_SM, "Saving session managed window %s, client ID '%s'\n", - window->desc, window->sm_client_id); - - fprintf (outfile, - " <window id=\"%s\" class=\"%s\" name=\"%s\" title=\"%s\" role=\"%s\" type=\"%s\" stacking=\"%d\">\n", - sm_client_id, - res_class ? res_class : "", - res_name ? res_name : "", - title ? title : "", - role ? role : "", - window_type_to_string (window->type), - stack_position); - - g_free (sm_client_id); - g_free (res_class); - g_free (res_name); - g_free (role); - g_free (title); - - /* Sticky */ - if (window->on_all_workspaces) - fputs (" <sticky/>\n", outfile); - - /* Minimized */ - if (window->minimized) - fputs (" <minimized/>\n", outfile); - - /* Maximized */ - if (META_WINDOW_MAXIMIZED (window)) - { - fprintf (outfile, - " <maximized saved_x=\"%d\" saved_y=\"%d\" saved_width=\"%d\" saved_height=\"%d\"/>\n", - window->saved_rect.x, - window->saved_rect.y, - window->saved_rect.width, - window->saved_rect.height); - } - - /* Workspaces we're on */ - { - int n; - n = meta_workspace_index (window->workspace); - fprintf (outfile, - " <workspace index=\"%d\"/>\n", n); - } - - /* Gravity */ - { - int x, y, w, h; - meta_window_get_geometry (window, &x, &y, &w, &h); - - fprintf (outfile, - " <geometry x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" gravity=\"%s\"/>\n", - x, y, w, h, - meta_gravity_to_string (window->size_hints.win_gravity)); - } - - fputs (" </window>\n", outfile); - } - else - { - meta_topic (META_DEBUG_SM, "Not saving window '%s', not session managed\n", - window->desc); - } - - tmp = tmp->next; - ++stack_position; - } - - g_slist_free (windows); - - fputs ("</metacity_session>\n", outfile); - - out: - if (outfile) - { - /* FIXME need a dialog for this */ - if (ferror (outfile)) - { - meta_warning (_("Error writing session file '%s': %s\n"), - full_save_file (), g_strerror (errno)); - } - if (fclose (outfile)) - { - meta_warning (_("Error closing session file '%s': %s\n"), - full_save_file (), g_strerror (errno)); - } - } - - g_free (metacity_dir); - g_free (session_dir); -} - -typedef enum -{ - WINDOW_TAG_NONE, - WINDOW_TAG_DESKTOP, - WINDOW_TAG_STICKY, - WINDOW_TAG_MINIMIZED, - WINDOW_TAG_MAXIMIZED, - WINDOW_TAG_GEOMETRY -} WindowTag; - -typedef struct -{ - MetaWindowSessionInfo *info; - char *previous_id; -} 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 char* -load_state (const char *previous_save_file) -{ - GMarkupParseContext *context; - GError *error; - ParseData parse_data; - char *text; - gsize length; - char *session_file; - - session_file = g_strconcat (g_get_user_config_dir (), - G_DIR_SEPARATOR_S "metacity" - G_DIR_SEPARATOR_S "sessions" G_DIR_SEPARATOR_S, - previous_save_file, - NULL); - - error = NULL; - if (!g_file_get_contents (session_file, - &text, - &length, - &error)) - { - char *canonical_session_file = session_file; - - /* Maybe they were doing it the old way, with ~/.metacity */ - session_file = g_strconcat (g_get_home_dir (), - G_DIR_SEPARATOR_S ".metacity" - G_DIR_SEPARATOR_S "sessions" - G_DIR_SEPARATOR_S, - previous_save_file, - NULL); - - if (!g_file_get_contents (session_file, - &text, - &length, - NULL)) - { - /* oh, just give up */ - - meta_warning (_("Failed to read saved session file %s: %s\n"), - canonical_session_file, error->message); - g_error_free (error); - g_free (session_file); - g_free (canonical_session_file); - return NULL; - } - - g_free (canonical_session_file); - } - - meta_topic (META_DEBUG_SM, "Parsing saved session file %s\n", session_file); - g_free (session_file); - session_file = NULL; - - parse_data.info = NULL; - parse_data.previous_id = 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); - - if (parse_data.info) - session_info_free (parse_data.info); - - g_free (parse_data.previous_id); - parse_data.previous_id = NULL; - - out: - - g_free (text); - - return parse_data.previous_id; -} - -/* FIXME this isn't very robust against bogus session files */ -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, "metacity_session") == 0) - { - /* Get previous ID */ - int i; - - i = 0; - while (attribute_names[i]) - { - const char *name; - const char *val; - - name = attribute_names[i]; - val = attribute_values[i]; - - if (pd->previous_id) - { - g_set_error (error, - G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("<metacity_session> attribute seen but we already have the session ID")); - return; - } - - if (strcmp (name, "id") == 0) - { - pd->previous_id = decode_text_from_utf8 (val); - } - else - { - g_set_error (error, - G_MARKUP_ERROR, - G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, - _("Unknown attribute %s on <%s> element"), - name, "metacity_session"); - return; - } - - ++i; - } - } - else if (strcmp (element_name, "window") == 0) - { - int i; - - if (pd->info) - { - g_set_error (error, - G_MARKUP_ERROR, - G_MARKUP_ERROR_PARSE, - _("nested <window> 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 = decode_text_from_utf8 (val); - } - else if (strcmp (name, "class") == 0) - { - if (*val) - pd->info->res_class = decode_text_from_utf8 (val); - } - else if (strcmp (name, "name") == 0) - { - if (*val) - pd->info->res_name = decode_text_from_utf8 (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 = decode_text_from_utf8 (val); - } - else if (strcmp (name, "type") == 0) - { - if (*val) - pd->info->type = window_type_from_string (val); - } - else if (strcmp (name, "stacking") == 0) - { - if (*val) - { - pd->info->stack_position = atoi (val); - pd->info->stack_position_set = TRUE; - } - } - else - { - g_set_error (error, - G_MARKUP_ERROR, - G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, - _("Unknown attribute %s on <%s> element"), - name, "window"); - 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 <%s> element"), - name, "window"); - 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, "minimized") == 0) - { - pd->info->minimized = TRUE; - pd->info->minimized_set = TRUE; - } - else if (strcmp (element_name, "maximized") == 0) - { - int i; - - i = 0; - pd->info->maximized = TRUE; - pd->info->maximized_set = TRUE; - while (attribute_names[i]) - { - const char *name; - const char *val; - - name = attribute_names[i]; - val = attribute_values[i]; - - if (strcmp (name, "saved_x") == 0) - { - if (*val) - { - pd->info->saved_rect.x = atoi (val); - pd->info->saved_rect_set = TRUE; - } - } - else if (strcmp (name, "saved_y") == 0) - { - if (*val) - { - pd->info->saved_rect.y = atoi (val); - pd->info->saved_rect_set = TRUE; - } - } - else if (strcmp (name, "saved_width") == 0) - { - if (*val) - { - pd->info->saved_rect.width = atoi (val); - pd->info->saved_rect_set = TRUE; - } - } - else if (strcmp (name, "saved_height") == 0) - { - if (*val) - { - pd->info->saved_rect.height = atoi (val); - pd->info->saved_rect_set = TRUE; - } - } - else - { - g_set_error (error, - G_MARKUP_ERROR, - G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, - _("Unknown attribute %s on <%s> element"), - name, "maximized"); - return; - } - - ++i; - } - - if (pd->info->saved_rect_set) - meta_topic (META_DEBUG_SM, "Saved unmaximized size %d,%d %dx%d \n", - pd->info->saved_rect.x, - pd->info->saved_rect.y, - pd->info->saved_rect.width, - pd->info->saved_rect.height); - } - else if (strcmp (element_name, "geometry") == 0) - { - int i; - - pd->info->geometry_set = TRUE; - - i = 0; - while (attribute_names[i]) - { - const char *name; - const char *val; - - name = attribute_names[i]; - val = attribute_values[i]; - - if (strcmp (name, "x") == 0) - { - if (*val) - pd->info->rect.x = atoi (val); - } - else if (strcmp (name, "y") == 0) - { - if (*val) - pd->info->rect.y = atoi (val); - } - else if (strcmp (name, "width") == 0) - { - if (*val) - pd->info->rect.width = atoi (val); - } - else if (strcmp (name, "height") == 0) - { - if (*val) - pd->info->rect.height = atoi (val); - } - else if (strcmp (name, "gravity") == 0) - { - if (*val) - pd->info->gravity = window_gravity_from_string (val); - } - else - { - g_set_error (error, - G_MARKUP_ERROR, - G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, - _("Unknown attribute %s on <%s> element"), - name, "geometry"); - return; - } - - ++i; - } - - meta_topic (META_DEBUG_SM, "Loaded geometry %d,%d %dx%d gravity %s\n", - pd->info->rect.x, - pd->info->rect.y, - pd->info->rect.width, - pd->info->rect.height, - meta_gravity_to_string (pd->info->gravity)); - } - 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_topic (META_DEBUG_SM, "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) -{ - /* 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; - gboolean ignore_client_id; - - retval = NULL; - - ignore_client_id = g_getenv ("METACITY_DEBUG_SM") != NULL; - - tmp = window_info_list; - while (tmp != NULL) - { - MetaWindowSessionInfo *info; - - info = tmp->data; - - if ((ignore_client_id || - 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_topic (META_DEBUG_SM, "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); - } - else - { - if (meta_is_verbose ()) - { - if (!both_null_or_matching (info->id, window->sm_client_id)) - meta_topic (META_DEBUG_SM, "Window %s has SM client ID %s, saved state has %s, no match\n", - window->desc, - window->sm_client_id ? window->sm_client_id : "(none)", - info->id ? info->id : "(none)"); - else if (!both_null_or_matching (info->res_class, window->res_class)) - meta_topic (META_DEBUG_SM, "Window %s has class %s doesn't match saved class %s, no match\n", - window->desc, - window->res_class ? window->res_class : "(none)", - info->res_class ? info->res_class : "(none)"); - - else if (!both_null_or_matching (info->res_name, window->res_name)) - meta_topic (META_DEBUG_SM, "Window %s has name %s doesn't match saved name %s, no match\n", - window->desc, - window->res_name ? window->res_name : "(none)", - info->res_name ? info->res_name : "(none)"); - else if (!both_null_or_matching (info->role, window->role)) - meta_topic (META_DEBUG_SM, "Window %s has role %s doesn't match saved role %s, no match\n", - window->desc, - window->role ? window->role : "(none)", - info->role ? info->role : "(none)"); - else - meta_topic (META_DEBUG_SM, "???? should not happen - window %s doesn't match saved state %s for no good reason\n", - window->desc, info->id); - } - } - - tmp = tmp->next; - } - - return retval; -} - -static 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; -} - -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) - { - meta_topic (META_DEBUG_SM, - "Window %s is not session managed, not checking for saved state\n", - window->desc); - return NULL; - } - - possibles = get_possible_matches (window); - - if (possibles == NULL) - { - meta_topic (META_DEBUG_SM, "Window %s has no possible matches in the list of saved window states\n", - window->desc); - return NULL; - } - - info = find_best_match (possibles, window); - - g_slist_free (possibles); - - return info; -} - -void -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; - info->gravity = NorthWestGravity; - - return info; -} - -static char* full_save_path = NULL; - -static void -regenerate_save_file (void) -{ - g_free (full_save_path); - - if (client_id) - full_save_path = g_strconcat (g_get_user_config_dir (), - G_DIR_SEPARATOR_S "metacity" - G_DIR_SEPARATOR_S "sessions" G_DIR_SEPARATOR_S, - client_id, - ".ms", - NULL); - else - full_save_path = NULL; -} - -static const char* -full_save_file (void) -{ - return full_save_path; -} - -static int -windows_cmp_by_title (MetaWindow *a, - MetaWindow *b) -{ - return g_utf8_collate (a->title, b->title); -} - -typedef struct -{ - int child_pid; - int child_pipe; - gboolean shutdown; -} LameClientsDialogData; - -static void -finish_interact (gboolean shutdown) -{ - if (current_state == STATE_DONE_WITH_INTERACT) /* paranoia */ - { - SmcInteractDone (session_connection, False /* don't cancel logout */); - - save_yourself_possibly_done (shutdown, TRUE); - } -} - -static gboolean -io_from_warning_dialog (GIOChannel *channel, - GIOCondition condition, - gpointer data) -{ - LameClientsDialogData *d; - - d = data; - - meta_topic (META_DEBUG_PING, - "IO handler from lame clients dialog, condition = %x\n", - condition); - - if (condition & (G_IO_HUP | G_IO_NVAL | G_IO_ERR)) - { - finish_interact (d->shutdown); - - /* Remove the callback, freeing data */ - return FALSE; - } - else if (condition & G_IO_IN) - { - /* Check for EOF */ - - char buf[16]; - int ret; - - ret = read (d->child_pipe, buf, sizeof (buf)); - if (ret == 0) - { - finish_interact (d->shutdown); - return FALSE; - } - } - - /* Keep callback installed */ - return TRUE; -} - -static void -warn_about_lame_clients_and_finish_interact (gboolean shutdown) -{ - GSList *lame; - GSList *windows; - char **argv; - int i; - GSList *tmp; - int len; - int child_pid; - int child_pipe; - GError *err; - GIOChannel *channel; - LameClientsDialogData *d; - guint32 timestamp; - char timestampbuf[32]; - - lame = NULL; - windows = meta_display_list_windows (meta_get_display ()); - tmp = windows; - while (tmp != NULL) - { - MetaWindow *window; - - window = tmp->data; - - /* only complain about normal windows, the others - * are kind of dumb to worry about - */ - if (window->sm_client_id == NULL && - window->type == META_WINDOW_NORMAL) - lame = g_slist_prepend (lame, window); - - tmp = tmp->next; - } - - g_slist_free (windows); - - if (lame == NULL) - { - /* No lame apps. */ - finish_interact (shutdown); - return; - } - - lame = g_slist_sort (lame, (GCompareFunc) windows_cmp_by_title); - - timestamp = meta_display_get_current_time_roundtrip (meta_get_display ()); - sprintf (timestampbuf, "%u", timestamp); - - len = g_slist_length (lame); - len *= 2; /* titles and also classes */ - len += 2; /* --timestamp flag and actual timestamp */ - len += 1; /* NULL term */ - len += 2; /* metacity-dialog command and option */ - - argv = g_new0 (char*, len); - - i = 0; - - argv[i] = METACITY_LIBEXECDIR"/metacity-dialog"; - ++i; - argv[i] = "--timestamp"; - ++i; - argv[i] = timestampbuf; - ++i; - argv[i] = "--warn-about-no-sm-support"; - ++i; - - tmp = lame; - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - - argv[i] = w->title; - ++i; - argv[i] = w->res_class ? w->res_class : ""; - ++i; - - tmp = tmp->next; - } - - child_pipe = -1; - child_pid = -1; - err = NULL; - if (!g_spawn_async_with_pipes ("/", - argv, - NULL, - 0, - NULL, NULL, - &child_pid, - NULL, - &child_pipe, - NULL, - &err)) - { - meta_warning (_("Error launching metacity-dialog to warn about apps that don't support session management: %s\n"), - err->message); - g_error_free (err); - } - - g_free (argv); - g_slist_free (lame); - - d = g_new0 (LameClientsDialogData, 1); - d->child_pipe = child_pipe; - d->child_pid = child_pid; - d->shutdown = shutdown; - - channel = g_io_channel_unix_new (d->child_pipe); - g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, - G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL, - io_from_warning_dialog, - d, g_free); - g_io_channel_unref (channel); -} - -#endif /* HAVE_SM */ |