diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index 0e092d169..9d4f03f1d 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -143,7 +143,6 @@ Highlevel D-Bus Support - diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index b09f26739..9c820a363 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2482,17 +2482,6 @@ g_bus_watch_name_with_closures g_bus_watch_name_on_connection_with_closures -
-gdbusproxywatching -GBusProxyAppearedCallback -GBusProxyVanishedCallback -g_bus_watch_proxy -g_bus_watch_proxy_on_connection -g_bus_unwatch_proxy -g_bus_watch_proxy_with_closures -g_bus_watch_proxy_on_connection_with_closures -
-
gdbuserror GDBusError @@ -2521,9 +2510,13 @@ GDBusProxyClass g_dbus_proxy_new g_dbus_proxy_new_finish g_dbus_proxy_new_sync +g_dbus_proxy_new_for_bus +g_dbus_proxy_new_for_bus_finish +g_dbus_proxy_new_for_bus_sync g_dbus_proxy_get_flags g_dbus_proxy_get_connection -g_dbus_proxy_get_unique_bus_name +g_dbus_proxy_get_name +g_dbus_proxy_get_name_owner g_dbus_proxy_get_object_path g_dbus_proxy_get_interface_name g_dbus_proxy_get_default_timeout diff --git a/docs/reference/gio/migrating-gdbus.xml b/docs/reference/gio/migrating-gdbus.xml index 14e70b702..bd7b8c522 100644 --- a/docs/reference/gio/migrating-gdbus.xml +++ b/docs/reference/gio/migrating-gdbus.xml @@ -1,5 +1,5 @@ - Migrating from dbus-glib to GDBus + Migrating to GDBus
Conceptual differences @@ -47,8 +47,8 @@ #DBusGMethodInvocation#GDBusMethodInvocation dbus_g_bus_get()g_bus_get_sync(), also see g_bus_get() - dbus_g_proxy_new_for_name()g_dbus_proxy_new_sync(), also see - g_dbus_proxy_new() + dbus_g_proxy_new_for_name()g_dbus_proxy_new_sync() and + g_dbus_proxy_new_for_bus_sync(), also see g_dbus_proxy_new() dbus_g_proxy_add_signal()not needed, use the generic #GDBusProxy::g-signal dbus_g_proxy_connect_signal()use g_signal_connect() with #GDBusProxy::g-signal dbus_g_connection_register_g_object()g_dbus_connection_register_object() @@ -177,46 +177,28 @@ on_name_acquired (GDBusConnection *connection, the current owner of the name, and that owner can change over time. - In contrast, #GDBusProxy instances are always bound to a unique name. - To get a proxy for a well-known name, you either have to call - GetNameOwner yourself and construct a proxy for the unique name - of the current name owner, or use the high-level API. The latter - option is highly recommended: + The same can be achieved with #GDBusProxy: - Like g_bus_own_name(), g_bus_watch_proxy() is asynchronous and - you are expected to enter your mainloop to await the on_proxy_appeared() - callback. Note that GDBus also does all the setup operations for the - proxy asynchronously, and only calls your callback when the proxy - is ready for use. + For an added layer of safety, you can specify what D-Bus + interface the proxy is expected to conform to by using the + #GDBusInterfaceInfo type. + + + Additionally, #GDBusProxy loads, caches and tracks changes to + the D-Bus properties on the remote object. It also sets up match + rules so D-Bus signals from the remote object are delivered + locally.
diff --git a/gio/Makefile.am b/gio/Makefile.am index fe4053e19..a94bde0e1 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -89,7 +89,6 @@ gdbus_headers = \ gdbusmessage.h \ gdbusnameowning.h \ gdbusnamewatching.h \ - gdbusproxywatching.h \ gdbusproxy.h \ gdbusintrospection.h \ gdbusmethodinvocation.h \ @@ -110,7 +109,6 @@ gdbus_sources = \ gdbusmessage.h gdbusmessage.c \ gdbusnameowning.h gdbusnameowning.c \ gdbusnamewatching.h gdbusnamewatching.c \ - gdbusproxywatching.h gdbusproxywatching.c \ gdbusproxy.h gdbusproxy.c \ gdbusprivate.h gdbusprivate.c \ gdbusintrospection.h gdbusintrospection.c \ diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index c6b34d148..a41ee856d 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -144,7 +144,7 @@ * * This class is rarely used directly in D-Bus clients. If you are writing * an D-Bus client, it is often easier to use the g_bus_own_name(), - * g_bus_watch_name() or g_bus_watch_proxy() APIs. + * g_bus_watch_name() or g_dbus_proxy_new_for_bus() APIs. * * D-Bus server exampleFIXME: MISSING XINCLUDE CONTENT * @@ -265,9 +265,9 @@ struct _GDBusConnectionPrivate GHashTable *map_method_serial_to_send_message_data; /* guint32 -> SendMessageData* */ /* Maps used for managing signal subscription */ - GHashTable *map_rule_to_signal_data; /* gchar* -> SignalData */ - GHashTable *map_id_to_signal_data; /* guint -> SignalData */ - GHashTable *map_sender_to_signal_data_array; /* gchar* -> GPtrArray* of SignalData */ + GHashTable *map_rule_to_signal_data; /* match rule (gchar*) -> SignalData */ + GHashTable *map_id_to_signal_data; /* id (guint) -> SignalData */ + GHashTable *map_sender_unique_name_to_signal_data_array; /* unique sender (gchar*) -> GPtrArray* of SignalData */ /* Maps used for managing exported objects and subtrees */ GHashTable *map_object_path_to_eo; /* gchar* -> ExportedObject* */ @@ -408,7 +408,7 @@ g_dbus_connection_finalize (GObject *object) purge_all_signal_subscriptions (connection); g_hash_table_unref (connection->priv->map_rule_to_signal_data); g_hash_table_unref (connection->priv->map_id_to_signal_data); - g_hash_table_unref (connection->priv->map_sender_to_signal_data_array); + g_hash_table_unref (connection->priv->map_sender_unique_name_to_signal_data_array); g_hash_table_unref (connection->priv->map_id_to_ei); g_hash_table_unref (connection->priv->map_object_path_to_eo); @@ -785,10 +785,10 @@ g_dbus_connection_init (GDBusConnection *connection) g_str_equal); connection->priv->map_id_to_signal_data = g_hash_table_new (g_direct_hash, g_direct_equal); - connection->priv->map_sender_to_signal_data_array = g_hash_table_new_full (g_str_hash, - g_str_equal, - g_free, - NULL); + connection->priv->map_sender_unique_name_to_signal_data_array = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); connection->priv->map_object_path_to_eo = g_hash_table_new_full (g_str_hash, g_str_equal, @@ -2372,6 +2372,7 @@ typedef struct { gchar *rule; gchar *sender; + gchar *sender_unique_name; /* if sender is unique or org.freedesktop.DBus, then that name... otherwise blank */ gchar *interface_name; gchar *member; gchar *object_path; @@ -2389,16 +2390,17 @@ typedef struct } SignalSubscriber; static void -signal_data_free (SignalData *data) +signal_data_free (SignalData *signal_data) { - g_free (data->rule); - g_free (data->sender); - g_free (data->interface_name); - g_free (data->member); - g_free (data->object_path); - g_free (data->arg0); - g_array_free (data->subscribers, TRUE); - g_free (data); + g_free (signal_data->rule); + g_free (signal_data->sender); + g_free (signal_data->sender_unique_name); + g_free (signal_data->interface_name); + g_free (signal_data->member); + g_free (signal_data->object_path); + g_free (signal_data->arg0); + g_array_free (signal_data->subscribers, TRUE); + g_free (signal_data); } static gchar * @@ -2444,7 +2446,6 @@ add_match_rule (GDBusConnection *connection, "org.freedesktop.DBus", /* interface */ "AddMatch"); g_dbus_message_set_body (message, g_variant_new ("(s)", match_rule)); - error = NULL; if (!g_dbus_connection_send_message_unlocked (connection, message, @@ -2490,7 +2491,7 @@ remove_match_rule (GDBusConnection *connection, static gboolean is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) { - return g_strcmp0 (signal_data->sender, "org.freedesktop.DBus") == 0 && + return g_strcmp0 (signal_data->sender_unique_name, "org.freedesktop.DBus") == 0 && g_strcmp0 (signal_data->interface_name, "org.freedesktop.DBus") == 0 && g_strcmp0 (signal_data->object_path, "/org/freedesktop/DBus") == 0 && (g_strcmp0 (signal_data->member, "NameLost") == 0 || @@ -2502,7 +2503,7 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) /** * g_dbus_connection_signal_subscribe: * @connection: A #GDBusConnection. - * @sender: Sender name to match on. Must be either org.freedesktop.DBus (for listening to signals from the message bus daemon) or a unique name or %NULL to listen from all senders. + * @sender: Sender name to match on (unique or well-known name) or %NULL to listen from all senders. * @interface_name: D-Bus interface name to match on or %NULL to match on all interfaces. * @member: D-Bus signal name to match on or %NULL to match on all signals. * @object_path: Object path to match on or %NULL to match on all object paths. @@ -2517,14 +2518,18 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data) * linkend="g-main-context-push-thread-default">thread-default main * loop of the thread you are calling this method from. * - * It is considered a programming error to use this function if @connection is closed. + * It is considered a programming error to use this function if + * @connection is closed. * - * Note that if @sender is not org.freedesktop.DBus (for listening to signals from the - * message bus daemon), then it needs to be a unique bus name or %NULL (for listening to signals from any - * name) - you cannot pass a name like com.example.MyApp. - * Use e.g. g_bus_watch_name() to find the unique name for the owner of the name you are interested in. Also note - * that this function does not remove a subscription if @sender vanishes from the bus. You have to manually - * call g_dbus_connection_signal_unsubscribe() to remove a subscription. + * If @connection is not a message bus connection, @sender must be + * %NULL. + * + * If @sender is a well-known name note that @callback is invoked with + * the unique name for the owner of @sender, not the well-known name + * as one would expect. This is because the message bus rewrites the + * name. As such, to avoid certain race conditions, users should be + * tracking the name owner of the well-known name and use that when + * processing the received signal. * * Returns: A subscription identifier that can be used with g_dbus_connection_signal_unsubscribe(). * @@ -2545,6 +2550,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, SignalData *signal_data; SignalSubscriber subscriber; GPtrArray *signal_data_array; + const gchar *sender_unique_name; /* Right now we abort if AddMatch() fails since it can only fail with the bus being in * an OOM condition. We might want to change that but that would involve making @@ -2558,8 +2564,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); g_return_val_if_fail (!g_dbus_connection_is_closed (connection), 0); - g_return_val_if_fail (sender == NULL || ((strcmp (sender, "org.freedesktop.DBus") == 0 || sender[0] == ':') && - (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION)), 0); + g_return_val_if_fail (sender == NULL || (g_dbus_is_name (sender) && (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION)), 0); g_return_val_if_fail (interface_name == NULL || g_dbus_is_interface_name (interface_name), 0); g_return_val_if_fail (member == NULL || g_dbus_is_member_name (member), 0); g_return_val_if_fail (object_path == NULL || g_variant_is_object_path (object_path), 0); @@ -2569,8 +2574,10 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, rule = args_to_rule (sender, interface_name, member, object_path, arg0); - if (sender == NULL) - sender = ""; + if (sender != NULL && (g_dbus_is_unique_name (sender) || g_strcmp0 (sender, "org.freedesktop.DBus") == 0)) + sender_unique_name = sender; + else + sender_unique_name = ""; subscriber.callback = callback; subscriber.user_data = user_data; @@ -2590,13 +2597,14 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, } signal_data = g_new0 (SignalData, 1); - signal_data->rule = rule; - signal_data->sender = g_strdup (sender); - signal_data->interface_name = g_strdup (interface_name); - signal_data->member = g_strdup (member); - signal_data->object_path = g_strdup (object_path); - signal_data->arg0 = g_strdup (arg0); - signal_data->subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber)); + signal_data->rule = rule; + signal_data->sender = g_strdup (sender); + signal_data->sender_unique_name = g_strdup (sender_unique_name); + signal_data->interface_name = g_strdup (interface_name); + signal_data->member = g_strdup (member); + signal_data->object_path = g_strdup (object_path); + signal_data->arg0 = g_strdup (arg0); + signal_data->subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber)); g_array_append_val (signal_data->subscribers, subscriber); g_hash_table_insert (connection->priv->map_rule_to_signal_data, @@ -2614,22 +2622,22 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection, add_match_rule (connection, signal_data->rule); } + signal_data_array = g_hash_table_lookup (connection->priv->map_sender_unique_name_to_signal_data_array, + signal_data->sender_unique_name); + if (signal_data_array == NULL) + { + signal_data_array = g_ptr_array_new (); + g_hash_table_insert (connection->priv->map_sender_unique_name_to_signal_data_array, + g_strdup (signal_data->sender_unique_name), + signal_data_array); + } + g_ptr_array_add (signal_data_array, signal_data); + out: g_hash_table_insert (connection->priv->map_id_to_signal_data, GUINT_TO_POINTER (subscriber.id), signal_data); - signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, - signal_data->sender); - if (signal_data_array == NULL) - { - signal_data_array = g_ptr_array_new (); - g_hash_table_insert (connection->priv->map_sender_to_signal_data_array, - g_strdup (signal_data->sender), - signal_data_array); - } - g_ptr_array_add (signal_data_array, signal_data); - CONNECTION_UNLOCK (connection); return subscriber.id; @@ -2669,24 +2677,27 @@ unsubscribe_id_internal (GDBusConnection *connection, g_array_remove_index (signal_data->subscribers, n); if (signal_data->subscribers->len == 0) - g_warn_if_fail (g_hash_table_remove (connection->priv->map_rule_to_signal_data, signal_data->rule)); - - signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, - signal_data->sender); - g_warn_if_fail (signal_data_array != NULL); - g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data)); - - if (signal_data_array->len == 0) { - g_warn_if_fail (g_hash_table_remove (connection->priv->map_sender_to_signal_data_array, signal_data->sender)); + g_warn_if_fail (g_hash_table_remove (connection->priv->map_rule_to_signal_data, signal_data->rule)); + + signal_data_array = g_hash_table_lookup (connection->priv->map_sender_unique_name_to_signal_data_array, + signal_data->sender_unique_name); + g_warn_if_fail (signal_data_array != NULL); + g_warn_if_fail (g_ptr_array_remove (signal_data_array, signal_data)); + + if (signal_data_array->len == 0) + { + g_warn_if_fail (g_hash_table_remove (connection->priv->map_sender_unique_name_to_signal_data_array, + signal_data->sender_unique_name)); + } /* remove the match rule from the bus unless NameLost or NameAcquired (see subscribe()) */ if (connection->priv->flags & G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION) { if (!is_signal_data_for_name_lost_or_acquired (signal_data)) - remove_match_rule (connection, signal_data->rule); + if (!connection->priv->closed) + remove_match_rule (connection, signal_data->rule); } - signal_data_free (signal_data); } @@ -2779,7 +2790,8 @@ emit_signal_instance_in_idle_cb (gpointer data) } #if 0 - g_debug ("in emit_signal_instance_in_idle_cb (sender=%s path=%s interface=%s member=%s params=%s)", + g_print ("in emit_signal_instance_in_idle_cb (id=%d sender=%s path=%s interface=%s member=%s params=%s)\n", + signal_instance->subscription_id, signal_instance->sender, signal_instance->path, signal_instance->interface, @@ -2842,11 +2854,17 @@ schedule_callbacks (GDBusConnection *connection, arg0 = g_dbus_message_get_arg0 (message); #if 0 - g_debug ("sender = `%s'", sender); - g_debug ("interface = `%s'", interface); - g_debug ("member = `%s'", member); - g_debug ("path = `%s'", path); - g_debug ("arg0 = `%s'", arg0); + g_print ("In schedule_callbacks:\n" + " sender = `%s'\n" + " interface = `%s'\n" + " member = `%s'\n" + " path = `%s'\n" + " arg0 = `%s'\n", + sender, + interface, + member, + path, + arg0); #endif /* TODO: if this is slow, then we can change signal_data_array into @@ -2912,13 +2930,13 @@ distribute_signals (GDBusConnection *connection, /* collect subscribers that match on sender */ if (sender != NULL) { - signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, sender); + signal_data_array = g_hash_table_lookup (connection->priv->map_sender_unique_name_to_signal_data_array, sender); if (signal_data_array != NULL) schedule_callbacks (connection, signal_data_array, message, sender); } /* collect subscribers not matching on sender */ - signal_data_array = g_hash_table_lookup (connection->priv->map_sender_to_signal_data_array, ""); + signal_data_array = g_hash_table_lookup (connection->priv->map_sender_unique_name_to_signal_data_array, ""); if (signal_data_array != NULL) schedule_callbacks (connection, signal_data_array, message, sender); } diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c index fe5c97d6b..1344b7c75 100644 --- a/gio/gdbusproxy.c +++ b/gio/gdbusproxy.c @@ -25,8 +25,6 @@ #include #include -#include - #include "gdbusutils.h" #include "gdbusproxy.h" #include "gioenumtypes.h" @@ -39,45 +37,61 @@ #include "gioerror.h" #include "gasyncresult.h" #include "gsimpleasyncresult.h" +#include "gcancellable.h" #include "glibintl.h" #include "gioalias.h" /** * SECTION:gdbusproxy - * @short_description: Base class for proxies + * @short_description: Client-side proxies * @include: gio/gio.h * * #GDBusProxy is a base class used for proxies to access a D-Bus - * interface on a remote object. A #GDBusProxy can only be constructed - * for unique name bus and does not track whether the name - * vanishes. Use g_bus_watch_proxy() to construct #GDBusProxy proxies - * for owners of a well-known names. + * interface on a remote object. A #GDBusProxy can be constructed for + * both well-known and unique names. * - * By default, #GDBusProxy will cache all properties (and listen for - * their changes) of the remote object, and proxy all signals that gets + * By default, #GDBusProxy will cache all properties (and listen to + * changes) of the remote object, and proxy all signals that gets * emitted. This behaviour can be changed by passing suitable - * #GDBusProxyFlags when the proxy is created. + * #GDBusProxyFlags when the proxy is created. If the proxy is for a + * well-known name, the property cache is flushed when the name owner + * vanishes and reloaded when a name owner appears. + * + * If a #GDBusProxy is used for a well-known name, the owner of the + * name is tracked and can be read from + * #GDBusProxy:g-name-owner. Connect to the #GObject::notify signal to + * get notified of changes. Additionally, only signals and property + * changes emitted from the current name owner are considered. This + * avoids a number of race conditions when the name is lost by one + * owner and claimed by another. * * The generic #GDBusProxy::g-properties-changed and #GDBusProxy::g-signal * signals are not very convenient to work with. Therefore, the recommended * way of working with proxies is to subclass #GDBusProxy, and have - * more natural properties and signals in your derived class. The - * @interface_type argument of g_bus_watch_proxy() lets you obtain - * instances of your derived class when using the high-level API. + * more natural properties and signals in your derived class. * * See for an example. + * + * GDBusProxy for a well-known-nameFIXME: MISSING XINCLUDE CONTENT */ struct _GDBusProxyPrivate { + GBusType bus_type; GDBusConnection *connection; + GDBusProxyFlags flags; - gchar *unique_bus_name; + gchar *name; + gchar *name_owner; gchar *object_path; gchar *interface_name; gint timeout_msec; + guint name_owner_changed_subscription_id; + + GCancellable *get_all_cancellable; + /* gchar* -> GVariant* */ GHashTable *properties; @@ -93,7 +107,9 @@ enum { PROP_0, PROP_G_CONNECTION, - PROP_G_UNIQUE_BUS_NAME, + PROP_G_BUS_TYPE, + PROP_G_NAME, + PROP_G_NAME_OWNER, PROP_G_FLAGS, PROP_G_OBJECT_PATH, PROP_G_INTERFACE_NAME, @@ -108,8 +124,6 @@ enum LAST_SIGNAL, }; -static void g_dbus_proxy_constructed (GObject *object); - guint signals[LAST_SIGNAL] = {0}; static void initable_iface_init (GInitableIface *initable_iface); @@ -125,6 +139,12 @@ g_dbus_proxy_finalize (GObject *object) { GDBusProxy *proxy = G_DBUS_PROXY (object); + g_warn_if_fail (proxy->priv->get_all_cancellable == NULL); + + if (proxy->priv->name_owner_changed_subscription_id > 0) + g_dbus_connection_signal_unsubscribe (proxy->priv->connection, + proxy->priv->name_owner_changed_subscription_id); + if (proxy->priv->properties_changed_subscriber_id > 0) g_dbus_connection_signal_unsubscribe (proxy->priv->connection, proxy->priv->properties_changed_subscriber_id); @@ -134,7 +154,8 @@ g_dbus_proxy_finalize (GObject *object) proxy->priv->signals_subscriber_id); g_object_unref (proxy->priv->connection); - g_free (proxy->priv->unique_bus_name); + g_free (proxy->priv->name); + g_free (proxy->priv->name_owner); g_free (proxy->priv->object_path); g_free (proxy->priv->interface_name); if (proxy->priv->properties != NULL) @@ -164,8 +185,12 @@ g_dbus_proxy_get_property (GObject *object, g_value_set_flags (value, proxy->priv->flags); break; - case PROP_G_UNIQUE_BUS_NAME: - g_value_set_string (value, proxy->priv->unique_bus_name); + case PROP_G_NAME: + g_value_set_string (value, proxy->priv->name); + break; + + case PROP_G_NAME_OWNER: + g_value_set_string (value, proxy->priv->name_owner); break; case PROP_G_OBJECT_PATH: @@ -208,8 +233,8 @@ g_dbus_proxy_set_property (GObject *object, proxy->priv->flags = g_value_get_flags (value); break; - case PROP_G_UNIQUE_BUS_NAME: - proxy->priv->unique_bus_name = g_value_dup_string (value); + case PROP_G_NAME: + proxy->priv->name = g_value_dup_string (value); break; case PROP_G_OBJECT_PATH: @@ -228,6 +253,10 @@ g_dbus_proxy_set_property (GObject *object, g_dbus_proxy_set_interface_info (proxy, g_value_get_boxed (value)); break; + case PROP_G_BUS_TYPE: + proxy->priv->bus_type = g_value_get_enum (value); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -242,7 +271,6 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) gobject_class->finalize = g_dbus_proxy_finalize; gobject_class->set_property = g_dbus_proxy_set_property; gobject_class->get_property = g_dbus_proxy_get_property; - gobject_class->constructed = g_dbus_proxy_constructed; /* Note that all property names are prefixed to avoid collisions with D-Bus property names * in derived classes */ @@ -290,6 +318,29 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); + /** + * GDBusProxy:g-bus-type: + * + * If this property is not %G_BUS_TYPE_NONE, then + * #GDBusProxy:g-connection must be %NULL and will be set to the + * #GDBusConnection obtained by calling g_bus_get() with the value + * of this property. + * + * Since: 2.26 + */ + g_object_class_install_property (gobject_class, + PROP_G_BUS_TYPE, + g_param_spec_enum ("g-bus-type", + P_("Bus Type"), + P_("The bus to connect to, if any"), + G_TYPE_BUS_TYPE, + G_BUS_TYPE_NONE, + G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + /** * GDBusProxy:g-flags: * @@ -312,17 +363,17 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) G_PARAM_STATIC_NICK)); /** - * GDBusProxy:g-unique-bus-name: + * GDBusProxy:g-name: * - * The unique bus name the proxy is for. + * The well-known or unique name that the proxy is for. * * Since: 2.26 */ g_object_class_install_property (gobject_class, - PROP_G_UNIQUE_BUS_NAME, - g_param_spec_string ("g-unique-bus-name", - P_("g-unique-bus-name"), - P_("The unique bus name the proxy is for"), + PROP_G_NAME, + g_param_spec_string ("g-name", + P_("g-name"), + P_("The well-known or unique name that the proxy is for"), NULL, G_PARAM_READABLE | G_PARAM_WRITABLE | @@ -331,6 +382,26 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass) G_PARAM_STATIC_BLURB | G_PARAM_STATIC_NICK)); + /** + * GDBusProxy:g-name-owner: + * + * The unique name that owns #GDBusProxy:name or %NULL if no-one + * currently owns that name. You may connect to #GObject::notify signal to + * track changes to this property. + * + * Since: 2.26 + */ + g_object_class_install_property (gobject_class, + PROP_G_NAME_OWNER, + g_param_spec_string ("g-name-owner", + P_("g-name-owner"), + P_("The unique name for the owner"), + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_NAME | + G_PARAM_STATIC_BLURB | + G_PARAM_STATIC_NICK)); + /** * GDBusProxy:g-object-path: * @@ -667,6 +738,9 @@ on_signal_received (GDBusConnection *connection, if (!proxy->priv->initialized) goto out; + if (g_strcmp0 (sender_name, proxy->priv->name_owner) != 0) + goto out; + g_signal_emit (proxy, signals[SIGNAL_SIGNAL], 0, @@ -705,6 +779,9 @@ on_properties_changed (GDBusConnection *connection, if (!proxy->priv->initialized) goto out; + if (g_strcmp0 (sender_name, proxy->priv->name_owner) != 0) + goto out; + if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa{sv}as)"))) { g_warning ("Value for PropertiesChanged signal with type `%s' does not match `(sa{sv}as)'", @@ -748,51 +825,6 @@ on_properties_changed (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ -static void -g_dbus_proxy_constructed (GObject *object) -{ - if (G_OBJECT_CLASS (g_dbus_proxy_parent_class)->constructed != NULL) - G_OBJECT_CLASS (g_dbus_proxy_parent_class)->constructed (object); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -subscribe_to_signals (GDBusProxy *proxy) -{ - if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)) - { - /* subscribe to PropertiesChanged() */ - proxy->priv->properties_changed_subscriber_id = - g_dbus_connection_signal_subscribe (proxy->priv->connection, - proxy->priv->unique_bus_name, - "org.freedesktop.DBus.Properties", - "PropertiesChanged", - proxy->priv->object_path, - proxy->priv->interface_name, - on_properties_changed, - proxy, - NULL); - } - - if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS)) - { - /* subscribe to all signals for the object */ - proxy->priv->signals_subscriber_id = - g_dbus_connection_signal_subscribe (proxy->priv->connection, - proxy->priv->unique_bus_name, - proxy->priv->interface_name, - NULL, /* member */ - proxy->priv->object_path, - NULL, /* arg0 */ - on_signal_received, - proxy, - NULL); - } -} - -/* ---------------------------------------------------------------------------------------------------- */ - static void process_get_all_reply (GDBusProxy *proxy, GVariant *result) @@ -811,7 +843,7 @@ process_get_all_reply (GDBusProxy *proxy, g_variant_get (result, "(a{sv})", &iter); while (g_variant_iter_next (iter, "{sv}", &key, &value)) { - //g_print ("got %s -> %s\n", key, g_variant_markup_print (value, FALSE, 0, 0)); + /*g_print ("got %s -> %s\n", key, g_variant_print (value, FALSE));*/ g_hash_table_insert (proxy->priv->properties, key, /* adopts string */ @@ -823,81 +855,183 @@ process_get_all_reply (GDBusProxy *proxy, ; } -static gboolean -initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) +typedef struct { - GDBusProxy *proxy = G_DBUS_PROXY (initable); + GDBusProxy *proxy; + GCancellable *cancellable; + gchar *name_owner; +} LoadPropertiesOnNameOwnerChangedData; + +static void +on_name_owner_changed_get_all_cb (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + LoadPropertiesOnNameOwnerChangedData *data = user_data; GVariant *result; - gboolean ret; + GError *error; + gboolean cancelled; - ret = FALSE; + cancelled = FALSE; - subscribe_to_signals (proxy); - - if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)) + error = NULL; + result = g_dbus_connection_call_finish (connection, + res, + &error); + if (result == NULL) { - /* load all properties synchronously */ - result = g_dbus_connection_call_sync (proxy->priv->connection, - proxy->priv->unique_bus_name, - proxy->priv->object_path, - "org.freedesktop.DBus.Properties", - "GetAll", - g_variant_new ("(s)", proxy->priv->interface_name), - G_VARIANT_TYPE ("(a{sv})"), - G_DBUS_CALL_FLAGS_NONE, - -1, /* timeout */ - cancellable, - error); - if (result == NULL) - { - /* We just ignore if GetAll() is failing. Because this might happen - * if the object has no properties at all. Or if the caller is - * not authorized to see the properties. - * - * Either way, apps can know about this by using - * get_cached_property_names() or get_cached_property(). - * - * TODO: handle G_DBUS_DEBUG flag 'proxy' and, if enabled, log the - * fact that GetAll() failed - */ - //g_debug ("error: %d %d %s", error->domain, error->code, error->message); - if (error != NULL) - { - g_error_free (*error); - *error = NULL; - } - ret = TRUE; - goto out; - } - - process_get_all_reply (proxy, result); - - g_variant_unref (result); + if (error->domain == G_IO_ERROR && error->code == G_IO_ERROR_CANCELLED) + cancelled = TRUE; + /* We just ignore if GetAll() is failing. Because this might happen + * if the object has no properties at all. Or if the caller is + * not authorized to see the properties. + * + * Either way, apps can know about this by using + * get_cached_property_names() or get_cached_property(). + * + * TODO: handle G_DBUS_DEBUG flag 'proxy' and, if enabled, log the + * fact that GetAll() failed + */ + //g_debug ("error: %d %d %s", error->domain, error->code, error->message); + g_error_free (error); } - ret = TRUE; + /* and finally we can notify */ + if (!cancelled) + { + g_free (data->proxy->priv->name_owner); + data->proxy->priv->name_owner = data->name_owner; + data->name_owner = NULL; /* to avoid an extra copy, we steal the string */ - out: - proxy->priv->initialized = TRUE; - return ret; + g_hash_table_remove_all (data->proxy->priv->properties); + if (result != NULL) + { + process_get_all_reply (data->proxy, result); + g_variant_unref (result); + } + + g_object_notify (G_OBJECT (data->proxy), "g-name-owner"); + } + + if (data->cancellable == data->proxy->priv->get_all_cancellable) + data->proxy->priv->get_all_cancellable = NULL; + + g_object_unref (data->proxy); + g_object_unref (data->cancellable); + g_free (data->name_owner); + g_free (data); } static void -initable_iface_init (GInitableIface *initable_iface) +on_name_owner_changed (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) { - initable_iface->init = initable_init; + GDBusProxy *proxy = G_DBUS_PROXY (user_data); + const gchar *name; + const gchar *old_owner; + const gchar *new_owner; + + /* if we are already trying to load properties, cancel that */ + if (proxy->priv->get_all_cancellable != NULL) + { + g_cancellable_cancel (proxy->priv->get_all_cancellable); + proxy->priv->get_all_cancellable = NULL; + } + + g_variant_get (parameters, + "(&s&s&s)", + &name, + &old_owner, + &new_owner); + + /* we only care about a specific name */ + if (g_strcmp0 (name, proxy->priv->name) != 0) + goto out; + + if (strlen (new_owner) == 0) + { + g_free (proxy->priv->name_owner); + proxy->priv->name_owner = NULL; + g_hash_table_remove_all (proxy->priv->properties); + g_object_notify (G_OBJECT (proxy), "g-name-owner"); + } + else + { + /* ignore duplicates - this can happen when activating the service */ + if (g_strcmp0 (new_owner, proxy->priv->name_owner) == 0) + goto out; + + if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES) + { + g_free (proxy->priv->name_owner); + proxy->priv->name_owner = g_strdup (new_owner); + g_hash_table_remove_all (proxy->priv->properties); + g_object_notify (G_OBJECT (proxy), "g-name-owner"); + } + else + { + LoadPropertiesOnNameOwnerChangedData *data; + + /* start loading properties.. only then emit notify::g-name-owner .. we + * need to be able to cancel this in the event another NameOwnerChanged + * signal suddenly happens + */ + + g_assert (proxy->priv->get_all_cancellable == NULL); + proxy->priv->get_all_cancellable = g_cancellable_new (); + data = g_new0 (LoadPropertiesOnNameOwnerChangedData, 1); + data->proxy = g_object_ref (proxy); + data->cancellable = proxy->priv->get_all_cancellable; + data->name_owner = g_strdup (new_owner); + g_dbus_connection_call (proxy->priv->connection, + data->name_owner, + proxy->priv->object_path, + "org.freedesktop.DBus.Properties", + "GetAll", + g_variant_new ("(s)", proxy->priv->interface_name), + G_VARIANT_TYPE ("(a{sv})"), + G_DBUS_CALL_FLAGS_NONE, + -1, /* timeout */ + proxy->priv->get_all_cancellable, + (GAsyncReadyCallback) on_name_owner_changed_get_all_cb, + data); + } + } + + out: + ; } /* ---------------------------------------------------------------------------------------------------- */ -static void -get_all_cb (GDBusConnection *connection, - GAsyncResult *res, - gpointer user_data) +typedef struct { - GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data); + GDBusProxy *proxy; + GCancellable *cancellable; + GSimpleAsyncResult *simple; +} AsyncInitData; + +static void +async_init_data_free (AsyncInitData *data) +{ + g_object_unref (data->proxy); + if (data->cancellable != NULL) + g_object_unref (data->cancellable); + g_object_unref (data->simple); + g_free (data); +} + +static void +async_init_get_all_cb (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + AsyncInitData *data = user_data; GVariant *result; GError *error; @@ -922,59 +1056,237 @@ get_all_cb (GDBusConnection *connection, } else { - g_simple_async_result_set_op_res_gpointer (simple, + g_simple_async_result_set_op_res_gpointer (data->simple, result, (GDestroyNotify) g_variant_unref); } - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); + g_simple_async_result_complete_in_idle (data->simple); + async_init_data_free (data); } + static void -async_initable_init_async (GAsyncInitable *initable, - gint io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) +async_init_get_name_owner_cb (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) { - GDBusProxy *proxy = G_DBUS_PROXY (initable); - GSimpleAsyncResult *simple; + AsyncInitData *data = user_data; - simple = g_simple_async_result_new (G_OBJECT (proxy), - callback, - user_data, - NULL); + if (res != NULL) + { + GError *error; + GVariant *result; - subscribe_to_signals (proxy); + error = NULL; + result = g_dbus_connection_call_finish (connection, + res, + &error); + if (result == NULL) + { + if (error->domain == G_DBUS_ERROR && + error->code == G_DBUS_ERROR_NAME_HAS_NO_OWNER) + { + g_error_free (error); + } + else + { + g_simple_async_result_set_from_error (data->simple, error); + g_error_free (error); + g_simple_async_result_complete_in_idle (data->simple); + async_init_data_free (data); + goto out; + } + } + else + { + g_variant_get (result, + "(s)", + &data->proxy->priv->name_owner); + g_variant_unref (result); + } + } - if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)) + if (!(data->proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)) { /* load all properties asynchronously */ - g_dbus_connection_call (proxy->priv->connection, - proxy->priv->unique_bus_name, - proxy->priv->object_path, + g_dbus_connection_call (data->proxy->priv->connection, + data->proxy->priv->name_owner, + data->proxy->priv->object_path, "org.freedesktop.DBus.Properties", "GetAll", - g_variant_new ("(s)", proxy->priv->interface_name), + g_variant_new ("(s)", data->proxy->priv->interface_name), G_VARIANT_TYPE ("(a{sv})"), G_DBUS_CALL_FLAGS_NONE, -1, /* timeout */ - cancellable, - (GAsyncReadyCallback) get_all_cb, - simple); + data->cancellable, + (GAsyncReadyCallback) async_init_get_all_cb, + data); } else { - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); + g_simple_async_result_complete_in_idle (data->simple); + async_init_data_free (data); + } + + out: + ; +} + +static void +async_init_call_get_name_owner (AsyncInitData *data) +{ + g_dbus_connection_call (data->proxy->priv->connection, + "org.freedesktop.DBus", /* name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface */ + "GetNameOwner", + g_variant_new ("(s)", + data->proxy->priv->name), + G_VARIANT_TYPE ("(s)"), + G_DBUS_CALL_FLAGS_NONE, + -1, /* timeout */ + data->cancellable, + (GAsyncReadyCallback) async_init_get_name_owner_cb, + data); +} + +static void +async_init_start_service_by_name_cb (GDBusConnection *connection, + GAsyncResult *res, + gpointer user_data) +{ + AsyncInitData *data = user_data; + GError *error; + GVariant *result; + + error = NULL; + result = g_dbus_connection_call_finish (connection, + res, + &error); + if (result == NULL) + { + /* Errors are not unexpected; the bus will reply e.g. + * + * org.freedesktop.DBus.Error.ServiceUnknown: The name org.gnome.Epiphany2 + * was not provided by any .service files + * + * This doesn't mean that the name doesn't have an owner, just + * that it's not provided by a .service file. So just proceed to + * invoke GetNameOwner() if dealing with that error. + */ + if (error->domain == G_DBUS_ERROR && + error->code == G_DBUS_ERROR_SERVICE_UNKNOWN) + { + g_error_free (error); + } + else + { + g_prefix_error (&error, + _("Error calling StartServiceByName for %s: "), + data->proxy->priv->name); + goto failed; + } + } + else + { + guint32 start_service_result; + g_variant_get (result, + "(u)", + &start_service_result); + g_variant_unref (result); + if (start_service_result == 1 || /* DBUS_START_REPLY_SUCCESS */ + start_service_result == 2) /* DBUS_START_REPLY_ALREADY_RUNNING */ + { + /* continue to invoke GetNameOwner() */ + } + else + { + error = g_error_new (G_IO_ERROR, + G_IO_ERROR_FAILED, + _("Unexpected reply %d from StartServiceByName(\"%s\") method"), + start_service_result, + data->proxy->priv->name); + goto failed; + } + } + + async_init_call_get_name_owner (data); + return; + + failed: + g_warn_if_fail (error != NULL); + g_simple_async_result_set_from_error (data->simple, error); + g_error_free (error); + g_simple_async_result_complete_in_idle (data->simple); + async_init_data_free (data); +} + +static void +async_init_call_start_service_by_name (AsyncInitData *data) +{ + g_dbus_connection_call (data->proxy->priv->connection, + "org.freedesktop.DBus", /* name */ + "/org/freedesktop/DBus", /* object path */ + "org.freedesktop.DBus", /* interface */ + "StartServiceByName", + g_variant_new ("(su)", + data->proxy->priv->name, + 0), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, + -1, /* timeout */ + data->cancellable, + (GAsyncReadyCallback) async_init_start_service_by_name_cb, + data); +} + +static void +async_initable_init_second_async (GAsyncInitable *initable, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDBusProxy *proxy = G_DBUS_PROXY (initable); + AsyncInitData *data; + + data = g_new0 (AsyncInitData, 1); + data->proxy = g_object_ref (proxy); + data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL; + data->simple = g_simple_async_result_new (G_OBJECT (proxy), + callback, + user_data, + NULL); + + /* Check name ownership asynchronously - possibly also start the service */ + if (proxy->priv->name == NULL) + { + /* Do nothing */ + async_init_get_name_owner_cb (proxy->priv->connection, NULL, data); + } + else if (g_dbus_is_unique_name (proxy->priv->name)) + { + proxy->priv->name_owner = g_strdup (proxy->priv->name); + async_init_get_name_owner_cb (proxy->priv->connection, NULL, data); + } + else + { + if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START) + { + async_init_call_get_name_owner (data); + } + else + { + async_init_call_start_service_by_name (data); + } } } static gboolean -async_initable_init_finish (GAsyncInitable *initable, - GAsyncResult *res, - GError **error) +async_initable_init_second_finish (GAsyncInitable *initable, + GAsyncResult *res, + GError **error) { GDBusProxy *proxy = G_DBUS_PROXY (initable); GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res); @@ -999,6 +1311,156 @@ async_initable_init_finish (GAsyncInitable *initable, return ret; } +/* ---------------------------------------------------------------------------------------------------- */ + +static void +async_initable_init_first (GAsyncInitable *initable) +{ + GDBusProxy *proxy = G_DBUS_PROXY (initable); + + if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES)) + { + /* subscribe to PropertiesChanged() */ + proxy->priv->properties_changed_subscriber_id = + g_dbus_connection_signal_subscribe (proxy->priv->connection, + proxy->priv->name, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + proxy->priv->object_path, + proxy->priv->interface_name, + on_properties_changed, + proxy, + NULL); + } + + if (!(proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS)) + { + /* subscribe to all signals for the object */ + proxy->priv->signals_subscriber_id = + g_dbus_connection_signal_subscribe (proxy->priv->connection, + proxy->priv->name, + proxy->priv->interface_name, + NULL, /* member */ + proxy->priv->object_path, + NULL, /* arg0 */ + on_signal_received, + proxy, + NULL); + } + + if (proxy->priv->name != NULL && !g_dbus_is_unique_name (proxy->priv->name)) + { + proxy->priv->name_owner_changed_subscription_id = + g_dbus_connection_signal_subscribe (proxy->priv->connection, + "org.freedesktop.DBus", /* name */ + "org.freedesktop.DBus", /* interface */ + "NameOwnerChanged", /* signal name */ + "/org/freedesktop/DBus", /* path */ + proxy->priv->name, /* arg0 */ + on_name_owner_changed, + proxy, + NULL); + } +} + +/* ---------------------------------------------------------------------------------------------------- */ + +/* initialization is split into two parts - the first is the + * non-blocing part that requires the callers GMainContext - the + * second is a blocking part async part that doesn't require the + * callers GMainContext.. we do this split so the code can be reused + * in the GInitable implementation below. + * + * Note that obtaining a GDBusConnection is not shared between the two + * paths. + */ + +typedef struct +{ + GDBusProxy *proxy; + gint io_priority; + GCancellable *cancellable; + GAsyncReadyCallback callback; + gpointer user_data; +} GetConnectionData; + +static void +get_connection_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GetConnectionData *data = user_data; + GError *error; + + data->proxy->priv->connection = g_bus_get_finish (res, &error); + if (data->proxy->priv->connection == NULL) + { + GSimpleAsyncResult *simple; + simple = g_simple_async_result_new (G_OBJECT (data->proxy), + data->callback, + data->user_data, + NULL); + g_simple_async_result_set_from_error (simple, error); + g_error_free (error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + } + else + { + async_initable_init_first (G_ASYNC_INITABLE (data->proxy)); + async_initable_init_second_async (G_ASYNC_INITABLE (data->proxy), + data->io_priority, + data->cancellable, + data->callback, + data->user_data); + } + + if (data->cancellable != NULL) + g_object_unref (data->cancellable); + g_free (data); +} + +static void +async_initable_init_async (GAsyncInitable *initable, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDBusProxy *proxy = G_DBUS_PROXY (initable); + + if (proxy->priv->bus_type != G_BUS_TYPE_NONE) + { + GetConnectionData *data; + + g_assert (proxy->priv->connection == NULL); + + data = g_new0 (GetConnectionData, 1); + data->proxy = proxy; + data->io_priority = io_priority; + data->cancellable = cancellable != NULL ? g_object_ref (cancellable) : NULL; + data->callback = callback; + data->user_data = user_data; + g_bus_get (proxy->priv->bus_type, + cancellable, + get_connection_cb, + data); + } + else + { + async_initable_init_first (initable); + async_initable_init_second_async (initable, io_priority, cancellable, callback, user_data); + } +} + +static gboolean +async_initable_init_finish (GAsyncInitable *initable, + GAsyncResult *res, + GError **error) +{ + return async_initable_init_second_finish (initable, res, error); +} + static void async_initable_iface_init (GAsyncInitableIface *async_initable_iface) { @@ -1008,42 +1470,135 @@ async_initable_iface_init (GAsyncInitableIface *async_initable_iface) /* ---------------------------------------------------------------------------------------------------- */ +typedef struct +{ + GMainContext *context; + GMainLoop *loop; + GAsyncResult *res; +} InitableAsyncInitableData; + +static void +async_initable_init_async_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + InitableAsyncInitableData *data = user_data; + data->res = g_object_ref (res); + g_main_loop_quit (data->loop); +} + +/* Simply reuse the GAsyncInitable implementation but run the first + * part (that is non-blocking and requires the callers GMainContext) + * with the callers GMainContext.. and the second with a private + * GMainContext (bug 621310 is slightly related). + * + * Note that obtaining a GDBusConnection is not shared between the two + * paths. + */ +static gboolean +initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GDBusProxy *proxy = G_DBUS_PROXY (initable); + InitableAsyncInitableData *data; + gboolean ret; + + ret = FALSE; + + if (proxy->priv->bus_type != G_BUS_TYPE_NONE) + { + g_assert (proxy->priv->connection == NULL); + proxy->priv->connection = g_bus_get_sync (proxy->priv->bus_type, + cancellable, + error); + if (proxy->priv->connection == NULL) + goto out; + } + + async_initable_init_first (G_ASYNC_INITABLE (initable)); + + data = g_new0 (InitableAsyncInitableData, 1); + data->context = g_main_context_new (); + data->loop = g_main_loop_new (data->context, FALSE); + + g_main_context_push_thread_default (data->context); + + async_initable_init_second_async (G_ASYNC_INITABLE (initable), + G_PRIORITY_DEFAULT, + cancellable, + async_initable_init_async_cb, + data); + + g_main_loop_run (data->loop); + + ret = async_initable_init_second_finish (G_ASYNC_INITABLE (initable), + data->res, + error); + + g_main_context_pop_thread_default (data->context); + + g_main_context_unref (data->context); + g_main_loop_unref (data->loop); + g_object_unref (data->res); + g_free (data); + + out: + + return ret; +} + +static void +initable_iface_init (GInitableIface *initable_iface) +{ + initable_iface->init = initable_init; +} + +/* ---------------------------------------------------------------------------------------------------- */ + /** * g_dbus_proxy_new: * @connection: A #GDBusConnection. - * @object_type: Either #G_TYPE_DBUS_PROXY or the #GType for the #GDBusProxy-derived type of proxy to create. * @flags: Flags used when constructing the proxy. * @info: A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL. - * @unique_bus_name: A unique bus name or %NULL if @connection is not a message bus connection. + * @name: A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. * @object_path: An object path. * @interface_name: A D-Bus interface name. * @cancellable: A #GCancellable or %NULL. * @callback: Callback function to invoke when the proxy is ready. * @user_data: User data to pass to @callback. * - * Creates a proxy for accessing @interface_name on the remote object at @object_path - * owned by @unique_bus_name at @connection and asynchronously loads D-Bus properties unless the - * #G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used. Connect to the - * #GDBusProxy::g-properties-changed signal to get notified about property changes. + * Creates a proxy for accessing @interface_name on the remote object + * at @object_path owned by @name at @connection and asynchronously + * loads D-Bus properties unless the + * #G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used. Connect to + * the #GDBusProxy::g-properties-changed signal to get notified about + * property changes. * * If the #G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up * match rules for signals. Connect to the #GDBusProxy::g-signal signal * to handle signals from the remote object. * + * If @name is a well-known name and the + * #G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag isn't set and no name + * owner currently exists, the message bus will be requested to launch + * a name owner for the name. + * * This is a failable asynchronous constructor - when the proxy is * ready, @callback will be invoked and you can use * g_dbus_proxy_new_finish() to get the result. * * See g_dbus_proxy_new_sync() and for a synchronous version of this constructor. * + * See for an example of how #GDBusProxy can be used. + * * Since: 2.26 */ void g_dbus_proxy_new (GDBusConnection *connection, - GType object_type, GDBusProxyFlags flags, GDBusInterfaceInfo *info, - const gchar *unique_bus_name, + const gchar *name, const gchar *object_path, const gchar *interface_name, GCancellable *cancellable, @@ -1051,20 +1606,18 @@ g_dbus_proxy_new (GDBusConnection *connection, gpointer user_data) { g_return_if_fail (G_IS_DBUS_CONNECTION (connection)); - g_return_if_fail (g_type_is_a (object_type, G_TYPE_DBUS_PROXY)); - g_return_if_fail ((unique_bus_name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) || - g_dbus_is_unique_name (unique_bus_name)); + g_return_if_fail ((name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) || g_dbus_is_name (name)); g_return_if_fail (g_variant_is_object_path (object_path)); g_return_if_fail (g_dbus_is_interface_name (interface_name)); - g_async_initable_new_async (object_type, + g_async_initable_new_async (G_TYPE_DBUS_PROXY, G_PRIORITY_DEFAULT, cancellable, callback, user_data, "g-flags", flags, "g-interface-info", info, - "g-unique-bus-name", unique_bus_name, + "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", interface_name, @@ -1103,42 +1656,45 @@ g_dbus_proxy_new_finish (GAsyncResult *res, return NULL; } - -/* ---------------------------------------------------------------------------------------------------- */ - /** * g_dbus_proxy_new_sync: * @connection: A #GDBusConnection. - * @object_type: Either #G_TYPE_DBUS_PROXY or the #GType for the #GDBusProxy-derived type of proxy to create. * @flags: Flags used when constructing the proxy. * @info: A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL. - * @unique_bus_name: A unique bus name or %NULL if @connection is not a message bus connection. + * @name: A bus name (well-known or unique) or %NULL if @connection is not a message bus connection. * @object_path: An object path. * @interface_name: A D-Bus interface name. * @cancellable: A #GCancellable or %NULL. * @error: Return location for error or %NULL. * - * Creates a proxy for accessing @interface_name on the remote object at @object_path - * owned by @unique_bus_name at @connection and synchronously loads D-Bus properties unless the + * Creates a proxy for accessing @interface_name on the remote object + * at @object_path owned by @name at @connection and synchronously + * loads D-Bus properties unless the * #G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES flag is used. * * If the #G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS flag is not set, also sets up * match rules for signals. Connect to the #GDBusProxy::g-signal signal * to handle signals from the remote object. * + * If @name is a well-known name and the + * #G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag isn't set and no name + * owner currently exists, the message bus will be requested to launch + * a name owner for the name. + * * This is a synchronous failable constructor. See g_dbus_proxy_new() * and g_dbus_proxy_new_finish() for the asynchronous version. * + * See for an example of how #GDBusProxy can be used. + * * Returns: A #GDBusProxy or %NULL if error is set. Free with g_object_unref(). * * Since: 2.26 */ GDBusProxy * g_dbus_proxy_new_sync (GDBusConnection *connection, - GType object_type, GDBusProxyFlags flags, GDBusInterfaceInfo *info, - const gchar *unique_bus_name, + const gchar *name, const gchar *object_path, const gchar *interface_name, GCancellable *cancellable, @@ -1147,18 +1703,17 @@ g_dbus_proxy_new_sync (GDBusConnection *connection, GInitable *initable; g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL); - g_return_val_if_fail (g_type_is_a (object_type, G_TYPE_DBUS_PROXY), NULL); - g_return_val_if_fail ((unique_bus_name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) || - g_dbus_is_unique_name (unique_bus_name), NULL); + g_return_val_if_fail ((name == NULL && g_dbus_connection_get_unique_name (connection) == NULL) || + g_dbus_is_name (name), NULL); g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL); - initable = g_initable_new (object_type, + initable = g_initable_new (G_TYPE_DBUS_PROXY, cancellable, error, "g-flags", flags, "g-interface-info", info, - "g-unique-bus-name", unique_bus_name, + "g-name", name, "g-connection", connection, "g-object-path", object_path, "g-interface-name", interface_name, @@ -1171,6 +1726,124 @@ g_dbus_proxy_new_sync (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ +/** + * g_dbus_proxy_new_for_bus: + * @bus_type: A #GBusType. + * @flags: Flags used when constructing the proxy. + * @info: A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @interface_name: A D-Bus interface name. + * @cancellable: A #GCancellable or %NULL. + * @callback: Callback function to invoke when the proxy is ready. + * @user_data: User data to pass to @callback. + * + * Like g_dbus_proxy_new() but takes a #GBusType instead of a #GDBusConnection. + * + * See for an example of how #GDBusProxy can be used. + * + * Since: 2.26 + */ +void +g_dbus_proxy_new_for_bus (GBusType bus_type, + GDBusProxyFlags flags, + GDBusInterfaceInfo *info, + const gchar *name, + const gchar *object_path, + const gchar *interface_name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (g_dbus_is_name (name)); + g_return_if_fail (g_variant_is_object_path (object_path)); + g_return_if_fail (g_dbus_is_interface_name (interface_name)); + + g_async_initable_new_async (G_TYPE_DBUS_PROXY, + G_PRIORITY_DEFAULT, + cancellable, + callback, + user_data, + "g-flags", flags, + "g-interface-info", info, + "g-name", name, + "g-bus-type", bus_type, + "g-object-path", object_path, + "g-interface-name", interface_name, + NULL); +} + +/** + * g_dbus_proxy_new_for_bus_finish: + * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback function passed to g_dbus_proxy_new_for_bus(). + * @error: Return location for error or %NULL. + * + * Finishes creating a #GDBusProxy. + * + * Returns: A #GDBusProxy or %NULL if @error is set. Free with g_object_unref(). + * + * Since: 2.26 + */ +GDBusProxy * +g_dbus_proxy_new_for_bus_finish (GAsyncResult *res, + GError **error) +{ + return g_dbus_proxy_new_finish (res, error); +} + +/** + * g_dbus_proxy_new_for_bus_sync: + * @bus_type: A #GBusType. + * @flags: Flags used when constructing the proxy. + * @info: A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL. + * @name: A bus name (well-known or unique). + * @object_path: An object path. + * @interface_name: A D-Bus interface name. + * @cancellable: A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Like g_dbus_proxy_new_sync() but takes a #GBusType instead of a #GDBusConnection. + * + * See for an example of how #GDBusProxy can be used. + * + * Returns: A #GDBusProxy or %NULL if error is set. Free with g_object_unref(). + * + * Since: 2.26 + */ +GDBusProxy * +g_dbus_proxy_new_for_bus_sync (GBusType bus_type, + GDBusProxyFlags flags, + GDBusInterfaceInfo *info, + const gchar *name, + const gchar *object_path, + const gchar *interface_name, + GCancellable *cancellable, + GError **error) +{ + GInitable *initable; + + g_return_val_if_fail (g_dbus_is_name (name), NULL); + g_return_val_if_fail (g_variant_is_object_path (object_path), NULL); + g_return_val_if_fail (g_dbus_is_interface_name (interface_name), NULL); + + initable = g_initable_new (G_TYPE_DBUS_PROXY, + cancellable, + error, + "g-flags", flags, + "g-interface-info", info, + "g-name", name, + "g-bus-type", bus_type, + "g-object-path", object_path, + "g-interface-name", interface_name, + NULL); + if (initable != NULL) + return G_DBUS_PROXY (initable); + else + return NULL; +} + +/* ---------------------------------------------------------------------------------------------------- */ + /** * g_dbus_proxy_get_connection: * @proxy: A #GDBusProxy. @@ -1206,20 +1879,40 @@ g_dbus_proxy_get_flags (GDBusProxy *proxy) } /** - * g_dbus_proxy_get_unique_bus_name: + * g_dbus_proxy_get_name: * @proxy: A #GDBusProxy. * - * Gets the unique bus name @proxy is for. + * Gets the name that @proxy was constructed for. * * Returns: A string owned by @proxy. Do not free. * * Since: 2.26 */ const gchar * -g_dbus_proxy_get_unique_bus_name (GDBusProxy *proxy) +g_dbus_proxy_get_name (GDBusProxy *proxy) { g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); - return proxy->priv->unique_bus_name; + return proxy->priv->name; +} + +/** + * g_dbus_proxy_get_name_owner: + * @proxy: A #GDBusProxy. + * + * The unique name that owns the name that @proxy is for or %NULL if + * no-one currently owns that name. You may connect to the + * #GObject::notify signal to track changes to the + * #GDBusProxy:g-name-owner property. + * + * Returns: The name owner or %NULL if no name owner exists. Free with g_free(). + * + * Since: 2.26 + */ +gchar * +g_dbus_proxy_get_name_owner (GDBusProxy *proxy) +{ + g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL); + return g_strdup (proxy->priv->name_owner); } /** @@ -1528,7 +2221,7 @@ g_dbus_proxy_call (GDBusProxy *proxy, reply_type = NULL; g_dbus_connection_call (proxy->priv->connection, - proxy->priv->unique_bus_name, + proxy->priv->name_owner, proxy->priv->object_path, target_interface_name, target_method_name, @@ -1681,7 +2374,7 @@ g_dbus_proxy_call_sync (GDBusProxy *proxy, reply_type = NULL; ret = g_dbus_connection_call_sync (proxy->priv->connection, - proxy->priv->unique_bus_name, + proxy->priv->name_owner, proxy->priv->object_path, target_interface_name, target_method_name, diff --git a/gio/gdbusproxy.h b/gio/gdbusproxy.h index 9279353d4..52942295e 100644 --- a/gio/gdbusproxy.h +++ b/gio/gdbusproxy.h @@ -95,10 +95,9 @@ struct _GDBusProxyClass GType g_dbus_proxy_get_type (void) G_GNUC_CONST; void g_dbus_proxy_new (GDBusConnection *connection, - GType object_type, GDBusProxyFlags flags, GDBusInterfaceInfo *info, - const gchar *unique_bus_name, + const gchar *name, const gchar *object_path, const gchar *interface_name, GCancellable *cancellable, @@ -107,17 +106,36 @@ void g_dbus_proxy_new (GDBusConnection *co GDBusProxy *g_dbus_proxy_new_finish (GAsyncResult *res, GError **error); GDBusProxy *g_dbus_proxy_new_sync (GDBusConnection *connection, - GType object_type, GDBusProxyFlags flags, GDBusInterfaceInfo *info, - const gchar *unique_bus_name, + const gchar *name, + const gchar *object_path, + const gchar *interface_name, + GCancellable *cancellable, + GError **error); +void g_dbus_proxy_new_for_bus (GBusType bus_type, + GDBusProxyFlags flags, + GDBusInterfaceInfo *info, + const gchar *name, + const gchar *object_path, + const gchar *interface_name, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +GDBusProxy *g_dbus_proxy_new_for_bus_finish (GAsyncResult *res, + GError **error); +GDBusProxy *g_dbus_proxy_new_for_bus_sync (GBusType bus_type, + GDBusProxyFlags flags, + GDBusInterfaceInfo *info, + const gchar *name, const gchar *object_path, const gchar *interface_name, GCancellable *cancellable, GError **error); GDBusConnection *g_dbus_proxy_get_connection (GDBusProxy *proxy); GDBusProxyFlags g_dbus_proxy_get_flags (GDBusProxy *proxy); -const gchar *g_dbus_proxy_get_unique_bus_name (GDBusProxy *proxy); +const gchar *g_dbus_proxy_get_name (GDBusProxy *proxy); +gchar *g_dbus_proxy_get_name_owner (GDBusProxy *proxy); const gchar *g_dbus_proxy_get_object_path (GDBusProxy *proxy); const gchar *g_dbus_proxy_get_interface_name (GDBusProxy *proxy); gint g_dbus_proxy_get_default_timeout (GDBusProxy *proxy); diff --git a/gio/gdbusproxywatching.c b/gio/gdbusproxywatching.c deleted file mode 100644 index 48dabd469..000000000 --- a/gio/gdbusproxywatching.c +++ /dev/null @@ -1,666 +0,0 @@ -/* GDBus - GLib D-Bus Library - * - * Copyright (C) 2008-2010 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. - * - * Author: David Zeuthen - */ - -#include "config.h" - -#include - -#include "gdbusutils.h" -#include "gdbusconnection.h" -#include "gdbusnamewatching.h" -#include "gdbusproxywatching.h" -#include "gdbuserror.h" -#include "gdbusprivate.h" -#include "gdbusproxy.h" -#include "gcancellable.h" - -#include "glibintl.h" -#include "gioalias.h" - -/** - * SECTION:gdbusproxywatching - * @title: Watching Proxies - * @short_description: Simple API for watching proxies - * @include: gio/gio.h - * - * Convenience API for watching bus proxies. - * - * Simple application watching a proxyFIXME: MISSING XINCLUDE CONTENT - */ - -/* ---------------------------------------------------------------------------------------------------- */ - -G_LOCK_DEFINE_STATIC (lock); - -static guint next_global_id = 1; -static GHashTable *map_id_to_client = NULL; - -/* ---------------------------------------------------------------------------------------------------- */ - -typedef struct -{ - guint id; - GBusProxyAppearedCallback proxy_appeared_handler; - GBusProxyVanishedCallback proxy_vanished_handler; - gpointer user_data; - GDestroyNotify user_data_free_func; - GMainContext *main_context; - - gchar *name; - gchar *name_owner; - GDBusConnection *connection; - guint name_watcher_id; - - GCancellable *cancellable; - - gchar *object_path; - gchar *interface_name; - GType interface_type; - GDBusProxyFlags proxy_flags; - GDBusProxy *proxy; - - gboolean initial_construction; -} Client; - -static void -client_unref (Client *client) -{ - /* ensure we're only called from g_bus_unwatch_proxy */ - g_assert (client->name_watcher_id == 0); - - g_free (client->name_owner); - if (client->connection != NULL) - g_object_unref (client->connection); - if (client->proxy != NULL) - g_object_unref (client->proxy); - - g_free (client->name); - g_free (client->object_path); - g_free (client->interface_name); - - if (client->main_context != NULL) - g_main_context_unref (client->main_context); - - if (client->user_data_free_func != NULL) - client->user_data_free_func (client->user_data); - g_free (client); -} - -/* ---------------------------------------------------------------------------------------------------- */ - -static void -proxy_constructed_cb (GObject *source_object, - GAsyncResult *res, - gpointer user_data) -{ - Client *client = user_data; - GDBusProxy *proxy; - GError *error; - - error = NULL; - proxy = g_dbus_proxy_new_finish (res, &error); - if (proxy == NULL) - { - /* g_warning ("error while constructing proxy: %s", error->message); */ - g_error_free (error); - - /* handle initial construction, send out vanished if the name - * is there but we constructing a proxy fails - */ - if (client->initial_construction) - { - if (client->proxy_vanished_handler != NULL) - { - client->proxy_vanished_handler (client->connection, - client->name, - client->user_data); - } - client->initial_construction = FALSE; - } - } - else - { - g_assert (client->proxy == NULL); - g_assert (client->cancellable != NULL); - client->proxy = G_DBUS_PROXY (proxy); - - g_object_unref (client->cancellable); - client->cancellable = NULL; - - /* perform callback */ - if (client->proxy_appeared_handler != NULL) - { - client->proxy_appeared_handler (client->connection, - client->name, - client->name_owner, - client->proxy, - client->user_data); - } - client->initial_construction = FALSE; - } -} - -static void -on_name_appeared (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - gpointer user_data) -{ - Client *client = user_data; - - //g_debug ("\n\nname appeared (owner `%s')", name_owner); - - /* invariants */ - g_assert (client->name_owner == NULL); - g_assert (client->connection == NULL); - g_assert (client->cancellable == NULL); - - client->name_owner = g_strdup (name_owner); - client->connection = g_object_ref (connection); - client->cancellable = g_cancellable_new (); - - g_dbus_proxy_new (client->connection, - client->interface_type, - client->proxy_flags, - NULL, /* GDBusInterfaceInfo */ - client->name_owner, - client->object_path, - client->interface_name, - client->cancellable, - proxy_constructed_cb, - client); -} - -static void -on_name_vanished (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - Client *client = user_data; - - /*g_debug ("\n\nname vanished");*/ - - g_free (client->name_owner); - if (client->connection != NULL) - g_object_unref (client->connection); - client->name_owner = NULL; - client->connection = NULL; - - /* free the proxy if we have it */ - if (client->proxy != NULL) - { - g_assert (client->cancellable == NULL); - - g_object_unref (client->proxy); - client->proxy = NULL; - - /* if we have the proxy, it means we last sent out a 'appeared' - * callback - so send out a 'vanished' callback - */ - if (client->proxy_vanished_handler != NULL) - { - client->proxy_vanished_handler (client->connection, - client->name, - client->user_data); - } - client->initial_construction = FALSE; - } - else - { - /* otherwise cancel construction of the proxy if applicable */ - if (client->cancellable != NULL) - { - g_cancellable_cancel (client->cancellable); - g_object_unref (client->cancellable); - client->cancellable = NULL; - } - else - { - /* handle initial construction, send out vanished if - * the name isn't there - */ - if (client->initial_construction) - { - if (client->proxy_vanished_handler != NULL) - { - client->proxy_vanished_handler (client->connection, - client->name, - client->user_data); - } - client->initial_construction = FALSE; - } - } - } -} - -/** - * g_bus_watch_proxy: - * @bus_type: The type of bus to watch a name on. - * @name: The name (well-known or unique) to watch. - * @flags: Flags from the #GBusNameWatcherFlags enumeration. - * @object_path: The object path of the remote object to watch. - * @interface_name: The D-Bus interface name for the proxy. - * @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type. - * @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy. - * @proxy_appeared_handler: Handler to invoke when @name is known to exist and the - * requested proxy is available. - * @proxy_vanished_handler: Handler to invoke when @name is known to not exist - * and the previously created proxy is no longer available. - * @user_data: User data to pass to handlers. - * @user_data_free_func: Function for freeing @user_data or %NULL. - * - * Starts watching a remote object at @object_path owned by @name on - * the bus specified by @bus_type. When the object is available, a - * #GDBusProxy (or derived class cf. @interface_type) instance is - * constructed for the @interface_name D-Bus interface and then - * @proxy_appeared_handler will be called when the proxy is ready and - * all properties have been loaded. When @name vanishes, - * @proxy_vanished_handler is called. - * - * This function makes it very simple to write applications that wants - * to watch a well-known remote object on a well-known name, see . Basically, the application simply - * starts using the proxy when @proxy_appeared_handler is called and - * stops using it when @proxy_vanished_handler is called. Callbacks - * will be invoked in the thread-default main - * loop of the thread you are calling this function from. - * - * Applications typically use this function to watch the - * manager object of a well-known name. Upon acquiring - * a proxy for the manager object, applications typically construct - * additional proxies in response to the result of enumeration methods - * on the manager object. - * - * Many of the comments that apply to g_bus_watch_name() also apply - * here. For example, you are guaranteed that one of the handlers will - * be invoked (on the main thread) after calling this function and - * also that the two handlers alternate. When you are done watching the - * proxy, just call g_bus_unwatch_proxy(). - * - * Returns: An identifier (never 0) that can be used with - * g_bus_unwatch_proxy() to stop watching the remote object. - * - * Since: 2.26 - */ -guint -g_bus_watch_proxy (GBusType bus_type, - const gchar *name, - GBusNameWatcherFlags flags, - const gchar *object_path, - const gchar *interface_name, - GType interface_type, - GDBusProxyFlags proxy_flags, - GBusProxyAppearedCallback proxy_appeared_handler, - GBusProxyVanishedCallback proxy_vanished_handler, - gpointer user_data, - GDestroyNotify user_data_free_func) -{ - Client *client; - - g_return_val_if_fail (g_dbus_is_name (name), 0); - g_return_val_if_fail (g_variant_is_object_path (object_path), 0); - g_return_val_if_fail (g_dbus_is_interface_name (interface_name), 0); - g_return_val_if_fail (g_type_is_a (interface_type, G_TYPE_DBUS_PROXY), 0); - - G_LOCK (lock); - - client = g_new0 (Client, 1); - client->id = next_global_id++; /* TODO: uh oh, handle overflow */ - client->name = g_strdup (name); - client->proxy_appeared_handler = proxy_appeared_handler; - client->proxy_vanished_handler = proxy_vanished_handler; - client->user_data = user_data; - client->user_data_free_func = user_data_free_func; - client->main_context = g_main_context_get_thread_default (); - if (client->main_context != NULL) - g_main_context_ref (client->main_context); - client->name_watcher_id = g_bus_watch_name (bus_type, - name, - flags, - on_name_appeared, - on_name_vanished, - client, - NULL); - - client->object_path = g_strdup (object_path); - client->interface_name = g_strdup (interface_name); - client->interface_type = interface_type; - client->proxy_flags = proxy_flags; - client->initial_construction = TRUE; - - if (map_id_to_client == NULL) - { - map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal); - } - g_hash_table_insert (map_id_to_client, - GUINT_TO_POINTER (client->id), - client); - - G_UNLOCK (lock); - - return client->id; -} - -/** - * g_bus_watch_proxy_on_connection: - * @connection: A #GDBusConnection that is not closed. - * @name: The name (well-known or unique) to watch. - * @flags: Flags from the #GBusNameWatcherFlags enumeration. - * @object_path: The object path of the remote object to watch. - * @interface_name: The D-Bus interface name for the proxy. - * @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type. - * @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy. - * @proxy_appeared_handler: Handler to invoke when @name is known to exist and the - * requested proxy is available. - * @proxy_vanished_handler: Handler to invoke when @name is known to not exist - * and the previously created proxy is no longer available. - * @user_data: User data to pass to handlers. - * @user_data_free_func: Function for freeing @user_data or %NULL. - * - * Like g_bus_watch_proxy() but takes a #GDBusConnection instead of a - * #GBusType. - * - * Returns: An identifier (never 0) that can be used with - * g_bus_unwatch_proxy() to stop watching the remote object. - * - * Since: 2.26 - */ -guint -g_bus_watch_proxy_on_connection (GDBusConnection *connection, - const gchar *name, - GBusNameWatcherFlags flags, - const gchar *object_path, - const gchar *interface_name, - GType interface_type, - GDBusProxyFlags proxy_flags, - GBusProxyAppearedCallback proxy_appeared_handler, - GBusProxyVanishedCallback proxy_vanished_handler, - gpointer user_data, - GDestroyNotify user_data_free_func) -{ - Client *client; - - g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), 0); - g_return_val_if_fail (g_dbus_is_name (name), 0); - g_return_val_if_fail (g_variant_is_object_path (object_path), 0); - g_return_val_if_fail (g_dbus_is_interface_name (interface_name), 0); - g_return_val_if_fail (g_type_is_a (interface_type, G_TYPE_DBUS_PROXY), 0); - - G_LOCK (lock); - - client = g_new0 (Client, 1); - client->id = next_global_id++; /* TODO: uh oh, handle overflow */ - client->name = g_strdup (name); - client->proxy_appeared_handler = proxy_appeared_handler; - client->proxy_vanished_handler = proxy_vanished_handler; - client->user_data = user_data; - client->user_data_free_func = user_data_free_func; - client->main_context = g_main_context_get_thread_default (); - if (client->main_context != NULL) - g_main_context_ref (client->main_context); - client->name_watcher_id = g_bus_watch_name_on_connection (connection, - name, - flags, - on_name_appeared, - on_name_vanished, - client, - NULL); - - client->object_path = g_strdup (object_path); - client->interface_name = g_strdup (interface_name); - client->interface_type = interface_type; - client->proxy_flags = proxy_flags; - client->initial_construction = TRUE; - - if (map_id_to_client == NULL) - { - map_id_to_client = g_hash_table_new (g_direct_hash, g_direct_equal); - } - g_hash_table_insert (map_id_to_client, - GUINT_TO_POINTER (client->id), - client); - - G_UNLOCK (lock); - - return client->id; -} - -typedef struct { - GClosure *proxy_appeared_closure; - GClosure *proxy_vanished_closure; -} WatchProxyData; - -static void -watch_with_closures_on_proxy_appeared (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy, - gpointer user_data) -{ - WatchProxyData *data = user_data; - GValue params[4] = { { 0, }, { 0, }, { 0, }, { 0, } }; - - g_value_init (¶ms[0], G_TYPE_DBUS_CONNECTION); - g_value_set_object (¶ms[0], connection); - - g_value_init (¶ms[1], G_TYPE_STRING); - g_value_set_string (¶ms[1], name); - - g_value_init (¶ms[2], G_TYPE_STRING); - g_value_set_string (¶ms[2], name_owner); - - g_value_init (¶ms[3], G_TYPE_DBUS_PROXY); - g_value_set_object (¶ms[3], proxy); - - g_closure_invoke (data->proxy_appeared_closure, NULL, 4, params, NULL); -} - -static void -watch_with_closures_on_proxy_vanished (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ - WatchProxyData *data = user_data; - GValue params[2] = { { 0, }, { 0, } }; - - g_value_init (¶ms[0], G_TYPE_DBUS_CONNECTION); - g_value_set_object (¶ms[0], connection); - - g_value_init (¶ms[1], G_TYPE_STRING); - g_value_set_string (¶ms[1], name); - - g_closure_invoke (data->proxy_vanished_closure, NULL, 2, params, NULL); -} - -static void -bus_watch_proxy_free_func (gpointer user_data) -{ - WatchProxyData *data = user_data; - - if (data->proxy_appeared_closure != NULL) - g_closure_unref (data->proxy_appeared_closure); - - if (data->proxy_vanished_closure != NULL) - g_closure_unref (data->proxy_vanished_closure); - - g_free (data); -} - -/** - * g_bus_watch_proxy_with_closures: - * @bus_type: The type of bus to watch a name on. - * @name: The name (well-known or unique) to watch. - * @flags: Flags from the #GBusNameWatcherFlags enumeration. - * @object_path: The object path of the remote object to watch. - * @interface_name: The D-Bus interface name for the proxy. - * @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type. - * @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy. - * @proxy_appeared_closure: (allow-none): #GClosure to invoke when @name is - * known to exist and the requested proxy is available. - * @proxy_vanished_closure: (allow-none): #GClosure to invoke when @name is - * known to not exist and the previously created proxy is no longer available. - * - * Version of g_bus_watch_proxy() using closures instead of callbacks for - * easier binding in other languages. - * - * Returns: An identifier (never 0) that can be used with - * g_bus_unwatch_proxy() to stop watching the remote object. - * - * Rename to: g_bus_watch_proxy - * - * Since: 2.26 - */ -guint -g_bus_watch_proxy_with_closures (GBusType bus_type, - const gchar *name, - GBusNameWatcherFlags flags, - const gchar *object_path, - const gchar *interface_name, - GType interface_type, - GDBusProxyFlags proxy_flags, - GClosure *proxy_appeared_closure, - GClosure *proxy_vanished_closure) -{ - WatchProxyData *data; - - data = g_new0 (WatchProxyData, 1); - - if (proxy_appeared_closure != NULL) - data->proxy_appeared_closure = g_closure_ref (proxy_appeared_closure); - - if (proxy_vanished_closure != NULL) - data->proxy_vanished_closure = g_closure_ref (proxy_vanished_closure); - - return g_bus_watch_proxy (bus_type, - name, - flags, - object_path, - interface_name, - interface_type, - proxy_flags, - proxy_appeared_closure != NULL ? watch_with_closures_on_proxy_appeared : NULL, - proxy_vanished_closure != NULL ? watch_with_closures_on_proxy_vanished : NULL, - data, - bus_watch_proxy_free_func); -} - -/** - * g_bus_watch_proxy_on_connection_with_closures: - * @connection: A #GDBusConnection that is not closed. - * @name: The name (well-known or unique) to watch. - * @flags: Flags from the #GBusNameWatcherFlags enumeration. - * @object_path: The object path of the remote object to watch. - * @interface_name: The D-Bus interface name for the proxy. - * @interface_type: The #GType for the kind of proxy to create. This must be a #GDBusProxy derived type. - * @proxy_flags: Flags from #GDBusProxyFlags to use when constructing the proxy. - * @proxy_appeared_closure: (allow-none): #GClosure to invoke when @name is - * known to exist and the requested proxy is available. - * @proxy_vanished_closure: (allow-none): #GClosure to invoke when @name is - * known to not exist and the previously created proxy is no longer available. - * - * Version of g_bus_watch_proxy_on_connection() using closures instead of - * callbacks for easier binding in other languages. - * - * Returns: An identifier (never 0) that can be used with - * g_bus_unwatch_proxy() to stop watching the remote object. - * - * Rename to: g_bus_watch_proxy_on_connection - * - * Since: 2.26 - */ -guint -g_bus_watch_proxy_on_connection_with_closures ( - GDBusConnection *connection, - const gchar *name, - GBusNameWatcherFlags flags, - const gchar *object_path, - const gchar *interface_name, - GType interface_type, - GDBusProxyFlags proxy_flags, - GClosure *proxy_appeared_closure, - GClosure *proxy_vanished_closure) -{ - WatchProxyData *data; - - data = g_new0 (WatchProxyData, 1); - - if (proxy_appeared_closure != NULL) - data->proxy_appeared_closure = g_closure_ref (proxy_appeared_closure); - - if (proxy_vanished_closure != NULL) - data->proxy_vanished_closure = g_closure_ref (proxy_vanished_closure); - - return g_bus_watch_proxy_on_connection (connection, - name, - flags, - object_path, - interface_name, - interface_type, - proxy_flags, - proxy_appeared_closure != NULL ? watch_with_closures_on_proxy_appeared : NULL, - proxy_vanished_closure != NULL ? watch_with_closures_on_proxy_vanished : NULL, - data, - bus_watch_proxy_free_func); -} - -/** - * g_bus_unwatch_proxy: - * @watcher_id: An identifier obtained from g_bus_watch_proxy() - * - * Stops watching proxy. - * - * Since: 2.26 - */ -void -g_bus_unwatch_proxy (guint watcher_id) -{ - Client *client; - - g_return_if_fail (watcher_id > 0); - - client = NULL; - - G_LOCK (lock); - if (watcher_id == 0 || - map_id_to_client == NULL || - (client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (watcher_id))) == NULL) - { - g_warning ("Invalid id %d passed to g_bus_unwatch_proxy()", watcher_id); - goto out; - } - - g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (watcher_id))); - - out: - G_UNLOCK (lock); - - if (client != NULL) - { - g_bus_unwatch_name (client->name_watcher_id); - client->name_watcher_id = 0; - client_unref (client); - } -} - -#define __G_DBUS_PROXY_WATCHING_C__ -#include "gioaliasdef.c" diff --git a/gio/gdbusproxywatching.h b/gio/gdbusproxywatching.h deleted file mode 100644 index 5b3bebdb1..000000000 --- a/gio/gdbusproxywatching.h +++ /dev/null @@ -1,115 +0,0 @@ -/* GDBus - GLib D-Bus Library - * - * Copyright (C) 2008-2010 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. - * - * Author: David Zeuthen - */ - -#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION) -#error "Only can be included directly." -#endif - -#ifndef __G_DBUS_PROXY_WATCHING_H__ -#define __G_DBUS_PROXY_WATCHING_H__ - -#include - -G_BEGIN_DECLS - -/** - * GBusProxyAppearedCallback: - * @connection: The #GDBusConnection the proxy is being watched on. - * @name: The name being watched. - * @name_owner: Unique name of the owner of the name being watched. - * @proxy: A #GDBusProxy (or derived) instance with all properties loaded. - * @user_data: User data passed to g_bus_watch_proxy(). - * - * Invoked when the proxy being watched is ready for use - the passed - * @proxy object is valid until the #GBusProxyVanishedCallback - * callback is invoked. - * - * Since: 2.26 - */ -typedef void (*GBusProxyAppearedCallback) (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy, - gpointer user_data); - -/** - * GBusProxyVanishedCallback: - * @connection: The #GDBusConnection the proxy is being watched on. - * @name: The name being watched. - * @user_data: User data passed to g_bus_watch_proxy(). - * - * Invoked when the proxy being watched has vanished. The #GDBusProxy - * object passed in the #GBusProxyAppearedCallback callback is no - * longer valid. - * - * Since: 2.26 - */ -typedef void (*GBusProxyVanishedCallback) (GDBusConnection *connection, - const gchar *name, - gpointer user_data); - -guint g_bus_watch_proxy (GBusType bus_type, - const gchar *name, - GBusNameWatcherFlags flags, - const gchar *object_path, - const gchar *interface_name, - GType interface_type, - GDBusProxyFlags proxy_flags, - GBusProxyAppearedCallback proxy_appeared_handler, - GBusProxyVanishedCallback proxy_vanished_handler, - gpointer user_data, - GDestroyNotify user_data_free_func); -guint g_bus_watch_proxy_on_connection (GDBusConnection *connection, - const gchar *name, - GBusNameWatcherFlags flags, - const gchar *object_path, - const gchar *interface_name, - GType interface_type, - GDBusProxyFlags proxy_flags, - GBusProxyAppearedCallback proxy_appeared_handler, - GBusProxyVanishedCallback proxy_vanished_handler, - gpointer user_data, - GDestroyNotify user_data_free_func); -guint g_bus_watch_proxy_with_closures (GBusType bus_type, - const gchar *name, - GBusNameWatcherFlags flags, - const gchar *object_path, - const gchar *interface_name, - GType interface_type, - GDBusProxyFlags proxy_flags, - GClosure *proxy_appeared_closure, - GClosure *proxy_vanished_closure); -guint g_bus_watch_proxy_on_connection_with_closures ( - GDBusConnection *connection, - const gchar *name, - GBusNameWatcherFlags flags, - const gchar *object_path, - const gchar *interface_name, - GType interface_type, - GDBusProxyFlags proxy_flags, - GClosure *proxy_appeared_closure, - GClosure *proxy_vanished_closure); -void g_bus_unwatch_proxy (guint watcher_id); - -G_END_DECLS - -#endif /* __G_DBUS_PROXY_WATCHING_H__ */ diff --git a/gio/gio.h b/gio/gio.h index 11cb725d5..a80ed8427 100644 --- a/gio/gio.h +++ b/gio/gio.h @@ -52,7 +52,6 @@ #include #include #include -#include #include #include #include diff --git a/gio/gio.symbols b/gio/gio.symbols index 3f61cffce..689f96e71 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -1695,6 +1695,9 @@ g_dbus_proxy_get_type G_GNUC_CONST g_dbus_proxy_new g_dbus_proxy_new_finish g_dbus_proxy_new_sync +g_dbus_proxy_new_for_bus +g_dbus_proxy_new_for_bus_finish +g_dbus_proxy_new_for_bus_sync g_dbus_proxy_get_cached_property g_dbus_proxy_set_cached_property g_dbus_proxy_get_cached_property_names @@ -1704,7 +1707,8 @@ g_dbus_proxy_get_flags g_dbus_proxy_get_interface_info g_dbus_proxy_get_interface_name g_dbus_proxy_get_object_path -g_dbus_proxy_get_unique_bus_name +g_dbus_proxy_get_name +g_dbus_proxy_get_name_owner g_dbus_proxy_set_default_timeout g_dbus_proxy_set_interface_info g_dbus_proxy_call @@ -1713,16 +1717,6 @@ g_dbus_proxy_call_sync #endif #endif -#if IN_HEADER(__G_DBUS_PROXY_WATCHING_H__) -#if IN_FILE(__G_DBUS_PROXY_WATCHING_C__) -g_bus_watch_proxy -g_bus_watch_proxy_on_connection -g_bus_unwatch_proxy -g_bus_watch_proxy_with_closures -g_bus_watch_proxy_on_connection_with_closures -#endif -#endif - #if IN_HEADER(__G_DBUS_SERVER_H__) #if IN_FILE(__G_DBUS_SERVER_C__) g_dbus_server_get_type G_GNUC_CONST diff --git a/gio/gioenums.h b/gio/gioenums.h index efd08d53f..9d1e126f7 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -741,6 +741,7 @@ typedef enum { /** * GBusType: * @G_BUS_TYPE_STARTER: An alias for the message bus that activated the process, if any. + * @G_BUS_TYPE_NONE: Not a message bus. * @G_BUS_TYPE_SYSTEM: The system-wide message bus. * @G_BUS_TYPE_SESSION: The login session message bus. * @@ -750,7 +751,8 @@ typedef enum { */ typedef enum { - G_BUS_TYPE_STARTER = 0, + G_BUS_TYPE_STARTER = -1, + G_BUS_TYPE_NONE = 0, G_BUS_TYPE_SYSTEM = 1, G_BUS_TYPE_SESSION = 2 } GBusType; @@ -795,6 +797,9 @@ typedef enum * @G_DBUS_PROXY_FLAGS_NONE: No flags set. * @G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES: Don't load properties. * @G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS: Don't connect to signals on the remote object. + * @G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START: If not set and the proxy if for a well-known name, + * then request the bus to launch an owner for the name if no-one owns the name. This flag can + * only be used in proxies for well-known names. * * Flags used when constructing an instance of a #GDBusProxy derived class. * @@ -802,9 +807,10 @@ typedef enum */ typedef enum { - G_DBUS_PROXY_FLAGS_NONE = 0, /*< nick=none >*/ - G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = (1<<0), /*< nick=do-not-load-properties >*/ - G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1) /*< nick=do-not-connect-signals >*/ + G_DBUS_PROXY_FLAGS_NONE = 0, + G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES = (1<<0), + G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS = (1<<1), + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START = (1<<2) } GDBusProxyFlags; /** diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am index 99d2db5f7..0abfa5ab1 100644 --- a/gio/tests/Makefile.am +++ b/gio/tests/Makefile.am @@ -48,6 +48,7 @@ TEST_PROGS += \ gdbus-connection \ gdbus-names \ gdbus-proxy \ + gdbus-proxy-well-known-name \ gdbus-introspection \ gdbus-threading \ gdbus-export \ @@ -208,6 +209,9 @@ gdbus_names_LDADD = $(progs_ldadd) gdbus_proxy_SOURCES = gdbus-proxy.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c gdbus_proxy_LDADD = $(progs_ldadd) +gdbus_proxy_well_known_name_SOURCES = gdbus-proxy-well-known-name.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c +gdbus_proxy_well_known_name_LDADD = $(progs_ldadd) + gdbus_introspection_SOURCES = gdbus-introspection.c gdbus-sessionbus.c gdbus-sessionbus.h gdbus-tests.h gdbus-tests.c gdbus_introspection_LDADD = $(progs_ldadd) diff --git a/gio/tests/gdbus-connection.c b/gio/tests/gdbus-connection.c index 2a481260a..15fca70e4 100644 --- a/gio/tests/gdbus-connection.c +++ b/gio/tests/gdbus-connection.c @@ -360,9 +360,11 @@ test_connection_signals (void) GDBusConnection *c2; GDBusConnection *c3; guint s1; + guint s1b; guint s2; guint s3; gint count_s1; + gint count_s1b; gint count_s2; gint count_name_owner_changed; GError *error; @@ -425,11 +427,26 @@ test_connection_signals (void) test_connection_signal_handler, &count_name_owner_changed, NULL); + /* Note that s1b is *just like* s1 - this is to catch a bug where N + * subscriptions of the same rule causes N calls to each of the N + * subscriptions instead of just 1 call to each of the N subscriptions. + */ + s1b = g_dbus_connection_signal_subscribe (c1, + ":1.2", + "org.gtk.GDBus.ExampleInterface", + "Foo", + "/org/gtk/GDBus/ExampleInterface", + NULL, + test_connection_signal_handler, + &count_s1b, + NULL); g_assert (s1 != 0); + g_assert (s1b != 0); g_assert (s2 != 0); g_assert (s3 != 0); count_s1 = 0; + count_s1b = 0; count_s2 = 0; count_name_owner_changed = 0; @@ -480,7 +497,7 @@ test_connection_signals (void) &error); g_assert_no_error (error); g_assert (ret); - while (!(count_s1 == 1 && count_s2 == 1)) + while (!(count_s1 >= 1 && count_s2 >= 1)) g_main_loop_run (loop); g_assert_cmpint (count_s1, ==, 1); g_assert_cmpint (count_s2, ==, 1); @@ -510,7 +527,7 @@ test_connection_signals (void) guint quit_mainloop_id; quit_mainloop_fired = FALSE; quit_mainloop_id = g_timeout_add (5000, test_connection_signal_quit_mainloop, &quit_mainloop_fired); - while (count_name_owner_changed != 2 && !quit_mainloop_fired) + while (count_name_owner_changed < 2 && !quit_mainloop_fired) g_main_loop_run (loop); g_source_remove (quit_mainloop_id); g_assert_cmpint (count_s1, ==, 1); @@ -520,6 +537,7 @@ test_connection_signals (void) g_dbus_connection_signal_unsubscribe (c1, s1); g_dbus_connection_signal_unsubscribe (c1, s2); g_dbus_connection_signal_unsubscribe (c1, s3); + g_dbus_connection_signal_unsubscribe (c1, s1b); _g_object_wait_for_single_ref (c1); _g_object_wait_for_single_ref (c2); diff --git a/gio/tests/gdbus-example-proxy-subclass.c b/gio/tests/gdbus-example-proxy-subclass.c index b5ee9f8ea..fb1a52879 100644 --- a/gio/tests/gdbus-example-proxy-subclass.c +++ b/gio/tests/gdbus-example-proxy-subclass.c @@ -349,93 +349,10 @@ accounts_user_frobnicate_finish (AccountsUser *user, 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; } diff --git a/gio/tests/gdbus-example-watch-proxy.c b/gio/tests/gdbus-example-watch-proxy.c index e5b844df2..c66826d86 100644 --- a/gio/tests/gdbus-example-watch-proxy.c +++ b/gio/tests/gdbus-example-watch-proxy.c @@ -4,7 +4,7 @@ static gchar *opt_name = NULL; static gchar *opt_object_path = NULL; static gchar *opt_interface = NULL; static gboolean opt_system_bus = FALSE; -static gboolean opt_auto_start = FALSE; +static gboolean opt_no_auto_start = FALSE; static gboolean opt_no_properties = FALSE; static GOptionEntry opt_entries[] = @@ -13,11 +13,13 @@ static GOptionEntry opt_entries[] = { "object-path", 'o', 0, G_OPTION_ARG_STRING, &opt_object_path, "Object path of the remote object", NULL }, { "interface", 'i', 0, G_OPTION_ARG_STRING, &opt_interface, "D-Bus interface of remote object", NULL }, { "system-bus", 's', 0, G_OPTION_ARG_NONE, &opt_system_bus, "Use the system-bus instead of the session-bus", NULL }, - { "auto-start", 'a', 0, G_OPTION_ARG_NONE, &opt_auto_start, "Instruct the bus to launch an owner for the name", NULL}, + { "no-auto-start", 'a', 0, G_OPTION_ARG_NONE, &opt_no_auto_start, "Don't instruct the bus to launch an owner for the name", NULL}, { "no-properties", 'p', 0, G_OPTION_ARG_NONE, &opt_no_properties, "Do not load properties", NULL}, { NULL} }; +static GMainLoop *loop = NULL; + static void print_properties (GDBusProxy *proxy) { @@ -100,64 +102,62 @@ on_signal (GDBusProxy *proxy, } static void -on_proxy_appeared (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy, - gpointer user_data) +print_proxy (GDBusProxy *proxy) { - g_print ("+++ Acquired proxy object for remote object owned by %s\n" - " bus: %s\n" - " name: %s\n" - " object path: %s\n" - " interface: %s\n", - name_owner, - opt_system_bus ? "System Bus" : "Session Bus", - opt_name, - opt_object_path, - opt_interface); + gchar *name_owner; - print_properties (proxy); - - g_signal_connect (proxy, - "g-properties-changed", - G_CALLBACK (on_properties_changed), - NULL); - - g_signal_connect (proxy, - "g-signal", - G_CALLBACK (on_signal), - NULL); + name_owner = g_dbus_proxy_get_name_owner (proxy); + if (name_owner != NULL) + { + g_print ("+++ Proxy object points to remote object owned by %s\n" + " bus: %s\n" + " name: %s\n" + " object path: %s\n" + " interface: %s\n", + name_owner, + opt_system_bus ? "System Bus" : "Session Bus", + opt_name, + opt_object_path, + opt_interface); + print_properties (proxy); + } + else + { + g_print ("--- Proxy object is inert - there is no name owner for the name\n" + " bus: %s\n" + " name: %s\n" + " object path: %s\n" + " interface: %s\n", + opt_system_bus ? "System Bus" : "Session Bus", + opt_name, + opt_object_path, + opt_interface); + } + g_free (name_owner); } static void -on_proxy_vanished (GDBusConnection *connection, - const gchar *name, - gpointer user_data) +on_name_owner_notify (GObject *object, + GParamSpec *pspec, + gpointer user_data) { - g_print ("--- Cannot create proxy object for\n" - " bus: %s\n" - " name: %s\n" - " object path: %s\n" - " interface: %s\n", - opt_system_bus ? "System Bus" : "Session Bus", - opt_name, - opt_object_path, - opt_interface); + GDBusProxy *proxy = G_DBUS_PROXY (object); + print_proxy (proxy); } int main (int argc, char *argv[]) { - guint watcher_id; - GMainLoop *loop; GOptionContext *opt_context; GError *error; - GBusNameWatcherFlags flags; - GDBusProxyFlags proxy_flags; + GDBusProxyFlags flags; + GDBusProxy *proxy; g_type_init (); + loop = NULL; + proxy = NULL; + opt_context = g_option_context_new ("g_bus_watch_proxy() example"); g_option_context_set_summary (opt_context, "Example: to watch the object of gdbus-example-server, use:\n" @@ -169,7 +169,7 @@ main (int argc, char *argv[]) error = NULL; if (!g_option_context_parse (opt_context, &argc, &argv, &error)) { - g_printerr ("Error parsing options: %s", error->message); + g_printerr ("Error parsing options: %s\n", error->message); goto out; } if (opt_name == NULL || opt_object_path == NULL || opt_interface == NULL) @@ -178,32 +178,51 @@ main (int argc, char *argv[]) goto out; } - flags = G_BUS_NAME_WATCHER_FLAGS_NONE; - if (opt_auto_start) - flags |= G_BUS_NAME_WATCHER_FLAGS_AUTO_START; - - proxy_flags = G_DBUS_PROXY_FLAGS_NONE; + flags = G_DBUS_PROXY_FLAGS_NONE; if (opt_no_properties) - proxy_flags |= G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES; - - watcher_id = g_bus_watch_proxy (opt_system_bus ? G_BUS_TYPE_SYSTEM : G_BUS_TYPE_SESSION, - opt_name, - flags, - opt_object_path, - opt_interface, - G_TYPE_DBUS_PROXY, - proxy_flags, - on_proxy_appeared, - on_proxy_vanished, - NULL, - NULL); + flags |= G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES; + if (opt_no_auto_start) + flags |= G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START; loop = g_main_loop_new (NULL, FALSE); + + error = NULL; + proxy = g_dbus_proxy_new_for_bus_sync (opt_system_bus ? G_BUS_TYPE_SYSTEM : G_BUS_TYPE_SESSION, + flags, + NULL, /* GDBusInterfaceInfo */ + opt_name, + opt_object_path, + opt_interface, + NULL, /* GCancellable */ + &error); + if (proxy == NULL) + { + g_printerr ("Error creating proxy: %s\n", error->message); + g_error_free (error); + goto out; + } + + g_signal_connect (proxy, + "g-properties-changed", + G_CALLBACK (on_properties_changed), + NULL); + g_signal_connect (proxy, + "g-signal", + G_CALLBACK (on_signal), + NULL); + g_signal_connect (proxy, + "notify::g-name-owner", + G_CALLBACK (on_name_owner_notify), + NULL); + print_proxy (proxy); + g_main_loop_run (loop); - g_bus_unwatch_proxy (watcher_id); - out: + if (proxy != NULL) + g_object_unref (proxy); + if (loop != NULL) + g_main_loop_unref (loop); g_option_context_free (opt_context); g_free (opt_name); g_free (opt_object_path); diff --git a/gio/tests/gdbus-export.c b/gio/tests/gdbus-export.c index 297fb231c..d1f3eed25 100644 --- a/gio/tests/gdbus-export.c +++ b/gio/tests/gdbus-export.c @@ -343,7 +343,6 @@ get_nodes_at (GDBusConnection *c, error = NULL; proxy = g_dbus_proxy_new_sync (c, - G_TYPE_DBUS_PROXY, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, NULL, @@ -400,7 +399,6 @@ has_interface (GDBusConnection *c, error = NULL; proxy = g_dbus_proxy_new_sync (c, - G_TYPE_DBUS_PROXY, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, NULL, @@ -450,7 +448,6 @@ count_interfaces (GDBusConnection *c, error = NULL; proxy = g_dbus_proxy_new_sync (c, - G_TYPE_DBUS_PROXY, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, NULL, @@ -522,7 +519,6 @@ dyna_create (GDBusConnection *c, error = NULL; proxy = g_dbus_proxy_new_sync (c, - G_TYPE_DBUS_PROXY, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, NULL, @@ -745,7 +741,6 @@ test_dispatch_thread_func (gpointer user_data) const gchar *value_str; foo_proxy = g_dbus_proxy_new_sync (c, - G_TYPE_DBUS_PROXY, G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, NULL, diff --git a/gio/tests/gdbus-introspection.c b/gio/tests/gdbus-introspection.c index 6209c5d4d..1ec3844c8 100644 --- a/gio/tests/gdbus-introspection.c +++ b/gio/tests/gdbus-introspection.c @@ -34,11 +34,7 @@ static GMainLoop *loop = NULL; /* ---------------------------------------------------------------------------------------------------- */ static void -introspection_on_proxy_appeared (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy, - gpointer user_data) +test_introspection (GDBusProxy *proxy) { GError *error; const gchar *xml_data; @@ -104,48 +100,47 @@ introspection_on_proxy_appeared (GDBusConnection *connection, g_main_loop_quit (loop); } -static void -introspection_on_proxy_vanished (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ -} - static void test_introspection_parser (void) { - guint watcher_id; + GDBusProxy *proxy; + GDBusConnection *connection; + GError *error; session_bus_up (); - watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION, - "com.example.TestService", - G_BUS_NAME_WATCHER_FLAGS_NONE, - "/com/example/TestObject", - "com.example.Frob", - G_TYPE_DBUS_PROXY, - G_DBUS_PROXY_FLAGS_NONE, - introspection_on_proxy_appeared, - introspection_on_proxy_vanished, - NULL, - NULL); - /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return * until one can connect to the bus but that's not how things work right now */ usleep (500 * 1000); + + error = NULL; + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, + NULL, + &error); + g_assert_no_error (error); + error = NULL; + proxy = g_dbus_proxy_new_sync (connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, /* GDBusInterfaceInfo */ + "com.example.TestService", /* name */ + "/com/example/TestObject", /* object path */ + "com.example.Frob", /* interface */ + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + /* this is safe; testserver will exit once the bus goes away */ g_assert (g_spawn_command_line_async (SRCDIR "/gdbus-testserver.py", NULL)); - g_main_loop_run (loop); + _g_assert_property_notify (proxy, "g-name-owner"); - g_bus_unwatch_proxy (watcher_id); + test_introspection (proxy); - /* tear down bus */ - session_bus_down (); + g_object_unref (proxy); + g_object_unref (connection); } - /* ---------------------------------------------------------------------------------------------------- */ int diff --git a/gio/tests/gdbus-peer.c b/gio/tests/gdbus-peer.c index 97f030840..7d8137244 100644 --- a/gio/tests/gdbus-peer.c +++ b/gio/tests/gdbus-peer.c @@ -526,7 +526,6 @@ test_peer (void) */ error = NULL; proxy = g_dbus_proxy_new_sync (c, - G_TYPE_DBUS_PROXY, G_DBUS_PROXY_FLAGS_NONE, NULL, NULL, /* bus_name */ diff --git a/gio/tests/gdbus-proxy-well-known-name.c b/gio/tests/gdbus-proxy-well-known-name.c new file mode 100644 index 000000000..a599ef6c5 --- /dev/null +++ b/gio/tests/gdbus-proxy-well-known-name.c @@ -0,0 +1,283 @@ +/* GLib testing framework examples and tests + * + * Copyright (C) 2008-2010 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: David Zeuthen + */ + +#include +#include +#include + +#include "gdbus-tests.h" + +/* all tests rely on a shared mainloop */ +static GMainLoop *loop = NULL; + +/* ---------------------------------------------------------------------------------------------------- */ + +static void +proxy_new_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GDBusProxy **ret = user_data; + GError *error; + + error = NULL; + *ret = g_dbus_proxy_new_finish (res, &error); + g_assert_no_error (error); + g_assert (ret != NULL); + + g_main_loop_quit (loop); +} + +static void +test_proxy_well_known_name (void) +{ + GDBusProxy *p; + GDBusProxy *p2; + GDBusProxy *ap; + GDBusProxy *ap2; + GDBusConnection *c; + GError *error; + gchar *name_owner; + gchar **property_names; + GVariant *variant; + GVariant *result; + + session_bus_up (); + + /* TODO: wait a bit for the bus to come up.. ideally session_bus_up() won't return + * until one can connect to the bus but that's not how things work right now + */ + usleep (500 * 1000); + + error = NULL; + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c != NULL); + + error = NULL; + p = g_dbus_proxy_new_sync (c, + G_DBUS_PROXY_FLAGS_NONE, + NULL, /* GDBusInterfaceInfo* */ + "com.example.TestService", /* name */ + "/com/example/TestObject", /* object path */ + "com.example.Frob", /* interface name */ + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + + /* we shouldn't have a name owner nor any cached properties */ + g_assert_cmpstr (g_dbus_proxy_get_name_owner (p), ==, NULL); + g_assert (g_dbus_proxy_get_cached_property_names (p) == NULL); + + /* also for async: we shouldn't have a name owner nor any cached properties */ + g_dbus_proxy_new (c, + G_DBUS_PROXY_FLAGS_NONE, + NULL, /* GDBusInterfaceInfo* */ + "com.example.TestService", /* name */ + "/com/example/TestObject", /* object path */ + "com.example.Frob", /* interface name */ + NULL, /* GCancellable */ + (GAsyncReadyCallback) proxy_new_cb, + &ap); + g_main_loop_run (loop); + g_assert_cmpstr (g_dbus_proxy_get_name_owner (ap), ==, NULL); + g_assert (g_dbus_proxy_get_cached_property_names (ap) == NULL); + + /* this is safe; testserver will exit once the bus goes away */ + g_assert (g_spawn_command_line_async (SRCDIR "/gdbus-testserver.py", NULL)); + + /* check that we get the notify::g-name-owner signal */ + _g_assert_property_notify (p, "g-name-owner"); + + /* Now we should have a name owner as well as properties */ + name_owner = g_dbus_proxy_get_name_owner (p); + property_names = g_dbus_proxy_get_cached_property_names (p); + g_assert (g_dbus_is_unique_name (name_owner)); + g_assert (property_names != NULL && g_strv_length (property_names) > 0); + g_free (name_owner); + g_strfreev (property_names); + + /* if we create another proxy with the service being available, check that + * it has a name owner and properties + */ + error = NULL; + p2 = g_dbus_proxy_new_sync (c, + G_DBUS_PROXY_FLAGS_NONE, + NULL, /* GDBusInterfaceInfo* */ + "com.example.TestService", /* name */ + "/com/example/TestObject", /* object path */ + "com.example.Frob", /* interface name */ + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); + name_owner = g_dbus_proxy_get_name_owner (p2); + property_names = g_dbus_proxy_get_cached_property_names (p2); + g_assert (g_dbus_is_unique_name (name_owner)); + g_assert (property_names != NULL && g_strv_length (property_names) > 0); + g_free (name_owner); + g_strfreev (property_names); + + /* also for async: we should have a name owner and cached properties */ + g_dbus_proxy_new (c, + G_DBUS_PROXY_FLAGS_NONE, + NULL, /* GDBusInterfaceInfo* */ + "com.example.TestService", /* name */ + "/com/example/TestObject", /* object path */ + "com.example.Frob", /* interface name */ + NULL, /* GCancellable */ + (GAsyncReadyCallback) proxy_new_cb, + &ap2); + g_main_loop_run (loop); + name_owner = g_dbus_proxy_get_name_owner (ap2); + property_names = g_dbus_proxy_get_cached_property_names (ap2); + g_assert (g_dbus_is_unique_name (name_owner)); + g_assert (property_names != NULL && g_strv_length (property_names) > 0); + g_free (name_owner); + g_strfreev (property_names); + + /* Check property value is the initial value */ + variant = g_dbus_proxy_get_cached_property (p, "y"); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 1); + g_variant_unref (variant); + variant = g_dbus_proxy_get_cached_property (p2, "y"); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 1); + g_variant_unref (variant); + variant = g_dbus_proxy_get_cached_property (ap, "y"); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 1); + g_variant_unref (variant); + variant = g_dbus_proxy_get_cached_property (ap2, "y"); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 1); + g_variant_unref (variant); + + /* Check that properties are updated on both p and p2 */ + result = g_dbus_proxy_call_sync (p, + "FrobSetProperty", + g_variant_new ("(sv)", + "y", + g_variant_new_byte (42)), + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + _g_assert_signal_received (p, "g-properties-changed"); + variant = g_dbus_proxy_get_cached_property (p, "y"); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 42); + g_variant_unref (variant); + variant = g_dbus_proxy_get_cached_property (p2, "y"); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 42); + g_variant_unref (variant); + variant = g_dbus_proxy_get_cached_property (ap, "y"); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 42); + g_variant_unref (variant); + variant = g_dbus_proxy_get_cached_property (ap2, "y"); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 42); + g_variant_unref (variant); + + /* Nuke the service and check that we get the signal and then don't + * have a name owner nor any cached properties + */ + result = g_dbus_proxy_call_sync (p, + "Quit", + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + g_assert_no_error (error); + g_assert (result != NULL); + g_assert_cmpstr (g_variant_get_type_string (result), ==, "()"); + g_variant_unref (result); + /* and wait... */ + _g_assert_property_notify (p, "g-name-owner"); + /* now we shouldn't have a name owner nor any cached properties */ + g_assert_cmpstr (g_dbus_proxy_get_name_owner (p), ==, NULL); + g_assert (g_dbus_proxy_get_cached_property_names (p) == NULL); + g_assert (g_dbus_proxy_get_cached_property (p, "y") == NULL); + + /* now bring back the server and wait for the proxy to be updated.. now + * the 'y' property should be back at 1... + */ + /* this is safe; testserver will exit once the bus goes away */ + g_assert (g_spawn_command_line_async (SRCDIR "/gdbus-testserver.py", NULL)); + /* check that we get the notify::g-name-owner signal */ + _g_assert_property_notify (p, "g-name-owner"); + /* Now we should have a name owner as well as properties */ + name_owner = g_dbus_proxy_get_name_owner (p); + property_names = g_dbus_proxy_get_cached_property_names (p); + g_assert (g_dbus_is_unique_name (name_owner)); + g_assert (property_names != NULL && g_strv_length (property_names) > 0); + g_free (name_owner); + g_strfreev (property_names); + /* and finally check the 'y' property */ + variant = g_dbus_proxy_get_cached_property (p, "y"); + g_assert (variant != NULL); + g_assert_cmpint (g_variant_get_byte (variant), ==, 1); + g_variant_unref (variant); + + g_object_unref (p2); + g_object_unref (p); + g_object_unref (ap2); + g_object_unref (ap); + + g_object_unref (c); + + /* tear down bus */ + session_bus_down (); +} + +/* ---------------------------------------------------------------------------------------------------- */ + +int +main (int argc, + char *argv[]) +{ + gint ret; + + g_type_init (); + g_test_init (&argc, &argv, NULL); + + /* all the tests rely on a shared main loop */ + loop = g_main_loop_new (NULL, FALSE); + + /* all the tests use a session bus with a well-known address that we can bring up and down + * using session_bus_up() and session_bus_down(). + */ + g_unsetenv ("DISPLAY"); + g_setenv ("DBUS_SESSION_BUS_ADDRESS", session_bus_get_temporary_address (), TRUE); + + g_test_add_func ("/gdbus/proxy-well-known-name", test_proxy_well_known_name); + + ret = g_test_run(); + return ret; +} diff --git a/gio/tests/gdbus-proxy.c b/gio/tests/gdbus-proxy.c index 970bf470e..782bf778f 100644 --- a/gio/tests/gdbus-proxy.c +++ b/gio/tests/gdbus-proxy.c @@ -34,10 +34,7 @@ static GMainLoop *loop = NULL; /* ---------------------------------------------------------------------------------------------------- */ static void -test_methods (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy) +test_methods (GDBusProxy *proxy) { GVariant *result; GError *error; @@ -133,10 +130,7 @@ test_methods (GDBusConnection *connection, /* ---------------------------------------------------------------------------------------------------- */ static void -test_properties (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy) +test_properties (GDBusProxy *proxy) { GError *error; GVariant *variant; @@ -281,10 +275,7 @@ test_proxy_signals_on_emit_signal_cb (GDBusProxy *proxy, } static void -test_signals (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy) +test_signals (GDBusProxy *proxy) { GError *error; GString *s; @@ -359,10 +350,7 @@ test_signals (GDBusConnection *connection, } static void -test_bogus_method_return (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy) +test_bogus_method_return (GDBusProxy *proxy) { GError *error = NULL; GVariant *result; @@ -401,16 +389,8 @@ static const gchar *frob_dbus_interface_xml = static GDBusInterfaceInfo *frob_dbus_interface_info; static void -on_proxy_appeared (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy, - gpointer user_data) +test_expected_interface (GDBusProxy *proxy) { - test_methods (connection, name, name_owner, proxy); - test_properties (connection, name, name_owner, proxy); - test_signals (connection, name, name_owner, proxy); - /* This is obviously wrong but expected interface is not set so we don't fail... */ g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_string ("error_me_out!")); g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42)); @@ -419,12 +399,12 @@ on_proxy_appeared (GDBusConnection *connection, /* Now repeat the method tests, with an expected interface set */ g_dbus_proxy_set_interface_info (proxy, frob_dbus_interface_info); - test_methods (connection, name, name_owner, proxy); + test_methods (proxy); /* And now one more test where we deliberately set the expected * interface definition incorrectly */ - test_bogus_method_return (connection, name, name_owner, proxy); + test_bogus_method_return (proxy); /* Also check that we complain if setting a cached property of the wrong type */ if (g_test_trap_fork (0, G_TEST_TRAP_SILENCE_STDOUT | G_TEST_TRAP_SILENCE_STDERR)) @@ -443,21 +423,14 @@ on_proxy_appeared (GDBusConnection *connection, /* this should work, however (since the type is correct) */ g_dbus_proxy_set_cached_property (proxy, "y", g_variant_new_byte (42)); - - g_main_loop_quit (loop); -} - -static void -on_proxy_vanished (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ } static void test_proxy (void) { - guint watcher_id; + GDBusProxy *proxy; + GDBusConnection *connection; + GError *error; session_bus_up (); @@ -466,27 +439,34 @@ test_proxy (void) */ usleep (500 * 1000); - watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION, - "com.example.TestService", - G_BUS_NAME_WATCHER_FLAGS_NONE, - "/com/example/TestObject", - "com.example.Frob", - G_TYPE_DBUS_PROXY, - G_DBUS_PROXY_FLAGS_NONE, - on_proxy_appeared, - on_proxy_vanished, - NULL, - NULL); + error = NULL; + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, + NULL, + &error); + g_assert_no_error (error); + error = NULL; + proxy = g_dbus_proxy_new_sync (connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, /* GDBusInterfaceInfo */ + "com.example.TestService", /* name */ + "/com/example/TestObject", /* object path */ + "com.example.Frob", /* interface */ + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); /* this is safe; testserver will exit once the bus goes away */ g_assert (g_spawn_command_line_async (SRCDIR "/gdbus-testserver.py", NULL)); - g_main_loop_run (loop); + _g_assert_property_notify (proxy, "g-name-owner"); - g_bus_unwatch_proxy (watcher_id); + test_methods (proxy); + test_properties (proxy); + test_signals (proxy); + test_expected_interface (proxy); - /* tear down bus */ - session_bus_down (); + g_object_unref (proxy); + g_object_unref (connection); } /* ---------------------------------------------------------------------------------------------------- */ diff --git a/gio/tests/gdbus-threading.c b/gio/tests/gdbus-threading.c index 24e070423..05c7c86d3 100644 --- a/gio/tests/gdbus-threading.c +++ b/gio/tests/gdbus-threading.c @@ -348,11 +348,7 @@ test_sleep_in_thread_func (gpointer _data) } static void -on_proxy_appeared (GDBusConnection *connection, - const gchar *name, - const gchar *name_owner, - GDBusProxy *proxy, - gpointer user_data) +test_method_calls_on_proxy (GDBusProxy *proxy) { guint n; @@ -453,33 +449,38 @@ on_proxy_appeared (GDBusConnection *connection, g_main_loop_quit (loop); } -static void -on_proxy_vanished (GDBusConnection *connection, - const gchar *name, - gpointer user_data) -{ -} - static void test_method_calls_in_thread (void) { - guint watcher_id; + GDBusProxy *proxy; + GDBusConnection *connection; + GError *error; + gchar *name_owner; - watcher_id = g_bus_watch_proxy (G_BUS_TYPE_SESSION, - "com.example.TestService", - G_BUS_NAME_WATCHER_FLAGS_NONE, - "/com/example/TestObject", - "com.example.Frob", - G_TYPE_DBUS_PROXY, - G_DBUS_PROXY_FLAGS_NONE, - on_proxy_appeared, - on_proxy_vanished, - NULL, - NULL); + error = NULL; + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, + NULL, + &error); + g_assert_no_error (error); + error = NULL; + proxy = g_dbus_proxy_new_sync (connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, /* GDBusInterfaceInfo */ + "com.example.TestService", /* name */ + "/com/example/TestObject", /* object path */ + "com.example.Frob", /* interface */ + NULL, /* GCancellable */ + &error); + g_assert_no_error (error); - g_main_loop_run (loop); + name_owner = g_dbus_proxy_get_name_owner (proxy); + g_assert_cmpstr (name_owner, !=, NULL); + g_free (name_owner); - g_bus_unwatch_proxy (watcher_id); + test_method_calls_on_proxy (proxy); + + g_object_unref (proxy); + g_object_unref (connection); } /* ---------------------------------------------------------------------------------------------------- */