From 5ef6ca21ab9e1cf4f69254234d190a634b777d52 Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Wed, 14 Oct 2026 09:38:31 +0300 Subject: Remove deprecated Cocoa OpenGL and private compatibility APIs --- README.md | 5 +- docs/getting-started.md | 9 +- docs/guide.md | 4 +- include/yasl/gl.h | 5 +- include/yasl/yasl.h | 38 ++- meson.build | 15 +- src/common/backend.c | 32 --- src/common/backend.h | 9 +- src/common/events.c | 56 +--- src/common/surface.c | 6 +- src/common/types.h | 106 ++----- src/platform/cocoa/api.h | 3 - src/platform/cocoa/window.m | 239 +-------------- src/platform/wayland/api.h | 3 - src/platform/wayland/backend.c | 23 -- src/platform/wayland/clipboard.c | 12 - src/platform/wayland/clipboard.h | 2 - src/platform/wayland/internal.h | 2 - src/platform/wayland/keyboard.c | 25 -- src/platform/wayland/popup.c | 17 +- src/platform/wayland/surface.c | 3 - src/platform/x11/window.c | 135 +++------ tests/cocoa/display.m | 13 + tests/cocoa/test_cursor.m | 2 +- tests/cocoa/test_keyboard.m | 9 +- tests/cocoa/test_native.c | 3 +- tests/cocoa/test_ownership.m | 22 +- tests/cocoa/test_tablet.m | 4 +- tests/common/test_backends.c | 495 -------------------------------- tests/common/test_contracts.c | 24 +- tests/common/test_events.c | 19 +- tests/common/test_surface_integration.c | 20 +- tests/helpers/package_cocoa_tests.py | 4 +- tests/helpers/run_backends.py | 119 -------- tests/meson.build | 16 +- tests/wayland/test_cursor.c | 8 +- tests/wayland/test_input.c | 10 +- tests/wayland/test_keyboard.c | 4 +- tests/wayland/test_popup_positioner.c | 11 +- tests/wayland/test_relative_pointer.c | 6 +- tests/wayland/test_state.c | 37 ++- tests/wayland/test_tablet.c | 8 +- tests/x11/test_input.c | 24 +- tests/x11/test_state.c | 25 +- tests/x11/test_tablet.c | 79 +++-- tests/x11/test_tablet_native.c | 126 ++++---- 46 files changed, 403 insertions(+), 1434 deletions(-) create mode 100644 tests/cocoa/display.m delete mode 100644 tests/common/test_backends.c delete mode 100644 tests/helpers/run_backends.py diff --git a/README.md b/README.md index d486b80..e528220 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ YASL is a small C desktop surface and input library for applications that own their event loop and renderer. It supports Wayland, X11, and Cocoa, with CPU -images, optional OpenGL contexts, Vulkan window surfaces, and direct Metal layers. +images, optional EGL/OpenGL contexts on Linux, Vulkan window surfaces, and direct +Metal layers on macOS. Pen input includes tool identity, proximity/contact, pressure, tilt, and available rotation/distance/tangential/wheel axes. It also supplies IME composition/editing, clipboard text, popups, file drops, and surface drags. @@ -17,7 +18,7 @@ yasl_surface *surface = yasl_surface_create("Example", 800, 600, 0); Flags zero creates a GPU window without choosing a graphics API. The application attaches a Vulkan surface or Metal layer, or uses `yasl_gl_surface_create` to create -an OpenGL window/context. `YASL_SURFACE_CPU` gives the window a pixel buffer: draw into +an OpenGL window/context on Linux. `YASL_SURFACE_CPU` gives the window a pixel buffer: draw into `yasl_cpu_pixels` and call `yasl_cpu_present`. Native handles are available through typed extension headers. **Status:** pre-release 0.1; source and ABI compatibility are not promised. diff --git a/docs/getting-started.md b/docs/getting-started.md index b82dae5..aeed4cd 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -7,14 +7,14 @@ Build with a C23 compiler, Meson 1.6+, and Ninja. Linux backend dependencies are wayland-client, wayland-cursor, xkbcommon, or x11, xi, xext, xkbcommon (optional xrandr). Optional egl and wayland-egl enable GL contexts; Vulkan SDK development files enable the Vulkan bridge. Optional dbus-1 enables Linux desktop services. -Cocoa requires macOS 14+ and the AppKit, Metal, QuartzCore, OpenGL, and -UserNotifications system frameworks. Optional Vulkan SDK enables MoltenVK WSI. +Cocoa requires macOS 14+ and the AppKit, Metal, QuartzCore, and UserNotifications +system frameworks. Use Metal or MoltenVK for GPU rendering on macOS; Cocoa does +not provide OpenGL contexts. Optional Vulkan SDK enables MoltenVK WSI. ```sh meson setup build meson compile -C build ./build/yasl-hello -./build/yasl-game meson test -C build --print-errorlogs ``` @@ -53,7 +53,8 @@ reconnects, and sends DISPLAY_RESTORED. CPU windows need nothing; GPU renderers their Vulkan surfaces and GL objects on loss and rebuild them on restore. [hello.c](../examples/hello.c) draws into a CPU surface's pixel buffer on every REFRESH. -[game.c](../examples/game.c) creates a GL context, binds it, loads procedures, draws, +On Linux with EGL, `./build/yasl-game` runs [game.c](../examples/game.c), which +creates a GL context, binds it, loads procedures, draws, swaps, and rebinds the context after a compositor restart. [tablet.c](../examples/tablet.c) retains a canvas and draws pressure-sensitive pen/eraser strokes. [notepad.c](../examples/notepad.c) uses a worker queue, wakeups, clipboard, IME, and its own blink deadline. diff --git a/docs/guide.md b/docs/guide.md index d0db071..5435370 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -64,8 +64,8 @@ One presenter attaches to each surface. Incompatible second attachments fail. key/value attribute list terminated by YASL_GL_END, and an optional sharing surface. NULL attributes default to GL3.2 core, RGBA8, depth24/stencil8. The surface owns its context. Bind, load procedures, set swap interval, and swap explicitly. - GL API support depends on the native driver; Cocoa rejects OpenGL ES/debug contexts - and supports its native legacy/core profiles. + Available on Linux with EGL; GL API support depends on the native driver. Cocoa + reports UNSUPPORTED for GL creation; use Metal or MoltenVK. - CPU: create with YASL_SURFACE_CPU and call `yasl_cpu_present` with premultiplied numeric 0xAARRGGBB, width/height, byte stride, and optional damage rows containing x/y/width/height. Extent must match current framebuffer. NULL/zero damage means full; diff --git a/include/yasl/gl.h b/include/yasl/gl.h index 66a245a..39d2c63 100644 --- a/include/yasl/gl.h +++ b/include/yasl/gl.h @@ -1,4 +1,4 @@ -/** @file Optional OpenGL/OpenGL ES contexts. Attribute lists are per-call. */ +/** @file Optional EGL/OpenGL/OpenGL ES contexts on Linux. Attribute lists are per-call. */ #ifndef YASL_GL_H #define YASL_GL_H #include "yasl/yasl.h" @@ -13,7 +13,8 @@ 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. */ + * 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. */ diff --git a/include/yasl/yasl.h b/include/yasl/yasl.h index 38c5b88..c0ebb75 100644 --- a/include/yasl/yasl.h +++ b/include/yasl/yasl.h @@ -55,6 +55,20 @@ enum yasl_event_type { YASL_EVENT_NONE, YASL_EVENT_CLOSE, YASL_EVENT_QUIT, YASL_ 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; }; + /** 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. * WINDOW: size, scale or state changed. REFRESH: draw now; sent once the window @@ -70,20 +84,18 @@ struct yasl_event { yasl_surface *surface; uint64_t time_ns; union { - struct { int width,height,pixel_width,pixel_height; double scale; unsigned state; } window; - struct { enum yasl_key key; enum yasl_key_action action; unsigned mods; - uint32_t codepoint, shifted, native_code; } key; - struct { const char *data; size_t length; } text; - struct { double x,y,dx,dy; enum yasl_pointer_action action; unsigned button,buttons,mods; } pointer; - struct { double x,y,lines_x,lines_y,pixels_x,pixels_y; unsigned valid,source,phase,mods; bool momentum; } scroll; - struct { 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; } pen; - struct { const char *data; size_t length; int begin,end; bool active; uint64_t revision,generation; } composition; - struct { const char *data; size_t length,begin,end; uint64_t revision,generation; } text_edit; - struct { const char *data; size_t length; uint64_t request_id; enum yasl_transfer_status status; } clipboard; + struct yasl_window_payload window; + struct yasl_key_payload key; + struct yasl_text_payload text; + struct yasl_pointer_payload pointer; + struct yasl_scroll_payload scroll; + struct yasl_pen_payload pen; + struct yasl_composition_payload composition; + struct yasl_text_edit_payload text_edit; + struct yasl_clipboard_payload clipboard; /** DATA carries a text/uri-list for yasl_drop_next_path; x/y are set for OVER. */ - struct { enum yasl_drop_action action; double x,y; const char *data; size_t length; } drop; - struct { enum yasl_drag_action action; double x,y; } drag; + struct yasl_drop_payload drop; + struct yasl_drag_payload drag; }; }; /** Last error on this thread; success does not clear it. */ diff --git a/meson.build b/meson.build index 4d7d7ce..5f5b26c 100644 --- a/meson.build +++ b/meson.build @@ -76,7 +76,7 @@ elif system == 'darwin' error('Cocoa requires macOS 14 or newer. Set MACOSX_DEPLOYMENT_TARGET=14.0 or a newer target before configuring.') endif deps += [ - dependency('appleframeworks', modules: ['AppKit', 'Metal', 'QuartzCore', 'UserNotifications', 'OpenGL']), + dependency('appleframeworks', modules: ['AppKit', 'Metal', 'QuartzCore', 'UserNotifications']), ] else error('YASL supports Linux and macOS.') @@ -92,6 +92,7 @@ if not has_wayland and not has_x11 and not has_cocoa endif add_project_arguments( + '-Werror=deprecated-declarations', '-DYASL_HAS_EGL=' + (has_egl ? '1' : '0'), '-DYASL_HAS_VULKAN=' + (has_vulkan ? '1' : '0'), '-DYASL_HAS_WAYLAND=' + (has_wayland ? '1' : '0'), @@ -172,10 +173,12 @@ notepad_example = executable( dependencies: yasl_dep, ) -game_example = executable( - 'yasl-game', - 'examples/game.c', - dependencies: yasl_dep, -) +if has_egl + game_example = executable( + 'yasl-game', + 'examples/game.c', + dependencies: yasl_dep, + ) +endif subdir('tests') diff --git a/src/common/backend.c b/src/common/backend.c index 8d20f67..939ec71 100644 --- a/src/common/backend.c +++ b/src/common/backend.c @@ -34,7 +34,6 @@ static struct yasl_private_display *open_backend(enum yasl_private_backend backe struct yasl_private_display *d = ops->display_open(loop, spec); if (!d && !errno) errno = ENODEV; if (d) { - d->callback_ud = spec ? spec->ud : NULL; if (!yasl_private_events_open(d)) { int error = errno; ops->display_close(d); @@ -132,26 +131,6 @@ int yasl_private_display_double_click_ms(const struct yasl_private_display *d) return ms > 0 && ms <= 10000 ? ms : YASL_PRIVATE_DOUBLE_CLICK_MS_DEFAULT; } -void yasl_private_display_copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text) -{ - if (!d || !d->ops->display_copy || !text || - (sel != YASL_PRIVATE_CLIPBOARD && sel != YASL_PRIVATE_PRIMARY) || - (sel == YASL_PRIVATE_PRIMARY && !(yasl_private_display_caps(d) & YASL_PRIVATE_CAP_PRIMARY_SELECTION))) - return; - d->ops->display_copy(d, sel, text); -} - -void yasl_private_display_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target) -{ - if (!d || !d->ops->display_paste || - (sel != YASL_PRIVATE_CLIPBOARD && sel != YASL_PRIVATE_PRIMARY) || - (sel == YASL_PRIVATE_PRIMARY && !(yasl_private_display_caps(d) & YASL_PRIVATE_CAP_PRIMARY_SELECTION))) - return; - if (!target || target->display != d) - return; - d->ops->display_paste(d, sel, target); -} - bool yasl_private_display_drag_start(struct yasl_private_display *d, struct yasl_surface *origin, const struct yasl_private_drag_image *image, const struct yasl_private_drag_listener *l, void *ud) { if (!d || !d->ops->display_drag_start) @@ -336,17 +315,6 @@ bool yasl_private_surface_present(struct yasl_surface *s, const struct yasl_priv return s->display->ops->surface_present(s, fb, &clipped); } -bool yasl_private_surface_poll_event(struct yasl_surface *s, struct yasl_private_event *ev) -{ - if (!s || !ev) - return false; - if (s->display && s->display->events) - return yasl_private_events_poll_surface(s, ev); - if (s->display && s->display->ops && s->display->ops->surface_poll_event) - return s->display->ops->surface_poll_event(s, ev); - return input_queue_pop(&s->input, ev); -} - bool yasl_private_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap, struct yasl_private_vector2i *size) { diff --git a/src/common/backend.h b/src/common/backend.h index bf6815a..6eefbb3 100644 --- a/src/common/backend.h +++ b/src/common/backend.h @@ -17,11 +17,9 @@ struct yasl_private_display { const struct yasl_private_backend_ops *ops; void *native_handle; bool offline; /* Connection lost; native handles are unusable until it is restored. */ - struct yasl_private_display_cbs callbacks; struct yasl_private_loop *loop; /* Backend's copy of yasl_private_display_spec.app_id, or NULL; for desktop services. */ const char *app_id; - void *callback_ud; struct yasl_private_event_state *events; struct yasl_surface *surfaces; }; @@ -82,7 +80,6 @@ bool yasl_private_events_register(struct yasl_surface *s); void yasl_private_events_unregister(struct yasl_surface *s); bool yasl_private_events_push(struct yasl_private_display *d, struct yasl_surface *queue_owner, const struct yasl_private_event *event); -bool yasl_private_events_poll_surface(struct yasl_surface *s, struct yasl_private_event *event); void yasl_private_surface_notify_configure(struct yasl_surface *s, struct yasl_private_geometry geometry); void yasl_private_surface_notify_refresh(struct yasl_surface *s); void yasl_private_surface_notify_close(struct yasl_surface *s); @@ -136,8 +133,6 @@ struct yasl_private_backend_ops { int (*display_modifiers)(const struct yasl_private_display *d); /* Optional; returns zero or less when the system reports no interval. */ int (*display_double_click_ms)(const struct yasl_private_display *d); - void (*display_copy)(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text); - void (*display_paste)(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target); bool (*display_copy_text)(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text); bool (*display_request_paste)(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target, uint64_t request_id); @@ -150,7 +145,7 @@ struct yasl_private_backend_ops { bool (*surface_popup_reposition)(struct yasl_surface *popup, const struct yasl_private_popup_spec *spec); void (*surface_destroy)(struct yasl_surface *s); struct input_queue *(*surface_input_queue)(struct yasl_surface *s); - bool (*surface_set_size)(struct yasl_surface *s, struct yasl_private_vector2i size_logical); + bool (*surface_set_size)(struct yasl_surface *s, struct yasl_private_vector2i size); bool (*surface_set_fullscreen)(struct yasl_surface *s, bool enabled); bool (*surface_set_maximized)(struct yasl_surface *s, bool enabled); bool (*surface_minimize)(struct yasl_surface *s); @@ -160,8 +155,6 @@ struct yasl_private_backend_ops { void (*surface_set_title)(struct yasl_surface *s, const char *title); void (*surface_schedule_redraw)(struct yasl_surface *s); bool (*surface_present)(struct yasl_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage); - /* Optional; NULL means the common input queue is drained. */ - bool (*surface_poll_event)(struct yasl_surface *s, struct yasl_private_event *ev); /* Optional; NULL means only YASL_PRIVATE_POINTER_NORMAL is supported. Backends that * implement RELATIVE push POINTER_MOTION with delta set and pos frozen. */ bool (*surface_set_pointer_mode)(struct yasl_surface *s, enum yasl_private_pointer_mode mode); diff --git a/src/common/events.c b/src/common/events.c index ba78d77..9245050 100644 --- a/src/common/events.c +++ b/src/common/events.c @@ -228,46 +228,18 @@ bool yasl_private_display_post(struct yasl_private_display *d, const struct yasl return true; } -static bool poll_event(struct yasl_private_display *d, uint64_t queue_id, struct yasl_private_event *event) +bool yasl_private_display_poll_event(struct yasl_private_display *d, struct yasl_private_event *event) { if (!d || !d->events || !event) return false; struct yasl_private_event_state *state = d->events; yasl_private_mutex_lock(&state->mutex); - struct event_node *discarded = NULL; - struct event_node **entry = &state->head; - struct event_node *previous = NULL; - if (queue_id) { - /* The input-only migration adapter uses legacy callbacks for lifecycle. - * Retaining those notifications would keep dispatch permanently ready. - * Mixing this adapter with display polling is deliberately unsupported. */ - while (*entry) { - struct event_node *candidate = *entry; - if (candidate->event.type < YASL_PRIVATE_EVENT_KEY || - candidate->event.type > YASL_PRIVATE_EVENT_INPUT_RESET) { - *entry = candidate->next; - if (state->tail == candidate) state->tail = previous; - candidate->next = discarded; - discarded = candidate; - } else { - previous = candidate; - entry = &candidate->next; - } - } - entry = &state->head; - previous = NULL; - while (*entry && (*entry)->queue_id != queue_id) { - previous = *entry; - entry = &(*entry)->next; - } - } - struct event_node *node = *entry; + struct event_node *node = state->head; if (!node) { yasl_private_mutex_unlock(&state->mutex); - dispose_list(discarded); return false; } - *entry = node->next; - if (state->tail == node) state->tail = previous; + state->head = node->next; + if (state->tail == node) state->tail = NULL; node->next = NULL; if (node->event.surface_id) node->event.surface = yasl_private_events_find(d, node->event.surface_id); @@ -276,20 +248,9 @@ static bool poll_event(struct yasl_private_display *d, uint64_t queue_id, struct *event = node->event; yasl_private_mutex_unlock(&state->mutex); dispose(last); - dispose_list(discarded); return true; } -bool yasl_private_display_poll_event(struct yasl_private_display *d, struct yasl_private_event *event) -{ - return poll_event(d, 0, event); -} - -bool yasl_private_events_poll_surface(struct yasl_surface *s, struct yasl_private_event *event) -{ - return s && s->id ? poll_event(s->display, s->id, event) : false; -} - uint64_t yasl_private_events_request_id(struct yasl_private_display *d) { if (!d->events || d->events->request == UINT64_MAX) { errno = EOVERFLOW; return 0; } @@ -310,14 +271,12 @@ void yasl_private_surface_notify_configure(struct yasl_surface *s, struct yasl_p .resize = {.geometry = geometry, .scale_changed = previous_scale != geometry.scale_120}}; yasl_private_events_push(s->display, s, &event); if (s->display) yasl_private_surface_request_frame(s); - if (s->callbacks.configure) s->callbacks.configure(s, geometry, s->ud); } void yasl_private_surface_notify_close(struct yasl_surface *s) { struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_WINDOW_CLOSE, .surface = s}; yasl_private_events_push(s->display, s, &event); - if (s->callbacks.close) s->callbacks.close(s, s->ud); } void yasl_private_surface_notify_state(struct yasl_surface *s, uint32_t flags) @@ -325,21 +284,18 @@ void yasl_private_surface_notify_state(struct yasl_surface *s, uint32_t flags) s->state = flags; struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_WINDOW_STATE, .surface = s, .state.flags = flags}; yasl_private_events_push(s->display, s, &event); - if (s->callbacks.state) s->callbacks.state(s, flags, s->ud); } void yasl_private_surface_notify_drag(struct yasl_surface *s, enum yasl_private_drag_event action, struct yasl_private_vector2i pos) { struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_DRAG, .surface = s, .drag = {action, pos}}; yasl_private_events_push(s->display, s, &event); - if (s->callbacks.drag) s->callbacks.drag(s, action, pos, s->ud); } void yasl_private_surface_notify_drop(struct yasl_surface *s, enum yasl_private_drop_event action, struct yasl_private_vector2i pos) { struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_DROP_HOVER, .surface = s, .drop = {action, pos}}; yasl_private_events_push(s->display, s, &event); - if (s->callbacks.drop) s->callbacks.drop(s, action, pos, s->ud); } void yasl_private_surface_notify_composition(struct yasl_surface *s, const char *text, int begin, int end) @@ -367,8 +323,4 @@ void yasl_private_display_notify_event(struct yasl_private_display *d, enum yasl { struct yasl_private_event event = {.type = type}; yasl_private_events_push(d, NULL, &event); - void (*callback)(void *) = type == YASL_PRIVATE_EVENT_DISPLAY_QUIT ? d->callbacks.quit - : type == YASL_PRIVATE_EVENT_DISPLAY_REOPEN ? d->callbacks.reopen - : type == YASL_PRIVATE_EVENT_DISPLAY_LOST ? d->callbacks.lost : NULL; - if (callback) callback(d->callback_ud); } diff --git a/src/common/surface.c b/src/common/surface.c index 9b4a8ad..b47fea7 100644 --- a/src/common/surface.c +++ b/src/common/surface.c @@ -119,7 +119,7 @@ yasl_surface *yasl_surface_create(const char *title,int width,int height,unsigne if(!creation_valid(width,height,flags)) return NULL; bool fresh=!display; if(!initialize()) return NULL; - struct yasl_private_window_spec spec={.title=title,.size_logical={width,height}, + 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) { @@ -446,7 +446,7 @@ static bool trigger_valid(yasl_surface *s,const yasl_event *e) 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,.positioner_explicit=true}; + *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) @@ -494,7 +494,7 @@ yasl_surface *yasl_gl_surface_create(const char *title,int w,int h,unsigned flag 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_logical={w,h},.flags=flags,.external_gpu=true}; + 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;} diff --git a/src/common/types.h b/src/common/types.h index 40c502f..8b9bbba 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -22,7 +22,7 @@ * Coordinates have their origin at the upper left; x grows right and y grows down. * Scale is an integer ratio with YASL_PRIVATE_SCALE_UNIT (120) representing 1x. * See the application guide for the display-wide dispatch/poll/flush cycle - * and backend limits. Legacy surface polling is an input-only filtered adapter. + * and backend limits. */ #ifndef YASL_PRIVATE_H #define YASL_PRIVATE_H @@ -145,7 +145,7 @@ enum yasl_private_surface_state : uint32_t { YASL_PRIVATE_STATE_TILED = 1 << 5, /**< At least one edge is tiled; currently reported by Wayland. */ }; -/** System cursor shapes. HIDDEN preserves pointer input; NONE is its compatibility alias. */ +/** System cursor shapes. HIDDEN preserves pointer input. */ enum yasl_private_cursor { YASL_PRIVATE_CURSOR_ARROW, /**< Default arrow. */ YASL_PRIVATE_CURSOR_TEXT, /**< Text insertion. */ @@ -157,7 +157,6 @@ enum yasl_private_cursor { YASL_PRIVATE_CURSOR_RESIZE_EW, /**< Horizontal resize. */ YASL_PRIVATE_CURSOR_RESIZE_NS, /**< Vertical resize. */ YASL_PRIVATE_CURSOR_HIDDEN, /**< Hidden system cursor; pointer input continues. */ - YASL_PRIVATE_CURSOR_NONE = YASL_PRIVATE_CURSOR_HIDDEN, /**< Compatibility alias for HIDDEN. */ }; /** Valid interactive resize edges; combinations are restricted to the named values. */ @@ -328,7 +327,7 @@ struct yasl_private_pointer_pen { enum yasl_private_event_type { YASL_PRIVATE_EVENT_NONE = 0, - /* Input: produced by backends, returned by yasl_private_surface_poll_event(). */ + /* Input: produced by backends and consumed from the display FIFO. */ YASL_PRIVATE_EVENT_KEY, /**< .key */ YASL_PRIVATE_EVENT_TEXT, /**< .text — committed UTF-8; insert this, not key codepoints. */ YASL_PRIVATE_EVENT_POINTER_DOWN, /**< .pointer — button/tip pressed; button valid. */ @@ -337,12 +336,11 @@ enum yasl_private_event_type { YASL_PRIVATE_EVENT_POINTER_SCROLL, /**< .pointer — scroll valid. */ YASL_PRIVATE_EVENT_POINTER_LEAVE, /**< .pointer — last known pos/device; cancel hover state. */ YASL_PRIVATE_EVENT_FOCUS, /**< .focus — keyboard focus changed; cancel held state on loss. */ - YASL_PRIVATE_EVENT_PASTE, /**< .text — UTF-8 from yasl_private_display_paste(). */ + YASL_PRIVATE_EVENT_PASTE, /**< .paste — clipboard request completion. */ YASL_PRIVATE_EVENT_DROP, /**< .text — dropped text/uri-list: CRLF-separated file URLs, not paths. */ YASL_PRIVATE_EVENT_INPUT_RESET, /**< No payload. Input stream interrupted; clear held keys, buttons, strokes, IME. */ - /* Window and application: part of the display FIFO, as well as runner delivery. - * Legacy window callbacks also receive matching native notifications. */ + /* Window and application notifications in the display FIFO. */ YASL_PRIVATE_EVENT_WINDOW_RESIZE, /**< .resize — size and/or scale changed; reallocate and redraw. */ YASL_PRIVATE_EVENT_WINDOW_CLOSE, /**< No payload. User asked to close the window. */ YASL_PRIVATE_EVENT_USER, /**< .user — posted application message. */ @@ -414,7 +412,7 @@ struct yasl_private_pointer_event { /** Borrowed NUL-terminated UTF-8, shared by TEXT, PASTE and DROP. */ struct yasl_private_text_event { - /** Borrowed NUL-terminated bytes; see yasl_private_surface_poll_event() for lifetime. */ + /** Borrowed NUL-terminated bytes; valid until the next successful display poll or display close. */ const char *data; /** Length in bytes excluding the terminating NUL. */ size_t len; @@ -427,11 +425,11 @@ enum yasl_private_transfer_status { YASL_PRIVATE_TRANSFER_FAILED, /**< Transfer or decoding failed. */ }; -/** Clipboard completion; data/len alias the legacy text payload. */ +/** Clipboard completion; data/len share the text payload layout. */ struct yasl_private_paste_event { const char *data; /**< Borrowed NUL-terminated UTF-8; empty on failure. */ size_t len; /**< Byte length excluding NUL. */ - uint64_t request_id; /**< Zero for a legacy paste request. */ + uint64_t request_id; /**< Nonzero clipboard request identity. */ enum yasl_private_transfer_status status; /**< Completion outcome. */ }; @@ -511,7 +509,7 @@ struct yasl_private_event { struct { const char *data; size_t len; uint64_t revision, generation; size_t begin, end; } text_edit; struct yasl_private_key_event key; /**< KEY */ struct yasl_private_pointer_event pointer; /**< POINTER_* */ - struct yasl_private_text_event text; /**< TEXT, legacy PASTE, DROP */ + struct yasl_private_text_event text; /**< TEXT, DROP */ struct yasl_private_paste_event paste; /**< PASTE */ struct yasl_private_composition_event composition; /**< COMPOSITION */ struct yasl_private_state_event state; /**< WINDOW_STATE */ @@ -534,35 +532,9 @@ struct yasl_private_event { */ /* Callbacks */ -/** Optional display notifications. NULL callback members are ignored. */ -struct yasl_private_display_cbs { - /** Application quit request; arrange orderly shutdown after dispatch returns. */ - void (*quit)(void *ud); - /** Application reopen/activation request, currently Cocoa. */ - void (*reopen)(void *ud); - /** Display connection lost; arrange cleanup after dispatch returns. */ - void (*lost)(void *ud); -}; - -/** Optional window/popup notifications, copied from yasl_private_window_spec.cbs or by -yasl_private_surface_set_callbacks(). -Callbacks may destroy their surface. They must then return without further use -of the surface or draw target. NULL members are ignored. */ +/** Internal CPU presenter callback, copied on installation. */ struct yasl_private_window_cbs { - /** Initialize the entire target and optionally present. Requested through schedule_redraw/flush. */ void (*draw)(struct yasl_surface *s, struct yasl_private_frame *frame, void *ud); - /** Configured size/scale changed; reallocate application content and request a redraw. */ - void (*configure)(struct yasl_surface *s, struct yasl_private_geometry geometry, void *ud); - /** Window/popup close request; application is responsible for destruction. */ - void (*close)(struct yasl_surface *s, void *ud); - /** Reported yasl_private_surface_state bitmask changed; matches yasl_private_surface_state(s). */ - void (*state)(struct yasl_surface *s, uint32_t state, void *ud); - /** YASL surface-drag target hover, leave, or drop; pos is in device pixels. */ - void (*drag)(struct yasl_surface *s, enum yasl_private_drag_event ev, - struct yasl_private_vector2i pos, void *ud); - /** External data-drag hover or leave; pos is in device pixels. */ - void (*drop)(struct yasl_surface *s, enum yasl_private_drop_event ev, - struct yasl_private_vector2i pos, void *ud); }; /* Display Lifecycle */ @@ -578,10 +550,6 @@ enum yasl_private_backend { struct yasl_private_display_spec { /** Copied application identifier, conventionally a reverse-domain UTF-8 string; NULL uses backend defaults. */ const char *app_id; - /** Copied optional display callbacks; NULL disables them. */ - const struct yasl_private_display_cbs *cbs; - /** Borrowed display callback userdata. */ - void *ud; /** Zero/AUTO chooses native backend; explicit choices never fall back. */ enum yasl_private_backend backend; @@ -661,26 +629,6 @@ int yasl_private_display_modifiers(const struct yasl_private_display *d); * rather than on every click. */ int yasl_private_display_double_click_ms(const struct yasl_private_display *d); -/* Unsupported/invalid selections are no-ops, never clipboard aliases. */ -/** - * Offer a copy of a NUL-terminated UTF-8 string as a selection. - * @param d Display, or NULL for no action. - * @param sel YASL_PRIVATE_CLIPBOARD or YASL_PRIVATE_PRIMARY. - * @param text Text copied during this call; NULL does nothing. - * Unsupported selections do nothing. The void return does not confirm that the - * system accepted the offer; Wayland may require a recent input serial. - */ -void yasl_private_display_copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text); -/** - * Request text delivery to a particular surface. - * @param d Display that owns target. - * @param sel YASL_PRIVATE_CLIPBOARD or YASL_PRIVATE_PRIMARY. - * @param target Destination surface; NULL or a surface on another display does nothing. - * Results arrive as YASL_PRIVATE_EVENT_PASTE, possibly during a later dispatch. No result - * is promised for unavailable data or a failed transfer. Destroying target cancels - * delivery. Unsupported selections do nothing. - */ -void yasl_private_display_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target); /* A successful drag invokes the copied listener's non-NULL end once, including * cancel and display close. Failed starts do not invoke it. */ @@ -714,19 +662,16 @@ struct yasl_private_window_spec { /** Copied UTF-8 title; NULL uses backend defaults. */ const char *title; /** Requested logical-pixel width and height; both must be positive. */ - union { - struct yasl_private_vector2i size_logical; /**< Requested logical-pixel extent. */ - struct yasl_private_vector2i size; /**< Migration alias for size_logical. */ - }; + struct yasl_private_vector2i size; /** Request initial fullscreen; actual state is reported asynchronously. */ bool fullscreen; /** Permit unpaced presentation where supported; a hint, not a guarantee. */ bool tearing; /** Copied optional window callbacks; NULL disables them. */ const struct yasl_private_window_cbs *cbs; - /** Borrowed window callback userdata; ignored by the runner. */ + /** Borrowed CPU presenter userdata. */ void *ud; - /** Borrowed application data, independent of callbacks and available in either loop style. */ + /** Borrowed application data, independent of the CPU presenter. */ void *userdata; }; @@ -738,13 +683,10 @@ Dimensions must be positive, at most 32767, and fit an INT32_MAX-byte buffer; backends may impose further limits after scaling or resolving placement. */ struct yasl_private_popup_spec { unsigned anchor_edges, gravity, constraints; - bool positioner_explicit; /** Full popup framebuffer extent in device pixels, including transparent margins. */ struct yasl_private_vector2i size; /** Rectangle in parent framebuffer coordinates, in device pixels. */ struct yasl_private_rect2i anchor; - /** true prefers right of anchor; false prefers below. Placement may flip/slide. */ - bool beside; /** Top-left content offset within the popup, in device pixels. */ struct yasl_private_vector2i content_offset; /** Content extent; zero per axis extends to the corresponding size edge. */ @@ -761,10 +703,8 @@ struct yasl_private_popup_spec { * size may differ from the request. The callback table in spec->cbs is copied; * spec->ud stays borrowed until callbacks are replaced or the surface is destroyed. * This window's callbacks cannot run before creation returns. Use - * yasl_private_surface_geometry() for initial geometry; an initial configure callback - * is not guaranteed. Schedule an initial redraw after setting up application state. - * On a display owned by yasl_private_run(), the runner installs its own callbacks instead; - * use yasl_private_app_desc.event and yasl_private_app_desc.frame for application notifications. + * yasl_private_surface_geometry() for initial geometry. Schedule an initial redraw + * after setting up the CPU presenter. Lifecycle notifications arrive in the display FIFO. */ struct yasl_surface *yasl_private_surface_create_toplevel(struct yasl_private_display *d, const struct yasl_private_window_spec *spec); @@ -809,7 +749,7 @@ struct yasl_private_display *yasl_private_surface_display(const struct yasl_surf * @param s Surface. * @param cbs Table copied by value; NULL removes all callbacks. NULL members are ignored. * @param ud Borrowed userdata passed to the installed callbacks. - * @return true if replaced; false with EINVAL for NULL, EBUSY on a runner-owned surface. + * @return true if replaced; false with EINVAL for NULL. * This call only replaces callbacks; it does not invoke them. * Use yasl_private_surface_geometry() to read current geometry; do not rely on receiving * an initial configure callback when registering. @@ -848,18 +788,6 @@ void yasl_private_surface_schedule_redraw(struct yasl_surface *s); */ bool yasl_private_surface_present(struct yasl_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage); -/** - * Migration adapter: consume one input event for a surface from the display FIFO. - * Popup input can belong to the root queue; inspect ev->surface. Returns false - * for empty/invalid arguments, leaving ev unchanged. Strings remain borrowed - * until the next successful display or surface poll, or display close. Pending - * recipient events are canceled on destruction. Prefer yasl_private_display_poll_event() - * to receive window, composition, and display events as well as input. This - * input-only adapter discards pending non-input events across the display - * (legacy callbacks already delivered lifecycle changes). Do not mix - * the two polling styles in one application. Use the display thread. - */ -bool yasl_private_surface_poll_event(struct yasl_surface *s, struct yasl_private_event *ev); /** * Decode the next local file path from a YASL_PRIVATE_EVENT_DROP payload. * @param cursor Required in/out position in the NUL-terminated list. Start it @@ -958,7 +886,7 @@ void yasl_private_surface_request_frame(struct yasl_surface *s); bool yasl_private_surface_set_pacing(struct yasl_surface *s, yasl_private_pacing_mode mode); /** Request a positive logical-pixel size. Success means issued, not configured. */ -bool yasl_private_surface_set_size(struct yasl_surface *s, struct yasl_private_vector2i size_logical); +bool yasl_private_surface_set_size(struct yasl_surface *s, struct yasl_private_vector2i size); /** Request fullscreen state; returns acceptance, not confirmation. */ bool yasl_private_surface_set_fullscreen(struct yasl_surface *s, bool enabled); /** Request maximized state; returns acceptance, not confirmation. */ diff --git a/src/platform/cocoa/api.h b/src/platform/cocoa/api.h index 4a22410..006954c 100644 --- a/src/platform/cocoa/api.h +++ b/src/platform/cocoa/api.h @@ -12,8 +12,6 @@ uint32_t cocoa_display_caps(const struct cocoa_display *d); int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms); void cocoa_display_flush(struct cocoa_display *d); int cocoa_display_modifiers(const struct cocoa_display *d); -void cocoa_display_copy(struct cocoa_display *d, enum yasl_private_selection sel, const char *text); -void cocoa_display_paste(struct cocoa_display *d, enum yasl_private_selection sel, struct cocoa_surface *target); bool cocoa_display_drag_start(struct cocoa_display *d, struct cocoa_surface *origin, const struct yasl_private_drag_image *image, const struct yasl_private_drag_listener *l, void *ud); struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d, const struct yasl_private_window_spec *spec, void *graphics_device); @@ -24,7 +22,6 @@ void cocoa_surface_set_callbacks(struct cocoa_surface *s, const struct yasl_priv void cocoa_surface_set_title(struct cocoa_surface *s, const char *title); void cocoa_surface_schedule_redraw(struct cocoa_surface *s); bool cocoa_surface_present(struct cocoa_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage); -bool cocoa_surface_poll_event(struct cocoa_surface *s, struct yasl_private_event *ev); struct yasl_private_geometry cocoa_surface_geometry(const struct cocoa_surface *s); struct yasl_private_timing cocoa_surface_timing(const struct cocoa_surface *s); void cocoa_surface_set_cursor(struct cocoa_surface *s, enum yasl_private_cursor cursor); diff --git a/src/platform/cocoa/window.m b/src/platform/cocoa/window.m index 7be78fd..4b97ce1 100644 --- a/src/platform/cocoa/window.m +++ b/src/platform/cocoa/window.m @@ -1,7 +1,6 @@ #import #import #import -#import #include "platform/cocoa/api.h" #include "common/backend.h" @@ -20,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -68,7 +66,6 @@ struct cocoa_surface { YaslWindowController *controller; YaslView *view; CAMetalLayer *layer; - NSOpenGLContext *gl_context; id software_queue; id software_buffer; NSUInteger software_buffer_capacity; @@ -130,8 +127,6 @@ struct cocoa_display { struct cocoa_surface **windows; int count; int capacity; - const struct yasl_private_display_cbs *cbs; - void *ud; char *app_id; struct yasl_private_loop *loop; id key_up_monitor; @@ -275,11 +270,7 @@ static void sync_size(struct cocoa_surface *w, bool force) w->layer.contentsScale = scale; w->layer.drawableSize = CGSizeMake(size.x, size.y); } - if (w->gl_context) - [w->gl_context update]; - /* Common configure notification already requests a frame before invoking - * the legacy callback. Finish local bookkeeping before that callback can - * destroy this surface. */ + /* Finish local bookkeeping before publishing geometry and requesting a frame. */ yasl_private_clock_note_resize(&w->clock, yasl_private_loop_now_ns() - start); yasl_private_surface_notify_configure(&w->base, (struct yasl_private_geometry){ .size = size, .scale_120 = scale_120, @@ -1642,10 +1633,6 @@ struct cocoa_display *cocoa_display_open(struct yasl_private_loop *loop, const s } darwin_ctx.base.loop = darwin_ctx.loop; if (spec) { - darwin_ctx.base.callbacks = spec->cbs ? *spec->cbs : (struct yasl_private_display_cbs){0}; - darwin_ctx.cbs = &darwin_ctx.base.callbacks; - darwin_ctx.ud = spec->ud; - darwin_ctx.base.callback_ud = spec->ud; if (spec->app_id) darwin_ctx.app_id = strdup(spec->app_id); darwin_ctx.base.app_id = darwin_ctx.app_id; } @@ -1789,7 +1776,6 @@ struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d, static void surface_free(struct cocoa_surface *w) { [w->link release]; - [w->gl_context release]; [w->software_buffer release]; [w->software_queue release]; [w->view release]; @@ -1987,11 +1973,6 @@ static NSPasteboard *pasteboard_for_selection(enum yasl_private_selection sel) static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_private_selection sel, const char *text); -void cocoa_display_copy(struct cocoa_display *d, enum yasl_private_selection sel, const char *text) -{ - (void)cocoa_display_copy_text(d, sel, text); -} - static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_private_selection sel, const char *text) { assert([NSThread isMainThread]); @@ -2030,11 +2011,6 @@ static bool cocoa_display_request_paste(struct cocoa_display *d, enum yasl_priva return true; } -void cocoa_display_paste(struct cocoa_display *d, enum yasl_private_selection sel, struct cocoa_surface *target) -{ - (void)cocoa_display_request_paste(d, sel, target, 0); -} - int cocoa_display_modifiers(const struct cocoa_display *d) { (void)d; @@ -2053,8 +2029,8 @@ static bool popup_spec_valid(const struct cocoa_surface *parent, const struct yasl_private_popup_spec *spec) { if (!parent || !spec) return false; - if (spec->positioner_explicit && (spec->anchor_edges > 8 || spec->gravity > 8 || - (spec->constraints & ~63u))) return false; + if (spec->anchor_edges > 8 || spec->gravity > 8 || + (spec->constraints & ~63u)) return false; int parent_cx = parent->is_popup ? parent->content_offset.x : 0; int parent_cy = parent->is_popup ? parent->content_offset.y : 0; int64_t x = (int64_t)spec->anchor.x - parent_cx; @@ -2069,70 +2045,6 @@ static bool popup_spec_valid(const struct cocoa_surface *parent, (int64_t)spec->content_offset.y + spec->content_size.y <= spec->size.y; } -static NSRect popup_calc_frame_legacy(struct cocoa_surface *parent, const struct yasl_private_popup_spec *spec) -{ - CGFloat scale = parent->window ? parent->window.backingScaleFactor : 1.0; - if (scale < 1.0) scale = 1.0; - - CGFloat pcx = (parent->is_popup ? parent->content_offset.x : 0) / scale; - CGFloat pcy = (parent->is_popup ? parent->content_offset.y : 0) / scale; - - CGFloat pw = spec->size.x / scale; - CGFloat ph = spec->size.y / scale; - CGFloat ax = (spec->anchor.x / scale) - pcx; - CGFloat ay = (spec->anchor.y / scale) - pcy; - CGFloat aw = spec->anchor.width / scale; - CGFloat ah = spec->anchor.height / scale; - CGFloat ox = spec->content_offset.x / scale; - CGFloat oy = spec->content_offset.y / scale; - - CGFloat top_left_x = spec->beside ? (ax + aw - ox) : (ax - ox); - CGFloat top_left_y = spec->beside ? (ay - oy) : (ay + ah - oy); - - CGFloat view_h = parent->view.bounds.size.height; - CGFloat view_y = view_h - top_left_y; - - NSPoint ptInView = NSMakePoint(top_left_x, view_y); - NSPoint ptInWin = [parent->view convertPoint:ptInView toView:nil]; - NSPoint screen_tl = [parent->window convertPointToScreen:ptInWin]; - - NSRect frame = NSMakeRect(screen_tl.x, screen_tl.y - ph, pw, ph); - - NSScreen *screen = parent->window.screen ?: [NSScreen mainScreen]; - if (screen) { - NSRect visible = screen.visibleFrame; - if (NSMaxX(frame) > NSMaxX(visible)) { - if (spec->beside) { - CGFloat flip_tl_x = ax - pw + ox; - NSPoint flip_v = NSMakePoint(flip_tl_x, view_y); - NSPoint flip_w = [parent->view convertPoint:flip_v toView:nil]; - NSPoint flip_s = [parent->window convertPointToScreen:flip_w]; - frame.origin.x = flip_s.x; - } - if (NSMaxX(frame) > NSMaxX(visible)) - frame.origin.x = NSMaxX(visible) - frame.size.width; - } - if (NSMinX(frame) < NSMinX(visible)) - frame.origin.x = NSMinX(visible); - - if (NSMinY(frame) < NSMinY(visible)) { - if (!spec->beside) { - CGFloat flip_top_y = ay - ph + oy; - CGFloat flip_vy = view_h - flip_top_y; - NSPoint flip_v = NSMakePoint(top_left_x, flip_vy); - NSPoint flip_w = [parent->view convertPoint:flip_v toView:nil]; - NSPoint flip_s = [parent->window convertPointToScreen:flip_w]; - frame.origin.y = flip_s.y - ph; - } - if (NSMinY(frame) < NSMinY(visible)) - frame.origin.y = NSMinY(visible); - } - if (NSMaxY(frame) > NSMaxY(visible)) - frame.origin.y = NSMaxY(visible) - frame.size.height; - } - return frame; -} - enum { POPUP_EDGE_TOP = 1, POPUP_EDGE_BOTTOM = 2, @@ -2227,8 +2139,6 @@ static NSRect popup_frame_explicit(struct cocoa_surface *parent, static NSRect popup_calc_frame(struct cocoa_surface *parent, const struct yasl_private_popup_spec *spec) { - if (!spec->positioner_explicit) - return popup_calc_frame_legacy(parent, spec); NSRect frame = popup_frame_explicit(parent, spec, spec->anchor_edges, spec->gravity); NSScreen *screen = parent->window.screen ?: [NSScreen mainScreen]; @@ -2505,22 +2415,6 @@ void cocoa_surface_set_callbacks(struct cocoa_surface *w, w->ud = ud; } -bool cocoa_surface_poll_event(struct cocoa_surface *w, struct yasl_private_event *ev) -{ - if (!w || !ev || !input_queue_pop(&w->input, ev)) - return false; - if (ev->type == YASL_PRIVATE_EVENT_INPUT_RESET) { - w->buttons = 0; - w->tablet_down = false; - w->mods = 0; - if (w->preedit) { - free(w->preedit); - w->preedit = NULL; - } - } - return true; -} - struct yasl_private_timing cocoa_surface_timing(const struct cocoa_surface *w) { struct yasl_private_timing timing = {0}; @@ -2572,16 +2466,6 @@ static int impl_display_modifiers(const struct yasl_private_display *d) return cocoa_display_modifiers((const struct cocoa_display *)d); } -static void impl_display_copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text) -{ - cocoa_display_copy((struct cocoa_display *)d, sel, text); -} - -static void impl_display_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target) -{ - cocoa_display_paste((struct cocoa_display *)d, sel, (struct cocoa_surface *)target); -} - static bool impl_display_copy_text(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text) { return cocoa_display_copy_text((struct cocoa_display *)d, sel, text); @@ -2725,116 +2609,6 @@ static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl return cocoa_surface_place_under_pointer((struct cocoa_surface *)s, at); } -static struct yasl_surface *impl_surface_create_gl(struct yasl_private_display *display, - const struct yasl_private_window_spec *spec, const int *attrs, struct yasl_surface *share) -{ - struct yasl_private_gl_options options; - if (!spec || !yasl_private_gl_parse_options(attrs, &options) || - options.api != YASL_PRIVATE_GL_OPENGL || options.debug || (share && - (share->display != display || share->presenter != YASL_PRIVATE_PRESENTER_GL))) - return NULL; - int profile = options.profile; - if (profile == YASL_PRIVATE_GL_ANY) - profile = options.major >= 3 ? YASL_PRIVATE_GL_CORE : YASL_PRIVATE_GL_COMPATIBILITY; - if (options.forward_compat && profile != YASL_PRIVATE_GL_CORE) - return NULL; - if ((profile == YASL_PRIVATE_GL_CORE && - (options.major != 3 || options.minor != 2)) || - (profile == YASL_PRIVATE_GL_COMPATIBILITY && - (options.major > 2 || (options.major == 2 && options.minor > 1)))) - return NULL; - - struct yasl_private_window_spec gl_spec = *spec; - gl_spec.external_gpu = true; - struct cocoa_surface *w = cocoa_surface_create_toplevel((struct cocoa_display *)display, &gl_spec, NULL); - if (!w) return NULL; - - NSOpenGLPixelFormatAttribute pixel_attrs[32]; - NSUInteger n = 0; - pixel_attrs[n++] = NSOpenGLPFAAccelerated; - pixel_attrs[n++] = NSOpenGLPFADoubleBuffer; - pixel_attrs[n++] = NSOpenGLPFAColorSize; - pixel_attrs[n++] = options.red_bits + options.green_bits + options.blue_bits + options.alpha_bits; - pixel_attrs[n++] = NSOpenGLPFAAlphaSize; - pixel_attrs[n++] = options.alpha_bits; - pixel_attrs[n++] = NSOpenGLPFADepthSize; - pixel_attrs[n++] = options.depth_bits; - pixel_attrs[n++] = NSOpenGLPFAStencilSize; - pixel_attrs[n++] = options.stencil_bits; - if (options.samples > 0) { - pixel_attrs[n++] = NSOpenGLPFASampleBuffers; - pixel_attrs[n++] = 1; - pixel_attrs[n++] = NSOpenGLPFASamples; - pixel_attrs[n++] = options.samples; - } - if (options.srgb) pixel_attrs[n++] = NSOpenGLPFASRGB; - if (profile == YASL_PRIVATE_GL_CORE) { - pixel_attrs[n++] = NSOpenGLPFAOpenGLProfile; - pixel_attrs[n++] = NSOpenGLProfileVersion3_2Core; - } else if (profile == YASL_PRIVATE_GL_COMPATIBILITY) { - pixel_attrs[n++] = NSOpenGLPFAOpenGLProfile; - pixel_attrs[n++] = NSOpenGLProfileVersionLegacy; - } - pixel_attrs[n++] = 0; - - NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixel_attrs]; - NSOpenGLContext *shared = share ? ((struct cocoa_surface *)share)->gl_context : nil; - if (format) - w->gl_context = [[NSOpenGLContext alloc] initWithFormat:format shareContext:shared]; - [format release]; - if (!w->gl_context) { - cocoa_surface_destroy(w); - reap_dead((struct cocoa_display *)display); - return NULL; - } - [w->view setLayer:[CALayer layer]]; - w->layer = nil; - [w->gl_context setView:w->view]; - w->base.presenter = YASL_PRIVATE_PRESENTER_GL; - return &w->base; -} - -static bool impl_gl_make_current(struct yasl_private_display *display, struct yasl_surface *surface) -{ - (void)display; - if (!surface) { - [NSOpenGLContext clearCurrentContext]; - return true; - } - struct cocoa_surface *w = (struct cocoa_surface *)surface; - if (w->base.presenter != YASL_PRIVATE_PRESENTER_GL || !w->gl_context || w->closed) - return false; - [w->gl_context makeCurrentContext]; - return [NSOpenGLContext currentContext] == w->gl_context; -} - -static yasl_private_gl_proc impl_gl_get_proc_address(struct yasl_private_display *display, const char *name) -{ - (void)display; - return name ? (yasl_private_gl_proc)dlsym(RTLD_DEFAULT, name) : NULL; -} - -static bool impl_gl_swap_interval(struct yasl_private_display *display, struct yasl_surface *surface, int interval) -{ - (void)display; - struct cocoa_surface *w = (struct cocoa_surface *)surface; - if (!w || w->base.presenter != YASL_PRIVATE_PRESENTER_GL || !w->gl_context || w->closed) - return false; - [w->gl_context setValues:&interval forParameter:NSOpenGLCPSwapInterval]; - return true; -} - -static bool impl_gl_swap_buffers(struct yasl_private_display *display, struct yasl_surface *surface) -{ - (void)display; - struct cocoa_surface *w = (struct cocoa_surface *)surface; - if (!w || w->base.presenter != YASL_PRIVATE_PRESENTER_GL || !w->gl_context || w->closed) - return false; - [w->gl_context flushBuffer]; - yasl_private_clock_presented(&w->clock, yasl_private_loop_now_ns(), false); - return true; -} - static void *impl_surface_get_metal_layer(struct yasl_surface *surface) { struct cocoa_surface *w = (struct cocoa_surface *)surface; @@ -2899,11 +2673,6 @@ static bool impl_surface_get_native_frame(struct yasl_surface *s, const struct yasl_private_backend_ops yasl_private_cocoa_backend = { .type = YASL_PRIVATE_BACKEND_COCOA, - .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, .surface_get_metal_layer = impl_surface_get_metal_layer, .surface_update_text_input = impl_surface_update_text_input, .display_open = impl_display_open, @@ -2913,8 +2682,6 @@ const struct yasl_private_backend_ops yasl_private_cocoa_backend = { .display_flush = impl_display_flush, .display_modifiers = impl_display_modifiers, .display_double_click_ms = impl_display_double_click_ms, - .display_copy = impl_display_copy, - .display_paste = impl_display_paste, .display_copy_text = impl_display_copy_text, .display_request_paste = impl_display_request_paste, .display_drag_start = impl_display_drag_start, diff --git a/src/platform/wayland/api.h b/src/platform/wayland/api.h index 9670af1..69f61b1 100644 --- a/src/platform/wayland/api.h +++ b/src/platform/wayland/api.h @@ -12,8 +12,6 @@ uint32_t wayland_display_caps(const struct wayland_display *d); int wayland_display_dispatch_timeout(struct wayland_display *d, int timeout_ms); void wayland_display_flush(struct wayland_display *d); int wayland_display_modifiers(const struct wayland_display *d); -void wayland_display_copy(struct wayland_display *d, enum yasl_private_selection sel, const char *text); -void wayland_display_paste(struct wayland_display *d, enum yasl_private_selection sel, struct wayland_surface *target); bool wayland_display_copy_text(struct wayland_display *d, enum yasl_private_selection sel, const char *text); bool wayland_display_request_paste(struct wayland_display *d, enum yasl_private_selection sel, struct wayland_surface *target, uint64_t request_id); @@ -28,7 +26,6 @@ void wayland_surface_set_callbacks(struct wayland_surface *s, const struct yasl_ void wayland_surface_set_title(struct wayland_surface *s, const char *title); void wayland_surface_schedule_redraw(struct wayland_surface *s); bool wayland_surface_present(struct wayland_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage); -bool wayland_surface_poll_event(struct wayland_surface *s, struct yasl_private_event *ev); struct yasl_private_geometry wayland_surface_geometry(const struct wayland_surface *s); struct yasl_private_timing wayland_surface_timing(const struct wayland_surface *s); void wayland_surface_set_cursor(struct wayland_surface *s, enum yasl_private_cursor cursor); diff --git a/src/platform/wayland/backend.c b/src/platform/wayland/backend.c index b3337cc..7ac1c65 100644 --- a/src/platform/wayland/backend.c +++ b/src/platform/wayland/backend.c @@ -40,11 +40,6 @@ static int impl_display_double_click_ms(const struct yasl_private_display *d) return portal_double_click_ms(); } -static void impl_display_copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text) -{ - wayland_display_copy((struct wayland_display *)d, sel, text); -} - static bool impl_display_copy_text(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text) { @@ -53,11 +48,6 @@ static bool impl_display_copy_text(struct yasl_private_display *d, enum yasl_pri return ok; } -static void impl_display_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target) -{ - wayland_display_paste((struct wayland_display *)d, sel, (struct wayland_surface *)target); -} - static bool impl_display_request_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target, uint64_t request_id) { @@ -348,11 +338,6 @@ static bool impl_surface_present(struct yasl_surface *s, const struct yasl_priva return wayland_surface_present((struct wayland_surface *)s, fb, damage); } -static bool impl_surface_poll_event(struct yasl_surface *s, struct yasl_private_event *ev) -{ - return wayland_surface_poll_event((struct wayland_surface *)s, ev); -} - static struct yasl_private_geometry impl_surface_geometry(const struct yasl_surface *s) { return wayland_surface_geometry((const struct wayland_surface *)s); @@ -449,11 +434,6 @@ 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, @@ -464,8 +444,6 @@ const struct yasl_private_backend_ops yasl_private_wayland_backend = { .display_flush = impl_display_flush, .display_modifiers = impl_display_modifiers, .display_double_click_ms = impl_display_double_click_ms, - .display_copy = impl_display_copy, - .display_paste = impl_display_paste, .display_copy_text = impl_display_copy_text, .display_request_paste = impl_display_request_paste, .display_drag_start = impl_display_drag_start, @@ -484,7 +462,6 @@ const struct yasl_private_backend_ops yasl_private_wayland_backend = { .surface_set_title = impl_surface_set_title, .surface_schedule_redraw = impl_surface_schedule_redraw, .surface_present = impl_surface_present, - .surface_poll_event = impl_surface_poll_event, .surface_set_pointer_mode = impl_surface_set_pointer_mode, .surface_geometry = impl_surface_geometry, .surface_timing = impl_surface_timing, diff --git a/src/platform/wayland/clipboard.c b/src/platform/wayland/clipboard.c index cf39b6c..9a75953 100644 --- a/src/platform/wayland/clipboard.c +++ b/src/platform/wayland/clipboard.c @@ -600,12 +600,6 @@ bool clipboard_copy(struct clipboard *c, const char *text, uint32_t serial) return true; } -void clipboard_paste(struct clipboard *c, struct wl_surface *target) -{ - if (c && target) - (void)clipboard_request_paste(c, YASL_PRIVATE_CLIPBOARD, target, 0); -} - bool clipboard_copy_primary(struct clipboard *c, const char *text, uint32_t serial) { if (!c || !c->primary_device || !c->primary_manager) { errno = ENOTSUP; return false; } @@ -627,12 +621,6 @@ bool clipboard_copy_primary(struct clipboard *c, const char *text, uint32_t seri return true; } -void clipboard_paste_primary(struct clipboard *c, struct wl_surface *target) -{ - if (c && target) - (void)clipboard_request_paste(c, YASL_PRIVATE_PRIMARY, target, 0); -} - bool clipboard_request_paste(struct clipboard *c, enum yasl_private_selection selection, struct wl_surface *target, uint64_t request_id) { diff --git a/src/platform/wayland/clipboard.h b/src/platform/wayland/clipboard.h index d03df17..7ab1719 100644 --- a/src/platform/wayland/clipboard.h +++ b/src/platform/wayland/clipboard.h @@ -25,12 +25,10 @@ void clipboard_destroy(struct clipboard *); bool clipboard_set_seat(struct clipboard *, struct wl_seat *); void clipboard_set_primary_manager(struct clipboard *, struct zwp_primary_selection_device_manager_v1 *); bool clipboard_copy(struct clipboard *, const char *, uint32_t); -void clipboard_paste(struct clipboard *, struct wl_surface *target); bool clipboard_request_paste(struct clipboard *, enum yasl_private_selection, struct wl_surface *target, uint64_t request_id); void clipboard_forget_surface(struct clipboard *, struct wl_surface *); bool clipboard_copy_primary(struct clipboard *, const char *, uint32_t); -void clipboard_paste_primary(struct clipboard *, struct wl_surface *target); typedef void (*clipboard_drag_fn)(enum yasl_private_drag_event, struct wl_surface *on, double x, double y, void *); diff --git a/src/platform/wayland/internal.h b/src/platform/wayland/internal.h index 92108c1..ff1b18f 100644 --- a/src/platform/wayland/internal.h +++ b/src/platform/wayland/internal.h @@ -244,8 +244,6 @@ struct wayland_display { struct wp_commit_timing_manager_v1 *commit_timing_mgr; struct xkb_context *xkb_ctx; struct yasl_private_loop *loop; - const struct yasl_private_display_cbs *cbs; - void *ud; char *app_id; struct xdg_toplevel_drag_manager_v1 *toplevel_drag_mgr; struct conn_drag drag; diff --git a/src/platform/wayland/keyboard.c b/src/platform/wayland/keyboard.c index 6f9dec9..9637341 100644 --- a/src/platform/wayland/keyboard.c +++ b/src/platform/wayland/keyboard.c @@ -425,11 +425,6 @@ int wayland_display_modifiers(const struct wayland_display *c) return c ? seat_modifiers(c->active_seat ? c->active_seat : c->seats) : 0; } -void wayland_display_copy(struct wayland_display *c, enum yasl_private_selection sel, const char *text) -{ - (void)wayland_display_copy_text(c, sel, text); -} - bool wayland_display_copy_text(struct wayland_display *c, enum yasl_private_selection sel, const char *text) { @@ -453,21 +448,6 @@ bool wayland_display_copy_text(struct wayland_display *c, enum yasl_private_sele return ok; } -void wayland_display_paste(struct wayland_display *c, enum yasl_private_selection sel, struct wayland_surface *target) -{ - struct seat *s = c ? (c->active_seat ? c->active_seat : c->seats) : NULL; - if (!target || target->closed || target->destroyed) return; - if (!s || !s->clipboard) { - yasl_private_surface_complete_paste(&target->base, 0, - YASL_PRIVATE_TRANSFER_UNAVAILABLE, NULL, 0); - return; - } - if (sel == YASL_PRIVATE_PRIMARY) - clipboard_paste_primary(s->clipboard, target->surface); - else - clipboard_paste(s->clipboard, target->surface); -} - bool wayland_display_request_paste(struct wayland_display *c, enum yasl_private_selection sel, struct wayland_surface *target, uint64_t request_id) { @@ -485,8 +465,3 @@ bool wayland_display_request_paste(struct wayland_display *c, enum yasl_private_ if (!ok) errno = ENOMEM; return ok; } - -bool wayland_surface_poll_event(struct wayland_surface *w, struct yasl_private_event *ev) -{ - return w && ev && input_queue_pop(&w->input, ev); -} diff --git a/src/platform/wayland/popup.c b/src/platform/wayland/popup.c index c563eb4..1ae4805 100644 --- a/src/platform/wayland/popup.c +++ b/src/platform/wayland/popup.c @@ -27,15 +27,14 @@ static bool popup_spec_valid(const struct wayland_surface *parent, const struct yasl_private_popup_spec *spec) { if (!parent || !spec) return false; - if (spec->positioner_explicit && - (spec->anchor_edges > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT || + if (spec->anchor_edges > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT || spec->gravity > XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT || (spec->constraints & ~(unsigned)(XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X | - XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y)))) + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y))) return false; int64_t x = (int64_t)spec->anchor.x - parent->content.x; int64_t y = (int64_t)spec->anchor.y - parent->content.y; @@ -121,15 +120,9 @@ static struct xdg_positioner *popup_positioner(struct wayland_surface *w, yasl_private_scale_to_logical(at.x, scale), yasl_private_scale_to_logical(at.y, scale), aw > 0 ? aw : 1, ah > 0 ? ah : 1); - uint32_t anchor = spec->positioner_explicit ? spec->anchor_edges : - (spec->beside ? XDG_POSITIONER_ANCHOR_TOP_RIGHT : XDG_POSITIONER_ANCHOR_TOP_LEFT); - uint32_t gravity = spec->positioner_explicit ? spec->gravity : - XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT; - uint32_t constraints = spec->positioner_explicit ? spec->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; + uint32_t anchor = spec->anchor_edges; + uint32_t gravity = spec->gravity; + uint32_t constraints = spec->constraints; xdg_positioner_set_anchor(pos, anchor); xdg_positioner_set_gravity(pos, gravity); xdg_positioner_set_constraint_adjustment(pos, constraints); diff --git a/src/platform/wayland/surface.c b/src/platform/wayland/surface.c index e04a594..635c905 100644 --- a/src/platform/wayland/surface.c +++ b/src/platform/wayland/surface.c @@ -347,9 +347,6 @@ struct wayland_display *wayland_display_open(struct yasl_private_loop *loop, con c->base.loop = c->loop; if (!c->loop) goto err; if (spec) { - c->base.callbacks = spec->cbs ? *spec->cbs : (struct yasl_private_display_cbs){0}; - c->cbs = &c->base.callbacks; - c->ud = spec->ud; if (spec->app_id) c->app_id = strdup(spec->app_id); c->base.app_id = c->app_id; } diff --git a/src/platform/x11/window.c b/src/platform/x11/window.c index ef13afe..43e2109 100644 --- a/src/platform/x11/window.c +++ b/src/platform/x11/window.c @@ -1384,8 +1384,8 @@ static bool popup_spec_valid(const struct x11_surface *parent, const struct yasl_private_popup_spec *spec) { if (!parent || !spec) return false; - if (spec->positioner_explicit && (spec->anchor_edges > 8 || spec->gravity > 8 || - (spec->constraints & ~63u))) return false; + if (spec->anchor_edges > 8 || spec->gravity > 8 || + (spec->constraints & ~63u)) return false; int parent_cx = parent->is_popup ? parent->content_offset.x : 0; int parent_cy = parent->is_popup ? parent->content_offset.y : 0; int64_t x = (int64_t)spec->anchor.x - parent_cx; @@ -1494,22 +1494,10 @@ static void popup_calculate_position(struct x11_display *d, struct yasl_private_vector2i size = spec->size; int anchor_x, anchor_y, root_x, root_y; - if (spec->positioner_explicit) { - popup_anchor_point(spec, &anchor_x, &anchor_y); - anchor_x += parent_rx - parent_cx; - anchor_y += parent_ry - parent_cy; - popup_position_at(spec, anchor_x, anchor_y, spec->gravity, size, &root_x, &root_y); - } else { - anchor_x = parent_rx + (spec->anchor.x - parent_cx); - anchor_y = parent_ry + (spec->anchor.y - parent_cy); - if (spec->beside) { - root_x = anchor_x + spec->anchor.width - spec->content_offset.x; - root_y = anchor_y - spec->content_offset.y; - } else { - root_x = anchor_x - spec->content_offset.x; - root_y = anchor_y + spec->anchor.height - spec->content_offset.y; - } - } + popup_anchor_point(spec, &anchor_x, &anchor_y); + anchor_x += parent_rx - parent_cx; + anchor_y += parent_ry - parent_cy; + popup_position_at(spec, anchor_x, anchor_y, spec->gravity, size, &root_x, &root_y); int mon_x = 0, mon_y = 0; int mon_w = d->native ? DisplayWidth(d->native, d->screen) : 1920; @@ -1535,60 +1523,44 @@ static void popup_calculate_position(struct x11_display *d, } #endif - if (spec->positioner_explicit) { - if (spec->constraints & X11_CONSTRAINT_FLIP_X) { - int flip_x, flip_y; - /* Reflect the selected anchor edge across the anchor rectangle center. */ - int anchor_origin_x = parent_rx - parent_cx + spec->anchor.x; - int flipped_anchor_x = 2 * anchor_origin_x + spec->anchor.width - anchor_x; - popup_position_at(spec, flipped_anchor_x, anchor_y, popup_flip_x(spec->gravity), size, &flip_x, &flip_y); - if (popup_overflow(flip_x, size.x, mon_x, mon_x + mon_w) < - popup_overflow(root_x, size.x, mon_x, mon_x + mon_w)) root_x = flip_x; - } - if (spec->constraints & X11_CONSTRAINT_FLIP_Y) { - int flip_x, flip_y; - int anchor_origin_y = parent_ry - parent_cy + spec->anchor.y; - int flipped_anchor_y = 2 * anchor_origin_y + spec->anchor.height - anchor_y; - popup_position_at(spec, anchor_x, flipped_anchor_y, popup_flip_y(spec->gravity), size, &flip_x, &flip_y); - if (popup_overflow(flip_y, size.y, mon_y, mon_y + mon_h) < - popup_overflow(root_y, size.y, mon_y, mon_y + mon_h)) root_y = flip_y; - } - if (spec->constraints & X11_CONSTRAINT_SLIDE_X) { - if (root_x < mon_x) root_x = mon_x; - if (root_x + size.x > mon_x + mon_w) root_x = mon_x + mon_w - size.x; - } - if (spec->constraints & X11_CONSTRAINT_SLIDE_Y) { - if (root_y < mon_y) root_y = mon_y; - if (root_y + size.y > mon_y + mon_h) root_y = mon_y + mon_h - size.y; - } - if ((spec->constraints & X11_CONSTRAINT_RESIZE_X) && - popup_overflow(root_x, size.x, mon_x, mon_x + mon_w)) { - if (root_x >= mon_x + mon_w) root_x = mon_x + mon_w - 1; - if (root_x < mon_x) { size.x -= mon_x - root_x; root_x = mon_x; } - if (root_x + size.x > mon_x + mon_w) size.x = mon_x + mon_w - root_x; - if (size.x < 1) size.x = 1; - } - if ((spec->constraints & X11_CONSTRAINT_RESIZE_Y) && - popup_overflow(root_y, size.y, mon_y, mon_y + mon_h)) { - if (root_y >= mon_y + mon_h) root_y = mon_y + mon_h - 1; - if (root_y < mon_y) { size.y -= mon_y - root_y; root_y = mon_y; } - if (root_y + size.y > mon_y + mon_h) size.y = mon_y + mon_h - root_y; - if (size.y < 1) size.y = 1; - } - } else { - if (spec->beside) { - if (root_x + size.x > mon_x + mon_w) { - int flipped_x = anchor_x - size.x + spec->content_offset.x; - if (flipped_x >= mon_x) root_x = flipped_x; - } - } else if (root_y + size.y > mon_y + mon_h) { - int flipped_y = anchor_y - size.y + spec->content_offset.y; - if (flipped_y >= mon_y) root_y = flipped_y; - } - if (root_x + size.x > mon_x + mon_w) root_x = mon_x + mon_w - size.x; + if (spec->constraints & X11_CONSTRAINT_FLIP_X) { + int flip_x, flip_y; + /* Reflect the selected anchor edge across the anchor rectangle center. */ + int anchor_origin_x = parent_rx - parent_cx + spec->anchor.x; + int flipped_anchor_x = 2 * anchor_origin_x + spec->anchor.width - anchor_x; + popup_position_at(spec, flipped_anchor_x, anchor_y, popup_flip_x(spec->gravity), size, &flip_x, &flip_y); + if (popup_overflow(flip_x, size.x, mon_x, mon_x + mon_w) < + popup_overflow(root_x, size.x, mon_x, mon_x + mon_w)) root_x = flip_x; + } + if (spec->constraints & X11_CONSTRAINT_FLIP_Y) { + int flip_x, flip_y; + int anchor_origin_y = parent_ry - parent_cy + spec->anchor.y; + int flipped_anchor_y = 2 * anchor_origin_y + spec->anchor.height - anchor_y; + popup_position_at(spec, anchor_x, flipped_anchor_y, popup_flip_y(spec->gravity), size, &flip_x, &flip_y); + if (popup_overflow(flip_y, size.y, mon_y, mon_y + mon_h) < + popup_overflow(root_y, size.y, mon_y, mon_y + mon_h)) root_y = flip_y; + } + if (spec->constraints & X11_CONSTRAINT_SLIDE_X) { if (root_x < mon_x) root_x = mon_x; - if (root_y + size.y > mon_y + mon_h) root_y = mon_y + mon_h - size.y; + if (root_x + size.x > mon_x + mon_w) root_x = mon_x + mon_w - size.x; + } + if (spec->constraints & X11_CONSTRAINT_SLIDE_Y) { if (root_y < mon_y) root_y = mon_y; + if (root_y + size.y > mon_y + mon_h) root_y = mon_y + mon_h - size.y; + } + if ((spec->constraints & X11_CONSTRAINT_RESIZE_X) && + popup_overflow(root_x, size.x, mon_x, mon_x + mon_w)) { + if (root_x >= mon_x + mon_w) root_x = mon_x + mon_w - 1; + if (root_x < mon_x) { size.x -= mon_x - root_x; root_x = mon_x; } + if (root_x + size.x > mon_x + mon_w) size.x = mon_x + mon_w - root_x; + if (size.x < 1) size.x = 1; + } + if ((spec->constraints & X11_CONSTRAINT_RESIZE_Y) && + popup_overflow(root_y, size.y, mon_y, mon_y + mon_h)) { + if (root_y >= mon_y + mon_h) root_y = mon_y + mon_h - 1; + if (root_y < mon_y) { size.y -= mon_y - root_y; root_y = mon_y; } + if (root_y + size.y > mon_y + mon_h) size.y = mon_y + mon_h - root_y; + if (size.y < 1) size.y = 1; } *out_x = root_x; @@ -2434,7 +2406,6 @@ static struct yasl_private_display *display_open(struct yasl_private_loop *loop, struct x11_display *d = calloc(1, sizeof(*d)); if (!d) return NULL; d->base.ops = &yasl_private_x11_backend; - d->base.callbacks = spec && spec->cbs ? *spec->cbs : (struct yasl_private_display_cbs){0}; d->native = XOpenDisplay(NULL); if (!d->native) { free(d); errno = ENODEV; return NULL; } XSetErrorHandler(yasl_private_x11_error_handler); @@ -2590,11 +2561,6 @@ static bool copy_text(struct yasl_private_display *base, enum yasl_private_selec return true; } -static void copy(struct yasl_private_display *base, enum yasl_private_selection sel, const char *text) -{ - (void)copy_text(base, sel, text); -} - static bool request_paste(struct yasl_private_display *base, enum yasl_private_selection sel, struct yasl_surface *target, uint64_t id) { @@ -2646,20 +2612,6 @@ static bool request_paste(struct yasl_private_display *base, enum yasl_private_s } } -static void paste(struct yasl_private_display *base, enum yasl_private_selection sel, - struct yasl_surface *target) -{ - (void)request_paste(base, sel, target, 0); -} - -static bool input_poll(struct yasl_surface *base, struct yasl_private_event *event) -{ - struct x11_surface *s = (struct x11_surface *)base; - if (s->destroyed) return false; - if (base->display->events) return yasl_private_events_poll_surface(base, event); - return input_queue_pop(&s->input, event); -} - static struct input_queue *surface_input_queue(struct yasl_surface *base) { return &((struct x11_surface *)base)->input; @@ -3077,8 +3029,6 @@ const struct yasl_private_backend_ops yasl_private_x11_backend = { .display_dispatch_timeout = dispatch_timeout, .display_flush = flush, .display_modifiers = mods, - .display_copy = copy, - .display_paste = paste, .display_copy_text = copy_text, .display_request_paste = request_paste, .display_drag_start = drag_start, @@ -3097,7 +3047,6 @@ const struct yasl_private_backend_ops yasl_private_x11_backend = { .surface_set_title = set_title, .surface_schedule_redraw = schedule_redraw, .surface_present = present, - .surface_poll_event = input_poll, .surface_geometry = geometry, .surface_timing = timing, .surface_frame_time_ns = frame_time, diff --git a/tests/cocoa/display.m b/tests/cocoa/display.m new file mode 100644 index 0000000..0b2d9d8 --- /dev/null +++ b/tests/cocoa/display.m @@ -0,0 +1,13 @@ +#import +#include "common/private_native.h" + +@interface NSView (YaslTestDisplayLink) +- (void)displayLinkFired:(id)link; +@end + +/* Exercise the native CPU presenter without depending on the host's cadence. */ +void yasl_test_frame_tick(struct yasl_surface *surface) +{ + NSWindow *window = (NSWindow *)yasl_private_surface_get_native_handle(surface); + [window.contentView displayLinkFired:nil]; +} diff --git a/tests/cocoa/test_cursor.m b/tests/cocoa/test_cursor.m index 4c961df..b50ab55 100644 --- a/tests/cocoa/test_cursor.m +++ b/tests/cocoa/test_cursor.m @@ -39,7 +39,7 @@ int main(void) assert([NSCursor currentCursor] == hidden); struct yasl_private_event event; bool moved = false; - while (yasl_private_surface_poll_event(surface, &event)) + while (yasl_private_display_poll_event(yasl_private_surface_display(surface), &event)) if (event.type == YASL_PRIVATE_EVENT_POINTER_MOTION) moved = true; assert(moved); diff --git a/tests/cocoa/test_keyboard.m b/tests/cocoa/test_keyboard.m index a41011c..a457a74 100644 --- a/tests/cocoa/test_keyboard.m +++ b/tests/cocoa/test_keyboard.m @@ -35,7 +35,7 @@ static struct yasl_private_event next(struct yasl_surface *surface, enum yasl_private_event_type type) { struct yasl_private_event event; - assert(yasl_private_surface_poll_event(surface, &event)); + assert(yasl_private_display_poll_event(yasl_private_surface_display(surface), &event)); assert(event.type == type && event.surface == surface); return event; } @@ -43,7 +43,7 @@ static struct yasl_private_event next(struct yasl_surface *surface, enum yasl_pr static void empty(struct yasl_surface *surface) { struct yasl_private_event event; - assert(!yasl_private_surface_poll_event(surface, &event)); + assert(!yasl_private_display_poll_event(yasl_private_surface_display(surface), &event)); } int main(void) @@ -65,7 +65,7 @@ int main(void) NSWindow *window = (NSWindow *)yasl_private_surface_get_native_handle(surface); NSView *view = (id)window.contentView; struct yasl_private_event event; - while (yasl_private_surface_poll_event(surface, &event)) {} + while (yasl_private_display_poll_event(yasl_private_surface_display(surface), &event)) {} InputSample *sample = [InputSample new]; NSEvent *native = (NSEvent *)sample; @@ -100,6 +100,8 @@ int main(void) [view setMarkedText:@"é" selectedRange:NSMakeRange(1, 0) replacementRange:NSMakeRange(1, 2)]; assert(strcmp(yasl_private_surface_ime_preedit(surface), "é") == 0); + event = next(surface, YASL_PRIVATE_EVENT_COMPOSITION); + assert(event.composition.active && !strcmp(event.composition.data, "é")); empty(surface); [view insertText:@"😀é" replacementRange:NSMakeRange(NSNotFound, 0)]; event = next(surface, YASL_PRIVATE_EVENT_TEXT_EDIT); @@ -107,6 +109,7 @@ int main(void) assert(event.text_edit.begin == 1 && event.text_edit.end == 5); assert(event.text_edit.revision == 7 && event.text_edit.generation == 3); assert(!yasl_private_surface_ime_preedit(surface)); + assert(!next(surface, YASL_PRIVATE_EVENT_COMPOSITION).composition.active); empty(surface); /* Exercise the keyDown -> interpretKeyEvents -> insertText route with diff --git a/tests/cocoa/test_native.c b/tests/cocoa/test_native.c index 9b8f8fa..d26a89d 100644 --- a/tests/cocoa/test_native.c +++ b/tests/cocoa/test_native.c @@ -49,8 +49,9 @@ static void test_window(enum yasl_private_backend request, bool null_spec) assert(state.display); assert(yasl_private_display_backend(state.display) == YASL_PRIVATE_BACKEND_COCOA); struct yasl_private_window_cbs callbacks = {.draw = draw}; + /* Check real Metal draw/ownership without waiting for a display-link tick. */ struct yasl_private_window_spec surface = { - .title = "Native backend test", .size = {160, 100}, + .title = "Native backend test", .size = {160, 100}, .tearing = true, .cbs = &callbacks, .ud = &state, }; state.surface = yasl_private_surface_create_toplevel(state.display, &surface); diff --git a/tests/cocoa/test_ownership.m b/tests/cocoa/test_ownership.m index 4efd164..10aae99 100644 --- a/tests/cocoa/test_ownership.m +++ b/tests/cocoa/test_ownership.m @@ -3,6 +3,7 @@ #include "common/backend.h" #import #import +#import #include "common/types.h" #include #include @@ -41,16 +42,8 @@ int main(void) assert(!hidden.isVisible && hidden.styleMask == NSWindowStyleMaskBorderless); yasl_private_surface_destroy(gpu); - struct yasl_surface *gl = d->ops->surface_create_gl(d, &gpu_spec, NULL, NULL); - if (gl) { - assert(gl->presenter == YASL_PRIVATE_PRESENTER_GL); - assert(d->ops->gl_make_current(d, gl)); - assert(d->ops->gl_get_proc_address(d, "glClear") != NULL); - assert(d->ops->gl_swap_interval(d, gl, 1)); - assert(d->ops->gl_swap_buffers(d, gl)); - assert(d->ops->gl_make_current(d, NULL)); - d->ops->surface_destroy(gl); - } + assert(!d->ops->surface_create_gl && !d->ops->gl_make_current && + !d->ops->gl_get_proc_address && !d->ops->gl_swap_interval && !d->ops->gl_swap_buffers); assert(yasl_private_surface_set_size(s, (struct yasl_private_vector2i){180, 110})); assert(yasl_private_surface_set_fullscreen(s, false)); @@ -58,7 +51,7 @@ int main(void) assert(!yasl_private_surface_set_size(s, (struct yasl_private_vector2i){0, 100})); assert(errno == EINVAL); struct yasl_private_popup_spec popup_spec = { - .positioner_explicit = true, + .anchor_edges = 4, .gravity = 3, .constraints = 63, .size = {80, 40}, .anchor = {10, 10, 2, 2}, }; @@ -117,7 +110,7 @@ int main(void) } const char *last = event.paste.data; assert(!yasl_private_display_poll_event(d, &event)); - @autoreleasepool { yasl_private_display_copy(d, YASL_PRIVATE_CLIPBOARD, "new selection"); } + @autoreleasepool { yasl_private_clipboard_set(d, YASL_PRIVATE_CLIPBOARD, "new selection"); } assert(strcmp(last, "paste-599") == 0); assert(!yasl_private_clipboard_set(d, YASL_PRIVATE_CLIPBOARD, "\xc3")); assert(errno == EILSEQ); @@ -131,11 +124,6 @@ int main(void) assert(unavailable_id && yasl_private_display_poll_event(d, &event)); assert(event.type == YASL_PRIVATE_EVENT_PASTE && event.paste.request_id == unavailable_id); assert(event.paste.status == YASL_PRIVATE_TRANSFER_UNAVAILABLE && !event.paste.len); - yasl_private_display_copy(d, YASL_PRIVATE_CLIPBOARD, "legacy paste"); - yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, s); - assert(yasl_private_display_poll_event(d, &event)); - assert(event.type == YASL_PRIVATE_EVENT_PASTE && event.paste.request_id == 0); - assert(event.paste.status == YASL_PRIVATE_TRANSFER_OK && strcmp(event.text.data, "legacy paste") == 0); yasl_private_surface_destroy(s); yasl_private_display_close(d); return 0; diff --git a/tests/cocoa/test_tablet.m b/tests/cocoa/test_tablet.m index 8eb63d5..5cd7375 100644 --- a/tests/cocoa/test_tablet.m +++ b/tests/cocoa/test_tablet.m @@ -46,7 +46,7 @@ static struct yasl_private_event next(struct yasl_surface *surface, enum yasl_private_event_type type) { struct yasl_private_event event; - assert(yasl_private_surface_poll_event(surface, &event)); + assert(yasl_private_display_poll_event(yasl_private_surface_display(surface), &event)); assert(event.type == type); return event; } @@ -62,7 +62,7 @@ int main(void) NSWindow *window = (NSWindow *)yasl_private_surface_get_native_handle(surface); NSView *view = window.contentView; struct yasl_private_event event; - while (yasl_private_surface_poll_event(surface, &event)) {} + while (yasl_private_display_poll_event(yasl_private_surface_display(surface), &event)) {} TabletSample *sample = [TabletSample new]; NSEvent *native = (NSEvent *)sample; sample.type = NSEventTypeTabletProximity; diff --git a/tests/common/test_backends.c b/tests/common/test_backends.c deleted file mode 100644 index 7eba57f..0000000 --- a/tests/common/test_backends.c +++ /dev/null @@ -1,495 +0,0 @@ -#define _POSIX_C_SOURCE 200809L -#include "common/private_native.h" -#if YASL_HAS_WAYLAND -#include "common/private_native_wayland.h" -#endif -#if YASL_HAS_X11 -#include "common/private_native_x11.h" -#endif -#include "common/private_loop.h" -#include "common/types.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct window { - struct yasl_surface *surface; - struct yasl_private_display *display; - int draws, configures, closes; - bool destroy_on_draw, destroy_on_configure; -}; - -struct pump { - struct yasl_private_loop *loop; - struct yasl_private_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_private_display_dispatch(p->displays[i], 0); - yasl_private_display_flush(p->displays[i]); - } - } - if (--p->ticks == 0) yasl_private_loop_quit(p->loop); - return 0; -} - -static void pump(struct yasl_private_loop *loop, struct yasl_private_display *a, struct yasl_private_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_private_loop_add_fd(loop, timer, POLLIN, tick, &p); - assert(watch > 0); - assert(yasl_private_loop_run(loop) == 0); - assert(yasl_private_loop_del_fd(loop, watch) == 0); - close(timer); -} - -static void draw(struct yasl_surface *s, struct yasl_private_frame *frame, void *ud) -{ - struct window *w = ud; - assert(s == w->surface); - assert(frame->surface == s); - assert(yasl_private_surface_display(s) == w->display); - struct yasl_private_vector2i size = yasl_private_surface_geometry(s).size; - assert(size.x == frame->target.size.x && size.y == frame->target.size.y); - assert(frame->target.pixels); - struct yasl_private_native_frame native = {0}; - assert(!yasl_private_surface_get_native_frame(s, &frame->target, &native)); - for (int i = 0; i < size.x * size.y; i++) frame->target.pixels[i] = 0xff4080c0; - struct yasl_private_damage_list damage = {.n = YASL_PRIVATE_DAMAGE_MAX + 1}; - assert(!yasl_private_surface_present(s, &frame->target, &damage)); - damage.n = -2; - assert(!yasl_private_surface_present(s, &frame->target, &damage)); - damage.n = 1; - damage.rects[0].width = -1; - assert(!yasl_private_surface_present(s, &frame->target, &damage)); - damage.n = -1; - struct yasl_private_framebuffer invalid = frame->target; - invalid.size.x++; - assert(!yasl_private_surface_present(s, &invalid, &damage)); - invalid = frame->target; - invalid.generation--; - assert(!yasl_private_surface_present(s, &invalid, &damage)); - invalid = frame->target; - invalid.pixels = (uint32_t *)(uintptr_t)1; - assert(!yasl_private_surface_present(s, &invalid, &damage)); - assert(!yasl_private_surface_present(s, &frame->target, NULL)); - assert(yasl_private_surface_present(s, &frame->target, &damage)); - assert(!yasl_private_surface_present(s, &frame->target, &damage)); - w->draws++; - if (w->destroy_on_draw) { yasl_private_surface_destroy(s); w->surface = NULL; } -} - -static void configure(struct yasl_surface *s, struct yasl_private_geometry geometry, void *ud) -{ - struct window *w = ud; - assert(w->surface == s); - struct yasl_private_vector2i size = yasl_private_surface_geometry(s).size; - assert(geometry.size.x == size.x && geometry.size.y == size.y); - assert(geometry.scale_120 == yasl_private_surface_geometry(s).scale_120); - w->configures++; - if (w->destroy_on_configure) { yasl_private_surface_destroy(s); w->surface = NULL; } -} - -static void draw_without_present(struct yasl_surface *s, struct yasl_private_frame *frame, void *ud) -{ - (void)s; - struct window *w = ud; - size_t count = (size_t)frame->target.size.x * (size_t)frame->target.size.y; - for (size_t i = 0; i < count; i++) frame->target.pixels[i] = 0xff000000; - w->draws++; -} - -static void close_window(struct yasl_surface *s, void *ud) -{ - struct window *w = ud; - assert(s == w->surface); - w->closes++; - yasl_private_surface_destroy(s); - w->surface = NULL; -} - -static const struct yasl_private_window_cbs callbacks = { - .draw = draw, .configure = configure, .close = close_window, -}; - -static void attach(struct window *w) -{ - struct yasl_private_window_cbs temporary = callbacks; - yasl_private_surface_set_callbacks(w->surface, &temporary, w); - temporary.draw = NULL; - temporary.configure = NULL; - temporary.close = NULL; -} - -static struct window create(struct yasl_private_display *d) -{ - struct yasl_private_window_spec spec = {.title = "Backend test", .size = {160, 100}}; - struct yasl_surface *s = yasl_private_surface_create_toplevel(d, &spec); - assert(s); - assert(yasl_private_display_get_native_handle(d)); - assert(yasl_private_surface_get_native_handle(s)); - assert(yasl_private_surface_display(s) == d); - assert(yasl_private_surface_timing(s).source == YASL_PRIVATE_TIMING_UNKNOWN); - return (struct window){.surface = s, .display = d}; -} - -/* The last presented frame reads back until a later draw replaces it. */ -static void test_read_pixels(struct yasl_private_loop *loop, struct yasl_private_display *d, struct window *w) -{ - struct yasl_private_vector2i size = {-1, -1}; - assert(!yasl_private_surface_read_pixels(w->surface, NULL, 0, NULL)); - assert(!yasl_private_surface_read_pixels(NULL, NULL, 0, &size) && !size.x && !size.y); - assert(!yasl_private_surface_read_pixels(w->surface, NULL, 0, &size)); - struct yasl_private_vector2i extent = yasl_private_surface_geometry(w->surface).size; - assert(size.x == extent.x && size.y == extent.y); - size_t count = (size_t)size.x * (size_t)size.y; - uint32_t *pixels = calloc(count, sizeof(*pixels)); - assert(pixels); - assert(!yasl_private_surface_read_pixels(w->surface, pixels, count - 1, &size)); - assert(!pixels[0]); - assert(yasl_private_surface_read_pixels(w->surface, pixels, count, &size)); - for (size_t i = 0; i < count; i++) assert(pixels[i] == 0xff4080c0); - - /* Draws that never present must not show through. X11 reuses its one - * target; Wayland keeps the presented buffer while the compositor holds it. */ - struct yasl_private_window_cbs skip = callbacks; - skip.draw = draw_without_present; - yasl_private_surface_set_callbacks(w->surface, &skip, w); - for (int i = 0; i < 4; i++) { - yasl_private_surface_schedule_redraw(w->surface); - pump(loop, d, NULL); - if (yasl_private_surface_read_pixels(w->surface, pixels, count, &size)) { - assert(yasl_private_display_backend(d) != YASL_PRIVATE_BACKEND_X11); - for (size_t j = 0; j < count; j++) assert(pixels[j] == 0xff4080c0); - } else { - assert(!size.x && !size.y); - } - } - - attach(w); - yasl_private_surface_schedule_redraw(w->surface); - pump(loop, d, NULL); - memset(pixels, 0, count * sizeof(*pixels)); - assert(yasl_private_surface_read_pixels(w->surface, pixels, count, &size)); - assert(pixels[0] == 0xff4080c0 && pixels[count - 1] == 0xff4080c0); - free(pixels); -} - -static void test_window_spec(struct yasl_private_loop *loop, struct yasl_private_display *d) -{ - struct window w = {.display = d, .destroy_on_draw = true}; - { - struct yasl_private_window_cbs temporary = callbacks; - struct yasl_private_window_spec spec = { - .title = "Callbacks at creation", .size = {160, 100}, - .cbs = &temporary, .ud = &w, - }; - w.surface = yasl_private_surface_create_toplevel(d, &spec); - assert(w.surface); - assert(!w.draws && !w.configures && !w.closes); - /* Neither the spec nor its callback table needs to outlive creation. */ - memset(&temporary, 0, sizeof(temporary)); - memset(&spec, 0, sizeof(spec)); - } - yasl_private_surface_schedule_redraw(w.surface); - pump(loop, d, NULL); - assert(w.draws == 1 && !w.surface); -} - -static void x11_behavior(struct yasl_private_loop *loop, struct yasl_private_display *d, struct window *w) -{ - Display *native = yasl_private_display_get_native_handle(d); - Window window = (Window)(uintptr_t)yasl_private_surface_get_native_handle(w->surface); - assert(native && window); - assert(yasl_private_x11_display_get_display(d) == native); - assert(yasl_private_x11_surface_get_window(w->surface) == (uintptr_t)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_private_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_private_vector2i size = yasl_private_surface_geometry(w->surface).size; - assert(size.x == 200 && size.y == 120); - /* A server-generated motion verifies the XI2 subscription and cookie - * dispatch path, including source-device lookup, rather than SendEvent. */ - struct yasl_private_event pointer_event; - while (yasl_private_surface_poll_event(w->surface, &pointer_event)) {} - XWarpPointer(native, None, window, 0, 0, 0, 0, 80, 50); - XFlush(native); - pump(loop, d, NULL); - bool saw_motion = false; - while (yasl_private_surface_poll_event(w->surface, &pointer_event)) { - if (pointer_event.type == YASL_PRIVATE_EVENT_POINTER_MOTION) { - assert(pointer_event.pointer.device == YASL_PRIVATE_PTR_DEV_MOUSE); - assert(!pointer_event.pointer.pen.pressure); - if (pointer_event.pointer.pos.x == 80 && pointer_event.pointer.pos.y == 50) - saw_motion = true; - } - } - assert(saw_motion); - 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_private_display_copy(d, YASL_PRIVATE_CLIPBOARD, "clipboard text"); - yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, w->surface); - pump(loop, d, NULL); - bool saw_key = false, saw_paste = false, saw_text = false; - struct yasl_private_event event; - while (yasl_private_surface_poll_event(w->surface, &event)) { - assert(event.surface == w->surface); - if (event.type == YASL_PRIVATE_EVENT_KEY) { - assert(event.key.codepoint == 'a'); - assert(event.key.shifted == 'A'); - assert(event.key.native_code == key.xkey.keycode); - saw_key = event.key.key == YASL_KEY_A; - } - if (event.type == YASL_PRIVATE_EVENT_PASTE) { - assert(event.text.len == strlen("clipboard text")); - assert(memcmp(event.text.data, "clipboard text", event.text.len) == 0); - saw_paste = true; - } - if (event.type == YASL_PRIVATE_EVENT_TEXT) { - assert(event.text.len == 1 && event.text.data[0] == 'a'); - saw_text = true; - } - } - assert(saw_key && saw_paste && saw_text); - /* Physical identity survives modifier and layout changes, including a - * release whose modifiers differ from its press. */ - key.xkey.state = ShiftMask; - XSendEvent(native, window, False, KeyPressMask, &key); - key.xkey.type = KeyRelease; - key.xkey.state = 0; - XSendEvent(native, window, False, KeyReleaseMask, &key); - XFlush(native); - pump(loop, d, NULL); - int key_count = 0, text_count = 0; - while (yasl_private_surface_poll_event(w->surface, &event)) { - if (event.type == YASL_PRIVATE_EVENT_KEY) { - assert(event.key.key == YASL_KEY_A); - assert(event.key.action == (key_count ? YASL_KEY_RELEASE : YASL_KEY_PRESS)); - key_count++; - } else if (event.type == YASL_PRIVATE_EVENT_TEXT) { - assert(event.text.len == 1 && event.text.data[0] == 'A'); - text_count++; - } - } - assert(key_count == 2 && text_count == 1); - key.xkey.type = KeyPress; - key.xkey.state = ControlMask; - XSendEvent(native, window, False, KeyPressMask, &key); - XFlush(native); - pump(loop, d, NULL); - assert(yasl_private_surface_poll_event(w->surface, &event)); - assert(event.type == YASL_PRIVATE_EVENT_KEY && event.key.key == YASL_KEY_A); - assert(event.key.mods & YASL_PRIVATE_MOD_CTRL); - assert(!yasl_private_surface_poll_event(w->surface, &event)); - - int symbols_per_key = 0; - KeySym *saved_symbols = XGetKeyboardMapping(native, key.xkey.keycode, 1, &symbols_per_key); - assert(saved_symbols && symbols_per_key >= 2); - KeySym *changed_symbols = calloc((size_t)symbols_per_key, sizeof(KeySym)); - assert(changed_symbols); - changed_symbols[0] = XK_q; - changed_symbols[1] = XK_Q; - XChangeKeyboardMapping(native, key.xkey.keycode, symbols_per_key, changed_symbols, 1); - XSync(native, False); - pump(loop, d, NULL); /* Consume MappingNotify before translating the key. */ - key.xkey.state = 0; - XSendEvent(native, window, False, KeyPressMask, &key); - XFlush(native); - pump(loop, d, NULL); - saw_key = saw_text = false; - while (yasl_private_surface_poll_event(w->surface, &event)) { - if (event.type == YASL_PRIVATE_EVENT_KEY) { - assert(event.key.key == YASL_KEY_A && event.key.codepoint == 'q'); - saw_key = true; - } else if (event.type == YASL_PRIVATE_EVENT_TEXT) { - assert(event.text.len == 1 && event.text.data[0] == 'q'); - saw_text = true; - } - } - assert(saw_key && saw_text); - XChangeKeyboardMapping(native, key.xkey.keycode, symbols_per_key, saved_symbols, 1); - free(changed_symbols); - XFree(saved_symbols); - XSync(native, False); - pump(loop, d, NULL); - - for (unsigned button = 4; button <= 7; button++) { - XEvent wheel = {.xbutton = {.type = ButtonPress, .display = native, - .window = window, .root = DefaultRootWindow(native), - .same_screen = True, .button = button}}; - /* Direct delivery exercises the core fallback even with XI2 enabled. */ - XSendEvent(native, window, False, NoEventMask, &wheel); - wheel.xbutton.type = ButtonRelease; - XSendEvent(native, window, False, NoEventMask, &wheel); - XFlush(native); - pump(loop, d, NULL); - assert(yasl_private_surface_poll_event(w->surface, &event)); - assert(event.type == YASL_PRIVATE_EVENT_POINTER_SCROLL); - assert(event.pointer.scroll.x == (button == 6 ? -1 : button == 7 ? 1 : 0)); - assert(event.pointer.scroll.y == (button == 4 ? -1 : button == 5 ? 1 : 0)); - assert(!yasl_private_surface_poll_event(w->surface, &event)); - } - assert(yasl_private_surface_timing(w->surface).source == YASL_PRIVATE_TIMING_SUBMISSION); - assert(!(yasl_private_display_caps(d) & YASL_PRIVATE_CAP_FRAME_PACING)); - - /* Reuse caller storage, replace the selection, and exceed the old queue - * cap before polling: each paste must retain its own original contents. */ - for (int i = 0; i < 300; i++) { - char text[32]; - snprintf(text, sizeof(text), "owned-%d", i); - yasl_private_display_copy(d, YASL_PRIVATE_CLIPBOARD, text); - yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, w->surface); - memset(text, 'x', sizeof(text)); - } - for (int i = 0; i < 300; i++) { - char text[32]; - snprintf(text, sizeof(text), "owned-%d", i); - assert(yasl_private_surface_poll_event(w->surface, &event)); - assert(event.type == YASL_PRIVATE_EVENT_PASTE && strcmp(event.text.data, text) == 0); - } - const char *last = event.text.data; - assert(!yasl_private_surface_poll_event(w->surface, &event)); - yasl_private_display_copy(d, YASL_PRIVATE_CLIPBOARD, "changed"); - pump(loop, d, NULL); - assert(strcmp(last, "owned-299") == 0); - yasl_private_display_copy(d, YASL_PRIVATE_CLIPBOARD, ""); - yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, w->surface); - assert(yasl_private_surface_poll_event(w->surface, &event)); - assert(event.type == YASL_PRIVATE_EVENT_PASTE && !event.text.len && !event.text.data[0]); - - /* Concurrent external conversions must not share a mutable target slot. */ - struct yasl_private_display_spec ds = {.backend = YASL_PRIVATE_BACKEND_X11}; - struct yasl_private_display *owner = yasl_private_display_open(loop, &ds); - assert(owner); - yasl_private_display_copy(owner, YASL_PRIVATE_CLIPBOARD, "external"); - struct window other = create(d), cancelled = create(d); - yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, w->surface); - yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, other.surface); - yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, cancelled.surface); - yasl_private_surface_destroy(cancelled.surface); - pump(loop, d, owner); - int first_pastes = 0, second_pastes = 0; - while (yasl_private_surface_poll_event(w->surface, &event)) { - if (event.type == YASL_PRIVATE_EVENT_PASTE) { - assert(strcmp(event.text.data, "external") == 0); - first_pastes++; - } - } - while (yasl_private_surface_poll_event(other.surface, &event)) { - if (event.type == YASL_PRIVATE_EVENT_PASTE) { - assert(strcmp(event.text.data, "external") == 0); - second_pastes++; - } - } - assert(first_pastes == 1 && second_pastes == 1); - yasl_private_surface_destroy(other.surface); - yasl_private_display_close(owner); - 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_private_loop *loop = yasl_private_loop_create(); - assert(loop); - if (strcmp(argv[1], "failure") == 0) { - struct yasl_private_display_spec spec = {.backend = (enum yasl_private_backend)atoi(argv[2])}; - assert(!yasl_private_display_open(loop, &spec)); - } else if (strcmp(argv[1], "mixed") == 0) { - struct yasl_private_display_spec spec = {.backend = YASL_PRIVATE_BACKEND_WAYLAND}; - struct yasl_private_display *a = yasl_private_display_open(loop, &spec); - spec.backend = YASL_PRIVATE_BACKEND_X11; - struct yasl_private_display *b = yasl_private_display_open(loop, &spec); - assert(a && b); - test_window_spec(loop, a); - test_window_spec(loop, b); - struct window wa = create(a), wb = create(b); -#if YASL_HAS_WAYLAND - assert(yasl_private_wayland_display_get_wl_display(a) != NULL); - assert(yasl_private_wayland_surface_get_wl_surface(wa.surface) != NULL); - assert(!yasl_private_x11_display_get_display(a)); -#endif -#if YASL_HAS_X11 - assert(yasl_private_x11_display_get_display(b) != NULL); - assert(yasl_private_x11_surface_get_window(wb.surface) != 0); - assert(!yasl_private_wayland_display_get_wl_display(b)); -#endif - attach(&wa); - attach(&wb); - pump(loop, a, b); - assert(wa.draws && wb.draws); - yasl_private_surface_destroy(wa.surface); - yasl_private_surface_destroy(wb.surface); - yasl_private_display_close(a); - yasl_private_display_close(b); - } else { - assert(argc == 4); - struct yasl_private_display_spec spec = {.backend = (enum yasl_private_backend)atoi(argv[2])}; - struct yasl_private_display *d = strcmp(argv[1], "null") == 0 ? - yasl_private_display_open(loop, NULL) : yasl_private_display_open(loop, &spec); - assert(d); - assert(yasl_private_display_backend(d) == (enum yasl_private_backend)atoi(argv[3])); - struct window w = create(d); - pump(loop, d, NULL); - assert(w.draws == 0); - attach(&w); - yasl_private_surface_schedule_redraw(w.surface); - pump(loop, d, NULL); - assert(w.draws > 0); - test_read_pixels(loop, d, &w); - if (yasl_private_display_backend(d) == YASL_PRIVATE_BACKEND_X11) - x11_behavior(loop, d, &w); - if (w.surface) yasl_private_surface_destroy(w.surface); - w = create(d); - w.destroy_on_draw = true; - attach(&w); - pump(loop, d, NULL); - assert(w.draws && !w.surface); - yasl_private_display_close(d); - } - yasl_private_loop_destroy(loop); - puts("Backend test passed."); - return 0; -} diff --git a/tests/common/test_contracts.c b/tests/common/test_contracts.c index be84586..f9f427f 100644 --- a/tests/common/test_contracts.c +++ b/tests/common/test_contracts.c @@ -117,10 +117,10 @@ static bool present(struct yasl_surface *s, const struct yasl_private_framebuffe return true; } static uint32_t caps(const struct yasl_private_display *d) { (void)d; return 0; } -static void copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text) -{ (void)d; (void)sel; (void)text; copies++; } -static void paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *s) -{ (void)d; (void)sel; (void)s; pastes++; } +static bool copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text) +{ (void)d; (void)sel; (void)text; copies++; return true; } +static bool paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *s, uint64_t id) +{ (void)d; (void)sel; (void)s; assert(id); pastes++; return true; } static struct yasl_surface *create(struct yasl_private_display *d, const struct yasl_private_window_spec *spec, void *graphics_device) { (void)d; (void)spec; (void)graphics_device; creates++; return NULL; } @@ -260,7 +260,7 @@ static void test_native(void) static void test_validation(void) { const struct yasl_private_backend_ops ops = {.surface_present = present, - .display_caps = caps, .display_copy = copy, .display_paste = paste, + .display_caps = caps, .display_copy_text = copy, .display_request_paste = paste, .surface_create_toplevel = create}; struct yasl_private_display d = {.ops = &ops}; struct yasl_surface s = {.display = &d, .frame_generation = 1}; @@ -293,14 +293,16 @@ static void test_validation(void) assert(yasl_private_surface_present(&s, &fb, &damage) && received.n == -1); damage.n = 0; assert(yasl_private_surface_present(&s, &fb, &damage) && received.n == 0); - yasl_private_display_copy(&d, YASL_PRIVATE_PRIMARY, "no alias"); - yasl_private_display_copy(&d, (enum yasl_private_selection)255, "invalid"); - yasl_private_display_paste(&d, YASL_PRIVATE_PRIMARY, &s); - yasl_private_display_paste(&d, (enum yasl_private_selection)255, &s); + assert(yasl_private_events_open(&d)); + yasl_private_clipboard_set(&d, YASL_PRIVATE_PRIMARY, "no alias"); + yasl_private_clipboard_set(&d, (enum yasl_private_selection)255, "invalid"); + yasl_private_clipboard_request(&s, YASL_PRIVATE_PRIMARY); + yasl_private_clipboard_request(&s, (enum yasl_private_selection)255); assert(!copies && !pastes); - yasl_private_display_copy(&d, YASL_PRIVATE_CLIPBOARD, "yes"); - yasl_private_display_paste(&d, YASL_PRIVATE_CLIPBOARD, &s); + yasl_private_clipboard_set(&d, YASL_PRIVATE_CLIPBOARD, "yes"); + yasl_private_clipboard_request(&s, YASL_PRIVATE_CLIPBOARD); assert(copies == 1 && pastes == 1); + yasl_private_events_close(&d); struct yasl_private_window_spec spec = {.size = {100, 80}}; assert(!yasl_private_surface_create_native(&d, &spec, &d) && errno == ENOTSUP); assert(!yasl_private_surface_create_native(&d, &spec, NULL) && errno == EINVAL); diff --git a/tests/common/test_events.c b/tests/common/test_events.c index fcfc118..e9f61da 100644 --- a/tests/common/test_events.c +++ b/tests/common/test_events.c @@ -179,20 +179,23 @@ static void test_failures(void) } -static void test_legacy_poll_does_not_spin(void) +static void test_fifo_preserves_notifications(void) { struct yasl_private_display d; struct yasl_surface a, b; setup(&d, &a, &b); yasl_private_surface_notify_state(&a, YASL_PRIVATE_STATE_ACTIVATED); yasl_private_surface_notify_close(&b); - assert(yasl_private_events_pending(&d)); + yasl_private_surface_notify_composition(&a, "preedit", 0, 7); + assert(input_queue_push(&b.input, &(struct yasl_private_event){ + .type = YASL_PRIVATE_EVENT_PEN, .surface = &b})); + const enum yasl_private_event_type types[] = {YASL_PRIVATE_EVENT_WINDOW_STATE, + YASL_PRIVATE_EVENT_WINDOW_CLOSE, YASL_PRIVATE_EVENT_COMPOSITION, YASL_PRIVATE_EVENT_PEN}; struct yasl_private_event ev; - assert(!yasl_private_surface_poll_event(&a, &ev)); - assert(!yasl_private_events_pending(&d)); - assert(input_queue_push(&b.input, &(struct yasl_private_event){.type = YASL_PRIVATE_EVENT_KEY, .surface = &b})); - assert(!yasl_private_surface_poll_event(&a, &ev)); - assert(yasl_private_surface_poll_event(&b, &ev) && ev.surface == &b); + for (size_t i = 0; i < sizeof(types) / sizeof(*types); ++i) { + assert(yasl_private_display_poll_event(&d, &ev)); + assert(ev.type == types[i] && ev.surface == (i % 2 ? &b : &a)); + } assert(!yasl_private_events_pending(&d)); teardown(&d, &a, &b); } @@ -203,6 +206,6 @@ int main(void) test_post_cleanup(); test_damage(); test_failures(); - test_legacy_poll_does_not_spin(); + test_fifo_preserves_notifications(); return 0; } diff --git a/tests/common/test_surface_integration.c b/tests/common/test_surface_integration.c index 2752208..7cc938a 100644 --- a/tests/common/test_surface_integration.c +++ b/tests/common/test_surface_integration.c @@ -1,6 +1,7 @@ #define _POSIX_C_SOURCE 200809L #include #include +#include #include "common/backend.h" #include #include @@ -8,6 +9,9 @@ #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; @@ -32,6 +36,15 @@ int main(int argc,char **argv) 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(!yasl_gl_get_proc_address("glClear")); + assert(yasl_get_error(NULL)==YASL_ERROR_UNSUPPORTED); + } /* 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++) { @@ -79,7 +92,12 @@ int main(int argc,char **argv) 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)); - drain();assert(refreshes>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()=0); + assert(refreshes>0); yasl_surface_set_refresh_callback(s,NULL,NULL); yasl_surface_destroy(s); assert(!yasl_set_backend(backend)); diff --git a/tests/helpers/package_cocoa_tests.py b/tests/helpers/package_cocoa_tests.py index 8f801f8..1f72126 100644 --- a/tests/helpers/package_cocoa_tests.py +++ b/tests/helpers/package_cocoa_tests.py @@ -21,8 +21,8 @@ def main(): if host['system'] != 'darwin': parser.error('Use a configured and compiled Cocoa build.') tests = json.loads((info / 'intro-tests.json').read_text()) - required = {'native', 'native-ownership', 'keyboard-cocoa', 'tablet-cocoa', - 'cursor-cocoa', 'hello-cocoa-window', 'tablet-cocoa-window'} + required = {'surface-cocoa', 'native-cocoa', 'ownership-cocoa', 'keyboard-cocoa', + 'tablet-cocoa', 'cursor-cocoa'} missing = required - {test['name'] for test in tests} if missing: parser.error('Enable integration_tests; missing: ' + ', '.join(sorted(missing))) diff --git a/tests/helpers/run_backends.py b/tests/helpers/run_backends.py deleted file mode 100644 index af7e02d..0000000 --- a/tests/helpers/run_backends.py +++ /dev/null @@ -1,119 +0,0 @@ -#!/usr/bin/env python3 -"""Test public window behavior on isolated display servers.""" - -import os -from pathlib import Path -import subprocess -import sys -import tempfile -import time - - -def wait_for(process, ready): - deadline = time.monotonic() + 10 - while time.monotonic() < deadline: - if process.poll() is not None: - raise RuntimeError("The display server stopped.") - if ready(): - return - time.sleep(0.02) - raise TimeoutError("The display server did not start.") - - -def main(): - weston, xwayland, test, timers, tablet, hello, input_x11, interactive, app, app_input = sys.argv[1:] - servers = [] - with tempfile.TemporaryDirectory(prefix="yasl-backends-") as directory: - root = Path(directory) - env = os.environ.copy() - env.pop("WAYLAND_SOCKET", None) - env.update(XDG_RUNTIME_DIR=directory, WAYLAND_DISPLAY="yasl-test", - XDG_SESSION_TYPE="wayland") - with (root / "weston.log").open("w+") as wl_log, \ - (root / "xwayland.log").open("w+") as x_log: - try: - wl = subprocess.Popen([ - weston, "--backend=headless", "--renderer=pixman", - "--socket=yasl-test", "--idle-time=0", "--no-config", - ], env=env, stdout=wl_log, stderr=wl_log) - servers.append(wl) - wait_for(wl, lambda: (root / "yasl-test").is_socket()) - display_file = root / "display" - with display_file.open("w") as display_fd: - x = subprocess.Popen([ - xwayland, "-displayfd", str(display_fd.fileno()), - "-shm", "-noreset", "-nolisten", "tcp", - ], env=env, pass_fds=(display_fd.fileno(),), stdout=x_log, stderr=x_log) - servers.append(x) - wait_for(x, lambda: bool(display_file.read_text().strip())) - env["DISPLAY"] = ":" + display_file.read_text().strip() - - def run(mode, request=None, expected=None, **changes): - case_env = env.copy() - for key, value in changes.items(): - if value is None: - case_env.pop(key, None) - else: - case_env[key] = value - args = [test, mode] - if request is not None: - args.append(str(request)) - if expected is not None: - args.append(str(expected)) - print("Test:", mode, request, expected, changes, flush=True) - subprocess.run(args, env=case_env, check=True, timeout=8) - - run("open", 1, 1) - run("open", 2, 2) - run("open", 0, 1) - run("null", 0, 1) - run("open", 0, 2, WAYLAND_DISPLAY=None, XDG_SESSION_TYPE="x11") - run("open", 0, 2, WAYLAND_DISPLAY="missing-socket") - run("failure", 1, WAYLAND_DISPLAY="missing-socket") - run("failure", 2, DISPLAY=":59999") - run("failure", 0, WAYLAND_DISPLAY="missing-socket", DISPLAY=":59999") - run("mixed") - print("Test: Wayland surface timers", flush=True) - subprocess.run([timers], env=env, check=True, timeout=8) - x_env = env.copy() - x_env.pop("WAYLAND_DISPLAY", None) - x_env["XDG_SESSION_TYPE"] = "x11" - print("Test: X11 surface timers", flush=True) - subprocess.run([timers], env=x_env, check=True, timeout=8) - print("Test: X11 UTF-8 keyboard fallback and clipboard", flush=True) - subprocess.run([input_x11], env=x_env, check=True, timeout=8) - print("Test: Wayland runner lifecycle and event destruction", flush=True) - subprocess.run([app], env=env, check=True, timeout=8) - subprocess.run([app_input], env=env, check=True, timeout=8) - print("Test: X11 runner lifecycle", flush=True) - subprocess.run([app], env=x_env, check=True, timeout=8) - for backend in ("wayland", "x11"): - print("Test: tablet example on", backend, flush=True) - subprocess.run([tablet, "--backend", backend, "--smoke-test"], - env=env, check=True, timeout=8) - print("Test: hello example on", backend, flush=True) - subprocess.run([hello], - env=env if backend == "wayland" else x_env, - check=True, timeout=8) - print("Test: interactive example on", backend, flush=True) - subprocess.run([interactive, "--smoke-test"], - env=env if backend == "wayland" else x_env, - check=True, timeout=8) - except BaseException: - for log in (wl_log, x_log): - log.flush() - log.seek(0) - print(log.read(), file=sys.stderr) - raise - finally: - for server in reversed(servers): - server.terminate() - try: - server.wait(timeout=5) - except subprocess.TimeoutExpired: - server.kill() - server.wait() - - -if __name__ == "__main__": - main() diff --git a/tests/meson.build b/tests/meson.build index d61d094..8370423 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -33,7 +33,7 @@ test('public-exports', python, args: [files('helpers/check_exports.py')] + expor foreach unit : ['contracts', 'utf8', 'timers', 'loop', 'selection', 'events', 'gl_options'] exe = executable('yasl-' + unit + '-test', 'common/test_' + unit + '.c', include_directories: inc, dependencies: yasl_dep) - test(unit, exe) + test(unit, exe, is_parallel: not has_cocoa or unit != 'timers') endforeach keyboard_sources = files('common/test_keyboard.c') if not has_wayland @@ -80,10 +80,14 @@ if has_x11 endif endif if get_option('integration_tests') - integration = executable('yasl-surface-integration', 'common/test_surface_integration.c', + display_test_sources = has_cocoa ? files('cocoa/display.m') : [] + integration = executable('yasl-surface-integration', ['common/test_surface_integration.c'] + display_test_sources, include_directories: inc, dependencies: yasl_dep) - gl_integration = executable('yasl-gl-integration', 'common/test_gl.c', - dependencies: yasl_dep) + gl_integration = '-' + if has_egl + gl_integration = executable('yasl-gl-integration', 'common/test_gl.c', + dependencies: yasl_dep) + endif vulkan_integration = '-' if has_vulkan vulkan_integration = executable('yasl-vulkan-integration', 'common/test_vulkan.c', dependencies: yasl_dep) @@ -99,12 +103,12 @@ if get_option('integration_tests') test('reconnect-wayland', python, args: [files('helpers/run_reconnect.py'), weston, reconnect], timeout: 60) endif elif has_cocoa - test('surface-cocoa', integration, args: ['cocoa'], timeout: 30) + test('surface-cocoa', integration, args: ['cocoa'], timeout: 30, is_parallel: false) foreach unit : ['native', 'ownership', 'keyboard', 'tablet', 'cursor'] extension = unit == 'native' ? '.c' : '.m' exe = executable('yasl-' + unit + '-cocoa-test', 'cocoa/test_' + unit + extension, include_directories: inc, dependencies: yasl_dep) - test(unit + '-cocoa', exe, timeout: 30) + test(unit + '-cocoa', exe, timeout: 30, is_parallel: false) endforeach endif endif diff --git a/tests/wayland/test_cursor.c b/tests/wayland/test_cursor.c index 3425821..87cea9a 100644 --- a/tests/wayland/test_cursor.c +++ b/tests/wayland/test_cursor.c @@ -77,15 +77,15 @@ int main(void) tablet_tool_motion(&tool, tool.proxy, wl_fixed_from_int(56), wl_fixed_from_int(78)); tool_frame(&tool, tool.proxy, 0); struct yasl_private_event event; - assert(yasl_private_surface_poll_event(&surface.base, &event)); + assert(input_queue_pop(&surface.input, &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_private_surface_poll_event(&surface.base, &event)); + assert(input_queue_pop(&surface.input, &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(input_queue_pop(&surface.input, &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_private_surface_poll_event(&surface.base, &event)); + assert(!input_queue_pop(&surface.input, &event)); /* Each new entry needs a request with its own serial. */ pointer_leave(&seat, seat.pointer, 12, surface.surface); diff --git a/tests/wayland/test_input.c b/tests/wayland/test_input.c index 2b5592a..820eb8b 100644 --- a/tests/wayland/test_input.c +++ b/tests/wayland/test_input.c @@ -62,7 +62,7 @@ int main(void) char text[32]; snprintf(text, sizeof(text), "commit-%d", i); struct yasl_private_event event; - assert(wayland_surface_poll_event(&w, &event)); + assert(input_queue_pop(&w.input, &event)); assert(event.type == YASL_PRIVATE_EVENT_TEXT && event.surface == &w.base); assert(strcmp(event.text.data, text) == 0); } @@ -77,21 +77,21 @@ int main(void) 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(input_queue_pop(&w.input, &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)); + assert(!input_queue_pop(&w.input, &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(input_queue_pop(&w.input, &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)); + assert(!input_queue_pop(&w.input, &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 ae4647f..2f55cf6 100644 --- a/tests/wayland/test_keyboard.c +++ b/tests/wayland/test_keyboard.c @@ -4,7 +4,7 @@ static struct yasl_private_event next(struct wayland_surface *w, enum yasl_private_event_type type) { struct yasl_private_event ev; - assert(yasl_private_surface_poll_event(&w->base, &ev)); + assert(input_queue_pop(&w->input, &ev)); assert(ev.type == type && ev.surface == &w->base); return ev; } @@ -12,7 +12,7 @@ static struct yasl_private_event next(struct wayland_surface *w, enum yasl_priva static void empty(struct wayland_surface *w) { struct yasl_private_event ev; - assert(!yasl_private_surface_poll_event(&w->base, &ev)); + assert(!input_queue_pop(&w->input, &ev)); } static void layout(struct seat *s, const char *name, const char *variant) diff --git a/tests/wayland/test_popup_positioner.c b/tests/wayland/test_popup_positioner.c index 86778f6..a851ddb 100644 --- a/tests/wayland/test_popup_positioner.c +++ b/tests/wayland/test_popup_positioner.c @@ -1,5 +1,4 @@ -/* Capture xdg_positioner requests to verify explicit placement and the legacy - * beside mapping without requiring a compositor. */ +/* Capture explicit xdg_positioner placement without requiring a compositor. */ #include "platform/wayland/internal.h" #include @@ -56,7 +55,12 @@ int main(void) struct yasl_private_popup_spec spec = { .size = {64, 48}, .anchor = {13, 24, 8, 6}, - .beside = true, + .anchor_edges = XDG_POSITIONER_ANCHOR_TOP_RIGHT, + .gravity = XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT, + .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, }; struct xdg_positioner *positioner = popup_positioner(&popup, &spec); @@ -72,7 +76,6 @@ int main(void) 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 | diff --git a/tests/wayland/test_relative_pointer.c b/tests/wayland/test_relative_pointer.c index d44238d..81cec2f 100644 --- a/tests/wayland/test_relative_pointer.c +++ b/tests/wayland/test_relative_pointer.c @@ -68,7 +68,7 @@ static void shape_device_destroy(struct wp_cursor_shape_device_v1 *device) { ass static struct yasl_private_event next_event(struct wayland_surface *w) { struct yasl_private_event event; - assert(yasl_private_surface_poll_event(&w->base, &event)); + assert(input_queue_pop(&w->input, &event)); return event; } @@ -121,7 +121,7 @@ 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_private_surface_poll_event(&surface.base, &event)); + assert(!input_queue_pop(&surface.input, &event)); /* Locked: relative samples in device pixels, position frozen at the lock. */ pointer_locked(&surface, surface.pointer_lock); @@ -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_private_surface_poll_event(&surface.base, &event)); + assert(!input_queue_pop(&surface.input, &event)); move_to(&seat, 8, 5); event = next_event(&surface); assert(event.pointer.delta.x == 2 && event.pointer.delta.y == 0); diff --git a/tests/wayland/test_state.c b/tests/wayland/test_state.c index 1ce9189..c02e01b 100644 --- a/tests/wayland/test_state.c +++ b/tests/wayland/test_state.c @@ -20,12 +20,15 @@ struct notifications { uint32_t state; }; -static void state_changed(struct yasl_surface *surface, uint32_t state, void *ud) +static void collect(struct yasl_private_display *display, struct notifications *notifications) { - struct notifications *notifications = ud; - assert(yasl_private_surface_state(surface) == state); - notifications->count++; - notifications->state = state; + struct yasl_private_event event; + while (yasl_private_display_poll_event(display, &event)) { + assert(event.type == YASL_PRIVATE_EVENT_WINDOW_STATE); + assert(yasl_private_surface_state(event.surface) == event.state.flags); + notifications->count++; + notifications->state = event.state.flags; + } } static void configure(struct wayland_surface *surface, uint32_t *states, size_t count) @@ -38,31 +41,39 @@ 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_PRIVATE_STATE_CSD}; - wayland_surface_set_callbacks(&surface, &(struct yasl_private_window_cbs){ - .state = state_changed}, ¬ifications); + const struct yasl_private_backend_ops ops = {0}; + struct yasl_private_display display = {.ops = &ops}; + struct wayland_surface surface = {.base = {.display = &display, .state = YASL_PRIVATE_STATE_CSD}}; + assert(yasl_private_events_open(&display)); + assert(yasl_private_events_register(&surface.base)); 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)); + collect(&display, ¬ifications); assert(notifications.count == 1); assert(notifications.state == (YASL_PRIVATE_STATE_CSD | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_ACTIVATED)); configure(&surface, fullscreen, sizeof(fullscreen) / sizeof(*fullscreen)); + collect(&display, ¬ifications); 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); + collect(&display, ¬ifications); assert(notifications.count == 2); assert(notifications.state == (YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_ACTIVATED)); toplevel_decoration_configure(&surface, NULL, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + collect(&display, ¬ifications); assert(notifications.count == 2); uint32_t maximized[] = {XDG_TOPLEVEL_STATE_MAXIMIZED, XDG_TOPLEVEL_STATE_SUSPENDED}; configure(&surface, maximized, sizeof(maximized) / sizeof(*maximized)); + collect(&display, ¬ifications); assert(notifications.count == 3); assert(notifications.state == (YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_SUSPENDED)); toplevel_decoration_configure(&surface, NULL, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE); + collect(&display, ¬ifications); assert(notifications.count == 4); assert(notifications.state == (YASL_PRIVATE_STATE_CSD | YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_SUSPENDED)); @@ -72,19 +83,21 @@ int main(void) XDG_TOPLEVEL_STATE_TILED_TOP, XDG_TOPLEVEL_STATE_TILED_BOTTOM}; for (size_t i = 0; i < sizeof(edges) / sizeof(*edges); i++) { configure(&surface, &edges[i], 1); + collect(&display, ¬ifications); assert(notifications.count == 5); assert(notifications.state == (YASL_PRIVATE_STATE_CSD | YASL_PRIVATE_STATE_TILED)); } configure(&surface, edges, 0); + collect(&display, ¬ifications); assert(notifications.count == 6); assert(notifications.state == YASL_PRIVATE_STATE_CSD); - /* State still advances when applications have not installed callbacks. */ - wayland_surface_set_callbacks(&surface, NULL, NULL); + /* The authoritative state advances with the queued notification. */ configure(&surface, fullscreen, sizeof(fullscreen) / sizeof(*fullscreen)); assert(yasl_private_surface_state(&surface.base) == (YASL_PRIVATE_STATE_CSD | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_ACTIVATED)); - assert(notifications.count == 6); + collect(&display, ¬ifications); + assert(notifications.count == 7); /* External presentation has no SHM commit callback to acknowledge xdg * configures. Apply geometry and acknowledge before renderer submission. */ @@ -106,5 +119,7 @@ int main(void) 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); + yasl_private_events_unregister(&surface.base); + yasl_private_events_close(&display); return 0; } diff --git a/tests/wayland/test_tablet.c b/tests/wayland/test_tablet.c index f47fbfa..0cce568 100644 --- a/tests/wayland/test_tablet.c +++ b/tests/wayland/test_tablet.c @@ -4,7 +4,7 @@ static struct yasl_private_event next_raw(struct wayland_surface *surface) { struct yasl_private_event event; - assert(yasl_private_surface_poll_event(&surface->base, &event)); + assert(input_queue_pop(&surface->input, &event)); assert(event.surface == &surface->base); return event; } @@ -111,9 +111,9 @@ int main(void) 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)); + assert(!input_queue_pop(&surface.input, &unused)); pointer_frame(&seat, NULL); - assert(!yasl_private_surface_poll_event(&surface.base, &unused)); + assert(!input_queue_pop(&surface.input, &unused)); /* Finger scrolling carries pixel offsets and coherent gesture phases. */ pointer_axis_source(&seat, NULL, WL_POINTER_AXIS_SOURCE_FINGER); @@ -152,7 +152,7 @@ int main(void) pointer_axis_value120(&seat, NULL, WL_POINTER_AXIS_VERTICAL_SCROLL, 60); seat_pointer_reset(&seat); pointer_frame(&seat, NULL); - assert(!yasl_private_surface_poll_event(&surface.base, &unused)); + assert(!input_queue_pop(&surface.input, &unused)); input_queue_clear(&surface.input); return 0; } diff --git a/tests/x11/test_input.c b/tests/x11/test_input.c index ccad859..a89812b 100644 --- a/tests/x11/test_input.c +++ b/tests/x11/test_input.c @@ -6,12 +6,14 @@ static void check_selection_encoding(struct x11_display *d, struct x11_surface *s) { + struct yasl_private_event queued; + while (yasl_private_display_poll_event(&d->base, &queued)) {} Window requestor = XCreateSimpleWindow(d->native, DefaultRootWindow(d->native), 0, 0, 1, 1, 0, 0, 0); Atom property = XInternAtom(d->native, "YASL_PRIVATE_TEST_STRING", False); XSelectionRequestEvent request = {.display = d->native, .requestor = requestor, .selection = d->clipboard, .target = XA_STRING, .property = property}; - yasl_private_display_copy(&d->base, YASL_PRIVATE_CLIPBOARD, "café £"); + yasl_private_clipboard_set(&d->base, YASL_PRIVATE_CLIPBOARD, "café £"); selection_request(d, &request); Atom actual; int format; @@ -22,7 +24,7 @@ static void check_selection_encoding(struct x11_display *d, struct x11_surface * assert(actual == XA_STRING && format == 8 && count == 6); assert(memcmp(data, "caf\xe9 \xa3", count) == 0); XFree(data); - yasl_private_display_copy(&d->base, YASL_PRIVATE_CLIPBOARD, "字"); + yasl_private_clipboard_set(&d->base, YASL_PRIVATE_CLIPBOARD, "字"); selection_request(d, &request); assert(XGetWindowProperty(d->native, requestor, property, 0, 100, True, AnyPropertyType, &actual, &format, &count, &left, &data) == Success); @@ -72,7 +74,7 @@ static void check_selection_encoding(struct x11_display *d, struct x11_surface * XSelectionEvent reply = {.requestor = pending->window, .property = property}; selection_notify(d, &reply); struct yasl_private_event event; - assert(yasl_private_surface_poll_event(&s->base, &event)); + assert(yasl_private_display_poll_event(&d->base, &event)); assert(event.type == YASL_PRIVATE_EVENT_PASTE && event.paste.request_id == i + 1); assert(event.paste.status == cases[i].status); if (cases[i].status == YASL_PRIVATE_TRANSFER_OK) { @@ -85,7 +87,7 @@ static void check_selection_encoding(struct x11_display *d, struct x11_surface * pending->timeout = 0; paste_timeout(pending, 0, PASTE_TIMEOUT_NS); } - assert(!yasl_private_surface_poll_event(&s->base, &event)); + assert(!yasl_private_display_poll_event(&d->base, &event)); } struct paste_request *rejected = calloc(1, sizeof(*rejected)); @@ -100,7 +102,7 @@ static void check_selection_encoding(struct x11_display *d, struct x11_surface * XSelectionEvent no_property = {.requestor = rejected->window, .property = None}; selection_notify(d, &no_property); struct yasl_private_event event; - assert(yasl_private_surface_poll_event(&s->base, &event)); + assert(yasl_private_display_poll_event(&d->base, &event)); assert(event.type == YASL_PRIVATE_EVENT_PASTE && event.paste.request_id == 99); assert(event.paste.status == YASL_PRIVATE_TRANSFER_FAILED); } @@ -336,21 +338,21 @@ int main(void) /* Native creation and keymap updates may already have queued focus or * reset input on the shared display stream. Isolate this synthetic key. */ struct yasl_private_event event; - while (yasl_private_surface_poll_event(surface, &event)) {} + while (yasl_private_display_poll_event(base, &event)) {} XKeyEvent native = {.type = KeyPress, .display = d->native, .window = s->window, .keycode = code}; key_event(s, &native, false); - assert(yasl_private_surface_poll_event(surface, &event)); + assert(yasl_private_display_poll_event(base, &event)); assert(event.type == YASL_PRIVATE_EVENT_KEY && event.key.key == YASL_KEY_A); assert(event.key.codepoint == cases[i].label); - assert(yasl_private_surface_poll_event(surface, &event)); + assert(yasl_private_display_poll_event(base, &event)); assert(event.type == YASL_PRIVATE_EVENT_TEXT && event.text.len == strlen(cases[i].text)); assert(strcmp(event.text.data, cases[i].text) == 0); - assert(!yasl_private_surface_poll_event(surface, &event)); + assert(!yasl_private_display_poll_event(base, &event)); native.type = KeyRelease; key_event(s, &native, false); - assert(yasl_private_surface_poll_event(surface, &event) && event.type == YASL_PRIVATE_EVENT_KEY); - assert(!yasl_private_surface_poll_event(surface, &event)); + assert(yasl_private_display_poll_event(base, &event) && event.type == YASL_PRIVATE_EVENT_KEY); + assert(!yasl_private_display_poll_event(base, &event)); } XChangeKeyboardMapping(d->native, code, width, saved, 1); XSync(d->native, False); diff --git a/tests/x11/test_state.c b/tests/x11/test_state.c index 4696e60..0c36638 100644 --- a/tests/x11/test_state.c +++ b/tests/x11/test_state.c @@ -28,12 +28,15 @@ int state_property(Display *display, Window window, Atom property, return Success; } -static void state_changed(struct yasl_surface *surface, uint32_t state, void *ud) +static void collect(struct yasl_private_display *display) { - (void)ud; - assert(yasl_private_surface_state(surface) == state); - reported_state = state; - notifications++; + struct yasl_private_event event; + while (yasl_private_display_poll_event(display, &event)) { + assert(event.type == YASL_PRIVATE_EVENT_WINDOW_STATE); + assert(yasl_private_surface_state(event.surface) == event.state.flags); + reported_state = event.state.flags; + notifications++; + } } /* Build an XSETTINGS blob in either byte order. */ @@ -98,35 +101,45 @@ int main(void) display.native = (Display *)&display; struct x11_surface surface = {.base = {.display = &display.base, .state = YASL_PRIVATE_STATE_ACTIVATED}, .window = 42}; - set_callbacks(&surface.base, &(struct yasl_private_window_cbs){.state = state_changed}, NULL); + assert(yasl_private_events_open(&display.base)); + assert(yasl_private_events_register(&surface.base)); properties[0] = display.fullscreen; property_count = 1; update_state(&surface); + collect(&display.base); assert(notifications == 1); assert(reported_state == (YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_ACTIVATED)); update_state(&surface); + collect(&display.base); assert(notifications == 1); /* Both maximized axes are required; activation survives property changes. */ properties[0] = display.maximized_h; update_state(&surface); + collect(&display.base); assert(notifications == 2 && reported_state == YASL_PRIVATE_STATE_ACTIVATED); properties[1] = display.maximized_v; property_count = 2; update_state(&surface); + collect(&display.base); assert(notifications == 3); assert(reported_state == (YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_ACTIVATED)); properties[2] = display.fullscreen; property_count = 3; update_state(&surface); + collect(&display.base); assert(notifications == 4); assert(reported_state == (YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_ACTIVATED)); property_count = 0; update_state(&surface); + collect(&display.base); assert(notifications == 5 && reported_state == YASL_PRIVATE_STATE_ACTIVATED); update_state(&surface); + collect(&display.base); assert(notifications == 5); + yasl_private_events_unregister(&surface.base); + yasl_private_events_close(&display.base); return 0; } diff --git a/tests/x11/test_tablet.c b/tests/x11/test_tablet.c index be5da4c..8d9106e 100644 --- a/tests/x11/test_tablet.c +++ b/tests/x11/test_tablet.c @@ -24,31 +24,32 @@ struct drag_test_recorder { struct yasl_private_vector2i last_pos; }; -static void on_test_drag(struct yasl_surface *s, enum yasl_private_drag_event ev, - struct yasl_private_vector2i pos, void *ud) -{ - (void)s; - struct drag_test_recorder *rec = ud; - rec->last_pos = pos; - if (ev == YASL_PRIVATE_DRAG_OVER) rec->over_count++; - else if (ev == YASL_PRIVATE_DRAG_LEAVE) rec->leave_count++; - else if (ev == YASL_PRIVATE_DRAG_DROP) rec->drop_count++; -} - struct drop_test_recorder { int over_count; int leave_count; struct yasl_private_vector2i last_pos; }; -static void on_test_drop(struct yasl_surface *s, enum yasl_private_drop_event ev, - struct yasl_private_vector2i pos, void *ud) +static void collect(struct yasl_private_display *display, struct yasl_surface *first, + struct yasl_surface *second, struct drag_test_recorder *a, + struct drag_test_recorder *b, struct drop_test_recorder *drop) { - (void)s; - struct drop_test_recorder *rec = ud; - rec->last_pos = pos; - if (ev == YASL_PRIVATE_DROP_OVER) rec->over_count++; - else if (ev == YASL_PRIVATE_DROP_LEAVE) rec->leave_count++; + struct yasl_private_event event; + while (yasl_private_display_poll_event(display, &event)) { + if (event.type == YASL_PRIVATE_EVENT_DRAG) { + assert(event.surface == first || event.surface == second); + struct drag_test_recorder *rec = event.surface == first ? a : b; + rec->last_pos = event.drag.pos; + if (event.drag.action == YASL_PRIVATE_DRAG_OVER) rec->over_count++; + else if (event.drag.action == YASL_PRIVATE_DRAG_LEAVE) rec->leave_count++; + else if (event.drag.action == YASL_PRIVATE_DRAG_DROP) rec->drop_count++; + } else if (event.type == YASL_PRIVATE_EVENT_DROP_HOVER) { + assert(event.surface == first); + drop->last_pos = event.drop.pos; + if (event.drop.action == YASL_PRIVATE_DROP_OVER) drop->over_count++; + else if (event.drop.action == YASL_PRIVATE_DROP_LEAVE) drop->leave_count++; + } + } } static void on_test_drag_end(void *ud, enum yasl_private_drag_end how) @@ -81,7 +82,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) struct yasl_private_popup_spec valid_spec = { .size = {100, 120}, .anchor = {30, 40, 50, 60}, - .beside = false, + .anchor_edges = 6, .gravity = 8, .constraints = 15, .content_offset = {5, 10}, }; assert(popup_spec_valid(s, &valid_spec)); @@ -93,7 +94,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) /* Explicit XDG-style anchor/gravity stays exact unless a constraint allows a fit. */ struct yasl_private_popup_spec positioned = { - .positioner_explicit = true, + .anchor_edges = 2, /* bottom */ .gravity = 2, /* below */ .size = {100, 120}, .anchor = {100, 100, 40, 20}, @@ -118,7 +119,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) popup_calculate_position(d, s, &positioned, &x, &y, &actual_size); assert(x == 1920 - actual_size.x && actual_size.x == 20); - valid_spec.beside = true; + valid_spec.anchor_edges = 7; popup_calculate_position(d, s, &valid_spec, &x, &y, &actual_size); assert(x == 30 + 50 - 5); assert(y == 40 - 10); @@ -133,7 +134,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) struct yasl_private_popup_spec repo_spec = { .size = {150, 180}, .anchor = {10, 20, 30, 40}, - .beside = false, + .anchor_edges = 6, .gravity = 8, .constraints = 15, .content_offset = {0, 0}, }; assert(surface_popup_reposition(popup_base, &repo_spec)); @@ -149,10 +150,10 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) struct drag_test_recorder rec1 = {0}; struct drag_test_recorder rec2 = {0}; - struct yasl_private_window_cbs cbs1 = {.drag = on_test_drag}; - struct yasl_private_window_cbs cbs2 = {.drag = on_test_drag}; - set_callbacks(&s->base, &cbs1, &rec1); - set_callbacks(&s2.base, &cbs2, &rec2); + struct drop_test_recorder drec = {0}; + assert(yasl_private_events_open(&d->base)); + assert(yasl_private_events_register(&s->base)); + assert(yasl_private_events_register(&s2.base)); enum yasl_private_drag_end end_reason = (enum yasl_private_drag_end)-1; struct yasl_private_drag_listener l = {.end = on_test_drag_end}; @@ -163,6 +164,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) XIDeviceEvent mev = {.evtype = XI_Motion, .sourceid = 8, .event = 42, .event_x = 12.0, .event_y = 34.0}; XGenericEventCookie cookie = {.evtype = XI_Motion, .data = &mev}; xi_event(d, &cookie); + collect(&d->base, &s->base, &s2.base, &rec1, &rec2, &drec); assert(rec1.over_count == 1); assert(rec1.last_pos.x == 12 && rec1.last_pos.y == 34); @@ -171,6 +173,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) mev.event_x = 56.0; mev.event_y = 78.0; xi_event(d, &cookie); + collect(&d->base, &s->base, &s2.base, &rec1, &rec2, &drec); assert(rec1.leave_count == 1); assert(rec2.over_count == 1); assert(rec2.last_pos.x == 56 && rec2.last_pos.y == 78); @@ -179,6 +182,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) XIDeviceEvent rel = {.evtype = XI_ButtonRelease, .sourceid = 8, .event = 43, .detail = 1}; cookie = (XGenericEventCookie){.evtype = XI_ButtonRelease, .data = &rel}; xi_event(d, &cookie); + collect(&d->base, &s->base, &s2.base, &rec1, &rec2, &drec); assert(rec2.drop_count == 1); assert(end_reason == YASL_PRIVATE_DRAG_DROPPED); assert(!d->drag.active); @@ -188,8 +192,10 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) cookie = (XGenericEventCookie){.evtype = XI_Motion, .data = &mev}; mev.event = 42; xi_event(d, &cookie); + collect(&d->base, &s->base, &s2.base, &rec1, &rec2, &drec); assert(rec1.over_count == 2); drag_cancel(&d->base); + collect(&d->base, &s->base, &s2.base, &rec1, &rec2, &drec); assert(rec1.leave_count == 2); assert(end_reason == YASL_PRIVATE_DRAG_CANCELLED); assert(!d->drag.active); @@ -203,9 +209,13 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) assert(end_reason == YASL_PRIVATE_DRAG_CANCELLED); assert(!d->drag.active); struct yasl_private_event reset_ev; - assert(yasl_private_surface_poll_event(&s->base, &reset_ev)); + /* The display stream also carries the reset of the second surface. */ + do { assert(yasl_private_display_poll_event(&d->base, &reset_ev)); } + while (reset_ev.type != YASL_PRIVATE_EVENT_INPUT_RESET || reset_ev.surface != &s->base); assert(reset_ev.type == YASL_PRIVATE_EVENT_INPUT_RESET); + collect(&d->base, &s->base, &s2.base, &rec1, &rec2, &drec); + /* 4. Drag image parameter */ uint32_t pixels[16] = {0xFF00FF00}; struct yasl_private_drag_image img = {.pixels = pixels, .size = {4, 4}, .hotspot = {2, 2}}; @@ -214,6 +224,8 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) assert(drag_carry(&s->base, (struct yasl_private_vector2i){10, 10})); drag_cancel(&d->base); + collect(&d->base, &s->base, &s2.base, &rec1, &rec2, &drec); + /* 5. XDND drop-target events and URI parsing */ size_t out_len = 0; char *parsed = parse_file_uris("file:///tmp/doc.txt\r\n", 21, &out_len); @@ -236,9 +248,6 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) d->xdnd_drop = 13; d->uri_list = 20; - struct drop_test_recorder drec = {0}; - cbs1.drop = on_test_drop; - set_callbacks(&s->base, &cbs1, &drec); XClientMessageEvent enter_msg = { .type = ClientMessage, @@ -256,6 +265,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) .data.l = {99, 0, (100 << 16) | 200, 1000, 0}, }; handle_client_message(d, s, &pos_msg); + collect(&d->base, &s->base, &s2.base, &rec1, &rec2, &drec); assert(drec.over_count == 1); assert(drec.last_pos.x == 100 && drec.last_pos.y == 200); @@ -266,10 +276,12 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) .data.l = {99, 0, 0, 0, 0}, }; handle_client_message(d, s, &leave_msg); + collect(&d->base, &s->base, &s2.base, &rec1, &rec2, &drec); assert(drec.leave_count == 1); handle_client_message(d, s, &enter_msg); handle_client_message(d, s, &pos_msg); + collect(&d->base, &s->base, &s2.base, &rec1, &rec2, &drec); assert(drec.over_count == 2); XClientMessageEvent drop_msg = { @@ -279,17 +291,22 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s) .data.l = {99, 0, 1005, 0, 0}, }; handle_client_message(d, s, &drop_msg); + collect(&d->base, &s->base, &s2.base, &rec1, &rec2, &drec); assert(drec.leave_count == 2); assert(d->pending_drop.active); /* Push YASL_PRIVATE_EVENT_DROP */ push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_DROP}, "file:///sample.png\r\n", 20); struct yasl_private_event drop_ev; - assert(yasl_private_surface_poll_event(&s->base, &drop_ev)); + assert(yasl_private_display_poll_event(&d->base, &drop_ev)); assert(drop_ev.type == YASL_PRIVATE_EVENT_DROP); assert(drop_ev.text.len == 20); assert(!strcmp(drop_ev.text.data, "file:///sample.png\r\n")); + yasl_private_events_unregister(&s->base); + yasl_private_events_unregister(&s2.base); + yasl_private_events_close(&d->base); + /* Clean up s2 */ s->next = NULL; input_queue_clear(&s->input); diff --git a/tests/x11/test_tablet_native.c b/tests/x11/test_tablet_native.c index 828082c..f4f8014 100644 --- a/tests/x11/test_tablet_native.c +++ b/tests/x11/test_tablet_native.c @@ -2,7 +2,10 @@ #define main tablet_example_main #include "../../examples/tablet.c" #undef main -#include +#include +#include +#include +#include #include #include #include @@ -55,41 +58,53 @@ static int connect_tool(const char *path) return fd; } -static void native_draw(struct yasl_surface *surface, struct yasl_private_frame *frame, void *ud) -{ - (void)surface; - draw(ud, surface, frame); -} +struct canvas_state { + struct yasl_pen_payload pen; + uint32_t *pixels; + int width, height, painted_width, painted_height; + bool down; +}; -static void native_configure(struct yasl_surface *surface, struct yasl_private_geometry geometry, void *ud) +static void render_event(yasl_surface *surface, struct canvas_state *state, const yasl_event *event) { - input(ud, &(struct yasl_private_event){.surface = surface, .type = YASL_PRIVATE_EVENT_WINDOW_RESIZE, - .resize = {.geometry = geometry}}); + if (event->type == YASL_EVENT_PEN) { + state->pen = event->pen; + state->down = event->pen.in_contact; + } + state->pixels = canvas(surface, &state->width, &state->height, + &state->painted_width, &state->painted_height, false); + assert(state->pixels); + if (event->type == YASL_EVENT_PEN && state->down && + (event->pen.action == YASL_PEN_DOWN || event->pen.action == YASL_PEN_MOVE)) { + double scale = yasl_surface_get_scale(surface); + stamp(state->pixels, state->width, state->height, + 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)); } -static void native_input(struct app *app, const struct yasl_private_event *event) +static void drain(yasl_surface *surface, struct canvas_state *state) { - input(app, event); + yasl_event event; + int result; + while ((result = yasl_wait_event(&event, 0)) > 0) + render_event(surface, state, &event); + assert(result == 0); } -static void pump(int fd, struct yasl_private_display *display, struct yasl_surface *surface, struct app *app) +static void pump(int fd, yasl_surface *surface, struct canvas_state *state) { xf86ITEventWaitForSync sync = {{sizeof(sync), XF86IT_EVENT_WAIT_FOR_SYNC}}; send_all(fd, &sync, sizeof(sync)); xf86ITResponseHeader response; receive_all(fd, &response, sizeof(response)); assert(response.type == XF86IT_RESPONSE_SYNC_FINISHED && response.length == sizeof(response)); - /* The driver acknowledgment guarantees input processing has finished. - * Round-trip on YASL's X connection before draining its event queue. */ - XSync(yasl_private_display_get_native_handle(display), False); - assert(yasl_private_display_dispatch(display, 0) == 0); - struct yasl_private_event event; - while (yasl_private_surface_poll_event(surface, &event)) { - native_input(app, &event); - yasl_private_surface_schedule_redraw(surface); - } - yasl_private_display_flush(display); - assert(!app->failed); + /* The driver acknowledgment guarantees XI2 processing has finished. */ + XSync(yasl_x11_display(surface), False); + drain(surface, state); + /* Public polling submits the CPU frame before the native readback. */ + XSync(yasl_x11_display(surface), False); } static void set_eraser_property(Display *display) @@ -116,23 +131,16 @@ static void set_eraser_property(Display *display) int main(int argc, char **argv) { assert(argc == 3); /* pen socket, eraser socket */ - struct app app = {.tablet_supported = true, .backend = YASL_PRIVATE_BACKEND_X11}; - struct yasl_private_display_spec ds = {.backend = YASL_PRIVATE_BACKEND_X11}; - struct yasl_private_display *display = yasl_private_display_open(NULL, &ds); - assert(display && (yasl_private_display_caps(display) & YASL_PRIVATE_CAP_TABLET)); - struct yasl_private_window_spec ss = {.title = "YASL native pressure test", .size = {500, 400}}; - struct yasl_surface *surface = yasl_private_surface_create_toplevel(display, &ss); - assert(surface); - struct yasl_private_window_cbs cbs = {.draw = native_draw, .configure = native_configure}; - yasl_private_surface_set_callbacks(surface, &cbs, &app); - yasl_private_surface_schedule_redraw(surface); - Display *x = yasl_private_display_get_native_handle(display); - Window window = (Window)(uintptr_t)yasl_private_surface_get_native_handle(surface); + 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); XMoveWindow(x, window, 0, 0); XSetInputFocus(x, window, RevertToParent, CurrentTime); XSync(x, False); - assert(yasl_private_display_dispatch(display, 0) == 0); - yasl_private_display_flush(display); + drain(surface, &app); set_eraser_property(x); int pen_fd = connect_tool(argv[1]), eraser_fd = connect_tool(argv[2]); xf86ITEventMotion motion = {.header = {sizeof(motion), XF86IT_EVENT_MOTION}, .is_absolute = 1}; @@ -143,52 +151,50 @@ int main(int argc, char **argv) .is_prox_in = 1, .valuators = motion.valuators}; send_all(pen_fd, &proximity, sizeof(proximity)); send_all(pen_fd, &motion, sizeof(motion)); - pump(pen_fd, display, surface, &app); - assert(app.visible && !app.down && app.pointer.device == YASL_PRIVATE_PTR_DEV_PEN); - assert(app.pointer.pos.x == 150 && app.pointer.pos.y == 200); - assert(app.canvas[(size_t)200 * app.geometry.size.x + 150] == PAPER); + pump(pen_fd, surface, &app); + assert(!app.down && app.pen.tool == YASL_PEN_PEN); + assert(app.pen.x == 150 && app.pen.y == 200); + assert(app.pixels[(size_t)200 * app.width + 150] == PAPER); xf86ITEventButton button = {.header = {sizeof(button), XF86IT_EVENT_BUTTON}, .is_absolute = 1, .button = 1, .is_press = 1, .valuators = motion.valuators}; button.valuators.valuators[4] = 250; /* Driver pressure range is 0..1000. */ send_all(pen_fd, &button, sizeof(button)); - pump(pen_fd, display, surface, &app); - assert(app.down && app.pointer.pen.pressure == 16384); - assert(app.canvas[(size_t)200 * app.geometry.size.x + 150] == INK && - app.canvas[(size_t)200 * app.geometry.size.x + 170] == PAPER); + pump(pen_fd, surface, &app); + assert(app.down && fabs(app.pen.pressure - 0.25) < 0.001); + assert(app.pixels[(size_t)200 * app.width + 150] == INK && + app.pixels[(size_t)200 * app.width + 160] == PAPER); motion.valuators.mask[0] = 1 << 4; /* No position change. */ motion.valuators.valuators[4] = 1000; send_all(pen_fd, &motion, sizeof(motion)); - pump(pen_fd, display, surface, &app); - assert(app.down && app.pointer.pen.pressure == 65535); - assert(app.canvas[(size_t)200 * app.geometry.size.x + 170] == INK); + pump(pen_fd, surface, &app); + assert(app.down && app.pen.pressure == 1.0); + assert(app.pixels[(size_t)200 * app.width + 160] == INK); /* Check the actual window's pixels too, after the example presents. */ - XImage *image = XGetImage(x, window, 170, 200, 1, 1, AllPlanes, ZPixmap); + XImage *image = XGetImage(x, window, 160, 200, 1, 1, AllPlanes, ZPixmap); assert(image && (XGetPixel(image, 0, 0) & 0xffffff) == (INK & 0xffffff)); XDestroyImage(image); button.is_press = 0; button.valuators.mask[0] = 0; /* Release omits all valuators. */ send_all(pen_fd, &button, sizeof(button)); - pump(pen_fd, display, surface, &app); - assert(!app.down && !app.pointer.pen.pressure); + pump(pen_fd, surface, &app); + assert(!app.down && !app.pen.pressure); send_all(eraser_fd, &proximity, sizeof(proximity)); button.is_press = 1; button.valuators = proximity.valuators; button.valuators.valuators[4] = 1000; send_all(eraser_fd, &button, sizeof(button)); - pump(eraser_fd, display, surface, &app); - assert(app.down && app.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER && app.pointer.pen.pressure == 65535); - assert(app.canvas[(size_t)200 * app.geometry.size.x + 150] == PAPER && - app.canvas[(size_t)200 * app.geometry.size.x + 170] == PAPER); + pump(eraser_fd, surface, &app); + assert(app.down && app.pen.tool == YASL_PEN_ERASER && app.pen.pressure == 1.0); + assert(app.pixels[(size_t)200 * app.width + 150] == PAPER && + app.pixels[(size_t)200 * app.width + 160] == PAPER); button.is_press = 0; send_all(eraser_fd, &button, sizeof(button)); - pump(eraser_fd, display, surface, &app); - assert(!app.down && !app.pointer.pen.pressure); + pump(eraser_fd, surface, &app); + assert(!app.down && !app.pen.pressure); close(pen_fd); close(eraser_fd); - free(app.canvas); - yasl_private_surface_destroy(surface); - yasl_private_display_close(display); + yasl_shutdown(); puts("Native Xorg pen pressure, stationary pressure, release, and eraser drawing passed."); return 0; } -- cgit v1.2.3