From 76073fe5fad8799e5850805f2f090af2cb62c741 Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Tue, 13 Oct 2026 23:41:31 +0300 Subject: Simplify the public event and CPU API Applications now need far less ceremony: hello.c is a 20-line loop that handles REFRESH, CLOSE and QUIT and draws into the surface's own pixels. - Fold GEOMETRY, STATE and FOCUS into one WINDOW event carrying size, scale and state; unchanged values send nothing. - Guarantee REFRESH once a window has a size and after every resize, never at size zero. - Merge DROP_HOVER into DROP with over/leave/data actions and give drag and drop actions enums. - Keep event strings valid until the next wait and drop yasl_event_release; empty or failed waits report NONE. - Replace copy-in presentation with a lent pixel buffer: yasl_cpu_pixels and yasl_cpu_present. - Remove yasl_poll_event, yasl_cpu_read_pixels, unused event fields and comments that restated names. - Wayland: resize floating windows in request_size instead of reporting it unsupported, and stop marking new GPU surfaces as Vulkan so yasl_vulkan_create_surface no longer fails with NATIVE_WINDOW_IN_USE. Also includes the in-progress public API refactor that was uncommitted in the tree (removal of the app/loop layer, new surface, GL and Vulkan entry points). Co-Authored-By: Claude Opus 5.5 --- tests/wayland/test_app_input.c | 174 ++++++++++++---------------------- tests/wayland/test_clipboard.c | 32 +++---- tests/wayland/test_cursor.c | 30 +++--- tests/wayland/test_input.c | 59 ++++++++++-- tests/wayland/test_keyboard.c | 63 ++++++------ tests/wayland/test_popup_positioner.c | 93 ++++++++++++++++++ tests/wayland/test_relative_pointer.c | 30 +++--- tests/wayland/test_state.c | 61 +++++++++--- tests/wayland/test_tablet.c | 115 ++++++++++++++++------ 9 files changed, 420 insertions(+), 237 deletions(-) create mode 100644 tests/wayland/test_popup_positioner.c (limited to 'tests/wayland') diff --git a/tests/wayland/test_app_input.c b/tests/wayland/test_app_input.c index 7e0e9a0..c8e4d2b 100644 --- a/tests/wayland/test_app_input.c +++ b/tests/wayland/test_app_input.c @@ -1,132 +1,78 @@ -/* An input event callback that destroys its own surface. The app runner - * must not touch the surface's entry after the callback, and must not - * deliver the surface's remaining input. Needs a Wayland compositor. */ -#include "platform/wayland/internal.h" -#include "yasl/app.h" +/* The public event queue must retain FIFO order and discard events targeted + * at a surface when that surface is destroyed. Native input is injected + * through the backend queue, without requiring a compositor. */ +#include "yasl/yasl.h" +#include "common/backend.h" #include -#include -#include +#include -struct test_state { - bool initialized; - int keys; - int buttons, motions, resets; - int end; +static struct input_queue *surface_queue(struct yasl_surface *s) +{ + return &s->input; +} + +static const struct yasl_private_backend_ops test_ops = { + .surface_input_queue = surface_queue, }; -static void push_key(struct yasl_surface *s) +static void setup(struct yasl_private_display *display, struct yasl_surface *surface) { - struct yasl_event ev = { - .surface = s, - .type = YASL_EVENT_KEY, - .key = { .key = YASL_KEY_A, .action = YASL_KEY_PRESS }, - }; - assert(input_queue_push(&((struct wayland_surface *)s)->input, &ev)); + *display = (struct yasl_private_display){.ops = &test_ops}; + *surface = (struct yasl_surface){.display = display, .scale_120 = 120}; + assert(yasl_private_events_open(display)); + assert(yasl_private_events_register(surface)); } -static enum yasl_app_result on_init(void *ud, struct yasl_display *d) +static void destroy_surface(struct yasl_surface *surface) { - struct test_state *state = ud; - state->initialized = true; - if (state->end == 3) { - /* With no event callback, a close request alone stops the runner. */ - struct yasl_surface *s = yasl_surface_create_toplevel(d, - &(struct yasl_window_spec){ .title = "App Close Test", .size = {64, 64} }); - assert(s); - yasl_surface_notify_close(s); - return YASL_APP_CONTINUE; - } - if (state->end) { - if (state->end == 1) - yasl_display_notify_event(d, YASL_EVENT_DISPLAY_QUIT); - else - yasl_display_notify_event(d, YASL_EVENT_DISPLAY_LOST); - return YASL_APP_CONTINUE; - } - struct yasl_surface *s = yasl_surface_create_toplevel(d, - &(struct yasl_window_spec){ .title = "App Input Test", .size = {64, 64} }); - assert(s); - struct input_queue *queue = &((struct wayland_surface *)s)->input; - input_queue_clear(queue); - struct yasl_event event = {.surface = s, .type = YASL_EVENT_POINTER_DOWN, - .pointer = {.device = YASL_PTR_DEV_ERASER, .button = YASL_BUTTON_PRIMARY, - .pos = {12, 34}, .pen = {.pressure = 50000, .tilt_x = -20, .tilt_y = 30}}}; - assert(input_queue_push(queue, &event)); - event.type = YASL_EVENT_POINTER_MOTION; - event.pointer.pos = (struct yasl_vector2i){15, 39}; - assert(input_queue_push(queue, &event)); - struct yasl_event reset = {.surface = s, .type = YASL_EVENT_INPUT_RESET}; - assert(input_queue_push(queue, &reset)); - event.pointer.device = YASL_PTR_DEV_PEN; - event.pointer.pos = (struct yasl_vector2i){100, 200}; - assert(input_queue_push(queue, &event)); - push_key(s); - push_key(s); - return YASL_APP_CONTINUE; + yasl_private_events_unregister(surface); + input_queue_clear(&surface->input); } -static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev) +static void test_fifo_and_destroy(void) { - struct test_state *state = ud; - if (ev->type == YASL_EVENT_POINTER_DOWN) { - assert(ev->pointer.device == YASL_PTR_DEV_ERASER); - assert(ev->pointer.button == YASL_BUTTON_PRIMARY); - assert(ev->pointer.pos.x == 12 && ev->pointer.pos.y == 34); - assert(ev->pointer.pen.pressure == 50000); - assert(ev->pointer.pen.tilt_x == -20 && ev->pointer.pen.tilt_y == 30); - state->buttons++; - } else if (ev->type == YASL_EVENT_POINTER_MOTION) { - if (!state->motions) { - assert(ev->pointer.device == YASL_PTR_DEV_ERASER); - assert(ev->pointer.delta.x == 3 && ev->pointer.delta.y == 5); - } else { - assert(ev->pointer.device == YASL_PTR_DEV_PEN); - assert(!ev->pointer.delta.x && !ev->pointer.delta.y); - } - assert(ev->pointer.pen.pressure == 50000); - state->motions++; - } else if (ev->type == YASL_EVENT_INPUT_RESET) { - state->resets++; - } else if (ev->type == YASL_EVENT_KEY) { - state->keys++; - yasl_surface_destroy(ev->surface); - } - return YASL_APP_CONTINUE; + struct yasl_private_display display; + struct yasl_surface surface; + setup(&display, &surface); + + assert(input_queue_push(&surface.input, &(struct yasl_private_event){ + .type = YASL_PRIVATE_EVENT_KEY, + .surface = &surface, + .key = {.key = YASL_KEY_A, .action = YASL_KEY_PRESS}, + })); + assert(input_queue_push(&surface.input, &(struct yasl_private_event){ + .type = YASL_PRIVATE_EVENT_TEXT, + .surface = &surface, + .text = {.data = "hello", .len = 5}, + })); + + struct yasl_private_event event; + assert(yasl_private_display_poll_event(&display, &event)); + assert(event.type == YASL_PRIVATE_EVENT_KEY && event.key.key == YASL_KEY_A); + assert(yasl_private_display_poll_event(&display, &event)); + assert(event.type == YASL_PRIVATE_EVENT_TEXT && event.text.len == 5); + assert(!strcmp(event.text.data, "hello")); + + /* Queue multiple events, then destroy the target before consumption. */ + assert(input_queue_push(&surface.input, &(struct yasl_private_event){ + .type = YASL_PRIVATE_EVENT_KEY, .surface = &surface, + .key = {.key = YASL_KEY_B, .action = YASL_KEY_PRESS}, + })); + assert(input_queue_push(&surface.input, &(struct yasl_private_event){ + .type = YASL_PRIVATE_EVENT_TEXT, .surface = &surface, + .text = {.data = "discard", .len = 7}, + })); + destroy_surface(&surface); + assert(!yasl_private_display_poll_event(&display, &event)); + yasl_private_events_close(&display); + + /* Public event names remain the event stream consumed by applications. */ + assert(YASL_EVENT_KEY != YASL_EVENT_TEXT); } int main(void) { - if (!getenv("WAYLAND_DISPLAY")) - return 77; - struct test_state state = {0}; - int res = yasl_run(&(struct yasl_app_desc){ - .app_id = "org.yasl.test.app-input", - .backend = YASL_BACKEND_WAYLAND, - .userdata = &state, - .init = on_init, - .event = on_event, - }); - /* A display variable may exist even when the test sandbox cannot connect - * to that compositor. Keep this as an integration test in that case. */ - if (res < 0 && !state.initialized && - (errno == EACCES || errno == EPERM || errno == ENOENT || - errno == ECONNREFUSED || errno == ENODEV)) - return 77; - assert(res == 0); - assert(state.keys == 1); - assert(state.buttons == 1 && state.motions == 2 && state.resets == 1); - for (int end = 1; end <= 2; end++) { - state = (struct test_state){.end = end}; - res = yasl_run(&(struct yasl_app_desc){ - .backend = YASL_BACKEND_WAYLAND, .userdata = &state, .init = on_init, - }); - assert(end == 1 ? res == 0 : res < 0); - } - state = (struct test_state){.end = 3}; - res = yasl_run(&(struct yasl_app_desc){ - .backend = YASL_BACKEND_WAYLAND, .userdata = &state, .init = on_init, - }); - assert(res == 0); + test_fifo_and_destroy(); return 0; } diff --git a/tests/wayland/test_clipboard.c b/tests/wayland/test_clipboard.c index 1778ad2..0ee74b6 100644 --- a/tests/wayland/test_clipboard.c +++ b/tests/wayland/test_clipboard.c @@ -6,7 +6,7 @@ struct received { int count; size_t len; uint64_t request_id; - enum yasl_transfer_status status; + enum yasl_private_transfer_status status; struct clipboard *clipboard; bool forget_on_receive; char text[32]; @@ -14,12 +14,12 @@ struct received { static void receive(const char *text, size_t len, bool uri_list, struct wl_surface *surface, uint64_t request_id, - enum yasl_transfer_status status, void *ud) + enum yasl_private_transfer_status status, void *ud) { (void)surface; struct received *r = ud; assert(!uri_list && len < sizeof(r->text)); - if (status == YASL_TRANSFER_OK) { + if (status == YASL_PRIVATE_TRANSFER_OK) { assert(text); memcpy(r->text, text, len); r->text[len] = '\0'; @@ -39,7 +39,7 @@ static void receive(const char *text, size_t len, bool uri_list, int main(void) { struct received received = {0}; - struct clipboard c = {.loop = yasl_loop_create(), .receive = receive, .ud = &received}; + struct clipboard c = {.loop = yasl_private_loop_create(), .receive = receive, .ud = &received}; received.clipboard = &c; assert(c.loop); const struct { const char *bytes; size_t len; bool valid; } cases[] = { @@ -55,19 +55,19 @@ int main(void) assert(pipe(fds) == 0); struct transfer *transfer = start_transfer_text(&c, fds[0], false, false, c.text); assert(transfer); - yasl_timer_id timeout_id = transfer->timeout; - assert(timeout_id && yasl_loop_timer_armed(c.loop, timeout_id)); + yasl_private_timer_id timeout_id = transfer->timeout; + assert(timeout_id && yasl_private_loop_timer_armed(c.loop, timeout_id)); transfer->paste = true; transfer->request_id = 42; assert(write(fds[1], cases[i].bytes, cases[i].len) == (ssize_t)cases[i].len); close(fds[1]); for (int steps = 0; c.transfers && steps < 10; steps++) - assert(yasl_loop_step(c.loop, 0) >= 0); + assert(yasl_private_loop_step(c.loop, 0) >= 0); assert(!c.transfers); - assert(!yasl_loop_timer_armed(c.loop, timeout_id)); + assert(!yasl_private_loop_timer_armed(c.loop, timeout_id)); assert(received.count == 1); assert(received.request_id == 42); - assert(received.status == (cases[i].valid ? YASL_TRANSFER_OK : YASL_TRANSFER_FAILED)); + assert(received.status == (cases[i].valid ? YASL_PRIVATE_TRANSFER_OK : YASL_PRIVATE_TRANSFER_FAILED)); if (cases[i].valid) { assert(received.len == cases[i].len); assert(memcmp(received.text, cases[i].bytes, received.len) == 0); @@ -79,8 +79,8 @@ int main(void) assert(pipe(cancel_fds) == 0); struct transfer *pending = start_transfer_text(&c, cancel_fds[0], false, false, NULL); assert(pending); - yasl_timer_id cancel_timeout_id = pending->timeout; - assert(cancel_timeout_id && yasl_loop_timer_armed(c.loop, cancel_timeout_id)); + yasl_private_timer_id cancel_timeout_id = pending->timeout; + assert(cancel_timeout_id && yasl_private_loop_timer_armed(c.loop, cancel_timeout_id)); pending->paste = true; pending->request_id = 99; struct wl_surface *target = (struct wl_surface *)(uintptr_t)1; @@ -88,7 +88,7 @@ int main(void) clipboard_forget_surface(&c, target); close(cancel_fds[1]); assert(!c.transfers && received.count == 0); - assert(!yasl_loop_timer_armed(c.loop, cancel_timeout_id)); + assert(!yasl_private_loop_timer_armed(c.loop, cancel_timeout_id)); received.forget_on_receive = true; int timeout_fds[2]; @@ -99,12 +99,12 @@ int main(void) timed->request_id = 100; struct wl_surface *timeout_target = (struct wl_surface *)(uintptr_t)2; timed->drop_surface = timeout_target; - yasl_timer_id timeout_id = timed->timeout; - assert(yasl_loop_del_timer(c.loop, timeout_id)); + yasl_private_timer_id timeout_id = timed->timeout; + assert(yasl_private_loop_del_timer(c.loop, timeout_id)); (void)transfer_timeout(timed, timeout_id, CLIPBOARD_TRANSFER_TIMEOUT_NS); close(timeout_fds[1]); assert(!c.transfers && received.count == 1); - assert(received.request_id == 100 && received.status == YASL_TRANSFER_FAILED); - yasl_loop_destroy(c.loop); + assert(received.request_id == 100 && received.status == YASL_PRIVATE_TRANSFER_FAILED); + yasl_private_loop_destroy(c.loop); return 0; } diff --git a/tests/wayland/test_cursor.c b/tests/wayland/test_cursor.c index eb729a3..3425821 100644 --- a/tests/wayland/test_cursor.c +++ b/tests/wayland/test_cursor.c @@ -51,9 +51,9 @@ static uint32_t pointer_version(struct wl_pointer *pointer) int main(void) { - struct wayland_display display = {.base.ops = &yasl_wayland_backend}; + struct wayland_display display = {.base.ops = &yasl_private_wayland_backend}; struct wayland_surface surface = {.base.display = &display.base, .conn = &display, - .scale_factor = 1, .cursor = YASL_CURSOR_TEXT}; + .scale_factor = 1, .cursor = YASL_PRIVATE_CURSOR_TEXT}; surface.surface = (struct wl_surface *)&surface; wl_surface_set_user_data(surface.surface, &surface); display.surfaces = &surface; @@ -64,7 +64,7 @@ int main(void) tool.proxy = (struct zwp_tablet_tool_v2 *)&tool; /* The example selects hidden before either input device has entered. */ - yasl_surface_set_cursor(&surface.base, YASL_CURSOR_HIDDEN); + yasl_private_surface_set_cursor(&surface.base, YASL_PRIVATE_CURSOR_HIDDEN); assert(!pointer_hides && !tool_hides); pointer_enter(&seat, seat.pointer, 10, surface.surface, 0, 0); assert(pointer_hides == 1 && pointer_serial == 10); @@ -76,14 +76,16 @@ int main(void) pointer_frame(&seat, seat.pointer); tablet_tool_motion(&tool, tool.proxy, wl_fixed_from_int(56), wl_fixed_from_int(78)); tool_frame(&tool, tool.proxy, 0); - struct yasl_event event; - assert(yasl_surface_poll_event(&surface.base, &event)); - assert(event.type == YASL_EVENT_POINTER_MOTION && event.pointer.device == YASL_PTR_DEV_MOUSE); + struct yasl_private_event event; + assert(yasl_private_surface_poll_event(&surface.base, &event)); + assert(event.type == YASL_PRIVATE_EVENT_POINTER_MOTION && event.pointer.device == YASL_PRIVATE_PTR_DEV_MOUSE); assert(event.pointer.pos.x == 12 && event.pointer.pos.y == 34); - assert(yasl_surface_poll_event(&surface.base, &event)); - assert(event.type == YASL_EVENT_POINTER_MOTION && event.pointer.device == YASL_PTR_DEV_PEN); + assert(yasl_private_surface_poll_event(&surface.base, &event)); + assert(event.type == YASL_PRIVATE_EVENT_PEN && event.pen.action == YASL_PRIVATE_PEN_ENTER); + assert(yasl_private_surface_poll_event(&surface.base, &event)); + assert(event.type == YASL_PRIVATE_EVENT_POINTER_MOTION && event.pointer.device == YASL_PRIVATE_PTR_DEV_PEN); assert(event.pointer.pos.x == 56 && event.pointer.pos.y == 78); - assert(!yasl_surface_poll_event(&surface.base, &event)); + assert(!yasl_private_surface_poll_event(&surface.base, &event)); /* Each new entry needs a request with its own serial. */ pointer_leave(&seat, seat.pointer, 12, surface.surface); @@ -98,20 +100,20 @@ int main(void) /* Restore a visible shape on both currently focused devices. */ pointer_device = seat.cursor_shape_dev = (struct wp_cursor_shape_device_v1 *)&seat; tool.shape_dev = (struct wp_cursor_shape_device_v1 *)&tool; - yasl_surface_set_cursor(&surface.base, YASL_CURSOR_ARROW); + yasl_private_surface_set_cursor(&surface.base, YASL_PRIVATE_CURSOR_ARROW); assert(pointer_shapes == 1 && tool_shapes == 1); assert(pointer_shape == WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); assert(tool_shape == WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT); - yasl_surface_set_cursor(&surface.base, YASL_CURSOR_HIDDEN); + yasl_private_surface_set_cursor(&surface.base, YASL_PRIVATE_CURSOR_HIDDEN); assert(pointer_hides == 3 && tool_hides == 3); assert(pointer_shapes == 1 && tool_shapes == 1); /* Updating an unfocused surface must not alter the active cursor. */ struct wayland_surface other = {.base.display = &display.base, .conn = &display}; - yasl_surface_set_cursor(&other.base, YASL_CURSOR_CROSSHAIR); + yasl_private_surface_set_cursor(&other.base, YASL_PRIVATE_CURSOR_CROSSHAIR); assert(pointer_shapes == 1 && tool_shapes == 1); - yasl_surface_set_cursor(&surface.base, (enum yasl_cursor)-1); - yasl_surface_set_cursor(&surface.base, (enum yasl_cursor)(YASL_CURSOR_HIDDEN + 1)); + yasl_private_surface_set_cursor(&surface.base, (enum yasl_private_cursor)-1); + yasl_private_surface_set_cursor(&surface.base, (enum yasl_private_cursor)(YASL_PRIVATE_CURSOR_HIDDEN + 1)); assert(pointer_hides == 3 && tool_hides == 3); assert(pointer_shapes == 1 && tool_shapes == 1); input_queue_clear(&surface.input); diff --git a/tests/wayland/test_input.c b/tests/wayland/test_input.c index f6112b6..2b5592a 100644 --- a/tests/wayland/test_input.c +++ b/tests/wayland/test_input.c @@ -1,17 +1,35 @@ #include "platform/wayland/internal.h" #include +#include +#include + +static void test_ime_capability(void) +{ + struct wayland_display display = {0}; + struct wayland_surface w = {.conn = &display}; + assert(!(wayland_display_caps(&display) & YASL_PRIVATE_CAP_TEXT_INPUT)); + errno = 0; + assert(!wayland_surface_update_text_input(&w) && errno == ENOTSUP); + display.text_input_mgr = (struct zwp_text_input_manager_v3 *)&display; + assert(wayland_display_caps(&display) & YASL_PRIVATE_CAP_TEXT_INPUT); + w.base.text_input_active = true; + w.base.text_purpose = 1; /* Public YASL_TEXT_PASSWORD. */ + /* A supported compositor can accept the requested state before a seat + * or keyboard focus has arrived. The enter callback applies it later. */ + assert(wayland_surface_update_text_input(&w)); +} static void test_popup_bounds(void) { struct seat seat = {.input_serial = 1}; - struct wayland_display d = {.base.ops = &yasl_wayland_backend, .seats = &seat}; + struct wayland_display d = {.base.ops = &yasl_private_wayland_backend, .seats = &seat}; struct wayland_surface w = {.base.display = &d.base, .conn = &d}; w.parent = &w; w.content.x = w.content.y = 1; /* Invalid bounds must be rejected before accessing any protocol object. */ w.xdg_surface = (struct xdg_surface *)&w; w.popup = (struct xdg_popup *)&w; - struct yasl_popup_spec specs[] = { + struct yasl_private_popup_spec specs[] = { {.size = {100, 100}, .content_offset = {INT_MAX, 0}, .content_size = {1, 0}}, {.size = {100, 100}, .content_offset = {0, INT_MAX}, .content_size = {0, 1}}, {.size = {100, 100}, .content_offset = {1, 0}, .content_size = {INT_MAX, 0}}, @@ -20,16 +38,18 @@ static void test_popup_bounds(void) {.size = {100, 100}, .anchor = {0, INT_MIN, 1, 1}}, }; for (size_t i = 0; i < sizeof(specs) / sizeof(specs[0]); i++) { - assert(!yasl_surface_create_popup(&w.base, &specs[i])); - assert(!yasl_surface_popup_reposition(&w.base, &specs[i])); + assert(!yasl_private_surface_create_popup(&w.base, &specs[i])); + assert(!yasl_private_surface_popup_reposition(&w.base, &specs[i])); } } int main(void) { + test_ime_capability(); test_popup_bounds(); struct wayland_surface w = {0}; struct seat seat = {.ime_focus = &w}; + w.base.text_input_active = true; wayland_surface_set_callbacks(&w, NULL, NULL); for (int i = 0; i < 600; i++) { char text[32]; @@ -41,11 +61,38 @@ int main(void) for (int i = 0; i < 600; i++) { char text[32]; snprintf(text, sizeof(text), "commit-%d", i); - struct yasl_event event; + struct yasl_private_event event; assert(wayland_surface_poll_event(&w, &event)); - assert(event.type == YASL_EVENT_TEXT && event.surface == &w.base); + assert(event.type == YASL_PRIVATE_EVENT_TEXT && event.surface == &w.base); assert(strcmp(event.text.data, text) == 0); } + w.base.text_input_active = true; + w.base.text_surrounding = strdup("ab\xf0\x9f\x98\x80" "cd"); + w.base.text_length = 8; + w.base.text_begin = 2; + w.base.text_end = 6; + w.base.text_revision = 41; + w.base.text_generation = 9; + text_input_listener.delete_surrounding_text(&seat, NULL, 2, 2); + text_input_listener.commit_string(&seat, NULL, "X"); + text_input_listener.done(&seat, NULL, 601); + struct yasl_private_event edit; + assert(wayland_surface_poll_event(&w, &edit)); + assert(edit.type == YASL_PRIVATE_EVENT_TEXT_EDIT && edit.surface == &w.base); + assert(strcmp(edit.text_edit.data, "X") == 0 && edit.text_edit.len == 1); + assert(edit.text_edit.begin == 2 && edit.text_edit.end == 8); + assert(edit.text_edit.revision == 41 && edit.text_edit.generation == 9); + struct yasl_private_event committed; + assert(!wayland_surface_poll_event(&w, &committed)); + + /* Deletion without a commit replaces the range with the empty string. */ + text_input_listener.delete_surrounding_text(&seat, NULL, 2, 2); + text_input_listener.done(&seat, NULL, 602); + assert(wayland_surface_poll_event(&w, &edit)); + assert(edit.type == YASL_PRIVATE_EVENT_TEXT_EDIT && edit.text_edit.len == 0); + assert(edit.text_edit.data[0] == '\0' && edit.text_edit.begin == 2 && edit.text_edit.end == 8); + assert(!wayland_surface_poll_event(&w, &committed)); input_queue_clear(&w.input); + free(w.base.text_surrounding); return 0; } diff --git a/tests/wayland/test_keyboard.c b/tests/wayland/test_keyboard.c index bce0a0c..ae4647f 100644 --- a/tests/wayland/test_keyboard.c +++ b/tests/wayland/test_keyboard.c @@ -1,18 +1,18 @@ #include "../../src/platform/wayland/keyboard.c" #include -static struct yasl_event next(struct wayland_surface *w, enum yasl_event_type type) +static struct yasl_private_event next(struct wayland_surface *w, enum yasl_private_event_type type) { - struct yasl_event ev; - assert(yasl_surface_poll_event(&w->base, &ev)); + struct yasl_private_event ev; + assert(yasl_private_surface_poll_event(&w->base, &ev)); assert(ev.type == type && ev.surface == &w->base); return ev; } static void empty(struct wayland_surface *w) { - struct yasl_event ev; - assert(!yasl_surface_poll_event(&w->base, &ev)); + struct yasl_private_event ev; + assert(!yasl_private_surface_poll_event(&w->base, &ev)); } static void layout(struct seat *s, const char *name, const char *variant) @@ -43,7 +43,7 @@ static void key(struct seat *s, uint32_t code, bool down) int main(void) { - struct wayland_display d = {.base.ops = &yasl_wayland_backend}; + struct wayland_display d = {.base.ops = &yasl_private_wayland_backend}; d.xkb_ctx = xkb_context_new(0); assert(d.xkb_ctx); struct wayland_surface w = {.base.display = &d.base, .conn = &d}; @@ -59,65 +59,66 @@ int main(void) xkb_compose_table_unref(table); layout(&s, "us", NULL); key(&s, KEY_A, true); - struct yasl_event ev = next(&w, YASL_EVENT_KEY); + struct yasl_private_event ev = next(&w, YASL_PRIVATE_EVENT_KEY); assert(ev.key.key == YASL_KEY_A && ev.key.native_code == KEY_A); assert(ev.key.codepoint == 'a' && ev.key.shifted == 'A' && !ev.key.mods); - assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "a") == 0); + assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "a") == 0); modifiers(&s, XKB_MOD_NAME_SHIFT); key(&s, KEY_A, false); /* Modifier changes cannot change physical identity. */ - ev = next(&w, YASL_EVENT_KEY); + ev = next(&w, YASL_PRIVATE_EVENT_KEY); assert(ev.key.key == YASL_KEY_A && ev.key.action == YASL_KEY_RELEASE); - assert(ev.key.mods == YASL_MOD_SHIFT); + assert(ev.key.mods == YASL_PRIVATE_MOD_SHIFT); empty(&w); key(&s, KEY_A, true); - next(&w, YASL_EVENT_KEY); - assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "A") == 0); + next(&w, YASL_PRIVATE_EVENT_KEY); + assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "A") == 0); modifiers(&s, XKB_MOD_NAME_CAPS); key(&s, KEY_A, true); - assert(next(&w, YASL_EVENT_KEY).key.mods == YASL_MOD_CAPS); - assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "A") == 0); + assert(next(&w, YASL_PRIVATE_EVENT_KEY).key.mods == YASL_PRIVATE_MOD_CAPS); + assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "A") == 0); const char *shortcuts[] = {XKB_MOD_NAME_CTRL, XKB_MOD_NAME_ALT, XKB_MOD_NAME_LOGO}; for (size_t i = 0; i < sizeof(shortcuts)/sizeof(shortcuts[0]); i++) { modifiers(&s, shortcuts[i]); key(&s, KEY_A, true); - next(&w, YASL_EVENT_KEY); + next(&w, YASL_PRIVATE_EVENT_KEY); empty(&w); /* Shortcut characters never leak into committed text. */ } layout(&s, "fr", NULL); key(&s, KEY_A, true); - ev = next(&w, YASL_EVENT_KEY); + ev = next(&w, YASL_PRIVATE_EVENT_KEY); assert(ev.key.key == YASL_KEY_A && ev.key.codepoint == 'q'); - assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "q") == 0); + assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "q") == 0); layout(&s, "de", NULL); modifiers(&s, "Mod5"); /* AltGr is text input, not an Alt shortcut. */ key(&s, KEY_E, true); - assert(next(&w, YASL_EVENT_KEY).key.key == YASL_KEY_E); - assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "€") == 0); + assert(next(&w, YASL_PRIVATE_EVENT_KEY).key.key == YASL_KEY_E); + assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "€") == 0); layout(&s, "us", "intl"); key(&s, KEY_APOSTROPHE, true); - next(&w, YASL_EVENT_KEY); + next(&w, YASL_PRIVATE_EVENT_KEY); empty(&w); key(&s, KEY_E, true); - next(&w, YASL_EVENT_KEY); - assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "é") == 0); + next(&w, YASL_PRIVATE_EVENT_KEY); + assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "é") == 0); assert(keyboard_dispatch_key(&s, &w, KEY_E, YASL_KEY_REPEAT)); - assert(next(&w, YASL_EVENT_KEY).key.action == YASL_KEY_REPEAT); - assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "e") == 0); + assert(next(&w, YASL_PRIVATE_EVENT_KEY).key.action == YASL_KEY_REPEAT); + assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "e") == 0); key(&s, KEY_APOSTROPHE, true); - next(&w, YASL_EVENT_KEY); + next(&w, YASL_PRIVATE_EVENT_KEY); keyboard_reset_keys(&s, &w); key(&s, KEY_E, true); - next(&w, YASL_EVENT_KEY); - assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "e") == 0); + next(&w, YASL_PRIVATE_EVENT_KEY); + assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "e") == 0); key(&s, KEY_APOSTROPHE, true); - next(&w, YASL_EVENT_KEY); + next(&w, YASL_PRIVATE_EVENT_KEY); s.ime_focus = &w; + w.base.text_input_active = true; text_input_listener.commit_string(&s, NULL, "字"); text_input_listener.done(&s, NULL, 0); - assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "字") == 0); + assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "字") == 0); key(&s, KEY_E, true); - next(&w, YASL_EVENT_KEY); - assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "e") == 0); + next(&w, YASL_PRIVATE_EVENT_KEY); + assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "e") == 0); empty(&w); xkb_compose_state_unref(s.compose); xkb_state_unref(s.xkb_state); diff --git a/tests/wayland/test_popup_positioner.c b/tests/wayland/test_popup_positioner.c new file mode 100644 index 0000000..86778f6 --- /dev/null +++ b/tests/wayland/test_popup_positioner.c @@ -0,0 +1,93 @@ +/* Capture xdg_positioner requests to verify explicit placement and the legacy + * beside mapping without requiring a compositor. */ +#include "platform/wayland/internal.h" +#include + +struct recorded_positioner { + int width, height; + int rect_x, rect_y, rect_width, rect_height; + uint32_t anchor, gravity, constraints; + bool reactive; +}; + +static struct recorded_positioner record; +static uint64_t fake_positioner_storage[8]; +#define FAKE_POSITIONER ((struct xdg_positioner *)fake_positioner_storage) +static struct xdg_positioner *fake_create(struct xdg_wm_base *base) +{ (void)base; record = (struct recorded_positioner){0}; return FAKE_POSITIONER; } +static void fake_size(struct xdg_positioner *pos, int32_t width, int32_t height) +{ assert(pos == FAKE_POSITIONER); record.width = width; record.height = height; } +static void fake_rect(struct xdg_positioner *pos, int32_t x, int32_t y, int32_t width, int32_t height) +{ assert(pos == FAKE_POSITIONER); record.rect_x=x; record.rect_y=y; record.rect_width=width; record.rect_height=height; } +static void fake_anchor(struct xdg_positioner *pos, uint32_t value) +{ assert(pos == FAKE_POSITIONER); record.anchor = value; } +static void fake_gravity(struct xdg_positioner *pos, uint32_t value) +{ assert(pos == FAKE_POSITIONER); record.gravity = value; } +static void fake_constraints(struct xdg_positioner *pos, uint32_t value) +{ assert(pos == FAKE_POSITIONER); record.constraints = value; } +static uint32_t fake_version(struct xdg_positioner *pos) +{ assert(pos == FAKE_POSITIONER); return XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION; } +static void fake_reactive(struct xdg_positioner *pos) +{ assert(pos == FAKE_POSITIONER); record.reactive = true; } + +#define xdg_wm_base_create_positioner fake_create +#define xdg_positioner_set_size fake_size +#define xdg_positioner_set_anchor_rect fake_rect +#define xdg_positioner_set_anchor fake_anchor +#define xdg_positioner_set_gravity fake_gravity +#define xdg_positioner_set_constraint_adjustment fake_constraints +#define xdg_positioner_get_version fake_version +#define xdg_positioner_set_reactive fake_reactive +#include "../../src/platform/wayland/popup.c" + +int main(void) +{ + struct wayland_display display = {.wm_base = (struct xdg_wm_base *)&record}; + struct wayland_surface parent = { + .scale_120 = 120, + .content = {.x = 3, .y = 4}, + }; + struct wayland_surface popup = { + .conn = &display, + .parent = &parent, + .scale_120 = 120, + .content = {.width = 48, .height = 36}, + }; + struct yasl_private_popup_spec spec = { + .size = {64, 48}, + .anchor = {13, 24, 8, 6}, + .beside = true, + }; + + struct xdg_positioner *positioner = popup_positioner(&popup, &spec); + assert(positioner == FAKE_POSITIONER); + assert(record.width == 48 && record.height == 36); + assert(record.rect_x == 10 && record.rect_y == 20 && + record.rect_width == 8 && record.rect_height == 6); + assert(record.anchor == XDG_POSITIONER_ANCHOR_TOP_RIGHT); + assert(record.gravity == XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT); + assert(record.constraints == (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y)); + assert(record.reactive); + + spec.positioner_explicit = true; + spec.anchor_edges = XDG_POSITIONER_ANCHOR_NONE; + spec.gravity = XDG_POSITIONER_GRAVITY_RIGHT; + spec.constraints = XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X | + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y; + assert(popup_spec_valid(&parent, &spec)); + positioner = popup_positioner(&popup, &spec); + assert(positioner == FAKE_POSITIONER); + assert(record.anchor == XDG_POSITIONER_ANCHOR_NONE); + assert(record.gravity == XDG_POSITIONER_GRAVITY_RIGHT); + assert(record.constraints == spec.constraints); + + spec.anchor_edges = XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT + 1; + assert(!popup_spec_valid(&parent, &spec)); + spec.anchor_edges = XDG_POSITIONER_ANCHOR_NONE; + spec.constraints = 1u << 9; + assert(!popup_spec_valid(&parent, &spec)); + return 0; +} diff --git a/tests/wayland/test_relative_pointer.c b/tests/wayland/test_relative_pointer.c index bcc41e3..d44238d 100644 --- a/tests/wayland/test_relative_pointer.c +++ b/tests/wayland/test_relative_pointer.c @@ -65,10 +65,10 @@ static void shape_device_destroy(struct wp_cursor_shape_device_v1 *device) { ass #define wl_pointer_release pointer_release #include "../../src/platform/wayland/pointer.c" -static struct yasl_event next_event(struct wayland_surface *w) +static struct yasl_private_event next_event(struct wayland_surface *w) { - struct yasl_event event; - assert(yasl_surface_poll_event(&w->base, &event)); + struct yasl_private_event event; + assert(yasl_private_surface_poll_event(&w->base, &event)); return event; } @@ -85,9 +85,9 @@ static void relative_move(struct seat *s, double dx, double dy) int main(void) { - struct wayland_display display = {.base.ops = &yasl_wayland_backend}; + struct wayland_display display = {.base.ops = &yasl_private_wayland_backend}; struct wayland_surface surface = {.base.display = &display.base, .conn = &display, - .scale_factor = 2, .cursor = YASL_CURSOR_TEXT}; + .scale_factor = 2, .cursor = YASL_PRIVATE_CURSOR_TEXT}; surface.surface = (struct wl_surface *)&surface; wl_surface_set_user_data(surface.surface, &surface); display.surfaces = &surface; @@ -95,10 +95,10 @@ int main(void) seat.pointer = (struct wl_pointer *)&seat; seat.cursor_shape_dev = (struct wp_cursor_shape_device_v1 *)&seat; display.seats = &seat; - struct yasl_event event; + struct yasl_private_event event; /* Without both protocols the surface stays in absolute mode. */ - assert(!yasl_surface_set_pointer_mode(&surface.base, YASL_POINTER_RELATIVE)); + assert(!yasl_private_surface_set_pointer_mode(&surface.base, YASL_PRIVATE_POINTER_RELATIVE)); assert(!surface.relative && !locks); display.relative_pointer_mgr = (struct zwp_relative_pointer_manager_v1 *)&display; display.pointer_constraints = (struct zwp_pointer_constraints_v1 *)&display; @@ -107,10 +107,10 @@ int main(void) pointer_frame(&seat, seat.pointer); assert(pointer_shapes == 1); event = next_event(&surface); - assert(event.type == YASL_EVENT_POINTER_MOTION && event.pointer.pos.x == 10); + assert(event.type == YASL_PRIVATE_EVENT_POINTER_MOTION && event.pointer.pos.x == 10); /* Relative mode locks persistently on the pointer's seat and hides the cursor. */ - assert(yasl_surface_set_pointer_mode(&surface.base, YASL_POINTER_RELATIVE)); + assert(yasl_private_surface_set_pointer_mode(&surface.base, YASL_PRIVATE_POINTER_RELATIVE)); assert(locks == 1 && locked_surface == surface.surface && locked_wl_pointer == seat.pointer); assert(lock_lifetime == ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); assert(pointer_hides == 1 && surface.lock_seat == &seat); @@ -121,14 +121,14 @@ int main(void) event = next_event(&surface); assert(event.pointer.pos.x == 14 && event.pointer.pos.y == 10); assert(event.pointer.delta.x == 4 && event.pointer.delta.y == 0); - assert(!yasl_surface_poll_event(&surface.base, &event)); + assert(!yasl_private_surface_poll_event(&surface.base, &event)); /* Locked: relative samples in device pixels, position frozen at the lock. */ pointer_locked(&surface, surface.pointer_lock); relative_move(&seat, 3.5, -2); relative_move(&seat, 1000, 0); event = next_event(&surface); - assert(event.type == YASL_EVENT_POINTER_MOTION); + assert(event.type == YASL_PRIVATE_EVENT_POINTER_MOTION); assert(event.pointer.pos.x == 14 && event.pointer.pos.y == 10); assert(event.pointer.delta.x == 7 && event.pointer.delta.y == -4); event = next_event(&surface); @@ -137,7 +137,7 @@ int main(void) /* Deactivation (e.g. focus loss) returns to absolute motion without unlocking. */ pointer_unlocked(&surface, surface.pointer_lock); relative_move(&seat, 5, 5); - assert(!yasl_surface_poll_event(&surface.base, &event)); + assert(!yasl_private_surface_poll_event(&surface.base, &event)); move_to(&seat, 8, 5); event = next_event(&surface); assert(event.pointer.delta.x == 2 && event.pointer.delta.y == 0); @@ -145,12 +145,12 @@ int main(void) pointer_locked(&surface, surface.pointer_lock); /* Returning to normal destroys the lock and restores the chosen cursor. */ - assert(yasl_surface_set_pointer_mode(&surface.base, YASL_POINTER_NORMAL)); + assert(yasl_private_surface_set_pointer_mode(&surface.base, YASL_PRIVATE_POINTER_NORMAL)); assert(unlocks == 1 && !surface.pointer_lock && !surface.input.relative); assert(pointer_shapes == 2); - assert(yasl_surface_set_pointer_mode(&surface.base, YASL_POINTER_NORMAL)); + assert(yasl_private_surface_set_pointer_mode(&surface.base, YASL_PRIVATE_POINTER_NORMAL)); assert(unlocks == 1); - assert(yasl_surface_set_pointer_mode(&surface.base, YASL_POINTER_RELATIVE)); + assert(yasl_private_surface_set_pointer_mode(&surface.base, YASL_PRIVATE_POINTER_RELATIVE)); assert(locks == 2 && pointer_hides == 2); pointer_locked(&surface, surface.pointer_lock); diff --git a/tests/wayland/test_state.c b/tests/wayland/test_state.c index 2da26e5..1ce9189 100644 --- a/tests/wayland/test_state.c +++ b/tests/wayland/test_state.c @@ -1,6 +1,20 @@ -#include "../../src/platform/wayland/shell.c" +#include "platform/wayland/internal.h" #include +static uint32_t acknowledged_serial; +static unsigned acknowledge_count; +static void test_ack_configure(struct xdg_surface *surface, uint32_t serial); +#define xdg_surface_ack_configure test_ack_configure +#include "../../src/platform/wayland/shell.c" +#undef xdg_surface_ack_configure + +static void test_ack_configure(struct xdg_surface *surface, uint32_t serial) +{ + (void)surface; + acknowledged_serial = serial; + acknowledge_count++; +} + struct notifications { int count; uint32_t state; @@ -9,7 +23,7 @@ struct notifications { static void state_changed(struct yasl_surface *surface, uint32_t state, void *ud) { struct notifications *notifications = ud; - assert(yasl_surface_state(surface) == state); + assert(yasl_private_surface_state(surface) == state); notifications->count++; notifications->state = state; } @@ -24,33 +38,33 @@ static void configure(struct wayland_surface *surface, uint32_t *states, size_t int main(void) { struct notifications notifications = {0}; - struct wayland_surface surface = {.base.state = YASL_STATE_CSD}; - wayland_surface_set_callbacks(&surface, &(struct yasl_window_cbs){ + struct wayland_surface surface = {.base.state = YASL_PRIVATE_STATE_CSD}; + wayland_surface_set_callbacks(&surface, &(struct yasl_private_window_cbs){ .state = state_changed}, ¬ifications); - assert(yasl_surface_state(&surface.base) == YASL_STATE_CSD); + assert(yasl_private_surface_state(&surface.base) == YASL_PRIVATE_STATE_CSD); uint32_t fullscreen[] = {XDG_TOPLEVEL_STATE_FULLSCREEN, XDG_TOPLEVEL_STATE_ACTIVATED, XDG_TOPLEVEL_STATE_FULLSCREEN, 31, UINT32_MAX}; configure(&surface, fullscreen, sizeof(fullscreen) / sizeof(*fullscreen)); assert(notifications.count == 1); - assert(notifications.state == (YASL_STATE_CSD | YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED)); + assert(notifications.state == (YASL_PRIVATE_STATE_CSD | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_ACTIVATED)); configure(&surface, fullscreen, sizeof(fullscreen) / sizeof(*fullscreen)); assert(notifications.count == 1); /* Decoration changes preserve the other flags and do not notify twice. */ toplevel_decoration_configure(&surface, NULL, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); assert(notifications.count == 2); - assert(notifications.state == (YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED)); + assert(notifications.state == (YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_ACTIVATED)); toplevel_decoration_configure(&surface, NULL, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); assert(notifications.count == 2); uint32_t maximized[] = {XDG_TOPLEVEL_STATE_MAXIMIZED, XDG_TOPLEVEL_STATE_SUSPENDED}; configure(&surface, maximized, sizeof(maximized) / sizeof(*maximized)); assert(notifications.count == 3); - assert(notifications.state == (YASL_STATE_MAXIMIZED | YASL_STATE_SUSPENDED)); + assert(notifications.state == (YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_SUSPENDED)); toplevel_decoration_configure(&surface, NULL, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); assert(notifications.count == 4); - assert(notifications.state == (YASL_STATE_CSD | YASL_STATE_MAXIMIZED | YASL_STATE_SUSPENDED)); + assert(notifications.state == (YASL_PRIVATE_STATE_CSD | YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_SUSPENDED)); /* Each tiled edge maps to the same portable flag; changing edges alone * leaves the reported state unchanged. */ @@ -59,17 +73,38 @@ int main(void) for (size_t i = 0; i < sizeof(edges) / sizeof(*edges); i++) { configure(&surface, &edges[i], 1); assert(notifications.count == 5); - assert(notifications.state == (YASL_STATE_CSD | YASL_STATE_TILED)); + assert(notifications.state == (YASL_PRIVATE_STATE_CSD | YASL_PRIVATE_STATE_TILED)); } configure(&surface, edges, 0); assert(notifications.count == 6); - assert(notifications.state == YASL_STATE_CSD); + assert(notifications.state == YASL_PRIVATE_STATE_CSD); /* State still advances when applications have not installed callbacks. */ wayland_surface_set_callbacks(&surface, NULL, NULL); configure(&surface, fullscreen, sizeof(fullscreen) / sizeof(*fullscreen)); - assert(yasl_surface_state(&surface.base) == - (YASL_STATE_CSD | YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED)); + assert(yasl_private_surface_state(&surface.base) == + (YASL_PRIVATE_STATE_CSD | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_ACTIVATED)); assert(notifications.count == 6); + + /* External presentation has no SHM commit callback to acknowledge xdg + * configures. Apply geometry and acknowledge before renderer submission. */ + struct wayland_surface gpu = { + .base.presenter = YASL_PRIVATE_PRESENTER_VULKAN, + .width = 0, .height = 0, + .logical_width = 64, .logical_height = 64, + .req_width = 64, .req_height = 64, + .scale_120 = YASL_PRIVATE_SCALE_UNIT, + .pending_scale_120 = 180, + }; + acknowledge_count = 0; + xdg_surface_configure(&gpu, (struct xdg_surface *)&gpu, 1234); + assert(acknowledge_count == 1 && acknowledged_serial == 1234); + assert(!gpu.has_pending_serial && !gpu.size_pending); + assert(gpu.configured && gpu.width == 96 && gpu.height == 96); + + /* CPU presentation keeps its existing deferred ack with the SHM commit. */ + struct wayland_surface cpu = {.base.presenter = YASL_PRIVATE_PRESENTER_CPU}; + xdg_surface_configure(&cpu, (struct xdg_surface *)&cpu, 5678); + assert(acknowledge_count == 1 && cpu.has_pending_serial && cpu.want_draw); return 0; } diff --git a/tests/wayland/test_tablet.c b/tests/wayland/test_tablet.c index c76ddf8..f47fbfa 100644 --- a/tests/wayland/test_tablet.c +++ b/tests/wayland/test_tablet.c @@ -1,63 +1,101 @@ #include "../../src/platform/wayland/pointer.c" #include -static struct yasl_event next(struct wayland_surface *surface) +static struct yasl_private_event next_raw(struct wayland_surface *surface) { - struct yasl_event event; - assert(yasl_surface_poll_event(&surface->base, &event)); + struct yasl_private_event event; + assert(yasl_private_surface_poll_event(&surface->base, &event)); assert(event.surface == &surface->base); return event; } +static struct yasl_private_event next_pointer(struct wayland_surface *surface) +{ + struct yasl_private_event event; + do { event = next_raw(surface); } while (event.type == YASL_PRIVATE_EVENT_PEN); + return event; +} + int main(void) { - struct wayland_display display = {.base.ops = &yasl_wayland_backend}; + struct wayland_display display = {.base.ops = &yasl_private_wayland_backend}; struct wayland_surface surface = {.base.display = &display.base, .conn = &display, .scale_factor = 1.5}; struct seat seat = {.conn = &display, .tablet_focus = &surface}; - struct tablet_tool tool = {.seat = &seat}; + struct tablet_tool tool = {.seat = &seat, .device_id = 77, .tool_id = 88, .kind = 1}; seat.tool = &tool; tablet_tool_motion(&tool, NULL, wl_fixed_from_int(100), wl_fixed_from_int(120)); tool_pressure(&tool, NULL, 32768); tool_tilt(&tool, NULL, wl_fixed_from_int(-35), wl_fixed_from_int(42)); + tool_distance(&tool, NULL, 16384); + tool_rotation(&tool, NULL, wl_fixed_from_int(17)); + tool_slider(&tool, NULL, -32768); + pen_action_push(&tool, YASL_PRIVATE_PEN_ENTER, 0); tablet_tool_down(&tool, NULL, 1); tool_frame(&tool, NULL, 0); - struct yasl_event ev = next(&surface); - assert(ev.type == YASL_EVENT_POINTER_MOTION && ev.pointer.device == YASL_PTR_DEV_PEN); + struct yasl_private_event pen = next_raw(&surface); + assert(pen.type == YASL_PRIVATE_EVENT_PEN); + assert(pen.pen.action == YASL_PRIVATE_PEN_ENTER && !pen.pen.in_contact); + assert(pen.pen.device_id == 77 && pen.pen.tool_id == 88); + pen = next_raw(&surface); + assert(pen.type == YASL_PRIVATE_EVENT_PEN); + assert(pen.pen.action == YASL_PRIVATE_PEN_DOWN && pen.pen.in_contact); + assert(pen.pen.buttons & YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY)); + assert(pen.pen.x == 150 && pen.pen.y == 180); + assert(fabs(pen.pen.pressure - (32768.0 / 65535.0)) < 0.00001); + assert(pen.pen.axes_valid == (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT | + YASL_PRIVATE_PEN_DISTANCE | YASL_PRIVATE_PEN_ROTATION | YASL_PRIVATE_PEN_TANGENTIAL)); + assert(pen.pen.axes_changed == pen.pen.axes_valid); + assert(fabs(pen.pen.distance - 16384.0 / 65535.0) < 0.00001); + assert(pen.pen.rotation == 17 && pen.pen.tangential == -32768.0 / 65535.0); + struct yasl_private_event ev = next_pointer(&surface); + assert(ev.type == YASL_PRIVATE_EVENT_POINTER_MOTION && ev.pointer.device == YASL_PRIVATE_PTR_DEV_PEN); assert(ev.pointer.pos.x == 150 && ev.pointer.pos.y == 180 && ev.pointer.pen.pressure == 32768); assert(ev.pointer.pen.tilt_x == -35 && ev.pointer.pen.tilt_y == 42); - ev = next(&surface); - assert(ev.type == YASL_EVENT_POINTER_DOWN && ev.pointer.button == YASL_BUTTON_PRIMARY && ev.pointer.pen.pressure == 32768); + ev = next_pointer(&surface); + assert(ev.type == YASL_PRIVATE_EVENT_POINTER_DOWN && ev.pointer.button == YASL_PRIVATE_BUTTON_PRIMARY && ev.pointer.pen.pressure == 32768); /* Stationary pressure and tilt changes are still motion samples. */ tool_pressure(&tool, NULL, 100000); tool_tilt(&tool, NULL, wl_fixed_from_int(-120), wl_fixed_from_int(120)); tool_frame(&tool, NULL, 0); - ev = next(&surface); - assert(ev.type == YASL_EVENT_POINTER_MOTION && ev.pointer.pen.pressure == 65535); + pen = next_raw(&surface); + assert(pen.type == YASL_PRIVATE_EVENT_PEN && pen.pen.action == YASL_PRIVATE_PEN_MOVE); + assert(pen.pen.axes_changed == (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT)); + ev = next_pointer(&surface); + assert(ev.type == YASL_PRIVATE_EVENT_POINTER_MOTION && ev.pointer.pen.pressure == 65535); assert(ev.pointer.pos.x == 150 && ev.pointer.pos.y == 180); assert(ev.pointer.pen.tilt_x == -90 && ev.pointer.pen.tilt_y == 90); tablet_tool_up(&tool, NULL); tool_frame(&tool, NULL, 0); - ev = next(&surface); - assert(ev.type == YASL_EVENT_POINTER_UP && ev.pointer.button == YASL_BUTTON_PRIMARY && !ev.pointer.pen.pressure); + pen = next_raw(&surface); + assert(pen.type == YASL_PRIVATE_EVENT_PEN && pen.pen.action == YASL_PRIVATE_PEN_UP); + assert(!pen.pen.in_contact && pen.pen.pressure == 0); + ev = next_pointer(&surface); + assert(ev.type == YASL_PRIVATE_EVENT_POINTER_UP && ev.pointer.button == YASL_PRIVATE_BUTTON_PRIMARY && !ev.pointer.pen.pressure); tool_type(&tool, NULL, ZWP_TABLET_TOOL_V2_TYPE_ERASER); tool_pressure(&tool, NULL, 22000); tool_frame(&tool, NULL, 0); - assert(next(&surface).pointer.device == YASL_PTR_DEV_ERASER); + pen = next_raw(&surface); + assert(pen.type == YASL_PRIVATE_EVENT_PEN && pen.pen.tool == 2); + assert(next_pointer(&surface).pointer.device == YASL_PRIVATE_PTR_DEV_ERASER); tool_wheel(&tool, NULL, 0, 1); tool_frame(&tool, NULL, 0); - ev = next(&surface); - assert(ev.type == YASL_EVENT_POINTER_SCROLL && ev.pointer.device == YASL_PTR_DEV_ERASER); + pen = next_raw(&surface); + assert(pen.type == YASL_PRIVATE_EVENT_PEN && pen.pen.axes_changed == YASL_PRIVATE_PEN_WHEEL); + ev = next_pointer(&surface); + assert(ev.type == YASL_PRIVATE_EVENT_POINTER_SCROLL && ev.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER); assert(ev.pointer.scroll.x == 0 && ev.pointer.scroll.y == 1); tablet_tool_proximity_out(&tool, NULL); - ev = next(&surface); - assert(ev.type == YASL_EVENT_POINTER_LEAVE && ev.pointer.device == YASL_PTR_DEV_ERASER && !ev.pointer.pen.pressure); + pen = next_raw(&surface); + assert(pen.type == YASL_PRIVATE_EVENT_PEN && pen.pen.action == YASL_PRIVATE_PEN_LEAVE); + ev = next_pointer(&surface); + assert(ev.type == YASL_PRIVATE_EVENT_POINTER_LEAVE && ev.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER && !ev.pointer.pen.pressure); assert(!seat.tool && !seat.tablet_focus && !seat.tool_buttons); - assert(!(yasl_display_caps(&display.base) & YASL_CAP_TABLET)); + assert(!(yasl_private_display_caps(&display.base) & YASL_PRIVATE_CAP_TABLET)); display.tablet_mgr = (void *)&display; /* Capability depends on protocol availability. */ - assert(yasl_display_caps(&display.base) & YASL_CAP_TABLET); + assert(yasl_private_display_caps(&display.base) & YASL_PRIVATE_CAP_TABLET); seat.pointer_focus = &surface; /* A compositor may describe the same detent in three ways. value120 wins, @@ -67,13 +105,32 @@ int main(void) pointer_axis_value120(&seat, NULL, WL_POINTER_AXIS_VERTICAL_SCROLL, 30); pointer_axis_value120(&seat, NULL, WL_POINTER_AXIS_HORIZONTAL_SCROLL, -60); pointer_frame(&seat, NULL); - ev = next(&surface); - assert(ev.type == YASL_EVENT_POINTER_SCROLL && ev.pointer.device == YASL_PTR_DEV_MOUSE); + ev = next_pointer(&surface); + assert(ev.type == YASL_PRIVATE_EVENT_POINTER_SCROLL && ev.pointer.device == YASL_PRIVATE_PTR_DEV_MOUSE); assert(ev.pointer.scroll.x == -0.5 && ev.pointer.scroll.y == 0.25); - struct yasl_event unused; - assert(!yasl_surface_poll_event(&surface.base, &unused)); + assert(ev.pointer.scroll_valid == 1); + assert(ev.pointer.scroll_source == 1 && ev.pointer.scroll_phase == 0); + struct yasl_private_event unused; + assert(!yasl_private_surface_poll_event(&surface.base, &unused)); + pointer_frame(&seat, NULL); + assert(!yasl_private_surface_poll_event(&surface.base, &unused)); + + /* Finger scrolling carries pixel offsets and coherent gesture phases. */ + pointer_axis_source(&seat, NULL, WL_POINTER_AXIS_SOURCE_FINGER); + pointer_axis(&seat, NULL, 0, WL_POINTER_AXIS_VERTICAL_SCROLL, wl_fixed_from_double(1.25)); + pointer_axis(&seat, NULL, 0, WL_POINTER_AXIS_HORIZONTAL_SCROLL, wl_fixed_from_double(-2.5)); + pointer_frame(&seat, NULL); + ev = next_pointer(&surface); + assert(ev.pointer.scroll_phase == 1 && ev.pointer.scroll_source == 2); + assert(ev.pointer.pixel_scroll.x == -3.75 && ev.pointer.pixel_scroll.y == 1.875); + pointer_axis(&seat, NULL, 0, WL_POINTER_AXIS_VERTICAL_SCROLL, wl_fixed_from_double(1.25)); + pointer_frame(&seat, NULL); + ev = next_pointer(&surface); + assert(ev.pointer.scroll_phase == 2 && ev.pointer.scroll.y == 0.125); + pointer_axis_stop(&seat, NULL, 0, WL_POINTER_AXIS_VERTICAL_SCROLL); pointer_frame(&seat, NULL); - assert(!yasl_surface_poll_event(&surface.base, &unused)); + ev = next_pointer(&surface); + assert(ev.pointer.scroll_phase == 3 && ev.pointer.scroll.x == 0 && ev.pointer.scroll.y == 0); /* Stops preserve offsets from the same frame. Repeated tiny movements * must be visible immediately rather than held until a whole detent. */ @@ -83,17 +140,19 @@ int main(void) pointer_axis(&seat, NULL, 0, WL_POINTER_AXIS_HORIZONTAL_SCROLL, wl_fixed_from_double(-2.5)); pointer_axis_stop(&seat, NULL, 0, WL_POINTER_AXIS_VERTICAL_SCROLL); pointer_frame(&seat, NULL); - ev = next(&surface); + ev = next_pointer(&surface); assert(ev.pointer.scroll.x == -0.25 && ev.pointer.scroll.y == 0.125); + assert(ev.pointer.scroll_source == 2 && ev.pointer.scroll_phase == 3); + assert(ev.pointer.pixel_scroll.x == -3.75 && ev.pointer.pixel_scroll.y == 1.875); } pointer_axis_discrete(&seat, NULL, WL_POINTER_AXIS_HORIZONTAL_SCROLL, 2); pointer_frame(&seat, NULL); - ev = next(&surface); + ev = next_pointer(&surface); assert(ev.pointer.scroll.x == 2 && ev.pointer.scroll.y == 0); pointer_axis_value120(&seat, NULL, WL_POINTER_AXIS_VERTICAL_SCROLL, 60); seat_pointer_reset(&seat); pointer_frame(&seat, NULL); - assert(!yasl_surface_poll_event(&surface.base, &unused)); + assert(!yasl_private_surface_poll_event(&surface.base, &unused)); input_queue_clear(&surface.input); return 0; } -- cgit v1.2.3