lite-xl/src/api/dirmonitor.c

142 lines
3.9 KiB
C

#include "api.h"
#include <SDL.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
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);
int get_mode_dirmonitor();
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->mutex = SDL_CreateMutex();
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 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}
};
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;
}