#define _POSIX_C_SOURCE 200809L #include "yasl/yasl.h" #include #include #include #include #include #include #include #include #include #include #include #include struct window { struct yasl_surface *surface; struct yasl_display *display; int draws, configures, closes; bool destroy_on_draw, destroy_on_configure; }; struct pump { struct yasl_loop *loop; struct yasl_display *displays[2]; int ticks; }; static int tick(int fd, short events, void *ud) { (void)events; uint64_t expirations; assert(read(fd, &expirations, sizeof(expirations)) == sizeof(expirations)); struct pump *p = ud; for (int i = 0; i < 2; i++) { if (p->displays[i]) { yasl_display_dispatch(p->displays[i]); yasl_display_flush(p->displays[i]); } } if (--p->ticks == 0) yasl_loop_quit(p->loop); return 0; } static void pump(struct yasl_loop *loop, struct yasl_display *a, struct yasl_display *b) { int timer = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); assert(timer >= 0); struct pump p = {.loop = loop, .displays = {a, b}, .ticks = 20}; struct itimerspec spec = {.it_value.tv_nsec = 10000000, .it_interval.tv_nsec = 10000000}; assert(timerfd_settime(timer, 0, &spec, NULL) == 0); int watch = yasl_loop_add_fd(loop, timer, POLLIN, tick, &p); assert(watch > 0); assert(yasl_loop_run(loop) == 0); assert(yasl_loop_del_fd(loop, watch) == 0); close(timer); } static void draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud) { struct window *w = ud; assert(s == w->surface); assert(frame->surface == s); assert(yasl_surface_display(s) == w->display); struct yasl_vector2i size = yasl_surface_size(s); assert(size.x == frame->target.size.x && size.y == frame->target.size.y); assert(frame->target.pixels); for (int i = 0; i < size.x * size.y; i++) frame->target.pixels[i] = 0xff4080c0; struct yasl_damage_list damage = {.n = -1}; assert(yasl_surface_present(s, &frame->target, &damage)); assert(!yasl_surface_present(s, &frame->target, &damage)); w->draws++; if (w->destroy_on_draw) { yasl_surface_destroy(s); w->surface = NULL; } } static void configure(struct yasl_surface *s, struct yasl_geometry geometry, void *ud) { struct window *w = ud; assert(w->surface == s); struct yasl_vector2i size = yasl_surface_size(s); assert(geometry.size.x == size.x && geometry.size.y == size.y); assert(geometry.scale_120 == yasl_surface_scale_120(s)); w->configures++; if (w->destroy_on_configure) { yasl_surface_destroy(s); w->surface = NULL; } } static void close_window(struct yasl_surface *s, void *ud) { struct window *w = ud; assert(s == w->surface); w->closes++; yasl_surface_destroy(s); w->surface = NULL; } static const struct yasl_surface_cbs callbacks = { .draw = draw, .configure = configure, .close = close_window, }; static struct window create(struct yasl_display *d) { struct yasl_surface_spec spec = {.title = "Backend test", .size = {160, 100}}; struct yasl_surface *s = yasl_surface_create_toplevel(d, &spec); assert(s); assert(yasl_surface_display(s) == d); return (struct window){.surface = s, .display = d}; } static void x11_behavior(struct yasl_loop *loop, struct yasl_display *d, struct window *w) { Display *native = yasl_display_get_native_handle(d); Window window = (Window)(uintptr_t)yasl_surface_get_native_handle(w->surface); assert(native && window); XSync(native, False); XImage *image = XGetImage(native, window, 0, 0, 1, 1, AllPlanes, ZPixmap); assert(image && (XGetPixel(image, 0, 0) & 0xffffff) == 0x4080c0); XDestroyImage(image); yasl_surface_set_title(w->surface, "Updated title"); char *title = NULL; assert(XFetchName(native, window, &title)); assert(strcmp(title, "Updated title") == 0); XFree(title); XResizeWindow(native, window, 200, 120); XFlush(native); pump(loop, d, NULL); assert(w->configures > 0); struct yasl_vector2i size = yasl_surface_size(w->surface); assert(size.x == 200 && size.y == 120); XEvent key = {0}; key.xkey = (XKeyEvent){.type = KeyPress, .display = native, .window = window, .root = DefaultRootWindow(native), .same_screen = True, .keycode = XKeysymToKeycode(native, XK_a)}; XSendEvent(native, window, False, KeyPressMask, &key); XFlush(native); yasl_display_copy(d, YASL_CLIPBOARD, "clipboard text"); yasl_display_paste(d, YASL_CLIPBOARD, w->surface); pump(loop, d, NULL); bool saw_key = false, saw_paste = false; struct yasl_input_event event; while (yasl_surface_input_poll(w->surface, &event)) { assert(event.surface == w->surface); if (event.kind == YASL_INPUT_KEY) { assert(event.key.codepoint == 'a'); assert(event.key.shifted == 'A'); assert(event.key.base == 30); saw_key = event.key.key == XK_a; } if (event.kind == YASL_INPUT_PASTE) { assert(event.paste.len == strlen("clipboard text")); assert(memcmp(event.paste.text, "clipboard text", event.paste.len) == 0); saw_paste = true; } } assert(saw_key && saw_paste); key.xclient = (XClientMessageEvent){.type = ClientMessage, .display = native, .window = window, .message_type = XInternAtom(native, "WM_PROTOCOLS", False), .format = 32, .data.l = {(long)XInternAtom(native, "WM_DELETE_WINDOW", False)}}; XSendEvent(native, window, False, NoEventMask, &key); XFlush(native); pump(loop, d, NULL); assert(w->closes == 1 && !w->surface); } int main(int argc, char **argv) { assert(argc >= 2); struct yasl_loop *loop = yasl_loop_create(); assert(loop); if (strcmp(argv[1], "failure") == 0) { struct yasl_display_spec spec = {.backend = (enum yasl_backend)atoi(argv[2])}; assert(!yasl_display_open(loop, &spec)); } else if (strcmp(argv[1], "mixed") == 0) { struct yasl_display_spec spec = {.backend = YASL_BACKEND_WAYLAND}; struct yasl_display *a = yasl_display_open(loop, &spec); spec.backend = YASL_BACKEND_X11; struct yasl_display *b = yasl_display_open(loop, &spec); assert(a && b); struct window wa = create(a), wb = create(b); yasl_surface_set_callbacks(wa.surface, &callbacks, &wa); yasl_surface_set_callbacks(wb.surface, &callbacks, &wb); pump(loop, a, b); assert(wa.draws && wb.draws); yasl_surface_destroy(wa.surface); yasl_surface_destroy(wb.surface); yasl_display_close(a); yasl_display_close(b); } else { assert(argc == 4); struct yasl_display_spec spec = {.backend = (enum yasl_backend)atoi(argv[2])}; struct yasl_display *d = strcmp(argv[1], "null") == 0 ? yasl_display_open(loop, NULL) : yasl_display_open(loop, &spec); assert(d); assert(yasl_display_backend(d) == (enum yasl_backend)atoi(argv[3])); struct window w = create(d); pump(loop, d, NULL); assert(w.draws == 0); yasl_surface_set_callbacks(w.surface, &callbacks, &w); yasl_surface_schedule_redraw(w.surface); pump(loop, d, NULL); assert(w.draws > 0); if (yasl_display_backend(d) == YASL_BACKEND_X11) x11_behavior(loop, d, &w); if (w.surface) yasl_surface_destroy(w.surface); w = create(d); w.destroy_on_draw = true; yasl_surface_set_callbacks(w.surface, &callbacks, &w); pump(loop, d, NULL); assert(w.draws && !w.surface); yasl_display_close(d); } yasl_loop_destroy(loop); puts("Backend test passed."); return 0; }