#define _POSIX_C_SOURCE 200809L #include #include #include #include #if YASL_HAS_WAYLAND #include #endif #if YASL_HAS_X11 #include #endif #if YASL_HAS_COCOA #include #endif #include "common/backend.h" #include #include #include #include #include #include #if YASL_HAS_COCOA 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); } static void *wake_thread(void *unused) { (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") == -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) { (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) { 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(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; } } 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(); 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")); /* 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(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); yasl_surface_destroy(s); 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; }