Add fsevents backend to dirmonitor (#1141)

* dirmonitor: added backend reporting of watch mode

* dirmonitor: added fsevents backend for macos
This commit is contained in:
Jefferson González 2022-10-10 17:40:41 -07:00 committed by GitHub
parent 34c4ac3cd5
commit a7888e96ea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 229 additions and 20 deletions

View File

@ -14,8 +14,8 @@ function dirwatch.new()
watched = {},
reverse_watched = {},
monitor = dirmonitor.new(),
windows_watch_top = nil,
windows_watch_count = 0
single_watch_top = nil,
single_watch_count = 0
}
setmetatable(t, dirwatch)
return t
@ -38,23 +38,23 @@ function dirwatch:watch(directory, bool)
local info = system.get_file_info(directory)
if not info then return end
if not self.watched[directory] and not self.scanned[directory] then
if PLATFORM == "Windows" then
if self.monitor:mode() == "single" then
if info.type ~= "dir" then return self:scan(directory) end
if not self.windows_watch_top or directory:find(self.windows_watch_top, 1, true) ~= 1 then
if not self.single_watch_top or directory:find(self.single_watch_top, 1, true) ~= 1 then
-- Get the highest level of directory that is common to this directory, and the original.
local target = directory
while self.windows_watch_top and self.windows_watch_top:find(target, 1, true) ~= 1 do
while self.single_watch_top and self.single_watch_top:find(target, 1, true) ~= 1 do
target = common.dirname(target)
end
if target ~= self.windows_watch_top then
if target ~= self.single_watch_top then
local value = self.monitor:watch(target)
if value and value < 0 then
return self:scan(directory)
end
self.windows_watch_top = target
self.single_watch_top = target
end
end
self.windows_watch_count = self.windows_watch_count + 1
self.single_watch_count = self.single_watch_count + 1
self.watched[directory] = true
else
local value = self.monitor:watch(directory)
@ -72,13 +72,13 @@ end
-- this should be an absolute path
function dirwatch:unwatch(directory)
if self.watched[directory] then
if PLATFORM ~= "Windows" then
if self.monitor:mode() == "multiple" then
self.monitor:unwatch(self.watched[directory])
self.reverse_watched[directory] = nil
else
self.windows_watch_count = self.windows_watch_count - 1
if self.windows_watch_count == 0 then
self.windows_watch_top = nil
self.single_watch_count = self.single_watch_count - 1
if self.single_watch_count == 0 then
self.single_watch_top = nil
self.monitor:unwatch(directory)
end
end
@ -93,8 +93,12 @@ function dirwatch:check(change_callback, scan_time, wait_time)
local had_change = false
self.monitor:check(function(id)
had_change = true
if PLATFORM == "Windows" then
change_callback(common.dirname(self.windows_watch_top .. PATHSEP .. id))
if self.monitor:mode() == "single" then
local path = common.dirname(id)
if not string.match(id, "^/") and not string.match(id, "^%a:[/\\]") then
path = common.dirname(self.single_watch_top .. PATHSEP .. id)
end
change_callback(path)
elseif self.reverse_watched[id] then
change_callback(self.reverse_watched[id])
end

View File

@ -2,4 +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('portable', type : 'boolean', value : false, description: 'Portable install')
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')
option('dirmonitor_backend', type : 'combo', value : '', choices : ['', 'inotify', 'fsevents', 'kqueue', 'win32', 'dummy'], description: 'define what dirmonitor backend to use')

View File

@ -23,6 +23,7 @@ int get_changes_dirmonitor(struct dirmonitor_internal*, char*, int);
int translate_changes_dirmonitor(struct dirmonitor_internal*, char*, int, int (*)(int, const char*, void*), void*);
int add_dirmonitor(struct dirmonitor_internal*, const char*);
void remove_dirmonitor(struct dirmonitor_internal*, int);
int get_mode_dirmonitor();
static int f_check_dir_callback(int watch_id, const char* path, void* L) {
@ -111,12 +112,23 @@ static int f_dirmonitor_check(lua_State* L) {
}
static int f_dirmonitor_mode(lua_State* L) {
int mode = get_mode_dirmonitor();
if (mode == 1)
lua_pushstring(L, "single");
else
lua_pushstring(L, "multiple");
return 1;
}
static const luaL_Reg dirmonitor_lib[] = {
{ "new", f_dirmonitor_new },
{ "__gc", f_dirmonitor_gc },
{ "watch", f_dirmonitor_watch },
{ "unwatch", f_dirmonitor_unwatch },
{ "check", f_dirmonitor_check },
{ "mode", f_dirmonitor_mode },
{NULL, NULL}
};

View File

@ -6,3 +6,4 @@ int get_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, si
int translate_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, int size, int (*callback)(int, const char*, void*), void* data) { return -1; }
int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) { return -1; }
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) { }
int get_mode_dirmonitor() { return 1; }

View File

@ -0,0 +1,181 @@
#include <SDL.h>
#include <CoreServices/CoreServices.h>
struct dirmonitor_internal {
SDL_mutex* lock;
char** changes;
size_t count;
FSEventStreamRef stream;
};
CFRunLoopRef main_run_loop;
struct dirmonitor_internal* init_dirmonitor() {
static bool mainloop_registered = false;
if (!mainloop_registered) {
main_run_loop = CFRunLoopGetCurrent();
mainloop_registered = true;
}
struct dirmonitor_internal* monitor = malloc(sizeof(struct dirmonitor_internal));
monitor->stream = NULL;
monitor->changes = NULL;
monitor->count = 0;
return monitor;
}
static void stop_monitor_stream(struct dirmonitor_internal* monitor) {
if (monitor->stream) {
FSEventStreamStop(monitor->stream);
FSEventStreamUnscheduleFromRunLoop(
monitor->stream, main_run_loop, kCFRunLoopDefaultMode
);
FSEventStreamInvalidate(monitor->stream);
FSEventStreamRelease(monitor->stream);
monitor->stream = NULL;
SDL_LockMutex(monitor->lock);
if (monitor->count > 0) {
for (size_t i = 0; i<monitor->count; i++) {
free(monitor->changes[i]);
}
free(monitor->changes);
monitor->changes = NULL;
monitor->count = 0;
}
SDL_UnlockMutex(monitor->lock);
SDL_DestroyMutex(monitor->lock);
}
}
void deinit_dirmonitor(struct dirmonitor_internal* monitor) {
stop_monitor_stream(monitor);
}
static void stream_callback(
ConstFSEventStreamRef streamRef,
void* monitor_ptr,
size_t numEvents,
void* eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]
)
{
if (numEvents <= 0) {
return;
}
struct dirmonitor_internal* monitor = monitor_ptr;
char** path_list = eventPaths;
SDL_LockMutex(monitor->lock);
size_t total = 0;
if (monitor->count == 0) {
total = numEvents;
monitor->changes = calloc(numEvents, sizeof(char*));
} else {
total = monitor->count + numEvents;
monitor->changes = realloc(
monitor->changes,
sizeof(char*) * total
);
}
for (size_t idx = monitor->count; idx < total; idx++) {
size_t pidx = idx - monitor->count;
monitor->changes[idx] = malloc(strlen(path_list[pidx])+1);
strcpy(monitor->changes[idx], path_list[pidx]);
}
monitor->count = total;
SDL_UnlockMutex(monitor->lock);
}
int get_changes_dirmonitor(
struct dirmonitor_internal* monitor,
char* buffer,
int buffer_size
) {
FSEventStreamFlushSync(monitor->stream);
size_t results = 0;
SDL_LockMutex(monitor->lock);
results = monitor->count;
SDL_UnlockMutex(monitor->lock);
return results;
}
int translate_changes_dirmonitor(
struct dirmonitor_internal* monitor,
char* buffer,
int buffer_size,
int (*change_callback)(int, const char*, void*),
void* L
) {
SDL_LockMutex(monitor->lock);
if (monitor->count > 0) {
for (size_t i = 0; i<monitor->count; i++) {
change_callback(strlen(monitor->changes[i]), monitor->changes[i], L);
free(monitor->changes[i]);
}
free(monitor->changes);
monitor->changes = NULL;
monitor->count = 0;
}
SDL_UnlockMutex(monitor->lock);
return 0;
}
int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) {
stop_monitor_stream(monitor);
FSEventStreamContext context = {
.info = monitor,
.retain = NULL,
.release = NULL,
.copyDescription = NULL,
.version = 0
};
CFStringRef paths[] = {
CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8)
};
monitor->stream = FSEventStreamCreate(
NULL,
stream_callback,
&context,
CFArrayCreate(NULL, (const void **)&paths, 1, NULL),
kFSEventStreamEventIdSinceNow,
10000,
kFSEventStreamCreateFlagNone
| kFSEventStreamCreateFlagWatchRoot
| kFSEventStreamCreateFlagFileEvents
);
FSEventStreamScheduleWithRunLoop(
monitor->stream, main_run_loop, kCFRunLoopDefaultMode
);
if (!FSEventStreamStart(monitor->stream)) {
stop_monitor_stream(monitor);
return -1;
}
return 1;
}
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) {
stop_monitor_stream(monitor);
}
int get_mode_dirmonitor() { return 1; }

View File

@ -13,7 +13,7 @@ struct dirmonitor_internal {
struct dirmonitor_internal* init_dirmonitor() {
struct dirmonitor_internal* monitor = calloc(sizeof(struct dirmonitor_internal), 1);
struct dirmonitor_internal* monitor = calloc(1, sizeof(struct dirmonitor_internal));
monitor->fd = inotify_init();
pipe(monitor->sig);
fcntl(monitor->sig[0], F_SETFD, FD_CLOEXEC);
@ -51,3 +51,6 @@ int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) {
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) {
inotify_rm_watch(monitor->fd, fd);
}
int get_mode_dirmonitor() { return 2; }

View File

@ -11,7 +11,7 @@ struct dirmonitor_internal {
struct dirmonitor_internal* init_dirmonitor() {
struct dirmonitor_internal* monitor = calloc(sizeof(struct dirmonitor_internal), 1);
struct dirmonitor_internal* monitor = calloc(1, sizeof(struct dirmonitor_internal));
monitor->fd = kqueue();
return monitor;
}
@ -53,3 +53,6 @@ int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) {
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) {
close(fd);
}
int get_mode_dirmonitor() { return 2; }

View File

@ -19,7 +19,7 @@ int get_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, in
struct dirmonitor* init_dirmonitor() {
return calloc(sizeof(struct dirmonitor_internal), 1);
return calloc(1, sizeof(struct dirmonitor_internal));
}
@ -60,3 +60,6 @@ int add_dirmonitor(struct dirmonitor_internal* monitor, const char* path) {
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) {
close_monitor_handle(monitor);
}
int get_mode_dirmonitor() { return 1; }

View File

@ -15,6 +15,8 @@ lite_sources = [
if get_option('dirmonitor_backend') == ''
if cc.has_function('inotify_init', prefix : '#include<sys/inotify.h>')
dirmonitor_backend = 'inotify'
elif host_machine.system() == 'darwin' and cc.check_header('CoreServices/CoreServices.h')
dirmonitor_backend = 'fsevents'
elif cc.has_function('kqueue', prefix : '#include<sys/event.h>')
dirmonitor_backend = 'kqueue'
elif dependency('libkqueue', required : false).found()