00001 #include <dbus/dbus-glib.h>
00002 #include <stdio.h>
00003 #include <stdlib.h>
00004 #include <string.h>
00005 #include <gtk/gtk.h>
00006 #include <glib/gi18n.h>
00007 #include "sm-marshal.h"
00008
00009 static void lose (const char *fmt, ...) G_GNUC_NORETURN G_GNUC_PRINTF (1, 2);
00010 static void lose_gerror (const char *prefix, GError *error) G_GNUC_NORETURN;
00011
00012 static void
00013 lose (const char *str, ...)
00014 {
00015 va_list args;
00016 GtkWidget *dialog;
00017 char *text;
00018
00019 va_start (args, str);
00020
00021 text = g_strdup_vprintf (str, args);
00022
00023 va_end (args);
00024
00025 dialog = gtk_message_dialog_new (NULL,
00026 GTK_DIALOG_DESTROY_WITH_PARENT,
00027 GTK_MESSAGE_ERROR,
00028 GTK_BUTTONS_CLOSE,
00029 "%s",
00030 text);
00031 gtk_dialog_run (GTK_DIALOG (dialog));
00032
00033 g_free (text);
00034
00035 exit (1);
00036 }
00037
00038 static void
00039 lose_gerror (const char *prefix, GError *error)
00040 {
00041 GtkWidget *dialog;
00042
00043 dialog = gtk_message_dialog_new (NULL,
00044 GTK_DIALOG_DESTROY_WITH_PARENT,
00045 GTK_MESSAGE_ERROR,
00046 GTK_BUTTONS_CLOSE,
00047 "%s",
00048 prefix);
00049 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
00050 "%s",
00051 error->message);
00052
00053 gtk_dialog_run (GTK_DIALOG (dialog));
00054
00055 exit (1);
00056 }
00057
00058 typedef struct
00059 {
00060 char *name;
00061 char *state;
00062 gdouble progress;
00063 DBusGProxy *proxy;
00064 DBusGProxyCall *get_progress_call;
00065 } MachineInfo;
00066
00067 typedef struct
00068 {
00069 GtkWindow *window;
00070 GtkWidget *view;
00071 GtkTreeModel *store;
00072
00073 DBusGConnection *bus;
00074 DBusGProxy *server_proxy;
00075
00076 GSList *pending_creation_calls;
00077 DBusGProxyCall *get_machines_call;
00078 } ClientState;
00079
00080 static gboolean
00081 proxy_to_iter (GtkTreeModel *model, DBusGProxy *proxy, GtkTreeIter *iter)
00082 {
00083 if (!gtk_tree_model_get_iter_first (model, iter))
00084 return FALSE;
00085 do {
00086 MachineInfo *info;
00087 gtk_tree_model_get (model, iter, 0, &info, -1);
00088 if (info->proxy == proxy)
00089 return TRUE;
00090 } while (gtk_tree_model_iter_next (model, iter));
00091 return FALSE;
00092 }
00093
00094 static void
00095 signal_row_change (ClientState *state, GtkTreeIter *iter)
00096 {
00097 GtkTreePath *path;
00098 path = gtk_tree_model_get_path (state->store, iter);
00099 gtk_tree_model_row_changed (state->store, path, iter);
00100 gtk_tree_path_free (path);
00101 }
00102
00103 static void
00104 get_machine_info_cb (DBusGProxy *proxy,
00105 DBusGProxyCall *call,
00106 gpointer data)
00107 {
00108 GtkTreeIter iter;
00109 ClientState *state = data;
00110 GError *error = NULL;
00111 char *name, *statename;
00112 MachineInfo *info;
00113
00114 if (!dbus_g_proxy_end_call (proxy, call, &error,
00115 G_TYPE_STRING, &name,
00116 G_TYPE_STRING, &statename,
00117 G_TYPE_INVALID))
00118 lose_gerror ("Couldn't complete GetInfo", error);
00119
00120 if (!proxy_to_iter (state->store, proxy, &iter))
00121 g_assert_not_reached ();
00122
00123 gtk_tree_model_get (state->store, &iter, 0, &info, -1);
00124 g_free (info->name);
00125 info->name = name;
00126 g_free (info->state);
00127 info->state = statename;
00128 signal_row_change (state, &iter);
00129 }
00130
00131 static void
00132 set_proxy_acquisition_progress (ClientState *state,
00133 DBusGProxy *proxy,
00134 double progress)
00135 {
00136 MachineInfo *info;
00137 GtkTreeIter iter;
00138
00139 if (!proxy_to_iter (state->store, proxy, &iter))
00140 g_assert_not_reached ();
00141 gtk_tree_model_get (state->store, &iter, 0, &info, -1);
00142
00143
00144 if (!info->state)
00145 return;
00146
00147 if (strcmp (info->state, "Acquired"))
00148 lose ("Got AcquisitionProgress signal in bad state %s",
00149 info->state);
00150
00151 g_print ("Got acquisition progress change for %p (%s) to %f\n", proxy, info->name ? info->name : "(unknown)", progress);
00152
00153 info->progress = progress;
00154
00155 signal_row_change (state, &iter);
00156 }
00157
00158 static void
00159 proxy_acquisition_changed_cb (DBusGProxy *proxy,
00160 double progress,
00161 gpointer user_data)
00162 {
00163 set_proxy_acquisition_progress (user_data, proxy, progress);
00164 }
00165
00166 static void
00167 get_acquiring_progress_cb (DBusGProxy *proxy,
00168 DBusGProxyCall *call,
00169 gpointer user_data)
00170 {
00171 GError *error = NULL;
00172 MachineInfo *info;
00173 GtkTreeIter iter;
00174 ClientState *state = user_data;
00175 gdouble progress;
00176
00177 if (!proxy_to_iter (state->store, proxy, &iter))
00178 g_assert_not_reached ();
00179 gtk_tree_model_get (state->store, &iter, 0, &info, -1);
00180
00181 g_assert (info->get_progress_call == call);
00182
00183 if (!dbus_g_proxy_end_call (proxy, call, &error,
00184 G_TYPE_DOUBLE, &progress, G_TYPE_INVALID))
00185 lose_gerror ("Failed to complete GetAcquiringProgress call", error);
00186 info->get_progress_call = NULL;
00187
00188 set_proxy_acquisition_progress (state, proxy, progress);
00189 }
00190
00191 static void
00192 proxy_state_changed_cb (DBusGProxy *proxy,
00193 const char *statename,
00194 gpointer user_data)
00195 {
00196 MachineInfo *info;
00197 GtkTreeIter iter;
00198 ClientState *state = user_data;
00199
00200 if (!proxy_to_iter (state->store, proxy, &iter))
00201 g_assert_not_reached ();
00202 gtk_tree_model_get (state->store, &iter, 0, &info, -1);
00203
00204 g_print ("Got state change for %p (%s) to %s\n", proxy, info->name ? info->name : "(unknown)", statename);
00205
00206 g_free (info->state);
00207 info->state = g_strdup (statename);
00208
00209 if (!strcmp (info->state, "Acquired"))
00210 {
00211 g_print ("Starting GetAcquiringProgress call for %p\n", info->proxy);
00212 if (info->get_progress_call != NULL)
00213 {
00214 dbus_g_proxy_cancel_call (info->proxy, info->get_progress_call);
00215 info->get_progress_call = NULL;
00216 }
00217 info->get_progress_call =
00218 dbus_g_proxy_begin_call (info->proxy, "GetAcquiringProgress",
00219 get_acquiring_progress_cb,
00220 state, NULL,
00221 G_TYPE_INVALID);
00222 }
00223 else
00224 info->progress = 0.0;
00225
00226 signal_row_change (state, &iter);
00227 }
00228
00229 static void
00230 add_machine (ClientState *state,
00231 const char *name,
00232 const char *mstate,
00233 const char *path)
00234 {
00235 MachineInfo *info;
00236 GtkTreeIter iter;
00237
00238 info = g_new0 (MachineInfo, 1);
00239 info->name = g_strdup (name);
00240 info->state = g_strdup (mstate);
00241 info->progress = 0.0;
00242
00243 info->proxy = dbus_g_proxy_new_for_name (state->bus,
00244 "com.example.StateServer",
00245 path,
00246 "com.example.StateMachine");
00247
00248 if (!info->state)
00249 {
00250 g_print ("Starting GetInfo call for %p\n", info->proxy);
00251 dbus_g_proxy_begin_call (info->proxy, "GetInfo",
00252 get_machine_info_cb,
00253 state, NULL,
00254 G_TYPE_INVALID);
00255 }
00256
00257
00258 dbus_g_proxy_add_signal (info->proxy, "StateChanged",
00259 G_TYPE_STRING, G_TYPE_INVALID);
00260
00261 dbus_g_proxy_connect_signal (info->proxy,
00262 "StateChanged",
00263 G_CALLBACK (proxy_state_changed_cb),
00264 state,
00265 NULL);
00266
00267 dbus_g_proxy_add_signal (info->proxy, "AcquisitionProgress",
00268 G_TYPE_DOUBLE, G_TYPE_INVALID);
00269
00270 dbus_g_proxy_connect_signal (info->proxy,
00271 "AcquisitionProgress",
00272 G_CALLBACK (proxy_acquisition_changed_cb),
00273 state,
00274 NULL);
00275
00276 gtk_list_store_prepend (GTK_LIST_STORE (state->store), &iter);
00277 gtk_list_store_set (GTK_LIST_STORE (state->store), &iter, 0, info, -1);
00278
00279 }
00280
00281 static void
00282 machine_created_cb (DBusGProxy *proxy,
00283 const char *name,
00284 const char *path,
00285 gpointer data)
00286 {
00287 ClientState *state = data;
00288
00289 add_machine (state, name, NULL, path);
00290 }
00291
00292 static void
00293 server_destroyed_cb (DBusGProxy *proxy, gpointer data)
00294 {
00295 g_print ("Server terminated!\n");
00296 GtkWidget *dialog;
00297
00298 dialog = gtk_message_dialog_new (NULL,
00299 GTK_DIALOG_DESTROY_WITH_PARENT,
00300 GTK_MESSAGE_INFO,
00301 GTK_BUTTONS_CLOSE,
00302 "State Machine server has exited");
00303
00304 gtk_dialog_run (GTK_DIALOG (dialog));
00305
00306 exit (1);
00307 }
00308
00309 static void
00310 window_destroyed_cb (GtkWidget *window, gpointer data)
00311 {
00312 gtk_main_quit ();
00313 }
00314
00315 static void
00316 create_machine_completed_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
00317 {
00318 GError *error = NULL;
00319 ClientState *state = data;
00320
00321 if (!dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID))
00322 {
00323
00324 if (dbus_g_error_has_name (error, "com.example.StateServer.NameInUse"))
00325 ;
00326 else
00327 lose_gerror ("Failed to create new state machine", error);
00328 }
00329 g_print ("machine created successfully\n");
00330 state->pending_creation_calls = g_slist_remove (state->pending_creation_calls, call);
00331 }
00332
00333 static void
00334 send_create_machine (ClientState *state)
00335 {
00336 DBusGProxyCall *call;
00337 char *name;
00338 gint n_children;
00339
00340 n_children = gtk_tree_model_iter_n_children (state->store, NULL);
00341 name = g_strdup_printf ("machine%d", n_children);
00342
00343 g_print ("Invoking CreateMachine(%s)\n", name);
00344 call = dbus_g_proxy_begin_call (state->server_proxy, "CreateMachine",
00345 create_machine_completed_cb,
00346 state, NULL,
00347 G_TYPE_STRING, name, G_TYPE_INVALID);
00348 g_free (name);
00349 state->pending_creation_calls = g_slist_prepend (state->pending_creation_calls, call);
00350 }
00351
00352 static void
00353 do_a_state_change (ClientState *state)
00354 {
00355 gint index;
00356 GtkTreeIter iter;
00357 gint n_children;
00358 MachineInfo *info;
00359
00360 n_children = gtk_tree_model_iter_n_children (state->store, NULL);
00361 if (n_children == 0)
00362 {
00363 g_print ("No machines yet, not doing a state switch\n");
00364 return;
00365 }
00366
00367 index = g_random_int_range (0, n_children);
00368 gtk_tree_model_iter_nth_child (state->store, &iter, NULL, index);
00369 gtk_tree_model_get (state->store, &iter, 0, &info, -1);
00370
00371 if (!info->state)
00372 {
00373 g_print ("Machine not yet in known state, skipping state switch\n");
00374 return;
00375 }
00376
00377 if (!strcmp (info->state, "Shutdown"))
00378 {
00379 g_print ("Sending Start request to machine %s\n", info->name);
00380 dbus_g_proxy_call_no_reply (info->proxy, "Start", G_TYPE_INVALID);
00381 }
00382 else if (!strcmp (info->state, "Loading"))
00383 {
00384
00385 g_print ("Sending Reacquire request to machine %s\n", info->name);
00386 dbus_g_proxy_call_no_reply (info->proxy, "Reacquire", G_TYPE_INVALID);
00387 }
00388 else
00389 {
00390 g_print ("Sending Shutdown request to machine %s\n", info->name);
00391 dbus_g_proxy_call_no_reply (info->proxy, "Shutdown", G_TYPE_INVALID);
00392 }
00393 }
00394
00395 static gboolean
00396 do_something_random_2 (gpointer data)
00397 {
00398 ClientState *state = data;
00399 do_a_state_change (state);
00400 g_timeout_add (g_random_int_range (2000, 5000), do_something_random_2, state);
00401 return FALSE;
00402 }
00403
00404 static gboolean
00405 do_something_random (gpointer data)
00406 {
00407 ClientState *state = data;
00408 gint n_children;
00409
00410 switch (g_random_int_range (0, 3))
00411 {
00412 case 0:
00413 send_create_machine (state);
00414 break;
00415 case 1:
00416 case 2:
00417 do_a_state_change (state);
00418 break;
00419 default:
00420 g_assert_not_reached ();
00421 }
00422
00423 n_children = gtk_tree_model_iter_n_children (state->store, NULL);
00424 if (n_children >= 5)
00425 {
00426 g_print ("MAX children reached, switching to state changes only\n");
00427 g_timeout_add (g_random_int_range (500, 3000), do_something_random_2, state);
00428 }
00429 else
00430 g_timeout_add (g_random_int_range (500, 3000), do_something_random, state);
00431 return FALSE;
00432 }
00433
00434 static void
00435 set_cell_name (GtkTreeViewColumn *tree_column,
00436 GtkCellRenderer *cell,
00437 GtkTreeModel *tree_model,
00438 GtkTreeIter *iter,
00439 gpointer data)
00440 {
00441 MachineInfo *info;
00442
00443 gtk_tree_model_get (tree_model, iter, 0, &info, -1);
00444
00445 g_object_set (cell, "text", info->name ? info->name : "", NULL);
00446 }
00447
00448 static gint
00449 sort_by_name (GtkTreeModel *model,
00450 GtkTreeIter *a,
00451 GtkTreeIter *b,
00452 gpointer user_data)
00453 {
00454 MachineInfo *info_a, *info_b;
00455
00456 gtk_tree_model_get (model, a, 0, &info_a, -1);
00457 gtk_tree_model_get (model, b, 0, &info_b, -1);
00458
00459 return strcmp (info_a->name ? info_a->name : "",
00460 info_b ? info_b->name : "");
00461 }
00462
00463 static void
00464 set_cell_state (GtkTreeViewColumn *tree_column,
00465 GtkCellRenderer *cell,
00466 GtkTreeModel *tree_model,
00467 GtkTreeIter *iter,
00468 gpointer data)
00469 {
00470 MachineInfo *info;
00471
00472 gtk_tree_model_get (tree_model, iter, 0, &info, -1);
00473
00474 g_object_set (cell, "text", info->state ? info->state : "", NULL);
00475 }
00476
00477 static gint
00478 sort_by_state (GtkTreeModel *model,
00479 GtkTreeIter *a,
00480 GtkTreeIter *b,
00481 gpointer user_data)
00482 {
00483 MachineInfo *info_a, *info_b;
00484
00485 gtk_tree_model_get (model, a, 0, &info_a, -1);
00486 gtk_tree_model_get (model, b, 0, &info_b, -1);
00487
00488 return strcmp (info_a->state ? info_a->state : "",
00489 info_b ? info_b->state : "");
00490 }
00491
00492 static void
00493 set_cell_progress (GtkTreeViewColumn *tree_column,
00494 GtkCellRenderer *cell,
00495 GtkTreeModel *tree_model,
00496 GtkTreeIter *iter,
00497 gpointer data)
00498 {
00499 MachineInfo *info;
00500
00501 gtk_tree_model_get (tree_model, iter, 0, &info, -1);
00502
00503 g_object_set (cell, "value", (int) (info->progress * 100), NULL);
00504 }
00505
00506 static gint
00507 sort_by_progress (GtkTreeModel *model,
00508 GtkTreeIter *a,
00509 GtkTreeIter *b,
00510 gpointer user_data)
00511 {
00512 MachineInfo *info_a, *info_b;
00513
00514 gtk_tree_model_get (model, a, 0, &info_a, -1);
00515 gtk_tree_model_get (model, b, 0, &info_b, -1);
00516
00517 return info_a->progress > info_b->progress ? 1 : -1;
00518 }
00519
00520 static void
00521 get_machines_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer data)
00522 {
00523 GError *error = NULL;
00524 ClientState *state = data;
00525 GPtrArray *objs;
00526 guint i;
00527 GtkWidget *scrolledwin;
00528 GtkTreeViewColumn *col;
00529 GtkCellRenderer *rend;
00530
00531 g_assert (call == state->get_machines_call);
00532
00533 if (!dbus_g_proxy_end_call (proxy, call, &error,
00534 dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
00535 &objs,
00536 G_TYPE_INVALID))
00537 lose_gerror ("Failed to get current machine list", error);
00538
00539 gtk_container_remove (GTK_CONTAINER (state->window),
00540 gtk_bin_get_child (GTK_BIN (state->window)));
00541
00542 scrolledwin = gtk_scrolled_window_new (NULL, NULL);
00543 gtk_widget_show (scrolledwin);
00544
00545 state->store = GTK_TREE_MODEL (gtk_list_store_new (1, G_TYPE_POINTER));
00546 state->view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (state->store));
00547 gtk_widget_show (state->view);
00548 gtk_container_add (GTK_CONTAINER (scrolledwin), state->view);
00549 gtk_container_add (GTK_CONTAINER (state->window), scrolledwin);
00550
00551 rend = gtk_cell_renderer_text_new ();
00552 col = gtk_tree_view_column_new_with_attributes (_("Name"),
00553 rend,
00554 NULL);
00555 gtk_tree_view_column_set_cell_data_func (col, rend, set_cell_name, NULL, NULL);
00556 gtk_tree_view_column_set_resizable (col, TRUE);
00557 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (state->store),
00558 0, sort_by_name, NULL, NULL);
00559 gtk_tree_view_column_set_sort_column_id (col, 0);
00560 gtk_tree_view_append_column (GTK_TREE_VIEW (state->view), col);
00561
00562 rend = gtk_cell_renderer_text_new ();
00563 col = gtk_tree_view_column_new_with_attributes (_("State"),
00564 rend,
00565 NULL);
00566 gtk_tree_view_column_set_cell_data_func (col, rend, set_cell_state, NULL, NULL);
00567 gtk_tree_view_column_set_resizable (col, TRUE);
00568 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (state->store),
00569 0, sort_by_state, NULL, NULL);
00570 gtk_tree_view_column_set_sort_column_id (col, 0);
00571 gtk_tree_view_append_column (GTK_TREE_VIEW (state->view), col);
00572
00573 rend = gtk_cell_renderer_progress_new ();
00574 col = gtk_tree_view_column_new_with_attributes (_("Progress"),
00575 rend,
00576 NULL);
00577 gtk_tree_view_column_set_cell_data_func (col, rend, set_cell_progress, NULL, NULL);
00578 gtk_tree_view_column_set_resizable (col, TRUE);
00579 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (state->store),
00580 0, sort_by_progress, NULL, NULL);
00581 gtk_tree_view_column_set_sort_column_id (col, 0);
00582 gtk_tree_view_append_column (GTK_TREE_VIEW (state->view), col);
00583
00584 for (i = 0; i < objs->len; i++)
00585 {
00586 add_machine (state, NULL, NULL, g_ptr_array_index (objs, i));
00587 g_free (g_ptr_array_index (objs, i));
00588 }
00589 g_ptr_array_free (objs, TRUE);
00590
00591 g_idle_add (do_something_random, state);
00592 }
00593
00594 int
00595 main (int argc, char **argv)
00596 {
00597 DBusGConnection *bus;
00598 DBusGProxy *server;
00599 GError *error = NULL;
00600 ClientState state;
00601 GtkWidget *label;
00602
00603 gtk_init (&argc, &argv);
00604
00605 g_log_set_always_fatal (G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL);
00606
00607 state.window = GTK_WINDOW (gtk_window_new (GTK_WINDOW_TOPLEVEL));
00608 gtk_window_set_resizable (GTK_WINDOW (state.window), TRUE);
00609 g_signal_connect (G_OBJECT (state.window), "destroy",
00610 G_CALLBACK (window_destroyed_cb),
00611 &state);
00612 gtk_window_set_title (GTK_WINDOW (state.window), _("D-BUS State Machine Demo"));
00613 gtk_window_set_default_size (GTK_WINDOW (state.window), 320, 240);
00614
00615 label = gtk_label_new ("");
00616 gtk_label_set_markup (GTK_LABEL (label), "<b>Loading...</b>");
00617 gtk_widget_show (label);
00618
00619 gtk_container_add (GTK_CONTAINER (state.window), label);
00620
00621 bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
00622 if (!bus)
00623 lose_gerror ("Couldn't connect to session bus", error);
00624
00625 state.bus = bus;
00626
00627 server = dbus_g_proxy_new_for_name_owner (bus,
00628 "com.example.StateServer",
00629 "/com/example/StateServer",
00630 "com.example.StateMachineServer",
00631 &error);
00632 if (!server)
00633 lose_gerror ("Couldn't find \"com.example.StateServer\"", error);
00634
00635 state.server_proxy = server;
00636
00637 g_signal_connect (server, "destroy",
00638 G_CALLBACK (server_destroyed_cb),
00639 &state);
00640
00641 dbus_g_object_register_marshaller (sm_marshal_VOID__STRING_BOXED,
00642 G_TYPE_NONE, G_TYPE_STRING,
00643 DBUS_TYPE_G_OBJECT_PATH);
00644
00645 dbus_g_proxy_add_signal (server, "MachineCreated", G_TYPE_STRING, DBUS_TYPE_G_OBJECT_PATH, G_TYPE_INVALID);
00646
00647 dbus_g_proxy_connect_signal (server, "MachineCreated",
00648 G_CALLBACK (machine_created_cb),
00649 &state, NULL);
00650
00651 state.get_machines_call = dbus_g_proxy_begin_call (server, "GetMachines",
00652 get_machines_cb, &state, NULL,
00653 G_TYPE_INVALID);
00654
00655 gtk_widget_show (GTK_WIDGET (state.window));
00656
00657 gtk_main ();
00658
00659 g_object_unref (G_OBJECT (server));
00660
00661 exit(0);
00662 }