1 | #include "api.h" |
2 | #include <SDL.h> |
3 | #include <stdlib.h> |
4 | #include <string.h> |
5 | #include <stdbool.h> |
6 | |
7 | static unsigned int DIR_EVENT_TYPE = 0; |
8 | |
9 | struct dirmonitor { |
10 | SDL_Thread* thread; |
11 | SDL_mutex* mutex; |
12 | char buffer[64512]; |
13 | volatile int length; |
14 | struct dirmonitor_internal* internal; |
15 | }; |
16 | |
17 | |
18 | struct dirmonitor_internal* init_dirmonitor(); |
19 | void deinit_dirmonitor(struct dirmonitor_internal*); |
20 | int get_changes_dirmonitor(struct dirmonitor_internal*, char*, int); |
21 | int translate_changes_dirmonitor(struct dirmonitor_internal*, char*, int, int (*)(int, const char*, void*), void*); |
22 | int add_dirmonitor(struct dirmonitor_internal*, const char*); |
23 | void remove_dirmonitor(struct dirmonitor_internal*, int); |
24 | int get_mode_dirmonitor(); |
25 | |
26 | |
27 | static int f_check_dir_callback(int watch_id, const char* path, void* L) { |
28 | lua_pushvalue(L, -1); |
29 | if (path) |
30 | lua_pushlstring(L, path, watch_id); |
31 | else |
32 | lua_pushnumber(L, watch_id); |
33 | lua_call(L, 1, 1); |
34 | int result = lua_toboolean(L, -1); |
35 | lua_pop(L, 1); |
36 | return !result; |
37 | } |
38 | |
39 | |
40 | static int dirmonitor_check_thread(void* data) { |
41 | struct dirmonitor* monitor = data; |
42 | while (monitor->length >= 0) { |
43 | if (monitor->length == 0) { |
44 | int result = get_changes_dirmonitor(monitor->internal, monitor->buffer, sizeof(monitor->buffer)); |
45 | SDL_LockMutex(monitor->mutex); |
46 | if (monitor->length == 0) |
47 | monitor->length = result; |
48 | SDL_UnlockMutex(monitor->mutex); |
49 | } |
50 | SDL_Delay(1); |
51 | SDL_Event event = { .type = DIR_EVENT_TYPE }; |
52 | SDL_PushEvent(&event); |
53 | } |
54 | return 0; |
55 | } |
56 | |
57 | |
58 | static int f_dirmonitor_new(lua_State* L) { |
59 | if (DIR_EVENT_TYPE == 0) |
60 | DIR_EVENT_TYPE = SDL_RegisterEvents(1); |
61 | struct dirmonitor* monitor = lua_newuserdata(L, sizeof(struct dirmonitor)); |
62 | luaL_setmetatable(L, API_TYPE_DIRMONITOR); |
63 | memset(monitor, 0, sizeof(struct dirmonitor)); |
64 | monitor->mutex = SDL_CreateMutex(); |
65 | monitor->internal = init_dirmonitor(); |
66 | return 1; |
67 | } |
68 | |
69 | |
70 | static int f_dirmonitor_gc(lua_State* L) { |
71 | struct dirmonitor* monitor = luaL_checkudata(L, 1, API_TYPE_DIRMONITOR); |
72 | SDL_LockMutex(monitor->mutex); |
73 | monitor->length = -1; |
74 | deinit_dirmonitor(monitor->internal); |
75 | SDL_UnlockMutex(monitor->mutex); |
76 | SDL_WaitThread(monitor->thread, NULL); |
77 | free(monitor->internal); |
78 | SDL_DestroyMutex(monitor->mutex); |
79 | return 0; |
80 | } |
81 | |
82 | |
83 | static int f_dirmonitor_watch(lua_State *L) { |
84 | struct dirmonitor* monitor = luaL_checkudata(L, 1, API_TYPE_DIRMONITOR); |
85 | lua_pushnumber(L, add_dirmonitor(monitor->internal, luaL_checkstring(L, 2))); |
86 | if (!monitor->thread) |
87 | monitor->thread = SDL_CreateThread(dirmonitor_check_thread, "dirmonitor_check_thread" , monitor); |
88 | return 1; |
89 | } |
90 | |
91 | |
92 | static int f_dirmonitor_unwatch(lua_State *L) { |
93 | remove_dirmonitor(((struct dirmonitor*)luaL_checkudata(L, 1, API_TYPE_DIRMONITOR))->internal, lua_tonumber(L, 2)); |
94 | return 0; |
95 | } |
96 | |
97 | |
98 | static int f_dirmonitor_check(lua_State* L) { |
99 | struct dirmonitor* monitor = luaL_checkudata(L, 1, API_TYPE_DIRMONITOR); |
100 | SDL_LockMutex(monitor->mutex); |
101 | if (monitor->length < 0) |
102 | lua_pushnil(L); |
103 | else if (monitor->length > 0) { |
104 | if (translate_changes_dirmonitor(monitor->internal, monitor->buffer, monitor->length, f_check_dir_callback, L) == 0) |
105 | monitor->length = 0; |
106 | lua_pushboolean(L, 1); |
107 | } else |
108 | lua_pushboolean(L, 0); |
109 | SDL_UnlockMutex(monitor->mutex); |
110 | return 1; |
111 | } |
112 | |
113 | |
114 | static int f_dirmonitor_mode(lua_State* L) { |
115 | int mode = get_mode_dirmonitor(); |
116 | if (mode == 1) |
117 | lua_pushstring(L, "single" ); |
118 | else |
119 | lua_pushstring(L, "multiple" ); |
120 | return 1; |
121 | } |
122 | |
123 | |
124 | static const luaL_Reg dirmonitor_lib[] = { |
125 | { "new" , f_dirmonitor_new }, |
126 | { "__gc" , f_dirmonitor_gc }, |
127 | { "watch" , f_dirmonitor_watch }, |
128 | { "unwatch" , f_dirmonitor_unwatch }, |
129 | { "check" , f_dirmonitor_check }, |
130 | { "mode" , f_dirmonitor_mode }, |
131 | {NULL, NULL} |
132 | }; |
133 | |
134 | |
135 | int luaopen_dirmonitor(lua_State* L) { |
136 | luaL_newmetatable(L, API_TYPE_DIRMONITOR); |
137 | luaL_setfuncs(L, dirmonitor_lib, 0); |
138 | lua_pushvalue(L, -1); |
139 | lua_setfield(L, -2, "__index" ); |
140 | return 1; |
141 | } |
142 | |