Implemented watching folders on win32
parent
87755fe791
commit
77713b92e0
|
@ -923,17 +923,18 @@ file_watch_event (G_GNUC_UNUSED MooFileWatch *watch,
|
||||||
|
|
||||||
switch (event->code)
|
switch (event->code)
|
||||||
{
|
{
|
||||||
case MOO_FILE_WATCH_CHANGED:
|
case MOO_FILE_WATCH_EVENT_CHANGED:
|
||||||
edit->priv->modified_on_disk = TRUE;
|
edit->priv->modified_on_disk = TRUE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MOO_FILE_WATCH_DELETED:
|
case MOO_FILE_WATCH_EVENT_DELETED:
|
||||||
edit->priv->deleted_from_disk = TRUE;
|
edit->priv->deleted_from_disk = TRUE;
|
||||||
edit->priv->file_monitor_id = 0;
|
edit->priv->file_monitor_id = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MOO_FILE_WATCH_CREATED:
|
case MOO_FILE_WATCH_EVENT_CREATED:
|
||||||
case MOO_FILE_WATCH_MOVED:
|
case MOO_FILE_WATCH_EVENT_MOVED:
|
||||||
|
case MOO_FILE_WATCH_EVENT_ERROR:
|
||||||
g_return_if_reached ();
|
g_return_if_reached ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -828,16 +828,20 @@ fam_event (MooFolder *folder,
|
||||||
|
|
||||||
switch (event->code)
|
switch (event->code)
|
||||||
{
|
{
|
||||||
case MOO_FILE_WATCH_CHANGED:
|
case MOO_FILE_WATCH_EVENT_CHANGED:
|
||||||
file_changed (folder, event->filename);
|
file_changed (folder, event->filename);
|
||||||
break;
|
break;
|
||||||
case MOO_FILE_WATCH_DELETED:
|
case MOO_FILE_WATCH_EVENT_DELETED:
|
||||||
file_deleted (folder, event->filename);
|
file_deleted (folder, event->filename);
|
||||||
break;
|
break;
|
||||||
case MOO_FILE_WATCH_CREATED:
|
case MOO_FILE_WATCH_EVENT_CREATED:
|
||||||
file_created (folder, event->filename);
|
file_created (folder, event->filename);
|
||||||
break;
|
break;
|
||||||
default:
|
case MOO_FILE_WATCH_EVENT_ERROR:
|
||||||
|
/* XXX */
|
||||||
|
file_deleted (folder, folder->priv->path);
|
||||||
|
break;
|
||||||
|
case MOO_FILE_WATCH_EVENT_MOVED:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -869,7 +873,7 @@ start_monitor (MooFolder *folder)
|
||||||
|
|
||||||
if (!folder->priv->fam_request)
|
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_warning ("%s: %s", G_STRLOC, error->message);
|
||||||
g_error_free (error);
|
g_error_free (error);
|
||||||
return;
|
return;
|
||||||
|
@ -1259,10 +1263,6 @@ _moo_file_unref (MooFile *file)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef __WIN32__
|
|
||||||
#define lstat stat
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
moo_file_stat_unix (MooFile *file,
|
moo_file_stat_unix (MooFile *file,
|
||||||
const char *dirname)
|
const char *dirname)
|
||||||
|
@ -1279,7 +1279,9 @@ moo_file_stat_unix (MooFile *file,
|
||||||
g_free (file->link_target);
|
g_free (file->link_target);
|
||||||
file->link_target = NULL;
|
file->link_target = NULL;
|
||||||
|
|
||||||
if (lstat (fullname, &file->statbuf) != 0)
|
errno = 0;
|
||||||
|
|
||||||
|
if (g_lstat (fullname, &file->statbuf) != 0)
|
||||||
{
|
{
|
||||||
if (errno == ENOENT)
|
if (errno == ENOENT)
|
||||||
{
|
{
|
||||||
|
@ -1310,8 +1312,9 @@ moo_file_stat_unix (MooFile *file,
|
||||||
gssize len;
|
gssize len;
|
||||||
|
|
||||||
file->info |= MOO_FILE_INFO_IS_LINK;
|
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)
|
if (errno == ENOENT)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1217,7 +1217,7 @@ init_actions (MooFileView *fileview)
|
||||||
"closure-callback", file_view_paste_clipboard,
|
"closure-callback", file_view_paste_clipboard,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
#ifdef __WIN32__
|
#if defined(__WIN32__) && 0
|
||||||
moo_action_group_add_action (group, "Reload",
|
moo_action_group_add_action (group, "Reload",
|
||||||
"label", _("Reload"),
|
"label", _("Reload"),
|
||||||
"tooltip", _("Reload"),
|
"tooltip", _("Reload"),
|
||||||
|
|
|
@ -22,6 +22,10 @@ gtk_2_4_sources = \
|
||||||
newgtk/gtkfontbutton.c \
|
newgtk/gtkfontbutton.c \
|
||||||
newgtk/gtkfontbutton.h
|
newgtk/gtkfontbutton.h
|
||||||
|
|
||||||
|
win32_sources = \
|
||||||
|
mooutils-thread.c \
|
||||||
|
mooutils-thread.h
|
||||||
|
|
||||||
mooutils_include_headers = \
|
mooutils_include_headers = \
|
||||||
mooaction.h \
|
mooaction.h \
|
||||||
mooactionbase.h \
|
mooactionbase.h \
|
||||||
|
@ -132,6 +136,9 @@ endif
|
||||||
if !GTK_2_4
|
if !GTK_2_4
|
||||||
mooutils_sources += $(moonewgtk_gtk_2_4_sources)
|
mooutils_sources += $(moonewgtk_gtk_2_4_sources)
|
||||||
endif
|
endif
|
||||||
|
if MOO_OS_MINGW
|
||||||
|
mooutils_sources += $(win32_sources)
|
||||||
|
endif
|
||||||
|
|
||||||
nodist_mooutils_sources = \
|
nodist_mooutils_sources = \
|
||||||
moomarshals.c \
|
moomarshals.c \
|
||||||
|
@ -146,6 +153,7 @@ EXTRA_DIST = \
|
||||||
xml2h.sh \
|
xml2h.sh \
|
||||||
$(gmappedfile_sources) \
|
$(gmappedfile_sources) \
|
||||||
$(gtk_2_4_sources) \
|
$(gtk_2_4_sources) \
|
||||||
|
$(win32_sources) \
|
||||||
glade/accelbutton.glade \
|
glade/accelbutton.glade \
|
||||||
glade/accelprefs.glade \
|
glade/accelprefs.glade \
|
||||||
glade/moologwindow.glade \
|
glade/moologwindow.glade \
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __WIN32__
|
#ifdef __WIN32__
|
||||||
|
#include "mooutils/mooutils-thread.h"
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <glib/gstdio.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
@ -40,7 +42,7 @@
|
||||||
#include "mooutils/moocompat.h"
|
#include "mooutils/moocompat.h"
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
#if defined(__WIN32__) && 0
|
||||||
#define DEBUG_PRINT g_message
|
#define DEBUG_PRINT g_message
|
||||||
#else
|
#else
|
||||||
static void DEBUG_PRINT (G_GNUC_UNUSED const char *format, ...)
|
static void DEBUG_PRINT (G_GNUC_UNUSED const char *format, ...)
|
||||||
|
@ -53,7 +55,8 @@ static const char *event_code_strings[] = {
|
||||||
"CHANGED",
|
"CHANGED",
|
||||||
"DELETED",
|
"DELETED",
|
||||||
"CREATED",
|
"CREATED",
|
||||||
"MOVED"
|
"MOVED",
|
||||||
|
"ERROR"
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _Monitor Monitor;
|
typedef struct _Monitor Monitor;
|
||||||
|
@ -87,7 +90,8 @@ typedef struct {
|
||||||
GError **error);
|
GError **error);
|
||||||
void (*suspend) (Monitor *monitor);
|
void (*suspend) (Monitor *monitor);
|
||||||
void (*resume) (Monitor *monitor);
|
void (*resume) (Monitor *monitor);
|
||||||
void (*delete) (Monitor *monitor);
|
void (*delete) (MooFileWatch *watch,
|
||||||
|
Monitor *monitor);
|
||||||
} WatchFuncs;
|
} WatchFuncs;
|
||||||
|
|
||||||
|
|
||||||
|
@ -96,6 +100,9 @@ struct _MooFileWatchPrivate {
|
||||||
#ifdef MOO_USE_FAM
|
#ifdef MOO_USE_FAM
|
||||||
FAMConnection fam_connection;
|
FAMConnection fam_connection;
|
||||||
guint fam_connection_watch;
|
guint fam_connection_watch;
|
||||||
|
#endif
|
||||||
|
#ifdef __WIN32__
|
||||||
|
guint id;
|
||||||
#endif
|
#endif
|
||||||
WatchFuncs funcs;
|
WatchFuncs funcs;
|
||||||
MooFileWatchMethod method;
|
MooFileWatchMethod method;
|
||||||
|
@ -121,7 +128,8 @@ static Monitor *monitor_fam_create (MooFileWatch *watch,
|
||||||
GError **error);
|
GError **error);
|
||||||
static void monitor_fam_suspend (Monitor *monitor);
|
static void monitor_fam_suspend (Monitor *monitor);
|
||||||
static void monitor_fam_resume (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 */
|
#endif /* MOO_USE_FAM */
|
||||||
|
|
||||||
static gboolean watch_stat_start (MooFileWatch *watch,
|
static gboolean watch_stat_start (MooFileWatch *watch,
|
||||||
|
@ -136,7 +144,8 @@ static Monitor *monitor_stat_create (MooFileWatch *watch,
|
||||||
GError **error);
|
GError **error);
|
||||||
static void monitor_stat_suspend (Monitor *monitor);
|
static void monitor_stat_suspend (Monitor *monitor);
|
||||||
static void monitor_stat_resume (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__
|
#ifdef __WIN32__
|
||||||
static gboolean watch_win32_start (MooFileWatch *watch,
|
static gboolean watch_win32_start (MooFileWatch *watch,
|
||||||
|
@ -151,7 +160,8 @@ static Monitor *monitor_win32_create (MooFileWatch *watch,
|
||||||
GError **error);
|
GError **error);
|
||||||
static void monitor_win32_suspend (Monitor *monitor);
|
static void monitor_win32_suspend (Monitor *monitor);
|
||||||
static void monitor_win32_resume (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__ */
|
#endif /* __WIN32__ */
|
||||||
|
|
||||||
|
|
||||||
|
@ -339,10 +349,11 @@ moo_file_watch_event_code_get_type (void)
|
||||||
if (!type)
|
if (!type)
|
||||||
{
|
{
|
||||||
static const GEnumValue values[] = {
|
static const GEnumValue values[] = {
|
||||||
{MOO_FILE_WATCH_CHANGED, (char*) "MOO_FILE_WATCH_CHANGED", (char*) "changed"},
|
{ MOO_FILE_WATCH_EVENT_CHANGED, (char*) "MOO_FILE_WATCH_EVENT_CHANGED", (char*) "changed"},
|
||||||
{MOO_FILE_WATCH_DELETED, (char*) "MOO_FILE_WATCH_DELETED", (char*) "deleted"},
|
{ MOO_FILE_WATCH_EVENT_DELETED, (char*) "MOO_FILE_WATCH_EVENT_DELETED", (char*) "deleted"},
|
||||||
{MOO_FILE_WATCH_CREATED, (char*) "MOO_FILE_WATCH_CREATED", (char*) "created"},
|
{ MOO_FILE_WATCH_EVENT_CREATED, (char*) "MOO_FILE_WATCH_EVENT_CREATED", (char*) "created"},
|
||||||
{MOO_FILE_WATCH_MOVED, (char*) "MOO_FILE_WATCH_MOVED", (char*) "moved"},
|
{ 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 }
|
{ 0, NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -450,7 +461,7 @@ moo_file_watch_cancel_monitor (MooFileWatch *watch,
|
||||||
GINT_TO_POINTER (monitor_id));
|
GINT_TO_POINTER (monitor_id));
|
||||||
g_return_if_fail (monitor != NULL);
|
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);
|
watch->priv->monitors = g_slist_remove (watch->priv->monitors, monitor);
|
||||||
g_hash_table_remove (watch->priv->requests,
|
g_hash_table_remove (watch->priv->requests,
|
||||||
GINT_TO_POINTER (monitor_id));
|
GINT_TO_POINTER (monitor_id));
|
||||||
|
@ -464,7 +475,7 @@ emit_event (MooFileWatch *watch,
|
||||||
DEBUG_PRINT ("watch %p, monitor %d: event %s for %s",
|
DEBUG_PRINT ("watch %p, monitor %d: event %s for %s",
|
||||||
watch, event->monitor_id,
|
watch, event->monitor_id,
|
||||||
event_code_strings[event->code],
|
event_code_strings[event->code],
|
||||||
event->filename);
|
event->filename ? event->filename : "<NULL>");
|
||||||
g_signal_emit (watch, signals[EVENT], 0, event);
|
g_signal_emit (watch, signals[EVENT], 0, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,10 +509,12 @@ moo_file_watch_close (MooFileWatch *watch,
|
||||||
|
|
||||||
watch->priv->alive = FALSE;
|
watch->priv->alive = FALSE;
|
||||||
|
|
||||||
g_slist_foreach (watch->priv->monitors,
|
while (watch->priv->monitors)
|
||||||
(GFunc) watch->priv->funcs.delete, NULL);
|
{
|
||||||
g_slist_free (watch->priv->monitors);
|
watch->priv->funcs.delete (watch, watch->priv->monitors->data);
|
||||||
watch->priv->monitors = NULL;
|
watch->priv->monitors =
|
||||||
|
g_slist_delete_link (watch->priv->monitors, watch->priv->monitors);
|
||||||
|
}
|
||||||
|
|
||||||
return watch->priv->funcs.shutdown (watch, error);
|
return watch->priv->funcs.shutdown (watch, error);
|
||||||
}
|
}
|
||||||
|
@ -697,7 +710,8 @@ monitor_fam_resume (Monitor *monitor)
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
monitor_fam_delete (Monitor *monitor)
|
monitor_fam_delete (G_GNUC_UNUSED MooFileWatch *watch,
|
||||||
|
Monitor *monitor)
|
||||||
{
|
{
|
||||||
FAMRequest fr;
|
FAMRequest fr;
|
||||||
int result;
|
int result;
|
||||||
|
@ -793,20 +807,21 @@ read_fam_events (G_GNUC_UNUSED GIOChannel *source,
|
||||||
event.monitor_id = fe.fr.reqnum;
|
event.monitor_id = fe.fr.reqnum;
|
||||||
event.filename = fe.filename;
|
event.filename = fe.filename;
|
||||||
event.data = fe.userdata;
|
event.data = fe.userdata;
|
||||||
|
event.error = NULL;
|
||||||
|
|
||||||
switch (fe.code)
|
switch (fe.code)
|
||||||
{
|
{
|
||||||
case FAMChanged:
|
case FAMChanged:
|
||||||
event.code = MOO_FILE_WATCH_CHANGED;
|
event.code = MOO_FILE_WATCH_EVENT_CHANGED;
|
||||||
break;
|
break;
|
||||||
case FAMDeleted:
|
case FAMDeleted:
|
||||||
event.code = MOO_FILE_WATCH_DELETED;
|
event.code = MOO_FILE_WATCH_EVENT_DELETED;
|
||||||
break;
|
break;
|
||||||
case FAMCreated:
|
case FAMCreated:
|
||||||
event.code = MOO_FILE_WATCH_CREATED;
|
event.code = MOO_FILE_WATCH_EVENT_CREATED;
|
||||||
break;
|
break;
|
||||||
case FAMMoved:
|
case FAMMoved:
|
||||||
event.code = MOO_FILE_WATCH_MOVED;
|
event.code = MOO_FILE_WATCH_EVENT_MOVED;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FAMStartExecuting:
|
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 (MOO_IS_FILE_WATCH (watch), NULL);
|
||||||
g_return_val_if_fail (filename != NULL, 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;
|
int saved_errno = errno;
|
||||||
g_set_error (error, MOO_FILE_WATCH_ERROR,
|
g_set_error (error, MOO_FILE_WATCH_ERROR,
|
||||||
|
@ -950,7 +967,8 @@ monitor_stat_resume (Monitor *monitor)
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
monitor_stat_delete (Monitor *monitor)
|
monitor_stat_delete (G_GNUC_UNUSED MooFileWatch *watch,
|
||||||
|
Monitor *monitor)
|
||||||
{
|
{
|
||||||
g_return_if_fail (monitor != NULL);
|
g_return_if_fail (monitor != NULL);
|
||||||
DEBUG_PRINT ("removing monitor for '%s'", monitor->filename);
|
DEBUG_PRINT ("removing monitor for '%s'", monitor->filename);
|
||||||
|
@ -991,9 +1009,11 @@ do_stat (MooFileWatch *watch)
|
||||||
|
|
||||||
old = monitor->statbuf.st_mtime;
|
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.monitor_id = monitor->request;
|
||||||
event.filename = monitor->filename;
|
event.filename = monitor->filename;
|
||||||
event.data = monitor->user_data;
|
event.data = monitor->user_data;
|
||||||
|
@ -1007,7 +1027,7 @@ do_stat (MooFileWatch *watch)
|
||||||
}
|
}
|
||||||
else if (monitor->statbuf.st_mtime > old)
|
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.monitor_id = monitor->request;
|
||||||
event.filename = monitor->filename;
|
event.filename = monitor->filename;
|
||||||
event.data = monitor->user_data;
|
event.data = monitor->user_data;
|
||||||
|
@ -1062,11 +1082,473 @@ errno_to_file_error (int code)
|
||||||
*/
|
*/
|
||||||
#ifdef __WIN32__
|
#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
|
static gboolean
|
||||||
watch_win32_start (MooFileWatch *watch,
|
watch_win32_start (MooFileWatch *watch,
|
||||||
GError **error)
|
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,
|
int *request,
|
||||||
GError **error)
|
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);
|
g_return_val_if_fail (filename != NULL, FALSE);
|
||||||
|
|
||||||
if (type == MONITOR_FILE)
|
if (type == MONITOR_FILE)
|
||||||
return monitor_stat_create (watch, type, filename, data, request, error);
|
return monitor_stat_create (watch, type, filename, data, request, error);
|
||||||
|
|
||||||
#ifdef __GNUC__
|
errno = 0;
|
||||||
#warning "Implement monitor_win32_create()"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
g_set_error (error, MOO_FILE_WATCH_ERROR,
|
if (g_stat (filename, &buf) != 0)
|
||||||
MOO_FILE_WATCH_ERROR_NOT_IMPLEMENTED,
|
{
|
||||||
"watching folders is not implemented on win32");
|
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;
|
return;
|
||||||
|
|
||||||
if (monitor->type == MONITOR_FILE)
|
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;
|
return;
|
||||||
|
|
||||||
if (monitor->type == MONITOR_FILE)
|
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
|
static void
|
||||||
monitor_win32_delete (Monitor *monitor)
|
monitor_win32_delete (MooFileWatch *watch,
|
||||||
|
Monitor *monitor)
|
||||||
{
|
{
|
||||||
g_return_if_fail (monitor != NULL);
|
g_return_if_fail (monitor != NULL);
|
||||||
|
|
||||||
if (monitor->type == MONITOR_FILE)
|
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__ */
|
#endif /* __WIN32__ */
|
||||||
|
|
|
@ -59,10 +59,11 @@ typedef enum {
|
||||||
|
|
||||||
/* Stripped FAMEventCode enumeration */
|
/* Stripped FAMEventCode enumeration */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
MOO_FILE_WATCH_CHANGED = 1,
|
MOO_FILE_WATCH_EVENT_CHANGED = 1,
|
||||||
MOO_FILE_WATCH_DELETED = 2,
|
MOO_FILE_WATCH_EVENT_DELETED = 2,
|
||||||
MOO_FILE_WATCH_CREATED = 3,
|
MOO_FILE_WATCH_EVENT_CREATED = 3,
|
||||||
MOO_FILE_WATCH_MOVED = 4
|
MOO_FILE_WATCH_EVENT_MOVED = 4,
|
||||||
|
MOO_FILE_WATCH_EVENT_ERROR = 5
|
||||||
} MooFileWatchEventCode;
|
} MooFileWatchEventCode;
|
||||||
|
|
||||||
/* The structure has the same meaning as the FAMEvent
|
/* The structure has the same meaning as the FAMEvent
|
||||||
|
@ -76,6 +77,7 @@ struct _MooFileWatchEvent {
|
||||||
MooFileWatchEventCode code; /* FAMEventCode */
|
MooFileWatchEventCode code; /* FAMEventCode */
|
||||||
int monitor_id; /* FAMRequest */
|
int monitor_id; /* FAMRequest */
|
||||||
char *filename;
|
char *filename;
|
||||||
|
GError *error;
|
||||||
gpointer data;
|
gpointer data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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__ */
|
Loading…
Reference in New Issue