--- gtk+-2.10.3/gtk/gtkrecentmanager.c.fam 2006-08-18 11:30:57.000000000 -0400 +++ gtk+-2.10.3/gtk/gtkrecentmanager.c 2006-09-05 11:33:48.000000000 -0400 @@ -38,6 +38,9 @@ #include "gtktypebuiltins.h" #include "gtkprivate.h" #include "gtkmarshalers.h" + +#include + #include "gtkalias.h" #ifdef G_OS_UNIX @@ -110,6 +113,9 @@ time_t last_mtime; guint poll_timeout; + + FAMRequest *fam_request; + guint changed_timeout; }; enum @@ -274,6 +280,309 @@ g_type_class_add_private (klass, sizeof (GtkRecentManagerPrivate)); } + +/* fam support */ +#undef DEBUG_FAM + +static FAMConnection fam_connection; +static gboolean opened_connection = FALSE; +static gboolean failed_to_connect = FALSE; +static guint fam_io_watch = 0; + +static int (*fam_open) (FAMConnection *fc) = NULL; +static int (*fam_close) (FAMConnection *fc) = NULL; +static int (*fam_pending) (FAMConnection *fc) = NULL; +static int (*fam_next_event) (FAMConnection *fc, + FAMEvent *fe) = NULL; +static int (*fam_monitor_file) (FAMConnection *fc, + const char *filename, + FAMRequest *fr, + void *userData) = NULL; +static int (*fam_cancel_monitor) (FAMConnection *fc, + const FAMRequest *fr) = NULL; + + +static struct FamDlMapping +{ + const char *fn_name; + gpointer fn_ptr_ref; +} fam_dl_mapping[] = { + { "FAMOpen", &fam_open }, + { "FAMClose", &fam_close }, + { "FAMPending", &fam_pending }, + { "FAMNextEvent", &fam_next_event }, + { "FAMMonitorFile", &fam_monitor_file }, + { "FAMCancelMonitor", &fam_cancel_monitor } +}; + +static void +open_libfam (void) +{ + static gboolean done = FALSE; + + if (!done) + { + int i; + GModule *fam; + + done = TRUE; + + fam = g_module_open ("libfam.so.0", G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + if (!fam) + { + g_warning ("Can't open libfam '%s'\n", g_module_error ()); + return; + } + + for (i = 0; i < G_N_ELEMENTS (fam_dl_mapping); i++) + { + if (!g_module_symbol (fam, fam_dl_mapping[i].fn_name, + fam_dl_mapping[i].fn_ptr_ref)) + { + g_warning ("Missing symbol '%s' in libfam\n", + fam_dl_mapping[i].fn_name); + g_module_close (fam); + for (i = 0; i < G_N_ELEMENTS (fam_dl_mapping); i++) + fam_dl_mapping[i].fn_ptr_ref = NULL; + + return; + } + } + } +} + +static gboolean +changed_timeout (gpointer data) +{ + GtkRecentManager *manager; + + GDK_THREADS_ENTER (); + + manager = (GtkRecentManager *)data; + manager->priv->changed_timeout = 0; + + gtk_recent_manager_changed (manager); + + GDK_THREADS_LEAVE (); + + return FALSE; +} + +static void +handle_fam_event (GtkRecentManager *manager, + FAMEvent *event) +{ + GtkRecentManagerPrivate *priv = manager->priv; + + /* try to group delete-create pairs */ + if (event->code == FAMDeleted) + { + if (priv->changed_timeout == 0) + priv->changed_timeout = g_timeout_add (500, changed_timeout, manager); + } + else + { + if (priv->changed_timeout != 0) + { + g_source_remove (priv->changed_timeout); + priv->changed_timeout = 0; + } + gtk_recent_manager_changed (manager); + } +} + +#ifdef DEBUG_FAM +static inline void +debug_event (FAMEvent *event) +{ +#define PRINT_EVENT(str) g_print ("Got event: %d %s <" str ">\n", event->code, event->filename); + + switch (event->code) + { + case FAMChanged: + PRINT_EVENT ("changed"); + break; + case FAMDeleted: + PRINT_EVENT ("deleted"); + break; + case FAMStartExecuting: + PRINT_EVENT ("start-executing"); + break; + case FAMStopExecuting: + PRINT_EVENT ("stop-executing"); + break; + case FAMCreated: + PRINT_EVENT ("created"); + break; + case FAMAcknowledge: + PRINT_EVENT ("acknowledge"); + break; + case FAMExists: + PRINT_EVENT ("exists"); + break; + case FAMEndExist: + PRINT_EVENT ("end-exist"); + break; + case FAMMoved: + PRINT_EVENT ("moved"); + break; + default: + PRINT_EVENT ("invalid"); + break; + } + +#undef PRINT_EVENT +} +#else +#define debug_event(event) +#endif + +static gboolean +process_fam_events (void) +{ + if (failed_to_connect) + return FALSE; + + while (fam_pending (&fam_connection)) + { + FAMEvent event; + + if (fam_next_event (&fam_connection, &event) != 1) + { + g_warning ("Failed to read next event from FAM"); + failed_to_connect = TRUE; + fam_close (&fam_connection); + return FALSE; + } + + debug_event (&event); + + if (event.code != FAMChanged && + event.code != FAMCreated && + event.code != FAMDeleted) + continue; + + handle_fam_event (event.userdata, &event); + } + + return TRUE; +} + +static gboolean +fam_data_pending (GIOChannel *source, + GIOCondition condition) +{ + g_assert (condition == G_IO_IN || condition == G_IO_PRI); + + if (!process_fam_events ()) + { + fam_io_watch = 0; + return FALSE; + } + + return TRUE; /* do come again */ +} + +static FAMConnection * +get_fam_connection (void) +{ + if (!opened_connection) + { + opened_connection = TRUE; + + open_libfam (); + + if (fam_open != NULL && + fam_open (&fam_connection) == 0) + { + GIOChannel *io_channel; + + io_channel = g_io_channel_unix_new (fam_connection.fd); + fam_io_watch = g_io_add_watch (io_channel, + G_IO_IN|G_IO_PRI, + (GIOFunc) fam_data_pending, + NULL); + g_io_channel_unref (io_channel); + } + else + { + g_warning ("Failed to connect to the FAM server"); + failed_to_connect = TRUE; + } + } + + return failed_to_connect ? NULL : &fam_connection; +} + +static FAMRequest * +register_fam_monitor (const gchar *path, + gpointer user_data) +{ + FAMConnection *fam_connection; + FAMRequest *request; + + if ((fam_connection = get_fam_connection ()) == NULL) + { + g_warning ("Not adding file monitor on '%s', " + "failed to connect to FAM server\n", + path); + return NULL; + } + + /* Need to process any pending events, otherwise we may block + * on write - i.e. the FAM sever is blocked because its write + * buffer is full notifying us of events, we need to read those + * events before it can process our new request. + */ + if (!process_fam_events ()) + { + g_source_remove (fam_io_watch); + fam_io_watch = 0; + return NULL; + } + + request = g_new0 (FAMRequest, 1); + + if (fam_monitor_file (fam_connection, path, request, user_data) != 0) + { + g_warning ("Failed to add file monitor on '%s'", path); + g_free (request); + + return NULL; + } + +#ifdef DEBUG_FAM + g_print ("registering file monitor for '%s'\n", path); +#endif + + return request; +} + +static void +unregister_fam_monitor (FAMRequest *request) +{ + if (failed_to_connect) + return; + + if (request != NULL) + fam_cancel_monitor (&fam_connection, request); + + /* Need to process any remaining events for this monitor + */ + if (!process_fam_events ()) + { + g_source_remove (fam_io_watch); + fam_io_watch = 0; + } + +#ifdef DEBUG_FAM + g_print ("unregistering file monitor\n"); +#endif +} + +/* end of fam support */ + + static void gtk_recent_manager_init (GtkRecentManager *manager) { @@ -296,9 +605,12 @@ priv->filename = g_build_filename (g_get_home_dir (), GTK_RECENTLY_USED_FILE, NULL); - priv->poll_timeout = g_timeout_add (POLL_DELTA, - gtk_recent_manager_poll_timeout, - manager); + + priv->fam_request = register_fam_monitor (priv->filename, manager); + if (priv->fam_request == NULL) + priv->poll_timeout = g_timeout_add (POLL_DELTA, + gtk_recent_manager_poll_timeout, + manager); build_recent_items_list (manager); } @@ -356,10 +668,19 @@ GtkRecentManager *manager = GTK_RECENT_MANAGER (object); GtkRecentManagerPrivate *priv = manager->priv; + if (priv->fam_request) + { + unregister_fam_monitor (priv->fam_request); + g_free (priv->fam_request); + priv->fam_request = NULL; + } + if (priv->changed_timeout) + g_source_remove (priv->changed_timeout); + /* remove the poll timeout */ if (priv->poll_timeout) g_source_remove (priv->poll_timeout); - + if (priv->filename) g_free (priv->filename); @@ -499,18 +820,28 @@ if (!filename || filename[0] == '\0') return; - g_free (manager->priv->filename); + g_free (priv->filename); - if (manager->priv->poll_timeout) + if (priv->fam_request) { - g_source_remove (manager->priv->poll_timeout); - manager->priv->poll_timeout = 0; + unregister_fam_monitor (priv->fam_request); + g_free (priv->fam_request); + priv->fam_request = NULL; + } + + if (priv->poll_timeout) + { + g_source_remove (priv->poll_timeout); + priv->poll_timeout = 0; } priv->filename = g_strdup (filename); - priv->poll_timeout = g_timeout_add (POLL_DELTA, - gtk_recent_manager_poll_timeout, - manager); + + priv->fam_request = register_fam_monitor (priv->filename, manager); + if (priv->fam_request == NULL) + priv->poll_timeout = g_timeout_add (POLL_DELTA, + gtk_recent_manager_poll_timeout, + manager); /* mark us clean, so that we can re-read the list * of recently used resources