summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-11 23:51:43 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-11 23:51:43 +0300
commite813252e685d761235ad45e170d97c9375f51958 (patch)
treee57bae545c0eb22aac4119a890b8344ee7cd09d2
parent608b49052178ea74f593c925cdeedf352aa1c357 (diff)
parent6eac0a5f2c552728efca420b1129fae7d4fea773 (diff)
Merge branch 'backend-selection' into main
- Implement runtime backend selection (Wayland, X11, Cocoa) - Add native X11 backend with clipboard, keyboard, mouse, and placement - Integrate multi-backend integration tests running under Weston and Xwayland - Retain zero-hole struct packing, branchless ALU/LUT primitives, and paced event loop
-rw-r--r--README.md74
-rw-r--r--include/yasl/yasl.h13
-rw-r--r--meson.build94
-rw-r--r--meson.options3
-rw-r--r--src/cocoa/api.h48
-rw-r--r--src/cocoa/meson.build1
-rw-r--r--src/cocoa/window.m528
-rw-r--r--src/common/backend.c333
-rw-r--r--src/common/backend.h68
-rw-r--r--src/common/loop.c5
-rw-r--r--src/common/meson.build1
-rw-r--r--src/meson.build10
-rw-r--r--src/wayland/api.h48
-rw-r--r--src/wayland/backend.c237
-rw-r--r--src/wayland/buffer.c62
-rw-r--r--src/wayland/drag.c42
-rw-r--r--src/wayland/ime.c26
-rw-r--r--src/wayland/internal.h114
-rw-r--r--src/wayland/keyboard.c32
-rw-r--r--src/wayland/meson.build13
-rw-r--r--src/wayland/pointer.c99
-rw-r--r--src/wayland/popup.c23
-rw-r--r--src/wayland/shell.c46
-rw-r--r--src/wayland/surface.c105
-rw-r--r--src/x11/meson.build1
-rw-r--r--src/x11/window.c756
-rw-r--r--tests/cocoa/test_native.c58
-rw-r--r--tests/common/test_backends.c215
-rw-r--r--tests/common/test_selection.c34
-rw-r--r--tests/helpers/run_backends.py93
-rw-r--r--tests/meson.build45
31 files changed, 2800 insertions, 427 deletions
diff --git a/README.md b/README.md
index ee061b9..7a5f06d 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 c211a0b..6fea487 100644
--- a/include/yasl/yasl.h
+++ b/include/yasl/yasl.h
@@ -259,14 +259,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);
@@ -287,6 +298,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;
};
@@ -347,6 +359,7 @@ YASL_API void yasl_loop_destroy(struct yasl_loop *l);
YASL_API int yasl_loop_run(struct yasl_loop *l);
YASL_API int yasl_loop_step(struct yasl_loop *l, int timeout_ms);
YASL_API void yasl_loop_quit(struct yasl_loop *l);
+YASL_API bool yasl_loop_is_running(const struct yasl_loop *l);
YASL_API void yasl_loop_kick(struct yasl_loop *l);
YASL_API uint64_t yasl_loop_now_ns(void);
YASL_API int yasl_loop_add_fd(struct yasl_loop *l, int fd, short events,
diff --git a/meson.build b/meson.build
index 912b7c9..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,16 +103,4 @@ executable(
dependencies: deps,
)
-test_exe = executable(
- 'yasl-test',
- 'tests/test_lifecycle.c',
- include_directories: inc,
- link_with: yasl_lib.get_static_lib(),
- dependencies: deps,
-)
-
-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..2fea415
--- /dev/null
+++ b/src/cocoa/api.h
@@ -0,0 +1,48 @@
+#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);
+int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms);
+void cocoa_display_flush(struct cocoa_display *d);
+int cocoa_display_modifiers(const struct cocoa_display *d);
+void cocoa_display_copy(struct cocoa_display *d, enum yasl_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 451f448..8712ce8 100644
--- a/src/cocoa/window.m
+++ b/src/cocoa/window.m
@@ -2,7 +2,7 @@
#import <Metal/Metal.h>
#import <QuartzCore/QuartzCore.h>
-#include "yasl/yasl.h"
+#include "cocoa/api.h"
#include "yasl/timing.h"
#include "cocoa/keycodes.h"
@@ -27,18 +27,18 @@ enum {
};
@interface YaslView : NSView <NSTextInputClient>
-@property (nonatomic, assign) struct yasl_surface *surface;
+@property (nonatomic, assign) struct cocoa_surface *surface;
@end
@interface YaslAppDelegate : NSObject <NSApplicationDelegate>
@end
@interface YaslWindow : NSWindow
-@property (nonatomic, assign) struct yasl_surface *surface;
+@property (nonatomic, assign) struct cocoa_surface *surface;
@end
@interface YaslWindowController : NSWindowController <NSWindowDelegate>
-@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<MTLCommandQueue> 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)
@@ -338,7 +341,7 @@ static void push_pointer(struct yasl_surface *w, NSEvent *ev, enum yasl_pointer_
}
struct yasl_input_event input_ev = {
- .surface = w,
+ .surface = &w->base,
.kind = YASL_INPUT_MOUSE,
.mouse = {
.pos = w->pointer,
@@ -414,7 +417,7 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor)
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender
{
- struct yasl_surface *w = self.surface;
+ struct cocoa_surface *w = self.surface;
if (!w) return NO;
NSPasteboard *pb = [sender draggingPasteboard];
NSArray<NSURL *> *urls = [pb readObjectsForClasses:@[[NSURL class]]
@@ -431,11 +434,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;
@@ -476,7 +479,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;
}
@@ -484,16 +487,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;
@@ -530,13 +533,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;
@@ -546,14 +549,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);
@@ -599,7 +602,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) {
@@ -611,7 +614,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,
@@ -634,14 +637,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; }
@@ -651,28 +654,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);
@@ -688,7 +691,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;
@@ -732,13 +735,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--;
@@ -759,7 +762,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++;
@@ -791,31 +794,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<MTLBuffer> 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<MTLCommandBuffer> command = [w->software_queue commandBuffer];
+ id<MTLBlitCommandEncoder> blit = [command blitCommandEncoder];
+ if (!blit) { [buffer release]; return false; }
+ id<CAMetalDrawable> drawable = (id<CAMetalDrawable>)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))
@@ -836,7 +864,7 @@ static void render_and_commit(struct yasl_surface *w)
id<CAMetalDrawable> 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,
@@ -846,7 +874,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++;
@@ -856,7 +884,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;
@@ -865,7 +893,7 @@ static void pump_window(struct yasl_surface *w)
@autoreleasepool { render_and_commit(w); }
}
-int yasl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms)
+int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms)
{
assert([NSThread isMainThread]);
if (!d || d->pumping)
@@ -909,21 +937,21 @@ int yasl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms)
return count;
}
-void yasl_display_dispatch(struct yasl_display *d)
+void cocoa_display_dispatch(struct cocoa_display *d)
{
if (!d) return;
bool immediate = false;
for (int i = 0; i < d->count; i++) {
- struct yasl_surface *w = d->windows[i];
+ struct cocoa_surface *w = d->windows[i];
if (w->input.count > 0 || (w->want_draw && (!w->link || w->frame_due || w->unpaced))) {
immediate = true;
break;
}
}
- yasl_display_dispatch_timeout(d, immediate ? 0 : -1);
+ cocoa_display_dispatch_timeout(d, immediate ? 0 : -1);
}
-void yasl_display_flush(struct yasl_display *d)
+void cocoa_display_flush(struct cocoa_display *d)
{
assert([NSThread isMainThread]);
if (!d) return;
@@ -939,7 +967,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;
@@ -1001,12 +1029,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) {
@@ -1017,7 +1046,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");
@@ -1031,32 +1060,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);
@@ -1082,7 +1112,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<MTLDevice>)spec->graphics_device;
+ if (spec->graphics_device) {
+ w->layer.device = (id<MTLDevice>)spec->graphics_device;
+ } else {
+ id<MTLDevice> 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];
@@ -1108,9 +1156,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];
@@ -1120,17 +1169,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;
@@ -1169,19 +1218,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;
@@ -1194,7 +1243,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;
@@ -1207,7 +1256,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;
@@ -1221,23 +1270,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;
@@ -1248,7 +1297,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;
@@ -1257,44 +1306,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)
{
@@ -1302,34 +1351,309 @@ 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 : &empty;
+ 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 int impl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms)
+{
+ return cocoa_display_dispatch_timeout((struct cocoa_display *)d, timeout_ms);
+}
+
+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_dispatch_timeout = impl_display_dispatch_timeout,
+ .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..2220f1f
--- /dev/null
+++ b/src/common/backend.c
@@ -0,0 +1,333 @@
+#include "backend.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+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);
+}
+
+int yasl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms)
+{
+ if (!d) return -1;
+ if (d->ops->display_dispatch_timeout)
+ return d->ops->display_dispatch_timeout(d, timeout_ms);
+ if (d->ops->display_dispatch) {
+ d->ops->display_dispatch(d);
+ return 0;
+ }
+ return -1;
+}
+
+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..c1d878b
--- /dev/null
+++ b/src/common/backend.h
@@ -0,0 +1,68 @@
+#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);
+ int (*display_dispatch_timeout)(struct yasl_display *d, int timeout_ms);
+ 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/loop.c b/src/common/loop.c
index de29c85..ea9bee3 100644
--- a/src/common/loop.c
+++ b/src/common/loop.c
@@ -254,3 +254,8 @@ void yasl_loop_quit(struct yasl_loop *l)
{
if (l) l->running = false;
}
+
+bool yasl_loop_is_running(const struct yasl_loop *l)
+{
+ return l ? l->running : false;
+}
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..8252041
--- /dev/null
+++ b/src/wayland/api.h
@@ -0,0 +1,48 @@
+#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);
+int wayland_display_dispatch_timeout(struct wayland_display *d, int timeout_ms);
+void wayland_display_flush(struct wayland_display *d);
+int wayland_display_modifiers(const struct wayland_display *d);
+void wayland_display_copy(struct wayland_display *d, enum yasl_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..31e412f
--- /dev/null
+++ b/src/wayland/backend.c
@@ -0,0 +1,237 @@
+#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 int impl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms)
+{
+ return wayland_display_dispatch_timeout((struct wayland_display *)d, timeout_ms);
+}
+
+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_dispatch_timeout = impl_display_dispatch_timeout,
+ .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 0b6b028..76f6c0e 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;
@@ -321,7 +321,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;
};
@@ -335,7 +335,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);
}
@@ -352,7 +352,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);
@@ -377,7 +377,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;
@@ -395,7 +395,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);
@@ -438,7 +438,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 ||
@@ -456,7 +456,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;
@@ -483,7 +483,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 },
@@ -496,13 +496,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) {
@@ -518,7 +518,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);
@@ -527,7 +527,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 (unlikely(!w)) return 0;
return ((uint32_t)w->fullscreen * YASL_STATE_FULLSCREEN) |
@@ -537,7 +537,7 @@ uint32_t yasl_surface_state(const struct yasl_surface *w)
((uint32_t)!w->server_decorated * YASL_STATE_CSD);
}
-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,
@@ -549,7 +549,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 79fd88d..cba5096 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;
@@ -111,7 +111,7 @@ struct conn_drag {
#define SEAT_TOUCH_POINTS 10
struct touch_point {
- struct yasl_surface *surface;
+ struct wayland_surface *surface;
double x, y;
int32_t id;
bool active;
@@ -122,7 +122,7 @@ struct touch_point {
struct seat {
/* 8-byte aligned pointers and 64-bit values */
- struct yasl_display *conn;
+ struct wayland_display *conn;
struct seat *next;
struct wl_seat *proxy;
struct wl_keyboard *keyboard;
@@ -140,13 +140,13 @@ struct seat {
struct xkb_keymap *xkb_keymap;
struct xkb_state *xkb_state;
uint64_t repeat_started_ns;
- 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 yasl_surface *press_surface;
- struct yasl_surface *ptr_left;
+ 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;
+ struct wayland_surface *press_surface;
+ struct wayland_surface *ptr_left;
struct tablet_tool *tool;
struct touch_point *touch_primary;
uint64_t pointer_buttons;
@@ -223,7 +223,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;
/* 8-byte pointers & 64-bit masks */
struct wl_display *wl;
struct wl_registry *registry;
@@ -255,9 +256,9 @@ struct yasl_display {
char *app_id;
struct xdg_toplevel_drag_manager_v1 *toplevel_drag_mgr;
struct conn_drag drag;
- struct yasl_surface *surfaces;
- struct yasl_surface *paste_target;
- struct yasl_surface *dead;
+ struct wayland_surface *surfaces;
+ struct wayland_surface *paste_target;
+ struct wayland_surface *dead;
/* 4-byte integers */
int cursor_size;
@@ -273,12 +274,13 @@ struct yasl_display {
bool lost;
};
-struct yasl_surface {
+struct wayland_surface {
+ struct yasl_surface base;
/* 8-byte pointers & 64-bit values */
- struct yasl_display *conn;
- struct yasl_surface *conn_next;
- struct yasl_surface *dead_next;
- struct yasl_surface *parent;
+ 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;
@@ -348,12 +350,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)
@@ -368,8 +370,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)
{
@@ -377,65 +379,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 6045962..6bdd58c 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;
@@ -353,12 +353,12 @@ int seat_modifiers(const struct seat *s)
(mod_index_active(s, s->mod_super) * YASL_MOD_SUPER);
}
-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)
@@ -369,7 +369,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)
@@ -381,7 +381,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 5f97370..d434047 100644
--- a/src/wayland/pointer.c
+++ b/src/wayland/pointer.c
@@ -30,7 +30,7 @@ static inline int device_px_round(double v)
return (int)lrint(v);
}
-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){
@@ -85,23 +85,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){
@@ -109,7 +109,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){
@@ -118,7 +118,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;
@@ -135,7 +135,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);
@@ -146,10 +146,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;
@@ -206,7 +206,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)
@@ -232,7 +232,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) {
@@ -243,7 +243,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;
@@ -265,7 +265,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]))) {
@@ -277,7 +277,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)
@@ -306,7 +306,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;
@@ -319,7 +319,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);
@@ -335,7 +335,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)
@@ -352,7 +352,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;
@@ -364,7 +364,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 (unlikely(!w)) return;
static const int8_t btn_map[8] = {
@@ -381,7 +381,6 @@ static void pointer_button(void *data, struct wl_pointer *pointer,
if (unlikely(idx >= 7)) return;
int b = btn_map[idx];
if (unlikely(b < 0)) return;
-
seat_note_input(s, serial);
bool release = (state == WL_POINTER_BUTTON_STATE_RELEASED);
if (!release) {
@@ -443,7 +442,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;
@@ -466,7 +465,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;
@@ -490,7 +489,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)
@@ -507,7 +506,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;
@@ -575,7 +574,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;
@@ -604,7 +603,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++)
@@ -732,7 +731,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;
@@ -751,7 +750,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);
@@ -771,7 +770,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;
@@ -783,7 +782,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;
s->pen.pressure = 0;
press_queue_push(&s->tool_presses, w, s->tool_pos, 0, true);
@@ -793,7 +792,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;
@@ -804,7 +803,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;
@@ -851,7 +850,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;
@@ -1032,13 +1031,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) {
@@ -1067,7 +1066,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) {
@@ -1111,7 +1110,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;
@@ -1132,7 +1131,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);
@@ -1194,7 +1193,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));
@@ -1215,9 +1214,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,
@@ -1240,7 +1239,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;
@@ -1249,7 +1248,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);
@@ -1262,11 +1261,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);
@@ -1288,7 +1287,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) {
@@ -1299,7 +1298,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);
@@ -1308,7 +1307,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 bc8083a..10af712 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;
uint32_t state_mask = 0;
uint32_t *s;
@@ -318,13 +318,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);
}
}
@@ -343,13 +343,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 = {
@@ -360,7 +360,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)
@@ -371,7 +371,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;
@@ -380,7 +380,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 86f30a3..8de4992 100644
--- a/src/wayland/surface.c
+++ b/src/wayland/surface.c
@@ -1,9 +1,9 @@
#include "internal.h"
#include <assert.h>
-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);
@@ -98,7 +98,7 @@ static int display_ready(int fd, short revents, void *ud)
return 0;
}
-static bool display_arm(struct yasl_display *c)
+static bool display_arm(struct wayland_display *c)
{
bool had_pending = false;
while (!c->reading && wl_display_prepare_read(c->wl) != 0) {
@@ -123,10 +123,10 @@ static bool display_arm(struct yasl_display *c)
return had_pending;
}
-static inline bool display_has_immediate_work(const struct yasl_display *c)
+static inline bool display_has_immediate_work(const struct wayland_display *c)
{
if (unlikely(!c || c->lost)) return false;
- for (struct yasl_surface *w = c->surfaces; w; w = w->conn_next) {
+ for (struct wayland_surface *w = c->surfaces; w; w = w->conn_next) {
bool ready_draw = w->want_draw & (w->frame_cb == NULL) & w->configured;
bool has_input = (w->input.count > 0);
if (unlikely((ready_draw | has_input) & !w->closed))
@@ -135,7 +135,7 @@ static inline bool display_has_immediate_work(const struct yasl_display *c)
return false;
}
-int yasl_display_dispatch_timeout(struct yasl_display *c, int timeout_ms)
+int wayland_display_dispatch_timeout(struct wayland_display *c, int timeout_ms)
{
if (!c || c->lost)
return -1;
@@ -177,20 +177,20 @@ int yasl_display_dispatch_timeout(struct yasl_display *c, int timeout_ms)
return n;
}
-void yasl_display_dispatch(struct yasl_display *c)
+void wayland_display_dispatch(struct wayland_display *c)
{
- if (!c || c->lost)
+ if (unlikely(!c || c->lost))
return;
- int timeout = display_has_immediate_work(c) ? 0 : -1;
- yasl_display_dispatch_timeout(c, timeout);
+ int timeout = (display_has_immediate_work(c) || yasl_loop_is_running(c->loop)) ? 0 : -1;
+ wayland_display_dispatch_timeout(c, timeout);
}
-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);
}
@@ -201,7 +201,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);
@@ -237,10 +237,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;
if (!loop) {
c->loop = yasl_loop_create();
c->owns_loop = true;
@@ -277,14 +278,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;
@@ -299,18 +300,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;
@@ -323,9 +325,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;
@@ -338,18 +340,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;
@@ -387,12 +389,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};
@@ -401,10 +403,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);
@@ -423,10 +425,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,
@@ -440,12 +443,12 @@ 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->closed) return;
w->want_draw = true;
@@ -453,22 +456,22 @@ void yasl_surface_schedule_redraw(struct yasl_surface *w)
yasl_loop_kick(w->conn->loop);
}
-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);
@@ -477,7 +480,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);
@@ -486,7 +489,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);
@@ -498,12 +501,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..4680d3f
--- /dev/null
+++ b/src/x11/window.c
@@ -0,0 +1,756 @@
+#define _POSIX_C_SOURCE 200809L
+#include "common/backend.h"
+
+#include <X11/Xatom.h>
+#include <X11/XKBlib.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+#include <xkbcommon/xkbcommon.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define INPUT_CAP 256
+#define TEXT_LIMIT (16 * 1024 * 1024)
+
+#ifndef likely
+#define likely(x) __builtin_expect(!!(x), 1)
+#endif
+#ifndef unlikely
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#endif
+
+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;
+ bool owns_loop;
+};
+
+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 (unlikely(!mask)) return 0;
+ unsigned shift = (unsigned)__builtin_ctzl(mask);
+ unsigned long m = mask >> shift;
+ return (((unsigned long)color * m + 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 int dispatch_timeout(struct yasl_display *base, int timeout_ms)
+{
+ struct x11_display *d = (struct x11_display *)base;
+ if (unlikely(!d || !d->native)) return -1;
+ dispatch(base);
+ if (!XPending(d->native) && d->loop && timeout_ms != 0) {
+ yasl_loop_step(d->loop, timeout_ms);
+ dispatch(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);
+ if (!loop) {
+ d->loop = yasl_loop_create();
+ d->owns_loop = true;
+ } else {
+ 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 (d->loop) {
+ d->watch = yasl_loop_add_fd(d->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);
+ if (d->owns_loop) yasl_loop_destroy(d->loop);
+ 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 && d->watch > 0) yasl_loop_del_fd(d->loop, d->watch);
+ if (d->owns_loop && d->loop) yasl_loop_destroy(d->loop);
+ 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;
+ struct x11_display *d = display_of(base);
+ if (d && d->loop)
+ yasl_loop_kick(d->loop);
+ }
+}
+
+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_dispatch_timeout = dispatch_timeout,
+ .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 <assert.h>
+#include <time.h>
+
+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 <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+#include <assert.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/timerfd.h>
+#include <time.h>
+#include <unistd.h>
+
+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 <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+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..950f5f5
--- /dev/null
+++ b/tests/meson.build
@@ -0,0 +1,45 @@
+test_exe = executable(
+ 'yasl-test',
+ 'test_lifecycle.c',
+ include_directories: inc,
+ link_with: yasl_lib.get_static_lib(),
+ dependencies: deps,
+)
+
+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