From 537b2d768b31deb0f95c5122e0d562e4406ae9a0 Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Tue, 13 Oct 2026 15:41:39 +0300 Subject: Unify core and runner events into struct yasl_event The core input queue and the yasl_run() runner each had their own event type, and the runner translated one into the other. Both now share struct yasl_event in : yasl_surface_poll_event() returns the same events the app event callback receives, so the runner forwards them unchanged. Pointer input becomes POINTER_DOWN/UP/MOTION/SCROLL/LEAVE with a named yasl_pointer_button and a motion delta computed in the input queue. TEXT, PASTE and DROP share one text payload, scale changes fold into WINDOW_RESIZE, and every event carries a monotonic time_ns. yasl_surface_set_mouse_mode() is replaced by yasl_surface_set_pointer_mode(); no backend implements relative mode yet. The export check now only counts YASL_API declarations, so function pointer fields returning enum yasl_app_result are not mistaken for exports. Co-Authored-By: Claude Opus 5.5 --- src/common/app.c | 197 ++++++++------------------------------------------- src/common/backend.c | 21 +++++- src/common/backend.h | 7 +- src/common/input.c | 80 +++++++++++---------- src/common/input.h | 14 +++- 5 files changed, 109 insertions(+), 210 deletions(-) (limited to 'src/common') diff --git a/src/common/app.c b/src/common/app.c index e1148f9..9ec1f6a 100644 --- a/src/common/app.c +++ b/src/common/app.c @@ -9,7 +9,7 @@ struct user_event_node { struct yasl_surface *surface; - uint32_t type; + uint32_t code; void *payload; size_t size; struct user_event_node *next; @@ -21,10 +21,6 @@ struct app_surface { struct yasl_surface *surface; struct app_runner *runner; yasl_pacing_mode pacing; - yasl_mouse_mode mouse_mode; - struct yasl_vector2i last_mouse_pos; - bool has_last_mouse_pos; - struct yasl_vector2i last_size; uint32_t last_scale_120; uint64_t frame_index; bool presented; @@ -45,6 +41,13 @@ struct app_runner { struct user_event_node *user_event_tail; }; +/* Deliver one event to the application; a QUIT result stops the runner. */ +static void emit(struct app_runner *ar, const struct yasl_event *ev) +{ + if (ar->desc->event && ar->desc->event(ar->desc->userdata, ev) == YASL_APP_QUIT) + ar->running = false; +} + static void app_runner_surface_draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud) { struct app_surface *as = ud; @@ -88,35 +91,17 @@ static void app_runner_surface_configure(struct yasl_surface *s, struct yasl_geo if (!ar || !ar->desc || !ar->desc->event) return; - bool scale_changed = (as->last_scale_120 != geom.scale_120 && as->last_scale_120 != 0); - as->last_scale_120 = geom.scale_120; - as->last_size = geom.size; - - if (scale_changed) { - struct yasl_event ev = { - .surface = s, - .type = YASL_EVENT_WINDOW_SCALE_CHANGED, - .scale = { - .scale_120 = geom.scale_120, - .size = geom.size, - }, - }; - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) - ar->running = false; - if (!as->surface) - return; - } - struct yasl_event ev = { - .surface = s, .type = YASL_EVENT_WINDOW_RESIZE, + .surface = s, + .time_ns = yasl_loop_now_ns(), .resize = { - .size = geom.size, - .scale_120 = geom.scale_120, + .geometry = geom, + .scale_changed = as->last_scale_120 && as->last_scale_120 != geom.scale_120, }, }; - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) - ar->running = false; + as->last_scale_120 = geom.scale_120; + emit(ar, &ev); if (as->surface) yasl_surface_schedule_redraw(s); @@ -129,13 +114,11 @@ static void app_runner_surface_close(struct yasl_surface *s, void *ud) if (!ar || !ar->desc || !ar->desc->event) return; - struct yasl_event ev = { + emit(ar, &(struct yasl_event){ + .type = YASL_EVENT_WINDOW_CLOSE, .surface = s, - .type = YASL_EVENT_WINDOW_CLOSE_REQUEST, - }; - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) { - ar->running = false; - } + .time_ns = yasl_loop_now_ns(), + }); } void app_runner_register_surface(void *runner, struct yasl_surface *s) @@ -150,9 +133,7 @@ void app_runner_register_surface(void *runner, struct yasl_surface *s) as->surface = s; as->runner = ar; as->pacing = YASL_PACING_ON_DEMAND; - as->mouse_mode = YASL_MOUSE_NORMAL; as->last_scale_120 = s->scale_120; - as->last_size = s->size; s->app_surface = as; as->next = ar->surfaces; @@ -237,21 +218,8 @@ void yasl_surface_request_frame(struct yasl_surface *s) } } -void yasl_surface_set_mouse_mode(struct yasl_surface *s, yasl_mouse_mode mode) -{ - if (!s || !s->app_surface) - return; - struct app_surface *as = s->app_surface; - as->mouse_mode = mode; - if (mode == YASL_MOUSE_NORMAL) { - yasl_surface_set_cursor(s, YASL_CURSOR_ARROW); - } else { - yasl_surface_set_cursor(s, YASL_CURSOR_HIDDEN); - } -} - bool yasl_post_event(struct yasl_display *d, struct yasl_surface *s, - uint32_t user_type, void *payload, size_t size) + uint32_t code, void *payload, size_t size) { if (!d || !d->app_runner) return false; @@ -261,7 +229,7 @@ bool yasl_post_event(struct yasl_display *d, struct yasl_surface *s, if (!node) return false; node->surface = s; - node->type = user_type; + node->code = code; node->payload = payload; node->size = size; node->next = NULL; @@ -322,19 +290,12 @@ static void app_runner_dispatch_user_events(struct app_runner *ar) while (node && ar->running) { struct user_event_node *next = node->next; - if (ar->desc->event) { - struct yasl_event ev = { - .surface = node->surface, - .type = YASL_EVENT_USER, - .user = { - .type = node->type, - .payload = node->payload, - .size = node->size, - }, - }; - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) - ar->running = false; - } + emit(ar, &(struct yasl_event){ + .type = YASL_EVENT_USER, + .surface = node->surface, + .time_ns = yasl_loop_now_ns(), + .user = {.code = node->code, .payload = node->payload, .size = node->size}, + }); free(node); node = next; } @@ -348,107 +309,11 @@ static void app_runner_dispatch_user_events(struct app_runner *ar) static void app_runner_dispatch_input_events(struct app_runner *ar) { for (struct app_surface *as = ar->surfaces; as && ar->running; as = as->next) { - struct yasl_input_event ie; - while (as->surface && yasl_surface_input_poll(as->surface, &ie) && - ar->running) { - if (!ar->desc->event) - continue; - - struct yasl_event ev = {0}; - ev.surface = ie.surface ? ie.surface : as->surface; - - switch (ie.kind) { - case YASL_INPUT_KEY: - ev.type = YASL_EVENT_KEY; - ev.key.key = ie.key.key; - ev.key.action = ie.key.action; - ev.key.mods = ie.key.mods; - ev.key.codepoint = ie.key.codepoint; - ev.key.shifted = ie.key.shifted; - ev.key.native_code = ie.key.native_code; - ev.key.repeat = (ie.key.action == YASL_KEY_REPEAT); - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) - ar->running = false; - break; - case YASL_INPUT_TEXT: - ev.type = YASL_EVENT_TEXT; - ev.text.text = ie.text.text; - ev.text.len = ie.text.len; - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) - ar->running = false; - break; - case YASL_INPUT_MOUSE: - if (ie.mouse.kind == YASL_PTR_PRESS || ie.mouse.kind == YASL_PTR_RELEASE) { - ev.type = YASL_EVENT_MOUSE_BUTTON; - ev.mouse_button.pos = ie.mouse.pos; - ev.mouse_button.device = ie.mouse.device; - ev.mouse_button.button = ie.mouse.button; - ev.mouse_button.pressed = (ie.mouse.kind == YASL_PTR_PRESS); - ev.mouse_button.mods = ie.mouse.mods; - ev.mouse_button.pen = ie.mouse.pen; - as->last_mouse_pos = ie.mouse.pos; - as->has_last_mouse_pos = true; - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) - ar->running = false; - } else if (ie.mouse.kind == YASL_PTR_MOTION) { - ev.type = YASL_EVENT_MOUSE_MOTION; - ev.mouse_motion.pos = ie.mouse.pos; - ev.mouse_motion.device = ie.mouse.device; - if (as->has_last_mouse_pos) { - ev.mouse_motion.delta.x = ie.mouse.pos.x - as->last_mouse_pos.x; - ev.mouse_motion.delta.y = ie.mouse.pos.y - as->last_mouse_pos.y; - } else { - ev.mouse_motion.delta = (struct yasl_vector2i){0, 0}; - } - ev.mouse_motion.mods = ie.mouse.mods; - ev.mouse_motion.pen = ie.mouse.pen; - as->last_mouse_pos = ie.mouse.pos; - as->has_last_mouse_pos = true; - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) - ar->running = false; - } else if (ie.mouse.kind == YASL_PTR_SCROLL) { - ev.type = YASL_EVENT_MOUSE_SCROLL; - ev.mouse_scroll.pos = ie.mouse.pos; - ev.mouse_scroll.scroll = ie.mouse.scroll; - ev.mouse_scroll.mods = ie.mouse.mods; - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) - ar->running = false; - } else if (ie.mouse.kind == YASL_PTR_LEAVE) { - ev.type = YASL_EVENT_MOUSE_LEAVE; - as->has_last_mouse_pos = false; - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) - ar->running = false; - } - break; - case YASL_INPUT_FOCUS: - ev.type = YASL_EVENT_WINDOW_FOCUS; - ev.focus.focused = ie.focus.focused; - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) - ar->running = false; - break; - case YASL_INPUT_PASTE: - ev.type = YASL_EVENT_PASTE; - ev.paste.text = ie.paste.text; - ev.paste.len = ie.paste.len; - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) - ar->running = false; - break; - case YASL_INPUT_DROP_FILES: - ev.type = YASL_EVENT_DROP_FILES; - ev.drop_files.files = ie.drop_files.files; - ev.drop_files.len = ie.drop_files.len; - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) - ar->running = false; - break; - case YASL_INPUT_RESET: - as->has_last_mouse_pos = false; - ev.type = YASL_EVENT_INPUT_RESET; - if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) - ar->running = false; - break; - default: - break; - } + struct yasl_event ev; + while (ar->running && as->surface && yasl_surface_poll_event(as->surface, &ev)) { + if (!ev.surface) + ev.surface = as->surface; + emit(ar, &ev); } } } diff --git a/src/common/backend.c b/src/common/backend.c index 3f2143d..d35d243 100644 --- a/src/common/backend.c +++ b/src/common/backend.c @@ -306,15 +306,30 @@ bool yasl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer return s->display->ops->surface_present(s, fb, &clipped); } -bool yasl_surface_input_poll(struct yasl_surface *s, struct yasl_input_event *ev) +bool yasl_surface_poll_event(struct yasl_surface *s, struct yasl_event *ev) { if (!s || !ev) return false; - if (s->display && s->display->ops && s->display->ops->surface_input_poll) - return s->display->ops->surface_input_poll(s, ev); + if (s->display && s->display->ops && s->display->ops->surface_poll_event) + return s->display->ops->surface_poll_event(s, ev); return input_queue_pop(&s->input, ev); } +bool yasl_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_pointer_mode mode) +{ + if (!s || (mode != YASL_POINTER_NORMAL && mode != YASL_POINTER_RELATIVE)) + return false; + if (mode == s->pointer_mode) + return true; + const struct yasl_backend_ops *ops = s->display ? s->display->ops : NULL; + if (!ops || !ops->surface_set_pointer_mode) + return mode == YASL_POINTER_NORMAL; + if (!ops->surface_set_pointer_mode(s, mode)) + return false; + s->pointer_mode = mode; + return true; +} + struct yasl_geometry yasl_surface_geometry(const struct yasl_surface *s) { if (!s) diff --git a/src/common/backend.h b/src/common/backend.h index 17d781e..f0fae03 100644 --- a/src/common/backend.h +++ b/src/common/backend.h @@ -28,6 +28,7 @@ struct yasl_surface { void *ud; uint64_t frame_generation; struct input_queue input; + enum yasl_pointer_mode pointer_mode; struct yasl_frame_clock clock; uint64_t frames_skipped; yasl_timer_id *timers; @@ -70,7 +71,11 @@ struct yasl_backend_ops { void (*surface_set_title)(struct yasl_surface *s, const char *title); void (*surface_schedule_redraw)(struct yasl_surface *s); bool (*surface_present)(struct yasl_surface *s, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage); - bool (*surface_input_poll)(struct yasl_surface *s, struct yasl_input_event *ev); + /* Optional; NULL means the common input queue is drained. */ + bool (*surface_poll_event)(struct yasl_surface *s, struct yasl_event *ev); + /* Optional; NULL means only YASL_POINTER_NORMAL is supported. Backends that + * implement RELATIVE push POINTER_MOTION with delta set and pos frozen. */ + bool (*surface_set_pointer_mode)(struct yasl_surface *s, enum yasl_pointer_mode mode); struct yasl_geometry (*surface_geometry)(const struct yasl_surface *s); struct yasl_timing (*surface_timing)(const struct yasl_surface *s); uint64_t (*surface_frame_time_ns)(struct yasl_surface *s, uint64_t now); diff --git a/src/common/input.c b/src/common/input.c index 471cd2b..0b55f1f 100644 --- a/src/common/input.c +++ b/src/common/input.c @@ -1,57 +1,64 @@ #include "input.h" +#include "yasl/loop.h" #include #include -static const char *event_text(const struct yasl_input_event *ev, size_t *len) +static bool has_text(const struct yasl_event *ev) { - 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; - } + return ev->type == YASL_EVENT_TEXT || ev->type == YASL_EVENT_PASTE || + ev->type == YASL_EVENT_DROP; } 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)); + struct yasl_event *ev = &q->events[(q->head + i) & (q->capacity - 1)]; + if (has_text(ev)) free((void *)ev->text.data); } q->head = q->count = 0; } -bool input_queue_push(struct input_queue *q, const struct yasl_input_event *ev) +bool input_queue_push(struct input_queue *q, const struct yasl_event *ev) { - struct yasl_input_event copy = *ev; + struct yasl_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; + if (!copy.time_ns) copy.time_ns = yasl_loop_now_ns(); + if (has_text(ev)) { + size_t len = ev->text.len; + if ((!ev->text.data && len) || len == SIZE_MAX) goto failed; owned = malloc(len + 1); if (!owned) goto failed; - if (len) memcpy(owned, text, len); + if (len) memcpy(owned, ev->text.data, len); owned[len] = '\0'; - set_text(©, owned); + copy.text.data = owned; + } + switch (ev->type) { + case YASL_EVENT_POINTER_MOTION: + if (!q->relative) { + copy.pointer.delta = q->has_last_pos + ? (struct yasl_vector2d){ev->pointer.pos.x - q->last_pos.x, + ev->pointer.pos.y - q->last_pos.y} + : (struct yasl_vector2d){0, 0}; + } + [[fallthrough]]; + case YASL_EVENT_POINTER_DOWN: + case YASL_EVENT_POINTER_UP: + q->last_pos = ev->pointer.pos; + q->has_last_pos = true; + break; + case YASL_EVENT_POINTER_LEAVE: + case YASL_EVENT_INPUT_RESET: + q->has_last_pos = false; + break; + default: + break; } 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)); + struct yasl_event *events = malloc(capacity * sizeof(*events)); if (!events) goto failed; if (q->events && q->count > 0) { size_t first = q->capacity - q->head; @@ -76,10 +83,11 @@ failed: discard_pending(q); q->reset_surface = ev->surface; q->reset_pending = true; + q->has_last_pos = false; return false; } -bool input_queue_pop(struct input_queue *q, struct yasl_input_event *ev) +bool input_queue_pop(struct input_queue *q, struct yasl_event *ev) { if (!ev || (!q->count && !q->reset_pending)) return false; if (q->last_text) { @@ -87,18 +95,16 @@ bool input_queue_pop(struct input_queue *q, struct yasl_input_event *ev) q->last_text = NULL; } if (q->reset_pending) { - *ev = (struct yasl_input_event){ - .surface = q->reset_surface, .kind = YASL_INPUT_RESET, + *ev = (struct yasl_event){ + .type = YASL_EVENT_INPUT_RESET, .surface = q->reset_surface, + .time_ns = yasl_loop_now_ns(), }; 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); - } + if (has_text(ev)) + q->last_text = (char *)ev->text.data; q->head = (q->head + 1) & (q->capacity - 1); q->count--; return true; diff --git a/src/common/input.h b/src/common/input.h index d7264f6..ffd1663 100644 --- a/src/common/input.h +++ b/src/common/input.h @@ -5,15 +5,23 @@ /* Each queued string and the last successfully polled string are owned here. */ struct input_queue { - struct yasl_input_event *events; + struct yasl_event *events; size_t head, count, capacity; char *last_text; + /* Last pointer position, for computing absolute-mode motion deltas. */ + struct yasl_vector2i last_pos; + bool has_last_pos; + /* Set by backends while YASL_POINTER_RELATIVE is active: keep pushed deltas. */ + bool relative; struct yasl_surface *reset_surface; bool reset_pending; }; -bool input_queue_push(struct input_queue *q, const struct yasl_input_event *ev); -bool input_queue_pop(struct input_queue *q, struct yasl_input_event *ev); +/* Copies text payloads and stamps time_ns when zero. Unless q->relative is set, + * POINTER_MOTION delta is computed from the previous position; otherwise the + * backend-supplied delta is kept. Leave and reset clear the position history. */ +bool input_queue_push(struct input_queue *q, const struct yasl_event *ev); +bool input_queue_pop(struct input_queue *q, struct yasl_event *ev); void input_queue_clear(struct input_queue *q); #endif -- cgit v1.2.3