/* $Id: xfsm-manager.c 23142 2006-09-13 15:52:01Z benny $ */ /*- * Copyright (c) 2003-2006 Benedikt Meurer * All rights reserved. * * 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, 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. * * The session id generator was taken from the KDE session manager. * Copyright (c) 2000 Matthias Ettrich */ #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_MEMORY_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_SESSION_NAME "Default" /* Prototypes */ static gboolean xfsm_manager_startup (void); static void xfsm_manager_handle_failed (void); static gboolean xfsm_manager_startup_timedout (gpointer user_data); static void xfsm_manager_load_settings (XfceRc *rc); static gboolean xfsm_manager_load_session (void); static GdkPixbuf *xfsm_manager_load_session_preview (const gchar *name); /* Static data */ static XfsmManagerState state = XFSM_MANAGER_STARTUP; static gboolean session_chooser = FALSE; static guint die_timeout_id = 0; static guint startup_timeout_id = 0; static gboolean xfsm_manager_startup (void) { xfsm_startup_foreign (); pending_properties = g_list_sort (pending_properties, (GCompareFunc) xfsm_properties_compare); xfsm_manager_startup_continue (NULL); return FALSE; } static void xfsm_manager_restore_active_workspace (XfceRc *rc) { NetkWorkspace *workspace; GdkDisplay *display; NetkScreen *screen; gchar buffer[1024]; gint n, m; display = gdk_display_get_default (); for (n = 0; n < gdk_display_get_n_screens (display); ++n) { g_snprintf (buffer, 1024, "Screen%d_ActiveWorkspace", n); if (!xfce_rc_has_entry (rc, buffer)) continue; m = xfce_rc_read_int_entry (rc, buffer, 0); screen = netk_screen_get (n); netk_screen_force_update (screen); if (netk_screen_get_workspace_count (screen) > m) { workspace = netk_screen_get_workspace (screen, m); netk_workspace_activate (workspace); } } } static void xfsm_manager_handle_failed (void) { /* Handle apps that failed to start here */ XfsmProperties *properties; GList *lp; for (lp = starting_properties; lp != NULL; lp = lp->next) { properties = XFSM_PROPERTIES (lp->data); if (properties->discard_command != NULL) { xfsm_verbose ("Client Id = %s failed to start, running discard " "command now.\n\n", properties->client_id); g_spawn_sync (properties->current_directory, properties->discard_command, properties->environment, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, NULL, NULL); } xfsm_properties_free (properties); } g_list_free (starting_properties); starting_properties = NULL; } static gboolean xfsm_manager_choose_session (XfceRc *rc) { XfsmSessionInfo *session; GdkPixbuf *preview_default = NULL; gboolean load = FALSE; GList *sessions = NULL; GList *lp; gchar **groups; gchar *name; gint result; gint n; groups = xfce_rc_get_groups (rc); for (n = 0; groups[n] != NULL; ++n) { if (strncmp (groups[n], "Session: ", 9) == 0) { xfce_rc_set_group (rc, groups[n]); session = g_new0 (XfsmSessionInfo, 1); session->name = groups[n] + 9; session->atime = xfce_rc_read_int_entry (rc, "LastAccess", 0); session->preview = xfsm_manager_load_session_preview (session->name); if (session->preview == NULL) { if (G_UNLIKELY (preview_default == NULL)) { preview_default = gdk_pixbuf_from_pixdata (&chooser_icon_data, FALSE, NULL); } session->preview = GDK_PIXBUF (g_object_ref (preview_default)); } sessions = g_list_append (sessions, session); } } if (preview_default != NULL) g_object_unref (preview_default); if (sessions != NULL) { result = xfsm_splash_screen_choose (splash_screen, sessions, session_name, &name); if (result == XFSM_CHOOSE_LOGOUT) { xfce_rc_close (rc); exit (EXIT_SUCCESS); } else if (result == XFSM_CHOOSE_LOAD) { load = TRUE; } if (session_name != NULL) g_free (session_name); session_name = name; for (lp = sessions; lp != NULL; lp = lp->next) { session = (XfsmSessionInfo *) lp->data; g_object_unref (session->preview); g_free (session); } g_list_free (sessions); } g_strfreev (groups); return load; } static gboolean xfsm_manager_load_session (void) { XfsmProperties *properties; gchar buffer[1024]; XfceRc *rc; gint count; if (!g_file_test (session_file, G_FILE_TEST_IS_REGULAR)) return FALSE; rc = xfce_rc_simple_open (session_file, FALSE); if (G_UNLIKELY (rc == NULL)) return FALSE; if (session_chooser && !xfsm_manager_choose_session (rc)) { xfce_rc_close (rc); return FALSE; } g_snprintf (buffer, 1024, "Session: %s", session_name); xfce_rc_set_group (rc, buffer); count = xfce_rc_read_int_entry (rc, "Count", 0); if (G_UNLIKELY (count <= 0)) { xfce_rc_close (rc); return FALSE; } while (count-- > 0) { g_snprintf (buffer, 1024, "Client%d_", count); properties = xfsm_properties_load (rc, buffer); if (G_UNLIKELY (properties == NULL)) continue; if (xfsm_properties_check (properties)) pending_properties = g_list_append (pending_properties, properties); else xfsm_properties_free (properties); } /* load legacy applications */ xfsm_legacy_load_session (rc); xfce_rc_close (rc); return pending_properties != NULL; } static gboolean xfsm_manager_load_failsafe (XfceRc *rc) { FailsafeClient *fclient; const gchar *old_group; GdkDisplay *display; gchar **command; gchar command_entry[256]; gchar screen_entry[256]; gint count; gint i; gint n_screen; if (!xfce_rc_has_group (rc, "Failsafe Session")) return FALSE; display = gdk_display_get_default (); old_group = xfce_rc_get_group (rc); xfce_rc_set_group (rc, "Failsafe Session"); count = xfce_rc_read_int_entry (rc, "Count", 0); for (i = 0; i < count; ++i) { g_snprintf (command_entry, 256, "Client%d_Command", i); command = xfce_rc_read_list_entry (rc, command_entry, NULL); if (G_UNLIKELY (command == NULL)) continue; g_snprintf (screen_entry, 256, "Client%d_PerScreen", i); if (xfce_rc_read_bool_entry (rc, screen_entry, FALSE)) { for (n_screen = 0; n_screen < gdk_display_get_n_screens (display); ++n_screen) { fclient = g_new0 (FailsafeClient, 1); fclient->command = xfce_rc_read_list_entry (rc, command_entry, NULL); fclient->screen = gdk_display_get_screen (display, n_screen); failsafe_clients = g_list_append (failsafe_clients, fclient); } } else { fclient = g_new0 (FailsafeClient, 1); fclient->command = command; fclient->screen = gdk_screen_get_default (); failsafe_clients = g_list_append (failsafe_clients, fclient); } } xfce_rc_set_group (rc, old_group); return failsafe_clients != NULL; } static void xfsm_manager_load_settings (XfceRc *rc) { gboolean session_loaded = FALSE; const gchar *name; name = xfce_rc_read_entry (rc, "SessionName", NULL); if (name != NULL && *name != '\0') session_name = g_strdup (name); else session_name = g_strdup (DEFAULT_SESSION_NAME); xfce_rc_set_group (rc, "Chooser"); session_chooser = xfce_rc_read_bool_entry (rc, "AlwaysDisplay", FALSE); session_loaded = xfsm_manager_load_session (); if (session_loaded) { xfsm_verbose ("Session \"%s\" loaded successfully.\n\n", session_name); failsafe_mode = FALSE; } else { if (!xfsm_manager_load_failsafe (rc)) { fprintf (stderr, "xfce4-session: Unable to load failsafe session, exiting. Please check\n" " the value of the environment variable XDG_CONFIG_DIRS\n" " and make sure that it includes the following path:\n\n" " " SYSCONFDIR "/xdg\n\n"); xfce_rc_close (rc); exit (EXIT_FAILURE); } failsafe_mode = TRUE; } } void xfsm_manager_init (XfceRc *rc) { gchar *display_name; gchar *resource_name; #ifdef HAVE_OS_CYGWIN gchar *s; #endif xfce_rc_set_group (rc, "General"); display_name = xfce_gdk_display_get_fullname (gdk_display_get_default ()); #ifdef HAVE_OS_CYGWIN /* rename a colon (:) to a hash (#) under cygwin. windows doesn't like * filenames with a colon... */ for (s = display_name; *s != '\0'; ++s) if (*s == ':') *s = '#'; #endif resource_name = g_strconcat ("sessions/xfce4-session-", display_name, NULL); session_file = xfce_resource_save_location (XFCE_RESOURCE_CACHE, resource_name, TRUE); g_free (resource_name); g_free (display_name); xfsm_manager_load_settings (rc); } gboolean xfsm_manager_restart (void) { GdkPixbuf *preview; unsigned steps; g_assert (session_name != NULL); /* setup legacy application handling */ xfsm_legacy_init (); /* tell splash screen that the session is starting now */ preview = xfsm_manager_load_session_preview (session_name); if (preview == NULL) preview = gdk_pixbuf_from_pixdata (&chooser_icon_data, FALSE, NULL); steps = g_list_length (failsafe_mode ? failsafe_clients : pending_properties); xfsm_splash_screen_start (splash_screen, session_name, preview, steps); g_object_unref (preview); g_idle_add ((GSourceFunc) xfsm_manager_startup, NULL); return TRUE; } void xfsm_manager_startup_continue (const gchar *previous_id) { gboolean startup_done = FALSE; gchar buffer[1024]; XfceRc *rc; xfsm_verbose ("Manager startup continues [Previous Id = %s]\n\n", previous_id != NULL ? previous_id : "None"); if (startup_timeout_id != 0) { g_source_remove (startup_timeout_id); startup_timeout_id = 0; /* work around broken clients */ if (state != XFSM_MANAGER_STARTUP) return; } startup_done = xfsm_startup_continue (previous_id); if (startup_done) { xfsm_verbose ("Manager finished startup, entering IDLE mode now\n\n"); state = XFSM_MANAGER_IDLE; if (!failsafe_mode) { /* handle apps that failed to start */ xfsm_manager_handle_failed (); /* restore active workspace, this has to be done after the * window manager is up, so we do it last. */ g_snprintf (buffer, 1024, "Session: %s", session_name); rc = xfce_rc_simple_open (session_file, TRUE); xfce_rc_set_group (rc, buffer); xfsm_manager_restore_active_workspace (rc); xfce_rc_close (rc); /* start legacy applications now */ xfsm_legacy_startup (); } } else { startup_timeout_id = g_timeout_add (STARTUP_TIMEOUT, xfsm_manager_startup_timedout, NULL); } } static gboolean xfsm_manager_startup_timedout (gpointer user_data) { xfsm_verbose ("Manager startup timed out\n\n"); startup_timeout_id = 0; /* will be removed automagically once we return */ xfsm_manager_startup_continue (NULL); return FALSE; } gchar* xfsm_manager_generate_client_id (SmsConn sms_conn) { static char *addr = NULL; static int sequence = 0; char *sms_id; char *id = NULL; if (sms_conn != NULL) { sms_id = SmsGenerateClientID (sms_conn); if (sms_id != NULL) { id = g_strdup (sms_id); free (sms_id); } } if (id == NULL) { if (addr == NULL) { /* * Faking our IP address, the 0 below is "unknown" * address format (1 would be IP, 2 would be DEC-NET * format). Stolen from KDE :-) */ addr = g_strdup_printf ("0%.8x", g_random_int ()); } id = (char *) g_malloc (50); g_snprintf (id, 50, "1%s%.13ld%.10d%.4d", addr, (long) time (NULL), (int) getpid (), sequence); sequence = (sequence + 1) % 10000; } return id; } XfsmClient* xfsm_manager_new_client (SmsConn sms_conn, gchar **error) { if (G_UNLIKELY (state != XFSM_MANAGER_IDLE) && G_UNLIKELY (state != XFSM_MANAGER_STARTUP)) { if (error != NULL) *error = "We don't accept clients while in CheckPoint/Shutdown state!"; return NULL; } return xfsm_client_new (sms_conn); } gboolean xfsm_manager_register_client (XfsmClient *client, const gchar *previous_id) { XfsmProperties *properties = NULL; gchar *client_id; GList *lp; if (previous_id != NULL) { for (lp = starting_properties; lp != NULL; lp = lp->next) { if (strcmp (XFSM_PROPERTIES (lp->data)->client_id, previous_id) == 0) { properties = XFSM_PROPERTIES (lp->data); starting_properties = g_list_remove (starting_properties, properties); break; } } if (properties == NULL) { for (lp = pending_properties; lp != NULL; lp = lp->next) { if (!strcmp (XFSM_PROPERTIES (lp->data)->client_id, previous_id)) { properties = XFSM_PROPERTIES (lp->data); pending_properties = g_list_remove (pending_properties, properties); break; } } } /* If previous_id is invalid, the SM will send a BadValue error message * to the client and reverts to register state waiting for another * RegisterClient message. */ if (properties == NULL) return FALSE; xfsm_client_set_initial_properties (client, properties); } else { client_id = xfsm_manager_generate_client_id (client->sms_conn); properties = xfsm_properties_new (client_id, SmsClientHostName (client->sms_conn)); xfsm_client_set_initial_properties (client, properties); g_free (client_id); } running_clients = g_list_append (running_clients, client); SmsRegisterClientReply (client->sms_conn, (char *) client->id); if (previous_id == NULL) { SmsSaveYourself (client->sms_conn, SmSaveLocal, False, SmInteractStyleNone, False); client->state = XFSM_CLIENT_SAVINGLOCAL; client->save_timeout_id = g_timeout_add (SAVE_TIMEOUT, xfsm_manager_save_timeout, client); } if ((failsafe_mode || previous_id != NULL) && state == XFSM_MANAGER_STARTUP) { /* Only continue the startup if we are either in Failsafe mode (which * means that we don't have any previous_id at all) or the previous_id * matched one of the starting_properties. If there was no match * above, previous_id will be NULL here. * See http://bugs.xfce.org/view_bug_page.php?f_id=212 for details. */ xfsm_manager_startup_continue (previous_id); } return TRUE; } void xfsm_manager_start_interact (XfsmClient *client) { /* notify client of interact */ SmsInteract (client->sms_conn); client->state = XFSM_CLIENT_INTERACTING; /* stop save yourself timeout */ g_source_remove (client->save_timeout_id); client->save_timeout_id = 0; } void xfsm_manager_interact (XfsmClient *client, int dialog_type) { GList *lp; if (G_UNLIKELY (client->state != XFSM_CLIENT_SAVING)) { xfsm_verbose ("Client Id = %s, requested INTERACT, but client is not in SAVING mode\n" " Client will be disconnected now.\n\n", client->id); xfsm_manager_close_connection (client, TRUE); } else if (G_UNLIKELY (state != XFSM_MANAGER_CHECKPOINT) && G_UNLIKELY (state != XFSM_MANAGER_SHUTDOWN)) { xfsm_verbose ("Client Id = %s, requested INTERACT, but manager is not in CheckPoint/Shutdown mode\n" " Clinet will be disconnected now.\n\n", client->id); xfsm_manager_close_connection (client, TRUE); } else { for (lp = running_clients; lp != NULL; lp = lp->next) if (XFSM_CLIENT (lp->data)->state == XFSM_CLIENT_INTERACTING) { client->state = XFSM_CLIENT_WAITFORINTERACT; return; } xfsm_manager_start_interact (client); } } void xfsm_manager_interact_done (XfsmClient *client, gboolean cancel_shutdown) { GList *lp; if (G_UNLIKELY (client->state != XFSM_CLIENT_INTERACTING)) { xfsm_verbose ("Client Id = %s, send INTERACT DONE, but client is not in INTERACTING state\n" " Client will be disconnected now.\n\n", client->id); xfsm_manager_close_connection (client, TRUE); return; } else if (G_UNLIKELY (state != XFSM_MANAGER_CHECKPOINT) && G_UNLIKELY (state != XFSM_MANAGER_SHUTDOWN)) { xfsm_verbose ("Client Id = %s, send INTERACT DONE, but manager is not in CheckPoint/Shutdown state\n" " Client will be disconnected now.\n\n", client->id); xfsm_manager_close_connection (client, TRUE); return; } client->state = XFSM_CLIENT_SAVING; /* Setting the cancel-shutdown field to True indicates that the user * has requested that the entire shutdown be cancelled. Cancel- * shutdown may only be True if the corresponding SaveYourself * message specified True for the shutdown field and Any or Error * for the interact-style field. Otherwise, cancel-shutdown must be * False. */ if (cancel_shutdown && state == XFSM_MANAGER_SHUTDOWN) { /* we go into checkpoint state from here... */ state = XFSM_MANAGER_CHECKPOINT; /* If a shutdown is in progress, the user may have the option * of cancelling the shutdown. If the shutdown is cancelled * (specified in the "Interact Done" message), the session * manager should send a "Shutdown Cancelled" message to each * client that requested to interact. */ for (lp = running_clients; lp != NULL; lp = lp->next) { if (XFSM_CLIENT (lp->data)->state != XFSM_CLIENT_WAITFORINTERACT) continue; /* reset all clients that are waiting for interact */ client->state = XFSM_CLIENT_SAVING; SmsShutdownCancelled (XFSM_CLIENT (lp->data)->sms_conn); } } else { /* let next client interact */ for (lp = running_clients; lp != NULL; lp = lp->next) if (XFSM_CLIENT (lp->data)->state == XFSM_CLIENT_WAITFORINTERACT) { xfsm_manager_start_interact (XFSM_CLIENT (lp->data)); break; } } /* restart save yourself timeout for client */ client->save_timeout_id = g_timeout_add (SAVE_TIMEOUT, xfsm_manager_save_timeout, client); } void xfsm_manager_save_yourself (XfsmClient *client, gint save_type, gboolean shutdown, gint interact_style, gboolean fast, gboolean global) { gboolean shutdown_save = TRUE; GList *lp; if (G_UNLIKELY (client->state != XFSM_CLIENT_IDLE)) { xfsm_verbose ("Client Id = %s, requested SAVE YOURSELF, but client is not in IDLE mode.\n" " Client will be nuked now.\n\n", client->id); xfsm_manager_close_connection (client, TRUE); return; } else if (G_UNLIKELY (state != XFSM_MANAGER_IDLE)) { xfsm_verbose ("Client Id = %s, requested SAVE YOURSELF, but manager is not in IDLE mode.\n" " Client will be nuked now.\n\n", client->id); xfsm_manager_close_connection (client, TRUE); return; } if (!global) { /* client requests a local checkpoint. We slightly ignore * shutdown here, since it does not make sense for a local * checkpoint. */ SmsSaveYourself (client->sms_conn, save_type, FALSE, interact_style, fast); client->state = XFSM_CLIENT_SAVINGLOCAL; client->save_timeout_id = g_timeout_add (SAVE_TIMEOUT, xfsm_manager_save_timeout, client); } else { if (!fast && shutdown && !shutdownDialog (&shutdown_type, &shutdown_save)) return; if (!shutdown || shutdown_save) { state = shutdown ? XFSM_MANAGER_SHUTDOWN : XFSM_MANAGER_CHECKPOINT; /* handle legacy applications first! */ xfsm_legacy_perform_session_save (); for (lp = running_clients; lp != NULL; lp = lp->next) { XfsmClient *client = XFSM_CLIENT (lp->data); /* xterm's session management is broken, so we won't * send a SAVE YOURSELF to xterms */ if (client->properties->program != NULL && strcasecmp (client->properties->program, "xterm") == 0) { continue; } if (client->state != XFSM_CLIENT_SAVINGLOCAL) { SmsSaveYourself (client->sms_conn, save_type, shutdown, interact_style, fast); } client->state = XFSM_CLIENT_SAVING; client->save_timeout_id = g_timeout_add (SAVE_TIMEOUT, xfsm_manager_save_timeout, client); } } else { /* shutdown session without saving */ xfsm_manager_perform_shutdown (); } } } void xfsm_manager_save_yourself_phase2 (XfsmClient *client) { if (state != XFSM_MANAGER_CHECKPOINT && state != XFSM_MANAGER_SHUTDOWN) { SmsSaveYourselfPhase2 (client->sms_conn); client->state = XFSM_CLIENT_SAVINGLOCAL; g_source_remove (client->save_timeout_id); client->save_timeout_id = g_timeout_add (SAVE_TIMEOUT, xfsm_manager_save_timeout, client); } else { client->state = XFSM_CLIENT_WAITFORPHASE2; g_source_remove (client->save_timeout_id); client->save_timeout_id = 0; if (!xfsm_manager_check_clients_saving ()) xfsm_manager_maybe_enter_phase2 (); } } void xfsm_manager_save_yourself_done (XfsmClient *client, gboolean success) { if (client->state != XFSM_CLIENT_SAVING && client->state != XFSM_CLIENT_SAVINGLOCAL) { xfsm_verbose ("Client Id = %s send SAVE YOURSELF DONE, while not being " "in save mode. Prepare to be nuked!\n", client->id); xfsm_manager_close_connection (client, TRUE); } /* remove client save timeout, as client responded in time */ g_source_remove (client->save_timeout_id); client->save_timeout_id = 0; if (client->state == XFSM_CLIENT_SAVINGLOCAL) { /* client completed local SaveYourself */ client->state = XFSM_CLIENT_IDLE; SmsSaveComplete (client->sms_conn); } else if (state != XFSM_MANAGER_CHECKPOINT && state != XFSM_MANAGER_SHUTDOWN) { xfsm_verbose ("Client Id = %s, send SAVE YOURSELF DONE, but manager is not in CheckPoint/Shutdown mode.\n" " Client will be nuked now.\n\n", client->id); xfsm_manager_close_connection (client, TRUE); } else { client->state = XFSM_CLIENT_SAVEDONE; xfsm_manager_complete_saveyourself (); } } void xfsm_manager_close_connection (XfsmClient *client, gboolean cleanup) { IceConn ice_conn; GList *lp; client->state = XFSM_CLIENT_DISCONNECTED; if (client->save_timeout_id > 0) { g_source_remove (client->save_timeout_id); client->save_timeout_id = 0; } if (cleanup) { ice_conn = SmsGetIceConnection (client->sms_conn); SmsCleanUp (client->sms_conn); IceSetShutdownNegotiation (ice_conn, False); IceCloseConnection (ice_conn); } if (state == XFSM_MANAGER_SHUTDOWNPHASE2) { for (lp = running_clients; lp != NULL; lp = lp->next) if (XFSM_CLIENT (lp->data)->state != XFSM_CLIENT_DISCONNECTED) return; /* all clients finished the DIE phase in time */ g_source_remove (die_timeout_id); gtk_main_quit (); } else if (state == XFSM_MANAGER_SHUTDOWN || state == XFSM_MANAGER_CHECKPOINT) { xfsm_verbose ("Client Id = %s, closed connection in checkpoint state\n" " Session manager will show NO MERCY\n\n", client->id); /* stupid client disconnected in CheckPoint state, prepare to be nuked! */ running_clients = g_list_remove (running_clients, client); xfsm_client_free (client); xfsm_manager_complete_saveyourself (); } else { XfsmProperties *properties = client->properties; if (properties != NULL && xfsm_properties_check (properties)) { if (properties->restart_style_hint == SmRestartAnyway) { restart_properties = g_list_append (restart_properties, properties); client->properties = NULL; } else if (properties->restart_style_hint == SmRestartImmediately) { if (++properties->restart_attempts > MAX_RESTART_ATTEMPTS) { xfsm_verbose ("Client Id = %s, reached maximum attempts [Restart attempts = %d]\n" " Will be re-scheduled for run on next startup\n", properties->client_id, properties->restart_attempts); restart_properties = g_list_append (restart_properties, properties); client->properties = NULL; } #if 0 else if (xfsm_manager_run_prop_command (properties, SmRestartCommand)) { /* XXX - add a timeout here, in case the application does not come up */ pending_properties = g_list_append (pending_properties, properties); client->properties = NULL; } #endif } /* Run the SmDiscardCommand after the client exited in IDLE state. * FIXME: Need to handle this better when we support restarting * immediately properly. * Unfortunately the spec isn't clear about the usage of the discard * command. Have to check ksmserver/gnome-session, and come up with * consistent behaviour. * But for now, this work-around fixes the problem of the evergrowing * number of xfwm4 session files when restarting xfwm4 within a session. */ if (state == XFSM_MANAGER_IDLE && properties->discard_command != NULL) { xfsm_verbose ("Client Id = %s exited while in IDLE state, running " "discard command now.\n\n", properties->client_id); g_spawn_sync (properties->current_directory, properties->discard_command, properties->environment, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, NULL, NULL); } } running_clients = g_list_remove (running_clients, client); xfsm_client_free (client); } } void xfsm_manager_close_connection_by_ice_conn (IceConn ice_conn) { GList *lp; for (lp = running_clients; lp != NULL; lp = lp->next) if (SmsGetIceConnection (XFSM_CLIENT (lp->data)->sms_conn) == ice_conn) { xfsm_manager_close_connection (XFSM_CLIENT (lp->data), FALSE); break; } /* be sure to close the Ice connection in any case */ IceSetShutdownNegotiation (ice_conn, False); IceCloseConnection (ice_conn); } void xfsm_manager_perform_shutdown (void) { GList *lp; /* send SmDie message to all clients */ state = XFSM_MANAGER_SHUTDOWNPHASE2; for (lp = running_clients; lp != NULL; lp = lp->next) SmsDie (XFSM_CLIENT (lp->data)->sms_conn); /* give all clients the chance to close the connection */ die_timeout_id = g_timeout_add (DIE_TIMEOUT, (GSourceFunc) gtk_main_quit, NULL); } gboolean xfsm_manager_check_clients_saving (void) { GList *lp; for (lp = running_clients; lp != NULL; lp = lp->next) if (XFSM_CLIENT (lp->data)->state == XFSM_CLIENT_SAVING) return TRUE; return FALSE; } gboolean xfsm_manager_maybe_enter_phase2 (void) { gboolean entered_phase2 = FALSE; GList *lp; for (lp = running_clients; lp != NULL; lp = lp->next) { XfsmClient *client = XFSM_CLIENT (lp->data); if (client->state == XFSM_CLIENT_WAITFORPHASE2) { entered_phase2 = TRUE; SmsSaveYourselfPhase2 (client->sms_conn); client->state = XFSM_CLIENT_SAVING; client->save_timeout_id = g_timeout_add (SAVE_TIMEOUT, xfsm_manager_save_timeout, client); xfsm_verbose ("Client Id = %s enters SAVE YOURSELF PHASE2.\n\n", client->id); } } return entered_phase2; } void xfsm_manager_complete_saveyourself (void) { GList *lp; /* Check if still clients in SAVING state or if we have to enter PHASE2 * now. In either case, SaveYourself cannot be completed in this run. */ if (xfsm_manager_check_clients_saving () || xfsm_manager_maybe_enter_phase2 ()) return; xfsm_verbose ("Manager finished SAVE YOURSELF, session data will be stored now.\n\n"); /* all clients done, store session data */ xfsm_manager_store_session (); if (state == XFSM_MANAGER_CHECKPOINT) { /* reset all clients to idle state */ state = XFSM_MANAGER_IDLE; for (lp = running_clients; lp != NULL; lp = lp->next) { XFSM_CLIENT (lp->data)->state = XFSM_CLIENT_IDLE; SmsSaveComplete (XFSM_CLIENT (lp->data)->sms_conn); } } else { /* shutdown the session */ xfsm_manager_perform_shutdown (); } } gboolean xfsm_manager_save_timeout (gpointer client_data) { XfsmClient *client = XFSM_CLIENT (client_data); xfsm_verbose ("Client id = %s, received SAVE TIMEOUT\n" " Client will be disconnected now.\n\n", client->id); xfsm_manager_close_connection (client, TRUE); return FALSE; } void xfsm_manager_store_session (void) { NetkWorkspace *workspace; GdkDisplay *display; NetkScreen *screen; XfceRc *rc; GList *lp; gchar prefix[64]; gchar *backup; gchar *group; gint count = 0; gint n, m; rc = xfce_rc_simple_open (session_file, FALSE); if (G_UNLIKELY (rc == NULL)) { fprintf (stderr, "xfce4-session: Unable to open session file %s for " "writing. Session data will not be stored. Please check " "your installation.\n", session_file); return; } /* backup the old session file first */ if (g_file_test (session_file, G_FILE_TEST_IS_REGULAR)) { backup = g_strconcat (session_file, ".bak", NULL); unlink (backup); link (session_file, backup); g_free (backup); } group = g_strconcat ("Session: ", session_name, NULL); xfce_rc_delete_group (rc, group, TRUE); xfce_rc_set_group (rc, group); g_free (group); for (lp = restart_properties; lp != NULL; lp = lp->next) { g_snprintf (prefix, 64, "Client%d_", count); xfsm_properties_store (XFSM_PROPERTIES (lp->data), rc, prefix); ++count; } for (lp = running_clients; lp != NULL; lp = lp->next) { if (XFSM_CLIENT (lp->data)->properties == NULL || !xfsm_properties_check (XFSM_CLIENT (lp->data)->properties)) continue; g_snprintf (prefix, 64, "Client%d_", count); xfsm_properties_store (XFSM_CLIENT (lp->data)->properties, rc, prefix); ++count; } xfce_rc_write_int_entry (rc, "Count", count); /* store legacy applications state */ xfsm_legacy_store_session (rc); /* store current workspace numbers */ display = gdk_display_get_default (); for (n = 0; n < gdk_display_get_n_screens (display); ++n) { screen = netk_screen_get (n); netk_screen_force_update (screen); workspace = netk_screen_get_active_workspace (screen); m = netk_workspace_get_number (workspace); g_snprintf (prefix, 64, "Screen%d_ActiveWorkspace", n); xfce_rc_write_int_entry (rc, prefix, m); } /* remember time */ xfce_rc_write_int_entry (rc, "LastAccess", time (NULL)); xfce_rc_close (rc); } static GdkPixbuf* xfsm_manager_load_session_preview (const gchar *name) { #ifdef SESSION_SCREENSHOTS GdkDisplay *display; GdkPixbuf *pb; gchar *display_name; gchar *resource; gchar *filename; /* determine thumb file */ display = gdk_display_get_default (); display_name = xfce_gdk_display_get_fullname (display); resource = g_strconcat ("sessions/thumbs-", display_name, "/", name, ".png", NULL); filename = xfce_resource_save_location (XFCE_RESOURCE_CACHE, resource, TRUE); g_free (display_name); g_free (resource); pb = gdk_pixbuf_new_from_file (filename, NULL); g_free (filename); return pb; #else return NULL; #endif }