seperate dirmonitor logic, add build time detection of features (#866)
this also adds libkqueue support
This commit is contained in:
parent
20763ed7ff
commit
120c769e7e
|
@ -2,3 +2,4 @@ option('bundle', type : 'boolean', value : false, description: 'Build a macOS bu
|
||||||
option('source-only', type : 'boolean', value : false, description: 'Configure source files only, doesn\'t checks for dependencies')
|
option('source-only', type : 'boolean', value : false, description: 'Configure source files only, doesn\'t checks for dependencies')
|
||||||
option('portable', type : 'boolean', value : false, description: 'Portable install')
|
option('portable', type : 'boolean', value : false, description: 'Portable install')
|
||||||
option('renderer', type : 'boolean', value : false, description: 'Use SDL renderer')
|
option('renderer', type : 'boolean', value : false, description: 'Use SDL renderer')
|
||||||
|
option('dirmonitor_backend', type : 'combo', value : '', choices : ['', 'inotify', 'kqueue', 'win32', 'dummy'], description: 'define what dirmonitor backend to use')
|
|
@ -15,144 +15,31 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
/*
|
#ifndef DIRMONITOR_BACKEND
|
||||||
This is *slightly* a clusterfuck. Normally, we'd
|
#error No dirmonitor backend defined
|
||||||
have windows wait on a list of handles like inotify,
|
|
||||||
however, MAXIMUM_WAIT_OBJECTS is 64. Yes, seriously.
|
|
||||||
|
|
||||||
So, for windows, we are recursive.
|
|
||||||
*/
|
|
||||||
struct dirmonitor {
|
|
||||||
int fd;
|
|
||||||
#if _WIN32
|
|
||||||
HANDLE handle;
|
|
||||||
char buffer[8192];
|
|
||||||
OVERLAPPED overlapped;
|
|
||||||
bool running;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dirmonitor* init_dirmonitor() {
|
|
||||||
struct dirmonitor* monitor = calloc(sizeof(struct dirmonitor), 1);
|
|
||||||
#ifndef _WIN32
|
|
||||||
#if __linux__
|
|
||||||
monitor->fd = inotify_init1(IN_NONBLOCK);
|
|
||||||
#else
|
|
||||||
monitor->fd = kqueue();
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
return monitor;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if _WIN32
|
|
||||||
static void close_monitor_handle(struct dirmonitor* monitor) {
|
|
||||||
if (monitor->handle) {
|
|
||||||
if (monitor->running) {
|
|
||||||
BOOL result = CancelIoEx(monitor->handle, &monitor->overlapped);
|
|
||||||
DWORD error = GetLastError();
|
|
||||||
if (result == TRUE || error != ERROR_NOT_FOUND) {
|
|
||||||
DWORD bytes_transferred;
|
|
||||||
GetOverlappedResult( monitor->handle, &monitor->overlapped, &bytes_transferred, TRUE );
|
|
||||||
}
|
|
||||||
monitor->running = false;
|
|
||||||
}
|
|
||||||
CloseHandle(monitor->handle);
|
|
||||||
}
|
|
||||||
monitor->handle = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void deinit_dirmonitor(struct dirmonitor* monitor) {
|
#define GLUE_HELPER(x, y) x##y
|
||||||
#if _WIN32
|
#define GLUE(x, y) GLUE_HELPER(x, y)
|
||||||
close_monitor_handle(monitor);
|
|
||||||
#else
|
|
||||||
close(monitor->fd);
|
|
||||||
#endif
|
|
||||||
free(monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
int check_dirmonitor(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
|
#define init_dirmonitor GLUE(init_dirmonitor_, DIRMONITOR_BACKEND)
|
||||||
#if _WIN32
|
#define deinit_dirmonitor GLUE(deinit_dirmonitor_, DIRMONITOR_BACKEND)
|
||||||
if (!monitor->running) {
|
#define check_dirmonitor GLUE(check_dirmonitor_, DIRMONITOR_BACKEND)
|
||||||
if (ReadDirectoryChangesW(monitor->handle, monitor->buffer, sizeof(monitor->buffer), TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &monitor->overlapped, NULL) == 0)
|
#define add_dirmonitor GLUE(add_dirmonitor_, DIRMONITOR_BACKEND)
|
||||||
return GetLastError();
|
#define remove_dirmonitor GLUE(remove_dirmonitor_, DIRMONITOR_BACKEND)
|
||||||
monitor->running = true;
|
|
||||||
}
|
|
||||||
DWORD bytes_transferred;
|
|
||||||
if (!GetOverlappedResult(monitor->handle, &monitor->overlapped, &bytes_transferred, FALSE)) {
|
|
||||||
int error = GetLastError();
|
|
||||||
return error == ERROR_IO_PENDING || error == ERROR_IO_INCOMPLETE ? 0 : error;
|
|
||||||
}
|
|
||||||
monitor->running = false;
|
|
||||||
for (FILE_NOTIFY_INFORMATION* info = (FILE_NOTIFY_INFORMATION*)monitor->buffer; (char*)info < monitor->buffer + sizeof(monitor->buffer); info = (FILE_NOTIFY_INFORMATION*)((char*)info) + info->NextEntryOffset) {
|
|
||||||
change_callback(info->FileNameLength, (char*)info->FileName, data);
|
|
||||||
if (!info->NextEntryOffset)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
monitor->running = false;
|
|
||||||
return 0;
|
|
||||||
#elif __linux__
|
|
||||||
char buf[PATH_MAX + sizeof(struct inotify_event)];
|
|
||||||
ssize_t offset = 0;
|
|
||||||
while (1) {
|
|
||||||
ssize_t len = read(monitor->fd, &buf[offset], sizeof(buf) - offset);
|
|
||||||
if (len == -1 && errno != EAGAIN)
|
|
||||||
return errno;
|
|
||||||
if (len <= 0)
|
|
||||||
return 0;
|
|
||||||
while (len > sizeof(struct inotify_event) && len >= ((struct inotify_event*)buf)->len + sizeof(struct inotify_event)) {
|
|
||||||
change_callback(((const struct inotify_event *)buf)->wd, NULL, data);
|
|
||||||
len -= sizeof(struct inotify_event) + ((struct inotify_event*)buf)->len;
|
|
||||||
memmove(buf, &buf[sizeof(struct inotify_event) + ((struct inotify_event*)buf)->len], len);
|
|
||||||
offset = len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
struct kevent event;
|
|
||||||
while (1) {
|
|
||||||
struct timespec tm = {0};
|
|
||||||
int nev = kevent(monitor->fd, NULL, 0, &event, 1, &tm);
|
|
||||||
if (nev == -1)
|
|
||||||
return errno;
|
|
||||||
if (nev <= 0)
|
|
||||||
return 0;
|
|
||||||
change_callback(event.ident, NULL, data);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int add_dirmonitor(struct dirmonitor* monitor, const char* path) {
|
struct dirmonitor {}; // dirmonitor struct is defined in each backend
|
||||||
#if _WIN32
|
|
||||||
close_monitor_handle(monitor);
|
|
||||||
monitor->handle = CreateFileA(path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
|
|
||||||
if (monitor->handle && monitor->handle != INVALID_HANDLE_VALUE)
|
|
||||||
return 1;
|
|
||||||
monitor->handle = NULL;
|
|
||||||
return -1;
|
|
||||||
#elif __linux__
|
|
||||||
return inotify_add_watch(monitor->fd, path, IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO);
|
|
||||||
#else
|
|
||||||
int fd = open(path, O_RDONLY);
|
|
||||||
struct kevent change;
|
|
||||||
EV_SET(&change, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME, 0, (void*)path);
|
|
||||||
kevent(monitor->fd, &change, 1, NULL, 0, NULL);
|
|
||||||
return fd;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_dirmonitor(struct dirmonitor* monitor, int fd) {
|
// define functions so we know their signature
|
||||||
#if _WIN32
|
struct dirmonitor* init_dirmonitor();
|
||||||
close_monitor_handle(monitor);
|
void deinit_dirmonitor(struct dirmonitor*);
|
||||||
#elif __linux__
|
int check_dirmonitor(struct dirmonitor*, int (*)(int, const char*, void*), void*);
|
||||||
inotify_rm_watch(monitor->fd, fd);
|
int add_dirmonitor(struct dirmonitor*, const char*);
|
||||||
#else
|
void remove_dirmonitor(struct dirmonitor*, int);
|
||||||
close(fd);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static int f_check_dir_callback(int watch_id, const char* path, void* L) {
|
static int f_check_dir_callback(int watch_id, const char* path, void* L) {
|
||||||
lua_pushvalue(L, -1);
|
lua_pushvalue(L, -1);
|
||||||
#if _WIN32
|
#ifdef DIRMONITOR_WIN32
|
||||||
char buffer[PATH_MAX*4];
|
char buffer[PATH_MAX*4];
|
||||||
int count = WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)path, watch_id, buffer, PATH_MAX*4 - 1, NULL, NULL);
|
int count = WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)path, watch_id, buffer, PATH_MAX*4 - 1, NULL, NULL);
|
||||||
lua_pushlstring(L, buffer, count);
|
lua_pushlstring(L, buffer, count);
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
struct dirmonitor {
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dirmonitor* init_dirmonitor_dummy() {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deinit_dirmonitor_dummy(struct dirmonitor* monitor) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int check_dirmonitor_dummy(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int add_dirmonitor_dummy(struct dirmonitor* monitor, const char* path) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_dirmonitor_dummy(struct dirmonitor* monitor, int fd) {
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
struct dirmonitor {
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dirmonitor* init_dirmonitor_inotify() {
|
||||||
|
struct dirmonitor* monitor = calloc(sizeof(struct dirmonitor), 1);
|
||||||
|
|
||||||
|
monitor->fd = inotify_init();
|
||||||
|
fcntl(monitor->fd, F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
|
|
||||||
|
return monitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deinit_dirmonitor_inotify(struct dirmonitor* monitor) {
|
||||||
|
close(monitor->fd);
|
||||||
|
free(monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
int check_dirmonitor_inotify(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
|
||||||
|
char buf[PATH_MAX + sizeof(struct inotify_event)];
|
||||||
|
ssize_t offset = 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
ssize_t len = read(monitor->fd, &buf[offset], sizeof(buf) - offset);
|
||||||
|
|
||||||
|
if (len == -1 && errno != EAGAIN) {
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (len > sizeof(struct inotify_event) && len >= ((struct inotify_event*)buf)->len + sizeof(struct inotify_event)) {
|
||||||
|
change_callback(((const struct inotify_event *)buf)->wd, NULL, data);
|
||||||
|
len -= sizeof(struct inotify_event) + ((struct inotify_event*)buf)->len;
|
||||||
|
memmove(buf, &buf[sizeof(struct inotify_event) + ((struct inotify_event*)buf)->len], len);
|
||||||
|
offset = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int add_dirmonitor_inotify(struct dirmonitor* monitor, const char* path) {
|
||||||
|
return inotify_add_watch(monitor->fd, path, IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_dirmonitor_inotify(struct dirmonitor* monitor, int fd) {
|
||||||
|
inotify_rm_watch(monitor->fd, fd);
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include <sys/event.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
struct dirmonitor {
|
||||||
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dirmonitor* init_dirmonitor_kqueue() {
|
||||||
|
struct dirmonitor* monitor = calloc(sizeof(struct dirmonitor), 1);
|
||||||
|
monitor->fd = kqueue();
|
||||||
|
return monitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deinit_dirmonitor_kqueue(struct dirmonitor* monitor) {
|
||||||
|
close(monitor->fd);
|
||||||
|
free(monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
int check_dirmonitor_kqueue(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
|
||||||
|
struct kevent event;
|
||||||
|
while (1) {
|
||||||
|
struct timespec tm = {0};
|
||||||
|
int nev = kevent(monitor->fd, NULL, 0, &event, 1, &tm);
|
||||||
|
|
||||||
|
if (nev == -1) {
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nev <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
change_callback(event.ident, NULL, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int add_dirmonitor_kqueue(struct dirmonitor* monitor, const char* path) {
|
||||||
|
int fd = open(path, O_RDONLY);
|
||||||
|
struct kevent change;
|
||||||
|
|
||||||
|
EV_SET(&change, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR, NOTE_DELETE | NOTE_EXTEND | NOTE_WRITE | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME, 0, (void*)path);
|
||||||
|
kevent(monitor->fd, &change, 1, NULL, 0, NULL);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_dirmonitor_kqueue(struct dirmonitor* monitor, int fd) {
|
||||||
|
close(fd);
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
struct dirmonitor {
|
||||||
|
HANDLE handle;
|
||||||
|
char buffer[8192];
|
||||||
|
OVERLAPPED overlapped;
|
||||||
|
bool running;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dirmonitor* init_dirmonitor_win32() {
|
||||||
|
struct dirmonitor* monitor = calloc(sizeof(struct dirmonitor), 1);
|
||||||
|
|
||||||
|
return monitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_monitor_handle(struct dirmonitor* monitor) {
|
||||||
|
if (monitor->handle) {
|
||||||
|
if (monitor->running) {
|
||||||
|
BOOL result = CancelIoEx(monitor->handle, &monitor->overlapped);
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
if (result == TRUE || error != ERROR_NOT_FOUND) {
|
||||||
|
DWORD bytes_transferred;
|
||||||
|
GetOverlappedResult( monitor->handle, &monitor->overlapped, &bytes_transferred, TRUE );
|
||||||
|
}
|
||||||
|
monitor->running = false;
|
||||||
|
}
|
||||||
|
CloseHandle(monitor->handle);
|
||||||
|
}
|
||||||
|
monitor->handle = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deinit_dirmonitor_win32(struct dirmonitor* monitor) {
|
||||||
|
close_monitor_handle(monitor);
|
||||||
|
free(monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
int check_dirmonitor_win32(struct dirmonitor* monitor, int (*change_callback)(int, const char*, void*), void* data) {
|
||||||
|
if (!monitor->running) {
|
||||||
|
if (ReadDirectoryChangesW(monitor->handle, monitor->buffer, sizeof(monitor->buffer), TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, NULL, &monitor->overlapped, NULL) == 0) {
|
||||||
|
return GetLastError();
|
||||||
|
}
|
||||||
|
monitor->running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD bytes_transferred;
|
||||||
|
|
||||||
|
if (!GetOverlappedResult(monitor->handle, &monitor->overlapped, &bytes_transferred, FALSE)) {
|
||||||
|
int error = GetLastError();
|
||||||
|
return error == ERROR_IO_PENDING || error == ERROR_IO_INCOMPLETE ? 0 : error;
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor->running = false;
|
||||||
|
|
||||||
|
for (FILE_NOTIFY_INFORMATION* info = (FILE_NOTIFY_INFORMATION*)monitor->buffer; (char*)info < monitor->buffer + sizeof(monitor->buffer); info = (FILE_NOTIFY_INFORMATION*)((char*)info) + info->NextEntryOffset) {
|
||||||
|
change_callback(info->FileNameLength, (char*)info->FileName, data);
|
||||||
|
if (!info->NextEntryOffset)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor->running = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int add_dirmonitor_win32(struct dirmonitor* monitor, const char* path) {
|
||||||
|
close_monitor_handle(monitor);
|
||||||
|
monitor->handle = CreateFileA(path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
|
||||||
|
if (monitor->handle && monitor->handle != INVALID_HANDLE_VALUE) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
monitor->handle = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_dirmonitor_win32(struct dirmonitor* monitor, int fd) {
|
||||||
|
close_monitor_handle(monitor);
|
||||||
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
lite_sources = [
|
lite_sources = [
|
||||||
'api/api.c',
|
'api/api.c',
|
||||||
'api/dirmonitor.c',
|
|
||||||
'api/renderer.c',
|
'api/renderer.c',
|
||||||
'api/regex.c',
|
'api/regex.c',
|
||||||
'api/system.c',
|
'api/system.c',
|
||||||
|
@ -11,6 +10,41 @@ lite_sources = [
|
||||||
'main.c',
|
'main.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# dirmonitor backend
|
||||||
|
if get_option('dirmonitor_backend') == ''
|
||||||
|
if cc.has_function('inotify_init', prefix : '#include<sys/inotify.h>')
|
||||||
|
dirmonitor_backend = 'inotify'
|
||||||
|
elif cc.has_function('kqueue', prefix : '#include<sys/event.h>')
|
||||||
|
dirmonitor_backend = 'kqueue'
|
||||||
|
elif dependency('libkqueue', required : false).found()
|
||||||
|
dirmonitor_backend = 'kqueue'
|
||||||
|
elif host_machine.system() == 'windows'
|
||||||
|
dirmonitor_backend = 'win32'
|
||||||
|
else
|
||||||
|
dirmonitor_backend = 'dummy'
|
||||||
|
warning('no suitable backend found, defaulting to dummy backend')
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
dirmonitor_backend = get_option('dirmonitor_backend')
|
||||||
|
endif
|
||||||
|
|
||||||
|
message('dirmonitor_backend: @0@'.format(dirmonitor_backend))
|
||||||
|
|
||||||
|
if dirmonitor_backend == 'kqueue'
|
||||||
|
libkqueue_dep = dependency('libkqueue', required : false)
|
||||||
|
if libkqueue_dep.found()
|
||||||
|
lite_deps += libkqueue_dep
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
lite_sources += [
|
||||||
|
'api/dirmonitor.c',
|
||||||
|
'api/dirmonitor/' + dirmonitor_backend + '.c',
|
||||||
|
]
|
||||||
|
lite_cargs += '-DDIRMONITOR_BACKEND=' + dirmonitor_backend
|
||||||
|
lite_cargs += '-DDIRMONITOR_' + dirmonitor_backend.to_upper()
|
||||||
|
|
||||||
|
|
||||||
lite_rc = []
|
lite_rc = []
|
||||||
if host_machine.system() == 'windows'
|
if host_machine.system() == 'windows'
|
||||||
windows = import('windows')
|
windows = import('windows')
|
||||||
|
|
Loading…
Reference in New Issue