#include "input.h" #include #include static const char *event_text(const struct yasl_input_event *ev, size_t *len) { switch (ev->kind) { case YASL_INPUT_TEXT: *len = ev->text.len; return ev->text.text; case YASL_INPUT_PASTE: *len = ev->paste.len; return ev->paste.text; case YASL_INPUT_DROP_FILES: *len = ev->drop_files.len; return ev->drop_files.files; default: *len = 0; return NULL; } } static void set_text(struct yasl_input_event *ev, const char *text) { switch (ev->kind) { case YASL_INPUT_TEXT: ev->text.text = text; break; case YASL_INPUT_PASTE: ev->paste.text = text; break; case YASL_INPUT_DROP_FILES: ev->drop_files.files = text; break; default: break; } } static void discard_pending(struct input_queue *q) { for (size_t i = 0; i < q->count; i++) { size_t len; free((void *)event_text(&q->events[(q->head + i) % q->capacity], &len)); } q->head = q->count = 0; } bool input_queue_push(struct input_queue *q, const struct yasl_input_event *ev) { struct yasl_input_event copy = *ev; char *owned = NULL; if (ev->kind == YASL_INPUT_TEXT || ev->kind == YASL_INPUT_PASTE || ev->kind == YASL_INPUT_DROP_FILES) { size_t len; const char *text = event_text(ev, &len); if ((!text && len) || len == SIZE_MAX) goto failed; owned = malloc(len + 1); if (!owned) goto failed; if (len) memcpy(owned, text, len); owned[len] = '\0'; set_text(©, owned); } if (q->count == q->capacity) { size_t capacity = q->capacity ? q->capacity * 2 : 32; if (capacity < q->capacity || capacity > SIZE_MAX / sizeof(*q->events)) goto failed; struct yasl_input_event *events = malloc(capacity * sizeof(*events)); if (!events) goto failed; if (q->events && q->count > 0) { size_t first = q->capacity - q->head; if (first > q->count) first = q->count; memcpy(events, q->events + q->head, first * sizeof(*events)); if (q->count > first) memcpy(events + first, q->events, (q->count - first) * sizeof(*events)); } free(q->events); q->events = events; q->capacity = capacity; q->head = 0; } q->events[(q->head + q->count) & (q->capacity - 1)] = copy; q->count++; return true; failed: free(owned); /* A missing release must never silently leave app input state stuck. * Discard the old stream and deliver an allocation-free reset first. */ discard_pending(q); q->reset_surface = ev->surface; q->reset_pending = true; return false; } bool input_queue_pop(struct input_queue *q, struct yasl_input_event *ev) { if (!ev || (!q->count && !q->reset_pending)) return false; if (q->last_text) { free(q->last_text); q->last_text = NULL; } if (q->reset_pending) { *ev = (struct yasl_input_event){ .surface = q->reset_surface, .kind = YASL_INPUT_RESET, }; q->reset_pending = false; return true; } *ev = q->events[q->head]; if (ev->kind == YASL_INPUT_TEXT || ev->kind == YASL_INPUT_PASTE || ev->kind == YASL_INPUT_DROP_FILES) { size_t len; q->last_text = (char *)event_text(ev, &len); } q->head = (q->head + 1) & (q->capacity - 1); q->count--; return true; } void input_queue_clear(struct input_queue *q) { discard_pending(q); free(q->last_text); free(q->events); *q = (struct input_queue){0}; }