implement our own gui module, drop microui

This commit is contained in:
asrael 2025-11-21 11:51:23 -06:00
parent 2555bec8eb
commit 8baf5f06ea
25 changed files with 495 additions and 507 deletions

View file

@ -8,12 +8,12 @@ local particles = require("pxl8.particles")
local tilemap = require("pxl8.tilemap")
local gfx3d = require("pxl8.gfx3d")
local math3d = require("pxl8.math")
local ui = require("pxl8.ui")
local gui = require("pxl8.gui")
local world = require("pxl8.world")
local transition = require("pxl8.transition")
local anim = require("pxl8.anim")
core.init(_pxl8_gfx, _pxl8_input, _pxl8_sys, _pxl8_ui)
core.init(_pxl8_gfx, _pxl8_input, _pxl8_sys)
local pxl8 = {}
@ -25,6 +25,7 @@ pxl8.warn = core.warn
pxl8.error = core.error
pxl8.debug = core.debug
pxl8.trace = core.trace
pxl8.quit = core.quit
pxl8.clear = gfx2d.clear
pxl8.pixel = gfx2d.pixel
@ -51,6 +52,11 @@ pxl8.mouse_wheel_x = input.mouse_wheel_x
pxl8.mouse_wheel_y = input.mouse_wheel_y
pxl8.mouse_x = input.mouse_x
pxl8.mouse_y = input.mouse_y
pxl8.get_mouse_pos = input.get_mouse_pos
pxl8.mouse_pressed = input.mouse_pressed
pxl8.mouse_released = input.mouse_released
pxl8.center_cursor = input.center_cursor
pxl8.set_cursor = input.set_cursor
pxl8.set_relative_mouse_mode = input.set_relative_mouse_mode
pxl8.vfx_raster_bars = vfx.raster_bars
@ -116,15 +122,18 @@ pxl8.mat4_perspective = math3d.mat4_perspective
pxl8.mat4_lookat = math3d.mat4_lookat
pxl8.bounds = math3d.bounds
pxl8.ui_button = ui.button
pxl8.ui_checkbox = ui.checkbox
pxl8.ui_has_mouse_focus = ui.has_mouse_focus
pxl8.ui_indent = ui.indent
pxl8.ui_label = ui.label
pxl8.ui_layout_row = ui.layout_row
pxl8.ui_window_begin = ui.window_begin
pxl8.ui_window_end = ui.window_end
pxl8.ui_window_set_open = ui.window_set_open
pxl8.gui_state_create = gui.state_create
pxl8.gui_state_destroy = gui.state_destroy
pxl8.gui_begin_frame = gui.begin_frame
pxl8.gui_end_frame = gui.end_frame
pxl8.gui_cursor_move = gui.cursor_move
pxl8.gui_cursor_down = gui.cursor_down
pxl8.gui_cursor_up = gui.cursor_up
pxl8.gui_button = gui.button
pxl8.gui_window = gui.window
pxl8.gui_label = gui.label
pxl8.gui_is_hovering = gui.is_hovering
pxl8.gui_get_cursor_pos = gui.get_cursor_pos
pxl8.world_new = world.new
pxl8.world_destroy = world.destroy

View file

@ -3,11 +3,10 @@ local C = ffi.C
local core = {}
function core.init(gfx_ptr, input_ptr, sys_ptr, ui_ptr)
function core.init(gfx_ptr, input_ptr, sys_ptr)
core.gfx = gfx_ptr
core.input = input_ptr
core.sys = sys_ptr
core.ui = ui_ptr
end
function core.get_fps()
@ -42,4 +41,8 @@ function core.trace(msg)
C.pxl8_lua_trace(msg)
end
function core.quit()
C.pxl8_set_running(core.sys, false)
end
return core

59
src/lua/pxl8/gui.lua Normal file
View file

@ -0,0 +1,59 @@
local ffi = require("ffi")
local C = ffi.C
local core = require("pxl8.core")
local gui = {}
local state = nil
local function gui_state()
if not state then
state = ffi.gc(C.pxl8_gui_state_create(), C.pxl8_gui_state_destroy)
end
return state
end
function gui.begin_frame()
C.pxl8_gui_begin_frame(gui_state())
end
function gui.end_frame()
C.pxl8_gui_end_frame(gui_state())
end
function gui.cursor_move(x, y)
C.pxl8_gui_cursor_move(gui_state(), x, y)
end
function gui.cursor_down()
C.pxl8_gui_cursor_down(gui_state())
end
function gui.cursor_up()
C.pxl8_gui_cursor_up(gui_state())
end
function gui.button(id, x, y, w, h, label)
return C.pxl8_gui_button(gui_state(), core.gfx, id, x, y, w, h, label)
end
function gui.window(x, y, w, h, title)
C.pxl8_gui_window(core.gfx, x, y, w, h, title)
end
function gui.label(x, y, text, color)
C.pxl8_gui_label(core.gfx, x, y, text, color)
end
function gui.is_hovering()
return C.pxl8_gui_is_hovering(gui_state())
end
function gui.get_cursor_pos()
local x = ffi.new("i32[1]")
local y = ffi.new("i32[1]")
C.pxl8_gui_get_cursor_pos(gui_state(), x, y)
return x[0], y[0]
end
return gui

View file

@ -40,8 +40,36 @@ function input.mouse_dy()
return C.pxl8_mouse_dy(core.input)
end
function input.get_mouse_pos()
return C.pxl8_mouse_x(core.input), C.pxl8_mouse_y(core.input)
end
function input.mouse_pressed(button)
return C.pxl8_mouse_pressed(core.input, button)
end
function input.mouse_released(button)
return C.pxl8_mouse_released(core.input, button)
end
function input.set_relative_mouse_mode(enabled)
C.pxl8_set_relative_mouse_mode(core.sys, enabled)
end
function input.center_cursor()
C.pxl8_center_cursor(core.sys)
end
function input.set_cursor(cursor_type)
local cursor_enum
if cursor_type == "arrow" then
cursor_enum = C.PXL8_CURSOR_ARROW
elseif cursor_type == "hand" then
cursor_enum = C.PXL8_CURSOR_HAND
else
cursor_enum = C.PXL8_CURSOR_ARROW
end
C.pxl8_set_cursor(core.sys, cursor_enum)
end
return input

View file

@ -1,52 +0,0 @@
local ffi = require("ffi")
local C = ffi.C
local core = require("pxl8.core")
local ui = {}
function ui.button(label)
return C.pxl8_ui_button(core.ui, label)
end
function ui.checkbox(label, state)
local state_ptr = ffi.new("bool[1]", state)
local changed = C.pxl8_ui_checkbox(core.ui, label, state_ptr)
return changed, state_ptr[0]
end
function ui.has_mouse_focus()
return C.pxl8_ui_has_mouse_focus(core.ui)
end
function ui.indent(amount)
C.pxl8_ui_indent(core.ui, amount)
end
function ui.label(text)
C.pxl8_ui_label(core.ui, text)
end
function ui.layout_row(item_count, widths, height)
local widths_array = widths
if type(widths) == "table" then
widths_array = ffi.new("int[?]", #widths, widths)
elseif type(widths) == "number" then
widths_array = ffi.new("int[1]", widths)
end
C.pxl8_ui_layout_row(core.ui, item_count, widths_array, height)
end
function 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(core.ui, title, rect, options or 0)
end
function ui.window_end()
C.pxl8_ui_window_end(core.ui)
end
function ui.window_set_open(title, open)
C.pxl8_ui_window_set_open(core.ui, title, open)
end
return ui

View file

@ -16,7 +16,6 @@
#include "pxl8_script.h"
#include "pxl8_sys.h"
#include "pxl8_types.h"
#include "pxl8_ui.h"
struct pxl8 {
pxl8_cart* cart;
@ -127,12 +126,6 @@ pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]) {
return PXL8_ERROR_INITIALIZATION_FAILED;
}
game->ui = pxl8_ui_create(game->gfx);
if (!game->ui) {
pxl8_error("Failed to create UI");
return PXL8_ERROR_INITIALIZATION_FAILED;
}
game->script = pxl8_script_create();
if (!game->script) {
pxl8_error("Failed to initialize scripting: %s", pxl8_script_get_last_error(game->script));
@ -170,7 +163,6 @@ pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]) {
pxl8_script_set_gfx(game->script, game->gfx);
pxl8_script_set_input(game->script, &game->input);
pxl8_script_set_sys(game->script, sys);
pxl8_script_set_ui(game->script, game->ui);
if (game->script_path[0] != '\0') {
pxl8_result result = pxl8_script_load_main(game->script, game->script_path);
@ -252,34 +244,6 @@ pxl8_result pxl8_update(pxl8* sys) {
}
}
if (game->ui) {
pxl8_ui_input_mousemove(game->ui, game->input.mouse_x, game->input.mouse_y);
if (game->input.mouse_wheel_x != 0 || game->input.mouse_wheel_y != 0) {
pxl8_ui_input_scroll(game->ui, game->input.mouse_wheel_x * 10, -game->input.mouse_wheel_y * 10);
}
for (i32 i = 0; i < 3; i++) {
if (game->input.mouse_buttons_pressed[i]) {
pxl8_ui_input_mousedown(game->ui, game->input.mouse_x, game->input.mouse_y, i + 1);
}
if (game->input.mouse_buttons_released[i]) {
pxl8_ui_input_mouseup(game->ui, game->input.mouse_x, game->input.mouse_y, i + 1);
}
}
for (i32 key = 0; key < 256; key++) {
if (game->input.keys_pressed[key]) {
pxl8_ui_input_keydown(game->ui, key);
}
if (!game->input.keys_down[key] && game->input.keys_pressed[key]) {
pxl8_ui_input_keyup(game->ui, key);
}
}
pxl8_ui_frame_begin(game->ui);
}
if (game->script_loaded) {
pxl8_script_call_function_f32(game->script, "update", dt);
}
@ -315,10 +279,6 @@ pxl8_result pxl8_frame(pxl8* sys) {
pxl8_size render_size = pxl8_get_resolution_dimensions(game->resolution);
if (game->ui) {
pxl8_ui_frame_end(game->ui);
}
pxl8_gfx_set_viewport(game->gfx, pxl8_gfx_viewport(bounds, render_size.w, render_size.h));
pxl8_gfx_upload_framebuffer(game->gfx);
pxl8_gfx_upload_atlas(game->gfx);
@ -332,8 +292,6 @@ pxl8_result pxl8_frame(pxl8* sys) {
game->input.mouse_dy = 0;
game->input.mouse_wheel_x = 0;
game->input.mouse_wheel_y = 0;
game->input.mouse_x = 0;
game->input.mouse_y = 0;
game->frame_count++;
@ -360,7 +318,6 @@ void pxl8_quit(pxl8* sys) {
pxl8_gfx_destroy(game->gfx);
pxl8_script_destroy(game->script);
if (game->ui) pxl8_ui_destroy(game->ui);
}
bool pxl8_is_running(const pxl8* sys) {
@ -389,6 +346,16 @@ pxl8_resolution pxl8_get_resolution(const pxl8* sys) {
return (sys && sys->game) ? sys->game->resolution : PXL8_RESOLUTION_640x360;
}
void pxl8_center_cursor(pxl8* sys) {
if (!sys || !sys->hal || !sys->hal->center_cursor) return;
sys->hal->center_cursor(sys->platform_data);
}
void pxl8_set_cursor(pxl8* sys, pxl8_cursor cursor) {
if (!sys || !sys->hal || !sys->hal->set_cursor) return;
sys->hal->set_cursor(sys->platform_data, cursor);
}
void pxl8_set_relative_mouse_mode(pxl8* sys, bool enabled) {
if (!sys || !sys->hal || !sys->hal->set_relative_mouse_mode) return;
sys->hal->set_relative_mouse_mode(sys->platform_data, enabled);

View file

@ -3,14 +3,12 @@
#include "pxl8_gfx.h"
#include "pxl8_script.h"
#include "pxl8_types.h"
#include "pxl8_ui.h"
typedef struct pxl8_game {
pxl8_color_mode color_mode;
pxl8_gfx* gfx;
pxl8_resolution resolution;
pxl8_script* script;
pxl8_ui* ui;
i32 frame_count;
u64 last_time;

131
src/pxl8_gui.c Normal file
View file

@ -0,0 +1,131 @@
#include "pxl8_gui.h"
#include <stdlib.h>
#include <string.h>
pxl8_gui_state* pxl8_gui_state_create(void) {
pxl8_gui_state* state = (pxl8_gui_state*)malloc(sizeof(pxl8_gui_state));
if (!state) return NULL;
memset(state, 0, sizeof(pxl8_gui_state));
return state;
}
void pxl8_gui_state_destroy(pxl8_gui_state* state) {
if (!state) return;
free(state);
}
void pxl8_gui_begin_frame(pxl8_gui_state* state) {
if (!state) return;
state->hot_id = 0;
}
void pxl8_gui_end_frame(pxl8_gui_state* state) {
if (!state) return;
if (!state->cursor_down) {
state->active_id = 0;
}
state->cursor_clicked = false;
}
void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y) {
if (!state) return;
state->cursor_x = x;
state->cursor_y = y;
}
void pxl8_gui_cursor_down(pxl8_gui_state* state) {
if (!state) return;
state->cursor_down = true;
}
void pxl8_gui_cursor_up(pxl8_gui_state* state) {
if (!state) return;
state->cursor_down = false;
state->cursor_clicked = true;
}
static bool is_cursor_over(const pxl8_gui_state* state, i32 x, i32 y, i32 w, i32 h) {
return state->cursor_x >= x && state->cursor_x < (x + w) &&
state->cursor_y >= y && state->cursor_y < (y + h);
}
bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y, i32 w, i32 h, const char* label) {
if (!state || !gfx || !label) return false;
bool cursor_over = is_cursor_over(state, x, y, w, h);
bool is_hot = (state->hot_id == id);
bool is_active = (state->active_id == id);
if (cursor_over) {
state->hot_id = id;
}
if (cursor_over && state->cursor_down && state->active_id == 0) {
state->active_id = id;
}
bool clicked = is_active && state->cursor_clicked && cursor_over;
if (clicked) {
state->active_id = 0;
}
u8 bg_color;
u8 border_color;
i32 offset_x = 0;
i32 offset_y = 0;
if (is_active) {
bg_color = 4;
border_color = 3;
offset_x = 1;
offset_y = 1;
} else if (is_hot || cursor_over) {
bg_color = 4;
border_color = 8;
} else {
bg_color = 3;
border_color = 4;
}
pxl8_rect_fill(gfx, x, y, w, h, bg_color);
pxl8_rect(gfx, x, y, w, h, border_color);
i32 text_len = (i32)strlen(label);
i32 text_x = x + (w / 2) - ((text_len * 8) / 2) + offset_x;
i32 text_y = y + (h / 2) - 5 + offset_y;
pxl8_text(gfx, label, text_x, text_y, 6);
return clicked;
}
void pxl8_gui_window(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, const char* title) {
if (!gfx || !title) return;
pxl8_rect_fill(gfx, x, y, w, 28, 1);
pxl8_rect_fill(gfx, x, y + 28, w, h - 28, 2);
pxl8_rect(gfx, x, y, w, h, 4);
pxl8_rect_fill(gfx, x, y + 28, w, 1, 4);
i32 title_x = x + 10;
i32 title_y = y + (28 / 2) - 5;
pxl8_text(gfx, title, title_x, title_y, 8);
}
void pxl8_gui_label(pxl8_gfx* gfx, i32 x, i32 y, const char* text, u8 color) {
if (!gfx || !text) return;
pxl8_text(gfx, text, x, y, color);
}
bool pxl8_gui_is_hovering(const pxl8_gui_state* state) {
if (!state) return false;
return state->hot_id != 0;
}
void pxl8_gui_get_cursor_pos(const pxl8_gui_state* state, i32* x, i32* y) {
if (!state) return;
if (x) *x = state->cursor_x;
if (y) *y = state->cursor_y;
}

38
src/pxl8_gui.h Normal file
View file

@ -0,0 +1,38 @@
#pragma once
#include "pxl8_gfx.h"
#include "pxl8_types.h"
typedef struct {
i32 cursor_x;
i32 cursor_y;
bool cursor_down;
bool cursor_clicked;
u32 hot_id;
u32 active_id;
} pxl8_gui_state;
#ifdef __cplusplus
extern "C" {
#endif
pxl8_gui_state* pxl8_gui_state_create(void);
void pxl8_gui_state_destroy(pxl8_gui_state* state);
void pxl8_gui_begin_frame(pxl8_gui_state* state);
void pxl8_gui_end_frame(pxl8_gui_state* state);
void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y);
void pxl8_gui_cursor_down(pxl8_gui_state* state);
void pxl8_gui_cursor_up(pxl8_gui_state* state);
bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y, i32 w, i32 h, const char* label);
void pxl8_gui_window(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, const char* title);
void pxl8_gui_label(pxl8_gfx* gfx, i32 x, i32 y, const char* text, u8 color);
bool pxl8_gui_is_hovering(const pxl8_gui_state* state);
void pxl8_gui_get_cursor_pos(const pxl8_gui_state* state, i32* x, i32* y);
#ifdef __cplusplus
}
#endif

View file

@ -12,7 +12,9 @@ typedef struct pxl8_hal {
u64 (*get_ticks)(void);
void (*center_cursor)(void* platform_data);
void (*present)(void* platform_data);
void (*set_cursor)(void* platform_data, pxl8_cursor cursor);
void (*set_relative_mouse_mode)(void* platform_data, bool enabled);
void (*upload_atlas)(void* platform_data, const pxl8_atlas* atlas,
const u32* palette, pxl8_color_mode mode);

View file

@ -170,6 +170,16 @@ i32 pxl8_mouse_wheel_y(const pxl8_input_state* input) {
return input->mouse_wheel_y;
}
bool pxl8_mouse_pressed(const pxl8_input_state* input, i32 button) {
if (!input || button < 1 || button > 3) return false;
return input->mouse_buttons_pressed[button - 1];
}
bool pxl8_mouse_released(const pxl8_input_state* input, i32 button) {
if (!input || button < 1 || button > 3) return false;
return input->mouse_buttons_released[button - 1];
}
i32 pxl8_mouse_x(const pxl8_input_state* input) {
if (!input) return 0;
return input->mouse_x;

View file

@ -100,6 +100,8 @@ 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);
bool pxl8_mouse_pressed(const pxl8_input_state* input, i32 button);
bool pxl8_mouse_released(const pxl8_input_state* input, i32 button);
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);

View file

@ -13,13 +13,12 @@
#include <lualib.h>
#include "pxl8_macros.h"
#include "pxl8_ui.h"
#include "pxl8_gui.h"
struct pxl8_script {
lua_State* L;
pxl8_gfx* gfx;
pxl8_input_state* input;
pxl8_ui* ui;
char last_error[2048];
char main_path[256];
char watch_dir[256];
@ -109,13 +108,19 @@ static const char* pxl8_ffi_cdefs =
"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"
"bool pxl8_mouse_pressed(const pxl8_input_state* input, i32 button);\n"
"bool pxl8_mouse_released(const pxl8_input_state* input, i32 button);\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"
"int pxl8_mouse_dx(const pxl8_input_state* input);\n"
"int pxl8_mouse_dy(const pxl8_input_state* input);\n"
"typedef enum { PXL8_CURSOR_ARROW = 0, PXL8_CURSOR_HAND = 1 } pxl8_cursor;\n"
"void pxl8_center_cursor(pxl8* sys);\n"
"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"
@ -303,32 +308,19 @@ static const char* pxl8_ffi_cdefs =
"void pxl8_world_render(pxl8_world* world, pxl8_gfx* gfx, pxl8_vec3 camera_pos);\n"
"void pxl8_world_unload(pxl8_world* world);\n"
"\n"
"typedef struct pxl8_ui pxl8_ui;\n"
"typedef struct { unsigned char bg_color; unsigned int sprite_id; int corner_size; int edge_size; int padding; } pxl8_frame_theme;\n"
"typedef struct { bool enabled; const char* label; } pxl8_menu_item;\n"
"pxl8_ui* pxl8_ui_create(pxl8_gfx* gfx);\n"
"void pxl8_ui_destroy(pxl8_ui* ui);\n"
"void pxl8_ui_frame_begin(pxl8_ui* ui);\n"
"void pxl8_ui_frame_end(pxl8_ui* ui);\n"
"void pxl8_ui_input_keydown(pxl8_ui* ui, int key);\n"
"void pxl8_ui_input_keyup(pxl8_ui* ui, int key);\n"
"void pxl8_ui_input_mousedown(pxl8_ui* ui, int x, int y, int button);\n"
"void pxl8_ui_input_mousemove(pxl8_ui* ui, int x, int y);\n"
"void pxl8_ui_input_mouseup(pxl8_ui* ui, int x, int y, int button);\n"
"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"
"bool pxl8_ui_has_mouse_focus(pxl8_ui* ui);\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";
"typedef struct { i32 cursor_x; i32 cursor_y; bool cursor_down; bool cursor_clicked; u32 hot_id; u32 active_id; } pxl8_gui_state;\n"
"pxl8_gui_state* pxl8_gui_state_create(void);\n"
"void pxl8_gui_state_destroy(pxl8_gui_state* state);\n"
"void pxl8_gui_begin_frame(pxl8_gui_state* state);\n"
"void pxl8_gui_end_frame(pxl8_gui_state* state);\n"
"void pxl8_gui_cursor_move(pxl8_gui_state* state, i32 x, i32 y);\n"
"void pxl8_gui_cursor_down(pxl8_gui_state* state);\n"
"void pxl8_gui_cursor_up(pxl8_gui_state* state);\n"
"bool pxl8_gui_button(pxl8_gui_state* state, pxl8_gfx* gfx, u32 id, i32 x, i32 y, i32 w, i32 h, const char* label);\n"
"void pxl8_gui_window(pxl8_gfx* gfx, i32 x, i32 y, i32 w, i32 h, const char* title);\n"
"void pxl8_gui_label(pxl8_gfx* gfx, i32 x, i32 y, const char* text, u8 color);\n"
"bool pxl8_gui_is_hovering(const pxl8_gui_state* state);\n"
"void pxl8_gui_get_cursor_pos(const pxl8_gui_state* state, i32* x, i32* y);\n";
void pxl8_lua_info(const char* msg) {
pxl8_info("%s", msg);
@ -444,14 +436,6 @@ void pxl8_script_set_input(pxl8_script* script, pxl8_input_state* input) {
}
}
void pxl8_script_set_ui(pxl8_script* script, pxl8_ui* ui) {
if (!script) return;
script->ui = ui;
if (script->L && ui) {
lua_pushlightuserdata(script->L, ui);
lua_setglobal(script->L, "_pxl8_ui");
}
}
void pxl8_script_set_sys(pxl8_script* script, void* sys) {
if (!script) return;
@ -719,16 +703,27 @@ static time_t get_latest_script_mod_time(const char* dir_path) {
while ((entry = readdir(dir)) != NULL) {
if (entry->d_name[0] == '.') continue;
size_t len = strlen(entry->d_name);
bool is_script = (len > 4 && strcmp(entry->d_name + len - 4, ".fnl") == 0) ||
(len > 4 && strcmp(entry->d_name + len - 4, ".lua") == 0);
char full_path[512];
snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name);
if (is_script) {
char full_path[512];
snprintf(full_path, sizeof(full_path), "%s/%s", dir_path, entry->d_name);
time_t mod_time = get_file_mod_time(full_path);
if (mod_time > latest) {
latest = mod_time;
struct stat st;
if (stat(full_path, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
time_t subdir_time = get_latest_script_mod_time(full_path);
if (subdir_time > latest) {
latest = subdir_time;
}
} else {
size_t len = strlen(entry->d_name);
bool is_script = (len > 4 && strcmp(entry->d_name + len - 4, ".fnl") == 0) ||
(len > 4 && strcmp(entry->d_name + len - 4, ".lua") == 0);
if (is_script) {
time_t mod_time = get_file_mod_time(full_path);
if (mod_time > latest) {
latest = mod_time;
}
}
}
}
}

View file

@ -2,7 +2,6 @@
#include "pxl8_gfx.h"
#include "pxl8_types.h"
#include "pxl8_ui.h"
typedef struct pxl8_script pxl8_script;
typedef struct pxl8_script_repl pxl8_script_repl;
@ -20,7 +19,6 @@ void pxl8_script_set_cart_path(pxl8_script* script, const char* cart_path, const
void pxl8_script_set_gfx(pxl8_script* script, pxl8_gfx* gfx);
void pxl8_script_set_input(pxl8_script* script, pxl8_input_state* input);
void pxl8_script_set_sys(pxl8_script* script, void* sys);
void pxl8_script_set_ui(pxl8_script* script, pxl8_ui* ui);
pxl8_result pxl8_script_call_function(pxl8_script* script, const char* name);
pxl8_result pxl8_script_call_function_f32(pxl8_script* script, const char* name, f32 arg);

View file

@ -367,11 +367,45 @@ static void sdl3_set_relative_mouse_mode(void* platform_data, bool enabled) {
}
}
static void sdl3_set_cursor(void* platform_data, pxl8_cursor cursor) {
if (!platform_data) return;
SDL_SystemCursor sdl_cursor;
switch (cursor) {
case PXL8_CURSOR_ARROW:
sdl_cursor = SDL_SYSTEM_CURSOR_DEFAULT;
break;
case PXL8_CURSOR_HAND:
sdl_cursor = SDL_SYSTEM_CURSOR_POINTER;
break;
default:
sdl_cursor = SDL_SYSTEM_CURSOR_DEFAULT;
break;
}
SDL_Cursor* cursor_obj = SDL_CreateSystemCursor(sdl_cursor);
if (cursor_obj) {
SDL_SetCursor(cursor_obj);
}
}
static void sdl3_center_cursor(void* platform_data) {
if (!platform_data) return;
pxl8_sdl3_context* ctx = (pxl8_sdl3_context*)platform_data;
i32 w, h;
if (SDL_GetWindowSize(ctx->window, &w, &h)) {
SDL_WarpMouseInWindow(ctx->window, w / 2, h / 2);
}
}
const pxl8_hal pxl8_hal_sdl3 = {
.create = sdl3_create,
.destroy = sdl3_destroy,
.get_ticks = sdl3_get_ticks,
.center_cursor = sdl3_center_cursor,
.present = sdl3_present,
.set_cursor = sdl3_set_cursor,
.upload_atlas = sdl3_upload_atlas,
.upload_framebuffer = sdl3_upload_framebuffer,
.set_relative_mouse_mode = sdl3_set_relative_mouse_mode,

View file

@ -18,6 +18,8 @@ pxl8_gfx* pxl8_get_gfx(const pxl8* sys);
pxl8_input_state* pxl8_get_input(const pxl8* sys);
pxl8_resolution pxl8_get_resolution(const pxl8* sys);
void pxl8_center_cursor(pxl8* sys);
void pxl8_set_cursor(pxl8* sys, pxl8_cursor cursor);
void pxl8_set_relative_mouse_mode(pxl8* sys, bool enabled);
pxl8_result pxl8_init(pxl8* sys, i32 argc, char* argv[]);

View file

@ -28,6 +28,11 @@ typedef enum pxl8_color_mode {
PXL8_COLOR_MODE_SNES
} pxl8_color_mode;
typedef enum pxl8_cursor {
PXL8_CURSOR_ARROW,
PXL8_CURSOR_HAND
} pxl8_cursor;
typedef enum pxl8_resolution {
PXL8_RESOLUTION_240x160,
PXL8_RESOLUTION_320x180,

View file

@ -59,7 +59,6 @@ pxl8_result pxl8_world_generate(pxl8_world* world, pxl8_gfx* gfx, const pxl8_pro
return result;
}
pxl8_debug("World generation succeeded, setting loaded=true");
world->loaded = true;
return PXL8_OK;
}