add proper fnl modules to demo

This commit is contained in:
asrael 2025-10-06 18:14:07 -05:00
parent 9bb9fa5f5b
commit 47c4f2045c
14 changed files with 510 additions and 240 deletions

View file

@ -107,19 +107,33 @@ function pxl8.trace(msg)
end
function pxl8.key_down(key)
if type(key) == "string" then
key = string.byte(key)
end
return C.pxl8_key_down(input, key)
end
function pxl8.key_pressed(key)
if type(key) == "string" then
key = string.byte(key)
end
return C.pxl8_key_pressed(input, key)
end
function pxl8.key_released(key)
return C.pxl8_key_released(input, key)
end
function pxl8.mouse_wheel_x()
return C.pxl8_mouse_wheel_x(input)
end
function pxl8.mouse_wheel_y()
return C.pxl8_mouse_wheel_y(input)
end
function pxl8.mouse_x()
return C.pxl8_mouse_x(input)
end
function pxl8.mouse_y()
return C.pxl8_mouse_y(input)
end
function pxl8.vfx_raster_bars(bars, time)
local c_bars = ffi.new("pxl8_raster_bar[?]", #bars)
for i, bar in ipairs(bars) do
@ -363,19 +377,20 @@ function pxl8.bounds(x, y, w, h)
return ffi.new("pxl8_bounds", {x = x, y = y, w = w, h = h})
end
function pxl8.ui_window_begin(title, x, y, w, h, options)
local rect = ffi.new("pxl8_bounds", {x = x, y = y, w = w, h = h})
return C.pxl8_ui_window_begin(_pxl8_ui, title, rect, options or 0)
end
function pxl8.ui_window_end()
C.pxl8_ui_window_end(_pxl8_ui)
end
function pxl8.ui_button(label)
return C.pxl8_ui_button(_pxl8_ui, label)
end
function pxl8.ui_checkbox(label, state)
local state_ptr = ffi.new("bool[1]", state)
local changed = C.pxl8_ui_checkbox(_pxl8_ui, label, state_ptr)
return changed, state_ptr[0]
end
function pxl8.ui_indent(amount)
C.pxl8_ui_indent(_pxl8_ui, amount)
end
function pxl8.ui_label(text)
C.pxl8_ui_label(_pxl8_ui, text)
end
@ -390,4 +405,17 @@ function pxl8.ui_layout_row(item_count, widths, height)
C.pxl8_ui_layout_row(_pxl8_ui, item_count, widths_array, height)
end
function pxl8.ui_window_begin(title, x, y, w, h, options)
local rect = ffi.new("pxl8_bounds", {x = x, y = y, w = w, h = h})
return C.pxl8_ui_window_begin(_pxl8_ui, title, rect, options or 0)
end
function pxl8.ui_window_end()
C.pxl8_ui_window_end(_pxl8_ui)
end
function pxl8.ui_window_set_open(title, open)
C.pxl8_ui_window_set_open(_pxl8_ui, title, open)
end
return pxl8

View file

@ -44,8 +44,6 @@ typedef struct pxl8_state {
pxl8_script* script;
pxl8_ui* ui;
f32 current_fps;
f32 fps_timer;
i32 frame_count;
u64 last_time;
f32 time;
@ -53,7 +51,6 @@ typedef struct pxl8_state {
bool repl_mode;
bool running;
bool script_loaded;
bool show_fps;
char script_path[256];
pxl8_input_state input;
@ -319,16 +316,8 @@ SDL_AppResult SDL_AppIterate(void* appstate) {
u64 current_time = SDL_GetTicksNS();
f32 dt = (f32)(current_time - app->last_time) / 1000000000.0f;
app->frame_count++;
app->fps_timer += dt;
app->last_time = current_time;
app->time += dt;
if (app->fps_timer >= 1.0f) {
app->current_fps = app->frame_count / app->fps_timer;
app->frame_count = 0;
app->fps_timer = 0.0f;
}
pxl8_script_check_reload(app->script);
@ -344,23 +333,24 @@ SDL_AppResult SDL_AppIterate(void* appstate) {
}
if (app->ui) {
pxl8_ui_input_mousemove(app->ui, app->input.mouse_x, app->input.mouse_y);
pxl8_ui_frame_begin(app->ui);
for (i32 i = 0; i < 3; i++) {
if (app->input.mouse_buttons[i] && app->input.mouse_buttons_pressed[i]) {
if (app->input.mouse_buttons_pressed[i]) {
pxl8_ui_input_mousedown(app->ui, app->input.mouse_x, app->input.mouse_y, i + 1);
}
if (!app->input.mouse_buttons[i]) {
if (app->input.mouse_buttons_released[i]) {
pxl8_ui_input_mouseup(app->ui, app->input.mouse_x, app->input.mouse_y, i + 1);
}
}
pxl8_ui_input_mousemove(app->ui, app->input.mouse_x, app->input.mouse_y);
for (i32 key = 0; key < 256; key++) {
if (app->input.keys_pressed[key]) {
pxl8_ui_input_keydown(app->ui, key);
}
if (!app->input.keys[key] && app->input.keys_pressed[key]) {
if (!app->input.keys_down[key] && app->input.keys_pressed[key]) {
pxl8_ui_input_keyup(app->ui, key);
}
}
@ -390,12 +380,6 @@ SDL_AppResult SDL_AppIterate(void* appstate) {
i32 render_width, render_height;
pxl8_gfx_get_resolution_dimensions(app->resolution, &render_width, &render_height);
if (app->show_fps && app->current_fps > 0.0f) {
char fps_text[32];
SDL_snprintf(fps_text, sizeof(fps_text), "FPS: %d", (i32)(app->current_fps + 0.5f));
pxl8_text(app->gfx, fps_text, render_width - 80, 10, 15);
}
if (app->ui) {
pxl8_ui_frame_end(app->ui);
}
@ -406,7 +390,9 @@ SDL_AppResult SDL_AppIterate(void* appstate) {
pxl8_gfx_present(app->gfx);
SDL_memset(app->input.keys_pressed, 0, sizeof(app->input.keys_pressed));
SDL_memset(app->input.keys_released, 0, sizeof(app->input.keys_released));
SDL_memset(app->input.mouse_buttons_pressed, 0, sizeof(app->input.mouse_buttons_pressed));
SDL_memset(app->input.mouse_buttons_released, 0, sizeof(app->input.mouse_buttons_released));
app->input.mouse_wheel_x = 0;
app->input.mouse_wheel_y = 0;
@ -427,25 +413,23 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
return SDL_APP_SUCCESS;
}
if (event->key.key == SDLK_F3) {
app->show_fps = !app->show_fps;
}
SDL_Keycode key = event->key.key;
if (key < 256) {
if (!app->input.keys[key]) {
app->input.keys_pressed[key] = true;
SDL_Scancode scancode = event->key.scancode;
if (scancode < 256) {
if (!app->input.keys_down[scancode]) {
app->input.keys_pressed[scancode] = true;
}
app->input.keys[key] = true;
app->input.keys_down[scancode] = true;
app->input.keys_released[scancode] = false;
}
break;
}
case SDL_EVENT_KEY_UP: {
SDL_Keycode key = event->key.key;
if (key < 256) {
app->input.keys[key] = false;
app->input.keys_pressed[key] = false;
SDL_Scancode scancode = event->key.scancode;
if (scancode < 256) {
app->input.keys_down[scancode] = false;
app->input.keys_pressed[scancode] = false;
app->input.keys_released[scancode] = true;
}
break;
}
@ -453,10 +437,11 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
u8 button = event->button.button - 1;
if (button < 3) {
if (!app->input.mouse_buttons[button]) {
if (!app->input.mouse_buttons_down[button]) {
app->input.mouse_buttons_pressed[button] = true;
}
app->input.mouse_buttons[button] = true;
app->input.mouse_buttons_down[button] = true;
app->input.mouse_buttons_released[button] = false;
}
break;
}
@ -464,7 +449,9 @@ SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
case SDL_EVENT_MOUSE_BUTTON_UP: {
u8 button = event->button.button - 1;
if (button < 3) {
app->input.mouse_buttons[button] = false;
app->input.mouse_buttons_down[button] = false;
app->input.mouse_buttons_pressed[button] = false;
app->input.mouse_buttons_released[button] = true;
}
break;
}

View file

@ -1,3 +1,4 @@
#include <ctype.h>
#include <SDL3/SDL.h>
#include "pxl8_io.h"
@ -97,12 +98,50 @@ void pxl8_io_free_binary_data(u8* data) {
}
}
bool pxl8_key_down(const pxl8_input_state* input, i32 key) {
if (!input || key < 0 || key >= 256) return false;
return input->keys[key];
static i32 pxl8_key_code(const char* key_name) {
if (!key_name || !key_name[0]) return 0;
SDL_Scancode scancode = SDL_GetScancodeFromName(key_name);
return (i32)scancode;
}
bool pxl8_key_pressed(const pxl8_input_state* input, i32 key) {
if (!input || key < 0 || key >= 256) return false;
bool pxl8_key_down(const pxl8_input_state* input, const char* key_name) {
if (!input) return false;
i32 key = pxl8_key_code(key_name);
if (key < 0 || key >= 256) return false;
return input->keys_down[key];
}
bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name) {
if (!input) return false;
i32 key = pxl8_key_code(key_name);
if (key < 0 || key >= 256) return false;
return input->keys_pressed[key];
}
bool pxl8_key_released(const pxl8_input_state* input, const char* key_name) {
if (!input) return false;
i32 key = pxl8_key_code(key_name);
if (key < 0 || key >= 256) return false;
return input->keys_released[key];
}
i32 pxl8_mouse_wheel_x(const pxl8_input_state* input) {
if (!input) return 0;
return input->mouse_wheel_x;
}
i32 pxl8_mouse_wheel_y(const pxl8_input_state* input) {
if (!input) return 0;
return input->mouse_wheel_y;
}
i32 pxl8_mouse_x(const pxl8_input_state* input) {
if (!input) return 0;
return input->mouse_x;
}
i32 pxl8_mouse_y(const pxl8_input_state* input) {
if (!input) return 0;
return input->mouse_y;
}

View file

@ -19,8 +19,14 @@ pxl8_result pxl8_io_read_file(const char* path, char** content, size_t* size);
pxl8_result pxl8_io_write_binary_file(const char* path, const u8* data, size_t size);
pxl8_result pxl8_io_write_file(const char* path, const char* content, size_t size);
bool pxl8_key_down(const pxl8_input_state* input, i32 key);
bool pxl8_key_pressed(const pxl8_input_state* input, i32 key);
bool pxl8_key_down(const pxl8_input_state* input, const char* key_name);
bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name);
bool pxl8_key_released(const pxl8_input_state* input, const char* key_name);
i32 pxl8_mouse_wheel_x(const pxl8_input_state* input);
i32 pxl8_mouse_wheel_y(const pxl8_input_state* input);
i32 pxl8_mouse_x(const pxl8_input_state* input);
i32 pxl8_mouse_y(const pxl8_input_state* input);
#ifdef __cplusplus
}

View file

@ -59,8 +59,13 @@ static const char* pxl8_ffi_cdefs =
"i32 pxl8_gfx_create_texture(pxl8_gfx* ctx, const u8* pixels, u32 width, u32 height);\n"
"void pxl8_gfx_upload_atlas(pxl8_gfx* ctx);\n"
"typedef struct pxl8_input_state pxl8_input_state;\n"
"bool pxl8_key_down(const pxl8_input_state* input, i32 key);\n"
"bool pxl8_key_pressed(const pxl8_input_state* input, i32 key);\n"
"bool pxl8_key_down(const pxl8_input_state* input, const char* key_name);\n"
"bool pxl8_key_pressed(const pxl8_input_state* input, const char* key_name);\n"
"bool pxl8_key_released(const pxl8_input_state* input, const char* key_name);\n"
"int pxl8_mouse_wheel_x(const pxl8_input_state* input);\n"
"int pxl8_mouse_wheel_y(const pxl8_input_state* input);\n"
"int pxl8_mouse_x(const pxl8_input_state* input);\n"
"int pxl8_mouse_y(const pxl8_input_state* input);\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"
@ -168,12 +173,15 @@ static const char* pxl8_ffi_cdefs =
"void pxl8_ui_input_scroll(pxl8_ui* ui, int x, int y);\n"
"void pxl8_ui_input_text(pxl8_ui* ui, const char* text);\n"
"bool pxl8_ui_button(pxl8_ui* ui, const char* label);\n"
"bool pxl8_ui_checkbox(pxl8_ui* ui, const char* label, bool* state);\n"
"void pxl8_ui_indent(pxl8_ui* ui, int amount);\n"
"void pxl8_ui_label(pxl8_ui* ui, const char* text);\n"
"void pxl8_ui_layout_row(pxl8_ui* ui, int item_count, const int* widths, int height);\n"
"int pxl8_ui_menu(pxl8_ui* ui, pxl8_menu_item* items, int item_count);\n"
"void pxl8_ui_panel(pxl8_ui* ui, pxl8_bounds rect, pxl8_frame_theme* theme);\n"
"bool pxl8_ui_window_begin(pxl8_ui* ui, const char* title, pxl8_bounds rect, int options);\n"
"void pxl8_ui_window_end(pxl8_ui* ui);\n"
"void pxl8_ui_window_set_open(pxl8_ui* ui, const char* title, bool open);\n"
"pxl8_frame_theme pxl8_ui_theme_default(void);\n";
void pxl8_lua_info(const char* msg) {
@ -246,6 +254,18 @@ pxl8_script* pxl8_script_create(void) {
if (luaL_dofile(script->L, "lib/fennel/fennel.lua") == 0) {
lua_setglobal(script->L, "fennel");
lua_getglobal(script->L, "fennel");
lua_getfield(script->L, -1, "install");
if (lua_isfunction(script->L, -1)) {
if (lua_pcall(script->L, 0, 0, 0) != 0) {
pxl8_warn("Failed to install fennel searcher: %s", lua_tostring(script->L, -1));
lua_pop(script->L, 1);
}
} else {
lua_pop(script->L, 1);
}
lua_pop(script->L, 1);
}
script->last_error[0] = '\0';

View file

@ -64,11 +64,13 @@ typedef struct pxl8_bounds {
} pxl8_bounds;
typedef struct pxl8_input_state {
bool keys[256];
bool keys_down[256];
bool keys_pressed[256];
bool keys_released[256];
bool mouse_buttons[3];
bool mouse_buttons_down[3];
bool mouse_buttons_pressed[3];
bool mouse_buttons_released[3];
i32 mouse_wheel_x;
i32 mouse_wheel_y;
i32 mouse_x;

View file

@ -38,26 +38,47 @@ static int mu_text_height(mu_Font font) {
return f->default_height;
}
static void pxl8_ui_render_icon(pxl8_gfx* gfx, i32 id, i32 x, i32 y, i32 w, i32 h, u8 color) {
switch (id) {
case 2: {
i32 cx = x + w / 2;
i32 cy = y + h / 2;
i32 size = (w < h ? w : h) / 3;
pxl8_line(gfx, cx - size, cy, cx, cy + size, color);
pxl8_line(gfx, cx, cy + size, cx + size, cy - size, color);
break;
}
case 1: {
i32 cx = x + w / 2;
i32 cy = y + h / 2;
i32 size = (w < h ? w : h) / 4;
pxl8_line(gfx, cx - size, cy - size, cx + size, cy + size, color);
pxl8_line(gfx, cx + size, cy - size, cx - size, cy + size, color);
break;
}
}
}
static void pxl8_ui_render_commands(pxl8_ui* ui) {
mu_Command* cmd = NULL;
while (mu_next_command(&ui->mu_ctx, &cmd)) {
switch (cmd->type) {
case MU_COMMAND_RECT: {
mu_RectCommand* rc = (mu_RectCommand*)cmd;
u8 color = rc->color.r;
pxl8_rect_fill(ui->gfx, rc->rect.x, rc->rect.y, rc->rect.w, rc->rect.h, color);
pxl8_rect_fill(ui->gfx, rc->rect.x, rc->rect.y, rc->rect.w, rc->rect.h, rc->color.r);
break;
}
case MU_COMMAND_TEXT: {
mu_TextCommand* tc = (mu_TextCommand*)cmd;
u8 color = tc->color.r;
pxl8_text(ui->gfx, tc->str, tc->pos.x, tc->pos.y, color);
pxl8_text(ui->gfx, tc->str, tc->pos.x, tc->pos.y, tc->color.r);
break;
}
case MU_COMMAND_CLIP: {
break;
}
case MU_COMMAND_ICON: {
mu_IconCommand* ic = (mu_IconCommand*)cmd;
pxl8_ui_render_icon(ui->gfx, ic->id, ic->rect.x, ic->rect.y, ic->rect.w, ic->rect.h, ic->color.r);
break;
}
}
@ -104,6 +125,21 @@ pxl8_ui* pxl8_ui_create(pxl8_gfx* gfx) {
ui->mu_ctx.text_height = mu_text_height;
ui->mu_ctx.text_width = mu_text_width;
ui->mu_ctx.style->colors[0] = (mu_Color){15, 0, 0, 255};
ui->mu_ctx.style->colors[1] = (mu_Color){8, 0, 0, 255};
ui->mu_ctx.style->colors[2] = (mu_Color){1, 0, 0, 255};
ui->mu_ctx.style->colors[3] = (mu_Color){2, 0, 0, 255};
ui->mu_ctx.style->colors[4] = (mu_Color){15, 0, 0, 255};
ui->mu_ctx.style->colors[5] = (mu_Color){0, 0, 0, 0};
ui->mu_ctx.style->colors[6] = (mu_Color){7, 0, 0, 255};
ui->mu_ctx.style->colors[7] = (mu_Color){8, 0, 0, 255};
ui->mu_ctx.style->colors[8] = (mu_Color){10, 0, 0, 255};
ui->mu_ctx.style->colors[9] = (mu_Color){2, 0, 0, 255};
ui->mu_ctx.style->colors[10] = (mu_Color){3, 0, 0, 255};
ui->mu_ctx.style->colors[11] = (mu_Color){10, 0, 0, 255};
ui->mu_ctx.style->colors[12] = (mu_Color){7, 0, 0, 255};
ui->mu_ctx.style->colors[13] = (mu_Color){8, 0, 0, 255};
return ui;
}
@ -163,6 +199,48 @@ bool pxl8_ui_button(pxl8_ui* ui, const char* label) {
return mu_button(&ui->mu_ctx, label) & MU_RES_SUBMIT;
}
bool pxl8_ui_checkbox(pxl8_ui* ui, const char* label, bool* state) {
if (!ui || !state || !label) return false;
mu_Context* ctx = &ui->mu_ctx;
mu_push_id(ctx, label, (int)strlen(label));
mu_Id id = mu_get_id(ctx, label, (int)strlen(label));
mu_Rect r = mu_layout_next(ctx);
mu_Rect box = mu_rect(r.x, r.y, r.h, r.h);
int had_focus_before = (ctx->focus == id);
mu_update_control(ctx, id, r, 0);
int has_focus_after = (ctx->focus == id);
int mouseover = mu_mouse_over(ctx, r);
int res = 0;
int int_state = *state ? 1 : 0;
if (had_focus_before && !has_focus_after && !ctx->mouse_down && mouseover) {
res |= MU_RES_CHANGE;
int_state = !int_state;
}
mu_draw_control_frame(ctx, id, box, MU_COLOR_BASE, 0);
if (int_state) {
mu_draw_icon(ctx, MU_ICON_CHECK, box, ctx->style->colors[MU_COLOR_TEXT]);
}
r = mu_rect(r.x + box.w, r.y, r.w - box.w, r.h);
mu_draw_control_text(ctx, label, r, MU_COLOR_TEXT, 0);
mu_pop_id(ctx);
*state = int_state != 0;
return res & MU_RES_CHANGE;
}
void pxl8_ui_indent(pxl8_ui* ui, i32 amount) {
if (!ui) return;
mu_Layout* layout = &ui->mu_ctx.layout_stack.items[ui->mu_ctx.layout_stack.idx - 1];
layout->indent += amount;
}
void pxl8_ui_label(pxl8_ui* ui, const char* text) {
if (!ui || !text) return;
mu_label(&ui->mu_ctx, text);
@ -205,6 +283,14 @@ void pxl8_ui_window_end(pxl8_ui* ui) {
mu_end_window(&ui->mu_ctx);
}
void pxl8_ui_window_set_open(pxl8_ui* ui, const char* title, bool open) {
if (!ui || !title) return;
mu_Container* win = mu_get_container(&ui->mu_ctx, title);
if (win) {
win->open = open ? 1 : 0;
}
}
pxl8_frame_theme pxl8_ui_theme_default(void) {
pxl8_frame_theme theme = {0};
theme.bg_color = 0;

View file

@ -5,6 +5,23 @@
typedef struct pxl8_ui pxl8_ui;
typedef struct pxl8_ui_theme {
u8 text;
u8 border;
u8 window_bg;
u8 title_bg;
u8 title_text;
u8 panel_bg;
u8 button;
u8 button_hover;
u8 button_focus;
u8 base;
u8 base_hover;
u8 base_focus;
u8 scroll_base;
u8 scroll_thumb;
} pxl8_ui_theme;
typedef struct pxl8_frame_theme {
u8 bg_color;
u32 sprite_id;
@ -44,12 +61,15 @@ void pxl8_ui_input_scroll(pxl8_ui* ui, i32 x, i32 y);
void pxl8_ui_input_text(pxl8_ui* ui, const char* text);
bool pxl8_ui_button(pxl8_ui* ui, const char* label);
bool pxl8_ui_checkbox(pxl8_ui* ui, const char* label, bool* state);
void pxl8_ui_indent(pxl8_ui* ui, i32 amount);
void pxl8_ui_label(pxl8_ui* ui, const char* text);
void pxl8_ui_layout_row(pxl8_ui* ui, i32 item_count, const i32* widths, i32 height);
i32 pxl8_ui_menu(pxl8_ui* ui, pxl8_menu_item* items, i32 item_count);
void pxl8_ui_panel(pxl8_ui* ui, pxl8_bounds rect, pxl8_frame_theme* theme);
bool pxl8_ui_window_begin(pxl8_ui* ui, const char* title, pxl8_bounds rect, i32 options);
void pxl8_ui_window_end(pxl8_ui* ui);
void pxl8_ui_window_set_open(pxl8_ui* ui, const char* title, bool open);
pxl8_frame_theme pxl8_ui_theme_default(void);

View file

@ -30,6 +30,10 @@ typedef struct pxl8_raster_bar {
f32 speed;
} pxl8_raster_bar;
#ifdef __cplusplus
extern "C" {
#endif
pxl8_particles* pxl8_particles_create(u32 max_count);
void pxl8_particles_destroy(pxl8_particles* particles);
@ -51,3 +55,7 @@ void pxl8_vfx_raster_bars(pxl8_gfx* gfx, pxl8_raster_bar* bars, u32 bar_count, f
void pxl8_vfx_rotozoom(pxl8_gfx* gfx, f32 angle, f32 zoom, i32 cx, i32 cy);
void pxl8_vfx_tunnel(pxl8_gfx* gfx, f32 time, f32 speed, f32 twist);
void pxl8_vfx_water_ripple(pxl8_gfx* gfx, f32* height_map, i32 drop_x, i32 drop_y, f32 damping);
#ifdef __cplusplus
}
#endif