diff options
| author | David Moc <personal@cdatgoose.org> | 2026-05-31 03:47:04 +0200 |
|---|---|---|
| committer | David Moc <personal@cdatgoose.org> | 2026-05-31 03:47:04 +0200 |
| commit | 6aeaa171dc1ca43392f53cbd02097f76e1b1c5a0 (patch) | |
| tree | b16f559f5a701123ebe7b15ecebb9325263b4a3c /include/ecex.h | |
| parent | e930cc6bdc7f62befac063d7d9d016ffb0a64f1a (diff) | |
Hardened API, tetris, MD-View
Diffstat (limited to 'include/ecex.h')
| -rw-r--r-- | include/ecex.h | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/include/ecex.h b/include/ecex.h index cefee9b..efcbe5a 100644 --- a/include/ecex.h +++ b/include/ecex.h @@ -4,10 +4,93 @@ #include "types.h" #include "buffers.h" #include "eval.h" +#include "path.h" +#include "media.h" + + +typedef struct ecex_config_command { + const char *name; + ecex_command_fn fn; +} ecex_config_command_t; + +typedef struct ecex_config_keybind { + const char *key; + const char *command; +} ecex_config_keybind_t; + +typedef struct ecex_config_mode_keybind { + const char *mode; + const char *key; + const char *command; +} ecex_config_mode_keybind_t; ecex_t *ecex_new(void); void ecex_free(ecex_t *ed); +/* Small runtime services exported to JIT configs/plugins. Prefer these over + * calling libc directly from config code; the tiny JIT resolver is intentionally + * conservative and host-exported wrappers are more reliable. */ +void *ecex_config_alloc(size_t size); +void *ecex_config_calloc(size_t count, size_t size); +void ecex_config_free(void *ptr); +double ecex_time_seconds(void); +void ecex_log(const char *message); +void ecex_log_int(const char *message, int value); +void ecex_log_double(const char *message, double value); +void ecex_log_ptr(const char *message, const void *ptr); +void ecex_mem_zero(void *ptr, size_t size); +int ecex_i32_get(const int *items, size_t index); +void ecex_i32_set(int *items, size_t index, int value); +int ecex_prng_next_bounded(unsigned int *state, int bound); +int ecex_random_bounded(int bound); +int ecex_tetris_shape_cell(int piece, int rot, int col, int row); + +/* Host-owned variable registry for JIT plugins. Plugins can keep persistent + * scalar values, dynamic arrays, or static host buffers behind a stable owner + * pointer and a name instead of relying on fragile JIT struct/array storage. */ +void *ecex_var_get(ecex_t *ed, void *owner, const char *name); +void *ecex_var_get_or_alloc(ecex_t *ed, void *owner, const char *name, size_t count, size_t elem_size); +int ecex_var_bind_static(ecex_t *ed, void *owner, const char *name, void *data, size_t count, size_t elem_size); +int ecex_var_free(ecex_t *ed, void *owner, const char *name); +int ecex_var_free_owner(ecex_t *ed, void *owner); +int ecex_var_i32_get(ecex_t *ed, void *owner, const char *name, size_t index, int fallback); +int ecex_var_i32_set(ecex_t *ed, void *owner, const char *name, size_t index, int value); +int ecex_var_i32(ecex_t *ed, void *owner, const char *name, int fallback); +int ecex_var_i32_set_scalar(ecex_t *ed, void *owner, const char *name, int value); + +/* Host-owned object allocator for plugin state. Returned pointers are tracked + * by the editor and can be freed through the API. Prefer this for callback + * userdata over plugin/local allocations when the object has callback lifetime. + */ +void *ecex_object_alloc(ecex_t *ed, size_t size); +void *ecex_object_calloc(ecex_t *ed, size_t count, size_t size); +int ecex_object_free(ecex_t *ed, void *object); +int ecex_object_valid(ecex_t *ed, void *object); +int ecex_object_i32_get(ecex_t *ed, void *object, size_t byte_offset, int fallback); +int ecex_object_i32_set(ecex_t *ed, void *object, size_t byte_offset, int value); +void *ecex_object_ptr_get(ecex_t *ed, void *object, size_t byte_offset); +int ecex_object_ptr_set(ecex_t *ed, void *object, size_t byte_offset, void *value); + +/* Host-owned text registry for plugins that need to render arbitrary text. + * Plugins copy text into host storage, then draw by owner/id. This avoids + * passing JIT-owned literals or stack strings into the normal text renderer. */ +int ecex_text_set(ecex_t *ed, void *owner, int id, const char *text, int len); +int ecex_text_set_buffer_title(ecex_t *ed, void *owner, int id, buffer_t *buffer); +int ecex_text_free(ecex_t *ed, void *owner, int id); +int ecex_text_free_owner(ecex_t *ed, void *owner); + +int ecex_buffer_text_len(buffer_t *buffer); +int ecex_buffer_scroll_line(buffer_t *buffer); +int ecex_buffer_line_count_i(buffer_t *buffer); +int ecex_buffer_line_copy(buffer_t *buffer, int line, char *out, int out_cap); +int ecex_markdown_draw_line_from_buffer_i(ecex_draw_context_t *ctx, void *owner, buffer_t *buffer, int line, int y, int in_code); +int ecex_markdown_body_y_i(ecex_draw_context_t *ctx); +int ecex_draw_context_height_i(ecex_draw_context_t *ctx); +int ecex_draw_context_line_height_i(ecex_draw_context_t *ctx); + +int ecex_register_file_handler(ecex_t *ed, const char *extension, ecex_file_handler_fn fn); +int ecex_run_file_handlers(ecex_t *ed, buffer_t *buffer); + int ecex_reserve_buffers(ecex_t *ed, size_t needed); int ecex_add_buffer(ecex_t *ed, buffer_t *buffer); @@ -20,6 +103,7 @@ buffer_t *ecex_find_buffer(ecex_t *ed, const char *name); int ecex_switch_buffer(ecex_t *ed, const char *name); buffer_t *ecex_current_buffer(ecex_t *ed); +buffer_t *ecex_other_buffer(ecex_t *ed); ecex_window_t *ecex_current_window(ecex_t *ed); size_t ecex_window_count(ecex_t *ed); int ecex_sync_current_buffer(ecex_t *ed); @@ -35,6 +119,9 @@ int ecex_balance_windows(ecex_t *ed); int ecex_next_buffer(ecex_t *ed); int ecex_previous_buffer(ecex_t *ed); int ecex_kill_buffer(ecex_t *ed, const char *name); +int ecex_kill_buffer_force(ecex_t *ed, const char *name); +int ecex_has_modified_buffers(ecex_t *ed); +int ecex_validate_bindings(ecex_t *ed); int ecex_keep_jit_module(ecex_t *ed, void *module); @@ -42,6 +129,12 @@ int ecex_set_config_path(ecex_t *ed, const char *path); const char *ecex_config_path(ecex_t *ed); int ecex_reload_config(ecex_t *ed); +int ecex_config_register_commands(ecex_t *ed, const ecex_config_command_t *commands, size_t count); +int ecex_config_bind_keys(ecex_t *ed, const ecex_config_keybind_t *bindings, size_t count); +int ecex_config_bind_mode_keys(ecex_t *ed, const ecex_config_mode_keybind_t *bindings, size_t count); +int ecex_config_define_modes(ecex_t *ed, const char *const *modes, size_t count); +int ecex_apply_theme(ecex_t *ed, const ecex_theme_t *theme); + int ecex_register_command(ecex_t *ed, const char *name, ecex_command_fn fn); int ecex_execute_command(ecex_t *ed, const char *name); void ecex_set_clipboard_callbacks(ecex_t *ed, @@ -77,6 +170,100 @@ int ecex_interactive_append_line(ecex_t *ed, void *userdata); int ecex_interactive_activate_current_line(ecex_t *ed); +/* + * Attach an immediate-mode renderer to a buffer. The callback runs while the + * buffer's window is being drawn and may call ecex_draw_* functions with + * coordinates local to that window. Pass ECEX_RENDER_REPLACE_CONTENT to draw a + * fully custom buffer; pass ECEX_RENDER_OVERLAY to draw on top of normal text. + */ +int ecex_buffer_set_renderer(buffer_t *buffer, + ecex_buffer_render_fn fn, + void *userdata, + ecex_buffer_userdata_free_fn free_fn, + int flags); +int ecex_buffer_clear_renderer(buffer_t *buffer); +int ecex_buffer_has_renderer(buffer_t *buffer); +void *ecex_buffer_renderer_userdata(buffer_t *buffer); + +/* + * Optional mouse/pointer input for rendered buffers. Coordinates are integer + * pixels local to the buffer window. Return nonzero from the callback when the + * event changes state and should trigger a redraw. Prefer this host-dispatched + * path over reading windowing-system globals from a plugin. + */ +int ecex_buffer_set_mouse_handler(buffer_t *buffer, + ecex_buffer_mouse_fn fn, + void *userdata, + ecex_buffer_userdata_free_fn free_fn); +int ecex_buffer_clear_mouse_handler(buffer_t *buffer); +int ecex_buffer_has_mouse_handler(buffer_t *buffer); +void *ecex_buffer_mouse_userdata(buffer_t *buffer); + +/* + * Host-driven animation for rendered buffers. The editor calls tick_fn at up to + * fps while the main loop is alive; returning nonzero marks the frame dirty and + * triggers a redraw. Use this instead of timers, sleep, clock calls, or threads + * in CCDJIT plugins. fps <= 0 defaults to 60; values above 240 are clamped. + */ +int ecex_buffer_set_animation(buffer_t *buffer, + ecex_buffer_tick_fn fn, + void *userdata, + ecex_buffer_userdata_free_fn free_fn, + double fps); +int ecex_buffer_set_animation_ms(buffer_t *buffer, + ecex_buffer_tick_ms_fn fn, + void *userdata, + ecex_buffer_userdata_free_fn free_fn, + int fps); +int ecex_buffer_clear_animation(buffer_t *buffer); +int ecex_buffer_is_animating(buffer_t *buffer); +void *ecex_buffer_animation_userdata(buffer_t *buffer); +int ecex_tick_animations(ecex_t *ed, double now_seconds); + +int ecex_buffer_replace_text(buffer_t *buffer, const char *text); +void ecex_buffer_set_modified(buffer_t *buffer, int modified); + +/* Drawing helpers. x/y are local to the buffer window; ecex_draw_text uses + * top-left coordinates. ecex_draw_rgba expects tightly packed RGBA8 pixels. */ +void ecex_draw_set_color(ecex_draw_context_t *ctx, float r, float g, float b, float a); +void ecex_draw_rect(ecex_draw_context_t *ctx, float x, float y, float w, float h); +void ecex_draw_rect_outline(ecex_draw_context_t *ctx, float x, float y, float w, float h, float thickness); +void ecex_draw_line(ecex_draw_context_t *ctx, float x1, float y1, float x2, float y2, float thickness); +void ecex_draw_text(ecex_draw_context_t *ctx, float x, float y, const char *text); +void ecex_draw_text_aligned(ecex_draw_context_t *ctx, float x, float y, float w, const char *text, int align); +float ecex_draw_text_width(ecex_draw_context_t *ctx, const char *text); +void ecex_draw_rgba(ecex_draw_context_t *ctx, + float x, + float y, + float w, + float h, + const unsigned char *rgba, + int image_w, + int image_h); + +/* CCDJIT-safe integer drawing wrappers. Prefer these from plugins when the + * JIT ABI is known to be fragile with float/double arguments. Colors are + * 0..255 RGBA and coordinates are integer pixels local to the buffer window. */ +void ecex_draw_color_rgba8(ecex_draw_context_t *ctx, int r, int g, int b, int a); +void ecex_draw_rect_i(ecex_draw_context_t *ctx, int x, int y, int w, int h); +void ecex_draw_rect_outline_i(ecex_draw_context_t *ctx, int x, int y, int w, int h, int thickness); +void ecex_draw_line_i(ecex_draw_context_t *ctx, int x1, int y1, int x2, int y2, int thickness); +void ecex_draw_text_i(ecex_draw_context_t *ctx, int x, int y, const char *text); +void ecex_draw_text_id_i(ecex_draw_context_t *ctx, void *owner, int id, int x, int y); + +void ecex_draw_markdown_canvas_i(ecex_draw_context_t *ctx, void *owner, int title_id, int x, int y, int w, int line_h); +void ecex_draw_markdown_text_i(ecex_draw_context_t *ctx, void *owner, int text_id, int x, int y, int w, int line_h, int style); +/* Lower-arity Markdown helpers preferred by CCDJIT plugins. Some JIT call + * paths are fragile with 7+ arguments, so these derive layout from ctx. */ +void ecex_draw_markdown_canvas_auto_i(ecex_draw_context_t *ctx, void *owner, int title_id); +void ecex_draw_markdown_line_auto_i(ecex_draw_context_t *ctx, void *owner, int text_id, int y, int style); + +/* CCDJIT-safe host-owned text helpers. Plugins pass integer ids instead of + * pointers to JIT-owned string literals/stack buffers. */ +void ecex_draw_label_i(ecex_draw_context_t *ctx, int x, int y, int label_id); +void ecex_draw_stat_i(ecex_draw_context_t *ctx, int x, int y, int label_id, int value); +void ecex_draw_tetris_preview_i(ecex_draw_context_t *ctx, int piece, int x, int y, int cell, int alpha); + int ecex_find_file(ecex_t *ed, const char *path); int ecex_save_current_buffer(ecex_t *ed); int ecex_write_current_buffer(ecex_t *ed, const char *path); @@ -121,4 +308,36 @@ void ecex_set_search_bg_color(ecex_t *ed, float r, float g, float b); void ecex_set_line_numbers_enabled(ecex_t *ed, int enabled); void ecex_set_current_line_enabled(ecex_t *ed, int enabled); + +/* + * Small config/plugin convenience layer. + * + * Config files are still plain C and must export ecex_config_init(ecex_t *ed), + * but these macros make them safer to write: every registration/bind operation + * is checked and failures abort config loading instead of being ignored. + */ +#define ECEX_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0])) +#define ECEX_RGB8(r, g, b) ((float)(r) / 255.0f), ((float)(g) / 255.0f), ((float)(b) / 255.0f) +#define ECEX_CONFIG_BEGIN int ecex_config_init(ecex_t *ed) { +#define ECEX_CONFIG_END return 0; } +#define ECEX_PLUGIN_BEGIN(name) int name(ecex_t *ed) { +#define ECEX_PLUGIN_END return 0; } +#define ECEX_CONFIG_TRY(expr) \ + do { \ + if ((expr) != 0) return -1; \ + } while (0) +#define ECEX_CONFIG_COMMAND(name, fn) ECEX_CONFIG_TRY(ecex_register_command(ed, (name), (fn))) +#define ECEX_CONFIG_BIND(key, command) ECEX_CONFIG_TRY(ecex_bind_key(ed, (key), (command))) +#define ECEX_CONFIG_MODE(name) \ + do { \ + if (ecex_define_major_mode(ed, (name)) == 0) return -1; \ + } while (0) +#define ECEX_CONFIG_MODE_BIND(mode, key, command) ECEX_CONFIG_TRY(ecex_bind_mode_key(ed, (mode), (key), (command))) +#define ECEX_CONFIG_COMMANDS(commands) ECEX_CONFIG_TRY(ecex_config_register_commands(ed, (commands), ECEX_ARRAY_COUNT(commands))) +#define ECEX_CONFIG_BINDS(bindings) ECEX_CONFIG_TRY(ecex_config_bind_keys(ed, (bindings), ECEX_ARRAY_COUNT(bindings))) +#define ECEX_CONFIG_MODE_BINDS(bindings) ECEX_CONFIG_TRY(ecex_config_bind_mode_keys(ed, (bindings), ECEX_ARRAY_COUNT(bindings))) +#define ECEX_CONFIG_MODES(modes) ECEX_CONFIG_TRY(ecex_config_define_modes(ed, (modes), ECEX_ARRAY_COUNT(modes))) +#define ECEX_CONFIG_THEME(theme_ptr) ECEX_CONFIG_TRY(ecex_apply_theme(ed, (theme_ptr))) +#define ECEX_CONFIG_INCLUDE(plugin_init_fn) ECEX_CONFIG_TRY((plugin_init_fn)(ed)) + #endif |
