#include "api.h" #include #include #include #include #include #include static unsigned int DIR_EVENT_TYPE = 0; struct dirmonitor { SDL_Thread* thread; SDL_mutex* mutex; char buffer[64512]; volatile int length; struct dirmonitor_internal* internal; }; struct dirmonitor_internal* init_dirmonitor(); void deinit_dirmonitor(struct dirmonitor_internal*); 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); static int f_check_dir_callback(int watch_id, const char* path, void* L) { lua_pushvalue(L, -1); if (path) lua_pushlstring(L, path, watch_id); else lua_pushnumber(L, watch_id); lua_call(L, 1, 1); int result = lua_toboolean(L, -1); lua_pop(L, 1); return !result; } static int dirmonitor_check_thread(void* data) { struct dirmonitor* monitor = data; while (monitor->length >= 0) { if (monitor->length == 0) { int result = get_changes_dirmonitor(monitor->internal, monitor->buffer, sizeof(monitor->buffer)); SDL_LockMutex(monitor->mutex); if (monitor->length == 0) monitor->length = result; SDL_UnlockMutex(monitor->mutex); } SDL_Delay(1); SDL_Event event = { .type = DIR_EVENT_TYPE }; SDL_PushEvent(&event); } return 0; } static int f_dirmonitor_new(lua_State* L) { if (DIR_EVENT_TYPE == 0) DIR_EVENT_TYPE = SDL_RegisterEvents(1); struct dirmonitor* monitor = lua_newuserdata(L, sizeof(struct dirmonitor)); luaL_setmetatable(L, API_TYPE_DIRMONITOR); memset(monitor, 0, sizeof(struct dirmonitor)); monitor->internal = init_dirmonitor(); return 1; } static int f_dirmonitor_gc(lua_State* L) { struct dirmonitor* monitor = luaL_checkudata(L, 1, API_TYPE_DIRMONITOR); SDL_LockMutex(monitor->mutex); monitor->length = -1; deinit_dirmonitor(monitor->internal); SDL_UnlockMutex(monitor->mutex); SDL_WaitThread(monitor->thread, NULL); free(monitor->internal); SDL_DestroyMutex(monitor->mutex); return 0; } static int f_dirmonitor_watch(lua_State *L) { struct dirmonitor* monitor = luaL_checkudata(L, 1, API_TYPE_DIRMONITOR); lua_pushnumber(L, add_dirmonitor(monitor->internal, luaL_checkstring(L, 2))); if (!monitor->thread) monitor->thread = SDL_CreateThread(dirmonitor_check_thread, "dirmonitor_check_thread", monitor); return 1; } static int f_dirmonitor_unwatch(lua_State *L) { remove_dirmonitor(((struct dirmonitor*)luaL_checkudata(L, 1, API_TYPE_DIRMONITOR))->internal, lua_tonumber(L, 2)); return 0; } static int f_dirmonitor_check(lua_State* L) { struct dirmonitor* monitor = luaL_checkudata(L, 1, API_TYPE_DIRMONITOR); SDL_LockMutex(monitor->mutex); if (monitor->length < 0) lua_pushnil(L); else if (monitor->length > 0) { if (translate_changes_dirmonitor(monitor->internal, monitor->buffer, monitor->length, f_check_dir_callback, L) == 0) monitor->length = 0; lua_pushboolean(L, 1); } else lua_pushboolean(L, 0); SDL_UnlockMutex(monitor->mutex); 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 }, {NULL, NULL} }; int luaopen_dirmonitor(lua_State* L) { luaL_newmetatable(L, API_TYPE_DIRMONITOR); luaL_setfuncs(L, dirmonitor_lib, 0); lua_pushvalue(L, -1); lua_setfield(L, -2, "__index"); return 1; }