1
0
Fork 0
mirror of https://github.com/bjornbytes/lovr.git synced 2024-07-05 13:53:38 +00:00

Live reloading;

Use -w or --watch to watch source directory for changes and call
lovr.filechanged when a file is changed.

By default lovr.filechanged will restart lovr.

It's powered by lovr.filesystem.watch/lovr.filesystem.unwatch functions,
but these are currently hardcoded to only watch the whole source folder.

Currently there is a bug where it only restarts on the first change,
this is seemingly a problem with dmon.
This commit is contained in:
bjorn 2023-12-02 19:57:58 -08:00
parent 52488725b8
commit 9301c73f14
13 changed files with 1870 additions and 9 deletions

View file

@ -421,6 +421,7 @@ if(LOVR_ENABLE_FILESYSTEM)
src/modules/filesystem/filesystem.c
src/api/l_filesystem.c
src/api/l_filesystem_file.c
src/lib/dmon/dmon.c
src/lib/miniz/miniz_tinfl.c
)
else()

View file

@ -393,6 +393,7 @@ src += 'src/lib/miniz/*.c'
src += (config.modules.audio or config.modules.data) and 'src/lib/miniaudio/*.c' or nil
src += config.modules.data and 'src/lib/jsmn/*.c' or nil
src += config.modules.data and 'src/lib/minimp3/*.c' or nil
src += config.modules.filesystem and 'src/lib/dmon/*.c' or nil
src += config.modules.math and 'src/lib/noise/*.c' or nil
src += config.modules.thread and 'src/core/job.c' or nil

View file

@ -282,6 +282,10 @@ function lovr.threaderror(thread, err)
error('Thread error\n\n' .. err, 0)
end
function lovr.filechanged(path, action, oldpath)
lovr.event.restart()
end
function lovr.log(message, level, tag)
message = message:gsub('\n$', '')
print(message)

View file

@ -3,7 +3,8 @@ function lovr.arg(arg)
_help = { short = '-h', long = '--help', help = 'Show help and exit' },
_version = { short = '-v', long = '--version', help = 'Show version and exit' },
console = { long = '--console', help = 'Attach Windows console' },
debug = { long = '--debug', help = 'Enable debugging checks and logging' }
debug = { long = '--debug', help = 'Enable debugging checks and logging' },
watch = { short = '-w', long = '--watch', help = 'Watch files and restart on change' }
}
local shift
@ -68,5 +69,9 @@ function lovr.arg(arg)
if arg.debug then
conf.graphics.debug = true
end
if arg.watch then
lovr.filesystem.watch()
end
end
end

View file

@ -38,6 +38,7 @@ extern StringEntry lovrDrawMode[];
extern StringEntry lovrDrawStyle[];
extern StringEntry lovrEffect[];
extern StringEntry lovrEventType[];
extern StringEntry lovrFileAction[];
extern StringEntry lovrFilterMode[];
extern StringEntry lovrHeadsetDriver[];
extern StringEntry lovrHorizontalAlign[];

View file

@ -22,6 +22,7 @@ StringEntry lovrEventType[] = {
#ifndef LOVR_DISABLE_THREAD
[EVENT_THREAD_ERROR] = ENTRY("threaderror"),
#endif
[EVENT_FILECHANGED] = ENTRY("filechanged"),
[EVENT_PERMISSION] = ENTRY("permission"),
{ 0 }
};
@ -207,6 +208,14 @@ static int nextEvent(lua_State* L) {
return 3;
#endif
case EVENT_FILECHANGED:
lua_pushstring(L, event.data.file.path);
luax_pushenum(L, FileAction, event.data.file.action);
lua_pushstring(L, event.data.file.oldpath);
free(event.data.file.path);
free(event.data.file.oldpath);
return 4;
case EVENT_PERMISSION:
luax_pushenum(L, Permission, event.data.permission.permission);
lua_pushboolean(L, event.data.permission.granted);

View file

@ -5,6 +5,14 @@
#include <stdlib.h>
#include <string.h>
StringEntry lovrFileAction[] = {
[FILE_CREATE] = ENTRY("create"),
[FILE_DELETE] = ENTRY("delete"),
[FILE_MODIFY] = ENTRY("modify"),
[FILE_RENAME] = ENTRY("rename"),
{ 0 }
};
StringEntry lovrOpenMode[] = {
[OPEN_READ] = ENTRY("r"),
[OPEN_WRITE] = ENTRY("w"),
@ -349,6 +357,16 @@ static int l_lovrFilesystemUnmount(lua_State* L) {
return 1;
}
static int l_lovrFilesystemUnwatch(lua_State* L) {
lovrFilesystemUnwatch();
return 0;
}
static int l_lovrFilesystemWatch(lua_State* L) {
lovrFilesystemWatch();
return 0;
}
static int l_lovrFilesystemWrite(lua_State* L) {
const char* path = luaL_checkstring(L, 1);
size_t size;
@ -411,6 +429,8 @@ static const luaL_Reg lovrFilesystem[] = {
{ "setRequirePath", l_lovrFilesystemSetRequirePath },
{ "setSource", l_lovrFilesystemSetSource },
{ "unmount", l_lovrFilesystemUnmount },
{ "unwatch", l_lovrFilesystemUnwatch },
{ "watch", l_lovrFilesystemWatch },
{ "write", l_lovrFilesystemWrite },
{ "newFile", l_lovrFilesystemNewFile },
{ NULL, NULL }

2
src/lib/dmon/dmon.c Normal file
View file

@ -0,0 +1,2 @@
#define DMON_IMPL
#include "dmon.h"

1748
src/lib/dmon/dmon.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -51,13 +51,26 @@ void lovrEventPush(Event event) {
if (event.type == EVENT_THREAD_ERROR) {
lovrRetain(event.data.thread.thread);
size_t length = strlen(event.data.thread.error);
char* copy = lovrMalloc(length + 1);
memcpy(copy, event.data.thread.error, length);
copy[length] = '\0';
char* copy = malloc(length + 1);
memcpy(copy, event.data.thread.error, length + 1);
event.data.thread.error = copy;
}
#endif
if (event.type == EVENT_FILECHANGED) {
size_t length = strlen(event.data.file.path);
char* copy = malloc(length + 1);
memcpy(copy, event.data.file.path, length + 1);
event.data.file.path = copy;
if (event.data.file.oldpath) {
length = strlen(event.data.file.oldpath);
copy = malloc(length + 1);
memcpy(copy, event.data.file.oldpath, length + 1);
event.data.file.oldpath = copy;
}
}
arr_push(&state.events, event);
}

View file

@ -25,6 +25,7 @@ typedef enum {
#ifndef LOVR_DISABLE_THREAD
EVENT_THREAD_ERROR,
#endif
EVENT_FILECHANGED,
EVENT_PERMISSION,
EVENT_CUSTOM
} EventType;
@ -115,16 +116,22 @@ typedef struct {
} ThreadEvent;
typedef struct {
char name[MAX_EVENT_NAME_LENGTH];
Variant data[4];
uint32_t count;
} CustomEvent;
char* path;
char* oldpath;
int action;
} FileEvent;
typedef struct {
uint32_t permission;
bool granted;
} PermissionEvent;
typedef struct {
char name[MAX_EVENT_NAME_LENGTH];
Variant data[4];
uint32_t count;
} CustomEvent;
typedef union {
QuitEvent quit;
BoolEvent boolean;
@ -134,8 +141,9 @@ typedef union {
MouseEvent mouse;
MouseWheelEvent wheel;
ThreadEvent thread;
CustomEvent custom;
FileEvent file;
PermissionEvent permission;
CustomEvent custom;
} EventData;
typedef struct {

View file

@ -1,7 +1,9 @@
#include "filesystem/filesystem.h"
#include "event/event.h"
#include "core/fs.h"
#include "core/os.h"
#include "util.h"
#include "lib/dmon/dmon.h"
#include "lib/miniz/miniz_tinfl.h"
#include <stdatomic.h>
#include <string.h>
@ -75,6 +77,8 @@ struct File {
static struct {
uint32_t ref;
bool watching;
dmon_watch_id watchId;
Archive* archives;
size_t savePathLength;
char savePath[1024];
@ -175,6 +179,7 @@ void lovrFilesystemDestroy(void) {
lovrRelease(archive, lovrArchiveDestroy);
archive = next;
}
lovrFilesystemUnwatch();
memset(&state, 0, sizeof(state));
}
@ -190,6 +195,41 @@ const char* lovrFilesystemGetSource(void) {
return state.source[0] ? state.source : NULL;
}
static void onFileEvent(dmon_watch_id id, dmon_action action, const char* dir, const char* path, const char* oldpath, void* ctx) {
static const FileAction map[] = {
[DMON_ACTION_CREATE] = FILE_CREATE,
[DMON_ACTION_DELETE] = FILE_DELETE,
[DMON_ACTION_MODIFY] = FILE_MODIFY,
[DMON_ACTION_MOVE] = FILE_RENAME
};
lovrEventPush((Event) {
.type = EVENT_FILECHANGED,
.data.file = {
.path = (char*) path,
.action = map[action],
.oldpath = (char*) oldpath
}
});
}
void lovrFilesystemWatch(void) {
FileInfo info;
if (!state.watching && fs_stat(state.source, &info) && info.type == FILE_DIRECTORY) {
state.watching = true;
dmon_init();
state.watchId = dmon_watch(state.source, onFileEvent, DMON_WATCHFLAGS_RECURSIVE, NULL);
}
}
void lovrFilesystemUnwatch(void) {
if (state.watching) {
state.watching = false;
dmon_unwatch(state.watchId);
dmon_deinit();
}
}
bool lovrFilesystemIsFused(void) {
char path[LOVR_PATH_MAX];
const char* root;

View file

@ -9,11 +9,20 @@
typedef struct Archive Archive;
typedef struct File File;
typedef enum {
FILE_CREATE,
FILE_DELETE,
FILE_MODIFY,
FILE_RENAME
} FileAction;
bool lovrFilesystemInit(void);
void lovrFilesystemDestroy(void);
void lovrFilesystemSetSource(const char* source);
const char* lovrFilesystemGetSource(void);
bool lovrFilesystemIsFused(void);
void lovrFilesystemWatch(void);
void lovrFilesystemUnwatch(void);
bool lovrFilesystemMount(const char* path, const char* mountpoint, bool append, const char *root);
bool lovrFilesystemUnmount(const char* path);
const char* lovrFilesystemGetRealDirectory(const char* path);