summaryrefslogtreecommitdiff
path: root/src/common/input.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/input.c')
-rw-r--r--src/common/input.c103
1 files changed, 103 insertions, 0 deletions
diff --git a/src/common/input.c b/src/common/input.c
new file mode 100644
index 0000000..eac4d1a
--- /dev/null
+++ b/src/common/input.c
@@ -0,0 +1,103 @@
+#include "input.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+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;
+ size_t len;
+ const char *text = event_text(ev, &len);
+ char *owned = NULL;
+ if (ev->kind == YASL_INPUT_TEXT || ev->kind == YASL_INPUT_PASTE ||
+ ev->kind == YASL_INPUT_DROP_FILES) {
+ 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(&copy, 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;
+ for (size_t i = 0; i < q->count; i++)
+ events[i] = q->events[(q->head + i) % q->capacity];
+ free(q->events);
+ q->events = events;
+ q->capacity = capacity;
+ q->head = 0;
+ }
+ q->events[(q->head + q->count) % q->capacity] = 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;
+ 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];
+ size_t len;
+ q->last_text = (char *)event_text(ev, &len);
+ q->head = (q->head + 1) % q->capacity;
+ 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};
+}