summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/backend.c71
-rw-r--r--src/common/backend.h21
-rw-r--r--src/common/desktop.c53
-rw-r--r--src/common/events.c2
-rw-r--r--src/common/native.c106
-rw-r--r--src/common/public_internal.h6
-rw-r--r--src/common/surface.c1508
-rw-r--r--src/common/types.h6
-rw-r--r--src/common/vulkan.c120
-rw-r--r--src/platform/cocoa/keymap.h1
-rw-r--r--src/platform/freedesktop/desktop.c1
-rw-r--r--src/platform/wayland/keymap.h1
-rw-r--r--src/platform/wayland/surface.c2
-rw-r--r--src/platform/x11/keymap.h1
14 files changed, 850 insertions, 1049 deletions
diff --git a/src/common/backend.c b/src/common/backend.c
index 939ec71..ad9e218 100644
--- a/src/common/backend.c
+++ b/src/common/backend.c
@@ -133,16 +133,21 @@ int yasl_private_display_double_click_ms(const struct yasl_private_display *d)
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;
- if (!origin || origin->display != d)
- return false;
+ if (!d || !origin || origin->display != d) { errno = EINVAL; return false; }
+ if (!d->ops->display_drag_start) { errno = ENOTSUP; return false; }
if (image && (!image->pixels || image->size.x <= 0 || image->size.y <= 0 ||
image->size.x > 32767 || image->size.y > 32767 ||
- (int64_t)image->size.x * image->size.y * 4 > INT32_MAX))
+ (int64_t)image->size.x * image->size.y * 4 > INT32_MAX ||
+ image->hotspot.x < 0 || image->hotspot.y < 0 ||
+ image->hotspot.x >= image->size.x || image->hotspot.y >= image->size.y)) {
+ errno = EINVAL;
return false;
+ }
const struct yasl_private_drag_listener empty = {0};
- return d->ops->display_drag_start(d, origin, image, l ? l : &empty, ud);
+ errno = 0;
+ bool ok = d->ops->display_drag_start(d, origin, image, l ? l : &empty, ud);
+ if (!ok && !errno) errno = EIO;
+ return ok;
}
void yasl_private_display_drag_cancel(struct yasl_private_display *d)
@@ -326,13 +331,18 @@ bool yasl_private_surface_read_pixels(struct yasl_surface *s, uint32_t *out, siz
bool yasl_private_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_private_pointer_mode mode)
{
- if (!s || (mode != YASL_PRIVATE_POINTER_NORMAL && mode != YASL_PRIVATE_POINTER_RELATIVE))
+ if (!s || (mode != YASL_PRIVATE_POINTER_NORMAL && mode != YASL_PRIVATE_POINTER_RELATIVE)) {
+ errno = EINVAL;
return false;
+ }
if (mode == s->pointer_mode)
return true;
const struct yasl_private_backend_ops *ops = s->display ? s->display->ops : NULL;
- if (!ops || !ops->surface_set_pointer_mode)
- return mode == YASL_PRIVATE_POINTER_NORMAL;
+ if (!ops || !ops->surface_set_pointer_mode) {
+ if (mode == YASL_PRIVATE_POINTER_NORMAL) return true;
+ errno = ENOTSUP;
+ return false;
+ }
if (!ops->surface_set_pointer_mode(s, mode))
return false;
s->pointer_mode = mode;
@@ -416,43 +426,48 @@ void yasl_private_surface_set_ime_caret(struct yasl_surface *s, struct yasl_priv
s->display->ops->surface_set_ime_caret(s, caret);
}
+/* Backend operations that may be missing: ENOTSUP when absent, EIO when a backend
+ * fails without saying why. */
+#define OPTIONAL_OP(s, op, ...) \
+ do { \
+ if (!(s)) { errno = EINVAL; return false; } \
+ if (!(s)->display->ops->op) { errno = ENOTSUP; return false; } \
+ errno = 0; \
+ bool ok_ = (s)->display->ops->op(__VA_ARGS__); \
+ if (!ok_ && !errno) errno = EIO; \
+ return ok_; \
+ } while (0)
+
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);
+ OPTIONAL_OP(s, surface_start_interactive_move, s);
}
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_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);
+ if (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) {
+ errno = EINVAL;
+ return false;
+ }
+ OPTIONAL_OP(s, surface_start_interactive_resize, s, edge);
}
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);
+ OPTIONAL_OP(s, surface_show_window_menu, s, pos);
}
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);
+ OPTIONAL_OP(s, surface_drag_carry, s, offset);
}
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);
+ OPTIONAL_OP(s, surface_place_under_pointer, s, at);
}
void yasl_private_surface_clear_timers(struct yasl_surface *s)
diff --git a/src/common/backend.h b/src/common/backend.h
index 246a9bc..e26f9d2 100644
--- a/src/common/backend.h
+++ b/src/common/backend.h
@@ -8,6 +8,7 @@
#include "common/gl_options.h"
#include "common/timing.h"
+#include <stdatomic.h>
#include <string.h>
struct yasl_private_backend_ops;
@@ -16,7 +17,12 @@ struct yasl_private_event_state;
struct yasl_private_display {
const struct yasl_private_backend_ops *ops;
void *native_handle;
- bool offline; /* Connection lost; native handles are unusable until it is restored. */
+ /* Connection lost; native handles are unusable until it is restored. Read by GL
+ * rendering threads, so it is atomic. */
+ atomic_bool offline;
+ /* Set by the backend while a native modal loop (macOS live resize) blocks the
+ * event loop; refresh callbacks run only then. */
+ bool modal;
struct yasl_private_loop *loop;
/* Backend's copy of yasl_private_display_spec.app_id, or NULL; for desktop services. */
const char *app_id;
@@ -29,19 +35,14 @@ struct yasl_surface {
unsigned creation_flags;
enum yasl_private_presenter presenter;
struct yasl_surface *parent;
- uint32_t *cpu_pixels;
- int cpu_width, cpu_height;
- uint64_t cpu_token;
- bool cpu_acquired;
- uint32_t *committed_pixels;
- int committed_width, committed_height;
+ /* CPU canvas returned by yasl_cpu_pixels and copied into each drawn frame. */
+ uint32_t *canvas;
+ int canvas_width, canvas_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;
+ void (*refresh)(struct yasl_surface *);
bool text_input_active;
unsigned text_purpose;
char *text_surrounding;
diff --git a/src/common/desktop.c b/src/common/desktop.c
index a9f72e3..b6dac7f 100644
--- a/src/common/desktop.c
+++ b/src/common/desktop.c
@@ -91,7 +91,6 @@ size_t yasl_private_default_locale(char *out, size_t cap)
}
#include "public_internal.h"
-#include "yasl/desktop.h"
#include <errno.h>
int yasl_open_uri(const char *uri)
{
@@ -114,48 +113,34 @@ unsigned yasl_get_appearance(void)
{
return yasl_public_owner() ? yasl_private_display_appearance(yasl_public_display()) : 0;
}
+int yasl_get_double_click_ms(void)
+{
+ return yasl_public_display() && yasl_public_owner()
+ ? yasl_private_display_double_click_ms(yasl_public_display())
+ : YASL_PRIVATE_DOUBLE_CLICK_MS_DEFAULT;
+}
int yasl_surface_bell(yasl_surface *s)
{
- if (!yasl_public_surface(s, false))
+ if (!yasl_public_connected(s))
return -errno;
if (!s->display->ops->surface_bell)
return yasl_public_error(ENOTSUP);
errno = 0;
return yasl_public_status(yasl_private_surface_bell(s) || yasl_public_fail(errno));
}
-size_t yasl_get_executable_path(char *out, size_t cap)
+size_t yasl_get_system_string(enum yasl_system_string which, char *out, size_t cap)
{
- if (!out && cap) {
- yasl_public_fail(EINVAL);
- return 0;
- }
- errno = 0;
- size_t n = yasl_private_executable_path(out, cap);
- if (!n)
- yasl_public_fail(errno);
- return n;
-}
-size_t yasl_get_resource_dir(char *out, size_t cap)
-{
- if (!out && cap) {
- yasl_public_fail(EINVAL);
- return 0;
- }
- errno = 0;
- size_t n = yasl_private_resource_dir(out, cap);
- if (!n)
- yasl_public_fail(errno);
- return n;
-}
-size_t yasl_get_default_locale(char *out, size_t cap)
-{
- if (!out && cap) {
- yasl_public_fail(EINVAL);
- return 0;
- }
+ if ((!out && cap) || (unsigned)which > YASL_LOCALE)
+ return yasl_public_fail(EINVAL), 0;
+ char buf[DESKTOP_STR_MAX];
errno = 0;
- size_t n = yasl_private_default_locale(out, cap);
+ size_t n = which == YASL_EXECUTABLE_PATH ? desktop_executable_path(buf, sizeof(buf))
+ : which == YASL_RESOURCE_DIR ? desktop_resource_dir(buf, sizeof(buf))
+ : desktop_default_locale(buf, sizeof(buf));
if (!n)
- yasl_public_fail(errno);
- return n;
+ return yasl_public_fail(errno ? errno : ENOENT), 0;
+ /* A POSIX locale name without its encoding or modifier, the same on every platform. */
+ if (which == YASL_LOCALE)
+ n = strcspn(buf, ".@");
+ return copy_out(out, cap, buf, n);
}
diff --git a/src/common/events.c b/src/common/events.c
index 9245050..b318d0c 100644
--- a/src/common/events.c
+++ b/src/common/events.c
@@ -60,11 +60,13 @@ void yasl_private_events_close(struct yasl_private_display *d)
free(state);
}
+/* Reports a lost event once, so one failed allocation does not fail every later dispatch. */
int yasl_private_events_error(struct yasl_private_display *d)
{
if (!d->events) return 0;
yasl_private_mutex_lock(&d->events->mutex);
int error = d->events->error;
+ d->events->error = 0;
yasl_private_mutex_unlock(&d->events->mutex);
return error;
}
diff --git a/src/common/native.c b/src/common/native.c
index 1b32094..36f7010 100644
--- a/src/common/native.c
+++ b/src/common/native.c
@@ -45,84 +45,54 @@ bool yasl_private_surface_get_native_frame(struct yasl_surface *s,
}
#include "public_internal.h"
-#include "yasl/metal.h"
-#if YASL_HAS_WAYLAND || YASL_HAS_X11 || YASL_HAS_COCOA
-static int native_validate(const yasl_surface *s, enum yasl_private_backend backend)
-{
- if (!yasl_public_surface((yasl_surface *)s, false))
- return -errno;
- if (s->display->ops->type != backend)
- return yasl_public_error(ENOTSUP);
- if (s->display->offline)
- return yasl_public_error(ENOTCONN);
- return 0;
-}
-#endif
-#if YASL_HAS_WAYLAND
-#include "yasl/native/wayland.h"
-int yasl_wayland_get_native(const yasl_surface *s, struct yasl_wayland_handles *out)
-{
- if (!out)
- return yasl_public_error(EINVAL);
- *out = (struct yasl_wayland_handles){0};
- int error = native_validate(s, YASL_PRIVATE_BACKEND_WAYLAND);
- if (error)
- return error;
- out->display = yasl_private_display_get_native_handle(s->display);
- out->surface = yasl_private_surface_get_native_handle((yasl_surface *)s);
- return 0;
-}
-#endif
-#if YASL_HAS_X11
-#include "yasl/native/x11.h"
-int yasl_x11_get_native(const yasl_surface *s, struct yasl_x11_handles *out)
-{
- if (!out)
- return yasl_public_error(EINVAL);
- *out = (struct yasl_x11_handles){0};
- int error = native_validate(s, YASL_PRIVATE_BACKEND_X11);
- if (error)
- return error;
- out->display = yasl_private_display_get_native_handle(s->display);
- out->window = (uintptr_t)yasl_private_surface_get_native_handle((yasl_surface *)s);
- return 0;
-}
-#endif
#if YASL_HAS_COCOA
-#include "yasl/native/cocoa.h"
#include "common/private_native_cocoa.h"
-int yasl_cocoa_get_native(const yasl_surface *s, struct yasl_cocoa_handles *out)
-{
- if (!out)
- return yasl_public_error(EINVAL);
- *out = (struct yasl_cocoa_handles){0};
- int error = native_validate(s, YASL_PRIVATE_BACKEND_COCOA);
- if (error)
- return error;
- out->application = yasl_private_cocoa_display_get_application(s->display);
- out->window = yasl_private_cocoa_surface_get_window((yasl_surface *)s);
- out->view = yasl_private_cocoa_surface_get_view((yasl_surface *)s);
- return 0;
-}
#endif
-int yasl_metal_get_layer(yasl_surface *s, CAMetalLayer **out)
+int yasl_surface_get_native(const yasl_surface *cs, struct yasl_native *out)
{
+ yasl_surface *s = (yasl_surface *)cs;
if (!out)
return yasl_public_error(EINVAL);
- *out = NULL;
- if (!yasl_public_surface(s, false))
+ *out = (struct yasl_native){0};
+ if (!yasl_public_connected(s))
return -errno;
- if (s->display->offline)
+ void *window = yasl_private_surface_get_native_handle(s);
+ switch (s->display->ops->type) {
+ case YASL_PRIVATE_BACKEND_WAYLAND:
+ out->wl_display = yasl_private_display_get_native_handle(s->display);
+ out->wl_surface = window;
+ break;
+ case YASL_PRIVATE_BACKEND_X11:
+ out->x11_display = yasl_private_display_get_native_handle(s->display);
+ out->x11_window = (unsigned long)(uintptr_t)window;
+ break;
+#if YASL_HAS_COCOA
+ case YASL_PRIVATE_BACKEND_COCOA:
+ out->ns_window = yasl_private_cocoa_surface_get_window(s);
+ out->ns_view = yasl_private_cocoa_surface_get_view(s);
+ break;
+#endif
+ default:
+ return yasl_public_error(ENOTSUP);
+ }
+ if (!window) {
+ *out = (struct yasl_native){0};
return yasl_public_error(ENOTCONN);
+ }
+ return 0;
+}
+void *yasl_metal_get_layer(yasl_surface *s)
+{
+ if (!yasl_public_connected(s))
+ return NULL;
if (s->vulkan || (s->presenter != YASL_PRIVATE_PRESENTER_NONE &&
s->presenter != YASL_PRIVATE_PRESENTER_METAL))
- return yasl_public_error(EBUSY);
- if (!s->display->ops->surface_get_metal_layer)
- return yasl_public_error(ENOTSUP);
- CAMetalLayer *layer = s->display->ops->surface_get_metal_layer(s);
+ return yasl_public_fail(EBUSY), NULL;
+ void *layer = s->display->ops->surface_get_metal_layer
+ ? s->display->ops->surface_get_metal_layer(s)
+ : NULL;
if (!layer)
- return yasl_public_error(ENOTSUP);
+ return yasl_public_fail(ENOTSUP), NULL;
s->presenter = YASL_PRIVATE_PRESENTER_METAL;
- *out = layer;
- return 0;
+ return layer;
}
diff --git a/src/common/public_internal.h b/src/common/public_internal.h
index 95ecb6d..65be842 100644
--- a/src/common/public_internal.h
+++ b/src/common/public_internal.h
@@ -3,11 +3,15 @@
#include "yasl/yasl.h"
#include "common/backend.h"
struct yasl_private_display *yasl_public_display(void);
+/* Initializes with defaults when needed; false with errno otherwise. */
+bool yasl_public_initialize(void);
bool yasl_public_owner(void);
+/* Validates a live surface on the owner thread; mutation also refuses inside a refresh callback. */
bool yasl_public_surface(yasl_surface *, bool mutation);
+/* yasl_public_surface plus a connected display, for requests sent to the server. */
+bool yasl_public_connected(yasl_surface *);
bool yasl_public_fail(int error);
int yasl_public_status(bool success);
int yasl_public_error(int 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
index de9e7b3..b9231ea 100644
--- a/src/common/surface.c
+++ b/src/common/surface.c
@@ -1,21 +1,22 @@
#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 <stdatomic.h>
#include <stdlib.h>
#include <string.h>
static struct yasl_private_display *display;
static pthread_t owner;
static pthread_mutex_t lifecycle_mutex = PTHREAD_MUTEX_INITIALIZER;
+static atomic_bool wake_requested;
static bool pumping;
static unsigned refresh_depth;
-static char *delivered_text; /* Strings of the last returned event; freed by the next poll/wait. */
+static char *delivered_text; /* Text of the last returned event; freed by the next wait. */
+
bool yasl_public_fail(int error)
{
errno = error > 0 ? error : EIO;
@@ -52,21 +53,24 @@ bool yasl_public_surface(yasl_surface *s, bool mutation)
return yasl_public_fail(EBUSY);
return true;
}
-int yasl_init(const struct yasl_options *options)
+bool yasl_public_connected(yasl_surface *s)
{
- if (display) {
- if (!yasl_public_owner())
- return -errno;
- return yasl_public_error(EALREADY);
- }
- struct yasl_options defaults = {0};
- if (!options)
- options = &defaults;
- if ((unsigned)options->backend > YASL_BACKEND_COCOA)
+ if (!yasl_public_surface(s, false))
+ return false;
+ return !atomic_load(&display->offline) || yasl_public_fail(ENOTCONN);
+}
+/* Runs a private call that reports failure through errno. */
+#define STATUS(call) (errno = 0, yasl_public_status((call) || yasl_public_fail(errno)))
+
+int yasl_init(enum yasl_backend backend, const char *app_id)
+{
+ if (display)
+ return yasl_public_owner() ? yasl_public_error(EALREADY) : -errno;
+ if ((unsigned)backend > YASL_BACKEND_COCOA)
return yasl_public_error(EINVAL);
struct yasl_private_display_spec spec = {
- .app_id = options->app_id,
- .backend = (enum yasl_private_backend)options->backend,
+ .app_id = app_id,
+ .backend = (enum yasl_private_backend)backend,
};
errno = 0;
struct yasl_private_display *d = yasl_private_display_open(NULL, &spec);
@@ -78,83 +82,214 @@ int yasl_init(const struct yasl_options *options)
pthread_mutex_unlock(&lifecycle_mutex);
return 0;
}
-static bool initialize(void)
+bool yasl_public_initialize(void)
{
- return display ? yasl_public_owner() : yasl_init(NULL) == 0;
+ return display ? yasl_public_owner() : yasl_init(YASL_BACKEND_AUTO, NULL) == 0;
}
-static bool creation_valid(int width, int height, unsigned flags)
+static void close_display(void)
{
- 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);
+ pthread_mutex_lock(&lifecycle_mutex);
+ struct yasl_private_display *d = display;
+ display = NULL;
+ pthread_mutex_unlock(&lifecycle_mutex);
+ yasl_private_display_close(d);
+}
+int yasl_shutdown(void)
+{
+ if (display && !yasl_public_owner())
+ return -errno;
if (pumping || refresh_depth)
- return yasl_public_fail(EBUSY);
+ return yasl_public_error(EBUSY);
+ if (display) {
+ for (yasl_surface *s = display->surfaces; s; s = s->event_next)
+ if (s->vulkan)
+ return yasl_public_error(EBUSY);
+ while (display->surfaces)
+ yasl_surface_destroy(display->surfaces);
+ close_display();
+ }
+ free(delivered_text);
+ delivered_text = NULL;
+ atomic_store(&wake_requested, false);
+#if YASL_HAS_VULKAN
+ extern void yasl_private_vulkan_reset(void);
+ yasl_private_vulkan_reset();
+#endif
+ return 0;
+}
+enum yasl_backend yasl_get_backend(void)
+{
+ return display && yasl_public_owner() ? (enum yasl_backend)display->ops->type
+ : YASL_BACKEND_AUTO;
+}
+unsigned yasl_get_features(void)
+{
+ if (!display || !yasl_public_owner())
+ return 0;
+ static const struct {
+ uint32_t cap;
+ unsigned feature;
+ } map[] = {
+ {YASL_PRIVATE_CAP_POPUPS, YASL_FEATURE_POPUPS},
+ {YASL_PRIVATE_CAP_DRAG, YASL_FEATURE_DRAG},
+ {YASL_PRIVATE_CAP_DRAG_CARRY, YASL_FEATURE_DRAG_CARRY},
+ {YASL_PRIVATE_CAP_PLACE_WINDOW, YASL_FEATURE_PLACE_WINDOW},
+ {YASL_PRIVATE_CAP_PRIMARY_SELECTION, YASL_FEATURE_PRIMARY_SELECTION},
+ {YASL_PRIVATE_CAP_TABLET, YASL_FEATURE_TABLET},
+ {YASL_PRIVATE_CAP_TEXT_INPUT, YASL_FEATURE_TEXT_INPUT},
+ {YASL_PRIVATE_CAP_RELATIVE_POINTER, YASL_FEATURE_LOCKED_CURSOR},
+ };
+ uint32_t caps = yasl_private_display_caps(display);
+ unsigned features = display->ops->surface_create_gl ? YASL_FEATURE_OPENGL : 0;
+#if YASL_HAS_VULKAN
+ features |= YASL_FEATURE_VULKAN;
+#endif
+ for (size_t i = 0; i < sizeof(map) / sizeof(map[0]); i++)
+ if (caps & map[i].cap)
+ features |= map[i].feature;
+ return features;
+}
+
+/* Surfaces */
+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);
+}
+static bool size_valid(int width, int height)
+{
+ return width > 0 && height > 0 && width <= 32767 && height <= 32767 &&
+ (unsigned long long)width * (unsigned)height <= INT32_MAX / 4;
+}
+/* Keeps the overlap of the old canvas and zeroes the rest. */
+static bool canvas_fit(yasl_surface *s, int w, int h)
+{
+ if (s->canvas && s->canvas_width == w && s->canvas_height == h)
+ return true;
+ if (w <= 0 || h <= 0)
+ return yasl_public_fail(EAGAIN);
+ uint32_t *pixels = calloc((size_t)w * (size_t)h, sizeof(*pixels));
+ if (!pixels)
+ return yasl_public_fail(ENOMEM);
+ int keep_w = w < s->canvas_width ? w : s->canvas_width;
+ int keep_h = h < s->canvas_height ? h : s->canvas_height;
+ for (int y = 0; s->canvas && y < keep_h; y++)
+ memcpy(pixels + (size_t)y * w, s->canvas + (size_t)y * s->canvas_width,
+ (size_t)keep_w * sizeof(*pixels));
+ free(s->canvas);
+ s->canvas = pixels;
+ s->canvas_width = w;
+ s->canvas_height = h;
return true;
}
-static void draw_committed(yasl_surface *s, struct yasl_private_frame *frame, void *ud)
+/* CPU presenter: every drawn frame shows the canvas; before the first present there is none. */
+static void draw_canvas(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)
+ if (s->presenter != YASL_PRIVATE_PRESENTER_CPU || !frame->target.pixels || !s->canvas)
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);
+ if (!canvas_fit(s, w, h))
+ return;
+ memcpy(frame->target.pixels, s->canvas, (size_t)w * (size_t)h * sizeof(uint32_t));
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)
+static void register_surface(yasl_surface *s, unsigned flags, yasl_surface *parent, void *userdata)
{
s->creation_flags = flags;
s->parent = parent;
+ s->userdata = userdata;
if (flags & YASL_PRIVATE_SURFACE_CPU)
s->presenter = YASL_PRIVATE_PRESENTER_CPU;
- struct yasl_private_window_cbs cb = {.draw = draw_committed};
+ struct yasl_private_window_cbs cb = {.draw = draw_canvas};
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_private_surface_notify_configure(s, yasl_private_surface_geometry(s));
+}
+/* Translates zero-means-default GL options into the private attribute list. */
+static bool gl_attributes(const struct yasl_gl_options *gl, int attrs[static 31])
+{
+ int major = gl->major, minor = gl->minor;
+ if (!major && !minor) {
+ major = 3;
+ minor = gl->es ? 0 : 2;
+ }
+ bool profiles = !gl->es && (major > 3 || (major == 3 && minor >= 2));
+ int profile = !profiles ? YASL_PRIVATE_GL_ANY
+ : gl->compatibility ? YASL_PRIVATE_GL_COMPATIBILITY
+ : YASL_PRIVATE_GL_CORE;
+ int depth = gl->no_depth_stencil ? 0 : 24, stencil = gl->no_depth_stencil ? 0 : 8;
+ const int list[] = {
+ YASL_PRIVATE_GL_MAJOR, major, YASL_PRIVATE_GL_MINOR, minor,
+ YASL_PRIVATE_GL_PROFILE, profile,
+ YASL_PRIVATE_GL_API, gl->es ? YASL_PRIVATE_GL_OPENGL_ES : YASL_PRIVATE_GL_OPENGL,
+ YASL_PRIVATE_GL_RED_BITS, 8, YASL_PRIVATE_GL_GREEN_BITS, 8,
+ YASL_PRIVATE_GL_BLUE_BITS, 8, YASL_PRIVATE_GL_ALPHA_BITS, 8,
+ YASL_PRIVATE_GL_DEPTH_BITS, depth, YASL_PRIVATE_GL_STENCIL_BITS, stencil,
+ YASL_PRIVATE_GL_SAMPLES, gl->samples, YASL_PRIVATE_GL_DEBUG, gl->debug,
+ YASL_PRIVATE_GL_FORWARD_COMPAT, 0, YASL_PRIVATE_GL_SRGB, gl->srgb,
+ YASL_PRIVATE_GL_END,
+ };
+ memcpy(attrs, list, sizeof(list));
+ struct yasl_private_gl_options parsed;
+ return yasl_private_gl_parse_options(attrs, &parsed) || yasl_public_fail(EINVAL);
}
-yasl_surface *yasl_surface_create(const struct yasl_surface_options *options)
+static yasl_surface *create_gl(const struct yasl_private_window_spec *spec, const int *attrs,
+ yasl_surface *share)
{
- if (!options || (unsigned)options->type > YASL_SURFACE_CPU || (options->flags & ~14u)) {
- yasl_public_fail(EINVAL);
- return NULL;
+ if (!display->ops->surface_create_gl)
+ return yasl_public_fail(ENOTSUP), NULL;
+ errno = 0;
+ yasl_surface *s = display->ops->surface_create_gl(display, spec, attrs, share);
+ if (!s)
+ return yasl_public_fail(errno), NULL;
+ if (!yasl_private_events_register(s)) {
+ int error = errno;
+ display->ops->surface_destroy(s);
+ return yasl_public_fail(error), NULL;
}
- const char *title = options->title;
- int width = options->width, height = options->height;
- unsigned flags =
- options->flags | (options->type == YASL_SURFACE_CPU ? YASL_PRIVATE_SURFACE_CPU : 0u);
- if (!creation_valid(width, height, flags))
+ s->presenter = YASL_PRIVATE_PRESENTER_GL;
+ return s;
+}
+yasl_surface *yasl_surface_create(const struct yasl_surface_options *o)
+{
+ if (!o || (unsigned)o->type > YASL_SURFACE_NATIVE || !size_valid(o->width, o->height))
+ return yasl_public_fail(EINVAL), NULL;
+ if (pumping || refresh_depth)
+ return yasl_public_fail(EBUSY), NULL;
+ unsigned flags = (o->type == YASL_SURFACE_CPU ? YASL_PRIVATE_SURFACE_CPU : 0u) |
+ (o->undecorated ? YASL_PRIVATE_SURFACE_UNDECORATED : 0u) |
+ (o->fixed_size ? YASL_PRIVATE_SURFACE_FIXED_SIZE : 0u);
+ int attrs[31];
+ yasl_surface *share = o->type == YASL_SURFACE_GL ? o->gl.share : NULL;
+ if (o->type == YASL_SURFACE_GL && !gl_attributes(&o->gl, attrs))
return NULL;
+ if (share && (!display || share->display != display ||
+ share->presenter != YASL_PRIVATE_PRESENTER_GL))
+ return yasl_public_fail(EINVAL), NULL;
bool fresh = !display;
- if (!initialize())
+ if (!yasl_public_initialize())
return NULL;
- struct yasl_private_window_spec spec = {.title = title,
- .size = {width, height},
- .flags = flags,
- .external_gpu = !(flags & YASL_PRIVATE_SURFACE_CPU)};
- yasl_surface *s = yasl_private_surface_create_toplevel(display, &spec);
+ struct yasl_private_window_spec spec = {
+ .title = o->title,
+ .size = {o->width, o->height},
+ .flags = flags,
+ .external_gpu = o->type != YASL_SURFACE_CPU,
+ };
+ yasl_surface *s = o->type == YASL_SURFACE_GL ? create_gl(&spec, attrs, share)
+ : 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;
+ if (fresh)
+ close_display();
+ return yasl_public_fail(error), NULL;
}
- yasl_public_register_surface(s, flags, NULL);
- s->userdata = options->userdata;
+ register_surface(s, flags, NULL, o->userdata);
+ s->refresh = o->refresh;
return s;
}
int yasl_surface_destroy(yasl_surface *s)
@@ -163,122 +298,39 @@ int yasl_surface_destroy(yasl_surface *s)
return 0;
if (!yasl_public_surface(s, true))
return -errno;
- for (yasl_surface *p = display->surfaces; p; p = p->event_next) {
+ for (yasl_surface *p = display->surfaces; p; p = p->event_next)
for (yasl_surface *ancestor = p; ancestor; ancestor = ancestor->parent)
if (ancestor == s && p->vulkan)
return yasl_public_error(EBUSY);
- }
for (;;) {
yasl_surface *child = NULL;
- for (yasl_surface *p = display->surfaces; p; p = p->event_next)
- if (p->parent == s) {
+ for (yasl_surface *p = display->surfaces; p && !child; p = p->event_next)
+ if (p->parent == s)
child = p;
- break;
- }
if (!child)
break;
yasl_surface_destroy(child);
}
- free(s->cpu_pixels);
- s->cpu_pixels = NULL;
- free(s->committed_pixels);
- s->committed_pixels = NULL;
+ free(s->canvas);
+ s->canvas = NULL;
free(s->text_surrounding);
s->text_surrounding = NULL;
yasl_private_surface_destroy(s);
return 0;
}
-int yasl_shutdown(void)
-{
- if (display && !yasl_public_owner())
- return -errno;
- if (pumping || refresh_depth) {
- yasl_public_fail(EBUSY);
- return -errno;
- }
- if (display) {
- for (yasl_surface *s = display->surfaces; s; s = s->event_next)
- if (s->vulkan)
- return yasl_public_error(EBUSY);
- 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;
-#if YASL_HAS_VULKAN
- extern void yasl_private_vulkan_reset(void);
- yasl_private_vulkan_reset();
-#endif
- return 0;
-}
-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_get_backend(const yasl_surface *s)
-{
- return s && yasl_public_surface((yasl_surface *)s, false)
- ? (enum yasl_backend)s->display->ops->type
- : YASL_BACKEND_AUTO;
-}
-unsigned yasl_surface_get_features(const yasl_surface *s)
+struct yasl_surface_info yasl_surface_get_info(const yasl_surface *s)
{
if (!s || !yasl_public_surface((yasl_surface *)s, false))
- 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 (s && !yasl_public_surface((yasl_surface *)s, false))
- s = NULL;
- 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)
-{
- if (s && !yasl_public_surface((yasl_surface *)s, false))
- s = NULL;
+ return (struct yasl_surface_info){.scale = 1.0};
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 s && yasl_public_surface((yasl_surface *)s, false) ? scale_of(s) : 1.0;
-}
-unsigned yasl_surface_get_state(const yasl_surface *s)
-{
- return s && yasl_public_surface((yasl_surface *)s, false) ? s->state : 0;
+ return (struct yasl_surface_info){
+ .width = logical(s->size.x, scale),
+ .height = logical(s->size.y, scale),
+ .pixel_width = s->size.x,
+ .pixel_height = s->size.y,
+ .scale = scale,
+ .state = s->state,
+ };
}
void *yasl_surface_get_userdata(const yasl_surface *s)
{
@@ -302,54 +354,69 @@ int yasl_surface_set_title(yasl_surface *s, const char *title)
s->display->ops->surface_set_title(s, title);
return 0;
}
-#define REQUEST(name, priv, args, call) \
- int name args \
- { \
- if (!yasl_public_surface(s, false)) \
- return -errno; \
- errno = 0; \
- return yasl_public_status(priv call || yasl_public_fail(errno)); \
+int yasl_surface_request(yasl_surface *s, enum yasl_request request)
+{
+ if (!yasl_public_connected(s))
+ return -errno;
+ switch (request) {
+ case YASL_REQUEST_RESTORE: {
+ uint32_t state = s->state;
+ errno = 0;
+ bool ok = !(state & YASL_PRIVATE_STATE_FULLSCREEN) || yasl_private_surface_set_fullscreen(s, false);
+ if (ok && (state & YASL_PRIVATE_STATE_MAXIMIZED || !(state & YASL_PRIVATE_STATE_FULLSCREEN)))
+ ok = yasl_private_surface_set_maximized(s, false);
+ return yasl_public_status(ok || yasl_public_fail(errno));
+ }
+ case YASL_REQUEST_FULLSCREEN:
+ return STATUS(yasl_private_surface_set_fullscreen(s, true));
+ case YASL_REQUEST_MAXIMIZE:
+ return STATUS(yasl_private_surface_set_maximized(s, true));
+ case YASL_REQUEST_MINIMIZE:
+ return STATUS(yasl_private_surface_minimize(s));
}
-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))
+ return yasl_public_error(EINVAL);
+}
+int yasl_surface_request_size(yasl_surface *s, int w, int h)
+{
+ if (!yasl_public_connected(s))
+ return -errno;
+ double scale = scale_of(s);
+ if (!size_valid(w, h) || !size_valid((int)ceil(w * scale), (int)ceil(h * scale)))
+ return yasl_public_error(EINVAL);
+ return STATUS(yasl_private_surface_set_size(s, (struct yasl_private_vector2i){w, h}));
+}
int yasl_surface_set_cursor(yasl_surface *s, enum yasl_cursor cursor)
{
if (!yasl_public_surface(s, false))
return -errno;
- if (cursor < YASL_CURSOR_ARROW || cursor > YASL_CURSOR_HIDDEN)
+ if ((unsigned)cursor > YASL_CURSOR_LOCKED)
return yasl_public_error(EINVAL);
+ bool locked = cursor == YASL_CURSOR_LOCKED;
+ if (!yasl_private_surface_set_pointer_mode(s, locked ? YASL_PRIVATE_POINTER_RELATIVE
+ : YASL_PRIVATE_POINTER_NORMAL))
+ return yasl_public_error(errno);
+ if (locked)
+ return 0;
if (!s->display->ops->surface_set_cursor)
return yasl_public_error(ENOTSUP);
yasl_private_surface_set_cursor(s, (enum yasl_private_cursor)cursor);
return 0;
}
-int yasl_surface_set_refresh_callback(yasl_surface *s, void (*fn)(yasl_surface *, void *), void *ud)
-{
- if (!yasl_public_surface(s, false))
- return -errno;
- s->refresh_callback = fn;
- s->refresh_userdata = ud;
- return 0;
-}
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) {
+ if (s->refresh && s->display->modal && pumping && !refresh_depth) {
++refresh_depth;
- s->refresh_callback(s, s->refresh_userdata);
+ s->refresh(s);
--refresh_depth;
+ return;
}
+ struct yasl_private_event ev = {.type = YASL_PRIVATE_EVENT_REFRESH, .surface = s};
+ yasl_private_events_push(s->display, s, &ev);
}
+
+/* Text input */
static char *copy_text(const char *data, size_t length)
{
if (length == SIZE_MAX || (!data && length)) {
@@ -357,20 +424,226 @@ static char *copy_text(const char *data, size_t length)
return NULL;
}
char *p = malloc(length + 1);
- if (!p)
+ if (!p) {
+ errno = ENOMEM;
return NULL;
+ }
if (length)
memcpy(p, data, length);
p[length] = 0;
return p;
}
+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);
+ double scale = scale_of(s);
+ if (fabs(x) * scale >= INT_MAX || fabs(y) * scale >= INT_MAX || w * scale >= INT_MAX ||
+ h * scale >= INT_MAX)
+ return yasl_public_fail(EOVERFLOW);
+ *r = (struct yasl_private_rect2i){
+ yasl_private_scale_to_device(x, s->scale_120), yasl_private_scale_to_device(y, s->scale_120),
+ yasl_private_scale_to_device(w, s->scale_120), yasl_private_scale_to_device(h, s->scale_120)};
+ return true;
+}
+static bool utf8_boundary(const char *text, size_t length, size_t at)
+{
+ return at >= length || ((unsigned char)text[at] & 0xc0) != 0x80;
+}
+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);
+}
+int yasl_surface_set_text_input(yasl_surface *s, const struct yasl_text_input *in)
+{
+ if (!yasl_public_surface(s, false))
+ return -errno;
+ struct yasl_private_rect2i caret = {0};
+ char *copy = NULL;
+ if (in) {
+ if ((unsigned)in->purpose > YASL_TEXT_TERMINAL || (!in->text && in->length) ||
+ in->selection_begin > in->selection_end || in->selection_end > in->length ||
+ in->length > INT32_MAX || (in->length && memchr(in->text, 0, in->length)))
+ return yasl_public_error(EINVAL);
+ if (!yasl_private_utf8_validate(in->text ? in->text : "", in->length) ||
+ !utf8_boundary(in->text, in->length, in->selection_begin) ||
+ !utf8_boundary(in->text, in->length, in->selection_end))
+ return yasl_public_error(EILSEQ);
+ if (!logical_rect(s, in->caret_x, in->caret_y, in->caret_width, in->caret_height, &caret))
+ return -errno;
+ if (!(copy = copy_text(in->text, in->length)))
+ return yasl_public_error(errno);
+ }
+ struct text_state {
+ bool active;
+ unsigned purpose;
+ char *surrounding;
+ size_t length, begin, end;
+ uint64_t revision, generation;
+ } old = {s->text_input_active, s->text_purpose, s->text_surrounding, s->text_length,
+ s->text_begin, s->text_end, s->text_revision, s->text_generation};
+ bool active = in != NULL;
+ unsigned purpose = in ? (unsigned)in->purpose : s->text_purpose;
+ if (active != old.active || purpose != old.purpose)
+ ++s->text_generation;
+ s->text_input_active = active;
+ s->text_purpose = purpose;
+ s->text_surrounding = copy;
+ s->text_length = in ? in->length : 0;
+ s->text_begin = in ? in->selection_begin : 0;
+ s->text_end = in ? in->selection_end : 0;
+ s->text_revision = in ? in->revision : old.revision;
+ if (!text_update(s)) {
+ int error = errno;
+ free(copy);
+ s->text_input_active = old.active;
+ s->text_purpose = old.purpose;
+ s->text_surrounding = old.surrounding;
+ s->text_length = old.length;
+ s->text_begin = old.begin;
+ s->text_end = old.end;
+ s->text_revision = old.revision;
+ s->text_generation = old.generation;
+ return yasl_public_error(error);
+ }
+ free(old.surrounding);
+ if (active && s->display->ops->surface_set_ime_caret)
+ yasl_private_surface_set_ime_caret(s, caret);
+ return 0;
+}
+
+/* Window management */
+int yasl_surface_begin_move_resize(yasl_surface *s, unsigned edges)
+{
+ if (!yasl_public_connected(s))
+ return -errno;
+ if ((edges & ~15u) || (edges & 3u) == 3u || (edges & 12u) == 12u)
+ return yasl_public_error(EINVAL);
+ if (!edges)
+ return STATUS(yasl_private_surface_start_interactive_move(s));
+ return STATUS(yasl_private_surface_start_interactive_resize(s, (enum yasl_private_resize_edge)edges));
+}
+int yasl_surface_show_window_menu(yasl_surface *s, double x, double y)
+{
+ struct yasl_private_rect2i r;
+ if (!yasl_public_connected(s) || !logical_rect(s, x, y, 0, 0, &r))
+ return -errno;
+ return STATUS(yasl_private_surface_show_window_menu(s, (struct yasl_private_vector2i){r.x, r.y}));
+}
+
+/* Popups */
+static bool popup_spec(yasl_surface *parent, const struct yasl_popup_options *o,
+ struct yasl_private_popup_spec *out)
+{
+ /* YASL_EDGE_* bits to xdg_positioner anchor/gravity values; -1 rejects opposite edges. */
+ static const signed char placement[16] = {0, 1, 2, -1, 3, 5, 6, -1, 4, 7, 8, -1, -1, -1, -1, -1};
+ if (!o || !size_valid(o->width, o->height) || o->anchor > 15 || o->gravity > 15 ||
+ placement[o->anchor] < 0 || placement[o->gravity] < 0 || (o->adjust & ~63u))
+ return yasl_public_fail(EINVAL);
+ double scale = scale_of(parent);
+ if (!size_valid((int)ceil(o->width * scale), (int)ceil(o->height * scale)))
+ return yasl_public_fail(EINVAL);
+ *out = (struct yasl_private_popup_spec){
+ .size = {yasl_private_scale_to_device(o->width, parent->scale_120),
+ yasl_private_scale_to_device(o->height, parent->scale_120)},
+ .anchor_edges = (unsigned)placement[o->anchor],
+ .gravity = (unsigned)placement[o->gravity],
+ .constraints = o->adjust ? o->adjust
+ : YASL_ADJUST_FLIP_X | YASL_ADJUST_FLIP_Y | YASL_ADJUST_SLIDE_X |
+ YASL_ADJUST_SLIDE_Y,
+ };
+ return logical_rect(parent, o->x, o->y, o->anchor_width, o->anchor_height, &out->anchor);
+}
+yasl_surface *yasl_popup_create(yasl_surface *parent, const struct yasl_popup_options *o)
+{
+ struct yasl_private_popup_spec spec;
+ if (!yasl_public_connected(parent) || !yasl_public_surface(parent, true) ||
+ !popup_spec(parent, o, &spec))
+ return NULL;
+ errno = 0;
+ yasl_surface *s = yasl_private_surface_create_popup(parent, &spec);
+ if (!s)
+ return yasl_public_fail(errno), NULL;
+ register_surface(s, YASL_PRIVATE_SURFACE_CPU, parent, o->userdata);
+ return s;
+}
+int yasl_popup_reposition(yasl_surface *s, const struct yasl_popup_options *o)
+{
+ struct yasl_private_popup_spec spec;
+ if (!yasl_public_connected(s))
+ return -errno;
+ if (!s->parent)
+ return yasl_public_error(EINVAL);
+ if (!popup_spec(s->parent, o, &spec))
+ return -errno;
+ return STATUS(yasl_private_surface_popup_reposition(s, &spec));
+}
+
+/* Clipboard, drag and drop */
+int yasl_clipboard_set(enum yasl_selection selection, const char *text)
+{
+ if (!yasl_public_owner())
+ return -errno;
+ if (atomic_load(&display->offline))
+ return yasl_public_error(ENOTCONN);
+ return STATUS(yasl_private_clipboard_set(display, (enum yasl_private_selection)selection, text));
+}
+int yasl_clipboard_request(yasl_surface *s, enum yasl_selection selection)
+{
+ if (!yasl_public_connected(s))
+ return -errno;
+ return STATUS(yasl_private_clipboard_request(s, (enum yasl_private_selection)selection) != 0);
+}
+int yasl_drag_start(yasl_surface *s, const struct yasl_drag_image *source)
+{
+ if (!yasl_public_connected(s))
+ return -errno;
+ struct yasl_private_drag_image image = {0};
+ if (source)
+ image = (struct yasl_private_drag_image){
+ .pixels = source->pixels,
+ .size = {source->pixel_width, source->pixel_height},
+ .hotspot = {source->hotspot_x, source->hotspot_y}};
+ return STATUS(yasl_private_display_drag_start(display, s, source ? &image : NULL, NULL, NULL));
+}
+int yasl_drag_cancel(void)
+{
+ if (!yasl_public_owner())
+ return -errno;
+ yasl_private_display_drag_cancel(display);
+ return 0;
+}
+int yasl_drag_carry(yasl_surface *s, double x, double y)
+{
+ struct yasl_private_rect2i r;
+ if (!yasl_public_connected(s) || !logical_rect(s, x, y, 0, 0, &r))
+ return -errno;
+ return STATUS(yasl_private_surface_drag_carry(s, (struct yasl_private_vector2i){r.x, r.y}));
+}
+int yasl_surface_place_under_pointer(yasl_surface *s, double x, double y)
+{
+ struct yasl_private_rect2i r;
+ if (!yasl_public_connected(s) || !logical_rect(s, x, y, 0, 0, &r))
+ return -errno;
+ return STATUS(yasl_private_surface_place_under_pointer(s, (struct yasl_private_vector2i){r.x, r.y}));
+}
+size_t yasl_drop_next_path(const char **cursor, char *out, size_t cap)
+{
+ return yasl_private_drop_next_path(cursor, out, cap);
+}
+
+/* Events */
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
+/* Folds native size, scale and state 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)
@@ -386,18 +659,31 @@ static bool window_event(yasl_surface *s, struct yasl_private_vector2i size, uin
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;
+ e->window = (struct yasl_surface_info){
+ .width = logical(size.x, scale),
+ .height = logical(size.y, scale),
+ .pixel_width = size.x,
+ .pixel_height = size.y,
+ .scale = scale,
+ .state = state,
+ };
return true;
}
static bool refresh_ready(yasl_surface *s)
{
return s->reported_size.x > 0 && s->reported_size.y > 0;
}
+static bool with_text(yasl_event *e, const char *data, size_t length)
+{
+ e->length = length;
+ e->text = deliver_text(data, length);
+ return e->text != NULL;
+}
+/* IME offsets: -1 (unknown) places the cursor at the end of the text. */
+static size_t ime_offset(int offset, size_t length)
+{
+ return offset < 0 || (size_t)offset > length ? length : (size_t)offset;
+}
static bool convert_event(const struct yasl_private_event *p, yasl_event *e)
{
*e = (yasl_event){.surface = p->surface, .time_ns = p->time_ns};
@@ -405,19 +691,19 @@ static bool convert_event(const struct yasl_private_event *p, yasl_event *e)
switch (p->type) {
case YASL_PRIVATE_EVENT_WINDOW_CLOSE:
e->type = YASL_EVENT_CLOSE;
- break;
+ return true;
case YASL_PRIVATE_EVENT_DISPLAY_QUIT:
e->type = YASL_EVENT_QUIT;
- break;
+ return true;
case YASL_PRIVATE_EVENT_DISPLAY_REOPEN:
e->type = YASL_EVENT_REOPEN;
- break;
+ return true;
case YASL_PRIVATE_EVENT_DISPLAY_LOST:
e->type = YASL_EVENT_DISPLAY_LOST;
- break;
+ return true;
case YASL_PRIVATE_EVENT_DISPLAY_RESTORED:
e->type = YASL_EVENT_DISPLAY_RESTORED;
- break;
+ return true;
case YASL_PRIVATE_EVENT_REFRESH:
if (!p->surface)
return false;
@@ -427,159 +713,134 @@ static bool convert_event(const struct yasl_private_event *p, yasl_event *e)
}
p->surface->refresh_owed = false;
e->type = YASL_EVENT_REFRESH;
- break;
+ return true;
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 true;
+ case YASL_PRIVATE_EVENT_FOCUS:
+ /* Activation comes from the window state; losing focus releases held input. */
+ if (p->focus.focused)
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);
- }
+ e->type = YASL_EVENT_INPUT_RESET;
+ return true;
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);
+ return p->surface && 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);
+ return p->surface && 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;
+ e->action = (unsigned)p->key.action;
+ e->mods = p->key.mods;
+ e->key = (struct yasl_key_event){p->key.key, p->key.codepoint, p->key.shifted,
+ p->key.native_code};
+ return true;
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;
+ return with_text(e, p->text.data, p->text.len);
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;
+ e->action = YASL_DROP_RELEASE;
+ return with_text(e, p->text.data, p->text.len);
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;
+ e->action = (unsigned)p->paste.status;
+ return with_text(e, p->paste.data, p->paste.len);
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;
+ e->ime = (struct yasl_ime_event){ime_offset(p->composition.begin, p->composition.len),
+ ime_offset(p->composition.end, p->composition.len),
+ p->composition.revision};
+ return with_text(e, p->composition.data, p->composition.len);
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;
+ e->ime = (struct yasl_ime_event){p->text_edit.begin, p->text_edit.end, p->text_edit.revision};
+ return with_text(e, p->text_edit.data, p->text_edit.len);
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->action = p->pen.action;
+ e->x = p->pen.x / scale;
+ e->y = p->pen.y / scale;
+ e->pen = (struct yasl_pen_event){
+ .device_id = p->pen.device_id,
+ .tool_id = p->pen.tool_id,
+ .pressure = p->pen.pressure,
+ .tilt_x = p->pen.tilt_x,
+ .tilt_y = p->pen.tilt_y,
+ .rotation = p->pen.rotation,
+ .distance = p->pen.distance,
+ .tangential = p->pen.tangential,
+ .wheel_degrees = p->pen.wheel_degrees,
+ .tool = (enum yasl_pen_tool)p->pen.tool,
+ .axes = p->pen.axes_valid,
+ .buttons = p->pen.buttons,
+ .button = p->pen.changed_button,
+ .in_contact = p->pen.in_contact,
+ };
+ return true;
+ case YASL_PRIVATE_EVENT_POINTER_SCROLL: {
+ unsigned valid = p->pointer.scroll_valid ? p->pointer.scroll_valid : YASL_PRIVATE_SCROLL_LINES;
+ bool lines = valid & YASL_PRIVATE_SCROLL_LINES, pixels = valid & YASL_PRIVATE_SCROLL_PIXELS;
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;
+ e->mods = p->pointer.mods;
+ e->x = p->pointer.pos.x / scale;
+ e->y = p->pointer.pos.y / scale;
+ e->scroll = (struct yasl_scroll_event){
+ .lines_x = lines ? p->pointer.scroll.x : 0,
+ .lines_y = lines ? p->pointer.scroll.y : 0,
+ .pixels_x = pixels ? p->pointer.pixel_scroll.x / scale : 0,
+ .pixels_y = pixels ? p->pointer.pixel_scroll.y / scale : 0,
+ .source = (enum yasl_scroll_source)p->pointer.scroll_source,
+ .phase = (enum yasl_scroll_phase)p->pointer.scroll_phase,
+ .momentum = p->pointer.scroll_momentum,
+ };
+ return true;
+ }
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;
+ e->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->mods = p->pointer.mods;
+ e->x = (p->pointer.precise_position ? p->pointer.precise_x : p->pointer.pos.x) / scale;
+ e->y = (p->pointer.precise_position ? p->pointer.precise_y : p->pointer.pos.y) / scale;
+ e->pointer = (struct yasl_pointer_event){p->pointer.delta.x / scale, p->pointer.delta.y / scale,
+ p->pointer.button, p->pointer.buttons};
+ return true;
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;
+ e->action = p->drag.action == YASL_PRIVATE_DRAG_OVER ? YASL_DROP_OVER
+ : p->drag.action == YASL_PRIVATE_DRAG_LEAVE ? YASL_DROP_LEAVE
+ : YASL_DROP_RELEASE;
+ e->x = p->drag.pos.x / scale;
+ e->y = p->drag.pos.y / scale;
+ return true;
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;
+ e->action = p->drop.action == YASL_PRIVATE_DROP_LEAVE ? YASL_DROP_LEAVE : YASL_DROP_OVER;
+ e->x = p->drop.pos.x / scale;
+ e->y = p->drop.pos.y / scale;
+ return true;
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()};
+ *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)) {
+ /* Input method events from an older text field are stale. */
if (p.surface && ((p.type == YASL_PRIVATE_EVENT_COMPOSITION &&
p.composition.generation != p.surface->text_generation) ||
(p.type == YASL_PRIVATE_EVENT_TEXT_EDIT &&
@@ -591,547 +852,130 @@ static int take_event(yasl_event *out)
*out = event;
return 1;
}
- if (errno) {
- yasl_public_fail(errno);
- return -errno;
- }
+ if (errno)
+ return yasl_public_error(errno);
}
return 0;
}
-int yasl_wait_event(yasl_event *out, int timeout)
+/* Internal wakeups, such as a redraw request, keep waiting; only an event, the deadline or
+ * yasl_wake end the wait. */
+static int pump(yasl_event *out, int timeout)
{
- if (!out) {
- yasl_public_fail(EINVAL);
- return -errno;
- }
- *out = (yasl_event){.type = YASL_EVENT_NONE};
- if (!yasl_public_owner())
- return -errno;
- if (pumping || refresh_depth) {
- yasl_public_fail(EBUSY);
- return -errno;
- }
- for (yasl_surface *s = display->surfaces; s; s = s->event_next)
- s->cpu_acquired = false;
- 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 = -errno;
+ uint64_t deadline = timeout > 0 ? yasl_time_ns() + (uint64_t)timeout * 1000000u : 0;
+ for (;;) {
+ int n = take_event(out);
+ if (n)
+ return n;
+ yasl_private_display_flush(display);
+ if ((n = take_event(out)))
+ return n;
+ if (atomic_exchange(&wake_requested, false))
+ return 0;
+ int wait = timeout;
+ if (timeout > 0) {
+ uint64_t now = yasl_time_ns();
+ if (now >= deadline)
+ return 0;
+ wait = (int)((deadline - now + 999999u) / 1000000u);
}
+ if (yasl_private_display_dispatch(display, wait) < 0)
+ return yasl_public_error(errno);
yasl_private_display_flush(display);
- if (n >= 0)
- n = take_event(out);
+ if ((n = take_event(out)) || timeout == 0)
+ return n;
}
- pumping = false;
- return n;
}
-static uint64_t cpu_sequence;
-int yasl_cpu_acquire(yasl_surface *s, struct yasl_cpu_buffer *out)
+int yasl_wait_event(yasl_event *out, int timeout)
{
if (!out)
return yasl_public_error(EINVAL);
- *out = (struct yasl_cpu_buffer){0};
- if (!yasl_public_surface(s, false))
- return -errno;
- if (s->presenter != YASL_PRIVATE_PRESENTER_CPU)
- return yasl_public_error(ENOTSUP);
- if (s->display->offline)
- return yasl_public_error(ENOTCONN);
- if (s->cpu_acquired)
- return yasl_public_error(EBUSY);
- int w = s->size.x, h = s->size.y;
- if (w <= 0 || h <= 0)
- return yasl_public_error(EAGAIN);
- if ((size_t)w > SIZE_MAX / sizeof(uint32_t) / (size_t)h)
- return yasl_public_error(EOVERFLOW);
- if (s->cpu_width != w || s->cpu_height != h || !s->cpu_pixels) {
- uint32_t *pixels = malloc((size_t)w * h * sizeof(*pixels));
- if (!pixels)
- return yasl_public_error(ENOMEM);
- free(s->cpu_pixels);
- s->cpu_pixels = pixels;
- s->cpu_width = w;
- s->cpu_height = h;
- }
- memset(s->cpu_pixels, 0, (size_t)w * h * sizeof(uint32_t));
- int keep_w = w < s->committed_width ? w : s->committed_width;
- int keep_h = h < s->committed_height ? h : s->committed_height;
- if (s->committed_pixels)
- for (int y = 0; y < keep_h; y++)
- memcpy(s->cpu_pixels + (size_t)y * w,
- s->committed_pixels + (size_t)y * s->committed_width,
- (size_t)keep_w * sizeof(uint32_t));
- if (++cpu_sequence == 0)
- ++cpu_sequence;
- s->cpu_token = cpu_sequence;
- s->cpu_acquired = true;
- *out =
- (struct yasl_cpu_buffer){s->cpu_pixels, w, h, (size_t)w * sizeof(uint32_t), s->cpu_token};
- return 0;
-}
-static int cpu_validate(yasl_surface *s, const struct yasl_cpu_buffer *buffer)
-{
- if (!yasl_public_surface(s, false))
- return -errno;
- if (!buffer)
- return yasl_public_error(EINVAL);
- if (s->presenter != YASL_PRIVATE_PRESENTER_CPU)
- return yasl_public_error(ENOTSUP);
- if (!s->cpu_acquired || !buffer->token || buffer->token != s->cpu_token ||
- s->cpu_width != s->size.x || s->cpu_height != s->size.y)
- return yasl_public_error(ESTALE);
- if (buffer->pixels != s->cpu_pixels || buffer->width != s->cpu_width ||
- buffer->height != s->cpu_height ||
- buffer->stride_bytes != (size_t)s->cpu_width * sizeof(uint32_t))
- return yasl_public_error(EINVAL);
- return 0;
-}
-int yasl_cpu_present(yasl_surface *s, const struct yasl_cpu_buffer *buffer)
-{
- int error = cpu_validate(s, buffer);
- if (error)
- return error;
- if (s->display->offline)
- return yasl_public_error(ENOTCONN);
- size_t bytes = (size_t)s->cpu_width * s->cpu_height * sizeof(uint32_t);
- if (s->committed_width != s->cpu_width || s->committed_height != s->cpu_height ||
- !s->committed_pixels) {
- uint32_t *pixels = malloc(bytes);
- if (!pixels)
- return yasl_public_error(ENOMEM);
- free(s->committed_pixels);
- s->committed_pixels = pixels;
- s->committed_width = s->cpu_width;
- s->committed_height = s->cpu_height;
- }
- memcpy(s->committed_pixels, s->cpu_pixels, bytes);
- s->cpu_acquired = false;
- yasl_private_surface_request_frame(s);
- return 0;
-}
-int yasl_cpu_discard(yasl_surface *s, const struct yasl_cpu_buffer *buffer)
-{
- int error = cpu_validate(s, buffer);
- if (error)
- return error;
- s->cpu_acquired = false;
- return 0;
-}
-int yasl_get_double_click_ms(void)
-{
- return display && yasl_public_owner() ? yasl_private_display_double_click_ms(display) : 400;
-}
-int yasl_clipboard_set(enum yasl_selection sel, const char *text)
-{
+ *out = (yasl_event){.type = YASL_EVENT_NONE};
if (!yasl_public_owner())
return -errno;
- errno = 0;
- return yasl_public_status(
- 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);
-}
-int yasl_surface_set_text_input(yasl_surface *s, bool active, enum yasl_text_purpose purpose)
-{
- if (!yasl_public_surface(s, false))
- return -errno;
- if (purpose < YASL_TEXT_NORMAL || purpose > YASL_TEXT_TERMINAL)
- return yasl_public_error(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 -errno;
- }
- return 0;
+ if (pumping || refresh_depth)
+ return yasl_public_error(EBUSY);
+ free(delivered_text);
+ delivered_text = NULL;
+ pumping = true;
+ int n = pump(out, timeout);
+ pumping = false;
+ if (n < 0)
+ *out = (yasl_event){.type = YASL_EVENT_NONE};
+ return n;
}
-int yasl_surface_set_surrounding_text(yasl_surface *s, const char *text, size_t len, size_t begin,
- size_t end, uint64_t revision)
+void yasl_wake(void)
{
- if (!yasl_public_surface(s, false))
- return -errno;
- if ((!text && len) || begin > end || end > len || len > INT32_MAX ||
- (len && memchr(text, 0, len)))
- return yasl_public_error(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_error(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_error(EILSEQ);
- for (size_t j = 1; j < n; j++)
- if (((unsigned char)text[i + j] & 0xc0) != 0x80)
- return yasl_public_error(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_error(EILSEQ);
- i += n;
- }
- char *copy = copy_text(text, len);
- if (!copy)
- return yasl_public_error(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 -errno;
- }
- free(old);
- return 0;
+ atomic_store(&wake_requested, true);
+ pthread_mutex_lock(&lifecycle_mutex);
+ if (display && display->loop)
+ yasl_private_loop_kick(display->loop);
+ pthread_mutex_unlock(&lifecycle_mutex);
}
-static bool logical_rect(yasl_surface *s, double x, double y, double w, double h,
- struct yasl_private_rect2i *r)
+uint64_t yasl_time_ns(void)
{
- if (!isfinite(x) || !isfinite(y) || !isfinite(w) || !isfinite(h) || w < 0 || h < 0)
- return yasl_public_fail(EINVAL);
- double scale = scale_of(s);
- if (fabs(x) * scale > INT_MAX || fabs(y) * scale > INT_MAX || w * scale > INT_MAX ||
- h * scale > INT_MAX)
- return yasl_public_fail(EOVERFLOW);
- 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;
+ return yasl_private_loop_now_ns();
}
-int yasl_surface_set_ime_caret(yasl_surface *s, double x, double y, double w, double h)
+
+/* CPU rendering */
+uint32_t *yasl_cpu_pixels(yasl_surface *s, int *pw, int *ph)
{
+ if (pw)
+ *pw = 0;
+ if (ph)
+ *ph = 0;
if (!yasl_public_surface(s, false))
- return -errno;
- struct yasl_private_rect2i r;
- if (!logical_rect(s, x, y, w, h, &r))
- return -errno;
- if (!s->display->ops->surface_set_ime_caret)
- return yasl_public_error(ENOTSUP);
- yasl_private_surface_set_ime_caret(s, r);
- return 0;
-}
-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))) {
- return yasl_public_fail(ESTALE);
- }
- 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 || w > 32767 || h > 32767 || (unsigned)anchor > 8 ||
- (unsigned)gravity > 8 || (constraints & ~63u))
- return yasl_public_fail(EINVAL);
- if (w * scale_of(s) > 32767 || h * scale_of(s) > 32767)
- return yasl_public_fail(EOVERFLOW);
- *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};
- return logical_rect(s, x, y, aw, ah, &out->anchor);
-}
-yasl_surface *yasl_popup_create(yasl_surface *parent, const struct yasl_popup_options *options,
- const yasl_event *trigger)
-{
- if (!options) {
- yasl_public_fail(EINVAL);
return NULL;
- }
- int w = options->width, h = options->height;
- double x = options->x, y = options->y, aw = options->anchor_width, ah = options->anchor_height;
- enum yasl_popup_edge anchor = options->anchor, gravity = options->gravity;
- unsigned constraints = options->constraints;
- 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);
+ if (s->presenter != YASL_PRIVATE_PRESENTER_CPU)
+ return yasl_public_fail(ENOTSUP), NULL;
+ if (!canvas_fit(s, s->size.x, s->size.y))
return NULL;
- }
- yasl_public_register_surface(s, parent->creation_flags, parent);
- return s;
+ if (pw)
+ *pw = s->canvas_width;
+ if (ph)
+ *ph = s->canvas_height;
+ return s->canvas;
}
-int yasl_popup_reposition(yasl_surface *s, const struct yasl_popup_options *options)
+int yasl_cpu_present(yasl_surface *s)
{
- if (!options)
- return yasl_public_error(EINVAL);
- int w = options->width, h = options->height;
- double x = options->x, y = options->y, aw = options->anchor_width, ah = options->anchor_height;
- enum yasl_popup_edge anchor = options->anchor, gravity = options->gravity;
- unsigned constraints = options->constraints;
- if (!yasl_public_surface(s, false))
+ if (!yasl_public_connected(s))
return -errno;
- struct yasl_private_popup_spec spec;
- if (!s->parent)
- return yasl_public_error(EINVAL);
- if (!popup_spec(s->parent, w, h, x, y, aw, ah, anchor, gravity, constraints, &spec))
- return -errno;
- errno = 0;
- return yasl_public_status(yasl_private_surface_popup_reposition(s, &spec) ||
- yasl_public_fail(errno));
-}
-int yasl_surface_start_interactive_move(yasl_surface *s, const yasl_event *e)
-{
- if (!yasl_public_surface(s, false) || !trigger_valid(s, e))
- return -errno;
- errno = 0;
- return yasl_public_status(yasl_private_surface_start_interactive_move(s) ||
- yasl_public_fail(errno));
-}
-int 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 -errno;
- errno = 0;
- return yasl_public_status(
- yasl_private_surface_start_interactive_resize(s, (enum yasl_private_resize_edge)edge) ||
- yasl_public_fail(errno));
-}
-int 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 -errno;
- struct yasl_private_rect2i r;
- if (!logical_rect(s, x, y, 0, 0, &r))
- return -errno;
- errno = 0;
- return yasl_public_status(
- yasl_private_surface_show_window_menu(s, (struct yasl_private_vector2i){r.x, r.y}) ||
- yasl_public_fail(errno));
-}
-int yasl_drag_start(yasl_surface *s, const yasl_event *e, const struct yasl_drag_image *source)
-{
- if (!yasl_public_surface(s, false) || !trigger_valid(s, e))
- return -errno;
- struct yasl_private_drag_image image = {0};
- if (source)
- image = (struct yasl_private_drag_image){.pixels = source->pixels,
- .size = {source->width, source->height},
- .hotspot = {source->hotspot_x, source->hotspot_y}};
- errno = 0;
- return yasl_public_status(
- yasl_private_display_drag_start(display, s, source ? &image : NULL, NULL, NULL) ||
- yasl_public_fail(errno));
-}
-int yasl_drag_cancel(void)
-{
- if (!yasl_public_owner())
+ if (s->presenter != YASL_PRIVATE_PRESENTER_CPU)
+ return yasl_public_error(ENOTSUP);
+ if (!canvas_fit(s, s->size.x, s->size.y))
return -errno;
- yasl_private_display_drag_cancel(display);
+ yasl_private_surface_request_frame(s);
return 0;
}
-int yasl_surface_drag_carry(yasl_surface *s, double x, double y)
-{
- if (!yasl_public_surface(s, false))
- return -errno;
- struct yasl_private_rect2i r;
- if (!logical_rect(s, x, y, 0, 0, &r))
- return -errno;
- errno = 0;
- return yasl_public_status(
- yasl_private_surface_drag_carry(s, (struct yasl_private_vector2i){r.x, r.y}) ||
- yasl_public_fail(errno));
-}
-int yasl_surface_place_under_pointer(yasl_surface *s, double x, double y)
-{
- if (!yasl_public_surface(s, false))
- return -errno;
- struct yasl_private_rect2i r;
- if (!logical_rect(s, x, y, 0, 0, &r))
- return -errno;
- errno = 0;
- return yasl_public_status(
- 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 struct yasl_surface_options *options,
- const struct yasl_gl_options *gl)
+/* OpenGL; these may run on a rendering thread, so they read only immutable or atomic state. */
+static bool gl_surface(yasl_surface *s)
{
- if (!options || options->type != YASL_SURFACE_NATIVE || (options->flags & ~14u)) {
- yasl_public_fail(EINVAL);
- return NULL;
- }
- const struct yasl_gl_options defaults = YASL_GL_OPTIONS_INIT;
- if (!gl)
- gl = &defaults;
- const int attrs[] = {
- YASL_PRIVATE_GL_MAJOR,
- gl->major,
- YASL_PRIVATE_GL_MINOR,
- gl->minor,
- YASL_PRIVATE_GL_PROFILE,
- gl->profile,
- YASL_PRIVATE_GL_API,
- gl->api,
- YASL_PRIVATE_GL_RED_BITS,
- gl->red_bits,
- YASL_PRIVATE_GL_GREEN_BITS,
- gl->green_bits,
- YASL_PRIVATE_GL_BLUE_BITS,
- gl->blue_bits,
- YASL_PRIVATE_GL_ALPHA_BITS,
- gl->alpha_bits,
- YASL_PRIVATE_GL_DEPTH_BITS,
- gl->depth_bits,
- YASL_PRIVATE_GL_STENCIL_BITS,
- gl->stencil_bits,
- YASL_PRIVATE_GL_SAMPLES,
- gl->samples,
- YASL_PRIVATE_GL_DEBUG,
- gl->debug,
- YASL_PRIVATE_GL_FORWARD_COMPAT,
- gl->forward_compat,
- YASL_PRIVATE_GL_SRGB,
- gl->srgb,
- YASL_PRIVATE_GL_END,
- };
- const char *title = options->title;
- int w = options->width, h = options->height;
- unsigned flags = options->flags;
- yasl_surface *share = gl->share;
- if (!creation_valid(w, h, flags)) {
- return NULL;
- }
- struct yasl_private_gl_options parsed;
- if (!yasl_private_gl_parse_options(attrs, &parsed)) {
- yasl_public_fail(errno);
- return NULL;
- }
- if (share && !display) {
- yasl_public_fail(EINVAL);
- 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 = {w, h}, .flags = flags, .external_gpu = true};
- errno = 0;
- 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);
- s->userdata = options->userdata;
- return s;
+ if (!display)
+ return yasl_public_fail(ENODEV);
+ if (!s || s->display != display || s->presenter != YASL_PRIVATE_PRESENTER_GL)
+ return yasl_public_fail(EINVAL);
+ return !atomic_load(&display->offline) || yasl_public_fail(ENOTCONN);
}
int yasl_gl_make_current(yasl_surface *s)
{
- if (!display) {
- return yasl_public_error(ENODEV);
- }
- if (s && (s->display != display || s->presenter != YASL_PRIVATE_PRESENTER_GL))
- return yasl_public_error(EINVAL);
- if (display->offline)
- return yasl_public_error(ENOTCONN);
+ if (!s && (!display || !display->ops->gl_make_current))
+ return 0; /* Nothing can be current. */
+ if (s && !gl_surface(s))
+ return -errno;
if (!display->ops->gl_make_current)
return yasl_public_error(ENOTSUP);
- errno = 0;
- return yasl_public_status(display->ops->gl_make_current(display, s) || yasl_public_fail(errno));
+ return STATUS(display->ops->gl_make_current(display, s));
}
yasl_gl_proc yasl_gl_get_proc_address(const char *name)
{
- if (!display) {
- yasl_public_fail(ENODEV);
- return NULL;
- }
- if (!name || !*name) {
- yasl_public_fail(EINVAL);
- return NULL;
- }
- if (display->offline) {
- yasl_public_fail(ENOTCONN);
- return NULL;
- }
- if (!display->ops->gl_get_proc_address) {
- yasl_public_fail(ENOTSUP);
- return NULL;
- }
+ if (!display)
+ return yasl_public_fail(ENODEV), NULL;
+ if (!name || !*name)
+ return yasl_public_fail(EINVAL), NULL;
+ if (atomic_load(&display->offline))
+ return yasl_public_fail(ENOTCONN), NULL;
+ if (!display->ops->gl_get_proc_address)
+ return yasl_public_fail(ENOTSUP), NULL;
yasl_gl_proc p = display->ops->gl_get_proc_address(display, name);
if (!p)
yasl_public_fail(ENOENT);
@@ -1139,27 +983,17 @@ yasl_gl_proc yasl_gl_get_proc_address(const char *name)
}
int yasl_gl_set_swap_interval(yasl_surface *s, int interval)
{
- if (!display)
- return yasl_public_error(ENODEV);
- if (display->offline)
- return yasl_public_error(ENOTCONN);
- if (!s || s->display != display || s->presenter != YASL_PRIVATE_PRESENTER_GL ||
- !s->display->ops->gl_swap_interval)
- return yasl_public_error(EINVAL);
- errno = 0;
- return yasl_public_status(s->display->ops->gl_swap_interval(s->display, s, interval) ||
- yasl_public_fail(errno));
+ if (!gl_surface(s))
+ return -errno;
+ if (!display->ops->gl_swap_interval)
+ return yasl_public_error(ENOTSUP);
+ return STATUS(display->ops->gl_swap_interval(display, s, interval));
}
int yasl_gl_present(yasl_surface *s)
{
- if (!display)
- return yasl_public_error(ENODEV);
- if (display->offline)
- return yasl_public_error(ENOTCONN);
- if (!s || s->display != display || s->presenter != YASL_PRIVATE_PRESENTER_GL ||
- !s->display->ops->gl_swap_buffers)
- return yasl_public_error(EINVAL);
- errno = 0;
- return yasl_public_status(s->display->ops->gl_swap_buffers(s->display, s) ||
- yasl_public_fail(errno));
+ if (!gl_surface(s))
+ return -errno;
+ if (!display->ops->gl_swap_buffers)
+ return yasl_public_error(ENOTSUP);
+ return STATUS(display->ops->gl_swap_buffers(display, s));
}
diff --git a/src/common/types.h b/src/common/types.h
index 8b9bbba..3fffd47 100644
--- a/src/common/types.h
+++ b/src/common/types.h
@@ -126,7 +126,8 @@ enum yasl_private_display_cap : uint32_t {
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. */
+ YASL_PRIVATE_CAP_RELATIVE_POINTER = 1 << 9, /**< YASL_PRIVATE_POINTER_RELATIVE is available. */
+ YASL_PRIVATE_CAP_TEXT_INPUT = 1 << 11, /**< Input method support. */
};
/** Independent selections. An unsupported selection never aliases the clipboard. */
@@ -378,6 +379,9 @@ struct yasl_private_key_event {
uint32_t native_code;
};
+/** Bits of yasl_private_pointer_event.scroll_valid. */
+enum yasl_private_scroll_valid { YASL_PRIVATE_SCROLL_LINES = 1u, YASL_PRIVATE_SCROLL_PIXELS = 2u };
+
/** 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. */
diff --git a/src/common/vulkan.c b/src/common/vulkan.c
index 77a4dd4..cdf3e8b 100644
--- a/src/common/vulkan.c
+++ b/src/common/vulkan.c
@@ -12,12 +12,6 @@
#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)
@@ -32,17 +26,40 @@ struct yasl_vulkan_binding {
bool has_allocator;
PFN_vkDestroySurfaceKHR destroy;
};
-int yasl_vulkan_init(PFN_vkGetInstanceProcAddr loader)
+static bool backend_requirements(struct yasl_vulkan_requirements *out)
{
- struct yasl_private_display *d = yasl_public_display();
- if (d && !yasl_public_owner())
- return -errno;
+ static const char *const wayland[] = {"VK_KHR_surface", "VK_KHR_wayland_surface"};
+ static const char *const x11[] = {"VK_KHR_surface", "VK_KHR_xlib_surface"};
+ static const char *const metal[] = {"VK_KHR_surface", "VK_EXT_metal_surface",
+ "VK_KHR_portability_enumeration"};
+ switch (yasl_public_display()->ops->type) {
+ case YASL_PRIVATE_BACKEND_WAYLAND:
+ *out = (struct yasl_vulkan_requirements){wayland, 2, 0};
+ return true;
+ case YASL_PRIVATE_BACKEND_X11:
+ *out = (struct yasl_vulkan_requirements){x11, 2, 0};
+ return true;
+ case YASL_PRIVATE_BACKEND_COCOA:
+ *out = (struct yasl_vulkan_requirements){
+ metal, portability ? 3 : 2,
+ portability ? VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR : 0};
+ return true;
+ default:
+ return yasl_public_fail(ENOTSUP);
+ }
+}
+int yasl_vulkan_init(PFN_vkGetInstanceProcAddr loader, struct yasl_vulkan_requirements *out)
+{
+ if (!out)
+ return yasl_public_error(EINVAL);
+ *out = (struct yasl_vulkan_requirements){0};
if (!loader)
return yasl_public_error(EINVAL);
- if (d)
- for (yasl_surface *s = d->surfaces; s; s = s->event_next)
- if (s->vulkan)
- return yasl_public_error(EBUSY);
+ if (!yasl_public_initialize())
+ return -errno;
+ for (yasl_surface *s = yasl_public_display()->surfaces; s; s = s->event_next)
+ if (s->vulkan)
+ return yasl_public_error(EBUSY);
PFN_vkEnumerateInstanceExtensionProperties enumerate =
(PFN_vkEnumerateInstanceExtensionProperties)loader(
VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
@@ -50,59 +67,22 @@ int yasl_vulkan_init(PFN_vkGetInstanceProcAddr loader)
return yasl_public_error(ENOTSUP);
uint32_t count = 0;
VkResult result = enumerate(NULL, &count, NULL);
- if (result != VK_SUCCESS)
- return yasl_public_error(EIO);
- VkExtensionProperties *ext = count ? calloc(count, sizeof(*ext)) : NULL;
- if (count && !ext)
- return yasl_public_error(ENOMEM);
- bool next_portability = false;
- if (count)
+ VkExtensionProperties *ext = NULL;
+ if (result == VK_SUCCESS && count) {
+ if (!(ext = calloc(count, sizeof(*ext))))
+ return yasl_public_error(ENOMEM);
result = enumerate(NULL, &count, ext);
- if (result == VK_SUCCESS)
- for (uint32_t i = 0; i < count; i++)
- if (!strcmp(ext[i].extensionName, "VK_KHR_portability_enumeration"))
- next_portability = true;
+ }
+ bool next_portability = false;
+ for (uint32_t i = 0; result == VK_SUCCESS && i < count; i++)
+ if (!strcmp(ext[i].extensionName, "VK_KHR_portability_enumeration"))
+ next_portability = true;
free(ext);
if (result != VK_SUCCESS)
return yasl_public_error(EIO);
get_proc = loader;
portability = next_portability;
- return 0;
-}
-int yasl_vulkan_get_requirements(const yasl_surface *s, struct yasl_vulkan_requirements *out)
-{
- static const char *const wayland[] = {"VK_KHR_surface", "VK_KHR_wayland_surface"};
- static const char *const x11[] = {"VK_KHR_surface", "VK_KHR_xlib_surface"};
- static const char *const metal[] = {"VK_KHR_surface", "VK_EXT_metal_surface",
- "VK_KHR_portability_enumeration"};
- if (!out)
- return yasl_public_error(EINVAL);
- *out = (struct yasl_vulkan_requirements){0};
- if (!yasl_public_surface((yasl_surface *)s, false))
- return -errno;
- if (!get_proc)
- return yasl_public_error(ENODEV);
- if (s->presenter != YASL_PRIVATE_PRESENTER_NONE &&
- s->presenter != YASL_PRIVATE_PRESENTER_VULKAN)
- return yasl_public_error(EBUSY);
- switch (s->display->ops->type) {
- case YASL_PRIVATE_BACKEND_WAYLAND:
- out->extensions = wayland;
- out->extension_count = 2;
- break;
- case YASL_PRIVATE_BACKEND_X11:
- out->extensions = x11;
- out->extension_count = 2;
- break;
- case YASL_PRIVATE_BACKEND_COCOA:
- out->extensions = metal;
- out->extension_count = portability ? 3 : 2;
- out->flags = portability ? VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR : 0;
- break;
- default:
- return yasl_public_error(ENOTSUP);
- }
- return 0;
+ return backend_requirements(out) ? 0 : -errno;
}
int yasl_vulkan_destroy_surface(yasl_surface *s)
{
@@ -133,7 +113,7 @@ VkResult yasl_vulkan_create_surface(yasl_surface *s, VkInstance instance,
yasl_public_fail(ENODEV);
return VK_ERROR_INITIALIZATION_FAILED;
}
- if (s->display->offline) {
+ if (atomic_load(&s->display->offline)) {
yasl_public_fail(ENOTCONN);
return VK_ERROR_SURFACE_LOST_KHR;
}
@@ -156,16 +136,16 @@ VkResult yasl_vulkan_create_surface(yasl_surface *s, VkInstance instance,
switch (s->display->ops->type) {
#if YASL_HAS_WAYLAND
case YASL_PRIVATE_BACKEND_WAYLAND: {
- struct yasl_wayland_handles native;
- if (yasl_wayland_get_native(s, &native) < 0)
+ struct yasl_native native;
+ if (yasl_surface_get_native(s, &native) < 0)
break;
PFN_vkCreateWaylandSurfaceKHR create =
(PFN_vkCreateWaylandSurfaceKHR)get_proc(instance, "vkCreateWaylandSurfaceKHR");
if (create) {
VkWaylandSurfaceCreateInfoKHR info = {
.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
- .display = native.display,
- .surface = native.surface};
+ .display = native.wl_display,
+ .surface = native.wl_surface};
result = create(instance, &info, allocator, out);
}
break;
@@ -173,16 +153,16 @@ VkResult yasl_vulkan_create_surface(yasl_surface *s, VkInstance instance,
#endif
#if YASL_HAS_X11
case YASL_PRIVATE_BACKEND_X11: {
- struct yasl_x11_handles native;
- if (yasl_x11_get_native(s, &native) < 0)
+ struct yasl_native native;
+ if (yasl_surface_get_native(s, &native) < 0)
break;
PFN_vkCreateXlibSurfaceKHR create =
(PFN_vkCreateXlibSurfaceKHR)get_proc(instance, "vkCreateXlibSurfaceKHR");
if (create) {
VkXlibSurfaceCreateInfoKHR info = {.sType =
VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
- .dpy = native.display,
- .window = (Window)native.window};
+ .dpy = native.x11_display,
+ .window = (Window)native.x11_window};
result = create(instance, &info, allocator, out);
}
break;
diff --git a/src/platform/cocoa/keymap.h b/src/platform/cocoa/keymap.h
index 792b10d..bfb9604 100644
--- a/src/platform/cocoa/keymap.h
+++ b/src/platform/cocoa/keymap.h
@@ -1,6 +1,7 @@
#ifndef YASL_PRIVATE_COCOA_KEYMAP_H
#define YASL_PRIVATE_COCOA_KEYMAP_H
+#include <stdint.h>
#include "yasl/keycodes.h"
enum yasl_key yasl_private_key_from_cocoa(uint32_t code);
diff --git a/src/platform/freedesktop/desktop.c b/src/platform/freedesktop/desktop.c
index f90701a..76a7956 100644
--- a/src/platform/freedesktop/desktop.c
+++ b/src/platform/freedesktop/desktop.c
@@ -136,6 +136,7 @@ size_t desktop_resource_dir(char *buf, size_t cap)
{
(void)buf;
(void)cap;
+ errno = ENOTSUP; /* Linux applications have no bundle. */
return 0;
}
diff --git a/src/platform/wayland/keymap.h b/src/platform/wayland/keymap.h
index 26b6ed4..2cef68c 100644
--- a/src/platform/wayland/keymap.h
+++ b/src/platform/wayland/keymap.h
@@ -1,6 +1,7 @@
#ifndef YASL_PRIVATE_WAYLAND_KEYMAP_H
#define YASL_PRIVATE_WAYLAND_KEYMAP_H
+#include <stdint.h>
#include "yasl/keycodes.h"
enum yasl_key yasl_private_key_from_evdev(uint32_t code);
diff --git a/src/platform/wayland/surface.c b/src/platform/wayland/surface.c
index 635c905..2b72317 100644
--- a/src/platform/wayland/surface.c
+++ b/src/platform/wayland/surface.c
@@ -382,6 +382,8 @@ uint32_t wayland_display_caps(const struct wayland_display *c)
caps |= YASL_PRIVATE_CAP_DRAG_CARRY;
if (c->primary_selection_mgr)
caps |= YASL_PRIVATE_CAP_PRIMARY_SELECTION;
+ if (c->relative_pointer_mgr && c->pointer_constraints)
+ caps |= YASL_PRIVATE_CAP_RELATIVE_POINTER;
return caps;
}
diff --git a/src/platform/x11/keymap.h b/src/platform/x11/keymap.h
index 9bb009d..daf4ce4 100644
--- a/src/platform/x11/keymap.h
+++ b/src/platform/x11/keymap.h
@@ -1,6 +1,7 @@
#ifndef YASL_PRIVATE_X11_KEYMAP_H
#define YASL_PRIVATE_X11_KEYMAP_H
+#include <stdint.h>
#include "yasl/keycodes.h"
enum yasl_key yasl_private_key_from_xkb_name(const char name[4]);