mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-09 19:06:15 +01:00
g_dbus_connection_signal_subscribe: add path and namespace matching
https://bugzilla.gnome.org/show_bug.cgi?id=695156
This commit is contained in:
parent
690d6b97f8
commit
00f6d78125
@ -3246,6 +3246,7 @@ typedef struct
|
||||
gchar *member;
|
||||
gchar *object_path;
|
||||
gchar *arg0;
|
||||
GDBusSignalFlags flags;
|
||||
GArray *subscribers;
|
||||
} SignalData;
|
||||
|
||||
@ -3273,17 +3274,17 @@ signal_data_free (SignalData *signal_data)
|
||||
}
|
||||
|
||||
static gchar *
|
||||
args_to_rule (const gchar *sender,
|
||||
const gchar *interface_name,
|
||||
const gchar *member,
|
||||
const gchar *object_path,
|
||||
const gchar *arg0,
|
||||
gboolean negate)
|
||||
args_to_rule (const gchar *sender,
|
||||
const gchar *interface_name,
|
||||
const gchar *member,
|
||||
const gchar *object_path,
|
||||
const gchar *arg0,
|
||||
GDBusSignalFlags flags)
|
||||
{
|
||||
GString *rule;
|
||||
|
||||
rule = g_string_new ("type='signal'");
|
||||
if (negate)
|
||||
if (flags & G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE)
|
||||
g_string_prepend_c (rule, '-');
|
||||
if (sender != NULL)
|
||||
g_string_append_printf (rule, ",sender='%s'", sender);
|
||||
@ -3293,8 +3294,16 @@ args_to_rule (const gchar *sender,
|
||||
g_string_append_printf (rule, ",member='%s'", member);
|
||||
if (object_path != NULL)
|
||||
g_string_append_printf (rule, ",path='%s'", object_path);
|
||||
|
||||
if (arg0 != NULL)
|
||||
g_string_append_printf (rule, ",arg0='%s'", arg0);
|
||||
{
|
||||
if (flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH)
|
||||
g_string_append_printf (rule, ",arg0path='%s'", arg0);
|
||||
else if (flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE)
|
||||
g_string_append_printf (rule, ",arg0namespace='%s'", arg0);
|
||||
else
|
||||
g_string_append_printf (rule, ",arg0='%s'", arg0);
|
||||
}
|
||||
|
||||
return g_string_free (rule, FALSE);
|
||||
}
|
||||
@ -3417,6 +3426,11 @@ is_signal_data_for_name_lost_or_acquired (SignalData *signal_data)
|
||||
* tracking the name owner of the well-known name and use that when
|
||||
* processing the received signal.
|
||||
*
|
||||
* If one of %G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE or
|
||||
* %G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH are given, @arg0 is
|
||||
* interpreted as part of a namespace or path. The first argument
|
||||
* of a signal is matched against that part as specified by D-Bus.
|
||||
*
|
||||
* Returns: A subscription identifier that can be used with g_dbus_connection_signal_unsubscribe().
|
||||
*
|
||||
* Since: 2.26
|
||||
@ -3456,6 +3470,8 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
|
||||
g_return_val_if_fail (object_path == NULL || g_variant_is_object_path (object_path), 0);
|
||||
g_return_val_if_fail (callback != NULL, 0);
|
||||
g_return_val_if_fail (check_initialized (connection), 0);
|
||||
g_return_val_if_fail (!((flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH) && (flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE)), 0);
|
||||
g_return_val_if_fail (!(arg0 == NULL && (flags & (G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH | G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE))), 0);
|
||||
|
||||
CONNECTION_LOCK (connection);
|
||||
|
||||
@ -3467,8 +3483,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
|
||||
* the usual way, but the '-' prevents the match rule from ever
|
||||
* actually being send to the bus (either for add or remove).
|
||||
*/
|
||||
rule = args_to_rule (sender, interface_name, member, object_path, arg0,
|
||||
(flags & G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE) != 0);
|
||||
rule = args_to_rule (sender, interface_name, member, object_path, arg0, flags);
|
||||
|
||||
if (sender != NULL && (g_dbus_is_unique_name (sender) || g_strcmp0 (sender, "org.freedesktop.DBus") == 0))
|
||||
sender_unique_name = sender;
|
||||
@ -3498,6 +3513,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
|
||||
signal_data->member = g_strdup (member);
|
||||
signal_data->object_path = g_strdup (object_path);
|
||||
signal_data->arg0 = g_strdup (arg0);
|
||||
signal_data->flags = flags;
|
||||
signal_data->subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber));
|
||||
g_array_append_val (signal_data->subscribers, subscriber);
|
||||
|
||||
@ -3733,6 +3749,43 @@ signal_instance_free (SignalInstance *signal_instance)
|
||||
g_free (signal_instance);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
namespace_rule_matches (const gchar *namespace,
|
||||
const gchar *name)
|
||||
{
|
||||
gint len_namespace;
|
||||
gint len_name;
|
||||
|
||||
len_namespace = strlen (namespace);
|
||||
len_name = strlen (name);
|
||||
|
||||
if (len_name < len_namespace)
|
||||
return FALSE;
|
||||
|
||||
if (memcmp (namespace, name, len_namespace) != 0)
|
||||
return FALSE;
|
||||
|
||||
return len_namespace == len_name || name[len_namespace] == '.';
|
||||
}
|
||||
|
||||
static gboolean
|
||||
path_rule_matches (const gchar *path_a,
|
||||
const gchar *path_b)
|
||||
{
|
||||
gint len_a, len_b;
|
||||
|
||||
len_a = strlen (path_a);
|
||||
len_b = strlen (path_b);
|
||||
|
||||
if (len_a < len_b && path_a[len_a - 1] != '/')
|
||||
return FALSE;
|
||||
|
||||
if (len_b < len_a && path_b[len_b - 1] != '/')
|
||||
return FALSE;
|
||||
|
||||
return memcmp (path_a, path_b, MIN (len_a, len_b)) == 0;
|
||||
}
|
||||
|
||||
/* called in GDBusWorker thread WITH lock held */
|
||||
static void
|
||||
schedule_callbacks (GDBusConnection *connection,
|
||||
@ -3786,8 +3839,24 @@ schedule_callbacks (GDBusConnection *connection,
|
||||
if (signal_data->object_path != NULL && g_strcmp0 (signal_data->object_path, path) != 0)
|
||||
continue;
|
||||
|
||||
if (signal_data->arg0 != NULL && g_strcmp0 (signal_data->arg0, arg0) != 0)
|
||||
continue;
|
||||
if (signal_data->arg0 != NULL)
|
||||
{
|
||||
if (arg0 == NULL)
|
||||
continue;
|
||||
|
||||
if (signal_data->flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE)
|
||||
{
|
||||
if (!namespace_rule_matches (signal_data->arg0, arg0))
|
||||
continue;
|
||||
}
|
||||
else if (signal_data->flags & G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH)
|
||||
{
|
||||
if (!path_rule_matches (signal_data->arg0, arg0))
|
||||
continue;
|
||||
}
|
||||
else if (!g_str_equal (signal_data->arg0, arg0))
|
||||
continue;
|
||||
}
|
||||
|
||||
for (m = 0; m < signal_data->subscribers->len; m++)
|
||||
{
|
||||
|
@ -1272,6 +1272,11 @@ typedef enum
|
||||
* @G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE: Don't actually send the AddMatch
|
||||
* D-Bus call for this signal subscription. This gives you more control
|
||||
* over which match rules you add (but you must add them manually).
|
||||
* @G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE: Match first arguments that
|
||||
* contain a bus or interface name with the given namespace.
|
||||
* @G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH: Match first arguments that
|
||||
* contain an object path that is either equivalent to the given path,
|
||||
* or one of the paths is a subpath of the other.
|
||||
*
|
||||
* Flags used when subscribing to signals via g_dbus_connection_signal_subscribe().
|
||||
*
|
||||
@ -1280,7 +1285,9 @@ typedef enum
|
||||
typedef enum /*< flags >*/
|
||||
{
|
||||
G_DBUS_SIGNAL_FLAGS_NONE = 0,
|
||||
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE = (1<<0)
|
||||
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE = (1<<0),
|
||||
G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE = (1<<1),
|
||||
G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH = (1<<2)
|
||||
} GDBusSignalFlags;
|
||||
|
||||
/**
|
||||
|
@ -713,6 +713,83 @@ test_connection_signals (void)
|
||||
session_bus_down ();
|
||||
}
|
||||
|
||||
static void
|
||||
test_match_rule (GDBusConnection *connection,
|
||||
GDBusSignalFlags flags,
|
||||
gchar *arg0_rule,
|
||||
gchar *arg0,
|
||||
gboolean should_match)
|
||||
{
|
||||
guint subscription_ids[2];
|
||||
gint emissions = 0;
|
||||
gint matches = 0;
|
||||
GError *error = NULL;
|
||||
|
||||
subscription_ids[0] = g_dbus_connection_signal_subscribe (connection,
|
||||
NULL, "org.gtk.ExampleInterface", "Foo", "/",
|
||||
NULL,
|
||||
G_DBUS_SIGNAL_FLAGS_NONE,
|
||||
test_connection_signal_handler,
|
||||
&emissions, NULL);
|
||||
subscription_ids[1] = g_dbus_connection_signal_subscribe (connection,
|
||||
NULL, "org.gtk.ExampleInterface", "Foo", "/",
|
||||
arg0_rule,
|
||||
flags,
|
||||
test_connection_signal_handler,
|
||||
&matches, NULL);
|
||||
g_assert_cmpint (subscription_ids[0], !=, 0);
|
||||
g_assert_cmpint (subscription_ids[1], !=, 0);
|
||||
|
||||
g_dbus_connection_emit_signal (connection,
|
||||
NULL, "/", "org.gtk.ExampleInterface",
|
||||
"Foo", g_variant_new ("(s)", arg0),
|
||||
&error);
|
||||
g_assert_no_error (error);
|
||||
|
||||
/* synchronously ping a non-existent method to make sure the signals are dispatched */
|
||||
g_dbus_connection_call_sync (connection, "org.gtk.ExampleInterface", "/", "org.gtk.ExampleInterface",
|
||||
"Bar", g_variant_new ("()"), G_VARIANT_TYPE_UNIT, G_DBUS_CALL_FLAGS_NONE,
|
||||
-1, NULL, NULL);
|
||||
|
||||
while (g_main_context_iteration (NULL, FALSE))
|
||||
;
|
||||
|
||||
g_assert_cmpint (emissions, ==, 1);
|
||||
g_assert_cmpint (matches, ==, should_match ? 1 : 0);
|
||||
|
||||
g_dbus_connection_signal_unsubscribe (connection, subscription_ids[0]);
|
||||
g_dbus_connection_signal_unsubscribe (connection, subscription_ids[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
test_connection_signal_match_rules (void)
|
||||
{
|
||||
GDBusConnection *con;
|
||||
|
||||
session_bus_up ();
|
||||
con = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
|
||||
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_NONE, "foo", "foo", TRUE);
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_NONE, "foo", "bar", FALSE);
|
||||
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "", FALSE);
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "org", FALSE);
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "org.gtk", TRUE);
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "org.gtk.Example", TRUE);
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_NAMESPACE, "org.gtk", "org.gtk+", FALSE);
|
||||
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/", "/", TRUE);
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/", "", FALSE);
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk/Example", "/org/gtk/Example", TRUE);
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk/", "/org/gtk/Example", TRUE);
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk/Example", "/org/gtk/", TRUE);
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk/Example", "/org/gtk", FALSE);
|
||||
test_match_rule (con, G_DBUS_SIGNAL_FLAGS_MATCH_ARG0_PATH, "/org/gtk+", "/org/gtk", FALSE);
|
||||
|
||||
g_object_unref (con);
|
||||
session_bus_down ();
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------- */
|
||||
|
||||
typedef struct
|
||||
@ -1153,6 +1230,7 @@ main (int argc,
|
||||
g_test_add_func ("/gdbus/connection/life-cycle", test_connection_life_cycle);
|
||||
g_test_add_func ("/gdbus/connection/send", test_connection_send);
|
||||
g_test_add_func ("/gdbus/connection/signals", test_connection_signals);
|
||||
g_test_add_func ("/gdbus/connection/signal-match-rules", test_connection_signal_match_rules);
|
||||
g_test_add_func ("/gdbus/connection/filter", test_connection_filter);
|
||||
g_test_add_func ("/gdbus/connection/serials", test_connection_serials);
|
||||
return g_test_run();
|
||||
|
Loading…
Reference in New Issue
Block a user