#include "twn_filewatch_c.h" #include "twn_util.h" #include "twn_engine_context_c.h" #define DMON_IMPL #include #include #include 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); }