From dd1775a9927922c14f8679ccbc4d3ca8d5917e20 Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Tue, 13 Oct 2026 21:23:03 +0300 Subject: public api refactor --- src/common/app.c | 468 ++++++++++--------------------------------------------- 1 file changed, 85 insertions(+), 383 deletions(-) (limited to 'src/common/app.c') diff --git a/src/common/app.c b/src/common/app.c index 435d020..63cf2ce 100644 --- a/src/common/app.c +++ b/src/common/app.c @@ -1,451 +1,153 @@ #include "yasl/app.h" #include "common/app_internal.h" #include "common/backend.h" -#include "platform/sync.h" #include -#include -#include - -struct user_event_node { - struct yasl_surface *surface; - uint32_t code; - void *payload; - size_t size; - struct user_event_node *next; -}; - -/* `surface` is NULL once the surface is destroyed. A callback may still - * hold the entry then, so only app_runner_free_dead() frees it. */ -struct app_surface { - struct yasl_surface *surface; - struct app_runner *runner; - yasl_pacing_mode pacing; - uint32_t last_scale_120; - uint64_t frame_index; - bool presented; - struct app_surface *next; -}; struct app_runner { struct yasl_display *display; const struct yasl_app_desc *desc; - bool running; - bool failed; - bool had_surfaces; - int surface_count; - struct app_surface *surfaces; - - yasl_mutex user_event_mutex; - struct user_event_node *user_event_head; - struct user_event_node *user_event_tail; + uint64_t initial_window; + bool running, failed, had_surfaces; + int error; }; -/* Deliver one event to the application; a QUIT result stops the runner. */ -static void emit(struct app_runner *ar, const struct yasl_event *ev) +void yasl_app_exit(struct yasl_display *d, enum yasl_app_result result) { - 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; - struct app_runner *ar = as ? as->runner : NULL; - if (!ar || !ar->desc || !ar->desc->frame) - return; - - as->presented = false; - - struct yasl_frame_context ctx = { - .surface = s, - .frame_index = as->frame_index++, - .target_time_ns = yasl_surface_frame_time_ns(s, yasl_loop_now_ns()), - .size = frame->target.size, - .scale_120 = s->scale_120, - .cpu = { - .pixels = frame->target.pixels, - .stride_bytes = frame->target.size.x * (int)sizeof(uint32_t), - }, - .gpu = { - .native_layer = NULL, - }, - .internal_handle = frame, - }; - - struct yasl_native_frame nf; - if (yasl_surface_get_native_frame(s, &frame->target, &nf)) { - ctx.gpu.native_layer = nf.drawable; - } - - ar->desc->frame(ar->desc->userdata, s, &ctx); - - if (as->surface && as->pacing == YASL_PACING_CONTINUOUS && ar->running) - yasl_surface_schedule_redraw(s); + if (!d || !d->app_runner || result == YASL_APP_CONTINUE) return; + struct app_runner *runner = d->app_runner; + runner->running = false; + if (result == YASL_APP_ERROR) { runner->failed = true; runner->error = EIO; } } -static void app_runner_surface_configure(struct yasl_surface *s, struct yasl_geometry geom, void *ud) +struct yasl_surface *yasl_app_window(struct yasl_display *d) { - struct app_surface *as = ud; - struct app_runner *ar = as ? as->runner : NULL; - if (!ar || !ar->desc || !ar->desc->event) - return; - - struct yasl_event ev = { - .type = YASL_EVENT_WINDOW_RESIZE, - .surface = s, - .time_ns = yasl_loop_now_ns(), - .resize = { - .geometry = geom, - .scale_changed = as->last_scale_120 && as->last_scale_120 != geom.scale_120, - }, - }; - as->last_scale_120 = geom.scale_120; - emit(ar, &ev); - - if (as->surface) - yasl_surface_schedule_redraw(s); + if (!d || !d->app_runner) return NULL; + struct app_runner *runner = d->app_runner; + return runner->initial_window ? yasl_events_find(d, runner->initial_window) : NULL; } -static void app_runner_surface_close(struct yasl_surface *s, void *ud) +static void dispatch_events(struct app_runner *runner) { - struct app_surface *as = ud; - struct app_runner *ar = as ? as->runner : NULL; - if (!ar || !ar->desc) - return; - /* Without an event callback nothing could answer the request, so quit. */ - if (!ar->desc->event) { - ar->running = false; - return; + struct yasl_event event; + while (runner->running && yasl_display_poll_event(runner->display, &event)) { + if (runner->desc->event) + yasl_app_exit(runner->display, runner->desc->event(runner->desc->userdata, &event)); + else if (event.type == YASL_EVENT_WINDOW_CLOSE) + yasl_app_exit(runner->display, YASL_APP_QUIT); + if (event.type == YASL_EVENT_DISPLAY_QUIT) + yasl_app_exit(runner->display, YASL_APP_QUIT); + if (event.type == YASL_EVENT_DISPLAY_LOST) + yasl_app_exit(runner->display, YASL_APP_ERROR); } - - emit(ar, &(struct yasl_event){ - .type = YASL_EVENT_WINDOW_CLOSE, - .surface = s, - .time_ns = yasl_loop_now_ns(), - }); -} - -void app_runner_register_surface(void *runner, struct yasl_surface *s) -{ - if (!runner || !s) - return; - struct app_runner *ar = runner; - struct app_surface *as = calloc(1, sizeof(*as)); - if (!as) - return; - - as->surface = s; - as->runner = ar; - as->pacing = YASL_PACING_ON_DEMAND; - as->last_scale_120 = s->scale_120; - s->app_surface = as; - - as->next = ar->surfaces; - ar->surfaces = as; - ar->surface_count++; - ar->had_surfaces = true; - - struct yasl_window_cbs cbs = { - .draw = app_runner_surface_draw, - .configure = app_runner_surface_configure, - .close = app_runner_surface_close, - }; - yasl_surface_set_callbacks(s, &cbs, as); } -void app_runner_unregister_surface(void *runner, struct yasl_surface *s) +static void draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud) { - if (!runner || !s || !s->app_surface) - return; - struct app_runner *ar = runner; - struct app_surface *as = s->app_surface; - as->surface = NULL; - s->app_surface = NULL; - ar->surface_count--; -} - -/* Call only when no callback runs. */ -static void app_runner_free_dead(struct app_runner *ar) -{ - for (struct app_surface **p = &ar->surfaces; *p;) { - struct app_surface *as = *p; - if (as->surface) { - p = &as->next; - continue; - } - *p = as->next; - free(as); - } + struct app_runner *runner = ud; + uint64_t id = s->id; + dispatch_events(runner); + /* Draining events can destroy the surface whose native draw is active. */ + struct yasl_surface *live = yasl_events_find(runner->display, id); + if (live && runner->running && runner->desc->frame) + runner->desc->frame(runner->desc->userdata, live, frame); } -bool yasl_frame_present_cpu(struct yasl_frame_context *ctx, - const struct yasl_rect2i *dirty_rects, - int count) +void app_runner_register_surface(void *data, struct yasl_surface *s) { - if (!ctx || !ctx->surface || !ctx->internal_handle) - return false; - - struct yasl_frame *frame = (struct yasl_frame *)ctx->internal_handle; - struct yasl_damage_list damage; - if (!dirty_rects || count <= 0) { - damage.n = -1; - damage.bounds = (struct yasl_rect2i){0}; - } else { - damage.n = count > YASL_DAMAGE_MAX ? YASL_DAMAGE_MAX : count; - damage.bounds = (struct yasl_rect2i){0}; - for (int i = 0; i < damage.n; i++) { - damage.rects[i] = dirty_rects[i]; - } - } - - return yasl_surface_present(ctx->surface, &frame->target, &damage); + struct app_runner *runner = data; + struct yasl_window_cbs callbacks = {.draw = draw}; + yasl_surface_set_callbacks(s, &callbacks, runner); + s->app_surface = runner; + runner->had_surfaces = true; } -void yasl_surface_set_pacing(struct yasl_surface *s, yasl_pacing_mode mode) +void app_runner_unregister_surface(void *data, struct yasl_surface *s) { - if (!s || !s->app_surface) - return; - struct app_surface *as = s->app_surface; - as->pacing = mode; - if (mode == YASL_PACING_CONTINUOUS) { - yasl_surface_schedule_redraw(s); - } + (void)data; + s->app_surface = NULL; } -void yasl_surface_request_frame(struct yasl_surface *s) +bool yasl_frame_present_cpu(struct yasl_frame *frame, const struct yasl_rect2i *rects, int count) { - if (!s) - return; - yasl_surface_schedule_redraw(s); - if (s->display && s->display->loop) { - yasl_loop_kick(s->display->loop); - } + /* Preserve the migration adapter's old NULL/nonpositive full-damage rule. */ + return yasl_frame_present(frame, count <= 0 ? NULL : rects, count <= 0 ? 0 : (size_t)count); } bool yasl_post_event(struct yasl_display *d, struct yasl_surface *s, uint32_t code, void *payload, size_t size) { - if (!d || !d->app_runner) - return false; - struct app_runner *ar = d->app_runner; - - struct user_event_node *node = malloc(sizeof(*node)); - if (!node) - return false; - node->surface = s; - node->code = code; - node->payload = payload; - node->size = size; - node->next = NULL; - - yasl_mutex_lock(&ar->user_event_mutex); - if (ar->user_event_tail) { - ar->user_event_tail->next = node; - ar->user_event_tail = node; - } else { - ar->user_event_head = node; - ar->user_event_tail = node; - } - yasl_mutex_unlock(&ar->user_event_mutex); - - if (d->loop) { - yasl_loop_kick(d->loop); - } - return true; + if (!d || !d->app_runner) { errno = EINVAL; return false; } + /* Legacy callers must keep s alive through this call. New code passes an ID. */ + return yasl_display_post(d, &(struct yasl_message){ + .target_id = s ? yasl_surface_id(s) : 0, .code = code, .payload = payload, .size = size, + }); } -int yasl_app_watch_fd(struct yasl_display *d, int fd, short poll_events, - int (*cb)(int fd, short revents, void *ud), void *ud) +int yasl_app_watch_fd(struct yasl_display *d, int fd, short events, + int (*cb)(int, short, void *), void *ud) { - if (!d || !d->loop) - return -1; - return yasl_loop_add_fd(d->loop, fd, poll_events, cb, ud); + if (!d || !d->loop) { errno = EINVAL; return -1; } + return yasl_loop_add_fd(d->loop, fd, events, cb, ud); } - -void yasl_app_unwatch_fd(struct yasl_display *d, int watch_id) +void yasl_app_unwatch_fd(struct yasl_display *d, int id) { - if (!d || !d->loop) - return; - yasl_loop_del_fd(d->loop, watch_id); + if (d && d->loop) yasl_loop_del_fd(d->loop, id); } - -yasl_timer_id yasl_app_add_timer(struct yasl_display *d, uint64_t interval_ns, - yasl_timer_cb cb, void *ud) +yasl_timer_id yasl_app_add_timer(struct yasl_display *d, uint64_t interval, yasl_timer_cb cb, void *ud) { - if (!d || !d->loop) - return 0; - return yasl_loop_add_timer(d->loop, interval_ns, cb, ud); + if (!d || !d->loop) return 0; + return yasl_loop_add_timer(d->loop, interval, cb, ud); } - void yasl_app_del_timer(struct yasl_display *d, yasl_timer_id id) { - if (!d || !d->loop) - return; - yasl_loop_del_timer(d->loop, id); -} - -static void app_runner_dispatch_user_events(struct app_runner *ar) -{ - yasl_mutex_lock(&ar->user_event_mutex); - struct user_event_node *node = ar->user_event_head; - ar->user_event_head = NULL; - ar->user_event_tail = NULL; - yasl_mutex_unlock(&ar->user_event_mutex); - - while (node && ar->running) { - struct user_event_node *next = node->next; - 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; - } - while (node) { - struct user_event_node *next = node->next; - free(node); - node = next; - } -} - -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_event ev; - while (ar->running && as->surface && yasl_surface_poll_event(as->surface, &ev)) { - if (!ev.surface) - ev.surface = as->surface; - emit(ar, &ev); - } - } -} - -static void app_runner_quit(void *ud) -{ - ((struct app_runner *)ud)->running = false; -} - -static void app_runner_lost(void *ud) -{ - struct app_runner *runner = ud; - runner->failed = true; - runner->running = false; + if (d && d->loop) yasl_loop_del_timer(d->loop, id); } int yasl_run(const struct yasl_app_desc *desc) { - if (!desc) - return -1; - - struct app_runner runner = {.desc = desc, .running = true}; - const struct yasl_display_cbs callbacks = { - .quit = app_runner_quit, .lost = app_runner_lost, - }; - struct yasl_display_spec dspec = { - .app_id = desc->app_id, - .backend = desc->backend, - .cbs = &callbacks, - .ud = &runner, - }; - struct yasl_display *d = yasl_display_open(NULL, &dspec); - if (!d) - return -1; - - runner.display = d; - int sync_error = yasl_mutex_init(&runner.user_event_mutex); - if (sync_error) { - yasl_display_close(d); - errno = sync_error; - return -1; - } + if (!desc) { errno = EINVAL; return -1; } + struct yasl_display *d = yasl_display_open(NULL, &(struct yasl_display_spec){ + .app_id = desc->app_id, .backend = desc->backend, + }); + if (!d) return -1; + struct app_runner runner = {.display = d, .desc = desc, .running = true}; d->app_runner = &runner; - if (desc->window) { struct yasl_surface *s = yasl_surface_create_toplevel(d, desc->window); if (!s) { - int create_error = errno; + int error = errno; d->app_runner = NULL; - yasl_mutex_destroy(&runner.user_event_mutex); yasl_display_close(d); - errno = create_error; + errno = error; return -1; } + runner.initial_window = s->id; yasl_surface_request_frame(s); } - - if (desc->init && desc->init(desc->userdata, d) == YASL_APP_QUIT) { - runner.running = false; - } - - uint32_t caps = yasl_display_caps(d); - bool has_frame_pacing = (caps & YASL_CAP_FRAME_PACING) != 0; - + if (desc->init) yasl_app_exit(d, desc->init(desc->userdata, d)); while (runner.running) { - app_runner_free_dead(&runner); - if (runner.had_surfaces && runner.surface_count == 0) { - runner.running = false; - break; - } - - bool any_continuous = false; - for (struct app_surface *as = runner.surfaces; as; as = as->next) { - if (as->pacing == YASL_PACING_CONTINUOUS) - any_continuous = true; - } - - int timeout_ms = -1; - if (any_continuous) { - timeout_ms = has_frame_pacing ? -1 : 16; - } - - int res = yasl_display_dispatch(d, timeout_ms); - if (res < 0) { + dispatch_events(&runner); + if (!runner.running || (runner.had_surfaces && !d->surfaces)) break; + if (yasl_display_dispatch(d, -1) < 0) { + int dispatch_error = errno; + dispatch_events(&runner); runner.failed = true; + runner.error = dispatch_error; break; } - - app_runner_dispatch_user_events(&runner); - app_runner_dispatch_input_events(&runner); - - if (runner.running) { - yasl_display_flush(d); - } - - if (!has_frame_pacing && any_continuous && runner.running) { - for (struct app_surface *as = runner.surfaces; as; as = as->next) { - if (as->surface && as->pacing == YASL_PACING_CONTINUOUS) { - yasl_surface_schedule_redraw(as->surface); - } - } - } - } - - if (desc->quit) { - desc->quit(desc->userdata, d); - } - - /* Destroy any surfaces that were not explicitly destroyed by the application */ - for (struct app_surface *as = runner.surfaces; as; as = as->next) { - if (as->surface) - yasl_surface_destroy(as->surface); - } - app_runner_free_dead(&runner); - + dispatch_events(&runner); + if (runner.running) yasl_display_flush(d); + } + int queue_error = yasl_events_error(d); + if (queue_error) { runner.failed = true; runner.error = queue_error; } + int error = runner.error; + if (desc->quit) desc->quit(desc->userdata, d); + /* Backend registries create children after parents: reverse creation order + * ensures child popups are destroyed before their parent. */ + while (d->surfaces) yasl_surface_destroy(d->surfaces); d->app_runner = NULL; - - yasl_mutex_lock(&runner.user_event_mutex); - struct user_event_node *node = runner.user_event_head; - while (node) { - struct user_event_node *next = node->next; - free(node); - node = next; - } - yasl_mutex_unlock(&runner.user_event_mutex); - yasl_mutex_destroy(&runner.user_event_mutex); - yasl_display_close(d); + if (runner.failed) errno = error; return runner.failed ? -1 : 0; } -- cgit v1.2.3