* 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.
+ * changes emitted from the current name owner are considered and
+ * calls are always sent to the current name owner. This avoids a
+ * number of race conditions when the name is lost by one owner and
+ * claimed by another. However, if no name owner currently exists,
+ * then calls will be sent to the well-known name which may result in
+ * the message bus launching an owner (unless
+ * %G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START is set).
*
* The generic #GDBusProxy::g-properties-changed and #GDBusProxy::g-signal
* signals are not very convenient to work with. Therefore, the recommended
g_dbus_connection_signal_unsubscribe (proxy->priv->connection,
proxy->priv->signals_subscriber_id);
- g_object_unref (proxy->priv->connection);
+ if (proxy->priv->connection != NULL)
+ g_object_unref (proxy->priv->connection);
g_free (proxy->priv->name);
g_free (proxy->priv->name_owner);
g_free (proxy->priv->object_path);
g_hash_table_unref (proxy->priv->properties);
if (proxy->priv->expected_interface != NULL)
- g_dbus_interface_info_unref (proxy->priv->expected_interface);
+ {
+ g_dbus_interface_info_cache_release (proxy->priv->expected_interface);
+ g_dbus_interface_info_unref (proxy->priv->expected_interface);
+ }
G_OBJECT_CLASS (g_dbus_proxy_parent_class)->finalize (object);
}
/* ---------------------------------------------------------------------------------------------------- */
+static gint
+property_name_sort_func (const gchar **a,
+ const gchar **b)
+{
+ return g_strcmp0 (*a, *b);
+}
+
/**
* g_dbus_proxy_get_cached_property_names:
* @proxy: A #GDBusProxy.
g_hash_table_iter_init (&iter, proxy->priv->properties);
while (g_hash_table_iter_next (&iter, (gpointer) &key, NULL))
g_ptr_array_add (p, g_strdup (key));
- g_ptr_array_sort (p, (GCompareFunc) g_strcmp0);
+ g_ptr_array_sort (p, (GCompareFunc) property_name_sort_func);
g_ptr_array_add (p, NULL);
names = (gchar **) g_ptr_array_free (p, FALSE);
value = g_hash_table_lookup (proxy->priv->properties, property_name);
if (value == NULL)
{
- const GDBusPropertyInfo *info;
- info = lookup_property_info_or_warn (proxy, property_name);
+ lookup_property_info_or_warn (proxy, property_name);
/* no difference */
goto out;
}
* g_dbus_proxy_set_cached_property:
* @proxy: A #GDBusProxy
* @property_name: Property name.
- * @value: Value for the property or %NULL to remove it from the cache.
+ * @value: (allow-none): Value for the property or %NULL to remove it from the cache.
*
* If @value is not %NULL, sets the cached value for the property with
* name @property_name to the value in @value.
if (!proxy->priv->initialized)
goto out;
- if (g_strcmp0 (sender_name, proxy->priv->name_owner) != 0)
+ if (proxy->priv->name_owner != NULL && g_strcmp0 (sender_name, proxy->priv->name_owner) != 0)
goto out;
+ if (proxy->priv->expected_interface != NULL)
+ {
+ const GDBusSignalInfo *info;
+ GVariantType *expected_type;
+ info = g_dbus_interface_info_lookup_signal (proxy->priv->expected_interface, signal_name);
+ if (info == NULL)
+ goto out;
+ expected_type = _g_dbus_compute_complete_signature (info->args);
+ if (!g_variant_type_equal (expected_type, g_variant_get_type (parameters)))
+ {
+ g_variant_type_free (expected_type);
+ goto out;
+ }
+ g_variant_type_free (expected_type);
+ }
+
g_signal_emit (proxy,
signals[SIGNAL_SIGNAL],
0,
/* ---------------------------------------------------------------------------------------------------- */
static void
+insert_property_checked (GDBusProxy *proxy,
+ gchar *property_name,
+ GVariant *value)
+{
+ if (proxy->priv->expected_interface != NULL)
+ {
+ const GDBusPropertyInfo *info;
+
+ info = g_dbus_interface_info_lookup_property (proxy->priv->expected_interface, property_name);
+ /* Ignore unknown properties */
+ if (info == NULL)
+ goto invalid;
+
+ /* Ignore properties with the wrong type */
+ if (g_strcmp0 (info->signature, g_variant_get_type_string (value)) != 0)
+ goto invalid;
+ }
+
+ g_hash_table_insert (proxy->priv->properties,
+ property_name, /* adopts string */
+ value); /* adopts value */
+
+ return;
+
+ invalid:
+ g_variant_unref (value);
+ g_free (property_name);
+}
+
+static void
on_properties_changed (GDBusConnection *connection,
const gchar *sender_name,
const gchar *object_path,
gpointer user_data)
{
GDBusProxy *proxy = G_DBUS_PROXY (user_data);
- GError *error;
const gchar *interface_name_for_signal;
GVariant *changed_properties;
gchar **invalidated_properties;
GVariant *value;
guint n;
- error = NULL;
changed_properties = NULL;
invalidated_properties = NULL;
if (!proxy->priv->initialized)
goto out;
- if (g_strcmp0 (sender_name, proxy->priv->name_owner) != 0)
+ if (proxy->priv->name_owner != NULL && 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_variant_iter_init (&iter, changed_properties);
while (g_variant_iter_next (&iter, "{sv}", &key, &value))
{
- g_hash_table_insert (proxy->priv->properties,
- key, /* adopts string */
- value); /* adopts value */
+ insert_property_checked (proxy,
+ key, /* adopts string */
+ value); /* adopts value */
}
for (n = 0; invalidated_properties[n] != NULL; n++)
g_variant_get (result, "(a{sv})", &iter);
while (g_variant_iter_next (iter, "{sv}", &key, &value))
{
- g_hash_table_insert (proxy->priv->properties,
- key, /* adopts string */
- value); /* adopts value */
+ insert_property_checked (proxy,
+ key, /* adopts string */
+ value); /* adopts value */
}
g_variant_iter_free (iter);
}
else
{
- g_simple_async_result_set_from_error (data->simple, error);
- g_error_free (error);
+ g_simple_async_result_take_error (data->simple, error);
g_simple_async_result_complete_in_idle (data->simple);
async_init_data_free (data);
goto out;
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_take_error (data->simple, error);
g_simple_async_result_complete_in_idle (data->simple);
async_init_data_free (data);
}
"PropertiesChanged",
proxy->priv->object_path,
proxy->priv->interface_name,
+ G_DBUS_SIGNAL_FLAGS_NONE,
on_properties_changed,
proxy,
NULL);
NULL, /* member */
proxy->priv->object_path,
NULL, /* arg0 */
+ G_DBUS_SIGNAL_FLAGS_NONE,
on_signal_received,
proxy,
NULL);
"NameOwnerChanged", /* signal name */
"/org/freedesktop/DBus", /* path */
proxy->priv->name, /* arg0 */
+ G_DBUS_SIGNAL_FLAGS_NONE,
on_name_owner_changed,
proxy,
NULL);
data->callback,
data->user_data,
NULL);
- g_simple_async_result_set_from_error (simple, error);
- g_error_free (error);
+ g_simple_async_result_take_error (simple, error);
g_simple_async_result_complete_in_idle (simple);
g_object_unref (simple);
}
* g_dbus_proxy_new:
* @connection: A #GDBusConnection.
* @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) or %NULL if @connection is not a message bus connection.
+ * @info: (allow-none): A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL.
+ * @name: (allow-none): 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.
* 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
+ * %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
+ * 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
+ * %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.
*
* g_dbus_proxy_new_sync:
* @connection: A #GDBusConnection.
* @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) or %NULL if @connection is not a message bus connection.
+ * @info: (allow-none): A #GDBusInterfaceInfo specifying the minimal interface that @proxy conforms to or %NULL.
+ * @name: (allow-none): 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.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: (allow-none): Return location for error or %NULL.
*
* 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.
+ * %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
+ * 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
+ * %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.
*
* 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.
+ * @info: (allow-none): 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.
* 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.
+ * @info: (allow-none): 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.
*
* Gets the connection @proxy is for.
*
- * Returns: A #GDBusConnection owned by @proxy. Do not free.
+ * Returns: (transfer none): A #GDBusConnection owned by @proxy. Do not free.
*
* Since: 2.26
*/
/**
* g_dbus_proxy_set_interface_info:
* @proxy: A #GDBusProxy
- * @info: Minimum interface this proxy conforms to or %NULL to unset.
+ * @info: (allow-none): Minimum interface this proxy conforms to or %NULL to unset.
*
* Ensure that interactions with @proxy conform to the given
* interface. For example, when completing a method call, if the type
{
g_return_if_fail (G_IS_DBUS_PROXY (proxy));
if (proxy->priv->expected_interface != NULL)
- g_dbus_interface_info_unref (proxy->priv->expected_interface);
+ {
+ g_dbus_interface_info_cache_release (proxy->priv->expected_interface);
+ g_dbus_interface_info_unref (proxy->priv->expected_interface);
+ }
proxy->priv->expected_interface = info != NULL ? g_dbus_interface_info_ref (info) : NULL;
+ if (proxy->priv->expected_interface != NULL)
+ g_dbus_interface_info_cache_build (proxy->priv->expected_interface);
}
/* ---------------------------------------------------------------------------------------------------- */
&error);
if (error != NULL)
{
- g_simple_async_result_set_from_error (simple,
- error);
- g_error_free (error);
+ g_simple_async_result_take_error (simple, error);
}
else
{
/* no need to complete in idle since the method GDBusConnection already does */
g_simple_async_result_complete (simple);
+ g_object_unref (simple);
}
static const GDBusMethodInfo *
return info;
}
+static const gchar *
+get_destination_for_call (GDBusProxy *proxy)
+{
+ const gchar *ret;
+
+ ret = NULL;
+
+ /* If proxy->priv->name is a unique name, then proxy->priv->name_owner
+ * is never NULL and always the same as proxy->priv->name. We use this
+ * knowledge to avoid checking if proxy->priv->name is a unique or
+ * well-known name.
+ */
+ ret = proxy->priv->name_owner;
+ if (ret != NULL)
+ goto out;
+
+ if (proxy->priv->flags & G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START)
+ goto out;
+
+ ret = proxy->priv->name;
+
+ out:
+ return ret;
+}
+
/**
* g_dbus_proxy_call:
* @proxy: A #GDBusProxy.
* @method_name: Name of method to invoke.
- * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters.
+ * @parameters: (allow-none): A #GVariant tuple with parameters for the signal or %NULL if not passing parameters.
* @flags: Flags from the #GDBusCallFlags enumeration.
- * @timeout_msec: The timeout in milliseconds or -1 to use the proxy default timeout.
+ * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning
+ * "infinite") or -1 to use the proxy default timeout.
* @cancellable: A #GCancellable or %NULL.
* @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't
* care about the result of the method invocation.
gboolean was_split;
gchar *split_interface_name;
const gchar *split_method_name;
- const GDBusMethodInfo *expected_method_info;
const gchar *target_method_name;
const gchar *target_interface_name;
+ const gchar *destination;
GVariantType *reply_type;
g_return_if_fail (G_IS_DBUS_PROXY (proxy));
g_return_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE));
g_return_if_fail (timeout_msec == -1 || timeout_msec >= 0);
+ reply_type = NULL;
+ split_interface_name = NULL;
+
simple = g_simple_async_result_new (G_OBJECT (proxy),
callback,
user_data,
target_method_name = was_split ? split_method_name : method_name;
target_interface_name = was_split ? split_interface_name : proxy->priv->interface_name;
- g_object_set_data_full (G_OBJECT (simple), "-gdbus-proxy-method-name", g_strdup (target_method_name), g_free);
-
/* Warn if method is unexpected (cf. :g-interface-info) */
- expected_method_info = NULL;
if (!was_split)
- expected_method_info = lookup_method_info_or_warn (proxy, target_method_name);
+ {
+ const GDBusMethodInfo *expected_method_info;
+ expected_method_info = lookup_method_info_or_warn (proxy, target_method_name);
+ if (expected_method_info != NULL)
+ reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
+ }
- if (expected_method_info)
- reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
- else
- reply_type = NULL;
+ destination = NULL;
+ if (proxy->priv->name != NULL)
+ {
+ destination = get_destination_for_call (proxy);
+ if (destination == NULL)
+ {
+ g_simple_async_result_set_error (simple,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Cannot invoke method; proxy is for a well-known name without an owner and proxy was constructed with the G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag"));
+ goto out;
+ }
+ }
g_dbus_connection_call (proxy->priv->connection,
- proxy->priv->name_owner,
+ destination,
proxy->priv->object_path,
target_interface_name,
target_method_name,
(GAsyncReadyCallback) reply_cb,
simple);
+ out:
if (reply_type != NULL)
g_variant_type_free (reply_type);
{
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
GVariant *value;
- const char *method_name;
g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
if (g_simple_async_result_propagate_error (simple, error))
goto out;
- value = g_simple_async_result_get_op_res_gpointer (simple);
- method_name = g_object_get_data (G_OBJECT (simple), "-gdbus-proxy-method-name");
+ value = g_variant_ref (g_simple_async_result_get_op_res_gpointer (simple));
out:
return value;
* g_dbus_proxy_call_sync:
* @proxy: A #GDBusProxy.
* @method_name: Name of method to invoke.
- * @parameters: A #GVariant tuple with parameters for the signal or %NULL if not passing parameters.
+ * @parameters: (allow-none): A #GVariant tuple with parameters for the signal
+ * or %NULL if not passing parameters.
* @flags: Flags from the #GDBusCallFlags enumeration.
- * @timeout_msec: The timeout in milliseconds or -1 to use the proxy default timeout.
+ * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning
+ * "infinite") or -1 to use the proxy default timeout.
* @cancellable: A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
gboolean was_split;
gchar *split_interface_name;
const gchar *split_method_name;
- const GDBusMethodInfo *expected_method_info;
const gchar *target_method_name;
const gchar *target_interface_name;
+ const gchar *destination;
GVariantType *reply_type;
g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
g_return_val_if_fail (timeout_msec == -1 || timeout_msec >= 0, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+ reply_type = NULL;
+
was_split = maybe_split_method_name (method_name, &split_interface_name, &split_method_name);
target_method_name = was_split ? split_method_name : method_name;
target_interface_name = was_split ? split_interface_name : proxy->priv->interface_name;
- if (proxy->priv->expected_interface)
+ /* Warn if method is unexpected (cf. :g-interface-info) */
+ if (!was_split)
{
- expected_method_info = g_dbus_interface_info_lookup_method (proxy->priv->expected_interface, target_method_name);
- if (expected_method_info == NULL)
- {
- g_warning ("Trying to invoke method `%s' which isn't in expected interface `%s'",
- target_method_name,
- target_interface_name);
- }
+ const GDBusMethodInfo *expected_method_info;
+ expected_method_info = lookup_method_info_or_warn (proxy, target_method_name);
+ if (expected_method_info != NULL)
+ reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
}
- else
+
+ destination = NULL;
+ if (proxy->priv->name != NULL)
{
- expected_method_info = NULL;
+ destination = get_destination_for_call (proxy);
+ if (destination == NULL)
+ {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Cannot invoke method; proxy is for a well-known name without an owner and proxy was constructed with the G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START flag"));
+ ret = NULL;
+ goto out;
+ }
}
- if (expected_method_info)
- reply_type = _g_dbus_compute_complete_signature (expected_method_info->out_args);
- else
- reply_type = NULL;
-
ret = g_dbus_connection_call_sync (proxy->priv->connection,
- proxy->priv->name_owner,
+ destination,
proxy->priv->object_path,
target_interface_name,
target_method_name,
cancellable,
error);
+ out:
if (reply_type != NULL)
g_variant_type_free (reply_type);