improve logging from scripts

This commit is contained in:
asrael 2025-12-03 09:41:33 -06:00
parent 3313c800a9
commit 4587ba7266
7 changed files with 99 additions and 60 deletions

View file

@ -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)

View file

@ -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;
}
}
}

View file

@ -3,6 +3,7 @@
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
@ -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) {

View file

@ -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);
}

View file

@ -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
}

View file

@ -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_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;
}
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);
}
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);

View file

@ -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