diff options
Diffstat (limited to 'dbus/examples/statemachine')
-rw-r--r-- | dbus/examples/statemachine/.cvsignore | 16 | ||||
-rw-r--r-- | dbus/examples/statemachine/Makefile.am | 35 | ||||
-rw-r--r-- | dbus/examples/statemachine/sm-marshal.list | 1 | ||||
-rw-r--r-- | dbus/examples/statemachine/statemachine-client.c | 662 | ||||
-rw-r--r-- | dbus/examples/statemachine/statemachine-server.c | 229 | ||||
-rw-r--r-- | dbus/examples/statemachine/statemachine-server.h | 37 | ||||
-rw-r--r-- | dbus/examples/statemachine/statemachine-server.xml | 14 | ||||
-rw-r--r-- | dbus/examples/statemachine/statemachine.c | 353 | ||||
-rw-r--r-- | dbus/examples/statemachine/statemachine.h | 77 | ||||
-rw-r--r-- | dbus/examples/statemachine/statemachine.xml | 33 |
10 files changed, 1457 insertions, 0 deletions
diff --git a/dbus/examples/statemachine/.cvsignore b/dbus/examples/statemachine/.cvsignore new file mode 100644 index 0000000..6bf7421 --- /dev/null +++ b/dbus/examples/statemachine/.cvsignore @@ -0,0 +1,16 @@ +.deps +.libs +Makefile +Makefile.in +*.lo +*.la +statemachine-client +statemachine-server +statemachine-glue.h +statemachine-server-glue.h +run-with-tmp-session-bus.conf +sm-marshal.[ch] +*.bb +*.bbg +*.da +*.gcov diff --git a/dbus/examples/statemachine/Makefile.am b/dbus/examples/statemachine/Makefile.am new file mode 100644 index 0000000..7a6de0b --- /dev/null +++ b/dbus/examples/statemachine/Makefile.am @@ -0,0 +1,35 @@ +INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_GTK_THREADS_CFLAGS) -DDBUS_COMPILATION + +## Makefile.am bits for sample client/server pair + +noinst_PROGRAMS= statemachine-server + +#if HAVE_GTK +#noinst_PROGRAMS += statemachine-client +#endif + +EXTRA_DIST = statemachine.h statemachine-server.h sm-marshal.list statemachine-server.xml statemachine.xml + +statemachine_server_SOURCES= statemachine-server.c sm-marshal.c statemachine.c +statemachine_server_LDADD= $(top_builddir)/glib/libdbus-glib-1.la + +#statemachine_client_SOURCES= statemachine-client.c sm-marshal.c statemachine.h +#statemachine_client_LDADD= $(top_builddir)/glib/libdbus-glib-1.la $(DBUS_GTK_THREADS_LIBS) + +BUILT_SOURCES = statemachine-server-glue.h statemachine-glue.h + +statemachine-server-glue.h: statemachine-server.xml + $(LIBTOOL) --mode=execute $(top_builddir)/glib/dbus-binding-tool --prefix=sm_server --mode=glib-server --output=$@ $< + +statemachine-glue.h: statemachine.xml + $(LIBTOOL) --mode=execute $(top_builddir)/glib/dbus-binding-tool --prefix=sm_object --mode=glib-server --output=$@ $< + +sm-marshal.c: Makefile sm-marshal.list + @GLIB_GENMARSHAL@ --prefix=sm_marshal $(srcdir)/sm-marshal.list --header --body > $@.tmp && mv $@.tmp $@ + +sm-marshal.h: Makefile sm-marshal.list + @GLIB_GENMARSHAL@ --prefix=sm_marshal $(srcdir)/sm-marshal.list --header > $@.tmp && mv $@.tmp $@ + +BUILT_SOURCES += sm-marshal.c sm-marshal.h + +CLEANFILES = $(BUILT_SOURCES) diff --git a/dbus/examples/statemachine/sm-marshal.list b/dbus/examples/statemachine/sm-marshal.list new file mode 100644 index 0000000..e72aa4b --- /dev/null +++ b/dbus/examples/statemachine/sm-marshal.list @@ -0,0 +1 @@ +VOID:STRING,BOXED diff --git a/dbus/examples/statemachine/statemachine-client.c b/dbus/examples/statemachine/statemachine-client.c new file mode 100644 index 0000000..88b2184 --- /dev/null +++ b/dbus/examples/statemachine/statemachine-client.c @@ -0,0 +1,662 @@ +#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; + gdouble progress; + DBusGProxy *proxy; + DBusGProxyCall *get_progress_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 +signal_row_change (ClientState *state, GtkTreeIter *iter) +{ + 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 +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; + signal_row_change (state, &iter); +} + +static void +set_proxy_acquisition_progress (ClientState *state, + DBusGProxy *proxy, + double progress) +{ + MachineInfo *info; + GtkTreeIter iter; + + if (!proxy_to_iter (state->store, proxy, &iter)) + g_assert_not_reached (); + gtk_tree_model_get (state->store, &iter, 0, &info, -1); + + /* Ignore machines in unknown state */ + if (!info->state) + return; + + if (strcmp (info->state, "Acquired")) + lose ("Got AcquisitionProgress signal in bad state %s", + info->state); + + g_print ("Got acquisition progress change for %p (%s) to %f\n", proxy, info->name ? info->name : "(unknown)", progress); + + info->progress = progress; + + signal_row_change (state, &iter); +} + +static void +proxy_acquisition_changed_cb (DBusGProxy *proxy, + double progress, + gpointer user_data) +{ + set_proxy_acquisition_progress (user_data, proxy, progress); +} + +static void +get_acquiring_progress_cb (DBusGProxy *proxy, + DBusGProxyCall *call, + gpointer user_data) +{ + GError *error = NULL; + MachineInfo *info; + GtkTreeIter iter; + ClientState *state = user_data; + gdouble progress; + + if (!proxy_to_iter (state->store, proxy, &iter)) + g_assert_not_reached (); + gtk_tree_model_get (state->store, &iter, 0, &info, -1); + + g_assert (info->get_progress_call == call); + + if (!dbus_g_proxy_end_call (proxy, call, &error, + G_TYPE_DOUBLE, &progress, G_TYPE_INVALID)) + lose_gerror ("Failed to complete GetAcquiringProgress call", error); + info->get_progress_call = NULL; + + set_proxy_acquisition_progress (state, proxy, progress); +} + +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); + + g_free (info->state); + info->state = g_strdup (statename); + + if (!strcmp (info->state, "Acquired")) + { + g_print ("Starting GetAcquiringProgress call for %p\n", info->proxy); + if (info->get_progress_call != NULL) + { + dbus_g_proxy_cancel_call (info->proxy, info->get_progress_call); + info->get_progress_call = NULL; + } + info->get_progress_call = + dbus_g_proxy_begin_call (info->proxy, "GetAcquiringProgress", + get_acquiring_progress_cb, + state, NULL, + G_TYPE_INVALID); + } + else + info->progress = 0.0; + + signal_row_change (state, &iter); +} + +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->progress = 0.0; + + 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); + dbus_g_proxy_begin_call (info->proxy, "GetInfo", + get_machine_info_cb, + state, NULL, + G_TYPE_INVALID); + } + + /* 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); + + dbus_g_proxy_add_signal (info->proxy, "AcquisitionProgress", + G_TYPE_DOUBLE, G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (info->proxy, + "AcquisitionProgress", + G_CALLBACK (proxy_acquisition_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 if (!strcmp (info->state, "Loading")) + { + + g_print ("Sending Reacquire request to machine %s\n", info->name); + dbus_g_proxy_call_no_reply (info->proxy, "Reacquire", 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 (2000, 5000), 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, 3)) + { + case 0: + send_create_machine (state); + break; + case 1: + case 2: + 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 >= 5) + { + g_print ("MAX children reached, switching to state changes only\n"); + 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 +set_cell_progress (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, "value", (int) (info->progress * 100), NULL); +} + +static gint +sort_by_progress (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 info_a->progress > info_b->progress ? 1 : -1; +} + +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); + + rend = gtk_cell_renderer_progress_new (); + col = gtk_tree_view_column_new_with_attributes (_("Progress"), + rend, + NULL); + gtk_tree_view_column_set_cell_data_func (col, rend, set_cell_progress, NULL, NULL); + gtk_tree_view_column_set_resizable (col, TRUE); + gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (state->store), + 0, sort_by_progress, 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); +} diff --git a/dbus/examples/statemachine/statemachine-server.c b/dbus/examples/statemachine/statemachine-server.c new file mode 100644 index 0000000..cc9ca4b --- /dev/null +++ b/dbus/examples/statemachine/statemachine-server.c @@ -0,0 +1,229 @@ +#include <dbus/dbus-glib.h> +#include <stdio.h> +#include <stdlib.h> + +#include "statemachine.h" +#include "sm-marshal.h" +#include "statemachine-server.h" + +enum +{ + PROP_O, + PROP_BUS +}; + +enum +{ + MACHINE_CREATED, + LAST_SIGNAL +}; + +static guint sm_server_signals[LAST_SIGNAL] = { 0 }; + +static void sm_server_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void sm_server_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +G_DEFINE_TYPE(SMServer, sm_server, G_TYPE_OBJECT) + +#include "statemachine-server-glue.h" +#include "statemachine-glue.h" + +static void +sm_server_init (SMServer *obj) +{ + obj->machines = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); +} + +static void +sm_server_class_init (SMServerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = sm_server_set_property; + object_class->get_property = sm_server_get_property; + + g_object_class_install_property (object_class, + PROP_BUS, + g_param_spec_boxed ("bus", + "bus", + "bus", + DBUS_TYPE_G_CONNECTION, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + sm_server_signals[MACHINE_CREATED] = + g_signal_new ("machine-created", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + sm_marshal_VOID__STRING_BOXED, + G_TYPE_NONE, 2, G_TYPE_STRING, DBUS_TYPE_G_OBJECT_PATH); +} + +static void +sm_server_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SMServer *server = SM_SERVER (object); + + switch (prop_id) + { + case PROP_BUS: + server->bus = g_value_get_boxed (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +sm_server_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SMServer *server = SM_SERVER (object); + + switch (prop_id) + { + case PROP_BUS: + g_value_set_boxed (value, server->bus); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +machine_state_changed_cb (SMObject *obj, const char *state, gpointer data) +{ + char *name; + + g_object_get (obj, "name", &name, NULL); + g_print ("Machine %s switching to state %s\n", name, state); + g_free (name); +} + +static void +machine_acquisition_changed_cb (SMObject *obj, gdouble progress, gpointer data) +{ + char *name; + + g_object_get (obj, "name", &name, NULL); + g_print ("Machine %s got progress %f\n", name, progress); + g_free (name); +} + +gboolean +sm_server_create_machine (SMServer *server, const char *name, GError **error) +{ + SMObject *machine; + char *path; + + machine = g_hash_table_lookup (server->machines, name); + if (machine != NULL) + { + g_set_error (error, + SM_ERROR, + SM_ERROR_NAME_IN_USE, + "Statemachine name \"%s\" is already in use", + name); + return FALSE; + } + + machine = g_object_new (SM_TYPE_OBJECT, "name", name, NULL); + + path = g_strdup_printf ("/com/example/StateMachines/%s", name); + dbus_g_connection_register_g_object (server->bus, path, G_OBJECT (machine)); + + g_hash_table_insert (server->machines, g_strdup (name), machine); + + g_print ("Created state machine with name %s at %s\n", name, path); + + g_signal_connect_object (machine, "state-changed", + G_CALLBACK (machine_state_changed_cb), + NULL, 0); + g_signal_connect_object (machine, "acquisition-progress", + G_CALLBACK (machine_acquisition_changed_cb), + NULL, 0); + + g_signal_emit (server, sm_server_signals[MACHINE_CREATED], 0, name, path); + + return TRUE; +} + +static void +add_machine_to_ptr_array (gpointer key, gpointer val, gpointer data) +{ + const char *name = key; + /* SMObject *sm = val; */ + GPtrArray *ptrarray = data; + + g_ptr_array_add (ptrarray, g_strdup_printf ("/com/example/StateMachines/%s", + name)); +} + +gboolean +sm_server_get_machines (SMServer *server, GPtrArray **machines, GError **error) +{ + *machines = g_ptr_array_new (); + + g_hash_table_foreach (server->machines, add_machine_to_ptr_array, *machines); + + return TRUE; +} + +int +main (int argc, char **argv) +{ + DBusGConnection *bus; + DBusGProxy *bus_proxy; + GError *error = NULL; + SMServer *server; + GMainLoop *mainloop; + guint request_name_result; + + g_type_init (); + + dbus_g_object_type_install_info (SM_TYPE_SERVER, &dbus_glib_sm_server_object_info); + dbus_g_object_type_install_info (SM_TYPE_OBJECT, &dbus_glib_sm_object_object_info); + dbus_g_error_domain_register (SM_ERROR, NULL, SM_TYPE_ERROR); + + mainloop = g_main_loop_new (NULL, FALSE); + + bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (!bus) + g_critical ("Couldn't connect to session bus: %s\n", error->message); + + bus_proxy = dbus_g_proxy_new_for_name (bus, "org.freedesktop.DBus", + "/org/freedesktop/DBus", + "org.freedesktop.DBus"); + + if (!dbus_g_proxy_call (bus_proxy, "RequestName", &error, + G_TYPE_STRING, "com.example.StateServer", + G_TYPE_UINT, 0, + G_TYPE_INVALID, + G_TYPE_UINT, &request_name_result, + G_TYPE_INVALID)) + g_critical ("Couldn't acquire com.example.StateServer: %s\n", error->message); + + server = g_object_new (SM_TYPE_SERVER, "bus", bus, NULL); + + dbus_g_connection_register_g_object (bus, "/com/example/StateServer", G_OBJECT (server)); + + g_print ("StateMachine server initialized\n"); + + g_main_loop_run (mainloop); + + exit (0); +} diff --git a/dbus/examples/statemachine/statemachine-server.h b/dbus/examples/statemachine/statemachine-server.h new file mode 100644 index 0000000..360be3a --- /dev/null +++ b/dbus/examples/statemachine/statemachine-server.h @@ -0,0 +1,37 @@ +#ifndef _SM_SERVER_H +#define _SM_SERVER_H + +#include "statemachine.h" +#include <dbus/dbus-glib.h> + +typedef struct SMServer SMServer; +typedef struct SMServerClass SMServerClass; + +struct SMServer +{ + GObject parent; + + /* Private */ + DBusGConnection *bus; + GHashTable *machines; +}; + +struct SMServerClass +{ + GObjectClass parent; +}; + +#define SM_TYPE_SERVER (sm_server_get_type ()) +#define SM_SERVER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SM_TYPE_SERVER, SMServer)) +#define SM_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SM_TYPE_SERVER, SMServerClass)) +#define SM_IS_SERVER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SM_TYPE_SERVER)) +#define SM_IS_SERVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SM_TYPE_SERVER)) +#define SM_SERVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SM_TYPE_SERVER, SMServerClass)) + +GType sm_server_get_type (void); + +gboolean sm_server_create_machine (SMServer *server, const char *name, GError **error); + +gboolean sm_server_get_machines (SMServer *server, GPtrArray **machines, GError **error); + +#endif diff --git a/dbus/examples/statemachine/statemachine-server.xml b/dbus/examples/statemachine/statemachine-server.xml new file mode 100644 index 0000000..fe9caaf --- /dev/null +++ b/dbus/examples/statemachine/statemachine-server.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/"> + <interface name="com.example.StateMachineServer"> + <method name="CreateMachine"> + <arg type="s" name="name" direction="in"/> + </method> + + <method name="GetMachines"> + <arg type="ao" direction="out"/> + </method> + <signal name="MachineCreated"/> + </interface> +</node> diff --git a/dbus/examples/statemachine/statemachine.c b/dbus/examples/statemachine/statemachine.c new file mode 100644 index 0000000..c94e294 --- /dev/null +++ b/dbus/examples/statemachine/statemachine.c @@ -0,0 +1,353 @@ +#include <stdio.h> +#include <stdlib.h> +#include "statemachine.h" + +static void clear_pending_tasks (SMObject *object); +static void state_change (SMObject *object, SMObjectState new_state); +static void sm_object_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void sm_object_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +enum +{ + PROP_0, + PROP_NAME, +}; + +enum +{ + STATE_CHANGED, + ACQUISITION_FAILED, + ACQUISITION_PROGRESS, + LAST_SIGNAL +}; + +static guint sm_object_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE(SMObject, sm_object, G_TYPE_OBJECT) + +static void +sm_object_init (SMObject *obj) +{ + obj->state = SM_OBJECT_STATE_SHUTDOWN; +} + +static void +sm_object_class_init (SMObjectClass *klass) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = sm_object_set_property; + object_class->get_property = sm_object_get_property; + + g_object_class_install_property (object_class, + PROP_NAME, + g_param_spec_string ("name", + "name", + "name", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + sm_object_signals[STATE_CHANGED] = + g_signal_new ("state-changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + sm_object_signals[ACQUISITION_PROGRESS] = + g_signal_new ("acquisition-progress", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__DOUBLE, + G_TYPE_NONE, 1, G_TYPE_DOUBLE); + sm_object_signals[ACQUISITION_FAILED] = + g_signal_new ("acquisition-failed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +/* This should really be standard. */ +#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } + +GQuark +sm_error_quark (void) +{ + static GQuark ret = 0; + if (!ret) + ret = g_quark_from_static_string ("SMObjectErrorQuark"); + return ret; +} + +GType +sm_object_state_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) + { + static const GEnumValue values[] = + { + + ENUM_ENTRY (SM_OBJECT_STATE_SHUTDOWN, "Shutdown"), + ENUM_ENTRY (SM_OBJECT_STATE_INITIALIZED, "Loading"), + ENUM_ENTRY (SM_OBJECT_STATE_ACQUIRED, "Acquired"), + ENUM_ENTRY (SM_OBJECT_STATE_OPERATING, "Operating"), + { 0, 0, 0 } + }; + + etype = g_enum_register_static ("SMObjectState", values); + } + + return etype; +} + +GType +sm_error_get_type (void) +{ + static GType etype = 0; + + if (etype == 0) + { + static const GEnumValue values[] = + { + + ENUM_ENTRY (SM_ERROR_INVALID_STATE, "InvalidState"), + ENUM_ENTRY (SM_ERROR_NAME_IN_USE, "NameInUse"), + { 0, 0, 0 } + }; + + g_assert (SM_NUM_ERRORS == G_N_ELEMENTS (values) - 1); + + etype = g_enum_register_static ("SMError", values); + } + + return etype; +} + +static void +sm_object_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + SMObject *sm = SM_OBJECT (object); + + switch (prop_id) + { + case PROP_NAME: + sm->name = g_strdup (g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +sm_object_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + SMObject *sm= SM_OBJECT (object); + + switch (prop_id) + { + case PROP_NAME: + g_value_set_string (value, sm->name); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static const char * +state_to_string (SMObjectState state) +{ + GEnumValue *value; + GEnumClass *prop_class; + const char *ret; + + prop_class = g_type_class_ref (SM_TYPE_OBJECT_STATE); + value = g_enum_get_value (prop_class, state); + ret = value->value_nick; + + g_type_class_unref (prop_class); + return ret; +} + +static void +queue_task (SMObject *object, guint delay, GSourceFunc func) +{ + guint id; + id = g_timeout_add (delay, func, object); + object->pending_tasks = g_slist_prepend (object->pending_tasks, GUINT_TO_POINTER (id)); +} + +static gboolean +idle_state_change (gpointer data) +{ + SMObject *object = data; + + g_print ("doing idle state change for %s to %s\n", + object->name, state_to_string (object->requested_state)); + state_change (object, object->requested_state); + return FALSE; +} + +static gboolean +idle_further_acquire (gpointer data) +{ + SMObject *object = data; + + g_print ("doing idle acquisition for machine %s\n", object->name); + object->acquisition_progress += g_random_double_range (0.20, 0.7); + if (object->acquisition_progress > 1.0) + { + object->acquisition_progress = 1.0; + return FALSE; + } + else + { + g_signal_emit (object, sm_object_signals[ACQUISITION_PROGRESS], 0, object->acquisition_progress); + return TRUE; + } +} + +static void +clear_pending_tasks (SMObject *object) +{ + GSList *tmp; + for (tmp = object->pending_tasks; tmp; tmp = tmp->next) + g_source_remove (GPOINTER_TO_UINT (tmp->data)); + g_slist_free (object->pending_tasks); + object->pending_tasks = NULL; +} + +static void +state_change (SMObject *object, SMObjectState new_state) +{ + g_signal_emit (object, sm_object_signals[STATE_CHANGED], 0, + state_to_string (new_state)); + + clear_pending_tasks (object); + + if (new_state == SM_OBJECT_STATE_ACQUIRED) + { + object->acquisition_progress = 0.0; + queue_task (object, 1000, idle_further_acquire); + } + else if (new_state == SM_OBJECT_STATE_INITIALIZED) + { + if (g_random_int_range (0, 2) == 0) + { + object->requested_state = SM_OBJECT_STATE_ACQUIRED; + queue_task (object, 3000, idle_state_change); + } + } + + object->state = new_state; +} + +gboolean +sm_object_get_info (SMObject *object, char **name, char **state, GError **error) +{ + *name= g_strdup (object->name); + *state = g_strdup (state_to_string (object->state)); + return TRUE; +} + +gboolean +sm_object_start (SMObject *object, GError **error) +{ + if (object->state != SM_OBJECT_STATE_SHUTDOWN) + { + g_set_error (error, + SM_ERROR, + SM_ERROR_INVALID_STATE, + "%s", + "Can't start from non-shutdown state"); + return FALSE; + } + state_change (object, SM_OBJECT_STATE_INITIALIZED); + return TRUE; +} + +gboolean +sm_object_shutdown (SMObject *object, GError **error) +{ + if (object->state == SM_OBJECT_STATE_SHUTDOWN) + { + g_set_error (error, + SM_ERROR, + SM_ERROR_INVALID_STATE, + "%s", + "Can't shutdown from shutdown state"); + return FALSE; + } + state_change (object, SM_OBJECT_STATE_SHUTDOWN); + return TRUE; +} + +gboolean +sm_object_reinitialize (SMObject *object, GError **error) +{ + if (object->state != SM_OBJECT_STATE_ACQUIRED + && object->state != SM_OBJECT_STATE_OPERATING) + { + g_set_error (error, + SM_ERROR, + SM_ERROR_INVALID_STATE, + "Can't reinitialize from state %d", + object->state); + return FALSE; + } + state_change (object, SM_OBJECT_STATE_INITIALIZED); + return TRUE; +} + +gboolean +sm_object_reacquire (SMObject *object, GError **error) +{ + if (object->state == SM_OBJECT_STATE_ACQUIRED) + { + g_set_error (error, + SM_ERROR, + SM_ERROR_INVALID_STATE, + "Can't reacquire from state %d", + object->state); + return FALSE; + } + state_change (object, SM_OBJECT_STATE_ACQUIRED); + return TRUE; +} + +gboolean +sm_object_get_acquiring_progress (SMObject *object, gdouble *out, GError **error) +{ + if (object->state != SM_OBJECT_STATE_ACQUIRED) + { + g_set_error (error, + SM_ERROR, + SM_ERROR_INVALID_STATE, + "Can't get progress from state %d", + object->state); + return FALSE; + } + *out = object->acquisition_progress; + return TRUE; +} diff --git a/dbus/examples/statemachine/statemachine.h b/dbus/examples/statemachine/statemachine.h new file mode 100644 index 0000000..ac6047b --- /dev/null +++ b/dbus/examples/statemachine/statemachine.h @@ -0,0 +1,77 @@ +#ifndef _SM_OBJECT_H +#define _SM_OBJECT_H + +#include <glib.h> +#include <glib-object.h> + +GQuark sm_error_quark (void); + +#define SM_ERROR (sm_error_quark ()) + +typedef enum +{ + SM_ERROR_INVALID_STATE = 0, + SM_ERROR_NAME_IN_USE, + SM_NUM_ERRORS +} SMError; + +GType sm_error_get_type (void); +#define SM_TYPE_ERROR (sm_error_get_type ()) + +typedef enum +{ + SM_OBJECT_STATE_SHUTDOWN = 0, + SM_OBJECT_STATE_INITIALIZED, + SM_OBJECT_STATE_ACQUIRED, + SM_OBJECT_STATE_OPERATING, + SM_OBJECT_NUM_STATES +} SMObjectState; + +GType sm_object_state_get_type (void); + +#define SM_TYPE_OBJECT_STATE (sm_object_state_get_type ()) + +typedef struct SMObject SMObject; +typedef struct SMObjectClass SMObjectClass; + +struct SMObject +{ + GObject parent; + + /* Private */ + char *name; + SMObjectState state; + double acquisition_progress; + + GSList /* guint */ *pending_tasks; + + SMObjectState requested_state; +}; + +struct SMObjectClass +{ + GObjectClass parent; +}; + +#define SM_TYPE_OBJECT (sm_object_get_type ()) +#define SM_OBJECT(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SM_TYPE_OBJECT, SMObject)) +#define SM_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SM_TYPE_OBJECT, SMObjectClass)) +#define SM_IS_OBJECT(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SM_TYPE_OBJECT)) +#define SM_IS_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SM_TYPE_OBJECT)) +#define SM_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SM_TYPE_OBJECT, SMObjectClass)) + +GType sm_object_get_type (void); + +gboolean sm_object_get_info (SMObject *object, char **name, char **state, GError **error); + +gboolean sm_object_start (SMObject *object, GError **error); + +gboolean sm_object_shutdown (SMObject *object, GError **error); + +gboolean sm_object_reinitialize (SMObject *object, GError **error); + +gboolean sm_object_reacquire (SMObject *object, GError **error); + +gboolean sm_object_get_acquiring_progress (SMObject *object, gdouble *out, GError **error); + +#endif diff --git a/dbus/examples/statemachine/statemachine.xml b/dbus/examples/statemachine/statemachine.xml new file mode 100644 index 0000000..c375ef1 --- /dev/null +++ b/dbus/examples/statemachine/statemachine.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/"> + <interface name="com.example.StateMachine"> + + <method name="GetInfo"> + <arg type="s" name="name" direction="out"/> + <arg type="s" name="state" direction="out"/> + </method> + + <method name="Start"> + </method> + + <method name="Shutdown"> + </method> + + <method name="Reinitialize"> + </method> + + <method name="Reacquire"> + </method> + + <method name="GetAcquiringProgress"> + <arg type="d" direction="out"/> + </method> + + <!-- Mark the signals as exported --> + <signal name="StateChanged"/> + <signal name="AcquisitionFailed"/> + <signal name="AcquisitionProgress"/> + + </interface> +</node> |