#ifdef HAVE_CONFIG_H #include "config.h" #endif #include <dbus/dbus-glib-bindings.h> #include <dbus/dbus-glib-lowlevel.h> #include "indicator-service.h" /* DBus Prototypes */ static gboolean _indicator_service_server_watch (IndicatorService * service, DBusGMethodInvocation * method); #include "indicator-service-server.h" #include "dbus-shared.h" /* Private Stuff */ typedef struct _IndicatorServicePrivate IndicatorServicePrivate; struct _IndicatorServicePrivate { gchar * name; DBusGProxy * dbus_proxy; guint timeout; GList * watchers; }; /* Signals Stuff */ enum { SHUTDOWN, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; /* Properties */ /* Enum for the properties so that they can be quickly found and looked up. */ enum { PROP_0, PROP_NAME, }; /* The strings so that they can be slowly looked up. */ #define PROP_NAME_S "name" /* GObject Stuff */ #define INDICATOR_SERVICE_GET_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), INDICATOR_SERVICE_TYPE, IndicatorServicePrivate)) static void indicator_service_class_init (IndicatorServiceClass *klass); static void indicator_service_init (IndicatorService *self); static void indicator_service_dispose (GObject *object); static void indicator_service_finalize (GObject *object); /* Other prototypes */ 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); G_DEFINE_TYPE (IndicatorService, indicator_service, G_TYPE_OBJECT); static void indicator_service_class_init (IndicatorServiceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (klass, sizeof (IndicatorServicePrivate)); object_class->dispose = indicator_service_dispose; object_class->finalize = indicator_service_finalize; /* Property funcs */ object_class->set_property = set_property; object_class->get_property = get_property; /* Properties */ g_object_class_install_property(object_class, PROP_NAME, g_param_spec_string(PROP_NAME_S, "The DBus name for this service", "This is the name that should be used on DBus for this service.", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); /* Signals */ /** IndicatorService::shutdown: @arg0: The #IndicatorService object Signaled when the service should shutdown as no one is listening anymore. */ signals[SHUTDOWN] = g_signal_new (INDICATOR_SERVICE_SIGNAL_SHUTDOWN, G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (IndicatorServiceClass, shutdown), NULL, NULL, 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); return; } static void indicator_service_init (IndicatorService *self) { IndicatorServicePrivate * priv = INDICATOR_SERVICE_GET_PRIVATE(self); /* Get the private variables in a decent state */ priv->name = NULL; priv->dbus_proxy = NULL; priv->timeout = 0; priv->watchers = NULL; /* Start talkin' dbus */ GError * error = NULL; DBusGConnection * 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; 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(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; } dbus_g_connection_register_g_object(bus, INDICATOR_SERVICE_OBJECT, G_OBJECT(self)); return; } static void indicator_service_dispose (GObject *object) { IndicatorServicePrivate * priv = INDICATOR_SERVICE_GET_PRIVATE(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; } G_OBJECT_CLASS (indicator_service_parent_class)->dispose (object); return; } static void indicator_service_finalize (GObject *object) { IndicatorServicePrivate * priv = INDICATOR_SERVICE_GET_PRIVATE(object); if (priv->name != NULL) { g_free(priv->name); } if (priv->watchers != NULL) { g_list_foreach(priv->watchers, (GFunc)g_free, NULL); g_list_free(priv->watchers); priv->watchers = NULL; } G_OBJECT_CLASS (indicator_service_parent_class)->finalize (object); return; } static void set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { IndicatorService * self = INDICATOR_SERVICE(object); g_return_if_fail(self != NULL); IndicatorServicePrivate * priv = INDICATOR_SERVICE_GET_PRIVATE(self); g_return_if_fail(priv != NULL); switch (prop_id) { /* *********************** */ case PROP_NAME: if (G_VALUE_HOLDS_STRING(value)) { if (priv->name != NULL) { g_error("Name can not be set twice!"); return; } priv->name = g_value_dup_string(value); try_and_get_name(self); } else { g_warning("Name property requires a string value."); } break; /* *********************** */ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } return; } static void get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { IndicatorService * self = INDICATOR_SERVICE(object); g_return_if_fail(self != NULL); IndicatorServicePrivate * priv = INDICATOR_SERVICE_GET_PRIVATE(self); g_return_if_fail(priv != NULL); switch (prop_id) { /* *********************** */ case PROP_NAME: if (G_VALUE_HOLDS_STRING(value)) { g_value_set_string(value, priv->name); } else { g_warning("Name property requires a string value."); } break; /* *********************** */ default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } return; } static gboolean timeout_no_watchers (gpointer data) { g_signal_emit(G_OBJECT(data), signals[SHUTDOWN], 0, TRUE); return FALSE; } static void try_and_get_name_cb (DBusGProxy * proxy, guint status, GError * error, gpointer data) { IndicatorService * service = INDICATOR_SERVICE(data); g_return_if_fail(service != NULL); 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_signal_emit(G_OBJECT(data), signals[SHUTDOWN], 0, TRUE); return; } IndicatorServicePrivate * priv = INDICATOR_SERVICE_GET_PRIVATE(service); priv->timeout = g_timeout_add(500, timeout_no_watchers, service); return; } 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); return; } static gboolean _indicator_service_server_watch (IndicatorService * service, DBusGMethodInvocation * method) { g_return_val_if_fail(INDICATOR_IS_SERVICE(service), FALSE); IndicatorServicePrivate * priv = INDICATOR_SERVICE_GET_PRIVATE(service); priv->watchers = g_list_append(priv->watchers, g_strdup(dbus_g_method_get_sender(method))); if (priv->timeout != 0) { g_source_remove(priv->timeout); priv->timeout = 0; } dbus_g_method_return(method, 1); return TRUE; } /* API */ /** indicator_service_new: @name: The name for the service on dbus This function creates the service on DBus and tries to get a well-known name specified in @name. If the name can't be estabilished then the #IndicatorService::shutdown signal will be sent. Return value: A brand new #IndicatorService object or #NULL if there is an error. */ IndicatorService * indicator_service_new (gchar * name) { GObject * obj = g_object_new(INDICATOR_SERVICE_TYPE, PROP_NAME_S, name, NULL); return INDICATOR_SERVICE(obj); }