diff options
Diffstat (limited to 'src')
61 files changed, 5732 insertions, 1764 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; +} diff --git a/src/platform/cocoa/api.h b/src/platform/cocoa/api.h index eaacc01..4a22410 100644 --- a/src/platform/cocoa/api.h +++ b/src/platform/cocoa/api.h @@ -1,40 +1,40 @@ -#ifndef YASL_COCOA_API_H -#define YASL_COCOA_API_H +#ifndef YASL_PRIVATE_COCOA_API_H +#define YASL_PRIVATE_COCOA_API_H -#include "yasl/yasl.h" +#include "common/types.h" struct cocoa_display; struct cocoa_surface; -struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec); +struct cocoa_display *cocoa_display_open(struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec); void cocoa_display_close(struct cocoa_display *d); uint32_t cocoa_display_caps(const struct cocoa_display *d); int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms); void cocoa_display_flush(struct cocoa_display *d); int cocoa_display_modifiers(const struct cocoa_display *d); -void cocoa_display_copy(struct cocoa_display *d, enum yasl_selection sel, const char *text); -void cocoa_display_paste(struct cocoa_display *d, enum yasl_selection sel, struct cocoa_surface *target); -bool cocoa_display_drag_start(struct cocoa_display *d, struct cocoa_surface *origin, const struct yasl_drag_image *image, const struct yasl_drag_listener *l, void *ud); +void cocoa_display_copy(struct cocoa_display *d, enum yasl_private_selection sel, const char *text); +void cocoa_display_paste(struct cocoa_display *d, enum yasl_private_selection sel, struct cocoa_surface *target); +bool cocoa_display_drag_start(struct cocoa_display *d, struct cocoa_surface *origin, const struct yasl_private_drag_image *image, const struct yasl_private_drag_listener *l, void *ud); struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d, - const struct yasl_window_spec *spec, void *graphics_device); -struct cocoa_surface *cocoa_surface_create_popup(struct cocoa_surface *parent, const struct yasl_popup_spec *spec); -bool cocoa_surface_popup_reposition(struct cocoa_surface *popup, const struct yasl_popup_spec *spec); + const struct yasl_private_window_spec *spec, void *graphics_device); +struct cocoa_surface *cocoa_surface_create_popup(struct cocoa_surface *parent, const struct yasl_private_popup_spec *spec); +bool cocoa_surface_popup_reposition(struct cocoa_surface *popup, const struct yasl_private_popup_spec *spec); void cocoa_surface_destroy(struct cocoa_surface *s); -void cocoa_surface_set_callbacks(struct cocoa_surface *s, const struct yasl_window_cbs *cbs, void *ud); +void cocoa_surface_set_callbacks(struct cocoa_surface *s, const struct yasl_private_window_cbs *cbs, void *ud); void cocoa_surface_set_title(struct cocoa_surface *s, const char *title); void cocoa_surface_schedule_redraw(struct cocoa_surface *s); -bool cocoa_surface_present(struct cocoa_surface *s, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage); -bool cocoa_surface_poll_event(struct cocoa_surface *s, struct yasl_event *ev); -struct yasl_geometry cocoa_surface_geometry(const struct cocoa_surface *s); -struct yasl_timing cocoa_surface_timing(const struct cocoa_surface *s); -void cocoa_surface_set_cursor(struct cocoa_surface *s, enum yasl_cursor cursor); +bool cocoa_surface_present(struct cocoa_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage); +bool cocoa_surface_poll_event(struct cocoa_surface *s, struct yasl_private_event *ev); +struct yasl_private_geometry cocoa_surface_geometry(const struct cocoa_surface *s); +struct yasl_private_timing cocoa_surface_timing(const struct cocoa_surface *s); +void cocoa_surface_set_cursor(struct cocoa_surface *s, enum yasl_private_cursor cursor); void cocoa_surface_set_background_color(struct cocoa_surface *s, uint32_t bg); bool cocoa_surface_ime_preedit_cursor(const struct cocoa_surface *s, int *begin, int *end); -void cocoa_surface_set_ime_caret(struct cocoa_surface *s, struct yasl_rect2i caret); +void cocoa_surface_set_ime_caret(struct cocoa_surface *s, struct yasl_private_rect2i caret); bool cocoa_surface_start_interactive_move(struct cocoa_surface *s); -bool cocoa_surface_start_interactive_resize(struct cocoa_surface *s, enum yasl_resize_edge edge); -bool cocoa_surface_show_window_menu(struct cocoa_surface *s, struct yasl_vector2i pos); -bool cocoa_surface_drag_carry(struct cocoa_surface *s, struct yasl_vector2i offset); -bool cocoa_surface_place_under_pointer(struct cocoa_surface *s, struct yasl_vector2i at); +bool cocoa_surface_start_interactive_resize(struct cocoa_surface *s, enum yasl_private_resize_edge edge); +bool cocoa_surface_show_window_menu(struct cocoa_surface *s, struct yasl_private_vector2i pos); +bool cocoa_surface_drag_carry(struct cocoa_surface *s, struct yasl_private_vector2i offset); +bool cocoa_surface_place_under_pointer(struct cocoa_surface *s, struct yasl_private_vector2i at); #endif diff --git a/src/platform/cocoa/desktop.m b/src/platform/cocoa/desktop.m index 526cbb9..76aa551 100644 --- a/src/platform/cocoa/desktop.m +++ b/src/platform/cocoa/desktop.m @@ -2,7 +2,7 @@ #import <UserNotifications/UserNotifications.h> #include "common/desktop.h" -#include "yasl/desktop.h" +#include "common/private_desktop.h" #include <CoreFoundation/CoreFoundation.h> #include <limits.h> @@ -94,11 +94,11 @@ uint32_t desktop_appearance(void) NSAppearanceName name = [NSApp.effectiveAppearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]]; if ([name isEqualToString:NSAppearanceNameDarkAqua]) - flags |= YASL_APPEARANCE_DARK; + flags |= YASL_PRIVATE_APPEARANCE_DARK; else if (name) - flags |= YASL_APPEARANCE_LIGHT; + flags |= YASL_PRIVATE_APPEARANCE_LIGHT; if ([NSWorkspace sharedWorkspace].accessibilityDisplayShouldIncreaseContrast) - flags |= YASL_APPEARANCE_HIGH_CONTRAST; + flags |= YASL_PRIVATE_APPEARANCE_HIGH_CONTRAST; return flags; } } diff --git a/src/platform/cocoa/keymap.c b/src/platform/cocoa/keymap.c index b81c364..c43837c 100644 --- a/src/platform/cocoa/keymap.c +++ b/src/platform/cocoa/keymap.c @@ -1,7 +1,7 @@ #include "keymap.h" #include "keycodes.h" -enum yasl_key yasl_key_from_cocoa(uint32_t code) +enum yasl_key yasl_private_key_from_cocoa(uint32_t code) { switch (code) { case kVK_ANSI_A: return YASL_KEY_A; diff --git a/src/platform/cocoa/keymap.h b/src/platform/cocoa/keymap.h index a5394e2..792b10d 100644 --- a/src/platform/cocoa/keymap.h +++ b/src/platform/cocoa/keymap.h @@ -1,8 +1,8 @@ -#ifndef YASL_COCOA_KEYMAP_H -#define YASL_COCOA_KEYMAP_H +#ifndef YASL_PRIVATE_COCOA_KEYMAP_H +#define YASL_PRIVATE_COCOA_KEYMAP_H #include "yasl/keycodes.h" -enum yasl_key yasl_key_from_cocoa(uint32_t code); +enum yasl_key yasl_private_key_from_cocoa(uint32_t code); #endif diff --git a/src/platform/cocoa/window.m b/src/platform/cocoa/window.m index 60b20b0..7be78fd 100644 --- a/src/platform/cocoa/window.m +++ b/src/platform/cocoa/window.m @@ -1,10 +1,12 @@ #import <AppKit/AppKit.h> #import <Metal/Metal.h> #import <QuartzCore/QuartzCore.h> +#import <OpenGL/OpenGL.h> #include "platform/cocoa/api.h" #include "common/backend.h" -#include "yasl/native/cocoa.h" +#include "common/public_internal.h" +#include "common/private_native_cocoa.h" #include "common/timing.h" #include "common/loop.h" #include "platform/cocoa/keycodes.h" @@ -18,8 +20,11 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <dlfcn.h> #include <sys/event.h> #include <unistd.h> +#include <objc/message.h> +#include <objc/runtime.h> #define LINK_IDLE_FRAMES 8 /* Application-defined event subtype that only wakes a waiting dispatch. */ @@ -63,6 +68,7 @@ struct cocoa_surface { YaslWindowController *controller; YaslView *view; CAMetalLayer *layer; + NSOpenGLContext *gl_context; id<MTLCommandQueue> software_queue; id<MTLBuffer> software_buffer; NSUInteger software_buffer_capacity; @@ -70,16 +76,19 @@ struct cocoa_surface { struct cocoa_surface *parent; bool is_popup; - struct yasl_vector2i content_offset; + struct yasl_private_vector2i content_offset; - const struct yasl_window_cbs *cbs; + const struct yasl_private_window_cbs *cbs; void *ud; uint64_t frames_skipped; char *preedit; - struct yasl_rect2i ime_caret; + size_t preedit_begin, preedit_end; + NSUInteger marked_utf16_location; + uint64_t preedit_revision, preedit_generation; + struct yasl_private_rect2i ime_caret; - struct yasl_vector2i size; + struct yasl_private_vector2i size; uint32_t scale_120; uint32_t bg_color; uint32_t *pixels; @@ -87,11 +96,11 @@ struct cocoa_surface { id<CAMetalDrawable> drawable; bool transaction; - struct yasl_frame frame; - struct yasl_damage_list damage; + struct yasl_private_frame frame; + struct yasl_private_damage_list damage; bool did_present; /* Extent of the frame in pixels, valid while did_present is set. */ - struct yasl_vector2i shown_size; + struct yasl_private_vector2i shown_size; unsigned idle_frames; bool want_draw; bool frame_due; @@ -102,27 +111,29 @@ struct cocoa_surface { struct cocoa_surface *dead_next; struct input_queue input; - struct yasl_vector2i pointer; + struct yasl_private_vector2i pointer; double scroll_x; uint32_t mods; uint32_t buttons; - enum yasl_cursor cursor; - enum yasl_pointer_device tablet_device; + enum yasl_private_cursor cursor; + enum yasl_private_pointer_device tablet_device; bool tablet_down; + uint64_t pen_device_id, pen_tool_id; + uint32_t pen_buttons; char title[160]; - struct yasl_frame_clock clock; + struct yasl_private_frame_clock clock; }; struct cocoa_display { - struct yasl_display base; + struct yasl_private_display base; struct cocoa_surface **windows; int count; int capacity; - const struct yasl_display_cbs *cbs; + const struct yasl_private_display_cbs *cbs; void *ud; char *app_id; - struct yasl_loop *loop; + struct yasl_private_loop *loop; id key_up_monitor; NSCursor *hidden_cursor; YaslAppDelegate *delegate; @@ -132,15 +143,15 @@ struct cocoa_display { bool active; struct cocoa_surface *origin; struct cocoa_surface *carried; - struct yasl_vector2i carry_offset; + struct yasl_private_vector2i carry_offset; struct cocoa_surface *hover; - struct yasl_vector2i hover_pos; - struct yasl_drag_listener listener; + struct yasl_private_vector2i hover_pos; + struct yasl_private_drag_listener listener; void *ud; NSImage *image; NSWindow *image_window; - struct yasl_vector2i image_size; - struct yasl_vector2i image_hotspot; + struct yasl_private_vector2i image_size; + struct yasl_private_vector2i image_hotspot; } drag; int depth; bool open; @@ -151,16 +162,16 @@ struct cocoa_display { static struct cocoa_display darwin_ctx; -static void drag_end(struct cocoa_display *d, enum yasl_drag_end how) +static void drag_end(struct cocoa_display *d, enum yasl_private_drag_end how) { if (!d || !d->drag.active) return; - struct yasl_drag_listener l = d->drag.listener; + struct yasl_private_drag_listener l = d->drag.listener; void *ud = d->drag.ud; struct cocoa_surface *hover = d->drag.hover; - struct yasl_vector2i pos = d->drag.hover_pos; + struct yasl_private_vector2i pos = d->drag.hover_pos; d->drag.active = false; - d->drag.listener = (struct yasl_drag_listener){0}; + d->drag.listener = (struct yasl_private_drag_listener){0}; d->drag.hover = NULL; d->drag.origin = NULL; d->drag.carried = NULL; @@ -175,10 +186,10 @@ static void drag_end(struct cocoa_display *d, enum yasl_drag_end how) if (hover && !hover->destroyed) { darwin_ctx.depth++; - if (how == YASL_DRAG_DROPPED) - yasl_surface_notify_drag(&hover->base, YASL_DRAG_DROP, pos); + if (how == YASL_PRIVATE_DRAG_DROPPED) + yasl_private_surface_notify_drag(&hover->base, YASL_PRIVATE_DRAG_DROP, pos); else - yasl_surface_notify_drag(&hover->base, YASL_DRAG_LEAVE, pos); + yasl_private_surface_notify_drag(&hover->base, YASL_PRIVATE_DRAG_LEAVE, pos); darwin_ctx.depth--; } if (l.end) @@ -187,6 +198,7 @@ static void drag_end(struct cocoa_display *d, enum yasl_drag_end how) static void render_and_commit(struct cocoa_surface *w); static void reap_dead(struct cocoa_display *d); +static void push_pen(struct cocoa_surface *w, NSEvent *event, unsigned action, bool contact); static void set_state(struct cocoa_surface *w, uint32_t bit, bool on) { @@ -197,21 +209,25 @@ static void set_state(struct cocoa_surface *w, uint32_t bit, bool on) return; w->base.state = next; darwin_ctx.depth++; - yasl_surface_notify_state(&w->base, next); + yasl_private_surface_notify_state(&w->base, next); darwin_ctx.depth--; } static void check_pointer_reset(struct cocoa_surface *w) { if (!w || w->closed) return; - if (w->buttons != 0 || w->tablet_down) { - struct yasl_event ev = { - .type = YASL_EVENT_INPUT_RESET, + if (w->buttons != 0 || w->tablet_down || w->tablet_device != YASL_PRIVATE_PTR_DEV_MOUSE) { + if (w->tablet_device != YASL_PRIVATE_PTR_DEV_MOUSE) + push_pen(w, nil, YASL_PRIVATE_PEN_CANCEL, false); + struct yasl_private_event ev = { + .type = YASL_PRIVATE_EVENT_INPUT_RESET, .surface = &w->base, }; input_queue_push(&w->input, &ev); w->buttons = 0; w->tablet_down = false; + w->tablet_device = YASL_PRIVATE_PTR_DEV_MOUSE; + w->pen_device_id = w->pen_tool_id = 0; } } @@ -221,14 +237,14 @@ static void push_focus(struct cocoa_surface *w, bool focused) return; if (!focused) check_pointer_reset(w); - struct yasl_event ev = { - .type = YASL_EVENT_FOCUS, + struct yasl_private_event ev = { + .type = YASL_PRIVATE_EVENT_FOCUS, .surface = &w->base, .focus = { .focused = focused }, }; input_queue_push(&w->input, &ev); darwin_ctx.depth++; - set_state(w, YASL_STATE_ACTIVATED, focused); + set_state(w, YASL_PRIVATE_STATE_ACTIVATED, focused); if (!w->destroyed && !w->closed) cocoa_surface_schedule_redraw(w); darwin_ctx.depth--; @@ -236,18 +252,18 @@ static void push_focus(struct cocoa_surface *w, bool focused) static void sync_size(struct cocoa_surface *w, bool force) { - if (!w || w->destroyed || !w->layer || w->closed) + if (!w || w->destroyed || w->closed || !w->view || !w->window) return; - uint64_t start = yasl_loop_now_ns(); + uint64_t start = yasl_private_loop_now_ns(); NSRect bounds = w->view.bounds; CGFloat scale = w->window.backingScaleFactor; if (scale < 1.0) scale = 1.0; - struct yasl_vector2i size = { + struct yasl_private_vector2i size = { .x = (int)lround(bounds.size.width * scale), .y = (int)lround(bounds.size.height * scale), }; - uint32_t scale_120 = (uint32_t)lround(scale * YASL_SCALE_UNIT); + uint32_t scale_120 = (uint32_t)lround(scale * YASL_PRIVATE_SCALE_UNIT); bool rescaled = force || scale_120 != w->scale_120; bool resized = force || (size.x != w->size.x || size.y != w->size.y); if (!rescaled && !resized) @@ -255,15 +271,21 @@ static void sync_size(struct cocoa_surface *w, bool force) darwin_ctx.depth++; w->size = size; w->scale_120 = scale_120; - w->layer.contentsScale = scale; - w->layer.drawableSize = CGSizeMake(size.x, size.y); + if (w->layer) { + w->layer.contentsScale = scale; + w->layer.drawableSize = CGSizeMake(size.x, size.y); + } + if (w->gl_context) + [w->gl_context update]; /* Common configure notification already requests a frame before invoking * the legacy callback. Finish local bookkeeping before that callback can * destroy this surface. */ - yasl_clock_note_resize(&w->clock, yasl_loop_now_ns() - start); - yasl_surface_notify_configure(&w->base, (struct yasl_geometry){ + yasl_private_clock_note_resize(&w->clock, yasl_private_loop_now_ns() - start); + yasl_private_surface_notify_configure(&w->base, (struct yasl_private_geometry){ .size = size, .scale_120 = scale_120, }); + if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU) + yasl_private_surface_notify_refresh(&w->base); if (!w->destroyed && !w->closed && w->view.inLiveResize) render_and_commit(w); darwin_ctx.depth--; @@ -272,11 +294,11 @@ static void sync_size(struct cocoa_surface *w, bool force) static unsigned cocoa_mods(NSEventModifierFlags m) { unsigned mods = 0; - if (m & NSEventModifierFlagShift) mods |= YASL_MOD_SHIFT; - if (m & NSEventModifierFlagOption) mods |= YASL_MOD_ALT; - if (m & NSEventModifierFlagControl) mods |= YASL_MOD_CTRL; - if (m & NSEventModifierFlagCommand) mods |= YASL_MOD_SUPER; - if (m & NSEventModifierFlagCapsLock) mods |= YASL_MOD_CAPS; + if (m & NSEventModifierFlagShift) mods |= YASL_PRIVATE_MOD_SHIFT; + if (m & NSEventModifierFlagOption) mods |= YASL_PRIVATE_MOD_ALT; + if (m & NSEventModifierFlagControl) mods |= YASL_PRIVATE_MOD_CTRL; + if (m & NSEventModifierFlagCommand) mods |= YASL_PRIVATE_MOD_SUPER; + if (m & NSEventModifierFlagCapsLock) mods |= YASL_PRIVATE_MOD_CAPS; return mods; } @@ -287,19 +309,19 @@ static uint32_t cocoa_key_label(NSString *characters) if (!characters.length) return 0; unichar first = [characters characterAtIndex:0]; if (characters.length == 1) - return yasl_key_label(first); + return yasl_private_key_label(first); if (characters.length == 2 && first >= 0xd800 && first <= 0xdbff) { unichar second = [characters characterAtIndex:1]; if (second >= 0xdc00 && second <= 0xdfff) - return yasl_key_label(0x10000 + ((first - 0xd800) << 10) + second - 0xdc00); + return yasl_private_key_label(0x10000 + ((first - 0xd800) << 10) + second - 0xdc00); } return 0; } -static struct yasl_key_event cocoa_key_event(NSEvent *ev, enum yasl_key_action action) +static struct yasl_private_key_event cocoa_key_event(NSEvent *ev, enum yasl_key_action action) { - struct yasl_key_event k = { - .key = yasl_key_from_cocoa(ev.keyCode), + struct yasl_private_key_event k = { + .key = yasl_private_key_from_cocoa(ev.keyCode), .action = action, .mods = cocoa_mods(ev.modifierFlags), .native_code = ev.keyCode, @@ -315,27 +337,15 @@ static struct yasl_key_event cocoa_key_event(NSEvent *ev, enum yasl_key_action a static void on_key_event(struct cocoa_surface *w, NSEvent *ev, enum yasl_key_action action) { w->mods = cocoa_mods(ev.modifierFlags); - struct yasl_key_event k = cocoa_key_event(ev, action); - struct yasl_event input_ev = { - .type = YASL_EVENT_KEY, + struct yasl_private_key_event k = cocoa_key_event(ev, action); + struct yasl_private_event input_ev = { + .type = YASL_PRIVATE_EVENT_KEY, .surface = &w->base, .key = k, }; input_queue_push(&w->input, &input_ev); } -static void on_text_input(struct cocoa_surface *w, const char *text, size_t len) -{ - if (len) { - struct yasl_event ev = { - .type = YASL_EVENT_TEXT, - .surface = &w->base, - .text = { .data = text, .len = len }, - }; - input_queue_push(&w->input, &ev); - } -} - static bool modifier_down(NSEvent *ev) { NSEventModifierFlags f = ev.modifierFlags; @@ -356,22 +366,22 @@ static bool modifier_down(NSEvent *ev) static int cocoa_button(NSInteger number) { switch (number) { - case 0: return YASL_BUTTON_PRIMARY; - case 1: return YASL_BUTTON_SECONDARY; - case 2: return YASL_BUTTON_MIDDLE; - case 3: return YASL_BUTTON_BACK; - case 4: return YASL_BUTTON_FORWARD; + case 0: return YASL_PRIVATE_BUTTON_PRIMARY; + case 1: return YASL_PRIVATE_BUTTON_SECONDARY; + case 2: return YASL_PRIVATE_BUTTON_MIDDLE; + case 3: return YASL_PRIVATE_BUTTON_BACK; + case 4: return YASL_PRIVATE_BUTTON_FORWARD; default: return -1; } } -static struct yasl_vector2i event_pos(struct cocoa_surface *w, NSEvent *ev) +static struct yasl_private_vector2i event_pos(struct cocoa_surface *w, NSEvent *ev) { NSPoint p = [w->view convertPoint:ev.locationInWindow fromView:nil]; CGFloat scale = w->window.backingScaleFactor; if (scale < 1.0) scale = 1.0; - return (struct yasl_vector2i){ + return (struct yasl_private_vector2i){ .x = (int)floor(p.x * scale), .y = (int)floor((w->view.bounds.size.height - p.y) * scale), }; @@ -389,7 +399,7 @@ static void update_drag_hover(struct cocoa_display *d, NSPoint screenPoint) NSInteger winNum = [NSWindow windowNumberAtPoint:screenPoint belowWindowWithWindowNumber:excludeNum]; struct cocoa_surface *target = NULL; - struct yasl_vector2i pos = {0}; + struct yasl_private_vector2i pos = {0}; if (winNum > 0) { NSWindow *nswin = [NSApp windowWithWindowNumber:winNum]; @@ -429,11 +439,11 @@ static void update_drag_hover(struct cocoa_display *d, NSPoint screenPoint) if (target != d->drag.hover) { if (d->drag.hover) { struct cocoa_surface *old = d->drag.hover; - struct yasl_vector2i old_pos = d->drag.hover_pos; + struct yasl_private_vector2i old_pos = d->drag.hover_pos; d->drag.hover = NULL; if (!old->destroyed) { darwin_ctx.depth++; - yasl_surface_notify_drag(&old->base, YASL_DRAG_LEAVE, old_pos); + yasl_private_surface_notify_drag(&old->base, YASL_PRIVATE_DRAG_LEAVE, old_pos); darwin_ctx.depth--; } if (!d->drag.active) return; @@ -443,23 +453,117 @@ static void update_drag_hover(struct cocoa_display *d, NSPoint screenPoint) d->drag.hover_pos = pos; if (target && !target->destroyed) { darwin_ctx.depth++; - yasl_surface_notify_drag(&target->base, YASL_DRAG_OVER, pos); + yasl_private_surface_notify_drag(&target->base, YASL_PRIVATE_DRAG_OVER, pos); darwin_ctx.depth--; } } else if (target && !target->destroyed) { d->drag.hover_pos = pos; darwin_ctx.depth++; - yasl_surface_notify_drag(&target->base, YASL_DRAG_OVER, pos); + yasl_private_surface_notify_drag(&target->base, YASL_PRIVATE_DRAG_OVER, pos); darwin_ctx.depth--; } } -static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_event_type type, +static uint64_t event_integer(NSEvent *event, const char *name) +{ + SEL selector = sel_registerName(name); + if (![event respondsToSelector:selector]) return 0; + return (uint64_t)((NSInteger (*)(id, SEL))objc_msgSend)(event, selector); +} + +static double event_double(NSEvent *event, const char *name) +{ + SEL selector = sel_registerName(name); + if (![event respondsToSelector:selector]) return 0; + return ((double (*)(id, SEL))objc_msgSend)(event, selector); +} + +static void push_pen(struct cocoa_surface *w, NSEvent *event, unsigned action, bool contact) +{ + if (!w || w->closed || w->tablet_device == YASL_PRIVATE_PTR_DEV_MOUSE) return; + struct yasl_private_pen_sample pen = { + .device_id = w->pen_device_id, + .tool_id = w->pen_tool_id, + .x = w->pointer.x, + .y = w->pointer.y, + .tool = w->tablet_device == YASL_PRIVATE_PTR_DEV_ERASER ? 2u : 1u, + .action = action, + .in_contact = contact, + }; + if (event) { + if (w->view && [event respondsToSelector:@selector(locationInWindow)]) { + NSPoint point = [w->view convertPoint:event.locationInWindow fromView:nil]; + CGFloat scale = w->window.backingScaleFactor; + if (scale < 1.0) scale = 1.0; + pen.x = point.x * scale; + pen.y = (w->view.bounds.size.height - point.y) * scale; + } + if (event.buttonMask & NSEventButtonMaskPenTip) + pen.buttons |= YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY); + if (event.buttonMask & NSEventButtonMaskPenLowerSide) + pen.buttons |= YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_SECONDARY); + if (event.buttonMask & NSEventButtonMaskPenUpperSide) + pen.buttons |= YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_MIDDLE); + if (event.type == NSEventTypeTabletPoint || event.subtype == NSEventSubtypeTabletPoint) { + pen.pressure = fmax(0.0, fmin(1.0, event.pressure)); + pen.axes_valid |= YASL_PRIVATE_PEN_PRESSURE; + pen.axes_changed |= YASL_PRIVATE_PEN_PRESSURE; + NSPoint tilt = event.tilt; + pen.tilt_x = fmax(-90.0, fmin(90.0, tilt.x * 90.0)); + pen.tilt_y = fmax(-90.0, fmin(90.0, -tilt.y * 90.0)); + pen.axes_valid |= YASL_PRIVATE_PEN_TILT; + pen.axes_changed |= YASL_PRIVATE_PEN_TILT; + if ([event respondsToSelector:@selector(rotation)]) { + pen.rotation = event_double(event, "rotation"); + pen.axes_valid |= YASL_PRIVATE_PEN_ROTATION; + pen.axes_changed |= YASL_PRIVATE_PEN_ROTATION; + } + if ([event respondsToSelector:@selector(tangentialPressure)]) { + pen.tangential = event_double(event, "tangentialPressure"); + pen.axes_valid |= YASL_PRIVATE_PEN_TANGENTIAL; + pen.axes_changed |= YASL_PRIVATE_PEN_TANGENTIAL; + } + } + uint64_t device = event_integer(event, "pointingDeviceID"); + uint64_t tool = event_integer(event, "pointingDeviceSerialNumber"); + if (device) pen.device_id = device; + if (tool) pen.tool_id = tool; + } + if (action == YASL_PRIVATE_PEN_DOWN) { + pen.buttons |= YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY); + pen.changed_button = YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY); + } else if (action == YASL_PRIVATE_PEN_UP) { + pen.buttons &= ~YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY); + pen.changed_button = YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY); + } + if (action == YASL_PRIVATE_PEN_LEAVE || action == YASL_PRIVATE_PEN_CANCEL) { + pen.buttons = 0; + pen.in_contact = false; + pen.pressure = 0; + w->pen_buttons = 0; + } else { + uint32_t changed = pen.buttons ^ w->pen_buttons; + if (action == YASL_PRIVATE_PEN_MOVE && changed) { + action = YASL_PRIVATE_PEN_BUTTON; + pen.changed_button = changed & (~changed + 1u); + } + w->pen_buttons = pen.buttons; + } + pen.action = action; + struct yasl_private_event ev = { + .type = YASL_PRIVATE_EVENT_PEN, + .surface = &w->base, + .pen = pen, + }; + input_queue_push(&w->input, &ev); +} + +static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_private_event_type type, int button) { if (!w || w->closed) return; - if ((type == YASL_EVENT_POINTER_DOWN || type == YASL_EVENT_POINTER_UP) && + if ((type == YASL_PRIVATE_EVENT_POINTER_DOWN || type == YASL_PRIVATE_EVENT_POINTER_UP) && (button < 0 || button > 4)) return; bool proximity = ev.type == NSEventTypeTabletProximity || @@ -467,26 +571,30 @@ static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_event_t if (proximity) { if (ev.isEnteringProximity) { w->tablet_device = ev.pointingDeviceType == NSPointingDeviceTypeEraser ? - YASL_PTR_DEV_ERASER : ev.pointingDeviceType == NSPointingDeviceTypePen ? - YASL_PTR_DEV_PEN : YASL_PTR_DEV_MOUSE; + YASL_PRIVATE_PTR_DEV_ERASER : ev.pointingDeviceType == NSPointingDeviceTypePen ? + YASL_PRIVATE_PTR_DEV_PEN : YASL_PRIVATE_PTR_DEV_MOUSE; + w->pen_device_id = event_integer(ev, "pointingDeviceID"); + w->pen_tool_id = event_integer(ev, "pointingDeviceSerialNumber"); + if (w->tablet_device != YASL_PRIVATE_PTR_DEV_MOUSE) + push_pen(w, ev, YASL_PRIVATE_PEN_ENTER, false); return; } - type = YASL_EVENT_POINTER_LEAVE; + type = YASL_PRIVATE_EVENT_POINTER_LEAVE; } w->mods = cocoa_mods(ev.modifierFlags); - if (type != YASL_EVENT_POINTER_LEAVE) + if (type != YASL_PRIVATE_EVENT_POINTER_LEAVE) w->pointer = event_pos(w, ev); - enum yasl_pointer_device device = YASL_PTR_DEV_MOUSE; - struct yasl_pointer_pen pen = {0}; + enum yasl_private_pointer_device device = YASL_PRIVATE_PTR_DEV_MOUSE; + struct yasl_private_pointer_pen pen = {0}; - if (type == YASL_EVENT_POINTER_LEAVE && w->tablet_device != YASL_PTR_DEV_MOUSE) { + if (type == YASL_PRIVATE_EVENT_POINTER_LEAVE && w->tablet_device != YASL_PRIVATE_PTR_DEV_MOUSE) { device = w->tablet_device; w->tablet_down = false; } else if (ev.type == NSEventTypeTabletPoint || ev.subtype == NSEventSubtypeTabletPoint) { /* pointingDeviceType belongs to proximity events. Remember that * event's tool so erasers and zero-pressure hover stay classified. */ - device = w->tablet_device == YASL_PTR_DEV_ERASER ? YASL_PTR_DEV_ERASER : YASL_PTR_DEV_PEN; + device = w->tablet_device == YASL_PRIVATE_PTR_DEV_ERASER ? YASL_PRIVATE_PTR_DEV_ERASER : YASL_PRIVATE_PTR_DEV_PEN; float press = (float)ev.pressure; if (press > 1.0f) press = 1.0f; if (press > 0.0f) @@ -495,32 +603,40 @@ static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_event_t NSPoint tilt = ev.tilt; pen.tilt_x = (int8_t)lround(fmax(-1, fmin(1, tilt.x)) * 90.0); pen.tilt_y = (int8_t)lround(fmax(-1, fmin(1, -tilt.y)) * 90.0); - if (button == 0 && type == YASL_EVENT_POINTER_DOWN) w->tablet_down = true; - if (button == 0 && type == YASL_EVENT_POINTER_UP) { + if (button == 0 && type == YASL_PRIVATE_EVENT_POINTER_DOWN) w->tablet_down = true; + if (button == 0 && type == YASL_PRIVATE_EVENT_POINTER_UP) { w->tablet_down = false; pen.pressure = 0; } } - if (type == YASL_EVENT_POINTER_DOWN && button >= 0 && button < 32) + if (type == YASL_PRIVATE_EVENT_POINTER_DOWN && button >= 0 && button < 32) w->buttons |= (1u << button); - else if (type == YASL_EVENT_POINTER_UP && button >= 0 && button < 32) + else if (type == YASL_PRIVATE_EVENT_POINTER_UP && button >= 0 && button < 32) w->buttons &= ~(1u << button); - struct yasl_event input_ev = { + struct yasl_private_event input_ev = { .type = type, .surface = &w->base, .pointer = { .pos = w->pointer, - .button = (type == YASL_EVENT_POINTER_DOWN || type == YASL_EVENT_POINTER_UP) ? - (enum yasl_pointer_button)button : YASL_BUTTON_PRIMARY, + .button = (type == YASL_PRIVATE_EVENT_POINTER_DOWN || type == YASL_PRIVATE_EVENT_POINTER_UP) ? + (enum yasl_private_pointer_button)button : YASL_PRIVATE_BUTTON_PRIMARY, .device = device, .mods = w->mods, .pen = pen, }, }; input_queue_push(&w->input, &input_ev); - if (type == YASL_EVENT_POINTER_MOTION && darwin_ctx.drag.active) { + if (type == YASL_PRIVATE_EVENT_POINTER_LEAVE && device != YASL_PRIVATE_PTR_DEV_MOUSE) { + push_pen(w, ev, YASL_PRIVATE_PEN_LEAVE, false); + } else if (ev.type == NSEventTypeTabletPoint || ev.subtype == NSEventSubtypeTabletPoint) { + unsigned action = button == YASL_PRIVATE_BUTTON_PRIMARY && type == YASL_PRIVATE_EVENT_POINTER_DOWN ? YASL_PRIVATE_PEN_DOWN : + button == YASL_PRIVATE_BUTTON_PRIMARY && type == YASL_PRIVATE_EVENT_POINTER_UP ? YASL_PRIVATE_PEN_UP : + YASL_PRIVATE_PEN_MOVE; + push_pen(w, ev, action, w->tablet_down); + } + if (type == YASL_PRIVATE_EVENT_POINTER_MOTION && darwin_ctx.drag.active) { if (darwin_ctx.drag.carried) { cocoa_surface_place_under_pointer(darwin_ctx.drag.carried, darwin_ctx.drag.carry_offset); } else if (darwin_ctx.drag.image_window) { @@ -534,23 +650,24 @@ static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_event_t mouse.y - pt_h + darwin_ctx.drag.image_hotspot.y / scale)]; } update_drag_hover(&darwin_ctx, [NSEvent mouseLocation]); - } else if (type == YASL_EVENT_POINTER_UP && darwin_ctx.drag.active) { + } else if (type == YASL_PRIVATE_EVENT_POINTER_UP && darwin_ctx.drag.active) { update_drag_hover(&darwin_ctx, [NSEvent mouseLocation]); - drag_end(&darwin_ctx, YASL_DRAG_DROPPED); + drag_end(&darwin_ctx, YASL_PRIVATE_DRAG_DROPPED); } if (proximity) { - w->tablet_device = YASL_PTR_DEV_MOUSE; + w->tablet_device = YASL_PRIVATE_PTR_DEV_MOUSE; w->tablet_down = false; + w->pen_device_id = w->pen_tool_id = 0; } } -static NSCursor *cocoa_cursor(enum yasl_cursor cursor) +static NSCursor *cocoa_cursor(enum yasl_private_cursor cursor) { switch (cursor) { - case YASL_CURSOR_TEXT: return [NSCursor IBeamCursor]; - case YASL_CURSOR_LINK: return [NSCursor pointingHandCursor]; - case YASL_CURSOR_CROSSHAIR: return [NSCursor crosshairCursor]; - case YASL_CURSOR_HIDDEN: + case YASL_PRIVATE_CURSOR_TEXT: return [NSCursor IBeamCursor]; + case YASL_PRIVATE_CURSOR_LINK: return [NSCursor pointingHandCursor]; + case YASL_PRIVATE_CURSOR_CROSSHAIR: return [NSCursor crosshairCursor]; + case YASL_PRIVATE_CURSOR_HIDDEN: if (!darwin_ctx.hidden_cursor) { NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(1, 1)]; [image lockFocus]; @@ -561,7 +678,7 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) [image release]; } return darwin_ctx.hidden_cursor; - case YASL_CURSOR_ARROW: + case YASL_PRIVATE_CURSOR_ARROW: default: return [NSCursor arrowCursor]; } } @@ -570,14 +687,14 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { (void)sender; - yasl_display_notify_event(&darwin_ctx.base, YASL_EVENT_DISPLAY_QUIT); + yasl_private_display_notify_event(&darwin_ctx.base, YASL_PRIVATE_EVENT_DISPLAY_QUIT); return NSTerminateCancel; } - (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)visible { (void)sender; if (!visible) - yasl_display_notify_event(&darwin_ctx.base, YASL_EVENT_DISPLAY_REOPEN); + yasl_private_display_notify_event(&darwin_ctx.base, YASL_PRIVATE_EVENT_DISPLAY_REOPEN); return YES; } - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender @@ -587,13 +704,13 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) } @end -static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> sender) +static struct yasl_private_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> sender) { NSPoint p = [view convertPoint:[sender draggingLocation] fromView:nil]; NSWindow *win = view.window; CGFloat scale = win ? win.backingScaleFactor : 1.0; if (scale < 1.0) scale = 1.0; - return (struct yasl_vector2i){ + return (struct yasl_private_vector2i){ .x = (int)floor(p.x * scale), .y = (int)floor((view.bounds.size.height - p.y) * scale), }; @@ -619,9 +736,9 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send if ([pb canReadItemWithDataConformingToTypes:@[NSPasteboardTypeFileURL]]) { struct cocoa_surface *w = self.surface; if (w && !w->closed) { - struct yasl_vector2i pos = dragging_pos(self, sender); + struct yasl_private_vector2i pos = dragging_pos(self, sender); darwin_ctx.depth++; - yasl_surface_notify_drop(&w->base, YASL_DROP_OVER, pos); + yasl_private_surface_notify_drop(&w->base, YASL_PRIVATE_DROP_OVER, pos); darwin_ctx.depth--; } return NSDragOperationCopy; @@ -640,9 +757,9 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send if ([pb canReadItemWithDataConformingToTypes:@[NSPasteboardTypeFileURL]]) { struct cocoa_surface *w = self.surface; if (w && !w->closed) { - struct yasl_vector2i pos = dragging_pos(self, sender); + struct yasl_private_vector2i pos = dragging_pos(self, sender); darwin_ctx.depth++; - yasl_surface_notify_drop(&w->base, YASL_DROP_LEAVE, pos); + yasl_private_surface_notify_drop(&w->base, YASL_PRIVATE_DROP_LEAVE, pos); darwin_ctx.depth--; } } @@ -652,7 +769,7 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send { struct cocoa_surface *w = self.surface; if (!w) return NO; - struct yasl_vector2i pos = dragging_pos(self, sender); + struct yasl_private_vector2i pos = dragging_pos(self, sender); BOOL ok = NO; NSPasteboard *pb = [sender draggingPasteboard]; @@ -666,8 +783,8 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send } const char *utf8 = result.UTF8String; if (utf8 && strlen(utf8)) { - struct yasl_event ev = { - .type = YASL_EVENT_DROP, + struct yasl_private_event ev = { + .type = YASL_PRIVATE_EVENT_DROP, .surface = &w->base, .text = { .data = utf8, .len = strlen(utf8) }, }; @@ -678,7 +795,7 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send } if (!w->destroyed) { darwin_ctx.depth++; - yasl_surface_notify_drop(&w->base, YASL_DROP_LEAVE, pos); + yasl_private_surface_notify_drop(&w->base, YASL_PRIVATE_DROP_LEAVE, pos); darwin_ctx.depth--; } return ok; @@ -704,6 +821,13 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send - (BOOL)canBecomeKeyView { return YES; } - (BOOL)wantsUpdateLayer { return YES; } +- (void)updateLayer +{ + struct cocoa_surface *w = self.surface; + if (w && !w->closed && w->base.presenter != YASL_PRIVATE_PRESENTER_CPU) + yasl_private_surface_notify_refresh(&w->base); +} + - (void)setFrameSize:(NSSize)size { [super setFrameSize:size]; @@ -743,14 +867,14 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send if (period > 0.0 && period < 0.25) w->clock.refresh_ns = period * 1e9; w->frame_due = true; - if (!w->want_draw || (w->base.state & YASL_STATE_SUSPENDED)) { + if (!w->want_draw || (w->base.state & YASL_PRIVATE_STATE_SUSPENDED)) { if (++w->idle_frames >= LINK_IDLE_FRAMES) link.paused = YES; return; } w->idle_frames = 0; if (darwin_ctx.loop) - yasl_loop_kick(darwin_ctx.loop); + yasl_private_loop_kick(darwin_ctx.loop); } - (void)updateTrackingAreas @@ -773,7 +897,7 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send { (void)event; struct cocoa_surface *w = self.surface; - [cocoa_cursor(w ? w->cursor : YASL_CURSOR_ARROW) set]; + [cocoa_cursor(w ? w->cursor : YASL_PRIVATE_CURSOR_ARROW) set]; } - (void)keyDown:(NSEvent *)event @@ -802,44 +926,44 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send - (void)mouseDown:(NSEvent *)event { [self.window makeFirstResponder:self]; - push_pointer(self.surface, event, YASL_EVENT_POINTER_DOWN, cocoa_button(event.buttonNumber)); + push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_DOWN, cocoa_button(event.buttonNumber)); } - (void)rightMouseDown:(NSEvent *)event { [self mouseDown:event]; } - (void)otherMouseDown:(NSEvent *)event { [self mouseDown:event]; } - (void)mouseUp:(NSEvent *)event { - push_pointer(self.surface, event, YASL_EVENT_POINTER_UP, cocoa_button(event.buttonNumber)); + push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_UP, cocoa_button(event.buttonNumber)); } - (void)rightMouseUp:(NSEvent *)event { [self mouseUp:event]; } - (void)otherMouseUp:(NSEvent *)event { [self mouseUp:event]; } - (void)mouseMoved:(NSEvent *)event { - push_pointer(self.surface, event, YASL_EVENT_POINTER_MOTION, 0); + push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_MOTION, 0); } - (void)mouseDragged:(NSEvent *)event { - push_pointer(self.surface, event, YASL_EVENT_POINTER_MOTION, 0); + push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_MOTION, 0); } - (void)rightMouseDragged:(NSEvent *)event { - push_pointer(self.surface, event, YASL_EVENT_POINTER_MOTION, 0); + push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_MOTION, 0); } - (void)otherMouseDragged:(NSEvent *)event { - push_pointer(self.surface, event, YASL_EVENT_POINTER_MOTION, 0); + push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_MOTION, 0); } - (void)mouseExited:(NSEvent *)event { - push_pointer(self.surface, event, YASL_EVENT_POINTER_LEAVE, 0); + push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_LEAVE, 0); } - (void)tabletProximity:(NSEvent *)event { - push_pointer(self.surface, event, YASL_EVENT_POINTER_MOTION, 0); + push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_MOTION, 0); } - (void)tabletPoint:(NSEvent *)event @@ -847,8 +971,8 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send struct cocoa_surface *w = self.surface; if (!w) return; bool down = (event.buttonMask & NSEventButtonMaskPenTip) != 0; - enum yasl_event_type type = down == w->tablet_down ? YASL_EVENT_POINTER_MOTION : - down ? YASL_EVENT_POINTER_DOWN : YASL_EVENT_POINTER_UP; + enum yasl_private_event_type type = down == w->tablet_down ? YASL_PRIVATE_EVENT_POINTER_MOTION : + down ? YASL_PRIVATE_EVENT_POINTER_DOWN : YASL_PRIVATE_EVENT_POINTER_UP; push_pointer(w, event, type, 0); } @@ -864,8 +988,8 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send if (!isfinite(dx) || !isfinite(dy) || (dx == 0 && dy == 0)) return; w->mods = cocoa_mods(event.modifierFlags); w->pointer = event_pos(w, event); - struct yasl_event input_ev = { - .type = YASL_EVENT_POINTER_SCROLL, + struct yasl_private_event input_ev = { + .type = YASL_PRIVATE_EVENT_POINTER_SCROLL, .surface = &w->base, .pointer = { .pos = w->pointer, @@ -882,34 +1006,131 @@ static NSString *plain_string(id string) ? [(NSAttributedString *)string string] : (NSString *)string; } +static bool text_utf16_to_utf8(NSString *text, NSUInteger index, size_t *byte_offset) +{ + if (!text || index > text.length) return false; + const char *prefix = [[text substringToIndex:index] UTF8String]; + if (!prefix) return false; + *byte_offset = strlen(prefix); + return true; +} + +static bool text_utf8_to_utf16(const char *text, size_t byte_offset, NSUInteger *index) +{ + size_t len = text ? strlen(text) : 0; + if (byte_offset > len) return false; + NSString *prefix = [[NSString alloc] initWithBytes:text length:byte_offset + encoding:NSUTF8StringEncoding]; + if (!prefix) return false; + *index = prefix.length; + [prefix release]; + return true; +} + +static bool text_edit_range(struct cocoa_surface *w, NSRange range, bool use_marked, + size_t *begin, size_t *end) +{ + if (range.location == NSNotFound) { + if (use_marked && w->preedit) { + if (w->preedit_revision != w->base.text_revision || + w->preedit_generation != w->base.text_generation) + return false; + *begin = w->preedit_begin; + *end = w->preedit_end; + } else { + *begin = w->base.text_begin; + *end = w->base.text_end; + } + return *begin <= *end && *end <= w->base.text_length; + } + NSString *surrounding = [NSString stringWithUTF8String:w->base.text_surrounding ?: ""]; + size_t a, b; + if (!surrounding || range.location > surrounding.length || + range.length > surrounding.length - range.location || + !text_utf16_to_utf8(surrounding, range.location, &a) || + !text_utf16_to_utf8(surrounding, range.location + range.length, &b)) + return false; + *begin = a; + *end = b; + return true; +} + +static void push_text_edit(struct cocoa_surface *w, const char *text, size_t len, + size_t begin, size_t end) +{ + if (!w->base.text_input_active || begin > end || end > w->base.text_length) return; + struct yasl_private_event ev = { + .type = YASL_PRIVATE_EVENT_TEXT_EDIT, + .surface = &w->base, + .text_edit = { + .data = text ?: "", .len = len, + .revision = w->base.text_revision, + .generation = w->base.text_generation, + .begin = begin, .end = end, + }, + }; + input_queue_push(&w->input, &ev); +} + - (void)insertText:(id)string replacementRange:(NSRange)replacementRange { - (void)replacementRange; struct cocoa_surface *w = self.surface; - if (!w || w->destroyed || w->closed) return; + if (!w || w->destroyed || w->closed || !w->base.text_input_active) return; const char *utf8 = plain_string(string).UTF8String; size_t len = utf8 ? strlen(utf8) : 0; + size_t begin, end; + if (!text_edit_range(w, replacementRange, true, &begin, &end)) return; bool had_preedit = w->preedit != NULL; + darwin_ctx.depth++; + push_text_edit(w, utf8, len, begin, end); free(w->preedit); w->preedit = NULL; - darwin_ctx.depth++; - on_text_input(w, utf8, len); + w->preedit_begin = w->preedit_end = 0; if (had_preedit) - yasl_surface_notify_composition(&w->base, NULL, -1, -1); + yasl_private_surface_notify_composition(&w->base, NULL, -1, -1); if (!w->destroyed && !w->closed) cocoa_surface_schedule_redraw(w); darwin_ctx.depth--; } -- (void)doCommandBySelector:(SEL)selector { (void)selector; } +- (void)doCommandBySelector:(SEL)selector +{ + struct cocoa_surface *w = self.surface; + if (!w || w->destroyed || w->closed || !w->base.text_input_active) return; + NSString *surrounding = [NSString stringWithUTF8String:w->base.text_surrounding ?: ""]; + NSUInteger begin, end; + if (!sel_isEqual(selector, @selector(deleteBackward)) && + !sel_isEqual(selector, @selector(deleteForward))) return; + if (!text_utf8_to_utf16(w->base.text_surrounding ?: "", w->base.text_begin, &begin) || + !text_utf8_to_utf16(w->base.text_surrounding ?: "", w->base.text_end, &end)) return; + if (begin == end && sel_isEqual(selector, @selector(deleteBackward)) && begin > 0) { + NSRange composed = [surrounding rangeOfComposedCharacterSequenceAtIndex:begin - 1]; + begin = composed.location; + } else if (begin == end && sel_isEqual(selector, @selector(deleteForward)) && end < surrounding.length) { + NSRange composed = [surrounding rangeOfComposedCharacterSequenceAtIndex:end]; + end = NSMaxRange(composed); + } + size_t byte_begin, byte_end; + if (text_utf16_to_utf8(surrounding, begin, &byte_begin) && + text_utf16_to_utf8(surrounding, end, &byte_end) && byte_begin != byte_end) + push_text_edit(w, "", 0, byte_begin, byte_end); +} - (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange { - (void)replacementRange; struct cocoa_surface *w = self.surface; - if (!w || w->destroyed || w->closed) return; + if (!w || w->destroyed || w->closed || !w->base.text_input_active) return; NSString *str = plain_string(string); + size_t range_begin, range_end; + if (!text_edit_range(w, replacementRange, true, &range_begin, &range_end)) return; + w->preedit_begin = range_begin; + w->preedit_end = range_end; + w->preedit_revision = w->base.text_revision; + w->preedit_generation = w->base.text_generation; + if (!text_utf8_to_utf16(w->base.text_surrounding ?: "", range_begin, + &w->marked_utf16_location)) + w->marked_utf16_location = 0; const char *utf8 = str.length ? str.UTF8String : NULL; free(w->preedit); w->preedit = utf8 ? strdup(utf8) : NULL; @@ -926,7 +1147,7 @@ static NSString *plain_string(id string) } } darwin_ctx.depth++; - yasl_surface_notify_composition(&w->base, w->preedit, begin, end); + yasl_private_surface_notify_composition(&w->base, w->preedit, begin, end); if (!w->destroyed && !w->closed) cocoa_surface_schedule_redraw(w); darwin_ctx.depth--; @@ -939,19 +1160,28 @@ static NSString *plain_string(id string) free(w->preedit); w->preedit = NULL; darwin_ctx.depth++; - yasl_surface_notify_composition(&w->base, NULL, -1, -1); + yasl_private_surface_notify_composition(&w->base, NULL, -1, -1); if (!w->destroyed && !w->closed) cocoa_surface_schedule_redraw(w); darwin_ctx.depth--; } -- (NSRange)selectedRange { return NSMakeRange(NSNotFound, 0); } +- (NSRange)selectedRange +{ + struct cocoa_surface *w = self.surface; + if (!w || !w->base.text_input_active) return NSMakeRange(NSNotFound, 0); + NSUInteger begin, end; + if (!text_utf8_to_utf16(w->base.text_surrounding ?: "", w->base.text_begin, &begin) || + !text_utf8_to_utf16(w->base.text_surrounding ?: "", w->base.text_end, &end)) + return NSMakeRange(NSNotFound, 0); + return NSMakeRange(begin, end - begin); +} - (NSRange)markedRange { struct cocoa_surface *w = self.surface; if (!w || !w->preedit) return NSMakeRange(NSNotFound, 0); - return NSMakeRange(0, [NSString stringWithUTF8String:w->preedit].length); + return NSMakeRange(w->marked_utf16_location, [NSString stringWithUTF8String:w->preedit].length); } - (BOOL)hasMarkedText { return self.surface && self.surface->preedit != NULL; } - (nullable NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range @@ -1012,7 +1242,7 @@ static NSString *plain_string(id string) if (!w) return; bool visible = (self.window.occlusionState & NSWindowOcclusionStateVisible) != 0; darwin_ctx.depth++; - set_state(w, YASL_STATE_SUSPENDED, !visible); + set_state(w, YASL_PRIVATE_STATE_SUSPENDED, !visible); if (!w->destroyed && !w->closed) { if (visible) cocoa_surface_schedule_redraw(w); @@ -1042,8 +1272,8 @@ static NSString *plain_string(id string) return; darwin_ctx.depth++; bool fullscreen = (self.window.styleMask & NSWindowStyleMaskFullScreen) != 0; - set_state(w, YASL_STATE_FULLSCREEN, fullscreen); - set_state(w, YASL_STATE_MAXIMIZED, !fullscreen && self.window.isZoomed); + set_state(w, YASL_PRIVATE_STATE_FULLSCREEN, fullscreen); + set_state(w, YASL_PRIVATE_STATE_MAXIMIZED, !fullscreen && self.window.isZoomed); sync_size(w, false); darwin_ctx.depth--; } @@ -1075,16 +1305,16 @@ static NSString *plain_string(id string) darwin_ctx.depth++; w->closed = true; [w->link invalidate]; - yasl_surface_notify_close(&w->base); + yasl_private_surface_notify_close(&w->base); darwin_ctx.depth--; } @end -bool cocoa_surface_present(struct cocoa_surface *w, const struct yasl_framebuffer *fb, - const struct yasl_damage_list *damage) +bool cocoa_surface_present(struct cocoa_surface *w, const struct yasl_private_framebuffer *fb, + const struct yasl_private_damage_list *damage) { - if (!w || w->closed || !w->rendering || w->did_present || !fb || !damage || + if (!w || w->base.presenter != YASL_PRIVATE_PRESENTER_CPU || w->closed || !w->rendering || w->did_present || !fb || !damage || fb->pixels != w->frame.target.pixels || fb->size.x != w->size.x || fb->size.y != w->size.y || !w->drawable) return false; @@ -1116,7 +1346,7 @@ bool cocoa_surface_present(struct cocoa_surface *w, const struct yasl_framebuffe [command presentDrawable:drawable]; [command commit]; } - yasl_clock_presented(&w->clock, yasl_loop_now_ns(), false); + yasl_private_clock_presented(&w->clock, yasl_private_loop_now_ns(), false); w->did_present = true; w->shown_size = w->size; return true; @@ -1124,8 +1354,8 @@ bool cocoa_surface_present(struct cocoa_surface *w, const struct yasl_framebuffe static void render_and_commit(struct cocoa_surface *w) { - if (w->destroyed || w->closed || !w->cbs->draw || w->rendering || w->size.x <= 0 || w->size.y <= 0 || - (w->base.state & YASL_STATE_SUSPENDED)) + if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU || w->destroyed || w->closed || !w->cbs->draw || w->rendering || w->size.x <= 0 || w->size.y <= 0 || + (w->base.state & YASL_PRIVATE_STATE_SUSPENDED)) return; size_t count = (size_t)w->size.x * (size_t)w->size.y; @@ -1143,17 +1373,17 @@ static void render_and_commit(struct cocoa_surface *w) id<CAMetalDrawable> drawable = [w->layer nextDrawable]; w->drawable = drawable; w->transaction = w->layer.presentsWithTransaction; - w->damage = (struct yasl_damage_list){0}; - w->frame = (struct yasl_frame){ + w->damage = (struct yasl_private_damage_list){0}; + w->frame = (struct yasl_private_frame){ .surface = &w->base, .target = { - .generation = yasl_frame_begin(&w->base), + .generation = yasl_private_frame_begin(&w->base), .pixels = w->software_queue ? w->pixels : NULL, .size = w->size, }, }; if (drawable) - yasl_surface_draw(&w->base, &w->frame); + yasl_private_surface_draw(&w->base, &w->frame); if (!w->destroyed && !w->closed && !w->did_present) { w->want_draw = true; w->frames_skipped++; @@ -1191,7 +1421,7 @@ static void loop_ready(CFFileDescriptorRef fd, CFOptionFlags flags, void *info) /* A kqueue mirroring the loop's poll set, or -1. complete is false when some * descriptor could not be registered. */ -static int loop_kqueue(struct yasl_loop *loop, bool *complete) +static int loop_kqueue(struct yasl_private_loop *loop, bool *complete) { *complete = true; int kq = kqueue(); @@ -1200,7 +1430,7 @@ static int loop_kqueue(struct yasl_loop *loop, bool *complete) return -1; } const struct pollfd *fds; - int n = yasl_loop_poll_set(loop, &fds); + int n = yasl_private_loop_poll_set(loop, &fds); for (int i = 0; i < n; i++) { struct kevent change; if (fds[i].events & POLLIN) { @@ -1238,7 +1468,7 @@ int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms) int count = 0; @try { if (d->loop) { - int timer_to = yasl_loop_next_timeout_ms(d->loop); + int timer_to = yasl_private_loop_next_timeout_ms(d->loop); if (timer_to >= 0 && (timeout_ms < 0 || timer_to < timeout_ms)) { timeout_ms = timer_to; } @@ -1303,7 +1533,7 @@ int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms) } if (d->loop) { - yasl_loop_step(d->loop, 0); + yasl_private_loop_step(d->loop, 0); } } @finally { d->depth--; @@ -1331,6 +1561,10 @@ void cocoa_display_flush(struct cocoa_display *d) void cocoa_surface_schedule_redraw(struct cocoa_surface *w) { if (!w || w->closed) return; + if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU) { + yasl_private_surface_notify_refresh(&w->base); + return; + } w->want_draw = true; w->idle_frames = 0; if (w->link.paused) { @@ -1338,7 +1572,7 @@ void cocoa_surface_schedule_redraw(struct cocoa_surface *w) w->link.paused = NO; } if (darwin_ctx.loop) - yasl_loop_kick(darwin_ctx.loop); + yasl_private_loop_kick(darwin_ctx.loop); } static void app_init(const char *app_name) @@ -1390,17 +1624,17 @@ static void place_window(NSWindow *window) darwin_ctx.cascade = [window cascadeTopLeftFromPoint:darwin_ctx.cascade]; } -struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec) +struct cocoa_display *cocoa_display_open(struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec) { assert([NSThread isMainThread]); if (darwin_ctx.open) return NULL; const char *name = spec && spec->app_id ? spec->app_id : "YASL"; @autoreleasepool { app_init(name); } - darwin_ctx.base.ops = &yasl_cocoa_backend; + darwin_ctx.base.ops = &yasl_private_cocoa_backend; darwin_ctx.base.native_handle = (__bridge void *)[NSApplication sharedApplication]; darwin_ctx.open = true; if (!loop) { - darwin_ctx.loop = yasl_loop_create(); + darwin_ctx.loop = yasl_private_loop_create(); darwin_ctx.owns_loop = true; } else { darwin_ctx.loop = loop; @@ -1408,7 +1642,7 @@ struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct ya } darwin_ctx.base.loop = darwin_ctx.loop; if (spec) { - darwin_ctx.base.callbacks = spec->cbs ? *spec->cbs : (struct yasl_display_cbs){0}; + darwin_ctx.base.callbacks = spec->cbs ? *spec->cbs : (struct yasl_private_display_cbs){0}; darwin_ctx.cbs = &darwin_ctx.base.callbacks; darwin_ctx.ud = spec->ud; darwin_ctx.base.callback_ud = spec->ud; @@ -1421,7 +1655,7 @@ struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct ya void cocoa_display_close(struct cocoa_display *d) { if (!d) return; - drag_end(d, YASL_DRAG_CANCELLED); + drag_end(d, YASL_PRIVATE_DRAG_CANCELLED); assert(!d->count && "destroy every surface before its display"); reap_dead(d); if (d->key_up_monitor) { @@ -1429,7 +1663,7 @@ void cocoa_display_close(struct cocoa_display *d) d->key_up_monitor = nil; } if (d->owns_loop && d->loop) { - yasl_loop_destroy(d->loop); + yasl_private_loop_destroy(d->loop); d->loop = NULL; d->base.loop = NULL; } @@ -1444,14 +1678,15 @@ void cocoa_display_close(struct cocoa_display *d) uint32_t cocoa_display_caps(const struct cocoa_display *d) { - return d ? (YASL_CAP_POPUPS | YASL_CAP_DRAG | YASL_CAP_DRAG_CARRY | - YASL_CAP_PLACE_WINDOW | YASL_CAP_PRIMARY_SELECTION | - YASL_CAP_CSD | YASL_CAP_NATIVE_GRAPHICS | - YASL_CAP_FRAME_PACING | YASL_CAP_TABLET) : 0; + return d ? (YASL_PRIVATE_CAP_POPUPS | YASL_PRIVATE_CAP_DRAG | YASL_PRIVATE_CAP_DRAG_CARRY | + YASL_PRIVATE_CAP_PLACE_WINDOW | YASL_PRIVATE_CAP_PRIMARY_SELECTION | + YASL_PRIVATE_CAP_CSD | YASL_PRIVATE_CAP_NATIVE_GRAPHICS | + YASL_PRIVATE_CAP_FRAME_PACING | YASL_PRIVATE_CAP_TABLET | + YASL_PRIVATE_CAP_TEXT_INPUT) : 0; } struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d, - const struct yasl_window_spec *spec, void *graphics_device) + const struct yasl_private_window_spec *spec, void *graphics_device) { assert([NSThread isMainThread]); if (!d || !d->open || !spec) @@ -1468,15 +1703,24 @@ struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d, struct cocoa_surface *w = calloc(1, sizeof(*w)); if (!w) return NULL; w->base.display = &d->base; + w->base.creation_flags = spec->flags; + bool external_gpu = spec->external_gpu || graphics_device != NULL; + /* Leave an external-GPU surface unclaimed until the public bridge + * selects Metal or Vulkan. Both bridges request the layer while the + * presenter is still NONE. */ + w->base.presenter = external_gpu ? YASL_PRIVATE_PRESENTER_NONE : YASL_PRIVATE_PRESENTER_CPU; cocoa_surface_set_callbacks(w, NULL, NULL); - w->clock.refresh_ns = YASL_REFRESH_DEFAULT_NS; + w->clock.refresh_ns = YASL_PRIVATE_REFRESH_DEFAULT_NS; const char *t = spec->title ? spec->title : "YASL"; strncpy(w->title, t, sizeof(w->title) - 1); int win_w = spec->size.x > 0 ? spec->size.x : 800; int win_h = spec->size.y > 0 ? spec->size.y : 480; - NSWindowStyleMask style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | - NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable; + NSWindowStyleMask style = (spec->flags & YASL_PRIVATE_SURFACE_UNDECORATED) ? + NSWindowStyleMaskBorderless : NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable; + if (!(spec->flags & (YASL_PRIVATE_SURFACE_UNDECORATED | YASL_PRIVATE_SURFACE_FIXED_SIZE))) + style |= NSWindowStyleMaskResizable; w->window = [[YaslWindow alloc] initWithContentRect:NSMakeRect(0, 0, win_w, win_h) styleMask:style backing:NSBackingStoreBuffered @@ -1499,7 +1743,7 @@ struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d, w->layer = (CAMetalLayer *)w->view.layer; if (graphics_device) { w->layer.device = (id<MTLDevice>)graphics_device; - } else { + } else if (!external_gpu) { id<MTLDevice> device = MTLCreateSystemDefaultDevice(); w->layer.device = device; w->software_queue = [device newCommandQueue]; @@ -1526,13 +1770,14 @@ struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d, w->view.surface = w; darwin_ctx.windows[darwin_ctx.count++] = w; - [w->window makeKeyAndOrderFront:nil]; - [w->window makeFirstResponder:w->view]; - [NSApp activate]; + if (!(spec->flags & YASL_PRIVATE_SURFACE_HIDDEN)) { + [w->window makeKeyAndOrderFront:nil]; + [w->window makeFirstResponder:w->view]; + } sync_size(w, true); if (spec->tearing) { - w->layer.displaySyncEnabled = NO; + if (w->layer) w->layer.displaySyncEnabled = NO; w->unpaced = true; } if (spec->fullscreen) @@ -1544,6 +1789,7 @@ struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d, static void surface_free(struct cocoa_surface *w) { [w->link release]; + [w->gl_context release]; [w->software_buffer release]; [w->software_queue release]; [w->view release]; @@ -1570,7 +1816,7 @@ void cocoa_surface_destroy(struct cocoa_surface *w) if (!w || w->destroyed) return; if (darwin_ctx.drag.active) { if (darwin_ctx.drag.origin == w || darwin_ctx.drag.carried == w) - drag_end(&darwin_ctx, YASL_DRAG_CANCELLED); + drag_end(&darwin_ctx, YASL_PRIVATE_DRAG_CANCELLED); else if (darwin_ctx.drag.hover == w) darwin_ctx.drag.hover = NULL; } @@ -1589,7 +1835,7 @@ void cocoa_surface_destroy(struct cocoa_surface *w) w->tablet_down = false; darwin_ctx.depth++; bool native_closed = w->closed; - static const struct yasl_window_cbs no_callbacks = {0}; + static const struct yasl_private_window_cbs no_callbacks = {0}; w->destroyed = true; w->closed = true; w->cbs = &no_callbacks; @@ -1619,7 +1865,7 @@ void cocoa_surface_destroy(struct cocoa_surface *w) } darwin_ctx.depth--; if (darwin_ctx.loop) - yasl_loop_kick(darwin_ctx.loop); + yasl_private_loop_kick(darwin_ctx.loop); } void cocoa_surface_set_title(struct cocoa_surface *w, const char *title) @@ -1629,7 +1875,7 @@ void cocoa_surface_set_title(struct cocoa_surface *w, const char *title) w->window.title = [NSString stringWithUTF8String:w->title] ?: @"YASL"; } -static bool impl_surface_set_size(struct yasl_surface *surface, struct yasl_vector2i size) +static bool impl_surface_set_size(struct yasl_surface *surface, struct yasl_private_vector2i size) { struct cocoa_surface *w = (struct cocoa_surface *)surface; if (!w || w->closed || w->is_popup || size.x <= 0 || size.y <= 0) { @@ -1693,16 +1939,16 @@ void cocoa_surface_set_background_color(struct cocoa_surface *w, uint32_t bg) dark ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua]; } -void cocoa_surface_set_cursor(struct cocoa_surface *w, enum yasl_cursor cursor) +void cocoa_surface_set_cursor(struct cocoa_surface *w, enum yasl_private_cursor cursor) { - if (!w || w->closed || cursor == w->cursor || (unsigned)cursor > YASL_CURSOR_HIDDEN) return; + if (!w || w->closed || cursor == w->cursor || (unsigned)cursor > YASL_PRIVATE_CURSOR_HIDDEN) return; w->cursor = cursor; NSPoint p = [w->view convertPoint:w->window.mouseLocationOutsideOfEventStream fromView:nil]; if (w->window.isKeyWindow && NSPointInRect(p, w->view.bounds)) [cocoa_cursor(cursor) set]; } -bool cocoa_surface_place_under_pointer(struct cocoa_surface *w, struct yasl_vector2i at) +bool cocoa_surface_place_under_pointer(struct cocoa_surface *w, struct yasl_private_vector2i at) { if (!w || w->closed || !w->window) return false; CGFloat scale = w->window.backingScaleFactor; @@ -1716,7 +1962,7 @@ bool cocoa_surface_place_under_pointer(struct cocoa_surface *w, struct yasl_vect return true; } -void cocoa_surface_set_ime_caret(struct cocoa_surface *w, struct yasl_rect2i caret) +void cocoa_surface_set_ime_caret(struct cocoa_surface *w, struct yasl_private_rect2i caret) { if (w) w->ime_caret = caret; } @@ -1727,26 +1973,26 @@ bool cocoa_surface_ime_preedit_cursor(const struct cocoa_surface *w, int *begin, return false; } -static NSPasteboard *pasteboard_for_selection(enum yasl_selection sel) +static NSPasteboard *pasteboard_for_selection(enum yasl_private_selection sel) { switch (sel) { - case YASL_CLIPBOARD: + case YASL_PRIVATE_CLIPBOARD: return [NSPasteboard generalPasteboard]; - case YASL_PRIMARY: + case YASL_PRIVATE_PRIMARY: return [NSPasteboard pasteboardWithName:@"dev.wsap.yasl.primary"]; default: return nil; } } -static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_selection sel, const char *text); +static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_private_selection sel, const char *text); -void cocoa_display_copy(struct cocoa_display *d, enum yasl_selection sel, const char *text) +void cocoa_display_copy(struct cocoa_display *d, enum yasl_private_selection sel, const char *text) { (void)cocoa_display_copy_text(d, sel, text); } -static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_selection sel, const char *text) +static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_private_selection sel, const char *text) { assert([NSThread isMainThread]); if (!d || !text) { errno = EINVAL; return false; } @@ -1763,7 +2009,7 @@ static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_selection return true; } -static bool cocoa_display_request_paste(struct cocoa_display *d, enum yasl_selection sel, +static bool cocoa_display_request_paste(struct cocoa_display *d, enum yasl_private_selection sel, struct cocoa_surface *target, uint64_t id) { assert([NSThread isMainThread]); @@ -1776,15 +2022,15 @@ static bool cocoa_display_request_paste(struct cocoa_display *d, enum yasl_selec if (!pb) { errno = EINVAL; return false; } NSString *s = [pb stringForType:NSPasteboardTypeString]; const char *utf8 = s ? s.UTF8String : NULL; - enum yasl_transfer_status status = !s ? YASL_TRANSFER_UNAVAILABLE : - utf8 ? YASL_TRANSFER_OK : YASL_TRANSFER_FAILED; - yasl_surface_complete_paste(&target->base, id, status, + enum yasl_private_transfer_status status = !s ? YASL_PRIVATE_TRANSFER_UNAVAILABLE : + utf8 ? YASL_PRIVATE_TRANSFER_OK : YASL_PRIVATE_TRANSFER_FAILED; + yasl_private_surface_complete_paste(&target->base, id, status, utf8 ? utf8 : NULL, utf8 ? strlen(utf8) : 0); errno = 0; return true; } -void cocoa_display_paste(struct cocoa_display *d, enum yasl_selection sel, struct cocoa_surface *target) +void cocoa_display_paste(struct cocoa_display *d, enum yasl_private_selection sel, struct cocoa_surface *target) { (void)cocoa_display_request_paste(d, sel, target, 0); } @@ -1795,18 +2041,20 @@ int cocoa_display_modifiers(const struct cocoa_display *d) return (int)cocoa_mods(NSEvent.modifierFlags); } -struct yasl_geometry cocoa_surface_geometry(const struct cocoa_surface *w) +struct yasl_private_geometry cocoa_surface_geometry(const struct cocoa_surface *w) { - return (struct yasl_geometry){ - .size = w ? w->size : (struct yasl_vector2i){0}, - .scale_120 = w ? w->scale_120 : YASL_SCALE_UNIT, + return (struct yasl_private_geometry){ + .size = w ? w->size : (struct yasl_private_vector2i){0}, + .scale_120 = w ? w->scale_120 : YASL_PRIVATE_SCALE_UNIT, }; } static bool popup_spec_valid(const struct cocoa_surface *parent, - const struct yasl_popup_spec *spec) + const struct yasl_private_popup_spec *spec) { if (!parent || !spec) return false; + if (spec->positioner_explicit && (spec->anchor_edges > 8 || spec->gravity > 8 || + (spec->constraints & ~63u))) return false; int parent_cx = parent->is_popup ? parent->content_offset.x : 0; int parent_cy = parent->is_popup ? parent->content_offset.y : 0; int64_t x = (int64_t)spec->anchor.x - parent_cx; @@ -1821,7 +2069,7 @@ static bool popup_spec_valid(const struct cocoa_surface *parent, (int64_t)spec->content_offset.y + spec->content_size.y <= spec->size.y; } -static NSRect popup_calc_frame(struct cocoa_surface *parent, const struct yasl_popup_spec *spec) +static NSRect popup_calc_frame_legacy(struct cocoa_surface *parent, const struct yasl_private_popup_spec *spec) { CGFloat scale = parent->window ? parent->window.backingScaleFactor : 1.0; if (scale < 1.0) scale = 1.0; @@ -1885,8 +2133,153 @@ static NSRect popup_calc_frame(struct cocoa_surface *parent, const struct yasl_p return frame; } +enum { + POPUP_EDGE_TOP = 1, + POPUP_EDGE_BOTTOM = 2, + POPUP_EDGE_LEFT = 3, + POPUP_EDGE_RIGHT = 4, + POPUP_EDGE_TOP_LEFT = 5, + POPUP_EDGE_BOTTOM_LEFT = 6, + POPUP_EDGE_TOP_RIGHT = 7, + POPUP_EDGE_BOTTOM_RIGHT = 8, + POPUP_CONSTRAINT_SLIDE_X = 1u << 0, + POPUP_CONSTRAINT_SLIDE_Y = 1u << 1, + POPUP_CONSTRAINT_FLIP_X = 1u << 2, + POPUP_CONSTRAINT_FLIP_Y = 1u << 3, + POPUP_CONSTRAINT_RESIZE_X = 1u << 4, + POPUP_CONSTRAINT_RESIZE_Y = 1u << 5, +}; + +static bool popup_edge_has_left(unsigned edge) +{ return edge == POPUP_EDGE_LEFT || edge == POPUP_EDGE_TOP_LEFT || edge == POPUP_EDGE_BOTTOM_LEFT; } +static bool popup_edge_has_right(unsigned edge) +{ return edge == POPUP_EDGE_RIGHT || edge == POPUP_EDGE_TOP_RIGHT || edge == POPUP_EDGE_BOTTOM_RIGHT; } +static bool popup_edge_has_top(unsigned edge) +{ return edge == POPUP_EDGE_TOP || edge == POPUP_EDGE_TOP_LEFT || edge == POPUP_EDGE_TOP_RIGHT; } +static bool popup_edge_has_bottom(unsigned edge) +{ return edge == POPUP_EDGE_BOTTOM || edge == POPUP_EDGE_BOTTOM_LEFT || edge == POPUP_EDGE_BOTTOM_RIGHT; } + +static unsigned popup_flip_x_edge(unsigned edge) +{ + switch (edge) { + case POPUP_EDGE_LEFT: return POPUP_EDGE_RIGHT; + case POPUP_EDGE_RIGHT: return POPUP_EDGE_LEFT; + case POPUP_EDGE_TOP_LEFT: return POPUP_EDGE_TOP_RIGHT; + case POPUP_EDGE_BOTTOM_LEFT: return POPUP_EDGE_BOTTOM_RIGHT; + case POPUP_EDGE_TOP_RIGHT: return POPUP_EDGE_TOP_LEFT; + case POPUP_EDGE_BOTTOM_RIGHT: return POPUP_EDGE_BOTTOM_LEFT; + default: return edge; + } +} + +static unsigned popup_flip_y_edge(unsigned edge) +{ + switch (edge) { + case POPUP_EDGE_TOP: return POPUP_EDGE_BOTTOM; + case POPUP_EDGE_BOTTOM: return POPUP_EDGE_TOP; + case POPUP_EDGE_TOP_LEFT: return POPUP_EDGE_BOTTOM_LEFT; + case POPUP_EDGE_TOP_RIGHT: return POPUP_EDGE_BOTTOM_RIGHT; + case POPUP_EDGE_BOTTOM_LEFT: return POPUP_EDGE_TOP_LEFT; + case POPUP_EDGE_BOTTOM_RIGHT: return POPUP_EDGE_TOP_RIGHT; + default: return edge; + } +} + +static CGFloat popup_axis_overflow(CGFloat min, CGFloat max, CGFloat visible_min, CGFloat visible_max) +{ + return MAX(0, visible_min - min) + MAX(0, max - visible_max); +} + +static NSRect popup_frame_explicit(struct cocoa_surface *parent, + const struct yasl_private_popup_spec *spec, unsigned anchor, unsigned gravity) +{ + CGFloat scale = parent->window.backingScaleFactor; + if (scale < 1.0) scale = 1.0; + CGFloat parent_x = (parent->is_popup ? parent->content_offset.x : 0) / scale; + CGFloat parent_y = (parent->is_popup ? parent->content_offset.y : 0) / scale; + CGFloat ax = spec->anchor.x / scale - parent_x; + CGFloat ay = spec->anchor.y / scale - parent_y; + CGFloat aw = spec->anchor.width / scale; + CGFloat ah = spec->anchor.height / scale; + CGFloat anchor_x = popup_edge_has_left(anchor) ? ax : + popup_edge_has_right(anchor) ? ax + aw : ax + aw * 0.5; + CGFloat anchor_y = popup_edge_has_top(anchor) ? ay : + popup_edge_has_bottom(anchor) ? ay + ah : ay + ah * 0.5; + CGFloat offset_x = spec->content_offset.x / scale; + CGFloat offset_y = spec->content_offset.y / scale; + CGFloat content_w = (spec->content_size.x ? spec->content_size.x : spec->size.x - spec->content_offset.x) / scale; + CGFloat content_h = (spec->content_size.y ? spec->content_size.y : spec->size.y - spec->content_offset.y) / scale; + CGFloat content_left = popup_edge_has_right(gravity) ? anchor_x : + popup_edge_has_left(gravity) ? anchor_x - content_w : anchor_x - content_w * 0.5; + CGFloat content_top = popup_edge_has_bottom(gravity) ? anchor_y : + popup_edge_has_top(gravity) ? anchor_y - content_h : anchor_y - content_h * 0.5; + CGFloat top_left_x = content_left - offset_x; + CGFloat top_left_y = content_top - offset_y; + + CGFloat view_h = parent->view.bounds.size.height; + NSPoint in_view = NSMakePoint(top_left_x, view_h - top_left_y); + NSPoint in_window = [parent->view convertPoint:in_view toView:nil]; + NSPoint screen_top_left = [parent->window convertPointToScreen:in_window]; + CGFloat width = spec->size.x / scale; + CGFloat height = spec->size.y / scale; + return NSMakeRect(screen_top_left.x, screen_top_left.y - height, width, height); +} + +static NSRect popup_calc_frame(struct cocoa_surface *parent, const struct yasl_private_popup_spec *spec) +{ + if (!spec->positioner_explicit) + return popup_calc_frame_legacy(parent, spec); + + NSRect frame = popup_frame_explicit(parent, spec, spec->anchor_edges, spec->gravity); + NSScreen *screen = parent->window.screen ?: [NSScreen mainScreen]; + if (!screen) return frame; + NSRect visible = screen.visibleFrame; + unsigned constraints = spec->constraints; + + if (constraints & POPUP_CONSTRAINT_FLIP_X) { + NSRect flipped = popup_frame_explicit(parent, spec, + popup_flip_x_edge(spec->anchor_edges), popup_flip_x_edge(spec->gravity)); + if (popup_axis_overflow(flipped.origin.x, NSMaxX(flipped), NSMinX(visible), NSMaxX(visible)) < + popup_axis_overflow(frame.origin.x, NSMaxX(frame), NSMinX(visible), NSMaxX(visible))) + frame = NSMakeRect(flipped.origin.x, frame.origin.y, flipped.size.width, frame.size.height); + } + if (constraints & POPUP_CONSTRAINT_FLIP_Y) { + NSRect flipped = popup_frame_explicit(parent, spec, + popup_flip_y_edge(spec->anchor_edges), popup_flip_y_edge(spec->gravity)); + if (popup_axis_overflow(flipped.origin.y, NSMaxY(flipped), NSMinY(visible), NSMaxY(visible)) < + popup_axis_overflow(frame.origin.y, NSMaxY(frame), NSMinY(visible), NSMaxY(visible))) + frame = NSMakeRect(frame.origin.x, flipped.origin.y, frame.size.width, flipped.size.height); + } + + if (constraints & POPUP_CONSTRAINT_SLIDE_X) { + if (NSMinX(frame) < NSMinX(visible)) frame.origin.x = NSMinX(visible); + if (NSMaxX(frame) > NSMaxX(visible)) frame.origin.x = NSMaxX(visible) - frame.size.width; + } + if (constraints & POPUP_CONSTRAINT_SLIDE_Y) { + if (NSMinY(frame) < NSMinY(visible)) frame.origin.y = NSMinY(visible); + if (NSMaxY(frame) > NSMaxY(visible)) frame.origin.y = NSMaxY(visible) - frame.size.height; + } + if (constraints & POPUP_CONSTRAINT_RESIZE_X) { + if (NSMinX(frame) < NSMinX(visible)) { + frame.size.width = MAX(0, frame.size.width - (NSMinX(visible) - NSMinX(frame))); + frame.origin.x = NSMinX(visible); + } + if (NSMaxX(frame) > NSMaxX(visible)) + frame.size.width = MAX(0, NSMaxX(visible) - frame.origin.x); + } + if (constraints & POPUP_CONSTRAINT_RESIZE_Y) { + if (NSMinY(frame) < NSMinY(visible)) { + frame.size.height = MAX(0, frame.size.height - (NSMinY(visible) - NSMinY(frame))); + frame.origin.y = NSMinY(visible); + } + if (NSMaxY(frame) > NSMaxY(visible)) + frame.size.height = MAX(0, NSMaxY(visible) - frame.origin.y); + } + return frame; +} + struct cocoa_surface *cocoa_surface_create_popup(struct cocoa_surface *parent, - const struct yasl_popup_spec *spec) + const struct yasl_private_popup_spec *spec) { assert([NSThread isMainThread]); if (!parent || parent->closed || !parent->window || !parent->view || @@ -1905,8 +2298,10 @@ struct cocoa_surface *cocoa_surface_create_popup(struct cocoa_surface *parent, struct cocoa_surface *w = calloc(1, sizeof(*w)); if (!w) return NULL; w->base.display = parent->base.display; + w->base.presenter = parent->base.presenter; + w->base.creation_flags = parent->base.creation_flags; cocoa_surface_set_callbacks(w, NULL, NULL); - w->clock.refresh_ns = parent->clock.refresh_ns ? parent->clock.refresh_ns : YASL_REFRESH_DEFAULT_NS; + w->clock.refresh_ns = parent->clock.refresh_ns ? parent->clock.refresh_ns : YASL_PRIVATE_REFRESH_DEFAULT_NS; w->parent = parent; w->is_popup = true; w->content_offset = spec->content_offset; @@ -1968,7 +2363,7 @@ struct cocoa_surface *cocoa_surface_create_popup(struct cocoa_surface *parent, } bool cocoa_surface_popup_reposition(struct cocoa_surface *popup, - const struct yasl_popup_spec *spec) + const struct yasl_private_popup_spec *spec) { if (!popup || popup->closed || !popup->is_popup || !popup->parent || !popup->parent->window || !popup->parent->view || @@ -2020,8 +2415,8 @@ static NSImage *image_from_pixels(const uint32_t *pixels, int width, int height) } bool cocoa_display_drag_start(struct cocoa_display *d, struct cocoa_surface *origin, - const struct yasl_drag_image *image, - const struct yasl_drag_listener *l, void *ud) + const struct yasl_private_drag_image *image, + const struct yasl_private_drag_listener *l, void *ud) { if (!d || !origin || d->drag.active || !l) return false; @@ -2029,7 +2424,7 @@ bool cocoa_display_drag_start(struct cocoa_display *d, struct cocoa_surface *ori d->drag.origin = origin; d->drag.carried = NULL; d->drag.hover = NULL; - d->drag.hover_pos = (struct yasl_vector2i){0}; + d->drag.hover_pos = (struct yasl_private_vector2i){0}; d->drag.listener = *l; d->drag.ud = ud; [d->drag.image release]; @@ -2074,7 +2469,7 @@ bool cocoa_display_drag_start(struct cocoa_display *d, struct cocoa_surface *ori return true; } -bool cocoa_surface_drag_carry(struct cocoa_surface *w, struct yasl_vector2i offset) +bool cocoa_surface_drag_carry(struct cocoa_surface *w, struct yasl_private_vector2i offset) { if (!w || w->closed || !darwin_ctx.drag.active) return false; @@ -2088,33 +2483,33 @@ bool cocoa_surface_drag_carry(struct cocoa_surface *w, struct yasl_vector2i offs return cocoa_surface_place_under_pointer(w, offset); } -bool cocoa_surface_start_interactive_resize(struct cocoa_surface *s, enum yasl_resize_edge edge) +bool cocoa_surface_start_interactive_resize(struct cocoa_surface *s, enum yasl_private_resize_edge edge) { (void)s; (void)edge; return false; } -bool cocoa_surface_show_window_menu(struct cocoa_surface *s, struct yasl_vector2i pos) +bool cocoa_surface_show_window_menu(struct cocoa_surface *s, struct yasl_private_vector2i pos) { (void)s; (void)pos; return false; } void cocoa_surface_set_callbacks(struct cocoa_surface *w, - const struct yasl_window_cbs *cbs, void *ud) + const struct yasl_private_window_cbs *cbs, void *ud) { if (!w || w->closed) return; - w->base.callbacks = cbs ? *cbs : (struct yasl_window_cbs){0}; + w->base.callbacks = cbs ? *cbs : (struct yasl_private_window_cbs){0}; w->base.ud = ud; w->cbs = &w->base.callbacks; w->ud = ud; } -bool cocoa_surface_poll_event(struct cocoa_surface *w, struct yasl_event *ev) +bool cocoa_surface_poll_event(struct cocoa_surface *w, struct yasl_private_event *ev) { if (!w || !ev || !input_queue_pop(&w->input, ev)) return false; - if (ev->type == YASL_EVENT_INPUT_RESET) { + if (ev->type == YASL_PRIVATE_EVENT_INPUT_RESET) { w->buttons = 0; w->tablet_down = false; w->mods = 0; @@ -2126,103 +2521,103 @@ bool cocoa_surface_poll_event(struct cocoa_surface *w, struct yasl_event *ev) return true; } -struct yasl_timing cocoa_surface_timing(const struct cocoa_surface *w) +struct yasl_private_timing cocoa_surface_timing(const struct cocoa_surface *w) { - struct yasl_timing timing = {0}; + struct yasl_private_timing timing = {0}; if (w) { timing.refresh_ns = w->clock.refresh_ns; timing.last_present_ns = w->clock.last_present_ns; - timing.next_present_ns = yasl_clock_next_present_ns(&w->clock); + timing.next_present_ns = yasl_private_clock_next_present_ns(&w->clock); timing.frames_skipped = w->frames_skipped; timing.source = w->clock.source; - yasl_clock_resize_stats(&w->clock, &timing.resize); + yasl_private_clock_resize_stats(&w->clock, &timing.resize); } return timing; } -static struct yasl_display *impl_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec) +static struct yasl_private_display *impl_display_open(struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec) { struct cocoa_display *result = cocoa_display_open(loop, spec); return result ? &result->base : NULL; } -static void impl_display_close(struct yasl_display *d) +static void impl_display_close(struct yasl_private_display *d) { cocoa_display_close((struct cocoa_display *)d); } -static uint32_t impl_display_caps(const struct yasl_display *d) +static uint32_t impl_display_caps(const struct yasl_private_display *d) { return cocoa_display_caps((const struct cocoa_display *)d); } -static int impl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms) +static int impl_display_dispatch_timeout(struct yasl_private_display *d, int timeout_ms) { return cocoa_display_dispatch_timeout((struct cocoa_display *)d, timeout_ms); } -static void impl_display_flush(struct yasl_display *d) +static void impl_display_flush(struct yasl_private_display *d) { cocoa_display_flush((struct cocoa_display *)d); } -static int impl_display_double_click_ms(const struct yasl_display *d) +static int impl_display_double_click_ms(const struct yasl_private_display *d) { (void)d; return (int)lround([NSEvent doubleClickInterval] * 1000.0); } -static int impl_display_modifiers(const struct yasl_display *d) +static int impl_display_modifiers(const struct yasl_private_display *d) { return cocoa_display_modifiers((const struct cocoa_display *)d); } -static void impl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text) +static void impl_display_copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text) { cocoa_display_copy((struct cocoa_display *)d, sel, text); } -static void impl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target) +static void impl_display_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target) { cocoa_display_paste((struct cocoa_display *)d, sel, (struct cocoa_surface *)target); } -static bool impl_display_copy_text(struct yasl_display *d, enum yasl_selection sel, const char *text) +static bool impl_display_copy_text(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text) { return cocoa_display_copy_text((struct cocoa_display *)d, sel, text); } -static bool impl_display_request_paste(struct yasl_display *d, enum yasl_selection sel, +static bool impl_display_request_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target, uint64_t request_id) { return cocoa_display_request_paste((struct cocoa_display *)d, sel, (struct cocoa_surface *)target, request_id); } -static bool impl_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) +static bool impl_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) { return cocoa_display_drag_start((struct cocoa_display *)d, (struct cocoa_surface *)origin, image, l, ud); } -static void impl_display_drag_cancel(struct yasl_display *d) +static void impl_display_drag_cancel(struct yasl_private_display *d) { - drag_end((struct cocoa_display *)d, YASL_DRAG_CANCELLED); + drag_end((struct cocoa_display *)d, YASL_PRIVATE_DRAG_CANCELLED); } -static struct yasl_surface *impl_surface_create_toplevel(struct yasl_display *d, - const struct yasl_window_spec *spec, void *graphics_device) +static struct yasl_surface *impl_surface_create_toplevel(struct yasl_private_display *d, + const struct yasl_private_window_spec *spec, void *graphics_device) { struct cocoa_surface *result = cocoa_surface_create_toplevel((struct cocoa_display *)d, spec, graphics_device); return result ? &result->base : NULL; } -static struct yasl_surface *impl_surface_create_popup(struct yasl_surface *parent, const struct yasl_popup_spec *spec) +static struct yasl_surface *impl_surface_create_popup(struct yasl_surface *parent, const struct yasl_private_popup_spec *spec) { struct cocoa_surface *result = cocoa_surface_create_popup((struct cocoa_surface *)parent, spec); return result ? &result->base : NULL; } -static bool impl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_popup_spec *spec) +static bool impl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_private_popup_spec *spec) { return cocoa_surface_popup_reposition((struct cocoa_surface *)popup, spec); } @@ -2238,12 +2633,12 @@ static struct input_queue *impl_surface_input_queue(struct yasl_surface *s) return w ? &w->input : NULL; } -static struct yasl_display *impl_surface_display(const struct yasl_surface *s) +static struct yasl_private_display *impl_surface_display(const struct yasl_surface *s) { return s ? &darwin_ctx.base : NULL; } -static void impl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_window_cbs *cbs, void *ud) +static void impl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_private_window_cbs *cbs, void *ud) { cocoa_surface_set_callbacks((struct cocoa_surface *)s, cbs, ud); } @@ -2258,17 +2653,17 @@ static void impl_surface_schedule_redraw(struct yasl_surface *s) cocoa_surface_schedule_redraw((struct cocoa_surface *)s); } -static bool impl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage) +static bool impl_surface_present(struct yasl_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage) { return cocoa_surface_present((struct cocoa_surface *)s, fb, damage); } -static struct yasl_geometry impl_surface_geometry(const struct yasl_surface *s) +static struct yasl_private_geometry impl_surface_geometry(const struct yasl_surface *s) { return cocoa_surface_geometry((const struct cocoa_surface *)s); } -static struct yasl_timing impl_surface_timing(const struct yasl_surface *s) +static struct yasl_private_timing impl_surface_timing(const struct yasl_surface *s) { return cocoa_surface_timing((const struct cocoa_surface *)s); } @@ -2276,10 +2671,10 @@ static struct yasl_timing impl_surface_timing(const struct yasl_surface *s) static uint64_t impl_surface_frame_time_ns(struct yasl_surface *s, uint64_t now) { struct cocoa_surface *w = (struct cocoa_surface *)s; - return w ? yasl_clock_frame_timestamp_ns(&w->clock, now) : now; + return w ? yasl_private_clock_frame_timestamp_ns(&w->clock, now) : now; } -static void impl_surface_set_cursor(struct yasl_surface *s, enum yasl_cursor cursor) +static void impl_surface_set_cursor(struct yasl_surface *s, enum yasl_private_cursor cursor) { cocoa_surface_set_cursor((struct cocoa_surface *)s, cursor); } @@ -2300,7 +2695,7 @@ static bool impl_surface_ime_preedit_cursor(const struct yasl_surface *s, int *b return cocoa_surface_ime_preedit_cursor((const struct cocoa_surface *)s, begin, end); } -static void impl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_rect2i caret) +static void impl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_private_rect2i caret) { cocoa_surface_set_ime_caret((struct cocoa_surface *)s, caret); } @@ -2310,35 +2705,165 @@ static bool impl_surface_start_interactive_move(struct yasl_surface *s) return cocoa_surface_start_interactive_move((struct cocoa_surface *)s); } -static bool impl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_resize_edge edge) +static bool impl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_private_resize_edge edge) { return cocoa_surface_start_interactive_resize((struct cocoa_surface *)s, edge); } -static bool impl_surface_show_window_menu(struct yasl_surface *s, struct yasl_vector2i pos) +static bool impl_surface_show_window_menu(struct yasl_surface *s, struct yasl_private_vector2i pos) { return cocoa_surface_show_window_menu((struct cocoa_surface *)s, pos); } -static bool impl_surface_drag_carry(struct yasl_surface *s, struct yasl_vector2i offset) +static bool impl_surface_drag_carry(struct yasl_surface *s, struct yasl_private_vector2i offset) { return cocoa_surface_drag_carry((struct cocoa_surface *)s, offset); } -static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_vector2i at) +static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_private_vector2i at) { return cocoa_surface_place_under_pointer((struct cocoa_surface *)s, at); } +static struct yasl_surface *impl_surface_create_gl(struct yasl_private_display *display, + const struct yasl_private_window_spec *spec, const int *attrs, struct yasl_surface *share) +{ + struct yasl_private_gl_options options; + if (!spec || !yasl_private_gl_parse_options(attrs, &options) || + options.api != YASL_PRIVATE_GL_OPENGL || options.debug || (share && + (share->display != display || share->presenter != YASL_PRIVATE_PRESENTER_GL))) + return NULL; + int profile = options.profile; + if (profile == YASL_PRIVATE_GL_ANY) + profile = options.major >= 3 ? YASL_PRIVATE_GL_CORE : YASL_PRIVATE_GL_COMPATIBILITY; + if (options.forward_compat && profile != YASL_PRIVATE_GL_CORE) + return NULL; + if ((profile == YASL_PRIVATE_GL_CORE && + (options.major != 3 || options.minor != 2)) || + (profile == YASL_PRIVATE_GL_COMPATIBILITY && + (options.major > 2 || (options.major == 2 && options.minor > 1)))) + return NULL; + + struct yasl_private_window_spec gl_spec = *spec; + gl_spec.external_gpu = true; + struct cocoa_surface *w = cocoa_surface_create_toplevel((struct cocoa_display *)display, &gl_spec, NULL); + if (!w) return NULL; + + NSOpenGLPixelFormatAttribute pixel_attrs[32]; + NSUInteger n = 0; + pixel_attrs[n++] = NSOpenGLPFAAccelerated; + pixel_attrs[n++] = NSOpenGLPFADoubleBuffer; + pixel_attrs[n++] = NSOpenGLPFAColorSize; + pixel_attrs[n++] = options.red_bits + options.green_bits + options.blue_bits + options.alpha_bits; + pixel_attrs[n++] = NSOpenGLPFAAlphaSize; + pixel_attrs[n++] = options.alpha_bits; + pixel_attrs[n++] = NSOpenGLPFADepthSize; + pixel_attrs[n++] = options.depth_bits; + pixel_attrs[n++] = NSOpenGLPFAStencilSize; + pixel_attrs[n++] = options.stencil_bits; + if (options.samples > 0) { + pixel_attrs[n++] = NSOpenGLPFASampleBuffers; + pixel_attrs[n++] = 1; + pixel_attrs[n++] = NSOpenGLPFASamples; + pixel_attrs[n++] = options.samples; + } + if (options.srgb) pixel_attrs[n++] = NSOpenGLPFASRGB; + if (profile == YASL_PRIVATE_GL_CORE) { + pixel_attrs[n++] = NSOpenGLPFAOpenGLProfile; + pixel_attrs[n++] = NSOpenGLProfileVersion3_2Core; + } else if (profile == YASL_PRIVATE_GL_COMPATIBILITY) { + pixel_attrs[n++] = NSOpenGLPFAOpenGLProfile; + pixel_attrs[n++] = NSOpenGLProfileVersionLegacy; + } + pixel_attrs[n++] = 0; + + NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixel_attrs]; + NSOpenGLContext *shared = share ? ((struct cocoa_surface *)share)->gl_context : nil; + if (format) + w->gl_context = [[NSOpenGLContext alloc] initWithFormat:format shareContext:shared]; + [format release]; + if (!w->gl_context) { + cocoa_surface_destroy(w); + reap_dead((struct cocoa_display *)display); + return NULL; + } + [w->view setLayer:[CALayer layer]]; + w->layer = nil; + [w->gl_context setView:w->view]; + w->base.presenter = YASL_PRIVATE_PRESENTER_GL; + return &w->base; +} + +static bool impl_gl_make_current(struct yasl_private_display *display, struct yasl_surface *surface) +{ + (void)display; + if (!surface) { + [NSOpenGLContext clearCurrentContext]; + return true; + } + struct cocoa_surface *w = (struct cocoa_surface *)surface; + if (w->base.presenter != YASL_PRIVATE_PRESENTER_GL || !w->gl_context || w->closed) + return false; + [w->gl_context makeCurrentContext]; + return [NSOpenGLContext currentContext] == w->gl_context; +} + +static yasl_private_gl_proc impl_gl_get_proc_address(struct yasl_private_display *display, const char *name) +{ + (void)display; + return name ? (yasl_private_gl_proc)dlsym(RTLD_DEFAULT, name) : NULL; +} + +static bool impl_gl_swap_interval(struct yasl_private_display *display, struct yasl_surface *surface, int interval) +{ + (void)display; + struct cocoa_surface *w = (struct cocoa_surface *)surface; + if (!w || w->base.presenter != YASL_PRIVATE_PRESENTER_GL || !w->gl_context || w->closed) + return false; + [w->gl_context setValues:&interval forParameter:NSOpenGLCPSwapInterval]; + return true; +} + +static bool impl_gl_swap_buffers(struct yasl_private_display *display, struct yasl_surface *surface) +{ + (void)display; + struct cocoa_surface *w = (struct cocoa_surface *)surface; + if (!w || w->base.presenter != YASL_PRIVATE_PRESENTER_GL || !w->gl_context || w->closed) + return false; + [w->gl_context flushBuffer]; + yasl_private_clock_presented(&w->clock, yasl_private_loop_now_ns(), false); + return true; +} + +static void *impl_surface_get_metal_layer(struct yasl_surface *surface) +{ + struct cocoa_surface *w = (struct cocoa_surface *)surface; + return w && (w->base.presenter == YASL_PRIVATE_PRESENTER_NONE || + w->base.presenter == YASL_PRIVATE_PRESENTER_METAL) ? (void *)w->layer : NULL; +} + +static bool impl_surface_update_text_input(struct yasl_surface *surface) +{ + struct cocoa_surface *w = (struct cocoa_surface *)surface; + if (!w || w->closed || w->destroyed || !w->view) return false; + if (w->base.text_input_active) { + [w->window makeFirstResponder:w->view]; + } else { + [w->view.inputContext discardMarkedText]; + if (w->preedit) [w->view unmarkText]; + } + return true; +} + static bool impl_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap, - struct yasl_vector2i *size) + struct yasl_private_vector2i *size) { struct cocoa_surface *w = (struct cocoa_surface *)s; /* did_present stays set from the last draw until the next one starts, and * pixels are reallocated only when a draw starts. */ if (w->closed || w->rendering || !w->did_present || !w->software_queue || !w->pixels) return false; - return yasl_copy_frame(w->pixels, w->shown_size, out, cap, size); + return yasl_private_copy_frame(w->pixels, w->shown_size, out, cap, size); } static bool impl_surface_bell(struct yasl_surface *s) @@ -2349,7 +2874,7 @@ static bool impl_surface_bell(struct yasl_surface *s) return true; } -static void *impl_display_get_native_handle(struct yasl_display *d) +static void *impl_display_get_native_handle(struct yasl_private_display *d) { return d ? (void *)NSApp : NULL; } @@ -2361,19 +2886,26 @@ static void *impl_surface_get_native_handle(struct yasl_surface *s) } static bool impl_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) { struct cocoa_surface *w = (struct cocoa_surface *)s; if (w->closed || !w->rendering || w->did_present || !w->drawable || target->pixels != w->frame.target.pixels || target->size.x != w->size.x || target->size.y != w->size.y) return false; - *out = (struct yasl_native_frame){(void *)w->drawable, w->transaction}; + *out = (struct yasl_private_native_frame){(void *)w->drawable, w->transaction}; return true; } -const struct yasl_backend_ops yasl_cocoa_backend = { - .type = YASL_BACKEND_COCOA, +const struct yasl_private_backend_ops yasl_private_cocoa_backend = { + .type = YASL_PRIVATE_BACKEND_COCOA, + .surface_create_gl = impl_surface_create_gl, + .gl_make_current = impl_gl_make_current, + .gl_get_proc_address = impl_gl_get_proc_address, + .gl_swap_interval = impl_gl_swap_interval, + .gl_swap_buffers = impl_gl_swap_buffers, + .surface_get_metal_layer = impl_surface_get_metal_layer, + .surface_update_text_input = impl_surface_update_text_input, .display_open = impl_display_open, .display_close = impl_display_close, .display_caps = impl_display_caps, @@ -2421,20 +2953,20 @@ const struct yasl_backend_ops yasl_cocoa_backend = { .surface_get_native_frame = impl_surface_get_native_frame, }; -NSApplication *yasl_cocoa_display_get_application(struct yasl_display *d) +NSApplication *yasl_private_cocoa_display_get_application(struct yasl_private_display *d) { - return (d && yasl_display_backend(d) == YASL_BACKEND_COCOA) ? - (NSApplication *)yasl_display_get_native_handle(d) : nil; + return (d && yasl_private_display_backend(d) == YASL_PRIVATE_BACKEND_COCOA) ? + (NSApplication *)yasl_private_display_get_native_handle(d) : nil; } -NSWindow *yasl_cocoa_surface_get_window(struct yasl_surface *s) +NSWindow *yasl_private_cocoa_surface_get_window(struct yasl_surface *s) { - return (s && s->display && yasl_display_backend(s->display) == YASL_BACKEND_COCOA) ? - (NSWindow *)yasl_surface_get_native_handle(s) : nil; + return (s && s->display && yasl_private_display_backend(s->display) == YASL_PRIVATE_BACKEND_COCOA) ? + (NSWindow *)yasl_private_surface_get_native_handle(s) : nil; } -NSView *yasl_cocoa_surface_get_view(struct yasl_surface *s) +NSView *yasl_private_cocoa_surface_get_view(struct yasl_surface *s) { - NSWindow *w = yasl_cocoa_surface_get_window(s); + NSWindow *w = yasl_private_cocoa_surface_get_window(s); return w ? w.contentView : nil; } diff --git a/src/platform/freedesktop/dbus.h b/src/platform/freedesktop/dbus.h index fd0aeb1..beda62b 100644 --- a/src/platform/freedesktop/dbus.h +++ b/src/platform/freedesktop/dbus.h @@ -1,5 +1,5 @@ -#ifndef YASL_FREEDESKTOP_DBUS_H -#define YASL_FREEDESKTOP_DBUS_H +#ifndef YASL_PRIVATE_FREEDESKTOP_DBUS_H +#define YASL_PRIVATE_FREEDESKTOP_DBUS_H #include <stdbool.h> #include <stdint.h> diff --git a/src/platform/freedesktop/desktop.c b/src/platform/freedesktop/desktop.c index a8b9bc7..f90701a 100644 --- a/src/platform/freedesktop/desktop.c +++ b/src/platform/freedesktop/desktop.c @@ -1,7 +1,7 @@ #define _GNU_SOURCE #include "common/desktop.h" #include "platform/freedesktop/dbus.h" -#include "yasl/desktop.h" +#include "common/private_desktop.h" #include <errno.h> #include <fcntl.h> @@ -105,21 +105,21 @@ uint32_t desktop_appearance(void) int64_t value; if (portal_read_int("org.freedesktop.appearance", "color-scheme", &value)) { portal = true; - if (value == 1) flags |= YASL_APPEARANCE_DARK; - else if (value == 2) flags |= YASL_APPEARANCE_LIGHT; + if (value == 1) flags |= YASL_PRIVATE_APPEARANCE_DARK; + else if (value == 2) flags |= YASL_PRIVATE_APPEARANCE_LIGHT; } if (portal_read_int("org.freedesktop.appearance", "contrast", &value)) { portal = true; - if (value == 1) flags |= YASL_APPEARANCE_HIGH_CONTRAST; + if (value == 1) flags |= YASL_PRIVATE_APPEARANCE_HIGH_CONTRAST; } if (portal) return flags; /* GTK_THEME is "Name" or "Name:variant", as in "Adwaita:dark". */ const char *theme = getenv("GTK_THEME"); if (!theme) return 0; - if (strstr(theme, "HighContrast")) flags |= YASL_APPEARANCE_HIGH_CONTRAST; + if (strstr(theme, "HighContrast")) flags |= YASL_PRIVATE_APPEARANCE_HIGH_CONTRAST; if (strstr(theme, ":dark") || strstr(theme, "HighContrastInverse")) - flags |= YASL_APPEARANCE_DARK; + flags |= YASL_PRIVATE_APPEARANCE_DARK; return flags; } diff --git a/src/platform/posix/loop.c b/src/platform/posix/loop.c index 3c3d730..49132f8 100644 --- a/src/platform/posix/loop.c +++ b/src/platform/posix/loop.c @@ -14,35 +14,35 @@ #include <time.h> #include <unistd.h> -#define YASL_LOOP_INIT_CAP 32 +#define YASL_PRIVATE_LOOP_INIT_CAP 32 -typedef int (*yasl_loop_fd_fn)(int fd, short revents, void *ud); +typedef int (*yasl_private_loop_fd_fn)(int fd, short revents, void *ud); -struct yasl_fd_source { - yasl_loop_fd_fn fn; +struct yasl_private_fd_source { + yasl_private_loop_fd_fn fn; void *ud; int id; int fd; short events; }; -struct yasl_timer_source { - yasl_timer_cb fn; +struct yasl_private_timer_source { + yasl_private_timer_cb fn; void *ud; uint64_t interval_ns; uint64_t deadline_ns; - yasl_timer_id id; + yasl_private_timer_id id; bool deleted; }; -struct yasl_post { +struct yasl_private_post { void (*fn)(void *ud); void *ud; - struct yasl_post *next; + struct yasl_private_post *next; }; /* Signals this loop watches; the previous actions are restored on unwatch. */ -struct yasl_signal_watch { +struct yasl_private_signal_watch { void (*cb)(void *ud, int signo); void *ud; int count; @@ -50,8 +50,8 @@ struct yasl_signal_watch { struct sigaction old[NSIG]; }; -struct yasl_loop { - struct yasl_fd_source *fds; +struct yasl_private_loop { + struct yasl_private_fd_source *fds; struct pollfd *pollfds; int count; int capacity; @@ -61,17 +61,17 @@ struct yasl_loop { int wake_wfd; int wake_watch; - struct yasl_timer_source *timers; + struct yasl_private_timer_source *timers; int timer_count; int timer_capacity; uint32_t next_timer_id; int timer_firing_depth; - yasl_mutex post_mutex; - struct yasl_post *post_head; - struct yasl_post *post_tail; + yasl_private_mutex post_mutex; + struct yasl_private_post *post_head; + struct yasl_private_post *post_tail; - struct yasl_signal_watch *signals; + struct yasl_private_signal_watch *signals; }; /* Handlers run on any thread, so they only touch lock-free atomics: they mark @@ -79,34 +79,34 @@ struct yasl_loop { * stored as fd + 1 so that the zero initializer means no pipe. */ static atomic_int signal_wake_fd[NSIG]; static atomic_bool signal_pending[NSIG]; -static struct yasl_loop *signal_owner[NSIG]; +static struct yasl_private_loop *signal_owner[NSIG]; -uint64_t yasl_loop_now_ns(void) +uint64_t yasl_private_loop_now_ns(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec; } -static void run_posts(struct yasl_loop *l) +static void run_posts(struct yasl_private_loop *l) { - yasl_mutex_lock(&l->post_mutex); - struct yasl_post *p = l->post_head; + yasl_private_mutex_lock(&l->post_mutex); + struct yasl_private_post *p = l->post_head; l->post_head = l->post_tail = NULL; - yasl_mutex_unlock(&l->post_mutex); + yasl_private_mutex_unlock(&l->post_mutex); while (p) { - struct yasl_post *next = p->next; + struct yasl_private_post *next = p->next; p->fn(p->ud); free(p); p = next; } } -static void run_signals(struct yasl_loop *l) +static void run_signals(struct yasl_private_loop *l) { /* A callback may unwatch, so re-check the watch before each delivery. */ for (int signo = 1; signo < NSIG; signo++) { - struct yasl_signal_watch *w = l->signals; + struct yasl_private_signal_watch *w = l->signals; if (!w) return; if (signal_owner[signo] != l || !atomic_exchange(&signal_pending[signo], false)) continue; @@ -120,18 +120,18 @@ static int loop_wake_cb(int fd, short revents, void *ud) char buf[128]; ssize_t n; while ((n = read(fd, buf, sizeof(buf))) > 0 || (n < 0 && errno == EINTR)) {} - struct yasl_loop *l = ud; + struct yasl_private_loop *l = ud; run_posts(l); run_signals(l); return 0; } -struct yasl_loop *yasl_loop_create(void) +struct yasl_private_loop *yasl_private_loop_create(void) { - struct yasl_loop *l = calloc(1, sizeof(*l)); + struct yasl_private_loop *l = calloc(1, sizeof(*l)); if (!l) return NULL; - l->capacity = YASL_LOOP_INIT_CAP; + l->capacity = YASL_PRIVATE_LOOP_INIT_CAP; l->fds = malloc((size_t)l->capacity * sizeof(*l->fds)); l->pollfds = malloc((size_t)l->capacity * sizeof(*l->pollfds)); if (!l->fds || !l->pollfds) { @@ -141,7 +141,7 @@ struct yasl_loop *yasl_loop_create(void) return NULL; } - if (yasl_mutex_init(&l->post_mutex) != 0) { + if (yasl_private_mutex_init(&l->post_mutex) != 0) { free(l->fds); free(l->pollfds); free(l); @@ -173,23 +173,23 @@ struct yasl_loop *yasl_loop_create(void) #endif if (l->wake_rfd >= 0) { - l->wake_watch = yasl_loop_add_fd(l, l->wake_rfd, POLLIN, loop_wake_cb, l); + l->wake_watch = yasl_private_loop_add_fd(l, l->wake_rfd, POLLIN, loop_wake_cb, l); } return l; } -void yasl_loop_destroy(struct yasl_loop *l) +void yasl_private_loop_destroy(struct yasl_private_loop *l) { if (!l) return; - yasl_loop_unwatch_signals(l); - for (struct yasl_post *p = l->post_head; p;) { - struct yasl_post *next = p->next; + yasl_private_loop_unwatch_signals(l); + for (struct yasl_private_post *p = l->post_head; p;) { + struct yasl_private_post *next = p->next; free(p); p = next; } - yasl_mutex_destroy(&l->post_mutex); + yasl_private_mutex_destroy(&l->post_mutex); if (l->wake_watch > 0) - yasl_loop_del_fd(l, l->wake_watch); + yasl_private_loop_del_fd(l, l->wake_watch); if (l->wake_rfd >= 0) close(l->wake_rfd); if (l->wake_wfd >= 0) @@ -200,15 +200,15 @@ void yasl_loop_destroy(struct yasl_loop *l) free(l); } -int yasl_loop_add_fd(struct yasl_loop *l, int fd, short events, +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) { if (!l || fd < 0) return -1; if (l->count >= l->capacity) { - int new_cap = l->capacity ? l->capacity * 2 : YASL_LOOP_INIT_CAP; - struct yasl_fd_source *new_fds = realloc(l->fds, (size_t)new_cap * sizeof(*new_fds)); + int new_cap = l->capacity ? l->capacity * 2 : YASL_PRIVATE_LOOP_INIT_CAP; + struct yasl_private_fd_source *new_fds = realloc(l->fds, (size_t)new_cap * sizeof(*new_fds)); if (!new_fds) return -1; l->fds = new_fds; @@ -221,7 +221,7 @@ int yasl_loop_add_fd(struct yasl_loop *l, int fd, short events, int id = l->next_id++; if (l->next_id <= 0) l->next_id = 1; - l->fds[l->count] = (struct yasl_fd_source){ + l->fds[l->count] = (struct yasl_private_fd_source){ .fn = fn, .ud = ud, .id = id, .fd = fd, .events = events, }; l->pollfds[l->count] = (struct pollfd){ @@ -231,7 +231,7 @@ int yasl_loop_add_fd(struct yasl_loop *l, int fd, short events, return id; } -int yasl_loop_del_fd(struct yasl_loop *l, int id) +int yasl_private_loop_del_fd(struct yasl_private_loop *l, int id) { if (!l || id <= 0) return -1; for (int i = 0; i < l->count; i++) { @@ -245,7 +245,7 @@ int yasl_loop_del_fd(struct yasl_loop *l, int id) return -1; } -int yasl_loop_set_fd_events(struct yasl_loop *l, int id, short events) +int yasl_private_loop_set_fd_events(struct yasl_private_loop *l, int id, short events) { if (!l || id <= 0) return -1; for (int i = 0; i < l->count; i++) { @@ -270,25 +270,25 @@ static void wake_write(int fd) errno = saved; } -void yasl_loop_kick(struct yasl_loop *l) +void yasl_private_loop_kick(struct yasl_private_loop *l) { if (!l || l->wake_wfd < 0) return; wake_write(l->wake_wfd); } -bool yasl_loop_post(struct yasl_loop *l, void (*fn)(void *ud), void *ud) +bool yasl_private_loop_post(struct yasl_private_loop *l, void (*fn)(void *ud), void *ud) { if (!l || !fn || l->wake_wfd < 0) return false; - struct yasl_post *p = malloc(sizeof(*p)); + struct yasl_private_post *p = malloc(sizeof(*p)); if (!p) return false; - *p = (struct yasl_post){.fn = fn, .ud = ud}; - yasl_mutex_lock(&l->post_mutex); + *p = (struct yasl_private_post){.fn = fn, .ud = ud}; + yasl_private_mutex_lock(&l->post_mutex); if (l->post_tail) l->post_tail->next = p; else l->post_head = p; l->post_tail = p; - yasl_mutex_unlock(&l->post_mutex); + yasl_private_mutex_unlock(&l->post_mutex); wake_write(l->wake_wfd); return true; } @@ -305,7 +305,7 @@ static bool signal_valid(int signo) return signo > 0 && signo < NSIG && signo != SIGKILL && signo != SIGSTOP; } -bool yasl_loop_watch_signals(struct yasl_loop *l, const int *signals, int n, +bool yasl_private_loop_watch_signals(struct yasl_private_loop *l, const int *signals, int n, void (*cb)(void *ud, int signo), void *ud) { if (!l || !signals || n <= 0 || !cb || l->wake_wfd < 0) { @@ -324,14 +324,14 @@ bool yasl_loop_watch_signals(struct yasl_loop *l, const int *signals, int n, } wanted[signals[i]] = true; } - struct yasl_signal_watch *w = calloc(1, sizeof(*w)); + struct yasl_private_signal_watch *w = calloc(1, sizeof(*w)); if (!w) return false; w->cb = cb; w->ud = ud; /* Keep the old watch's saved actions for signals that stay watched, so * that unwatch restores what the application had before any watch. */ - struct yasl_signal_watch *old = l->signals; + struct yasl_private_signal_watch *old = l->signals; struct sigaction sa = {.sa_handler = signal_handler, .sa_flags = SA_RESTART}; sigemptyset(&sa.sa_mask); for (int signo = 1; signo < NSIG; signo++) { @@ -380,10 +380,10 @@ bool yasl_loop_watch_signals(struct yasl_loop *l, const int *signals, int n, return true; } -void yasl_loop_unwatch_signals(struct yasl_loop *l) +void yasl_private_loop_unwatch_signals(struct yasl_private_loop *l) { if (!l || !l->signals) return; - struct yasl_signal_watch *w = l->signals; + struct yasl_private_signal_watch *w = l->signals; l->signals = NULL; for (int i = 0; i < w->count; i++) { int signo = w->signals[i]; @@ -395,19 +395,19 @@ void yasl_loop_unwatch_signals(struct yasl_loop *l) free(w); } -static void timer_cleanup_deleted(struct yasl_loop *l) +static void timer_cleanup_deleted(struct yasl_private_loop *l) { if (!l || l->timer_firing_depth > 0) return; int dest = 0; for (int src = 0; src < l->timer_count; src++) { - struct yasl_timer_source t = l->timers[src]; + struct yasl_private_timer_source t = l->timers[src]; l->timers[dest] = t; dest += !t.deleted; } l->timer_count = dest; } -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) { if (!l || id == 0) return false; for (int i = 0; i < l->timer_count; i++) @@ -416,7 +416,7 @@ bool yasl_loop_timer_armed(const struct yasl_loop *l, yasl_timer_id id) return false; } -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) { if (!l) { *fds = NULL; @@ -426,10 +426,10 @@ int yasl_loop_poll_set(const struct yasl_loop *l, const struct pollfd **fds) return l->count; } -int yasl_loop_next_timeout_ms(const struct yasl_loop *l) +int yasl_private_loop_next_timeout_ms(const struct yasl_private_loop *l) { if (!l || l->timer_count <= 0) return -1; - uint64_t now = yasl_loop_now_ns(); + uint64_t now = yasl_private_loop_now_ns(); uint64_t min_deadline = UINT64_MAX; bool has_active = false; for (int i = 0; i < l->timer_count; i++) { @@ -446,14 +446,14 @@ int yasl_loop_next_timeout_ms(const struct yasl_loop *l) return (ms > (uint64_t)INT_MAX) ? INT_MAX : (int)ms; } -yasl_timer_id yasl_loop_add_timer(struct yasl_loop *l, uint64_t interval_ns, - yasl_timer_cb cb, void *ud) +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) { if (!l || !cb || interval_ns == 0) return 0; if (l->timer_count >= l->timer_capacity) { int new_cap = l->timer_capacity ? l->timer_capacity * 2 : 16; - struct yasl_timer_source *new_timers = realloc(l->timers, (size_t)new_cap * sizeof(*new_timers)); + struct yasl_private_timer_source *new_timers = realloc(l->timers, (size_t)new_cap * sizeof(*new_timers)); if (!new_timers) return 0; l->timers = new_timers; l->timer_capacity = new_cap; @@ -462,8 +462,8 @@ yasl_timer_id yasl_loop_add_timer(struct yasl_loop *l, uint64_t interval_ns, uint32_t id = l->next_timer_id++; if (l->next_timer_id == 0) l->next_timer_id = 1; - uint64_t now = yasl_loop_now_ns(); - l->timers[l->timer_count++] = (struct yasl_timer_source){ + uint64_t now = yasl_private_loop_now_ns(); + l->timers[l->timer_count++] = (struct yasl_private_timer_source){ .fn = cb, .ud = ud, .id = id, @@ -472,11 +472,11 @@ yasl_timer_id yasl_loop_add_timer(struct yasl_loop *l, uint64_t interval_ns, .deleted = false, }; - yasl_loop_kick(l); + yasl_private_loop_kick(l); return id; } -bool yasl_loop_del_timer(struct yasl_loop *l, yasl_timer_id id) +bool yasl_private_loop_del_timer(struct yasl_private_loop *l, yasl_private_timer_id id) { if (!l || id == 0) return false; for (int i = l->timer_count - 1; i >= 0; i--) { @@ -496,11 +496,11 @@ bool yasl_loop_del_timer(struct yasl_loop *l, yasl_timer_id id) return false; } -int yasl_loop_step(struct yasl_loop *l, int timeout_ms) +int yasl_private_loop_step(struct yasl_private_loop *l, int timeout_ms) { if (!l) return -1; - int timer_to = yasl_loop_next_timeout_ms(l); + int timer_to = yasl_private_loop_next_timeout_ms(l); int effective_timeout = timeout_ms; if (timer_to >= 0) { if (effective_timeout < 0 || timer_to < effective_timeout) @@ -528,9 +528,9 @@ int yasl_loop_step(struct yasl_loop *l, int timeout_ms) if (l->count > 0) { int count = l->count; - struct yasl_fd_source sstack[32]; + struct yasl_private_fd_source sstack[32]; struct pollfd pstack[32]; - struct yasl_fd_source *scopy = (count <= 32) ? sstack : malloc((size_t)count * sizeof(*scopy)); + struct yasl_private_fd_source *scopy = (count <= 32) ? sstack : malloc((size_t)count * sizeof(*scopy)); struct pollfd *pcopy = (count <= 32) ? pstack : malloc((size_t)count * sizeof(*pcopy)); if (!scopy || !pcopy) { if (scopy && scopy != sstack) free(scopy); @@ -570,7 +570,7 @@ int yasl_loop_step(struct yasl_loop *l, int timeout_ms) } if (l->timer_count > 0) { - uint64_t now = yasl_loop_now_ns(); + uint64_t now = yasl_private_loop_now_ns(); l->timer_firing_depth++; int initial_count = l->timer_count; for (int i = 0; i < initial_count; i++) { @@ -578,10 +578,10 @@ int yasl_loop_step(struct yasl_loop *l, int timeout_ms) if (l->timers[i].deleted || l->timers[i].deadline_ns > now) continue; - yasl_timer_id tid = l->timers[i].id; + yasl_private_timer_id tid = l->timers[i].id; uint64_t interval = l->timers[i].interval_ns; void *ud = l->timers[i].ud; - struct yasl_timer_source timer_src = l->timers[i]; + struct yasl_private_timer_source timer_src = l->timers[i]; uint64_t next_interval = timer_src.fn(ud, tid, interval); handled++; @@ -623,18 +623,18 @@ int yasl_loop_step(struct yasl_loop *l, int timeout_ms) return handled; } -int yasl_loop_run(struct yasl_loop *l) +int yasl_private_loop_run(struct yasl_private_loop *l) { if (!l) return -1; l->running = true; while (l->running) { - if (yasl_loop_step(l, -1) < 0) + if (yasl_private_loop_step(l, -1) < 0) return -1; } return 0; } -void yasl_loop_quit(struct yasl_loop *l) +void yasl_private_loop_quit(struct yasl_private_loop *l) { if (l) l->running = false; } diff --git a/src/platform/posix/yasl_platform_sync.h b/src/platform/posix/yasl_platform_sync.h index f94a25d..972eef2 100644 --- a/src/platform/posix/yasl_platform_sync.h +++ b/src/platform/posix/yasl_platform_sync.h @@ -1,26 +1,26 @@ -#ifndef YASL_POSIX_SYNC_H -#define YASL_POSIX_SYNC_H +#ifndef YASL_PRIVATE_POSIX_SYNC_H +#define YASL_PRIVATE_POSIX_SYNC_H #include <pthread.h> -typedef pthread_mutex_t yasl_mutex; +typedef pthread_mutex_t yasl_private_mutex; -static inline int yasl_mutex_init(yasl_mutex *mutex) +static inline int yasl_private_mutex_init(yasl_private_mutex *mutex) { return pthread_mutex_init(mutex, NULL); } -static inline void yasl_mutex_destroy(yasl_mutex *mutex) +static inline void yasl_private_mutex_destroy(yasl_private_mutex *mutex) { pthread_mutex_destroy(mutex); } -static inline void yasl_mutex_lock(yasl_mutex *mutex) +static inline void yasl_private_mutex_lock(yasl_private_mutex *mutex) { pthread_mutex_lock(mutex); } -static inline void yasl_mutex_unlock(yasl_mutex *mutex) +static inline void yasl_private_mutex_unlock(yasl_private_mutex *mutex) { pthread_mutex_unlock(mutex); } diff --git a/src/platform/sync.h b/src/platform/sync.h index 43b4a34..444ff03 100644 --- a/src/platform/sync.h +++ b/src/platform/sync.h @@ -1,8 +1,8 @@ -#ifndef YASL_PLATFORM_SYNC_H -#define YASL_PLATFORM_SYNC_H +#ifndef YASL_PRIVATE_PLATFORM_SYNC_H +#define YASL_PRIVATE_PLATFORM_SYNC_H -/* The OS support directory selected by Meson provides yasl_mutex and the - * yasl_mutex_init/destroy/lock/unlock operations. Init returns zero on success +/* The OS support directory selected by Meson provides yasl_private_mutex and the + * yasl_private_mutex_init/destroy/lock/unlock operations. Init returns zero on success * or an errno value on failure. The other operations require an initialized * mutex. Native types and calls stay in the selected implementation header. */ #include "yasl_platform_sync.h" // IWYU pragma: export diff --git a/src/platform/wayland/api.h b/src/platform/wayland/api.h index 0f935b2..9670af1 100644 --- a/src/platform/wayland/api.h +++ b/src/platform/wayland/api.h @@ -1,48 +1,48 @@ -#ifndef YASL_WAYLAND_API_H -#define YASL_WAYLAND_API_H +#ifndef YASL_PRIVATE_WAYLAND_API_H +#define YASL_PRIVATE_WAYLAND_API_H -#include "yasl/yasl.h" +#include "common/types.h" struct wayland_display; struct wayland_surface; -struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec); +struct wayland_display *wayland_display_open(struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec); void wayland_display_close(struct wayland_display *d); uint32_t wayland_display_caps(const struct wayland_display *d); int wayland_display_dispatch_timeout(struct wayland_display *d, int timeout_ms); void wayland_display_flush(struct wayland_display *d); int wayland_display_modifiers(const struct wayland_display *d); -void wayland_display_copy(struct wayland_display *d, enum yasl_selection sel, const char *text); -void wayland_display_paste(struct wayland_display *d, enum yasl_selection sel, struct wayland_surface *target); -bool wayland_display_copy_text(struct wayland_display *d, enum yasl_selection sel, const char *text); -bool wayland_display_request_paste(struct wayland_display *d, enum yasl_selection sel, +void wayland_display_copy(struct wayland_display *d, enum yasl_private_selection sel, const char *text); +void wayland_display_paste(struct wayland_display *d, enum yasl_private_selection sel, struct wayland_surface *target); +bool wayland_display_copy_text(struct wayland_display *d, enum yasl_private_selection sel, const char *text); +bool wayland_display_request_paste(struct wayland_display *d, enum yasl_private_selection sel, struct wayland_surface *target, uint64_t request_id); -bool wayland_display_drag_start(struct wayland_display *d, struct wayland_surface *origin, const struct yasl_drag_image *image, const struct yasl_drag_listener *l, void *ud); +bool wayland_display_drag_start(struct wayland_display *d, struct wayland_surface *origin, const struct yasl_private_drag_image *image, const struct yasl_private_drag_listener *l, void *ud); void wayland_display_drag_cancel(struct wayland_display *d); struct wayland_surface *wayland_surface_create_toplevel(struct wayland_display *d, - const struct yasl_window_spec *spec, void *graphics_device); -struct wayland_surface *wayland_surface_create_popup(struct wayland_surface *parent, const struct yasl_popup_spec *spec); -bool wayland_surface_popup_reposition(struct wayland_surface *popup, const struct yasl_popup_spec *spec); + const struct yasl_private_window_spec *spec, void *graphics_device); +struct wayland_surface *wayland_surface_create_popup(struct wayland_surface *parent, const struct yasl_private_popup_spec *spec); +bool wayland_surface_popup_reposition(struct wayland_surface *popup, const struct yasl_private_popup_spec *spec); void wayland_surface_destroy(struct wayland_surface *s); -void wayland_surface_set_callbacks(struct wayland_surface *s, const struct yasl_window_cbs *cbs, void *ud); +void wayland_surface_set_callbacks(struct wayland_surface *s, const struct yasl_private_window_cbs *cbs, void *ud); void wayland_surface_set_title(struct wayland_surface *s, const char *title); void wayland_surface_schedule_redraw(struct wayland_surface *s); -bool wayland_surface_present(struct wayland_surface *s, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage); -bool wayland_surface_poll_event(struct wayland_surface *s, struct yasl_event *ev); -struct yasl_geometry wayland_surface_geometry(const struct wayland_surface *s); -struct yasl_timing wayland_surface_timing(const struct wayland_surface *s); -void wayland_surface_set_cursor(struct wayland_surface *s, enum yasl_cursor cursor); -bool wayland_surface_set_pointer_mode(struct wayland_surface *s, enum yasl_pointer_mode mode); +bool wayland_surface_present(struct wayland_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage); +bool wayland_surface_poll_event(struct wayland_surface *s, struct yasl_private_event *ev); +struct yasl_private_geometry wayland_surface_geometry(const struct wayland_surface *s); +struct yasl_private_timing wayland_surface_timing(const struct wayland_surface *s); +void wayland_surface_set_cursor(struct wayland_surface *s, enum yasl_private_cursor cursor); +bool wayland_surface_set_pointer_mode(struct wayland_surface *s, enum yasl_private_pointer_mode mode); void wayland_surface_set_background_color(struct wayland_surface *s, uint32_t bg); bool wayland_surface_ime_preedit_cursor(const struct wayland_surface *s, int *begin, int *end); -void wayland_surface_set_ime_caret(struct wayland_surface *s, struct yasl_rect2i caret); +void wayland_surface_set_ime_caret(struct wayland_surface *s, struct yasl_private_rect2i caret); bool wayland_surface_start_interactive_move(struct wayland_surface *s); -bool wayland_surface_start_interactive_resize(struct wayland_surface *s, enum yasl_resize_edge edge); -bool wayland_surface_show_window_menu(struct wayland_surface *s, struct yasl_vector2i pos); -bool wayland_surface_drag_carry(struct wayland_surface *s, struct yasl_vector2i offset); -bool wayland_surface_place_under_pointer(struct wayland_surface *s, struct yasl_vector2i at); +bool wayland_surface_start_interactive_resize(struct wayland_surface *s, enum yasl_private_resize_edge edge); +bool wayland_surface_show_window_menu(struct wayland_surface *s, struct yasl_private_vector2i pos); +bool wayland_surface_drag_carry(struct wayland_surface *s, struct yasl_private_vector2i offset); +bool wayland_surface_place_under_pointer(struct wayland_surface *s, struct yasl_private_vector2i at); bool wayland_surface_bell(struct wayland_surface *s); bool wayland_surface_read_pixels(struct wayland_surface *s, uint32_t *out, size_t cap, - struct yasl_vector2i *size); + struct yasl_private_vector2i *size); #endif diff --git a/src/platform/wayland/backend.c b/src/platform/wayland/backend.c index 7026a8c..02aae59 100644 --- a/src/platform/wayland/backend.c +++ b/src/platform/wayland/backend.c @@ -2,50 +2,50 @@ #include "platform/freedesktop/dbus.h" #include <errno.h> -static struct yasl_display *impl_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec) +static struct yasl_private_display *impl_display_open(struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec) { struct wayland_display *result = wayland_display_open(loop, spec); return result ? &result->base : NULL; } -static void impl_display_close(struct yasl_display *d) +static void impl_display_close(struct yasl_private_display *d) { wayland_display_close((struct wayland_display *)d); } -static uint32_t impl_display_caps(const struct yasl_display *d) +static uint32_t impl_display_caps(const struct yasl_private_display *d) { return wayland_display_caps((const struct wayland_display *)d); } -static int impl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms) +static int impl_display_dispatch_timeout(struct yasl_private_display *d, int timeout_ms) { return wayland_display_dispatch_timeout((struct wayland_display *)d, timeout_ms); } -static void impl_display_flush(struct yasl_display *d) +static void impl_display_flush(struct yasl_private_display *d) { wayland_display_flush((struct wayland_display *)d); } -static int impl_display_modifiers(const struct yasl_display *d) +static int impl_display_modifiers(const struct yasl_private_display *d) { return wayland_display_modifiers((const struct wayland_display *)d); } -static int impl_display_double_click_ms(const struct yasl_display *d) +static int impl_display_double_click_ms(const struct yasl_private_display *d) { /* Wayland has no protocol for it; desktops publish it as a setting. */ (void)d; return portal_double_click_ms(); } -static void impl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text) +static void impl_display_copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text) { wayland_display_copy((struct wayland_display *)d, sel, text); } -static bool impl_display_copy_text(struct yasl_display *d, enum yasl_selection sel, +static bool impl_display_copy_text(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text) { bool ok = wayland_display_copy_text((struct wayland_display *)d, sel, text); @@ -53,12 +53,12 @@ static bool impl_display_copy_text(struct yasl_display *d, enum yasl_selection s return ok; } -static void impl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target) +static void impl_display_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target) { wayland_display_paste((struct wayland_display *)d, sel, (struct wayland_surface *)target); } -static bool impl_display_request_paste(struct yasl_display *d, enum yasl_selection sel, +static bool impl_display_request_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target, uint64_t request_id) { bool ok = wayland_display_request_paste((struct wayland_display *)d, sel, @@ -67,30 +67,171 @@ static bool impl_display_request_paste(struct yasl_display *d, enum yasl_selecti return ok; } -static bool impl_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) +static bool impl_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) { return wayland_display_drag_start((struct wayland_display *)d, (struct wayland_surface *)origin, image, l, ud); } -static void impl_display_drag_cancel(struct yasl_display *d) +static void impl_display_drag_cancel(struct yasl_private_display *d) { wayland_display_drag_cancel((struct wayland_display *)d); } -static struct yasl_surface *impl_surface_create_toplevel(struct yasl_display *d, - const struct yasl_window_spec *spec, void *graphics_device) +static struct yasl_surface *impl_surface_create_toplevel(struct yasl_private_display *d, + const struct yasl_private_window_spec *spec, void *graphics_device) { struct wayland_surface *result = wayland_surface_create_toplevel((struct wayland_display *)d, spec, graphics_device); return result ? &result->base : NULL; } -static struct yasl_surface *impl_surface_create_popup(struct yasl_surface *parent, const struct yasl_popup_spec *spec) +static bool impl_surface_update_text_input(struct yasl_surface *s) +{ + return wayland_surface_update_text_input((struct wayland_surface *)s); +} + +#if YASL_HAS_EGL +static bool egl_configure(struct wayland_display *d, const struct yasl_private_gl_options *o, EGLConfig *config) +{ + if (!d->egl_initialized) return false; + EGLint renderable = o->api == YASL_PRIVATE_GL_OPENGL_ES + ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT; +#ifdef EGL_OPENGL_ES_BIT + if (o->api == YASL_PRIVATE_GL_OPENGL_ES && o->major < 2) + renderable = EGL_OPENGL_ES_BIT; +#endif +#ifdef EGL_OPENGL_ES3_BIT_KHR + if (o->api == YASL_PRIVATE_GL_OPENGL_ES && o->major >= 3) + renderable = EGL_OPENGL_ES3_BIT_KHR; +#endif + EGLint attrs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, renderable, + EGL_RED_SIZE, o->red_bits, + EGL_GREEN_SIZE, o->green_bits, + EGL_BLUE_SIZE, o->blue_bits, + EGL_ALPHA_SIZE, o->alpha_bits, + EGL_DEPTH_SIZE, o->depth_bits, + EGL_STENCIL_SIZE, o->stencil_bits, + EGL_SAMPLE_BUFFERS, o->samples > 0 ? 1 : 0, + EGL_SAMPLES, o->samples, + EGL_NONE, + }; + EGLint api = o->api == YASL_PRIVATE_GL_OPENGL_ES ? EGL_OPENGL_ES_API : EGL_OPENGL_API; + if (d->egl_api && d->egl_api != api) return false; + EGLint count = 0; + if (!eglChooseConfig(d->egl_display, attrs, config, 1, &count) || count < 1) + return false; + d->egl_api = api; + return eglBindAPI(api) == EGL_TRUE; +} + +static struct yasl_surface *impl_surface_create_gl(struct yasl_private_display *display, + const struct yasl_private_window_spec *spec, const int *attributes, struct yasl_surface *share) +{ + struct wayland_display *d = (struct wayland_display *)display; + struct yasl_private_gl_options o; + if (!spec || !yasl_private_gl_parse_options(attributes, &o) || !d || !d->egl_initialized) + return NULL; + struct yasl_private_window_spec gl_spec = *spec; + gl_spec.external_gpu = true; + gl_spec.flags &= ~YASL_PRIVATE_SURFACE_CPU; + struct wayland_surface *w = wayland_surface_create_toplevel(d, &gl_spec, NULL); + if (!w) return NULL; + if (!egl_configure(d, &o, &w->egl_config)) goto fail; + EGLContext share_context = share && share->display == display + ? ((struct wayland_surface *)share)->egl_context : EGL_NO_CONTEXT; + if (share && share_context == EGL_NO_CONTEXT) goto fail; + w->egl_window = wl_egl_window_create(w->surface, w->width, w->height); + if (!w->egl_window) goto fail; + EGLint context_attrs[12]; + int n = 0; + if (o.api == YASL_PRIVATE_GL_OPENGL_ES) { + context_attrs[n++] = EGL_CONTEXT_CLIENT_VERSION; + context_attrs[n++] = o.major; + } else { + context_attrs[n++] = EGL_CONTEXT_MAJOR_VERSION_KHR; + context_attrs[n++] = o.major; + context_attrs[n++] = EGL_CONTEXT_MINOR_VERSION_KHR; + context_attrs[n++] = o.minor; + if (o.profile != YASL_PRIVATE_GL_ANY) { + context_attrs[n++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; + context_attrs[n++] = o.profile == YASL_PRIVATE_GL_CORE + ? EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR + : EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; + } + EGLint flags = (o.debug ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0) | + (o.forward_compat ? EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR : 0); + if (flags) { + context_attrs[n++] = EGL_CONTEXT_FLAGS_KHR; + context_attrs[n++] = flags; + } + } + context_attrs[n] = EGL_NONE; + w->egl_context = eglCreateContext(d->egl_display, w->egl_config, share_context, context_attrs); + if (w->egl_context == EGL_NO_CONTEXT) goto fail; + EGLint surface_attrs[] = { +#ifdef EGL_GL_COLORSPACE_KHR + EGL_GL_COLORSPACE_KHR, o.srgb ? EGL_GL_COLORSPACE_SRGB_KHR : EGL_GL_COLORSPACE_LINEAR_KHR, +#endif + EGL_NONE, + }; + w->egl_surface = eglCreateWindowSurface(d->egl_display, w->egl_config, + (EGLNativeWindowType)w->egl_window, surface_attrs); + if (w->egl_surface == EGL_NO_SURFACE) { w->egl_surface = 0; goto fail; } + w->base.presenter = YASL_PRIVATE_PRESENTER_GL; + return &w->base; +fail: + wayland_surface_destroy(w); + return NULL; +} + +static bool impl_gl_make_current(struct yasl_private_display *display, struct yasl_surface *surface) +{ + struct wayland_display *d = (struct wayland_display *)display; + struct wayland_surface *w = (struct wayland_surface *)surface; + if (!d) return false; + if (!w) + return eglMakeCurrent(d->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_TRUE; + return w->conn == d && w->base.presenter == YASL_PRIVATE_PRESENTER_GL && + eglMakeCurrent(d->egl_display, w->egl_surface, w->egl_surface, w->egl_context) == EGL_TRUE; +} + +static yasl_private_gl_proc impl_gl_get_proc_address(struct yasl_private_display *display, const char *name) +{ + (void)display; + return name ? (yasl_private_gl_proc)eglGetProcAddress(name) : NULL; +} + +static bool impl_gl_swap_interval(struct yasl_private_display *display, struct yasl_surface *surface, int interval) +{ + struct wayland_display *d = (struct wayland_display *)display; + struct wayland_surface *w = (struct wayland_surface *)surface; + return d && w && w->conn == d && w->base.presenter == YASL_PRIVATE_PRESENTER_GL && + impl_gl_make_current(display, surface) && eglSwapInterval(d->egl_display, interval) == EGL_TRUE; +} + +static bool impl_gl_swap_buffers(struct yasl_private_display *display, struct yasl_surface *surface) +{ + struct wayland_display *d = (struct wayland_display *)display; + struct wayland_surface *w = (struct wayland_surface *)surface; + if (!d || !w || w->conn != d || w->closed || !w->configured || + w->base.presenter != YASL_PRIVATE_PRESENTER_GL) + return false; + if (!impl_gl_make_current(display, surface)) return false; + wayland_surface_arm_frame(w); + surface_request_presentation_feedback(w); + if (!eglSwapBuffers(d->egl_display, w->egl_surface)) return false; + return true; +} +#endif + +static struct yasl_surface *impl_surface_create_popup(struct yasl_surface *parent, const struct yasl_private_popup_spec *spec) { struct wayland_surface *result = wayland_surface_create_popup((struct wayland_surface *)parent, spec); return result ? &result->base : NULL; } -static bool impl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_popup_spec *spec) +static bool impl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_private_popup_spec *spec) { return wayland_surface_popup_reposition((struct wayland_surface *)popup, spec); } @@ -106,7 +247,7 @@ static struct input_queue *impl_surface_input_queue(struct yasl_surface *s) return w ? &w->input : NULL; } -static bool impl_surface_set_size(struct yasl_surface *s, struct yasl_vector2i size) +static bool impl_surface_set_size(struct yasl_surface *s, struct yasl_private_vector2i size) { struct wayland_surface *w = (struct wayland_surface *)s; if (!w || w->closed || w->destroyed || !w->toplevel || @@ -114,8 +255,19 @@ static bool impl_surface_set_size(struct yasl_surface *s, struct yasl_vector2i s errno = EINVAL; return false; } - errno = ENOTSUP; - return false; + /* Floating windows choose their own size; constrained states keep it for later. */ + w->floating_width = size.x; + w->floating_height = size.y; + if (w->base.state & (YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_TILED)) + return true; + w->req_width = w->req_height = 0; + w->size_pending = true; + if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU && w->configured && apply_pending_geometry(w) < 0) { + errno = EIO; + return false; + } + wayland_surface_schedule_redraw(w); + return true; } static bool impl_surface_set_fullscreen(struct yasl_surface *s, bool enabled) @@ -153,14 +305,14 @@ static bool impl_surface_minimize(struct yasl_surface *s) return true; } -static struct yasl_display *impl_surface_display(const struct yasl_surface *s) +static struct yasl_private_display *impl_surface_display(const struct yasl_surface *s) { const struct wayland_surface *w = (const struct wayland_surface *)s; struct wayland_display *result = w ? w->conn : NULL; return result ? &result->base : NULL; } -static void impl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_window_cbs *cbs, void *ud) +static void impl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_private_window_cbs *cbs, void *ud) { wayland_surface_set_callbacks((struct wayland_surface *)s, cbs, ud); } @@ -175,37 +327,37 @@ static void impl_surface_schedule_redraw(struct yasl_surface *s) wayland_surface_schedule_redraw((struct wayland_surface *)s); } -static bool impl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage) +static bool impl_surface_present(struct yasl_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage) { return wayland_surface_present((struct wayland_surface *)s, fb, damage); } -static bool impl_surface_poll_event(struct yasl_surface *s, struct yasl_event *ev) +static bool impl_surface_poll_event(struct yasl_surface *s, struct yasl_private_event *ev) { return wayland_surface_poll_event((struct wayland_surface *)s, ev); } -static struct yasl_geometry impl_surface_geometry(const struct yasl_surface *s) +static struct yasl_private_geometry impl_surface_geometry(const struct yasl_surface *s) { return wayland_surface_geometry((const struct wayland_surface *)s); } -static struct yasl_timing impl_surface_timing(const struct yasl_surface *s) +static struct yasl_private_timing impl_surface_timing(const struct yasl_surface *s) { return wayland_surface_timing((const struct wayland_surface *)s); } static uint64_t impl_surface_frame_time_ns(struct yasl_surface *s, uint64_t now) { - return yasl_clock_frame_timestamp_ns(&((struct wayland_surface *)s)->clock, now); + return yasl_private_clock_frame_timestamp_ns(&((struct wayland_surface *)s)->clock, now); } -static void impl_surface_set_cursor(struct yasl_surface *s, enum yasl_cursor cursor) +static void impl_surface_set_cursor(struct yasl_surface *s, enum yasl_private_cursor cursor) { wayland_surface_set_cursor((struct wayland_surface *)s, cursor); } -static bool impl_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_pointer_mode mode) +static bool impl_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_private_pointer_mode mode) { return wayland_surface_set_pointer_mode((struct wayland_surface *)s, mode); } @@ -226,7 +378,7 @@ static bool impl_surface_ime_preedit_cursor(const struct yasl_surface *s, int *b return wayland_surface_ime_preedit_cursor((const struct wayland_surface *)s, begin, end); } -static void impl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_rect2i caret) +static void impl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_private_rect2i caret) { wayland_surface_set_ime_caret((struct wayland_surface *)s, caret); } @@ -236,28 +388,28 @@ static bool impl_surface_start_interactive_move(struct yasl_surface *s) return wayland_surface_start_interactive_move((struct wayland_surface *)s); } -static bool impl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_resize_edge edge) +static bool impl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_private_resize_edge edge) { return wayland_surface_start_interactive_resize((struct wayland_surface *)s, edge); } -static bool impl_surface_show_window_menu(struct yasl_surface *s, struct yasl_vector2i pos) +static bool impl_surface_show_window_menu(struct yasl_surface *s, struct yasl_private_vector2i pos) { return wayland_surface_show_window_menu((struct wayland_surface *)s, pos); } -static bool impl_surface_drag_carry(struct yasl_surface *s, struct yasl_vector2i offset) +static bool impl_surface_drag_carry(struct yasl_surface *s, struct yasl_private_vector2i offset) { return wayland_surface_drag_carry((struct wayland_surface *)s, offset); } -static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_vector2i at) +static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_private_vector2i at) { return wayland_surface_place_under_pointer((struct wayland_surface *)s, at); } static bool impl_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap, - struct yasl_vector2i *size) + struct yasl_private_vector2i *size) { return wayland_surface_read_pixels((struct wayland_surface *)s, out, cap, size); } @@ -267,7 +419,7 @@ static bool impl_surface_bell(struct yasl_surface *s) return wayland_surface_bell((struct wayland_surface *)s); } -static void *impl_display_get_native_handle(struct yasl_display *d) +static void *impl_display_get_native_handle(struct yasl_private_display *d) { struct wayland_display *w = (struct wayland_display *)d; return w ? w->wl : NULL; @@ -279,8 +431,16 @@ static void *impl_surface_get_native_handle(struct yasl_surface *s) return w ? w->surface : NULL; } -const struct yasl_backend_ops yasl_wayland_backend = { - .type = YASL_BACKEND_WAYLAND, +const struct yasl_private_backend_ops yasl_private_wayland_backend = { +#if YASL_HAS_EGL + .surface_create_gl = impl_surface_create_gl, + .gl_make_current = impl_gl_make_current, + .gl_get_proc_address = impl_gl_get_proc_address, + .gl_swap_interval = impl_gl_swap_interval, + .gl_swap_buffers = impl_gl_swap_buffers, +#endif + .surface_update_text_input = impl_surface_update_text_input, + .type = YASL_PRIVATE_BACKEND_WAYLAND, .display_open = impl_display_open, .display_close = impl_display_close, .display_caps = impl_display_caps, diff --git a/src/platform/wayland/buffer.c b/src/platform/wayland/buffer.c index 874cc76..bbc055d 100644 --- a/src/platform/wayland/buffer.c +++ b/src/platform/wayland/buffer.c @@ -24,7 +24,7 @@ static const struct wl_buffer_listener buffer_listener; int device_px(int logical, uint32_t scale_120) { - int64_t px = ((int64_t)logical * scale_120 + YASL_SCALE_UNIT / 2) / YASL_SCALE_UNIT; + int64_t px = ((int64_t)logical * scale_120 + YASL_PRIVATE_SCALE_UNIT / 2) / YASL_PRIVATE_SCALE_UNIT; return px > 0 && px <= INT_MAX ? (int)px : -1; } @@ -58,7 +58,7 @@ int buffer_reserve(struct wayland_surface *w, struct buffer *b, size_t needed) if (b->busy || needed > INT32_MAX) return -1; size_t capacity = b->capacity <= INT32_MAX / 2 ? b->capacity * 2 : INT32_MAX; if (capacity < needed) capacity = needed; - int fd = memfd_create("yasl_surface_buffer", MFD_CLOEXEC | MFD_ALLOW_SEALING); + int fd = memfd_create("yasl_private_surface_buffer", MFD_CLOEXEC | MFD_ALLOW_SEALING); if (fd < 0) return -1; if (!fd_truncate(fd, capacity) || fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL) < 0) { @@ -89,7 +89,7 @@ int buffer_prepare(struct wayland_surface *w, struct buffer *b, int width, int h { if (b->proxy && !b->busy && b->width == width && b->height == height) return 0; long faults_before = minor_faults(); - uint64_t t0 = yasl_loop_now_ns(); + uint64_t t0 = yasl_private_loop_now_ns(); size_t bytes; if (!buffer_size(width, height, &bytes) || buffer_reserve(w, b, bytes) < 0) return -1; @@ -104,7 +104,7 @@ int buffer_prepare(struct wayland_surface *w, struct buffer *b, int width, int h b->busy = false; b->presented = false; wl_buffer_add_listener(proxy, listener, b); - yasl_clock_note_resize(&w->clock, yasl_loop_now_ns() - t0); + yasl_private_clock_note_resize(&w->clock, yasl_private_loop_now_ns() - t0); long faults = minor_faults() - faults_before; if (faults > 0) w->clock.resize_minflt += faults; return 0; @@ -114,10 +114,10 @@ static void publish_surface_scale(struct wayland_surface *w) { if (w->viewport) { wp_viewport_set_destination(w->viewport, w->logical_width, w->logical_height); - } else if (w->scale_120 % YASL_SCALE_UNIT == 0 && + } else if (w->scale_120 % YASL_PRIVATE_SCALE_UNIT == 0 && wl_proxy_get_version((struct wl_proxy *)w->surface) >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) { - wl_surface_set_buffer_scale(w->surface, (int32_t)(w->scale_120 / YASL_SCALE_UNIT)); + wl_surface_set_buffer_scale(w->surface, (int32_t)(w->scale_120 / YASL_PRIVATE_SCALE_UNIT)); } } @@ -137,16 +137,16 @@ int apply_pending_geometry(struct wayland_surface *w) return 0; } bool floating = !w->parent && !(w->base.state & - (YASL_STATE_MAXIMIZED | YASL_STATE_FULLSCREEN | YASL_STATE_TILED)); + (YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_TILED)); int free_w = floating && w->floating_width > 0 ? w->floating_width : w->logical_width; int free_h = floating && w->floating_height > 0 ? w->floating_height : w->logical_height; int logical_w = !w->parent && w->req_width > 0 ? w->req_width : free_w; int logical_h = !w->parent && w->req_height > 0 ? w->req_height : free_h; if (!w->parent) { - if (logical_w < YASL_MIN_LOGICAL_PX) logical_w = YASL_MIN_LOGICAL_PX; - if (logical_h < YASL_MIN_LOGICAL_PX) logical_h = YASL_MIN_LOGICAL_PX; + if (logical_w < YASL_PRIVATE_MIN_LOGICAL_PX) logical_w = YASL_PRIVATE_MIN_LOGICAL_PX; + if (logical_h < YASL_PRIVATE_MIN_LOGICAL_PX) logical_h = YASL_PRIVATE_MIN_LOGICAL_PX; } - if (!(w->base.state & YASL_STATE_FULLSCREEN)) { + if (!(w->base.state & YASL_PRIVATE_STATE_FULLSCREEN)) { if (w->bound_width > 0 && logical_w > w->bound_width) logical_w = w->bound_width; if (w->bound_height > 0 && logical_h > w->bound_height) @@ -155,7 +155,9 @@ int apply_pending_geometry(struct wayland_surface *w) int width = device_px(logical_w, w->pending_scale_120); int height = device_px(logical_h, w->pending_scale_120); size_t bytes; - if (!buffer_size(width, height, &bytes)) { + if (width <= 0 || height <= 0 || + (w->base.presenter == YASL_PRIVATE_PRESENTER_CPU && + !buffer_size(width, height, &bytes))) { return -1; } bool rescaled = w->scale_120 != w->pending_scale_120; @@ -169,13 +171,17 @@ int apply_pending_geometry(struct wayland_surface *w) return 0; w->scale_120 = w->pending_scale_120; - w->scale_factor = (double)w->scale_120 / YASL_SCALE_UNIT; + w->scale_factor = (double)w->scale_120 / YASL_PRIVATE_SCALE_UNIT; w->width = width; w->height = height; - w->base.size = (struct yasl_vector2i){width, height}; + w->base.size = (struct yasl_private_vector2i){width, height}; w->base.scale_120 = w->scale_120; w->logical_width = logical_w; w->logical_height = logical_h; +#if YASL_HAS_EGL + if (w->egl_window && (resized || rescaled)) + wl_egl_window_resize(w->egl_window, width, height, 0, 0); +#endif surface_update_opaque_region(w); if (resized || rescaled) { for (int i = 0; i < NBUF; i++) @@ -183,7 +189,7 @@ int apply_pending_geometry(struct wayland_surface *w) } publish_surface_scale(w); if (resized || rescaled) { - yasl_surface_notify_configure(&w->base, (struct yasl_geometry){ + yasl_private_surface_notify_configure(&w->base, (struct yasl_private_geometry){ .size = { .x = width, .y = height }, .scale_120 = w->scale_120, }); if (w->closed || w->destroyed) @@ -225,7 +231,7 @@ void surface_follow_outputs(struct wayland_surface *w) w->clock.refresh_ns = refresh; if (need_scale) - surface_request_scale(w, (uint32_t)max_scale * YASL_SCALE_UNIT); + surface_request_scale(w, (uint32_t)max_scale * YASL_PRIVATE_SCALE_UNIT); } static void surface_enter(void *data, struct wl_surface *surface, struct wl_output *output) @@ -250,7 +256,7 @@ static void surface_preferred_scale(void *data, struct wl_surface *surface, int3 struct wayland_surface *w = data; if (w->frac_scale || w->parent || factor <= 0) return; - surface_request_scale(w, (uint32_t)factor * YASL_SCALE_UNIT); + surface_request_scale(w, (uint32_t)factor * YASL_PRIVATE_SCALE_UNIT); } static void surface_preferred_transform(void *data, struct wl_surface *surface, @@ -285,13 +291,15 @@ static void frame_done(void *data, struct wl_callback *cb, uint32_t ms) wl_callback_destroy(cb); w->frame_cb = NULL; - uint64_t now = yasl_loop_now_ns(); + uint64_t now = yasl_private_loop_now_ns(); if (!w->conn->pres_clock_known) { - yasl_clock_presented(&w->clock, yasl_clock_extend_ms(ms, now), w->sample_refresh); - w->clock.source = YASL_TIMING_FRAME_CALLBACK; + yasl_private_clock_presented(&w->clock, yasl_private_clock_extend_ms(ms, now), w->sample_refresh); + w->clock.source = YASL_PRIVATE_TIMING_FRAME_CALLBACK; } - if (w->want_draw) + if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU) + yasl_private_surface_notify_refresh(&w->base); + else if (w->want_draw) wayland_surface_schedule_redraw(w); } @@ -299,14 +307,14 @@ static const struct wl_callback_listener frame_listener = { .done = frame_done, }; -static void arm_frame(struct wayland_surface *w) +void wayland_surface_arm_frame(struct wayland_surface *w) { if (!w->configured || w->closed || w->frame_cb) return; w->frame_cb = wl_surface_frame(w->surface); if (!w->frame_cb) { surface_fail(w); return; } w->sample_refresh = w->clock.last_present_ns && - yasl_loop_now_ns() - w->clock.last_present_ns < + yasl_private_loop_now_ns() - w->clock.last_present_ns < (uint64_t)(w->clock.refresh_ns / 2); if (wl_callback_add_listener(w->frame_cb, &frame_listener, w) < 0) { wl_callback_destroy(w->frame_cb); @@ -365,7 +373,7 @@ static void presented(void *data, struct wp_presentation_feedback *fb, (((uint64_t)tv_sec_hi << 32) | tv_sec_lo) * 1000000000ull + tv_nsec); if (refresh) w->clock.refresh_ns = refresh; w->clock.last_present_ns = ns; - w->clock.source = YASL_TIMING_PRESENTATION; + w->clock.source = YASL_PRIVATE_TIMING_PRESENTATION; } (void)fb; feedback_destroy(feedback); @@ -419,7 +427,7 @@ static void commit_buffer(struct wayland_surface *w, int idx) { wl_surface_attach(w->surface, w->buffers[idx].proxy, 0, 0); - const struct yasl_damage_list *d = &w->damage; + const struct yasl_private_damage_list *d = &w->damage; if (d->n < 0 || !w->buffers[idx].presented) wl_surface_damage_buffer(w->surface, 0, 0, w->width, w->height); else @@ -438,8 +446,8 @@ static void commit_buffer(struct wayland_surface *w, int idx) wp_fifo_v1_set_barrier(w->fifo); } if (w->commit_timer && w->conn->pres_clock_known) { - uint64_t now = yasl_loop_now_ns(); - uint64_t next = yasl_clock_next_present_ns(&w->clock); + uint64_t now = yasl_private_loop_now_ns(); + uint64_t next = yasl_private_clock_next_present_ns(&w->clock); bool soon = next > now && next - now < (uint64_t)(w->clock.refresh_ns * 1.5); next = conn_mono_to_pres(w->conn, next); if (soon) @@ -458,10 +466,11 @@ static void commit_buffer(struct wayland_surface *w, int idx) w->buffers[idx].presented = true; } -bool wayland_surface_present(struct wayland_surface *w, const struct yasl_framebuffer *fb, - const struct yasl_damage_list *damage) +bool wayland_surface_present(struct wayland_surface *w, const struct yasl_private_framebuffer *fb, + const struct yasl_private_damage_list *damage) { - if (!w || w->closed || !w->frame_active || w->frame_presented || !fb || + if (!w || w->base.presenter != YASL_PRIVATE_PRESENTER_CPU || + w->closed || !w->frame_active || w->frame_presented || !fb || !damage || fb->pixels != w->frame.target.pixels || fb->size.x != w->width || fb->size.y != w->height) return false; @@ -470,7 +479,7 @@ bool wayland_surface_present(struct wayland_surface *w, const struct yasl_frameb w->damage.bounds = damage->bounds; if (damage->n > 0) memcpy(w->damage.rects, damage->rects, (size_t)damage->n * sizeof(damage->rects[0])); - arm_frame(w); + wayland_surface_arm_frame(w); if (w->closed) return false; commit_buffer(w, w->cur); @@ -481,17 +490,17 @@ bool wayland_surface_present(struct wayland_surface *w, const struct yasl_frameb } bool wayland_surface_read_pixels(struct wayland_surface *w, uint32_t *out, size_t cap, - struct yasl_vector2i *size) + struct yasl_private_vector2i *size) { if (!w || w->closed || w->frame_active || !w->has_shown) return false; const struct buffer *b = &w->buffers[w->shown]; if (!b->pixels) return false; - return yasl_copy_frame(b->pixels, (struct yasl_vector2i){b->width, b->height}, out, cap, size); + return yasl_private_copy_frame(b->pixels, (struct yasl_private_vector2i){b->width, b->height}, out, cap, size); } void surface_draw_frame(struct wayland_surface *w) { - if (w->closed || !w->configured) + if (w->closed || !w->configured || w->base.presenter != YASL_PRIVATE_PRESENTER_CPU) return; if (apply_pending_geometry(w) < 0) { surface_fail(w); @@ -517,18 +526,18 @@ void surface_draw_frame(struct wayland_surface *w) if (w->parent) memset(w->buffers[idx].pixels, 0, (size_t)w->width * (size_t)w->height * sizeof(uint32_t)); - w->damage = (struct yasl_damage_list){0}; - w->frame = (struct yasl_frame){ + w->damage = (struct yasl_private_damage_list){0}; + w->frame = (struct yasl_private_frame){ .surface = &w->base, .target = { - .generation = yasl_frame_begin(&w->base), + .generation = yasl_private_frame_begin(&w->base), .pixels = w->buffers[idx].pixels, .size = { .x = w->width, .y = w->height }, }, }; w->frame_active = true; w->frame_presented = false; - yasl_surface_draw(&w->base, &w->frame); + yasl_private_surface_draw(&w->base, &w->frame); if (w->closed || w->destroyed) return; w->frame_active = false; @@ -555,21 +564,21 @@ int surface_init_scaling(struct wayland_surface *w) void wayland_surface_set_title(struct wayland_surface *w, const char *title) { if (!w || !w->toplevel || !title) return; - char *copy = yasl_utf8_sanitize_dup(title); + char *copy = yasl_private_utf8_sanitize_dup(title); if (!copy) return; xdg_toplevel_set_title(w->toplevel, copy); free(copy); } -struct yasl_timing wayland_surface_timing(const struct wayland_surface *w) +struct yasl_private_timing wayland_surface_timing(const struct wayland_surface *w) { - struct yasl_timing t = { + struct yasl_private_timing t = { .refresh_ns = w->clock.refresh_ns, .last_present_ns = w->clock.last_present_ns, - .next_present_ns = yasl_clock_next_present_ns(&w->clock), + .next_present_ns = yasl_private_clock_next_present_ns(&w->clock), .frames_skipped = w->frames_skipped, .source = w->clock.source, }; - yasl_clock_resize_stats(&w->clock, &t.resize); + yasl_private_clock_resize_stats(&w->clock, &t.resize); return t; } diff --git a/src/platform/wayland/clipboard.c b/src/platform/wayland/clipboard.c index 8ca9b93..cf39b6c 100644 --- a/src/platform/wayland/clipboard.c +++ b/src/platform/wayland/clipboard.c @@ -25,7 +25,7 @@ struct transfer { struct offer *drop; struct wl_surface *drop_surface; int fd, id; - yasl_timer_id timeout; + yasl_private_timer_id timeout; char *text; size_t len, offset, capacity; uint64_t request_id; @@ -48,7 +48,7 @@ struct clipboard { struct offer *drag_offer; struct primary_offer *primary_selection; struct primary_offer *primary_pending_offers; - struct yasl_loop *loop; + struct yasl_private_loop *loop; struct transfer *transfers; char *text; char *primary_text; @@ -70,7 +70,7 @@ const char *clipboard_drag_mime(void) return mime; } -static void drag_event(struct clipboard *c, enum yasl_drag_event ev, +static void drag_event(struct clipboard *c, enum yasl_private_drag_event ev, wl_fixed_t x, wl_fixed_t y) { if (c->drag) @@ -78,7 +78,7 @@ static void drag_event(struct clipboard *c, enum yasl_drag_event ev, c->drag_ud); } -static void drop_event(struct clipboard *c, enum yasl_drop_event ev) +static void drop_event(struct clipboard *c, enum yasl_private_drop_event ev) { if (c->drop && c->drag_surface) c->drop(ev, c->drag_surface, wl_fixed_to_double(c->drag_x), @@ -89,7 +89,7 @@ static void free_offer(struct offer *o); static void transfer_end(struct transfer *t, bool complete); -static uint64_t transfer_timeout(void *ud, yasl_timer_id id, uint64_t interval) +static uint64_t transfer_timeout(void *ud, yasl_private_timer_id id, uint64_t interval) { (void)interval; struct transfer *t = ud; @@ -106,9 +106,9 @@ static void transfer_end(struct transfer *t, bool complete) struct transfer **p = &owner->transfers; while (*p && *p != t) p = &(*p)->next; if (*p) *p = t->next; - yasl_loop_del_fd(owner->loop, t->id); + yasl_private_loop_del_fd(owner->loop, t->id); if (t->timeout) - yasl_loop_del_timer(owner->loop, t->timeout); + yasl_private_loop_del_timer(owner->loop, t->timeout); close(t->fd); if (t->drop) { if (complete && wl_data_offer_get_version(t->drop->proxy) >= @@ -124,7 +124,7 @@ static void transfer_end(struct transfer *t, bool complete) struct wl_surface *surface = t->drop_surface; uint64_t request_id = t->request_id; bool uri_list = t->uri_list; - enum yasl_transfer_status status = complete ? YASL_TRANSFER_OK : YASL_TRANSFER_FAILED; + enum yasl_private_transfer_status status = complete ? YASL_PRIVATE_TRANSFER_OK : YASL_PRIVATE_TRANSFER_FAILED; const char *text = complete ? (t->text ? t->text : "") : NULL; size_t len = complete ? t->len : 0; if (notify) @@ -156,7 +156,7 @@ static int transfer_io(int fd, short events, void *ud) if (n > 0) { t->len += (size_t)n; } else if (n == 0) { - bool valid = yasl_utf8_validate(t->text, t->len); + bool valid = yasl_private_utf8_validate(t->text, t->len); transfer_end(t, valid); } else if (errno != EAGAIN && errno != EINTR) transfer_end(t, false); } @@ -176,12 +176,12 @@ static struct transfer *start_transfer_text(struct clipboard *c, int fd, bool se if (!t->text) { close(fd); free(t); return NULL; } t->len = strlen(t->text); } - t->id = yasl_loop_add_fd(c->loop, fd, sending ? POLLOUT : POLLIN, transfer_io, t); + t->id = yasl_private_loop_add_fd(c->loop, fd, sending ? POLLOUT : POLLIN, transfer_io, t); if (t->id < 0) { close(fd); free(t->text); free(t); return NULL; } - t->timeout = yasl_loop_add_timer(c->loop, CLIPBOARD_TRANSFER_TIMEOUT_NS, + t->timeout = yasl_private_loop_add_timer(c->loop, CLIPBOARD_TRANSFER_TIMEOUT_NS, transfer_timeout, t); if (!t->timeout) { - yasl_loop_del_fd(c->loop, t->id); + yasl_private_loop_del_fd(c->loop, t->id); close(fd); free(t->text); free(t); @@ -311,9 +311,9 @@ static void drag_offer_enter(void *ud, struct wl_data_device *device, uint32_t s c->drag_y = y; c->drag_surface = surface; if (o && o->tab) - drag_event(c, YASL_DRAG_OVER, x, y); + drag_event(c, YASL_PRIVATE_DRAG_OVER, x, y); else if (o && !o->tab && (o->uri_list || o->mime)) - drop_event(c, YASL_DROP_OVER); + drop_event(c, YASL_PRIVATE_DROP_OVER); } } static void drag_offer_clear(void *ud, struct wl_data_device *device) @@ -321,10 +321,10 @@ static void drag_offer_clear(void *ud, struct wl_data_device *device) (void)device; struct clipboard *c = ud; if (c->drag_offer && c->drag_offer->tab) - drag_event(c, YASL_DRAG_LEAVE, c->drag_x, c->drag_y); + drag_event(c, YASL_PRIVATE_DRAG_LEAVE, c->drag_x, c->drag_y); else if (c->drag_offer && !c->drag_offer->tab && (c->drag_offer->uri_list || c->drag_offer->mime)) - drop_event(c, YASL_DROP_LEAVE); + drop_event(c, YASL_PRIVATE_DROP_LEAVE); free_offer(c->drag_offer); c->drag_offer = NULL; c->drag_surface = NULL; @@ -336,10 +336,10 @@ static void drag_motion(void *ud, struct wl_data_device *device, uint32_t time, c->drag_x = x; c->drag_y = y; if (c->drag_offer && c->drag_offer->tab) - drag_event(c, YASL_DRAG_OVER, x, y); + drag_event(c, YASL_PRIVATE_DRAG_OVER, x, y); else if (c->drag_offer && !c->drag_offer->tab && (c->drag_offer->uri_list || c->drag_offer->mime)) - drop_event(c, YASL_DROP_OVER); + drop_event(c, YASL_PRIVATE_DROP_OVER); } static void drag_drop(void *ud, struct wl_data_device *device) { @@ -350,11 +350,11 @@ static void drag_drop(void *ud, struct wl_data_device *device) c->drag_offer = NULL; bool v3 = wl_data_offer_get_version(o->proxy) >= 3; if (o->tab) { - drag_event(c, YASL_DRAG_DROP, c->drag_x, c->drag_y); + drag_event(c, YASL_PRIVATE_DRAG_DROP, c->drag_x, c->drag_y); if (v3) wl_data_offer_finish(o->proxy); } else if (o && !o->tab && (o->uri_list || o->mime)) { - drop_event(c, YASL_DROP_LEAVE); + drop_event(c, YASL_PRIVATE_DROP_LEAVE); int fds[2]; if (pipe2(fds, O_CLOEXEC) == 0) { wl_data_offer_receive(o->proxy, o->uri_list ? "text/uri-list" : o->mime, fds[1]); @@ -522,7 +522,7 @@ struct wl_data_device *clipboard_data_device(const struct clipboard *c) } struct clipboard *clipboard_create(struct wl_display *display, struct wl_data_device_manager *manager, - struct wl_seat *seat, struct yasl_loop *loop, + struct wl_seat *seat, struct yasl_private_loop *loop, clipboard_receive_fn receive, void *ud) { if (!manager || !seat) return NULL; @@ -603,7 +603,7 @@ bool clipboard_copy(struct clipboard *c, const char *text, uint32_t serial) void clipboard_paste(struct clipboard *c, struct wl_surface *target) { if (c && target) - (void)clipboard_request_paste(c, YASL_CLIPBOARD, target, 0); + (void)clipboard_request_paste(c, YASL_PRIVATE_CLIPBOARD, target, 0); } bool clipboard_copy_primary(struct clipboard *c, const char *text, uint32_t serial) @@ -630,24 +630,24 @@ bool clipboard_copy_primary(struct clipboard *c, const char *text, uint32_t seri void clipboard_paste_primary(struct clipboard *c, struct wl_surface *target) { if (c && target) - (void)clipboard_request_paste(c, YASL_PRIMARY, target, 0); + (void)clipboard_request_paste(c, YASL_PRIVATE_PRIMARY, target, 0); } -bool clipboard_request_paste(struct clipboard *c, enum yasl_selection selection, +bool clipboard_request_paste(struct clipboard *c, enum yasl_private_selection selection, struct wl_surface *target, uint64_t request_id) { if (!c || !target || !c->receive) return false; int fds[2]; - if (selection == YASL_PRIMARY) { + if (selection == YASL_PRIVATE_PRIMARY) { if (!c->primary_device || !c->primary_selection || !c->primary_selection->mime) { c->receive(NULL, 0, false, target, request_id, - YASL_TRANSFER_UNAVAILABLE, c->ud); + YASL_PRIVATE_TRANSFER_UNAVAILABLE, c->ud); return true; } if (pipe2(fds, O_CLOEXEC) < 0) { c->receive(NULL, 0, false, target, request_id, - YASL_TRANSFER_FAILED, c->ud); + YASL_PRIVATE_TRANSFER_FAILED, c->ud); return true; } zwp_primary_selection_offer_v1_receive(c->primary_selection->proxy, @@ -656,12 +656,12 @@ bool clipboard_request_paste(struct clipboard *c, enum yasl_selection selection, } else { if (!c->device || !c->selection || !c->selection->mime) { c->receive(NULL, 0, false, target, request_id, - YASL_TRANSFER_UNAVAILABLE, c->ud); + YASL_PRIVATE_TRANSFER_UNAVAILABLE, c->ud); return true; } if (pipe2(fds, O_CLOEXEC) < 0) { c->receive(NULL, 0, false, target, request_id, - YASL_TRANSFER_FAILED, c->ud); + YASL_PRIVATE_TRANSFER_FAILED, c->ud); return true; } wl_data_offer_receive(c->selection->proxy, c->selection->mime, fds[1]); @@ -670,7 +670,7 @@ bool clipboard_request_paste(struct clipboard *c, enum yasl_selection selection, struct transfer *transfer = start_transfer_paste(c, fds[0], target, request_id); if (!transfer) { c->receive(NULL, 0, false, target, request_id, - YASL_TRANSFER_FAILED, c->ud); + YASL_PRIVATE_TRANSFER_FAILED, c->ud); return true; } if (wl_display_flush(c->display) < 0 && errno != EAGAIN) diff --git a/src/platform/wayland/clipboard.h b/src/platform/wayland/clipboard.h index 133cc14..d03df17 100644 --- a/src/platform/wayland/clipboard.h +++ b/src/platform/wayland/clipboard.h @@ -1,10 +1,10 @@ -#ifndef YASL_WAYLAND_CLIPBOARD_H -#define YASL_WAYLAND_CLIPBOARD_H +#ifndef YASL_PRIVATE_WAYLAND_CLIPBOARD_H +#define YASL_PRIVATE_WAYLAND_CLIPBOARD_H #include <stdint.h> #include <stddef.h> #include <stdbool.h> -#include "yasl/yasl.h" +#include "common/types.h" struct wl_display; struct wl_data_device_manager; @@ -16,27 +16,27 @@ struct zwp_primary_selection_device_manager_v1; typedef void (*clipboard_receive_fn)(const char *, size_t, bool uri_list, struct wl_surface *on, uint64_t request_id, - enum yasl_transfer_status, void *); + enum yasl_private_transfer_status, void *); struct clipboard *clipboard_create(struct wl_display *, struct wl_data_device_manager *, - struct wl_seat *, struct yasl_loop *, + struct wl_seat *, struct yasl_private_loop *, clipboard_receive_fn, void *); void clipboard_destroy(struct clipboard *); bool clipboard_set_seat(struct clipboard *, struct wl_seat *); void clipboard_set_primary_manager(struct clipboard *, struct zwp_primary_selection_device_manager_v1 *); bool clipboard_copy(struct clipboard *, const char *, uint32_t); void clipboard_paste(struct clipboard *, struct wl_surface *target); -bool clipboard_request_paste(struct clipboard *, enum yasl_selection, +bool clipboard_request_paste(struct clipboard *, enum yasl_private_selection, struct wl_surface *target, uint64_t request_id); void clipboard_forget_surface(struct clipboard *, struct wl_surface *); bool clipboard_copy_primary(struct clipboard *, const char *, uint32_t); void clipboard_paste_primary(struct clipboard *, struct wl_surface *target); -typedef void (*clipboard_drag_fn)(enum yasl_drag_event, struct wl_surface *on, +typedef void (*clipboard_drag_fn)(enum yasl_private_drag_event, struct wl_surface *on, double x, double y, void *); void clipboard_set_drag_handler(struct clipboard *, clipboard_drag_fn, void *); -typedef void (*clipboard_drop_fn)(enum yasl_drop_event, struct wl_surface *on, +typedef void (*clipboard_drop_fn)(enum yasl_private_drop_event, struct wl_surface *on, double x, double y, void *); void clipboard_set_drop_handler(struct clipboard *, clipboard_drop_fn, void *); const char *clipboard_drag_mime(void); diff --git a/src/platform/wayland/drag.c b/src/platform/wayland/drag.c index 1f4a78b..07e31b5 100644 --- a/src/platform/wayland/drag.c +++ b/src/platform/wayland/drag.c @@ -11,13 +11,13 @@ static void icon_clear(struct drag_icon *icon) *icon = (struct drag_icon){0}; } -static bool icon_create(struct wayland_display *c, const struct yasl_drag_image *image) +static bool icon_create(struct wayland_display *c, const struct yasl_private_drag_image *image) { struct drag_icon *icon = &c->drag.icon; size_t bytes; if (!image->pixels || !buffer_size(image->size.x, image->size.y, &bytes)) return false; - int fd = memfd_create("yasl_drag_icon", MFD_CLOEXEC | MFD_ALLOW_SEALING); + int fd = memfd_create("yasl_private_drag_icon", MFD_CLOEXEC | MFD_ALLOW_SEALING); if (fd < 0) return false; void *mapping = MAP_FAILED; @@ -48,7 +48,7 @@ static bool icon_create(struct wayland_display *c, const struct yasl_drag_image return true; } -static void icon_show(struct wayland_display *c, const struct yasl_drag_image *image) +static void icon_show(struct wayland_display *c, const struct yasl_private_drag_image *image) { struct drag_icon *icon = &c->drag.icon; uint32_t scale = c->drag.scale_120; @@ -56,18 +56,18 @@ static void icon_show(struct wayland_display *c, const struct yasl_drag_image *i icon->viewport = wp_viewporter_get_viewport(c->viewporter, icon->surface); if (icon->viewport) wp_viewport_set_destination(icon->viewport, - yasl_scale_to_logical(image->size.x, scale), - yasl_scale_to_logical(image->size.y, scale)); - } else if (scale % YASL_SCALE_UNIT == 0 && + yasl_private_scale_to_logical(image->size.x, scale), + yasl_private_scale_to_logical(image->size.y, scale)); + } else if (scale % YASL_PRIVATE_SCALE_UNIT == 0 && wl_proxy_get_version((struct wl_proxy *)icon->surface) >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) { - wl_surface_set_buffer_scale(icon->surface, (int32_t)(scale / YASL_SCALE_UNIT)); + wl_surface_set_buffer_scale(icon->surface, (int32_t)(scale / YASL_PRIVATE_SCALE_UNIT)); } if (wl_proxy_get_version((struct wl_proxy *)icon->surface) >= WL_SURFACE_OFFSET_SINCE_VERSION) wl_surface_offset(icon->surface, - -yasl_scale_to_logical(image->hotspot.x, scale), - -yasl_scale_to_logical(image->hotspot.y, scale)); + -yasl_private_scale_to_logical(image->hotspot.x, scale), + -yasl_private_scale_to_logical(image->hotspot.y, scale)); wl_surface_attach(icon->surface, icon->buffer, 0, 0); wl_surface_damage_buffer(icon->surface, 0, 0, image->size.x, image->size.y); wl_surface_commit(icon->surface); @@ -88,9 +88,9 @@ static void drag_clear(struct wayland_display *c, bool ended) *d = (struct conn_drag){0}; } -static void drag_finish(struct wayland_display *c, enum yasl_drag_end how, bool ended) +static void drag_finish(struct wayland_display *c, enum yasl_private_drag_end how, bool ended) { - struct yasl_drag_listener l = c->drag.listener; + struct yasl_private_drag_listener l = c->drag.listener; void *ud = c->drag.ud; drag_clear(c, ended); if (l.end) @@ -100,7 +100,7 @@ static void drag_finish(struct wayland_display *c, enum yasl_drag_end how, bool void conn_drag_end(struct wayland_display *c) { if (c && c->drag.source) - drag_finish(c, YASL_DRAG_CANCELLED, false); + drag_finish(c, YASL_PRIVATE_DRAG_CANCELLED, false); } static void source_target(void *ud, struct wl_data_source *s, const char *mime) { (void)ud; (void)s; (void)mime; } @@ -112,7 +112,7 @@ static void source_cancelled(void *ud, struct wl_data_source *s) (void)s; struct wayland_display *c = ud; bool released = c->drag.dropped; - drag_finish(c, released ? YASL_DRAG_DROPPED : YASL_DRAG_CANCELLED, true); + drag_finish(c, released ? YASL_PRIVATE_DRAG_DROPPED : YASL_PRIVATE_DRAG_CANCELLED, true); } static void source_dropped(void *ud, struct wl_data_source *s) @@ -124,7 +124,7 @@ static void source_dropped(void *ud, struct wl_data_source *s) static void source_finished(void *ud, struct wl_data_source *s) { (void)s; - drag_finish(ud, YASL_DRAG_DROPPED, true); + drag_finish(ud, YASL_PRIVATE_DRAG_DROPPED, true); } static const struct wl_data_source_listener source_listener = { @@ -150,8 +150,8 @@ static struct seat *drag_seat(struct wayland_display *c, struct wayland_surface } bool wayland_display_drag_start(struct wayland_display *c, struct wayland_surface *origin, - const struct yasl_drag_image *image, - const struct yasl_drag_listener *l, void *ud) + const struct yasl_private_drag_image *image, + const struct yasl_private_drag_listener *l, void *ud) { if (!c || !origin || origin->conn != c || !l || c->drag.source) return false; @@ -190,39 +190,39 @@ bool wayland_display_drag_start(struct wayland_display *c, struct wayland_surfac void wayland_display_drag_cancel(struct wayland_display *c) { if (c && c->drag.source) - drag_finish(c, YASL_DRAG_CANCELLED, false); + drag_finish(c, YASL_PRIVATE_DRAG_CANCELLED, false); } -bool wayland_surface_drag_carry(struct wayland_surface *w, struct yasl_vector2i offset) +bool wayland_surface_drag_carry(struct wayland_surface *w, struct yasl_private_vector2i offset) { struct wayland_display *c = w ? w->conn : NULL; if (!c || !c->drag.source || !c->drag.toplevel || !w->toplevel) return false; uint32_t scale = c->drag.scale_120; xdg_toplevel_drag_v1_attach(c->drag.toplevel, w->toplevel, - yasl_scale_to_logical(offset.x, scale), - yasl_scale_to_logical(offset.y, scale)); + yasl_private_scale_to_logical(offset.x, scale), + yasl_private_scale_to_logical(offset.y, scale)); c->drag.carrying = true; return true; } -bool wayland_surface_place_under_pointer(struct wayland_surface *w, struct yasl_vector2i at) +bool wayland_surface_place_under_pointer(struct wayland_surface *w, struct yasl_private_vector2i at) { (void)w; (void)at; return false; } -static struct yasl_vector2i drag_device_pos(const struct wayland_surface *w, double x, double y) +static struct yasl_private_vector2i drag_device_pos(const struct wayland_surface *w, double x, double y) { double dx = x * w->scale_factor, dy = y * w->scale_factor; - return (struct yasl_vector2i){ + return (struct yasl_private_vector2i){ .x = (int)(dx < 0 ? dx - 0.5 : dx + 0.5), .y = (int)(dy < 0 ? dy - 0.5 : dy + 0.5), }; } -void conn_drop_target(struct wayland_display *c, enum yasl_drop_event ev, +void conn_drop_target(struct wayland_display *c, enum yasl_private_drop_event ev, struct wl_surface *on, double x, double y) { (void)c; @@ -230,10 +230,10 @@ void conn_drop_target(struct wayland_display *c, enum yasl_drop_event ev, (struct wayland_surface *)wl_surface_get_user_data(on) : NULL; if (!w || w->parent || w->closed) return; - yasl_surface_notify_drop(&w->base, ev, drag_device_pos(w, x, y)); + yasl_private_surface_notify_drop(&w->base, ev, drag_device_pos(w, x, y)); } -void conn_drag_target(struct wayland_display *c, enum yasl_drag_event ev, +void conn_drag_target(struct wayland_display *c, enum yasl_private_drag_event ev, struct wl_surface *on, double x, double y) { (void)c; @@ -241,5 +241,5 @@ void conn_drag_target(struct wayland_display *c, enum yasl_drag_event ev, (struct wayland_surface *)wl_surface_get_user_data(on) : NULL; if (!w || w->parent || w->closed) return; - yasl_surface_notify_drag(&w->base, ev, drag_device_pos(w, x, y)); + yasl_private_surface_notify_drag(&w->base, ev, drag_device_pos(w, x, y)); } diff --git a/src/platform/wayland/ime.c b/src/platform/wayland/ime.c index 4a091d8..62e5936 100644 --- a/src/platform/wayland/ime.c +++ b/src/platform/wayland/ime.c @@ -1,5 +1,7 @@ #include "internal.h" +#include <errno.h> + #define IME_TEXT_MAX (256u * 1024) static void show_preedit(struct wayland_surface *w, char *text, int begin, int end) @@ -12,7 +14,7 @@ static void show_preedit(struct wayland_surface *w, char *text, int begin, int e w->ime_shown_begin = begin; w->ime_shown_end = end; if (changed) { - yasl_surface_notify_composition(&w->base, w->ime_shown, + yasl_private_surface_notify_composition(&w->base, w->ime_shown, w->ime_shown_begin, w->ime_shown_end); wayland_surface_schedule_redraw(w); } @@ -24,21 +26,63 @@ void ime_reset(struct wayland_surface *w) w->ime_preedit = NULL; free(w->ime_commit); w->ime_commit = NULL; + w->ime_delete_pending = false; show_preedit(w, NULL, -1, -1); } void ime_send_caret(struct wayland_surface *w) { + wayland_surface_update_text_input(w); +} + +bool wayland_surface_update_text_input(struct wayland_surface *w) +{ + if (!w || !w->conn) { + errno = EINVAL; + return false; + } + if (!w->conn->text_input_mgr) { + errno = ENOTSUP; + return false; + } struct seat *seat = w->ime_seat; - if (!seat || !seat->text_input) - return; - uint32_t scale = w->scale_120 ? w->scale_120 : YASL_SCALE_UNIT; + if (!seat || !seat->text_input || seat->ime_focus != w) + return true; + if (!w->base.text_input_active) { + if (seat->ime_enabled) { + zwp_text_input_v3_disable(seat->text_input); + zwp_text_input_v3_commit(seat->text_input); + seat->ime_enabled = false; + } + ime_reset(w); + return true; + } + if (!seat->ime_enabled) { + zwp_text_input_v3_enable(seat->text_input); + seat->ime_enabled = true; + } + unsigned purpose = w->base.text_purpose; + if (purpose > ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL) + purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL; + zwp_text_input_v3_set_content_type(seat->text_input, + ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, purpose); + const char *surrounding = w->base.text_surrounding ? w->base.text_surrounding : ""; + size_t length = w->base.text_surrounding ? w->base.text_length : 0; + size_t cursor = w->base.text_surrounding ? w->base.text_end : 0; + size_t anchor = w->base.text_surrounding ? w->base.text_begin : 0; + if (cursor > length) cursor = length; + if (anchor > length) anchor = length; + if (length <= INT32_MAX && cursor <= INT32_MAX && anchor <= INT32_MAX) + zwp_text_input_v3_set_surrounding_text(seat->text_input, surrounding, + (int32_t)cursor, (int32_t)anchor); + uint32_t scale = w->scale_120 ? w->scale_120 : YASL_PRIVATE_SCALE_UNIT; zwp_text_input_v3_set_cursor_rectangle(seat->text_input, - yasl_scale_to_logical(w->ime_caret.x, scale), - yasl_scale_to_logical(w->ime_caret.y, scale), - yasl_scale_to_logical(w->ime_caret.width, scale), - yasl_scale_to_logical(w->ime_caret.height, scale)); + yasl_private_scale_to_logical(w->ime_caret.x, scale), + yasl_private_scale_to_logical(w->ime_caret.y, scale), + yasl_private_scale_to_logical(w->ime_caret.width, scale), + yasl_private_scale_to_logical(w->ime_caret.height, scale)); zwp_text_input_v3_commit(seat->text_input); + return true; } bool wayland_surface_ime_preedit_cursor(const struct wayland_surface *w, int *begin, int *end) @@ -50,7 +94,7 @@ bool wayland_surface_ime_preedit_cursor(const struct wayland_surface *w, int *be return true; } -void wayland_surface_set_ime_caret(struct wayland_surface *w, struct yasl_rect2i caret) +void wayland_surface_set_ime_caret(struct wayland_surface *w, struct yasl_private_rect2i caret) { if (!w || (w->ime_caret.x == caret.x && w->ime_caret.y == caret.y && w->ime_caret.width == caret.width && w->ime_caret.height == caret.height)) @@ -62,17 +106,14 @@ void wayland_surface_set_ime_caret(struct wayland_surface *w, struct yasl_rect2i static void text_input_enter(void *data, struct zwp_text_input_v3 *text_input, struct wl_surface *surface) { + (void)text_input; struct seat *s = data; struct wayland_surface *w = surface_root(surface ? (struct wayland_surface *)wl_surface_get_user_data(surface) : NULL); s->ime_focus = w; if (!w) return; - zwp_text_input_v3_enable(text_input); - zwp_text_input_v3_set_content_type(text_input, - ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, - ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL); w->ime_seat = s; - ime_send_caret(w); + wayland_surface_update_text_input(w); } static void text_input_leave(void *data, struct zwp_text_input_v3 *text_input, @@ -83,8 +124,11 @@ static void text_input_leave(void *data, struct zwp_text_input_v3 *text_input, struct wayland_surface *w = s->ime_focus; s->ime_focus = NULL; if (!w) return; - zwp_text_input_v3_disable(text_input); - zwp_text_input_v3_commit(text_input); + if (s->ime_enabled) { + zwp_text_input_v3_disable(text_input); + zwp_text_input_v3_commit(text_input); + s->ime_enabled = false; + } if (w->ime_seat == s) w->ime_seat = NULL; ime_reset(w); @@ -110,7 +154,7 @@ static void text_input_preedit_string(void *data, { (void)text_input; struct wayland_surface *w = ((struct seat *)data)->ime_focus; - if (!w) return; + if (!w || !w->base.text_input_active) return; keyboard_text_reset((struct seat *)data); free(w->ime_preedit); w->ime_preedit = text ? copy_text(text) : NULL; @@ -126,7 +170,7 @@ static void text_input_commit_string(void *data, { (void)text_input; struct wayland_surface *w = ((struct seat *)data)->ime_focus; - if (!w) return; + if (!w || !w->base.text_input_active) return; keyboard_text_reset((struct seat *)data); free(w->ime_commit); w->ime_commit = (text && text[0]) ? copy_text(text) : NULL; @@ -136,7 +180,31 @@ static void text_input_delete_surrounding_text(void *data, struct zwp_text_input_v3 *text_input, uint32_t before_length, uint32_t after_length) { - (void)data; (void)text_input; (void)before_length; (void)after_length; + (void)text_input; + struct seat *seat = data; + struct wayland_surface *w = seat->ime_focus; + if (!w || !w->base.text_input_active || !w->base.text_surrounding) + return; + size_t length = w->base.text_length; + size_t cursor = w->base.text_end <= length ? w->base.text_end : length; + while (cursor && cursor < length && + (((unsigned char)w->base.text_surrounding[cursor] & 0xc0) == 0x80)) + cursor--; + size_t begin = before_length > cursor ? 0 : cursor - before_length; + size_t end = after_length > length - cursor ? length : cursor + after_length; + while (begin && begin < length && + (((unsigned char)w->base.text_surrounding[begin] & 0xc0) == 0x80)) + begin--; + while (end < length && (((unsigned char)w->base.text_surrounding[end] & 0xc0) == 0x80)) + end++; + /* text_input_v3 batches deletion and commit into one `done` group. Keep + * the replacement range paired with that group's committed text and + * enqueue both together below, preserving snapshot metadata. */ + w->ime_delete_pending = true; + w->ime_delete_begin = begin; + w->ime_delete_end = end; + w->ime_delete_revision = w->base.text_revision; + w->ime_delete_generation = w->base.text_generation; } static void text_input_done(void *data, struct zwp_text_input_v3 *text_input, @@ -144,17 +212,46 @@ static void text_input_done(void *data, struct zwp_text_input_v3 *text_input, { (void)text_input; (void)serial; struct wayland_surface *w = ((struct seat *)data)->ime_focus; - if (!w) return; - if (w->ime_commit) { - struct yasl_event ev = { - .type = YASL_EVENT_TEXT, + if (!w || !w->base.text_input_active) return; + bool has_snapshot = w->base.text_surrounding != NULL && + w->base.text_length <= INT32_MAX; + bool has_commit = w->ime_commit && w->ime_commit[0]; + bool edit = w->ime_delete_pending || (has_commit && has_snapshot); + if (edit) { + size_t begin = w->ime_delete_pending ? w->ime_delete_begin : w->base.text_begin; + size_t end = w->ime_delete_pending ? w->ime_delete_end : w->base.text_end; + if (begin > w->base.text_length) begin = w->base.text_length; + if (end > w->base.text_length) end = w->base.text_length; + if (end < begin) end = begin; + struct yasl_private_event ev = { + .type = YASL_PRIVATE_EVENT_TEXT_EDIT, + .surface = &w->base, + .text_edit = { + .data = has_commit ? w->ime_commit : "", + .len = has_commit ? strlen(w->ime_commit) : 0, + .revision = w->ime_delete_revision, + .generation = w->ime_delete_generation, + .begin = begin, + .end = end, + }, + }; + if (!w->ime_delete_pending) { + ev.text_edit.revision = w->base.text_revision; + ev.text_edit.generation = w->base.text_generation; + } + input_queue_push(&w->input, &ev); + w->ime_delete_pending = false; + } + if (w->ime_commit && !edit) { + struct yasl_private_event ev = { + .type = YASL_PRIVATE_EVENT_TEXT, .surface = &w->base, .text = { .data = w->ime_commit, .len = strlen(w->ime_commit) }, }; input_queue_push(&w->input, &ev); - free(w->ime_commit); - w->ime_commit = NULL; } + free(w->ime_commit); + w->ime_commit = NULL; char *preedit = w->ime_preedit && w->ime_preedit[0] ? w->ime_preedit : NULL; if (!preedit) free(w->ime_preedit); diff --git a/src/platform/wayland/internal.h b/src/platform/wayland/internal.h index 34d4133..a479e2b 100644 --- a/src/platform/wayland/internal.h +++ b/src/platform/wayland/internal.h @@ -1,5 +1,5 @@ -#ifndef YASL_WAYLAND_INTERNAL_H -#define YASL_WAYLAND_INTERNAL_H +#ifndef YASL_PRIVATE_WAYLAND_INTERNAL_H +#define YASL_PRIVATE_WAYLAND_INTERNAL_H #define _GNU_SOURCE @@ -10,6 +10,11 @@ #include <wayland-client.h> #include <wayland-cursor.h> +#if YASL_HAS_EGL +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <wayland-egl.h> +#endif #include "protocols/xdg-shell-client-protocol.h" #include "protocols/xdg-decoration-client-protocol.h" #include "protocols/cursor-shape-v1-client-protocol.h" @@ -73,7 +78,7 @@ struct wl_conn_output { struct pending_press { struct wayland_surface *surface; - struct yasl_vector2i pos; + struct yasl_private_vector2i pos; int button; bool release; }; @@ -105,7 +110,7 @@ struct conn_drag { struct wl_data_source *source; struct xdg_toplevel_drag_v1 *toplevel; struct drag_icon icon; - struct yasl_drag_listener listener; + struct yasl_private_drag_listener listener; void *ud; struct seat *seat; uint32_t scale_120; @@ -160,15 +165,20 @@ struct seat { uint64_t pointer_buttons; uint64_t tool_buttons; double axis[2], axis_value120[2]; + double axis_pixels[2]; int64_t axis_discrete[2]; + uint32_t axis_source; + bool scroll_gesture, axis_stop_pending; + unsigned axis_phase; + bool ime_enabled; bool axis_has_value120[2], axis_has_discrete[2]; double touch_scroll_y; /* Embedded structs */ xkb_mod_mask_t input_mod_masks[8]; - struct yasl_vector2i pointer_pos; - struct yasl_vector2i ptr_left_pos; - struct yasl_vector2i tool_pos; + struct yasl_private_vector2i pointer_pos; + struct yasl_private_vector2i ptr_left_pos; + struct yasl_private_vector2i tool_pos; struct touch_point touch_points[SEAT_TOUCH_POINTS]; struct press_queue ptr_presses; struct press_queue tool_presses; @@ -183,7 +193,7 @@ struct seat { uint32_t press_serial; uint32_t pointer_serial; int cursor_shape; - struct yasl_pointer_pen pen; + struct yasl_private_pointer_pen pen; int tool_wheel; /* 1-byte booleans grouped tightly */ @@ -199,9 +209,14 @@ struct seat { #include "common/input.h" struct wayland_display { - struct yasl_display base; + struct yasl_private_display base; /* 8-byte pointers & 64-bit masks */ struct wl_display *wl; +#if YASL_HAS_EGL + EGLDisplay egl_display; + EGLint egl_api; + bool egl_initialized; +#endif struct wl_registry *registry; struct wl_compositor *compositor; struct wl_shm *shm; @@ -228,8 +243,8 @@ struct wayland_display { struct wp_fifo_manager_v1 *fifo_mgr; struct wp_commit_timing_manager_v1 *commit_timing_mgr; struct xkb_context *xkb_ctx; - struct yasl_loop *loop; - const struct yasl_display_cbs *cbs; + struct yasl_private_loop *loop; + const struct yasl_private_display_cbs *cbs; void *ud; char *app_id; struct xdg_toplevel_drag_manager_v1 *toplevel_drag_mgr; @@ -243,6 +258,7 @@ struct wayland_display { clockid_t pres_clock; int watch; int depth; + uint64_t next_tool_id; /* 1-byte booleans */ bool owns_loop; @@ -266,6 +282,12 @@ struct wayland_surface { struct wayland_surface *dead_next; struct wayland_surface *parent; struct wl_surface *surface; +#if YASL_HAS_EGL + struct wl_egl_window *egl_window; + EGLSurface egl_surface; + EGLContext egl_context; + EGLConfig egl_config; +#endif struct xdg_surface *xdg_surface; struct xdg_toplevel *toplevel; struct xdg_popup *popup; @@ -276,6 +298,8 @@ struct wayland_surface { char *ime_commit; char *ime_shown; struct seat *ime_seat; + size_t ime_delete_begin, ime_delete_end; + uint64_t ime_delete_revision, ime_delete_generation; struct wp_tearing_control_v1 *tearing_control; struct latency_feedback *latency_feedbacks; struct latency_feedback latency_pool[8]; @@ -285,24 +309,24 @@ struct wayland_surface { /* Persistent lock held while relative; lock_seat owns the locked wl_pointer. */ struct zwp_locked_pointer_v1 *pointer_lock; struct seat *lock_seat; - const struct yasl_window_cbs *cbs; + const struct yasl_private_window_cbs *cbs; void *ud; uint64_t frames_skipped; uint64_t outputs; double scale_factor; /* Sub-structures */ - struct yasl_rect2i ime_caret; - struct yasl_rect2i content; - struct yasl_frame_clock clock; + struct yasl_private_rect2i ime_caret; + struct yasl_private_rect2i content; + struct yasl_private_frame_clock clock; struct buffer buffers[NBUF]; - struct yasl_frame frame; - struct yasl_damage_list damage; + struct yasl_private_frame frame; + struct yasl_private_damage_list damage; int ime_cursor_begin; struct input_queue input; /* 4-byte integers */ - enum yasl_cursor cursor; + enum yasl_private_cursor cursor; int ime_cursor_end; int ime_shown_begin, ime_shown_end; int floating_width, floating_height; @@ -331,6 +355,7 @@ struct wayland_surface { bool want_draw; bool relative; bool pointer_locked; + bool ime_delete_pending; }; @@ -343,7 +368,7 @@ int buffer_prepare(struct wayland_surface *w, struct buffer *b, int width, int const struct wl_buffer_listener *listener); int apply_pending_geometry(struct wayland_surface *w); -static inline void wl_damage_list(struct wl_surface *s, const struct yasl_damage_list *d) +static inline void wl_damage_list(struct wl_surface *s, const struct yasl_private_damage_list *d) { if (d->n < 0) { wl_surface_damage_buffer(s, d->bounds.x, d->bounds.y, @@ -365,6 +390,7 @@ void surface_follow_outputs(struct wayland_surface *w); uint64_t conn_pres_to_mono(const struct wayland_display *c, uint64_t ns); uint64_t conn_mono_to_pres(const struct wayland_display *c, uint64_t ns); void surface_draw_frame(struct wayland_surface *w); +void wayland_surface_arm_frame(struct wayland_surface *w); void surface_fail(struct wayland_surface *w); void display_leave(struct wayland_display *c); @@ -376,9 +402,9 @@ static inline struct wayland_surface *surface_root(struct wayland_surface *w) } void conn_drag_end(struct wayland_display *c); -void conn_drop_target(struct wayland_display *c, enum yasl_drop_event ev, +void conn_drop_target(struct wayland_display *c, enum yasl_private_drop_event ev, struct wl_surface *on, double x, double y); -void conn_drag_target(struct wayland_display *c, enum yasl_drag_event ev, +void conn_drag_target(struct wayland_display *c, enum yasl_private_drag_event ev, struct wl_surface *on, double x, double y); void seat_bind(struct wayland_display *c, struct wl_registry *registry, @@ -386,6 +412,7 @@ void seat_bind(struct wayland_display *c, struct wl_registry *registry, bool seat_remove(struct wayland_display *c, uint32_t name); void seat_destroy(struct seat *s); void seat_note_input(struct seat *s, uint32_t serial); +void seat_ensure_text_input(struct seat *s); void seat_forget_surface(struct seat *s, struct wayland_surface *w); void seat_clipboard_ensure(struct seat *s); int display_input_init(struct wayland_display *c); @@ -409,6 +436,7 @@ extern const struct zwp_text_input_v3_listener text_input_listener; void ime_reset(struct wayland_surface *w); void keyboard_text_reset(struct seat *s); void ime_send_caret(struct wayland_surface *w); +bool wayland_surface_update_text_input(struct wayland_surface *w); int surface_init_shell(struct wayland_surface *w, const char *title); extern const struct xdg_surface_listener shell_surface_listener; diff --git a/src/platform/wayland/keyboard.c b/src/platform/wayland/keyboard.c index 7f8274d..6f9dec9 100644 --- a/src/platform/wayland/keyboard.c +++ b/src/platform/wayland/keyboard.c @@ -133,8 +133,8 @@ bool conn_keyboard_on(const struct wayland_display *c, const struct wayland_surf static void push_focus(struct wayland_surface *w, bool focused) { - struct yasl_event ev = { - .type = YASL_EVENT_FOCUS, + struct yasl_private_event ev = { + .type = YASL_PRIVATE_EVENT_FOCUS, .surface = &w->base, .focus = { .focused = focused }, }; @@ -218,18 +218,18 @@ static uint32_t key_codepoint_at_level(struct seat *s, uint32_t key, xkb_layout_ return n == 1 ? xkb_keysym_to_utf32(syms[0]) : 0; } -static struct yasl_key_event xkb_key_event(struct seat *s, uint32_t key, +static struct yasl_private_key_event xkb_key_event(struct seat *s, uint32_t key, enum yasl_key_action action) { uint32_t code = key + XKB_EVDEV_OFFSET; xkb_layout_index_t layout = xkb_state_key_get_layout(s->xkb_state, code); - struct yasl_key_event k = { - .key = yasl_key_from_evdev(key), + struct yasl_private_key_event k = { + .key = yasl_private_key_from_evdev(key), .action = action, .mods = input_modifiers(s), - .codepoint = yasl_key_label(key_codepoint_at_level(s, key, layout, 0)), - .shifted = yasl_key_label(key_codepoint_at_level(s, key, layout, 1)), + .codepoint = yasl_private_key_label(key_codepoint_at_level(s, key, layout, 0)), + .shifted = yasl_private_key_label(key_codepoint_at_level(s, key, layout, 1)), .native_code = key, }; return k; @@ -239,8 +239,8 @@ static void keyboard_text(struct seat *s, struct wayland_surface *w, uint32_t ke { uint32_t code = key + XKB_EVDEV_OFFSET; unsigned mods = input_modifiers(s); - if ((mods & (YASL_MOD_CTRL | YASL_MOD_SUPER)) || - ((mods & YASL_MOD_ALT) && + if ((mods & (YASL_PRIVATE_MOD_CTRL | YASL_PRIVATE_MOD_SUPER)) || + ((mods & YASL_PRIVATE_MOD_ALT) && !xkb_state_mod_index_is_consumed(s->xkb_state, code, s->mod_alt))) { keyboard_text_reset(s); return; @@ -278,13 +278,13 @@ static void keyboard_text(struct seat *s, struct wayland_surface *w, uint32_t ke else xkb_state_key_get_utf8(s->xkb_state, code, text, (size_t)len + 1); /* Editing commands belong to key events, not text insertion. */ if ((unsigned char)text[0] >= 0x20 && text[0] != 0x7f) { - struct yasl_event ev = {.type = YASL_EVENT_TEXT, + struct yasl_private_event ev = {.type = YASL_PRIVATE_EVENT_TEXT, .surface = &w->base, .text = {.data = text, .len = (size_t)len}}; input_queue_push(&w->input, &ev); } if (text != local) free(text); } else { - struct yasl_event ev = {.type = YASL_EVENT_INPUT_RESET, .surface = &w->base}; + struct yasl_private_event ev = {.type = YASL_PRIVATE_EVENT_INPUT_RESET, .surface = &w->base}; input_queue_push(&w->input, &ev); } } @@ -296,9 +296,9 @@ static bool keyboard_dispatch_key(struct seat *s, struct wayland_surface *w, uin { if (!s->xkb_state || !w->cbs) return false; - struct yasl_key_event k = xkb_key_event(s, key, action); - struct yasl_event ev = { - .type = YASL_EVENT_KEY, + struct yasl_private_key_event k = xkb_key_event(s, key, action); + struct yasl_private_event ev = { + .type = YASL_PRIVATE_EVENT_KEY, .surface = &w->base, .key = k, }; @@ -329,7 +329,7 @@ static void keyboard_key(void *data, struct wl_keyboard *keyboard, if (keyboard_dispatch_key(s, w, key, YASL_KEY_PRESS) && repeatable) { s->repeat_key = key; s->repeat_held = true; - s->repeat_started_ns = yasl_loop_now_ns(); + s->repeat_started_ns = yasl_private_loop_now_ns(); keyboard_repeat_sync(s); } } @@ -389,7 +389,7 @@ int keyboard_repeat_init(struct seat *s) if (s->repeat_fd >= 0) return 0; s->repeat_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); if (s->repeat_fd < 0) return -1; - s->repeat_watch = yasl_loop_add_fd(s->conn->loop, s->repeat_fd, POLLIN, + s->repeat_watch = yasl_private_loop_add_fd(s->conn->loop, s->repeat_fd, POLLIN, keyboard_repeat_ready, s); if (s->repeat_watch <= 0) { s->repeat_watch = 0; @@ -406,18 +406,18 @@ int seat_modifiers(const struct seat *s) if (unlikely(!s || !s->xkb_state)) return 0; return ((s->mod_shift != XKB_MOD_INVALID && xkb_state_mod_index_is_active(s->xkb_state, s->mod_shift, - XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_MOD_SHIFT) | + XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_PRIVATE_MOD_SHIFT) | ((s->mod_alt != XKB_MOD_INVALID && xkb_state_mod_index_is_active(s->xkb_state, s->mod_alt, - XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_MOD_ALT) | + XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_PRIVATE_MOD_ALT) | ((s->mod_ctrl != XKB_MOD_INVALID && xkb_state_mod_index_is_active(s->xkb_state, s->mod_ctrl, - XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_MOD_CTRL) | + XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_PRIVATE_MOD_CTRL) | ((s->mod_super != XKB_MOD_INVALID && xkb_state_mod_index_is_active(s->xkb_state, s->mod_super, - XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_MOD_SUPER) | + XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_PRIVATE_MOD_SUPER) | ((xkb_state_mod_name_is_active(s->xkb_state, XKB_MOD_NAME_CAPS, - XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_MOD_CAPS); + XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_PRIVATE_MOD_CAPS); } int wayland_display_modifiers(const struct wayland_display *c) @@ -425,12 +425,12 @@ int wayland_display_modifiers(const struct wayland_display *c) return c ? seat_modifiers(c->active_seat ? c->active_seat : c->seats) : 0; } -void wayland_display_copy(struct wayland_display *c, enum yasl_selection sel, const char *text) +void wayland_display_copy(struct wayland_display *c, enum yasl_private_selection sel, const char *text) { (void)wayland_display_copy_text(c, sel, text); } -bool wayland_display_copy_text(struct wayland_display *c, enum yasl_selection sel, +bool wayland_display_copy_text(struct wayland_display *c, enum yasl_private_selection sel, const char *text) { struct seat *s = c ? (c->active_seat ? c->active_seat : c->seats) : NULL; @@ -447,28 +447,28 @@ bool wayland_display_copy_text(struct wayland_display *c, enum yasl_selection se return false; } errno = 0; - bool ok = sel == YASL_PRIMARY ? + bool ok = sel == YASL_PRIVATE_PRIMARY ? clipboard_copy_primary(s->clipboard, text, s->input_serial) : clipboard_copy(s->clipboard, text, s->input_serial); return ok; } -void wayland_display_paste(struct wayland_display *c, enum yasl_selection sel, struct wayland_surface *target) +void wayland_display_paste(struct wayland_display *c, enum yasl_private_selection sel, struct wayland_surface *target) { struct seat *s = c ? (c->active_seat ? c->active_seat : c->seats) : NULL; if (!target || target->closed || target->destroyed) return; if (!s || !s->clipboard) { - yasl_surface_complete_paste(&target->base, 0, - YASL_TRANSFER_UNAVAILABLE, NULL, 0); + yasl_private_surface_complete_paste(&target->base, 0, + YASL_PRIVATE_TRANSFER_UNAVAILABLE, NULL, 0); return; } - if (sel == YASL_PRIMARY) + if (sel == YASL_PRIVATE_PRIMARY) clipboard_paste_primary(s->clipboard, target->surface); else clipboard_paste(s->clipboard, target->surface); } -bool wayland_display_request_paste(struct wayland_display *c, enum yasl_selection sel, +bool wayland_display_request_paste(struct wayland_display *c, enum yasl_private_selection sel, struct wayland_surface *target, uint64_t request_id) { struct seat *s = c ? (c->active_seat ? c->active_seat : c->seats) : NULL; @@ -486,7 +486,7 @@ bool wayland_display_request_paste(struct wayland_display *c, enum yasl_selectio return ok; } -bool wayland_surface_poll_event(struct wayland_surface *w, struct yasl_event *ev) +bool wayland_surface_poll_event(struct wayland_surface *w, struct yasl_private_event *ev) { return w && ev && input_queue_pop(&w->input, ev); } diff --git a/src/platform/wayland/keymap.c b/src/platform/wayland/keymap.c index d626f30..6c3b4dd 100644 --- a/src/platform/wayland/keymap.c +++ b/src/platform/wayland/keymap.c @@ -1,7 +1,7 @@ #include "keymap.h" /* Numeric Linux evdev codes; no platform headers are needed by this table. */ -enum yasl_key yasl_key_from_evdev(uint32_t code) +enum yasl_key yasl_private_key_from_evdev(uint32_t code) { switch (code) { case 1: return YASL_KEY_ESCAPE; /* KEY_ESC */ diff --git a/src/platform/wayland/keymap.h b/src/platform/wayland/keymap.h index c0ff81e..26b6ed4 100644 --- a/src/platform/wayland/keymap.h +++ b/src/platform/wayland/keymap.h @@ -1,8 +1,8 @@ -#ifndef YASL_WAYLAND_KEYMAP_H -#define YASL_WAYLAND_KEYMAP_H +#ifndef YASL_PRIVATE_WAYLAND_KEYMAP_H +#define YASL_PRIVATE_WAYLAND_KEYMAP_H #include "yasl/keycodes.h" -enum yasl_key yasl_key_from_evdev(uint32_t code); +enum yasl_key yasl_private_key_from_evdev(uint32_t code); #endif diff --git a/src/platform/wayland/native.c b/src/platform/wayland/native.c index 55ce445..54ca184 100644 --- a/src/platform/wayland/native.c +++ b/src/platform/wayland/native.c @@ -1,14 +1,14 @@ #include "common/backend.h" -#include "yasl/native/wayland.h" +#include "common/private_native_wayland.h" -struct wl_display *yasl_wayland_display_get_wl_display(struct yasl_display *d) +struct wl_display *yasl_private_wayland_display_get_wl_display(struct yasl_private_display *d) { - return (d && yasl_display_backend(d) == YASL_BACKEND_WAYLAND) ? - (struct wl_display *)yasl_display_get_native_handle(d) : NULL; + return (d && yasl_private_display_backend(d) == YASL_PRIVATE_BACKEND_WAYLAND) ? + (struct wl_display *)yasl_private_display_get_native_handle(d) : NULL; } -struct wl_surface *yasl_wayland_surface_get_wl_surface(struct yasl_surface *s) +struct wl_surface *yasl_private_wayland_surface_get_wl_surface(struct yasl_surface *s) { - return (s && s->display && yasl_display_backend(s->display) == YASL_BACKEND_WAYLAND) ? - (struct wl_surface *)yasl_surface_get_native_handle(s) : NULL; + return (s && s->display && yasl_private_display_backend(s->display) == YASL_PRIVATE_BACKEND_WAYLAND) ? + (struct wl_surface *)yasl_private_surface_get_native_handle(s) : NULL; } diff --git a/src/platform/wayland/pointer.c b/src/platform/wayland/pointer.c index da666da..5f486ae 100644 --- a/src/platform/wayland/pointer.c +++ b/src/platform/wayland/pointer.c @@ -12,12 +12,22 @@ struct tablet_tool { struct zwp_tablet_tool_v2 *proxy; struct wp_cursor_shape_device_v1 *shape_dev; uint32_t proximity_serial; + uint64_t device_id, tool_id; + uint32_t axes_valid, axes_changed, capability_mask; + double x, y; + double pressure, tilt_x, tilt_y, rotation, distance, tangential, wheel_degrees; + unsigned changed_button; + unsigned pending_actions[8], pending_buttons[8], pending_action_count; + bool pending_contacts[8]; + unsigned kind; + bool in_contact, pen_in_proximity; bool eraser; }; struct tablet_device { struct tablet_device *next; struct zwp_tablet_v2 *proxy; + uint64_t id; }; struct tablet_pad { @@ -25,10 +35,10 @@ struct tablet_pad { struct zwp_tablet_pad_v2 *proxy; }; -static struct yasl_vector2i device_from_fixed(const struct wayland_surface *w, +static struct yasl_private_vector2i device_from_fixed(const struct wayland_surface *w, wl_fixed_t x, wl_fixed_t y) { - return (struct yasl_vector2i){ + return (struct yasl_private_vector2i){ .x = (int)lrint(wl_fixed_to_double(x) * w->scale_factor), .y = (int)lrint(wl_fixed_to_double(y) * w->scale_factor), }; @@ -41,10 +51,10 @@ void seat_note_input(struct seat *s, uint32_t serial) } static void push_pointer(struct seat *s, struct wayland_surface *w, - enum yasl_event_type type, struct yasl_pointer_event ev) + enum yasl_private_event_type type, struct yasl_private_pointer_event ev) { ev.mods = (uint32_t)seat_modifiers(s); - struct yasl_event input_ev = { + struct yasl_private_event input_ev = { .type = type, .surface = &w->base, .pointer = ev, @@ -52,13 +62,15 @@ static void push_pointer(struct seat *s, struct wayland_surface *w, input_queue_push(&surface_root(w)->input, &input_ev); } -static void report_wheel(struct seat *s, struct wayland_surface *w, - struct yasl_vector2i pos, double x, double y) +static unsigned scroll_source_of(uint32_t source) { - if ((x == 0 && y == 0) || !isfinite(x) || !isfinite(y)) return; - push_pointer(s, w, YASL_EVENT_POINTER_SCROLL, (struct yasl_pointer_event){ - .pos = pos, .scroll = {x, y}, - }); + switch (source) { + case WL_POINTER_AXIS_SOURCE_WHEEL: return 1; + case WL_POINTER_AXIS_SOURCE_WHEEL_TILT: return 1; + case WL_POINTER_AXIS_SOURCE_FINGER: return 2; + case WL_POINTER_AXIS_SOURCE_CONTINUOUS: return 3; + default: return 0; + } } static bool cursor_theme_for(struct wayland_display *c, int scale) @@ -90,10 +102,10 @@ static bool seat_cursor_surface(struct seat *s) } static void set_cursor_theme_fallback(struct seat *s, struct wayland_surface *w, - enum yasl_cursor shape) + enum yasl_private_cursor shape) { struct wayland_display *c = s->conn; - int scale = (int)((w->scale_120 + YASL_SCALE_UNIT - 1) / YASL_SCALE_UNIT); + int scale = (int)((w->scale_120 + YASL_PRIVATE_SCALE_UNIT - 1) / YASL_PRIVATE_SCALE_UNIT); if (scale < 1) scale = 1; if (!seat_cursor_surface(s) || !cursor_theme_for(c, scale)) return; static const char *names[][2] = { @@ -107,7 +119,7 @@ static void set_cursor_theme_fallback(struct seat *s, struct wayland_surface *w, {"ew-resize", "ew-resize"}, {"ns-resize", "ns-resize"}, }; - int idx = (unsigned)shape < YASL_CURSOR_HIDDEN ? (int)shape : 0; + int idx = (unsigned)shape < YASL_PRIVATE_CURSOR_HIDDEN ? (int)shape : 0; struct wl_cursor *cursor = wl_cursor_theme_get_cursor(c->cursor_theme, names[idx][0]); if (!cursor) cursor = wl_cursor_theme_get_cursor(c->cursor_theme, names[idx][1]); if (!cursor || !cursor->image_count) return; @@ -117,11 +129,11 @@ static void set_cursor_theme_fallback(struct seat *s, struct wayland_surface *w, int theme_scale = c->cursor_theme_scale; if (s->cursor_viewport) { - uint32_t scale_120 = (uint32_t)theme_scale * YASL_SCALE_UNIT; + uint32_t scale_120 = (uint32_t)theme_scale * YASL_PRIVATE_SCALE_UNIT; wp_viewport_set_destination(s->cursor_viewport, - (int)(((int64_t)(int)img->width * YASL_SCALE_UNIT + scale_120 - 1) / + (int)(((int64_t)(int)img->width * YASL_PRIVATE_SCALE_UNIT + scale_120 - 1) / scale_120), - (int)(((int64_t)(int)img->height * YASL_SCALE_UNIT + scale_120 - 1) / + (int)(((int64_t)(int)img->height * YASL_PRIVATE_SCALE_UNIT + scale_120 - 1) / scale_120)); } else { if (img->width % (uint32_t)theme_scale || img->height % (uint32_t)theme_scale) @@ -137,17 +149,17 @@ static void set_cursor_theme_fallback(struct seat *s, struct wayland_surface *w, wl_surface_commit(s->cursor_surface); } -static uint32_t cursor_shape_of(enum yasl_cursor shape) +static uint32_t cursor_shape_of(enum yasl_private_cursor shape) { switch (shape) { - case YASL_CURSOR_TEXT: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT; - case YASL_CURSOR_LINK: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER; - case YASL_CURSOR_CROSSHAIR: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR; - case YASL_CURSOR_MOVE: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE; - case YASL_CURSOR_RESIZE_EW: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE; - case YASL_CURSOR_RESIZE_NS: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE; - case YASL_CURSOR_RESIZE_NWSE: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE; - case YASL_CURSOR_RESIZE_NESW: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE; + case YASL_PRIVATE_CURSOR_TEXT: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT; + case YASL_PRIVATE_CURSOR_LINK: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER; + case YASL_PRIVATE_CURSOR_CROSSHAIR: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR; + case YASL_PRIVATE_CURSOR_MOVE: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE; + case YASL_PRIVATE_CURSOR_RESIZE_EW: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE; + case YASL_PRIVATE_CURSOR_RESIZE_NS: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE; + case YASL_PRIVATE_CURSOR_RESIZE_NWSE: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE; + case YASL_PRIVATE_CURSOR_RESIZE_NESW: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE; default: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT; } } @@ -155,11 +167,11 @@ static uint32_t cursor_shape_of(enum yasl_cursor shape) /* Relative mode hides the pointer but keeps w->cursor for the return to NORMAL. */ static void seat_set_cursor(struct seat *s, struct wayland_surface *w) { - enum yasl_cursor shape = w->relative ? YASL_CURSOR_HIDDEN : w->cursor; + enum yasl_private_cursor shape = w->relative ? YASL_PRIVATE_CURSOR_HIDDEN : w->cursor; if (!s->pointer || !s->pointer_serial || s->pointer_focus != w || s->cursor_shape == (int)shape) return; - if (shape == YASL_CURSOR_HIDDEN) + if (shape == YASL_PRIVATE_CURSOR_HIDDEN) wl_pointer_set_cursor(s->pointer, s->pointer_serial, NULL, 0, 0); else if (s->cursor_shape_dev) wp_cursor_shape_device_v1_set_shape(s->cursor_shape_dev, @@ -169,20 +181,20 @@ static void seat_set_cursor(struct seat *s, struct wayland_surface *w) s->cursor_shape = shape; } -static void tool_set_cursor(struct tablet_tool *t, enum yasl_cursor shape) +static void tool_set_cursor(struct tablet_tool *t, enum yasl_private_cursor shape) { if (!t->proximity_serial) return; - if (shape == YASL_CURSOR_HIDDEN) + if (shape == YASL_PRIVATE_CURSOR_HIDDEN) zwp_tablet_tool_v2_set_cursor(t->proxy, t->proximity_serial, NULL, 0, 0); else if (t->shape_dev) wp_cursor_shape_device_v1_set_shape(t->shape_dev, t->proximity_serial, cursor_shape_of(shape)); } -void wayland_surface_set_cursor(struct wayland_surface *w, enum yasl_cursor shape) +void wayland_surface_set_cursor(struct wayland_surface *w, enum yasl_private_cursor shape) { - if (!w || !w->conn || w->destroyed || (unsigned)shape > YASL_CURSOR_HIDDEN) return; + if (!w || !w->conn || w->destroyed || (unsigned)shape > YASL_PRIVATE_CURSOR_HIDDEN) return; w->cursor = shape; for (struct seat *s = w->conn->seats; s; s = s->next) { seat_set_cursor(s, w); @@ -193,7 +205,7 @@ void wayland_surface_set_cursor(struct wayland_surface *w, enum yasl_cursor shap static void press_queue_flush(struct seat *s, struct press_queue *q, struct wayland_surface *only, uint64_t *buttons, - struct yasl_pointer_event like) + struct yasl_private_pointer_event like) { int kept = 0; int count = q->n; @@ -206,10 +218,10 @@ static void press_queue_flush(struct seat *s, struct press_queue *q, if (!p.surface) continue; uint64_t mask = 1ull << p.button; *buttons = (*buttons & ~mask) | ((uint64_t)(!p.release) << p.button); - struct yasl_pointer_event ev = like; + struct yasl_private_pointer_event ev = like; ev.pos = p.pos; - ev.button = (enum yasl_pointer_button)p.button; - enum yasl_event_type type = p.release ? YASL_EVENT_POINTER_UP : YASL_EVENT_POINTER_DOWN; + ev.button = (enum yasl_private_pointer_button)p.button; + enum yasl_private_event_type type = p.release ? YASL_PRIVATE_EVENT_POINTER_UP : YASL_PRIVATE_EVENT_POINTER_DOWN; push_pointer(s, p.surface, type, ev); } q->head = 0; @@ -217,7 +229,7 @@ static void press_queue_flush(struct seat *s, struct press_queue *q, } static void press_queue_push(struct press_queue *q, struct wayland_surface *w, - struct yasl_vector2i pos, int button, bool release) + struct yasl_private_vector2i pos, int button, bool release) { if (q->n == 8) { q->head = (q->head + 1) & 7; @@ -244,10 +256,15 @@ void seat_pointer_reset(struct seat *s) s->pointer_serial = 0; s->motion_pending = false; memset(s->axis, 0, sizeof(s->axis)); + memset(s->axis_pixels, 0, sizeof(s->axis_pixels)); memset(s->axis_value120, 0, sizeof(s->axis_value120)); memset(s->axis_discrete, 0, sizeof(s->axis_discrete)); memset(s->axis_has_value120, 0, sizeof(s->axis_has_value120)); memset(s->axis_has_discrete, 0, sizeof(s->axis_has_discrete)); + s->axis_stop_pending = false; + s->scroll_gesture = false; + s->axis_phase = 0; + s->axis_source = WL_POINTER_AXIS_SOURCE_WHEEL; s->cursor_shape = -1; } @@ -263,7 +280,7 @@ static void flush_pending_motion(struct seat *s, struct wayland_surface *w) { if (!s->motion_pending) return; s->motion_pending = false; - push_pointer(s, w, YASL_EVENT_POINTER_MOTION, (struct yasl_pointer_event){ + push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_MOTION, (struct yasl_private_pointer_event){ .pos = s->pointer_pos, }); } @@ -356,6 +373,13 @@ static void pointer_axis(void *data, struct wl_pointer *pointer, double v = wl_fixed_to_double(value); if (likely(axis < 2)) { s->axis[axis] += v; + s->axis_pixels[axis] += v; + if (s->axis_source == WL_POINTER_AXIS_SOURCE_FINGER || + s->axis_source == WL_POINTER_AXIS_SOURCE_CONTINUOUS) { + if (!s->scroll_gesture) s->axis_phase = 1u; + else if (s->axis_phase != 1u) s->axis_phase = 2u; + s->scroll_gesture = true; + } } pointer_event_done(s); } @@ -363,13 +387,14 @@ static void pointer_axis(void *data, struct wl_pointer *pointer, static void pointer_axis_source(void *data, struct wl_pointer *pointer, uint32_t axis_source) { (void)pointer; - (void)data; (void)axis_source; + ((struct seat *)data)->axis_source = axis_source; } static void pointer_axis_stop(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis) { /* A stop ends a gesture, not the offsets already received in this frame. */ - (void)data; (void)pointer; (void)time; (void)axis; + (void)pointer; (void)time; (void)axis; + ((struct seat *)data)->axis_stop_pending = true; } static void pointer_axis_discrete(void *data, struct wl_pointer *pointer, uint32_t axis, int32_t discrete) @@ -405,7 +430,28 @@ static void pointer_frame_scroll(struct seat *s, struct wayland_surface *w) s->axis_discrete[axis] = 0; s->axis_has_value120[axis] = s->axis_has_discrete[axis] = false; } - report_wheel(s, w, s->pointer_pos, lines[1], lines[0]); + bool stopped = s->axis_stop_pending; + unsigned source = scroll_source_of(s->axis_source); + unsigned phase = source == 2 || source == 3 + ? (stopped ? 3u : s->axis_phase) : 0u; + bool has_pixels = (source == 2 || source == 3) && + (s->axis_pixels[0] != 0 || s->axis_pixels[1] != 0); + if (lines[0] != 0 || lines[1] != 0 || stopped) { + push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_SCROLL, + (struct yasl_private_pointer_event){ + .pos = s->pointer_pos, + .scroll = {lines[1], lines[0]}, + .pixel_scroll = {s->axis_pixels[1] * w->scale_factor, + s->axis_pixels[0] * w->scale_factor}, + .scroll_valid = 1u | (has_pixels ? 2u : 0u), + .scroll_source = source, + .scroll_phase = phase, + }); + } + memset(s->axis_pixels, 0, sizeof(s->axis_pixels)); + s->axis_stop_pending = false; + if (stopped) s->scroll_gesture = false; + s->axis_phase = 0; } static void pointer_frame(void *data, struct wl_pointer *pointer) @@ -416,14 +462,14 @@ static void pointer_frame(void *data, struct wl_pointer *pointer) if (left) { s->ptr_left = NULL; if (s->ptr_left_motion) - push_pointer(s, left, YASL_EVENT_POINTER_MOTION, (struct yasl_pointer_event){ + push_pointer(s, left, YASL_PRIVATE_EVENT_POINTER_MOTION, (struct yasl_private_pointer_event){ .pos = s->ptr_left_pos, }); - press_queue_flush(s, &s->ptr_presses, left, &s->pointer_buttons, (struct yasl_pointer_event){0}); - push_pointer(s, left, YASL_EVENT_POINTER_LEAVE, (struct yasl_pointer_event){ + press_queue_flush(s, &s->ptr_presses, left, &s->pointer_buttons, (struct yasl_private_pointer_event){0}); + push_pointer(s, left, YASL_PRIVATE_EVENT_POINTER_LEAVE, (struct yasl_private_pointer_event){ .pos = s->ptr_left_pos, }); - struct yasl_vector2i pos = s->pointer_pos; + struct yasl_private_vector2i pos = s->pointer_pos; bool motion = s->motion_pending; uint32_t serial = s->pointer_focus ? s->pointer_serial : 0; int shape = s->pointer_focus ? s->cursor_shape : -1; @@ -440,7 +486,7 @@ static void pointer_frame(void *data, struct wl_pointer *pointer) } flush_pending_motion(s, w); - press_queue_flush(s, &s->ptr_presses, NULL, &s->pointer_buttons, (struct yasl_pointer_event){0}); + press_queue_flush(s, &s->ptr_presses, NULL, &s->pointer_buttons, (struct yasl_private_pointer_event){0}); pointer_frame_scroll(s, w); } @@ -460,7 +506,7 @@ static void relative_motion(void *data, struct zwp_relative_pointer_v1 *pointer, /* Unlocked movement arrives as wl_pointer.motion; the queue derives its delta. */ if (!w || !w->pointer_locked || w->lock_seat != s) return; flush_pending_motion(s, w); - push_pointer(s, w, YASL_EVENT_POINTER_MOTION, (struct yasl_pointer_event){ + push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_MOTION, (struct yasl_private_pointer_event){ .pos = s->pointer_pos, .delta = {wl_fixed_to_double(dx) * w->scale_factor, wl_fixed_to_double(dy) * w->scale_factor}, @@ -525,12 +571,12 @@ static void seat_lock_surfaces(struct seat *s) surface_lock_pointer(w, s); } -bool wayland_surface_set_pointer_mode(struct wayland_surface *w, enum yasl_pointer_mode mode) +bool wayland_surface_set_pointer_mode(struct wayland_surface *w, enum yasl_private_pointer_mode mode) { if (!w || !w->conn || w->destroyed) return false; struct wayland_display *c = w->conn; - bool relative = mode == YASL_POINTER_RELATIVE; + bool relative = mode == YASL_PRIVATE_POINTER_RELATIVE; if (relative && (!c->relative_pointer_mgr || !c->pointer_constraints)) return false; w->relative = relative; @@ -572,10 +618,10 @@ static struct touch_point *touch_find(struct seat *s, int32_t id) return NULL; } -static struct yasl_vector2i touch_pos(const struct touch_point *p) +static struct yasl_private_vector2i touch_pos(const struct touch_point *p) { double scale = p->surface->scale_factor; - return (struct yasl_vector2i){ + return (struct yasl_private_vector2i){ .x = (int)lrint(p->x * scale), .y = (int)lrint(p->y * scale), }; } @@ -583,11 +629,11 @@ static struct yasl_vector2i touch_pos(const struct touch_point *p) static void touch_report(struct seat *s, struct touch_point *p, int button, - enum yasl_event_type type) + enum yasl_private_event_type type) { - push_pointer(s, p->surface, type, (struct yasl_pointer_event){ - .pos = touch_pos(p), .button = (enum yasl_pointer_button)button, - .device = YASL_PTR_DEV_TOUCH, + push_pointer(s, p->surface, type, (struct yasl_private_pointer_event){ + .pos = touch_pos(p), .button = (enum yasl_private_pointer_button)button, + .device = YASL_PRIVATE_PTR_DEV_TOUCH, }); } @@ -644,7 +690,7 @@ static void touch_frame(void *data, struct wl_touch *touch) if (p->down && !s->touch_primary && !s->touch_scrolling) { s->touch_primary = p; s->touch_pressed = true; - touch_report(s, p, 0, YASL_EVENT_POINTER_DOWN); + touch_report(s, p, 0, YASL_PRIVATE_EVENT_POINTER_DOWN); } if (!p->up) { active_n++; @@ -657,7 +703,7 @@ static void touch_frame(void *data, struct wl_touch *touch) struct touch_point *primary = s->touch_primary; if (primary && !s->touch_scrolling && active_n >= 2) { if (s->touch_pressed) - touch_report(s, primary, 0, YASL_EVENT_POINTER_UP); + touch_report(s, primary, 0, YASL_PRIVATE_EVENT_POINTER_UP); s->touch_pressed = false; s->touch_scrolling = true; s->touch_scroll_y = centroid_n ? centroid_sum / centroid_n : 0; @@ -667,14 +713,14 @@ static void touch_frame(void *data, struct wl_touch *touch) s->touch_scroll_y = y; if (dy != 0.0) { double lines = dy / SCROLL_PX_PER_LINE; - push_pointer(s, primary->surface, YASL_EVENT_POINTER_SCROLL, (struct yasl_pointer_event){ + push_pointer(s, primary->surface, YASL_PRIVATE_EVENT_POINTER_SCROLL, (struct yasl_private_pointer_event){ .pos = touch_pos(primary), .scroll = {0, -lines}, - .device = YASL_PTR_DEV_TOUCH, + .device = YASL_PRIVATE_PTR_DEV_TOUCH, }); } } else if (primary && primary->moved && s->touch_pressed) { - touch_report(s, primary, 0, YASL_EVENT_POINTER_MOTION); + touch_report(s, primary, 0, YASL_PRIVATE_EVENT_POINTER_MOTION); } for (int i = 0; i < SEAT_TOUCH_POINTS; i++) { struct touch_point *p = &s->touch_points[i]; @@ -683,7 +729,7 @@ static void touch_frame(void *data, struct wl_touch *touch) active_n--; if (p == s->touch_primary) { if (s->touch_pressed) - touch_report(s, p, 0, YASL_EVENT_POINTER_UP); + touch_report(s, p, 0, YASL_PRIVATE_EVENT_POINTER_UP); s->touch_pressed = false; s->touch_primary = NULL; } @@ -701,7 +747,7 @@ static void touch_cancel(void *data, struct wl_touch *touch) (void)touch; struct seat *s = data; if (s->touch_primary && s->touch_pressed) - touch_report(s, s->touch_primary, 0, YASL_EVENT_POINTER_UP); + touch_report(s, s->touch_primary, 0, YASL_PRIVATE_EVENT_POINTER_UP); memset(s->touch_points, 0, sizeof(s->touch_points)); s->touch_primary = NULL; s->touch_pressed = false; @@ -719,29 +765,100 @@ static const struct wl_touch_listener touch_listener = { .frame = touch_frame, .cancel = touch_cancel, }; -static struct yasl_pointer_event tool_event(const struct seat *s) +static struct yasl_private_pointer_event tool_event(const struct seat *s) { - return (struct yasl_pointer_event){ - .device = s->tool && s->tool->eraser ? YASL_PTR_DEV_ERASER : YASL_PTR_DEV_PEN, + return (struct yasl_private_pointer_event){ + .device = s->tool && s->tool->eraser ? YASL_PRIVATE_PTR_DEV_ERASER : YASL_PRIVATE_PTR_DEV_PEN, .pen = s->pen, }; } +static void push_pen_sample(struct tablet_tool *tool, struct wayland_surface *w, + unsigned action, uint32_t changed) +{ + struct seat *s = tool->seat; + if (!w) return; + uint64_t buttons = s->tool_buttons; + for (int i = 0; i < s->tool_presses.n; i++) { + const struct pending_press *press = &s->tool_presses.ev[(s->tool_presses.head + i) & 7]; + if (press->surface && press->button >= 0 && press->button < 32) { + uint64_t bit = 1ull << press->button; + buttons = (buttons & ~bit) | ((uint64_t)!press->release << press->button); + } + } + if (action == YASL_PRIVATE_PEN_LEAVE || action == YASL_PRIVATE_PEN_CANCEL) + buttons = 0; + struct yasl_private_event ev = { + .type = YASL_PRIVATE_EVENT_PEN, + .surface = &w->base, + .pen = { + .device_id = tool->device_id, + .tool_id = tool->tool_id, + .x = tool->x, + .y = tool->y, + .pressure = tool->pressure, + .tilt_x = tool->tilt_x, + .tilt_y = tool->tilt_y, + .rotation = tool->rotation, + .distance = tool->distance, + .tangential = tool->tangential, + .wheel_degrees = tool->wheel_degrees, + .axes_valid = tool->axes_valid, + .axes_changed = changed, + .buttons = (uint32_t)buttons, + .changed_button = tool->changed_button, + .in_contact = tool->in_contact, + .tool = tool->kind, + .action = action, + }, + }; + input_queue_push(&surface_root(w)->input, &ev); +} + +static void pen_action_push(struct tablet_tool *tool, unsigned action, unsigned button) +{ + if (tool->pending_action_count == 8) { + memmove(tool->pending_actions, tool->pending_actions + 1, + 7 * sizeof(tool->pending_actions[0])); + memmove(tool->pending_buttons, tool->pending_buttons + 1, + 7 * sizeof(tool->pending_buttons[0])); + memmove(tool->pending_contacts, tool->pending_contacts + 1, + 7 * sizeof(tool->pending_contacts[0])); + tool->pending_action_count--; + } + unsigned i = tool->pending_action_count++; + tool->pending_actions[i] = action; + tool->pending_buttons[i] = button; + tool->pending_contacts[i] = tool->in_contact; +} + static void tablet_tool_proximity_in(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface) { - (void)tool; (void)tablet; + (void)tool; struct tablet_tool *t = data; struct seat *s = t->seat; struct wayland_surface *w = surface ? (struct wayland_surface *)wl_surface_get_user_data(surface) : NULL; s->tablet_focus = w; s->tool = t; + t->device_id = 0; + for (struct tablet_device *device = s->tablet_devices; device; device = device->next) + if (device->proxy == tablet) { t->device_id = device->id; break; } + t->pen_in_proximity = true; + t->in_contact = false; + t->pending_action_count = 0; + pen_action_push(t, YASL_PRIVATE_PEN_ENTER, 0); + t->axes_changed = 0; + t->pressure = t->tilt_x = t->tilt_y = t->rotation = t->distance = 0; + t->tangential = t->wheel_degrees = 0; s->tool_buttons = 0; - s->pen = (struct yasl_pointer_pen){0}; + s->pen = (struct yasl_private_pointer_pen){0}; t->proximity_serial = serial; if (!w) return; + t->x = s->tool_pos.x; + t->y = s->tool_pos.y; if (s->conn->cursor_shape_mgr && !t->shape_dev) t->shape_dev = wp_cursor_shape_manager_v1_get_tablet_tool_v2( s->conn->cursor_shape_mgr, t->proxy); @@ -756,11 +873,14 @@ static void tablet_tool_proximity_out(void *data, struct zwp_tablet_tool_v2 *too struct seat *s = t->seat; struct wayland_surface *w = s->tablet_focus; if (w) { + t->in_contact = false; + t->pressure = 0; + push_pen_sample(t, w, YASL_PRIVATE_PEN_LEAVE, 0); press_queue_flush(s, &s->tool_presses, NULL, &s->tool_buttons, tool_event(s)); s->pen.pressure = 0; - struct yasl_pointer_event ev = tool_event(s); + struct yasl_private_pointer_event ev = tool_event(s); ev.pos = s->tool_pos; - push_pointer(s, w, YASL_EVENT_POINTER_LEAVE, ev); + push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_LEAVE, ev); } s->tool_presses.n = 0; s->tablet_focus = NULL; @@ -769,28 +889,38 @@ static void tablet_tool_proximity_out(void *data, struct zwp_tablet_tool_v2 *too s->tool_wheel = 0; s->tool_buttons = 0; t->proximity_serial = 0; + t->pen_in_proximity = false; + t->in_contact = false; } static void tablet_tool_down(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial) { (void)tool; - struct seat *s = ((struct tablet_tool *)data)->seat; + struct tablet_tool *t = data; + struct seat *s = t->seat; struct wayland_surface *w = s->tablet_focus; if (!w) return; seat_note_input(s, serial); s->press_serial = serial; s->press_surface = w; press_queue_push(&s->tool_presses, w, s->tool_pos, 0, false); + t->in_contact = true; + pen_action_push(t, YASL_PRIVATE_PEN_DOWN, 0); } static void tablet_tool_up(void *data, struct zwp_tablet_tool_v2 *tool) { (void)tool; struct seat *s = ((struct tablet_tool *)data)->seat; + struct tablet_tool *t = data; struct wayland_surface *w = s->tablet_focus; if (!w) return; s->pen.pressure = 0; press_queue_push(&s->tool_presses, w, s->tool_pos, 0, true); + t->pressure = 0; + t->in_contact = false; + pen_action_push(t, YASL_PRIVATE_PEN_UP, 0); + t->axes_changed |= YASL_PRIVATE_PEN_PRESSURE; } static void tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t x, wl_fixed_t y) @@ -800,6 +930,9 @@ static void tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *tool, wl_f struct wayland_surface *w = s->tablet_focus; if (!w) return; s->tool_pos = device_from_fixed(w, x, y); + struct tablet_tool *owned = data; + owned->x = wl_fixed_to_double(x) * w->scale_factor; + owned->y = wl_fixed_to_double(y) * w->scale_factor; s->tool_motion = true; } @@ -807,7 +940,8 @@ static void tablet_tool_button(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial, uint32_t button, uint32_t state) { (void)tool; - struct seat *s = ((struct tablet_tool *)data)->seat; + struct tablet_tool *t = data; + struct seat *s = t->seat; struct wayland_surface *w = s->tablet_focus; if (!w) return; seat_note_input(s, serial); @@ -820,13 +954,18 @@ static void tablet_tool_button(void *data, struct zwp_tablet_tool_v2 *tool, } bool release = state == ZWP_TABLET_TOOL_V2_BUTTON_STATE_RELEASED; press_queue_push(&s->tool_presses, w, s->tool_pos, b, release); + pen_action_push(t, YASL_PRIVATE_PEN_BUTTON, (unsigned)b); } static void tool_pressure(void *d, struct zwp_tablet_tool_v2 *t, uint32_t v) { (void)t; struct seat *s = ((struct tablet_tool *)d)->seat; + struct tablet_tool *owned = d; s->pen.pressure = v > 65535 ? 65535 : (uint16_t)v; + owned->pressure = (double)(v > 65535 ? 65535 : v) / 65535.0; + owned->axes_valid |= YASL_PRIVATE_PEN_PRESSURE; + owned->axes_changed |= YASL_PRIVATE_PEN_PRESSURE; s->tool_motion = true; } @@ -840,40 +979,68 @@ static void tool_tilt(void *d, struct zwp_tablet_tool_v2 *t, wl_fixed_t x, wl_fi { (void)t; struct seat *s = ((struct tablet_tool *)d)->seat; + struct tablet_tool *owned = d; s->pen.tilt_x = tilt_degrees(x); s->pen.tilt_y = tilt_degrees(y); + owned->tilt_x = wl_fixed_to_double(x); + owned->tilt_y = wl_fixed_to_double(y); + owned->axes_valid |= YASL_PRIVATE_PEN_TILT; + owned->axes_changed |= YASL_PRIVATE_PEN_TILT; s->tool_motion = true; } static void tool_wheel(void *d, struct zwp_tablet_tool_v2 *t, wl_fixed_t deg, int32_t clicks) { - (void)t; (void)deg; - ((struct tablet_tool *)d)->seat->tool_wheel += clicks; + (void)t; + struct tablet_tool *owned = d; + owned->seat->tool_wheel += clicks; + owned->wheel_degrees += wl_fixed_to_double(deg); + owned->axes_valid |= YASL_PRIVATE_PEN_WHEEL; + owned->axes_changed |= YASL_PRIVATE_PEN_WHEEL; } static void tool_frame(void *d, struct zwp_tablet_tool_v2 *t, uint32_t time) { (void)t; (void)time; struct seat *s = ((struct tablet_tool *)d)->seat; + struct tablet_tool *owned = d; struct wayland_surface *w = s->tablet_focus; if (!w) { s->tool_presses.n = 0; s->tool_wheel = 0; return; } - struct yasl_pointer_event like = tool_event(s); + struct yasl_private_pointer_event like = tool_event(s); + if (owned->pending_action_count) { + unsigned actions = owned->pending_action_count; + bool contact = owned->in_contact; + for (unsigned i = 0; i < actions; i++) { + owned->changed_button = owned->pending_buttons[i]; + owned->in_contact = owned->pending_contacts[i]; + push_pen_sample(owned, w, owned->pending_actions[i], + i + 1 == actions ? owned->axes_changed : 0); + } + owned->in_contact = contact; + owned->pending_action_count = 0; + owned->axes_changed = 0; + owned->changed_button = 0; + } else if (s->tool_motion || owned->axes_changed) { + push_pen_sample(owned, w, YASL_PRIVATE_PEN_MOVE, owned->axes_changed); + owned->axes_changed = 0; + owned->changed_button = 0; + } if (s->tool_motion) { - struct yasl_pointer_event ev = like; + struct yasl_private_pointer_event ev = like; ev.pos = s->tool_pos; - push_pointer(s, w, YASL_EVENT_POINTER_MOTION, ev); + push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_MOTION, ev); s->tool_motion = false; } press_queue_flush(s, &s->tool_presses, NULL, &s->tool_buttons, like); if (s->tool_wheel) { - struct yasl_pointer_event ev = like; + struct yasl_private_pointer_event ev = like; ev.pos = s->tool_pos; ev.scroll.y = s->tool_wheel; - push_pointer(s, w, YASL_EVENT_POINTER_SCROLL, ev); + push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_SCROLL, ev); s->tool_wheel = 0; } } @@ -899,16 +1066,43 @@ static void tablet_tool_removed(void *data, struct zwp_tablet_tool_v2 *tool) static void tool_type(void *d, struct zwp_tablet_tool_v2 *t, uint32_t type) { (void)t; - ((struct tablet_tool *)d)->eraser = type == ZWP_TABLET_TOOL_V2_TYPE_ERASER; + struct tablet_tool *tool = d; + tool->eraser = type == ZWP_TABLET_TOOL_V2_TYPE_ERASER; + switch (type) { + case ZWP_TABLET_TOOL_V2_TYPE_PEN: tool->kind = 1; break; + case ZWP_TABLET_TOOL_V2_TYPE_ERASER: tool->kind = 2; break; + case ZWP_TABLET_TOOL_V2_TYPE_BRUSH: tool->kind = 3; break; + case ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH: tool->kind = 4; break; + case ZWP_TABLET_TOOL_V2_TYPE_PENCIL: tool->kind = 5; break; + default: tool->kind = 0; break; + } } -static void tool_hardware_serial(void *d, struct zwp_tablet_tool_v2 *t, uint32_t hi, uint32_t lo) { (void)d; (void)t; (void)hi; (void)lo; } +static void tool_hardware_serial(void *d, struct zwp_tablet_tool_v2 *t, uint32_t hi, uint32_t lo) +{ (void)t; struct tablet_tool *tool = d; uint64_t id = ((uint64_t)hi << 32) | lo; if (id) tool->tool_id = id; } static void tool_hardware_id_wacom(void *d, struct zwp_tablet_tool_v2 *t, uint32_t hi, uint32_t lo) { (void)d; (void)t; (void)hi; (void)lo; } -static void tool_capability(void *d, struct zwp_tablet_tool_v2 *t, uint32_t cap) { (void)d; (void)t; (void)cap; } +static void tool_capability(void *d, struct zwp_tablet_tool_v2 *t, uint32_t cap) +{ + (void)t; + struct tablet_tool *tool = d; + tool->capability_mask |= 1u << cap; + switch (cap) { + case ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE: tool->axes_valid |= YASL_PRIVATE_PEN_PRESSURE; break; + case ZWP_TABLET_TOOL_V2_CAPABILITY_TILT: tool->axes_valid |= YASL_PRIVATE_PEN_TILT; break; + case ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION: tool->axes_valid |= YASL_PRIVATE_PEN_ROTATION; break; + case ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE: tool->axes_valid |= YASL_PRIVATE_PEN_DISTANCE; break; + case ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER: tool->axes_valid |= YASL_PRIVATE_PEN_TANGENTIAL; break; + case ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL: tool->axes_valid |= YASL_PRIVATE_PEN_WHEEL; break; + default: break; + } +} static void tool_done(void *d, struct zwp_tablet_tool_v2 *t) { (void)d; (void)t; } -static void tool_distance(void *d, struct zwp_tablet_tool_v2 *t, uint32_t v) { (void)d; (void)t; (void)v; } -static void tool_rotation(void *d, struct zwp_tablet_tool_v2 *t, wl_fixed_t deg) { (void)d; (void)t; (void)deg; } -static void tool_slider(void *d, struct zwp_tablet_tool_v2 *t, int32_t pos) { (void)d; (void)t; (void)pos; } +static void tool_distance(void *d, struct zwp_tablet_tool_v2 *t, uint32_t v) +{ (void)t; struct tablet_tool *tool = d; tool->distance = (double)(v > 65535 ? 65535 : v) / 65535.0; tool->axes_valid |= YASL_PRIVATE_PEN_DISTANCE; tool->axes_changed |= YASL_PRIVATE_PEN_DISTANCE; } +static void tool_rotation(void *d, struct zwp_tablet_tool_v2 *t, wl_fixed_t deg) +{ (void)t; struct tablet_tool *tool = d; tool->rotation = wl_fixed_to_double(deg); tool->axes_valid |= YASL_PRIVATE_PEN_ROTATION; tool->axes_changed |= YASL_PRIVATE_PEN_ROTATION; } +static void tool_slider(void *d, struct zwp_tablet_tool_v2 *t, int32_t pos) +{ (void)t; struct tablet_tool *tool = d; tool->tangential = pos / 65535.0; if (tool->tangential < -1) tool->tangential = -1; if (tool->tangential > 1) tool->tangential = 1; tool->axes_valid |= YASL_PRIVATE_PEN_TANGENTIAL; tool->axes_changed |= YASL_PRIVATE_PEN_TANGENTIAL; } static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = { .type = tool_type, .hardware_serial = tool_hardware_serial, @@ -997,6 +1191,7 @@ static void tablet_seat_tablet_added(void *data, struct zwp_tablet_seat_v2 *tabl struct tablet_device *owned = calloc(1, sizeof(*owned)); if (!owned) { zwp_tablet_v2_destroy(tablet); return; } owned->proxy = tablet; + owned->id = ++s->conn->next_tool_id; owned->next = s->tablet_devices; s->tablet_devices = owned; zwp_tablet_v2_add_listener(tablet, &tablet_listener, s); @@ -1010,6 +1205,7 @@ static void tablet_seat_tool_added(void *data, struct zwp_tablet_seat_v2 *tablet if (!owned) { zwp_tablet_tool_v2_destroy(tool); return; } owned->proxy = tool; owned->seat = s; + owned->tool_id = ++s->conn->next_tool_id; owned->next = s->tablet_tools; s->tablet_tools = owned; zwp_tablet_tool_v2_add_listener(tool, &tablet_tool_listener, owned); @@ -1035,7 +1231,7 @@ static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = { static void clipboard_received(const char *text, size_t len, bool uri_list, struct wl_surface *on, uint64_t request_id, - enum yasl_transfer_status status, void *ud) + enum yasl_private_transfer_status status, void *ud) { (void)ud; struct wayland_surface *w = on ? @@ -1043,13 +1239,13 @@ static void clipboard_received(const char *text, size_t len, bool uri_list, if (uri_list) w = surface_root(w); if (!w || w->closed || w->destroyed) return; if (!uri_list) { - yasl_surface_complete_paste(&w->base, request_id, status, text, len); + yasl_private_surface_complete_paste(&w->base, request_id, status, text, len); return; } - if (status != YASL_TRANSFER_OK) return; - struct yasl_event ev = { + if (status != YASL_PRIVATE_TRANSFER_OK) return; + struct yasl_private_event ev = { .surface = &w->base, - .type = uri_list ? YASL_EVENT_DROP : YASL_EVENT_PASTE, + .type = uri_list ? YASL_PRIVATE_EVENT_DROP : YASL_PRIVATE_EVENT_PASTE, .text = { .data = text, .len = len, @@ -1058,14 +1254,14 @@ static void clipboard_received(const char *text, size_t len, bool uri_list, input_queue_push(&w->input, &ev); } -static void clipboard_drag(enum yasl_drag_event ev, struct wl_surface *on, +static void clipboard_drag(enum yasl_private_drag_event ev, struct wl_surface *on, double x, double y, void *ud) { struct seat *s = ud; conn_drag_target(s->conn, ev, on, x, y); } -static void clipboard_drop_hover(enum yasl_drop_event ev, struct wl_surface *on, +static void clipboard_drop_hover(enum yasl_private_drop_event ev, struct wl_surface *on, double x, double y, void *ud) { struct seat *s = ud; @@ -1152,6 +1348,16 @@ static void seat_drop_touch(struct seat *s) } } +void seat_ensure_text_input(struct seat *s) +{ + if (!s || !s->conn->text_input_mgr || s->text_input || !s->proxy) + return; + s->text_input = zwp_text_input_manager_v3_get_text_input( + s->conn->text_input_mgr, s->proxy); + if (s->text_input) + zwp_text_input_v3_add_listener(s->text_input, &text_input_listener, s); +} + static void seat_capabilities(void *data, struct wl_seat *proxy, uint32_t caps) { struct seat *s = data; @@ -1209,11 +1415,7 @@ static void seat_capabilities(void *data, struct wl_seat *proxy, uint32_t caps) zwp_tablet_seat_v2_add_listener(s->tablet_seat, &tablet_seat_listener, s); } - if (c->text_input_mgr && !s->text_input) { - s->text_input = zwp_text_input_manager_v3_get_text_input(c->text_input_mgr, proxy); - if (s->text_input) - zwp_text_input_v3_add_listener(s->text_input, &text_input_listener, s); - } + seat_ensure_text_input(s); seat_clipboard_ensure(s); } @@ -1297,8 +1499,8 @@ void seat_destroy(struct seat *s) seat_drop_keyboard(s); struct wayland_surface *left = s->focus_leaving; if (left && !conn_keyboard_on(c, left, NULL)) { - struct yasl_event ev = { - .type = YASL_EVENT_FOCUS, + struct yasl_private_event ev = { + .type = YASL_PRIVATE_EVENT_FOCUS, .surface = &left->base, .focus = { .focused = false }, }; @@ -1306,7 +1508,7 @@ void seat_destroy(struct seat *s) } seat_drop_touch(s); clipboard_destroy(s->clipboard); - if (s->repeat_watch > 0) yasl_loop_del_fd(c->loop, s->repeat_watch); + if (s->repeat_watch > 0) yasl_private_loop_del_fd(c->loop, s->repeat_watch); if (s->repeat_fd >= 0) close(s->repeat_fd); if (s->cursor_viewport) wp_viewport_destroy(s->cursor_viewport); if (s->cursor_surface) wl_surface_destroy(s->cursor_surface); diff --git a/src/platform/wayland/popup.c b/src/platform/wayland/popup.c index 44c7206..c563eb4 100644 --- a/src/platform/wayland/popup.c +++ b/src/platform/wayland/popup.c @@ -24,9 +24,19 @@ static const struct xdg_popup_listener popup_listener = { }; static bool popup_spec_valid(const struct wayland_surface *parent, - const struct yasl_popup_spec *spec) + const struct yasl_private_popup_spec *spec) { if (!parent || !spec) return false; + if (spec->positioner_explicit && + (spec->anchor_edges > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT || + spec->gravity > XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT || + (spec->constraints & ~(unsigned)(XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y)))) + return false; int64_t x = (int64_t)spec->anchor.x - parent->content.x; int64_t y = (int64_t)spec->anchor.y - parent->content.y; return x >= INT_MIN && x <= INT_MAX && y >= INT_MIN && y <= INT_MAX && @@ -37,29 +47,29 @@ static bool popup_spec_valid(const struct wayland_surface *parent, (int64_t)spec->content_offset.y + spec->content_size.y <= spec->size.y; } -static bool popup_apply_spec(struct wayland_surface *w, const struct yasl_popup_spec *spec) +static bool popup_apply_spec(struct wayland_surface *w, const struct yasl_private_popup_spec *spec) { uint32_t scale = w->parent->scale_120; - struct yasl_vector2i logical = { - .x = (int)(((int64_t)spec->size.x * YASL_SCALE_UNIT + scale - 1) / scale), - .y = (int)(((int64_t)spec->size.y * YASL_SCALE_UNIT + scale - 1) / scale), + struct yasl_private_vector2i logical = { + .x = (int)(((int64_t)spec->size.x * YASL_PRIVATE_SCALE_UNIT + scale - 1) / scale), + .y = (int)(((int64_t)spec->size.y * YASL_PRIVATE_SCALE_UNIT + scale - 1) / scale), }; int width = device_px(logical.x, scale); int height = device_px(logical.y, scale); size_t bytes; if (logical.x <= 0 || logical.y <= 0 || !buffer_size(width, height, &bytes)) return false; - struct yasl_vector2i content = spec->content_size; + struct yasl_private_vector2i content = spec->content_size; if (!content.x) content.x = spec->size.x - spec->content_offset.x; if (!content.y) content.y = spec->size.y - spec->content_offset.y; bool rescaled = scale != w->scale_120; w->scale_120 = w->pending_scale_120 = scale; - w->scale_factor = (double)scale / YASL_SCALE_UNIT; + w->scale_factor = (double)scale / YASL_PRIVATE_SCALE_UNIT; w->width = width; w->height = height; w->logical_width = logical.x; w->logical_height = logical.y; - w->content = (struct yasl_rect2i){ + w->content = (struct yasl_private_rect2i){ .x = spec->content_offset.x, .y = spec->content_offset.y, .width = content.x, .height = content.y }; @@ -67,20 +77,20 @@ static bool popup_apply_spec(struct wayland_surface *w, const struct yasl_popup_ w->buffers[i].presented = false; if (w->viewport) wp_viewport_set_destination(w->viewport, logical.x, logical.y); - else if ((rescaled || !w->configured) && scale % YASL_SCALE_UNIT == 0 && + else if ((rescaled || !w->configured) && scale % YASL_PRIVATE_SCALE_UNIT == 0 && wl_proxy_get_version((struct wl_proxy *)w->surface) >= WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) - wl_surface_set_buffer_scale(w->surface, (int32_t)(scale / YASL_SCALE_UNIT)); + wl_surface_set_buffer_scale(w->surface, (int32_t)(scale / YASL_PRIVATE_SCALE_UNIT)); return true; } static void popup_set_geometry(struct wayland_surface *w) { uint32_t scale = w->scale_120; - int gx = yasl_scale_to_logical(w->content.x, scale); - int gy = yasl_scale_to_logical(w->content.y, scale); - int gw = yasl_scale_to_logical(w->content.width, scale); - int gh = yasl_scale_to_logical(w->content.height, scale); + int gx = yasl_private_scale_to_logical(w->content.x, scale); + int gy = yasl_private_scale_to_logical(w->content.y, scale); + int gw = yasl_private_scale_to_logical(w->content.width, scale); + int gh = yasl_private_scale_to_logical(w->content.height, scale); if (gw < 1) gw = 1; if (gh < 1) gh = 1; if (gx + gw > w->logical_width) @@ -91,41 +101,45 @@ static void popup_set_geometry(struct wayland_surface *w) } static struct xdg_positioner *popup_positioner(struct wayland_surface *w, - const struct yasl_popup_spec *spec) + const struct yasl_private_popup_spec *spec) { struct wayland_surface *parent = w->parent; struct xdg_positioner *pos = xdg_wm_base_create_positioner(w->conn->wm_base); if (!pos) return NULL; uint32_t scale = parent->scale_120; - int gw = yasl_scale_to_logical(w->content.width, scale); - int gh = yasl_scale_to_logical(w->content.height, scale); + int gw = yasl_private_scale_to_logical(w->content.width, scale); + int gh = yasl_private_scale_to_logical(w->content.height, scale); xdg_positioner_set_size(pos, gw > 0 ? gw : 1, gh > 0 ? gh : 1); - struct yasl_vector2i at = { + struct yasl_private_vector2i at = { .x = spec->anchor.x - parent->content.x, .y = spec->anchor.y - parent->content.y, }; - int aw = yasl_scale_to_logical(spec->anchor.width, scale); - int ah = yasl_scale_to_logical(spec->anchor.height, scale); + int aw = yasl_private_scale_to_logical(spec->anchor.width, scale); + int ah = yasl_private_scale_to_logical(spec->anchor.height, scale); xdg_positioner_set_anchor_rect(pos, - yasl_scale_to_logical(at.x, scale), - yasl_scale_to_logical(at.y, scale), + yasl_private_scale_to_logical(at.x, scale), + yasl_private_scale_to_logical(at.y, scale), aw > 0 ? aw : 1, ah > 0 ? ah : 1); - xdg_positioner_set_anchor(pos, spec->beside ? XDG_POSITIONER_ANCHOR_TOP_RIGHT - : XDG_POSITIONER_ANCHOR_TOP_LEFT); - xdg_positioner_set_gravity(pos, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT); - xdg_positioner_set_constraint_adjustment(pos, + uint32_t anchor = spec->positioner_explicit ? spec->anchor_edges : + (spec->beside ? XDG_POSITIONER_ANCHOR_TOP_RIGHT : XDG_POSITIONER_ANCHOR_TOP_LEFT); + uint32_t gravity = spec->positioner_explicit ? spec->gravity : + XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT; + uint32_t constraints = spec->positioner_explicit ? spec->constraints : XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y); + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y; + xdg_positioner_set_anchor(pos, anchor); + xdg_positioner_set_gravity(pos, gravity); + xdg_positioner_set_constraint_adjustment(pos, constraints); if (xdg_positioner_get_version(pos) >= XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION) xdg_positioner_set_reactive(pos); return pos; } struct wayland_surface *wayland_surface_create_popup(struct wayland_surface *parent, - const struct yasl_popup_spec *spec) + const struct yasl_private_popup_spec *spec) { struct wayland_display *c = parent ? parent->conn : NULL; struct seat *seat = c ? (c->active_seat ? c->active_seat : c->seats) : NULL; @@ -137,7 +151,8 @@ struct wayland_surface *wayland_surface_create_popup(struct wayland_surface *par if (!w) return NULL; w->base.display = &c->base; - w->base.state = YASL_STATE_CSD; + w->base.state = YASL_PRIVATE_STATE_CSD; + w->base.presenter = YASL_PRIVATE_PRESENTER_CPU; w->input.leave_ends_press = true; w->conn = c; w->conn_next = c->surfaces; @@ -150,7 +165,7 @@ struct wayland_surface *wayland_surface_create_popup(struct wayland_surface *par if (!w->surface) goto err; w->base.native_handle = w->surface; - w->base.size = (struct yasl_vector2i){spec->size.x, spec->size.y}; + w->base.size = (struct yasl_private_vector2i){spec->size.x, spec->size.y}; w->base.scale_120 = parent->scale_120; wl_surface_set_user_data(w->surface, w); if (surface_init_scaling(w) < 0) @@ -180,13 +195,13 @@ err: return NULL; } -bool wayland_surface_popup_reposition(struct wayland_surface *w, const struct yasl_popup_spec *spec) +bool wayland_surface_popup_reposition(struct wayland_surface *w, const struct yasl_private_popup_spec *spec) { if (!w || !w->popup || w->closed || !w->conn || w->conn->lost || !popup_spec_valid(w->parent, spec) || xdg_popup_get_version(w->popup) < XDG_POPUP_REPOSITION_SINCE_VERSION) return false; - struct yasl_popup_spec old = { + struct yasl_private_popup_spec old = { .size = { .x = w->width, .y = w->height }, .content_offset = { .x = w->content.x, .y = w->content.y }, .content_size = { .x = w->content.width, .y = w->content.height }, diff --git a/src/platform/wayland/shell.c b/src/platform/wayland/shell.c index fc2ce2e..c2578e7 100644 --- a/src/platform/wayland/shell.c +++ b/src/platform/wayland/shell.c @@ -173,7 +173,7 @@ uint32_t conn_output_scale_120(const struct wayland_display *c, uint64_t outputs for (const struct wl_conn_output *o = c->outputs; o; o = o->next) if ((o->mask & outputs) && o->scale > scale) scale = o->scale; - return (uint32_t)scale * YASL_SCALE_UNIT; + return (uint32_t)scale * YASL_PRIVATE_SCALE_UNIT; } static void registry_global(void *data, struct wl_registry *registry, @@ -227,6 +227,8 @@ static void registry_global(void *data, struct wl_registry *registry, } else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) { c->text_input_mgr = wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1); + for (struct seat *seat = c->seats; seat; seat = seat->next) + seat_ensure_text_input(seat); } else if (strcmp(interface, wp_tearing_control_manager_v1_interface.name) == 0) { c->tearing_mgr = wl_registry_bind(registry, name, &wp_tearing_control_manager_v1_interface, 1); @@ -277,7 +279,7 @@ static void set_state(struct wayland_surface *w, uint32_t state) { if (state == w->base.state) return; w->base.state = state; - yasl_surface_notify_state(&w->base, state); + yasl_private_surface_notify_state(&w->base, state); } static void toplevel_configure(void *data, struct xdg_toplevel *toplevel, @@ -286,21 +288,21 @@ static void toplevel_configure(void *data, struct xdg_toplevel *toplevel, (void)toplevel; struct wayland_surface *w = data; - uint32_t state = w->base.state & ~(YASL_STATE_FULLSCREEN | - YASL_STATE_MAXIMIZED | YASL_STATE_ACTIVATED | - YASL_STATE_SUSPENDED | YASL_STATE_TILED); + uint32_t state = w->base.state & ~(YASL_PRIVATE_STATE_FULLSCREEN | + YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_ACTIVATED | + YASL_PRIVATE_STATE_SUSPENDED | YASL_PRIVATE_STATE_TILED); uint32_t *s; wl_array_for_each(s, states) { switch (*s) { - case XDG_TOPLEVEL_STATE_FULLSCREEN: state |= YASL_STATE_FULLSCREEN; break; - case XDG_TOPLEVEL_STATE_MAXIMIZED: state |= YASL_STATE_MAXIMIZED; break; - case XDG_TOPLEVEL_STATE_ACTIVATED: state |= YASL_STATE_ACTIVATED; break; - case XDG_TOPLEVEL_STATE_SUSPENDED: state |= YASL_STATE_SUSPENDED; break; + case XDG_TOPLEVEL_STATE_FULLSCREEN: state |= YASL_PRIVATE_STATE_FULLSCREEN; break; + case XDG_TOPLEVEL_STATE_MAXIMIZED: state |= YASL_PRIVATE_STATE_MAXIMIZED; break; + case XDG_TOPLEVEL_STATE_ACTIVATED: state |= YASL_PRIVATE_STATE_ACTIVATED; break; + case XDG_TOPLEVEL_STATE_SUSPENDED: state |= YASL_PRIVATE_STATE_SUSPENDED; break; case XDG_TOPLEVEL_STATE_TILED_LEFT: case XDG_TOPLEVEL_STATE_TILED_RIGHT: case XDG_TOPLEVEL_STATE_TILED_TOP: case XDG_TOPLEVEL_STATE_TILED_BOTTOM: - state |= YASL_STATE_TILED; + state |= YASL_PRIVATE_STATE_TILED; break; default: break; } @@ -316,7 +318,7 @@ static void toplevel_close(void *data, struct xdg_toplevel *toplevel) (void)toplevel; struct wayland_surface *w = data; if (w && !w->closed) - yasl_surface_notify_close(&w->base); + yasl_private_surface_notify_close(&w->base); } static void toplevel_configure_bounds(void *data, struct xdg_toplevel *toplevel, @@ -349,11 +351,21 @@ static const struct xdg_toplevel_listener toplevel_listener = { static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { struct wayland_surface *w = data; - (void)xdg_surface; w->pending_serial = serial; w->has_pending_serial = true; w->size_pending = true; w->configured = true; + /* CPU surfaces acknowledge with their SHM commit. External presenters + * commit through EGL/Vulkan, so apply the configure and acknowledge it + * before renderer code can submit that first native buffer. */ + if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU) { + xdg_surface_ack_configure(xdg_surface, serial); + w->has_pending_serial = false; + if (apply_pending_geometry(w) < 0) { + surface_fail(w); + return; + } + } wayland_surface_schedule_redraw(w); } @@ -368,9 +380,9 @@ static void toplevel_decoration_configure(void *data, struct wayland_surface *w = data; uint32_t state = w->base.state; if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE) - state &= ~YASL_STATE_CSD; + state &= ~YASL_PRIVATE_STATE_CSD; else - state |= YASL_STATE_CSD; + state |= YASL_PRIVATE_STATE_CSD; set_state(w, state); } @@ -387,11 +399,11 @@ int surface_init_shell(struct wayland_surface *w, const char *title) if (!w->toplevel) return -1; xdg_toplevel_add_listener(w->toplevel, &toplevel_listener, w); - wayland_surface_set_title(w, title ? title : "yasl_app"); + wayland_surface_set_title(w, title ? title : "yasl_private_app"); - const char *app_id = w->conn->app_id ? w->conn->app_id : "yasl_app"; + const char *app_id = w->conn->app_id ? w->conn->app_id : "yasl_private_app"; xdg_toplevel_set_app_id(w->toplevel, app_id); - xdg_toplevel_set_min_size(w->toplevel, YASL_MIN_LOGICAL_PX, YASL_MIN_LOGICAL_PX); + xdg_toplevel_set_min_size(w->toplevel, YASL_PRIVATE_MIN_LOGICAL_PX, YASL_PRIVATE_MIN_LOGICAL_PX); if (w->conn->decor_mgr) { w->toplevel_decor = zxdg_decoration_manager_v1_get_toplevel_decoration( @@ -400,7 +412,9 @@ int surface_init_shell(struct wayland_surface *w, const char *title) zxdg_toplevel_decoration_v1_add_listener(w->toplevel_decor, &toplevel_decoration_listener, w); zxdg_toplevel_decoration_v1_set_mode(w->toplevel_decor, - ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + (w->base.creation_flags & YASL_PRIVATE_SURFACE_UNDECORATED) + ? ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE + : ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); } return 0; } diff --git a/src/platform/wayland/surface.c b/src/platform/wayland/surface.c index 2e85e2b..31096f4 100644 --- a/src/platform/wayland/surface.c +++ b/src/platform/wayland/surface.c @@ -52,7 +52,7 @@ static void display_lose(struct wayland_display *c) surface_fail(w); } display_leave(c); - yasl_display_notify_event(&c->base, YASL_EVENT_DISPLAY_LOST); + yasl_private_display_notify_event(&c->base, YASL_PRIVATE_EVENT_DISPLAY_LOST); } void surface_fail(struct wayland_surface *w) @@ -60,7 +60,7 @@ void surface_fail(struct wayland_surface *w) if (w->closed) return; w->closed = true; - yasl_surface_notify_close(&w->base); + yasl_private_surface_notify_close(&w->base); } static int display_ready(int fd, short revents, void *ud) @@ -80,7 +80,7 @@ static int display_ready(int fd, short revents, void *ud) if (revents & POLLOUT) { if (wl_display_flush(c->wl) >= 0) { if (c->loop && c->watch > 0) - yasl_loop_set_fd_events(c->loop, c->watch, POLLIN); + yasl_private_loop_set_fd_events(c->loop, c->watch, POLLIN); } else if (errno != EAGAIN) { display_lose(c); } @@ -113,7 +113,7 @@ static bool display_arm(struct wayland_display *c) events |= POLLOUT; } if (c->loop && c->watch > 0) - yasl_loop_set_fd_events(c->loop, c->watch, events); + yasl_private_loop_set_fd_events(c->loop, c->watch, events); return had_pending; } @@ -154,7 +154,7 @@ int wayland_display_dispatch_timeout(struct wayland_display *c, int timeout_ms) int n = 0; if (c->loop) { - n = yasl_loop_step(c->loop, timeout_ms); + n = yasl_private_loop_step(c->loop, timeout_ms); } if (c->reading) { @@ -190,7 +190,7 @@ void wayland_display_flush(struct wayland_display *c) static void display_free(struct wayland_display *c) { conn_drag_end(c); - if (c->loop && c->watch > 0) yasl_loop_del_fd(c->loop, c->watch); + if (c->loop && c->watch > 0) yasl_private_loop_del_fd(c->loop, c->watch); if (c->wl) display_cancel_read(c); while (c->seats) seat_destroy(c->seats); @@ -217,9 +217,12 @@ static void display_free(struct wayland_display *c) if (c->shm) wl_shm_destroy(c->shm); if (c->compositor) wl_compositor_destroy(c->compositor); if (c->registry) wl_registry_destroy(c->registry); +#if YASL_HAS_EGL + if (c->egl_initialized) eglTerminate(c->egl_display); +#endif if (c->wl) wl_display_disconnect(c->wl); if (c->owns_loop && c->loop) { - yasl_loop_destroy(c->loop); + yasl_private_loop_destroy(c->loop); c->loop = NULL; c->base.loop = NULL; } @@ -227,13 +230,13 @@ static void display_free(struct wayland_display *c) free(c); } -struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec) +struct wayland_display *wayland_display_open(struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec) { struct wayland_display *c = calloc(1, sizeof(*c)); if (!c) return NULL; - c->base.ops = &yasl_wayland_backend; + c->base.ops = &yasl_private_wayland_backend; if (!loop) { - c->loop = yasl_loop_create(); + c->loop = yasl_private_loop_create(); c->owns_loop = true; } else { c->loop = loop; @@ -241,7 +244,7 @@ struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struc c->base.loop = c->loop; if (!c->loop) goto err; if (spec) { - c->base.callbacks = spec->cbs ? *spec->cbs : (struct yasl_display_cbs){0}; + c->base.callbacks = spec->cbs ? *spec->cbs : (struct yasl_private_display_cbs){0}; c->cbs = &c->base.callbacks; c->ud = spec->ud; if (spec->app_id) c->app_id = strdup(spec->app_id); @@ -251,6 +254,13 @@ struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struc c->wl = c->xkb_ctx ? wl_display_connect(NULL) : NULL; if (!c->wl) goto err; c->base.native_handle = c->wl; +#if YASL_HAS_EGL + c->egl_display = eglGetDisplay((EGLNativeDisplayType)c->wl); + if (c->egl_display != EGL_NO_DISPLAY) { + EGLint major = 0, minor = 0; + c->egl_initialized = eglInitialize(c->egl_display, &major, &minor) == EGL_TRUE; + } +#endif c->registry = wl_display_get_registry(c->wl); if (!c->registry) goto err; @@ -260,7 +270,7 @@ struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struc surface_init_cursor(c); if (c->loop) { - c->watch = yasl_loop_add_fd(c->loop, wl_display_get_fd(c->wl), POLLIN, display_ready, c); + c->watch = yasl_private_loop_add_fd(c->loop, wl_display_get_fd(c->wl), POLLIN, display_ready, c); if (c->watch <= 0) { c->watch = 0; goto err; } } if (display_input_init(c) < 0) @@ -282,16 +292,18 @@ void wayland_display_close(struct wayland_display *c) uint32_t wayland_display_caps(const struct wayland_display *c) { if (!c) return 0; - uint32_t caps = YASL_CAP_POPUPS | YASL_CAP_CSD | YASL_CAP_FRAME_PACING; - if (c->tablet_mgr) caps |= YASL_CAP_TABLET; + uint32_t caps = YASL_PRIVATE_CAP_POPUPS | YASL_PRIVATE_CAP_CSD | + YASL_PRIVATE_CAP_FRAME_PACING | YASL_PRIVATE_CAP_NATIVE_GRAPHICS; + if (c->tablet_mgr) caps |= YASL_PRIVATE_CAP_TABLET; + if (c->text_input_mgr) caps |= YASL_PRIVATE_CAP_TEXT_INPUT; struct seat *s = c->active_seat ? c->active_seat : c->seats; const struct wl_data_device *device = s ? clipboard_data_device(s->clipboard) : NULL; if (device && wl_data_device_get_version((struct wl_data_device *)device) >= 3) - caps |= YASL_CAP_DRAG; + caps |= YASL_PRIVATE_CAP_DRAG; if (c->toplevel_drag_mgr) - caps |= YASL_CAP_DRAG_CARRY; + caps |= YASL_PRIVATE_CAP_DRAG_CARRY; if (c->primary_selection_mgr) - caps |= YASL_CAP_PRIMARY_SELECTION; + caps |= YASL_PRIVATE_CAP_PRIMARY_SELECTION; return caps; } @@ -321,15 +333,18 @@ static void conn_unlink(struct wayland_surface *w) } -static bool spec_valid(const struct yasl_window_spec *spec) +static bool spec_valid(const struct yasl_private_window_spec *spec) { size_t bytes; - return spec && spec->size.x > 0 && spec->size.y > 0 && - buffer_size(spec->size.x, spec->size.y, &bytes); + if (!spec || spec->size.x <= 0 || spec->size.y <= 0) + return false; + return spec->external_gpu && !(spec->flags & YASL_PRIVATE_SURFACE_CPU) + ? spec->size.x <= INT32_MAX && spec->size.y <= INT32_MAX + : buffer_size(spec->size.x, spec->size.y, &bytes); } struct wayland_surface *wayland_surface_create_toplevel(struct wayland_display *c, - const struct yasl_window_spec *spec, void *graphics_device) + const struct yasl_private_window_spec *spec, void *graphics_device) { (void)graphics_device; if (!c || c->lost || !spec_valid(spec)) @@ -340,28 +355,35 @@ struct wayland_surface *wayland_surface_create_toplevel(struct wayland_display * if (!w) return NULL; c->depth++; conn_link(c, w); - w->base.state = YASL_STATE_CSD; + w->base.state = YASL_PRIVATE_STATE_CSD; + w->base.creation_flags = spec->flags; + w->base.presenter = (spec->external_gpu && !(spec->flags & YASL_PRIVATE_SURFACE_CPU)) + ? YASL_PRIVATE_PRESENTER_NONE : YASL_PRIVATE_PRESENTER_CPU; wayland_surface_set_callbacks(w, NULL, NULL); - w->cursor = YASL_CURSOR_TEXT; + w->cursor = YASL_PRIVATE_CURSOR_TEXT; w->req_width = width; w->req_height = height; w->logical_width = width; w->logical_height = height; - w->scale_120 = YASL_SCALE_UNIT; - w->pending_scale_120 = YASL_SCALE_UNIT; + w->scale_120 = YASL_PRIVATE_SCALE_UNIT; + w->pending_scale_120 = YASL_PRIVATE_SCALE_UNIT; w->scale_factor = 1.0; w->size_pending = true; w->clock.refresh_ns = c->outputs && c->outputs->refresh_ns - ? c->outputs->refresh_ns : YASL_REFRESH_DEFAULT_NS; + ? c->outputs->refresh_ns : YASL_PRIVATE_REFRESH_DEFAULT_NS; w->surface = wl_compositor_create_surface(c->compositor); if (!w->surface) goto err; w->base.native_handle = w->surface; - w->base.size = (struct yasl_vector2i){width, height}; - w->base.scale_120 = YASL_SCALE_UNIT; + w->base.size = (struct yasl_private_vector2i){width, height}; + w->base.scale_120 = YASL_PRIVATE_SCALE_UNIT; wl_surface_set_user_data(w->surface, w); if (surface_init_scaling(w) < 0) goto err; if (surface_init_shell(w, spec->title) < 0) goto err; + if (spec->flags & YASL_PRIVATE_SURFACE_FIXED_SIZE) { + xdg_toplevel_set_min_size(w->toplevel, width, height); + xdg_toplevel_set_max_size(w->toplevel, width, height); + } w->opaque = spec->fullscreen; if (spec->fullscreen) xdg_toplevel_set_fullscreen(w->toplevel, NULL); @@ -389,10 +411,10 @@ err: return NULL; } -void wayland_surface_set_callbacks(struct wayland_surface *w, const struct yasl_window_cbs *cbs, void *ud) +void wayland_surface_set_callbacks(struct wayland_surface *w, const struct yasl_private_window_cbs *cbs, void *ud) { if (w && !w->closed) { - w->base.callbacks = cbs ? *cbs : (struct yasl_window_cbs){0}; + w->base.callbacks = cbs ? *cbs : (struct yasl_private_window_cbs){0}; w->base.ud = ud; w->cbs = &w->base.callbacks; w->ud = ud; @@ -410,6 +432,15 @@ void wayland_surface_destroy(struct wayland_surface *w) free(w->ime_commit); free(w->ime_shown); if (w->tearing_control) wp_tearing_control_v1_destroy(w->tearing_control); +#if YASL_HAS_EGL + if (w->egl_context) { + if (eglGetCurrentContext() == w->egl_context) + eglMakeCurrent(w->conn->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroyContext(w->conn->egl_display, w->egl_context); + } + if (w->egl_surface) eglDestroySurface(w->conn->egl_display, w->egl_surface); + if (w->egl_window) wl_egl_window_destroy(w->egl_window); +#endif if (w->fifo) wp_fifo_v1_destroy(w->fifo); if (w->commit_timer) wp_commit_timer_v1_destroy(w->commit_timer); surface_feedback_destroy(w); @@ -427,7 +458,7 @@ void wayland_surface_destroy(struct wayland_surface *w) } struct wayland_display *c = w->conn; conn_unlink(w); - static const struct yasl_window_cbs no_callbacks = {0}; + static const struct yasl_private_window_cbs no_callbacks = {0}; *w = (struct wayland_surface){ .base = w->base, .conn_next = w->conn_next, @@ -446,16 +477,20 @@ void wayland_surface_destroy(struct wayland_surface *w) void wayland_surface_schedule_redraw(struct wayland_surface *w) { if (!w || w->closed) return; + if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU) { + yasl_private_surface_notify_refresh(&w->base); + return; + } w->want_draw = true; if (w->conn && w->conn->loop) - yasl_loop_kick(w->conn->loop); + yasl_private_loop_kick(w->conn->loop); } -struct yasl_geometry wayland_surface_geometry(const struct wayland_surface *w) +struct yasl_private_geometry wayland_surface_geometry(const struct wayland_surface *w) { - return (struct yasl_geometry){ - .size = w ? (struct yasl_vector2i){w->width, w->height} : (struct yasl_vector2i){0}, - .scale_120 = w ? w->scale_120 : YASL_SCALE_UNIT, + return (struct yasl_private_geometry){ + .size = w ? (struct yasl_private_vector2i){w->width, w->height} : (struct yasl_private_vector2i){0}, + .scale_120 = w ? w->scale_120 : YASL_PRIVATE_SCALE_UNIT, }; } @@ -473,23 +508,23 @@ bool wayland_surface_start_interactive_move(struct wayland_surface *s) return true; } -bool wayland_surface_start_interactive_resize(struct wayland_surface *s, enum yasl_resize_edge edge) +bool wayland_surface_start_interactive_resize(struct wayland_surface *s, enum yasl_private_resize_edge edge) { - if (!s || !s->toplevel || !s->conn || edge == YASL_EDGE_NONE) return false; + if (!s || !s->toplevel || !s->conn || edge == YASL_PRIVATE_EDGE_NONE) return false; struct seat *seat = s->conn->active_seat ? s->conn->active_seat : s->conn->seats; if (!seat || !seat->proxy || !seat->press_serial) return false; xdg_toplevel_resize(s->toplevel, seat->proxy, seat->press_serial, (uint32_t)edge); return true; } -bool wayland_surface_show_window_menu(struct wayland_surface *s, struct yasl_vector2i pos) +bool wayland_surface_show_window_menu(struct wayland_surface *s, struct yasl_private_vector2i pos) { if (!s || !s->toplevel || !s->conn) return false; struct seat *seat = s->conn->active_seat ? s->conn->active_seat : s->conn->seats; if (!seat || !seat->proxy || !seat->press_serial) return false; - uint32_t scale = s->scale_120 ? s->scale_120 : YASL_SCALE_UNIT; + uint32_t scale = s->scale_120 ? s->scale_120 : YASL_PRIVATE_SCALE_UNIT; xdg_toplevel_show_window_menu(s->toplevel, seat->proxy, seat->press_serial, - yasl_scale_to_logical(pos.x, scale), - yasl_scale_to_logical(pos.y, scale)); + yasl_private_scale_to_logical(pos.x, scale), + yasl_private_scale_to_logical(pos.y, scale)); return true; } diff --git a/src/platform/x11/keymap.c b/src/platform/x11/keymap.c index 3c566b3..35178dd 100644 --- a/src/platform/x11/keymap.c +++ b/src/platform/x11/keymap.c @@ -2,7 +2,7 @@ #include <string.h> /* XKB standard physical key names, independent of server numeric keycodes. */ -enum yasl_key yasl_key_from_xkb_name(const char name[4]) +enum yasl_key yasl_private_key_from_xkb_name(const char name[4]) { static const struct { char name[4]; enum yasl_key key; } keys[] = { {{'T', 'L', 'D', 'E'}, YASL_KEY_GRAVE}, diff --git a/src/platform/x11/keymap.h b/src/platform/x11/keymap.h index 8114fb5..9bb009d 100644 --- a/src/platform/x11/keymap.h +++ b/src/platform/x11/keymap.h @@ -1,8 +1,8 @@ -#ifndef YASL_X11_KEYMAP_H -#define YASL_X11_KEYMAP_H +#ifndef YASL_PRIVATE_X11_KEYMAP_H +#define YASL_PRIVATE_X11_KEYMAP_H #include "yasl/keycodes.h" -enum yasl_key yasl_key_from_xkb_name(const char name[4]); +enum yasl_key yasl_private_key_from_xkb_name(const char name[4]); #endif diff --git a/src/platform/x11/native.c b/src/platform/x11/native.c index 249adec..fffe171 100644 --- a/src/platform/x11/native.c +++ b/src/platform/x11/native.c @@ -1,14 +1,14 @@ #include "common/backend.h" -#include "yasl/native/x11.h" +#include "common/private_native_x11.h" -Display *yasl_x11_display_get_display(struct yasl_display *d) +Display *yasl_private_x11_display_get_display(struct yasl_private_display *d) { - return (d && yasl_display_backend(d) == YASL_BACKEND_X11) ? - (Display *)yasl_display_get_native_handle(d) : NULL; + return (d && yasl_private_display_backend(d) == YASL_PRIVATE_BACKEND_X11) ? + (Display *)yasl_private_display_get_native_handle(d) : NULL; } -uintptr_t yasl_x11_surface_get_window(struct yasl_surface *s) +uintptr_t yasl_private_x11_surface_get_window(struct yasl_surface *s) { - return (s && s->display && yasl_display_backend(s->display) == YASL_BACKEND_X11) ? - (uintptr_t)yasl_surface_get_native_handle(s) : 0; + return (s && s->display && yasl_private_display_backend(s->display) == YASL_PRIVATE_BACKEND_X11) ? + (uintptr_t)yasl_private_surface_get_native_handle(s) : 0; } diff --git a/src/platform/x11/window.c b/src/platform/x11/window.c index 004d56c..8129dd5 100644 --- a/src/platform/x11/window.c +++ b/src/platform/x11/window.c @@ -16,6 +16,10 @@ #include <X11/keysym.h> #include <X11/extensions/XInput2.h> #include <X11/extensions/XShm.h> +#if YASL_HAS_EGL +#include <EGL/egl.h> +#include <EGL/eglext.h> +#endif #include <sys/ipc.h> #include <sys/shm.h> #if YASL_HAS_XRANDR @@ -35,7 +39,7 @@ #include <wchar.h> #define TEXT_LIMIT (16 * 1024 * 1024) -#define PASTE_TIMEOUT_NS YASL_MS_TO_NS(30000) +#define PASTE_TIMEOUT_NS YASL_PRIVATE_MS_TO_NS(30000) #ifndef likely #define likely(x) __builtin_expect(!!(x), 1) @@ -50,26 +54,33 @@ struct x11_surface { struct x11_surface *dead_next; Window window; GC gc; + Colormap colormap; XIC ic; +#if YASL_HAS_EGL + EGLContext gl_context; + EGLSurface gl_surface; + EGLConfig gl_config; +#endif XImage *image; uint32_t *pixels; bool shm_active; XShmSegmentInfo shm_info; - struct yasl_vector2i size; - const struct yasl_window_cbs *cbs; + struct yasl_private_vector2i size; + const struct yasl_private_window_cbs *cbs; void *ud; struct input_queue input; - struct yasl_frame frame; - struct yasl_damage_list damage; - struct yasl_frame_clock clock; + struct yasl_private_frame frame; + struct yasl_private_damage_list damage; + struct yasl_private_frame_clock clock; bool want_draw, drawing, presented, destroyed; + bool focused; struct x11_surface *parent; bool is_popup; - struct yasl_vector2i content_offset; + struct yasl_private_vector2i content_offset; char *ime_preedit; int ime_cursor_begin; int ime_cursor_end; - struct yasl_rect2i ime_caret; + struct yasl_private_rect2i ime_caret; }; struct paste_request { @@ -79,33 +90,45 @@ struct paste_request { Window window; Atom selection; uint64_t id; - yasl_timer_id timeout; + yasl_private_timer_id timeout; }; struct tablet_axis { int number; double min, max, value; + bool valid; +}; + +struct x11_scroll_axis { + int number; + double increment, value; + bool valid, initialized; + int type; }; struct x11_pointer { struct x11_pointer *next; int id; - enum yasl_pointer_device device; - struct tablet_axis pressure, tilt_x, tilt_y; + enum yasl_private_pointer_device device; + unsigned tool; + struct tablet_axis pressure, tilt_x, tilt_y, rotation, distance, tangential, wheel; + struct x11_scroll_axis scroll_x, scroll_y; + uint32_t buttons; + bool finger_scroll; }; struct x11_display { - struct yasl_display base; + struct yasl_private_display base; Display *native; int screen; - struct yasl_loop *loop; + struct yasl_private_loop *loop; int watch; struct x11_surface *surfaces, *dead; int depth; XIM im; Window selection_window; Atom protocols, delete_window, utf8, clipboard, targets, paste_property, incr; - Atom net_name, net_state, fullscreen, maximized_h, maximized_v; + Atom net_name, net_state, fullscreen, maximized_h, maximized_v, motif_hints; Atom xdnd_aware, xdnd_enter, xdnd_position, xdnd_status, xdnd_leave, xdnd_drop, xdnd_finished; Atom xdnd_selection, xdnd_action_copy, xdnd_type_list, uri_list; char *selection[2]; @@ -121,20 +144,26 @@ struct x11_display { int xkb_event; enum yasl_key keys[256]; Cursor cursors[16]; - Atom abs_pressure, abs_tilt_x, abs_tilt_y, tool_type, eraser; + Atom abs_pressure, abs_tilt_x, abs_tilt_y, abs_rotation, abs_distance, abs_tangential, abs_wheel; + Atom tool_type, eraser; + Atom brush, airbrush, pencil; struct x11_pointer *pointers; +#if YASL_HAS_EGL + EGLDisplay egl; + bool egl_ready; +#endif struct { bool active; struct x11_surface *origin; struct x11_surface *carried; - struct yasl_vector2i carry_offset; - struct yasl_drag_listener listener; + struct yasl_private_vector2i carry_offset; + struct yasl_private_drag_listener listener; void *ud; struct x11_surface *hover; - struct yasl_vector2i hover_pos; + struct yasl_private_vector2i hover_pos; Window icon_window; Pixmap icon_pixmap; - struct yasl_vector2i hotspot; + struct yasl_private_vector2i hotspot; } drag; struct { Window source; @@ -142,7 +171,7 @@ struct x11_display { bool accepts; bool has_uri_list; struct x11_surface *surface; - struct yasl_vector2i pos; + struct yasl_private_vector2i pos; Time time; } xdnd; struct { @@ -153,11 +182,11 @@ struct x11_display { } pending_drop; }; -static bool place(struct yasl_surface *base, struct yasl_vector2i at); -static void drag_end(struct x11_display *d, enum yasl_drag_end how); -static void drag_cancel(struct yasl_display *base); +static bool place(struct yasl_surface *base, struct yasl_private_vector2i at); +static void drag_end(struct x11_display *d, enum yasl_private_drag_end how); +static void drag_cancel(struct yasl_private_display *base); static void schedule_redraw(struct yasl_surface *base); -static uint64_t paste_timeout(void *ud, yasl_timer_id id, uint64_t interval_ns); +static uint64_t paste_timeout(void *ud, yasl_private_timer_id id, uint64_t interval_ns); static void leave(struct x11_display *d) { @@ -169,7 +198,7 @@ static void leave(struct x11_display *d) } } -static void push(struct x11_surface *s, struct yasl_event event, +static void push(struct x11_surface *s, struct yasl_private_event event, const char *text, size_t len) { if (s->destroyed) return; @@ -189,39 +218,39 @@ static unsigned long component(uint32_t color, unsigned long mask) return (((unsigned long)color * m + 127) / 255) << shift; } -static void drag_set_hover(struct x11_display *d, struct x11_surface *s, struct yasl_vector2i pos) +static void drag_set_hover(struct x11_display *d, struct x11_surface *s, struct yasl_private_vector2i pos) { if (!d->drag.active) return; if (d->drag.hover && d->drag.hover != s) { struct x11_surface *old = d->drag.hover; - struct yasl_vector2i old_pos = d->drag.hover_pos; + struct yasl_private_vector2i old_pos = d->drag.hover_pos; d->drag.hover = NULL; if (!old->destroyed) - yasl_surface_notify_drag(&old->base, YASL_DRAG_LEAVE, old_pos); + yasl_private_surface_notify_drag(&old->base, YASL_PRIVATE_DRAG_LEAVE, old_pos); if (!d->drag.active || (s && s->destroyed)) return; } if (s && s->destroyed) return; d->drag.hover = s; d->drag.hover_pos = pos; if (s && !s->destroyed) - yasl_surface_notify_drag(&s->base, YASL_DRAG_OVER, pos); + yasl_private_surface_notify_drag(&s->base, YASL_PRIVATE_DRAG_OVER, pos); } -static void drag_leave_hover(struct x11_display *d, struct x11_surface *s, struct yasl_vector2i pos) +static void drag_leave_hover(struct x11_display *d, struct x11_surface *s, struct yasl_private_vector2i pos) { if (!d->drag.active || d->drag.hover != s) return; d->drag.hover = NULL; if (s && !s->destroyed) - yasl_surface_notify_drag(&s->base, YASL_DRAG_LEAVE, pos); + yasl_private_surface_notify_drag(&s->base, YASL_PRIVATE_DRAG_LEAVE, pos); } static void drag_drop_hover(struct x11_display *d) { if (!d->drag.active) return; - drag_end(d, YASL_DRAG_DROPPED); + drag_end(d, YASL_PRIVATE_DRAG_DROPPED); } -static int yasl_x11_error_handler(Display *native, XErrorEvent *event) +static int yasl_private_x11_error_handler(Display *native, XErrorEvent *event) { (void)native; (void)event; @@ -230,7 +259,7 @@ static int yasl_x11_error_handler(Display *native, XErrorEvent *event) return 0; } -static struct x11_surface *surface_at_root_pos(struct x11_display *d, int rx, int ry, struct yasl_vector2i *out_pos) +static struct x11_surface *surface_at_root_pos(struct x11_display *d, int rx, int ry, struct yasl_private_vector2i *out_pos) { if (!d->native) return NULL; Window ignore_icon = d->drag.icon_window; @@ -243,7 +272,7 @@ static struct x11_surface *surface_at_root_pos(struct x11_display *d, int rx, in if (XTranslateCoordinates(d->native, RootWindow(d->native, d->screen), s->window, rx, ry, &tx, &ty, &sub)) { if (tx >= 0 && ty >= 0 && tx < s->size.x && ty < s->size.y) { - if (out_pos) *out_pos = (struct yasl_vector2i){tx, ty}; + if (out_pos) *out_pos = (struct yasl_private_vector2i){tx, ty}; return s; } } @@ -253,19 +282,31 @@ static struct x11_surface *surface_at_root_pos(struct x11_display *d, int rx, in static uint32_t modifiers(unsigned state) { - return ((state & ShiftMask) ? YASL_MOD_SHIFT : 0) | - ((state & ControlMask) ? YASL_MOD_CTRL : 0) | - ((state & Mod1Mask) ? YASL_MOD_ALT : 0) | - ((state & Mod4Mask) ? YASL_MOD_SUPER : 0) | - ((state & LockMask) ? YASL_MOD_CAPS : 0); + return ((state & ShiftMask) ? YASL_PRIVATE_MOD_SHIFT : 0) | + ((state & ControlMask) ? YASL_PRIVATE_MOD_CTRL : 0) | + ((state & Mod1Mask) ? YASL_PRIVATE_MOD_ALT : 0) | + ((state & Mod4Mask) ? YASL_PRIVATE_MOD_SUPER : 0) | + ((state & LockMask) ? YASL_PRIVATE_MOD_CAPS : 0); } static void pointer_info(struct x11_display *d, struct x11_pointer *p, const XIDeviceInfo *info) { p->id = info->deviceid; - p->device = YASL_PTR_DEV_MOUSE; + p->device = YASL_PRIVATE_PTR_DEV_MOUSE; + p->tool = 0; + p->buttons = 0; p->pressure.number = p->tilt_x.number = p->tilt_y.number = -1; + p->rotation.number = p->distance.number = p->tangential.number = p->wheel.number = -1; + p->scroll_x.number = p->scroll_y.number = -1; + if (info->name) { + for (const char *s = info->name; *s; s++) { + if (!strncasecmp(s, "touchpad", 8) || !strncasecmp(s, "trackpad", 8)) { + p->finger_scroll = true; + break; + } + } + } bool touch = false; for (int i = 0; i < info->num_classes; i++) { XIAnyClassInfo *c = info->classes[i]; @@ -275,17 +316,42 @@ static void pointer_info(struct x11_display *d, struct x11_pointer *p, if (v->mode != XIModeAbsolute) continue; struct tablet_axis *axis = v->label == d->abs_pressure ? &p->pressure : v->label == d->abs_tilt_x ? &p->tilt_x : - v->label == d->abs_tilt_y ? &p->tilt_y : NULL; - if (axis) *axis = (struct tablet_axis){v->number, v->min, v->max, v->value}; + v->label == d->abs_tilt_y ? &p->tilt_y : + v->label == d->abs_rotation ? &p->rotation : + v->label == d->abs_distance ? &p->distance : + v->label == d->abs_tangential ? &p->tangential : + v->label == d->abs_wheel ? &p->wheel : NULL; + if (axis) *axis = (struct tablet_axis){v->number, v->min, v->max, v->value, true}; + } + for (int i = 0; i < info->num_classes; i++) { + XIAnyClassInfo *c = info->classes[i]; + if (c->type != XIScrollClass) continue; + XIScrollClassInfo *scroll = (XIScrollClassInfo *)c; + struct x11_scroll_axis *axis = scroll->scroll_type == XIScrollTypeVertical ? + &p->scroll_y : scroll->scroll_type == XIScrollTypeHorizontal ? &p->scroll_x : NULL; + if (!axis) continue; + *axis = (struct x11_scroll_axis){.number = scroll->number, + .increment = scroll->increment, .type = scroll->scroll_type}; + for (int j = 0; j < info->num_classes; j++) { + XIAnyClassInfo *vclass = info->classes[j]; + if (vclass->type != XIValuatorClass) continue; + XIValuatorClassInfo *v = (XIValuatorClassInfo *)vclass; + if (v->number == scroll->number) { + axis->value = v->value; + axis->initialized = axis->valid = true; + break; + } + } } if (!touch && p->pressure.number >= 0) { - p->device = YASL_PTR_DEV_PEN; + p->device = YASL_PRIVATE_PTR_DEV_PEN; + p->tool = 1; /* Xorg's Wacom and libinput drivers expose separate eraser devices. * Use the tool property when available, with the device name as a * fallback for servers (including Xwayland) without that property. */ if (info->name) { for (const char *s = info->name; *s; s++) - if (!strncasecmp(s, "eraser", 6)) { p->device = YASL_PTR_DEV_ERASER; break; } + if (!strncasecmp(s, "eraser", 6)) { p->device = YASL_PRIVATE_PTR_DEV_ERASER; break; } } } } @@ -301,17 +367,21 @@ static struct x11_pointer *pointer_for(struct x11_display *d, int id) struct x11_pointer *p = count ? calloc(1, sizeof(*p)) : NULL; if (p) { pointer_info(d, p, info); - if (p->device == YASL_PTR_DEV_PEN || p->device == YASL_PTR_DEV_ERASER) { + if (p->device == YASL_PRIVATE_PTR_DEV_PEN || p->device == YASL_PRIVATE_PTR_DEV_ERASER) { Atom type; int format; unsigned long n, after; unsigned char *value = NULL; if (XIGetProperty(d->native, id, d->tool_type, 0, 1, False, XA_ATOM, &type, &format, &n, &after, &value) == Success && - type == XA_ATOM && format == 32 && n == 1 && value) + type == XA_ATOM && format == 32 && n == 1 && value) { /* XIGetProperty packs format-32 values as uint32_t, unlike * XGetWindowProperty's array of native-sized longs. */ - p->device = *(uint32_t *)value == d->eraser ? YASL_PTR_DEV_ERASER : p->device; + if (*(uint32_t *)value == d->eraser) p->device = YASL_PRIVATE_PTR_DEV_ERASER; + else if (d->brush && *(uint32_t *)value == d->brush) p->tool = 3; + else if (d->airbrush && *(uint32_t *)value == d->airbrush) p->tool = 4; + else if (d->pencil && *(uint32_t *)value == d->pencil) p->tool = 5; + } if (value) XFree(value); } p->next = d->pointers; @@ -330,21 +400,61 @@ static void clear_pointers(struct x11_display *d) } } -static void update_axes(struct x11_pointer *p, const XIValuatorState *state) +static uint32_t update_axes(struct x11_pointer *p, const XIValuatorState *state) { + uint32_t changed = 0; const double *value = state->values; for (int i = 0; i < state->mask_len * 8; i++) { if (!XIMaskIsSet(state->mask, i)) continue; - struct tablet_axis *axes[] = {&p->pressure, &p->tilt_x, &p->tilt_y}; + struct tablet_axis *axes[] = {&p->pressure, &p->tilt_x, &p->tilt_y, &p->rotation, + &p->distance, &p->tangential, &p->wheel}; + const uint32_t bits[] = {YASL_PRIVATE_PEN_PRESSURE, YASL_PRIVATE_PEN_TILT, + YASL_PRIVATE_PEN_TILT, YASL_PRIVATE_PEN_ROTATION, YASL_PRIVATE_PEN_DISTANCE, + YASL_PRIVATE_PEN_TANGENTIAL, YASL_PRIVATE_PEN_WHEEL}; for (unsigned a = 0; a < sizeof(axes) / sizeof(axes[0]); a++) - if (axes[a]->number == i && isfinite(*value)) axes[a]->value = *value; + if (axes[a]->number == i && isfinite(*value)) { + axes[a]->value = *value; + axes[a]->valid = true; + changed |= bits[a]; + } + value++; + } + return changed; +} + +static void update_scroll(struct x11_pointer *p, const XIValuatorState *state, + struct yasl_private_pointer_event *out) +{ + const double *value = state->values; + for (int i = 0; i < state->mask_len * 8; i++) { + if (!XIMaskIsSet(state->mask, i)) continue; + struct x11_scroll_axis *axes[] = {&p->scroll_x, &p->scroll_y}; + for (unsigned a = 0; a < 2; a++) { + struct x11_scroll_axis *axis = axes[a]; + if (axis->number != i || !isfinite(*value)) continue; + if (axis->initialized) { + double delta = *value - axis->value; + if (axis->increment != 0 && isfinite(delta)) { + double lines = delta / axis->increment; + if (a == 0) out->scroll.x += lines; + else out->scroll.y += lines; + if (lines != 0) { + out->scroll_valid |= 1u; + out->scroll_source = p->finger_scroll ? 2u : 1u; + out->scroll_phase = 2u; + } + } + } + axis->value = *value; + axis->initialized = true; + } value++; } } static uint16_t pressure_value(const struct tablet_axis *a) { - if (a->number < 0 || !(a->max > a->min) || !isfinite(a->value)) return 0; + if (a->number < 0 || !(a->max > a->min) || !a->valid || !isfinite(a->value)) return 0; double v = (a->value - a->min) / (a->max - a->min); return (uint16_t)lround(fmax(0, fmin(1, v)) * 65535); } @@ -352,50 +462,121 @@ static uint16_t pressure_value(const struct tablet_axis *a) static int8_t tilt_value(const struct tablet_axis *a) { /* Xorg tablet tilt valuators are already in degrees. */ - return a->number < 0 || !isfinite(a->value) ? 0 : + return a->number < 0 || !a->valid || !isfinite(a->value) ? 0 : (int8_t)lround(fmax(-90, fmin(90, a->value))); } -static enum yasl_pointer_button pointer_button(unsigned button) +static enum yasl_private_pointer_button pointer_button(unsigned button) +{ + return button == 1 ? YASL_PRIVATE_BUTTON_PRIMARY : + button == 2 ? YASL_PRIVATE_BUTTON_MIDDLE : + button == 3 ? YASL_PRIVATE_BUTTON_SECONDARY : + button >= 8 && button <= INT16_MAX ? (enum yasl_private_pointer_button)(button - 5) : + YASL_PRIVATE_BUTTON_BACK; +} + +static uint32_t pen_axes_valid(const struct x11_pointer *p) { - return button == 1 ? YASL_BUTTON_PRIMARY : - button == 2 ? YASL_BUTTON_MIDDLE : - button == 3 ? YASL_BUTTON_SECONDARY : - button >= 8 && button <= INT16_MAX ? (enum yasl_pointer_button)(button - 5) : - YASL_BUTTON_BACK; + return (p->pressure.valid ? YASL_PRIVATE_PEN_PRESSURE : 0) | + (p->tilt_x.valid || p->tilt_y.valid ? YASL_PRIVATE_PEN_TILT : 0) | + (p->rotation.valid ? YASL_PRIVATE_PEN_ROTATION : 0) | + (p->distance.valid ? YASL_PRIVATE_PEN_DISTANCE : 0) | + (p->tangential.valid ? YASL_PRIVATE_PEN_TANGENTIAL : 0) | + (p->wheel.valid ? YASL_PRIVATE_PEN_WHEEL : 0); +} + +static double axis_normalized(const struct tablet_axis *axis) +{ + return axis->valid && axis->max > axis->min && isfinite(axis->value) ? + fmax(0, fmin(1, (axis->value - axis->min) / (axis->max - axis->min))) : 0; +} + +static void push_pen_sample(struct x11_surface *s, struct x11_pointer *p, + unsigned action, double x, double y, uint32_t changed, unsigned changed_button) +{ + if (!p || (p->device != YASL_PRIVATE_PTR_DEV_PEN && p->device != YASL_PRIVATE_PTR_DEV_ERASER)) return; + push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_PEN, + .pen = { + .device_id = (uint64_t)(unsigned)p->id, + .tool_id = 0, /* XI2 exposes source IDs, but not a stable per-tool serial. */ + .x = x, .y = y, + .pressure = p->pressure.valid && p->pressure.max > p->pressure.min ? + fmax(0, fmin(1, (p->pressure.value - p->pressure.min) / + (p->pressure.max - p->pressure.min))) : 0, + .tilt_x = p->tilt_x.valid ? fmax(-90, fmin(90, p->tilt_x.value)) : 0, + .tilt_y = p->tilt_y.valid ? fmax(-90, fmin(90, p->tilt_y.value)) : 0, + .rotation = p->rotation.valid ? p->rotation.value : 0, + .distance = axis_normalized(&p->distance), + .tangential = p->tangential.valid && p->tangential.max > p->tangential.min ? + fmax(-1, fmin(1, 2 * axis_normalized(&p->tangential) - 1)) : 0, + .wheel_degrees = p->wheel.valid ? p->wheel.value : 0, + .axes_valid = pen_axes_valid(p), .axes_changed = changed, + .buttons = p->buttons, .changed_button = changed_button, + .in_contact = (p->buttons & YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY)) != 0, + .tool = p->device == YASL_PRIVATE_PTR_DEV_ERASER ? 2u : p->tool, + .action = action, + }}, NULL, 0); } static void xi_pointer_event(struct x11_surface *s, const XIDeviceEvent *ev) { struct x11_display *d = (struct x11_display *)s->base.display; struct x11_pointer *p = pointer_for(d, ev->sourceid); - struct yasl_event event = { + struct yasl_private_event event = { .pointer = { - .pos = {yasl_scale_to_device(ev->event_x, YASL_SCALE_UNIT), - yasl_scale_to_device(ev->event_y, YASL_SCALE_UNIT)}, - .device = p ? p->device : YASL_PTR_DEV_MOUSE, + .pos = {yasl_private_scale_to_device(ev->event_x, YASL_PRIVATE_SCALE_UNIT), + yasl_private_scale_to_device(ev->event_y, YASL_PRIVATE_SCALE_UNIT)}, + .device = p ? p->device : YASL_PRIVATE_PTR_DEV_MOUSE, .mods = modifiers(ev->mods.effective), }, }; - if (p && (p->device == YASL_PTR_DEV_PEN || p->device == YASL_PTR_DEV_ERASER)) { - update_axes(p, &ev->valuators); - if (ev->evtype == XI_ButtonRelease && ev->detail == 1) + uint32_t pen_changed = 0; + if (p && (p->device == YASL_PRIVATE_PTR_DEV_PEN || p->device == YASL_PRIVATE_PTR_DEV_ERASER)) { + pen_changed = update_axes(p, &ev->valuators); + if (ev->evtype == XI_ButtonRelease && ev->detail == 1) { p->pressure.value = p->pressure.min; - event.pointer.pen = (struct yasl_pointer_pen){ + pen_changed |= p->pressure.valid ? YASL_PRIVATE_PEN_PRESSURE : 0; + } + if (ev->evtype == XI_ButtonPress || ev->evtype == XI_ButtonRelease) { + enum yasl_private_pointer_button button = pointer_button(ev->detail); + uint32_t mask = YASL_PRIVATE_BUTTON_BIT(button); + if (ev->evtype == XI_ButtonPress) p->buttons |= mask; + else p->buttons &= ~mask; + } + event.pointer.pen = (struct yasl_private_pointer_pen){ pressure_value(&p->pressure), tilt_value(&p->tilt_x), tilt_value(&p->tilt_y)}; } - if (ev->evtype == XI_Motion) event.type = YASL_EVENT_POINTER_MOTION; + if (p) update_scroll(p, &ev->valuators, &event.pointer); + if (ev->evtype == XI_Motion) event.type = YASL_PRIVATE_EVENT_POINTER_MOTION; else if (ev->detail >= 4 && ev->detail <= 7) { if (ev->evtype == XI_ButtonRelease) return; - event.type = YASL_EVENT_POINTER_SCROLL; + event.type = YASL_PRIVATE_EVENT_POINTER_SCROLL; if (ev->detail <= 5) event.pointer.scroll.y = ev->detail == 4 ? -1 : 1; else event.pointer.scroll.x = ev->detail == 6 ? -1 : 1; + event.pointer.scroll_valid = 1u; + event.pointer.scroll_source = p && p->finger_scroll ? 2u : 1u; + event.pointer.scroll_phase = 2u; } else { - event.type = ev->evtype == XI_ButtonPress ? YASL_EVENT_POINTER_DOWN : YASL_EVENT_POINTER_UP; + event.type = ev->evtype == XI_ButtonPress ? YASL_PRIVATE_EVENT_POINTER_DOWN : YASL_PRIVATE_EVENT_POINTER_UP; event.pointer.button = pointer_button(ev->detail); } d->mods = event.pointer.mods; + if (p && (p->device == YASL_PRIVATE_PTR_DEV_PEN || p->device == YASL_PRIVATE_PTR_DEV_ERASER) && + ev->detail != 4 && ev->detail != 5 && ev->detail != 6 && ev->detail != 7) { + unsigned action = ev->evtype == XI_Motion ? YASL_PRIVATE_PEN_MOVE : + ev->evtype == XI_ButtonPress ? (ev->detail == 1 ? YASL_PRIVATE_PEN_DOWN : YASL_PRIVATE_PEN_BUTTON) : + (ev->detail == 1 ? YASL_PRIVATE_PEN_UP : YASL_PRIVATE_PEN_BUTTON); + push_pen_sample(s, p, action, ev->event_x, ev->event_y, pen_changed, + ev->evtype == XI_Motion ? 0u : (unsigned)ev->detail); + } push(s, event, NULL, 0); + if (event.type == YASL_PRIVATE_EVENT_POINTER_MOTION && event.pointer.scroll_valid) { + event.type = YASL_PRIVATE_EVENT_POINTER_SCROLL; + event.pointer.pos = (struct yasl_private_vector2i){ + yasl_private_scale_to_device(ev->event_x, YASL_PRIVATE_SCALE_UNIT), + yasl_private_scale_to_device(ev->event_y, YASL_PRIVATE_SCALE_UNIT)}; + push(s, event, NULL, 0); + } } static void xi_event(struct x11_display *d, XGenericEventCookie *cookie) @@ -403,15 +584,20 @@ static void xi_event(struct x11_display *d, XGenericEventCookie *cookie) if (cookie->evtype == XI_HierarchyChanged || cookie->evtype == XI_DeviceChanged) { if (cookie->evtype == XI_DeviceChanged && ((XIDeviceChangedEvent *)cookie->data)->reason == XISlaveSwitch) return; + for (struct x11_pointer *p = d->pointers; p; p = p->next) { + if (!p->buttons) continue; + for (struct x11_surface *s = d->surfaces; s; s = s->next) + push_pen_sample(s, p, YASL_PRIVATE_PEN_CANCEL, 0, 0, 0, 0); + } clear_pointers(d); if (cookie->evtype == XI_HierarchyChanged && (((XIHierarchyEvent *)cookie->data)->flags & (XISlaveRemoved | XIDeviceDisabled | XISlaveDetached | XIMasterRemoved))) { /* Removal/disable may interrupt a drag before a release arrives. */ if (d->drag.active) - drag_end(d, YASL_DRAG_CANCELLED); + drag_end(d, YASL_PRIVATE_DRAG_CANCELLED); for (struct x11_surface *s = d->surfaces; s; s = s->next) - push(s, (struct yasl_event){.type = YASL_EVENT_INPUT_RESET}, NULL, 0); + push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_INPUT_RESET}, NULL, 0); } return; } @@ -430,22 +616,22 @@ static void xi_event(struct x11_display *d, XGenericEventCookie *cookie) XMoveWindow(d->native, d->drag.icon_window, (int)round(mev->root_x) - d->drag.hotspot.x, (int)round(mev->root_y) - d->drag.hotspot.y); - struct yasl_vector2i hpos = {yasl_scale_to_device(mev->event_x, YASL_SCALE_UNIT), - yasl_scale_to_device(mev->event_y, YASL_SCALE_UNIT)}; + struct yasl_private_vector2i hpos = {yasl_private_scale_to_device(mev->event_x, YASL_PRIVATE_SCALE_UNIT), + yasl_private_scale_to_device(mev->event_y, YASL_PRIVATE_SCALE_UNIT)}; struct x11_surface *hsurf = d->native ? surface_at_root_pos(d, (int)round(mev->root_x), (int)round(mev->root_y), &hpos) : s; drag_set_hover(d, hsurf, hpos); } else if (cookie->evtype == XI_Enter && d->drag.active) { const XIEnterEvent *eev = cookie->data; - struct yasl_vector2i hpos = {yasl_scale_to_device(eev->event_x, YASL_SCALE_UNIT), - yasl_scale_to_device(eev->event_y, YASL_SCALE_UNIT)}; + struct yasl_private_vector2i hpos = {yasl_private_scale_to_device(eev->event_x, YASL_PRIVATE_SCALE_UNIT), + yasl_private_scale_to_device(eev->event_y, YASL_PRIVATE_SCALE_UNIT)}; struct x11_surface *hsurf = d->native ? surface_at_root_pos(d, (int)round(eev->root_x), (int)round(eev->root_y), &hpos) : s; drag_set_hover(d, hsurf, hpos); } else if (cookie->evtype == XI_Leave && d->drag.active) { const XIEnterEvent *lev = cookie->data; - struct yasl_vector2i lpos = {yasl_scale_to_device(lev->event_x, YASL_SCALE_UNIT), - yasl_scale_to_device(lev->event_y, YASL_SCALE_UNIT)}; + struct yasl_private_vector2i lpos = {yasl_private_scale_to_device(lev->event_x, YASL_PRIVATE_SCALE_UNIT), + yasl_private_scale_to_device(lev->event_y, YASL_PRIVATE_SCALE_UNIT)}; drag_leave_hover(d, s, lpos); } else if (cookie->evtype == XI_ButtonRelease && d->drag.active) { drag_drop_hover(d); @@ -455,24 +641,35 @@ static void xi_event(struct x11_display *d, XGenericEventCookie *cookie) else { const XIEnterEvent *ev = cookie->data; struct x11_pointer *p = pointer_for(d, ev->sourceid); - if (p) p->pressure.value = p->pressure.min; - push(s, (struct yasl_event){ - .type = cookie->evtype == XI_Leave ? YASL_EVENT_POINTER_LEAVE : YASL_EVENT_POINTER_MOTION, + if (p && cookie->evtype == XI_Leave) { + p->pressure.value = p->pressure.min; + if (p->pressure.valid) push_pen_sample(s, p, YASL_PRIVATE_PEN_LEAVE, + ev->event_x, ev->event_y, YASL_PRIVATE_PEN_PRESSURE, 0); + } else if (p && (p->device == YASL_PRIVATE_PTR_DEV_PEN || p->device == YASL_PRIVATE_PTR_DEV_ERASER)) { + push_pen_sample(s, p, YASL_PRIVATE_PEN_ENTER, ev->event_x, ev->event_y, 0, 0); + } + push(s, (struct yasl_private_event){ + .type = cookie->evtype == XI_Leave ? YASL_PRIVATE_EVENT_POINTER_LEAVE : YASL_PRIVATE_EVENT_POINTER_MOTION, .pointer = { - .pos = {yasl_scale_to_device(ev->event_x, YASL_SCALE_UNIT), - yasl_scale_to_device(ev->event_y, YASL_SCALE_UNIT)}, - .device = p ? p->device : YASL_PTR_DEV_MOUSE, + .pos = {yasl_private_scale_to_device(ev->event_x, YASL_PRIVATE_SCALE_UNIT), + yasl_private_scale_to_device(ev->event_y, YASL_PRIVATE_SCALE_UNIT)}, + .device = p ? p->device : YASL_PRIVATE_PTR_DEV_MOUSE, .mods = modifiers(ev->mods.effective), }, }, NULL, 0); } } -static bool resize(struct x11_surface *s, struct yasl_vector2i size) +static bool resize(struct x11_surface *s, struct yasl_private_vector2i size) { if (size.x <= 0 || size.y <= 0 || size.x > 32767 || size.y > 32767 || (size_t)size.x > SIZE_MAX / sizeof(uint32_t) / (size_t)size.y) return false; + if (s->base.presenter != YASL_PRIVATE_PRESENTER_CPU) { + s->size = size; + s->base.size = size; + return true; + } if (s->image && size.x == s->size.x && size.y == s->size.y) return true; struct x11_display *d = (struct x11_display *)s->base.display; if (!d->native) { @@ -480,7 +677,7 @@ static bool resize(struct x11_surface *s, struct yasl_vector2i size) s->want_draw = true; return true; } - uint64_t start = yasl_loop_now_ns(); + uint64_t start = yasl_private_loop_now_ns(); /* The presented frame goes with the old pixels. */ s->presented = false; @@ -557,9 +754,9 @@ static bool resize(struct x11_surface *s, struct yasl_vector2i size) s->pixels = pixels; s->size = size; s->base.size = size; - s->base.scale_120 = YASL_SCALE_UNIT; + s->base.scale_120 = YASL_PRIVATE_SCALE_UNIT; s->want_draw = true; - yasl_clock_note_resize(&s->clock, yasl_loop_now_ns() - start); + yasl_private_clock_note_resize(&s->clock, yasl_private_loop_now_ns() - start); return true; } @@ -575,7 +772,7 @@ static void cancel_pastes(struct x11_display *d, struct x11_surface *target) p = &r->next; } else if (!target) { *p = r->next; - if (r->timeout) yasl_loop_del_timer(d->loop, r->timeout); + if (r->timeout) yasl_private_loop_del_timer(d->loop, r->timeout); XDestroyWindow(d->native, r->window); free(r); } else { @@ -584,7 +781,7 @@ static void cancel_pastes(struct x11_display *d, struct x11_surface *target) } } -static uint64_t paste_timeout(void *ud, yasl_timer_id id, uint64_t interval_ns) +static uint64_t paste_timeout(void *ud, yasl_private_timer_id id, uint64_t interval_ns) { (void)id; (void)interval_ns; @@ -597,7 +794,7 @@ static uint64_t paste_timeout(void *ud, yasl_timer_id id, uint64_t interval_ns) r->timeout = 0; /* The loop retires this one-shot timer after the callback. */ XDestroyWindow(d->native, r->window); if (s && !s->destroyed) - yasl_surface_complete_paste(&s->base, r->id, YASL_TRANSFER_FAILED, NULL, 0); + yasl_private_surface_complete_paste(&s->base, r->id, YASL_PRIVATE_TRANSFER_FAILED, NULL, 0); free(r); return 0; } @@ -609,7 +806,7 @@ static void surface_destroy(struct yasl_surface *base) struct x11_display *d = (struct x11_display *)base->display; if (d->drag.active) { if (d->drag.origin == s || d->drag.carried == s) - drag_end(d, YASL_DRAG_CANCELLED); + drag_end(d, YASL_PRIVATE_DRAG_CANCELLED); else if (d->drag.hover == s) d->drag.hover = NULL; } @@ -627,6 +824,10 @@ static void surface_destroy(struct yasl_surface *base) free(s->ime_preedit); s->ime_preedit = NULL; if (s->ic) XDestroyIC(s->ic); +#if YASL_HAS_EGL + if (s->gl_context && d->egl_ready) eglDestroyContext(d->egl, s->gl_context); + if (s->gl_surface && d->egl_ready) eglDestroySurface(d->egl, s->gl_surface); +#endif if (s->shm_active) { XShmDetach(d->native, &s->shm_info); if (s->image) XDestroyImage(s->image); @@ -643,6 +844,7 @@ static void surface_destroy(struct yasl_surface *base) if (d->native) { if (s->gc) XFreeGC(d->native, s->gc); if (s->window) XDestroyWindow(d->native, s->window); + if (s->colormap) XFreeColormap(d->native, s->colormap); } input_queue_clear(&s->input); if (d->depth) { @@ -652,11 +854,11 @@ static void surface_destroy(struct yasl_surface *base) } static void set_callbacks(struct yasl_surface *base, - const struct yasl_window_cbs *cbs, void *ud) + const struct yasl_private_window_cbs *cbs, void *ud) { struct x11_surface *s = (struct x11_surface *)base; if (s->destroyed) return; - s->base.callbacks = cbs ? *cbs : (struct yasl_window_cbs){0}; + s->base.callbacks = cbs ? *cbs : (struct yasl_private_window_cbs){0}; s->cbs = &s->base.callbacks; s->base.ud = ud; s->ud = ud; @@ -685,7 +887,7 @@ static bool net_message(struct x11_surface *s, Atom type, SubstructureRedirectMask | SubstructureNotifyMask, &event) != 0; } -static bool surface_set_size(struct yasl_surface *base, struct yasl_vector2i size) +static bool surface_set_size(struct yasl_surface *base, struct yasl_private_vector2i size) { struct x11_surface *s = (struct x11_surface *)base; struct x11_display *d = (struct x11_display *)base->display; @@ -817,7 +1019,7 @@ static char *xim_text_dup(const XIMText *text, size_t *len) if (converted != text->length) { free(out); return NULL; } } out[used] = '\0'; - if (!yasl_utf8_validate(out, used)) { free(out); return NULL; } + if (!yasl_private_utf8_validate(out, used)) { free(out); return NULL; } *len = used; return out; } @@ -831,7 +1033,7 @@ static void xim_preedit_done(XIM im, XPointer client_data, XPointer call_data) free(s->ime_preedit); s->ime_preedit = NULL; s->ime_cursor_begin = s->ime_cursor_end = -1; - yasl_surface_notify_composition(&s->base, NULL, -1, -1); + yasl_private_surface_notify_composition(&s->base, NULL, -1, -1); } static int xim_preedit_start(XIM im, XPointer client_data, XPointer call_data) @@ -880,7 +1082,7 @@ static void xim_preedit_draw(XIM im, XPointer client_data, XPointer call_data) int caret = draw->caret < 0 ? -1 : (int)utf8_offset_for_chars( s->ime_preedit ? s->ime_preedit : "", new_len, draw->caret); s->ime_cursor_begin = s->ime_cursor_end = caret; - yasl_surface_notify_composition(&s->base, s->ime_preedit, caret, caret); + yasl_private_surface_notify_composition(&s->base, s->ime_preedit, caret, caret); } static void xim_preedit_caret(XIM im, XPointer client_data, XPointer call_data) @@ -892,7 +1094,7 @@ static void xim_preedit_caret(XIM im, XPointer client_data, XPointer call_data) int offset = caret->position < 0 ? -1 : (int)utf8_offset_for_chars( s->ime_preedit, strlen(s->ime_preedit), caret->position); s->ime_cursor_begin = s->ime_cursor_end = offset; - yasl_surface_notify_composition(&s->base, s->ime_preedit, offset, offset); + yasl_private_surface_notify_composition(&s->base, s->ime_preedit, offset, offset); } static XIC create_ic(struct x11_display *d, struct x11_surface *s) @@ -923,22 +1125,35 @@ static XIC create_ic(struct x11_display *d, struct x11_surface *s) return ic; } -static struct yasl_surface *create_toplevel(struct yasl_display *base, - const struct yasl_window_spec *spec, void *graphics_device) +static struct yasl_surface *create_toplevel_visual(struct yasl_private_display *base, + const struct yasl_private_window_spec *spec, Visual *visual, int depth, + enum yasl_private_presenter presenter) { - (void)graphics_device; if (!spec) return NULL; struct x11_display *d = (struct x11_display *)base; struct x11_surface *s = calloc(1, sizeof(*s)); if (!s) return NULL; s->base.display = base; - s->clock.refresh_ns = YASL_REFRESH_DEFAULT_NS; + s->base.creation_flags = spec->flags | (spec->external_gpu ? 0u : YASL_PRIVATE_SURFACE_CPU); + s->base.presenter = presenter; + s->clock.refresh_ns = YASL_PRIVATE_REFRESH_DEFAULT_NS; if (!resize(s, spec->size)) { free(s); return NULL; } - s->window = XCreateSimpleWindow(d->native, RootWindow(d->native, d->screen), - 0, 0, (unsigned)spec->size.x, (unsigned)spec->size.y, 0, 0, 0); + if (!visual) visual = DefaultVisual(d->native, d->screen); + if (!depth) depth = DefaultDepth(d->native, d->screen); + s->colormap = XCreateColormap(d->native, RootWindow(d->native, d->screen), visual, AllocNone); + XSetWindowAttributes attrs = {.colormap = s->colormap, .border_pixel = 0, .background_pixel = 0}; + s->window = XCreateWindow(d->native, RootWindow(d->native, d->screen), 0, 0, + (unsigned)spec->size.x, (unsigned)spec->size.y, 0, depth, InputOutput, + visual, CWBorderPixel | CWBackPixel | CWColormap, &attrs); + if (!s->window) { + XFreeColormap(d->native, s->colormap); + free(s); + errno = ENODEV; + return NULL; + } s->base.native_handle = (void *)(uintptr_t)s->window; s->base.size = s->size; - s->base.scale_120 = YASL_SCALE_UNIT; + s->base.scale_120 = YASL_PRIVATE_SCALE_UNIT; s->gc = XCreateGC(d->native, s->window, 0, NULL); XSelectInput(d->native, s->window, ExposureMask | StructureNotifyMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | @@ -955,6 +1170,19 @@ static struct yasl_surface *create_toplevel(struct yasl_display *base, XISelectEvents(d->native, s->window, &mask, 1); } XSetWMProtocols(d->native, s->window, &d->delete_window, 1); + if (spec->flags & YASL_PRIVATE_SURFACE_UNDECORATED) { + struct { unsigned long flags, functions, decorations; long input_mode; unsigned long status; } hints = { + .flags = 1, .decorations = 0, + }; + XChangeProperty(d->native, s->window, d->motif_hints, d->motif_hints, 32, + PropModeReplace, (unsigned char *)&hints, 5); + } + if (spec->flags & YASL_PRIVATE_SURFACE_FIXED_SIZE) { + XSizeHints size_hints = {.flags = PMinSize | PMaxSize, + .min_width = spec->size.x, .max_width = spec->size.x, + .min_height = spec->size.y, .max_height = spec->size.y}; + XSetWMNormalHints(d->native, s->window, &size_hints); + } XClassHint hint = { .res_name = d->app_id, .res_class = d->app_id }; XSetClassHint(d->native, s->window, &hint); Atom xdnd_version = 5; @@ -967,17 +1195,197 @@ static struct yasl_surface *create_toplevel(struct yasl_display *base, if (spec->fullscreen) { XChangeProperty(d->native, s->window, d->net_state, XA_ATOM, 32, PropModeReplace, (unsigned char *)&d->fullscreen, 1); - s->base.state |= YASL_STATE_FULLSCREEN; + s->base.state |= YASL_PRIVATE_STATE_FULLSCREEN; } - XMapWindow(d->native, s->window); + if (!(spec->flags & YASL_PRIVATE_SURFACE_HIDDEN)) XMapWindow(d->native, s->window); XFlush(d->native); return &s->base; } +static struct yasl_surface *create_toplevel(struct yasl_private_display *base, + const struct yasl_private_window_spec *spec, void *graphics_device) +{ + if (!spec) return NULL; + if (graphics_device) { errno = ENOTSUP; return NULL; } + return create_toplevel_visual(base, spec, NULL, 0, + spec->external_gpu ? YASL_PRIVATE_PRESENTER_NONE : YASL_PRIVATE_PRESENTER_CPU); +} + +#if YASL_HAS_EGL +static bool egl_open(struct x11_display *d) +{ + if (d->egl_ready) return true; + d->egl = eglGetDisplay((EGLNativeDisplayType)d->native); + EGLint major = 0, minor = 0; + if (d->egl == EGL_NO_DISPLAY || !eglInitialize(d->egl, &major, &minor)) { + errno = ENOTSUP; + return false; + } + d->egl_ready = true; + return true; +} + +static struct yasl_surface *create_gl_surface(struct yasl_private_display *base, + const struct yasl_private_window_spec *spec, const int *attrs, + struct yasl_surface *share) +{ + if (!spec) { errno = EINVAL; return NULL; } + struct x11_display *d = (struct x11_display *)base; + struct yasl_private_gl_options options; + if (!yasl_private_gl_parse_options(attrs, &options)) { errno = EINVAL; return NULL; } + if (!egl_open(d)) return NULL; + if (options.srgb) { + const char *extensions = eglQueryString(d->egl, EGL_EXTENSIONS); + if (!extensions || !strstr(extensions, "EGL_KHR_gl_colorspace")) { + errno = ENOTSUP; + return NULL; + } + } + + EGLint renderable = options.api == YASL_PRIVATE_GL_OPENGL_ES ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT; + EGLint config_attrs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, renderable, + EGL_RED_SIZE, options.red_bits, + EGL_GREEN_SIZE, options.green_bits, + EGL_BLUE_SIZE, options.blue_bits, + EGL_ALPHA_SIZE, options.alpha_bits, + EGL_DEPTH_SIZE, options.depth_bits, + EGL_STENCIL_SIZE, options.stencil_bits, + EGL_SAMPLE_BUFFERS, options.samples > 0 ? 1 : 0, + EGL_SAMPLES, options.samples, + EGL_NONE, + }; + EGLConfig config = NULL; + EGLint count = 0; + if (!eglChooseConfig(d->egl, config_attrs, &config, 1, &count) || !count) { + errno = ENOTSUP; + return NULL; + } + EGLint visual_id = 0, depth = 0; + if (!eglGetConfigAttrib(d->egl, config, EGL_NATIVE_VISUAL_ID, &visual_id) || + !eglGetConfigAttrib(d->egl, config, EGL_BUFFER_SIZE, &depth)) { + errno = ENODEV; + return NULL; + } + XVisualInfo visual_query = {.visualid = (VisualID)visual_id}; + int visual_count = 0; + XVisualInfo *visual_info = XGetVisualInfo(d->native, VisualIDMask, &visual_query, &visual_count); + if (!visual_info || !visual_count) { + if (visual_info) XFree(visual_info); + errno = ENODEV; + return NULL; + } + Visual *visual = visual_info[0].visual; + depth = visual_info[0].depth; + struct yasl_private_window_spec gl_spec = *spec; + gl_spec.external_gpu = true; + gl_spec.flags &= ~YASL_PRIVATE_SURFACE_CPU; + struct yasl_surface *base_surface = create_toplevel_visual(base, &gl_spec, visual, depth, + YASL_PRIVATE_PRESENTER_GL); + XFree(visual_info); + if (!base_surface) return NULL; + struct x11_surface *s = (struct x11_surface *)base_surface; + s->gl_config = config; + EGLint surface_attrs[] = {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, EGL_NONE}; + s->gl_surface = eglCreateWindowSurface(d->egl, config, + (EGLNativeWindowType)s->window, options.srgb ? surface_attrs : NULL); + if (s->gl_surface == EGL_NO_SURFACE) { + surface_destroy(base_surface); + errno = ENODEV; + return NULL; + } + if (options.api == YASL_PRIVATE_GL_OPENGL) { + if (!eglBindAPI(EGL_OPENGL_API)) { surface_destroy(base_surface); errno = ENOTSUP; return NULL; } + } else if (!eglBindAPI(EGL_OPENGL_ES_API)) { + surface_destroy(base_surface); errno = ENOTSUP; return NULL; + } + EGLContext share_context = EGL_NO_CONTEXT; + if (share) { + struct x11_surface *shared = (struct x11_surface *)share; + if (share->display != base || shared->base.presenter != YASL_PRIVATE_PRESENTER_GL || + shared->gl_context == EGL_NO_CONTEXT) { + surface_destroy(base_surface); errno = EINVAL; return NULL; + } + share_context = shared->gl_context; + } + EGLint context_attrs[12]; + EGLint n = 0; + context_attrs[n++] = EGL_CONTEXT_MAJOR_VERSION; + context_attrs[n++] = options.major; + context_attrs[n++] = EGL_CONTEXT_MINOR_VERSION; + context_attrs[n++] = options.minor; + if (options.api == YASL_PRIVATE_GL_OPENGL && options.profile != YASL_PRIVATE_GL_ANY) { + context_attrs[n++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; + context_attrs[n++] = options.profile == YASL_PRIVATE_GL_CORE ? + EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR : EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; + } + EGLint context_flags = (options.debug ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0) | + (options.forward_compat ? EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR : 0); + if (context_flags) { + context_attrs[n++] = EGL_CONTEXT_FLAGS_KHR; + context_attrs[n++] = context_flags; + } + context_attrs[n] = EGL_NONE; + s->gl_context = eglCreateContext(d->egl, config, share_context, context_attrs); + if (s->gl_context == EGL_NO_CONTEXT) { + surface_destroy(base_surface); + errno = ENOTSUP; + return NULL; + } + return base_surface; +} + +static bool gl_make_current(struct yasl_private_display *base, struct yasl_surface *surface) +{ + struct x11_display *d = (struct x11_display *)base; + if (!d->egl_ready) { errno = ENOTSUP; return false; } + if (!surface) return eglMakeCurrent(d->egl, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + struct x11_surface *s = (struct x11_surface *)surface; + if (surface->display != base || s->base.presenter != YASL_PRIVATE_PRESENTER_GL || !s->gl_context) { + errno = EINVAL; + return false; + } + return eglMakeCurrent(d->egl, s->gl_surface, s->gl_surface, s->gl_context) == EGL_TRUE; +} + +static yasl_private_gl_proc gl_get_proc_address(struct yasl_private_display *base, const char *name) +{ + (void)base; + return name ? (yasl_private_gl_proc)eglGetProcAddress(name) : NULL; +} + +static bool gl_swap_interval(struct yasl_private_display *base, struct yasl_surface *surface, int interval) +{ + struct x11_display *d = (struct x11_display *)base; + if (!surface || surface->display != base || ((struct x11_surface *)surface)->base.presenter != YASL_PRIVATE_PRESENTER_GL) { + errno = EINVAL; + return false; + } + return d->egl_ready && eglSwapInterval(d->egl, interval) == EGL_TRUE; +} + +static bool gl_swap_buffers(struct yasl_private_display *base, struct yasl_surface *surface) +{ + struct x11_display *d = (struct x11_display *)base; + struct x11_surface *s = (struct x11_surface *)surface; + if (!surface || surface->display != base || s->base.presenter != YASL_PRIVATE_PRESENTER_GL || !d->egl_ready) { + errno = EINVAL; + return false; + } + if (!eglSwapBuffers(d->egl, s->gl_surface)) return false; + s->presented = true; + yasl_private_clock_presented(&s->clock, yasl_private_loop_now_ns(), false); + return true; +} +#endif + static bool popup_spec_valid(const struct x11_surface *parent, - const struct yasl_popup_spec *spec) + const struct yasl_private_popup_spec *spec) { if (!parent || !spec) return false; + if (spec->positioner_explicit && (spec->anchor_edges > 8 || spec->gravity > 8 || + (spec->constraints & ~63u))) return false; int parent_cx = parent->is_popup ? parent->content_offset.x : 0; int parent_cy = parent->is_popup ? parent->content_offset.y : 0; int64_t x = (int64_t)spec->anchor.x - parent_cx; @@ -992,10 +1400,87 @@ static bool popup_spec_valid(const struct x11_surface *parent, (int64_t)spec->content_offset.y + spec->content_size.y <= spec->size.y; } +enum { X11_ANCHOR_NONE, X11_ANCHOR_TOP, X11_ANCHOR_BOTTOM, X11_ANCHOR_LEFT, + X11_ANCHOR_RIGHT, X11_ANCHOR_TOP_LEFT, X11_ANCHOR_BOTTOM_LEFT, + X11_ANCHOR_TOP_RIGHT, X11_ANCHOR_BOTTOM_RIGHT }; +enum { X11_CONSTRAINT_SLIDE_X = 1u, X11_CONSTRAINT_SLIDE_Y = 2u, + X11_CONSTRAINT_FLIP_X = 4u, X11_CONSTRAINT_FLIP_Y = 8u, + X11_CONSTRAINT_RESIZE_X = 16u, X11_CONSTRAINT_RESIZE_Y = 32u }; + +static void popup_anchor_point(const struct yasl_private_popup_spec *spec, int *x, int *y) +{ + switch (spec->anchor_edges) { + case X11_ANCHOR_TOP: *x = spec->anchor.x + spec->anchor.width / 2; *y = spec->anchor.y; break; + case X11_ANCHOR_BOTTOM: *x = spec->anchor.x + spec->anchor.width / 2; *y = spec->anchor.y + spec->anchor.height; break; + case X11_ANCHOR_LEFT: *x = spec->anchor.x; *y = spec->anchor.y + spec->anchor.height / 2; break; + case X11_ANCHOR_RIGHT: *x = spec->anchor.x + spec->anchor.width; *y = spec->anchor.y + spec->anchor.height / 2; break; + case X11_ANCHOR_TOP_LEFT: *x = spec->anchor.x; *y = spec->anchor.y; break; + case X11_ANCHOR_BOTTOM_LEFT: *x = spec->anchor.x; *y = spec->anchor.y + spec->anchor.height; break; + case X11_ANCHOR_TOP_RIGHT: *x = spec->anchor.x + spec->anchor.width; *y = spec->anchor.y; break; + case X11_ANCHOR_BOTTOM_RIGHT: *x = spec->anchor.x + spec->anchor.width; *y = spec->anchor.y + spec->anchor.height; break; + default: *x = spec->anchor.x + spec->anchor.width / 2; *y = spec->anchor.y + spec->anchor.height / 2; break; + } +} + +static unsigned popup_flip_x(unsigned gravity) +{ + switch (gravity) { + case X11_ANCHOR_LEFT: return X11_ANCHOR_RIGHT; + case X11_ANCHOR_RIGHT: return X11_ANCHOR_LEFT; + case X11_ANCHOR_TOP_LEFT: return X11_ANCHOR_TOP_RIGHT; + case X11_ANCHOR_BOTTOM_LEFT: return X11_ANCHOR_BOTTOM_RIGHT; + case X11_ANCHOR_TOP_RIGHT: return X11_ANCHOR_TOP_LEFT; + case X11_ANCHOR_BOTTOM_RIGHT: return X11_ANCHOR_BOTTOM_LEFT; + default: return gravity; + } +} + +static unsigned popup_flip_y(unsigned gravity) +{ + switch (gravity) { + case X11_ANCHOR_TOP: return X11_ANCHOR_BOTTOM; + case X11_ANCHOR_BOTTOM: return X11_ANCHOR_TOP; + case X11_ANCHOR_TOP_LEFT: return X11_ANCHOR_BOTTOM_LEFT; + case X11_ANCHOR_TOP_RIGHT: return X11_ANCHOR_BOTTOM_RIGHT; + case X11_ANCHOR_BOTTOM_LEFT: return X11_ANCHOR_TOP_LEFT; + case X11_ANCHOR_BOTTOM_RIGHT: return X11_ANCHOR_TOP_RIGHT; + default: return gravity; + } +} + +static void popup_position_at(const struct yasl_private_popup_spec *spec, int ax, int ay, + unsigned gravity, struct yasl_private_vector2i size, int *x, int *y) +{ + int content_w = spec->content_size.x ? spec->content_size.x : size.x - spec->content_offset.x; + int content_h = spec->content_size.y ? spec->content_size.y : size.y - spec->content_offset.y; + switch (gravity) { + case X11_ANCHOR_LEFT: case X11_ANCHOR_TOP_LEFT: case X11_ANCHOR_BOTTOM_LEFT: + *x = ax - content_w - spec->content_offset.x; break; + case X11_ANCHOR_RIGHT: case X11_ANCHOR_TOP_RIGHT: case X11_ANCHOR_BOTTOM_RIGHT: + *x = ax - spec->content_offset.x; break; + default: *x = ax - content_w / 2 - spec->content_offset.x; break; + } + switch (gravity) { + case X11_ANCHOR_TOP: case X11_ANCHOR_TOP_LEFT: case X11_ANCHOR_TOP_RIGHT: + *y = ay - content_h - spec->content_offset.y; break; + case X11_ANCHOR_BOTTOM: case X11_ANCHOR_BOTTOM_LEFT: case X11_ANCHOR_BOTTOM_RIGHT: + *y = ay - spec->content_offset.y; break; + default: *y = ay - content_h / 2 - spec->content_offset.y; break; + } +} + +static int popup_overflow(int pos, int extent, int low, int high) +{ + int64_t end = (int64_t)pos + extent; + int64_t overflow = (pos < low ? (int64_t)low - pos : 0) + (end > high ? end - high : 0); + return overflow > INT_MAX ? INT_MAX : (int)overflow; +} + static void popup_calculate_position(struct x11_display *d, struct x11_surface *parent, - const struct yasl_popup_spec *spec, - int *out_x, int *out_y) + const struct yasl_private_popup_spec *spec, + int *out_x, int *out_y, + struct yasl_private_vector2i *out_size) { int parent_rx = 0, parent_ry = 0; if (d->native) { @@ -1007,16 +1492,23 @@ static void popup_calculate_position(struct x11_display *d, int parent_cx = parent->is_popup ? parent->content_offset.x : 0; int parent_cy = parent->is_popup ? parent->content_offset.y : 0; - int anchor_x = parent_rx + (spec->anchor.x - parent_cx); - int anchor_y = parent_ry + (spec->anchor.y - parent_cy); - - int root_x, root_y; - if (spec->beside) { - root_x = anchor_x + spec->anchor.width - spec->content_offset.x; - root_y = anchor_y - spec->content_offset.y; + struct yasl_private_vector2i size = spec->size; + int anchor_x, anchor_y, root_x, root_y; + if (spec->positioner_explicit) { + popup_anchor_point(spec, &anchor_x, &anchor_y); + anchor_x += parent_rx - parent_cx; + anchor_y += parent_ry - parent_cy; + popup_position_at(spec, anchor_x, anchor_y, spec->gravity, size, &root_x, &root_y); } else { - root_x = anchor_x - spec->content_offset.x; - root_y = anchor_y + spec->anchor.height - spec->content_offset.y; + anchor_x = parent_rx + (spec->anchor.x - parent_cx); + anchor_y = parent_ry + (spec->anchor.y - parent_cy); + if (spec->beside) { + root_x = anchor_x + spec->anchor.width - spec->content_offset.x; + root_y = anchor_y - spec->content_offset.y; + } else { + root_x = anchor_x - spec->content_offset.x; + root_y = anchor_y + spec->anchor.height - spec->content_offset.y; + } } int mon_x = 0, mon_y = 0; @@ -1043,29 +1535,69 @@ static void popup_calculate_position(struct x11_display *d, } #endif - if (spec->beside) { - if (root_x + spec->size.x > mon_x + mon_w) { - int flipped_x = anchor_x - spec->size.x + spec->content_offset.x; - if (flipped_x >= mon_x) root_x = flipped_x; + if (spec->positioner_explicit) { + if (spec->constraints & X11_CONSTRAINT_FLIP_X) { + int flip_x, flip_y; + /* Reflect the selected anchor edge across the anchor rectangle center. */ + int anchor_origin_x = parent_rx - parent_cx + spec->anchor.x; + int flipped_anchor_x = 2 * anchor_origin_x + spec->anchor.width - anchor_x; + popup_position_at(spec, flipped_anchor_x, anchor_y, popup_flip_x(spec->gravity), size, &flip_x, &flip_y); + if (popup_overflow(flip_x, size.x, mon_x, mon_x + mon_w) < + popup_overflow(root_x, size.x, mon_x, mon_x + mon_w)) root_x = flip_x; + } + if (spec->constraints & X11_CONSTRAINT_FLIP_Y) { + int flip_x, flip_y; + int anchor_origin_y = parent_ry - parent_cy + spec->anchor.y; + int flipped_anchor_y = 2 * anchor_origin_y + spec->anchor.height - anchor_y; + popup_position_at(spec, anchor_x, flipped_anchor_y, popup_flip_y(spec->gravity), size, &flip_x, &flip_y); + if (popup_overflow(flip_y, size.y, mon_y, mon_y + mon_h) < + popup_overflow(root_y, size.y, mon_y, mon_y + mon_h)) root_y = flip_y; + } + if (spec->constraints & X11_CONSTRAINT_SLIDE_X) { + if (root_x < mon_x) root_x = mon_x; + if (root_x + size.x > mon_x + mon_w) root_x = mon_x + mon_w - size.x; + } + if (spec->constraints & X11_CONSTRAINT_SLIDE_Y) { + if (root_y < mon_y) root_y = mon_y; + if (root_y + size.y > mon_y + mon_h) root_y = mon_y + mon_h - size.y; + } + if ((spec->constraints & X11_CONSTRAINT_RESIZE_X) && + popup_overflow(root_x, size.x, mon_x, mon_x + mon_w)) { + if (root_x >= mon_x + mon_w) root_x = mon_x + mon_w - 1; + if (root_x < mon_x) { size.x -= mon_x - root_x; root_x = mon_x; } + if (root_x + size.x > mon_x + mon_w) size.x = mon_x + mon_w - root_x; + if (size.x < 1) size.x = 1; + } + if ((spec->constraints & X11_CONSTRAINT_RESIZE_Y) && + popup_overflow(root_y, size.y, mon_y, mon_y + mon_h)) { + if (root_y >= mon_y + mon_h) root_y = mon_y + mon_h - 1; + if (root_y < mon_y) { size.y -= mon_y - root_y; root_y = mon_y; } + if (root_y + size.y > mon_y + mon_h) size.y = mon_y + mon_h - root_y; + if (size.y < 1) size.y = 1; } } else { - if (root_y + spec->size.y > mon_y + mon_h) { - int flipped_y = anchor_y - spec->size.y + spec->content_offset.y; + if (spec->beside) { + if (root_x + size.x > mon_x + mon_w) { + int flipped_x = anchor_x - size.x + spec->content_offset.x; + if (flipped_x >= mon_x) root_x = flipped_x; + } + } else if (root_y + size.y > mon_y + mon_h) { + int flipped_y = anchor_y - size.y + spec->content_offset.y; if (flipped_y >= mon_y) root_y = flipped_y; } + if (root_x + size.x > mon_x + mon_w) root_x = mon_x + mon_w - size.x; + if (root_x < mon_x) root_x = mon_x; + if (root_y + size.y > mon_y + mon_h) root_y = mon_y + mon_h - size.y; + if (root_y < mon_y) root_y = mon_y; } - if (root_x + spec->size.x > mon_x + mon_w) root_x = mon_x + mon_w - spec->size.x; - if (root_x < mon_x) root_x = mon_x; - if (root_y + spec->size.y > mon_y + mon_h) root_y = mon_y + mon_h - spec->size.y; - if (root_y < mon_y) root_y = mon_y; - *out_x = root_x; *out_y = root_y; + if (out_size) *out_size = size; } static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_base, - const struct yasl_popup_spec *spec) + const struct yasl_private_popup_spec *spec) { struct x11_surface *parent = (struct x11_surface *)parent_base; if (!parent || parent->destroyed || !popup_spec_valid(parent, spec)) @@ -1074,14 +1606,17 @@ static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_bas struct x11_surface *s = calloc(1, sizeof(*s)); if (!s) return NULL; s->base.display = parent_base->display; + s->base.creation_flags = parent_base->creation_flags; + s->base.presenter = (parent_base->creation_flags & YASL_PRIVATE_SURFACE_CPU) ? + YASL_PRIVATE_PRESENTER_CPU : YASL_PRIVATE_PRESENTER_NONE; s->parent = parent; s->is_popup = true; s->content_offset = spec->content_offset; s->clock.refresh_ns = parent->clock.refresh_ns; - if (!resize(s, spec->size)) { free(s); return NULL; } - int root_x = 0, root_y = 0; - popup_calculate_position(d, parent, spec, &root_x, &root_y); + struct yasl_private_vector2i actual_size; + popup_calculate_position(d, parent, spec, &root_x, &root_y, &actual_size); + if (!resize(s, actual_size)) { free(s); return NULL; } if (d->native) { XSetWindowAttributes attrs = { @@ -1089,7 +1624,7 @@ static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_bas .save_under = True, }; s->window = XCreateWindow(d->native, RootWindow(d->native, d->screen), - root_x, root_y, (unsigned)spec->size.x, (unsigned)spec->size.y, + root_x, root_y, (unsigned)actual_size.x, (unsigned)actual_size.y, 0, CopyFromParent, InputOutput, CopyFromParent, CWOverrideRedirect | CWSaveUnder, &attrs); if (!s->window) { @@ -1098,7 +1633,7 @@ static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_bas } s->base.native_handle = (void *)(uintptr_t)s->window; s->base.size = s->size; - s->base.scale_120 = YASL_SCALE_UNIT; + s->base.scale_120 = YASL_PRIVATE_SCALE_UNIT; s->gc = XCreateGC(d->native, s->window, 0, NULL); XSelectInput(d->native, s->window, ExposureMask | StructureNotifyMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | @@ -1127,25 +1662,27 @@ static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_bas } static bool surface_popup_reposition(struct yasl_surface *popup_base, - const struct yasl_popup_spec *spec) + const struct yasl_private_popup_spec *spec) { struct x11_surface *s = (struct x11_surface *)popup_base; if (!s || s->destroyed || !s->is_popup || !s->parent || s->parent->destroyed || !popup_spec_valid(s->parent, spec)) return false; struct x11_display *d = (struct x11_display *)popup_base->display; - if (spec->size.x != s->size.x || spec->size.y != s->size.y) { - if (!resize(s, spec->size)) - return false; - } s->content_offset = spec->content_offset; int root_x = 0, root_y = 0; - popup_calculate_position(d, s->parent, spec, &root_x, &root_y); + struct yasl_private_vector2i actual_size; + popup_calculate_position(d, s->parent, spec, &root_x, &root_y, &actual_size); + bool changed_size = actual_size.x != s->size.x || actual_size.y != s->size.y; + if (changed_size && !resize(s, actual_size)) return false; if (d->native && s->window) { XMoveResizeWindow(d->native, s->window, root_x, root_y, - (unsigned)spec->size.x, (unsigned)spec->size.y); + (unsigned)actual_size.x, (unsigned)actual_size.y); XFlush(d->native); } + if (changed_size) + yasl_private_surface_notify_configure(&s->base, + (struct yasl_private_geometry){actual_size, YASL_PRIVATE_SCALE_UNIT}); schedule_redraw(&s->base); return true; } @@ -1157,22 +1694,22 @@ static void update_state(struct x11_surface *s) int format; unsigned long count, left; unsigned char *data = NULL; - uint32_t state = s->base.state & ~(YASL_STATE_FULLSCREEN | YASL_STATE_MAXIMIZED); + uint32_t state = s->base.state & ~(YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_MAXIMIZED); if (XGetWindowProperty(d->native, s->window, d->net_state, 0, 64, False, XA_ATOM, &actual, &format, &count, &left, &data) == Success && format == 32) { Atom *atoms = (Atom *)data; bool h = false, v = false; for (unsigned long i = 0; i < count; i++) { - if (atoms[i] == d->fullscreen) state |= YASL_STATE_FULLSCREEN; + if (atoms[i] == d->fullscreen) state |= YASL_PRIVATE_STATE_FULLSCREEN; if (atoms[i] == d->maximized_h) h = true; if (atoms[i] == d->maximized_v) v = true; } - if (h && v) state |= YASL_STATE_MAXIMIZED; + if (h && v) state |= YASL_PRIVATE_STATE_MAXIMIZED; } if (data) XFree(data); if (state == s->base.state) return; s->base.state = state; - yasl_surface_notify_state(&s->base, state); + yasl_private_surface_notify_state(&s->base, state); } static void selection_request(struct x11_display *d, XSelectionRequestEvent *request) @@ -1184,10 +1721,10 @@ static void selection_request(struct x11_display *d, XSelectionRequestEvent *req .target = request->target, .time = request->time, }; Atom property = request->property ? request->property : request->target; - int slot = request->selection == XA_PRIMARY ? YASL_PRIMARY : YASL_CLIPBOARD; + int slot = request->selection == XA_PRIMARY ? YASL_PRIVATE_PRIMARY : YASL_PRIVATE_CLIPBOARD; const char *text = d->selection[slot]; size_t latin1_len = 0; - char *latin1 = text ? yasl_utf8_to_latin1(text, &latin1_len) : NULL; + char *latin1 = text ? yasl_private_utf8_to_latin1(text, &latin1_len) : NULL; if (text && request->target == d->targets) { Atom targets[] = {d->targets, d->utf8, XA_STRING}; XChangeProperty(d->native, request->requestor, property, XA_ATOM, 32, @@ -1267,7 +1804,7 @@ static void xdnd_selection_notify(struct x11_display *d, XSelectionEvent *event) size_t out_len = 0; char *parsed = parse_file_uris((const char *)data, count, &out_len); if (parsed && out_len > 0) { - push(s, (struct yasl_event){.type = YASL_EVENT_DROP}, parsed, out_len); + push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_DROP}, parsed, out_len); free(parsed); success = true; } @@ -1304,20 +1841,20 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event) /* Surface destruction detached this accepted request. The reply means * the owner has finished writing the initial property; retire safely. */ *p = r->next; - if (r->timeout) yasl_loop_del_timer(d->loop, r->timeout); + if (r->timeout) yasl_private_loop_del_timer(d->loop, r->timeout); XDestroyWindow(d->native, r->window); free(r); return; } if (event->property == None) { *p = r->next; - if (r->timeout) yasl_loop_del_timer(d->loop, r->timeout); - enum yasl_transfer_status status = + if (r->timeout) yasl_private_loop_del_timer(d->loop, r->timeout); + enum yasl_private_transfer_status status = XGetSelectionOwner(d->native, r->selection) == None - ? YASL_TRANSFER_UNAVAILABLE : YASL_TRANSFER_FAILED; + ? YASL_PRIVATE_TRANSFER_UNAVAILABLE : YASL_PRIVATE_TRANSFER_FAILED; XDestroyWindow(d->native, r->window); if (s && !s->destroyed) - yasl_surface_complete_paste(&s->base, r->id, status, NULL, 0); + yasl_private_surface_complete_paste(&s->base, r->id, status, NULL, 0); free(r); return; } @@ -1333,14 +1870,14 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event) &probe_count, &probe_left, &probe_data); if (probe_data) XFree(probe_data); if (probe_result == Success && probe_type == d->incr) { - yasl_surface_complete_paste(&s->base, r->id, - YASL_TRANSFER_FAILED, NULL, 0); + yasl_private_surface_complete_paste(&s->base, r->id, + YASL_PRIVATE_TRANSFER_FAILED, NULL, 0); r->target = NULL; r->id = 0; return; } *p = r->next; - if (r->timeout) yasl_loop_del_timer(d->loop, r->timeout); + if (r->timeout) yasl_private_loop_del_timer(d->loop, r->timeout); Atom actual = None; int format = 0; unsigned long count = 0, left = 0; @@ -1389,17 +1926,17 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event) } if (complete && actual == XA_STRING) { size_t utf8_len; - char *utf8 = yasl_utf8_from_latin1(buf, total, &utf8_len); + char *utf8 = yasl_private_utf8_from_latin1(buf, total, &utf8_len); free(buf); buf = utf8; if (buf) total = utf8_len; } - enum yasl_transfer_status status = YASL_TRANSFER_FAILED; - if (complete && buf && yasl_utf8_validate(buf, total)) - status = YASL_TRANSFER_OK; - yasl_surface_complete_paste(&s->base, r->id, status, - status == YASL_TRANSFER_OK ? buf : NULL, - status == YASL_TRANSFER_OK ? total : 0); + enum yasl_private_transfer_status status = YASL_PRIVATE_TRANSFER_FAILED; + if (complete && buf && yasl_private_utf8_validate(buf, total)) + status = YASL_PRIVATE_TRANSFER_OK; + yasl_private_surface_complete_paste(&s->base, r->id, status, + status == YASL_PRIVATE_TRANSFER_OK ? buf : NULL, + status == YASL_PRIVATE_TRANSFER_OK ? total : 0); free(buf); XDestroyWindow(d->native, r->window); free(r); @@ -1413,7 +1950,7 @@ static void refresh_keyboard(struct x11_display *d) if (!keyboard) return; if (keyboard->names && keyboard->names->keys) { for (unsigned code = keyboard->min_key_code; code <= keyboard->max_key_code; code++) - d->keys[code] = yasl_key_from_xkb_name(keyboard->names->keys[code].name); + d->keys[code] = yasl_private_key_from_xkb_name(keyboard->names->keys[code].name); } XkbFreeKeyboard(keyboard, XkbAllComponentsMask, True); } @@ -1460,18 +1997,18 @@ static void key_event(struct x11_surface *s, XKeyEvent *key, bool repeat) } } uint32_t cp = xkb_keysym_to_utf32((xkb_keysym_t)base); - push(s, (struct yasl_event){ - .type = YASL_EVENT_KEY, + push(s, (struct yasl_private_event){ + .type = YASL_PRIVATE_EVENT_KEY, .key = {.key = key->keycode < 256 ? d->keys[key->keycode] : YASL_KEY_UNKNOWN, - .codepoint = yasl_key_label(cp), .native_code = key->keycode, - .shifted = yasl_key_label(xkb_keysym_to_utf32((xkb_keysym_t)shifted)), + .codepoint = yasl_private_key_label(cp), .native_code = key->keycode, + .shifted = yasl_private_key_label(xkb_keysym_to_utf32((xkb_keysym_t)shifted)), .mods = d->mods, .action = key->type == KeyRelease ? YASL_KEY_RELEASE : repeat ? YASL_KEY_REPEAT : YASL_KEY_PRESS}, }, NULL, 0); if (len > 0 && text && (unsigned char)text[0] >= 0x20 && text[0] != 0x7f && - !(d->mods & (YASL_MOD_CTRL | YASL_MOD_SUPER | YASL_MOD_ALT))) - push(s, (struct yasl_event){.type = YASL_EVENT_TEXT}, text, (size_t)len); + !(d->mods & (YASL_PRIVATE_MOD_CTRL | YASL_PRIVATE_MOD_SUPER | YASL_PRIVATE_MOD_ALT))) + push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_TEXT}, text, (size_t)len); if (text != local) free(text); } @@ -1480,7 +2017,7 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s, { if (ev->message_type == d->protocols && (Atom)ev->data.l[0] == d->delete_window) { - yasl_surface_notify_close(&s->base); + yasl_private_surface_notify_close(&s->base); } else if (ev->message_type == d->xdnd_enter) { Window source = (Window)ev->data.l[0]; int version = (int)((unsigned long)ev->data.l[1] >> 24); @@ -1521,19 +2058,19 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s, XTranslateCoordinates(d->native, RootWindow(d->native, d->screen), target, rx, ry, &wx, &wy, &child); } - struct yasl_vector2i pos = {wx, wy}; + struct yasl_private_vector2i pos = {wx, wy}; if (d->xdnd.surface && d->xdnd.surface != s) { struct x11_surface *old = d->xdnd.surface; - struct yasl_vector2i old_pos = d->xdnd.pos; + struct yasl_private_vector2i old_pos = d->xdnd.pos; d->xdnd.surface = NULL; if (d->xdnd.accepts && !old->destroyed) - yasl_surface_notify_drop(&old->base, YASL_DROP_LEAVE, old_pos); + yasl_private_surface_notify_drop(&old->base, YASL_PRIVATE_DROP_LEAVE, old_pos); } d->xdnd.surface = s; d->xdnd.pos = pos; d->xdnd.time = t; if (d->xdnd.accepts && !s->destroyed) - yasl_surface_notify_drop(&s->base, YASL_DROP_OVER, pos); + yasl_private_surface_notify_drop(&s->base, YASL_PRIVATE_DROP_OVER, pos); if (d->native) { XEvent reply = {0}; @@ -1556,7 +2093,7 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s, } else if (ev->message_type == d->xdnd_leave) { if (d->xdnd.surface == s) { if (d->xdnd.accepts && !s->destroyed) - yasl_surface_notify_drop(&s->base, YASL_DROP_LEAVE, d->xdnd.pos); + yasl_private_surface_notify_drop(&s->base, YASL_PRIVATE_DROP_LEAVE, d->xdnd.pos); } d->xdnd = (typeof(d->xdnd)){0}; } else if (ev->message_type == d->xdnd_drop) { @@ -1566,7 +2103,7 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s, bool was_accepting = d->xdnd.accepts && d->xdnd.has_uri_list && (d->xdnd.source == source); if (d->xdnd.surface == s) { if (was_accepting && !s->destroyed) - yasl_surface_notify_drop(&s->base, YASL_DROP_LEAVE, d->xdnd.pos); + yasl_private_surface_notify_drop(&s->base, YASL_PRIVATE_DROP_LEAVE, d->xdnd.pos); } if (was_accepting && !s->destroyed) { d->pending_drop.active = true; @@ -1593,7 +2130,7 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s, } } -static void dispatch(struct yasl_display *base) +static void dispatch(struct yasl_private_display *base) { struct x11_display *d = (struct x11_display *)base; d->depth++; @@ -1636,11 +2173,11 @@ static void dispatch(struct yasl_display *base) switch (event.type) { case Expose: s->want_draw = true; break; case ConfigureNotify: { - struct yasl_vector2i size = {event.xconfigure.width, event.xconfigure.height}; + struct yasl_private_vector2i size = {event.xconfigure.width, event.xconfigure.height}; if (size.x == s->size.x && size.y == s->size.y) break; if (resize(s, size)) - yasl_surface_notify_configure(&s->base, - (struct yasl_geometry){size, YASL_SCALE_UNIT}); + yasl_private_surface_notify_configure(&s->base, + (struct yasl_private_geometry){size, YASL_PRIVATE_SCALE_UNIT}); break; } case ClientMessage: @@ -1648,12 +2185,16 @@ static void dispatch(struct yasl_display *base) break; case FocusIn: case FocusOut: { bool focused = event.type == FocusIn; - if (s->ic) { if (focused) XSetICFocus(s->ic); else XUnsetICFocus(s->ic); } - if (focused) s->base.state |= YASL_STATE_ACTIVATED; - else s->base.state &= ~YASL_STATE_ACTIVATED; - push(s, (struct yasl_event){ - .type = YASL_EVENT_FOCUS, .focus.focused = focused}, NULL, 0); - yasl_surface_notify_state(&s->base, s->base.state); + s->focused = focused; + if (s->ic) { + if (focused && s->base.text_input_active) XSetICFocus(s->ic); + else XUnsetICFocus(s->ic); + } + if (focused) s->base.state |= YASL_PRIVATE_STATE_ACTIVATED; + else s->base.state &= ~YASL_PRIVATE_STATE_ACTIVATED; + push(s, (struct yasl_private_event){ + .type = YASL_PRIVATE_EVENT_FOCUS, .focus.focused = focused}, NULL, 0); + yasl_private_surface_notify_state(&s->base, s->base.state); break; } case PropertyNotify: @@ -1674,13 +2215,13 @@ static void dispatch(struct yasl_display *base) break; } case MotionNotify: case LeaveNotify: case EnterNotify: case ButtonPress: case ButtonRelease: { - struct yasl_event ev = { - .pointer = {.device = YASL_PTR_DEV_MOUSE}, + struct yasl_private_event ev = { + .pointer = {.device = YASL_PRIVATE_PTR_DEV_MOUSE}, }; unsigned state; if (event.type == MotionNotify) { - ev.pointer.pos = (struct yasl_vector2i){event.xmotion.x, event.xmotion.y}; - ev.type = YASL_EVENT_POINTER_MOTION; + ev.pointer.pos = (struct yasl_private_vector2i){event.xmotion.x, event.xmotion.y}; + ev.type = YASL_PRIVATE_EVENT_POINTER_MOTION; state = event.xmotion.state; if (d->drag.active) { if (d->drag.carried) @@ -1689,38 +2230,41 @@ static void dispatch(struct yasl_display *base) XMoveWindow(d->native, d->drag.icon_window, event.xmotion.x_root - d->drag.hotspot.x, event.xmotion.y_root - d->drag.hotspot.y); - struct yasl_vector2i hpos = ev.pointer.pos; + struct yasl_private_vector2i hpos = ev.pointer.pos; struct x11_surface *hsurf = d->native ? surface_at_root_pos(d, event.xmotion.x_root, event.xmotion.y_root, &hpos) : s; drag_set_hover(d, hsurf, hpos); } } else if (event.type == EnterNotify) { - ev.pointer.pos = (struct yasl_vector2i){event.xcrossing.x, event.xcrossing.y}; - ev.type = YASL_EVENT_POINTER_MOTION; + ev.pointer.pos = (struct yasl_private_vector2i){event.xcrossing.x, event.xcrossing.y}; + ev.type = YASL_PRIVATE_EVENT_POINTER_MOTION; state = event.xcrossing.state; if (d->drag.active) { - struct yasl_vector2i hpos = ev.pointer.pos; + struct yasl_private_vector2i hpos = ev.pointer.pos; struct x11_surface *hsurf = d->native ? surface_at_root_pos(d, event.xcrossing.x_root, event.xcrossing.y_root, &hpos) : s; drag_set_hover(d, hsurf, hpos); } } else if (event.type == LeaveNotify) { - ev.pointer.pos = (struct yasl_vector2i){event.xcrossing.x, event.xcrossing.y}; - ev.type = YASL_EVENT_POINTER_LEAVE; + ev.pointer.pos = (struct yasl_private_vector2i){event.xcrossing.x, event.xcrossing.y}; + ev.type = YASL_PRIVATE_EVENT_POINTER_LEAVE; state = event.xcrossing.state; if (d->drag.active) drag_leave_hover(d, s, ev.pointer.pos); } else { - ev.pointer.pos = (struct yasl_vector2i){event.xbutton.x, event.xbutton.y}; + ev.pointer.pos = (struct yasl_private_vector2i){event.xbutton.x, event.xbutton.y}; state = event.xbutton.state; unsigned button = event.xbutton.button; if (button >= 4 && button <= 7) { if (event.type == ButtonRelease) break; - ev.type = YASL_EVENT_POINTER_SCROLL; + ev.type = YASL_PRIVATE_EVENT_POINTER_SCROLL; if (button <= 5) ev.pointer.scroll.y = button == 4 ? -1 : 1; else ev.pointer.scroll.x = button == 6 ? -1 : 1; + ev.pointer.scroll_valid = 1u; + ev.pointer.scroll_source = 1u; + ev.pointer.scroll_phase = 2u; } else { - ev.type = event.type == ButtonPress ? YASL_EVENT_POINTER_DOWN : YASL_EVENT_POINTER_UP; + ev.type = event.type == ButtonPress ? YASL_PRIVATE_EVENT_POINTER_DOWN : YASL_PRIVATE_EVENT_POINTER_UP; ev.pointer.button = pointer_button(button); if (event.type == ButtonRelease && d->drag.active) drag_drop_hover(d); @@ -1735,13 +2279,13 @@ static void dispatch(struct yasl_display *base) leave(d); } -static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb, - const struct yasl_damage_list *damage) +static bool present(struct yasl_surface *base, const struct yasl_private_framebuffer *fb, + const struct yasl_private_damage_list *damage) { struct x11_surface *s = (struct x11_surface *)base; if (s->destroyed || !s->drawing || s->presented || !fb || !damage || fb->pixels != s->pixels || fb->size.x != s->size.x || fb->size.y != s->size.y || - damage->n < -1 || damage->n > YASL_DAMAGE_MAX) return false; + damage->n < -1 || damage->n > YASL_PRIVATE_DAMAGE_MAX) return false; struct x11_display *d = (struct x11_display *)base->display; XImage *im = s->image; if (s->shm_active) { @@ -1749,7 +2293,7 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb XShmPutImage(d->native, s->window, s->gc, im, 0, 0, 0, 0, (unsigned)s->size.x, (unsigned)s->size.y, False); else for (int i = 0; i < damage->n; i++) { - struct yasl_rect2i r = damage->rects[i]; + struct yasl_private_rect2i r = damage->rects[i]; int64_t right = (int64_t)r.x + r.width, bottom = (int64_t)r.y + r.height; int x = r.x > 0 ? r.x : 0, y = r.y > 0 ? r.y : 0; int w = (int)(right < s->size.x ? right : s->size.x) - x; @@ -1758,7 +2302,7 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb XShmPutImage(d->native, s->window, s->gc, im, x, y, x, y, (unsigned)w, (unsigned)h, False); } s->presented = true; - yasl_clock_presented(&s->clock, yasl_loop_now_ns(), false); + yasl_private_clock_presented(&s->clock, yasl_private_loop_now_ns(), false); return true; } bool direct = im->bits_per_pixel == 32 && im->red_mask == 0xff0000 && @@ -1769,7 +2313,7 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb y_start = s->size.y; y_end = 0; x_start = s->size.x; x_end = 0; for (int i = 0; i < damage->n; i++) { - struct yasl_rect2i r = damage->rects[i]; + struct yasl_private_rect2i r = damage->rects[i]; int y0 = r.y > 0 ? r.y : 0; int y1 = (r.y + r.height < s->size.y) ? r.y + r.height : s->size.y; int x0 = r.x > 0 ? r.x : 0; @@ -1798,7 +2342,7 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb XPutImage(d->native, s->window, s->gc, im, 0, 0, 0, 0, (unsigned)s->size.x, (unsigned)s->size.y); else for (int i = 0; i < damage->n; i++) { - struct yasl_rect2i r = damage->rects[i]; + struct yasl_private_rect2i r = damage->rects[i]; int64_t right = (int64_t)r.x + r.width, bottom = (int64_t)r.y + r.height; int x = r.x > 0 ? r.x : 0, y = r.y > 0 ? r.y : 0; int w = (int)(right < s->size.x ? right : s->size.x) - x; @@ -1807,27 +2351,33 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb XPutImage(d->native, s->window, s->gc, im, x, y, x, y, (unsigned)w, (unsigned)h); } s->presented = true; - yasl_clock_presented(&s->clock, yasl_loop_now_ns(), false); + yasl_private_clock_presented(&s->clock, yasl_private_loop_now_ns(), false); return true; } -static void flush(struct yasl_display *base) +static void flush(struct yasl_private_display *base) { struct x11_display *d = (struct x11_display *)base; d->depth++; for (struct x11_surface *s = d->surfaces, *next; s; s = next) { next = s->next; - if (s->destroyed || !s->want_draw || !s->cbs || !s->cbs->draw || s->drawing) continue; + if (s->destroyed || !s->want_draw || s->drawing) continue; + if (s->base.presenter != YASL_PRIVATE_PRESENTER_CPU) { + s->want_draw = false; + yasl_private_surface_notify_refresh(&s->base); + continue; + } + if (!s->cbs || !s->cbs->draw) continue; s->want_draw = false; s->drawing = true; s->presented = false; - s->damage = (struct yasl_damage_list){0}; - s->frame = (struct yasl_frame){ + s->damage = (struct yasl_private_damage_list){0}; + s->frame = (struct yasl_private_frame){ .surface = &s->base, - .target = {.generation = yasl_frame_begin(&s->base), + .target = {.generation = yasl_private_frame_begin(&s->base), .pixels = s->pixels, .size = s->size}, }; - yasl_surface_draw(&s->base, &s->frame); + yasl_private_surface_draw(&s->base, &s->frame); if (!s->destroyed) s->drawing = false; } XFlush(d->native); @@ -1837,11 +2387,11 @@ static void flush(struct yasl_display *base) static int ready(int fd, short events, void *ud) { (void)fd; - struct yasl_display *base = ud; + struct yasl_private_display *base = ud; struct x11_display *d = (struct x11_display *)base; if ((events & (POLLERR | POLLHUP)) && !d->lost) { d->lost = true; - yasl_display_notify_event(base, YASL_EVENT_DISPLAY_LOST); + yasl_private_display_notify_event(base, YASL_PRIVATE_EVENT_DISPLAY_LOST); return 0; } if ((events & POLLIN) && !d->lost) { @@ -1851,7 +2401,7 @@ static int ready(int fd, short events, void *ud) return 0; } -static int dispatch_timeout(struct yasl_display *base, int timeout_ms) +static int dispatch_timeout(struct yasl_private_display *base, int timeout_ms) { struct x11_display *d = (struct x11_display *)base; if (unlikely(!d || !d->native || d->lost)) return -1; @@ -1867,26 +2417,26 @@ static int dispatch_timeout(struct yasl_display *base, int timeout_ms) if (XEventsQueued(d->native, QueuedAlready) > 0) timeout_ms = 0; if (!XPending(d->native) && timeout_ms != 0) { - yasl_loop_step(d->loop, timeout_ms); + yasl_private_loop_step(d->loop, timeout_ms); dispatch(base); } else { - yasl_loop_step(d->loop, 0); + yasl_private_loop_step(d->loop, 0); dispatch(base); } } return 0; } -static struct yasl_display *display_open(struct yasl_loop *loop, - const struct yasl_display_spec *spec) +static struct yasl_private_display *display_open(struct yasl_private_loop *loop, + const struct yasl_private_display_spec *spec) { struct x11_display *d = calloc(1, sizeof(*d)); if (!d) return NULL; - d->base.ops = &yasl_x11_backend; - d->base.callbacks = spec && spec->cbs ? *spec->cbs : (struct yasl_display_cbs){0}; + d->base.ops = &yasl_private_x11_backend; + d->base.callbacks = spec && spec->cbs ? *spec->cbs : (struct yasl_private_display_cbs){0}; d->native = XOpenDisplay(NULL); if (!d->native) { free(d); errno = ENODEV; return NULL; } - XSetErrorHandler(yasl_x11_error_handler); + XSetErrorHandler(yasl_private_x11_error_handler); d->base.native_handle = d->native; d->screen = DefaultScreen(d->native); d->has_shm = XShmQueryExtension(d->native); @@ -1908,8 +2458,15 @@ static struct yasl_display *display_open(struct yasl_loop *loop, d->abs_pressure = XInternAtom(d->native, "Abs Pressure", False); d->abs_tilt_x = XInternAtom(d->native, "Abs Tilt X", False); d->abs_tilt_y = XInternAtom(d->native, "Abs Tilt Y", False); + d->abs_rotation = XInternAtom(d->native, "Abs Rotation", False); + d->abs_distance = XInternAtom(d->native, "Abs Distance", False); + d->abs_tangential = XInternAtom(d->native, "Abs TPressure", False); + d->abs_wheel = XInternAtom(d->native, "Abs Wheel", False); d->tool_type = XInternAtom(d->native, "Wacom Tool Type", False); d->eraser = XInternAtom(d->native, "ERASER", False); + d->brush = XInternAtom(d->native, "BRUSH", False); + d->airbrush = XInternAtom(d->native, "AIRBRUSH", False); + d->pencil = XInternAtom(d->native, "PENCIL", False); unsigned char bits[XIMaskLen(XI_LASTEVENT)] = {0}; XISetMask(bits, XI_HierarchyChanged); XISetMask(bits, XI_DeviceChanged); @@ -1917,7 +2474,7 @@ static struct yasl_display *display_open(struct yasl_loop *loop, XISelectEvents(d->native, RootWindow(d->native, d->screen), &mask, 1); } if (!loop) { - d->loop = yasl_loop_create(); + d->loop = yasl_private_loop_create(); d->owns_loop = true; } else { d->loop = loop; @@ -1926,7 +2483,7 @@ static struct yasl_display *display_open(struct yasl_loop *loop, d->app_id = strdup(spec && spec->app_id ? spec->app_id : "YASL"); d->base.app_id = d->app_id; if (!d->loop || !d->app_id) { - if (d->owns_loop) yasl_loop_destroy(d->loop); + if (d->owns_loop) yasl_private_loop_destroy(d->loop); free(d->app_id); XCloseDisplay(d->native); free(d); @@ -1941,10 +2498,11 @@ static struct yasl_display *display_open(struct yasl_loop *loop, ATOM(utf8, "UTF8_STRING"); ATOM(clipboard, "CLIPBOARD"); ATOM(targets, "TARGETS"); - ATOM(paste_property, "YASL_SELECTION"); + ATOM(paste_property, "YASL_PRIVATE_SELECTION"); ATOM(incr, "INCR"); ATOM(net_name, "_NET_WM_NAME"); ATOM(net_state, "_NET_WM_STATE"); + ATOM(motif_hints, "_MOTIF_WM_HINTS"); ATOM(fullscreen, "_NET_WM_STATE_FULLSCREEN"); ATOM(maximized_h, "_NET_WM_STATE_MAXIMIZED_HORZ"); ATOM(maximized_v, "_NET_WM_STATE_MAXIMIZED_VERT"); @@ -1961,13 +2519,13 @@ static struct yasl_display *display_open(struct yasl_loop *loop, ATOM(uri_list, "text/uri-list"); #undef ATOM if (d->loop) { - d->watch = yasl_loop_add_fd(d->loop, ConnectionNumber(d->native), POLLIN, ready, &d->base); + d->watch = yasl_private_loop_add_fd(d->loop, ConnectionNumber(d->native), POLLIN, ready, &d->base); if (d->watch < 0) { if (d->im) XCloseIM(d->im); XDestroyWindow(d->native, d->selection_window); XCloseDisplay(d->native); free(d->app_id); - if (d->owns_loop) yasl_loop_destroy(d->loop); + if (d->owns_loop) yasl_private_loop_destroy(d->loop); free(d); return NULL; } @@ -1975,21 +2533,24 @@ static struct yasl_display *display_open(struct yasl_loop *loop, return &d->base; } -static void display_close(struct yasl_display *base) +static void display_close(struct yasl_private_display *base) { struct x11_display *d = (struct x11_display *)base; assert(!d->surfaces && "destroy every surface before its display"); drag_cancel(base); cancel_pastes(d, NULL); d->pending_drop.active = false; - if (d->loop && d->watch > 0) yasl_loop_del_fd(d->loop, d->watch); + if (d->loop && d->watch > 0) yasl_private_loop_del_fd(d->loop, d->watch); if (d->owns_loop && d->loop) { - yasl_loop_destroy(d->loop); + yasl_private_loop_destroy(d->loop); d->loop = NULL; d->base.loop = NULL; } if (d->im) XCloseIM(d->im); clear_pointers(d); +#if YASL_HAS_EGL + if (d->egl_ready) eglTerminate(d->egl); +#endif for (size_t i = 0; i < sizeof(d->cursors) / sizeof(d->cursors[0]); i++) { if (d->cursors[i]) { XFreeCursor(d->native, d->cursors[i]); @@ -2004,16 +2565,16 @@ static void display_close(struct yasl_display *base) free(d); } -static uint32_t caps(const struct yasl_display *d) +static uint32_t caps(const struct yasl_private_display *d) { - return YASL_CAP_POPUPS | YASL_CAP_DRAG | YASL_CAP_DRAG_CARRY | YASL_CAP_PLACE_WINDOW | - YASL_CAP_PRIMARY_SELECTION | YASL_CAP_CSD | - (((const struct x11_display *)d)->xi2 ? YASL_CAP_TABLET : 0); + return YASL_PRIVATE_CAP_POPUPS | YASL_PRIVATE_CAP_DRAG | YASL_PRIVATE_CAP_DRAG_CARRY | YASL_PRIVATE_CAP_PLACE_WINDOW | + YASL_PRIVATE_CAP_PRIMARY_SELECTION | YASL_PRIVATE_CAP_CSD | YASL_PRIVATE_CAP_NATIVE_GRAPHICS | + (((const struct x11_display *)d)->xi2 ? YASL_PRIVATE_CAP_TABLET : 0); } -static bool copy_text(struct yasl_display *base, enum yasl_selection sel, const char *text) +static bool copy_text(struct yasl_private_display *base, enum yasl_private_selection sel, const char *text) { - if (!text || (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY)) { + if (!text || (sel != YASL_PRIVATE_CLIPBOARD && sel != YASL_PRIVATE_PRIMARY)) { errno = EINVAL; return false; } @@ -2022,39 +2583,39 @@ static bool copy_text(struct yasl_display *base, enum yasl_selection sel, const if (!value) { errno = ENOMEM; return false; } free(d->selection[sel]); d->selection[sel] = value; - XSetSelectionOwner(d->native, sel == YASL_PRIMARY ? XA_PRIMARY : d->clipboard, + XSetSelectionOwner(d->native, sel == YASL_PRIVATE_PRIMARY ? XA_PRIMARY : d->clipboard, d->selection_window, CurrentTime); XFlush(d->native); return true; } -static void copy(struct yasl_display *base, enum yasl_selection sel, const char *text) +static void copy(struct yasl_private_display *base, enum yasl_private_selection sel, const char *text) { (void)copy_text(base, sel, text); } -static bool request_paste(struct yasl_display *base, enum yasl_selection sel, +static bool request_paste(struct yasl_private_display *base, enum yasl_private_selection sel, struct yasl_surface *target, uint64_t id) { - if (!target || (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY)) { + if (!target || (sel != YASL_PRIVATE_CLIPBOARD && sel != YASL_PRIVATE_PRIMARY)) { errno = EINVAL; return false; } struct x11_display *d = (struct x11_display *)base; struct x11_surface *s = (struct x11_surface *)target; if (s->destroyed || target->display != base) { errno = EINVAL; return false; } - Atom selection = sel == YASL_PRIMARY ? XA_PRIMARY : d->clipboard; + Atom selection = sel == YASL_PRIVATE_PRIMARY ? XA_PRIMARY : d->clipboard; Window owner = XGetSelectionOwner(d->native, selection); if (owner == None) { - yasl_surface_complete_paste(target, id, YASL_TRANSFER_UNAVAILABLE, NULL, 0); + yasl_private_surface_complete_paste(target, id, YASL_PRIVATE_TRANSFER_UNAVAILABLE, NULL, 0); return true; } if (owner == d->selection_window) { const char *text = d->selection[sel]; if (text) - yasl_surface_complete_paste(target, id, YASL_TRANSFER_OK, text, strlen(text)); + yasl_private_surface_complete_paste(target, id, YASL_PRIVATE_TRANSFER_OK, text, strlen(text)); else - yasl_surface_complete_paste(target, id, YASL_TRANSFER_UNAVAILABLE, NULL, 0); + yasl_private_surface_complete_paste(target, id, YASL_PRIVATE_TRANSFER_UNAVAILABLE, NULL, 0); return true; } else { struct paste_request *r = calloc(1, sizeof(*r)); @@ -2067,7 +2628,7 @@ static bool request_paste(struct yasl_display *base, enum yasl_selection sel, r->selection = selection; r->id = id; errno = 0; - r->timeout = yasl_loop_add_timer(d->loop, PASTE_TIMEOUT_NS, + r->timeout = yasl_private_loop_add_timer(d->loop, PASTE_TIMEOUT_NS, paste_timeout, r); if (!r->timeout) { XDestroyWindow(d->native, r->window); @@ -2084,17 +2645,17 @@ static bool request_paste(struct yasl_display *base, enum yasl_selection sel, } } -static void paste(struct yasl_display *base, enum yasl_selection sel, +static void paste(struct yasl_private_display *base, enum yasl_private_selection sel, struct yasl_surface *target) { (void)request_paste(base, sel, target, 0); } -static bool input_poll(struct yasl_surface *base, struct yasl_event *event) +static bool input_poll(struct yasl_surface *base, struct yasl_private_event *event) { struct x11_surface *s = (struct x11_surface *)base; if (s->destroyed) return false; - if (base->display->events) return yasl_events_poll_surface(base, event); + if (base->display->events) return yasl_private_events_poll_surface(base, event); return input_queue_pop(&s->input, event); } @@ -2110,47 +2671,47 @@ static void schedule_redraw(struct yasl_surface *base) s->want_draw = true; struct x11_display *d = (struct x11_display *)base->display; if (d && d->loop) - yasl_loop_kick(d->loop); + yasl_private_loop_kick(d->loop); } } -static struct yasl_display *surface_display(const struct yasl_surface *s) { return s->display; } -static struct yasl_geometry geometry(const struct yasl_surface *s) +static struct yasl_private_display *surface_display(const struct yasl_surface *s) { return s->display; } +static struct yasl_private_geometry geometry(const struct yasl_surface *s) { - return (struct yasl_geometry){((const struct x11_surface *)s)->size, YASL_SCALE_UNIT}; + return (struct yasl_private_geometry){((const struct x11_surface *)s)->size, YASL_PRIVATE_SCALE_UNIT}; } -static int mods(const struct yasl_display *d) { return (int)((const struct x11_display *)d)->mods; } -static void *native_display(struct yasl_display *d) { return ((struct x11_display *)d)->native; } +static int mods(const struct yasl_private_display *d) { return (int)((const struct x11_display *)d)->mods; } +static void *native_display(struct yasl_private_display *d) { return ((struct x11_display *)d)->native; } static void *native_surface(struct yasl_surface *s) { return (void *)(uintptr_t)((struct x11_surface *)s)->window; } -static struct yasl_timing timing(const struct yasl_surface *base) +static struct yasl_private_timing timing(const struct yasl_surface *base) { const struct x11_surface *s = (const struct x11_surface *)base; - struct yasl_timing t = {.refresh_ns = s->clock.refresh_ns, + 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), .source = s->clock.source}; - yasl_clock_resize_stats(&s->clock, &t.resize); + yasl_private_clock_resize_stats(&s->clock, &t.resize); return t; } static uint64_t frame_time(struct yasl_surface *s, uint64_t now) { - return yasl_clock_frame_timestamp_ns(&((struct x11_surface *)s)->clock, now); + return yasl_private_clock_frame_timestamp_ns(&((struct x11_surface *)s)->clock, now); } -static void cursor(struct yasl_surface *base, enum yasl_cursor cursor) +static void cursor(struct yasl_surface *base, enum yasl_private_cursor cursor) { static const unsigned shapes[] = {XC_left_ptr, XC_xterm, XC_hand2, XC_crosshair, XC_fleur, XC_bottom_right_corner, XC_bottom_left_corner, XC_sb_h_double_arrow, XC_sb_v_double_arrow}; struct x11_surface *s = (struct x11_surface *)base; - if (s->destroyed || (unsigned)cursor > YASL_CURSOR_HIDDEN) return; + if (s->destroyed || (unsigned)cursor > YASL_PRIVATE_CURSOR_HIDDEN) return; struct x11_display *d = (struct x11_display *)base->display; if (!d->native) return; if (!d->cursors[cursor]) { - if (cursor == YASL_CURSOR_HIDDEN) { + if (cursor == YASL_PRIVATE_CURSOR_HIDDEN) { char bits[] = {0}; Pixmap pixmap = XCreateBitmapFromData(d->native, s->window, bits, 1, 1); XColor color = {0}; @@ -2164,7 +2725,7 @@ static void cursor(struct yasl_surface *base, enum yasl_cursor cursor) XDefineCursor(d->native, s->window, d->cursors[cursor]); } -static bool place(struct yasl_surface *base, struct yasl_vector2i at) +static bool place(struct yasl_surface *base, struct yasl_private_vector2i at) { struct x11_surface *s = (struct x11_surface *)base; struct x11_display *d = (struct x11_display *)base->display; @@ -2227,7 +2788,7 @@ static bool xsettings_int(const unsigned char *p, size_t len, const char *name, return false; } -static int double_click_ms(const struct yasl_display *base) +static int double_click_ms(const struct yasl_private_display *base) { const struct x11_display *d = (const struct x11_display *)base; if (d->native) { @@ -2255,12 +2816,12 @@ static int double_click_ms(const struct yasl_display *base) } static bool read_pixels(struct yasl_surface *base, uint32_t *out, size_t cap, - struct yasl_vector2i *size) + struct yasl_private_vector2i *size) { struct x11_surface *s = (struct x11_surface *)base; /* presented stays set from the last draw until the next one starts. */ if (s->destroyed || s->drawing || !s->presented || !s->pixels) return false; - return yasl_copy_frame(s->pixels, s->size, out, cap, size); + return yasl_private_copy_frame(s->pixels, s->size, out, cap, size); } static bool bell(struct yasl_surface *base) @@ -2275,16 +2836,16 @@ static bool bell(struct yasl_surface *base) return true; } -static void drag_end(struct x11_display *d, enum yasl_drag_end how) +static void drag_end(struct x11_display *d, enum yasl_private_drag_end how) { if (!d || !d->drag.active) return; - struct yasl_drag_listener l = d->drag.listener; + struct yasl_private_drag_listener l = d->drag.listener; void *ud = d->drag.ud; struct x11_surface *hover = d->drag.hover; - struct yasl_vector2i hover_pos = d->drag.hover_pos; + struct yasl_private_vector2i hover_pos = d->drag.hover_pos; d->drag.active = false; - d->drag.listener = (struct yasl_drag_listener){0}; + d->drag.listener = (struct yasl_private_drag_listener){0}; d->drag.hover = NULL; d->drag.origin = NULL; d->drag.carried = NULL; @@ -2299,19 +2860,19 @@ static void drag_end(struct x11_display *d, enum yasl_drag_end how) } if (hover && !hover->destroyed) { - if (how == YASL_DRAG_DROPPED) - yasl_surface_notify_drag(&hover->base, YASL_DRAG_DROP, hover_pos); + if (how == YASL_PRIVATE_DRAG_DROPPED) + yasl_private_surface_notify_drag(&hover->base, YASL_PRIVATE_DRAG_DROP, hover_pos); else - yasl_surface_notify_drag(&hover->base, YASL_DRAG_LEAVE, hover_pos); + yasl_private_surface_notify_drag(&hover->base, YASL_PRIVATE_DRAG_LEAVE, hover_pos); } if (l.end) l.end(ud, how); } -static bool drag_start(struct yasl_display *base, struct yasl_surface *origin, - const struct yasl_drag_image *image, - const struct yasl_drag_listener *l, void *ud) +static bool drag_start(struct yasl_private_display *base, struct yasl_surface *origin, + const struct yasl_private_drag_image *image, + const struct yasl_private_drag_listener *l, void *ud) { struct x11_display *d = (struct x11_display *)base; if (!d || !origin || d->drag.active || !l) @@ -2383,12 +2944,12 @@ static bool drag_start(struct yasl_display *base, struct yasl_surface *origin, return true; } -static void drag_cancel(struct yasl_display *base) +static void drag_cancel(struct yasl_private_display *base) { - drag_end((struct x11_display *)base, YASL_DRAG_CANCELLED); + drag_end((struct x11_display *)base, YASL_PRIVATE_DRAG_CANCELLED); } -static bool drag_carry(struct yasl_surface *base, struct yasl_vector2i offset) +static bool drag_carry(struct yasl_surface *base, struct yasl_private_vector2i offset) { struct x11_display *d = (struct x11_display *)base->display; if (!d || !d->drag.active) @@ -2420,7 +2981,7 @@ static bool interactive_move(struct yasl_surface *base) return true; } -static bool interactive_resize(struct yasl_surface *base, enum yasl_resize_edge edge) +static bool interactive_resize(struct yasl_surface *base, enum yasl_private_resize_edge edge) { struct x11_surface *s = (struct x11_surface *)base; struct x11_display *d = (struct x11_display *)base->display; @@ -2432,14 +2993,14 @@ static bool interactive_resize(struct yasl_surface *base, enum yasl_resize_edge int dir; switch (edge) { - case YASL_EDGE_TOP_LEFT: dir = 0; break; - case YASL_EDGE_TOP: dir = 1; break; - case YASL_EDGE_TOP_RIGHT: dir = 2; break; - case YASL_EDGE_RIGHT: dir = 3; break; - case YASL_EDGE_BOTTOM_RIGHT: dir = 4; break; - case YASL_EDGE_BOTTOM: dir = 5; break; - case YASL_EDGE_BOTTOM_LEFT: dir = 6; break; - case YASL_EDGE_LEFT: dir = 7; break; + case YASL_PRIVATE_EDGE_TOP_LEFT: dir = 0; break; + case YASL_PRIVATE_EDGE_TOP: dir = 1; break; + case YASL_PRIVATE_EDGE_TOP_RIGHT: dir = 2; break; + case YASL_PRIVATE_EDGE_RIGHT: dir = 3; break; + case YASL_PRIVATE_EDGE_BOTTOM_RIGHT: dir = 4; break; + case YASL_PRIVATE_EDGE_BOTTOM: dir = 5; break; + case YASL_PRIVATE_EDGE_BOTTOM_LEFT: dir = 6; break; + case YASL_PRIVATE_EDGE_LEFT: dir = 7; break; default: return false; } @@ -2448,7 +3009,7 @@ static bool interactive_resize(struct yasl_surface *base, enum yasl_resize_edge return true; } -static bool show_window_menu(struct yasl_surface *base, struct yasl_vector2i pos) +static bool show_window_menu(struct yasl_surface *base, struct yasl_private_vector2i pos) { struct x11_surface *s = (struct x11_surface *)base; struct x11_display *d = (struct x11_display *)base->display; @@ -2458,7 +3019,18 @@ static bool show_window_menu(struct yasl_surface *base, struct yasl_vector2i pos return true; } -static void set_ime_caret(struct yasl_surface *base, struct yasl_rect2i caret) +static bool surface_update_text_input(struct yasl_surface *base) +{ + struct x11_surface *s = (struct x11_surface *)base; + if (!s || s->destroyed || !base->display) return false; + if (s->ic) { + if (s->focused && base->text_input_active) XSetICFocus(s->ic); + else XUnsetICFocus(s->ic); + } + return true; +} + +static void set_ime_caret(struct yasl_surface *base, struct yasl_private_rect2i caret) { struct x11_surface *s = (struct x11_surface *)base; if (!s) return; @@ -2489,8 +3061,15 @@ static bool ime_preedit_cursor(const struct yasl_surface *base, int *begin, int return true; } -const struct yasl_backend_ops yasl_x11_backend = { - .type = YASL_BACKEND_X11, +const struct yasl_private_backend_ops yasl_private_x11_backend = { +#if YASL_HAS_EGL + .surface_create_gl = create_gl_surface, + .gl_make_current = gl_make_current, + .gl_get_proc_address = gl_get_proc_address, + .gl_swap_interval = gl_swap_interval, + .gl_swap_buffers = gl_swap_buffers, +#endif + .type = YASL_PRIVATE_BACKEND_X11, .display_open = display_open, .display_close = display_close, .display_caps = caps, @@ -2530,6 +3109,7 @@ const struct yasl_backend_ops yasl_x11_backend = { .surface_start_interactive_move = interactive_move, .surface_start_interactive_resize = interactive_resize, .surface_show_window_menu = show_window_menu, + .surface_update_text_input = surface_update_text_input, .surface_set_ime_caret = set_ime_caret, .surface_ime_preedit = ime_preedit, .surface_ime_preedit_cursor = ime_preedit_cursor, |
