diff options
author | Ryan Lortie <desrt@desrt.ca> | 2013-09-25 09:19:43 -0400 |
---|---|---|
committer | Ryan Lortie <desrt@desrt.ca> | 2013-11-06 11:26:53 -0500 |
commit | e55d33edc1336ddc6d5cdfa0e3003a69a5812d26 (patch) | |
tree | 6d17890b632ece9fa3ad60f64ee325d144095996 | |
parent | 86ce3bf48e40756a360b13e18493a15d8d1bf5ae (diff) | |
download | glib-e55d33edc1336ddc6d5cdfa0e3003a69a5812d26.tar.gz |
Add GAppInfoMonitor
This is a simple object that emits a "change" signal when the installed
applications may have changed in some way.
https://bugzilla.gnome.org/show_bug.cgi?id=711556
-rw-r--r-- | docs/reference/gio/gio-docs.xml | 1 | ||||
-rw-r--r-- | docs/reference/gio/gio-sections.txt | 13 | ||||
-rw-r--r-- | docs/reference/gio/gio.types | 1 | ||||
-rw-r--r-- | gio/gappinfo.c | 225 | ||||
-rw-r--r-- | gio/gappinfo.h | 14 | ||||
-rw-r--r-- | gio/gappinfoprivate.h | 28 | ||||
-rw-r--r-- | gio/gdesktopappinfo.c | 4 |
7 files changed, 286 insertions, 0 deletions
diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml index 1d65a1176..46b8bb8dd 100644 --- a/docs/reference/gio/gio-docs.xml +++ b/docs/reference/gio/gio-docs.xml @@ -83,6 +83,7 @@ <title>File types and applications</title> <xi:include href="xml/gcontenttype.xml"/> <xi:include href="xml/gappinfo.xml"/> + <xi:include href="xml/gappinfomonitor.xml"/> <xi:include href="xml/gdesktopappinfo.xml"/> </chapter> <chapter id="volume_mon"> diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 67afe3ef9..22dc8fdf7 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -1399,6 +1399,19 @@ GAppLaunchContextPrivate </SECTION> <SECTION> +<FILE>gappinfomonitor</FILE> +<TITLE>GAppInfoMonitor</TITLE> +GAppInfoMonitor +g_app_info_monitor_get +<SUBSECTION Standard> +GAppInfoMonitor +G_APP_INFO_MONITOR +G_IS_APP_INFO_MONITOR +<SUBSECTION Private> +g_app_info_monitor_get_type +</SECTION> + +<SECTION> <FILE>gmountoperation</FILE> <TITLE>GMountOperation</TITLE> GAskPasswordFlags diff --git a/docs/reference/gio/gio.types b/docs/reference/gio/gio.types index 73e09a22a..9d4390568 100644 --- a/docs/reference/gio/gio.types +++ b/docs/reference/gio/gio.types @@ -4,6 +4,7 @@ g_action_group_get_type g_action_map_get_type g_simple_action_group_get_type g_app_info_get_type +g_app_info_monitor_get_type g_app_launch_context_get_type g_application_get_type g_async_initable_get_type diff --git a/gio/gappinfo.c b/gio/gappinfo.c index 2d0b58cb2..941015c0d 100644 --- a/gio/gappinfo.c +++ b/gio/gappinfo.c @@ -21,7 +21,10 @@ */ #include "config.h" + #include "gappinfo.h" +#include "gappinfoprivate.h" + #include "glibintl.h" #include <gioerror.h> #include <gfile.h> @@ -31,6 +34,7 @@ * SECTION:gappinfo * @short_description: Application information and launch contexts * @include: gio/gio.h + * @see_also: #GAppInfoMonitor * * #GAppInfo and #GAppLaunchContext are used for describing and launching * applications installed on the system. @@ -1025,3 +1029,224 @@ g_app_launch_context_launch_failed (GAppLaunchContext *context, g_signal_emit (context, signals[LAUNCH_FAILED], 0, startup_notify_id); } + + +/** + * SECTION:gappinfomonitor + * @short_description: Monitor application information for changes + * + * #GAppInfoMonitor is a very simple object used for monitoring the app + * info database for changes (ie: newly installed or removed + * applications). + * + * Call g_app_info_monitor_get() to get a #GAppInfoMonitor and connect + * to the "changed" signal. + * + * In the usual case, applications should try to make note of the change + * (doing things like invalidating caches) but not act on it. In + * particular, applications should avoid making calls to #GAppInfo APIs + * in response to the change signal, deferring these until the time that + * the data is actually required. The exception to this case is when + * application information is actually being displayed on the screen + * (eg: during a search or when the list of all applications is shown). + * The reason for this is that changes to the list of installed + * applications often come in groups (like during system updates) and + * rescanning the list on every change is pointless and expensive. + * + * Since: 2.40 + **/ + +/** + * GAppInfoMonitor: + * + * The only thing you can do with this is to get it via + * g_app_info_monitor_get() and connect to the "changed" signal. + * + * Since: 2.40 + **/ + +/* We have one of each of these per main context and hand them out + * according to the thread default main context at the time of the call + * to g_app_info_monitor_get(). + * + * g_object_unref() is only ever called from the same context, so we + * effectively have a single-threaded scenario for each GAppInfoMonitor. + * + * We use a hashtable to cache the per-context monitor (but we do not + * hold a ref). During finalize, we remove it. This is possible + * because we don't have to worry about the usual races due to the + * single-threaded nature of each object. + * + * We keep a global list of all contexts that have a monitor for them, + * which we have to access under a lock. When we dispatch the events to + * be handled in each context, we don't pass the monitor, but the + * context itself. + * + * We dispatch from the GLib worker context, so if we passed the + * monitor, we would need to take a ref on it (in case it was destroyed + * in its own thread meanwhile). The monitor holds a ref on a context + * and the dispatch would mean that the context would hold a ref on the + * monitor. If someone stopped iterating the context at just this + * moment both the context and monitor would leak. + * + * Instead, we dispatch the context to itself. We don't hold a ref. + * There is the danger that the context will be destroyed during the + * dispatch, but if that is the case then we just won't receive our + * callback. + * + * When the dispatch occurs we just lookup the monitor in the hashtable, + * by context. We can now add and remove refs, since the context will + * have been acquired. + */ + +typedef struct _GAppInfoMonitorClass GAppInfoMonitorClass; + +struct _GAppInfoMonitor +{ + GObject parent_instance; + GMainContext *context; +}; + +struct _GAppInfoMonitorClass +{ + GObjectClass parent_class; +}; + +static GHashTable *g_app_info_monitors; +static GMutex g_app_info_monitor_lock; +static guint g_app_info_monitor_changed_signal; + +G_DEFINE_TYPE (GAppInfoMonitor, g_app_info_monitor, G_TYPE_OBJECT) + +static void +g_app_info_monitor_finalize (GObject *object) +{ + GAppInfoMonitor *monitor = G_APP_INFO_MONITOR (object); + + g_mutex_lock (&g_app_info_monitor_lock); + g_hash_table_remove (g_app_info_monitors, monitor->context); + g_mutex_unlock (&g_app_info_monitor_lock); + + G_OBJECT_CLASS (g_app_info_monitor_parent_class)->finalize (object); +} + +static void +g_app_info_monitor_init (GAppInfoMonitor *monitor) +{ +} + +static void +g_app_info_monitor_class_init (GAppInfoMonitorClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + g_app_info_monitor_changed_signal = g_signal_new ("changed", G_TYPE_APP_INFO_MONITOR, G_SIGNAL_RUN_FIRST, + 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + + object_class->finalize = g_app_info_monitor_finalize; +} + +/** + * g_app_info_monitor_get: + * + * Gets the #GAppInfoMonitor for the current thread-default main + * context. + * + * The #GAppInfoMonitor will emit a "changed" signal in the + * thread-default main context whenever the list of installed + * applications (as reported by g_app_info_get_all()) may have changed. + * + * You must only call g_object_unref() on the return value from under + * the same main context as you created it. + * + * Returns: (transfer full): a reference to a #GAppInfoMonitor + * + * Since: 2.40 + **/ +GAppInfoMonitor * +g_app_info_monitor_get (void) +{ + GAppInfoMonitor *monitor; + GMainContext *context; + + context = g_main_context_get_thread_default (); + if (!context) + context = g_main_context_default (); + + g_return_val_if_fail (g_main_context_acquire (context), NULL); + + g_mutex_lock (&g_app_info_monitor_lock); + if (!g_app_info_monitors) + g_app_info_monitors = g_hash_table_new (NULL, NULL); + + monitor = g_hash_table_lookup (g_app_info_monitors, context); + g_mutex_unlock (&g_app_info_monitor_lock); + + if (!monitor) + { + monitor = g_object_new (G_TYPE_APP_INFO_MONITOR, NULL); + monitor->context = g_main_context_ref (context); + + g_mutex_lock (&g_app_info_monitor_lock); + g_hash_table_insert (g_app_info_monitors, context, monitor); + g_mutex_unlock (&g_app_info_monitor_lock); + } + else + g_object_ref (monitor); + + g_main_context_release (context); + + return monitor; +} + +static gboolean +g_app_info_monitor_emit (gpointer user_data) +{ + GMainContext *context = user_data; + GAppInfoMonitor *monitor; + + g_mutex_lock (&g_app_info_monitor_lock); + monitor = g_hash_table_lookup (g_app_info_monitors, context); + g_mutex_unlock (&g_app_info_monitor_lock); + + /* It is possible that the monitor was already destroyed by the time + * we get here, so make sure it's not NULL. + */ + if (monitor != NULL) + { + /* We don't have to worry about another thread disposing the + * monitor but we do have to worry about the possibility that one + * of the attached handlers may do so. + * + * Take a ref so that the monitor doesn't disappear in the middle + * of the emission. + */ + g_object_ref (monitor); + g_signal_emit (monitor, g_app_info_monitor_changed_signal, 0); + g_object_unref (monitor); + } + + return FALSE; +} + +void +g_app_info_monitor_fire (void) +{ + GHashTableIter iter; + gpointer context; + + g_mutex_lock (&g_app_info_monitor_lock); + + g_hash_table_iter_init (&iter, g_app_info_monitors); + while (g_hash_table_iter_next (&iter, &context, NULL)) + { + GSource *idle; + + idle = g_idle_source_new (); + g_source_set_callback (idle, g_app_info_monitor_emit, context, NULL); + g_source_attach (idle, context); + g_source_unref (idle); + } + + g_mutex_unlock (&g_app_info_monitor_lock); +} diff --git a/gio/gappinfo.h b/gio/gappinfo.h index baed4c405..8b793cefb 100644 --- a/gio/gappinfo.h +++ b/gio/gappinfo.h @@ -296,6 +296,20 @@ GLIB_AVAILABLE_IN_ALL void g_app_launch_context_launch_failed (GAppLaunchContext *context, const char * startup_notify_id); +#define G_TYPE_APP_INFO_MONITOR (g_app_info_monitor_get_type ()) +#define G_APP_INFO_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ + G_TYPE_APP_INFO_MONITOR, GAppInfoMonitor)) +#define G_IS_APP_INFO_MONITOR(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ + G_TYPE_APP_INFO_MONITOR)) + +typedef struct _GAppInfoMonitor GAppInfoMonitor; + +GLIB_AVAILABLE_IN_2_40 +GType g_app_info_monitor_get_type (void); + +GLIB_AVAILABLE_IN_2_40 +GAppInfoMonitor * g_app_info_monitor_get (void); + G_END_DECLS #endif /* __G_APP_INFO_H__ */ diff --git a/gio/gappinfoprivate.h b/gio/gappinfoprivate.h new file mode 100644 index 000000000..c9879777c --- /dev/null +++ b/gio/gappinfoprivate.h @@ -0,0 +1,28 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright © 2013 Canonical Limited + * + * 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: Ryan Lortie <desrt@desrt.ca> + */ + +#ifndef __G_APP_INFO_PRIVATE_H__ +#define __G_APP_INFO_PRIVATE_H__ + +void g_app_info_monitor_fire (void); + +#endif /* __G_APP_INFO_PRIVATE_H__ */ diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c index c67186fe3..a76369c77 100644 --- a/gio/gdesktopappinfo.c +++ b/gio/gdesktopappinfo.c @@ -47,6 +47,7 @@ #include "glibintl.h" #include "giomodule-priv.h" #include "gappinfo.h" +#include "gappinfoprivate.h" #include "glocaldirectorymonitor.h" @@ -179,6 +180,9 @@ desktop_file_dir_changed (GFileMonitor *monitor, desktop_file_dir_reset (dir); g_mutex_unlock (&desktop_file_dir_lock); + + /* Notify anyone else who may be interested */ + g_app_info_monitor_fire (); } /* Internal utility functions {{{2 */ |