summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHavoc Pennington <hp@src.gnome.org>2001-09-13 04:50:18 +0000
committerHavoc Pennington <hp@src.gnome.org>2001-09-13 04:50:18 +0000
commit82aa7363f92ac2e8d1018bd90adaf66c0c59bbd6 (patch)
tree6f1b45ee1005801d07fa294636f40c7a22537900
parenta4346200e8f45c3a08543d2238c46110eeb142b7 (diff)
downloadmetacity-82aa7363f92ac2e8d1018bd90adaf66c0c59bbd6.tar.gz
still does not work.
-rw-r--r--src/msm/client.c231
-rw-r--r--src/msm/client.h64
-rw-r--r--src/msm/main.c7
-rw-r--r--src/msm/server.c591
-rw-r--r--src/msm/server.h17
-rw-r--r--src/msm/util.h2
6 files changed, 860 insertions, 52 deletions
diff --git a/src/msm/client.c b/src/msm/client.c
index ed6d1a9b..abe9f407 100644
--- a/src/msm/client.c
+++ b/src/msm/client.c
@@ -21,4 +21,235 @@
#include "client.h"
+struct _MsmClient
+{
+ MsmServer *server;
+ SmsConn cnxn;
+ MsmClientState state;
+ char *id;
+ char *hostname;
+ char *desc;
+ int restart_style;
+};
+MsmClient*
+msm_client_new (MsmServer *server,
+ SmsConn cnxn)
+{
+ MsmClient *client;
+
+ client = g_new (MsmClient, 1);
+
+ client->server = server;
+ client->cnxn = cnxn;
+ client->state = MSM_CLIENT_STATE_NEW;
+ client->id = NULL;
+ client->hostname = NULL;
+ client->desc = g_strdup ("unknown");
+ client->restart_style = SmRestartIfRunning;
+
+ return client;
+}
+
+void
+msm_client_free (MsmClient *client)
+{
+ IceConn ice_cnxn;
+
+ ice_cnxn = SmsGetIceConnection (client->cnxn);
+ SmsCleanUp (client->cnxn);
+ IceSetShutdownNegotiation (ice_cnxn, False);
+ IceCloseConnection (ice_cnxn);
+
+ g_free (client->id);
+ g_free (client->hostname);
+ g_free (client->desc);
+
+ g_free (client);
+}
+
+SmsConn
+msm_client_get_connection (MsmClient *client)
+{
+ return client->cnxn;
+}
+
+const char*
+msm_client_get_description (MsmClient *client)
+{
+ return client->desc;
+}
+
+MsmClientState
+msm_client_get_state (MsmClient *client)
+{
+ return client->state;
+}
+
+MsmServer*
+msm_client_get_server (MsmClient *client)
+{
+ return client->server;
+}
+
+void
+msm_client_register (MsmClient *client,
+ const char *id)
+{
+ char *p;
+
+ if (client->state != MSM_CLIENT_STATE_NEW)
+ {
+ msm_warning (_("Client '%s' attempted to register when it was already registered\n"), client->desc);
+
+ return;
+ }
+
+ client->state = MSM_CLIENT_STATE_IDLE;
+ client->id = g_strdup (id);
+
+ SmsRegisterClientReply (client->cnxn, client->id);
+
+ p = SmsClientHostName (smsConn);
+ client->hostname = g_strdup (p);
+ free (p);
+}
+
+void
+msm_client_interact_request (MsmClient *client)
+{
+ if (client->state != MSM_CLIENT_STATE_SAVING &&
+ client->state != MSM_CLIENT_STATE_SAVING_PHASE2)
+ {
+ msm_warning (_("Client '%s' requested interaction when it was not being saved\n"),
+ client->desc);
+
+ return;
+ }
+
+ msm_server_queue_interaction (client->server, client);
+}
+
+void
+msm_client_begin_interact (MsmClient *client)
+{
+ g_return_if_fail (client->interact_requested);
+
+ SmsInteract (client->cnxn);
+}
+
+void
+msm_client_save (MsmClient *client,
+ gboolean allow_interaction,
+ gboolean shut_down)
+{
+ if (client->state != MSM_CLIENT_STATE_IDLE)
+ {
+ msm_warning (_("Tried to save client '%s' but it was not in the idle state\n"),
+ client->desc);
+
+ return;
+ }
+
+ client->state = MSM_CLIENT_STATE_SAVING;
+
+ SmsSaveYourself (client->cnxn,
+ SmSaveBoth, /* This arg makes no sense whatsoever */
+ shut_down,
+ allow_interaction ? SmInteractStyleAny : SmInteractStyleNone,
+ FALSE /* not "fast" */);
+}
+
+void
+msm_client_shutdown_cancelled (MsmClient *client)
+{
+ if (client->state != MSM_CLIENT_STATE_SAVING &&
+ client->state != MSM_CLIENT_STATE_SAVING_PHASE2)
+ {
+ msm_warning (_("Tried to send cancel shutdown to client '%s' which was not saving\n"),
+ client->desc);
+ return;
+ }
+
+ client->state = MSM_CLIENT_STATE_IDLE;
+ SmsShutdownCancelled (client->cnxn);
+}
+
+void
+msm_client_phase2_request (MsmClient *client)
+{
+ if (client->state != MSM_CLIENT_STATE_SAVING)
+ {
+ msm_warning (_("Client '%s' requested phase 2 save but was not in a phase 1 save\n"),
+ client->desc);
+ return;
+ }
+
+ client->state = MSM_CLIENT_STATE_PHASE2_REQUESTED;
+}
+
+void
+msm_client_save_phase2 (MsmClient *client)
+{
+ if (client->state != MSM_CLIENT_STATE_PHASE2_REQUESTED)
+ {
+ msm_warning (_("We tried to save client '%s' in phase 2, but it hadn't requested it.\n"), client->desc);
+ return;
+ }
+
+ SmsSaveYourselfPhase2 (client->cnxn);
+}
+
+void
+msm_client_die (MsmClient *client)
+{
+ client->state = MSM_CLIENT_STATE_DEAD;
+ SmsDie (client->cnxn);
+}
+
+void
+msm_client_save_complete (MsmClient *client)
+{
+ client->state = MSM_CLIENT_STATE_IDLE;
+ SmsSaveComplete (client->cnxn);
+}
+
+void
+msm_client_save_confirmed (MsmClient *client,
+ gboolean successful)
+{
+ if (client->state != MSM_CLIENT_STATE_SAVING &&
+ client->state != MSM_CLIENT_STATE_SAVING_PHASE2)
+ {
+ msm_warning (_("Client '%s' said it was done saving, but it hadn't been told to save\n"),
+ client->desc);
+ return;
+ }
+
+ if (successful)
+ client->state = MSM_CLIENT_STATE_SAVE_DONE;
+ else
+ client->state = MSM_CLIENT_STATE_SAVE_FAILED;
+}
+
+void
+msm_client_set_property (MsmClient *client,
+ SmProp *prop)
+{
+
+
+}
+
+void
+msm_client_unset_property (MsmClient *client,
+ const char *name)
+{
+
+}
+
+void
+msm_client_send_properties (MsmClient *client)
+{
+ SmsReturnProperties (/* FIXME */);
+
+}
diff --git a/src/msm/client.h b/src/msm/client.h
index 1140432a..24759698 100644
--- a/src/msm/client.h
+++ b/src/msm/client.h
@@ -23,11 +23,71 @@
#define MSM_CLIENT_H
#include <glib.h>
-#include <X11/ICE/ICElib.h>
-#include <X11/SM/SMlib.h>
#include "server.h"
+/* See xsmp docs for a state description. This enum doesn't
+ * correspond exactly, but close enough.
+ */
+typedef enum
+{
+ /* Client has just newly connected, not yet registered */
+ MSM_CLIENT_STATE_NEW,
+ /* Client has registered with us successfully, isn't doing
+ * anything special
+ */
+ MSM_CLIENT_STATE_IDLE,
+ /* Client is saving self in phase 1 */
+ MSM_CLIENT_STATE_SAVING,
+ /* Client has requested phase 2 save, but we aren't in phase 2 yet */
+ MSM_CLIENT_STATE_PHASE2_REQUESTED,
+ /* Client is in phase 2 save; all the same things are
+ * allowed as with STATE_SAVING, except you can't request
+ * a phase 2 save
+ */
+ MSM_CLIENT_STATE_SAVING_PHASE2,
+
+ /* Client sent SaveYourselfDone with success = TRUE */
+ MSM_CLIENT_STATE_SAVE_DONE,
+
+ /* Client sent SaveYourselfDone with success = FALSE */
+ MSM_CLIENT_STATE_SAVE_FAILED,
+
+ /* Client was asked to die */
+ MSM_CLIENT_STATE_DEAD
+
+} MsmClientState;
+MsmClient* msm_client_new (MsmServer *server,
+ SmsConn cnxn);
+void msm_client_free (MsmClient *client);
+
+SmsConn msm_client_get_connection (MsmClient *client);
+const char* msm_client_get_description (MsmClient *client);
+MsmClientState msm_client_get_state (MsmClient *client);
+MsmServer* msm_client_get_server (MsmClient *client);
+
+void msm_client_set_property (MsmClient *client,
+ SmProp *prop);
+void msm_client_unset_property (MsmClient *client,
+ const char *name);
+void msm_client_send_properties (MsmClient *client);
+
+void msm_client_register (MsmClient *client,
+ const char *id);
+void msm_client_interact_request (MsmClient *client);
+void msm_client_begin_interact (MsmClient *client);
+void msm_client_save (MsmClient *client,
+ gboolean allow_interaction,
+ gboolean shut_down);
+void msm_client_shutdown_cancelled (MsmClient *client);
+void msm_client_phase2_request (MsmClient *client);
+void msm_client_save_phase2 (MsmClient *client);
+void msm_client_save_confirmed (MsmClient *client,
+ gboolean successful);
+
+void msm_client_die (MsmClient *client);
+void msm_client_save_complete (MsmClient *client);
#endif
+
diff --git a/src/msm/main.c b/src/msm/main.c
index 23d5df90..27f91ef3 100644
--- a/src/msm/main.c
+++ b/src/msm/main.c
@@ -44,6 +44,13 @@ shutdown_cleanly_on_signal (int signo)
g_main_quit (main_loop);
}
+void
+msm_quit (void)
+{
+ if (main_loop && g_main_is_running (main_loop))
+ g_main_quit (main_loop);
+}
+
int
main (int argc, char **argv)
{
diff --git a/src/msm/server.c b/src/msm/server.c
index e425b011..02dc51df 100644
--- a/src/msm/server.c
+++ b/src/msm/server.c
@@ -48,38 +48,55 @@ struct _MsmServer
GList *clients;
IceAuthDataEntry *auth_entries;
int n_auth_entries;
+ MsmClient *currently_interacting;
+ GList *interact_pending;
+
+ guint in_shutdown : 1;
+ guint save_allows_interaction : 1;
};
-static Status register_client_callback (SmsConn smsConn,
- SmPointer managerData,
- char *previousId);
-static void interact_request_callback (SmsConn smsConn,
- SmPointer managerData,
- int dialogType);
-static void interact_done_callback (SmsConn smsConn,
- SmPointer managerData,
- Bool cancelShutdown);
-static void save_yourself_request_callback (SmsConn smsConn,
- SmPointer managerData,
- int saveType,
+static Status register_client_callback (SmsConn cnxn,
+ SmPointer manager_data,
+ char *previous_id);
+static void interact_request_callback (SmsConn cnxn,
+ SmPointer manager_data,
+ int dialog_type);
+static void interact_done_callback (SmsConn cnxn,
+ SmPointer manager_data,
+ Bool cancel_shutdown);
+static void save_yourself_request_callback (SmsConn cnxn,
+ SmPointer manager_data,
+ int save_type,
Bool shutdown,
- int interactStyle,
+ int interact_style,
Bool fast,
Bool global);
-static void save_yourself_phase2_request_callback (SmsConn smsConn,
- SmPointer managerData);
-static void save_yourself_done_callback (SmsConn smsConn,
- SmPointer managerData,
+static void save_yourself_phase2_request_callback (SmsConn cnxn,
+ SmPointer manager_data);
+static void save_yourself_done_callback (SmsConn cnxn,
+ SmPointer manager_data,
Bool success);
-static void close_connection_callback (SmsConn smsConn,
- SmPointer managerData,
+static void close_connection_callback (SmsConn cnxn,
+ SmPointer manager_data,
int count,
char **reasonMsgs);
-static Status new_client_callback (SmsConn smsConn,
- SmPointer managerData,
+static void set_properties_callback (SmsConn cnxn,
+ SmPointer manager_data,
+ int numProps,
+ SmProp **props);
+static void delete_properties_callback (SmsConn cnxn,
+ SmPointer manager_data,
+ int numProps,
+ char **propNames);
+static void get_properties_callback (SmsConn cnxn,
+ SmPointer manager_data);
+
+
+static Status new_client_callback (SmsConn cnxn,
+ SmPointer manager_data,
unsigned long *maskRet,
SmsCallbacks *callbacksRet,
- char **failureReasonRet);
+ char **failure_reason_ret);
static Bool host_auth_callback (char *hostname);
@@ -101,6 +118,10 @@ msm_server_new (void)
server->clients = NULL;
server->auth_entries = NULL;
server->n_auth_entries = 0;
+ server->currently_interacting = NULL;
+ server->interact_pending = NULL;
+ server->in_shutdown = FALSE;
+ server->save_allows_interaction = FALSE;
if (!SmsInitialize (PACKAGE, VERSION,
new_client_callback,
@@ -109,80 +130,343 @@ msm_server_new (void)
sizeof (errbuf), errbuf))
msm_fatal (_("Could not initialize SMS: %s\n"), errbuf);
- ice_init ();
+ ice_init (server);
-
+ return server;
}
void
msm_server_free (MsmServer *server)
{
-
+ g_list_free (server->clients);
+ g_list_free (server->interact_pending);
+
free_auth_entries (server->auth_entries, server->n_auth_entries);
g_free (server);
}
+
+void
+msm_server_drop_client (MsmServer *server,
+ MsmClient *client)
+{
+ server->clients = g_list_remove (server->clients, client);
+
+ if (server->currently_interacting == client)
+ msm_server_next_pending_interaction (server);
+
+ msm_client_free (client);
+
+ msm_server_consider_phase_change (server);
+
+ /* We can quit after all clients have been dropped. */
+ if (server->in_shutdown &&
+ server->clients == NULL)
+ msm_quit ();
+}
+
+void
+msm_server_next_pending_interaction (MsmServer *server)
+{
+ server->currently_interacting = NULL;
+ if (server->interact_pending)
+ {
+ /* Start up the next interaction */
+ server->currently_interacting = server->interact_pending->data;
+ server->interact_pending =
+ g_list_remove (server->interact_pending,
+ server->currently_interacting);
+ msm_client_begin_interact (server->currently_interacting);
+ }
+}
+
static Status
-register_client_callback (SmsConn smsConn,
- SmPointer managerData,
- char *previousId)
+register_client_callback (SmsConn cnxn,
+ SmPointer manager_data,
+ char *previous_id)
{
+ /* This callback should:
+ * a) if previous_id is NULL, this is a new client; register
+ * it and return TRUE
+ * b) if previous_id is non-NULL and is an ID we know about,
+ * register client and return TRUE
+ * c) if previous_id is non-NULL and we've never heard of it,
+ * return FALSE
+ *
+ * Whenever previous_id is non-NULL we need to free() it.
+ * (What an incredibly broken interface...)
+ */
+
+ MsmClient *client;
+
+ client = manager_data;
+
+ if (previous_id == NULL)
+ {
+ char *id;
+
+ id = SmsGenerateClientID (msm_client_get_connection (client));
+
+ msm_client_register (client, id);
+
+ free (id);
+
+ /* FIXME ksm and gnome-session send a SaveYourself to the client
+ * here. I don't understand why though.
+ */
+
+ return TRUE;
+ }
+ else
+ {
+ /* FIXME check for pending/known client IDs and register the client,
+ * return TRUE if we know about this previous_id
+ */
+
+ free (previous_id);
+ return FALSE;
+ }
}
static void
-interact_request_callback (SmsConn smsConn,
- SmPointer managerData,
- int dialogType)
+interact_request_callback (SmsConn cnxn,
+ SmPointer manager_data,
+ int dialog_type)
{
+ MsmClient *client;
+ MsmServer *server;
+
+ client = manager_data;
+ server = msm_client_get_server (client);
+
+ if (!server->save_allows_interaction)
+ {
+ msm_warning (_("Client '%s' requested interaction, but interaction is not allowed right now.\n"),
+ msm_client_get_description (client));
+
+ return;
+ }
+
+ msm_client_interact_request (client);
}
static void
-interact_done_callback (SmsConn smsConn,
- SmPointer managerData,
- Bool cancelShutdown)
+interact_done_callback (SmsConn cnxn,
+ SmPointer manager_data,
+ Bool cancel_shutdown)
{
+ MsmClient *client;
+ MsmServer *server;
+
+ client = manager_data;
+ server = msm_client_get_server (client);
+
+ if (cancel_shutdown &&
+ server->in_shutdown &&
+ server->save_allows_interaction)
+ {
+ msm_server_cancel_shutdown (server);
+ }
+ else
+ {
+ if (server->currently_interacting == client)
+ {
+ msm_server_next_pending_interaction (server);
+ }
+ else
+ {
+ msm_warning (_("Received InteractDone from client '%s' which should not be interacting right now\n"),
+ msm_client_get_description (client));
+ }
+ }
}
static void
-save_yourself_request_callback (SmsConn smsConn,
- SmPointer managerData,
- int saveType,
- Bool shutdown,
- int interactStyle,
- Bool fast,
- Bool global)
+save_yourself_request_callback (SmsConn cnxn,
+ SmPointer manager_data,
+ int save_type,
+ Bool shutdown,
+ int interact_style,
+ Bool fast,
+ Bool global)
{
+ /* The spec says we "may" honor this exactly as requested;
+ * we decide not to, because some of the fields are stupid
+ * and/or useless
+ */
+ MsmClient *client;
+ MsmServer *server;
+
+ client = manager_data;
+ server = msm_client_get_server (client);
+
+ if (global)
+ {
+ msm_server_save_all (server,
+ interact_style != SmInteractStyleNone,
+ shutdown != FALSE);
+ }
+ else
+ {
+ if (msm_client_get_state (client) == MSM_CLIENT_STATE_IDLE)
+ msm_client_save (client,
+ interact_style != SmInteractStyleNone,
+ shutdown != FALSE);
+ else
+ msm_warning (_("Client '%s' requested save, but is not currently in the idle state\n"),
+ msm_client_get_description (client));
+ }
}
static void
-save_yourself_phase2_request_callback (SmsConn smsConn,
- SmPointer managerData)
+save_yourself_phase2_request_callback (SmsConn cnxn,
+ SmPointer manager_data)
{
+ MsmClient *client;
+ MsmServer *server;
+
+ client = manager_data;
+ server = msm_client_get_server (client);
+
+ msm_client_phase2_request (client);
}
static void
-save_yourself_done_callback (SmsConn smsConn,
- SmPointer managerData,
+save_yourself_done_callback (SmsConn cnxn,
+ SmPointer manager_data,
Bool success)
{
+ MsmClient *client;
+ MsmServer *server;
+
+ client = manager_data;
+ server = msm_client_get_server (client);
+
+ msm_client_save_confirmed (client, success != FALSE);
+
+ msm_server_consider_phase_change (server);
}
static void
-close_connection_callback (SmsConn smsConn,
- SmPointer managerData,
+close_connection_callback (SmsConn cnxn,
+ SmPointer manager_data,
int count,
char **reasonMsgs)
{
+ MsmClient *client;
+ MsmServer *server;
+
+ client = manager_data;
+ server = msm_client_get_server (client);
+
+ msm_server_drop_client (server, client);
+
+ /* I'm assuming these messages would be on crack, and therefore not
+ * displaying them.
+ */
+ SmFreeReasons (count, reasonMsgs);
+}
+
+static void
+set_properties_callback (SmsConn cnxn,
+ SmPointer manager_data,
+ int numProps,
+ SmProp **props)
+{
+
+
}
+static void
+delete_properties_callback (SmsConn cnxn,
+ SmPointer manager_data,
+ int numProps,
+ char **propNames)
+{
+
+}
+
+static void
+get_properties_callback (SmsConn cnxn,
+ SmPointer manager_data)
+{
+
+
+}
+
+
static Status
-new_client_callback (SmsConn smsConn,
- SmPointer managerData,
+new_client_callback (SmsConn cnxn,
+ SmPointer manager_data,
unsigned long *maskRet,
SmsCallbacks *callbacksRet,
- char **failureReasonRet)
+ char **failure_reason_ret)
{
+ MsmClient *client;
+ MsmServer *server;
+
+ server = manager_data;
+
+
+ /* If we want to disallow the new client, here we fill
+ * failure_reason_ret with a malloc'd string and return FALSE
+ */
+ if (server->in_shutdown)
+ {
+ /* have to use malloc() */
+ *failure_reason_ret = malloc (256);
+ g_strncpy (*failure_reason_ret, _("Refusing new client connection because the session is currently being shut down\n"), 255);
+ (*failure_reason_ret)[255] = '\0'; /* paranoia */
+ return FALSE;
+ }
+
+ client = msm_client_new (server, cnxn);
+ server->clients = g_list_prepend (server->clients, client);
+
+ *maskRet = 0;
+
+ *maskRet |= SmsRegisterClientProcMask;
+ callbacksRet->register_client.callback = register_client_callback;
+ callbacksRet->register_client.manager_data = client;
+
+ *maskRet |= SmsInteractRequestProcMask;
+ callbacksRet->interact_request.callback = interact_request_callback;
+ callbacksRet->interact_request.manager_data = client;
+
+ *maskRet |= SmsInteractDoneProcMask;
+ callbacksRet->interact_done.callback = interact_done_callback;
+ callbacksRet->interact_done.manager_data = client;
+
+ *maskRet |= SmsSaveYourselfRequestProcMask;
+ callbacksRet->save_yourself_request.callback = save_yourself_request_callback;
+ callbacksRet->save_yourself_request.manager_data = client;
+
+ *maskRet |= SmsSaveYourselfP2RequestProcMask;
+ callbacksRet->save_yourself_phase2_request.callback = save_yourself_phase2_request_callback;
+ callbacksRet->save_yourself_phase2_request.manager_data = client;
+
+ *maskRet |= SmsSaveYourselfDoneProcMask;
+ callbacksRet->save_yourself_done.callback = save_yourself_done_callback;
+ callbacksRet->save_yourself_done.manager_data = client;
+
+ *maskRet |= SmsCloseConnectionProcMask;
+ callbacksRet->close_connection.callback = close_connection_callback;
+ callbacksRet->close_connection.manager_data = client;
+
+ *maskRet |= SmsSetPropertiesProcMask;
+ callbacksRet->set_properties.callback = set_properties_callback;
+ callbacksRet->set_properties.manager_data = client;
+
+ *maskRet |= SmsDeletePropertiesProcMask;
+ callbacksRet->delete_properties.callback = delete_properties_callback;
+ callbacksRet->delete_properties.manager_data = client;
+
+ *maskRet |= SmsGetPropertiesProcMask;
+ callbacksRet->get_properties.callback = get_properties_callback;
+ callbacksRet->get_properties.manager_data = client;
+
+ return TRUE;
}
static Bool
@@ -193,6 +477,213 @@ host_auth_callback (char *hostname)
return False;
}
+void
+msm_server_queue_interaction (MsmServer *server,
+ MsmClient *client)
+{
+ if (server->currently_interacting == client ||
+ g_list_find (server->interact_pending, client) != NULL)
+ return; /* Already queued */
+
+ server->interact_pending = g_list_prepend (server->interact_pending,
+ client);
+
+ msm_server_next_pending_interaction (server);
+}
+
+void
+msm_server_save_all (MsmServer *server,
+ gboolean allow_interaction,
+ gboolean shut_down)
+{
+ GList *tmp;
+
+ if (shut_down) /* never cancel a shutdown here */
+ server->in_shutdown = TRUE;
+
+ /* We just assume the most recent request for interaction or no is
+ * correct
+ */
+ server->save_allows_interaction = allow_interaction;
+
+ tmp = server->clients;
+ while (tmp != NULL)
+ {
+ MsmClient *client;
+
+ client = tmp->data;
+
+ if (msm_client_get_state (client) == MSM_CLIENT_STATE_IDLE)
+ msm_client_save (client,
+ server->save_allows_interaction,
+ server->in_shutdown);
+
+ tmp = tmp->next;
+ }
+}
+
+void
+msm_server_cancel_shutdown (MsmServer *server)
+{
+ GList *tmp;
+
+ if (!server->in_shutdown)
+ return;
+
+ server->in_shutdown = FALSE;
+
+ /* Cancel any interactions in progress */
+ g_list_free (server->interact_pending);
+ server->interact_pending = NULL;
+ server->currently_interacting = NULL;
+
+ tmp = server->clients;
+ while (tmp != NULL)
+ {
+ MsmClient *client;
+
+ client = tmp->data;
+
+ if (msm_client_get_state (client) == MSM_CLIENT_STATE_SAVING)
+ msm_client_shutdown_cancelled (client);
+
+ tmp = tmp->next;
+ }
+}
+
+/* Think about whether to move to phase 2, return to idle state,
+ * or shut down
+ */
+void
+msm_server_consider_phase_change (MsmServer *server)
+{
+ GList *tmp;
+ gboolean some_phase1;
+ gboolean some_phase2;
+ gboolean some_phase2_requested;
+ gboolean some_alive;
+
+ some_phase1 = FALSE;
+ some_phase2 = FALSE;
+ some_phase2_requested = FALSE;
+ some_alive = FALSE;
+
+ tmp = server->clients;
+ while (tmp != NULL)
+ {
+ MsmClient *client;
+
+ client = tmp->data;
+
+ switch (msm_client_get_state (client))
+ {
+ case MSM_CLIENT_STATE_SAVING:
+ some_phase1 = TRUE;
+ break;
+ case MSM_CLIENT_STATE_SAVING_PHASE2:
+ some_phase2 = TRUE;
+ break;
+ case MSM_CLIENT_STATE_PHASE2_REQUESTED:
+ some_phase2_requested = TRUE;
+ break;
+ default:
+ break;
+ }
+
+ if (msm_client_get_state (client) != MSM_CLIENT_STATE_DEAD)
+ some_alive = TRUE;
+
+ tmp = tmp->next;
+ }
+
+ if (some_phase1)
+ return; /* still saving phase 1 */
+
+ if (some_phase2)
+ return; /* we are in phase 2 */
+
+ if (some_phase2_requested)
+ {
+ tmp = server->clients;
+ while (tmp != NULL)
+ {
+ MsmClient *client;
+
+ client = tmp->data;
+
+ if (msm_client_get_state (client) == MSM_CLIENT_STATE_PHASE2_REQUESTED)
+ msm_client_save_phase2 (client);
+
+ tmp = tmp->next;
+ }
+
+ return;
+ }
+
+ if (server->in_shutdown)
+ {
+ /* We are shutting down, and all clients are in the idle state.
+ * Tell all clients to die. When they all close their connections,
+ * we can exit.
+ */
+
+ if (some_alive)
+ {
+ tmp = server->clients;
+ while (tmp != NULL)
+ {
+ MsmClient *client = tmp->data;
+
+ if (msm_client_get_state (client) != MSM_CLIENT_STATE_DEAD)
+ msm_client_die (client);
+
+ tmp = tmp->next;
+ }
+ }
+ }
+ else
+ {
+ /* Send SaveComplete to all clients that are finished saving */
+ GList *tmp;
+
+ tmp = server->clients;
+ while (tmp != NULL)
+ {
+ MsmClient *client = tmp->data;
+
+ switch (msm_client_get_state (client))
+ {
+ case MSM_CLIENT_STATE_SAVE_DONE:
+ case MSM_CLIENT_STATE_SAVE_FAILED:
+ msm_client_save_complete (client);
+ break;
+ default:
+ break;
+ }
+
+ tmp = tmp->next;
+ }
+ }
+}
+
+void
+msm_server_foreach_client (MsmServer *server,
+ MsmClientFunc func)
+{
+ GList *tmp;
+
+ tmp = server->clients;
+ while (tmp != NULL)
+ {
+ MsmClient *client = tmp->data;
+
+ (* func) (client);
+
+ tmp = tmp->next;
+ }
+}
+
+
/*
* ICE utility code, cut-and-pasted from Metacity, and in turn
* from libgnomeui, and also some merged in from gsm, and xsm,
diff --git a/src/msm/server.h b/src/msm/server.h
index 4dce6bca..d9d66558 100644
--- a/src/msm/server.h
+++ b/src/msm/server.h
@@ -29,10 +29,27 @@
typedef struct _MsmClient MsmClient;
typedef struct _MsmServer MsmServer;
+typedef void (* MsmClientFunc) (MsmClient* client);
+
MsmServer* msm_server_new (void);
void msm_server_free (MsmServer *server);
+void msm_server_queue_interaction (MsmServer *server,
+ MsmClient *client);
+
+void msm_server_save_all (MsmServer *server,
+ gboolean allow_interaction,
+ gboolean shut_down);
+void msm_server_cancel_shutdown (MsmServer *server);
+
+void msm_server_consider_phase_change (MsmServer *server);
+
+void msm_server_foreach_client (MsmServer *server,
+ MsmClientFunc func);
+void msm_server_drop_client (MsmServer *server,
+ MsmClient *client);
+void msm_server_next_pending_interaction (MsmServer *server);
#endif
diff --git a/src/msm/util.h b/src/msm/util.h
index 4a9dfdb4..9a8d131d 100644
--- a/src/msm/util.h
+++ b/src/msm/util.h
@@ -34,5 +34,7 @@ void msm_warning (const char *format,
void msm_fatal (const char *format,
...) G_GNUC_PRINTF (1, 2);
+void msm_quit (void);
+
#endif