townengine/src/twn_filewatch.c
2025-02-01 14:25:54 +03:00

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);
}