diff options
Diffstat (limited to 'src/eggsmclient-xsmp.c')
-rw-r--r-- | src/eggsmclient-xsmp.c | 1977 |
1 files changed, 989 insertions, 988 deletions
diff --git a/src/eggsmclient-xsmp.c b/src/eggsmclient-xsmp.c index 9cf343b..5cade1f 100644 --- a/src/eggsmclient-xsmp.c +++ b/src/eggsmclient-xsmp.c @@ -56,108 +56,109 @@ typedef struct _EggSMClientXSMPClass EggSMClientXSMPClass; */ typedef enum { - XSMP_STATE_IDLE, - XSMP_STATE_SAVE_YOURSELF, - XSMP_STATE_INTERACT_REQUEST, - XSMP_STATE_INTERACT, - XSMP_STATE_SAVE_YOURSELF_DONE, - XSMP_STATE_SHUTDOWN_CANCELLED, - XSMP_STATE_CONNECTION_CLOSED + XSMP_STATE_IDLE, + XSMP_STATE_SAVE_YOURSELF, + XSMP_STATE_INTERACT_REQUEST, + XSMP_STATE_INTERACT, + XSMP_STATE_SAVE_YOURSELF_DONE, + XSMP_STATE_SHUTDOWN_CANCELLED, + XSMP_STATE_CONNECTION_CLOSED } EggSMClientXSMPState; -static const char *state_names[] = { - "idle", - "save-yourself", - "interact-request", - "interact", - "save-yourself-done", - "shutdown-cancelled", - "connection-closed" +static const char *state_names[] = +{ + "idle", + "save-yourself", + "interact-request", + "interact", + "save-yourself-done", + "shutdown-cancelled", + "connection-closed" }; #define EGG_SM_CLIENT_XSMP_STATE(xsmp) (state_names[(xsmp)->state]) struct _EggSMClientXSMP { - EggSMClient parent; + EggSMClient parent; - SmcConn connection; - char *client_id; + SmcConn connection; + char *client_id; - EggSMClientXSMPState state; - char **restart_command; - gboolean set_restart_command; - int restart_style; - char **discard_command; - gboolean set_discard_command; + EggSMClientXSMPState state; + char **restart_command; + gboolean set_restart_command; + int restart_style; + char **discard_command; + gboolean set_discard_command; - guint idle; + guint idle; - /* Current SaveYourself state */ - guint expecting_initial_save_yourself : 1; - guint need_save_state : 1; - guint need_quit_requested : 1; - guint interact_errors : 1; - guint shutting_down : 1; + /* Current SaveYourself state */ + guint expecting_initial_save_yourself : 1; + guint need_save_state : 1; + guint need_quit_requested : 1; + guint interact_errors : 1; + guint shutting_down : 1; - /* Todo list */ - guint waiting_to_set_initial_properties : 1; - guint waiting_to_emit_quit : 1; - guint waiting_to_emit_quit_cancelled : 1; - guint waiting_to_save_myself : 1; + /* Todo list */ + guint waiting_to_set_initial_properties : 1; + guint waiting_to_emit_quit : 1; + guint waiting_to_emit_quit_cancelled : 1; + guint waiting_to_save_myself : 1; }; struct _EggSMClientXSMPClass { - EggSMClientClass parent_class; + EggSMClientClass parent_class; }; static void sm_client_xsmp_startup (EggSMClient *client, - const char *client_id); + const char *client_id); static void sm_client_xsmp_set_restart_command (EggSMClient *client, - int argc, - const char **argv); + int argc, + const char **argv); static void sm_client_xsmp_set_discard_command (EggSMClient *client, - int argc, - const char **argv); + int argc, + const char **argv); static void sm_client_xsmp_will_quit (EggSMClient *client, - gboolean will_quit); + gboolean will_quit); static gboolean sm_client_xsmp_end_session (EggSMClient *client, - EggSMClientEndStyle style, - gboolean request_confirmation); + EggSMClientEndStyle style, + gboolean request_confirmation); static void xsmp_save_yourself (SmcConn smc_conn, - SmPointer client_data, - int save_style, - Bool shutdown, - int interact_style, - Bool fast); + SmPointer client_data, + int save_style, + Bool shutdown, + int interact_style, + Bool fast); static void xsmp_die (SmcConn smc_conn, - SmPointer client_data); + SmPointer client_data); static void xsmp_save_complete (SmcConn smc_conn, - SmPointer client_data); + SmPointer client_data); static void xsmp_shutdown_cancelled (SmcConn smc_conn, - SmPointer client_data); + SmPointer client_data); static void xsmp_interact (SmcConn smc_conn, - SmPointer client_data); + SmPointer client_data); static SmProp *array_prop (const char *name, - ...); + ...); static SmProp *ptrarray_prop (const char *name, - GPtrArray *values); + GPtrArray *values); static SmProp *string_prop (const char *name, - const char *value); + const char *value); static SmProp *card8_prop (const char *name, - unsigned char value); + unsigned char value); static void set_properties (EggSMClientXSMP *xsmp, ...); static void delete_properties (EggSMClientXSMP *xsmp, ...); static GPtrArray *generate_command (char **argv, - const char *client_id, - const char *state_file); + const char *client_id, + const char *state_file); static void save_state (EggSMClientXSMP *xsmp); static void do_save_yourself (EggSMClientXSMP *xsmp); @@ -166,129 +167,129 @@ static void update_pending_events (EggSMClientXSMP *xsmp); static void ice_init (void); static gboolean process_ice_messages (IceConn ice_conn); static void smc_error_handler (SmcConn smc_conn, - Bool swap, - int offending_minor_opcode, - unsigned long offending_sequence, - int error_class, - int severity, - SmPointer values); + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence, + int error_class, + int severity, + SmPointer values); G_DEFINE_TYPE (EggSMClientXSMP, egg_sm_client_xsmp, EGG_TYPE_SM_CLIENT) static void egg_sm_client_xsmp_init (EggSMClientXSMP *xsmp) { - xsmp->state = XSMP_STATE_CONNECTION_CLOSED; - xsmp->connection = NULL; - xsmp->restart_style = SmRestartIfRunning; + xsmp->state = XSMP_STATE_CONNECTION_CLOSED; + xsmp->connection = NULL; + xsmp->restart_style = SmRestartIfRunning; } static void egg_sm_client_xsmp_class_init (EggSMClientXSMPClass *klass) { - EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass); + EggSMClientClass *sm_client_class = EGG_SM_CLIENT_CLASS (klass); - sm_client_class->startup = sm_client_xsmp_startup; - sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command; - sm_client_class->set_discard_command = sm_client_xsmp_set_discard_command; - sm_client_class->will_quit = sm_client_xsmp_will_quit; - sm_client_class->end_session = sm_client_xsmp_end_session; + sm_client_class->startup = sm_client_xsmp_startup; + sm_client_class->set_restart_command = sm_client_xsmp_set_restart_command; + sm_client_class->set_discard_command = sm_client_xsmp_set_discard_command; + sm_client_class->will_quit = sm_client_xsmp_will_quit; + sm_client_class->end_session = sm_client_xsmp_end_session; } EggSMClient * egg_sm_client_xsmp_new (void) { - if (!g_getenv ("SESSION_MANAGER")) - return NULL; + if (!g_getenv ("SESSION_MANAGER")) + return NULL; - return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL); + return g_object_new (EGG_TYPE_SM_CLIENT_XSMP, NULL); } static gboolean sm_client_xsmp_set_initial_properties (gpointer user_data) { - EggSMClientXSMP *xsmp = user_data; - EggDesktopFile *desktop_file; - GPtrArray *clone, *restart; - char pid_str[64]; - - if (xsmp->idle) - { - g_source_remove (xsmp->idle); - xsmp->idle = 0; - } - xsmp->waiting_to_set_initial_properties = FALSE; - - if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART) - xsmp->restart_style = SmRestartNever; - - /* Parse info out of desktop file */ - desktop_file = egg_get_desktop_file (); - if (desktop_file) - { - GError *err = NULL; - char *cmdline, **argv; - int argc; - - if (xsmp->restart_style == SmRestartIfRunning) + EggSMClientXSMP *xsmp = user_data; + EggDesktopFile *desktop_file; + GPtrArray *clone, *restart; + char pid_str[64]; + + if (xsmp->idle) { - if (egg_desktop_file_get_boolean (desktop_file, - "X-MATE-AutoRestart", NULL)) - xsmp->restart_style = SmRestartImmediately; + g_source_remove (xsmp->idle); + xsmp->idle = 0; } + xsmp->waiting_to_set_initial_properties = FALSE; + + if (egg_sm_client_get_mode () == EGG_SM_CLIENT_MODE_NO_RESTART) + xsmp->restart_style = SmRestartNever; - if (!xsmp->set_restart_command) + /* Parse info out of desktop file */ + desktop_file = egg_get_desktop_file (); + if (desktop_file) { - cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err); - if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err)) - { - egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp), - argc, (const char **)argv); - g_strfreev (argv); - } - else - { - g_warning ("Could not parse Exec line in desktop file: %s", - err->message); - g_error_free (err); - } - g_free (cmdline); + GError *err = NULL; + char *cmdline, **argv; + int argc; + + if (xsmp->restart_style == SmRestartIfRunning) + { + if (egg_desktop_file_get_boolean (desktop_file, + "X-MATE-AutoRestart", NULL)) + xsmp->restart_style = SmRestartImmediately; + } + + if (!xsmp->set_restart_command) + { + cmdline = egg_desktop_file_parse_exec (desktop_file, NULL, &err); + if (cmdline && g_shell_parse_argv (cmdline, &argc, &argv, &err)) + { + egg_sm_client_set_restart_command (EGG_SM_CLIENT (xsmp), + argc, (const char **)argv); + g_strfreev (argv); + } + else + { + g_warning ("Could not parse Exec line in desktop file: %s", + err->message); + g_error_free (err); + } + g_free (cmdline); + } } - } - - if (!xsmp->set_restart_command) - xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1); - - clone = generate_command (xsmp->restart_command, NULL, NULL); - restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL); - - g_debug ("Setting initial properties"); - - /* Program, CloneCommand, RestartCommand, and UserID are required. - * ProcessID isn't required, but the SM may be able to do something - * useful with it. - */ - g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ()); - set_properties (xsmp, - string_prop (SmProgram, g_get_prgname ()), - ptrarray_prop (SmCloneCommand, clone), - ptrarray_prop (SmRestartCommand, restart), - string_prop (SmUserID, g_get_user_name ()), - string_prop (SmProcessID, pid_str), - card8_prop (SmRestartStyleHint, xsmp->restart_style), - NULL); - g_ptr_array_free (clone, TRUE); - g_ptr_array_free (restart, TRUE); - - if (desktop_file) - { - set_properties (xsmp, - string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)), - NULL); - } - - update_pending_events (xsmp); - return FALSE; + + if (!xsmp->set_restart_command) + xsmp->restart_command = g_strsplit (g_get_prgname (), " ", -1); + + clone = generate_command (xsmp->restart_command, NULL, NULL); + restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL); + + g_debug ("Setting initial properties"); + + /* Program, CloneCommand, RestartCommand, and UserID are required. + * ProcessID isn't required, but the SM may be able to do something + * useful with it. + */ + g_snprintf (pid_str, sizeof (pid_str), "%lu", (gulong) getpid ()); + set_properties (xsmp, + string_prop (SmProgram, g_get_prgname ()), + ptrarray_prop (SmCloneCommand, clone), + ptrarray_prop (SmRestartCommand, restart), + string_prop (SmUserID, g_get_user_name ()), + string_prop (SmProcessID, pid_str), + card8_prop (SmRestartStyleHint, xsmp->restart_style), + NULL); + g_ptr_array_free (clone, TRUE); + g_ptr_array_free (restart, TRUE); + + if (desktop_file) + { + set_properties (xsmp, + string_prop ("_GSM_DesktopFile", egg_desktop_file_get_source (desktop_file)), + NULL); + } + + update_pending_events (xsmp); + return FALSE; } /* This gets called from two different places: xsmp_die() (when the @@ -298,771 +299,771 @@ sm_client_xsmp_set_initial_properties (gpointer user_data) static void sm_client_xsmp_disconnect (EggSMClientXSMP *xsmp) { - SmcConn connection; + SmcConn connection; - if (!xsmp->connection) - return; + if (!xsmp->connection) + return; - g_debug ("Disconnecting"); + g_debug ("Disconnecting"); - connection = xsmp->connection; - xsmp->connection = NULL; - SmcCloseConnection (connection, 0, NULL); - xsmp->state = XSMP_STATE_CONNECTION_CLOSED; + connection = xsmp->connection; + xsmp->connection = NULL; + SmcCloseConnection (connection, 0, NULL); + xsmp->state = XSMP_STATE_CONNECTION_CLOSED; - xsmp->waiting_to_save_myself = FALSE; - update_pending_events (xsmp); + xsmp->waiting_to_save_myself = FALSE; + update_pending_events (xsmp); } static void sm_client_xsmp_startup (EggSMClient *client, - const char *client_id) + const char *client_id) { - EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; - SmcCallbacks callbacks; - char *ret_client_id; - char error_string_ret[256]; - - xsmp->client_id = g_strdup (client_id); - - ice_init (); - SmcSetErrorHandler (smc_error_handler); - - callbacks.save_yourself.callback = xsmp_save_yourself; - callbacks.die.callback = xsmp_die; - callbacks.save_complete.callback = xsmp_save_complete; - callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled; - - callbacks.save_yourself.client_data = xsmp; - callbacks.die.client_data = xsmp; - callbacks.save_complete.client_data = xsmp; - callbacks.shutdown_cancelled.client_data = xsmp; - - client_id = NULL; - error_string_ret[0] = '\0'; - xsmp->connection = - SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor, - SmcSaveYourselfProcMask | SmcDieProcMask | - SmcSaveCompleteProcMask | - SmcShutdownCancelledProcMask, - &callbacks, - xsmp->client_id, &ret_client_id, - sizeof (error_string_ret), error_string_ret); - - if (!xsmp->connection) - { - g_warning ("Failed to connect to the session manager: %s\n", - error_string_ret[0] ? - error_string_ret : "no error message given"); - xsmp->state = XSMP_STATE_CONNECTION_CLOSED; - return; - } - - /* We expect a pointless initial SaveYourself if either (a) we - * didn't have an initial client ID, or (b) we DID have an initial - * client ID, but the server rejected it and gave us a new one. - */ - if (!xsmp->client_id || - (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0)) - xsmp->expecting_initial_save_yourself = TRUE; - - if (ret_client_id) - { - g_free (xsmp->client_id); - xsmp->client_id = g_strdup (ret_client_id); - free (ret_client_id); - - gdk_threads_enter (); - gdk_set_sm_client_id (xsmp->client_id); - gdk_threads_leave (); - - g_debug ("Got client ID \"%s\"", xsmp->client_id); - } - - xsmp->state = XSMP_STATE_IDLE; - - /* Do not set the initial properties until we reach the main loop, - * so that the application has a chance to call - * egg_set_desktop_file(). (This may also help the session manager - * have a better idea of when the application is fully up and - * running.) - */ - xsmp->waiting_to_set_initial_properties = TRUE; - xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client); + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + SmcCallbacks callbacks; + char *ret_client_id; + char error_string_ret[256]; + + xsmp->client_id = g_strdup (client_id); + + ice_init (); + SmcSetErrorHandler (smc_error_handler); + + callbacks.save_yourself.callback = xsmp_save_yourself; + callbacks.die.callback = xsmp_die; + callbacks.save_complete.callback = xsmp_save_complete; + callbacks.shutdown_cancelled.callback = xsmp_shutdown_cancelled; + + callbacks.save_yourself.client_data = xsmp; + callbacks.die.client_data = xsmp; + callbacks.save_complete.client_data = xsmp; + callbacks.shutdown_cancelled.client_data = xsmp; + + client_id = NULL; + error_string_ret[0] = '\0'; + xsmp->connection = + SmcOpenConnection (NULL, xsmp, SmProtoMajor, SmProtoMinor, + SmcSaveYourselfProcMask | SmcDieProcMask | + SmcSaveCompleteProcMask | + SmcShutdownCancelledProcMask, + &callbacks, + xsmp->client_id, &ret_client_id, + sizeof (error_string_ret), error_string_ret); + + if (!xsmp->connection) + { + g_warning ("Failed to connect to the session manager: %s\n", + error_string_ret[0] ? + error_string_ret : "no error message given"); + xsmp->state = XSMP_STATE_CONNECTION_CLOSED; + return; + } + + /* We expect a pointless initial SaveYourself if either (a) we + * didn't have an initial client ID, or (b) we DID have an initial + * client ID, but the server rejected it and gave us a new one. + */ + if (!xsmp->client_id || + (ret_client_id && strcmp (xsmp->client_id, ret_client_id) != 0)) + xsmp->expecting_initial_save_yourself = TRUE; + + if (ret_client_id) + { + g_free (xsmp->client_id); + xsmp->client_id = g_strdup (ret_client_id); + free (ret_client_id); + + gdk_threads_enter (); + gdk_set_sm_client_id (xsmp->client_id); + gdk_threads_leave (); + + g_debug ("Got client ID \"%s\"", xsmp->client_id); + } + + xsmp->state = XSMP_STATE_IDLE; + + /* Do not set the initial properties until we reach the main loop, + * so that the application has a chance to call + * egg_set_desktop_file(). (This may also help the session manager + * have a better idea of when the application is fully up and + * running.) + */ + xsmp->waiting_to_set_initial_properties = TRUE; + xsmp->idle = g_idle_add (sm_client_xsmp_set_initial_properties, client); } static void sm_client_xsmp_set_restart_command (EggSMClient *client, - int argc, - const char **argv) + int argc, + const char **argv) { - EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; - int i; + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + int i; - g_strfreev (xsmp->restart_command); + g_strfreev (xsmp->restart_command); - xsmp->restart_command = g_new (char *, argc + 1); - for (i = 0; i < argc; i++) - xsmp->restart_command[i] = g_strdup (argv[i]); - xsmp->restart_command[i] = NULL; + xsmp->restart_command = g_new (char *, argc + 1); + for (i = 0; i < argc; i++) + xsmp->restart_command[i] = g_strdup (argv[i]); + xsmp->restart_command[i] = NULL; - xsmp->set_restart_command = TRUE; + xsmp->set_restart_command = TRUE; } static void sm_client_xsmp_set_discard_command (EggSMClient *client, - int argc, - const char **argv) + int argc, + const char **argv) { - EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; - int i; + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + int i; - g_strfreev (xsmp->discard_command); + g_strfreev (xsmp->discard_command); - xsmp->discard_command = g_new (char *, argc + 1); - for (i = 0; i < argc; i++) - xsmp->discard_command[i] = g_strdup (argv[i]); - xsmp->discard_command[i] = NULL; + xsmp->discard_command = g_new (char *, argc + 1); + for (i = 0; i < argc; i++) + xsmp->discard_command[i] = g_strdup (argv[i]); + xsmp->discard_command[i] = NULL; - xsmp->set_discard_command = TRUE; + xsmp->set_discard_command = TRUE; } static void sm_client_xsmp_will_quit (EggSMClient *client, - gboolean will_quit) + gboolean will_quit) { - EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; - - if (xsmp->state == XSMP_STATE_CONNECTION_CLOSED) - { - /* The session manager has already exited! Schedule a quit - * signal. - */ - xsmp->waiting_to_emit_quit = TRUE; - update_pending_events (xsmp); - return; - } - else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) - { - /* We received a ShutdownCancelled message while the application - * was interacting; Schedule a quit_cancelled signal. - */ - xsmp->waiting_to_emit_quit_cancelled = TRUE; - update_pending_events (xsmp); - return; - } - - g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT); - - g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True"); - SmcInteractDone (xsmp->connection, !will_quit); - - if (will_quit && xsmp->need_save_state) - save_state (xsmp); - - g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False"); - SmcSaveYourselfDone (xsmp->connection, will_quit); - xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + + if (xsmp->state == XSMP_STATE_CONNECTION_CLOSED) + { + /* The session manager has already exited! Schedule a quit + * signal. + */ + xsmp->waiting_to_emit_quit = TRUE; + update_pending_events (xsmp); + return; + } + else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) + { + /* We received a ShutdownCancelled message while the application + * was interacting; Schedule a quit_cancelled signal. + */ + xsmp->waiting_to_emit_quit_cancelled = TRUE; + update_pending_events (xsmp); + return; + } + + g_return_if_fail (xsmp->state == XSMP_STATE_INTERACT); + + g_debug ("Sending InteractDone(%s)", will_quit ? "False" : "True"); + SmcInteractDone (xsmp->connection, !will_quit); + + if (will_quit && xsmp->need_save_state) + save_state (xsmp); + + g_debug ("Sending SaveYourselfDone(%s)", will_quit ? "True" : "False"); + SmcSaveYourselfDone (xsmp->connection, will_quit); + xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; } static gboolean sm_client_xsmp_end_session (EggSMClient *client, - EggSMClientEndStyle style, - gboolean request_confirmation) + EggSMClientEndStyle style, + gboolean request_confirmation) { - EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; - int save_type; - - /* To end the session via XSMP, we have to send a - * SaveYourselfRequest. We aren't allowed to do that if anything - * else is going on, but we don't want to expose this fact to the - * application. So we do our best to patch things up here... - * - * In the worst case, this method might block for some length of - * time in process_ice_messages, but the only time that code path is - * honestly likely to get hit is if the application tries to end the - * session as the very first thing it does, in which case it - * probably won't actually block anyway. It's not worth gunking up - * the API to try to deal nicely with the other 0.01% of cases where - * this happens. - */ - - while (xsmp->state != XSMP_STATE_IDLE || - xsmp->expecting_initial_save_yourself) - { - /* If we're already shutting down, we don't need to do anything. */ - if (xsmp->shutting_down) - return TRUE; - - switch (xsmp->state) + EggSMClientXSMP *xsmp = (EggSMClientXSMP *)client; + int save_type; + + /* To end the session via XSMP, we have to send a + * SaveYourselfRequest. We aren't allowed to do that if anything + * else is going on, but we don't want to expose this fact to the + * application. So we do our best to patch things up here... + * + * In the worst case, this method might block for some length of + * time in process_ice_messages, but the only time that code path is + * honestly likely to get hit is if the application tries to end the + * session as the very first thing it does, in which case it + * probably won't actually block anyway. It's not worth gunking up + * the API to try to deal nicely with the other 0.01% of cases where + * this happens. + */ + + while (xsmp->state != XSMP_STATE_IDLE || + xsmp->expecting_initial_save_yourself) { - case XSMP_STATE_CONNECTION_CLOSED: - return FALSE; - - case XSMP_STATE_SAVE_YOURSELF: - /* Trying to log out from the save_state callback? Whatever. - * Abort the save_state. - */ - SmcSaveYourselfDone (xsmp->connection, FALSE); - xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; - break; - - case XSMP_STATE_INTERACT_REQUEST: - case XSMP_STATE_INTERACT: - case XSMP_STATE_SHUTDOWN_CANCELLED: - /* Already in a shutdown-related state, just ignore - * the new shutdown request... - */ - return TRUE; - - case XSMP_STATE_IDLE: - if (xsmp->waiting_to_set_initial_properties) - sm_client_xsmp_set_initial_properties (xsmp); - - if (!xsmp->expecting_initial_save_yourself) - break; - /* else fall through */ - - case XSMP_STATE_SAVE_YOURSELF_DONE: - /* We need to wait for some response from the server.*/ - process_ice_messages (SmcGetIceConnection (xsmp->connection)); - break; + /* If we're already shutting down, we don't need to do anything. */ + if (xsmp->shutting_down) + return TRUE; - default: - /* Hm... shouldn't happen */ - return FALSE; + switch (xsmp->state) + { + case XSMP_STATE_CONNECTION_CLOSED: + return FALSE; + + case XSMP_STATE_SAVE_YOURSELF: + /* Trying to log out from the save_state callback? Whatever. + * Abort the save_state. + */ + SmcSaveYourselfDone (xsmp->connection, FALSE); + xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; + break; + + case XSMP_STATE_INTERACT_REQUEST: + case XSMP_STATE_INTERACT: + case XSMP_STATE_SHUTDOWN_CANCELLED: + /* Already in a shutdown-related state, just ignore + * the new shutdown request... + */ + return TRUE; + + case XSMP_STATE_IDLE: + if (xsmp->waiting_to_set_initial_properties) + sm_client_xsmp_set_initial_properties (xsmp); + + if (!xsmp->expecting_initial_save_yourself) + break; + /* else fall through */ + + case XSMP_STATE_SAVE_YOURSELF_DONE: + /* We need to wait for some response from the server.*/ + process_ice_messages (SmcGetIceConnection (xsmp->connection)); + break; + + default: + /* Hm... shouldn't happen */ + return FALSE; + } } - } - - /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and - * the user chooses to save the session. But mate-session will do - * the wrong thing if we pass SmSaveBoth and the user chooses NOT to - * save the session... Sigh. - */ - if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session")) - save_type = SmSaveBoth; - else - save_type = SmSaveGlobal; - - g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : ""); - SmcRequestSaveYourself (xsmp->connection, - save_type, - True, /* shutdown */ - SmInteractStyleAny, - !request_confirmation, /* fast */ - True /* global */); - return TRUE; + + /* xfce4-session will do the wrong thing if we pass SmSaveGlobal and + * the user chooses to save the session. But mate-session will do + * the wrong thing if we pass SmSaveBoth and the user chooses NOT to + * save the session... Sigh. + */ + if (!strcmp (SmcVendor (xsmp->connection), "xfce4-session")) + save_type = SmSaveBoth; + else + save_type = SmSaveGlobal; + + g_debug ("Sending SaveYourselfRequest(SmSaveGlobal, Shutdown, SmInteractStyleAny, %sFast)", request_confirmation ? "!" : ""); + SmcRequestSaveYourself (xsmp->connection, + save_type, + True, /* shutdown */ + SmInteractStyleAny, + !request_confirmation, /* fast */ + True /* global */); + return TRUE; } static gboolean idle_do_pending_events (gpointer data) { - EggSMClientXSMP *xsmp = data; - EggSMClient *client = data; - - gdk_threads_enter (); - - xsmp->idle = 0; - - if (xsmp->waiting_to_emit_quit) - { - xsmp->waiting_to_emit_quit = FALSE; - egg_sm_client_quit (client); - goto out; - } - - if (xsmp->waiting_to_emit_quit_cancelled) - { - xsmp->waiting_to_emit_quit_cancelled = FALSE; - egg_sm_client_quit_cancelled (client); - xsmp->state = XSMP_STATE_IDLE; - } - - if (xsmp->waiting_to_save_myself) - { - xsmp->waiting_to_save_myself = FALSE; - do_save_yourself (xsmp); - } - - out: - gdk_threads_leave (); - return FALSE; + EggSMClientXSMP *xsmp = data; + EggSMClient *client = data; + + gdk_threads_enter (); + + xsmp->idle = 0; + + if (xsmp->waiting_to_emit_quit) + { + xsmp->waiting_to_emit_quit = FALSE; + egg_sm_client_quit (client); + goto out; + } + + if (xsmp->waiting_to_emit_quit_cancelled) + { + xsmp->waiting_to_emit_quit_cancelled = FALSE; + egg_sm_client_quit_cancelled (client); + xsmp->state = XSMP_STATE_IDLE; + } + + if (xsmp->waiting_to_save_myself) + { + xsmp->waiting_to_save_myself = FALSE; + do_save_yourself (xsmp); + } + +out: + gdk_threads_leave (); + return FALSE; } static void update_pending_events (EggSMClientXSMP *xsmp) { - gboolean want_idle = - xsmp->waiting_to_emit_quit || - xsmp->waiting_to_emit_quit_cancelled || - xsmp->waiting_to_save_myself; - - if (want_idle) - { - if (xsmp->idle == 0) - xsmp->idle = g_idle_add (idle_do_pending_events, xsmp); - } - else - { - if (xsmp->idle != 0) - g_source_remove (xsmp->idle); - xsmp->idle = 0; - } + gboolean want_idle = + xsmp->waiting_to_emit_quit || + xsmp->waiting_to_emit_quit_cancelled || + xsmp->waiting_to_save_myself; + + if (want_idle) + { + if (xsmp->idle == 0) + xsmp->idle = g_idle_add (idle_do_pending_events, xsmp); + } + else + { + if (xsmp->idle != 0) + g_source_remove (xsmp->idle); + xsmp->idle = 0; + } } static void fix_broken_state (EggSMClientXSMP *xsmp, const char *message, - gboolean send_interact_done, - gboolean send_save_yourself_done) + gboolean send_interact_done, + gboolean send_save_yourself_done) { - g_warning ("Received XSMP %s message in state %s: client or server error", - message, EGG_SM_CLIENT_XSMP_STATE (xsmp)); + g_warning ("Received XSMP %s message in state %s: client or server error", + message, EGG_SM_CLIENT_XSMP_STATE (xsmp)); - /* Forget any pending SaveYourself plans we had */ - xsmp->waiting_to_save_myself = FALSE; - update_pending_events (xsmp); + /* Forget any pending SaveYourself plans we had */ + xsmp->waiting_to_save_myself = FALSE; + update_pending_events (xsmp); - if (send_interact_done) - SmcInteractDone (xsmp->connection, False); - if (send_save_yourself_done) - SmcSaveYourselfDone (xsmp->connection, True); + if (send_interact_done) + SmcInteractDone (xsmp->connection, False); + if (send_save_yourself_done) + SmcSaveYourselfDone (xsmp->connection, True); - xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE; + xsmp->state = send_save_yourself_done ? XSMP_STATE_SAVE_YOURSELF_DONE : XSMP_STATE_IDLE; } /* SM callbacks */ static void xsmp_save_yourself (SmcConn smc_conn, - SmPointer client_data, - int save_type, - Bool shutdown, - int interact_style, - Bool fast) + SmPointer client_data, + int save_type, + Bool shutdown, + int interact_style, + Bool fast) { - EggSMClientXSMP *xsmp = client_data; - gboolean wants_quit_requested; - - g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s", - save_type == SmSaveLocal ? "SmSaveLocal" : - save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth", - shutdown ? "Shutdown" : "!Shutdown", - interact_style == SmInteractStyleAny ? "SmInteractStyleAny" : - interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" : - "SmInteractStyleNone", fast ? "Fast" : "!Fast", - EGG_SM_CLIENT_XSMP_STATE (xsmp)); - - if (xsmp->state != XSMP_STATE_IDLE && - xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED) - { - fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE); - return; - } - - if (xsmp->waiting_to_set_initial_properties) - sm_client_xsmp_set_initial_properties (xsmp); - - /* If this is the initial SaveYourself, ignore it; we've already set - * properties and there's no reason to actually save state too. - */ - if (xsmp->expecting_initial_save_yourself) - { - xsmp->expecting_initial_save_yourself = FALSE; - - if (save_type == SmSaveLocal && - interact_style == SmInteractStyleNone && - !shutdown && !fast) + EggSMClientXSMP *xsmp = client_data; + gboolean wants_quit_requested; + + g_debug ("Received SaveYourself(%s, %s, %s, %s) in state %s", + save_type == SmSaveLocal ? "SmSaveLocal" : + save_type == SmSaveGlobal ? "SmSaveGlobal" : "SmSaveBoth", + shutdown ? "Shutdown" : "!Shutdown", + interact_style == SmInteractStyleAny ? "SmInteractStyleAny" : + interact_style == SmInteractStyleErrors ? "SmInteractStyleErrors" : + "SmInteractStyleNone", fast ? "Fast" : "!Fast", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + if (xsmp->state != XSMP_STATE_IDLE && + xsmp->state != XSMP_STATE_SHUTDOWN_CANCELLED) + { + fix_broken_state (xsmp, "SaveYourself", FALSE, TRUE); + return; + } + + if (xsmp->waiting_to_set_initial_properties) + sm_client_xsmp_set_initial_properties (xsmp); + + /* If this is the initial SaveYourself, ignore it; we've already set + * properties and there's no reason to actually save state too. + */ + if (xsmp->expecting_initial_save_yourself) { - g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself"); - SmcSaveYourselfDone (xsmp->connection, True); - /* As explained in the comment at the end of - * do_save_yourself(), SAVE_YOURSELF_DONE is the correct - * state here, not IDLE. - */ - xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; - return; + xsmp->expecting_initial_save_yourself = FALSE; + + if (save_type == SmSaveLocal && + interact_style == SmInteractStyleNone && + !shutdown && !fast) + { + g_debug ("Sending SaveYourselfDone(True) for initial SaveYourself"); + SmcSaveYourselfDone (xsmp->connection, True); + /* As explained in the comment at the end of + * do_save_yourself(), SAVE_YOURSELF_DONE is the correct + * state here, not IDLE. + */ + xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; + return; + } + else + g_warning ("First SaveYourself was not the expected one!"); } - else - g_warning ("First SaveYourself was not the expected one!"); - } - - /* Even ignoring the "fast" flag completely, there are still 18 - * different combinations of save_type, shutdown and interact_style. - * We interpret them as follows: - * - * Type Shutdown Interact Interpretation - * G F A/E/N do nothing (1) - * G T N do nothing (1)* - * G T A/E quit_requested (2) - * L/B F A/E/N save_state (3) - * L/B T N save_state (3)* - * L/B T A/E quit_requested, then save_state (4) - * - * 1. Do nothing, because the SM asked us to do something - * uninteresting (save open files, but then don't quit - * afterward) or rude (save open files without asking the user - * for confirmation). - * - * 2. Request interaction and then emit ::quit_requested. This - * perhaps isn't quite correct for the SmInteractStyleErrors - * case, but we don't care. - * - * 3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these - * rows essentially get demoted to SmSaveLocal, because their - * Global halves correspond to "do nothing". - * - * 4. Request interaction, emit ::quit_requested, and then emit - * ::save_state after interacting. This is the SmSaveBoth - * equivalent of #2, but we also promote SmSaveLocal shutdown - * SaveYourselfs to SmSaveBoth here, because we want to give - * the user a chance to save open files before quitting. - * - * (* It would be nice if we could do something useful when the - * session manager sends a SaveYourself with shutdown True and - * SmInteractStyleNone. But we can't, so we just pretend it didn't - * even tell us it was shutting down. The docs for ::quit mention - * that it might not always be preceded by ::quit_requested.) - */ - - /* As an optimization, we don't actually request interaction and - * emit ::quit_requested if the application isn't listening to the - * signal. - */ - wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE); - - xsmp->need_save_state = (save_type != SmSaveGlobal); - xsmp->need_quit_requested = (shutdown && wants_quit_requested && - interact_style != SmInteractStyleNone); - xsmp->interact_errors = (interact_style == SmInteractStyleErrors); - - xsmp->shutting_down = shutdown; - - do_save_yourself (xsmp); + + /* Even ignoring the "fast" flag completely, there are still 18 + * different combinations of save_type, shutdown and interact_style. + * We interpret them as follows: + * + * Type Shutdown Interact Interpretation + * G F A/E/N do nothing (1) + * G T N do nothing (1)* + * G T A/E quit_requested (2) + * L/B F A/E/N save_state (3) + * L/B T N save_state (3)* + * L/B T A/E quit_requested, then save_state (4) + * + * 1. Do nothing, because the SM asked us to do something + * uninteresting (save open files, but then don't quit + * afterward) or rude (save open files without asking the user + * for confirmation). + * + * 2. Request interaction and then emit ::quit_requested. This + * perhaps isn't quite correct for the SmInteractStyleErrors + * case, but we don't care. + * + * 3. Emit ::save_state. The SmSaveBoth SaveYourselfs in these + * rows essentially get demoted to SmSaveLocal, because their + * Global halves correspond to "do nothing". + * + * 4. Request interaction, emit ::quit_requested, and then emit + * ::save_state after interacting. This is the SmSaveBoth + * equivalent of #2, but we also promote SmSaveLocal shutdown + * SaveYourselfs to SmSaveBoth here, because we want to give + * the user a chance to save open files before quitting. + * + * (* It would be nice if we could do something useful when the + * session manager sends a SaveYourself with shutdown True and + * SmInteractStyleNone. But we can't, so we just pretend it didn't + * even tell us it was shutting down. The docs for ::quit mention + * that it might not always be preceded by ::quit_requested.) + */ + + /* As an optimization, we don't actually request interaction and + * emit ::quit_requested if the application isn't listening to the + * signal. + */ + wants_quit_requested = g_signal_has_handler_pending (xsmp, g_signal_lookup ("quit_requested", EGG_TYPE_SM_CLIENT), 0, FALSE); + + xsmp->need_save_state = (save_type != SmSaveGlobal); + xsmp->need_quit_requested = (shutdown && wants_quit_requested && + interact_style != SmInteractStyleNone); + xsmp->interact_errors = (interact_style == SmInteractStyleErrors); + + xsmp->shutting_down = shutdown; + + do_save_yourself (xsmp); } static void do_save_yourself (EggSMClientXSMP *xsmp) { - if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) - { - /* The SM cancelled a previous SaveYourself, but we haven't yet - * had a chance to tell the application, so we can't start - * processing this SaveYourself yet. - */ - xsmp->waiting_to_save_myself = TRUE; - update_pending_events (xsmp); - return; - } - - if (xsmp->need_quit_requested) - { - xsmp->state = XSMP_STATE_INTERACT_REQUEST; - - g_debug ("Sending InteractRequest(%s)", - xsmp->interact_errors ? "Error" : "Normal"); - SmcInteractRequest (xsmp->connection, - xsmp->interact_errors ? SmDialogError : SmDialogNormal, - xsmp_interact, - xsmp); - return; - } - - if (xsmp->need_save_state) - { - save_state (xsmp); - - /* Though unlikely, the client could have been disconnected - * while the application was saving its state. - */ - if (!xsmp->connection) - return; - } - - g_debug ("Sending SaveYourselfDone(True)"); - SmcSaveYourselfDone (xsmp->connection, True); - - /* The client state diagram in the XSMP spec says that after a - * non-shutdown SaveYourself, we go directly back to "idle". But - * everything else in both the XSMP spec and the libSM docs - * disagrees. - */ - xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; + if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) + { + /* The SM cancelled a previous SaveYourself, but we haven't yet + * had a chance to tell the application, so we can't start + * processing this SaveYourself yet. + */ + xsmp->waiting_to_save_myself = TRUE; + update_pending_events (xsmp); + return; + } + + if (xsmp->need_quit_requested) + { + xsmp->state = XSMP_STATE_INTERACT_REQUEST; + + g_debug ("Sending InteractRequest(%s)", + xsmp->interact_errors ? "Error" : "Normal"); + SmcInteractRequest (xsmp->connection, + xsmp->interact_errors ? SmDialogError : SmDialogNormal, + xsmp_interact, + xsmp); + return; + } + + if (xsmp->need_save_state) + { + save_state (xsmp); + + /* Though unlikely, the client could have been disconnected + * while the application was saving its state. + */ + if (!xsmp->connection) + return; + } + + g_debug ("Sending SaveYourselfDone(True)"); + SmcSaveYourselfDone (xsmp->connection, True); + + /* The client state diagram in the XSMP spec says that after a + * non-shutdown SaveYourself, we go directly back to "idle". But + * everything else in both the XSMP spec and the libSM docs + * disagrees. + */ + xsmp->state = XSMP_STATE_SAVE_YOURSELF_DONE; } static void save_state (EggSMClientXSMP *xsmp) { - GKeyFile *state_file; - char *state_file_path, *data; - EggDesktopFile *desktop_file; - GPtrArray *restart, *discard; - int offset, fd; - - /* We set xsmp->state before emitting save_state, but our caller is - * responsible for setting it back afterward. - */ - xsmp->state = XSMP_STATE_SAVE_YOURSELF; - - state_file = egg_sm_client_save_state ((EggSMClient *)xsmp); - if (!state_file) - { - restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL); - set_properties (xsmp, - ptrarray_prop (SmRestartCommand, restart), - NULL); - g_ptr_array_free (restart, TRUE); - - if (xsmp->set_discard_command) - { - discard = generate_command (xsmp->discard_command, NULL, NULL); - set_properties (xsmp, - ptrarray_prop (SmDiscardCommand, discard), - NULL); - g_ptr_array_free (discard, TRUE); - } - else - delete_properties (xsmp, SmDiscardCommand, NULL); - - return; - } - - desktop_file = egg_get_desktop_file (); - if (desktop_file) - { - GKeyFile *merged_file; - char *desktop_file_path; - - merged_file = g_key_file_new (); - desktop_file_path = - g_filename_from_uri (egg_desktop_file_get_source (desktop_file), - NULL, NULL); - if (desktop_file_path && - g_key_file_load_from_file (merged_file, desktop_file_path, - G_KEY_FILE_KEEP_COMMENTS | - G_KEY_FILE_KEEP_TRANSLATIONS, NULL)) + GKeyFile *state_file; + char *state_file_path, *data; + EggDesktopFile *desktop_file; + GPtrArray *restart, *discard; + int offset, fd; + + /* We set xsmp->state before emitting save_state, but our caller is + * responsible for setting it back afterward. + */ + xsmp->state = XSMP_STATE_SAVE_YOURSELF; + + state_file = egg_sm_client_save_state ((EggSMClient *)xsmp); + if (!state_file) { - guint g, k, i; - char **groups, **keys, *value, *exec; - - groups = g_key_file_get_groups (state_file, NULL); - for (g = 0; groups[g]; g++) - { - keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL); - for (k = 0; keys[k]; k++) + restart = generate_command (xsmp->restart_command, xsmp->client_id, NULL); + set_properties (xsmp, + ptrarray_prop (SmRestartCommand, restart), + NULL); + g_ptr_array_free (restart, TRUE); + + if (xsmp->set_discard_command) { - value = g_key_file_get_value (state_file, groups[g], - keys[k], NULL); - if (value) - { - g_key_file_set_value (merged_file, groups[g], - keys[k], value); - g_free (value); - } + discard = generate_command (xsmp->discard_command, NULL, NULL); + set_properties (xsmp, + ptrarray_prop (SmDiscardCommand, discard), + NULL); + g_ptr_array_free (discard, TRUE); } - g_strfreev (keys); - } - g_strfreev (groups); - - g_key_file_free (state_file); - state_file = merged_file; - - /* Update Exec key using "--sm-client-state-file %k" */ - restart = generate_command (xsmp->restart_command, - NULL, "%k"); - for (i = 0; i < restart->len; i++) - restart->pdata[i] = g_shell_quote (restart->pdata[i]); - g_ptr_array_add (restart, NULL); - exec = g_strjoinv (" ", (char **)restart->pdata); - g_strfreev ((char **)restart->pdata); - g_ptr_array_free (restart, FALSE); - - g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP, - EGG_DESKTOP_FILE_KEY_EXEC, - exec); - g_free (exec); + else + delete_properties (xsmp, SmDiscardCommand, NULL); + + return; } - else - desktop_file = NULL; - - g_free (desktop_file_path); - } - - /* Now write state_file to disk. (We can't use mktemp(), because - * that requires the filename to end with "XXXXXX", and we want - * it to end with ".desktop".) - */ - - data = g_key_file_to_data (state_file, NULL, NULL); - g_key_file_free (state_file); - - offset = 0; - while (1) - { - state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s", - g_get_user_config_dir (), - G_DIR_SEPARATOR, G_DIR_SEPARATOR, - g_get_prgname (), - (long)time (NULL) + offset, - desktop_file ? "desktop" : "state"); - - fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644); - if (fd == -1) + + desktop_file = egg_get_desktop_file (); + if (desktop_file) { - if (errno == EEXIST) - { - offset++; - g_free (state_file_path); - continue; - } - else if (errno == ENOTDIR || errno == ENOENT) - { - char *sep = strrchr (state_file_path, G_DIR_SEPARATOR); - - *sep = '\0'; - if (g_mkdir_with_parents (state_file_path, 0755) != 0) + GKeyFile *merged_file; + char *desktop_file_path; + + merged_file = g_key_file_new (); + desktop_file_path = + g_filename_from_uri (egg_desktop_file_get_source (desktop_file), + NULL, NULL); + if (desktop_file_path && + g_key_file_load_from_file (merged_file, desktop_file_path, + G_KEY_FILE_KEEP_COMMENTS | + G_KEY_FILE_KEEP_TRANSLATIONS, NULL)) { - g_warning ("Could not create directory '%s'", - state_file_path); - g_free (state_file_path); - state_file_path = NULL; - break; + guint g, k, i; + char **groups, **keys, *value, *exec; + + groups = g_key_file_get_groups (state_file, NULL); + for (g = 0; groups[g]; g++) + { + keys = g_key_file_get_keys (state_file, groups[g], NULL, NULL); + for (k = 0; keys[k]; k++) + { + value = g_key_file_get_value (state_file, groups[g], + keys[k], NULL); + if (value) + { + g_key_file_set_value (merged_file, groups[g], + keys[k], value); + g_free (value); + } + } + g_strfreev (keys); + } + g_strfreev (groups); + + g_key_file_free (state_file); + state_file = merged_file; + + /* Update Exec key using "--sm-client-state-file %k" */ + restart = generate_command (xsmp->restart_command, + NULL, "%k"); + for (i = 0; i < restart->len; i++) + restart->pdata[i] = g_shell_quote (restart->pdata[i]); + g_ptr_array_add (restart, NULL); + exec = g_strjoinv (" ", (char **)restart->pdata); + g_strfreev ((char **)restart->pdata); + g_ptr_array_free (restart, FALSE); + + g_key_file_set_string (state_file, EGG_DESKTOP_FILE_GROUP, + EGG_DESKTOP_FILE_KEY_EXEC, + exec); + g_free (exec); } + else + desktop_file = NULL; + + g_free (desktop_file_path); + } - continue; - } + /* Now write state_file to disk. (We can't use mktemp(), because + * that requires the filename to end with "XXXXXX", and we want + * it to end with ".desktop".) + */ - g_warning ("Could not create file '%s': %s", - state_file_path, g_strerror (errno)); - g_free (state_file_path); - state_file_path = NULL; - break; + data = g_key_file_to_data (state_file, NULL, NULL); + g_key_file_free (state_file); + + offset = 0; + while (1) + { + state_file_path = g_strdup_printf ("%s%csession-state%c%s-%ld.%s", + g_get_user_config_dir (), + G_DIR_SEPARATOR, G_DIR_SEPARATOR, + g_get_prgname (), + (long)time (NULL) + offset, + desktop_file ? "desktop" : "state"); + + fd = open (state_file_path, O_WRONLY | O_CREAT | O_EXCL, 0644); + if (fd == -1) + { + if (errno == EEXIST) + { + offset++; + g_free (state_file_path); + continue; + } + else if (errno == ENOTDIR || errno == ENOENT) + { + char *sep = strrchr (state_file_path, G_DIR_SEPARATOR); + + *sep = '\0'; + if (g_mkdir_with_parents (state_file_path, 0755) != 0) + { + g_warning ("Could not create directory '%s'", + state_file_path); + g_free (state_file_path); + state_file_path = NULL; + break; + } + + continue; + } + + g_warning ("Could not create file '%s': %s", + state_file_path, g_strerror (errno)); + g_free (state_file_path); + state_file_path = NULL; + break; + } + + close (fd); + g_file_set_contents (state_file_path, data, -1, NULL); + break; } + g_free (data); + + restart = generate_command (xsmp->restart_command, xsmp->client_id, + state_file_path); + set_properties (xsmp, + ptrarray_prop (SmRestartCommand, restart), + NULL); + g_ptr_array_free (restart, TRUE); - close (fd); - g_file_set_contents (state_file_path, data, -1, NULL); - break; - } - g_free (data); - - restart = generate_command (xsmp->restart_command, xsmp->client_id, - state_file_path); - set_properties (xsmp, - ptrarray_prop (SmRestartCommand, restart), - NULL); - g_ptr_array_free (restart, TRUE); - - if (state_file_path) - { - set_properties (xsmp, - array_prop (SmDiscardCommand, - "/bin/rm", "-rf", state_file_path, - NULL), - NULL); - g_free (state_file_path); - } + if (state_file_path) + { + set_properties (xsmp, + array_prop (SmDiscardCommand, + "/bin/rm", "-rf", state_file_path, + NULL), + NULL); + g_free (state_file_path); + } } static void xsmp_interact (SmcConn smc_conn, - SmPointer client_data) + SmPointer client_data) { - EggSMClientXSMP *xsmp = client_data; - EggSMClient *client = client_data; + EggSMClientXSMP *xsmp = client_data; + EggSMClient *client = client_data; - g_debug ("Received Interact message in state %s", - EGG_SM_CLIENT_XSMP_STATE (xsmp)); + g_debug ("Received Interact message in state %s", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); - if (xsmp->state != XSMP_STATE_INTERACT_REQUEST) - { - fix_broken_state (xsmp, "Interact", TRUE, TRUE); - return; - } + if (xsmp->state != XSMP_STATE_INTERACT_REQUEST) + { + fix_broken_state (xsmp, "Interact", TRUE, TRUE); + return; + } - xsmp->state = XSMP_STATE_INTERACT; - egg_sm_client_quit_requested (client); + xsmp->state = XSMP_STATE_INTERACT; + egg_sm_client_quit_requested (client); } static void xsmp_die (SmcConn smc_conn, - SmPointer client_data) + SmPointer client_data) { - EggSMClientXSMP *xsmp = client_data; - EggSMClient *client = client_data; + EggSMClientXSMP *xsmp = client_data; + EggSMClient *client = client_data; - g_debug ("Received Die message in state %s", - EGG_SM_CLIENT_XSMP_STATE (xsmp)); + g_debug ("Received Die message in state %s", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); - sm_client_xsmp_disconnect (xsmp); - egg_sm_client_quit (client); + sm_client_xsmp_disconnect (xsmp); + egg_sm_client_quit (client); } static void xsmp_save_complete (SmcConn smc_conn, - SmPointer client_data) + SmPointer client_data) { - EggSMClientXSMP *xsmp = client_data; + EggSMClientXSMP *xsmp = client_data; - g_debug ("Received SaveComplete message in state %s", - EGG_SM_CLIENT_XSMP_STATE (xsmp)); + g_debug ("Received SaveComplete message in state %s", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); - if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE) - xsmp->state = XSMP_STATE_IDLE; - else - fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE); + if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE) + xsmp->state = XSMP_STATE_IDLE; + else + fix_broken_state (xsmp, "SaveComplete", FALSE, FALSE); } static void xsmp_shutdown_cancelled (SmcConn smc_conn, - SmPointer client_data) + SmPointer client_data) { - EggSMClientXSMP *xsmp = client_data; - EggSMClient *client = client_data; - - g_debug ("Received ShutdownCancelled message in state %s", - EGG_SM_CLIENT_XSMP_STATE (xsmp)); - - xsmp->shutting_down = FALSE; - - if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE) - { - /* We've finished interacting and now the SM has agreed to - * cancel the shutdown. - */ - xsmp->state = XSMP_STATE_IDLE; - egg_sm_client_quit_cancelled (client); - } - else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) - { - /* Hm... ok, so we got a shutdown SaveYourself, which got - * cancelled, but the application was still interacting, so we - * didn't tell it yet, and then *another* SaveYourself arrived, - * which we must still be waiting to tell the app about, except - * that now that SaveYourself has been cancelled too! Dizzy yet? - */ - xsmp->waiting_to_save_myself = FALSE; - update_pending_events (xsmp); - } - else - { - g_debug ("Sending SaveYourselfDone(False)"); - SmcSaveYourselfDone (xsmp->connection, False); - - if (xsmp->state == XSMP_STATE_INTERACT) + EggSMClientXSMP *xsmp = client_data; + EggSMClient *client = client_data; + + g_debug ("Received ShutdownCancelled message in state %s", + EGG_SM_CLIENT_XSMP_STATE (xsmp)); + + xsmp->shutting_down = FALSE; + + if (xsmp->state == XSMP_STATE_SAVE_YOURSELF_DONE) + { + /* We've finished interacting and now the SM has agreed to + * cancel the shutdown. + */ + xsmp->state = XSMP_STATE_IDLE; + egg_sm_client_quit_cancelled (client); + } + else if (xsmp->state == XSMP_STATE_SHUTDOWN_CANCELLED) { - /* The application is currently interacting, so we can't - * tell it about the cancellation yet; we will wait until - * after it calls egg_sm_client_will_quit(). - */ - xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED; + /* Hm... ok, so we got a shutdown SaveYourself, which got + * cancelled, but the application was still interacting, so we + * didn't tell it yet, and then *another* SaveYourself arrived, + * which we must still be waiting to tell the app about, except + * that now that SaveYourself has been cancelled too! Dizzy yet? + */ + xsmp->waiting_to_save_myself = FALSE; + update_pending_events (xsmp); } - else + else { - /* The shutdown was cancelled before the application got a - * chance to interact. - */ - xsmp->state = XSMP_STATE_IDLE; + g_debug ("Sending SaveYourselfDone(False)"); + SmcSaveYourselfDone (xsmp->connection, False); + + if (xsmp->state == XSMP_STATE_INTERACT) + { + /* The application is currently interacting, so we can't + * tell it about the cancellation yet; we will wait until + * after it calls egg_sm_client_will_quit(). + */ + xsmp->state = XSMP_STATE_SHUTDOWN_CANCELLED; + } + else + { + /* The shutdown was cancelled before the application got a + * chance to interact. + */ + xsmp->state = XSMP_STATE_IDLE; + } } - } } /* Utilities */ @@ -1077,30 +1078,30 @@ xsmp_shutdown_cancelled (SmcConn smc_conn, */ static GPtrArray * generate_command (char **argv, const char *client_id, - const char *state_file) + const char *state_file) { - GPtrArray *cmd; - int i; + GPtrArray *cmd; + int i; - cmd = g_ptr_array_new (); - g_ptr_array_add (cmd, argv[0]); + cmd = g_ptr_array_new (); + g_ptr_array_add (cmd, argv[0]); - if (client_id) - { - g_ptr_array_add (cmd, (char *)"--sm-client-id"); - g_ptr_array_add (cmd, (char *)client_id); - } + if (client_id) + { + g_ptr_array_add (cmd, (char *)"--sm-client-id"); + g_ptr_array_add (cmd, (char *)client_id); + } - if (state_file) - { - g_ptr_array_add (cmd, (char *)"--sm-client-state-file"); - g_ptr_array_add (cmd, (char *)state_file); - } + if (state_file) + { + g_ptr_array_add (cmd, (char *)"--sm-client-state-file"); + g_ptr_array_add (cmd, (char *)state_file); + } - for (i = 1; argv[i]; i++) - g_ptr_array_add (cmd, argv[i]); + for (i = 1; argv[i]; i++) + g_ptr_array_add (cmd, argv[i]); - return cmd; + return cmd; } /* Takes a NULL-terminated list of SmProp * values, created by @@ -1110,55 +1111,55 @@ generate_command (char **argv, const char *client_id, static void set_properties (EggSMClientXSMP *xsmp, ...) { - GPtrArray *props; - SmProp *prop; - va_list ap; - guint i; - - props = g_ptr_array_new (); - - va_start (ap, xsmp); - while ((prop = va_arg (ap, SmProp *))) - g_ptr_array_add (props, prop); - va_end (ap); - - if (xsmp->connection) - { - SmcSetProperties (xsmp->connection, props->len, - (SmProp **)props->pdata); - } - - for (i = 0; i < props->len; i++) - { - prop = props->pdata[i]; - g_free (prop->vals); - g_free (prop); - } - g_ptr_array_free (props, TRUE); + GPtrArray *props; + SmProp *prop; + va_list ap; + guint i; + + props = g_ptr_array_new (); + + va_start (ap, xsmp); + while ((prop = va_arg (ap, SmProp *))) + g_ptr_array_add (props, prop); + va_end (ap); + + if (xsmp->connection) + { + SmcSetProperties (xsmp->connection, props->len, + (SmProp **)props->pdata); + } + + for (i = 0; i < props->len; i++) + { + prop = props->pdata[i]; + g_free (prop->vals); + g_free (prop); + } + g_ptr_array_free (props, TRUE); } /* Takes a NULL-terminated list of property names and deletes them. */ static void delete_properties (EggSMClientXSMP *xsmp, ...) { - GPtrArray *props; - char *prop; - va_list ap; + GPtrArray *props; + char *prop; + va_list ap; - if (!xsmp->connection) - return; + if (!xsmp->connection) + return; - props = g_ptr_array_new (); + props = g_ptr_array_new (); - va_start (ap, xsmp); - while ((prop = va_arg (ap, char *))) - g_ptr_array_add (props, prop); - va_end (ap); + va_start (ap, xsmp); + while ((prop = va_arg (ap, char *))) + g_ptr_array_add (props, prop); + va_end (ap); - SmcDeleteProperties (xsmp->connection, props->len, - (char **)props->pdata); + SmcDeleteProperties (xsmp->connection, props->len, + (char **)props->pdata); - g_ptr_array_free (props, TRUE); + g_ptr_array_free (props, TRUE); } /* Takes an array of strings and creates a LISTofARRAY8 property. The @@ -1166,34 +1167,34 @@ delete_properties (EggSMClientXSMP *xsmp, ...) * until you're done with the SmProp. */ static SmProp * -array_prop (const char *name, ...) +array_prop (const char *name, ...) { - SmProp *prop; - SmPropValue pv; - GArray *vals; - char *value; - va_list ap; + SmProp *prop; + SmPropValue pv; + GArray *vals; + char *value; + va_list ap; - prop = g_new (SmProp, 1); - prop->name = (char *)name; - prop->type = (char *)SmLISTofARRAY8; + prop = g_new (SmProp, 1); + prop->name = (char *)name; + prop->type = (char *)SmLISTofARRAY8; - vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); + vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); - va_start (ap, name); - while ((value = va_arg (ap, char *))) - { - pv.length = strlen (value); - pv.value = value; - g_array_append_val (vals, pv); - } + va_start (ap, name); + while ((value = va_arg (ap, char *))) + { + pv.length = strlen (value); + pv.value = value; + g_array_append_val (vals, pv); + } - prop->num_vals = vals->len; - prop->vals = (SmPropValue *)vals->data; + prop->num_vals = vals->len; + prop->vals = (SmPropValue *)vals->data; - g_array_free (vals, FALSE); + g_array_free (vals, FALSE); - return prop; + return prop; } /* Takes a GPtrArray of strings and creates a LISTofARRAY8 property. @@ -1203,30 +1204,30 @@ array_prop (const char *name, ...) static SmProp * ptrarray_prop (const char *name, GPtrArray *values) { - SmProp *prop; - SmPropValue pv; - GArray *vals; - guint i; + SmProp *prop; + SmPropValue pv; + GArray *vals; + guint i; - prop = g_new (SmProp, 1); - prop->name = (char *)name; - prop->type = (char *)SmLISTofARRAY8; + prop = g_new (SmProp, 1); + prop->name = (char *)name; + prop->type = (char *)SmLISTofARRAY8; - vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); + vals = g_array_new (FALSE, FALSE, sizeof (SmPropValue)); - for (i = 0; i < values->len; i++) - { - pv.length = strlen (values->pdata[i]); - pv.value = values->pdata[i]; - g_array_append_val (vals, pv); - } + for (i = 0; i < values->len; i++) + { + pv.length = strlen (values->pdata[i]); + pv.value = values->pdata[i]; + g_array_append_val (vals, pv); + } - prop->num_vals = vals->len; - prop->vals = (SmPropValue *)vals->data; + prop->num_vals = vals->len; + prop->vals = (SmPropValue *)vals->data; - g_array_free (vals, FALSE); + g_array_free (vals, FALSE); - return prop; + return prop; } /* Takes a string and creates an ARRAY8 property. The string is @@ -1236,46 +1237,46 @@ ptrarray_prop (const char *name, GPtrArray *values) static SmProp * string_prop (const char *name, const char *value) { - SmProp *prop; + SmProp *prop; - prop = g_new (SmProp, 1); - prop->name = (char *)name; - prop->type = (char *)SmARRAY8; + prop = g_new (SmProp, 1); + prop->name = (char *)name; + prop->type = (char *)SmARRAY8; - prop->num_vals = 1; - prop->vals = g_new (SmPropValue, 1); + prop->num_vals = 1; + prop->vals = g_new (SmPropValue, 1); - prop->vals[0].length = strlen (value); - prop->vals[0].value = (char *)value; + prop->vals[0].length = strlen (value); + prop->vals[0].value = (char *)value; - return prop; + return prop; } /* Takes a char and creates a CARD8 property. */ static SmProp * card8_prop (const char *name, unsigned char value) { - SmProp *prop; - char *card8val; + SmProp *prop; + char *card8val; - /* To avoid having to allocate and free prop->vals[0], we cheat and - * make vals a 2-element-long array and then use the second element - * to store value. - */ + /* To avoid having to allocate and free prop->vals[0], we cheat and + * make vals a 2-element-long array and then use the second element + * to store value. + */ - prop = g_new (SmProp, 1); - prop->name = (char *)name; - prop->type = (char *)SmCARD8; + prop = g_new (SmProp, 1); + prop->name = (char *)name; + prop->type = (char *)SmCARD8; - prop->num_vals = 1; - prop->vals = g_new (SmPropValue, 2); - card8val = (char *)(&prop->vals[1]); - card8val[0] = value; + prop->num_vals = 1; + prop->vals = g_new (SmPropValue, 2); + card8val = (char *)(&prop->vals[1]); + card8val[0] = value; - prop->vals[0].length = 1; - prop->vals[0].value = card8val; + prop->vals[0].length = 1; + prop->vals[0].value = card8val; - return prop; + return prop; } /* ICE code. This makes no effort to play nice with anyone else trying @@ -1292,105 +1293,105 @@ card8_prop (const char *name, unsigned char value) #include <fcntl.h> static void ice_error_handler (IceConn ice_conn, - Bool swap, - int offending_minor_opcode, - unsigned long offending_sequence, - int error_class, - int severity, - IcePointer values); + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence, + int error_class, + int severity, + IcePointer values); static void ice_io_error_handler (IceConn ice_conn); static void ice_connection_watch (IceConn ice_conn, - IcePointer client_data, - Bool opening, - IcePointer *watch_data); + IcePointer client_data, + Bool opening, + IcePointer *watch_data); static void ice_init (void) { - IceSetIOErrorHandler (ice_io_error_handler); - IceSetErrorHandler (ice_error_handler); - IceAddConnectionWatch (ice_connection_watch, NULL); + IceSetIOErrorHandler (ice_io_error_handler); + IceSetErrorHandler (ice_error_handler); + IceAddConnectionWatch (ice_connection_watch, NULL); } static gboolean process_ice_messages (IceConn ice_conn) { - IceProcessMessagesStatus status; + IceProcessMessagesStatus status; - gdk_threads_enter (); - status = IceProcessMessages (ice_conn, NULL, NULL); - gdk_threads_leave (); + gdk_threads_enter (); + status = IceProcessMessages (ice_conn, NULL, NULL); + gdk_threads_leave (); - switch (status) - { - case IceProcessMessagesSuccess: - return TRUE; + switch (status) + { + case IceProcessMessagesSuccess: + return TRUE; - case IceProcessMessagesIOError: - sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn)); - return FALSE; + case IceProcessMessagesIOError: + sm_client_xsmp_disconnect (IceGetConnectionContext (ice_conn)); + return FALSE; - case IceProcessMessagesConnectionClosed: - return FALSE; + case IceProcessMessagesConnectionClosed: + return FALSE; - default: - g_assert_not_reached (); - } + default: + g_assert_not_reached (); + } } static gboolean ice_iochannel_watch (GIOChannel *channel, - GIOCondition condition, - gpointer client_data) + GIOCondition condition, + gpointer client_data) { - return process_ice_messages (client_data); + return process_ice_messages (client_data); } static void ice_connection_watch (IceConn ice_conn, - IcePointer client_data, - Bool opening, - IcePointer *watch_data) + IcePointer client_data, + Bool opening, + IcePointer *watch_data) { - guint watch_id; - - if (opening) - { - GIOChannel *channel; - int fd = IceConnectionNumber (ice_conn); - - fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC); - channel = g_io_channel_unix_new (fd); - watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR, - ice_iochannel_watch, ice_conn); - g_io_channel_unref (channel); - - *watch_data = GUINT_TO_POINTER (watch_id); - } - else - { - watch_id = GPOINTER_TO_UINT (*watch_data); - g_source_remove (watch_id); - } + guint watch_id; + + if (opening) + { + GIOChannel *channel; + int fd = IceConnectionNumber (ice_conn); + + fcntl (fd, F_SETFD, fcntl (fd, F_GETFD, 0) | FD_CLOEXEC); + channel = g_io_channel_unix_new (fd); + watch_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR, + ice_iochannel_watch, ice_conn); + g_io_channel_unref (channel); + + *watch_data = GUINT_TO_POINTER (watch_id); + } + else + { + watch_id = GPOINTER_TO_UINT (*watch_data); + g_source_remove (watch_id); + } } static void ice_error_handler (IceConn ice_conn, - Bool swap, - int offending_minor_opcode, - unsigned long offending_sequence, - int error_class, - int severity, - IcePointer values) + Bool swap, + int offending_minor_opcode, + unsigned long offending_sequence, + int error_class, + int severity, + IcePointer values) { - /* Do nothing */ -} + /* Do nothing */ +} static void ice_io_error_handler (IceConn ice_conn) { - /* Do nothing */ -} + /* Do nothing */ +} static void smc_error_handler (SmcConn smc_conn, @@ -1401,5 +1402,5 @@ smc_error_handler (SmcConn smc_conn, int severity, SmPointer values) { - /* Do nothing */ + /* Do nothing */ } |