[glib/gdbus-merge] GDBus: Add an example of a GDBusProxy subclass
- From: David Zeuthen <davidz src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/gdbus-merge] GDBus: Add an example of a GDBusProxy subclass
- Date: Wed, 12 May 2010 19:53:04 +0000 (UTC)
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]