Index: gtk/gtkrecentmanager.c
===================================================================
--- gtk/gtkrecentmanager.c	(revision 19932)
+++ gtk/gtkrecentmanager.c	(working copy)
@@ -30,6 +30,7 @@
 #include <stdlib.h>
 #include <glib.h>
 #include <glib/gstdio.h>
+#include <gio/gio.h>
 
 #include "gtkrecentmanager.h"
 #include "gtkintl.h"
@@ -48,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
 
@@ -59,13 +57,6 @@
 
 typedef struct
 {
-  GSourceFunc func;
-  gpointer data;
-  GDestroyNotify notify;
-} ThreadsDispatch;
-
-typedef struct
-{
   gchar *name;
   gchar *exec;
   
@@ -99,21 +90,20 @@ struct _GtkRecentInfo
   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
@@ -125,33 +115,35 @@ enum
   PROP_SIZE
 };
 
-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_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 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_changed         (GtkRecentManager  *manager);
+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_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 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;
 
@@ -197,33 +189,6 @@ gtk_recent_manager_error_quark (void)
   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)
 {
@@ -250,7 +215,7 @@ gtk_recent_manager_class_init (GtkRecent
 					   		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
    *
@@ -312,34 +277,19 @@ static void
 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
@@ -395,10 +345,13 @@ gtk_recent_manager_dispose (GObject *obj
   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);
@@ -428,15 +381,12 @@ gtk_recent_manager_real_changed (GtkRece
   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
        */
@@ -457,26 +407,6 @@ gtk_recent_manager_real_changed (GtkRece
 	  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;
     }
@@ -492,44 +422,28 @@ gtk_recent_manager_real_changed (GtkRece
   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)
-{
-  GtkRecentManager *manager = GTK_RECENT_MANAGER (data);
-  GtkRecentManagerPrivate *priv = manager->priv;
-  struct stat stat_buf;
-  int stat_res;
+static void
+gtk_recent_manager_monitor_changed (GFileMonitor      *monitor,
+                                    GFile             *file,
+                                    GFile             *other_file,
+                                    GFileMonitorEvent  event_type,
+                                    gpointer           user_data)
+{
+  GtkRecentManager *manager = user_data;
+
+  switch (event_type)
+    {
+    case G_FILE_MONITOR_EVENT_CHANGED:
+    case G_FILE_MONITOR_EVENT_CREATED:
+      gtk_recent_manager_changed (manager);
+      break;
 
-  /* wait for the next timeout if we have a read/write in progress */
-  if (priv->write_in_progress || priv->read_in_progress)
-    return TRUE;
+    case G_FILE_MONITOR_EVENT_DELETED:
+      break;
 
-  stat_res = g_stat (priv->filename, &stat_buf);
-  if (stat_res < 0)
-    {
-      /* 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;
+    default:
+      break;
     }
-
-  /* the file didn't change from the last poll, so we bail out */
-  if (stat_buf.st_mtime == priv->last_mtime)
-    return TRUE;
-
-  /* the file has been changed, hence we emit the "changed" signal */
-  gtk_recent_manager_changed (manager);
-
-  return TRUE;
 }
 
 static void
@@ -537,37 +451,48 @@ gtk_recent_manager_set_filename (GtkRece
 				 const gchar      *filename)
 {
   GtkRecentManagerPrivate *priv;
-  ThreadsDispatch *dispatch;
+  GFile *file;
+  GError *error;
   
   g_assert (GTK_IS_RECENT_MANAGER (manager));
+
   priv = manager->priv;
   
-  if (!filename || filename[0] == '\0')
-    return;
-  
-  g_free (manager->priv->filename);
+  g_free (priv->filename);
 
-  if (manager->priv->poll_timeout)
+  if (priv->monitor)
     {
-      g_source_remove (manager->priv->poll_timeout);
-      manager->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;
     }
 
+  if (!filename || filename[0] == '\0')
+    return;
+  
   priv->filename = g_strdup (filename);
+  file = g_file_new_for_path (priv->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);
+  error = NULL;
+  priv->monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, &error);
+  if (error)
+    {
+      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);
+
+  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);
 }
@@ -580,14 +505,10 @@ gtk_recent_manager_set_filename (GtkRece
 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)
@@ -596,60 +517,41 @@ build_recent_items_list (GtkRecentManage
       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;
 }
 
Index: configure.in
===================================================================
--- configure.in	(revision 19932)
+++ configure.in	(working copy)
@@ -1674,7 +1674,7 @@ else
         LIBS="$gtk_save_LIBS"
 fi
 
-GTK_PACKAGES="atk cairo"
+GTK_PACKAGES="atk cairo gio-2.0"
 GTK_EXTRA_LIBS=
 GTK_EXTRA_CFLAGS= 
 GTK_DEP_LIBS="$GDK_EXTRA_LIBS $GTK_DEP_LIBS_FOR_X `$PKG_CONFIG --libs $GDK_PIXBUF_PACKAGES $PANGO_PACKAGES $GTK_PACKAGES_FOR_X $GTK_PACKAGES` $GTK_EXTRA_LIBS $GDK_PIXBUF_EXTRA_LIBS"
