mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-26 23:46:15 +01:00
402ad1958c
The GClosure API is a bit funky (and badly documented), and requires you to set a marshaller on the closure, and the marshaller has an implicit 'this' argument, and the caller is reponsible for unsetting the values after invoking the closure. I've added some calls of the _with_closures variants to the gdbus-names test now.
854 lines
28 KiB
C
854 lines
28 KiB
C
/* 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 <davidz@redhat.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "gdbusutils.h"
|
|
#include "gdbusnamewatching.h"
|
|
#include "gdbuserror.h"
|
|
#include "gdbusprivate.h"
|
|
#include "gdbusconnection.h"
|
|
#include "gio-marshal.h"
|
|
|
|
#include "glibintl.h"
|
|
|
|
/**
|
|
* SECTION:gdbusnamewatching
|
|
* @title: Watching Bus Names
|
|
* @short_description: Simple API for watching bus names
|
|
* @include: gio/gio.h
|
|
*
|
|
* Convenience API for watching bus names.
|
|
*
|
|
* <example id="gdbus-watching-names"><title>Simple application watching a name</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gio/tests/gdbus-example-watch-name.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
|
|
*/
|
|
|
|
G_LOCK_DEFINE_STATIC (lock);
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
typedef enum
|
|
{
|
|
PREVIOUS_CALL_NONE = 0,
|
|
PREVIOUS_CALL_APPEARED,
|
|
PREVIOUS_CALL_VANISHED,
|
|
} PreviousCall;
|
|
|
|
typedef struct
|
|
{
|
|
volatile gint ref_count;
|
|
guint id;
|
|
gchar *name;
|
|
GBusNameWatcherFlags flags;
|
|
gchar *name_owner;
|
|
GBusNameAppearedCallback name_appeared_handler;
|
|
GBusNameVanishedCallback name_vanished_handler;
|
|
gpointer user_data;
|
|
GDestroyNotify user_data_free_func;
|
|
GMainContext *main_context;
|
|
|
|
GDBusConnection *connection;
|
|
gulong disconnected_signal_handler_id;
|
|
guint name_owner_changed_subscription_id;
|
|
|
|
PreviousCall previous_call;
|
|
|
|
gboolean cancelled;
|
|
gboolean initialized;
|
|
} Client;
|
|
|
|
static guint next_global_id = 1;
|
|
static GHashTable *map_id_to_client = NULL;
|
|
|
|
static Client *
|
|
client_ref (Client *client)
|
|
{
|
|
g_atomic_int_inc (&client->ref_count);
|
|
return client;
|
|
}
|
|
|
|
static void
|
|
client_unref (Client *client)
|
|
{
|
|
if (g_atomic_int_dec_and_test (&client->ref_count))
|
|
{
|
|
if (client->connection != NULL)
|
|
{
|
|
if (client->name_owner_changed_subscription_id > 0)
|
|
g_dbus_connection_signal_unsubscribe (client->connection, client->name_owner_changed_subscription_id);
|
|
if (client->disconnected_signal_handler_id > 0)
|
|
g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id);
|
|
g_object_unref (client->connection);
|
|
}
|
|
g_free (client->name);
|
|
g_free (client->name_owner);
|
|
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);
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
typedef enum
|
|
{
|
|
CALL_TYPE_NAME_APPEARED,
|
|
CALL_TYPE_NAME_VANISHED
|
|
} CallType;
|
|
|
|
typedef struct
|
|
{
|
|
Client *client;
|
|
|
|
/* keep this separate because client->connection may
|
|
* be set to NULL after scheduling the call
|
|
*/
|
|
GDBusConnection *connection;
|
|
|
|
/* ditto */
|
|
gchar *name_owner;
|
|
|
|
CallType call_type;
|
|
} CallHandlerData;
|
|
|
|
static void
|
|
call_handler_data_free (CallHandlerData *data)
|
|
{
|
|
if (data->connection != NULL)
|
|
g_object_unref (data->connection);
|
|
g_free (data->name_owner);
|
|
client_unref (data->client);
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
actually_do_call (Client *client, GDBusConnection *connection, const gchar *name_owner, CallType call_type)
|
|
{
|
|
switch (call_type)
|
|
{
|
|
case CALL_TYPE_NAME_APPEARED:
|
|
if (client->name_appeared_handler != NULL)
|
|
{
|
|
client->name_appeared_handler (connection,
|
|
client->name,
|
|
name_owner,
|
|
client->user_data);
|
|
}
|
|
break;
|
|
|
|
case CALL_TYPE_NAME_VANISHED:
|
|
if (client->name_vanished_handler != NULL)
|
|
{
|
|
client->name_vanished_handler (connection,
|
|
client->name,
|
|
client->user_data);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
call_in_idle_cb (gpointer _data)
|
|
{
|
|
CallHandlerData *data = _data;
|
|
actually_do_call (data->client, data->connection, data->name_owner, data->call_type);
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
schedule_call_in_idle (Client *client, CallType call_type)
|
|
{
|
|
CallHandlerData *data;
|
|
GSource *idle_source;
|
|
|
|
data = g_new0 (CallHandlerData, 1);
|
|
data->client = client_ref (client);
|
|
data->connection = client->connection != NULL ? g_object_ref (client->connection) : NULL;
|
|
data->name_owner = g_strdup (client->name_owner);
|
|
data->call_type = call_type;
|
|
|
|
idle_source = g_idle_source_new ();
|
|
g_source_set_priority (idle_source, G_PRIORITY_HIGH);
|
|
g_source_set_callback (idle_source,
|
|
call_in_idle_cb,
|
|
data,
|
|
(GDestroyNotify) call_handler_data_free);
|
|
g_source_attach (idle_source, client->main_context);
|
|
g_source_unref (idle_source);
|
|
}
|
|
|
|
static void
|
|
do_call (Client *client, CallType call_type)
|
|
{
|
|
/* only schedule in idle if we're not in the right thread */
|
|
if (g_main_context_get_thread_default () != client->main_context)
|
|
schedule_call_in_idle (client, call_type);
|
|
else
|
|
actually_do_call (client, client->connection, client->name_owner, call_type);
|
|
}
|
|
|
|
static void
|
|
call_appeared_handler (Client *client)
|
|
{
|
|
if (client->previous_call != PREVIOUS_CALL_APPEARED)
|
|
{
|
|
client->previous_call = PREVIOUS_CALL_APPEARED;
|
|
if (!client->cancelled && client->name_appeared_handler != NULL)
|
|
{
|
|
do_call (client, CALL_TYPE_NAME_APPEARED);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
call_vanished_handler (Client *client,
|
|
gboolean ignore_cancelled)
|
|
{
|
|
if (client->previous_call != PREVIOUS_CALL_VANISHED)
|
|
{
|
|
client->previous_call = PREVIOUS_CALL_VANISHED;
|
|
if (((!client->cancelled) || ignore_cancelled) && client->name_vanished_handler != NULL)
|
|
{
|
|
do_call (client, CALL_TYPE_NAME_VANISHED);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
static void
|
|
on_connection_disconnected (GDBusConnection *connection,
|
|
gboolean remote_peer_vanished,
|
|
GError *error,
|
|
gpointer user_data)
|
|
{
|
|
Client *client = user_data;
|
|
|
|
if (client->name_owner_changed_subscription_id > 0)
|
|
g_dbus_connection_signal_unsubscribe (client->connection, client->name_owner_changed_subscription_id);
|
|
if (client->disconnected_signal_handler_id > 0)
|
|
g_signal_handler_disconnect (client->connection, client->disconnected_signal_handler_id);
|
|
g_object_unref (client->connection);
|
|
client->disconnected_signal_handler_id = 0;
|
|
client->name_owner_changed_subscription_id = 0;
|
|
client->connection = NULL;
|
|
|
|
call_vanished_handler (client, FALSE);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
static void
|
|
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)
|
|
{
|
|
Client *client = user_data;
|
|
const gchar *name;
|
|
const gchar *old_owner;
|
|
const gchar *new_owner;
|
|
|
|
if (!client->initialized)
|
|
goto out;
|
|
|
|
if (g_strcmp0 (object_path, "/org/freedesktop/DBus") != 0 ||
|
|
g_strcmp0 (interface_name, "org.freedesktop.DBus") != 0 ||
|
|
g_strcmp0 (sender_name, "org.freedesktop.DBus") != 0)
|
|
goto out;
|
|
|
|
g_variant_get (parameters,
|
|
"(&s&s&s)",
|
|
&name,
|
|
&old_owner,
|
|
&new_owner);
|
|
|
|
/* we only care about a specific name */
|
|
if (g_strcmp0 (name, client->name) != 0)
|
|
goto out;
|
|
|
|
if ((old_owner != NULL && strlen (old_owner) > 0) && client->name_owner != NULL)
|
|
{
|
|
g_free (client->name_owner);
|
|
client->name_owner = NULL;
|
|
call_vanished_handler (client, FALSE);
|
|
}
|
|
|
|
if (new_owner != NULL && strlen (new_owner) > 0)
|
|
{
|
|
g_warn_if_fail (client->name_owner == NULL);
|
|
g_free (client->name_owner);
|
|
client->name_owner = g_strdup (new_owner);
|
|
call_appeared_handler (client);
|
|
}
|
|
|
|
out:
|
|
;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
static void
|
|
get_name_owner_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
Client *client = user_data;
|
|
GVariant *result;
|
|
const char *name_owner;
|
|
|
|
name_owner = NULL;
|
|
result = NULL;
|
|
|
|
result = g_dbus_connection_call_finish (client->connection,
|
|
res,
|
|
NULL);
|
|
if (result != NULL)
|
|
{
|
|
g_variant_get (result, "(&s)", &name_owner);
|
|
}
|
|
|
|
if (name_owner != NULL)
|
|
{
|
|
g_warn_if_fail (client->name_owner == NULL);
|
|
client->name_owner = g_strdup (name_owner);
|
|
call_appeared_handler (client);
|
|
}
|
|
else
|
|
{
|
|
call_vanished_handler (client, FALSE);
|
|
}
|
|
|
|
client->initialized = TRUE;
|
|
|
|
if (result != NULL)
|
|
g_variant_unref (result);
|
|
client_unref (client);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
static void
|
|
invoke_get_name_owner (Client *client)
|
|
{
|
|
g_dbus_connection_call (client->connection,
|
|
"org.freedesktop.DBus", /* bus name */
|
|
"/org/freedesktop/DBus", /* object path */
|
|
"org.freedesktop.DBus", /* interface name */
|
|
"GetNameOwner", /* method name */
|
|
g_variant_new ("(s)", client->name),
|
|
G_VARIANT_TYPE ("(s)"),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
(GAsyncReadyCallback) get_name_owner_cb,
|
|
client_ref (client));
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
static void
|
|
start_service_by_name_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
Client *client = user_data;
|
|
GVariant *result;
|
|
|
|
result = NULL;
|
|
|
|
result = g_dbus_connection_call_finish (client->connection,
|
|
res,
|
|
NULL);
|
|
if (result != NULL)
|
|
{
|
|
guint32 start_service_result;
|
|
g_variant_get (result, "(u)", &start_service_result);
|
|
|
|
if (start_service_result == 1) /* DBUS_START_REPLY_SUCCESS */
|
|
{
|
|
invoke_get_name_owner (client);
|
|
}
|
|
else if (start_service_result == 2) /* DBUS_START_REPLY_ALREADY_RUNNING */
|
|
{
|
|
invoke_get_name_owner (client);
|
|
}
|
|
else
|
|
{
|
|
g_warning ("Unexpected reply %d from StartServiceByName() method", start_service_result);
|
|
call_vanished_handler (client, FALSE);
|
|
client->initialized = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* 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 proceed to
|
|
* invoke GetNameOwner().
|
|
*/
|
|
invoke_get_name_owner (client);
|
|
}
|
|
|
|
if (result != NULL)
|
|
g_variant_unref (result);
|
|
client_unref (client);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
static void
|
|
has_connection (Client *client)
|
|
{
|
|
/* listen for disconnection */
|
|
client->disconnected_signal_handler_id = g_signal_connect (client->connection,
|
|
"closed",
|
|
G_CALLBACK (on_connection_disconnected),
|
|
client);
|
|
|
|
/* start listening to NameOwnerChanged messages immediately */
|
|
client->name_owner_changed_subscription_id = g_dbus_connection_signal_subscribe (client->connection,
|
|
"org.freedesktop.DBus", /* name */
|
|
"org.freedesktop.DBus", /* if */
|
|
"NameOwnerChanged", /* signal */
|
|
"/org/freedesktop/DBus", /* path */
|
|
client->name,
|
|
G_DBUS_SIGNAL_FLAGS_NONE,
|
|
on_name_owner_changed,
|
|
client,
|
|
NULL);
|
|
|
|
if (client->flags & G_BUS_NAME_WATCHER_FLAGS_AUTO_START)
|
|
{
|
|
g_dbus_connection_call (client->connection,
|
|
"org.freedesktop.DBus", /* bus name */
|
|
"/org/freedesktop/DBus", /* object path */
|
|
"org.freedesktop.DBus", /* interface name */
|
|
"StartServiceByName", /* method name */
|
|
g_variant_new ("(su)", client->name, 0),
|
|
G_VARIANT_TYPE ("(u)"),
|
|
G_DBUS_CALL_FLAGS_NONE,
|
|
-1,
|
|
NULL,
|
|
(GAsyncReadyCallback) start_service_by_name_cb,
|
|
client_ref (client));
|
|
}
|
|
else
|
|
{
|
|
/* check owner */
|
|
invoke_get_name_owner (client);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
connection_get_cb (GObject *source_object,
|
|
GAsyncResult *res,
|
|
gpointer user_data)
|
|
{
|
|
Client *client = user_data;
|
|
|
|
client->connection = g_bus_get_finish (res, NULL);
|
|
if (client->connection == NULL)
|
|
{
|
|
call_vanished_handler (client, FALSE);
|
|
goto out;
|
|
}
|
|
|
|
has_connection (client);
|
|
|
|
out:
|
|
client_unref (client);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------------------------------- */
|
|
|
|
/**
|
|
* g_bus_watch_name:
|
|
* @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.
|
|
* @name_appeared_handler: Handler to invoke when @name is known to exist or %NULL.
|
|
* @name_vanished_handler: Handler to invoke when @name is known to not exist or %NULL.
|
|
* @user_data: User data to pass to handlers.
|
|
* @user_data_free_func: Function for freeing @user_data or %NULL.
|
|
*
|
|
* Starts watching @name on the bus specified by @bus_type and calls
|
|
* @name_appeared_handler and @name_vanished_handler when the name is
|
|
* known to have a owner respectively known to lose its
|
|
* owner. Callbacks will be invoked in the <link
|
|
* linkend="g-main-context-push-thread-default">thread-default main
|
|
* loop</link> of the thread you are calling this function from.
|
|
*
|
|
* You are guaranteed that one of the handlers will be invoked after
|
|
* calling this function. When you are done watching the name, just
|
|
* call g_bus_unwatch_name() with the watcher id this function
|
|
* returns.
|
|
*
|
|
* If the name vanishes or appears (for example the application owning
|
|
* the name could restart), the handlers are also invoked. If the
|
|
* #GDBusConnection that is used for watching the name disconnects, then
|
|
* @name_vanished_handler is invoked since it is no longer
|
|
* possible to access the name.
|
|
*
|
|
* Another guarantee is that invocations of @name_appeared_handler
|
|
* and @name_vanished_handler are guaranteed to alternate; that
|
|
* is, if @name_appeared_handler is invoked then you are
|
|
* guaranteed that the next time one of the handlers is invoked, it
|
|
* will be @name_vanished_handler. The reverse is also true.
|
|
*
|
|
* This behavior makes it very simple to write applications that wants
|
|
* to take action when a certain name exists, see <xref
|
|
* linkend="gdbus-watching-names"/>. Basically, the application
|
|
* should create object proxies in @name_appeared_handler and destroy
|
|
* them again (if any) in @name_vanished_handler.
|
|
*
|
|
* Returns: An identifier (never 0) that an be used with
|
|
* g_bus_unwatch_name() to stop watching the name.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
guint
|
|
g_bus_watch_name (GBusType bus_type,
|
|
const gchar *name,
|
|
GBusNameWatcherFlags flags,
|
|
GBusNameAppearedCallback name_appeared_handler,
|
|
GBusNameVanishedCallback name_vanished_handler,
|
|
gpointer user_data,
|
|
GDestroyNotify user_data_free_func)
|
|
{
|
|
Client *client;
|
|
|
|
g_return_val_if_fail (g_dbus_is_name (name), 0);
|
|
|
|
G_LOCK (lock);
|
|
|
|
client = g_new0 (Client, 1);
|
|
client->ref_count = 1;
|
|
client->id = next_global_id++; /* TODO: uh oh, handle overflow */
|
|
client->name = g_strdup (name);
|
|
client->flags = flags;
|
|
client->name_appeared_handler = name_appeared_handler;
|
|
client->name_vanished_handler = name_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);
|
|
|
|
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_bus_get (bus_type,
|
|
NULL,
|
|
connection_get_cb,
|
|
client_ref (client));
|
|
|
|
G_UNLOCK (lock);
|
|
|
|
return client->id;
|
|
}
|
|
|
|
/**
|
|
* g_bus_watch_name_on_connection:
|
|
* @connection: A #GDBusConnection.
|
|
* @name: The name (well-known or unique) to watch.
|
|
* @flags: Flags from the #GBusNameWatcherFlags enumeration.
|
|
* @name_appeared_handler: Handler to invoke when @name is known to exist or %NULL.
|
|
* @name_vanished_handler: Handler to invoke when @name is known to not exist or %NULL.
|
|
* @user_data: User data to pass to handlers.
|
|
* @user_data_free_func: Function for freeing @user_data or %NULL.
|
|
*
|
|
* Like g_bus_watch_name() but takes a #GDBusConnection instead of a
|
|
* #GBusType.
|
|
*
|
|
* Returns: An identifier (never 0) that an be used with
|
|
* g_bus_unwatch_name() to stop watching the name.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
guint g_bus_watch_name_on_connection (GDBusConnection *connection,
|
|
const gchar *name,
|
|
GBusNameWatcherFlags flags,
|
|
GBusNameAppearedCallback name_appeared_handler,
|
|
GBusNameVanishedCallback name_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_LOCK (lock);
|
|
|
|
client = g_new0 (Client, 1);
|
|
client->ref_count = 1;
|
|
client->id = next_global_id++; /* TODO: uh oh, handle overflow */
|
|
client->name = g_strdup (name);
|
|
client->flags = flags;
|
|
client->name_appeared_handler = name_appeared_handler;
|
|
client->name_vanished_handler = name_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);
|
|
|
|
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);
|
|
|
|
client->connection = g_object_ref (connection);
|
|
G_UNLOCK (lock);
|
|
|
|
has_connection (client);
|
|
|
|
return client->id;
|
|
}
|
|
|
|
typedef struct {
|
|
GClosure *name_appeared_closure;
|
|
GClosure *name_vanished_closure;
|
|
} WatchNameData;
|
|
|
|
static WatchNameData *
|
|
watch_name_data_new (GClosure *name_appeared_closure,
|
|
GClosure *name_vanished_closure)
|
|
{
|
|
WatchNameData *data;
|
|
|
|
data = g_new0 (WatchNameData, 1);
|
|
|
|
if (name_appeared_closure != NULL)
|
|
{
|
|
data->name_appeared_closure = g_closure_ref (name_appeared_closure);
|
|
g_closure_sink (name_appeared_closure);
|
|
if (G_CLOSURE_NEEDS_MARSHAL (name_appeared_closure))
|
|
g_closure_set_marshal (name_appeared_closure, _gio_marshal_VOID__STRING_STRING);
|
|
}
|
|
|
|
if (name_vanished_closure != NULL)
|
|
{
|
|
data->name_vanished_closure = g_closure_ref (name_vanished_closure);
|
|
g_closure_sink (name_vanished_closure);
|
|
if (G_CLOSURE_NEEDS_MARSHAL (name_vanished_closure))
|
|
g_closure_set_marshal (name_vanished_closure, _gio_marshal_VOID__STRING);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
static void
|
|
watch_with_closures_on_name_appeared (GDBusConnection *connection,
|
|
const gchar *name,
|
|
const gchar *name_owner,
|
|
gpointer user_data)
|
|
{
|
|
WatchNameData *data = user_data;
|
|
GValue params[3] = { { 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_closure_invoke (data->name_appeared_closure, NULL, 3, params, NULL);
|
|
|
|
g_value_unset (params + 0);
|
|
g_value_unset (params + 1);
|
|
g_value_unset (params + 2);
|
|
}
|
|
|
|
static void
|
|
watch_with_closures_on_name_vanished (GDBusConnection *connection,
|
|
const gchar *name,
|
|
gpointer user_data)
|
|
{
|
|
WatchNameData *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->name_vanished_closure, NULL, 2, params, NULL);
|
|
|
|
g_value_unset (params + 0);
|
|
g_value_unset (params + 1);
|
|
}
|
|
|
|
static void
|
|
bus_watch_name_free_func (gpointer user_data)
|
|
{
|
|
WatchNameData *data = user_data;
|
|
|
|
if (data->name_appeared_closure != NULL)
|
|
g_closure_unref (data->name_appeared_closure);
|
|
|
|
if (data->name_vanished_closure != NULL)
|
|
g_closure_unref (data->name_vanished_closure);
|
|
|
|
g_free (data);
|
|
}
|
|
|
|
/**
|
|
* g_bus_watch_name_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.
|
|
* @name_appeared_closure: (allow-none): #GClosure to invoke when @name is known
|
|
* to exist or %NULL.
|
|
* @name_vanished_closure: (allow-none): #GClosure to invoke when @name is known
|
|
* to not exist or %NULL.
|
|
*
|
|
* Version of g_bus_watch_name() using closures instead of callbacks for
|
|
* easier binding in other languages.
|
|
*
|
|
* Returns: An identifier (never 0) that an be used with
|
|
* g_bus_unwatch_name() to stop watching the name.
|
|
*
|
|
* Rename to: g_bus_watch_name
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
guint
|
|
g_bus_watch_name_with_closures (GBusType bus_type,
|
|
const gchar *name,
|
|
GBusNameWatcherFlags flags,
|
|
GClosure *name_appeared_closure,
|
|
GClosure *name_vanished_closure)
|
|
{
|
|
return g_bus_watch_name (bus_type,
|
|
name,
|
|
flags,
|
|
name_appeared_closure != NULL ? watch_with_closures_on_name_appeared : NULL,
|
|
name_vanished_closure != NULL ? watch_with_closures_on_name_vanished : NULL,
|
|
watch_name_data_new (name_appeared_closure, name_vanished_closure),
|
|
bus_watch_name_free_func);
|
|
}
|
|
|
|
/**
|
|
* g_bus_watch_name_on_connection_with_closures:
|
|
* @connection: A #GDBusConnection.
|
|
* @name: The name (well-known or unique) to watch.
|
|
* @flags: Flags from the #GBusNameWatcherFlags enumeration.
|
|
* @name_appeared_closure: (allow-none): #GClosure to invoke when @name is known
|
|
* to exist or %NULL.
|
|
* @name_vanished_closure: (allow-none): #GClosure to invoke when @name is known
|
|
* to not exist or %NULL.
|
|
*
|
|
* Version of g_bus_watch_name_on_connection() using closures instead of callbacks for
|
|
* easier binding in other languages.
|
|
*
|
|
* Returns: An identifier (never 0) that an be used with
|
|
* g_bus_unwatch_name() to stop watching the name.
|
|
*
|
|
* Rename to: g_bus_watch_name_on_connection
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
guint g_bus_watch_name_on_connection_with_closures (
|
|
GDBusConnection *connection,
|
|
const gchar *name,
|
|
GBusNameWatcherFlags flags,
|
|
GClosure *name_appeared_closure,
|
|
GClosure *name_vanished_closure)
|
|
{
|
|
return g_bus_watch_name_on_connection (connection,
|
|
name,
|
|
flags,
|
|
name_appeared_closure != NULL ? watch_with_closures_on_name_appeared : NULL,
|
|
name_vanished_closure != NULL ? watch_with_closures_on_name_vanished : NULL,
|
|
watch_name_data_new (name_appeared_closure, name_vanished_closure),
|
|
bus_watch_name_free_func);
|
|
}
|
|
|
|
/**
|
|
* g_bus_unwatch_name:
|
|
* @watcher_id: An identifier obtained from g_bus_watch_name()
|
|
*
|
|
* Stops watching a name.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
void
|
|
g_bus_unwatch_name (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_name()", watcher_id);
|
|
goto out;
|
|
}
|
|
|
|
client->cancelled = TRUE;
|
|
g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (watcher_id)));
|
|
|
|
out:
|
|
G_UNLOCK (lock);
|
|
|
|
/* do callback without holding lock */
|
|
if (client != NULL)
|
|
{
|
|
client_unref (client);
|
|
}
|
|
}
|