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/backend.c | 222 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 217 insertions(+), 5 deletions(-) (limited to 'src/common/backend.c') diff --git a/src/common/backend.c b/src/common/backend.c index bb5ae33..a81e1e7 100644 --- a/src/common/backend.c +++ b/src/common/backend.c @@ -1,6 +1,7 @@ #include "backend.h" #include "loop.h" #include "common/app_internal.h" +#include "common/utf8.h" #include #include @@ -33,6 +34,15 @@ static struct yasl_display *open_backend(enum yasl_backend backend, errno = 0; struct yasl_display *d = ops->display_open(loop, spec); if (!d && !errno) errno = ENODEV; + if (d) { + d->callback_ud = spec ? spec->ud : NULL; + if (!yasl_events_open(d)) { + int error = errno; + ops->display_close(d); + errno = error; + return NULL; + } + } return d; } @@ -70,6 +80,7 @@ void yasl_display_close(struct yasl_display *d) { if (!d || !d->ops->display_close) return; + yasl_events_close(d); d->ops->display_close(d); } @@ -90,8 +101,13 @@ int yasl_display_dispatch(struct yasl_display *d, int timeout_ms) errno = ENOTSUP; return -1; } + int queue_error = yasl_events_error(d); + if (queue_error) { errno = queue_error; return -1; } + if (yasl_events_pending(d)) timeout_ms = 0; errno = 0; int result = d->ops->display_dispatch_timeout(d, timeout_ms); + queue_error = yasl_events_error(d); + if (queue_error) { errno = queue_error; return -1; } if (result < 0 && !errno) errno = EIO; return result; } @@ -175,7 +191,14 @@ struct yasl_surface *yasl_create_toplevel(struct yasl_display *d, errno = 0; struct yasl_surface *s = d->ops->surface_create_toplevel(d, spec, graphics_device); if (!s && !errno) errno = ENODEV; + if (s && !yasl_events_register(s)) { + int error = errno; + d->ops->surface_destroy(s); + errno = error; + return NULL; + } if (s) { + s->userdata = spec->userdata; /* Native creation may dispatch events. Attach this window's callbacks * only after it finishes, so callers can store the returned surface. */ if (d->app_runner) @@ -218,8 +241,16 @@ struct yasl_surface *yasl_surface_create_popup(struct yasl_surface *parent, cons errno = 0; struct yasl_surface *s = parent->display->ops->surface_create_popup(parent, spec); if (!s && !errno) errno = ENODEV; - if (s && parent->display->app_runner) { - app_runner_register_surface(parent->display->app_runner, s); + if (s) { + s->popup = true; + if (!yasl_events_register(s)) { + int error = errno; + parent->display->ops->surface_destroy(s); + errno = error; + return NULL; + } + if (parent->display->app_runner) + app_runner_register_surface(parent->display->app_runner, s); } return s; } @@ -248,6 +279,7 @@ void yasl_surface_destroy(struct yasl_surface *s) if (s->app_surface && s->display->app_runner) { app_runner_unregister_surface(s->display->app_runner, s); } + yasl_events_unregister(s); yasl_surface_clear_timers(s); input_queue_clear(&s->input); s->display->ops->surface_destroy(s); @@ -258,14 +290,18 @@ struct yasl_display *yasl_surface_display(const struct yasl_surface *s) return s ? s->display : NULL; } -void yasl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_window_cbs *cbs, void *ud) +bool yasl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_window_cbs *cbs, void *ud) { - if (!s) - return; + if (!s) { errno = EINVAL; return false; } + if (s->display && s->display->app_runner && s->app_surface) { + errno = EBUSY; + return false; + } s->callbacks = cbs ? *cbs : (struct yasl_window_cbs){0}; s->ud = ud; if (s->display && s->display->ops && s->display->ops->surface_set_callbacks) s->display->ops->surface_set_callbacks(s, cbs, ud); + return true; } void yasl_surface_set_title(struct yasl_surface *s, const char *title) @@ -317,6 +353,8 @@ bool yasl_surface_poll_event(struct yasl_surface *s, struct yasl_event *ev) { if (!s || !ev) return false; + if (s->display && s->display->events) + return yasl_events_poll_surface(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); @@ -525,3 +563,177 @@ struct yasl_loop *yasl_display_loop(struct yasl_display *d) { return d ? d->loop : NULL; } + +bool yasl_frame_present(struct yasl_frame *frame, const struct yasl_rect2i *rects, size_t count) +{ + if (!frame || !frame->surface || (!rects && count)) { errno = EINVAL; return false; } + struct yasl_surface *s = frame->surface; + if (!s->drawing || s->active_frame != frame) { errno = EINVAL; return false; } + if (s->frame_presented) { errno = EALREADY; return false; } + for (size_t i = 0; i < count; i++) { + if (rects[i].width < 0 || rects[i].height < 0) { errno = EINVAL; return false; } + } + struct yasl_damage_list damage = {0}; + if (!rects || count > YASL_DAMAGE_MAX) damage.n = -1; + else { + damage.n = (int)count; + for (size_t i = 0; i < count; i++) damage.rects[i] = rects[i]; + } + errno = 0; + bool ok = yasl_surface_present(s, &frame->target, &damage); + if (ok) s->frame_presented = true; + if (!ok && !errno) errno = EIO; + return ok; +} + +void yasl_surface_request_frame(struct yasl_surface *s) +{ + if (!s) return; + yasl_surface_schedule_redraw(s); + if (s->display->loop) yasl_loop_kick(s->display->loop); +} + +static uint64_t pacing_tick(void *ud, yasl_timer_id id, uint64_t interval) +{ + (void)id; (void)interval; + struct yasl_surface *s = ud; + yasl_surface_request_frame(s); + double refresh = yasl_surface_timing(s).refresh_ns; + return refresh >= 1000000.0 && refresh <= 1000000000.0 + ? (uint64_t)refresh : 16666667; +} + +bool yasl_surface_set_pacing(struct yasl_surface *s, yasl_pacing_mode mode) +{ + if (!s || (mode != YASL_PACING_ON_DEMAND && mode != YASL_PACING_CONTINUOUS)) { + errno = EINVAL; + return false; + } + if (s->pacing_timer) { + yasl_surface_del_timer(s, s->pacing_timer); + s->pacing_timer = 0; + } + if (mode == YASL_PACING_CONTINUOUS && !(yasl_display_caps(s->display) & YASL_CAP_FRAME_PACING)) { + s->pacing_timer = yasl_surface_add_timer(s, 16666667, pacing_tick, s); + if (!s->pacing_timer) { + s->pacing = YASL_PACING_ON_DEMAND; + errno = ENOMEM; + return false; + } + } + s->pacing = mode; + if (mode == YASL_PACING_CONTINUOUS) yasl_surface_request_frame(s); + return true; +} + +void yasl_surface_draw(struct yasl_surface *s, struct yasl_frame *frame) +{ + if (!s || !frame || !s->callbacks.draw) return; + struct yasl_display *d = s->display; + uint64_t id = s->id; + frame->surface = s; + frame->target.stride_bytes = frame->target.size.x * (int)sizeof(uint32_t); + frame->scale_120 = s->scale_120; + frame->frame_index = s->draw_index++; + frame->target_time_ns = yasl_surface_frame_time_ns(s, yasl_loop_now_ns()); + s->drawing = true; + s->frame_presented = false; + s->active_frame = frame; + s->callbacks.draw(s, frame, s->ud); + /* The callback may destroy s. Lookup by identity before accessing it. */ + struct yasl_surface *live = id ? yasl_events_find(d, id) : NULL; + if (live) { + live->drawing = false; + live->active_frame = NULL; + if (live->pacing == YASL_PACING_CONTINUOUS && + (yasl_display_caps(d) & YASL_CAP_FRAME_PACING)) + yasl_surface_schedule_redraw(live); + } +} + +static bool window_valid(struct yasl_surface *s) +{ + if (!s || s->popup) { errno = EINVAL; return false; } + return true; +} + +bool yasl_surface_set_size(struct yasl_surface *s, struct yasl_vector2i size) +{ + if (!window_valid(s)) return false; + if (size.x <= 0 || size.y <= 0 || size.x > 32767 || size.y > 32767 || + (int64_t)size.x * size.y * 4 > INT32_MAX) { errno = EINVAL; return false; } + if (!s->display->ops->surface_set_size) { errno = ENOTSUP; return false; } + errno = 0; + bool ok = s->display->ops->surface_set_size(s, size); + if (!ok && !errno) errno = EIO; + return ok; +} + +bool yasl_surface_set_fullscreen(struct yasl_surface *s, bool enabled) +{ + if (!window_valid(s)) return false; + if (!s->display->ops->surface_set_fullscreen) { errno = ENOTSUP; return false; } + errno = 0; + bool ok = s->display->ops->surface_set_fullscreen(s, enabled); + if (!ok && !errno) errno = EIO; + return ok; +} + +bool yasl_surface_set_maximized(struct yasl_surface *s, bool enabled) +{ + if (!window_valid(s)) return false; + if (!s->display->ops->surface_set_maximized) { errno = ENOTSUP; return false; } + errno = 0; + bool ok = s->display->ops->surface_set_maximized(s, enabled); + if (!ok && !errno) errno = EIO; + return ok; +} + +bool yasl_surface_minimize(struct yasl_surface *s) +{ + if (!window_valid(s)) return false; + if (!s->display->ops->surface_minimize) { errno = ENOTSUP; return false; } + errno = 0; + bool ok = s->display->ops->surface_minimize(s); + if (!ok && !errno) errno = EIO; + return ok; +} + +static bool selection_valid(struct yasl_display *d, enum yasl_selection selection) +{ + if (!d || (selection != YASL_CLIPBOARD && selection != YASL_PRIMARY)) { + errno = EINVAL; return false; + } + if (selection == YASL_PRIMARY && !(yasl_display_caps(d) & YASL_CAP_PRIMARY_SELECTION)) { + errno = ENOTSUP; return false; + } + return true; +} + +bool yasl_clipboard_set(struct yasl_display *d, enum yasl_selection selection, const char *text) +{ + if (!selection_valid(d, selection)) return false; + if (!text) { errno = EINVAL; return false; } + if (!yasl_utf8_validate(text, strlen(text))) { errno = EILSEQ; return false; } + if (!d->ops->display_copy_text) { errno = ENOTSUP; return false; } + errno = 0; + bool ok = d->ops->display_copy_text(d, selection, text); + if (!ok && !errno) errno = EIO; + return ok; +} + +uint64_t yasl_clipboard_request(struct yasl_surface *s, enum yasl_selection selection) +{ + if (!s) { errno = EINVAL; return 0; } + struct yasl_display *d = s->display; + if (!selection_valid(d, selection)) return 0; + if (!d->ops->display_request_paste) { errno = ENOTSUP; return 0; } + uint64_t id = yasl_events_request_id(d); + if (!id) return 0; + errno = 0; + if (!d->ops->display_request_paste(d, selection, s, id)) { + if (!errno) errno = EIO; + return 0; + } + return id; +} -- cgit v1.2.3