summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/app.c468
-rw-r--r--src/common/backend.c222
-rw-r--r--src/common/backend.h45
-rw-r--r--src/common/events.c372
-rw-r--r--src/common/input.c13
-rw-r--r--src/common/input.h4
-rw-r--r--src/common/meson.build2
7 files changed, 735 insertions, 391 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;
}
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 <errno.h>
#include <stdlib.h>
@@ -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;
+}
diff --git a/src/common/backend.h b/src/common/backend.h
index 13ce9b5..876a4ca 100644
--- a/src/common/backend.h
+++ b/src/common/backend.h
@@ -10,6 +10,7 @@
#include <string.h>
struct yasl_backend_ops;
+struct yasl_event_state;
struct yasl_display {
const struct yasl_backend_ops *ops;
@@ -19,6 +20,9 @@ struct yasl_display {
void *app_runner;
/* Backend's copy of yasl_display_spec.app_id, or NULL; for desktop services. */
const char *app_id;
+ void *callback_ud;
+ struct yasl_event_state *events;
+ struct yasl_surface *surfaces;
};
struct yasl_surface {
@@ -39,8 +43,41 @@ struct yasl_surface {
int timer_count;
int timer_capacity;
void *app_surface;
+ struct yasl_surface *event_next;
+ uint64_t id;
+ void *userdata;
+ bool popup;
+ bool drawing;
+ bool frame_presented;
+ uint32_t reported_scale;
+ uint64_t draw_index;
+ struct yasl_frame *active_frame;
+ yasl_pacing_mode pacing;
+ yasl_timer_id pacing_timer;
};
+bool yasl_events_open(struct yasl_display *d);
+void yasl_events_close(struct yasl_display *d);
+int yasl_events_error(struct yasl_display *d);
+bool yasl_events_pending(struct yasl_display *d);
+struct yasl_surface *yasl_events_find(struct yasl_display *d, uint64_t id);
+bool yasl_events_register(struct yasl_surface *s);
+void yasl_events_unregister(struct yasl_surface *s);
+bool yasl_events_push(struct yasl_display *d, struct yasl_surface *queue_owner,
+ const struct yasl_event *event);
+bool yasl_events_poll_surface(struct yasl_surface *s, struct yasl_event *event);
+void yasl_surface_notify_configure(struct yasl_surface *s, struct yasl_geometry geometry);
+void yasl_surface_notify_close(struct yasl_surface *s);
+void yasl_surface_notify_state(struct yasl_surface *s, uint32_t state);
+void yasl_surface_notify_drag(struct yasl_surface *s, enum yasl_drag_event ev, struct yasl_vector2i pos);
+void yasl_surface_notify_drop(struct yasl_surface *s, enum yasl_drop_event ev, struct yasl_vector2i pos);
+void yasl_surface_notify_composition(struct yasl_surface *s, const char *text, int begin, int end);
+void yasl_surface_draw(struct yasl_surface *s, struct yasl_frame *frame);
+void yasl_display_notify_event(struct yasl_display *d, enum yasl_event_type type);
+void yasl_surface_complete_paste(struct yasl_surface *s, uint64_t request_id,
+ enum yasl_transfer_status status, const char *text, size_t len);
+uint64_t yasl_events_request_id(struct yasl_display *d);
+
void yasl_surface_clear_timers(struct yasl_surface *s);
struct yasl_surface *yasl_create_toplevel(struct yasl_display *d,
const struct yasl_window_spec *spec, void *graphics_device);
@@ -74,6 +111,9 @@ struct yasl_backend_ops {
int (*display_double_click_ms)(const struct yasl_display *d);
void (*display_copy)(struct yasl_display *d, enum yasl_selection sel, const char *text);
void (*display_paste)(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target);
+ bool (*display_copy_text)(struct yasl_display *d, enum yasl_selection sel, const char *text);
+ bool (*display_request_paste)(struct yasl_display *d, enum yasl_selection sel,
+ struct yasl_surface *target, uint64_t request_id);
bool (*display_drag_start)(struct yasl_display *d, struct yasl_surface *origin, const struct yasl_drag_image *image, const struct yasl_drag_listener *l, void *ud);
void (*display_drag_cancel)(struct yasl_display *d);
/* Create with callbacks disabled; common code installs them after return. */
@@ -82,6 +122,11 @@ struct yasl_backend_ops {
struct yasl_surface *(*surface_create_popup)(struct yasl_surface *parent, const struct yasl_popup_spec *spec);
bool (*surface_popup_reposition)(struct yasl_surface *popup, const struct yasl_popup_spec *spec);
void (*surface_destroy)(struct yasl_surface *s);
+ struct input_queue *(*surface_input_queue)(struct yasl_surface *s);
+ bool (*surface_set_size)(struct yasl_surface *s, struct yasl_vector2i size_logical);
+ bool (*surface_set_fullscreen)(struct yasl_surface *s, bool enabled);
+ bool (*surface_set_maximized)(struct yasl_surface *s, bool enabled);
+ bool (*surface_minimize)(struct yasl_surface *s);
struct yasl_display *(*surface_display)(const struct yasl_surface *s);
/* Copy only; must not invoke application callbacks. */
void (*surface_set_callbacks)(struct yasl_surface *s, const struct yasl_window_cbs *cbs, void *ud);
diff --git a/src/common/events.c b/src/common/events.c
new file mode 100644
index 0000000..a9bbcaf
--- /dev/null
+++ b/src/common/events.c
@@ -0,0 +1,372 @@
+#include "backend.h"
+#include "platform/sync.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct event_node {
+ struct yasl_event event;
+ uint64_t queue_id;
+ char *text;
+ void (*release)(void *);
+ struct event_node *next;
+};
+
+struct yasl_event_state {
+ yasl_mutex mutex;
+ struct event_node *head, *tail, *last;
+ uint64_t sequence, identity, request;
+ int error;
+};
+
+static void dispose(struct event_node *node)
+{
+ if (!node) return;
+ if (node->release) node->release(node->event.user.payload);
+ free(node->text);
+ free(node);
+}
+
+static void dispose_list(struct event_node *nodes)
+{
+ while (nodes) {
+ struct event_node *next = nodes->next;
+ dispose(nodes);
+ nodes = next;
+ }
+}
+
+bool yasl_events_open(struct yasl_display *d)
+{
+ struct yasl_event_state *state = calloc(1, sizeof(*state));
+ if (!state) { errno = ENOMEM; return false; }
+ int error = yasl_mutex_init(&state->mutex);
+ if (error) { free(state); errno = error; return false; }
+ d->events = state;
+ return true;
+}
+
+void yasl_events_close(struct yasl_display *d)
+{
+ struct yasl_event_state *state = d->events;
+ if (!state) return;
+ /* Producers must already have stopped. Detach before callbacks so cleanup
+ * can make harmless failed posts without touching a dying queue. */
+ d->events = NULL;
+ dispose_list(state->head);
+ dispose(state->last);
+ yasl_mutex_destroy(&state->mutex);
+ free(state);
+}
+
+int yasl_events_error(struct yasl_display *d)
+{
+ if (!d->events) return 0;
+ yasl_mutex_lock(&d->events->mutex);
+ int error = d->events->error;
+ yasl_mutex_unlock(&d->events->mutex);
+ return error;
+}
+
+bool yasl_events_pending(struct yasl_display *d)
+{
+ if (!d->events) return false;
+ yasl_mutex_lock(&d->events->mutex);
+ bool pending = d->events->head != NULL;
+ yasl_mutex_unlock(&d->events->mutex);
+ return pending;
+}
+
+struct yasl_surface *yasl_events_find(struct yasl_display *d, uint64_t id)
+{
+ for (struct yasl_surface *s = d->surfaces; s; s = s->event_next)
+ if (s->id == id) return s;
+ return NULL;
+}
+
+static bool publish_input(const struct yasl_event *event, void *ud)
+{
+ struct yasl_surface *owner = ud;
+ return yasl_events_push(owner->display, owner, event);
+}
+
+bool yasl_events_register(struct yasl_surface *s)
+{
+ struct yasl_display *d = s->display;
+ if (!d->events) return true; /* Internal test doubles need no queue. */
+ yasl_mutex_lock(&d->events->mutex);
+ if (d->events->identity == UINT64_MAX) {
+ yasl_mutex_unlock(&d->events->mutex);
+ errno = EOVERFLOW;
+ return false;
+ }
+ s->reported_scale = s->scale_120;
+ s->id = ++d->events->identity;
+ s->event_next = d->surfaces;
+ d->surfaces = s;
+ yasl_mutex_unlock(&d->events->mutex);
+ struct input_queue *q = d->ops->surface_input_queue
+ ? d->ops->surface_input_queue(s) : &s->input;
+ if (q) {
+ /* Preserve input generated during native creation. */
+ struct yasl_event event;
+ while (input_queue_pop(q, &event))
+ yasl_events_push(d, s, &event);
+ q->publish = publish_input;
+ q->publish_ud = s;
+ }
+ return true;
+}
+
+void yasl_events_unregister(struct yasl_surface *s)
+{
+ struct yasl_display *d = s->display;
+ if (!d->events) return;
+ struct event_node *discarded = NULL;
+ yasl_mutex_lock(&d->events->mutex);
+ struct yasl_surface **surface = &d->surfaces;
+ while (*surface && *surface != s) surface = &(*surface)->event_next;
+ if (*surface) *surface = s->event_next;
+ struct event_node **entry = &d->events->head;
+ d->events->tail = NULL;
+ while (*entry) {
+ struct event_node *node = *entry;
+ if (node->event.surface_id == s->id || node->queue_id == s->id) {
+ *entry = node->next;
+ node->next = discarded;
+ discarded = node;
+ } else {
+ d->events->tail = node;
+ entry = &node->next;
+ }
+ }
+ s->event_next = NULL;
+ yasl_mutex_unlock(&d->events->mutex);
+ dispose_list(discarded);
+}
+
+static bool append(struct yasl_display *d, struct event_node *node)
+{
+ struct yasl_event_state *state = d->events;
+ if (state->sequence == UINT64_MAX) { errno = EOVERFLOW; return false; }
+ node->event.sequence = ++state->sequence;
+ if (!node->event.time_ns) node->event.time_ns = yasl_loop_now_ns();
+ if (state->tail) state->tail->next = node;
+ else state->head = node;
+ state->tail = node;
+ return true;
+}
+
+static bool string_event(enum yasl_event_type type)
+{
+ return type == YASL_EVENT_TEXT || type == YASL_EVENT_PASTE ||
+ type == YASL_EVENT_DROP || type == YASL_EVENT_COMPOSITION;
+}
+
+bool yasl_events_push(struct yasl_display *d, struct yasl_surface *queue_owner,
+ const struct yasl_event *event)
+{
+ if (!d || !d->events || (event->surface && !event->surface->id)) return true;
+ struct event_node *node = calloc(1, sizeof(*node));
+ if (!node) goto failed;
+ node->event = *event;
+ node->event.surface_id = event->surface ? event->surface->id : 0;
+ node->queue_id = queue_owner ? queue_owner->id : node->event.surface_id;
+ if (string_event(event->type)) {
+ if (event->text.len == SIZE_MAX || (!event->text.data && event->text.len)) {
+ free(node);
+ goto failed;
+ }
+ node->text = malloc(event->text.len + 1);
+ if (!node->text) { free(node); goto failed; }
+ if (event->text.len) memcpy(node->text, event->text.data, event->text.len);
+ node->text[event->text.len] = '\0';
+ if (event->type == YASL_EVENT_PASTE) node->event.paste.data = node->text;
+ else if (event->type == YASL_EVENT_COMPOSITION) node->event.composition.data = node->text;
+ else node->event.text.data = node->text;
+ }
+ yasl_mutex_lock(&d->events->mutex);
+ if ((node->event.surface_id && !yasl_events_find(d, node->event.surface_id)) ||
+ (node->queue_id && !yasl_events_find(d, node->queue_id))) {
+ yasl_mutex_unlock(&d->events->mutex);
+ dispose(node);
+ return true; /* A native teardown notification for a canceled recipient. */
+ }
+ bool ok = append(d, node);
+ if (!ok) d->events->error = errno;
+ yasl_mutex_unlock(&d->events->mutex);
+ if (!ok) dispose(node);
+ return ok;
+failed:
+ yasl_mutex_lock(&d->events->mutex);
+ d->events->error = ENOMEM;
+ yasl_mutex_unlock(&d->events->mutex);
+ errno = ENOMEM;
+ return false;
+}
+
+bool yasl_display_post(struct yasl_display *d, const struct yasl_message *message)
+{
+ if (!d || !d->events || !message) { errno = EINVAL; return false; }
+ struct event_node *node = calloc(1, sizeof(*node));
+ if (!node) { errno = ENOMEM; return false; }
+ node->event = (struct yasl_event){
+ .type = YASL_EVENT_USER, .surface_id = message->target_id,
+ .user = {.code = message->code, .payload = message->payload, .size = message->size},
+ };
+ node->queue_id = message->target_id;
+ node->release = message->release;
+ yasl_mutex_lock(&d->events->mutex);
+ bool ok = !message->target_id || yasl_events_find(d, message->target_id);
+ if (!ok) errno = ENOENT;
+ else ok = append(d, node);
+ yasl_mutex_unlock(&d->events->mutex);
+ if (!ok) { free(node); return false; }
+ if (d->loop) yasl_loop_kick(d->loop);
+ return true;
+}
+
+static bool poll_event(struct yasl_display *d, uint64_t queue_id, struct yasl_event *event)
+{
+ if (!d || !d->events || !event) return false;
+ struct yasl_event_state *state = d->events;
+ yasl_mutex_lock(&state->mutex);
+ struct event_node *discarded = NULL;
+ struct event_node **entry = &state->head;
+ struct event_node *previous = NULL;
+ if (queue_id) {
+ /* The input-only migration adapter uses legacy callbacks for lifecycle.
+ * Retaining those notifications would keep dispatch permanently ready.
+ * Mixing this adapter with display polling is deliberately unsupported. */
+ while (*entry) {
+ struct event_node *candidate = *entry;
+ if (candidate->event.type < YASL_EVENT_KEY ||
+ candidate->event.type > YASL_EVENT_INPUT_RESET) {
+ *entry = candidate->next;
+ if (state->tail == candidate) state->tail = previous;
+ candidate->next = discarded;
+ discarded = candidate;
+ } else {
+ previous = candidate;
+ entry = &candidate->next;
+ }
+ }
+ entry = &state->head;
+ previous = NULL;
+ while (*entry && (*entry)->queue_id != queue_id) {
+ previous = *entry;
+ entry = &(*entry)->next;
+ }
+ }
+ struct event_node *node = *entry;
+ if (!node) {
+ yasl_mutex_unlock(&state->mutex);
+ dispose_list(discarded);
+ return false;
+ }
+ *entry = node->next;
+ if (state->tail == node) state->tail = previous;
+ node->next = NULL;
+ if (node->event.surface_id)
+ node->event.surface = yasl_events_find(d, node->event.surface_id);
+ struct event_node *last = state->last;
+ state->last = node;
+ *event = node->event;
+ yasl_mutex_unlock(&state->mutex);
+ dispose(last);
+ dispose_list(discarded);
+ return true;
+}
+
+bool yasl_display_poll_event(struct yasl_display *d, struct yasl_event *event)
+{
+ return poll_event(d, 0, event);
+}
+
+bool yasl_events_poll_surface(struct yasl_surface *s, struct yasl_event *event)
+{
+ return s && s->id ? poll_event(s->display, s->id, event) : false;
+}
+
+uint64_t yasl_events_request_id(struct yasl_display *d)
+{
+ if (!d->events || d->events->request == UINT64_MAX) { errno = EOVERFLOW; return 0; }
+ return ++d->events->request;
+}
+
+uint64_t yasl_surface_id(const struct yasl_surface *s) { return s ? s->id : 0; }
+void yasl_surface_set_userdata(struct yasl_surface *s, void *ud) { if (s) s->userdata = ud; }
+void *yasl_surface_userdata(const struct yasl_surface *s) { return s ? s->userdata : NULL; }
+
+void yasl_surface_notify_configure(struct yasl_surface *s, struct yasl_geometry geometry)
+{
+ uint32_t previous_scale = s->reported_scale;
+ s->reported_scale = geometry.scale_120;
+ s->size = geometry.size;
+ s->scale_120 = geometry.scale_120;
+ struct yasl_event event = {.type = YASL_EVENT_WINDOW_RESIZE, .surface = s,
+ .resize = {.geometry = geometry, .scale_changed = previous_scale != geometry.scale_120}};
+ yasl_events_push(s->display, s, &event);
+ if (s->display) yasl_surface_request_frame(s);
+ if (s->callbacks.configure) s->callbacks.configure(s, geometry, s->ud);
+}
+
+void yasl_surface_notify_close(struct yasl_surface *s)
+{
+ struct yasl_event event = {.type = YASL_EVENT_WINDOW_CLOSE, .surface = s};
+ yasl_events_push(s->display, s, &event);
+ if (s->callbacks.close) s->callbacks.close(s, s->ud);
+}
+
+void yasl_surface_notify_state(struct yasl_surface *s, uint32_t flags)
+{
+ s->state = flags;
+ struct yasl_event event = {.type = YASL_EVENT_WINDOW_STATE, .surface = s, .state.flags = flags};
+ yasl_events_push(s->display, s, &event);
+ if (s->callbacks.state) s->callbacks.state(s, flags, s->ud);
+}
+
+void yasl_surface_notify_drag(struct yasl_surface *s, enum yasl_drag_event action, struct yasl_vector2i pos)
+{
+ struct yasl_event event = {.type = YASL_EVENT_DRAG, .surface = s, .drag = {action, pos}};
+ yasl_events_push(s->display, s, &event);
+ if (s->callbacks.drag) s->callbacks.drag(s, action, pos, s->ud);
+}
+
+void yasl_surface_notify_drop(struct yasl_surface *s, enum yasl_drop_event action, struct yasl_vector2i pos)
+{
+ struct yasl_event event = {.type = YASL_EVENT_DROP_HOVER, .surface = s, .drop = {action, pos}};
+ yasl_events_push(s->display, s, &event);
+ if (s->callbacks.drop) s->callbacks.drop(s, action, pos, s->ud);
+}
+
+void yasl_surface_notify_composition(struct yasl_surface *s, const char *text, int begin, int end)
+{
+ struct yasl_event event = {.type = YASL_EVENT_COMPOSITION, .surface = s,
+ .composition = {.data = text ? text : "", .len = text ? strlen(text) : 0,
+ .begin = begin, .end = end, .active = text && text[0]}};
+ yasl_events_push(s->display, s, &event);
+}
+
+void yasl_surface_complete_paste(struct yasl_surface *s, uint64_t id,
+ enum yasl_transfer_status status, const char *text, size_t len)
+{
+ struct yasl_event event = {.type = YASL_EVENT_PASTE, .surface = s,
+ .paste = {.data = text ? text : "", .len = text ? len : 0, .request_id = id, .status = status}};
+ if (!s->display->events) {
+ struct input_queue *q = s->display->ops->surface_input_queue
+ ? s->display->ops->surface_input_queue(s) : &s->input;
+ input_queue_push(q, &event);
+ } else yasl_events_push(s->display, s, &event);
+}
+
+void yasl_display_notify_event(struct yasl_display *d, enum yasl_event_type type)
+{
+ struct yasl_event event = {.type = type};
+ yasl_events_push(d, NULL, &event);
+ void (*callback)(void *) = type == YASL_EVENT_DISPLAY_QUIT ? d->callbacks.quit
+ : type == YASL_EVENT_DISPLAY_REOPEN ? d->callbacks.reopen
+ : type == YASL_EVENT_DISPLAY_LOST ? d->callbacks.lost : NULL;
+ if (callback) callback(d->callback_ud);
+}
diff --git a/src/common/input.c b/src/common/input.c
index ce3219c..511cd0a 100644
--- a/src/common/input.c
+++ b/src/common/input.c
@@ -24,14 +24,15 @@ bool input_queue_push(struct input_queue *q, const struct yasl_event *ev)
struct yasl_event copy = *ev;
char *owned = NULL;
if (!copy.time_ns) copy.time_ns = yasl_loop_now_ns();
- if (has_text(ev)) {
+ if (has_text(ev) && !q->publish) {
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, ev->text.data, len);
owned[len] = '\0';
- copy.text.data = owned;
+ if (ev->type == YASL_EVENT_PASTE) copy.paste.data = owned;
+ else copy.text.data = owned;
}
uint32_t buttons = q->buttons;
switch (ev->type) {
@@ -67,6 +68,10 @@ bool input_queue_push(struct input_queue *q, const struct yasl_event *ev)
}
if (ev->type >= YASL_EVENT_POINTER_DOWN && ev->type <= YASL_EVENT_POINTER_LEAVE)
copy.pointer.buttons = buttons;
+ if (q->publish) {
+ q->buttons = buttons;
+ return q->publish(&copy, q->publish_ud);
+ }
if (q->count == q->capacity) {
size_t capacity = q->capacity ? q->capacity * 2 : 32;
if (capacity < q->capacity || capacity > SIZE_MAX / sizeof(*q->events))
@@ -127,8 +132,12 @@ bool input_queue_pop(struct input_queue *q, struct yasl_event *ev)
void input_queue_clear(struct input_queue *q)
{
+ bool (*publish)(const struct yasl_event *, void *) = q->publish;
+ void *publish_ud = q->publish_ud;
discard_pending(q);
free(q->last_text);
free(q->events);
*q = (struct input_queue){0};
+ q->publish = publish;
+ q->publish_ud = publish_ud;
}
diff --git a/src/common/input.h b/src/common/input.h
index aad5c9a..8980c55 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -19,6 +19,10 @@ struct input_queue {
uint32_t buttons;
/* Set by backends whose pointer leave ends any press: no release follows. */
bool leave_ends_press;
+ /* Installed by common code for display-wide publication. Standalone queues
+ * retain their normal storage/pop semantics when publish is NULL. */
+ bool (*publish)(const struct yasl_event *ev, void *ud);
+ void *publish_ud;
};
/* Copies text payloads and stamps time_ns when zero. Unless q->relative is set,
diff --git a/src/common/meson.build b/src/common/meson.build
index 9a30f52..1577132 100644
--- a/src/common/meson.build
+++ b/src/common/meson.build
@@ -1 +1 @@
-sources += files('app.c', 'backend.c', 'desktop.c', 'input.c', 'native.c', 'timing.c', 'uri.c', 'utf8.c')
+sources += files('app.c', 'backend.c', 'desktop.c', 'events.c', 'input.c', 'native.c', 'timing.c', 'uri.c', 'utf8.c')