From 6aeaa171dc1ca43392f53cbd02097f76e1b1c5a0 Mon Sep 17 00:00:00 2001 From: David Moc Date: Sun, 31 May 2026 03:47:04 +0200 Subject: Hardened API, tetris, MD-View --- config/render_demo.c | 302 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 config/render_demo.c (limited to 'config/render_demo.c') diff --git a/config/render_demo.c b/config/render_demo.c new file mode 100644 index 0000000..761b552 --- /dev/null +++ b/config/render_demo.c @@ -0,0 +1,302 @@ +#include "ecex.h" + +#define RENDER_DEMO_BUF "*render-demo*" +#define RENDER_DEMO_VAR_X "x_milli" +#define RENDER_DEMO_VAR_Y "y_milli" +#define RENDER_DEMO_VAR_TARGET_X "target_x_milli" +#define RENDER_DEMO_VAR_TARGET_Y "target_y_milli" +#define RENDER_DEMO_VAR_MOVING "moving" +#define RENDER_DEMO_VAR_BOX_X "box_x" +#define RENDER_DEMO_VAR_BOX_Y "box_y" +#define RENDER_DEMO_VAR_BOX_SIZE "box_size" +#define RENDER_DEMO_VAR_TRAVEL_X "travel_x" +#define RENDER_DEMO_VAR_TRAVEL_Y "travel_y" +#define RENDER_DEMO_VAR_AREA_X "area_x" +#define RENDER_DEMO_VAR_AREA_Y "area_y" + +#define RENDER_DEMO_LABEL_TITLE 20 +#define RENDER_DEMO_LABEL_SUBTITLE 21 +#define RENDER_DEMO_LABEL_SAFE_DRAW 22 +#define RENDER_DEMO_LABEL_ANIM 23 +#define RENDER_DEMO_LABEL_POSITION 24 +#define RENDER_DEMO_LABEL_CLICK 25 + +typedef struct render_demo_state { + ecex_t *ed; + int log_draw_count; +} render_demo_state_t; + +static int render_demo_get(ecex_t *ed, render_demo_state_t *s, const char *name, int fallback) { + if (!ed || !s || !name) return fallback; + return ecex_var_i32(ed, s, name, fallback); +} + +static void render_demo_set(ecex_t *ed, render_demo_state_t *s, const char *name, int value) { + if (!ed || !s || !name) return; + ecex_var_i32_set_scalar(ed, s, name, value); +} + +static void render_demo_reset(ecex_t *ed, render_demo_state_t *s) { + ecex_log_ptr("render_demo_reset: state=", s); + if (!ed || !s) return; + + /* Store mutable demo state in the host variable registry. This mirrors the + * Tetris plugin flow and avoids relying on CCDJIT struct-field writes for + * values that must survive renderer, animation, and mouse callbacks. + * Values are fixed-point integers: 0..1000 represents the available travel + * range inside the demo frame. */ + render_demo_set(ed, s, RENDER_DEMO_VAR_X, 0); + render_demo_set(ed, s, RENDER_DEMO_VAR_Y, 500); + render_demo_set(ed, s, RENDER_DEMO_VAR_TARGET_X, 0); + render_demo_set(ed, s, RENDER_DEMO_VAR_TARGET_Y, 500); + render_demo_set(ed, s, RENDER_DEMO_VAR_MOVING, 0); + s->log_draw_count = 0; +} + +static int render_demo_draw(ecex_t *ed, buffer_t *buffer, ecex_draw_context_t *ctx, void *userdata) { + render_demo_state_t *s = (render_demo_state_t *)userdata; + int w; + int h; + int cx; + int cy; + int cw; + int ch; + int line_h; + int box; + int travel; + int x_milli; + int y_milli; + int box_x; + int box_y; + int travel_y; + int area_x; + int area_y; + + (void)buffer; + if (!ed || !ctx || !s) return 0; + + s->log_draw_count++; + if (s->log_draw_count <= 4 || (s->log_draw_count % 60) == 0) { + ecex_log_int("render_demo_draw: count=", s->log_draw_count); + } + + w = (int)ctx->w; + h = (int)ctx->h; + cx = (int)ctx->content_x; + cy = (int)ctx->content_y; + cw = (int)ctx->content_w; + ch = (int)ctx->content_h; + line_h = (int)ctx->line_height; + if (line_h < 18) line_h = 18; + if (cw < 1) cw = w - cx * 2; + if (ch < 1) ch = h - cy * 2; + if (cw < 1) cw = 1; + if (ch < 1) ch = 1; + + ecex_draw_color_rgba8(ctx, 26, 28, 36, 255); + ecex_draw_rect_i(ctx, 0, 0, w, h); + + ecex_draw_color_rgba8(ctx, 48, 96, 180, 255); + ecex_draw_rect_i(ctx, cx, cy, (cw * 55) / 100, line_h * 2); + + ecex_draw_color_rgba8(ctx, 242, 230, 191, 255); + ecex_draw_rect_outline_i(ctx, cx, cy, cw, ch, 2); + ecex_draw_label_i(ctx, cx + 12, cy + 10, RENDER_DEMO_LABEL_TITLE); + + ecex_draw_color_rgba8(ctx, 229, 89, 64, 255); + ecex_draw_line_i(ctx, cx, cy + ch, cx + cw, cy, 3); + + ecex_draw_color_rgba8(ctx, 160, 230, 180, 255); + ecex_draw_label_i(ctx, cx + 12, cy + line_h * 3, RENDER_DEMO_LABEL_SUBTITLE); + ecex_draw_label_i(ctx, cx + 12, cy + line_h * 4, RENDER_DEMO_LABEL_SAFE_DRAW); + ecex_draw_label_i(ctx, cx + 12, cy + line_h * 5, RENDER_DEMO_LABEL_ANIM); + ecex_draw_label_i(ctx, cx + 12, cy + line_h * 6, RENDER_DEMO_LABEL_CLICK); + + x_milli = render_demo_get(ed, s, RENDER_DEMO_VAR_X, 0); + y_milli = render_demo_get(ed, s, RENDER_DEMO_VAR_Y, 500); + ecex_draw_stat_i(ctx, cx + 12, cy + line_h * 8, RENDER_DEMO_LABEL_POSITION, x_milli); + + box = line_h * 2; + if (box < 36) box = 36; + if (box > 72) box = 72; + travel = cw - box - 24; + if (travel < 0) travel = 0; + area_x = cx + 12; + area_y = cy + line_h * 10; + travel_y = ch - (area_y - cy) - box - 12; + if (travel_y < 0) travel_y = 0; + if (x_milli < 0) x_milli = 0; + if (x_milli > 1000) x_milli = 1000; + if (y_milli < 0) y_milli = 0; + if (y_milli > 1000) y_milli = 1000; + box_x = area_x + (travel * x_milli) / 1000; + box_y = area_y + (travel_y * y_milli) / 1000; + + render_demo_set(ed, s, RENDER_DEMO_VAR_BOX_X, box_x); + render_demo_set(ed, s, RENDER_DEMO_VAR_BOX_Y, box_y); + render_demo_set(ed, s, RENDER_DEMO_VAR_BOX_SIZE, box); + render_demo_set(ed, s, RENDER_DEMO_VAR_TRAVEL_X, travel); + render_demo_set(ed, s, RENDER_DEMO_VAR_TRAVEL_Y, travel_y); + render_demo_set(ed, s, RENDER_DEMO_VAR_AREA_X, area_x); + render_demo_set(ed, s, RENDER_DEMO_VAR_AREA_Y, area_y); + + ecex_draw_color_rgba8(ctx, 70, 76, 90, 255); + ecex_draw_rect_outline_i(ctx, area_x, area_y, travel + box, travel_y + box, 1); + + ecex_draw_color_rgba8(ctx, 89, 242, 140, 255); + ecex_draw_rect_i(ctx, box_x, box_y, box, box); + ecex_draw_color_rgba8(ctx, 20, 24, 30, 255); + ecex_draw_rect_outline_i(ctx, box_x, box_y, box, box, 2); + + return 0; +} + + +static int render_demo_clamp_milli(int value) { + if (value < 0) return 0; + if (value > 1000) return 1000; + return value; +} + +static int render_demo_delta_step(int current, int target) { + int delta = target - current; + int step; + if (delta < 0) { + if (delta >= -4) return target; + step = delta / 5; + if (step == 0) step = -1; + return current + step; + } + if (delta <= 4) return target; + step = delta / 5; + if (step == 0) step = 1; + return current + step; +} + +static int render_demo_tick(ecex_t *ed, buffer_t *buffer, int now_ms, void *userdata) { + render_demo_state_t *s = (render_demo_state_t *)userdata; + int x; + int y; + int target_x; + int target_y; + int next_x; + int next_y; + + (void)buffer; + (void)now_ms; + if (!ed || !s) return 0; + if (!render_demo_get(ed, s, RENDER_DEMO_VAR_MOVING, 0)) return 0; + + x = render_demo_get(ed, s, RENDER_DEMO_VAR_X, 0); + y = render_demo_get(ed, s, RENDER_DEMO_VAR_Y, 500); + target_x = render_demo_get(ed, s, RENDER_DEMO_VAR_TARGET_X, x); + target_y = render_demo_get(ed, s, RENDER_DEMO_VAR_TARGET_Y, y); + + next_x = render_demo_delta_step(x, target_x); + next_y = render_demo_delta_step(y, target_y); + render_demo_set(ed, s, RENDER_DEMO_VAR_X, render_demo_clamp_milli(next_x)); + render_demo_set(ed, s, RENDER_DEMO_VAR_Y, render_demo_clamp_milli(next_y)); + + if (next_x == target_x && next_y == target_y) { + render_demo_set(ed, s, RENDER_DEMO_VAR_MOVING, 0); + ecex_log("render_demo_tick: target reached"); + } + return 1; +} + +static int render_demo_mouse(ecex_t *ed, buffer_t *buffer, int event, int x, int y, int button, void *userdata) { + render_demo_state_t *s = (render_demo_state_t *)userdata; + int box; + int area_x; + int area_y; + int travel_x; + int travel_y; + int nx; + int ny; + + (void)buffer; + if (!ed || !s || button != ECEX_MOUSE_BUTTON_LEFT) return 0; + if (event != ECEX_MOUSE_PRESS) return 0; + + box = render_demo_get(ed, s, RENDER_DEMO_VAR_BOX_SIZE, 0); + area_x = render_demo_get(ed, s, RENDER_DEMO_VAR_AREA_X, 0); + area_y = render_demo_get(ed, s, RENDER_DEMO_VAR_AREA_Y, 0); + travel_x = render_demo_get(ed, s, RENDER_DEMO_VAR_TRAVEL_X, 0); + travel_y = render_demo_get(ed, s, RENDER_DEMO_VAR_TRAVEL_Y, 0); + if (box <= 0) return 0; + + if (x < area_x || x >= area_x + travel_x + box || + y < area_y || y >= area_y + travel_y + box) { + return 0; + } + + nx = x - area_x - box / 2; + ny = y - area_y - box / 2; + if (travel_x <= 0) render_demo_set(ed, s, RENDER_DEMO_VAR_TARGET_X, 0); + else render_demo_set(ed, s, RENDER_DEMO_VAR_TARGET_X, render_demo_clamp_milli((nx * 1000) / travel_x)); + if (travel_y <= 0) render_demo_set(ed, s, RENDER_DEMO_VAR_TARGET_Y, 0); + else render_demo_set(ed, s, RENDER_DEMO_VAR_TARGET_Y, render_demo_clamp_milli((ny * 1000) / travel_y)); + + render_demo_set(ed, s, RENDER_DEMO_VAR_MOVING, 1); + ecex_log("render_demo_mouse: target set"); + return 1; +} + +static void render_demo_free_state(void *userdata) { + render_demo_state_t *s = (render_demo_state_t *)userdata; + if (!s) return; + ecex_var_free_owner(s->ed, s); + ecex_config_free(s); +} + +static int cmd_render_demo(ecex_t *ed) { + buffer_t *buffer; + render_demo_state_t *s; + + ecex_log("cmd_render_demo: enter"); + if (!ed) return -1; + + buffer = ecex_find_buffer(ed, RENDER_DEMO_BUF); + if (!buffer) buffer = ecex_create_interactive_buffer(ed, RENDER_DEMO_BUF); + if (!buffer) return -1; + + s = (render_demo_state_t *)ecex_buffer_renderer_userdata(buffer); + if (!s) { + s = (render_demo_state_t *)ecex_config_calloc(1, sizeof(*s)); + if (!s) return -1; + s->ed = ed; + render_demo_reset(ed, s); + + if (ecex_buffer_set_renderer(buffer, render_demo_draw, s, render_demo_free_state, ECEX_RENDER_REPLACE_CONTENT) != 0) { + render_demo_free_state(s); + return -1; + } + if (ecex_buffer_set_mouse_handler(buffer, render_demo_mouse, s, 0) != 0) { + ecex_buffer_clear_renderer(buffer); + return -1; + } + if (ecex_buffer_set_animation_ms(buffer, render_demo_tick, s, 0, 60) != 0) { + ecex_buffer_clear_mouse_handler(buffer); + ecex_buffer_clear_renderer(buffer); + return -1; + } + } + + if (ecex_buffer_replace_text(buffer, "Render demo. The renderer replaces normal buffer content.\n") != 0) { + return -1; + } + ecex_buffer_set_modified(buffer, 0); + + return ecex_switch_buffer(ed, RENDER_DEMO_BUF); +} + +int ecex_render_demo_plugin(ecex_t *ed) { + ECEX_CONFIG_COMMAND("render-demo", cmd_render_demo); + return 0; +} + +#ifndef ECEX_NO_STANDALONE_CONFIG +ECEX_CONFIG_BEGIN + ECEX_CONFIG_INCLUDE(ecex_render_demo_plugin); +ECEX_CONFIG_END +#endif -- cgit v1.2.3