summaryrefslogtreecommitdiffstats
path: root/glib/examples/statemachine/statemachine-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'glib/examples/statemachine/statemachine-client.c')
-rw-r--r--glib/examples/statemachine/statemachine-client.c540
1 files changed, 540 insertions, 0 deletions
diff --git a/glib/examples/statemachine/statemachine-client.c b/glib/examples/statemachine/statemachine-client.c
new file mode 100644
index 00000000..54c630ff
--- /dev/null
+++ b/glib/examples/statemachine/statemachine-client.c
@@ -0,0 +1,540 @@
+#include <dbus/dbus-glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include "sm-marshal.h"
+
+static void lose (const char *fmt, ...) G_GNUC_NORETURN G_GNUC_PRINTF (1, 2);
+static void lose_gerror (const char *prefix, GError *error) G_GNUC_NORETURN;
+
+static void
+lose (const char *str, ...)
+{
+ va_list args;
+ GtkWidget *dialog;
+ char *text;
+
+ va_start (args, str);
+
+ text = g_strdup_vprintf (str, args);
+
+ va_end (args);
+
+ dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ "%s",
+ text);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ g_free (text);
+
+ exit (1);
+}
+
+static void
+lose_gerror (const char *prefix, GError *error)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ "%s",
+ prefix);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s",
+ error->message);
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ exit (1);
+}
+
+typedef struct
+{
+ char *name;
+ char *state;
+ DBusGProxy *proxy;
+ DBusGProxyCall *get_initial_state_call;
+} MachineInfo;
+
+typedef struct
+{
+ GtkWindow *window;
+ GtkWidget *view;
+ GtkTreeModel *store;
+
+ DBusGConnection *bus;
+ DBusGProxy *server_proxy;
+
+ GSList *pending_creation_calls;
+ DBusGProxyCall *get_machines_call;
+} ClientState;
+
+static gboolean
+proxy_to_iter (GtkTreeModel *model, DBusGProxy *proxy, GtkTreeIter *iter)
+{
+ if (!gtk_tree_model_get_iter_first (model, iter))
+ return FALSE;
+ do {
+ MachineInfo *info;
+ gtk_tree_model_get (model, iter, 0, &info, -1);
+ if (info->proxy == proxy)
+ return TRUE;
+ } while (gtk_tree_model_iter_next (model, iter));
+ return FALSE;
+}
+
+static void
+get_machine_info_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer data)
+{
+ GtkTreeIter iter;
+ ClientState *state = data;
+ GError *error = NULL;
+ char *name, *statename;
+ MachineInfo *info;
+
+ if (!dbus_g_proxy_end_call (proxy, call, &error,
+ G_TYPE_STRING, &name,
+ G_TYPE_STRING, &statename,
+ G_TYPE_INVALID))
+ lose_gerror ("Couldn't complete GetInfo", error);
+
+ if (!proxy_to_iter (state->store, proxy, &iter))
+ g_assert_not_reached ();
+
+ gtk_tree_model_get (state->store, &iter, 0, &info, -1);
+ g_free (info->name);
+ info->name = name;
+ g_free (info->state);
+ info->state = statename;
+ {
+ GtkTreePath *path;
+ path = gtk_tree_model_get_path (state->store, &iter);
+ gtk_tree_model_row_changed (state->store, path, &iter);
+ gtk_tree_path_free (path);
+ }
+ info->get_initial_state_call = NULL;
+}
+
+static void
+proxy_state_changed_cb (DBusGProxy *proxy,
+ const char *statename,
+ gpointer user_data)
+{
+ MachineInfo *info;
+ GtkTreeIter iter;
+ ClientState *state = user_data;
+
+ if (!proxy_to_iter (state->store, proxy, &iter))
+ g_assert_not_reached ();
+ gtk_tree_model_get (state->store, &iter, 0, &info, -1);
+
+ g_print ("Got state change for %p (%s) to %s\n", proxy, info->name ? info->name : "(unknown)", statename);
+
+ /* If we got a signal for the state, we shouldn't update
+ * based on the (possibly stale) call
+ */
+ if (info->get_initial_state_call != NULL)
+ {
+ g_print ("Cancelling outstanding GetInfo call for %p due to signal\n", proxy);
+ dbus_g_proxy_cancel_call (proxy, info->get_initial_state_call);
+ info->get_initial_state_call = NULL;
+ }
+
+ g_free (info->state);
+ info->state = g_strdup (statename);
+
+ {
+ GtkTreePath *path;
+ path = gtk_tree_model_get_path (state->store, &iter);
+ gtk_tree_model_row_changed (state->store, path, &iter);
+ gtk_tree_path_free (path);
+ }
+}
+
+static void
+add_machine (ClientState *state,
+ const char *name,
+ const char *mstate,
+ const char *path)
+{
+ MachineInfo *info;
+ GtkTreeIter iter;
+
+ info = g_new0 (MachineInfo, 1);
+ info->name = g_strdup (name);
+ info->state = g_strdup (mstate);
+
+ info->proxy = dbus_g_proxy_new_for_name (state->bus,
+ "com.example.StateServer",
+ path,
+ "com.example.StateMachine");
+
+ if (!info->state)
+ {
+ g_print ("Starting GetInfo call for %p\n", info->proxy);
+ info->get_initial_state_call
+ = dbus_g_proxy_begin_call (info->proxy, "GetInfo",
+ get_machine_info_cb,
+ state, NULL,
+ G_TYPE_INVALID);
+ }
+ else
+ info->get_initial_state_call = NULL;
+
+ /* Watch for state changes */
+ dbus_g_proxy_add_signal (info->proxy, "StateChanged",
+ G_TYPE_STRING, G_TYPE_INVALID);
+
+ dbus_g_proxy_connect_signal (info->proxy,
+ "StateChanged",
+ G_CALLBACK (proxy_state_changed_cb),
+ state,
+ NULL);
+
+ gtk_list_store_prepend (GTK_LIST_STORE (state->store), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (state->store), &iter, 0, info, -1);
+
+}
+
+static void
+machine_created_cb (DBusGProxy *proxy,
+ const char *name,
+ const char *path,
+ gpointer data)
+{
+ ClientState *state = data;
+
+ add_machine (state, name, NULL, path);
+}
+
+static void
+server_destroyed_cb (DBusGProxy *proxy, gpointer data)
+{
+ g_print ("Server terminated!\n");
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_INFO,
+ GTK_BUTTONS_CLOSE,
+ "State Machine server has exited");
+
+ gtk_dialog_run (GTK_DIALOG (dialog));
+
+ exit (1);
+}
+
+static void
+window_destroyed_cb (GtkWidget *window, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+static void
+create_machine_completed_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
+{
+ GError *error = NULL;
+ ClientState *state = data;
+
+ if (!dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID))
+ {
+ /* Ignore NameInUse errors */
+ if (dbus_g_error_has_name (error, "com.example.StateServer.NameInUse"))
+ ;
+ else
+ lose_gerror ("Failed to create new state machine", error);
+ }
+ g_print ("machine created successfully\n");
+ state->pending_creation_calls = g_slist_remove (state->pending_creation_calls, call);
+}
+
+static void
+send_create_machine (ClientState *state)
+{
+ DBusGProxyCall *call;
+ char *name;
+ gint n_children;
+
+ n_children = gtk_tree_model_iter_n_children (state->store, NULL);
+ name = g_strdup_printf ("machine%d", n_children);
+
+ g_print ("Invoking CreateMachine(%s)\n", name);
+ call = dbus_g_proxy_begin_call (state->server_proxy, "CreateMachine",
+ create_machine_completed_cb,
+ state, NULL,
+ G_TYPE_STRING, name, G_TYPE_INVALID);
+ g_free (name);
+ state->pending_creation_calls = g_slist_prepend (state->pending_creation_calls, call);
+}
+
+static void
+do_a_state_change (ClientState *state)
+{
+ gint index;
+ GtkTreeIter iter;
+ gint n_children;
+ MachineInfo *info;
+
+ n_children = gtk_tree_model_iter_n_children (state->store, NULL);
+ if (n_children == 0)
+ {
+ g_print ("No machines yet, not doing a state switch\n");
+ return;
+ }
+
+ index = g_random_int_range (0, n_children);
+ gtk_tree_model_iter_nth_child (state->store, &iter, NULL, index);
+ gtk_tree_model_get (state->store, &iter, 0, &info, -1);
+
+ if (!info->state)
+ {
+ g_print ("Machine not yet in known state, skipping state switch\n");
+ return;
+ }
+
+ if (!strcmp (info->state, "Shutdown"))
+ {
+ g_print ("Sending Start request to machine %s\n", info->name);
+ dbus_g_proxy_call_no_reply (info->proxy, "Start", G_TYPE_INVALID);
+ }
+ else
+ {
+ g_print ("Sending Shutdown request to machine %s\n", info->name);
+ dbus_g_proxy_call_no_reply (info->proxy, "Shutdown", G_TYPE_INVALID);
+ }
+}
+
+static gboolean
+do_something_random_2 (gpointer data)
+{
+ ClientState *state = data;
+ do_a_state_change (state);
+ g_timeout_add (g_random_int_range (500, 3000), do_something_random_2, state);
+ return FALSE;
+}
+
+static gboolean
+do_something_random (gpointer data)
+{
+ ClientState *state = data;
+ gint n_children;
+
+ switch (g_random_int_range (0, 2))
+ {
+ case 0:
+ send_create_machine (state);
+ break;
+ case 1:
+ do_a_state_change (state);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ n_children = gtk_tree_model_iter_n_children (state->store, NULL);
+ if (n_children >= 8)
+ g_timeout_add (g_random_int_range (500, 3000), do_something_random_2, state);
+ else
+ g_timeout_add (g_random_int_range (500, 3000), do_something_random, state);
+ return FALSE;
+}
+
+static void
+set_cell_name (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ MachineInfo *info;
+
+ gtk_tree_model_get (tree_model, iter, 0, &info, -1);
+
+ g_object_set (cell, "text", info->name ? info->name : "", NULL);
+}
+
+static gint
+sort_by_name (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ MachineInfo *info_a, *info_b;
+
+ gtk_tree_model_get (model, a, 0, &info_a, -1);
+ gtk_tree_model_get (model, b, 0, &info_b, -1);
+
+ return strcmp (info_a->name ? info_a->name : "",
+ info_b ? info_b->name : "");
+}
+
+static void
+set_cell_state (GtkTreeViewColumn *tree_column,
+ GtkCellRenderer *cell,
+ GtkTreeModel *tree_model,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ MachineInfo *info;
+
+ gtk_tree_model_get (tree_model, iter, 0, &info, -1);
+
+ g_object_set (cell, "text", info->state ? info->state : "", NULL);
+}
+
+static gint
+sort_by_state (GtkTreeModel *model,
+ GtkTreeIter *a,
+ GtkTreeIter *b,
+ gpointer user_data)
+{
+ MachineInfo *info_a, *info_b;
+
+ gtk_tree_model_get (model, a, 0, &info_a, -1);
+ gtk_tree_model_get (model, b, 0, &info_b, -1);
+
+ return strcmp (info_a->state ? info_a->state : "",
+ info_b ? info_b->state : "");
+}
+
+static void
+get_machines_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
+{
+ GError *error = NULL;
+ ClientState *state = data;
+ GPtrArray *objs;
+ guint i;
+ GtkWidget *scrolledwin;
+ GtkTreeViewColumn *col;
+ GtkCellRenderer *rend;
+
+ g_assert (call == state->get_machines_call);
+
+ if (!dbus_g_proxy_end_call (proxy, call, &error,
+ dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
+ &objs,
+ G_TYPE_INVALID))
+ lose_gerror ("Failed to get current machine list", error);
+
+ gtk_container_remove (GTK_CONTAINER (state->window),
+ gtk_bin_get_child (GTK_BIN (state->window)));
+
+ scrolledwin = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_show (scrolledwin);
+
+ state->store = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_POINTER));
+ state->view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (state->store));
+ gtk_widget_show (state->view);
+ gtk_container_add (GTK_CONTAINER (scrolledwin), state->view);
+ gtk_container_add (GTK_CONTAINER (state->window), scrolledwin);
+
+ rend = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new_with_attributes (_("Name"),
+ rend,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func (col, rend, set_cell_name, NULL, NULL);
+ gtk_tree_view_column_set_resizable (col, TRUE);
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (state->store),
+ 0, sort_by_name, NULL, NULL);
+ gtk_tree_view_column_set_sort_column_id (col, 0);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (state->view), col);
+
+ rend = gtk_cell_renderer_text_new ();
+ col = gtk_tree_view_column_new_with_attributes (_("State"),
+ rend,
+ NULL);
+ gtk_tree_view_column_set_cell_data_func (col, rend, set_cell_state, NULL, NULL);
+ gtk_tree_view_column_set_resizable (col, TRUE);
+ gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (state->store),
+ 0, sort_by_state, NULL, NULL);
+ gtk_tree_view_column_set_sort_column_id (col, 0);
+ gtk_tree_view_append_column (GTK_TREE_VIEW (state->view), col);
+
+ for (i = 0; i < objs->len; i++)
+ {
+ add_machine (state, NULL, NULL, g_ptr_array_index (objs, i));
+ g_free (g_ptr_array_index (objs, i));
+ }
+ g_ptr_array_free (objs, TRUE);
+
+ g_idle_add (do_something_random, state);
+}
+
+int
+main (int argc, char **argv)
+{
+ DBusGConnection *bus;
+ DBusGProxy *server;
+ GError *error = NULL;
+ ClientState state;
+ GtkWidget *label;
+
+ gtk_init (&argc, &argv);
+
+ g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
+
+ state.window = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
+ gtk_window_set_resizable (GTK_WINDOW (state.window), TRUE);
+ g_signal_connect (G_OBJECT (state.window), "destroy",
+ G_CALLBACK (window_destroyed_cb),
+ &state);
+ gtk_window_set_title (GTK_WINDOW (state.window), _("D-BUS State Machine Demo"));
+ gtk_window_set_default_size (GTK_WINDOW (state.window), 320, 240);
+
+ label = gtk_label_new ("");
+ gtk_label_set_markup (GTK_LABEL (label), "<b>Loading...</b>");
+ gtk_widget_show (label);
+
+ gtk_container_add (GTK_CONTAINER (state.window), label);
+
+ bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+ if (!bus)
+ lose_gerror ("Couldn't connect to session bus", error);
+
+ state.bus = bus;
+
+ server = dbus_g_proxy_new_for_name_owner (bus,
+ "com.example.StateServer",
+ "/com/example/StateServer",
+ "com.example.StateMachineServer",
+ &error);
+ if (!server)
+ lose_gerror ("Couldn't find \"com.example.StateServer\"", error);
+
+ state.server_proxy = server;
+
+ g_signal_connect (server, "destroy",
+ G_CALLBACK (server_destroyed_cb),
+ &state);
+
+ dbus_g_object_register_marshaller (sm_marshal_VOID__STRING_BOXED,
+ G_TYPE_NONE, G_TYPE_STRING,
+ DBUS_TYPE_G_OBJECT_PATH);
+
+ dbus_g_proxy_add_signal (server, "MachineCreated", G_TYPE_STRING, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
+
+ dbus_g_proxy_connect_signal (server, "MachineCreated",
+ G_CALLBACK (machine_created_cb),
+ &state, NULL);
+
+ state.get_machines_call = dbus_g_proxy_begin_call (server, "GetMachines",
+ get_machines_cb, &state, NULL,
+ G_TYPE_INVALID);
+
+ gtk_widget_show (GTK_WIDGET (state.window));
+
+ gtk_main ();
+
+ g_object_unref (G_OBJECT (server));
+
+ exit(0);
+}