summaryrefslogtreecommitdiff
path: root/src/common/backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/backend.c')
-rw-r--r--src/common/backend.c222
1 files changed, 217 insertions, 5 deletions
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;
+}