summaryrefslogtreecommitdiff
path: root/dbus/examples/statemachine
diff options
context:
space:
mode:
Diffstat (limited to 'dbus/examples/statemachine')
-rw-r--r--dbus/examples/statemachine/.cvsignore16
-rw-r--r--dbus/examples/statemachine/Makefile.am35
-rw-r--r--dbus/examples/statemachine/sm-marshal.list1
-rw-r--r--dbus/examples/statemachine/statemachine-client.c662
-rw-r--r--dbus/examples/statemachine/statemachine-server.c229
-rw-r--r--dbus/examples/statemachine/statemachine-server.h37
-rw-r--r--dbus/examples/statemachine/statemachine-server.xml14
-rw-r--r--dbus/examples/statemachine/statemachine.c353
-rw-r--r--dbus/examples/statemachine/statemachine.h77
-rw-r--r--dbus/examples/statemachine/statemachine.xml33
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>