1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
#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;
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};
}
|