121 lines
3.5 KiB
C
121 lines
3.5 KiB
C
#include "twn_filewatch_c.h"
|
|
#include "twn_util.h"
|
|
#include "twn_engine_context_c.h"
|
|
|
|
#define DMON_IMPL
|
|
#include <dmon.h>
|
|
#include <stb_ds.h>
|
|
#include <SDL2/SDL.h>
|
|
|
|
|
|
struct FilewatchEntry {
|
|
char *path;
|
|
FilewatchCallback callback;
|
|
enum FilewatchAction *actions_pending;
|
|
};
|
|
|
|
static struct FilewatchEntry *filewatch_directories;
|
|
static struct FilewatchEntry *filewatch_files;
|
|
/* note: it gets rebuilt on every addition, as api is such */
|
|
/* TODO: test whether it's possible to miss on update while watcher is being rebuilt */
|
|
/* we might have to enumerate things, just to make sure */
|
|
static SDL_mutex *filewatcher_lock;
|
|
static bool filewatcher_initialized;
|
|
|
|
static void filewatch_callback(dmon_watch_id watch_id,
|
|
dmon_action action,
|
|
const char* rootdir,
|
|
const char* filepath,
|
|
const char* oldfilepath,
|
|
void* user)
|
|
{
|
|
(void)watch_id; (void)rootdir; (void)filepath; (void)oldfilepath;
|
|
|
|
enum FilewatchAction faction;
|
|
|
|
switch (action) {
|
|
case DMON_ACTION_CREATE:
|
|
faction = FILEWATCH_ACTION_FILE_CREATED;
|
|
break;
|
|
case DMON_ACTION_DELETE:
|
|
faction = FILEWATCH_ACTION_FILE_DELETED;
|
|
break;
|
|
case DMON_ACTION_MODIFY:
|
|
faction = FILEWATCH_ACTION_FILE_MODIFIED;
|
|
break;
|
|
case DMON_ACTION_MOVE:
|
|
default:
|
|
return;
|
|
}
|
|
|
|
SDL_LockMutex(filewatcher_lock);
|
|
|
|
intptr_t const context = (intptr_t)user;
|
|
struct FilewatchEntry *p;
|
|
|
|
if (context < 0)
|
|
p = &filewatch_files[-context - 1];
|
|
else
|
|
p = &filewatch_directories[context];
|
|
|
|
arrpush(p->actions_pending, faction);
|
|
SDL_UnlockMutex(filewatcher_lock);
|
|
}
|
|
|
|
|
|
bool filewatch_add_directory(char const *dir, FilewatchCallback callback) {
|
|
SDL_assert(dir && callback);
|
|
|
|
if (!filewatcher_initialized) {
|
|
dmon_init();
|
|
filewatcher_initialized = true;
|
|
}
|
|
|
|
struct FilewatchEntry const w = {
|
|
.callback = callback,
|
|
.path = SDL_strdup(dir), /* TODO: free */
|
|
};
|
|
arrpush(filewatch_directories, w);
|
|
dmon_watch(dir, filewatch_callback, DMON_WATCHFLAGS_RECURSIVE, (void *)(intptr_t)(arrlen(filewatch_directories) - 1));
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool filewatch_add_file(char const *filepath, FilewatchCallback callback) {
|
|
SDL_assert(filepath && callback);
|
|
|
|
if (!filewatcher_initialized) {
|
|
dmon_init();
|
|
filewatcher_initialized = true;
|
|
}
|
|
|
|
struct FilewatchEntry const f = {
|
|
.callback = callback,
|
|
.path = SDL_strdup(filepath), /* TODO: free */
|
|
};
|
|
arrpush(filewatch_files, f);
|
|
dmon_watch("./", filewatch_callback, 0, (void *)(intptr_t)(-arrlen(filewatch_files)));
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void filewatch_poll(void) {
|
|
SDL_LockMutex(filewatcher_lock);
|
|
|
|
for (int i = 0; i < arrlen(filewatch_directories); ++i) {
|
|
for (int u = 0; u < arrlen(filewatch_directories[i].actions_pending); ++u)
|
|
filewatch_directories[i].callback(filewatch_directories[i].path, filewatch_directories[i].actions_pending[u]);
|
|
arrfree(filewatch_directories[i].actions_pending);
|
|
}
|
|
|
|
for (int i = 0; i < arrlen(filewatch_files); ++i) {
|
|
for (int u = 0; u < arrlen(filewatch_files[i].actions_pending); ++u)
|
|
filewatch_files[i].callback(filewatch_files[i].path, filewatch_files[i].actions_pending[u]);
|
|
arrfree(filewatch_files[i].actions_pending);
|
|
}
|
|
|
|
SDL_UnlockMutex(filewatcher_lock);
|
|
}
|