diff options
Diffstat (limited to 'libindicator')
-rw-r--r-- | libindicator/Makefile.am | 8 | ||||
-rw-r--r-- | libindicator/indicator-object-marshal.list | 1 | ||||
-rw-r--r-- | libindicator/indicator-object.c | 54 | ||||
-rw-r--r-- | libindicator/indicator-object.h | 13 | ||||
-rw-r--r-- | libindicator/indicator-service-manager.c | 106 |
5 files changed, 176 insertions, 6 deletions
diff --git a/libindicator/Makefile.am b/libindicator/Makefile.am index 6d5c627..19247ce 100644 --- a/libindicator/Makefile.am +++ b/libindicator/Makefile.am @@ -1,8 +1,11 @@ BUILT_SOURCES = CLEANFILES = +DISTCLEANFILES = EXTRA_DIST = \ indicator.pc.in +include $(top_srcdir)/Makefile.am.marshal + libindicatorincludedir=$(includedir)/libindicator-0.3/libindicator indicator_headers = \ @@ -21,6 +24,8 @@ libindicator_la_SOURCES = \ $(indicator_headers) \ dbus-shared.h \ indicator-object.c \ + indicator-object-marshal.h \ + indicator-object-marshal.c \ indicator-service.c \ indicator-service-manager.c @@ -35,6 +40,9 @@ libindicator_la_LIBADD = \ pkgconfig_DATA = indicator.pc pkgconfigdir = $(libdir)/pkgconfig +glib_marshal_list = indicator-object-marshal.list +glib_marshal_prefix = _indicator_object_marshal + ################################## # DBus Specs ################################## diff --git a/libindicator/indicator-object-marshal.list b/libindicator/indicator-object-marshal.list new file mode 100644 index 0000000..5c11033 --- /dev/null +++ b/libindicator/indicator-object-marshal.list @@ -0,0 +1 @@ +VOID: POINTER, UINT, UINT diff --git a/libindicator/indicator-object.c b/libindicator/indicator-object.c index e87fa5f..87cd648 100644 --- a/libindicator/indicator-object.c +++ b/libindicator/indicator-object.c @@ -27,6 +27,7 @@ License along with this library. If not, see #include "indicator.h" #include "indicator-object.h" +#include "indicator-object-marshal.h" /** IndicatorObjectPrivate: @@ -54,6 +55,7 @@ struct _IndicatorObjectPrivate { enum { ENTRY_ADDED, ENTRY_REMOVED, + ENTRY_MOVED, LAST_SIGNAL }; @@ -86,11 +88,14 @@ indicator_object_class_init (IndicatorObjectClass *klass) klass->get_image = NULL; klass->get_entries = get_entries_default; + klass->get_location = NULL; /** IndicatorObject::entry-added: @arg0: The #IndicatorObject object - + @arg1: A pointer to the #IndicatorObjectEntry that + is being added. + Signaled when a new entry is added and should be shown by the person using this object. */ @@ -105,7 +110,9 @@ indicator_object_class_init (IndicatorObjectClass *klass) /** IndicatorObject::entry-removed: @arg0: The #IndicatorObject object - + @arg1: A pointer to the #IndicatorObjectEntry that + is being removed. + Signaled when an entry is removed and should be removed by the person using this object. */ @@ -116,6 +123,24 @@ indicator_object_class_init (IndicatorObjectClass *klass) NULL, NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER, G_TYPE_NONE); + /** + IndicatorObject::entry-moved: + @arg0: The #IndicatorObject object + @arg1: A pointer to the #IndicatorObjectEntry that + is being moved. + @arg2: The old location of the entry + @arg3: The new location of the entry + + When the order of the entries change, then this signal + is sent to tell the new location. + */ + signals[ENTRY_MOVED] = g_signal_new (INDICATOR_OBJECT_SIGNAL_ENTRY_MOVED, + G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (IndicatorObjectClass, entry_moved), + NULL, NULL, + _indicator_object_marshal_VOID__POINTER_UINT_UINT, + G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_NONE); return; } @@ -342,3 +367,28 @@ indicator_object_get_entries (IndicatorObject * io) g_error("No get_entries function on object. It must have been deleted?!?!"); return NULL; } + +/** + indicator_object_get_location: + @io: #IndicatorObject to query + @entry: The #IndicatorObjectEntry to look for. + + This function looks on the class for the object and calls + it's #IndicatorObjectClass::get_location function. If the + function doesn't exist it returns zero. + + Return value: Location of the @entry in the display or + zero if no location is specified. +*/ +guint +indicator_object_get_location (IndicatorObject * io, IndicatorObjectEntry * entry) +{ + g_return_val_if_fail(INDICATOR_IS_OBJECT(io), 0); + IndicatorObjectClass * class = INDICATOR_OBJECT_GET_CLASS(io); + + if (class->get_location) { + return class->get_location(io, entry); + } + + return 0; +} diff --git a/libindicator/indicator-object.h b/libindicator/indicator-object.h index c100d02..0356d1c 100644 --- a/libindicator/indicator-object.h +++ b/libindicator/indicator-object.h @@ -40,6 +40,8 @@ G_BEGIN_DECLS #define INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED_ID (g_signal_lookup(INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED, INDICATOR_OBJECT_TYPE)) #define INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED "entry-removed" #define INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED_ID (g_signal_lookup(INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, INDICATOR_OBJECT_TYPE)) +#define INDICATOR_OBJECT_SIGNAL_ENTRY_MOVED "entry-moved" +#define INDICATOR_OBJECT_SIGNAL_ENTRY_MOVED_ID (g_signal_lookup(INDICATOR_OBJECT_SIGNAL_ENTRY_MOVED, INDICATOR_OBJECT_TYPE)) typedef struct _IndicatorObject IndicatorObject; typedef struct _IndicatorObjectClass IndicatorObjectClass; @@ -62,12 +64,14 @@ typedef struct _IndicatorObjectEntry IndicatorObjectEntry; a #GList of #IndicatorObjectEntries. The list should be under the ownership of the caller but the entires will not be. + @get_location: Returns the location that a particular entry + should be placed in. This is really only relevant for + indicators that have more than one entry. @entry_added: Slot for #IndicatorObject::entry-added @entry_removed: Slot for #IndicatorObject::entry-removed + @entry_moved: Slot for #IndicatorObject::entry-moved @indicator_object_reserved_1: Reserved for future use @indicator_object_reserved_2: Reserved for future use - @indicator_object_reserved_3: Reserved for future use - @indicator_object_reserved_4: Reserved for future use */ struct _IndicatorObjectClass { GObjectClass parent_class; @@ -78,16 +82,16 @@ struct _IndicatorObjectClass { GtkMenu * (*get_menu) (IndicatorObject * io); GList * (*get_entries) (IndicatorObject * io); + guint (*get_location) (IndicatorObject * io, IndicatorObjectEntry * entry); /* Signals */ void (*entry_added) (IndicatorObject * io, IndicatorObjectEntry * entry, gpointer user_data); void (*entry_removed) (IndicatorObject * io, IndicatorObjectEntry * entry, gpointer user_data); + void (*entry_moved) (IndicatorObject * io, IndicatorObjectEntry * entry, guint old_pos, guint new_pos, gpointer user_data); /* Reserved */ void (* indicator_object_reserved_1) (void); void (* indicator_object_reserved_2) (void); - void (* indicator_object_reserved_3) (void); - void (* indicator_object_reserved_4) (void); }; /** @@ -117,6 +121,7 @@ GType indicator_object_get_type (void); IndicatorObject * indicator_object_new_from_file (const gchar * file); GList * indicator_object_get_entries (IndicatorObject * io); +guint indicator_object_get_location (IndicatorObject * io, IndicatorObjectEntry * entry); G_END_DECLS diff --git a/libindicator/indicator-service-manager.c b/libindicator/indicator-service-manager.c index 69b1343..7bb9a9b 100644 --- a/libindicator/indicator-service-manager.c +++ b/libindicator/indicator-service-manager.c @@ -25,6 +25,8 @@ License along with this library. If not, see #include "config.h" #endif +#include <stdlib.h> + #include <dbus/dbus-glib-bindings.h> #include <dbus/dbus-glib-lowlevel.h> @@ -41,6 +43,7 @@ License along with this library. If not, see @connected: Whether we're connected to the service or not. @this_service_version: The version of the service that we're looking for. @bus: A reference to the bus so we don't have to keep getting it. + @restart_count: The number of times we've restarted this service. */ typedef struct _IndicatorServiceManagerPrivate IndicatorServiceManagerPrivate; struct _IndicatorServiceManagerPrivate { @@ -50,6 +53,8 @@ struct _IndicatorServiceManagerPrivate { gboolean connected; guint this_service_version; DBusGConnection * bus; + guint restart_count; + gint restart_source; }; /* Signals Stuff */ @@ -60,6 +65,13 @@ enum { static guint signals[LAST_SIGNAL] = { 0 }; +/* If this env variable is set, we don't restart */ +#define TIMEOUT_ENV_NAME "INDICATOR_SERVICE_RESTART_DISABLE" +#define TIMEOUT_MULTIPLIER 100 /* In ms */ +/* What to reset the restart_count to if we know that we're + in a recoverable error condition, but waiting a little bit + will probably make things better. 5 ~= 3 sec. */ +#define TIMEOUT_A_LITTLE_WHILE 5 /* Properties */ /* Enum for the properties so that they can be quickly @@ -86,7 +98,9 @@ static void indicator_service_manager_finalize (GObject *object); /* 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 service_proxy_destroyed (DBusGProxy * proxy, gpointer user_data); static void start_service (IndicatorServiceManager * service); +static void start_service_again (IndicatorServiceManager * manager); G_DEFINE_TYPE (IndicatorServiceManager, indicator_service_manager, G_TYPE_OBJECT); @@ -154,6 +168,8 @@ indicator_service_manager_init (IndicatorServiceManager *self) priv->connected = FALSE; priv->this_service_version = 0; priv->bus = NULL; + priv->restart_count = 0; + priv->restart_source = 0; /* Start talkin' dbus */ GError * error = NULL; @@ -187,6 +203,13 @@ indicator_service_manager_dispose (GObject *object) { IndicatorServiceManagerPrivate * priv = INDICATOR_SERVICE_MANAGER_GET_PRIVATE(object); + /* Removing the idle task to restart if it exists. */ + if (priv->restart_source != 0) { + g_source_remove(priv->restart_source); + } + /* Block any restart calls */ + priv->restart_source = -1; + /* If we were connected we need to make sure to tell people that it's no longer the case. */ if (priv->connected) { @@ -309,18 +332,33 @@ watch_cb (DBusGProxy * proxy, guint service_api_version, guint this_service_vers if (error != NULL) { g_warning("Unable to set watch on '%s': '%s'", priv->name, error->message); g_error_free(error); + start_service_again(INDICATOR_SERVICE_MANAGER(user_data)); return; } + /* We've done it, now let's stop counting. */ + /* Note: we're not checking versions. Because, the hope is that + the guy holding the name we want with the wrong version will + drop and we can start another service quickly. */ + priv->restart_count = 0; + if (service_api_version != INDICATOR_SERVICE_VERSION) { g_warning("Service is using a different version of the service interface. Expecting %d and got %d.", INDICATOR_SERVICE_VERSION, service_api_version); dbus_g_proxy_call_no_reply(priv->service_proxy, "UnWatch", G_TYPE_INVALID); + + /* Let's make us wait a little while, then try again */ + priv->restart_count = TIMEOUT_A_LITTLE_WHILE; + start_service_again(INDICATOR_SERVICE_MANAGER(user_data)); return; } if (this_service_version != priv->this_service_version) { g_warning("Service is using a different API version than the manager. Expecting %d and got %d.", priv->this_service_version, this_service_version); dbus_g_proxy_call_no_reply(priv->service_proxy, "UnWatch", G_TYPE_INVALID); + + /* Let's make us wait a little while, then try again */ + priv->restart_count = TIMEOUT_A_LITTLE_WHILE; + start_service_again(INDICATOR_SERVICE_MANAGER(user_data)); return; } @@ -343,11 +381,13 @@ start_service_cb (DBusGProxy * proxy, guint status, GError * error, gpointer use if (error != NULL) { g_warning("Unable to start service '%s': %s", priv->name, error->message); + start_service_again(INDICATOR_SERVICE_MANAGER(user_data)); return; } if (status != DBUS_START_REPLY_SUCCESS && status != DBUS_START_REPLY_ALREADY_RUNNING) { g_warning("Status of starting the process '%s' was an error: %d", priv->name, status); + start_service_again(INDICATOR_SERVICE_MANAGER(user_data)); return; } @@ -358,6 +398,7 @@ start_service_cb (DBusGProxy * proxy, guint status, GError * error, gpointer use INDICATOR_SERVICE_INTERFACE, &error); g_object_add_weak_pointer(G_OBJECT(priv->service_proxy), (gpointer *)&(priv->service_proxy)); + g_signal_connect(G_OBJECT(priv->service_proxy), "destroy", G_CALLBACK(service_proxy_destroyed), user_data); org_ayatana_indicator_service_watch_async(priv->service_proxy, watch_cb, @@ -379,6 +420,11 @@ start_service (IndicatorServiceManager * service) g_return_if_fail(priv->dbus_proxy != NULL); g_return_if_fail(priv->name != NULL); + if (priv->service_proxy != NULL) { + g_object_unref(priv->service_proxy); + priv->service_proxy = NULL; + } + /* Check to see if we can get a proxy to it first. */ priv->service_proxy = dbus_g_proxy_new_for_name_owner(priv->bus, priv->name, @@ -396,6 +442,7 @@ start_service (IndicatorServiceManager * service) service); } else { g_object_add_weak_pointer(G_OBJECT(priv->service_proxy), (gpointer *)&(priv->service_proxy)); + g_signal_connect(G_OBJECT(priv->service_proxy), "destroy", G_CALLBACK(service_proxy_destroyed), service); /* If we got a proxy just because we're good people then we need to call watch on it just like 'start_service_cb' @@ -408,6 +455,65 @@ start_service (IndicatorServiceManager * service) return; } +/* Responds to the destory event of the proxy and starts + setting up to restart the service. */ +static void +service_proxy_destroyed (DBusGProxy * proxy, gpointer user_data) +{ + IndicatorServiceManagerPrivate * priv = INDICATOR_SERVICE_MANAGER_GET_PRIVATE(user_data); + if (priv->connected) { + priv->connected = FALSE; + g_signal_emit(G_OBJECT(user_data), signals[CONNECTION_CHANGE], 0, FALSE, TRUE); + } + return start_service_again(INDICATOR_SERVICE_MANAGER(user_data)); +} + +/* The callback that starts the service for real after + the timeout as determined in 'start_service_again'. + This could be in the idle or a timer. */ +static gboolean +start_service_again_cb (gpointer data) +{ + IndicatorServiceManagerPrivate * priv = INDICATOR_SERVICE_MANAGER_GET_PRIVATE(data); + priv->restart_count++; + start_service(INDICATOR_SERVICE_MANAGER(data)); + priv->restart_source = 0; + return FALSE; +} + +/* This function tries to start a new service, perhaps + after a timeout that it determines. The real issue + here is that it throttles restarting if we're not + being successful. */ +static void +start_service_again (IndicatorServiceManager * manager) +{ + IndicatorServiceManagerPrivate * priv = INDICATOR_SERVICE_MANAGER_GET_PRIVATE(manager); + + /* If we've already got a restart source running then + let's not do this again. */ + if (priv->restart_source != 0) { + return; + } + + /* Allow the restarting to be disabled */ + if (g_getenv(TIMEOUT_ENV_NAME)) { + return; + } + + if (priv->restart_count == 0) { + /* First time, do it in idle */ + g_idle_add(start_service_again_cb, manager); + } else { + /* Not our first time 'round the block. Let's slow this down. */ + if (priv->restart_count > 16) + priv->restart_count = 16; /* Not more than 1024x */ + priv->restart_source = g_timeout_add((1 << priv->restart_count) * TIMEOUT_MULTIPLIER, start_service_again_cb, manager); + } + + return; +} + /* API */ /** |