aboutsummaryrefslogtreecommitdiff
path: root/libindicator/indicator-service.c
diff options
context:
space:
mode:
Diffstat (limited to 'libindicator/indicator-service.c')
-rw-r--r--libindicator/indicator-service.c333
1 files changed, 188 insertions, 145 deletions
diff --git a/libindicator/indicator-service.c b/libindicator/indicator-service.c
index e9005db..e5eaa5b 100644
--- a/libindicator/indicator-service.c
+++ b/libindicator/indicator-service.c
@@ -24,40 +24,39 @@ License along with this library. If not, see
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-#include <dbus/dbus-glib-bindings.h>
-#include <dbus/dbus-glib-lowlevel.h>
+
+#include <gio/gio.h>
#include "indicator-service.h"
+#include "gen-indicator-service.xml.h"
+#include "dbus-shared.h"
static void unwatch_core (IndicatorService * service, const gchar * name);
-static void proxy_destroyed (GObject * proxy, gpointer user_data);
static gboolean watchers_remove (gpointer key, gpointer value, gpointer user_data);
-/* DBus Prototypes */
-static gboolean _indicator_service_server_watch (IndicatorService * service, DBusGMethodInvocation * method);
-static gboolean _indicator_service_server_un_watch (IndicatorService * service, DBusGMethodInvocation * method);
-
-#include "indicator-service-server.h"
-#include "dbus-shared.h"
+static void bus_get_cb (GObject * object, GAsyncResult * res, gpointer user_data);
+static GVariant * bus_watch (IndicatorService * service, const gchar * sender);
/* Private Stuff */
/**
IndicatorSevicePrivate:
@name: The DBus well known name for the service.
- @dbus_proxy: A proxy for talking to the dbus bus manager.
@timeout: The source ID for the timeout event.
@watcher: A list of processes on dbus that are watching us.
@this_service_version: The version to hand out that we're
implementing. May not be set, so we'll send zero (default).
+ @dbus_registration: The handle for this object being registered
+ on dbus.
*/
typedef struct _IndicatorServicePrivate IndicatorServicePrivate;
struct _IndicatorServicePrivate {
gchar * name;
- DBusGProxy * dbus_proxy;
- DBusGConnection * bus;
+ GDBusConnection * bus;
+ GCancellable * bus_cancel;
guint timeout;
guint timeout_length;
GHashTable * watchers;
guint this_service_version;
+ guint dbus_registration;
};
/* Signals Stuff */
@@ -94,7 +93,18 @@ static void indicator_service_finalize (GObject *object);
static void set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec);
static void get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec);
static void try_and_get_name (IndicatorService * service);
+static void bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data);
+
+/* GDBus Stuff */
+static GDBusNodeInfo * node_info = NULL;
+static GDBusInterfaceInfo * interface_info = NULL;
+static GDBusInterfaceVTable interface_table = {
+ method_call: bus_method_call,
+ get_property: NULL, /* No properties */
+ set_property: NULL /* No properties */
+};
+/* THE define */
G_DEFINE_TYPE (IndicatorService, indicator_service, G_TYPE_OBJECT);
static void
@@ -142,9 +152,24 @@ indicator_service_class_init (IndicatorServiceClass *klass)
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0, G_TYPE_NONE);
- /* Initialize the object as a DBus type */
- dbus_g_object_type_install_info(INDICATOR_SERVICE_TYPE,
- &dbus_glib__indicator_service_server_object_info);
+ /* Setting up the DBus interfaces */
+ if (node_info == NULL) {
+ GError * error = NULL;
+
+ node_info = g_dbus_node_info_new_for_xml(_indicator_service, &error);
+ if (error != NULL) {
+ g_error("Unable to parse Indicator Service Interface description: %s", error->message);
+ g_error_free(error);
+ }
+ }
+
+ if (interface_info == NULL) {
+ interface_info = g_dbus_node_info_lookup_interface(node_info, INDICATOR_SERVICE_INTERFACE);
+
+ if (interface_info == NULL) {
+ g_error("Unable to find interface '" INDICATOR_SERVICE_INTERFACE "'");
+ }
+ }
return;
}
@@ -160,12 +185,13 @@ indicator_service_init (IndicatorService *self)
/* Get the private variables in a decent state */
priv->name = NULL;
- priv->dbus_proxy = NULL;
priv->timeout = 0;
priv->watchers = NULL;
priv->bus = NULL;
+ priv->bus_cancel = NULL;
priv->this_service_version = 0;
priv->timeout_length = 500;
+ priv->dbus_registration = 0;
const gchar * timeoutenv = g_getenv("INDICATOR_SERVICE_SHUTDOWN_TIMEOUT");
if (timeoutenv != NULL) {
@@ -176,45 +202,17 @@ indicator_service_init (IndicatorService *self)
}
}
- /* NOTE: We're using g_object_unref here because that's what needs to
+ /* NOTE: We're using g_free here because that's what needs to
happen, but you really should call watchers_remove first as well
since that disconnects the signals. We can't do that with a callback
here because there is no user data to pass the object as well. */
- priv->watchers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref);
-
- /* Start talkin' dbus */
- GError * error = NULL;
- priv->bus = dbus_g_bus_get(DBUS_BUS_STARTER, &error);
- if (error != NULL) {
- g_error("Unable to get starter bus: %s", error->message);
- g_error_free(error);
-
- /* Okay, fine let's try the session bus then. */
- /* I think this should automatically, but I can't find confirmation
- of that, so we're putting the extra little code in here. */
- error = NULL;
- priv->bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
- if (error != NULL) {
- g_error("Unable to get session bus: %s", error->message);
- g_error_free(error);
- return;
- }
- }
-
- priv->dbus_proxy = dbus_g_proxy_new_for_name_owner(priv->bus,
- DBUS_SERVICE_DBUS,
- DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS,
- &error);
- if (error != NULL) {
- g_error("Unable to get the proxy to DBus: %s", error->message);
- g_error_free(error);
- return;
- }
+ priv->watchers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
- dbus_g_connection_register_g_object(priv->bus,
- INDICATOR_SERVICE_OBJECT,
- G_OBJECT(self));
+ priv->bus_cancel = g_cancellable_new();
+ g_bus_get(G_BUS_TYPE_SESSION,
+ priv->bus_cancel,
+ bus_get_cb,
+ self);
return;
}
@@ -230,16 +228,28 @@ indicator_service_dispose (GObject *object)
g_hash_table_foreach_remove(priv->watchers, watchers_remove, object);
}
- if (priv->dbus_proxy != NULL) {
- g_object_unref(G_OBJECT(priv->dbus_proxy));
- priv->dbus_proxy = NULL;
- }
-
if (priv->timeout != 0) {
g_source_remove(priv->timeout);
priv->timeout = 0;
}
+ if (priv->dbus_registration != 0) {
+ g_dbus_connection_unregister_object(priv->bus, priv->dbus_registration);
+ /* Don't care if it fails, there's nothing we can do */
+ priv->dbus_registration = 0;
+ }
+
+ if (priv->bus != NULL) {
+ g_object_unref(priv->bus);
+ priv->bus = NULL;
+ }
+
+ if (priv->bus_cancel != NULL) {
+ g_cancellable_cancel(priv->bus_cancel);
+ g_object_unref(priv->bus_cancel);
+ priv->bus_cancel = NULL;
+ }
+
G_OBJECT_CLASS (indicator_service_parent_class)->dispose (object);
return;
}
@@ -335,12 +345,72 @@ get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspe
return;
}
+/* Callback for getting our connection to DBus */
+static void
+bus_get_cb (GObject * object, GAsyncResult * res, gpointer user_data)
+{
+ GError * error = NULL;
+ GDBusConnection * connection = g_bus_get_finish(res, &error);
+
+ if (error != NULL) {
+ g_error("OMG! Unable to get a connection to DBus: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ IndicatorServicePrivate * priv = INDICATOR_SERVICE_GET_PRIVATE(user_data);
+
+ g_warn_if_fail(priv->bus == NULL);
+ priv->bus = connection;
+
+ if (priv->bus_cancel != NULL) {
+ g_object_unref(priv->bus_cancel);
+ priv->bus_cancel = NULL;
+ }
+
+ /* Now register our object on our new connection */
+ priv->dbus_registration = g_dbus_connection_register_object(priv->bus,
+ INDICATOR_SERVICE_OBJECT,
+ interface_info,
+ &interface_table,
+ user_data,
+ NULL,
+ &error);
+ if (error != NULL) {
+ g_error("Unable to register the object to DBus: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ return;
+}
+
+/* A method has been called from our dbus inteface. Figure out what it
+ is and dispatch it. */
+static void
+bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data)
+{
+ IndicatorService * service = INDICATOR_SERVICE(user_data);
+ GVariant * retval = NULL;
+
+ if (g_strcmp0(method, "Watch") == 0) {
+ retval = bus_watch(service, sender);
+ } else if (g_strcmp0(method, "UnWatch") == 0) {
+ unwatch_core(service, sender);
+ } else {
+ g_warning("Calling method '%s' on the indicator service and it's unknown", method);
+ }
+
+ g_dbus_method_invocation_return_value(invocation, retval);
+ return;
+}
+
/* A function to remove the signals on a proxy before we destroy
it because in this case we've stopped caring. */
static gboolean
watchers_remove (gpointer key, gpointer value, gpointer user_data)
{
- g_signal_handlers_disconnect_by_func(G_OBJECT(value), G_CALLBACK(proxy_destroyed), user_data);
+ g_bus_unwatch_name(GPOINTER_TO_UINT(value));
return TRUE;
}
@@ -359,34 +429,41 @@ timeout_no_watchers (gpointer data)
return FALSE;
}
-/* The callback from our request to get a well known name
- on dbus. If we can't get it we send the shutdown signal.
- Else we start the timer to see if anyone cares about us. */
+/* Callback saying that the name we were looking for has been
+ found and we've got it. Now start the timer to see if anyone
+ cares about us. */
static void
-try_and_get_name_cb (DBusGProxy * proxy, guint status, GError * error, gpointer data)
+try_and_get_name_acquired_cb (GDBusConnection * connection, const gchar * name, gpointer user_data)
{
- IndicatorService * service = INDICATOR_SERVICE(data);
- g_return_if_fail(service != NULL);
+ g_return_if_fail(connection != NULL);
+ g_return_if_fail(INDICATOR_IS_SERVICE(user_data));
- if (error != NULL) {
- g_warning("Unable to send message to request name: %s", error->message);
- g_signal_emit(G_OBJECT(data), signals[SHUTDOWN], 0, TRUE);
- return;
- }
+ IndicatorServicePrivate * priv = INDICATOR_SERVICE_GET_PRIVATE(user_data);
- if (status != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER && status != DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER) {
- /* The already owner seems like it shouldn't ever
- happen, but I have a hard time throwing an error
- on it as we did achieve our goals. */
- g_warning("Name request failed. Status returned: %d", status);
- g_signal_emit(G_OBJECT(data), signals[SHUTDOWN], 0, TRUE);
- return;
+ /* Check to see if we already had a timer, if so we want to
+ extend it a bit. */
+ if (priv->timeout != 0) {
+ g_source_remove(priv->timeout);
+ priv->timeout = 0;
}
- IndicatorServicePrivate * priv = INDICATOR_SERVICE_GET_PRIVATE(service);
/* Allow some extra time at start up as things can be in high
contention then. */
- priv->timeout = g_timeout_add(priv->timeout_length * 2, timeout_no_watchers, service);
+ priv->timeout = g_timeout_add(priv->timeout_length * 2, timeout_no_watchers, user_data);
+
+ return;
+}
+
+/* Callback saying that we didn't get the name, so we need to
+ shutdown this service. */
+static void
+try_and_get_name_lost_cb (GDBusConnection * connection, const gchar * name, gpointer user_data)
+{
+ g_return_if_fail(connection != NULL);
+ g_return_if_fail(INDICATOR_IS_SERVICE(user_data));
+
+ g_warning("Name request failed.");
+ g_signal_emit(G_OBJECT(user_data), signals[SHUTDOWN], 0, TRUE);
return;
}
@@ -396,50 +473,34 @@ static void
try_and_get_name (IndicatorService * service)
{
IndicatorServicePrivate * priv = INDICATOR_SERVICE_GET_PRIVATE(service);
- g_return_if_fail(priv->dbus_proxy != NULL);
g_return_if_fail(priv->name != NULL);
- org_freedesktop_DBus_request_name_async(priv->dbus_proxy,
- priv->name,
- DBUS_NAME_FLAG_DO_NOT_QUEUE,
- try_and_get_name_cb,
- service);
+ g_bus_own_name(G_BUS_TYPE_SESSION,
+ priv->name,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ NULL, /* bus acquired */
+ try_and_get_name_acquired_cb, /* name acquired */
+ try_and_get_name_lost_cb, /* name lost */
+ service,
+ NULL); /* user data destroy */
return;
}
-typedef struct _hash_table_find_t hash_table_find_t;
-struct _hash_table_find_t {
- GObject * proxy;
- gchar * name;
-};
-
-/* Look in the hash table for the proxy, as it won't give us
- its name. */
-static gboolean
-hash_table_find (gpointer key, gpointer value, gpointer user_data)
-{
- hash_table_find_t * finddata = (hash_table_find_t *)user_data;
- if (value == finddata->proxy) {
- finddata->name = key;
- return TRUE;
- }
- return FALSE;
-}
-
-/* If the proxy gets destroyed that's the same as getting an
- unwatch signal. Make it so. */
+/* When the watcher vanishes we don't really care about it
+ anymore. */
static void
-proxy_destroyed (GObject * proxy, gpointer user_data)
+watcher_vanished_cb (GDBusConnection * connection, const gchar * name, gpointer user_data)
{
g_return_if_fail(INDICATOR_IS_SERVICE(user_data));
IndicatorServicePrivate * priv = INDICATOR_SERVICE_GET_PRIVATE(user_data);
- hash_table_find_t finddata = {0};
- finddata.proxy = proxy;
-
- g_hash_table_find(priv->watchers, hash_table_find, &finddata);
- unwatch_core(INDICATOR_SERVICE(user_data), finddata.name);
+ gpointer finddata = g_hash_table_lookup(priv->watchers, name);
+ if (finddata != NULL) {
+ unwatch_core(INDICATOR_SERVICE(user_data), name);
+ } else {
+ g_warning("Odd, we were watching for '%s' and it disappeard, but then it wasn't in the hashtable.", name);
+ }
return;
}
@@ -448,28 +509,25 @@ proxy_destroyed (GObject * proxy, gpointer user_data)
interface "Watch" function. It is an async function so
that we can get the sender and store that information. We
put them in a list and reset the timeout. */
-static gboolean
-_indicator_service_server_watch (IndicatorService * service, DBusGMethodInvocation * method)
+static GVariant *
+bus_watch (IndicatorService * service, const gchar * sender)
{
- g_return_val_if_fail(INDICATOR_IS_SERVICE(service), FALSE);
+ g_return_val_if_fail(INDICATOR_IS_SERVICE(service), NULL);
IndicatorServicePrivate * priv = INDICATOR_SERVICE_GET_PRIVATE(service);
-
- const gchar * sender = dbus_g_method_get_sender(method);
- if (g_hash_table_lookup(priv->watchers, sender) == NULL) {
- GError * error = NULL;
- DBusGProxy * senderproxy = dbus_g_proxy_new_for_name_owner(priv->bus,
- sender,
- "/",
- DBUS_INTERFACE_INTROSPECTABLE,
- &error);
-
- g_signal_connect(G_OBJECT(senderproxy), "destroy", G_CALLBACK(proxy_destroyed), service);
-
- if (error == NULL) {
- g_hash_table_insert(priv->watchers, g_strdup(sender), senderproxy);
+
+ if (GPOINTER_TO_UINT(g_hash_table_lookup(priv->watchers, sender)) == 0) {
+ guint watch = g_bus_watch_name_on_connection(priv->bus,
+ sender,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ NULL, /* appeared, we dont' care, should have already happened. */
+ watcher_vanished_cb,
+ service,
+ NULL);
+
+ if (watch != 0) {
+ g_hash_table_insert(priv->watchers, g_strdup(sender), GUINT_TO_POINTER(watch));
} else {
- g_warning("Unable to create proxy for watcher '%s': %s", sender, error->message);
- g_error_free(error);
+ g_warning("Unable watch for '%s'", sender);
}
}
@@ -478,22 +536,7 @@ _indicator_service_server_watch (IndicatorService * service, DBusGMethodInvocati
priv->timeout = 0;
}
- dbus_g_method_return(method, INDICATOR_SERVICE_VERSION, priv->this_service_version);
- return TRUE;
-}
-
-/* A function connecting into the dbus interface for the
- "UnWatch" function. It is also an async function to get
- the sender and passes everything to unwatch_core to remove it. */
-static gboolean
-_indicator_service_server_un_watch (IndicatorService * service, DBusGMethodInvocation * method)
-{
- g_return_val_if_fail(INDICATOR_IS_SERVICE(service), FALSE);
-
- unwatch_core(service, dbus_g_method_get_sender(method));
-
- dbus_g_method_return(method);
- return TRUE;
+ return g_variant_new("(uu)", INDICATOR_SERVICE_VERSION, priv->this_service_version);
}
/* Performs the core of loosing a watcher; it removes them