summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/app.c529
-rw-r--r--src/common/app_internal.h10
-rw-r--r--src/common/backend.c10
-rw-r--r--src/common/backend.h2
-rw-r--r--src/common/meson.build2
5 files changed, 552 insertions, 1 deletions
diff --git a/src/common/app.c b/src/common/app.c
new file mode 100644
index 0000000..15c2a3f
--- /dev/null
+++ b/src/common/app.c
@@ -0,0 +1,529 @@
+#include "yasl/app.h"
+#include "common/app_internal.h"
+#include "common/backend.h"
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct user_event_node {
+ struct yasl_surface *surface;
+ uint32_t type;
+ void *payload;
+ size_t size;
+ struct user_event_node *next;
+};
+
+struct app_surface {
+ struct yasl_surface *surface;
+ struct app_runner *runner;
+ yasl_pacing_mode pacing;
+ yasl_mouse_mode mouse_mode;
+ struct yasl_vector2i last_mouse_pos;
+ bool has_last_mouse_pos;
+ struct yasl_vector2i last_size;
+ uint32_t last_scale_120;
+ uint64_t frame_index;
+ bool frame_in_progress;
+ bool presented;
+ struct app_surface *next;
+};
+
+struct app_runner {
+ struct yasl_display *display;
+ const struct yasl_app_desc *desc;
+ bool running;
+ bool had_surfaces;
+ int surface_count;
+ struct app_surface *surfaces;
+
+ pthread_mutex_t user_event_mutex;
+ struct user_event_node *user_event_head;
+ struct user_event_node *user_event_tail;
+};
+
+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->frame_in_progress = true;
+ 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->pacing == YASL_PACING_CONTINUOUS && ar->running) {
+ yasl_surface_schedule_redraw(s);
+ }
+
+ as->frame_in_progress = false;
+}
+
+static void app_runner_surface_configure(struct yasl_surface *s, struct yasl_geometry geom, void *ud)
+{
+ struct app_surface *as = ud;
+ struct app_runner *ar = as ? as->runner : NULL;
+ if (!ar || !ar->desc || !ar->desc->event)
+ return;
+
+ bool scale_changed = (as->last_scale_120 != geom.scale_120 && as->last_scale_120 != 0);
+ as->last_scale_120 = geom.scale_120;
+ as->last_size = geom.size;
+
+ if (scale_changed) {
+ struct yasl_event ev = {
+ .surface = s,
+ .type = YASL_EVENT_WINDOW_SCALE_CHANGED,
+ .scale = {
+ .scale_120 = geom.scale_120,
+ .size = geom.size,
+ },
+ };
+ if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT)
+ ar->running = false;
+ }
+
+ struct yasl_event ev = {
+ .surface = s,
+ .type = YASL_EVENT_WINDOW_RESIZE,
+ .resize = {
+ .size = geom.size,
+ .scale_120 = geom.scale_120,
+ },
+ };
+ if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT)
+ ar->running = false;
+
+ yasl_surface_schedule_redraw(s);
+}
+
+static void app_runner_surface_close(struct yasl_surface *s, void *ud)
+{
+ 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 = {
+ .surface = s,
+ .type = YASL_EVENT_WINDOW_CLOSE_REQUEST,
+ };
+ if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT) {
+ ar->running = false;
+ }
+}
+
+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->mouse_mode = YASL_MOUSE_NORMAL;
+ as->last_scale_120 = s->scale_120;
+ as->last_size = s->size;
+ s->app_surface = as;
+
+ as->next = ar->surfaces;
+ ar->surfaces = as;
+ ar->surface_count++;
+ ar->had_surfaces = true;
+
+ struct yasl_surface_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)
+{
+ if (!runner || !s || !s->app_surface)
+ return;
+ struct app_runner *ar = runner;
+ struct app_surface *as = s->app_surface;
+
+ struct app_surface **curr = &ar->surfaces;
+ while (*curr) {
+ if (*curr == as) {
+ *curr = as->next;
+ break;
+ }
+ curr = &(*curr)->next;
+ }
+
+ ar->surface_count--;
+ s->app_surface = NULL;
+ free(as);
+}
+
+bool yasl_frame_present_cpu(struct yasl_frame_context *ctx,
+ const struct yasl_rect2i *dirty_rects,
+ int count)
+{
+ 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);
+}
+
+void yasl_surface_set_pacing(struct yasl_surface *s, yasl_pacing_mode mode)
+{
+ 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 yasl_surface_request_frame(struct yasl_surface *s)
+{
+ if (!s)
+ return;
+ yasl_surface_schedule_redraw(s);
+ if (s->display && s->display->loop) {
+ yasl_loop_kick(s->display->loop);
+ }
+}
+
+void yasl_surface_set_mouse_mode(struct yasl_surface *s, yasl_mouse_mode mode)
+{
+ if (!s || !s->app_surface)
+ return;
+ struct app_surface *as = s->app_surface;
+ as->mouse_mode = mode;
+ if (mode == YASL_MOUSE_NORMAL) {
+ yasl_surface_set_cursor(s, YASL_CURSOR_ARROW);
+ } else {
+ yasl_surface_set_cursor(s, YASL_CURSOR_HIDDEN);
+ }
+}
+
+bool yasl_post_event(struct yasl_display *d, struct yasl_surface *s,
+ uint32_t user_type, 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->type = user_type;
+ node->payload = payload;
+ node->size = size;
+ node->next = NULL;
+
+ pthread_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;
+ }
+ pthread_mutex_unlock(&ar->user_event_mutex);
+
+ if (d->loop) {
+ yasl_loop_kick(d->loop);
+ }
+ return true;
+}
+
+int yasl_app_watch_fd(struct yasl_display *d, int fd, short poll_events,
+ int (*cb)(int fd, short revents, void *ud), void *ud)
+{
+ if (!d || !d->loop)
+ return -1;
+ return yasl_loop_add_fd(d->loop, fd, poll_events, cb, ud);
+}
+
+void yasl_app_unwatch_fd(struct yasl_display *d, int watch_id)
+{
+ if (!d || !d->loop)
+ return;
+ yasl_loop_del_fd(d->loop, watch_id);
+}
+
+yasl_timer_id yasl_app_add_timer(struct yasl_display *d, uint64_t interval_ns,
+ yasl_timer_cb cb, void *ud)
+{
+ if (!d || !d->loop)
+ return 0;
+ return yasl_loop_add_timer(d->loop, interval_ns, 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)
+{
+ pthread_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;
+ pthread_mutex_unlock(&ar->user_event_mutex);
+
+ while (node && ar->running) {
+ struct user_event_node *next = node->next;
+ if (ar->desc->event) {
+ struct yasl_event ev = {
+ .surface = node->surface,
+ .type = YASL_EVENT_USER,
+ .user = {
+ .type = node->type,
+ .payload = node->payload,
+ .size = node->size,
+ },
+ };
+ if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT)
+ ar->running = false;
+ }
+ 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_input_event ie;
+ while (yasl_surface_input_poll(as->surface, &ie) && ar->running) {
+ if (!ar->desc->event)
+ continue;
+
+ struct yasl_event ev = {0};
+ ev.surface = ie.surface ? ie.surface : as->surface;
+
+ switch (ie.kind) {
+ case YASL_INPUT_KEY:
+ ev.type = YASL_EVENT_KEY;
+ ev.key.key = ie.key.key;
+ ev.key.action = ie.key.action;
+ ev.key.mods = ie.key.mods;
+ ev.key.codepoint = ie.key.codepoint;
+ ev.key.shifted = ie.key.shifted;
+ ev.key.native_code = ie.key.native_code;
+ ev.key.repeat = (ie.key.action == YASL_KEY_REPEAT);
+ if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT)
+ ar->running = false;
+ break;
+ case YASL_INPUT_TEXT:
+ ev.type = YASL_EVENT_TEXT;
+ ev.text.text = ie.text.text;
+ ev.text.len = ie.text.len;
+ if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT)
+ ar->running = false;
+ break;
+ case YASL_INPUT_MOUSE:
+ if (ie.mouse.kind == YASL_PTR_PRESS || ie.mouse.kind == YASL_PTR_RELEASE) {
+ ev.type = YASL_EVENT_MOUSE_BUTTON;
+ ev.mouse_button.pos = ie.mouse.pos;
+ ev.mouse_button.button = ie.mouse.button;
+ ev.mouse_button.pressed = (ie.mouse.kind == YASL_PTR_PRESS);
+ ev.mouse_button.mods = ie.mouse.mods;
+ ev.mouse_button.pen = ie.mouse.pen;
+ as->last_mouse_pos = ie.mouse.pos;
+ as->has_last_mouse_pos = true;
+ if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT)
+ ar->running = false;
+ } else if (ie.mouse.kind == YASL_PTR_MOTION) {
+ ev.type = YASL_EVENT_MOUSE_MOTION;
+ ev.mouse_motion.pos = ie.mouse.pos;
+ if (as->has_last_mouse_pos) {
+ ev.mouse_motion.delta.x = ie.mouse.pos.x - as->last_mouse_pos.x;
+ ev.mouse_motion.delta.y = ie.mouse.pos.y - as->last_mouse_pos.y;
+ } else {
+ ev.mouse_motion.delta = (struct yasl_vector2i){0, 0};
+ }
+ ev.mouse_motion.mods = ie.mouse.mods;
+ ev.mouse_motion.pen = ie.mouse.pen;
+ as->last_mouse_pos = ie.mouse.pos;
+ as->has_last_mouse_pos = true;
+ if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT)
+ ar->running = false;
+ } else if (ie.mouse.kind == YASL_PTR_SCROLL) {
+ ev.type = YASL_EVENT_MOUSE_SCROLL;
+ ev.mouse_scroll.pos = ie.mouse.pos;
+ ev.mouse_scroll.scroll = ie.mouse.scroll;
+ ev.mouse_scroll.mods = ie.mouse.mods;
+ if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT)
+ ar->running = false;
+ } else if (ie.mouse.kind == YASL_PTR_LEAVE) {
+ ev.type = YASL_EVENT_MOUSE_LEAVE;
+ as->has_last_mouse_pos = false;
+ if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT)
+ ar->running = false;
+ }
+ break;
+ case YASL_INPUT_FOCUS:
+ ev.type = YASL_EVENT_WINDOW_FOCUS;
+ ev.focus.focused = ie.focus.focused;
+ if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT)
+ ar->running = false;
+ break;
+ case YASL_INPUT_DROP_FILES:
+ ev.type = YASL_EVENT_DROP_FILES;
+ ev.drop_files.files = ie.drop_files.files;
+ ev.drop_files.len = ie.drop_files.len;
+ if (ar->desc->event(ar->desc->userdata, &ev) == YASL_APP_QUIT)
+ ar->running = false;
+ break;
+ case YASL_INPUT_RESET:
+ as->has_last_mouse_pos = false;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
+
+int yasl_run(const struct yasl_app_desc *desc)
+{
+ if (!desc || !desc->init)
+ return -1;
+
+ struct yasl_display_spec dspec = {
+ .app_id = desc->app_id,
+ .backend = desc->backend,
+ };
+ struct yasl_display *d = yasl_display_open(NULL, &dspec);
+ if (!d)
+ return -1;
+
+ struct app_runner runner = {
+ .display = d,
+ .desc = desc,
+ .running = true,
+ .had_surfaces = false,
+ .surface_count = 0,
+ .surfaces = NULL,
+ };
+ pthread_mutex_init(&runner.user_event_mutex, NULL);
+ d->app_runner = &runner;
+
+ if (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;
+
+ while (runner.running) {
+ 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) {
+ 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->pacing == YASL_PACING_CONTINUOUS) {
+ yasl_surface_schedule_redraw(as->surface);
+ }
+ }
+ }
+ }
+
+ if (desc->quit) {
+ desc->quit(desc->userdata, d);
+ }
+
+ d->app_runner = NULL;
+ while (runner.surfaces) {
+ struct app_surface *next = runner.surfaces->next;
+ free(runner.surfaces);
+ runner.surfaces = next;
+ }
+
+ pthread_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;
+ }
+ pthread_mutex_unlock(&runner.user_event_mutex);
+ pthread_mutex_destroy(&runner.user_event_mutex);
+
+ yasl_display_close(d);
+ return 0;
+}
diff --git a/src/common/app_internal.h b/src/common/app_internal.h
new file mode 100644
index 0000000..64fa34c
--- /dev/null
+++ b/src/common/app_internal.h
@@ -0,0 +1,10 @@
+#ifndef YASL_APP_INTERNAL_H
+#define YASL_APP_INTERNAL_H
+
+#include "yasl/app.h"
+#include "common/backend.h"
+
+void app_runner_register_surface(void *runner, struct yasl_surface *s);
+void app_runner_unregister_surface(void *runner, struct yasl_surface *s);
+
+#endif
diff --git a/src/common/backend.c b/src/common/backend.c
index 23275d6..e690697 100644
--- a/src/common/backend.c
+++ b/src/common/backend.c
@@ -1,5 +1,6 @@
#include "backend.h"
#include "loop.h"
+#include "common/app_internal.h"
#include <errno.h>
#include <stdlib.h>
@@ -167,6 +168,9 @@ 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 && d->app_runner) {
+ app_runner_register_surface(d->app_runner, s);
+ }
return s;
}
@@ -202,6 +206,9 @@ 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);
+ }
return s;
}
@@ -226,6 +233,9 @@ void yasl_surface_destroy(struct yasl_surface *s)
{
if (!s || !s->display->ops->surface_destroy)
return;
+ if (s->app_surface && s->display->app_runner) {
+ app_runner_unregister_surface(s->display->app_runner, s);
+ }
yasl_surface_clear_timers(s);
input_queue_clear(&s->input);
s->display->ops->surface_destroy(s);
diff --git a/src/common/backend.h b/src/common/backend.h
index 6014cda..b93547a 100644
--- a/src/common/backend.h
+++ b/src/common/backend.h
@@ -14,6 +14,7 @@ struct yasl_display {
void *native_handle;
struct yasl_display_cbs callbacks;
struct yasl_loop *loop;
+ void *app_runner;
};
struct yasl_surface {
@@ -32,6 +33,7 @@ struct yasl_surface {
yasl_timer_id *timers;
int timer_count;
int timer_capacity;
+ void *app_surface;
};
void yasl_surface_clear_timers(struct yasl_surface *s);
diff --git a/src/common/meson.build b/src/common/meson.build
index 1247ff5..e3986cd 100644
--- a/src/common/meson.build
+++ b/src/common/meson.build
@@ -1 +1 @@
-sources += files('backend.c', 'input.c', 'keyboard.c', 'loop.c', 'native.c', 'timing.c', 'utf8.c')
+sources += files('app.c', 'backend.c', 'input.c', 'keyboard.c', 'loop.c', 'native.c', 'timing.c', 'utf8.c')