obt/signal.c \
obt/util.h \
obt/watch.h \
+ obt/watch_interface.h \
obt/watch.c \
obt/watch_inotify.c \
+ obt/watch_manual.c \
obt/xqueue.h \
obt/xqueue.c
/*! Called when a change happens in the filesystem. */
static void update(ObtWatch *w, const gchar *base_path,
const gchar *sub_path,
- const gchar *full_path,
ObtWatchNotifyType type,
gpointer data)
{
ObtLinkBaseEntry *remove = NULL;
ObtLinkBaseEntry *show, *hide;
ObtLink *link;
- gchar *id;
+ gchar *id, *full_path;
GList *list, *it;
GList *remove_it = NULL;
gint *priority;
id = obt_link_id_from_ddfile(sub_path);
list = g_hash_table_lookup(self->base, id);
+ full_path = g_build_filename(base_path, sub_path, NULL);
switch (type) {
case OBT_WATCH_SELF_REMOVED:
g_hash_table_steal(self->base, id);
}
}
+ g_free(full_path);
g_free(id);
}
#include "obt/watch.h"
#include <glib.h>
-void func(ObtWatch *w, const gchar *base_path,
- const gchar *subpath, ObtWatchNotifyType type,
- gpointer data)
+static GMainLoop *loop;
+
+void func(ObtWatch *w, const gchar *base_path, const gchar *subpath,
+ ObtWatchNotifyType type, gpointer data)
{
g_print("base path: %s subpath: %s type=%d\n", base_path, subpath, type);
}
+gboolean force_refresh(gpointer data)
+{
+ ObtWatch *w = data;
+ obt_watch_refresh(w);
+ return TRUE;
+}
+
+static void sighandler(gint signal)
+{
+ g_main_loop_quit(loop);
+}
+
gint main()
{
ObtWatch *watch;
- GMainLoop *loop;
+ struct sigaction action, oldaction;
+ sigset_t sigset;
+
+ loop = g_main_loop_new(NULL, FALSE);
+ g_return_val_if_fail(loop != NULL, 1);
watch = obt_watch_new();
obt_watch_add(watch, "/tmp/a", FALSE, func, NULL);
- loop = g_main_loop_new(NULL, FALSE);
+
+ sigemptyset(&sigset);
+ action.sa_handler = sighandler;
+ action.sa_mask = sigset;
+ action.sa_flags = SA_NOCLDSTOP;
+ sigaction(SIGINT, &action, &oldaction);
+
+ g_timeout_add(5000, force_refresh, watch);
+
g_main_loop_run(loop);
+ g_print("bye\n");
+
+ obt_watch_unref(watch);
+
return 0;
}
*/
#include "obt/watch.h"
+#include "obt/watch_interface.h"
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
#include <glib.h>
-typedef struct _ObtWatchTarget ObtWatchTarget;
-
-/*! Callback function for the system-specific GSource to alert us to changes.
-*/
-typedef void (*ObtWatchNotifyFunc)(const gchar *sub_path,
- const gchar *full_path, gpointer target,
- ObtWatchNotifyType type);
-
-
/* Interface for system-specific stuff (e.g. inotify). the functions are
defined in in watch_<system>.c
*/
-/*! Initializes the watch subsystem, and returns a GSource for it.
- @param notify The GSource will call @notify when a watched file is changed.
- @return Returns a GSource* on success, and a NULL if an error occurred.
-*/
-GSource* watch_sys_create_source(ObtWatchNotifyFunc notify);
-/*! Add a target to the watch subsystem.
- @return Returns an integer key that is used to uniquely identify the target
- within this subsystem. A negative value indicates an error.
-*/
-gint watch_sys_add_target(GSource *source, const char *path,
- gboolean watch_hidden, gpointer target);
-/*! Remove a target from the watch system, by its key.
- Use the key returned from watch_sys_add_target() to remove the target.
-*/
-void watch_sys_remove_target(GSource *source, gint key);
-
-
/* General system which uses the watch_sys_* stuff
*/
gchar *base_path;
ObtWatchFunc func;
gpointer data;
- gint key;
+ gboolean watch_hidden;
+ ObtWatchFile *file;
+ gpointer watch_sys_data;
+};
+
+struct _ObtWatchFile {
+ ObtWatchFile *parent;
+ gchar *name;
+ time_t modified;
+ /* If this is a directory, then the hash table contains pointers to other
+ ObtWatchFile objects that are contained in this one. */
+ GHashTable *children_by_name;
+
+ gboolean seen;
+ gpointer watch_sys_data;
};
static void target_free(ObtWatchTarget *t);
-static void target_notify(const gchar *sub_path, const gchar *full_path,
- gpointer target, ObtWatchNotifyType type);
+
+static ObtWatchFile* tree_create(ObtWatchTarget *t,
+ const gchar *path, const gchar *last,
+ ObtWatchFile *parent);
+static void tree_destroy(ObtWatchFile *file);
+
+static void notify(ObtWatchTarget *target, ObtWatchFile *file,
+ ObtWatchNotifyType type);
ObtWatch* obt_watch_new()
{
GSource *source;
w = NULL;
- source = watch_sys_create_source(target_notify);
+ source = watch_sys_create_source();
if (source) {
w = g_slice_new(ObtWatch);
w->ref = 1;
static void target_free(ObtWatchTarget *t)
{
- if (t->key >= 0)
- watch_sys_remove_target(t->w->source, t->key);
+ if (t->file) tree_destroy(t->file);
g_free(t->base_path);
g_slice_free(ObtWatchTarget, t);
}
-gboolean obt_watch_add(ObtWatch *w, const gchar *path,
- gboolean watch_hidden,
+/*! Scans through the filesystem under the target and reports each file/dir
+ that it finds to the watch subsystem.
+ @path Absolute path to the file to scan for.
+ @last The last component (filename) of the file to scan for.
+ @parent The parent directory (if it exists) of the file we are scanning for.
+ */
+static ObtWatchFile* tree_create(ObtWatchTarget *target,
+ const gchar *path, const gchar *last,
+ ObtWatchFile *parent)
+{
+ ObtWatchFile *file;
+ struct stat buf;
+
+ if (stat(path, &buf) < 0)
+ return NULL;
+
+ file = g_slice_new(ObtWatchFile);
+ file->name = g_strdup(last);
+ file->modified = 0;
+ file->parent = parent;
+ file->children_by_name = NULL;
+
+ if (S_ISDIR(buf.st_mode))
+ file->children_by_name =
+ g_hash_table_new_full(g_str_hash, g_str_equal,
+ NULL, (GDestroyNotify)tree_destroy);
+
+ if (!parent) {
+ g_assert(target->file == NULL);
+ target->file = file;
+ }
+ else
+ g_hash_table_replace(parent->children_by_name,
+ file->name,
+ file);
+
+ if (!S_ISDIR(buf.st_mode))
+ notify(target, file, OBT_WATCH_ADDED);
+
+ file->watch_sys_data =
+ watch_sys_add_file(target->w->source,
+ target, file, S_ISDIR(buf.st_mode));
+
+ /* recurse on the contents if it's a directory */
+ if (file && watch_main_file_is_dir(file)) {
+ GDir *dir;
+
+ dir = g_dir_open(path, 0, NULL);
+ if (dir) {
+ const gchar *name;
+
+ while ((name = g_dir_read_name(dir))) {
+ if (name[0] != '.' || target->watch_hidden) {
+ gchar *subpath;
+ ObtWatchFile *child;
+
+ subpath = g_build_filename(path, name, NULL);
+ child = tree_create(target, subpath, name, file);
+ g_free(subpath);
+ }
+ }
+ }
+ g_dir_close(dir);
+ }
+
+ if (file)
+ file->modified = buf.st_mtime;
+
+ return file;
+}
+
+static void tree_destroy(ObtWatchFile *file)
+{
+ g_free(file->name);
+ if (file->children_by_name)
+ g_hash_table_unref(file->children_by_name);
+ g_slice_free(ObtWatchFile, file);
+}
+
+gboolean obt_watch_add(ObtWatch *w, const gchar *path, gboolean watch_hidden,
ObtWatchFunc func, gpointer data)
{
ObtWatchTarget *t;
g_return_val_if_fail(func != NULL, FALSE);
g_return_val_if_fail(path[0] == G_DIR_SEPARATOR, FALSE);
- t = g_slice_new0(ObtWatchTarget);
+ t = g_slice_new(ObtWatchTarget);
t->w = w;
t->base_path = g_strdup(path);
+ t->watch_hidden = watch_hidden;
t->func = func;
t->data = data;
- g_hash_table_insert(w->targets_by_path, t->base_path, t);
+ t->file = NULL;
+ g_hash_table_replace(w->targets_by_path, t->base_path, t);
- t->key = watch_sys_add_target(w->source, path, watch_hidden, t);
- if (t->key < 0) {
- g_hash_table_remove(w->targets_by_path, t->base_path);
- return FALSE;
- }
+ watch_main_notify_add(t, NULL, NULL);
return TRUE;
}
g_return_if_fail(path != NULL);
g_return_if_fail(path[0] == G_DIR_SEPARATOR);
- /* this also calls target_free */
+ /* this also calls target_free which does notifies */
g_hash_table_remove(w->targets_by_path, path);
}
-static void target_notify(const gchar *sub_path, const gchar *full_path,
- gpointer target, ObtWatchNotifyType type)
+static ObtWatchFile* refresh_file(ObtWatchTarget *target, ObtWatchFile *file,
+ ObtWatchFile *parent, const gchar *path)
+{
+ struct stat buf;
+
+ g_assert(file != NULL);
+
+ if (stat(path, &buf) < 0) {
+ watch_main_notify_remove(target, file);
+ file = NULL;
+ }
+
+ else if (S_ISDIR(buf.st_mode) != watch_main_file_is_dir(file)) {
+ gchar *name = g_strdup(file->name);
+
+ watch_main_notify_remove(target, file);
+ watch_main_notify_add(target, parent, name);
+ g_free(name);
+ file = NULL;
+ }
+
+ /* recurse on the contents if it's a directory */
+ else if (watch_main_file_is_dir(file)) {
+ GDir *dir;
+ GList *children, *it;
+
+ children = watch_main_file_children(file);
+ for (it = children; it; it = g_list_next(it))
+ ((ObtWatchFile*)it->data)->seen = FALSE;
+ g_list_free(children);
+
+ dir = g_dir_open(path, 0, NULL);
+ if (dir) {
+ const gchar *name;
+
+ while ((name = g_dir_read_name(dir))) {
+ if (name[0] != '.' || target->watch_hidden) {
+ ObtWatchFile *child = watch_main_file_child(file, name);
+
+ if (!child)
+ watch_main_notify_add(target, file, name);
+ else {
+ gchar *subpath;
+ ObtWatchFile *newchild;
+
+ subpath = g_build_filename(path, name, NULL);
+ newchild = refresh_file(target, child, file, subpath);
+ g_free(subpath);
+
+ if (newchild)
+ child->seen = TRUE;
+ }
+ }
+ }
+ }
+ g_dir_close(dir);
+
+ children = watch_main_file_children(file);
+ for (it = children; it; it = g_list_next(it))
+ if (((ObtWatchFile*)it->data)->seen == FALSE)
+ watch_main_notify_remove(target, it->data);
+ g_list_free(children);
+ }
+
+ /* check for modifications if it's a file */
+ else {
+ if (file->modified >= 0 && buf.st_mtime > file->modified)
+ watch_main_notify_modify(target, file);
+ }
+
+ if (file)
+ file->modified = buf.st_mtime;
+
+ return file;
+}
+
+static void foreach_refresh_target(gpointer k, gpointer v, gpointer u)
+{
+ ObtWatchTarget *target = v;
+
+ if (!target->file)
+ /* we don't have any files being watched, try look for
+ the target's root again */
+ watch_main_notify_add(target, NULL, NULL);
+ else
+ refresh_file(target, target->file, NULL, target->base_path);
+}
+
+void obt_watch_refresh(ObtWatch *w)
+{
+ g_return_if_fail(w != NULL);
+
+ g_hash_table_foreach(w->targets_by_path, foreach_refresh_target, NULL);
+}
+
+static void notify(ObtWatchTarget *target, ObtWatchFile *file,
+ ObtWatchNotifyType type)
+{
+ gchar *sub_path = watch_main_file_sub_path(file);
+ target->func(target->w, target->base_path, sub_path, type,
+ target->data);
+ g_free(sub_path);
+}
+
+void watch_main_notify_add(ObtWatchTarget *target,
+ ObtWatchFile *parent, const gchar *name)
+{
+ gchar *path;
+
+ if (parent && name[0] == '.' && !target->watch_hidden)
+ return;
+
+ path = g_build_filename(target->base_path,
+ watch_main_file_sub_path(parent),
+ name,
+ NULL);
+ tree_create(target, path, name, parent);
+ g_free(path);
+}
+
+static void foreach_child_notify_removed(gpointer k, gpointer v, gpointer u)
{
- ObtWatchTarget *t = target;
- if (type == OBT_WATCH_SELF_REMOVED) {
- /* this also calls target_free */
- g_hash_table_remove(t->w->targets_by_path, t->base_path);
+ ObtWatchTarget *target = u;
+ ObtWatchFile *file = v;
+
+ if (watch_main_file_is_dir(file)) {
+ g_hash_table_foreach(file->children_by_name,
+ foreach_child_notify_removed,
+ target);
+ g_hash_table_remove_all(file->children_by_name);
}
- t->func(t->w, t->base_path, sub_path, full_path, type, t->data);
+ else if (!file->parent)
+ notify(target, file, OBT_WATCH_SELF_REMOVED);
+ else
+ notify(target, file, OBT_WATCH_REMOVED);
+
+ watch_sys_remove_file(target->w->source,
+ target, file, file->watch_sys_data);
+}
+
+void watch_main_notify_remove(ObtWatchTarget *target, ObtWatchFile *file)
+{
+ foreach_child_notify_removed(NULL, file, target);
+
+ tree_destroy(file);
+ if (!file->parent)
+ target->file = NULL;
+}
+
+void watch_main_notify_modify(ObtWatchTarget *target, ObtWatchFile *file)
+{
+ if (!watch_main_file_is_dir(file)) {
+ notify(target, file, OBT_WATCH_MODIFIED);
+ }
+ file->modified = -1;
+}
+
+void build_sub_path(GString *str, ObtWatchFile *file)
+{
+ if (file->parent) {
+ build_sub_path(str, file->parent);
+ g_string_append(str, G_DIR_SEPARATOR_S);
+ g_string_append(str, file->name);
+ }
+}
+
+gboolean watch_main_target_watch_hidden(ObtWatchTarget *target)
+{
+ return target->watch_hidden;
+}
+
+ObtWatchFile* watch_main_target_root(ObtWatchTarget *target)
+{
+ return target->file;
+}
+
+gchar* watch_main_file_sub_path(ObtWatchFile *file)
+{
+ GString *str;
+ gchar *ret = NULL;
+
+ if (file) {
+ str = g_string_new("");
+ build_sub_path(str, file);
+ ret = str->str;
+ g_string_free(str, FALSE);
+ }
+
+ return ret;
+}
+
+gchar* watch_main_target_file_full_path(ObtWatchTarget *target,
+ ObtWatchFile *file)
+{
+ GString *str;
+ gchar *ret = NULL;
+
+ if (file) {
+ str = g_string_new(target->base_path);
+ build_sub_path(str, file);
+ ret = str->str;
+ g_string_free(str, FALSE);
+ }
+
+ return ret;
+}
+
+gboolean watch_main_file_is_dir(ObtWatchFile *file)
+{
+ return file->children_by_name != NULL;
+}
+
+ObtWatchFile* watch_main_file_child(ObtWatchFile *file,
+ const gchar *name)
+{
+ return g_hash_table_lookup(file->children_by_name, name);
+}
+
+GList* watch_main_file_children(ObtWatchFile *file)
+{
+ return g_hash_table_get_values(file->children_by_name);
}
notification is about the watch target itself, the subpath will be
an empty string.
*/
-typedef void (*ObtWatchFunc)(ObtWatch *w, const gchar *base_path,
- const gchar *sub_path, const gchar *full_path,
- ObtWatchNotifyType type, gpointer data);
+typedef void (*ObtWatchFunc)(ObtWatch *w,
+ const gchar *base_path,
+ const gchar *sub_path,
+ ObtWatchNotifyType type,
+ gpointer data);
enum _ObtWatchNotifyType {
OBT_WATCH_ADDED, /*!< A file/dir was added in a watched dir */
ObtWatchFunc func, gpointer data);
void obt_watch_remove(ObtWatch *w, const gchar *path);
+/*! Force a refresh of the watcher.
+ This will report any changes since the last time the watcher refreshed its
+ view of the file system. Note that any watchers that work off notifications
+ will have nothing to report for this function.
+*/
+void obt_watch_refresh(ObtWatch *w);
+
G_END_DECLS
#endif
#ifdef HAVE_SYS_INOTIFY_H
#include "watch.h"
+#include "watch_interface.h"
#include <sys/inotify.h>
#ifdef HAVE_UNISTD_H
#include <glib.h>
typedef struct _InoSource InoSource;
-typedef struct _InoTarget InoTarget;
-
-/*! Callback function in the watch general system.
- Matches definition in watch.c
-*/
-typedef void (*ObtWatchNotifyFunc)(const gchar *sub_path,
- const gchar *full_path, gpointer target,
- ObtWatchNotifyType type);
+typedef struct _InoData InoData;
struct _InoSource {
GSource source;
GPollFD pfd;
- ObtWatchNotifyFunc notify;
GHashTable *targets_by_key;
- GHashTable *targets_by_path;
+ GHashTable *files_by_key;
};
-struct _InoTarget {
+struct _InoData {
gint key;
- gchar *path;
- guint base_len; /* the length of the prefix of path which is the
- target's path */
- gpointer watch_target;
- gboolean is_dir;
- gboolean watch_hidden;
};
static gboolean source_check(GSource *source);
static gboolean source_prepare(GSource *source, gint *timeout);
static gboolean source_read(GSource *source, GSourceFunc cb, gpointer data);
static void source_finalize(GSource *source);
-static gint add_target(GSource *source, InoTarget *parent,
- const gchar *path, gboolean watch_hidden,
- gpointer target);
-static void remove_target(GSource *source, InoTarget *target);
-static void target_free(InoTarget *target);
-static void notify_target(GSource *source, InoTarget *ino_target,
- const gchar *path, ObtWatchNotifyType type);
static GSourceFuncs source_funcs = {
source_prepare,
source_finalize
};
-gint watch_sys_add_target(GSource *source, const char *path,
- gboolean watch_hidden, gpointer target)
-{
- return add_target(source, NULL, path, watch_hidden, target);
-}
-
-void watch_sys_remove_target(GSource *source, gint key)
-{
- InoSource *ino_source = (InoSource*)source;
- InoTarget *t;
-
- t = g_hash_table_lookup(ino_source->targets_by_key, &key);
- remove_target(source, t);
-}
-
-GSource* watch_sys_create_source(ObtWatchNotifyFunc notify)
+GSource* watch_sys_create_source(void)
{
gint fd;
GSource *source;
InoSource *ino_source;
- g_return_val_if_fail(notify != NULL, NULL);
-
source = NULL;
fd = inotify_init();
if (fd < 0) {
g_warning("Failed to initialize inotify: %s", s);
}
else {
- g_debug("initialized inotify on fd %d", fd);
+ g_debug("initialized inotify on fd %u", fd);
source = g_source_new(&source_funcs, sizeof(InoSource));
ino_source = (InoSource*)source;
- ino_source->notify = notify;
ino_source->targets_by_key = g_hash_table_new_full(
g_int_hash, g_int_equal, NULL, NULL);
- ino_source->targets_by_path = g_hash_table_new_full(
- g_str_hash, g_str_equal, NULL, (GDestroyNotify)target_free);
- ino_source->pfd = (GPollFD){ fd, G_IO_IN, G_IO_IN };
+ ino_source->files_by_key = g_hash_table_new_full(
+ g_int_hash, g_int_equal, NULL, NULL);
+ ino_source->pfd = (GPollFD){ fd, G_IO_IN, 0 };
g_source_add_poll(source, &ino_source->pfd);
}
return source;
static gboolean source_check(GSource *source)
{
- return TRUE;
+ InoSource *ino_source = (InoSource*)source;
+
+ return ino_source->pfd.revents;
}
static gboolean source_read(GSource *source, GSourceFunc cb, gpointer data)
name_len == event.len)
{
/* done reading the file name ! */
- InoTarget *t;
- gboolean report;
- ObtWatchNotifyType type;
- gchar *full_path;
- g_debug("read filename %s mask %x", name, event.mask);
+ if (event.len)
+ g_debug("read filename %s mask %x", name, event.mask);
+ else
+ g_debug("read no filename mask %x", event.mask);
event.mask &= ~IN_IGNORED; /* skip this one, we watch for things
to get removed explicitly so this
will just be double-reporting */
if (event.mask) {
+ ObtWatchTarget *t;
+ ObtWatchFile *f;
+ gchar **name_split;
+ gchar **c;
+ f = g_hash_table_lookup(ino_source->files_by_key, &event.wd);
t = g_hash_table_lookup(ino_source->targets_by_key, &event.wd);
- g_assert(t != NULL);
-
- full_path = g_build_filename(t->path, name, NULL);
- g_debug("full path to change: %s", full_path);
-
- /* don't report hidden stuff inside a directory watch */
- report = !t->is_dir || name[0] != '.' || t->watch_hidden;
- if (event.mask & IN_MOVE_SELF) {
- g_warning("Watched target was moved away: %s", t->path);
- type = OBT_WATCH_SELF_REMOVED;
- }
- else if (event.mask & IN_ISDIR) {
- if (event.mask & IN_MOVED_TO ||
- event.mask & IN_CREATE)
- {
- add_target(source, t, full_path, t->watch_hidden,
- t->watch_target);
- g_debug("added %s", full_path);
+ g_assert(f != NULL);
+
+ if (event.len)
+ name_split = g_strsplit(name, G_DIR_SEPARATOR_S, 0);
+ else
+ name_split = g_strsplit("", G_DIR_SEPARATOR_S, 0);
+
+ if (f) {
+ if (event.mask & IN_MOVED_TO || event.mask & IN_CREATE) {
+ ObtWatchFile *parent = f;
+ for (c = name_split; *(c+1); ++c) {
+ parent = watch_main_file_child(parent, *c);
+ g_assert(parent);
+ }
+ watch_main_notify_add(t, parent, *c);
}
else if (event.mask & IN_MOVED_FROM ||
- event.mask & IN_DELETE)
+ event.mask & IN_MOVE_SELF ||
+ event.mask & IN_DELETE ||
+ event.mask & IN_DELETE_SELF)
{
- InoTarget *subt;
-
- subt = g_hash_table_lookup(ino_source->targets_by_path,
- full_path);
- g_assert(subt);
- remove_target(source, subt);
- g_debug("removed %s", full_path);
+ ObtWatchFile *file = f;
+ for (c = name_split; *c; ++c)
+ file = watch_main_file_child(file, *c);
+ if (file) /* may not have been tracked */
+ watch_main_notify_remove(t, file);
+ }
+ else if (event.mask & IN_MODIFY) {
+ ObtWatchFile *file = f;
+ for (c = name_split; *c; ++c)
+ file = watch_main_file_child(file, *c);
+ if (file) /* may not have been tracked */
+ watch_main_notify_modify(t, file);
}
- report = FALSE;
- }
- else {
- if (event.mask & IN_MOVED_TO || event.mask & IN_CREATE)
- type = OBT_WATCH_ADDED;
- else if (event.mask & IN_MOVED_FROM ||
- event.mask & IN_DELETE)
- type = OBT_WATCH_REMOVED;
- else if (event.mask & IN_MODIFY)
- type = OBT_WATCH_MODIFIED;
else
g_assert_not_reached();
- }
- if (report) {
/* call the GSource callback if there is one */
if (cb) cb(data);
-
- /* call the WatchNotify callback */
- notify_target(source, t, full_path, type);
}
- g_free(full_path);
+ g_strfreev(name_split);
/* only read one event at a time, so poll can tell us if there
is another one ready, and we don't block on the read()
state = READING_EVENT;
}
}
+ return TRUE;
}
static void source_finalize(GSource *source)
g_debug("source_finalize");
close(ino_source->pfd.fd);
g_hash_table_destroy(ino_source->targets_by_key);
+ g_hash_table_destroy(ino_source->files_by_key);
}
-static gint add_target(GSource *source, InoTarget *parent,
- const gchar *path,
- gboolean watch_hidden, gpointer target)
+
+gpointer watch_sys_add_file(GSource *source,
+ ObtWatchTarget *target,
+ ObtWatchFile *file,
+ gboolean is_dir)
{
InoSource *ino_source;
- InoTarget *ino_target;
+ InoData *ino_data;
guint32 mask;
gint key;
- gboolean is_dir;
+ gchar *path;
ino_source = (InoSource*)source;
- is_dir = g_file_test(path, G_FILE_TEST_IS_DIR);
if (is_dir)
mask = IN_MODIFY | IN_CREATE | IN_DELETE | IN_MOVE;
else
mask = IN_MODIFY;
/* only watch IN_MOVE_SELF on the top-most target of the watch */
- if (!parent)
- mask |= IN_MOVE_SELF;
+ if (watch_main_target_root(target) == file)
+ mask |= IN_MOVE_SELF | IN_DELETE_SELF;
+
+ path = watch_main_target_file_full_path(target, file);
- ino_target = NULL;
+ ino_data = NULL;
key = inotify_add_watch(ino_source->pfd.fd, path, mask);
g_debug("added watch descriptor %d for fd %d on path %s",
key, ino_source->pfd.fd, path);
g_warning("Unable to watch path %s: %s", path, s);
}
else {
- ino_target = g_slice_new(InoTarget);
- ino_target->key = key;
- ino_target->path = g_strdup(path);
- ino_target->base_len = (parent ? parent->base_len : strlen(path));
- ino_target->is_dir = is_dir;
- ino_target->watch_hidden = watch_hidden;
- ino_target->watch_target = target;
- g_hash_table_insert(ino_source->targets_by_key, &ino_target->key,
- ino_target);
- g_hash_table_insert(ino_source->targets_by_path, ino_target->path,
- ino_target);
+ ino_data = g_slice_new(InoData);
+ ino_data->key = key;
+ g_hash_table_insert(ino_source->targets_by_key,
+ &ino_data->key,
+ target);
+ g_hash_table_insert(ino_source->files_by_key,
+ &ino_data->key,
+ file);
}
- if (key >= 0 && is_dir) {
- /* recurse */
- GDir *dir;
-
- dir = g_dir_open(path, 0, NULL);
- if (dir) {
- const gchar *name;
+ g_free(path);
- while ((name = g_dir_read_name(dir))) {
- if (name[0] != '.' || watch_hidden) {
- gchar *subpath;
-
- subpath = g_build_filename(path, name, NULL);
- if (g_file_test(subpath, G_FILE_TEST_IS_DIR))
- add_target(source, ino_target, subpath,
- watch_hidden, target);
- else
- /* notify for each file in the directory on startup */
- notify_target(source, ino_target, subpath,
- OBT_WATCH_ADDED);
- g_free(subpath);
- }
- }
- }
- g_dir_close(dir);
- }
-
- return key;
+ return ino_data;
}
-static void remove_target(GSource *source, InoTarget *target)
+void watch_sys_remove_file(GSource *source,
+ ObtWatchTarget *target,
+ ObtWatchFile *file,
+ gpointer data)
{
- InoSource *ino_source = (InoSource*)source;
- g_debug("removing wd %d for fd %d", target->key, ino_source->pfd.fd);
- inotify_rm_watch(ino_source->pfd.fd, (guint32)target->key);
- g_hash_table_remove(ino_source->targets_by_key, &target->key);
- g_hash_table_remove(ino_source->targets_by_path, target->path);
-}
+ InoSource *ino_source;
+ InoData *ino_data;
-static void target_free(InoTarget *target)
-{
- g_free(target->path);
- g_slice_free(InoTarget, target);
-}
+ if (data) {
+ ino_source = (InoSource*)source;
+ ino_data = data;
-static void notify_target(GSource *source, InoTarget *ino_target,
- const gchar *path, ObtWatchNotifyType type)
-{
- InoSource *ino_source = (InoSource*)source;
- ino_source->notify(path + ino_target->base_len,
- path,
- ino_target->watch_target,
- type);
+ g_debug("removing wd %d for fd %d", ino_data->key, ino_source->pfd.fd);
+ inotify_rm_watch(ino_source->pfd.fd, (guint32)ino_data->key);
+
+ g_hash_table_remove(ino_source->targets_by_key, &ino_data->key);
+ g_hash_table_remove(ino_source->files_by_key, &ino_data->key);
+ g_slice_free(InoData, ino_data);
+ }
}
+
#endif
--- /dev/null
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/watch_interface.h for the Openbox window manager
+ Copyright (c) 2010 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+#ifndef __obt_watch_interface_h
+#define __obt_watch_interface_h
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct _ObtWatchTarget ObtWatchTarget;
+typedef struct _ObtWatchFile ObtWatchFile;
+
+/*! Initializes the watch subsystem, and returns a GSource for it.
+ @param notify The GSource will call @notify when a watched file is changed.
+ @return Returns a GSource* on success, and a NULL if an error occurred.
+*/
+GSource* watch_sys_create_source(void);
+
+/*! Informs the watch system about a new file/dir.
+ It should return a structure that it wants to store inside the file.
+*/
+gpointer watch_sys_add_file(GSource *source,
+ ObtWatchTarget *target,
+ ObtWatchFile *file,
+ gboolean is_dir);
+
+/*! Informs the watch system about the destruction of a file.
+ It can free the structure returned from watch_sys_add_file as this is given
+ in @data.
+*/
+void watch_sys_remove_file(GSource *source,
+ ObtWatchTarget *target,
+ ObtWatchFile *file,
+ gpointer data);
+
+/* These are in watch.c, they are part of the main watch system */
+void watch_main_notify_add(ObtWatchTarget *target,
+ ObtWatchFile *parent,
+ const gchar *name);
+void watch_main_notify_remove(ObtWatchTarget *target,
+ ObtWatchFile *file);
+void watch_main_notify_modify(ObtWatchTarget *target,
+ ObtWatchFile *file);
+
+gboolean watch_main_target_watch_hidden(ObtWatchTarget *target);
+ObtWatchFile* watch_main_target_root(ObtWatchTarget *target);
+gchar* watch_main_target_file_full_path(ObtWatchTarget *target,
+ ObtWatchFile *file);
+
+gchar* watch_main_file_sub_path(ObtWatchFile *file);
+gboolean watch_main_file_is_dir(ObtWatchFile *file);
+ObtWatchFile* watch_main_file_child(ObtWatchFile *file,
+ const gchar *name);
+GList* watch_main_file_children(ObtWatchFile *file);
+
+G_END_DECLS
+
+#endif
--- /dev/null
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ obt/watch_manual.c for the Openbox window manager
+ Copyright (c) 2010 Dana Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
+/*! This will just do nothing if a better mechanism isn't present, requiring
+ a manual refresh (via obt_watch_refresh()) to see updates in the filesystem.
+*/
+
+#ifndef HAVE_SYS_INOTIFY_H
+
+#include "watch.h"
+#include "watch_interface.h"
+
+#include <glib.h>
+
+typedef struct _ManualSource ManualSource;
+typedef struct _ManualFile ManualFile;
+
+static gboolean source_check(GSource *source);
+static gboolean source_prepare(GSource *source, gint *timeout);
+static gboolean source_read(GSource *source, GSourceFunc cb, gpointer data);
+static void source_finalize(GSource *source);
+
+static GSourceFuncs source_funcs = {
+ source_prepare,
+ source_check,
+ source_read,
+ source_finalize
+};
+
+GSource* watch_sys_create_source()
+{
+ return g_source_new(&source_funcs, sizeof(GSource));
+}
+
+gpointer watch_sys_add_target(ObtWatchTarget *target)
+{
+ return NULL;
+}
+
+void watch_sys_remove_target(ObtWatchTarget *target, gpointer data)
+{
+}
+
+gpointer watch_sys_add_file(GSource *source,
+ ObtWatchTarget *target,
+ ObtWatchFile *file,
+ gboolean is_dir)
+{
+ return NULL;
+}
+
+void watch_sys_remove_file(GSource *source,
+ ObtWatchTarget *target,
+ ObtWatchFile *file,
+ gpointer data)
+{
+}
+
+static gboolean source_prepare(GSource *source, gint *timeout)
+{
+ *timeout = -1;
+ return FALSE;
+}
+
+static gboolean source_check(GSource *source)
+{
+ return FALSE;
+}
+
+static gboolean source_read(GSource *source, GSourceFunc cb, gpointer data)
+{
+ return TRUE;
+}
+
+static void source_finalize(GSource *source)
+{
+}
+
+#endif