From 4587ba72662467cb2b7429a9596656e7bda776c9 Mon Sep 17 00:00:00 2001 From: asrael Date: Wed, 3 Dec 2025 09:41:33 -0600 Subject: [PATCH] improve logging from scripts --- src/lua/pxl8/core.lua | 35 +++++++++++++--------- src/pxl8.c | 11 ++++--- src/pxl8_log.c | 10 +++++++ src/pxl8_repl.c | 33 ++++++++++++++------- src/pxl8_repl.h | 1 + src/pxl8_script.c | 67 ++++++++++++++++++++++--------------------- src/pxl8_tilemap.h | 2 +- 7 files changed, 99 insertions(+), 60 deletions(-) diff --git a/src/lua/pxl8/core.lua b/src/lua/pxl8/core.lua index 1ca9d9e..a629765 100644 --- a/src/lua/pxl8/core.lua +++ b/src/lua/pxl8/core.lua @@ -21,25 +21,34 @@ function core.get_height() return C.pxl8_gfx_get_height(core.gfx) end -function core.info(msg) - C.pxl8_lua_info(msg) +local function is_user_script(info) + local src = info and info.short_src + return src and (src:match("%.fnl$") or src:match("%.lua$")) end -function core.warn(msg) - C.pxl8_lua_warn(msg) +local function get_caller_info() + for level = 2, 10 do + local info = debug.getinfo(level, "Sl") + if not info then break end + if is_user_script(info) then + return info.short_src, info.currentline or 0 + end + end + return "?", 0 end -function core.error(msg) - C.pxl8_lua_error(msg) +local function make_logger(level) + return function(msg) + local src, line = get_caller_info() + C.pxl8_lua_log(level, src, line, msg) + end end -function core.debug(msg) - C.pxl8_lua_debug(msg) -end - -function core.trace(msg) - C.pxl8_lua_trace(msg) -end +core.info = make_logger(0) +core.warn = make_logger(1) +core.error = make_logger(2) +core.debug = make_logger(3) +core.trace = make_logger(4) function core.quit() C.pxl8_set_running(core.sys, false) diff --git a/src/pxl8.c b/src/pxl8.c index a7e0259..e2fec1d 100644 --- a/src/pxl8.c +++ b/src/pxl8.c @@ -263,12 +263,15 @@ pxl8_result pxl8_update(pxl8* sys) { pxl8_result result = pxl8_script_eval_repl(game->script, pxl8_repl_command_buffer(cmd)); if (result != PXL8_OK) { if (pxl8_script_is_incomplete_input(game->script)) { + pxl8_repl_signal_complete(sys->repl); } else { pxl8_error("%s", pxl8_script_get_last_error(game->script)); pxl8_repl_clear_accumulator(sys->repl); + pxl8_repl_signal_complete(sys->repl); } } else { pxl8_repl_clear_accumulator(sys->repl); + pxl8_repl_signal_complete(sys->repl); } } } @@ -339,10 +342,6 @@ void pxl8_quit(pxl8* sys) { pxl8_gfx_destroy(game->gfx); pxl8_script_destroy(game->script); - - if (sys->repl) { - pxl8_repl_destroy(sys->repl); - } } bool pxl8_is_running(const pxl8* sys) { @@ -352,6 +351,10 @@ bool pxl8_is_running(const pxl8* sys) { void pxl8_set_running(pxl8* sys, bool running) { if (sys && sys->game) { sys->game->running = running; + if (!running && sys->repl) { + pxl8_repl_destroy(sys->repl); + sys->repl = NULL; + } } } diff --git a/src/pxl8_log.c b/src/pxl8_log.c index 7611f4f..d897000 100644 --- a/src/pxl8_log.c +++ b/src/pxl8_log.c @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -18,6 +19,15 @@ static pxl8_log* g_log = NULL; void pxl8_log_init(pxl8_log* log) { g_log = log; g_log->level = PXL8_LOG_LEVEL_DEBUG; + + const char* env_level = getenv("PXL8_LOG_LEVEL"); + if (env_level) { + if (strcmp(env_level, "trace") == 0) g_log->level = PXL8_LOG_LEVEL_TRACE; + else if (strcmp(env_level, "debug") == 0) g_log->level = PXL8_LOG_LEVEL_DEBUG; + else if (strcmp(env_level, "info") == 0) g_log->level = PXL8_LOG_LEVEL_INFO; + else if (strcmp(env_level, "warn") == 0) g_log->level = PXL8_LOG_LEVEL_WARN; + else if (strcmp(env_level, "error") == 0) g_log->level = PXL8_LOG_LEVEL_ERROR; + } } void pxl8_log_set_level(pxl8_log_level level) { diff --git a/src/pxl8_repl.c b/src/pxl8_repl.c index 75ace26..5c0d3e0 100644 --- a/src/pxl8_repl.c +++ b/src/pxl8_repl.c @@ -22,6 +22,7 @@ struct pxl8_repl { char commands[PXL8_REPL_QUEUE_SIZE][PXL8_MAX_REPL_COMMAND_SIZE]; atomic_uint cmd_write_idx; atomic_uint cmd_read_idx; + atomic_bool cmd_complete; char logs[PXL8_REPL_QUEUE_SIZE][PXL8_MAX_REPL_COMMAND_SIZE]; atomic_uint log_write_idx; @@ -113,7 +114,6 @@ static void* pxl8_repl_thread(void* arg) { struct linenoiseState ls; char input_buf[PXL8_MAX_REPL_COMMAND_SIZE]; bool editing = false; - bool stopped_for_logs = false; struct pollfd pfd = { .fd = STDIN_FILENO, @@ -125,28 +125,23 @@ static void* pxl8_repl_thread(void* arg) { u32 log_write_idx = atomic_load(&repl->log_write_idx); if (log_read_idx != log_write_idx) { + printf("\r\033[K"); if (editing) { linenoiseEditStop(&ls); editing = false; - stopped_for_logs = true; + printf("\033[A\r\033[K"); } while (log_read_idx != log_write_idx) { printf("%s", repl->logs[log_read_idx]); - fflush(stdout); atomic_store(&repl->log_read_idx, (log_read_idx + 1) % PXL8_REPL_QUEUE_SIZE); log_read_idx = atomic_load(&repl->log_read_idx); log_write_idx = atomic_load(&repl->log_write_idx); } + fflush(stdout); continue; } if (!editing && !atomic_load(&repl->should_quit)) { - if (stopped_for_logs) { - struct timespec ts = {.tv_sec = 0, .tv_nsec = 5000000}; - nanosleep(&ts, NULL); - stopped_for_logs = false; - continue; - } const char* prompt = (repl->accumulator[0] != '\0') ? ".. " : ">> "; if (linenoiseEditStart(&ls, STDIN_FILENO, STDOUT_FILENO, input_buf, sizeof(input_buf), prompt) == -1) { atomic_store(&repl->should_quit, true); @@ -197,10 +192,22 @@ static void* pxl8_repl_thread(void* arg) { linenoiseFree(line); - while (atomic_load(&repl->cmd_write_idx) != atomic_load(&repl->cmd_read_idx)) { + while (!atomic_load(&repl->should_quit) && + (atomic_load(&repl->cmd_write_idx) != atomic_load(&repl->cmd_read_idx) || + !atomic_load(&repl->cmd_complete))) { + u32 lr = atomic_load(&repl->log_read_idx); + u32 lw = atomic_load(&repl->log_write_idx); + while (lr != lw) { + printf("%s", repl->logs[lr]); + atomic_store(&repl->log_read_idx, (lr + 1) % PXL8_REPL_QUEUE_SIZE); + lr = atomic_load(&repl->log_read_idx); + lw = atomic_load(&repl->log_write_idx); + } + fflush(stdout); struct timespec ts = {.tv_sec = 0, .tv_nsec = 1000000}; nanosleep(&ts, NULL); } + atomic_store(&repl->cmd_complete, false); } if (editing) linenoiseEditStop(&ls); @@ -217,6 +224,7 @@ pxl8_repl* pxl8_repl_create(void) { repl->accumulator[0] = '\0'; atomic_store(&repl->cmd_write_idx, 0); atomic_store(&repl->cmd_read_idx, 0); + atomic_store(&repl->cmd_complete, true); atomic_store(&repl->log_write_idx, 0); atomic_store(&repl->log_read_idx, 0); atomic_store(&repl->should_quit, false); @@ -301,3 +309,8 @@ void pxl8_repl_clear_accumulator(pxl8_repl* repl) { if (!repl) return; repl->accumulator[0] = '\0'; } + +void pxl8_repl_signal_complete(pxl8_repl* repl) { + if (!repl) return; + atomic_store(&repl->cmd_complete, true); +} diff --git a/src/pxl8_repl.h b/src/pxl8_repl.h index ee815e2..53a5d6f 100644 --- a/src/pxl8_repl.h +++ b/src/pxl8_repl.h @@ -17,6 +17,7 @@ const char* pxl8_repl_command_buffer(pxl8_repl_command* cmd); bool pxl8_repl_push_log(const char* message); void pxl8_repl_clear_accumulator(pxl8_repl* repl); bool pxl8_repl_should_quit(pxl8_repl* repl); +void pxl8_repl_signal_complete(pxl8_repl* repl); #ifdef __cplusplus } diff --git a/src/pxl8_script.c b/src/pxl8_script.c index 94b2df1..28be5ed 100644 --- a/src/pxl8_script.c +++ b/src/pxl8_script.c @@ -25,6 +25,7 @@ struct pxl8_script { char main_path[PXL8_MAX_PATH]; char watch_dir[PXL8_MAX_PATH]; time_t latest_mod_time; + int repl_env_ref; bool repl_mode; }; @@ -210,12 +211,7 @@ static const char* pxl8_ffi_cdefs = "void pxl8_set_cursor(pxl8* sys, pxl8_cursor cursor);\n" "void pxl8_set_relative_mouse_mode(pxl8* sys, bool enabled);\n" "void pxl8_set_running(pxl8* sys, bool running);\n" -"void pxl8_lua_debug(const char* msg);\n" -"void pxl8_lua_error(const char* msg);\n" -"void pxl8_lua_info(const char* msg);\n" -"void pxl8_lua_trace(const char* msg);\n" -"void pxl8_lua_warn(const char* msg);\n" -"\n" +"void pxl8_lua_log(int level, const char* file, int line, const char* msg);\n" "typedef u32 pxl8_tile;\n" "typedef struct pxl8_tilemap pxl8_tilemap;\n" "typedef struct pxl8_tilesheet pxl8_tilesheet;\n" @@ -423,24 +419,15 @@ static const char* pxl8_ffi_cdefs = "i32 pxl8_save_delete(pxl8_save* save, u8 slot);\n" "const char* pxl8_save_get_directory(pxl8_save* save);\n"; -void pxl8_lua_info(const char* msg) { - pxl8_info("%s", msg); -} - -void pxl8_lua_warn(const char* msg) { - pxl8_warn("%s", msg); -} - -void pxl8_lua_error(const char* msg) { - pxl8_error("%s", msg); -} - -void pxl8_lua_debug(const char* msg) { - pxl8_debug("%s", msg); -} - -void pxl8_lua_trace(const char* msg) { - pxl8_trace("%s", msg); +void pxl8_lua_log(int level, const char* file, int line, const char* msg) { + if (file && (file[0] == '?' || file[0] == '\0')) file = NULL; + switch (level) { + case 0: pxl8_log_write_info("%s", msg); break; + case 1: pxl8_log_write_warn(file, line, "%s", msg); break; + case 2: pxl8_log_write_error(file, line, "%s", msg); break; + case 3: pxl8_log_write_debug(file, line, "%s", msg); break; + case 4: pxl8_log_write_trace(file, line, "%s", msg); break; + } } static void pxl8_script_set_error(pxl8_script* script, const char* error) { @@ -526,15 +513,11 @@ pxl8_script* pxl8_script_create(bool repl_mode) { lua_getglobal(script->L, "fennel"); lua_getfield(script->L, -1, "install"); if (lua_isfunction(script->L, -1)) { - // Pass options table for fennel.install() - // lambdaAsFn: removes arity checking overhead from fn/lambda - // correlate: aligns Lua line numbers with Fennel source - // useMetadata: enables docstrings (only in REPL mode for perf) lua_newtable(script->L); lua_pushboolean(script->L, 1); - lua_setfield(script->L, -2, "lambdaAsFn"); - lua_pushboolean(script->L, 1); lua_setfield(script->L, -2, "correlate"); + lua_pushboolean(script->L, 1); + lua_setfield(script->L, -2, "lambdaAsFn"); if (script->repl_mode) { lua_pushboolean(script->L, 1); lua_setfield(script->L, -2, "useMetadata"); @@ -573,12 +556,26 @@ pxl8_script* pxl8_script_create(bool repl_mode) { } script->last_error[0] = '\0'; + script->repl_env_ref = LUA_NOREF; + + if (script->repl_mode) { + lua_newtable(script->L); + lua_newtable(script->L); + lua_getglobal(script->L, "_G"); + lua_setfield(script->L, -2, "__index"); + lua_setmetatable(script->L, -2); + script->repl_env_ref = luaL_ref(script->L, LUA_REGISTRYINDEX); + } + return script; } void pxl8_script_destroy(pxl8_script* script) { if (!script) return; if (script->L) { + if (script->repl_env_ref != LUA_NOREF) { + luaL_unref(script->L, LUA_REGISTRYINDEX, script->repl_env_ref); + } lua_close(script->L); } free(script); @@ -771,9 +768,15 @@ static pxl8_result pxl8_script_eval_internal(pxl8_script* script, const char* co lua_pushstring(script->L, code); lua_newtable(script->L); - lua_pushstring(script->L, "useMetadata"); lua_pushboolean(script->L, true); - lua_settable(script->L, -3); + lua_setfield(script->L, -2, "useMetadata"); + + if (repl_mode && script->repl_env_ref != LUA_NOREF) { + lua_rawgeti(script->L, LUA_REGISTRYINDEX, script->repl_env_ref); + lua_setfield(script->L, -2, "env"); + lua_pushboolean(script->L, false); + lua_setfield(script->L, -2, "allowedGlobals"); + } if (lua_pcall(script->L, 2, 1, 0) != 0) { const char* error = lua_tostring(script->L, -1); diff --git a/src/pxl8_tilemap.h b/src/pxl8_tilemap.h index ca7962c..e96bcd3 100644 --- a/src/pxl8_tilemap.h +++ b/src/pxl8_tilemap.h @@ -4,7 +4,7 @@ #include "pxl8_tilesheet.h" #include "pxl8_types.h" -#define PXL8_TILE_SIZE 16 +#define PXL8_TILE_SIZE 8 #define PXL8_MAX_TILEMAP_WIDTH 256 #define PXL8_MAX_TILEMAP_HEIGHT 256 #define PXL8_MAX_TILE_LAYERS 4