From a2f8b6a5261e9a2ca47d773130d738cb7f1eea51 Mon Sep 17 00:00:00 2001 From: Vincent Torri Date: Wed, 11 Nov 2009 17:45:55 +0000 Subject: [PATCH] Add Windows directory monitoring to ecore_file. It was tough. A lot of thanks to Lars Munch for his great help SVN revision: 43617 --- legacy/ecore/configure.ac | 5 + legacy/ecore/m4/ecore_check_options.m4 | 31 ++ legacy/ecore/src/lib/ecore_file/Makefile.am | 1 + .../src/lib/ecore_file/ecore_file_monitor.c | 31 +- .../lib/ecore_file/ecore_file_monitor_win32.c | 310 ++++++++++++++++++ .../src/lib/ecore_file/ecore_file_private.h | 12 + 6 files changed, 386 insertions(+), 4 deletions(-) create mode 100644 legacy/ecore/src/lib/ecore_file/ecore_file_monitor_win32.c diff --git a/legacy/ecore/configure.ac b/legacy/ecore/configure.ac index b8ce039ec0..36cc6d842f 100644 --- a/legacy/ecore/configure.ac +++ b/legacy/ecore/configure.ac @@ -86,6 +86,7 @@ want_cipher="no" want_signature="no" want_poll="yes" want_inotify="no" +want_notify_win32="no" want_tslib="no" want_glib="no" @@ -136,6 +137,7 @@ case "$host_os" in want_ecore_evas_software_16_wince="yes" ;; mingw*) + want_notify_win32="yes" want_curl="yes" want_glib="auto" want_ecore_imf="yes" @@ -837,9 +839,11 @@ ECORE_CHECK_MODULE([File], [${want_ecore_file}]) have_poll="no" have_inotify="no" +have_notify_win32="no" if test "x${have_ecore_file}" = "xyes" ; then ECORE_CHECK_POLL([${want_poll}], [have_poll="yes"], [have_poll="no"]) ECORE_CHECK_INOTIFY([${want_inotify}], [have_inotify="yes"], [have_inotify="no"]) + ECORE_CHECK_NOTIFY_WIN32([${want_notify_win32}], [have_notify_win32="yes"], [have_notify_win32="no"]) if test "x${have_ecore_con}" = "xyes" ; then requirements_ecore_file="ecore-con ${requirements_ecore_file}" @@ -1205,6 +1209,7 @@ fi echo " Ecore_File...................: $have_ecore_file" if test "x$have_ecore_file" = "xyes" ; then echo " Inotify....................: $have_inotify" + echo " Windows notification.......: $have_notify_win32" echo " Poll.......................: $have_poll" echo " CURL.......................: $have_curl" fi diff --git a/legacy/ecore/m4/ecore_check_options.m4 b/legacy/ecore/m4/ecore_check_options.m4 index 32908a74ad..d39d6f7e6b 100644 --- a/legacy/ecore/m4/ecore_check_options.m4 +++ b/legacy/ecore/m4/ecore_check_options.m4 @@ -84,6 +84,37 @@ else m4_default([$3], [:]) fi ]) + +dnl use: ECORE_CHECK_NOTIFY_WIN32(default-enabled[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +AC_DEFUN([ECORE_CHECK_NOTIFY_WIN32], +[ +_ecore_want_notify_win32=$1 +_ecore_have_notify_win32="no" + +AC_ARG_ENABLE(notify-win32, + [AC_HELP_STRING([--disable-notify-win32], [disable Windows notification in the ecore_file module])], + [ + if test "x${enableval}" = "xyes" ; then + _ecore_want_notify_win32="yes" + else + _ecore_want_notify_win32="no" + fi + ]) + +AC_MSG_CHECKING(whether Windows notification is to be used for filemonitoring) +AC_MSG_RESULT(${_ecore_want_notify_win32}) + +if test "x${_ecore_want_notify_win32}" = "xyes" ; then + AC_DEFINE([HAVE_NOTIFY_WIN32], [1], [ File monitoring with Windows notification ]) + _ecore_have_notify_win32="yes" +fi + +if test "x${_ecore_have_notify_win32}" = "xyes" ; then + m4_default([$2], [:]) +else + m4_default([$3], [:]) +fi +]) dnl use: ECORE_CHECK_CURL(default-enabled[, ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) AC_DEFUN([ECORE_CHECK_CURL], diff --git a/legacy/ecore/src/lib/ecore_file/Makefile.am b/legacy/ecore/src/lib/ecore_file/Makefile.am index dc544685df..fff3591dda 100644 --- a/legacy/ecore/src/lib/ecore_file/Makefile.am +++ b/legacy/ecore/src/lib/ecore_file/Makefile.am @@ -25,6 +25,7 @@ libecore_file_la_SOURCES = \ ecore_file.c \ ecore_file_monitor.c \ ecore_file_monitor_inotify.c \ +ecore_file_monitor_win32.c \ ecore_file_monitor_poll.c \ ecore_file_path.c \ ecore_file_download.c diff --git a/legacy/ecore/src/lib/ecore_file/ecore_file_monitor.c b/legacy/ecore/src/lib/ecore_file/ecore_file_monitor.c index 3eec572abe..72a2496130 100644 --- a/legacy/ecore/src/lib/ecore_file/ecore_file_monitor.c +++ b/legacy/ecore/src/lib/ecore_file/ecore_file_monitor.c @@ -13,6 +13,9 @@ typedef enum { #ifdef HAVE_INOTIFY ECORE_FILE_MONITOR_TYPE_INOTIFY, #endif +#ifdef HAVE_NOTIFY_WIN32 + ECORE_FILE_MONITOR_TYPE_NOTIFY_WIN32, +#endif #ifdef HAVE_POLL ECORE_FILE_MONITOR_TYPE_POLL #endif @@ -28,6 +31,11 @@ ecore_file_monitor_init(void) if (ecore_file_monitor_inotify_init()) return 1; #endif +#ifdef HAVE_NOTIFY_WIN32 + monitor_type = ECORE_FILE_MONITOR_TYPE_NOTIFY_WIN32; + if (ecore_file_monitor_win32_init()) + return 1; +#endif #ifdef HAVE_POLL monitor_type = ECORE_FILE_MONITOR_TYPE_POLL; if (ecore_file_monitor_poll_init()) @@ -49,6 +57,11 @@ ecore_file_monitor_shutdown(void) ecore_file_monitor_inotify_shutdown(); break; #endif +#ifdef HAVE_NOTIFY_WIN32 + case ECORE_FILE_MONITOR_TYPE_NOTIFY_WIN32: + ecore_file_monitor_win32_shutdown(); + break; +#endif #ifdef HAVE_POLL case ECORE_FILE_MONITOR_TYPE_POLL: ecore_file_monitor_poll_shutdown(); @@ -66,10 +79,11 @@ ecore_file_monitor_shutdown(void) */ EAPI Ecore_File_Monitor * ecore_file_monitor_add(const char *path, - void (*func) (void *data, Ecore_File_Monitor *em, - Ecore_File_Event event, - const char *path), - void *data) + void (*func) (void *data, + Ecore_File_Monitor *em, + Ecore_File_Event event, + const char *path), + void *data) { switch (monitor_type) { @@ -79,6 +93,10 @@ ecore_file_monitor_add(const char *path, case ECORE_FILE_MONITOR_TYPE_INOTIFY: return ecore_file_monitor_inotify_add(path, func, data); #endif +#ifdef HAVE_NOTIFY_WIN32 + case ECORE_FILE_MONITOR_TYPE_NOTIFY_WIN32: + return ecore_file_monitor_win32_add(path, func, data); +#endif #ifdef HAVE_POLL case ECORE_FILE_MONITOR_TYPE_POLL: return ecore_file_monitor_poll_add(path, func, data); @@ -103,6 +121,11 @@ ecore_file_monitor_del(Ecore_File_Monitor *em) ecore_file_monitor_inotify_del(em); break; #endif +#ifdef HAVE_NOTIFY_WIN32 + case ECORE_FILE_MONITOR_TYPE_NOTIFY_WIN32: + ecore_file_monitor_win32_del(em); + break; +#endif #ifdef HAVE_POLL case ECORE_FILE_MONITOR_TYPE_POLL: ecore_file_monitor_poll_del(em); diff --git a/legacy/ecore/src/lib/ecore_file/ecore_file_monitor_win32.c b/legacy/ecore/src/lib/ecore_file/ecore_file_monitor_win32.c new file mode 100644 index 0000000000..b63d674ce4 --- /dev/null +++ b/legacy/ecore/src/lib/ecore_file/ecore_file_monitor_win32.c @@ -0,0 +1,310 @@ +/* + * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2 + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_NOTIFY_WIN32 + +# define WIN32_LEAN_AND_MEAN +# include +# undef WIN32_LEAN_AND_MEAN +# include + +# include "ecore_file_private.h" + + +typedef struct _Ecore_File_Monitor_Win32 Ecore_File_Monitor_Win32; +typedef struct _Ecore_File_Monitor_Win32_Data Ecore_File_Monitor_Win32_Data; + +/* 4096 = 256 * sizeof(FILE_NOTIFY_INFORMATION) */ +# define ECORE_FILE_MONITOR_WIN32_BUFFER_SIZE 4096 +# define ECORE_FILE_MONITOR_WIN32(x) ((Ecore_File_Monitor_Win32 *)(x)) + +struct _Ecore_File_Monitor_Win32_Data +{ + char buffer[ECORE_FILE_MONITOR_WIN32_BUFFER_SIZE]; + OVERLAPPED overlapped; + HANDLE handle; + HANDLE event; + Ecore_File_Monitor *monitor; + Ecore_Win32_Handler *h; + DWORD buf_length; + int is_dir; +}; + +struct _Ecore_File_Monitor_Win32 +{ + Ecore_File_Monitor monitor; + Ecore_File_Monitor_Win32_Data *file; + Ecore_File_Monitor_Win32_Data *dir; +}; + +static Ecore_File_Monitor *_monitors = NULL; + +static int _ecore_file_monitor_win32_cb(void *data, Ecore_Win32_Handler *wh); + + +static Ecore_File_Monitor_Win32_Data * +_ecore_file_monitor_win32_data_new(Ecore_File_Monitor *monitor, int type) +{ + Ecore_File_Monitor_Win32_Data *md; + DWORD filter; + + md = (Ecore_File_Monitor_Win32_Data *)calloc(1, sizeof(Ecore_File_Monitor_Win32_Data)); + if (!md) return NULL; + + md->handle = CreateFile(monitor->path, + FILE_LIST_DIRECTORY, + FILE_SHARE_READ | + FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | + FILE_FLAG_OVERLAPPED, + NULL); + if (md->handle == INVALID_HANDLE_VALUE) + goto free_md; + + md->event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!md->event) + goto close_handle; + + ZeroMemory (&md->overlapped, sizeof(md->overlapped)); + md->overlapped.hEvent = md->event; + + filter = (type == 0) ? FILE_NOTIFY_CHANGE_FILE_NAME : FILE_NOTIFY_CHANGE_DIR_NAME; + filter |= + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE | + FILE_NOTIFY_CHANGE_LAST_ACCESS | + FILE_NOTIFY_CHANGE_CREATION | + FILE_NOTIFY_CHANGE_SECURITY; + + if (!ReadDirectoryChangesW(md->handle, + md->buffer, + ECORE_FILE_MONITOR_WIN32_BUFFER_SIZE, + FALSE, + filter, + &md->buf_length, + &md->overlapped, + NULL)) + goto close_event; + + md->h = ecore_main_win32_handler_add(md->event, + _ecore_file_monitor_win32_cb, + md); + if (!md->h) + goto close_event; + + md->monitor = monitor; + md->is_dir = type; + + return md; + + close_event: + CloseHandle(md->event); + close_handle: + CloseHandle(md->handle); + free_md: + free(md); + + return NULL; +} + +static void +_ecore_file_monitor_win32_data_free(Ecore_File_Monitor_Win32_Data *md) +{ + if (!md) return; + + CloseHandle(md->event); + CloseHandle (md->handle); + free (md); +} + +static int +_ecore_file_monitor_win32_cb(void *data, Ecore_Win32_Handler *wh) +{ + char filename[PATH_MAX]; + PFILE_NOTIFY_INFORMATION fni; + Ecore_File_Monitor_Win32_Data *md; + wchar_t *wname; + char *name; + DWORD filter; + DWORD offset; + DWORD buf_length; + Ecore_File_Event event = ECORE_FILE_EVENT_NONE; + + md = (Ecore_File_Monitor_Win32_Data *)data; + + if (!GetOverlappedResult (md->handle, &md->overlapped, &buf_length, TRUE)) + return 1; + + fni = (PFILE_NOTIFY_INFORMATION)md->buffer; + do { + if (!fni) + break; + offset = fni->NextEntryOffset; + + wname = (wchar_t *)malloc(sizeof(wchar_t) * (fni->FileNameLength + 1)); + if (!wname) + return 0; + + memcpy(wname, fni->FileName, fni->FileNameLength); + wname[fni->FileNameLength]='\0'; + name = evil_wchar_to_char(wname); + free(wname); + if (!name) + return 0; + + _snprintf(filename, PATH_MAX, "%s\\%s", md->monitor->path, name); + free(name); + + switch (fni->Action) + { + case FILE_ACTION_ADDED: + if (md->is_dir) + event = ECORE_FILE_EVENT_CREATED_DIRECTORY; + else + event = ECORE_FILE_EVENT_CREATED_FILE; + break; + case FILE_ACTION_REMOVED: + if (md->is_dir) + event = ECORE_FILE_EVENT_DELETED_DIRECTORY; + else + event = ECORE_FILE_EVENT_DELETED_FILE; + break; + case FILE_ACTION_MODIFIED: + if (!md->is_dir) + event = ECORE_FILE_EVENT_MODIFIED; + break; + case FILE_ACTION_RENAMED_OLD_NAME: + if (md->is_dir) + event = ECORE_FILE_EVENT_DELETED_DIRECTORY; + else + event = ECORE_FILE_EVENT_DELETED_FILE; + break; + case FILE_ACTION_RENAMED_NEW_NAME: + if (md->is_dir) + event = ECORE_FILE_EVENT_CREATED_DIRECTORY; + else + event = ECORE_FILE_EVENT_CREATED_FILE; + break; + default: + fprintf(stderr, "unknown event\n"); + event = ECORE_FILE_EVENT_NONE; + break; + } + if (event != ECORE_FILE_EVENT_NONE) + md->monitor->func(md->monitor->data, md->monitor, event, filename); + + fni = (PFILE_NOTIFY_INFORMATION)((LPBYTE)fni + offset); + } while (offset); + + filter = (md->is_dir == 0) ? FILE_NOTIFY_CHANGE_FILE_NAME : FILE_NOTIFY_CHANGE_DIR_NAME; + filter |= + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE | + FILE_NOTIFY_CHANGE_LAST_ACCESS | + FILE_NOTIFY_CHANGE_CREATION | + FILE_NOTIFY_CHANGE_SECURITY; + + ReadDirectoryChangesW(md->handle, + md->buffer, + ECORE_FILE_MONITOR_WIN32_BUFFER_SIZE, + FALSE, + filter, + &md->buf_length, + &md->overlapped, + NULL); + return 0; +} + +int +ecore_file_monitor_win32_init(void) +{ + return 1; +} + +int +ecore_file_monitor_win32_shutdown(void) +{ + return 1; +} + +Ecore_File_Monitor * +ecore_file_monitor_win32_add(const char *path, + void (*func) (void *data, Ecore_File_Monitor *em, + Ecore_File_Event event, + const char *path), + void *data) +{ + Ecore_File_Monitor_Win32 *m; + Ecore_File_Monitor *em; + size_t len; + + if (!path || (*path == '\0')) return NULL; + if (!ecore_file_exists(path) || !ecore_file_is_dir(path)) + return NULL; + if (!func) return NULL; + + em = (Ecore_File_Monitor *)calloc(1, sizeof(Ecore_File_Monitor_Win32)); + if (!em) return NULL; + + em->func = func; + em->data = data; + + em->path = strdup(path); + if (!em->path) + { + free(em); + return NULL; + } + len = strlen(em->path); + if (em->path[len - 1] == '/' || em->path[len - 1] == '\\') + em->path[len - 1] = '\0'; + + m = ECORE_FILE_MONITOR_WIN32(em); + + m->file = _ecore_file_monitor_win32_data_new(em, 0); + if (!m->file) + { + free(em->path); + free(em); + return NULL; + } + + m->dir = _ecore_file_monitor_win32_data_new(em, 1); + if (!m->dir) + { + _ecore_file_monitor_win32_data_free(m->file); + free(em->path); + free(em); + return NULL; + } + + _monitors = ECORE_FILE_MONITOR(eina_inlist_append(EINA_INLIST_GET(_monitors), EINA_INLIST_GET(em))); + + return em; +} + +void +ecore_file_monitor_win32_del(Ecore_File_Monitor *em) +{ + Ecore_File_Monitor_Win32 *m; + + if (!em) + return; + + m = ECORE_FILE_MONITOR_WIN32(em); + _ecore_file_monitor_win32_data_free(m->dir); + _ecore_file_monitor_win32_data_free(m->file); + free(em->path); + free(em); +} + +#endif diff --git a/legacy/ecore/src/lib/ecore_file/ecore_file_private.h b/legacy/ecore/src/lib/ecore_file/ecore_file_private.h index 6b781a2eb1..5fe5a2e453 100644 --- a/legacy/ecore/src/lib/ecore_file/ecore_file_private.h +++ b/legacy/ecore/src/lib/ecore_file/ecore_file_private.h @@ -57,6 +57,18 @@ Ecore_File_Monitor *ecore_file_monitor_inotify_add(const char *path, void *data); void ecore_file_monitor_inotify_del(Ecore_File_Monitor *ecore_file_monitor); #endif + +#ifdef HAVE_NOTIFY_WIN32 +int ecore_file_monitor_win32_init(void); +int ecore_file_monitor_win32_shutdown(void); +Ecore_File_Monitor *ecore_file_monitor_win32_add(const char *path, + void (*func) (void *data, + Ecore_File_Monitor *ecore_file_monitor, + Ecore_File_Event event, + const char *path), + void *data); +void ecore_file_monitor_win32_del(Ecore_File_Monitor *ecore_file_monitor); +#endif #ifdef HAVE_POLL int ecore_file_monitor_poll_init(void);