Implemented watching folders on win32

master
Yevgen Muntyan 2006-12-08 02:28:18 -06:00
parent 87755fe791
commit 77713b92e0
8 changed files with 903 additions and 61 deletions

View File

@ -923,17 +923,18 @@ file_watch_event (G_GNUC_UNUSED MooFileWatch *watch,
switch (event->code)
{
case MOO_FILE_WATCH_CHANGED:
case MOO_FILE_WATCH_EVENT_CHANGED:
edit->priv->modified_on_disk = TRUE;
break;
case MOO_FILE_WATCH_DELETED:
case MOO_FILE_WATCH_EVENT_DELETED:
edit->priv->deleted_from_disk = TRUE;
edit->priv->file_monitor_id = 0;
break;
case MOO_FILE_WATCH_CREATED:
case MOO_FILE_WATCH_MOVED:
case MOO_FILE_WATCH_EVENT_CREATED:
case MOO_FILE_WATCH_EVENT_MOVED:
case MOO_FILE_WATCH_EVENT_ERROR:
g_return_if_reached ();
}

View File

@ -828,16 +828,20 @@ fam_event (MooFolder *folder,
switch (event->code)
{
case MOO_FILE_WATCH_CHANGED:
case MOO_FILE_WATCH_EVENT_CHANGED:
file_changed (folder, event->filename);
break;
case MOO_FILE_WATCH_DELETED:
case MOO_FILE_WATCH_EVENT_DELETED:
file_deleted (folder, event->filename);
break;
case MOO_FILE_WATCH_CREATED:
case MOO_FILE_WATCH_EVENT_CREATED:
file_created (folder, event->filename);
break;
default:
case MOO_FILE_WATCH_EVENT_ERROR:
/* XXX */
file_deleted (folder, folder->priv->path);
break;
case MOO_FILE_WATCH_EVENT_MOVED:
break;
}
}
@ -869,7 +873,7 @@ start_monitor (MooFolder *folder)
if (!folder->priv->fam_request)
{
g_warning ("%s: moo_fam_monitor_directory failed", G_STRLOC);
g_warning ("%s: moo_fam_monitor_directory failed for path '%s'", G_STRLOC, folder->priv->path);
g_warning ("%s: %s", G_STRLOC, error->message);
g_error_free (error);
return;
@ -1259,10 +1263,6 @@ _moo_file_unref (MooFile *file)
}
#ifdef __WIN32__
#define lstat stat
#endif
static void
moo_file_stat_unix (MooFile *file,
const char *dirname)
@ -1279,7 +1279,9 @@ moo_file_stat_unix (MooFile *file,
g_free (file->link_target);
file->link_target = NULL;
if (lstat (fullname, &file->statbuf) != 0)
errno = 0;
if (g_lstat (fullname, &file->statbuf) != 0)
{
if (errno == ENOENT)
{
@ -1310,8 +1312,9 @@ moo_file_stat_unix (MooFile *file,
gssize len;
file->info |= MOO_FILE_INFO_IS_LINK;
errno = 0;
if (stat (fullname, &file->statbuf) != 0)
if (g_stat (fullname, &file->statbuf) != 0)
{
if (errno == ENOENT)
{

View File

@ -1217,7 +1217,7 @@ init_actions (MooFileView *fileview)
"closure-callback", file_view_paste_clipboard,
NULL);
#ifdef __WIN32__
#if defined(__WIN32__) && 0
moo_action_group_add_action (group, "Reload",
"label", _("Reload"),
"tooltip", _("Reload"),

View File

@ -22,6 +22,10 @@ gtk_2_4_sources = \
newgtk/gtkfontbutton.c \
newgtk/gtkfontbutton.h
win32_sources = \
mooutils-thread.c \
mooutils-thread.h
mooutils_include_headers = \
mooaction.h \
mooactionbase.h \
@ -132,6 +136,9 @@ endif
if !GTK_2_4
mooutils_sources += $(moonewgtk_gtk_2_4_sources)
endif
if MOO_OS_MINGW
mooutils_sources += $(win32_sources)
endif
nodist_mooutils_sources = \
moomarshals.c \
@ -146,6 +153,7 @@ EXTRA_DIST = \
xml2h.sh \
$(gmappedfile_sources) \
$(gtk_2_4_sources) \
$(win32_sources) \
glade/accelbutton.glade \
glade/accelprefs.glade \
glade/moologwindow.glade \

View File

@ -20,6 +20,7 @@
#endif
#ifdef __WIN32__
#include "mooutils/mooutils-thread.h"
#include <windows.h>
#include <io.h>
#include <fcntl.h>
@ -29,6 +30,7 @@
#include <time.h>
#endif
#include <glib/gstdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
@ -40,7 +42,7 @@
#include "mooutils/moocompat.h"
#if 0
#if defined(__WIN32__) && 0
#define DEBUG_PRINT g_message
#else
static void DEBUG_PRINT (G_GNUC_UNUSED const char *format, ...)
@ -53,7 +55,8 @@ static const char *event_code_strings[] = {
"CHANGED",
"DELETED",
"CREATED",
"MOVED"
"MOVED",
"ERROR"
};
typedef struct _Monitor Monitor;
@ -87,7 +90,8 @@ typedef struct {
GError **error);
void (*suspend) (Monitor *monitor);
void (*resume) (Monitor *monitor);
void (*delete) (Monitor *monitor);
void (*delete) (MooFileWatch *watch,
Monitor *monitor);
} WatchFuncs;
@ -96,6 +100,9 @@ struct _MooFileWatchPrivate {
#ifdef MOO_USE_FAM
FAMConnection fam_connection;
guint fam_connection_watch;
#endif
#ifdef __WIN32__
guint id;
#endif
WatchFuncs funcs;
MooFileWatchMethod method;
@ -121,7 +128,8 @@ static Monitor *monitor_fam_create (MooFileWatch *watch,
GError **error);
static void monitor_fam_suspend (Monitor *monitor);
static void monitor_fam_resume (Monitor *monitor);
static void monitor_fam_delete (Monitor *monitor);
static void monitor_fam_delete (MooFileWatch *watch,
Monitor *monitor);
#endif /* MOO_USE_FAM */
static gboolean watch_stat_start (MooFileWatch *watch,
@ -136,7 +144,8 @@ static Monitor *monitor_stat_create (MooFileWatch *watch,
GError **error);
static void monitor_stat_suspend (Monitor *monitor);
static void monitor_stat_resume (Monitor *monitor);
static void monitor_stat_delete (Monitor *monitor);
static void monitor_stat_delete (MooFileWatch *watch,
Monitor *monitor);
#ifdef __WIN32__
static gboolean watch_win32_start (MooFileWatch *watch,
@ -151,7 +160,8 @@ static Monitor *monitor_win32_create (MooFileWatch *watch,
GError **error);
static void monitor_win32_suspend (Monitor *monitor);
static void monitor_win32_resume (Monitor *monitor);
static void monitor_win32_delete (Monitor *monitor);
static void monitor_win32_delete (MooFileWatch *watch,
Monitor *monitor);
#endif /* __WIN32__ */
@ -339,10 +349,11 @@ moo_file_watch_event_code_get_type (void)
if (!type)
{
static const GEnumValue values[] = {
{MOO_FILE_WATCH_CHANGED, (char*) "MOO_FILE_WATCH_CHANGED", (char*) "changed"},
{MOO_FILE_WATCH_DELETED, (char*) "MOO_FILE_WATCH_DELETED", (char*) "deleted"},
{MOO_FILE_WATCH_CREATED, (char*) "MOO_FILE_WATCH_CREATED", (char*) "created"},
{MOO_FILE_WATCH_MOVED, (char*) "MOO_FILE_WATCH_MOVED", (char*) "moved"},
{ MOO_FILE_WATCH_EVENT_CHANGED, (char*) "MOO_FILE_WATCH_EVENT_CHANGED", (char*) "changed"},
{ MOO_FILE_WATCH_EVENT_DELETED, (char*) "MOO_FILE_WATCH_EVENT_DELETED", (char*) "deleted"},
{ MOO_FILE_WATCH_EVENT_CREATED, (char*) "MOO_FILE_WATCH_EVENT_CREATED", (char*) "created"},
{ MOO_FILE_WATCH_EVENT_MOVED, (char*) "MOO_FILE_WATCH_EVENT_MOVED", (char*) "moved"},
{ MOO_FILE_WATCH_EVENT_ERROR, (char*) "MOO_FILE_WATCH_EVENT_ERROR", (char*) "error"},
{ 0, NULL, NULL }
};
@ -450,7 +461,7 @@ moo_file_watch_cancel_monitor (MooFileWatch *watch,
GINT_TO_POINTER (monitor_id));
g_return_if_fail (monitor != NULL);
watch->priv->funcs.delete (monitor);
watch->priv->funcs.delete (watch, monitor);
watch->priv->monitors = g_slist_remove (watch->priv->monitors, monitor);
g_hash_table_remove (watch->priv->requests,
GINT_TO_POINTER (monitor_id));
@ -464,7 +475,7 @@ emit_event (MooFileWatch *watch,
DEBUG_PRINT ("watch %p, monitor %d: event %s for %s",
watch, event->monitor_id,
event_code_strings[event->code],
event->filename);
event->filename ? event->filename : "<NULL>");
g_signal_emit (watch, signals[EVENT], 0, event);
}
@ -498,10 +509,12 @@ moo_file_watch_close (MooFileWatch *watch,
watch->priv->alive = FALSE;
g_slist_foreach (watch->priv->monitors,
(GFunc) watch->priv->funcs.delete, NULL);
g_slist_free (watch->priv->monitors);
watch->priv->monitors = NULL;
while (watch->priv->monitors)
{
watch->priv->funcs.delete (watch, watch->priv->monitors->data);
watch->priv->monitors =
g_slist_delete_link (watch->priv->monitors, watch->priv->monitors);
}
return watch->priv->funcs.shutdown (watch, error);
}
@ -697,7 +710,8 @@ monitor_fam_resume (Monitor *monitor)
static void
monitor_fam_delete (Monitor *monitor)
monitor_fam_delete (G_GNUC_UNUSED MooFileWatch *watch,
Monitor *monitor)
{
FAMRequest fr;
int result;
@ -793,20 +807,21 @@ read_fam_events (G_GNUC_UNUSED GIOChannel *source,
event.monitor_id = fe.fr.reqnum;
event.filename = fe.filename;
event.data = fe.userdata;
event.error = NULL;
switch (fe.code)
{
case FAMChanged:
event.code = MOO_FILE_WATCH_CHANGED;
event.code = MOO_FILE_WATCH_EVENT_CHANGED;
break;
case FAMDeleted:
event.code = MOO_FILE_WATCH_DELETED;
event.code = MOO_FILE_WATCH_EVENT_DELETED;
break;
case FAMCreated:
event.code = MOO_FILE_WATCH_CREATED;
event.code = MOO_FILE_WATCH_EVENT_CREATED;
break;
case FAMMoved:
event.code = MOO_FILE_WATCH_MOVED;
event.code = MOO_FILE_WATCH_EVENT_MOVED;
break;
case FAMStartExecuting:
@ -886,7 +901,9 @@ monitor_stat_create (MooFileWatch *watch,
g_return_val_if_fail (MOO_IS_FILE_WATCH (watch), NULL);
g_return_val_if_fail (filename != NULL, NULL);
if (stat (filename, &buf))
errno = 0;
if (g_stat (filename, &buf) != 0)
{
int saved_errno = errno;
g_set_error (error, MOO_FILE_WATCH_ERROR,
@ -950,7 +967,8 @@ monitor_stat_resume (Monitor *monitor)
static void
monitor_stat_delete (Monitor *monitor)
monitor_stat_delete (G_GNUC_UNUSED MooFileWatch *watch,
Monitor *monitor)
{
g_return_if_fail (monitor != NULL);
DEBUG_PRINT ("removing monitor for '%s'", monitor->filename);
@ -991,9 +1009,11 @@ do_stat (MooFileWatch *watch)
old = monitor->statbuf.st_mtime;
if (stat (monitor->filename, &monitor->statbuf))
errno = 0;
if (g_stat (monitor->filename, &monitor->statbuf) != 0)
{
event.code = MOO_FILE_WATCH_DELETED;
event.code = MOO_FILE_WATCH_EVENT_DELETED;
event.monitor_id = monitor->request;
event.filename = monitor->filename;
event.data = monitor->user_data;
@ -1007,7 +1027,7 @@ do_stat (MooFileWatch *watch)
}
else if (monitor->statbuf.st_mtime > old)
{
event.code = MOO_FILE_WATCH_CHANGED;
event.code = MOO_FILE_WATCH_EVENT_CHANGED;
event.monitor_id = monitor->request;
event.filename = monitor->filename;
event.data = monitor->user_data;
@ -1062,11 +1082,473 @@ errno_to_file_error (int code)
*/
#ifdef __WIN32__
typedef struct {
guint watch_id;
guint request;
char *path;
} FAMThreadWatch;
typedef struct {
GMutex *lock;
GAsyncQueue *incoming;
HANDLE events[MAXIMUM_WAIT_OBJECTS];
FAMThreadWatch watches[MAXIMUM_WAIT_OBJECTS];
guint n_events;
} FAMThread;
typedef enum {
COMMAND_ADD_PATH,
COMMAND_REMOVE_PATH
} FAMThreadCommandType;
typedef struct {
FAMThreadCommandType type;
char *path;
guint watch_id;
guint request;
} FAMThreadCommand;
typedef struct {
FAMThread thread_data;
guint event_id;
gboolean running;
GSList *watches;
} FAMWin32;
typedef struct {
gboolean error;
guint watch_id;
guint request;
guint code;
char *msg;
} FAMEvent;
static FAMWin32 *fam;
static guint last_watch_id;
/****************************************************************************/
/* Watch thread
*/
static void
fam_event_free (FAMEvent *event)
{
if (event)
{
g_free (event->msg);
g_free (event);
}
}
static void
fam_thread_event (guint code,
gboolean error,
const char *msg,
guint watch_id,
guint request)
{
FAMEvent *event = g_new0 (FAMEvent, 1);
event->error = error;
event->code = code;
event->watch_id = watch_id;
event->request = request;
event->msg = g_strdup (msg);
_moo_event_queue_push (fam->event_id, event, (GDestroyNotify) fam_event_free);
}
static void
fam_thread_add_path (FAMThread *thr,
const char *path,
guint watch_id,
guint request)
{
gunichar2 *win_path;
if (thr->n_events == MAXIMUM_WAIT_OBJECTS)
{
g_critical ("%s: too many folders watched", G_STRLOC);
return;
}
win_path = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL);
if (!win_path)
{
char *msg = g_strdup_printf ("Could not convert filename '%s' to UTF16",
path);
fam_thread_event (MOO_FILE_WATCH_ERROR_BAD_FILENAME, TRUE, msg, watch_id, request);
g_free (msg);
return;
}
thr->events[thr->n_events] =
FindFirstChangeNotificationW (win_path, FALSE,
FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME);
if (thr->events[thr->n_events] == INVALID_HANDLE_VALUE)
{
char *err = g_win32_error_message (GetLastError ());
char *msg = g_strdup_printf ("Could not convert start watch for '%s': %s", path, err);
fam_thread_event (MOO_FILE_WATCH_ERROR_FAILED, TRUE, msg, watch_id, request);
g_free (msg);
g_free (err);
return;
}
thr->watches[thr->n_events].watch_id = watch_id;
thr->watches[thr->n_events].request = request;
thr->watches[thr->n_events].path = g_strdup (path);
thr->n_events += 1;
}
static void
fam_thread_remove_path (FAMThread *thr,
guint watch_id,
guint request)
{
guint i;
for (i = 1; i < thr->n_events; ++i)
{
if (thr->watches[i].watch_id == watch_id &&
thr->watches[i].request == request)
{
FindCloseChangeNotification (thr->events[i]);
g_free (thr->watches[i].path);
if (i < thr->n_events - 1)
{
memmove (&thr->watches[i], &thr->watches[i+1],
(thr->n_events - i - 1) * sizeof(thr->watches[i]));
memmove (&thr->events[i], &thr->events[i+1],
(thr->n_events - i - 1) * sizeof(thr->events[i]));
}
thr->n_events -= 1;
return;
}
}
g_critical ("%s: can't remove watch %d, request %d",
G_STRLOC, watch_id, request);
}
static void
fam_thread_check_dir (FAMThread *thr,
guint idx)
{
struct stat buf;
errno = 0;
if (g_stat (thr->watches[idx].path, &buf) != 0 &&
errno == ENOENT)
{
fam_thread_event (MOO_FILE_WATCH_EVENT_DELETED,
FALSE, NULL,
thr->watches[idx].watch_id,
thr->watches[idx].request);
fam_thread_remove_path (thr,
thr->watches[idx].watch_id,
thr->watches[idx].request);
}
else
{
fam_thread_event (MOO_FILE_WATCH_EVENT_CHANGED,
FALSE, NULL,
thr->watches[idx].watch_id,
thr->watches[idx].request);
if (!FindNextChangeNotification (thr->events[idx]))
{
char *err = g_win32_error_message (GetLastError ());
char *msg = g_strdup_printf ("Error in FindNextChangeNotification: %s", err);
fam_thread_event (MOO_FILE_WATCH_ERROR_FAILED, TRUE, msg,
thr->watches[idx].watch_id,
thr->watches[idx].request);
fam_thread_remove_path (thr,
thr->watches[idx].watch_id,
thr->watches[idx].request);
g_free (msg);
g_free (err);
}
}
}
static void
fam_thread_do_command (FAMThread *thr)
{
FAMThreadCommand *cmd;
GSList *list = NULL;
DEBUG_PRINT ("fam_thread_do_command start");
g_mutex_lock (thr->lock);
while ((cmd = g_async_queue_try_pop (thr->incoming)))
list = g_slist_prepend (list, cmd);
ResetEvent (thr->events[0]);
g_mutex_unlock (thr->lock);
list = g_slist_reverse (list);
while (list)
{
cmd = list->data;
switch (cmd->type)
{
case COMMAND_ADD_PATH:
fam_thread_add_path (thr, cmd->path, cmd->watch_id, cmd->request);
break;
case COMMAND_REMOVE_PATH:
fam_thread_remove_path (thr, cmd->watch_id, cmd->request);
break;
}
list = g_slist_delete_link (list, list);
g_free (cmd->path);
g_free (cmd);
}
DEBUG_PRINT ("fam_thread_do_command end");
}
static gpointer
fam_thread_main (FAMThread *thr)
{
while (TRUE)
{
int ret;
DEBUG_PRINT ("calling WaitForMultipleObjects for %d handles", thr->n_events);
ret = WaitForMultipleObjects (thr->n_events, thr->events, FALSE, INFINITE);
DEBUG_PRINT ("WaitForMultipleObjects returned %d", ret);
if (ret == (int) WAIT_FAILED)
{
char *msg = g_win32_error_message (GetLastError ());
g_critical ("%s: %s", G_STRLOC, msg);
g_free (msg);
break;
}
if (ret == WAIT_OBJECT_0)
fam_thread_do_command (thr);
else if (WAIT_OBJECT_0 < ret && ret < (int) thr->n_events)
fam_thread_check_dir (thr, ret - WAIT_OBJECT_0);
else
{
g_critical ("%s: oops", G_STRLOC);
break;
}
}
/* XXX cleanup */
return NULL;
}
static void
fam_thread_command (FAMThreadCommandType type,
const char *filename,
guint watch_id,
guint request)
{
FAMThreadCommand *cmd;
cmd = g_new0 (FAMThreadCommand, 1);
cmd->type = type;
cmd->path = g_strdup (filename);
cmd->watch_id = watch_id;
cmd->request = request;
g_mutex_lock (fam->thread_data.lock);
g_async_queue_push (fam->thread_data.incoming, cmd);
SetEvent (fam->thread_data.events[0]);
g_mutex_unlock (fam->thread_data.lock);
}
/****************************************************************************/
/* Monitors
*/
static MooFileWatch *
find_watch (guint id)
{
GSList *l;
for (l = fam->watches; l != NULL; l = l->next)
{
MooFileWatch *watch = l->data;
if (watch->priv->id == id)
return watch;
}
return NULL;
}
static void
do_event (FAMEvent *event)
{
MooFileWatch *watch;
Monitor *monitor;
MooFileWatchEvent watch_event;
watch = find_watch (event->watch_id);
if (!watch)
{
DEBUG_PRINT ("got event for dead watch %d", event->watch_id);
return;
}
monitor = g_hash_table_lookup (watch->priv->requests,
GUINT_TO_POINTER (event->request));
if (!monitor)
{
DEBUG_PRINT ("got event for dead monitor %d", event->request);
return;
}
watch_event.monitor_id = event->request;
watch_event.filename = monitor->filename;
watch_event.data = monitor->user_data;
watch_event.error = NULL;
if (event->error)
{
watch_event.code = MOO_FILE_WATCH_EVENT_ERROR;
g_set_error (&watch_event.error, MOO_FILE_WATCH_ERROR,
event->code,
event->msg ? event->msg : "FAILED");
DEBUG_PRINT ("got error for watch %d: %s", event->watch_id, watch_event.error->message);
}
else
{
DEBUG_PRINT ("got event for filename %s", monitor->filename);
watch_event.code = event->code;
}
emit_event (watch, &watch_event);
if (watch_event.error)
g_error_free (watch_event.error);
}
static void
event_callback (GList *events)
{
GList *trimmed = NULL;
while (events)
{
GList *l;
FAMEvent *event = events->data;
gboolean found = FALSE;
event = events->data;
events = events->next;
for (l = trimmed; l != NULL; l = l->next)
{
FAMEvent *old_event = l->data;
if (old_event->watch_id == event->watch_id &&
old_event->request == event->request)
{
found = TRUE;
if (!old_event->error && event->error)
l->data = event;
break;
}
}
if (!found)
trimmed = g_list_prepend (trimmed, event);
}
trimmed = g_list_reverse (trimmed);
while (trimmed)
{
do_event (trimmed->data);
trimmed = g_list_delete_link (trimmed, trimmed);
}
}
static gboolean
fam_win32_init (void)
{
if (!fam)
{
GError *error = NULL;
fam = g_new0 (FAMWin32, 1);
fam->running = FALSE;
fam->thread_data.n_events = 1;
fam->thread_data.events[0] = CreateEvent (NULL, TRUE, FALSE, NULL);;
if (!fam->thread_data.events[0])
{
char *msg = g_win32_error_message (GetLastError ());
g_critical ("%s: could not create incoming event", G_STRLOC);
g_critical ("%s: %s", G_STRLOC, msg);
g_free (msg);
return FALSE;
}
fam->thread_data.incoming = g_async_queue_new ();
fam->thread_data.lock = g_mutex_new ();
fam->event_id = _moo_event_queue_connect ((MooEventQueueCallback) event_callback,
NULL, NULL);
if (!g_thread_create ((GThreadFunc) fam_thread_main, &fam->thread_data, FALSE, &error))
{
g_critical ("%s: could not start watch thread", G_STRLOC);
g_critical ("%s: %s", G_STRLOC, error->message);
g_error_free (error);
}
else
{
fam->running = TRUE;
DEBUG_PRINT ("initialized folder watch");
}
}
return fam->running;
}
static gboolean
watch_win32_start (MooFileWatch *watch,
GError **error)
{
return watch_stat_start (watch, error);
if (!watch_stat_start (watch, error))
return FALSE;
if (!fam_win32_init ())
{
g_set_error (error, MOO_FILE_WATCH_ERROR,
MOO_FILE_WATCH_ERROR_FAILED,
"Could not initialize folder watch thread");
watch_stat_shutdown (watch, NULL);
return FALSE;
}
watch->priv->id = ++last_watch_id;
fam->watches = g_slist_prepend (fam->watches, watch);
DEBUG_PRINT ("started watch %d", watch->priv->id);
return TRUE;
}
@ -1086,20 +1568,61 @@ monitor_win32_create (MooFileWatch *watch,
int *request,
GError **error)
{
Monitor *monitor;
struct stat buf;
g_return_val_if_fail (MOO_IS_FILE_WATCH (watch), NULL);
g_return_val_if_fail (filename != NULL, FALSE);
if (type == MONITOR_FILE)
return monitor_stat_create (watch, type, filename, data, request, error);
#ifdef __GNUC__
#warning "Implement monitor_win32_create()"
#endif
errno = 0;
g_set_error (error, MOO_FILE_WATCH_ERROR,
MOO_FILE_WATCH_ERROR_NOT_IMPLEMENTED,
"watching folders is not implemented on win32");
if (g_stat (filename, &buf) != 0)
{
int saved_errno = errno;
g_set_error (error, MOO_FILE_WATCH_ERROR,
errno_to_file_error (saved_errno),
"stat: %s", g_strerror (saved_errno));
return NULL;
}
return NULL;
if (type == MONITOR_DIR && !S_ISDIR (buf.st_mode))
{
char *display_name = g_filename_display_name (filename);
g_set_error (error, MOO_FILE_WATCH_ERROR,
MOO_FILE_WATCH_ERROR_NOT_DIR,
"%s is not a directory", display_name);
g_free (display_name);
return NULL;
}
else if (type == MONITOR_FILE && S_ISDIR (buf.st_mode)) /* it's fatal on windows */
{
char *display_name = g_filename_display_name (filename);
g_set_error (error, MOO_FILE_WATCH_ERROR,
MOO_FILE_WATCH_ERROR_IS_DIR,
"%s is a directory", display_name);
g_free (display_name);
return NULL;
}
DEBUG_PRINT ("created monitor for '%s'", filename);
monitor = g_new0 (Monitor, 1);
monitor->parent = watch;
monitor->type = type;
monitor->filename = g_strdup (filename);
monitor->user_data = data;
monitor->request = ++last_monitor_id;
monitor->suspended = FALSE;
if (request)
*request = monitor->request;
fam_thread_command (COMMAND_ADD_PATH, filename, watch->priv->id, monitor->request);
return monitor;
}
@ -1112,9 +1635,12 @@ monitor_win32_suspend (Monitor *monitor)
return;
if (monitor->type == MONITOR_FILE)
return monitor_stat_suspend (monitor);
{
monitor_stat_suspend (monitor);
return;
}
g_return_if_reached ();
g_critical ("%s: implement me", G_STRFUNC);
}
@ -1127,20 +1653,30 @@ monitor_win32_resume (Monitor *monitor)
return;
if (monitor->type == MONITOR_FILE)
return monitor_stat_resume (monitor);
{
monitor_stat_resume (monitor);
return;
}
g_return_if_reached ();
g_critical ("%s: implement me", G_STRFUNC);
}
static void
monitor_win32_delete (Monitor *monitor)
monitor_win32_delete (MooFileWatch *watch,
Monitor *monitor)
{
g_return_if_fail (monitor != NULL);
if (monitor->type == MONITOR_FILE)
return monitor_stat_delete (monitor);
{
monitor_stat_delete (watch, monitor);
return;
}
g_return_if_reached ();
DEBUG_PRINT ("removing monitor for '%s'", monitor->filename);
fam_thread_command (COMMAND_REMOVE_PATH, monitor->filename, watch->priv->id, monitor->request);
g_free (monitor->filename);
g_free (monitor);
}
#endif /* __WIN32__ */

View File

@ -59,10 +59,11 @@ typedef enum {
/* Stripped FAMEventCode enumeration */
typedef enum {
MOO_FILE_WATCH_CHANGED = 1,
MOO_FILE_WATCH_DELETED = 2,
MOO_FILE_WATCH_CREATED = 3,
MOO_FILE_WATCH_MOVED = 4
MOO_FILE_WATCH_EVENT_CHANGED = 1,
MOO_FILE_WATCH_EVENT_DELETED = 2,
MOO_FILE_WATCH_EVENT_CREATED = 3,
MOO_FILE_WATCH_EVENT_MOVED = 4,
MOO_FILE_WATCH_EVENT_ERROR = 5
} MooFileWatchEventCode;
/* The structure has the same meaning as the FAMEvent
@ -76,6 +77,7 @@ struct _MooFileWatchEvent {
MooFileWatchEventCode code; /* FAMEventCode */
int monitor_id; /* FAMRequest */
char *filename;
GError *error;
gpointer data;
};

View File

@ -0,0 +1,251 @@
/*
* mooutils-thread.c
*
* Copyright (C) 2004-2006 by Yevgen Muntyan <muntyan@math.tamu.edu>
*
* 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.
*
* See COPYING file that comes with this distribution.
*/
#include "config.h"
#include "mooutils/mooutils-thread.h"
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
typedef struct {
MooEventQueueCallback callback;
gpointer callback_data;
GDestroyNotify notify;
guint id;
} QueueClient;
typedef struct {
gpointer data;
GDestroyNotify destroy;
guint id;
} EventData;
typedef struct {
GSList *clients;
guint last_id;
int pipe_in;
int pipe_out;
GIOChannel *io;
GHashTable *data;
} EventQueue;
static GStaticMutex queue_lock = G_STATIC_MUTEX_INIT;
static EventQueue *queue;
static QueueClient *
get_event_client (guint id)
{
GSList *l;
g_return_val_if_fail (queue != NULL, NULL);
for (l = queue->clients; l != NULL; l = l->next)
{
QueueClient *s = l->data;
if (s->id == id)
return s;
}
return NULL;
}
static void
invoke_callback (gpointer id,
GQueue *events)
{
GList *l;
QueueClient *client;
g_message ("processing events for id %u", GPOINTER_TO_UINT (id));
client = get_event_client (GPOINTER_TO_UINT (id));
if (client)
client->callback (events->head, client->callback_data);
for (l = events->head; l != NULL; l = l->next)
{
EventData *data = l->data;
if (data->destroy)
data->destroy (data->data);
g_free (data);
}
g_queue_free (events);
}
static gboolean
got_data (GIOChannel *io)
{
GHashTable *data;
char buf[1];
g_static_mutex_lock (&queue_lock);
data = queue->data;
queue->data = NULL;
g_io_channel_read_chars (io, buf, 1, NULL, NULL);
g_static_mutex_unlock (&queue_lock);
g_hash_table_foreach (data, (GHFunc) invoke_callback, NULL);
g_hash_table_destroy (data);
return TRUE;
}
void
_moo_event_queue_do_events (guint event_id)
{
GQueue *events = NULL;
g_return_if_fail (queue != NULL);
g_static_mutex_lock (&queue_lock);
if (queue->data)
{
events = g_hash_table_lookup (queue->data, GUINT_TO_POINTER (event_id));
if (events)
g_hash_table_remove (queue->data, GUINT_TO_POINTER (event_id));
}
g_static_mutex_unlock (&queue_lock);
if (events)
invoke_callback (GUINT_TO_POINTER (event_id), events);
}
static void
init_queue (void)
{
int fds[2];
if (queue)
return;
if (pipe (fds) != 0)
{
perror ("pipe");
return;
}
queue = g_new0 (EventQueue, 1);
queue->clients = NULL;
queue->pipe_in = fds[1];
queue->pipe_out = fds[0];
#ifdef __WIN32__
queue->io = g_io_channel_win32_new_fd (queue->pipe_out);
#else
queue->io = g_io_channel_unix_new (queue->pipe_out);
#endif
g_io_add_watch (queue->io, G_IO_IN, (GIOFunc) got_data, NULL);
queue->data = NULL;
}
guint
_moo_event_queue_connect (MooEventQueueCallback callback,
gpointer data,
GDestroyNotify notify)
{
QueueClient *client;
g_return_val_if_fail (callback != NULL, 0);
init_queue ();
client = g_new0 (QueueClient, 1);
client->id = queue->last_id++;
client->callback = callback;
client->callback_data = data;
client->notify = notify;
queue->clients = g_slist_prepend (queue->clients, client);
return client->id;
}
void
_moo_event_queue_disconnect (guint event_id)
{
QueueClient *client;
g_return_if_fail (event_id != 0);
g_return_if_fail (queue != NULL);
client = get_event_client (event_id);
g_return_if_fail (client != NULL);
queue->clients = g_slist_remove (queue->clients, client);
if (client->notify)
client->notify (client->callback_data);
g_free (client);
}
/* called from a thread */
void
_moo_event_queue_push (guint event_id,
gpointer data,
GDestroyNotify data_destroy)
{
char c = 'd';
EventData *event_data;
GQueue *events;
event_data = g_new (EventData, 1);
event_data->data = data;
event_data->destroy = data_destroy;
event_data->id = event_id;
g_static_mutex_lock (&queue_lock);
if (!queue->data)
{
write (queue->pipe_in, &c, 1);
queue->data = g_hash_table_new (g_direct_hash, g_direct_equal);
}
events = g_hash_table_lookup (queue->data, GUINT_TO_POINTER (event_id));
if (!events)
{
events = g_queue_new ();
g_hash_table_insert (queue->data, GUINT_TO_POINTER (event_id), events);
}
g_queue_push_tail (events, event_data);
g_static_mutex_unlock (&queue_lock);
}

View File

@ -0,0 +1,41 @@
/*
* mooutils-thread.h
*
* Copyright (C) 2004-2006 by Yevgen Muntyan <muntyan@math.tamu.edu>
*
* 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.
*
* See COPYING file that comes with this distribution.
*/
#ifndef __MOO_UTILS_WIN32_H__
#define __MOO_UTILS_WIN32_H__
#include <glib.h>
G_BEGIN_DECLS
typedef void (*MooEventQueueCallback) (GList *events,
gpointer data);
guint _moo_event_queue_connect (MooEventQueueCallback callback,
gpointer data,
GDestroyNotify notify);
void _moo_event_queue_disconnect (guint event_id);
void _moo_event_queue_do_events (guint event_id);
/* called from a thread */
void _moo_event_queue_push (guint event_id,
gpointer data,
GDestroyNotify data_destroy);
G_END_DECLS
#endif /* __MOO_UTILS_WIN32_H__ */