gtk+ r20004 - in trunk: . gtk
- From: ebassi svn gnome org
- To: svn-commits-list gnome org
- Subject: gtk+ r20004 - in trunk: . gtk
- Date: Tue, 15 Apr 2008 23:22:44 +0100 (BST)
Author: ebassi
Date: Tue Apr 15 23:22:44 2008
New Revision: 20004
URL: http://svn.gnome.org/viewvc/gtk+?rev=20004&view=rev
Log:
2008-04-15 Emmanuele Bassi <ebassi gnome org>
Bug 487375 â gtkrecent apps poll ~/.recently-used.xbel
every 5 seconds
* gtk/gtkrecentmanager.c: Use GFileMonitor to check for
changes in the recently used resources storage file, and
drop the timeout-based poll.
* ChangeLog: Fix attribution of the patch of the previous
commit.
Modified:
trunk/ChangeLog
trunk/gtk/gtkrecentmanager.c
Modified: trunk/gtk/gtkrecentmanager.c
==============================================================================
--- trunk/gtk/gtkrecentmanager.c (original)
+++ trunk/gtk/gtkrecentmanager.c Tue Apr 15 23:22:44 2008
@@ -49,9 +49,6 @@
/* the file where we store the recently used items */
#define GTK_RECENTLY_USED_FILE ".recently-used.xbel"
-/* a poll approximately every five seconds */
-#define POLL_DELTA 5
-
/* return all items by default */
#define DEFAULT_LIMIT -1
@@ -60,13 +57,6 @@
typedef struct
{
- GSourceFunc func;
- gpointer data;
- GDestroyNotify notify;
-} ThreadsDispatch;
-
-typedef struct
-{
gchar *name;
gchar *exec;
@@ -100,21 +90,20 @@
gint ref_count;
};
+#define GTK_RECENT_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_RECENT_MANAGER, GtkRecentManagerPrivate))
+
struct _GtkRecentManagerPrivate
{
gchar *filename;
guint is_dirty : 1;
- guint write_in_progress : 1;
- guint read_in_progress : 1;
gint limit;
gint size;
GBookmarkFile *recent_items;
-
- time_t last_mtime;
- guint poll_timeout;
+
+ GFileMonitor *monitor;
};
enum
@@ -126,36 +115,38 @@
PROP_SIZE
};
-static void gtk_recent_manager_dispose (GObject *object);
-static void gtk_recent_manager_finalize (GObject *object);
+static void gtk_recent_manager_dispose (GObject *object);
+static void gtk_recent_manager_finalize (GObject *object);
+static void gtk_recent_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gtk_recent_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gtk_recent_manager_add_item_query_info_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data);
+static void gtk_recent_manager_monitor_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data);
+static void gtk_recent_manager_changed (GtkRecentManager *manager);
+static void gtk_recent_manager_real_changed (GtkRecentManager *manager);
+static void gtk_recent_manager_set_filename (GtkRecentManager *manager,
+ const gchar *filename);
+
+static void build_recent_items_list (GtkRecentManager *manager);
+static void purge_recent_items_list (GtkRecentManager *manager,
+ GError **error);
-static void gtk_recent_manager_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec);
-static void gtk_recent_manager_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec);
-static void gtk_recent_manager_add_item_query_info_cb (GObject *source_object,
- GAsyncResult *res,
- gpointer user_data);
-static void gtk_recent_manager_changed (GtkRecentManager *manager);
-
-static void gtk_recent_manager_real_changed (GtkRecentManager *manager);
-static gboolean gtk_recent_manager_poll_timeout (gpointer data);
-static void gtk_recent_manager_set_filename (GtkRecentManager *manager,
- const gchar *filename);
-
-static void build_recent_items_list (GtkRecentManager *manager);
-static void purge_recent_items_list (GtkRecentManager *manager,
- GError **error);
+static RecentAppInfo *recent_app_info_new (const gchar *app_name);
+static void recent_app_info_free (RecentAppInfo *app_info);
-static RecentAppInfo *recent_app_info_new (const gchar *app_name);
-static void recent_app_info_free (RecentAppInfo *app_info);
-
-static GtkRecentInfo *gtk_recent_info_new (const gchar *uri);
-static void gtk_recent_info_free (GtkRecentInfo *recent_info);
+static GtkRecentInfo *gtk_recent_info_new (const gchar *uri);
+static void gtk_recent_info_free (GtkRecentInfo *recent_info);
static guint signal_changed = 0;
@@ -201,40 +192,11 @@
return g_quark_from_static_string ("gtk-recent-manager-error-quark");
}
-static gboolean
-threads_dispatch (gpointer data)
-{
- ThreadsDispatch *dispatch = data;
- gboolean res = FALSE;
-
- GDK_THREADS_ENTER ();
-
- if (!g_source_is_destroyed (g_main_current_source ()))
- res = dispatch->func (dispatch->data);
-
- GDK_THREADS_LEAVE ();
-
- return res;
-}
-
-static void
-threads_free (gpointer data)
-{
- ThreadsDispatch *dispatch = data;
-
- if (dispatch->notify)
- dispatch->notify (dispatch->data);
-
- g_slice_free (ThreadsDispatch, dispatch);
-}
-
static void
gtk_recent_manager_class_init (GtkRecentManagerClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- gtk_recent_manager_parent_class = g_type_class_peek_parent (klass);
-
gobject_class->set_property = gtk_recent_manager_set_property;
gobject_class->get_property = gtk_recent_manager_get_property;
gobject_class->dispose = gtk_recent_manager_dispose;
@@ -254,7 +216,7 @@
P_("Filename"),
P_("The full path to the file to be used to store and read the list"),
NULL,
- (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READABLE | G_PARAM_WRITABLE)));
+ (G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)));
/**
* GtkRecentManager:limit
*
@@ -316,34 +278,19 @@
gtk_recent_manager_init (GtkRecentManager *manager)
{
GtkRecentManagerPrivate *priv;
- ThreadsDispatch *dispatch;
-
- priv = g_type_instance_get_private ((GTypeInstance *) manager,
- GTK_TYPE_RECENT_MANAGER);
- manager->priv = priv;
+ gchar *filename;
+
+ manager->priv = priv = GTK_RECENT_MANAGER_GET_PRIVATE (manager);
priv->limit = DEFAULT_LIMIT;
priv->size = 0;
-
- priv->is_dirty = FALSE;
- priv->write_in_progress = FALSE;
- priv->read_in_progress = FALSE;
- priv->filename = g_build_filename (g_get_home_dir (),
- GTK_RECENTLY_USED_FILE,
- NULL);
-
- dispatch = g_slice_new (ThreadsDispatch);
- dispatch->func = gtk_recent_manager_poll_timeout;
- dispatch->data = manager;
- dispatch->notify = NULL;
- priv->poll_timeout = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT + 30,
- POLL_DELTA,
- threads_dispatch,
- dispatch,
- threads_free);
-
- build_recent_items_list (manager);
+ /* this will take care of building the files list */
+ filename = g_build_filename (g_get_home_dir (),
+ GTK_RECENTLY_USED_FILE,
+ NULL);
+ gtk_recent_manager_set_filename (manager, filename);
+ g_free (filename);
}
static void
@@ -399,10 +346,13 @@
GtkRecentManager *manager = GTK_RECENT_MANAGER (object);
GtkRecentManagerPrivate *priv = manager->priv;
- if (priv->poll_timeout)
+ if (priv->monitor)
{
- g_source_remove (priv->poll_timeout);
- priv->poll_timeout = 0;
+ g_signal_handlers_disconnect_by_func (priv->monitor,
+ G_CALLBACK (gtk_recent_manager_monitor_changed),
+ manager);
+ g_object_unref (priv->monitor);
+ priv->monitor = NULL;
}
G_OBJECT_CLASS (gtk_recent_manager_parent_class)->dispose (object);
@@ -432,15 +382,12 @@
if (priv->is_dirty)
{
GError *write_error;
- struct stat stat_buf;
/* we are marked as dirty, so we dump the content of our
* recently used items list
*/
g_assert (priv->filename != NULL);
- priv->write_in_progress = TRUE;
-
/* if no container object has been defined, we create a new
* empty container, and dump it
*/
@@ -461,26 +408,6 @@
g_error_free (write_error);
}
- priv->write_in_progress = FALSE;
-
- /* we have sync'ed our list with the storage file, so we
- * update the file mtime in order to skip the timed check
- * and spare us from a re-read.
- */
- if (g_stat (priv->filename, &stat_buf) < 0)
- {
- filename_warning ("Unable to stat() the recently used resources file "
- "at `%s': %s.",
- priv->filename,
- g_strerror (errno));
-
- g_object_thaw_notify (G_OBJECT (manager));
-
- return;
- }
-
- priv->last_mtime = stat_buf.st_mtime;
-
/* mark us as clean */
priv->is_dirty = FALSE;
}
@@ -496,44 +423,28 @@
g_object_thaw_notify (G_OBJECT (manager));
}
-/* timed poll()-ing of the recently used resources file.
- * an event-based system would be more efficient.
- */
-static gboolean
-gtk_recent_manager_poll_timeout (gpointer data)
+static void
+gtk_recent_manager_monitor_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data)
{
- GtkRecentManager *manager = GTK_RECENT_MANAGER (data);
- GtkRecentManagerPrivate *priv = manager->priv;
- struct stat stat_buf;
- int stat_res;
-
- /* wait for the next timeout if we have a read/write in progress */
- if (priv->write_in_progress || priv->read_in_progress)
- return TRUE;
+ GtkRecentManager *manager = user_data;
- stat_res = g_stat (priv->filename, &stat_buf);
- if (stat_res < 0)
+ switch (event_type)
{
- /* the file does not exist, yet, so we wait */
- if (errno == ENOENT)
- return TRUE;
-
- filename_warning ("Unable to stat() the recently used resources file "
- "at `%s': %s.",
- priv->filename,
- g_strerror (errno));
-
- return TRUE;
- }
-
- /* the file didn't change from the last poll, so we bail out */
- if (stat_buf.st_mtime == priv->last_mtime)
- return TRUE;
+ case G_FILE_MONITOR_EVENT_CHANGED:
+ case G_FILE_MONITOR_EVENT_CREATED:
+ gtk_recent_manager_changed (manager);
+ break;
- /* the file has been changed, hence we emit the "changed" signal */
- gtk_recent_manager_changed (manager);
+ case G_FILE_MONITOR_EVENT_DELETED:
+ break;
- return TRUE;
+ default:
+ break;
+ }
}
static void
@@ -541,37 +452,48 @@
const gchar *filename)
{
GtkRecentManagerPrivate *priv;
- ThreadsDispatch *dispatch;
+ GFile *file;
+ GError *error;
g_assert (GTK_IS_RECENT_MANAGER (manager));
+
priv = manager->priv;
+ g_free (priv->filename);
+
+ if (priv->monitor)
+ {
+ g_signal_handlers_disconnect_by_func (priv->monitor,
+ G_CALLBACK (gtk_recent_manager_monitor_changed),
+ manager);
+ g_object_unref (priv->monitor);
+ priv->monitor = NULL;
+ }
+
if (!filename || filename[0] == '\0')
return;
- g_free (manager->priv->filename);
+ priv->filename = g_strdup (filename);
+ file = g_file_new_for_path (priv->filename);
- if (manager->priv->poll_timeout)
+ error = NULL;
+ priv->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error);
+ if (error)
{
- g_source_remove (manager->priv->poll_timeout);
- manager->priv->poll_timeout = 0;
+ filename_warning ("Unable to monitor `%s': %s\n"
+ "The GtkRecentManager will not update its contents "
+ "if the file is changed from other instances",
+ priv->filename,
+ error->message);
+ g_error_free (error);
}
+ else
+ g_signal_connect (priv->monitor, "changed",
+ G_CALLBACK (gtk_recent_manager_monitor_changed),
+ manager);
- priv->filename = g_strdup (filename);
-
- dispatch = g_slice_new (ThreadsDispatch);
- dispatch->func = gtk_recent_manager_poll_timeout;
- dispatch->data = manager;
- dispatch->notify = NULL;
- priv->poll_timeout = g_timeout_add_seconds_full (G_PRIORITY_DEFAULT + 30,
- POLL_DELTA,
- threads_dispatch,
- dispatch,
- threads_free);
+ g_object_unref (file);
- /* mark us clean, so that we can re-read the list
- * of recently used resources
- */
priv->is_dirty = FALSE;
build_recent_items_list (manager);
}
@@ -584,14 +506,10 @@
static void
build_recent_items_list (GtkRecentManager *manager)
{
- GtkRecentManagerPrivate *priv;
- struct stat stat_buf;
- int stat_res;
- gboolean res;
+ GtkRecentManagerPrivate *priv = manager->priv;
GError *read_error;
gint size;
- priv = manager->priv;
g_assert (priv->filename != NULL);
if (!priv->recent_items)
@@ -600,60 +518,41 @@
priv->size = 0;
}
- stat_res = g_stat (priv->filename, &stat_buf);
- if (stat_res < 0)
- {
- /* the file doesn't exists, so we bail out and wait for the first
- * write operation
- */
-
- if (errno == ENOENT)
- return;
- else
- {
- filename_warning ("Attempting to read the recently used resources file "
- "at `%s', but an error occurred: %s. Aborting.",
- priv->filename,
- g_strerror (errno));
-
- return;
- }
- }
-
- /* record the last mtime, for later use */
- priv->last_mtime = stat_buf.st_mtime;
-
- priv->read_in_progress = TRUE;
-
/* the file exists, and it's valid (we hope); if not, destroy the container
* object and hope for a better result when the next "changed" signal is
* fired. */
read_error = NULL;
- res = g_bookmark_file_load_from_file (priv->recent_items,
- priv->filename,
- &read_error);
+ g_bookmark_file_load_from_file (priv->recent_items, priv->filename, &read_error);
if (read_error)
{
- filename_warning ("Attempting to read the recently used resources file "
- "at `%s', but the parser failed: %s.",
- priv->filename,
- read_error->message);
+ /* if the file does not exist we just wait for the first write
+ * operation on this recent manager instance, to avoid creating
+ * empty files and leading to spurious file system events (Sabayon
+ * will not be happy about those)
+ */
+ if (read_error->domain == G_FILE_ERROR &&
+ read_error->code != G_FILE_ERROR_NOENT)
+ filename_warning ("Attempting to read the recently used resources "
+ "file at `%s', but the parser failed: %s.",
+ priv->filename,
+ read_error->message);
g_bookmark_file_free (priv->recent_items);
priv->recent_items = NULL;
g_error_free (read_error);
}
-
- size = g_bookmark_file_get_size (priv->recent_items);
- if (priv->size != size)
+ else
{
- priv->size = size;
-
- g_object_notify (G_OBJECT (manager), "size");
+ size = g_bookmark_file_get_size (priv->recent_items);
+ if (priv->size != size)
+ {
+ priv->size = size;
+
+ g_object_notify (G_OBJECT (manager), "size");
+ }
}
- priv->read_in_progress = FALSE;
priv->is_dirty = FALSE;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]