#include "yasl/app.h" #include "common/app_internal.h" #include "common/backend.h" #include struct app_runner { struct yasl_display *display; const struct yasl_app_desc *desc; uint64_t initial_window; bool running, failed, had_surfaces; int error; }; void yasl_app_exit(struct yasl_display *d, enum yasl_app_result result) { 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; } } struct yasl_surface *yasl_app_window(struct yasl_display *d) { 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 dispatch_events(struct app_runner *runner) { 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); } } static void draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud) { 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); } void app_runner_register_surface(void *data, struct yasl_surface *s) { 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 app_runner_unregister_surface(void *data, struct yasl_surface *s) { (void)data; s->app_surface = NULL; } bool yasl_frame_present_cpu(struct yasl_frame *frame, const struct yasl_rect2i *rects, int count) { /* 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) { 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 events, int (*cb)(int, short, void *), void *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 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, yasl_timer_cb cb, void *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) yasl_loop_del_timer(d->loop, id); } int yasl_run(const struct yasl_app_desc *desc) { 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 error = errno; d->app_runner = NULL; yasl_display_close(d); errno = error; return -1; } runner.initial_window = s->id; yasl_surface_request_frame(s); } if (desc->init) yasl_app_exit(d, desc->init(desc->userdata, d)); while (runner.running) { 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; } 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_display_close(d); if (runner.failed) errno = error; return runner.failed ? -1 : 0; }