summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/game.c12
-rw-r--r--examples/hello.c56
-rw-r--r--examples/notepad.c14
-rw-r--r--examples/simple_window.c10
-rw-r--r--examples/tablet.c18
-rw-r--r--include/yasl/cpu.h25
-rw-r--r--include/yasl/desktop.h20
-rw-r--r--include/yasl/gl.h37
-rw-r--r--include/yasl/metal.h5
-rw-r--r--include/yasl/native/cocoa.h15
-rw-r--r--include/yasl/native/wayland.h13
-rw-r--r--include/yasl/native/x11.h13
-rw-r--r--include/yasl/vulkan.h28
-rw-r--r--include/yasl/yasl.h401
-rw-r--r--src/common/backend.h5
-rw-r--r--src/common/desktop.c106
-rw-r--r--src/common/native.c119
-rw-r--r--src/common/public_internal.h3
-rw-r--r--src/common/surface.c1429
-rw-r--r--src/common/vulkan.c223
-rw-r--r--src/platform/wayland/backend.c5
-rw-r--r--tests/common/test_gl.c44
-rw-r--r--tests/common/test_surface_integration.c302
-rw-r--r--tests/common/test_vulkan.c35
-rw-r--r--tests/meson.build6
-rw-r--r--tests/test_api.cpp17
-rw-r--r--tests/test_lifecycle.c45
-rw-r--r--tests/wayland/test_reconnect.c57
-rw-r--r--tests/x11/test_tablet_native.c29
29 files changed, 2263 insertions, 829 deletions
diff --git a/examples/game.c b/examples/game.c
index 0be4461..850b39b 100644
--- a/examples/game.c
+++ b/examples/game.c
@@ -12,10 +12,10 @@ typedef void (*viewport_fn)(int, int, int, int);
int main(int argc, char **argv)
{
bool smoke = argc == 2 && !strcmp(argv[1], "--smoke-test");
- yasl_surface *s = yasl_gl_surface_create("YASL OpenGL", 800, 600, 0, NULL, NULL);
- if (!s || !yasl_gl_make_current(s)) {
+ yasl_surface *s = yasl_gl_surface_create(&(struct yasl_surface_options){.title="YASL OpenGL", .width=800, .height=600, .type=YASL_SURFACE_NATIVE, .flags=0}, NULL);
+ if (!s || (yasl_gl_make_current(s) < 0)) {
const char *error;
- yasl_get_error(&error);
+ error = strerror(errno);
fprintf(stderr, "%s\n", error);
yasl_shutdown();
return 1;
@@ -27,7 +27,7 @@ int main(int argc, char **argv)
yasl_shutdown();
return 1;
}
- yasl_gl_swap_interval(s, 1);
+ yasl_gl_set_swap_interval(s, 1);
int result = 0, frames = 0;
bool online = true;
for (bool running = true; running;) {
@@ -40,7 +40,7 @@ int main(int argc, char **argv)
if (e.type == YASL_EVENT_DISPLAY_LOST) online = false;
if (e.type == YASL_EVENT_DISPLAY_RESTORED) {
/* The context is new: bind it again and restore GL state here. */
- online = yasl_gl_make_current(s) && yasl_gl_swap_interval(s, 1);
+ online = (yasl_gl_make_current(s) == 0) && (yasl_gl_set_swap_interval(s, 1) == 0);
}
if (n > 0 || !online || !running) continue;
int w, h;
@@ -49,7 +49,7 @@ int main(int argc, char **argv)
double t = yasl_time_ns() / 1e9;
clear_color((float)(0.3 + 0.2 * sin(t)), 0.2f, 0.4f, 1);
clear(0x00004000u);
- if (!yasl_gl_swap_buffers(s)) { result = 1; break; }
+ if ((yasl_gl_present(s) < 0)) { result = 1; break; }
if (smoke && ++frames == 3) break;
}
yasl_gl_make_current(NULL);
diff --git a/examples/hello.c b/examples/hello.c
index 9e324a6..054051d 100644
--- a/examples/hello.c
+++ b/examples/hello.c
@@ -1,26 +1,50 @@
-/* A CPU-rendered window. Build instructions: docs/getting-started.md. */
+/* A CPU-rendered window. */
#include <yasl/yasl.h>
#include <yasl/cpu.h>
int main(void)
{
- yasl_surface *surface = yasl_surface_create("Hello YASL", 640, 480, YASL_SURFACE_CPU);
- yasl_event event;
- while (surface && yasl_wait_event(&event, -1) >= 0 &&
- event.type != YASL_EVENT_CLOSE && event.type != YASL_EVENT_QUIT) {
- if (event.type != YASL_EVENT_REFRESH) continue;
- int w, h;
- uint32_t *pixels = yasl_cpu_pixels(surface, &w, &h);
- for (int y = 0; pixels && y < h; y++) {
- for (int x = 0; x < w; x++) {
- /* Red grows rightward, green downward, blue fades; opaque premultiplied ARGB. */
- uint32_t r = (uint32_t)(x * 255 / (w > 1 ? w - 1 : 1));
- uint32_t g = (uint32_t)(y * 255 / (h > 1 ? h - 1 : 1));
- pixels[(size_t)y * w + x] = 0xff000000 | r << 16 | g << 8 | (255 - r);
+ struct yasl_surface_options options = {
+ .title = "Hello YASL",
+ .width = 640,
+ .height = 480,
+ .type = YASL_SURFACE_CPU,
+ };
+ yasl_surface *surface = yasl_surface_create(&options);
+ if (!surface)
+ return 1;
+
+ int result = 0;
+ for (;;) {
+ yasl_event event;
+ if (yasl_wait_event(&event, -1) < 0) {
+ result = 1;
+ break;
+ }
+ if (event.type == YASL_EVENT_CLOSE || event.type == YASL_EVENT_QUIT)
+ break;
+ if (event.type != YASL_EVENT_REFRESH)
+ continue;
+
+ struct yasl_cpu_buffer buffer;
+ if (yasl_cpu_acquire(surface, &buffer) < 0) {
+ result = 1;
+ break;
+ }
+ for (int y = 0; y < buffer.height; y++) {
+ uint32_t *row = (uint32_t *)((unsigned char *)buffer.pixels + y * buffer.stride_bytes);
+ for (int x = 0; x < buffer.width; x++) {
+ /* Opaque premultiplied ARGB: red grows rightward, green downward. */
+ uint32_t r = (uint32_t)(x * 255 / (buffer.width > 1 ? buffer.width - 1 : 1));
+ uint32_t g = (uint32_t)(y * 255 / (buffer.height > 1 ? buffer.height - 1 : 1));
+ row[x] = 0xff000000 | r << 16 | g << 8 | (255 - r);
}
}
- yasl_cpu_present(surface);
+ if (yasl_cpu_present(surface, &buffer) < 0) {
+ result = 1;
+ break;
+ }
}
yasl_shutdown();
- return surface ? 0 : 1;
+ return result;
}
diff --git a/examples/notepad.c b/examples/notepad.c
index 9134653..ff12038 100644
--- a/examples/notepad.c
+++ b/examples/notepad.c
@@ -576,7 +576,7 @@ static void editor_copy(struct editor *ed)
snprintf(buf, (size_t)MAX_LINES * MAX_LINE_LEN, "%s\n", ed->lines[ed->cursor_row]);
}
- if (!yasl_clipboard_set(YASL_CLIPBOARD, buf))
+ if ((yasl_clipboard_set(YASL_CLIPBOARD, buf) < 0))
editor_set_status(ed, "Clipboard copy failed");
free(buf);
editor_set_status(ed, "Copied to clipboard");
@@ -988,7 +988,7 @@ static void prompt_execute(struct editor *ed)
static bool editor_init(struct editor *ed)
{
- ed->surface = yasl_surface_create("Untitled - YASL Notepad", 960, 640, YASL_SURFACE_CPU);
+ ed->surface = yasl_surface_create(&(struct yasl_surface_options){.title="Untitled - YASL Notepad", .width=960, .height=640, .type=YASL_SURFACE_CPU, .flags=0});
if (!ed->surface) return false;
ed->surface_scale = yasl_surface_get_scale(ed->surface);
yasl_surface_set_text_input(ed->surface, true, YASL_TEXT_NORMAL);
@@ -1483,8 +1483,10 @@ static void on_event(struct editor *ed, const yasl_event *ev)
static void editor_render(struct editor *ed)
{
- int w, h;
- uint32_t *pix = yasl_cpu_pixels(ed->surface, &w, &h);
+ struct yasl_cpu_buffer buffer;
+ int acquired = yasl_cpu_acquire(ed->surface, &buffer);
+ int w = buffer.width, h = buffer.height;
+ uint32_t *pix = acquired == 0 ? buffer.pixels : NULL;
if (!pix) return;
double display_scale = yasl_surface_get_scale(ed->surface);
@@ -1624,7 +1626,7 @@ static void editor_render(struct editor *ed)
ed->is_modified ? 0xfffab387 : status_accent, scale);
}
- if (!yasl_cpu_present(ed->surface)) {
+ if ((yasl_cpu_present(ed->surface, &buffer) < 0)) {
fprintf(stderr, "Could not present CPU image\n");
ed->failed = true;
ed->running = false;
@@ -1697,7 +1699,7 @@ int main(int argc, char **argv)
}
}
- if (!yasl_set_app_id("org.yasl.notepad") || !editor_init(&ed)) {
+ if ((yasl_init(&(struct yasl_options){.app_id="org.yasl.notepad"}) < 0) || !editor_init(&ed)) {
fprintf(stderr, "Could not initialize YASL Notepad\n");
editor_shutdown(&ed);
yasl_shutdown();
diff --git a/examples/simple_window.c b/examples/simple_window.c
index cb12db1..4b4b552 100644
--- a/examples/simple_window.c
+++ b/examples/simple_window.c
@@ -6,7 +6,7 @@
int main(int argc, char **argv)
{
bool smoke = argc == 2 && !strcmp(argv[1], "--smoke-test");
- yasl_surface *s = yasl_surface_create("YASL input", 640, 480, YASL_SURFACE_CPU);
+ yasl_surface *s = yasl_surface_create(&(struct yasl_surface_options){.title="YASL input", .width=640, .height=480, .type=YASL_SURFACE_CPU, .flags=0});
if (!s) return 1;
double px = -100, py = -100, pressure = 0;
bool dirty = false;
@@ -29,8 +29,10 @@ int main(int argc, char **argv)
}
if (e.type == YASL_EVENT_REFRESH) dirty = true;
if (dirty) {
- int w, h;
- uint32_t *pixels = yasl_cpu_pixels(s, &w, &h);
+ struct yasl_cpu_buffer buffer;
+ int acquired = yasl_cpu_acquire(s, &buffer);
+ int w = buffer.width, h = buffer.height;
+ uint32_t *pixels = acquired == 0 ? buffer.pixels : NULL;
if (!pixels) break;
double scale = yasl_surface_get_scale(s), radius = (8 + pressure * 48) * scale;
for (int y = 0; y < h; y++) {
@@ -39,7 +41,7 @@ int main(int argc, char **argv)
pixels[(size_t)y * w + x] = dx * dx + dy * dy < radius * radius ? 0xffffc060u : 0xff203040u;
}
}
- if (!yasl_cpu_present(s)) break;
+ if ((yasl_cpu_present(s, &buffer) < 0)) break;
dirty = false;
frames++;
}
diff --git a/examples/tablet.c b/examples/tablet.c
index 176a82b..ce796a4 100644
--- a/examples/tablet.c
+++ b/examples/tablet.c
@@ -24,10 +24,11 @@ static void stamp(uint32_t *pixels, int w, int h, double x, double y, double rad
}
/* Paints paper over the area a resize added, or everything when clear is set. */
-static uint32_t *canvas(yasl_surface *s, int *w, int *h, int *painted_w, int *painted_h, bool clear)
+static uint32_t *canvas(yasl_surface *s, int *w, int *h, int *painted_w, int *painted_h, bool clear, struct yasl_cpu_buffer *buffer)
{
- uint32_t *pixels = yasl_cpu_pixels(s, w, h);
- if (!pixels) return NULL;
+ if (yasl_cpu_acquire(s, buffer) < 0) return NULL;
+ *w = buffer->width; *h = buffer->height;
+ uint32_t *pixels = buffer->pixels;
if (clear) *painted_w = *painted_h = 0;
for (int y = 0; y < *h; y++)
for (int x = y < *painted_h ? *painted_w : 0; x < *w; x++) pixels[(size_t)y * *w + x] = PAPER;
@@ -43,11 +44,11 @@ int main(int argc, char **argv)
if (!strcmp(argv[i], "--smoke-test")) smoke = true;
else if (!strcmp(argv[i], "--backend") && i + 1 < argc) {
const char *b = argv[++i];
- if (!yasl_set_backend(!strcmp(b, "wayland") ? YASL_BACKEND_WAYLAND :
- !strcmp(b, "x11") ? YASL_BACKEND_X11 : YASL_BACKEND_COCOA)) return 1;
+ if (!(yasl_init(&(struct yasl_options){.backend=!strcmp(b, "wayland") ? YASL_BACKEND_WAYLAND :
+ !strcmp(b, "x11") ? YASL_BACKEND_X11 : YASL_BACKEND_COCOA}) == 0)) return 1;
} else return 1;
}
- yasl_surface *s = yasl_surface_create("YASL pen canvas — C clears, Escape exits", 800, 600, YASL_SURFACE_CPU);
+ yasl_surface *s = yasl_surface_create(&(struct yasl_surface_options){.title="YASL pen canvas — C clears, Escape exits", .width=800, .height=600, .type=YASL_SURFACE_CPU, .flags=0});
if (!s) return 1;
int painted_w = 0, painted_h = 0, frames = 0, result = 1;
bool contact = false;
@@ -87,7 +88,8 @@ int main(int argc, char **argv)
if (draw || present) {
int w, h;
int old_w = painted_w, old_h = painted_h;
- uint32_t *pixels = canvas(s, &w, &h, &painted_w, &painted_h, clear);
+ struct yasl_cpu_buffer buffer;
+ uint32_t *pixels = canvas(s, &w, &h, &painted_w, &painted_h, clear, &buffer);
if (!pixels) break;
if (w != old_w || h != old_h) contact = false;
if (draw) {
@@ -99,7 +101,7 @@ int main(int argc, char **argv)
for (int i = 1; i <= steps; i++) stamp(pixels, w, h, x - dx + dx * i / steps, y - dy + dy * i / steps, radius, erase);
last_x = x; last_y = y;
}
- if (!yasl_cpu_present(s)) break;
+ if ((yasl_cpu_present(s, &buffer) < 0)) break;
frames++;
}
if (e.type == YASL_EVENT_PEN || e.type == YASL_EVENT_POINTER) contact = down;
diff --git a/include/yasl/cpu.h b/include/yasl/cpu.h
index d18f91f..23e0803 100644
--- a/include/yasl/cpu.h
+++ b/include/yasl/cpu.h
@@ -1,14 +1,29 @@
-/** @file CPU surfaces: draw into the lent pixel buffer, then present it. */
+/** @file CPU rendering with explicitly acquired frame buffers. */
#ifndef YASL_CPU_H
#define YASL_CPU_H
#include "yasl/yasl.h"
#ifdef __cplusplus
extern "C" {
#endif
-/** width*height premultiplied 0xAARRGGBB pixels, rows tightly packed. Contents
- * persist; a resize keeps the overlap and zeroes the rest. Valid until the next wait. */
-YASL_API uint32_t *yasl_cpu_pixels(yasl_surface *surface, int *width, int *height);
-YASL_API bool yasl_cpu_present(yasl_surface *surface);
+/** Borrowed premultiplied 0xAARRGGBB pixels; stride_bytes describes each row.
+ * Only pixels may be edited. Preserve the remaining fields for present/discard.
+ * The buffer expires on successful present/discard, the next wait, or destruction.
+ * Do not retain or access pixels after expiry. */
+struct yasl_cpu_buffer {
+ uint32_t *pixels;
+ int width, height;
+ size_t stride_bytes;
+ uint64_t token;
+};
+/** One outstanding acquisition per surface (-EBUSY otherwise). Starts with the
+ * last submitted pixels; resize preserves their overlap and clears new pixels.
+ * Clears out on failure. Acquisition does not change the visible frame. */
+YASL_API int yasl_cpu_acquire(yasl_surface *surface, struct yasl_cpu_buffer *out);
+/** Snapshot this buffer for presentation, then consume its token. Failed calls
+ * leave a valid acquisition retryable. Stale/consumed tokens return -ESTALE.
+ * Success means queued, not confirmed scanout. */
+YASL_API int yasl_cpu_present(yasl_surface *surface, const struct yasl_cpu_buffer *buffer);
+YASL_API int yasl_cpu_discard(yasl_surface *surface, const struct yasl_cpu_buffer *buffer);
#ifdef __cplusplus
}
#endif
diff --git a/include/yasl/desktop.h b/include/yasl/desktop.h
index 2c2379e..6dc24e9 100644
--- a/include/yasl/desktop.h
+++ b/include/yasl/desktop.h
@@ -5,16 +5,20 @@
#ifdef __cplusplus
extern "C" {
#endif
-enum yasl_appearance { YASL_APPEARANCE_DARK=1u, YASL_APPEARANCE_LIGHT=2u, YASL_APPEARANCE_HIGH_CONTRAST=4u };
-YASL_API bool yasl_open_uri(const char *uri);
-YASL_API bool yasl_notify(const char *title, const char *body);
-YASL_API unsigned yasl_appearance(void);
-YASL_API bool yasl_surface_bell(yasl_surface *surface);
+enum yasl_appearance {
+ YASL_APPEARANCE_DARK = 1u,
+ YASL_APPEARANCE_LIGHT = 2u,
+ YASL_APPEARANCE_HIGH_CONTRAST = 4u
+};
+YASL_API int yasl_open_uri(const char *uri);
+YASL_API int yasl_notify(const char *title, const char *body);
+YASL_API unsigned yasl_get_appearance(void);
+YASL_API int yasl_surface_bell(yasl_surface *surface);
/** Full byte length excluding NUL, zero on failure. Buffer unchanged unless cap
* exceeds returned length. NULL/zero queries size. These three are thread-safe. */
-YASL_API size_t yasl_executable_path(char *out, size_t capacity);
-YASL_API size_t yasl_resource_dir(char *out, size_t capacity);
-YASL_API size_t yasl_default_locale(char *out, size_t capacity);
+YASL_API size_t yasl_get_executable_path(char *out, size_t capacity);
+YASL_API size_t yasl_get_resource_dir(char *out, size_t capacity);
+YASL_API size_t yasl_get_default_locale(char *out, size_t capacity);
#ifdef __cplusplus
}
#endif
diff --git a/include/yasl/gl.h b/include/yasl/gl.h
index 39d2c63..d5603e3 100644
--- a/include/yasl/gl.h
+++ b/include/yasl/gl.h
@@ -1,27 +1,36 @@
-/** @file Optional EGL/OpenGL/OpenGL ES contexts on Linux. Attribute lists are per-call. */
+/** @file Optional EGL/OpenGL/OpenGL ES contexts on Linux. */
#ifndef YASL_GL_H
#define YASL_GL_H
#include "yasl/yasl.h"
#ifdef __cplusplus
extern "C" {
#endif
-enum yasl_gl_attribute { YASL_GL_END, YASL_GL_MAJOR, YASL_GL_MINOR, YASL_GL_PROFILE, YASL_GL_DEBUG,
- YASL_GL_FORWARD_COMPAT, YASL_GL_RED_BITS, YASL_GL_GREEN_BITS, YASL_GL_BLUE_BITS, YASL_GL_ALPHA_BITS,
- YASL_GL_DEPTH_BITS, YASL_GL_STENCIL_BITS, YASL_GL_SAMPLES, YASL_GL_SRGB, YASL_GL_API };
enum yasl_gl_profile { YASL_GL_ANY, YASL_GL_CORE, YASL_GL_COMPATIBILITY };
enum yasl_gl_api { YASL_GL_OPENGL, YASL_GL_OPENGL_ES };
typedef void (*yasl_gl_proc)(void);
-/** key/value pairs terminated by END, NULL defaults to GL3.2 core RGBA8 D24 S8.
- * share is a live GL surface or NULL. CPU flag is invalid. Context is owned by
- * the returned surface. Detach it from all rendering threads before destruction.
- * Cocoa returns NULL with YASL_ERROR_UNSUPPORTED; use Metal or MoltenVK. */
-YASL_API yasl_surface *yasl_gl_surface_create(const char *title, int width, int height,
- unsigned flags, const int *attributes, yasl_surface *share);
-/** May run on a rendering thread. NULL detaches this thread's current context. */
-YASL_API bool yasl_gl_make_current(yasl_surface *surface);
+struct yasl_gl_options {
+ int major, minor;
+ enum yasl_gl_profile profile;
+ enum yasl_gl_api api;
+ int red_bits, green_bits, blue_bits, alpha_bits;
+ int depth_bits, stencil_bits, samples;
+ bool debug, forward_compat, srgb;
+ yasl_surface *share;
+};
+/** C and C++ initializer: GL 3.2 core, RGBA8, D24, S8. */
+#define YASL_GL_OPTIONS_INIT \
+ {3, 2, YASL_GL_CORE, YASL_GL_OPENGL, 8, 8, 8, 8, 24, 8, 0, false, false, false, NULL}
+/** NULL gl selects defaults. options->type must be NATIVE. share, when set,
+ * must be a live GL surface. Detach the owned context from every rendering
+ * thread before destruction. Cocoa fails with errno=ENOTSUP. */
+YASL_API yasl_surface *yasl_gl_surface_create(const struct yasl_surface_options *options,
+ const struct yasl_gl_options *gl);
+/** These four operations may run on a rendering thread. The application must
+ * synchronize surface/display destruction. NULL make_current detaches. */
+YASL_API int yasl_gl_make_current(yasl_surface *surface);
YASL_API yasl_gl_proc yasl_gl_get_proc_address(const char *name);
-YASL_API bool yasl_gl_swap_interval(yasl_surface *surface, int interval);
-YASL_API bool yasl_gl_swap_buffers(yasl_surface *surface);
+YASL_API int yasl_gl_set_swap_interval(yasl_surface *surface, int interval);
+YASL_API int yasl_gl_present(yasl_surface *surface);
#ifdef __cplusplus
}
#endif
diff --git a/include/yasl/metal.h b/include/yasl/metal.h
index 695f80c..8197d80 100644
--- a/include/yasl/metal.h
+++ b/include/yasl/metal.h
@@ -12,8 +12,9 @@ extern "C" {
#endif
/** Borrowed until surface destruction. YASL owns view/layer and drawableSize.
* No device, drawable acquisition, command submission, or implicit present.
- * Returns NULL on unsupported backend, CPU/GL surface, or incompatible presenter. */
-YASL_API CAMetalLayer *yasl_metal_layer(yasl_surface *surface);
+ * Returns -ENOTSUP on unsupported backends, -EBUSY for a different presenter.
+ * Clears out on failure. Successful access reserves the surface for Metal. */
+YASL_API int yasl_metal_get_layer(yasl_surface *surface, CAMetalLayer **out);
#ifdef __cplusplus
}
#endif
diff --git a/include/yasl/native/cocoa.h b/include/yasl/native/cocoa.h
index bd92c98..2d2d432 100644
--- a/include/yasl/native/cocoa.h
+++ b/include/yasl/native/cocoa.h
@@ -1,3 +1,4 @@
+/** @file Borrowed cocoa handles for an initialized, connected surface. */
#ifndef YASL_NATIVE_COCOA_H
#define YASL_NATIVE_COCOA_H
#include "yasl/yasl.h"
@@ -13,10 +14,16 @@ typedef struct objc_object NSView;
#ifdef __cplusplus
extern "C" {
#endif
-/** Borrowed native objects. Use on main thread, never change native ownership. */
-YASL_API NSApplication *yasl_cocoa_application(void);
-YASL_API NSWindow *yasl_cocoa_window(const yasl_surface *surface);
-YASL_API NSView *yasl_cocoa_view(const yasl_surface *surface);
+struct yasl_cocoa_handles {
+ NSApplication *application;
+ NSWindow *window;
+ NSView *view;
+};
+/** Owner thread only. Returns -ENOTSUP for another backend and -ENOTCONN
+ * while disconnected. Clears out on failure. Handles expire on display loss
+ * or surface destruction; query again after DISPLAY_RESTORED. Never destroy
+ * borrowed objects or dispatch/close the connection independently. */
+YASL_API int yasl_cocoa_get_native(const yasl_surface *surface, struct yasl_cocoa_handles *out);
#ifdef __cplusplus
}
#endif
diff --git a/include/yasl/native/wayland.h b/include/yasl/native/wayland.h
index 1b8881e..5c18a80 100644
--- a/include/yasl/native/wayland.h
+++ b/include/yasl/native/wayland.h
@@ -1,3 +1,4 @@
+/** @file Borrowed wayland handles for an initialized, connected surface. */
#ifndef YASL_NATIVE_WAYLAND_H
#define YASL_NATIVE_WAYLAND_H
#include "yasl/yasl.h"
@@ -6,9 +7,15 @@ struct wl_surface;
#ifdef __cplusplus
extern "C" {
#endif
-/** Borrowed handles; never dispatch or close native connections independently. */
-YASL_API struct wl_display *yasl_wayland_display(const yasl_surface *surface);
-YASL_API struct wl_surface *yasl_wayland_surface(const yasl_surface *surface);
+struct yasl_wayland_handles {
+ struct wl_display *display;
+ struct wl_surface *surface;
+};
+/** Owner thread only. Returns -ENOTSUP for another backend and -ENOTCONN
+ * while disconnected. Clears out on failure. Handles expire on display loss
+ * or surface destruction; query again after DISPLAY_RESTORED. Never destroy
+ * borrowed objects or dispatch/close the connection independently. */
+YASL_API int yasl_wayland_get_native(const yasl_surface *surface, struct yasl_wayland_handles *out);
#ifdef __cplusplus
}
#endif
diff --git a/include/yasl/native/x11.h b/include/yasl/native/x11.h
index f8c37ae..6cc85fb 100644
--- a/include/yasl/native/x11.h
+++ b/include/yasl/native/x11.h
@@ -1,3 +1,4 @@
+/** @file Borrowed x11 handles for an initialized, connected surface. */
#ifndef YASL_NATIVE_X11_H
#define YASL_NATIVE_X11_H
#include "yasl/yasl.h"
@@ -5,9 +6,15 @@ typedef struct _XDisplay Display;
#ifdef __cplusplus
extern "C" {
#endif
-/** Borrowed handles; surface/window lifetime and native connection stay in YASL. */
-YASL_API Display *yasl_x11_display(const yasl_surface *surface);
-YASL_API uintptr_t yasl_x11_window(const yasl_surface *surface);
+struct yasl_x11_handles {
+ Display *display;
+ uintptr_t window;
+};
+/** Owner thread only. Returns -ENOTSUP for another backend and -ENOTCONN
+ * while disconnected. Clears out on failure. Handles expire on display loss
+ * or surface destruction; query again after DISPLAY_RESTORED. Never destroy
+ * borrowed objects or dispatch/close the connection independently. */
+YASL_API int yasl_x11_get_native(const yasl_surface *surface, struct yasl_x11_handles *out);
#ifdef __cplusplus
}
#endif
diff --git a/include/yasl/vulkan.h b/include/yasl/vulkan.h
index afa1089..1a1c5be 100644
--- a/include/yasl/vulkan.h
+++ b/include/yasl/vulkan.h
@@ -1,4 +1,4 @@
-/** @file Vulkan WSI only; application owns all Vulkan objects. */
+/** @file Vulkan WSI; YASL tracks the WSI handle, the application owns rendering. */
#ifndef YASL_VULKAN_H
#define YASL_VULKAN_H
#include "yasl/yasl.h"
@@ -6,13 +6,27 @@
#ifdef __cplusplus
extern "C" {
#endif
-YASL_API bool yasl_vulkan_init(PFN_vkGetInstanceProcAddr loader);
-/** Borrowed list through shutdown; merge with app extensions before instance creation. */
-YASL_API const char *const *yasl_vulkan_instance_extensions(const yasl_surface *surface, uint32_t *count);
-YASL_API VkInstanceCreateFlags yasl_vulkan_instance_flags(const yasl_surface *surface);
-/** Uses injected loader; destroy returned VkSurfaceKHR before YASL surface. */
+YASL_API int yasl_vulkan_init(PFN_vkGetInstanceProcAddr loader);
+struct yasl_vulkan_requirements {
+ const char *const *extensions;
+ uint32_t extension_count;
+ VkInstanceCreateFlags flags;
+};
+/** Initialize the loader first. Borrowed extensions live through shutdown;
+ * merge them with application extensions before creating the instance.
+ * Clears out on failure. */
+YASL_API int yasl_vulkan_get_requirements(const yasl_surface *surface,
+ struct yasl_vulkan_requirements *out);
+/** WSI creation retains VkResult semantics. Uses the injected loader.
+ * One live VkSurfaceKHR per window; destroy it with yasl_vulkan_destroy_surface
+ * before destroying the YASL surface. The application owns swapchains/devices. */
YASL_API VkResult yasl_vulkan_create_surface(yasl_surface *surface, VkInstance instance,
- const VkAllocationCallbacks *allocator, VkSurfaceKHR *out);
+ const VkAllocationCallbacks *allocator,
+ VkSurfaceKHR *out);
+/** Destroy the WSI surface (including while disconnected), releasing the window
+ * for another renderer. Destroy dependent swapchains first. Uses the instance
+ * and allocation callbacks recorded at creation. No live WSI surface is a no-op. */
+YASL_API int yasl_vulkan_destroy_surface(yasl_surface *surface);
#ifdef __cplusplus
}
#endif
diff --git a/include/yasl/yasl.h b/include/yasl/yasl.h
index c0ebb75..284039a 100644
--- a/include/yasl/yasl.h
+++ b/include/yasl/yasl.h
@@ -1,8 +1,11 @@
-/** @file Windows, input and events. The first surface starts the backend; use the
- * creating thread (main on Cocoa) for every call except yasl_wake. Coordinates are
- * logical units unless named pixel. */
+/** @file Windows, input and events.
+ * Commands return 0 on success or a negative errno value on failure. Constructors
+ * return NULL and set errno on failure; data queries document their sentinel.
+ * Use the initializing thread (main on Cocoa), except where stated otherwise.
+ * Coordinates are logical units unless named pixel. */
#ifndef YASL_H
#define YASL_H
+#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@@ -18,56 +21,226 @@ extern "C" {
typedef struct yasl_surface yasl_surface;
typedef struct yasl_event yasl_event;
enum yasl_backend { YASL_BACKEND_AUTO, YASL_BACKEND_WAYLAND, YASL_BACKEND_X11, YASL_BACKEND_COCOA };
-enum yasl_surface_flags { YASL_SURFACE_CPU=1u, YASL_SURFACE_HIDDEN=2u, YASL_SURFACE_UNDECORATED=4u, YASL_SURFACE_FIXED_SIZE=8u };
-enum yasl_error { YASL_ERROR_NONE, YASL_ERROR_INVALID, YASL_ERROR_UNSUPPORTED, YASL_ERROR_NO_MEMORY,
- YASL_ERROR_NOT_INITIALIZED, YASL_ERROR_BUSY, YASL_ERROR_WRONG_THREAD, YASL_ERROR_CONTEXT_EXPIRED, YASL_ERROR_IO, YASL_ERROR_DISCONNECTED };
+enum yasl_surface_type { YASL_SURFACE_NATIVE, YASL_SURFACE_CPU };
+enum yasl_surface_flags {
+ YASL_SURFACE_HIDDEN = 2u,
+ YASL_SURFACE_UNDECORATED = 4u,
+ YASL_SURFACE_FIXED_SIZE = 8u
+};
enum yasl_selection { YASL_CLIPBOARD, YASL_PRIMARY };
enum yasl_key_action { YASL_KEY_PRESS, YASL_KEY_RELEASE, YASL_KEY_REPEAT };
-enum yasl_mod { YASL_MOD_SHIFT=1u, YASL_MOD_ALT=2u, YASL_MOD_CTRL=4u, YASL_MOD_SUPER=8u, YASL_MOD_CAPS=16u };
-enum yasl_pointer_action { YASL_POINTER_MOVE, YASL_POINTER_DOWN, YASL_POINTER_UP, YASL_POINTER_LEAVE };
+enum yasl_mod {
+ YASL_MOD_SHIFT = 1u,
+ YASL_MOD_ALT = 2u,
+ YASL_MOD_CTRL = 4u,
+ YASL_MOD_SUPER = 8u,
+ YASL_MOD_CAPS = 16u
+};
+enum yasl_pointer_action {
+ YASL_POINTER_MOVE,
+ YASL_POINTER_DOWN,
+ YASL_POINTER_UP,
+ YASL_POINTER_LEAVE
+};
enum yasl_pointer_mode { YASL_POINTER_NORMAL, YASL_POINTER_RELATIVE };
-enum yasl_button { YASL_BUTTON_PRIMARY, YASL_BUTTON_MIDDLE, YASL_BUTTON_SECONDARY, YASL_BUTTON_BACK, YASL_BUTTON_FORWARD };
+enum yasl_button {
+ YASL_BUTTON_PRIMARY,
+ YASL_BUTTON_MIDDLE,
+ YASL_BUTTON_SECONDARY,
+ YASL_BUTTON_BACK,
+ YASL_BUTTON_FORWARD
+};
#define YASL_BUTTON_BIT(button) (1u << (button))
-enum yasl_pen_tool { YASL_PEN_UNKNOWN, YASL_PEN_PEN, YASL_PEN_ERASER, YASL_PEN_BRUSH, YASL_PEN_AIRBRUSH, YASL_PEN_PENCIL };
-enum yasl_pen_action { YASL_PEN_ENTER, YASL_PEN_LEAVE, YASL_PEN_MOVE, YASL_PEN_DOWN, YASL_PEN_UP, YASL_PEN_BUTTON, YASL_PEN_CANCEL };
-enum yasl_pen_axis { YASL_PEN_PRESSURE=1u, YASL_PEN_TILT=2u, YASL_PEN_ROTATION=4u, YASL_PEN_DISTANCE=8u, YASL_PEN_TANGENTIAL=16u, YASL_PEN_WHEEL=32u };
-enum yasl_scroll_valid { YASL_SCROLL_LINES=1u, YASL_SCROLL_PIXELS=2u };
-enum yasl_scroll_source { YASL_SCROLL_UNKNOWN, YASL_SCROLL_WHEEL, YASL_SCROLL_FINGER, YASL_SCROLL_CONTINUOUS };
-enum yasl_scroll_phase { YASL_SCROLL_PHASE_UNKNOWN, YASL_SCROLL_BEGIN, YASL_SCROLL_UPDATE, YASL_SCROLL_END };
+enum yasl_pen_tool {
+ YASL_PEN_UNKNOWN,
+ YASL_PEN_PEN,
+ YASL_PEN_ERASER,
+ YASL_PEN_BRUSH,
+ YASL_PEN_AIRBRUSH,
+ YASL_PEN_PENCIL
+};
+enum yasl_pen_action {
+ YASL_PEN_ENTER,
+ YASL_PEN_LEAVE,
+ YASL_PEN_MOVE,
+ YASL_PEN_DOWN,
+ YASL_PEN_UP,
+ YASL_PEN_BUTTON,
+ YASL_PEN_CANCEL
+};
+enum yasl_pen_axis {
+ YASL_PEN_PRESSURE = 1u,
+ YASL_PEN_TILT = 2u,
+ YASL_PEN_ROTATION = 4u,
+ YASL_PEN_DISTANCE = 8u,
+ YASL_PEN_TANGENTIAL = 16u,
+ YASL_PEN_WHEEL = 32u
+};
+enum yasl_scroll_valid { YASL_SCROLL_LINES = 1u, YASL_SCROLL_PIXELS = 2u };
+enum yasl_scroll_source {
+ YASL_SCROLL_UNKNOWN,
+ YASL_SCROLL_WHEEL,
+ YASL_SCROLL_FINGER,
+ YASL_SCROLL_CONTINUOUS
+};
+enum yasl_scroll_phase {
+ YASL_SCROLL_PHASE_UNKNOWN,
+ YASL_SCROLL_BEGIN,
+ YASL_SCROLL_UPDATE,
+ YASL_SCROLL_END
+};
enum yasl_transfer_status { YASL_TRANSFER_OK, YASL_TRANSFER_UNAVAILABLE, YASL_TRANSFER_FAILED };
-enum yasl_text_purpose { YASL_TEXT_NORMAL, YASL_TEXT_PASSWORD, YASL_TEXT_NUMBER, YASL_TEXT_TERMINAL };
-enum yasl_cursor { YASL_CURSOR_ARROW, YASL_CURSOR_TEXT, YASL_CURSOR_LINK, YASL_CURSOR_CROSSHAIR, YASL_CURSOR_MOVE,
- YASL_CURSOR_RESIZE_NWSE, YASL_CURSOR_RESIZE_NESW, YASL_CURSOR_RESIZE_EW, YASL_CURSOR_RESIZE_NS, YASL_CURSOR_HIDDEN };
-enum yasl_surface_state { YASL_STATE_FULLSCREEN=1u, YASL_STATE_MAXIMIZED=2u, YASL_STATE_ACTIVATED=4u,
- YASL_STATE_SUSPENDED=8u, YASL_STATE_CSD=16u, YASL_STATE_TILED=32u };
-enum yasl_feature { YASL_CAP_POPUPS=1u, YASL_CAP_DRAG=2u, YASL_CAP_DRAG_CARRY=4u, YASL_CAP_PLACE_WINDOW=8u,
- YASL_CAP_PRIMARY_SELECTION=16u, YASL_CAP_CSD=32u, YASL_CAP_NATIVE_GRAPHICS=64u, YASL_CAP_FRAME_PACING=128u,
- YASL_CAP_TABLET=256u, YASL_CAP_OPENGL=512u, YASL_CAP_VULKAN=1024u, YASL_CAP_TEXT_INPUT=2048u };
-enum yasl_popup_edge { YASL_POPUP_CENTER, YASL_POPUP_TOP, YASL_POPUP_BOTTOM, YASL_POPUP_LEFT,
- YASL_POPUP_RIGHT, YASL_POPUP_TOP_LEFT, YASL_POPUP_BOTTOM_LEFT, YASL_POPUP_TOP_RIGHT, YASL_POPUP_BOTTOM_RIGHT };
-enum yasl_popup_constraint { YASL_POPUP_SLIDE_X=1u, YASL_POPUP_SLIDE_Y=2u, YASL_POPUP_FLIP_X=4u,
- YASL_POPUP_FLIP_Y=8u, YASL_POPUP_RESIZE_X=16u, YASL_POPUP_RESIZE_Y=32u };
-enum yasl_resize_edge { YASL_EDGE_TOP=1, YASL_EDGE_BOTTOM=2, YASL_EDGE_LEFT=4, YASL_EDGE_TOP_LEFT=5,
- YASL_EDGE_BOTTOM_LEFT=6, YASL_EDGE_RIGHT=8, YASL_EDGE_TOP_RIGHT=9, YASL_EDGE_BOTTOM_RIGHT=10 };
-enum yasl_event_type { YASL_EVENT_NONE, YASL_EVENT_CLOSE, YASL_EVENT_QUIT, YASL_EVENT_REOPEN, YASL_EVENT_WINDOW,
- YASL_EVENT_REFRESH, YASL_EVENT_KEY, YASL_EVENT_TEXT, YASL_EVENT_POINTER, YASL_EVENT_SCROLL, YASL_EVENT_PEN,
- YASL_EVENT_INPUT_RESET, YASL_EVENT_COMPOSITION, YASL_EVENT_TEXT_EDIT, YASL_EVENT_CLIPBOARD, YASL_EVENT_DROP,
- YASL_EVENT_DRAG, YASL_EVENT_DISPLAY_LOST, YASL_EVENT_DISPLAY_RESTORED };
+enum yasl_text_purpose {
+ YASL_TEXT_NORMAL,
+ YASL_TEXT_PASSWORD,
+ YASL_TEXT_NUMBER,
+ YASL_TEXT_TERMINAL
+};
+enum yasl_cursor {
+ YASL_CURSOR_ARROW,
+ YASL_CURSOR_TEXT,
+ YASL_CURSOR_LINK,
+ YASL_CURSOR_CROSSHAIR,
+ YASL_CURSOR_MOVE,
+ YASL_CURSOR_RESIZE_NWSE,
+ YASL_CURSOR_RESIZE_NESW,
+ YASL_CURSOR_RESIZE_EW,
+ YASL_CURSOR_RESIZE_NS,
+ YASL_CURSOR_HIDDEN
+};
+enum yasl_surface_state {
+ YASL_STATE_FULLSCREEN = 1u,
+ YASL_STATE_MAXIMIZED = 2u,
+ YASL_STATE_ACTIVATED = 4u,
+ YASL_STATE_SUSPENDED = 8u,
+ YASL_STATE_CSD = 16u,
+ YASL_STATE_TILED = 32u
+};
+enum yasl_feature {
+ YASL_CAP_POPUPS = 1u,
+ YASL_CAP_DRAG = 2u,
+ YASL_CAP_DRAG_CARRY = 4u,
+ YASL_CAP_PLACE_WINDOW = 8u,
+ YASL_CAP_PRIMARY_SELECTION = 16u,
+ YASL_CAP_CSD = 32u,
+ YASL_CAP_NATIVE_GRAPHICS = 64u,
+ YASL_CAP_FRAME_PACING = 128u,
+ YASL_CAP_TABLET = 256u,
+ YASL_CAP_OPENGL = 512u,
+ YASL_CAP_VULKAN = 1024u,
+ YASL_CAP_TEXT_INPUT = 2048u
+};
+enum yasl_popup_edge {
+ YASL_POPUP_CENTER,
+ YASL_POPUP_TOP,
+ YASL_POPUP_BOTTOM,
+ YASL_POPUP_LEFT,
+ YASL_POPUP_RIGHT,
+ YASL_POPUP_TOP_LEFT,
+ YASL_POPUP_BOTTOM_LEFT,
+ YASL_POPUP_TOP_RIGHT,
+ YASL_POPUP_BOTTOM_RIGHT
+};
+enum yasl_popup_constraint {
+ YASL_POPUP_SLIDE_X = 1u,
+ YASL_POPUP_SLIDE_Y = 2u,
+ YASL_POPUP_FLIP_X = 4u,
+ YASL_POPUP_FLIP_Y = 8u,
+ YASL_POPUP_RESIZE_X = 16u,
+ YASL_POPUP_RESIZE_Y = 32u
+};
+enum yasl_resize_edge {
+ YASL_EDGE_TOP = 1,
+ YASL_EDGE_BOTTOM = 2,
+ YASL_EDGE_LEFT = 4,
+ YASL_EDGE_TOP_LEFT = 5,
+ YASL_EDGE_BOTTOM_LEFT = 6,
+ YASL_EDGE_RIGHT = 8,
+ YASL_EDGE_TOP_RIGHT = 9,
+ YASL_EDGE_BOTTOM_RIGHT = 10
+};
+enum yasl_event_type {
+ YASL_EVENT_NONE,
+ YASL_EVENT_CLOSE,
+ YASL_EVENT_QUIT,
+ YASL_EVENT_REOPEN,
+ YASL_EVENT_WINDOW,
+ YASL_EVENT_REFRESH,
+ YASL_EVENT_KEY,
+ YASL_EVENT_TEXT,
+ YASL_EVENT_POINTER,
+ YASL_EVENT_SCROLL,
+ YASL_EVENT_PEN,
+ YASL_EVENT_INPUT_RESET,
+ YASL_EVENT_COMPOSITION,
+ YASL_EVENT_TEXT_EDIT,
+ YASL_EVENT_CLIPBOARD,
+ YASL_EVENT_DROP,
+ YASL_EVENT_DRAG,
+ YASL_EVENT_DISPLAY_LOST,
+ YASL_EVENT_DISPLAY_RESTORED
+};
enum yasl_drop_action { YASL_DROP_OVER, YASL_DROP_LEAVE, YASL_DROP_DATA };
enum yasl_drag_action { YASL_DRAG_OVER, YASL_DRAG_LEAVE, YASL_DRAG_DROP };
-struct yasl_window_payload { int width,height,pixel_width,pixel_height; double scale; unsigned state; };
-struct yasl_key_payload { enum yasl_key key; enum yasl_key_action action; unsigned mods;
- uint32_t codepoint, shifted, native_code; };
-struct yasl_text_payload { const char *data; size_t length; };
-struct yasl_pointer_payload { double x,y,dx,dy; enum yasl_pointer_action action; unsigned button,buttons,mods; };
-struct yasl_scroll_payload { double x,y,lines_x,lines_y,pixels_x,pixels_y; unsigned valid,source,phase,mods; bool momentum; };
-struct yasl_pen_payload { uint64_t device_id,tool_id; double x,y,pressure,tilt_x,tilt_y,rotation,distance,tangential,wheel_degrees;
- unsigned axes_valid,axes_changed,buttons,changed_button,tool,action; bool in_contact; };
-struct yasl_composition_payload { const char *data; size_t length; int begin,end; bool active; uint64_t revision,generation; };
-struct yasl_text_edit_payload { const char *data; size_t length,begin,end; uint64_t revision,generation; };
-struct yasl_clipboard_payload { const char *data; size_t length; uint64_t request_id; enum yasl_transfer_status status; };
-struct yasl_drop_payload { enum yasl_drop_action action; double x,y; const char *data; size_t length; };
-struct yasl_drag_payload { enum yasl_drag_action action; double x,y; };
+struct yasl_window_payload {
+ int width, height, pixel_width, pixel_height;
+ double scale;
+ unsigned state;
+};
+struct yasl_key_payload {
+ enum yasl_key key;
+ enum yasl_key_action action;
+ unsigned mods;
+ uint32_t codepoint, shifted, native_code;
+};
+struct yasl_text_payload {
+ const char *data;
+ size_t length;
+};
+struct yasl_pointer_payload {
+ double x, y, dx, dy;
+ enum yasl_pointer_action action;
+ unsigned button, buttons, mods;
+};
+struct yasl_scroll_payload {
+ double x, y, lines_x, lines_y, pixels_x, pixels_y;
+ unsigned valid, source, phase, mods;
+ bool momentum;
+};
+struct yasl_pen_payload {
+ uint64_t device_id, tool_id;
+ double x, y, pressure, tilt_x, tilt_y, rotation, distance, tangential, wheel_degrees;
+ unsigned axes_valid, axes_changed, buttons, changed_button, tool, action;
+ bool in_contact;
+};
+struct yasl_composition_payload {
+ const char *data;
+ size_t length;
+ int begin, end;
+ bool active;
+ uint64_t revision, generation;
+};
+struct yasl_text_edit_payload {
+ const char *data;
+ size_t length, begin, end;
+ uint64_t revision, generation;
+};
+struct yasl_clipboard_payload {
+ const char *data;
+ size_t length;
+ uint64_t request_id;
+ enum yasl_transfer_status status;
+};
+struct yasl_drop_payload {
+ enum yasl_drop_action action;
+ double x, y;
+ const char *data;
+ size_t length;
+};
+struct yasl_drag_payload {
+ enum yasl_drag_action action;
+ double x, y;
+};
/** Read the union member named by type. Strings stay valid until the next wait.
* CLOSE: the user closed this window. QUIT: the system asked the app to quit.
@@ -98,69 +271,107 @@ struct yasl_event {
struct yasl_drag_payload drag;
};
};
-/** Last error on this thread; success does not clear it. */
-YASL_API enum yasl_error yasl_get_error(const char **description);
-YASL_API int yasl_get_native_error(void);
-/** Only before the first surface is created. */
-YASL_API bool yasl_set_app_id(const char *app_id);
-YASL_API bool yasl_set_backend(enum yasl_backend backend);
-/** Flags zero creates a surface for your own Vulkan or Metal renderer. */
-YASL_API yasl_surface *yasl_surface_create(const char *title, int width, int height, unsigned flags);
-/** Also destroys its popups. */
-YASL_API void yasl_surface_destroy(yasl_surface *surface);
-/** Destroys all surfaces. Release your GPU objects first. */
-YASL_API void yasl_shutdown(void);
-/** 1 event, 0 none (timeout or wake), -1 failure. timeout_ms zero polls, negative
+/** NULL options select the automatic backend and no application ID. */
+struct yasl_options {
+ enum yasl_backend backend;
+ const char *app_id;
+};
+/** Zero initialization selects a native GPU window. A positive logical size is
+ * required; title may be NULL. Strings are copied before creation returns. */
+struct yasl_surface_options {
+ const char *title;
+ int width, height;
+ enum yasl_surface_type type;
+ unsigned flags;
+ void *userdata;
+};
+/** Optional explicit initialization. Creation initializes with defaults when
+ * needed. Reinitialization returns -EALREADY; failed initialization is retryable. */
+YASL_API int yasl_init(const struct yasl_options *options);
+YASL_API yasl_surface *yasl_surface_create(const struct yasl_surface_options *options);
+/** Also destroys its popups. NULL is a no-op. Returns -EBUSY while a window
+ * in the subtree owns a Vulkan surface; destroy that WSI surface first. */
+YASL_API int yasl_surface_destroy(yasl_surface *surface);
+/** Destroys all surfaces. Returns -EBUSY without destroying any windows if
+ * a Vulkan surface remains live. Release your GPU objects first. */
+YASL_API int yasl_shutdown(void);
+/** 1 event, 0 none (timeout or wake), negative errno on failure. timeout_ms zero polls, negative
* waits forever. Without an event, out->type is NONE. */
YASL_API int yasl_wait_event(yasl_event *out, int timeout_ms);
/** Makes a waiting yasl_wait_event return. Thread-safe. */
YASL_API void yasl_wake(void);
+/** Monotonic nanoseconds; thread-safe and independent of initialization. */
YASL_API uint64_t yasl_time_ns(void);
-YASL_API enum yasl_backend yasl_surface_backend(const yasl_surface *surface);
-YASL_API unsigned yasl_surface_features(const yasl_surface *surface);
+/** Queries accept NULL: return AUTO, zero, NULL or scale 1; size outputs are
+ * optional and become zero. Non-NULL surfaces must belong to the owner thread. */
+YASL_API enum yasl_backend yasl_surface_get_backend(const yasl_surface *surface);
+YASL_API unsigned yasl_surface_get_features(const yasl_surface *surface);
YASL_API void yasl_surface_get_size(const yasl_surface *surface, int *width, int *height);
-YASL_API void yasl_surface_get_framebuffer_size(const yasl_surface *surface, int *width, int *height);
+YASL_API void yasl_surface_get_framebuffer_size(const yasl_surface *surface, int *width,
+ int *height);
YASL_API double yasl_surface_get_scale(const yasl_surface *surface);
-YASL_API unsigned yasl_surface_state(const yasl_surface *surface);
-YASL_API void *yasl_surface_userdata(const yasl_surface *surface);
-YASL_API void yasl_surface_set_userdata(yasl_surface *surface, void *userdata);
-YASL_API bool yasl_surface_set_title(yasl_surface *surface, const char *title);
+YASL_API unsigned yasl_surface_get_state(const yasl_surface *surface);
+YASL_API void *yasl_surface_get_userdata(const yasl_surface *surface);
+YASL_API int yasl_surface_set_userdata(yasl_surface *surface, void *userdata);
+YASL_API int yasl_surface_set_title(yasl_surface *surface, const char *title);
/** Requests; the result arrives as a WINDOW event. */
-YASL_API bool yasl_surface_request_size(yasl_surface *surface, int width, int height);
-YASL_API bool yasl_surface_request_fullscreen(yasl_surface *surface, bool enabled);
-YASL_API bool yasl_surface_request_maximized(yasl_surface *surface, bool enabled);
-YASL_API bool yasl_surface_request_minimize(yasl_surface *surface);
-YASL_API bool yasl_surface_set_cursor(yasl_surface *surface, enum yasl_cursor cursor);
-YASL_API bool yasl_surface_set_pointer_mode(yasl_surface *surface, enum yasl_pointer_mode mode);
+YASL_API int yasl_surface_request_size(yasl_surface *surface, int width, int height);
+YASL_API int yasl_surface_request_fullscreen(yasl_surface *surface, bool enabled);
+YASL_API int yasl_surface_request_maximized(yasl_surface *surface, bool enabled);
+YASL_API int yasl_surface_request_minimize(yasl_surface *surface);
+YASL_API int yasl_surface_set_cursor(yasl_surface *surface, enum yasl_cursor cursor);
+YASL_API int yasl_surface_set_pointer_mode(yasl_surface *surface, enum yasl_pointer_mode mode);
/** Draws during native modal loops such as a live resize. The callback may render
* but must not wait, create or destroy surfaces, or shut down. */
-YASL_API void yasl_surface_set_refresh_callback(yasl_surface *surface, void (*refresh)(yasl_surface *,void *), void *userdata);
-YASL_API int yasl_double_click_ms(void);
-YASL_API bool yasl_clipboard_set(enum yasl_selection selection, const char *text);
+YASL_API int yasl_surface_set_refresh_callback(yasl_surface *surface,
+ void (*refresh)(yasl_surface *, void *),
+ void *userdata);
+YASL_API int yasl_get_double_click_ms(void);
+YASL_API int yasl_clipboard_set(enum yasl_selection selection, const char *text);
/** Returns a request ID; the text arrives as a CLIPBOARD event. Zero on failure. */
YASL_API uint64_t yasl_clipboard_request(yasl_surface *target, enum yasl_selection selection);
/** IME support for a text field. */
-YASL_API bool yasl_surface_set_text_input(yasl_surface *surface, bool active, enum yasl_text_purpose purpose);
+YASL_API int yasl_surface_set_text_input(yasl_surface *surface, bool active,
+ enum yasl_text_purpose purpose);
/** UTF-8; selection offsets are bytes. */
-YASL_API bool yasl_surface_set_surrounding_text(yasl_surface *surface, const char *text, size_t length,
- size_t selection_begin, size_t selection_end, uint64_t revision);
-YASL_API bool yasl_surface_set_ime_caret(yasl_surface *surface, double x, double y, double width, double height);
-/** Anchor picks a point on the x/y/anchor rectangle of the parent; gravity is the
- * direction the popup grows. Functions taking a trigger need a recent press event. */
-YASL_API yasl_surface *yasl_popup_create(yasl_surface *parent, int width, int height,
- double x, double y, double anchor_width, double anchor_height, enum yasl_popup_edge anchor,
- enum yasl_popup_edge gravity, unsigned constraints, const yasl_event *trigger);
-YASL_API bool yasl_popup_reposition(yasl_surface *popup, int width, int height,
- double x, double y, double anchor_width, double anchor_height, enum yasl_popup_edge anchor,
- enum yasl_popup_edge gravity, unsigned constraints);
-YASL_API bool yasl_surface_start_interactive_move(yasl_surface *surface, const yasl_event *trigger);
-YASL_API bool yasl_surface_start_interactive_resize(yasl_surface *surface, enum yasl_resize_edge edge, const yasl_event *trigger);
-YASL_API bool yasl_surface_show_window_menu(yasl_surface *surface, double x, double y, const yasl_event *trigger);
-YASL_API bool yasl_drag_start(yasl_surface *origin, const yasl_event *trigger,
- const uint32_t *pixels, int width, int height, int hotspot_x, int hotspot_y);
-YASL_API void yasl_drag_cancel(void);
-YASL_API bool yasl_surface_drag_carry(yasl_surface *surface, double x, double y);
-YASL_API bool yasl_surface_place_under_pointer(yasl_surface *surface, double x, double y);
+YASL_API int yasl_surface_set_surrounding_text(yasl_surface *surface, const char *text,
+ size_t length, size_t selection_begin,
+ size_t selection_end, uint64_t revision);
+YASL_API int yasl_surface_set_ime_caret(yasl_surface *surface, double x, double y, double width,
+ double height);
+/** Anchor selects a point on the parent rectangle; gravity is the direction
+ * the popup grows. Zero edges mean center. All dimensions are logical units. */
+struct yasl_popup_options {
+ int width, height;
+ double x, y, anchor_width, anchor_height;
+ enum yasl_popup_edge anchor, gravity;
+ unsigned constraints;
+};
+/** Trigger must be a recent pointer/pen press on parent. */
+YASL_API yasl_surface *yasl_popup_create(yasl_surface *parent,
+ const struct yasl_popup_options *options,
+ const yasl_event *trigger);
+YASL_API int yasl_popup_reposition(yasl_surface *popup, const struct yasl_popup_options *options);
+YASL_API int yasl_surface_start_interactive_move(yasl_surface *surface, const yasl_event *trigger);
+YASL_API int yasl_surface_start_interactive_resize(yasl_surface *surface,
+ enum yasl_resize_edge edge,
+ const yasl_event *trigger);
+YASL_API int yasl_surface_show_window_menu(yasl_surface *surface, double x, double y,
+ const yasl_event *trigger);
+/** Optional premultiplied ARGB image in device pixels. Copied by drag_start. */
+struct yasl_drag_image {
+ const uint32_t *pixels;
+ int width, height;
+ int hotspot_x, hotspot_y;
+};
+YASL_API int yasl_drag_start(yasl_surface *origin, const yasl_event *trigger,
+ const struct yasl_drag_image *image);
+YASL_API int yasl_drag_cancel(void);
+YASL_API int yasl_surface_drag_carry(yasl_surface *surface, double x, double y);
+YASL_API int yasl_surface_place_under_pointer(yasl_surface *surface, double x, double y);
+/** Parse local file URIs. Returns byte length excluding NUL, zero at end.
+ * If capacity <= length, leaves cursor/output unchanged for retry. NULL/zero
+ * queries size. The caller owns the cursor string; this helper is thread-safe. */
YASL_API size_t yasl_drop_next_path(const char **cursor, char *out, size_t capacity);
#ifdef __cplusplus
}
diff --git a/src/common/backend.h b/src/common/backend.h
index 6eefbb3..246a9bc 100644
--- a/src/common/backend.h
+++ b/src/common/backend.h
@@ -25,9 +25,14 @@ struct yasl_private_display {
};
struct yasl_surface {
+ struct yasl_vulkan_binding *vulkan;
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;
/* Window values last reported through public WINDOW events. */
diff --git a/src/common/desktop.c b/src/common/desktop.c
index 5045604..a9f72e3 100644
--- a/src/common/desktop.c
+++ b/src/common/desktop.c
@@ -11,17 +11,20 @@
static bool uri_valid(const char *uri)
{
unsigned char c = (unsigned char)uri[0];
- if ((unsigned)((c | 32) - 'a') >= 26) return false;
+ if ((unsigned)((c | 32) - 'a') >= 26)
+ return false;
const char *p = uri + 1;
for (; *p != ':'; p++) {
c = (unsigned char)*p;
bool alpha = (unsigned)((c | 32) - 'a') < 26;
bool digit = (unsigned)(c - '0') < 10;
- if (!alpha && !digit && c != '+' && c != '-' && c != '.') return false;
+ if (!alpha && !digit && c != '+' && c != '-' && c != '.')
+ return false;
}
for (; *p; p++) {
c = (unsigned char)*p;
- if (c < 0x20 || c == 0x7f) return false;
+ if (c < 0x20 || c == 0x7f)
+ return false;
}
return true;
}
@@ -38,13 +41,16 @@ static size_t copy_out(char *out, size_t cap, const char *s, size_t len)
bool yasl_private_display_open_uri(struct yasl_private_display *d, const char *uri)
{
- if (!d || !uri || !uri_valid(uri)) return false;
+ if (!d || !uri || !uri_valid(uri))
+ return false;
return desktop_open_uri(uri);
}
-bool yasl_private_display_notify(struct yasl_private_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;
+ if (!d || !title)
+ return false;
return desktop_notify(d->app_id, title, body ? body : "");
}
@@ -55,27 +61,31 @@ uint32_t yasl_private_display_appearance(struct yasl_private_display *d)
bool yasl_private_surface_bell(struct yasl_surface *s)
{
- if (!s || !s->display->ops->surface_bell) return false;
+ if (!s || !s->display->ops->surface_bell)
+ return false;
return s->display->ops->surface_bell(s);
}
size_t yasl_private_executable_path(char *out, size_t cap)
{
- if (!out && cap) return 0;
+ 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_private_resource_dir(char *out, size_t cap)
{
- if (!out && cap) return 0;
+ 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_private_default_locale(char *out, size_t cap)
{
- if (!out && cap) return 0;
+ if (!out && cap)
+ return 0;
char buf[DESKTOP_STR_MAX];
return copy_out(out, cap, buf, desktop_default_locale(buf, sizeof(buf)));
}
@@ -83,13 +93,69 @@ size_t yasl_private_default_locale(char *out, size_t cap)
#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); }
+int yasl_open_uri(const char *uri)
+{
+ if (!uri || !uri_valid(uri))
+ return yasl_public_error(EINVAL);
+ errno = 0;
+ return yasl_public_status(desktop_open_uri(uri) || yasl_public_fail(errno));
+}
+int yasl_notify(const char *title, const char *body)
+{
+ if (!yasl_public_owner())
+ return -errno;
+ if (!title)
+ return yasl_public_error(EINVAL);
+ errno = 0;
+ return yasl_public_status(yasl_private_display_notify(yasl_public_display(), title, body) ||
+ yasl_public_fail(errno));
+}
+unsigned yasl_get_appearance(void)
+{
+ return yasl_public_owner() ? yasl_private_display_appearance(yasl_public_display()) : 0;
+}
+int yasl_surface_bell(yasl_surface *s)
+{
+ if (!yasl_public_surface(s, false))
+ 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)
+{
+ 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;
+ }
+ errno = 0;
+ size_t n = yasl_private_default_locale(out, cap);
+ if (!n)
+ yasl_public_fail(errno);
+ return n;
+}
diff --git a/src/common/native.c b/src/common/native.c
index 88604c9..1b32094 100644
--- a/src/common/native.c
+++ b/src/common/native.c
@@ -4,22 +4,28 @@
void *yasl_private_display_get_native_handle(struct yasl_private_display *d)
{
- if (!d) return NULL;
- if (d->native_handle) return d->native_handle;
- return d->ops && d->ops->display_get_native_handle ?
- d->ops->display_get_native_handle(d) : NULL;
+ if (!d)
+ return NULL;
+ if (d->native_handle)
+ return d->native_handle;
+ return d->ops && d->ops->display_get_native_handle ? d->ops->display_get_native_handle(d)
+ : NULL;
}
void *yasl_private_surface_get_native_handle(struct yasl_surface *s)
{
- if (!s) return NULL;
- if (s->native_handle) return s->native_handle;
- return s->display && s->display->ops && s->display->ops->surface_get_native_handle ?
- s->display->ops->surface_get_native_handle(s) : NULL;
+ if (!s)
+ return NULL;
+ if (s->native_handle)
+ return s->native_handle;
+ return s->display && s->display->ops && s->display->ops->surface_get_native_handle
+ ? s->display->ops->surface_get_native_handle(s)
+ : NULL;
}
struct yasl_surface *yasl_private_surface_create_native(struct yasl_private_display *d,
- const struct yasl_private_window_spec *spec, void *graphics_device)
+ const struct yasl_private_window_spec *spec,
+ void *graphics_device)
{
if (!graphics_device) {
errno = EINVAL;
@@ -29,10 +35,10 @@ struct yasl_surface *yasl_private_surface_create_native(struct yasl_private_disp
}
bool yasl_private_surface_get_native_frame(struct yasl_surface *s,
- const struct yasl_private_framebuffer *target, struct yasl_private_native_frame *out)
+ const struct yasl_private_framebuffer *target,
+ struct yasl_private_native_frame *out)
{
- if (!s || !target || !out || !target->generation ||
- target->generation != s->frame_generation ||
+ if (!s || !target || !out || !target->generation || target->generation != s->frame_generation ||
!s->display->ops->surface_get_native_frame)
return false;
return s->display->ops->surface_get_native_frame(s, target, out);
@@ -40,36 +46,83 @@ 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"
-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; }
+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"
-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; }
+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"
-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); }
+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
-CAMetalLayer *yasl_metal_layer(yasl_surface *s)
+int yasl_metal_get_layer(yasl_surface *s, CAMetalLayer **out)
{
- 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;
+ if (!out)
+ return yasl_public_error(EINVAL);
+ *out = NULL;
+ if (!yasl_public_surface(s, false))
+ return -errno;
+ if (s->display->offline)
+ return yasl_public_error(ENOTCONN);
+ 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);
+ if (!layer)
+ return yasl_public_error(ENOTSUP);
+ s->presenter = YASL_PRIVATE_PRESENTER_METAL;
+ *out = layer;
+ return 0;
}
diff --git a/src/common/public_internal.h b/src/common/public_internal.h
index 210b222..95ecb6d 100644
--- a/src/common/public_internal.h
+++ b/src/common/public_internal.h
@@ -6,7 +6,8 @@ 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);
+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 b47fea7..de9e7b3 100644
--- a/src/common/surface.c
+++ b/src/common/surface.c
@@ -11,514 +11,1155 @@
#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;
- else if (error==ENOTCONN) code=YASL_ERROR_DISCONNECTED;
- 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","Display disconnected"};
- 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; }
+ errno = error > 0 ? error : EIO;
+ return false;
+}
+int yasl_public_error(int error)
+{
+ yasl_public_fail(error);
+ return -errno;
+}
+int yasl_public_status(bool success)
+{
+ return success ? 0 : yasl_public_error(errno);
+}
+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);
+ if (!display)
+ return yasl_public_fail(ENODEV);
+ if (!pthread_equal(owner, pthread_self()))
+ return yasl_public_fail(EPERM);
return true;
}
-bool yasl_public_surface(yasl_surface *s,bool mutation)
+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);
+ 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)
+int yasl_init(const struct yasl_options *options)
{
- 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);
+ 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)
+ return yasl_public_error(EINVAL);
+ struct yasl_private_display_spec spec = {
+ .app_id = options->app_id,
+ .backend = (enum yasl_private_backend)options->backend,
+ };
+ errno = 0;
+ struct yasl_private_display *d = yasl_private_display_open(NULL, &spec);
+ if (!d)
+ return yasl_public_error(errno);
pthread_mutex_lock(&lifecycle_mutex);
- owner=pthread_self(); display=d;
+ 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;
+ return 0;
}
-bool yasl_set_backend(enum yasl_backend backend)
+static bool initialize(void)
{
- 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;
+ return display ? yasl_public_owner() : yasl_init(NULL) == 0;
}
-static bool creation_valid(int width,int height,unsigned flags)
+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))
+ 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);
+ 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)
+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={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;
+ 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_PRIVATE_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 struct yasl_surface_options *options)
+{
+ if (!options || (unsigned)options->type > YASL_SURFACE_CPU || (options->flags & ~14u)) {
+ yasl_public_fail(EINVAL);
+ return 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))
+ return NULL;
+ bool fresh = !display;
+ if (!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);
+ 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);
+ s->userdata = options->userdata;
+ return s;
+}
+int yasl_surface_destroy(yasl_surface *s)
+{
+ if (!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 *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) {
+ 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;
+ free(s->cpu_pixels);
+ s->cpu_pixels = NULL;
+ free(s->committed_pixels);
+ s->committed_pixels = NULL;
+ free(s->text_surrounding);
+ s->text_surrounding = NULL;
yasl_private_surface_destroy(s);
+ return 0;
}
-void yasl_shutdown(void)
+int 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);
+ 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;
- free(application_id); application_id=NULL; requested_backend=YASL_BACKEND_AUTO;
+ 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);
+ 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(); }
+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;
+{
+ 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)
+{
+ 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;
+ 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_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;
+ 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;
+}
+void *yasl_surface_get_userdata(const yasl_surface *s)
+{
+ return s && yasl_public_surface((yasl_surface *)s, false) ? s->userdata : NULL;
+}
+int yasl_surface_set_userdata(yasl_surface *s, void *ud)
+{
+ if (!yasl_public_surface(s, false))
+ return -errno;
+ s->userdata = ud;
+ return 0;
+}
+int yasl_surface_set_title(yasl_surface *s, const char *title)
+{
+ if (!yasl_public_surface(s, false))
+ return -errno;
+ if (!title)
+ return yasl_public_error(EINVAL);
+ if (!s->display->ops->surface_set_title)
+ return yasl_public_error(ENOTSUP);
+ 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)); \
+ }
+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))
+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)
+ return yasl_public_error(EINVAL);
+ 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) {
- ++refresh_depth; s->refresh_callback(s,s->refresh_userdata); --refresh_depth;
+ 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)
+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;
+ 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)
+static const char *deliver_text(const char *data, size_t length)
{
- free(delivered_text); delivered_text=copy_text(data,length); return delivered_text;
+ 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)
+/* 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;
+ 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_DISPLAY_RESTORED:e->type=YASL_EVENT_DISPLAY_RESTORED;break;
+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_DISPLAY_RESTORED:
+ e->type = YASL_EVENT_DISPLAY_RESTORED;
+ 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;
+ 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);
+ 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);
+ 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);
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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;
+ 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);
+ 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;
+ 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;
+ 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;
+ 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;
- }
+ 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;
+ 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; }
+ errno = 0;
+ if (convert_event(&p, &event)) {
+ *out = event;
+ return 1;
+ }
+ if (errno) {
+ yasl_public_fail(errno);
+ return -errno;
+ }
}
return 0;
}
-int yasl_wait_event(yasl_event *out,int timeout)
+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;
+ 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=-1; }
+ n = take_event(out);
+ if (!n) {
+ int work = yasl_private_display_dispatch(display, timeout);
+ if (work < 0) {
+ yasl_public_fail(errno);
+ n = -errno;
+ }
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; }
+ if (n >= 0)
+ n = take_event(out);
+ }
+ pumping = false;
+ return n;
+}
+static uint64_t cpu_sequence;
+int yasl_cpu_acquire(yasl_surface *s, struct yasl_cpu_buffer *out)
+{
+ 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)
+{
+ 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); }
-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;
+ 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_surrounding_text(yasl_surface *s,const char *text,size_t len,size_t begin,size_t end,uint64_t revision)
+int yasl_surface_set_text_input(yasl_surface *s, bool active, enum yasl_text_purpose purpose)
{
- 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);
+ 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;
+}
+int 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 -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_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;
+ 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;
}
- 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;
+ free(old);
+ return 0;
}
-static bool logical_rect(yasl_surface *s,double x,double y,double w,double h,struct yasl_private_rect2i *r)
+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;
+ 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;
}
-bool yasl_surface_set_ime_caret(yasl_surface *s,double x,double y,double w,double h)
+int yasl_surface_set_ime_caret(yasl_surface *s, double x, double y, double w, double h)
{
- if(!yasl_public_surface(s,false))return false;
+ if (!yasl_public_surface(s, false))
+ return -errno;
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;
+ 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)
+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;
+ 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)
+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};
- return logical_rect(s,x,y,aw,ah,&out->anchor);
+ 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,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)
+yasl_surface *yasl_popup_create(yasl_surface *parent, const struct yasl_popup_options *options,
+ 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;
+ 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);
+ 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)
+int yasl_popup_reposition(yasl_surface *s, const struct yasl_popup_options *options)
{
- if(!yasl_public_surface(s,false))return false;
+ 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))
+ return -errno;
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); }
+ 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())
+ return -errno;
+ yasl_private_display_drag_cancel(display);
+ 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 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={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_surface *yasl_gl_surface_create(const struct yasl_surface_options *options,
+ const struct yasl_gl_options *gl)
+{
+ 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;
+}
+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 (!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));
}
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;
+ 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;
+ }
+ 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)
+int yasl_gl_set_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);
+ 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));
}
-bool yasl_gl_swap_buffers(yasl_surface *s)
+int yasl_gl_present(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);
+ 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));
}
diff --git a/src/common/vulkan.c b/src/common/vulkan.c
index 62bc3a0..77a4dd4 100644
--- a/src/common/vulkan.c
+++ b/src/common/vulkan.c
@@ -20,69 +20,202 @@
#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)
+void yasl_private_vulkan_reset(void)
{
- 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);
+ get_proc = NULL;
+ portability = false;
}
-const char *const *yasl_vulkan_instance_extensions(const yasl_surface *s,uint32_t *count)
+struct yasl_vulkan_binding {
+ VkInstance instance;
+ VkSurfaceKHR surface;
+ VkAllocationCallbacks allocator;
+ bool has_allocator;
+ PFN_vkDestroySurfaceKHR destroy;
+};
+int yasl_vulkan_init(PFN_vkGetInstanceProcAddr loader)
{
- 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;
+ struct yasl_private_display *d = yasl_public_display();
+ if (d && !yasl_public_owner())
+ return -errno;
+ 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);
+ PFN_vkEnumerateInstanceExtensionProperties enumerate =
+ (PFN_vkEnumerateInstanceExtensionProperties)loader(
+ VK_NULL_HANDLE, "vkEnumerateInstanceExtensionProperties");
+ if (!enumerate)
+ 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)
+ 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;
+ 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;
+}
+int yasl_vulkan_destroy_surface(yasl_surface *s)
+{
+ if (!yasl_public_surface(s, true))
+ return -errno;
+ struct yasl_vulkan_binding *binding = s->vulkan;
+ if (!binding)
+ return 0;
+ binding->destroy(binding->instance, binding->surface,
+ binding->has_allocator ? &binding->allocator : NULL);
+ free(binding);
+ s->vulkan = NULL;
+ s->presenter = YASL_PRIVATE_PRESENTER_NONE;
+ return 0;
}
-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)
+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->display->offline){yasl_public_fail(ENOTCONN);return VK_ERROR_SURFACE_LOST_KHR;}
- 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 (out)
+ *out = VK_NULL_HANDLE;
+ if (!yasl_public_surface(s, true))
+ return VK_ERROR_INITIALIZATION_FAILED;
+ if (!instance || !out) {
+ yasl_public_fail(EINVAL);
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+ if (!get_proc) {
+ yasl_public_fail(ENODEV);
+ return VK_ERROR_INITIALIZATION_FAILED;
+ }
+ if (s->display->offline) {
+ yasl_public_fail(ENOTCONN);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ if (s->vulkan || s->presenter != YASL_PRIVATE_PRESENTER_NONE) {
+ yasl_public_fail(EBUSY);
+ return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;
+ }
+ PFN_vkDestroySurfaceKHR destroy =
+ (PFN_vkDestroySurfaceKHR)get_proc(instance, "vkDestroySurfaceKHR");
+ if (!destroy) {
+ yasl_public_fail(ENOTSUP);
+ return VK_ERROR_EXTENSION_NOT_PRESENT;
+ }
+ struct yasl_vulkan_binding *binding = calloc(1, sizeof(*binding));
+ if (!binding) {
+ yasl_public_fail(ENOMEM);
+ return VK_ERROR_OUT_OF_HOST_MEMORY;
+ }
+ 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;
+ struct yasl_wayland_handles native;
+ if (yasl_wayland_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};
+ 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;
+ struct yasl_x11_handles native;
+ if (yasl_x11_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};
+ 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;
+ 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;
+ default:
+ break;
+ }
+ if (result == VK_SUCCESS) {
+ binding->instance = instance;
+ binding->surface = *out;
+ binding->destroy = destroy;
+ binding->has_allocator = allocator != NULL;
+ if (allocator)
+ binding->allocator = *allocator;
+ s->vulkan = binding;
+ s->presenter = YASL_PRIVATE_PRESENTER_VULKAN;
+ } else {
+ free(binding);
+ yasl_public_fail(result == VK_ERROR_EXTENSION_NOT_PRESENT ? ENOTSUP : EIO);
}
- 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/wayland/backend.c b/src/platform/wayland/backend.c
index 7ac1c65..6b7758f 100644
--- a/src/platform/wayland/backend.c
+++ b/src/platform/wayland/backend.c
@@ -434,6 +434,11 @@ static void *impl_surface_get_native_handle(struct yasl_surface *s)
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,
diff --git a/tests/common/test_gl.c b/tests/common/test_gl.c
index cd51575..0e4597b 100644
--- a/tests/common/test_gl.c
+++ b/tests/common/test_gl.c
@@ -27,9 +27,10 @@ static yasl_surface *cpu;
static void fail(const char *message, int line)
{
const char *description = NULL;
- enum yasl_error error = yasl_get_error(&description);
+ int error = errno;
+ description = strerror(error);
fprintf(stderr, "GL integration failure at line %d: %s (YASL error %d: %s, native error %d)\n",
- line, message, error, description ? description : "none", yasl_get_native_error());
+ line, message, error, description ? description : "none", errno);
(void)yasl_gl_make_current(NULL);
if (cpu) yasl_surface_destroy(cpu);
if (shared) yasl_surface_destroy(shared);
@@ -74,13 +75,13 @@ static void wait_for_size(yasl_surface *surface, int width, int height)
int main(int argc, char **argv)
{
REQUIRE(argc == 2, "expected backend argument: wayland or x11");
- if (!strcmp(argv[1], "wayland")) REQUIRE(yasl_set_backend(YASL_BACKEND_WAYLAND), "select Wayland backend");
- else if (!strcmp(argv[1], "x11")) REQUIRE(yasl_set_backend(YASL_BACKEND_X11), "select X11 backend");
+ if (!strcmp(argv[1], "wayland")) REQUIRE((yasl_init(&(struct yasl_options){.backend=YASL_BACKEND_WAYLAND}) == 0), "select Wayland backend");
+ else if (!strcmp(argv[1], "x11")) REQUIRE((yasl_init(&(struct yasl_options){.backend=YASL_BACKEND_X11}) == 0), "select X11 backend");
else REQUIRE(false, "backend must be wayland or x11");
- primary = yasl_gl_surface_create("YASL GL test", 320, 240, 0, NULL, NULL);
+ primary = yasl_gl_surface_create(&(struct yasl_surface_options){.title="YASL GL test", .width=320, .height=240, .type=YASL_SURFACE_NATIVE, .flags=0}, NULL);
REQUIRE(primary != NULL, "create default OpenGL 3.2 surface");
- REQUIRE(yasl_gl_make_current(primary), "make first GL context current");
+ REQUIRE((yasl_gl_make_current(primary) == 0), "make first GL context current");
get_string_fn get_string = NULL;
clear_color_fn clear_color = NULL;
@@ -114,22 +115,24 @@ int main(int argc, char **argv)
REQUIRE(abs((int)pixel[0] - 51) <= 3 && abs((int)pixel[1] - 102) <= 3 &&
abs((int)pixel[2] - 204) <= 3 && pixel[3] >= 250,
"read rendered pixel from the native GL framebuffer");
- REQUIRE(yasl_gl_swap_interval(primary, 0), "disable swap interval");
- REQUIRE(yasl_gl_swap_buffers(primary), "swap first GL surface");
+ REQUIRE((yasl_gl_set_swap_interval(primary, 0) == 0), "disable swap interval");
+ REQUIRE((yasl_gl_present(primary) == 0), "swap first GL surface");
unsigned texture = 0;
gen_textures(1, &texture);
bind_texture(GL_TEXTURE_2D_, texture);
REQUIRE(is_texture(texture), "create a shareable GL texture");
- shared = yasl_gl_surface_create("YASL GL shared context", 160, 120, 0, NULL, primary);
+ struct yasl_gl_options sharing = YASL_GL_OPTIONS_INIT;
+ sharing.share = primary;
+ shared = yasl_gl_surface_create(&(struct yasl_surface_options){.title="YASL GL shared context", .width=160, .height=120, .type=YASL_SURFACE_NATIVE, .flags=0}, &sharing);
REQUIRE(shared != NULL, "create shared GL context");
- REQUIRE(yasl_gl_make_current(shared), "make shared context current");
+ REQUIRE((yasl_gl_make_current(shared) == 0), "make shared context current");
REQUIRE(is_texture(texture), "verify context shares GL objects");
delete_textures(1, &texture);
- REQUIRE(yasl_gl_make_current(primary), "restore primary GL context");
+ REQUIRE((yasl_gl_make_current(primary) == 0), "restore primary GL context");
- REQUIRE(yasl_surface_request_size(primary, 400, 300), "request surface resize");
+ REQUIRE((yasl_surface_request_size(primary, 400, 300) == 0), "request surface resize");
wait_for_size(primary, 400, 300);
yasl_surface_get_framebuffer_size(primary, &pixel_width, &pixel_height);
double scale = yasl_surface_get_scale(primary);
@@ -138,18 +141,19 @@ int main(int argc, char **argv)
"query resized framebuffer dimensions");
clear_color(0.7f, 0.1f, 0.3f, 1.0f);
clear(GL_COLOR_BUFFER_BIT_);
- REQUIRE(yasl_gl_swap_buffers(primary), "swap resized GL surface");
+ REQUIRE((yasl_gl_present(primary) == 0), "swap resized GL surface");
- const int invalid_attributes[] = {0x7fff, 1, YASL_GL_END};
- REQUIRE(yasl_gl_surface_create("invalid attrs", 100, 100, 0, invalid_attributes, NULL) == NULL,
- "reject unknown GL context attributes");
- REQUIRE(yasl_gl_surface_create("invalid CPU GL", 100, 100, YASL_SURFACE_CPU, NULL, NULL) == NULL,
+ struct yasl_gl_options invalid_options = YASL_GL_OPTIONS_INIT;
+ invalid_options.major = -1;
+ REQUIRE(yasl_gl_surface_create(&(struct yasl_surface_options){.title="invalid attrs", .width=100, .height=100, .type=YASL_SURFACE_NATIVE, .flags=0}, &invalid_options) == NULL,
+ "reject invalid GL context options");
+ REQUIRE(yasl_gl_surface_create(&(struct yasl_surface_options){.title="invalid CPU GL", .width=100, .height=100, .type=YASL_SURFACE_CPU, .flags=0}, NULL) == NULL,
"reject CPU flag on GL surface creation");
- cpu = yasl_surface_create("CPU surface", 100, 100, YASL_SURFACE_CPU);
+ cpu = yasl_surface_create(&(struct yasl_surface_options){.title="CPU surface", .width=100, .height=100, .type=YASL_SURFACE_CPU, .flags=0});
REQUIRE(cpu != NULL, "create CPU surface for incompatibility check");
- REQUIRE(!yasl_gl_make_current(cpu), "reject CPU surface as a GL context");
+ REQUIRE((yasl_gl_make_current(cpu) < 0), "reject CPU surface as a GL context");
- REQUIRE(yasl_gl_make_current(NULL), "detach GL context before destruction");
+ REQUIRE((yasl_gl_make_current(NULL) == 0), "detach GL context before destruction");
yasl_surface_destroy(cpu); cpu = NULL;
yasl_surface_destroy(shared); shared = NULL;
yasl_surface_destroy(primary); primary = NULL;
diff --git a/tests/common/test_surface_integration.c b/tests/common/test_surface_integration.c
index 7cc938a..1f18f23 100644
--- a/tests/common/test_surface_integration.c
+++ b/tests/common/test_surface_integration.c
@@ -2,6 +2,16 @@
#include <yasl/yasl.h>
#include <yasl/cpu.h>
#include <yasl/gl.h>
+#include <yasl/metal.h>
+#if YASL_HAS_WAYLAND
+#include <yasl/native/wayland.h>
+#endif
+#if YASL_HAS_X11
+#include <yasl/native/x11.h>
+#endif
+#if YASL_HAS_COCOA
+#include <yasl/native/cocoa.h>
+#endif
#include "common/backend.h"
#include <assert.h>
#include <pthread.h>
@@ -14,95 +24,241 @@ void yasl_test_frame_tick(struct yasl_surface *surface);
#endif
static void drain(void)
{
- yasl_event e;int n=0;
- while(yasl_wait_event(&e,0)>0)assert(++n<1000);
+ yasl_event e;
+ int n = 0;
+ while (yasl_wait_event(&e, 0) > 0)
+ assert(++n < 1000);
}
static void *wake_thread(void *unused)
-{ (void)unused;struct timespec delay={0,20000000};nanosleep(&delay,NULL);yasl_wake();return NULL; }
+{
+ (void)unused;
+ struct timespec delay = {0, 20000000};
+ nanosleep(&delay, NULL);
+ yasl_wake();
+ return NULL;
+}
static void *wrong_thread(void *data)
-{ assert(!yasl_surface_set_title(data,"wrong"));assert(yasl_get_error(NULL)==YASL_ERROR_WRONG_THREAD);return NULL; }
+{
+ assert(yasl_surface_set_title(data, "wrong") == -EPERM);
+ struct yasl_cpu_buffer buffer;
+ assert(yasl_cpu_acquire(data, &buffer) == -EPERM && !buffer.pixels);
+#if YASL_HAS_WAYLAND
+ struct yasl_wayland_handles native;
+ assert(yasl_wayland_get_native(data, &native) == -EPERM && !native.display && !native.surface);
+#endif
+ return NULL;
+}
static int refreshes;
-static void refresh(yasl_surface *s,void *ud)
+static void refresh(yasl_surface *s, void *ud)
{
- (void)ud;refreshes++;
- yasl_event e;assert(yasl_wait_event(&e,0)==-1);assert(yasl_get_error(NULL)==YASL_ERROR_BUSY);
- yasl_surface_destroy(s);assert(yasl_get_error(NULL)==YASL_ERROR_BUSY);
+ (void)ud;
+ refreshes++;
+ yasl_event e;
+ assert(yasl_wait_event(&e, 0) == -EBUSY);
+ assert(errno == EBUSY);
+ assert(yasl_surface_destroy(s) == -EBUSY);
+ struct yasl_surface_options options = {.width = 32, .height = 32};
+ assert(!yasl_surface_create(&options) && errno == EBUSY);
+ assert(!yasl_gl_surface_create(&options, NULL) && errno == EBUSY);
}
-int main(int argc,char **argv)
+int main(int argc, char **argv)
{
- assert(argc==2);
- enum yasl_backend backend=!strcmp(argv[1],"wayland")?YASL_BACKEND_WAYLAND:!strcmp(argv[1],"x11")?YASL_BACKEND_X11:YASL_BACKEND_COCOA;
- assert(yasl_set_backend(backend));assert(yasl_set_app_id("org.yasl.surface-test"));
- yasl_surface *s=yasl_surface_create("CPU contract",96,80,YASL_SURFACE_CPU);
- if(!s){const char *why;yasl_get_error(&why);fprintf(stderr,"surface: %s native=%d\n",why,yasl_get_native_error());return 1;}
- assert(yasl_surface_backend(s)==backend);
- if(backend==YASL_BACKEND_COCOA) {
- assert(!(yasl_surface_features(s)&YASL_CAP_OPENGL));
- assert(!yasl_gl_surface_create("unsupported GL",64,64,0,NULL,NULL));
- assert(yasl_get_error(NULL)==YASL_ERROR_UNSUPPORTED);
- assert(!yasl_gl_make_current(NULL));
- assert(yasl_get_error(NULL)==YASL_ERROR_UNSUPPORTED);
+ assert(argc == 2);
+ enum yasl_backend backend = !strcmp(argv[1], "wayland") ? YASL_BACKEND_WAYLAND
+ : !strcmp(argv[1], "x11") ? YASL_BACKEND_X11
+ : YASL_BACKEND_COCOA;
+ assert(yasl_init(
+ &(struct yasl_options){.backend = backend, .app_id = "org.yasl.surface-test"}) == 0);
+ yasl_surface *s = yasl_surface_create(&(struct yasl_surface_options){
+ .title = "CPU contract", .width = 96, .height = 80, .type = YASL_SURFACE_CPU, .flags = 0});
+ if (!s) {
+ const char *why;
+ why = strerror(errno);
+ fprintf(stderr, "surface: %s native=%d\n", why, errno);
+ return 1;
+ }
+ assert(yasl_surface_get_backend(s) == backend);
+ assert(yasl_surface_set_userdata(s, &backend) == 0);
+ assert(yasl_surface_get_userdata(s) == &backend);
+#if YASL_HAS_WAYLAND
+ struct yasl_wayland_handles wl;
+ int wl_result = yasl_wayland_get_native(s, &wl);
+ assert(wl_result == (backend == YASL_BACKEND_WAYLAND ? 0 : -ENOTSUP));
+ assert(backend == YASL_BACKEND_WAYLAND ? wl.display && wl.surface : !wl.display && !wl.surface);
+ assert(yasl_wayland_get_native(s, NULL) == -EINVAL);
+#endif
+#if YASL_HAS_X11
+ struct yasl_x11_handles x;
+ int x_result = yasl_x11_get_native(s, &x);
+ assert(x_result == (backend == YASL_BACKEND_X11 ? 0 : -ENOTSUP));
+ assert(backend == YASL_BACKEND_X11 ? x.display && x.window : !x.display && !x.window);
+#endif
+#if YASL_HAS_COCOA
+ struct yasl_cocoa_handles cocoa;
+ assert(yasl_cocoa_get_native(s, &cocoa) == 0 && cocoa.application && cocoa.window &&
+ cocoa.view);
+#endif
+ CAMetalLayer *layer = NULL;
+ assert(yasl_metal_get_layer(s, &layer) == -EBUSY && !layer);
+ assert(yasl_surface_set_ime_caret(s, 1e300, 0, 1, 1) == -EOVERFLOW);
+ if (backend == YASL_BACKEND_COCOA) {
+ assert(!(yasl_surface_get_features(s) & YASL_CAP_OPENGL));
+ assert(!yasl_gl_surface_create(&(struct yasl_surface_options){.title = "unsupported GL",
+ .width = 64,
+ .height = 64,
+ .type = YASL_SURFACE_NATIVE,
+ .flags = 0},
+ NULL));
+ assert(errno == ENOTSUP);
+ assert((yasl_gl_make_current(NULL) < 0));
+ assert(errno == ENOTSUP);
assert(!yasl_gl_get_proc_address("glClear"));
- assert(yasl_get_error(NULL)==YASL_ERROR_UNSUPPORTED);
+ assert(errno == ENOTSUP);
}
/* The first events report the window size, then ask for a frame. */
- yasl_event e;int fw=0,fh=0;bool window=false;
- for(int i=0;i<1000;i++) {
- assert(yasl_wait_event(&e,i?20:0)>=0);
- if(e.type==YASL_EVENT_WINDOW){window=true;fw=e.window.pixel_width;fh=e.window.pixel_height;}
- if(e.type==YASL_EVENT_REFRESH){assert(window&&fw>0&&fh>0&&e.surface==s);break;}
+ yasl_event e;
+ int fw = 0, fh = 0;
+ bool window = false;
+ for (int i = 0; i < 1000; i++) {
+ assert(yasl_wait_event(&e, i ? 20 : 0) >= 0);
+ if (e.type == YASL_EVENT_WINDOW) {
+ window = true;
+ fw = e.window.pixel_width;
+ fh = e.window.pixel_height;
+ }
+ if (e.type == YASL_EVENT_REFRESH) {
+ assert(window && fw > 0 && fh > 0 && e.surface == s);
+ break;
+ }
}
- assert(e.type==YASL_EVENT_REFRESH);
- int w,h;yasl_surface_get_framebuffer_size(s,&w,&h);assert(w==fw&&h==fh);
- assert(!yasl_set_backend(backend));assert(yasl_get_error(NULL)==YASL_ERROR_BUSY);
- int pw,ph;uint32_t *pixels=yasl_cpu_pixels(s,&pw,&ph);assert(pixels&&pw==w&&ph==h);
- assert(pixels[0]==0&&pixels[(size_t)w*h-1]==0);
- for(size_t i=0;i<(size_t)w*h;i++)pixels[i]=0xff102030u;
- pixels[(size_t)3*w+2]=0xffaabbccu;
- assert(yasl_cpu_present(s));
- assert(yasl_cpu_pixels(s,&pw,&ph)==pixels&&pixels[0]==0xff102030u&&pixels[(size_t)3*w+2]==0xffaabbccu);
+ assert(e.type == YASL_EVENT_REFRESH);
+ int w, h;
+ yasl_surface_get_framebuffer_size(s, &w, &h);
+ assert(w == fw && h == fh);
+ assert((yasl_init(&(struct yasl_options){.backend = backend}) < 0));
+ assert(errno == EALREADY);
+ struct yasl_cpu_buffer buffer, other;
+ assert(yasl_cpu_acquire(s, &buffer) == 0);
+ uint32_t *pixels = buffer.pixels;
+ assert(buffer.width == w && buffer.height == h && buffer.stride_bytes == (size_t)w * 4);
+ assert(pixels[0] == 0 && pixels[(size_t)w * h - 1] == 0);
+ assert(yasl_cpu_acquire(s, &other) == -EBUSY && !other.pixels);
+ for (size_t i = 0; i < (size_t)w * h; i++)
+ pixels[i] = 0xff102030u;
+ pixels[(size_t)3 * w + 2] = 0xffaabbccu;
+ other = buffer;
+ other.stride_bytes++;
+ assert(yasl_cpu_present(s, &other) == -EINVAL);
+ assert(yasl_cpu_present(s, &buffer) == 0);
+ assert(yasl_cpu_present(s, &buffer) == -ESTALE);
+ assert(yasl_cpu_acquire(s, &buffer) == 0);
+ assert(buffer.pixels[0] == 0xff102030u && buffer.pixels[(size_t)3 * w + 2] == 0xffaabbccu);
+ buffer.pixels[0] = 0xffffffffu;
+ assert(s->committed_pixels[0] == 0xff102030u);
+ assert(yasl_cpu_discard(s, &buffer) == 0);
+ assert(yasl_cpu_discard(s, &buffer) == -ESTALE);
+ assert(yasl_cpu_acquire(s, &buffer) == 0 && buffer.pixels[0] == 0xff102030u);
drain();
- pthread_t thread;assert(!pthread_create(&thread,NULL,wrong_thread,s));pthread_join(thread,NULL);
- assert(!pthread_create(&thread,NULL,wake_thread,NULL));
- int n=yasl_wait_event(&e,500);assert(n>=0);pthread_join(thread,NULL);
- bool ime=yasl_surface_set_text_input(s,true,YASL_TEXT_NORMAL);
- if(!ime)assert(yasl_get_error(NULL)==YASL_ERROR_UNSUPPORTED);
- bool snapshot=yasl_surface_set_surrounding_text(s,"a\xc3\xa9z",4,1,3,7);
- if(ime)assert(snapshot);
- else assert(!snapshot&&yasl_get_error(NULL)==YASL_ERROR_UNSUPPORTED);
- assert(!yasl_surface_set_surrounding_text(s,"a\xc3\xa9z",4,2,3,8));
- assert(!yasl_surface_set_surrounding_text(s,"\xc0\xaf",2,0,2,8));
+ assert(yasl_cpu_present(s, &buffer) == -ESTALE);
+ yasl_surface *second = yasl_surface_create(&(struct yasl_surface_options){
+ .width = 32, .height = 32, .type = YASL_SURFACE_CPU, .userdata = &backend});
+ assert(second && yasl_surface_get_userdata(second) == &backend);
+ assert(yasl_cpu_acquire(s, &buffer) == 0);
+ assert(yasl_cpu_acquire(second, &other) == 0);
+ assert(yasl_cpu_present(second, &buffer) == -ESTALE);
+ assert(yasl_cpu_discard(second, &other) == 0);
+ assert(yasl_surface_destroy(second) == 0);
+ assert(yasl_cpu_discard(s, &buffer) == 0);
+ pthread_t thread;
+ assert(!pthread_create(&thread, NULL, wrong_thread, s));
+ pthread_join(thread, NULL);
+ assert(!pthread_create(&thread, NULL, wake_thread, NULL));
+ int n = yasl_wait_event(&e, 500);
+ assert(n >= 0);
+ pthread_join(thread, NULL);
+ bool ime = (yasl_surface_set_text_input(s, true, YASL_TEXT_NORMAL) == 0);
+ if (!ime)
+ assert(errno == ENOTSUP);
+ bool snapshot = (yasl_surface_set_surrounding_text(s, "a\xc3\xa9z", 4, 1, 3, 7) == 0);
+ if (ime)
+ assert(snapshot);
+ else
+ assert(!snapshot && errno == ENOTSUP);
+ assert((yasl_surface_set_surrounding_text(s, "a\xc3\xa9z", 4, 2, 3, 8) < 0));
+ assert((yasl_surface_set_surrounding_text(s, "\xc0\xaf", 2, 0, 2, 8) < 0));
drain();
- struct yasl_private_event pen={.type=YASL_PRIVATE_EVENT_PEN,.surface=s,
- .pen={.device_id=10,.tool_id=20,.x=12.5,.y=9.25,.pressure=0.3,
- .axes_valid=YASL_PRIVATE_PEN_PRESSURE,.action=YASL_PRIVATE_PEN_DOWN,.in_contact=true}};
- assert(yasl_private_events_push(s->display,s,&pen));
- assert(yasl_wait_event(&e,0)==1&&e.type==YASL_EVENT_PEN&&e.pen.device_id==10&&e.pen.pressure==0.3&&e.pen.in_contact);
- struct yasl_private_event text={.type=YASL_PRIVATE_EVENT_TEXT,.surface=s,.text={.data="owned",.len=5}};
- assert(yasl_private_events_push(s->display,s,&text));assert(yasl_wait_event(&e,0)==1&&e.type==YASL_EVENT_TEXT);
- assert(e.text.length==5&&!strcmp(e.text.data,"owned"));
+ struct yasl_private_event pen = {.type = YASL_PRIVATE_EVENT_PEN,
+ .surface = s,
+ .pen = {.device_id = 10,
+ .tool_id = 20,
+ .x = 12.5,
+ .y = 9.25,
+ .pressure = 0.3,
+ .axes_valid = YASL_PRIVATE_PEN_PRESSURE,
+ .action = YASL_PRIVATE_PEN_DOWN,
+ .in_contact = true}};
+ assert(yasl_private_events_push(s->display, s, &pen));
+ assert(yasl_wait_event(&e, 0) == 1 && e.type == YASL_EVENT_PEN && e.pen.device_id == 10 &&
+ e.pen.pressure == 0.3 && e.pen.in_contact);
+ struct yasl_private_event text = {
+ .type = YASL_PRIVATE_EVENT_TEXT, .surface = s, .text = {.data = "owned", .len = 5}};
+ assert(yasl_private_events_push(s->display, s, &text));
+ assert(yasl_wait_event(&e, 0) == 1 && e.type == YASL_EVENT_TEXT);
+ assert(e.text.length == 5 && !strcmp(e.text.data, "owned"));
/* Unchanged focus folds into no event; a change reports the activated state. */
- unsigned active=yasl_surface_state(s)&YASL_STATE_ACTIVATED;
- struct yasl_private_event focus={.type=YASL_PRIVATE_EVENT_FOCUS,.surface=s,.focus={.focused=!active}};
- assert(yasl_private_events_push(s->display,s,&focus));assert(yasl_wait_event(&e,0)==1&&e.type==YASL_EVENT_WINDOW);
- assert((e.window.state&YASL_STATE_ACTIVATED)==(active?0u:YASL_STATE_ACTIVATED)&&e.window.pixel_width==w);
- assert(yasl_private_events_push(s->display,s,&focus));assert(yasl_wait_event(&e,0)==0&&e.type==YASL_EVENT_NONE);
- struct yasl_private_event hover={.type=YASL_PRIVATE_EVENT_DROP_HOVER,.surface=s,.drop={YASL_PRIVATE_DROP_LEAVE,{0,0}}};
- assert(yasl_private_events_push(s->display,s,&hover));
- assert(yasl_wait_event(&e,0)==1&&e.type==YASL_EVENT_DROP&&e.drop.action==YASL_DROP_LEAVE);
- yasl_surface_set_refresh_callback(s,refresh,NULL);
- assert(yasl_cpu_present(s));
+ unsigned active = yasl_surface_get_state(s) & YASL_STATE_ACTIVATED;
+ struct yasl_private_event focus = {
+ .type = YASL_PRIVATE_EVENT_FOCUS, .surface = s, .focus = {.focused = !active}};
+ assert(yasl_private_events_push(s->display, s, &focus));
+ assert(yasl_wait_event(&e, 0) == 1 && e.type == YASL_EVENT_WINDOW);
+ assert((e.window.state & YASL_STATE_ACTIVATED) == (active ? 0u : YASL_STATE_ACTIVATED) &&
+ e.window.pixel_width == w);
+ assert(yasl_private_events_push(s->display, s, &focus));
+ assert(yasl_wait_event(&e, 0) == 0 && e.type == YASL_EVENT_NONE);
+ struct yasl_private_event hover = {.type = YASL_PRIVATE_EVENT_DROP_HOVER,
+ .surface = s,
+ .drop = {YASL_PRIVATE_DROP_LEAVE, {0, 0}}};
+ assert(yasl_private_events_push(s->display, s, &hover));
+ assert(yasl_wait_event(&e, 0) == 1 && e.type == YASL_EVENT_DROP &&
+ e.drop.action == YASL_DROP_LEAVE);
+ assert(yasl_surface_request_size(s, 112, 96) == 0);
+ uint64_t resize_deadline = yasl_time_ns() + 3000000000ull;
+ int logical_w = 0, logical_h = 0;
+ do {
+ assert(yasl_wait_event(&e, 20) >= 0);
+ yasl_surface_get_size(s, &logical_w, &logical_h);
+ } while ((logical_w != 112 || logical_h != 96) && yasl_time_ns() < resize_deadline);
+ assert(logical_w == 112 && logical_h == 96);
+ assert(yasl_cpu_acquire(s, &buffer) == 0);
+ assert(buffer.pixels[0] == 0xff102030u);
+ assert(buffer.pixels[(size_t)3 * buffer.width + 2] == 0xffaabbccu);
+ assert(buffer.pixels[(size_t)buffer.width * buffer.height - 1] == 0);
+ assert(yasl_cpu_present(s, &buffer) == 0);
+ yasl_surface_set_refresh_callback(s, refresh, NULL);
+ assert(yasl_cpu_acquire(s, &buffer) == 0);
+ assert(yasl_cpu_present(s, &buffer) == 0);
#if YASL_HAS_COCOA
yasl_test_frame_tick(s);
#endif
- uint64_t deadline=yasl_private_loop_now_ns()+3000000000ull;
- while(!refreshes && yasl_private_loop_now_ns()<deadline)assert(yasl_wait_event(&e,20)>=0);
- assert(refreshes>0);
- yasl_surface_set_refresh_callback(s,NULL,NULL);
+ uint64_t deadline = yasl_private_loop_now_ns() + 3000000000ull;
+ while (!refreshes && yasl_private_loop_now_ns() < deadline)
+ assert(yasl_wait_event(&e, 20) >= 0);
+ assert(refreshes > 0);
+ yasl_surface_set_refresh_callback(s, NULL, NULL);
yasl_surface_destroy(s);
- assert(!yasl_set_backend(backend));
- s=yasl_surface_create("shared backend remains",64,64,YASL_SURFACE_CPU);assert(s);
- yasl_shutdown();assert(yasl_set_backend(backend));
- s=yasl_surface_create("reinitialized",64,64,YASL_SURFACE_CPU);assert(s);yasl_shutdown();
- puts("public surface lifecycle, pixels, ownership, input, wake and refresh passed");return 0;
+ assert((yasl_init(&(struct yasl_options){.backend = backend}) < 0));
+ s = yasl_surface_create(&(struct yasl_surface_options){.title = "shared backend remains",
+ .width = 64,
+ .height = 64,
+ .type = YASL_SURFACE_CPU,
+ .flags = 0});
+ assert(s);
+ yasl_shutdown();
+ assert((yasl_init(&(struct yasl_options){.backend = backend}) == 0));
+ s = yasl_surface_create(&(struct yasl_surface_options){
+ .title = "reinitialized", .width = 64, .height = 64, .type = YASL_SURFACE_CPU, .flags = 0});
+ assert(s);
+ yasl_shutdown();
+ puts("public surface lifecycle, pixels, ownership, input, wake and refresh passed");
+ return 0;
}
diff --git a/tests/common/test_vulkan.c b/tests/common/test_vulkan.c
index 12daa22..b12256a 100644
--- a/tests/common/test_vulkan.c
+++ b/tests/common/test_vulkan.c
@@ -41,29 +41,32 @@ static int run_test(enum yasl_backend backend)
VkSemaphore acquired = VK_NULL_HANDLE, rendered = VK_NULL_HANDLE;
VkCommandBuffer command = VK_NULL_HANDLE;
- if (!yasl_set_backend(backend)) {
+ if ((yasl_init(&(struct yasl_options){.backend=backend}) < 0)) {
fprintf(stderr, "vulkan integration: cannot select requested backend %d (YASL error %d)\n",
- backend, yasl_get_error(NULL));
+ backend, errno);
goto cleanup;
}
- if (!yasl_vulkan_init(vkGetInstanceProcAddr)) {
+ if ((yasl_vulkan_init(vkGetInstanceProcAddr) < 0)) {
fprintf(stderr, "vulkan integration: loader initialization failed (YASL error %d)\n",
- yasl_get_error(NULL));
+ errno);
goto cleanup;
}
- surface = yasl_surface_create("yasl Vulkan WSI integration", 256, 192, 0);
+ surface = yasl_surface_create(&(struct yasl_surface_options){.title="yasl Vulkan WSI integration", .width=256, .height=192, .type=YASL_SURFACE_NATIVE, .flags=0});
if (!surface) {
fprintf(stderr, "vulkan integration: surface creation failed (YASL error %d)\n",
- yasl_get_error(NULL));
+ errno);
goto cleanup;
}
- if (!(yasl_surface_features(surface) & YASL_CAP_VULKAN)) {
+ if (!(yasl_surface_get_features(surface) & YASL_CAP_VULKAN)) {
fprintf(stderr, "vulkan integration: requested backend does not advertise Vulkan WSI\n");
goto cleanup;
}
uint32_t extension_count = 0;
- const char *const *required = yasl_vulkan_instance_extensions(surface, &extension_count);
+ struct yasl_vulkan_requirements requirements;
+ if (yasl_vulkan_get_requirements(surface, &requirements) < 0) goto cleanup;
+ extension_count = requirements.extension_count;
+ const char *const *required = requirements.extensions;
if (!required || !extension_count) {
fprintf(stderr, "vulkan integration: surface returned no required instance extensions\n");
goto cleanup;
@@ -99,7 +102,7 @@ static int run_test(enum yasl_backend backend)
VkInstanceCreateInfo instance_info = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &app,
- .flags = yasl_vulkan_instance_flags(surface),
+ .flags = requirements.flags,
.enabledExtensionCount = extension_count,
.ppEnabledExtensionNames = required,
};
@@ -108,6 +111,18 @@ static int run_test(enum yasl_backend backend)
vr = yasl_vulkan_create_surface(surface, instance, NULL, &vk_surface);
if (vr != VK_SUCCESS) { status = fail_vk("yasl_vulkan_create_surface", vr); goto cleanup; }
+ VkSurfaceKHR duplicate = VK_NULL_HANDLE;
+ if (yasl_vulkan_create_surface(surface, instance, NULL, &duplicate) != VK_ERROR_NATIVE_WINDOW_IN_USE_KHR ||
+ duplicate != VK_NULL_HANDLE || yasl_surface_destroy(surface) != -EBUSY ||
+ yasl_shutdown() != -EBUSY || yasl_vulkan_init(vkGetInstanceProcAddr) != -EBUSY) {
+ fprintf(stderr, "vulkan integration: live WSI ownership was not enforced\n");
+ goto cleanup;
+ }
+ if (yasl_vulkan_destroy_surface(surface) != 0 || yasl_vulkan_destroy_surface(surface) != 0) goto cleanup;
+ vk_surface = VK_NULL_HANDLE;
+ vr = yasl_vulkan_create_surface(surface, instance, NULL, &vk_surface);
+ if (vr != VK_SUCCESS) { status = fail_vk("recreate Vulkan surface", vr); goto cleanup; }
+
uint32_t physical_count = 0;
vr = vkEnumeratePhysicalDevices(instance, &physical_count, NULL);
if (vr != VK_SUCCESS || !physical_count) {
@@ -365,7 +380,7 @@ cleanup:
if (command_pool) vkDestroyCommandPool(device, command_pool, NULL);
if (swapchain) vkDestroySwapchainKHR(device, swapchain, NULL);
if (device) vkDestroyDevice(device, NULL);
- if (vk_surface) vkDestroySurfaceKHR(instance, vk_surface, NULL);
+ if (vk_surface) yasl_vulkan_destroy_surface(surface);
if (instance) vkDestroyInstance(instance, NULL);
if (surface) yasl_surface_destroy(surface);
yasl_shutdown();
diff --git a/tests/meson.build b/tests/meson.build
index 8370423..e34f072 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -8,7 +8,11 @@ foreach kind : ['static', 'shared']
endforeach
if add_languages('cpp', native: false, required: false)
cpp_api = executable('yasl-cpp-api-test', 'test_api.cpp', dependencies: shared_dep,
- cpp_args: ['-pedantic-errors'], override_options: ['cpp_std=c++11'])
+ cpp_args: ['-pedantic-errors',
+ '-DYASL_HAS_VULKAN=' + (has_vulkan ? '1' : '0'),
+ '-DYASL_HAS_WAYLAND=' + (has_wayland ? '1' : '0'),
+ '-DYASL_HAS_X11=' + (has_x11 ? '1' : '0'),
+ '-DYASL_HAS_COCOA=' + (has_cocoa ? '1' : '0')], override_options: ['cpp_std=c++11'])
test('cpp-api', cpp_api)
endif
headers = files('../include/yasl/yasl.h', '../include/yasl/cpu.h', '../include/yasl/gl.h',
diff --git a/tests/test_api.cpp b/tests/test_api.cpp
index c0693eb..acfbbb8 100644
--- a/tests/test_api.cpp
+++ b/tests/test_api.cpp
@@ -19,8 +19,17 @@
int main()
{
yasl_event e{};
- e.pen.x=1.5;e.pen.pressure=0.25;
- assert(e.pen.pressure==0.25);
- assert(!yasl_surface_create("invalid",-1,10,0));
- yasl_shutdown();return 0;
+ e.pen.x = 1.5;
+ e.pen.pressure = 0.25;
+ assert(e.pen.pressure == 0.25);
+ yasl_surface_options options{};
+ options.width = -1;
+ options.height = 10;
+ assert(!yasl_surface_create(&options) && errno == EINVAL);
+ yasl_gl_options gl = YASL_GL_OPTIONS_INIT;
+ assert(gl.major == 3 && gl.minor == 2 && gl.share == nullptr);
+ yasl_cpu_buffer buffer{};
+ assert(yasl_cpu_acquire(nullptr, &buffer) == -ENODEV);
+ yasl_shutdown();
+ return 0;
}
diff --git a/tests/test_lifecycle.c b/tests/test_lifecycle.c
index a334fed..08213b0 100644
--- a/tests/test_lifecycle.c
+++ b/tests/test_lifecycle.c
@@ -1,24 +1,37 @@
#include <yasl/yasl.h>
#include <yasl/cpu.h>
#include <yasl/gl.h>
+#include <yasl/metal.h>
#include <yasl/desktop.h>
#include <assert.h>
int main(void)
{
- yasl_shutdown(); yasl_wake();
- yasl_event e={.type=YASL_EVENT_QUIT};
- assert(yasl_wait_event(&e,0)==-1 && e.type==YASL_EVENT_NONE);
- assert(yasl_get_error(NULL)==YASL_ERROR_NOT_INITIALIZED);
- assert(!yasl_set_backend((enum yasl_backend)999));
- assert(yasl_get_error(NULL)==YASL_ERROR_INVALID);
- assert(!yasl_surface_create("bad",0,64,0));
- assert(!yasl_surface_create("bad",64,64,16));
- assert(yasl_set_app_id("org.yasl.api"));
- assert(yasl_set_backend(YASL_BACKEND_AUTO));
- yasl_surface_destroy(NULL);
- assert(yasl_time_ns()>0);
- const char *description=NULL;yasl_get_error(&description);assert(description);
- int w=9,h=9;yasl_surface_get_size(NULL,&w,&h);assert(w==0&&h==0);
- assert(yasl_executable_path(NULL,0)>0);
- yasl_shutdown();return 0;
+ assert(yasl_shutdown() == 0);
+ yasl_wake();
+ yasl_event event = {.type = YASL_EVENT_QUIT};
+ assert(yasl_wait_event(&event, 0) == -ENODEV && event.type == YASL_EVENT_NONE);
+ assert(yasl_wait_event(NULL, 0) == -EINVAL);
+ assert(yasl_init(&(struct yasl_options){.backend = (enum yasl_backend)999}) == -EINVAL);
+ assert(!yasl_surface_create(NULL) && errno == EINVAL);
+ struct yasl_surface_options options = {.width = 0, .height = 64};
+ assert(!yasl_surface_create(&options) && errno == EINVAL);
+ options.width = 64;
+ options.flags = 16;
+ assert(!yasl_surface_create(&options) && errno == EINVAL);
+ options.flags = 0;
+ options.type = (enum yasl_surface_type)99;
+ assert(!yasl_surface_create(&options) && errno == EINVAL);
+ struct yasl_cpu_buffer buffer = {.width = 9};
+ assert(yasl_cpu_acquire(NULL, &buffer) == -ENODEV && !buffer.pixels && !buffer.width);
+ assert(yasl_cpu_acquire(NULL, NULL) == -EINVAL);
+ CAMetalLayer *layer = NULL;
+ assert(yasl_metal_get_layer(NULL, &layer) == -ENODEV && !layer);
+ assert(yasl_surface_destroy(NULL) == 0);
+ assert(yasl_time_ns() > 0);
+ int w = 9, h = 9;
+ yasl_surface_get_size(NULL, &w, &h);
+ assert(w == 0 && h == 0);
+ assert(yasl_get_executable_path(NULL, 0) > 0);
+ assert(yasl_shutdown() == 0);
+ return 0;
}
diff --git a/tests/wayland/test_reconnect.c b/tests/wayland/test_reconnect.c
index 0c42043..737afb2 100644
--- a/tests/wayland/test_reconnect.c
+++ b/tests/wayland/test_reconnect.c
@@ -4,6 +4,7 @@
#include <yasl/yasl.h>
#include <yasl/cpu.h>
#include <yasl/gl.h>
+#include <yasl/native/wayland.h>
#if TEST_VULKAN
#include <yasl/vulkan.h>
#endif
@@ -11,7 +12,7 @@
#include <stdlib.h>
#include <string.h>
-#define CHECK(cond, what) do { if (!(cond)) { const char *why; yasl_get_error(&why); \
+#define CHECK(cond, what) do { if (!(cond)) { const char *why; why = strerror(errno); \
fprintf(stderr, "reconnect: %s failed at line %d (%s)\n", what, __LINE__, why); exit(1); } } while (0)
typedef void (*clear_color_fn)(float, float, float, float);
@@ -34,13 +35,13 @@ static yasl_event expect(enum yasl_event_type type, yasl_surface *surface, int t
static void draw_gl(float red)
{
- CHECK(yasl_gl_make_current(gl), "binding GL");
+ CHECK((yasl_gl_make_current(gl) == 0), "binding GL");
clear_color_fn clear_color = (clear_color_fn)yasl_gl_get_proc_address("glClearColor");
clear_fn clear = (clear_fn)yasl_gl_get_proc_address("glClear");
CHECK(clear_color && clear, "loading GL procedures");
clear_color(red, 0.2f, 0.3f, 1);
clear(0x00004000u);
- CHECK(yasl_gl_swap_buffers(gl), "swapping GL buffers");
+ CHECK((yasl_gl_present(gl) == 0), "swapping GL buffers");
}
#if TEST_VULKAN
@@ -57,60 +58,72 @@ static VkSurfaceKHR vulkan_surface(void)
int main(void)
{
- CHECK(yasl_set_backend(YASL_BACKEND_WAYLAND), "selecting Wayland");
- cpu = yasl_surface_create("reconnect CPU", 160, 120, YASL_SURFACE_CPU);
+ CHECK((yasl_init(&(struct yasl_options){.backend=YASL_BACKEND_WAYLAND}) == 0), "selecting Wayland");
+ cpu = yasl_surface_create(&(struct yasl_surface_options){.title="reconnect CPU", .width=160, .height=120, .type=YASL_SURFACE_CPU, .flags=0});
CHECK(cpu, "creating CPU window");
- gl = yasl_gl_surface_create("reconnect GL", 160, 120, 0, NULL, NULL);
+ gl = yasl_gl_surface_create(&(struct yasl_surface_options){.title="reconnect GL", .width=160, .height=120, .type=YASL_SURFACE_NATIVE, .flags=0}, NULL);
CHECK(gl, "creating GL window");
expect(YASL_EVENT_REFRESH, cpu, 5000);
int w, h;
- uint32_t *pixels = yasl_cpu_pixels(cpu, &w, &h);
+ struct yasl_cpu_buffer buffer;
+ CHECK(yasl_cpu_acquire(cpu, &buffer) == 0, "acquire CPU buffer");
+ w=buffer.width; h=buffer.height;
+ uint32_t *pixels = buffer.pixels;
CHECK(pixels, "getting pixels");
for (size_t i = 0; i < (size_t)w * h; i++) pixels[i] = 0xff336699u;
- CHECK(yasl_cpu_present(cpu), "presenting");
+ CHECK((yasl_cpu_present(cpu, &buffer) == 0), "presenting");
draw_gl(0.8f);
#if TEST_VULKAN
- CHECK(yasl_vulkan_init(vkGetInstanceProcAddr), "loading Vulkan");
- vk = yasl_surface_create("reconnect Vulkan", 160, 120, 0);
+ CHECK((yasl_vulkan_init(vkGetInstanceProcAddr) == 0), "loading Vulkan");
+ vk = yasl_surface_create(&(struct yasl_surface_options){.title="reconnect Vulkan", .width=160, .height=120, .type=YASL_SURFACE_NATIVE, .flags=0});
CHECK(vk, "creating Vulkan window");
- uint32_t count = 0;
- const char *const *extensions = yasl_vulkan_instance_extensions(vk, &count);
+ struct yasl_vulkan_requirements requirements;
+ CHECK(yasl_vulkan_get_requirements(vk, &requirements) == 0, "Vulkan requirements");
VkInstanceCreateInfo info = {.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
- .enabledExtensionCount = count, .ppEnabledExtensionNames = extensions};
+ .flags = requirements.flags, .enabledExtensionCount = requirements.extension_count,
+ .ppEnabledExtensionNames = requirements.extensions};
CHECK(vkCreateInstance(&info, NULL, &instance) == VK_SUCCESS, "creating Vulkan instance");
- VkSurfaceKHR surface = vulkan_surface();
+ CHECK(vulkan_surface() != VK_NULL_HANDLE, "Vulkan handle");
#endif
puts("ready");
fflush(stdout);
expect(YASL_EVENT_DISPLAY_LOST, NULL, 10000);
+ struct yasl_wayland_handles native;
+ CHECK(yasl_wayland_get_native(cpu, &native) == -ENOTCONN && !native.display && !native.surface,
+ "rejecting native handles while offline");
+ CHECK(yasl_cpu_acquire(cpu, &buffer) == -ENOTCONN, "rejecting CPU acquisition while offline");
+ CHECK(yasl_gl_make_current(gl) == -ENOTCONN, "rejecting GL binding while offline");
#if TEST_VULKAN
- vkDestroySurfaceKHR(instance, surface, NULL);
+ CHECK(yasl_vulkan_destroy_surface(vk) == 0, "destroying Vulkan surface");
#endif
- CHECK(!yasl_surface_create("offline", 64, 64, YASL_SURFACE_CPU), "refusing windows while offline");
- CHECK(yasl_get_error(NULL) == YASL_ERROR_DISCONNECTED, "reporting the disconnect");
+ CHECK(!yasl_surface_create(&(struct yasl_surface_options){.title="offline", .width=64, .height=64, .type=YASL_SURFACE_CPU, .flags=0}), "refusing windows while offline");
+ CHECK(errno == ENOTCONN, "reporting the disconnect");
puts("lost");
fflush(stdout);
expect(YASL_EVENT_DISPLAY_RESTORED, NULL, 15000);
+ CHECK(yasl_wayland_get_native(cpu, &native) == 0 && native.display && native.surface,
+ "querying restored native handles");
expect(YASL_EVENT_REFRESH, cpu, 5000);
int rw, rh;
- pixels = yasl_cpu_pixels(cpu, &rw, &rh);
+ CHECK(yasl_cpu_acquire(cpu, &buffer) == 0, "reacquire CPU buffer");
+ rw=buffer.width; rh=buffer.height; pixels=buffer.pixels;
CHECK(pixels && rw == w && rh == h, "keeping the window size");
CHECK(pixels[0] == 0xff336699u && pixels[(size_t)w * h - 1] == 0xff336699u, "keeping the pixels");
- CHECK(yasl_cpu_present(cpu), "presenting after reconnect");
+ CHECK((yasl_cpu_present(cpu, &buffer) == 0), "presenting after reconnect");
draw_gl(0.1f);
#if TEST_VULKAN
- surface = vulkan_surface();
- vkDestroySurfaceKHR(instance, surface, NULL);
+ CHECK(vulkan_surface() != VK_NULL_HANDLE, "restored Vulkan handle");
+ CHECK(yasl_vulkan_destroy_surface(vk) == 0, "destroying Vulkan surface");
vkDestroyInstance(instance, NULL);
#endif
/* A second window proves the new connection accepts creation. */
- CHECK(yasl_surface_create("after reconnect", 64, 64, YASL_SURFACE_CPU), "creating a window after reconnect");
+ CHECK(yasl_surface_create(&(struct yasl_surface_options){.title="after reconnect", .width=64, .height=64, .type=YASL_SURFACE_CPU, .flags=0}), "creating a window after reconnect");
yasl_gl_make_current(NULL);
yasl_shutdown();
puts("restored");
diff --git a/tests/x11/test_tablet_native.c b/tests/x11/test_tablet_native.c
index f4f8014..66cdd93 100644
--- a/tests/x11/test_tablet_native.c
+++ b/tests/x11/test_tablet_native.c
@@ -71,8 +71,10 @@ static void render_event(yasl_surface *surface, struct canvas_state *state, cons
state->pen = event->pen;
state->down = event->pen.in_contact;
}
+ struct yasl_cpu_buffer buffer;
+ free(state->pixels);
state->pixels = canvas(surface, &state->width, &state->height,
- &state->painted_width, &state->painted_height, false);
+ &state->painted_width, &state->painted_height, false, &buffer);
assert(state->pixels);
if (event->type == YASL_EVENT_PEN && state->down &&
(event->pen.action == YASL_PEN_DOWN || event->pen.action == YASL_PEN_MOVE)) {
@@ -81,7 +83,11 @@ static void render_event(yasl_surface *surface, struct canvas_state *state, cons
event->pen.x * scale, event->pen.y * scale,
(1.5 + 12 * event->pen.pressure) * scale, event->pen.tool == YASL_PEN_ERASER);
}
- assert(yasl_cpu_present(surface));
+ uint32_t *snapshot = malloc((size_t)state->width * state->height * sizeof(*snapshot));
+ assert(snapshot);
+ memcpy(snapshot, buffer.pixels, (size_t)state->width * state->height * sizeof(*snapshot));
+ assert(yasl_cpu_present(surface, &buffer) == 0);
+ state->pixels = snapshot;
}
static void drain(yasl_surface *surface, struct canvas_state *state)
@@ -101,10 +107,12 @@ static void pump(int fd, yasl_surface *surface, struct canvas_state *state)
receive_all(fd, &response, sizeof(response));
assert(response.type == XF86IT_RESPONSE_SYNC_FINISHED && response.length == sizeof(response));
/* The driver acknowledgment guarantees XI2 processing has finished. */
- XSync(yasl_x11_display(surface), False);
+ struct yasl_x11_handles native;
+ assert(yasl_x11_get_native(surface, &native) == 0);
+ XSync(native.display, False);
drain(surface, state);
/* Public polling submits the CPU frame before the native readback. */
- XSync(yasl_x11_display(surface), False);
+ XSync(native.display, False);
}
static void set_eraser_property(Display *display)
@@ -132,11 +140,13 @@ int main(int argc, char **argv)
{
assert(argc == 3); /* pen socket, eraser socket */
struct canvas_state app = {0};
- assert(yasl_set_backend(YASL_BACKEND_X11));
- yasl_surface *surface = yasl_surface_create("YASL native pressure test", 500, 400, YASL_SURFACE_CPU);
- assert(surface && (yasl_surface_features(surface) & YASL_CAP_TABLET));
- Display *x = yasl_x11_display(surface);
- Window window = (Window)yasl_x11_window(surface);
+ assert((yasl_init(&(struct yasl_options){.backend=YASL_BACKEND_X11}) == 0));
+ yasl_surface *surface = yasl_surface_create(&(struct yasl_surface_options){.title="YASL native pressure test", .width=500, .height=400, .type=YASL_SURFACE_CPU, .flags=0});
+ assert(surface && (yasl_surface_get_features(surface) & YASL_CAP_TABLET));
+ struct yasl_x11_handles native;
+ assert(yasl_x11_get_native(surface, &native) == 0);
+ Display *x = native.display;
+ Window window = (Window)native.window;
XMoveWindow(x, window, 0, 0);
XSetInputFocus(x, window, RevertToParent, CurrentTime);
XSync(x, False);
@@ -194,6 +204,7 @@ int main(int argc, char **argv)
assert(!app.down && !app.pen.pressure);
close(pen_fd);
close(eraser_fd);
+ free(app.pixels);
yasl_shutdown();
puts("Native Xorg pen pressure, stationary pressure, release, and eraser drawing passed.");
return 0;