mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-01-11 23:16:14 +01:00
substantially rework file monitors
Remove all event merging and dispatch logic from GFileMonitor. The only implementation of GFileMonitor outside of glib is in gvfs and it already does these things properly. Get rid of GLocalDirectoryMonitor. We will use a single class, GLocalFileMonitor, for both directory and file monitoring. This will prevent every single backend from having to create two objects separately (eg: ginotifydirectorymonitor.c and ginotifyfilemonitor.c). Introduce GFileMonitorSource as a thread-safe cross-context dispatch mechanism. Put it in GLocalFileMonitor. All backends will be expected to dispatch via the source and not touch the GFileMonitor object at all from the worker thread. Remove all construct properties from GLocalFileMonitor and remove the "context" construct property from GFileMonitor. All backends must now get the information about what file to monitor from the ->start() call which is mandatory to implement. Remove the implementation of rate limiting in GFileMonitor and add an implementation in GLocalFileMonitor. gvfs never did anything with this anyway, but if it wanted to, it would have to implement it for itself. This was done in order to get the rate_limit field into the GFileMonitorSource so that it could be safely accessed from the worker thread. Expose g_local_file_is_remote() internally for NFS detection. With the "is_remote" functionality exposed, we can now move all functions for creating local file monitors to a proper location in glocalfilemonitor.c Port the inotify backend to adjust to the changes above. None of the other backends are ported yet. Those will come in future commits.
This commit is contained in:
parent
779c809a3d
commit
2737ab3201
@ -185,8 +185,6 @@ application_sources = \
|
|||||||
local_sources = \
|
local_sources = \
|
||||||
ghttpproxy.c \
|
ghttpproxy.c \
|
||||||
ghttpproxy.h \
|
ghttpproxy.h \
|
||||||
glocaldirectorymonitor.c \
|
|
||||||
glocaldirectorymonitor.h \
|
|
||||||
glocalfile.c \
|
glocalfile.c \
|
||||||
glocalfile.h \
|
glocalfile.h \
|
||||||
glocalfileenumerator.c \
|
glocalfileenumerator.c \
|
||||||
|
@ -46,8 +46,7 @@
|
|||||||
#include "giomodule-priv.h"
|
#include "giomodule-priv.h"
|
||||||
#include "gappinfo.h"
|
#include "gappinfo.h"
|
||||||
#include "gappinfoprivate.h"
|
#include "gappinfoprivate.h"
|
||||||
#include "glocaldirectorymonitor.h"
|
#include "glocalfilemonitor.h"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION:gdesktopappinfo
|
* SECTION:gdesktopappinfo
|
||||||
@ -140,7 +139,7 @@ typedef struct
|
|||||||
gchar *alternatively_watching;
|
gchar *alternatively_watching;
|
||||||
gboolean is_config;
|
gboolean is_config;
|
||||||
gboolean is_setup;
|
gboolean is_setup;
|
||||||
GLocalDirectoryMonitor *monitor;
|
GFileMonitor *monitor;
|
||||||
GHashTable *app_names;
|
GHashTable *app_names;
|
||||||
GHashTable *mime_tweaks;
|
GHashTable *mime_tweaks;
|
||||||
GHashTable *memory_index;
|
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
|
* does (and we catch the unlikely race), the only degradation is that
|
||||||
* we will fall back to polling.
|
* we will fall back to polling.
|
||||||
*/
|
*/
|
||||||
dir->monitor = g_local_directory_monitor_new_in_worker (watch_dir, G_FILE_MONITOR_NONE, NULL);
|
dir->monitor = g_local_file_monitor_new_in_worker (watch_dir, TRUE, G_FILE_MONITOR_NONE,
|
||||||
|
desktop_file_dir_changed, dir, NULL);
|
||||||
if (dir->monitor)
|
|
||||||
{
|
|
||||||
g_signal_connect (dir->monitor, "changed", G_CALLBACK (desktop_file_dir_changed), dir);
|
|
||||||
g_local_directory_monitor_start (dir->monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
desktop_file_dir_unindexed_init (dir);
|
desktop_file_dir_unindexed_init (dir);
|
||||||
|
|
||||||
|
@ -27,11 +27,6 @@
|
|||||||
#include "gvfs.h"
|
#include "gvfs.h"
|
||||||
#include "glibintl.h"
|
#include "glibintl.h"
|
||||||
|
|
||||||
|
|
||||||
struct _FileChange;
|
|
||||||
typedef struct _FileChange FileChange;
|
|
||||||
static void file_change_free (FileChange *change);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SECTION:gfilemonitor
|
* SECTION:gfilemonitor
|
||||||
* @short_description: File Monitor
|
* @short_description: File Monitor
|
||||||
@ -53,69 +48,38 @@ static void file_change_free (FileChange *change);
|
|||||||
* context is still running).
|
* context is still running).
|
||||||
**/
|
**/
|
||||||
|
|
||||||
G_LOCK_DEFINE_STATIC(cancelled);
|
#define DEFAULT_RATE_LIMIT_MSECS 800
|
||||||
|
|
||||||
enum {
|
struct _GFileMonitorPrivate
|
||||||
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 {
|
|
||||||
gboolean cancelled;
|
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)
|
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
|
static void
|
||||||
g_file_monitor_set_property (GObject *object,
|
g_file_monitor_set_property (GObject *object,
|
||||||
guint prop_id,
|
guint prop_id,
|
||||||
const GValue *value,
|
const GValue *value,
|
||||||
GParamSpec *pspec)
|
GParamSpec *pspec)
|
||||||
{
|
{
|
||||||
GFileMonitor *monitor;
|
//GFileMonitor *monitor;
|
||||||
|
|
||||||
monitor = G_FILE_MONITOR (object);
|
//monitor = G_FILE_MONITOR (object);
|
||||||
|
|
||||||
switch (prop_id)
|
switch (prop_id)
|
||||||
{
|
{
|
||||||
case PROP_RATE_LIMIT:
|
case PROP_RATE_LIMIT:
|
||||||
g_file_monitor_set_rate_limit (monitor, g_value_get_int (value));
|
/* not supported by default */
|
||||||
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 ();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -130,22 +94,17 @@ g_file_monitor_get_property (GObject *object,
|
|||||||
GValue *value,
|
GValue *value,
|
||||||
GParamSpec *pspec)
|
GParamSpec *pspec)
|
||||||
{
|
{
|
||||||
GFileMonitor *monitor;
|
|
||||||
GFileMonitorPrivate *priv;
|
|
||||||
|
|
||||||
monitor = G_FILE_MONITOR (object);
|
|
||||||
priv = monitor->priv;
|
|
||||||
|
|
||||||
switch (prop_id)
|
switch (prop_id)
|
||||||
{
|
{
|
||||||
case PROP_RATE_LIMIT:
|
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;
|
break;
|
||||||
|
|
||||||
case PROP_CANCELLED:
|
case PROP_CANCELLED:
|
||||||
G_LOCK (cancelled);
|
//g_mutex_lock (&fms->lock);
|
||||||
g_value_set_boolean (value, priv->cancelled);
|
g_value_set_boolean (value, FALSE);//fms->cancelled);
|
||||||
G_UNLOCK (cancelled);
|
//g_mutex_unlock (&fms->lock);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
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
|
static void
|
||||||
g_file_monitor_dispose (GObject *object)
|
g_file_monitor_dispose (GObject *object)
|
||||||
{
|
{
|
||||||
GFileMonitor *monitor;
|
GFileMonitor *monitor = G_FILE_MONITOR (object);
|
||||||
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;
|
|
||||||
|
|
||||||
/* Make sure we cancel on last unref */
|
/* Make sure we cancel on last unref */
|
||||||
g_file_monitor_cancel (monitor);
|
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);
|
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
|
static void
|
||||||
g_file_monitor_class_init (GFileMonitorClass *klass)
|
g_file_monitor_class_init (GFileMonitorClass *klass)
|
||||||
{
|
{
|
||||||
GObjectClass *object_class;
|
GObjectClass *object_class;
|
||||||
|
|
||||||
object_class = G_OBJECT_CLASS (klass);
|
object_class = G_OBJECT_CLASS (klass);
|
||||||
object_class->finalize = g_file_monitor_finalize;
|
|
||||||
object_class->dispose = g_file_monitor_dispose;
|
object_class->dispose = g_file_monitor_dispose;
|
||||||
object_class->get_property = g_file_monitor_get_property;
|
object_class->get_property = g_file_monitor_get_property;
|
||||||
object_class->set_property = g_file_monitor_set_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.
|
* 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
|
* #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.
|
* 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.
|
* In all the other cases, @other_file will be set to #NULL.
|
||||||
**/
|
**/
|
||||||
signals[CHANGED] =
|
g_file_monitor_changed_signal = g_signal_new (I_("changed"),
|
||||||
g_signal_new (I_("changed"),
|
G_TYPE_FILE_MONITOR,
|
||||||
G_TYPE_FILE_MONITOR,
|
G_SIGNAL_RUN_LAST,
|
||||||
G_SIGNAL_RUN_LAST,
|
G_STRUCT_OFFSET (GFileMonitorClass, changed),
|
||||||
G_STRUCT_OFFSET (GFileMonitorClass, changed),
|
NULL, NULL,
|
||||||
NULL, NULL,
|
NULL,
|
||||||
NULL,
|
G_TYPE_NONE, 3,
|
||||||
G_TYPE_NONE, 3,
|
G_TYPE_FILE, G_TYPE_FILE, G_TYPE_FILE_MONITOR_EVENT);
|
||||||
G_TYPE_FILE, G_TYPE_FILE, G_TYPE_FILE_MONITOR_EVENT);
|
|
||||||
|
|
||||||
g_object_class_install_property (object_class,
|
g_object_class_install_property (object_class, PROP_RATE_LIMIT,
|
||||||
PROP_RATE_LIMIT,
|
|
||||||
g_param_spec_int ("rate-limit",
|
g_param_spec_int ("rate-limit",
|
||||||
P_("Rate limit"),
|
P_("Rate limit"),
|
||||||
P_("The limit of the monitor to watch for changes, in milliseconds"),
|
P_("The limit of the monitor to watch for changes, in milliseconds"),
|
||||||
0, G_MAXINT,
|
0, G_MAXINT, DEFAULT_RATE_LIMIT_MSECS, G_PARAM_READWRITE |
|
||||||
DEFAULT_RATE_LIMIT_MSECS,
|
G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS));
|
||||||
G_PARAM_READWRITE|
|
|
||||||
G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
|
|
||||||
|
|
||||||
g_object_class_install_property (object_class,
|
g_object_class_install_property (object_class, PROP_CANCELLED,
|
||||||
PROP_CANCELLED,
|
|
||||||
g_param_spec_boolean ("cancelled",
|
g_param_spec_boolean ("cancelled",
|
||||||
P_("Cancelled"),
|
P_("Cancelled"),
|
||||||
P_("Whether the monitor has been cancelled"),
|
P_("Whether the monitor has been cancelled"),
|
||||||
FALSE,
|
FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -300,9 +214,7 @@ g_file_monitor_is_cancelled (GFileMonitor *monitor)
|
|||||||
|
|
||||||
g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
|
g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
|
||||||
|
|
||||||
G_LOCK (cancelled);
|
|
||||||
res = monitor->priv->cancelled;
|
res = monitor->priv->cancelled;
|
||||||
G_UNLOCK (cancelled);
|
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -313,29 +225,22 @@ g_file_monitor_is_cancelled (GFileMonitor *monitor)
|
|||||||
*
|
*
|
||||||
* Cancels a file monitor.
|
* Cancels a file monitor.
|
||||||
*
|
*
|
||||||
* Returns: %TRUE if monitor was cancelled.
|
* Returns: always %TRUE
|
||||||
**/
|
**/
|
||||||
gboolean
|
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_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
|
||||||
|
|
||||||
G_LOCK (cancelled);
|
if (!monitor->priv->cancelled)
|
||||||
if (monitor->priv->cancelled)
|
|
||||||
{
|
{
|
||||||
G_UNLOCK (cancelled);
|
G_FILE_MONITOR_GET_CLASS (monitor)->cancel (monitor);
|
||||||
return TRUE;
|
|
||||||
|
monitor->priv->cancelled = TRUE;
|
||||||
|
g_object_notify (G_OBJECT (monitor), "cancelled");
|
||||||
}
|
}
|
||||||
|
|
||||||
monitor->priv->cancelled = TRUE;
|
return TRUE;
|
||||||
G_UNLOCK (cancelled);
|
|
||||||
|
|
||||||
g_object_notify (G_OBJECT (monitor), "cancelled");
|
|
||||||
|
|
||||||
klass = G_FILE_MONITOR_GET_CLASS (monitor);
|
|
||||||
return (* klass->cancel) (monitor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -351,396 +256,21 @@ void
|
|||||||
g_file_monitor_set_rate_limit (GFileMonitor *monitor,
|
g_file_monitor_set_rate_limit (GFileMonitor *monitor,
|
||||||
gint limit_msecs)
|
gint limit_msecs)
|
||||||
{
|
{
|
||||||
GFileMonitorPrivate *priv;
|
g_object_set (monitor, "rate-limit", limit_msecs, NULL);
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
void
|
||||||
g_file_monitor_emit_event (GFileMonitor *monitor,
|
g_file_monitor_emit_event (GFileMonitor *monitor,
|
||||||
GFile *child,
|
GFile *child,
|
||||||
GFile *other_file,
|
GFile *other_file,
|
||||||
GFileMonitorEvent event_type)
|
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_MONITOR (monitor));
|
||||||
g_return_if_fail (G_IS_FILE (child));
|
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)
|
g_signal_emit (monitor, g_file_monitor_changed_signal, 0, child, other_file, event_type);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -328,9 +328,14 @@ typedef enum {
|
|||||||
* by file renames (moves) and send a single G_FILE_MONITOR_EVENT_MOVED
|
* by file renames (moves) and send a single G_FILE_MONITOR_EVENT_MOVED
|
||||||
* event instead (NB: not supported on all backends; the default
|
* event instead (NB: not supported on all backends; the default
|
||||||
* behaviour -without specifying this flag- is to send single DELETED
|
* 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
|
* @G_FILE_MONITOR_WATCH_HARD_LINKS: Watch for changes to the file made
|
||||||
* via another hard link. Since 2.36.
|
* 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.
|
* Flags used to set what a #GFileMonitor will watch for.
|
||||||
*/
|
*/
|
||||||
@ -338,7 +343,8 @@ typedef enum {
|
|||||||
G_FILE_MONITOR_NONE = 0,
|
G_FILE_MONITOR_NONE = 0,
|
||||||
G_FILE_MONITOR_WATCH_MOUNTS = (1 << 0),
|
G_FILE_MONITOR_WATCH_MOUNTS = (1 << 0),
|
||||||
G_FILE_MONITOR_SEND_MOVED = (1 << 1),
|
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;
|
} GFileMonitorFlags;
|
||||||
|
|
||||||
|
|
||||||
@ -393,7 +399,17 @@ typedef enum {
|
|||||||
* @G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED: a file attribute was changed.
|
* @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_PRE_UNMOUNT: the file location will soon be unmounted.
|
||||||
* @G_FILE_MONITOR_EVENT_UNMOUNTED: the file location was 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.
|
* 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_ATTRIBUTE_CHANGED,
|
||||||
G_FILE_MONITOR_EVENT_PRE_UNMOUNT,
|
G_FILE_MONITOR_EVENT_PRE_UNMOUNT,
|
||||||
G_FILE_MONITOR_EVENT_UNMOUNTED,
|
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;
|
} GFileMonitorEvent;
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
#include "giomodule.h"
|
#include "giomodule.h"
|
||||||
#include "giomodule-priv.h"
|
#include "giomodule-priv.h"
|
||||||
#include "glocalfilemonitor.h"
|
#include "glocalfilemonitor.h"
|
||||||
#include "glocaldirectorymonitor.h"
|
|
||||||
#include "gnativevolumemonitor.h"
|
#include "gnativevolumemonitor.h"
|
||||||
#include "gproxyresolver.h"
|
#include "gproxyresolver.h"
|
||||||
#include "gproxy.h"
|
#include "gproxy.h"
|
||||||
@ -37,6 +36,8 @@
|
|||||||
#include "gtlsbackend.h"
|
#include "gtlsbackend.h"
|
||||||
#include "gvfs.h"
|
#include "gvfs.h"
|
||||||
#include "gnotificationbackend.h"
|
#include "gnotificationbackend.h"
|
||||||
|
#include "ginitable.h"
|
||||||
|
#include "gnetworkmonitor.h"
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
#include "gregistrysettingsbackend.h"
|
#include "gregistrysettingsbackend.h"
|
||||||
#endif
|
#endif
|
||||||
@ -886,17 +887,15 @@ _g_io_module_get_default (const gchar *extension_point,
|
|||||||
G_LOCK_DEFINE_STATIC (registered_extensions);
|
G_LOCK_DEFINE_STATIC (registered_extensions);
|
||||||
G_LOCK_DEFINE_STATIC (loaded_dirs);
|
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_fen_file_monitor_get_type (void);
|
extern GType g_inotify_file_monitor_get_type (void);
|
||||||
extern GType _g_inotify_directory_monitor_get_type (void);
|
extern GType g_kqueue_file_monitor_get_type (void);
|
||||||
extern GType _g_inotify_file_monitor_get_type (void);
|
extern GType g_win32_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_unix_volume_monitor_get_type (void);
|
extern GType _g_unix_volume_monitor_get_type (void);
|
||||||
extern GType _g_local_vfs_get_type (void);
|
extern GType _g_local_vfs_get_type (void);
|
||||||
|
|
||||||
extern GType _g_win32_volume_monitor_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_winhttp_vfs_get_type (void);
|
||||||
|
|
||||||
extern GType _g_dummy_proxy_resolver_get_type (void);
|
extern GType _g_dummy_proxy_resolver_get_type (void);
|
||||||
@ -980,15 +979,9 @@ _g_io_modules_ensure_extension_points_registered (void)
|
|||||||
#endif
|
#endif
|
||||||
#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);
|
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);
|
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);
|
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);
|
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_null_settings_backend_get_type ());
|
||||||
g_type_ensure (g_memory_settings_backend_get_type ());
|
g_type_ensure (g_memory_settings_backend_get_type ());
|
||||||
#if defined(HAVE_INOTIFY_INIT1)
|
#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
|
#endif
|
||||||
#if defined(HAVE_KQUEUE)
|
#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
|
#endif
|
||||||
#if defined(HAVE_FEN)
|
#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
|
#endif
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
g_type_ensure (_g_win32_volume_monitor_get_type ());
|
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 ());
|
g_type_ensure (g_registry_backend_get_type ());
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_COCOA
|
#ifdef HAVE_COCOA
|
||||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* Author: Alexander Larsson <alexl@redhat.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
#include "glocaldirectorymonitor.h"
|
|
||||||
#include "gunixmounts.h"
|
|
||||||
#include "giomodule-priv.h"
|
|
||||||
#include "gfile.h"
|
|
||||||
#include "gioerror.h"
|
|
||||||
#include "glibintl.h"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
* Author: Alexander Larsson <alexl@redhat.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __G_LOCAL_DIRECTORY_MONITOR_H__
|
|
||||||
#define __G_LOCAL_DIRECTORY_MONITOR_H__
|
|
||||||
|
|
||||||
#include <gio/gfilemonitor.h>
|
|
||||||
|
|
||||||
#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__ */
|
|
@ -56,7 +56,6 @@
|
|||||||
#include "glocalfileinputstream.h"
|
#include "glocalfileinputstream.h"
|
||||||
#include "glocalfileoutputstream.h"
|
#include "glocalfileoutputstream.h"
|
||||||
#include "glocalfileiostream.h"
|
#include "glocalfileiostream.h"
|
||||||
#include "glocaldirectorymonitor.h"
|
|
||||||
#include "glocalfilemonitor.h"
|
#include "glocalfilemonitor.h"
|
||||||
#include "gmountprivate.h"
|
#include "gmountprivate.h"
|
||||||
#include "gunixmounts.h"
|
#include "gunixmounts.h"
|
||||||
@ -2451,8 +2450,8 @@ g_local_file_move (GFile *source,
|
|||||||
|
|
||||||
#ifdef G_OS_WIN32
|
#ifdef G_OS_WIN32
|
||||||
|
|
||||||
static gboolean
|
gboolean
|
||||||
is_remote (const gchar *filename)
|
g_local_file_is_remote (const gchar *filename)
|
||||||
{
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
@ -2508,8 +2507,8 @@ is_remote_fs (const gchar *filename)
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
gboolean
|
||||||
is_remote (const gchar *filename)
|
g_local_file_is_remote (const gchar *filename)
|
||||||
{
|
{
|
||||||
static gboolean remote_home;
|
static gboolean remote_home;
|
||||||
static gsize initialized;
|
static gsize initialized;
|
||||||
@ -2536,8 +2535,9 @@ g_local_file_monitor_dir (GFile *file,
|
|||||||
GCancellable *cancellable,
|
GCancellable *cancellable,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
GLocalFile* local_file = G_LOCAL_FILE(file);
|
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);
|
|
||||||
|
return g_local_file_monitor_new_for_path (local_file->filename, TRUE, flags, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static GFileMonitor*
|
static GFileMonitor*
|
||||||
@ -2546,31 +2546,11 @@ g_local_file_monitor_file (GFile *file,
|
|||||||
GCancellable *cancellable,
|
GCancellable *cancellable,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
GLocalFile* local_file = G_LOCAL_FILE(file);
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
GLocalDirectoryMonitor *
|
return g_local_file_monitor_new_for_path (local_file->filename, FALSE, flags, error);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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().
|
/* Here is the GLocalFile implementation of g_file_measure_disk_usage().
|
||||||
*
|
*
|
||||||
* If available, we use fopenat() in preference to filenames for
|
* If available, we use fopenat() in preference to filenames for
|
||||||
|
@ -46,6 +46,8 @@ GFile * _g_local_file_new (const char *filename);
|
|||||||
|
|
||||||
const char * _g_local_file_get_filename (GLocalFile *file);
|
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,
|
GFile * g_local_file_new_from_dirname_and_basename (const char *dirname,
|
||||||
const char *basename);
|
const char *basename);
|
||||||
|
|
||||||
|
@ -25,57 +25,756 @@
|
|||||||
#include "giomodule-priv.h"
|
#include "giomodule-priv.h"
|
||||||
#include "gioerror.h"
|
#include "gioerror.h"
|
||||||
#include "glibintl.h"
|
#include "glibintl.h"
|
||||||
|
#include "glocalfile.h"
|
||||||
|
#include "glib-private.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#define DEFAULT_RATE_LIMIT 800 * G_TIME_SPAN_MILLISECOND
|
||||||
|
#define VIRTUAL_CHANGES_DONE_DELAY 2 * G_TIME_SPAN_SECOND
|
||||||
|
|
||||||
enum
|
/* GFileMonitorSource is a GSource responsible for emitting the changed
|
||||||
{
|
* signals in the owner-context of the GFileMonitor.
|
||||||
PROP_0,
|
*
|
||||||
PROP_FILENAME,
|
* It contains functionality for cross-thread queuing of events. It
|
||||||
PROP_FLAGS
|
* 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
|
/* QueuedEvent is a signal that will be sent immediately, as soon as the
|
||||||
g_local_file_monitor_init (GLocalFileMonitor* local_monitor)
|
* 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
|
static void
|
||||||
g_local_file_monitor_set_property (GObject *object,
|
pending_change_free (gpointer data)
|
||||||
guint property_id,
|
|
||||||
const GValue *value,
|
|
||||||
GParamSpec *pspec)
|
|
||||||
{
|
{
|
||||||
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:
|
change->dirty = TRUE;
|
||||||
local_monitor->filename = g_value_dup_string (value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PROP_FLAGS:
|
g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms);
|
||||||
local_monitor->flags = g_value_get_flags (value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static gboolean
|
||||||
g_local_file_monitor_start (GLocalFileMonitor *local_monitor)
|
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)
|
return change->dirty;
|
||||||
class->start (local_monitor);
|
}
|
||||||
|
|
||||||
|
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
|
static void
|
||||||
@ -83,49 +782,32 @@ g_local_file_monitor_finalize (GObject *object)
|
|||||||
{
|
{
|
||||||
GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (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);
|
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*
|
static void g_local_file_monitor_class_init (GLocalFileMonitorClass *class)
|
||||||
_g_local_file_monitor_new (const char *pathname,
|
{
|
||||||
GFileMonitorFlags flags,
|
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
|
||||||
GMainContext *context,
|
|
||||||
gboolean is_remote_fs,
|
gobject_class->get_property = g_local_file_monitor_get_property;
|
||||||
gboolean do_start,
|
gobject_class->set_property = g_local_file_monitor_set_property;
|
||||||
GError **error)
|
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;
|
GType type = G_TYPE_INVALID;
|
||||||
|
|
||||||
if (is_remote_fs)
|
if (is_remote_fs)
|
||||||
@ -138,14 +820,57 @@ _g_local_file_monitor_new (const char *pathname,
|
|||||||
"GIO_USE_FILE_MONITOR",
|
"GIO_USE_FILE_MONITOR",
|
||||||
G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported));
|
G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported));
|
||||||
|
|
||||||
if (type != G_TYPE_INVALID)
|
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,
|
||||||
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
|
_("Unable to find default local file monitor type"));
|
||||||
_("Unable to find default local file monitor type"));
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (monitor && do_start)
|
return g_object_new (type, NULL);
|
||||||
g_local_file_monitor_start (G_LOCAL_FILE_MONITOR (monitor));
|
}
|
||||||
|
|
||||||
return monitor;
|
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);
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#define __G_LOCAL_FILE_MONITOR_H__
|
#define __G_LOCAL_FILE_MONITOR_H__
|
||||||
|
|
||||||
#include <gio/gfilemonitor.h>
|
#include <gio/gfilemonitor.h>
|
||||||
|
#include "gunixmounts.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
@ -37,13 +38,15 @@ G_BEGIN_DECLS
|
|||||||
|
|
||||||
typedef struct _GLocalFileMonitor GLocalFileMonitor;
|
typedef struct _GLocalFileMonitor GLocalFileMonitor;
|
||||||
typedef struct _GLocalFileMonitorClass GLocalFileMonitorClass;
|
typedef struct _GLocalFileMonitorClass GLocalFileMonitorClass;
|
||||||
|
typedef struct _GFileMonitorSource GFileMonitorSource;
|
||||||
|
|
||||||
struct _GLocalFileMonitor
|
struct _GLocalFileMonitor
|
||||||
{
|
{
|
||||||
GFileMonitor parent_instance;
|
GFileMonitor parent_instance;
|
||||||
|
|
||||||
gchar *filename;
|
GFileMonitorSource *source;
|
||||||
GFileMonitorFlags flags;
|
GUnixMountMonitor *mount_monitor;
|
||||||
|
gboolean was_mounted;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GLocalFileMonitorClass
|
struct _GLocalFileMonitorClass
|
||||||
@ -51,7 +54,13 @@ struct _GLocalFileMonitorClass
|
|||||||
GFileMonitorClass parent_class;
|
GFileMonitorClass parent_class;
|
||||||
|
|
||||||
gboolean (* is_supported) (void);
|
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
|
#ifdef G_OS_UNIX
|
||||||
@ -59,18 +68,37 @@ GLIB_AVAILABLE_IN_ALL
|
|||||||
#endif
|
#endif
|
||||||
GType g_local_file_monitor_get_type (void) G_GNUC_CONST;
|
GType g_local_file_monitor_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
GFileMonitor * _g_local_file_monitor_new (const char *pathname,
|
/* for glocalfile.c */
|
||||||
GFileMonitorFlags flags,
|
GFileMonitor *
|
||||||
GMainContext *context,
|
g_local_file_monitor_new_for_path (const gchar *pathname,
|
||||||
gboolean is_remote_fs,
|
gboolean is_directory,
|
||||||
gboolean do_start,
|
GFileMonitorFlags flags,
|
||||||
GError **error);
|
GError **error);
|
||||||
void g_local_file_monitor_start (GLocalFileMonitor *local_monitor);
|
|
||||||
|
/* 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
|
G_END_DECLS
|
||||||
|
|
||||||
|
@ -15,8 +15,6 @@ libinotify_la_SOURCES = \
|
|||||||
inotify-helper.h \
|
inotify-helper.h \
|
||||||
ginotifyfilemonitor.c \
|
ginotifyfilemonitor.c \
|
||||||
ginotifyfilemonitor.h \
|
ginotifyfilemonitor.h \
|
||||||
ginotifydirectorymonitor.c \
|
|
||||||
ginotifydirectorymonitor.h \
|
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
libinotify_la_CFLAGS = \
|
libinotify_la_CFLAGS = \
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
struct _GInotifyDirectoryMonitor
|
struct _GInotifyDirectoryMonitor
|
||||||
{
|
{
|
||||||
GLocalDirectoryMonitor parent_instance;
|
GLocalFileMonitor parent_instance;
|
||||||
inotify_sub *sub;
|
inotify_sub *sub;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
* Authors: Alexander Larsson <alexl@redhat.com>
|
* Authors: Alexander Larsson <alexl@redhat.com>
|
||||||
* John McCutchan <john@johnmccutchan.com>
|
* John McCutchan <john@johnmccutchan.com>
|
||||||
* Sebastian Dröge <slomo@circular-chaos.org>
|
* Sebastian Dröge <slomo@circular-chaos.org>
|
||||||
|
* Ryan Lortie <desrt@desrt.ca>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@ -32,81 +33,13 @@
|
|||||||
struct _GInotifyFileMonitor
|
struct _GInotifyFileMonitor
|
||||||
{
|
{
|
||||||
GLocalFileMonitor parent_instance;
|
GLocalFileMonitor parent_instance;
|
||||||
gchar *filename;
|
|
||||||
gchar *dirname;
|
|
||||||
inotify_sub *sub;
|
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_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_io_extension_point_implement (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
|
||||||
g_define_type_id,
|
g_define_type_id, "inotify", 20))
|
||||||
"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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
g_inotify_file_monitor_is_supported (void)
|
g_inotify_file_monitor_is_supported (void)
|
||||||
@ -114,6 +47,55 @@ g_inotify_file_monitor_is_supported (void)
|
|||||||
return _ih_startup ();
|
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
|
static void
|
||||||
g_inotify_file_monitor_class_init (GInotifyFileMonitorClass* klass)
|
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);
|
GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (klass);
|
||||||
GLocalFileMonitorClass *local_file_monitor_class = G_LOCAL_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->is_supported = g_inotify_file_monitor_is_supported;
|
||||||
local_file_monitor_class->start = g_inotify_file_monitor_start;
|
local_file_monitor_class->start = g_inotify_file_monitor_start;
|
||||||
}
|
local_file_monitor_class->mount_notify = TRUE;
|
||||||
|
file_monitor_class->cancel = g_inotify_file_monitor_cancel;
|
||||||
static void
|
|
||||||
g_inotify_file_monitor_init (GInotifyFileMonitor* monitor)
|
gobject_class->finalize = g_inotify_file_monitor_finalize;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
G_BEGIN_DECLS
|
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(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_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))
|
#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;
|
GLocalFileMonitorClass parent_class;
|
||||||
};
|
};
|
||||||
|
|
||||||
GType _g_inotify_file_monitor_get_type (void);
|
GType g_inotify_file_monitor_get_type (void);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
@ -29,8 +29,7 @@
|
|||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
/* Just include the local header to stop all the pain */
|
/* Just include the local header to stop all the pain */
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
#include <gio/glocalfile.h>
|
#include <gio/glocalfilemonitor.h>
|
||||||
#include <gio/gfilemonitor.h>
|
|
||||||
#include <gio/gfile.h>
|
#include <gio/gfile.h>
|
||||||
#include "inotify-helper.h"
|
#include "inotify-helper.h"
|
||||||
#include "inotify-missing.h"
|
#include "inotify-missing.h"
|
||||||
@ -109,6 +108,7 @@ _ih_sub_add (inotify_sub *sub)
|
|||||||
_im_add (sub);
|
_im_add (sub);
|
||||||
|
|
||||||
G_UNLOCK (inotify_lock);
|
G_UNLOCK (inotify_lock);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,113 +150,60 @@ _ih_fullpath_from_event (ik_event_t *event,
|
|||||||
return fullpath;
|
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
|
static void
|
||||||
ih_event_callback (ik_event_t *event,
|
ih_event_callback (ik_event_t *event,
|
||||||
inotify_sub *sub,
|
inotify_sub *sub,
|
||||||
gboolean file_event)
|
gboolean file_event)
|
||||||
{
|
{
|
||||||
gchar *fullpath;
|
g_assert (!file_event); /* XXX hardlink support */
|
||||||
GFileMonitorEvent eflags;
|
|
||||||
GFile* child;
|
|
||||||
GFile* other;
|
|
||||||
|
|
||||||
eflags = ih_mask_to_EventFlags (event->mask);
|
if (event->mask & IN_MOVE)
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
const char *parent_dir = (char *) _ip_get_path_for_wd (event->pair->wd);
|
/* We either have a rename (in the same directory) or a move
|
||||||
fullpath = _ih_fullpath_from_event (event->pair, parent_dir, NULL);
|
* (between different directories).
|
||||||
other = g_file_new_for_path (fullpath);
|
*/
|
||||||
g_free (fullpath);
|
if (event->pair && event->pair->wd == event->wd)
|
||||||
eflags = G_FILE_MONITOR_EVENT_MOVED;
|
{
|
||||||
event->pair = NULL; /* prevents the paired event to be emitted as well */
|
/* 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
|
else
|
||||||
other = NULL;
|
/* unpaired event -- no 'other' field */
|
||||||
|
g_file_monitor_source_handle_event (sub->user_data, ih_mask_to_EventFlags (event->mask),
|
||||||
g_file_monitor_emit_event (G_FILE_MONITOR (sub->user_data),
|
event->name, NULL, NULL, event->timestamp);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ih_not_missing_callback (inotify_sub *sub)
|
ih_not_missing_callback (inotify_sub *sub)
|
||||||
{
|
{
|
||||||
gchar *fullpath;
|
g_file_monitor_source_handle_event (sub->user_data, G_FILE_MONITOR_EVENT_CREATED,
|
||||||
GFileMonitorEvent eflags;
|
sub->filename, NULL, NULL, g_get_monotonic_time ());
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Transforms a inotify event to a GVFS event. */
|
/* Transforms a inotify event to a GVFS event. */
|
||||||
@ -273,13 +220,15 @@ ih_mask_to_EventFlags (guint32 mask)
|
|||||||
case IN_ATTRIB:
|
case IN_ATTRIB:
|
||||||
return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
|
return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
|
||||||
case IN_MOVE_SELF:
|
case IN_MOVE_SELF:
|
||||||
case IN_MOVED_FROM:
|
|
||||||
case IN_DELETE:
|
case IN_DELETE:
|
||||||
case IN_DELETE_SELF:
|
case IN_DELETE_SELF:
|
||||||
return G_FILE_MONITOR_EVENT_DELETED;
|
return G_FILE_MONITOR_EVENT_DELETED;
|
||||||
case IN_CREATE:
|
case IN_CREATE:
|
||||||
case IN_MOVED_TO:
|
|
||||||
return G_FILE_MONITOR_EVENT_CREATED;
|
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:
|
case IN_UNMOUNT:
|
||||||
return G_FILE_MONITOR_EVENT_UNMOUNTED;
|
return G_FILE_MONITOR_EVENT_UNMOUNTED;
|
||||||
case IN_Q_OVERFLOW:
|
case IN_Q_OVERFLOW:
|
||||||
|
@ -63,7 +63,7 @@ ik_event_new (struct inotify_event *kevent,
|
|||||||
if (event->len)
|
if (event->len)
|
||||||
event->name = g_strdup (kevent->name);
|
event->name = g_strdup (kevent->name);
|
||||||
else
|
else
|
||||||
event->name = g_strdup ("");
|
event->name = NULL;
|
||||||
|
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
@ -72,7 +72,10 @@ void
|
|||||||
_ik_event_free (ik_event_t *event)
|
_ik_event_free (ik_event_t *event)
|
||||||
{
|
{
|
||||||
if (event->pair)
|
if (event->pair)
|
||||||
_ik_event_free (event->pair);
|
{
|
||||||
|
event->pair->pair = NULL;
|
||||||
|
_ik_event_free (event->pair);
|
||||||
|
}
|
||||||
|
|
||||||
g_free (event->name);
|
g_free (event->name);
|
||||||
g_free (event);
|
g_free (event);
|
||||||
@ -148,6 +151,7 @@ ik_source_try_to_pair_head (InotifyKernelSource *iks)
|
|||||||
g_queue_remove (&iks->queue, candidate);
|
g_queue_remove (&iks->queue, candidate);
|
||||||
candidate->is_second_in_pair = TRUE;
|
candidate->is_second_in_pair = TRUE;
|
||||||
head->pair = candidate;
|
head->pair = candidate;
|
||||||
|
candidate->pair = head;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -438,9 +438,7 @@ ip_wd_delete (gpointer data,
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
ip_event_dispatch (GList *dir_list,
|
ip_event_dispatch (GList *dir_list,
|
||||||
GList *pair_dir_list,
|
|
||||||
GList *file_list,
|
GList *file_list,
|
||||||
GList *pair_file_list,
|
|
||||||
ik_event_t *event)
|
ik_event_t *event)
|
||||||
{
|
{
|
||||||
GList *l;
|
GList *l;
|
||||||
@ -521,92 +519,13 @@ ip_event_dispatch (GList *dir_list,
|
|||||||
event_callback (event, sub, TRUE);
|
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
|
static void
|
||||||
ip_event_callback (ik_event_t *event)
|
ip_event_callback (ik_event_t *event)
|
||||||
{
|
{
|
||||||
GList* dir_list = NULL;
|
GList* dir_list = NULL;
|
||||||
GList* pair_dir_list = NULL;
|
|
||||||
GList *file_list = NULL;
|
GList *file_list = NULL;
|
||||||
GList *pair_file_list = NULL;
|
|
||||||
|
|
||||||
/* We can ignore the IGNORED events */
|
/* We can ignore the IGNORED events */
|
||||||
if (event->mask & IN_IGNORED)
|
if (event->mask & IN_IGNORED)
|
||||||
@ -618,14 +537,18 @@ ip_event_callback (ik_event_t *event)
|
|||||||
dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->wd));
|
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));
|
file_list = g_hash_table_lookup (wd_file_hash, GINT_TO_POINTER (event->wd));
|
||||||
|
|
||||||
if (event->pair)
|
|
||||||
{
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event->mask & IP_INOTIFY_DIR_MASK)
|
if (event->mask & IP_INOTIFY_DIR_MASK)
|
||||||
ip_event_dispatch (dir_list, pair_dir_list, file_list, pair_file_list, event);
|
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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/* We have to manage the missing list
|
/* We have to manage the missing list
|
||||||
* when we get an event that means the
|
* when we get an event that means the
|
||||||
|
@ -46,7 +46,6 @@ dup_dirname (const gchar *dirname)
|
|||||||
inotify_sub*
|
inotify_sub*
|
||||||
_ih_sub_new (const gchar *dirname,
|
_ih_sub_new (const gchar *dirname,
|
||||||
const gchar *filename,
|
const gchar *filename,
|
||||||
gboolean pair_moves,
|
|
||||||
gboolean watch_hardlinks,
|
gboolean watch_hardlinks,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
@ -55,7 +54,6 @@ _ih_sub_new (const gchar *dirname,
|
|||||||
sub = g_new0 (inotify_sub, 1);
|
sub = g_new0 (inotify_sub, 1);
|
||||||
sub->dirname = dup_dirname (dirname);
|
sub->dirname = dup_dirname (dirname);
|
||||||
sub->filename = g_strdup (filename);
|
sub->filename = g_strdup (filename);
|
||||||
sub->pair_moves = pair_moves;
|
|
||||||
sub->hardlinks = watch_hardlinks;
|
sub->hardlinks = watch_hardlinks;
|
||||||
sub->user_data = user_data;
|
sub->user_data = user_data;
|
||||||
|
|
||||||
|
@ -35,7 +35,6 @@ typedef struct
|
|||||||
|
|
||||||
inotify_sub *_ih_sub_new (const gchar *dirname,
|
inotify_sub *_ih_sub_new (const gchar *dirname,
|
||||||
const gchar *filename,
|
const gchar *filename,
|
||||||
gboolean pair_moves,
|
|
||||||
gboolean watch_hardlinks,
|
gboolean watch_hardlinks,
|
||||||
gpointer user_data);
|
gpointer user_data);
|
||||||
void _ih_sub_free (inotify_sub *sub);
|
void _ih_sub_free (inotify_sub *sub);
|
||||||
|
@ -63,7 +63,6 @@ gio/giostream.c
|
|||||||
gio/glib-compile-resources.c
|
gio/glib-compile-resources.c
|
||||||
gio/glib-compile-schemas.c
|
gio/glib-compile-schemas.c
|
||||||
gio/gloadableicon.c
|
gio/gloadableicon.c
|
||||||
gio/glocaldirectorymonitor.c
|
|
||||||
gio/glocalfile.c
|
gio/glocalfile.c
|
||||||
gio/glocalfileenumerator.c
|
gio/glocalfileenumerator.c
|
||||||
gio/glocalfileinfo.c
|
gio/glocalfileinfo.c
|
||||||
|
Loading…
Reference in New Issue
Block a user