mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-02-23 10:42:11 +01:00
Add GAppInfoMonitor
This is a simple object that emits a "change" signal when the installed applications may have changed in some way.
This commit is contained in:
parent
52552e4231
commit
02dab68174
192
gio/gappinfo.c
192
gio/gappinfo.c
@ -21,7 +21,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "gappinfo.h"
|
#include "gappinfo.h"
|
||||||
|
#include "gappinfoprivate.h"
|
||||||
|
|
||||||
#include "glibintl.h"
|
#include "glibintl.h"
|
||||||
#include <gioerror.h>
|
#include <gioerror.h>
|
||||||
#include <gfile.h>
|
#include <gfile.h>
|
||||||
@ -1025,3 +1028,192 @@ g_app_launch_context_launch_failed (GAppLaunchContext *context,
|
|||||||
|
|
||||||
g_signal_emit (context, signals[LAUNCH_FAILED], 0, startup_notify_id);
|
g_signal_emit (context, signals[LAUNCH_FAILED], 0, startup_notify_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* GAppInfoMonitor:
|
||||||
|
*
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
@ -296,6 +296,20 @@ GLIB_AVAILABLE_IN_ALL
|
|||||||
void g_app_launch_context_launch_failed (GAppLaunchContext *context,
|
void g_app_launch_context_launch_failed (GAppLaunchContext *context,
|
||||||
const char * startup_notify_id);
|
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
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __G_APP_INFO_H__ */
|
#endif /* __G_APP_INFO_H__ */
|
||||||
|
28
gio/gappinfoprivate.h
Normal file
28
gio/gappinfoprivate.h
Normal file
@ -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__ */
|
@ -48,6 +48,7 @@
|
|||||||
#include "glibintl.h"
|
#include "glibintl.h"
|
||||||
#include "giomodule-priv.h"
|
#include "giomodule-priv.h"
|
||||||
#include "gappinfo.h"
|
#include "gappinfo.h"
|
||||||
|
#include "gappinfoprivate.h"
|
||||||
#include "glocaldirectorymonitor.h"
|
#include "glocaldirectorymonitor.h"
|
||||||
|
|
||||||
#include "dfi-reader.h"
|
#include "dfi-reader.h"
|
||||||
@ -330,6 +331,9 @@ desktop_file_dir_changed (GFileMonitor *monitor,
|
|||||||
desktop_file_dir_reset (dir);
|
desktop_file_dir_reset (dir);
|
||||||
|
|
||||||
g_mutex_unlock (&desktop_file_dir_lock);
|
g_mutex_unlock (&desktop_file_dir_lock);
|
||||||
|
|
||||||
|
/* Notify anyone else who may be interested */
|
||||||
|
g_app_info_monitor_fire ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Internal utility functions {{{2 */
|
/* Internal utility functions {{{2 */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user