Adding folder_monitor (config)

* Automatically refresh the window when any file in the root folder gets changed.
This commit is contained in:
Hassan DRAGA 2024-06-25 22:40:25 -04:00
parent 88cd5f3774
commit b576b2f9de
2 changed files with 183 additions and 3 deletions

View File

@ -83,6 +83,7 @@
#include <signal.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/inotify.h>
#include <unistd.h>
#define WEBUI_GET_CURRENT_DIR getcwd
#define WEBUI_FILE_EXIST access
@ -103,6 +104,8 @@
#include <sys/sysctl.h>
#include <sys/syslimits.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/event.h>
#include <unistd.h>
#define WEBUI_GET_CURRENT_DIR getcwd
#define WEBUI_FILE_EXIST access
@ -157,6 +160,11 @@ typedef enum {
//
// Default: False
ui_event_blocking,
// Automatically refresh the window UI when any file in the
// root folder gets changed.
//
// Default: False
folder_monitor,
} webui_config;
// -- Structs -------------------------
@ -617,7 +625,7 @@ WEBUI_EXPORT bool webui_set_port(size_t window, size_t port);
* @param option The desired option from `webui_config` enum
* @param status The status of the option, `true` or `false`
*
* @example webui_config(show_wait_connection, false);
* @example webui_set_config(show_wait_connection, false);
*/
WEBUI_EXPORT void webui_set_config(webui_config option, bool status);

View File

@ -344,6 +344,7 @@ typedef struct _webui_core_t {
bool show_wait_connection;
bool show_auto_js_inject;
bool ws_block;
bool folder_monitor;
} config;
volatile size_t servers;
char* html_elements[WEBUI_MAX_IDS];
@ -421,6 +422,12 @@ typedef struct _webui_recv_arg_t {
}
_webui_recv_arg_t;
typedef struct _webui_monitor_arg_t {
size_t win_num;
const char *path;
}
_webui_monitor_arg_t;
typedef struct _webui_cmd_async_t {
_webui_window_t * win;
char* cmd;
@ -439,6 +446,7 @@ static bool _webui_str_to_wide(const char *s, wchar_t **w);
#define WEBUI_THREAD_SERVER_START DWORD WINAPI _webui_server_thread(LPVOID arg)
#define WEBUI_THREAD_RECEIVE DWORD WINAPI _webui_process_thread(LPVOID _arg)
#define WEBUI_THREAD_WEBVIEW DWORD WINAPI _webui_webview_thread(LPVOID arg)
#define WEBUI_THREAD_MONITOR DWORD WINAPI _webui_folder_monitor_thread(LPVOID arg)
#define WEBUI_THREAD_RETURN return 0;
#else
static const char* webui_sep = "/";
@ -446,6 +454,7 @@ static void * _webui_run_browser_task(void * _arg);
#define WEBUI_THREAD_SERVER_START void * _webui_server_thread(void * arg)
#define WEBUI_THREAD_RECEIVE void * _webui_process_thread(void * _arg)
#define WEBUI_THREAD_WEBVIEW void * _webui_webview_thread(void * arg)
#define WEBUI_THREAD_MONITOR void * _webui_folder_monitor_thread(void * arg)
#define WEBUI_THREAD_RETURN pthread_exit(NULL);
#endif
static void _webui_init(void);
@ -576,6 +585,7 @@ static int _webui_http_log(const struct mg_connection * conn, const char* messag
static WEBUI_THREAD_SERVER_START;
static WEBUI_THREAD_RECEIVE;
static WEBUI_THREAD_WEBVIEW;
static WEBUI_THREAD_MONITOR;
// Safe C STD
#ifdef _WIN32
@ -2077,6 +2087,9 @@ void webui_set_config(webui_config option, bool status) {
case show_wait_connection:
_webui_core.config.show_wait_connection = status;
break;
case folder_monitor:
_webui_core.config.folder_monitor = status;
break;
case ui_event_blocking:
_webui_core.config.ws_block = status;
// Update all created windows
@ -7514,6 +7527,13 @@ static WEBUI_THREAD_SERVER_START {
printf("[Core]\t\t_webui_server_thread([%zu]) -> URL: [%s]\n", win->window_number, win->url);
#endif
// Folder monitor thread
#ifdef _WIN32
HANDLE monitor_thread = NULL;
#else
pthread_t monitor_thread = NULL;
#endif
// Initialization
_webui_core.servers++;
win->server_running = true;
@ -7672,6 +7692,21 @@ static WEBUI_THREAD_SERVER_START {
);
#endif
if (_webui_core.config.folder_monitor && !monitor_thread) {
// Folder monitor thread
_webui_monitor_arg_t* arg = (_webui_monitor_arg_t * ) _webui_malloc(sizeof(_webui_monitor_arg_t));
arg->win_num = win->window_number;
arg->path = win->server_root_path;
#ifdef _WIN32
monitor_thread = CreateThread(NULL, 0, _webui_folder_monitor_thread, (void*)arg, 0, NULL);
if (monitor_thread != NULL)
CloseHandle(monitor_thread);
#else
pthread_create(&monitor_thread, NULL, &_webui_folder_monitor_thread, (void*)win);
pthread_detach(monitor_thread);
#endif
}
while(!stop) {
// Wait forever for disconnection
@ -7780,6 +7815,15 @@ static WEBUI_THREAD_SERVER_START {
_webui_mutex_is_webview_update(win, WEBUI_MUTEX_TRUE);
}
// Clean monitor thread
#ifdef _WIN32
TerminateThread(monitor_thread, 0);
CloseHandle(monitor_thread);
#else
pthread_cancel(monitor_thread);
pthread_join(monitor_thread, NULL);
#endif
// Clean
win->server_running = false;
win->html_handled = false;
@ -8418,8 +8462,7 @@ static void _webui_process(_webui_window_t * win, void * ptr, size_t len, size_t
#ifdef WEBUI_LOG
printf(
"[Core]\t\t_webui_process(%zu) -> WebSocket "
"connected\n",
"[Core]\t\t_webui_process(%zu) -> WebSocket connected\n",
recvNum
);
#endif
@ -10208,6 +10251,135 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
}
#endif
static WEBUI_THREAD_MONITOR {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread()\n");
#endif
// Get arguments
_webui_monitor_arg_t* args = (_webui_monitor_arg_t*)arg;
#ifdef _WIN32
// Windows
HANDLE hDir = CreateFile(
args->path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL
);
if (hDir == INVALID_HANDLE_VALUE) {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Failed to open folder: %s\n", args->path);
#endif
WEBUI_THREAD_RETURN
}
char buffer[1024];
DWORD bytesReturned;
while (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) {
if (ReadDirectoryChangesW(
hDir, buffer, sizeof(buffer), TRUE,
FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE, &bytesReturned, NULL, NULL
))
{
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Folder updated\n");
#endif
webui_run(args->win_num, "location.reload();");
} else {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Failed to read folder changes\n");
#endif
break;
}
}
CloseHandle(hDir);
#elif __linux__
// Linux
int fd = inotify_init();
if (fd < 0) {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> inotify_init error\n");
#endif
WEBUI_THREAD_RETURN
}
int wd = inotify_add_watch(fd, args->path, IN_MODIFY | IN_CREATE | IN_DELETE);
if (wd < 0) {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> inotify_add_watch error\n");
#endif
close(fd);
WEBUI_THREAD_RETURN
}
char buffer[1024];
while (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) {
int length = read(fd, buffer, sizeof(buffer));
if (length < 0) {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> read error\n");
#endif
break;
}
int i = 0;
while (i < length) {
struct inotify_event *event = (struct inotify_event *)&buffer[i];
if (event->len) {
if (event->mask & (IN_CREATE | IN_DELETE | IN_MODIFY)) {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Folder updated\n");
#endif
webui_run(args->win_num, "location.reload();");
}
}
i += sizeof(struct inotify_event) + event->len;
}
}
inotify_rm_watch(fd, wd);
close(fd);
#else
// macOS
int kq = kqueue();
if (kq == -1) {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> kqueue error\n");
#endif
WEBUI_THREAD_RETURN
}
int fd = open(args->path, O_RDONLY);
if (fd == -1) {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> open error\n");
#endif
close(kq);
WEBUI_THREAD_RETURN
}
struct kevent change;
EV_SET(&change, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT, NOTE_WRITE, 0, NULL);
while (!_webui_mutex_is_exit_now(WEBUI_MUTEX_NONE)) {
struct kevent event;
int nev = kevent(kq, &change, 1, &event, 1, NULL);
if (nev == -1) {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> kevent error\n");
#endif
break;
} else if (nev > 0) {
if (event.fflags & NOTE_WRITE) {
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Folder updated\n");
#endif
webui_run(args->win_num, "location.reload();");
}
}
}
close(fd);
close(kq);
#endif
#ifdef WEBUI_LOG
printf("[Core]\t\t[Thread .] _webui_folder_monitor_thread() -> Exit\n");
#endif
WEBUI_THREAD_RETURN
}
static void _webui_bridge_api_handler(webui_event_t* e) {
#ifdef WEBUI_LOG