diff options
| author | Berke Güzel <wenekar1@gmail.com> | 2026-10-14 13:56:48 +0300 |
|---|---|---|
| committer | Berke Güzel <wenekar1@gmail.com> | 2026-10-14 13:56:48 +0300 |
| commit | 87279f6903343e621c0446b20d1e7d6a495f1125 (patch) | |
| tree | 999b5fb5fb76702efb02f856f082147304f0859b /tests/common/test_surface_integration.c | |
| parent | 1ab3e5a123109704789c2768e11a4f9af515821d (diff) | |
Simplify the public API into one dense header
yasl.h now holds everything except Vulkan: 45 functions instead of 62,
and three installed headers instead of ten.
- One constructor: yasl_surface_create takes CPU, GL or NATIVE, with
zero-initialized GL options meaning 3.2 core, RGBA8, D24S8.
- Events carry action, mods, x, y and text at the top level; pen
positions are now logical like pointer positions.
- yasl_cpu_pixels/yasl_cpu_present draw into one persistent canvas,
cutting three full-frame copies per frame.
- yasl_surface_get_info, yasl_surface_request, YASL_CURSOR_LOCKED,
yasl_surface_set_text_input, yasl_surface_begin_move_resize and
yasl_surface_get_native replace groups of narrower calls.
- Interactive calls drop the decorative trigger event argument.
- yasl_wait_event returns 0 only on timeout or yasl_wake, the refresh
callback runs only in native modal loops, focus loss sends
INPUT_RESET, and a lost event no longer fails every later wait.
- Unsupported operations and bad arguments report ENOTSUP and EINVAL
instead of EIO; the keycodes header no longer needs C23.
- The shared library gets a versioned SONAME and version macros.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'tests/common/test_surface_integration.c')
| -rw-r--r-- | tests/common/test_surface_integration.c | 316 |
1 files changed, 156 insertions, 160 deletions
diff --git a/tests/common/test_surface_integration.c b/tests/common/test_surface_integration.c index 1f18f23..072a307 100644 --- a/tests/common/test_surface_integration.c +++ b/tests/common/test_surface_integration.c @@ -1,17 +1,5 @@ #define _POSIX_C_SOURCE 200809L #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> @@ -40,18 +28,20 @@ static void *wake_thread(void *unused) static void *wrong_thread(void *data) { 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 + int w = 1, h = 1; + assert(!yasl_cpu_pixels(data, &w, &h) && errno == EPERM && !w && !h); + struct yasl_native native; + assert(yasl_surface_get_native(data, &native) == -EPERM && !native.wl_display && !native.x11_display); return NULL; } +static void push(yasl_surface *s, struct yasl_private_event event) +{ + event.surface = s; + assert(yasl_private_events_push(s->display, s, &event)); +} static int refreshes; -static void refresh(yasl_surface *s, void *ud) +static void refresh(yasl_surface *s) { - (void)ud; refreshes++; yasl_event e; assert(yasl_wait_event(&e, 0) == -EBUSY); @@ -59,7 +49,10 @@ static void refresh(yasl_surface *s, void *ud) 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); + options.type = YASL_SURFACE_GL; + assert(!yasl_surface_create(&options) && errno == EBUSY); + int w, h; + assert(yasl_cpu_pixels(s, &w, &h) && yasl_cpu_present(s) == 0); } int main(int argc, char **argv) { @@ -67,53 +60,32 @@ int main(int argc, char **argv) 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}); + assert(yasl_init(backend, "org.yasl.surface-test") == 0); + assert(yasl_init(backend, NULL) == -EALREADY); + assert(yasl_get_backend() == backend); + unsigned features = yasl_get_features(); + yasl_surface *s = yasl_surface_create( + &(struct yasl_surface_options){.title = "CPU contract", .width = 96, .height = 80}); if (!s) { - const char *why; - why = strerror(errno); - fprintf(stderr, "surface: %s native=%d\n", why, errno); + fprintf(stderr, "surface: %s\n", strerror(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); + struct yasl_native native; + assert(yasl_surface_get_native(s, &native) == 0); + assert(backend != YASL_BACKEND_WAYLAND || (native.wl_display && native.wl_surface)); + assert(backend != YASL_BACKEND_X11 || (native.x11_display && native.x11_window)); + assert(backend != YASL_BACKEND_COCOA || (native.ns_window && native.ns_view)); + assert(yasl_surface_get_native(s, NULL) == -EINVAL); + assert(!yasl_metal_get_layer(s) && errno == EBUSY); 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(!(features & YASL_FEATURE_OPENGL)); + assert(!yasl_surface_create(&(struct yasl_surface_options){ + .title = "unsupported GL", .width = 64, .height = 64, .type = YASL_SURFACE_GL})); assert(errno == ENOTSUP); + assert(yasl_gl_make_current(NULL) == 0); + assert(!yasl_gl_get_proc_address("glClear") && errno == ENOTSUP); } /* The first events report the window size, then ask for a frame. */ yasl_event e; @@ -132,131 +104,155 @@ int main(int argc, char **argv) } } assert(e.type == YASL_EVENT_REFRESH); + struct yasl_surface_info info = yasl_surface_get_info(s); + assert(info.pixel_width == fw && info.pixel_height == fh && info.scale > 0); + + /* CPU pixels persist between frames and are the window's own buffer. */ 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); + uint32_t *pixels = yasl_cpu_pixels(s, &w, &h); + assert(pixels && w == fw && h == fh); 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); + assert(yasl_cpu_present(s) == 0); + drain(); + pixels = yasl_cpu_pixels(s, &w, &h); + assert(pixels[0] == 0xff102030u && pixels[(size_t)3 * w + 2] == 0xffaabbccu); + + /* Waiting ends only on an event, the timeout or yasl_wake, never early. */ + drain(); + uint64_t start = yasl_time_ns(); + int n = yasl_wait_event(&e, 200); + assert(n == 1 || yasl_time_ns() - start >= 190000000ull); drain(); - 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); + start = yasl_time_ns(); + n = yasl_wait_event(&e, 5000); 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)); + assert(n == 1 || (n == 0 && yasl_time_ns() - start < 4000000000ull)); + assert(!pthread_create(&thread, NULL, wrong_thread, s)); + pthread_join(thread, NULL); + + yasl_surface *second = yasl_surface_create( + &(struct yasl_surface_options){.width = 32, .height = 32, .userdata = &backend}); + assert(second && yasl_surface_get_userdata(second) == &backend); + assert(yasl_surface_destroy(second) == 0); + + /* Text input */ + int ime = yasl_surface_set_text_input(s, &(struct yasl_text_input){ + .text = "a\xc3\xa9z", .length = 4, .selection_begin = 1, .selection_end = 3, + .revision = 7, .caret_x = 1, .caret_y = 2, .caret_width = 1, .caret_height = 10}); + assert(ime == 0 || ime == -ENOTSUP); + assert(yasl_surface_set_text_input(s, &(struct yasl_text_input){ + .text = "a\xc3\xa9z", .length = 4, .selection_begin = 2, .selection_end = 3}) == -EILSEQ); + assert(yasl_surface_set_text_input(s, &(struct yasl_text_input){ + .text = "\xc0\xaf", .length = 2}) == -EILSEQ); + assert(yasl_surface_set_text_input(s, &(struct yasl_text_input){ + .text = "ab", .length = 2, .selection_end = 3}) == -EINVAL); + assert(yasl_surface_set_text_input(s, &(struct yasl_text_input){.caret_x = 1e300}) == -EOVERFLOW); + assert(yasl_surface_set_text_input(s, NULL) == 0 || ime == -ENOTSUP); + + /* Arguments are checked before the backend sees them. */ + assert(yasl_surface_begin_move_resize(s, YASL_EDGE_TOP | YASL_EDGE_BOTTOM) == -EINVAL); + assert(yasl_surface_begin_move_resize(s, 16) == -EINVAL); + assert(yasl_surface_request(s, (enum yasl_request)9) == -EINVAL); + assert(yasl_surface_request_size(s, 0, 10) == -EINVAL); + assert(yasl_surface_set_cursor(s, (enum yasl_cursor)99) == -EINVAL); + int locked = yasl_surface_set_cursor(s, YASL_CURSOR_LOCKED); + assert(locked == ((features & YASL_FEATURE_LOCKED_CURSOR) ? 0 : -ENOTSUP)); + assert(yasl_surface_set_cursor(s, YASL_CURSOR_ARROW) == 0); + assert(yasl_drag_start(s, &(struct yasl_drag_image){.pixel_width = 4, .pixel_height = 4}) == -EINVAL); + uint32_t icon[4] = {0}; + assert(yasl_drag_start(s, &(struct yasl_drag_image){ + .pixels = icon, .pixel_width = 2, .pixel_height = 2, .hotspot_x = 2}) == -EINVAL); + assert(!yasl_popup_create(s, &(struct yasl_popup_options){.width = 10, .height = 10, + .anchor = YASL_EDGE_LEFT | YASL_EDGE_RIGHT}) && + errno == EINVAL); + assert(yasl_clipboard_set((enum yasl_selection)5, "x") == -EINVAL); + assert(yasl_clipboard_set(YASL_CLIPBOARD, NULL) == -EINVAL); + assert(yasl_clipboard_set(YASL_CLIPBOARD, "\xff") == -EILSEQ); + assert(yasl_clipboard_set(YASL_PRIMARY, "x") == + ((features & YASL_FEATURE_PRIMARY_SELECTION) ? 0 : -ENOTSUP)); 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)); + + /* Event conversion: positions are logical, text is owned until the next wait. */ + uint32_t real_scale = s->scale_120; + s->scale_120 = 240; + push(s, (struct yasl_private_event){ + .type = YASL_PRIVATE_EVENT_PEN, + .pen = {.device_id = 10, .x = 25, .y = 18.5, .pressure = 0.3, + .axes_valid = YASL_PRIVATE_PEN_PRESSURE, .action = YASL_PRIVATE_PEN_DOWN, + .in_contact = true}}); + assert(yasl_wait_event(&e, 0) == 1 && e.type == YASL_EVENT_PEN && e.action == YASL_PEN_DOWN); + assert(e.x == 12.5 && e.y == 9.25 && e.pen.device_id == 10 && e.pen.pressure == 0.3 && + (e.pen.axes & YASL_PEN_PRESSURE) && e.pen.in_contact); + push(s, (struct yasl_private_event){ + .type = YASL_PRIVATE_EVENT_POINTER_DOWN, + .pointer = {.pos = {20, 10}, .button = YASL_PRIVATE_BUTTON_SECONDARY, + .buttons = 4, .mods = YASL_PRIVATE_MOD_CTRL}}); + assert(yasl_wait_event(&e, 0) == 1 && e.type == YASL_EVENT_POINTER && + e.action == YASL_POINTER_DOWN && e.x == 10 && e.y == 5 && e.mods == YASL_MOD_CTRL && + e.pointer.button == YASL_BUTTON_SECONDARY && e.pointer.buttons == 4); + s->scale_120 = real_scale; + push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_TEXT, .text = {"owned", 5}}); 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_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(e.length == 5 && !strcmp(e.text, "owned")); + push(s, (struct yasl_private_event){ + .type = YASL_PRIVATE_EVENT_PASTE, + .paste = {.data = "", .request_id = 1, .status = YASL_PRIVATE_TRANSFER_UNAVAILABLE}}); + assert(yasl_wait_event(&e, 0) == 1 && e.type == YASL_EVENT_CLIPBOARD && + e.action == YASL_TRANSFER_UNAVAILABLE && e.length == 0); + /* Losing focus releases held input; activation itself comes from the window state. */ + push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_FOCUS, .focus = {.focused = false}}); + assert(yasl_wait_event(&e, 0) == 1 && e.type == YASL_EVENT_INPUT_RESET); + push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_FOCUS, .focus = {.focused = true}}); 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); + push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_DROP_HOVER, + .drop = {YASL_PRIVATE_DROP_LEAVE, {0, 0}}}); + assert(yasl_wait_event(&e, 0) == 1 && e.type == YASL_EVENT_DROP && e.action == YASL_DROP_LEAVE); + + /* A resize keeps the overlap of the pixels and zeroes the rest. */ 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); + info = yasl_surface_get_info(s); + } while ((info.width != 112 || info.height != 96) && yasl_time_ns() < resize_deadline); + assert(info.width == 112 && info.height == 96); + pixels = yasl_cpu_pixels(s, &w, &h); + assert(pixels && w == info.pixel_width && h == info.pixel_height); + assert(pixels[0] == 0xff102030u && pixels[(size_t)3 * w + 2] == 0xffaabbccu); + assert(pixels[(size_t)w * h - 1] == 0); + assert(yasl_cpu_present(s) == 0); + + /* The refresh callback is reserved for modal loops; ordinary waits deliver REFRESH. */ + s->refresh = refresh; + for (int i = 0; i < 5; i++) { + assert(yasl_cpu_pixels(s, &w, &h) && yasl_cpu_present(s) == 0); + assert(yasl_wait_event(&e, 20) >= 0); + } + assert(refreshes == 0); #if YASL_HAS_COCOA + s->display->modal = true; 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); + s->display->modal = false; +#endif + s->refresh = NULL; yasl_surface_destroy(s); - assert((yasl_init(&(struct yasl_options){.backend = backend}) < 0)); + assert(yasl_init(backend, NULL) == -EALREADY); s = yasl_surface_create(&(struct yasl_surface_options){.title = "shared backend remains", - .width = 64, - .height = 64, - .type = YASL_SURFACE_CPU, - .flags = 0}); + .width = 64, .height = 64}); 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(yasl_init(backend, NULL) == 0); + s = yasl_surface_create( + &(struct yasl_surface_options){.title = "reinitialized", .width = 64, .height = 64}); assert(s); yasl_shutdown(); puts("public surface lifecycle, pixels, ownership, input, wake and refresh passed"); |
