dbus/glib/examples/statemachine statemachine.xml, NONE, 1.1 statemachine.h, NONE, 1.1 statemachine.c, NONE, 1.1 statemachine-server.xml, NONE, 1.1 statemachine-server.h, NONE, 1.1 statemachine-server.c, NONE, 1.1 statemachine-client.c, NONE, 1.1 sm-marshal.list, NONE, 1.1 Makefile.am, NONE, 1.1 .cvsignore, NONE, 1.1

Colin Walters walters at freedesktop.org
Sat Jul 9 02:25:34 EST 2005


Update of /cvs/dbus/dbus/glib/examples/statemachine
In directory gabe:/tmp/cvs-serv2325/glib/examples/statemachine

Added Files:
	statemachine.xml statemachine.h statemachine.c 
	statemachine-server.xml statemachine-server.h 
	statemachine-server.c statemachine-client.c sm-marshal.list 
	Makefile.am .cvsignore 
Log Message:
2005-07-08  Colin Walters  <walters at verbum.org>

	* tools/Makefile.am: Kill of print-introspect in favor of using
	dbus-send --print-reply=literal.
	* tools/print-introspect.c: Deleted.

	* test/glib/test-service-glib.xml: 
	* test/glib/test-service-glib.c (my_object_get_objs): New test
	for "ao".

	* test/glib/test-dbus-glib.c (echo_received_cb): Free echo data.
	(main): Test GetObjs.

	* glib/examples/statemachine/Makefile.am:
	* glib/examples/statemachine/sm-marshal.list:
	* glib/examples/statemachine/statemachine-client.c:
	* glib/examples/statemachine/statemachine-server.c:
	* glib/examples/statemachine/statemachine-server.xml:
	* glib/examples/statemachine/statemachine.c:
	* glib/examples/statemachine/statemachine.h:
	* glib/examples/statemachine/statemachine.xml:

	New example.

	* glib/examples/example-service.c (main): Move invocation
	of dbus_g_object_type_install_info earlier, to emphasize it
	should only be done once.

	* glib/examples/example-signal-emitter.c (main): Ditto.

	* glib/examples/Makefile.am (SUBDIRS): Include statemachine.

	* glib/dbus-gvalue.h (dbus_gtype_to_signature)
	(dbus_gvalue_marshal): Update prototypes.

	* glib/dbus-gvalue.c: Update all marshalling functions to take
	const GValue instead of GValue.
	(signature_iter_to_g_type_array): Return a GPtrArray for nonfixed
	types.
	(dbus_gvalue_to_signature): Update for dbus_gtype_to_signature
	change.
	(dbus_gtype_to_signature): Handle generic collecitons and maps.
	Return a newly-allocated string.
	(demarshal_proxy, demarshal_object_path, demarshal_object)
	(demarshal_strv, demarshal_ghashtable): Set error, don't assert if
	we get the wrong types from message.
	(get_type_demarshaller): New function, extracted from
	dbus_gvalue_demarshal.
	(demarshal_collection): New function, demarshals generic
	collection.
	(dbus_gvalue_demarshal): Just invoke result of
	get_type_demarshaller.  Throw error if we don't have one.
	(marshal_garray_basic): Abort on OOM.
	(get_type_marshaller): New function, extracted from
	dbus_gvalue_marshal.
	(collection_marshal_iterator, marshal_collection): New functions;
	implements generic marshalling for an iteratable specialized
	collection.
	(dbus_gvalue_marshal): Just invoke result of get_type_marshaller.

	* glib/dbus-gvalue-utils.c (gvalue_from_ptrarray_value): Handle
	G_TYPE_STRING.
	(ptrarray_value_from_gvalue): Ditto.
	(ptrarray_append, ptrarray_free): New functions.
	(slist_constructor, slist_iterator, slist_copy_elt, slist_copy) 
	(slist_append, slist_end_append, slist_free): New functions.
	(dbus_g_type_specialized_builtins_init): Add append fuctions
	for GPtrArray and GSList.  Register GSList.
	(test_specialized_hash, _dbus_gvalue_utils_test): New functions.

	* glib/dbus-gtype-specialized.h (DBusGTypeSpecializedAppendContext):
	New.
	(dbus_g_type_specialized_collection_init_append)
	(dbus_g_type_specialized_collection_append)
	(dbus_g_type_specialized_collection_end_append): Prototype.
	(DBusGTypeSpecializedCollectionVtable): Add append_func and
	end_append_func.

	* glib/dbus-gtype-specialized.c (dbus_g_type_specialized_collection_init_append) 
	(dbus_g_type_specialized_collection_append) 
	(dbus_g_type_specialized_collection_end_append): New functions.
	(dbus_g_type_map_value_iterate): Take const GValue.
	(dbus_g_type_collection_value_iterate): Ditto.

	* glib/dbus-gtest.c (dbus_glib_internal_do_not_use_run_tests): Run
	_dbus_gvalue_utils_test.
	
	* glib/dbus-gtest.h: Prototype it.

	* glib/dbus-gproxy.c (dbus_g_proxy_manager_filter): Avoid
	using uninitialized owner_list.
	(dbus_g_proxy_begin_call_internal): Move return_if_fail to
	public API.
	(dbus_g_proxy_end_call_internal): Update to use error set
	from dbus_gvalue_demarshal instead of setting it here.
	(dbus_g_proxy_begin_call): Move return_if_fail here.

	* glib/dbus-gobject.c (write_interface): Update for
	dbus_gtype_to_signature returning new string.

	* configure.in: Add glib/examples/statemachine.


--- NEW FILE: statemachine.xml ---
<?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>

--- NEW FILE: statemachine.h ---
#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

--- NEW FILE: statemachine.c ---
#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, "Resource acquired"),
	  ENUM_ENTRY (SM_OBJECT_STATE_OPERATING, "Operating normally"),
	  { 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 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;

  state_change (object, object->requested_state);
  return FALSE;
}

static gboolean
idle_further_acquire (gpointer data)
{
  SMObject *object = data;

  object->acquisition_progress += g_random_double_range (0.05, 0.5);
  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 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
state_change (SMObject *object, SMObjectState new_state)
{
  g_signal_emit (object, sm_object_signals[STATE_CHANGED], 0,
		 state_to_string (object->state),
		 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_INITIALIZED)
    {
      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;
}

--- NEW FILE: statemachine-server.xml ---
<?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>

--- NEW FILE: statemachine-server.h ---
#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

--- NEW FILE: statemachine-server.c ---
#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;
    }
}

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_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, DBUS_NAME_FLAG_PROHIBIT_REPLACEMENT,
			  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);
}

--- NEW FILE: statemachine-client.c ---
#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);
}

--- NEW FILE: sm-marshal.list ---
VOID:STRING,BOXED

--- NEW FILE: Makefile.am ---
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_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
	$(top_builddir)/glib/dbus-binding-tool --prefix=sm_server --mode=glib-server --output=$@ $<

statemachine-glue.h: statemachine.xml
	$(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)

--- NEW FILE: .cvsignore ---
.deps
.libs
Makefile
Makefile.in
*.lo
*.la
statemachine-client
statemachine-server
statemachine-glue.h
run-with-tmp-session-bus.conf
sm-marshal.[ch]
*.bb
*.bbg
*.da
*.gcov



More information about the dbus-commit mailing list