From d40767fc62972f9cc85ebfb23e113068cc316f3a Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Wed, 12 May 2010 15:49:48 -0400 Subject: [PATCH] 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(-) create mode 100644 gio/tests/gdbus-example-proxy-subclass.c 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. + + + For an example of a #GDBusProxy-derived class that wraps a D-Bus + interface in a type-safe way, see . The comparison is as + follows: + + Wrapping the org.freedesktop.Accounts.User D-Bus interface in the AccountUser GObject type + + + D-Bus conceptGObject concept + + + + AutomaticLogin property + + AccountsUser:automatic-login GObject property + C getter: accounts_user_get_automatic_login() + Watch changes via the notify::automatic-login signal + + + + RealName property + + AccountsUser:real-name GObject property + C getter: accounts_user_get_real_name() + Watch changes via the notify::real-name signal + + + + UserName property + + AccountsUser:user-name GObject property + C getter: accounts_user_get_user_name() + Watch changes via the notify::user-name signal + + + + Changed signal + + AccountsUser::changed GObject signal + Watch via e.g. g_signal_connect() + + + + Frobnicate method + + Use accounts_user_frobnicate() + accounts_user_frobnicate_finish() or accounts_user_frobnicate_sync() to invoke + + + + +
+
+ GDBusProxy subclass exampleFIXME: MISSING XINCLUDE CONTENT
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 + +/* ---------------------------------------------------------------------------------------------------- */ +/* The D-Bus interface definition we want to create a GDBusProxy-derived type for: */ +/* ---------------------------------------------------------------------------------------------------- */ + +static const gchar introspection_xml[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +/* ---------------------------------------------------------------------------------------------------- */ +/* 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; +} -- 2.7.4