summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-13 23:41:31 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-13 23:41:31 +0300
commit76073fe5fad8799e5850805f2f090af2cb62c741 (patch)
tree9658019babe4760a3634bdab4da0731a10e42e68 /src/common
parentdd1775a9927922c14f8679ccbc4d3ca8d5917e20 (diff)
Simplify the public event and CPU API
Applications now need far less ceremony: hello.c is a 20-line loop that handles REFRESH, CLOSE and QUIT and draws into the surface's own pixels. - Fold GEOMETRY, STATE and FOCUS into one WINDOW event carrying size, scale and state; unchanged values send nothing. - Guarantee REFRESH once a window has a size and after every resize, never at size zero. - Merge DROP_HOVER into DROP with over/leave/data actions and give drag and drop actions enums. - Keep event strings valid until the next wait and drop yasl_event_release; empty or failed waits report NONE. - Replace copy-in presentation with a lent pixel buffer: yasl_cpu_pixels and yasl_cpu_present. - Remove yasl_poll_event, yasl_cpu_read_pixels, unused event fields and comments that restated names. - Wayland: resize floating windows in request_size instead of reporting it unsupported, and stop marking new GPU surfaces as Vulkan so yasl_vulkan_create_surface no longer fails with NATIVE_WINDOW_IN_USE. Also includes the in-progress public API refactor that was uncommitted in the tree (removal of the app/loop layer, new surface, GL and Vulkan entry points). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'src/common')
-rw-r--r--src/common/app.c153
-rw-r--r--src/common/app_internal.h9
-rw-r--r--src/common/backend.c315
-rw-r--r--src/common/backend.h210
-rw-r--r--src/common/desktop.c30
-rw-r--r--src/common/desktop.h4
-rw-r--r--src/common/events.c196
-rw-r--r--src/common/gl_options.c38
-rw-r--r--src/common/gl_options.h20
-rw-r--r--src/common/input.c51
-rw-r--r--src/common/input.h20
-rw-r--r--src/common/keyboard.h6
-rw-r--r--src/common/loop.h14
-rw-r--r--src/common/meson.build6
-rw-r--r--src/common/native.c50
-rw-r--r--src/common/private_desktop.h113
-rw-r--r--src/common/private_loop.h214
-rw-r--r--src/common/private_native.h90
-rw-r--r--src/common/private_native_cocoa.h55
-rw-r--r--src/common/private_native_wayland.h40
-rw-r--r--src/common/private_native_x11.h40
-rw-r--r--src/common/private_timing.h56
-rw-r--r--src/common/public_internal.h12
-rw-r--r--src/common/surface.c522
-rw-r--r--src/common/timing.c16
-rw-r--r--src/common/timing.h28
-rw-r--r--src/common/types.h1101
-rw-r--r--src/common/uri.c4
-rw-r--r--src/common/utf8.c10
-rw-r--r--src/common/utf8.h12
-rw-r--r--src/common/vulkan.c87
31 files changed, 2909 insertions, 613 deletions
diff --git a/src/common/app.c b/src/common/app.c
deleted file mode 100644
index 63cf2ce..0000000
--- a/src/common/app.c
+++ /dev/null
@@ -1,153 +0,0 @@
-#include "yasl/app.h"
-#include "common/app_internal.h"
-#include "common/backend.h"
-
-#include <errno.h>
-
-struct app_runner {
- struct yasl_display *display;
- const struct yasl_app_desc *desc;
- uint64_t initial_window;
- bool running, failed, had_surfaces;
- int error;
-};
-
-void yasl_app_exit(struct yasl_display *d, enum yasl_app_result result)
-{
- 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; }
-}
-
-struct yasl_surface *yasl_app_window(struct yasl_display *d)
-{
- 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 dispatch_events(struct app_runner *runner)
-{
- 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);
- }
-}
-
-static void draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud)
-{
- 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);
-}
-
-void app_runner_register_surface(void *data, struct yasl_surface *s)
-{
- 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 app_runner_unregister_surface(void *data, struct yasl_surface *s)
-{
- (void)data;
- s->app_surface = NULL;
-}
-
-bool yasl_frame_present_cpu(struct yasl_frame *frame, const struct yasl_rect2i *rects, int count)
-{
- /* 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) { 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 events,
- int (*cb)(int, short, void *), void *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 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, yasl_timer_cb cb, void *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) yasl_loop_del_timer(d->loop, id);
-}
-
-int yasl_run(const struct yasl_app_desc *desc)
-{
- 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 error = errno;
- d->app_runner = NULL;
- yasl_display_close(d);
- errno = error;
- return -1;
- }
- runner.initial_window = s->id;
- yasl_surface_request_frame(s);
- }
- if (desc->init) yasl_app_exit(d, desc->init(desc->userdata, d));
- while (runner.running) {
- 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;
- }
- 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_display_close(d);
- if (runner.failed) errno = error;
- return runner.failed ? -1 : 0;
-}
diff --git a/src/common/app_internal.h b/src/common/app_internal.h
deleted file mode 100644
index 143df41..0000000
--- a/src/common/app_internal.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef YASL_APP_INTERNAL_H
-#define YASL_APP_INTERNAL_H
-
-struct yasl_surface;
-
-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 a81e1e7..8d20f67 100644
--- a/src/common/backend.c
+++ b/src/common/backend.c
@@ -1,42 +1,41 @@
#include "backend.h"
#include "loop.h"
-#include "common/app_internal.h"
#include "common/utf8.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
-static const struct yasl_backend_ops *backend_ops(enum yasl_backend backend)
+static const struct yasl_private_backend_ops *backend_ops(enum yasl_private_backend backend)
{
switch (backend) {
#if YASL_HAS_WAYLAND
- case YASL_BACKEND_WAYLAND: return &yasl_wayland_backend;
+ case YASL_PRIVATE_BACKEND_WAYLAND: return &yasl_private_wayland_backend;
#endif
#if YASL_HAS_X11
- case YASL_BACKEND_X11: return &yasl_x11_backend;
+ case YASL_PRIVATE_BACKEND_X11: return &yasl_private_x11_backend;
#endif
#if YASL_HAS_COCOA
- case YASL_BACKEND_COCOA: return &yasl_cocoa_backend;
+ case YASL_PRIVATE_BACKEND_COCOA: return &yasl_private_cocoa_backend;
#endif
default: return NULL;
}
}
-static struct yasl_display *open_backend(enum yasl_backend backend,
- struct yasl_loop *loop, const struct yasl_display_spec *spec)
+static struct yasl_private_display *open_backend(enum yasl_private_backend backend,
+ struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec)
{
- const struct yasl_backend_ops *ops = backend_ops(backend);
+ const struct yasl_private_backend_ops *ops = backend_ops(backend);
if (!ops) {
errno = ENOSYS;
return NULL;
}
errno = 0;
- struct yasl_display *d = ops->display_open(loop, spec);
+ struct yasl_private_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)) {
+ if (!yasl_private_events_open(d)) {
int error = errno;
ops->display_close(d);
errno = error;
@@ -46,52 +45,52 @@ static struct yasl_display *open_backend(enum yasl_backend backend,
return d;
}
-struct yasl_display *yasl_display_open(struct yasl_loop *loop,
- const struct yasl_display_spec *spec)
+struct yasl_private_display *yasl_private_display_open(struct yasl_private_loop *loop,
+ const struct yasl_private_display_spec *spec)
{
- enum yasl_backend backend = spec ? spec->backend : YASL_BACKEND_AUTO;
- if (backend < YASL_BACKEND_AUTO || backend > YASL_BACKEND_COCOA) {
+ enum yasl_private_backend backend = spec ? spec->backend : YASL_PRIVATE_BACKEND_AUTO;
+ if (backend < YASL_PRIVATE_BACKEND_AUTO || backend > YASL_PRIVATE_BACKEND_COCOA) {
errno = EINVAL;
return NULL;
}
- if (backend != YASL_BACKEND_AUTO)
+ if (backend != YASL_PRIVATE_BACKEND_AUTO)
return open_backend(backend, loop, spec);
#if YASL_HAS_COCOA
- return open_backend(YASL_BACKEND_COCOA, loop, spec);
+ return open_backend(YASL_PRIVATE_BACKEND_COCOA, loop, spec);
#else
const char *session = getenv("XDG_SESSION_TYPE");
bool wayland = getenv("WAYLAND_DISPLAY") || getenv("WAYLAND_SOCKET") ||
(session && strcmp(session, "wayland") == 0);
- enum yasl_backend first = wayland ? YASL_BACKEND_WAYLAND : YASL_BACKEND_X11;
- enum yasl_backend second = wayland ? YASL_BACKEND_X11 : YASL_BACKEND_WAYLAND;
- struct yasl_display *d = NULL;
+ enum yasl_private_backend first = wayland ? YASL_PRIVATE_BACKEND_WAYLAND : YASL_PRIVATE_BACKEND_X11;
+ enum yasl_private_backend second = wayland ? YASL_PRIVATE_BACKEND_X11 : YASL_PRIVATE_BACKEND_WAYLAND;
+ struct yasl_private_display *d = NULL;
if (backend_ops(first)) d = open_backend(first, loop, spec);
if (!d && backend_ops(second)) d = open_backend(second, loop, spec);
return d;
#endif
}
-enum yasl_backend yasl_display_backend(const struct yasl_display *d)
+enum yasl_private_backend yasl_private_display_backend(const struct yasl_private_display *d)
{
- return d ? d->ops->type : YASL_BACKEND_AUTO;
+ return d ? d->ops->type : YASL_PRIVATE_BACKEND_AUTO;
}
-void yasl_display_close(struct yasl_display *d)
+void yasl_private_display_close(struct yasl_private_display *d)
{
if (!d || !d->ops->display_close)
return;
- yasl_events_close(d);
+ yasl_private_events_close(d);
d->ops->display_close(d);
}
-uint32_t yasl_display_caps(const struct yasl_display *d)
+uint32_t yasl_private_display_caps(const struct yasl_private_display *d)
{
if (!d || !d->ops->display_caps)
return 0;
return d->ops->display_caps(d);
}
-int yasl_display_dispatch(struct yasl_display *d, int timeout_ms)
+int yasl_private_display_dispatch(struct yasl_private_display *d, int timeout_ms)
{
if (!d) {
errno = EINVAL;
@@ -101,59 +100,59 @@ int yasl_display_dispatch(struct yasl_display *d, int timeout_ms)
errno = ENOTSUP;
return -1;
}
- int queue_error = yasl_events_error(d);
+ int queue_error = yasl_private_events_error(d);
if (queue_error) { errno = queue_error; return -1; }
- if (yasl_events_pending(d)) timeout_ms = 0;
+ if (yasl_private_events_pending(d)) timeout_ms = 0;
errno = 0;
int result = d->ops->display_dispatch_timeout(d, timeout_ms);
- queue_error = yasl_events_error(d);
+ queue_error = yasl_private_events_error(d);
if (queue_error) { errno = queue_error; return -1; }
if (result < 0 && !errno) errno = EIO;
return result;
}
-void yasl_display_flush(struct yasl_display *d)
+void yasl_private_display_flush(struct yasl_private_display *d)
{
if (!d || !d->ops->display_flush)
return;
d->ops->display_flush(d);
}
-int yasl_display_modifiers(const struct yasl_display *d)
+int yasl_private_display_modifiers(const struct yasl_private_display *d)
{
if (!d || !d->ops->display_modifiers)
return 0;
return d->ops->display_modifiers(d);
}
-int yasl_display_double_click_ms(const struct yasl_display *d)
+int yasl_private_display_double_click_ms(const struct yasl_private_display *d)
{
int ms = d && d->ops->display_double_click_ms ? d->ops->display_double_click_ms(d) : 0;
/* Reject nonsense from misconfigured settings as well as no answer. */
- return ms > 0 && ms <= 10000 ? ms : YASL_DOUBLE_CLICK_MS_DEFAULT;
+ return ms > 0 && ms <= 10000 ? ms : YASL_PRIVATE_DOUBLE_CLICK_MS_DEFAULT;
}
-void yasl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text)
+void yasl_private_display_copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text)
{
if (!d || !d->ops->display_copy || !text ||
- (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY) ||
- (sel == YASL_PRIMARY && !(yasl_display_caps(d) & YASL_CAP_PRIMARY_SELECTION)))
+ (sel != YASL_PRIVATE_CLIPBOARD && sel != YASL_PRIVATE_PRIMARY) ||
+ (sel == YASL_PRIVATE_PRIMARY && !(yasl_private_display_caps(d) & YASL_PRIVATE_CAP_PRIMARY_SELECTION)))
return;
d->ops->display_copy(d, sel, text);
}
-void yasl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target)
+void yasl_private_display_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target)
{
if (!d || !d->ops->display_paste ||
- (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY) ||
- (sel == YASL_PRIMARY && !(yasl_display_caps(d) & YASL_CAP_PRIMARY_SELECTION)))
+ (sel != YASL_PRIVATE_CLIPBOARD && sel != YASL_PRIVATE_PRIMARY) ||
+ (sel == YASL_PRIVATE_PRIMARY && !(yasl_private_display_caps(d) & YASL_PRIVATE_CAP_PRIMARY_SELECTION)))
return;
if (!target || target->display != d)
return;
d->ops->display_paste(d, sel, target);
}
-bool yasl_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)
+bool yasl_private_display_drag_start(struct yasl_private_display *d, struct yasl_surface *origin, const struct yasl_private_drag_image *image, const struct yasl_private_drag_listener *l, void *ud)
{
if (!d || !d->ops->display_drag_start)
return 0;
@@ -163,19 +162,19 @@ bool yasl_display_drag_start(struct yasl_display *d, struct yasl_surface *origin
image->size.x > 32767 || image->size.y > 32767 ||
(int64_t)image->size.x * image->size.y * 4 > INT32_MAX))
return false;
- const struct yasl_drag_listener empty = {0};
+ const struct yasl_private_drag_listener empty = {0};
return d->ops->display_drag_start(d, origin, image, l ? l : &empty, ud);
}
-void yasl_display_drag_cancel(struct yasl_display *d)
+void yasl_private_display_drag_cancel(struct yasl_private_display *d)
{
if (!d || !d->ops->display_drag_cancel)
return;
d->ops->display_drag_cancel(d);
}
-struct yasl_surface *yasl_create_toplevel(struct yasl_display *d,
- const struct yasl_window_spec *spec, void *graphics_device)
+struct yasl_surface *yasl_private_create_toplevel(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *graphics_device)
{
if (!d || !d->ops->surface_create_toplevel || !spec ||
spec->size.x <= 0 || spec->size.y <= 0 ||
@@ -184,14 +183,14 @@ struct yasl_surface *yasl_create_toplevel(struct yasl_display *d,
errno = EINVAL;
return NULL;
}
- if (graphics_device && !(yasl_display_caps(d) & YASL_CAP_NATIVE_GRAPHICS)) {
+ if (graphics_device && !(yasl_private_display_caps(d) & YASL_PRIVATE_CAP_NATIVE_GRAPHICS)) {
errno = ENOTSUP;
return NULL;
}
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)) {
+ if (s && !yasl_private_events_register(s)) {
int error = errno;
d->ops->surface_destroy(s);
errno = error;
@@ -201,21 +200,18 @@ struct yasl_surface *yasl_create_toplevel(struct yasl_display *d,
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)
- app_runner_register_surface(d->app_runner, s);
- else
- yasl_surface_set_callbacks(s, spec->cbs, spec->ud);
+ yasl_private_surface_set_callbacks(s, spec->cbs, spec->ud);
}
return s;
}
-struct yasl_surface *yasl_surface_create_toplevel(struct yasl_display *d,
- const struct yasl_window_spec *spec)
+struct yasl_surface *yasl_private_surface_create_toplevel(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec)
{
- return yasl_create_toplevel(d, spec, NULL);
+ return yasl_private_create_toplevel(d, spec, NULL);
}
-static bool popup_spec_valid(const struct yasl_popup_spec *spec)
+static bool popup_spec_valid(const struct yasl_private_popup_spec *spec)
{
return spec && spec->size.x > 0 && spec->size.y > 0 &&
spec->size.x <= 32767 && spec->size.y <= 32767 &&
@@ -227,13 +223,13 @@ static bool popup_spec_valid(const struct yasl_popup_spec *spec)
(int64_t)spec->content_offset.y + spec->content_size.y <= spec->size.y;
}
-struct yasl_surface *yasl_surface_create_popup(struct yasl_surface *parent, const struct yasl_popup_spec *spec)
+struct yasl_surface *yasl_private_surface_create_popup(struct yasl_surface *parent, const struct yasl_private_popup_spec *spec)
{
if (!parent || !popup_spec_valid(spec)) {
errno = EINVAL;
return NULL;
}
- if (!(yasl_display_caps(parent->display) & YASL_CAP_POPUPS) ||
+ if (!(yasl_private_display_caps(parent->display) & YASL_PRIVATE_CAP_POPUPS) ||
!parent->display->ops->surface_create_popup) {
errno = ENOTSUP;
return NULL;
@@ -243,25 +239,23 @@ struct yasl_surface *yasl_surface_create_popup(struct yasl_surface *parent, cons
if (!s && !errno) errno = ENODEV;
if (s) {
s->popup = true;
- if (!yasl_events_register(s)) {
+ if (!yasl_private_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;
}
-bool yasl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_popup_spec *spec)
+bool yasl_private_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_private_popup_spec *spec)
{
if (!popup || !popup_spec_valid(spec)) {
errno = EINVAL;
return false;
}
- if (!(yasl_display_caps(popup->display) & YASL_CAP_POPUPS) ||
+ if (!(yasl_private_display_caps(popup->display) & YASL_PRIVATE_CAP_POPUPS) ||
!popup->display->ops->surface_popup_reposition) {
errno = ENOTSUP;
return false;
@@ -272,68 +266,61 @@ bool yasl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl
return result;
}
-void yasl_surface_destroy(struct yasl_surface *s)
+void yasl_private_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_events_unregister(s);
- yasl_surface_clear_timers(s);
+ yasl_private_events_unregister(s);
+ yasl_private_surface_clear_timers(s);
input_queue_clear(&s->input);
s->display->ops->surface_destroy(s);
}
-struct yasl_display *yasl_surface_display(const struct yasl_surface *s)
+struct yasl_private_display *yasl_private_surface_display(const struct yasl_surface *s)
{
return s ? s->display : NULL;
}
-bool yasl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_window_cbs *cbs, void *ud)
+bool yasl_private_surface_set_callbacks(struct yasl_surface *s, const struct yasl_private_window_cbs *cbs, void *ud)
{
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->callbacks = cbs ? *cbs : (struct yasl_private_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)
+void yasl_private_surface_set_title(struct yasl_surface *s, const char *title)
{
if (!s || !s->display->ops->surface_set_title)
return;
s->display->ops->surface_set_title(s, title);
}
-void yasl_surface_schedule_redraw(struct yasl_surface *s)
+void yasl_private_surface_schedule_redraw(struct yasl_surface *s)
{
if (!s || !s->display->ops->surface_schedule_redraw)
return;
s->display->ops->surface_schedule_redraw(s);
}
-bool yasl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage)
+bool yasl_private_surface_present(struct yasl_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage)
{
if (!s || !s->display->ops->surface_present || !fb || !damage ||
!fb->generation || fb->generation != s->frame_generation ||
fb->size.x <= 0 || fb->size.y <= 0 ||
- damage->n < -1 || damage->n > YASL_DAMAGE_MAX)
+ damage->n < -1 || damage->n > YASL_PRIVATE_DAMAGE_MAX)
return false;
- struct yasl_damage_list clipped;
+ struct yasl_private_damage_list clipped;
clipped.n = damage->n;
if (damage->n < 0) {
clipped.bounds = damage->bounds;
} else {
clipped.n = 0;
- clipped.bounds = (struct yasl_rect2i){0};
+ clipped.bounds = (struct yasl_private_rect2i){0};
for (int i = 0; i < damage->n; i++) {
- struct yasl_rect2i r = damage->rects[i];
+ struct yasl_private_rect2i r = damage->rects[i];
if (r.width < 0 || r.height < 0) return false;
int64_t right = (int64_t)r.x + r.width;
int64_t bottom = (int64_t)r.y + r.height;
@@ -341,7 +328,7 @@ bool yasl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer
if (right > fb->size.x) right = fb->size.x;
if (bottom > fb->size.y) bottom = fb->size.y;
if (right > x && bottom > y)
- clipped.rects[clipped.n++] = (struct yasl_rect2i){
+ clipped.rects[clipped.n++] = (struct yasl_private_rect2i){
(int)x, (int)y, (int)(right - x), (int)(bottom - y),
};
}
@@ -349,164 +336,164 @@ bool yasl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer
return s->display->ops->surface_present(s, fb, &clipped);
}
-bool yasl_surface_poll_event(struct yasl_surface *s, struct yasl_event *ev)
+bool yasl_private_surface_poll_event(struct yasl_surface *s, struct yasl_private_event *ev)
{
if (!s || !ev)
return false;
if (s->display && s->display->events)
- return yasl_events_poll_surface(s, ev);
+ return yasl_private_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);
}
-bool yasl_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
- struct yasl_vector2i *size)
+bool yasl_private_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
+ struct yasl_private_vector2i *size)
{
if (!size) return false;
- *size = (struct yasl_vector2i){0};
+ *size = (struct yasl_private_vector2i){0};
if (!s || (!out && cap) || !s->display->ops->surface_read_pixels) return false;
return s->display->ops->surface_read_pixels(s, out, cap, size);
}
-bool yasl_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_pointer_mode mode)
+bool yasl_private_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_private_pointer_mode mode)
{
- if (!s || (mode != YASL_POINTER_NORMAL && mode != YASL_POINTER_RELATIVE))
+ if (!s || (mode != YASL_PRIVATE_POINTER_NORMAL && mode != YASL_PRIVATE_POINTER_RELATIVE))
return false;
if (mode == s->pointer_mode)
return true;
- const struct yasl_backend_ops *ops = s->display ? s->display->ops : NULL;
+ const struct yasl_private_backend_ops *ops = s->display ? s->display->ops : NULL;
if (!ops || !ops->surface_set_pointer_mode)
- return mode == YASL_POINTER_NORMAL;
+ return mode == YASL_PRIVATE_POINTER_NORMAL;
if (!ops->surface_set_pointer_mode(s, mode))
return false;
s->pointer_mode = mode;
return true;
}
-struct yasl_geometry yasl_surface_geometry(const struct yasl_surface *s)
+struct yasl_private_geometry yasl_private_surface_geometry(const struct yasl_surface *s)
{
if (!s)
- return (struct yasl_geometry){.scale_120 = YASL_SCALE_UNIT};
+ return (struct yasl_private_geometry){.scale_120 = YASL_PRIVATE_SCALE_UNIT};
if (s->scale_120)
- return (struct yasl_geometry){s->size, s->scale_120};
+ return (struct yasl_private_geometry){s->size, s->scale_120};
if (s->display && s->display->ops && s->display->ops->surface_geometry)
return s->display->ops->surface_geometry(s);
- return (struct yasl_geometry){s->size, YASL_SCALE_UNIT};
+ return (struct yasl_private_geometry){s->size, YASL_PRIVATE_SCALE_UNIT};
}
-uint32_t yasl_surface_state(const struct yasl_surface *s)
+uint32_t yasl_private_surface_state(const struct yasl_surface *s)
{
return s ? s->state : 0;
}
-struct yasl_timing yasl_surface_timing(const struct yasl_surface *s)
+struct yasl_private_timing yasl_private_surface_timing(const struct yasl_surface *s)
{
if (!s)
- return (struct yasl_timing){0};
+ return (struct yasl_private_timing){0};
if (s->display && s->display->ops && s->display->ops->surface_timing)
return s->display->ops->surface_timing(s);
- struct yasl_timing t = {
+ struct yasl_private_timing t = {
.refresh_ns = s->clock.refresh_ns,
.last_present_ns = s->clock.last_present_ns,
- .next_present_ns = yasl_clock_next_present_ns(&s->clock),
+ .next_present_ns = yasl_private_clock_next_present_ns(&s->clock),
.frames_skipped = s->frames_skipped,
.source = s->clock.source,
};
- yasl_clock_resize_stats(&s->clock, &t.resize);
+ yasl_private_clock_resize_stats(&s->clock, &t.resize);
return t;
}
-uint64_t yasl_surface_frame_time_ns(struct yasl_surface *s, uint64_t now)
+uint64_t yasl_private_surface_frame_time_ns(struct yasl_surface *s, uint64_t now)
{
if (!s)
return 0;
if (s->display && s->display->ops && s->display->ops->surface_frame_time_ns)
return s->display->ops->surface_frame_time_ns(s, now);
- return yasl_clock_frame_timestamp_ns(&s->clock, now);
+ return yasl_private_clock_frame_timestamp_ns(&s->clock, now);
}
-void yasl_surface_set_cursor(struct yasl_surface *s, enum yasl_cursor cursor)
+void yasl_private_surface_set_cursor(struct yasl_surface *s, enum yasl_private_cursor cursor)
{
if (!s || !s->display->ops->surface_set_cursor)
return;
s->display->ops->surface_set_cursor(s, cursor);
}
-void yasl_surface_set_background_color(struct yasl_surface *s, uint32_t bg)
+void yasl_private_surface_set_background_color(struct yasl_surface *s, uint32_t bg)
{
if (!s || !s->display->ops->surface_set_background_color)
return;
s->display->ops->surface_set_background_color(s, bg);
}
-const char *yasl_surface_ime_preedit(const struct yasl_surface *s)
+const char *yasl_private_surface_ime_preedit(const struct yasl_surface *s)
{
if (!s || !s->display->ops->surface_ime_preedit)
return 0;
return s->display->ops->surface_ime_preedit(s);
}
-bool yasl_surface_ime_preedit_cursor(const struct yasl_surface *s, int *begin, int *end)
+bool yasl_private_surface_ime_preedit_cursor(const struct yasl_surface *s, int *begin, int *end)
{
if (!s || !begin || !end || !s->display->ops->surface_ime_preedit_cursor)
return 0;
return s->display->ops->surface_ime_preedit_cursor(s, begin, end);
}
-void yasl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_rect2i caret)
+void yasl_private_surface_set_ime_caret(struct yasl_surface *s, struct yasl_private_rect2i caret)
{
if (!s || !s->display->ops->surface_set_ime_caret)
return;
s->display->ops->surface_set_ime_caret(s, caret);
}
-bool yasl_surface_start_interactive_move(struct yasl_surface *s)
+bool yasl_private_surface_start_interactive_move(struct yasl_surface *s)
{
if (!s || !s->display->ops->surface_start_interactive_move)
return 0;
return s->display->ops->surface_start_interactive_move(s);
}
-bool yasl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_resize_edge edge)
+bool yasl_private_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_private_resize_edge edge)
{
if (!s || !s->display->ops->surface_start_interactive_resize ||
- (edge != YASL_EDGE_TOP && edge != YASL_EDGE_BOTTOM &&
- edge != YASL_EDGE_LEFT && edge != YASL_EDGE_RIGHT &&
- edge != YASL_EDGE_TOP_LEFT && edge != YASL_EDGE_TOP_RIGHT &&
- edge != YASL_EDGE_BOTTOM_LEFT && edge != YASL_EDGE_BOTTOM_RIGHT))
+ (edge != YASL_PRIVATE_EDGE_TOP && edge != YASL_PRIVATE_EDGE_BOTTOM &&
+ edge != YASL_PRIVATE_EDGE_LEFT && edge != YASL_PRIVATE_EDGE_RIGHT &&
+ edge != YASL_PRIVATE_EDGE_TOP_LEFT && edge != YASL_PRIVATE_EDGE_TOP_RIGHT &&
+ edge != YASL_PRIVATE_EDGE_BOTTOM_LEFT && edge != YASL_PRIVATE_EDGE_BOTTOM_RIGHT))
return 0;
return s->display->ops->surface_start_interactive_resize(s, edge);
}
-bool yasl_surface_show_window_menu(struct yasl_surface *s, struct yasl_vector2i pos)
+bool yasl_private_surface_show_window_menu(struct yasl_surface *s, struct yasl_private_vector2i pos)
{
if (!s || !s->display->ops->surface_show_window_menu)
return 0;
return s->display->ops->surface_show_window_menu(s, pos);
}
-bool yasl_surface_drag_carry(struct yasl_surface *s, struct yasl_vector2i offset)
+bool yasl_private_surface_drag_carry(struct yasl_surface *s, struct yasl_private_vector2i offset)
{
if (!s || !s->display->ops->surface_drag_carry)
return 0;
return s->display->ops->surface_drag_carry(s, offset);
}
-bool yasl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_vector2i at)
+bool yasl_private_surface_place_under_pointer(struct yasl_surface *s, struct yasl_private_vector2i at)
{
if (!s || !s->display->ops->surface_place_under_pointer)
return 0;
return s->display->ops->surface_place_under_pointer(s, at);
}
-void yasl_surface_clear_timers(struct yasl_surface *s)
+void yasl_private_surface_clear_timers(struct yasl_surface *s)
{
if (!s || !s->timers) return;
- struct yasl_loop *loop = s->display ? s->display->loop : NULL;
+ struct yasl_private_loop *loop = s->display ? s->display->loop : NULL;
for (int i = 0; i < s->timer_count; i++) {
if (loop && s->timers[i]) {
- yasl_loop_del_timer(loop, s->timers[i]);
+ yasl_private_loop_del_timer(loop, s->timers[i]);
}
}
free(s->timers);
@@ -515,13 +502,13 @@ void yasl_surface_clear_timers(struct yasl_surface *s)
s->timer_capacity = 0;
}
-yasl_timer_id yasl_surface_add_timer(struct yasl_surface *s, uint64_t interval_ns,
- yasl_timer_cb cb, void *ud)
+yasl_private_timer_id yasl_private_surface_add_timer(struct yasl_surface *s, uint64_t interval_ns,
+ yasl_private_timer_cb cb, void *ud)
{
if (!s || !s->display || !s->display->loop || !cb || interval_ns == 0)
return 0;
- yasl_timer_id id = yasl_loop_add_timer(s->display->loop, interval_ns, cb, ud);
+ yasl_private_timer_id id = yasl_private_loop_add_timer(s->display->loop, interval_ns, cb, ud);
if (!id) return 0;
/* A one-shot timer retires itself in the loop. Drop retired ids before
@@ -529,15 +516,15 @@ yasl_timer_id yasl_surface_add_timer(struct yasl_surface *s, uint64_t interval_n
if (s->timer_count >= s->timer_capacity) {
int kept = 0;
for (int i = 0; i < s->timer_count; i++)
- if (yasl_loop_timer_armed(s->display->loop, s->timers[i]))
+ if (yasl_private_loop_timer_armed(s->display->loop, s->timers[i]))
s->timers[kept++] = s->timers[i];
s->timer_count = kept;
}
if (s->timer_count >= s->timer_capacity) {
int new_cap = s->timer_capacity ? s->timer_capacity * 2 : 4;
- yasl_timer_id *nt = realloc(s->timers, (size_t)new_cap * sizeof(*nt));
+ yasl_private_timer_id *nt = realloc(s->timers, (size_t)new_cap * sizeof(*nt));
if (!nt) {
- yasl_loop_del_timer(s->display->loop, id);
+ yasl_private_loop_del_timer(s->display->loop, id);
return 0;
}
s->timers = nt;
@@ -547,24 +534,24 @@ yasl_timer_id yasl_surface_add_timer(struct yasl_surface *s, uint64_t interval_n
return id;
}
-bool yasl_surface_del_timer(struct yasl_surface *s, yasl_timer_id id)
+bool yasl_private_surface_del_timer(struct yasl_surface *s, yasl_private_timer_id id)
{
if (!s || id == 0) return false;
for (int i = 0; i < s->timer_count; i++) {
if (s->timers[i] == id) {
s->timers[i] = s->timers[--s->timer_count];
- return s->display && yasl_loop_del_timer(s->display->loop, id);
+ return s->display && yasl_private_loop_del_timer(s->display->loop, id);
}
}
return false;
}
-struct yasl_loop *yasl_display_loop(struct yasl_display *d)
+struct yasl_private_loop *yasl_private_display_loop(struct yasl_private_display *d)
{
return d ? d->loop : NULL;
}
-bool yasl_frame_present(struct yasl_frame *frame, const struct yasl_rect2i *rects, size_t count)
+bool yasl_private_frame_present(struct yasl_private_frame *frame, const struct yasl_private_rect2i *rects, size_t count)
{
if (!frame || !frame->surface || (!rects && count)) { errno = EINVAL; return false; }
struct yasl_surface *s = frame->surface;
@@ -573,81 +560,81 @@ bool yasl_frame_present(struct yasl_frame *frame, const struct yasl_rect2i *rect
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;
+ struct yasl_private_damage_list damage = {0};
+ if (!rects || count > YASL_PRIVATE_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);
+ bool ok = yasl_private_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)
+void yasl_private_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);
+ yasl_private_surface_schedule_redraw(s);
+ if (s->display->loop) yasl_private_loop_kick(s->display->loop);
}
-static uint64_t pacing_tick(void *ud, yasl_timer_id id, uint64_t interval)
+static uint64_t pacing_tick(void *ud, yasl_private_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;
+ yasl_private_surface_request_frame(s);
+ double refresh = yasl_private_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)
+bool yasl_private_surface_set_pacing(struct yasl_surface *s, yasl_private_pacing_mode mode)
{
- if (!s || (mode != YASL_PACING_ON_DEMAND && mode != YASL_PACING_CONTINUOUS)) {
+ if (!s || (mode != YASL_PRIVATE_PACING_ON_DEMAND && mode != YASL_PRIVATE_PACING_CONTINUOUS)) {
errno = EINVAL;
return false;
}
if (s->pacing_timer) {
- yasl_surface_del_timer(s, s->pacing_timer);
+ yasl_private_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 (mode == YASL_PRIVATE_PACING_CONTINUOUS && !(yasl_private_display_caps(s->display) & YASL_PRIVATE_CAP_FRAME_PACING)) {
+ s->pacing_timer = yasl_private_surface_add_timer(s, 16666667, pacing_tick, s);
if (!s->pacing_timer) {
- s->pacing = YASL_PACING_ON_DEMAND;
+ s->pacing = YASL_PRIVATE_PACING_ON_DEMAND;
errno = ENOMEM;
return false;
}
}
s->pacing = mode;
- if (mode == YASL_PACING_CONTINUOUS) yasl_surface_request_frame(s);
+ if (mode == YASL_PRIVATE_PACING_CONTINUOUS) yasl_private_surface_request_frame(s);
return true;
}
-void yasl_surface_draw(struct yasl_surface *s, struct yasl_frame *frame)
+void yasl_private_surface_draw(struct yasl_surface *s, struct yasl_private_frame *frame)
{
if (!s || !frame || !s->callbacks.draw) return;
- struct yasl_display *d = s->display;
+ struct yasl_private_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());
+ frame->target_time_ns = yasl_private_surface_frame_time_ns(s, yasl_private_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;
+ struct yasl_surface *live = id ? yasl_private_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);
+ if (live->pacing == YASL_PRIVATE_PACING_CONTINUOUS &&
+ (yasl_private_display_caps(d) & YASL_PRIVATE_CAP_FRAME_PACING))
+ yasl_private_surface_schedule_redraw(live);
}
}
@@ -657,7 +644,7 @@ static bool window_valid(struct yasl_surface *s)
return true;
}
-bool yasl_surface_set_size(struct yasl_surface *s, struct yasl_vector2i size)
+bool yasl_private_surface_set_size(struct yasl_surface *s, struct yasl_private_vector2i size)
{
if (!window_valid(s)) return false;
if (size.x <= 0 || size.y <= 0 || size.x > 32767 || size.y > 32767 ||
@@ -669,7 +656,7 @@ bool yasl_surface_set_size(struct yasl_surface *s, struct yasl_vector2i size)
return ok;
}
-bool yasl_surface_set_fullscreen(struct yasl_surface *s, bool enabled)
+bool yasl_private_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; }
@@ -679,7 +666,7 @@ bool yasl_surface_set_fullscreen(struct yasl_surface *s, bool enabled)
return ok;
}
-bool yasl_surface_set_maximized(struct yasl_surface *s, bool enabled)
+bool yasl_private_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; }
@@ -689,7 +676,7 @@ bool yasl_surface_set_maximized(struct yasl_surface *s, bool enabled)
return ok;
}
-bool yasl_surface_minimize(struct yasl_surface *s)
+bool yasl_private_surface_minimize(struct yasl_surface *s)
{
if (!window_valid(s)) return false;
if (!s->display->ops->surface_minimize) { errno = ENOTSUP; return false; }
@@ -699,22 +686,22 @@ bool yasl_surface_minimize(struct yasl_surface *s)
return ok;
}
-static bool selection_valid(struct yasl_display *d, enum yasl_selection selection)
+static bool selection_valid(struct yasl_private_display *d, enum yasl_private_selection selection)
{
- if (!d || (selection != YASL_CLIPBOARD && selection != YASL_PRIMARY)) {
+ if (!d || (selection != YASL_PRIVATE_CLIPBOARD && selection != YASL_PRIVATE_PRIMARY)) {
errno = EINVAL; return false;
}
- if (selection == YASL_PRIMARY && !(yasl_display_caps(d) & YASL_CAP_PRIMARY_SELECTION)) {
+ if (selection == YASL_PRIVATE_PRIMARY && !(yasl_private_display_caps(d) & YASL_PRIVATE_CAP_PRIMARY_SELECTION)) {
errno = ENOTSUP; return false;
}
return true;
}
-bool yasl_clipboard_set(struct yasl_display *d, enum yasl_selection selection, const char *text)
+bool yasl_private_clipboard_set(struct yasl_private_display *d, enum yasl_private_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 (!yasl_private_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);
@@ -722,13 +709,13 @@ bool yasl_clipboard_set(struct yasl_display *d, enum yasl_selection selection, c
return ok;
}
-uint64_t yasl_clipboard_request(struct yasl_surface *s, enum yasl_selection selection)
+uint64_t yasl_private_clipboard_request(struct yasl_surface *s, enum yasl_private_selection selection)
{
if (!s) { errno = EINVAL; return 0; }
- struct yasl_display *d = s->display;
+ struct yasl_private_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);
+ uint64_t id = yasl_private_events_request_id(d);
if (!id) return 0;
errno = 0;
if (!d->ops->display_request_paste(d, selection, s, id)) {
diff --git a/src/common/backend.h b/src/common/backend.h
index 876a4ca..e3dc6de 100644
--- a/src/common/backend.h
+++ b/src/common/backend.h
@@ -1,48 +1,64 @@
-#ifndef YASL_BACKEND_H
-#define YASL_BACKEND_H
+#ifndef YASL_PRIVATE_BACKEND_H
+#define YASL_PRIVATE_BACKEND_H
-#include "yasl/yasl.h"
-#include "yasl/loop.h"
-#include "yasl/native.h"
+#include "common/types.h"
+#include "common/private_loop.h"
+#include "common/private_native.h"
#include "common/input.h"
+#include "common/gl_options.h"
#include "common/timing.h"
#include <string.h>
-struct yasl_backend_ops;
-struct yasl_event_state;
+struct yasl_private_backend_ops;
+struct yasl_private_event_state;
-struct yasl_display {
- const struct yasl_backend_ops *ops;
+struct yasl_private_display {
+ const struct yasl_private_backend_ops *ops;
void *native_handle;
- struct yasl_display_cbs callbacks;
- struct yasl_loop *loop;
- void *app_runner;
- /* Backend's copy of yasl_display_spec.app_id, or NULL; for desktop services. */
+ struct yasl_private_display_cbs callbacks;
+ struct yasl_private_loop *loop;
+ /* Backend's copy of yasl_private_display_spec.app_id, or NULL; for desktop services. */
const char *app_id;
void *callback_ud;
- struct yasl_event_state *events;
+ struct yasl_private_event_state *events;
struct yasl_surface *surfaces;
};
struct yasl_surface {
- struct yasl_display *display;
+ unsigned creation_flags;
+ enum yasl_private_presenter presenter;
+ struct yasl_surface *parent;
+ uint32_t *committed_pixels;
+ int committed_width, committed_height;
+ /* Window values last reported through public WINDOW events. */
+ struct yasl_private_vector2i reported_size;
+ uint32_t reported_scale_120, reported_state;
+ bool refresh_owed;
+ bool refresh_active;
+ void (*refresh_callback)(struct yasl_surface *, void *);
+ void *refresh_userdata;
+ bool text_input_active;
+ unsigned text_purpose;
+ char *text_surrounding;
+ size_t text_length, text_begin, text_end;
+ uint64_t text_revision, text_generation;
+ struct yasl_private_display *display;
void *native_handle;
- struct yasl_vector2i size;
+ struct yasl_private_vector2i size;
uint32_t scale_120;
- uint32_t state; /* Authoritative YASL_STATE_* bitmask, maintained by the backend. */
+ uint32_t state; /* Authoritative YASL_PRIVATE_STATE_* bitmask, maintained by the backend. */
uint32_t bg_color;
- struct yasl_window_cbs callbacks;
+ struct yasl_private_window_cbs callbacks;
void *ud;
uint64_t frame_generation;
struct input_queue input;
- enum yasl_pointer_mode pointer_mode;
- struct yasl_frame_clock clock;
+ enum yasl_private_pointer_mode pointer_mode;
+ struct yasl_private_frame_clock clock;
uint64_t frames_skipped;
- yasl_timer_id *timers;
+ yasl_private_timer_id *timers;
int timer_count;
int timer_capacity;
- void *app_surface;
struct yasl_surface *event_next;
uint64_t id;
void *userdata;
@@ -51,40 +67,41 @@ struct yasl_surface {
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;
+ struct yasl_private_frame *active_frame;
+ yasl_private_pacing_mode pacing;
+ yasl_private_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);
+bool yasl_private_events_open(struct yasl_private_display *d);
+void yasl_private_events_close(struct yasl_private_display *d);
+int yasl_private_events_error(struct yasl_private_display *d);
+bool yasl_private_events_pending(struct yasl_private_display *d);
+struct yasl_surface *yasl_private_events_find(struct yasl_private_display *d, uint64_t id);
+bool yasl_private_events_register(struct yasl_surface *s);
+void yasl_private_events_unregister(struct yasl_surface *s);
+bool yasl_private_events_push(struct yasl_private_display *d, struct yasl_surface *queue_owner,
+ const struct yasl_private_event *event);
+bool yasl_private_events_poll_surface(struct yasl_surface *s, struct yasl_private_event *event);
+void yasl_private_surface_notify_configure(struct yasl_surface *s, struct yasl_private_geometry geometry);
+void yasl_private_surface_notify_refresh(struct yasl_surface *s);
+void yasl_private_surface_notify_close(struct yasl_surface *s);
+void yasl_private_surface_notify_state(struct yasl_surface *s, uint32_t state);
+void yasl_private_surface_notify_drag(struct yasl_surface *s, enum yasl_private_drag_event ev, struct yasl_private_vector2i pos);
+void yasl_private_surface_notify_drop(struct yasl_surface *s, enum yasl_private_drop_event ev, struct yasl_private_vector2i pos);
+void yasl_private_surface_notify_composition(struct yasl_surface *s, const char *text, int begin, int end);
+void yasl_private_surface_draw(struct yasl_surface *s, struct yasl_private_frame *frame);
+void yasl_private_display_notify_event(struct yasl_private_display *d, enum yasl_private_event_type type);
+void yasl_private_surface_complete_paste(struct yasl_surface *s, uint64_t request_id,
+ enum yasl_private_transfer_status status, const char *text, size_t len);
+uint64_t yasl_private_events_request_id(struct yasl_private_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);
+void yasl_private_surface_clear_timers(struct yasl_surface *s);
+struct yasl_surface *yasl_private_create_toplevel(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *graphics_device);
/* Report a presented CPU frame's extent, and copy it when out can hold it. */
-static inline bool yasl_copy_frame(const uint32_t *pixels, struct yasl_vector2i extent,
- uint32_t *out, size_t cap, struct yasl_vector2i *size)
+static inline bool yasl_private_copy_frame(const uint32_t *pixels, struct yasl_private_vector2i extent,
+ uint32_t *out, size_t cap, struct yasl_private_vector2i *size)
{
*size = extent;
size_t count = (size_t)extent.x * (size_t)extent.y;
@@ -93,83 +110,92 @@ static inline bool yasl_copy_frame(const uint32_t *pixels, struct yasl_vector2i
return true;
}
-static inline uint64_t yasl_frame_begin(struct yasl_surface *s)
+static inline uint64_t yasl_private_frame_begin(struct yasl_surface *s)
{
if (++s->frame_generation == 0) ++s->frame_generation;
return s->frame_generation;
}
-struct yasl_backend_ops {
- enum yasl_backend type;
- struct yasl_display *(*display_open)(struct yasl_loop *loop, const struct yasl_display_spec *spec);
- void (*display_close)(struct yasl_display *d);
- uint32_t (*display_caps)(const struct yasl_display *d);
- int (*display_dispatch_timeout)(struct yasl_display *d, int timeout_ms);
- void (*display_flush)(struct yasl_display *d);
- int (*display_modifiers)(const struct yasl_display *d);
+typedef void (*yasl_private_gl_proc)(void);
+struct yasl_private_backend_ops {
+ struct yasl_surface *(*surface_create_gl)(struct yasl_private_display *, const struct yasl_private_window_spec *, const int *, struct yasl_surface *share);
+ bool (*gl_make_current)(struct yasl_private_display *, struct yasl_surface *);
+ yasl_private_gl_proc (*gl_get_proc_address)(struct yasl_private_display *, const char *);
+ bool (*gl_swap_interval)(struct yasl_private_display *, struct yasl_surface *, int);
+ bool (*gl_swap_buffers)(struct yasl_private_display *, struct yasl_surface *);
+ void *(*surface_get_metal_layer)(struct yasl_surface *);
+ bool (*surface_update_text_input)(struct yasl_surface *);
+
+ enum yasl_private_backend type;
+ struct yasl_private_display *(*display_open)(struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec);
+ void (*display_close)(struct yasl_private_display *d);
+ uint32_t (*display_caps)(const struct yasl_private_display *d);
+ int (*display_dispatch_timeout)(struct yasl_private_display *d, int timeout_ms);
+ void (*display_flush)(struct yasl_private_display *d);
+ int (*display_modifiers)(const struct yasl_private_display *d);
/* Optional; returns zero or less when the system reports no interval. */
- 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,
+ int (*display_double_click_ms)(const struct yasl_private_display *d);
+ void (*display_copy)(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text);
+ void (*display_paste)(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target);
+ bool (*display_copy_text)(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text);
+ bool (*display_request_paste)(struct yasl_private_display *d, enum yasl_private_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);
+ bool (*display_drag_start)(struct yasl_private_display *d, struct yasl_surface *origin, const struct yasl_private_drag_image *image, const struct yasl_private_drag_listener *l, void *ud);
+ void (*display_drag_cancel)(struct yasl_private_display *d);
/* Create with callbacks disabled; common code installs them after return. */
- struct yasl_surface *(*surface_create_toplevel)(struct yasl_display *d,
- const struct yasl_window_spec *spec, void *graphics_device);
- 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);
+ struct yasl_surface *(*surface_create_toplevel)(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *graphics_device);
+ struct yasl_surface *(*surface_create_popup)(struct yasl_surface *parent, const struct yasl_private_popup_spec *spec);
+ bool (*surface_popup_reposition)(struct yasl_surface *popup, const struct yasl_private_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_size)(struct yasl_surface *s, struct yasl_private_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);
+ struct yasl_private_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);
+ void (*surface_set_callbacks)(struct yasl_surface *s, const struct yasl_private_window_cbs *cbs, void *ud);
void (*surface_set_title)(struct yasl_surface *s, const char *title);
void (*surface_schedule_redraw)(struct yasl_surface *s);
- bool (*surface_present)(struct yasl_surface *s, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage);
+ bool (*surface_present)(struct yasl_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage);
/* Optional; NULL means the common input queue is drained. */
- bool (*surface_poll_event)(struct yasl_surface *s, struct yasl_event *ev);
- /* Optional; NULL means only YASL_POINTER_NORMAL is supported. Backends that
+ bool (*surface_poll_event)(struct yasl_surface *s, struct yasl_private_event *ev);
+ /* Optional; NULL means only YASL_PRIVATE_POINTER_NORMAL is supported. Backends that
* implement RELATIVE push POINTER_MOTION with delta set and pos frozen. */
- bool (*surface_set_pointer_mode)(struct yasl_surface *s, enum yasl_pointer_mode mode);
- struct yasl_geometry (*surface_geometry)(const struct yasl_surface *s);
- struct yasl_timing (*surface_timing)(const struct yasl_surface *s);
+ bool (*surface_set_pointer_mode)(struct yasl_surface *s, enum yasl_private_pointer_mode mode);
+ struct yasl_private_geometry (*surface_geometry)(const struct yasl_surface *s);
+ struct yasl_private_timing (*surface_timing)(const struct yasl_surface *s);
uint64_t (*surface_frame_time_ns)(struct yasl_surface *s, uint64_t now);
- void (*surface_set_cursor)(struct yasl_surface *s, enum yasl_cursor cursor);
+ void (*surface_set_cursor)(struct yasl_surface *s, enum yasl_private_cursor cursor);
void (*surface_set_background_color)(struct yasl_surface *s, uint32_t bg);
const char *(*surface_ime_preedit)(const struct yasl_surface *s);
bool (*surface_ime_preedit_cursor)(const struct yasl_surface *s, int *begin, int *end);
- void (*surface_set_ime_caret)(struct yasl_surface *s, struct yasl_rect2i caret);
+ void (*surface_set_ime_caret)(struct yasl_surface *s, struct yasl_private_rect2i caret);
bool (*surface_start_interactive_move)(struct yasl_surface *s);
- bool (*surface_start_interactive_resize)(struct yasl_surface *s, enum yasl_resize_edge edge);
- bool (*surface_show_window_menu)(struct yasl_surface *s, struct yasl_vector2i pos);
- bool (*surface_drag_carry)(struct yasl_surface *s, struct yasl_vector2i offset);
- bool (*surface_place_under_pointer)(struct yasl_surface *s, struct yasl_vector2i at);
+ bool (*surface_start_interactive_resize)(struct yasl_surface *s, enum yasl_private_resize_edge edge);
+ bool (*surface_show_window_menu)(struct yasl_surface *s, struct yasl_private_vector2i pos);
+ bool (*surface_drag_carry)(struct yasl_surface *s, struct yasl_private_vector2i offset);
+ bool (*surface_place_under_pointer)(struct yasl_surface *s, struct yasl_private_vector2i at);
/* Optional; NULL means the bell is unsupported. */
bool (*surface_bell)(struct yasl_surface *s);
/* Optional; NULL means no readback. Fills size only for an available frame. */
bool (*surface_read_pixels)(struct yasl_surface *s, uint32_t *out, size_t cap,
- struct yasl_vector2i *size);
- void *(*display_get_native_handle)(struct yasl_display *d);
+ struct yasl_private_vector2i *size);
+ void *(*display_get_native_handle)(struct yasl_private_display *d);
void *(*surface_get_native_handle)(struct yasl_surface *s);
bool (*surface_get_native_frame)(struct yasl_surface *s,
- const struct yasl_framebuffer *target, struct yasl_native_frame *out);
+ const struct yasl_private_framebuffer *target, struct yasl_private_native_frame *out);
};
#if YASL_HAS_WAYLAND
-extern const struct yasl_backend_ops yasl_wayland_backend;
+extern const struct yasl_private_backend_ops yasl_private_wayland_backend;
#endif
#if YASL_HAS_X11
-extern const struct yasl_backend_ops yasl_x11_backend;
+extern const struct yasl_private_backend_ops yasl_private_x11_backend;
#endif
#if YASL_HAS_COCOA
-extern const struct yasl_backend_ops yasl_cocoa_backend;
+extern const struct yasl_private_backend_ops yasl_private_cocoa_backend;
#endif
#endif
diff --git a/src/common/desktop.c b/src/common/desktop.c
index 23c62d7..5045604 100644
--- a/src/common/desktop.c
+++ b/src/common/desktop.c
@@ -1,4 +1,4 @@
-#include "yasl/desktop.h"
+#include "common/private_desktop.h"
#include "common/backend.h"
#include "common/desktop.h"
@@ -36,46 +36,60 @@ static size_t copy_out(char *out, size_t cap, const char *s, size_t len)
return len;
}
-bool yasl_display_open_uri(struct yasl_display *d, const char *uri)
+bool yasl_private_display_open_uri(struct yasl_private_display *d, const char *uri)
{
if (!d || !uri || !uri_valid(uri)) return false;
return desktop_open_uri(uri);
}
-bool yasl_display_notify(struct yasl_display *d, const char *title, const char *body)
+bool yasl_private_display_notify(struct yasl_private_display *d, const char *title, const char *body)
{
if (!d || !title) return false;
return desktop_notify(d->app_id, title, body ? body : "");
}
-uint32_t yasl_display_appearance(struct yasl_display *d)
+uint32_t yasl_private_display_appearance(struct yasl_private_display *d)
{
return d ? desktop_appearance() : 0;
}
-bool yasl_surface_bell(struct yasl_surface *s)
+bool yasl_private_surface_bell(struct yasl_surface *s)
{
if (!s || !s->display->ops->surface_bell) return false;
return s->display->ops->surface_bell(s);
}
-size_t yasl_executable_path(char *out, size_t cap)
+size_t yasl_private_executable_path(char *out, size_t cap)
{
if (!out && cap) return 0;
char buf[DESKTOP_STR_MAX];
return copy_out(out, cap, buf, desktop_executable_path(buf, sizeof(buf)));
}
-size_t yasl_resource_dir(char *out, size_t cap)
+size_t yasl_private_resource_dir(char *out, size_t cap)
{
if (!out && cap) return 0;
char buf[DESKTOP_STR_MAX];
return copy_out(out, cap, buf, desktop_resource_dir(buf, sizeof(buf)));
}
-size_t yasl_default_locale(char *out, size_t cap)
+size_t yasl_private_default_locale(char *out, size_t cap)
{
if (!out && cap) return 0;
char buf[DESKTOP_STR_MAX];
return copy_out(out, cap, buf, desktop_default_locale(buf, sizeof(buf)));
}
+
+#include "public_internal.h"
+#include "yasl/desktop.h"
+#include <errno.h>
+bool yasl_open_uri(const char *uri)
+{ if(!uri||!uri_valid(uri))return yasl_public_fail(EINVAL);errno=0;return desktop_open_uri(uri)||yasl_public_fail(errno); }
+bool yasl_notify(const char *title,const char *body)
+{ if(!yasl_public_owner())return false;errno=0;return yasl_private_display_notify(yasl_public_display(),title,body)||yasl_public_fail(errno); }
+unsigned yasl_appearance(void) { return yasl_private_display_appearance(yasl_public_display()); }
+bool yasl_surface_bell(yasl_surface *s)
+{ if(!yasl_public_surface(s,false))return false;errno=0;return yasl_private_surface_bell(s)||yasl_public_fail(errno); }
+size_t yasl_executable_path(char *out,size_t cap) { return yasl_private_executable_path(out,cap); }
+size_t yasl_resource_dir(char *out,size_t cap) { return yasl_private_resource_dir(out,cap); }
+size_t yasl_default_locale(char *out,size_t cap) { return yasl_private_default_locale(out,cap); }
diff --git a/src/common/desktop.h b/src/common/desktop.h
index 9af5952..68763b2 100644
--- a/src/common/desktop.h
+++ b/src/common/desktop.h
@@ -1,5 +1,5 @@
-#ifndef YASL_COMMON_DESKTOP_H
-#define YASL_COMMON_DESKTOP_H
+#ifndef YASL_PRIVATE_COMMON_DESKTOP_H
+#define YASL_PRIVATE_COMMON_DESKTOP_H
#include <stdbool.h>
#include <stddef.h>
diff --git a/src/common/events.c b/src/common/events.c
index a9bbcaf..ba78d77 100644
--- a/src/common/events.c
+++ b/src/common/events.c
@@ -6,15 +6,15 @@
#include <string.h>
struct event_node {
- struct yasl_event event;
+ struct yasl_private_event event;
uint64_t queue_id;
char *text;
void (*release)(void *);
struct event_node *next;
};
-struct yasl_event_state {
- yasl_mutex mutex;
+struct yasl_private_event_state {
+ yasl_private_mutex mutex;
struct event_node *head, *tail, *last;
uint64_t sequence, identity, request;
int error;
@@ -37,67 +37,67 @@ static void dispose_list(struct event_node *nodes)
}
}
-bool yasl_events_open(struct yasl_display *d)
+bool yasl_private_events_open(struct yasl_private_display *d)
{
- struct yasl_event_state *state = calloc(1, sizeof(*state));
+ struct yasl_private_event_state *state = calloc(1, sizeof(*state));
if (!state) { errno = ENOMEM; return false; }
- int error = yasl_mutex_init(&state->mutex);
+ int error = yasl_private_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)
+void yasl_private_events_close(struct yasl_private_display *d)
{
- struct yasl_event_state *state = d->events;
+ struct yasl_private_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);
+ yasl_private_mutex_destroy(&state->mutex);
free(state);
}
-int yasl_events_error(struct yasl_display *d)
+int yasl_private_events_error(struct yasl_private_display *d)
{
if (!d->events) return 0;
- yasl_mutex_lock(&d->events->mutex);
+ yasl_private_mutex_lock(&d->events->mutex);
int error = d->events->error;
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
return error;
}
-bool yasl_events_pending(struct yasl_display *d)
+bool yasl_private_events_pending(struct yasl_private_display *d)
{
if (!d->events) return false;
- yasl_mutex_lock(&d->events->mutex);
+ yasl_private_mutex_lock(&d->events->mutex);
bool pending = d->events->head != NULL;
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
return pending;
}
-struct yasl_surface *yasl_events_find(struct yasl_display *d, uint64_t id)
+struct yasl_surface *yasl_private_events_find(struct yasl_private_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)
+static bool publish_input(const struct yasl_private_event *event, void *ud)
{
struct yasl_surface *owner = ud;
- return yasl_events_push(owner->display, owner, event);
+ return yasl_private_events_push(owner->display, owner, event);
}
-bool yasl_events_register(struct yasl_surface *s)
+bool yasl_private_events_register(struct yasl_surface *s)
{
- struct yasl_display *d = s->display;
+ struct yasl_private_display *d = s->display;
if (!d->events) return true; /* Internal test doubles need no queue. */
- yasl_mutex_lock(&d->events->mutex);
+ yasl_private_mutex_lock(&d->events->mutex);
if (d->events->identity == UINT64_MAX) {
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
errno = EOVERFLOW;
return false;
}
@@ -105,26 +105,26 @@ bool yasl_events_register(struct yasl_surface *s)
s->id = ++d->events->identity;
s->event_next = d->surfaces;
d->surfaces = s;
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_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;
+ struct yasl_private_event event;
while (input_queue_pop(q, &event))
- yasl_events_push(d, s, &event);
+ yasl_private_events_push(d, s, &event);
q->publish = publish_input;
q->publish_ud = s;
}
return true;
}
-void yasl_events_unregister(struct yasl_surface *s)
+void yasl_private_events_unregister(struct yasl_surface *s)
{
- struct yasl_display *d = s->display;
+ struct yasl_private_display *d = s->display;
if (!d->events) return;
struct event_node *discarded = NULL;
- yasl_mutex_lock(&d->events->mutex);
+ yasl_private_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;
@@ -142,30 +142,30 @@ void yasl_events_unregister(struct yasl_surface *s)
}
}
s->event_next = NULL;
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
dispose_list(discarded);
}
-static bool append(struct yasl_display *d, struct event_node *node)
+static bool append(struct yasl_private_display *d, struct event_node *node)
{
- struct yasl_event_state *state = d->events;
+ struct yasl_private_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 (!node->event.time_ns) node->event.time_ns = yasl_private_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)
+static bool string_event(enum yasl_private_event_type type)
{
- return type == YASL_EVENT_TEXT || type == YASL_EVENT_PASTE ||
- type == YASL_EVENT_DROP || type == YASL_EVENT_COMPOSITION;
+ return type == YASL_PRIVATE_EVENT_TEXT || type == YASL_PRIVATE_EVENT_PASTE ||
+ type == YASL_PRIVATE_EVENT_DROP || type == YASL_PRIVATE_EVENT_COMPOSITION || type == YASL_PRIVATE_EVENT_TEXT_EDIT;
}
-bool yasl_events_push(struct yasl_display *d, struct yasl_surface *queue_owner,
- const struct yasl_event *event)
+bool yasl_private_events_push(struct yasl_private_display *d, struct yasl_surface *queue_owner,
+ const struct yasl_private_event *event)
{
if (!d || !d->events || (event->surface && !event->surface->id)) return true;
struct event_node *node = calloc(1, sizeof(*node));
@@ -182,56 +182,57 @@ bool yasl_events_push(struct yasl_display *d, struct yasl_surface *queue_owner,
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;
+ if (event->type == YASL_PRIVATE_EVENT_PASTE) node->event.paste.data = node->text;
+ else if (event->type == YASL_PRIVATE_EVENT_TEXT_EDIT) node->event.text_edit.data = node->text;
+ else if (event->type == YASL_PRIVATE_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);
+ yasl_private_mutex_lock(&d->events->mutex);
+ if ((node->event.surface_id && !yasl_private_events_find(d, node->event.surface_id)) ||
+ (node->queue_id && !yasl_private_events_find(d, node->queue_id))) {
+ yasl_private_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);
+ yasl_private_mutex_unlock(&d->events->mutex);
if (!ok) dispose(node);
return ok;
failed:
- yasl_mutex_lock(&d->events->mutex);
+ yasl_private_mutex_lock(&d->events->mutex);
d->events->error = ENOMEM;
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
errno = ENOMEM;
return false;
}
-bool yasl_display_post(struct yasl_display *d, const struct yasl_message *message)
+bool yasl_private_display_post(struct yasl_private_display *d, const struct yasl_private_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,
+ node->event = (struct yasl_private_event){
+ .type = YASL_PRIVATE_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);
+ yasl_private_mutex_lock(&d->events->mutex);
+ bool ok = !message->target_id || yasl_private_events_find(d, message->target_id);
if (!ok) errno = ENOENT;
else ok = append(d, node);
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
if (!ok) { free(node); return false; }
- if (d->loop) yasl_loop_kick(d->loop);
+ if (d->loop) yasl_private_loop_kick(d->loop);
return true;
}
-static bool poll_event(struct yasl_display *d, uint64_t queue_id, struct yasl_event *event)
+static bool poll_event(struct yasl_private_display *d, uint64_t queue_id, struct yasl_private_event *event)
{
if (!d || !d->events || !event) return false;
- struct yasl_event_state *state = d->events;
- yasl_mutex_lock(&state->mutex);
+ struct yasl_private_event_state *state = d->events;
+ yasl_private_mutex_lock(&state->mutex);
struct event_node *discarded = NULL;
struct event_node **entry = &state->head;
struct event_node *previous = NULL;
@@ -241,8 +242,8 @@ static bool poll_event(struct yasl_display *d, uint64_t queue_id, struct yasl_ev
* 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) {
+ if (candidate->event.type < YASL_PRIVATE_EVENT_KEY ||
+ candidate->event.type > YASL_PRIVATE_EVENT_INPUT_RESET) {
*entry = candidate->next;
if (state->tail == candidate) state->tail = previous;
candidate->next = discarded;
@@ -261,7 +262,7 @@ static bool poll_event(struct yasl_display *d, uint64_t queue_id, struct yasl_ev
}
struct event_node *node = *entry;
if (!node) {
- yasl_mutex_unlock(&state->mutex);
+ yasl_private_mutex_unlock(&state->mutex);
dispose_list(discarded);
return false;
}
@@ -269,104 +270,105 @@ static bool poll_event(struct yasl_display *d, uint64_t queue_id, struct yasl_ev
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);
+ node->event.surface = yasl_private_events_find(d, node->event.surface_id);
struct event_node *last = state->last;
state->last = node;
*event = node->event;
- yasl_mutex_unlock(&state->mutex);
+ yasl_private_mutex_unlock(&state->mutex);
dispose(last);
dispose_list(discarded);
return true;
}
-bool yasl_display_poll_event(struct yasl_display *d, struct yasl_event *event)
+bool yasl_private_display_poll_event(struct yasl_private_display *d, struct yasl_private_event *event)
{
return poll_event(d, 0, event);
}
-bool yasl_events_poll_surface(struct yasl_surface *s, struct yasl_event *event)
+bool yasl_private_events_poll_surface(struct yasl_surface *s, struct yasl_private_event *event)
{
return s && s->id ? poll_event(s->display, s->id, event) : false;
}
-uint64_t yasl_events_request_id(struct yasl_display *d)
+uint64_t yasl_private_events_request_id(struct yasl_private_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; }
+uint64_t yasl_private_surface_id(const struct yasl_surface *s) { return s ? s->id : 0; }
+void yasl_private_surface_set_userdata(struct yasl_surface *s, void *ud) { if (s) s->userdata = ud; }
+void *yasl_private_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)
+void yasl_private_surface_notify_configure(struct yasl_surface *s, struct yasl_private_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,
+ struct yasl_private_event event = {.type = YASL_PRIVATE_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);
+ yasl_private_events_push(s->display, s, &event);
+ if (s->display) yasl_private_surface_request_frame(s);
if (s->callbacks.configure) s->callbacks.configure(s, geometry, s->ud);
}
-void yasl_surface_notify_close(struct yasl_surface *s)
+void yasl_private_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);
+ struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_WINDOW_CLOSE, .surface = s};
+ yasl_private_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)
+void yasl_private_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);
+ struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_WINDOW_STATE, .surface = s, .state.flags = flags};
+ yasl_private_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)
+void yasl_private_surface_notify_drag(struct yasl_surface *s, enum yasl_private_drag_event action, struct yasl_private_vector2i pos)
{
- struct yasl_event event = {.type = YASL_EVENT_DRAG, .surface = s, .drag = {action, pos}};
- yasl_events_push(s->display, s, &event);
+ struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_DRAG, .surface = s, .drag = {action, pos}};
+ yasl_private_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)
+void yasl_private_surface_notify_drop(struct yasl_surface *s, enum yasl_private_drop_event action, struct yasl_private_vector2i pos)
{
- struct yasl_event event = {.type = YASL_EVENT_DROP_HOVER, .surface = s, .drop = {action, pos}};
- yasl_events_push(s->display, s, &event);
+ struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_DROP_HOVER, .surface = s, .drop = {action, pos}};
+ yasl_private_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)
+void yasl_private_surface_notify_composition(struct yasl_surface *s, const char *text, int begin, int end)
{
- struct yasl_event event = {.type = YASL_EVENT_COMPOSITION, .surface = s,
+ struct yasl_private_event event = {.type = YASL_PRIVATE_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);
+ .begin = begin, .end = end, .active = text && text[0],
+ .revision = s->text_revision, .generation = s->text_generation}};
+ yasl_private_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)
+void yasl_private_surface_complete_paste(struct yasl_surface *s, uint64_t id,
+ enum yasl_private_transfer_status status, const char *text, size_t len)
{
- struct yasl_event event = {.type = YASL_EVENT_PASTE, .surface = s,
+ struct yasl_private_event event = {.type = YASL_PRIVATE_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);
+ } else yasl_private_events_push(s->display, s, &event);
}
-void yasl_display_notify_event(struct yasl_display *d, enum yasl_event_type type)
+void yasl_private_display_notify_event(struct yasl_private_display *d, enum yasl_private_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;
+ struct yasl_private_event event = {.type = type};
+ yasl_private_events_push(d, NULL, &event);
+ void (*callback)(void *) = type == YASL_PRIVATE_EVENT_DISPLAY_QUIT ? d->callbacks.quit
+ : type == YASL_PRIVATE_EVENT_DISPLAY_REOPEN ? d->callbacks.reopen
+ : type == YASL_PRIVATE_EVENT_DISPLAY_LOST ? d->callbacks.lost : NULL;
if (callback) callback(d->callback_ud);
}
diff --git a/src/common/gl_options.c b/src/common/gl_options.c
new file mode 100644
index 0000000..ec8f98a
--- /dev/null
+++ b/src/common/gl_options.c
@@ -0,0 +1,38 @@
+#include "gl_options.h"
+#include <errno.h>
+#include <stddef.h>
+bool yasl_private_gl_parse_options(const int *attrs, struct yasl_private_gl_options *out)
+{
+ if (!out) { errno = EINVAL; return false; }
+ struct yasl_private_gl_options o = {.major=3, .minor=2, .profile=YASL_PRIVATE_GL_CORE,
+ .red_bits=8, .green_bits=8, .blue_bits=8, .alpha_bits=8, .depth_bits=24, .stencil_bits=8};
+ if (attrs) for (size_t i=0;;i+=2) {
+ if (i >= 128) { errno=EINVAL; return false; }
+ int key=attrs[i];
+ if (key==YASL_PRIVATE_GL_END) break;
+ int v=attrs[i+1];
+ switch(key) {
+ case YASL_PRIVATE_GL_MAJOR: o.major=v; break;
+ case YASL_PRIVATE_GL_MINOR: o.minor=v; break;
+ case YASL_PRIVATE_GL_PROFILE: o.profile=v; break;
+ case YASL_PRIVATE_GL_DEBUG: o.debug=v; break;
+ case YASL_PRIVATE_GL_FORWARD_COMPAT: o.forward_compat=v; break;
+ case YASL_PRIVATE_GL_RED_BITS: o.red_bits=v; break;
+ case YASL_PRIVATE_GL_GREEN_BITS: o.green_bits=v; break;
+ case YASL_PRIVATE_GL_BLUE_BITS: o.blue_bits=v; break;
+ case YASL_PRIVATE_GL_ALPHA_BITS: o.alpha_bits=v; break;
+ case YASL_PRIVATE_GL_DEPTH_BITS: o.depth_bits=v; break;
+ case YASL_PRIVATE_GL_STENCIL_BITS: o.stencil_bits=v; break;
+ case YASL_PRIVATE_GL_SAMPLES: o.samples=v; break;
+ case YASL_PRIVATE_GL_SRGB: o.srgb=v; break;
+ case YASL_PRIVATE_GL_API: o.api=v; break;
+ default: errno=EINVAL; return false;
+ }
+ }
+ if (o.major<1 || o.major>4 || o.minor<0 || o.minor>9 || o.profile<0 || o.profile>2 ||
+ o.api<0 || o.api>1 || (unsigned)o.debug>1 || (unsigned)o.forward_compat>1 ||
+ (unsigned)o.srgb>1 || (unsigned)o.red_bits>64 || (unsigned)o.green_bits>64 ||
+ (unsigned)o.blue_bits>64 || (unsigned)o.alpha_bits>64 || (unsigned)o.depth_bits>64 ||
+ (unsigned)o.stencil_bits>64 || (unsigned)o.samples>64) { errno=EINVAL; return false; }
+ *out=o; return true;
+}
diff --git a/src/common/gl_options.h b/src/common/gl_options.h
new file mode 100644
index 0000000..eae38bb
--- /dev/null
+++ b/src/common/gl_options.h
@@ -0,0 +1,20 @@
+#ifndef YASL_PRIVATE_GL_OPTIONS_H
+#define YASL_PRIVATE_GL_OPTIONS_H
+#include <stdbool.h>
+/* Attribute values are shared with the public GL bridge; not installed. */
+enum {
+ YASL_PRIVATE_GL_END = 0, YASL_PRIVATE_GL_MAJOR = 1, YASL_PRIVATE_GL_MINOR,
+ YASL_PRIVATE_GL_PROFILE, YASL_PRIVATE_GL_DEBUG, YASL_PRIVATE_GL_FORWARD_COMPAT,
+ YASL_PRIVATE_GL_RED_BITS, YASL_PRIVATE_GL_GREEN_BITS, YASL_PRIVATE_GL_BLUE_BITS,
+ YASL_PRIVATE_GL_ALPHA_BITS, YASL_PRIVATE_GL_DEPTH_BITS, YASL_PRIVATE_GL_STENCIL_BITS,
+ YASL_PRIVATE_GL_SAMPLES, YASL_PRIVATE_GL_SRGB, YASL_PRIVATE_GL_API
+};
+enum { YASL_PRIVATE_GL_ANY = 0, YASL_PRIVATE_GL_CORE = 1, YASL_PRIVATE_GL_COMPATIBILITY = 2 };
+enum { YASL_PRIVATE_GL_OPENGL = 0, YASL_PRIVATE_GL_OPENGL_ES = 1 };
+struct yasl_private_gl_options {
+ int major, minor, profile, debug, forward_compat;
+ int red_bits, green_bits, blue_bits, alpha_bits, depth_bits, stencil_bits;
+ int samples, srgb, api;
+};
+bool yasl_private_gl_parse_options(const int *, struct yasl_private_gl_options *);
+#endif
diff --git a/src/common/input.c b/src/common/input.c
index 511cd0a..338ed2d 100644
--- a/src/common/input.c
+++ b/src/common/input.c
@@ -1,29 +1,29 @@
#include "input.h"
-#include "yasl/loop.h"
+#include "common/private_loop.h"
#include <stdlib.h>
#include <string.h>
-static bool has_text(const struct yasl_event *ev)
+static bool has_text(const struct yasl_private_event *ev)
{
- return ev->type == YASL_EVENT_TEXT || ev->type == YASL_EVENT_PASTE ||
- ev->type == YASL_EVENT_DROP;
+ return ev->type == YASL_PRIVATE_EVENT_TEXT || ev->type == YASL_PRIVATE_EVENT_PASTE ||
+ ev->type == YASL_PRIVATE_EVENT_DROP || ev->type == YASL_PRIVATE_EVENT_TEXT_EDIT;
}
static void discard_pending(struct input_queue *q)
{
for (size_t i = 0; i < q->count; i++) {
- struct yasl_event *ev = &q->events[(q->head + i) & (q->capacity - 1)];
+ struct yasl_private_event *ev = &q->events[(q->head + i) & (q->capacity - 1)];
if (has_text(ev)) free((void *)ev->text.data);
}
q->head = q->count = 0;
}
-bool input_queue_push(struct input_queue *q, const struct yasl_event *ev)
+bool input_queue_push(struct input_queue *q, const struct yasl_private_event *ev)
{
- struct yasl_event copy = *ev;
+ struct yasl_private_event copy = *ev;
char *owned = NULL;
- if (!copy.time_ns) copy.time_ns = yasl_loop_now_ns();
+ if (!copy.time_ns) copy.time_ns = yasl_private_loop_now_ns();
if (has_text(ev) && !q->publish) {
size_t len = ev->text.len;
if ((!ev->text.data && len) || len == SIZE_MAX) goto failed;
@@ -31,42 +31,43 @@ bool input_queue_push(struct input_queue *q, const struct yasl_event *ev)
if (!owned) goto failed;
if (len) memcpy(owned, ev->text.data, len);
owned[len] = '\0';
- if (ev->type == YASL_EVENT_PASTE) copy.paste.data = owned;
+ if (ev->type == YASL_PRIVATE_EVENT_PASTE) copy.paste.data = owned;
+ else if (ev->type == YASL_PRIVATE_EVENT_TEXT_EDIT) copy.text_edit.data = owned;
else copy.text.data = owned;
}
uint32_t buttons = q->buttons;
switch (ev->type) {
- case YASL_EVENT_POINTER_MOTION:
+ case YASL_PRIVATE_EVENT_POINTER_MOTION:
if (!q->relative) {
copy.pointer.delta = q->has_last_pos
- ? (struct yasl_vector2d){ev->pointer.pos.x - q->last_pos.x,
+ ? (struct yasl_private_vector2d){ev->pointer.pos.x - q->last_pos.x,
ev->pointer.pos.y - q->last_pos.y}
- : (struct yasl_vector2d){0, 0};
+ : (struct yasl_private_vector2d){0, 0};
}
q->last_pos = ev->pointer.pos;
q->has_last_pos = true;
break;
- case YASL_EVENT_POINTER_DOWN:
- case YASL_EVENT_POINTER_UP:
+ case YASL_PRIVATE_EVENT_POINTER_DOWN:
+ case YASL_PRIVATE_EVENT_POINTER_UP:
if ((unsigned)ev->pointer.button < 32) {
- uint32_t bit = YASL_BUTTON_BIT(ev->pointer.button);
- buttons = ev->type == YASL_EVENT_POINTER_DOWN ? buttons | bit : buttons & ~bit;
+ uint32_t bit = YASL_PRIVATE_BUTTON_BIT(ev->pointer.button);
+ buttons = ev->type == YASL_PRIVATE_EVENT_POINTER_DOWN ? buttons | bit : buttons & ~bit;
}
q->last_pos = ev->pointer.pos;
q->has_last_pos = true;
break;
- case YASL_EVENT_POINTER_LEAVE:
+ case YASL_PRIVATE_EVENT_POINTER_LEAVE:
if (q->leave_ends_press) buttons = 0;
q->has_last_pos = false;
break;
- case YASL_EVENT_INPUT_RESET:
+ case YASL_PRIVATE_EVENT_INPUT_RESET:
buttons = 0;
q->has_last_pos = false;
break;
default:
break;
}
- if (ev->type >= YASL_EVENT_POINTER_DOWN && ev->type <= YASL_EVENT_POINTER_LEAVE)
+ if (ev->type >= YASL_PRIVATE_EVENT_POINTER_DOWN && ev->type <= YASL_PRIVATE_EVENT_POINTER_LEAVE)
copy.pointer.buttons = buttons;
if (q->publish) {
q->buttons = buttons;
@@ -76,7 +77,7 @@ bool input_queue_push(struct input_queue *q, const struct yasl_event *ev)
size_t capacity = q->capacity ? q->capacity * 2 : 32;
if (capacity < q->capacity || capacity > SIZE_MAX / sizeof(*q->events))
goto failed;
- struct yasl_event *events = malloc(capacity * sizeof(*events));
+ struct yasl_private_event *events = malloc(capacity * sizeof(*events));
if (!events) goto failed;
if (q->events && q->count > 0) {
size_t first = q->capacity - q->head;
@@ -107,7 +108,7 @@ failed:
return false;
}
-bool input_queue_pop(struct input_queue *q, struct yasl_event *ev)
+bool input_queue_pop(struct input_queue *q, struct yasl_private_event *ev)
{
if (!ev || (!q->count && !q->reset_pending)) return false;
if (q->last_text) {
@@ -115,9 +116,9 @@ bool input_queue_pop(struct input_queue *q, struct yasl_event *ev)
q->last_text = NULL;
}
if (q->reset_pending) {
- *ev = (struct yasl_event){
- .type = YASL_EVENT_INPUT_RESET, .surface = q->reset_surface,
- .time_ns = yasl_loop_now_ns(),
+ *ev = (struct yasl_private_event){
+ .type = YASL_PRIVATE_EVENT_INPUT_RESET, .surface = q->reset_surface,
+ .time_ns = yasl_private_loop_now_ns(),
};
q->reset_pending = false;
return true;
@@ -132,7 +133,7 @@ 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;
+ bool (*publish)(const struct yasl_private_event *, void *) = q->publish;
void *publish_ud = q->publish_ud;
discard_pending(q);
free(q->last_text);
diff --git a/src/common/input.h b/src/common/input.h
index 8980c55..d97ad17 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -1,27 +1,27 @@
-#ifndef YASL_INPUT_H
-#define YASL_INPUT_H
+#ifndef YASL_PRIVATE_INPUT_H
+#define YASL_PRIVATE_INPUT_H
-#include "yasl/yasl.h"
+#include "common/types.h"
/* Each queued string and the last successfully polled string are owned here. */
struct input_queue {
- struct yasl_event *events;
+ struct yasl_private_event *events;
size_t head, count, capacity;
char *last_text;
/* Last pointer position, for computing absolute-mode motion deltas. */
- struct yasl_vector2i last_pos;
+ struct yasl_private_vector2i last_pos;
bool has_last_pos;
- /* Set by backends while YASL_POINTER_RELATIVE is active: keep pushed deltas. */
+ /* Set by backends while YASL_PRIVATE_POINTER_RELATIVE is active: keep pushed deltas. */
bool relative;
struct yasl_surface *reset_surface;
bool reset_pending;
- /* YASL_BUTTON_BIT() mask from pushed DOWN/UP events. */
+ /* YASL_PRIVATE_BUTTON_BIT() mask from pushed DOWN/UP events. */
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);
+ bool (*publish)(const struct yasl_private_event *ev, void *ud);
void *publish_ud;
};
@@ -29,8 +29,8 @@ struct input_queue {
* POINTER_MOTION delta is computed from the previous position; otherwise the
* backend-supplied delta is kept. Leave and reset clear the position history.
* Pointer events get the held-button mask; reset clears it. */
-bool input_queue_push(struct input_queue *q, const struct yasl_event *ev);
-bool input_queue_pop(struct input_queue *q, struct yasl_event *ev);
+bool input_queue_push(struct input_queue *q, const struct yasl_private_event *ev);
+bool input_queue_pop(struct input_queue *q, struct yasl_private_event *ev);
void input_queue_clear(struct input_queue *q);
#endif
diff --git a/src/common/keyboard.h b/src/common/keyboard.h
index d8b8bbc..ce144fb 100644
--- a/src/common/keyboard.h
+++ b/src/common/keyboard.h
@@ -1,10 +1,10 @@
-#ifndef YASL_COMMON_KEYBOARD_H
-#define YASL_COMMON_KEYBOARD_H
+#ifndef YASL_PRIVATE_COMMON_KEYBOARD_H
+#define YASL_PRIVATE_COMMON_KEYBOARD_H
#include <stdint.h>
/* Layout labels omit controls, invalid Unicode, and Cocoa function-key PUA. */
-static inline uint32_t yasl_key_label(uint32_t cp)
+static inline uint32_t yasl_private_key_label(uint32_t cp)
{
return cp >= 0x20 && cp != 0x7f && cp <= 0x10ffff &&
!(cp >= 0x80 && cp <= 0x9f) && !(cp >= 0xd800 && cp <= 0xdfff) &&
diff --git a/src/common/loop.h b/src/common/loop.h
index 60c5b64..3a1c542 100644
--- a/src/common/loop.h
+++ b/src/common/loop.h
@@ -1,15 +1,15 @@
-#ifndef YASL_COMMON_LOOP_H
-#define YASL_COMMON_LOOP_H
+#ifndef YASL_PRIVATE_COMMON_LOOP_H
+#define YASL_PRIVATE_COMMON_LOOP_H
-#include "yasl/loop.h"
+#include "common/private_loop.h"
struct pollfd;
-int yasl_loop_next_timeout_ms(const struct yasl_loop *l);
+int yasl_private_loop_next_timeout_ms(const struct yasl_private_loop *l);
/* The loop's current poll set, valid until the next watch change. Backends
* that wait on native events use it to wake on loop readiness. */
-int yasl_loop_poll_set(const struct yasl_loop *l, const struct pollfd **fds);
+int yasl_private_loop_poll_set(const struct yasl_private_loop *l, const struct pollfd **fds);
/* Whether id is armed: added, not deleted, and not retired by its callback. */
-bool yasl_loop_timer_armed(const struct yasl_loop *l, yasl_timer_id id);
+bool yasl_private_loop_timer_armed(const struct yasl_private_loop *l, yasl_private_timer_id id);
-#endif /* YASL_COMMON_LOOP_H */
+#endif /* YASL_PRIVATE_COMMON_LOOP_H */
diff --git a/src/common/meson.build b/src/common/meson.build
index 1577132..819e4a8 100644
--- a/src/common/meson.build
+++ b/src/common/meson.build
@@ -1 +1,5 @@
-sources += files('app.c', 'backend.c', 'desktop.c', 'events.c', 'input.c', 'native.c', 'timing.c', 'uri.c', 'utf8.c')
+sources += files('backend.c', 'desktop.c', 'events.c', 'input.c', 'native.c', 'timing.c', 'uri.c', 'utf8.c', 'surface.c', 'gl_options.c')
+if has_vulkan
+ sources += files('vulkan.c')
+ deps += vulkan_dep
+endif
diff --git a/src/common/native.c b/src/common/native.c
index 42b065c..88604c9 100644
--- a/src/common/native.c
+++ b/src/common/native.c
@@ -2,7 +2,7 @@
#include <errno.h>
-void *yasl_display_get_native_handle(struct yasl_display *d)
+void *yasl_private_display_get_native_handle(struct yasl_private_display *d)
{
if (!d) return NULL;
if (d->native_handle) return d->native_handle;
@@ -10,7 +10,7 @@ void *yasl_display_get_native_handle(struct yasl_display *d)
d->ops->display_get_native_handle(d) : NULL;
}
-void *yasl_surface_get_native_handle(struct yasl_surface *s)
+void *yasl_private_surface_get_native_handle(struct yasl_surface *s)
{
if (!s) return NULL;
if (s->native_handle) return s->native_handle;
@@ -18,18 +18,18 @@ void *yasl_surface_get_native_handle(struct yasl_surface *s)
s->display->ops->surface_get_native_handle(s) : NULL;
}
-struct yasl_surface *yasl_surface_create_native(struct yasl_display *d,
- const struct yasl_window_spec *spec, void *graphics_device)
+struct yasl_surface *yasl_private_surface_create_native(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *graphics_device)
{
if (!graphics_device) {
errno = EINVAL;
return NULL;
}
- return yasl_create_toplevel(d, spec, graphics_device);
+ return yasl_private_create_toplevel(d, spec, graphics_device);
}
-bool yasl_surface_get_native_frame(struct yasl_surface *s,
- const struct yasl_framebuffer *target, struct yasl_native_frame *out)
+bool yasl_private_surface_get_native_frame(struct yasl_surface *s,
+ const struct yasl_private_framebuffer *target, struct yasl_private_native_frame *out)
{
if (!s || !target || !out || !target->generation ||
target->generation != s->frame_generation ||
@@ -37,3 +37,39 @@ bool yasl_surface_get_native_frame(struct yasl_surface *s,
return false;
return s->display->ops->surface_get_native_frame(s, target, out);
}
+
+#include "public_internal.h"
+#include "yasl/metal.h"
+#if YASL_HAS_WAYLAND
+#include "yasl/native/wayland.h"
+struct wl_display *yasl_wayland_display(const yasl_surface *s)
+{ return s && s->display->ops->type==YASL_PRIVATE_BACKEND_WAYLAND ? yasl_private_display_get_native_handle(s->display):NULL; }
+struct wl_surface *yasl_wayland_surface(const yasl_surface *s)
+{ return s && s->display->ops->type==YASL_PRIVATE_BACKEND_WAYLAND ? yasl_private_surface_get_native_handle((yasl_surface*)s):NULL; }
+#endif
+#if YASL_HAS_X11
+#include "yasl/native/x11.h"
+Display *yasl_x11_display(const yasl_surface *s)
+{ return s && s->display->ops->type==YASL_PRIVATE_BACKEND_X11 ? yasl_private_display_get_native_handle(s->display):NULL; }
+uintptr_t yasl_x11_window(const yasl_surface *s)
+{ return s && s->display->ops->type==YASL_PRIVATE_BACKEND_X11 ? (uintptr_t)yasl_private_surface_get_native_handle((yasl_surface*)s):0; }
+#endif
+#if YASL_HAS_COCOA
+#include "yasl/native/cocoa.h"
+#include "common/private_native_cocoa.h"
+NSApplication *yasl_cocoa_application(void)
+{ struct yasl_private_display *d=yasl_public_display();return d ? yasl_private_cocoa_display_get_application(d):NULL; }
+NSWindow *yasl_cocoa_window(const yasl_surface *s)
+{ return yasl_private_cocoa_surface_get_window((yasl_surface*)s); }
+NSView *yasl_cocoa_view(const yasl_surface *s)
+{ return yasl_private_cocoa_surface_get_view((yasl_surface*)s); }
+#endif
+CAMetalLayer *yasl_metal_layer(yasl_surface *s)
+{
+ if(!yasl_public_surface(s,false))return NULL;
+ if(s->presenter!=YASL_PRIVATE_PRESENTER_NONE && s->presenter!=YASL_PRIVATE_PRESENTER_METAL) {yasl_public_fail(EBUSY);return NULL;}
+ if(!s->display->ops->surface_get_metal_layer){yasl_public_fail(ENOTSUP);return NULL;}
+ CAMetalLayer *layer=s->display->ops->surface_get_metal_layer(s);
+ if(!layer){yasl_public_fail(ENOTSUP);return NULL;}
+ s->presenter=YASL_PRIVATE_PRESENTER_METAL;return layer;
+}
diff --git a/src/common/private_desktop.h b/src/common/private_desktop.h
new file mode 100644
index 0000000..b408585
--- /dev/null
+++ b/src/common/private_desktop.h
@@ -0,0 +1,113 @@
+/**
+ * @file
+ * Desktop services next to windowing: opening URIs, notifications, the bell,
+ * appearance preferences, and process paths.
+ *
+ * Display and surface functions use the display's opening thread (the main
+ * thread on Cocoa). Path and locale functions may be called from any thread.
+ * On Linux, notifications and appearance use the D-Bus session bus when YASL
+ * is built with libdbus-1; without it they report unsupported.
+ *
+ * Functions that fill a buffer return the full string length in bytes,
+ * excluding the NUL, or zero on failure. A result less than cap means out
+ * holds the NUL-terminated string. Otherwise out is unchanged; call again with
+ * a buffer of at least the result plus one.
+ */
+#ifndef YASL_PRIVATE_DESKTOP_H
+#define YASL_PRIVATE_DESKTOP_H
+
+#include "common/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Appearance preference flags returned by yasl_private_display_appearance(). */
+enum yasl_private_appearance : uint32_t {
+ YASL_PRIVATE_APPEARANCE_DARK = 1 << 0, /**< The user prefers dark colors. */
+ YASL_PRIVATE_APPEARANCE_LIGHT = 1 << 1, /**< The user prefers light colors. */
+ YASL_PRIVATE_APPEARANCE_HIGH_CONTRAST = 1 << 2, /**< The user asks for higher contrast. */
+};
+
+/**
+ * Open a URI with the user's default handler.
+ * @param d Display, or NULL.
+ * @param uri NUL-terminated absolute URI with a scheme, such as https: or file:.
+ * @return true if the request was handed to the system; false for NULL
+ * arguments, a missing scheme, or a failed launch.
+ * YASL checks only the URI syntax. The application decides which schemes and
+ * hosts it trusts. Success does not confirm that a handler opened the URI.
+ * Linux runs xdg-open from PATH in a new session, with default signal
+ * dispositions, an empty signal mask, stdin on /dev/null, and no descriptors
+ * above stderr. It forks a short-lived helper first, which raises SIGCHLD;
+ * YASL reaps it unless the application's reaper does. Cocoa uses NSWorkspace.
+ */
+bool yasl_private_display_open_uri(struct yasl_private_display *d, const char *uri);
+/**
+ * Show a desktop notification.
+ * @param d Display, or NULL.
+ * @param title NUL-terminated UTF-8 summary.
+ * @param body NUL-terminated UTF-8 body, or NULL for none.
+ * @return true if the request was sent; false for NULL arguments or when no
+ * notification service is available.
+ * Linux sends org.freedesktop.Notifications.Notify with the display's app_id
+ * as the application name and desktop-entry hint. Cocoa uses the user
+ * notification center, which needs an app bundle with a bundle identifier,
+ * and asks for permission on first use. Success does not confirm that the
+ * notification was shown.
+ */
+bool yasl_private_display_notify(struct yasl_private_display *d, const char *title, const char *body);
+/**
+ * Read the user's appearance preferences.
+ * @param d Display, or NULL.
+ * @return A yasl_private_appearance bitmask, or zero for NULL, no preference, or no
+ * way to read it.
+ * Linux reads color-scheme and contrast from the XDG Settings portal, which
+ * waits briefly for a reply, then falls back to the GTK_THEME variable. Cocoa
+ * reads the effective appearance and the increase-contrast setting. The
+ * result is not cached and no change notification is sent; query again when
+ * the window is focused.
+ */
+uint32_t yasl_private_display_appearance(struct yasl_private_display *d);
+/**
+ * Ring the system bell for a surface.
+ * @param s Surface, or NULL.
+ * @return true if the request was sent; false for NULL or when unsupported.
+ * The system decides whether the bell is audible, visual, or muted. Wayland
+ * needs the xdg-system-bell protocol; X11 uses the XKB bell for the window
+ * and Cocoa uses NSBeep.
+ */
+bool yasl_private_surface_bell(struct yasl_surface *s);
+/**
+ * Get the absolute path of the running executable, with symbolic links resolved.
+ * @param out Output buffer, or NULL when cap is zero.
+ * @param cap Size of out in bytes.
+ * @return Path length, or zero on failure. See the buffer rule in this file.
+ */
+size_t yasl_private_executable_path(char *out, size_t cap);
+/**
+ * Get the resource directory of the app bundle that contains the executable.
+ * @param out Output buffer, or NULL when cap is zero.
+ * @param cap Size of out in bytes.
+ * @return Path length, or zero when the executable is not in an app bundle.
+ * Only Cocoa has app bundles; other platforms return zero.
+ */
+size_t yasl_private_resource_dir(char *out, size_t cap);
+/**
+ * Get the user's preferred locale as a POSIX locale name, such as "en_US.UTF-8".
+ * @param out Output buffer, or NULL when cap is zero.
+ * @param cap Size of out in bytes.
+ * @return Name length, or zero on failure.
+ * Linux returns the first nonempty LC_ALL, LC_CTYPE, or LANG, and "C.UTF-8"
+ * when none is set. Cocoa builds the name from the current locale's language
+ * and region with a ".UTF-8" codeset, because apps started from Finder have no
+ * locale variables. Use the result to set LANG for child processes; YASL does
+ * not change the process locale.
+ */
+size_t yasl_private_default_locale(char *out, size_t cap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* YASL_PRIVATE_DESKTOP_H */
diff --git a/src/common/private_loop.h b/src/common/private_loop.h
new file mode 100644
index 0000000..432bbc3
--- /dev/null
+++ b/src/common/private_loop.h
@@ -0,0 +1,214 @@
+/**
+ * @file
+ * Optional event loop, file watches, and timers.
+ *
+ * Use the loop's owning thread for all calls and callbacks, except
+ * yasl_private_loop_now_ns(), yasl_private_loop_kick(), and yasl_private_loop_post(), which any thread
+ * may call. A display borrows a supplied loop. For window applications,
+ * drive yasl_private_display_dispatch(), drain input, then yasl_private_display_flush(); the
+ * standalone loop_run() function does not replace this cycle. Timer intervals are
+ * nanoseconds; timeout_ms arguments are milliseconds. Do not recursively step/run
+ * or destroy a loop from a callback.
+ */
+#ifndef YASL_PRIVATE_LOOP_H
+#define YASL_PRIVATE_LOOP_H
+
+#include "common/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Optional event loop. Use the display's loop when adding application timers.
+ * A step waits at most timeout_ms: zero polls, a negative value waits indefinitely.
+ * Calls and callbacks use the loop's owning thread. */
+/**
+ * Create an owned standalone file-descriptor and timer loop.
+ * @return A loop to destroy with yasl_private_loop_destroy(), or NULL on allocation failure.
+ * Use on one owning thread. Passing it to yasl_private_display_open() only lends it to
+ * the display; the caller retains ownership.
+ */
+struct yasl_private_loop *yasl_private_loop_create(void);
+/**
+ * Release a loop, its watches, its timers, and its signal watch.
+ * @param l Loop, or NULL for no action.
+ * Close displays borrowing this loop first. Must not run during a loop callback
+ * or an active step/run. Watched file descriptors are not closed. Posted
+ * functions that have not run are dropped; no thread may still post or kick.
+ */
+void yasl_private_loop_destroy(struct yasl_private_loop *l);
+/**
+ * Step a standalone loop until yasl_private_loop_quit() is called.
+ * @param l Loop.
+ * @return Zero after quit, or -1 on failure.
+ * For a window application, use a dispatch, input-poll, flush cycle instead;
+ * loop_run alone does not perform that full cycle on every backend.
+ */
+int yasl_private_loop_run(struct yasl_private_loop *l);
+/**
+ * Wait for file-descriptor readiness or a timer deadline and run callbacks.
+ * @param l Loop.
+ * @param timeout_ms Maximum wait: zero polls, negative waits indefinitely.
+ * @return Number of callbacks handled, or -1 on failure.
+ * Timer deadlines shorten the wait. Callbacks can add/delete sources, including
+ * themselves. Do not recursively step/run this loop.
+ */
+int yasl_private_loop_step(struct yasl_private_loop *l, int timeout_ms);
+/**
+ * Ask the currently running yasl_private_loop_run() to return after this step.
+ * @param l Loop, or NULL for no action.
+ * Call on the owning thread, usually from a callback.
+ */
+void yasl_private_loop_quit(struct yasl_private_loop *l);
+/**
+ * Wake a loop that is waiting, without running any callback.
+ * @param l Loop, or NULL for no action.
+ * May be called from any thread and from a signal handler. The loop must stay
+ * alive until the call returns. A waiting yasl_private_loop_step(), yasl_private_loop_run(), or
+ * yasl_private_display_dispatch() on this loop returns early; a loop that is not
+ * waiting returns early from its next wait instead. Kicks coalesce. This does
+ * not schedule a redraw or enqueue an event; use yasl_private_loop_post() to run code
+ * on the owning thread.
+ */
+void yasl_private_loop_kick(struct yasl_private_loop *l);
+/**
+ * Run a function on the loop's owning thread.
+ * @param l Loop.
+ * @param fn Required function, called once with ud during a later step.
+ * @param ud Borrowed userdata until fn runs.
+ * @return true if queued; false for NULL arguments, a loop without a wake
+ * pipe, or allocation failure.
+ * May be called from any thread, but not from a signal handler. Wakes the loop
+ * like yasl_private_loop_kick(). Functions run in posting order, during a
+ * yasl_private_loop_step() or display dispatch on the owning thread. They may post
+ * again; a function posted while posts are running waits for a later step.
+ * Stop posting before yasl_private_loop_destroy(), which drops functions that have
+ * not run.
+ */
+bool yasl_private_loop_post(struct yasl_private_loop *l, void (*fn)(void *ud), void *ud);
+/**
+ * Deliver POSIX signals to a callback on the loop's owning thread.
+ * @param l Loop.
+ * @param signals Signal numbers to watch; the array is read during the call.
+ * @param n Number of entries, at least one.
+ * @param cb Required callback, given ud and one signal number per delivery.
+ * @param ud Borrowed userdata until the watch is removed.
+ * @return true if every signal is watched; false with errno set and no
+ * dispositions changed. EINVAL means a NULL or invalid argument, including
+ * SIGKILL and SIGSTOP. EBUSY means another loop watches one of the signals.
+ * Replaces this loop's previous watch, if any. Installs one process-wide
+ * handler per signal with SA_RESTART and an empty handler mask, and saves the
+ * previous action. The handler only records the signal and wakes the loop,
+ * so it may run on any thread. YASL does not change any thread's signal mask:
+ * a signal blocked in every thread stays pending and is not delivered. Caught
+ * signals reset to their default action across exec, so spawned programs
+ * do not inherit the handler. Repeated deliveries of one signal before the
+ * callback runs coalesce into one callback. SIGCHLD reports stopped and
+ * continued children as well as exits. Callbacks run during yasl_private_loop_step()
+ * or display dispatch, and may remove the watch.
+ */
+bool yasl_private_loop_watch_signals(struct yasl_private_loop *l, const int *signals, int n,
+ void (*cb)(void *ud, int signo), void *ud);
+/**
+ * Stop watching signals and restore their previous actions.
+ * @param l Loop, or NULL for no action.
+ * Does nothing if the loop watches no signals. Signals that arrived but have
+ * not reached the callback are dropped. yasl_private_loop_destroy() also unwatches.
+ */
+void yasl_private_loop_unwatch_signals(struct yasl_private_loop *l);
+/**
+ * Read the monotonic clock used by YASL timers and timing results.
+ * @return Nanoseconds from CLOCK_MONOTONIC, unrelated to wall-clock time.
+ * May be called from any thread.
+ */
+uint64_t yasl_private_loop_now_ns(void);
+/**
+ * Get the loop used for a display's file watches and application timers.
+ * @param d Display, or NULL.
+ * @return Borrowed loop, or NULL. Do not destroy a display-owned loop yourself.
+ */
+struct yasl_private_loop *yasl_private_display_loop(struct yasl_private_display *d);
+/**
+ * Register a borrowed file descriptor for poll-style readiness.
+ * @param l Loop.
+ * @param fd Open file descriptor; it remains owned by the caller.
+ * @param events Bitmask from <poll.h>, for example POLLIN or POLLOUT.
+ * @param fn Callback receiving poll revents (including errors/hangup); return value is ignored.
+ * @param ud Borrowed userdata until watch removal or loop destruction.
+ * @return Positive watch ID, or -1 on failure.
+ * Delete the watch before closing fd. The callback must consume readiness or
+ * change/remove its watch to avoid repeated immediate wakeups.
+ */
+int yasl_private_loop_add_fd(struct yasl_private_loop *l, int fd, short events,
+ int (*fn)(int fd, short revents, void *ud), void *ud);
+/**
+ * Remove a file watch without closing its descriptor.
+ * @param l Loop.
+ * @param id Watch ID returned by yasl_private_loop_add_fd().
+ * @return Zero if removed, or -1 for an invalid/missing watch.
+ */
+int yasl_private_loop_del_fd(struct yasl_private_loop *l, int id);
+/**
+ * Replace a watch's poll event mask.
+ * @param l Loop.
+ * @param id Watch ID.
+ * @param events New <poll.h> mask; zero disables requested readiness (errors may still arrive).
+ * @return Zero if updated, or -1 for an invalid/missing watch.
+ */
+int yasl_private_loop_set_fd_events(struct yasl_private_loop *l, int id, short events);
+
+/* Timers use nanoseconds. Return the next interval to repeat/reschedule, or zero
+ * to remove the timer. A zero ID indicates failure. Loop timers live until they
+ * stop, are deleted, or the loop is destroyed. Surface timers are also canceled
+ * when their surface is destroyed. */
+/** Timer identity scoped to its loop. Zero is invalid; do not reuse a retired ID. */
+typedef uint32_t yasl_private_timer_id;
+/** Timer callback. interval_ns is the last scheduled delay; return the next delay or zero to stop. */
+typedef uint64_t (*yasl_private_timer_cb)(void *ud, yasl_private_timer_id id, uint64_t interval_ns);
+
+/** Convert a nonnegative integer millisecond value to nanoseconds; avoid overflow. */
+#define YASL_PRIVATE_MS_TO_NS(ms) ((uint64_t)(ms) * 1000000ull)
+
+/**
+ * Schedule a timer on a loop.
+ * @param l Loop.
+ * @param interval_ns Nonzero delay in monotonic nanoseconds before first firing.
+ * @param cb Required callback; returns a new nonzero interval to repeat, or zero to stop.
+ * @param ud Borrowed userdata until the timer stops, is deleted, or the loop is destroyed.
+ * @return Nonzero timer ID, or zero for invalid arguments or allocation failure.
+ * Repeating timers coalesce missed deadlines; do not use them to count elapsed
+ * wall time. The callback receives the last scheduled interval, not elapsed time.
+ */
+yasl_private_timer_id yasl_private_loop_add_timer(struct yasl_private_loop *l, uint64_t interval_ns,
+ yasl_private_timer_cb cb, void *ud);
+/**
+ * Cancel a loop timer, including from its own callback.
+ * @param l Loop.
+ * @param id Timer ID.
+ * @return true if an armed timer was removed, false if invalid or already retired.
+ */
+bool yasl_private_loop_del_timer(struct yasl_private_loop *l, yasl_private_timer_id id);
+/**
+ * Schedule a timer canceled automatically on surface destruction.
+ * @param s Surface whose display loop will run the timer.
+ * @param interval_ns Nonzero initial delay in nanoseconds.
+ * @param cb Required timer callback, with the same rules as yasl_private_loop_add_timer().
+ * @param ud Borrowed userdata until the timer ends or the surface is destroyed.
+ * @return Nonzero timer ID, or zero on failure.
+ * The surface does not own userdata. Cancel with yasl_private_surface_del_timer().
+ */
+yasl_private_timer_id yasl_private_surface_add_timer(struct yasl_surface *s, uint64_t interval_ns,
+ yasl_private_timer_cb cb, void *ud);
+/**
+ * Cancel a timer registered through yasl_private_surface_add_timer().
+ * @param s Owning surface.
+ * @param id Timer ID.
+ * @return true if still armed and removed; false if unknown or already retired.
+ */
+bool yasl_private_surface_del_timer(struct yasl_surface *s, yasl_private_timer_id id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* YASL_PRIVATE_LOOP_H */
diff --git a/src/common/private_native.h b/src/common/private_native.h
new file mode 100644
index 0000000..d3d9aae
--- /dev/null
+++ b/src/common/private_native.h
@@ -0,0 +1,90 @@
+/**
+ * @file
+ * Optional native handles and application-managed rendering.
+ *
+ * These extensions follow the core thread and lifetime rules. Handles are borrowed;
+ * never destroy them or dispatch their native connection independently. Typed
+ * backend accessors live in the yasl/native headers and are installed only for built
+ * backends. Application-managed drawing currently supports Cocoa/Metal only.
+ */
+#ifndef YASL_PRIVATE_NATIVE_H
+#define YASL_PRIVATE_NATIVE_H
+
+#include "common/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Optional platform interoperability. All handles are borrowed and expire on
+ * object destruction. Check yasl_private_display_backend() before casting:
+ *
+ * display surface
+ * Wayland struct wl_display * struct wl_surface *
+ * X11 Display * Window (cast through uintptr_t)
+ * Cocoa NSApplication * NSWindow * (view: window.contentView)
+ */
+/**
+ * Get a borrowed, backend-specific display handle.
+ * @param d Display, or NULL.
+ * @return Native handle, or NULL. Inspect yasl_private_display_backend() before casting.
+ * Prefer a typed accessor from yasl/native/wayland.h, x11.h, or cocoa.h.
+ * The handle expires on display close; do not close or dispatch it independently.
+ */
+void *yasl_private_display_get_native_handle(struct yasl_private_display *d);
+/**
+ * Get a borrowed, backend-specific surface handle.
+ * @param s Surface, or NULL.
+ * @return Native handle, or NULL. X11 encodes a Window through uintptr_t.
+ * Prefer typed accessors. The handle expires on surface destruction. Do not
+ * change native ownership, event masks, buffer attachment, or surface roles.
+ */
+void *yasl_private_surface_get_native_handle(struct yasl_surface *s);
+
+/* Create a surface for application-managed native rendering. graphics_device
+ * must be non-NULL; currently an id<MTLDevice> on Cocoa, retained by the surface.
+ * Other backends return NULL with errno=ENOTSUP. CPU rendering uses the ordinary
+ * yasl_private_surface_create_toplevel() and requires no native device. */
+/**
+ * Create a toplevel for application-managed native rendering.
+ * @param d Display with YASL_PRIVATE_CAP_NATIVE_GRAPHICS.
+ * @param spec Copied window options, with logical-pixel size and optional callbacks.
+ * @param graphics_device Required native graphics device; currently id<MTLDevice> on Cocoa.
+ * @return Owned surface, or NULL with errno set: EINVAL for invalid arguments,
+ * ENOTSUP for an unsupported backend, or a backend error.
+ * Cocoa retains the device. Query the drawable during draw, submit/present it,
+ * then call yasl_private_surface_present() to record submission.
+ * Callback ownership and timing follow yasl_private_surface_create_toplevel().
+ */
+struct yasl_surface *yasl_private_surface_create_native(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *graphics_device);
+
+/** Borrowed native drawable for the current draw target, currently Cocoa/Metal. */
+struct yasl_private_native_frame {
+ /** Borrowed id<CAMetalDrawable> on Cocoa. */
+ void *drawable;
+ /** Cocoa CAMetalLayer presentsWithTransaction setting for this frame. */
+ bool transaction;
+};
+
+/* Query the current draw target's native drawable. Returns false for unsupported
+ * backends, stale targets, or outside the draw callback. The result expires on
+ * successful present, callback return, or surface destruction. Native renderers
+ * submit/present the drawable, then call yasl_private_surface_present() to record it. */
+/**
+ * Get the current draw target's native drawable.
+ * @param s Surface currently being drawn.
+ * @param target Exact framebuffer identity supplied to this draw callback.
+ * @param out Required output native frame.
+ * @return true if available, false for NULL arguments, unsupported backends,
+ * stale targets, or calls outside draw. The drawable is borrowed and expires on
+ * successful present, callback return, or surface destruction. Currently Cocoa only.
+ */
+bool yasl_private_surface_get_native_frame(struct yasl_surface *s,
+ const struct yasl_private_framebuffer *target, struct yasl_private_native_frame *out);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* YASL_PRIVATE_NATIVE_H */
diff --git a/src/common/private_native_cocoa.h b/src/common/private_native_cocoa.h
new file mode 100644
index 0000000..2eecd6e
--- /dev/null
+++ b/src/common/private_native_cocoa.h
@@ -0,0 +1,55 @@
+/**
+ * @file
+ * Typed borrowed Cocoa handles; available on macOS. Use on the main thread.
+ */
+#ifndef YASL_PRIVATE_NATIVE_COCOA_H
+#define YASL_PRIVATE_NATIVE_COCOA_H
+
+#include "common/types.h"
+
+#ifdef __OBJC__
+@class NSApplication;
+@class NSWindow;
+@class NSView;
+#else
+typedef struct objc_object NSApplication; /**< Opaque Cocoa application in plain C/C++. */
+typedef struct objc_object NSWindow; /**< Opaque Cocoa window in plain C/C++. */
+typedef struct objc_object NSView; /**< Opaque Cocoa view in plain C/C++. */
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Return borrowed Cocoa handles (expire on display/surface destruction).
+ * Return NULL / nil if display/surface is not a Cocoa object or NULL. */
+/**
+ * Get the borrowed NSApplication for an object of the matching backend.
+ * @param d Live display, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on display destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+NSApplication *yasl_private_cocoa_display_get_application(struct yasl_private_display *d);
+/**
+ * Get the borrowed NSWindow for an object of the matching backend.
+ * @param s Live surface, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on surface destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+NSWindow *yasl_private_cocoa_surface_get_window(struct yasl_surface *s);
+/**
+ * Get the borrowed NSView for an object of the matching backend.
+ * @param s Live surface, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on surface destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+NSView *yasl_private_cocoa_surface_get_view(struct yasl_surface *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* YASL_PRIVATE_NATIVE_COCOA_H */
diff --git a/src/common/private_native_wayland.h b/src/common/private_native_wayland.h
new file mode 100644
index 0000000..5033f7f
--- /dev/null
+++ b/src/common/private_native_wayland.h
@@ -0,0 +1,40 @@
+/**
+ * @file
+ * Typed borrowed Wayland handles; available when built with Wayland support.
+ */
+#ifndef YASL_PRIVATE_NATIVE_WAYLAND_H
+#define YASL_PRIVATE_NATIVE_WAYLAND_H
+
+#include "common/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct wl_display;
+struct wl_surface;
+
+/* Return borrowed Wayland handles (expire on display/surface destruction).
+ * Return NULL if display/surface is not a Wayland object or NULL. */
+/**
+ * Get the borrowed Wayland connection for an object of the matching backend.
+ * @param d Live display, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on display destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+struct wl_display *yasl_private_wayland_display_get_wl_display(struct yasl_private_display *d);
+/**
+ * Get the borrowed Wayland surface for an object of the matching backend.
+ * @param s Live surface, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on surface destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+struct wl_surface *yasl_private_wayland_surface_get_wl_surface(struct yasl_surface *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* YASL_PRIVATE_NATIVE_WAYLAND_H */
diff --git a/src/common/private_native_x11.h b/src/common/private_native_x11.h
new file mode 100644
index 0000000..d1930bd
--- /dev/null
+++ b/src/common/private_native_x11.h
@@ -0,0 +1,40 @@
+/**
+ * @file
+ * Typed borrowed X11 handles; available when built with X11 support.
+ */
+#ifndef YASL_PRIVATE_NATIVE_X11_H
+#define YASL_PRIVATE_NATIVE_X11_H
+
+#include "common/types.h"
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _XDisplay Display; /**< Opaque Xlib connection, compatible with Xlib.h. */
+
+/* Return borrowed X11 handles (expire on display/surface destruction).
+ * Return NULL / 0 if display/surface is not an X11 object or NULL. */
+/**
+ * Get the borrowed X11 Display for an object of the matching backend.
+ * @param d Live display, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on display destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+Display *yasl_private_x11_display_get_display(struct yasl_private_display *d);
+/**
+ * Get the borrowed X11 Window ID for an object of the matching backend.
+ * @param s Live surface, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on surface destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+uintptr_t yasl_private_x11_surface_get_window(struct yasl_surface *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* YASL_PRIVATE_NATIVE_X11_H */
diff --git a/src/common/private_timing.h b/src/common/private_timing.h
new file mode 100644
index 0000000..308765c
--- /dev/null
+++ b/src/common/private_timing.h
@@ -0,0 +1,56 @@
+/**
+ * @file
+ * Timing provenance, refresh estimates, and buffer-resize statistics.
+ * All timestamps are monotonic nanoseconds, not wall-clock dates.
+ */
+#ifndef YASL_PRIVATE_TIMING_H
+#define YASL_PRIVATE_TIMING_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Cumulative backend buffer-resize measurements; unavailable counters are zero. */
+struct yasl_private_resize_stats {
+ /** Number of measured buffer resizes. */
+ uint64_t count;
+ /** Mean measured resize duration in microseconds. */
+ double avg_us;
+ /** Maximum measured resize duration in microseconds. */
+ double max_us;
+ /** Backend minor page-fault counter, if collected. */
+ long minflt;
+};
+
+/* Describes last_present_ns, not merely availability of an extension. */
+/** Provenance of last_present_ns; only PRESENTATION confirms compositor presentation. */
+enum yasl_private_timing_source {
+ YASL_PRIVATE_TIMING_UNKNOWN = 0, /**< No timing sample yet. */
+ YASL_PRIVATE_TIMING_SUBMISSION, /**< CPU submission time, not scanout confirmation. */
+ YASL_PRIVATE_TIMING_FRAME_CALLBACK, /**< Compositor pacing callback, not scanout confirmation. */
+ YASL_PRIVATE_TIMING_PRESENTATION, /**< Compositor-confirmed presentation. */
+};
+
+/** Snapshot of estimates and timing provenance. Zero timestamps mean no sample yet. */
+struct yasl_private_timing {
+ /** Estimated refresh interval in nanoseconds; zero if unavailable. */
+ double refresh_ns;
+ /** Last timestamp, whose meaning is specified by source. */
+ uint64_t last_present_ns;
+ /** Estimated next presentation time, or zero without a prior sample. */
+ uint64_t next_present_ns;
+ /** Backend skipped-frame counter; not comparable across backends. */
+ uint64_t frames_skipped;
+ /** Provenance of last_present_ns. */
+ enum yasl_private_timing_source source;
+ /** Cumulative backend resize statistics. */
+ struct yasl_private_resize_stats resize;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* YASL_PRIVATE_TIMING_H */
diff --git a/src/common/public_internal.h b/src/common/public_internal.h
new file mode 100644
index 0000000..210b222
--- /dev/null
+++ b/src/common/public_internal.h
@@ -0,0 +1,12 @@
+#ifndef YASL_PUBLIC_INTERNAL_H
+#define YASL_PUBLIC_INTERNAL_H
+#include "yasl/yasl.h"
+#include "common/backend.h"
+struct yasl_private_display *yasl_public_display(void);
+bool yasl_public_owner(void);
+bool yasl_public_surface(yasl_surface *, bool mutation);
+bool yasl_public_fail(int error);
+void yasl_public_set_error(enum yasl_error, int native_error);
+void yasl_public_register_surface(yasl_surface *, unsigned flags, yasl_surface *parent);
+void yasl_private_surface_notify_refresh(struct yasl_surface *);
+#endif
diff --git a/src/common/surface.c b/src/common/surface.c
new file mode 100644
index 0000000..897b95a
--- /dev/null
+++ b/src/common/surface.c
@@ -0,0 +1,522 @@
+#define _POSIX_C_SOURCE 200809L
+#include "public_internal.h"
+#include "yasl/cpu.h"
+#include "yasl/gl.h"
+#include "common/utf8.h"
+#include <errno.h>
+#include <limits.h>
+#include <math.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct yasl_private_display *display;
+static enum yasl_backend requested_backend;
+static char *application_id;
+static pthread_t owner;
+static pthread_mutex_t lifecycle_mutex = PTHREAD_MUTEX_INITIALIZER;
+static bool pumping;
+static unsigned refresh_depth;
+static char *delivered_text; /* Strings of the last returned event; freed by the next poll/wait. */
+static _Thread_local enum yasl_error last_error;
+static _Thread_local int native_error;
+
+void yasl_public_set_error(enum yasl_error error, int native)
+{ last_error=error; native_error=native; }
+bool yasl_public_fail(int error)
+{
+ if (!error) error=EIO;
+ enum yasl_error code=YASL_ERROR_IO;
+ if (error==EINVAL || error==EOVERFLOW || error==EILSEQ) code=YASL_ERROR_INVALID;
+ else if (error==ENOTSUP || error==ENOSYS) code=YASL_ERROR_UNSUPPORTED;
+ else if (error==ENOMEM) code=YASL_ERROR_NO_MEMORY;
+ else if (error==EBUSY) code=YASL_ERROR_BUSY;
+ else if (error==EPERM) code=YASL_ERROR_WRONG_THREAD;
+ yasl_public_set_error(code,error); errno=error; return false;
+}
+enum yasl_error yasl_get_error(const char **description)
+{
+ static const char *const messages[]={"No error","Invalid argument","Unsupported operation",
+ "Out of memory","Library not initialized","Operation is busy","Wrong thread",
+ "Input context expired","Native operation failed"};
+ if(description) *description=messages[last_error];
+ return last_error;
+}
+int yasl_get_native_error(void) { return native_error; }
+struct yasl_private_display *yasl_public_display(void) { return display; }
+bool yasl_public_owner(void)
+{
+ if(!display) { yasl_public_set_error(YASL_ERROR_NOT_INITIALIZED,0); return false; }
+ if(!pthread_equal(owner,pthread_self())) return yasl_public_fail(EPERM);
+ return true;
+}
+bool yasl_public_surface(yasl_surface *s,bool mutation)
+{
+ if(!yasl_public_owner()) return false;
+ if(!s || s->display!=display) return yasl_public_fail(EINVAL);
+ if(mutation && refresh_depth) return yasl_public_fail(EBUSY);
+ return true;
+}
+static bool initialize(void)
+{
+ if(display) return yasl_public_owner();
+ struct yasl_private_display_spec spec={.app_id=application_id,.backend=(enum yasl_private_backend)requested_backend};
+ struct yasl_private_display *d=yasl_private_display_open(NULL,&spec);
+ if(!d) return yasl_public_fail(errno);
+ pthread_mutex_lock(&lifecycle_mutex);
+ owner=pthread_self(); display=d;
+ pthread_mutex_unlock(&lifecycle_mutex);
+ return true;
+}
+bool yasl_set_app_id(const char *id)
+{
+ if(display) return yasl_public_fail(EBUSY);
+ char *copy=id ? strdup(id) : NULL;
+ if(id&&!copy) return yasl_public_fail(ENOMEM);
+ free(application_id); application_id=copy; return true;
+}
+bool yasl_set_backend(enum yasl_backend backend)
+{
+ if(display) return yasl_public_fail(EBUSY);
+ if(backend<YASL_BACKEND_AUTO || backend>YASL_BACKEND_COCOA) return yasl_public_fail(EINVAL);
+ requested_backend=backend; return true;
+}
+static bool creation_valid(int width,int height,unsigned flags)
+{
+ if(width<=0 || height<=0 || width>32767 || height>32767 ||
+ (unsigned long long)width*(unsigned)height>INT32_MAX/4 || (flags & ~15u))
+ return yasl_public_fail(EINVAL);
+ if(pumping || refresh_depth) return yasl_public_fail(EBUSY);
+ return true;
+}
+static void draw_committed(yasl_surface *s,struct yasl_private_frame *frame,void *ud)
+{
+ (void)ud;
+ if(s->refresh_callback && pumping && !refresh_depth) {
+ ++refresh_depth; s->refresh_callback(s,s->refresh_userdata); --refresh_depth;
+ }
+ if(s->presenter!=YASL_PRIVATE_PRESENTER_CPU || !frame->target.pixels) return;
+ int w=frame->target.size.x,h=frame->target.size.y;
+ if(w==s->committed_width && h==s->committed_height && s->committed_pixels)
+ memcpy(frame->target.pixels,s->committed_pixels,(size_t)w*h*4);
+ else memset(frame->target.pixels,0,(size_t)w*h*4);
+ struct yasl_private_damage_list damage={.n=-1};
+ yasl_private_surface_present(s,&frame->target,&damage);
+}
+void yasl_public_register_surface(yasl_surface *s,unsigned flags,yasl_surface *parent)
+{
+ s->creation_flags=flags; s->parent=parent;
+ if(flags&YASL_SURFACE_CPU) s->presenter=YASL_PRIVATE_PRESENTER_CPU;
+ struct yasl_private_window_cbs cb={.draw=draw_committed};
+ yasl_private_surface_set_callbacks(s,&cb,NULL);
+ s->reported_state=s->state;
+ struct yasl_private_geometry geometry=yasl_private_surface_geometry(s);
+ yasl_private_surface_notify_configure(s,geometry);
+}
+yasl_surface *yasl_surface_create(const char *title,int width,int height,unsigned flags)
+{
+ if(!creation_valid(width,height,flags)) return NULL;
+ bool fresh=!display;
+ if(!initialize()) return NULL;
+ struct yasl_private_window_spec spec={.title=title,.size_logical={width,height},
+ .flags=flags,.external_gpu=!(flags&YASL_SURFACE_CPU)};
+ yasl_surface *s=yasl_private_surface_create_toplevel(display,&spec);
+ if(!s) {
+ int error=errno;
+ if(fresh) { pthread_mutex_lock(&lifecycle_mutex); struct yasl_private_display *d=display; display=NULL;
+ pthread_mutex_unlock(&lifecycle_mutex); yasl_private_display_close(d); }
+ yasl_public_fail(error); return NULL;
+ }
+ yasl_public_register_surface(s,flags,NULL); return s;
+}
+void yasl_surface_destroy(yasl_surface *s)
+{
+ if(!s) return;
+ if(!yasl_public_surface(s,true)) return;
+ for(;;) {
+ yasl_surface *child=NULL;
+ for(yasl_surface *p=display->surfaces;p;p=p->event_next) if(p->parent==s) { child=p; break; }
+ if(!child) break;
+ yasl_surface_destroy(child);
+ }
+ free(s->committed_pixels); s->committed_pixels=NULL;
+ free(s->text_surrounding); s->text_surrounding=NULL;
+ yasl_private_surface_destroy(s);
+}
+void yasl_shutdown(void)
+{
+ if(display && !yasl_public_owner()) return;
+ if(pumping || refresh_depth) { yasl_public_fail(EBUSY); return; }
+ if(display) {
+ while(display->surfaces) yasl_surface_destroy(display->surfaces);
+ pthread_mutex_lock(&lifecycle_mutex); struct yasl_private_display *d=display; display=NULL;
+ pthread_mutex_unlock(&lifecycle_mutex); yasl_private_display_close(d);
+ }
+ free(delivered_text); delivered_text=NULL;
+ free(application_id); application_id=NULL; requested_backend=YASL_BACKEND_AUTO;
+#if YASL_HAS_VULKAN
+ extern void yasl_private_vulkan_reset(void);
+ yasl_private_vulkan_reset();
+#endif
+}
+void yasl_wake(void)
+{
+ pthread_mutex_lock(&lifecycle_mutex);
+ if(display && display->loop) yasl_private_loop_kick(display->loop);
+ pthread_mutex_unlock(&lifecycle_mutex);
+}
+uint64_t yasl_time_ns(void) { return yasl_private_loop_now_ns(); }
+static double scale_of(const yasl_surface *s)
+{ return s && s->scale_120 ? (double)s->scale_120/120.0 : 1.0; }
+static int logical(int pixels,double scale) { return (int)lround(pixels/scale); }
+enum yasl_backend yasl_surface_backend(const yasl_surface *s)
+{ return s ? (enum yasl_backend)s->display->ops->type : YASL_BACKEND_AUTO; }
+unsigned yasl_surface_features(const yasl_surface *s)
+{
+ if(!s) return 0;
+ unsigned caps=yasl_private_display_caps(s->display);
+ if(s->display->ops->surface_create_gl) caps|=YASL_CAP_OPENGL;
+#if YASL_HAS_VULKAN
+ caps|=YASL_CAP_VULKAN;
+#endif
+ return caps;
+}
+void yasl_surface_get_framebuffer_size(const yasl_surface *s,int *w,int *h)
+{ if(w) *w=s?s->size.x:0; if(h) *h=s?s->size.y:0; }
+void yasl_surface_get_size(const yasl_surface *s,int *w,int *h)
+{ double scale=scale_of(s); if(w) *w=s?logical(s->size.x,scale):0; if(h) *h=s?logical(s->size.y,scale):0; }
+double yasl_surface_get_scale(const yasl_surface *s) { return scale_of(s); }
+unsigned yasl_surface_state(const yasl_surface *s) { return s?s->state:0; }
+void *yasl_surface_userdata(const yasl_surface *s) { return s?s->userdata:NULL; }
+void yasl_surface_set_userdata(yasl_surface *s,void *ud) { if(yasl_public_surface(s,false)) s->userdata=ud; }
+bool yasl_surface_set_title(yasl_surface *s,const char *title)
+{
+ if(!yasl_public_surface(s,false)) return false;
+ if(!title) return yasl_public_fail(EINVAL);
+ if(!s->display->ops->surface_set_title) return yasl_public_fail(ENOTSUP);
+ s->display->ops->surface_set_title(s,title); return true;
+}
+#define REQUEST(name,priv,args,call) bool name args { if(!yasl_public_surface(s,false)) return false; errno=0; return priv call || yasl_public_fail(errno); }
+REQUEST(yasl_surface_request_size,yasl_private_surface_set_size,(yasl_surface *s,int w,int h),(s,((struct yasl_private_vector2i){w,h})))
+REQUEST(yasl_surface_request_fullscreen,yasl_private_surface_set_fullscreen,(yasl_surface *s,bool v),(s,v))
+REQUEST(yasl_surface_request_maximized,yasl_private_surface_set_maximized,(yasl_surface *s,bool v),(s,v))
+REQUEST(yasl_surface_request_minimize,yasl_private_surface_minimize,(yasl_surface *s),(s))
+REQUEST(yasl_surface_set_pointer_mode,yasl_private_surface_set_pointer_mode,(yasl_surface *s,enum yasl_pointer_mode m),(s,(enum yasl_private_pointer_mode)m))
+bool yasl_surface_set_cursor(yasl_surface *s,enum yasl_cursor cursor)
+{
+ if(!yasl_public_surface(s,false)) return false;
+ if(cursor<YASL_CURSOR_ARROW || cursor>YASL_CURSOR_HIDDEN) return yasl_public_fail(EINVAL);
+ if(!s->display->ops->surface_set_cursor) return yasl_public_fail(ENOTSUP);
+ yasl_private_surface_set_cursor(s,(enum yasl_private_cursor)cursor); return true;
+}
+void yasl_surface_set_refresh_callback(yasl_surface *s,void(*fn)(yasl_surface *,void *),void *ud)
+{ if(yasl_public_surface(s,false)) { s->refresh_callback=fn; s->refresh_userdata=ud; } }
+void yasl_private_surface_notify_refresh(yasl_surface *s)
+{
+ if(!s || !s->display) return;
+ struct yasl_private_event ev={.type=YASL_PRIVATE_EVENT_REFRESH,.surface=s};
+ yasl_private_events_push(s->display,s,&ev);
+ if(s->refresh_callback && pumping && !refresh_depth) {
+ ++refresh_depth; s->refresh_callback(s,s->refresh_userdata); --refresh_depth;
+ }
+}
+static char *copy_text(const char *data,size_t length)
+{
+ if(length==SIZE_MAX || (!data&&length)) { errno=EINVAL; return NULL; }
+ char *p=malloc(length+1); if(!p) return NULL;
+ if(length) memcpy(p,data,length);
+ p[length]=0; return p;
+}
+static const char *deliver_text(const char *data,size_t length)
+{
+ free(delivered_text); delivered_text=copy_text(data,length); return delivered_text;
+}
+/* Folds native size, state and focus changes into one WINDOW snapshot; unchanged values report nothing. */
+static bool window_event(yasl_surface *s,struct yasl_private_vector2i size,uint32_t scale_120,uint32_t state,yasl_event *e)
+{
+ bool resized=size.x!=s->reported_size.x || size.y!=s->reported_size.y || scale_120!=s->reported_scale_120;
+ if(!resized && state==s->reported_state) return false;
+ s->reported_size=size; s->reported_scale_120=scale_120; s->reported_state=state;
+ if(resized) s->refresh_owed=true;
+ double scale=scale_120 ? scale_120/120.0 : 1.0;
+ e->type=YASL_EVENT_WINDOW; e->window.pixel_width=size.x; e->window.pixel_height=size.y; e->window.scale=scale;
+ e->window.width=logical(size.x,scale); e->window.height=logical(size.y,scale);
+ e->window.state=state;
+ return true;
+}
+static bool refresh_ready(yasl_surface *s) { return s->reported_size.x>0 && s->reported_size.y>0; }
+static bool convert_event(const struct yasl_private_event *p,yasl_event *e)
+{
+ *e=(yasl_event){.surface=p->surface,.time_ns=p->time_ns};
+ double scale=scale_of(p->surface);
+ switch(p->type) {
+ case YASL_PRIVATE_EVENT_WINDOW_CLOSE:e->type=YASL_EVENT_CLOSE;break;
+ case YASL_PRIVATE_EVENT_DISPLAY_QUIT:e->type=YASL_EVENT_QUIT;break;
+ case YASL_PRIVATE_EVENT_DISPLAY_REOPEN:e->type=YASL_EVENT_REOPEN;break;
+ case YASL_PRIVATE_EVENT_DISPLAY_LOST:e->type=YASL_EVENT_DISPLAY_LOST;break;
+ case YASL_PRIVATE_EVENT_REFRESH:
+ if(!p->surface) return false;
+ if(!refresh_ready(p->surface)) { p->surface->refresh_owed=true; return false; }
+ p->surface->refresh_owed=false; e->type=YASL_EVENT_REFRESH; break;
+ case YASL_PRIVATE_EVENT_INPUT_RESET:e->type=YASL_EVENT_INPUT_RESET;break;
+ case YASL_PRIVATE_EVENT_FOCUS: {
+ yasl_surface *s=p->surface; if(!s) return false;
+ uint32_t state=p->focus.focused ? s->reported_state|YASL_STATE_ACTIVATED : s->reported_state&~(uint32_t)YASL_STATE_ACTIVATED;
+ return window_event(s,s->reported_size,s->reported_scale_120,state,e);
+ }
+ case YASL_PRIVATE_EVENT_WINDOW_STATE:
+ if(!p->surface) return false;
+ return window_event(p->surface,p->surface->reported_size,p->surface->reported_scale_120,p->state.flags,e);
+ case YASL_PRIVATE_EVENT_WINDOW_RESIZE:
+ if(!p->surface) return false;
+ return window_event(p->surface,p->resize.geometry.size,p->resize.geometry.scale_120,p->surface->reported_state,e);
+ case YASL_PRIVATE_EVENT_KEY:
+ e->type=YASL_EVENT_KEY;e->key.key=p->key.key;e->key.action=(enum yasl_key_action)p->key.action;
+ e->key.mods=p->key.mods;e->key.codepoint=p->key.codepoint;e->key.shifted=p->key.shifted;e->key.native_code=p->key.native_code;break;
+ case YASL_PRIVATE_EVENT_TEXT:
+ e->type=YASL_EVENT_TEXT;e->text.length=p->text.len;e->text.data=deliver_text(p->text.data,p->text.len);return e->text.data!=NULL;
+ case YASL_PRIVATE_EVENT_DROP:
+ e->type=YASL_EVENT_DROP;e->drop.action=YASL_DROP_DATA;e->drop.length=p->text.len;
+ e->drop.data=deliver_text(p->text.data,p->text.len);return e->drop.data!=NULL;
+ case YASL_PRIVATE_EVENT_PASTE:
+ e->type=YASL_EVENT_CLIPBOARD;e->clipboard.length=p->paste.len;e->clipboard.request_id=p->paste.request_id;
+ e->clipboard.status=(enum yasl_transfer_status)p->paste.status;
+ e->clipboard.data=deliver_text(p->paste.data,p->paste.len);return e->clipboard.data!=NULL;
+ case YASL_PRIVATE_EVENT_COMPOSITION:
+ e->type=YASL_EVENT_COMPOSITION;e->composition.length=p->composition.len;e->composition.begin=p->composition.begin;
+ e->composition.end=p->composition.end;e->composition.active=p->composition.active;
+ e->composition.revision=p->composition.revision; e->composition.generation=p->composition.generation;
+ e->composition.data=deliver_text(p->composition.data,p->composition.len);return e->composition.data!=NULL;
+ case YASL_PRIVATE_EVENT_TEXT_EDIT:
+ e->type=YASL_EVENT_TEXT_EDIT;e->text_edit.length=p->text_edit.len;e->text_edit.begin=p->text_edit.begin;e->text_edit.end=p->text_edit.end;
+ e->text_edit.revision=p->text_edit.revision;e->text_edit.generation=p->text_edit.generation;
+ e->text_edit.data=deliver_text(p->text_edit.data,p->text_edit.len);return e->text_edit.data!=NULL;
+ case YASL_PRIVATE_EVENT_PEN:
+ e->type=YASL_EVENT_PEN;
+#define PEN(field) e->pen.field=p->pen.field
+ PEN(device_id);PEN(tool_id);PEN(x);PEN(y);PEN(pressure);PEN(tilt_x);PEN(tilt_y);PEN(rotation);PEN(distance);PEN(tangential);PEN(wheel_degrees);
+ PEN(axes_valid);PEN(axes_changed);PEN(buttons);PEN(changed_button);PEN(tool);PEN(action);PEN(in_contact);
+#undef PEN
+ break;
+ case YASL_PRIVATE_EVENT_POINTER_SCROLL:
+ e->type=YASL_EVENT_SCROLL;e->scroll.x=p->pointer.pos.x/scale;e->scroll.y=p->pointer.pos.y/scale;
+ e->scroll.lines_x=p->pointer.scroll.x;e->scroll.lines_y=p->pointer.scroll.y;
+ e->scroll.pixels_x=p->pointer.pixel_scroll.x/scale;e->scroll.pixels_y=p->pointer.pixel_scroll.y/scale;
+ e->scroll.valid=p->pointer.scroll_valid?p->pointer.scroll_valid:YASL_SCROLL_LINES;
+ e->scroll.source=p->pointer.scroll_source;e->scroll.phase=p->pointer.scroll_phase;e->scroll.momentum=p->pointer.scroll_momentum;e->scroll.mods=p->pointer.mods;break;
+ case YASL_PRIVATE_EVENT_POINTER_DOWN:case YASL_PRIVATE_EVENT_POINTER_UP:case YASL_PRIVATE_EVENT_POINTER_MOTION:case YASL_PRIVATE_EVENT_POINTER_LEAVE:
+ e->type=YASL_EVENT_POINTER;e->pointer.x=(p->pointer.precise_position?p->pointer.precise_x:p->pointer.pos.x)/scale;
+ e->pointer.y=(p->pointer.precise_position?p->pointer.precise_y:p->pointer.pos.y)/scale;
+ e->pointer.dx=p->pointer.delta.x/scale;e->pointer.dy=p->pointer.delta.y/scale;
+ e->pointer.action=p->type==YASL_PRIVATE_EVENT_POINTER_DOWN?YASL_POINTER_DOWN:p->type==YASL_PRIVATE_EVENT_POINTER_UP?YASL_POINTER_UP:
+ p->type==YASL_PRIVATE_EVENT_POINTER_LEAVE?YASL_POINTER_LEAVE:YASL_POINTER_MOVE;
+ e->pointer.button=p->pointer.button;e->pointer.buttons=p->pointer.buttons;e->pointer.mods=p->pointer.mods;break;
+ case YASL_PRIVATE_EVENT_DRAG:
+ e->type=YASL_EVENT_DRAG;e->drag.action=(enum yasl_drag_action)p->drag.action;
+ e->drag.x=p->drag.pos.x/scale;e->drag.y=p->drag.pos.y/scale;break;
+ case YASL_PRIVATE_EVENT_DROP_HOVER:
+ e->type=YASL_EVENT_DROP;e->drop.action=p->drop.action==YASL_PRIVATE_DROP_LEAVE?YASL_DROP_LEAVE:YASL_DROP_OVER;
+ e->drop.x=p->drop.pos.x/scale;e->drop.y=p->drop.pos.y/scale;break;
+ default:return false;
+ }
+ return true;
+}
+static int take_event(yasl_event *out)
+{
+ for(yasl_surface *s=display->surfaces;s;s=s->event_next) if(s->refresh_owed && refresh_ready(s)) {
+ s->refresh_owed=false;
+ *out=(yasl_event){.type=YASL_EVENT_REFRESH,.surface=s,.time_ns=yasl_time_ns()};
+ return 1;
+ }
+ struct yasl_private_event p;
+ while(yasl_private_display_poll_event(display,&p)) {
+ if(p.surface && ((p.type==YASL_PRIVATE_EVENT_COMPOSITION && p.composition.generation!=p.surface->text_generation) ||
+ (p.type==YASL_PRIVATE_EVENT_TEXT_EDIT && p.text_edit.generation!=p.surface->text_generation))) continue;
+ yasl_event event;
+ errno=0;
+ if(convert_event(&p,&event)) { *out=event;return 1; }
+ if(errno) { yasl_public_fail(errno);return -1; }
+ }
+ return 0;
+}
+int yasl_wait_event(yasl_event *out,int timeout)
+{
+ if(!out) { yasl_public_fail(EINVAL);return -1; }
+ *out=(yasl_event){.type=YASL_EVENT_NONE};
+ if(!yasl_public_owner()) return -1;
+ if(pumping || refresh_depth) { yasl_public_fail(EBUSY);return -1; }
+ free(delivered_text); delivered_text=NULL;
+ int n=take_event(out); if(n) return n;
+ pumping=true;
+ yasl_private_display_flush(display);
+ n=take_event(out);
+ if(!n) {
+ int work=yasl_private_display_dispatch(display,timeout);
+ if(work<0) { yasl_public_fail(errno);n=-1; }
+ yasl_private_display_flush(display);
+ if(n>=0) n=take_event(out);
+ }
+ pumping=false;return n;
+}
+uint32_t *yasl_cpu_pixels(yasl_surface *s,int *w,int *h)
+{
+ if(w) *w=0;
+ if(h) *h=0;
+ if(!yasl_public_surface(s,false)) return NULL;
+ if(s->presenter!=YASL_PRIVATE_PRESENTER_CPU) { yasl_public_fail(ENOTSUP); return NULL; }
+ int width=s->size.x,height=s->size.y;
+ if(width<=0 || height<=0) { yasl_public_fail(EBUSY); return NULL; }
+ if(width!=s->committed_width || height!=s->committed_height || !s->committed_pixels) {
+ uint32_t *next=calloc((size_t)width*height,4);
+ if(!next) { yasl_public_fail(ENOMEM); return NULL; }
+ int keep_w=width<s->committed_width?width:s->committed_width,keep_h=height<s->committed_height?height:s->committed_height;
+ if(s->committed_pixels) for(int y=0;y<keep_h;y++)
+ memcpy(next+(size_t)y*width,s->committed_pixels+(size_t)y*s->committed_width,(size_t)keep_w*4);
+ free(s->committed_pixels); s->committed_pixels=next; s->committed_width=width; s->committed_height=height;
+ }
+ if(w) *w=width;
+ if(h) *h=height;
+ return s->committed_pixels;
+}
+bool yasl_cpu_present(yasl_surface *s)
+{
+ if(!yasl_public_surface(s,false)) return false;
+ if(s->presenter!=YASL_PRIVATE_PRESENTER_CPU) return yasl_public_fail(ENOTSUP);
+ if(!s->committed_pixels || s->committed_width!=s->size.x || s->committed_height!=s->size.y) return yasl_public_fail(EINVAL);
+ yasl_private_surface_request_frame(s); return true;
+}
+int yasl_double_click_ms(void) { return display?yasl_private_display_double_click_ms(display):400; }
+bool yasl_clipboard_set(enum yasl_selection sel,const char *text)
+{ if(!yasl_public_owner())return false;errno=0;return yasl_private_clipboard_set(display,(enum yasl_private_selection)sel,text)||yasl_public_fail(errno); }
+uint64_t yasl_clipboard_request(yasl_surface *s,enum yasl_selection sel)
+{ if(!yasl_public_surface(s,false))return 0;errno=0;uint64_t id=yasl_private_clipboard_request(s,(enum yasl_private_selection)sel);if(!id)yasl_public_fail(errno);return id; }
+static bool text_update(yasl_surface *s)
+{ if(!s->display->ops->surface_update_text_input)return true;errno=0;return s->display->ops->surface_update_text_input(s)||yasl_public_fail(errno); }
+bool yasl_surface_set_text_input(yasl_surface *s,bool active,enum yasl_text_purpose purpose)
+{
+ if(!yasl_public_surface(s,false))return false;
+ if(purpose<YASL_TEXT_NORMAL||purpose>YASL_TEXT_TERMINAL)return yasl_public_fail(EINVAL);
+ bool old=s->text_input_active;unsigned old_purpose=s->text_purpose;uint64_t generation=s->text_generation;
+ if(old!=active || old_purpose!=(unsigned)purpose)++s->text_generation;
+ s->text_input_active=active;s->text_purpose=purpose;
+ if(!text_update(s)) { s->text_input_active=old;s->text_purpose=old_purpose;s->text_generation=generation;return false; }return true;
+}
+bool yasl_surface_set_surrounding_text(yasl_surface *s,const char *text,size_t len,size_t begin,size_t end,uint64_t revision)
+{
+ if(!yasl_public_surface(s,false))return false;
+ if((!text&&len)||begin>end||end>len||len>INT32_MAX || (len&&memchr(text,0,len)))return yasl_public_fail(EINVAL);
+ /* Validate UTF-8 and require selection boundaries between code points. */
+ if((begin<len&&((unsigned char)text[begin]&0xc0)==0x80)||(end<len&&((unsigned char)text[end]&0xc0)==0x80))return yasl_public_fail(EINVAL);
+ for(size_t i=0;i<len;) { unsigned char c=(unsigned char)text[i];size_t n=c<0x80?1:c>=0xc2&&c<=0xdf?2:c>=0xe0&&c<=0xef?3:c>=0xf0&&c<=0xf4?4:0;
+ if(!n||i+n>len)return yasl_public_fail(EILSEQ);
+ for(size_t j=1;j<n;j++)if(((unsigned char)text[i+j]&0xc0)!=0x80)return yasl_public_fail(EILSEQ);
+ if(n>=3 && ((c==0xe0&&(unsigned char)text[i+1]<0xa0)||(c==0xed&&(unsigned char)text[i+1]>=0xa0)||
+ (c==0xf0&&(unsigned char)text[i+1]<0x90)||(c==0xf4&&(unsigned char)text[i+1]>=0x90)))return yasl_public_fail(EILSEQ);
+ i+=n;
+ }
+ char *copy=copy_text(text,len);if(!copy)return yasl_public_fail(errno);
+ char *old=s->text_surrounding;size_t old_len=s->text_length,old_begin=s->text_begin,old_end=s->text_end;uint64_t old_rev=s->text_revision;
+ s->text_surrounding=copy;s->text_length=len;s->text_begin=begin;s->text_end=end;s->text_revision=revision;
+ if(!text_update(s)){free(copy);s->text_surrounding=old;s->text_length=old_len;s->text_begin=old_begin;s->text_end=old_end;s->text_revision=old_rev;return false;}
+ free(old);return true;
+}
+static bool logical_rect(yasl_surface *s,double x,double y,double w,double h,struct yasl_private_rect2i *r)
+{
+ if(!isfinite(x)||!isfinite(y)||!isfinite(w)||!isfinite(h)||w<0||h<0)return yasl_public_fail(EINVAL);
+ r->x=yasl_private_scale_to_device(x,s->scale_120);r->y=yasl_private_scale_to_device(y,s->scale_120);
+ r->width=yasl_private_scale_to_device(w,s->scale_120);r->height=yasl_private_scale_to_device(h,s->scale_120);return true;
+}
+bool yasl_surface_set_ime_caret(yasl_surface *s,double x,double y,double w,double h)
+{
+ if(!yasl_public_surface(s,false))return false;
+ struct yasl_private_rect2i r;
+ if(!logical_rect(s,x,y,w,h,&r))return false;
+ if(!s->display->ops->surface_set_ime_caret)return yasl_public_fail(ENOTSUP);
+ yasl_private_surface_set_ime_caret(s,r);return true;
+}
+static bool trigger_valid(yasl_surface *s,const yasl_event *e)
+{
+ if(!e||e->surface!=s||!e->time_ns||yasl_time_ns()-e->time_ns>5000000000ull ||
+ !((e->type==YASL_EVENT_POINTER&&e->pointer.action==YASL_POINTER_DOWN)||(e->type==YASL_EVENT_PEN&&e->pen.action==YASL_PEN_DOWN))) {
+ yasl_public_set_error(YASL_ERROR_CONTEXT_EXPIRED,0);return false;
+ }return true;
+}
+static bool popup_spec(yasl_surface *s,int w,int h,double x,double y,double aw,double ah,enum yasl_popup_edge anchor,enum yasl_popup_edge gravity,unsigned constraints,struct yasl_private_popup_spec *out)
+{
+ if(w<=0||h<=0 || (unsigned)anchor>8 || (unsigned)gravity>8 || (constraints&~63u))return yasl_public_fail(EINVAL);
+ *out=(struct yasl_private_popup_spec){.size={yasl_private_scale_to_device(w,s->scale_120),yasl_private_scale_to_device(h,s->scale_120)},.anchor_edges=anchor,.gravity=gravity,.constraints=constraints,.positioner_explicit=true};
+ return logical_rect(s,x,y,aw,ah,&out->anchor);
+}
+yasl_surface *yasl_popup_create(yasl_surface *parent,int w,int h,double x,double y,double aw,double ah,enum yasl_popup_edge anchor,enum yasl_popup_edge gravity,unsigned constraints,const yasl_event *trigger)
+{
+ if(!yasl_public_surface(parent,true)||!trigger_valid(parent,trigger))return NULL;
+ struct yasl_private_popup_spec spec;if(!popup_spec(parent,w,h,x,y,aw,ah,anchor,gravity,constraints,&spec))return NULL;
+ yasl_surface *s=yasl_private_surface_create_popup(parent,&spec);if(!s){yasl_public_fail(errno);return NULL;}
+ yasl_public_register_surface(s,parent->creation_flags,parent);return s;
+}
+bool yasl_popup_reposition(yasl_surface *s,int w,int h,double x,double y,double aw,double ah,enum yasl_popup_edge anchor,enum yasl_popup_edge gravity,unsigned constraints)
+{
+ if(!yasl_public_surface(s,false))return false;
+ struct yasl_private_popup_spec spec;
+ if(!s->parent)return yasl_public_fail(EINVAL);
+ if(!popup_spec(s->parent,w,h,x,y,aw,ah,anchor,gravity,constraints,&spec))return false;
+ errno=0;return yasl_private_surface_popup_reposition(s,&spec)||yasl_public_fail(errno);
+}
+bool yasl_surface_start_interactive_move(yasl_surface *s,const yasl_event *e)
+{ if(!yasl_public_surface(s,false)||!trigger_valid(s,e))return false;errno=0;return yasl_private_surface_start_interactive_move(s)||yasl_public_fail(errno); }
+bool yasl_surface_start_interactive_resize(yasl_surface *s,enum yasl_resize_edge edge,const yasl_event *e)
+{ if(!yasl_public_surface(s,false)||!trigger_valid(s,e))return false;errno=0;return yasl_private_surface_start_interactive_resize(s,(enum yasl_private_resize_edge)edge)||yasl_public_fail(errno); }
+bool yasl_surface_show_window_menu(yasl_surface *s,double x,double y,const yasl_event *e)
+{ if(!yasl_public_surface(s,false)||!trigger_valid(s,e))return false;
+ struct yasl_private_rect2i r;if(!logical_rect(s,x,y,0,0,&r))return false;errno=0;return yasl_private_surface_show_window_menu(s,(struct yasl_private_vector2i){r.x,r.y})||yasl_public_fail(errno); }
+bool yasl_drag_start(yasl_surface *s,const yasl_event *e,const uint32_t *pixels,int w,int h,int hx,int hy)
+{
+ if(!yasl_public_surface(s,false)||!trigger_valid(s,e))return false;
+ struct yasl_private_drag_image image={.pixels=pixels,.size={w,h},.hotspot={hx,hy}};
+ errno=0;return yasl_private_display_drag_start(display,s,pixels?&image:NULL,NULL,NULL)||yasl_public_fail(errno);
+}
+void yasl_drag_cancel(void) { if(yasl_public_owner())yasl_private_display_drag_cancel(display); }
+bool yasl_surface_drag_carry(yasl_surface *s,double x,double y)
+{ if(!yasl_public_surface(s,false))return false;
+ struct yasl_private_rect2i r;if(!logical_rect(s,x,y,0,0,&r))return false;errno=0;return yasl_private_surface_drag_carry(s,(struct yasl_private_vector2i){r.x,r.y})||yasl_public_fail(errno); }
+bool yasl_surface_place_under_pointer(yasl_surface *s,double x,double y)
+{ if(!yasl_public_surface(s,false))return false;
+ struct yasl_private_rect2i r;if(!logical_rect(s,x,y,0,0,&r))return false;errno=0;return yasl_private_surface_place_under_pointer(s,(struct yasl_private_vector2i){r.x,r.y})||yasl_public_fail(errno); }
+size_t yasl_drop_next_path(const char **cursor,char *out,size_t cap)
+{ return yasl_private_drop_next_path(cursor,out,cap); }
+
+yasl_surface *yasl_gl_surface_create(const char *title,int w,int h,unsigned flags,const int *attrs,yasl_surface *share)
+{
+ if(!creation_valid(w,h,flags)||(flags&YASL_SURFACE_CPU)) { yasl_public_fail(EINVAL);return NULL; }
+ struct yasl_private_gl_options parsed;if(!yasl_private_gl_parse_options(attrs,&parsed)){yasl_public_fail(errno);return NULL;}
+ bool fresh=!display;if(!initialize())return NULL;
+ if(share && (share->display!=display || share->presenter!=YASL_PRIVATE_PRESENTER_GL)) {yasl_public_fail(EINVAL);return NULL;}
+ if(!display->ops->surface_create_gl) {if(fresh)yasl_shutdown();yasl_public_fail(ENOTSUP);return NULL;}
+ struct yasl_private_window_spec spec={.title=title,.size_logical={w,h},.flags=flags,.external_gpu=true};
+ yasl_surface *s=display->ops->surface_create_gl(display,&spec,attrs,share);
+ if(!s){int error=errno;if(fresh)yasl_shutdown();yasl_public_fail(error);return NULL;}
+ if(!yasl_private_events_register(s)){int error=errno;display->ops->surface_destroy(s);if(fresh)yasl_shutdown();yasl_public_fail(error);return NULL;}
+ s->presenter=YASL_PRIVATE_PRESENTER_GL;yasl_public_register_surface(s,flags,NULL);return s;
+}
+bool yasl_gl_make_current(yasl_surface *s)
+{
+ if(!display){yasl_public_set_error(YASL_ERROR_NOT_INITIALIZED,0);return false;}
+ if(s && (s->display!=display||s->presenter!=YASL_PRIVATE_PRESENTER_GL))return yasl_public_fail(EINVAL);
+ if(!display->ops->gl_make_current)return yasl_public_fail(ENOTSUP);
+ errno=0;return display->ops->gl_make_current(display,s)||yasl_public_fail(errno);
+}
+yasl_gl_proc yasl_gl_get_proc_address(const char *name)
+{
+ if(!display||!name||!display->ops->gl_get_proc_address){yasl_public_fail(ENOTSUP);return NULL;}
+ yasl_gl_proc p=display->ops->gl_get_proc_address(display,name);if(!p)yasl_public_fail(ENOENT);return p;
+}
+bool yasl_gl_swap_interval(yasl_surface *s,int interval)
+{
+ if(!s||s->presenter!=YASL_PRIVATE_PRESENTER_GL||!s->display->ops->gl_swap_interval)return yasl_public_fail(EINVAL);
+ errno=0;return s->display->ops->gl_swap_interval(s->display,s,interval)||yasl_public_fail(errno);
+}
+bool yasl_gl_swap_buffers(yasl_surface *s)
+{
+ if(!s||s->presenter!=YASL_PRIVATE_PRESENTER_GL||!s->display->ops->gl_swap_buffers)return yasl_public_fail(EINVAL);
+ errno=0;return s->display->ops->gl_swap_buffers(s->display,s)||yasl_public_fail(errno);
+}
diff --git a/src/common/timing.c b/src/common/timing.c
index 7a91327..69c25cf 100644
--- a/src/common/timing.c
+++ b/src/common/timing.c
@@ -1,6 +1,6 @@
#include "timing.h"
-uint64_t yasl_clock_extend_ms(uint32_t ms, uint64_t now_ns)
+uint64_t yasl_private_clock_extend_ms(uint32_t ms, uint64_t now_ns)
{
uint64_t now_ms = now_ns / 1000000;
int64_t ahead = (int32_t)(ms - (uint32_t)now_ms);
@@ -9,7 +9,7 @@ uint64_t yasl_clock_extend_ms(uint32_t ms, uint64_t now_ns)
return (uint64_t)((int64_t)now_ms + ahead) * 1000000ull;
}
-void yasl_clock_presented(struct yasl_frame_clock *c, uint64_t now, bool sample)
+void yasl_private_clock_presented(struct yasl_private_frame_clock *c, uint64_t now, bool sample)
{
if (c->last_present_ns) {
double dt = (double)(now - c->last_present_ns);
@@ -17,10 +17,10 @@ void yasl_clock_presented(struct yasl_frame_clock *c, uint64_t now, bool sample)
c->refresh_ns += 0.125 * (dt - c->refresh_ns);
}
c->last_present_ns = now;
- c->source = YASL_TIMING_SUBMISSION;
+ c->source = YASL_PRIVATE_TIMING_SUBMISSION;
}
-void yasl_clock_note_resize(struct yasl_frame_clock *c, uint64_t dt_ns)
+void yasl_private_clock_note_resize(struct yasl_private_frame_clock *c, uint64_t dt_ns)
{
c->resize_count++;
c->resize_sum_ns += dt_ns;
@@ -28,8 +28,8 @@ void yasl_clock_note_resize(struct yasl_frame_clock *c, uint64_t dt_ns)
c->resize_max_ns = dt_ns;
}
-void yasl_clock_resize_stats(const struct yasl_frame_clock *c,
- struct yasl_resize_stats *out)
+void yasl_private_clock_resize_stats(const struct yasl_private_frame_clock *c,
+ struct yasl_private_resize_stats *out)
{
out->count = c->resize_count;
out->avg_us = c->resize_count
@@ -39,13 +39,13 @@ void yasl_clock_resize_stats(const struct yasl_frame_clock *c,
out->minflt = c->resize_minflt;
}
-uint64_t yasl_clock_next_present_ns(const struct yasl_frame_clock *c)
+uint64_t yasl_private_clock_next_present_ns(const struct yasl_private_frame_clock *c)
{
uint64_t next = c->last_present_ns + (uint64_t)c->refresh_ns;
return (uint64_t)-(c->last_present_ns != 0) & next;
}
-uint64_t yasl_clock_frame_timestamp_ns(struct yasl_frame_clock *c, uint64_t now)
+uint64_t yasl_private_clock_frame_timestamp_ns(struct yasl_private_frame_clock *c, uint64_t now)
{
if (!c->last_present_ns || c->refresh_ns <= 0.0) {
c->frame_time_ns = now;
diff --git a/src/common/timing.h b/src/common/timing.h
index a3c3c88..a0c6ef1 100644
--- a/src/common/timing.h
+++ b/src/common/timing.h
@@ -1,12 +1,12 @@
-#ifndef YASL_COMMON_TIMING_H
-#define YASL_COMMON_TIMING_H
+#ifndef YASL_PRIVATE_COMMON_TIMING_H
+#define YASL_PRIVATE_COMMON_TIMING_H
-#include "yasl/timing.h"
+#include "common/private_timing.h"
#include <stdbool.h>
-#define YASL_REFRESH_DEFAULT_NS 16666666.0
+#define YASL_PRIVATE_REFRESH_DEFAULT_NS 16666666.0
-struct yasl_frame_clock {
+struct yasl_private_frame_clock {
uint64_t last_present_ns;
uint64_t resize_count;
uint64_t resize_sum_ns;
@@ -14,15 +14,15 @@ struct yasl_frame_clock {
long resize_minflt;
uint64_t frame_time_ns;
double refresh_ns;
- enum yasl_timing_source source;
+ enum yasl_private_timing_source source;
};
-uint64_t yasl_clock_extend_ms(uint32_t ms, uint64_t now_ns);
-void yasl_clock_presented(struct yasl_frame_clock *c, uint64_t now, bool sample);
-void yasl_clock_note_resize(struct yasl_frame_clock *c, uint64_t dt_ns);
-void yasl_clock_resize_stats(const struct yasl_frame_clock *c,
- struct yasl_resize_stats *out);
-uint64_t yasl_clock_next_present_ns(const struct yasl_frame_clock *c);
-uint64_t yasl_clock_frame_timestamp_ns(struct yasl_frame_clock *c, uint64_t now);
+uint64_t yasl_private_clock_extend_ms(uint32_t ms, uint64_t now_ns);
+void yasl_private_clock_presented(struct yasl_private_frame_clock *c, uint64_t now, bool sample);
+void yasl_private_clock_note_resize(struct yasl_private_frame_clock *c, uint64_t dt_ns);
+void yasl_private_clock_resize_stats(const struct yasl_private_frame_clock *c,
+ struct yasl_private_resize_stats *out);
+uint64_t yasl_private_clock_next_present_ns(const struct yasl_private_frame_clock *c);
+uint64_t yasl_private_clock_frame_timestamp_ns(struct yasl_private_frame_clock *c, uint64_t now);
-#endif /* YASL_COMMON_TIMING_H */
+#endif /* YASL_PRIVATE_COMMON_TIMING_H */
diff --git a/src/common/types.h b/src/common/types.h
new file mode 100644
index 0000000..91f8a47
--- /dev/null
+++ b/src/common/types.h
@@ -0,0 +1,1101 @@
+/**
+ * @file
+ * Portable desktop surfaces, CPU drawing, input, and display dispatch.
+ *
+ * All calls and callbacks use the display's opening thread (the main thread on
+ * Cocoa), unless explicitly documented otherwise. Userdata is borrowed; callback
+ * tables and creation specs are copied. Do not recursively dispatch/flush or
+ * destroy a display/loop while dispatch or a callback is active. Destroy child
+ * popups before parents, surfaces before displays, displays before borrowed loops.
+ * Surface destruction from its own callback is supported; stop using it immediately.
+ *
+ * Zero-initialize option and callback structs before setting fields. Pointers to
+ * destroyed objects are invalid, even for getters and destroy functions. NULL
+ * handling is documented per function; it does not make freed pointers safe.
+ * Only functions explicitly documenting errno guarantee a diagnostic there.
+ * Other boolean results report success/availability; void setters are best effort.
+ * New window requests, clipboard operations, frame presentation, and posting
+ * document errno on failure. Event strings belong to the display queue.
+ *
+ * Toplevel creation sizes use logical pixels. Geometry, framebuffers, pointer
+ * positions, damage, IME carets, popup specs, and drag offsets use device pixels.
+ * Coordinates have their origin at the upper left; x grows right and y grows down.
+ * Scale is an integer ratio with YASL_PRIVATE_SCALE_UNIT (120) representing 1x.
+ * See the application guide for the display-wide dispatch/poll/flush cycle
+ * and backend limits. Legacy surface polling is an input-only filtered adapter.
+ */
+#ifndef YASL_PRIVATE_H
+#define YASL_PRIVATE_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <limits.h>
+#include <float.h>
+#include <math.h>
+
+#include "common/private_timing.h"
+#include "yasl/yasl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct yasl_private_display;
+struct yasl_surface;
+struct yasl_private_loop;
+
+/* Geometry */
+/** A two-dimensional integer size, position, or offset; units depend on the caller. */
+struct yasl_private_vector2i {
+ /** Horizontal coordinate or width. */
+ int x;
+ /** Vertical coordinate or height. */
+ int y;
+};
+
+/** Two-dimensional fractional offset; units depend on the containing structure. */
+struct yasl_private_vector2d {
+ /** Horizontal component. */
+ double x;
+ /** Vertical component. */
+ double y;
+};
+
+/** Integer rectangle; origin is upper left and extents must be nonnegative. */
+struct yasl_private_rect2i {
+ /** Left coordinate. */
+ int x;
+ /** Top coordinate. */
+ int y;
+ /** Nonnegative horizontal extent. */
+ int width;
+ /** Nonnegative vertical extent. */
+ int height;
+};
+
+/** Scale numerator representing 1x. */
+#define YASL_PRIVATE_SCALE_UNIT 120
+/** Backend minimum logical dimension used when constraining toplevel sizes. */
+#define YASL_PRIVATE_MIN_LOGICAL_PX 64
+
+/* Nearest integer, ties away from zero. Zero scale returns zero; finite
+ * out-of-range results saturate. Non-finite logical coordinates return zero. */
+/** Convert a logical coordinate to device pixels, rounding ties away from zero.
+ * @param logical Logical coordinate; non-finite values return zero.
+ * @param scale_120 Scale numerator; zero returns zero.
+ * @return Rounded device coordinate, saturated to the range of int.
+ */
+static inline int yasl_private_scale_to_device(double logical, uint32_t scale_120)
+{
+ if (!scale_120 || !(logical >= -DBL_MAX && logical <= DBL_MAX)) return 0;
+ double px = (scale_120 == YASL_PRIVATE_SCALE_UNIT) ? logical : (logical * ((double)scale_120 / YASL_PRIVATE_SCALE_UNIT));
+ if (px >= INT_MAX) return INT_MAX;
+ if (px <= INT_MIN) return INT_MIN;
+ return (int)(px + copysign(0.5, px));
+}
+
+/** Convert a device coordinate to logical pixels, rounding ties away from zero.
+ * @param device Device coordinate.
+ * @param scale_120 Scale numerator; zero returns zero.
+ * @return Rounded logical coordinate, saturated to the range of int.
+ */
+static inline int yasl_private_scale_to_logical(int device, uint32_t scale_120)
+{
+ if (!scale_120) return 0;
+ if (scale_120 == YASL_PRIVATE_SCALE_UNIT) return device;
+ int64_t px = (int64_t)device * YASL_PRIVATE_SCALE_UNIT;
+ int64_t half = (int64_t)(scale_120 / 2);
+ int64_t bias = (px < 0) ? -half : half;
+ px = (px + bias) / scale_120;
+ if (px > INT_MAX) return INT_MAX;
+ if (px < INT_MIN) return INT_MIN;
+ return (int)px;
+}
+
+/* Capabilities */
+/** Optional backend/server features, returned as a bitmask by yasl_private_display_caps(). */
+enum yasl_private_display_cap : uint32_t {
+ YASL_PRIVATE_CAP_POPUPS = 1 << 0, /**< Popup creation and placement. */
+ YASL_PRIVATE_CAP_DRAG = 1 << 1, /**< Surface-drag source and target support. */
+ YASL_PRIVATE_CAP_DRAG_CARRY = 1 << 2, /**< Carry a toplevel during a drag. */
+ YASL_PRIVATE_CAP_PLACE_WINDOW = 1 << 3, /**< Place a window relative to the pointer. */
+ YASL_PRIVATE_CAP_PRIMARY_SELECTION = 1 << 4, /**< Separate primary selection. */
+ YASL_PRIVATE_CAP_CSD = 1 << 5, /**< Backend advertises client decoration support; check each action result. */
+ YASL_PRIVATE_CAP_NATIVE_GRAPHICS = 1 << 6, /**< Application-managed native rendering. */
+ YASL_PRIVATE_CAP_FRAME_PACING = 1 << 7, /**< Compositor/display-driven draws. */
+ /* Backend/protocol supports tablet axes, independent of attached hardware. */
+ YASL_PRIVATE_CAP_TABLET = 1 << 8,
+ YASL_PRIVATE_CAP_TEXT_INPUT = 1 << 11, /**< Tablet protocol support, independent of attached hardware. */
+};
+
+/** Independent selections. An unsupported selection never aliases the clipboard. */
+enum yasl_private_selection : uint8_t {
+ YASL_PRIVATE_CLIPBOARD, /**< Explicit copy/paste selection. */
+ YASL_PRIVATE_PRIMARY, /**< Independent primary selection, if supported. */
+};
+
+/** Reported surface state flags; absent flags may also mean unsupported state. */
+enum yasl_private_surface_state : uint32_t {
+ YASL_PRIVATE_STATE_FULLSCREEN = 1 << 0, /**< Fullscreen. */
+ YASL_PRIVATE_STATE_MAXIMIZED = 1 << 1, /**< Maximized. */
+ YASL_PRIVATE_STATE_ACTIVATED = 1 << 2, /**< Active window. */
+ YASL_PRIVATE_STATE_SUSPENDED = 1 << 3, /**< Compositor reports suspension. */
+ YASL_PRIVATE_STATE_CSD = 1 << 4, /**< Backend reports client-side decorations. */
+ YASL_PRIVATE_STATE_TILED = 1 << 5, /**< At least one edge is tiled; currently reported by Wayland. */
+};
+
+/** System cursor shapes. HIDDEN preserves pointer input; NONE is its compatibility alias. */
+enum yasl_private_cursor {
+ YASL_PRIVATE_CURSOR_ARROW, /**< Default arrow. */
+ YASL_PRIVATE_CURSOR_TEXT, /**< Text insertion. */
+ YASL_PRIVATE_CURSOR_LINK, /**< Link/pointer hand. */
+ YASL_PRIVATE_CURSOR_CROSSHAIR, /**< Crosshair. */
+ YASL_PRIVATE_CURSOR_MOVE, /**< Move in any direction. */
+ YASL_PRIVATE_CURSOR_RESIZE_NWSE, /**< Northwest/southeast resize. */
+ YASL_PRIVATE_CURSOR_RESIZE_NESW, /**< Northeast/southwest resize. */
+ YASL_PRIVATE_CURSOR_RESIZE_EW, /**< Horizontal resize. */
+ YASL_PRIVATE_CURSOR_RESIZE_NS, /**< Vertical resize. */
+ YASL_PRIVATE_CURSOR_HIDDEN, /**< Hidden system cursor; pointer input continues. */
+ YASL_PRIVATE_CURSOR_NONE = YASL_PRIVATE_CURSOR_HIDDEN, /**< Compatibility alias for HIDDEN. */
+};
+
+/** Valid interactive resize edges; combinations are restricted to the named values. */
+enum yasl_private_resize_edge : uint32_t {
+ YASL_PRIVATE_EDGE_NONE = 0, /**< No resize edge; rejected by interactive resize. */
+ YASL_PRIVATE_EDGE_TOP = 1, /**< Top edge. */
+ YASL_PRIVATE_EDGE_BOTTOM = 2, /**< Bottom edge. */
+ YASL_PRIVATE_EDGE_LEFT = 4, /**< Left edge. */
+ YASL_PRIVATE_EDGE_TOP_LEFT = 5, /**< Top-left corner. */
+ YASL_PRIVATE_EDGE_BOTTOM_LEFT = 6, /**< Bottom-left corner. */
+ YASL_PRIVATE_EDGE_RIGHT = 8, /**< Right edge. */
+ YASL_PRIVATE_EDGE_TOP_RIGHT = 9, /**< Top-right corner. */
+ YASL_PRIVATE_EDGE_BOTTOM_RIGHT = 10, /**< Bottom-right corner. */
+};
+
+/* Damage & Framebuffer
+ * CPU pixels are tightly packed premultiplied ARGB8888 (0xAARRGGBB).
+ * Size, pointer positions, IME caret and damage use device pixels; surface
+ * toplevel creation sizes use logical pixels. Damage is a hint: backends may upload
+ * the whole frame. Every pixel must contain valid content on each draw.
+ */
+/** Maximum number of damage rectangles in one presentation. */
+#define YASL_PRIVATE_DAMAGE_MAX 64
+
+/** Damage hints for one presentation; every target still needs complete valid pixels. */
+struct yasl_private_damage_list {
+ /** -1: full target; 0: no damaged rectangles; otherwise number of rects. */
+ int n;
+ /** Reserved hint; callers may leave zero. Core presentation uses rects. */
+ struct yasl_private_rect2i bounds;
+ /** First n rectangles, in device pixels; ignored for n == -1. */
+ struct yasl_private_rect2i rects[YASL_PRIVATE_DAMAGE_MAX];
+};
+
+/** Borrowed draw target, valid only for the current draw callback. Never free pixels. */
+struct yasl_private_framebuffer {
+ /** Opaque draw identity. Preserve it and all target fields when copying. */
+ uint64_t generation;
+ /** Writable premultiplied ARGB8888, row stride size.x * sizeof(uint32_t); may be NULL for native rendering. */
+ uint32_t *pixels;
+ /** Target extent in device pixels. */
+ struct yasl_private_vector2i size;
+ /** Row stride in bytes, populated for the active draw callback. */
+ int stride_bytes;
+};
+
+/** One draw invocation. Do not retain its pointer after the callback returns. */
+struct yasl_private_frame {
+ /** Surface being drawn. */
+ struct yasl_surface *surface;
+ /** Target to initialize and present before returning. */
+ struct yasl_private_framebuffer target;
+ /** Zero-based index of this surface draw. */
+ uint64_t frame_index;
+ /** Estimated animation time; not confirmed presentation. */
+ uint64_t target_time_ns;
+ /** Device scale numerator, with 120 representing 1x. */
+ uint32_t scale_120;
+};
+
+/** Coalesced redraw policy, valid with either loop style. */
+typedef enum yasl_private_pacing_mode {
+ YASL_PRIVATE_PACING_ON_DEMAND = 0, /**< Draw only when invalidated or exposed. */
+ YASL_PRIVATE_PACING_CONTINUOUS, /**< Draw continuously using native pacing or a timer. */
+} yasl_private_pacing_mode;
+
+/* Drag and Drop */
+/** Target notifications for YASL surface drags, separate from external file drops. */
+enum yasl_private_drag_event {
+ YASL_PRIVATE_DRAG_OVER, /**< Surface drag entered or moved over the target. */
+ YASL_PRIVATE_DRAG_LEAVE, /**< Surface drag left the target. */
+ YASL_PRIVATE_DRAG_DROP, /**< Surface drag dropped on the target. */
+};
+
+/** External text/file drag hover notifications; completed data arrives through input. */
+enum yasl_private_drop_event {
+ YASL_PRIVATE_DROP_OVER, /**< External data drag entered or moved over the target. */
+ YASL_PRIVATE_DROP_LEAVE, /**< External data drag left the target. */
+};
+
+/** Source-side drag completion; DROPPED does not prove application-level acceptance. */
+enum yasl_private_drag_end {
+ YASL_PRIVATE_DRAG_DROPPED, /**< Drag ended in a drop; not an application acceptance receipt. */
+ YASL_PRIVATE_DRAG_CANCELLED, /**< Drag canceled, including display close. */
+};
+
+/** Copied drag icon in tightly packed premultiplied 0xAARRGGBB device pixels. */
+struct yasl_private_drag_image {
+ /** Copied pixel array; required when an image is supplied. */
+ const uint32_t *pixels;
+ /** Positive width and height in device pixels. */
+ struct yasl_private_vector2i size;
+ /** Pointer position within the image, in device pixels. */
+ struct yasl_private_vector2i hotspot;
+};
+
+/** Copied drag completion listener; userdata remains owned by the application. */
+struct yasl_private_drag_listener {
+ /** Called exactly once after a successful start, possibly synchronously on cancel/close. */
+ void (*end)(void *ud, enum yasl_private_drag_end how);
+};
+
+/** Configured device-pixel content size and the corresponding display scale. */
+struct yasl_private_geometry {
+ /** Current content extent in device pixels. */
+ struct yasl_private_vector2i size;
+ /** Positive scale numerator; 120 means 1x and 180 means 1.5x. */
+ uint32_t scale_120;
+};
+
+/* Input Model */
+/** Keyboard press, release, or autorepeat; releases must not insert text. */
+enum yasl_private_key_action {
+ YASL_PRIVATE_KEY_PRESS, /**< Key pressed. */
+ YASL_PRIVATE_KEY_RELEASE, /**< Key released. */
+ YASL_PRIVATE_KEY_REPEAT, /**< Key autorepeat. */
+};
+
+/** Modifier bitmask. SUPER is Command on macOS. */
+enum yasl_private_mod : uint32_t {
+ YASL_PRIVATE_MOD_SHIFT = 1 << 0, /**< Shift modifier. */
+ YASL_PRIVATE_MOD_ALT = 1 << 1, /**< Alt/Option modifier. */
+ YASL_PRIVATE_MOD_CTRL = 1 << 2, /**< Control modifier. */
+ YASL_PRIVATE_MOD_SUPER = 1 << 3, /**< Super/Command modifier. */
+ YASL_PRIVATE_MOD_CAPS = 1 << 4, /**< Caps Lock modifier. */
+};
+
+/** Pointer source class; capability support does not imply an attached device. */
+enum yasl_private_pointer_device {
+ YASL_PRIVATE_PTR_DEV_MOUSE, /**< Mouse or trackpad pointer. */
+ YASL_PRIVATE_PTR_DEV_TOUCH, /**< Touch translated into pointer input. */
+ YASL_PRIVATE_PTR_DEV_PEN, /**< Tablet stylus. */
+ YASL_PRIVATE_PTR_DEV_ERASER, /**< Tablet eraser tool. */
+};
+
+/** Pointer button; meaningful only for POINTER_DOWN and POINTER_UP. */
+enum yasl_private_pointer_button {
+ YASL_PRIVATE_BUTTON_PRIMARY = 0, /**< Left button, pen tip, or touch contact. */
+ YASL_PRIVATE_BUTTON_MIDDLE = 1, /**< Middle button / wheel click. */
+ YASL_PRIVATE_BUTTON_SECONDARY = 2, /**< Right button or pen barrel button. */
+ YASL_PRIVATE_BUTTON_BACK = 3, /**< Side "back" button. */
+ YASL_PRIVATE_BUTTON_FORWARD = 4, /**< Side "forward" button. */
+};
+
+/** Bit for a yasl_private_pointer_button in yasl_private_pointer_event.buttons. */
+#define YASL_PRIVATE_BUTTON_BIT(button) (1u << (button))
+
+/** Pointer capture mode for a surface. */
+enum yasl_private_pointer_mode {
+ YASL_PRIVATE_POINTER_NORMAL = 0, /**< Absolute positions; cursor per yasl_private_surface_set_cursor(). */
+ /** Cursor hidden and locked in place while the surface has focus. POINTER_MOTION
+ * reports unbounded relative motion in delta; pos stays at the lock position.
+ * The lock is released on focus loss and re-acquired on focus gain. */
+ YASL_PRIVATE_POINTER_RELATIVE,
+};
+
+/** Normalized tablet axes; zero for unavailable axes and non-tablet devices. */
+struct yasl_private_pointer_pen {
+ /** 0..65535 normalized to device range. Zero alone does not end contact. */
+ uint16_t pressure;
+ /** Degrees in -90..90; positive right. */
+ int8_t tilt_x;
+ /** Degrees in -90..90; positive down. */
+ int8_t tilt_y;
+};
+
+/** Event tag. Each tag lists the union member it selects. */
+enum yasl_private_event_type {
+ YASL_PRIVATE_EVENT_NONE = 0,
+
+ /* Input: produced by backends, returned by yasl_private_surface_poll_event(). */
+ YASL_PRIVATE_EVENT_KEY, /**< .key */
+ YASL_PRIVATE_EVENT_TEXT, /**< .text — committed UTF-8; insert this, not key codepoints. */
+ YASL_PRIVATE_EVENT_POINTER_DOWN, /**< .pointer — button/tip pressed; button valid. */
+ YASL_PRIVATE_EVENT_POINTER_UP, /**< .pointer — button/tip released; button valid. */
+ YASL_PRIVATE_EVENT_POINTER_MOTION, /**< .pointer — position, relative motion, or pen axes changed. */
+ YASL_PRIVATE_EVENT_POINTER_SCROLL, /**< .pointer — scroll valid. */
+ YASL_PRIVATE_EVENT_POINTER_LEAVE, /**< .pointer — last known pos/device; cancel hover state. */
+ YASL_PRIVATE_EVENT_FOCUS, /**< .focus — keyboard focus changed; cancel held state on loss. */
+ YASL_PRIVATE_EVENT_PASTE, /**< .text — UTF-8 from yasl_private_display_paste(). */
+ YASL_PRIVATE_EVENT_DROP, /**< .text — dropped text/uri-list: CRLF-separated file URLs, not paths. */
+ YASL_PRIVATE_EVENT_INPUT_RESET, /**< No payload. Input stream interrupted; clear held keys, buttons, strokes, IME. */
+
+ /* Window and application: part of the display FIFO, as well as runner delivery.
+ * Legacy window callbacks also receive matching native notifications. */
+ YASL_PRIVATE_EVENT_WINDOW_RESIZE, /**< .resize — size and/or scale changed; reallocate and redraw. */
+ YASL_PRIVATE_EVENT_WINDOW_CLOSE, /**< No payload. User asked to close the window. */
+ YASL_PRIVATE_EVENT_USER, /**< .user — posted application message. */
+ YASL_PRIVATE_EVENT_WINDOW_STATE, /**< .state — reported state changed. */
+ YASL_PRIVATE_EVENT_DRAG, /**< .drag — surface-drag target notification. */
+ YASL_PRIVATE_EVENT_DROP_HOVER, /**< .drop — external drag hover/leave. */
+ YASL_PRIVATE_EVENT_COMPOSITION, /**< .composition — preedit snapshot, including ending. */
+ YASL_PRIVATE_EVENT_DISPLAY_QUIT, /**< No payload; system requested application quit. */
+ YASL_PRIVATE_EVENT_DISPLAY_REOPEN, /**< No payload; system requested activation. */
+ YASL_PRIVATE_EVENT_DISPLAY_LOST,
+ YASL_PRIVATE_EVENT_PEN,
+ YASL_PRIVATE_EVENT_TEXT_EDIT,
+ YASL_PRIVATE_EVENT_REFRESH, /**< No payload; display connection failed. */
+};
+
+/** Physical keyboard transition with optional layout labels.
+ * key is independent of modifiers/layout and stable between press and release.
+ * codepoint and shifted are optional single Unicode scalars for the current
+ * layout at levels zero/one, not text input. Zero means unavailable, a control
+ * key, or a layout mapping that produces multiple characters. Insert committed
+ * YASL_PRIVATE_EVENT_TEXT into documents instead. */
+struct yasl_private_key_event {
+ /** Portable physical key; YASL_KEY_UNKNOWN when unmapped. */
+ enum yasl_key key;
+ /** Press, release, or autorepeat. */
+ enum yasl_key_action action;
+ /** yasl_private_mod bitmask at this event. */
+ uint32_t mods;
+ /** Optional unshifted layout Unicode scalar; zero if unavailable. */
+ uint32_t codepoint;
+ /** Optional shifted layout Unicode scalar; zero if unavailable. */
+ uint32_t shifted;
+ /** Native code: Wayland evdev, X11 server keycode, Cocoa virtual keycode. */
+ uint32_t native_code;
+};
+
+/** Pointer sample shared by all POINTER_* events. Pen and eraser share it with
+ * mouse input. Track contact with DOWN/UP of the primary button, not pressure.
+ * This is not a multi-contact touch API. */
+struct yasl_private_pointer_event {
+ struct yasl_private_vector2d pixel_scroll;
+ unsigned scroll_valid, scroll_source, scroll_phase;
+ bool scroll_momentum;
+ double precise_x, precise_y;
+ bool precise_position;
+ /** Surface-relative device-pixel position. Frozen while YASL_PRIVATE_POINTER_RELATIVE is locked. */
+ struct yasl_private_vector2i pos;
+ /** MOTION: device-pixel movement since the previous sample on this surface.
+ * Unbounded under YASL_PRIVATE_POINTER_RELATIVE; zero after enter, leave, or reset. */
+ struct yasl_private_vector2d delta;
+ /** SCROLL: fractional line offsets after system preferences; positive right/down. */
+ struct yasl_private_vector2d scroll;
+ /** DOWN/UP: which button changed. */
+ enum yasl_private_pointer_button button;
+ /** YASL_PRIVATE_BUTTON_BIT() mask of buttons held after this event, on every
+ * POINTER_* type. Tracked from this queue's DOWN and UP events for all
+ * devices; INPUT_RESET clears it. On Wayland LEAVE clears it too, since the
+ * compositor sends leave during a press only when it takes the pointer.
+ * X11 and Cocoa keep sending motion and the release after LEAVE. */
+ uint32_t buttons;
+ /** Mouse, touch, pen, or eraser. */
+ enum yasl_private_pointer_device device;
+ /** yasl_private_mod bitmask at this event. */
+ uint32_t mods;
+ /** Current tablet axes, including on pressure-only motion; missing axes are zero. */
+ struct yasl_private_pointer_pen pen;
+};
+
+/** Borrowed NUL-terminated UTF-8, shared by TEXT, PASTE and DROP. */
+struct yasl_private_text_event {
+ /** Borrowed NUL-terminated bytes; see yasl_private_surface_poll_event() for lifetime. */
+ const char *data;
+ /** Length in bytes excluding the terminating NUL. */
+ size_t len;
+};
+
+/** Clipboard transfer outcome. */
+enum yasl_private_transfer_status {
+ YASL_PRIVATE_TRANSFER_OK = 0, /**< Text received successfully, including empty text. */
+ YASL_PRIVATE_TRANSFER_UNAVAILABLE, /**< No text selection is available. */
+ YASL_PRIVATE_TRANSFER_FAILED, /**< Transfer or decoding failed. */
+};
+
+/** Clipboard completion; data/len alias the legacy text payload. */
+struct yasl_private_paste_event {
+ const char *data; /**< Borrowed NUL-terminated UTF-8; empty on failure. */
+ size_t len; /**< Byte length excluding NUL. */
+ uint64_t request_id; /**< Zero for a legacy paste request. */
+ enum yasl_private_transfer_status status; /**< Completion outcome. */
+};
+
+/** Borrowed UTF-8 preedit snapshot, valid until the next successful display poll. */
+struct yasl_private_composition_event {
+ const char *data; /**< Borrowed NUL-terminated UTF-8 snapshot. */
+ size_t len; /**< Byte length excluding NUL. */
+ int begin; /**< UTF-8 byte offset, or -1 if unavailable. */
+ int end; /**< UTF-8 byte offset, or -1 if unavailable. */
+ bool active; /**< Whether composition text is present. */
+ uint64_t revision, generation;
+};
+
+/** Reported window-state change. */
+struct yasl_private_state_event { uint32_t flags; /**< Current YASL_PRIVATE_STATE_* flags. */ };
+/** Surface-drag target notification; position is in device pixels. */
+struct yasl_private_drag_target_event {
+ enum yasl_private_drag_event action; /**< Hover, leave, or drop. */
+ struct yasl_private_vector2i pos; /**< Surface-relative device pixels. */
+};
+/** External drag hover notification; position is in device pixels. */
+struct yasl_private_drop_hover_event {
+ enum yasl_private_drop_event action; /**< Hover or leave. */
+ struct yasl_private_vector2i pos; /**< Surface-relative device pixels. */
+};
+
+/** Keyboard focus transition. */
+struct yasl_private_focus_event {
+ /** true on focus gain, false on loss. */
+ bool focused;
+};
+
+/** New configured geometry for a surface. */
+struct yasl_private_resize_event {
+ /** Content size in device pixels and display scale. */
+ struct yasl_private_geometry geometry;
+ /** true when scale_120 differs from the previous resize of this surface. */
+ bool scale_changed;
+};
+
+/** Application message posted with yasl_private_post_event(). */
+struct yasl_private_user_event {
+ /** Application-defined message code. */
+ uint32_t code;
+ /** Pointer passed through unchanged; ownership stays with the application. */
+ void *payload;
+ /** Optional payload size, passed through unchanged. */
+ size_t size;
+};
+
+/** One tagged event; read only the union member selected by type. */
+enum yasl_private_surface_flags { YASL_PRIVATE_SURFACE_CPU = 1u, YASL_PRIVATE_SURFACE_HIDDEN = 2u, YASL_PRIVATE_SURFACE_UNDECORATED = 4u, YASL_PRIVATE_SURFACE_FIXED_SIZE = 8u };
+enum yasl_private_pen_action { YASL_PRIVATE_PEN_ENTER, YASL_PRIVATE_PEN_LEAVE, YASL_PRIVATE_PEN_MOVE, YASL_PRIVATE_PEN_DOWN, YASL_PRIVATE_PEN_UP, YASL_PRIVATE_PEN_BUTTON, YASL_PRIVATE_PEN_CANCEL };
+enum yasl_private_pen_axis { YASL_PRIVATE_PEN_PRESSURE = 1u, YASL_PRIVATE_PEN_TILT = 2u, YASL_PRIVATE_PEN_ROTATION = 4u, YASL_PRIVATE_PEN_DISTANCE = 8u, YASL_PRIVATE_PEN_TANGENTIAL = 16u, YASL_PRIVATE_PEN_WHEEL = 32u };
+enum yasl_private_presenter { YASL_PRIVATE_PRESENTER_NONE, YASL_PRIVATE_PRESENTER_CPU, YASL_PRIVATE_PRESENTER_GL, YASL_PRIVATE_PRESENTER_VULKAN, YASL_PRIVATE_PRESENTER_METAL };
+struct yasl_private_pen_sample {
+ uint64_t device_id, tool_id;
+ double x, y, pressure, tilt_x, tilt_y, rotation, distance, tangential, wheel_degrees;
+ uint32_t axes_valid, axes_changed, buttons, changed_button;
+ bool in_contact;
+ unsigned tool, action;
+};
+struct yasl_private_event {
+ /** Selects the valid union member. */
+ enum yasl_private_event_type type;
+ /** Recipient surface, or NULL for display-wide events. Can differ from the
+ * polled surface for routed popup input. */
+ struct yasl_surface *surface;
+ /** Monotonic timestamp in nanoseconds, same clock as yasl_private_loop_now_ns(). */
+ uint64_t time_ns;
+ /** Increasing display-wide enqueue order; zero for standalone private queues. */
+ uint64_t sequence;
+ /** Stable recipient identity; zero for display-wide events. */
+ uint64_t surface_id;
+ union {
+ struct yasl_private_pen_sample pen;
+ struct { const char *data; size_t len; uint64_t revision, generation; size_t begin, end; } text_edit;
+ struct yasl_private_key_event key; /**< KEY */
+ struct yasl_private_pointer_event pointer; /**< POINTER_* */
+ struct yasl_private_text_event text; /**< TEXT, legacy PASTE, DROP */
+ struct yasl_private_paste_event paste; /**< PASTE */
+ struct yasl_private_composition_event composition; /**< COMPOSITION */
+ struct yasl_private_state_event state; /**< WINDOW_STATE */
+ struct yasl_private_drag_target_event drag; /**< DRAG */
+ struct yasl_private_drop_hover_event drop; /**< DROP_HOVER */
+ struct yasl_private_focus_event focus; /**< FOCUS */
+ struct yasl_private_resize_event resize; /**< WINDOW_RESIZE */
+ struct yasl_private_user_event user; /**< USER */
+ };
+};
+
+/* Ownership and threading
+ * All calls and callbacks run on the display's opening thread (main thread on
+ * Cocoa). Displays borrow supplied loops; destroy surfaces before displays,
+ * popups before parents, and displays before their supplied loop.
+ * Specs, strings, callback tables and drag listeners are copied during calls.
+ * Userdata is borrowed until callbacks are removed or the object/drag ends.
+ * Surface destruction is allowed from its callbacks; display/loop destruction
+ * during dispatch or callbacks is not supported.
+ */
+
+/* Callbacks */
+/** Optional display notifications. NULL callback members are ignored. */
+struct yasl_private_display_cbs {
+ /** Application quit request; arrange orderly shutdown after dispatch returns. */
+ void (*quit)(void *ud);
+ /** Application reopen/activation request, currently Cocoa. */
+ void (*reopen)(void *ud);
+ /** Display connection lost; arrange cleanup after dispatch returns. */
+ void (*lost)(void *ud);
+};
+
+/** Optional window/popup notifications, copied from yasl_private_window_spec.cbs or by
+yasl_private_surface_set_callbacks().
+Callbacks may destroy their surface. They must then return without further use
+of the surface or draw target. NULL members are ignored. */
+struct yasl_private_window_cbs {
+ /** Initialize the entire target and optionally present. Requested through schedule_redraw/flush. */
+ void (*draw)(struct yasl_surface *s, struct yasl_private_frame *frame, void *ud);
+ /** Configured size/scale changed; reallocate application content and request a redraw. */
+ void (*configure)(struct yasl_surface *s, struct yasl_private_geometry geometry, void *ud);
+ /** Window/popup close request; application is responsible for destruction. */
+ void (*close)(struct yasl_surface *s, void *ud);
+ /** Reported yasl_private_surface_state bitmask changed; matches yasl_private_surface_state(s). */
+ void (*state)(struct yasl_surface *s, uint32_t state, void *ud);
+ /** YASL surface-drag target hover, leave, or drop; pos is in device pixels. */
+ void (*drag)(struct yasl_surface *s, enum yasl_private_drag_event ev,
+ struct yasl_private_vector2i pos, void *ud);
+ /** External data-drag hover or leave; pos is in device pixels. */
+ void (*drop)(struct yasl_surface *s, enum yasl_private_drop_event ev,
+ struct yasl_private_vector2i pos, void *ud);
+};
+
+/* Display Lifecycle */
+/** Runtime backend choice. AUTO selects a native backend and allows fallback. */
+enum yasl_private_backend {
+ YASL_PRIVATE_BACKEND_AUTO = 0, /**< Choose native backend with fallback. */
+ YASL_PRIVATE_BACKEND_WAYLAND, /**< Require Wayland. */
+ YASL_PRIVATE_BACKEND_X11, /**< Require X11. */
+ YASL_PRIVATE_BACKEND_COCOA, /**< Require Cocoa. */
+};
+
+/** Display options. Zero-initialize for automatic backend selection. */
+struct yasl_private_display_spec {
+ /** Copied application identifier, conventionally a reverse-domain UTF-8 string; NULL uses backend defaults. */
+ const char *app_id;
+ /** Copied optional display callbacks; NULL disables them. */
+ const struct yasl_private_display_cbs *cbs;
+ /** Borrowed display callback userdata. */
+ void *ud;
+
+ /** Zero/AUTO chooses native backend; explicit choices never fall back. */
+ enum yasl_private_backend backend;
+};
+
+/* Return NULL and set errno if the requested backend cannot open. */
+/**
+ * Open a desktop connection and its event machinery.
+ * @param loop Borrowed loop, or NULL to create a display-owned loop.
+ * @param spec Copied options, or NULL for automatic backend selection and defaults.
+ * @return An owned display, or NULL with errno set. EINVAL means an invalid backend;
+ * ENOSYS means the requested backend was not built; ENODEV is the fallback when
+ * opening fails without a more specific platform error. Explicit choices never
+ * fall back. Close with yasl_private_display_close() after destroying its surfaces.
+ */
+struct yasl_private_display *yasl_private_display_open(struct yasl_private_loop *loop,
+ const struct yasl_private_display_spec *spec);
+/**
+ * Read the selected backend.
+ * @param d Display, or NULL.
+ * @return The actual backend, or YASL_PRIVATE_BACKEND_AUTO for NULL.
+ */
+enum yasl_private_backend yasl_private_display_backend(const struct yasl_private_display *d);
+/**
+ * Close a display and release its owned loop, if any.
+ * @param d Display to close, or NULL for no action.
+ * Destroy all surfaces first. Cancels an active drag and may invoke its end
+ * listener. Must not be called during dispatch or from a callback.
+ */
+void yasl_private_display_close(struct yasl_private_display *d);
+/**
+ * Query currently available optional features.
+ * @param d Display, or NULL.
+ * @return A bitmask of yasl_private_display_cap values, or zero for NULL.
+ * Capabilities describe backend/server support, not attached input hardware or
+ * permission to perform an operation at any arbitrary moment.
+ */
+uint32_t yasl_private_display_caps(const struct yasl_private_display *d);
+/* Process events, waiting at most timeout_ms: zero polls, negative waits.
+ * Pending input or redraw work returns immediately. Returns -1 on failure. */
+/**
+ * Receive events and run ready loop sources.
+ * @param d Display to dispatch.
+ * @param timeout_ms Maximum wait in milliseconds: zero polls, negative waits indefinitely.
+ * @return A nonnegative backend-specific work count on success, or -1 with errno
+ * set on failure (EINVAL for NULL, EIO if no specific backend error is available).
+ * Do not interpret the count as the number of queued input events. Pending input
+ * or runnable redraw work prevents waiting. Drain input and call
+ * yasl_private_display_flush() after updating application state. Do not dispatch recursively.
+ */
+int yasl_private_display_dispatch(struct yasl_private_display *d, int timeout_ms);
+/**
+ * Draw scheduled surfaces and send pending display requests.
+ * @param d Display, or NULL for no action.
+ * May synchronously invoke draw and other callbacks. A paced surface may wait
+ * for its next display/compositor signal. This is not a GPU completion wait.
+ */
+void yasl_private_display_flush(struct yasl_private_display *d);
+/**
+ * Read the latest modifier state known to the backend.
+ * @param d Display, or NULL.
+ * @return A bitmask of yasl_private_mod values, or zero for NULL.
+ * For an individual event use its own mods field instead.
+ */
+int yasl_private_display_modifiers(const struct yasl_private_display *d);
+/** Double-click interval used when the system reports none. */
+#define YASL_PRIVATE_DOUBLE_CLICK_MS_DEFAULT 400
+/**
+ * Read the system's double-click interval.
+ * @param d Display, or NULL.
+ * @return Maximum milliseconds between the clicks of a double click, or
+ * YASL_PRIVATE_DOUBLE_CLICK_MS_DEFAULT when d is NULL or the system reports none.
+ * X11 reads the XSETTINGS Net/DoubleClickTime value. Wayland, and X11 without
+ * XSETTINGS, read the KDE or GNOME setting through the XDG Settings portal,
+ * which needs D-Bus and can wait briefly. Cocoa reads the system interval.
+ * The user can change it at any time; read it again when a window gains focus
+ * rather than on every click.
+ */
+int yasl_private_display_double_click_ms(const struct yasl_private_display *d);
+/* Unsupported/invalid selections are no-ops, never clipboard aliases. */
+/**
+ * Offer a copy of a NUL-terminated UTF-8 string as a selection.
+ * @param d Display, or NULL for no action.
+ * @param sel YASL_PRIVATE_CLIPBOARD or YASL_PRIVATE_PRIMARY.
+ * @param text Text copied during this call; NULL does nothing.
+ * Unsupported selections do nothing. The void return does not confirm that the
+ * system accepted the offer; Wayland may require a recent input serial.
+ */
+void yasl_private_display_copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text);
+/**
+ * Request text delivery to a particular surface.
+ * @param d Display that owns target.
+ * @param sel YASL_PRIVATE_CLIPBOARD or YASL_PRIVATE_PRIMARY.
+ * @param target Destination surface; NULL or a surface on another display does nothing.
+ * Results arrive as YASL_PRIVATE_EVENT_PASTE, possibly during a later dispatch. No result
+ * is promised for unavailable data or a failed transfer. Destroying target cancels
+ * delivery. Unsupported selections do nothing.
+ */
+void yasl_private_display_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target);
+
+/* A successful drag invokes the copied listener's non-NULL end once, including
+ * cancel and display close. Failed starts do not invoke it. */
+/**
+ * Start a surface drag from a recent pointer press.
+ * @param d Display that owns origin.
+ * @param origin Origin surface.
+ * @param image Optional copied premultiplied ARGB8888 drag image, or NULL.
+ * @param l Optional copied completion listener, or NULL.
+ * @param ud Borrowed listener userdata until completion.
+ * @return true if started; false if unsupported, invalid, or not currently permitted.
+ * A successful start invokes a non-NULL end callback exactly once, including
+ * cancellation or display close. Failed starts do not invoke it. This API carries
+ * surfaces, not arbitrary MIME payloads; use application state to identify the item.
+ */
+bool yasl_private_display_drag_start(struct yasl_private_display *d, struct yasl_surface *origin,
+ const struct yasl_private_drag_image *image,
+ const struct yasl_private_drag_listener *l, void *ud);
+/**
+ * Cancel the active surface drag, if any.
+ * @param d Display, or NULL for no action.
+ * May invoke the end listener synchronously.
+ */
+void yasl_private_display_drag_cancel(struct yasl_private_display *d);
+
+/* Surface Lifecycle */
+/** Toplevel options. Zero-initialize, then supply a positive logical-pixel size. */
+struct yasl_private_window_spec {
+ unsigned flags;
+ bool external_gpu;
+ /** Copied UTF-8 title; NULL uses backend defaults. */
+ const char *title;
+ /** Requested logical-pixel width and height; both must be positive. */
+ union {
+ struct yasl_private_vector2i size_logical; /**< Requested logical-pixel extent. */
+ struct yasl_private_vector2i size; /**< Migration alias for size_logical. */
+ };
+ /** Request initial fullscreen; actual state is reported asynchronously. */
+ bool fullscreen;
+ /** Permit unpaced presentation where supported; a hint, not a guarantee. */
+ bool tearing;
+ /** Copied optional window callbacks; NULL disables them. */
+ const struct yasl_private_window_cbs *cbs;
+ /** Borrowed window callback userdata; ignored by the runner. */
+ void *ud;
+ /** Borrowed application data, independent of callbacks and available in either loop style. */
+ void *userdata;
+};
+
+/** Popup options, entirely in device pixels, including size.
+The content rectangle excludes transparent margins/shadows used for placement.
+An axis of content_size equal to zero extends to that axis of size. The offset
+and resolved content size must fit in size. Anchor extents must be nonnegative.
+Dimensions must be positive, at most 32767, and fit an INT32_MAX-byte buffer;
+backends may impose further limits after scaling or resolving placement. */
+struct yasl_private_popup_spec {
+ unsigned anchor_edges, gravity, constraints;
+ bool positioner_explicit;
+ /** Full popup framebuffer extent in device pixels, including transparent margins. */
+ struct yasl_private_vector2i size;
+ /** Rectangle in parent framebuffer coordinates, in device pixels. */
+ struct yasl_private_rect2i anchor;
+ /** true prefers right of anchor; false prefers below. Placement may flip/slide. */
+ bool beside;
+ /** Top-left content offset within the popup, in device pixels. */
+ struct yasl_private_vector2i content_offset;
+ /** Content extent; zero per axis extends to the corresponding size edge. */
+ struct yasl_private_vector2i content_size;
+};
+
+/**
+ * Create a resizable desktop window with CPU drawing targets.
+ * @param d Owning display.
+ * @param spec Required copied options; size is in logical pixels.
+ * @return An owned surface, or NULL with errno set. EINVAL means invalid options,
+ * including a nonpositive dimension, a dimension above 32767, or a buffer above
+ * INT32_MAX bytes. ENODEV is the fallback backend failure. Actual configured
+ * size may differ from the request. The callback table in spec->cbs is copied;
+ * spec->ud stays borrowed until callbacks are replaced or the surface is destroyed.
+ * This window's callbacks cannot run before creation returns. Use
+ * yasl_private_surface_geometry() for initial geometry; an initial configure callback
+ * is not guaranteed. Schedule an initial redraw after setting up application state.
+ * On a display owned by yasl_private_run(), the runner installs its own callbacks instead;
+ * use yasl_private_app_desc.event and yasl_private_app_desc.frame for application notifications.
+ */
+struct yasl_surface *yasl_private_surface_create_toplevel(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec);
+/**
+ * Create an owned popup attached to a parent surface.
+ * @param parent Borrowed parent; destroy the popup before the parent.
+ * @param spec Required copied options, entirely in device pixels.
+ * @return A popup, or NULL with errno set: EINVAL for invalid options, ENOTSUP
+ * for missing popup support, or a backend error (ENODEV if none was supplied).
+ * Wayland requires a recent input serial for its popup grab. The backend may
+ * flip or slide placement to fit the screen. See yasl_private_popup_spec.
+ */
+struct yasl_surface *yasl_private_surface_create_popup(struct yasl_surface *parent,
+ const struct yasl_private_popup_spec *spec);
+/**
+ * Update a popup's geometry and placement.
+ * @param popup An existing popup.
+ * @param spec Required copied replacement options, in device pixels.
+ * @return true on success; false with errno set: EINVAL for invalid options,
+ * ENOTSUP for missing popup support, or a backend error (ENODEV if unspecified).
+ * Older Wayland popup protocol versions can reject repositioning.
+ */
+bool yasl_private_surface_popup_reposition(struct yasl_surface *popup,
+ const struct yasl_private_popup_spec *spec);
+/**
+ * Destroy a surface and cancel its surface timers and pending input.
+ * @param s Surface, or NULL for no action.
+ * Destroy child popups first. May be called from a surface callback; return from
+ * that callback without further use of the surface or its draw target. Every
+ * pointer or native handle obtained from this surface becomes invalid.
+ */
+void yasl_private_surface_destroy(struct yasl_surface *s);
+/**
+ * Get the display that owns a surface.
+ * @param s Surface, or NULL.
+ * @return A borrowed display, or NULL.
+ */
+struct yasl_private_display *yasl_private_surface_display(const struct yasl_surface *s);
+
+/**
+ * Replace the surface's callback table and userdata.
+ * @param s Surface.
+ * @param cbs Table copied by value; NULL removes all callbacks. NULL members are ignored.
+ * @param ud Borrowed userdata passed to the installed callbacks.
+ * @return true if replaced; false with EINVAL for NULL, EBUSY on a runner-owned surface.
+ * This call only replaces callbacks; it does not invoke them.
+ * Use yasl_private_surface_geometry() to read current geometry; do not rely on receiving
+ * an initial configure callback when registering.
+ */
+bool yasl_private_surface_set_callbacks(struct yasl_surface *s,
+ const struct yasl_private_window_cbs *cbs, void *ud);
+/**
+ * Replace the window title with copied UTF-8 text.
+ * @param s Surface.
+ * @param title NUL-terminated UTF-8 title. Pass an empty string to clear it.
+ * Backends may truncate the title.
+ */
+void yasl_private_surface_set_title(struct yasl_surface *s, const char *title);
+/**
+ * Request a future draw callback, coalescing repeated requests.
+ * @param s Surface, or NULL for no action.
+ * Call yasl_private_display_flush() to service the request. Schedule again from draw to
+ * animate. Without YASL_PRIVATE_CAP_FRAME_PACING, throttle animation with a timer.
+ */
+void yasl_private_surface_schedule_redraw(struct yasl_surface *s);
+/* Present exactly the target supplied to the current draw callback, at most
+ * once. Targets expire when that callback returns or the surface is destroyed.
+ * damage is required: n must be -1 or 0..YASL_PRIVATE_DAMAGE_MAX. Rectangles must have
+ * nonnegative extents; they are clipped to the target. Invalid input returns
+ * false without consuming the frame. See yasl/native.h for native rendering. */
+/**
+ * Submit the current draw target at most once.
+ * @param s Surface currently being drawn.
+ * @param fb Exact target from the current draw callback, including its generation.
+ * @param damage Required damage hint: n is -1 for all pixels or 0..YASL_PRIVATE_DAMAGE_MAX.
+ * @return true if accepted; false for invalid, stale, already presented, or failed targets.
+ * Rectangles must have nonnegative extents and are clipped to the target. Invalid
+ * input does not consume the frame. All pixels must be initialized even with
+ * partial damage. The backend may upload all pixels. Success records submission,
+ * not confirmed scanout. The target expires when draw returns or s is destroyed.
+ */
+bool yasl_private_surface_present(struct yasl_surface *s, const struct yasl_private_framebuffer *fb,
+ const struct yasl_private_damage_list *damage);
+/**
+ * Migration adapter: consume one input event for a surface from the display FIFO.
+ * Popup input can belong to the root queue; inspect ev->surface. Returns false
+ * for empty/invalid arguments, leaving ev unchanged. Strings remain borrowed
+ * until the next successful display or surface poll, or display close. Pending
+ * recipient events are canceled on destruction. Prefer yasl_private_display_poll_event()
+ * to receive window, composition, and display events as well as input. This
+ * input-only adapter discards pending non-input events across the display
+ * (legacy callbacks already delivered lifecycle changes). Do not mix
+ * the two polling styles in one application. Use the display thread.
+ */
+bool yasl_private_surface_poll_event(struct yasl_surface *s, struct yasl_private_event *ev);
+/**
+ * Decode the next local file path from a YASL_PRIVATE_EVENT_DROP payload.
+ * @param cursor Required in/out position in the NUL-terminated list. Start it
+ * at event.text.data; a NULL *cursor returns zero.
+ * @param out Output for the NUL-terminated path, or NULL when cap is zero.
+ * @param cap Size of out in bytes.
+ * @return Path length in bytes, excluding the NUL, or zero when no paths remain.
+ * When the result is less than cap, out holds the path and *cursor moves past
+ * it. Otherwise out and *cursor are unchanged; call again with a buffer of at
+ * least the result plus one.
+ * Accepts file:///path, file:/path, and file://host/path when host is
+ * localhost or this machine's name. Percent escapes decode to raw bytes, and
+ * a '?' or '#' ends the path. Skips blank lines, comments, other schemes,
+ * other hosts, and lines with a malformed escape or an escaped NUL. May be
+ * called from any thread.
+ */
+size_t yasl_private_drop_next_path(const char **cursor, char *out, size_t cap);
+/**
+ * Copy the last frame presented from the surface's CPU target.
+ * @param s Surface.
+ * @param out Output for size->x * size->y premultiplied ARGB8888 pixels with
+ * tightly packed rows, or NULL when cap is zero.
+ * @param cap Capacity of out in pixels.
+ * @param size Required output: the frame extent in device pixels, or zero when
+ * no frame is available.
+ * @return true if copied. false leaves out unchanged; when *size is nonzero,
+ * out was too small, so allocate size->x * size->y pixels and call again.
+ * A frame is available from a successful yasl_private_surface_present() until the
+ * next draw callback starts, and again once that draw presents. After a draw
+ * that does not present or a resize that replaces the target, no frame may be
+ * available; native rendering never has one. Call outside draw callbacks. This copies YASL's frame, not
+ * the composited screen: decorations, other windows, and color conversion are
+ * not included.
+ */
+bool yasl_private_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
+ struct yasl_private_vector2i *size);
+/**
+ * Select absolute or relative (locked) pointer input for a surface.
+ * @param s Surface.
+ * @param mode YASL_PRIVATE_POINTER_NORMAL or YASL_PRIVATE_POINTER_RELATIVE.
+ * @return true if the mode is supported; false leaves the current mode.
+ * RELATIVE hides the cursor and locks it while the surface is focused;
+ * POINTER_MOTION then carries unbounded delta. Returning to NORMAL restores
+ * the cursor selected with yasl_private_surface_set_cursor(). RELATIVE is supported on
+ * Wayland compositors with the relative-pointer and pointer-constraints
+ * protocols; X11 and Cocoa return false.
+ */
+bool yasl_private_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_private_pointer_mode mode);
+
+/**
+ * Consume one event from the display-wide FIFO without waiting.
+ * Returns false for an empty queue or invalid arguments, leaving ev unchanged.
+ * Strings remain valid until the next successful poll or display close.
+ * Surface destruction cancels undelivered events, not the last polled payload.
+ * Use the display thread. Do not mix display and surface polling in one app.
+ */
+bool yasl_private_display_poll_event(struct yasl_private_display *d, struct yasl_private_event *ev);
+
+/** Stable nonzero identity within a display; zero for NULL. Use on the display thread. */
+uint64_t yasl_private_surface_id(const struct yasl_surface *s);
+/** Store borrowed application data independently of callback userdata. */
+void yasl_private_surface_set_userdata(struct yasl_surface *s, void *userdata);
+/** Read borrowed application data, or NULL for NULL; use on the display thread. */
+void *yasl_private_surface_userdata(const struct yasl_surface *s);
+
+/** Thread-safe message descriptor. Zero target_id means display-wide. */
+struct yasl_private_message {
+ uint64_t target_id; /**< Surface identity, or zero for the display. */
+ uint32_t code; /**< Application-defined message code. */
+ void *payload; /**< Borrowed until release; passed through unchanged. */
+ size_t size; /**< Application-defined payload length. */
+ /** Optional cleanup on display thread after delivery/cancellation, exactly once.
+ * May post messages, but must not dispatch, poll, or destroy YASL objects. */
+ void (*release)(void *payload);
+};
+/**
+ * Copy and enqueue a message from any thread; the display must stay alive.
+ * Stop producer threads before closing the display. On success release runs
+ * once after consumption or cancellation. On failure ownership stays with the
+ * caller; errno is EINVAL, ENOENT (retired identity), ENOMEM, or EOVERFLOW.
+ */
+bool yasl_private_display_post(struct yasl_private_display *d, const struct yasl_private_message *message);
+
+/**
+ * Present the active frame once. NULL/0 means full damage; non-NULL/0 means none.
+ * Validate all rectangles; excess rectangles fall back to full damage.
+ * Returns false with EINVAL for invalid/stale targets, EALREADY for a consumed
+ * frame, or errno from the backend (EIO if unspecified).
+ * Frame and pixels expire at callback return; use the display thread.
+ */
+bool yasl_private_frame_present(struct yasl_private_frame *frame,
+ const struct yasl_private_rect2i *rects, size_t count);
+/** Coalesce a redraw request; use on the display thread. NULL is ignored. */
+void yasl_private_surface_request_frame(struct yasl_surface *s);
+/** Set drawing policy for either loop style. Returns false with errno on failure. */
+bool yasl_private_surface_set_pacing(struct yasl_surface *s, yasl_private_pacing_mode mode);
+
+/** Request a positive logical-pixel size. Success means issued, not configured. */
+bool yasl_private_surface_set_size(struct yasl_surface *s, struct yasl_private_vector2i size_logical);
+/** Request fullscreen state; returns acceptance, not confirmation. */
+bool yasl_private_surface_set_fullscreen(struct yasl_surface *s, bool enabled);
+/** Request maximized state; returns acceptance, not confirmation. */
+bool yasl_private_surface_set_maximized(struct yasl_surface *s, bool enabled);
+/** Request minimization. Window requests set errno on failure; display thread only. */
+bool yasl_private_surface_minimize(struct yasl_surface *s);
+
+/** Offer copied UTF-8 text. Returns false with errno if invalid/unsupported/failed. */
+bool yasl_private_clipboard_set(struct yasl_private_display *d, enum yasl_private_selection selection,
+ const char *text);
+/**
+ * Request text; returns a nonzero ID or zero with errno. One PASTE completion
+ * follows each accepted request unless its surface/display is destroyed.
+ * Inspect paste.status even when data is empty. Use the display thread.
+ */
+uint64_t yasl_private_clipboard_request(struct yasl_surface *s, enum yasl_private_selection selection);
+
+/* Current device-pixel size and scale, matching the configure callback. */
+/**
+ * Read current device-pixel size and scale together.
+ * @param s Surface, or NULL.
+ * @return Geometry matching configure callbacks; NULL gives zero size and unit scale.
+ */
+struct yasl_private_geometry yasl_private_surface_geometry(const struct yasl_surface *s);
+/**
+ * Read current window state.
+ * @param s Surface, or NULL.
+ * @return A bitmask of yasl_private_surface_state values, or zero for NULL.
+ * Flags are reported state, not requests; unsupported states are absent.
+ * Flags may coexist. Windowed and floating have no separate flags. Suspension
+ * does not imply minimization, and no minimized flag is reported.
+ */
+uint32_t yasl_private_surface_state(const struct yasl_surface *s);
+/* Timing uses monotonic nanoseconds. Check source before treating timestamps
+ * as presentation confirmation. refresh/next_present are estimates. Without
+ * YASL_PRIVATE_CAP_FRAME_PACING, redraws run on flush without display synchronization.
+ * Tearing may bypass pacing even when the capability is present. */
+/**
+ * Read frame timing estimates and their provenance.
+ * @param s Surface, or NULL.
+ * @return Current timing, or a zero-initialized result for NULL.
+ * Timestamps use monotonic nanoseconds. Inspect source before treating a timestamp
+ * as confirmation of presentation. Refresh and next-present values are estimates.
+ */
+struct yasl_private_timing yasl_private_surface_timing(const struct yasl_surface *s);
+/**
+ * Get a nondecreasing animation timestamp aligned to estimated refresh.
+ * @param s Surface, or NULL.
+ * @param now Current monotonic nanoseconds, usually from yasl_private_loop_now_ns().
+ * @return Animation timestamp, or zero for NULL. Before timing is known, returns now.
+ * Use this to advance animations; it is not presentation feedback.
+ */
+uint64_t yasl_private_surface_frame_time_ns(struct yasl_surface *s, uint64_t now);
+/* Set the mouse and tablet cursor for this surface, including future entries.
+ * YASL_PRIVATE_CURSOR_HIDDEN stays hidden until another cursor is selected. */
+/**
+ * Select the surface's system mouse and tablet cursor.
+ * @param s Surface.
+ * @param cursor Cursor shape; YASL_PRIVATE_CURSOR_HIDDEN hides the cursor without disabling input.
+ * The choice persists across motion and re-entry until another shape is selected.
+ */
+void yasl_private_surface_set_cursor(struct yasl_surface *s, enum yasl_private_cursor cursor);
+/**
+ * Set a backend window background hint.
+ * @param s Surface.
+ * @param bg Packed 0xAARRGGBB color; backends may ignore alpha or the entire hint.
+ * Does not initialize framebuffer pixels or replace drawing.
+ */
+void yasl_private_surface_set_background_color(struct yasl_surface *s, uint32_t bg);
+
+/* IME Support: preedit is borrowed until the next dispatch or destruction. */
+/**
+ * Read the current uncommitted UTF-8 composition text.
+ * @param s Surface, or NULL.
+ * @return Borrowed NUL-terminated text, or NULL if absent/unsupported.
+ * Copy to retain across the next dispatch or surface destruction. Composition
+ * changes can schedule a redraw. Insert committed YASL_PRIVATE_EVENT_TEXT into documents,
+ * not preedit text.
+ */
+const char *yasl_private_surface_ime_preedit(const struct yasl_surface *s);
+/**
+ * Read the cursor/selection range in the preedit string.
+ * @param s Surface.
+ * @param begin Required output UTF-8 byte offset for the range start.
+ * @param end Required output UTF-8 byte offset for the range end.
+ * @return true if available; false without changing outputs otherwise.
+ * Currently available on Wayland; Cocoa preedit text has no range result.
+ */
+bool yasl_private_surface_ime_preedit_cursor(const struct yasl_surface *s, int *begin, int *end);
+/**
+ * Place the composition candidate window near the application caret.
+ * @param s Surface.
+ * @param caret Caret rectangle relative to the surface, in device pixels.
+ * Update after caret motion or scale changes. Unsupported backends ignore it.
+ */
+void yasl_private_surface_set_ime_caret(struct yasl_surface *s, struct yasl_private_rect2i caret);
+
+/* Client-Side Decorations (CSD) Actions */
+/**
+ * Ask the window system to begin an interactive move.
+ * @param s Toplevel surface, normally during handling of a pointer press.
+ * @return true if the request was issued; false if unsupported or not permitted.
+ * A recent input serial may be required. Success does not confirm a final position.
+ */
+bool yasl_private_surface_start_interactive_move(struct yasl_surface *s);
+/**
+ * Ask the window system to begin an interactive resize.
+ * @param s Toplevel surface, normally during handling of a pointer press.
+ * @param edge One nonzero yasl_private_resize_edge enumerator.
+ * @return true if issued; false for an invalid edge, missing support, or rejected request.
+ */
+bool yasl_private_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_private_resize_edge edge);
+/**
+ * Ask the window system to display its window management menu.
+ * @param s Toplevel surface.
+ * @param pos Surface-relative position in device pixels.
+ * @return true if issued; false if unsupported or not currently permitted.
+ */
+bool yasl_private_surface_show_window_menu(struct yasl_surface *s, struct yasl_private_vector2i pos);
+
+/* Drag Support */
+/**
+ * Attach a toplevel surface to the current surface drag.
+ * @param s Surface to carry; check YASL_PRIVATE_CAP_DRAG_CARRY first.
+ * @param offset Device-pixel offset of the pointer within the carried surface.
+ * @return true if accepted; false if unsupported or no compatible drag is active.
+ */
+bool yasl_private_surface_drag_carry(struct yasl_surface *s, struct yasl_private_vector2i offset);
+/**
+ * Place a toplevel surface relative to the current pointer.
+ * @param s Surface; check YASL_PRIVATE_CAP_PLACE_WINDOW first.
+ * @param at Device-pixel position within the surface to place under the pointer.
+ * @return true if issued; false if unsupported or unavailable.
+ */
+bool yasl_private_surface_place_under_pointer(struct yasl_surface *s, struct yasl_private_vector2i at);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* YASL_PRIVATE_H */
diff --git a/src/common/uri.c b/src/common/uri.c
index d836943..cc539a1 100644
--- a/src/common/uri.c
+++ b/src/common/uri.c
@@ -1,5 +1,5 @@
#define _POSIX_C_SOURCE 200809L
-#include "yasl/yasl.h"
+#include "common/types.h"
#include <string.h>
#include <strings.h>
@@ -55,7 +55,7 @@ static size_t decode_path(const char *line, size_t len, char *out)
return n;
}
-size_t yasl_drop_next_path(const char **cursor, char *out, size_t cap)
+size_t yasl_private_drop_next_path(const char **cursor, char *out, size_t cap)
{
if (!cursor || !*cursor || (!out && cap)) return 0;
const char *p = *cursor;
diff --git a/src/common/utf8.c b/src/common/utf8.c
index 78a5d8e..2fb737a 100644
--- a/src/common/utf8.c
+++ b/src/common/utf8.c
@@ -11,7 +11,7 @@
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif
-bool yasl_utf8_validate(const char *str, size_t len)
+bool yasl_private_utf8_validate(const char *str, size_t len)
{
if (!str) return len == 0;
const unsigned char *p = (const unsigned char *)str;
@@ -54,7 +54,7 @@ bool yasl_utf8_validate(const char *str, size_t len)
return true;
}
-char *yasl_utf8_from_latin1(const char *str, size_t len, size_t *out_len)
+char *yasl_private_utf8_from_latin1(const char *str, size_t len, size_t *out_len)
{
if (len > (SIZE_MAX - 1) / 2) return NULL;
char *out = malloc(len * 2 + 1);
@@ -73,7 +73,7 @@ char *yasl_utf8_from_latin1(const char *str, size_t len, size_t *out_len)
return out;
}
-char *yasl_utf8_to_latin1(const char *str, size_t *out_len)
+char *yasl_private_utf8_to_latin1(const char *str, size_t *out_len)
{
char *out = malloc(strlen(str) + 1);
if (!out) return NULL;
@@ -94,11 +94,11 @@ char *yasl_utf8_to_latin1(const char *str, size_t *out_len)
return out;
}
-char *yasl_utf8_sanitize_dup(const char *str)
+char *yasl_private_utf8_sanitize_dup(const char *str)
{
if (unlikely(!str)) return NULL;
size_t len = strlen(str);
- if (likely(yasl_utf8_validate(str, len))) {
+ if (likely(yasl_private_utf8_validate(str, len))) {
char *out = malloc(len + 1);
if (unlikely(!out)) return NULL;
memcpy(out, str, len + 1);
diff --git a/src/common/utf8.h b/src/common/utf8.h
index f31c99d..5ffdb15 100644
--- a/src/common/utf8.h
+++ b/src/common/utf8.h
@@ -1,14 +1,14 @@
-#ifndef YASL_COMMON_UTF8_H
-#define YASL_COMMON_UTF8_H
+#ifndef YASL_PRIVATE_COMMON_UTF8_H
+#define YASL_PRIVATE_COMMON_UTF8_H
#include <stdbool.h>
#include <stddef.h>
-bool yasl_utf8_validate(const char *str, size_t len);
-char *yasl_utf8_sanitize_dup(const char *str);
+bool yasl_private_utf8_validate(const char *str, size_t len);
+char *yasl_private_utf8_sanitize_dup(const char *str);
/* Owned conversions for X11 STRING interoperability. to_latin1 rejects text
* outside Latin-1; byte lengths exclude the allocated NUL terminator. */
-char *yasl_utf8_from_latin1(const char *str, size_t len, size_t *out_len);
-char *yasl_utf8_to_latin1(const char *str, size_t *out_len);
+char *yasl_private_utf8_from_latin1(const char *str, size_t len, size_t *out_len);
+char *yasl_private_utf8_to_latin1(const char *str, size_t *out_len);
#endif
diff --git a/src/common/vulkan.c b/src/common/vulkan.c
new file mode 100644
index 0000000..1bca415
--- /dev/null
+++ b/src/common/vulkan.c
@@ -0,0 +1,87 @@
+#if YASL_HAS_WAYLAND
+#define VK_USE_PLATFORM_WAYLAND_KHR
+#endif
+#if YASL_HAS_X11
+#define VK_USE_PLATFORM_XLIB_KHR
+#endif
+#if YASL_HAS_COCOA
+#define VK_USE_PLATFORM_METAL_EXT
+#endif
+#include "yasl/vulkan.h"
+#include "public_internal.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#if YASL_HAS_WAYLAND
+#include "yasl/native/wayland.h"
+#endif
+#if YASL_HAS_X11
+#include "yasl/native/x11.h"
+#endif
+static PFN_vkGetInstanceProcAddr get_proc;
+static bool portability;
+void yasl_private_vulkan_reset(void) { get_proc=NULL; portability=false; }
+bool yasl_vulkan_init(PFN_vkGetInstanceProcAddr loader)
+{
+ if(!loader)return yasl_public_fail(EINVAL);
+ get_proc=loader;portability=false;
+ PFN_vkEnumerateInstanceExtensionProperties enumerate=(PFN_vkEnumerateInstanceExtensionProperties)get_proc(VK_NULL_HANDLE,"vkEnumerateInstanceExtensionProperties");
+ if(!enumerate)return yasl_public_fail(ENOTSUP);
+ uint32_t count=0;VkResult r=enumerate(NULL,&count,NULL);
+ if(r!=VK_SUCCESS)return yasl_public_fail(EIO);
+ VkExtensionProperties *ext=count?calloc(count,sizeof(*ext)):NULL;
+ if(count&&!ext)return yasl_public_fail(ENOMEM);
+ r=enumerate(NULL,&count,ext);
+ if(r==VK_SUCCESS)for(uint32_t i=0;i<count;i++)if(!strcmp(ext[i].extensionName,"VK_KHR_portability_enumeration"))portability=true;
+ free(ext);return r==VK_SUCCESS||yasl_public_fail(EIO);
+}
+const char *const *yasl_vulkan_instance_extensions(const yasl_surface *s,uint32_t *count)
+{
+ static const char *wayland[]={"VK_KHR_surface","VK_KHR_wayland_surface"};
+ static const char *x11[]={"VK_KHR_surface","VK_KHR_xlib_surface"};
+ static const char *metal[]={"VK_KHR_surface","VK_EXT_metal_surface","VK_KHR_portability_enumeration"};
+ if(count)*count=0;
+ if(!s||!count||s->presenter==YASL_PRIVATE_PRESENTER_CPU||s->presenter==YASL_PRIVATE_PRESENTER_GL){yasl_public_fail(EINVAL);return NULL;}
+ switch(s->display->ops->type) {
+ case YASL_PRIVATE_BACKEND_WAYLAND:*count=2;return wayland;
+ case YASL_PRIVATE_BACKEND_X11:*count=2;return x11;
+ case YASL_PRIVATE_BACKEND_COCOA:*count=portability?3:2;return metal;
+ default:yasl_public_fail(ENOTSUP);return NULL;
+ }
+}
+VkInstanceCreateFlags yasl_vulkan_instance_flags(const yasl_surface *s)
+{ return s&&s->display->ops->type==YASL_PRIVATE_BACKEND_COCOA&&portability?VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR:0; }
+VkResult yasl_vulkan_create_surface(yasl_surface *s,VkInstance instance,const VkAllocationCallbacks *allocator,VkSurfaceKHR *out)
+{
+ if(out)*out=VK_NULL_HANDLE;
+ if(!yasl_public_surface(s,false)||!instance||!out||!get_proc){yasl_public_fail(EINVAL);return VK_ERROR_INITIALIZATION_FAILED;}
+ if(s->presenter!=YASL_PRIVATE_PRESENTER_NONE){yasl_public_fail(EBUSY);return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;}
+ VkResult result=VK_ERROR_EXTENSION_NOT_PRESENT;
+ switch(s->display->ops->type) {
+#if YASL_HAS_WAYLAND
+ case YASL_PRIVATE_BACKEND_WAYLAND: {
+ PFN_vkCreateWaylandSurfaceKHR create=(PFN_vkCreateWaylandSurfaceKHR)get_proc(instance,"vkCreateWaylandSurfaceKHR");
+ if(create) { VkWaylandSurfaceCreateInfoKHR info={.sType=VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
+ .display=yasl_wayland_display(s),.surface=yasl_wayland_surface(s)};result=create(instance,&info,allocator,out); }break;
+ }
+#endif
+#if YASL_HAS_X11
+ case YASL_PRIVATE_BACKEND_X11: {
+ PFN_vkCreateXlibSurfaceKHR create=(PFN_vkCreateXlibSurfaceKHR)get_proc(instance,"vkCreateXlibSurfaceKHR");
+ if(create) { VkXlibSurfaceCreateInfoKHR info={.sType=VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
+ .dpy=yasl_x11_display(s),.window=(Window)yasl_x11_window(s)};result=create(instance,&info,allocator,out); }break;
+ }
+#endif
+#if YASL_HAS_COCOA
+ case YASL_PRIVATE_BACKEND_COCOA: {
+ PFN_vkCreateMetalSurfaceEXT create=(PFN_vkCreateMetalSurfaceEXT)get_proc(instance,"vkCreateMetalSurfaceEXT");
+ if(create&&s->display->ops->surface_get_metal_layer) { VkMetalSurfaceCreateInfoEXT info={.sType=VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT,
+ .pLayer=s->display->ops->surface_get_metal_layer(s)};result=create(instance,&info,allocator,out); }break;
+ }
+#endif
+ default:break;
+ }
+ if(result==VK_SUCCESS)s->presenter=YASL_PRIVATE_PRESENTER_VULKAN;
+ else yasl_public_fail(result==VK_ERROR_EXTENSION_NOT_PRESENT?ENOTSUP:EIO);
+ return result;
+}