Eio: add file monitoring on Windows (might contain bugs, though)
authorVincent Torri <vincent.torri@gmail.com>
Mon, 2 Apr 2012 16:46:16 +0000 (16:46 +0000)
committerVincent Torri <vincent.torri@gmail.com>
Mon, 2 Apr 2012 16:46:16 +0000 (16:46 +0000)
SVN revision: 69867

legacy/eio/src/lib/eio_monitor_win32.c

index 30d86b6..d2949f3 100644 (file)
  * @cond LOCAL
  */
 
+typedef struct _Eio_Monitor_Win32_Watcher Eio_Monitor_Win32_Watcher;
+
+/* 4096 = 256 * sizeof(FILE_NOTIFY_INFORMATION) */
+# define EIO_MONITOR_WIN32_BUFFER_SIZE 4096
+
+struct _Eio_Monitor_Win32_Watcher
+{
+   char                 buffer[EIO_MONITOR_WIN32_BUFFER_SIZE];
+   OVERLAPPED           overlapped;
+   HANDLE               handle;
+   HANDLE               event;
+   Eio_Monitor         *monitor;
+   Ecore_Win32_Handler *h;
+   DWORD                buf_length;
+   int                  is_dir;
+};
+
 struct _Eio_Monitor_Backend
 {
+   Eio_Monitor               *parent;
+
+   Eio_Monitor_Win32_Watcher *file;
+   Eio_Monitor_Win32_Watcher *dir;
 };
 
+static Eina_Bool
+_eio_monitor_win32_cb(void *data, Ecore_Win32_Handler *wh __UNUSED__)
+{
+   char                       filename[PATH_MAX];
+   PFILE_NOTIFY_INFORMATION   fni;
+   Eio_Monitor_Win32_Watcher *w;
+   wchar_t                   *wname;
+   char                      *name;
+   DWORD                      filter;
+   DWORD                      offset;
+   DWORD                      buf_length;
+   int                        event = EIO_MONITOR_ERROR;
+
+   w = (Eio_Monitor_Win32_Watcher *)data;
+
+   if (!GetOverlappedResult(w->handle, &w->overlapped, &buf_length, TRUE))
+     return ECORE_CALLBACK_RENEW;
+
+   fni = (PFILE_NOTIFY_INFORMATION)w->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 ECORE_CALLBACK_CANCEL;
+
+      _snprintf(filename, PATH_MAX, "%s\\%s", w->monitor->path, name);
+      free(name);
+
+      switch (fni->Action)
+        {
+        case FILE_ACTION_ADDED:
+          if (w->is_dir)
+            event = EIO_MONITOR_DIRECTORY_CREATED;
+          else
+            event = EIO_MONITOR_FILE_CREATED;
+          break;
+        case FILE_ACTION_REMOVED:
+          if (w->is_dir)
+            event = EIO_MONITOR_DIRECTORY_DELETED;
+          else
+            event = EIO_MONITOR_FILE_DELETED;
+          break;
+        case FILE_ACTION_MODIFIED:
+          if (!w->is_dir)
+            event = EIO_MONITOR_FILE_MODIFIED;
+          break;
+        case FILE_ACTION_RENAMED_OLD_NAME:
+          if (w->is_dir)
+            event = EIO_MONITOR_DIRECTORY_DELETED;
+          else
+            event = EIO_MONITOR_FILE_DELETED;
+          break;
+        case FILE_ACTION_RENAMED_NEW_NAME:
+          if (w->is_dir)
+            event = EIO_MONITOR_DIRECTORY_CREATED;
+          else
+            event = EIO_MONITOR_FILE_CREATED;
+          break;
+        default:
+          fprintf(stderr, "unknown event\n");
+          event = EIO_MONITOR_ERROR;
+          break;
+        }
+      if (event != EIO_MONITOR_ERROR)
+        _eio_monitor_send(w->monitor, filename, event);
+
+      fni = (PFILE_NOTIFY_INFORMATION)((LPBYTE)fni + offset);
+   } while (offset);
+
+   filter = (w->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(w->handle,
+                          w->buffer,
+                          EIO_MONITOR_WIN32_BUFFER_SIZE,
+                          FALSE,
+                          filter,
+                          &w->buf_length,
+                          &w->overlapped,
+                          NULL);
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+static Eio_Monitor_Win32_Watcher *
+_eio_monitor_win32_watcher_new(Eio_Monitor *monitor, unsigned char is_dir)
+{
+   Eio_Monitor_Win32_Watcher *w;
+   DWORD                      filter;
+
+   w = (Eio_Monitor_Win32_Watcher *)calloc(1, sizeof(Eio_Monitor_Win32_Watcher));
+   if (!w) return NULL;
+
+   w->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 (w->handle == INVALID_HANDLE_VALUE)
+     goto free_w;
+
+   w->event = CreateEvent(NULL, FALSE, FALSE, NULL);
+   if (!w->event)
+     goto close_handle;
+
+   ZeroMemory (&w->overlapped, sizeof(w->overlapped));
+   w->overlapped.hEvent = w->event;
+
+   filter = (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;
+
+   if (!ReadDirectoryChangesW(w->handle,
+                              w->buffer,
+                              EIO_MONITOR_WIN32_BUFFER_SIZE,
+                              FALSE,
+                              filter,
+                              &w->buf_length,
+                              &w->overlapped,
+                              NULL))
+     goto close_event;
+
+   w->h = ecore_main_win32_handler_add(w->event,
+                                       _eio_monitor_win32_cb,
+                                       w);
+   if (!w->h)
+     goto close_event;
+
+   w->monitor = monitor;
+   w->is_dir = is_dir;
+
+   return w;
+
+ close_event:
+   CloseHandle(w->event);
+ close_handle:
+   CloseHandle(w->handle);
+ free_w:
+   free(w);
+
+   return NULL;
+}
+
+static void
+_eio_monitor_win32_watcher_free(Eio_Monitor_Win32_Watcher *w)
+{
+   if (!w) return;
+
+   CloseHandle(w->event);
+   CloseHandle (w->handle);
+   free (w);
+}
+
 /**
  * @endcond
  */
@@ -46,7 +245,6 @@ struct _Eio_Monitor_Backend
 
 void eio_monitor_backend_init(void)
 {
-  abort();
 }
 
 void eio_monitor_backend_shutdown(void)
@@ -55,10 +253,48 @@ void eio_monitor_backend_shutdown(void)
 
 void eio_monitor_backend_add(Eio_Monitor *monitor)
 {
+   Eio_Monitor_Backend *backend;
+
+   backend = calloc(1, sizeof (Eio_Monitor_Backend));
+   if (!backend)
+     {
+        eio_monitor_fallback_add(monitor);
+        return;
+     }
+
+   backend->parent = monitor;
+   backend->file = _eio_monitor_win32_watcher_new(monitor, 0);
+   if (!backend->file)
+     {
+        free(backend);
+        eio_monitor_fallback_add(monitor);
+        return;
+     }
+
+   backend->dir = _eio_monitor_win32_watcher_new(monitor, 1);
+   if (!backend->dir)
+     {
+        _eio_monitor_win32_watcher_free(backend->file);
+        free(backend);
+        eio_monitor_fallback_add(monitor);
+        return;
+     }
+
+   monitor->backend = backend;
 }
 
 void eio_monitor_backend_del(Eio_Monitor *monitor)
 {
+   if (!monitor->backend)
+     {
+        eio_monitor_fallback_del(monitor);
+        return ;
+     }
+
+   _eio_monitor_win32_watcher_free(monitor->backend->file);
+   _eio_monitor_win32_watcher_free(monitor->backend->dir);
+   free(monitor->backend);
+   monitor->backend = NULL;
 }