diff options
Diffstat (limited to 'glib/examples/statemachine/statemachine-client.c')
| -rw-r--r-- | glib/examples/statemachine/statemachine-client.c | 540 | 
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); +}  | 
