[glib/gdbus-merge] GDBus: Add an example of a GDBusProxy subclass



commit d40767fc62972f9cc85ebfb23e113068cc316f3a
Author: David Zeuthen <davidz redhat com>
Date:   Wed May 12 15:49:48 2010 -0400

    GDBus: Add an example of a GDBusProxy subclass

 docs/reference/gio/migrating-gdbus.xml   |   55 ++++
 gio/gdbusconnection.c                    |    2 -
 gio/tests/Makefile.am                    |    4 +
 gio/tests/gdbus-example-proxy-subclass.c |  443 ++++++++++++++++++++++++++++++
 4 files changed, 502 insertions(+), 2 deletions(-)
---
diff --git a/docs/reference/gio/migrating-gdbus.xml b/docs/reference/gio/migrating-gdbus.xml
index ba0278c..b8967fd 100644
--- a/docs/reference/gio/migrating-gdbus.xml
+++ b/docs/reference/gio/migrating-gdbus.xml
@@ -218,6 +218,61 @@ on_proxy_appeared (GDBusConnection *connection,
       proxy asynchronously, and only calls your callback when the proxy
       is ready for use.
     </para>
+
+    <para>
+      For an example of a #GDBusProxy-derived class that wraps a D-Bus
+      interface in a type-safe way, see <xref
+      linkend="gdbus-example-proxy-subclass"/>. The comparison is as
+      follows:
+      <table id="gdbus-example-type-safe-proxy">
+        <title>Wrapping the org.freedesktop.Accounts.User D-Bus interface in the AccountUser GObject type</title>
+        <tgroup cols="2">
+          <thead>
+            <row><entry>D-Bus concept</entry><entry>GObject concept</entry></row>
+          </thead>
+          <tbody>
+            <row>
+              <entry>AutomaticLogin property</entry>
+              <entry>
+                <para><literal>AccountsUser:automatic-login</literal> GObject property</para>
+                <para>C getter: accounts_user_get_automatic_login()</para>
+                <para>Watch changes via the <literal>notify::automatic-login</literal> signal</para>
+              </entry>
+            </row>
+            <row>
+              <entry>RealName property</entry>
+              <entry>
+                <para><literal>AccountsUser:real-name</literal> GObject property</para>
+                <para>C getter: accounts_user_get_real_name()</para>
+                <para>Watch changes via the <literal>notify::real-name signal</literal></para>
+              </entry>
+            </row>
+            <row>
+              <entry>UserName property</entry>
+              <entry>
+                <para><literal>AccountsUser:user-name</literal> GObject property</para>
+                <para>C getter: accounts_user_get_user_name()</para>
+                <para>Watch changes via the <literal>notify::user-name</literal> signal</para>
+              </entry>
+            </row>
+            <row>
+              <entry>Changed signal</entry>
+              <entry>
+                <para><literal>AccountsUser::changed</literal> GObject signal</para>
+                <para>Watch via e.g. g_signal_connect()</para>
+              </entry>
+            </row>
+            <row>
+              <entry>Frobnicate method</entry>
+              <entry>
+                <para>Use accounts_user_frobnicate() + accounts_user_frobnicate_finish() or accounts_user_frobnicate_sync() to invoke</para>
+              </entry>
+            </row>
+          </tbody>
+        </tgroup>
+      </table>
+    </para>
+    <example id="gdbus-example-proxy-subclass"><title>GDBusProxy subclass example</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude"; parse="text" href="../../../../gio/tests/gdbus-example-proxy-subclass.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
   </section>
 
   <section>
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index ff425ab..aa5ad05 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -101,8 +101,6 @@
  *
  *  - Consistent timeout handling (25s vs 30s?)
  *
- *  - GDBusProxy subclass example
- *
  *  - Update GDBusAuthObserver (s/deny/authorize/)
  */
 
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index 4aab457..cbd63a1 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -64,6 +64,7 @@ SAMPLE_PROGS = 				\
 	gdbus-example-server		\
 	gdbus-example-subtree		\
 	gdbus-example-peer		\
+	gdbus-example-proxy-subclass	\
 	$(NULL)
 
 
@@ -230,6 +231,9 @@ gdbus_example_subtree_LDADD   = $(progs_ldadd)
 gdbus_example_peer_SOURCES = gdbus-example-peer.c
 gdbus_example_peer_LDADD   = $(progs_ldadd)
 
+gdbus_example_proxy_subclass_SOURCES = gdbus-example-proxy-subclass.c
+gdbus_example_proxy_subclass_LDADD   = $(progs_ldadd)
+
 EXTRA_DIST += \
 	socket-common.c						\
 	org.gtk.test.gschema					\
diff --git a/gio/tests/gdbus-example-proxy-subclass.c b/gio/tests/gdbus-example-proxy-subclass.c
new file mode 100644
index 0000000..6c09909
--- /dev/null
+++ b/gio/tests/gdbus-example-proxy-subclass.c
@@ -0,0 +1,443 @@
+
+#include <gio/gio.h>
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* The D-Bus interface definition we want to create a GDBusProxy-derived type for: */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static const gchar introspection_xml[] =
+  "<node>"
+  "  <interface name='org.freedesktop.Accounts.User'>"
+  "    <method name='Frobnicate'>"
+  "      <arg name='flux' type='s' direction='in'/>"
+  "      <arg name='baz' type='s' direction='in'/>"
+  "      <arg name='result' type='s' direction='out'/>"
+  "    </method>"
+  "    <signal name='Changed'/>"
+  "    <property name='AutomaticLogin' type='b' access='readwrite'/>"
+  "    <property name='RealName' type='s' access='read'/>"
+  "    <property name='UserName' type='s' access='read'/>"
+  "  </interface>"
+  "</node>";
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Definition of the AccountsUser type */
+/* ---------------------------------------------------------------------------------------------------- */
+
+#define ACCOUNTS_TYPE_USER         (accounts_user_get_type ())
+#define ACCOUNTS_USER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), ACCOUNTS_TYPE_USER, AccountsUser))
+#define ACCOUNTS_USER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), ACCOUNTS_TYPE_USER, AccountsUserClass))
+#define ACCOUNTS_USER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), ACCOUNTS_TYPE_USER, AccountsUserClass))
+#define ACCOUNTS_IS_USER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), ACCOUNTS_TYPE_USER))
+#define ACCOUNTS_IS_USER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), ACCOUNTS_TYPE_USER))
+
+typedef struct _AccountsUser        AccountsUser;
+typedef struct _AccountsUserClass   AccountsUserClass;
+typedef struct _AccountsUserPrivate AccountsUserPrivate;
+
+struct _AccountsUser
+{
+  /*< private >*/
+  GDBusProxy parent_instance;
+  AccountsUserPrivate *priv;
+};
+
+struct _AccountsUserClass
+{
+  /*< private >*/
+  GDBusProxyClass parent_class;
+  void (*changed) (AccountsUser *user);
+};
+
+GType        accounts_user_get_type            (void) G_GNUC_CONST;
+
+const gchar *accounts_user_get_user_name       (AccountsUser        *user);
+const gchar *accounts_user_get_real_name       (AccountsUser        *user);
+gboolean     accounts_user_get_automatic_login (AccountsUser        *user);
+
+void         accounts_user_frobnicate          (AccountsUser        *user,
+                                                const gchar         *flux,
+                                                gint                 baz,
+                                                GCancellable        *cancellable,
+                                                GAsyncReadyCallback  callback,
+                                                gpointer             user_data);
+gchar       *accounts_user_frobnicate_finish   (AccountsUser        *user,
+                                                GAsyncResult        *res,
+                                                GError             **error);
+gchar       *accounts_user_frobnicate_sync     (AccountsUser        *user,
+                                                const gchar         *flux,
+                                                gint                 baz,
+                                                GCancellable        *cancellable,
+                                                GError             **error);
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Implementation of the AccountsUser type */
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* A more efficient approach than parsing XML is to use const static
+ * GDBusInterfaceInfo, GDBusMethodInfo, ... structures
+ */
+static GDBusInterfaceInfo *
+accounts_user_get_interface_info (void)
+{
+  static gsize has_info = 0;
+  static GDBusInterfaceInfo *info = NULL;
+  if (g_once_init_enter (&has_info))
+    {
+      GDBusNodeInfo *introspection_data;
+      introspection_data = g_dbus_node_info_new_for_xml (introspection_xml, NULL);
+      info = introspection_data->interfaces[0];
+      g_once_init_leave (&has_info, 1);
+    }
+  return info;
+}
+
+enum
+{
+  PROP_0,
+  PROP_USER_NAME,
+  PROP_REAL_NAME,
+  PROP_AUTOMATIC_LOGIN,
+};
+
+enum
+{
+  CHANGED_SIGNAL,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = {0};
+
+G_DEFINE_TYPE (AccountsUser, accounts_user, G_TYPE_DBUS_PROXY);
+
+static void
+accounts_user_finalize (GObject *object)
+{
+  G_GNUC_UNUSED AccountsUser *user = ACCOUNTS_USER (object);
+
+  if (G_OBJECT_CLASS (accounts_user_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (accounts_user_parent_class)->finalize (object);
+}
+
+static void
+accounts_user_init (AccountsUser *user)
+{
+  /* Sets the expected interface */
+  g_dbus_proxy_set_interface_info (G_DBUS_PROXY (user), accounts_user_get_interface_info ());
+}
+
+static void
+accounts_user_get_property (GObject    *object,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  AccountsUser *user = ACCOUNTS_USER (object);
+
+  switch (prop_id)
+    {
+    case PROP_USER_NAME:
+      g_value_set_string (value, accounts_user_get_user_name (user));
+      break;
+
+    case PROP_REAL_NAME:
+      g_value_set_string (value, accounts_user_get_real_name (user));
+      break;
+
+    case PROP_AUTOMATIC_LOGIN:
+      g_value_set_boolean (value, accounts_user_get_automatic_login (user));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+const gchar *
+accounts_user_get_user_name (AccountsUser *user)
+{
+  GVariant *value;
+  const gchar *ret;
+  g_return_val_if_fail (ACCOUNTS_IS_USER (user), NULL);
+  value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "UserName", NULL);
+  ret = g_variant_get_string (value, NULL);
+  g_variant_unref (value);
+  return ret;
+}
+
+const gchar *
+accounts_user_get_real_name (AccountsUser *user)
+{
+  GVariant *value;
+  const gchar *ret;
+  g_return_val_if_fail (ACCOUNTS_IS_USER (user), NULL);
+  value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "RealName", NULL);
+  ret = g_variant_get_string (value, NULL);
+  g_variant_unref (value);
+  return ret;
+}
+
+gboolean
+accounts_user_get_automatic_login (AccountsUser *user)
+{
+  GVariant *value;
+  gboolean ret;
+  g_return_val_if_fail (ACCOUNTS_IS_USER (user), FALSE);
+  value = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (user), "AutomaticLogin", NULL);
+  ret = g_variant_get_boolean (value);
+  g_variant_unref (value);
+  return ret;
+}
+
+static void
+accounts_user_g_signal (GDBusProxy   *proxy,
+                        const gchar  *sender_name,
+                        const gchar  *signal_name,
+                        GVariant     *parameters)
+{
+  AccountsUser *user = ACCOUNTS_USER (proxy);
+  if (g_strcmp0 (signal_name, "Changed") == 0)
+    g_signal_emit (user, signals[CHANGED_SIGNAL], 0);
+}
+
+static void
+accounts_user_g_properties_changed (GDBusProxy *proxy,
+                                    GVariant   *changed_properties)
+{
+  AccountsUser *user = ACCOUNTS_USER (proxy);
+  GVariantIter *iter;
+  GVariant *item;
+
+  g_variant_get (changed_properties, "a{sv}", &iter);
+  while ((item = g_variant_iter_next_value (iter)) != NULL)
+    {
+      const gchar *key;
+      g_variant_get (item,
+                     "{sv}",
+                     &key,
+                     NULL);
+      if (g_strcmp0 (key, "AutomaticLogin") == 0)
+        g_object_notify (G_OBJECT (user), "automatic-login");
+      else if (g_strcmp0 (key, "RealName") == 0)
+        g_object_notify (G_OBJECT (user), "real-name");
+      else if (g_strcmp0 (key, "UserName") == 0)
+        g_object_notify (G_OBJECT (user), "user-name");
+    }
+}
+
+static void
+accounts_user_class_init (AccountsUserClass *klass)
+{
+  GObjectClass *gobject_class;
+  GDBusProxyClass *proxy_class;
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->get_property = accounts_user_get_property;
+  gobject_class->finalize = accounts_user_finalize;
+
+  proxy_class = G_DBUS_PROXY_CLASS (klass);
+  proxy_class->g_signal             = accounts_user_g_signal;
+  proxy_class->g_properties_changed = accounts_user_g_properties_changed;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_USER_NAME,
+                                   g_param_spec_string ("user-name",
+                                                        "User Name",
+                                                        "The user name of the user",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_REAL_NAME,
+                                   g_param_spec_string ("real-name",
+                                                        "Real Name",
+                                                        "The real name of the user",
+                                                        NULL,
+                                                        G_PARAM_READABLE |
+                                                        G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_AUTOMATIC_LOGIN,
+                                   g_param_spec_boolean ("automatic-login",
+                                                         "Automatic Login",
+                                                         "Whether the user is automatically logged in",
+                                                         FALSE,
+                                                         G_PARAM_READABLE |
+                                                         G_PARAM_STATIC_STRINGS));
+
+  signals[CHANGED_SIGNAL] = g_signal_new ("changed",
+                                          ACCOUNTS_TYPE_USER,
+                                          G_SIGNAL_RUN_LAST,
+                                          G_STRUCT_OFFSET (AccountsUserClass, changed),
+                                          NULL,
+                                          NULL,
+                                          g_cclosure_marshal_VOID__VOID,
+                                          G_TYPE_NONE,
+                                          0);
+}
+
+gchar *
+accounts_user_frobnicate_sync (AccountsUser        *user,
+                               const gchar         *flux,
+                               gint                 baz,
+                               GCancellable        *cancellable,
+                               GError             **error)
+{
+  gchar *ret;
+  GVariant *value;
+
+  g_return_val_if_fail (ACCOUNTS_IS_USER (user), NULL);
+
+  ret = NULL;
+
+  value = g_dbus_proxy_call_sync (G_DBUS_PROXY (user),
+                                  "Frobnicate",
+                                  g_variant_new ("(si)",
+                                                 flux,
+                                                 baz),
+                                  G_DBUS_CALL_FLAGS_NONE,
+                                  -1,
+                                  cancellable,
+                                  error);
+  if (value != NULL)
+    {
+      g_variant_get (value, "(s)", &ret);
+      ret = g_strdup (ret);
+      g_variant_unref (value);
+    }
+  return ret;
+}
+
+void
+accounts_user_frobnicate (AccountsUser        *user,
+                          const gchar         *flux,
+                          gint                 baz,
+                          GCancellable        *cancellable,
+                          GAsyncReadyCallback  callback,
+                          gpointer             user_data)
+{
+  g_return_if_fail (ACCOUNTS_IS_USER (user));
+  g_dbus_proxy_call (G_DBUS_PROXY (user),
+                     "Frobnicate",
+                     g_variant_new ("(si)",
+                                    flux,
+                                    baz),
+                     G_DBUS_CALL_FLAGS_NONE,
+                     -1,
+                     cancellable,
+                     callback,
+                     user_data);
+}
+
+
+gchar *
+accounts_user_frobnicate_finish (AccountsUser        *user,
+                                 GAsyncResult        *res,
+                                 GError             **error)
+{
+  gchar *ret;
+  GVariant *value;
+
+  ret = NULL;
+  value = g_dbus_proxy_call_finish (G_DBUS_PROXY (user), res, error);
+  if (value != NULL)
+    {
+      g_variant_get (value, "(s)", &ret);
+      ret = g_strdup (ret);
+      g_variant_unref (value);
+    }
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* Example usage of the AccountsUser type */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+print_user (AccountsUser *user)
+{
+  g_print ("  user-name       = `%s'\n", accounts_user_get_user_name (user));
+  g_print ("  real-name       = `%s'\n", accounts_user_get_real_name (user));
+  g_print ("  automatic-login = %s\n", accounts_user_get_automatic_login (user) ? "true" : "false");
+}
+
+static void
+on_changed (AccountsUser *user,
+            gpointer      user_data)
+{
+  g_print ("+++ Received the AccountsUser::changed signal\n");
+  print_user (user);
+}
+
+static void
+on_notify (GObject    *object,
+           GParamSpec *pspec,
+           gpointer    user_data)
+{
+  AccountsUser *user = ACCOUNTS_USER (object);
+  g_print ("+++ Received the GObject::notify signal for property `%s'\n",
+           pspec->name);
+  print_user (user);
+}
+
+static void
+on_proxy_appeared (GDBusConnection *connection,
+                   const gchar     *name,
+                   const gchar     *name_owner,
+                   GDBusProxy      *proxy,
+                   gpointer         user_data)
+{
+  AccountsUser *user = ACCOUNTS_USER (proxy);
+
+  g_print ("+++ Acquired proxy for user\n");
+  print_user (user);
+
+  g_signal_connect (proxy,
+                    "notify",
+                    G_CALLBACK (on_notify),
+                    NULL);
+  g_signal_connect (user,
+                    "changed",
+                    G_CALLBACK (on_changed),
+                    NULL);
+}
+
+static void
+on_proxy_vanished (GDBusConnection *connection,
+                   const gchar     *name,
+                   gpointer         user_data)
+{
+  g_print ("--- Cannot create proxy for user: no remote object\n");
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+gint
+main (gint argc, gchar *argv[])
+{
+  guint watcher_id;
+  GMainLoop *loop;
+
+  g_type_init ();
+
+  watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SYSTEM,
+                                  "org.freedesktop.Accounts",
+                                  G_BUS_NAME_WATCHER_FLAGS_AUTO_START,
+                                  "/org/freedesktop/Accounts/User500",
+                                  "org.freedesktop.Accounts.User",
+                                  ACCOUNTS_TYPE_USER,
+                                  G_DBUS_PROXY_FLAGS_NONE,
+                                  on_proxy_appeared,
+                                  on_proxy_vanished,
+                                  NULL,
+                                  NULL);
+
+  loop = g_main_loop_new (NULL, FALSE);
+  g_main_loop_run (loop);
+  g_main_loop_unref (loop);
+  g_bus_unwatch_proxy (watcher_id);
+
+  return 0;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]