efl/src/lib/eio/eio_monitor_win32.c

426 lines
11 KiB
C

/* EIO - EFL data type library
* Copyright (C) 2011 Enlightenment Developers:
* Cedric Bail <cedric.bail@free.fr>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library;
* if not, see <http://www.gnu.org/licenses/>.
*/
#include "eio_private.h"
#include "Eio.h"
/*============================================================================*
* Local *
*============================================================================*/
/**
* @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;
char *current;
char *file;
DWORD buf_length;
Eina_Bool monitor_file : 1;
Eina_Bool monitor_parent : 1;
};
struct _Eio_Monitor_Backend
{
Eio_Monitor *parent;
Eio_Monitor_Win32_Watcher *watcher_file;
Eio_Monitor_Win32_Watcher *watcher_dir;
Eio_Monitor_Win32_Watcher *watcher_parent;
};
static Eina_Bool _eio_monitor_win32_native = EINA_FALSE;
static Eina_Bool
_eio_monitor_win32_cb(void *data, Ecore_Win32_Handler *wh EINA_UNUSED)
{
PFILE_NOTIFY_INFORMATION fni;
Eio_Monitor_Win32_Watcher *w;
wchar_t *wname;
char *name;
DWORD filter;
DWORD offset;
DWORD buf_length;
int event;
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(fni->FileNameLength + sizeof(wchar_t));
if (!wname)
return 0;
memcpy(wname, fni->FileName, fni->FileNameLength);
wname[fni->FileNameLength / sizeof(wchar_t)] = 0;
name = evil_wchar_to_char(wname);
free(wname);
if (!name)
return ECORE_CALLBACK_CANCEL;
event = -1;
switch (fni->Action)
{
case FILE_ACTION_ADDED:
if (!w->monitor_parent)
{
if (w->monitor_file)
event = EIO_MONITOR_FILE_CREATED;
else
event = EIO_MONITOR_DIRECTORY_CREATED;
}
break;
case FILE_ACTION_REMOVED:
if (w->monitor_parent)
{
char path[MAX_PATH];
char *res;
res = _fullpath(path, name, MAX_PATH);
if (res && (strcmp(res, w->current) == 0))
event = EIO_MONITOR_SELF_DELETED;
}
else
{
if (w->monitor_file)
event = EIO_MONITOR_FILE_DELETED;
else
event = EIO_MONITOR_DIRECTORY_DELETED;
}
break;
case FILE_ACTION_MODIFIED:
if (!w->monitor_parent)
{
if (w->monitor_file)
event = EIO_MONITOR_FILE_MODIFIED;
else
event = EIO_MONITOR_DIRECTORY_MODIFIED;
}
break;
case FILE_ACTION_RENAMED_OLD_NAME:
if (!w->monitor_parent)
{
if (w->monitor_file)
event = EIO_MONITOR_FILE_DELETED;
else
event = EIO_MONITOR_DIRECTORY_DELETED;
}
break;
case FILE_ACTION_RENAMED_NEW_NAME:
if (!w->monitor_parent)
{
if (w->monitor_file)
event = EIO_MONITOR_FILE_CREATED;
else
event = EIO_MONITOR_DIRECTORY_CREATED;
}
break;
default:
ERR("unknown event");
event = EIO_MONITOR_ERROR;
break;
}
if (event >= 0)
_eio_monitor_send(w->monitor, name, event);
free(name);
fni = (PFILE_NOTIFY_INFORMATION)((LPBYTE)fni + offset);
} while (offset);
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 (w->monitor_file)
filter |= FILE_NOTIFY_CHANGE_FILE_NAME;
else
filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
ReadDirectoryChangesW(w->handle,
(LPVOID)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,
char *current,
char *file,
Eina_Bool monitor_file,
Eina_Bool monitor_parent)
{
Eio_Monitor_Win32_Watcher *w;
char *monitored;
DWORD filter;
w = (Eio_Monitor_Win32_Watcher *)calloc(1, sizeof(Eio_Monitor_Win32_Watcher));
if (!w) return NULL;
if (!monitor_parent)
monitored = current;
else
{
char *tmp;
tmp = strrchr(current, '\\');
monitored = (char *)alloca((tmp - current) + 1);
memcpy(monitored, current, tmp - current);
monitored[tmp - current] = '\0';
}
w->handle = CreateFile(monitored,
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 =
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 (monitor_file)
filter |= FILE_NOTIFY_CHANGE_FILE_NAME;
else
filter |= FILE_NOTIFY_CHANGE_DIR_NAME;
if (!ReadDirectoryChangesW(w->handle,
(LPVOID)w->buffer,
EIO_MONITOR_WIN32_BUFFER_SIZE,
FALSE,
filter,
&w->buf_length,
&w->overlapped,
NULL))
{
char *msg;
msg = evil_last_error_get();
if (msg)
{
ERR("%s", msg);
free(msg);
}
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->monitor_file = monitor_file;
w->monitor_parent = monitor_parent;
w->file = file;
w->current = current;
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;
if (w->file)
free(w->file);
free(w->current);
CloseHandle(w->event);
CloseHandle (w->handle);
free (w);
}
/**
* @endcond
*/
/*============================================================================*
* Global *
*============================================================================*/
/**
* @cond LOCAL
*/
void eio_monitor_backend_init(void)
{
}
void eio_monitor_backend_shutdown(void)
{
}
void eio_monitor_backend_add(Eio_Monitor *monitor)
{
char path[PATH_MAX];
struct _stat s;
char *res;
char *current;
char *file = NULL;
Eio_Monitor_Backend *backend;
int ret;
res = _fullpath(path, monitor->path, MAX_PATH);
if (!res)
goto fallback;
ret = _stat(res, &s);
if (ret != 0)
goto fallback;
if (_S_IFDIR & s.st_mode)
{
current = strdup(path);
if (!current)
goto fallback;
}
else if (_S_IFREG & s.st_mode)
{
char *tmp;
tmp = strrchr(path, '\\');
file = strdup(tmp + 1);
if (!file)
goto fallback;
*tmp = '\0';
current = strdup(path);
if (!current)
{
free(file);
goto fallback;
}
}
else
goto fallback;
backend = calloc(1, sizeof (Eio_Monitor_Backend));
if (!backend)
goto fallback;
backend->parent = monitor;
backend->watcher_file = _eio_monitor_win32_watcher_new(monitor, current, file, EINA_TRUE, EINA_FALSE);
if (!backend->watcher_file)
goto free_backend;
backend->watcher_dir = _eio_monitor_win32_watcher_new(monitor, current, file, EINA_FALSE, EINA_FALSE);
if (!backend->watcher_dir)
goto free_backend_file;
backend->watcher_parent = _eio_monitor_win32_watcher_new(monitor, current, file, EINA_FALSE, EINA_TRUE);
if (!backend->watcher_parent)
goto free_backend_dir;
_eio_monitor_win32_native = EINA_TRUE;
monitor->backend = backend;
return;
free_backend_dir:
_eio_monitor_win32_watcher_free(backend->watcher_dir);
free_backend_file:
_eio_monitor_win32_watcher_free(backend->watcher_file);
free_backend:
free(backend);
fallback:
INF("falling back to poll monitoring");
eio_monitor_fallback_add(monitor);
}
void eio_monitor_backend_del(Eio_Monitor *monitor)
{
if (!_eio_monitor_win32_native)
{
eio_monitor_fallback_del(monitor);
return ;
}
_eio_monitor_win32_watcher_free(monitor->backend->watcher_parent);
_eio_monitor_win32_watcher_free(monitor->backend->watcher_dir);
_eio_monitor_win32_watcher_free(monitor->backend->watcher_file);
free(monitor->backend);
monitor->backend = NULL;
}
/**
* @endcond
*/
/*============================================================================*
* API *
*============================================================================*/