aboutsummaryrefslogtreecommitdiff
path: root/src/buffers.c
diff options
context:
space:
mode:
authorDavid Moc <personal@cdatgoose.org>2026-05-31 03:47:04 +0200
committerDavid Moc <personal@cdatgoose.org>2026-05-31 03:47:04 +0200
commit6aeaa171dc1ca43392f53cbd02097f76e1b1c5a0 (patch)
treeb16f559f5a701123ebe7b15ecebb9325263b4a3c /src/buffers.c
parente930cc6bdc7f62befac063d7d9d016ffb0a64f1a (diff)
Hardened API, tetris, MD-View
Diffstat (limited to 'src/buffers.c')
-rw-r--r--src/buffers.c320
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;
+}