Add fsevents backend to dirmonitor (#1141)
* dirmonitor: added backend reporting of watch mode * dirmonitor: added fsevents backend for macos
This commit is contained in:
parent
34c4ac3cd5
commit
a7888e96ea
|
@ -2,7 +2,7 @@ local common = require "core.common"
|
|||
local config = require "core.config"
|
||||
local dirwatch = {}
|
||||
|
||||
function dirwatch:__index(idx)
|
||||
function dirwatch:__index(idx)
|
||||
local value = rawget(self, idx)
|
||||
if value ~= nil then return value end
|
||||
return dirwatch[idx]
|
||||
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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}
|
||||
};
|
||||
|
||||
|
|
|
@ -5,4 +5,5 @@ void deinit_dirmonitor(struct dirmonitor_internal* monitor) { }
|
|||
int get_changes_dirmonitor(struct dirmonitor_internal* monitor, char* buffer, size_t len) { return -1; }
|
||||
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) { }
|
||||
void remove_dirmonitor(struct dirmonitor_internal* monitor, int fd) { }
|
||||
int get_mode_dirmonitor() { return 1; }
|
||||
|
|
|
@ -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; }
|
|
@ -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; }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue