diff --git a/gio/Makefile.am b/gio/Makefile.am index 28aea41ae..8d9febb1d 100644 --- a/gio/Makefile.am +++ b/gio/Makefile.am @@ -185,8 +185,6 @@ application_sources = \ local_sources = \ ghttpproxy.c \ ghttpproxy.h \ - glocaldirectorymonitor.c \ - glocaldirectorymonitor.h \ glocalfile.c \ glocalfile.h \ glocalfileenumerator.c \ diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c index 786ce0f52..0b34235ce 100644 --- a/gio/gdesktopappinfo.c +++ b/gio/gdesktopappinfo.c @@ -46,8 +46,7 @@ #include "giomodule-priv.h" #include "gappinfo.h" #include "gappinfoprivate.h" -#include "glocaldirectorymonitor.h" - +#include "glocalfilemonitor.h" /** * SECTION:gdesktopappinfo @@ -140,7 +139,7 @@ typedef struct gchar *alternatively_watching; gboolean is_config; gboolean is_setup; - GLocalDirectoryMonitor *monitor; + GFileMonitor *monitor; GHashTable *app_names; GHashTable *mime_tweaks; GHashTable *memory_index; @@ -1335,13 +1334,8 @@ desktop_file_dir_init (DesktopFileDir *dir) * does (and we catch the unlikely race), the only degradation is that * we will fall back to polling. */ - dir->monitor = g_local_directory_monitor_new_in_worker (watch_dir, G_FILE_MONITOR_NONE, NULL); - - if (dir->monitor) - { - g_signal_connect (dir->monitor, "changed", G_CALLBACK (desktop_file_dir_changed), dir); - g_local_directory_monitor_start (dir->monitor); - } + dir->monitor = g_local_file_monitor_new_in_worker (watch_dir, TRUE, G_FILE_MONITOR_NONE, + desktop_file_dir_changed, dir, NULL); desktop_file_dir_unindexed_init (dir); diff --git a/gio/gfilemonitor.c b/gio/gfilemonitor.c index c81f6fcb5..71d5f55c9 100644 --- a/gio/gfilemonitor.c +++ b/gio/gfilemonitor.c @@ -27,11 +27,6 @@ #include "gvfs.h" #include "glibintl.h" - -struct _FileChange; -typedef struct _FileChange FileChange; -static void file_change_free (FileChange *change); - /** * SECTION:gfilemonitor * @short_description: File Monitor @@ -53,69 +48,38 @@ static void file_change_free (FileChange *change); * context is still running). **/ -G_LOCK_DEFINE_STATIC(cancelled); +#define DEFAULT_RATE_LIMIT_MSECS 800 -enum { - CHANGED, - LAST_SIGNAL -}; - -typedef struct { - GFile *file; - guint32 last_sent_change_time; /* 0 == not sent */ - guint32 send_delayed_change_at; /* 0 == never */ - guint32 send_virtual_changes_done_at; /* 0 == never */ -} RateLimiter; - -struct _GFileMonitorPrivate { +struct _GFileMonitorPrivate +{ gboolean cancelled; - int rate_limit_msec; - - /* Rate limiting change events */ - GHashTable *rate_limiter; - - GMutex mutex; - GSource *pending_file_change_source; - GSList *pending_file_changes; /* FileChange */ - - GSource *timeout; - guint32 timeout_fires_at; - - GMainContext *context; }; -enum { - PROP_0, - PROP_RATE_LIMIT, - PROP_CANCELLED, - PROP_CONTEXT -}; - -/* work around a limitation of the aliasing foo */ -#undef g_file_monitor - G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GFileMonitor, g_file_monitor, G_TYPE_OBJECT) +enum +{ + PROP_0, + PROP_RATE_LIMIT, + PROP_CANCELLED +}; + +static guint g_file_monitor_changed_signal; + static void g_file_monitor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - GFileMonitor *monitor; + //GFileMonitor *monitor; - monitor = G_FILE_MONITOR (object); + //monitor = G_FILE_MONITOR (object); switch (prop_id) { case PROP_RATE_LIMIT: - g_file_monitor_set_rate_limit (monitor, g_value_get_int (value)); - break; - - case PROP_CONTEXT: - monitor->priv->context = g_value_dup_boxed (value); - if (monitor->priv->context == NULL) - monitor->priv->context = g_main_context_ref_thread_default (); + /* not supported by default */ break; default: @@ -130,22 +94,17 @@ g_file_monitor_get_property (GObject *object, GValue *value, GParamSpec *pspec) { - GFileMonitor *monitor; - GFileMonitorPrivate *priv; - - monitor = G_FILE_MONITOR (object); - priv = monitor->priv; - switch (prop_id) { case PROP_RATE_LIMIT: - g_value_set_int (value, priv->rate_limit_msec); + /* we expect this to be overridden... */ + g_value_set_int (value, DEFAULT_RATE_LIMIT_MSECS); break; case PROP_CANCELLED: - G_LOCK (cancelled); - g_value_set_boolean (value, priv->cancelled); - G_UNLOCK (cancelled); + //g_mutex_lock (&fms->lock); + g_value_set_boolean (value, FALSE);//fms->cancelled); + //g_mutex_unlock (&fms->lock); break; default: @@ -154,56 +113,10 @@ g_file_monitor_get_property (GObject *object, } } -#define DEFAULT_RATE_LIMIT_MSECS 800 -#define DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS 2 - -static guint signals[LAST_SIGNAL] = { 0 }; - -static void -rate_limiter_free (RateLimiter *limiter) -{ - g_object_unref (limiter->file); - g_slice_free (RateLimiter, limiter); -} - -static void -g_file_monitor_finalize (GObject *object) -{ - GFileMonitor *monitor; - - monitor = G_FILE_MONITOR (object); - - if (monitor->priv->timeout) - { - g_source_destroy (monitor->priv->timeout); - g_source_unref (monitor->priv->timeout); - } - - g_hash_table_destroy (monitor->priv->rate_limiter); - - g_main_context_unref (monitor->priv->context); - g_mutex_clear (&monitor->priv->mutex); - - G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize (object); -} - static void g_file_monitor_dispose (GObject *object) { - GFileMonitor *monitor; - GFileMonitorPrivate *priv; - - monitor = G_FILE_MONITOR (object); - priv = monitor->priv; - - if (priv->pending_file_change_source) - { - g_source_destroy (priv->pending_file_change_source); - g_source_unref (priv->pending_file_change_source); - priv->pending_file_change_source = NULL; - } - g_slist_free_full (priv->pending_file_changes, (GDestroyNotify) file_change_free); - priv->pending_file_changes = NULL; + GFileMonitor *monitor = G_FILE_MONITOR (object); /* Make sure we cancel on last unref */ g_file_monitor_cancel (monitor); @@ -211,13 +124,18 @@ g_file_monitor_dispose (GObject *object) G_OBJECT_CLASS (g_file_monitor_parent_class)->dispose (object); } +static void +g_file_monitor_init (GFileMonitor *monitor) +{ + monitor->priv = g_file_monitor_get_instance_private (monitor); +} + static void g_file_monitor_class_init (GFileMonitorClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); - object_class->finalize = g_file_monitor_finalize; object_class->dispose = g_file_monitor_dispose; object_class->get_property = g_file_monitor_get_property; object_class->set_property = g_file_monitor_set_property; @@ -231,58 +149,54 @@ g_file_monitor_class_init (GFileMonitorClass *klass) * * Emitted when @file has been changed. * - * If using #G_FILE_MONITOR_SEND_MOVED flag and @event_type is + * If using %G_FILE_MONITOR_WATCH_RENAMES on a directory monitor, and + * the information is available (and if supported by the backend), + * @event_type may be %G_FILE_MONITOR_EVENT_RENAMED, + * %G_FILE_MONITOR_EVENT_MOVED_IN or %G_FILE_MONITOR_EVENT_MOVED_OUT. + * + * In all cases @file will be a child of the monitored directory. For + * renames, @file will be the old name and @other_file is the new + * name. For "moved in" events, @file is the name of the file that + * appeared and @other_file is the old name that it was moved from (in + * another directory). For "moved out" events, @file is the name of + * the file that used to be in this directory and @other_file is the + * name of the file at its new location. + * + * It makes sense to treat %G_FILE_MONITOR_EVENT_MOVED_IN as + * equivalent to %G_FILE_MONITOR_EVENT_CREATED and + * %G_FILE_MONITOR_EVENT_MOVED_OUT as equivalent to + * %G_FILE_MONITOR_EVENT_DELETED, with extra information. + * %G_FILE_MONITOR_EVENT_RENAMED is equivalent to a delete/create + * pair. This is exactly how the events will be reported in the case + * that the %G_FILE_MONITOR_WATCH_RENAMES flag is not in use. + * + * If using the deprecated flag %G_FILE_MONITOR_SEND_MOVED flag and @event_type is * #G_FILE_MONITOR_EVENT_MOVED, @file will be set to a #GFile containing the * old path, and @other_file will be set to a #GFile containing the new path. * * In all the other cases, @other_file will be set to #NULL. **/ - signals[CHANGED] = - g_signal_new (I_("changed"), - G_TYPE_FILE_MONITOR, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GFileMonitorClass, changed), - NULL, NULL, - NULL, - G_TYPE_NONE, 3, - G_TYPE_FILE, G_TYPE_FILE, G_TYPE_FILE_MONITOR_EVENT); + g_file_monitor_changed_signal = g_signal_new (I_("changed"), + G_TYPE_FILE_MONITOR, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GFileMonitorClass, changed), + NULL, NULL, + NULL, + G_TYPE_NONE, 3, + G_TYPE_FILE, G_TYPE_FILE, G_TYPE_FILE_MONITOR_EVENT); - g_object_class_install_property (object_class, - PROP_RATE_LIMIT, + g_object_class_install_property (object_class, PROP_RATE_LIMIT, g_param_spec_int ("rate-limit", P_("Rate limit"), P_("The limit of the monitor to watch for changes, in milliseconds"), - 0, G_MAXINT, - DEFAULT_RATE_LIMIT_MSECS, - G_PARAM_READWRITE| - G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB)); + 0, G_MAXINT, DEFAULT_RATE_LIMIT_MSECS, G_PARAM_READWRITE | + G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (object_class, - PROP_CANCELLED, + g_object_class_install_property (object_class, PROP_CANCELLED, g_param_spec_boolean ("cancelled", P_("Cancelled"), P_("Whether the monitor has been cancelled"), - FALSE, - G_PARAM_READABLE| - G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB)); - - g_object_class_install_property (object_class, - PROP_CONTEXT, - g_param_spec_boxed ("context", - P_("Context"), - P_("The main context to dispatch from"), - G_TYPE_MAIN_CONTEXT, G_PARAM_WRITABLE | - G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); -} - -static void -g_file_monitor_init (GFileMonitor *monitor) -{ - monitor->priv = g_file_monitor_get_instance_private (monitor); - monitor->priv->rate_limit_msec = DEFAULT_RATE_LIMIT_MSECS; - monitor->priv->rate_limiter = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, - NULL, (GDestroyNotify) rate_limiter_free); - g_mutex_init (&monitor->priv->mutex); + FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); } /** @@ -300,42 +214,33 @@ g_file_monitor_is_cancelled (GFileMonitor *monitor) g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE); - G_LOCK (cancelled); res = monitor->priv->cancelled; - G_UNLOCK (cancelled); - + return res; } /** * g_file_monitor_cancel: * @monitor: a #GFileMonitor. - * + * * Cancels a file monitor. - * - * Returns: %TRUE if monitor was cancelled. + * + * Returns: always %TRUE **/ gboolean -g_file_monitor_cancel (GFileMonitor* monitor) +g_file_monitor_cancel (GFileMonitor *monitor) { - GFileMonitorClass *klass; - g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE); - - G_LOCK (cancelled); - if (monitor->priv->cancelled) - { - G_UNLOCK (cancelled); - return TRUE; - } - - monitor->priv->cancelled = TRUE; - G_UNLOCK (cancelled); - - g_object_notify (G_OBJECT (monitor), "cancelled"); - klass = G_FILE_MONITOR_GET_CLASS (monitor); - return (* klass->cancel) (monitor); + if (!monitor->priv->cancelled) + { + G_FILE_MONITOR_GET_CLASS (monitor)->cancel (monitor); + + monitor->priv->cancelled = TRUE; + g_object_notify (G_OBJECT (monitor), "cancelled"); + } + + return TRUE; } /** @@ -351,396 +256,21 @@ void g_file_monitor_set_rate_limit (GFileMonitor *monitor, gint limit_msecs) { - GFileMonitorPrivate *priv; - - g_return_if_fail (G_IS_FILE_MONITOR (monitor)); - g_return_if_fail (limit_msecs >= 0); - - priv = monitor->priv; - if (priv->rate_limit_msec != limit_msecs) - { - monitor->priv->rate_limit_msec = limit_msecs; - g_object_notify (G_OBJECT (monitor), "rate-limit"); - } + g_object_set (monitor, "rate-limit", limit_msecs, NULL); } -struct _FileChange { - GFile *child; - GFile *other_file; - GFileMonitorEvent event_type; -}; - -static void -file_change_free (FileChange *change) -{ - g_object_unref (change->child); - if (change->other_file) - g_object_unref (change->other_file); - - g_slice_free (FileChange, change); -} - -static gboolean -emit_cb (gpointer data) -{ - GFileMonitor *monitor = G_FILE_MONITOR (data); - GSList *pending, *iter; - - g_mutex_lock (&monitor->priv->mutex); - pending = g_slist_reverse (monitor->priv->pending_file_changes); - monitor->priv->pending_file_changes = NULL; - if (monitor->priv->pending_file_change_source) - { - g_source_unref (monitor->priv->pending_file_change_source); - monitor->priv->pending_file_change_source = NULL; - } - g_mutex_unlock (&monitor->priv->mutex); - - g_object_ref (monitor); - for (iter = pending; iter; iter = iter->next) - { - FileChange *change = iter->data; - - g_signal_emit (monitor, signals[CHANGED], 0, - change->child, change->other_file, change->event_type); - file_change_free (change); - } - g_slist_free (pending); - g_object_unref (monitor); - - return FALSE; -} - -static void -emit_in_idle (GFileMonitor *monitor, - GFile *child, - GFile *other_file, - GFileMonitorEvent event_type) -{ - GSource *source; - FileChange *change; - GFileMonitorPrivate *priv; - - priv = monitor->priv; - - change = g_slice_new (FileChange); - - change->child = g_object_ref (child); - if (other_file) - change->other_file = g_object_ref (other_file); - else - change->other_file = NULL; - change->event_type = event_type; - - g_mutex_lock (&monitor->priv->mutex); - if (!priv->pending_file_change_source) - { - source = g_idle_source_new (); - priv->pending_file_change_source = source; - g_source_set_priority (source, 0); - - /* We don't ref monitor here - instead dispose will free any - * pending idles. - */ - g_source_set_callback (source, emit_cb, monitor, NULL); - g_source_set_name (source, "[gio] emit_cb"); - g_source_attach (source, monitor->priv->context); - } - /* We reverse this in the processor */ - priv->pending_file_changes = g_slist_prepend (priv->pending_file_changes, change); - g_mutex_unlock (&monitor->priv->mutex); -} - -static guint32 -get_time_msecs (void) -{ - return g_get_monotonic_time () / G_TIME_SPAN_MILLISECOND; -} - -static guint32 -time_difference (guint32 from, guint32 to) -{ - if (from > to) - return 0; - return to - from; -} - -/* Change event rate limiting support: */ - -static RateLimiter * -new_limiter (GFileMonitor *monitor, - GFile *file) -{ - RateLimiter *limiter; - - limiter = g_slice_new0 (RateLimiter); - limiter->file = g_object_ref (file); - g_hash_table_insert (monitor->priv->rate_limiter, file, limiter); - - return limiter; -} - -static void -rate_limiter_send_virtual_changes_done_now (GFileMonitor *monitor, - RateLimiter *limiter) -{ - if (limiter->send_virtual_changes_done_at != 0) - { - emit_in_idle (monitor, limiter->file, NULL, - G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT); - limiter->send_virtual_changes_done_at = 0; - } -} - -static void -rate_limiter_send_delayed_change_now (GFileMonitor *monitor, - RateLimiter *limiter, - guint32 time_now) -{ - if (limiter->send_delayed_change_at != 0) - { - emit_in_idle (monitor, - limiter->file, NULL, - G_FILE_MONITOR_EVENT_CHANGED); - limiter->send_delayed_change_at = 0; - limiter->last_sent_change_time = time_now; - } -} - -typedef struct { - guint32 min_time; - guint32 time_now; - GFileMonitor *monitor; -} ForEachData; - -static gboolean -calc_min_time (GFileMonitor *monitor, - RateLimiter *limiter, - guint32 time_now, - guint32 *min_time) -{ - gboolean delete_me; - guint32 expire_at; - - delete_me = TRUE; - - if (limiter->last_sent_change_time != 0) - { - /* Set a timeout at 2*rate limit so that we can clear out the change from the hash eventually */ - expire_at = limiter->last_sent_change_time + 2 * monitor->priv->rate_limit_msec; - - if (time_difference (time_now, expire_at) > 0) - { - delete_me = FALSE; - *min_time = MIN (*min_time, - time_difference (time_now, expire_at)); - } - } - - if (limiter->send_delayed_change_at != 0) - { - delete_me = FALSE; - *min_time = MIN (*min_time, - time_difference (time_now, limiter->send_delayed_change_at)); - } - - if (limiter->send_virtual_changes_done_at != 0) - { - delete_me = FALSE; - *min_time = MIN (*min_time, - time_difference (time_now, limiter->send_virtual_changes_done_at)); - } - - return delete_me; -} - -static gboolean -foreach_rate_limiter_fire (gpointer key, - gpointer value, - gpointer user_data) -{ - RateLimiter *limiter = value; - ForEachData *data = user_data; - - if (limiter->send_delayed_change_at != 0 && - time_difference (data->time_now, limiter->send_delayed_change_at) == 0) - rate_limiter_send_delayed_change_now (data->monitor, limiter, data->time_now); - - if (limiter->send_virtual_changes_done_at != 0 && - time_difference (data->time_now, limiter->send_virtual_changes_done_at) == 0) - rate_limiter_send_virtual_changes_done_now (data->monitor, limiter); - - return calc_min_time (data->monitor, limiter, data->time_now, &data->min_time); -} - -static gboolean -rate_limiter_timeout (gpointer timeout_data) -{ - GFileMonitor *monitor = timeout_data; - ForEachData data; - GSource *source; - - data.min_time = G_MAXUINT32; - data.monitor = monitor; - data.time_now = get_time_msecs (); - g_hash_table_foreach_remove (monitor->priv->rate_limiter, - foreach_rate_limiter_fire, - &data); - - /* Remove old timeout */ - if (monitor->priv->timeout) - { - g_source_destroy (monitor->priv->timeout); - g_source_unref (monitor->priv->timeout); - monitor->priv->timeout = NULL; - monitor->priv->timeout_fires_at = 0; - } - - /* Set up new timeout */ - if (data.min_time != G_MAXUINT32) - { - source = g_timeout_source_new (data.min_time + 1); /* + 1 to make sure we've really passed the time */ - g_source_set_callback (source, rate_limiter_timeout, monitor, NULL); - g_source_attach (source, monitor->priv->context); - - monitor->priv->timeout = source; - monitor->priv->timeout_fires_at = data.time_now + data.min_time; - } - - return FALSE; -} - -static gboolean -foreach_rate_limiter_update (gpointer key, - gpointer value, - gpointer user_data) -{ - RateLimiter *limiter = value; - ForEachData *data = user_data; - - return calc_min_time (data->monitor, limiter, data->time_now, &data->min_time); -} - -static void -update_rate_limiter_timeout (GFileMonitor *monitor, - guint new_time) -{ - ForEachData data; - GSource *source; - - if (monitor->priv->timeout_fires_at != 0 && new_time != 0 && - time_difference (new_time, monitor->priv->timeout_fires_at) == 0) - return; /* Nothing to do, we already fire earlier than that */ - - data.min_time = G_MAXUINT32; - data.monitor = monitor; - data.time_now = get_time_msecs (); - g_hash_table_foreach_remove (monitor->priv->rate_limiter, - foreach_rate_limiter_update, - &data); - - /* Remove old timeout */ - if (monitor->priv->timeout) - { - g_source_destroy (monitor->priv->timeout); - g_source_unref (monitor->priv->timeout); - monitor->priv->timeout_fires_at = 0; - monitor->priv->timeout = NULL; - } - - /* Set up new timeout */ - if (data.min_time != G_MAXUINT32) - { - source = g_timeout_source_new (data.min_time + 1); /* + 1 to make sure we've really passed the time */ - g_source_set_callback (source, rate_limiter_timeout, monitor, NULL); - g_source_attach (source, monitor->priv->context); - - monitor->priv->timeout = source; - monitor->priv->timeout_fires_at = data.time_now + data.min_time; - } -} - -/** - * g_file_monitor_emit_event: - * @monitor: a #GFileMonitor. - * @child: a #GFile. - * @other_file: a #GFile. - * @event_type: a set of #GFileMonitorEvent flags. - * - * Emits the #GFileMonitor::changed signal if a change - * has taken place. Should be called from file monitor - * implementations only. - * - * The signal will be emitted from an idle handler (in the - * [thread-default main context][g-main-context-push-thread-default]). - **/ void g_file_monitor_emit_event (GFileMonitor *monitor, - GFile *child, - GFile *other_file, - GFileMonitorEvent event_type) + GFile *child, + GFile *other_file, + GFileMonitorEvent event_type) { - guint32 time_now, since_last; - gboolean emit_now; - RateLimiter *limiter; - g_return_if_fail (G_IS_FILE_MONITOR (monitor)); g_return_if_fail (G_IS_FILE (child)); + g_return_if_fail (!other_file || G_IS_FILE (other_file)); - limiter = g_hash_table_lookup (monitor->priv->rate_limiter, child); + if (monitor->priv->cancelled) + return; - if (event_type != G_FILE_MONITOR_EVENT_CHANGED) - { - if (limiter) - { - rate_limiter_send_delayed_change_now (monitor, limiter, get_time_msecs ()); - if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT) - limiter->send_virtual_changes_done_at = 0; - else - rate_limiter_send_virtual_changes_done_now (monitor, limiter); - update_rate_limiter_timeout (monitor, 0); - } - emit_in_idle (monitor, child, other_file, event_type); - } - else - { - /* Changed event, rate limit */ - time_now = get_time_msecs (); - emit_now = TRUE; - - if (limiter) - { - since_last = time_difference (limiter->last_sent_change_time, time_now); - if (since_last < monitor->priv->rate_limit_msec) - { - /* We ignore this change, but arm a timer so that we can fire it later if we - don't get any other events (that kill this timeout) */ - emit_now = FALSE; - if (limiter->send_delayed_change_at == 0) - { - limiter->send_delayed_change_at = time_now + monitor->priv->rate_limit_msec; - update_rate_limiter_timeout (monitor, limiter->send_delayed_change_at); - } - } - } - - if (limiter == NULL) - limiter = new_limiter (monitor, child); - - if (emit_now) - { - emit_in_idle (monitor, child, other_file, event_type); - - limiter->last_sent_change_time = time_now; - limiter->send_delayed_change_at = 0; - /* Set a timeout of 2*rate limit so that we can clear out the change from the hash eventually */ - update_rate_limiter_timeout (monitor, time_now + 2 * monitor->priv->rate_limit_msec); - } - - /* Schedule a virtual change done. This is removed if we get a real one, and - postponed if we get more change events. */ - - limiter->send_virtual_changes_done_at = time_now + DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS * 1000; - update_rate_limiter_timeout (monitor, limiter->send_virtual_changes_done_at); - } + g_signal_emit (monitor, g_file_monitor_changed_signal, 0, child, other_file, event_type); } diff --git a/gio/gioenums.h b/gio/gioenums.h index 6b8ab728b..9fd563dcc 100644 --- a/gio/gioenums.h +++ b/gio/gioenums.h @@ -328,9 +328,14 @@ typedef enum { * by file renames (moves) and send a single G_FILE_MONITOR_EVENT_MOVED * event instead (NB: not supported on all backends; the default * behaviour -without specifying this flag- is to send single DELETED - * and CREATED events). + * and CREATED events). Deprecated since 2.44: use + * %G_FILE_MONITOR_WATCH_MOVES instead. * @G_FILE_MONITOR_WATCH_HARD_LINKS: Watch for changes to the file made * via another hard link. Since 2.36. + * @G_FILE_MONITOR_WATCH_MOVES: Watch for rename operations on a + * monitored directory. This causes %G_FILE_MONITOR_EVENT_RENAMED, + * %G_FILE_MONITOR_EVENT_MOVED_IN and %G_FILE_MONITOR_EVENT_MOVED_OUT + * events to be emitted when possible. Since: 2.44. * * Flags used to set what a #GFileMonitor will watch for. */ @@ -338,7 +343,8 @@ typedef enum { G_FILE_MONITOR_NONE = 0, G_FILE_MONITOR_WATCH_MOUNTS = (1 << 0), G_FILE_MONITOR_SEND_MOVED = (1 << 1), - G_FILE_MONITOR_WATCH_HARD_LINKS = (1 << 2) + G_FILE_MONITOR_WATCH_HARD_LINKS = (1 << 2), + G_FILE_MONITOR_WATCH_MOVES = (1 << 3) } GFileMonitorFlags; @@ -393,7 +399,17 @@ typedef enum { * @G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: a file attribute was changed. * @G_FILE_MONITOR_EVENT_PRE_UNMOUNT: the file location will soon be unmounted. * @G_FILE_MONITOR_EVENT_UNMOUNTED: the file location was unmounted. - * @G_FILE_MONITOR_EVENT_MOVED: the file was moved. + * @G_FILE_MONITOR_EVENT_MOVED: the file was moved -- only sent if the + * (deprecated) %G_FILE_MONITOR_SEND_MOVED flag is set + * @G_FILE_MONITOR_EVENT_RENAMED: the file was renamed within the + * current directory -- only sent if the %G_FILE_MONITOR_WATCH_MOVES + * flag is set. Since: 2.44. + * @G_FILE_MONITOR_EVENT_MOVED_IN: the file was moved into the + * monitored directory from another location -- only sent if the + * %G_FILE_MONITOR_WATCH_MOVES flag is set. Since: 2.44. + * @G_FILE_MONITOR_EVENT_MOVED_OUT: the file was moved out of the + * monitored directory to another location -- only sent if the + * %G_FILE_MONITOR_WATCH_MOVES flag is set. Since: 2.44 * * Specifies what type of event a monitor event is. **/ @@ -405,7 +421,10 @@ typedef enum { G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED, G_FILE_MONITOR_EVENT_PRE_UNMOUNT, G_FILE_MONITOR_EVENT_UNMOUNTED, - G_FILE_MONITOR_EVENT_MOVED + G_FILE_MONITOR_EVENT_MOVED, + G_FILE_MONITOR_EVENT_RENAMED, + G_FILE_MONITOR_EVENT_MOVED_IN, + G_FILE_MONITOR_EVENT_MOVED_OUT } GFileMonitorEvent; diff --git a/gio/giomodule.c b/gio/giomodule.c index 4b16a831c..f559c748d 100644 --- a/gio/giomodule.c +++ b/gio/giomodule.c @@ -25,7 +25,6 @@ #include "giomodule.h" #include "giomodule-priv.h" #include "glocalfilemonitor.h" -#include "glocaldirectorymonitor.h" #include "gnativevolumemonitor.h" #include "gproxyresolver.h" #include "gproxy.h" @@ -37,6 +36,8 @@ #include "gtlsbackend.h" #include "gvfs.h" #include "gnotificationbackend.h" +#include "ginitable.h" +#include "gnetworkmonitor.h" #ifdef G_OS_WIN32 #include "gregistrysettingsbackend.h" #endif @@ -886,17 +887,15 @@ _g_io_module_get_default (const gchar *extension_point, G_LOCK_DEFINE_STATIC (registered_extensions); G_LOCK_DEFINE_STATIC (loaded_dirs); -extern GType _g_fen_directory_monitor_get_type (void); -extern GType _g_fen_file_monitor_get_type (void); -extern GType _g_inotify_directory_monitor_get_type (void); -extern GType _g_inotify_file_monitor_get_type (void); -extern GType _g_kqueue_directory_monitor_get_type (void); -extern GType _g_kqueue_file_monitor_get_type (void); +extern GType g_fen_file_monitor_get_type (void); +extern GType g_inotify_file_monitor_get_type (void); +extern GType g_kqueue_file_monitor_get_type (void); +extern GType g_win32_file_monitor_get_type (void); + extern GType _g_unix_volume_monitor_get_type (void); extern GType _g_local_vfs_get_type (void); extern GType _g_win32_volume_monitor_get_type (void); -extern GType g_win32_directory_monitor_get_type (void); extern GType _g_winhttp_vfs_get_type (void); extern GType _g_dummy_proxy_resolver_get_type (void); @@ -979,15 +978,9 @@ _g_io_modules_ensure_extension_points_registered (void) G_GNUC_END_IGNORE_DEPRECATIONS #endif #endif - - ep = g_io_extension_point_register (G_LOCAL_DIRECTORY_MONITOR_EXTENSION_POINT_NAME); - g_io_extension_point_set_required_type (ep, G_TYPE_LOCAL_DIRECTORY_MONITOR); - + ep = g_io_extension_point_register (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME); g_io_extension_point_set_required_type (ep, G_TYPE_LOCAL_FILE_MONITOR); - - ep = g_io_extension_point_register (G_NFS_DIRECTORY_MONITOR_EXTENSION_POINT_NAME); - g_io_extension_point_set_required_type (ep, G_TYPE_LOCAL_DIRECTORY_MONITOR); ep = g_io_extension_point_register (G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME); g_io_extension_point_set_required_type (ep, G_TYPE_LOCAL_FILE_MONITOR); @@ -1070,20 +1063,17 @@ _g_io_modules_ensure_loaded (void) g_type_ensure (g_null_settings_backend_get_type ()); g_type_ensure (g_memory_settings_backend_get_type ()); #if defined(HAVE_INOTIFY_INIT1) - g_type_ensure (_g_inotify_directory_monitor_get_type ()); - g_type_ensure (_g_inotify_file_monitor_get_type ()); + g_type_ensure (g_inotify_file_monitor_get_type ()); #endif #if defined(HAVE_KQUEUE) - g_type_ensure (_g_kqueue_directory_monitor_get_type ()); - g_type_ensure (_g_kqueue_file_monitor_get_type ()); + g_type_ensure (g_kqueue_file_monitor_get_type ()); #endif #if defined(HAVE_FEN) - g_type_ensure (_g_fen_directory_monitor_get_type ()); - g_type_ensure (_g_fen_file_monitor_get_type ()); + g_type_ensure (g_fen_file_monitor_get_type ()); #endif #ifdef G_OS_WIN32 g_type_ensure (_g_win32_volume_monitor_get_type ()); - g_type_ensure (g_win32_directory_monitor_get_type ()); + g_type_ensure (g_win32_file_monitor_get_type ()); g_type_ensure (g_registry_backend_get_type ()); #endif #ifdef HAVE_COCOA diff --git a/gio/glocaldirectorymonitor.c b/gio/glocaldirectorymonitor.c deleted file mode 100644 index 9bc9fa6af..000000000 --- a/gio/glocaldirectorymonitor.c +++ /dev/null @@ -1,245 +0,0 @@ -/* GIO - GLib Input, Output and Streaming Library - * - * Copyright (C) 2006-2007 Red Hat, Inc. - * - * 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, see . - * - * Author: Alexander Larsson - */ - -#include "config.h" - -#include "glocaldirectorymonitor.h" -#include "gunixmounts.h" -#include "giomodule-priv.h" -#include "gfile.h" -#include "gioerror.h" -#include "glibintl.h" - -#include - - -enum -{ - PROP_0, - PROP_DIRNAME, - PROP_FLAGS -}; - -static gboolean g_local_directory_monitor_cancel (GFileMonitor *monitor); -static void mounts_changed (GUnixMountMonitor *mount_monitor, - gpointer user_data); - -G_DEFINE_ABSTRACT_TYPE (GLocalDirectoryMonitor, g_local_directory_monitor, G_TYPE_FILE_MONITOR) - -static void -g_local_directory_monitor_finalize (GObject *object) -{ - GLocalDirectoryMonitor *local_monitor; - local_monitor = G_LOCAL_DIRECTORY_MONITOR (object); - - g_free (local_monitor->dirname); - - if (local_monitor->mount_monitor) - { - g_signal_handlers_disconnect_by_func (local_monitor->mount_monitor, mounts_changed, local_monitor); - g_object_unref (local_monitor->mount_monitor); - local_monitor->mount_monitor = NULL; - } - - G_OBJECT_CLASS (g_local_directory_monitor_parent_class)->finalize (object); -} - -static void -g_local_directory_monitor_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - GLocalDirectoryMonitor *local_monitor = G_LOCAL_DIRECTORY_MONITOR (object); - - switch (property_id) - { - case PROP_DIRNAME: - local_monitor->dirname = g_value_dup_string (value); - break; - - case PROP_FLAGS: - local_monitor->flags = g_value_get_flags (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -void -g_local_directory_monitor_start (GLocalDirectoryMonitor *local_monitor) -{ - GLocalDirectoryMonitorClass *class; - - class = G_LOCAL_DIRECTORY_MONITOR_GET_CLASS (local_monitor); - - if (!class->mount_notify && (local_monitor->flags & G_FILE_MONITOR_WATCH_MOUNTS)) - { -#ifdef G_OS_WIN32 - /*claim everything was mounted */ - local_monitor->was_mounted = TRUE; -#else - GUnixMountEntry *mount; - - /* Emulate unmount detection */ - - mount = g_unix_mount_at (local_monitor->dirname, NULL); - - local_monitor->was_mounted = mount != NULL; - - if (mount) - g_unix_mount_free (mount); - - local_monitor->mount_monitor = g_unix_mount_monitor_get (); - g_signal_connect_object (local_monitor->mount_monitor, "mounts-changed", - G_CALLBACK (mounts_changed), local_monitor, 0); -#endif - } - - if (class->start) - class->start (local_monitor); -} - -static void -g_local_directory_monitor_class_init (GLocalDirectoryMonitorClass* klass) -{ - GObjectClass* gobject_class = G_OBJECT_CLASS (klass); - GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (klass); - - gobject_class->finalize = g_local_directory_monitor_finalize; - gobject_class->set_property = g_local_directory_monitor_set_property; - - file_monitor_class->cancel = g_local_directory_monitor_cancel; - - g_object_class_install_property (gobject_class, - PROP_DIRNAME, - g_param_spec_string ("dirname", - P_("Directory name"), - P_("Directory to monitor"), - NULL, - G_PARAM_CONSTRUCT_ONLY| - G_PARAM_WRITABLE| - G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB)); - g_object_class_install_property (gobject_class, - PROP_FLAGS, - g_param_spec_flags ("flags", - P_("Monitor flags"), - P_("Monitor flags"), - G_TYPE_FILE_MONITOR_FLAGS, - 0, - G_PARAM_CONSTRUCT_ONLY| - G_PARAM_WRITABLE| - G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB)); - - klass->mount_notify = FALSE; -} - -static void -g_local_directory_monitor_init (GLocalDirectoryMonitor *local_monitor) -{ -} - -static void -mounts_changed (GUnixMountMonitor *mount_monitor, - gpointer user_data) -{ - GLocalDirectoryMonitor *local_monitor = user_data; -#ifdef G_OS_UNIX - GUnixMountEntry *mount; -#endif - gboolean is_mounted; - GFile *file; - - /* Emulate unmount detection */ -#ifdef G_OS_UNIX - mount = g_unix_mount_at (local_monitor->dirname, NULL); - - is_mounted = mount != NULL; - - if (mount) - g_unix_mount_free (mount); -#else - /*claim everything was mounted */ - is_mounted = TRUE; -#endif - - if (local_monitor->was_mounted != is_mounted) - { - if (local_monitor->was_mounted && !is_mounted) - { - file = g_file_new_for_path (local_monitor->dirname); - g_file_monitor_emit_event (G_FILE_MONITOR (local_monitor), - file, NULL, - G_FILE_MONITOR_EVENT_UNMOUNTED); - g_object_unref (file); - } - local_monitor->was_mounted = is_mounted; - } -} - -GFileMonitor* -_g_local_directory_monitor_new (const char *dirname, - GFileMonitorFlags flags, - GMainContext *context, - gboolean is_remote_fs, - gboolean do_start, - GError **error) -{ - GFileMonitor *monitor = NULL; - GType type = G_TYPE_INVALID; - - if (is_remote_fs) - type = _g_io_module_get_default_type (G_NFS_DIRECTORY_MONITOR_EXTENSION_POINT_NAME, - "GIO_USE_FILE_MONITOR", - G_STRUCT_OFFSET (GLocalDirectoryMonitorClass, is_supported)); - - if (type == G_TYPE_INVALID) - type = _g_io_module_get_default_type (G_LOCAL_DIRECTORY_MONITOR_EXTENSION_POINT_NAME, - "GIO_USE_FILE_MONITOR", - G_STRUCT_OFFSET (GLocalDirectoryMonitorClass, is_supported)); - - if (type != G_TYPE_INVALID) - monitor = G_FILE_MONITOR (g_object_new (type, "dirname", dirname, "flags", flags, "context", context, NULL)); - else - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - _("Unable to find default local directory monitor type")); - - if (monitor && do_start) - g_local_directory_monitor_start (G_LOCAL_DIRECTORY_MONITOR (monitor)); - - return monitor; -} - -static gboolean -g_local_directory_monitor_cancel (GFileMonitor *monitor) -{ - GLocalDirectoryMonitor *local_monitor = G_LOCAL_DIRECTORY_MONITOR (monitor); - - if (local_monitor->mount_monitor) - { - g_signal_handlers_disconnect_by_func (local_monitor->mount_monitor, mounts_changed, local_monitor); - g_object_unref (local_monitor->mount_monitor); - local_monitor->mount_monitor = NULL; - } - - return TRUE; -} diff --git a/gio/glocaldirectorymonitor.h b/gio/glocaldirectorymonitor.h deleted file mode 100644 index b873404ed..000000000 --- a/gio/glocaldirectorymonitor.h +++ /dev/null @@ -1,84 +0,0 @@ -/* GIO - GLib Input, Output and Streaming Library - * - * Copyright (C) 2006-2007 Red Hat, Inc. - * - * 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, see . - * - * Author: Alexander Larsson - */ - -#ifndef __G_LOCAL_DIRECTORY_MONITOR_H__ -#define __G_LOCAL_DIRECTORY_MONITOR_H__ - -#include - -#include "gunixmounts.h" - -G_BEGIN_DECLS - -#define G_TYPE_LOCAL_DIRECTORY_MONITOR (g_local_directory_monitor_get_type ()) -#define G_LOCAL_DIRECTORY_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_LOCAL_DIRECTORY_MONITOR, GLocalDirectoryMonitor)) -#define G_LOCAL_DIRECTORY_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_LOCAL_DIRECTORY_MONITOR, GLocalDirectoryMonitorClass)) -#define G_IS_LOCAL_DIRECTORY_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_LOCAL_DIRECTORY_MONITOR)) -#define G_IS_LOCAL_DIRECTORY_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_LOCAL_DIRECTORY_MONITOR)) -#define G_LOCAL_DIRECTORY_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_LOCAL_DIRECTORY_MONITOR, GLocalDirectoryMonitorClass)) - -#define G_LOCAL_DIRECTORY_MONITOR_EXTENSION_POINT_NAME "gio-local-directory-monitor" -#define G_NFS_DIRECTORY_MONITOR_EXTENSION_POINT_NAME "gio-nfs-directory-monitor" - -typedef struct _GLocalDirectoryMonitor GLocalDirectoryMonitor; -typedef struct _GLocalDirectoryMonitorClass GLocalDirectoryMonitorClass; - -struct _GLocalDirectoryMonitor -{ - GFileMonitor parent_instance; - - gchar *dirname; - GFileMonitorFlags flags; - /* For mount emulation */ - gboolean was_mounted; - GUnixMountMonitor *mount_monitor; -}; - -struct _GLocalDirectoryMonitorClass -{ - GFileMonitorClass parent_class; - - gboolean mount_notify; - - gboolean (* is_supported) (void); - void (* start) (GLocalDirectoryMonitor *local_monitor); -}; - -#ifdef G_OS_UNIX -GLIB_AVAILABLE_IN_ALL -#endif -GType g_local_directory_monitor_get_type (void) G_GNUC_CONST; - -GFileMonitor * _g_local_directory_monitor_new (const char *dirname, - GFileMonitorFlags flags, - GMainContext *context, - gboolean is_remote_fs, - gboolean do_start, - GError **error); -void g_local_directory_monitor_start (GLocalDirectoryMonitor *local_monitor); - -/* Actually in glocalfile.c */ -GLocalDirectoryMonitor * g_local_directory_monitor_new_in_worker (const char *pathname, - GFileMonitorFlags flags, - GError **error); - -G_END_DECLS - -#endif /* __G_LOCAL_DIRECTORY_MONITOR_H__ */ diff --git a/gio/glocalfile.c b/gio/glocalfile.c index 20e45ab1f..eed3a99e3 100644 --- a/gio/glocalfile.c +++ b/gio/glocalfile.c @@ -56,7 +56,6 @@ #include "glocalfileinputstream.h" #include "glocalfileoutputstream.h" #include "glocalfileiostream.h" -#include "glocaldirectorymonitor.h" #include "glocalfilemonitor.h" #include "gmountprivate.h" #include "gunixmounts.h" @@ -2451,8 +2450,8 @@ g_local_file_move (GFile *source, #ifdef G_OS_WIN32 -static gboolean -is_remote (const gchar *filename) +gboolean +g_local_file_is_remote (const gchar *filename) { return FALSE; } @@ -2508,8 +2507,8 @@ is_remote_fs (const gchar *filename) return FALSE; } -static gboolean -is_remote (const gchar *filename) +gboolean +g_local_file_is_remote (const gchar *filename) { static gboolean remote_home; static gsize initialized; @@ -2536,8 +2535,9 @@ g_local_file_monitor_dir (GFile *file, GCancellable *cancellable, GError **error) { - GLocalFile* local_file = G_LOCAL_FILE(file); - return _g_local_directory_monitor_new (local_file->filename, flags, NULL, is_remote (local_file->filename), TRUE, error); + GLocalFile *local_file = G_LOCAL_FILE (file); + + return g_local_file_monitor_new_for_path (local_file->filename, TRUE, flags, error); } static GFileMonitor* @@ -2546,31 +2546,11 @@ g_local_file_monitor_file (GFile *file, GCancellable *cancellable, GError **error) { - GLocalFile* local_file = G_LOCAL_FILE(file); - return _g_local_file_monitor_new (local_file->filename, flags, NULL, is_remote (local_file->filename), TRUE, error); -} + GLocalFile *local_file = G_LOCAL_FILE (file); -GLocalDirectoryMonitor * -g_local_directory_monitor_new_in_worker (const char *pathname, - GFileMonitorFlags flags, - GError **error) -{ - return (gpointer) _g_local_directory_monitor_new (pathname, flags, - GLIB_PRIVATE_CALL (g_get_worker_context) (), - is_remote (pathname), FALSE, error); + return g_local_file_monitor_new_for_path (local_file->filename, FALSE, flags, error); } -GLocalFileMonitor * -g_local_file_monitor_new_in_worker (const char *pathname, - GFileMonitorFlags flags, - GError **error) -{ - return (gpointer) _g_local_file_monitor_new (pathname, flags, - GLIB_PRIVATE_CALL (g_get_worker_context) (), - is_remote (pathname), FALSE, error); -} - - /* Here is the GLocalFile implementation of g_file_measure_disk_usage(). * * If available, we use fopenat() in preference to filenames for diff --git a/gio/glocalfile.h b/gio/glocalfile.h index e6cf4bfe5..6b1422295 100644 --- a/gio/glocalfile.h +++ b/gio/glocalfile.h @@ -46,6 +46,8 @@ GFile * _g_local_file_new (const char *filename); const char * _g_local_file_get_filename (GLocalFile *file); +gboolean g_local_file_is_remote (const gchar *filename); + GFile * g_local_file_new_from_dirname_and_basename (const char *dirname, const char *basename); diff --git a/gio/glocalfilemonitor.c b/gio/glocalfilemonitor.c index 818440b6e..1d5d9a983 100644 --- a/gio/glocalfilemonitor.c +++ b/gio/glocalfilemonitor.c @@ -1,5 +1,5 @@ /* GIO - GLib Input, Output and Streaming Library - * + * * Copyright (C) 2006-2007 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -25,57 +25,756 @@ #include "giomodule-priv.h" #include "gioerror.h" #include "glibintl.h" +#include "glocalfile.h" +#include "glib-private.h" #include +#define DEFAULT_RATE_LIMIT 800 * G_TIME_SPAN_MILLISECOND +#define VIRTUAL_CHANGES_DONE_DELAY 2 * G_TIME_SPAN_SECOND -enum -{ - PROP_0, - PROP_FILENAME, - PROP_FLAGS +/* GFileMonitorSource is a GSource responsible for emitting the changed + * signals in the owner-context of the GFileMonitor. + * + * It contains functionality for cross-thread queuing of events. It + * also handles merging of CHANGED events and emission of CHANGES_DONE + * events. + * + * We use the "priv" pointer in the external struct to store it. + */ +struct _GFileMonitorSource { + GSource source; + + GMutex lock; + gpointer instance; + GFileMonitorFlags flags; + gchar *dirname; + gchar *basename; + gchar *filename; + GSequence *pending_changes; /* sorted by ready time */ + GHashTable *pending_changes_table; + GQueue event_queue; + gint64 rate_limit; }; -G_DEFINE_ABSTRACT_TYPE (GLocalFileMonitor, g_local_file_monitor, G_TYPE_FILE_MONITOR) +/* PendingChange is a struct to keep track of a file that needs to have + * (at least) a CHANGES_DONE_HINT event sent for it in the near future. + * + * If 'dirty' is TRUE then a CHANGED event also needs to be sent. + * + * last_emission is the last time a CHANGED event was emitted. It is + * used to calculate the time to send the next event. + */ +typedef struct { + gchar *child; + guint64 last_emission : 63; + guint64 dirty : 1; +} PendingChange; -static void -g_local_file_monitor_init (GLocalFileMonitor* local_monitor) +/* QueuedEvent is a signal that will be sent immediately, as soon as the + * source gets a chance to dispatch. The existence of any queued event + * implies that the source is ready now. + */ +typedef struct { + GFileMonitorEvent event_type; + GFile *child; + GFile *other; +} QueuedEvent; + +static gint64 +pending_change_get_ready_time (const PendingChange *change, + GFileMonitorSource *fms) +{ + if (change->dirty) + return change->last_emission + fms->rate_limit; + else + return change->last_emission + VIRTUAL_CHANGES_DONE_DELAY; +} + +static int +pending_change_compare_ready_time (gconstpointer a_p, + gconstpointer b_p, + gpointer user_data) +{ + GFileMonitorSource *fms = user_data; + const PendingChange *a = a_p; + const PendingChange *b = b_p; + gint64 ready_time_a; + gint64 ready_time_b; + + ready_time_a = pending_change_get_ready_time (a, fms); + ready_time_b = pending_change_get_ready_time (b, fms); + + if (ready_time_a < ready_time_b) + return -1; + else + return ready_time_a > ready_time_b; } static void -g_local_file_monitor_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) +pending_change_free (gpointer data) { - GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object); + PendingChange *change = data; - switch (property_id) + g_free (change->child); + + g_slice_free (PendingChange, change); +} + +static void +queued_event_free (QueuedEvent *event) +{ + g_object_unref (event->child); + if (event->other) + g_object_unref (event->other); + + g_slice_free (QueuedEvent, event); +} + +static gint64 +g_file_monitor_source_get_ready_time (GFileMonitorSource *fms) +{ + GSequenceIter *iter; + + if (fms->event_queue.length) + return 0; + + iter = g_sequence_get_begin_iter (fms->pending_changes); + if (g_sequence_iter_is_end (iter)) + return -1; + + return pending_change_get_ready_time (g_sequence_get (iter), fms); +} + +static void +g_file_monitor_source_update_ready_time (GFileMonitorSource *fms) +{ + g_source_set_ready_time ((GSource *) fms, g_file_monitor_source_get_ready_time (fms)); +} + +static GSequenceIter * +g_file_monitor_source_find_pending_change (GFileMonitorSource *fms, + const gchar *child) +{ + return g_hash_table_lookup (fms->pending_changes_table, child); +} + +static void +g_file_monitor_source_add_pending_change (GFileMonitorSource *fms, + const gchar *child, + gint64 now) +{ + PendingChange *change; + GSequenceIter *iter; + + change = g_slice_new (PendingChange); + change->child = g_strdup (child); + change->last_emission = now; + change->dirty = FALSE; + + iter = g_sequence_insert_sorted (fms->pending_changes, change, pending_change_compare_ready_time, fms); + g_hash_table_insert (fms->pending_changes_table, change->child, iter); +} + +static void +g_file_monitor_source_set_pending_change_dirty (GFileMonitorSource *fms, + GSequenceIter *iter) +{ + PendingChange *change; + + change = g_sequence_get (iter); + + if (!change->dirty) { - case PROP_FILENAME: - local_monitor->filename = g_value_dup_string (value); - break; + change->dirty = TRUE; - case PROP_FLAGS: - local_monitor->flags = g_value_get_flags (value); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; + g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms); } } -void -g_local_file_monitor_start (GLocalFileMonitor *local_monitor) +static gboolean +g_file_monitor_source_get_pending_change_dirty (GFileMonitorSource *fms, + GSequenceIter *iter) { - GLocalFileMonitorClass *class; + PendingChange *change; - class = G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor); + change = g_sequence_get (iter); - if (class->start) - class->start (local_monitor); + return change->dirty; +} + +static void +g_file_monitor_source_remove_pending_change (GFileMonitorSource *fms, + GSequenceIter *iter, + const gchar *child) +{ + /* must remove the hash entry first -- its key is owned by the data + * which will be freed when removing the sequence iter + */ + g_hash_table_remove (fms->pending_changes_table, child); + g_sequence_remove (iter); +} + +static void +g_file_monitor_source_queue_event (GFileMonitorSource *fms, + GFileMonitorEvent event_type, + const gchar *child, + GFile *other) +{ + QueuedEvent *event; + + event = g_slice_new (QueuedEvent); + event->event_type = event_type; + if (child) + event->child = g_local_file_new_from_dirname_and_basename (fms->dirname, child); + else if (fms->dirname) + event->child = _g_local_file_new (fms->dirname); + else if (fms->filename) + event->child = _g_local_file_new (fms->filename); + event->other = other; + if (other) + g_object_ref (other); + + g_queue_push_tail (&fms->event_queue, event); +} + +static void +g_file_monitor_source_file_changed (GFileMonitorSource *fms, + const gchar *child, + gint64 now) +{ + GSequenceIter *pending; + + pending = g_file_monitor_source_find_pending_change (fms, child); + + /* If there is no pending change, emit one and create a record, + * else: just mark the existing record as dirty. + */ + if (!pending) + { + g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL); + g_file_monitor_source_add_pending_change (fms, child, now); + } + else + g_file_monitor_source_set_pending_change_dirty (fms, pending); + + g_file_monitor_source_update_ready_time (fms); +} + +static void +g_file_monitor_source_file_changes_done (GFileMonitorSource *fms, + const gchar *child) +{ + GSequenceIter *pending; + + pending = g_file_monitor_source_find_pending_change (fms, child); + if (pending) + { + /* If it is dirty, make sure we push out the last CHANGED event */ + if (g_file_monitor_source_get_pending_change_dirty (fms, pending)) + g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL); + + g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL); + g_file_monitor_source_remove_pending_change (fms, pending, child); + } +} + +static void +g_file_monitor_source_file_created (GFileMonitorSource *fms, + const gchar *child, + gint64 event_time) +{ + /* Unlikely, but if we have pending changes for this filename, make + * sure we flush those out first, before creating the new ones. + */ + g_file_monitor_source_file_changes_done (fms, child); + + /* Emit CREATE and add a pending changes record */ + g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL); + g_file_monitor_source_add_pending_change (fms, child, event_time); +} + +static void +g_file_monitor_source_send_event (GFileMonitorSource *fms, + GFileMonitorEvent event_type, + const gchar *child, + GFile *other) +{ + /* always flush any pending changes before we queue a new event */ + g_file_monitor_source_file_changes_done (fms, child); + g_file_monitor_source_queue_event (fms, event_type, child, other); +} + +static void +g_file_monitor_source_send_synthetic_created (GFileMonitorSource *fms, + const gchar *child) +{ + g_file_monitor_source_file_changes_done (fms, child); + g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL); + g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL); +} + +static gboolean +is_basename (const gchar *name) +{ + if (name[0] == '.' && ((name[1] == '.' && name[2] == '\0') || name[1] == '\0')) + return FALSE; + + return !strchr (name, '/'); +} + +void +g_file_monitor_source_handle_event (GFileMonitorSource *fms, + GFileMonitorEvent event_type, + const gchar *child, + const gchar *rename_to, + GFile *other, + gint64 event_time) +{ + g_assert (!child || is_basename (child)); + g_assert (!rename_to || is_basename (rename_to)); + + if (fms->basename && (!child || !g_str_equal (child, fms->basename))) + return; + + g_mutex_lock (&fms->lock); + + /* monitor is already gone -- don't bother */ + if (!fms->instance) + { + g_mutex_unlock (&fms->lock); + return; + } + + switch (event_type) + { + case G_FILE_MONITOR_EVENT_CREATED: + g_assert (!other && !rename_to); + g_file_monitor_source_file_created (fms, child, event_time); + break; + + case G_FILE_MONITOR_EVENT_CHANGED: + g_assert (!other && !rename_to); + g_file_monitor_source_file_changed (fms, child, event_time); + break; + + case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT: + g_assert (!other && !rename_to); + g_file_monitor_source_file_changes_done (fms, child); + break; + + case G_FILE_MONITOR_EVENT_MOVED_IN: + g_assert (!rename_to); + if (fms->flags & G_FILE_MONITOR_WATCH_MOVES) + g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_IN, child, other); + else + g_file_monitor_source_send_synthetic_created (fms, child); + break; + + case G_FILE_MONITOR_EVENT_MOVED_OUT: + g_assert (!rename_to); + if (fms->flags & G_FILE_MONITOR_WATCH_MOVES) + g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_OUT, child, other); + else if (fms->flags & G_FILE_MONITOR_SEND_MOVED) + g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED, child, other); + else + g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL); + break; + + case G_FILE_MONITOR_EVENT_RENAMED: + g_assert (!other && rename_to); + if (fms->flags & G_FILE_MONITOR_WATCH_MOVES) + { + GFile *other; + + other = g_local_file_new_from_dirname_and_basename (fms->dirname, rename_to); + g_file_monitor_source_file_changes_done (fms, rename_to); + g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_RENAMED, child, other); + g_object_unref (other); + } + else if (fms->flags & G_FILE_MONITOR_SEND_MOVED) + { + GFile *other; + + other = g_local_file_new_from_dirname_and_basename (fms->dirname, rename_to); + g_file_monitor_source_file_changes_done (fms, rename_to); + g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED, child, other); + g_object_unref (other); + } + else + { + g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL); + g_file_monitor_source_send_synthetic_created (fms, child); + } + break; + + case G_FILE_MONITOR_EVENT_DELETED: + case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: + case G_FILE_MONITOR_EVENT_PRE_UNMOUNT: + case G_FILE_MONITOR_EVENT_UNMOUNTED: + g_assert (!other && !rename_to); + g_file_monitor_source_send_event (fms, event_type, child, NULL); + break; + + case G_FILE_MONITOR_EVENT_MOVED: + /* was never available in this API */ + default: + g_assert_not_reached (); + } + + g_file_monitor_source_update_ready_time (fms); + + g_mutex_unlock (&fms->lock); +} + +static gint64 +g_file_monitor_source_get_rate_limit (GFileMonitorSource *fms) +{ + gint64 rate_limit; + + g_mutex_lock (&fms->lock); + rate_limit = fms->rate_limit; + g_mutex_unlock (&fms->lock); + + return rate_limit; +} + +static gboolean +g_file_monitor_source_set_rate_limit (GFileMonitorSource *fms, + gint64 rate_limit) +{ + gboolean changed; + + g_mutex_lock (&fms->lock); + + if (rate_limit != fms->rate_limit) + { + fms->rate_limit = rate_limit; + + g_sequence_sort (fms->pending_changes, pending_change_compare_ready_time, fms); + g_file_monitor_source_update_ready_time (fms); + } + else + changed = FALSE; + + g_mutex_unlock (&fms->lock); + + return changed; +} + +static gboolean +g_file_monitor_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + GFileMonitorSource *fms = (GFileMonitorSource *) source; + QueuedEvent *event; + GQueue event_queue; + gint64 now; + + /* make sure the monitor still exists */ + if (!fms->instance) + return FALSE; + + now = g_source_get_time (source); + + /* Acquire the lock once and grab all events in one go, handling the + * queued events first. This avoids strange possibilities in cases of + * long delays, such as CHANGED events coming before CREATED events. + * + * We do this by converting the applicable pending changes into queued + * events (after the ones already queued) and then stealing the entire + * event queue in one go. + */ + g_mutex_lock (&fms->lock); + + /* Create events for any pending changes that are due to fire */ + while (g_sequence_get_length (fms->pending_changes)) + { + GSequenceIter *iter = g_sequence_get_begin_iter (fms->pending_changes); + PendingChange *pending = g_sequence_get (iter); + + /* We've gotten to a pending change that's not ready. Stop. */ + if (pending_change_get_ready_time (pending, fms) > now) + break; + + if (pending->dirty) + { + /* It's time to send another CHANGED and update the record */ + g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, pending->child, NULL); + pending->last_emission = now; + pending->dirty = FALSE; + + g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms); + } + else + { + /* It's time to send CHANGES_DONE and remove the pending record */ + g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, pending->child, NULL); + g_file_monitor_source_remove_pending_change (fms, iter, pending->child); + } + } + + /* Steal the queue */ + memcpy (&event_queue, &fms->event_queue, sizeof event_queue); + memset (&fms->event_queue, 0, sizeof fms->event_queue); + + g_file_monitor_source_update_ready_time (fms); + + g_mutex_unlock (&fms->lock); + + /* We now have our list of events to deliver */ + while ((event = g_queue_pop_head (&event_queue))) + { + /* an event handler could destroy 'instance', so check each time */ + if (fms->instance) + g_file_monitor_emit_event (fms->instance, event->child, event->other, event->event_type); + + queued_event_free (event); + } + + return TRUE; +} + +static void +g_file_monitor_source_dispose (GFileMonitorSource *fms) +{ + g_mutex_lock (&fms->lock); + + if (fms->instance) + { + GHashTableIter iter; + gpointer seqiter; + QueuedEvent *event; + + g_hash_table_iter_init (&iter, fms->pending_changes_table); + while (g_hash_table_iter_next (&iter, NULL, &seqiter)) + { + g_hash_table_iter_remove (&iter); + g_sequence_remove (seqiter); + } + + while ((event = g_queue_pop_head (&fms->event_queue))) + queued_event_free (event); + + g_assert (g_sequence_get_length (fms->pending_changes) == 0); + g_assert (g_hash_table_size (fms->pending_changes_table) == 0); + g_assert (fms->event_queue.length == 0); + fms->instance = NULL; + + g_file_monitor_source_update_ready_time (fms); + } + + g_mutex_unlock (&fms->lock); + + g_source_destroy ((GSource *) fms); +} + +static void +g_file_monitor_source_finalize (GSource *source) +{ + GFileMonitorSource *fms = (GFileMonitorSource *) source; + + /* should already have been cleared in dispose of the monitor */ + g_assert (fms->instance == NULL); + g_assert (g_sequence_get_length (fms->pending_changes) == 0); + g_assert (g_hash_table_size (fms->pending_changes_table) == 0); + g_assert (fms->event_queue.length == 0); + + g_hash_table_unref (fms->pending_changes_table); + g_sequence_free (fms->pending_changes); + + g_free (fms->dirname); + g_free (fms->basename); + g_free (fms->filename); + + g_mutex_clear (&fms->lock); +} + +static guint +str_hash0 (gconstpointer str) +{ + return str ? g_str_hash (str) : 0; +} + +static gboolean +str_equal0 (gconstpointer a, + gconstpointer b) +{ + return g_strcmp0 (a, b) == 0; +} + +static GFileMonitorSource * +g_file_monitor_source_new (gpointer instance, + const gchar *filename, + gboolean is_directory, + GFileMonitorFlags flags) +{ + static GSourceFuncs source_funcs = { + NULL, NULL, + g_file_monitor_source_dispatch, + g_file_monitor_source_finalize + }; + GFileMonitorSource *fms; + GSource *source; + + source = g_source_new (&source_funcs, sizeof (GFileMonitorSource)); + fms = (GFileMonitorSource *) source; + + g_mutex_init (&fms->lock); + fms->instance = instance; + fms->pending_changes = g_sequence_new (pending_change_free); + fms->pending_changes_table = g_hash_table_new (str_hash0, str_equal0); + fms->rate_limit = DEFAULT_RATE_LIMIT; + fms->flags = flags; + + if (is_directory) + { + fms->dirname = g_strdup (filename); + fms->basename = NULL; + fms->filename = NULL; + } + else if (flags & G_FILE_MONITOR_WATCH_HARD_LINKS) + { + fms->dirname = NULL; + fms->basename = NULL; + fms->filename = g_strdup (filename); + } + else + { + fms->dirname = g_path_get_dirname (filename); + fms->basename = g_path_get_basename (filename); + fms->filename = NULL; + } + + return fms; +} + +G_DEFINE_ABSTRACT_TYPE (GLocalFileMonitor, g_local_file_monitor, G_TYPE_FILE_MONITOR) + +enum { + PROP_0, + PROP_RATE_LIMIT, +}; + +static void +g_local_file_monitor_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GLocalFileMonitor *monitor = G_LOCAL_FILE_MONITOR (object); + gint64 rate_limit; + + g_assert (prop_id == PROP_RATE_LIMIT); + + rate_limit = g_file_monitor_source_get_rate_limit (monitor->source); + rate_limit /= G_TIME_SPAN_MILLISECOND; + + g_value_set_int (value, rate_limit); +} + +static void +g_local_file_monitor_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + GLocalFileMonitor *monitor = G_LOCAL_FILE_MONITOR (object); + gint64 rate_limit; + + g_assert (prop_id == PROP_RATE_LIMIT); + + rate_limit = g_value_get_int (value); + rate_limit *= G_TIME_SPAN_MILLISECOND; + + if (g_file_monitor_source_set_rate_limit (monitor->source, rate_limit)) + g_object_notify (object, "rate-limit"); +} + +#ifndef G_OS_WIN32 +static void +g_local_file_monitor_mounts_changed (GUnixMountMonitor *mount_monitor, + gpointer user_data) +{ + GLocalFileMonitor *local_monitor = user_data; + GUnixMountEntry *mount; + gboolean is_mounted; + GFile *file; + + /* Emulate unmount detection */ + mount = g_unix_mount_at (local_monitor->source->dirname, NULL); + + is_mounted = mount != NULL; + + if (mount) + g_unix_mount_free (mount); + + if (local_monitor->was_mounted != is_mounted) + { + if (local_monitor->was_mounted && !is_mounted) + { + file = g_file_new_for_path (local_monitor->source->dirname); + g_file_monitor_emit_event (G_FILE_MONITOR (local_monitor), file, NULL, G_FILE_MONITOR_EVENT_UNMOUNTED); + g_object_unref (file); + } + local_monitor->was_mounted = is_mounted; + } +} +#endif + +static void +g_local_file_monitor_start (GLocalFileMonitor *local_monitor, + const gchar *filename, + gboolean is_directory, + GFileMonitorFlags flags, + GMainContext *context) +{ + GLocalFileMonitorClass *class = G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor); + GFileMonitorSource *source; + + g_return_if_fail (G_IS_LOCAL_FILE_MONITOR (local_monitor)); + + g_assert (!local_monitor->source); + + if (is_directory && !class->mount_notify && (flags & G_FILE_MONITOR_WATCH_MOUNTS)) + { +#ifdef G_OS_WIN32 + /*claim everything was mounted */ + local_monitor->was_mounted = TRUE; +#else + GUnixMountEntry *mount; + + /* Emulate unmount detection */ + + mount = g_unix_mount_at (local_monitor->source->dirname, NULL); + + local_monitor->was_mounted = mount != NULL; + + if (mount) + g_unix_mount_free (mount); + + local_monitor->mount_monitor = g_unix_mount_monitor_get (); + g_signal_connect_object (local_monitor->mount_monitor, "mounts-changed", + G_CALLBACK (g_local_file_monitor_mounts_changed), local_monitor, 0); +#endif + } + + source = g_file_monitor_source_new (local_monitor, filename, is_directory, flags); + local_monitor->source = source; /* owns the ref */ + + G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor)->start (local_monitor, + source->dirname, source->basename, source->filename, + source); + + g_source_attach ((GSource *) source, context); +} + +static void +g_local_file_monitor_dispose (GObject *object) +{ + GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object); + + g_file_monitor_source_dispose (local_monitor->source); + + G_OBJECT_CLASS (g_local_file_monitor_parent_class)->dispose (object); } static void @@ -83,49 +782,32 @@ g_local_file_monitor_finalize (GObject *object) { GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object); - g_free (local_monitor->filename); + g_source_unref ((GSource *) local_monitor->source); G_OBJECT_CLASS (g_local_file_monitor_parent_class)->finalize (object); } -static void g_local_file_monitor_class_init (GLocalFileMonitorClass *klass) +static void +g_local_file_monitor_init (GLocalFileMonitor* local_monitor) { - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - - gobject_class->set_property = g_local_file_monitor_set_property; - gobject_class->finalize = g_local_file_monitor_finalize; - - g_object_class_install_property (gobject_class, - PROP_FILENAME, - g_param_spec_string ("filename", - P_("File name"), - P_("File name to monitor"), - NULL, - G_PARAM_CONSTRUCT_ONLY| - G_PARAM_WRITABLE| - G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB)); - - g_object_class_install_property (gobject_class, - PROP_FLAGS, - g_param_spec_flags ("flags", - P_("Monitor flags"), - P_("Monitor flags"), - G_TYPE_FILE_MONITOR_FLAGS, - 0, - G_PARAM_CONSTRUCT_ONLY| - G_PARAM_WRITABLE| - G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB)); } -GFileMonitor* -_g_local_file_monitor_new (const char *pathname, - GFileMonitorFlags flags, - GMainContext *context, - gboolean is_remote_fs, - gboolean do_start, - GError **error) +static void g_local_file_monitor_class_init (GLocalFileMonitorClass *class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (class); + + gobject_class->get_property = g_local_file_monitor_get_property; + gobject_class->set_property = g_local_file_monitor_set_property; + gobject_class->dispose = g_local_file_monitor_dispose; + gobject_class->finalize = g_local_file_monitor_finalize; + + g_object_class_override_property (gobject_class, PROP_RATE_LIMIT, "rate-limit"); +} + +static GLocalFileMonitor * +g_local_file_monitor_new (gboolean is_remote_fs, + GError **error) { - GFileMonitor *monitor = NULL; GType type = G_TYPE_INVALID; if (is_remote_fs) @@ -138,14 +820,57 @@ _g_local_file_monitor_new (const char *pathname, "GIO_USE_FILE_MONITOR", G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported)); - if (type != G_TYPE_INVALID) - monitor = G_FILE_MONITOR (g_object_new (type, "filename", pathname, "flags", flags, "context", context, NULL)); - else - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, - _("Unable to find default local file monitor type")); + if (type == G_TYPE_INVALID) + { + g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Unable to find default local file monitor type")); + return NULL; + } - if (monitor && do_start) - g_local_file_monitor_start (G_LOCAL_FILE_MONITOR (monitor)); - - return monitor; + return g_object_new (type, NULL); +} + +GFileMonitor * +g_local_file_monitor_new_for_path (const gchar *pathname, + gboolean is_directory, + GFileMonitorFlags flags, + GError **error) +{ + GLocalFileMonitor *monitor; + gboolean is_remote_fs; + + is_remote_fs = g_local_file_is_remote (pathname); + + monitor = g_local_file_monitor_new (is_remote_fs, error); + + if (monitor) + g_local_file_monitor_start (monitor, pathname, is_directory, flags, g_main_context_get_thread_default ()); + + return G_FILE_MONITOR (monitor); +} + +GFileMonitor * +g_local_file_monitor_new_in_worker (const gchar *pathname, + gboolean is_directory, + GFileMonitorFlags flags, + GFileMonitorCallback callback, + gpointer user_data, + GError **error) +{ + GLocalFileMonitor *monitor; + gboolean is_remote_fs; + + is_remote_fs = g_local_file_is_remote (pathname); + + monitor = g_local_file_monitor_new (is_remote_fs, error); + + if (monitor) + { + if (callback) + g_signal_connect (monitor, "changed", G_CALLBACK (callback), user_data); + + g_local_file_monitor_start (monitor, pathname, is_directory, flags, GLIB_PRIVATE_CALL(g_get_worker_context) ()); + } + + return G_FILE_MONITOR (monitor); } diff --git a/gio/glocalfilemonitor.h b/gio/glocalfilemonitor.h index aed2e9cd1..ced880fd6 100644 --- a/gio/glocalfilemonitor.h +++ b/gio/glocalfilemonitor.h @@ -22,6 +22,7 @@ #define __G_LOCAL_FILE_MONITOR_H__ #include +#include "gunixmounts.h" G_BEGIN_DECLS @@ -37,13 +38,15 @@ G_BEGIN_DECLS typedef struct _GLocalFileMonitor GLocalFileMonitor; typedef struct _GLocalFileMonitorClass GLocalFileMonitorClass; +typedef struct _GFileMonitorSource GFileMonitorSource; struct _GLocalFileMonitor { GFileMonitor parent_instance; - gchar *filename; - GFileMonitorFlags flags; + GFileMonitorSource *source; + GUnixMountMonitor *mount_monitor; + gboolean was_mounted; }; struct _GLocalFileMonitorClass @@ -51,7 +54,13 @@ struct _GLocalFileMonitorClass GFileMonitorClass parent_class; gboolean (* is_supported) (void); - void (* start) (GLocalFileMonitor *local_monitor); + void (* start) (GLocalFileMonitor *local_monitor, + const gchar *dirname, + const gchar *basename, + const gchar *filename, + GFileMonitorSource *source); + + gboolean mount_notify; }; #ifdef G_OS_UNIX @@ -59,18 +68,37 @@ GLIB_AVAILABLE_IN_ALL #endif GType g_local_file_monitor_get_type (void) G_GNUC_CONST; -GFileMonitor * _g_local_file_monitor_new (const char *pathname, - GFileMonitorFlags flags, - GMainContext *context, - gboolean is_remote_fs, - gboolean do_start, - GError **error); -void g_local_file_monitor_start (GLocalFileMonitor *local_monitor); +/* for glocalfile.c */ +GFileMonitor * +g_local_file_monitor_new_for_path (const gchar *pathname, + gboolean is_directory, + GFileMonitorFlags flags, + GError **error); + +/* for various users in glib */ +typedef void (* GFileMonitorCallback) (GFileMonitor *monitor, + GFile *child, + GFile *other, + GFileMonitorEvent event, + gpointer user_data); +GFileMonitor * +g_local_file_monitor_new_in_worker (const gchar *pathname, + gboolean is_directory, + GFileMonitorFlags flags, + GFileMonitorCallback callback, + gpointer user_data, + GError **error); + +/* for implementations of GLocalFileMonitor */ +GLIB_AVAILABLE_IN_2_44 +void +g_file_monitor_source_handle_event (GFileMonitorSource *fms, + GFileMonitorEvent event_type, + const gchar *child, + const gchar *rename_to, + GFile *other, + gint64 event_time); -/* Actually in glocalfile.c */ -GLocalFileMonitor * g_local_file_monitor_new_in_worker (const char *pathname, - GFileMonitorFlags flags, - GError **error); G_END_DECLS diff --git a/gio/inotify/Makefile.am b/gio/inotify/Makefile.am index 283868553..8b4b3ae18 100644 --- a/gio/inotify/Makefile.am +++ b/gio/inotify/Makefile.am @@ -15,8 +15,6 @@ libinotify_la_SOURCES = \ inotify-helper.h \ ginotifyfilemonitor.c \ ginotifyfilemonitor.h \ - ginotifydirectorymonitor.c \ - ginotifydirectorymonitor.h \ $(NULL) libinotify_la_CFLAGS = \ diff --git a/gio/inotify/ginotifydirectorymonitor.c b/gio/inotify/ginotifydirectorymonitor.c index cd4a61091..80a2d6f83 100644 --- a/gio/inotify/ginotifydirectorymonitor.c +++ b/gio/inotify/ginotifydirectorymonitor.c @@ -31,7 +31,7 @@ struct _GInotifyDirectoryMonitor { - GLocalDirectoryMonitor parent_instance; + GLocalFileMonitor parent_instance; inotify_sub *sub; }; diff --git a/gio/inotify/ginotifyfilemonitor.c b/gio/inotify/ginotifyfilemonitor.c index e122d671b..15a113139 100644 --- a/gio/inotify/ginotifyfilemonitor.c +++ b/gio/inotify/ginotifyfilemonitor.c @@ -19,6 +19,7 @@ * Authors: Alexander Larsson * John McCutchan * Sebastian Dröge + * Ryan Lortie */ #include "config.h" @@ -32,81 +33,13 @@ struct _GInotifyFileMonitor { GLocalFileMonitor parent_instance; - gchar *filename; - gchar *dirname; + inotify_sub *sub; - gboolean pair_moves; }; -static gboolean g_inotify_file_monitor_cancel (GFileMonitor* monitor); - -#define g_inotify_file_monitor_get_type _g_inotify_file_monitor_get_type G_DEFINE_TYPE_WITH_CODE (GInotifyFileMonitor, g_inotify_file_monitor, G_TYPE_LOCAL_FILE_MONITOR, - g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME, - g_define_type_id, - "inotify", - 20)) - -static void -g_inotify_file_monitor_finalize (GObject *object) -{ - GInotifyFileMonitor *inotify_monitor = G_INOTIFY_FILE_MONITOR (object); - inotify_sub *sub = inotify_monitor->sub; - - if (sub) - { - _ih_sub_cancel (sub); - _ih_sub_free (sub); - inotify_monitor->sub = NULL; - } - - g_free (inotify_monitor->filename); - g_free (inotify_monitor->dirname); - - G_OBJECT_CLASS (g_inotify_file_monitor_parent_class)->finalize (object); -} - -static void -g_inotify_file_monitor_start (GLocalFileMonitor *local_monitor) -{ - GInotifyFileMonitor *inotify_monitor = G_INOTIFY_FILE_MONITOR (local_monitor); - const gchar *filename = NULL; - gboolean watch_hardlinks; - inotify_sub *sub = NULL; - gboolean pair_moves; - gboolean ret_ih_startup; /* return value of _ih_startup, for asserting */ - - filename = local_monitor->filename; - g_assert (filename != NULL); - - inotify_monitor->filename = g_path_get_basename (filename); - inotify_monitor->dirname = g_path_get_dirname (filename); - - /* Will never fail as is_supported() should be called before instantiating - * anyway */ - /* assert on return value */ - ret_ih_startup = _ih_startup(); - g_assert (ret_ih_startup); - - pair_moves = local_monitor->flags & G_FILE_MONITOR_SEND_MOVED; - watch_hardlinks = local_monitor->flags & G_FILE_MONITOR_WATCH_HARD_LINKS; - - sub = _ih_sub_new (inotify_monitor->dirname, - inotify_monitor->filename, - pair_moves, - watch_hardlinks, - inotify_monitor); - - /* FIXME: what to do about errors here? we can't return NULL or another - * kind of error and an assertion is probably too hard */ - g_assert (sub != NULL); - - /* _ih_sub_add allways returns TRUE, see gio/inotify/inotify-helper.c line 109 - * g_assert (_ih_sub_add (sub)); */ - _ih_sub_add (sub); - - inotify_monitor->sub = sub; -} + g_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME, + g_define_type_id, "inotify", 20)) static gboolean g_inotify_file_monitor_is_supported (void) @@ -114,6 +47,55 @@ g_inotify_file_monitor_is_supported (void) return _ih_startup (); } +static void +g_inotify_file_monitor_start (GLocalFileMonitor *local_monitor, + const gchar *dirname, + const gchar *basename, + const gchar *filename, + GFileMonitorSource *source) +{ + GInotifyFileMonitor *inotify_monitor = G_INOTIFY_FILE_MONITOR (local_monitor); + gboolean success; + + /* should already have been called, from is_supported() */ + success = _ih_startup (); + g_assert (success); + + inotify_monitor->sub = _ih_sub_new (dirname, basename, filename != NULL, source); + _ih_sub_add (inotify_monitor->sub); +} + +static gboolean +g_inotify_file_monitor_cancel (GFileMonitor *monitor) +{ + GInotifyFileMonitor *inotify_monitor = G_INOTIFY_FILE_MONITOR (monitor); + + if (inotify_monitor->sub) + { + _ih_sub_cancel (inotify_monitor->sub); + _ih_sub_free (inotify_monitor->sub); + inotify_monitor->sub = NULL; + } + + return TRUE; +} + +static void +g_inotify_file_monitor_finalize (GObject *object) +{ + GInotifyFileMonitor *inotify_monitor = G_INOTIFY_FILE_MONITOR (object); + + /* must surely have been cancelled already */ + g_assert (!inotify_monitor->sub); + + G_OBJECT_CLASS (g_inotify_file_monitor_parent_class)->finalize (object); +} + +static void +g_inotify_file_monitor_init (GInotifyFileMonitor* monitor) +{ +} + static void g_inotify_file_monitor_class_init (GInotifyFileMonitorClass* klass) { @@ -121,33 +103,10 @@ g_inotify_file_monitor_class_init (GInotifyFileMonitorClass* klass) GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (klass); GLocalFileMonitorClass *local_file_monitor_class = G_LOCAL_FILE_MONITOR_CLASS (klass); - gobject_class->finalize = g_inotify_file_monitor_finalize; - file_monitor_class->cancel = g_inotify_file_monitor_cancel; - local_file_monitor_class->is_supported = g_inotify_file_monitor_is_supported; local_file_monitor_class->start = g_inotify_file_monitor_start; -} - -static void -g_inotify_file_monitor_init (GInotifyFileMonitor* monitor) -{ -} - -static gboolean -g_inotify_file_monitor_cancel (GFileMonitor* monitor) -{ - GInotifyFileMonitor *inotify_monitor = G_INOTIFY_FILE_MONITOR (monitor); - inotify_sub *sub = inotify_monitor->sub; - - if (sub) - { - _ih_sub_cancel (sub); - _ih_sub_free (sub); - inotify_monitor->sub = NULL; - } - - if (G_FILE_MONITOR_CLASS (g_inotify_file_monitor_parent_class)->cancel) - (*G_FILE_MONITOR_CLASS (g_inotify_file_monitor_parent_class)->cancel) (monitor); - - return TRUE; + local_file_monitor_class->mount_notify = TRUE; + file_monitor_class->cancel = g_inotify_file_monitor_cancel; + + gobject_class->finalize = g_inotify_file_monitor_finalize; } diff --git a/gio/inotify/ginotifyfilemonitor.h b/gio/inotify/ginotifyfilemonitor.h index 05837b95a..2ff8de8b8 100644 --- a/gio/inotify/ginotifyfilemonitor.h +++ b/gio/inotify/ginotifyfilemonitor.h @@ -32,7 +32,7 @@ G_BEGIN_DECLS -#define G_TYPE_INOTIFY_FILE_MONITOR (_g_inotify_file_monitor_get_type ()) +#define G_TYPE_INOTIFY_FILE_MONITOR (g_inotify_file_monitor_get_type ()) #define G_INOTIFY_FILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_INOTIFY_FILE_MONITOR, GInotifyFileMonitor)) #define G_INOTIFY_FILE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_INOTIFY_FILE_MONITOR, GInotifyFileMonitorClass)) #define G_IS_INOTIFY_FILE_MONITOR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_INOTIFY_FILE_MONITOR)) @@ -45,7 +45,7 @@ struct _GInotifyFileMonitorClass { GLocalFileMonitorClass parent_class; }; -GType _g_inotify_file_monitor_get_type (void); +GType g_inotify_file_monitor_get_type (void); G_END_DECLS diff --git a/gio/inotify/inotify-helper.c b/gio/inotify/inotify-helper.c index abf66c869..843db2655 100644 --- a/gio/inotify/inotify-helper.c +++ b/gio/inotify/inotify-helper.c @@ -29,8 +29,7 @@ #include /* Just include the local header to stop all the pain */ #include -#include -#include +#include #include #include "inotify-helper.h" #include "inotify-missing.h" @@ -109,6 +108,7 @@ _ih_sub_add (inotify_sub *sub) _im_add (sub); G_UNLOCK (inotify_lock); + return TRUE; } @@ -129,7 +129,7 @@ _ih_sub_cancel (inotify_sub *sub) } G_UNLOCK (inotify_lock); - + return TRUE; } @@ -150,113 +150,60 @@ _ih_fullpath_from_event (ik_event_t *event, return fullpath; } - -static gboolean -ih_event_is_paired_move (ik_event_t *event) -{ - if (event->pair) - { - ik_event_t *paired = event->pair; - /* intofiy(7): IN_MOVE == IN_MOVED_FROM | IN_MOVED_TO */ - return (event->mask | paired->mask) & IN_MOVE; - } - - return FALSE; -} - static void -ih_event_callback (ik_event_t *event, +ih_event_callback (ik_event_t *event, inotify_sub *sub, - gboolean file_event) + gboolean file_event) { - gchar *fullpath; - GFileMonitorEvent eflags; - GFile* child; - GFile* other; + g_assert (!file_event); /* XXX hardlink support */ - eflags = ih_mask_to_EventFlags (event->mask); - fullpath = _ih_fullpath_from_event (event, sub->dirname, - file_event ? sub->filename : NULL); - child = g_file_new_for_path (fullpath); - g_free (fullpath); - - if (ih_event_is_paired_move (event) && sub->pair_moves) + if (event->mask & IN_MOVE) { - const char *parent_dir = (char *) _ip_get_path_for_wd (event->pair->wd); - fullpath = _ih_fullpath_from_event (event->pair, parent_dir, NULL); - other = g_file_new_for_path (fullpath); - g_free (fullpath); - eflags = G_FILE_MONITOR_EVENT_MOVED; - event->pair = NULL; /* prevents the paired event to be emitted as well */ + /* We either have a rename (in the same directory) or a move + * (between different directories). + */ + if (event->pair && event->pair->wd == event->wd) + { + /* this is a rename */ + g_file_monitor_source_handle_event (sub->user_data, G_FILE_MONITOR_EVENT_RENAMED, + event->name, event->pair->name, NULL, event->timestamp); + } + else + { + GFile *other; + + if (event->pair) + { + const char *parent_dir; + gchar *fullpath; + + parent_dir = _ip_get_path_for_wd (event->pair->wd); + fullpath = _ih_fullpath_from_event (event->pair, parent_dir, NULL); + other = g_file_new_for_path (fullpath); + g_free (fullpath); + } + else + other = NULL; + + /* this is either an incoming or outgoing move */ + g_file_monitor_source_handle_event (sub->user_data, ih_mask_to_EventFlags (event->mask), + event->name, NULL, other, event->timestamp); + + if (other) + g_object_unref (other); + } } else - other = NULL; - - g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data), - child, other, eflags); - - /* For paired moves or moves whose mask has been changed from IN_MOVED_TO to - * IN_CREATE, notify also that it's probably the last change to the file, - * emitting CHANGES_DONE_HINT. - * The first (first part of the if's guard below) is the case of a normal - * move within the monitored tree and in the same mounted volume. - * The latter (second part of the guard) is the case of a move within the - * same mounted volume, but from a not monitored directory. - * - * It's not needed in cases like moves across mounted volumes as the IN_CREATE - * will be followed by a IN_MODIFY and IN_CLOSE_WRITE events. - * Also not needed if sub->pair_moves is set as EVENT_MOVED will be emitted - * instead of EVENT_CREATED which implies no further modification will be - * applied to the file - * See: https://bugzilla.gnome.org/show_bug.cgi?id=640077 - */ - if ((!sub->pair_moves && - event->is_second_in_pair && (event->mask & IN_MOVED_TO)) || - (!ih_event_is_paired_move (event) && - (event->original_mask & IN_MOVED_TO) && (event->mask & IN_CREATE))) - { - g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data), - child, NULL, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT); - } - - g_object_unref (child); - if (other) - g_object_unref (other); + /* unpaired event -- no 'other' field */ + g_file_monitor_source_handle_event (sub->user_data, ih_mask_to_EventFlags (event->mask), + event->name, NULL, NULL, event->timestamp); } static void ih_not_missing_callback (inotify_sub *sub) { - gchar *fullpath; - GFileMonitorEvent eflags; - guint32 mask; - GFile* child; - - if (sub->filename) - { - fullpath = g_strdup_printf ("%s/%s", sub->dirname, sub->filename); - g_warning ("Missing callback called fullpath = %s\n", fullpath); - if (!g_file_test (fullpath, G_FILE_TEST_EXISTS)) - { - g_free (fullpath); - return; - } - mask = IN_CREATE; - } - else - { - fullpath = g_strdup_printf ("%s", sub->dirname); - mask = IN_CREATE|IN_ISDIR; - } - - eflags = ih_mask_to_EventFlags (mask); - child = g_file_new_for_path (fullpath); - g_free (fullpath); - - g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data), - child, NULL, eflags); - - g_object_unref (child); + g_file_monitor_source_handle_event (sub->user_data, G_FILE_MONITOR_EVENT_CREATED, + sub->filename, NULL, NULL, g_get_monotonic_time ()); } /* Transforms a inotify event to a GVFS event. */ @@ -273,13 +220,15 @@ ih_mask_to_EventFlags (guint32 mask) case IN_ATTRIB: return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED; case IN_MOVE_SELF: - case IN_MOVED_FROM: case IN_DELETE: case IN_DELETE_SELF: return G_FILE_MONITOR_EVENT_DELETED; case IN_CREATE: - case IN_MOVED_TO: return G_FILE_MONITOR_EVENT_CREATED; + case IN_MOVED_FROM: + return G_FILE_MONITOR_EVENT_MOVED_OUT; + case IN_MOVED_TO: + return G_FILE_MONITOR_EVENT_MOVED_IN; case IN_UNMOUNT: return G_FILE_MONITOR_EVENT_UNMOUNTED; case IN_Q_OVERFLOW: diff --git a/gio/inotify/inotify-kernel.c b/gio/inotify/inotify-kernel.c index 1d20ecf3e..d7ea64661 100644 --- a/gio/inotify/inotify-kernel.c +++ b/gio/inotify/inotify-kernel.c @@ -63,7 +63,7 @@ ik_event_new (struct inotify_event *kevent, if (event->len) event->name = g_strdup (kevent->name); else - event->name = g_strdup (""); + event->name = NULL; return event; } @@ -72,7 +72,10 @@ void _ik_event_free (ik_event_t *event) { if (event->pair) - _ik_event_free (event->pair); + { + event->pair->pair = NULL; + _ik_event_free (event->pair); + } g_free (event->name); g_free (event); @@ -148,6 +151,7 @@ ik_source_try_to_pair_head (InotifyKernelSource *iks) g_queue_remove (&iks->queue, candidate); candidate->is_second_in_pair = TRUE; head->pair = candidate; + candidate->pair = head; return; } } diff --git a/gio/inotify/inotify-path.c b/gio/inotify/inotify-path.c index 9d6c00ed3..830f514d1 100644 --- a/gio/inotify/inotify-path.c +++ b/gio/inotify/inotify-path.c @@ -438,9 +438,7 @@ ip_wd_delete (gpointer data, static void ip_event_dispatch (GList *dir_list, - GList *pair_dir_list, GList *file_list, - GList *pair_file_list, ik_event_t *event) { GList *l; @@ -521,92 +519,13 @@ ip_event_dispatch (GList *dir_list, event_callback (event, sub, TRUE); } } - - if (!event->pair) - return; - - for (l = pair_dir_list; l; l = l->next) - { - GList *subl; - ip_watched_dir_t *dir = l->data; - - for (subl = dir->subs; subl; subl = subl->next) - { - inotify_sub *sub = subl->data; - - /* If the subscription and the event - * contain a filename and they don't - * match, we don't deliver this event. - */ - if (sub->filename && - event->pair->name && - strcmp (sub->filename, event->pair->name)) - continue; - - /* If the subscription has a filename - * but this event doesn't, we don't - * deliver this event. - */ - if (sub->filename && !event->pair->name) - continue; - - /* If we're also watching the file directly - * don't report events that will also be - * reported on the file itself. - */ - if (sub->hardlinks) - { - event->mask &= ~IP_INOTIFY_FILE_MASK; - if (!event->mask) - continue; - } - - /* FIXME: We might need to synthesize - * DELETE/UNMOUNT events when - * the filename doesn't match - */ - - event_callback (event->pair, sub, FALSE); - - if (sub->hardlinks) - { - ip_watched_file_t *file; - - file = g_hash_table_lookup (dir->files_hash, sub->filename); - - if (file != NULL) - { - if (event->pair->mask & (IN_MOVED_FROM | IN_DELETE)) - ip_watched_file_stop (file); - - if (event->pair->mask & (IN_MOVED_TO | IN_CREATE)) - ip_watched_file_start (file); - } - } - } - } - - for (l = pair_file_list; l; l = l->next) - { - ip_watched_file_t *file = l->data; - GList *subl; - - for (subl = file->subs; subl; subl = subl->next) - { - inotify_sub *sub = subl->data; - - event_callback (event->pair, sub, TRUE); - } - } } static void ip_event_callback (ik_event_t *event) { GList* dir_list = NULL; - GList* pair_dir_list = NULL; GList *file_list = NULL; - GList *pair_file_list = NULL; /* We can ignore the IGNORED events */ if (event->mask & IN_IGNORED) @@ -618,15 +537,19 @@ ip_event_callback (ik_event_t *event) dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->wd)); file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (event->wd)); - if (event->pair) + if (event->mask & IP_INOTIFY_DIR_MASK) + ip_event_dispatch (dir_list, file_list, event); + + /* Only deliver paired events if the wds are separate */ + if (event->pair && event->pair->wd != event->wd) { - pair_dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->pair->wd)); - pair_file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (event->pair->wd)); + dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->pair->wd)); + file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (event->pair->wd)); + + if (event->pair->mask & IP_INOTIFY_DIR_MASK) + ip_event_dispatch (dir_list, file_list, event->pair); } - if (event->mask & IP_INOTIFY_DIR_MASK) - ip_event_dispatch (dir_list, pair_dir_list, file_list, pair_file_list, event); - /* We have to manage the missing list * when we get an event that means the * file has been deleted/moved/unmounted. diff --git a/gio/inotify/inotify-sub.c b/gio/inotify/inotify-sub.c index 85b34119d..a00a89071 100644 --- a/gio/inotify/inotify-sub.c +++ b/gio/inotify/inotify-sub.c @@ -46,7 +46,6 @@ dup_dirname (const gchar *dirname) inotify_sub* _ih_sub_new (const gchar *dirname, const gchar *filename, - gboolean pair_moves, gboolean watch_hardlinks, gpointer user_data) { @@ -55,7 +54,6 @@ _ih_sub_new (const gchar *dirname, sub = g_new0 (inotify_sub, 1); sub->dirname = dup_dirname (dirname); sub->filename = g_strdup (filename); - sub->pair_moves = pair_moves; sub->hardlinks = watch_hardlinks; sub->user_data = user_data; diff --git a/gio/inotify/inotify-sub.h b/gio/inotify/inotify-sub.h index ff37567a0..ab24dd857 100644 --- a/gio/inotify/inotify-sub.h +++ b/gio/inotify/inotify-sub.h @@ -35,7 +35,6 @@ typedef struct inotify_sub *_ih_sub_new (const gchar *dirname, const gchar *filename, - gboolean pair_moves, gboolean watch_hardlinks, gpointer user_data); void _ih_sub_free (inotify_sub *sub); diff --git a/po/POTFILES.in b/po/POTFILES.in index 6ea427c67..34fadb940 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -63,7 +63,6 @@ gio/giostream.c gio/glib-compile-resources.c gio/glib-compile-schemas.c gio/gloadableicon.c -gio/glocaldirectorymonitor.c gio/glocalfile.c gio/glocalfileenumerator.c gio/glocalfileinfo.c