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 /src/buffers.c | |
| parent | e930cc6bdc7f62befac063d7d9d016ffb0a64f1a (diff) | |
Hardened API, tetris, MD-View
Diffstat (limited to 'src/buffers.c')
| -rw-r--r-- | src/buffers.c | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/src/buffers.c b/src/buffers.c index e8c1d23..a932712 100644 --- a/src/buffers.c +++ b/src/buffers.c @@ -8,8 +8,15 @@ #include <stdlib.h> #include <string.h> +extern int pclose(FILE *stream); + #define BUFFER_INITIAL_CAP 64 +static int ecex_trace_callbacks_enabled(void) { + const char *v = getenv("ECEX_TRACE_CALLBACKS"); + return v && v[0] && v[0] != '0'; +} + static int buffer_is_word_char(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || @@ -116,6 +123,12 @@ void buffer_free(buffer_t *buffer) { if (!buffer) return; buffer_clear_interactive_actions(buffer); + ecex_buffer_clear_animation(buffer); + ecex_buffer_clear_mouse_handler(buffer); + ecex_buffer_clear_renderer(buffer); + if (buffer->media_pipe) pclose((FILE *)buffer->media_pipe); + free(buffer->media_path); + free(buffer->media_pixels); buffer_undo_stack_clear(buffer->undo_stack, buffer->undo_count); buffer_undo_stack_clear(buffer->redo_stack, buffer->redo_count); free(buffer->undo_stack); @@ -699,3 +712,310 @@ ecex_interactive_line_action_t *buffer_interactive_action_at_line(buffer_t *buff return NULL; } + +int ecex_buffer_set_renderer(buffer_t *buffer, + ecex_buffer_render_fn fn, + void *userdata, + ecex_buffer_userdata_free_fn free_fn, + int flags) { + if (ecex_trace_callbacks_enabled()) { + fprintf(stderr, "ecex-log: buffer_set_renderer buffer=%p fn=%p userdata=%p free=%p flags=%d\n", + (void *)buffer, (void *)fn, userdata, (void *)free_fn, flags); + fflush(stderr); + } + if (!buffer) return ECEX_ERR; + if (buffer->render_userdata_free && buffer->render_userdata && buffer->render_userdata != userdata) { + buffer->render_userdata_free(buffer->render_userdata); + } + buffer->render_fn = fn; + buffer->render_userdata = userdata; + buffer->render_userdata_free = free_fn; + buffer->render_flags = flags; + return ECEX_OK; +} + +int ecex_buffer_clear_renderer(buffer_t *buffer) { + if (ecex_trace_callbacks_enabled()) { + fprintf(stderr, "ecex-log: buffer_clear_renderer buffer=%p userdata=%p free=%p\n", + (void *)buffer, buffer ? buffer->render_userdata : NULL, + buffer ? (void *)buffer->render_userdata_free : NULL); + fflush(stderr); + } + if (!buffer) return ECEX_ERR; + if (buffer->render_userdata_free && buffer->render_userdata) { + buffer->render_userdata_free(buffer->render_userdata); + } + buffer->render_fn = NULL; + buffer->render_userdata = NULL; + buffer->render_userdata_free = NULL; + buffer->render_flags = 0; + return ECEX_OK; +} + +int ecex_buffer_has_renderer(buffer_t *buffer) { + return buffer && buffer->render_fn; +} + +void *ecex_buffer_renderer_userdata(buffer_t *buffer) { + return buffer ? buffer->render_userdata : NULL; +} + + +int ecex_buffer_set_mouse_handler(buffer_t *buffer, + ecex_buffer_mouse_fn fn, + void *userdata, + ecex_buffer_userdata_free_fn free_fn) { + if (ecex_trace_callbacks_enabled()) { + fprintf(stderr, "ecex-log: buffer_set_mouse_handler buffer=%p fn=%p userdata=%p free=%p\n", + (void *)buffer, (void *)fn, userdata, (void *)free_fn); + fflush(stderr); + } + if (!buffer || !fn) return ECEX_ERR; + if (buffer->mouse_userdata_free && buffer->mouse_userdata && buffer->mouse_userdata != userdata) { + buffer->mouse_userdata_free(buffer->mouse_userdata); + } + buffer->mouse_fn = fn; + buffer->mouse_userdata = userdata; + buffer->mouse_userdata_free = free_fn; + return ECEX_OK; +} + +int ecex_buffer_clear_mouse_handler(buffer_t *buffer) { + if (ecex_trace_callbacks_enabled()) { + fprintf(stderr, "ecex-log: buffer_clear_mouse_handler buffer=%p userdata=%p free=%p\n", + (void *)buffer, buffer ? buffer->mouse_userdata : NULL, + buffer ? (void *)buffer->mouse_userdata_free : NULL); + fflush(stderr); + } + if (!buffer) return ECEX_ERR; + if (buffer->mouse_userdata_free && buffer->mouse_userdata) { + buffer->mouse_userdata_free(buffer->mouse_userdata); + } + buffer->mouse_fn = NULL; + buffer->mouse_userdata = NULL; + buffer->mouse_userdata_free = NULL; + return ECEX_OK; +} + +int ecex_buffer_has_mouse_handler(buffer_t *buffer) { + return buffer && buffer->mouse_fn; +} + +void *ecex_buffer_mouse_userdata(buffer_t *buffer) { + return buffer ? buffer->mouse_userdata : NULL; +} + +int ecex_buffer_set_animation(buffer_t *buffer, + ecex_buffer_tick_fn fn, + void *userdata, + ecex_buffer_userdata_free_fn free_fn, + double fps) { + if (ecex_trace_callbacks_enabled()) { + fprintf(stderr, "ecex-log: buffer_set_animation buffer=%p fn=%p userdata=%p free=%p fps=%.3f\n", + (void *)buffer, (void *)fn, userdata, (void *)free_fn, fps); + fflush(stderr); + } + if (!buffer || !fn) return ECEX_ERR; + + if (buffer->tick_userdata_free && buffer->tick_userdata && buffer->tick_userdata != userdata) { + buffer->tick_userdata_free(buffer->tick_userdata); + } + + if (fps <= 0.0) fps = 60.0; + if (fps > 240.0) fps = 240.0; + + buffer->tick_fn = fn; + buffer->tick_ms_fn = NULL; + buffer->tick_userdata = userdata; + buffer->tick_userdata_free = free_fn; + buffer->tick_interval = 1.0 / fps; + buffer->tick_last_time = 0.0; + buffer->tick_enabled = 1; + buffer->tick_uses_ms = 0; + return ECEX_OK; +} + +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) { + if (ecex_trace_callbacks_enabled()) { + fprintf(stderr, "ecex-log: buffer_set_animation_ms buffer=%p fn=%p userdata=%p free=%p fps=%d\n", + (void *)buffer, (void *)fn, userdata, (void *)free_fn, fps); + fflush(stderr); + } + if (!buffer || !fn) return ECEX_ERR; + + if (buffer->tick_userdata_free && buffer->tick_userdata && buffer->tick_userdata != userdata) { + buffer->tick_userdata_free(buffer->tick_userdata); + } + + if (fps <= 0) fps = 60; + if (fps > 240) fps = 240; + + buffer->tick_fn = NULL; + buffer->tick_ms_fn = fn; + buffer->tick_userdata = userdata; + buffer->tick_userdata_free = free_fn; + buffer->tick_interval = 1.0 / (double)fps; + buffer->tick_last_time = 0.0; + buffer->tick_enabled = 1; + buffer->tick_uses_ms = 1; + return ECEX_OK; +} + +int ecex_buffer_clear_animation(buffer_t *buffer) { + if (ecex_trace_callbacks_enabled()) { + fprintf(stderr, "ecex-log: buffer_clear_animation buffer=%p userdata=%p free=%p\n", + (void *)buffer, buffer ? buffer->tick_userdata : NULL, + buffer ? (void *)buffer->tick_userdata_free : NULL); + fflush(stderr); + } + if (!buffer) return ECEX_ERR; + if (buffer->tick_userdata_free && buffer->tick_userdata) { + buffer->tick_userdata_free(buffer->tick_userdata); + } + buffer->tick_fn = NULL; + buffer->tick_ms_fn = NULL; + buffer->tick_userdata = NULL; + buffer->tick_userdata_free = NULL; + buffer->tick_interval = 0.0; + buffer->tick_last_time = 0.0; + buffer->tick_enabled = 0; + buffer->tick_uses_ms = 0; + return ECEX_OK; +} + +int ecex_buffer_is_animating(buffer_t *buffer) { + return buffer && buffer->tick_enabled && (buffer->tick_fn || buffer->tick_ms_fn); +} + +void *ecex_buffer_animation_userdata(buffer_t *buffer) { + return buffer ? buffer->tick_userdata : NULL; +} + +int ecex_tick_animations(ecex_t *ed, double now_seconds) { + if (!ed) return 0; + + int dirty = 0; + for (size_t i = 0; i < ed->buffer_count; i++) { + buffer_t *buffer = ed->buffers[i]; + if (!buffer || !buffer->tick_enabled) continue; + if (buffer->tick_uses_ms) { + if (!buffer->tick_ms_fn) { + if (ecex_trace_callbacks_enabled()) { + fprintf(stderr, "ecex-log: animation_tick_skip_ms_null buffer=%p userdata=%p\n", + (void *)buffer, buffer->tick_userdata); + fflush(stderr); + } + buffer->tick_enabled = 0; + continue; + } + } else { + if (!buffer->tick_fn) { + if (ecex_trace_callbacks_enabled()) { + fprintf(stderr, "ecex-log: animation_tick_skip_null buffer=%p userdata=%p\n", + (void *)buffer, buffer->tick_userdata); + fflush(stderr); + } + buffer->tick_enabled = 0; + continue; + } + } + + if (buffer->tick_last_time <= 0.0) { + buffer->tick_last_time = now_seconds; + } + + double interval = buffer->tick_interval > 0.0 ? buffer->tick_interval : (1.0 / 60.0); + if (now_seconds - buffer->tick_last_time + 0.000001 < interval) { + continue; + } + + buffer->tick_last_time = now_seconds; + int tick_dirty = 0; + if (buffer->tick_uses_ms) { + int now_ms = (int)(now_seconds * 1000.0); + if (ecex_trace_callbacks_enabled()) { + fprintf(stderr, "ecex-log: animation_tick_ms_enter buffer=%p fn=%p userdata=%p now_ms=%d\n", + (void *)buffer, (void *)buffer->tick_ms_fn, buffer->tick_userdata, now_ms); + fflush(stderr); + } + tick_dirty = buffer->tick_ms_fn(ed, buffer, now_ms, buffer->tick_userdata); + if (ecex_trace_callbacks_enabled()) { + fprintf(stderr, "ecex-log: animation_tick_ms_leave buffer=%p result=%d\n", + (void *)buffer, tick_dirty); + fflush(stderr); + } + } else { + if (ecex_trace_callbacks_enabled()) { + fprintf(stderr, "ecex-log: animation_tick_enter buffer=%p fn=%p userdata=%p now=%.6f\n", + (void *)buffer, (void *)buffer->tick_fn, buffer->tick_userdata, now_seconds); + fflush(stderr); + } + tick_dirty = buffer->tick_fn(ed, buffer, now_seconds, buffer->tick_userdata); + if (ecex_trace_callbacks_enabled()) { + fprintf(stderr, "ecex-log: animation_tick_leave buffer=%p result=%d\n", + (void *)buffer, tick_dirty); + fflush(stderr); + } + } + if (tick_dirty != 0) { + dirty = 1; + } + } + + return dirty; +} + + +int ecex_buffer_replace_text(buffer_t *buffer, const char *text) { + return buffer_set_text(buffer, text ? text : ""); +} + +void ecex_buffer_set_modified(buffer_t *buffer, int modified) { + if (buffer) buffer->modified = modified ? 1 : 0; +} + +int ecex_buffer_text_len(buffer_t *buffer) { + if (!buffer) return 0; + return buffer->len > (size_t)2147483647 ? 2147483647 : (int)buffer->len; +} + +int ecex_buffer_scroll_line(buffer_t *buffer) { + if (!buffer) return 0; + return buffer->scroll_line > (size_t)2147483647 ? 2147483647 : (int)buffer->scroll_line; +} + +int ecex_buffer_line_count_i(buffer_t *buffer) { + size_t n = buffer_line_count(buffer); + return n > (size_t)2147483647 ? 2147483647 : (int)n; +} + +int ecex_buffer_line_copy(buffer_t *buffer, int line, char *out, int out_cap) { + size_t current = 0; + size_t pos = 0; + size_t start; + size_t end; + size_t n; + + if (!out || out_cap <= 0) return ECEX_ERR; + out[0] = '\0'; + if (!buffer || !buffer->data || line < 0) return ECEX_ERR; + + while (current < (size_t)line && pos < buffer->len) { + if (buffer->data[pos++] == '\n') current++; + } + if (current != (size_t)line) return ECEX_ERR; + + start = pos; + while (pos < buffer->len && buffer->data[pos] != '\n') pos++; + end = pos; + while (end > start && (buffer->data[end - 1] == '\n' || buffer->data[end - 1] == '\r')) end--; + n = end - start; + if (n >= (size_t)out_cap) n = (size_t)out_cap - 1; + if (n > 0) memcpy(out, buffer->data + start, n); + out[n] = '\0'; + return (int)n; +} |
