From eaef567cabc5c0dda9706faa0e17e20909131ade Mon Sep 17 00:00:00 2001 From: simpoir Date: Fri, 30 Jan 2009 04:14:48 +0000 Subject: ajout initial du tree jhbuild [jhbuild base] --- (limited to 'src/sugar/gsm-session.c') diff --git a/src/sugar/gsm-session.c b/src/sugar/gsm-session.c new file mode 100644 index 0000000..0fe2fb5 --- /dev/null +++ b/src/sugar/gsm-session.c @@ -0,0 +1,497 @@ +/* session.c + * Copyright (C) 2007 Novell, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include "gsm-session.h" +#include "gsm-app.h" +#include "gsm-xsmp.h" + +GsmSession *global_session; + +static void initiate_shutdown (GsmSession *session); + +static void session_shutdown (GsmSession *session); + +static void client_saved_state (GsmClient *client, + gpointer data); +static void client_request_phase2 (GsmClient *client, + gpointer data); +static void client_request_interaction (GsmClient *client, + gpointer data); +static void client_interaction_done (GsmClient *client, + gboolean cancel_shutdown, + gpointer data); +static void client_save_yourself_done (GsmClient *client, + gpointer data); +static void client_disconnected (GsmClient *client, + gpointer data); + +struct _GsmSession { + GObject parent; + + char *name; + + /* Current status */ + GsmSessionPhase phase; + guint timeout; + GSList *pending_apps; + + /* SM clients */ + GSList *clients; + + /* When shutdown starts, all clients are put into shutdown_clients. + * If they request phase2, they are moved from shutdown_clients to + * phase2_clients. If they request interaction, they are appended + * to interact_clients (the first client in interact_clients is + * the one currently interacting). If they report that they're done, + * they're removed from shutdown_clients/phase2_clients. + * + * Once shutdown_clients is empty, phase2 starts. Once phase2_clients + * is empty, shutdown is complete. + */ + GSList *shutdown_clients; + GSList *interact_clients; + GSList *phase2_clients; + + /* List of clients which were disconnected due to disabled condition + * and shouldn't be automatically restarted */ + GSList *condition_clients; +}; + +struct _GsmSessionClass +{ + GObjectClass parent_class; + + void (* shutdown_completed) (GsmSession *client); +}; + +enum { + SHUTDOWN_COMPLETED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (GsmSession, gsm_session, G_TYPE_OBJECT) + +#define GSM_SESSION_PHASE_TIMEOUT 10 /* seconds */ + +void +gsm_session_init (GsmSession *session) +{ + session->name = NULL; + session->clients = NULL; + session->condition_clients = NULL; +} + +static void +gsm_session_class_init (GsmSessionClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + signals[SHUTDOWN_COMPLETED] = + g_signal_new ("shutdown_completed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmSessionClass, shutdown_completed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} + + +/** + * gsm_session_set_name: + * @session: session instance + * @name: name of the session + * + * Sets the name of a running session. + **/ +void +gsm_session_set_name (GsmSession *session, const char *name) +{ + if (session->name) + g_free (session->name); + + session->name = g_strdup (name); +} + +static void start_phase (GsmSession *session); + +static void +end_phase (GsmSession *session) +{ + g_slist_free (session->pending_apps); + session->pending_apps = NULL; + + g_debug ("ending phase %d\n", session->phase); + + session->phase++; + + if (session->phase < GSM_SESSION_PHASE_RUNNING) + start_phase (session); +} + +static void +app_registered (GsmApp *app, gpointer data) +{ + GsmSession *session = data; + + session->pending_apps = g_slist_remove (session->pending_apps, app); + g_signal_handlers_disconnect_by_func (app, app_registered, session); + + if (!session->pending_apps) + { + if (session->timeout > 0) + { + g_source_remove (session->timeout); + session->timeout = 0; + } + + end_phase (session); + } +} + +static gboolean +phase_timeout (gpointer data) +{ + GsmSession *session = data; + GSList *a; + + session->timeout = 0; + + for (a = session->pending_apps; a; a = a->next) + { + g_warning ("Application '%s' failed to register before timeout", + gsm_app_get_basename (a->data)); + g_signal_handlers_disconnect_by_func (a->data, app_registered, session); + + /* FIXME: what if the app was filling in a required slot? */ + } + + end_phase (session); + return FALSE; +} + +static void +start_phase (GsmSession *session) +{ + g_debug ("starting phase %d\n", session->phase); + + g_slist_free (session->pending_apps); + session->pending_apps = NULL; + + if (session->pending_apps) + { + if (session->phase < GSM_SESSION_PHASE_APPLICATION) + { + session->timeout = g_timeout_add_seconds (GSM_SESSION_PHASE_TIMEOUT, + phase_timeout, session); + } + } + else + end_phase (session); +} + +void +gsm_session_start (GsmSession *session) +{ + session->phase = GSM_SESSION_PHASE_INITIALIZATION; + + start_phase (session); +} + +GsmSessionPhase +gsm_session_get_phase (GsmSession *session) +{ + return session->phase; +} + +char * +gsm_session_register_client (GsmSession *session, + GsmClient *client, + const char *id) +{ + GSList *a; + char *client_id = NULL; + + /* If we're shutting down, we don't accept any new session + clients. */ + if (session->phase == GSM_SESSION_PHASE_SHUTDOWN) + return FALSE; + + if (id == NULL) + client_id = gsm_xsmp_generate_client_id (); + else + { + for (a = session->clients; a; a = a->next) + { + GsmClient *client = GSM_CLIENT (a->data); + + /* We can't have two clients with the same id. */ + if (!strcmp (id, gsm_client_get_client_id (client))) + { + return NULL; + } + } + + client_id = g_strdup (id); + } + + g_debug ("Adding new client %s to session", id); + + g_signal_connect (client, "saved_state", + G_CALLBACK (client_saved_state), session); + g_signal_connect (client, "request_phase2", + G_CALLBACK (client_request_phase2), session); + g_signal_connect (client, "request_interaction", + G_CALLBACK (client_request_interaction), session); + g_signal_connect (client, "interaction_done", + G_CALLBACK (client_interaction_done), session); + g_signal_connect (client, "save_yourself_done", + G_CALLBACK (client_save_yourself_done), session); + g_signal_connect (client, "disconnected", + G_CALLBACK (client_disconnected), session); + + session->clients = g_slist_prepend (session->clients, client); + + /* If it's a brand new client id, we just accept the client*/ + if (id == NULL) + return client_id; + + /* If we're starting up the session, try to match the new client + * with one pending apps for the current phase. If not, try to match + * with any of the autostarted apps. */ + if (session->phase < GSM_SESSION_PHASE_APPLICATION) + a = session->pending_apps; + + for (; a; a = a->next) + { + GsmApp *app = GSM_APP (a->data); + + if (!strcmp (client_id, app->client_id)) + { + gsm_app_registered (app); + return client_id; + } + } + + g_free (client_id); + + return NULL; +} + +static void +client_saved_state (GsmClient *client, gpointer data) +{ + /* FIXME */ +} + +void +gsm_session_initiate_shutdown (GsmSession *session) +{ + if (session->phase == GSM_SESSION_PHASE_SHUTDOWN) + { + /* Already shutting down, nothing more to do */ + return; + } + + initiate_shutdown (session); +} + +static void +session_shutdown_phase2 (GsmSession *session) +{ + GSList *cl; + + for (cl = session->phase2_clients; cl; cl = cl->next) + gsm_client_save_yourself_phase2 (cl->data); +} + +static void +session_cancel_shutdown (GsmSession *session) +{ + GSList *cl; + + session->phase = GSM_SESSION_PHASE_RUNNING; + + g_slist_free (session->shutdown_clients); + session->shutdown_clients = NULL; + g_slist_free (session->interact_clients); + session->interact_clients = NULL; + g_slist_free (session->phase2_clients); + session->phase2_clients = NULL; + + for (cl = session->clients; cl; cl = cl->next) + gsm_client_shutdown_cancelled (cl->data); +} + +static void +initiate_shutdown (GsmSession *session) +{ + GSList *cl; + + session->phase = GSM_SESSION_PHASE_SHUTDOWN; + + if (session->clients == NULL) + session_shutdown (session); + + for (cl = session->clients; cl; cl = cl->next) + { + GsmClient *client = GSM_CLIENT (cl->data); + + session->shutdown_clients = + g_slist_prepend (session->shutdown_clients, client); + + gsm_client_save_yourself (client, FALSE); + } +} + +static void +session_shutdown (GsmSession *session) +{ + GSList *cl; + + /* FIXME: do this in reverse phase order */ + for (cl = session->clients; cl; cl = cl->next) + gsm_client_die (cl->data); + + g_signal_emit (session, signals[SHUTDOWN_COMPLETED], 0); +} + +static void +client_request_phase2 (GsmClient *client, gpointer data) +{ + GsmSession *session = data; + + /* Move the client from shutdown_clients to phase2_clients */ + + session->shutdown_clients = + g_slist_remove (session->shutdown_clients, client); + session->phase2_clients = + g_slist_prepend (session->phase2_clients, client); +} + +static void +client_request_interaction (GsmClient *client, gpointer data) +{ + GsmSession *session = data; + + session->interact_clients = + g_slist_append (session->interact_clients, client); + + if (!session->interact_clients->next) + gsm_client_interact (client); +} + +static void +client_interaction_done (GsmClient *client, gboolean cancel_shutdown, + gpointer data) +{ + GsmSession *session = data; + + g_return_if_fail (session->interact_clients && + (GsmClient *)session->interact_clients->data == client); + + if (cancel_shutdown) + { + session_cancel_shutdown (session); + return; + } + + /* Remove this client from interact_clients, and if there's another + * client waiting to interact, let it interact now. + */ + session->interact_clients = + g_slist_remove (session->interact_clients, client); + if (session->interact_clients) + gsm_client_interact (session->interact_clients->data); +} + +static void +client_save_yourself_done (GsmClient *client, gpointer data) +{ + GsmSession *session = data; + + session->shutdown_clients = + g_slist_remove (session->shutdown_clients, client); + session->interact_clients = + g_slist_remove (session->interact_clients, client); + session->phase2_clients = + g_slist_remove (session->phase2_clients, client); + + if (session->phase == GSM_SESSION_PHASE_SHUTDOWN && + !session->shutdown_clients) + { + if (session->phase2_clients) + session_shutdown_phase2 (session); + else + session_shutdown (session); + } +} + +static void +client_disconnected (GsmClient *client, gpointer data) +{ + GsmSession *session = data; + gboolean is_condition_client = FALSE; + + session->clients = + g_slist_remove (session->clients, client); + session->shutdown_clients = + g_slist_remove (session->shutdown_clients, client); + session->interact_clients = + g_slist_remove (session->interact_clients, client); + session->phase2_clients = + g_slist_remove (session->phase2_clients, client); + + if (g_slist_find (session->condition_clients, client)) + { + session->condition_clients = + g_slist_remove (session->condition_clients, client); + + is_condition_client = TRUE; + } + + if (session->phase != GSM_SESSION_PHASE_SHUTDOWN && + gsm_client_get_autorestart (client) && + !is_condition_client) + { + GError *error = NULL; + + gsm_client_restart (client, &error); + + if (error) + { + g_warning ("Error on restarting session client: %s", error->message); + g_clear_error (&error); + } + } + + g_object_unref (client); +} + +GsmSession * +gsm_session_create_global (void) +{ + global_session = GSM_SESSION(g_object_new (GSM_TYPE_SESSION, NULL)); + return global_session; +} -- cgit v0.9.1