summaryrefslogtreecommitdiff
path: root/src/common/app.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/app.c')
-rw-r--r--src/common/app.c468
1 files changed, 85 insertions, 383 deletions
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 <errno.h>
-#include <stdlib.h>
-#include <string.h>
-
-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;
}