diff options
Diffstat (limited to 'src/session.c')
-rw-r--r-- | src/session.c | 456 |
1 files changed, 453 insertions, 3 deletions
diff --git a/src/session.c b/src/session.c index 6986d5f..414b598 100644 --- a/src/session.c +++ b/src/session.c @@ -1,7 +1,8 @@ /* Metacity Session Management */ /* - * Copyright (C) 2001 Havoc Pennington + * Copyright (C) 2001 Havoc Pennington (some code in here from + * libgnomeui, (C) Tom Tromey, Carsten Schaar) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -16,12 +17,461 @@ * 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. - */ + * 02111-1307, USA. */ #include "session.h" +#ifndef HAVE_SM +void +meta_session_init (const char *previous_id) +{ + meta_verbose ("Compiled without session management support\n"); +} +#else /* HAVE_SM */ +#include <X11/ICE/ICElib.h> +#include <X11/SM/SMlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <glib.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include "main.h" +#include "util.h" +static void ice_io_error_handler (IceConn connection); +static void new_ice_connection (IceConn connection, IcePointer client_data, + Bool opening, IcePointer *watch_data); +/* 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; + + status = IceProcessMessages (connection, NULL, NULL); + + if (status == IceProcessMessagesIOError) + { +#if 0 + IcePointer context = IceGetConnectionContext (connection); +#endif + + /* We were disconnected */ + IceSetShutdownNegotiation (connection, False); + IceCloseConnection (connection); + } + + 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 gnome_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 (gnome_ice_installed_handler) + (*gnome_ice_installed_handler) (connection); +} + +static void +ice_init (void) +{ + static gboolean ice_initted = FALSE; + + if (! ice_initted) + { + IceIOErrorHandler default_handler; + + gnome_ice_installed_handler = IceSetIOErrorHandler (NULL); + default_handler = IceSetIOErrorHandler (ice_io_error_handler); + + if (gnome_ice_installed_handler == default_handler) + gnome_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_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 gchar *client_id = NULL; +static gpointer session_connection = NULL; +static ClientState current_state = STATE_DISCONNECTED; + +void +meta_session_init (const char *previous_id) +{ + /* Some code here from twm */ + char buf[256]; + unsigned long mask; + SmcCallbacks callbacks; + + meta_verbose ("Initializing session with session ID '%s'\n", + previous_id ? previous_id : "(none)"); + + 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, + previous_id, + &client_id, + 255, buf); + + if (session_connection == NULL) + { + meta_warning ("Failed to open connection to session manager: %s\n", buf); + return; + } + else + meta_verbose ("Obtained session ID '%s'\n", client_id); + + if (previous_id && strcmp (previous_id, client_id) == 0) + current_state = STATE_IDLE; + else + current_state = STATE_REGISTERING; + + { + SmProp prop1, prop2, prop3, prop4, prop5, *props[5]; + SmPropValue prop1val, prop2val, prop3val, prop4val, prop5val; + char pid[32]; + char hint = SmRestartIfRunning; + + 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 = 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 = g_get_home_dir (); + prop5val.length = strlen (prop5val.value); + + props[0] = &prop1; + props[1] = &prop2; + props[2] = &prop3; + props[3] = &prop4; + props[4] = &prop5; + + SmcSetProperties (session_connection, 5, props); + } + + set_clone_restart_commands (); +} + +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) +{ + 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; + } + + if (current_state == STATE_SAVING_PHASE_1 || + current_state == STATE_SAVING_PHASE_2) + { + 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; + + shutdown = GPOINTER_TO_INT (client_data); + + current_state = STATE_SAVING_PHASE_2; + + 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; + + successful = TRUE; + + /* The first SaveYourself after registering for the first time + * is a special case (SM specs 7.2). + * + * This SaveYourself seems to be included in the protocol to + * ask the client to specify its initial SmProperties since + * there is little point saving a copy of the initial state. + * + * A bug in xsm means that it does not send us a SaveComplete + * in response to this initial SaveYourself. Therefore, we + * must not set a grab because it would never be released. + * Indeed, even telling the app that this SaveYourself has + * arrived is hazardous as the app may take its own steps + * to freeze its WM state while waiting for the SaveComplete. + * + * Fortunately, we have already set the SmProperties during + * gnome_client_connect so there is little lost in simply + * returning immediately. + * + * Apps which really want to save their initial states can + * do so safely using gnome_client_save_yourself_request. + */ + + 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; + } + } + + current_state = STATE_SAVING_PHASE_1; + + set_clone_restart_commands (); + + save_yourself_possibly_done (shutdown, successful); +} + + +static void +die_callback (SmcConn smc_conn, SmPointer client_data) +{ + meta_verbose ("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 */ +} + +static void +shutdown_cancelled_callback (SmcConn smc_conn, SmPointer client_data) +{ + /* nothing */ +} + +static void +interact_callback (SmcConn smc_conn, SmPointer client_data) +{ + /* nothing */ +} + +static void +set_clone_restart_commands (void) +{ + char *restartv[10]; + char *clonev[10]; + int i; + SmProp prop1, prop2, *props[2]; + + /* Restart (use same client ID) */ + + prop1.name = SmRestartCommand; + prop1.type = SmLISTofARRAY8; + + 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; + + props[0] = &prop1; + props[1] = &prop2; + + SmcSetProperties (session_connection, 2, props); +} + +#endif /* HAVE_SM */ |