From 6eac0a5f2c552728efca420b1129fae7d4fea773 Mon Sep 17 00:00:00 2001 From: Berke Güzel Date: Sun, 11 Oct 2026 23:40:57 +0300 Subject: feat: backend selection (Wayland, X11, Cocoa) --- README.md | 74 +++++ include/yasl/yasl.h | 12 + meson.build | 93 +++--- meson.options | 3 + src/cocoa/api.h | 47 +++ src/cocoa/meson.build | 1 + src/cocoa/window.m | 516 ++++++++++++++++++++++++------ src/common/backend.c | 321 +++++++++++++++++++ src/common/backend.h | 67 ++++ src/common/meson.build | 1 + src/meson.build | 10 + src/wayland/api.h | 47 +++ src/wayland/backend.c | 231 ++++++++++++++ src/wayland/buffer.c | 62 ++-- src/wayland/drag.c | 42 +-- src/wayland/ime.c | 26 +- src/wayland/internal.h | 114 +++---- src/wayland/keyboard.c | 32 +- src/wayland/meson.build | 13 + src/wayland/pointer.c | 98 +++--- src/wayland/popup.c | 23 +- src/wayland/shell.c | 46 +-- src/wayland/surface.c | 93 +++--- src/x11/meson.build | 1 + src/x11/window.c | 723 ++++++++++++++++++++++++++++++++++++++++++ tests/cocoa/test_native.c | 58 ++++ tests/common/test_backends.c | 215 +++++++++++++ tests/common/test_selection.c | 34 ++ tests/helpers/run_backends.py | 93 ++++++ tests/meson.build | 44 +++ 30 files changed, 2724 insertions(+), 416 deletions(-) create mode 100644 meson.options create mode 100644 src/cocoa/api.h create mode 100644 src/cocoa/meson.build create mode 100644 src/common/backend.c create mode 100644 src/common/backend.h create mode 100644 src/common/meson.build create mode 100644 src/meson.build create mode 100644 src/wayland/api.h create mode 100644 src/wayland/backend.c create mode 100644 src/wayland/meson.build create mode 100644 src/x11/meson.build create mode 100644 src/x11/window.c create mode 100644 tests/cocoa/test_native.c create mode 100644 tests/common/test_backends.c create mode 100644 tests/common/test_selection.c create mode 100644 tests/helpers/run_backends.py create mode 100644 tests/meson.build diff --git a/README.md b/README.md index 23f4811..52dea16 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,61 @@ It provides window creation, frame pacing, high-DPI scaling, and input routing w - Self-contained event queue. - No terminal-specific telemetry or hardcoded application names. +## Backend selection + +YASL selects the native window backend at runtime. +Leave `yasl_display_spec.backend` at zero to use this mode. +`YASL_BACKEND_AUTO` has the same effect. +You can also pass NULL as the display spec. + +On Linux, YASL tries Wayland first in a Wayland session. +It uses `WAYLAND_DISPLAY`, `WAYLAND_SOCKET`, and `XDG_SESSION_TYPE` to detect that session. +In other sessions, it tries X11 first. +If the first backend cannot open, it tries the other built backend. +On macOS, it uses Cocoa. + +Set `backend` to request one backend: + +```c +struct yasl_display_spec spec = { + .app_id = "org.example.app", + .backend = YASL_BACKEND_X11, +}; +struct yasl_display *display = yasl_display_open(loop, &spec); +``` + +Use `YASL_BACKEND_WAYLAND` to request Wayland. +Use `YASL_BACKEND_COCOA` to request Cocoa. +An explicit request never falls back to another backend. +Open returns NULL if the requested backend cannot open. +It sets `errno` to `ENOSYS` for a backend that was not built. +It sets `errno` to `EINVAL` for an invalid backend value. + +Call `yasl_display_backend(display)` to read the selected backend. +All surfaces on a display use that backend. +Open separate displays to use Wayland and X11 in one process. +The public API needs no window system headers. +Use `yasl_display_caps()` to check optional features. + +X11 supports CPU pixel buffers, keyboard and pointer input, and clipboard transfer. +It supports the primary selection and window placement. +It does not yet support YASL popups, drag operations, or IME preedit text. +Those features return their normal unsupported result. + +The surface spec needs no native graphics device for CPU drawing. +On Cocoa, YASL creates its own Metal device for this mode. +An app that renders with Metal can supply its device through `graphics_device`. +That app submits and presents the native drawable. + +The native display handle is a `wl_display *` on Wayland. +It is a `Display *` on X11. +The native surface handle is a `wl_surface *` on Wayland. +On X11, convert it to `Window` through `uintptr_t`. +On Cocoa, the native surface handle is the content `NSView *`. + ## Building +Use a C23 compiler and Meson 1.6 or later. Build with Meson and Ninja: ```sh @@ -34,6 +87,27 @@ ninja -C build ninja -C build test ``` +Linux builds include each backend whose dependencies are installed. +Wayland needs `wayland-client`, `wayland-cursor`, and `xkbcommon`. +X11 needs `x11` and `xkbcommon`. +Use `-Dwayland=enabled` or `-Dx11=enabled` to require a backend. +Use `-Dwayland=disabled` or `-Dx11=disabled` to omit a backend. +At least one backend must be built. + +To run the Linux window tests, install Weston and Xwayland. +Then run: + +```sh +meson setup build-test -Dintegration_tests=true +meson test -C build-test --print-errorlogs +``` + +The tests start isolated display servers. +They test explicit requests, automatic selection, and fallback. +They also test both backends in one process. +On macOS, the same option runs native Cocoa window tests. +These tests need a desktop session. + ## Quick Example ```c diff --git a/include/yasl/yasl.h b/include/yasl/yasl.h index 0c2235a..2574457 100644 --- a/include/yasl/yasl.h +++ b/include/yasl/yasl.h @@ -258,14 +258,25 @@ struct yasl_surface_cbs { }; /* Display Lifecycle */ +enum yasl_backend { + YASL_BACKEND_AUTO = 0, + YASL_BACKEND_WAYLAND, + YASL_BACKEND_X11, + YASL_BACKEND_COCOA, +}; + struct yasl_display_spec { const char *app_id; const struct yasl_display_cbs *cbs; void *ud; + /* Zero selects the native backend. An explicit choice never falls back. */ + enum yasl_backend backend; }; +/* Return NULL and set errno if the requested backend cannot open. */ YASL_API struct yasl_display *yasl_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec); +YASL_API enum yasl_backend yasl_display_backend(const struct yasl_display *d); YASL_API void yasl_display_close(struct yasl_display *d); YASL_API uint32_t yasl_display_caps(const struct yasl_display *d); YASL_API void yasl_display_dispatch(struct yasl_display *d); @@ -285,6 +296,7 @@ struct yasl_surface_spec { struct yasl_vector2i size; bool fullscreen; bool tearing; + /* Optional native graphics device. NULL selects CPU pixel presentation. */ void *graphics_device; }; diff --git a/meson.build b/meson.build index e4396b2..e599acf 100644 --- a/meson.build +++ b/meson.build @@ -1,9 +1,10 @@ project( 'yasl', 'c', + meson_version: '>=1.6.0', version: '0.1.0', default_options: [ - 'c_std=c11', + 'c_std=c23', 'warning_level=2', 'b_lto=false', ], @@ -15,60 +16,59 @@ system = host_machine.system() inc = include_directories('include', 'src') deps = [] -sources = files( - 'src/common/timing.c', - 'src/common/utf8.c', - 'src/common/loop.c', -) +sources = [] + +has_wayland = false +has_x11 = false +has_cocoa = system == 'darwin' if system == 'linux' - wl_client = dependency('wayland-client') - wl_cursor = dependency('wayland-cursor') - xkbcommon = dependency('xkbcommon') + wl_client = dependency('wayland-client', required: get_option('wayland')) + wl_cursor = dependency('wayland-cursor', required: get_option('wayland')) + xkbcommon = dependency('xkbcommon', required: get_option('wayland')) + has_wayland = wl_client.found() and wl_cursor.found() and xkbcommon.found() + x11 = dependency('x11', required: get_option('x11')) + x11_xkb = dependency('xkbcommon', required: get_option('x11')) + has_x11 = x11.found() and x11_xkb.found() + if has_x11 + deps += [x11, x11_xkb] + endif threads = dependency('threads') m_dep = cc.find_library('m', required: false) rt_dep = cc.find_library('rt', required: false) - deps += [wl_client, wl_cursor, xkbcommon, threads, m_dep, rt_dep] - - proto_src = files( - 'src/wayland/protocols/commit-timing-v1-protocol.c', - 'src/wayland/protocols/cursor-shape-v1-protocol.c', - 'src/wayland/protocols/fifo-v1-protocol.c', - 'src/wayland/protocols/fractional-scale-v1-protocol.c', - 'src/wayland/protocols/presentation-time-protocol.c', - 'src/wayland/protocols/primary-selection-unstable-v1-protocol.c', - 'src/wayland/protocols/tablet-v2-protocol.c', - 'src/wayland/protocols/tearing-control-v1-protocol.c', - 'src/wayland/protocols/text-input-unstable-v3-protocol.c', - 'src/wayland/protocols/viewporter-protocol.c', - 'src/wayland/protocols/xdg-decoration-protocol.c', - 'src/wayland/protocols/xdg-shell-protocol.c', - 'src/wayland/protocols/xdg-toplevel-drag-v1-protocol.c', - ) - - sources += files( - 'src/wayland/buffer.c', - 'src/wayland/clipboard.c', - 'src/wayland/drag.c', - 'src/wayland/ime.c', - 'src/wayland/keyboard.c', - 'src/wayland/pointer.c', - 'src/wayland/popup.c', - 'src/wayland/shell.c', - 'src/wayland/surface.c', - ) + proto_src + deps += [threads, m_dep, rt_dep] + if has_wayland + deps += [wl_client, wl_cursor, xkbcommon] + endif elif system == 'darwin' add_languages('objc') - sources += files('src/cocoa/window.m') deps += [ dependency('appleframeworks', modules: ['AppKit', 'Metal', 'QuartzCore']), ] +else + error('YASL supports Linux and macOS.') +endif + +if not has_wayland and not has_x11 and not has_cocoa + error('Enable at least one window backend.') +endif + +add_project_arguments( + '-DYASL_HAS_WAYLAND=' + (has_wayland ? '1' : '0'), + '-DYASL_HAS_X11=' + (has_x11 ? '1' : '0'), + '-DYASL_HAS_COCOA=' + (has_cocoa ? '1' : '0'), + language: has_cocoa ? ['c', 'objc'] : ['c'], +) +if has_cocoa + add_project_arguments('-fblocks', language: 'objc') endif +subdir('src') + yasl_lib = both_libraries( 'yasl', sources, - include_directories: [inc, include_directories('src/wayland')], + include_directories: inc, dependencies: deps, install: true, ) @@ -103,15 +103,4 @@ executable( dependencies: deps, ) -test_exe = executable( - 'yasl-test', - 'tests/test_lifecycle.c', - include_directories: inc, - link_with: yasl_lib.get_static_lib(), -) - -add_test_setup('asan') -add_test_setup('tsan') -add_test_setup('msan') - -test('basic', test_exe) +subdir('tests') diff --git a/meson.options b/meson.options new file mode 100644 index 0000000..1498b4e --- /dev/null +++ b/meson.options @@ -0,0 +1,3 @@ +option('wayland', type: 'feature', value: 'auto', description: 'Build the Wayland backend.') +option('x11', type: 'feature', value: 'auto', description: 'Build the X11 backend.') +option('integration_tests', type: 'boolean', value: false, description: 'Test windows with Linux display servers or the native macOS display.') diff --git a/src/cocoa/api.h b/src/cocoa/api.h new file mode 100644 index 0000000..ad259df --- /dev/null +++ b/src/cocoa/api.h @@ -0,0 +1,47 @@ +#ifndef YASL_COCOA_API_H +#define YASL_COCOA_API_H + +#include "common/backend.h" + +struct cocoa_display; +struct cocoa_surface; + +struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec); +void cocoa_display_close(struct cocoa_display *d); +uint32_t cocoa_display_caps(const struct cocoa_display *d); +void cocoa_display_dispatch(struct cocoa_display *d); +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_selection sel, const char *text); +void cocoa_display_paste(struct cocoa_display *d, enum yasl_selection sel, struct cocoa_surface *target); +bool cocoa_display_drag_start(struct cocoa_display *d, struct cocoa_surface *origin, const struct yasl_drag_image *image, const struct yasl_drag_listener *l, void *ud); +void cocoa_display_drag_cancel(struct cocoa_display *d); +struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d, const struct yasl_surface_spec *spec); +struct cocoa_surface *cocoa_surface_create_popup(struct cocoa_surface *parent, const struct yasl_popup_spec *spec); +bool cocoa_surface_popup_reposition(struct cocoa_surface *popup, const struct yasl_popup_spec *spec); +void cocoa_surface_destroy(struct cocoa_surface *s); +struct cocoa_display *cocoa_surface_display(const struct cocoa_surface *s); +void cocoa_surface_set_callbacks(struct cocoa_surface *s, const struct yasl_surface_cbs *cbs, void *ud); +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_framebuffer *fb, const struct yasl_damage_list *damage); +bool cocoa_surface_input_poll(struct cocoa_surface *s, struct yasl_input_event *ev); +struct yasl_vector2i cocoa_surface_size(const struct cocoa_surface *s); +uint32_t cocoa_surface_scale_120(const struct cocoa_surface *s); +uint32_t cocoa_surface_state(const struct cocoa_surface *s); +struct yasl_timing cocoa_surface_timing(const struct cocoa_surface *s); +uint64_t cocoa_surface_frame_time_ns(struct cocoa_surface *s, uint64_t now); +void cocoa_surface_set_cursor(struct cocoa_surface *s, enum yasl_cursor cursor); +void cocoa_surface_set_background_color(struct cocoa_surface *s, uint32_t bg); +const char *cocoa_surface_ime_preedit(const struct cocoa_surface *s); +bool cocoa_surface_ime_preedit_cursor(const struct cocoa_surface *s, int *begin, int *end); +void cocoa_surface_set_ime_caret(struct cocoa_surface *s, struct yasl_rect2i caret); +bool cocoa_surface_start_interactive_move(struct cocoa_surface *s); +bool cocoa_surface_start_interactive_resize(struct cocoa_surface *s, enum yasl_resize_edge edge); +bool cocoa_surface_show_window_menu(struct cocoa_surface *s, struct yasl_vector2i pos); +bool cocoa_surface_drag_carry(struct cocoa_surface *s, struct yasl_vector2i offset); +bool cocoa_surface_place_under_pointer(struct cocoa_surface *s, struct yasl_vector2i at); +void *cocoa_display_get_native_handle(struct cocoa_display *d); +void *cocoa_surface_get_native_handle(struct cocoa_surface *s); + +#endif diff --git a/src/cocoa/meson.build b/src/cocoa/meson.build new file mode 100644 index 0000000..0bd40a3 --- /dev/null +++ b/src/cocoa/meson.build @@ -0,0 +1 @@ +sources += files('window.m') diff --git a/src/cocoa/window.m b/src/cocoa/window.m index 0edf694..bc6060f 100644 --- a/src/cocoa/window.m +++ b/src/cocoa/window.m @@ -2,7 +2,7 @@ #import #import -#include "yasl/yasl.h" +#include "cocoa/api.h" #include "yasl/timing.h" #include "cocoa/keycodes.h" @@ -27,18 +27,18 @@ enum { }; @interface YaslView : NSView -@property (nonatomic, assign) struct yasl_surface *surface; +@property (nonatomic, assign) struct cocoa_surface *surface; @end @interface YaslAppDelegate : NSObject @end @interface YaslWindow : NSWindow -@property (nonatomic, assign) struct yasl_surface *surface; +@property (nonatomic, assign) struct cocoa_surface *surface; @end @interface YaslWindowController : NSWindowController -@property (nonatomic, assign) struct yasl_surface *surface; +@property (nonatomic, assign) struct cocoa_surface *surface; @property (nonatomic, assign) BOOL fullscreenTransition; @end @@ -72,11 +72,13 @@ static inline void input_queue_clear(struct input_queue *q) q->head = q->tail = q->count = 0; } -struct yasl_surface { +struct cocoa_surface { + struct yasl_surface base; YaslWindow *window; YaslWindowController *controller; YaslView *view; CAMetalLayer *layer; + id software_queue; CADisplayLink *link; const struct yasl_surface_cbs *cbs; @@ -106,7 +108,7 @@ struct yasl_surface { bool rendering; bool closed; bool destroyed; - struct yasl_surface *dead_next; + struct cocoa_surface *dead_next; struct input_queue input; struct yasl_vector2i pointer; @@ -119,8 +121,9 @@ struct yasl_surface { struct yasl_frame_clock clock; }; -struct yasl_display { - struct yasl_surface **windows; +struct cocoa_display { + struct yasl_display base; + struct cocoa_surface **windows; int count; int capacity; const struct yasl_display_cbs *cbs; @@ -130,17 +133,17 @@ struct yasl_display { id key_up_monitor; YaslAppDelegate *delegate; NSPoint cascade; - struct yasl_surface *dead; + struct cocoa_surface *dead; int depth; bool open; bool launched; bool pumping; }; -static struct yasl_display darwin_ctx; +static struct cocoa_display darwin_ctx; -static void render_and_commit(struct yasl_surface *w); -static void reap_dead(struct yasl_display *d); +static void render_and_commit(struct cocoa_surface *w); +static void reap_dead(struct cocoa_display *d); static uint64_t media_to_loop_ns(CFTimeInterval t) { @@ -151,7 +154,7 @@ static uint64_t media_to_loop_ns(CFTimeInterval t) return (uint64_t)((double)now + delta); } -static void set_state(struct yasl_surface *w, uint32_t bit, bool on) +static void set_state(struct cocoa_surface *w, uint32_t bit, bool on) { if (!w || w->closed) return; @@ -161,27 +164,27 @@ static void set_state(struct yasl_surface *w, uint32_t bit, bool on) w->state = next; darwin_ctx.depth++; if (w->cbs->state) - w->cbs->state(w, w->state, w->ud); + w->cbs->state(&w->base, w->state, w->ud); darwin_ctx.depth--; } -static void push_focus(struct yasl_surface *w, bool focused) +static void push_focus(struct cocoa_surface *w, bool focused) { if (!w || w->closed) return; struct yasl_input_event ev = { .kind = YASL_INPUT_FOCUS, - .surface = w, + .surface = &w->base, .focus = { .focused = focused }, }; input_queue_push(&w->input, &ev); darwin_ctx.depth++; set_state(w, YASL_STATE_ACTIVATED, focused); - yasl_surface_schedule_redraw(w); + cocoa_surface_schedule_redraw(w); darwin_ctx.depth--; } -static void sync_size(struct yasl_surface *w, bool force) +static void sync_size(struct cocoa_surface *w, bool force) { if (!w || !w->layer || w->closed) return; @@ -205,10 +208,10 @@ static void sync_size(struct yasl_surface *w, bool force) w->layer.contentsScale = scale; w->layer.drawableSize = CGSizeMake(size.x, size.y); if (w->cbs->configure) - w->cbs->configure(w, (struct yasl_geometry){ + w->cbs->configure(&w->base, (struct yasl_geometry){ .size = size, .scale_120 = scale_120, }, w->ud); - yasl_surface_schedule_redraw(w); + cocoa_surface_schedule_redraw(w); if (w->view.inLiveResize) render_and_commit(w); yasl_clock_note_resize(&w->clock, yasl_loop_now_ns() - start); @@ -246,24 +249,24 @@ static struct yasl_key_event cocoa_key_event(NSEvent *ev, enum yasl_key_action a return k; } -static void on_key_event(struct yasl_surface *w, NSEvent *ev, enum yasl_key_action action) +static void on_key_event(struct cocoa_surface *w, NSEvent *ev, enum yasl_key_action action) { w->mods = cocoa_mods(ev.modifierFlags); struct yasl_key_event k = cocoa_key_event(ev, action); struct yasl_input_event input_ev = { .kind = YASL_INPUT_KEY, - .surface = w, + .surface = &w->base, .key = k, }; input_queue_push(&w->input, &input_ev); } -static void on_text_input(struct yasl_surface *w, const char *text, size_t len) +static void on_text_input(struct cocoa_surface *w, const char *text, size_t len) { if (len) { struct yasl_input_event ev = { .kind = YASL_INPUT_TEXT, - .surface = w, + .surface = &w->base, .text = { .text = text, .len = len }, }; input_queue_push(&w->input, &ev); @@ -297,7 +300,7 @@ static int cocoa_button(NSInteger number) } } -static struct yasl_vector2i event_pos(struct yasl_surface *w, NSEvent *ev) +static struct yasl_vector2i event_pos(struct cocoa_surface *w, NSEvent *ev) { NSPoint p = [w->view convertPoint:ev.locationInWindow fromView:nil]; CGFloat scale = w->window.backingScaleFactor; @@ -309,7 +312,7 @@ static struct yasl_vector2i event_pos(struct yasl_surface *w, NSEvent *ev) }; } -static void push_pointer(struct yasl_surface *w, NSEvent *ev, enum yasl_pointer_kind kind, +static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_pointer_kind kind, int button) { if (!w || w->closed || button < 0) @@ -319,7 +322,7 @@ static void push_pointer(struct yasl_surface *w, NSEvent *ev, enum yasl_pointer_ w->pointer = event_pos(w, ev); struct yasl_input_event input_ev = { .kind = YASL_INPUT_MOUSE, - .surface = w, + .surface = &w->base, .mouse = { .pos = w->pointer, .button = button, @@ -392,7 +395,7 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) - (BOOL)performDragOperation:(id)sender { - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w) return NO; NSPasteboard *pb = [sender draggingPasteboard]; NSArray *urls = [pb readObjectsForClasses:@[[NSURL class]] @@ -409,11 +412,11 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) if (utf8 && strlen(utf8)) { struct yasl_input_event ev = { .kind = YASL_INPUT_DROP_FILES, - .surface = w, + .surface = &w->base, .drop_files = { .files = utf8, .len = strlen(utf8) }, }; input_queue_push(&w->input, &ev); - yasl_surface_schedule_redraw(w); + cocoa_surface_schedule_redraw(w); return YES; } return NO; @@ -454,7 +457,7 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) - (void)viewWillStartLiveResize { [super viewWillStartLiveResize]; - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (w) w->layer.presentsWithTransaction = YES; } @@ -462,16 +465,16 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) - (void)viewDidEndLiveResize { [super viewDidEndLiveResize]; - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w) return; w->layer.presentsWithTransaction = NO; - yasl_surface_schedule_redraw(w); + cocoa_surface_schedule_redraw(w); } - (void)displayLinkFired:(CADisplayLink *)link { - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w || w->closed) return; CFTimeInterval period = link.targetTimestamp - link.timestamp; @@ -508,13 +511,13 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) - (void)cursorUpdate:(NSEvent *)event { (void)event; - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; [cocoa_cursor(w ? w->cursor : YASL_CURSOR_ARROW) set]; } - (void)keyDown:(NSEvent *)event { - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w) return; on_key_event(w, event, event.isARepeat ? YASL_KEY_REPEAT : YASL_KEY_PRESS); NSString *chars = event.characters; @@ -524,14 +527,14 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) - (void)keyUp:(NSEvent *)event { - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (w) on_key_event(w, event, YASL_KEY_RELEASE); } - (void)flagsChanged:(NSEvent *)event { - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w) return; w->mods = cocoa_mods(event.modifierFlags); on_key_event(w, event, modifier_down(event) ? YASL_KEY_PRESS : YASL_KEY_RELEASE); @@ -577,7 +580,7 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) - (void)scrollWheel:(NSEvent *)event { - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w) return; double dx = event.scrollingDeltaX, dy = event.scrollingDeltaY; if (event.hasPreciseScrollingDeltas) { @@ -589,7 +592,7 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor) if (dy != 0.0) { struct yasl_input_event input_ev = { .kind = YASL_INPUT_MOUSE, - .surface = w, + .surface = &w->base, .mouse = { .pos = w->pointer, .button = dy > 0.0 ? 4 : 5, @@ -612,14 +615,14 @@ static NSString *plain_string(id string) - (void)insertText:(id)string replacementRange:(NSRange)replacementRange { (void)replacementRange; - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w) return; const char *utf8 = plain_string(string).UTF8String; size_t len = utf8 ? strlen(utf8) : 0; free(w->preedit); w->preedit = NULL; on_text_input(w, utf8, len); - yasl_surface_schedule_redraw(w); + cocoa_surface_schedule_redraw(w); } - (void)doCommandBySelector:(SEL)selector { (void)selector; } @@ -629,28 +632,28 @@ static NSString *plain_string(id string) { (void)selectedRange; (void)replacementRange; - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w) return; NSString *str = plain_string(string); const char *utf8 = str.length ? str.UTF8String : NULL; free(w->preedit); w->preedit = utf8 ? strdup(utf8) : NULL; - yasl_surface_schedule_redraw(w); + cocoa_surface_schedule_redraw(w); } - (void)unmarkText { - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w || !w->preedit) return; free(w->preedit); w->preedit = NULL; - yasl_surface_schedule_redraw(w); + cocoa_surface_schedule_redraw(w); } - (NSRange)selectedRange { return NSMakeRange(NSNotFound, 0); } - (NSRange)markedRange { - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w || !w->preedit) return NSMakeRange(NSNotFound, 0); return NSMakeRange(0, [NSString stringWithUTF8String:w->preedit].length); @@ -666,7 +669,7 @@ static NSString *plain_string(id string) - (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange { (void)range; (void)actualRange; - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w || !w->window) return NSZeroRect; CGFloat scale = w->window.backingScaleFactor; if (scale < 1.0) scale = 1.0; @@ -710,13 +713,13 @@ static NSString *plain_string(id string) - (void)windowDidChangeOcclusionState:(NSNotification *)notification { (void)notification; - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w) return; bool visible = (self.window.occlusionState & NSWindowOcclusionStateVisible) != 0; darwin_ctx.depth++; set_state(w, YASL_STATE_SUSPENDED, !visible); if (visible) - yasl_surface_schedule_redraw(w); + cocoa_surface_schedule_redraw(w); else w->link.paused = YES; darwin_ctx.depth--; @@ -737,7 +740,7 @@ static NSString *plain_string(id string) - (void)syncWindowState { self.fullscreenTransition = NO; - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w || w->closed) return; darwin_ctx.depth++; @@ -769,31 +772,56 @@ static NSString *plain_string(id string) - (void)windowWillClose:(NSNotification *)notification { (void)notification; - struct yasl_surface *w = self.surface; + struct cocoa_surface *w = self.surface; if (!w || w->closed) return; darwin_ctx.depth++; w->closed = true; [w->link invalidate]; if (w->cbs->close) - w->cbs->close(w, w->ud); + w->cbs->close(&w->base, w->ud); darwin_ctx.depth--; } @end -bool yasl_surface_present(struct yasl_surface *w, const struct yasl_framebuffer *fb, +bool cocoa_surface_present(struct cocoa_surface *w, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage) { (void)damage; if (!w || w->closed || !w->rendering || w->did_present || !fb || !fb->native_drawable || fb->native_drawable != w->frame.target.native_drawable) return false; + if (w->software_queue) { + if (fb->pixels != w->pixels || fb->size.x != w->size.x || fb->size.y != w->size.y) + return false; + NSUInteger row_bytes = ((NSUInteger)w->size.x * 4 + 255) & ~(NSUInteger)255; + NSUInteger length = row_bytes * (NSUInteger)w->size.y; + id buffer = [w->layer.device newBufferWithLength:length + options:MTLResourceStorageModeShared]; + if (!buffer) return false; + for (int y = 0; y < w->size.y; y++) + memcpy((char *)buffer.contents + (NSUInteger)y * row_bytes, + w->pixels + (size_t)y * w->size.x, (size_t)w->size.x * 4); + id command = [w->software_queue commandBuffer]; + id blit = [command blitCommandEncoder]; + if (!blit) { [buffer release]; return false; } + id drawable = (id)fb->native_drawable; + [blit copyFromBuffer:buffer sourceOffset:0 sourceBytesPerRow:row_bytes + sourceBytesPerImage:length sourceSize:MTLSizeMake(w->size.x, w->size.y, 1) + toTexture:drawable.texture destinationSlice:0 destinationLevel:0 + destinationOrigin:MTLOriginMake(0, 0, 0)]; + [blit endEncoding]; + [command presentDrawable:drawable]; + [command commit]; + [buffer release]; + } + yasl_clock_presented(&w->clock, yasl_loop_now_ns(), false); w->did_present = true; return true; } -static void render_and_commit(struct yasl_surface *w) +static void render_and_commit(struct cocoa_surface *w) { if (w->closed || !w->cbs->draw || w->rendering || w->size.x <= 0 || w->size.y <= 0 || (w->state & YASL_STATE_SUSPENDED)) @@ -814,7 +842,7 @@ static void render_and_commit(struct yasl_surface *w) id drawable = [w->layer nextDrawable]; w->damage = (struct yasl_damage_list){0}; w->frame = (struct yasl_frame){ - .surface = w, + .surface = &w->base, .target = { .pixels = w->pixels, .size = w->size, @@ -824,7 +852,7 @@ static void render_and_commit(struct yasl_surface *w) }, }; if (drawable) - w->cbs->draw(w, &w->frame, w->ud); + w->cbs->draw(&w->base, &w->frame, w->ud); if (!w->closed && !w->did_present) { w->want_draw = true; w->frames_skipped++; @@ -834,7 +862,7 @@ static void render_and_commit(struct yasl_surface *w) darwin_ctx.depth--; } -static void pump_window(struct yasl_surface *w) +static void pump_window(struct cocoa_surface *w) { if (!w || w->closed || !w->want_draw || !w->cbs->draw) return; @@ -843,7 +871,7 @@ static void pump_window(struct yasl_surface *w) @autoreleasepool { render_and_commit(w); } } -void yasl_display_dispatch(struct yasl_display *d) +void cocoa_display_dispatch(struct cocoa_display *d) { assert([NSThread isMainThread]); if (!d || d->pumping) @@ -868,7 +896,7 @@ void yasl_display_dispatch(struct yasl_display *d) } } -void yasl_display_flush(struct yasl_display *d) +void cocoa_display_flush(struct cocoa_display *d) { assert([NSThread isMainThread]); if (!d) return; @@ -884,7 +912,7 @@ void yasl_display_flush(struct yasl_display *d) reap_dead(d); } -void yasl_surface_schedule_redraw(struct yasl_surface *w) +void cocoa_surface_schedule_redraw(struct cocoa_surface *w) { if (!w || w->closed) return; w->want_draw = true; @@ -946,12 +974,13 @@ static void place_window(NSWindow *window) darwin_ctx.cascade = [window cascadeTopLeftFromPoint:darwin_ctx.cascade]; } -struct yasl_display *yasl_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec) +struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec) { assert([NSThread isMainThread]); if (darwin_ctx.open) return NULL; const char *name = spec && spec->app_id ? spec->app_id : "YASL"; @autoreleasepool { app_init(name); } + darwin_ctx.base.ops = &yasl_cocoa_backend; darwin_ctx.open = true; darwin_ctx.loop = loop; if (spec) { @@ -962,7 +991,7 @@ struct yasl_display *yasl_display_open(struct yasl_loop *loop, const struct yasl return &darwin_ctx; } -void yasl_display_close(struct yasl_display *d) +void cocoa_display_close(struct cocoa_display *d) { if (!d) return; assert(!d->count && "destroy every surface before its display"); @@ -976,32 +1005,33 @@ void yasl_display_close(struct yasl_display *d) d->delegate = nil; free(d->app_id); free(d->windows); - *d = (struct yasl_display){0}; + *d = (struct cocoa_display){0}; } -uint32_t yasl_display_caps(const struct yasl_display *d) +uint32_t cocoa_display_caps(const struct cocoa_display *d) { return d ? (YASL_CAP_PLACE_WINDOW | YASL_CAP_CSD) : 0; } -struct yasl_surface *yasl_surface_create_toplevel(struct yasl_display *d, +struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d, const struct yasl_surface_spec *spec) { assert([NSThread isMainThread]); - if (!d || !d->open || !spec || !spec->graphics_device) + if (!d || !d->open || !spec) return NULL; @autoreleasepool { if (darwin_ctx.count >= darwin_ctx.capacity) { int cap = darwin_ctx.capacity ? darwin_ctx.capacity * 2 : 16; - struct yasl_surface **wins = realloc(darwin_ctx.windows, (size_t)cap * sizeof(*wins)); + struct cocoa_surface **wins = realloc(darwin_ctx.windows, (size_t)cap * sizeof(*wins)); if (!wins) return NULL; darwin_ctx.windows = wins; darwin_ctx.capacity = cap; } - struct yasl_surface *w = calloc(1, sizeof(*w)); + struct cocoa_surface *w = calloc(1, sizeof(*w)); if (!w) return NULL; - yasl_surface_set_callbacks(w, NULL, NULL); + w->base.display = &d->base; + cocoa_surface_set_callbacks(w, NULL, NULL); w->clock.refresh_ns = YASL_REFRESH_DEFAULT_NS; const char *t = spec->title ? spec->title : "YASL"; strncpy(w->title, t, sizeof(w->title) - 1); @@ -1027,7 +1057,25 @@ struct yasl_surface *yasl_surface_create_toplevel(struct yasl_display *d, w->view = [[YaslView alloc] initWithFrame:w->window.contentView.bounds]; w->window.contentView = w->view; w->layer = (CAMetalLayer *)w->view.layer; - w->layer.device = (id)spec->graphics_device; + if (spec->graphics_device) { + w->layer.device = (id)spec->graphics_device; + } else { + id device = MTLCreateSystemDefaultDevice(); + w->layer.device = device; + w->software_queue = [device newCommandQueue]; + [device release]; + if (!w->software_queue) { + w->window.delegate = nil; + w->controller.surface = NULL; + [w->window close]; + [w->view release]; + [w->controller release]; + [w->window release]; + free(w); + return NULL; + } + w->layer.framebufferOnly = NO; + } w->link = [[w->view displayLinkWithTarget:w->view selector:@selector(displayLinkFired:)] retain]; @@ -1053,9 +1101,10 @@ struct yasl_surface *yasl_surface_create_toplevel(struct yasl_display *d, } } -static void surface_free(struct yasl_surface *w) +static void surface_free(struct cocoa_surface *w) { [w->link release]; + [w->software_queue release]; [w->view release]; [w->controller release]; [w->window release]; @@ -1065,17 +1114,17 @@ static void surface_free(struct yasl_surface *w) free(w); } -static void reap_dead(struct yasl_display *d) +static void reap_dead(struct cocoa_display *d) { assert(d->depth == 0); while (d->dead) { - struct yasl_surface *w = d->dead; + struct cocoa_surface *w = d->dead; d->dead = w->dead_next; surface_free(w); } } -void yasl_surface_destroy(struct yasl_surface *w) +void cocoa_surface_destroy(struct cocoa_surface *w) { assert([NSThread isMainThread]); if (!w || w->destroyed) return; @@ -1114,19 +1163,19 @@ void yasl_surface_destroy(struct yasl_surface *w) yasl_loop_kick(darwin_ctx.loop); } -struct yasl_display *yasl_surface_display(const struct yasl_surface *w) +struct cocoa_display *cocoa_surface_display(const struct cocoa_surface *w) { return w ? &darwin_ctx : NULL; } -void yasl_surface_set_title(struct yasl_surface *w, const char *title) +void cocoa_surface_set_title(struct cocoa_surface *w, const char *title) { if (!w || w->closed || !title) return; strncpy(w->title, title, sizeof(w->title) - 1); w->window.title = [NSString stringWithUTF8String:w->title] ?: @"YASL"; } -void yasl_surface_set_background_color(struct yasl_surface *w, uint32_t bg) +void cocoa_surface_set_background_color(struct cocoa_surface *w, uint32_t bg) { if (!w || w->closed) return; w->bg_color = bg; @@ -1139,7 +1188,7 @@ void yasl_surface_set_background_color(struct yasl_surface *w, uint32_t bg) dark ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua]; } -void yasl_surface_set_cursor(struct yasl_surface *w, enum yasl_cursor cursor) +void cocoa_surface_set_cursor(struct cocoa_surface *w, enum yasl_cursor cursor) { if (!w || w->closed || cursor == w->cursor) return; w->cursor = cursor; @@ -1152,7 +1201,7 @@ void yasl_surface_set_cursor(struct yasl_surface *w, enum yasl_cursor cursor) [cocoa_cursor(cursor) set]; } -bool yasl_surface_place_under_pointer(struct yasl_surface *w, struct yasl_vector2i at) +bool cocoa_surface_place_under_pointer(struct cocoa_surface *w, struct yasl_vector2i at) { if (!w || w->closed || !w->window) return false; CGFloat scale = w->window.backingScaleFactor; @@ -1166,23 +1215,23 @@ bool yasl_surface_place_under_pointer(struct yasl_surface *w, struct yasl_vector return true; } -void yasl_surface_set_ime_caret(struct yasl_surface *w, struct yasl_rect2i caret) +void cocoa_surface_set_ime_caret(struct cocoa_surface *w, struct yasl_rect2i caret) { if (w) w->ime_caret = caret; } -const char *yasl_surface_ime_preedit(const struct yasl_surface *w) +const char *cocoa_surface_ime_preedit(const struct cocoa_surface *w) { return w ? w->preedit : NULL; } -bool yasl_surface_ime_preedit_cursor(const struct yasl_surface *w, int *begin, int *end) +bool cocoa_surface_ime_preedit_cursor(const struct cocoa_surface *w, int *begin, int *end) { (void)w; (void)begin; (void)end; return false; } -void yasl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text) +void cocoa_display_copy(struct cocoa_display *d, enum yasl_selection sel, const char *text) { (void)d; (void)sel; if (!text) return; @@ -1193,7 +1242,7 @@ void yasl_display_copy(struct yasl_display *d, enum yasl_selection sel, const ch [pb setString:s forType:NSPasteboardTypeString]; } -void yasl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target) +void cocoa_display_paste(struct cocoa_display *d, enum yasl_selection sel, struct cocoa_surface *target) { (void)d; (void)sel; if (!target) return; @@ -1202,44 +1251,44 @@ void yasl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct if (utf8) { struct yasl_input_event ev = { .kind = YASL_INPUT_PASTE, - .surface = target, + .surface = &target->base, .paste = { .text = utf8, .len = strlen(utf8) }, }; input_queue_push(&target->input, &ev); } } -int yasl_display_modifiers(const struct yasl_display *d) +int cocoa_display_modifiers(const struct cocoa_display *d) { (void)d; return (int)cocoa_mods(NSEvent.modifierFlags); } -struct yasl_vector2i yasl_surface_size(const struct yasl_surface *w) +struct yasl_vector2i cocoa_surface_size(const struct cocoa_surface *w) { return w ? w->size : (struct yasl_vector2i){0}; } -uint32_t yasl_surface_scale_120(const struct yasl_surface *w) +uint32_t cocoa_surface_scale_120(const struct cocoa_surface *w) { return w ? w->scale_120 : YASL_SCALE_UNIT; } -struct yasl_surface *yasl_surface_create_popup(struct yasl_surface *parent, +struct cocoa_surface *cocoa_surface_create_popup(struct cocoa_surface *parent, const struct yasl_popup_spec *spec) { (void)parent; (void)spec; return NULL; } -bool yasl_surface_popup_reposition(struct yasl_surface *popup, +bool cocoa_surface_popup_reposition(struct cocoa_surface *popup, const struct yasl_popup_spec *spec) { (void)popup; (void)spec; return false; } -bool yasl_display_drag_start(struct yasl_display *d, struct yasl_surface *origin, +bool cocoa_display_drag_start(struct cocoa_display *d, struct cocoa_surface *origin, const struct yasl_drag_image *image, const struct yasl_drag_listener *l, void *ud) { @@ -1247,34 +1296,303 @@ bool yasl_display_drag_start(struct yasl_display *d, struct yasl_surface *origin return false; } -void yasl_display_drag_cancel(struct yasl_display *d) { (void)d; } -bool yasl_surface_drag_carry(struct yasl_surface *w, struct yasl_vector2i offset) { (void)w; (void)offset; return false; } +void cocoa_display_drag_cancel(struct cocoa_display *d) { (void)d; } +bool cocoa_surface_drag_carry(struct cocoa_surface *w, struct yasl_vector2i offset) { (void)w; (void)offset; return false; } -bool yasl_surface_start_interactive_move(struct yasl_surface *s) +bool cocoa_surface_start_interactive_move(struct cocoa_surface *s) { if (!s || !s->window) return false; [s->window performWindowDragWithEvent:[NSApp currentEvent]]; return true; } -bool yasl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_resize_edge edge) +bool cocoa_surface_start_interactive_resize(struct cocoa_surface *s, enum yasl_resize_edge edge) { (void)s; (void)edge; return false; } -bool yasl_surface_show_window_menu(struct yasl_surface *s, struct yasl_vector2i pos) +bool cocoa_surface_show_window_menu(struct cocoa_surface *s, struct yasl_vector2i pos) { (void)s; (void)pos; return false; } -void *yasl_display_get_native_handle(struct yasl_display *d) +void *cocoa_display_get_native_handle(struct cocoa_display *d) { return d ? (void *)d : NULL; } -void *yasl_surface_get_native_handle(struct yasl_surface *s) +void *cocoa_surface_get_native_handle(struct cocoa_surface *s) { return s ? (void *)s->view : NULL; } + + + +void cocoa_surface_set_callbacks(struct cocoa_surface *w, + const struct yasl_surface_cbs *cbs, void *ud) +{ + static const struct yasl_surface_cbs empty = {0}; + if (!w || w->closed) return; + w->cbs = cbs ? cbs : ∅ + w->ud = ud; +} + +bool cocoa_surface_input_poll(struct cocoa_surface *w, struct yasl_input_event *ev) +{ + return w && ev && input_queue_pop(&w->input, ev); +} + +uint32_t cocoa_surface_state(const struct cocoa_surface *w) +{ + return w ? w->state : 0; +} + +struct yasl_timing cocoa_surface_timing(const struct cocoa_surface *w) +{ + struct yasl_timing timing = {0}; + if (w) { + timing.refresh_ns = w->clock.refresh_ns; + timing.last_present_ns = w->clock.last_present_ns; + timing.next_present_ns = yasl_clock_next_present_ns(&w->clock); + timing.frames_skipped = w->frames_skipped; + yasl_clock_resize_stats(&w->clock, &timing.resize); + } + return timing; +} + +uint64_t cocoa_surface_frame_time_ns(struct cocoa_surface *w, uint64_t now) +{ + return w ? yasl_clock_frame_timestamp_ns(&w->clock, now) : now; +} + +static struct yasl_display *impl_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec) +{ + struct cocoa_display *result = cocoa_display_open(loop, spec); + return result ? &result->base : NULL; +} + +static void impl_display_close(struct yasl_display *d) +{ + cocoa_display_close((struct cocoa_display *)d); +} + +static uint32_t impl_display_caps(const struct yasl_display *d) +{ + return cocoa_display_caps((const struct cocoa_display *)d); +} + +static void impl_display_dispatch(struct yasl_display *d) +{ + cocoa_display_dispatch((struct cocoa_display *)d); +} + +static void impl_display_flush(struct yasl_display *d) +{ + cocoa_display_flush((struct cocoa_display *)d); +} + +static int impl_display_modifiers(const struct yasl_display *d) +{ + return cocoa_display_modifiers((const struct cocoa_display *)d); +} + +static void impl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text) +{ + cocoa_display_copy((struct cocoa_display *)d, sel, text); +} + +static void impl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target) +{ + cocoa_display_paste((struct cocoa_display *)d, sel, (struct cocoa_surface *)target); +} + +static bool impl_display_drag_start(struct yasl_display *d, struct yasl_surface *origin, const struct yasl_drag_image *image, const struct yasl_drag_listener *l, void *ud) +{ + return cocoa_display_drag_start((struct cocoa_display *)d, (struct cocoa_surface *)origin, image, l, ud); +} + +static void impl_display_drag_cancel(struct yasl_display *d) +{ + cocoa_display_drag_cancel((struct cocoa_display *)d); +} + +static struct yasl_surface *impl_surface_create_toplevel(struct yasl_display *d, const struct yasl_surface_spec *spec) +{ + struct cocoa_surface *result = cocoa_surface_create_toplevel((struct cocoa_display *)d, spec); + return result ? &result->base : NULL; +} + +static struct yasl_surface *impl_surface_create_popup(struct yasl_surface *parent, const struct yasl_popup_spec *spec) +{ + struct cocoa_surface *result = cocoa_surface_create_popup((struct cocoa_surface *)parent, spec); + return result ? &result->base : NULL; +} + +static bool impl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_popup_spec *spec) +{ + return cocoa_surface_popup_reposition((struct cocoa_surface *)popup, spec); +} + +static void impl_surface_destroy(struct yasl_surface *s) +{ + cocoa_surface_destroy((struct cocoa_surface *)s); +} + +static struct yasl_display *impl_surface_display(const struct yasl_surface *s) +{ + struct cocoa_display *result = cocoa_surface_display((const struct cocoa_surface *)s); + return result ? &result->base : NULL; +} + +static void impl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_surface_cbs *cbs, void *ud) +{ + cocoa_surface_set_callbacks((struct cocoa_surface *)s, cbs, ud); +} + +static void impl_surface_set_title(struct yasl_surface *s, const char *title) +{ + cocoa_surface_set_title((struct cocoa_surface *)s, title); +} + +static void impl_surface_schedule_redraw(struct yasl_surface *s) +{ + cocoa_surface_schedule_redraw((struct cocoa_surface *)s); +} + +static bool impl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage) +{ + return cocoa_surface_present((struct cocoa_surface *)s, fb, damage); +} + +static bool impl_surface_input_poll(struct yasl_surface *s, struct yasl_input_event *ev) +{ + return cocoa_surface_input_poll((struct cocoa_surface *)s, ev); +} + +static struct yasl_vector2i impl_surface_size(const struct yasl_surface *s) +{ + return cocoa_surface_size((const struct cocoa_surface *)s); +} + +static uint32_t impl_surface_scale_120(const struct yasl_surface *s) +{ + return cocoa_surface_scale_120((const struct cocoa_surface *)s); +} + +static uint32_t impl_surface_state(const struct yasl_surface *s) +{ + return cocoa_surface_state((const struct cocoa_surface *)s); +} + +static struct yasl_timing impl_surface_timing(const struct yasl_surface *s) +{ + return cocoa_surface_timing((const struct cocoa_surface *)s); +} + +static uint64_t impl_surface_frame_time_ns(struct yasl_surface *s, uint64_t now) +{ + return cocoa_surface_frame_time_ns((struct cocoa_surface *)s, now); +} + +static void impl_surface_set_cursor(struct yasl_surface *s, enum yasl_cursor cursor) +{ + cocoa_surface_set_cursor((struct cocoa_surface *)s, cursor); +} + +static void impl_surface_set_background_color(struct yasl_surface *s, uint32_t bg) +{ + cocoa_surface_set_background_color((struct cocoa_surface *)s, bg); +} + +static const char *impl_surface_ime_preedit(const struct yasl_surface *s) +{ + return cocoa_surface_ime_preedit((const struct cocoa_surface *)s); +} + +static bool impl_surface_ime_preedit_cursor(const struct yasl_surface *s, int *begin, int *end) +{ + return cocoa_surface_ime_preedit_cursor((const struct cocoa_surface *)s, begin, end); +} + +static void impl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_rect2i caret) +{ + cocoa_surface_set_ime_caret((struct cocoa_surface *)s, caret); +} + +static bool impl_surface_start_interactive_move(struct yasl_surface *s) +{ + return cocoa_surface_start_interactive_move((struct cocoa_surface *)s); +} + +static bool impl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_resize_edge edge) +{ + return cocoa_surface_start_interactive_resize((struct cocoa_surface *)s, edge); +} + +static bool impl_surface_show_window_menu(struct yasl_surface *s, struct yasl_vector2i pos) +{ + return cocoa_surface_show_window_menu((struct cocoa_surface *)s, pos); +} + +static bool impl_surface_drag_carry(struct yasl_surface *s, struct yasl_vector2i offset) +{ + return cocoa_surface_drag_carry((struct cocoa_surface *)s, offset); +} + +static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_vector2i at) +{ + return cocoa_surface_place_under_pointer((struct cocoa_surface *)s, at); +} + +static void *impl_display_get_native_handle(struct yasl_display *d) +{ + return cocoa_display_get_native_handle((struct cocoa_display *)d); +} + +static void *impl_surface_get_native_handle(struct yasl_surface *s) +{ + return cocoa_surface_get_native_handle((struct cocoa_surface *)s); +} + +const struct yasl_backend_ops yasl_cocoa_backend = { + .type = YASL_BACKEND_COCOA, + .display_open = impl_display_open, + .display_close = impl_display_close, + .display_caps = impl_display_caps, + .display_dispatch = impl_display_dispatch, + .display_flush = impl_display_flush, + .display_modifiers = impl_display_modifiers, + .display_copy = impl_display_copy, + .display_paste = impl_display_paste, + .display_drag_start = impl_display_drag_start, + .display_drag_cancel = impl_display_drag_cancel, + .surface_create_toplevel = impl_surface_create_toplevel, + .surface_create_popup = impl_surface_create_popup, + .surface_popup_reposition = impl_surface_popup_reposition, + .surface_destroy = impl_surface_destroy, + .surface_display = impl_surface_display, + .surface_set_callbacks = impl_surface_set_callbacks, + .surface_set_title = impl_surface_set_title, + .surface_schedule_redraw = impl_surface_schedule_redraw, + .surface_present = impl_surface_present, + .surface_input_poll = impl_surface_input_poll, + .surface_size = impl_surface_size, + .surface_scale_120 = impl_surface_scale_120, + .surface_state = impl_surface_state, + .surface_timing = impl_surface_timing, + .surface_frame_time_ns = impl_surface_frame_time_ns, + .surface_set_cursor = impl_surface_set_cursor, + .surface_set_background_color = impl_surface_set_background_color, + .surface_ime_preedit = impl_surface_ime_preedit, + .surface_ime_preedit_cursor = impl_surface_ime_preedit_cursor, + .surface_set_ime_caret = impl_surface_set_ime_caret, + .surface_start_interactive_move = impl_surface_start_interactive_move, + .surface_start_interactive_resize = impl_surface_start_interactive_resize, + .surface_show_window_menu = impl_surface_show_window_menu, + .surface_drag_carry = impl_surface_drag_carry, + .surface_place_under_pointer = impl_surface_place_under_pointer, + .display_get_native_handle = impl_display_get_native_handle, + .surface_get_native_handle = impl_surface_get_native_handle, +}; diff --git a/src/common/backend.c b/src/common/backend.c new file mode 100644 index 0000000..a6fa6f2 --- /dev/null +++ b/src/common/backend.c @@ -0,0 +1,321 @@ +#include "backend.h" + +#include +#include +#include + +static const struct yasl_backend_ops *backend_ops(enum yasl_backend backend) +{ + switch (backend) { +#if YASL_HAS_WAYLAND + case YASL_BACKEND_WAYLAND: return &yasl_wayland_backend; +#endif +#if YASL_HAS_X11 + case YASL_BACKEND_X11: return &yasl_x11_backend; +#endif +#if YASL_HAS_COCOA + case YASL_BACKEND_COCOA: return &yasl_cocoa_backend; +#endif + default: return NULL; + } +} + +static struct yasl_display *open_backend(enum yasl_backend backend, + struct yasl_loop *loop, const struct yasl_display_spec *spec) +{ + const struct yasl_backend_ops *ops = backend_ops(backend); + if (!ops) { + errno = ENOSYS; + return NULL; + } + errno = 0; + struct yasl_display *d = ops->display_open(loop, spec); + if (!d && !errno) errno = ENODEV; + return d; +} + +struct yasl_display *yasl_display_open(struct yasl_loop *loop, + const struct yasl_display_spec *spec) +{ + enum yasl_backend backend = spec ? spec->backend : YASL_BACKEND_AUTO; + if (backend < YASL_BACKEND_AUTO || backend > YASL_BACKEND_COCOA) { + errno = EINVAL; + return NULL; + } + if (backend != YASL_BACKEND_AUTO) + return open_backend(backend, loop, spec); +#if YASL_HAS_COCOA + return open_backend(YASL_BACKEND_COCOA, loop, spec); +#else + const char *session = getenv("XDG_SESSION_TYPE"); + bool wayland = getenv("WAYLAND_DISPLAY") || getenv("WAYLAND_SOCKET") || + (session && strcmp(session, "wayland") == 0); + enum yasl_backend first = wayland ? YASL_BACKEND_WAYLAND : YASL_BACKEND_X11; + enum yasl_backend second = wayland ? YASL_BACKEND_X11 : YASL_BACKEND_WAYLAND; + struct yasl_display *d = NULL; + if (backend_ops(first)) d = open_backend(first, loop, spec); + if (!d && backend_ops(second)) d = open_backend(second, loop, spec); + return d; +#endif +} + +enum yasl_backend yasl_display_backend(const struct yasl_display *d) +{ + return d ? d->ops->type : YASL_BACKEND_AUTO; +} + +void yasl_display_close(struct yasl_display *d) +{ + if (!d || !d->ops->display_close) + return; + d->ops->display_close(d); +} + +uint32_t yasl_display_caps(const struct yasl_display *d) +{ + if (!d || !d->ops->display_caps) + return 0; + return d->ops->display_caps(d); +} + +void yasl_display_dispatch(struct yasl_display *d) +{ + if (!d || !d->ops->display_dispatch) + return; + d->ops->display_dispatch(d); +} + +void yasl_display_flush(struct yasl_display *d) +{ + if (!d || !d->ops->display_flush) + return; + d->ops->display_flush(d); +} + +int yasl_display_modifiers(const struct yasl_display *d) +{ + if (!d || !d->ops->display_modifiers) + return 0; + return d->ops->display_modifiers(d); +} + +void yasl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text) +{ + if (!d || !d->ops->display_copy) + return; + d->ops->display_copy(d, sel, text); +} + +void yasl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target) +{ + if (!d || !d->ops->display_paste) + return; + if (!target || target->display != d) + return; + d->ops->display_paste(d, sel, target); +} + +bool yasl_display_drag_start(struct yasl_display *d, struct yasl_surface *origin, const struct yasl_drag_image *image, const struct yasl_drag_listener *l, void *ud) +{ + if (!d || !d->ops->display_drag_start) + return 0; + if (!origin || origin->display != d) + return false; + return d->ops->display_drag_start(d, origin, image, l, ud); +} + +void yasl_display_drag_cancel(struct yasl_display *d) +{ + if (!d || !d->ops->display_drag_cancel) + return; + d->ops->display_drag_cancel(d); +} + +struct yasl_surface *yasl_surface_create_toplevel(struct yasl_display *d, const struct yasl_surface_spec *spec) +{ + if (!d || !d->ops->surface_create_toplevel) + return 0; + return d->ops->surface_create_toplevel(d, spec); +} + +struct yasl_surface *yasl_surface_create_popup(struct yasl_surface *parent, const struct yasl_popup_spec *spec) +{ + if (!parent || !parent->display->ops->surface_create_popup) + return 0; + return parent->display->ops->surface_create_popup(parent, spec); +} + +bool yasl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_popup_spec *spec) +{ + if (!popup || !popup->display->ops->surface_popup_reposition) + return 0; + return popup->display->ops->surface_popup_reposition(popup, spec); +} + +void yasl_surface_destroy(struct yasl_surface *s) +{ + if (!s || !s->display->ops->surface_destroy) + return; + s->display->ops->surface_destroy(s); +} + +struct yasl_display *yasl_surface_display(const struct yasl_surface *s) +{ + if (!s || !s->display->ops->surface_display) + return 0; + return s->display->ops->surface_display(s); +} + +void yasl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_surface_cbs *cbs, void *ud) +{ + if (!s || !s->display->ops->surface_set_callbacks) + return; + s->display->ops->surface_set_callbacks(s, cbs, ud); +} + +void yasl_surface_set_title(struct yasl_surface *s, const char *title) +{ + if (!s || !s->display->ops->surface_set_title) + return; + s->display->ops->surface_set_title(s, title); +} + +void yasl_surface_schedule_redraw(struct yasl_surface *s) +{ + if (!s || !s->display->ops->surface_schedule_redraw) + return; + s->display->ops->surface_schedule_redraw(s); +} + +bool yasl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage) +{ + if (!s || !s->display->ops->surface_present) + return 0; + return s->display->ops->surface_present(s, fb, damage); +} + +bool yasl_surface_input_poll(struct yasl_surface *s, struct yasl_input_event *ev) +{ + if (!s || !s->display->ops->surface_input_poll) + return 0; + return s->display->ops->surface_input_poll(s, ev); +} + +struct yasl_vector2i yasl_surface_size(const struct yasl_surface *s) +{ + if (!s || !s->display->ops->surface_size) + return (struct yasl_vector2i){0}; + return s->display->ops->surface_size(s); +} + +uint32_t yasl_surface_scale_120(const struct yasl_surface *s) +{ + if (!s || !s->display->ops->surface_scale_120) + return YASL_SCALE_UNIT; + return s->display->ops->surface_scale_120(s); +} + +uint32_t yasl_surface_state(const struct yasl_surface *s) +{ + if (!s || !s->display->ops->surface_state) + return 0; + return s->display->ops->surface_state(s); +} + +struct yasl_timing yasl_surface_timing(const struct yasl_surface *s) +{ + if (!s || !s->display->ops->surface_timing) + return (struct yasl_timing){0}; + return s->display->ops->surface_timing(s); +} + +uint64_t yasl_surface_frame_time_ns(struct yasl_surface *s, uint64_t now) +{ + if (!s || !s->display->ops->surface_frame_time_ns) + return 0; + return s->display->ops->surface_frame_time_ns(s, now); +} + +void yasl_surface_set_cursor(struct yasl_surface *s, enum yasl_cursor cursor) +{ + if (!s || !s->display->ops->surface_set_cursor) + return; + s->display->ops->surface_set_cursor(s, cursor); +} + +void yasl_surface_set_background_color(struct yasl_surface *s, uint32_t bg) +{ + if (!s || !s->display->ops->surface_set_background_color) + return; + s->display->ops->surface_set_background_color(s, bg); +} + +const char *yasl_surface_ime_preedit(const struct yasl_surface *s) +{ + if (!s || !s->display->ops->surface_ime_preedit) + return 0; + return s->display->ops->surface_ime_preedit(s); +} + +bool yasl_surface_ime_preedit_cursor(const struct yasl_surface *s, int *begin, int *end) +{ + if (!s || !s->display->ops->surface_ime_preedit_cursor) + return 0; + return s->display->ops->surface_ime_preedit_cursor(s, begin, end); +} + +void yasl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_rect2i caret) +{ + if (!s || !s->display->ops->surface_set_ime_caret) + return; + s->display->ops->surface_set_ime_caret(s, caret); +} + +bool yasl_surface_start_interactive_move(struct yasl_surface *s) +{ + if (!s || !s->display->ops->surface_start_interactive_move) + return 0; + return s->display->ops->surface_start_interactive_move(s); +} + +bool yasl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_resize_edge edge) +{ + if (!s || !s->display->ops->surface_start_interactive_resize) + return 0; + return s->display->ops->surface_start_interactive_resize(s, edge); +} + +bool yasl_surface_show_window_menu(struct yasl_surface *s, struct yasl_vector2i pos) +{ + if (!s || !s->display->ops->surface_show_window_menu) + return 0; + return s->display->ops->surface_show_window_menu(s, pos); +} + +bool yasl_surface_drag_carry(struct yasl_surface *s, struct yasl_vector2i offset) +{ + if (!s || !s->display->ops->surface_drag_carry) + return 0; + return s->display->ops->surface_drag_carry(s, offset); +} + +bool yasl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_vector2i at) +{ + if (!s || !s->display->ops->surface_place_under_pointer) + return 0; + return s->display->ops->surface_place_under_pointer(s, at); +} + +void *yasl_display_get_native_handle(struct yasl_display *d) +{ + if (!d || !d->ops->display_get_native_handle) + return 0; + return d->ops->display_get_native_handle(d); +} + +void *yasl_surface_get_native_handle(struct yasl_surface *s) +{ + if (!s || !s->display->ops->surface_get_native_handle) + return 0; + return s->display->ops->surface_get_native_handle(s); +} diff --git a/src/common/backend.h b/src/common/backend.h new file mode 100644 index 0000000..0294aed --- /dev/null +++ b/src/common/backend.h @@ -0,0 +1,67 @@ +#ifndef YASL_BACKEND_H +#define YASL_BACKEND_H + +#include "yasl/yasl.h" + +struct yasl_backend_ops; + +struct yasl_display { + const struct yasl_backend_ops *ops; +}; + +struct yasl_surface { + struct yasl_display *display; +}; + +struct yasl_backend_ops { + enum yasl_backend type; + struct yasl_display *(*display_open)(struct yasl_loop *loop, const struct yasl_display_spec *spec); + void (*display_close)(struct yasl_display *d); + uint32_t (*display_caps)(const struct yasl_display *d); + void (*display_dispatch)(struct yasl_display *d); + void (*display_flush)(struct yasl_display *d); + int (*display_modifiers)(const struct yasl_display *d); + void (*display_copy)(struct yasl_display *d, enum yasl_selection sel, const char *text); + void (*display_paste)(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target); + bool (*display_drag_start)(struct yasl_display *d, struct yasl_surface *origin, const struct yasl_drag_image *image, const struct yasl_drag_listener *l, void *ud); + void (*display_drag_cancel)(struct yasl_display *d); + struct yasl_surface *(*surface_create_toplevel)(struct yasl_display *d, const struct yasl_surface_spec *spec); + struct yasl_surface *(*surface_create_popup)(struct yasl_surface *parent, const struct yasl_popup_spec *spec); + bool (*surface_popup_reposition)(struct yasl_surface *popup, const struct yasl_popup_spec *spec); + void (*surface_destroy)(struct yasl_surface *s); + struct yasl_display *(*surface_display)(const struct yasl_surface *s); + void (*surface_set_callbacks)(struct yasl_surface *s, const struct yasl_surface_cbs *cbs, void *ud); + 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_framebuffer *fb, const struct yasl_damage_list *damage); + bool (*surface_input_poll)(struct yasl_surface *s, struct yasl_input_event *ev); + struct yasl_vector2i (*surface_size)(const struct yasl_surface *s); + uint32_t (*surface_scale_120)(const struct yasl_surface *s); + uint32_t (*surface_state)(const struct yasl_surface *s); + struct yasl_timing (*surface_timing)(const struct yasl_surface *s); + uint64_t (*surface_frame_time_ns)(struct yasl_surface *s, uint64_t now); + void (*surface_set_cursor)(struct yasl_surface *s, enum yasl_cursor cursor); + void (*surface_set_background_color)(struct yasl_surface *s, uint32_t bg); + const char *(*surface_ime_preedit)(const struct yasl_surface *s); + bool (*surface_ime_preedit_cursor)(const struct yasl_surface *s, int *begin, int *end); + void (*surface_set_ime_caret)(struct yasl_surface *s, struct yasl_rect2i caret); + bool (*surface_start_interactive_move)(struct yasl_surface *s); + bool (*surface_start_interactive_resize)(struct yasl_surface *s, enum yasl_resize_edge edge); + bool (*surface_show_window_menu)(struct yasl_surface *s, struct yasl_vector2i pos); + bool (*surface_drag_carry)(struct yasl_surface *s, struct yasl_vector2i offset); + bool (*surface_place_under_pointer)(struct yasl_surface *s, struct yasl_vector2i at); + void *(*display_get_native_handle)(struct yasl_display *d); + void *(*surface_get_native_handle)(struct yasl_surface *s); +}; + +#if YASL_HAS_WAYLAND +extern const struct yasl_backend_ops yasl_wayland_backend; +#endif +#if YASL_HAS_X11 +extern const struct yasl_backend_ops yasl_x11_backend; +#endif +#if YASL_HAS_COCOA +extern const struct yasl_backend_ops yasl_cocoa_backend; +#endif + +#endif diff --git a/src/common/meson.build b/src/common/meson.build new file mode 100644 index 0000000..61653df --- /dev/null +++ b/src/common/meson.build @@ -0,0 +1 @@ +sources += files('backend.c', 'loop.c', 'timing.c', 'utf8.c') diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..0dae030 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,10 @@ +subdir('common') +if has_wayland + subdir('wayland') +endif +if has_x11 + subdir('x11') +endif +if has_cocoa + subdir('cocoa') +endif diff --git a/src/wayland/api.h b/src/wayland/api.h new file mode 100644 index 0000000..e4473cb --- /dev/null +++ b/src/wayland/api.h @@ -0,0 +1,47 @@ +#ifndef YASL_WAYLAND_API_H +#define YASL_WAYLAND_API_H + +#include "common/backend.h" + +struct wayland_display; +struct wayland_surface; + +struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec); +void wayland_display_close(struct wayland_display *d); +uint32_t wayland_display_caps(const struct wayland_display *d); +void wayland_display_dispatch(struct wayland_display *d); +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_selection sel, const char *text); +void wayland_display_paste(struct wayland_display *d, enum yasl_selection sel, struct wayland_surface *target); +bool wayland_display_drag_start(struct wayland_display *d, struct wayland_surface *origin, const struct yasl_drag_image *image, const struct yasl_drag_listener *l, void *ud); +void wayland_display_drag_cancel(struct wayland_display *d); +struct wayland_surface *wayland_surface_create_toplevel(struct wayland_display *d, const struct yasl_surface_spec *spec); +struct wayland_surface *wayland_surface_create_popup(struct wayland_surface *parent, const struct yasl_popup_spec *spec); +bool wayland_surface_popup_reposition(struct wayland_surface *popup, const struct yasl_popup_spec *spec); +void wayland_surface_destroy(struct wayland_surface *s); +struct wayland_display *wayland_surface_display(const struct wayland_surface *s); +void wayland_surface_set_callbacks(struct wayland_surface *s, const struct yasl_surface_cbs *cbs, void *ud); +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_framebuffer *fb, const struct yasl_damage_list *damage); +bool wayland_surface_input_poll(struct wayland_surface *s, struct yasl_input_event *ev); +struct yasl_vector2i wayland_surface_size(const struct wayland_surface *s); +uint32_t wayland_surface_scale_120(const struct wayland_surface *s); +uint32_t wayland_surface_state(const struct wayland_surface *s); +struct yasl_timing wayland_surface_timing(const struct wayland_surface *s); +uint64_t wayland_surface_frame_time_ns(struct wayland_surface *s, uint64_t now); +void wayland_surface_set_cursor(struct wayland_surface *s, enum yasl_cursor cursor); +void wayland_surface_set_background_color(struct wayland_surface *s, uint32_t bg); +const char *wayland_surface_ime_preedit(const struct wayland_surface *s); +bool wayland_surface_ime_preedit_cursor(const struct wayland_surface *s, int *begin, int *end); +void wayland_surface_set_ime_caret(struct wayland_surface *s, struct yasl_rect2i caret); +bool wayland_surface_start_interactive_move(struct wayland_surface *s); +bool wayland_surface_start_interactive_resize(struct wayland_surface *s, enum yasl_resize_edge edge); +bool wayland_surface_show_window_menu(struct wayland_surface *s, struct yasl_vector2i pos); +bool wayland_surface_drag_carry(struct wayland_surface *s, struct yasl_vector2i offset); +bool wayland_surface_place_under_pointer(struct wayland_surface *s, struct yasl_vector2i at); +void *wayland_display_get_native_handle(struct wayland_display *d); +void *wayland_surface_get_native_handle(struct wayland_surface *s); + +#endif diff --git a/src/wayland/backend.c b/src/wayland/backend.c new file mode 100644 index 0000000..da7f1a3 --- /dev/null +++ b/src/wayland/backend.c @@ -0,0 +1,231 @@ +#include "wayland/internal.h" + +static struct yasl_display *impl_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec) +{ + struct wayland_display *result = wayland_display_open(loop, spec); + return result ? &result->base : NULL; +} + +static void impl_display_close(struct yasl_display *d) +{ + wayland_display_close((struct wayland_display *)d); +} + +static uint32_t impl_display_caps(const struct yasl_display *d) +{ + return wayland_display_caps((const struct wayland_display *)d); +} + +static void impl_display_dispatch(struct yasl_display *d) +{ + wayland_display_dispatch((struct wayland_display *)d); +} + +static void impl_display_flush(struct yasl_display *d) +{ + wayland_display_flush((struct wayland_display *)d); +} + +static int impl_display_modifiers(const struct yasl_display *d) +{ + return wayland_display_modifiers((const struct wayland_display *)d); +} + +static void impl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text) +{ + wayland_display_copy((struct wayland_display *)d, sel, text); +} + +static void impl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target) +{ + wayland_display_paste((struct wayland_display *)d, sel, (struct wayland_surface *)target); +} + +static bool impl_display_drag_start(struct yasl_display *d, struct yasl_surface *origin, const struct yasl_drag_image *image, const struct yasl_drag_listener *l, void *ud) +{ + return wayland_display_drag_start((struct wayland_display *)d, (struct wayland_surface *)origin, image, l, ud); +} + +static void impl_display_drag_cancel(struct yasl_display *d) +{ + wayland_display_drag_cancel((struct wayland_display *)d); +} + +static struct yasl_surface *impl_surface_create_toplevel(struct yasl_display *d, const struct yasl_surface_spec *spec) +{ + struct wayland_surface *result = wayland_surface_create_toplevel((struct wayland_display *)d, spec); + return result ? &result->base : NULL; +} + +static struct yasl_surface *impl_surface_create_popup(struct yasl_surface *parent, const struct yasl_popup_spec *spec) +{ + struct wayland_surface *result = wayland_surface_create_popup((struct wayland_surface *)parent, spec); + return result ? &result->base : NULL; +} + +static bool impl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_popup_spec *spec) +{ + return wayland_surface_popup_reposition((struct wayland_surface *)popup, spec); +} + +static void impl_surface_destroy(struct yasl_surface *s) +{ + wayland_surface_destroy((struct wayland_surface *)s); +} + +static struct yasl_display *impl_surface_display(const struct yasl_surface *s) +{ + struct wayland_display *result = wayland_surface_display((const struct wayland_surface *)s); + return result ? &result->base : NULL; +} + +static void impl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_surface_cbs *cbs, void *ud) +{ + wayland_surface_set_callbacks((struct wayland_surface *)s, cbs, ud); +} + +static void impl_surface_set_title(struct yasl_surface *s, const char *title) +{ + wayland_surface_set_title((struct wayland_surface *)s, title); +} + +static void impl_surface_schedule_redraw(struct yasl_surface *s) +{ + wayland_surface_schedule_redraw((struct wayland_surface *)s); +} + +static bool impl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage) +{ + return wayland_surface_present((struct wayland_surface *)s, fb, damage); +} + +static bool impl_surface_input_poll(struct yasl_surface *s, struct yasl_input_event *ev) +{ + return wayland_surface_input_poll((struct wayland_surface *)s, ev); +} + +static struct yasl_vector2i impl_surface_size(const struct yasl_surface *s) +{ + return wayland_surface_size((const struct wayland_surface *)s); +} + +static uint32_t impl_surface_scale_120(const struct yasl_surface *s) +{ + return wayland_surface_scale_120((const struct wayland_surface *)s); +} + +static uint32_t impl_surface_state(const struct yasl_surface *s) +{ + return wayland_surface_state((const struct wayland_surface *)s); +} + +static struct yasl_timing impl_surface_timing(const struct yasl_surface *s) +{ + return wayland_surface_timing((const struct wayland_surface *)s); +} + +static uint64_t impl_surface_frame_time_ns(struct yasl_surface *s, uint64_t now) +{ + return wayland_surface_frame_time_ns((struct wayland_surface *)s, now); +} + +static void impl_surface_set_cursor(struct yasl_surface *s, enum yasl_cursor cursor) +{ + wayland_surface_set_cursor((struct wayland_surface *)s, cursor); +} + +static void impl_surface_set_background_color(struct yasl_surface *s, uint32_t bg) +{ + wayland_surface_set_background_color((struct wayland_surface *)s, bg); +} + +static const char *impl_surface_ime_preedit(const struct yasl_surface *s) +{ + return wayland_surface_ime_preedit((const struct wayland_surface *)s); +} + +static bool impl_surface_ime_preedit_cursor(const struct yasl_surface *s, int *begin, int *end) +{ + return wayland_surface_ime_preedit_cursor((const struct wayland_surface *)s, begin, end); +} + +static void impl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_rect2i caret) +{ + wayland_surface_set_ime_caret((struct wayland_surface *)s, caret); +} + +static bool impl_surface_start_interactive_move(struct yasl_surface *s) +{ + return wayland_surface_start_interactive_move((struct wayland_surface *)s); +} + +static bool impl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_resize_edge edge) +{ + return wayland_surface_start_interactive_resize((struct wayland_surface *)s, edge); +} + +static bool impl_surface_show_window_menu(struct yasl_surface *s, struct yasl_vector2i pos) +{ + return wayland_surface_show_window_menu((struct wayland_surface *)s, pos); +} + +static bool impl_surface_drag_carry(struct yasl_surface *s, struct yasl_vector2i offset) +{ + return wayland_surface_drag_carry((struct wayland_surface *)s, offset); +} + +static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_vector2i at) +{ + return wayland_surface_place_under_pointer((struct wayland_surface *)s, at); +} + +static void *impl_display_get_native_handle(struct yasl_display *d) +{ + return wayland_display_get_native_handle((struct wayland_display *)d); +} + +static void *impl_surface_get_native_handle(struct yasl_surface *s) +{ + return wayland_surface_get_native_handle((struct wayland_surface *)s); +} + +const struct yasl_backend_ops yasl_wayland_backend = { + .type = YASL_BACKEND_WAYLAND, + .display_open = impl_display_open, + .display_close = impl_display_close, + .display_caps = impl_display_caps, + .display_dispatch = impl_display_dispatch, + .display_flush = impl_display_flush, + .display_modifiers = impl_display_modifiers, + .display_copy = impl_display_copy, + .display_paste = impl_display_paste, + .display_drag_start = impl_display_drag_start, + .display_drag_cancel = impl_display_drag_cancel, + .surface_create_toplevel = impl_surface_create_toplevel, + .surface_create_popup = impl_surface_create_popup, + .surface_popup_reposition = impl_surface_popup_reposition, + .surface_destroy = impl_surface_destroy, + .surface_display = impl_surface_display, + .surface_set_callbacks = impl_surface_set_callbacks, + .surface_set_title = impl_surface_set_title, + .surface_schedule_redraw = impl_surface_schedule_redraw, + .surface_present = impl_surface_present, + .surface_input_poll = impl_surface_input_poll, + .surface_size = impl_surface_size, + .surface_scale_120 = impl_surface_scale_120, + .surface_state = impl_surface_state, + .surface_timing = impl_surface_timing, + .surface_frame_time_ns = impl_surface_frame_time_ns, + .surface_set_cursor = impl_surface_set_cursor, + .surface_set_background_color = impl_surface_set_background_color, + .surface_ime_preedit = impl_surface_ime_preedit, + .surface_ime_preedit_cursor = impl_surface_ime_preedit_cursor, + .surface_set_ime_caret = impl_surface_set_ime_caret, + .surface_start_interactive_move = impl_surface_start_interactive_move, + .surface_start_interactive_resize = impl_surface_start_interactive_resize, + .surface_show_window_menu = impl_surface_show_window_menu, + .surface_drag_carry = impl_surface_drag_carry, + .surface_place_under_pointer = impl_surface_place_under_pointer, + .display_get_native_handle = impl_display_get_native_handle, + .surface_get_native_handle = impl_surface_get_native_handle, +}; diff --git a/src/wayland/buffer.c b/src/wayland/buffer.c index 492cfde..9776f13 100644 --- a/src/wayland/buffer.c +++ b/src/wayland/buffer.c @@ -7,7 +7,7 @@ static long minor_faults(void) return getrusage(RUSAGE_SELF, &usage) == 0 ? usage.ru_minflt : 0; } -void surface_update_opaque_region(struct yasl_surface *w) +void surface_update_opaque_region(struct wayland_surface *w) { if (!w || !w->opaque || w->logical_width <= 0 || w->logical_height <= 0) return; @@ -55,7 +55,7 @@ static bool fd_truncate(int fd, size_t bytes) return rc == 0; } -int buffer_reserve(struct yasl_surface *w, struct buffer *b, size_t needed) +int buffer_reserve(struct wayland_surface *w, struct buffer *b, size_t needed) { if (needed <= b->capacity) return 0; if (b->busy || needed > INT32_MAX) return -1; @@ -87,7 +87,7 @@ int buffer_reserve(struct yasl_surface *w, struct buffer *b, size_t needed) return 0; } -int buffer_prepare(struct yasl_surface *w, struct buffer *b, int width, int height, +int buffer_prepare(struct wayland_surface *w, struct buffer *b, int width, int height, const struct wl_buffer_listener *listener) { if (b->proxy && !b->busy && b->width == width && b->height == height) return 0; @@ -113,7 +113,7 @@ int buffer_prepare(struct yasl_surface *w, struct buffer *b, int width, int heig return 0; } -static void publish_surface_scale(struct yasl_surface *w) +static void publish_surface_scale(struct wayland_surface *w) { if (w->viewport) { wp_viewport_set_destination(w->viewport, w->logical_width, w->logical_height); @@ -124,15 +124,15 @@ static void publish_surface_scale(struct yasl_surface *w) } } -static void surface_request_scale(struct yasl_surface *w, uint32_t scale_120) +static void surface_request_scale(struct wayland_surface *w, uint32_t scale_120) { if (!scale_120 || scale_120 == w->pending_scale_120) return; w->pending_scale_120 = scale_120; w->size_pending = true; - yasl_surface_schedule_redraw(w); + wayland_surface_schedule_redraw(w); } -int apply_pending_geometry(struct yasl_surface *w) +int apply_pending_geometry(struct wayland_surface *w) { if (!w->size_pending) return 0; if (w->parent && w->pending_scale_120 == w->scale_120) { @@ -183,7 +183,7 @@ int apply_pending_geometry(struct yasl_surface *w) } publish_surface_scale(w); if (w->cbs->configure && (resized || rescaled)) - w->cbs->configure(w, (struct yasl_geometry){ + w->cbs->configure(&w->base, (struct yasl_geometry){ .size = { .x = width, .y = height }, .scale_120 = w->scale_120, }, w->ud); return 0; @@ -200,7 +200,7 @@ static const struct wp_fractional_scale_v1_listener frac_scale_listener = { .preferred_scale = fractional_preferred_scale, }; -static void surface_follow_refresh(struct yasl_surface *w) +static void surface_follow_refresh(struct wayland_surface *w) { uint32_t refresh = 0; for (const struct wl_conn_output *o = w->conn->outputs; o; o = o->next) @@ -213,7 +213,7 @@ static void surface_follow_refresh(struct yasl_surface *w) w->clock.refresh_ns = refresh; } -void surface_follow_outputs(struct yasl_surface *w) +void surface_follow_outputs(struct wayland_surface *w) { surface_follow_refresh(w); if (!w->frac_scale && !surface_gets_preferred_scale(w->surface)) @@ -223,7 +223,7 @@ void surface_follow_outputs(struct yasl_surface *w) static void surface_enter(void *data, struct wl_surface *surface, struct wl_output *output) { (void)surface; - struct yasl_surface *w = data; + struct wayland_surface *w = data; w->outputs |= conn_output_mask(w->conn, output); surface_follow_outputs(w); } @@ -231,7 +231,7 @@ static void surface_enter(void *data, struct wl_surface *surface, struct wl_outp static void surface_leave(void *data, struct wl_surface *surface, struct wl_output *output) { (void)surface; - struct yasl_surface *w = data; + struct wayland_surface *w = data; w->outputs &= ~conn_output_mask(w->conn, output); surface_follow_outputs(w); } @@ -239,7 +239,7 @@ static void surface_leave(void *data, struct wl_surface *surface, struct wl_outp static void surface_preferred_scale(void *data, struct wl_surface *surface, int32_t factor) { (void)surface; - struct yasl_surface *w = data; + struct wayland_surface *w = data; if (w->frac_scale || w->parent || factor <= 0) return; surface_request_scale(w, (uint32_t)factor * YASL_SCALE_UNIT); @@ -264,7 +264,7 @@ static void buffer_release(void *data, struct wl_buffer *buffer) struct buffer *b = data; b->busy = false; if (b->owner && b->owner->want_draw) - yasl_surface_schedule_redraw(b->owner); + wayland_surface_schedule_redraw(b->owner); } static const struct wl_buffer_listener buffer_listener = { @@ -273,7 +273,7 @@ static const struct wl_buffer_listener buffer_listener = { static void frame_done(void *data, struct wl_callback *cb, uint32_t ms) { - struct yasl_surface *w = data; + struct wayland_surface *w = data; wl_callback_destroy(cb); w->frame_cb = NULL; @@ -282,14 +282,14 @@ static void frame_done(void *data, struct wl_callback *cb, uint32_t ms) yasl_clock_presented(&w->clock, yasl_clock_extend_ms(ms, now), w->sample_refresh); if (w->want_draw) - yasl_surface_schedule_redraw(w); + wayland_surface_schedule_redraw(w); } static const struct wl_callback_listener frame_listener = { .done = frame_done, }; -static void arm_frame(struct yasl_surface *w) +static void arm_frame(struct wayland_surface *w) { if (!w->configured || w->closed || w->frame_cb) return; @@ -315,7 +315,7 @@ int buffer_pick_free_index(const struct buffer *bufs, int cur) } struct latency_feedback { - struct yasl_surface *window; + struct wayland_surface *window; struct wp_presentation_feedback *proxy; struct latency_feedback *next; }; @@ -329,7 +329,7 @@ static void feedback_destroy(struct latency_feedback *feedback) free(feedback); } -void surface_feedback_destroy(struct yasl_surface *w) +void surface_feedback_destroy(struct wayland_surface *w) { while (w->latency_feedbacks) feedback_destroy(w->latency_feedbacks); } @@ -346,7 +346,7 @@ static void presented(void *data, struct wp_presentation_feedback *fb, { (void)seq_hi; (void)seq_lo; (void)flags; struct latency_feedback *feedback = data; - struct yasl_surface *w = feedback->window; + struct wayland_surface *w = feedback->window; if (w->conn->pres_clock_known) { uint64_t ns = conn_pres_to_mono(w->conn, (((uint64_t)tv_sec_hi << 32) | tv_sec_lo) * 1000000000ull + tv_nsec); @@ -371,7 +371,7 @@ static const struct wp_presentation_feedback_listener feedback_listener = { .discarded = discarded, }; -void surface_request_presentation_feedback(struct yasl_surface *w) +void surface_request_presentation_feedback(struct wayland_surface *w) { if (!w || !w->conn->presentation) return; @@ -389,7 +389,7 @@ void surface_request_presentation_feedback(struct yasl_surface *w) wp_presentation_feedback_add_listener(fb, &feedback_listener, feedback); } -static void commit_buffer(struct yasl_surface *w, int idx) +static void commit_buffer(struct wayland_surface *w, int idx) { wl_surface_attach(w->surface, w->buffers[idx].proxy, 0, 0); @@ -432,7 +432,7 @@ static void commit_buffer(struct yasl_surface *w, int idx) w->buffers[idx].presented = true; } -bool yasl_surface_present(struct yasl_surface *w, const struct yasl_framebuffer *fb, +bool wayland_surface_present(struct wayland_surface *w, const struct yasl_framebuffer *fb, const struct yasl_damage_list *damage) { if (!w || w->closed || !w->frame_active || w->frame_presented || !fb || @@ -450,7 +450,7 @@ bool yasl_surface_present(struct yasl_surface *w, const struct yasl_framebuffer return true; } -void surface_draw_frame(struct yasl_surface *w) +void surface_draw_frame(struct wayland_surface *w) { if (w->closed || !w->configured) return; @@ -477,7 +477,7 @@ void surface_draw_frame(struct yasl_surface *w) memset(w->buffers[idx].pixels, 0, (size_t)w->width * (size_t)w->height * sizeof(uint32_t)); w->damage = (struct yasl_damage_list){0}; w->frame = (struct yasl_frame){ - .surface = w, + .surface = &w->base, .target = { .pixels = w->buffers[idx].pixels, .size = { .x = w->width, .y = w->height }, @@ -490,13 +490,13 @@ void surface_draw_frame(struct yasl_surface *w) }; w->frame_active = true; w->frame_presented = false; - w->cbs->draw(w, &w->frame, w->ud); + w->cbs->draw(&w->base, &w->frame, w->ud); w->frame_active = false; if (!w->closed && !w->frame_presented) w->frames_skipped++; } -int surface_init_scaling(struct yasl_surface *w) +int surface_init_scaling(struct wayland_surface *w) { wl_surface_add_listener(w->surface, &surface_listener, w); if (w->conn->viewporter) { @@ -512,7 +512,7 @@ int surface_init_scaling(struct yasl_surface *w) return 0; } -void yasl_surface_set_title(struct yasl_surface *w, const char *title) +void wayland_surface_set_title(struct wayland_surface *w, const char *title) { if (!w || !w->toplevel || !title) return; char *copy = yasl_utf8_sanitize_dup(title); @@ -521,7 +521,7 @@ void yasl_surface_set_title(struct yasl_surface *w, const char *title) free(copy); } -uint32_t yasl_surface_state(const struct yasl_surface *w) +uint32_t wayland_surface_state(const struct wayland_surface *w) { if (!w) return 0; uint32_t s = 0; @@ -533,7 +533,7 @@ uint32_t yasl_surface_state(const struct yasl_surface *w) return s; } -struct yasl_timing yasl_surface_timing(const struct yasl_surface *w) +struct yasl_timing wayland_surface_timing(const struct wayland_surface *w) { struct yasl_timing t = { .refresh_ns = w->clock.refresh_ns, @@ -545,7 +545,7 @@ struct yasl_timing yasl_surface_timing(const struct yasl_surface *w) return t; } -uint64_t yasl_surface_frame_time_ns(struct yasl_surface *w, uint64_t now) +uint64_t wayland_surface_frame_time_ns(struct wayland_surface *w, uint64_t now) { return yasl_clock_frame_timestamp_ns(&w->clock, now); } diff --git a/src/wayland/drag.c b/src/wayland/drag.c index 107ec52..7c15002 100644 --- a/src/wayland/drag.c +++ b/src/wayland/drag.c @@ -11,7 +11,7 @@ static void icon_clear(struct drag_icon *icon) *icon = (struct drag_icon){0}; } -static bool icon_create(struct yasl_display *c, const struct yasl_drag_image *image) +static bool icon_create(struct wayland_display *c, const struct yasl_drag_image *image) { struct drag_icon *icon = &c->drag.icon; size_t bytes; @@ -48,7 +48,7 @@ static bool icon_create(struct yasl_display *c, const struct yasl_drag_image *im return true; } -static void icon_show(struct yasl_display *c, const struct yasl_drag_image *image) +static void icon_show(struct wayland_display *c, const struct yasl_drag_image *image) { struct drag_icon *icon = &c->drag.icon; uint32_t scale = c->drag.scale_120; @@ -73,7 +73,7 @@ static void icon_show(struct yasl_display *c, const struct yasl_drag_image *imag wl_surface_commit(icon->surface); } -static void drag_clear(struct yasl_display *c, bool ended) +static void drag_clear(struct wayland_display *c, bool ended) { struct conn_drag *d = &c->drag; if (d->toplevel) { @@ -88,7 +88,7 @@ static void drag_clear(struct yasl_display *c, bool ended) *d = (struct conn_drag){0}; } -static void drag_finish(struct yasl_display *c, enum yasl_drag_end how, bool ended) +static void drag_finish(struct wayland_display *c, enum yasl_drag_end how, bool ended) { const struct yasl_drag_listener *l = c->drag.listener; void *ud = c->drag.ud; @@ -97,7 +97,7 @@ static void drag_finish(struct yasl_display *c, enum yasl_drag_end how, bool end l->end(ud, how); } -void conn_drag_end(struct yasl_display *c) +void conn_drag_end(struct wayland_display *c) { if (c && c->drag.source) drag_finish(c, YASL_DRAG_CANCELLED, false); @@ -110,7 +110,7 @@ static void source_action(void *ud, struct wl_data_source *s, uint32_t action) { static void source_cancelled(void *ud, struct wl_data_source *s) { (void)s; - struct yasl_display *c = ud; + struct wayland_display *c = ud; bool released = c->drag.dropped || !c->drag.carrying; drag_finish(c, released ? YASL_DRAG_DROPPED : YASL_DRAG_CANCELLED, true); } @@ -118,7 +118,7 @@ static void source_cancelled(void *ud, struct wl_data_source *s) static void source_dropped(void *ud, struct wl_data_source *s) { (void)s; - ((struct yasl_display *)ud)->drag.dropped = true; + ((struct wayland_display *)ud)->drag.dropped = true; } static void source_finished(void *ud, struct wl_data_source *s) @@ -136,9 +136,9 @@ static const struct wl_data_source_listener source_listener = { .action = source_action, }; -static struct seat *drag_seat(struct yasl_display *c, struct yasl_surface *origin) +static struct seat *drag_seat(struct wayland_display *c, struct wayland_surface *origin) { - struct yasl_surface *root = surface_root(origin); + struct wayland_surface *root = surface_root(origin); struct seat *active = conn_active_seat(c); if (active && active->press_serial && surface_root(active->press_surface) == root) @@ -149,7 +149,7 @@ static struct seat *drag_seat(struct yasl_display *c, struct yasl_surface *origi return NULL; } -bool yasl_display_drag_start(struct yasl_display *c, struct yasl_surface *origin, +bool wayland_display_drag_start(struct wayland_display *c, struct wayland_surface *origin, const struct yasl_drag_image *image, const struct yasl_drag_listener *l, void *ud) { @@ -187,15 +187,15 @@ bool yasl_display_drag_start(struct yasl_display *c, struct yasl_surface *origin return true; } -void yasl_display_drag_cancel(struct yasl_display *c) +void wayland_display_drag_cancel(struct wayland_display *c) { if (c && c->drag.source) drag_clear(c, false); } -bool yasl_surface_drag_carry(struct yasl_surface *w, struct yasl_vector2i offset) +bool wayland_surface_drag_carry(struct wayland_surface *w, struct yasl_vector2i offset) { - struct yasl_display *c = w ? w->conn : NULL; + struct wayland_display *c = w ? w->conn : NULL; if (!c || !c->drag.source || !c->drag.toplevel || !w->toplevel) return false; uint32_t scale = c->drag.scale_120; @@ -206,14 +206,14 @@ bool yasl_surface_drag_carry(struct yasl_surface *w, struct yasl_vector2i offset return true; } -bool yasl_surface_place_under_pointer(struct yasl_surface *w, struct yasl_vector2i at) +bool wayland_surface_place_under_pointer(struct wayland_surface *w, struct yasl_vector2i at) { (void)w; (void)at; return false; } -static struct yasl_vector2i drag_device_pos(const struct yasl_surface *w, double x, double y) +static struct yasl_vector2i drag_device_pos(const struct wayland_surface *w, double x, double y) { double dx = x * w->scale_factor, dy = y * w->scale_factor; return (struct yasl_vector2i){ @@ -222,20 +222,20 @@ static struct yasl_vector2i drag_device_pos(const struct yasl_surface *w, double }; } -void conn_drop_target(struct yasl_display *c, enum yasl_drop_event ev, +void conn_drop_target(struct wayland_display *c, enum yasl_drop_event ev, struct wl_surface *on, double x, double y) { - struct yasl_surface *w = conn_surface_for(c, on); + struct wayland_surface *w = conn_surface_for(c, on); if (!w || w->parent || !w->cbs->drop) return; - w->cbs->drop(w, ev, drag_device_pos(w, x, y), w->ud); + w->cbs->drop(&w->base, ev, drag_device_pos(w, x, y), w->ud); } -void conn_drag_target(struct yasl_display *c, enum yasl_drag_event ev, +void conn_drag_target(struct wayland_display *c, enum yasl_drag_event ev, struct wl_surface *on, double x, double y) { - struct yasl_surface *w = conn_surface_for(c, on); + struct wayland_surface *w = conn_surface_for(c, on); if (!w || w->parent || !w->cbs->drag) return; - w->cbs->drag(w, ev, drag_device_pos(w, x, y), w->ud); + w->cbs->drag(&w->base, ev, drag_device_pos(w, x, y), w->ud); } diff --git a/src/wayland/ime.c b/src/wayland/ime.c index 9da019c..74db53f 100644 --- a/src/wayland/ime.c +++ b/src/wayland/ime.c @@ -2,7 +2,7 @@ #define IME_TEXT_MAX (256u * 1024) -static void show_preedit(struct yasl_surface *w, char *text, int begin, int end) +static void show_preedit(struct wayland_surface *w, char *text, int begin, int end) { bool changed = (text || w->ime_shown) && (!text || !w->ime_shown || strcmp(text, w->ime_shown) || @@ -12,10 +12,10 @@ static void show_preedit(struct yasl_surface *w, char *text, int begin, int end) w->ime_shown_begin = begin; w->ime_shown_end = end; if (changed) - yasl_surface_schedule_redraw(w); + wayland_surface_schedule_redraw(w); } -void ime_reset(struct yasl_surface *w) +void ime_reset(struct wayland_surface *w) { free(w->ime_preedit); w->ime_preedit = NULL; @@ -24,7 +24,7 @@ void ime_reset(struct yasl_surface *w) show_preedit(w, NULL, -1, -1); } -void ime_send_caret(struct yasl_surface *w) +void ime_send_caret(struct wayland_surface *w) { struct seat *seat = w->ime_seat; if (!seat || !seat->text_input) @@ -38,12 +38,12 @@ void ime_send_caret(struct yasl_surface *w) zwp_text_input_v3_commit(seat->text_input); } -const char *yasl_surface_ime_preedit(const struct yasl_surface *w) +const char *wayland_surface_ime_preedit(const struct wayland_surface *w) { return w ? w->ime_shown : NULL; } -bool yasl_surface_ime_preedit_cursor(const struct yasl_surface *w, int *begin, int *end) +bool wayland_surface_ime_preedit_cursor(const struct wayland_surface *w, int *begin, int *end) { if (!w || !w->ime_shown || w->ime_shown_begin < 0) return false; @@ -52,7 +52,7 @@ bool yasl_surface_ime_preedit_cursor(const struct yasl_surface *w, int *begin, i return true; } -void yasl_surface_set_ime_caret(struct yasl_surface *w, struct yasl_rect2i caret) +void wayland_surface_set_ime_caret(struct wayland_surface *w, struct yasl_rect2i caret) { if (!w || (w->ime_caret.x == caret.x && w->ime_caret.y == caret.y && w->ime_caret.width == caret.width && w->ime_caret.height == caret.height)) @@ -65,7 +65,7 @@ static void text_input_enter(void *data, struct zwp_text_input_v3 *text_input, struct wl_surface *surface) { struct seat *s = data; - struct yasl_surface *w = surface_root(conn_surface_for(s->conn, surface)); + struct wayland_surface *w = surface_root(conn_surface_for(s->conn, surface)); s->ime_focus = w; if (!w) return; zwp_text_input_v3_enable(text_input); @@ -81,7 +81,7 @@ static void text_input_leave(void *data, struct zwp_text_input_v3 *text_input, { (void)surface; struct seat *s = data; - struct yasl_surface *w = s->ime_focus; + struct wayland_surface *w = s->ime_focus; s->ime_focus = NULL; if (!w) return; zwp_text_input_v3_disable(text_input); @@ -96,7 +96,7 @@ static void text_input_preedit_string(void *data, int32_t cursor_begin, int32_t cursor_end) { (void)text_input; - struct yasl_surface *w = ((struct seat *)data)->ime_focus; + struct wayland_surface *w = ((struct seat *)data)->ime_focus; if (!w) return; free(w->ime_preedit); w->ime_preedit = text ? strndup(text, IME_TEXT_MAX) : NULL; @@ -111,7 +111,7 @@ static void text_input_commit_string(void *data, struct zwp_text_input_v3 *text_input, const char *text) { (void)text_input; - struct yasl_surface *w = ((struct seat *)data)->ime_focus; + struct wayland_surface *w = ((struct seat *)data)->ime_focus; if (!w) return; free(w->ime_commit); w->ime_commit = (text && text[0]) ? strndup(text, IME_TEXT_MAX) : NULL; @@ -128,12 +128,12 @@ static void text_input_done(void *data, struct zwp_text_input_v3 *text_input, uint32_t serial) { (void)text_input; (void)serial; - struct yasl_surface *w = ((struct seat *)data)->ime_focus; + struct wayland_surface *w = ((struct seat *)data)->ime_focus; if (!w) return; if (w->ime_commit) { struct yasl_input_event ev = { .kind = YASL_INPUT_TEXT, - .surface = w, + .surface = &w->base, .text = { .text = w->ime_commit, .len = strlen(w->ime_commit) }, }; input_queue_push(&w->input, &ev); diff --git a/src/wayland/internal.h b/src/wayland/internal.h index dc85943..6b9001f 100644 --- a/src/wayland/internal.h +++ b/src/wayland/internal.h @@ -3,7 +3,7 @@ #define _GNU_SOURCE -#include "yasl/yasl.h" +#include "api.h" #include "yasl/timing.h" #include "clipboard.h" @@ -41,13 +41,13 @@ #define NBUF 3 -struct yasl_surface; +struct wayland_surface; struct tablet_tool; struct tablet_device; struct tablet_pad; struct buffer { - struct yasl_surface *owner; + struct wayland_surface *owner; struct wl_shm_pool *pool; struct wl_buffer *proxy; uint32_t *pixels; @@ -58,7 +58,7 @@ struct buffer { }; struct wl_conn_output { - struct yasl_display *conn; + struct wayland_display *conn; struct wl_output *proxy; struct wl_conn_output *next; uint64_t mask; @@ -68,7 +68,7 @@ struct wl_conn_output { }; struct pending_press { - struct yasl_surface *surface; + struct wayland_surface *surface; struct yasl_vector2i pos; int button; bool release; @@ -104,7 +104,7 @@ struct conn_drag { #define SEAT_TOUCH_POINTS 10 struct touch_point { - struct yasl_surface *surface; + struct wayland_surface *surface; int32_t id; double x, y; bool active; @@ -112,7 +112,7 @@ struct touch_point { }; struct seat { - struct yasl_display *conn; + struct wayland_display *conn; struct seat *next; struct wl_seat *proxy; uint32_t name; @@ -140,14 +140,14 @@ struct seat { uint32_t repeat_key; bool repeat_held; - struct yasl_surface *pointer_focus; - struct yasl_surface *keyboard_focus; - struct yasl_surface *tablet_focus; - struct yasl_surface *ime_focus; - struct yasl_surface *focus_leaving; + struct wayland_surface *pointer_focus; + struct wayland_surface *keyboard_focus; + struct wayland_surface *tablet_focus; + struct wayland_surface *ime_focus; + struct wayland_surface *focus_leaving; uint32_t input_serial; uint32_t press_serial; - struct yasl_surface *press_surface; + struct wayland_surface *press_surface; uint32_t pointer_serial; int cursor_shape; @@ -161,7 +161,7 @@ struct seat { bool axis_smooth; bool motion_pending; struct press_queue ptr_presses; - struct yasl_surface *ptr_left; + struct wayland_surface *ptr_left; struct yasl_vector2i ptr_left_pos; bool ptr_left_motion; @@ -210,7 +210,8 @@ static inline void input_queue_clear(struct input_queue *q) q->head = q->tail = q->count = 0; } -struct yasl_display { +struct wayland_display { + struct yasl_display base; struct wl_display *wl; struct wl_registry *registry; struct wl_compositor *compositor; @@ -244,20 +245,21 @@ struct yasl_display { bool pres_clock_known; struct xdg_toplevel_drag_manager_v1 *toplevel_drag_mgr; struct conn_drag drag; - struct yasl_surface *surfaces; - struct yasl_surface *paste_target; + struct wayland_surface *surfaces; + struct wayland_surface *paste_target; int watch; int depth; - struct yasl_surface *dead; + struct wayland_surface *dead; bool reading; bool lost; }; -struct yasl_surface { - struct yasl_display *conn; - struct yasl_surface *conn_next; - struct yasl_surface *dead_next; - struct yasl_surface *parent; +struct wayland_surface { + struct yasl_surface base; + struct wayland_display *conn; + struct wayland_surface *conn_next; + struct wayland_surface *dead_next; + struct wayland_surface *parent; struct wl_surface *surface; struct xdg_surface *xdg_surface; struct xdg_toplevel *toplevel; @@ -325,12 +327,12 @@ bool yasl_utf8_validate(const char *str, size_t len); int device_px(int logical, uint32_t scale_120); bool buffer_size(int width, int height, size_t *bytes); -int buffer_reserve(struct yasl_surface *w, struct buffer *b, size_t needed); +int buffer_reserve(struct wayland_surface *w, struct buffer *b, size_t needed); void buffer_destroy(struct buffer *b); int buffer_pick_free_index(const struct buffer *bufs, int cur); -int buffer_prepare(struct yasl_surface *w, struct buffer *b, int width, int height, +int buffer_prepare(struct wayland_surface *w, struct buffer *b, int width, int height, const struct wl_buffer_listener *listener); -int apply_pending_geometry(struct yasl_surface *w); +int apply_pending_geometry(struct wayland_surface *w); int logical_px_ceil(int device, uint32_t scale_120); static inline void wl_damage_list(struct wl_surface *s, const struct yasl_damage_list *d) @@ -345,8 +347,8 @@ static inline void wl_damage_list(struct wl_surface *s, const struct yasl_damage d->rects[i].width, d->rects[i].height); } -void surface_update_opaque_region(struct yasl_surface *w); -int surface_init_scaling(struct yasl_surface *w); +void surface_update_opaque_region(struct wayland_surface *w); +int surface_init_scaling(struct wayland_surface *w); static inline bool surface_gets_preferred_scale(struct wl_surface *s) { @@ -354,65 +356,65 @@ static inline bool surface_gets_preferred_scale(struct wl_surface *s) WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION; } -uint64_t conn_output_mask(const struct yasl_display *c, const struct wl_output *proxy); -uint32_t conn_output_scale_120(const struct yasl_display *c, uint64_t outputs); -void conn_destroy_outputs(struct yasl_display *c); -void surface_follow_outputs(struct yasl_surface *w); -uint64_t conn_pres_to_mono(const struct yasl_display *c, uint64_t ns); -uint64_t conn_mono_to_pres(const struct yasl_display *c, uint64_t ns); -void surface_draw_frame(struct yasl_surface *w); -void surface_fail(struct yasl_surface *w); -void display_enter(struct yasl_display *c); -void display_leave(struct yasl_display *c); +uint64_t conn_output_mask(const struct wayland_display *c, const struct wl_output *proxy); +uint32_t conn_output_scale_120(const struct wayland_display *c, uint64_t outputs); +void conn_destroy_outputs(struct wayland_display *c); +void surface_follow_outputs(struct wayland_surface *w); +uint64_t conn_pres_to_mono(const struct wayland_display *c, uint64_t ns); +uint64_t conn_mono_to_pres(const struct wayland_display *c, uint64_t ns); +void surface_draw_frame(struct wayland_surface *w); +void surface_fail(struct wayland_surface *w); +void display_enter(struct wayland_display *c); +void display_leave(struct wayland_display *c); -struct yasl_surface *conn_surface_for(struct yasl_display *c, struct wl_surface *s); +struct wayland_surface *conn_surface_for(struct wayland_display *c, struct wl_surface *s); -static inline struct yasl_surface *surface_root(struct yasl_surface *w) +static inline struct wayland_surface *surface_root(struct wayland_surface *w) { while (w && w->parent) w = w->parent; return w; } -void conn_drag_end(struct yasl_display *c); -void conn_drop_target(struct yasl_display *c, enum yasl_drop_event ev, +void conn_drag_end(struct wayland_display *c); +void conn_drop_target(struct wayland_display *c, enum yasl_drop_event ev, struct wl_surface *on, double x, double y); -void conn_drag_target(struct yasl_display *c, enum yasl_drag_event ev, +void conn_drag_target(struct wayland_display *c, enum yasl_drag_event ev, struct wl_surface *on, double x, double y); -void seat_bind(struct yasl_display *c, struct wl_registry *registry, +void seat_bind(struct wayland_display *c, struct wl_registry *registry, uint32_t name, uint32_t version); -bool seat_remove(struct yasl_display *c, uint32_t name); +bool seat_remove(struct wayland_display *c, uint32_t name); void seat_destroy(struct seat *s); -struct seat *conn_active_seat(const struct yasl_display *c); +struct seat *conn_active_seat(const struct wayland_display *c); void seat_note_input(struct seat *s, uint32_t serial); -void seat_forget_surface(struct seat *s, struct yasl_surface *w); +void seat_forget_surface(struct seat *s, struct wayland_surface *w); void seat_clipboard_ensure(struct seat *s); -int display_input_init(struct yasl_display *c); +int display_input_init(struct wayland_display *c); extern const struct wl_seat_listener seat_listener; void keyboard_repeat_stop(struct seat *s); void keyboard_repeat_sync(struct seat *s); int keyboard_repeat_init(struct seat *s); -void keyboard_settle_focus(struct yasl_display *c); -bool conn_keyboard_on(const struct yasl_display *c, const struct yasl_surface *w, +void keyboard_settle_focus(struct wayland_display *c); +bool conn_keyboard_on(const struct wayland_display *c, const struct wayland_surface *w, const struct seat *except); extern const struct wl_keyboard_listener keyboard_listener; int seat_modifiers(const struct seat *s); -void surface_init_cursor(struct yasl_display *c); +void surface_init_cursor(struct wayland_display *c); void seat_destroy_tablet(struct seat *s); void seat_pointer_reset(struct seat *s); extern const struct zwp_text_input_v3_listener text_input_listener; -void ime_reset(struct yasl_surface *w); -void ime_send_caret(struct yasl_surface *w); +void ime_reset(struct wayland_surface *w); +void ime_send_caret(struct wayland_surface *w); -int surface_init_shell(struct yasl_surface *w, const char *title); +int surface_init_shell(struct wayland_surface *w, const char *title); extern const struct xdg_surface_listener shell_surface_listener; extern const struct wl_registry_listener registry_listener; -void surface_feedback_destroy(struct yasl_surface *w); -void surface_request_presentation_feedback(struct yasl_surface *w); +void surface_feedback_destroy(struct wayland_surface *w); +void surface_request_presentation_feedback(struct wayland_surface *w); #endif diff --git a/src/wayland/keyboard.c b/src/wayland/keyboard.c index 7283d58..1746ac6 100644 --- a/src/wayland/keyboard.c +++ b/src/wayland/keyboard.c @@ -112,13 +112,13 @@ static void keyboard_keymap(void *data, struct wl_keyboard *keyboard, } } -static void keyboard_reset_keys(struct seat *s, struct yasl_surface *w) +static void keyboard_reset_keys(struct seat *s, struct wayland_surface *w) { (void)w; keyboard_repeat_stop(s); } -bool conn_keyboard_on(const struct yasl_display *c, const struct yasl_surface *w, +bool conn_keyboard_on(const struct wayland_display *c, const struct wayland_surface *w, const struct seat *except) { for (const struct seat *s = c->seats; s; s = s->next) @@ -127,11 +127,11 @@ bool conn_keyboard_on(const struct yasl_display *c, const struct yasl_surface *w return false; } -static void push_focus(struct yasl_surface *w, bool focused) +static void push_focus(struct wayland_surface *w, bool focused) { struct yasl_input_event ev = { .kind = YASL_INPUT_FOCUS, - .surface = w, + .surface = &w->base, .focus = { .focused = focused }, }; input_queue_push(&w->input, &ev); @@ -142,7 +142,7 @@ static void keyboard_enter(void *data, struct wl_keyboard *keyboard, { (void)keyboard; (void)serial; (void)keys; struct seat *s = data; - struct yasl_surface *w = surface_root(conn_surface_for(s->conn, surface)); + struct wayland_surface *w = surface_root(conn_surface_for(s->conn, surface)); bool same_window = w && w == s->focus_leaving; if (same_window) s->focus_leaving = NULL; @@ -159,7 +159,7 @@ static void keyboard_enter(void *data, struct wl_keyboard *keyboard, static void seat_settle_focus(struct seat *s) { - struct yasl_surface *w = s->focus_leaving; + struct wayland_surface *w = s->focus_leaving; if (!w) return; s->focus_leaving = NULL; @@ -167,7 +167,7 @@ static void seat_settle_focus(struct seat *s) push_focus(w, false); } -void keyboard_settle_focus(struct yasl_display *c) +void keyboard_settle_focus(struct wayland_display *c) { for (struct seat *s = c->seats; s; s = s->next) seat_settle_focus(s); @@ -178,7 +178,7 @@ static void keyboard_leave(void *data, struct wl_keyboard *keyboard, { (void)keyboard; (void)serial; (void)surface; struct seat *s = data; - struct yasl_surface *w = s->keyboard_focus; + struct wayland_surface *w = s->keyboard_focus; s->keyboard_focus = NULL; if (!w) return; keyboard_reset_keys(s, w); @@ -231,7 +231,7 @@ static struct yasl_key_event xkb_key_event(struct seat *s, uint32_t key, return k; } -static bool keyboard_dispatch_key(struct seat *s, struct yasl_surface *w, uint32_t key, +static bool keyboard_dispatch_key(struct seat *s, struct wayland_surface *w, uint32_t key, enum yasl_key_action action) { if (!s->xkb_state || !w->cbs) return false; @@ -239,7 +239,7 @@ static bool keyboard_dispatch_key(struct seat *s, struct yasl_surface *w, uint32 struct yasl_key_event k = xkb_key_event(s, key, action); struct yasl_input_event ev = { .kind = YASL_INPUT_KEY, - .surface = w, + .surface = &w->base, .key = k, }; input_queue_push(&w->input, &ev); @@ -251,7 +251,7 @@ static void keyboard_key(void *data, struct wl_keyboard *keyboard, { (void)keyboard; (void)time; struct seat *s = data; - struct yasl_surface *w = s->keyboard_focus; + struct wayland_surface *w = s->keyboard_focus; if (!w) return; seat_note_input(s, serial); if (state == WL_KEYBOARD_KEY_STATE_RELEASED) { @@ -276,7 +276,7 @@ static int keyboard_repeat_ready(int fd, short revents, void *data) uint64_t count; if (!(revents & POLLIN) || read(fd, &count, sizeof(count)) != sizeof(count)) return 0; - struct yasl_surface *w = s->keyboard_focus; + struct wayland_surface *w = s->keyboard_focus; if (!w) { keyboard_repeat_stop(s); return 0; @@ -355,12 +355,12 @@ int seat_modifiers(const struct seat *s) return mods; } -int yasl_display_modifiers(const struct yasl_display *c) +int wayland_display_modifiers(const struct wayland_display *c) { return c ? seat_modifiers(conn_active_seat(c)) : 0; } -void yasl_display_copy(struct yasl_display *c, enum yasl_selection sel, const char *text) +void wayland_display_copy(struct wayland_display *c, enum yasl_selection sel, const char *text) { struct seat *s = c ? conn_active_seat(c) : NULL; if (!s || !s->clipboard) @@ -371,7 +371,7 @@ void yasl_display_copy(struct yasl_display *c, enum yasl_selection sel, const ch clipboard_copy(s->clipboard, text, s->input_serial); } -void yasl_display_paste(struct yasl_display *c, enum yasl_selection sel, struct yasl_surface *target) +void wayland_display_paste(struct wayland_display *c, enum yasl_selection sel, struct wayland_surface *target) { struct seat *s = c ? conn_active_seat(c) : NULL; if (!s || !s->clipboard) @@ -383,7 +383,7 @@ void yasl_display_paste(struct yasl_display *c, enum yasl_selection sel, struct clipboard_paste(s->clipboard); } -bool yasl_surface_input_poll(struct yasl_surface *w, struct yasl_input_event *ev) +bool wayland_surface_input_poll(struct wayland_surface *w, struct yasl_input_event *ev) { return w && ev && input_queue_pop(&w->input, ev); } diff --git a/src/wayland/meson.build b/src/wayland/meson.build new file mode 100644 index 0000000..ca83755 --- /dev/null +++ b/src/wayland/meson.build @@ -0,0 +1,13 @@ +subdir('protocols') +sources += wl_protocol_src + files( + 'backend.c', + 'buffer.c', + 'clipboard.c', + 'drag.c', + 'ime.c', + 'keyboard.c', + 'pointer.c', + 'popup.c', + 'shell.c', + 'surface.c', +) diff --git a/src/wayland/pointer.c b/src/wayland/pointer.c index 37e72f3..f2902b4 100644 --- a/src/wayland/pointer.c +++ b/src/wayland/pointer.c @@ -30,7 +30,7 @@ static int device_px_round(double v) return (int)(v < 0 ? v - 0.5 : v + 0.5); } -static struct yasl_vector2i device_from_fixed(const struct yasl_surface *w, +static struct yasl_vector2i device_from_fixed(const struct wayland_surface *w, wl_fixed_t x, wl_fixed_t y) { return (struct yasl_vector2i){ @@ -53,23 +53,23 @@ void seat_note_input(struct seat *s, uint32_t serial) s->conn->active_seat = s; } -struct seat *conn_active_seat(const struct yasl_display *c) +struct seat *conn_active_seat(const struct wayland_display *c) { return c->active_seat ? c->active_seat : c->seats; } -static void push_mouse(struct seat *s, struct yasl_surface *w, struct yasl_mouse_event ev) +static void push_mouse(struct seat *s, struct wayland_surface *w, struct yasl_mouse_event ev) { ev.mods = (unsigned)seat_modifiers(s); struct yasl_input_event input_ev = { .kind = YASL_INPUT_MOUSE, - .surface = w, + .surface = &w->base, .mouse = ev, }; input_queue_push(&surface_root(w)->input, &input_ev); } -static void report_mouse(struct seat *s, struct yasl_surface *w, struct yasl_vector2i pos, +static void report_mouse(struct seat *s, struct wayland_surface *w, struct yasl_vector2i pos, int button, enum yasl_pointer_kind kind) { push_mouse(s, w, (struct yasl_mouse_event){ @@ -77,7 +77,7 @@ static void report_mouse(struct seat *s, struct yasl_surface *w, struct yasl_vec }); } -static void report_wheel(struct seat *s, struct yasl_surface *w, struct yasl_vector2i pos, +static void report_wheel(struct seat *s, struct wayland_surface *w, struct yasl_vector2i pos, int button, int lines, float delta) { push_mouse(s, w, (struct yasl_mouse_event){ @@ -86,7 +86,7 @@ static void report_wheel(struct seat *s, struct yasl_surface *w, struct yasl_vec }); } -static bool cursor_theme_for(struct yasl_display *c, int scale) +static bool cursor_theme_for(struct wayland_display *c, int scale) { if (c->cursor_theme && c->cursor_theme_scale == scale) return true; @@ -103,7 +103,7 @@ static bool cursor_theme_for(struct yasl_display *c, int scale) static bool seat_cursor_surface(struct seat *s) { - struct yasl_display *c = s->conn; + struct wayland_display *c = s->conn; if (s->cursor_surface) return true; s->cursor_surface = wl_compositor_create_surface(c->compositor); @@ -114,10 +114,10 @@ static bool seat_cursor_surface(struct seat *s) return true; } -static void set_cursor_theme_fallback(struct seat *s, struct yasl_surface *w, +static void set_cursor_theme_fallback(struct seat *s, struct wayland_surface *w, enum yasl_cursor shape) { - struct yasl_display *c = s->conn; + struct wayland_display *c = s->conn; int scale = (int)((w->scale_120 + YASL_SCALE_UNIT - 1) / YASL_SCALE_UNIT); if (scale < 1) scale = 1; if (!seat_cursor_surface(s) || !cursor_theme_for(c, scale)) return; @@ -174,7 +174,7 @@ static uint32_t cursor_shape_of(enum yasl_cursor shape) } } -static void seat_set_cursor(struct seat *s, struct yasl_surface *w, enum yasl_cursor shape) +static void seat_set_cursor(struct seat *s, struct wayland_surface *w, enum yasl_cursor shape) { if (!s->pointer || !s->pointer_serial || s->pointer_focus != w || s->cursor_shape == (int)shape) @@ -200,7 +200,7 @@ static void tool_set_cursor(struct tablet_tool *t, enum yasl_cursor shape) cursor_shape_of(shape)); } -void yasl_surface_set_cursor(struct yasl_surface *w, enum yasl_cursor shape) +void wayland_surface_set_cursor(struct wayland_surface *w, enum yasl_cursor shape) { if (!w || !w->conn) return; for (struct seat *s = w->conn->seats; s; s = s->next) { @@ -211,7 +211,7 @@ void yasl_surface_set_cursor(struct yasl_surface *w, enum yasl_cursor shape) } static void press_queue_flush(struct seat *s, struct press_queue *q, - struct yasl_surface *only, uint64_t *buttons, + struct wayland_surface *only, uint64_t *buttons, struct yasl_mouse_event like) { int kept = 0; @@ -233,7 +233,7 @@ static void press_queue_flush(struct seat *s, struct press_queue *q, q->n = kept; } -static void press_queue_push(struct press_queue *q, struct yasl_surface *w, +static void press_queue_push(struct press_queue *q, struct wayland_surface *w, struct yasl_vector2i pos, int button, bool release) { if (q->n == (int)(sizeof(q->ev) / sizeof(q->ev[0]))) { @@ -245,7 +245,7 @@ static void press_queue_push(struct press_queue *q, struct yasl_surface *w, }; } -static void press_queue_forget(struct press_queue *q, struct yasl_surface *w) +static void press_queue_forget(struct press_queue *q, struct wayland_surface *w) { for (int i = 0; i < q->n; i++) if (q->ev[i].surface == w) @@ -274,7 +274,7 @@ static void pointer_event_done(struct seat *s) pointer_frame(s, s->pointer); } -static void flush_pending_motion(struct seat *s, struct yasl_surface *w) +static void flush_pending_motion(struct seat *s, struct wayland_surface *w) { if (!s->motion_pending) return; s->motion_pending = false; @@ -287,7 +287,7 @@ static void pointer_enter(void *data, struct wl_pointer *pointer, { (void)pointer; struct seat *s = data; - struct yasl_surface *w = conn_surface_for(s->conn, surface); + struct wayland_surface *w = conn_surface_for(s->conn, surface); s->pointer_focus = w; if (!w) return; s->pointer_pos = device_from_fixed(w, surface_x, surface_y); @@ -303,7 +303,7 @@ static void pointer_leave(void *data, struct wl_pointer *pointer, { (void)pointer; (void)serial; (void)surface; struct seat *s = data; - struct yasl_surface *w = s->pointer_focus; + struct wayland_surface *w = s->pointer_focus; s->pointer_focus = NULL; if (!w) return; if (s->ptr_left) @@ -320,7 +320,7 @@ static void pointer_motion(void *data, struct wl_pointer *pointer, { (void)pointer; (void)time; struct seat *s = data; - struct yasl_surface *w = s->pointer_focus; + struct wayland_surface *w = s->pointer_focus; if (!w) return; s->pointer_pos = device_from_fixed(w, surface_x, surface_y); s->motion_pending = true; @@ -332,7 +332,7 @@ static void pointer_button(void *data, struct wl_pointer *pointer, { (void)pointer; (void)time; struct seat *s = data; - struct yasl_surface *w = s->pointer_focus; + struct wayland_surface *w = s->pointer_focus; if (!w) return; int b; switch (button) { @@ -415,7 +415,7 @@ static void pointer_axis_value120(void *data, struct wl_pointer *pointer, uint32 } } -static void pointer_frame_vertical(struct seat *s, struct yasl_surface *w) +static void pointer_frame_vertical(struct seat *s, struct wayland_surface *w) { if (s->axis_has_v120_v) { int discrete = s->axis_value120_v / 120; @@ -438,7 +438,7 @@ static void pointer_frame_vertical(struct seat *s, struct yasl_surface *w) s->frame_v120 = 0; } -static void pointer_frame_horizontal(struct seat *s, struct yasl_surface *w) +static void pointer_frame_horizontal(struct seat *s, struct wayland_surface *w) { if (s->axis_has_v120_h) { int discrete = s->axis_value120_h / 120; @@ -462,7 +462,7 @@ static void pointer_frame(void *data, struct wl_pointer *pointer) { (void)pointer; struct seat *s = data; - struct yasl_surface *left = s->ptr_left; + struct wayland_surface *left = s->ptr_left; if (left) { s->ptr_left = NULL; if (s->ptr_left_motion) @@ -479,7 +479,7 @@ static void pointer_frame(void *data, struct wl_pointer *pointer) s->pointer_serial = serial; s->cursor_shape = shape; } - struct yasl_surface *w = s->pointer_focus; + struct wayland_surface *w = s->pointer_focus; if (!w) { s->ptr_presses.n = 0; return; @@ -547,7 +547,7 @@ static int touch_count(const struct seat *s) return n; } -static double touch_centroid_y(const struct seat *s, const struct yasl_surface *on) +static double touch_centroid_y(const struct seat *s, const struct wayland_surface *on) { double sum = 0; int n = 0; @@ -576,7 +576,7 @@ static void touch_down(void *data, struct wl_touch *touch, { (void)touch; (void)time; struct seat *s = data; - struct yasl_surface *w = conn_surface_for(s->conn, surface); + struct wayland_surface *w = conn_surface_for(s->conn, surface); if (!w || touch_find(s, id)) return; struct touch_point *p = NULL; for (int i = 0; i < SEAT_TOUCH_POINTS && !p; i++) @@ -704,7 +704,7 @@ static void tablet_tool_proximity_in(void *data, (void)tool; (void)tablet; struct tablet_tool *t = data; struct seat *s = t->seat; - struct yasl_surface *w = conn_surface_for(s->conn, surface); + struct wayland_surface *w = conn_surface_for(s->conn, surface); s->tablet_focus = w; s->tool = t; s->tool_buttons = 0; @@ -723,7 +723,7 @@ static void tablet_tool_proximity_out(void *data, struct zwp_tablet_tool_v2 *too (void)tool; struct tablet_tool *t = data; struct seat *s = t->seat; - struct yasl_surface *w = s->tablet_focus; + struct wayland_surface *w = s->tablet_focus; if (w) { press_queue_flush(s, &s->tool_presses, NULL, &s->tool_buttons, tool_event(s)); struct yasl_mouse_event ev = tool_event(s); @@ -743,7 +743,7 @@ static void tablet_tool_down(void *data, struct zwp_tablet_tool_v2 *tool, uint32 { (void)tool; struct seat *s = ((struct tablet_tool *)data)->seat; - struct yasl_surface *w = s->tablet_focus; + struct wayland_surface *w = s->tablet_focus; if (!w) return; seat_note_input(s, serial); s->press_serial = serial; @@ -755,7 +755,7 @@ static void tablet_tool_up(void *data, struct zwp_tablet_tool_v2 *tool) { (void)tool; struct seat *s = ((struct tablet_tool *)data)->seat; - struct yasl_surface *w = s->tablet_focus; + struct wayland_surface *w = s->tablet_focus; if (!w) return; press_queue_push(&s->tool_presses, w, s->tool_pos, 0, true); } @@ -764,7 +764,7 @@ static void tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *tool, wl_f { (void)tool; struct seat *s = ((struct tablet_tool *)data)->seat; - struct yasl_surface *w = s->tablet_focus; + struct wayland_surface *w = s->tablet_focus; if (!w) return; s->tool_pos = device_from_fixed(w, x, y); s->tool_motion = true; @@ -775,7 +775,7 @@ static void tablet_tool_button(void *data, struct zwp_tablet_tool_v2 *tool, { (void)tool; struct seat *s = ((struct tablet_tool *)data)->seat; - struct yasl_surface *w = s->tablet_focus; + struct wayland_surface *w = s->tablet_focus; if (!w) return; seat_note_input(s, serial); int b; @@ -822,7 +822,7 @@ static void tool_frame(void *d, struct zwp_tablet_tool_v2 *t, uint32_t time) { (void)t; (void)time; struct seat *s = ((struct tablet_tool *)d)->seat; - struct yasl_surface *w = s->tablet_focus; + struct wayland_surface *w = s->tablet_focus; if (!w) { s->tool_presses.n = 0; s->tool_wheel = 0; @@ -1003,13 +1003,13 @@ static void clipboard_received(const char *text, size_t len, bool uri_list, struct wl_surface *on, void *ud) { struct seat *s = ud; - struct yasl_display *c = s->conn; - struct yasl_surface *w = on ? surface_root(conn_surface_for(c, on)) : NULL; + struct wayland_display *c = s->conn; + struct wayland_surface *w = on ? surface_root(conn_surface_for(c, on)) : NULL; if (!w) w = c->paste_target ? c->paste_target : s->keyboard_focus; if (!len || !w) return; struct yasl_input_event ev = { - .surface = w, + .surface = &w->base, .kind = uri_list ? YASL_INPUT_DROP_FILES : YASL_INPUT_PASTE, }; if (uri_list) { @@ -1038,7 +1038,7 @@ static void clipboard_drop_hover(enum yasl_drop_event ev, struct wl_surface *on, void seat_clipboard_ensure(struct seat *s) { - struct yasl_display *c = s->conn; + struct wayland_display *c = s->conn; if (!c->data_manager || !s->proxy) return; if (!s->clipboard) { @@ -1082,7 +1082,7 @@ static void seat_drop_keyboard(struct seat *s) s->keyboard = NULL; } s->repeat_rate = 0; - struct yasl_surface *w = s->keyboard_focus; + struct wayland_surface *w = s->keyboard_focus; s->keyboard_focus = NULL; if (w && !s->focus_leaving) s->focus_leaving = w; @@ -1103,7 +1103,7 @@ static void seat_drop_touch(struct seat *s) static void seat_capabilities(void *data, struct wl_seat *proxy, uint32_t caps) { struct seat *s = data; - struct yasl_display *c = s->conn; + struct wayland_display *c = s->conn; if (caps & WL_SEAT_CAPABILITY_KEYBOARD) { if (!s->keyboard) { s->keyboard = wl_seat_get_keyboard(proxy); @@ -1165,7 +1165,7 @@ const struct wl_seat_listener seat_listener = { .name = seat_name, }; -void seat_bind(struct yasl_display *c, struct wl_registry *registry, +void seat_bind(struct wayland_display *c, struct wl_registry *registry, uint32_t name, uint32_t version) { struct seat *s = calloc(1, sizeof(*s)); @@ -1186,9 +1186,9 @@ void seat_bind(struct yasl_display *c, struct wl_registry *registry, keyboard_repeat_init(s); } -void seat_forget_surface(struct seat *s, struct yasl_surface *w) +void seat_forget_surface(struct seat *s, struct wayland_surface *w) { - struct yasl_surface **refs[] = { + struct wayland_surface **refs[] = { &s->pointer_focus, &s->keyboard_focus, &s->tablet_focus, &s->ime_focus, &s->focus_leaving, &s->ptr_left, &s->press_surface, @@ -1211,7 +1211,7 @@ void seat_forget_surface(struct seat *s, struct yasl_surface *w) void seat_destroy(struct seat *s) { - struct yasl_display *c = s->conn; + struct wayland_display *c = s->conn; for (struct seat **link = &c->seats; *link; link = &(*link)->next) { if (*link == s) { *link = s->next; @@ -1220,7 +1220,7 @@ void seat_destroy(struct seat *s) } if (c->active_seat == s) c->active_seat = NULL; - for (struct yasl_surface *w = c->surfaces; w; w = w->conn_next) { + for (struct wayland_surface *w = c->surfaces; w; w = w->conn_next) { if (w->ime_seat == s) { w->ime_seat = NULL; ime_reset(w); @@ -1233,11 +1233,11 @@ void seat_destroy(struct seat *s) seat_destroy_tablet(s); seat_drop_pointer(s); seat_drop_keyboard(s); - struct yasl_surface *left = s->focus_leaving; + struct wayland_surface *left = s->focus_leaving; if (left && !conn_keyboard_on(c, left, NULL)) { struct yasl_input_event ev = { .kind = YASL_INPUT_FOCUS, - .surface = left, + .surface = &left->base, .focus = { .focused = false }, }; input_queue_push(&left->input, &ev); @@ -1259,7 +1259,7 @@ void seat_destroy(struct seat *s) free(s); } -bool seat_remove(struct yasl_display *c, uint32_t name) +bool seat_remove(struct wayland_display *c, uint32_t name) { for (struct seat *s = c->seats; s; s = s->next) { if (s->name == name) { @@ -1270,7 +1270,7 @@ bool seat_remove(struct yasl_display *c, uint32_t name) return false; } -int display_input_init(struct yasl_display *c) +int display_input_init(struct wayland_display *c) { for (struct seat *s = c->seats; s; s = s->next) { keyboard_repeat_init(s); @@ -1279,7 +1279,7 @@ int display_input_init(struct yasl_display *c) return 0; } -void surface_init_cursor(struct yasl_display *c) +void surface_init_cursor(struct wayland_display *c) { if (c->cursor_shape_mgr) return; diff --git a/src/wayland/popup.c b/src/wayland/popup.c index e809a43..f0ef305 100644 --- a/src/wayland/popup.c +++ b/src/wayland/popup.c @@ -32,7 +32,7 @@ static bool popup_spec_valid(const struct yasl_popup_spec *spec) spec->content_offset.y + spec->content_size.y <= spec->size.y; } -static bool popup_apply_spec(struct yasl_surface *w, const struct yasl_popup_spec *spec) +static bool popup_apply_spec(struct wayland_surface *w, const struct yasl_popup_spec *spec) { uint32_t scale = w->parent->scale_120; struct yasl_vector2i logical = { @@ -69,7 +69,7 @@ static bool popup_apply_spec(struct yasl_surface *w, const struct yasl_popup_spe return true; } -static void popup_set_geometry(struct yasl_surface *w) +static void popup_set_geometry(struct wayland_surface *w) { uint32_t scale = w->scale_120; int gx = yasl_scale_to_logical(w->content.x, scale); @@ -85,10 +85,10 @@ static void popup_set_geometry(struct yasl_surface *w) xdg_surface_set_window_geometry(w->xdg_surface, gx, gy, gw, gh); } -static struct xdg_positioner *popup_positioner(struct yasl_surface *w, +static struct xdg_positioner *popup_positioner(struct wayland_surface *w, const struct yasl_popup_spec *spec) { - struct yasl_surface *parent = w->parent; + struct wayland_surface *parent = w->parent; struct xdg_positioner *pos = xdg_wm_base_create_positioner(w->conn->wm_base); if (!pos) return NULL; @@ -119,23 +119,24 @@ static struct xdg_positioner *popup_positioner(struct yasl_surface *w, return pos; } -struct yasl_surface *yasl_surface_create_popup(struct yasl_surface *parent, +struct wayland_surface *wayland_surface_create_popup(struct wayland_surface *parent, const struct yasl_popup_spec *spec) { - struct yasl_display *c = parent ? parent->conn : NULL; + struct wayland_display *c = parent ? parent->conn : NULL; struct seat *seat = c ? conn_active_seat(c) : NULL; if (!c || c->lost || parent->closed || !parent->xdg_surface || !seat || !seat->input_serial || !popup_spec_valid(spec)) return NULL; - struct yasl_surface *w = calloc(1, sizeof(*w)); + struct wayland_surface *w = calloc(1, sizeof(*w)); if (!w) return NULL; + w->base.display = &c->base; w->conn = c; w->conn_next = c->surfaces; c->surfaces = w; w->parent = parent; - yasl_surface_set_callbacks(w, NULL, NULL); + wayland_surface_set_callbacks(w, NULL, NULL); w->clock.refresh_ns = parent->clock.refresh_ns; w->surface = wl_compositor_create_surface(c->compositor); @@ -162,11 +163,11 @@ struct yasl_surface *yasl_surface_create_popup(struct yasl_surface *parent, return w; err: - yasl_surface_destroy(w); + wayland_surface_destroy(w); return NULL; } -bool yasl_surface_popup_reposition(struct yasl_surface *w, const struct yasl_popup_spec *spec) +bool wayland_surface_popup_reposition(struct wayland_surface *w, const struct yasl_popup_spec *spec) { if (!w || !w->popup || w->closed || !w->conn || w->conn->lost || !popup_spec_valid(spec) || @@ -188,6 +189,6 @@ bool yasl_surface_popup_reposition(struct yasl_surface *w, const struct yasl_pop static uint32_t token; xdg_popup_reposition(w->popup, pos, ++token); xdg_positioner_destroy(pos); - yasl_surface_schedule_redraw(w); + wayland_surface_schedule_redraw(w); return true; } diff --git a/src/wayland/shell.c b/src/wayland/shell.c index ef72512..f068f48 100644 --- a/src/wayland/shell.c +++ b/src/wayland/shell.c @@ -7,7 +7,7 @@ static void presentation_clock_id(void *data, struct wp_presentation *presentati uint32_t clk_id) { (void)presentation; - struct yasl_display *c = data; + struct wayland_display *c = data; struct timespec ts; c->pres_clock = (clockid_t)clk_id; c->pres_clock_known = clock_gettime(c->pres_clock, &ts) == 0; @@ -21,7 +21,7 @@ static uint64_t clock_ns(clockid_t clock) return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec; } -uint64_t conn_pres_to_mono(const struct yasl_display *c, uint64_t ns) +uint64_t conn_pres_to_mono(const struct wayland_display *c, uint64_t ns) { if (c->pres_clock == CLOCK_MONOTONIC) return ns; @@ -30,7 +30,7 @@ uint64_t conn_pres_to_mono(const struct yasl_display *c, uint64_t ns) return ns + mono - pres; } -uint64_t conn_mono_to_pres(const struct yasl_display *c, uint64_t ns) +uint64_t conn_mono_to_pres(const struct wayland_display *c, uint64_t ns) { if (c->pres_clock == CLOCK_MONOTONIC) return ns; @@ -61,9 +61,9 @@ static void output_mode(void *data, struct wl_output *output, uint32_t flags, o->pending_refresh_ns = (uint32_t)(1000000000000ull / (uint32_t)refresh); } -static void conn_follow_outputs(struct yasl_display *c) +static void conn_follow_outputs(struct wayland_display *c) { - for (struct yasl_surface *w = c->surfaces; w; w = w->conn_next) + for (struct wayland_surface *w = c->surfaces; w; w = w->conn_next) surface_follow_outputs(w); } @@ -101,7 +101,7 @@ static const struct wl_output_listener output_listener = { .scale = output_scale, }; -static void conn_bind_output(struct yasl_display *c, struct wl_registry *registry, +static void conn_bind_output(struct wayland_display *c, struct wl_registry *registry, uint32_t name, uint32_t version) { struct wl_conn_output *o = calloc(1, sizeof(*o)); @@ -134,7 +134,7 @@ static void output_destroy(struct wl_conn_output *o) free(o); } -static bool conn_remove_output(struct yasl_display *c, uint32_t name) +static bool conn_remove_output(struct wayland_display *c, uint32_t name) { struct wl_conn_output **link = &c->outputs; while (*link && (*link)->name != name) @@ -143,14 +143,14 @@ static bool conn_remove_output(struct yasl_display *c, uint32_t name) if (!o) return false; *link = o->next; - for (struct yasl_surface *w = c->surfaces; w; w = w->conn_next) + for (struct wayland_surface *w = c->surfaces; w; w = w->conn_next) w->outputs &= ~o->mask; output_destroy(o); conn_follow_outputs(c); return true; } -void conn_destroy_outputs(struct yasl_display *c) +void conn_destroy_outputs(struct wayland_display *c) { while (c->outputs) { struct wl_conn_output *o = c->outputs; @@ -159,7 +159,7 @@ void conn_destroy_outputs(struct yasl_display *c) } } -uint64_t conn_output_mask(const struct yasl_display *c, const struct wl_output *proxy) +uint64_t conn_output_mask(const struct wayland_display *c, const struct wl_output *proxy) { for (const struct wl_conn_output *o = c->outputs; o; o = o->next) if (o->proxy == proxy) @@ -167,7 +167,7 @@ uint64_t conn_output_mask(const struct yasl_display *c, const struct wl_output * return 0; } -uint32_t conn_output_scale_120(const struct yasl_display *c, uint64_t outputs) +uint32_t conn_output_scale_120(const struct wayland_display *c, uint64_t outputs) { int32_t scale = 0; for (const struct wl_conn_output *o = c->outputs; o; o = o->next) @@ -179,7 +179,7 @@ uint32_t conn_output_scale_120(const struct yasl_display *c, uint64_t outputs) static void registry_global(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { - struct yasl_display *c = data; + struct wayland_display *c = data; if (strcmp(interface, wl_compositor_interface.name) == 0) { c->compositor = wl_registry_bind(registry, name, &wl_compositor_interface, version < 6 ? version : 6); @@ -245,7 +245,7 @@ static void registry_global_remove(void *data, struct wl_registry *registry, uint32_t name) { (void)registry; - struct yasl_display *c = data; + struct wayland_display *c = data; if (c && !conn_remove_output(c, name)) seat_remove(c, name); } @@ -265,17 +265,17 @@ static const struct xdg_wm_base_listener wm_base_listener = { .ping = wm_base_ping, }; -static void notify_state(struct yasl_surface *w) +static void notify_state(struct wayland_surface *w) { if (w->cbs && w->cbs->state) - w->cbs->state(w, yasl_surface_state(w), w->ud); + w->cbs->state(&w->base, wayland_surface_state(w), w->ud); } static void toplevel_configure(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height, struct wl_array *states) { (void)toplevel; - struct yasl_surface *w = data; + struct wayland_surface *w = data; bool fs = false, max = false, act = false, susp = false, tiled = false; uint32_t *s; @@ -317,13 +317,13 @@ static void toplevel_configure_bounds(void *data, struct xdg_toplevel *toplevel, int32_t width, int32_t height) { (void)toplevel; - struct yasl_surface *w = data; + struct wayland_surface *w = data; w->bound_width = width; w->bound_height = height; if ((width > 0 && w->logical_width > width) || (height > 0 && w->logical_height > height)) { w->size_pending = true; - yasl_surface_schedule_redraw(w); + wayland_surface_schedule_redraw(w); } } @@ -342,13 +342,13 @@ static const struct xdg_toplevel_listener toplevel_listener = { static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial) { - struct yasl_surface *w = data; + struct wayland_surface *w = data; (void)xdg_surface; w->pending_serial = serial; w->has_pending_serial = true; w->size_pending = true; w->configured = true; - yasl_surface_schedule_redraw(w); + wayland_surface_schedule_redraw(w); } const struct xdg_surface_listener shell_surface_listener = { @@ -359,7 +359,7 @@ static void toplevel_decoration_configure(void *data, struct zxdg_toplevel_decoration_v1 *toplevel_decor, uint32_t mode) { (void)toplevel_decor; - struct yasl_surface *w = data; + struct wayland_surface *w = data; bool was_server = w->server_decorated; w->server_decorated = (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); if (was_server != w->server_decorated) @@ -370,7 +370,7 @@ static const struct zxdg_toplevel_decoration_v1_listener toplevel_decoration_lis .configure = toplevel_decoration_configure, }; -int surface_init_shell(struct yasl_surface *w, const char *title) +int surface_init_shell(struct wayland_surface *w, const char *title) { w->xdg_surface = xdg_wm_base_get_xdg_surface(w->conn->wm_base, w->surface); if (!w->xdg_surface) return -1; @@ -379,7 +379,7 @@ int surface_init_shell(struct yasl_surface *w, const char *title) if (!w->toplevel) return -1; xdg_toplevel_add_listener(w->toplevel, &toplevel_listener, w); - yasl_surface_set_title(w, title ? title : "yasl_app"); + wayland_surface_set_title(w, title ? title : "yasl_app"); const char *app_id = w->conn->app_id ? w->conn->app_id : "yasl_app"; xdg_toplevel_set_app_id(w->toplevel, app_id); diff --git a/src/wayland/surface.c b/src/wayland/surface.c index da334e4..ced7d89 100644 --- a/src/wayland/surface.c +++ b/src/wayland/surface.c @@ -1,9 +1,9 @@ #include "internal.h" #include -static void display_free(struct yasl_display *c); +static void display_free(struct wayland_display *c); -static void display_cancel_read(struct yasl_display *c) +static void display_cancel_read(struct wayland_display *c) { if (!c->reading) return; @@ -11,7 +11,7 @@ static void display_cancel_read(struct yasl_display *c) c->reading = false; } -static void report_display_error(struct yasl_display *c) +static void report_display_error(struct wayland_display *c) { int err = wl_display_get_error(c->wl); if (!err) @@ -26,24 +26,24 @@ static void report_display_error(struct yasl_display *c) fprintf(stderr, "[yasl] display error %d\n", err); } -void display_enter(struct yasl_display *c) +void display_enter(struct wayland_display *c) { c->depth++; } -void display_leave(struct yasl_display *c) +void display_leave(struct wayland_display *c) { if (--c->depth > 0) return; keyboard_settle_focus(c); while (c->dead) { - struct yasl_surface *w = c->dead; + struct wayland_surface *w = c->dead; c->dead = w->dead_next; free(w); } } -static void display_lose(struct yasl_display *c) +static void display_lose(struct wayland_display *c) { if (c->lost) return; @@ -51,7 +51,7 @@ static void display_lose(struct yasl_display *c) report_display_error(c); display_cancel_read(c); display_enter(c); - for (struct yasl_surface *w = c->surfaces, *next; w; w = next) { + for (struct wayland_surface *w = c->surfaces, *next; w; w = next) { next = w->conn_next; surface_fail(w); } @@ -60,19 +60,19 @@ static void display_lose(struct yasl_display *c) c->cbs->lost(c->ud); } -void surface_fail(struct yasl_surface *w) +void surface_fail(struct wayland_surface *w) { if (w->closed) return; w->closed = true; if (w->cbs->close) - w->cbs->close(w, w->ud); + w->cbs->close(&w->base, w->ud); } static int display_ready(int fd, short revents, void *ud) { (void)fd; - struct yasl_display *c = ud; + struct wayland_display *c = ud; if (c->lost) return 0; display_enter(c); @@ -90,7 +90,7 @@ static int display_ready(int fd, short revents, void *ud) return 0; } -void yasl_display_dispatch(struct yasl_display *c) +void wayland_display_dispatch(struct wayland_display *c) { if (!c || c->lost || c->reading) return; @@ -100,7 +100,7 @@ void yasl_display_dispatch(struct yasl_display *c) display_leave(c); } -static void display_arm(struct yasl_display *c) +static void display_arm(struct wayland_display *c) { while (!c->reading && wl_display_prepare_read(c->wl) != 0) { if (wl_display_dispatch_pending(c->wl) < 0) { @@ -121,12 +121,12 @@ static void display_arm(struct yasl_display *c) yasl_loop_set_fd_events(c->loop, c->watch, events); } -void yasl_display_flush(struct yasl_display *c) +void wayland_display_flush(struct wayland_display *c) { if (!c || c->lost) return; display_enter(c); - for (struct yasl_surface *w = c->surfaces, *next; w; w = next) { + for (struct wayland_surface *w = c->surfaces, *next; w; w = next) { next = w->conn_next; surface_draw_frame(w); } @@ -135,7 +135,7 @@ void yasl_display_flush(struct yasl_display *c) display_leave(c); } -static void display_free(struct yasl_display *c) +static void display_free(struct wayland_display *c) { conn_drag_end(c); if (c->loop && c->watch > 0) yasl_loop_del_fd(c->loop, c->watch); @@ -167,10 +167,11 @@ static void display_free(struct yasl_display *c) free(c); } -struct yasl_display *yasl_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec) +struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec) { - struct yasl_display *c = calloc(1, sizeof(*c)); + struct wayland_display *c = calloc(1, sizeof(*c)); if (!c) return NULL; + c->base.ops = &yasl_wayland_backend; c->loop = loop; if (spec) { c->cbs = spec->cbs; @@ -201,14 +202,14 @@ err: return NULL; } -void yasl_display_close(struct yasl_display *c) +void wayland_display_close(struct wayland_display *c) { if (!c) return; assert(!c->surfaces && "destroy every surface before its display"); display_free(c); } -uint32_t yasl_display_caps(const struct yasl_display *c) +uint32_t wayland_display_caps(const struct wayland_display *c) { if (!c) return 0; uint32_t caps = YASL_CAP_POPUPS | YASL_CAP_CSD; @@ -223,18 +224,19 @@ uint32_t yasl_display_caps(const struct yasl_display *c) return caps; } -static void conn_link(struct yasl_display *c, struct yasl_surface *w) +static void conn_link(struct wayland_display *c, struct wayland_surface *w) { + w->base.display = &c->base; w->conn = c; w->conn_next = c->surfaces; c->surfaces = w; } -static void conn_unlink(struct yasl_surface *w) +static void conn_unlink(struct wayland_surface *w) { - struct yasl_display *c = w->conn; + struct wayland_display *c = w->conn; if (!c) return; - for (struct yasl_surface **p = &c->surfaces; *p; p = &(*p)->conn_next) { + for (struct wayland_surface **p = &c->surfaces; *p; p = &(*p)->conn_next) { if (*p == w) { *p = w->conn_next; break; @@ -247,9 +249,9 @@ static void conn_unlink(struct yasl_surface *w) w->conn = NULL; } -struct yasl_surface *conn_surface_for(struct yasl_display *c, struct wl_surface *s) +struct wayland_surface *conn_surface_for(struct wayland_display *c, struct wl_surface *s) { - for (struct yasl_surface *w = c->surfaces; w && s; w = w->conn_next) + for (struct wayland_surface *w = c->surfaces; w && s; w = w->conn_next) if (w->surface == s) return w; return NULL; @@ -262,18 +264,18 @@ static bool spec_valid(const struct yasl_surface_spec *spec) buffer_size(spec->size.x, spec->size.y, &bytes); } -struct yasl_surface *yasl_surface_create_toplevel(struct yasl_display *c, +struct wayland_surface *wayland_surface_create_toplevel(struct wayland_display *c, const struct yasl_surface_spec *spec) { if (!c || c->lost || !spec_valid(spec)) return NULL; int width = spec->size.x, height = spec->size.y; display_cancel_read(c); - struct yasl_surface *w = calloc(1, sizeof(*w)); + struct wayland_surface *w = calloc(1, sizeof(*w)); if (!w) return NULL; display_enter(c); conn_link(c, w); - yasl_surface_set_callbacks(w, NULL, NULL); + wayland_surface_set_callbacks(w, NULL, NULL); w->req_width = width; w->req_height = height; w->logical_width = width; @@ -311,12 +313,12 @@ struct yasl_surface *yasl_surface_create_toplevel(struct yasl_display *c, return w; err: - yasl_surface_destroy(w); + wayland_surface_destroy(w); display_leave(c); return NULL; } -void yasl_surface_set_callbacks(struct yasl_surface *w, const struct yasl_surface_cbs *cbs, void *ud) +void wayland_surface_set_callbacks(struct wayland_surface *w, const struct yasl_surface_cbs *cbs, void *ud) { if (w && !w->closed) { static const struct yasl_surface_cbs no_callbacks = {0}; @@ -325,10 +327,10 @@ void yasl_surface_set_callbacks(struct yasl_surface *w, const struct yasl_surfac } } -void yasl_surface_destroy(struct yasl_surface *w) +void wayland_surface_destroy(struct wayland_surface *w) { if (!w || w->destroyed) return; - for (struct yasl_surface *o = w->conn ? w->conn->surfaces : NULL; o; o = o->conn_next) + for (struct wayland_surface *o = w->conn ? w->conn->surfaces : NULL; o; o = o->conn_next) assert(o->parent != w && "destroy a popup before its parent"); input_queue_clear(&w->input); free(w->ime_preedit); @@ -347,10 +349,11 @@ void yasl_surface_destroy(struct yasl_surface *w) if (w->popup) xdg_popup_destroy(w->popup); if (w->xdg_surface) xdg_surface_destroy(w->xdg_surface); if (w->surface) wl_surface_destroy(w->surface); - struct yasl_display *c = w->conn; + struct wayland_display *c = w->conn; conn_unlink(w); static const struct yasl_surface_cbs no_callbacks = {0}; - *w = (struct yasl_surface){ + *w = (struct wayland_surface){ + .base = w->base, .conn_next = w->conn_next, .cbs = &no_callbacks, .closed = true, @@ -364,32 +367,32 @@ void yasl_surface_destroy(struct yasl_surface *w) } } -struct yasl_display *yasl_surface_display(const struct yasl_surface *w) +struct wayland_display *wayland_surface_display(const struct wayland_surface *w) { return w ? w->conn : NULL; } -void yasl_surface_schedule_redraw(struct yasl_surface *w) +void wayland_surface_schedule_redraw(struct wayland_surface *w) { if (w) w->want_draw = true; } -struct yasl_vector2i yasl_surface_size(const struct yasl_surface *w) +struct yasl_vector2i wayland_surface_size(const struct wayland_surface *w) { return w ? (struct yasl_vector2i){ .x = w->width, .y = w->height } : (struct yasl_vector2i){0}; } -uint32_t yasl_surface_scale_120(const struct yasl_surface *w) +uint32_t wayland_surface_scale_120(const struct wayland_surface *w) { return w ? w->scale_120 : YASL_SCALE_UNIT; } -void yasl_surface_set_background_color(struct yasl_surface *w, uint32_t bg) +void wayland_surface_set_background_color(struct wayland_surface *w, uint32_t bg) { (void)w; (void)bg; } -bool yasl_surface_start_interactive_move(struct yasl_surface *s) +bool wayland_surface_start_interactive_move(struct wayland_surface *s) { if (!s || !s->toplevel || !s->conn) return false; struct seat *seat = conn_active_seat(s->conn); @@ -398,7 +401,7 @@ bool yasl_surface_start_interactive_move(struct yasl_surface *s) return true; } -bool yasl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_resize_edge edge) +bool wayland_surface_start_interactive_resize(struct wayland_surface *s, enum yasl_resize_edge edge) { if (!s || !s->toplevel || !s->conn || edge == YASL_EDGE_NONE) return false; struct seat *seat = conn_active_seat(s->conn); @@ -407,7 +410,7 @@ bool yasl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_res return true; } -bool yasl_surface_show_window_menu(struct yasl_surface *s, struct yasl_vector2i pos) +bool wayland_surface_show_window_menu(struct wayland_surface *s, struct yasl_vector2i pos) { if (!s || !s->toplevel || !s->conn) return false; struct seat *seat = conn_active_seat(s->conn); @@ -419,12 +422,12 @@ bool yasl_surface_show_window_menu(struct yasl_surface *s, struct yasl_vector2i return true; } -void *yasl_display_get_native_handle(struct yasl_display *d) +void *wayland_display_get_native_handle(struct wayland_display *d) { return d ? d->wl : NULL; } -void *yasl_surface_get_native_handle(struct yasl_surface *s) +void *wayland_surface_get_native_handle(struct wayland_surface *s) { return s ? s->surface : NULL; } diff --git a/src/x11/meson.build b/src/x11/meson.build new file mode 100644 index 0000000..e1a38f0 --- /dev/null +++ b/src/x11/meson.build @@ -0,0 +1 @@ +sources += files('window.c') diff --git a/src/x11/window.c b/src/x11/window.c new file mode 100644 index 0000000..b80bfe0 --- /dev/null +++ b/src/x11/window.c @@ -0,0 +1,723 @@ +#define _POSIX_C_SOURCE 200809L +#include "common/backend.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define INPUT_CAP 256 +#define TEXT_LIMIT (16 * 1024 * 1024) + +struct x11_surface { + struct yasl_surface base; + struct x11_surface *next; + struct x11_surface *dead_next; + Window window; + GC gc; + XIC ic; + XImage *image; + uint32_t *pixels; + struct yasl_vector2i size; + const struct yasl_surface_cbs *cbs; + void *ud; + struct yasl_input_event events[INPUT_CAP]; + char *event_text[INPUT_CAP]; + char *last_text; + unsigned head, count; + struct yasl_frame frame; + struct yasl_damage_list damage; + struct yasl_frame_clock clock; + uint32_t state; + bool want_draw, drawing, presented, destroyed; +}; + +struct x11_display { + struct yasl_display base; + Display *native; + int screen; + struct yasl_loop *loop; + int watch; + struct x11_surface *surfaces, *dead; + int depth; + XIM im; + Window selection_window; + Atom protocols, delete_window, utf8, clipboard, targets, paste_property; + Atom net_name, net_state, fullscreen, maximized_h, maximized_v; + char *selection[2]; + struct x11_surface *paste_target; + uint32_t mods; + char *app_id; +}; + +static struct x11_display *display_of(const struct yasl_surface *s) +{ + return (struct x11_display *)s->display; +} + +static void leave(struct x11_display *d) +{ + if (--d->depth) return; + while (d->dead) { + struct x11_surface *s = d->dead; + d->dead = s->dead_next; + free(s); + } +} + +static void push(struct x11_surface *s, struct yasl_input_event event, + const char *text, size_t len) +{ + if (s->destroyed || s->count == INPUT_CAP) return; + unsigned slot = (s->head + s->count) % INPUT_CAP; + if (text) { + char *copy = malloc(len + 1); + if (!copy) return; + memcpy(copy, text, len); + copy[len] = 0; + s->event_text[slot] = copy; + if (event.kind == YASL_INPUT_TEXT) { + event.text.text = copy; + event.text.len = len; + } else { + event.paste.text = copy; + event.paste.len = len; + } + } + event.surface = &s->base; + s->events[slot] = event; + s->count++; +} + +static uint32_t modifiers(unsigned state) +{ + return ((state & ShiftMask) ? YASL_MOD_SHIFT : 0) | + ((state & ControlMask) ? YASL_MOD_CTRL : 0) | + ((state & Mod1Mask) ? YASL_MOD_ALT : 0) | + ((state & Mod4Mask) ? YASL_MOD_SUPER : 0) | + ((state & LockMask) ? YASL_MOD_CAPS : 0); +} + +static bool resize(struct x11_surface *s, struct yasl_vector2i size) +{ + if (size.x <= 0 || size.y <= 0 || size.x > 32767 || size.y > 32767 || + (size_t)size.x > SIZE_MAX / sizeof(uint32_t) / (size_t)size.y) + return false; + if (s->image && size.x == s->size.x && size.y == s->size.y) return true; + struct x11_display *d = display_of(&s->base); + uint64_t start = yasl_loop_now_ns(); + XImage *image = XCreateImage(d->native, DefaultVisual(d->native, d->screen), + (unsigned)DefaultDepth(d->native, d->screen), ZPixmap, 0, NULL, + (unsigned)size.x, (unsigned)size.y, 32, 0); + uint32_t *pixels = calloc((size_t)size.x * (size_t)size.y, sizeof(*pixels)); + if (!image || !pixels) { + if (image) XDestroyImage(image); + free(pixels); + return false; + } + image->data = calloc((size_t)image->bytes_per_line, (size_t)size.y); + if (!image->data) { + XDestroyImage(image); + free(pixels); + return false; + } + if (s->image) XDestroyImage(s->image); + free(s->pixels); + s->image = image; + s->pixels = pixels; + s->size = size; + s->want_draw = true; + yasl_clock_note_resize(&s->clock, yasl_loop_now_ns() - start); + return true; +} + +static void surface_destroy(struct yasl_surface *base) +{ + struct x11_surface *s = (struct x11_surface *)base; + if (s->destroyed) return; + struct x11_display *d = display_of(base); + s->destroyed = true; + for (struct x11_surface **p = &d->surfaces; *p; p = &(*p)->next) { + if (*p == s) { *p = s->next; break; } + } + if (d->paste_target == s) d->paste_target = NULL; + if (s->ic) XDestroyIC(s->ic); + if (s->image) XDestroyImage(s->image); + free(s->pixels); + if (s->gc) XFreeGC(d->native, s->gc); + if (s->window) XDestroyWindow(d->native, s->window); + for (unsigned i = 0; i < INPUT_CAP; i++) free(s->event_text[i]); + free(s->last_text); + if (d->depth) { + s->dead_next = d->dead; + d->dead = s; + } else free(s); +} + +static void set_callbacks(struct yasl_surface *base, + const struct yasl_surface_cbs *cbs, void *ud) +{ + struct x11_surface *s = (struct x11_surface *)base; + if (s->destroyed) return; + s->cbs = cbs; + s->ud = ud; +} + +static void set_title(struct yasl_surface *base, const char *title) +{ + struct x11_surface *s = (struct x11_surface *)base; + if (s->destroyed || !title) return; + struct x11_display *d = display_of(base); + XStoreName(d->native, s->window, title); + XChangeProperty(d->native, s->window, d->net_name, d->utf8, 8, + PropModeReplace, (const unsigned char *)title, (int)strlen(title)); +} + +static void net_message(struct x11_surface *s, Atom type, + long a, long b, long c, long e, long f) +{ + struct x11_display *d = display_of(&s->base); + XEvent event = {0}; + event.xclient = (XClientMessageEvent){ + .type = ClientMessage, .display = d->native, .window = s->window, + .message_type = type, .format = 32, .data.l = {a, b, c, e, f}, + }; + XSendEvent(d->native, RootWindow(d->native, d->screen), False, + SubstructureRedirectMask | SubstructureNotifyMask, &event); +} + +static struct yasl_surface *create_toplevel(struct yasl_display *base, + const struct yasl_surface_spec *spec) +{ + if (!spec) return NULL; + struct x11_display *d = (struct x11_display *)base; + struct x11_surface *s = calloc(1, sizeof(*s)); + if (!s) return NULL; + s->base.display = base; + s->clock.refresh_ns = YASL_REFRESH_DEFAULT_NS; + if (!resize(s, spec->size)) { free(s); return NULL; } + s->window = XCreateSimpleWindow(d->native, RootWindow(d->native, d->screen), + 0, 0, (unsigned)spec->size.x, (unsigned)spec->size.y, 0, 0, 0); + s->gc = XCreateGC(d->native, s->window, 0, NULL); + XSelectInput(d->native, s->window, ExposureMask | StructureNotifyMask | + FocusChangeMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | + ButtonReleaseMask | PointerMotionMask | LeaveWindowMask | PropertyChangeMask); + XSetWMProtocols(d->native, s->window, &d->delete_window, 1); + XClassHint hint = { .res_name = d->app_id, .res_class = d->app_id }; + XSetClassHint(d->native, s->window, &hint); + if (d->im) { + s->ic = XCreateIC(d->im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, s->window, XNFocusWindow, s->window, NULL); + } + s->next = d->surfaces; + d->surfaces = s; + set_title(&s->base, spec->title ? spec->title : "YASL"); + if (spec->fullscreen) { + XChangeProperty(d->native, s->window, d->net_state, XA_ATOM, 32, + PropModeReplace, (unsigned char *)&d->fullscreen, 1); + s->state |= YASL_STATE_FULLSCREEN; + } + XMapWindow(d->native, s->window); + XFlush(d->native); + return &s->base; +} + +static void update_state(struct x11_surface *s) +{ + struct x11_display *d = display_of(&s->base); + Atom actual; + int format; + unsigned long count, left; + unsigned char *data = NULL; + uint32_t state = s->state & YASL_STATE_ACTIVATED; + if (XGetWindowProperty(d->native, s->window, d->net_state, 0, 64, False, + XA_ATOM, &actual, &format, &count, &left, &data) == Success && format == 32) { + Atom *atoms = (Atom *)data; + bool h = false, v = false; + for (unsigned long i = 0; i < count; i++) { + if (atoms[i] == d->fullscreen) state |= YASL_STATE_FULLSCREEN; + if (atoms[i] == d->maximized_h) h = true; + if (atoms[i] == d->maximized_v) v = true; + } + if (h && v) state |= YASL_STATE_MAXIMIZED; + } + if (data) XFree(data); + if (state == s->state) return; + s->state = state; + if (s->cbs && s->cbs->state) s->cbs->state(&s->base, state, s->ud); +} + +static void selection_request(struct x11_display *d, XSelectionRequestEvent *request) +{ + XEvent reply = {0}; + reply.xselection = (XSelectionEvent){ + .type = SelectionNotify, .display = d->native, + .requestor = request->requestor, .selection = request->selection, + .target = request->target, .time = request->time, + }; + Atom property = request->property ? request->property : request->target; + int slot = request->selection == XA_PRIMARY ? YASL_PRIMARY : YASL_CLIPBOARD; + const char *text = d->selection[slot]; + if (text && request->target == d->targets) { + Atom targets[] = {d->targets, d->utf8, XA_STRING}; + XChangeProperty(d->native, request->requestor, property, XA_ATOM, 32, + PropModeReplace, (unsigned char *)targets, 3); + reply.xselection.property = property; + } else if (text && (request->target == d->utf8 || request->target == XA_STRING)) { + size_t len = strlen(text); + long max = XMaxRequestSize(d->native) * 4 - 128; + if (len <= (size_t)max) { + XChangeProperty(d->native, request->requestor, property, request->target, + 8, PropModeReplace, (const unsigned char *)text, (int)len); + reply.xselection.property = property; + } + } + XSendEvent(d->native, request->requestor, False, 0, &reply); +} + +static void selection_notify(struct x11_display *d, XSelectionEvent *event) +{ + struct x11_surface *s = d->paste_target; + d->paste_target = NULL; + if (!s || event->property == None) return; + Atom actual; + int format; + unsigned long count, left; + unsigned char *data = NULL; + if (XGetWindowProperty(d->native, d->selection_window, event->property, + 0, TEXT_LIMIT / 4, True, AnyPropertyType, + &actual, &format, &count, &left, &data) == Success && + (actual == d->utf8 || actual == XA_STRING) && format == 8 && !left) { + push(s, (struct yasl_input_event){.kind = YASL_INPUT_PASTE}, + (const char *)data, count); + } + if (data) XFree(data); +} + +static void key_event(struct x11_surface *s, XKeyEvent *key, bool repeat) +{ + struct x11_display *d = display_of(&s->base); + d->mods = modifiers(key->state); + KeySym sym = XLookupKeysym(key, (key->state & ShiftMask) ? 1 : 0); + KeySym base = XLookupKeysym(key, 0), shifted = XLookupKeysym(key, 1); + char local[128]; + char *text = local; + int len = 0; + if (key->type == KeyPress) { + if (s->ic) { + Status status; + len = Xutf8LookupString(s->ic, key, local, sizeof(local), &sym, &status); + if (status == XBufferOverflow && len > 0 && len < TEXT_LIMIT) { + text = malloc((size_t)len + 1); + if (text) len = Xutf8LookupString(s->ic, key, text, len + 1, &sym, &status); + else len = 0; + } + if (status != XLookupChars && status != XLookupBoth) len = 0; + } else len = XLookupString(key, local, sizeof(local), &sym, NULL); + } + uint32_t cp = xkb_keysym_to_utf32((xkb_keysym_t)base); + if (!cp) cp = xkb_keysym_to_utf32((xkb_keysym_t)sym); + push(s, (struct yasl_input_event){ + .kind = YASL_INPUT_KEY, + .key = {.key = (uint32_t)sym, .codepoint = cp, + .base = key->keycode >= 8 ? key->keycode - 8 : 0, + .keypad = sym >= XK_KP_Space && sym <= XK_KP_Equal, + .shifted = xkb_keysym_to_utf32((xkb_keysym_t)shifted), + .text = xkb_keysym_to_utf32((xkb_keysym_t)sym), .mods = d->mods, + .action = key->type == KeyRelease ? YASL_KEY_RELEASE : + repeat ? YASL_KEY_REPEAT : YASL_KEY_PRESS}, + }, NULL, 0); + if (len > 0 && text) + push(s, (struct yasl_input_event){.kind = YASL_INPUT_TEXT}, text, (size_t)len); + if (text != local) free(text); +} + +static void dispatch(struct yasl_display *base) +{ + struct x11_display *d = (struct x11_display *)base; + d->depth++; + while (XPending(d->native)) { + XEvent event; + XNextEvent(d->native, &event); + if (XFilterEvent(&event, None)) continue; + if (event.type == SelectionRequest) { + selection_request(d, &event.xselectionrequest); + continue; + } + if (event.type == SelectionNotify) { + selection_notify(d, &event.xselection); + continue; + } + struct x11_surface *s = d->surfaces; + while (s && s->window != event.xany.window) s = s->next; + if (!s) continue; + switch (event.type) { + case Expose: s->want_draw = true; break; + case ConfigureNotify: { + struct yasl_vector2i size = {event.xconfigure.width, event.xconfigure.height}; + if (size.x == s->size.x && size.y == s->size.y) break; + if (resize(s, size) && s->cbs && s->cbs->configure) + s->cbs->configure(&s->base, (struct yasl_geometry){size, YASL_SCALE_UNIT}, s->ud); + break; + } + case ClientMessage: + if (event.xclient.message_type == d->protocols && + (Atom)event.xclient.data.l[0] == d->delete_window && s->cbs && s->cbs->close) + s->cbs->close(&s->base, s->ud); + break; + case FocusIn: case FocusOut: { + bool focused = event.type == FocusIn; + if (s->ic) { if (focused) XSetICFocus(s->ic); else XUnsetICFocus(s->ic); } + if (focused) s->state |= YASL_STATE_ACTIVATED; + else s->state &= ~YASL_STATE_ACTIVATED; + push(s, (struct yasl_input_event){ + .kind = YASL_INPUT_FOCUS, .focus.focused = focused}, NULL, 0); + if (s->cbs && s->cbs->state) s->cbs->state(&s->base, s->state, s->ud); + break; + } + case PropertyNotify: + if (event.xproperty.atom == d->net_state) update_state(s); + break; + case KeyPress: case KeyRelease: { + bool repeat = false; + if (event.type == KeyRelease && XPending(d->native)) { + XEvent next; + XPeekEvent(d->native, &next); + if (next.type == KeyPress && next.xkey.window == event.xkey.window && + next.xkey.keycode == event.xkey.keycode && next.xkey.time == event.xkey.time) { + XNextEvent(d->native, &event); + repeat = true; + } + } + key_event(s, &event.xkey, repeat); + break; + } + case MotionNotify: case LeaveNotify: case ButtonPress: case ButtonRelease: { + struct yasl_mouse_event mouse = {.device = YASL_PTR_DEV_MOUSE}; + unsigned state; + if (event.type == MotionNotify) { + mouse.pos = (struct yasl_vector2i){event.xmotion.x, event.xmotion.y}; + mouse.kind = YASL_PTR_MOTION; + state = event.xmotion.state; + } else if (event.type == LeaveNotify) { + mouse.pos = (struct yasl_vector2i){event.xcrossing.x, event.xcrossing.y}; + mouse.kind = YASL_PTR_LEAVE; + state = event.xcrossing.state; + } else { + mouse.pos = (struct yasl_vector2i){event.xbutton.x, event.xbutton.y}; + state = event.xbutton.state; + unsigned button = event.xbutton.button; + if (button >= 4 && button <= 7) { + if (event.type == ButtonRelease) break; + mouse.kind = YASL_PTR_SCROLL; + mouse.scroll_lines = (button == 4 || button == 6) ? -1 : 1; + } else { + mouse.kind = event.type == ButtonPress ? YASL_PTR_PRESS : YASL_PTR_RELEASE; + mouse.button = button == 1 ? 0x110 : button == 2 ? 0x112 : + button == 3 ? 0x111 : (int)button + 0x10b; + } + } + d->mods = mouse.mods = modifiers(state); + push(s, (struct yasl_input_event){.kind = YASL_INPUT_MOUSE, .mouse = mouse}, NULL, 0); + break; + } + } + } + leave(d); +} + +static unsigned long component(uint32_t color, unsigned long mask) +{ + if (!mask) return 0; + unsigned shift = 0; + while (!(mask & 1)) { mask >>= 1; shift++; } + return (((unsigned long)color * mask + 127) / 255) << shift; +} + +static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb, + const struct yasl_damage_list *damage) +{ + struct x11_surface *s = (struct x11_surface *)base; + if (s->destroyed || !s->drawing || s->presented || !fb || !damage || + fb->pixels != s->pixels || fb->size.x != s->size.x || fb->size.y != s->size.y || + damage->n < -1 || damage->n > YASL_DAMAGE_MAX) return false; + struct x11_display *d = display_of(base); + XImage *im = s->image; + bool direct = im->bits_per_pixel == 32 && im->red_mask == 0xff0000 && + im->green_mask == 0xff00 && im->blue_mask == 0xff; + for (int y = 0; y < s->size.y; y++) { + const uint32_t *row = s->pixels + (size_t)y * s->size.x; + if (direct) memcpy(im->data + (size_t)y * im->bytes_per_line, row, (size_t)s->size.x * 4); + else for (int x = 0; x < s->size.x; x++) { + uint32_t p = row[x]; + XPutPixel(im, x, y, component((p >> 16) & 255, im->red_mask) | + component((p >> 8) & 255, im->green_mask) | component(p & 255, im->blue_mask)); + } + } + if (damage->n == -1) + XPutImage(d->native, s->window, s->gc, im, 0, 0, 0, 0, + (unsigned)s->size.x, (unsigned)s->size.y); + else for (int i = 0; i < damage->n; i++) { + struct yasl_rect2i r = damage->rects[i]; + int64_t right = (int64_t)r.x + r.width, bottom = (int64_t)r.y + r.height; + int x = r.x > 0 ? r.x : 0, y = r.y > 0 ? r.y : 0; + int w = (int)(right < s->size.x ? right : s->size.x) - x; + int h = (int)(bottom < s->size.y ? bottom : s->size.y) - y; + if (w > 0 && h > 0) + XPutImage(d->native, s->window, s->gc, im, x, y, x, y, (unsigned)w, (unsigned)h); + } + s->presented = true; + yasl_clock_presented(&s->clock, yasl_loop_now_ns(), false); + return true; +} + +static void flush(struct yasl_display *base) +{ + struct x11_display *d = (struct x11_display *)base; + d->depth++; + for (struct x11_surface *s = d->surfaces, *next; s; s = next) { + next = s->next; + if (s->destroyed || !s->want_draw || !s->cbs || !s->cbs->draw || s->drawing) continue; + s->want_draw = false; + s->drawing = true; + s->presented = false; + s->damage = (struct yasl_damage_list){0}; + s->frame = (struct yasl_frame){ + .surface = &s->base, + .target = {.pixels = s->pixels, .size = s->size, .damage = &s->damage}, + }; + s->cbs->draw(&s->base, &s->frame, s->ud); + s->drawing = false; + } + XFlush(d->native); + leave(d); +} + +static int ready(int fd, short events, void *ud) +{ + (void)fd; + struct yasl_display *base = ud; + if (events & POLLIN) { dispatch(base); flush(base); } + return 0; +} + +static struct yasl_display *display_open(struct yasl_loop *loop, + const struct yasl_display_spec *spec) +{ + struct x11_display *d = calloc(1, sizeof(*d)); + if (!d) return NULL; + d->base.ops = &yasl_x11_backend; + d->native = XOpenDisplay(NULL); + if (!d->native) { free(d); errno = ENODEV; return NULL; } + d->screen = DefaultScreen(d->native); + d->loop = loop; + d->app_id = strdup(spec && spec->app_id ? spec->app_id : "YASL"); + if (!d->app_id) { XCloseDisplay(d->native); free(d); return NULL; } + d->im = XOpenIM(d->native, NULL, NULL, NULL); + d->selection_window = XCreateSimpleWindow(d->native, + RootWindow(d->native, d->screen), 0, 0, 1, 1, 0, 0, 0); +#define ATOM(field, name) d->field = XInternAtom(d->native, name, False) + ATOM(protocols, "WM_PROTOCOLS"); + ATOM(delete_window, "WM_DELETE_WINDOW"); + ATOM(utf8, "UTF8_STRING"); + ATOM(clipboard, "CLIPBOARD"); + ATOM(targets, "TARGETS"); + ATOM(paste_property, "YASL_SELECTION"); + ATOM(net_name, "_NET_WM_NAME"); + ATOM(net_state, "_NET_WM_STATE"); + ATOM(fullscreen, "_NET_WM_STATE_FULLSCREEN"); + ATOM(maximized_h, "_NET_WM_STATE_MAXIMIZED_HORZ"); + ATOM(maximized_v, "_NET_WM_STATE_MAXIMIZED_VERT"); +#undef ATOM + if (loop) { + d->watch = yasl_loop_add_fd(loop, ConnectionNumber(d->native), POLLIN, ready, &d->base); + if (d->watch < 0) { + if (d->im) XCloseIM(d->im); + XDestroyWindow(d->native, d->selection_window); + XCloseDisplay(d->native); + free(d->app_id); + free(d); + return NULL; + } + } + return &d->base; +} + +static void display_close(struct yasl_display *base) +{ + struct x11_display *d = (struct x11_display *)base; + assert(!d->surfaces && "destroy every surface before its display"); + if (d->loop) yasl_loop_del_fd(d->loop, d->watch); + if (d->im) XCloseIM(d->im); + XDestroyWindow(d->native, d->selection_window); + XCloseDisplay(d->native); + free(d->selection[0]); + free(d->selection[1]); + free(d->app_id); + free(d); +} + +static uint32_t caps(const struct yasl_display *d) +{ + (void)d; + return YASL_CAP_PLACE_WINDOW | YASL_CAP_PRIMARY_SELECTION; +} + +static void copy(struct yasl_display *base, enum yasl_selection sel, const char *text) +{ + if (!text || (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY)) return; + struct x11_display *d = (struct x11_display *)base; + char *value = strdup(text); + if (!value) return; + free(d->selection[sel]); + d->selection[sel] = value; + XSetSelectionOwner(d->native, sel == YASL_PRIMARY ? XA_PRIMARY : d->clipboard, + d->selection_window, CurrentTime); + XFlush(d->native); +} + +static void paste(struct yasl_display *base, enum yasl_selection sel, struct yasl_surface *target) +{ + if (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY) return; + struct x11_display *d = (struct x11_display *)base; + struct x11_surface *s = (struct x11_surface *)target; + Atom selection = sel == YASL_PRIMARY ? XA_PRIMARY : d->clipboard; + if (XGetSelectionOwner(d->native, selection) == d->selection_window) { + const char *text = d->selection[sel]; + if (text) push(s, (struct yasl_input_event){.kind = YASL_INPUT_PASTE}, text, strlen(text)); + } else { + d->paste_target = s; + XConvertSelection(d->native, selection, d->utf8, d->paste_property, + d->selection_window, CurrentTime); + XFlush(d->native); + } +} + +static bool input_poll(struct yasl_surface *base, struct yasl_input_event *event) +{ + struct x11_surface *s = (struct x11_surface *)base; + if (!event || s->destroyed || !s->count) return false; + free(s->last_text); + s->last_text = s->event_text[s->head]; + s->event_text[s->head] = NULL; + *event = s->events[s->head]; + s->head = (s->head + 1) % INPUT_CAP; + s->count--; + return true; +} + +static void schedule_redraw(struct yasl_surface *base) +{ + struct x11_surface *s = (struct x11_surface *)base; + if (!s->destroyed) s->want_draw = true; +} + +static struct yasl_display *surface_display(const struct yasl_surface *s) { return s->display; } +static struct yasl_vector2i size(const struct yasl_surface *s) { return ((const struct x11_surface *)s)->size; } +static uint32_t scale(const struct yasl_surface *s) { (void)s; return YASL_SCALE_UNIT; } +static uint32_t state(const struct yasl_surface *s) { return ((const struct x11_surface *)s)->state; } +static int mods(const struct yasl_display *d) { return (int)((const struct x11_display *)d)->mods; } +static void *native_display(struct yasl_display *d) { return ((struct x11_display *)d)->native; } +static void *native_surface(struct yasl_surface *s) { return (void *)(uintptr_t)((struct x11_surface *)s)->window; } + +static struct yasl_timing timing(const struct yasl_surface *base) +{ + const struct x11_surface *s = (const struct x11_surface *)base; + struct yasl_timing t = {.refresh_ns = s->clock.refresh_ns, + .last_present_ns = s->clock.last_present_ns, + .next_present_ns = yasl_clock_next_present_ns(&s->clock)}; + yasl_clock_resize_stats(&s->clock, &t.resize); + return t; +} + +static uint64_t frame_time(struct yasl_surface *s, uint64_t now) +{ + return yasl_clock_frame_timestamp_ns(&((struct x11_surface *)s)->clock, now); +} + +static void cursor(struct yasl_surface *base, enum yasl_cursor cursor) +{ + static const unsigned shapes[] = {XC_left_ptr, XC_xterm, XC_hand2, XC_crosshair, + XC_fleur, XC_bottom_right_corner, XC_bottom_left_corner, + XC_sb_h_double_arrow, XC_sb_v_double_arrow}; + struct x11_surface *s = (struct x11_surface *)base; + if (s->destroyed || (unsigned)cursor > YASL_CURSOR_NONE) return; + struct x11_display *d = display_of(base); + Cursor native; + if (cursor == YASL_CURSOR_NONE) { + char bits[] = {0}; + Pixmap pixmap = XCreateBitmapFromData(d->native, s->window, bits, 1, 1); + XColor color = {0}; + native = XCreatePixmapCursor(d->native, pixmap, pixmap, &color, &color, 0, 0); + XFreePixmap(d->native, pixmap); + } else native = XCreateFontCursor(d->native, shapes[cursor]); + XDefineCursor(d->native, s->window, native); + XFreeCursor(d->native, native); +} + +static bool place(struct yasl_surface *base, struct yasl_vector2i at) +{ + struct x11_surface *s = (struct x11_surface *)base; + struct x11_display *d = display_of(base); + Window root, child; + int root_x, root_y, x, y; + unsigned mask; + if (s->destroyed || !XQueryPointer(d->native, s->window, + &root, &child, &root_x, &root_y, &x, &y, &mask)) return false; + XMoveWindow(d->native, s->window, root_x - at.x, root_y - at.y); + return true; +} + +static bool interactive_move(struct yasl_surface *base) +{ + struct x11_surface *s = (struct x11_surface *)base; + struct x11_display *d = display_of(base); + Window root, child; + int rx, ry, x, y; + unsigned mask; + if (s->destroyed || !XQueryPointer(d->native, s->window, + &root, &child, &rx, &ry, &x, &y, &mask)) return false; + XUngrabPointer(d->native, CurrentTime); + net_message(s, XInternAtom(d->native, "_NET_WM_MOVERESIZE", False), rx, ry, 8, 1, 1); + return true; +} + +const struct yasl_backend_ops yasl_x11_backend = { + .type = YASL_BACKEND_X11, + .display_open = display_open, + .display_close = display_close, + .display_caps = caps, + .display_dispatch = dispatch, + .display_flush = flush, + .display_modifiers = mods, + .display_copy = copy, + .display_paste = paste, + .surface_create_toplevel = create_toplevel, + .surface_destroy = surface_destroy, + .surface_display = surface_display, + .surface_set_callbacks = set_callbacks, + .surface_set_title = set_title, + .surface_schedule_redraw = schedule_redraw, + .surface_present = present, + .surface_input_poll = input_poll, + .surface_size = size, + .surface_scale_120 = scale, + .surface_state = state, + .surface_timing = timing, + .surface_frame_time_ns = frame_time, + .surface_set_cursor = cursor, + .surface_place_under_pointer = place, + .surface_start_interactive_move = interactive_move, + .display_get_native_handle = native_display, + .surface_get_native_handle = native_surface, +}; diff --git a/tests/cocoa/test_native.c b/tests/cocoa/test_native.c new file mode 100644 index 0000000..03fde88 --- /dev/null +++ b/tests/cocoa/test_native.c @@ -0,0 +1,58 @@ +#define _POSIX_C_SOURCE 200809L +#include "yasl/yasl.h" + +#include +#include + +struct state { + struct yasl_display *display; + struct yasl_surface *surface; + unsigned draws; +}; + +static void draw(struct yasl_surface *surface, struct yasl_frame *frame, void *ud) +{ + struct state *state = ud; + assert(surface == state->surface && frame->surface == surface); + assert(yasl_surface_display(surface) == state->display); + assert(frame->target.pixels); + struct yasl_vector2i size = yasl_surface_size(surface); + assert(size.x == frame->target.size.x && size.y == frame->target.size.y); + for (int i = 0; i < size.x * size.y; i++) frame->target.pixels[i] = 0xff4080c0; + struct yasl_damage_list damage = {.n = -1}; + assert(yasl_surface_present(surface, &frame->target, &damage)); + assert(!yasl_surface_present(surface, &frame->target, &damage)); + state->draws++; + yasl_surface_destroy(surface); + state->surface = NULL; +} + +static void test_window(enum yasl_backend request, bool null_spec) +{ + struct yasl_display_spec spec = {.backend = request}; + struct state state = {.display = yasl_display_open(NULL, null_spec ? NULL : &spec)}; + assert(state.display); + assert(yasl_display_backend(state.display) == YASL_BACKEND_COCOA); + struct yasl_surface_spec surface = {.title = "Native backend test", .size = {160, 100}}; + state.surface = yasl_surface_create_toplevel(state.display, &surface); + assert(state.surface); + static const struct yasl_surface_cbs callbacks = {.draw = draw}; + yasl_surface_set_callbacks(state.surface, &callbacks, &state); + yasl_surface_schedule_redraw(state.surface); + uint64_t deadline = yasl_loop_now_ns() + 3000000000ull; + while (!state.draws && yasl_loop_now_ns() < deadline) { + yasl_display_dispatch(state.display); + yasl_display_flush(state.display); + nanosleep(&(struct timespec){.tv_nsec = 1000000}, NULL); + } + assert(state.draws == 1 && !state.surface); + yasl_display_close(state.display); +} + +int main(void) +{ + test_window(YASL_BACKEND_AUTO, true); + test_window(YASL_BACKEND_AUTO, false); + test_window(YASL_BACKEND_COCOA, false); + return 0; +} diff --git a/tests/common/test_backends.c b/tests/common/test_backends.c new file mode 100644 index 0000000..48a6b68 --- /dev/null +++ b/tests/common/test_backends.c @@ -0,0 +1,215 @@ +#define _POSIX_C_SOURCE 200809L +#include "yasl/yasl.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct window { + struct yasl_surface *surface; + struct yasl_display *display; + int draws, configures, closes; + bool destroy_on_draw, destroy_on_configure; +}; + +struct pump { + struct yasl_loop *loop; + struct yasl_display *displays[2]; + int ticks; +}; + +static int tick(int fd, short events, void *ud) +{ + (void)events; + uint64_t expirations; + assert(read(fd, &expirations, sizeof(expirations)) == sizeof(expirations)); + struct pump *p = ud; + for (int i = 0; i < 2; i++) { + if (p->displays[i]) { + yasl_display_dispatch(p->displays[i]); + yasl_display_flush(p->displays[i]); + } + } + if (--p->ticks == 0) yasl_loop_quit(p->loop); + return 0; +} + +static void pump(struct yasl_loop *loop, struct yasl_display *a, struct yasl_display *b) +{ + int timer = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC); + assert(timer >= 0); + struct pump p = {.loop = loop, .displays = {a, b}, .ticks = 20}; + struct itimerspec spec = {.it_value.tv_nsec = 10000000, .it_interval.tv_nsec = 10000000}; + assert(timerfd_settime(timer, 0, &spec, NULL) == 0); + int watch = yasl_loop_add_fd(loop, timer, POLLIN, tick, &p); + assert(watch > 0); + assert(yasl_loop_run(loop) == 0); + assert(yasl_loop_del_fd(loop, watch) == 0); + close(timer); +} + +static void draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud) +{ + struct window *w = ud; + assert(s == w->surface); + assert(frame->surface == s); + assert(yasl_surface_display(s) == w->display); + struct yasl_vector2i size = yasl_surface_size(s); + assert(size.x == frame->target.size.x && size.y == frame->target.size.y); + assert(frame->target.pixels); + for (int i = 0; i < size.x * size.y; i++) frame->target.pixels[i] = 0xff4080c0; + struct yasl_damage_list damage = {.n = -1}; + assert(yasl_surface_present(s, &frame->target, &damage)); + assert(!yasl_surface_present(s, &frame->target, &damage)); + w->draws++; + if (w->destroy_on_draw) { yasl_surface_destroy(s); w->surface = NULL; } +} + +static void configure(struct yasl_surface *s, struct yasl_geometry geometry, void *ud) +{ + struct window *w = ud; + assert(w->surface == s); + struct yasl_vector2i size = yasl_surface_size(s); + assert(geometry.size.x == size.x && geometry.size.y == size.y); + assert(geometry.scale_120 == yasl_surface_scale_120(s)); + w->configures++; + if (w->destroy_on_configure) { yasl_surface_destroy(s); w->surface = NULL; } +} + +static void close_window(struct yasl_surface *s, void *ud) +{ + struct window *w = ud; + assert(s == w->surface); + w->closes++; + yasl_surface_destroy(s); + w->surface = NULL; +} + +static const struct yasl_surface_cbs callbacks = { + .draw = draw, .configure = configure, .close = close_window, +}; + +static struct window create(struct yasl_display *d) +{ + struct yasl_surface_spec spec = {.title = "Backend test", .size = {160, 100}}; + struct yasl_surface *s = yasl_surface_create_toplevel(d, &spec); + assert(s); + assert(yasl_surface_display(s) == d); + return (struct window){.surface = s, .display = d}; +} + +static void x11_behavior(struct yasl_loop *loop, struct yasl_display *d, struct window *w) +{ + Display *native = yasl_display_get_native_handle(d); + Window window = (Window)(uintptr_t)yasl_surface_get_native_handle(w->surface); + assert(native && window); + XSync(native, False); + XImage *image = XGetImage(native, window, 0, 0, 1, 1, AllPlanes, ZPixmap); + assert(image && (XGetPixel(image, 0, 0) & 0xffffff) == 0x4080c0); + XDestroyImage(image); + yasl_surface_set_title(w->surface, "Updated title"); + char *title = NULL; + assert(XFetchName(native, window, &title)); + assert(strcmp(title, "Updated title") == 0); + XFree(title); + XResizeWindow(native, window, 200, 120); + XFlush(native); + pump(loop, d, NULL); + assert(w->configures > 0); + struct yasl_vector2i size = yasl_surface_size(w->surface); + assert(size.x == 200 && size.y == 120); + XEvent key = {0}; + key.xkey = (XKeyEvent){.type = KeyPress, .display = native, .window = window, + .root = DefaultRootWindow(native), .same_screen = True, + .keycode = XKeysymToKeycode(native, XK_a)}; + XSendEvent(native, window, False, KeyPressMask, &key); + XFlush(native); + yasl_display_copy(d, YASL_CLIPBOARD, "clipboard text"); + yasl_display_paste(d, YASL_CLIPBOARD, w->surface); + pump(loop, d, NULL); + bool saw_key = false, saw_paste = false; + struct yasl_input_event event; + while (yasl_surface_input_poll(w->surface, &event)) { + assert(event.surface == w->surface); + if (event.kind == YASL_INPUT_KEY) { + assert(event.key.codepoint == 'a'); + assert(event.key.shifted == 'A'); + assert(event.key.base == 30); + saw_key = event.key.key == XK_a; + } + if (event.kind == YASL_INPUT_PASTE) { + assert(event.paste.len == strlen("clipboard text")); + assert(memcmp(event.paste.text, "clipboard text", event.paste.len) == 0); + saw_paste = true; + } + } + assert(saw_key && saw_paste); + key.xclient = (XClientMessageEvent){.type = ClientMessage, .display = native, + .window = window, .message_type = XInternAtom(native, "WM_PROTOCOLS", False), + .format = 32, .data.l = {(long)XInternAtom(native, "WM_DELETE_WINDOW", False)}}; + XSendEvent(native, window, False, NoEventMask, &key); + XFlush(native); + pump(loop, d, NULL); + assert(w->closes == 1 && !w->surface); +} + +int main(int argc, char **argv) +{ + assert(argc >= 2); + struct yasl_loop *loop = yasl_loop_create(); + assert(loop); + if (strcmp(argv[1], "failure") == 0) { + struct yasl_display_spec spec = {.backend = (enum yasl_backend)atoi(argv[2])}; + assert(!yasl_display_open(loop, &spec)); + } else if (strcmp(argv[1], "mixed") == 0) { + struct yasl_display_spec spec = {.backend = YASL_BACKEND_WAYLAND}; + struct yasl_display *a = yasl_display_open(loop, &spec); + spec.backend = YASL_BACKEND_X11; + struct yasl_display *b = yasl_display_open(loop, &spec); + assert(a && b); + struct window wa = create(a), wb = create(b); + yasl_surface_set_callbacks(wa.surface, &callbacks, &wa); + yasl_surface_set_callbacks(wb.surface, &callbacks, &wb); + pump(loop, a, b); + assert(wa.draws && wb.draws); + yasl_surface_destroy(wa.surface); + yasl_surface_destroy(wb.surface); + yasl_display_close(a); + yasl_display_close(b); + } else { + assert(argc == 4); + struct yasl_display_spec spec = {.backend = (enum yasl_backend)atoi(argv[2])}; + struct yasl_display *d = strcmp(argv[1], "null") == 0 ? + yasl_display_open(loop, NULL) : yasl_display_open(loop, &spec); + assert(d); + assert(yasl_display_backend(d) == (enum yasl_backend)atoi(argv[3])); + struct window w = create(d); + pump(loop, d, NULL); + assert(w.draws == 0); + yasl_surface_set_callbacks(w.surface, &callbacks, &w); + yasl_surface_schedule_redraw(w.surface); + pump(loop, d, NULL); + assert(w.draws > 0); + if (yasl_display_backend(d) == YASL_BACKEND_X11) + x11_behavior(loop, d, &w); + if (w.surface) yasl_surface_destroy(w.surface); + w = create(d); + w.destroy_on_draw = true; + yasl_surface_set_callbacks(w.surface, &callbacks, &w); + pump(loop, d, NULL); + assert(w.draws && !w.surface); + yasl_display_close(d); + } + yasl_loop_destroy(loop); + puts("Backend test passed."); + return 0; +} diff --git a/tests/common/test_selection.c b/tests/common/test_selection.c new file mode 100644 index 0000000..d1b3eb8 --- /dev/null +++ b/tests/common/test_selection.c @@ -0,0 +1,34 @@ +#define _POSIX_C_SOURCE 200809L +#include "yasl/yasl.h" + +#include +#include +#include + +int main(void) +{ + struct yasl_display_spec spec = {.backend = (enum yasl_backend)999}; + errno = 0; + assert(!yasl_display_open(NULL, &spec)); + assert(errno == EINVAL); + assert(yasl_display_backend(NULL) == YASL_BACKEND_AUTO); +#if !YASL_HAS_COCOA + spec.backend = YASL_BACKEND_COCOA; + errno = 0; + assert(!yasl_display_open(NULL, &spec)); + assert(errno == ENOSYS); +#endif +#if !YASL_HAS_WAYLAND + spec.backend = YASL_BACKEND_WAYLAND; + errno = 0; + assert(!yasl_display_open(NULL, &spec)); + assert(errno == ENOSYS); +#endif +#if !YASL_HAS_X11 + spec.backend = YASL_BACKEND_X11; + errno = 0; + assert(!yasl_display_open(NULL, &spec)); + assert(errno == ENOSYS); +#endif + return 0; +} diff --git a/tests/helpers/run_backends.py b/tests/helpers/run_backends.py new file mode 100644 index 0000000..9ad9b0d --- /dev/null +++ b/tests/helpers/run_backends.py @@ -0,0 +1,93 @@ +#!/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 = 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") + 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 new file mode 100644 index 0000000..a78ae65 --- /dev/null +++ b/tests/meson.build @@ -0,0 +1,44 @@ +test_exe = executable( + 'yasl-test', + 'test_lifecycle.c', + include_directories: inc, + link_with: yasl_lib.get_static_lib(), +) + +add_test_setup('asan') +add_test_setup('tsan') +add_test_setup('msan') + +test('basic', test_exe) + +selection_test = executable( + 'yasl-selection-test', + 'common/test_selection.c', + dependencies: yasl_dep, +) +test('selection', selection_test) + +if get_option('integration_tests') + if has_cocoa + native_test = executable('yasl-native-test', 'cocoa/test_native.c', + dependencies: yasl_dep, + ) + test('native', native_test, timeout: 20) + else + if not has_wayland or not has_x11 + error('Linux integration tests require Wayland and X11.') + endif + backend_test = executable( + 'yasl-backend-test', + 'common/test_backends.c', + dependencies: yasl_dep, + ) + python = find_program('python3') + weston = find_program('weston') + xwayland = find_program('Xwayland') + test('backends', python, + args: [files('helpers/run_backends.py'), weston, xwayland, backend_test], + timeout: 60, + ) + endif +endif -- cgit v1.2.3