summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBerke Güzel <wenekar1@gmail.com>2026-10-13 23:41:31 +0300
committerBerke Güzel <wenekar1@gmail.com>2026-10-13 23:41:31 +0300
commit76073fe5fad8799e5850805f2f090af2cb62c741 (patch)
tree9658019babe4760a3634bdab4da0731a10e42e68
parentdd1775a9927922c14f8679ccbc4d3ca8d5917e20 (diff)
Simplify the public event and CPU API
Applications now need far less ceremony: hello.c is a 20-line loop that handles REFRESH, CLOSE and QUIT and draws into the surface's own pixels. - Fold GEOMETRY, STATE and FOCUS into one WINDOW event carrying size, scale and state; unchanged values send nothing. - Guarantee REFRESH once a window has a size and after every resize, never at size zero. - Merge DROP_HOVER into DROP with over/leave/data actions and give drag and drop actions enums. - Keep event strings valid until the next wait and drop yasl_event_release; empty or failed waits report NONE. - Replace copy-in presentation with a lent pixel buffer: yasl_cpu_pixels and yasl_cpu_present. - Remove yasl_poll_event, yasl_cpu_read_pixels, unused event fields and comments that restated names. - Wayland: resize floating windows in request_size instead of reporting it unsupported, and stop marking new GPU surfaces as Vulkan so yasl_vulkan_create_surface no longer fails with NATIVE_WINDOW_IN_USE. Also includes the in-progress public API refactor that was uncommitted in the tree (removal of the app/loop layer, new surface, GL and Vulkan entry points). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--ARCHITECTURE_PLAN.md89
-rw-r--r--CONTRIBUTING.md64
-rw-r--r--IDE_INTEGRATION.md198
-rw-r--r--README.md83
-rw-r--r--docs/api-policy.md48
-rw-r--r--docs/api-redesign.md147
-rw-r--r--docs/getting-started.md138
-rw-r--r--docs/guide.md617
-rw-r--r--docs/migrating.md132
-rw-r--r--docs/testing.md166
-rw-r--r--examples/game.c403
-rw-r--r--examples/hello.c54
-rw-r--r--examples/notepad.c552
-rw-r--r--examples/simple_window.c232
-rw-r--r--examples/tablet.c419
-rw-r--r--include/yasl/app.h147
-rw-r--r--include/yasl/cpu.h15
-rw-r--r--include/yasl/desktop.h116
-rw-r--r--include/yasl/gl.h27
-rw-r--r--include/yasl/keycodes.h271
-rw-r--r--include/yasl/metal.h20
-rw-r--r--include/yasl/native/cocoa.h48
-rw-r--r--include/yasl/native/wayland.h37
-rw-r--r--include/yasl/native/x11.h36
-rw-r--r--include/yasl/vulkan.h19
-rw-r--r--include/yasl/yasl.h1203
-rw-r--r--meson.build30
-rw-r--r--src/common/app.c153
-rw-r--r--src/common/app_internal.h9
-rw-r--r--src/common/backend.c315
-rw-r--r--src/common/backend.h210
-rw-r--r--src/common/desktop.c30
-rw-r--r--src/common/desktop.h4
-rw-r--r--src/common/events.c196
-rw-r--r--src/common/gl_options.c38
-rw-r--r--src/common/gl_options.h20
-rw-r--r--src/common/input.c51
-rw-r--r--src/common/input.h20
-rw-r--r--src/common/keyboard.h6
-rw-r--r--src/common/loop.h14
-rw-r--r--src/common/meson.build6
-rw-r--r--src/common/native.c50
-rw-r--r--src/common/private_desktop.h113
-rw-r--r--src/common/private_loop.h (renamed from include/yasl/loop.h)90
-rw-r--r--src/common/private_native.h (renamed from include/yasl/native.h)36
-rw-r--r--src/common/private_native_cocoa.h55
-rw-r--r--src/common/private_native_wayland.h40
-rw-r--r--src/common/private_native_x11.h40
-rw-r--r--src/common/private_timing.h (renamed from include/yasl/timing.h)24
-rw-r--r--src/common/public_internal.h12
-rw-r--r--src/common/surface.c522
-rw-r--r--src/common/timing.c16
-rw-r--r--src/common/timing.h28
-rw-r--r--src/common/types.h1101
-rw-r--r--src/common/uri.c4
-rw-r--r--src/common/utf8.c10
-rw-r--r--src/common/utf8.h12
-rw-r--r--src/common/vulkan.c87
-rw-r--r--src/platform/cocoa/api.h42
-rw-r--r--src/platform/cocoa/desktop.m8
-rw-r--r--src/platform/cocoa/keymap.c2
-rw-r--r--src/platform/cocoa/keymap.h6
-rw-r--r--src/platform/cocoa/window.m1072
-rw-r--r--src/platform/freedesktop/dbus.h4
-rw-r--r--src/platform/freedesktop/desktop.c12
-rw-r--r--src/platform/posix/loop.c156
-rw-r--r--src/platform/posix/yasl_platform_sync.h14
-rw-r--r--src/platform/sync.h8
-rw-r--r--src/platform/wayland/api.h50
-rw-r--r--src/platform/wayland/backend.c236
-rw-r--r--src/platform/wayland/buffer.c93
-rw-r--r--src/platform/wayland/clipboard.c60
-rw-r--r--src/platform/wayland/clipboard.h16
-rw-r--r--src/platform/wayland/drag.c54
-rw-r--r--src/platform/wayland/ime.c147
-rw-r--r--src/platform/wayland/internal.h70
-rw-r--r--src/platform/wayland/keyboard.c60
-rw-r--r--src/platform/wayland/keymap.c2
-rw-r--r--src/platform/wayland/keymap.h6
-rw-r--r--src/platform/wayland/native.c14
-rw-r--r--src/platform/wayland/pointer.c398
-rw-r--r--src/platform/wayland/popup.c79
-rw-r--r--src/platform/wayland/shell.c50
-rw-r--r--src/platform/wayland/surface.c119
-rw-r--r--src/platform/x11/keymap.c2
-rw-r--r--src/platform/x11/keymap.h6
-rw-r--r--src/platform/x11/native.c14
-rw-r--r--src/platform/x11/window.c1174
-rw-r--r--tests/benchmark.c150
-rw-r--r--tests/cocoa/test_cursor.m24
-rw-r--r--tests/cocoa/test_keyboard.m76
-rw-r--r--tests/cocoa/test_native.c70
-rw-r--r--tests/cocoa/test_ownership.m154
-rw-r--r--tests/cocoa/test_tablet.m111
-rw-r--r--tests/common/test_app.c204
-rw-r--r--tests/common/test_backends.c302
-rw-r--r--tests/common/test_contracts.c300
-rw-r--r--tests/common/test_desktop.c72
-rw-r--r--tests/common/test_events.c169
-rw-r--r--tests/common/test_gl.c158
-rw-r--r--tests/common/test_gl_options.c16
-rw-r--r--tests/common/test_hello_example.c20
-rw-r--r--tests/common/test_input_alloc.c14
-rw-r--r--tests/common/test_keyboard.c44
-rw-r--r--tests/common/test_loop.c76
-rw-r--r--tests/common/test_selection.c20
-rw-r--r--tests/common/test_surface_integration.c90
-rw-r--r--tests/common/test_tablet_example.c65
-rw-r--r--tests/common/test_timers.c148
-rw-r--r--tests/common/test_utf8.c22
-rw-r--r--tests/common/test_vulkan.c389
-rw-r--r--tests/helpers/run_surfaces.py82
-rw-r--r--tests/meson.build348
-rw-r--r--tests/test_api.cpp95
-rw-r--r--tests/test_lifecycle.c91
-rw-r--r--tests/wayland/test_app_input.c174
-rw-r--r--tests/wayland/test_clipboard.c32
-rw-r--r--tests/wayland/test_cursor.c30
-rw-r--r--tests/wayland/test_input.c59
-rw-r--r--tests/wayland/test_keyboard.c63
-rw-r--r--tests/wayland/test_popup_positioner.c93
-rw-r--r--tests/wayland/test_relative_pointer.c30
-rw-r--r--tests/wayland/test_state.c61
-rw-r--r--tests/wayland/test_tablet.c115
-rw-r--r--tests/x11/inputtest-protocol.h4
-rw-r--r--tests/x11/test_input.c148
-rw-r--r--tests/x11/test_state.c18
-rw-r--r--tests/x11/test_tablet.c210
-rw-r--r--tests/x11/test_tablet_native.c58
-rw-r--r--yasl-gaps-report.md179
130 files changed, 9122 insertions, 8059 deletions
diff --git a/ARCHITECTURE_PLAN.md b/ARCHITECTURE_PLAN.md
deleted file mode 100644
index f2ec38c..0000000
--- a/ARCHITECTURE_PLAN.md
+++ /dev/null
@@ -1,89 +0,0 @@
-# YASL Modern Architecture Plan
-
-This document outlines the transition of YASL to **Approach A: Inverted Lifecycle & Unified Event Architecture**, designed specifically to serve both high-performance game development and reactive desktop applications (IDEs and editors like `examples/notepad.c`).
-
----
-
-## 1. Motivation & Core Goals
-
-### The Problem in YASL 0.1
-1. **Split-Brain Event Model**: Window lifecycle and drawing use callbacks, but input events are queued in a surface ring buffer requiring a manual `dispatch -> poll -> flush` pump.
-2. **Platform Live-Resize Quirks**: On macOS (`inLiveResize`) and Wayland (`xdg_surface.configure`), drawing must be strictly synchronized to the OS event to avoid freezing, visual stutter, or black borders.
-3. **Game vs. IDE Divergence**:
- - Games need continuous, VSync-paced ticks (60/120/144+ Hz) and direct GPU presentation (Metal/Vulkan).
- - IDEs need reactive, on-demand redraws (0% CPU when idle), rich text input (IME, UTF-8, autorepeat), multi-window support, and cross-thread communication (LSP/file watchers).
-4. **Stack Overhead**: Presenting a CPU frame currently requires passing a 1,044-byte stack struct (`yasl_damage_list`).
-
----
-
-## 2. Architecture Overview
-
-```
-┌────────────────────────────────────────────────────────┐
-│ Layer 2: Specialized Helpers (Opt-in) │
-│ - File Descriptor Watcher (LSP, Sockets, Pipes) │
-│ - Cross-Thread Event Dispatcher (yasl_post_event) │
-│ - Mouse Relative Mode (Pointer Lock for 3D/Games) │
-├────────────────────────────────────────────────────────┤
-│ Layer 1: Unified App Runner (include/yasl/app.h) │
-│ - yasl_run(const struct yasl_app_desc *desc) │
-│ - Dual-mode pacing: YASL_PACING_CONTINUOUS vs ON_DEMAND│
-│ - Decoupled frame target (CPU software vs GPU native) │
-├────────────────────────────────────────────────────────┤
-│ Layer 0: Core Native Surfaces & Dispatchers │
-│ - Wayland / X11 / Cocoa display & surface backends │
-└────────────────────────────────────────────────────────┘
-```
-
----
-
-## 3. Phased Execution Plan
-
-### Phase 1: Define Public Interface (`include/yasl/app.h` & modernized types)
-1. **Unified Event Pipeline (`yasl_event`)**:
- - Merge input and window events into a single enum:
- - `YASL_EVENT_KEY`, `YASL_EVENT_TEXT`
- - `YASL_EVENT_MOUSE_MOTION`, `YASL_EVENT_MOUSE_BUTTON`, `YASL_EVENT_MOUSE_SCROLL`
- - `YASL_EVENT_WINDOW_RESIZE`, `YASL_EVENT_WINDOW_SCALE_CHANGED`, `YASL_EVENT_WINDOW_CLOSE_REQUEST`, `YASL_EVENT_WINDOW_FOCUS`
- - `YASL_EVENT_USER` (custom payloads from background threads)
- - Add `bool repeat` to `yasl_key_event` to distinguish game keypresses from editor autorepeat.
-2. **Dual-Target Frame Context (`yasl_frame_context`)**:
- - Provide `ctx->cpu` (software buffer) and `ctx->gpu` (native layer/surface).
- - Lightweight presentation: `yasl_frame_present_cpu(ctx, dirty_rects, count)` where `NULL` dirty rects indicate full presentation without a 1 KB stack structure.
-3. **Pacing Modes**:
- - `YASL_PACING_CONTINUOUS`: Drives `frame()` every display refresh for games.
- - `YASL_PACING_ON_DEMAND`: Sleeps at 0% CPU when idle; wakes on events, timers, or `yasl_surface_request_frame()`.
-4. **App Descriptor & Runner**:
- - Define `struct yasl_app_desc` with `init`, `event`, `frame`, and `quit`.
- - Declare `int yasl_run(const struct yasl_app_desc *desc);`.
-
-### Phase 2: Implement Core Runner & Cross-Thread Messaging (`src/common/app.c`)
-1. **The Lifecycle Pump (`yasl_run`)**:
- - Initialize display and event loop, invoke `desc->init()`.
- - Run the unified event loop:
- - Drain native inputs and dispatch clean `const yasl_event *` into `desc->event()`.
- - Automatically synchronize macOS live-resize (`inLiveResize`) and Wayland configure passes to call `desc->frame()`.
- - Regulate pacing: continuous ticks for games vs. reactive idle sleep for desktop apps.
-2. **Cross-Thread Event Posting (`yasl_post_event`)**:
- - Implement thread-safe event enqueueing backed by a mutex-protected ringbuffer and loop wake pipe (`yasl_loop_kick`).
- - Allows background threads (LSP, compilers, physics, asset loaders) to post `YASL_EVENT_USER` safely to the main thread.
-
-### Phase 3: Add Key Capabilities for Games & Desktop UI
-1. **Relative Mouse Mode (`yasl_surface_set_mouse_mode`)**:
- - Implement `YASL_MOUSE_RELATIVE` (pointer lock / virtualized motion deltas) for games and 3D viewports.
-2. **Background File Descriptor Watching (`yasl_app_watch_fd`)**:
- - Register pipes/sockets (e.g., LSP stdin/stdout) directly with the main loop.
-
-### Phase 4: Verification, Examples & Testing
-1. **`examples/game.c`**:
- - Continuous 60/144 FPS game loop, animated graphics, raw WASD input, and relative mouse control.
-2. **`examples/notepad.c`**:
- - Comprehensive text editor example demonstrating YASL's capabilities:
- - Reactive rendering (0% CPU at idle, redrawing only when dirty).
- - UTF-8 text insertion, backspace, enter, and cursor navigation with autorepeat.
- - Blinking text caret driven by a timer.
- - Scale-aware text rendering and dynamic window resizing.
- - Background async task (e.g., word count / syntax check / LSP task) posting results via `yasl_post_event`.
-3. **Automated Tests**:
- - Add test suites in `tests/` verifying `yasl_run`, event propagation, cross-thread posting, and pacing modes.
- - Verify all 18 existing backend tests pass without regressions.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 788425e..7e31c97 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,57 +1,27 @@
# Contributing
-YASL focuses on desktop surfaces for applications that own their renderer.
-Read the [API policy](docs/api-policy.md) and [application guide](docs/guide.md)
-before changing public behavior. Keep backend details in native extensions and
-make common operations usable through the portable headers.
+YASL is a small desktop surface/input library for application-owned loops and
+renderers. Read [the guide](docs/guide.md) and [API policy](docs/api-policy.md).
+Keep public C APIs direct: opaque surface pointers, plain IDs/numeric arguments,
+and one tagged event. Avoid new descriptor/geometry/string/handle helper structs,
+application runners, graphics engines, or hidden presentation policy.
-Shared API behavior and backend interfaces live in `src/common/`. Native
-windowing implementations, key mappings, and typed native accessors live in
-`src/platform/<backend>/`; keep their generated protocols with the backend.
-OS support shared by multiple backends lives separately in `src/platform/posix/`
-and is selected by `src/platform/meson.build`. It currently supplies the event
-loop and the private mutex implementation used through `platform/sync.h`.
-Linux desktop services shared by Wayland and X11, such as xdg-open and the
-D-Bus session bus, live in `src/platform/freedesktop/`; Cocoa keeps its own in
-`src/platform/cocoa/desktop.m`. Both implement `src/common/desktop.h`.
-Common code should depend on these internal interfaces rather than native OS
-headers. Keep public headers in `include/yasl/` and tests in `tests/<backend>/`.
+Portable lifetime, event ownership, validation, and rendering bridges live in
+src/common. Native implementations live in src/platform/wayland, x11, and cocoa.
+Private backend geometry/frame/loop types are implementation details, not installed
+API. Linux desktop services live in src/platform/freedesktop; POSIX synchronization
+and waiting live in src/platform/posix. Keep native handles typed in extension headers.
-For a future Win32 backend, add `src/platform/win32/`, select its event loop and
-`yasl_platform_sync.h` in the platform build, and register its backend through
-the common dispatch interface. Windows still needs build/dependency selection,
-public API/export support, and ports of POSIX-based examples and test harnesses;
-the directory structure alone does not provide Windows support.
-
-For a bug report, include the revision, operating system, selected backend from
-`yasl_display_backend()`, capability mask, compiler, build options, reproduction
-steps, and a small example if possible. For input issues include the relevant
-event fields, layout/device/driver, and whether the issue occurs on another
-backend. Report observations separately from expected behavior.
-
-Build and test with:
+Document public units, ownership, errors, thread requirements, callback timing,
+and unsupported behavior. Add useful behavior coverage and examples. Run:
```sh
-meson setup build
meson compile -C build
meson test -C build --print-errorlogs
```
-See [testing](docs/testing.md) for isolated Linux integration tests, sanitizer
-builds, and native tablet tests. Changes to shared behavior need both backend
-configurations checked. Cocoa changes need a macOS desktop build/test; Linux
-success alone does not validate them. Keep changes focused and describe any
-platforms that were not exercised.
-
-Document public behavior in the public header first: argument validity,
-units, ownership/lifetime, return/error behavior, thread requirements, callback
-rules, and unsupported results. Update guides/examples when the workflow
-changes. Use zero-initialized specs and check fallible operations in examples.
-Build the reference with `doxygen Doxyfile`; warnings are errors. Do not edit
-vendored generated protocol files for unrelated changes.
-
-Use the existing C style and `yasl_`/`YASL_` public names. Run the tests relevant
-to the changed behavior, including shared exports and public C/C++ compilation.
-For a pull request, explain the observable problem, resulting behavior, and
-validation, including any native tests still needed. Contributions use the
-project's MIT license.
+Changes to portable/native behavior need both Linux backends and installed shared/
+static consumers checked. Use the isolated GPU suite described in [testing](docs/testing.md).
+Cocoa changes require a native macOS build/test; disclose platforms not exercised.
+Do not edit generated protocol files for unrelated changes. Check exact exports and
+public C/C++ compilation. Contributions use the project's MIT license.
diff --git a/IDE_INTEGRATION.md b/IDE_INTEGRATION.md
deleted file mode 100644
index 67ba96e..0000000
--- a/IDE_INTEGRATION.md
+++ /dev/null
@@ -1,198 +0,0 @@
-# IDE integration
-
-YASL includes Zed debugger profiles for stepping from example applications into
-the library's display and window creation code. Open the repository root (the
-directory containing `meson.build`) as your Zed project. The shared configuration
-uses `build/` for the build directory on Linux and macOS.
-
-## Prerequisites
-
-Install Zed, a C23 compiler, Meson 1.6 or newer, Ninja, pkg-config, and Python 3.
-A C++ compiler enables the optional public-header compatibility test. Install
-the backend development dependencies described in
-[Getting started](docs/getting-started.md#build-and-run): at least one of Wayland
-or X11 on Linux, or the macOS 14+ SDK on macOS. Running windows on macOS also
-requires a Metal-capable device.
-
-[Zed's C support](https://zed.dev/docs/languages/c) includes clangd for code
-completion, diagnostics, and navigation, and CodeLLDB for debugging. Allow Zed
-to obtain these tools when prompted, or configure your existing installations
-in your personal Zed settings. Use a recent clangd with C23 support.
-
-Make sure `meson`, `ninja`, your compiler, `pkg-config`, and `python3` are on the
-PATH visible to Zed's terminal. You can open the project from an initialized
-terminal with `zed .` when Zed's CLI is installed. On macOS, if your toolchain
-defaults to an older deployment target, launch from a terminal with
-`MACOSX_DEPLOYMENT_TARGET=14.0` (or newer) exported before configuring.
-
-## Start debugging window creation
-
-1. Save your changes and open `examples/hello.c`.
-2. Set gutter breakpoints on the calls to `yasl_display_open()` and
- `yasl_surface_create_toplevel()` in `main()`.
-3. Open the command palette (`Ctrl+Shift+P` on Linux, `Cmd+Shift+P` on macOS),
- run `debugger: start`, and choose **YASL: debug hello**.
-4. The debugger prepares the build and pauses on entry. Continue to the display
- creation breakpoint, then use **Step Into** to enter YASL's implementation in
- `src/common/backend.c`. Continue to the surface creation breakpoint and step
- into `yasl_surface_create_toplevel()` and `yasl_create_toplevel()`.
-5. Step into `d->ops->surface_create_toplevel(...)` to reach native window
- creation. Inspect `spec`, `d`, local variables, and the call stack in Zed's
- debug panel. Use **Step Over** for calls you want to execute without entering.
-
-Every debug profile runs **YASL: build for debugger** before launching. This
-task creates or reconfigures `build/` with debug symbols, optimization disabled,
-and LTO disabled, then builds the library and examples. It explicitly sets
-`buildtype=debug`, `debug=true`, `optimization=0`, and `b_lto=false`, including
-when `build/` was previously a release or customized build. Other Meson options,
-including backend selection, are preserved. Both the example and YASL have
-source-level debug information; the shared-library examples load the library
-from the Meson build tree.
-
-Profiles pause on entry through CodeLLDB's `stopOnEntry` setting, so initialization
-does not run before you can inspect the session. To launch directly to your
-breakpoints instead, set `"stopOnEntry": false` in the desired profile. See
-[CodeLLDB's launch settings](https://github.com/vadimcn/codelldb/blob/master/MANUAL.md#launching-a-new-process).
-
-Native creation follows the backend selected at runtime:
-
-| Backend | Window creation implementation |
-| --- | --- |
-| Wayland | `wayland_surface_create_toplevel()` in `src/platform/wayland/surface.c` |
-| X11 | `create_toplevel()` in `src/platform/x11/window.c` |
-| Cocoa | `cocoa_surface_create_toplevel()` in `src/platform/cocoa/window.m` |
-
-You can also put a source breakpoint directly in these functions before
-launching. A breakpoint in a backend that is disabled or not selected will not
-be hit. In the notepad and game examples, `yasl_run()` opens the display before
-calling the example's initialization callback; set a breakpoint in `yasl_run()`
-in `src/common/app.c` to follow that path.
-
-## Configure and build without launching
-
-1. Open the command palette (`Ctrl+Shift+P` on Linux, `Cmd+Shift+P` on macOS).
-2. Run `task: spawn`, then select **YASL: configure debug build**.
-3. Run **YASL: build** from the same task picker.
-4. Open `examples/hello.c` or a file in `src/` to use completion and navigation.
-
-The configure task runs:
-
-```sh
-meson setup build --backend=ninja --buildtype=debug
-```
-
-Meson's Ninja backend writes `build/compile_commands.json`. The root
-[.clangd](.clangd) points clangd at that directory, so it uses Meson's actual
-include paths, C standard, and enabled-backend definitions. Keep the generated
-database in the ignored build directory; no root-level copy or symlink is
-needed. The same configuration works with other clangd-based editors. See
-[clangd's compilation database setting](https://clangd.llvm.org/config#compilationdatabase).
-
-If `build/` is already configured, the configure task leaves its existing
-options in place. To change an existing build to debug, run this in Zed's
-terminal:
-
-```sh
-meson configure build -Dbuildtype=debug
-```
-
-Save edited files before building, running, or debugging. Use **YASL:
-reconfigure** to rerun Meson configuration and refresh its metadata. Meson also
-regenerates the build automatically when build definitions change.
-
-## Tasks and examples
-
-The project tasks live in [.zed/tasks.json](.zed/tasks.json). Select them with
-`task: spawn`; `task: rerun` repeats the last task. See
-[Zed's task documentation](https://zed.dev/docs/tasks) for personal keybindings.
-
-| Task | Behavior |
-| --- | --- |
-| YASL: configure debug build | Set up `build/` with Ninja and debug symbols. |
-| YASL: reconfigure | Reconfigure an existing `build/`, preserving its options. |
-| YASL: build | Compile the library, examples, and enabled tests. |
-| YASL: build for debugger | Configure symbols and disable optimization/LTO, then build. |
-| YASL: test | Build as needed and run enabled tests, printing failure logs. |
-| YASL: benchmark | Build as needed and run Meson's registered benchmarks. |
-| YASL: run hello | Build and launch `yasl-hello`. |
-| YASL: run simple window | Build and launch `yasl-example`. |
-| YASL: run tablet | Build and launch `yasl-tablet`. |
-| YASL: run notepad | Build and launch `yasl-notepad`. |
-| YASL: run game | Build and launch `yasl-game`. |
-
-Each run task builds its executable before launching and stops if compilation
-fails. Run tasks explicitly use `sh` for their compound command. All tasks use
-the repository root as their working directory, including when a nested source
-file is active.
-
-Examples need a desktop connection. On Linux, run Zed in your desktop session
-so the launched program inherits `WAYLAND_DISPLAY`/`DISPLAY` and
-`XDG_RUNTIME_DIR` as appropriate. Most tests can run without a display, but the
-currently registered `app-runner` test opens a window even in a default build.
-`app-input-destroy` also needs a Wayland compositor when `WAYLAND_DISPLAY` is
-set. Run the full suite in a desktop session with the corresponding backend
-enabled; additional tests retain their configured requirements.
-
-## Debugging
-
-The profiles in [.zed/debug.json](.zed/debug.json) use CodeLLDB. Run
-`debugger: start` from the command palette and select an example. Profiles cover
-hello, simple window, tablet, notepad, game, and the lifecycle test
-(`build/tests/yasl-test`), which needs no display. Each profile builds through
-**YASL: build for debugger** and pauses on entry.
-See [Zed's debugger documentation](https://zed.dev/docs/debugger).
-
-To debug another test, copy the lifecycle profile and change its `label` and
-`program` to the executable under `build/tests/`. To pass example arguments,
-add an `args` array to a debug profile; for example, the tablet profile accepts
-`"args": ["--backend", "x11"]` when that backend is enabled.
-
-## Backend options and additional builds
-
-Linux backend selection defaults to automatic dependency detection. To require
-X11 and disable Wayland in the IDE build, run:
-
-```sh
-meson configure build -Dx11=enabled -Dwayland=disabled
-meson compile -C build
-```
-
-Restore automatic selection with `-Dx11=auto -Dwayland=auto`. Integration tests
-are opt-in through `-Dintegration_tests=true`; their server dependencies and
-desktop requirements are documented in [Testing](docs/testing.md).
-
-For sanitizers or a different compiler, prefer a separate build directory:
-
-```sh
-meson setup build-sanitize -Db_sanitize=address,undefined
-meson test -C build-sanitize --print-errorlogs
-```
-
-The checked-in Zed tasks and profiles continue to target `build/`. To use a
-different directory throughout the IDE, update the tasks' build arguments and
-run commands, debugger program paths, and `.clangd`'s `CompilationDatabase`
-together. Ignore additional build directories locally (for example through
-`.git/info/exclude`) and keep machine-specific tool paths out of shared files.
-
-## Troubleshooting
-
-- **Missing headers or incorrect backend diagnostics:** confirm that
- `build/compile_commands.json` exists, build successfully, then run
- `editor: restart language server`. clangd analyzes the backend configuration
- in that database; disabled native backends may lack their SDK headers.
-- **C23 syntax errors in the editor:** update clangd; the compiler used by Meson
- and the clangd used by Zed are separate tools.
-- **A task cannot find Meson or a compiler:** check `command -v meson ninja cc`
- in Zed's terminal and fix the PATH used by Zed and its task shell.
-- **No window opens:** check the desktop environment and enabled backends.
- Use the lifecycle debug profile to check debugging without a display.
-- **`app-runner` or `app-input-destroy` fails at startup:** check that the test
- can connect to the inherited desktop session. For a headless check of the
- lifecycle test alone, run `meson test -C build basic --print-errorlogs`.
-- **Breakpoints do not bind:** use a **YASL: debug ...** profile so the debugger
- build task runs, and confirm the executable and library come from `build/`.
- Shared-library breakpoints may remain pending until the library loads. Check
- that a native backend breakpoint belongs to the enabled, selected backend.
-- **Configuration fails on macOS:** check the SDK and deployment target against
- [Getting started](docs/getting-started.md#build-and-run). Compiler or SDK
- changes may require a fresh build directory.
diff --git a/README.md b/README.md
index 1fea452..d486b80 100644
--- a/README.md
+++ b/README.md
@@ -1,66 +1,45 @@
# YASL — Yet Another Surface Library
-YASL is a C desktop surface library for applications that own their renderer.
-It creates windows and supplies CPU pixel targets, scale-aware geometry, queued
-input, clipboard text, popups, surface drags, and optional frame pacing.
-It runs on Wayland, X11, and macOS Cocoa.
-
-A software-rendered editor, terminal, drawing tool, or custom interface can open
-a window and paint pixels without creating an OpenGL context or adopting a
-widget toolkit. YASL also exposes tablet pressure/tilt and IME composition.
-It does not provide fonts, widgets, audio, or a graphics engine. Native handles
-and Cocoa/Metal rendering are separate extensions.
-
-**Status:** pre-release 0.1; source and ABI compatibility are not yet guaranteed.
-Physical keys have portable names, committed text arrives as UTF-8, and scroll
-offsets use fractional X/Y line units. Read the [application guide](docs/guide.md)
-for platform capabilities and limits.
-
-## Start here
+YASL is a small C desktop surface and input library for applications that own
+their event loop and renderer. It supports Wayland, X11, and Cocoa, with CPU
+images, optional OpenGL contexts, Vulkan window surfaces, and direct Metal layers.
+Pen input includes tool identity, proximity/contact, pressure, tilt, and available
+rotation/distance/tangential/wheel axes. It also supplies IME composition/editing,
+clipboard text, popups, file drops, and surface drags.
+
+The first surface initializes a shared backend. Surface operations use ordinary
+arguments; there are no public display objects, creation descriptors, frame
+objects, point/size/string wrappers, or mandatory application runner.
+
+```c
+yasl_surface *surface = yasl_surface_create("Example", 800, 600, 0);
+```
-- [Getting started](docs/getting-started.md): build, install, link, and run a window.
-- [Application guide](docs/guide.md): geometry, drawing, input, ownership, and backend limits.
-- [Public reference](include/yasl/yasl.h), [app runner](include/yasl/app.h), [loop](include/yasl/loop.h),
- [timing](include/yasl/timing.h), and [native extensions](include/yasl/native.h).
- Run `doxygen Doxyfile` for a searchable HTML reference in `build-docs/html`.
-- [API redesign](docs/api-redesign.md): queue, frame, ownership, and backend contracts.
-- [API policy](docs/api-policy.md), [migration notes](docs/migrating.md), and
- [contributing](CONTRIBUTING.md).
-- [Tests, tablet example, and benchmarks](docs/testing.md).
-- [IDE integration](IDE_INTEGRATION.md): step through example and window creation code with Zed's debugger.
+Flags zero creates a GPU window without choosing a graphics API. The application
+attaches a Vulkan surface or Metal layer, or uses `yasl_gl_surface_create` to create
+an OpenGL window/context. `YASL_SURFACE_CPU` gives the window a pixel buffer: draw into
+`yasl_cpu_pixels` and call `yasl_cpu_present`. Native handles are available through typed extension headers.
-## Build a window
+**Status:** pre-release 0.1; source and ABI compatibility are not promised.
+Use matching headers and library. Supported operations depend on the backend,
+server, and optional dependencies; check results and `yasl_get_error`.
-Use a C23 compiler, Meson 1.6 or newer, and Ninja. Linux needs the development
-packages for Wayland (`wayland-client`, `wayland-cursor`, `xkbcommon`) or X11
-(`x11`, `xi`, `xkbcommon`, `xext`; optional `xrandr`). Optional `dbus-1` enables
-desktop notifications and appearance queries on Linux. Cocoa requires macOS 14 or
-newer and the system SDK, with a Metal-capable device for window drawing.
+Build with a C23 compiler, Meson 1.6+, and Ninja:
```sh
meson setup build
meson compile -C build
-./build/yasl-notepad
+./build/yasl-hello
meson test -C build --print-errorlogs
```
-Linux builds automatically include backends with available dependencies. Require
-one with `-Dwayland=enabled` or `-Dx11=enabled`; omit one with `disabled`. Runtime
-selection chooses the native session and allows fallback, or you can request an
-explicit backend with `yasl_display_spec.backend`.
-
-Include `<yasl/app.h>` and pass application callbacks to `yasl_run()`.
-The complete [hello.c](examples/hello.c) describes its window in
-`yasl_app_desc.window`, lets the runner quit when it is closed, and fills every
-pixel with an RGB gradient in its frame callback.
-
-Toplevel creation sizes are logical pixels; drawing and input use device pixels.
-Both loop styles consume one display event FIFO and receive `yasl_frame`;
-`yasl_frame_present()` submits its target. Targets are borrowed for one frame callback. Keep durable content in application
-memory; the runner handles dispatch, input polling, pacing, and cleanup.
-Applications managing their own loop can use `<yasl/yasl.h>` directly. Both
-workflows are explained in the application guide.
+Linux needs Wayland or X11 development packages; EGL and wayland-egl enable GL,
+and the Vulkan SDK enables its typed bridge. Cocoa needs macOS 14+ and its SDK.
-## License
+- [Getting started](docs/getting-started.md): creation, linking, and rendering.
+- [Application guide](docs/guide.md): ownership, events, GPUs, pen, and backend limits.
+- [Testing](docs/testing.md): unit tests and isolated GPU/backend tests.
+- [API policy](docs/api-policy.md) and [contributing](CONTRIBUTING.md).
+- Public headers live in [include/yasl](include/yasl); examples live in [examples](examples).
-[MIT](LICENSE).
+MIT licensed; see [LICENSE](LICENSE).
diff --git a/docs/api-policy.md b/docs/api-policy.md
index 3e510f3..f2f4d92 100644
--- a/docs/api-policy.md
+++ b/docs/api-policy.md
@@ -1,38 +1,18 @@
# Public API policy
-YASL 0.1 is pre-release. The headers under `include/yasl/` are the public API; implementation
-headers under `src/` are private even when their symbols appear in a static
-archive. The shared library exports only declarations from public headers.
-Native backend accessors are exported only for built backends. Installation
-copies both libraries, public headers, enabled native headers, and pkg-config metadata.
+YASL is pre-release 0.1. Source and ABI compatibility are not guaranteed. Rebuild
+consumers against matching installed headers/library; do not serialize public events
+or assume their layout is a stable file/network format.
-There is currently no stable ABI promise and no versioned shared-library SONAME.
-Rebuild consumers against the exact headers/library pair they ship. Source and
-binary compatibility can change before 1.0. Do not assume struct layout is stable
-across releases, mix headers and binaries from different revisions, or persist
-public structs as a file/network format. The release version is declared in
-`meson.build`.
+Only installed include/yasl headers are public. The surface library exposes an
+opaque surface and one tagged event record, with direct C arguments and ordinary
+pointer/integer results. Native backend headers, geometry records, callbacks, loops,
+and drawing targets under src are private even when a static archive contains them.
+The shared library exports exactly the installed public declarations.
-See [migration notes](migrating.md) for window creation, portable keyboard, and
-fractional scroll API changes.
-
-API changes should keep ordinary CPU drawing usable with only `<yasl/yasl.h>`
-and the C standard library. Prefer an explicit capability and an ordinary
-unsupported result over backend names in portable application logic. Native
-rendering and handles belong in the extension headers. Avoid adding engine,
-widget, renderer, or application policy to the surface layer.
-
-Every public addition or behavior change needs:
-
-- A header contract describing arguments, result, units, lifetime, threading,
- callback timing, and unsupported behavior where applicable.
-- An update to the guide and its backend limits when observable behavior changes.
-- A runnable example when the new workflow cannot be understood from an isolated
- call, and meaningful regression coverage for ownership or behavior changes.
-- A check that shared exports and consumers of the installed library still work.
-
-Functions documenting errno set it on failure. Other booleans report acceptance
-or availability without a guaranteed errno; void setters are best effort.
-Callbacks and successful returns do not imply confirmed screen presentation.
-See `yasl_timing.source` for provenance. Future changes that strengthen error
-reporting must preserve these distinctions in documentation.
+Do not restore removed runner/frame/display APIs or compatibility aliases. Keep
+rendering bridges separate from the portable surface/input core and preserve the
+application's ownership of GPU resources and its loop. Public additions need clear
+units, ownership, thread/callback timing, errors and unsupported behavior, useful
+examples, and behavior coverage. Update the guide and test installed shared/static
+consumers and exports. Changes affecting Cocoa need a native macOS build/test.
diff --git a/docs/api-redesign.md b/docs/api-redesign.md
deleted file mode 100644
index a111a92..0000000
--- a/docs/api-redesign.md
+++ /dev/null
@@ -1,147 +0,0 @@
-# Public API redesign
-
-This is the implementation contract for the pre-release API revision. The
-portable layer owns event ordering, queued payloads, surface identities, and
-frame validation. Backends translate native behavior into that contract.
-
-## One event stream
-
-`yasl_display_poll_event(display, &event)` consumes one display-wide FIFO. Input,
-window resize/close/state, drag hover/drop, composition, clipboard results, and
-display quit/reopen/loss use the same `yasl_event`. Events have an increasing
-`sequence` assigned when YASL queues them, and a monotonic `time_ns`. Sequence
-orders capture by YASL, not unknowable ordering between OS threads or devices.
-The runner consumes this same queue; it does not drain separate queues by window.
-
-Event strings are copied on enqueue and borrowed until the next successful
-display poll or display close, even if the recipient is destroyed. Destroying a
-surface removes its undelivered events. A callback may destroy its recipient;
-it must stop using that surface. Draws remain synchronous to satisfy native
-live-resize rules; the runner drains queued events before invoking a frame.
-
-Legacy surface polling consumes input from the same queue and discards pending
-non-input notifications, which were already delivered through legacy callbacks.
-Applications must choose one polling style; discarding prevents idle spin and
-unbounded retention in old manual loops. Legacy window
-callbacks remain for migration, but applications should choose queue delivery
-and a draw callback. Backend notifications must queue before calling a legacy
-callback, and must not access a surface after a callback that can destroy it.
-
-## One frame contract
-
-Both manual drawing and `yasl_app_desc.frame` receive `struct yasl_frame`.
-Its `target` describes CPU pixels (NULL for native rendering), device-pixel size,
-and row stride. The frame also carries frame index, scale, and an estimated
-animation timestamp. Native drawables are queried through `<yasl/native.h>`;
-there is no untyped GPU layer or public internal pointer in the portable frame.
-
-`yasl_frame_present(frame, rects, count)` submits the exact active frame once.
-NULL with zero count means full damage; a non-NULL array with zero count means
-no damage. NULL with nonzero count and negative extents fail with EINVAL.
-More than the backend rectangle limit falls back to full damage after validating
-every rectangle; damage must never be silently lost. Targets expire at callback
-return. Pixels must always describe a complete frame.
-
-`yasl_surface_request_frame` is the normal coalescing redraw operation.
-`yasl_surface_set_pacing` returns success/failure and works with either loop
-style. Continuous drawing uses native pacing where available and a refresh-based
-timer otherwise. On-demand surfaces require explicit invalidation.
-
-## Identities, posting, and shutdown
-
-`yasl_surface_id(surface)` returns a nonzero identity unique within its display.
-`yasl_display_post(display, &message)` is thread-safe; messages target an identity
-(zero means display-wide), never a borrowed surface pointer. The display must
-remain alive during posting, and producer threads must stop before display close.
-After a successful post the optional release callback runs exactly once on the
-display thread, after consumption or cancellation (including surface destruction
-and shutdown). A failed post leaves payload ownership with the caller. Posting
-to a retired identity fails. The display queue owns only its copy of the message;
-the release callback defines payload cleanup. Release may post, but must not
-poll, dispatch, or destroy YASL objects. Legacy `yasl_post_event` adapts to
-this mechanism, with the caller responsible for its borrowed target lifetime.
-
-Application callbacks may return CONTINUE, QUIT, or ERROR. ERROR makes
-`yasl_run` return failure after cleanup. `yasl_app_exit(display, result)` permits
-timers, fd callbacks, and frames to request the same orderly termination.
-The optional initial window is available through `yasl_app_window(display)`.
-Surface userdata is independent of legacy callback userdata and accessible
-through `yasl_surface_set_userdata` / `yasl_surface_userdata`.
-
-## Window requests and units
-
-New code spells toplevel creation size `size_logical`; `size` remains an alias
-for migration. Framebuffer geometry, pointer coordinates, popup placement,
-damage, IME carets, and drag-image coordinates are device pixels as documented
-on their structures. Conversion helpers remain explicit.
-
-`yasl_surface_set_size(surface, logical_size)`,
-`yasl_surface_set_fullscreen(surface, enabled)`,
-`yasl_surface_set_maximized(surface, enabled)`, and
-`yasl_surface_minimize(surface)` return whether the native request was issued.
-They set errno on failure: EINVAL for invalid input/non-toplevel surfaces,
-ENOTSUP for unsupported requests, or an OS error. Success is acceptance of a
-request, not a promise that the window manager honored it. Resulting size/state
-is reported through events. No portable absolute window-position setter exists.
-
-## Clipboard and composition
-
-`yasl_clipboard_set(display, selection, text)` returns whether a copied UTF-8
-offer was issued, with errno on failure. `yasl_clipboard_request(surface,
-selection)` returns a nonzero request ID, or zero with errno on failure.
-The backend operation `display_request_paste(d, selection, surface, id)` returns
-true only after accepting responsibility for one completion. It completes via
-`yasl_surface_complete_paste(surface, id, status, text, len)`; status is
-YASL_TRANSFER_OK, UNAVAILABLE, or FAILED. Results use YASL_EVENT_PASTE and its
-`.paste` payload (data/len/request_id/status); legacy `.text` aliases data/len.
-Surface destruction cancels delivery and native transfers. A backend must
-complete errors as well as success, including empty text. Request IDs are
-monotonic per display. Legacy paste uses ID zero. External Linux transfers time out after 30 seconds.
-
-YASL_EVENT_COMPOSITION carries copied UTF-8 preedit text, byte selection offsets
-(both -1 if unavailable), and an active flag. Each native change queues a
-snapshot, including composition ending; committed text remains YASL_EVENT_TEXT.
-The query functions remain migration helpers.
-
-## Backend boundary
-
-Common helpers declared in `common/backend.h`:
-
-* `yasl_surface_notify_configure(s, geometry)`
-* `yasl_surface_notify_close(s)`
-* `yasl_surface_notify_state(s, state)`
-* `yasl_surface_notify_drag(s, event, position_px)`
-* `yasl_surface_notify_drop(s, event, position_px)`
-* `yasl_surface_notify_composition(s, text, begin, end)` (NULL/empty ends it)
-* `yasl_surface_draw(s, frame)` (fills frame metadata and invokes draw)
-* `yasl_display_notify_event(d, YASL_EVENT_DISPLAY_QUIT/REOPEN/LOST)`
-* `yasl_surface_complete_paste(s, request_id, status, text, length)`
-
-Backends keep using their existing input_queue; a new `surface_input_queue`
-operation exposes that queue to common code for attaching its event publisher.
-Backends must replace direct lifecycle/draw/drag callbacks with these helpers,
-without weakening their native destruction guards. They implement optional
-surface_set_size/fullscreen/maximized/minimize and display_copy_text /
-display_request_paste operations. Common wrappers validate arguments and
-report unsupported operations uniformly.
-
-Linux backends are compiled and tested independently and together. Cocoa changes
-can be inspected here but require macOS compilation/runtime verification.
-
-## Validation
-
-The combined Linux configuration passes all 26 Meson tests, including isolated
-Weston/Xwayland integration, runner error/cleanup paths, event ordering,
-clipboard outcomes, composition, and destruction during callbacks. Separate
-Wayland-only and X11-only builds pass their unit suites and the final public
-export, C++11, and event-contract checks.
-
-AddressSanitizer and UndefinedBehaviorSanitizer pass the common contract tests
-and the isolated backend integration suite. Leak detection was disabled because
-LeakSanitizer is unavailable in this execution environment. Doxygen builds with
-warnings treated as errors. A staged install links both shared and static
-consumers using only the installed headers and pkg-config metadata.
-
-Cocoa code and native regression tests were updated and reviewed, but were not
-compiled or executed: macOS validation remains required. Existing X11 INCR and
-Wayland arbitrary-size limitations are reported explicitly rather than hidden.
diff --git a/docs/getting-started.md b/docs/getting-started.md
index d1f284d..bb7f095 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -1,113 +1,71 @@
# Getting started
-YASL gives a C application desktop windows with CPU pixel targets, scale-aware
-geometry, queued input, clipboard text, and optional frame pacing. It fits
-software-rendered tools such as editors, terminals, drawing applications, and
-custom interfaces that already have their own renderer. A window needs neither
-an OpenGL context nor a graphics device supplied by the application.
+YASL creates desktop surfaces and delivers queued input. You own the event loop,
+renderer, graphics device/context use, and application lifetime.
-The core headers expose no window system types. Native handles and Metal drawing
-are separate opt-in extensions. YASL currently supports Linux (Wayland and X11)
-and macOS (Cocoa); it does not provide widgets, font shaping, audio, an OpenGL
-context API, or a Vulkan surface API. It is pre-release software; see the
-[API policy](api-policy.md) before distributing a library that depends on its ABI.
-
-## Build and run
-
-Use a C23 compiler, Meson 1.6 or newer, and Ninja. GCC and Clang can build the library. On Linux install either backend's development dependencies:
-
-| Backend | pkg-config dependencies |
-| --- | --- |
-| Wayland | `wayland-client`, `wayland-cursor`, `xkbcommon` |
-| X11 | `x11`, `xi`, `xkbcommon`, `xext`; optional `xrandr` |
-| Linux desktop services | Optional `dbus-1` for notifications and appearance |
-| Cocoa | macOS 14+ SDK with AppKit, Metal, QuartzCore, and UserNotifications; Metal-capable device |
+Build with a C23 compiler, Meson 1.6+, and Ninja. Linux backend dependencies are
+wayland-client, wayland-cursor, xkbcommon, or x11, xi, xext, xkbcommon (optional
+xrandr). Optional egl and wayland-egl enable GL contexts; Vulkan SDK development
+files enable the Vulkan bridge. Optional dbus-1 enables Linux desktop services.
+Cocoa requires macOS 14+ and the AppKit, Metal, QuartzCore, OpenGL, and
+UserNotifications system frameworks. Optional Vulkan SDK enables MoltenVK WSI.
```sh
meson setup build
meson compile -C build
./build/yasl-hello
+./build/yasl-game
meson test -C build --print-errorlogs
```
-Each Linux backend defaults to `auto`, meaning build it when its dependencies
-are available. Require one with `-Dwayland=enabled` or `-Dx11=enabled`; omit one
-with `-Dwayland=disabled` or `-Dx11=disabled`. At least one backend is required.
-`-Ddbus` works the same way for the optional D-Bus desktop services.
-Cocoa is selected on macOS and requires no extra third-party dependencies.
-Its minimum deployment target is macOS 14. Set `MACOSX_DEPLOYMENT_TARGET=14.0`
-(or newer) before configuring if your toolchain defaults to an older target.
-CPU pixel targets are presented through the system's Metal device; applications
-do not need to create or manage that device themselves.
+Backend options `-Dwayland=enabled|disabled|auto`, `-Dx11=...`, and `-Ddbus=...`
+control dependency selection. At least one window backend must be built. Runtime
+AUTO selects the native session and falls back to another built Linux backend.
+For an explicit choice call `yasl_set_backend` before creating any surface.
-The complete beginner example is [hello.c](../examples/hello.c). It lets
-`yasl_run()` create its window from `yasl_app_desc.window`, fills every pixel
-with an RGB gradient, and presents inside a frame callback.
-`yasl_run()` handles input delivery, display notifications, and cleanup. The
-[interactive window](../examples/simple_window.c) adds pointer input; the
-[tablet example](../examples/tablet.c) demonstrates retained application content,
-pressure, erasers, resizing, and scale changes.
+## A window
-## Link an application
+```c
+#include <yasl/yasl.h>
+
+int main(void)
+{
+ yasl_surface *s = yasl_surface_create("Example", 800, 600, 0);
+ if (!s) return 1;
+ /* Attach a GPU renderer here and draw on each REFRESH. */
+ yasl_event event;
+ while (yasl_wait_event(&event, -1) >= 0 &&
+ event.type != YASL_EVENT_CLOSE && event.type != YASL_EVENT_QUIT) {
+ }
+ /* Destroy GPU objects before shutting down. */
+ yasl_shutdown();
+ return 0;
+}
+```
-Install the libraries, public headers, and pkg-config metadata into your prefix:
+The first creation initializes a shared backend. More windows use that backend;
+there is no public display/context object. Closing the final window does not
+implicitly terminate the application or reset configuration. `yasl_shutdown`
+releases shared resources and restores defaults.
+
+[hello.c](../examples/hello.c) draws into a CPU surface's pixel buffer on every REFRESH. [game.c](../examples/game.c) creates a GL context, binds it,
+loads procedures, draws, and swaps. [tablet.c](../examples/tablet.c) retains a canvas
+and draws pressure-sensitive pen/eraser strokes. [notepad.c](../examples/notepad.c)
+uses a worker queue, wakeups, clipboard, IME, and its own blink deadline.
+
+## Link and install
```sh
meson setup build --prefix=/usr/local --libdir=lib
meson compile -C build
meson install -C build
cc -std=c23 examples/hello.c -o hello $(pkg-config --cflags --libs yasl)
-./hello
-```
-
-The prefix must be writable. For a custom prefix, add its pkg-config directory
-to `PKG_CONFIG_PATH` and its library directory to the runtime loader path.
-Use `pkg-config --static --libs yasl` for static consumers so backend dependencies
-are included. Installed native accessor headers match the built backends.
-
-A Meson application can instead vendor the source under `subprojects/yasl`:
-
-```meson
-project('my-app', 'c', default_options: ['c_std=c23'])
-yasl = subproject('yasl').get_variable('yasl_dep')
-executable('my-app', 'main.c', dependencies: yasl)
-```
-
-The subproject dependency includes headers and backend link dependencies.
-Public headers can be included from C++11 or newer; the library itself builds
-as C23. The libraries, public headers, and pkg-config metadata are installed together.
-
-## The application cycle
-
-1. Include `<yasl/app.h>` and initialize a `yasl_app_desc` with your userdata and
- `init`, `event`, and `frame` callbacks. Call `yasl_run(&desc)`.
-2. In `init`, create a toplevel with a positive **logical-pixel** size and call
- `yasl_surface_request_frame(surface)` for the first redraw.
-3. In `event`, handle the union member selected by `event->type`. Return
- `YASL_APP_QUIT` for a close request, `YASL_APP_ERROR` for failure, or
- `YASL_APP_CONTINUE` to keep running.
-4. In `frame`, fill every pixel in `frame->target.pixels` with premultiplied
- `0xAARRGGBB` and present once with `yasl_frame_present(frame, NULL, 0)`.
-5. Let the runner close remaining surfaces and the display. Release your own
- resources in the optional `quit` callback or after `yasl_run()` returns.
-
-```c
-struct yasl_app_desc desc = {
- .app_id = "org.example.app",
- .userdata = &app,
- .init = init,
- .event = event,
- .frame = draw,
-};
-return yasl_run(&desc);
```
-The default pacing mode sleeps until input, resize, or a requested redraw.
-Use `YASL_PACING_CONTINUOUS` for an animation or game. The core API still supports
-manual dispatch/poll/flush when an application owns its event loop; see the guide.
+Use a writable prefix. For a custom prefix configure PKG_CONFIG_PATH and the
+runtime library search path. Static consumers use `pkg-config --static --libs yasl`.
+Public headers also compile as C++11. Native headers and the Vulkan header are
+installed only when their respective support is built.
-See the [application guide](guide.md) for drawing, input, popups, timers, and
-platform limits, or the comments in the [public header](../include/yasl/yasl.h)
-for each function's contract. To build the searchable HTML reference, install
-Doxygen and run `doxygen Doxyfile` from the repository root. Open
-`build-docs/html/index.html`; Doxygen is not needed to build the library.
+See the [guide](guide.md) for geometry, event lifetime, GPU ownership, input, and
+backend limits. `doxygen Doxyfile` builds the reference in build-docs/html.
diff --git a/docs/guide.md b/docs/guide.md
index 1b64880..d0db071 100644
--- a/docs/guide.md
+++ b/docs/guide.md
@@ -1,476 +1,141 @@
-# Application guide
-
-The contracts in [yasl.h](../include/yasl/yasl.h), [loop.h](../include/yasl/loop.h),
-[timing.h](../include/yasl/timing.h), [desktop.h](../include/yasl/desktop.h), and
-[native.h](../include/yasl/native.h) are the reference for individual calls. All examples here use the display's opening
-thread, which must be the main thread on macOS. Userdata stays owned by the
-application. Copy borrowed data before retaining it beyond its documented life.
-
-## Backend selection and capabilities
-
-Zero-initialize `yasl_display_spec` or pass NULL to select automatically. On
-Linux, `WAYLAND_DISPLAY`, `WAYLAND_SOCKET`, or `XDG_SESSION_TYPE=wayland` makes
-YASL try Wayland first; otherwise it tries X11 first. It falls back to the other
-built backend if opening fails. On macOS it uses Cocoa. Explicit
-`YASL_BACKEND_WAYLAND`, `YASL_BACKEND_X11`, or `YASL_BACKEND_COCOA` requests never
-fall back. Query the result with `yasl_display_backend()`.
-
-Use `yasl_display_caps()` for optional behavior. A bit means backend/server
-support, not that a device is attached or that the current input state allows
-an operation. Always check the operation's return value too.
-
-| Capability | Meaning | Wayland | X11 | Cocoa |
-| --- | --- | --- | --- | --- |
-| `YASL_CAP_POPUPS` | Child popup surfaces | Yes, input grab required | Yes | Yes |
-| `YASL_CAP_DRAG` | Surface-drag source and target events | Data device v3 | Yes | Yes |
-| `YASL_CAP_DRAG_CARRY` | Carry a toplevel with a surface drag | Optional toplevel-drag protocol | Yes | Yes |
-| `YASL_CAP_PLACE_WINDOW` | Place a window under the pointer | No | Yes | Yes |
-| `YASL_CAP_PRIMARY_SELECTION` | Separate primary text selection | Optional primary-selection protocol | Yes | Named pasteboard emulation |
-| `YASL_CAP_CSD` | Backend advertises client decoration support | Yes | No | Yes |
-| `YASL_CAP_NATIVE_GRAPHICS` | Application-managed native drawables | No | No | Metal |
-| `YASL_CAP_FRAME_PACING` | Display/compositor-driven drawing | Yes | No | Yes |
-| `YASL_CAP_TABLET` | Tablet event translation | Optional tablet-v2 protocol | XInput 2 | Yes |
-
-This table describes the implementation, not universal operating-system
-promises. Wayland protocols and XInput availability depend on the running
-server. A capability does not guarantee every related action: Cocoa currently
-returns false for interactive resize and window-menu requests even though
-it advertises CSD. Check each result and provide an application fallback.
-
-## Window creation and callbacks
-
-All examples include `<yasl/app.h>` and call `yasl_run()` with a
-`yasl_app_desc`. Set `window` to a `yasl_window_spec` for a single toplevel,
-which the runner creates before `init` with its first frame requested;
-`yasl_run()` returns an error if creation fails. Create further surfaces in the
-optional `init`, handle `YASL_EVENT_*` notifications in `event`, and render into
-`frame->target.pixels` in `frame`. Present with `yasl_frame_present(frame, NULL,
-0)` and return `YASL_APP_QUIT` from an event handler to exit. Without an `event`
-callback, closing any window stops the runner. The runner handles dispatch, queued event delivery, resize redraws,
-and remaining surface/display cleanup. Display quit stops the runner; connection
-loss and dispatch failures return an error after cleanup. The runner delivers
-the same display-wide FIFO consumed by `yasl_display_poll_event()`, including
-window state, drag hover, composition, clipboard completion, and display lifecycle
-events. `event.sequence` increases in enqueue order across all windows; it is
-YASL capture order, not a claim about ordering between native producers.
-Return `YASL_APP_ERROR` on application failure, or call
-`yasl_app_exit(display, YASL_APP_ERROR)` from a frame, timer, or fd callback.
-Cleanup still runs, and `yasl_run()` returns failure. During `init`,
-`yasl_app_window(display)` exposes the optional initial window. Application data
-can be supplied in `window.userdata` or attached with
-`yasl_surface_set_userdata()`; it is independent of callback userdata.
-
-Unified pointer button and motion events retain `device` and `pen` axes,
-including erasers and pressure-only motion. Cancel held input or active strokes
-on `YASL_EVENT_INPUT_RESET`, pointer leave, or focus loss. Reset also starts the
-next motion delta from zero. [hello.c](../examples/hello.c) shows the minimal
-lifecycle; [tablet.c](../examples/tablet.c) demonstrates device-aware drawing.
-
-For applications managing their own event loop, use the core API below.
-
-Open the display with application-wide options and notifications in
-`yasl_display_spec`. Create each toplevel with `yasl_window_spec`, including its
-title, `size_logical`, optional `yasl_window_cbs` table in `cbs`, and userdata in
-`ud`. One display can own multiple windows with separate callback tables.
-
-The window copies its callbacks during creation. Its callbacks cannot run before
-creation returns, so the application can store the returned surface first.
-Userdata stays borrowed until callbacks are replaced or the surface is destroyed.
-Read initial geometry with `yasl_surface_geometry()`; creation does not guarantee
-an initial `configure` notification. Request the first redraw once application
-state is ready. `yasl_surface_set_callbacks()` replaces callbacks later and also
-installs popup callbacks; the setter does not invoke the callbacks.
-
-When using `yasl_run()`, its runner installs callbacks for windows on its display.
-Use `yasl_app_desc.event` and `.frame` rather than `yasl_window_spec.cbs` and `.ud`.
-Replacing callbacks on a runner-owned surface fails with `EBUSY`.
-With a manual loop, install just the draw callback and drain the display FIFO
-after dispatch; lifecycle and input share the same event handler logic.
-
-Read window state with `yasl_surface_state()`. Its `YASL_STATE_*` flags can
-coexist, and the `state` callback receives the same current bitmask. Wayland
-also reports `YASL_STATE_TILED` when any window edge is tiled; X11 and Cocoa
-currently leave that flag unset. Unsupported flags are absent. On Wayland, an
-ordinary floating toplevel has fullscreen, maximized, and tiled all absent.
-`YASL_STATE_SUSPENDED` describes repaint suspension rather than minimization;
-there is no portable minimized flag.
-
-## Geometry and scaling
-
-| Value | Units |
-| --- | --- |
-| Toplevel `yasl_window_spec.size` | Logical pixels |
-| `yasl_geometry.size`, framebuffer size | Device pixels |
-| Pointer position, caret, window-menu position | Device pixels, relative to the surface |
-| Damage rectangles | Device pixels, relative to the draw target |
-| All popup fields, including `size` | Device pixels; anchor is relative to the parent |
-| Drag image size, hotspot, carry/place offsets | Device pixels |
-| `scale_120` | 120 = 1x, 180 = 1.5x, 240 = 2x |
-
-The origin is the top left, x increases right, and y increases down. Use
-`yasl_surface_geometry()` for the current size and scale, and update cached
-layout on `configure`. The native system may choose a different size from the
-request. X11 currently reports unit scale; automatic output DPI scaling is not
-implemented there.
-
-```c
-struct yasl_geometry geometry = yasl_surface_geometry(surface);
-int padding_px = yasl_scale_to_device(8.0, geometry.scale_120);
-int logical_width = yasl_scale_to_logical(geometry.size.x, geometry.scale_120);
-```
-
-Scale conversion rounds to the nearest integer, with ties away from zero,
-saturates out-of-range finite results, and returns zero for a zero scale.
-`yasl_scale_to_device()` also returns zero for non-finite logical coordinates.
-
-## Drawing and pacing
-
-`yasl_surface_request_frame()` coalesces requests. `yasl_display_flush()`
-services them when the backend can draw. During a native resize, dispatch can
-also draw synchronously. Avoid assuming callbacks occur only in flush.
-
-The CPU target is tightly packed premultiplied `0xAARRGGBB`. Row stride is
-`target.size.x * sizeof(uint32_t)`. For example, opaque red is `0xffff0000`,
-50% transparent red is approximately `0x80800000`, and transparent black is
-`0x00000000`. The numeric format describes a 32-bit value, not a portable byte
-sequence. Keep your application's durable content separately: a new target may
-contain no previous frame contents.
-
-```c
-static void draw(struct yasl_surface *surface, struct yasl_frame *frame, void *ud)
-{
- (void)ud;
- size_t count = (size_t)frame->target.size.x * frame->target.size.y;
- for (size_t i = 0; i < count; i++)
- frame->target.pixels[i] = 0xff306090;
- if (!yasl_frame_present(frame, NULL, 0)) {
- /* Arrange application error handling after returning. */
- }
-}
-```
-
-Present the exact active frame at most once during its callback. Do not mutate
-the target identity or retain frame/pixel pointers after return. NULL rectangles
-with count zero mean full damage; a non-NULL array with zero count means no damage.
-All supplied extents must be nonnegative. Rectangles are clipped, and more than
-`YASL_DAMAGE_MAX` rectangles fall back to full damage after all are validated.
-Damage may reduce uploads, but every target must contain complete valid pixels.
-Native rendering uses the same frame, with a drawable queried in the extension.
-
-`yasl_surface_set_pacing()` works in either loop style. ON_DEMAND coalesces
-explicit requests and sleeps when idle. CONTINUOUS uses native display signals
-where available and a refresh-based timer otherwise, with about 60 Hz as a
-fallback estimate. It reports allocation/argument failures instead of silently
-ignoring surfaces outside the runner. Suspension can suppress actual drawing.
-Legacy `yasl_surface_present()` and `yasl_surface_schedule_redraw()` remain
-migration adapters; new code uses the frame presentation and request functions.
-
-`yasl_surface_read_pixels()` copies the last presented CPU frame, for example
-for a screenshot or a debugging tool. Call it outside draw callbacks. A frame
-is available from a successful present until the next draw starts, and again
-once that draw presents. Call it with no buffer first to learn the extent. The
-copy is YASL's frame, not the composited screen, and native rendering has none.
-
-`yasl_surface_timing()` reports estimates and their provenance. All timestamps
-are monotonic nanoseconds. `YASL_TIMING_SUBMISSION` is CPU submission time,
-`YASL_TIMING_FRAME_CALLBACK` is a compositor pacing signal, and only
-`YASL_TIMING_PRESENTATION` confirms compositor presentation. `UNKNOWN` means no
-sample. `refresh_ns` and `next_present_ns` remain estimates. Use
-`yasl_surface_frame_time_ns(surface, yasl_loop_now_ns())` as a stable animation
-clock; it does not establish actual scanout time.
-
-## Input, text, and payload lifetime
-
-Dispatch receives native input; `yasl_display_poll_event()` drains the display
-FIFO without waiting. Drain it once for the whole display, including multiple
-windows and popup input. `event.surface` identifies the live recipient and
-`surface_id` is its stable identity. Destruction cancels undelivered recipient
-events. `yasl_surface_poll_event()` remains an input-only migration adapter and
-discards pending non-input notifications, which legacy callbacks already
-delivered. Do not mix the two polling styles.
-
-Read the union member selected by `event.type`. `time_ns` is monotonic and
-`sequence` orders capture across the display. TEXT and DROP use `.text`, PASTE
-uses `.paste`, and COMPOSITION uses `.composition`. Each string is copied when
-queued, NUL-terminated, and borrowed until the next successful display/surface
-poll or display close. Dispatch, unsuccessful polls, and recipient destruction
-do not invalidate the last polled string. Copy it to retain it longer.
-
-Use `YASL_EVENT_TEXT` for document text insertion, including committed IME text.
-It contains UTF-8 and can contain several characters. Key actions are for
-commands and held-key state; releases and repeats have separate tags. A text
-commit need not correspond to one key event, and a key need not produce text.
-
-`key.key` is a portable physical identity from
-[keycodes.h](../include/yasl/keycodes.h), included by `<yasl/yasl.h>`. Names follow
-US keyboard positions: `YASL_KEY_A` identifies the same position on a French
-layout even though it types Q. Modifiers do not change this identity. Keypad
-keys and left/right modifiers have distinct names. Unknown native keys report
-`YASL_KEY_UNKNOWN`; `native_code` preserves their backend-specific identity
-(Wayland evdev code, X11 server keycode, or Cocoa virtual keycode).
-
-`codepoint` and `shifted` are optional Unicode scalar labels for the active
-layout without modifiers and with Shift. Zero means no single printable label,
-including function keys, unavailable translations, and mappings that produce
-multiple characters. Dead keys may have a spacing label or zero. These fields
-are useful for displaying shortcuts or
-choosing layout-sensitive commands; they are never document text. For example:
-
-```c
-if (event.type == YASL_EVENT_KEY && event.key.action == YASL_KEY_PRESS &&
- event.key.key == YASL_KEY_ESCAPE) {
- running = false;
-}
-/* Insert event.text.data[0..event.text.len) only for YASL_EVENT_TEXT. */
-```
-
-Wayland uses the supplied XKB layout and locale compose rules for ordinary text
-and dead keys, with text-input-v3 for IME commits. Compose rules use `LC_ALL`,
-`LC_CTYPE`, then `LANG`, falling back to `C.UTF-8`. X11 uses the X input method
-when available; Cocoa uses AppKit text services. X11 input methods use the
-application's `LC_CTYPE` locale. Initialize it before opening a display when
-using locale-specific XIM, for example with `setlocale(LC_CTYPE, "")`; YASL does
-not change the process locale. IME availability and system configuration still
-determine which compositions work. Physical key events
-remain available for commands; input methods may consume keys while composing.
-
-Pointer events use the `YASL_EVENT_POINTER_*` types and share `event.pointer`,
-with `device` distinguishing mouse, touch, pen, and eraser. `POINTER_DOWN` and
-`POINTER_UP` carry a `YASL_BUTTON_*` value: `PRIMARY` is the left button, pen tip,
-or touch contact. Other types leave `button` meaningless. Every pointer event
-also carries `buttons`, a `YASL_BUTTON_BIT()` mask of the buttons held after
-that event, tracked from the queue's down and up events. Use it to tell a drag
-from a hover. Count clicks against `yasl_display_double_click_ms()`, the
-system interval: XSETTINGS on X11, the KDE or GNOME setting through the
-Settings portal on Wayland, and the system value on Cocoa. It falls back to
-`YASL_DOUBLE_CLICK_MS_DEFAULT` (400 ms). The query can wait briefly on Linux, so
-read it when a window gains focus. `YASL_EVENT_INPUT_RESET` clears the mask. On Wayland,
-`POINTER_LEAVE` also clears it, because the compositor sends leave during a
-press only when it takes the pointer away. X11 and Cocoa keep the press across
-leave and still deliver the release. `POINTER_MOTION` reports `delta`, the device-pixel movement since
-the previous sample, which restarts from zero after enter, leave, or reset.
-`yasl_surface_set_pointer_mode()` with `YASL_POINTER_RELATIVE` hides and locks the
-cursor while the surface is focused; motion then carries unbounded `delta` and
-`pos` stays at the lock position. Wayland implements it with the
-relative-pointer and pointer-constraints protocols, reporting the compositor's
-accelerated motion in device pixels. The compositor decides when the lock
-engages; until then, and while it is released on focus loss, motion stays
-absolute with a computed `delta`. X11, Cocoa, and compositors without those
-protocols return false and keep the current mode. Pen
-pressure is normalized to 0..65535, signed tilt is in degrees, and missing axes
-are zero. Pressure-only changes can produce motion. Zero pressure alone does
-not mean release. This interface does not expose simultaneous touch contacts.
-
-For `YASL_EVENT_POINTER_SCROLL`, read `pointer.scroll.x` and `.y`: fractional line offsets,
-positive right and down, after system scroll preferences. Both axes may be
-nonzero in one event. Preserve fractions and choose your application's own
-pixels per line; one wheel detent normally gives one line. `button` is unused
-for scroll. Wayland high-resolution wheel values take precedence over duplicate
-discrete/raw descriptions. Smooth Wayland offsets and precise Cocoa offsets
-use ten native units per line. X11 currently reports whole wheel steps, so it
-does not provide the same smooth-scroll precision as the other backends.
-
-Cancel held keys, buttons, active strokes, and composition on `YASL_EVENT_INPUT_RESET`,
-focus loss, or pointer leave as appropriate to your application. RESET has no
-union payload. It is emitted after input-queue allocation failure and certain
-device interruptions; ignoring it can leave application input stuck.
-
-## Clipboard, file drops, and IME
-
-`yasl_clipboard_set(display, selection, text)` copies a NUL-terminated UTF-8
-offer and reports failure through its return and errno. Wayland may require a
-recent input serial. `yasl_clipboard_request(surface, selection)` returns a
-nonzero request ID, or zero with errno when rejected. Each accepted request
-produces one `YASL_EVENT_PASTE` carrying `.paste.request_id`, `.status`, `.data`,
-and `.len`, unless its target/display is destroyed. OK includes empty text;
-UNAVAILABLE means no text offer, and FAILED means transfer/decoding failure.
-External Linux transfers have a 30-second timeout. X11 does not implement INCR
-and reports unsupported large transfers as FAILED. Legacy STRING uses Latin-1
-conversion. Malformed UTF-8 completes as failure.
-
-Unsupported primary selection reports ENOTSUP and never aliases the clipboard.
-Cocoa uses a YASL named pasteboard for primary selection. Legacy copy/paste calls
-remain adapters; legacy paste completions carry request ID zero.
-
-External file drag hover uses `YASL_EVENT_DROP_HOVER`; actual file URLs
-arrive as `YASL_EVENT_DROP`. `text.data` is a `text/uri-list` string separated by
-CRLF, not a list of native paths. `yasl_drop_next_path()` walks it and decodes
-each local `file:` URL into a path. It skips comments, other schemes, remote
-hosts, and malformed lines:
-
-```c
-const char *cursor = event.text.data;
-char path[4096];
-size_t len;
-while ((len = yasl_drop_next_path(&cursor, path, sizeof(path))) != 0) {
- if (len >= sizeof(path)) break; /* Too long; grow the buffer instead. */
- /* Open path. */
-}
-```
-
-Decoded paths are raw bytes and may contain any character the file system
-allows, including newlines. Quote them before pasting into a shell.
-
-Handle `YASL_EVENT_COMPOSITION` for immutable UTF-8 preedit snapshots, byte
-selection offsets, and composition ending. Missing selection information uses
--1 offsets. Insert only committed `YASL_EVENT_TEXT` into the document. Set the
-candidate-window caret with `yasl_surface_set_ime_caret()` in device pixels.
-X11 uses XIM preedit callbacks when offered; fallback input methods may provide
-no inline composition. The old preedit getters remain migration helpers.
-
-## Popups and surface drags
-
-Create popups from a live parent, normally in response to input. All fields in
-`yasl_popup_spec` use device pixels. `size` includes transparent margins;
-`content_offset` and `content_size` describe the actual content rectangle used
-for positioning. A zero content-size axis extends to the corresponding size
-edge. `beside = true` prefers the anchor's right edge; false prefers below.
-Placement may flip or slide to stay on screen. Wayland requires a recent input
-serial to grab a popup, and older protocols may reject repositioning. Destroy
-children before parents. The close callback requests application destruction;
-it does not automatically free the popup.
-
-`YASL_EVENT_DRAG` handles a YASL surface drag; `YASL_EVENT_DROP_HOVER` handles
-external data hover. Legacy callbacks remain migration adapters. `yasl_display_drag_start()` is intended for window/tab tear-off
-workflows, not arbitrary MIME data sources. Keep transferred application state
-in your own registry. Start from a recent pointer press, and check the boolean
-result even when `YASL_CAP_DRAG` is present. Supply an optional copied image and
-completion listener. One active drag is supported per display. A successful
-start calls a non-NULL `end` exactly once, including explicit cancel or display
-close; a failed start never does. `DROPPED` does not prove that another
-application accepted your application object.
-
-Where `YASL_CAP_DRAG_CARRY` is present, `yasl_surface_drag_carry()` can attach a
-toplevel to the active drag. Otherwise an image can represent it. With
-`YASL_CAP_PLACE_WINDOW`, `yasl_surface_place_under_pointer()` requests placement
-relative to the pointer. These calls may fail due to current input/drag state.
-Completion can run synchronously during cancel/close; keep listener userdata
-alive until then and do not destroy the display recursively.
-
-## Timers and file descriptors
-
-Include `<yasl/loop.h>`. Use `yasl_display_loop(display)` for application timers
-so they participate in display dispatch. Intervals are nanoseconds; the
-`YASL_MS_TO_NS(ms)` macro converts a nonnegative integer millisecond value.
-
-```c
-static uint64_t tick(void *ud, yasl_timer_id id, uint64_t interval_ns)
-{
- (void)id;
- yasl_surface_schedule_redraw(ud);
- return interval_ns; /* Return zero for a one-shot timer. */
-}
-/* Check zero for failure. This timer is canceled with the surface. */
-yasl_timer_id timer = yasl_surface_add_timer(surface, YASL_MS_TO_NS(16), tick, surface);
-```
-
-A timer callback receives the scheduled interval, not actual elapsed time. A
-nonzero return repeats/reschedules; zero retires it. Missed periods coalesce.
-Timers can add/delete timers, including themselves. Loop timers end when retired,
-deleted, or the loop is destroyed; surface timers also end on surface destruction.
-Userdata remains application-owned. Delete surface timers with
-`yasl_surface_del_timer()` and ordinary timers with `yasl_loop_del_timer()`.
-
-File watches use `<poll.h>` masks. `yasl_loop_add_fd()` returns a positive watch
-ID or -1. Its callback receives `revents`, including error/hangup flags; its
-integer return is ignored. Consume readiness or disable/delete the watch.
-The loop never closes application file descriptors: delete the watch before
-closing its descriptor. Ready callbacks may add or remove watches. Do not call
-`yasl_loop_step()` or `yasl_loop_run()` recursively.
-
-Every backend's dispatch wakes when a loop descriptor becomes ready. Cocoa
-waits for native events and mirrors the loop's descriptors in a kqueue while it
-waits. If kqueue rejects a descriptor, Cocoa bounds the wait to 10 ms instead.
-
-Loop wake and post calls work from any thread. `yasl_loop_kick()` only wakes the loop, and it
-is also safe in a signal handler. `yasl_loop_post()` queues a function to run
-on the owning thread during a later step or dispatch. Use it to hand results
-from worker threads to the loop:
-
-```c
-static void results_ready(void *ud)
-{
- worker_drain(ud); /* Runs on the loop thread. */
-}
-/* On the worker thread; false means allocation failed. */
-yasl_loop_post(loop, results_ready, worker);
-```
-
-`yasl_loop_watch_signals()` delivers POSIX signals to a callback on the loop
-thread. It installs a process-wide handler for each signal, with `SA_RESTART`,
-and restores the previous action on unwatch. YASL leaves thread signal masks
-alone: a watched signal must stay unblocked in at least one thread. Only one
-loop can watch a given signal at a time.
-
-## Worker messages and window requests
-
-Capture `yasl_surface_id(surface)` on the display thread and pass that value to
-workers. `yasl_display_post(display, &message)` accepts an identity instead of a
-borrowed surface pointer. Zero targets the display. On success, optional
-`message.release(payload)` runs exactly once on the display thread after delivery
-or cancellation, including shutdown. It may post messages but must not poll,
-dispatch, or destroy YASL objects. Failed posting leaves ownership with the caller.
-Stop and join producers before closing the display. The same API works with a
-manual loop and with the runner; `yasl_post_event()` is a legacy adapter.
-
-Toplevel requests `yasl_surface_set_size(surface, logical_size)`,
-`yasl_surface_set_fullscreen`, `yasl_surface_set_maximized`, and
-`yasl_surface_minimize` return acceptance with errno on failure. Confirm actual
-geometry/state through queued events. Popup targets are invalid. Wayland cannot
-request an arbitrary live size and returns ENOTSUP for that operation; fullscreen,
-maximize, and minimize use xdg-shell requests. X11 and Cocoa implement all four.
-
-## Desktop services
-
-Include `<yasl/desktop.h>` for services that sit next to windowing. Display and
-surface calls use the display thread; path and locale calls work from any
-thread.
-
-| Call | Linux | Cocoa |
-| --- | --- | --- |
-| `yasl_display_open_uri()` | `xdg-open` from `PATH` | NSWorkspace |
-| `yasl_display_notify()` | Notifications service over D-Bus | User notifications; app bundle required |
-| `yasl_display_appearance()` | Settings portal over D-Bus, then `GTK_THEME` | Effective appearance and increase-contrast setting |
-| `yasl_surface_bell()` | Wayland xdg-system-bell; X11 XKB bell | `NSBeep` |
-| `yasl_executable_path()` | `/proc/self/exe` | Executable path, resolved |
-| `yasl_resource_dir()` | Always zero | App bundle resources |
-| `yasl_default_locale()` | `LC_ALL`, `LC_CTYPE`, `LANG`, then `C.UTF-8` | Current language and region |
-
-`yasl_display_open_uri()` checks only that the URI has a scheme. Decide which
-schemes and hosts your application trusts before calling it, for example
-before opening a link from untrusted text. On Linux the opener starts in its
-own session with default signal handling and none of the application's
-descriptors above stderr. D-Bus services need YASL built with `dbus-1`; without
-it, notifications return false and appearance falls back to `GTK_THEME`.
-Appearance changes send no notification, so query again when a window gains
-focus.
-
-Functions that fill a buffer return the full length, excluding the NUL. A
-result less than the buffer size means the string was written. Otherwise the
-buffer is unchanged, so call again with a larger one:
-
-```c
-char path[4096];
-size_t len = yasl_executable_path(path, sizeof(path));
-if (len == 0 || len >= sizeof(path)) {
- /* No path, or a longer buffer is needed. */
-}
-```
-
-## Native interoperability
-
-Portable CPU drawing needs no native headers. Opt into `<yasl/native.h>` and
-`<yasl/native/wayland.h>`, `<yasl/native/x11.h>`, or `<yasl/native/cocoa.h>` when
-integrating a platform API. Typed native functions are exported only for
-built backends. Accessors return borrowed handles and zero/NULL for the wrong
-backend. Do not pass freed objects, destroy borrowed handles, independently
-dispatch native connections, or change surface roles and buffer ownership.
-
-Application-managed native drawing currently supports Cocoa/Metal. Use
-`yasl_surface_create_native()` with a non-NULL Metal device, which YASL retains.
-During draw, query `yasl_surface_get_native_frame()` for a borrowed drawable,
-encode and present with your renderer, then call `yasl_frame_present()` to
-record submission. The drawable expires on successful present, callback return,
-or surface destruction. Do not infer GPU completion from the boolean result.
-Other backends return NULL with `errno = ENOTSUP` for native creation; having a
-native window handle does not imply this rendering path is supported.
+# Surface and input guide
+
+## Lifetime, threads, and errors
+
+The first successful `yasl_surface_create` or `yasl_gl_surface_create` initializes
+shared native state. Subsequent surfaces share one backend. Optional `yasl_set_app_id`
+and `yasl_set_backend` configure the next initialization; they fail after initialization.
+Destroying the final surface preserves shared state. `yasl_shutdown` destroys remaining
+surfaces, releases the backend, and restores defaults. Release application-owned GPU
+objects before their surface or shutdown. Parent destruction destroys popup descendants.
+
+Creation, window operations, event pumping, and shutdown use the first creation's
+thread, which must be the main thread on Cocoa. GL context binding/drawing/swapping
+may use a rendering thread; detach its context before destroying the window. The
+application synchronizes renderer state/geometry and joins workers before shutdown.
+`yasl_wake` is thread-safe and does nothing before initialization or after shutdown.
+
+Constructors return NULL on failure; fallible mutations return bool. Event reads
+return 1 for an event, 0 for empty/timeout/wake, -1 for failure. `yasl_get_error`
+returns a stable per-thread error and static description; `yasl_get_native_error`
+returns supplementary native diagnostics. Success does not clear a preceding error.
+Valid-object size/scale/userdata getters return ordinary values. Destroyed pointers
+are invalid. Strings passed to creation and setters are copied; userdata is borrowed.
+
+## Events and coordinates
+
+`yasl_poll_event` and `yasl_wait_event` process native events and consume one shared
+FIFO. No manual dispatch/flush cycle. A failed/empty read leaves output unchanged.
+Wait timeout zero polls, negative waits for work, positive milliseconds bound waiting.
+A wake can return zero. Polling before initialization returns zero; waiting reports
+NOT_INITIALIZED. Close and quit are requests; the application decides how to respond.
+Display loss is a terminal native failure; destroy resources and shut down.
+
+Read the union member selected by type. Events own their text through
+`yasl_event_release`, independently of later polls and source destruction. Release
+every successful event exactly once; numeric events need no allocation to release.
+Do not duplicate event ownership by shallow copying. Event surface pointers remain
+borrowed and become invalid when their surface is destroyed. Undelivered events for
+a destroyed surface are canceled. Sequence orders capture by YASL, not OS causality.
+Timestamps use monotonic nanoseconds; native_time states whether normalized native
+sample timing was available or capture time was used.
+
+Creation dimensions, pointer/pen coordinates, popup placement, caret, and CSD actions
+use logical content units. Fractional positions use doubles. Image dimensions,
+byte stride, framebuffer size, and damage use device pixels. Query actual framebuffer
+extent for swapchain/viewport sizing; scale alone need not determine integer rounding.
+Request functions report acceptance, while geometry/state events report actual results.
+There is no portable absolute window-position promise. State's known mask distinguishes
+reported false from unavailable state fields.
+
+## Rendering
+
+GPU creation (flags zero) does not create a device, context, or software framebuffer.
+One presenter attaches to each surface. Incompatible second attachments fail.
+
+- Vulkan: inject vkGetInstanceProcAddr, query instance extensions and flags through
+ the live GPU surface, create the instance, then `yasl_vulkan_create_surface`.
+ The application owns VkInstance/VkDevice/queues/swapchains/VkSurfaceKHR, handles
+ image acquisition, synchronization and recreation, and presents with Vulkan.
+- Metal: `yasl_metal_layer` returns a borrowed CAMetalLayer on Cocoa. YASL owns
+ the native view/layer and updates its dimensions. The app sets device/pixel format,
+ obtains drawables, and submits/presents commands. No additional YASL present call.
+- GL: `yasl_gl_surface_create` accepts title, dimensions, flags, a per-call int
+ key/value attribute list terminated by YASL_GL_END, and an optional sharing surface.
+ NULL attributes default to GL3.2 core, RGBA8, depth24/stencil8. The surface owns
+ its context. Bind, load procedures, set swap interval, and swap explicitly.
+ GL API support depends on the native driver; Cocoa rejects OpenGL ES/debug contexts
+ and supports its native legacy/core profiles.
+- CPU: create with YASL_SURFACE_CPU and call `yasl_cpu_present` with premultiplied
+ numeric 0xAARRGGBB, width/height, byte stride, and optional damage rows containing
+ x/y/width/height. Extent must match current framebuffer. NULL/zero damage means full;
+ explicit lists must be nonempty. Initial submission and changed extent require full
+ images. Later patches update the retained submitted image. Source bytes are copied
+ before return, so the app may reuse/free them. Native uploads may copy the entire
+ retained image, but never unsubmitted source changes. `yasl_cpu_read_pixels` reads
+ that submitted CPU image, not the composited screen.
+
+GPU creation/presentation uses native requests directly; YASL acknowledges Wayland
+configuration before external GPU submission. Native refresh events can be handled
+in the ordinary loop. An optional refresh callback permits drawing while the native
+window system remains in a modal resize path. It runs only during pumping and never
+recursively; it may query geometry and render, but must not pump, create/destroy
+surfaces, or shut down. Geometry events may not yet have been consumed, so query
+current geometry there. Without a hook, CPU exposes reuse committed pixels and GPU
+redraw waits for the application's loop to resume.
+
+## Keyboard, pen, and scrolling
+
+Physical YASL_KEY names identify US keyboard positions independent of layout.
+Layout labels support shortcuts; committed UTF-8 TEXT is for document insertion.
+Keys and text need not correspond one-to-one. Input RESET/focus loss cancels held
+application input. Keep pointer and pen streams separate to avoid duplicate strokes.
+
+PEN events carry connection-local device/tool IDs, tool type, proximity/contact,
+button transitions, coherent position and axes. Pressure is 0..1; zero does not end
+contact. Tilt uses degrees -90..90; rotation uses clockwise degrees; distance is
+normalized, not a physical unit. Axis validity distinguishes unavailable from zero.
+Changed masks indicate native updates. Tools/axes depend on hardware and backend;
+IDs do not guarantee persistent hardware serials. Pen-generated mouse duplicates
+are suppressed where recognized; do not treat tool class as a multi-contact API.
+
+SCROLL preserves wheel-line and precise-pixel values with validity, source, phase,
+and momentum metadata where supplied. Applications choose layout pixels per wheel
+line. X11 supplies native wheel/smooth valuator data where available. Missing native
+metadata stays explicitly unavailable instead of being inferred.
+
+## Text input, clipboard, popups, and drags
+
+Activate text input for a text control with `yasl_surface_set_text_input`. Set purpose,
+caret, and copied UTF-8 surrounding text/selection/revision through direct setters.
+Snapshot offsets and TEXT_EDIT replacement ranges are UTF-8 bytes; COMPOSITION is
+uncommitted preedit. Apply a matching-revision edit once, update your snapshot, and
+ignore/rebase stale edits. Activation generation distinguishes controls. Ordinary key
+text remains TEXT. Never insert preedit as committed document text. Wayland requires
+text-input-v3 for native IME; absence is reported as unsupported. Cocoa translates
+native UTF-16 replacement/deletion to snapshot UTF-8 ranges. X11 XIM supplies ordinary
+text/preedit but cannot promise surrounding-text replacement support.
+
+Clipboard set copies UTF-8; read returns a nonzero request ID and later CLIPBOARD
+completion with OK, UNAVAILABLE, or FAILED. Empty successful text is distinct from
+failure. Destroying the target cancels transfer and delivery. Primary selection stays
+independent and reports unsupported where unavailable. Linux external transfers time
+out after 30 seconds; X11 currently lacks INCR for large transfers. File drops carry
+URI-list text; `yasl_drop_next_path` decodes supported local paths into caller buffers.
+
+Popup arguments use logical parent anchor coordinates, anchor/gravity directions,
+and flip/slide/resize constraint bits. Popup size includes its content. Destroying a
+parent retires descendants too. Native grabs, CSD moves/resizes, menus, and surface
+drags require a live triggering press event; unsupported/expired input fails explicitly.
+Surface drags support application tab/window carry; they are not an arbitrary MIME
+source API. Check operation results even when feature bits advertise support.
+
+## Backend limits
+
+Wayland support varies by compositor protocols; no portable absolute placement or
+minimized-state query is assumed. X11 currently reports unit scale and lacks an
+implemented relative-pointer lock. Cocoa requires macOS 14+; native Metal/GL and
+Objective-C paths require validation on macOS. EGL/wayland-egl and Vulkan SDK support
+are independently optional at build time. Feature bits describe implementation/
+server support, not guaranteed permission or attached hardware. The current work was
+compiled and tested on Linux; macOS validation remains a separate required check.
diff --git a/docs/migrating.md b/docs/migrating.md
deleted file mode 100644
index 912ea4d..0000000
--- a/docs/migrating.md
+++ /dev/null
@@ -1,132 +0,0 @@
-# Migrating pre-release applications
-
-YASL 0.1 has no stable source or binary compatibility promise. Rebuild all
-consumers against matching public headers and the library after updating.
-See the [application guide](guide.md) for the full window and input workflow.
-
-## Display queue, frames, and application failures
-
-The runner now consumes the display-wide FIFO directly. Manual loops should
-replace per-surface polling with `yasl_display_poll_event(display, &event)`.
-Surface polling remains an input-only adapter that discards pending non-input
-notifications delivered through legacy callbacks. Do not mix polling styles. Strings from either poll
-now survive recipient destruction and expire at the next successful poll on
-that display, or display close. Queue allocation failure is reported by dispatch
-as an error instead of silently losing a lifecycle notification.
-
-Replace `struct yasl_frame_context` with `struct yasl_frame`, `frame->cpu.pixels`
-with `frame->target.pixels`, and `frame->size` with `frame->target.size`.
-`target.stride_bytes` replaces `cpu.stride_bytes`. Native drawing uses
-`yasl_surface_get_native_frame(surface, &frame->target, &native)`.
-`yasl_frame_present(frame, NULL, 0)` replaces the CPU presentation wrapper.
-A non-NULL damage array with count zero now explicitly means no damaged pixels.
-Lists larger than the backend limit become full damage instead of truncating.
-
-`YASL_APP_ERROR` and `yasl_app_exit(display, result)` report application failures
-through `yasl_run`. `yasl_app_window(display)` gives init access to desc.window.
-Use `yasl_surface_set_userdata` / `yasl_surface_userdata` for per-window state,
-or `window.userdata` during creation. `yasl_surface_set_callbacks` now returns a
-boolean and rejects runner-owned surfaces with EBUSY.
-
-Spell toplevel creation sizes `size_logical`; `size` remains an alias.
-New window request functions report acceptance with errno. Pacing now works
-outside the runner too, and returns a boolean instead of silently ignoring calls.
-
-Replace clipboard copy/paste with `yasl_clipboard_set` / `yasl_clipboard_request`.
-Check the returned request ID and `event.paste.status` on completion. Empty text
-is successful; missing data and transfer errors now have explicit outcomes.
-Preedit snapshots arrive through `YASL_EVENT_COMPOSITION`; state and hover use
-`YASL_EVENT_WINDOW_STATE`, `YASL_EVENT_DRAG`, and `YASL_EVENT_DROP_HOVER`.
-
-For worker messages, capture a surface ID on the display thread and use
-`yasl_display_post` with an optional release callback. Do not also free the
-payload in the event handler when release owns cleanup. Stop producer threads
-before display shutdown; retired IDs reject posting with ENOENT.
-
-## Application callbacks
-
-All examples now use `<yasl/app.h>` and `yasl_run()`. Describe a single window
-in `yasl_app_desc.window`, or create windows in `.init`; move input handling to
-`.event` and drawing to `.frame`.
-Replace manual dispatch/poll/flush loops with the runner and request redraws with
-`yasl_surface_request_frame()`. Present the frame context with
-`yasl_frame_present()`; the runner destroys remaining surfaces on exit.
-
-Window state remains a `YASL_STATE_*` bitmask; `YASL_STATE_TILED` is reported
-for tiled Wayland toplevels.
-
-## Events
-
-The core API and the runner now share one `struct yasl_event`, declared in
-`<yasl/yasl.h>`. `yasl_display_poll_event()` returns the same events that
-`yasl_app_desc.event` receives. Legacy surface polling returns only input types.
-
-| Previous use | Replacement |
-| --- | --- |
-| `yasl_surface_input_poll()`, `struct yasl_input_event` | `yasl_surface_poll_event()`, `struct yasl_event` |
-| `event.kind`, `YASL_INPUT_*` | `event.type`, `YASL_EVENT_*` |
-| `YASL_INPUT_MOUSE` with `mouse.kind` | `YASL_EVENT_POINTER_DOWN`, `_UP`, `_MOTION`, `_SCROLL`, `_LEAVE` with `event.pointer` |
-| Runner `YASL_EVENT_MOUSE_BUTTON` with `pressed` | `YASL_EVENT_POINTER_DOWN` / `_UP` |
-| Integer `button` 0/1/2 | `YASL_BUTTON_PRIMARY`, `_MIDDLE`, `_SECONDARY`, `_BACK`, `_FORWARD` |
-| `paste.text`, `drop_files.files`, `text.text` | `text.data` for `PASTE`, `DROP`, and `TEXT` |
-| `YASL_INPUT_DROP_FILES` / `YASL_EVENT_DROP_FILES` | `YASL_EVENT_DROP` |
-| `YASL_EVENT_WINDOW_FOCUS` | `YASL_EVENT_FOCUS` |
-| `YASL_EVENT_WINDOW_SCALE_CHANGED` | `YASL_EVENT_WINDOW_RESIZE` with `resize.scale_changed` |
-| `YASL_EVENT_WINDOW_CLOSE_REQUEST` | `YASL_EVENT_WINDOW_CLOSE` |
-| `user.type` | `user.code` |
-| `key.repeat` | `key.action == YASL_KEY_REPEAT` |
-| `yasl_surface_set_mouse_mode()` | `yasl_surface_set_pointer_mode()` |
-
-Every event carries a monotonic `time_ns`. Pointer motion carries `delta` on
-both paths. Handle `YASL_EVENT_INPUT_RESET` by clearing held input and active
-strokes.
-
-## Window creation
-
-Rename `struct yasl_surface_spec` to `struct yasl_window_spec` and
-`struct yasl_surface_cbs` to `struct yasl_window_cbs`. Surface handles and
-functions retain their names. Supply the window's callback table in `spec.cbs`
-and its userdata in `spec.ud` when calling `yasl_surface_create_toplevel()` or
-`yasl_surface_create_native()`. Callback tables are copied, and userdata is
-borrowed. The new window's callbacks cannot run before creation returns; read
-initial geometry with `yasl_surface_geometry()` and request the first redraw
-after initializing application state.
-
-`yasl_surface_set_callbacks()` remains available to replace callbacks after
-creation and to configure popups. A NULL `spec.cbs` creates a window without
-callbacks. Displays owned by `yasl_run()` use the runner's callbacks instead;
-provide application notifications through `yasl_app_desc.event` and `.frame`.
-
-## Keyboard events
-
-| Previous use | Replacement |
-| --- | --- |
-| Linux keysym or Cocoa virtual keycode in `key.key` | Portable physical `YASL_KEY_*` identity |
-| `key.base` | `key.native_code`, with explicit backend-specific encoding |
-| `key.keypad` | Distinct `YASL_KEY_KP_*` identities |
-| Advisory `key.text` character | `YASL_EVENT_TEXT` UTF-8 payload for insertion |
-| Layout label from a keysym | Optional `key.codepoint` and `key.shifted` Unicode scalars |
-
-Replace magic numbers and X11 symbols with named keys. For example, a quit
-command checks `key.key == YASL_KEY_ESCAPE` on a press. `YASL_KEY_A` means the
-US A position on every layout; use layout labels when a command should follow
-the printed letter instead. Do not cast key identities to characters. Keypad
-Enter and ordinary Enter now have distinct identities without a separate flag.
-
-Text comes only from `YASL_EVENT_TEXT`. Handle its byte length, copy it when
-retaining it after the next successful poll, and allow multiple characters per
-commit. Cocoa sends key events through its text input manager; Wayland emits
-ordinary XKB text and locale compose results in addition to IME commits.
-
-## Scroll events
-
-Replace `scroll_lines`, `scroll_delta`, and scroll button numbers with
-`pointer.scroll.x` and `.y` for `YASL_EVENT_POINTER_SCROLL`. These double
-values are fractional line offsets, positive right/down on every backend.
-Do not infer scroll direction from `button`. One event may include both axes.
-Remove backend-specific sign inversions and whole-line casts; multiply the
-offset by your application's preferred pixels per line instead.
-
-X11 still supplies whole wheel steps. Wayland high-resolution wheels and smooth
-offsets, and Cocoa precise scrolling, preserve fractions. Device acceleration
-and system preferences still affect the native offsets.
diff --git a/docs/testing.md b/docs/testing.md
index bd3f2f0..ce2082f 100644
--- a/docs/testing.md
+++ b/docs/testing.md
@@ -1,156 +1,36 @@
-# Testing and examples
-
-Configure isolated Linux display-server integration tests with Weston and
-Xwayland installed:
-
-```sh
-meson setup build-test -Dintegration_tests=true
-meson test -C build-test --print-errorlogs
-```
-
-The harness starts and cleans up its own servers. It checks explicit requests,
-automatic selection, fallback, both backends in one process, frame lifetimes,
-callback copying, clipboard ownership, and timers. It also runs the beginner
-and tablet examples on both backends. On macOS the option enables native window,
-input ownership, keyboard/text/scroll, cursor, tablet, and example tests; these
-require a desktop session. Linux results do not verify Cocoa behavior.
-
-For memory/undefined-behavior checks use a separate build:
-
-```sh
-meson setup build-sanitize -Db_sanitize=address,undefined -Dintegration_tests=true
-meson test -C build-sanitize --print-errorlogs
-```
-
-Default tests need no desktop connection. C++ header coverage is built when a
-C++ compiler is available. On Linux and macOS, the shared-export test ensures
-only public API declarations are exported. Test disabled-backend configurations separately with
-`-Dwayland=disabled` or `-Dx11=disabled`; the combined integration harness needs
-both backends.
-
-Keyboard tests check physical identities, distinct keypad/modifier keys, layout
-labels, Shift/Caps Lock, shortcut text suppression, AltGr, dead-key composition,
-repeat, and composition reset. The X11 integration test changes and restores
-the isolated server's keymap to verify that labels change while physical keys
-retain their identity. The X11 text fallback is also checked without an input
-method, including Latin-1, Cyrillic, and non-BMP characters encoded as UTF-8.
-Scroll tests cover both axes, fractional Wayland offsets,
-duplicate protocol representations, and X11 core/XInput wheel directions.
-Cocoa integration tests cover UTF-16 labels, modifier-only events, native text
-commit routing, and diagonal precise scrolling. Native IME interaction and
-hardware gestures still need testing on the target desktop.
-Clipboard tests cover Latin-1 interoperability, empty transfers, and rejection
-of malformed UTF-8 on Linux.
-
-## Cocoa validation
-
-On macOS 14 or newer, use a logged-in desktop session with a Metal-capable
-device. Configure `-Dintegration_tests=true`, compile, and run the full suite.
-Cross-compiling with an Apple SDK checks compilation and linking; run
-`meson test -C build-macos public-exports` to check the dylib's exports on the
-build machine. A cross file should provide an `nm` that can read Mach-O, such
-as `llvm-nm`. These checks do not execute the native window and input tests.
-
-To move an already compiled, unsanitized Cocoa suite to another Mac without
-rebuilding it:
-
-```sh
-python3 tests/helpers/package_cocoa_tests.py build-macos cocoa-tests.tar.gz
-```
-
-On the matching Mac, extract the archive and run:
-
-```sh
-tar -xzf cocoa-tests.tar.gz
-python3 yasl-cocoa-tests/run.py
-```
-
-The bundle includes the dylib, every executable test registered by Meson, and
-the license. The build-machine export check is separate. The runner checks file
-hashes, enforces each test's timeout, saves output under `logs/`, and writes
-`results.json`. A nonzero exit means validation failed. `--list` lists the
-bundled tests without running them. Use Python 3.8 or newer and a bundle matching
-its architecture; Apple Silicon uses `aarch64`, Intel uses `x86_64`.
-
-## Tablet drawing example
-
-Build and run the same C application on Wayland, X11, or Cocoa:
+# Tests
```sh
meson setup build
meson compile -C build
-./build/yasl-tablet
-./build/yasl-tablet --backend wayland
-./build/yasl-tablet --backend x11
-# On macOS:
-./build/yasl-tablet --backend cocoa
+meson test -C build --print-errorlogs
```
-Draw with the pen tip: pressure controls brush width. Flip to the eraser to
-remove ink. The window shows the selected backend, tool, contact state, raw
-pressure (0..65535), percentage, and tilt in degrees, with a live pressure bar,
-brush outline, and tilt vector. The system cursor is hidden so the brush overlay
-serves as the drawing cursor. Mouse drawing uses a fixed width. Press C or
-Space to clear and Escape to quit. Resizing and display scale changes preserve
-the drawing. `--smoke-test` presents one frame and exits for automated checks.
-
-[`examples/tablet.c`](../examples/tablet.c) includes `<yasl/app.h>` and the C
-standard library. It uses `yasl_run()`, frame callbacks, and unified input events
-on every backend. Tablet button and motion events retain `device`, which is
-`YASL_PTR_DEV_PEN` or `YASL_PTR_DEV_ERASER`; their `pen` member contains
-pressure and signed tilt. Button 0 is tip contact on every backend. Track tip
-press/release events: zero pressure alone does not mean release. Hover and side
-buttons do not paint, and focus loss, leave, or `YASL_EVENT_INPUT_RESET` cancels a stroke.
-Stationary pressure changes arrive as motion events and update the brush too.
-
-The example uses portable physical keys: C and Space clear, Escape exits.
-Letter names describe US positions, so the clear key follows the C position
-when the user selects another layout.
+Tests cover public static/shared C and C++11 consumers, exact shared exports,
+queue ownership/order/destruction, UTF-8, GL attributes, private native event loops,
+clipboard, IME editing, pointer/pen coherence, cursor, and popup positioning. Native
+unit fixtures deliberately use private implementation headers; applications must use
+installed public headers.
-`YASL_CAP_TABLET` indicates backend/protocol support, not whether a tablet is
-plugged in or which axes it has. Missing axes are zero. Wayland requires the
-compositor's `zwp_tablet_manager_v2`; X11 requires XInput 2 and driver-provided
-`Abs Pressure`/`Abs Tilt X`/`Abs Tilt Y` valuators. Cocoa handles both tablet mouse
-subtypes and native tablet point/proximity events. X11 identifies erasers by
-the Wacom tool property or the driver's device name. Hardware and driver
-support determine which readings are available. The example displays a message
-when the server has no tablet protocol and still allows mouse drawing.
-
-The test suite checks synthetic Wayland/XInput samples and the drawing behavior
-without hardware, including pressure-only changes, sparse axes, eraser input,
-hover, release, reset, and scale changes. Linux integration tests launch the
-example on isolated Wayland and X11 servers; macOS integration tests also check
-Cocoa tablet event translation. Physical tablet testing is still needed to
-validate a particular device/driver combination.
-
-For an end-to-end X11 pressure test without hardware, install Xorg's `dummy`
-video driver and `inputtest` input driver, then run:
+For private Linux display servers and actual GPU presentation, install Weston,
+Xwayland, EGL/wayland-egl, and Vulkan/Mesa software drivers, then:
```sh
-meson configure build -Dx11_tablet_tests=true
-meson test -C build tablet-x11-native --print-errorlogs
+meson configure build -Dintegration_tests=true
+meson compile -C build
+meson test -C build --print-errorlogs
```
-This starts an isolated Xorg server with virtual pen and eraser devices, sends
-real driver samples, checks YASL's shared events through the example, and verifies
-its presented window pixels. It checks stationary pressure changes, release
-without valuators, and eraser identification through the driver's tool property.
-The test disables automatic physical input/GPU attachment and cleans up its
-server. Set `YASL_XORG_MODULE_PATH` to a comma-separated module search path if
-the test drivers are outside Xorg's default directories.
-
-## Benchmarks
+The integration harness starts its own headless GL Weston and Xwayland, runs public
+surface ownership/CPU damage/wakeup/refresh checks, GL drawing/readback/context-sharing/
+resize, Vulkan swapchain clear/present, and example smoke tests. It selects Mesa
+software rendering and Lavapipe when available. It needs permission to create local
+Unix sockets. A sandbox blocking those sockets is not a backend test result.
-`yasl-benchmark` measures queue, timer, and surface-operation costs. Results
-depend on the build, backend, and display server; they do not measure confirmed
-screen presentation latency.
-
-```sh
-# Run via meson test runner
-meson test -C build --benchmark --verbose
-
-# Or invoke the executable directly
-ninja -C build tests/yasl-benchmark
-./build/tests/yasl-benchmark
-```
+`-Dx11_tablet_tests=true` additionally uses Xorg dummy/inputtest drivers for pen input
+without physical hardware. These are separate from the compositor-free native unit
+fixtures. Cocoa integration tests run only on macOS and need the native SDK/display.
+Linux results do not validate Cocoa compilation or runtime.
+For memory/undefined-behavior checks use a separate build with `-Db_sanitize=address,undefined`
+and run the same suites. Keep sanitizer builds isolated from the normal build.
diff --git a/examples/game.c b/examples/game.c
index 33c2f6e..79beb7c 100644
--- a/examples/game.c
+++ b/examples/game.c
@@ -1,360 +1,53 @@
-/**
- * @file game.c
- * A continuous 60/144+ FPS game loop demonstration built on YASL Approach A.
- *
- * Demonstrates:
- * - Continuous display-synchronized ticks (YASL_PACING_CONTINUOUS)
- * - 3D software rendering (rotating shaded cube + starfield)
- * - WASD continuous player movement & key state tracking
- * - Relative pointer mode (YASL_POINTER_RELATIVE) for smooth first-person camera orbit
- * - Spacebar toggles pointer lock (Relative vs Normal cursor)
- * - Real-time FPS counter and frame latency display
- * - Dynamic window resizing with automatic aspect ratio adjustment
- */
-
-#define _DEFAULT_SOURCE
-#include <yasl/app.h>
-
-#include <math.h>
-#include <stdbool.h>
-#include <stdint.h>
+/* GPU example: the application owns context binding, drawing and swap pacing. */
+#include <yasl/yasl.h>
+#include <yasl/gl.h>
#include <stdio.h>
-#include <stdlib.h>
+#include <math.h>
#include <string.h>
-#define NUM_STARS 256
-#define FOV_SCALE 320.0f
-
-struct star {
- float x, y, z;
-};
-
-struct vec3 {
- float x, y, z;
-};
-
-struct game_state {
- struct yasl_display *display;
- struct yasl_surface *surface;
-
- /* Camera & controls */
- float cam_x, cam_y, cam_z;
- float cam_yaw, cam_pitch;
- bool key_w, key_a, key_s, key_d;
- bool key_q, key_e;
- bool relative_mouse;
-
- /* Simulation objects */
- float cube_rot_x, cube_rot_y;
- struct star stars[NUM_STARS];
-
- /* FPS & timing stats */
- uint64_t last_stat_time_ns;
- int frame_count;
- float current_fps;
- float avg_frame_time_ms;
-};
-
-/* 8x8 bitmap font for overlay HUD */
-static const uint8_t font8x8_digits[10][8] = {
- {0x3C,0x66,0x6E,0x76,0x66,0x66,0x3C,0x00}, /* 0 */
- {0x18,0x38,0x18,0x18,0x18,0x18,0x7E,0x00}, /* 1 */
- {0x3C,0x66,0x06,0x0C,0x18,0x30,0x7E,0x00}, /* 2 */
- {0x3C,0x66,0x06,0x1C,0x06,0x66,0x3C,0x00}, /* 3 */
- {0x0C,0x1C,0x34,0x64,0x7E,0x04,0x04,0x00}, /* 4 */
- {0x7E,0x60,0x7C,0x06,0x06,0x66,0x3C,0x00}, /* 5 */
- {0x1C,0x30,0x60,0x7C,0x66,0x66,0x3C,0x00}, /* 6 */
- {0x7E,0x06,0x0C,0x18,0x30,0x30,0x30,0x00}, /* 7 */
- {0x3C,0x66,0x66,0x3C,0x66,0x66,0x3C,0x00}, /* 8 */
- {0x3C,0x66,0x66,0x3E,0x06,0x0C,0x38,0x00}, /* 9 */
-};
-
-static void draw_digit(uint32_t *pix, int w, int h, int px, int py, int digit, uint32_t col, int s)
-{
- if (digit < 0 || digit > 9) return;
- const uint8_t *glyph = font8x8_digits[digit];
- for (int y = 0; y < 8; y++) {
- uint8_t row = glyph[y];
- for (int x = 0; x < 8; x++) {
- if (row & (0x80 >> x)) {
- for (int dy = 0; dy < s; dy++) {
- int yy = py + y * s + dy;
- if (yy < 0 || yy >= h) continue;
- for (int dx = 0; dx < s; dx++) {
- int xx = px + x * s + dx;
- if (xx >= 0 && xx < w) pix[(size_t)yy * w + xx] = col;
- }
- }
- }
+typedef void (*clear_color_fn)(float, float, float, float);
+typedef void (*clear_fn)(unsigned);
+typedef void (*viewport_fn)(int, int, int, int);
+
+int main(int argc, char **argv)
+{
+ bool smoke = argc == 2 && !strcmp(argv[1], "--smoke-test");
+ yasl_surface *s = yasl_gl_surface_create("YASL OpenGL", 800, 600, 0, NULL, NULL);
+ if (!s || !yasl_gl_make_current(s)) {
+ const char *error;
+ yasl_get_error(&error);
+ fprintf(stderr, "%s\n", error);
+ yasl_shutdown();
+ return 1;
+ }
+ clear_color_fn clear_color = (clear_color_fn)yasl_gl_get_proc_address("glClearColor");
+ clear_fn clear = (clear_fn)yasl_gl_get_proc_address("glClear");
+ viewport_fn viewport = (viewport_fn)yasl_gl_get_proc_address("glViewport");
+ if (!clear_color || !clear || !viewport) {
+ yasl_shutdown();
+ return 1;
+ }
+ yasl_gl_swap_interval(s, 1);
+ int result = 0, frames = 0;
+ for (bool running = true; running;) {
+ yasl_event e;
+ int n;
+ while ((n = yasl_wait_event(&e, 0)) > 0) {
+ if (e.type == YASL_EVENT_CLOSE || e.type == YASL_EVENT_QUIT ||
+ (e.type == YASL_EVENT_KEY && e.key.key == YASL_KEY_ESCAPE)) running = false;
+ if (e.type == YASL_EVENT_DISPLAY_LOST) { result = 1; running = false; }
}
- }
-}
-
-static void draw_number(uint32_t *pix, int w, int h, int px, int py, int num, uint32_t col, int scale)
-{
- char buf[16];
- snprintf(buf, sizeof(buf), "%d", num);
- for (int i = 0; buf[i]; i++) {
- draw_digit(pix, w, h, px + i * 8 * scale, py, buf[i] - '0', col, scale);
- }
-}
-
-static void draw_line(uint32_t *pix, int w, int h, int x0, int y0, int x1, int y1, uint32_t col)
-{
- int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
- int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
- int err = dx + dy, e2;
-
- while (1) {
- if (x0 >= 0 && x0 < w && y0 >= 0 && y0 < h) {
- pix[(size_t)y0 * w + x0] = col;
- }
- if (x0 == x1 && y0 == y1) break;
- e2 = 2 * err;
- if (e2 >= dy) { err += dy; x0 += sx; }
- if (e2 <= dx) { err += dx; y0 += sy; }
- }
-}
-
-static struct vec3 rotate_vertex(struct vec3 v, float rx, float ry)
-{
- /* Rotate around Y */
- float cy = cosf(ry), sy = sinf(ry);
- float x1 = v.x * cy + v.z * sy;
- float z1 = -v.x * sy + v.z * cy;
-
- /* Rotate around X */
- float cx = cosf(rx), sx = sinf(rx);
- float y2 = v.y * cx - z1 * sx;
- float z2 = v.y * sx + z1 * cx;
-
- return (struct vec3){x1, y2, z2};
-}
-
-static bool project_point(struct vec3 p, int w, int h, int *out_x, int *out_y)
-{
- if (p.z <= 0.2f) return false;
- float inv_z = FOV_SCALE / p.z;
- *out_x = (int)(w * 0.5f + p.x * inv_z);
- *out_y = (int)(h * 0.5f - p.y * inv_z);
- return true;
-}
-
-static enum yasl_app_result on_init(void *ud, struct yasl_display *d)
-{
- struct game_state *game = ud;
- game->display = d;
-
- struct yasl_window_spec spec = {
- .title = "YASL 3D Game Loop (Approach A)",
- .size_logical = {800, 600},
- };
- game->surface = yasl_surface_create_toplevel(d, &spec);
- if (!game->surface) return YASL_APP_ERROR;
-
- /* Drive continuous 60/144Hz ticks */
- if (!yasl_surface_set_pacing(game->surface, YASL_PACING_CONTINUOUS))
- return YASL_APP_ERROR;
-
- game->cam_z = 3.5f;
- game->relative_mouse = false;
-
- /* Initialize stars */
- for (int i = 0; i < NUM_STARS; i++) {
- game->stars[i].x = ((float)(rand() % 2000) - 1000.0f) / 100.0f;
- game->stars[i].y = ((float)(rand() % 2000) - 1000.0f) / 100.0f;
- game->stars[i].z = ((float)(rand() % 1000) + 10.0f) / 50.0f;
- }
-
- game->last_stat_time_ns = yasl_loop_now_ns();
- return YASL_APP_CONTINUE;
-}
-
-static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev)
-{
- struct game_state *game = ud;
-
- switch (ev->type) {
- case YASL_EVENT_KEY: {
- bool down = ev->key.action != YASL_KEY_RELEASE;
-
- switch (ev->key.key) {
- case YASL_KEY_W: game->key_w = down; break;
- case YASL_KEY_S: game->key_s = down; break;
- case YASL_KEY_A: game->key_a = down; break;
- case YASL_KEY_D: game->key_d = down; break;
- case YASL_KEY_Q: game->key_q = down; break;
- case YASL_KEY_E: game->key_e = down; break;
- case YASL_KEY_SPACE:
- if (ev->key.action == YASL_KEY_PRESS) {
- /* Toggle relative pointer mode; unsupported backends stay normal. */
- game->relative_mouse = !game->relative_mouse &&
- yasl_surface_set_pointer_mode(game->surface, YASL_POINTER_RELATIVE);
- if (!game->relative_mouse)
- yasl_surface_set_pointer_mode(game->surface, YASL_POINTER_NORMAL);
- }
- break;
- case YASL_KEY_ESCAPE:
- if (ev->key.action == YASL_KEY_PRESS) {
- if (game->relative_mouse) {
- game->relative_mouse = false;
- yasl_surface_set_pointer_mode(game->surface, YASL_POINTER_NORMAL);
- } else {
- return YASL_APP_QUIT;
- }
- }
- break;
- default:
- break;
- }
- break;
- }
-
- case YASL_EVENT_POINTER_MOTION:
- if (game->relative_mouse) {
- /* Smooth first-person camera orbit */
- game->cam_yaw += (float)ev->pointer.delta.x * 0.005f;
- game->cam_pitch += (float)ev->pointer.delta.y * 0.005f;
- if (game->cam_pitch > 1.4f) game->cam_pitch = 1.4f;
- if (game->cam_pitch < -1.4f) game->cam_pitch = -1.4f;
- }
- break;
-
- case YASL_EVENT_POINTER_DOWN:
- if (!game->relative_mouse) {
- /* Click window to capture cursor */
- game->relative_mouse =
- yasl_surface_set_pointer_mode(game->surface, YASL_POINTER_RELATIVE);
- }
- break;
-
- case YASL_EVENT_WINDOW_CLOSE:
- return YASL_APP_QUIT;
-
- default:
- break;
- }
-
- return YASL_APP_CONTINUE;
-}
-
-static void on_frame(void *ud, struct yasl_surface *s, struct yasl_frame *ctx)
-{
- (void)s;
- struct game_state *game = ud;
- int w = ctx->target.size.x;
- int h = ctx->target.size.y;
- uint32_t *pix = ctx->target.pixels;
- if (!pix || w <= 0 || h <= 0) return;
-
- /* Update game simulation */
- float speed = 0.04f;
- float forward_x = sinf(game->cam_yaw);
- float forward_z = cosf(game->cam_yaw);
- float right_x = cosf(game->cam_yaw);
- float right_z = -sinf(game->cam_yaw);
-
- if (game->key_w) { game->cam_x -= forward_x * speed; game->cam_z -= forward_z * speed; }
- if (game->key_s) { game->cam_x += forward_x * speed; game->cam_z += forward_z * speed; }
- if (game->key_a) { game->cam_x -= right_x * speed; game->cam_z -= right_z * speed; }
- if (game->key_d) { game->cam_x += right_x * speed; game->cam_z += right_z * speed; }
- if (game->key_q) { game->cam_y -= speed; }
- if (game->key_e) { game->cam_y += speed; }
-
- game->cube_rot_x += 0.015f;
- game->cube_rot_y += 0.025f;
-
- /* Clear screen to deep space dark blue */
- memset(pix, 0x08, (size_t)w * h * sizeof(uint32_t));
-
- /* 1. Render 3D Starfield */
- for (int i = 0; i < NUM_STARS; i++) {
- game->stars[i].z -= 0.05f;
- if (game->stars[i].z <= 0.2f) game->stars[i].z += 20.0f;
-
- struct vec3 sp = {game->stars[i].x - game->cam_x,
- game->stars[i].y - game->cam_y,
- game->stars[i].z};
- int sx, sy;
- if (project_point(sp, w, h, &sx, &sy) && sx >= 0 && sx < w && sy >= 0 && sy < h) {
- uint8_t brightness = (uint8_t)(255.0f * (1.0f - sp.z / 20.0f));
- uint32_t star_col = 0xff000000 | (brightness << 16) | (brightness << 8) | brightness;
- pix[(size_t)sy * w + sx] = star_col;
- }
- }
-
- /* 2. Render 3D Cube */
- static const struct vec3 cube_vertices[8] = {
- {-1.0f, -1.0f, -1.0f}, { 1.0f, -1.0f, -1.0f},
- { 1.0f, 1.0f, -1.0f}, {-1.0f, 1.0f, -1.0f},
- {-1.0f, -1.0f, 1.0f}, { 1.0f, -1.0f, 1.0f},
- { 1.0f, 1.0f, 1.0f}, {-1.0f, 1.0f, 1.0f},
- };
-
- static const int cube_edges[12][2] = {
- {0, 1}, {1, 2}, {2, 3}, {3, 0},
- {4, 5}, {5, 6}, {6, 7}, {7, 4},
- {0, 4}, {1, 5}, {2, 6}, {3, 7},
- };
-
- int proj_x[8], proj_y[8];
- bool valid[8];
-
- for (int i = 0; i < 8; i++) {
- struct vec3 rot = rotate_vertex(cube_vertices[i], game->cube_rot_x, game->cube_rot_y);
- rot.x -= game->cam_x;
- rot.y -= game->cam_y;
- rot.z += game->cam_z;
- valid[i] = project_point(rot, w, h, &proj_x[i], &proj_y[i]);
- }
-
- uint32_t cyan = 0xff00ffff;
- for (int i = 0; i < 12; i++) {
- int u = cube_edges[i][0];
- int v = cube_edges[i][1];
- if (valid[u] && valid[v]) {
- draw_line(pix, w, h, proj_x[u], proj_y[u], proj_x[v], proj_y[v], cyan);
- }
- }
-
- /* 3. Center crosshair in relative mouse mode */
- if (game->relative_mouse) {
- int cx = w / 2, cy = h / 2;
- draw_line(pix, w, h, cx - 6, cy, cx + 6, cy, 0xffffffff);
- draw_line(pix, w, h, cx, cy - 6, cx, cy + 6, 0xffffffff);
- }
-
- /* 4. FPS counter calculation */
- game->frame_count++;
- uint64_t now = yasl_loop_now_ns();
- if (now - game->last_stat_time_ns >= 1000000000ull) {
- uint64_t elapsed = now - game->last_stat_time_ns;
- game->current_fps = (float)game->frame_count * 1000000000.0f / (float)elapsed;
- game->avg_frame_time_ms = 1000.0f / (game->current_fps > 0 ? game->current_fps : 1.0f);
- game->frame_count = 0;
- game->last_stat_time_ns = now;
- }
-
- /* Draw FPS overlay digits in top-left */
- int scale = ctx->scale_120 >= 240 ? 2 : 1;
- draw_number(pix, w, h, 16 * scale, 16 * scale, (int)game->current_fps, 0xff00ff00, scale);
-
- /* Submit frame */
- if (!yasl_frame_present(ctx, NULL, 0))
- yasl_app_exit(yasl_surface_display(s), YASL_APP_ERROR);
-}
-
-int main(void)
-{
- static struct game_state game;
- struct yasl_app_desc desc = {
- .app_id = "org.yasl.game",
- .backend = YASL_BACKEND_AUTO,
- .userdata = &game,
- .init = on_init,
- .event = on_event,
- .frame = on_frame,
- };
-
- return yasl_run(&desc);
+ if (n < 0) { result = 1; break; }
+ int w, h;
+ yasl_surface_get_framebuffer_size(s, &w, &h);
+ viewport(0, 0, w, h);
+ double t = yasl_time_ns() / 1e9;
+ clear_color((float)(0.3 + 0.2 * sin(t)), 0.2f, 0.4f, 1);
+ clear(0x00004000u);
+ if (!yasl_gl_swap_buffers(s)) { result = 1; break; }
+ if (smoke && ++frames == 3) break;
+ }
+ yasl_gl_make_current(NULL);
+ yasl_shutdown();
+ return result;
}
diff --git a/examples/hello.c b/examples/hello.c
index 31ab18b..9e324a6 100644
--- a/examples/hello.c
+++ b/examples/hello.c
@@ -1,38 +1,26 @@
-/* A complete CPU-rendered window. Build instructions: docs/getting-started.md. */
-#include <yasl/app.h>
-#include <stdio.h>
+/* A CPU-rendered window. Build instructions: docs/getting-started.md. */
+#include <yasl/yasl.h>
+#include <yasl/cpu.h>
-static bool failed;
-static void draw(void *ud, struct yasl_surface *surface, struct yasl_frame *frame)
+int main(void)
{
- (void)ud;
- int w = frame->target.size.x, h = frame->target.size.y;
- for (int y = 0; y < h; y++) {
- uint32_t *row = frame->target.pixels + (size_t)y * (frame->target.stride_bytes / 4);
- for (int x = 0; x < w; x++) {
- /* Red grows rightward, green downward, blue fades; opaque premultiplied ARGB. */
- uint32_t r = (uint32_t)(x * 255 / (w > 1 ? w - 1 : 1));
- uint32_t g = (uint32_t)(y * 255 / (h > 1 ? h - 1 : 1));
- row[x] = 0xff000000 | r << 16 | g << 8 | (255 - r);
+ yasl_surface *surface = yasl_surface_create("Hello YASL", 640, 480, YASL_SURFACE_CPU);
+ yasl_event event;
+ while (surface && yasl_wait_event(&event, -1) >= 0 &&
+ event.type != YASL_EVENT_CLOSE && event.type != YASL_EVENT_QUIT) {
+ if (event.type != YASL_EVENT_REFRESH) continue;
+ int w, h;
+ uint32_t *pixels = yasl_cpu_pixels(surface, &w, &h);
+ for (int y = 0; pixels && y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ /* Red grows rightward, green downward, blue fades; opaque premultiplied ARGB. */
+ uint32_t r = (uint32_t)(x * 255 / (w > 1 ? w - 1 : 1));
+ uint32_t g = (uint32_t)(y * 255 / (h > 1 ? h - 1 : 1));
+ pixels[(size_t)y * w + x] = 0xff000000 | r << 16 | g << 8 | (255 - r);
+ }
}
+ yasl_cpu_present(surface);
}
-
- if (!yasl_frame_present(frame, NULL, 0)) {
- fprintf(stderr, "Could not present the draw target\n");
- failed = true;
- }
- if (failed) yasl_app_exit(yasl_surface_display(surface), YASL_APP_ERROR);
-}
-
-int main(void)
-{
- struct yasl_window_spec window = {.title = "Hello YASL", .size_logical = {640, 480}};
- struct yasl_app_desc desc = {
- .app_id = "org.example.hello",
- .window = &window,
- .frame = draw,
- };
- int result = yasl_run(&desc);
- if (result) fprintf(stderr, "Could not run the application\n");
- return result || failed ? 1 : 0;
+ yasl_shutdown();
+ return surface ? 0 : 1;
}
diff --git a/examples/notepad.c b/examples/notepad.c
index 0b331e8..92b9d44 100644
--- a/examples/notepad.c
+++ b/examples/notepad.c
@@ -1,6 +1,6 @@
/**
* @file notepad.c
- * A full-featured desktop text editor built on YASL Approach A.
+ * A desktop text editor built on YASL's public CPU surface API.
*
* Features:
* - New (Ctrl+N), Open (Ctrl+O, CLI arg, drag-and-drop), Save (Ctrl+S), Save As (Ctrl+Shift+S)
@@ -14,14 +14,15 @@
* - Zoom In / Out / Reset (Ctrl+=, Ctrl+-, Ctrl+0)
* - Mouse selection with click, drag, double-click (word), triple-click (line)
* - Unsaved changes confirmation on exit
- * - Real-time asynchronous background stats via yasl_display_post()
- * - 0% CPU consumption when idle (YASL_PACING_ON_DEMAND)
+ * - Real-time asynchronous background stats via a worker and yasl_wake()
*/
#define _DEFAULT_SOURCE
-#include <yasl/app.h>
+#include <yasl/yasl.h>
+#include <yasl/cpu.h>
#include <ctype.h>
+#include <math.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
@@ -134,8 +135,6 @@ static const uint8_t font8x8[95][8] = {
{0x76,0xDC,0x00,0x00,0x00,0x00,0x00,0x00} /* ~ */
};
-#define MSG_STATS_READY 100
-
typedef enum prompt_mode {
PROMPT_NONE = 0,
PROMPT_OPEN,
@@ -160,8 +159,15 @@ struct undo_snapshot {
};
struct editor {
- struct yasl_display *display;
- struct yasl_surface *surface;
+ yasl_surface *surface;
+ double surface_scale;
+ bool dirty, running, failed;
+ uint64_t text_revision;
+ char ime_snapshot[MAX_LINE_LEN];
+ size_t ime_begin, ime_end;
+ bool ime_snapshot_ready;
+ double ime_caret_x, ime_caret_y;
+ bool ime_caret_ready;
/* File tracking */
char file_path[512];
@@ -182,7 +188,7 @@ struct editor {
/* Double/Triple click tracking */
uint64_t last_click_time;
- struct yasl_vector2i last_click_pos;
+ double last_click_x, last_click_y;
int click_count;
/* Viewport & Layout */
@@ -213,8 +219,10 @@ struct editor {
struct doc_stats stats;
_Atomic bool worker_running;
bool worker_started, worker_mutex_ready, worker_cond_ready;
- uint64_t worker_surface_id;
bool worker_dirty;
+ char (*worker_lines)[MAX_LINE_LEN];
+ int worker_line_count;
+ uint64_t stats_generation;
pthread_t worker_tid;
pthread_mutex_t worker_mutex;
pthread_cond_t worker_cond;
@@ -223,7 +231,13 @@ struct editor {
static void editor_set_status(struct editor *ed, const char *msg)
{
snprintf(ed->status_msg, sizeof(ed->status_msg), "%s", msg);
- ed->status_msg_time = yasl_loop_now_ns();
+ ed->status_msg_time = yasl_time_ns();
+ ed->dirty = true;
+}
+
+static void editor_request_render(struct editor *ed)
+{
+ if (ed) ed->dirty = true;
}
static void editor_update_title(struct editor *ed)
@@ -316,7 +330,13 @@ static void editor_redo(struct editor *ed)
static void editor_notify_worker(struct editor *ed)
{
+ char (*snapshot)[MAX_LINE_LEN] = malloc(sizeof(ed->lines));
+ if (!snapshot) return;
+ memcpy(snapshot, ed->lines, sizeof(ed->lines));
pthread_mutex_lock(&ed->worker_mutex);
+ free(ed->worker_lines);
+ ed->worker_lines = snapshot;
+ ed->worker_line_count = ed->line_count;
ed->worker_dirty = true;
pthread_cond_signal(&ed->worker_cond);
pthread_mutex_unlock(&ed->worker_mutex);
@@ -334,15 +354,17 @@ static void *worker_thread(void *arg)
pthread_mutex_unlock(&ed->worker_mutex);
break;
}
+ char (*lines)[MAX_LINE_LEN] = ed->worker_lines;
+ int line_count = ed->worker_line_count;
+ ed->worker_lines = NULL;
ed->worker_dirty = false;
pthread_mutex_unlock(&ed->worker_mutex);
-
- usleep(30000);
+ if (!lines) continue;
int word_count = 0;
int char_count = 0;
- for (int i = 0; i < ed->line_count; i++) {
- const char *p = ed->lines[i];
+ for (int i = 0; i < line_count; i++) {
+ const char *p = lines[i];
bool in_word = false;
while (*p) {
char_count++;
@@ -356,31 +378,19 @@ static void *worker_thread(void *arg)
}
}
+ free(lines);
if (!ed->worker_running)
break;
-
- struct doc_stats *s = malloc(sizeof(*s));
- if (s) {
- s->words = word_count;
- s->chars = char_count;
- if (!yasl_display_post(ed->display, &(struct yasl_message){
- .target_id = ed->worker_surface_id, .code = MSG_STATS_READY,
- .payload = s, .size = sizeof(*s), .release = free}))
- free(s);
- }
+ pthread_mutex_lock(&ed->worker_mutex);
+ ed->stats.words = word_count;
+ ed->stats.chars = char_count;
+ ed->stats_generation++;
+ pthread_mutex_unlock(&ed->worker_mutex);
+ yasl_wake();
}
return NULL;
}
-static uint64_t on_blink_timer(void *ud, yasl_timer_id id, uint64_t interval_ns)
-{
- (void)id;
- struct editor *ed = ud;
- ed->caret_visible = !ed->caret_visible;
- yasl_surface_request_frame(ed->surface);
- return interval_ns;
-}
-
/* File I/O */
static bool editor_open_file(struct editor *ed, const char *path)
{
@@ -472,27 +482,6 @@ static void editor_new_file(struct editor *ed)
editor_notify_worker(ed);
}
-static void decode_uri_path(const char *uri, char *out, size_t max_out)
-{
- if (strncmp(uri, "file://", 7) == 0) {
- uri += 7;
- if (strncmp(uri, "localhost/", 10) == 0) {
- uri += 9;
- }
- }
- size_t out_len = 0;
- while (*uri && *uri != '\r' && *uri != '\n' && out_len < max_out - 1) {
- if (*uri == '%' && isxdigit((unsigned char)uri[1]) && isxdigit((unsigned char)uri[2])) {
- char hex[3] = {uri[1], uri[2], '\0'};
- out[out_len++] = (char)strtol(hex, NULL, 16);
- uri += 3;
- } else {
- out[out_len++] = *uri++;
- }
- }
- out[out_len] = '\0';
-}
-
/* Selection helper */
static void get_selection_bounds(const struct editor *ed, int *r0, int *c0, int *r1, int *c1)
{
@@ -505,7 +494,8 @@ static void get_selection_bounds(const struct editor *ed, int *r0, int *c0, int
(ed->sel_anchor_row == ed->cursor_row && ed->sel_anchor_col <= ed->cursor_col)) {
*r0 = ed->sel_anchor_row; *c0 = ed->sel_anchor_col;
*r1 = ed->cursor_row; *c1 = ed->cursor_col;
- } else {
+ }
+ else {
*r0 = ed->cursor_row; *c0 = ed->cursor_col;
*r1 = ed->sel_anchor_row; *c1 = ed->sel_anchor_col;
}
@@ -535,7 +525,8 @@ static void editor_delete_selection(struct editor *ed)
char *line = ed->lines[r0];
int len = (int)strlen(line);
memmove(line + c0, line + c1, (size_t)(len - c1 + 1));
- } else {
+ }
+ else {
char *first = ed->lines[r0];
char *last = ed->lines[r1];
char temp[MAX_LINE_LEN];
@@ -585,7 +576,7 @@ static void editor_copy(struct editor *ed)
snprintf(buf, (size_t)MAX_LINES * MAX_LINE_LEN, "%s\n", ed->lines[ed->cursor_row]);
}
- if (!yasl_clipboard_set(ed->display, YASL_CLIPBOARD, buf))
+ if (!yasl_clipboard_set(YASL_CLIPBOARD, buf))
editor_set_status(ed, "Clipboard copy failed");
free(buf);
editor_set_status(ed, "Copied to clipboard");
@@ -995,30 +986,19 @@ static void prompt_execute(struct editor *ed)
}
}
-/* YASL App callbacks */
-static enum yasl_app_result on_init(void *ud, struct yasl_display *d)
+static bool editor_init(struct editor *ed)
{
- struct editor *ed = ud;
- ed->display = d;
-
- struct yasl_window_spec spec = {
- .title = "Untitled - YASL Notepad",
- .size_logical = {960, 640},
- };
- ed->surface = yasl_surface_create_toplevel(d, &spec);
- if (!ed->surface) return YASL_APP_ERROR;
- ed->worker_surface_id = yasl_surface_id(ed->surface);
-
- yasl_surface_set_pacing(ed->surface, YASL_PACING_ON_DEMAND);
- if (!yasl_surface_add_timer(ed->surface, YASL_MS_TO_NS(500), on_blink_timer, ed))
- return YASL_APP_ERROR;
+ ed->surface = yasl_surface_create("Untitled - YASL Notepad", 960, 640, YASL_SURFACE_CPU);
+ if (!ed->surface) return false;
+ ed->surface_scale = yasl_surface_get_scale(ed->surface);
+ yasl_surface_set_text_input(ed->surface, true, YASL_TEXT_NORMAL);
ed->worker_running = true;
- if (pthread_mutex_init(&ed->worker_mutex, NULL)) return YASL_APP_ERROR;
+ if (pthread_mutex_init(&ed->worker_mutex, NULL)) return false;
ed->worker_mutex_ready = true;
- if (pthread_cond_init(&ed->worker_cond, NULL)) return YASL_APP_ERROR;
+ if (pthread_cond_init(&ed->worker_cond, NULL)) return false;
ed->worker_cond_ready = true;
- if (pthread_create(&ed->worker_tid, NULL, worker_thread, ed)) return YASL_APP_ERROR;
+ if (pthread_create(&ed->worker_tid, NULL, worker_thread, ed)) return false;
ed->worker_started = true;
if (ed->has_file) {
@@ -1028,49 +1008,104 @@ static enum yasl_app_result on_init(void *ud, struct yasl_display *d)
}
editor_notify_worker(ed);
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ ed->running = true;
+ return true;
}
-static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev)
+static void editor_update_ime(struct editor *ed)
{
- struct editor *ed = ud;
+ if (!ed->surface || ed->line_count <= 0) return;
+ const char *line = ed->lines[ed->cursor_row];
+ size_t length = strlen(line);
+ size_t begin = (size_t)ed->cursor_col, end = begin;
+ if (ed->has_selection && ed->sel_anchor_row == ed->cursor_row) {
+ begin = (size_t)(ed->sel_anchor_col < ed->cursor_col ? ed->sel_anchor_col : ed->cursor_col);
+ end = (size_t)(ed->sel_anchor_col > ed->cursor_col ? ed->sel_anchor_col : ed->cursor_col);
+ }
+ if (begin > length) begin = length;
+ if (end > length) end = length;
+ bool snapshot_changed = !ed->ime_snapshot_ready || strcmp(ed->ime_snapshot, line) ||
+ begin != ed->ime_begin || end != ed->ime_end;
+ if (snapshot_changed) {
+ snprintf(ed->ime_snapshot, sizeof(ed->ime_snapshot), "%s", line);
+ ed->ime_begin = begin;
+ ed->ime_end = end;
+ ed->ime_snapshot_ready = true;
+ ed->text_revision++;
+ }
+ if (snapshot_changed)
+ yasl_surface_set_surrounding_text(ed->surface, ed->ime_snapshot, strlen(ed->ime_snapshot),
+ ed->ime_begin, ed->ime_end, ed->text_revision);
+ double scale = yasl_surface_get_scale(ed->surface);
+ int zoom = (scale >= 2.0 ? 2 : 1) + ed->zoom_offset;
+ if (zoom < 1) zoom = 1;
+ double caret_x = (48.0 * zoom + 8.0 * zoom + ed->cursor_col * 8.0 * zoom) / scale;
+ double caret_y = (24.0 * zoom + (ed->cursor_row - ed->scroll_y) * 16.0 * zoom) / scale;
+ if (!ed->ime_caret_ready || caret_x != ed->ime_caret_x || caret_y != ed->ime_caret_y) {
+ ed->ime_caret_x = caret_x;
+ ed->ime_caret_y = caret_y;
+ ed->ime_caret_ready = true;
+ yasl_surface_set_ime_caret(ed->surface, caret_x, caret_y, 2.0, 16.0);
+ }
+}
+static void on_event(struct editor *ed, const yasl_event *ev)
+{
switch (ev->type) {
case YASL_EVENT_TEXT:
if (ed->prompt != PROMPT_NONE) {
- for (size_t i = 0; i < ev->text.len && ed->prompt_len < (int)sizeof(ed->prompt_buf) - 1; i++) {
+ for (size_t i = 0; i < ev->text.length && ed->prompt_len < (int)sizeof(ed->prompt_buf) - 1; i++) {
if (ev->text.data[i] >= 32 && ev->text.data[i] <= 126) {
ed->prompt_buf[ed->prompt_len++] = ev->text.data[i];
ed->prompt_buf[ed->prompt_len] = '\0';
}
}
} else {
- insert_text(ed, ev->text.data, ev->text.len);
+ insert_text(ed, ev->text.data, ev->text.length);
}
- yasl_surface_request_frame(ed->surface);
+ editor_request_render(ed);
break;
- case YASL_EVENT_PASTE:
- if (ev->paste.status != YASL_TRANSFER_OK) {
- editor_set_status(ed, ev->paste.status == YASL_TRANSFER_UNAVAILABLE
+ case YASL_EVENT_CLIPBOARD:
+ if (ev->clipboard.status != YASL_TRANSFER_OK) {
+ editor_set_status(ed, ev->clipboard.status == YASL_TRANSFER_UNAVAILABLE
? "Clipboard has no text" : "Clipboard transfer failed");
- yasl_surface_request_frame(ed->surface);
+ editor_request_render(ed);
break;
}
- if (ev->text.data && ev->text.len > 0) {
- insert_text(ed, ev->text.data, ev->text.len);
- yasl_surface_request_frame(ed->surface);
+ if (ev->clipboard.data && ev->clipboard.length > 0) {
+ insert_text(ed, ev->clipboard.data, ev->clipboard.length);
+ editor_request_render(ed);
+ }
+ break;
+
+ case YASL_EVENT_TEXT_EDIT:
+ if (ed->prompt == PROMPT_NONE && ev->text_edit.data &&
+ ev->text_edit.revision == ed->text_revision &&
+ ev->text_edit.begin <= ev->text_edit.end &&
+ ev->text_edit.end <= strlen(ed->ime_snapshot)) {
+ ed->cursor_col = (int)ev->text_edit.begin;
+ ed->sel_anchor_row = ed->cursor_row;
+ ed->sel_anchor_col = (int)ev->text_edit.begin;
+ ed->cursor_col = (int)ev->text_edit.end;
+ ed->has_selection = ev->text_edit.begin != ev->text_edit.end;
+ insert_text(ed, ev->text_edit.data, ev->text_edit.length);
+ editor_request_render(ed);
}
break;
case YASL_EVENT_DROP:
- if (ev->text.data && ev->text.len > 0) {
+ if (ev->drop.action == YASL_DROP_DATA) {
+ const char *cursor = ev->drop.data;
char path[512];
- decode_uri_path(ev->text.data, path, sizeof(path));
- if (path[0]) {
- editor_open_file(ed, path);
- yasl_surface_request_frame(ed->surface);
+ size_t length;
+ while ((length = yasl_drop_next_path(&cursor, path, sizeof(path))) > 0) {
+ if (length < sizeof(path)) {
+ editor_open_file(ed, path);
+ editor_request_render(ed);
+ break;
+ }
}
}
break;
@@ -1089,30 +1124,30 @@ static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev)
} else if (ev->key.key == YASL_KEY_BACKSPACE && ed->prompt_len > 0) {
ed->prompt_buf[--ed->prompt_len] = '\0';
}
- yasl_surface_request_frame(ed->surface);
+ editor_request_render(ed);
break;
}
/* Function Keys */
if (ev->key.key == YASL_KEY_F3) {
editor_find_next(ed, ed->search_query, !shift);
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
} else if (ev->key.key == YASL_KEY_F5) {
insert_date_time(ed);
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
}
/* Shortcuts */
if (ctrl) {
switch (ev->key.key) {
case YASL_KEY_Q:
- return YASL_APP_QUIT;
+ ed->running = false; return;
case YASL_KEY_N:
editor_new_file(ed);
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_S:
if (shift || !ed->has_file) {
ed->prompt = PROMPT_SAVE;
@@ -1121,89 +1156,89 @@ static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev)
} else {
editor_save_file(ed, ed->file_path);
}
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_O:
ed->prompt = PROMPT_OPEN;
ed->prompt_len = 0;
ed->prompt_buf[0] = '\0';
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_F:
ed->prompt = PROMPT_FIND;
ed->prompt_len = 0;
ed->prompt_buf[0] = '\0';
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_H:
ed->prompt = PROMPT_REPLACE_FIND;
ed->prompt_len = 0;
ed->prompt_buf[0] = '\0';
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_G:
ed->prompt = PROMPT_GOTO;
ed->prompt_len = 0;
ed->prompt_buf[0] = '\0';
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_W:
ed->word_wrap = !ed->word_wrap;
editor_set_status(ed, ed->word_wrap ? "Word Wrap: ON" : "Word Wrap: OFF");
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_EQUAL: /* Zoom In */
if (ed->zoom_offset < 2) ed->zoom_offset++;
editor_set_status(ed, "Zoom In");
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_MINUS: /* Zoom Out */
if (ed->zoom_offset > -1) ed->zoom_offset--;
editor_set_status(ed, "Zoom Out");
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_0: /* Zoom Reset */
ed->zoom_offset = 0;
editor_set_status(ed, "Zoom Reset");
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_A:
ed->sel_anchor_row = 0;
ed->sel_anchor_col = 0;
ed->cursor_row = ed->line_count - 1;
ed->cursor_col = (int)strlen(ed->lines[ed->line_count - 1]);
ed->has_selection = true;
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_C:
editor_copy(ed);
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_X:
editor_cut(ed);
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_V:
if (!yasl_clipboard_request(ed->surface, YASL_CLIPBOARD))
editor_set_status(ed, "Clipboard paste failed");
- return YASL_APP_CONTINUE;
+ return;
case YASL_KEY_Z:
if (shift) editor_redo(ed);
else editor_undo(ed);
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_Y:
editor_redo(ed);
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_BACKSPACE:
handle_backspace(ed, true);
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
case YASL_KEY_DELETE:
handle_delete(ed, true);
- yasl_surface_request_frame(ed->surface);
- return YASL_APP_CONTINUE;
+ editor_request_render(ed);
+ return;
default:
break;
}
@@ -1212,7 +1247,7 @@ static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev)
/* Tab key */
if (ev->key.key == YASL_KEY_TAB) {
handle_tab(ed, shift);
- yasl_surface_request_frame(ed->surface);
+ editor_request_render(ed);
break;
}
@@ -1301,38 +1336,40 @@ static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev)
break;
}
ed->caret_visible = true;
- yasl_surface_request_frame(ed->surface);
+ editor_request_render(ed);
}
break;
- case YASL_EVENT_POINTER_DOWN: {
- if (ev->pointer.button == YASL_BUTTON_PRIMARY) {
- struct yasl_geometry geom = yasl_surface_geometry(ed->surface);
- int scale = (geom.scale_120 >= 240 ? 2 : 1) + ed->zoom_offset;
+ case YASL_EVENT_POINTER: {
+ double device_scale = yasl_surface_get_scale(ed->surface);
+ int px = (int)lround(ev->pointer.x * device_scale);
+ int py = (int)lround(ev->pointer.y * device_scale);
+ if (ev->pointer.action == YASL_POINTER_DOWN && ev->pointer.button == YASL_BUTTON_PRIMARY) {
+ int scale = (device_scale >= 2.0 ? 2 : 1) + ed->zoom_offset;
if (scale < 1) scale = 1;
int char_w = 8 * scale;
int line_h = 16 * scale;
int gutter_w = 48 * scale;
int header_h = 24 * scale;
- int row = (ev->pointer.pos.y - header_h) / line_h + ed->scroll_y;
- int col = (ev->pointer.pos.x - gutter_w) / char_w + (ed->word_wrap ? 0 : ed->scroll_x);
+ int row = (py - header_h) / line_h + ed->scroll_y;
+ int col = (px - gutter_w) / char_w + (ed->word_wrap ? 0 : ed->scroll_x);
if (row < 0) row = 0;
if (row >= ed->line_count) row = ed->line_count - 1;
int len = (int)strlen(ed->lines[row]);
if (col < 0) col = 0;
if (col > len) col = len;
- uint64_t now = yasl_loop_now_ns();
+ uint64_t now = yasl_time_ns();
if (now - ed->last_click_time < 350000000ull &&
- abs(ev->pointer.pos.x - ed->last_click_pos.x) < 8 &&
- abs(ev->pointer.pos.y - ed->last_click_pos.y) < 8) {
+ abs(px - (int)ed->last_click_x) < 8 && abs(py - (int)ed->last_click_y) < 8) {
ed->click_count++;
} else {
ed->click_count = 1;
}
ed->last_click_time = now;
- ed->last_click_pos = ev->pointer.pos;
+ ed->last_click_x = px;
+ ed->last_click_y = py;
if (ed->click_count == 2) {
/* Double click: select word */
@@ -1357,29 +1394,21 @@ static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev)
ed->mouse_dragging = true;
}
ed->caret_visible = true;
- yasl_surface_request_frame(ed->surface);
- }
- break;
- }
-
- case YASL_EVENT_POINTER_UP:
- if (ev->pointer.button == YASL_BUTTON_PRIMARY) {
+ editor_request_render(ed);
+ } else if (ev->pointer.action == YASL_POINTER_UP && ev->pointer.button == YASL_BUTTON_PRIMARY) {
ed->mouse_dragging = false;
- }
- break;
-
- case YASL_EVENT_POINTER_MOTION:
- if (ed->mouse_dragging) {
- struct yasl_geometry geom = yasl_surface_geometry(ed->surface);
- int scale = (geom.scale_120 >= 240 ? 2 : 1) + ed->zoom_offset;
+ } else if (ev->pointer.action == YASL_POINTER_LEAVE) {
+ ed->mouse_dragging = false;
+ } else if (ev->pointer.action == YASL_POINTER_MOVE && ed->mouse_dragging) {
+ int scale = (device_scale >= 2.0 ? 2 : 1) + ed->zoom_offset;
if (scale < 1) scale = 1;
int char_w = 8 * scale;
int line_h = 16 * scale;
int gutter_w = 48 * scale;
int header_h = 24 * scale;
- int row = (ev->pointer.pos.y - header_h) / line_h + ed->scroll_y;
- int col = (ev->pointer.pos.x - gutter_w) / char_w + (ed->word_wrap ? 0 : ed->scroll_x);
+ int row = (py - header_h) / line_h + ed->scroll_y;
+ int col = (px - gutter_w) / char_w + (ed->word_wrap ? 0 : ed->scroll_x);
if (row < 0) row = 0;
if (row >= ed->line_count) row = ed->line_count - 1;
int len = (int)strlen(ed->lines[row]);
@@ -1389,56 +1418,81 @@ static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev)
ed->cursor_row = row;
ed->cursor_col = col;
ed->has_selection = (ed->cursor_row != ed->sel_anchor_row || ed->cursor_col != ed->sel_anchor_col);
- yasl_surface_request_frame(ed->surface);
+ editor_request_render(ed);
+ }
+ break;
+ }
+
+ case YASL_EVENT_PEN: {
+ yasl_event pointer = {0};
+ pointer.type = YASL_EVENT_POINTER;
+ pointer.surface = ev->surface;
+ pointer.pointer.x = ev->pen.x;
+ pointer.pointer.y = ev->pen.y;
+ pointer.pointer.button = YASL_BUTTON_PRIMARY;
+ pointer.pointer.mods = 0;
+ switch (ev->pen.action) {
+ case YASL_PEN_DOWN: pointer.pointer.action = YASL_POINTER_DOWN; break;
+ case YASL_PEN_UP: pointer.pointer.action = YASL_POINTER_UP; break;
+ case YASL_PEN_LEAVE:
+ case YASL_PEN_CANCEL: pointer.pointer.action = YASL_POINTER_LEAVE; break;
+ default: pointer.pointer.action = YASL_POINTER_MOVE; break;
}
+ on_event(ed, &pointer);
break;
+ }
- case YASL_EVENT_POINTER_SCROLL:
- if (ev->pointer.mods & (YASL_MOD_CTRL | YASL_MOD_SUPER)) {
+ case YASL_EVENT_SCROLL:
+ if (ev->scroll.mods & (YASL_MOD_CTRL | YASL_MOD_SUPER)) {
/* Ctrl + Wheel = Zoom */
- if (ev->pointer.scroll.y > 0 && ed->zoom_offset < 2) ed->zoom_offset++;
- else if (ev->pointer.scroll.y < 0 && ed->zoom_offset > -1) ed->zoom_offset--;
- } else if (ev->pointer.mods & YASL_MOD_SHIFT) {
+ if (ev->scroll.lines_y > 0 && ed->zoom_offset < 2) ed->zoom_offset++;
+ else if (ev->scroll.lines_y < 0 && ed->zoom_offset > -1) ed->zoom_offset--;
+ } else if (ev->scroll.mods & YASL_MOD_SHIFT) {
/* Shift + Wheel = Horizontal scroll */
- ed->scroll_x -= (int)(ev->pointer.scroll.y * 3.0);
+ ed->scroll_x -= (int)(ev->scroll.lines_y * 3.0);
if (ed->scroll_x < 0) ed->scroll_x = 0;
} else {
/* Normal vertical scroll */
- ed->scroll_y -= (int)(ev->pointer.scroll.y * 3.0);
+ ed->scroll_y -= (int)(ev->scroll.lines_y * 3.0);
if (ed->scroll_y < 0) ed->scroll_y = 0;
if (ed->scroll_y >= ed->line_count) ed->scroll_y = ed->line_count - 1;
}
- yasl_surface_request_frame(ed->surface);
+ editor_request_render(ed);
break;
- case YASL_EVENT_USER:
- if (ev->user.code == MSG_STATS_READY && ev->user.payload) {
- struct doc_stats *s = (struct doc_stats *)ev->user.payload;
- ed->stats = *s;
- yasl_surface_request_frame(ed->surface);
- }
- break;
+ case YASL_EVENT_CLOSE:
+ ed->running = false; return;
+ case YASL_EVENT_QUIT:
+ ed->running = false; return;
+ case YASL_EVENT_DISPLAY_LOST:
+ ed->failed = true;
+ ed->running = false;
+ return;
- case YASL_EVENT_WINDOW_CLOSE:
- return YASL_APP_QUIT;
+ case YASL_EVENT_WINDOW:
+ ed->surface_scale = ev->window.scale;
+ if (ev->window.state & YASL_STATE_ACTIVATED) ed->caret_visible = true;
+ editor_request_render(ed);
+ break;
+ case YASL_EVENT_REFRESH:
+ editor_request_render(ed);
+ break;
default:
break;
}
- return YASL_APP_CONTINUE;
+ return;
}
-static void on_frame(void *ud, struct yasl_surface *s, struct yasl_frame *ctx)
+static void editor_render(struct editor *ed)
{
- (void)s;
- struct editor *ed = ud;
- int w = ctx->target.size.x;
- int h = ctx->target.size.y;
- uint32_t *pix = ctx->target.pixels;
- if (!pix || w <= 0 || h <= 0) return;
-
- int scale = (ctx->scale_120 >= 240 ? 2 : 1) + ed->zoom_offset;
+ int w, h;
+ uint32_t *pix = yasl_cpu_pixels(ed->surface, &w, &h);
+ if (!pix) return;
+
+ double display_scale = yasl_surface_get_scale(ed->surface);
+ int scale = (display_scale >= 2.0 ? 2 : 1) + ed->zoom_offset;
if (scale < 1) scale = 1;
int char_w = 8 * scale;
int line_h = 16 * scale;
@@ -1554,11 +1608,14 @@ static void on_frame(void *ud, struct yasl_surface *s, struct yasl_frame *ctx)
fill_rect(pix, w, h, 0, status_y, w, status_h, bg_status);
char status_left[160];
- if (ed->status_msg[0] && (yasl_loop_now_ns() - ed->status_msg_time < 3000000000ull)) {
+ if (ed->status_msg[0] && (yasl_time_ns() - ed->status_msg_time < 3000000000ull)) {
snprintf(status_left, sizeof(status_left), "%s", ed->status_msg);
} else {
+ pthread_mutex_lock(&ed->worker_mutex);
+ struct doc_stats stats = ed->stats;
+ pthread_mutex_unlock(&ed->worker_mutex);
snprintf(status_left, sizeof(status_left), "Ln %d, Col %d Lines: %d Words: %d Chars: %d",
- ed->cursor_row + 1, ed->cursor_col + 1, ed->line_count, ed->stats.words, ed->stats.chars);
+ ed->cursor_row + 1, ed->cursor_col + 1, ed->line_count, stats.words, stats.chars);
}
draw_string(pix, w, h, 12 * scale, status_y + 6 * scale, status_left, text_status, scale);
@@ -1571,14 +1628,17 @@ static void on_frame(void *ud, struct yasl_surface *s, struct yasl_frame *ctx)
ed->is_modified ? 0xfffab387 : status_accent, scale);
}
- if (!yasl_frame_present(ctx, NULL, 0))
- yasl_app_exit(yasl_surface_display(s), YASL_APP_ERROR);
+ if (!yasl_cpu_present(ed->surface)) {
+ fprintf(stderr, "Could not present CPU image\n");
+ ed->failed = true;
+ ed->running = false;
+ return;
+ }
+ ed->dirty = false;
}
-static void on_quit(void *ud, struct yasl_display *d)
+static void editor_shutdown(struct editor *ed)
{
- (void)d;
- struct editor *ed = ud;
ed->worker_running = false;
if (ed->worker_started) {
pthread_mutex_lock(&ed->worker_mutex);
@@ -1586,6 +1646,7 @@ static void on_quit(void *ud, struct yasl_display *d)
pthread_mutex_unlock(&ed->worker_mutex);
pthread_join(ed->worker_tid, NULL);
}
+ free(ed->worker_lines);
if (ed->worker_cond_ready) pthread_cond_destroy(&ed->worker_cond);
if (ed->worker_mutex_ready) pthread_mutex_destroy(&ed->worker_mutex);
}
@@ -1593,15 +1654,19 @@ static void on_quit(void *ud, struct yasl_display *d)
int main(int argc, char **argv)
{
static struct editor ed;
+ bool smoke_test = false;
- /* Check command-line file argument */
- if (argc > 1) {
- snprintf(ed.file_path, sizeof(ed.file_path), "%s", argv[1]);
- ed.has_file = true;
- } else {
+ for (int i = 1; i < argc; i++) {
+ if (!strcmp(argv[i], "--smoke-test")) smoke_test = true;
+ else if (!ed.has_file) {
+ snprintf(ed.file_path, sizeof(ed.file_path), "%s", argv[i]);
+ ed.has_file = true;
+ }
+ }
+ if (!ed.has_file) {
const char *initial_content[] = {
"// Welcome to YASL Notepad!",
- "// A full-featured desktop text editor built on YASL Approach A.",
+ "// A full-featured desktop text editor built with YASL.",
"",
"#include <stdio.h>",
"",
@@ -1636,15 +1701,68 @@ int main(int argc, char **argv)
}
}
- struct yasl_app_desc desc = {
- .app_id = "org.yasl.notepad",
- .backend = YASL_BACKEND_AUTO,
- .userdata = &ed,
- .init = on_init,
- .event = on_event,
- .frame = on_frame,
- .quit = on_quit,
- };
-
- return yasl_run(&desc);
+ if (!yasl_set_app_id("org.yasl.notepad") || !editor_init(&ed)) {
+ fprintf(stderr, "Could not initialize YASL Notepad\n");
+ editor_shutdown(&ed);
+ yasl_shutdown();
+ return 1;
+ }
+
+ editor_update_ime(&ed);
+ editor_render(&ed);
+ if (smoke_test) {
+ editor_shutdown(&ed);
+ yasl_surface_destroy(ed.surface);
+ yasl_shutdown();
+ return ed.failed ? 1 : 0;
+ }
+
+ uint64_t next_blink = yasl_time_ns() + 500000000ull;
+ uint64_t seen_stats = 0;
+ int exit_status = 0;
+ while (ed.running) {
+ uint64_t now = yasl_time_ns();
+ int timeout_ms = next_blink > now ? (int)((next_blink - now + 999999ull) / 1000000ull) : 0;
+ yasl_event ev;
+ int result = yasl_wait_event(&ev, timeout_ms);
+ if (result > 0) {
+ on_event(&ed, &ev);
+ } else if (result < 0) {
+ fprintf(stderr, "YASL event wait failed\n");
+ exit_status = 1;
+ break;
+ }
+ int queued = 0;
+ while (ed.running && (queued = yasl_wait_event(&ev, 0)) > 0)
+ on_event(&ed, &ev);
+ if (queued < 0) {
+ fprintf(stderr, "YASL event poll failed\n");
+ exit_status = 1;
+ break;
+ }
+
+ now = yasl_time_ns();
+ if (now >= next_blink) {
+ ed.caret_visible = !ed.caret_visible;
+ ed.dirty = true;
+ next_blink = now + 500000000ull;
+ }
+ pthread_mutex_lock(&ed.worker_mutex);
+ uint64_t stats_generation = ed.stats_generation;
+ pthread_mutex_unlock(&ed.worker_mutex);
+ if (stats_generation != seen_stats) {
+ seen_stats = stats_generation;
+ ed.dirty = true;
+ }
+ editor_update_ime(&ed);
+ if (ed.dirty) {
+ editor_render(&ed);
+ editor_update_ime(&ed);
+ }
+ }
+
+ editor_shutdown(&ed);
+ yasl_surface_destroy(ed.surface);
+ yasl_shutdown();
+ return exit_status || ed.failed ? 1 : 0;
}
diff --git a/examples/simple_window.c b/examples/simple_window.c
index 5a8b314..114bfbf 100644
--- a/examples/simple_window.c
+++ b/examples/simple_window.c
@@ -1,198 +1,50 @@
-#include <yasl/app.h>
-#include <stdio.h>
+/* Mouse and pen input on a CPU surface. */
+#include <yasl/yasl.h>
+#include <yasl/cpu.h>
#include <string.h>
-static bool failed, smoke_test;
-static int mouse_x = -1;
-static int mouse_y = -1;
-static bool mouse_down = false;
-static int color_theme = 0;
-static uint16_t pen_pressure = 0;
-static int8_t pen_tilt_x = 0;
-static int8_t pen_tilt_y = 0;
-static enum yasl_pointer_device pointer_device = YASL_PTR_DEV_MOUSE;
-
-static const char *device_name(enum yasl_pointer_device dev)
-{
- switch (dev) {
- case YASL_PTR_DEV_PEN: return "Pen/Stylus";
- case YASL_PTR_DEV_ERASER: return "Eraser";
- case YASL_PTR_DEV_TOUCH: return "Touch";
- default: return "Mouse";
- }
-}
-
-static void on_draw(void *ud, struct yasl_surface *s, struct yasl_frame *frame)
+int main(int argc, char **argv)
{
- (void)ud;
- int w = frame->target.size.x;
- int h = frame->target.size.y;
- uint32_t *pixels = frame->target.pixels;
- if (!pixels || w <= 0 || h <= 0)
- return;
-
- for (int y = 0; y < h; y++) {
- for (int x = 0; x < w; x++) {
- uint8_t r, g, b;
- uint8_t u = (uint8_t)((x * 255) / (w > 0 ? w : 1));
- uint8_t v = (uint8_t)((y * 255) / (h > 0 ? h : 1));
-
- switch (color_theme % 3) {
- case 0:
- r = u;
- g = v;
- b = 160;
- break;
- case 1:
- r = 40;
- g = u;
- b = v;
- break;
- default:
- r = v;
- g = 60;
- b = u;
- break;
- }
-
- /* Draw interactive pointer / stylus indicator */
- if (mouse_x >= 0 && mouse_y >= 0) {
- int dx = x - mouse_x;
- int dy = y - mouse_y;
- int dist_sq = dx * dx + dy * dy;
-
- /* Pressure-sensitive radius: scales dynamically from 8px to 64px */
- int radius;
- if (pen_pressure > 0) {
- radius = 8 + (int)(((uint32_t)pen_pressure * 56) / 65535);
- } else {
- radius = mouse_down ? 36 : 18;
- }
-
- if (dist_sq < radius * radius) {
- r = (uint8_t)(255 - r / 2);
- g = (uint8_t)(255 - g / 2);
- b = (uint8_t)(255 - b / 2);
+ bool smoke = argc == 2 && !strcmp(argv[1], "--smoke-test");
+ yasl_surface *s = yasl_surface_create("YASL input", 640, 480, YASL_SURFACE_CPU);
+ if (!s) return 1;
+ double px = -100, py = -100, pressure = 0;
+ bool dirty = false;
+ int frames = 0, result = 1;
+ yasl_event e;
+ for (;;) {
+ int n = yasl_wait_event(&e, smoke ? 20 : -1);
+ if (n < 0 || e.type == YASL_EVENT_DISPLAY_LOST) break;
+ if (e.type == YASL_EVENT_CLOSE || e.type == YASL_EVENT_QUIT ||
+ (e.type == YASL_EVENT_KEY && e.key.key == YASL_KEY_ESCAPE)) { result = 0; break; }
+ if (e.type == YASL_EVENT_POINTER) {
+ px = e.pointer.x; py = e.pointer.y;
+ pressure = (e.pointer.buttons & YASL_BUTTON_BIT(YASL_BUTTON_PRIMARY)) ? 0.5 : 0;
+ dirty = true;
+ }
+ if (e.type == YASL_EVENT_PEN) {
+ px = e.pen.x; py = e.pen.y;
+ pressure = (e.pen.axes_valid & YASL_PEN_PRESSURE) ? e.pen.pressure : 0;
+ dirty = true;
+ }
+ if (e.type == YASL_EVENT_REFRESH) dirty = true;
+ if (dirty) {
+ int w, h;
+ uint32_t *pixels = yasl_cpu_pixels(s, &w, &h);
+ if (!pixels) break;
+ double scale = yasl_surface_get_scale(s), radius = (8 + pressure * 48) * scale;
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ double dx = x - px * scale, dy = y - py * scale;
+ pixels[(size_t)y * w + x] = dx * dx + dy * dy < radius * radius ? 0xffffc060u : 0xff203040u;
}
}
-
- pixels[y * w + x] = (255u << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
- }
- }
-
- if (!yasl_frame_present(frame, NULL, 0)) {
- fprintf(stderr, "Failed to present frame\n");
- failed = true;
- }
- if (failed) yasl_app_exit(yasl_surface_display(s), YASL_APP_ERROR);
- else if (smoke_test) yasl_surface_destroy(s);
-}
-
-static enum yasl_app_result on_init(void *ud, struct yasl_display *display)
-{
- (void)ud;
- struct yasl_window_spec window_spec = {
- .title = "YASL Interactive Window (Pen & Touch Enabled)",
- .size_logical = { .x = 640, .y = 480 },
- };
- struct yasl_surface *s = yasl_surface_create_toplevel(display, &window_spec);
- if (!s) {
- fprintf(stderr, "Failed to create surface\n");
- failed = true;
- return YASL_APP_ERROR;
- }
-
- yasl_surface_request_frame(s);
-
- printf("=== YASL Demo Running ===\n");
- printf(" - Stylus/Pen: full pressure (0..65535) and tilt detection\n");
- printf(" - Mouse/Touch: interactive click/motion highlights\n");
- printf(" - Space: cycle background themes\n");
- printf(" - Escape: exit\n\n");
-
- return YASL_APP_CONTINUE;
-}
-
-static void pointer_sample(struct yasl_vector2i pos, enum yasl_pointer_device device,
- struct yasl_pointer_pen pen)
-{
- mouse_x = pos.x;
- mouse_y = pos.y;
- pointer_device = device;
- pen_pressure = pen.pressure;
- pen_tilt_x = pen.tilt_x;
- pen_tilt_y = pen.tilt_y;
-}
-
-static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev)
-{
- (void)ud;
- switch (ev->type) {
- case YASL_EVENT_WINDOW_CLOSE:
- return YASL_APP_QUIT;
- case YASL_EVENT_WINDOW_RESIZE: {
- uint32_t flags = yasl_surface_state(ev->surface);
- const char *mode = flags & YASL_STATE_FULLSCREEN ? "fullscreen" :
- flags & YASL_STATE_MAXIMIZED ? "maximized" :
- flags & YASL_STATE_TILED ? "tiled" : "windowed";
- printf("[YASL] Resized to %dx%d (scale: %.2fx, %s)\n", ev->resize.geometry.size.x,
- ev->resize.geometry.size.y, (double)ev->resize.geometry.scale_120 / YASL_SCALE_UNIT, mode);
- break;
- }
- case YASL_EVENT_KEY:
- if (ev->key.action != YASL_KEY_PRESS) return YASL_APP_CONTINUE;
- if (ev->key.key == YASL_KEY_ESCAPE) return YASL_APP_QUIT;
- if (ev->key.key == YASL_KEY_SPACE || ev->key.key == YASL_KEY_ENTER)
- color_theme++;
- break;
- case YASL_EVENT_POINTER_DOWN:
- case YASL_EVENT_POINTER_UP:
- case YASL_EVENT_POINTER_MOTION:
- pointer_sample(ev->pointer.pos, ev->pointer.device, ev->pointer.pen);
- if (ev->type == YASL_EVENT_POINTER_DOWN && ev->pointer.button == YASL_BUTTON_PRIMARY) {
- mouse_down = true;
- } else if (ev->type == YASL_EVENT_POINTER_UP && ev->pointer.button == YASL_BUTTON_PRIMARY) {
- mouse_down = false;
- pen_pressure = 0;
+ if (!yasl_cpu_present(s)) break;
+ dirty = false;
+ frames++;
}
- break;
- case YASL_EVENT_FOCUS:
- if (ev->focus.focused) return YASL_APP_CONTINUE;
- /* Fall through to cancel held input on focus loss. */
- [[fallthrough]];
- case YASL_EVENT_INPUT_RESET:
- case YASL_EVENT_POINTER_LEAVE:
- mouse_x = mouse_y = -1;
- mouse_down = false;
- pen_pressure = 0;
- break;
- default:
- return YASL_APP_CONTINUE;
- }
- if (pen_pressure > 0) {
- printf("[Pen] Pressure: %5u/65535 (%.1f%%) | Tilt: (%+3d°, %+3d°) | Device: %s\n",
- pen_pressure, pen_pressure * 100.0 / 65535.0,
- pen_tilt_x, pen_tilt_y, device_name(pointer_device));
- }
- yasl_surface_request_frame(ev->surface);
- return YASL_APP_CONTINUE;
-}
-
-int main(int argc, char **argv)
-{
- if (argc == 2 && strcmp(argv[1], "--smoke-test") == 0)
- smoke_test = true;
- else if (argc != 1) {
- fprintf(stderr, "Usage: %s [--smoke-test]\n", argv[0]);
- return 1;
+ if (smoke && frames && n == 0) { result = 0; break; }
}
- struct yasl_app_desc desc = {
- .app_id = "org.yasl.example",
- .init = on_init,
- .event = on_event,
- .frame = on_draw,
- };
- int result = yasl_run(&desc);
- if (result) perror("yasl_run");
- return result || failed ? 1 : 0;
+ yasl_shutdown();
+ return result;
}
diff --git a/examples/tablet.c b/examples/tablet.c
index ba1a94f..dad4b30 100644
--- a/examples/tablet.c
+++ b/examples/tablet.c
@@ -1,346 +1,109 @@
-/* Pressure-sensitive drawing using only YASL's portable API. */
-#include <yasl/app.h>
-
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
+/* Pen canvas: pressure, eraser and coherent strokes drawn straight into the CPU surface. */
+#include <yasl/yasl.h>
+#include <yasl/cpu.h>
#include <string.h>
+#include <math.h>
-#define PAPER 0xfff5f2eau
-#define INK 0xff315fadu
-#define PANEL 0xff172338u
-#define TEXT 0xffe5edf8u
-#define ACCENT 0xff59d5b6u
-#define HEADER 104
-
-struct app {
- bool smoke_test, failed, tablet_supported;
- bool down, visible, previous;
- struct yasl_surface *surface;
- enum yasl_backend backend;
- struct {
- struct yasl_vector2i pos;
- enum yasl_pointer_device device;
- struct yasl_pointer_pen pen;
- } pointer;
- double last_x, last_y, last_radius;
- uint32_t *canvas;
- struct yasl_geometry geometry;
-};
-
-static const char *backend_name(enum yasl_backend backend)
-{
- switch (backend) {
- case YASL_BACKEND_WAYLAND: return "WAYLAND";
- case YASL_BACKEND_X11: return "X11";
- case YASL_BACKEND_COCOA: return "COCOA";
- default: return "AUTO";
- }
-}
-
-static const char *device_name(enum yasl_pointer_device device)
-{
- switch (device) {
- case YASL_PTR_DEV_PEN: return "PEN";
- case YASL_PTR_DEV_ERASER: return "ERASER";
- case YASL_PTR_DEV_TOUCH: return "TOUCH";
- default: return "MOUSE";
- }
-}
-
-static void cancel_stroke(struct app *app)
-{
- app->down = app->previous = app->visible = false;
- app->pointer.pen = (struct yasl_pointer_pen){0};
-}
-
-static bool resize_canvas(struct app *app, struct yasl_geometry geometry)
-{
- int w = geometry.size.x, h = geometry.size.y;
- if (w <= 0 || h <= 0 || !geometry.scale_120 ||
- (size_t)w > SIZE_MAX / sizeof(uint32_t) / (size_t)h) return false;
- if (app->canvas && w == app->geometry.size.x && h == app->geometry.size.y &&
- geometry.scale_120 == app->geometry.scale_120) return true;
- uint32_t *pixels = malloc((size_t)w * h * sizeof(*pixels));
- if (!pixels) return false;
- double ratio = (double)app->geometry.scale_120 / geometry.scale_120;
- for (int y = 0; y < h; y++) {
- int old_y = (int)(y * ratio);
- for (int x = 0; x < w; x++) {
- int old_x = (int)(x * ratio);
- pixels[(size_t)y * w + x] = app->canvas &&
- old_x < app->geometry.size.x && old_y < app->geometry.size.y ?
- app->canvas[(size_t)old_y * app->geometry.size.x + old_x] : PAPER;
- }
- }
- free(app->canvas);
- app->canvas = pixels;
- app->geometry = geometry;
- cancel_stroke(app);
- return true;
-}
-
-static void clear_canvas(struct app *app)
-{
- for (size_t i = 0, n = (size_t)app->geometry.size.x * app->geometry.size.y; i < n; i++)
- app->canvas[i] = PAPER;
- app->previous = false;
-}
-
-static double brush_radius(const struct app *app)
-{
- return ((double)app->geometry.scale_120 / YASL_SCALE_UNIT) *
- ((app->pointer.device == YASL_PTR_DEV_PEN ||
- app->pointer.device == YASL_PTR_DEV_ERASER) ?
- 2 + 24 * ((double)app->pointer.pen.pressure / 65535) : 6);
-}
-
-static void stamp(struct app *app, double cx, double cy, double radius)
-{
- int w = app->geometry.size.x, h = app->geometry.size.y;
- int top = yasl_scale_to_device(HEADER, app->geometry.scale_120);
- int x0 = (int)fmax(0, fmin(w, floor(cx - radius)));
- int y0 = (int)fmax(top, fmin(h, floor(cy - radius)));
- int x1 = (int)fmax(0, fmin(w, ceil(cx + radius)));
- int y1 = (int)fmax(0, fmin(h, ceil(cy + radius)));
- uint32_t color = app->pointer.device == YASL_PTR_DEV_ERASER ? PAPER : INK;
- for (int y = y0; y < y1; y++)
- for (int x = x0; x < x1; x++) {
- double dx = x + 0.5 - cx, dy = y + 0.5 - cy;
- if (dx * dx + dy * dy <= radius * radius)
- app->canvas[(size_t)y * w + x] = color;
- }
-}
-
-static void stroke(struct app *app)
-{
- double x = app->pointer.pos.x, y = app->pointer.pos.y;
- double radius = brush_radius(app);
- /* Leaving the canvas breaks the line so re-entry cannot bridge the UI. */
- if (x < 0 || x >= app->geometry.size.x ||
- y < HEADER * ((double)app->geometry.scale_120 / YASL_SCALE_UNIT) ||
- y >= app->geometry.size.y) {
- app->previous = false;
- return;
- }
- if (app->previous) {
- double dx = x - app->last_x, dy = y - app->last_y;
- int steps = (int)ceil(hypot(dx, dy));
- for (int i = 1; i <= steps; i++) {
- double t = (double)i / steps;
- stamp(app, app->last_x + dx * t, app->last_y + dy * t,
- app->last_radius + (radius - app->last_radius) * t);
- }
- }
- /* Also stamp pressure-only motion, even when position has not changed. */
- stamp(app, x, y, radius);
- app->last_x = x;
- app->last_y = y;
- app->last_radius = radius;
- app->previous = true;
-}
-
-static enum yasl_app_result input(void *ud, const struct yasl_event *event)
-{
- struct app *app = ud;
- if (event->type == YASL_EVENT_WINDOW_CLOSE)
- return YASL_APP_QUIT;
- if (event->type == YASL_EVENT_WINDOW_RESIZE) {
- if (!resize_canvas(app, event->resize.geometry)) {
- app->failed = true;
- return YASL_APP_ERROR;
- }
- } else if (event->type == YASL_EVENT_INPUT_RESET || event->type == YASL_EVENT_POINTER_LEAVE ||
- (event->type == YASL_EVENT_FOCUS && !event->focus.focused)) {
- cancel_stroke(app);
- } else if (event->type == YASL_EVENT_KEY && event->key.action == YASL_KEY_PRESS) {
- if (event->key.key == YASL_KEY_ESCAPE) return YASL_APP_QUIT;
- if (event->key.key == YASL_KEY_C || event->key.key == YASL_KEY_SPACE)
- clear_canvas(app);
- } else if (event->type == YASL_EVENT_POINTER_DOWN || event->type == YASL_EVENT_POINTER_UP ||
- event->type == YASL_EVENT_POINTER_MOTION) {
- if (app->pointer.device != event->pointer.device) cancel_stroke(app);
- app->pointer.pos = event->pointer.pos;
- app->pointer.device = event->pointer.device;
- app->pointer.pen = event->pointer.pen;
- app->visible = true;
- if (event->type == YASL_EVENT_POINTER_DOWN && event->pointer.button == YASL_BUTTON_PRIMARY) {
- app->down = true;
- app->previous = false;
- stroke(app);
- } else if (event->type == YASL_EVENT_POINTER_UP && event->pointer.button == YASL_BUTTON_PRIMARY) {
- app->down = app->previous = false;
- app->pointer.pen.pressure = 0;
- } else if (event->type == YASL_EVENT_POINTER_MOTION) {
- if (app->down) stroke(app);
- }
- } else {
- return YASL_APP_CONTINUE;
- }
- yasl_surface_request_frame(event->surface);
- return YASL_APP_CONTINUE;
-}
-
-/* Tiny built-in 5x7 font keeps this example independent of a text library. */
-static const uint8_t glyphs[128][7] = {
- ['A']={14,17,17,31,17,17,17}, ['B']={30,17,17,30,17,17,30},
- ['C']={14,17,16,16,16,17,14}, ['D']={30,17,17,17,17,17,30},
- ['E']={31,16,16,30,16,16,31}, ['F']={31,16,16,30,16,16,16},
- ['G']={14,17,16,23,17,17,14}, ['H']={17,17,17,31,17,17,17},
- ['I']={14,4,4,4,4,4,14}, ['J']={7,2,2,2,2,18,12},
- ['K']={17,18,20,24,20,18,17}, ['L']={16,16,16,16,16,16,31},
- ['M']={17,27,21,21,17,17,17}, ['N']={17,25,21,19,17,17,17},
- ['O']={14,17,17,17,17,17,14}, ['P']={30,17,17,30,16,16,16},
- ['Q']={14,17,17,17,21,18,13}, ['R']={30,17,17,30,20,18,17},
- ['S']={15,16,16,14,1,1,30}, ['T']={31,4,4,4,4,4,4},
- ['U']={17,17,17,17,17,17,14}, ['V']={17,17,17,17,17,10,4},
- ['W']={17,17,17,21,21,21,10}, ['X']={17,17,10,4,10,17,17},
- ['Y']={17,17,10,4,4,4,4}, ['Z']={31,1,2,4,8,16,31},
- ['0']={14,17,19,21,25,17,14}, ['1']={4,12,4,4,4,4,14},
- ['2']={14,17,1,2,4,8,31}, ['3']={30,1,1,14,1,1,30},
- ['4']={2,6,10,18,31,2,2}, ['5']={31,16,16,30,1,1,30},
- ['6']={14,16,16,30,17,17,14}, ['7']={31,1,2,4,8,8,8},
- ['8']={14,17,17,14,17,17,14}, ['9']={14,17,17,15,1,1,14},
- ['-']={0,0,0,31,0,0,0}, ['+']={0,4,4,31,4,4,0},
- ['/']={1,1,2,4,8,16,16}, [':']={0,4,4,0,4,4,0},
- ['.']={0,0,0,0,0,4,4}, ['%']={17,2,4,4,8,16,17},
-};
-
-static void rect(struct yasl_framebuffer *fb, int x, int y, int w, int h, uint32_t color)
-{
- int right = x + w < fb->size.x ? x + w : fb->size.x;
- int bottom = y + h < fb->size.y ? y + h : fb->size.y;
- for (int j = y > 0 ? y : 0; j < bottom; j++)
- for (int i = x > 0 ? x : 0; i < right; i++)
- fb->pixels[(size_t)j * fb->size.x + i] = color;
-}
-
-static void text(struct yasl_framebuffer *fb, double dpi, int x, int y,
- const char *str, uint32_t color)
-{
- for (; *str; str++, x += 12) {
- unsigned ch = (unsigned char)*str;
- if (ch >= 128) continue;
- for (int row = 0; row < 7; row++)
- for (int col = 0; col < 5; col++) {
- if (!(glyphs[ch][row] & (1 << (4 - col)))) continue;
- int left = (int)lround((x + col * 2) * dpi);
- int top = (int)lround((y + row * 2) * dpi);
- rect(fb, left, top,
- (int)lround((x + col * 2 + 2) * dpi) - left,
- (int)lround((y + row * 2 + 2) * dpi) - top, color);
- }
- }
-}
+#define PAPER 0xfff0eee8u
+#define INK 0xff202830u
-static void draw(void *ud, struct yasl_surface *surface, struct yasl_frame *frame)
+static void stamp(uint32_t *pixels, int w, int h, double x, double y, double radius, bool erase)
{
- struct app *app = ud;
- struct yasl_geometry geometry = {frame->target.size, frame->scale_120};
- if (!resize_canvas(app, geometry)) {
- fprintf(stderr, "Cannot allocate drawing canvas\n");
- app->failed = true;
- yasl_app_exit(yasl_surface_display(surface), YASL_APP_ERROR);
- yasl_surface_destroy(surface);
- app->surface = NULL;
- return;
- }
- struct yasl_framebuffer target = {.size = frame->target.size, .pixels = frame->target.pixels};
- struct yasl_framebuffer *fb = &target;
- memcpy(fb->pixels, app->canvas, (size_t)geometry.size.x * geometry.size.y * sizeof(uint32_t));
- double dpi = (double)app->geometry.scale_120 / YASL_SCALE_UNIT;
- rect(fb, 0, 0, fb->size.x, (int)lround(HEADER * dpi), PANEL);
- char line[128];
- snprintf(line, sizeof(line), "YASL TABLET / %s / %s / %s", backend_name(app->backend),
- device_name(app->pointer.device), app->down ? "CONTACT" : "HOVER");
- text(fb, dpi, 16, 12, line, TEXT);
- if (app->tablet_supported)
- snprintf(line, sizeof(line), "PRESSURE %5u / 65535 %5.1f%%", app->pointer.pen.pressure,
- app->pointer.pen.pressure * 100.0 / 65535);
- else snprintf(line, sizeof(line), "TABLET PROTOCOL UNAVAILABLE / MOUSE DRAWING");
- text(fb, dpi, 16, 36, line, ACCENT);
- int bar_width = (int)lround(220 * dpi);
- rect(fb, (int)(16 * dpi), (int)(58 * dpi), bar_width, (int)(6 * dpi), 0xff34455eu);
- rect(fb, (int)(16 * dpi), (int)(58 * dpi),
- (int)((uint32_t)app->pointer.pen.pressure * (uint64_t)bar_width / 65535), (int)(6 * dpi), ACCENT);
- snprintf(line, sizeof(line), "TILT X %+d / Y %+d", app->pointer.pen.tilt_x, app->pointer.pen.tilt_y);
- text(fb, dpi, 260, 54, line, TEXT);
- text(fb, dpi, 16, 82, "DRAW WITH TIP / FLIP TO ERASE / C CLEAR / ESC QUIT", TEXT);
- if (app->visible && app->pointer.pos.x >= 0 && app->pointer.pos.x < fb->size.x &&
- app->pointer.pos.y >= HEADER * dpi && app->pointer.pos.y < fb->size.y) {
- double radius = brush_radius(app);
- double cx = app->pointer.pos.x, cy = app->pointer.pos.y;
- /* Brush outline and tilt vector are overlays, never stored in canvas. */
- for (int i = 0; i < 180; i++) {
- double angle = i * (6.283185307179586 / 180);
- rect(fb, (int)lround(cx + cos(angle) * radius),
- (int)lround(cy + sin(angle) * radius), 1, 1, PANEL);
+ int left = (int)floor(x - radius), top = (int)floor(y - radius);
+ int right = (int)ceil(x + radius), bottom = (int)ceil(y + radius);
+ if (left < 0) left = 0;
+ if (top < 0) top = 0;
+ if (right > w) right = w;
+ if (bottom > h) bottom = h;
+ for (int row = top; row < bottom; row++) {
+ for (int col = left; col < right; col++) {
+ double dx = col - x, dy = row - y;
+ if (dx * dx + dy * dy <= radius * radius) pixels[(size_t)row * w + col] = erase ? PAPER : INK;
}
- double tx = app->pointer.pen.tilt_x * dpi / 2;
- double ty = app->pointer.pen.tilt_y * dpi / 2;
- for (int i = 0; i <= 64; i++)
- rect(fb, (int)lround(cx + tx * i / 64), (int)lround(cy + ty * i / 64), 2, 2, ACCENT);
- }
- if (!yasl_frame_present(frame, NULL, 0)) {
- app->failed = true;
- }
- if (app->failed) yasl_app_exit(yasl_surface_display(surface), YASL_APP_ERROR);
- if (app->failed || app->smoke_test) {
- yasl_surface_destroy(surface);
- app->surface = NULL;
}
}
-static enum yasl_app_result init(void *ud, struct yasl_display *display)
+/* Paints paper over the area a resize added, or everything when clear is set. */
+static uint32_t *canvas(yasl_surface *s, int *w, int *h, int *painted_w, int *painted_h, bool clear)
{
- struct app *app = ud;
- app->backend = yasl_display_backend(display);
- app->tablet_supported = (yasl_display_caps(display) & YASL_CAP_TABLET) != 0;
- struct yasl_window_spec spec = {
- .title = "YASL Tablet - pressure and tilt", .size_logical = {900, 640},
- };
- app->surface = yasl_surface_create_toplevel(display, &spec);
- if (!app->surface || !resize_canvas(app, yasl_surface_geometry(app->surface))) {
- fprintf(stderr, "Cannot create drawing surface\n");
- app->failed = true;
- return YASL_APP_ERROR;
- }
- yasl_surface_set_cursor(app->surface, YASL_CURSOR_HIDDEN);
- yasl_surface_request_frame(app->surface);
- printf("YASL tablet on %s: pressure changes brush width; eraser removes ink.\n"
- "Mouse: fixed-width drawing. C/Space: clear. Escape: quit.\n",
- backend_name(app->backend));
- if (!app->tablet_supported)
- printf("This display server does not expose tablet input.\n");
- return YASL_APP_CONTINUE;
+ uint32_t *pixels = yasl_cpu_pixels(s, w, h);
+ if (!pixels) return NULL;
+ if (clear) *painted_w = *painted_h = 0;
+ for (int y = 0; y < *h; y++)
+ for (int x = y < *painted_h ? *painted_w : 0; x < *w; x++) pixels[(size_t)y * *w + x] = PAPER;
+ *painted_w = *w;
+ *painted_h = *h;
+ return pixels;
}
int main(int argc, char **argv)
{
- struct app app = {0};
- enum yasl_backend backend = YASL_BACKEND_AUTO;
+ bool smoke = false;
for (int i = 1; i < argc; i++) {
- if (!strcmp(argv[i], "--smoke-test")) app.smoke_test = true;
+ if (!strcmp(argv[i], "--smoke-test")) smoke = true;
else if (!strcmp(argv[i], "--backend") && i + 1 < argc) {
- const char *name = argv[++i];
- if (!strcmp(name, "auto")) backend = YASL_BACKEND_AUTO;
- else if (!strcmp(name, "wayland")) backend = YASL_BACKEND_WAYLAND;
- else if (!strcmp(name, "x11")) backend = YASL_BACKEND_X11;
- else if (!strcmp(name, "cocoa")) backend = YASL_BACKEND_COCOA;
- else { fprintf(stderr, "Unknown backend: %s\n", name); return 1; }
- } else {
- bool help = !strcmp(argv[i], "--help");
- fprintf(help ? stdout : stderr,
- "Usage: %s [--backend auto|wayland|x11|cocoa] [--smoke-test]\n", argv[0]);
- return help ? 0 : 1;
+ const char *b = argv[++i];
+ if (!yasl_set_backend(!strcmp(b, "wayland") ? YASL_BACKEND_WAYLAND :
+ !strcmp(b, "x11") ? YASL_BACKEND_X11 : YASL_BACKEND_COCOA)) return 1;
+ } else return 1;
+ }
+ yasl_surface *s = yasl_surface_create("YASL pen canvas — C clears, Escape exits", 800, 600, YASL_SURFACE_CPU);
+ if (!s) return 1;
+ int painted_w = 0, painted_h = 0, frames = 0, result = 1;
+ bool contact = false;
+ double last_x = 0, last_y = 0;
+ yasl_event e;
+ for (;;) {
+ int n = yasl_wait_event(&e, smoke ? 20 : -1);
+ if (n < 0 || e.type == YASL_EVENT_DISPLAY_LOST) break;
+ if (smoke && frames && n == 0) { result = 0; break; }
+ bool escape = e.type == YASL_EVENT_KEY && e.key.action == YASL_KEY_PRESS && e.key.key == YASL_KEY_ESCAPE;
+ if (e.type == YASL_EVENT_CLOSE || e.type == YASL_EVENT_QUIT || escape) { result = 0; break; }
+ bool clear = false, draw = false, erase = false, down = false, present = e.type == YASL_EVENT_REFRESH;
+ double x = 0, y = 0, p = 0.5;
+ switch (e.type) {
+ case YASL_EVENT_KEY:
+ if (e.key.action == YASL_KEY_PRESS && e.key.key == YASL_KEY_C) clear = present = true;
+ break;
+ case YASL_EVENT_PEN:
+ x = e.pen.x; y = e.pen.y;
+ p = (e.pen.axes_valid & YASL_PEN_PRESSURE) ? e.pen.pressure : 0.5;
+ erase = e.pen.tool == YASL_PEN_ERASER;
+ down = e.pen.in_contact;
+ draw = down && (e.pen.action == YASL_PEN_DOWN || e.pen.action == YASL_PEN_MOVE);
+ if (e.pen.action == YASL_PEN_LEAVE || e.pen.action == YASL_PEN_CANCEL) contact = false;
+ break;
+ case YASL_EVENT_POINTER:
+ x = e.pointer.x; y = e.pointer.y;
+ down = (e.pointer.buttons & YASL_BUTTON_BIT(YASL_BUTTON_PRIMARY)) != 0;
+ draw = down && (e.pointer.action == YASL_POINTER_DOWN || e.pointer.action == YASL_POINTER_MOVE);
+ break;
+ case YASL_EVENT_INPUT_RESET:
+ contact = false;
+ break;
+ default:
+ break;
+ }
+ if (draw || present) {
+ int w, h;
+ int old_w = painted_w, old_h = painted_h;
+ uint32_t *pixels = canvas(s, &w, &h, &painted_w, &painted_h, clear);
+ if (!pixels) break;
+ if (w != old_w || h != old_h) contact = false;
+ if (draw) {
+ double scale = yasl_surface_get_scale(s);
+ x *= scale; y *= scale;
+ double radius = (1.5 + 12 * p) * scale, dx = contact ? x - last_x : 0, dy = contact ? y - last_y : 0;
+ int steps = (int)ceil(hypot(dx, dy) / (radius * 0.4));
+ if (steps < 1) steps = 1;
+ for (int i = 1; i <= steps; i++) stamp(pixels, w, h, x - dx + dx * i / steps, y - dy + dy * i / steps, radius, erase);
+ last_x = x; last_y = y;
+ }
+ if (!yasl_cpu_present(s)) break;
+ frames++;
}
+ if (e.type == YASL_EVENT_PEN || e.type == YASL_EVENT_POINTER) contact = down;
}
- struct yasl_app_desc desc = {
- .app_id = "org.yasl.tablet", .backend = backend, .userdata = &app,
- .init = init, .event = input, .frame = draw,
- };
- int result = yasl_run(&desc);
- if (result) perror("yasl_run");
- free(app.canvas);
- return result || app.failed ? 1 : 0;
+ yasl_shutdown();
+ return result;
}
diff --git a/include/yasl/app.h b/include/yasl/app.h
deleted file mode 100644
index 78e62c4..0000000
--- a/include/yasl/app.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- * @file
- * Unified application runner, event pipeline, and presentation context.
- *
- * Implements the inverted lifecycle architecture for modern cross-platform
- * applications (games, IDEs, and desktop tools). Instead of managing manual
- * dispatch/poll/flush pumps, the application provides event and frame callbacks
- * to yasl_run(), which drives platform runloops, display pacing, and live resizing.
- */
-#ifndef YASL_APP_H
-#define YASL_APP_H
-
-#include "yasl/yasl.h"
-#include "yasl/loop.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** Application callback return status. */
-typedef enum yasl_app_result {
- YASL_APP_CONTINUE = 0, /**< Continue running the application. */
- YASL_APP_QUIT, /**< Request graceful termination of yasl_run(). */
- YASL_APP_ERROR, /**< Request orderly cleanup and a failing run result. */
-} yasl_app_result;
-
-/** Migration adapter; prefer yasl_frame_present() from yasl.h. */
-YASL_API bool yasl_frame_present_cpu(struct yasl_frame *frame,
- const struct yasl_rect2i *dirty_rects, int count);
-
-/**
- * Safely post a custom user event to the main event loop from ANY thread.
- * Wakes the display loop and delivers YASL_EVENT_USER to the event callback.
- * @param d Display.
- * @param s Optional target surface, or NULL.
- * @param code Application-defined message code (yasl_user_event.code).
- * @param payload Arbitrary pointer passed through to the event.
- * @param size Optional payload size.
- * @return true if queued; false if memory failed or d is not run by yasl_run().
- * Migration adapter: keep s alive through the call, and stop producers before
- * shutdown. Prefer yasl_display_post() with a captured ID and release callback
- * in either loop style; legacy payloads have no cancellation cleanup.
- */
-YASL_API bool yasl_post_event(struct yasl_display *d, struct yasl_surface *s,
- uint32_t code, void *payload, size_t size);
-
-/**
- * Watch an open file descriptor or socket for poll-style readiness in the main loop.
- * @param d Display.
- * @param fd Open file descriptor (e.g. LSP stdout pipe or socket).
- * @param poll_events Poll bitmask (e.g. POLLIN, POLLOUT).
- * @param cb Readiness callback run on the main thread.
- * @param ud Userdata.
- * @return Positive watch ID, or -1 on failure.
- */
-YASL_API int yasl_app_watch_fd(struct yasl_display *d, int fd, short poll_events,
- int (*cb)(int fd, short revents, void *ud), void *ud);
-
-/**
- * Unregister a file watch.
- * @param d Display.
- * @param watch_id Watch ID returned by yasl_app_watch_fd().
- */
-YASL_API void yasl_app_unwatch_fd(struct yasl_display *d, int watch_id);
-
-/**
- * Schedule a repeating or one-shot timer on the display's event loop.
- * @param d Display.
- * @param interval_ns Delay in monotonic nanoseconds.
- * @param cb Callback returning next interval in nanoseconds, or 0 to stop.
- * @param ud Userdata.
- * @return Positive timer ID, or 0 on failure.
- */
-YASL_API yasl_timer_id yasl_app_add_timer(struct yasl_display *d, uint64_t interval_ns,
- yasl_timer_cb cb, void *ud);
-
-/**
- * Cancel an active timer.
- * @param d Display.
- * @param id Timer ID returned by yasl_app_add_timer().
- */
-YASL_API void yasl_app_del_timer(struct yasl_display *d, yasl_timer_id id);
-
-/** Application callbacks descriptor passed to yasl_run(). */
-struct yasl_app_desc {
- /** Application identifier, e.g. "org.example.app". */
- const char *app_id;
- /** Desired backend; YASL_BACKEND_AUTO chooses native with fallback. */
- enum yasl_backend backend;
- /** User data passed to all callbacks. */
- void *userdata;
- /**
- * Optional toplevel created before init(), with its first frame requested.
- * Borrowed for the call; spec cbs and ud are ignored as for any runner window.
- * yasl_run() returns an error without calling init() if creation fails.
- */
- const struct yasl_window_spec *window;
-
- /**
- * Optional; called once after display and event machinery initialization.
- * Use this to create further windows and initialize application resources.
- * Return YASL_APP_CONTINUE, YASL_APP_QUIT, or YASL_APP_ERROR.
- */
- enum yasl_app_result (*init)(void *ud, struct yasl_display *d);
-
- /**
- * Unified event handler for input, window and user events, in display-wide enqueue order.
- * Event payloads and borrowed strings are valid for the duration of the callback.
- * INPUT_RESET has no payload and requires clearing held application input.
- * Return YASL_APP_CONTINUE, YASL_APP_QUIT, or YASL_APP_ERROR. Optional; without it, a
- * WINDOW_CLOSE request on any window stops yasl_run().
- */
- enum yasl_app_result (*event)(void *ud, const struct yasl_event *ev);
-
- /**
- * Invoked when a surface needs to present a frame.
- * Synchronized automatically with display pacing and live window resizing.
- */
- void (*frame)(void *ud, struct yasl_surface *s, struct yasl_frame *frame);
-
- /**
- * Called right before the display is closed and yasl_run() exits.
- * Destroy remaining surfaces and release application resources here.
- */
- void (*quit)(void *ud, struct yasl_display *d);
-};
-
-/** Request orderly exit from any display-thread callback; ERROR returns failure. */
-YASL_API void yasl_app_exit(struct yasl_display *d, enum yasl_app_result result);
-/** Borrow the optional desc.window surface during init or later; NULL if absent/destroyed. */
-YASL_API struct yasl_surface *yasl_app_window(struct yasl_display *d);
-
-/**
- * Run the application with the inverted lifecycle runner.
- * Handles the display event loop, live-resizing, pacing, and cleanup.
- * @param desc Application callbacks and options.
- * @return 0 on orderly exit, or non-zero on error.
- * Display quit notifications stop the runner; connection loss and dispatch
- * failures return an error after application cleanup.
- */
-YASL_API int yasl_run(const struct yasl_app_desc *desc);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* YASL_APP_H */
diff --git a/include/yasl/cpu.h b/include/yasl/cpu.h
new file mode 100644
index 0000000..d18f91f
--- /dev/null
+++ b/include/yasl/cpu.h
@@ -0,0 +1,15 @@
+/** @file CPU surfaces: draw into the lent pixel buffer, then present it. */
+#ifndef YASL_CPU_H
+#define YASL_CPU_H
+#include "yasl/yasl.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+/** width*height premultiplied 0xAARRGGBB pixels, rows tightly packed. Contents
+ * persist; a resize keeps the overlap and zeroes the rest. Valid until the next wait. */
+YASL_API uint32_t *yasl_cpu_pixels(yasl_surface *surface, int *width, int *height);
+YASL_API bool yasl_cpu_present(yasl_surface *surface);
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/include/yasl/desktop.h b/include/yasl/desktop.h
index 3e72a50..2c2379e 100644
--- a/include/yasl/desktop.h
+++ b/include/yasl/desktop.h
@@ -1,113 +1,21 @@
-/**
- * @file
- * Desktop services next to windowing: opening URIs, notifications, the bell,
- * appearance preferences, and process paths.
- *
- * Display and surface functions use the display's opening thread (the main
- * thread on Cocoa). Path and locale functions may be called from any thread.
- * On Linux, notifications and appearance use the D-Bus session bus when YASL
- * is built with libdbus-1; without it they report unsupported.
- *
- * Functions that fill a buffer return the full string length in bytes,
- * excluding the NUL, or zero on failure. A result less than cap means out
- * holds the NUL-terminated string. Otherwise out is unchanged; call again with
- * a buffer of at least the result plus one.
- */
+/** @file Optional desktop services; window operations use the owning thread. */
#ifndef YASL_DESKTOP_H
#define YASL_DESKTOP_H
-
#include "yasl/yasl.h"
-
#ifdef __cplusplus
extern "C" {
#endif
-
-/** Appearance preference flags returned by yasl_display_appearance(). */
-enum yasl_appearance : uint32_t {
- YASL_APPEARANCE_DARK = 1 << 0, /**< The user prefers dark colors. */
- YASL_APPEARANCE_LIGHT = 1 << 1, /**< The user prefers light colors. */
- YASL_APPEARANCE_HIGH_CONTRAST = 1 << 2, /**< The user asks for higher contrast. */
-};
-
-/**
- * Open a URI with the user's default handler.
- * @param d Display, or NULL.
- * @param uri NUL-terminated absolute URI with a scheme, such as https: or file:.
- * @return true if the request was handed to the system; false for NULL
- * arguments, a missing scheme, or a failed launch.
- * YASL checks only the URI syntax. The application decides which schemes and
- * hosts it trusts. Success does not confirm that a handler opened the URI.
- * Linux runs xdg-open from PATH in a new session, with default signal
- * dispositions, an empty signal mask, stdin on /dev/null, and no descriptors
- * above stderr. It forks a short-lived helper first, which raises SIGCHLD;
- * YASL reaps it unless the application's reaper does. Cocoa uses NSWorkspace.
- */
-YASL_API bool yasl_display_open_uri(struct yasl_display *d, const char *uri);
-/**
- * Show a desktop notification.
- * @param d Display, or NULL.
- * @param title NUL-terminated UTF-8 summary.
- * @param body NUL-terminated UTF-8 body, or NULL for none.
- * @return true if the request was sent; false for NULL arguments or when no
- * notification service is available.
- * Linux sends org.freedesktop.Notifications.Notify with the display's app_id
- * as the application name and desktop-entry hint. Cocoa uses the user
- * notification center, which needs an app bundle with a bundle identifier,
- * and asks for permission on first use. Success does not confirm that the
- * notification was shown.
- */
-YASL_API bool yasl_display_notify(struct yasl_display *d, const char *title, const char *body);
-/**
- * Read the user's appearance preferences.
- * @param d Display, or NULL.
- * @return A yasl_appearance bitmask, or zero for NULL, no preference, or no
- * way to read it.
- * Linux reads color-scheme and contrast from the XDG Settings portal, which
- * waits briefly for a reply, then falls back to the GTK_THEME variable. Cocoa
- * reads the effective appearance and the increase-contrast setting. The
- * result is not cached and no change notification is sent; query again when
- * the window is focused.
- */
-YASL_API uint32_t yasl_display_appearance(struct yasl_display *d);
-/**
- * Ring the system bell for a surface.
- * @param s Surface, or NULL.
- * @return true if the request was sent; false for NULL or when unsupported.
- * The system decides whether the bell is audible, visual, or muted. Wayland
- * needs the xdg-system-bell protocol; X11 uses the XKB bell for the window
- * and Cocoa uses NSBeep.
- */
-YASL_API bool yasl_surface_bell(struct yasl_surface *s);
-/**
- * Get the absolute path of the running executable, with symbolic links resolved.
- * @param out Output buffer, or NULL when cap is zero.
- * @param cap Size of out in bytes.
- * @return Path length, or zero on failure. See the buffer rule in this file.
- */
-YASL_API size_t yasl_executable_path(char *out, size_t cap);
-/**
- * Get the resource directory of the app bundle that contains the executable.
- * @param out Output buffer, or NULL when cap is zero.
- * @param cap Size of out in bytes.
- * @return Path length, or zero when the executable is not in an app bundle.
- * Only Cocoa has app bundles; other platforms return zero.
- */
-YASL_API size_t yasl_resource_dir(char *out, size_t cap);
-/**
- * Get the user's preferred locale as a POSIX locale name, such as "en_US.UTF-8".
- * @param out Output buffer, or NULL when cap is zero.
- * @param cap Size of out in bytes.
- * @return Name length, or zero on failure.
- * Linux returns the first nonempty LC_ALL, LC_CTYPE, or LANG, and "C.UTF-8"
- * when none is set. Cocoa builds the name from the current locale's language
- * and region with a ".UTF-8" codeset, because apps started from Finder have no
- * locale variables. Use the result to set LANG for child processes; YASL does
- * not change the process locale.
- */
-YASL_API size_t yasl_default_locale(char *out, size_t cap);
-
+enum yasl_appearance { YASL_APPEARANCE_DARK=1u, YASL_APPEARANCE_LIGHT=2u, YASL_APPEARANCE_HIGH_CONTRAST=4u };
+YASL_API bool yasl_open_uri(const char *uri);
+YASL_API bool yasl_notify(const char *title, const char *body);
+YASL_API unsigned yasl_appearance(void);
+YASL_API bool yasl_surface_bell(yasl_surface *surface);
+/** Full byte length excluding NUL, zero on failure. Buffer unchanged unless cap
+ * exceeds returned length. NULL/zero queries size. These three are thread-safe. */
+YASL_API size_t yasl_executable_path(char *out, size_t capacity);
+YASL_API size_t yasl_resource_dir(char *out, size_t capacity);
+YASL_API size_t yasl_default_locale(char *out, size_t capacity);
#ifdef __cplusplus
}
#endif
-
-#endif /* YASL_DESKTOP_H */
+#endif
diff --git a/include/yasl/gl.h b/include/yasl/gl.h
new file mode 100644
index 0000000..66a245a
--- /dev/null
+++ b/include/yasl/gl.h
@@ -0,0 +1,27 @@
+/** @file Optional OpenGL/OpenGL ES contexts. Attribute lists are per-call. */
+#ifndef YASL_GL_H
+#define YASL_GL_H
+#include "yasl/yasl.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+enum yasl_gl_attribute { YASL_GL_END, YASL_GL_MAJOR, YASL_GL_MINOR, YASL_GL_PROFILE, YASL_GL_DEBUG,
+ YASL_GL_FORWARD_COMPAT, YASL_GL_RED_BITS, YASL_GL_GREEN_BITS, YASL_GL_BLUE_BITS, YASL_GL_ALPHA_BITS,
+ YASL_GL_DEPTH_BITS, YASL_GL_STENCIL_BITS, YASL_GL_SAMPLES, YASL_GL_SRGB, YASL_GL_API };
+enum yasl_gl_profile { YASL_GL_ANY, YASL_GL_CORE, YASL_GL_COMPATIBILITY };
+enum yasl_gl_api { YASL_GL_OPENGL, YASL_GL_OPENGL_ES };
+typedef void (*yasl_gl_proc)(void);
+/** key/value pairs terminated by END, NULL defaults to GL3.2 core RGBA8 D24 S8.
+ * share is a live GL surface or NULL. CPU flag is invalid. Context is owned by
+ * the returned surface. Detach it from all rendering threads before destruction. */
+YASL_API yasl_surface *yasl_gl_surface_create(const char *title, int width, int height,
+ unsigned flags, const int *attributes, yasl_surface *share);
+/** May run on a rendering thread. NULL detaches this thread's current context. */
+YASL_API bool yasl_gl_make_current(yasl_surface *surface);
+YASL_API yasl_gl_proc yasl_gl_get_proc_address(const char *name);
+YASL_API bool yasl_gl_swap_interval(yasl_surface *surface, int interval);
+YASL_API bool yasl_gl_swap_buffers(yasl_surface *surface);
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/include/yasl/keycodes.h b/include/yasl/keycodes.h
index d34aeae..afa3eed 100644
--- a/include/yasl/keycodes.h
+++ b/include/yasl/keycodes.h
@@ -1,149 +1,142 @@
-/**
- * @file
- * Portable physical keyboard identities, included by yasl/yasl.h.
- * Letter/punctuation names describe positions on a US keyboard, regardless of
- * active layout or modifiers. Use YASL_EVENT_TEXT for text and codepoint/shifted
- * in yasl_key_event for layout labels. These values are neither Unicode nor
- * native scancodes; do not derive a character from a key enumerator.
- */
+/** @file Physical keys, named by their position on a US keyboard whatever the
+ * layout. Use TEXT events for typed text and key.codepoint for layout labels. */
#ifndef YASL_KEYCODES_H
#define YASL_KEYCODES_H
#include <stdint.h>
-/** Physical keys. Unrecognized native keys report YASL_KEY_UNKNOWN. */
enum yasl_key : uint32_t {
YASL_KEY_UNKNOWN = 0x000, /**< No portable identity; native_code may still identify the key. */
- YASL_KEY_SPACE = 0x020, /**< Space. */
- YASL_KEY_APOSTROPHE = 0x027, /**< Apostrophe. */
- YASL_KEY_COMMA = 0x02c, /**< Comma. */
- YASL_KEY_MINUS = 0x02d, /**< Minus. */
- YASL_KEY_PERIOD = 0x02e, /**< Period. */
- YASL_KEY_SLASH = 0x02f, /**< Slash. */
- YASL_KEY_0 = 0x030, /**< Physical 0 key. */
- YASL_KEY_1 = 0x031, /**< Physical 1 key. */
- YASL_KEY_2 = 0x032, /**< Physical 2 key. */
- YASL_KEY_3 = 0x033, /**< Physical 3 key. */
- YASL_KEY_4 = 0x034, /**< Physical 4 key. */
- YASL_KEY_5 = 0x035, /**< Physical 5 key. */
- YASL_KEY_6 = 0x036, /**< Physical 6 key. */
- YASL_KEY_7 = 0x037, /**< Physical 7 key. */
- YASL_KEY_8 = 0x038, /**< Physical 8 key. */
- YASL_KEY_9 = 0x039, /**< Physical 9 key. */
- YASL_KEY_SEMICOLON = 0x03b, /**< Semicolon. */
- YASL_KEY_EQUAL = 0x03d, /**< Equal. */
- YASL_KEY_A = 0x041, /**< Physical US A position; independent of layout and modifiers. */
- YASL_KEY_B = 0x042, /**< Physical US B position; independent of layout and modifiers. */
- YASL_KEY_C = 0x043, /**< Physical US C position; independent of layout and modifiers. */
- YASL_KEY_D = 0x044, /**< Physical US D position; independent of layout and modifiers. */
- YASL_KEY_E = 0x045, /**< Physical US E position; independent of layout and modifiers. */
- YASL_KEY_F = 0x046, /**< Physical US F position; independent of layout and modifiers. */
- YASL_KEY_G = 0x047, /**< Physical US G position; independent of layout and modifiers. */
- YASL_KEY_H = 0x048, /**< Physical US H position; independent of layout and modifiers. */
- YASL_KEY_I = 0x049, /**< Physical US I position; independent of layout and modifiers. */
- YASL_KEY_J = 0x04a, /**< Physical US J position; independent of layout and modifiers. */
- YASL_KEY_K = 0x04b, /**< Physical US K position; independent of layout and modifiers. */
- YASL_KEY_L = 0x04c, /**< Physical US L position; independent of layout and modifiers. */
- YASL_KEY_M = 0x04d, /**< Physical US M position; independent of layout and modifiers. */
- YASL_KEY_N = 0x04e, /**< Physical US N position; independent of layout and modifiers. */
- YASL_KEY_O = 0x04f, /**< Physical US O position; independent of layout and modifiers. */
- YASL_KEY_P = 0x050, /**< Physical US P position; independent of layout and modifiers. */
- YASL_KEY_Q = 0x051, /**< Physical US Q position; independent of layout and modifiers. */
- YASL_KEY_R = 0x052, /**< Physical US R position; independent of layout and modifiers. */
- YASL_KEY_S = 0x053, /**< Physical US S position; independent of layout and modifiers. */
- YASL_KEY_T = 0x054, /**< Physical US T position; independent of layout and modifiers. */
- YASL_KEY_U = 0x055, /**< Physical US U position; independent of layout and modifiers. */
- YASL_KEY_V = 0x056, /**< Physical US V position; independent of layout and modifiers. */
- YASL_KEY_W = 0x057, /**< Physical US W position; independent of layout and modifiers. */
- YASL_KEY_X = 0x058, /**< Physical US X position; independent of layout and modifiers. */
- YASL_KEY_Y = 0x059, /**< Physical US Y position; independent of layout and modifiers. */
- YASL_KEY_Z = 0x05a, /**< Physical US Z position; independent of layout and modifiers. */
- YASL_KEY_LEFT_BRACKET = 0x05b, /**< Left Bracket. */
- YASL_KEY_BACKSLASH = 0x05c, /**< Backslash. */
- YASL_KEY_RIGHT_BRACKET = 0x05d, /**< Right Bracket. */
- YASL_KEY_GRAVE = 0x060, /**< Grave. */
- YASL_KEY_ESCAPE = 0x100, /**< Escape. */
- YASL_KEY_ENTER = 0x101, /**< Enter. */
- YASL_KEY_TAB = 0x102, /**< Tab. */
- YASL_KEY_BACKSPACE = 0x103, /**< Backspace. */
- YASL_KEY_INSERT = 0x104, /**< Insert. */
- YASL_KEY_DELETE = 0x105, /**< Delete. */
- YASL_KEY_RIGHT = 0x106, /**< Right. */
- YASL_KEY_LEFT = 0x107, /**< Left. */
- YASL_KEY_DOWN = 0x108, /**< Down. */
- YASL_KEY_UP = 0x109, /**< Up. */
- YASL_KEY_PAGE_UP = 0x10a, /**< Page Up. */
- YASL_KEY_PAGE_DOWN = 0x10b, /**< Page Down. */
- YASL_KEY_HOME = 0x10c, /**< Home. */
- YASL_KEY_END = 0x10d, /**< End. */
- YASL_KEY_CAPS_LOCK = 0x10e, /**< Caps Lock. */
- YASL_KEY_SCROLL_LOCK = 0x10f, /**< Scroll Lock. */
- YASL_KEY_NUM_LOCK = 0x110, /**< Num Lock. */
- YASL_KEY_PRINT_SCREEN = 0x111, /**< Print Screen. */
- YASL_KEY_PAUSE = 0x112, /**< Pause. */
- YASL_KEY_ISO_BACKSLASH = 0x113, /**< Iso Backslash. */
- YASL_KEY_YEN = 0x114, /**< Yen. */
- YASL_KEY_RO = 0x115, /**< Ro. */
- YASL_KEY_KANA = 0x116, /**< Kana. */
- YASL_KEY_EISU = 0x117, /**< Eisu. */
- YASL_KEY_CONVERT = 0x118, /**< Convert. */
- YASL_KEY_NONCONVERT = 0x119, /**< Nonconvert. */
- YASL_KEY_F1 = 0x120, /**< Function key F1. */
- YASL_KEY_F2 = 0x121, /**< Function key F2. */
- YASL_KEY_F3 = 0x122, /**< Function key F3. */
- YASL_KEY_F4 = 0x123, /**< Function key F4. */
- YASL_KEY_F5 = 0x124, /**< Function key F5. */
- YASL_KEY_F6 = 0x125, /**< Function key F6. */
- YASL_KEY_F7 = 0x126, /**< Function key F7. */
- YASL_KEY_F8 = 0x127, /**< Function key F8. */
- YASL_KEY_F9 = 0x128, /**< Function key F9. */
- YASL_KEY_F10 = 0x129, /**< Function key F10. */
- YASL_KEY_F11 = 0x12a, /**< Function key F11. */
- YASL_KEY_F12 = 0x12b, /**< Function key F12. */
- YASL_KEY_F13 = 0x12c, /**< Function key F13. */
- YASL_KEY_F14 = 0x12d, /**< Function key F14. */
- YASL_KEY_F15 = 0x12e, /**< Function key F15. */
- YASL_KEY_F16 = 0x12f, /**< Function key F16. */
- YASL_KEY_F17 = 0x130, /**< Function key F17. */
- YASL_KEY_F18 = 0x131, /**< Function key F18. */
- YASL_KEY_F19 = 0x132, /**< Function key F19. */
- YASL_KEY_F20 = 0x133, /**< Function key F20. */
- YASL_KEY_F21 = 0x134, /**< Function key F21. */
- YASL_KEY_F22 = 0x135, /**< Function key F22. */
- YASL_KEY_F23 = 0x136, /**< Function key F23. */
- YASL_KEY_F24 = 0x137, /**< Function key F24. */
- YASL_KEY_F25 = 0x138, /**< Function key F25. */
- YASL_KEY_KP_0 = 0x140, /**< Numeric keypad 0. */
- YASL_KEY_KP_1 = 0x141, /**< Numeric keypad 1. */
- YASL_KEY_KP_2 = 0x142, /**< Numeric keypad 2. */
- YASL_KEY_KP_3 = 0x143, /**< Numeric keypad 3. */
- YASL_KEY_KP_4 = 0x144, /**< Numeric keypad 4. */
- YASL_KEY_KP_5 = 0x145, /**< Numeric keypad 5. */
- YASL_KEY_KP_6 = 0x146, /**< Numeric keypad 6. */
- YASL_KEY_KP_7 = 0x147, /**< Numeric keypad 7. */
- YASL_KEY_KP_8 = 0x148, /**< Numeric keypad 8. */
- YASL_KEY_KP_9 = 0x149, /**< Numeric keypad 9. */
- YASL_KEY_KP_DECIMAL = 0x14a, /**< Numeric keypad decimal. */
- YASL_KEY_KP_DIVIDE = 0x14b, /**< Numeric keypad divide. */
- YASL_KEY_KP_MULTIPLY = 0x14c, /**< Numeric keypad multiply. */
- YASL_KEY_KP_SUBTRACT = 0x14d, /**< Numeric keypad subtract. */
- YASL_KEY_KP_ADD = 0x14e, /**< Numeric keypad add. */
- YASL_KEY_KP_ENTER = 0x14f, /**< Numeric keypad enter. */
- YASL_KEY_KP_EQUAL = 0x150, /**< Numeric keypad equal. */
- YASL_KEY_KP_COMMA = 0x151, /**< Numeric keypad comma. */
- YASL_KEY_KP_CLEAR = 0x152, /**< Numeric keypad clear. */
- YASL_KEY_LEFT_SHIFT = 0x160, /**< Left Shift. */
- YASL_KEY_LEFT_CONTROL = 0x161, /**< Left Control. */
- YASL_KEY_LEFT_ALT = 0x162, /**< Left Alt. */
- YASL_KEY_LEFT_SUPER = 0x163, /**< Left Super. */
- YASL_KEY_RIGHT_SHIFT = 0x164, /**< Right Shift. */
- YASL_KEY_RIGHT_CONTROL = 0x165, /**< Right Control. */
- YASL_KEY_RIGHT_ALT = 0x166, /**< Right Alt. */
- YASL_KEY_RIGHT_SUPER = 0x167, /**< Right Super. */
- YASL_KEY_MENU = 0x168, /**< Menu. */
- YASL_KEY_VOLUME_UP = 0x169, /**< Volume Up. */
- YASL_KEY_VOLUME_DOWN = 0x16a, /**< Volume Down. */
- YASL_KEY_MUTE = 0x16b, /**< Mute. */
+ YASL_KEY_SPACE = 0x020,
+ YASL_KEY_APOSTROPHE = 0x027,
+ YASL_KEY_COMMA = 0x02c,
+ YASL_KEY_MINUS = 0x02d,
+ YASL_KEY_PERIOD = 0x02e,
+ YASL_KEY_SLASH = 0x02f,
+ YASL_KEY_0 = 0x030,
+ YASL_KEY_1 = 0x031,
+ YASL_KEY_2 = 0x032,
+ YASL_KEY_3 = 0x033,
+ YASL_KEY_4 = 0x034,
+ YASL_KEY_5 = 0x035,
+ YASL_KEY_6 = 0x036,
+ YASL_KEY_7 = 0x037,
+ YASL_KEY_8 = 0x038,
+ YASL_KEY_9 = 0x039,
+ YASL_KEY_SEMICOLON = 0x03b,
+ YASL_KEY_EQUAL = 0x03d,
+ YASL_KEY_A = 0x041,
+ YASL_KEY_B = 0x042,
+ YASL_KEY_C = 0x043,
+ YASL_KEY_D = 0x044,
+ YASL_KEY_E = 0x045,
+ YASL_KEY_F = 0x046,
+ YASL_KEY_G = 0x047,
+ YASL_KEY_H = 0x048,
+ YASL_KEY_I = 0x049,
+ YASL_KEY_J = 0x04a,
+ YASL_KEY_K = 0x04b,
+ YASL_KEY_L = 0x04c,
+ YASL_KEY_M = 0x04d,
+ YASL_KEY_N = 0x04e,
+ YASL_KEY_O = 0x04f,
+ YASL_KEY_P = 0x050,
+ YASL_KEY_Q = 0x051,
+ YASL_KEY_R = 0x052,
+ YASL_KEY_S = 0x053,
+ YASL_KEY_T = 0x054,
+ YASL_KEY_U = 0x055,
+ YASL_KEY_V = 0x056,
+ YASL_KEY_W = 0x057,
+ YASL_KEY_X = 0x058,
+ YASL_KEY_Y = 0x059,
+ YASL_KEY_Z = 0x05a,
+ YASL_KEY_LEFT_BRACKET = 0x05b,
+ YASL_KEY_BACKSLASH = 0x05c,
+ YASL_KEY_RIGHT_BRACKET = 0x05d,
+ YASL_KEY_GRAVE = 0x060,
+ YASL_KEY_ESCAPE = 0x100,
+ YASL_KEY_ENTER = 0x101,
+ YASL_KEY_TAB = 0x102,
+ YASL_KEY_BACKSPACE = 0x103,
+ YASL_KEY_INSERT = 0x104,
+ YASL_KEY_DELETE = 0x105,
+ YASL_KEY_RIGHT = 0x106,
+ YASL_KEY_LEFT = 0x107,
+ YASL_KEY_DOWN = 0x108,
+ YASL_KEY_UP = 0x109,
+ YASL_KEY_PAGE_UP = 0x10a,
+ YASL_KEY_PAGE_DOWN = 0x10b,
+ YASL_KEY_HOME = 0x10c,
+ YASL_KEY_END = 0x10d,
+ YASL_KEY_CAPS_LOCK = 0x10e,
+ YASL_KEY_SCROLL_LOCK = 0x10f,
+ YASL_KEY_NUM_LOCK = 0x110,
+ YASL_KEY_PRINT_SCREEN = 0x111,
+ YASL_KEY_PAUSE = 0x112,
+ YASL_KEY_ISO_BACKSLASH = 0x113,
+ YASL_KEY_YEN = 0x114,
+ YASL_KEY_RO = 0x115,
+ YASL_KEY_KANA = 0x116,
+ YASL_KEY_EISU = 0x117,
+ YASL_KEY_CONVERT = 0x118,
+ YASL_KEY_NONCONVERT = 0x119,
+ YASL_KEY_F1 = 0x120,
+ YASL_KEY_F2 = 0x121,
+ YASL_KEY_F3 = 0x122,
+ YASL_KEY_F4 = 0x123,
+ YASL_KEY_F5 = 0x124,
+ YASL_KEY_F6 = 0x125,
+ YASL_KEY_F7 = 0x126,
+ YASL_KEY_F8 = 0x127,
+ YASL_KEY_F9 = 0x128,
+ YASL_KEY_F10 = 0x129,
+ YASL_KEY_F11 = 0x12a,
+ YASL_KEY_F12 = 0x12b,
+ YASL_KEY_F13 = 0x12c,
+ YASL_KEY_F14 = 0x12d,
+ YASL_KEY_F15 = 0x12e,
+ YASL_KEY_F16 = 0x12f,
+ YASL_KEY_F17 = 0x130,
+ YASL_KEY_F18 = 0x131,
+ YASL_KEY_F19 = 0x132,
+ YASL_KEY_F20 = 0x133,
+ YASL_KEY_F21 = 0x134,
+ YASL_KEY_F22 = 0x135,
+ YASL_KEY_F23 = 0x136,
+ YASL_KEY_F24 = 0x137,
+ YASL_KEY_F25 = 0x138,
+ YASL_KEY_KP_0 = 0x140,
+ YASL_KEY_KP_1 = 0x141,
+ YASL_KEY_KP_2 = 0x142,
+ YASL_KEY_KP_3 = 0x143,
+ YASL_KEY_KP_4 = 0x144,
+ YASL_KEY_KP_5 = 0x145,
+ YASL_KEY_KP_6 = 0x146,
+ YASL_KEY_KP_7 = 0x147,
+ YASL_KEY_KP_8 = 0x148,
+ YASL_KEY_KP_9 = 0x149,
+ YASL_KEY_KP_DECIMAL = 0x14a,
+ YASL_KEY_KP_DIVIDE = 0x14b,
+ YASL_KEY_KP_MULTIPLY = 0x14c,
+ YASL_KEY_KP_SUBTRACT = 0x14d,
+ YASL_KEY_KP_ADD = 0x14e,
+ YASL_KEY_KP_ENTER = 0x14f,
+ YASL_KEY_KP_EQUAL = 0x150,
+ YASL_KEY_KP_COMMA = 0x151,
+ YASL_KEY_KP_CLEAR = 0x152,
+ YASL_KEY_LEFT_SHIFT = 0x160,
+ YASL_KEY_LEFT_CONTROL = 0x161,
+ YASL_KEY_LEFT_ALT = 0x162,
+ YASL_KEY_LEFT_SUPER = 0x163,
+ YASL_KEY_RIGHT_SHIFT = 0x164,
+ YASL_KEY_RIGHT_CONTROL = 0x165,
+ YASL_KEY_RIGHT_ALT = 0x166,
+ YASL_KEY_RIGHT_SUPER = 0x167,
+ YASL_KEY_MENU = 0x168,
+ YASL_KEY_VOLUME_UP = 0x169,
+ YASL_KEY_VOLUME_DOWN = 0x16a,
+ YASL_KEY_MUTE = 0x16b,
};
#endif /* YASL_KEYCODES_H */
diff --git a/include/yasl/metal.h b/include/yasl/metal.h
new file mode 100644
index 0000000..695f80c
--- /dev/null
+++ b/include/yasl/metal.h
@@ -0,0 +1,20 @@
+/** @file Direct Metal layer; application configures its device/format and presents. */
+#ifndef YASL_METAL_H
+#define YASL_METAL_H
+#include "yasl/yasl.h"
+#ifdef __OBJC__
+@class CAMetalLayer;
+#else
+typedef struct objc_object CAMetalLayer;
+#endif
+#ifdef __cplusplus
+extern "C" {
+#endif
+/** Borrowed until surface destruction. YASL owns view/layer and drawableSize.
+ * No device, drawable acquisition, command submission, or implicit present.
+ * Returns NULL on unsupported backend, CPU/GL surface, or incompatible presenter. */
+YASL_API CAMetalLayer *yasl_metal_layer(yasl_surface *surface);
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/include/yasl/native/cocoa.h b/include/yasl/native/cocoa.h
index 9b221cf..bd92c98 100644
--- a/include/yasl/native/cocoa.h
+++ b/include/yasl/native/cocoa.h
@@ -1,55 +1,23 @@
-/**
- * @file
- * Typed borrowed Cocoa handles; available on macOS. Use on the main thread.
- */
#ifndef YASL_NATIVE_COCOA_H
#define YASL_NATIVE_COCOA_H
-
#include "yasl/yasl.h"
-
#ifdef __OBJC__
@class NSApplication;
@class NSWindow;
@class NSView;
#else
-typedef struct objc_object NSApplication; /**< Opaque Cocoa application in plain C/C++. */
-typedef struct objc_object NSWindow; /**< Opaque Cocoa window in plain C/C++. */
-typedef struct objc_object NSView; /**< Opaque Cocoa view in plain C/C++. */
+typedef struct objc_object NSApplication;
+typedef struct objc_object NSWindow;
+typedef struct objc_object NSView;
#endif
-
#ifdef __cplusplus
extern "C" {
#endif
-
-/* Return borrowed Cocoa handles (expire on display/surface destruction).
- * Return NULL / nil if display/surface is not a Cocoa object or NULL. */
-/**
- * Get the borrowed NSApplication for an object of the matching backend.
- * @param d Live display, or NULL.
- * @return The handle, or zero/NULL for NULL or a different backend.
- * Expires on display destruction. Available only when this backend is built.
- * Never pass a pointer to a destroyed object.
- */
-YASL_API NSApplication *yasl_cocoa_display_get_application(struct yasl_display *d);
-/**
- * Get the borrowed NSWindow for an object of the matching backend.
- * @param s Live surface, or NULL.
- * @return The handle, or zero/NULL for NULL or a different backend.
- * Expires on surface destruction. Available only when this backend is built.
- * Never pass a pointer to a destroyed object.
- */
-YASL_API NSWindow *yasl_cocoa_surface_get_window(struct yasl_surface *s);
-/**
- * Get the borrowed NSView for an object of the matching backend.
- * @param s Live surface, or NULL.
- * @return The handle, or zero/NULL for NULL or a different backend.
- * Expires on surface destruction. Available only when this backend is built.
- * Never pass a pointer to a destroyed object.
- */
-YASL_API NSView *yasl_cocoa_surface_get_view(struct yasl_surface *s);
-
+/** Borrowed native objects. Use on main thread, never change native ownership. */
+YASL_API NSApplication *yasl_cocoa_application(void);
+YASL_API NSWindow *yasl_cocoa_window(const yasl_surface *surface);
+YASL_API NSView *yasl_cocoa_view(const yasl_surface *surface);
#ifdef __cplusplus
}
#endif
-
-#endif /* YASL_NATIVE_COCOA_H */
+#endif
diff --git a/include/yasl/native/wayland.h b/include/yasl/native/wayland.h
index b11e3b8..1b8881e 100644
--- a/include/yasl/native/wayland.h
+++ b/include/yasl/native/wayland.h
@@ -1,40 +1,15 @@
-/**
- * @file
- * Typed borrowed Wayland handles; available when built with Wayland support.
- */
#ifndef YASL_NATIVE_WAYLAND_H
#define YASL_NATIVE_WAYLAND_H
-
#include "yasl/yasl.h"
-
+struct wl_display;
+struct wl_surface;
#ifdef __cplusplus
extern "C" {
#endif
-
-struct wl_display;
-struct wl_surface;
-
-/* Return borrowed Wayland handles (expire on display/surface destruction).
- * Return NULL if display/surface is not a Wayland object or NULL. */
-/**
- * Get the borrowed Wayland connection for an object of the matching backend.
- * @param d Live display, or NULL.
- * @return The handle, or zero/NULL for NULL or a different backend.
- * Expires on display destruction. Available only when this backend is built.
- * Never pass a pointer to a destroyed object.
- */
-YASL_API struct wl_display *yasl_wayland_display_get_wl_display(struct yasl_display *d);
-/**
- * Get the borrowed Wayland surface for an object of the matching backend.
- * @param s Live surface, or NULL.
- * @return The handle, or zero/NULL for NULL or a different backend.
- * Expires on surface destruction. Available only when this backend is built.
- * Never pass a pointer to a destroyed object.
- */
-YASL_API struct wl_surface *yasl_wayland_surface_get_wl_surface(struct yasl_surface *s);
-
+/** Borrowed handles; never dispatch or close native connections independently. */
+YASL_API struct wl_display *yasl_wayland_display(const yasl_surface *surface);
+YASL_API struct wl_surface *yasl_wayland_surface(const yasl_surface *surface);
#ifdef __cplusplus
}
#endif
-
-#endif /* YASL_NATIVE_WAYLAND_H */
+#endif
diff --git a/include/yasl/native/x11.h b/include/yasl/native/x11.h
index b719b4b..f8c37ae 100644
--- a/include/yasl/native/x11.h
+++ b/include/yasl/native/x11.h
@@ -1,40 +1,14 @@
-/**
- * @file
- * Typed borrowed X11 handles; available when built with X11 support.
- */
#ifndef YASL_NATIVE_X11_H
#define YASL_NATIVE_X11_H
-
#include "yasl/yasl.h"
-#include <stdint.h>
-
+typedef struct _XDisplay Display;
#ifdef __cplusplus
extern "C" {
#endif
-
-typedef struct _XDisplay Display; /**< Opaque Xlib connection, compatible with Xlib.h. */
-
-/* Return borrowed X11 handles (expire on display/surface destruction).
- * Return NULL / 0 if display/surface is not an X11 object or NULL. */
-/**
- * Get the borrowed X11 Display for an object of the matching backend.
- * @param d Live display, or NULL.
- * @return The handle, or zero/NULL for NULL or a different backend.
- * Expires on display destruction. Available only when this backend is built.
- * Never pass a pointer to a destroyed object.
- */
-YASL_API Display *yasl_x11_display_get_display(struct yasl_display *d);
-/**
- * Get the borrowed X11 Window ID for an object of the matching backend.
- * @param s Live surface, or NULL.
- * @return The handle, or zero/NULL for NULL or a different backend.
- * Expires on surface destruction. Available only when this backend is built.
- * Never pass a pointer to a destroyed object.
- */
-YASL_API uintptr_t yasl_x11_surface_get_window(struct yasl_surface *s);
-
+/** Borrowed handles; surface/window lifetime and native connection stay in YASL. */
+YASL_API Display *yasl_x11_display(const yasl_surface *surface);
+YASL_API uintptr_t yasl_x11_window(const yasl_surface *surface);
#ifdef __cplusplus
}
#endif
-
-#endif /* YASL_NATIVE_X11_H */
+#endif
diff --git a/include/yasl/vulkan.h b/include/yasl/vulkan.h
new file mode 100644
index 0000000..afa1089
--- /dev/null
+++ b/include/yasl/vulkan.h
@@ -0,0 +1,19 @@
+/** @file Vulkan WSI only; application owns all Vulkan objects. */
+#ifndef YASL_VULKAN_H
+#define YASL_VULKAN_H
+#include "yasl/yasl.h"
+#include <vulkan/vulkan.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+YASL_API bool yasl_vulkan_init(PFN_vkGetInstanceProcAddr loader);
+/** Borrowed list through shutdown; merge with app extensions before instance creation. */
+YASL_API const char *const *yasl_vulkan_instance_extensions(const yasl_surface *surface, uint32_t *count);
+YASL_API VkInstanceCreateFlags yasl_vulkan_instance_flags(const yasl_surface *surface);
+/** Uses injected loader; destroy returned VkSurfaceKHR before YASL surface. */
+YASL_API VkResult yasl_vulkan_create_surface(yasl_surface *surface, VkInstance instance,
+ const VkAllocationCallbacks *allocator, VkSurfaceKHR *out);
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/include/yasl/yasl.h b/include/yasl/yasl.h
index 889f70a..350339b 100644
--- a/include/yasl/yasl.h
+++ b/include/yasl/yasl.h
@@ -1,1089 +1,152 @@
-/**
- * @file
- * Portable desktop surfaces, CPU drawing, input, and display dispatch.
- *
- * All calls and callbacks use the display's opening thread (the main thread on
- * Cocoa), unless explicitly documented otherwise. Userdata is borrowed; callback
- * tables and creation specs are copied. Do not recursively dispatch/flush or
- * destroy a display/loop while dispatch or a callback is active. Destroy child
- * popups before parents, surfaces before displays, displays before borrowed loops.
- * Surface destruction from its own callback is supported; stop using it immediately.
- *
- * Zero-initialize option and callback structs before setting fields. Pointers to
- * destroyed objects are invalid, even for getters and destroy functions. NULL
- * handling is documented per function; it does not make freed pointers safe.
- * Only functions explicitly documenting errno guarantee a diagnostic there.
- * Other boolean results report success/availability; void setters are best effort.
- * New window requests, clipboard operations, frame presentation, and posting
- * document errno on failure. Event strings belong to the display queue.
- *
- * Toplevel creation sizes use logical pixels. Geometry, framebuffers, pointer
- * positions, damage, IME carets, popup specs, and drag offsets use device pixels.
- * Coordinates have their origin at the upper left; x grows right and y grows down.
- * Scale is an integer ratio with YASL_SCALE_UNIT (120) representing 1x.
- * See the application guide for the display-wide dispatch/poll/flush cycle
- * and backend limits. Legacy surface polling is an input-only filtered adapter.
- */
+/** @file Windows, input and events. The first surface starts the backend; use the
+ * creating thread (main on Cocoa) for every call except yasl_wake. Coordinates are
+ * logical units unless named pixel. */
#ifndef YASL_H
#define YASL_H
-
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
-#include <limits.h>
-#include <float.h>
-#include <math.h>
-
-#include "yasl/timing.h"
#include "yasl/keycodes.h"
-
+#if defined(__GNUC__)
+#define YASL_API __attribute__((visibility("default")))
+#else
+#define YASL_API
+#endif
#ifdef __cplusplus
extern "C" {
#endif
-
-/** Public shared-library symbol visibility annotation. */
-#if defined(_WIN32) || defined(__CYGWIN__)
- #if defined(YASL_BUILD_SHARED)
- #define YASL_API __declspec(dllexport)
- #elif defined(YASL_SHARED)
- #define YASL_API __declspec(dllimport)
- #else
- #define YASL_API
- #endif
-#elif defined(__GNUC__) && __GNUC__ >= 4
- #define YASL_API __attribute__((visibility("default")))
-#else
- #define YASL_API
-#endif
-
-struct yasl_display;
-struct yasl_surface;
-struct yasl_loop;
-
-/* Geometry */
-/** A two-dimensional integer size, position, or offset; units depend on the caller. */
-struct yasl_vector2i {
- /** Horizontal coordinate or width. */
- int x;
- /** Vertical coordinate or height. */
- int y;
-};
-
-/** Two-dimensional fractional offset; units depend on the containing structure. */
-struct yasl_vector2d {
- /** Horizontal component. */
- double x;
- /** Vertical component. */
- double y;
-};
-
-/** Integer rectangle; origin is upper left and extents must be nonnegative. */
-struct yasl_rect2i {
- /** Left coordinate. */
- int x;
- /** Top coordinate. */
- int y;
- /** Nonnegative horizontal extent. */
- int width;
- /** Nonnegative vertical extent. */
- int height;
-};
-
-/** Scale numerator representing 1x. */
-#define YASL_SCALE_UNIT 120
-/** Backend minimum logical dimension used when constraining toplevel sizes. */
-#define YASL_MIN_LOGICAL_PX 64
-
-/* Nearest integer, ties away from zero. Zero scale returns zero; finite
- * out-of-range results saturate. Non-finite logical coordinates return zero. */
-/** Convert a logical coordinate to device pixels, rounding ties away from zero.
- * @param logical Logical coordinate; non-finite values return zero.
- * @param scale_120 Scale numerator; zero returns zero.
- * @return Rounded device coordinate, saturated to the range of int.
- */
-static inline int yasl_scale_to_device(double logical, uint32_t scale_120)
-{
- if (!scale_120 || !(logical >= -DBL_MAX && logical <= DBL_MAX)) return 0;
- double px = (scale_120 == YASL_SCALE_UNIT) ? logical : (logical * ((double)scale_120 / YASL_SCALE_UNIT));
- if (px >= INT_MAX) return INT_MAX;
- if (px <= INT_MIN) return INT_MIN;
- return (int)(px + copysign(0.5, px));
-}
-
-/** Convert a device coordinate to logical pixels, rounding ties away from zero.
- * @param device Device coordinate.
- * @param scale_120 Scale numerator; zero returns zero.
- * @return Rounded logical coordinate, saturated to the range of int.
- */
-static inline int yasl_scale_to_logical(int device, uint32_t scale_120)
-{
- if (!scale_120) return 0;
- if (scale_120 == YASL_SCALE_UNIT) return device;
- int64_t px = (int64_t)device * YASL_SCALE_UNIT;
- int64_t half = (int64_t)(scale_120 / 2);
- int64_t bias = (px < 0) ? -half : half;
- px = (px + bias) / scale_120;
- if (px > INT_MAX) return INT_MAX;
- if (px < INT_MIN) return INT_MIN;
- return (int)px;
-}
-
-/* Capabilities */
-/** Optional backend/server features, returned as a bitmask by yasl_display_caps(). */
-enum yasl_display_cap : uint32_t {
- YASL_CAP_POPUPS = 1 << 0, /**< Popup creation and placement. */
- YASL_CAP_DRAG = 1 << 1, /**< Surface-drag source and target support. */
- YASL_CAP_DRAG_CARRY = 1 << 2, /**< Carry a toplevel during a drag. */
- YASL_CAP_PLACE_WINDOW = 1 << 3, /**< Place a window relative to the pointer. */
- YASL_CAP_PRIMARY_SELECTION = 1 << 4, /**< Separate primary selection. */
- YASL_CAP_CSD = 1 << 5, /**< Backend advertises client decoration support; check each action result. */
- YASL_CAP_NATIVE_GRAPHICS = 1 << 6, /**< Application-managed native rendering. */
- YASL_CAP_FRAME_PACING = 1 << 7, /**< Compositor/display-driven draws. */
- /* Backend/protocol supports tablet axes, independent of attached hardware. */
- YASL_CAP_TABLET = 1 << 8, /**< Tablet protocol support, independent of attached hardware. */
-};
-
-/** Independent selections. An unsupported selection never aliases the clipboard. */
-enum yasl_selection : uint8_t {
- YASL_CLIPBOARD, /**< Explicit copy/paste selection. */
- YASL_PRIMARY, /**< Independent primary selection, if supported. */
-};
-
-/** Reported surface state flags; absent flags may also mean unsupported state. */
-enum yasl_surface_state : uint32_t {
- YASL_STATE_FULLSCREEN = 1 << 0, /**< Fullscreen. */
- YASL_STATE_MAXIMIZED = 1 << 1, /**< Maximized. */
- YASL_STATE_ACTIVATED = 1 << 2, /**< Active window. */
- YASL_STATE_SUSPENDED = 1 << 3, /**< Compositor reports suspension. */
- YASL_STATE_CSD = 1 << 4, /**< Backend reports client-side decorations. */
- YASL_STATE_TILED = 1 << 5, /**< At least one edge is tiled; currently reported by Wayland. */
-};
-
-/** System cursor shapes. HIDDEN preserves pointer input; NONE is its compatibility alias. */
-enum yasl_cursor {
- YASL_CURSOR_ARROW, /**< Default arrow. */
- YASL_CURSOR_TEXT, /**< Text insertion. */
- YASL_CURSOR_LINK, /**< Link/pointer hand. */
- YASL_CURSOR_CROSSHAIR, /**< Crosshair. */
- YASL_CURSOR_MOVE, /**< Move in any direction. */
- YASL_CURSOR_RESIZE_NWSE, /**< Northwest/southeast resize. */
- YASL_CURSOR_RESIZE_NESW, /**< Northeast/southwest resize. */
- YASL_CURSOR_RESIZE_EW, /**< Horizontal resize. */
- YASL_CURSOR_RESIZE_NS, /**< Vertical resize. */
- YASL_CURSOR_HIDDEN, /**< Hidden system cursor; pointer input continues. */
- YASL_CURSOR_NONE = YASL_CURSOR_HIDDEN, /**< Compatibility alias for HIDDEN. */
-};
-
-/** Valid interactive resize edges; combinations are restricted to the named values. */
-enum yasl_resize_edge : uint32_t {
- YASL_EDGE_NONE = 0, /**< No resize edge; rejected by interactive resize. */
- YASL_EDGE_TOP = 1, /**< Top edge. */
- YASL_EDGE_BOTTOM = 2, /**< Bottom edge. */
- YASL_EDGE_LEFT = 4, /**< Left edge. */
- YASL_EDGE_TOP_LEFT = 5, /**< Top-left corner. */
- YASL_EDGE_BOTTOM_LEFT = 6, /**< Bottom-left corner. */
- YASL_EDGE_RIGHT = 8, /**< Right edge. */
- YASL_EDGE_TOP_RIGHT = 9, /**< Top-right corner. */
- YASL_EDGE_BOTTOM_RIGHT = 10, /**< Bottom-right corner. */
-};
-
-/* Damage & Framebuffer
- * CPU pixels are tightly packed premultiplied ARGB8888 (0xAARRGGBB).
- * Size, pointer positions, IME caret and damage use device pixels; surface
- * toplevel creation sizes use logical pixels. Damage is a hint: backends may upload
- * the whole frame. Every pixel must contain valid content on each draw.
- */
-/** Maximum number of damage rectangles in one presentation. */
-#define YASL_DAMAGE_MAX 64
-
-/** Damage hints for one presentation; every target still needs complete valid pixels. */
-struct yasl_damage_list {
- /** -1: full target; 0: no damaged rectangles; otherwise number of rects. */
- int n;
- /** Reserved hint; callers may leave zero. Core presentation uses rects. */
- struct yasl_rect2i bounds;
- /** First n rectangles, in device pixels; ignored for n == -1. */
- struct yasl_rect2i rects[YASL_DAMAGE_MAX];
-};
-
-/** Borrowed draw target, valid only for the current draw callback. Never free pixels. */
-struct yasl_framebuffer {
- /** Opaque draw identity. Preserve it and all target fields when copying. */
- uint64_t generation;
- /** Writable premultiplied ARGB8888, row stride size.x * sizeof(uint32_t); may be NULL for native rendering. */
- uint32_t *pixels;
- /** Target extent in device pixels. */
- struct yasl_vector2i size;
- /** Row stride in bytes, populated for the active draw callback. */
- int stride_bytes;
-};
-
-/** One draw invocation. Do not retain its pointer after the callback returns. */
-struct yasl_frame {
- /** Surface being drawn. */
- struct yasl_surface *surface;
- /** Target to initialize and present before returning. */
- struct yasl_framebuffer target;
- /** Zero-based index of this surface draw. */
- uint64_t frame_index;
- /** Estimated animation time; not confirmed presentation. */
- uint64_t target_time_ns;
- /** Device scale numerator, with 120 representing 1x. */
- uint32_t scale_120;
-};
-
-/** Coalesced redraw policy, valid with either loop style. */
-typedef enum yasl_pacing_mode {
- YASL_PACING_ON_DEMAND = 0, /**< Draw only when invalidated or exposed. */
- YASL_PACING_CONTINUOUS, /**< Draw continuously using native pacing or a timer. */
-} yasl_pacing_mode;
-
-/* Drag and Drop */
-/** Target notifications for YASL surface drags, separate from external file drops. */
-enum yasl_drag_event {
- YASL_DRAG_OVER, /**< Surface drag entered or moved over the target. */
- YASL_DRAG_LEAVE, /**< Surface drag left the target. */
- YASL_DRAG_DROP, /**< Surface drag dropped on the target. */
-};
-
-/** External text/file drag hover notifications; completed data arrives through input. */
-enum yasl_drop_event {
- YASL_DROP_OVER, /**< External data drag entered or moved over the target. */
- YASL_DROP_LEAVE, /**< External data drag left the target. */
-};
-
-/** Source-side drag completion; DROPPED does not prove application-level acceptance. */
-enum yasl_drag_end {
- YASL_DRAG_DROPPED, /**< Drag ended in a drop; not an application acceptance receipt. */
- YASL_DRAG_CANCELLED, /**< Drag canceled, including display close. */
-};
-
-/** Copied drag icon in tightly packed premultiplied 0xAARRGGBB device pixels. */
-struct yasl_drag_image {
- /** Copied pixel array; required when an image is supplied. */
- const uint32_t *pixels;
- /** Positive width and height in device pixels. */
- struct yasl_vector2i size;
- /** Pointer position within the image, in device pixels. */
- struct yasl_vector2i hotspot;
-};
-
-/** Copied drag completion listener; userdata remains owned by the application. */
-struct yasl_drag_listener {
- /** Called exactly once after a successful start, possibly synchronously on cancel/close. */
- void (*end)(void *ud, enum yasl_drag_end how);
-};
-
-/** Configured device-pixel content size and the corresponding display scale. */
-struct yasl_geometry {
- /** Current content extent in device pixels. */
- struct yasl_vector2i size;
- /** Positive scale numerator; 120 means 1x and 180 means 1.5x. */
- uint32_t scale_120;
-};
-
-/* Input Model */
-/** Keyboard press, release, or autorepeat; releases must not insert text. */
-enum yasl_key_action {
- YASL_KEY_PRESS, /**< Key pressed. */
- YASL_KEY_RELEASE, /**< Key released. */
- YASL_KEY_REPEAT, /**< Key autorepeat. */
-};
-
-/** Modifier bitmask. SUPER is Command on macOS. */
-enum yasl_mod : uint32_t {
- YASL_MOD_SHIFT = 1 << 0, /**< Shift modifier. */
- YASL_MOD_ALT = 1 << 1, /**< Alt/Option modifier. */
- YASL_MOD_CTRL = 1 << 2, /**< Control modifier. */
- YASL_MOD_SUPER = 1 << 3, /**< Super/Command modifier. */
- YASL_MOD_CAPS = 1 << 4, /**< Caps Lock modifier. */
-};
-
-/** Pointer source class; capability support does not imply an attached device. */
-enum yasl_pointer_device {
- YASL_PTR_DEV_MOUSE, /**< Mouse or trackpad pointer. */
- YASL_PTR_DEV_TOUCH, /**< Touch translated into pointer input. */
- YASL_PTR_DEV_PEN, /**< Tablet stylus. */
- YASL_PTR_DEV_ERASER, /**< Tablet eraser tool. */
-};
-
-/** Pointer button; meaningful only for POINTER_DOWN and POINTER_UP. */
-enum yasl_pointer_button {
- YASL_BUTTON_PRIMARY = 0, /**< Left button, pen tip, or touch contact. */
- YASL_BUTTON_MIDDLE = 1, /**< Middle button / wheel click. */
- YASL_BUTTON_SECONDARY = 2, /**< Right button or pen barrel button. */
- YASL_BUTTON_BACK = 3, /**< Side "back" button. */
- YASL_BUTTON_FORWARD = 4, /**< Side "forward" button. */
-};
-
-/** Bit for a yasl_pointer_button in yasl_pointer_event.buttons. */
+typedef struct yasl_surface yasl_surface;
+typedef struct yasl_event yasl_event;
+enum yasl_backend { YASL_BACKEND_AUTO, YASL_BACKEND_WAYLAND, YASL_BACKEND_X11, YASL_BACKEND_COCOA };
+enum yasl_surface_flags { YASL_SURFACE_CPU=1u, YASL_SURFACE_HIDDEN=2u, YASL_SURFACE_UNDECORATED=4u, YASL_SURFACE_FIXED_SIZE=8u };
+enum yasl_error { YASL_ERROR_NONE, YASL_ERROR_INVALID, YASL_ERROR_UNSUPPORTED, YASL_ERROR_NO_MEMORY,
+ YASL_ERROR_NOT_INITIALIZED, YASL_ERROR_BUSY, YASL_ERROR_WRONG_THREAD, YASL_ERROR_CONTEXT_EXPIRED, YASL_ERROR_IO };
+enum yasl_selection { YASL_CLIPBOARD, YASL_PRIMARY };
+enum yasl_key_action { YASL_KEY_PRESS, YASL_KEY_RELEASE, YASL_KEY_REPEAT };
+enum yasl_mod { YASL_MOD_SHIFT=1u, YASL_MOD_ALT=2u, YASL_MOD_CTRL=4u, YASL_MOD_SUPER=8u, YASL_MOD_CAPS=16u };
+enum yasl_pointer_action { YASL_POINTER_MOVE, YASL_POINTER_DOWN, YASL_POINTER_UP, YASL_POINTER_LEAVE };
+enum yasl_pointer_mode { YASL_POINTER_NORMAL, YASL_POINTER_RELATIVE };
+enum yasl_button { YASL_BUTTON_PRIMARY, YASL_BUTTON_MIDDLE, YASL_BUTTON_SECONDARY, YASL_BUTTON_BACK, YASL_BUTTON_FORWARD };
#define YASL_BUTTON_BIT(button) (1u << (button))
-
-/** Pointer capture mode for a surface. */
-enum yasl_pointer_mode {
- YASL_POINTER_NORMAL = 0, /**< Absolute positions; cursor per yasl_surface_set_cursor(). */
- /** Cursor hidden and locked in place while the surface has focus. POINTER_MOTION
- * reports unbounded relative motion in delta; pos stays at the lock position.
- * The lock is released on focus loss and re-acquired on focus gain. */
- YASL_POINTER_RELATIVE,
-};
-
-/** Normalized tablet axes; zero for unavailable axes and non-tablet devices. */
-struct yasl_pointer_pen {
- /** 0..65535 normalized to device range. Zero alone does not end contact. */
- uint16_t pressure;
- /** Degrees in -90..90; positive right. */
- int8_t tilt_x;
- /** Degrees in -90..90; positive down. */
- int8_t tilt_y;
-};
-
-/** Event tag. Each tag lists the union member it selects. */
-enum yasl_event_type {
- YASL_EVENT_NONE = 0,
-
- /* Input: produced by backends, returned by yasl_surface_poll_event(). */
- YASL_EVENT_KEY, /**< .key */
- YASL_EVENT_TEXT, /**< .text — committed UTF-8; insert this, not key codepoints. */
- YASL_EVENT_POINTER_DOWN, /**< .pointer — button/tip pressed; button valid. */
- YASL_EVENT_POINTER_UP, /**< .pointer — button/tip released; button valid. */
- YASL_EVENT_POINTER_MOTION, /**< .pointer — position, relative motion, or pen axes changed. */
- YASL_EVENT_POINTER_SCROLL, /**< .pointer — scroll valid. */
- YASL_EVENT_POINTER_LEAVE, /**< .pointer — last known pos/device; cancel hover state. */
- YASL_EVENT_FOCUS, /**< .focus — keyboard focus changed; cancel held state on loss. */
- YASL_EVENT_PASTE, /**< .text — UTF-8 from yasl_display_paste(). */
- YASL_EVENT_DROP, /**< .text — dropped text/uri-list: CRLF-separated file URLs, not paths. */
- YASL_EVENT_INPUT_RESET, /**< No payload. Input stream interrupted; clear held keys, buttons, strokes, IME. */
-
- /* Window and application: part of the display FIFO, as well as runner delivery.
- * Legacy window callbacks also receive matching native notifications. */
- YASL_EVENT_WINDOW_RESIZE, /**< .resize — size and/or scale changed; reallocate and redraw. */
- YASL_EVENT_WINDOW_CLOSE, /**< No payload. User asked to close the window. */
- YASL_EVENT_USER, /**< .user — posted application message. */
- YASL_EVENT_WINDOW_STATE, /**< .state — reported state changed. */
- YASL_EVENT_DRAG, /**< .drag — surface-drag target notification. */
- YASL_EVENT_DROP_HOVER, /**< .drop — external drag hover/leave. */
- YASL_EVENT_COMPOSITION, /**< .composition — preedit snapshot, including ending. */
- YASL_EVENT_DISPLAY_QUIT, /**< No payload; system requested application quit. */
- YASL_EVENT_DISPLAY_REOPEN, /**< No payload; system requested activation. */
- YASL_EVENT_DISPLAY_LOST, /**< No payload; display connection failed. */
-};
-
-/** Physical keyboard transition with optional layout labels.
- * key is independent of modifiers/layout and stable between press and release.
- * codepoint and shifted are optional single Unicode scalars for the current
- * layout at levels zero/one, not text input. Zero means unavailable, a control
- * key, or a layout mapping that produces multiple characters. Insert committed
- * YASL_EVENT_TEXT into documents instead. */
-struct yasl_key_event {
- /** Portable physical key; YASL_KEY_UNKNOWN when unmapped. */
- enum yasl_key key;
- /** Press, release, or autorepeat. */
- enum yasl_key_action action;
- /** yasl_mod bitmask at this event. */
- uint32_t mods;
- /** Optional unshifted layout Unicode scalar; zero if unavailable. */
- uint32_t codepoint;
- /** Optional shifted layout Unicode scalar; zero if unavailable. */
- uint32_t shifted;
- /** Native code: Wayland evdev, X11 server keycode, Cocoa virtual keycode. */
- uint32_t native_code;
-};
-
-/** Pointer sample shared by all POINTER_* events. Pen and eraser share it with
- * mouse input. Track contact with DOWN/UP of the primary button, not pressure.
- * This is not a multi-contact touch API. */
-struct yasl_pointer_event {
- /** Surface-relative device-pixel position. Frozen while YASL_POINTER_RELATIVE is locked. */
- struct yasl_vector2i pos;
- /** MOTION: device-pixel movement since the previous sample on this surface.
- * Unbounded under YASL_POINTER_RELATIVE; zero after enter, leave, or reset. */
- struct yasl_vector2d delta;
- /** SCROLL: fractional line offsets after system preferences; positive right/down. */
- struct yasl_vector2d scroll;
- /** DOWN/UP: which button changed. */
- enum yasl_pointer_button button;
- /** YASL_BUTTON_BIT() mask of buttons held after this event, on every
- * POINTER_* type. Tracked from this queue's DOWN and UP events for all
- * devices; INPUT_RESET clears it. On Wayland LEAVE clears it too, since the
- * compositor sends leave during a press only when it takes the pointer.
- * X11 and Cocoa keep sending motion and the release after LEAVE. */
- uint32_t buttons;
- /** Mouse, touch, pen, or eraser. */
- enum yasl_pointer_device device;
- /** yasl_mod bitmask at this event. */
- uint32_t mods;
- /** Current tablet axes, including on pressure-only motion; missing axes are zero. */
- struct yasl_pointer_pen pen;
-};
-
-/** Borrowed NUL-terminated UTF-8, shared by TEXT, PASTE and DROP. */
-struct yasl_text_event {
- /** Borrowed NUL-terminated bytes; see yasl_surface_poll_event() for lifetime. */
- const char *data;
- /** Length in bytes excluding the terminating NUL. */
- size_t len;
-};
-
-/** Clipboard transfer outcome. */
-enum yasl_transfer_status {
- YASL_TRANSFER_OK = 0, /**< Text received successfully, including empty text. */
- YASL_TRANSFER_UNAVAILABLE, /**< No text selection is available. */
- YASL_TRANSFER_FAILED, /**< Transfer or decoding failed. */
-};
-
-/** Clipboard completion; data/len alias the legacy text payload. */
-struct yasl_paste_event {
- const char *data; /**< Borrowed NUL-terminated UTF-8; empty on failure. */
- size_t len; /**< Byte length excluding NUL. */
- uint64_t request_id; /**< Zero for a legacy paste request. */
- enum yasl_transfer_status status; /**< Completion outcome. */
-};
-
-/** Borrowed UTF-8 preedit snapshot, valid until the next successful display poll. */
-struct yasl_composition_event {
- const char *data; /**< Borrowed NUL-terminated UTF-8 snapshot. */
- size_t len; /**< Byte length excluding NUL. */
- int begin; /**< UTF-8 byte offset, or -1 if unavailable. */
- int end; /**< UTF-8 byte offset, or -1 if unavailable. */
- bool active; /**< Whether composition text is present. */
-};
-
-/** Reported window-state change. */
-struct yasl_state_event { uint32_t flags; /**< Current YASL_STATE_* flags. */ };
-/** Surface-drag target notification; position is in device pixels. */
-struct yasl_drag_target_event {
- enum yasl_drag_event action; /**< Hover, leave, or drop. */
- struct yasl_vector2i pos; /**< Surface-relative device pixels. */
-};
-/** External drag hover notification; position is in device pixels. */
-struct yasl_drop_hover_event {
- enum yasl_drop_event action; /**< Hover or leave. */
- struct yasl_vector2i pos; /**< Surface-relative device pixels. */
-};
-
-/** Keyboard focus transition. */
-struct yasl_focus_event {
- /** true on focus gain, false on loss. */
- bool focused;
-};
-
-/** New configured geometry for a surface. */
-struct yasl_resize_event {
- /** Content size in device pixels and display scale. */
- struct yasl_geometry geometry;
- /** true when scale_120 differs from the previous resize of this surface. */
- bool scale_changed;
-};
-
-/** Application message posted with yasl_post_event(). */
-struct yasl_user_event {
- /** Application-defined message code. */
- uint32_t code;
- /** Pointer passed through unchanged; ownership stays with the application. */
- void *payload;
- /** Optional payload size, passed through unchanged. */
- size_t size;
-};
-
-/** One tagged event; read only the union member selected by type. */
+enum yasl_pen_tool { YASL_PEN_UNKNOWN, YASL_PEN_PEN, YASL_PEN_ERASER, YASL_PEN_BRUSH, YASL_PEN_AIRBRUSH, YASL_PEN_PENCIL };
+enum yasl_pen_action { YASL_PEN_ENTER, YASL_PEN_LEAVE, YASL_PEN_MOVE, YASL_PEN_DOWN, YASL_PEN_UP, YASL_PEN_BUTTON, YASL_PEN_CANCEL };
+enum yasl_pen_axis { YASL_PEN_PRESSURE=1u, YASL_PEN_TILT=2u, YASL_PEN_ROTATION=4u, YASL_PEN_DISTANCE=8u, YASL_PEN_TANGENTIAL=16u, YASL_PEN_WHEEL=32u };
+enum yasl_scroll_valid { YASL_SCROLL_LINES=1u, YASL_SCROLL_PIXELS=2u };
+enum yasl_scroll_source { YASL_SCROLL_UNKNOWN, YASL_SCROLL_WHEEL, YASL_SCROLL_FINGER, YASL_SCROLL_CONTINUOUS };
+enum yasl_scroll_phase { YASL_SCROLL_PHASE_UNKNOWN, YASL_SCROLL_BEGIN, YASL_SCROLL_UPDATE, YASL_SCROLL_END };
+enum yasl_transfer_status { YASL_TRANSFER_OK, YASL_TRANSFER_UNAVAILABLE, YASL_TRANSFER_FAILED };
+enum yasl_text_purpose { YASL_TEXT_NORMAL, YASL_TEXT_PASSWORD, YASL_TEXT_NUMBER, YASL_TEXT_TERMINAL };
+enum yasl_cursor { YASL_CURSOR_ARROW, YASL_CURSOR_TEXT, YASL_CURSOR_LINK, YASL_CURSOR_CROSSHAIR, YASL_CURSOR_MOVE,
+ YASL_CURSOR_RESIZE_NWSE, YASL_CURSOR_RESIZE_NESW, YASL_CURSOR_RESIZE_EW, YASL_CURSOR_RESIZE_NS, YASL_CURSOR_HIDDEN };
+enum yasl_surface_state { YASL_STATE_FULLSCREEN=1u, YASL_STATE_MAXIMIZED=2u, YASL_STATE_ACTIVATED=4u,
+ YASL_STATE_SUSPENDED=8u, YASL_STATE_CSD=16u, YASL_STATE_TILED=32u };
+enum yasl_feature { YASL_CAP_POPUPS=1u, YASL_CAP_DRAG=2u, YASL_CAP_DRAG_CARRY=4u, YASL_CAP_PLACE_WINDOW=8u,
+ YASL_CAP_PRIMARY_SELECTION=16u, YASL_CAP_CSD=32u, YASL_CAP_NATIVE_GRAPHICS=64u, YASL_CAP_FRAME_PACING=128u,
+ YASL_CAP_TABLET=256u, YASL_CAP_OPENGL=512u, YASL_CAP_VULKAN=1024u, YASL_CAP_TEXT_INPUT=2048u };
+enum yasl_popup_edge { YASL_POPUP_CENTER, YASL_POPUP_TOP, YASL_POPUP_BOTTOM, YASL_POPUP_LEFT,
+ YASL_POPUP_RIGHT, YASL_POPUP_TOP_LEFT, YASL_POPUP_BOTTOM_LEFT, YASL_POPUP_TOP_RIGHT, YASL_POPUP_BOTTOM_RIGHT };
+enum yasl_popup_constraint { YASL_POPUP_SLIDE_X=1u, YASL_POPUP_SLIDE_Y=2u, YASL_POPUP_FLIP_X=4u,
+ YASL_POPUP_FLIP_Y=8u, YASL_POPUP_RESIZE_X=16u, YASL_POPUP_RESIZE_Y=32u };
+enum yasl_resize_edge { YASL_EDGE_TOP=1, YASL_EDGE_BOTTOM=2, YASL_EDGE_LEFT=4, YASL_EDGE_TOP_LEFT=5,
+ YASL_EDGE_BOTTOM_LEFT=6, YASL_EDGE_RIGHT=8, YASL_EDGE_TOP_RIGHT=9, YASL_EDGE_BOTTOM_RIGHT=10 };
+enum yasl_event_type { YASL_EVENT_NONE, YASL_EVENT_CLOSE, YASL_EVENT_QUIT, YASL_EVENT_REOPEN, YASL_EVENT_WINDOW,
+ YASL_EVENT_REFRESH, YASL_EVENT_KEY, YASL_EVENT_TEXT, YASL_EVENT_POINTER, YASL_EVENT_SCROLL, YASL_EVENT_PEN,
+ YASL_EVENT_INPUT_RESET, YASL_EVENT_COMPOSITION, YASL_EVENT_TEXT_EDIT, YASL_EVENT_CLIPBOARD, YASL_EVENT_DROP,
+ YASL_EVENT_DRAG, YASL_EVENT_DISPLAY_LOST };
+enum yasl_drop_action { YASL_DROP_OVER, YASL_DROP_LEAVE, YASL_DROP_DATA };
+enum yasl_drag_action { YASL_DRAG_OVER, YASL_DRAG_LEAVE, YASL_DRAG_DROP };
+/** Read the union member named by type. Strings stay valid until the next wait.
+ * CLOSE: the user closed this window. QUIT: the system asked the app to quit.
+ * WINDOW: size, scale or state changed. REFRESH: draw now; sent once the window
+ * has a size, after every resize and when the system needs the contents again.
+ * INPUT_RESET: forget held keys, buttons and strokes. DROP: external data over
+ * the window, delivered as DATA. DRAG: another YASL surface dragged over. */
struct yasl_event {
- /** Selects the valid union member. */
enum yasl_event_type type;
- /** Recipient surface, or NULL for display-wide events. Can differ from the
- * polled surface for routed popup input. */
- struct yasl_surface *surface;
- /** Monotonic timestamp in nanoseconds, same clock as yasl_loop_now_ns(). */
+ yasl_surface *surface;
uint64_t time_ns;
- /** Increasing display-wide enqueue order; zero for standalone private queues. */
- uint64_t sequence;
- /** Stable recipient identity; zero for display-wide events. */
- uint64_t surface_id;
- union {
- struct yasl_key_event key; /**< KEY */
- struct yasl_pointer_event pointer; /**< POINTER_* */
- struct yasl_text_event text; /**< TEXT, legacy PASTE, DROP */
- struct yasl_paste_event paste; /**< PASTE */
- struct yasl_composition_event composition; /**< COMPOSITION */
- struct yasl_state_event state; /**< WINDOW_STATE */
- struct yasl_drag_target_event drag; /**< DRAG */
- struct yasl_drop_hover_event drop; /**< DROP_HOVER */
- struct yasl_focus_event focus; /**< FOCUS */
- struct yasl_resize_event resize; /**< WINDOW_RESIZE */
- struct yasl_user_event user; /**< USER */
- };
-};
-
-/* Ownership and threading
- * All calls and callbacks run on the display's opening thread (main thread on
- * Cocoa). Displays borrow supplied loops; destroy surfaces before displays,
- * popups before parents, and displays before their supplied loop.
- * Specs, strings, callback tables and drag listeners are copied during calls.
- * Userdata is borrowed until callbacks are removed or the object/drag ends.
- * Surface destruction is allowed from its callbacks; display/loop destruction
- * during dispatch or callbacks is not supported.
- */
-
-/* Callbacks */
-/** Optional display notifications. NULL callback members are ignored. */
-struct yasl_display_cbs {
- /** Application quit request; arrange orderly shutdown after dispatch returns. */
- void (*quit)(void *ud);
- /** Application reopen/activation request, currently Cocoa. */
- void (*reopen)(void *ud);
- /** Display connection lost; arrange cleanup after dispatch returns. */
- void (*lost)(void *ud);
-};
-
-/** Optional window/popup notifications, copied from yasl_window_spec.cbs or by
-yasl_surface_set_callbacks().
-Callbacks may destroy their surface. They must then return without further use
-of the surface or draw target. NULL members are ignored. */
-struct yasl_window_cbs {
- /** Initialize the entire target and optionally present. Requested through schedule_redraw/flush. */
- void (*draw)(struct yasl_surface *s, struct yasl_frame *frame, void *ud);
- /** Configured size/scale changed; reallocate application content and request a redraw. */
- void (*configure)(struct yasl_surface *s, struct yasl_geometry geometry, void *ud);
- /** Window/popup close request; application is responsible for destruction. */
- void (*close)(struct yasl_surface *s, void *ud);
- /** Reported yasl_surface_state bitmask changed; matches yasl_surface_state(s). */
- void (*state)(struct yasl_surface *s, uint32_t state, void *ud);
- /** YASL surface-drag target hover, leave, or drop; pos is in device pixels. */
- void (*drag)(struct yasl_surface *s, enum yasl_drag_event ev,
- struct yasl_vector2i pos, void *ud);
- /** External data-drag hover or leave; pos is in device pixels. */
- void (*drop)(struct yasl_surface *s, enum yasl_drop_event ev,
- struct yasl_vector2i pos, void *ud);
-};
-
-/* Display Lifecycle */
-/** Runtime backend choice. AUTO selects a native backend and allows fallback. */
-enum yasl_backend {
- YASL_BACKEND_AUTO = 0, /**< Choose native backend with fallback. */
- YASL_BACKEND_WAYLAND, /**< Require Wayland. */
- YASL_BACKEND_X11, /**< Require X11. */
- YASL_BACKEND_COCOA, /**< Require Cocoa. */
-};
-
-/** Display options. Zero-initialize for automatic backend selection. */
-struct yasl_display_spec {
- /** Copied application identifier, conventionally a reverse-domain UTF-8 string; NULL uses backend defaults. */
- const char *app_id;
- /** Copied optional display callbacks; NULL disables them. */
- const struct yasl_display_cbs *cbs;
- /** Borrowed display callback userdata. */
- void *ud;
-
- /** Zero/AUTO chooses native backend; explicit choices never fall back. */
- enum yasl_backend backend;
-};
-
-/* Return NULL and set errno if the requested backend cannot open. */
-/**
- * Open a desktop connection and its event machinery.
- * @param loop Borrowed loop, or NULL to create a display-owned loop.
- * @param spec Copied options, or NULL for automatic backend selection and defaults.
- * @return An owned display, or NULL with errno set. EINVAL means an invalid backend;
- * ENOSYS means the requested backend was not built; ENODEV is the fallback when
- * opening fails without a more specific platform error. Explicit choices never
- * fall back. Close with yasl_display_close() after destroying its surfaces.
- */
-YASL_API struct yasl_display *yasl_display_open(struct yasl_loop *loop,
- const struct yasl_display_spec *spec);
-/**
- * Read the selected backend.
- * @param d Display, or NULL.
- * @return The actual backend, or YASL_BACKEND_AUTO for NULL.
- */
-YASL_API enum yasl_backend yasl_display_backend(const struct yasl_display *d);
-/**
- * Close a display and release its owned loop, if any.
- * @param d Display to close, or NULL for no action.
- * Destroy all surfaces first. Cancels an active drag and may invoke its end
- * listener. Must not be called during dispatch or from a callback.
- */
-YASL_API void yasl_display_close(struct yasl_display *d);
-/**
- * Query currently available optional features.
- * @param d Display, or NULL.
- * @return A bitmask of yasl_display_cap values, or zero for NULL.
- * Capabilities describe backend/server support, not attached input hardware or
- * permission to perform an operation at any arbitrary moment.
- */
-YASL_API uint32_t yasl_display_caps(const struct yasl_display *d);
-/* Process events, waiting at most timeout_ms: zero polls, negative waits.
- * Pending input or redraw work returns immediately. Returns -1 on failure. */
-/**
- * Receive events and run ready loop sources.
- * @param d Display to dispatch.
- * @param timeout_ms Maximum wait in milliseconds: zero polls, negative waits indefinitely.
- * @return A nonnegative backend-specific work count on success, or -1 with errno
- * set on failure (EINVAL for NULL, EIO if no specific backend error is available).
- * Do not interpret the count as the number of queued input events. Pending input
- * or runnable redraw work prevents waiting. Drain input and call
- * yasl_display_flush() after updating application state. Do not dispatch recursively.
- */
-YASL_API int yasl_display_dispatch(struct yasl_display *d, int timeout_ms);
-/**
- * Draw scheduled surfaces and send pending display requests.
- * @param d Display, or NULL for no action.
- * May synchronously invoke draw and other callbacks. A paced surface may wait
- * for its next display/compositor signal. This is not a GPU completion wait.
- */
-YASL_API void yasl_display_flush(struct yasl_display *d);
-/**
- * Read the latest modifier state known to the backend.
- * @param d Display, or NULL.
- * @return A bitmask of yasl_mod values, or zero for NULL.
- * For an individual event use its own mods field instead.
- */
-YASL_API int yasl_display_modifiers(const struct yasl_display *d);
-/** Double-click interval used when the system reports none. */
-#define YASL_DOUBLE_CLICK_MS_DEFAULT 400
-/**
- * Read the system's double-click interval.
- * @param d Display, or NULL.
- * @return Maximum milliseconds between the clicks of a double click, or
- * YASL_DOUBLE_CLICK_MS_DEFAULT when d is NULL or the system reports none.
- * X11 reads the XSETTINGS Net/DoubleClickTime value. Wayland, and X11 without
- * XSETTINGS, read the KDE or GNOME setting through the XDG Settings portal,
- * which needs D-Bus and can wait briefly. Cocoa reads the system interval.
- * The user can change it at any time; read it again when a window gains focus
- * rather than on every click.
- */
-YASL_API int yasl_display_double_click_ms(const struct yasl_display *d);
-/* Unsupported/invalid selections are no-ops, never clipboard aliases. */
-/**
- * Offer a copy of a NUL-terminated UTF-8 string as a selection.
- * @param d Display, or NULL for no action.
- * @param sel YASL_CLIPBOARD or YASL_PRIMARY.
- * @param text Text copied during this call; NULL does nothing.
- * Unsupported selections do nothing. The void return does not confirm that the
- * system accepted the offer; Wayland may require a recent input serial.
- */
-YASL_API void yasl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text);
-/**
- * Request text delivery to a particular surface.
- * @param d Display that owns target.
- * @param sel YASL_CLIPBOARD or YASL_PRIMARY.
- * @param target Destination surface; NULL or a surface on another display does nothing.
- * Results arrive as YASL_EVENT_PASTE, possibly during a later dispatch. No result
- * is promised for unavailable data or a failed transfer. Destroying target cancels
- * delivery. Unsupported selections do nothing.
- */
-YASL_API void yasl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target);
-
-/* A successful drag invokes the copied listener's non-NULL end once, including
- * cancel and display close. Failed starts do not invoke it. */
-/**
- * Start a surface drag from a recent pointer press.
- * @param d Display that owns origin.
- * @param origin Origin surface.
- * @param image Optional copied premultiplied ARGB8888 drag image, or NULL.
- * @param l Optional copied completion listener, or NULL.
- * @param ud Borrowed listener userdata until completion.
- * @return true if started; false if unsupported, invalid, or not currently permitted.
- * A successful start invokes a non-NULL end callback exactly once, including
- * cancellation or display close. Failed starts do not invoke it. This API carries
- * surfaces, not arbitrary MIME payloads; use application state to identify the item.
- */
-YASL_API 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);
-/**
- * Cancel the active surface drag, if any.
- * @param d Display, or NULL for no action.
- * May invoke the end listener synchronously.
- */
-YASL_API void yasl_display_drag_cancel(struct yasl_display *d);
-
-/* Surface Lifecycle */
-/** Toplevel options. Zero-initialize, then supply a positive logical-pixel size. */
-struct yasl_window_spec {
- /** Copied UTF-8 title; NULL uses backend defaults. */
- const char *title;
- /** Requested logical-pixel width and height; both must be positive. */
union {
- struct yasl_vector2i size_logical; /**< Requested logical-pixel extent. */
- struct yasl_vector2i size; /**< Migration alias for size_logical. */
+ struct { int width,height,pixel_width,pixel_height; double scale; unsigned state; } window;
+ struct { enum yasl_key key; enum yasl_key_action action; unsigned mods;
+ uint32_t codepoint, shifted, native_code; } key;
+ struct { const char *data; size_t length; } text;
+ struct { double x,y,dx,dy; enum yasl_pointer_action action; unsigned button,buttons,mods; } pointer;
+ struct { double x,y,lines_x,lines_y,pixels_x,pixels_y; unsigned valid,source,phase,mods; bool momentum; } scroll;
+ struct { uint64_t device_id,tool_id; double x,y,pressure,tilt_x,tilt_y,rotation,distance,tangential,wheel_degrees;
+ unsigned axes_valid,axes_changed,buttons,changed_button,tool,action; bool in_contact; } pen;
+ struct { const char *data; size_t length; int begin,end; bool active; uint64_t revision,generation; } composition;
+ struct { const char *data; size_t length,begin,end; uint64_t revision,generation; } text_edit;
+ struct { const char *data; size_t length; uint64_t request_id; enum yasl_transfer_status status; } clipboard;
+ /** DATA carries a text/uri-list for yasl_drop_next_path; x/y are set for OVER. */
+ struct { enum yasl_drop_action action; double x,y; const char *data; size_t length; } drop;
+ struct { enum yasl_drag_action action; double x,y; } drag;
};
- /** Request initial fullscreen; actual state is reported asynchronously. */
- bool fullscreen;
- /** Permit unpaced presentation where supported; a hint, not a guarantee. */
- bool tearing;
- /** Copied optional window callbacks; NULL disables them. */
- const struct yasl_window_cbs *cbs;
- /** Borrowed window callback userdata; ignored by the runner. */
- void *ud;
- /** Borrowed application data, independent of callbacks and available in either loop style. */
- void *userdata;
};
-
-/** Popup options, entirely in device pixels, including size.
-The content rectangle excludes transparent margins/shadows used for placement.
-An axis of content_size equal to zero extends to that axis of size. The offset
-and resolved content size must fit in size. Anchor extents must be nonnegative.
-Dimensions must be positive, at most 32767, and fit an INT32_MAX-byte buffer;
-backends may impose further limits after scaling or resolving placement. */
-struct yasl_popup_spec {
- /** Full popup framebuffer extent in device pixels, including transparent margins. */
- struct yasl_vector2i size;
- /** Rectangle in parent framebuffer coordinates, in device pixels. */
- struct yasl_rect2i anchor;
- /** true prefers right of anchor; false prefers below. Placement may flip/slide. */
- bool beside;
- /** Top-left content offset within the popup, in device pixels. */
- struct yasl_vector2i content_offset;
- /** Content extent; zero per axis extends to the corresponding size edge. */
- struct yasl_vector2i content_size;
-};
-
-/**
- * Create a resizable desktop window with CPU drawing targets.
- * @param d Owning display.
- * @param spec Required copied options; size is in logical pixels.
- * @return An owned surface, or NULL with errno set. EINVAL means invalid options,
- * including a nonpositive dimension, a dimension above 32767, or a buffer above
- * INT32_MAX bytes. ENODEV is the fallback backend failure. Actual configured
- * size may differ from the request. The callback table in spec->cbs is copied;
- * spec->ud stays borrowed until callbacks are replaced or the surface is destroyed.
- * This window's callbacks cannot run before creation returns. Use
- * yasl_surface_geometry() for initial geometry; an initial configure callback
- * is not guaranteed. Schedule an initial redraw after setting up application state.
- * On a display owned by yasl_run(), the runner installs its own callbacks instead;
- * use yasl_app_desc.event and yasl_app_desc.frame for application notifications.
- */
-YASL_API struct yasl_surface *yasl_surface_create_toplevel(struct yasl_display *d,
- const struct yasl_window_spec *spec);
-/**
- * Create an owned popup attached to a parent surface.
- * @param parent Borrowed parent; destroy the popup before the parent.
- * @param spec Required copied options, entirely in device pixels.
- * @return A popup, or NULL with errno set: EINVAL for invalid options, ENOTSUP
- * for missing popup support, or a backend error (ENODEV if none was supplied).
- * Wayland requires a recent input serial for its popup grab. The backend may
- * flip or slide placement to fit the screen. See yasl_popup_spec.
- */
-YASL_API struct yasl_surface *yasl_surface_create_popup(struct yasl_surface *parent,
- const struct yasl_popup_spec *spec);
-/**
- * Update a popup's geometry and placement.
- * @param popup An existing popup.
- * @param spec Required copied replacement options, in device pixels.
- * @return true on success; false with errno set: EINVAL for invalid options,
- * ENOTSUP for missing popup support, or a backend error (ENODEV if unspecified).
- * Older Wayland popup protocol versions can reject repositioning.
- */
-YASL_API bool yasl_surface_popup_reposition(struct yasl_surface *popup,
- const struct yasl_popup_spec *spec);
-/**
- * Destroy a surface and cancel its surface timers and pending input.
- * @param s Surface, or NULL for no action.
- * Destroy child popups first. May be called from a surface callback; return from
- * that callback without further use of the surface or its draw target. Every
- * pointer or native handle obtained from this surface becomes invalid.
- */
-YASL_API void yasl_surface_destroy(struct yasl_surface *s);
-/**
- * Get the display that owns a surface.
- * @param s Surface, or NULL.
- * @return A borrowed display, or NULL.
- */
-YASL_API struct yasl_display *yasl_surface_display(const struct yasl_surface *s);
-
-/**
- * Replace the surface's callback table and userdata.
- * @param s Surface.
- * @param cbs Table copied by value; NULL removes all callbacks. NULL members are ignored.
- * @param ud Borrowed userdata passed to the installed callbacks.
- * @return true if replaced; false with EINVAL for NULL, EBUSY on a runner-owned surface.
- * This call only replaces callbacks; it does not invoke them.
- * Use yasl_surface_geometry() to read current geometry; do not rely on receiving
- * an initial configure callback when registering.
- */
-YASL_API bool yasl_surface_set_callbacks(struct yasl_surface *s,
- const struct yasl_window_cbs *cbs, void *ud);
-/**
- * Replace the window title with copied UTF-8 text.
- * @param s Surface.
- * @param title NUL-terminated UTF-8 title. Pass an empty string to clear it.
- * Backends may truncate the title.
- */
-YASL_API void yasl_surface_set_title(struct yasl_surface *s, const char *title);
-/**
- * Request a future draw callback, coalescing repeated requests.
- * @param s Surface, or NULL for no action.
- * Call yasl_display_flush() to service the request. Schedule again from draw to
- * animate. Without YASL_CAP_FRAME_PACING, throttle animation with a timer.
- */
-YASL_API void yasl_surface_schedule_redraw(struct yasl_surface *s);
-/* Present exactly the target supplied to the current draw callback, at most
- * once. Targets expire when that callback returns or the surface is destroyed.
- * damage is required: n must be -1 or 0..YASL_DAMAGE_MAX. Rectangles must have
- * nonnegative extents; they are clipped to the target. Invalid input returns
- * false without consuming the frame. See yasl/native.h for native rendering. */
-/**
- * Submit the current draw target at most once.
- * @param s Surface currently being drawn.
- * @param fb Exact target from the current draw callback, including its generation.
- * @param damage Required damage hint: n is -1 for all pixels or 0..YASL_DAMAGE_MAX.
- * @return true if accepted; false for invalid, stale, already presented, or failed targets.
- * Rectangles must have nonnegative extents and are clipped to the target. Invalid
- * input does not consume the frame. All pixels must be initialized even with
- * partial damage. The backend may upload all pixels. Success records submission,
- * not confirmed scanout. The target expires when draw returns or s is destroyed.
- */
-YASL_API bool yasl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer *fb,
- const struct yasl_damage_list *damage);
-/**
- * Migration adapter: consume one input event for a surface from the display FIFO.
- * Popup input can belong to the root queue; inspect ev->surface. Returns false
- * for empty/invalid arguments, leaving ev unchanged. Strings remain borrowed
- * until the next successful display or surface poll, or display close. Pending
- * recipient events are canceled on destruction. Prefer yasl_display_poll_event()
- * to receive window, composition, and display events as well as input. This
- * input-only adapter discards pending non-input events across the display
- * (legacy callbacks already delivered lifecycle changes). Do not mix
- * the two polling styles in one application. Use the display thread.
- */
-YASL_API bool yasl_surface_poll_event(struct yasl_surface *s, struct yasl_event *ev);
-/**
- * Decode the next local file path from a YASL_EVENT_DROP payload.
- * @param cursor Required in/out position in the NUL-terminated list. Start it
- * at event.text.data; a NULL *cursor returns zero.
- * @param out Output for the NUL-terminated path, or NULL when cap is zero.
- * @param cap Size of out in bytes.
- * @return Path length in bytes, excluding the NUL, or zero when no paths remain.
- * When the result is less than cap, out holds the path and *cursor moves past
- * it. Otherwise out and *cursor are unchanged; call again with a buffer of at
- * least the result plus one.
- * Accepts file:///path, file:/path, and file://host/path when host is
- * localhost or this machine's name. Percent escapes decode to raw bytes, and
- * a '?' or '#' ends the path. Skips blank lines, comments, other schemes,
- * other hosts, and lines with a malformed escape or an escaped NUL. May be
- * called from any thread.
- */
-YASL_API size_t yasl_drop_next_path(const char **cursor, char *out, size_t cap);
-/**
- * Copy the last frame presented from the surface's CPU target.
- * @param s Surface.
- * @param out Output for size->x * size->y premultiplied ARGB8888 pixels with
- * tightly packed rows, or NULL when cap is zero.
- * @param cap Capacity of out in pixels.
- * @param size Required output: the frame extent in device pixels, or zero when
- * no frame is available.
- * @return true if copied. false leaves out unchanged; when *size is nonzero,
- * out was too small, so allocate size->x * size->y pixels and call again.
- * A frame is available from a successful yasl_surface_present() until the
- * next draw callback starts, and again once that draw presents. After a draw
- * that does not present or a resize that replaces the target, no frame may be
- * available; native rendering never has one. Call outside draw callbacks. This copies YASL's frame, not
- * the composited screen: decorations, other windows, and color conversion are
- * not included.
- */
-YASL_API bool yasl_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
- struct yasl_vector2i *size);
-/**
- * Select absolute or relative (locked) pointer input for a surface.
- * @param s Surface.
- * @param mode YASL_POINTER_NORMAL or YASL_POINTER_RELATIVE.
- * @return true if the mode is supported; false leaves the current mode.
- * RELATIVE hides the cursor and locks it while the surface is focused;
- * POINTER_MOTION then carries unbounded delta. Returning to NORMAL restores
- * the cursor selected with yasl_surface_set_cursor(). RELATIVE is supported on
- * Wayland compositors with the relative-pointer and pointer-constraints
- * protocols; X11 and Cocoa return false.
- */
-YASL_API bool yasl_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_pointer_mode mode);
-
-/**
- * Consume one event from the display-wide FIFO without waiting.
- * Returns false for an empty queue or invalid arguments, leaving ev unchanged.
- * Strings remain valid until the next successful poll or display close.
- * Surface destruction cancels undelivered events, not the last polled payload.
- * Use the display thread. Do not mix display and surface polling in one app.
- */
-YASL_API bool yasl_display_poll_event(struct yasl_display *d, struct yasl_event *ev);
-
-/** Stable nonzero identity within a display; zero for NULL. Use on the display thread. */
-YASL_API uint64_t yasl_surface_id(const struct yasl_surface *s);
-/** Store borrowed application data independently of callback userdata. */
-YASL_API void yasl_surface_set_userdata(struct yasl_surface *s, void *userdata);
-/** Read borrowed application data, or NULL for NULL; use on the display thread. */
-YASL_API void *yasl_surface_userdata(const struct yasl_surface *s);
-
-/** Thread-safe message descriptor. Zero target_id means display-wide. */
-struct yasl_message {
- uint64_t target_id; /**< Surface identity, or zero for the display. */
- uint32_t code; /**< Application-defined message code. */
- void *payload; /**< Borrowed until release; passed through unchanged. */
- size_t size; /**< Application-defined payload length. */
- /** Optional cleanup on display thread after delivery/cancellation, exactly once.
- * May post messages, but must not dispatch, poll, or destroy YASL objects. */
- void (*release)(void *payload);
-};
-/**
- * Copy and enqueue a message from any thread; the display must stay alive.
- * Stop producer threads before closing the display. On success release runs
- * once after consumption or cancellation. On failure ownership stays with the
- * caller; errno is EINVAL, ENOENT (retired identity), ENOMEM, or EOVERFLOW.
- */
-YASL_API bool yasl_display_post(struct yasl_display *d, const struct yasl_message *message);
-
-/**
- * Present the active frame once. NULL/0 means full damage; non-NULL/0 means none.
- * Validate all rectangles; excess rectangles fall back to full damage.
- * Returns false with EINVAL for invalid/stale targets, EALREADY for a consumed
- * frame, or errno from the backend (EIO if unspecified).
- * Frame and pixels expire at callback return; use the display thread.
- */
-YASL_API bool yasl_frame_present(struct yasl_frame *frame,
- const struct yasl_rect2i *rects, size_t count);
-/** Coalesce a redraw request; use on the display thread. NULL is ignored. */
-YASL_API void yasl_surface_request_frame(struct yasl_surface *s);
-/** Set drawing policy for either loop style. Returns false with errno on failure. */
-YASL_API bool yasl_surface_set_pacing(struct yasl_surface *s, yasl_pacing_mode mode);
-
-/** Request a positive logical-pixel size. Success means issued, not configured. */
-YASL_API bool yasl_surface_set_size(struct yasl_surface *s, struct yasl_vector2i size_logical);
-/** Request fullscreen state; returns acceptance, not confirmation. */
-YASL_API bool yasl_surface_set_fullscreen(struct yasl_surface *s, bool enabled);
-/** Request maximized state; returns acceptance, not confirmation. */
-YASL_API bool yasl_surface_set_maximized(struct yasl_surface *s, bool enabled);
-/** Request minimization. Window requests set errno on failure; display thread only. */
-YASL_API bool yasl_surface_minimize(struct yasl_surface *s);
-
-/** Offer copied UTF-8 text. Returns false with errno if invalid/unsupported/failed. */
-YASL_API bool yasl_clipboard_set(struct yasl_display *d, enum yasl_selection selection,
- const char *text);
-/**
- * Request text; returns a nonzero ID or zero with errno. One PASTE completion
- * follows each accepted request unless its surface/display is destroyed.
- * Inspect paste.status even when data is empty. Use the display thread.
- */
-YASL_API uint64_t yasl_clipboard_request(struct yasl_surface *s, enum yasl_selection selection);
-
-/* Current device-pixel size and scale, matching the configure callback. */
-/**
- * Read current device-pixel size and scale together.
- * @param s Surface, or NULL.
- * @return Geometry matching configure callbacks; NULL gives zero size and unit scale.
- */
-YASL_API struct yasl_geometry yasl_surface_geometry(const struct yasl_surface *s);
-/**
- * Read current window state.
- * @param s Surface, or NULL.
- * @return A bitmask of yasl_surface_state values, or zero for NULL.
- * Flags are reported state, not requests; unsupported states are absent.
- * Flags may coexist. Windowed and floating have no separate flags. Suspension
- * does not imply minimization, and no minimized flag is reported.
- */
-YASL_API uint32_t yasl_surface_state(const struct yasl_surface *s);
-/* Timing uses monotonic nanoseconds. Check source before treating timestamps
- * as presentation confirmation. refresh/next_present are estimates. Without
- * YASL_CAP_FRAME_PACING, redraws run on flush without display synchronization.
- * Tearing may bypass pacing even when the capability is present. */
-/**
- * Read frame timing estimates and their provenance.
- * @param s Surface, or NULL.
- * @return Current timing, or a zero-initialized result for NULL.
- * Timestamps use monotonic nanoseconds. Inspect source before treating a timestamp
- * as confirmation of presentation. Refresh and next-present values are estimates.
- */
-YASL_API struct yasl_timing yasl_surface_timing(const struct yasl_surface *s);
-/**
- * Get a nondecreasing animation timestamp aligned to estimated refresh.
- * @param s Surface, or NULL.
- * @param now Current monotonic nanoseconds, usually from yasl_loop_now_ns().
- * @return Animation timestamp, or zero for NULL. Before timing is known, returns now.
- * Use this to advance animations; it is not presentation feedback.
- */
-YASL_API uint64_t yasl_surface_frame_time_ns(struct yasl_surface *s, uint64_t now);
-/* Set the mouse and tablet cursor for this surface, including future entries.
- * YASL_CURSOR_HIDDEN stays hidden until another cursor is selected. */
-/**
- * Select the surface's system mouse and tablet cursor.
- * @param s Surface.
- * @param cursor Cursor shape; YASL_CURSOR_HIDDEN hides the cursor without disabling input.
- * The choice persists across motion and re-entry until another shape is selected.
- */
-YASL_API void yasl_surface_set_cursor(struct yasl_surface *s, enum yasl_cursor cursor);
-/**
- * Set a backend window background hint.
- * @param s Surface.
- * @param bg Packed 0xAARRGGBB color; backends may ignore alpha or the entire hint.
- * Does not initialize framebuffer pixels or replace drawing.
- */
-YASL_API void yasl_surface_set_background_color(struct yasl_surface *s, uint32_t bg);
-
-/* IME Support: preedit is borrowed until the next dispatch or destruction. */
-/**
- * Read the current uncommitted UTF-8 composition text.
- * @param s Surface, or NULL.
- * @return Borrowed NUL-terminated text, or NULL if absent/unsupported.
- * Copy to retain across the next dispatch or surface destruction. Composition
- * changes can schedule a redraw. Insert committed YASL_EVENT_TEXT into documents,
- * not preedit text.
- */
-YASL_API const char *yasl_surface_ime_preedit(const struct yasl_surface *s);
-/**
- * Read the cursor/selection range in the preedit string.
- * @param s Surface.
- * @param begin Required output UTF-8 byte offset for the range start.
- * @param end Required output UTF-8 byte offset for the range end.
- * @return true if available; false without changing outputs otherwise.
- * Currently available on Wayland; Cocoa preedit text has no range result.
- */
-YASL_API bool yasl_surface_ime_preedit_cursor(const struct yasl_surface *s, int *begin, int *end);
-/**
- * Place the composition candidate window near the application caret.
- * @param s Surface.
- * @param caret Caret rectangle relative to the surface, in device pixels.
- * Update after caret motion or scale changes. Unsupported backends ignore it.
- */
-YASL_API void yasl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_rect2i caret);
-
-/* Client-Side Decorations (CSD) Actions */
-/**
- * Ask the window system to begin an interactive move.
- * @param s Toplevel surface, normally during handling of a pointer press.
- * @return true if the request was issued; false if unsupported or not permitted.
- * A recent input serial may be required. Success does not confirm a final position.
- */
-YASL_API bool yasl_surface_start_interactive_move(struct yasl_surface *s);
-/**
- * Ask the window system to begin an interactive resize.
- * @param s Toplevel surface, normally during handling of a pointer press.
- * @param edge One nonzero yasl_resize_edge enumerator.
- * @return true if issued; false for an invalid edge, missing support, or rejected request.
- */
-YASL_API bool yasl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_resize_edge edge);
-/**
- * Ask the window system to display its window management menu.
- * @param s Toplevel surface.
- * @param pos Surface-relative position in device pixels.
- * @return true if issued; false if unsupported or not currently permitted.
- */
-YASL_API bool yasl_surface_show_window_menu(struct yasl_surface *s, struct yasl_vector2i pos);
-
-/* Drag Support */
-/**
- * Attach a toplevel surface to the current surface drag.
- * @param s Surface to carry; check YASL_CAP_DRAG_CARRY first.
- * @param offset Device-pixel offset of the pointer within the carried surface.
- * @return true if accepted; false if unsupported or no compatible drag is active.
- */
-YASL_API bool yasl_surface_drag_carry(struct yasl_surface *s, struct yasl_vector2i offset);
-/**
- * Place a toplevel surface relative to the current pointer.
- * @param s Surface; check YASL_CAP_PLACE_WINDOW first.
- * @param at Device-pixel position within the surface to place under the pointer.
- * @return true if issued; false if unsupported or unavailable.
- */
-YASL_API bool yasl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_vector2i at);
-
+/** Last error on this thread; success does not clear it. */
+YASL_API enum yasl_error yasl_get_error(const char **description);
+YASL_API int yasl_get_native_error(void);
+/** Only before the first surface is created. */
+YASL_API bool yasl_set_app_id(const char *app_id);
+YASL_API bool yasl_set_backend(enum yasl_backend backend);
+/** Flags zero creates a surface for your own Vulkan or Metal renderer. */
+YASL_API yasl_surface *yasl_surface_create(const char *title, int width, int height, unsigned flags);
+/** Also destroys its popups. */
+YASL_API void yasl_surface_destroy(yasl_surface *surface);
+/** Destroys all surfaces. Release your GPU objects first. */
+YASL_API void yasl_shutdown(void);
+/** 1 event, 0 none (timeout or wake), -1 failure. timeout_ms zero polls, negative
+ * waits forever. Without an event, out->type is NONE. */
+YASL_API int yasl_wait_event(yasl_event *out, int timeout_ms);
+/** Makes a waiting yasl_wait_event return. Thread-safe. */
+YASL_API void yasl_wake(void);
+YASL_API uint64_t yasl_time_ns(void);
+YASL_API enum yasl_backend yasl_surface_backend(const yasl_surface *surface);
+YASL_API unsigned yasl_surface_features(const yasl_surface *surface);
+YASL_API void yasl_surface_get_size(const yasl_surface *surface, int *width, int *height);
+YASL_API void yasl_surface_get_framebuffer_size(const yasl_surface *surface, int *width, int *height);
+YASL_API double yasl_surface_get_scale(const yasl_surface *surface);
+YASL_API unsigned yasl_surface_state(const yasl_surface *surface);
+YASL_API void *yasl_surface_userdata(const yasl_surface *surface);
+YASL_API void yasl_surface_set_userdata(yasl_surface *surface, void *userdata);
+YASL_API bool yasl_surface_set_title(yasl_surface *surface, const char *title);
+/** Requests; the result arrives as a WINDOW event. */
+YASL_API bool yasl_surface_request_size(yasl_surface *surface, int width, int height);
+YASL_API bool yasl_surface_request_fullscreen(yasl_surface *surface, bool enabled);
+YASL_API bool yasl_surface_request_maximized(yasl_surface *surface, bool enabled);
+YASL_API bool yasl_surface_request_minimize(yasl_surface *surface);
+YASL_API bool yasl_surface_set_cursor(yasl_surface *surface, enum yasl_cursor cursor);
+YASL_API bool yasl_surface_set_pointer_mode(yasl_surface *surface, enum yasl_pointer_mode mode);
+/** Draws during native modal loops such as a live resize. The callback may render
+ * but must not wait, create or destroy surfaces, or shut down. */
+YASL_API void yasl_surface_set_refresh_callback(yasl_surface *surface, void (*refresh)(yasl_surface *,void *), void *userdata);
+YASL_API int yasl_double_click_ms(void);
+YASL_API bool yasl_clipboard_set(enum yasl_selection selection, const char *text);
+/** Returns a request ID; the text arrives as a CLIPBOARD event. Zero on failure. */
+YASL_API uint64_t yasl_clipboard_request(yasl_surface *target, enum yasl_selection selection);
+/** IME support for a text field. */
+YASL_API bool yasl_surface_set_text_input(yasl_surface *surface, bool active, enum yasl_text_purpose purpose);
+/** UTF-8; selection offsets are bytes. */
+YASL_API bool yasl_surface_set_surrounding_text(yasl_surface *surface, const char *text, size_t length,
+ size_t selection_begin, size_t selection_end, uint64_t revision);
+YASL_API bool yasl_surface_set_ime_caret(yasl_surface *surface, double x, double y, double width, double height);
+/** Anchor picks a point on the x/y/anchor rectangle of the parent; gravity is the
+ * direction the popup grows. Functions taking a trigger need a recent press event. */
+YASL_API yasl_surface *yasl_popup_create(yasl_surface *parent, int width, int height,
+ double x, double y, double anchor_width, double anchor_height, enum yasl_popup_edge anchor,
+ enum yasl_popup_edge gravity, unsigned constraints, const yasl_event *trigger);
+YASL_API bool yasl_popup_reposition(yasl_surface *popup, int width, int height,
+ double x, double y, double anchor_width, double anchor_height, enum yasl_popup_edge anchor,
+ enum yasl_popup_edge gravity, unsigned constraints);
+YASL_API bool yasl_surface_start_interactive_move(yasl_surface *surface, const yasl_event *trigger);
+YASL_API bool yasl_surface_start_interactive_resize(yasl_surface *surface, enum yasl_resize_edge edge, const yasl_event *trigger);
+YASL_API bool yasl_surface_show_window_menu(yasl_surface *surface, double x, double y, const yasl_event *trigger);
+YASL_API bool yasl_drag_start(yasl_surface *origin, const yasl_event *trigger,
+ const uint32_t *pixels, int width, int height, int hotspot_x, int hotspot_y);
+YASL_API void yasl_drag_cancel(void);
+YASL_API bool yasl_surface_drag_carry(yasl_surface *surface, double x, double y);
+YASL_API bool yasl_surface_place_under_pointer(yasl_surface *surface, double x, double y);
+YASL_API size_t yasl_drop_next_path(const char **cursor, char *out, size_t capacity);
#ifdef __cplusplus
}
#endif
-
-#endif /* YASL_H */
+#endif
diff --git a/meson.build b/meson.build
index 9a59016..4d7d7ce 100644
--- a/meson.build
+++ b/meson.build
@@ -21,6 +21,8 @@ sources = []
has_wayland = false
has_x11 = false
has_dbus = false
+has_egl = false
+has_vulkan = false
has_cocoa = system == 'darwin'
if system == 'linux'
@@ -46,6 +48,17 @@ if system == 'linux'
if has_dbus
deps += [dbus]
endif
+ egl_dep = dependency('egl', required: false)
+ wayland_egl_dep = dependency('wayland-egl', required: false)
+ has_egl = egl_dep.found() and (not has_wayland or wayland_egl_dep.found())
+ if has_egl
+ deps += egl_dep
+ if has_wayland
+ deps += wayland_egl_dep
+ endif
+ endif
+ vulkan_dep = dependency('vulkan', required: false)
+ has_vulkan = vulkan_dep.found()
m_dep = cc.find_library('m', required: false)
rt_dep = cc.find_library('rt', required: false)
deps += [m_dep, rt_dep]
@@ -63,17 +76,24 @@ elif system == 'darwin'
error('Cocoa requires macOS 14 or newer. Set MACOSX_DEPLOYMENT_TARGET=14.0 or a newer target before configuring.')
endif
deps += [
- dependency('appleframeworks', modules: ['AppKit', 'Metal', 'QuartzCore', 'UserNotifications']),
+ dependency('appleframeworks', modules: ['AppKit', 'Metal', 'QuartzCore', 'UserNotifications', 'OpenGL']),
]
else
error('YASL supports Linux and macOS.')
endif
+if has_cocoa
+ vulkan_dep = dependency('vulkan', required: false)
+ has_vulkan = vulkan_dep.found()
+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_EGL=' + (has_egl ? '1' : '0'),
+ '-DYASL_HAS_VULKAN=' + (has_vulkan ? '1' : '0'),
'-DYASL_HAS_WAYLAND=' + (has_wayland ? '1' : '0'),
'-DYASL_HAS_X11=' + (has_x11 ? '1' : '0'),
'-DYASL_HAS_COCOA=' + (has_cocoa ? '1' : '0'),
@@ -103,10 +123,12 @@ yasl_dep = declare_dependency(
meson.override_dependency('yasl', yasl_dep)
install_headers(
- 'include/yasl/yasl.h', 'include/yasl/app.h', 'include/yasl/loop.h',
- 'include/yasl/timing.h', 'include/yasl/keycodes.h', 'include/yasl/native.h',
- 'include/yasl/desktop.h', subdir: 'yasl',
+ 'include/yasl/yasl.h', 'include/yasl/keycodes.h', 'include/yasl/cpu.h',
+ 'include/yasl/gl.h', 'include/yasl/metal.h', 'include/yasl/desktop.h', subdir: 'yasl',
)
+if has_vulkan
+ install_headers('include/yasl/vulkan.h', subdir: 'yasl')
+endif
if has_wayland
install_headers('include/yasl/native/wayland.h', subdir: 'yasl/native')
endif
diff --git a/src/common/app.c b/src/common/app.c
deleted file mode 100644
index 63cf2ce..0000000
--- a/src/common/app.c
+++ /dev/null
@@ -1,153 +0,0 @@
-#include "yasl/app.h"
-#include "common/app_internal.h"
-#include "common/backend.h"
-
-#include <errno.h>
-
-struct app_runner {
- struct yasl_display *display;
- const struct yasl_app_desc *desc;
- uint64_t initial_window;
- bool running, failed, had_surfaces;
- int error;
-};
-
-void yasl_app_exit(struct yasl_display *d, enum yasl_app_result result)
-{
- if (!d || !d->app_runner || result == YASL_APP_CONTINUE) return;
- struct app_runner *runner = d->app_runner;
- runner->running = false;
- if (result == YASL_APP_ERROR) { runner->failed = true; runner->error = EIO; }
-}
-
-struct yasl_surface *yasl_app_window(struct yasl_display *d)
-{
- if (!d || !d->app_runner) return NULL;
- struct app_runner *runner = d->app_runner;
- return runner->initial_window ? yasl_events_find(d, runner->initial_window) : NULL;
-}
-
-static void dispatch_events(struct app_runner *runner)
-{
- struct yasl_event event;
- while (runner->running && yasl_display_poll_event(runner->display, &event)) {
- if (runner->desc->event)
- yasl_app_exit(runner->display, runner->desc->event(runner->desc->userdata, &event));
- else if (event.type == YASL_EVENT_WINDOW_CLOSE)
- yasl_app_exit(runner->display, YASL_APP_QUIT);
- if (event.type == YASL_EVENT_DISPLAY_QUIT)
- yasl_app_exit(runner->display, YASL_APP_QUIT);
- if (event.type == YASL_EVENT_DISPLAY_LOST)
- yasl_app_exit(runner->display, YASL_APP_ERROR);
- }
-}
-
-static void draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud)
-{
- struct app_runner *runner = ud;
- uint64_t id = s->id;
- dispatch_events(runner);
- /* Draining events can destroy the surface whose native draw is active. */
- struct yasl_surface *live = yasl_events_find(runner->display, id);
- if (live && runner->running && runner->desc->frame)
- runner->desc->frame(runner->desc->userdata, live, frame);
-}
-
-void app_runner_register_surface(void *data, struct yasl_surface *s)
-{
- struct app_runner *runner = data;
- struct yasl_window_cbs callbacks = {.draw = draw};
- yasl_surface_set_callbacks(s, &callbacks, runner);
- s->app_surface = runner;
- runner->had_surfaces = true;
-}
-
-void app_runner_unregister_surface(void *data, struct yasl_surface *s)
-{
- (void)data;
- s->app_surface = NULL;
-}
-
-bool yasl_frame_present_cpu(struct yasl_frame *frame, const struct yasl_rect2i *rects, int count)
-{
- /* Preserve the migration adapter's old NULL/nonpositive full-damage rule. */
- return yasl_frame_present(frame, count <= 0 ? NULL : rects, count <= 0 ? 0 : (size_t)count);
-}
-
-bool yasl_post_event(struct yasl_display *d, struct yasl_surface *s,
- uint32_t code, void *payload, size_t size)
-{
- if (!d || !d->app_runner) { errno = EINVAL; return false; }
- /* Legacy callers must keep s alive through this call. New code passes an ID. */
- return yasl_display_post(d, &(struct yasl_message){
- .target_id = s ? yasl_surface_id(s) : 0, .code = code, .payload = payload, .size = size,
- });
-}
-
-int yasl_app_watch_fd(struct yasl_display *d, int fd, short events,
- int (*cb)(int, short, void *), void *ud)
-{
- if (!d || !d->loop) { errno = EINVAL; return -1; }
- return yasl_loop_add_fd(d->loop, fd, events, cb, ud);
-}
-void yasl_app_unwatch_fd(struct yasl_display *d, int id)
-{
- if (d && d->loop) yasl_loop_del_fd(d->loop, id);
-}
-yasl_timer_id yasl_app_add_timer(struct yasl_display *d, uint64_t interval, yasl_timer_cb cb, void *ud)
-{
- if (!d || !d->loop) return 0;
- return yasl_loop_add_timer(d->loop, interval, cb, ud);
-}
-void yasl_app_del_timer(struct yasl_display *d, yasl_timer_id id)
-{
- if (d && d->loop) yasl_loop_del_timer(d->loop, id);
-}
-
-int yasl_run(const struct yasl_app_desc *desc)
-{
- if (!desc) { errno = EINVAL; return -1; }
- struct yasl_display *d = yasl_display_open(NULL, &(struct yasl_display_spec){
- .app_id = desc->app_id, .backend = desc->backend,
- });
- if (!d) return -1;
- struct app_runner runner = {.display = d, .desc = desc, .running = true};
- d->app_runner = &runner;
- if (desc->window) {
- struct yasl_surface *s = yasl_surface_create_toplevel(d, desc->window);
- if (!s) {
- int error = errno;
- d->app_runner = NULL;
- yasl_display_close(d);
- errno = error;
- return -1;
- }
- runner.initial_window = s->id;
- yasl_surface_request_frame(s);
- }
- if (desc->init) yasl_app_exit(d, desc->init(desc->userdata, d));
- while (runner.running) {
- dispatch_events(&runner);
- if (!runner.running || (runner.had_surfaces && !d->surfaces)) break;
- if (yasl_display_dispatch(d, -1) < 0) {
- int dispatch_error = errno;
- dispatch_events(&runner);
- runner.failed = true;
- runner.error = dispatch_error;
- break;
- }
- dispatch_events(&runner);
- if (runner.running) yasl_display_flush(d);
- }
- int queue_error = yasl_events_error(d);
- if (queue_error) { runner.failed = true; runner.error = queue_error; }
- int error = runner.error;
- if (desc->quit) desc->quit(desc->userdata, d);
- /* Backend registries create children after parents: reverse creation order
- * ensures child popups are destroyed before their parent. */
- while (d->surfaces) yasl_surface_destroy(d->surfaces);
- d->app_runner = NULL;
- yasl_display_close(d);
- if (runner.failed) errno = error;
- return runner.failed ? -1 : 0;
-}
diff --git a/src/common/app_internal.h b/src/common/app_internal.h
deleted file mode 100644
index 143df41..0000000
--- a/src/common/app_internal.h
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef YASL_APP_INTERNAL_H
-#define YASL_APP_INTERNAL_H
-
-struct yasl_surface;
-
-void app_runner_register_surface(void *runner, struct yasl_surface *s);
-void app_runner_unregister_surface(void *runner, struct yasl_surface *s);
-
-#endif
diff --git a/src/common/backend.c b/src/common/backend.c
index a81e1e7..8d20f67 100644
--- a/src/common/backend.c
+++ b/src/common/backend.c
@@ -1,42 +1,41 @@
#include "backend.h"
#include "loop.h"
-#include "common/app_internal.h"
#include "common/utf8.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
-static const struct yasl_backend_ops *backend_ops(enum yasl_backend backend)
+static const struct yasl_private_backend_ops *backend_ops(enum yasl_private_backend backend)
{
switch (backend) {
#if YASL_HAS_WAYLAND
- case YASL_BACKEND_WAYLAND: return &yasl_wayland_backend;
+ case YASL_PRIVATE_BACKEND_WAYLAND: return &yasl_private_wayland_backend;
#endif
#if YASL_HAS_X11
- case YASL_BACKEND_X11: return &yasl_x11_backend;
+ case YASL_PRIVATE_BACKEND_X11: return &yasl_private_x11_backend;
#endif
#if YASL_HAS_COCOA
- case YASL_BACKEND_COCOA: return &yasl_cocoa_backend;
+ case YASL_PRIVATE_BACKEND_COCOA: return &yasl_private_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)
+static struct yasl_private_display *open_backend(enum yasl_private_backend backend,
+ struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec)
{
- const struct yasl_backend_ops *ops = backend_ops(backend);
+ const struct yasl_private_backend_ops *ops = backend_ops(backend);
if (!ops) {
errno = ENOSYS;
return NULL;
}
errno = 0;
- struct yasl_display *d = ops->display_open(loop, spec);
+ struct yasl_private_display *d = ops->display_open(loop, spec);
if (!d && !errno) errno = ENODEV;
if (d) {
d->callback_ud = spec ? spec->ud : NULL;
- if (!yasl_events_open(d)) {
+ if (!yasl_private_events_open(d)) {
int error = errno;
ops->display_close(d);
errno = error;
@@ -46,52 +45,52 @@ static struct yasl_display *open_backend(enum yasl_backend backend,
return d;
}
-struct yasl_display *yasl_display_open(struct yasl_loop *loop,
- const struct yasl_display_spec *spec)
+struct yasl_private_display *yasl_private_display_open(struct yasl_private_loop *loop,
+ const struct yasl_private_display_spec *spec)
{
- enum yasl_backend backend = spec ? spec->backend : YASL_BACKEND_AUTO;
- if (backend < YASL_BACKEND_AUTO || backend > YASL_BACKEND_COCOA) {
+ enum yasl_private_backend backend = spec ? spec->backend : YASL_PRIVATE_BACKEND_AUTO;
+ if (backend < YASL_PRIVATE_BACKEND_AUTO || backend > YASL_PRIVATE_BACKEND_COCOA) {
errno = EINVAL;
return NULL;
}
- if (backend != YASL_BACKEND_AUTO)
+ if (backend != YASL_PRIVATE_BACKEND_AUTO)
return open_backend(backend, loop, spec);
#if YASL_HAS_COCOA
- return open_backend(YASL_BACKEND_COCOA, loop, spec);
+ return open_backend(YASL_PRIVATE_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;
+ enum yasl_private_backend first = wayland ? YASL_PRIVATE_BACKEND_WAYLAND : YASL_PRIVATE_BACKEND_X11;
+ enum yasl_private_backend second = wayland ? YASL_PRIVATE_BACKEND_X11 : YASL_PRIVATE_BACKEND_WAYLAND;
+ struct yasl_private_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)
+enum yasl_private_backend yasl_private_display_backend(const struct yasl_private_display *d)
{
- return d ? d->ops->type : YASL_BACKEND_AUTO;
+ return d ? d->ops->type : YASL_PRIVATE_BACKEND_AUTO;
}
-void yasl_display_close(struct yasl_display *d)
+void yasl_private_display_close(struct yasl_private_display *d)
{
if (!d || !d->ops->display_close)
return;
- yasl_events_close(d);
+ yasl_private_events_close(d);
d->ops->display_close(d);
}
-uint32_t yasl_display_caps(const struct yasl_display *d)
+uint32_t yasl_private_display_caps(const struct yasl_private_display *d)
{
if (!d || !d->ops->display_caps)
return 0;
return d->ops->display_caps(d);
}
-int yasl_display_dispatch(struct yasl_display *d, int timeout_ms)
+int yasl_private_display_dispatch(struct yasl_private_display *d, int timeout_ms)
{
if (!d) {
errno = EINVAL;
@@ -101,59 +100,59 @@ int yasl_display_dispatch(struct yasl_display *d, int timeout_ms)
errno = ENOTSUP;
return -1;
}
- int queue_error = yasl_events_error(d);
+ int queue_error = yasl_private_events_error(d);
if (queue_error) { errno = queue_error; return -1; }
- if (yasl_events_pending(d)) timeout_ms = 0;
+ if (yasl_private_events_pending(d)) timeout_ms = 0;
errno = 0;
int result = d->ops->display_dispatch_timeout(d, timeout_ms);
- queue_error = yasl_events_error(d);
+ queue_error = yasl_private_events_error(d);
if (queue_error) { errno = queue_error; return -1; }
if (result < 0 && !errno) errno = EIO;
return result;
}
-void yasl_display_flush(struct yasl_display *d)
+void yasl_private_display_flush(struct yasl_private_display *d)
{
if (!d || !d->ops->display_flush)
return;
d->ops->display_flush(d);
}
-int yasl_display_modifiers(const struct yasl_display *d)
+int yasl_private_display_modifiers(const struct yasl_private_display *d)
{
if (!d || !d->ops->display_modifiers)
return 0;
return d->ops->display_modifiers(d);
}
-int yasl_display_double_click_ms(const struct yasl_display *d)
+int yasl_private_display_double_click_ms(const struct yasl_private_display *d)
{
int ms = d && d->ops->display_double_click_ms ? d->ops->display_double_click_ms(d) : 0;
/* Reject nonsense from misconfigured settings as well as no answer. */
- return ms > 0 && ms <= 10000 ? ms : YASL_DOUBLE_CLICK_MS_DEFAULT;
+ return ms > 0 && ms <= 10000 ? ms : YASL_PRIVATE_DOUBLE_CLICK_MS_DEFAULT;
}
-void yasl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text)
+void yasl_private_display_copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text)
{
if (!d || !d->ops->display_copy || !text ||
- (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY) ||
- (sel == YASL_PRIMARY && !(yasl_display_caps(d) & YASL_CAP_PRIMARY_SELECTION)))
+ (sel != YASL_PRIVATE_CLIPBOARD && sel != YASL_PRIVATE_PRIMARY) ||
+ (sel == YASL_PRIVATE_PRIMARY && !(yasl_private_display_caps(d) & YASL_PRIVATE_CAP_PRIMARY_SELECTION)))
return;
d->ops->display_copy(d, sel, text);
}
-void yasl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target)
+void yasl_private_display_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target)
{
if (!d || !d->ops->display_paste ||
- (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY) ||
- (sel == YASL_PRIMARY && !(yasl_display_caps(d) & YASL_CAP_PRIMARY_SELECTION)))
+ (sel != YASL_PRIVATE_CLIPBOARD && sel != YASL_PRIVATE_PRIMARY) ||
+ (sel == YASL_PRIVATE_PRIMARY && !(yasl_private_display_caps(d) & YASL_PRIVATE_CAP_PRIMARY_SELECTION)))
return;
if (!target || target->display != d)
return;
d->ops->display_paste(d, sel, target);
}
-bool yasl_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)
+bool yasl_private_display_drag_start(struct yasl_private_display *d, struct yasl_surface *origin, const struct yasl_private_drag_image *image, const struct yasl_private_drag_listener *l, void *ud)
{
if (!d || !d->ops->display_drag_start)
return 0;
@@ -163,19 +162,19 @@ bool yasl_display_drag_start(struct yasl_display *d, struct yasl_surface *origin
image->size.x > 32767 || image->size.y > 32767 ||
(int64_t)image->size.x * image->size.y * 4 > INT32_MAX))
return false;
- const struct yasl_drag_listener empty = {0};
+ const struct yasl_private_drag_listener empty = {0};
return d->ops->display_drag_start(d, origin, image, l ? l : &empty, ud);
}
-void yasl_display_drag_cancel(struct yasl_display *d)
+void yasl_private_display_drag_cancel(struct yasl_private_display *d)
{
if (!d || !d->ops->display_drag_cancel)
return;
d->ops->display_drag_cancel(d);
}
-struct yasl_surface *yasl_create_toplevel(struct yasl_display *d,
- const struct yasl_window_spec *spec, void *graphics_device)
+struct yasl_surface *yasl_private_create_toplevel(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *graphics_device)
{
if (!d || !d->ops->surface_create_toplevel || !spec ||
spec->size.x <= 0 || spec->size.y <= 0 ||
@@ -184,14 +183,14 @@ struct yasl_surface *yasl_create_toplevel(struct yasl_display *d,
errno = EINVAL;
return NULL;
}
- if (graphics_device && !(yasl_display_caps(d) & YASL_CAP_NATIVE_GRAPHICS)) {
+ if (graphics_device && !(yasl_private_display_caps(d) & YASL_PRIVATE_CAP_NATIVE_GRAPHICS)) {
errno = ENOTSUP;
return NULL;
}
errno = 0;
struct yasl_surface *s = d->ops->surface_create_toplevel(d, spec, graphics_device);
if (!s && !errno) errno = ENODEV;
- if (s && !yasl_events_register(s)) {
+ if (s && !yasl_private_events_register(s)) {
int error = errno;
d->ops->surface_destroy(s);
errno = error;
@@ -201,21 +200,18 @@ struct yasl_surface *yasl_create_toplevel(struct yasl_display *d,
s->userdata = spec->userdata;
/* Native creation may dispatch events. Attach this window's callbacks
* only after it finishes, so callers can store the returned surface. */
- if (d->app_runner)
- app_runner_register_surface(d->app_runner, s);
- else
- yasl_surface_set_callbacks(s, spec->cbs, spec->ud);
+ yasl_private_surface_set_callbacks(s, spec->cbs, spec->ud);
}
return s;
}
-struct yasl_surface *yasl_surface_create_toplevel(struct yasl_display *d,
- const struct yasl_window_spec *spec)
+struct yasl_surface *yasl_private_surface_create_toplevel(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec)
{
- return yasl_create_toplevel(d, spec, NULL);
+ return yasl_private_create_toplevel(d, spec, NULL);
}
-static bool popup_spec_valid(const struct yasl_popup_spec *spec)
+static bool popup_spec_valid(const struct yasl_private_popup_spec *spec)
{
return spec && spec->size.x > 0 && spec->size.y > 0 &&
spec->size.x <= 32767 && spec->size.y <= 32767 &&
@@ -227,13 +223,13 @@ static bool popup_spec_valid(const struct yasl_popup_spec *spec)
(int64_t)spec->content_offset.y + spec->content_size.y <= spec->size.y;
}
-struct yasl_surface *yasl_surface_create_popup(struct yasl_surface *parent, const struct yasl_popup_spec *spec)
+struct yasl_surface *yasl_private_surface_create_popup(struct yasl_surface *parent, const struct yasl_private_popup_spec *spec)
{
if (!parent || !popup_spec_valid(spec)) {
errno = EINVAL;
return NULL;
}
- if (!(yasl_display_caps(parent->display) & YASL_CAP_POPUPS) ||
+ if (!(yasl_private_display_caps(parent->display) & YASL_PRIVATE_CAP_POPUPS) ||
!parent->display->ops->surface_create_popup) {
errno = ENOTSUP;
return NULL;
@@ -243,25 +239,23 @@ struct yasl_surface *yasl_surface_create_popup(struct yasl_surface *parent, cons
if (!s && !errno) errno = ENODEV;
if (s) {
s->popup = true;
- if (!yasl_events_register(s)) {
+ if (!yasl_private_events_register(s)) {
int error = errno;
parent->display->ops->surface_destroy(s);
errno = error;
return NULL;
}
- if (parent->display->app_runner)
- app_runner_register_surface(parent->display->app_runner, s);
}
return s;
}
-bool yasl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_popup_spec *spec)
+bool yasl_private_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_private_popup_spec *spec)
{
if (!popup || !popup_spec_valid(spec)) {
errno = EINVAL;
return false;
}
- if (!(yasl_display_caps(popup->display) & YASL_CAP_POPUPS) ||
+ if (!(yasl_private_display_caps(popup->display) & YASL_PRIVATE_CAP_POPUPS) ||
!popup->display->ops->surface_popup_reposition) {
errno = ENOTSUP;
return false;
@@ -272,68 +266,61 @@ bool yasl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl
return result;
}
-void yasl_surface_destroy(struct yasl_surface *s)
+void yasl_private_surface_destroy(struct yasl_surface *s)
{
if (!s || !s->display->ops->surface_destroy)
return;
- if (s->app_surface && s->display->app_runner) {
- app_runner_unregister_surface(s->display->app_runner, s);
- }
- yasl_events_unregister(s);
- yasl_surface_clear_timers(s);
+ yasl_private_events_unregister(s);
+ yasl_private_surface_clear_timers(s);
input_queue_clear(&s->input);
s->display->ops->surface_destroy(s);
}
-struct yasl_display *yasl_surface_display(const struct yasl_surface *s)
+struct yasl_private_display *yasl_private_surface_display(const struct yasl_surface *s)
{
return s ? s->display : NULL;
}
-bool yasl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_window_cbs *cbs, void *ud)
+bool yasl_private_surface_set_callbacks(struct yasl_surface *s, const struct yasl_private_window_cbs *cbs, void *ud)
{
if (!s) { errno = EINVAL; return false; }
- if (s->display && s->display->app_runner && s->app_surface) {
- errno = EBUSY;
- return false;
- }
- s->callbacks = cbs ? *cbs : (struct yasl_window_cbs){0};
+ s->callbacks = cbs ? *cbs : (struct yasl_private_window_cbs){0};
s->ud = ud;
if (s->display && s->display->ops && s->display->ops->surface_set_callbacks)
s->display->ops->surface_set_callbacks(s, cbs, ud);
return true;
}
-void yasl_surface_set_title(struct yasl_surface *s, const char *title)
+void yasl_private_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)
+void yasl_private_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)
+bool yasl_private_surface_present(struct yasl_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage)
{
if (!s || !s->display->ops->surface_present || !fb || !damage ||
!fb->generation || fb->generation != s->frame_generation ||
fb->size.x <= 0 || fb->size.y <= 0 ||
- damage->n < -1 || damage->n > YASL_DAMAGE_MAX)
+ damage->n < -1 || damage->n > YASL_PRIVATE_DAMAGE_MAX)
return false;
- struct yasl_damage_list clipped;
+ struct yasl_private_damage_list clipped;
clipped.n = damage->n;
if (damage->n < 0) {
clipped.bounds = damage->bounds;
} else {
clipped.n = 0;
- clipped.bounds = (struct yasl_rect2i){0};
+ clipped.bounds = (struct yasl_private_rect2i){0};
for (int i = 0; i < damage->n; i++) {
- struct yasl_rect2i r = damage->rects[i];
+ struct yasl_private_rect2i r = damage->rects[i];
if (r.width < 0 || r.height < 0) return false;
int64_t right = (int64_t)r.x + r.width;
int64_t bottom = (int64_t)r.y + r.height;
@@ -341,7 +328,7 @@ bool yasl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer
if (right > fb->size.x) right = fb->size.x;
if (bottom > fb->size.y) bottom = fb->size.y;
if (right > x && bottom > y)
- clipped.rects[clipped.n++] = (struct yasl_rect2i){
+ clipped.rects[clipped.n++] = (struct yasl_private_rect2i){
(int)x, (int)y, (int)(right - x), (int)(bottom - y),
};
}
@@ -349,164 +336,164 @@ bool yasl_surface_present(struct yasl_surface *s, const struct yasl_framebuffer
return s->display->ops->surface_present(s, fb, &clipped);
}
-bool yasl_surface_poll_event(struct yasl_surface *s, struct yasl_event *ev)
+bool yasl_private_surface_poll_event(struct yasl_surface *s, struct yasl_private_event *ev)
{
if (!s || !ev)
return false;
if (s->display && s->display->events)
- return yasl_events_poll_surface(s, ev);
+ return yasl_private_events_poll_surface(s, ev);
if (s->display && s->display->ops && s->display->ops->surface_poll_event)
return s->display->ops->surface_poll_event(s, ev);
return input_queue_pop(&s->input, ev);
}
-bool yasl_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
- struct yasl_vector2i *size)
+bool yasl_private_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
+ struct yasl_private_vector2i *size)
{
if (!size) return false;
- *size = (struct yasl_vector2i){0};
+ *size = (struct yasl_private_vector2i){0};
if (!s || (!out && cap) || !s->display->ops->surface_read_pixels) return false;
return s->display->ops->surface_read_pixels(s, out, cap, size);
}
-bool yasl_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_pointer_mode mode)
+bool yasl_private_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_private_pointer_mode mode)
{
- if (!s || (mode != YASL_POINTER_NORMAL && mode != YASL_POINTER_RELATIVE))
+ if (!s || (mode != YASL_PRIVATE_POINTER_NORMAL && mode != YASL_PRIVATE_POINTER_RELATIVE))
return false;
if (mode == s->pointer_mode)
return true;
- const struct yasl_backend_ops *ops = s->display ? s->display->ops : NULL;
+ const struct yasl_private_backend_ops *ops = s->display ? s->display->ops : NULL;
if (!ops || !ops->surface_set_pointer_mode)
- return mode == YASL_POINTER_NORMAL;
+ return mode == YASL_PRIVATE_POINTER_NORMAL;
if (!ops->surface_set_pointer_mode(s, mode))
return false;
s->pointer_mode = mode;
return true;
}
-struct yasl_geometry yasl_surface_geometry(const struct yasl_surface *s)
+struct yasl_private_geometry yasl_private_surface_geometry(const struct yasl_surface *s)
{
if (!s)
- return (struct yasl_geometry){.scale_120 = YASL_SCALE_UNIT};
+ return (struct yasl_private_geometry){.scale_120 = YASL_PRIVATE_SCALE_UNIT};
if (s->scale_120)
- return (struct yasl_geometry){s->size, s->scale_120};
+ return (struct yasl_private_geometry){s->size, s->scale_120};
if (s->display && s->display->ops && s->display->ops->surface_geometry)
return s->display->ops->surface_geometry(s);
- return (struct yasl_geometry){s->size, YASL_SCALE_UNIT};
+ return (struct yasl_private_geometry){s->size, YASL_PRIVATE_SCALE_UNIT};
}
-uint32_t yasl_surface_state(const struct yasl_surface *s)
+uint32_t yasl_private_surface_state(const struct yasl_surface *s)
{
return s ? s->state : 0;
}
-struct yasl_timing yasl_surface_timing(const struct yasl_surface *s)
+struct yasl_private_timing yasl_private_surface_timing(const struct yasl_surface *s)
{
if (!s)
- return (struct yasl_timing){0};
+ return (struct yasl_private_timing){0};
if (s->display && s->display->ops && s->display->ops->surface_timing)
return s->display->ops->surface_timing(s);
- struct yasl_timing t = {
+ struct yasl_private_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),
+ .next_present_ns = yasl_private_clock_next_present_ns(&s->clock),
.frames_skipped = s->frames_skipped,
.source = s->clock.source,
};
- yasl_clock_resize_stats(&s->clock, &t.resize);
+ yasl_private_clock_resize_stats(&s->clock, &t.resize);
return t;
}
-uint64_t yasl_surface_frame_time_ns(struct yasl_surface *s, uint64_t now)
+uint64_t yasl_private_surface_frame_time_ns(struct yasl_surface *s, uint64_t now)
{
if (!s)
return 0;
if (s->display && s->display->ops && s->display->ops->surface_frame_time_ns)
return s->display->ops->surface_frame_time_ns(s, now);
- return yasl_clock_frame_timestamp_ns(&s->clock, now);
+ return yasl_private_clock_frame_timestamp_ns(&s->clock, now);
}
-void yasl_surface_set_cursor(struct yasl_surface *s, enum yasl_cursor cursor)
+void yasl_private_surface_set_cursor(struct yasl_surface *s, enum yasl_private_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)
+void yasl_private_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)
+const char *yasl_private_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)
+bool yasl_private_surface_ime_preedit_cursor(const struct yasl_surface *s, int *begin, int *end)
{
if (!s || !begin || !end || !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)
+void yasl_private_surface_set_ime_caret(struct yasl_surface *s, struct yasl_private_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)
+bool yasl_private_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)
+bool yasl_private_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_private_resize_edge edge)
{
if (!s || !s->display->ops->surface_start_interactive_resize ||
- (edge != YASL_EDGE_TOP && edge != YASL_EDGE_BOTTOM &&
- edge != YASL_EDGE_LEFT && edge != YASL_EDGE_RIGHT &&
- edge != YASL_EDGE_TOP_LEFT && edge != YASL_EDGE_TOP_RIGHT &&
- edge != YASL_EDGE_BOTTOM_LEFT && edge != YASL_EDGE_BOTTOM_RIGHT))
+ (edge != YASL_PRIVATE_EDGE_TOP && edge != YASL_PRIVATE_EDGE_BOTTOM &&
+ edge != YASL_PRIVATE_EDGE_LEFT && edge != YASL_PRIVATE_EDGE_RIGHT &&
+ edge != YASL_PRIVATE_EDGE_TOP_LEFT && edge != YASL_PRIVATE_EDGE_TOP_RIGHT &&
+ edge != YASL_PRIVATE_EDGE_BOTTOM_LEFT && edge != YASL_PRIVATE_EDGE_BOTTOM_RIGHT))
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)
+bool yasl_private_surface_show_window_menu(struct yasl_surface *s, struct yasl_private_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)
+bool yasl_private_surface_drag_carry(struct yasl_surface *s, struct yasl_private_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)
+bool yasl_private_surface_place_under_pointer(struct yasl_surface *s, struct yasl_private_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_surface_clear_timers(struct yasl_surface *s)
+void yasl_private_surface_clear_timers(struct yasl_surface *s)
{
if (!s || !s->timers) return;
- struct yasl_loop *loop = s->display ? s->display->loop : NULL;
+ struct yasl_private_loop *loop = s->display ? s->display->loop : NULL;
for (int i = 0; i < s->timer_count; i++) {
if (loop && s->timers[i]) {
- yasl_loop_del_timer(loop, s->timers[i]);
+ yasl_private_loop_del_timer(loop, s->timers[i]);
}
}
free(s->timers);
@@ -515,13 +502,13 @@ void yasl_surface_clear_timers(struct yasl_surface *s)
s->timer_capacity = 0;
}
-yasl_timer_id yasl_surface_add_timer(struct yasl_surface *s, uint64_t interval_ns,
- yasl_timer_cb cb, void *ud)
+yasl_private_timer_id yasl_private_surface_add_timer(struct yasl_surface *s, uint64_t interval_ns,
+ yasl_private_timer_cb cb, void *ud)
{
if (!s || !s->display || !s->display->loop || !cb || interval_ns == 0)
return 0;
- yasl_timer_id id = yasl_loop_add_timer(s->display->loop, interval_ns, cb, ud);
+ yasl_private_timer_id id = yasl_private_loop_add_timer(s->display->loop, interval_ns, cb, ud);
if (!id) return 0;
/* A one-shot timer retires itself in the loop. Drop retired ids before
@@ -529,15 +516,15 @@ yasl_timer_id yasl_surface_add_timer(struct yasl_surface *s, uint64_t interval_n
if (s->timer_count >= s->timer_capacity) {
int kept = 0;
for (int i = 0; i < s->timer_count; i++)
- if (yasl_loop_timer_armed(s->display->loop, s->timers[i]))
+ if (yasl_private_loop_timer_armed(s->display->loop, s->timers[i]))
s->timers[kept++] = s->timers[i];
s->timer_count = kept;
}
if (s->timer_count >= s->timer_capacity) {
int new_cap = s->timer_capacity ? s->timer_capacity * 2 : 4;
- yasl_timer_id *nt = realloc(s->timers, (size_t)new_cap * sizeof(*nt));
+ yasl_private_timer_id *nt = realloc(s->timers, (size_t)new_cap * sizeof(*nt));
if (!nt) {
- yasl_loop_del_timer(s->display->loop, id);
+ yasl_private_loop_del_timer(s->display->loop, id);
return 0;
}
s->timers = nt;
@@ -547,24 +534,24 @@ yasl_timer_id yasl_surface_add_timer(struct yasl_surface *s, uint64_t interval_n
return id;
}
-bool yasl_surface_del_timer(struct yasl_surface *s, yasl_timer_id id)
+bool yasl_private_surface_del_timer(struct yasl_surface *s, yasl_private_timer_id id)
{
if (!s || id == 0) return false;
for (int i = 0; i < s->timer_count; i++) {
if (s->timers[i] == id) {
s->timers[i] = s->timers[--s->timer_count];
- return s->display && yasl_loop_del_timer(s->display->loop, id);
+ return s->display && yasl_private_loop_del_timer(s->display->loop, id);
}
}
return false;
}
-struct yasl_loop *yasl_display_loop(struct yasl_display *d)
+struct yasl_private_loop *yasl_private_display_loop(struct yasl_private_display *d)
{
return d ? d->loop : NULL;
}
-bool yasl_frame_present(struct yasl_frame *frame, const struct yasl_rect2i *rects, size_t count)
+bool yasl_private_frame_present(struct yasl_private_frame *frame, const struct yasl_private_rect2i *rects, size_t count)
{
if (!frame || !frame->surface || (!rects && count)) { errno = EINVAL; return false; }
struct yasl_surface *s = frame->surface;
@@ -573,81 +560,81 @@ bool yasl_frame_present(struct yasl_frame *frame, const struct yasl_rect2i *rect
for (size_t i = 0; i < count; i++) {
if (rects[i].width < 0 || rects[i].height < 0) { errno = EINVAL; return false; }
}
- struct yasl_damage_list damage = {0};
- if (!rects || count > YASL_DAMAGE_MAX) damage.n = -1;
+ struct yasl_private_damage_list damage = {0};
+ if (!rects || count > YASL_PRIVATE_DAMAGE_MAX) damage.n = -1;
else {
damage.n = (int)count;
for (size_t i = 0; i < count; i++) damage.rects[i] = rects[i];
}
errno = 0;
- bool ok = yasl_surface_present(s, &frame->target, &damage);
+ bool ok = yasl_private_surface_present(s, &frame->target, &damage);
if (ok) s->frame_presented = true;
if (!ok && !errno) errno = EIO;
return ok;
}
-void yasl_surface_request_frame(struct yasl_surface *s)
+void yasl_private_surface_request_frame(struct yasl_surface *s)
{
if (!s) return;
- yasl_surface_schedule_redraw(s);
- if (s->display->loop) yasl_loop_kick(s->display->loop);
+ yasl_private_surface_schedule_redraw(s);
+ if (s->display->loop) yasl_private_loop_kick(s->display->loop);
}
-static uint64_t pacing_tick(void *ud, yasl_timer_id id, uint64_t interval)
+static uint64_t pacing_tick(void *ud, yasl_private_timer_id id, uint64_t interval)
{
(void)id; (void)interval;
struct yasl_surface *s = ud;
- yasl_surface_request_frame(s);
- double refresh = yasl_surface_timing(s).refresh_ns;
+ yasl_private_surface_request_frame(s);
+ double refresh = yasl_private_surface_timing(s).refresh_ns;
return refresh >= 1000000.0 && refresh <= 1000000000.0
? (uint64_t)refresh : 16666667;
}
-bool yasl_surface_set_pacing(struct yasl_surface *s, yasl_pacing_mode mode)
+bool yasl_private_surface_set_pacing(struct yasl_surface *s, yasl_private_pacing_mode mode)
{
- if (!s || (mode != YASL_PACING_ON_DEMAND && mode != YASL_PACING_CONTINUOUS)) {
+ if (!s || (mode != YASL_PRIVATE_PACING_ON_DEMAND && mode != YASL_PRIVATE_PACING_CONTINUOUS)) {
errno = EINVAL;
return false;
}
if (s->pacing_timer) {
- yasl_surface_del_timer(s, s->pacing_timer);
+ yasl_private_surface_del_timer(s, s->pacing_timer);
s->pacing_timer = 0;
}
- if (mode == YASL_PACING_CONTINUOUS && !(yasl_display_caps(s->display) & YASL_CAP_FRAME_PACING)) {
- s->pacing_timer = yasl_surface_add_timer(s, 16666667, pacing_tick, s);
+ if (mode == YASL_PRIVATE_PACING_CONTINUOUS && !(yasl_private_display_caps(s->display) & YASL_PRIVATE_CAP_FRAME_PACING)) {
+ s->pacing_timer = yasl_private_surface_add_timer(s, 16666667, pacing_tick, s);
if (!s->pacing_timer) {
- s->pacing = YASL_PACING_ON_DEMAND;
+ s->pacing = YASL_PRIVATE_PACING_ON_DEMAND;
errno = ENOMEM;
return false;
}
}
s->pacing = mode;
- if (mode == YASL_PACING_CONTINUOUS) yasl_surface_request_frame(s);
+ if (mode == YASL_PRIVATE_PACING_CONTINUOUS) yasl_private_surface_request_frame(s);
return true;
}
-void yasl_surface_draw(struct yasl_surface *s, struct yasl_frame *frame)
+void yasl_private_surface_draw(struct yasl_surface *s, struct yasl_private_frame *frame)
{
if (!s || !frame || !s->callbacks.draw) return;
- struct yasl_display *d = s->display;
+ struct yasl_private_display *d = s->display;
uint64_t id = s->id;
frame->surface = s;
frame->target.stride_bytes = frame->target.size.x * (int)sizeof(uint32_t);
frame->scale_120 = s->scale_120;
frame->frame_index = s->draw_index++;
- frame->target_time_ns = yasl_surface_frame_time_ns(s, yasl_loop_now_ns());
+ frame->target_time_ns = yasl_private_surface_frame_time_ns(s, yasl_private_loop_now_ns());
s->drawing = true;
s->frame_presented = false;
s->active_frame = frame;
s->callbacks.draw(s, frame, s->ud);
/* The callback may destroy s. Lookup by identity before accessing it. */
- struct yasl_surface *live = id ? yasl_events_find(d, id) : NULL;
+ struct yasl_surface *live = id ? yasl_private_events_find(d, id) : NULL;
if (live) {
live->drawing = false;
live->active_frame = NULL;
- if (live->pacing == YASL_PACING_CONTINUOUS &&
- (yasl_display_caps(d) & YASL_CAP_FRAME_PACING))
- yasl_surface_schedule_redraw(live);
+ if (live->pacing == YASL_PRIVATE_PACING_CONTINUOUS &&
+ (yasl_private_display_caps(d) & YASL_PRIVATE_CAP_FRAME_PACING))
+ yasl_private_surface_schedule_redraw(live);
}
}
@@ -657,7 +644,7 @@ static bool window_valid(struct yasl_surface *s)
return true;
}
-bool yasl_surface_set_size(struct yasl_surface *s, struct yasl_vector2i size)
+bool yasl_private_surface_set_size(struct yasl_surface *s, struct yasl_private_vector2i size)
{
if (!window_valid(s)) return false;
if (size.x <= 0 || size.y <= 0 || size.x > 32767 || size.y > 32767 ||
@@ -669,7 +656,7 @@ bool yasl_surface_set_size(struct yasl_surface *s, struct yasl_vector2i size)
return ok;
}
-bool yasl_surface_set_fullscreen(struct yasl_surface *s, bool enabled)
+bool yasl_private_surface_set_fullscreen(struct yasl_surface *s, bool enabled)
{
if (!window_valid(s)) return false;
if (!s->display->ops->surface_set_fullscreen) { errno = ENOTSUP; return false; }
@@ -679,7 +666,7 @@ bool yasl_surface_set_fullscreen(struct yasl_surface *s, bool enabled)
return ok;
}
-bool yasl_surface_set_maximized(struct yasl_surface *s, bool enabled)
+bool yasl_private_surface_set_maximized(struct yasl_surface *s, bool enabled)
{
if (!window_valid(s)) return false;
if (!s->display->ops->surface_set_maximized) { errno = ENOTSUP; return false; }
@@ -689,7 +676,7 @@ bool yasl_surface_set_maximized(struct yasl_surface *s, bool enabled)
return ok;
}
-bool yasl_surface_minimize(struct yasl_surface *s)
+bool yasl_private_surface_minimize(struct yasl_surface *s)
{
if (!window_valid(s)) return false;
if (!s->display->ops->surface_minimize) { errno = ENOTSUP; return false; }
@@ -699,22 +686,22 @@ bool yasl_surface_minimize(struct yasl_surface *s)
return ok;
}
-static bool selection_valid(struct yasl_display *d, enum yasl_selection selection)
+static bool selection_valid(struct yasl_private_display *d, enum yasl_private_selection selection)
{
- if (!d || (selection != YASL_CLIPBOARD && selection != YASL_PRIMARY)) {
+ if (!d || (selection != YASL_PRIVATE_CLIPBOARD && selection != YASL_PRIVATE_PRIMARY)) {
errno = EINVAL; return false;
}
- if (selection == YASL_PRIMARY && !(yasl_display_caps(d) & YASL_CAP_PRIMARY_SELECTION)) {
+ if (selection == YASL_PRIVATE_PRIMARY && !(yasl_private_display_caps(d) & YASL_PRIVATE_CAP_PRIMARY_SELECTION)) {
errno = ENOTSUP; return false;
}
return true;
}
-bool yasl_clipboard_set(struct yasl_display *d, enum yasl_selection selection, const char *text)
+bool yasl_private_clipboard_set(struct yasl_private_display *d, enum yasl_private_selection selection, const char *text)
{
if (!selection_valid(d, selection)) return false;
if (!text) { errno = EINVAL; return false; }
- if (!yasl_utf8_validate(text, strlen(text))) { errno = EILSEQ; return false; }
+ if (!yasl_private_utf8_validate(text, strlen(text))) { errno = EILSEQ; return false; }
if (!d->ops->display_copy_text) { errno = ENOTSUP; return false; }
errno = 0;
bool ok = d->ops->display_copy_text(d, selection, text);
@@ -722,13 +709,13 @@ bool yasl_clipboard_set(struct yasl_display *d, enum yasl_selection selection, c
return ok;
}
-uint64_t yasl_clipboard_request(struct yasl_surface *s, enum yasl_selection selection)
+uint64_t yasl_private_clipboard_request(struct yasl_surface *s, enum yasl_private_selection selection)
{
if (!s) { errno = EINVAL; return 0; }
- struct yasl_display *d = s->display;
+ struct yasl_private_display *d = s->display;
if (!selection_valid(d, selection)) return 0;
if (!d->ops->display_request_paste) { errno = ENOTSUP; return 0; }
- uint64_t id = yasl_events_request_id(d);
+ uint64_t id = yasl_private_events_request_id(d);
if (!id) return 0;
errno = 0;
if (!d->ops->display_request_paste(d, selection, s, id)) {
diff --git a/src/common/backend.h b/src/common/backend.h
index 876a4ca..e3dc6de 100644
--- a/src/common/backend.h
+++ b/src/common/backend.h
@@ -1,48 +1,64 @@
-#ifndef YASL_BACKEND_H
-#define YASL_BACKEND_H
+#ifndef YASL_PRIVATE_BACKEND_H
+#define YASL_PRIVATE_BACKEND_H
-#include "yasl/yasl.h"
-#include "yasl/loop.h"
-#include "yasl/native.h"
+#include "common/types.h"
+#include "common/private_loop.h"
+#include "common/private_native.h"
#include "common/input.h"
+#include "common/gl_options.h"
#include "common/timing.h"
#include <string.h>
-struct yasl_backend_ops;
-struct yasl_event_state;
+struct yasl_private_backend_ops;
+struct yasl_private_event_state;
-struct yasl_display {
- const struct yasl_backend_ops *ops;
+struct yasl_private_display {
+ const struct yasl_private_backend_ops *ops;
void *native_handle;
- struct yasl_display_cbs callbacks;
- struct yasl_loop *loop;
- void *app_runner;
- /* Backend's copy of yasl_display_spec.app_id, or NULL; for desktop services. */
+ struct yasl_private_display_cbs callbacks;
+ struct yasl_private_loop *loop;
+ /* Backend's copy of yasl_private_display_spec.app_id, or NULL; for desktop services. */
const char *app_id;
void *callback_ud;
- struct yasl_event_state *events;
+ struct yasl_private_event_state *events;
struct yasl_surface *surfaces;
};
struct yasl_surface {
- struct yasl_display *display;
+ unsigned creation_flags;
+ enum yasl_private_presenter presenter;
+ struct yasl_surface *parent;
+ uint32_t *committed_pixels;
+ int committed_width, committed_height;
+ /* Window values last reported through public WINDOW events. */
+ struct yasl_private_vector2i reported_size;
+ uint32_t reported_scale_120, reported_state;
+ bool refresh_owed;
+ bool refresh_active;
+ void (*refresh_callback)(struct yasl_surface *, void *);
+ void *refresh_userdata;
+ bool text_input_active;
+ unsigned text_purpose;
+ char *text_surrounding;
+ size_t text_length, text_begin, text_end;
+ uint64_t text_revision, text_generation;
+ struct yasl_private_display *display;
void *native_handle;
- struct yasl_vector2i size;
+ struct yasl_private_vector2i size;
uint32_t scale_120;
- uint32_t state; /* Authoritative YASL_STATE_* bitmask, maintained by the backend. */
+ uint32_t state; /* Authoritative YASL_PRIVATE_STATE_* bitmask, maintained by the backend. */
uint32_t bg_color;
- struct yasl_window_cbs callbacks;
+ struct yasl_private_window_cbs callbacks;
void *ud;
uint64_t frame_generation;
struct input_queue input;
- enum yasl_pointer_mode pointer_mode;
- struct yasl_frame_clock clock;
+ enum yasl_private_pointer_mode pointer_mode;
+ struct yasl_private_frame_clock clock;
uint64_t frames_skipped;
- yasl_timer_id *timers;
+ yasl_private_timer_id *timers;
int timer_count;
int timer_capacity;
- void *app_surface;
struct yasl_surface *event_next;
uint64_t id;
void *userdata;
@@ -51,40 +67,41 @@ struct yasl_surface {
bool frame_presented;
uint32_t reported_scale;
uint64_t draw_index;
- struct yasl_frame *active_frame;
- yasl_pacing_mode pacing;
- yasl_timer_id pacing_timer;
+ struct yasl_private_frame *active_frame;
+ yasl_private_pacing_mode pacing;
+ yasl_private_timer_id pacing_timer;
};
-bool yasl_events_open(struct yasl_display *d);
-void yasl_events_close(struct yasl_display *d);
-int yasl_events_error(struct yasl_display *d);
-bool yasl_events_pending(struct yasl_display *d);
-struct yasl_surface *yasl_events_find(struct yasl_display *d, uint64_t id);
-bool yasl_events_register(struct yasl_surface *s);
-void yasl_events_unregister(struct yasl_surface *s);
-bool yasl_events_push(struct yasl_display *d, struct yasl_surface *queue_owner,
- const struct yasl_event *event);
-bool yasl_events_poll_surface(struct yasl_surface *s, struct yasl_event *event);
-void yasl_surface_notify_configure(struct yasl_surface *s, struct yasl_geometry geometry);
-void yasl_surface_notify_close(struct yasl_surface *s);
-void yasl_surface_notify_state(struct yasl_surface *s, uint32_t state);
-void yasl_surface_notify_drag(struct yasl_surface *s, enum yasl_drag_event ev, struct yasl_vector2i pos);
-void yasl_surface_notify_drop(struct yasl_surface *s, enum yasl_drop_event ev, struct yasl_vector2i pos);
-void yasl_surface_notify_composition(struct yasl_surface *s, const char *text, int begin, int end);
-void yasl_surface_draw(struct yasl_surface *s, struct yasl_frame *frame);
-void yasl_display_notify_event(struct yasl_display *d, enum yasl_event_type type);
-void yasl_surface_complete_paste(struct yasl_surface *s, uint64_t request_id,
- enum yasl_transfer_status status, const char *text, size_t len);
-uint64_t yasl_events_request_id(struct yasl_display *d);
+bool yasl_private_events_open(struct yasl_private_display *d);
+void yasl_private_events_close(struct yasl_private_display *d);
+int yasl_private_events_error(struct yasl_private_display *d);
+bool yasl_private_events_pending(struct yasl_private_display *d);
+struct yasl_surface *yasl_private_events_find(struct yasl_private_display *d, uint64_t id);
+bool yasl_private_events_register(struct yasl_surface *s);
+void yasl_private_events_unregister(struct yasl_surface *s);
+bool yasl_private_events_push(struct yasl_private_display *d, struct yasl_surface *queue_owner,
+ const struct yasl_private_event *event);
+bool yasl_private_events_poll_surface(struct yasl_surface *s, struct yasl_private_event *event);
+void yasl_private_surface_notify_configure(struct yasl_surface *s, struct yasl_private_geometry geometry);
+void yasl_private_surface_notify_refresh(struct yasl_surface *s);
+void yasl_private_surface_notify_close(struct yasl_surface *s);
+void yasl_private_surface_notify_state(struct yasl_surface *s, uint32_t state);
+void yasl_private_surface_notify_drag(struct yasl_surface *s, enum yasl_private_drag_event ev, struct yasl_private_vector2i pos);
+void yasl_private_surface_notify_drop(struct yasl_surface *s, enum yasl_private_drop_event ev, struct yasl_private_vector2i pos);
+void yasl_private_surface_notify_composition(struct yasl_surface *s, const char *text, int begin, int end);
+void yasl_private_surface_draw(struct yasl_surface *s, struct yasl_private_frame *frame);
+void yasl_private_display_notify_event(struct yasl_private_display *d, enum yasl_private_event_type type);
+void yasl_private_surface_complete_paste(struct yasl_surface *s, uint64_t request_id,
+ enum yasl_private_transfer_status status, const char *text, size_t len);
+uint64_t yasl_private_events_request_id(struct yasl_private_display *d);
-void yasl_surface_clear_timers(struct yasl_surface *s);
-struct yasl_surface *yasl_create_toplevel(struct yasl_display *d,
- const struct yasl_window_spec *spec, void *graphics_device);
+void yasl_private_surface_clear_timers(struct yasl_surface *s);
+struct yasl_surface *yasl_private_create_toplevel(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *graphics_device);
/* Report a presented CPU frame's extent, and copy it when out can hold it. */
-static inline bool yasl_copy_frame(const uint32_t *pixels, struct yasl_vector2i extent,
- uint32_t *out, size_t cap, struct yasl_vector2i *size)
+static inline bool yasl_private_copy_frame(const uint32_t *pixels, struct yasl_private_vector2i extent,
+ uint32_t *out, size_t cap, struct yasl_private_vector2i *size)
{
*size = extent;
size_t count = (size_t)extent.x * (size_t)extent.y;
@@ -93,83 +110,92 @@ static inline bool yasl_copy_frame(const uint32_t *pixels, struct yasl_vector2i
return true;
}
-static inline uint64_t yasl_frame_begin(struct yasl_surface *s)
+static inline uint64_t yasl_private_frame_begin(struct yasl_surface *s)
{
if (++s->frame_generation == 0) ++s->frame_generation;
return s->frame_generation;
}
-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);
- 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);
+typedef void (*yasl_private_gl_proc)(void);
+struct yasl_private_backend_ops {
+ struct yasl_surface *(*surface_create_gl)(struct yasl_private_display *, const struct yasl_private_window_spec *, const int *, struct yasl_surface *share);
+ bool (*gl_make_current)(struct yasl_private_display *, struct yasl_surface *);
+ yasl_private_gl_proc (*gl_get_proc_address)(struct yasl_private_display *, const char *);
+ bool (*gl_swap_interval)(struct yasl_private_display *, struct yasl_surface *, int);
+ bool (*gl_swap_buffers)(struct yasl_private_display *, struct yasl_surface *);
+ void *(*surface_get_metal_layer)(struct yasl_surface *);
+ bool (*surface_update_text_input)(struct yasl_surface *);
+
+ enum yasl_private_backend type;
+ struct yasl_private_display *(*display_open)(struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec);
+ void (*display_close)(struct yasl_private_display *d);
+ uint32_t (*display_caps)(const struct yasl_private_display *d);
+ int (*display_dispatch_timeout)(struct yasl_private_display *d, int timeout_ms);
+ void (*display_flush)(struct yasl_private_display *d);
+ int (*display_modifiers)(const struct yasl_private_display *d);
/* Optional; returns zero or less when the system reports no interval. */
- int (*display_double_click_ms)(const struct yasl_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_copy_text)(struct yasl_display *d, enum yasl_selection sel, const char *text);
- bool (*display_request_paste)(struct yasl_display *d, enum yasl_selection sel,
+ int (*display_double_click_ms)(const struct yasl_private_display *d);
+ void (*display_copy)(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text);
+ void (*display_paste)(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target);
+ bool (*display_copy_text)(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text);
+ bool (*display_request_paste)(struct yasl_private_display *d, enum yasl_private_selection sel,
struct yasl_surface *target, uint64_t request_id);
- 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);
+ bool (*display_drag_start)(struct yasl_private_display *d, struct yasl_surface *origin, const struct yasl_private_drag_image *image, const struct yasl_private_drag_listener *l, void *ud);
+ void (*display_drag_cancel)(struct yasl_private_display *d);
/* Create with callbacks disabled; common code installs them after return. */
- struct yasl_surface *(*surface_create_toplevel)(struct yasl_display *d,
- const struct yasl_window_spec *spec, void *graphics_device);
- 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);
+ struct yasl_surface *(*surface_create_toplevel)(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *graphics_device);
+ struct yasl_surface *(*surface_create_popup)(struct yasl_surface *parent, const struct yasl_private_popup_spec *spec);
+ bool (*surface_popup_reposition)(struct yasl_surface *popup, const struct yasl_private_popup_spec *spec);
void (*surface_destroy)(struct yasl_surface *s);
struct input_queue *(*surface_input_queue)(struct yasl_surface *s);
- bool (*surface_set_size)(struct yasl_surface *s, struct yasl_vector2i size_logical);
+ bool (*surface_set_size)(struct yasl_surface *s, struct yasl_private_vector2i size_logical);
bool (*surface_set_fullscreen)(struct yasl_surface *s, bool enabled);
bool (*surface_set_maximized)(struct yasl_surface *s, bool enabled);
bool (*surface_minimize)(struct yasl_surface *s);
- struct yasl_display *(*surface_display)(const struct yasl_surface *s);
+ struct yasl_private_display *(*surface_display)(const struct yasl_surface *s);
/* Copy only; must not invoke application callbacks. */
- void (*surface_set_callbacks)(struct yasl_surface *s, const struct yasl_window_cbs *cbs, void *ud);
+ void (*surface_set_callbacks)(struct yasl_surface *s, const struct yasl_private_window_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_present)(struct yasl_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage);
/* Optional; NULL means the common input queue is drained. */
- bool (*surface_poll_event)(struct yasl_surface *s, struct yasl_event *ev);
- /* Optional; NULL means only YASL_POINTER_NORMAL is supported. Backends that
+ bool (*surface_poll_event)(struct yasl_surface *s, struct yasl_private_event *ev);
+ /* Optional; NULL means only YASL_PRIVATE_POINTER_NORMAL is supported. Backends that
* implement RELATIVE push POINTER_MOTION with delta set and pos frozen. */
- bool (*surface_set_pointer_mode)(struct yasl_surface *s, enum yasl_pointer_mode mode);
- struct yasl_geometry (*surface_geometry)(const struct yasl_surface *s);
- struct yasl_timing (*surface_timing)(const struct yasl_surface *s);
+ bool (*surface_set_pointer_mode)(struct yasl_surface *s, enum yasl_private_pointer_mode mode);
+ struct yasl_private_geometry (*surface_geometry)(const struct yasl_surface *s);
+ struct yasl_private_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_cursor)(struct yasl_surface *s, enum yasl_private_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);
+ void (*surface_set_ime_caret)(struct yasl_surface *s, struct yasl_private_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);
+ bool (*surface_start_interactive_resize)(struct yasl_surface *s, enum yasl_private_resize_edge edge);
+ bool (*surface_show_window_menu)(struct yasl_surface *s, struct yasl_private_vector2i pos);
+ bool (*surface_drag_carry)(struct yasl_surface *s, struct yasl_private_vector2i offset);
+ bool (*surface_place_under_pointer)(struct yasl_surface *s, struct yasl_private_vector2i at);
/* Optional; NULL means the bell is unsupported. */
bool (*surface_bell)(struct yasl_surface *s);
/* Optional; NULL means no readback. Fills size only for an available frame. */
bool (*surface_read_pixels)(struct yasl_surface *s, uint32_t *out, size_t cap,
- struct yasl_vector2i *size);
- void *(*display_get_native_handle)(struct yasl_display *d);
+ struct yasl_private_vector2i *size);
+ void *(*display_get_native_handle)(struct yasl_private_display *d);
void *(*surface_get_native_handle)(struct yasl_surface *s);
bool (*surface_get_native_frame)(struct yasl_surface *s,
- const struct yasl_framebuffer *target, struct yasl_native_frame *out);
+ const struct yasl_private_framebuffer *target, struct yasl_private_native_frame *out);
};
#if YASL_HAS_WAYLAND
-extern const struct yasl_backend_ops yasl_wayland_backend;
+extern const struct yasl_private_backend_ops yasl_private_wayland_backend;
#endif
#if YASL_HAS_X11
-extern const struct yasl_backend_ops yasl_x11_backend;
+extern const struct yasl_private_backend_ops yasl_private_x11_backend;
#endif
#if YASL_HAS_COCOA
-extern const struct yasl_backend_ops yasl_cocoa_backend;
+extern const struct yasl_private_backend_ops yasl_private_cocoa_backend;
#endif
#endif
diff --git a/src/common/desktop.c b/src/common/desktop.c
index 23c62d7..5045604 100644
--- a/src/common/desktop.c
+++ b/src/common/desktop.c
@@ -1,4 +1,4 @@
-#include "yasl/desktop.h"
+#include "common/private_desktop.h"
#include "common/backend.h"
#include "common/desktop.h"
@@ -36,46 +36,60 @@ static size_t copy_out(char *out, size_t cap, const char *s, size_t len)
return len;
}
-bool yasl_display_open_uri(struct yasl_display *d, const char *uri)
+bool yasl_private_display_open_uri(struct yasl_private_display *d, const char *uri)
{
if (!d || !uri || !uri_valid(uri)) return false;
return desktop_open_uri(uri);
}
-bool yasl_display_notify(struct yasl_display *d, const char *title, const char *body)
+bool yasl_private_display_notify(struct yasl_private_display *d, const char *title, const char *body)
{
if (!d || !title) return false;
return desktop_notify(d->app_id, title, body ? body : "");
}
-uint32_t yasl_display_appearance(struct yasl_display *d)
+uint32_t yasl_private_display_appearance(struct yasl_private_display *d)
{
return d ? desktop_appearance() : 0;
}
-bool yasl_surface_bell(struct yasl_surface *s)
+bool yasl_private_surface_bell(struct yasl_surface *s)
{
if (!s || !s->display->ops->surface_bell) return false;
return s->display->ops->surface_bell(s);
}
-size_t yasl_executable_path(char *out, size_t cap)
+size_t yasl_private_executable_path(char *out, size_t cap)
{
if (!out && cap) return 0;
char buf[DESKTOP_STR_MAX];
return copy_out(out, cap, buf, desktop_executable_path(buf, sizeof(buf)));
}
-size_t yasl_resource_dir(char *out, size_t cap)
+size_t yasl_private_resource_dir(char *out, size_t cap)
{
if (!out && cap) return 0;
char buf[DESKTOP_STR_MAX];
return copy_out(out, cap, buf, desktop_resource_dir(buf, sizeof(buf)));
}
-size_t yasl_default_locale(char *out, size_t cap)
+size_t yasl_private_default_locale(char *out, size_t cap)
{
if (!out && cap) return 0;
char buf[DESKTOP_STR_MAX];
return copy_out(out, cap, buf, desktop_default_locale(buf, sizeof(buf)));
}
+
+#include "public_internal.h"
+#include "yasl/desktop.h"
+#include <errno.h>
+bool yasl_open_uri(const char *uri)
+{ if(!uri||!uri_valid(uri))return yasl_public_fail(EINVAL);errno=0;return desktop_open_uri(uri)||yasl_public_fail(errno); }
+bool yasl_notify(const char *title,const char *body)
+{ if(!yasl_public_owner())return false;errno=0;return yasl_private_display_notify(yasl_public_display(),title,body)||yasl_public_fail(errno); }
+unsigned yasl_appearance(void) { return yasl_private_display_appearance(yasl_public_display()); }
+bool yasl_surface_bell(yasl_surface *s)
+{ if(!yasl_public_surface(s,false))return false;errno=0;return yasl_private_surface_bell(s)||yasl_public_fail(errno); }
+size_t yasl_executable_path(char *out,size_t cap) { return yasl_private_executable_path(out,cap); }
+size_t yasl_resource_dir(char *out,size_t cap) { return yasl_private_resource_dir(out,cap); }
+size_t yasl_default_locale(char *out,size_t cap) { return yasl_private_default_locale(out,cap); }
diff --git a/src/common/desktop.h b/src/common/desktop.h
index 9af5952..68763b2 100644
--- a/src/common/desktop.h
+++ b/src/common/desktop.h
@@ -1,5 +1,5 @@
-#ifndef YASL_COMMON_DESKTOP_H
-#define YASL_COMMON_DESKTOP_H
+#ifndef YASL_PRIVATE_COMMON_DESKTOP_H
+#define YASL_PRIVATE_COMMON_DESKTOP_H
#include <stdbool.h>
#include <stddef.h>
diff --git a/src/common/events.c b/src/common/events.c
index a9bbcaf..ba78d77 100644
--- a/src/common/events.c
+++ b/src/common/events.c
@@ -6,15 +6,15 @@
#include <string.h>
struct event_node {
- struct yasl_event event;
+ struct yasl_private_event event;
uint64_t queue_id;
char *text;
void (*release)(void *);
struct event_node *next;
};
-struct yasl_event_state {
- yasl_mutex mutex;
+struct yasl_private_event_state {
+ yasl_private_mutex mutex;
struct event_node *head, *tail, *last;
uint64_t sequence, identity, request;
int error;
@@ -37,67 +37,67 @@ static void dispose_list(struct event_node *nodes)
}
}
-bool yasl_events_open(struct yasl_display *d)
+bool yasl_private_events_open(struct yasl_private_display *d)
{
- struct yasl_event_state *state = calloc(1, sizeof(*state));
+ struct yasl_private_event_state *state = calloc(1, sizeof(*state));
if (!state) { errno = ENOMEM; return false; }
- int error = yasl_mutex_init(&state->mutex);
+ int error = yasl_private_mutex_init(&state->mutex);
if (error) { free(state); errno = error; return false; }
d->events = state;
return true;
}
-void yasl_events_close(struct yasl_display *d)
+void yasl_private_events_close(struct yasl_private_display *d)
{
- struct yasl_event_state *state = d->events;
+ struct yasl_private_event_state *state = d->events;
if (!state) return;
/* Producers must already have stopped. Detach before callbacks so cleanup
* can make harmless failed posts without touching a dying queue. */
d->events = NULL;
dispose_list(state->head);
dispose(state->last);
- yasl_mutex_destroy(&state->mutex);
+ yasl_private_mutex_destroy(&state->mutex);
free(state);
}
-int yasl_events_error(struct yasl_display *d)
+int yasl_private_events_error(struct yasl_private_display *d)
{
if (!d->events) return 0;
- yasl_mutex_lock(&d->events->mutex);
+ yasl_private_mutex_lock(&d->events->mutex);
int error = d->events->error;
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
return error;
}
-bool yasl_events_pending(struct yasl_display *d)
+bool yasl_private_events_pending(struct yasl_private_display *d)
{
if (!d->events) return false;
- yasl_mutex_lock(&d->events->mutex);
+ yasl_private_mutex_lock(&d->events->mutex);
bool pending = d->events->head != NULL;
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
return pending;
}
-struct yasl_surface *yasl_events_find(struct yasl_display *d, uint64_t id)
+struct yasl_surface *yasl_private_events_find(struct yasl_private_display *d, uint64_t id)
{
for (struct yasl_surface *s = d->surfaces; s; s = s->event_next)
if (s->id == id) return s;
return NULL;
}
-static bool publish_input(const struct yasl_event *event, void *ud)
+static bool publish_input(const struct yasl_private_event *event, void *ud)
{
struct yasl_surface *owner = ud;
- return yasl_events_push(owner->display, owner, event);
+ return yasl_private_events_push(owner->display, owner, event);
}
-bool yasl_events_register(struct yasl_surface *s)
+bool yasl_private_events_register(struct yasl_surface *s)
{
- struct yasl_display *d = s->display;
+ struct yasl_private_display *d = s->display;
if (!d->events) return true; /* Internal test doubles need no queue. */
- yasl_mutex_lock(&d->events->mutex);
+ yasl_private_mutex_lock(&d->events->mutex);
if (d->events->identity == UINT64_MAX) {
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
errno = EOVERFLOW;
return false;
}
@@ -105,26 +105,26 @@ bool yasl_events_register(struct yasl_surface *s)
s->id = ++d->events->identity;
s->event_next = d->surfaces;
d->surfaces = s;
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
struct input_queue *q = d->ops->surface_input_queue
? d->ops->surface_input_queue(s) : &s->input;
if (q) {
/* Preserve input generated during native creation. */
- struct yasl_event event;
+ struct yasl_private_event event;
while (input_queue_pop(q, &event))
- yasl_events_push(d, s, &event);
+ yasl_private_events_push(d, s, &event);
q->publish = publish_input;
q->publish_ud = s;
}
return true;
}
-void yasl_events_unregister(struct yasl_surface *s)
+void yasl_private_events_unregister(struct yasl_surface *s)
{
- struct yasl_display *d = s->display;
+ struct yasl_private_display *d = s->display;
if (!d->events) return;
struct event_node *discarded = NULL;
- yasl_mutex_lock(&d->events->mutex);
+ yasl_private_mutex_lock(&d->events->mutex);
struct yasl_surface **surface = &d->surfaces;
while (*surface && *surface != s) surface = &(*surface)->event_next;
if (*surface) *surface = s->event_next;
@@ -142,30 +142,30 @@ void yasl_events_unregister(struct yasl_surface *s)
}
}
s->event_next = NULL;
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
dispose_list(discarded);
}
-static bool append(struct yasl_display *d, struct event_node *node)
+static bool append(struct yasl_private_display *d, struct event_node *node)
{
- struct yasl_event_state *state = d->events;
+ struct yasl_private_event_state *state = d->events;
if (state->sequence == UINT64_MAX) { errno = EOVERFLOW; return false; }
node->event.sequence = ++state->sequence;
- if (!node->event.time_ns) node->event.time_ns = yasl_loop_now_ns();
+ if (!node->event.time_ns) node->event.time_ns = yasl_private_loop_now_ns();
if (state->tail) state->tail->next = node;
else state->head = node;
state->tail = node;
return true;
}
-static bool string_event(enum yasl_event_type type)
+static bool string_event(enum yasl_private_event_type type)
{
- return type == YASL_EVENT_TEXT || type == YASL_EVENT_PASTE ||
- type == YASL_EVENT_DROP || type == YASL_EVENT_COMPOSITION;
+ return type == YASL_PRIVATE_EVENT_TEXT || type == YASL_PRIVATE_EVENT_PASTE ||
+ type == YASL_PRIVATE_EVENT_DROP || type == YASL_PRIVATE_EVENT_COMPOSITION || type == YASL_PRIVATE_EVENT_TEXT_EDIT;
}
-bool yasl_events_push(struct yasl_display *d, struct yasl_surface *queue_owner,
- const struct yasl_event *event)
+bool yasl_private_events_push(struct yasl_private_display *d, struct yasl_surface *queue_owner,
+ const struct yasl_private_event *event)
{
if (!d || !d->events || (event->surface && !event->surface->id)) return true;
struct event_node *node = calloc(1, sizeof(*node));
@@ -182,56 +182,57 @@ bool yasl_events_push(struct yasl_display *d, struct yasl_surface *queue_owner,
if (!node->text) { free(node); goto failed; }
if (event->text.len) memcpy(node->text, event->text.data, event->text.len);
node->text[event->text.len] = '\0';
- if (event->type == YASL_EVENT_PASTE) node->event.paste.data = node->text;
- else if (event->type == YASL_EVENT_COMPOSITION) node->event.composition.data = node->text;
+ if (event->type == YASL_PRIVATE_EVENT_PASTE) node->event.paste.data = node->text;
+ else if (event->type == YASL_PRIVATE_EVENT_TEXT_EDIT) node->event.text_edit.data = node->text;
+ else if (event->type == YASL_PRIVATE_EVENT_COMPOSITION) node->event.composition.data = node->text;
else node->event.text.data = node->text;
}
- yasl_mutex_lock(&d->events->mutex);
- if ((node->event.surface_id && !yasl_events_find(d, node->event.surface_id)) ||
- (node->queue_id && !yasl_events_find(d, node->queue_id))) {
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_lock(&d->events->mutex);
+ if ((node->event.surface_id && !yasl_private_events_find(d, node->event.surface_id)) ||
+ (node->queue_id && !yasl_private_events_find(d, node->queue_id))) {
+ yasl_private_mutex_unlock(&d->events->mutex);
dispose(node);
return true; /* A native teardown notification for a canceled recipient. */
}
bool ok = append(d, node);
if (!ok) d->events->error = errno;
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
if (!ok) dispose(node);
return ok;
failed:
- yasl_mutex_lock(&d->events->mutex);
+ yasl_private_mutex_lock(&d->events->mutex);
d->events->error = ENOMEM;
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
errno = ENOMEM;
return false;
}
-bool yasl_display_post(struct yasl_display *d, const struct yasl_message *message)
+bool yasl_private_display_post(struct yasl_private_display *d, const struct yasl_private_message *message)
{
if (!d || !d->events || !message) { errno = EINVAL; return false; }
struct event_node *node = calloc(1, sizeof(*node));
if (!node) { errno = ENOMEM; return false; }
- node->event = (struct yasl_event){
- .type = YASL_EVENT_USER, .surface_id = message->target_id,
+ node->event = (struct yasl_private_event){
+ .type = YASL_PRIVATE_EVENT_USER, .surface_id = message->target_id,
.user = {.code = message->code, .payload = message->payload, .size = message->size},
};
node->queue_id = message->target_id;
node->release = message->release;
- yasl_mutex_lock(&d->events->mutex);
- bool ok = !message->target_id || yasl_events_find(d, message->target_id);
+ yasl_private_mutex_lock(&d->events->mutex);
+ bool ok = !message->target_id || yasl_private_events_find(d, message->target_id);
if (!ok) errno = ENOENT;
else ok = append(d, node);
- yasl_mutex_unlock(&d->events->mutex);
+ yasl_private_mutex_unlock(&d->events->mutex);
if (!ok) { free(node); return false; }
- if (d->loop) yasl_loop_kick(d->loop);
+ if (d->loop) yasl_private_loop_kick(d->loop);
return true;
}
-static bool poll_event(struct yasl_display *d, uint64_t queue_id, struct yasl_event *event)
+static bool poll_event(struct yasl_private_display *d, uint64_t queue_id, struct yasl_private_event *event)
{
if (!d || !d->events || !event) return false;
- struct yasl_event_state *state = d->events;
- yasl_mutex_lock(&state->mutex);
+ struct yasl_private_event_state *state = d->events;
+ yasl_private_mutex_lock(&state->mutex);
struct event_node *discarded = NULL;
struct event_node **entry = &state->head;
struct event_node *previous = NULL;
@@ -241,8 +242,8 @@ static bool poll_event(struct yasl_display *d, uint64_t queue_id, struct yasl_ev
* Mixing this adapter with display polling is deliberately unsupported. */
while (*entry) {
struct event_node *candidate = *entry;
- if (candidate->event.type < YASL_EVENT_KEY ||
- candidate->event.type > YASL_EVENT_INPUT_RESET) {
+ if (candidate->event.type < YASL_PRIVATE_EVENT_KEY ||
+ candidate->event.type > YASL_PRIVATE_EVENT_INPUT_RESET) {
*entry = candidate->next;
if (state->tail == candidate) state->tail = previous;
candidate->next = discarded;
@@ -261,7 +262,7 @@ static bool poll_event(struct yasl_display *d, uint64_t queue_id, struct yasl_ev
}
struct event_node *node = *entry;
if (!node) {
- yasl_mutex_unlock(&state->mutex);
+ yasl_private_mutex_unlock(&state->mutex);
dispose_list(discarded);
return false;
}
@@ -269,104 +270,105 @@ static bool poll_event(struct yasl_display *d, uint64_t queue_id, struct yasl_ev
if (state->tail == node) state->tail = previous;
node->next = NULL;
if (node->event.surface_id)
- node->event.surface = yasl_events_find(d, node->event.surface_id);
+ node->event.surface = yasl_private_events_find(d, node->event.surface_id);
struct event_node *last = state->last;
state->last = node;
*event = node->event;
- yasl_mutex_unlock(&state->mutex);
+ yasl_private_mutex_unlock(&state->mutex);
dispose(last);
dispose_list(discarded);
return true;
}
-bool yasl_display_poll_event(struct yasl_display *d, struct yasl_event *event)
+bool yasl_private_display_poll_event(struct yasl_private_display *d, struct yasl_private_event *event)
{
return poll_event(d, 0, event);
}
-bool yasl_events_poll_surface(struct yasl_surface *s, struct yasl_event *event)
+bool yasl_private_events_poll_surface(struct yasl_surface *s, struct yasl_private_event *event)
{
return s && s->id ? poll_event(s->display, s->id, event) : false;
}
-uint64_t yasl_events_request_id(struct yasl_display *d)
+uint64_t yasl_private_events_request_id(struct yasl_private_display *d)
{
if (!d->events || d->events->request == UINT64_MAX) { errno = EOVERFLOW; return 0; }
return ++d->events->request;
}
-uint64_t yasl_surface_id(const struct yasl_surface *s) { return s ? s->id : 0; }
-void yasl_surface_set_userdata(struct yasl_surface *s, void *ud) { if (s) s->userdata = ud; }
-void *yasl_surface_userdata(const struct yasl_surface *s) { return s ? s->userdata : NULL; }
+uint64_t yasl_private_surface_id(const struct yasl_surface *s) { return s ? s->id : 0; }
+void yasl_private_surface_set_userdata(struct yasl_surface *s, void *ud) { if (s) s->userdata = ud; }
+void *yasl_private_surface_userdata(const struct yasl_surface *s) { return s ? s->userdata : NULL; }
-void yasl_surface_notify_configure(struct yasl_surface *s, struct yasl_geometry geometry)
+void yasl_private_surface_notify_configure(struct yasl_surface *s, struct yasl_private_geometry geometry)
{
uint32_t previous_scale = s->reported_scale;
s->reported_scale = geometry.scale_120;
s->size = geometry.size;
s->scale_120 = geometry.scale_120;
- struct yasl_event event = {.type = YASL_EVENT_WINDOW_RESIZE, .surface = s,
+ struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_WINDOW_RESIZE, .surface = s,
.resize = {.geometry = geometry, .scale_changed = previous_scale != geometry.scale_120}};
- yasl_events_push(s->display, s, &event);
- if (s->display) yasl_surface_request_frame(s);
+ yasl_private_events_push(s->display, s, &event);
+ if (s->display) yasl_private_surface_request_frame(s);
if (s->callbacks.configure) s->callbacks.configure(s, geometry, s->ud);
}
-void yasl_surface_notify_close(struct yasl_surface *s)
+void yasl_private_surface_notify_close(struct yasl_surface *s)
{
- struct yasl_event event = {.type = YASL_EVENT_WINDOW_CLOSE, .surface = s};
- yasl_events_push(s->display, s, &event);
+ struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_WINDOW_CLOSE, .surface = s};
+ yasl_private_events_push(s->display, s, &event);
if (s->callbacks.close) s->callbacks.close(s, s->ud);
}
-void yasl_surface_notify_state(struct yasl_surface *s, uint32_t flags)
+void yasl_private_surface_notify_state(struct yasl_surface *s, uint32_t flags)
{
s->state = flags;
- struct yasl_event event = {.type = YASL_EVENT_WINDOW_STATE, .surface = s, .state.flags = flags};
- yasl_events_push(s->display, s, &event);
+ struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_WINDOW_STATE, .surface = s, .state.flags = flags};
+ yasl_private_events_push(s->display, s, &event);
if (s->callbacks.state) s->callbacks.state(s, flags, s->ud);
}
-void yasl_surface_notify_drag(struct yasl_surface *s, enum yasl_drag_event action, struct yasl_vector2i pos)
+void yasl_private_surface_notify_drag(struct yasl_surface *s, enum yasl_private_drag_event action, struct yasl_private_vector2i pos)
{
- struct yasl_event event = {.type = YASL_EVENT_DRAG, .surface = s, .drag = {action, pos}};
- yasl_events_push(s->display, s, &event);
+ struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_DRAG, .surface = s, .drag = {action, pos}};
+ yasl_private_events_push(s->display, s, &event);
if (s->callbacks.drag) s->callbacks.drag(s, action, pos, s->ud);
}
-void yasl_surface_notify_drop(struct yasl_surface *s, enum yasl_drop_event action, struct yasl_vector2i pos)
+void yasl_private_surface_notify_drop(struct yasl_surface *s, enum yasl_private_drop_event action, struct yasl_private_vector2i pos)
{
- struct yasl_event event = {.type = YASL_EVENT_DROP_HOVER, .surface = s, .drop = {action, pos}};
- yasl_events_push(s->display, s, &event);
+ struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_DROP_HOVER, .surface = s, .drop = {action, pos}};
+ yasl_private_events_push(s->display, s, &event);
if (s->callbacks.drop) s->callbacks.drop(s, action, pos, s->ud);
}
-void yasl_surface_notify_composition(struct yasl_surface *s, const char *text, int begin, int end)
+void yasl_private_surface_notify_composition(struct yasl_surface *s, const char *text, int begin, int end)
{
- struct yasl_event event = {.type = YASL_EVENT_COMPOSITION, .surface = s,
+ struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_COMPOSITION, .surface = s,
.composition = {.data = text ? text : "", .len = text ? strlen(text) : 0,
- .begin = begin, .end = end, .active = text && text[0]}};
- yasl_events_push(s->display, s, &event);
+ .begin = begin, .end = end, .active = text && text[0],
+ .revision = s->text_revision, .generation = s->text_generation}};
+ yasl_private_events_push(s->display, s, &event);
}
-void yasl_surface_complete_paste(struct yasl_surface *s, uint64_t id,
- enum yasl_transfer_status status, const char *text, size_t len)
+void yasl_private_surface_complete_paste(struct yasl_surface *s, uint64_t id,
+ enum yasl_private_transfer_status status, const char *text, size_t len)
{
- struct yasl_event event = {.type = YASL_EVENT_PASTE, .surface = s,
+ struct yasl_private_event event = {.type = YASL_PRIVATE_EVENT_PASTE, .surface = s,
.paste = {.data = text ? text : "", .len = text ? len : 0, .request_id = id, .status = status}};
if (!s->display->events) {
struct input_queue *q = s->display->ops->surface_input_queue
? s->display->ops->surface_input_queue(s) : &s->input;
input_queue_push(q, &event);
- } else yasl_events_push(s->display, s, &event);
+ } else yasl_private_events_push(s->display, s, &event);
}
-void yasl_display_notify_event(struct yasl_display *d, enum yasl_event_type type)
+void yasl_private_display_notify_event(struct yasl_private_display *d, enum yasl_private_event_type type)
{
- struct yasl_event event = {.type = type};
- yasl_events_push(d, NULL, &event);
- void (*callback)(void *) = type == YASL_EVENT_DISPLAY_QUIT ? d->callbacks.quit
- : type == YASL_EVENT_DISPLAY_REOPEN ? d->callbacks.reopen
- : type == YASL_EVENT_DISPLAY_LOST ? d->callbacks.lost : NULL;
+ struct yasl_private_event event = {.type = type};
+ yasl_private_events_push(d, NULL, &event);
+ void (*callback)(void *) = type == YASL_PRIVATE_EVENT_DISPLAY_QUIT ? d->callbacks.quit
+ : type == YASL_PRIVATE_EVENT_DISPLAY_REOPEN ? d->callbacks.reopen
+ : type == YASL_PRIVATE_EVENT_DISPLAY_LOST ? d->callbacks.lost : NULL;
if (callback) callback(d->callback_ud);
}
diff --git a/src/common/gl_options.c b/src/common/gl_options.c
new file mode 100644
index 0000000..ec8f98a
--- /dev/null
+++ b/src/common/gl_options.c
@@ -0,0 +1,38 @@
+#include "gl_options.h"
+#include <errno.h>
+#include <stddef.h>
+bool yasl_private_gl_parse_options(const int *attrs, struct yasl_private_gl_options *out)
+{
+ if (!out) { errno = EINVAL; return false; }
+ struct yasl_private_gl_options o = {.major=3, .minor=2, .profile=YASL_PRIVATE_GL_CORE,
+ .red_bits=8, .green_bits=8, .blue_bits=8, .alpha_bits=8, .depth_bits=24, .stencil_bits=8};
+ if (attrs) for (size_t i=0;;i+=2) {
+ if (i >= 128) { errno=EINVAL; return false; }
+ int key=attrs[i];
+ if (key==YASL_PRIVATE_GL_END) break;
+ int v=attrs[i+1];
+ switch(key) {
+ case YASL_PRIVATE_GL_MAJOR: o.major=v; break;
+ case YASL_PRIVATE_GL_MINOR: o.minor=v; break;
+ case YASL_PRIVATE_GL_PROFILE: o.profile=v; break;
+ case YASL_PRIVATE_GL_DEBUG: o.debug=v; break;
+ case YASL_PRIVATE_GL_FORWARD_COMPAT: o.forward_compat=v; break;
+ case YASL_PRIVATE_GL_RED_BITS: o.red_bits=v; break;
+ case YASL_PRIVATE_GL_GREEN_BITS: o.green_bits=v; break;
+ case YASL_PRIVATE_GL_BLUE_BITS: o.blue_bits=v; break;
+ case YASL_PRIVATE_GL_ALPHA_BITS: o.alpha_bits=v; break;
+ case YASL_PRIVATE_GL_DEPTH_BITS: o.depth_bits=v; break;
+ case YASL_PRIVATE_GL_STENCIL_BITS: o.stencil_bits=v; break;
+ case YASL_PRIVATE_GL_SAMPLES: o.samples=v; break;
+ case YASL_PRIVATE_GL_SRGB: o.srgb=v; break;
+ case YASL_PRIVATE_GL_API: o.api=v; break;
+ default: errno=EINVAL; return false;
+ }
+ }
+ if (o.major<1 || o.major>4 || o.minor<0 || o.minor>9 || o.profile<0 || o.profile>2 ||
+ o.api<0 || o.api>1 || (unsigned)o.debug>1 || (unsigned)o.forward_compat>1 ||
+ (unsigned)o.srgb>1 || (unsigned)o.red_bits>64 || (unsigned)o.green_bits>64 ||
+ (unsigned)o.blue_bits>64 || (unsigned)o.alpha_bits>64 || (unsigned)o.depth_bits>64 ||
+ (unsigned)o.stencil_bits>64 || (unsigned)o.samples>64) { errno=EINVAL; return false; }
+ *out=o; return true;
+}
diff --git a/src/common/gl_options.h b/src/common/gl_options.h
new file mode 100644
index 0000000..eae38bb
--- /dev/null
+++ b/src/common/gl_options.h
@@ -0,0 +1,20 @@
+#ifndef YASL_PRIVATE_GL_OPTIONS_H
+#define YASL_PRIVATE_GL_OPTIONS_H
+#include <stdbool.h>
+/* Attribute values are shared with the public GL bridge; not installed. */
+enum {
+ YASL_PRIVATE_GL_END = 0, YASL_PRIVATE_GL_MAJOR = 1, YASL_PRIVATE_GL_MINOR,
+ YASL_PRIVATE_GL_PROFILE, YASL_PRIVATE_GL_DEBUG, YASL_PRIVATE_GL_FORWARD_COMPAT,
+ YASL_PRIVATE_GL_RED_BITS, YASL_PRIVATE_GL_GREEN_BITS, YASL_PRIVATE_GL_BLUE_BITS,
+ YASL_PRIVATE_GL_ALPHA_BITS, YASL_PRIVATE_GL_DEPTH_BITS, YASL_PRIVATE_GL_STENCIL_BITS,
+ YASL_PRIVATE_GL_SAMPLES, YASL_PRIVATE_GL_SRGB, YASL_PRIVATE_GL_API
+};
+enum { YASL_PRIVATE_GL_ANY = 0, YASL_PRIVATE_GL_CORE = 1, YASL_PRIVATE_GL_COMPATIBILITY = 2 };
+enum { YASL_PRIVATE_GL_OPENGL = 0, YASL_PRIVATE_GL_OPENGL_ES = 1 };
+struct yasl_private_gl_options {
+ int major, minor, profile, debug, forward_compat;
+ int red_bits, green_bits, blue_bits, alpha_bits, depth_bits, stencil_bits;
+ int samples, srgb, api;
+};
+bool yasl_private_gl_parse_options(const int *, struct yasl_private_gl_options *);
+#endif
diff --git a/src/common/input.c b/src/common/input.c
index 511cd0a..338ed2d 100644
--- a/src/common/input.c
+++ b/src/common/input.c
@@ -1,29 +1,29 @@
#include "input.h"
-#include "yasl/loop.h"
+#include "common/private_loop.h"
#include <stdlib.h>
#include <string.h>
-static bool has_text(const struct yasl_event *ev)
+static bool has_text(const struct yasl_private_event *ev)
{
- return ev->type == YASL_EVENT_TEXT || ev->type == YASL_EVENT_PASTE ||
- ev->type == YASL_EVENT_DROP;
+ return ev->type == YASL_PRIVATE_EVENT_TEXT || ev->type == YASL_PRIVATE_EVENT_PASTE ||
+ ev->type == YASL_PRIVATE_EVENT_DROP || ev->type == YASL_PRIVATE_EVENT_TEXT_EDIT;
}
static void discard_pending(struct input_queue *q)
{
for (size_t i = 0; i < q->count; i++) {
- struct yasl_event *ev = &q->events[(q->head + i) & (q->capacity - 1)];
+ struct yasl_private_event *ev = &q->events[(q->head + i) & (q->capacity - 1)];
if (has_text(ev)) free((void *)ev->text.data);
}
q->head = q->count = 0;
}
-bool input_queue_push(struct input_queue *q, const struct yasl_event *ev)
+bool input_queue_push(struct input_queue *q, const struct yasl_private_event *ev)
{
- struct yasl_event copy = *ev;
+ struct yasl_private_event copy = *ev;
char *owned = NULL;
- if (!copy.time_ns) copy.time_ns = yasl_loop_now_ns();
+ if (!copy.time_ns) copy.time_ns = yasl_private_loop_now_ns();
if (has_text(ev) && !q->publish) {
size_t len = ev->text.len;
if ((!ev->text.data && len) || len == SIZE_MAX) goto failed;
@@ -31,42 +31,43 @@ bool input_queue_push(struct input_queue *q, const struct yasl_event *ev)
if (!owned) goto failed;
if (len) memcpy(owned, ev->text.data, len);
owned[len] = '\0';
- if (ev->type == YASL_EVENT_PASTE) copy.paste.data = owned;
+ if (ev->type == YASL_PRIVATE_EVENT_PASTE) copy.paste.data = owned;
+ else if (ev->type == YASL_PRIVATE_EVENT_TEXT_EDIT) copy.text_edit.data = owned;
else copy.text.data = owned;
}
uint32_t buttons = q->buttons;
switch (ev->type) {
- case YASL_EVENT_POINTER_MOTION:
+ case YASL_PRIVATE_EVENT_POINTER_MOTION:
if (!q->relative) {
copy.pointer.delta = q->has_last_pos
- ? (struct yasl_vector2d){ev->pointer.pos.x - q->last_pos.x,
+ ? (struct yasl_private_vector2d){ev->pointer.pos.x - q->last_pos.x,
ev->pointer.pos.y - q->last_pos.y}
- : (struct yasl_vector2d){0, 0};
+ : (struct yasl_private_vector2d){0, 0};
}
q->last_pos = ev->pointer.pos;
q->has_last_pos = true;
break;
- case YASL_EVENT_POINTER_DOWN:
- case YASL_EVENT_POINTER_UP:
+ case YASL_PRIVATE_EVENT_POINTER_DOWN:
+ case YASL_PRIVATE_EVENT_POINTER_UP:
if ((unsigned)ev->pointer.button < 32) {
- uint32_t bit = YASL_BUTTON_BIT(ev->pointer.button);
- buttons = ev->type == YASL_EVENT_POINTER_DOWN ? buttons | bit : buttons & ~bit;
+ uint32_t bit = YASL_PRIVATE_BUTTON_BIT(ev->pointer.button);
+ buttons = ev->type == YASL_PRIVATE_EVENT_POINTER_DOWN ? buttons | bit : buttons & ~bit;
}
q->last_pos = ev->pointer.pos;
q->has_last_pos = true;
break;
- case YASL_EVENT_POINTER_LEAVE:
+ case YASL_PRIVATE_EVENT_POINTER_LEAVE:
if (q->leave_ends_press) buttons = 0;
q->has_last_pos = false;
break;
- case YASL_EVENT_INPUT_RESET:
+ case YASL_PRIVATE_EVENT_INPUT_RESET:
buttons = 0;
q->has_last_pos = false;
break;
default:
break;
}
- if (ev->type >= YASL_EVENT_POINTER_DOWN && ev->type <= YASL_EVENT_POINTER_LEAVE)
+ if (ev->type >= YASL_PRIVATE_EVENT_POINTER_DOWN && ev->type <= YASL_PRIVATE_EVENT_POINTER_LEAVE)
copy.pointer.buttons = buttons;
if (q->publish) {
q->buttons = buttons;
@@ -76,7 +77,7 @@ bool input_queue_push(struct input_queue *q, const struct yasl_event *ev)
size_t capacity = q->capacity ? q->capacity * 2 : 32;
if (capacity < q->capacity || capacity > SIZE_MAX / sizeof(*q->events))
goto failed;
- struct yasl_event *events = malloc(capacity * sizeof(*events));
+ struct yasl_private_event *events = malloc(capacity * sizeof(*events));
if (!events) goto failed;
if (q->events && q->count > 0) {
size_t first = q->capacity - q->head;
@@ -107,7 +108,7 @@ failed:
return false;
}
-bool input_queue_pop(struct input_queue *q, struct yasl_event *ev)
+bool input_queue_pop(struct input_queue *q, struct yasl_private_event *ev)
{
if (!ev || (!q->count && !q->reset_pending)) return false;
if (q->last_text) {
@@ -115,9 +116,9 @@ bool input_queue_pop(struct input_queue *q, struct yasl_event *ev)
q->last_text = NULL;
}
if (q->reset_pending) {
- *ev = (struct yasl_event){
- .type = YASL_EVENT_INPUT_RESET, .surface = q->reset_surface,
- .time_ns = yasl_loop_now_ns(),
+ *ev = (struct yasl_private_event){
+ .type = YASL_PRIVATE_EVENT_INPUT_RESET, .surface = q->reset_surface,
+ .time_ns = yasl_private_loop_now_ns(),
};
q->reset_pending = false;
return true;
@@ -132,7 +133,7 @@ bool input_queue_pop(struct input_queue *q, struct yasl_event *ev)
void input_queue_clear(struct input_queue *q)
{
- bool (*publish)(const struct yasl_event *, void *) = q->publish;
+ bool (*publish)(const struct yasl_private_event *, void *) = q->publish;
void *publish_ud = q->publish_ud;
discard_pending(q);
free(q->last_text);
diff --git a/src/common/input.h b/src/common/input.h
index 8980c55..d97ad17 100644
--- a/src/common/input.h
+++ b/src/common/input.h
@@ -1,27 +1,27 @@
-#ifndef YASL_INPUT_H
-#define YASL_INPUT_H
+#ifndef YASL_PRIVATE_INPUT_H
+#define YASL_PRIVATE_INPUT_H
-#include "yasl/yasl.h"
+#include "common/types.h"
/* Each queued string and the last successfully polled string are owned here. */
struct input_queue {
- struct yasl_event *events;
+ struct yasl_private_event *events;
size_t head, count, capacity;
char *last_text;
/* Last pointer position, for computing absolute-mode motion deltas. */
- struct yasl_vector2i last_pos;
+ struct yasl_private_vector2i last_pos;
bool has_last_pos;
- /* Set by backends while YASL_POINTER_RELATIVE is active: keep pushed deltas. */
+ /* Set by backends while YASL_PRIVATE_POINTER_RELATIVE is active: keep pushed deltas. */
bool relative;
struct yasl_surface *reset_surface;
bool reset_pending;
- /* YASL_BUTTON_BIT() mask from pushed DOWN/UP events. */
+ /* YASL_PRIVATE_BUTTON_BIT() mask from pushed DOWN/UP events. */
uint32_t buttons;
/* Set by backends whose pointer leave ends any press: no release follows. */
bool leave_ends_press;
/* Installed by common code for display-wide publication. Standalone queues
* retain their normal storage/pop semantics when publish is NULL. */
- bool (*publish)(const struct yasl_event *ev, void *ud);
+ bool (*publish)(const struct yasl_private_event *ev, void *ud);
void *publish_ud;
};
@@ -29,8 +29,8 @@ struct input_queue {
* POINTER_MOTION delta is computed from the previous position; otherwise the
* backend-supplied delta is kept. Leave and reset clear the position history.
* Pointer events get the held-button mask; reset clears it. */
-bool input_queue_push(struct input_queue *q, const struct yasl_event *ev);
-bool input_queue_pop(struct input_queue *q, struct yasl_event *ev);
+bool input_queue_push(struct input_queue *q, const struct yasl_private_event *ev);
+bool input_queue_pop(struct input_queue *q, struct yasl_private_event *ev);
void input_queue_clear(struct input_queue *q);
#endif
diff --git a/src/common/keyboard.h b/src/common/keyboard.h
index d8b8bbc..ce144fb 100644
--- a/src/common/keyboard.h
+++ b/src/common/keyboard.h
@@ -1,10 +1,10 @@
-#ifndef YASL_COMMON_KEYBOARD_H
-#define YASL_COMMON_KEYBOARD_H
+#ifndef YASL_PRIVATE_COMMON_KEYBOARD_H
+#define YASL_PRIVATE_COMMON_KEYBOARD_H
#include <stdint.h>
/* Layout labels omit controls, invalid Unicode, and Cocoa function-key PUA. */
-static inline uint32_t yasl_key_label(uint32_t cp)
+static inline uint32_t yasl_private_key_label(uint32_t cp)
{
return cp >= 0x20 && cp != 0x7f && cp <= 0x10ffff &&
!(cp >= 0x80 && cp <= 0x9f) && !(cp >= 0xd800 && cp <= 0xdfff) &&
diff --git a/src/common/loop.h b/src/common/loop.h
index 60c5b64..3a1c542 100644
--- a/src/common/loop.h
+++ b/src/common/loop.h
@@ -1,15 +1,15 @@
-#ifndef YASL_COMMON_LOOP_H
-#define YASL_COMMON_LOOP_H
+#ifndef YASL_PRIVATE_COMMON_LOOP_H
+#define YASL_PRIVATE_COMMON_LOOP_H
-#include "yasl/loop.h"
+#include "common/private_loop.h"
struct pollfd;
-int yasl_loop_next_timeout_ms(const struct yasl_loop *l);
+int yasl_private_loop_next_timeout_ms(const struct yasl_private_loop *l);
/* The loop's current poll set, valid until the next watch change. Backends
* that wait on native events use it to wake on loop readiness. */
-int yasl_loop_poll_set(const struct yasl_loop *l, const struct pollfd **fds);
+int yasl_private_loop_poll_set(const struct yasl_private_loop *l, const struct pollfd **fds);
/* Whether id is armed: added, not deleted, and not retired by its callback. */
-bool yasl_loop_timer_armed(const struct yasl_loop *l, yasl_timer_id id);
+bool yasl_private_loop_timer_armed(const struct yasl_private_loop *l, yasl_private_timer_id id);
-#endif /* YASL_COMMON_LOOP_H */
+#endif /* YASL_PRIVATE_COMMON_LOOP_H */
diff --git a/src/common/meson.build b/src/common/meson.build
index 1577132..819e4a8 100644
--- a/src/common/meson.build
+++ b/src/common/meson.build
@@ -1 +1,5 @@
-sources += files('app.c', 'backend.c', 'desktop.c', 'events.c', 'input.c', 'native.c', 'timing.c', 'uri.c', 'utf8.c')
+sources += files('backend.c', 'desktop.c', 'events.c', 'input.c', 'native.c', 'timing.c', 'uri.c', 'utf8.c', 'surface.c', 'gl_options.c')
+if has_vulkan
+ sources += files('vulkan.c')
+ deps += vulkan_dep
+endif
diff --git a/src/common/native.c b/src/common/native.c
index 42b065c..88604c9 100644
--- a/src/common/native.c
+++ b/src/common/native.c
@@ -2,7 +2,7 @@
#include <errno.h>
-void *yasl_display_get_native_handle(struct yasl_display *d)
+void *yasl_private_display_get_native_handle(struct yasl_private_display *d)
{
if (!d) return NULL;
if (d->native_handle) return d->native_handle;
@@ -10,7 +10,7 @@ void *yasl_display_get_native_handle(struct yasl_display *d)
d->ops->display_get_native_handle(d) : NULL;
}
-void *yasl_surface_get_native_handle(struct yasl_surface *s)
+void *yasl_private_surface_get_native_handle(struct yasl_surface *s)
{
if (!s) return NULL;
if (s->native_handle) return s->native_handle;
@@ -18,18 +18,18 @@ void *yasl_surface_get_native_handle(struct yasl_surface *s)
s->display->ops->surface_get_native_handle(s) : NULL;
}
-struct yasl_surface *yasl_surface_create_native(struct yasl_display *d,
- const struct yasl_window_spec *spec, void *graphics_device)
+struct yasl_surface *yasl_private_surface_create_native(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *graphics_device)
{
if (!graphics_device) {
errno = EINVAL;
return NULL;
}
- return yasl_create_toplevel(d, spec, graphics_device);
+ return yasl_private_create_toplevel(d, spec, graphics_device);
}
-bool yasl_surface_get_native_frame(struct yasl_surface *s,
- const struct yasl_framebuffer *target, struct yasl_native_frame *out)
+bool yasl_private_surface_get_native_frame(struct yasl_surface *s,
+ const struct yasl_private_framebuffer *target, struct yasl_private_native_frame *out)
{
if (!s || !target || !out || !target->generation ||
target->generation != s->frame_generation ||
@@ -37,3 +37,39 @@ bool yasl_surface_get_native_frame(struct yasl_surface *s,
return false;
return s->display->ops->surface_get_native_frame(s, target, out);
}
+
+#include "public_internal.h"
+#include "yasl/metal.h"
+#if YASL_HAS_WAYLAND
+#include "yasl/native/wayland.h"
+struct wl_display *yasl_wayland_display(const yasl_surface *s)
+{ return s && s->display->ops->type==YASL_PRIVATE_BACKEND_WAYLAND ? yasl_private_display_get_native_handle(s->display):NULL; }
+struct wl_surface *yasl_wayland_surface(const yasl_surface *s)
+{ return s && s->display->ops->type==YASL_PRIVATE_BACKEND_WAYLAND ? yasl_private_surface_get_native_handle((yasl_surface*)s):NULL; }
+#endif
+#if YASL_HAS_X11
+#include "yasl/native/x11.h"
+Display *yasl_x11_display(const yasl_surface *s)
+{ return s && s->display->ops->type==YASL_PRIVATE_BACKEND_X11 ? yasl_private_display_get_native_handle(s->display):NULL; }
+uintptr_t yasl_x11_window(const yasl_surface *s)
+{ return s && s->display->ops->type==YASL_PRIVATE_BACKEND_X11 ? (uintptr_t)yasl_private_surface_get_native_handle((yasl_surface*)s):0; }
+#endif
+#if YASL_HAS_COCOA
+#include "yasl/native/cocoa.h"
+#include "common/private_native_cocoa.h"
+NSApplication *yasl_cocoa_application(void)
+{ struct yasl_private_display *d=yasl_public_display();return d ? yasl_private_cocoa_display_get_application(d):NULL; }
+NSWindow *yasl_cocoa_window(const yasl_surface *s)
+{ return yasl_private_cocoa_surface_get_window((yasl_surface*)s); }
+NSView *yasl_cocoa_view(const yasl_surface *s)
+{ return yasl_private_cocoa_surface_get_view((yasl_surface*)s); }
+#endif
+CAMetalLayer *yasl_metal_layer(yasl_surface *s)
+{
+ if(!yasl_public_surface(s,false))return NULL;
+ if(s->presenter!=YASL_PRIVATE_PRESENTER_NONE && s->presenter!=YASL_PRIVATE_PRESENTER_METAL) {yasl_public_fail(EBUSY);return NULL;}
+ if(!s->display->ops->surface_get_metal_layer){yasl_public_fail(ENOTSUP);return NULL;}
+ CAMetalLayer *layer=s->display->ops->surface_get_metal_layer(s);
+ if(!layer){yasl_public_fail(ENOTSUP);return NULL;}
+ s->presenter=YASL_PRIVATE_PRESENTER_METAL;return layer;
+}
diff --git a/src/common/private_desktop.h b/src/common/private_desktop.h
new file mode 100644
index 0000000..b408585
--- /dev/null
+++ b/src/common/private_desktop.h
@@ -0,0 +1,113 @@
+/**
+ * @file
+ * Desktop services next to windowing: opening URIs, notifications, the bell,
+ * appearance preferences, and process paths.
+ *
+ * Display and surface functions use the display's opening thread (the main
+ * thread on Cocoa). Path and locale functions may be called from any thread.
+ * On Linux, notifications and appearance use the D-Bus session bus when YASL
+ * is built with libdbus-1; without it they report unsupported.
+ *
+ * Functions that fill a buffer return the full string length in bytes,
+ * excluding the NUL, or zero on failure. A result less than cap means out
+ * holds the NUL-terminated string. Otherwise out is unchanged; call again with
+ * a buffer of at least the result plus one.
+ */
+#ifndef YASL_PRIVATE_DESKTOP_H
+#define YASL_PRIVATE_DESKTOP_H
+
+#include "common/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Appearance preference flags returned by yasl_private_display_appearance(). */
+enum yasl_private_appearance : uint32_t {
+ YASL_PRIVATE_APPEARANCE_DARK = 1 << 0, /**< The user prefers dark colors. */
+ YASL_PRIVATE_APPEARANCE_LIGHT = 1 << 1, /**< The user prefers light colors. */
+ YASL_PRIVATE_APPEARANCE_HIGH_CONTRAST = 1 << 2, /**< The user asks for higher contrast. */
+};
+
+/**
+ * Open a URI with the user's default handler.
+ * @param d Display, or NULL.
+ * @param uri NUL-terminated absolute URI with a scheme, such as https: or file:.
+ * @return true if the request was handed to the system; false for NULL
+ * arguments, a missing scheme, or a failed launch.
+ * YASL checks only the URI syntax. The application decides which schemes and
+ * hosts it trusts. Success does not confirm that a handler opened the URI.
+ * Linux runs xdg-open from PATH in a new session, with default signal
+ * dispositions, an empty signal mask, stdin on /dev/null, and no descriptors
+ * above stderr. It forks a short-lived helper first, which raises SIGCHLD;
+ * YASL reaps it unless the application's reaper does. Cocoa uses NSWorkspace.
+ */
+bool yasl_private_display_open_uri(struct yasl_private_display *d, const char *uri);
+/**
+ * Show a desktop notification.
+ * @param d Display, or NULL.
+ * @param title NUL-terminated UTF-8 summary.
+ * @param body NUL-terminated UTF-8 body, or NULL for none.
+ * @return true if the request was sent; false for NULL arguments or when no
+ * notification service is available.
+ * Linux sends org.freedesktop.Notifications.Notify with the display's app_id
+ * as the application name and desktop-entry hint. Cocoa uses the user
+ * notification center, which needs an app bundle with a bundle identifier,
+ * and asks for permission on first use. Success does not confirm that the
+ * notification was shown.
+ */
+bool yasl_private_display_notify(struct yasl_private_display *d, const char *title, const char *body);
+/**
+ * Read the user's appearance preferences.
+ * @param d Display, or NULL.
+ * @return A yasl_private_appearance bitmask, or zero for NULL, no preference, or no
+ * way to read it.
+ * Linux reads color-scheme and contrast from the XDG Settings portal, which
+ * waits briefly for a reply, then falls back to the GTK_THEME variable. Cocoa
+ * reads the effective appearance and the increase-contrast setting. The
+ * result is not cached and no change notification is sent; query again when
+ * the window is focused.
+ */
+uint32_t yasl_private_display_appearance(struct yasl_private_display *d);
+/**
+ * Ring the system bell for a surface.
+ * @param s Surface, or NULL.
+ * @return true if the request was sent; false for NULL or when unsupported.
+ * The system decides whether the bell is audible, visual, or muted. Wayland
+ * needs the xdg-system-bell protocol; X11 uses the XKB bell for the window
+ * and Cocoa uses NSBeep.
+ */
+bool yasl_private_surface_bell(struct yasl_surface *s);
+/**
+ * Get the absolute path of the running executable, with symbolic links resolved.
+ * @param out Output buffer, or NULL when cap is zero.
+ * @param cap Size of out in bytes.
+ * @return Path length, or zero on failure. See the buffer rule in this file.
+ */
+size_t yasl_private_executable_path(char *out, size_t cap);
+/**
+ * Get the resource directory of the app bundle that contains the executable.
+ * @param out Output buffer, or NULL when cap is zero.
+ * @param cap Size of out in bytes.
+ * @return Path length, or zero when the executable is not in an app bundle.
+ * Only Cocoa has app bundles; other platforms return zero.
+ */
+size_t yasl_private_resource_dir(char *out, size_t cap);
+/**
+ * Get the user's preferred locale as a POSIX locale name, such as "en_US.UTF-8".
+ * @param out Output buffer, or NULL when cap is zero.
+ * @param cap Size of out in bytes.
+ * @return Name length, or zero on failure.
+ * Linux returns the first nonempty LC_ALL, LC_CTYPE, or LANG, and "C.UTF-8"
+ * when none is set. Cocoa builds the name from the current locale's language
+ * and region with a ".UTF-8" codeset, because apps started from Finder have no
+ * locale variables. Use the result to set LANG for child processes; YASL does
+ * not change the process locale.
+ */
+size_t yasl_private_default_locale(char *out, size_t cap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* YASL_PRIVATE_DESKTOP_H */
diff --git a/include/yasl/loop.h b/src/common/private_loop.h
index a8e9974..432bbc3 100644
--- a/include/yasl/loop.h
+++ b/src/common/private_loop.h
@@ -3,17 +3,17 @@
* Optional event loop, file watches, and timers.
*
* Use the loop's owning thread for all calls and callbacks, except
- * yasl_loop_now_ns(), yasl_loop_kick(), and yasl_loop_post(), which any thread
+ * yasl_private_loop_now_ns(), yasl_private_loop_kick(), and yasl_private_loop_post(), which any thread
* may call. A display borrows a supplied loop. For window applications,
- * drive yasl_display_dispatch(), drain input, then yasl_display_flush(); the
+ * drive yasl_private_display_dispatch(), drain input, then yasl_private_display_flush(); the
* standalone loop_run() function does not replace this cycle. Timer intervals are
* nanoseconds; timeout_ms arguments are milliseconds. Do not recursively step/run
* or destroy a loop from a callback.
*/
-#ifndef YASL_LOOP_H
-#define YASL_LOOP_H
+#ifndef YASL_PRIVATE_LOOP_H
+#define YASL_PRIVATE_LOOP_H
-#include "yasl/yasl.h"
+#include "common/types.h"
#ifdef __cplusplus
extern "C" {
@@ -24,11 +24,11 @@ extern "C" {
* Calls and callbacks use the loop's owning thread. */
/**
* Create an owned standalone file-descriptor and timer loop.
- * @return A loop to destroy with yasl_loop_destroy(), or NULL on allocation failure.
- * Use on one owning thread. Passing it to yasl_display_open() only lends it to
+ * @return A loop to destroy with yasl_private_loop_destroy(), or NULL on allocation failure.
+ * Use on one owning thread. Passing it to yasl_private_display_open() only lends it to
* the display; the caller retains ownership.
*/
-YASL_API struct yasl_loop *yasl_loop_create(void);
+struct yasl_private_loop *yasl_private_loop_create(void);
/**
* Release a loop, its watches, its timers, and its signal watch.
* @param l Loop, or NULL for no action.
@@ -36,15 +36,15 @@ YASL_API struct yasl_loop *yasl_loop_create(void);
* or an active step/run. Watched file descriptors are not closed. Posted
* functions that have not run are dropped; no thread may still post or kick.
*/
-YASL_API void yasl_loop_destroy(struct yasl_loop *l);
+void yasl_private_loop_destroy(struct yasl_private_loop *l);
/**
- * Step a standalone loop until yasl_loop_quit() is called.
+ * Step a standalone loop until yasl_private_loop_quit() is called.
* @param l Loop.
* @return Zero after quit, or -1 on failure.
* For a window application, use a dispatch, input-poll, flush cycle instead;
* loop_run alone does not perform that full cycle on every backend.
*/
-YASL_API int yasl_loop_run(struct yasl_loop *l);
+int yasl_private_loop_run(struct yasl_private_loop *l);
/**
* Wait for file-descriptor readiness or a timer deadline and run callbacks.
* @param l Loop.
@@ -53,24 +53,24 @@ YASL_API int yasl_loop_run(struct yasl_loop *l);
* Timer deadlines shorten the wait. Callbacks can add/delete sources, including
* themselves. Do not recursively step/run this loop.
*/
-YASL_API int yasl_loop_step(struct yasl_loop *l, int timeout_ms);
+int yasl_private_loop_step(struct yasl_private_loop *l, int timeout_ms);
/**
- * Ask the currently running yasl_loop_run() to return after this step.
+ * Ask the currently running yasl_private_loop_run() to return after this step.
* @param l Loop, or NULL for no action.
* Call on the owning thread, usually from a callback.
*/
-YASL_API void yasl_loop_quit(struct yasl_loop *l);
+void yasl_private_loop_quit(struct yasl_private_loop *l);
/**
* Wake a loop that is waiting, without running any callback.
* @param l Loop, or NULL for no action.
* May be called from any thread and from a signal handler. The loop must stay
- * alive until the call returns. A waiting yasl_loop_step(), yasl_loop_run(), or
- * yasl_display_dispatch() on this loop returns early; a loop that is not
+ * alive until the call returns. A waiting yasl_private_loop_step(), yasl_private_loop_run(), or
+ * yasl_private_display_dispatch() on this loop returns early; a loop that is not
* waiting returns early from its next wait instead. Kicks coalesce. This does
- * not schedule a redraw or enqueue an event; use yasl_loop_post() to run code
+ * not schedule a redraw or enqueue an event; use yasl_private_loop_post() to run code
* on the owning thread.
*/
-YASL_API void yasl_loop_kick(struct yasl_loop *l);
+void yasl_private_loop_kick(struct yasl_private_loop *l);
/**
* Run a function on the loop's owning thread.
* @param l Loop.
@@ -79,13 +79,13 @@ YASL_API void yasl_loop_kick(struct yasl_loop *l);
* @return true if queued; false for NULL arguments, a loop without a wake
* pipe, or allocation failure.
* May be called from any thread, but not from a signal handler. Wakes the loop
- * like yasl_loop_kick(). Functions run in posting order, during a
- * yasl_loop_step() or display dispatch on the owning thread. They may post
+ * like yasl_private_loop_kick(). Functions run in posting order, during a
+ * yasl_private_loop_step() or display dispatch on the owning thread. They may post
* again; a function posted while posts are running waits for a later step.
- * Stop posting before yasl_loop_destroy(), which drops functions that have
+ * Stop posting before yasl_private_loop_destroy(), which drops functions that have
* not run.
*/
-YASL_API bool yasl_loop_post(struct yasl_loop *l, void (*fn)(void *ud), void *ud);
+bool yasl_private_loop_post(struct yasl_private_loop *l, void (*fn)(void *ud), void *ud);
/**
* Deliver POSIX signals to a callback on the loop's owning thread.
* @param l Loop.
@@ -104,30 +104,30 @@ YASL_API bool yasl_loop_post(struct yasl_loop *l, void (*fn)(void *ud), void *ud
* signals reset to their default action across exec, so spawned programs
* do not inherit the handler. Repeated deliveries of one signal before the
* callback runs coalesce into one callback. SIGCHLD reports stopped and
- * continued children as well as exits. Callbacks run during yasl_loop_step()
+ * continued children as well as exits. Callbacks run during yasl_private_loop_step()
* or display dispatch, and may remove the watch.
*/
-YASL_API bool yasl_loop_watch_signals(struct yasl_loop *l, const int *signals, int n,
+bool yasl_private_loop_watch_signals(struct yasl_private_loop *l, const int *signals, int n,
void (*cb)(void *ud, int signo), void *ud);
/**
* Stop watching signals and restore their previous actions.
* @param l Loop, or NULL for no action.
* Does nothing if the loop watches no signals. Signals that arrived but have
- * not reached the callback are dropped. yasl_loop_destroy() also unwatches.
+ * not reached the callback are dropped. yasl_private_loop_destroy() also unwatches.
*/
-YASL_API void yasl_loop_unwatch_signals(struct yasl_loop *l);
+void yasl_private_loop_unwatch_signals(struct yasl_private_loop *l);
/**
* Read the monotonic clock used by YASL timers and timing results.
* @return Nanoseconds from CLOCK_MONOTONIC, unrelated to wall-clock time.
* May be called from any thread.
*/
-YASL_API uint64_t yasl_loop_now_ns(void);
+uint64_t yasl_private_loop_now_ns(void);
/**
* Get the loop used for a display's file watches and application timers.
* @param d Display, or NULL.
* @return Borrowed loop, or NULL. Do not destroy a display-owned loop yourself.
*/
-YASL_API struct yasl_loop *yasl_display_loop(struct yasl_display *d);
+struct yasl_private_loop *yasl_private_display_loop(struct yasl_private_display *d);
/**
* Register a borrowed file descriptor for poll-style readiness.
* @param l Loop.
@@ -139,15 +139,15 @@ YASL_API struct yasl_loop *yasl_display_loop(struct yasl_display *d);
* Delete the watch before closing fd. The callback must consume readiness or
* change/remove its watch to avoid repeated immediate wakeups.
*/
-YASL_API int yasl_loop_add_fd(struct yasl_loop *l, int fd, short events,
+int yasl_private_loop_add_fd(struct yasl_private_loop *l, int fd, short events,
int (*fn)(int fd, short revents, void *ud), void *ud);
/**
* Remove a file watch without closing its descriptor.
* @param l Loop.
- * @param id Watch ID returned by yasl_loop_add_fd().
+ * @param id Watch ID returned by yasl_private_loop_add_fd().
* @return Zero if removed, or -1 for an invalid/missing watch.
*/
-YASL_API int yasl_loop_del_fd(struct yasl_loop *l, int id);
+int yasl_private_loop_del_fd(struct yasl_private_loop *l, int id);
/**
* Replace a watch's poll event mask.
* @param l Loop.
@@ -155,19 +155,19 @@ YASL_API int yasl_loop_del_fd(struct yasl_loop *l, int id);
* @param events New <poll.h> mask; zero disables requested readiness (errors may still arrive).
* @return Zero if updated, or -1 for an invalid/missing watch.
*/
-YASL_API int yasl_loop_set_fd_events(struct yasl_loop *l, int id, short events);
+int yasl_private_loop_set_fd_events(struct yasl_private_loop *l, int id, short events);
/* Timers use nanoseconds. Return the next interval to repeat/reschedule, or zero
* to remove the timer. A zero ID indicates failure. Loop timers live until they
* stop, are deleted, or the loop is destroyed. Surface timers are also canceled
* when their surface is destroyed. */
/** Timer identity scoped to its loop. Zero is invalid; do not reuse a retired ID. */
-typedef uint32_t yasl_timer_id;
+typedef uint32_t yasl_private_timer_id;
/** Timer callback. interval_ns is the last scheduled delay; return the next delay or zero to stop. */
-typedef uint64_t (*yasl_timer_cb)(void *ud, yasl_timer_id id, uint64_t interval_ns);
+typedef uint64_t (*yasl_private_timer_cb)(void *ud, yasl_private_timer_id id, uint64_t interval_ns);
/** Convert a nonnegative integer millisecond value to nanoseconds; avoid overflow. */
-#define YASL_MS_TO_NS(ms) ((uint64_t)(ms) * 1000000ull)
+#define YASL_PRIVATE_MS_TO_NS(ms) ((uint64_t)(ms) * 1000000ull)
/**
* Schedule a timer on a loop.
@@ -179,36 +179,36 @@ typedef uint64_t (*yasl_timer_cb)(void *ud, yasl_timer_id id, uint64_t interval_
* Repeating timers coalesce missed deadlines; do not use them to count elapsed
* wall time. The callback receives the last scheduled interval, not elapsed time.
*/
-YASL_API yasl_timer_id yasl_loop_add_timer(struct yasl_loop *l, uint64_t interval_ns,
- yasl_timer_cb cb, void *ud);
+yasl_private_timer_id yasl_private_loop_add_timer(struct yasl_private_loop *l, uint64_t interval_ns,
+ yasl_private_timer_cb cb, void *ud);
/**
* Cancel a loop timer, including from its own callback.
* @param l Loop.
* @param id Timer ID.
* @return true if an armed timer was removed, false if invalid or already retired.
*/
-YASL_API bool yasl_loop_del_timer(struct yasl_loop *l, yasl_timer_id id);
+bool yasl_private_loop_del_timer(struct yasl_private_loop *l, yasl_private_timer_id id);
/**
* Schedule a timer canceled automatically on surface destruction.
* @param s Surface whose display loop will run the timer.
* @param interval_ns Nonzero initial delay in nanoseconds.
- * @param cb Required timer callback, with the same rules as yasl_loop_add_timer().
+ * @param cb Required timer callback, with the same rules as yasl_private_loop_add_timer().
* @param ud Borrowed userdata until the timer ends or the surface is destroyed.
* @return Nonzero timer ID, or zero on failure.
- * The surface does not own userdata. Cancel with yasl_surface_del_timer().
+ * The surface does not own userdata. Cancel with yasl_private_surface_del_timer().
*/
-YASL_API yasl_timer_id yasl_surface_add_timer(struct yasl_surface *s, uint64_t interval_ns,
- yasl_timer_cb cb, void *ud);
+yasl_private_timer_id yasl_private_surface_add_timer(struct yasl_surface *s, uint64_t interval_ns,
+ yasl_private_timer_cb cb, void *ud);
/**
- * Cancel a timer registered through yasl_surface_add_timer().
+ * Cancel a timer registered through yasl_private_surface_add_timer().
* @param s Owning surface.
* @param id Timer ID.
* @return true if still armed and removed; false if unknown or already retired.
*/
-YASL_API bool yasl_surface_del_timer(struct yasl_surface *s, yasl_timer_id id);
+bool yasl_private_surface_del_timer(struct yasl_surface *s, yasl_private_timer_id id);
#ifdef __cplusplus
}
#endif
-#endif /* YASL_LOOP_H */
+#endif /* YASL_PRIVATE_LOOP_H */
diff --git a/include/yasl/native.h b/src/common/private_native.h
index 64bc161..d3d9aae 100644
--- a/include/yasl/native.h
+++ b/src/common/private_native.h
@@ -7,17 +7,17 @@
* backend accessors live in the yasl/native headers and are installed only for built
* backends. Application-managed drawing currently supports Cocoa/Metal only.
*/
-#ifndef YASL_NATIVE_H
-#define YASL_NATIVE_H
+#ifndef YASL_PRIVATE_NATIVE_H
+#define YASL_PRIVATE_NATIVE_H
-#include "yasl/yasl.h"
+#include "common/types.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Optional platform interoperability. All handles are borrowed and expire on
- * object destruction. Check yasl_display_backend() before casting:
+ * object destruction. Check yasl_private_display_backend() before casting:
*
* display surface
* Wayland struct wl_display * struct wl_surface *
@@ -27,11 +27,11 @@ extern "C" {
/**
* Get a borrowed, backend-specific display handle.
* @param d Display, or NULL.
- * @return Native handle, or NULL. Inspect yasl_display_backend() before casting.
+ * @return Native handle, or NULL. Inspect yasl_private_display_backend() before casting.
* Prefer a typed accessor from yasl/native/wayland.h, x11.h, or cocoa.h.
* The handle expires on display close; do not close or dispatch it independently.
*/
-YASL_API void *yasl_display_get_native_handle(struct yasl_display *d);
+void *yasl_private_display_get_native_handle(struct yasl_private_display *d);
/**
* Get a borrowed, backend-specific surface handle.
* @param s Surface, or NULL.
@@ -39,28 +39,28 @@ YASL_API void *yasl_display_get_native_handle(struct yasl_display *d);
* Prefer typed accessors. The handle expires on surface destruction. Do not
* change native ownership, event masks, buffer attachment, or surface roles.
*/
-YASL_API void *yasl_surface_get_native_handle(struct yasl_surface *s);
+void *yasl_private_surface_get_native_handle(struct yasl_surface *s);
/* Create a surface for application-managed native rendering. graphics_device
* must be non-NULL; currently an id<MTLDevice> on Cocoa, retained by the surface.
* Other backends return NULL with errno=ENOTSUP. CPU rendering uses the ordinary
- * yasl_surface_create_toplevel() and requires no native device. */
+ * yasl_private_surface_create_toplevel() and requires no native device. */
/**
* Create a toplevel for application-managed native rendering.
- * @param d Display with YASL_CAP_NATIVE_GRAPHICS.
+ * @param d Display with YASL_PRIVATE_CAP_NATIVE_GRAPHICS.
* @param spec Copied window options, with logical-pixel size and optional callbacks.
* @param graphics_device Required native graphics device; currently id<MTLDevice> on Cocoa.
* @return Owned surface, or NULL with errno set: EINVAL for invalid arguments,
* ENOTSUP for an unsupported backend, or a backend error.
* Cocoa retains the device. Query the drawable during draw, submit/present it,
- * then call yasl_surface_present() to record submission.
- * Callback ownership and timing follow yasl_surface_create_toplevel().
+ * then call yasl_private_surface_present() to record submission.
+ * Callback ownership and timing follow yasl_private_surface_create_toplevel().
*/
-YASL_API struct yasl_surface *yasl_surface_create_native(struct yasl_display *d,
- const struct yasl_window_spec *spec, void *graphics_device);
+struct yasl_surface *yasl_private_surface_create_native(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *graphics_device);
/** Borrowed native drawable for the current draw target, currently Cocoa/Metal. */
-struct yasl_native_frame {
+struct yasl_private_native_frame {
/** Borrowed id<CAMetalDrawable> on Cocoa. */
void *drawable;
/** Cocoa CAMetalLayer presentsWithTransaction setting for this frame. */
@@ -70,7 +70,7 @@ struct yasl_native_frame {
/* Query the current draw target's native drawable. Returns false for unsupported
* backends, stale targets, or outside the draw callback. The result expires on
* successful present, callback return, or surface destruction. Native renderers
- * submit/present the drawable, then call yasl_surface_present() to record it. */
+ * submit/present the drawable, then call yasl_private_surface_present() to record it. */
/**
* Get the current draw target's native drawable.
* @param s Surface currently being drawn.
@@ -80,11 +80,11 @@ struct yasl_native_frame {
* stale targets, or calls outside draw. The drawable is borrowed and expires on
* successful present, callback return, or surface destruction. Currently Cocoa only.
*/
-YASL_API bool yasl_surface_get_native_frame(struct yasl_surface *s,
- const struct yasl_framebuffer *target, struct yasl_native_frame *out);
+bool yasl_private_surface_get_native_frame(struct yasl_surface *s,
+ const struct yasl_private_framebuffer *target, struct yasl_private_native_frame *out);
#ifdef __cplusplus
}
#endif
-#endif /* YASL_NATIVE_H */
+#endif /* YASL_PRIVATE_NATIVE_H */
diff --git a/src/common/private_native_cocoa.h b/src/common/private_native_cocoa.h
new file mode 100644
index 0000000..2eecd6e
--- /dev/null
+++ b/src/common/private_native_cocoa.h
@@ -0,0 +1,55 @@
+/**
+ * @file
+ * Typed borrowed Cocoa handles; available on macOS. Use on the main thread.
+ */
+#ifndef YASL_PRIVATE_NATIVE_COCOA_H
+#define YASL_PRIVATE_NATIVE_COCOA_H
+
+#include "common/types.h"
+
+#ifdef __OBJC__
+@class NSApplication;
+@class NSWindow;
+@class NSView;
+#else
+typedef struct objc_object NSApplication; /**< Opaque Cocoa application in plain C/C++. */
+typedef struct objc_object NSWindow; /**< Opaque Cocoa window in plain C/C++. */
+typedef struct objc_object NSView; /**< Opaque Cocoa view in plain C/C++. */
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Return borrowed Cocoa handles (expire on display/surface destruction).
+ * Return NULL / nil if display/surface is not a Cocoa object or NULL. */
+/**
+ * Get the borrowed NSApplication for an object of the matching backend.
+ * @param d Live display, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on display destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+NSApplication *yasl_private_cocoa_display_get_application(struct yasl_private_display *d);
+/**
+ * Get the borrowed NSWindow for an object of the matching backend.
+ * @param s Live surface, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on surface destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+NSWindow *yasl_private_cocoa_surface_get_window(struct yasl_surface *s);
+/**
+ * Get the borrowed NSView for an object of the matching backend.
+ * @param s Live surface, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on surface destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+NSView *yasl_private_cocoa_surface_get_view(struct yasl_surface *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* YASL_PRIVATE_NATIVE_COCOA_H */
diff --git a/src/common/private_native_wayland.h b/src/common/private_native_wayland.h
new file mode 100644
index 0000000..5033f7f
--- /dev/null
+++ b/src/common/private_native_wayland.h
@@ -0,0 +1,40 @@
+/**
+ * @file
+ * Typed borrowed Wayland handles; available when built with Wayland support.
+ */
+#ifndef YASL_PRIVATE_NATIVE_WAYLAND_H
+#define YASL_PRIVATE_NATIVE_WAYLAND_H
+
+#include "common/types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct wl_display;
+struct wl_surface;
+
+/* Return borrowed Wayland handles (expire on display/surface destruction).
+ * Return NULL if display/surface is not a Wayland object or NULL. */
+/**
+ * Get the borrowed Wayland connection for an object of the matching backend.
+ * @param d Live display, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on display destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+struct wl_display *yasl_private_wayland_display_get_wl_display(struct yasl_private_display *d);
+/**
+ * Get the borrowed Wayland surface for an object of the matching backend.
+ * @param s Live surface, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on surface destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+struct wl_surface *yasl_private_wayland_surface_get_wl_surface(struct yasl_surface *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* YASL_PRIVATE_NATIVE_WAYLAND_H */
diff --git a/src/common/private_native_x11.h b/src/common/private_native_x11.h
new file mode 100644
index 0000000..d1930bd
--- /dev/null
+++ b/src/common/private_native_x11.h
@@ -0,0 +1,40 @@
+/**
+ * @file
+ * Typed borrowed X11 handles; available when built with X11 support.
+ */
+#ifndef YASL_PRIVATE_NATIVE_X11_H
+#define YASL_PRIVATE_NATIVE_X11_H
+
+#include "common/types.h"
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _XDisplay Display; /**< Opaque Xlib connection, compatible with Xlib.h. */
+
+/* Return borrowed X11 handles (expire on display/surface destruction).
+ * Return NULL / 0 if display/surface is not an X11 object or NULL. */
+/**
+ * Get the borrowed X11 Display for an object of the matching backend.
+ * @param d Live display, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on display destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+Display *yasl_private_x11_display_get_display(struct yasl_private_display *d);
+/**
+ * Get the borrowed X11 Window ID for an object of the matching backend.
+ * @param s Live surface, or NULL.
+ * @return The handle, or zero/NULL for NULL or a different backend.
+ * Expires on surface destruction. Available only when this backend is built.
+ * Never pass a pointer to a destroyed object.
+ */
+uintptr_t yasl_private_x11_surface_get_window(struct yasl_surface *s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* YASL_PRIVATE_NATIVE_X11_H */
diff --git a/include/yasl/timing.h b/src/common/private_timing.h
index 7e4af9e..308765c 100644
--- a/include/yasl/timing.h
+++ b/src/common/private_timing.h
@@ -3,8 +3,8 @@
* Timing provenance, refresh estimates, and buffer-resize statistics.
* All timestamps are monotonic nanoseconds, not wall-clock dates.
*/
-#ifndef YASL_TIMING_H
-#define YASL_TIMING_H
+#ifndef YASL_PRIVATE_TIMING_H
+#define YASL_PRIVATE_TIMING_H
#include <stdint.h>
@@ -13,7 +13,7 @@ extern "C" {
#endif
/** Cumulative backend buffer-resize measurements; unavailable counters are zero. */
-struct yasl_resize_stats {
+struct yasl_private_resize_stats {
/** Number of measured buffer resizes. */
uint64_t count;
/** Mean measured resize duration in microseconds. */
@@ -26,15 +26,15 @@ struct yasl_resize_stats {
/* Describes last_present_ns, not merely availability of an extension. */
/** Provenance of last_present_ns; only PRESENTATION confirms compositor presentation. */
-enum yasl_timing_source {
- YASL_TIMING_UNKNOWN = 0, /**< No timing sample yet. */
- YASL_TIMING_SUBMISSION, /**< CPU submission time, not scanout confirmation. */
- YASL_TIMING_FRAME_CALLBACK, /**< Compositor pacing callback, not scanout confirmation. */
- YASL_TIMING_PRESENTATION, /**< Compositor-confirmed presentation. */
+enum yasl_private_timing_source {
+ YASL_PRIVATE_TIMING_UNKNOWN = 0, /**< No timing sample yet. */
+ YASL_PRIVATE_TIMING_SUBMISSION, /**< CPU submission time, not scanout confirmation. */
+ YASL_PRIVATE_TIMING_FRAME_CALLBACK, /**< Compositor pacing callback, not scanout confirmation. */
+ YASL_PRIVATE_TIMING_PRESENTATION, /**< Compositor-confirmed presentation. */
};
/** Snapshot of estimates and timing provenance. Zero timestamps mean no sample yet. */
-struct yasl_timing {
+struct yasl_private_timing {
/** Estimated refresh interval in nanoseconds; zero if unavailable. */
double refresh_ns;
/** Last timestamp, whose meaning is specified by source. */
@@ -44,13 +44,13 @@ struct yasl_timing {
/** Backend skipped-frame counter; not comparable across backends. */
uint64_t frames_skipped;
/** Provenance of last_present_ns. */
- enum yasl_timing_source source;
+ enum yasl_private_timing_source source;
/** Cumulative backend resize statistics. */
- struct yasl_resize_stats resize;
+ struct yasl_private_resize_stats resize;
};
#ifdef __cplusplus
}
#endif
-#endif /* YASL_TIMING_H */
+#endif /* YASL_PRIVATE_TIMING_H */
diff --git a/src/common/public_internal.h b/src/common/public_internal.h
new file mode 100644
index 0000000..210b222
--- /dev/null
+++ b/src/common/public_internal.h
@@ -0,0 +1,12 @@
+#ifndef YASL_PUBLIC_INTERNAL_H
+#define YASL_PUBLIC_INTERNAL_H
+#include "yasl/yasl.h"
+#include "common/backend.h"
+struct yasl_private_display *yasl_public_display(void);
+bool yasl_public_owner(void);
+bool yasl_public_surface(yasl_surface *, bool mutation);
+bool yasl_public_fail(int error);
+void yasl_public_set_error(enum yasl_error, int native_error);
+void yasl_public_register_surface(yasl_surface *, unsigned flags, yasl_surface *parent);
+void yasl_private_surface_notify_refresh(struct yasl_surface *);
+#endif
diff --git a/src/common/surface.c b/src/common/surface.c
new file mode 100644
index 0000000..897b95a
--- /dev/null
+++ b/src/common/surface.c
@@ -0,0 +1,522 @@
+#define _POSIX_C_SOURCE 200809L
+#include "public_internal.h"
+#include "yasl/cpu.h"
+#include "yasl/gl.h"
+#include "common/utf8.h"
+#include <errno.h>
+#include <limits.h>
+#include <math.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct yasl_private_display *display;
+static enum yasl_backend requested_backend;
+static char *application_id;
+static pthread_t owner;
+static pthread_mutex_t lifecycle_mutex = PTHREAD_MUTEX_INITIALIZER;
+static bool pumping;
+static unsigned refresh_depth;
+static char *delivered_text; /* Strings of the last returned event; freed by the next poll/wait. */
+static _Thread_local enum yasl_error last_error;
+static _Thread_local int native_error;
+
+void yasl_public_set_error(enum yasl_error error, int native)
+{ last_error=error; native_error=native; }
+bool yasl_public_fail(int error)
+{
+ if (!error) error=EIO;
+ enum yasl_error code=YASL_ERROR_IO;
+ if (error==EINVAL || error==EOVERFLOW || error==EILSEQ) code=YASL_ERROR_INVALID;
+ else if (error==ENOTSUP || error==ENOSYS) code=YASL_ERROR_UNSUPPORTED;
+ else if (error==ENOMEM) code=YASL_ERROR_NO_MEMORY;
+ else if (error==EBUSY) code=YASL_ERROR_BUSY;
+ else if (error==EPERM) code=YASL_ERROR_WRONG_THREAD;
+ yasl_public_set_error(code,error); errno=error; return false;
+}
+enum yasl_error yasl_get_error(const char **description)
+{
+ static const char *const messages[]={"No error","Invalid argument","Unsupported operation",
+ "Out of memory","Library not initialized","Operation is busy","Wrong thread",
+ "Input context expired","Native operation failed"};
+ if(description) *description=messages[last_error];
+ return last_error;
+}
+int yasl_get_native_error(void) { return native_error; }
+struct yasl_private_display *yasl_public_display(void) { return display; }
+bool yasl_public_owner(void)
+{
+ if(!display) { yasl_public_set_error(YASL_ERROR_NOT_INITIALIZED,0); return false; }
+ if(!pthread_equal(owner,pthread_self())) return yasl_public_fail(EPERM);
+ return true;
+}
+bool yasl_public_surface(yasl_surface *s,bool mutation)
+{
+ if(!yasl_public_owner()) return false;
+ if(!s || s->display!=display) return yasl_public_fail(EINVAL);
+ if(mutation && refresh_depth) return yasl_public_fail(EBUSY);
+ return true;
+}
+static bool initialize(void)
+{
+ if(display) return yasl_public_owner();
+ struct yasl_private_display_spec spec={.app_id=application_id,.backend=(enum yasl_private_backend)requested_backend};
+ struct yasl_private_display *d=yasl_private_display_open(NULL,&spec);
+ if(!d) return yasl_public_fail(errno);
+ pthread_mutex_lock(&lifecycle_mutex);
+ owner=pthread_self(); display=d;
+ pthread_mutex_unlock(&lifecycle_mutex);
+ return true;
+}
+bool yasl_set_app_id(const char *id)
+{
+ if(display) return yasl_public_fail(EBUSY);
+ char *copy=id ? strdup(id) : NULL;
+ if(id&&!copy) return yasl_public_fail(ENOMEM);
+ free(application_id); application_id=copy; return true;
+}
+bool yasl_set_backend(enum yasl_backend backend)
+{
+ if(display) return yasl_public_fail(EBUSY);
+ if(backend<YASL_BACKEND_AUTO || backend>YASL_BACKEND_COCOA) return yasl_public_fail(EINVAL);
+ requested_backend=backend; return true;
+}
+static bool creation_valid(int width,int height,unsigned flags)
+{
+ if(width<=0 || height<=0 || width>32767 || height>32767 ||
+ (unsigned long long)width*(unsigned)height>INT32_MAX/4 || (flags & ~15u))
+ return yasl_public_fail(EINVAL);
+ if(pumping || refresh_depth) return yasl_public_fail(EBUSY);
+ return true;
+}
+static void draw_committed(yasl_surface *s,struct yasl_private_frame *frame,void *ud)
+{
+ (void)ud;
+ if(s->refresh_callback && pumping && !refresh_depth) {
+ ++refresh_depth; s->refresh_callback(s,s->refresh_userdata); --refresh_depth;
+ }
+ if(s->presenter!=YASL_PRIVATE_PRESENTER_CPU || !frame->target.pixels) return;
+ int w=frame->target.size.x,h=frame->target.size.y;
+ if(w==s->committed_width && h==s->committed_height && s->committed_pixels)
+ memcpy(frame->target.pixels,s->committed_pixels,(size_t)w*h*4);
+ else memset(frame->target.pixels,0,(size_t)w*h*4);
+ struct yasl_private_damage_list damage={.n=-1};
+ yasl_private_surface_present(s,&frame->target,&damage);
+}
+void yasl_public_register_surface(yasl_surface *s,unsigned flags,yasl_surface *parent)
+{
+ s->creation_flags=flags; s->parent=parent;
+ if(flags&YASL_SURFACE_CPU) s->presenter=YASL_PRIVATE_PRESENTER_CPU;
+ struct yasl_private_window_cbs cb={.draw=draw_committed};
+ yasl_private_surface_set_callbacks(s,&cb,NULL);
+ s->reported_state=s->state;
+ struct yasl_private_geometry geometry=yasl_private_surface_geometry(s);
+ yasl_private_surface_notify_configure(s,geometry);
+}
+yasl_surface *yasl_surface_create(const char *title,int width,int height,unsigned flags)
+{
+ if(!creation_valid(width,height,flags)) return NULL;
+ bool fresh=!display;
+ if(!initialize()) return NULL;
+ struct yasl_private_window_spec spec={.title=title,.size_logical={width,height},
+ .flags=flags,.external_gpu=!(flags&YASL_SURFACE_CPU)};
+ yasl_surface *s=yasl_private_surface_create_toplevel(display,&spec);
+ if(!s) {
+ int error=errno;
+ if(fresh) { pthread_mutex_lock(&lifecycle_mutex); struct yasl_private_display *d=display; display=NULL;
+ pthread_mutex_unlock(&lifecycle_mutex); yasl_private_display_close(d); }
+ yasl_public_fail(error); return NULL;
+ }
+ yasl_public_register_surface(s,flags,NULL); return s;
+}
+void yasl_surface_destroy(yasl_surface *s)
+{
+ if(!s) return;
+ if(!yasl_public_surface(s,true)) return;
+ for(;;) {
+ yasl_surface *child=NULL;
+ for(yasl_surface *p=display->surfaces;p;p=p->event_next) if(p->parent==s) { child=p; break; }
+ if(!child) break;
+ yasl_surface_destroy(child);
+ }
+ free(s->committed_pixels); s->committed_pixels=NULL;
+ free(s->text_surrounding); s->text_surrounding=NULL;
+ yasl_private_surface_destroy(s);
+}
+void yasl_shutdown(void)
+{
+ if(display && !yasl_public_owner()) return;
+ if(pumping || refresh_depth) { yasl_public_fail(EBUSY); return; }
+ if(display) {
+ while(display->surfaces) yasl_surface_destroy(display->surfaces);
+ pthread_mutex_lock(&lifecycle_mutex); struct yasl_private_display *d=display; display=NULL;
+ pthread_mutex_unlock(&lifecycle_mutex); yasl_private_display_close(d);
+ }
+ free(delivered_text); delivered_text=NULL;
+ free(application_id); application_id=NULL; requested_backend=YASL_BACKEND_AUTO;
+#if YASL_HAS_VULKAN
+ extern void yasl_private_vulkan_reset(void);
+ yasl_private_vulkan_reset();
+#endif
+}
+void yasl_wake(void)
+{
+ pthread_mutex_lock(&lifecycle_mutex);
+ if(display && display->loop) yasl_private_loop_kick(display->loop);
+ pthread_mutex_unlock(&lifecycle_mutex);
+}
+uint64_t yasl_time_ns(void) { return yasl_private_loop_now_ns(); }
+static double scale_of(const yasl_surface *s)
+{ return s && s->scale_120 ? (double)s->scale_120/120.0 : 1.0; }
+static int logical(int pixels,double scale) { return (int)lround(pixels/scale); }
+enum yasl_backend yasl_surface_backend(const yasl_surface *s)
+{ return s ? (enum yasl_backend)s->display->ops->type : YASL_BACKEND_AUTO; }
+unsigned yasl_surface_features(const yasl_surface *s)
+{
+ if(!s) return 0;
+ unsigned caps=yasl_private_display_caps(s->display);
+ if(s->display->ops->surface_create_gl) caps|=YASL_CAP_OPENGL;
+#if YASL_HAS_VULKAN
+ caps|=YASL_CAP_VULKAN;
+#endif
+ return caps;
+}
+void yasl_surface_get_framebuffer_size(const yasl_surface *s,int *w,int *h)
+{ if(w) *w=s?s->size.x:0; if(h) *h=s?s->size.y:0; }
+void yasl_surface_get_size(const yasl_surface *s,int *w,int *h)
+{ double scale=scale_of(s); if(w) *w=s?logical(s->size.x,scale):0; if(h) *h=s?logical(s->size.y,scale):0; }
+double yasl_surface_get_scale(const yasl_surface *s) { return scale_of(s); }
+unsigned yasl_surface_state(const yasl_surface *s) { return s?s->state:0; }
+void *yasl_surface_userdata(const yasl_surface *s) { return s?s->userdata:NULL; }
+void yasl_surface_set_userdata(yasl_surface *s,void *ud) { if(yasl_public_surface(s,false)) s->userdata=ud; }
+bool yasl_surface_set_title(yasl_surface *s,const char *title)
+{
+ if(!yasl_public_surface(s,false)) return false;
+ if(!title) return yasl_public_fail(EINVAL);
+ if(!s->display->ops->surface_set_title) return yasl_public_fail(ENOTSUP);
+ s->display->ops->surface_set_title(s,title); return true;
+}
+#define REQUEST(name,priv,args,call) bool name args { if(!yasl_public_surface(s,false)) return false; errno=0; return priv call || yasl_public_fail(errno); }
+REQUEST(yasl_surface_request_size,yasl_private_surface_set_size,(yasl_surface *s,int w,int h),(s,((struct yasl_private_vector2i){w,h})))
+REQUEST(yasl_surface_request_fullscreen,yasl_private_surface_set_fullscreen,(yasl_surface *s,bool v),(s,v))
+REQUEST(yasl_surface_request_maximized,yasl_private_surface_set_maximized,(yasl_surface *s,bool v),(s,v))
+REQUEST(yasl_surface_request_minimize,yasl_private_surface_minimize,(yasl_surface *s),(s))
+REQUEST(yasl_surface_set_pointer_mode,yasl_private_surface_set_pointer_mode,(yasl_surface *s,enum yasl_pointer_mode m),(s,(enum yasl_private_pointer_mode)m))
+bool yasl_surface_set_cursor(yasl_surface *s,enum yasl_cursor cursor)
+{
+ if(!yasl_public_surface(s,false)) return false;
+ if(cursor<YASL_CURSOR_ARROW || cursor>YASL_CURSOR_HIDDEN) return yasl_public_fail(EINVAL);
+ if(!s->display->ops->surface_set_cursor) return yasl_public_fail(ENOTSUP);
+ yasl_private_surface_set_cursor(s,(enum yasl_private_cursor)cursor); return true;
+}
+void yasl_surface_set_refresh_callback(yasl_surface *s,void(*fn)(yasl_surface *,void *),void *ud)
+{ if(yasl_public_surface(s,false)) { s->refresh_callback=fn; s->refresh_userdata=ud; } }
+void yasl_private_surface_notify_refresh(yasl_surface *s)
+{
+ if(!s || !s->display) return;
+ struct yasl_private_event ev={.type=YASL_PRIVATE_EVENT_REFRESH,.surface=s};
+ yasl_private_events_push(s->display,s,&ev);
+ if(s->refresh_callback && pumping && !refresh_depth) {
+ ++refresh_depth; s->refresh_callback(s,s->refresh_userdata); --refresh_depth;
+ }
+}
+static char *copy_text(const char *data,size_t length)
+{
+ if(length==SIZE_MAX || (!data&&length)) { errno=EINVAL; return NULL; }
+ char *p=malloc(length+1); if(!p) return NULL;
+ if(length) memcpy(p,data,length);
+ p[length]=0; return p;
+}
+static const char *deliver_text(const char *data,size_t length)
+{
+ free(delivered_text); delivered_text=copy_text(data,length); return delivered_text;
+}
+/* Folds native size, state and focus changes into one WINDOW snapshot; unchanged values report nothing. */
+static bool window_event(yasl_surface *s,struct yasl_private_vector2i size,uint32_t scale_120,uint32_t state,yasl_event *e)
+{
+ bool resized=size.x!=s->reported_size.x || size.y!=s->reported_size.y || scale_120!=s->reported_scale_120;
+ if(!resized && state==s->reported_state) return false;
+ s->reported_size=size; s->reported_scale_120=scale_120; s->reported_state=state;
+ if(resized) s->refresh_owed=true;
+ double scale=scale_120 ? scale_120/120.0 : 1.0;
+ e->type=YASL_EVENT_WINDOW; e->window.pixel_width=size.x; e->window.pixel_height=size.y; e->window.scale=scale;
+ e->window.width=logical(size.x,scale); e->window.height=logical(size.y,scale);
+ e->window.state=state;
+ return true;
+}
+static bool refresh_ready(yasl_surface *s) { return s->reported_size.x>0 && s->reported_size.y>0; }
+static bool convert_event(const struct yasl_private_event *p,yasl_event *e)
+{
+ *e=(yasl_event){.surface=p->surface,.time_ns=p->time_ns};
+ double scale=scale_of(p->surface);
+ switch(p->type) {
+ case YASL_PRIVATE_EVENT_WINDOW_CLOSE:e->type=YASL_EVENT_CLOSE;break;
+ case YASL_PRIVATE_EVENT_DISPLAY_QUIT:e->type=YASL_EVENT_QUIT;break;
+ case YASL_PRIVATE_EVENT_DISPLAY_REOPEN:e->type=YASL_EVENT_REOPEN;break;
+ case YASL_PRIVATE_EVENT_DISPLAY_LOST:e->type=YASL_EVENT_DISPLAY_LOST;break;
+ case YASL_PRIVATE_EVENT_REFRESH:
+ if(!p->surface) return false;
+ if(!refresh_ready(p->surface)) { p->surface->refresh_owed=true; return false; }
+ p->surface->refresh_owed=false; e->type=YASL_EVENT_REFRESH; break;
+ case YASL_PRIVATE_EVENT_INPUT_RESET:e->type=YASL_EVENT_INPUT_RESET;break;
+ case YASL_PRIVATE_EVENT_FOCUS: {
+ yasl_surface *s=p->surface; if(!s) return false;
+ uint32_t state=p->focus.focused ? s->reported_state|YASL_STATE_ACTIVATED : s->reported_state&~(uint32_t)YASL_STATE_ACTIVATED;
+ return window_event(s,s->reported_size,s->reported_scale_120,state,e);
+ }
+ case YASL_PRIVATE_EVENT_WINDOW_STATE:
+ if(!p->surface) return false;
+ return window_event(p->surface,p->surface->reported_size,p->surface->reported_scale_120,p->state.flags,e);
+ case YASL_PRIVATE_EVENT_WINDOW_RESIZE:
+ if(!p->surface) return false;
+ return window_event(p->surface,p->resize.geometry.size,p->resize.geometry.scale_120,p->surface->reported_state,e);
+ case YASL_PRIVATE_EVENT_KEY:
+ e->type=YASL_EVENT_KEY;e->key.key=p->key.key;e->key.action=(enum yasl_key_action)p->key.action;
+ e->key.mods=p->key.mods;e->key.codepoint=p->key.codepoint;e->key.shifted=p->key.shifted;e->key.native_code=p->key.native_code;break;
+ case YASL_PRIVATE_EVENT_TEXT:
+ e->type=YASL_EVENT_TEXT;e->text.length=p->text.len;e->text.data=deliver_text(p->text.data,p->text.len);return e->text.data!=NULL;
+ case YASL_PRIVATE_EVENT_DROP:
+ e->type=YASL_EVENT_DROP;e->drop.action=YASL_DROP_DATA;e->drop.length=p->text.len;
+ e->drop.data=deliver_text(p->text.data,p->text.len);return e->drop.data!=NULL;
+ case YASL_PRIVATE_EVENT_PASTE:
+ e->type=YASL_EVENT_CLIPBOARD;e->clipboard.length=p->paste.len;e->clipboard.request_id=p->paste.request_id;
+ e->clipboard.status=(enum yasl_transfer_status)p->paste.status;
+ e->clipboard.data=deliver_text(p->paste.data,p->paste.len);return e->clipboard.data!=NULL;
+ case YASL_PRIVATE_EVENT_COMPOSITION:
+ e->type=YASL_EVENT_COMPOSITION;e->composition.length=p->composition.len;e->composition.begin=p->composition.begin;
+ e->composition.end=p->composition.end;e->composition.active=p->composition.active;
+ e->composition.revision=p->composition.revision; e->composition.generation=p->composition.generation;
+ e->composition.data=deliver_text(p->composition.data,p->composition.len);return e->composition.data!=NULL;
+ case YASL_PRIVATE_EVENT_TEXT_EDIT:
+ e->type=YASL_EVENT_TEXT_EDIT;e->text_edit.length=p->text_edit.len;e->text_edit.begin=p->text_edit.begin;e->text_edit.end=p->text_edit.end;
+ e->text_edit.revision=p->text_edit.revision;e->text_edit.generation=p->text_edit.generation;
+ e->text_edit.data=deliver_text(p->text_edit.data,p->text_edit.len);return e->text_edit.data!=NULL;
+ case YASL_PRIVATE_EVENT_PEN:
+ e->type=YASL_EVENT_PEN;
+#define PEN(field) e->pen.field=p->pen.field
+ PEN(device_id);PEN(tool_id);PEN(x);PEN(y);PEN(pressure);PEN(tilt_x);PEN(tilt_y);PEN(rotation);PEN(distance);PEN(tangential);PEN(wheel_degrees);
+ PEN(axes_valid);PEN(axes_changed);PEN(buttons);PEN(changed_button);PEN(tool);PEN(action);PEN(in_contact);
+#undef PEN
+ break;
+ case YASL_PRIVATE_EVENT_POINTER_SCROLL:
+ e->type=YASL_EVENT_SCROLL;e->scroll.x=p->pointer.pos.x/scale;e->scroll.y=p->pointer.pos.y/scale;
+ e->scroll.lines_x=p->pointer.scroll.x;e->scroll.lines_y=p->pointer.scroll.y;
+ e->scroll.pixels_x=p->pointer.pixel_scroll.x/scale;e->scroll.pixels_y=p->pointer.pixel_scroll.y/scale;
+ e->scroll.valid=p->pointer.scroll_valid?p->pointer.scroll_valid:YASL_SCROLL_LINES;
+ e->scroll.source=p->pointer.scroll_source;e->scroll.phase=p->pointer.scroll_phase;e->scroll.momentum=p->pointer.scroll_momentum;e->scroll.mods=p->pointer.mods;break;
+ case YASL_PRIVATE_EVENT_POINTER_DOWN:case YASL_PRIVATE_EVENT_POINTER_UP:case YASL_PRIVATE_EVENT_POINTER_MOTION:case YASL_PRIVATE_EVENT_POINTER_LEAVE:
+ e->type=YASL_EVENT_POINTER;e->pointer.x=(p->pointer.precise_position?p->pointer.precise_x:p->pointer.pos.x)/scale;
+ e->pointer.y=(p->pointer.precise_position?p->pointer.precise_y:p->pointer.pos.y)/scale;
+ e->pointer.dx=p->pointer.delta.x/scale;e->pointer.dy=p->pointer.delta.y/scale;
+ e->pointer.action=p->type==YASL_PRIVATE_EVENT_POINTER_DOWN?YASL_POINTER_DOWN:p->type==YASL_PRIVATE_EVENT_POINTER_UP?YASL_POINTER_UP:
+ p->type==YASL_PRIVATE_EVENT_POINTER_LEAVE?YASL_POINTER_LEAVE:YASL_POINTER_MOVE;
+ e->pointer.button=p->pointer.button;e->pointer.buttons=p->pointer.buttons;e->pointer.mods=p->pointer.mods;break;
+ case YASL_PRIVATE_EVENT_DRAG:
+ e->type=YASL_EVENT_DRAG;e->drag.action=(enum yasl_drag_action)p->drag.action;
+ e->drag.x=p->drag.pos.x/scale;e->drag.y=p->drag.pos.y/scale;break;
+ case YASL_PRIVATE_EVENT_DROP_HOVER:
+ e->type=YASL_EVENT_DROP;e->drop.action=p->drop.action==YASL_PRIVATE_DROP_LEAVE?YASL_DROP_LEAVE:YASL_DROP_OVER;
+ e->drop.x=p->drop.pos.x/scale;e->drop.y=p->drop.pos.y/scale;break;
+ default:return false;
+ }
+ return true;
+}
+static int take_event(yasl_event *out)
+{
+ for(yasl_surface *s=display->surfaces;s;s=s->event_next) if(s->refresh_owed && refresh_ready(s)) {
+ s->refresh_owed=false;
+ *out=(yasl_event){.type=YASL_EVENT_REFRESH,.surface=s,.time_ns=yasl_time_ns()};
+ return 1;
+ }
+ struct yasl_private_event p;
+ while(yasl_private_display_poll_event(display,&p)) {
+ if(p.surface && ((p.type==YASL_PRIVATE_EVENT_COMPOSITION && p.composition.generation!=p.surface->text_generation) ||
+ (p.type==YASL_PRIVATE_EVENT_TEXT_EDIT && p.text_edit.generation!=p.surface->text_generation))) continue;
+ yasl_event event;
+ errno=0;
+ if(convert_event(&p,&event)) { *out=event;return 1; }
+ if(errno) { yasl_public_fail(errno);return -1; }
+ }
+ return 0;
+}
+int yasl_wait_event(yasl_event *out,int timeout)
+{
+ if(!out) { yasl_public_fail(EINVAL);return -1; }
+ *out=(yasl_event){.type=YASL_EVENT_NONE};
+ if(!yasl_public_owner()) return -1;
+ if(pumping || refresh_depth) { yasl_public_fail(EBUSY);return -1; }
+ free(delivered_text); delivered_text=NULL;
+ int n=take_event(out); if(n) return n;
+ pumping=true;
+ yasl_private_display_flush(display);
+ n=take_event(out);
+ if(!n) {
+ int work=yasl_private_display_dispatch(display,timeout);
+ if(work<0) { yasl_public_fail(errno);n=-1; }
+ yasl_private_display_flush(display);
+ if(n>=0) n=take_event(out);
+ }
+ pumping=false;return n;
+}
+uint32_t *yasl_cpu_pixels(yasl_surface *s,int *w,int *h)
+{
+ if(w) *w=0;
+ if(h) *h=0;
+ if(!yasl_public_surface(s,false)) return NULL;
+ if(s->presenter!=YASL_PRIVATE_PRESENTER_CPU) { yasl_public_fail(ENOTSUP); return NULL; }
+ int width=s->size.x,height=s->size.y;
+ if(width<=0 || height<=0) { yasl_public_fail(EBUSY); return NULL; }
+ if(width!=s->committed_width || height!=s->committed_height || !s->committed_pixels) {
+ uint32_t *next=calloc((size_t)width*height,4);
+ if(!next) { yasl_public_fail(ENOMEM); return NULL; }
+ int keep_w=width<s->committed_width?width:s->committed_width,keep_h=height<s->committed_height?height:s->committed_height;
+ if(s->committed_pixels) for(int y=0;y<keep_h;y++)
+ memcpy(next+(size_t)y*width,s->committed_pixels+(size_t)y*s->committed_width,(size_t)keep_w*4);
+ free(s->committed_pixels); s->committed_pixels=next; s->committed_width=width; s->committed_height=height;
+ }
+ if(w) *w=width;
+ if(h) *h=height;
+ return s->committed_pixels;
+}
+bool yasl_cpu_present(yasl_surface *s)
+{
+ if(!yasl_public_surface(s,false)) return false;
+ if(s->presenter!=YASL_PRIVATE_PRESENTER_CPU) return yasl_public_fail(ENOTSUP);
+ if(!s->committed_pixels || s->committed_width!=s->size.x || s->committed_height!=s->size.y) return yasl_public_fail(EINVAL);
+ yasl_private_surface_request_frame(s); return true;
+}
+int yasl_double_click_ms(void) { return display?yasl_private_display_double_click_ms(display):400; }
+bool yasl_clipboard_set(enum yasl_selection sel,const char *text)
+{ if(!yasl_public_owner())return false;errno=0;return yasl_private_clipboard_set(display,(enum yasl_private_selection)sel,text)||yasl_public_fail(errno); }
+uint64_t yasl_clipboard_request(yasl_surface *s,enum yasl_selection sel)
+{ if(!yasl_public_surface(s,false))return 0;errno=0;uint64_t id=yasl_private_clipboard_request(s,(enum yasl_private_selection)sel);if(!id)yasl_public_fail(errno);return id; }
+static bool text_update(yasl_surface *s)
+{ if(!s->display->ops->surface_update_text_input)return true;errno=0;return s->display->ops->surface_update_text_input(s)||yasl_public_fail(errno); }
+bool yasl_surface_set_text_input(yasl_surface *s,bool active,enum yasl_text_purpose purpose)
+{
+ if(!yasl_public_surface(s,false))return false;
+ if(purpose<YASL_TEXT_NORMAL||purpose>YASL_TEXT_TERMINAL)return yasl_public_fail(EINVAL);
+ bool old=s->text_input_active;unsigned old_purpose=s->text_purpose;uint64_t generation=s->text_generation;
+ if(old!=active || old_purpose!=(unsigned)purpose)++s->text_generation;
+ s->text_input_active=active;s->text_purpose=purpose;
+ if(!text_update(s)) { s->text_input_active=old;s->text_purpose=old_purpose;s->text_generation=generation;return false; }return true;
+}
+bool yasl_surface_set_surrounding_text(yasl_surface *s,const char *text,size_t len,size_t begin,size_t end,uint64_t revision)
+{
+ if(!yasl_public_surface(s,false))return false;
+ if((!text&&len)||begin>end||end>len||len>INT32_MAX || (len&&memchr(text,0,len)))return yasl_public_fail(EINVAL);
+ /* Validate UTF-8 and require selection boundaries between code points. */
+ if((begin<len&&((unsigned char)text[begin]&0xc0)==0x80)||(end<len&&((unsigned char)text[end]&0xc0)==0x80))return yasl_public_fail(EINVAL);
+ for(size_t i=0;i<len;) { unsigned char c=(unsigned char)text[i];size_t n=c<0x80?1:c>=0xc2&&c<=0xdf?2:c>=0xe0&&c<=0xef?3:c>=0xf0&&c<=0xf4?4:0;
+ if(!n||i+n>len)return yasl_public_fail(EILSEQ);
+ for(size_t j=1;j<n;j++)if(((unsigned char)text[i+j]&0xc0)!=0x80)return yasl_public_fail(EILSEQ);
+ if(n>=3 && ((c==0xe0&&(unsigned char)text[i+1]<0xa0)||(c==0xed&&(unsigned char)text[i+1]>=0xa0)||
+ (c==0xf0&&(unsigned char)text[i+1]<0x90)||(c==0xf4&&(unsigned char)text[i+1]>=0x90)))return yasl_public_fail(EILSEQ);
+ i+=n;
+ }
+ char *copy=copy_text(text,len);if(!copy)return yasl_public_fail(errno);
+ char *old=s->text_surrounding;size_t old_len=s->text_length,old_begin=s->text_begin,old_end=s->text_end;uint64_t old_rev=s->text_revision;
+ s->text_surrounding=copy;s->text_length=len;s->text_begin=begin;s->text_end=end;s->text_revision=revision;
+ if(!text_update(s)){free(copy);s->text_surrounding=old;s->text_length=old_len;s->text_begin=old_begin;s->text_end=old_end;s->text_revision=old_rev;return false;}
+ free(old);return true;
+}
+static bool logical_rect(yasl_surface *s,double x,double y,double w,double h,struct yasl_private_rect2i *r)
+{
+ if(!isfinite(x)||!isfinite(y)||!isfinite(w)||!isfinite(h)||w<0||h<0)return yasl_public_fail(EINVAL);
+ r->x=yasl_private_scale_to_device(x,s->scale_120);r->y=yasl_private_scale_to_device(y,s->scale_120);
+ r->width=yasl_private_scale_to_device(w,s->scale_120);r->height=yasl_private_scale_to_device(h,s->scale_120);return true;
+}
+bool yasl_surface_set_ime_caret(yasl_surface *s,double x,double y,double w,double h)
+{
+ if(!yasl_public_surface(s,false))return false;
+ struct yasl_private_rect2i r;
+ if(!logical_rect(s,x,y,w,h,&r))return false;
+ if(!s->display->ops->surface_set_ime_caret)return yasl_public_fail(ENOTSUP);
+ yasl_private_surface_set_ime_caret(s,r);return true;
+}
+static bool trigger_valid(yasl_surface *s,const yasl_event *e)
+{
+ if(!e||e->surface!=s||!e->time_ns||yasl_time_ns()-e->time_ns>5000000000ull ||
+ !((e->type==YASL_EVENT_POINTER&&e->pointer.action==YASL_POINTER_DOWN)||(e->type==YASL_EVENT_PEN&&e->pen.action==YASL_PEN_DOWN))) {
+ yasl_public_set_error(YASL_ERROR_CONTEXT_EXPIRED,0);return false;
+ }return true;
+}
+static bool popup_spec(yasl_surface *s,int w,int h,double x,double y,double aw,double ah,enum yasl_popup_edge anchor,enum yasl_popup_edge gravity,unsigned constraints,struct yasl_private_popup_spec *out)
+{
+ if(w<=0||h<=0 || (unsigned)anchor>8 || (unsigned)gravity>8 || (constraints&~63u))return yasl_public_fail(EINVAL);
+ *out=(struct yasl_private_popup_spec){.size={yasl_private_scale_to_device(w,s->scale_120),yasl_private_scale_to_device(h,s->scale_120)},.anchor_edges=anchor,.gravity=gravity,.constraints=constraints,.positioner_explicit=true};
+ return logical_rect(s,x,y,aw,ah,&out->anchor);
+}
+yasl_surface *yasl_popup_create(yasl_surface *parent,int w,int h,double x,double y,double aw,double ah,enum yasl_popup_edge anchor,enum yasl_popup_edge gravity,unsigned constraints,const yasl_event *trigger)
+{
+ if(!yasl_public_surface(parent,true)||!trigger_valid(parent,trigger))return NULL;
+ struct yasl_private_popup_spec spec;if(!popup_spec(parent,w,h,x,y,aw,ah,anchor,gravity,constraints,&spec))return NULL;
+ yasl_surface *s=yasl_private_surface_create_popup(parent,&spec);if(!s){yasl_public_fail(errno);return NULL;}
+ yasl_public_register_surface(s,parent->creation_flags,parent);return s;
+}
+bool yasl_popup_reposition(yasl_surface *s,int w,int h,double x,double y,double aw,double ah,enum yasl_popup_edge anchor,enum yasl_popup_edge gravity,unsigned constraints)
+{
+ if(!yasl_public_surface(s,false))return false;
+ struct yasl_private_popup_spec spec;
+ if(!s->parent)return yasl_public_fail(EINVAL);
+ if(!popup_spec(s->parent,w,h,x,y,aw,ah,anchor,gravity,constraints,&spec))return false;
+ errno=0;return yasl_private_surface_popup_reposition(s,&spec)||yasl_public_fail(errno);
+}
+bool yasl_surface_start_interactive_move(yasl_surface *s,const yasl_event *e)
+{ if(!yasl_public_surface(s,false)||!trigger_valid(s,e))return false;errno=0;return yasl_private_surface_start_interactive_move(s)||yasl_public_fail(errno); }
+bool yasl_surface_start_interactive_resize(yasl_surface *s,enum yasl_resize_edge edge,const yasl_event *e)
+{ if(!yasl_public_surface(s,false)||!trigger_valid(s,e))return false;errno=0;return yasl_private_surface_start_interactive_resize(s,(enum yasl_private_resize_edge)edge)||yasl_public_fail(errno); }
+bool yasl_surface_show_window_menu(yasl_surface *s,double x,double y,const yasl_event *e)
+{ if(!yasl_public_surface(s,false)||!trigger_valid(s,e))return false;
+ struct yasl_private_rect2i r;if(!logical_rect(s,x,y,0,0,&r))return false;errno=0;return yasl_private_surface_show_window_menu(s,(struct yasl_private_vector2i){r.x,r.y})||yasl_public_fail(errno); }
+bool yasl_drag_start(yasl_surface *s,const yasl_event *e,const uint32_t *pixels,int w,int h,int hx,int hy)
+{
+ if(!yasl_public_surface(s,false)||!trigger_valid(s,e))return false;
+ struct yasl_private_drag_image image={.pixels=pixels,.size={w,h},.hotspot={hx,hy}};
+ errno=0;return yasl_private_display_drag_start(display,s,pixels?&image:NULL,NULL,NULL)||yasl_public_fail(errno);
+}
+void yasl_drag_cancel(void) { if(yasl_public_owner())yasl_private_display_drag_cancel(display); }
+bool yasl_surface_drag_carry(yasl_surface *s,double x,double y)
+{ if(!yasl_public_surface(s,false))return false;
+ struct yasl_private_rect2i r;if(!logical_rect(s,x,y,0,0,&r))return false;errno=0;return yasl_private_surface_drag_carry(s,(struct yasl_private_vector2i){r.x,r.y})||yasl_public_fail(errno); }
+bool yasl_surface_place_under_pointer(yasl_surface *s,double x,double y)
+{ if(!yasl_public_surface(s,false))return false;
+ struct yasl_private_rect2i r;if(!logical_rect(s,x,y,0,0,&r))return false;errno=0;return yasl_private_surface_place_under_pointer(s,(struct yasl_private_vector2i){r.x,r.y})||yasl_public_fail(errno); }
+size_t yasl_drop_next_path(const char **cursor,char *out,size_t cap)
+{ return yasl_private_drop_next_path(cursor,out,cap); }
+
+yasl_surface *yasl_gl_surface_create(const char *title,int w,int h,unsigned flags,const int *attrs,yasl_surface *share)
+{
+ if(!creation_valid(w,h,flags)||(flags&YASL_SURFACE_CPU)) { yasl_public_fail(EINVAL);return NULL; }
+ struct yasl_private_gl_options parsed;if(!yasl_private_gl_parse_options(attrs,&parsed)){yasl_public_fail(errno);return NULL;}
+ bool fresh=!display;if(!initialize())return NULL;
+ if(share && (share->display!=display || share->presenter!=YASL_PRIVATE_PRESENTER_GL)) {yasl_public_fail(EINVAL);return NULL;}
+ if(!display->ops->surface_create_gl) {if(fresh)yasl_shutdown();yasl_public_fail(ENOTSUP);return NULL;}
+ struct yasl_private_window_spec spec={.title=title,.size_logical={w,h},.flags=flags,.external_gpu=true};
+ yasl_surface *s=display->ops->surface_create_gl(display,&spec,attrs,share);
+ if(!s){int error=errno;if(fresh)yasl_shutdown();yasl_public_fail(error);return NULL;}
+ if(!yasl_private_events_register(s)){int error=errno;display->ops->surface_destroy(s);if(fresh)yasl_shutdown();yasl_public_fail(error);return NULL;}
+ s->presenter=YASL_PRIVATE_PRESENTER_GL;yasl_public_register_surface(s,flags,NULL);return s;
+}
+bool yasl_gl_make_current(yasl_surface *s)
+{
+ if(!display){yasl_public_set_error(YASL_ERROR_NOT_INITIALIZED,0);return false;}
+ if(s && (s->display!=display||s->presenter!=YASL_PRIVATE_PRESENTER_GL))return yasl_public_fail(EINVAL);
+ if(!display->ops->gl_make_current)return yasl_public_fail(ENOTSUP);
+ errno=0;return display->ops->gl_make_current(display,s)||yasl_public_fail(errno);
+}
+yasl_gl_proc yasl_gl_get_proc_address(const char *name)
+{
+ if(!display||!name||!display->ops->gl_get_proc_address){yasl_public_fail(ENOTSUP);return NULL;}
+ yasl_gl_proc p=display->ops->gl_get_proc_address(display,name);if(!p)yasl_public_fail(ENOENT);return p;
+}
+bool yasl_gl_swap_interval(yasl_surface *s,int interval)
+{
+ if(!s||s->presenter!=YASL_PRIVATE_PRESENTER_GL||!s->display->ops->gl_swap_interval)return yasl_public_fail(EINVAL);
+ errno=0;return s->display->ops->gl_swap_interval(s->display,s,interval)||yasl_public_fail(errno);
+}
+bool yasl_gl_swap_buffers(yasl_surface *s)
+{
+ if(!s||s->presenter!=YASL_PRIVATE_PRESENTER_GL||!s->display->ops->gl_swap_buffers)return yasl_public_fail(EINVAL);
+ errno=0;return s->display->ops->gl_swap_buffers(s->display,s)||yasl_public_fail(errno);
+}
diff --git a/src/common/timing.c b/src/common/timing.c
index 7a91327..69c25cf 100644
--- a/src/common/timing.c
+++ b/src/common/timing.c
@@ -1,6 +1,6 @@
#include "timing.h"
-uint64_t yasl_clock_extend_ms(uint32_t ms, uint64_t now_ns)
+uint64_t yasl_private_clock_extend_ms(uint32_t ms, uint64_t now_ns)
{
uint64_t now_ms = now_ns / 1000000;
int64_t ahead = (int32_t)(ms - (uint32_t)now_ms);
@@ -9,7 +9,7 @@ uint64_t yasl_clock_extend_ms(uint32_t ms, uint64_t now_ns)
return (uint64_t)((int64_t)now_ms + ahead) * 1000000ull;
}
-void yasl_clock_presented(struct yasl_frame_clock *c, uint64_t now, bool sample)
+void yasl_private_clock_presented(struct yasl_private_frame_clock *c, uint64_t now, bool sample)
{
if (c->last_present_ns) {
double dt = (double)(now - c->last_present_ns);
@@ -17,10 +17,10 @@ void yasl_clock_presented(struct yasl_frame_clock *c, uint64_t now, bool sample)
c->refresh_ns += 0.125 * (dt - c->refresh_ns);
}
c->last_present_ns = now;
- c->source = YASL_TIMING_SUBMISSION;
+ c->source = YASL_PRIVATE_TIMING_SUBMISSION;
}
-void yasl_clock_note_resize(struct yasl_frame_clock *c, uint64_t dt_ns)
+void yasl_private_clock_note_resize(struct yasl_private_frame_clock *c, uint64_t dt_ns)
{
c->resize_count++;
c->resize_sum_ns += dt_ns;
@@ -28,8 +28,8 @@ void yasl_clock_note_resize(struct yasl_frame_clock *c, uint64_t dt_ns)
c->resize_max_ns = dt_ns;
}
-void yasl_clock_resize_stats(const struct yasl_frame_clock *c,
- struct yasl_resize_stats *out)
+void yasl_private_clock_resize_stats(const struct yasl_private_frame_clock *c,
+ struct yasl_private_resize_stats *out)
{
out->count = c->resize_count;
out->avg_us = c->resize_count
@@ -39,13 +39,13 @@ void yasl_clock_resize_stats(const struct yasl_frame_clock *c,
out->minflt = c->resize_minflt;
}
-uint64_t yasl_clock_next_present_ns(const struct yasl_frame_clock *c)
+uint64_t yasl_private_clock_next_present_ns(const struct yasl_private_frame_clock *c)
{
uint64_t next = c->last_present_ns + (uint64_t)c->refresh_ns;
return (uint64_t)-(c->last_present_ns != 0) & next;
}
-uint64_t yasl_clock_frame_timestamp_ns(struct yasl_frame_clock *c, uint64_t now)
+uint64_t yasl_private_clock_frame_timestamp_ns(struct yasl_private_frame_clock *c, uint64_t now)
{
if (!c->last_present_ns || c->refresh_ns <= 0.0) {
c->frame_time_ns = now;
diff --git a/src/common/timing.h b/src/common/timing.h
index a3c3c88..a0c6ef1 100644
--- a/src/common/timing.h
+++ b/src/common/timing.h
@@ -1,12 +1,12 @@
-#ifndef YASL_COMMON_TIMING_H
-#define YASL_COMMON_TIMING_H
+#ifndef YASL_PRIVATE_COMMON_TIMING_H
+#define YASL_PRIVATE_COMMON_TIMING_H
-#include "yasl/timing.h"
+#include "common/private_timing.h"
#include <stdbool.h>
-#define YASL_REFRESH_DEFAULT_NS 16666666.0
+#define YASL_PRIVATE_REFRESH_DEFAULT_NS 16666666.0
-struct yasl_frame_clock {
+struct yasl_private_frame_clock {
uint64_t last_present_ns;
uint64_t resize_count;
uint64_t resize_sum_ns;
@@ -14,15 +14,15 @@ struct yasl_frame_clock {
long resize_minflt;
uint64_t frame_time_ns;
double refresh_ns;
- enum yasl_timing_source source;
+ enum yasl_private_timing_source source;
};
-uint64_t yasl_clock_extend_ms(uint32_t ms, uint64_t now_ns);
-void yasl_clock_presented(struct yasl_frame_clock *c, uint64_t now, bool sample);
-void yasl_clock_note_resize(struct yasl_frame_clock *c, uint64_t dt_ns);
-void yasl_clock_resize_stats(const struct yasl_frame_clock *c,
- struct yasl_resize_stats *out);
-uint64_t yasl_clock_next_present_ns(const struct yasl_frame_clock *c);
-uint64_t yasl_clock_frame_timestamp_ns(struct yasl_frame_clock *c, uint64_t now);
+uint64_t yasl_private_clock_extend_ms(uint32_t ms, uint64_t now_ns);
+void yasl_private_clock_presented(struct yasl_private_frame_clock *c, uint64_t now, bool sample);
+void yasl_private_clock_note_resize(struct yasl_private_frame_clock *c, uint64_t dt_ns);
+void yasl_private_clock_resize_stats(const struct yasl_private_frame_clock *c,
+ struct yasl_private_resize_stats *out);
+uint64_t yasl_private_clock_next_present_ns(const struct yasl_private_frame_clock *c);
+uint64_t yasl_private_clock_frame_timestamp_ns(struct yasl_private_frame_clock *c, uint64_t now);
-#endif /* YASL_COMMON_TIMING_H */
+#endif /* YASL_PRIVATE_COMMON_TIMING_H */
diff --git a/src/common/types.h b/src/common/types.h
new file mode 100644
index 0000000..91f8a47
--- /dev/null
+++ b/src/common/types.h
@@ -0,0 +1,1101 @@
+/**
+ * @file
+ * Portable desktop surfaces, CPU drawing, input, and display dispatch.
+ *
+ * All calls and callbacks use the display's opening thread (the main thread on
+ * Cocoa), unless explicitly documented otherwise. Userdata is borrowed; callback
+ * tables and creation specs are copied. Do not recursively dispatch/flush or
+ * destroy a display/loop while dispatch or a callback is active. Destroy child
+ * popups before parents, surfaces before displays, displays before borrowed loops.
+ * Surface destruction from its own callback is supported; stop using it immediately.
+ *
+ * Zero-initialize option and callback structs before setting fields. Pointers to
+ * destroyed objects are invalid, even for getters and destroy functions. NULL
+ * handling is documented per function; it does not make freed pointers safe.
+ * Only functions explicitly documenting errno guarantee a diagnostic there.
+ * Other boolean results report success/availability; void setters are best effort.
+ * New window requests, clipboard operations, frame presentation, and posting
+ * document errno on failure. Event strings belong to the display queue.
+ *
+ * Toplevel creation sizes use logical pixels. Geometry, framebuffers, pointer
+ * positions, damage, IME carets, popup specs, and drag offsets use device pixels.
+ * Coordinates have their origin at the upper left; x grows right and y grows down.
+ * Scale is an integer ratio with YASL_PRIVATE_SCALE_UNIT (120) representing 1x.
+ * See the application guide for the display-wide dispatch/poll/flush cycle
+ * and backend limits. Legacy surface polling is an input-only filtered adapter.
+ */
+#ifndef YASL_PRIVATE_H
+#define YASL_PRIVATE_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <limits.h>
+#include <float.h>
+#include <math.h>
+
+#include "common/private_timing.h"
+#include "yasl/yasl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct yasl_private_display;
+struct yasl_surface;
+struct yasl_private_loop;
+
+/* Geometry */
+/** A two-dimensional integer size, position, or offset; units depend on the caller. */
+struct yasl_private_vector2i {
+ /** Horizontal coordinate or width. */
+ int x;
+ /** Vertical coordinate or height. */
+ int y;
+};
+
+/** Two-dimensional fractional offset; units depend on the containing structure. */
+struct yasl_private_vector2d {
+ /** Horizontal component. */
+ double x;
+ /** Vertical component. */
+ double y;
+};
+
+/** Integer rectangle; origin is upper left and extents must be nonnegative. */
+struct yasl_private_rect2i {
+ /** Left coordinate. */
+ int x;
+ /** Top coordinate. */
+ int y;
+ /** Nonnegative horizontal extent. */
+ int width;
+ /** Nonnegative vertical extent. */
+ int height;
+};
+
+/** Scale numerator representing 1x. */
+#define YASL_PRIVATE_SCALE_UNIT 120
+/** Backend minimum logical dimension used when constraining toplevel sizes. */
+#define YASL_PRIVATE_MIN_LOGICAL_PX 64
+
+/* Nearest integer, ties away from zero. Zero scale returns zero; finite
+ * out-of-range results saturate. Non-finite logical coordinates return zero. */
+/** Convert a logical coordinate to device pixels, rounding ties away from zero.
+ * @param logical Logical coordinate; non-finite values return zero.
+ * @param scale_120 Scale numerator; zero returns zero.
+ * @return Rounded device coordinate, saturated to the range of int.
+ */
+static inline int yasl_private_scale_to_device(double logical, uint32_t scale_120)
+{
+ if (!scale_120 || !(logical >= -DBL_MAX && logical <= DBL_MAX)) return 0;
+ double px = (scale_120 == YASL_PRIVATE_SCALE_UNIT) ? logical : (logical * ((double)scale_120 / YASL_PRIVATE_SCALE_UNIT));
+ if (px >= INT_MAX) return INT_MAX;
+ if (px <= INT_MIN) return INT_MIN;
+ return (int)(px + copysign(0.5, px));
+}
+
+/** Convert a device coordinate to logical pixels, rounding ties away from zero.
+ * @param device Device coordinate.
+ * @param scale_120 Scale numerator; zero returns zero.
+ * @return Rounded logical coordinate, saturated to the range of int.
+ */
+static inline int yasl_private_scale_to_logical(int device, uint32_t scale_120)
+{
+ if (!scale_120) return 0;
+ if (scale_120 == YASL_PRIVATE_SCALE_UNIT) return device;
+ int64_t px = (int64_t)device * YASL_PRIVATE_SCALE_UNIT;
+ int64_t half = (int64_t)(scale_120 / 2);
+ int64_t bias = (px < 0) ? -half : half;
+ px = (px + bias) / scale_120;
+ if (px > INT_MAX) return INT_MAX;
+ if (px < INT_MIN) return INT_MIN;
+ return (int)px;
+}
+
+/* Capabilities */
+/** Optional backend/server features, returned as a bitmask by yasl_private_display_caps(). */
+enum yasl_private_display_cap : uint32_t {
+ YASL_PRIVATE_CAP_POPUPS = 1 << 0, /**< Popup creation and placement. */
+ YASL_PRIVATE_CAP_DRAG = 1 << 1, /**< Surface-drag source and target support. */
+ YASL_PRIVATE_CAP_DRAG_CARRY = 1 << 2, /**< Carry a toplevel during a drag. */
+ YASL_PRIVATE_CAP_PLACE_WINDOW = 1 << 3, /**< Place a window relative to the pointer. */
+ YASL_PRIVATE_CAP_PRIMARY_SELECTION = 1 << 4, /**< Separate primary selection. */
+ YASL_PRIVATE_CAP_CSD = 1 << 5, /**< Backend advertises client decoration support; check each action result. */
+ YASL_PRIVATE_CAP_NATIVE_GRAPHICS = 1 << 6, /**< Application-managed native rendering. */
+ YASL_PRIVATE_CAP_FRAME_PACING = 1 << 7, /**< Compositor/display-driven draws. */
+ /* Backend/protocol supports tablet axes, independent of attached hardware. */
+ YASL_PRIVATE_CAP_TABLET = 1 << 8,
+ YASL_PRIVATE_CAP_TEXT_INPUT = 1 << 11, /**< Tablet protocol support, independent of attached hardware. */
+};
+
+/** Independent selections. An unsupported selection never aliases the clipboard. */
+enum yasl_private_selection : uint8_t {
+ YASL_PRIVATE_CLIPBOARD, /**< Explicit copy/paste selection. */
+ YASL_PRIVATE_PRIMARY, /**< Independent primary selection, if supported. */
+};
+
+/** Reported surface state flags; absent flags may also mean unsupported state. */
+enum yasl_private_surface_state : uint32_t {
+ YASL_PRIVATE_STATE_FULLSCREEN = 1 << 0, /**< Fullscreen. */
+ YASL_PRIVATE_STATE_MAXIMIZED = 1 << 1, /**< Maximized. */
+ YASL_PRIVATE_STATE_ACTIVATED = 1 << 2, /**< Active window. */
+ YASL_PRIVATE_STATE_SUSPENDED = 1 << 3, /**< Compositor reports suspension. */
+ YASL_PRIVATE_STATE_CSD = 1 << 4, /**< Backend reports client-side decorations. */
+ YASL_PRIVATE_STATE_TILED = 1 << 5, /**< At least one edge is tiled; currently reported by Wayland. */
+};
+
+/** System cursor shapes. HIDDEN preserves pointer input; NONE is its compatibility alias. */
+enum yasl_private_cursor {
+ YASL_PRIVATE_CURSOR_ARROW, /**< Default arrow. */
+ YASL_PRIVATE_CURSOR_TEXT, /**< Text insertion. */
+ YASL_PRIVATE_CURSOR_LINK, /**< Link/pointer hand. */
+ YASL_PRIVATE_CURSOR_CROSSHAIR, /**< Crosshair. */
+ YASL_PRIVATE_CURSOR_MOVE, /**< Move in any direction. */
+ YASL_PRIVATE_CURSOR_RESIZE_NWSE, /**< Northwest/southeast resize. */
+ YASL_PRIVATE_CURSOR_RESIZE_NESW, /**< Northeast/southwest resize. */
+ YASL_PRIVATE_CURSOR_RESIZE_EW, /**< Horizontal resize. */
+ YASL_PRIVATE_CURSOR_RESIZE_NS, /**< Vertical resize. */
+ YASL_PRIVATE_CURSOR_HIDDEN, /**< Hidden system cursor; pointer input continues. */
+ YASL_PRIVATE_CURSOR_NONE = YASL_PRIVATE_CURSOR_HIDDEN, /**< Compatibility alias for HIDDEN. */
+};
+
+/** Valid interactive resize edges; combinations are restricted to the named values. */
+enum yasl_private_resize_edge : uint32_t {
+ YASL_PRIVATE_EDGE_NONE = 0, /**< No resize edge; rejected by interactive resize. */
+ YASL_PRIVATE_EDGE_TOP = 1, /**< Top edge. */
+ YASL_PRIVATE_EDGE_BOTTOM = 2, /**< Bottom edge. */
+ YASL_PRIVATE_EDGE_LEFT = 4, /**< Left edge. */
+ YASL_PRIVATE_EDGE_TOP_LEFT = 5, /**< Top-left corner. */
+ YASL_PRIVATE_EDGE_BOTTOM_LEFT = 6, /**< Bottom-left corner. */
+ YASL_PRIVATE_EDGE_RIGHT = 8, /**< Right edge. */
+ YASL_PRIVATE_EDGE_TOP_RIGHT = 9, /**< Top-right corner. */
+ YASL_PRIVATE_EDGE_BOTTOM_RIGHT = 10, /**< Bottom-right corner. */
+};
+
+/* Damage & Framebuffer
+ * CPU pixels are tightly packed premultiplied ARGB8888 (0xAARRGGBB).
+ * Size, pointer positions, IME caret and damage use device pixels; surface
+ * toplevel creation sizes use logical pixels. Damage is a hint: backends may upload
+ * the whole frame. Every pixel must contain valid content on each draw.
+ */
+/** Maximum number of damage rectangles in one presentation. */
+#define YASL_PRIVATE_DAMAGE_MAX 64
+
+/** Damage hints for one presentation; every target still needs complete valid pixels. */
+struct yasl_private_damage_list {
+ /** -1: full target; 0: no damaged rectangles; otherwise number of rects. */
+ int n;
+ /** Reserved hint; callers may leave zero. Core presentation uses rects. */
+ struct yasl_private_rect2i bounds;
+ /** First n rectangles, in device pixels; ignored for n == -1. */
+ struct yasl_private_rect2i rects[YASL_PRIVATE_DAMAGE_MAX];
+};
+
+/** Borrowed draw target, valid only for the current draw callback. Never free pixels. */
+struct yasl_private_framebuffer {
+ /** Opaque draw identity. Preserve it and all target fields when copying. */
+ uint64_t generation;
+ /** Writable premultiplied ARGB8888, row stride size.x * sizeof(uint32_t); may be NULL for native rendering. */
+ uint32_t *pixels;
+ /** Target extent in device pixels. */
+ struct yasl_private_vector2i size;
+ /** Row stride in bytes, populated for the active draw callback. */
+ int stride_bytes;
+};
+
+/** One draw invocation. Do not retain its pointer after the callback returns. */
+struct yasl_private_frame {
+ /** Surface being drawn. */
+ struct yasl_surface *surface;
+ /** Target to initialize and present before returning. */
+ struct yasl_private_framebuffer target;
+ /** Zero-based index of this surface draw. */
+ uint64_t frame_index;
+ /** Estimated animation time; not confirmed presentation. */
+ uint64_t target_time_ns;
+ /** Device scale numerator, with 120 representing 1x. */
+ uint32_t scale_120;
+};
+
+/** Coalesced redraw policy, valid with either loop style. */
+typedef enum yasl_private_pacing_mode {
+ YASL_PRIVATE_PACING_ON_DEMAND = 0, /**< Draw only when invalidated or exposed. */
+ YASL_PRIVATE_PACING_CONTINUOUS, /**< Draw continuously using native pacing or a timer. */
+} yasl_private_pacing_mode;
+
+/* Drag and Drop */
+/** Target notifications for YASL surface drags, separate from external file drops. */
+enum yasl_private_drag_event {
+ YASL_PRIVATE_DRAG_OVER, /**< Surface drag entered or moved over the target. */
+ YASL_PRIVATE_DRAG_LEAVE, /**< Surface drag left the target. */
+ YASL_PRIVATE_DRAG_DROP, /**< Surface drag dropped on the target. */
+};
+
+/** External text/file drag hover notifications; completed data arrives through input. */
+enum yasl_private_drop_event {
+ YASL_PRIVATE_DROP_OVER, /**< External data drag entered or moved over the target. */
+ YASL_PRIVATE_DROP_LEAVE, /**< External data drag left the target. */
+};
+
+/** Source-side drag completion; DROPPED does not prove application-level acceptance. */
+enum yasl_private_drag_end {
+ YASL_PRIVATE_DRAG_DROPPED, /**< Drag ended in a drop; not an application acceptance receipt. */
+ YASL_PRIVATE_DRAG_CANCELLED, /**< Drag canceled, including display close. */
+};
+
+/** Copied drag icon in tightly packed premultiplied 0xAARRGGBB device pixels. */
+struct yasl_private_drag_image {
+ /** Copied pixel array; required when an image is supplied. */
+ const uint32_t *pixels;
+ /** Positive width and height in device pixels. */
+ struct yasl_private_vector2i size;
+ /** Pointer position within the image, in device pixels. */
+ struct yasl_private_vector2i hotspot;
+};
+
+/** Copied drag completion listener; userdata remains owned by the application. */
+struct yasl_private_drag_listener {
+ /** Called exactly once after a successful start, possibly synchronously on cancel/close. */
+ void (*end)(void *ud, enum yasl_private_drag_end how);
+};
+
+/** Configured device-pixel content size and the corresponding display scale. */
+struct yasl_private_geometry {
+ /** Current content extent in device pixels. */
+ struct yasl_private_vector2i size;
+ /** Positive scale numerator; 120 means 1x and 180 means 1.5x. */
+ uint32_t scale_120;
+};
+
+/* Input Model */
+/** Keyboard press, release, or autorepeat; releases must not insert text. */
+enum yasl_private_key_action {
+ YASL_PRIVATE_KEY_PRESS, /**< Key pressed. */
+ YASL_PRIVATE_KEY_RELEASE, /**< Key released. */
+ YASL_PRIVATE_KEY_REPEAT, /**< Key autorepeat. */
+};
+
+/** Modifier bitmask. SUPER is Command on macOS. */
+enum yasl_private_mod : uint32_t {
+ YASL_PRIVATE_MOD_SHIFT = 1 << 0, /**< Shift modifier. */
+ YASL_PRIVATE_MOD_ALT = 1 << 1, /**< Alt/Option modifier. */
+ YASL_PRIVATE_MOD_CTRL = 1 << 2, /**< Control modifier. */
+ YASL_PRIVATE_MOD_SUPER = 1 << 3, /**< Super/Command modifier. */
+ YASL_PRIVATE_MOD_CAPS = 1 << 4, /**< Caps Lock modifier. */
+};
+
+/** Pointer source class; capability support does not imply an attached device. */
+enum yasl_private_pointer_device {
+ YASL_PRIVATE_PTR_DEV_MOUSE, /**< Mouse or trackpad pointer. */
+ YASL_PRIVATE_PTR_DEV_TOUCH, /**< Touch translated into pointer input. */
+ YASL_PRIVATE_PTR_DEV_PEN, /**< Tablet stylus. */
+ YASL_PRIVATE_PTR_DEV_ERASER, /**< Tablet eraser tool. */
+};
+
+/** Pointer button; meaningful only for POINTER_DOWN and POINTER_UP. */
+enum yasl_private_pointer_button {
+ YASL_PRIVATE_BUTTON_PRIMARY = 0, /**< Left button, pen tip, or touch contact. */
+ YASL_PRIVATE_BUTTON_MIDDLE = 1, /**< Middle button / wheel click. */
+ YASL_PRIVATE_BUTTON_SECONDARY = 2, /**< Right button or pen barrel button. */
+ YASL_PRIVATE_BUTTON_BACK = 3, /**< Side "back" button. */
+ YASL_PRIVATE_BUTTON_FORWARD = 4, /**< Side "forward" button. */
+};
+
+/** Bit for a yasl_private_pointer_button in yasl_private_pointer_event.buttons. */
+#define YASL_PRIVATE_BUTTON_BIT(button) (1u << (button))
+
+/** Pointer capture mode for a surface. */
+enum yasl_private_pointer_mode {
+ YASL_PRIVATE_POINTER_NORMAL = 0, /**< Absolute positions; cursor per yasl_private_surface_set_cursor(). */
+ /** Cursor hidden and locked in place while the surface has focus. POINTER_MOTION
+ * reports unbounded relative motion in delta; pos stays at the lock position.
+ * The lock is released on focus loss and re-acquired on focus gain. */
+ YASL_PRIVATE_POINTER_RELATIVE,
+};
+
+/** Normalized tablet axes; zero for unavailable axes and non-tablet devices. */
+struct yasl_private_pointer_pen {
+ /** 0..65535 normalized to device range. Zero alone does not end contact. */
+ uint16_t pressure;
+ /** Degrees in -90..90; positive right. */
+ int8_t tilt_x;
+ /** Degrees in -90..90; positive down. */
+ int8_t tilt_y;
+};
+
+/** Event tag. Each tag lists the union member it selects. */
+enum yasl_private_event_type {
+ YASL_PRIVATE_EVENT_NONE = 0,
+
+ /* Input: produced by backends, returned by yasl_private_surface_poll_event(). */
+ YASL_PRIVATE_EVENT_KEY, /**< .key */
+ YASL_PRIVATE_EVENT_TEXT, /**< .text — committed UTF-8; insert this, not key codepoints. */
+ YASL_PRIVATE_EVENT_POINTER_DOWN, /**< .pointer — button/tip pressed; button valid. */
+ YASL_PRIVATE_EVENT_POINTER_UP, /**< .pointer — button/tip released; button valid. */
+ YASL_PRIVATE_EVENT_POINTER_MOTION, /**< .pointer — position, relative motion, or pen axes changed. */
+ YASL_PRIVATE_EVENT_POINTER_SCROLL, /**< .pointer — scroll valid. */
+ YASL_PRIVATE_EVENT_POINTER_LEAVE, /**< .pointer — last known pos/device; cancel hover state. */
+ YASL_PRIVATE_EVENT_FOCUS, /**< .focus — keyboard focus changed; cancel held state on loss. */
+ YASL_PRIVATE_EVENT_PASTE, /**< .text — UTF-8 from yasl_private_display_paste(). */
+ YASL_PRIVATE_EVENT_DROP, /**< .text — dropped text/uri-list: CRLF-separated file URLs, not paths. */
+ YASL_PRIVATE_EVENT_INPUT_RESET, /**< No payload. Input stream interrupted; clear held keys, buttons, strokes, IME. */
+
+ /* Window and application: part of the display FIFO, as well as runner delivery.
+ * Legacy window callbacks also receive matching native notifications. */
+ YASL_PRIVATE_EVENT_WINDOW_RESIZE, /**< .resize — size and/or scale changed; reallocate and redraw. */
+ YASL_PRIVATE_EVENT_WINDOW_CLOSE, /**< No payload. User asked to close the window. */
+ YASL_PRIVATE_EVENT_USER, /**< .user — posted application message. */
+ YASL_PRIVATE_EVENT_WINDOW_STATE, /**< .state — reported state changed. */
+ YASL_PRIVATE_EVENT_DRAG, /**< .drag — surface-drag target notification. */
+ YASL_PRIVATE_EVENT_DROP_HOVER, /**< .drop — external drag hover/leave. */
+ YASL_PRIVATE_EVENT_COMPOSITION, /**< .composition — preedit snapshot, including ending. */
+ YASL_PRIVATE_EVENT_DISPLAY_QUIT, /**< No payload; system requested application quit. */
+ YASL_PRIVATE_EVENT_DISPLAY_REOPEN, /**< No payload; system requested activation. */
+ YASL_PRIVATE_EVENT_DISPLAY_LOST,
+ YASL_PRIVATE_EVENT_PEN,
+ YASL_PRIVATE_EVENT_TEXT_EDIT,
+ YASL_PRIVATE_EVENT_REFRESH, /**< No payload; display connection failed. */
+};
+
+/** Physical keyboard transition with optional layout labels.
+ * key is independent of modifiers/layout and stable between press and release.
+ * codepoint and shifted are optional single Unicode scalars for the current
+ * layout at levels zero/one, not text input. Zero means unavailable, a control
+ * key, or a layout mapping that produces multiple characters. Insert committed
+ * YASL_PRIVATE_EVENT_TEXT into documents instead. */
+struct yasl_private_key_event {
+ /** Portable physical key; YASL_KEY_UNKNOWN when unmapped. */
+ enum yasl_key key;
+ /** Press, release, or autorepeat. */
+ enum yasl_key_action action;
+ /** yasl_private_mod bitmask at this event. */
+ uint32_t mods;
+ /** Optional unshifted layout Unicode scalar; zero if unavailable. */
+ uint32_t codepoint;
+ /** Optional shifted layout Unicode scalar; zero if unavailable. */
+ uint32_t shifted;
+ /** Native code: Wayland evdev, X11 server keycode, Cocoa virtual keycode. */
+ uint32_t native_code;
+};
+
+/** Pointer sample shared by all POINTER_* events. Pen and eraser share it with
+ * mouse input. Track contact with DOWN/UP of the primary button, not pressure.
+ * This is not a multi-contact touch API. */
+struct yasl_private_pointer_event {
+ struct yasl_private_vector2d pixel_scroll;
+ unsigned scroll_valid, scroll_source, scroll_phase;
+ bool scroll_momentum;
+ double precise_x, precise_y;
+ bool precise_position;
+ /** Surface-relative device-pixel position. Frozen while YASL_PRIVATE_POINTER_RELATIVE is locked. */
+ struct yasl_private_vector2i pos;
+ /** MOTION: device-pixel movement since the previous sample on this surface.
+ * Unbounded under YASL_PRIVATE_POINTER_RELATIVE; zero after enter, leave, or reset. */
+ struct yasl_private_vector2d delta;
+ /** SCROLL: fractional line offsets after system preferences; positive right/down. */
+ struct yasl_private_vector2d scroll;
+ /** DOWN/UP: which button changed. */
+ enum yasl_private_pointer_button button;
+ /** YASL_PRIVATE_BUTTON_BIT() mask of buttons held after this event, on every
+ * POINTER_* type. Tracked from this queue's DOWN and UP events for all
+ * devices; INPUT_RESET clears it. On Wayland LEAVE clears it too, since the
+ * compositor sends leave during a press only when it takes the pointer.
+ * X11 and Cocoa keep sending motion and the release after LEAVE. */
+ uint32_t buttons;
+ /** Mouse, touch, pen, or eraser. */
+ enum yasl_private_pointer_device device;
+ /** yasl_private_mod bitmask at this event. */
+ uint32_t mods;
+ /** Current tablet axes, including on pressure-only motion; missing axes are zero. */
+ struct yasl_private_pointer_pen pen;
+};
+
+/** Borrowed NUL-terminated UTF-8, shared by TEXT, PASTE and DROP. */
+struct yasl_private_text_event {
+ /** Borrowed NUL-terminated bytes; see yasl_private_surface_poll_event() for lifetime. */
+ const char *data;
+ /** Length in bytes excluding the terminating NUL. */
+ size_t len;
+};
+
+/** Clipboard transfer outcome. */
+enum yasl_private_transfer_status {
+ YASL_PRIVATE_TRANSFER_OK = 0, /**< Text received successfully, including empty text. */
+ YASL_PRIVATE_TRANSFER_UNAVAILABLE, /**< No text selection is available. */
+ YASL_PRIVATE_TRANSFER_FAILED, /**< Transfer or decoding failed. */
+};
+
+/** Clipboard completion; data/len alias the legacy text payload. */
+struct yasl_private_paste_event {
+ const char *data; /**< Borrowed NUL-terminated UTF-8; empty on failure. */
+ size_t len; /**< Byte length excluding NUL. */
+ uint64_t request_id; /**< Zero for a legacy paste request. */
+ enum yasl_private_transfer_status status; /**< Completion outcome. */
+};
+
+/** Borrowed UTF-8 preedit snapshot, valid until the next successful display poll. */
+struct yasl_private_composition_event {
+ const char *data; /**< Borrowed NUL-terminated UTF-8 snapshot. */
+ size_t len; /**< Byte length excluding NUL. */
+ int begin; /**< UTF-8 byte offset, or -1 if unavailable. */
+ int end; /**< UTF-8 byte offset, or -1 if unavailable. */
+ bool active; /**< Whether composition text is present. */
+ uint64_t revision, generation;
+};
+
+/** Reported window-state change. */
+struct yasl_private_state_event { uint32_t flags; /**< Current YASL_PRIVATE_STATE_* flags. */ };
+/** Surface-drag target notification; position is in device pixels. */
+struct yasl_private_drag_target_event {
+ enum yasl_private_drag_event action; /**< Hover, leave, or drop. */
+ struct yasl_private_vector2i pos; /**< Surface-relative device pixels. */
+};
+/** External drag hover notification; position is in device pixels. */
+struct yasl_private_drop_hover_event {
+ enum yasl_private_drop_event action; /**< Hover or leave. */
+ struct yasl_private_vector2i pos; /**< Surface-relative device pixels. */
+};
+
+/** Keyboard focus transition. */
+struct yasl_private_focus_event {
+ /** true on focus gain, false on loss. */
+ bool focused;
+};
+
+/** New configured geometry for a surface. */
+struct yasl_private_resize_event {
+ /** Content size in device pixels and display scale. */
+ struct yasl_private_geometry geometry;
+ /** true when scale_120 differs from the previous resize of this surface. */
+ bool scale_changed;
+};
+
+/** Application message posted with yasl_private_post_event(). */
+struct yasl_private_user_event {
+ /** Application-defined message code. */
+ uint32_t code;
+ /** Pointer passed through unchanged; ownership stays with the application. */
+ void *payload;
+ /** Optional payload size, passed through unchanged. */
+ size_t size;
+};
+
+/** One tagged event; read only the union member selected by type. */
+enum yasl_private_surface_flags { YASL_PRIVATE_SURFACE_CPU = 1u, YASL_PRIVATE_SURFACE_HIDDEN = 2u, YASL_PRIVATE_SURFACE_UNDECORATED = 4u, YASL_PRIVATE_SURFACE_FIXED_SIZE = 8u };
+enum yasl_private_pen_action { YASL_PRIVATE_PEN_ENTER, YASL_PRIVATE_PEN_LEAVE, YASL_PRIVATE_PEN_MOVE, YASL_PRIVATE_PEN_DOWN, YASL_PRIVATE_PEN_UP, YASL_PRIVATE_PEN_BUTTON, YASL_PRIVATE_PEN_CANCEL };
+enum yasl_private_pen_axis { YASL_PRIVATE_PEN_PRESSURE = 1u, YASL_PRIVATE_PEN_TILT = 2u, YASL_PRIVATE_PEN_ROTATION = 4u, YASL_PRIVATE_PEN_DISTANCE = 8u, YASL_PRIVATE_PEN_TANGENTIAL = 16u, YASL_PRIVATE_PEN_WHEEL = 32u };
+enum yasl_private_presenter { YASL_PRIVATE_PRESENTER_NONE, YASL_PRIVATE_PRESENTER_CPU, YASL_PRIVATE_PRESENTER_GL, YASL_PRIVATE_PRESENTER_VULKAN, YASL_PRIVATE_PRESENTER_METAL };
+struct yasl_private_pen_sample {
+ uint64_t device_id, tool_id;
+ double x, y, pressure, tilt_x, tilt_y, rotation, distance, tangential, wheel_degrees;
+ uint32_t axes_valid, axes_changed, buttons, changed_button;
+ bool in_contact;
+ unsigned tool, action;
+};
+struct yasl_private_event {
+ /** Selects the valid union member. */
+ enum yasl_private_event_type type;
+ /** Recipient surface, or NULL for display-wide events. Can differ from the
+ * polled surface for routed popup input. */
+ struct yasl_surface *surface;
+ /** Monotonic timestamp in nanoseconds, same clock as yasl_private_loop_now_ns(). */
+ uint64_t time_ns;
+ /** Increasing display-wide enqueue order; zero for standalone private queues. */
+ uint64_t sequence;
+ /** Stable recipient identity; zero for display-wide events. */
+ uint64_t surface_id;
+ union {
+ struct yasl_private_pen_sample pen;
+ struct { const char *data; size_t len; uint64_t revision, generation; size_t begin, end; } text_edit;
+ struct yasl_private_key_event key; /**< KEY */
+ struct yasl_private_pointer_event pointer; /**< POINTER_* */
+ struct yasl_private_text_event text; /**< TEXT, legacy PASTE, DROP */
+ struct yasl_private_paste_event paste; /**< PASTE */
+ struct yasl_private_composition_event composition; /**< COMPOSITION */
+ struct yasl_private_state_event state; /**< WINDOW_STATE */
+ struct yasl_private_drag_target_event drag; /**< DRAG */
+ struct yasl_private_drop_hover_event drop; /**< DROP_HOVER */
+ struct yasl_private_focus_event focus; /**< FOCUS */
+ struct yasl_private_resize_event resize; /**< WINDOW_RESIZE */
+ struct yasl_private_user_event user; /**< USER */
+ };
+};
+
+/* Ownership and threading
+ * All calls and callbacks run on the display's opening thread (main thread on
+ * Cocoa). Displays borrow supplied loops; destroy surfaces before displays,
+ * popups before parents, and displays before their supplied loop.
+ * Specs, strings, callback tables and drag listeners are copied during calls.
+ * Userdata is borrowed until callbacks are removed or the object/drag ends.
+ * Surface destruction is allowed from its callbacks; display/loop destruction
+ * during dispatch or callbacks is not supported.
+ */
+
+/* Callbacks */
+/** Optional display notifications. NULL callback members are ignored. */
+struct yasl_private_display_cbs {
+ /** Application quit request; arrange orderly shutdown after dispatch returns. */
+ void (*quit)(void *ud);
+ /** Application reopen/activation request, currently Cocoa. */
+ void (*reopen)(void *ud);
+ /** Display connection lost; arrange cleanup after dispatch returns. */
+ void (*lost)(void *ud);
+};
+
+/** Optional window/popup notifications, copied from yasl_private_window_spec.cbs or by
+yasl_private_surface_set_callbacks().
+Callbacks may destroy their surface. They must then return without further use
+of the surface or draw target. NULL members are ignored. */
+struct yasl_private_window_cbs {
+ /** Initialize the entire target and optionally present. Requested through schedule_redraw/flush. */
+ void (*draw)(struct yasl_surface *s, struct yasl_private_frame *frame, void *ud);
+ /** Configured size/scale changed; reallocate application content and request a redraw. */
+ void (*configure)(struct yasl_surface *s, struct yasl_private_geometry geometry, void *ud);
+ /** Window/popup close request; application is responsible for destruction. */
+ void (*close)(struct yasl_surface *s, void *ud);
+ /** Reported yasl_private_surface_state bitmask changed; matches yasl_private_surface_state(s). */
+ void (*state)(struct yasl_surface *s, uint32_t state, void *ud);
+ /** YASL surface-drag target hover, leave, or drop; pos is in device pixels. */
+ void (*drag)(struct yasl_surface *s, enum yasl_private_drag_event ev,
+ struct yasl_private_vector2i pos, void *ud);
+ /** External data-drag hover or leave; pos is in device pixels. */
+ void (*drop)(struct yasl_surface *s, enum yasl_private_drop_event ev,
+ struct yasl_private_vector2i pos, void *ud);
+};
+
+/* Display Lifecycle */
+/** Runtime backend choice. AUTO selects a native backend and allows fallback. */
+enum yasl_private_backend {
+ YASL_PRIVATE_BACKEND_AUTO = 0, /**< Choose native backend with fallback. */
+ YASL_PRIVATE_BACKEND_WAYLAND, /**< Require Wayland. */
+ YASL_PRIVATE_BACKEND_X11, /**< Require X11. */
+ YASL_PRIVATE_BACKEND_COCOA, /**< Require Cocoa. */
+};
+
+/** Display options. Zero-initialize for automatic backend selection. */
+struct yasl_private_display_spec {
+ /** Copied application identifier, conventionally a reverse-domain UTF-8 string; NULL uses backend defaults. */
+ const char *app_id;
+ /** Copied optional display callbacks; NULL disables them. */
+ const struct yasl_private_display_cbs *cbs;
+ /** Borrowed display callback userdata. */
+ void *ud;
+
+ /** Zero/AUTO chooses native backend; explicit choices never fall back. */
+ enum yasl_private_backend backend;
+};
+
+/* Return NULL and set errno if the requested backend cannot open. */
+/**
+ * Open a desktop connection and its event machinery.
+ * @param loop Borrowed loop, or NULL to create a display-owned loop.
+ * @param spec Copied options, or NULL for automatic backend selection and defaults.
+ * @return An owned display, or NULL with errno set. EINVAL means an invalid backend;
+ * ENOSYS means the requested backend was not built; ENODEV is the fallback when
+ * opening fails without a more specific platform error. Explicit choices never
+ * fall back. Close with yasl_private_display_close() after destroying its surfaces.
+ */
+struct yasl_private_display *yasl_private_display_open(struct yasl_private_loop *loop,
+ const struct yasl_private_display_spec *spec);
+/**
+ * Read the selected backend.
+ * @param d Display, or NULL.
+ * @return The actual backend, or YASL_PRIVATE_BACKEND_AUTO for NULL.
+ */
+enum yasl_private_backend yasl_private_display_backend(const struct yasl_private_display *d);
+/**
+ * Close a display and release its owned loop, if any.
+ * @param d Display to close, or NULL for no action.
+ * Destroy all surfaces first. Cancels an active drag and may invoke its end
+ * listener. Must not be called during dispatch or from a callback.
+ */
+void yasl_private_display_close(struct yasl_private_display *d);
+/**
+ * Query currently available optional features.
+ * @param d Display, or NULL.
+ * @return A bitmask of yasl_private_display_cap values, or zero for NULL.
+ * Capabilities describe backend/server support, not attached input hardware or
+ * permission to perform an operation at any arbitrary moment.
+ */
+uint32_t yasl_private_display_caps(const struct yasl_private_display *d);
+/* Process events, waiting at most timeout_ms: zero polls, negative waits.
+ * Pending input or redraw work returns immediately. Returns -1 on failure. */
+/**
+ * Receive events and run ready loop sources.
+ * @param d Display to dispatch.
+ * @param timeout_ms Maximum wait in milliseconds: zero polls, negative waits indefinitely.
+ * @return A nonnegative backend-specific work count on success, or -1 with errno
+ * set on failure (EINVAL for NULL, EIO if no specific backend error is available).
+ * Do not interpret the count as the number of queued input events. Pending input
+ * or runnable redraw work prevents waiting. Drain input and call
+ * yasl_private_display_flush() after updating application state. Do not dispatch recursively.
+ */
+int yasl_private_display_dispatch(struct yasl_private_display *d, int timeout_ms);
+/**
+ * Draw scheduled surfaces and send pending display requests.
+ * @param d Display, or NULL for no action.
+ * May synchronously invoke draw and other callbacks. A paced surface may wait
+ * for its next display/compositor signal. This is not a GPU completion wait.
+ */
+void yasl_private_display_flush(struct yasl_private_display *d);
+/**
+ * Read the latest modifier state known to the backend.
+ * @param d Display, or NULL.
+ * @return A bitmask of yasl_private_mod values, or zero for NULL.
+ * For an individual event use its own mods field instead.
+ */
+int yasl_private_display_modifiers(const struct yasl_private_display *d);
+/** Double-click interval used when the system reports none. */
+#define YASL_PRIVATE_DOUBLE_CLICK_MS_DEFAULT 400
+/**
+ * Read the system's double-click interval.
+ * @param d Display, or NULL.
+ * @return Maximum milliseconds between the clicks of a double click, or
+ * YASL_PRIVATE_DOUBLE_CLICK_MS_DEFAULT when d is NULL or the system reports none.
+ * X11 reads the XSETTINGS Net/DoubleClickTime value. Wayland, and X11 without
+ * XSETTINGS, read the KDE or GNOME setting through the XDG Settings portal,
+ * which needs D-Bus and can wait briefly. Cocoa reads the system interval.
+ * The user can change it at any time; read it again when a window gains focus
+ * rather than on every click.
+ */
+int yasl_private_display_double_click_ms(const struct yasl_private_display *d);
+/* Unsupported/invalid selections are no-ops, never clipboard aliases. */
+/**
+ * Offer a copy of a NUL-terminated UTF-8 string as a selection.
+ * @param d Display, or NULL for no action.
+ * @param sel YASL_PRIVATE_CLIPBOARD or YASL_PRIVATE_PRIMARY.
+ * @param text Text copied during this call; NULL does nothing.
+ * Unsupported selections do nothing. The void return does not confirm that the
+ * system accepted the offer; Wayland may require a recent input serial.
+ */
+void yasl_private_display_copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text);
+/**
+ * Request text delivery to a particular surface.
+ * @param d Display that owns target.
+ * @param sel YASL_PRIVATE_CLIPBOARD or YASL_PRIVATE_PRIMARY.
+ * @param target Destination surface; NULL or a surface on another display does nothing.
+ * Results arrive as YASL_PRIVATE_EVENT_PASTE, possibly during a later dispatch. No result
+ * is promised for unavailable data or a failed transfer. Destroying target cancels
+ * delivery. Unsupported selections do nothing.
+ */
+void yasl_private_display_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target);
+
+/* A successful drag invokes the copied listener's non-NULL end once, including
+ * cancel and display close. Failed starts do not invoke it. */
+/**
+ * Start a surface drag from a recent pointer press.
+ * @param d Display that owns origin.
+ * @param origin Origin surface.
+ * @param image Optional copied premultiplied ARGB8888 drag image, or NULL.
+ * @param l Optional copied completion listener, or NULL.
+ * @param ud Borrowed listener userdata until completion.
+ * @return true if started; false if unsupported, invalid, or not currently permitted.
+ * A successful start invokes a non-NULL end callback exactly once, including
+ * cancellation or display close. Failed starts do not invoke it. This API carries
+ * surfaces, not arbitrary MIME payloads; use application state to identify the item.
+ */
+bool yasl_private_display_drag_start(struct yasl_private_display *d, struct yasl_surface *origin,
+ const struct yasl_private_drag_image *image,
+ const struct yasl_private_drag_listener *l, void *ud);
+/**
+ * Cancel the active surface drag, if any.
+ * @param d Display, or NULL for no action.
+ * May invoke the end listener synchronously.
+ */
+void yasl_private_display_drag_cancel(struct yasl_private_display *d);
+
+/* Surface Lifecycle */
+/** Toplevel options. Zero-initialize, then supply a positive logical-pixel size. */
+struct yasl_private_window_spec {
+ unsigned flags;
+ bool external_gpu;
+ /** Copied UTF-8 title; NULL uses backend defaults. */
+ const char *title;
+ /** Requested logical-pixel width and height; both must be positive. */
+ union {
+ struct yasl_private_vector2i size_logical; /**< Requested logical-pixel extent. */
+ struct yasl_private_vector2i size; /**< Migration alias for size_logical. */
+ };
+ /** Request initial fullscreen; actual state is reported asynchronously. */
+ bool fullscreen;
+ /** Permit unpaced presentation where supported; a hint, not a guarantee. */
+ bool tearing;
+ /** Copied optional window callbacks; NULL disables them. */
+ const struct yasl_private_window_cbs *cbs;
+ /** Borrowed window callback userdata; ignored by the runner. */
+ void *ud;
+ /** Borrowed application data, independent of callbacks and available in either loop style. */
+ void *userdata;
+};
+
+/** Popup options, entirely in device pixels, including size.
+The content rectangle excludes transparent margins/shadows used for placement.
+An axis of content_size equal to zero extends to that axis of size. The offset
+and resolved content size must fit in size. Anchor extents must be nonnegative.
+Dimensions must be positive, at most 32767, and fit an INT32_MAX-byte buffer;
+backends may impose further limits after scaling or resolving placement. */
+struct yasl_private_popup_spec {
+ unsigned anchor_edges, gravity, constraints;
+ bool positioner_explicit;
+ /** Full popup framebuffer extent in device pixels, including transparent margins. */
+ struct yasl_private_vector2i size;
+ /** Rectangle in parent framebuffer coordinates, in device pixels. */
+ struct yasl_private_rect2i anchor;
+ /** true prefers right of anchor; false prefers below. Placement may flip/slide. */
+ bool beside;
+ /** Top-left content offset within the popup, in device pixels. */
+ struct yasl_private_vector2i content_offset;
+ /** Content extent; zero per axis extends to the corresponding size edge. */
+ struct yasl_private_vector2i content_size;
+};
+
+/**
+ * Create a resizable desktop window with CPU drawing targets.
+ * @param d Owning display.
+ * @param spec Required copied options; size is in logical pixels.
+ * @return An owned surface, or NULL with errno set. EINVAL means invalid options,
+ * including a nonpositive dimension, a dimension above 32767, or a buffer above
+ * INT32_MAX bytes. ENODEV is the fallback backend failure. Actual configured
+ * size may differ from the request. The callback table in spec->cbs is copied;
+ * spec->ud stays borrowed until callbacks are replaced or the surface is destroyed.
+ * This window's callbacks cannot run before creation returns. Use
+ * yasl_private_surface_geometry() for initial geometry; an initial configure callback
+ * is not guaranteed. Schedule an initial redraw after setting up application state.
+ * On a display owned by yasl_private_run(), the runner installs its own callbacks instead;
+ * use yasl_private_app_desc.event and yasl_private_app_desc.frame for application notifications.
+ */
+struct yasl_surface *yasl_private_surface_create_toplevel(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec);
+/**
+ * Create an owned popup attached to a parent surface.
+ * @param parent Borrowed parent; destroy the popup before the parent.
+ * @param spec Required copied options, entirely in device pixels.
+ * @return A popup, or NULL with errno set: EINVAL for invalid options, ENOTSUP
+ * for missing popup support, or a backend error (ENODEV if none was supplied).
+ * Wayland requires a recent input serial for its popup grab. The backend may
+ * flip or slide placement to fit the screen. See yasl_private_popup_spec.
+ */
+struct yasl_surface *yasl_private_surface_create_popup(struct yasl_surface *parent,
+ const struct yasl_private_popup_spec *spec);
+/**
+ * Update a popup's geometry and placement.
+ * @param popup An existing popup.
+ * @param spec Required copied replacement options, in device pixels.
+ * @return true on success; false with errno set: EINVAL for invalid options,
+ * ENOTSUP for missing popup support, or a backend error (ENODEV if unspecified).
+ * Older Wayland popup protocol versions can reject repositioning.
+ */
+bool yasl_private_surface_popup_reposition(struct yasl_surface *popup,
+ const struct yasl_private_popup_spec *spec);
+/**
+ * Destroy a surface and cancel its surface timers and pending input.
+ * @param s Surface, or NULL for no action.
+ * Destroy child popups first. May be called from a surface callback; return from
+ * that callback without further use of the surface or its draw target. Every
+ * pointer or native handle obtained from this surface becomes invalid.
+ */
+void yasl_private_surface_destroy(struct yasl_surface *s);
+/**
+ * Get the display that owns a surface.
+ * @param s Surface, or NULL.
+ * @return A borrowed display, or NULL.
+ */
+struct yasl_private_display *yasl_private_surface_display(const struct yasl_surface *s);
+
+/**
+ * Replace the surface's callback table and userdata.
+ * @param s Surface.
+ * @param cbs Table copied by value; NULL removes all callbacks. NULL members are ignored.
+ * @param ud Borrowed userdata passed to the installed callbacks.
+ * @return true if replaced; false with EINVAL for NULL, EBUSY on a runner-owned surface.
+ * This call only replaces callbacks; it does not invoke them.
+ * Use yasl_private_surface_geometry() to read current geometry; do not rely on receiving
+ * an initial configure callback when registering.
+ */
+bool yasl_private_surface_set_callbacks(struct yasl_surface *s,
+ const struct yasl_private_window_cbs *cbs, void *ud);
+/**
+ * Replace the window title with copied UTF-8 text.
+ * @param s Surface.
+ * @param title NUL-terminated UTF-8 title. Pass an empty string to clear it.
+ * Backends may truncate the title.
+ */
+void yasl_private_surface_set_title(struct yasl_surface *s, const char *title);
+/**
+ * Request a future draw callback, coalescing repeated requests.
+ * @param s Surface, or NULL for no action.
+ * Call yasl_private_display_flush() to service the request. Schedule again from draw to
+ * animate. Without YASL_PRIVATE_CAP_FRAME_PACING, throttle animation with a timer.
+ */
+void yasl_private_surface_schedule_redraw(struct yasl_surface *s);
+/* Present exactly the target supplied to the current draw callback, at most
+ * once. Targets expire when that callback returns or the surface is destroyed.
+ * damage is required: n must be -1 or 0..YASL_PRIVATE_DAMAGE_MAX. Rectangles must have
+ * nonnegative extents; they are clipped to the target. Invalid input returns
+ * false without consuming the frame. See yasl/native.h for native rendering. */
+/**
+ * Submit the current draw target at most once.
+ * @param s Surface currently being drawn.
+ * @param fb Exact target from the current draw callback, including its generation.
+ * @param damage Required damage hint: n is -1 for all pixels or 0..YASL_PRIVATE_DAMAGE_MAX.
+ * @return true if accepted; false for invalid, stale, already presented, or failed targets.
+ * Rectangles must have nonnegative extents and are clipped to the target. Invalid
+ * input does not consume the frame. All pixels must be initialized even with
+ * partial damage. The backend may upload all pixels. Success records submission,
+ * not confirmed scanout. The target expires when draw returns or s is destroyed.
+ */
+bool yasl_private_surface_present(struct yasl_surface *s, const struct yasl_private_framebuffer *fb,
+ const struct yasl_private_damage_list *damage);
+/**
+ * Migration adapter: consume one input event for a surface from the display FIFO.
+ * Popup input can belong to the root queue; inspect ev->surface. Returns false
+ * for empty/invalid arguments, leaving ev unchanged. Strings remain borrowed
+ * until the next successful display or surface poll, or display close. Pending
+ * recipient events are canceled on destruction. Prefer yasl_private_display_poll_event()
+ * to receive window, composition, and display events as well as input. This
+ * input-only adapter discards pending non-input events across the display
+ * (legacy callbacks already delivered lifecycle changes). Do not mix
+ * the two polling styles in one application. Use the display thread.
+ */
+bool yasl_private_surface_poll_event(struct yasl_surface *s, struct yasl_private_event *ev);
+/**
+ * Decode the next local file path from a YASL_PRIVATE_EVENT_DROP payload.
+ * @param cursor Required in/out position in the NUL-terminated list. Start it
+ * at event.text.data; a NULL *cursor returns zero.
+ * @param out Output for the NUL-terminated path, or NULL when cap is zero.
+ * @param cap Size of out in bytes.
+ * @return Path length in bytes, excluding the NUL, or zero when no paths remain.
+ * When the result is less than cap, out holds the path and *cursor moves past
+ * it. Otherwise out and *cursor are unchanged; call again with a buffer of at
+ * least the result plus one.
+ * Accepts file:///path, file:/path, and file://host/path when host is
+ * localhost or this machine's name. Percent escapes decode to raw bytes, and
+ * a '?' or '#' ends the path. Skips blank lines, comments, other schemes,
+ * other hosts, and lines with a malformed escape or an escaped NUL. May be
+ * called from any thread.
+ */
+size_t yasl_private_drop_next_path(const char **cursor, char *out, size_t cap);
+/**
+ * Copy the last frame presented from the surface's CPU target.
+ * @param s Surface.
+ * @param out Output for size->x * size->y premultiplied ARGB8888 pixels with
+ * tightly packed rows, or NULL when cap is zero.
+ * @param cap Capacity of out in pixels.
+ * @param size Required output: the frame extent in device pixels, or zero when
+ * no frame is available.
+ * @return true if copied. false leaves out unchanged; when *size is nonzero,
+ * out was too small, so allocate size->x * size->y pixels and call again.
+ * A frame is available from a successful yasl_private_surface_present() until the
+ * next draw callback starts, and again once that draw presents. After a draw
+ * that does not present or a resize that replaces the target, no frame may be
+ * available; native rendering never has one. Call outside draw callbacks. This copies YASL's frame, not
+ * the composited screen: decorations, other windows, and color conversion are
+ * not included.
+ */
+bool yasl_private_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
+ struct yasl_private_vector2i *size);
+/**
+ * Select absolute or relative (locked) pointer input for a surface.
+ * @param s Surface.
+ * @param mode YASL_PRIVATE_POINTER_NORMAL or YASL_PRIVATE_POINTER_RELATIVE.
+ * @return true if the mode is supported; false leaves the current mode.
+ * RELATIVE hides the cursor and locks it while the surface is focused;
+ * POINTER_MOTION then carries unbounded delta. Returning to NORMAL restores
+ * the cursor selected with yasl_private_surface_set_cursor(). RELATIVE is supported on
+ * Wayland compositors with the relative-pointer and pointer-constraints
+ * protocols; X11 and Cocoa return false.
+ */
+bool yasl_private_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_private_pointer_mode mode);
+
+/**
+ * Consume one event from the display-wide FIFO without waiting.
+ * Returns false for an empty queue or invalid arguments, leaving ev unchanged.
+ * Strings remain valid until the next successful poll or display close.
+ * Surface destruction cancels undelivered events, not the last polled payload.
+ * Use the display thread. Do not mix display and surface polling in one app.
+ */
+bool yasl_private_display_poll_event(struct yasl_private_display *d, struct yasl_private_event *ev);
+
+/** Stable nonzero identity within a display; zero for NULL. Use on the display thread. */
+uint64_t yasl_private_surface_id(const struct yasl_surface *s);
+/** Store borrowed application data independently of callback userdata. */
+void yasl_private_surface_set_userdata(struct yasl_surface *s, void *userdata);
+/** Read borrowed application data, or NULL for NULL; use on the display thread. */
+void *yasl_private_surface_userdata(const struct yasl_surface *s);
+
+/** Thread-safe message descriptor. Zero target_id means display-wide. */
+struct yasl_private_message {
+ uint64_t target_id; /**< Surface identity, or zero for the display. */
+ uint32_t code; /**< Application-defined message code. */
+ void *payload; /**< Borrowed until release; passed through unchanged. */
+ size_t size; /**< Application-defined payload length. */
+ /** Optional cleanup on display thread after delivery/cancellation, exactly once.
+ * May post messages, but must not dispatch, poll, or destroy YASL objects. */
+ void (*release)(void *payload);
+};
+/**
+ * Copy and enqueue a message from any thread; the display must stay alive.
+ * Stop producer threads before closing the display. On success release runs
+ * once after consumption or cancellation. On failure ownership stays with the
+ * caller; errno is EINVAL, ENOENT (retired identity), ENOMEM, or EOVERFLOW.
+ */
+bool yasl_private_display_post(struct yasl_private_display *d, const struct yasl_private_message *message);
+
+/**
+ * Present the active frame once. NULL/0 means full damage; non-NULL/0 means none.
+ * Validate all rectangles; excess rectangles fall back to full damage.
+ * Returns false with EINVAL for invalid/stale targets, EALREADY for a consumed
+ * frame, or errno from the backend (EIO if unspecified).
+ * Frame and pixels expire at callback return; use the display thread.
+ */
+bool yasl_private_frame_present(struct yasl_private_frame *frame,
+ const struct yasl_private_rect2i *rects, size_t count);
+/** Coalesce a redraw request; use on the display thread. NULL is ignored. */
+void yasl_private_surface_request_frame(struct yasl_surface *s);
+/** Set drawing policy for either loop style. Returns false with errno on failure. */
+bool yasl_private_surface_set_pacing(struct yasl_surface *s, yasl_private_pacing_mode mode);
+
+/** Request a positive logical-pixel size. Success means issued, not configured. */
+bool yasl_private_surface_set_size(struct yasl_surface *s, struct yasl_private_vector2i size_logical);
+/** Request fullscreen state; returns acceptance, not confirmation. */
+bool yasl_private_surface_set_fullscreen(struct yasl_surface *s, bool enabled);
+/** Request maximized state; returns acceptance, not confirmation. */
+bool yasl_private_surface_set_maximized(struct yasl_surface *s, bool enabled);
+/** Request minimization. Window requests set errno on failure; display thread only. */
+bool yasl_private_surface_minimize(struct yasl_surface *s);
+
+/** Offer copied UTF-8 text. Returns false with errno if invalid/unsupported/failed. */
+bool yasl_private_clipboard_set(struct yasl_private_display *d, enum yasl_private_selection selection,
+ const char *text);
+/**
+ * Request text; returns a nonzero ID or zero with errno. One PASTE completion
+ * follows each accepted request unless its surface/display is destroyed.
+ * Inspect paste.status even when data is empty. Use the display thread.
+ */
+uint64_t yasl_private_clipboard_request(struct yasl_surface *s, enum yasl_private_selection selection);
+
+/* Current device-pixel size and scale, matching the configure callback. */
+/**
+ * Read current device-pixel size and scale together.
+ * @param s Surface, or NULL.
+ * @return Geometry matching configure callbacks; NULL gives zero size and unit scale.
+ */
+struct yasl_private_geometry yasl_private_surface_geometry(const struct yasl_surface *s);
+/**
+ * Read current window state.
+ * @param s Surface, or NULL.
+ * @return A bitmask of yasl_private_surface_state values, or zero for NULL.
+ * Flags are reported state, not requests; unsupported states are absent.
+ * Flags may coexist. Windowed and floating have no separate flags. Suspension
+ * does not imply minimization, and no minimized flag is reported.
+ */
+uint32_t yasl_private_surface_state(const struct yasl_surface *s);
+/* Timing uses monotonic nanoseconds. Check source before treating timestamps
+ * as presentation confirmation. refresh/next_present are estimates. Without
+ * YASL_PRIVATE_CAP_FRAME_PACING, redraws run on flush without display synchronization.
+ * Tearing may bypass pacing even when the capability is present. */
+/**
+ * Read frame timing estimates and their provenance.
+ * @param s Surface, or NULL.
+ * @return Current timing, or a zero-initialized result for NULL.
+ * Timestamps use monotonic nanoseconds. Inspect source before treating a timestamp
+ * as confirmation of presentation. Refresh and next-present values are estimates.
+ */
+struct yasl_private_timing yasl_private_surface_timing(const struct yasl_surface *s);
+/**
+ * Get a nondecreasing animation timestamp aligned to estimated refresh.
+ * @param s Surface, or NULL.
+ * @param now Current monotonic nanoseconds, usually from yasl_private_loop_now_ns().
+ * @return Animation timestamp, or zero for NULL. Before timing is known, returns now.
+ * Use this to advance animations; it is not presentation feedback.
+ */
+uint64_t yasl_private_surface_frame_time_ns(struct yasl_surface *s, uint64_t now);
+/* Set the mouse and tablet cursor for this surface, including future entries.
+ * YASL_PRIVATE_CURSOR_HIDDEN stays hidden until another cursor is selected. */
+/**
+ * Select the surface's system mouse and tablet cursor.
+ * @param s Surface.
+ * @param cursor Cursor shape; YASL_PRIVATE_CURSOR_HIDDEN hides the cursor without disabling input.
+ * The choice persists across motion and re-entry until another shape is selected.
+ */
+void yasl_private_surface_set_cursor(struct yasl_surface *s, enum yasl_private_cursor cursor);
+/**
+ * Set a backend window background hint.
+ * @param s Surface.
+ * @param bg Packed 0xAARRGGBB color; backends may ignore alpha or the entire hint.
+ * Does not initialize framebuffer pixels or replace drawing.
+ */
+void yasl_private_surface_set_background_color(struct yasl_surface *s, uint32_t bg);
+
+/* IME Support: preedit is borrowed until the next dispatch or destruction. */
+/**
+ * Read the current uncommitted UTF-8 composition text.
+ * @param s Surface, or NULL.
+ * @return Borrowed NUL-terminated text, or NULL if absent/unsupported.
+ * Copy to retain across the next dispatch or surface destruction. Composition
+ * changes can schedule a redraw. Insert committed YASL_PRIVATE_EVENT_TEXT into documents,
+ * not preedit text.
+ */
+const char *yasl_private_surface_ime_preedit(const struct yasl_surface *s);
+/**
+ * Read the cursor/selection range in the preedit string.
+ * @param s Surface.
+ * @param begin Required output UTF-8 byte offset for the range start.
+ * @param end Required output UTF-8 byte offset for the range end.
+ * @return true if available; false without changing outputs otherwise.
+ * Currently available on Wayland; Cocoa preedit text has no range result.
+ */
+bool yasl_private_surface_ime_preedit_cursor(const struct yasl_surface *s, int *begin, int *end);
+/**
+ * Place the composition candidate window near the application caret.
+ * @param s Surface.
+ * @param caret Caret rectangle relative to the surface, in device pixels.
+ * Update after caret motion or scale changes. Unsupported backends ignore it.
+ */
+void yasl_private_surface_set_ime_caret(struct yasl_surface *s, struct yasl_private_rect2i caret);
+
+/* Client-Side Decorations (CSD) Actions */
+/**
+ * Ask the window system to begin an interactive move.
+ * @param s Toplevel surface, normally during handling of a pointer press.
+ * @return true if the request was issued; false if unsupported or not permitted.
+ * A recent input serial may be required. Success does not confirm a final position.
+ */
+bool yasl_private_surface_start_interactive_move(struct yasl_surface *s);
+/**
+ * Ask the window system to begin an interactive resize.
+ * @param s Toplevel surface, normally during handling of a pointer press.
+ * @param edge One nonzero yasl_private_resize_edge enumerator.
+ * @return true if issued; false for an invalid edge, missing support, or rejected request.
+ */
+bool yasl_private_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_private_resize_edge edge);
+/**
+ * Ask the window system to display its window management menu.
+ * @param s Toplevel surface.
+ * @param pos Surface-relative position in device pixels.
+ * @return true if issued; false if unsupported or not currently permitted.
+ */
+bool yasl_private_surface_show_window_menu(struct yasl_surface *s, struct yasl_private_vector2i pos);
+
+/* Drag Support */
+/**
+ * Attach a toplevel surface to the current surface drag.
+ * @param s Surface to carry; check YASL_PRIVATE_CAP_DRAG_CARRY first.
+ * @param offset Device-pixel offset of the pointer within the carried surface.
+ * @return true if accepted; false if unsupported or no compatible drag is active.
+ */
+bool yasl_private_surface_drag_carry(struct yasl_surface *s, struct yasl_private_vector2i offset);
+/**
+ * Place a toplevel surface relative to the current pointer.
+ * @param s Surface; check YASL_PRIVATE_CAP_PLACE_WINDOW first.
+ * @param at Device-pixel position within the surface to place under the pointer.
+ * @return true if issued; false if unsupported or unavailable.
+ */
+bool yasl_private_surface_place_under_pointer(struct yasl_surface *s, struct yasl_private_vector2i at);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* YASL_PRIVATE_H */
diff --git a/src/common/uri.c b/src/common/uri.c
index d836943..cc539a1 100644
--- a/src/common/uri.c
+++ b/src/common/uri.c
@@ -1,5 +1,5 @@
#define _POSIX_C_SOURCE 200809L
-#include "yasl/yasl.h"
+#include "common/types.h"
#include <string.h>
#include <strings.h>
@@ -55,7 +55,7 @@ static size_t decode_path(const char *line, size_t len, char *out)
return n;
}
-size_t yasl_drop_next_path(const char **cursor, char *out, size_t cap)
+size_t yasl_private_drop_next_path(const char **cursor, char *out, size_t cap)
{
if (!cursor || !*cursor || (!out && cap)) return 0;
const char *p = *cursor;
diff --git a/src/common/utf8.c b/src/common/utf8.c
index 78a5d8e..2fb737a 100644
--- a/src/common/utf8.c
+++ b/src/common/utf8.c
@@ -11,7 +11,7 @@
#define unlikely(x) __builtin_expect(!!(x), 0)
#endif
-bool yasl_utf8_validate(const char *str, size_t len)
+bool yasl_private_utf8_validate(const char *str, size_t len)
{
if (!str) return len == 0;
const unsigned char *p = (const unsigned char *)str;
@@ -54,7 +54,7 @@ bool yasl_utf8_validate(const char *str, size_t len)
return true;
}
-char *yasl_utf8_from_latin1(const char *str, size_t len, size_t *out_len)
+char *yasl_private_utf8_from_latin1(const char *str, size_t len, size_t *out_len)
{
if (len > (SIZE_MAX - 1) / 2) return NULL;
char *out = malloc(len * 2 + 1);
@@ -73,7 +73,7 @@ char *yasl_utf8_from_latin1(const char *str, size_t len, size_t *out_len)
return out;
}
-char *yasl_utf8_to_latin1(const char *str, size_t *out_len)
+char *yasl_private_utf8_to_latin1(const char *str, size_t *out_len)
{
char *out = malloc(strlen(str) + 1);
if (!out) return NULL;
@@ -94,11 +94,11 @@ char *yasl_utf8_to_latin1(const char *str, size_t *out_len)
return out;
}
-char *yasl_utf8_sanitize_dup(const char *str)
+char *yasl_private_utf8_sanitize_dup(const char *str)
{
if (unlikely(!str)) return NULL;
size_t len = strlen(str);
- if (likely(yasl_utf8_validate(str, len))) {
+ if (likely(yasl_private_utf8_validate(str, len))) {
char *out = malloc(len + 1);
if (unlikely(!out)) return NULL;
memcpy(out, str, len + 1);
diff --git a/src/common/utf8.h b/src/common/utf8.h
index f31c99d..5ffdb15 100644
--- a/src/common/utf8.h
+++ b/src/common/utf8.h
@@ -1,14 +1,14 @@
-#ifndef YASL_COMMON_UTF8_H
-#define YASL_COMMON_UTF8_H
+#ifndef YASL_PRIVATE_COMMON_UTF8_H
+#define YASL_PRIVATE_COMMON_UTF8_H
#include <stdbool.h>
#include <stddef.h>
-bool yasl_utf8_validate(const char *str, size_t len);
-char *yasl_utf8_sanitize_dup(const char *str);
+bool yasl_private_utf8_validate(const char *str, size_t len);
+char *yasl_private_utf8_sanitize_dup(const char *str);
/* Owned conversions for X11 STRING interoperability. to_latin1 rejects text
* outside Latin-1; byte lengths exclude the allocated NUL terminator. */
-char *yasl_utf8_from_latin1(const char *str, size_t len, size_t *out_len);
-char *yasl_utf8_to_latin1(const char *str, size_t *out_len);
+char *yasl_private_utf8_from_latin1(const char *str, size_t len, size_t *out_len);
+char *yasl_private_utf8_to_latin1(const char *str, size_t *out_len);
#endif
diff --git a/src/common/vulkan.c b/src/common/vulkan.c
new file mode 100644
index 0000000..1bca415
--- /dev/null
+++ b/src/common/vulkan.c
@@ -0,0 +1,87 @@
+#if YASL_HAS_WAYLAND
+#define VK_USE_PLATFORM_WAYLAND_KHR
+#endif
+#if YASL_HAS_X11
+#define VK_USE_PLATFORM_XLIB_KHR
+#endif
+#if YASL_HAS_COCOA
+#define VK_USE_PLATFORM_METAL_EXT
+#endif
+#include "yasl/vulkan.h"
+#include "public_internal.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#if YASL_HAS_WAYLAND
+#include "yasl/native/wayland.h"
+#endif
+#if YASL_HAS_X11
+#include "yasl/native/x11.h"
+#endif
+static PFN_vkGetInstanceProcAddr get_proc;
+static bool portability;
+void yasl_private_vulkan_reset(void) { get_proc=NULL; portability=false; }
+bool yasl_vulkan_init(PFN_vkGetInstanceProcAddr loader)
+{
+ if(!loader)return yasl_public_fail(EINVAL);
+ get_proc=loader;portability=false;
+ PFN_vkEnumerateInstanceExtensionProperties enumerate=(PFN_vkEnumerateInstanceExtensionProperties)get_proc(VK_NULL_HANDLE,"vkEnumerateInstanceExtensionProperties");
+ if(!enumerate)return yasl_public_fail(ENOTSUP);
+ uint32_t count=0;VkResult r=enumerate(NULL,&count,NULL);
+ if(r!=VK_SUCCESS)return yasl_public_fail(EIO);
+ VkExtensionProperties *ext=count?calloc(count,sizeof(*ext)):NULL;
+ if(count&&!ext)return yasl_public_fail(ENOMEM);
+ r=enumerate(NULL,&count,ext);
+ if(r==VK_SUCCESS)for(uint32_t i=0;i<count;i++)if(!strcmp(ext[i].extensionName,"VK_KHR_portability_enumeration"))portability=true;
+ free(ext);return r==VK_SUCCESS||yasl_public_fail(EIO);
+}
+const char *const *yasl_vulkan_instance_extensions(const yasl_surface *s,uint32_t *count)
+{
+ static const char *wayland[]={"VK_KHR_surface","VK_KHR_wayland_surface"};
+ static const char *x11[]={"VK_KHR_surface","VK_KHR_xlib_surface"};
+ static const char *metal[]={"VK_KHR_surface","VK_EXT_metal_surface","VK_KHR_portability_enumeration"};
+ if(count)*count=0;
+ if(!s||!count||s->presenter==YASL_PRIVATE_PRESENTER_CPU||s->presenter==YASL_PRIVATE_PRESENTER_GL){yasl_public_fail(EINVAL);return NULL;}
+ switch(s->display->ops->type) {
+ case YASL_PRIVATE_BACKEND_WAYLAND:*count=2;return wayland;
+ case YASL_PRIVATE_BACKEND_X11:*count=2;return x11;
+ case YASL_PRIVATE_BACKEND_COCOA:*count=portability?3:2;return metal;
+ default:yasl_public_fail(ENOTSUP);return NULL;
+ }
+}
+VkInstanceCreateFlags yasl_vulkan_instance_flags(const yasl_surface *s)
+{ return s&&s->display->ops->type==YASL_PRIVATE_BACKEND_COCOA&&portability?VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR:0; }
+VkResult yasl_vulkan_create_surface(yasl_surface *s,VkInstance instance,const VkAllocationCallbacks *allocator,VkSurfaceKHR *out)
+{
+ if(out)*out=VK_NULL_HANDLE;
+ if(!yasl_public_surface(s,false)||!instance||!out||!get_proc){yasl_public_fail(EINVAL);return VK_ERROR_INITIALIZATION_FAILED;}
+ if(s->presenter!=YASL_PRIVATE_PRESENTER_NONE){yasl_public_fail(EBUSY);return VK_ERROR_NATIVE_WINDOW_IN_USE_KHR;}
+ VkResult result=VK_ERROR_EXTENSION_NOT_PRESENT;
+ switch(s->display->ops->type) {
+#if YASL_HAS_WAYLAND
+ case YASL_PRIVATE_BACKEND_WAYLAND: {
+ PFN_vkCreateWaylandSurfaceKHR create=(PFN_vkCreateWaylandSurfaceKHR)get_proc(instance,"vkCreateWaylandSurfaceKHR");
+ if(create) { VkWaylandSurfaceCreateInfoKHR info={.sType=VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR,
+ .display=yasl_wayland_display(s),.surface=yasl_wayland_surface(s)};result=create(instance,&info,allocator,out); }break;
+ }
+#endif
+#if YASL_HAS_X11
+ case YASL_PRIVATE_BACKEND_X11: {
+ PFN_vkCreateXlibSurfaceKHR create=(PFN_vkCreateXlibSurfaceKHR)get_proc(instance,"vkCreateXlibSurfaceKHR");
+ if(create) { VkXlibSurfaceCreateInfoKHR info={.sType=VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
+ .dpy=yasl_x11_display(s),.window=(Window)yasl_x11_window(s)};result=create(instance,&info,allocator,out); }break;
+ }
+#endif
+#if YASL_HAS_COCOA
+ case YASL_PRIVATE_BACKEND_COCOA: {
+ PFN_vkCreateMetalSurfaceEXT create=(PFN_vkCreateMetalSurfaceEXT)get_proc(instance,"vkCreateMetalSurfaceEXT");
+ if(create&&s->display->ops->surface_get_metal_layer) { VkMetalSurfaceCreateInfoEXT info={.sType=VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT,
+ .pLayer=s->display->ops->surface_get_metal_layer(s)};result=create(instance,&info,allocator,out); }break;
+ }
+#endif
+ default:break;
+ }
+ if(result==VK_SUCCESS)s->presenter=YASL_PRIVATE_PRESENTER_VULKAN;
+ else yasl_public_fail(result==VK_ERROR_EXTENSION_NOT_PRESENT?ENOTSUP:EIO);
+ return result;
+}
diff --git a/src/platform/cocoa/api.h b/src/platform/cocoa/api.h
index eaacc01..4a22410 100644
--- a/src/platform/cocoa/api.h
+++ b/src/platform/cocoa/api.h
@@ -1,40 +1,40 @@
-#ifndef YASL_COCOA_API_H
-#define YASL_COCOA_API_H
+#ifndef YASL_PRIVATE_COCOA_API_H
+#define YASL_PRIVATE_COCOA_API_H
-#include "yasl/yasl.h"
+#include "common/types.h"
struct cocoa_display;
struct cocoa_surface;
-struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec);
+struct cocoa_display *cocoa_display_open(struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec);
void cocoa_display_close(struct cocoa_display *d);
uint32_t cocoa_display_caps(const struct cocoa_display *d);
int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms);
void cocoa_display_flush(struct cocoa_display *d);
int cocoa_display_modifiers(const struct cocoa_display *d);
-void cocoa_display_copy(struct cocoa_display *d, enum yasl_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_copy(struct cocoa_display *d, enum yasl_private_selection sel, const char *text);
+void cocoa_display_paste(struct cocoa_display *d, enum yasl_private_selection sel, struct cocoa_surface *target);
+bool cocoa_display_drag_start(struct cocoa_display *d, struct cocoa_surface *origin, const struct yasl_private_drag_image *image, const struct yasl_private_drag_listener *l, void *ud);
struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d,
- const struct yasl_window_spec *spec, void *graphics_device);
-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);
+ const struct yasl_private_window_spec *spec, void *graphics_device);
+struct cocoa_surface *cocoa_surface_create_popup(struct cocoa_surface *parent, const struct yasl_private_popup_spec *spec);
+bool cocoa_surface_popup_reposition(struct cocoa_surface *popup, const struct yasl_private_popup_spec *spec);
void cocoa_surface_destroy(struct cocoa_surface *s);
-void cocoa_surface_set_callbacks(struct cocoa_surface *s, const struct yasl_window_cbs *cbs, void *ud);
+void cocoa_surface_set_callbacks(struct cocoa_surface *s, const struct yasl_private_window_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_poll_event(struct cocoa_surface *s, struct yasl_event *ev);
-struct yasl_geometry cocoa_surface_geometry(const struct cocoa_surface *s);
-struct yasl_timing cocoa_surface_timing(const struct cocoa_surface *s);
-void cocoa_surface_set_cursor(struct cocoa_surface *s, enum yasl_cursor cursor);
+bool cocoa_surface_present(struct cocoa_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage);
+bool cocoa_surface_poll_event(struct cocoa_surface *s, struct yasl_private_event *ev);
+struct yasl_private_geometry cocoa_surface_geometry(const struct cocoa_surface *s);
+struct yasl_private_timing cocoa_surface_timing(const struct cocoa_surface *s);
+void cocoa_surface_set_cursor(struct cocoa_surface *s, enum yasl_private_cursor cursor);
void cocoa_surface_set_background_color(struct cocoa_surface *s, uint32_t bg);
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);
+void cocoa_surface_set_ime_caret(struct cocoa_surface *s, struct yasl_private_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);
+bool cocoa_surface_start_interactive_resize(struct cocoa_surface *s, enum yasl_private_resize_edge edge);
+bool cocoa_surface_show_window_menu(struct cocoa_surface *s, struct yasl_private_vector2i pos);
+bool cocoa_surface_drag_carry(struct cocoa_surface *s, struct yasl_private_vector2i offset);
+bool cocoa_surface_place_under_pointer(struct cocoa_surface *s, struct yasl_private_vector2i at);
#endif
diff --git a/src/platform/cocoa/desktop.m b/src/platform/cocoa/desktop.m
index 526cbb9..76aa551 100644
--- a/src/platform/cocoa/desktop.m
+++ b/src/platform/cocoa/desktop.m
@@ -2,7 +2,7 @@
#import <UserNotifications/UserNotifications.h>
#include "common/desktop.h"
-#include "yasl/desktop.h"
+#include "common/private_desktop.h"
#include <CoreFoundation/CoreFoundation.h>
#include <limits.h>
@@ -94,11 +94,11 @@ uint32_t desktop_appearance(void)
NSAppearanceName name = [NSApp.effectiveAppearance
bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];
if ([name isEqualToString:NSAppearanceNameDarkAqua])
- flags |= YASL_APPEARANCE_DARK;
+ flags |= YASL_PRIVATE_APPEARANCE_DARK;
else if (name)
- flags |= YASL_APPEARANCE_LIGHT;
+ flags |= YASL_PRIVATE_APPEARANCE_LIGHT;
if ([NSWorkspace sharedWorkspace].accessibilityDisplayShouldIncreaseContrast)
- flags |= YASL_APPEARANCE_HIGH_CONTRAST;
+ flags |= YASL_PRIVATE_APPEARANCE_HIGH_CONTRAST;
return flags;
}
}
diff --git a/src/platform/cocoa/keymap.c b/src/platform/cocoa/keymap.c
index b81c364..c43837c 100644
--- a/src/platform/cocoa/keymap.c
+++ b/src/platform/cocoa/keymap.c
@@ -1,7 +1,7 @@
#include "keymap.h"
#include "keycodes.h"
-enum yasl_key yasl_key_from_cocoa(uint32_t code)
+enum yasl_key yasl_private_key_from_cocoa(uint32_t code)
{
switch (code) {
case kVK_ANSI_A: return YASL_KEY_A;
diff --git a/src/platform/cocoa/keymap.h b/src/platform/cocoa/keymap.h
index a5394e2..792b10d 100644
--- a/src/platform/cocoa/keymap.h
+++ b/src/platform/cocoa/keymap.h
@@ -1,8 +1,8 @@
-#ifndef YASL_COCOA_KEYMAP_H
-#define YASL_COCOA_KEYMAP_H
+#ifndef YASL_PRIVATE_COCOA_KEYMAP_H
+#define YASL_PRIVATE_COCOA_KEYMAP_H
#include "yasl/keycodes.h"
-enum yasl_key yasl_key_from_cocoa(uint32_t code);
+enum yasl_key yasl_private_key_from_cocoa(uint32_t code);
#endif
diff --git a/src/platform/cocoa/window.m b/src/platform/cocoa/window.m
index 60b20b0..7be78fd 100644
--- a/src/platform/cocoa/window.m
+++ b/src/platform/cocoa/window.m
@@ -1,10 +1,12 @@
#import <AppKit/AppKit.h>
#import <Metal/Metal.h>
#import <QuartzCore/QuartzCore.h>
+#import <OpenGL/OpenGL.h>
#include "platform/cocoa/api.h"
#include "common/backend.h"
-#include "yasl/native/cocoa.h"
+#include "common/public_internal.h"
+#include "common/private_native_cocoa.h"
#include "common/timing.h"
#include "common/loop.h"
#include "platform/cocoa/keycodes.h"
@@ -18,8 +20,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <dlfcn.h>
#include <sys/event.h>
#include <unistd.h>
+#include <objc/message.h>
+#include <objc/runtime.h>
#define LINK_IDLE_FRAMES 8
/* Application-defined event subtype that only wakes a waiting dispatch. */
@@ -63,6 +68,7 @@ struct cocoa_surface {
YaslWindowController *controller;
YaslView *view;
CAMetalLayer *layer;
+ NSOpenGLContext *gl_context;
id<MTLCommandQueue> software_queue;
id<MTLBuffer> software_buffer;
NSUInteger software_buffer_capacity;
@@ -70,16 +76,19 @@ struct cocoa_surface {
struct cocoa_surface *parent;
bool is_popup;
- struct yasl_vector2i content_offset;
+ struct yasl_private_vector2i content_offset;
- const struct yasl_window_cbs *cbs;
+ const struct yasl_private_window_cbs *cbs;
void *ud;
uint64_t frames_skipped;
char *preedit;
- struct yasl_rect2i ime_caret;
+ size_t preedit_begin, preedit_end;
+ NSUInteger marked_utf16_location;
+ uint64_t preedit_revision, preedit_generation;
+ struct yasl_private_rect2i ime_caret;
- struct yasl_vector2i size;
+ struct yasl_private_vector2i size;
uint32_t scale_120;
uint32_t bg_color;
uint32_t *pixels;
@@ -87,11 +96,11 @@ struct cocoa_surface {
id<CAMetalDrawable> drawable;
bool transaction;
- struct yasl_frame frame;
- struct yasl_damage_list damage;
+ struct yasl_private_frame frame;
+ struct yasl_private_damage_list damage;
bool did_present;
/* Extent of the frame in pixels, valid while did_present is set. */
- struct yasl_vector2i shown_size;
+ struct yasl_private_vector2i shown_size;
unsigned idle_frames;
bool want_draw;
bool frame_due;
@@ -102,27 +111,29 @@ struct cocoa_surface {
struct cocoa_surface *dead_next;
struct input_queue input;
- struct yasl_vector2i pointer;
+ struct yasl_private_vector2i pointer;
double scroll_x;
uint32_t mods;
uint32_t buttons;
- enum yasl_cursor cursor;
- enum yasl_pointer_device tablet_device;
+ enum yasl_private_cursor cursor;
+ enum yasl_private_pointer_device tablet_device;
bool tablet_down;
+ uint64_t pen_device_id, pen_tool_id;
+ uint32_t pen_buttons;
char title[160];
- struct yasl_frame_clock clock;
+ struct yasl_private_frame_clock clock;
};
struct cocoa_display {
- struct yasl_display base;
+ struct yasl_private_display base;
struct cocoa_surface **windows;
int count;
int capacity;
- const struct yasl_display_cbs *cbs;
+ const struct yasl_private_display_cbs *cbs;
void *ud;
char *app_id;
- struct yasl_loop *loop;
+ struct yasl_private_loop *loop;
id key_up_monitor;
NSCursor *hidden_cursor;
YaslAppDelegate *delegate;
@@ -132,15 +143,15 @@ struct cocoa_display {
bool active;
struct cocoa_surface *origin;
struct cocoa_surface *carried;
- struct yasl_vector2i carry_offset;
+ struct yasl_private_vector2i carry_offset;
struct cocoa_surface *hover;
- struct yasl_vector2i hover_pos;
- struct yasl_drag_listener listener;
+ struct yasl_private_vector2i hover_pos;
+ struct yasl_private_drag_listener listener;
void *ud;
NSImage *image;
NSWindow *image_window;
- struct yasl_vector2i image_size;
- struct yasl_vector2i image_hotspot;
+ struct yasl_private_vector2i image_size;
+ struct yasl_private_vector2i image_hotspot;
} drag;
int depth;
bool open;
@@ -151,16 +162,16 @@ struct cocoa_display {
static struct cocoa_display darwin_ctx;
-static void drag_end(struct cocoa_display *d, enum yasl_drag_end how)
+static void drag_end(struct cocoa_display *d, enum yasl_private_drag_end how)
{
if (!d || !d->drag.active) return;
- struct yasl_drag_listener l = d->drag.listener;
+ struct yasl_private_drag_listener l = d->drag.listener;
void *ud = d->drag.ud;
struct cocoa_surface *hover = d->drag.hover;
- struct yasl_vector2i pos = d->drag.hover_pos;
+ struct yasl_private_vector2i pos = d->drag.hover_pos;
d->drag.active = false;
- d->drag.listener = (struct yasl_drag_listener){0};
+ d->drag.listener = (struct yasl_private_drag_listener){0};
d->drag.hover = NULL;
d->drag.origin = NULL;
d->drag.carried = NULL;
@@ -175,10 +186,10 @@ static void drag_end(struct cocoa_display *d, enum yasl_drag_end how)
if (hover && !hover->destroyed) {
darwin_ctx.depth++;
- if (how == YASL_DRAG_DROPPED)
- yasl_surface_notify_drag(&hover->base, YASL_DRAG_DROP, pos);
+ if (how == YASL_PRIVATE_DRAG_DROPPED)
+ yasl_private_surface_notify_drag(&hover->base, YASL_PRIVATE_DRAG_DROP, pos);
else
- yasl_surface_notify_drag(&hover->base, YASL_DRAG_LEAVE, pos);
+ yasl_private_surface_notify_drag(&hover->base, YASL_PRIVATE_DRAG_LEAVE, pos);
darwin_ctx.depth--;
}
if (l.end)
@@ -187,6 +198,7 @@ static void drag_end(struct cocoa_display *d, enum yasl_drag_end how)
static void render_and_commit(struct cocoa_surface *w);
static void reap_dead(struct cocoa_display *d);
+static void push_pen(struct cocoa_surface *w, NSEvent *event, unsigned action, bool contact);
static void set_state(struct cocoa_surface *w, uint32_t bit, bool on)
{
@@ -197,21 +209,25 @@ static void set_state(struct cocoa_surface *w, uint32_t bit, bool on)
return;
w->base.state = next;
darwin_ctx.depth++;
- yasl_surface_notify_state(&w->base, next);
+ yasl_private_surface_notify_state(&w->base, next);
darwin_ctx.depth--;
}
static void check_pointer_reset(struct cocoa_surface *w)
{
if (!w || w->closed) return;
- if (w->buttons != 0 || w->tablet_down) {
- struct yasl_event ev = {
- .type = YASL_EVENT_INPUT_RESET,
+ if (w->buttons != 0 || w->tablet_down || w->tablet_device != YASL_PRIVATE_PTR_DEV_MOUSE) {
+ if (w->tablet_device != YASL_PRIVATE_PTR_DEV_MOUSE)
+ push_pen(w, nil, YASL_PRIVATE_PEN_CANCEL, false);
+ struct yasl_private_event ev = {
+ .type = YASL_PRIVATE_EVENT_INPUT_RESET,
.surface = &w->base,
};
input_queue_push(&w->input, &ev);
w->buttons = 0;
w->tablet_down = false;
+ w->tablet_device = YASL_PRIVATE_PTR_DEV_MOUSE;
+ w->pen_device_id = w->pen_tool_id = 0;
}
}
@@ -221,14 +237,14 @@ static void push_focus(struct cocoa_surface *w, bool focused)
return;
if (!focused)
check_pointer_reset(w);
- struct yasl_event ev = {
- .type = YASL_EVENT_FOCUS,
+ struct yasl_private_event ev = {
+ .type = YASL_PRIVATE_EVENT_FOCUS,
.surface = &w->base,
.focus = { .focused = focused },
};
input_queue_push(&w->input, &ev);
darwin_ctx.depth++;
- set_state(w, YASL_STATE_ACTIVATED, focused);
+ set_state(w, YASL_PRIVATE_STATE_ACTIVATED, focused);
if (!w->destroyed && !w->closed)
cocoa_surface_schedule_redraw(w);
darwin_ctx.depth--;
@@ -236,18 +252,18 @@ static void push_focus(struct cocoa_surface *w, bool focused)
static void sync_size(struct cocoa_surface *w, bool force)
{
- if (!w || w->destroyed || !w->layer || w->closed)
+ if (!w || w->destroyed || w->closed || !w->view || !w->window)
return;
- uint64_t start = yasl_loop_now_ns();
+ uint64_t start = yasl_private_loop_now_ns();
NSRect bounds = w->view.bounds;
CGFloat scale = w->window.backingScaleFactor;
if (scale < 1.0)
scale = 1.0;
- struct yasl_vector2i size = {
+ struct yasl_private_vector2i size = {
.x = (int)lround(bounds.size.width * scale),
.y = (int)lround(bounds.size.height * scale),
};
- uint32_t scale_120 = (uint32_t)lround(scale * YASL_SCALE_UNIT);
+ uint32_t scale_120 = (uint32_t)lround(scale * YASL_PRIVATE_SCALE_UNIT);
bool rescaled = force || scale_120 != w->scale_120;
bool resized = force || (size.x != w->size.x || size.y != w->size.y);
if (!rescaled && !resized)
@@ -255,15 +271,21 @@ static void sync_size(struct cocoa_surface *w, bool force)
darwin_ctx.depth++;
w->size = size;
w->scale_120 = scale_120;
- w->layer.contentsScale = scale;
- w->layer.drawableSize = CGSizeMake(size.x, size.y);
+ if (w->layer) {
+ w->layer.contentsScale = scale;
+ w->layer.drawableSize = CGSizeMake(size.x, size.y);
+ }
+ if (w->gl_context)
+ [w->gl_context update];
/* Common configure notification already requests a frame before invoking
* the legacy callback. Finish local bookkeeping before that callback can
* destroy this surface. */
- yasl_clock_note_resize(&w->clock, yasl_loop_now_ns() - start);
- yasl_surface_notify_configure(&w->base, (struct yasl_geometry){
+ yasl_private_clock_note_resize(&w->clock, yasl_private_loop_now_ns() - start);
+ yasl_private_surface_notify_configure(&w->base, (struct yasl_private_geometry){
.size = size, .scale_120 = scale_120,
});
+ if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU)
+ yasl_private_surface_notify_refresh(&w->base);
if (!w->destroyed && !w->closed && w->view.inLiveResize)
render_and_commit(w);
darwin_ctx.depth--;
@@ -272,11 +294,11 @@ static void sync_size(struct cocoa_surface *w, bool force)
static unsigned cocoa_mods(NSEventModifierFlags m)
{
unsigned mods = 0;
- if (m & NSEventModifierFlagShift) mods |= YASL_MOD_SHIFT;
- if (m & NSEventModifierFlagOption) mods |= YASL_MOD_ALT;
- if (m & NSEventModifierFlagControl) mods |= YASL_MOD_CTRL;
- if (m & NSEventModifierFlagCommand) mods |= YASL_MOD_SUPER;
- if (m & NSEventModifierFlagCapsLock) mods |= YASL_MOD_CAPS;
+ if (m & NSEventModifierFlagShift) mods |= YASL_PRIVATE_MOD_SHIFT;
+ if (m & NSEventModifierFlagOption) mods |= YASL_PRIVATE_MOD_ALT;
+ if (m & NSEventModifierFlagControl) mods |= YASL_PRIVATE_MOD_CTRL;
+ if (m & NSEventModifierFlagCommand) mods |= YASL_PRIVATE_MOD_SUPER;
+ if (m & NSEventModifierFlagCapsLock) mods |= YASL_PRIVATE_MOD_CAPS;
return mods;
}
@@ -287,19 +309,19 @@ static uint32_t cocoa_key_label(NSString *characters)
if (!characters.length) return 0;
unichar first = [characters characterAtIndex:0];
if (characters.length == 1)
- return yasl_key_label(first);
+ return yasl_private_key_label(first);
if (characters.length == 2 && first >= 0xd800 && first <= 0xdbff) {
unichar second = [characters characterAtIndex:1];
if (second >= 0xdc00 && second <= 0xdfff)
- return yasl_key_label(0x10000 + ((first - 0xd800) << 10) + second - 0xdc00);
+ return yasl_private_key_label(0x10000 + ((first - 0xd800) << 10) + second - 0xdc00);
}
return 0;
}
-static struct yasl_key_event cocoa_key_event(NSEvent *ev, enum yasl_key_action action)
+static struct yasl_private_key_event cocoa_key_event(NSEvent *ev, enum yasl_key_action action)
{
- struct yasl_key_event k = {
- .key = yasl_key_from_cocoa(ev.keyCode),
+ struct yasl_private_key_event k = {
+ .key = yasl_private_key_from_cocoa(ev.keyCode),
.action = action,
.mods = cocoa_mods(ev.modifierFlags),
.native_code = ev.keyCode,
@@ -315,27 +337,15 @@ static struct yasl_key_event cocoa_key_event(NSEvent *ev, enum yasl_key_action a
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_event input_ev = {
- .type = YASL_EVENT_KEY,
+ struct yasl_private_key_event k = cocoa_key_event(ev, action);
+ struct yasl_private_event input_ev = {
+ .type = YASL_PRIVATE_EVENT_KEY,
.surface = &w->base,
.key = k,
};
input_queue_push(&w->input, &input_ev);
}
-static void on_text_input(struct cocoa_surface *w, const char *text, size_t len)
-{
- if (len) {
- struct yasl_event ev = {
- .type = YASL_EVENT_TEXT,
- .surface = &w->base,
- .text = { .data = text, .len = len },
- };
- input_queue_push(&w->input, &ev);
- }
-}
-
static bool modifier_down(NSEvent *ev)
{
NSEventModifierFlags f = ev.modifierFlags;
@@ -356,22 +366,22 @@ static bool modifier_down(NSEvent *ev)
static int cocoa_button(NSInteger number)
{
switch (number) {
- case 0: return YASL_BUTTON_PRIMARY;
- case 1: return YASL_BUTTON_SECONDARY;
- case 2: return YASL_BUTTON_MIDDLE;
- case 3: return YASL_BUTTON_BACK;
- case 4: return YASL_BUTTON_FORWARD;
+ case 0: return YASL_PRIVATE_BUTTON_PRIMARY;
+ case 1: return YASL_PRIVATE_BUTTON_SECONDARY;
+ case 2: return YASL_PRIVATE_BUTTON_MIDDLE;
+ case 3: return YASL_PRIVATE_BUTTON_BACK;
+ case 4: return YASL_PRIVATE_BUTTON_FORWARD;
default: return -1;
}
}
-static struct yasl_vector2i event_pos(struct cocoa_surface *w, NSEvent *ev)
+static struct yasl_private_vector2i event_pos(struct cocoa_surface *w, NSEvent *ev)
{
NSPoint p = [w->view convertPoint:ev.locationInWindow fromView:nil];
CGFloat scale = w->window.backingScaleFactor;
if (scale < 1.0)
scale = 1.0;
- return (struct yasl_vector2i){
+ return (struct yasl_private_vector2i){
.x = (int)floor(p.x * scale),
.y = (int)floor((w->view.bounds.size.height - p.y) * scale),
};
@@ -389,7 +399,7 @@ static void update_drag_hover(struct cocoa_display *d, NSPoint screenPoint)
NSInteger winNum = [NSWindow windowNumberAtPoint:screenPoint belowWindowWithWindowNumber:excludeNum];
struct cocoa_surface *target = NULL;
- struct yasl_vector2i pos = {0};
+ struct yasl_private_vector2i pos = {0};
if (winNum > 0) {
NSWindow *nswin = [NSApp windowWithWindowNumber:winNum];
@@ -429,11 +439,11 @@ static void update_drag_hover(struct cocoa_display *d, NSPoint screenPoint)
if (target != d->drag.hover) {
if (d->drag.hover) {
struct cocoa_surface *old = d->drag.hover;
- struct yasl_vector2i old_pos = d->drag.hover_pos;
+ struct yasl_private_vector2i old_pos = d->drag.hover_pos;
d->drag.hover = NULL;
if (!old->destroyed) {
darwin_ctx.depth++;
- yasl_surface_notify_drag(&old->base, YASL_DRAG_LEAVE, old_pos);
+ yasl_private_surface_notify_drag(&old->base, YASL_PRIVATE_DRAG_LEAVE, old_pos);
darwin_ctx.depth--;
}
if (!d->drag.active) return;
@@ -443,23 +453,117 @@ static void update_drag_hover(struct cocoa_display *d, NSPoint screenPoint)
d->drag.hover_pos = pos;
if (target && !target->destroyed) {
darwin_ctx.depth++;
- yasl_surface_notify_drag(&target->base, YASL_DRAG_OVER, pos);
+ yasl_private_surface_notify_drag(&target->base, YASL_PRIVATE_DRAG_OVER, pos);
darwin_ctx.depth--;
}
} else if (target && !target->destroyed) {
d->drag.hover_pos = pos;
darwin_ctx.depth++;
- yasl_surface_notify_drag(&target->base, YASL_DRAG_OVER, pos);
+ yasl_private_surface_notify_drag(&target->base, YASL_PRIVATE_DRAG_OVER, pos);
darwin_ctx.depth--;
}
}
-static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_event_type type,
+static uint64_t event_integer(NSEvent *event, const char *name)
+{
+ SEL selector = sel_registerName(name);
+ if (![event respondsToSelector:selector]) return 0;
+ return (uint64_t)((NSInteger (*)(id, SEL))objc_msgSend)(event, selector);
+}
+
+static double event_double(NSEvent *event, const char *name)
+{
+ SEL selector = sel_registerName(name);
+ if (![event respondsToSelector:selector]) return 0;
+ return ((double (*)(id, SEL))objc_msgSend)(event, selector);
+}
+
+static void push_pen(struct cocoa_surface *w, NSEvent *event, unsigned action, bool contact)
+{
+ if (!w || w->closed || w->tablet_device == YASL_PRIVATE_PTR_DEV_MOUSE) return;
+ struct yasl_private_pen_sample pen = {
+ .device_id = w->pen_device_id,
+ .tool_id = w->pen_tool_id,
+ .x = w->pointer.x,
+ .y = w->pointer.y,
+ .tool = w->tablet_device == YASL_PRIVATE_PTR_DEV_ERASER ? 2u : 1u,
+ .action = action,
+ .in_contact = contact,
+ };
+ if (event) {
+ if (w->view && [event respondsToSelector:@selector(locationInWindow)]) {
+ NSPoint point = [w->view convertPoint:event.locationInWindow fromView:nil];
+ CGFloat scale = w->window.backingScaleFactor;
+ if (scale < 1.0) scale = 1.0;
+ pen.x = point.x * scale;
+ pen.y = (w->view.bounds.size.height - point.y) * scale;
+ }
+ if (event.buttonMask & NSEventButtonMaskPenTip)
+ pen.buttons |= YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY);
+ if (event.buttonMask & NSEventButtonMaskPenLowerSide)
+ pen.buttons |= YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_SECONDARY);
+ if (event.buttonMask & NSEventButtonMaskPenUpperSide)
+ pen.buttons |= YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_MIDDLE);
+ if (event.type == NSEventTypeTabletPoint || event.subtype == NSEventSubtypeTabletPoint) {
+ pen.pressure = fmax(0.0, fmin(1.0, event.pressure));
+ pen.axes_valid |= YASL_PRIVATE_PEN_PRESSURE;
+ pen.axes_changed |= YASL_PRIVATE_PEN_PRESSURE;
+ NSPoint tilt = event.tilt;
+ pen.tilt_x = fmax(-90.0, fmin(90.0, tilt.x * 90.0));
+ pen.tilt_y = fmax(-90.0, fmin(90.0, -tilt.y * 90.0));
+ pen.axes_valid |= YASL_PRIVATE_PEN_TILT;
+ pen.axes_changed |= YASL_PRIVATE_PEN_TILT;
+ if ([event respondsToSelector:@selector(rotation)]) {
+ pen.rotation = event_double(event, "rotation");
+ pen.axes_valid |= YASL_PRIVATE_PEN_ROTATION;
+ pen.axes_changed |= YASL_PRIVATE_PEN_ROTATION;
+ }
+ if ([event respondsToSelector:@selector(tangentialPressure)]) {
+ pen.tangential = event_double(event, "tangentialPressure");
+ pen.axes_valid |= YASL_PRIVATE_PEN_TANGENTIAL;
+ pen.axes_changed |= YASL_PRIVATE_PEN_TANGENTIAL;
+ }
+ }
+ uint64_t device = event_integer(event, "pointingDeviceID");
+ uint64_t tool = event_integer(event, "pointingDeviceSerialNumber");
+ if (device) pen.device_id = device;
+ if (tool) pen.tool_id = tool;
+ }
+ if (action == YASL_PRIVATE_PEN_DOWN) {
+ pen.buttons |= YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY);
+ pen.changed_button = YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY);
+ } else if (action == YASL_PRIVATE_PEN_UP) {
+ pen.buttons &= ~YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY);
+ pen.changed_button = YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY);
+ }
+ if (action == YASL_PRIVATE_PEN_LEAVE || action == YASL_PRIVATE_PEN_CANCEL) {
+ pen.buttons = 0;
+ pen.in_contact = false;
+ pen.pressure = 0;
+ w->pen_buttons = 0;
+ } else {
+ uint32_t changed = pen.buttons ^ w->pen_buttons;
+ if (action == YASL_PRIVATE_PEN_MOVE && changed) {
+ action = YASL_PRIVATE_PEN_BUTTON;
+ pen.changed_button = changed & (~changed + 1u);
+ }
+ w->pen_buttons = pen.buttons;
+ }
+ pen.action = action;
+ struct yasl_private_event ev = {
+ .type = YASL_PRIVATE_EVENT_PEN,
+ .surface = &w->base,
+ .pen = pen,
+ };
+ input_queue_push(&w->input, &ev);
+}
+
+static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_private_event_type type,
int button)
{
if (!w || w->closed)
return;
- if ((type == YASL_EVENT_POINTER_DOWN || type == YASL_EVENT_POINTER_UP) &&
+ if ((type == YASL_PRIVATE_EVENT_POINTER_DOWN || type == YASL_PRIVATE_EVENT_POINTER_UP) &&
(button < 0 || button > 4))
return;
bool proximity = ev.type == NSEventTypeTabletProximity ||
@@ -467,26 +571,30 @@ static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_event_t
if (proximity) {
if (ev.isEnteringProximity) {
w->tablet_device = ev.pointingDeviceType == NSPointingDeviceTypeEraser ?
- YASL_PTR_DEV_ERASER : ev.pointingDeviceType == NSPointingDeviceTypePen ?
- YASL_PTR_DEV_PEN : YASL_PTR_DEV_MOUSE;
+ YASL_PRIVATE_PTR_DEV_ERASER : ev.pointingDeviceType == NSPointingDeviceTypePen ?
+ YASL_PRIVATE_PTR_DEV_PEN : YASL_PRIVATE_PTR_DEV_MOUSE;
+ w->pen_device_id = event_integer(ev, "pointingDeviceID");
+ w->pen_tool_id = event_integer(ev, "pointingDeviceSerialNumber");
+ if (w->tablet_device != YASL_PRIVATE_PTR_DEV_MOUSE)
+ push_pen(w, ev, YASL_PRIVATE_PEN_ENTER, false);
return;
}
- type = YASL_EVENT_POINTER_LEAVE;
+ type = YASL_PRIVATE_EVENT_POINTER_LEAVE;
}
w->mods = cocoa_mods(ev.modifierFlags);
- if (type != YASL_EVENT_POINTER_LEAVE)
+ if (type != YASL_PRIVATE_EVENT_POINTER_LEAVE)
w->pointer = event_pos(w, ev);
- enum yasl_pointer_device device = YASL_PTR_DEV_MOUSE;
- struct yasl_pointer_pen pen = {0};
+ enum yasl_private_pointer_device device = YASL_PRIVATE_PTR_DEV_MOUSE;
+ struct yasl_private_pointer_pen pen = {0};
- if (type == YASL_EVENT_POINTER_LEAVE && w->tablet_device != YASL_PTR_DEV_MOUSE) {
+ if (type == YASL_PRIVATE_EVENT_POINTER_LEAVE && w->tablet_device != YASL_PRIVATE_PTR_DEV_MOUSE) {
device = w->tablet_device;
w->tablet_down = false;
} else if (ev.type == NSEventTypeTabletPoint || ev.subtype == NSEventSubtypeTabletPoint) {
/* pointingDeviceType belongs to proximity events. Remember that
* event's tool so erasers and zero-pressure hover stay classified. */
- device = w->tablet_device == YASL_PTR_DEV_ERASER ? YASL_PTR_DEV_ERASER : YASL_PTR_DEV_PEN;
+ device = w->tablet_device == YASL_PRIVATE_PTR_DEV_ERASER ? YASL_PRIVATE_PTR_DEV_ERASER : YASL_PRIVATE_PTR_DEV_PEN;
float press = (float)ev.pressure;
if (press > 1.0f) press = 1.0f;
if (press > 0.0f)
@@ -495,32 +603,40 @@ static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_event_t
NSPoint tilt = ev.tilt;
pen.tilt_x = (int8_t)lround(fmax(-1, fmin(1, tilt.x)) * 90.0);
pen.tilt_y = (int8_t)lround(fmax(-1, fmin(1, -tilt.y)) * 90.0);
- if (button == 0 && type == YASL_EVENT_POINTER_DOWN) w->tablet_down = true;
- if (button == 0 && type == YASL_EVENT_POINTER_UP) {
+ if (button == 0 && type == YASL_PRIVATE_EVENT_POINTER_DOWN) w->tablet_down = true;
+ if (button == 0 && type == YASL_PRIVATE_EVENT_POINTER_UP) {
w->tablet_down = false;
pen.pressure = 0;
}
}
- if (type == YASL_EVENT_POINTER_DOWN && button >= 0 && button < 32)
+ if (type == YASL_PRIVATE_EVENT_POINTER_DOWN && button >= 0 && button < 32)
w->buttons |= (1u << button);
- else if (type == YASL_EVENT_POINTER_UP && button >= 0 && button < 32)
+ else if (type == YASL_PRIVATE_EVENT_POINTER_UP && button >= 0 && button < 32)
w->buttons &= ~(1u << button);
- struct yasl_event input_ev = {
+ struct yasl_private_event input_ev = {
.type = type,
.surface = &w->base,
.pointer = {
.pos = w->pointer,
- .button = (type == YASL_EVENT_POINTER_DOWN || type == YASL_EVENT_POINTER_UP) ?
- (enum yasl_pointer_button)button : YASL_BUTTON_PRIMARY,
+ .button = (type == YASL_PRIVATE_EVENT_POINTER_DOWN || type == YASL_PRIVATE_EVENT_POINTER_UP) ?
+ (enum yasl_private_pointer_button)button : YASL_PRIVATE_BUTTON_PRIMARY,
.device = device,
.mods = w->mods,
.pen = pen,
},
};
input_queue_push(&w->input, &input_ev);
- if (type == YASL_EVENT_POINTER_MOTION && darwin_ctx.drag.active) {
+ if (type == YASL_PRIVATE_EVENT_POINTER_LEAVE && device != YASL_PRIVATE_PTR_DEV_MOUSE) {
+ push_pen(w, ev, YASL_PRIVATE_PEN_LEAVE, false);
+ } else if (ev.type == NSEventTypeTabletPoint || ev.subtype == NSEventSubtypeTabletPoint) {
+ unsigned action = button == YASL_PRIVATE_BUTTON_PRIMARY && type == YASL_PRIVATE_EVENT_POINTER_DOWN ? YASL_PRIVATE_PEN_DOWN :
+ button == YASL_PRIVATE_BUTTON_PRIMARY && type == YASL_PRIVATE_EVENT_POINTER_UP ? YASL_PRIVATE_PEN_UP :
+ YASL_PRIVATE_PEN_MOVE;
+ push_pen(w, ev, action, w->tablet_down);
+ }
+ if (type == YASL_PRIVATE_EVENT_POINTER_MOTION && darwin_ctx.drag.active) {
if (darwin_ctx.drag.carried) {
cocoa_surface_place_under_pointer(darwin_ctx.drag.carried, darwin_ctx.drag.carry_offset);
} else if (darwin_ctx.drag.image_window) {
@@ -534,23 +650,24 @@ static void push_pointer(struct cocoa_surface *w, NSEvent *ev, enum yasl_event_t
mouse.y - pt_h + darwin_ctx.drag.image_hotspot.y / scale)];
}
update_drag_hover(&darwin_ctx, [NSEvent mouseLocation]);
- } else if (type == YASL_EVENT_POINTER_UP && darwin_ctx.drag.active) {
+ } else if (type == YASL_PRIVATE_EVENT_POINTER_UP && darwin_ctx.drag.active) {
update_drag_hover(&darwin_ctx, [NSEvent mouseLocation]);
- drag_end(&darwin_ctx, YASL_DRAG_DROPPED);
+ drag_end(&darwin_ctx, YASL_PRIVATE_DRAG_DROPPED);
}
if (proximity) {
- w->tablet_device = YASL_PTR_DEV_MOUSE;
+ w->tablet_device = YASL_PRIVATE_PTR_DEV_MOUSE;
w->tablet_down = false;
+ w->pen_device_id = w->pen_tool_id = 0;
}
}
-static NSCursor *cocoa_cursor(enum yasl_cursor cursor)
+static NSCursor *cocoa_cursor(enum yasl_private_cursor cursor)
{
switch (cursor) {
- case YASL_CURSOR_TEXT: return [NSCursor IBeamCursor];
- case YASL_CURSOR_LINK: return [NSCursor pointingHandCursor];
- case YASL_CURSOR_CROSSHAIR: return [NSCursor crosshairCursor];
- case YASL_CURSOR_HIDDEN:
+ case YASL_PRIVATE_CURSOR_TEXT: return [NSCursor IBeamCursor];
+ case YASL_PRIVATE_CURSOR_LINK: return [NSCursor pointingHandCursor];
+ case YASL_PRIVATE_CURSOR_CROSSHAIR: return [NSCursor crosshairCursor];
+ case YASL_PRIVATE_CURSOR_HIDDEN:
if (!darwin_ctx.hidden_cursor) {
NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(1, 1)];
[image lockFocus];
@@ -561,7 +678,7 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor)
[image release];
}
return darwin_ctx.hidden_cursor;
- case YASL_CURSOR_ARROW:
+ case YASL_PRIVATE_CURSOR_ARROW:
default: return [NSCursor arrowCursor];
}
}
@@ -570,14 +687,14 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor)
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
{
(void)sender;
- yasl_display_notify_event(&darwin_ctx.base, YASL_EVENT_DISPLAY_QUIT);
+ yasl_private_display_notify_event(&darwin_ctx.base, YASL_PRIVATE_EVENT_DISPLAY_QUIT);
return NSTerminateCancel;
}
- (BOOL)applicationShouldHandleReopen:(NSApplication *)sender hasVisibleWindows:(BOOL)visible
{
(void)sender;
if (!visible)
- yasl_display_notify_event(&darwin_ctx.base, YASL_EVENT_DISPLAY_REOPEN);
+ yasl_private_display_notify_event(&darwin_ctx.base, YASL_PRIVATE_EVENT_DISPLAY_REOPEN);
return YES;
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
@@ -587,13 +704,13 @@ static NSCursor *cocoa_cursor(enum yasl_cursor cursor)
}
@end
-static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> sender)
+static struct yasl_private_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> sender)
{
NSPoint p = [view convertPoint:[sender draggingLocation] fromView:nil];
NSWindow *win = view.window;
CGFloat scale = win ? win.backingScaleFactor : 1.0;
if (scale < 1.0) scale = 1.0;
- return (struct yasl_vector2i){
+ return (struct yasl_private_vector2i){
.x = (int)floor(p.x * scale),
.y = (int)floor((view.bounds.size.height - p.y) * scale),
};
@@ -619,9 +736,9 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send
if ([pb canReadItemWithDataConformingToTypes:@[NSPasteboardTypeFileURL]]) {
struct cocoa_surface *w = self.surface;
if (w && !w->closed) {
- struct yasl_vector2i pos = dragging_pos(self, sender);
+ struct yasl_private_vector2i pos = dragging_pos(self, sender);
darwin_ctx.depth++;
- yasl_surface_notify_drop(&w->base, YASL_DROP_OVER, pos);
+ yasl_private_surface_notify_drop(&w->base, YASL_PRIVATE_DROP_OVER, pos);
darwin_ctx.depth--;
}
return NSDragOperationCopy;
@@ -640,9 +757,9 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send
if ([pb canReadItemWithDataConformingToTypes:@[NSPasteboardTypeFileURL]]) {
struct cocoa_surface *w = self.surface;
if (w && !w->closed) {
- struct yasl_vector2i pos = dragging_pos(self, sender);
+ struct yasl_private_vector2i pos = dragging_pos(self, sender);
darwin_ctx.depth++;
- yasl_surface_notify_drop(&w->base, YASL_DROP_LEAVE, pos);
+ yasl_private_surface_notify_drop(&w->base, YASL_PRIVATE_DROP_LEAVE, pos);
darwin_ctx.depth--;
}
}
@@ -652,7 +769,7 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send
{
struct cocoa_surface *w = self.surface;
if (!w) return NO;
- struct yasl_vector2i pos = dragging_pos(self, sender);
+ struct yasl_private_vector2i pos = dragging_pos(self, sender);
BOOL ok = NO;
NSPasteboard *pb = [sender draggingPasteboard];
@@ -666,8 +783,8 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send
}
const char *utf8 = result.UTF8String;
if (utf8 && strlen(utf8)) {
- struct yasl_event ev = {
- .type = YASL_EVENT_DROP,
+ struct yasl_private_event ev = {
+ .type = YASL_PRIVATE_EVENT_DROP,
.surface = &w->base,
.text = { .data = utf8, .len = strlen(utf8) },
};
@@ -678,7 +795,7 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send
}
if (!w->destroyed) {
darwin_ctx.depth++;
- yasl_surface_notify_drop(&w->base, YASL_DROP_LEAVE, pos);
+ yasl_private_surface_notify_drop(&w->base, YASL_PRIVATE_DROP_LEAVE, pos);
darwin_ctx.depth--;
}
return ok;
@@ -704,6 +821,13 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send
- (BOOL)canBecomeKeyView { return YES; }
- (BOOL)wantsUpdateLayer { return YES; }
+- (void)updateLayer
+{
+ struct cocoa_surface *w = self.surface;
+ if (w && !w->closed && w->base.presenter != YASL_PRIVATE_PRESENTER_CPU)
+ yasl_private_surface_notify_refresh(&w->base);
+}
+
- (void)setFrameSize:(NSSize)size
{
[super setFrameSize:size];
@@ -743,14 +867,14 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send
if (period > 0.0 && period < 0.25)
w->clock.refresh_ns = period * 1e9;
w->frame_due = true;
- if (!w->want_draw || (w->base.state & YASL_STATE_SUSPENDED)) {
+ if (!w->want_draw || (w->base.state & YASL_PRIVATE_STATE_SUSPENDED)) {
if (++w->idle_frames >= LINK_IDLE_FRAMES)
link.paused = YES;
return;
}
w->idle_frames = 0;
if (darwin_ctx.loop)
- yasl_loop_kick(darwin_ctx.loop);
+ yasl_private_loop_kick(darwin_ctx.loop);
}
- (void)updateTrackingAreas
@@ -773,7 +897,7 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send
{
(void)event;
struct cocoa_surface *w = self.surface;
- [cocoa_cursor(w ? w->cursor : YASL_CURSOR_ARROW) set];
+ [cocoa_cursor(w ? w->cursor : YASL_PRIVATE_CURSOR_ARROW) set];
}
- (void)keyDown:(NSEvent *)event
@@ -802,44 +926,44 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send
- (void)mouseDown:(NSEvent *)event
{
[self.window makeFirstResponder:self];
- push_pointer(self.surface, event, YASL_EVENT_POINTER_DOWN, cocoa_button(event.buttonNumber));
+ push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_DOWN, cocoa_button(event.buttonNumber));
}
- (void)rightMouseDown:(NSEvent *)event { [self mouseDown:event]; }
- (void)otherMouseDown:(NSEvent *)event { [self mouseDown:event]; }
- (void)mouseUp:(NSEvent *)event
{
- push_pointer(self.surface, event, YASL_EVENT_POINTER_UP, cocoa_button(event.buttonNumber));
+ push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_UP, cocoa_button(event.buttonNumber));
}
- (void)rightMouseUp:(NSEvent *)event { [self mouseUp:event]; }
- (void)otherMouseUp:(NSEvent *)event { [self mouseUp:event]; }
- (void)mouseMoved:(NSEvent *)event
{
- push_pointer(self.surface, event, YASL_EVENT_POINTER_MOTION, 0);
+ push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_MOTION, 0);
}
- (void)mouseDragged:(NSEvent *)event
{
- push_pointer(self.surface, event, YASL_EVENT_POINTER_MOTION, 0);
+ push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_MOTION, 0);
}
- (void)rightMouseDragged:(NSEvent *)event
{
- push_pointer(self.surface, event, YASL_EVENT_POINTER_MOTION, 0);
+ push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_MOTION, 0);
}
- (void)otherMouseDragged:(NSEvent *)event
{
- push_pointer(self.surface, event, YASL_EVENT_POINTER_MOTION, 0);
+ push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_MOTION, 0);
}
- (void)mouseExited:(NSEvent *)event
{
- push_pointer(self.surface, event, YASL_EVENT_POINTER_LEAVE, 0);
+ push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_LEAVE, 0);
}
- (void)tabletProximity:(NSEvent *)event
{
- push_pointer(self.surface, event, YASL_EVENT_POINTER_MOTION, 0);
+ push_pointer(self.surface, event, YASL_PRIVATE_EVENT_POINTER_MOTION, 0);
}
- (void)tabletPoint:(NSEvent *)event
@@ -847,8 +971,8 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send
struct cocoa_surface *w = self.surface;
if (!w) return;
bool down = (event.buttonMask & NSEventButtonMaskPenTip) != 0;
- enum yasl_event_type type = down == w->tablet_down ? YASL_EVENT_POINTER_MOTION :
- down ? YASL_EVENT_POINTER_DOWN : YASL_EVENT_POINTER_UP;
+ enum yasl_private_event_type type = down == w->tablet_down ? YASL_PRIVATE_EVENT_POINTER_MOTION :
+ down ? YASL_PRIVATE_EVENT_POINTER_DOWN : YASL_PRIVATE_EVENT_POINTER_UP;
push_pointer(w, event, type, 0);
}
@@ -864,8 +988,8 @@ static struct yasl_vector2i dragging_pos(YaslView *view, id<NSDraggingInfo> send
if (!isfinite(dx) || !isfinite(dy) || (dx == 0 && dy == 0)) return;
w->mods = cocoa_mods(event.modifierFlags);
w->pointer = event_pos(w, event);
- struct yasl_event input_ev = {
- .type = YASL_EVENT_POINTER_SCROLL,
+ struct yasl_private_event input_ev = {
+ .type = YASL_PRIVATE_EVENT_POINTER_SCROLL,
.surface = &w->base,
.pointer = {
.pos = w->pointer,
@@ -882,34 +1006,131 @@ static NSString *plain_string(id string)
? [(NSAttributedString *)string string] : (NSString *)string;
}
+static bool text_utf16_to_utf8(NSString *text, NSUInteger index, size_t *byte_offset)
+{
+ if (!text || index > text.length) return false;
+ const char *prefix = [[text substringToIndex:index] UTF8String];
+ if (!prefix) return false;
+ *byte_offset = strlen(prefix);
+ return true;
+}
+
+static bool text_utf8_to_utf16(const char *text, size_t byte_offset, NSUInteger *index)
+{
+ size_t len = text ? strlen(text) : 0;
+ if (byte_offset > len) return false;
+ NSString *prefix = [[NSString alloc] initWithBytes:text length:byte_offset
+ encoding:NSUTF8StringEncoding];
+ if (!prefix) return false;
+ *index = prefix.length;
+ [prefix release];
+ return true;
+}
+
+static bool text_edit_range(struct cocoa_surface *w, NSRange range, bool use_marked,
+ size_t *begin, size_t *end)
+{
+ if (range.location == NSNotFound) {
+ if (use_marked && w->preedit) {
+ if (w->preedit_revision != w->base.text_revision ||
+ w->preedit_generation != w->base.text_generation)
+ return false;
+ *begin = w->preedit_begin;
+ *end = w->preedit_end;
+ } else {
+ *begin = w->base.text_begin;
+ *end = w->base.text_end;
+ }
+ return *begin <= *end && *end <= w->base.text_length;
+ }
+ NSString *surrounding = [NSString stringWithUTF8String:w->base.text_surrounding ?: ""];
+ size_t a, b;
+ if (!surrounding || range.location > surrounding.length ||
+ range.length > surrounding.length - range.location ||
+ !text_utf16_to_utf8(surrounding, range.location, &a) ||
+ !text_utf16_to_utf8(surrounding, range.location + range.length, &b))
+ return false;
+ *begin = a;
+ *end = b;
+ return true;
+}
+
+static void push_text_edit(struct cocoa_surface *w, const char *text, size_t len,
+ size_t begin, size_t end)
+{
+ if (!w->base.text_input_active || begin > end || end > w->base.text_length) return;
+ struct yasl_private_event ev = {
+ .type = YASL_PRIVATE_EVENT_TEXT_EDIT,
+ .surface = &w->base,
+ .text_edit = {
+ .data = text ?: "", .len = len,
+ .revision = w->base.text_revision,
+ .generation = w->base.text_generation,
+ .begin = begin, .end = end,
+ },
+ };
+ input_queue_push(&w->input, &ev);
+}
+
- (void)insertText:(id)string replacementRange:(NSRange)replacementRange
{
- (void)replacementRange;
struct cocoa_surface *w = self.surface;
- if (!w || w->destroyed || w->closed) return;
+ if (!w || w->destroyed || w->closed || !w->base.text_input_active) return;
const char *utf8 = plain_string(string).UTF8String;
size_t len = utf8 ? strlen(utf8) : 0;
+ size_t begin, end;
+ if (!text_edit_range(w, replacementRange, true, &begin, &end)) return;
bool had_preedit = w->preedit != NULL;
+ darwin_ctx.depth++;
+ push_text_edit(w, utf8, len, begin, end);
free(w->preedit);
w->preedit = NULL;
- darwin_ctx.depth++;
- on_text_input(w, utf8, len);
+ w->preedit_begin = w->preedit_end = 0;
if (had_preedit)
- yasl_surface_notify_composition(&w->base, NULL, -1, -1);
+ yasl_private_surface_notify_composition(&w->base, NULL, -1, -1);
if (!w->destroyed && !w->closed)
cocoa_surface_schedule_redraw(w);
darwin_ctx.depth--;
}
-- (void)doCommandBySelector:(SEL)selector { (void)selector; }
+- (void)doCommandBySelector:(SEL)selector
+{
+ struct cocoa_surface *w = self.surface;
+ if (!w || w->destroyed || w->closed || !w->base.text_input_active) return;
+ NSString *surrounding = [NSString stringWithUTF8String:w->base.text_surrounding ?: ""];
+ NSUInteger begin, end;
+ if (!sel_isEqual(selector, @selector(deleteBackward)) &&
+ !sel_isEqual(selector, @selector(deleteForward))) return;
+ if (!text_utf8_to_utf16(w->base.text_surrounding ?: "", w->base.text_begin, &begin) ||
+ !text_utf8_to_utf16(w->base.text_surrounding ?: "", w->base.text_end, &end)) return;
+ if (begin == end && sel_isEqual(selector, @selector(deleteBackward)) && begin > 0) {
+ NSRange composed = [surrounding rangeOfComposedCharacterSequenceAtIndex:begin - 1];
+ begin = composed.location;
+ } else if (begin == end && sel_isEqual(selector, @selector(deleteForward)) && end < surrounding.length) {
+ NSRange composed = [surrounding rangeOfComposedCharacterSequenceAtIndex:end];
+ end = NSMaxRange(composed);
+ }
+ size_t byte_begin, byte_end;
+ if (text_utf16_to_utf8(surrounding, begin, &byte_begin) &&
+ text_utf16_to_utf8(surrounding, end, &byte_end) && byte_begin != byte_end)
+ push_text_edit(w, "", 0, byte_begin, byte_end);
+}
- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange
replacementRange:(NSRange)replacementRange
{
- (void)replacementRange;
struct cocoa_surface *w = self.surface;
- if (!w || w->destroyed || w->closed) return;
+ if (!w || w->destroyed || w->closed || !w->base.text_input_active) return;
NSString *str = plain_string(string);
+ size_t range_begin, range_end;
+ if (!text_edit_range(w, replacementRange, true, &range_begin, &range_end)) return;
+ w->preedit_begin = range_begin;
+ w->preedit_end = range_end;
+ w->preedit_revision = w->base.text_revision;
+ w->preedit_generation = w->base.text_generation;
+ if (!text_utf8_to_utf16(w->base.text_surrounding ?: "", range_begin,
+ &w->marked_utf16_location))
+ w->marked_utf16_location = 0;
const char *utf8 = str.length ? str.UTF8String : NULL;
free(w->preedit);
w->preedit = utf8 ? strdup(utf8) : NULL;
@@ -926,7 +1147,7 @@ static NSString *plain_string(id string)
}
}
darwin_ctx.depth++;
- yasl_surface_notify_composition(&w->base, w->preedit, begin, end);
+ yasl_private_surface_notify_composition(&w->base, w->preedit, begin, end);
if (!w->destroyed && !w->closed)
cocoa_surface_schedule_redraw(w);
darwin_ctx.depth--;
@@ -939,19 +1160,28 @@ static NSString *plain_string(id string)
free(w->preedit);
w->preedit = NULL;
darwin_ctx.depth++;
- yasl_surface_notify_composition(&w->base, NULL, -1, -1);
+ yasl_private_surface_notify_composition(&w->base, NULL, -1, -1);
if (!w->destroyed && !w->closed)
cocoa_surface_schedule_redraw(w);
darwin_ctx.depth--;
}
-- (NSRange)selectedRange { return NSMakeRange(NSNotFound, 0); }
+- (NSRange)selectedRange
+{
+ struct cocoa_surface *w = self.surface;
+ if (!w || !w->base.text_input_active) return NSMakeRange(NSNotFound, 0);
+ NSUInteger begin, end;
+ if (!text_utf8_to_utf16(w->base.text_surrounding ?: "", w->base.text_begin, &begin) ||
+ !text_utf8_to_utf16(w->base.text_surrounding ?: "", w->base.text_end, &end))
+ return NSMakeRange(NSNotFound, 0);
+ return NSMakeRange(begin, end - begin);
+}
- (NSRange)markedRange
{
struct cocoa_surface *w = self.surface;
if (!w || !w->preedit)
return NSMakeRange(NSNotFound, 0);
- return NSMakeRange(0, [NSString stringWithUTF8String:w->preedit].length);
+ return NSMakeRange(w->marked_utf16_location, [NSString stringWithUTF8String:w->preedit].length);
}
- (BOOL)hasMarkedText { return self.surface && self.surface->preedit != NULL; }
- (nullable NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range
@@ -1012,7 +1242,7 @@ static NSString *plain_string(id string)
if (!w) return;
bool visible = (self.window.occlusionState & NSWindowOcclusionStateVisible) != 0;
darwin_ctx.depth++;
- set_state(w, YASL_STATE_SUSPENDED, !visible);
+ set_state(w, YASL_PRIVATE_STATE_SUSPENDED, !visible);
if (!w->destroyed && !w->closed) {
if (visible)
cocoa_surface_schedule_redraw(w);
@@ -1042,8 +1272,8 @@ static NSString *plain_string(id string)
return;
darwin_ctx.depth++;
bool fullscreen = (self.window.styleMask & NSWindowStyleMaskFullScreen) != 0;
- set_state(w, YASL_STATE_FULLSCREEN, fullscreen);
- set_state(w, YASL_STATE_MAXIMIZED, !fullscreen && self.window.isZoomed);
+ set_state(w, YASL_PRIVATE_STATE_FULLSCREEN, fullscreen);
+ set_state(w, YASL_PRIVATE_STATE_MAXIMIZED, !fullscreen && self.window.isZoomed);
sync_size(w, false);
darwin_ctx.depth--;
}
@@ -1075,16 +1305,16 @@ static NSString *plain_string(id string)
darwin_ctx.depth++;
w->closed = true;
[w->link invalidate];
- yasl_surface_notify_close(&w->base);
+ yasl_private_surface_notify_close(&w->base);
darwin_ctx.depth--;
}
@end
-bool cocoa_surface_present(struct cocoa_surface *w, const struct yasl_framebuffer *fb,
- const struct yasl_damage_list *damage)
+bool cocoa_surface_present(struct cocoa_surface *w, const struct yasl_private_framebuffer *fb,
+ const struct yasl_private_damage_list *damage)
{
- if (!w || w->closed || !w->rendering || w->did_present || !fb || !damage ||
+ if (!w || w->base.presenter != YASL_PRIVATE_PRESENTER_CPU || w->closed || !w->rendering || w->did_present || !fb || !damage ||
fb->pixels != w->frame.target.pixels || fb->size.x != w->size.x ||
fb->size.y != w->size.y || !w->drawable)
return false;
@@ -1116,7 +1346,7 @@ bool cocoa_surface_present(struct cocoa_surface *w, const struct yasl_framebuffe
[command presentDrawable:drawable];
[command commit];
}
- yasl_clock_presented(&w->clock, yasl_loop_now_ns(), false);
+ yasl_private_clock_presented(&w->clock, yasl_private_loop_now_ns(), false);
w->did_present = true;
w->shown_size = w->size;
return true;
@@ -1124,8 +1354,8 @@ bool cocoa_surface_present(struct cocoa_surface *w, const struct yasl_framebuffe
static void render_and_commit(struct cocoa_surface *w)
{
- if (w->destroyed || w->closed || !w->cbs->draw || w->rendering || w->size.x <= 0 || w->size.y <= 0 ||
- (w->base.state & YASL_STATE_SUSPENDED))
+ if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU || w->destroyed || w->closed || !w->cbs->draw || w->rendering || w->size.x <= 0 || w->size.y <= 0 ||
+ (w->base.state & YASL_PRIVATE_STATE_SUSPENDED))
return;
size_t count = (size_t)w->size.x * (size_t)w->size.y;
@@ -1143,17 +1373,17 @@ static void render_and_commit(struct cocoa_surface *w)
id<CAMetalDrawable> drawable = [w->layer nextDrawable];
w->drawable = drawable;
w->transaction = w->layer.presentsWithTransaction;
- w->damage = (struct yasl_damage_list){0};
- w->frame = (struct yasl_frame){
+ w->damage = (struct yasl_private_damage_list){0};
+ w->frame = (struct yasl_private_frame){
.surface = &w->base,
.target = {
- .generation = yasl_frame_begin(&w->base),
+ .generation = yasl_private_frame_begin(&w->base),
.pixels = w->software_queue ? w->pixels : NULL,
.size = w->size,
},
};
if (drawable)
- yasl_surface_draw(&w->base, &w->frame);
+ yasl_private_surface_draw(&w->base, &w->frame);
if (!w->destroyed && !w->closed && !w->did_present) {
w->want_draw = true;
w->frames_skipped++;
@@ -1191,7 +1421,7 @@ static void loop_ready(CFFileDescriptorRef fd, CFOptionFlags flags, void *info)
/* A kqueue mirroring the loop's poll set, or -1. complete is false when some
* descriptor could not be registered. */
-static int loop_kqueue(struct yasl_loop *loop, bool *complete)
+static int loop_kqueue(struct yasl_private_loop *loop, bool *complete)
{
*complete = true;
int kq = kqueue();
@@ -1200,7 +1430,7 @@ static int loop_kqueue(struct yasl_loop *loop, bool *complete)
return -1;
}
const struct pollfd *fds;
- int n = yasl_loop_poll_set(loop, &fds);
+ int n = yasl_private_loop_poll_set(loop, &fds);
for (int i = 0; i < n; i++) {
struct kevent change;
if (fds[i].events & POLLIN) {
@@ -1238,7 +1468,7 @@ int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms)
int count = 0;
@try {
if (d->loop) {
- int timer_to = yasl_loop_next_timeout_ms(d->loop);
+ int timer_to = yasl_private_loop_next_timeout_ms(d->loop);
if (timer_to >= 0 && (timeout_ms < 0 || timer_to < timeout_ms)) {
timeout_ms = timer_to;
}
@@ -1303,7 +1533,7 @@ int cocoa_display_dispatch_timeout(struct cocoa_display *d, int timeout_ms)
}
if (d->loop) {
- yasl_loop_step(d->loop, 0);
+ yasl_private_loop_step(d->loop, 0);
}
} @finally {
d->depth--;
@@ -1331,6 +1561,10 @@ void cocoa_display_flush(struct cocoa_display *d)
void cocoa_surface_schedule_redraw(struct cocoa_surface *w)
{
if (!w || w->closed) return;
+ if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU) {
+ yasl_private_surface_notify_refresh(&w->base);
+ return;
+ }
w->want_draw = true;
w->idle_frames = 0;
if (w->link.paused) {
@@ -1338,7 +1572,7 @@ void cocoa_surface_schedule_redraw(struct cocoa_surface *w)
w->link.paused = NO;
}
if (darwin_ctx.loop)
- yasl_loop_kick(darwin_ctx.loop);
+ yasl_private_loop_kick(darwin_ctx.loop);
}
static void app_init(const char *app_name)
@@ -1390,17 +1624,17 @@ static void place_window(NSWindow *window)
darwin_ctx.cascade = [window cascadeTopLeftFromPoint:darwin_ctx.cascade];
}
-struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec)
+struct cocoa_display *cocoa_display_open(struct yasl_private_loop *loop, const struct yasl_private_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.base.ops = &yasl_private_cocoa_backend;
darwin_ctx.base.native_handle = (__bridge void *)[NSApplication sharedApplication];
darwin_ctx.open = true;
if (!loop) {
- darwin_ctx.loop = yasl_loop_create();
+ darwin_ctx.loop = yasl_private_loop_create();
darwin_ctx.owns_loop = true;
} else {
darwin_ctx.loop = loop;
@@ -1408,7 +1642,7 @@ struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct ya
}
darwin_ctx.base.loop = darwin_ctx.loop;
if (spec) {
- darwin_ctx.base.callbacks = spec->cbs ? *spec->cbs : (struct yasl_display_cbs){0};
+ darwin_ctx.base.callbacks = spec->cbs ? *spec->cbs : (struct yasl_private_display_cbs){0};
darwin_ctx.cbs = &darwin_ctx.base.callbacks;
darwin_ctx.ud = spec->ud;
darwin_ctx.base.callback_ud = spec->ud;
@@ -1421,7 +1655,7 @@ struct cocoa_display *cocoa_display_open(struct yasl_loop *loop, const struct ya
void cocoa_display_close(struct cocoa_display *d)
{
if (!d) return;
- drag_end(d, YASL_DRAG_CANCELLED);
+ drag_end(d, YASL_PRIVATE_DRAG_CANCELLED);
assert(!d->count && "destroy every surface before its display");
reap_dead(d);
if (d->key_up_monitor) {
@@ -1429,7 +1663,7 @@ void cocoa_display_close(struct cocoa_display *d)
d->key_up_monitor = nil;
}
if (d->owns_loop && d->loop) {
- yasl_loop_destroy(d->loop);
+ yasl_private_loop_destroy(d->loop);
d->loop = NULL;
d->base.loop = NULL;
}
@@ -1444,14 +1678,15 @@ void cocoa_display_close(struct cocoa_display *d)
uint32_t cocoa_display_caps(const struct cocoa_display *d)
{
- return d ? (YASL_CAP_POPUPS | YASL_CAP_DRAG | YASL_CAP_DRAG_CARRY |
- YASL_CAP_PLACE_WINDOW | YASL_CAP_PRIMARY_SELECTION |
- YASL_CAP_CSD | YASL_CAP_NATIVE_GRAPHICS |
- YASL_CAP_FRAME_PACING | YASL_CAP_TABLET) : 0;
+ return d ? (YASL_PRIVATE_CAP_POPUPS | YASL_PRIVATE_CAP_DRAG | YASL_PRIVATE_CAP_DRAG_CARRY |
+ YASL_PRIVATE_CAP_PLACE_WINDOW | YASL_PRIVATE_CAP_PRIMARY_SELECTION |
+ YASL_PRIVATE_CAP_CSD | YASL_PRIVATE_CAP_NATIVE_GRAPHICS |
+ YASL_PRIVATE_CAP_FRAME_PACING | YASL_PRIVATE_CAP_TABLET |
+ YASL_PRIVATE_CAP_TEXT_INPUT) : 0;
}
struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d,
- const struct yasl_window_spec *spec, void *graphics_device)
+ const struct yasl_private_window_spec *spec, void *graphics_device)
{
assert([NSThread isMainThread]);
if (!d || !d->open || !spec)
@@ -1468,15 +1703,24 @@ struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d,
struct cocoa_surface *w = calloc(1, sizeof(*w));
if (!w) return NULL;
w->base.display = &d->base;
+ w->base.creation_flags = spec->flags;
+ bool external_gpu = spec->external_gpu || graphics_device != NULL;
+ /* Leave an external-GPU surface unclaimed until the public bridge
+ * selects Metal or Vulkan. Both bridges request the layer while the
+ * presenter is still NONE. */
+ w->base.presenter = external_gpu ? YASL_PRIVATE_PRESENTER_NONE : YASL_PRIVATE_PRESENTER_CPU;
cocoa_surface_set_callbacks(w, NULL, NULL);
- w->clock.refresh_ns = YASL_REFRESH_DEFAULT_NS;
+ w->clock.refresh_ns = YASL_PRIVATE_REFRESH_DEFAULT_NS;
const char *t = spec->title ? spec->title : "YASL";
strncpy(w->title, t, sizeof(w->title) - 1);
int win_w = spec->size.x > 0 ? spec->size.x : 800;
int win_h = spec->size.y > 0 ? spec->size.y : 480;
- NSWindowStyleMask style = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
- NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable;
+ NSWindowStyleMask style = (spec->flags & YASL_PRIVATE_SURFACE_UNDECORATED) ?
+ NSWindowStyleMaskBorderless : NSWindowStyleMaskTitled | NSWindowStyleMaskClosable |
+ NSWindowStyleMaskMiniaturizable;
+ if (!(spec->flags & (YASL_PRIVATE_SURFACE_UNDECORATED | YASL_PRIVATE_SURFACE_FIXED_SIZE)))
+ style |= NSWindowStyleMaskResizable;
w->window = [[YaslWindow alloc] initWithContentRect:NSMakeRect(0, 0, win_w, win_h)
styleMask:style
backing:NSBackingStoreBuffered
@@ -1499,7 +1743,7 @@ struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d,
w->layer = (CAMetalLayer *)w->view.layer;
if (graphics_device) {
w->layer.device = (id<MTLDevice>)graphics_device;
- } else {
+ } else if (!external_gpu) {
id<MTLDevice> device = MTLCreateSystemDefaultDevice();
w->layer.device = device;
w->software_queue = [device newCommandQueue];
@@ -1526,13 +1770,14 @@ struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d,
w->view.surface = w;
darwin_ctx.windows[darwin_ctx.count++] = w;
- [w->window makeKeyAndOrderFront:nil];
- [w->window makeFirstResponder:w->view];
- [NSApp activate];
+ if (!(spec->flags & YASL_PRIVATE_SURFACE_HIDDEN)) {
+ [w->window makeKeyAndOrderFront:nil];
+ [w->window makeFirstResponder:w->view];
+ }
sync_size(w, true);
if (spec->tearing) {
- w->layer.displaySyncEnabled = NO;
+ if (w->layer) w->layer.displaySyncEnabled = NO;
w->unpaced = true;
}
if (spec->fullscreen)
@@ -1544,6 +1789,7 @@ struct cocoa_surface *cocoa_surface_create_toplevel(struct cocoa_display *d,
static void surface_free(struct cocoa_surface *w)
{
[w->link release];
+ [w->gl_context release];
[w->software_buffer release];
[w->software_queue release];
[w->view release];
@@ -1570,7 +1816,7 @@ void cocoa_surface_destroy(struct cocoa_surface *w)
if (!w || w->destroyed) return;
if (darwin_ctx.drag.active) {
if (darwin_ctx.drag.origin == w || darwin_ctx.drag.carried == w)
- drag_end(&darwin_ctx, YASL_DRAG_CANCELLED);
+ drag_end(&darwin_ctx, YASL_PRIVATE_DRAG_CANCELLED);
else if (darwin_ctx.drag.hover == w)
darwin_ctx.drag.hover = NULL;
}
@@ -1589,7 +1835,7 @@ void cocoa_surface_destroy(struct cocoa_surface *w)
w->tablet_down = false;
darwin_ctx.depth++;
bool native_closed = w->closed;
- static const struct yasl_window_cbs no_callbacks = {0};
+ static const struct yasl_private_window_cbs no_callbacks = {0};
w->destroyed = true;
w->closed = true;
w->cbs = &no_callbacks;
@@ -1619,7 +1865,7 @@ void cocoa_surface_destroy(struct cocoa_surface *w)
}
darwin_ctx.depth--;
if (darwin_ctx.loop)
- yasl_loop_kick(darwin_ctx.loop);
+ yasl_private_loop_kick(darwin_ctx.loop);
}
void cocoa_surface_set_title(struct cocoa_surface *w, const char *title)
@@ -1629,7 +1875,7 @@ void cocoa_surface_set_title(struct cocoa_surface *w, const char *title)
w->window.title = [NSString stringWithUTF8String:w->title] ?: @"YASL";
}
-static bool impl_surface_set_size(struct yasl_surface *surface, struct yasl_vector2i size)
+static bool impl_surface_set_size(struct yasl_surface *surface, struct yasl_private_vector2i size)
{
struct cocoa_surface *w = (struct cocoa_surface *)surface;
if (!w || w->closed || w->is_popup || size.x <= 0 || size.y <= 0) {
@@ -1693,16 +1939,16 @@ void cocoa_surface_set_background_color(struct cocoa_surface *w, uint32_t bg)
dark ? NSAppearanceNameDarkAqua : NSAppearanceNameAqua];
}
-void cocoa_surface_set_cursor(struct cocoa_surface *w, enum yasl_cursor cursor)
+void cocoa_surface_set_cursor(struct cocoa_surface *w, enum yasl_private_cursor cursor)
{
- if (!w || w->closed || cursor == w->cursor || (unsigned)cursor > YASL_CURSOR_HIDDEN) return;
+ if (!w || w->closed || cursor == w->cursor || (unsigned)cursor > YASL_PRIVATE_CURSOR_HIDDEN) return;
w->cursor = cursor;
NSPoint p = [w->view convertPoint:w->window.mouseLocationOutsideOfEventStream fromView:nil];
if (w->window.isKeyWindow && NSPointInRect(p, w->view.bounds))
[cocoa_cursor(cursor) set];
}
-bool cocoa_surface_place_under_pointer(struct cocoa_surface *w, struct yasl_vector2i at)
+bool cocoa_surface_place_under_pointer(struct cocoa_surface *w, struct yasl_private_vector2i at)
{
if (!w || w->closed || !w->window) return false;
CGFloat scale = w->window.backingScaleFactor;
@@ -1716,7 +1962,7 @@ bool cocoa_surface_place_under_pointer(struct cocoa_surface *w, struct yasl_vect
return true;
}
-void cocoa_surface_set_ime_caret(struct cocoa_surface *w, struct yasl_rect2i caret)
+void cocoa_surface_set_ime_caret(struct cocoa_surface *w, struct yasl_private_rect2i caret)
{
if (w) w->ime_caret = caret;
}
@@ -1727,26 +1973,26 @@ bool cocoa_surface_ime_preedit_cursor(const struct cocoa_surface *w, int *begin,
return false;
}
-static NSPasteboard *pasteboard_for_selection(enum yasl_selection sel)
+static NSPasteboard *pasteboard_for_selection(enum yasl_private_selection sel)
{
switch (sel) {
- case YASL_CLIPBOARD:
+ case YASL_PRIVATE_CLIPBOARD:
return [NSPasteboard generalPasteboard];
- case YASL_PRIMARY:
+ case YASL_PRIVATE_PRIMARY:
return [NSPasteboard pasteboardWithName:@"dev.wsap.yasl.primary"];
default:
return nil;
}
}
-static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_selection sel, const char *text);
+static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_private_selection sel, const char *text);
-void cocoa_display_copy(struct cocoa_display *d, enum yasl_selection sel, const char *text)
+void cocoa_display_copy(struct cocoa_display *d, enum yasl_private_selection sel, const char *text)
{
(void)cocoa_display_copy_text(d, sel, text);
}
-static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_selection sel, const char *text)
+static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_private_selection sel, const char *text)
{
assert([NSThread isMainThread]);
if (!d || !text) { errno = EINVAL; return false; }
@@ -1763,7 +2009,7 @@ static bool cocoa_display_copy_text(struct cocoa_display *d, enum yasl_selection
return true;
}
-static bool cocoa_display_request_paste(struct cocoa_display *d, enum yasl_selection sel,
+static bool cocoa_display_request_paste(struct cocoa_display *d, enum yasl_private_selection sel,
struct cocoa_surface *target, uint64_t id)
{
assert([NSThread isMainThread]);
@@ -1776,15 +2022,15 @@ static bool cocoa_display_request_paste(struct cocoa_display *d, enum yasl_selec
if (!pb) { errno = EINVAL; return false; }
NSString *s = [pb stringForType:NSPasteboardTypeString];
const char *utf8 = s ? s.UTF8String : NULL;
- enum yasl_transfer_status status = !s ? YASL_TRANSFER_UNAVAILABLE :
- utf8 ? YASL_TRANSFER_OK : YASL_TRANSFER_FAILED;
- yasl_surface_complete_paste(&target->base, id, status,
+ enum yasl_private_transfer_status status = !s ? YASL_PRIVATE_TRANSFER_UNAVAILABLE :
+ utf8 ? YASL_PRIVATE_TRANSFER_OK : YASL_PRIVATE_TRANSFER_FAILED;
+ yasl_private_surface_complete_paste(&target->base, id, status,
utf8 ? utf8 : NULL, utf8 ? strlen(utf8) : 0);
errno = 0;
return true;
}
-void cocoa_display_paste(struct cocoa_display *d, enum yasl_selection sel, struct cocoa_surface *target)
+void cocoa_display_paste(struct cocoa_display *d, enum yasl_private_selection sel, struct cocoa_surface *target)
{
(void)cocoa_display_request_paste(d, sel, target, 0);
}
@@ -1795,18 +2041,20 @@ int cocoa_display_modifiers(const struct cocoa_display *d)
return (int)cocoa_mods(NSEvent.modifierFlags);
}
-struct yasl_geometry cocoa_surface_geometry(const struct cocoa_surface *w)
+struct yasl_private_geometry cocoa_surface_geometry(const struct cocoa_surface *w)
{
- return (struct yasl_geometry){
- .size = w ? w->size : (struct yasl_vector2i){0},
- .scale_120 = w ? w->scale_120 : YASL_SCALE_UNIT,
+ return (struct yasl_private_geometry){
+ .size = w ? w->size : (struct yasl_private_vector2i){0},
+ .scale_120 = w ? w->scale_120 : YASL_PRIVATE_SCALE_UNIT,
};
}
static bool popup_spec_valid(const struct cocoa_surface *parent,
- const struct yasl_popup_spec *spec)
+ const struct yasl_private_popup_spec *spec)
{
if (!parent || !spec) return false;
+ if (spec->positioner_explicit && (spec->anchor_edges > 8 || spec->gravity > 8 ||
+ (spec->constraints & ~63u))) return false;
int parent_cx = parent->is_popup ? parent->content_offset.x : 0;
int parent_cy = parent->is_popup ? parent->content_offset.y : 0;
int64_t x = (int64_t)spec->anchor.x - parent_cx;
@@ -1821,7 +2069,7 @@ static bool popup_spec_valid(const struct cocoa_surface *parent,
(int64_t)spec->content_offset.y + spec->content_size.y <= spec->size.y;
}
-static NSRect popup_calc_frame(struct cocoa_surface *parent, const struct yasl_popup_spec *spec)
+static NSRect popup_calc_frame_legacy(struct cocoa_surface *parent, const struct yasl_private_popup_spec *spec)
{
CGFloat scale = parent->window ? parent->window.backingScaleFactor : 1.0;
if (scale < 1.0) scale = 1.0;
@@ -1885,8 +2133,153 @@ static NSRect popup_calc_frame(struct cocoa_surface *parent, const struct yasl_p
return frame;
}
+enum {
+ POPUP_EDGE_TOP = 1,
+ POPUP_EDGE_BOTTOM = 2,
+ POPUP_EDGE_LEFT = 3,
+ POPUP_EDGE_RIGHT = 4,
+ POPUP_EDGE_TOP_LEFT = 5,
+ POPUP_EDGE_BOTTOM_LEFT = 6,
+ POPUP_EDGE_TOP_RIGHT = 7,
+ POPUP_EDGE_BOTTOM_RIGHT = 8,
+ POPUP_CONSTRAINT_SLIDE_X = 1u << 0,
+ POPUP_CONSTRAINT_SLIDE_Y = 1u << 1,
+ POPUP_CONSTRAINT_FLIP_X = 1u << 2,
+ POPUP_CONSTRAINT_FLIP_Y = 1u << 3,
+ POPUP_CONSTRAINT_RESIZE_X = 1u << 4,
+ POPUP_CONSTRAINT_RESIZE_Y = 1u << 5,
+};
+
+static bool popup_edge_has_left(unsigned edge)
+{ return edge == POPUP_EDGE_LEFT || edge == POPUP_EDGE_TOP_LEFT || edge == POPUP_EDGE_BOTTOM_LEFT; }
+static bool popup_edge_has_right(unsigned edge)
+{ return edge == POPUP_EDGE_RIGHT || edge == POPUP_EDGE_TOP_RIGHT || edge == POPUP_EDGE_BOTTOM_RIGHT; }
+static bool popup_edge_has_top(unsigned edge)
+{ return edge == POPUP_EDGE_TOP || edge == POPUP_EDGE_TOP_LEFT || edge == POPUP_EDGE_TOP_RIGHT; }
+static bool popup_edge_has_bottom(unsigned edge)
+{ return edge == POPUP_EDGE_BOTTOM || edge == POPUP_EDGE_BOTTOM_LEFT || edge == POPUP_EDGE_BOTTOM_RIGHT; }
+
+static unsigned popup_flip_x_edge(unsigned edge)
+{
+ switch (edge) {
+ case POPUP_EDGE_LEFT: return POPUP_EDGE_RIGHT;
+ case POPUP_EDGE_RIGHT: return POPUP_EDGE_LEFT;
+ case POPUP_EDGE_TOP_LEFT: return POPUP_EDGE_TOP_RIGHT;
+ case POPUP_EDGE_BOTTOM_LEFT: return POPUP_EDGE_BOTTOM_RIGHT;
+ case POPUP_EDGE_TOP_RIGHT: return POPUP_EDGE_TOP_LEFT;
+ case POPUP_EDGE_BOTTOM_RIGHT: return POPUP_EDGE_BOTTOM_LEFT;
+ default: return edge;
+ }
+}
+
+static unsigned popup_flip_y_edge(unsigned edge)
+{
+ switch (edge) {
+ case POPUP_EDGE_TOP: return POPUP_EDGE_BOTTOM;
+ case POPUP_EDGE_BOTTOM: return POPUP_EDGE_TOP;
+ case POPUP_EDGE_TOP_LEFT: return POPUP_EDGE_BOTTOM_LEFT;
+ case POPUP_EDGE_TOP_RIGHT: return POPUP_EDGE_BOTTOM_RIGHT;
+ case POPUP_EDGE_BOTTOM_LEFT: return POPUP_EDGE_TOP_LEFT;
+ case POPUP_EDGE_BOTTOM_RIGHT: return POPUP_EDGE_TOP_RIGHT;
+ default: return edge;
+ }
+}
+
+static CGFloat popup_axis_overflow(CGFloat min, CGFloat max, CGFloat visible_min, CGFloat visible_max)
+{
+ return MAX(0, visible_min - min) + MAX(0, max - visible_max);
+}
+
+static NSRect popup_frame_explicit(struct cocoa_surface *parent,
+ const struct yasl_private_popup_spec *spec, unsigned anchor, unsigned gravity)
+{
+ CGFloat scale = parent->window.backingScaleFactor;
+ if (scale < 1.0) scale = 1.0;
+ CGFloat parent_x = (parent->is_popup ? parent->content_offset.x : 0) / scale;
+ CGFloat parent_y = (parent->is_popup ? parent->content_offset.y : 0) / scale;
+ CGFloat ax = spec->anchor.x / scale - parent_x;
+ CGFloat ay = spec->anchor.y / scale - parent_y;
+ CGFloat aw = spec->anchor.width / scale;
+ CGFloat ah = spec->anchor.height / scale;
+ CGFloat anchor_x = popup_edge_has_left(anchor) ? ax :
+ popup_edge_has_right(anchor) ? ax + aw : ax + aw * 0.5;
+ CGFloat anchor_y = popup_edge_has_top(anchor) ? ay :
+ popup_edge_has_bottom(anchor) ? ay + ah : ay + ah * 0.5;
+ CGFloat offset_x = spec->content_offset.x / scale;
+ CGFloat offset_y = spec->content_offset.y / scale;
+ CGFloat content_w = (spec->content_size.x ? spec->content_size.x : spec->size.x - spec->content_offset.x) / scale;
+ CGFloat content_h = (spec->content_size.y ? spec->content_size.y : spec->size.y - spec->content_offset.y) / scale;
+ CGFloat content_left = popup_edge_has_right(gravity) ? anchor_x :
+ popup_edge_has_left(gravity) ? anchor_x - content_w : anchor_x - content_w * 0.5;
+ CGFloat content_top = popup_edge_has_bottom(gravity) ? anchor_y :
+ popup_edge_has_top(gravity) ? anchor_y - content_h : anchor_y - content_h * 0.5;
+ CGFloat top_left_x = content_left - offset_x;
+ CGFloat top_left_y = content_top - offset_y;
+
+ CGFloat view_h = parent->view.bounds.size.height;
+ NSPoint in_view = NSMakePoint(top_left_x, view_h - top_left_y);
+ NSPoint in_window = [parent->view convertPoint:in_view toView:nil];
+ NSPoint screen_top_left = [parent->window convertPointToScreen:in_window];
+ CGFloat width = spec->size.x / scale;
+ CGFloat height = spec->size.y / scale;
+ return NSMakeRect(screen_top_left.x, screen_top_left.y - height, width, height);
+}
+
+static NSRect popup_calc_frame(struct cocoa_surface *parent, const struct yasl_private_popup_spec *spec)
+{
+ if (!spec->positioner_explicit)
+ return popup_calc_frame_legacy(parent, spec);
+
+ NSRect frame = popup_frame_explicit(parent, spec, spec->anchor_edges, spec->gravity);
+ NSScreen *screen = parent->window.screen ?: [NSScreen mainScreen];
+ if (!screen) return frame;
+ NSRect visible = screen.visibleFrame;
+ unsigned constraints = spec->constraints;
+
+ if (constraints & POPUP_CONSTRAINT_FLIP_X) {
+ NSRect flipped = popup_frame_explicit(parent, spec,
+ popup_flip_x_edge(spec->anchor_edges), popup_flip_x_edge(spec->gravity));
+ if (popup_axis_overflow(flipped.origin.x, NSMaxX(flipped), NSMinX(visible), NSMaxX(visible)) <
+ popup_axis_overflow(frame.origin.x, NSMaxX(frame), NSMinX(visible), NSMaxX(visible)))
+ frame = NSMakeRect(flipped.origin.x, frame.origin.y, flipped.size.width, frame.size.height);
+ }
+ if (constraints & POPUP_CONSTRAINT_FLIP_Y) {
+ NSRect flipped = popup_frame_explicit(parent, spec,
+ popup_flip_y_edge(spec->anchor_edges), popup_flip_y_edge(spec->gravity));
+ if (popup_axis_overflow(flipped.origin.y, NSMaxY(flipped), NSMinY(visible), NSMaxY(visible)) <
+ popup_axis_overflow(frame.origin.y, NSMaxY(frame), NSMinY(visible), NSMaxY(visible)))
+ frame = NSMakeRect(frame.origin.x, flipped.origin.y, frame.size.width, flipped.size.height);
+ }
+
+ if (constraints & POPUP_CONSTRAINT_SLIDE_X) {
+ if (NSMinX(frame) < NSMinX(visible)) frame.origin.x = NSMinX(visible);
+ if (NSMaxX(frame) > NSMaxX(visible)) frame.origin.x = NSMaxX(visible) - frame.size.width;
+ }
+ if (constraints & POPUP_CONSTRAINT_SLIDE_Y) {
+ if (NSMinY(frame) < NSMinY(visible)) frame.origin.y = NSMinY(visible);
+ if (NSMaxY(frame) > NSMaxY(visible)) frame.origin.y = NSMaxY(visible) - frame.size.height;
+ }
+ if (constraints & POPUP_CONSTRAINT_RESIZE_X) {
+ if (NSMinX(frame) < NSMinX(visible)) {
+ frame.size.width = MAX(0, frame.size.width - (NSMinX(visible) - NSMinX(frame)));
+ frame.origin.x = NSMinX(visible);
+ }
+ if (NSMaxX(frame) > NSMaxX(visible))
+ frame.size.width = MAX(0, NSMaxX(visible) - frame.origin.x);
+ }
+ if (constraints & POPUP_CONSTRAINT_RESIZE_Y) {
+ if (NSMinY(frame) < NSMinY(visible)) {
+ frame.size.height = MAX(0, frame.size.height - (NSMinY(visible) - NSMinY(frame)));
+ frame.origin.y = NSMinY(visible);
+ }
+ if (NSMaxY(frame) > NSMaxY(visible))
+ frame.size.height = MAX(0, NSMaxY(visible) - frame.origin.y);
+ }
+ return frame;
+}
+
struct cocoa_surface *cocoa_surface_create_popup(struct cocoa_surface *parent,
- const struct yasl_popup_spec *spec)
+ const struct yasl_private_popup_spec *spec)
{
assert([NSThread isMainThread]);
if (!parent || parent->closed || !parent->window || !parent->view ||
@@ -1905,8 +2298,10 @@ struct cocoa_surface *cocoa_surface_create_popup(struct cocoa_surface *parent,
struct cocoa_surface *w = calloc(1, sizeof(*w));
if (!w) return NULL;
w->base.display = parent->base.display;
+ w->base.presenter = parent->base.presenter;
+ w->base.creation_flags = parent->base.creation_flags;
cocoa_surface_set_callbacks(w, NULL, NULL);
- w->clock.refresh_ns = parent->clock.refresh_ns ? parent->clock.refresh_ns : YASL_REFRESH_DEFAULT_NS;
+ w->clock.refresh_ns = parent->clock.refresh_ns ? parent->clock.refresh_ns : YASL_PRIVATE_REFRESH_DEFAULT_NS;
w->parent = parent;
w->is_popup = true;
w->content_offset = spec->content_offset;
@@ -1968,7 +2363,7 @@ struct cocoa_surface *cocoa_surface_create_popup(struct cocoa_surface *parent,
}
bool cocoa_surface_popup_reposition(struct cocoa_surface *popup,
- const struct yasl_popup_spec *spec)
+ const struct yasl_private_popup_spec *spec)
{
if (!popup || popup->closed || !popup->is_popup || !popup->parent ||
!popup->parent->window || !popup->parent->view ||
@@ -2020,8 +2415,8 @@ static NSImage *image_from_pixels(const uint32_t *pixels, int width, int height)
}
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)
+ const struct yasl_private_drag_image *image,
+ const struct yasl_private_drag_listener *l, void *ud)
{
if (!d || !origin || d->drag.active || !l)
return false;
@@ -2029,7 +2424,7 @@ bool cocoa_display_drag_start(struct cocoa_display *d, struct cocoa_surface *ori
d->drag.origin = origin;
d->drag.carried = NULL;
d->drag.hover = NULL;
- d->drag.hover_pos = (struct yasl_vector2i){0};
+ d->drag.hover_pos = (struct yasl_private_vector2i){0};
d->drag.listener = *l;
d->drag.ud = ud;
[d->drag.image release];
@@ -2074,7 +2469,7 @@ bool cocoa_display_drag_start(struct cocoa_display *d, struct cocoa_surface *ori
return true;
}
-bool cocoa_surface_drag_carry(struct cocoa_surface *w, struct yasl_vector2i offset)
+bool cocoa_surface_drag_carry(struct cocoa_surface *w, struct yasl_private_vector2i offset)
{
if (!w || w->closed || !darwin_ctx.drag.active)
return false;
@@ -2088,33 +2483,33 @@ bool cocoa_surface_drag_carry(struct cocoa_surface *w, struct yasl_vector2i offs
return cocoa_surface_place_under_pointer(w, offset);
}
-bool cocoa_surface_start_interactive_resize(struct cocoa_surface *s, enum yasl_resize_edge edge)
+bool cocoa_surface_start_interactive_resize(struct cocoa_surface *s, enum yasl_private_resize_edge edge)
{
(void)s; (void)edge;
return false;
}
-bool cocoa_surface_show_window_menu(struct cocoa_surface *s, struct yasl_vector2i pos)
+bool cocoa_surface_show_window_menu(struct cocoa_surface *s, struct yasl_private_vector2i pos)
{
(void)s; (void)pos;
return false;
}
void cocoa_surface_set_callbacks(struct cocoa_surface *w,
- const struct yasl_window_cbs *cbs, void *ud)
+ const struct yasl_private_window_cbs *cbs, void *ud)
{
if (!w || w->closed) return;
- w->base.callbacks = cbs ? *cbs : (struct yasl_window_cbs){0};
+ w->base.callbacks = cbs ? *cbs : (struct yasl_private_window_cbs){0};
w->base.ud = ud;
w->cbs = &w->base.callbacks;
w->ud = ud;
}
-bool cocoa_surface_poll_event(struct cocoa_surface *w, struct yasl_event *ev)
+bool cocoa_surface_poll_event(struct cocoa_surface *w, struct yasl_private_event *ev)
{
if (!w || !ev || !input_queue_pop(&w->input, ev))
return false;
- if (ev->type == YASL_EVENT_INPUT_RESET) {
+ if (ev->type == YASL_PRIVATE_EVENT_INPUT_RESET) {
w->buttons = 0;
w->tablet_down = false;
w->mods = 0;
@@ -2126,103 +2521,103 @@ bool cocoa_surface_poll_event(struct cocoa_surface *w, struct yasl_event *ev)
return true;
}
-struct yasl_timing cocoa_surface_timing(const struct cocoa_surface *w)
+struct yasl_private_timing cocoa_surface_timing(const struct cocoa_surface *w)
{
- struct yasl_timing timing = {0};
+ struct yasl_private_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.next_present_ns = yasl_private_clock_next_present_ns(&w->clock);
timing.frames_skipped = w->frames_skipped;
timing.source = w->clock.source;
- yasl_clock_resize_stats(&w->clock, &timing.resize);
+ yasl_private_clock_resize_stats(&w->clock, &timing.resize);
}
return timing;
}
-static struct yasl_display *impl_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec)
+static struct yasl_private_display *impl_display_open(struct yasl_private_loop *loop, const struct yasl_private_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)
+static void impl_display_close(struct yasl_private_display *d)
{
cocoa_display_close((struct cocoa_display *)d);
}
-static uint32_t impl_display_caps(const struct yasl_display *d)
+static uint32_t impl_display_caps(const struct yasl_private_display *d)
{
return cocoa_display_caps((const struct cocoa_display *)d);
}
-static int impl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms)
+static int impl_display_dispatch_timeout(struct yasl_private_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)
+static void impl_display_flush(struct yasl_private_display *d)
{
cocoa_display_flush((struct cocoa_display *)d);
}
-static int impl_display_double_click_ms(const struct yasl_display *d)
+static int impl_display_double_click_ms(const struct yasl_private_display *d)
{
(void)d;
return (int)lround([NSEvent doubleClickInterval] * 1000.0);
}
-static int impl_display_modifiers(const struct yasl_display *d)
+static int impl_display_modifiers(const struct yasl_private_display *d)
{
return cocoa_display_modifiers((const struct cocoa_display *)d);
}
-static void impl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text)
+static void impl_display_copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text)
{
cocoa_display_copy((struct cocoa_display *)d, sel, text);
}
-static void impl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target)
+static void impl_display_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target)
{
cocoa_display_paste((struct cocoa_display *)d, sel, (struct cocoa_surface *)target);
}
-static bool impl_display_copy_text(struct yasl_display *d, enum yasl_selection sel, const char *text)
+static bool impl_display_copy_text(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text)
{
return cocoa_display_copy_text((struct cocoa_display *)d, sel, text);
}
-static bool impl_display_request_paste(struct yasl_display *d, enum yasl_selection sel,
+static bool impl_display_request_paste(struct yasl_private_display *d, enum yasl_private_selection sel,
struct yasl_surface *target, uint64_t request_id)
{
return cocoa_display_request_paste((struct cocoa_display *)d, sel,
(struct cocoa_surface *)target, request_id);
}
-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)
+static bool impl_display_drag_start(struct yasl_private_display *d, struct yasl_surface *origin, const struct yasl_private_drag_image *image, const struct yasl_private_drag_listener *l, void *ud)
{
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)
+static void impl_display_drag_cancel(struct yasl_private_display *d)
{
- drag_end((struct cocoa_display *)d, YASL_DRAG_CANCELLED);
+ drag_end((struct cocoa_display *)d, YASL_PRIVATE_DRAG_CANCELLED);
}
-static struct yasl_surface *impl_surface_create_toplevel(struct yasl_display *d,
- const struct yasl_window_spec *spec, void *graphics_device)
+static struct yasl_surface *impl_surface_create_toplevel(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *graphics_device)
{
struct cocoa_surface *result = cocoa_surface_create_toplevel((struct cocoa_display *)d, spec, graphics_device);
return result ? &result->base : NULL;
}
-static struct yasl_surface *impl_surface_create_popup(struct yasl_surface *parent, const struct yasl_popup_spec *spec)
+static struct yasl_surface *impl_surface_create_popup(struct yasl_surface *parent, const struct yasl_private_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)
+static bool impl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_private_popup_spec *spec)
{
return cocoa_surface_popup_reposition((struct cocoa_surface *)popup, spec);
}
@@ -2238,12 +2633,12 @@ static struct input_queue *impl_surface_input_queue(struct yasl_surface *s)
return w ? &w->input : NULL;
}
-static struct yasl_display *impl_surface_display(const struct yasl_surface *s)
+static struct yasl_private_display *impl_surface_display(const struct yasl_surface *s)
{
return s ? &darwin_ctx.base : NULL;
}
-static void impl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_window_cbs *cbs, void *ud)
+static void impl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_private_window_cbs *cbs, void *ud)
{
cocoa_surface_set_callbacks((struct cocoa_surface *)s, cbs, ud);
}
@@ -2258,17 +2653,17 @@ 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)
+static bool impl_surface_present(struct yasl_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage)
{
return cocoa_surface_present((struct cocoa_surface *)s, fb, damage);
}
-static struct yasl_geometry impl_surface_geometry(const struct yasl_surface *s)
+static struct yasl_private_geometry impl_surface_geometry(const struct yasl_surface *s)
{
return cocoa_surface_geometry((const struct cocoa_surface *)s);
}
-static struct yasl_timing impl_surface_timing(const struct yasl_surface *s)
+static struct yasl_private_timing impl_surface_timing(const struct yasl_surface *s)
{
return cocoa_surface_timing((const struct cocoa_surface *)s);
}
@@ -2276,10 +2671,10 @@ static struct yasl_timing impl_surface_timing(const struct yasl_surface *s)
static uint64_t impl_surface_frame_time_ns(struct yasl_surface *s, uint64_t now)
{
struct cocoa_surface *w = (struct cocoa_surface *)s;
- return w ? yasl_clock_frame_timestamp_ns(&w->clock, now) : now;
+ return w ? yasl_private_clock_frame_timestamp_ns(&w->clock, now) : now;
}
-static void impl_surface_set_cursor(struct yasl_surface *s, enum yasl_cursor cursor)
+static void impl_surface_set_cursor(struct yasl_surface *s, enum yasl_private_cursor cursor)
{
cocoa_surface_set_cursor((struct cocoa_surface *)s, cursor);
}
@@ -2300,7 +2695,7 @@ static bool impl_surface_ime_preedit_cursor(const struct yasl_surface *s, int *b
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)
+static void impl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_private_rect2i caret)
{
cocoa_surface_set_ime_caret((struct cocoa_surface *)s, caret);
}
@@ -2310,35 +2705,165 @@ 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)
+static bool impl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_private_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)
+static bool impl_surface_show_window_menu(struct yasl_surface *s, struct yasl_private_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)
+static bool impl_surface_drag_carry(struct yasl_surface *s, struct yasl_private_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)
+static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_private_vector2i at)
{
return cocoa_surface_place_under_pointer((struct cocoa_surface *)s, at);
}
+static struct yasl_surface *impl_surface_create_gl(struct yasl_private_display *display,
+ const struct yasl_private_window_spec *spec, const int *attrs, struct yasl_surface *share)
+{
+ struct yasl_private_gl_options options;
+ if (!spec || !yasl_private_gl_parse_options(attrs, &options) ||
+ options.api != YASL_PRIVATE_GL_OPENGL || options.debug || (share &&
+ (share->display != display || share->presenter != YASL_PRIVATE_PRESENTER_GL)))
+ return NULL;
+ int profile = options.profile;
+ if (profile == YASL_PRIVATE_GL_ANY)
+ profile = options.major >= 3 ? YASL_PRIVATE_GL_CORE : YASL_PRIVATE_GL_COMPATIBILITY;
+ if (options.forward_compat && profile != YASL_PRIVATE_GL_CORE)
+ return NULL;
+ if ((profile == YASL_PRIVATE_GL_CORE &&
+ (options.major != 3 || options.minor != 2)) ||
+ (profile == YASL_PRIVATE_GL_COMPATIBILITY &&
+ (options.major > 2 || (options.major == 2 && options.minor > 1))))
+ return NULL;
+
+ struct yasl_private_window_spec gl_spec = *spec;
+ gl_spec.external_gpu = true;
+ struct cocoa_surface *w = cocoa_surface_create_toplevel((struct cocoa_display *)display, &gl_spec, NULL);
+ if (!w) return NULL;
+
+ NSOpenGLPixelFormatAttribute pixel_attrs[32];
+ NSUInteger n = 0;
+ pixel_attrs[n++] = NSOpenGLPFAAccelerated;
+ pixel_attrs[n++] = NSOpenGLPFADoubleBuffer;
+ pixel_attrs[n++] = NSOpenGLPFAColorSize;
+ pixel_attrs[n++] = options.red_bits + options.green_bits + options.blue_bits + options.alpha_bits;
+ pixel_attrs[n++] = NSOpenGLPFAAlphaSize;
+ pixel_attrs[n++] = options.alpha_bits;
+ pixel_attrs[n++] = NSOpenGLPFADepthSize;
+ pixel_attrs[n++] = options.depth_bits;
+ pixel_attrs[n++] = NSOpenGLPFAStencilSize;
+ pixel_attrs[n++] = options.stencil_bits;
+ if (options.samples > 0) {
+ pixel_attrs[n++] = NSOpenGLPFASampleBuffers;
+ pixel_attrs[n++] = 1;
+ pixel_attrs[n++] = NSOpenGLPFASamples;
+ pixel_attrs[n++] = options.samples;
+ }
+ if (options.srgb) pixel_attrs[n++] = NSOpenGLPFASRGB;
+ if (profile == YASL_PRIVATE_GL_CORE) {
+ pixel_attrs[n++] = NSOpenGLPFAOpenGLProfile;
+ pixel_attrs[n++] = NSOpenGLProfileVersion3_2Core;
+ } else if (profile == YASL_PRIVATE_GL_COMPATIBILITY) {
+ pixel_attrs[n++] = NSOpenGLPFAOpenGLProfile;
+ pixel_attrs[n++] = NSOpenGLProfileVersionLegacy;
+ }
+ pixel_attrs[n++] = 0;
+
+ NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:pixel_attrs];
+ NSOpenGLContext *shared = share ? ((struct cocoa_surface *)share)->gl_context : nil;
+ if (format)
+ w->gl_context = [[NSOpenGLContext alloc] initWithFormat:format shareContext:shared];
+ [format release];
+ if (!w->gl_context) {
+ cocoa_surface_destroy(w);
+ reap_dead((struct cocoa_display *)display);
+ return NULL;
+ }
+ [w->view setLayer:[CALayer layer]];
+ w->layer = nil;
+ [w->gl_context setView:w->view];
+ w->base.presenter = YASL_PRIVATE_PRESENTER_GL;
+ return &w->base;
+}
+
+static bool impl_gl_make_current(struct yasl_private_display *display, struct yasl_surface *surface)
+{
+ (void)display;
+ if (!surface) {
+ [NSOpenGLContext clearCurrentContext];
+ return true;
+ }
+ struct cocoa_surface *w = (struct cocoa_surface *)surface;
+ if (w->base.presenter != YASL_PRIVATE_PRESENTER_GL || !w->gl_context || w->closed)
+ return false;
+ [w->gl_context makeCurrentContext];
+ return [NSOpenGLContext currentContext] == w->gl_context;
+}
+
+static yasl_private_gl_proc impl_gl_get_proc_address(struct yasl_private_display *display, const char *name)
+{
+ (void)display;
+ return name ? (yasl_private_gl_proc)dlsym(RTLD_DEFAULT, name) : NULL;
+}
+
+static bool impl_gl_swap_interval(struct yasl_private_display *display, struct yasl_surface *surface, int interval)
+{
+ (void)display;
+ struct cocoa_surface *w = (struct cocoa_surface *)surface;
+ if (!w || w->base.presenter != YASL_PRIVATE_PRESENTER_GL || !w->gl_context || w->closed)
+ return false;
+ [w->gl_context setValues:&interval forParameter:NSOpenGLCPSwapInterval];
+ return true;
+}
+
+static bool impl_gl_swap_buffers(struct yasl_private_display *display, struct yasl_surface *surface)
+{
+ (void)display;
+ struct cocoa_surface *w = (struct cocoa_surface *)surface;
+ if (!w || w->base.presenter != YASL_PRIVATE_PRESENTER_GL || !w->gl_context || w->closed)
+ return false;
+ [w->gl_context flushBuffer];
+ yasl_private_clock_presented(&w->clock, yasl_private_loop_now_ns(), false);
+ return true;
+}
+
+static void *impl_surface_get_metal_layer(struct yasl_surface *surface)
+{
+ struct cocoa_surface *w = (struct cocoa_surface *)surface;
+ return w && (w->base.presenter == YASL_PRIVATE_PRESENTER_NONE ||
+ w->base.presenter == YASL_PRIVATE_PRESENTER_METAL) ? (void *)w->layer : NULL;
+}
+
+static bool impl_surface_update_text_input(struct yasl_surface *surface)
+{
+ struct cocoa_surface *w = (struct cocoa_surface *)surface;
+ if (!w || w->closed || w->destroyed || !w->view) return false;
+ if (w->base.text_input_active) {
+ [w->window makeFirstResponder:w->view];
+ } else {
+ [w->view.inputContext discardMarkedText];
+ if (w->preedit) [w->view unmarkText];
+ }
+ return true;
+}
+
static bool impl_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
- struct yasl_vector2i *size)
+ struct yasl_private_vector2i *size)
{
struct cocoa_surface *w = (struct cocoa_surface *)s;
/* did_present stays set from the last draw until the next one starts, and
* pixels are reallocated only when a draw starts. */
if (w->closed || w->rendering || !w->did_present || !w->software_queue || !w->pixels)
return false;
- return yasl_copy_frame(w->pixels, w->shown_size, out, cap, size);
+ return yasl_private_copy_frame(w->pixels, w->shown_size, out, cap, size);
}
static bool impl_surface_bell(struct yasl_surface *s)
@@ -2349,7 +2874,7 @@ static bool impl_surface_bell(struct yasl_surface *s)
return true;
}
-static void *impl_display_get_native_handle(struct yasl_display *d)
+static void *impl_display_get_native_handle(struct yasl_private_display *d)
{
return d ? (void *)NSApp : NULL;
}
@@ -2361,19 +2886,26 @@ static void *impl_surface_get_native_handle(struct yasl_surface *s)
}
static bool impl_surface_get_native_frame(struct yasl_surface *s,
- const struct yasl_framebuffer *target, struct yasl_native_frame *out)
+ const struct yasl_private_framebuffer *target, struct yasl_private_native_frame *out)
{
struct cocoa_surface *w = (struct cocoa_surface *)s;
if (w->closed || !w->rendering || w->did_present || !w->drawable ||
target->pixels != w->frame.target.pixels ||
target->size.x != w->size.x || target->size.y != w->size.y)
return false;
- *out = (struct yasl_native_frame){(void *)w->drawable, w->transaction};
+ *out = (struct yasl_private_native_frame){(void *)w->drawable, w->transaction};
return true;
}
-const struct yasl_backend_ops yasl_cocoa_backend = {
- .type = YASL_BACKEND_COCOA,
+const struct yasl_private_backend_ops yasl_private_cocoa_backend = {
+ .type = YASL_PRIVATE_BACKEND_COCOA,
+ .surface_create_gl = impl_surface_create_gl,
+ .gl_make_current = impl_gl_make_current,
+ .gl_get_proc_address = impl_gl_get_proc_address,
+ .gl_swap_interval = impl_gl_swap_interval,
+ .gl_swap_buffers = impl_gl_swap_buffers,
+ .surface_get_metal_layer = impl_surface_get_metal_layer,
+ .surface_update_text_input = impl_surface_update_text_input,
.display_open = impl_display_open,
.display_close = impl_display_close,
.display_caps = impl_display_caps,
@@ -2421,20 +2953,20 @@ const struct yasl_backend_ops yasl_cocoa_backend = {
.surface_get_native_frame = impl_surface_get_native_frame,
};
-NSApplication *yasl_cocoa_display_get_application(struct yasl_display *d)
+NSApplication *yasl_private_cocoa_display_get_application(struct yasl_private_display *d)
{
- return (d && yasl_display_backend(d) == YASL_BACKEND_COCOA) ?
- (NSApplication *)yasl_display_get_native_handle(d) : nil;
+ return (d && yasl_private_display_backend(d) == YASL_PRIVATE_BACKEND_COCOA) ?
+ (NSApplication *)yasl_private_display_get_native_handle(d) : nil;
}
-NSWindow *yasl_cocoa_surface_get_window(struct yasl_surface *s)
+NSWindow *yasl_private_cocoa_surface_get_window(struct yasl_surface *s)
{
- return (s && s->display && yasl_display_backend(s->display) == YASL_BACKEND_COCOA) ?
- (NSWindow *)yasl_surface_get_native_handle(s) : nil;
+ return (s && s->display && yasl_private_display_backend(s->display) == YASL_PRIVATE_BACKEND_COCOA) ?
+ (NSWindow *)yasl_private_surface_get_native_handle(s) : nil;
}
-NSView *yasl_cocoa_surface_get_view(struct yasl_surface *s)
+NSView *yasl_private_cocoa_surface_get_view(struct yasl_surface *s)
{
- NSWindow *w = yasl_cocoa_surface_get_window(s);
+ NSWindow *w = yasl_private_cocoa_surface_get_window(s);
return w ? w.contentView : nil;
}
diff --git a/src/platform/freedesktop/dbus.h b/src/platform/freedesktop/dbus.h
index fd0aeb1..beda62b 100644
--- a/src/platform/freedesktop/dbus.h
+++ b/src/platform/freedesktop/dbus.h
@@ -1,5 +1,5 @@
-#ifndef YASL_FREEDESKTOP_DBUS_H
-#define YASL_FREEDESKTOP_DBUS_H
+#ifndef YASL_PRIVATE_FREEDESKTOP_DBUS_H
+#define YASL_PRIVATE_FREEDESKTOP_DBUS_H
#include <stdbool.h>
#include <stdint.h>
diff --git a/src/platform/freedesktop/desktop.c b/src/platform/freedesktop/desktop.c
index a8b9bc7..f90701a 100644
--- a/src/platform/freedesktop/desktop.c
+++ b/src/platform/freedesktop/desktop.c
@@ -1,7 +1,7 @@
#define _GNU_SOURCE
#include "common/desktop.h"
#include "platform/freedesktop/dbus.h"
-#include "yasl/desktop.h"
+#include "common/private_desktop.h"
#include <errno.h>
#include <fcntl.h>
@@ -105,21 +105,21 @@ uint32_t desktop_appearance(void)
int64_t value;
if (portal_read_int("org.freedesktop.appearance", "color-scheme", &value)) {
portal = true;
- if (value == 1) flags |= YASL_APPEARANCE_DARK;
- else if (value == 2) flags |= YASL_APPEARANCE_LIGHT;
+ if (value == 1) flags |= YASL_PRIVATE_APPEARANCE_DARK;
+ else if (value == 2) flags |= YASL_PRIVATE_APPEARANCE_LIGHT;
}
if (portal_read_int("org.freedesktop.appearance", "contrast", &value)) {
portal = true;
- if (value == 1) flags |= YASL_APPEARANCE_HIGH_CONTRAST;
+ if (value == 1) flags |= YASL_PRIVATE_APPEARANCE_HIGH_CONTRAST;
}
if (portal) return flags;
/* GTK_THEME is "Name" or "Name:variant", as in "Adwaita:dark". */
const char *theme = getenv("GTK_THEME");
if (!theme) return 0;
- if (strstr(theme, "HighContrast")) flags |= YASL_APPEARANCE_HIGH_CONTRAST;
+ if (strstr(theme, "HighContrast")) flags |= YASL_PRIVATE_APPEARANCE_HIGH_CONTRAST;
if (strstr(theme, ":dark") || strstr(theme, "HighContrastInverse"))
- flags |= YASL_APPEARANCE_DARK;
+ flags |= YASL_PRIVATE_APPEARANCE_DARK;
return flags;
}
diff --git a/src/platform/posix/loop.c b/src/platform/posix/loop.c
index 3c3d730..49132f8 100644
--- a/src/platform/posix/loop.c
+++ b/src/platform/posix/loop.c
@@ -14,35 +14,35 @@
#include <time.h>
#include <unistd.h>
-#define YASL_LOOP_INIT_CAP 32
+#define YASL_PRIVATE_LOOP_INIT_CAP 32
-typedef int (*yasl_loop_fd_fn)(int fd, short revents, void *ud);
+typedef int (*yasl_private_loop_fd_fn)(int fd, short revents, void *ud);
-struct yasl_fd_source {
- yasl_loop_fd_fn fn;
+struct yasl_private_fd_source {
+ yasl_private_loop_fd_fn fn;
void *ud;
int id;
int fd;
short events;
};
-struct yasl_timer_source {
- yasl_timer_cb fn;
+struct yasl_private_timer_source {
+ yasl_private_timer_cb fn;
void *ud;
uint64_t interval_ns;
uint64_t deadline_ns;
- yasl_timer_id id;
+ yasl_private_timer_id id;
bool deleted;
};
-struct yasl_post {
+struct yasl_private_post {
void (*fn)(void *ud);
void *ud;
- struct yasl_post *next;
+ struct yasl_private_post *next;
};
/* Signals this loop watches; the previous actions are restored on unwatch. */
-struct yasl_signal_watch {
+struct yasl_private_signal_watch {
void (*cb)(void *ud, int signo);
void *ud;
int count;
@@ -50,8 +50,8 @@ struct yasl_signal_watch {
struct sigaction old[NSIG];
};
-struct yasl_loop {
- struct yasl_fd_source *fds;
+struct yasl_private_loop {
+ struct yasl_private_fd_source *fds;
struct pollfd *pollfds;
int count;
int capacity;
@@ -61,17 +61,17 @@ struct yasl_loop {
int wake_wfd;
int wake_watch;
- struct yasl_timer_source *timers;
+ struct yasl_private_timer_source *timers;
int timer_count;
int timer_capacity;
uint32_t next_timer_id;
int timer_firing_depth;
- yasl_mutex post_mutex;
- struct yasl_post *post_head;
- struct yasl_post *post_tail;
+ yasl_private_mutex post_mutex;
+ struct yasl_private_post *post_head;
+ struct yasl_private_post *post_tail;
- struct yasl_signal_watch *signals;
+ struct yasl_private_signal_watch *signals;
};
/* Handlers run on any thread, so they only touch lock-free atomics: they mark
@@ -79,34 +79,34 @@ struct yasl_loop {
* stored as fd + 1 so that the zero initializer means no pipe. */
static atomic_int signal_wake_fd[NSIG];
static atomic_bool signal_pending[NSIG];
-static struct yasl_loop *signal_owner[NSIG];
+static struct yasl_private_loop *signal_owner[NSIG];
-uint64_t yasl_loop_now_ns(void)
+uint64_t yasl_private_loop_now_ns(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000000000ull + (uint64_t)ts.tv_nsec;
}
-static void run_posts(struct yasl_loop *l)
+static void run_posts(struct yasl_private_loop *l)
{
- yasl_mutex_lock(&l->post_mutex);
- struct yasl_post *p = l->post_head;
+ yasl_private_mutex_lock(&l->post_mutex);
+ struct yasl_private_post *p = l->post_head;
l->post_head = l->post_tail = NULL;
- yasl_mutex_unlock(&l->post_mutex);
+ yasl_private_mutex_unlock(&l->post_mutex);
while (p) {
- struct yasl_post *next = p->next;
+ struct yasl_private_post *next = p->next;
p->fn(p->ud);
free(p);
p = next;
}
}
-static void run_signals(struct yasl_loop *l)
+static void run_signals(struct yasl_private_loop *l)
{
/* A callback may unwatch, so re-check the watch before each delivery. */
for (int signo = 1; signo < NSIG; signo++) {
- struct yasl_signal_watch *w = l->signals;
+ struct yasl_private_signal_watch *w = l->signals;
if (!w) return;
if (signal_owner[signo] != l || !atomic_exchange(&signal_pending[signo], false))
continue;
@@ -120,18 +120,18 @@ static int loop_wake_cb(int fd, short revents, void *ud)
char buf[128];
ssize_t n;
while ((n = read(fd, buf, sizeof(buf))) > 0 || (n < 0 && errno == EINTR)) {}
- struct yasl_loop *l = ud;
+ struct yasl_private_loop *l = ud;
run_posts(l);
run_signals(l);
return 0;
}
-struct yasl_loop *yasl_loop_create(void)
+struct yasl_private_loop *yasl_private_loop_create(void)
{
- struct yasl_loop *l = calloc(1, sizeof(*l));
+ struct yasl_private_loop *l = calloc(1, sizeof(*l));
if (!l) return NULL;
- l->capacity = YASL_LOOP_INIT_CAP;
+ l->capacity = YASL_PRIVATE_LOOP_INIT_CAP;
l->fds = malloc((size_t)l->capacity * sizeof(*l->fds));
l->pollfds = malloc((size_t)l->capacity * sizeof(*l->pollfds));
if (!l->fds || !l->pollfds) {
@@ -141,7 +141,7 @@ struct yasl_loop *yasl_loop_create(void)
return NULL;
}
- if (yasl_mutex_init(&l->post_mutex) != 0) {
+ if (yasl_private_mutex_init(&l->post_mutex) != 0) {
free(l->fds);
free(l->pollfds);
free(l);
@@ -173,23 +173,23 @@ struct yasl_loop *yasl_loop_create(void)
#endif
if (l->wake_rfd >= 0) {
- l->wake_watch = yasl_loop_add_fd(l, l->wake_rfd, POLLIN, loop_wake_cb, l);
+ l->wake_watch = yasl_private_loop_add_fd(l, l->wake_rfd, POLLIN, loop_wake_cb, l);
}
return l;
}
-void yasl_loop_destroy(struct yasl_loop *l)
+void yasl_private_loop_destroy(struct yasl_private_loop *l)
{
if (!l) return;
- yasl_loop_unwatch_signals(l);
- for (struct yasl_post *p = l->post_head; p;) {
- struct yasl_post *next = p->next;
+ yasl_private_loop_unwatch_signals(l);
+ for (struct yasl_private_post *p = l->post_head; p;) {
+ struct yasl_private_post *next = p->next;
free(p);
p = next;
}
- yasl_mutex_destroy(&l->post_mutex);
+ yasl_private_mutex_destroy(&l->post_mutex);
if (l->wake_watch > 0)
- yasl_loop_del_fd(l, l->wake_watch);
+ yasl_private_loop_del_fd(l, l->wake_watch);
if (l->wake_rfd >= 0)
close(l->wake_rfd);
if (l->wake_wfd >= 0)
@@ -200,15 +200,15 @@ void yasl_loop_destroy(struct yasl_loop *l)
free(l);
}
-int yasl_loop_add_fd(struct yasl_loop *l, int fd, short events,
+int yasl_private_loop_add_fd(struct yasl_private_loop *l, int fd, short events,
int (*fn)(int fd, short revents, void *ud), void *ud)
{
if (!l || fd < 0)
return -1;
if (l->count >= l->capacity) {
- int new_cap = l->capacity ? l->capacity * 2 : YASL_LOOP_INIT_CAP;
- struct yasl_fd_source *new_fds = realloc(l->fds, (size_t)new_cap * sizeof(*new_fds));
+ int new_cap = l->capacity ? l->capacity * 2 : YASL_PRIVATE_LOOP_INIT_CAP;
+ struct yasl_private_fd_source *new_fds = realloc(l->fds, (size_t)new_cap * sizeof(*new_fds));
if (!new_fds) return -1;
l->fds = new_fds;
@@ -221,7 +221,7 @@ int yasl_loop_add_fd(struct yasl_loop *l, int fd, short events,
int id = l->next_id++;
if (l->next_id <= 0) l->next_id = 1;
- l->fds[l->count] = (struct yasl_fd_source){
+ l->fds[l->count] = (struct yasl_private_fd_source){
.fn = fn, .ud = ud, .id = id, .fd = fd, .events = events,
};
l->pollfds[l->count] = (struct pollfd){
@@ -231,7 +231,7 @@ int yasl_loop_add_fd(struct yasl_loop *l, int fd, short events,
return id;
}
-int yasl_loop_del_fd(struct yasl_loop *l, int id)
+int yasl_private_loop_del_fd(struct yasl_private_loop *l, int id)
{
if (!l || id <= 0) return -1;
for (int i = 0; i < l->count; i++) {
@@ -245,7 +245,7 @@ int yasl_loop_del_fd(struct yasl_loop *l, int id)
return -1;
}
-int yasl_loop_set_fd_events(struct yasl_loop *l, int id, short events)
+int yasl_private_loop_set_fd_events(struct yasl_private_loop *l, int id, short events)
{
if (!l || id <= 0) return -1;
for (int i = 0; i < l->count; i++) {
@@ -270,25 +270,25 @@ static void wake_write(int fd)
errno = saved;
}
-void yasl_loop_kick(struct yasl_loop *l)
+void yasl_private_loop_kick(struct yasl_private_loop *l)
{
if (!l || l->wake_wfd < 0) return;
wake_write(l->wake_wfd);
}
-bool yasl_loop_post(struct yasl_loop *l, void (*fn)(void *ud), void *ud)
+bool yasl_private_loop_post(struct yasl_private_loop *l, void (*fn)(void *ud), void *ud)
{
if (!l || !fn || l->wake_wfd < 0) return false;
- struct yasl_post *p = malloc(sizeof(*p));
+ struct yasl_private_post *p = malloc(sizeof(*p));
if (!p) return false;
- *p = (struct yasl_post){.fn = fn, .ud = ud};
- yasl_mutex_lock(&l->post_mutex);
+ *p = (struct yasl_private_post){.fn = fn, .ud = ud};
+ yasl_private_mutex_lock(&l->post_mutex);
if (l->post_tail)
l->post_tail->next = p;
else
l->post_head = p;
l->post_tail = p;
- yasl_mutex_unlock(&l->post_mutex);
+ yasl_private_mutex_unlock(&l->post_mutex);
wake_write(l->wake_wfd);
return true;
}
@@ -305,7 +305,7 @@ static bool signal_valid(int signo)
return signo > 0 && signo < NSIG && signo != SIGKILL && signo != SIGSTOP;
}
-bool yasl_loop_watch_signals(struct yasl_loop *l, const int *signals, int n,
+bool yasl_private_loop_watch_signals(struct yasl_private_loop *l, const int *signals, int n,
void (*cb)(void *ud, int signo), void *ud)
{
if (!l || !signals || n <= 0 || !cb || l->wake_wfd < 0) {
@@ -324,14 +324,14 @@ bool yasl_loop_watch_signals(struct yasl_loop *l, const int *signals, int n,
}
wanted[signals[i]] = true;
}
- struct yasl_signal_watch *w = calloc(1, sizeof(*w));
+ struct yasl_private_signal_watch *w = calloc(1, sizeof(*w));
if (!w) return false;
w->cb = cb;
w->ud = ud;
/* Keep the old watch's saved actions for signals that stay watched, so
* that unwatch restores what the application had before any watch. */
- struct yasl_signal_watch *old = l->signals;
+ struct yasl_private_signal_watch *old = l->signals;
struct sigaction sa = {.sa_handler = signal_handler, .sa_flags = SA_RESTART};
sigemptyset(&sa.sa_mask);
for (int signo = 1; signo < NSIG; signo++) {
@@ -380,10 +380,10 @@ bool yasl_loop_watch_signals(struct yasl_loop *l, const int *signals, int n,
return true;
}
-void yasl_loop_unwatch_signals(struct yasl_loop *l)
+void yasl_private_loop_unwatch_signals(struct yasl_private_loop *l)
{
if (!l || !l->signals) return;
- struct yasl_signal_watch *w = l->signals;
+ struct yasl_private_signal_watch *w = l->signals;
l->signals = NULL;
for (int i = 0; i < w->count; i++) {
int signo = w->signals[i];
@@ -395,19 +395,19 @@ void yasl_loop_unwatch_signals(struct yasl_loop *l)
free(w);
}
-static void timer_cleanup_deleted(struct yasl_loop *l)
+static void timer_cleanup_deleted(struct yasl_private_loop *l)
{
if (!l || l->timer_firing_depth > 0) return;
int dest = 0;
for (int src = 0; src < l->timer_count; src++) {
- struct yasl_timer_source t = l->timers[src];
+ struct yasl_private_timer_source t = l->timers[src];
l->timers[dest] = t;
dest += !t.deleted;
}
l->timer_count = dest;
}
-bool yasl_loop_timer_armed(const struct yasl_loop *l, yasl_timer_id id)
+bool yasl_private_loop_timer_armed(const struct yasl_private_loop *l, yasl_private_timer_id id)
{
if (!l || id == 0) return false;
for (int i = 0; i < l->timer_count; i++)
@@ -416,7 +416,7 @@ bool yasl_loop_timer_armed(const struct yasl_loop *l, yasl_timer_id id)
return false;
}
-int yasl_loop_poll_set(const struct yasl_loop *l, const struct pollfd **fds)
+int yasl_private_loop_poll_set(const struct yasl_private_loop *l, const struct pollfd **fds)
{
if (!l) {
*fds = NULL;
@@ -426,10 +426,10 @@ int yasl_loop_poll_set(const struct yasl_loop *l, const struct pollfd **fds)
return l->count;
}
-int yasl_loop_next_timeout_ms(const struct yasl_loop *l)
+int yasl_private_loop_next_timeout_ms(const struct yasl_private_loop *l)
{
if (!l || l->timer_count <= 0) return -1;
- uint64_t now = yasl_loop_now_ns();
+ uint64_t now = yasl_private_loop_now_ns();
uint64_t min_deadline = UINT64_MAX;
bool has_active = false;
for (int i = 0; i < l->timer_count; i++) {
@@ -446,14 +446,14 @@ int yasl_loop_next_timeout_ms(const struct yasl_loop *l)
return (ms > (uint64_t)INT_MAX) ? INT_MAX : (int)ms;
}
-yasl_timer_id yasl_loop_add_timer(struct yasl_loop *l, uint64_t interval_ns,
- yasl_timer_cb cb, void *ud)
+yasl_private_timer_id yasl_private_loop_add_timer(struct yasl_private_loop *l, uint64_t interval_ns,
+ yasl_private_timer_cb cb, void *ud)
{
if (!l || !cb || interval_ns == 0) return 0;
if (l->timer_count >= l->timer_capacity) {
int new_cap = l->timer_capacity ? l->timer_capacity * 2 : 16;
- struct yasl_timer_source *new_timers = realloc(l->timers, (size_t)new_cap * sizeof(*new_timers));
+ struct yasl_private_timer_source *new_timers = realloc(l->timers, (size_t)new_cap * sizeof(*new_timers));
if (!new_timers) return 0;
l->timers = new_timers;
l->timer_capacity = new_cap;
@@ -462,8 +462,8 @@ yasl_timer_id yasl_loop_add_timer(struct yasl_loop *l, uint64_t interval_ns,
uint32_t id = l->next_timer_id++;
if (l->next_timer_id == 0) l->next_timer_id = 1;
- uint64_t now = yasl_loop_now_ns();
- l->timers[l->timer_count++] = (struct yasl_timer_source){
+ uint64_t now = yasl_private_loop_now_ns();
+ l->timers[l->timer_count++] = (struct yasl_private_timer_source){
.fn = cb,
.ud = ud,
.id = id,
@@ -472,11 +472,11 @@ yasl_timer_id yasl_loop_add_timer(struct yasl_loop *l, uint64_t interval_ns,
.deleted = false,
};
- yasl_loop_kick(l);
+ yasl_private_loop_kick(l);
return id;
}
-bool yasl_loop_del_timer(struct yasl_loop *l, yasl_timer_id id)
+bool yasl_private_loop_del_timer(struct yasl_private_loop *l, yasl_private_timer_id id)
{
if (!l || id == 0) return false;
for (int i = l->timer_count - 1; i >= 0; i--) {
@@ -496,11 +496,11 @@ bool yasl_loop_del_timer(struct yasl_loop *l, yasl_timer_id id)
return false;
}
-int yasl_loop_step(struct yasl_loop *l, int timeout_ms)
+int yasl_private_loop_step(struct yasl_private_loop *l, int timeout_ms)
{
if (!l) return -1;
- int timer_to = yasl_loop_next_timeout_ms(l);
+ int timer_to = yasl_private_loop_next_timeout_ms(l);
int effective_timeout = timeout_ms;
if (timer_to >= 0) {
if (effective_timeout < 0 || timer_to < effective_timeout)
@@ -528,9 +528,9 @@ int yasl_loop_step(struct yasl_loop *l, int timeout_ms)
if (l->count > 0) {
int count = l->count;
- struct yasl_fd_source sstack[32];
+ struct yasl_private_fd_source sstack[32];
struct pollfd pstack[32];
- struct yasl_fd_source *scopy = (count <= 32) ? sstack : malloc((size_t)count * sizeof(*scopy));
+ struct yasl_private_fd_source *scopy = (count <= 32) ? sstack : malloc((size_t)count * sizeof(*scopy));
struct pollfd *pcopy = (count <= 32) ? pstack : malloc((size_t)count * sizeof(*pcopy));
if (!scopy || !pcopy) {
if (scopy && scopy != sstack) free(scopy);
@@ -570,7 +570,7 @@ int yasl_loop_step(struct yasl_loop *l, int timeout_ms)
}
if (l->timer_count > 0) {
- uint64_t now = yasl_loop_now_ns();
+ uint64_t now = yasl_private_loop_now_ns();
l->timer_firing_depth++;
int initial_count = l->timer_count;
for (int i = 0; i < initial_count; i++) {
@@ -578,10 +578,10 @@ int yasl_loop_step(struct yasl_loop *l, int timeout_ms)
if (l->timers[i].deleted || l->timers[i].deadline_ns > now)
continue;
- yasl_timer_id tid = l->timers[i].id;
+ yasl_private_timer_id tid = l->timers[i].id;
uint64_t interval = l->timers[i].interval_ns;
void *ud = l->timers[i].ud;
- struct yasl_timer_source timer_src = l->timers[i];
+ struct yasl_private_timer_source timer_src = l->timers[i];
uint64_t next_interval = timer_src.fn(ud, tid, interval);
handled++;
@@ -623,18 +623,18 @@ int yasl_loop_step(struct yasl_loop *l, int timeout_ms)
return handled;
}
-int yasl_loop_run(struct yasl_loop *l)
+int yasl_private_loop_run(struct yasl_private_loop *l)
{
if (!l) return -1;
l->running = true;
while (l->running) {
- if (yasl_loop_step(l, -1) < 0)
+ if (yasl_private_loop_step(l, -1) < 0)
return -1;
}
return 0;
}
-void yasl_loop_quit(struct yasl_loop *l)
+void yasl_private_loop_quit(struct yasl_private_loop *l)
{
if (l) l->running = false;
}
diff --git a/src/platform/posix/yasl_platform_sync.h b/src/platform/posix/yasl_platform_sync.h
index f94a25d..972eef2 100644
--- a/src/platform/posix/yasl_platform_sync.h
+++ b/src/platform/posix/yasl_platform_sync.h
@@ -1,26 +1,26 @@
-#ifndef YASL_POSIX_SYNC_H
-#define YASL_POSIX_SYNC_H
+#ifndef YASL_PRIVATE_POSIX_SYNC_H
+#define YASL_PRIVATE_POSIX_SYNC_H
#include <pthread.h>
-typedef pthread_mutex_t yasl_mutex;
+typedef pthread_mutex_t yasl_private_mutex;
-static inline int yasl_mutex_init(yasl_mutex *mutex)
+static inline int yasl_private_mutex_init(yasl_private_mutex *mutex)
{
return pthread_mutex_init(mutex, NULL);
}
-static inline void yasl_mutex_destroy(yasl_mutex *mutex)
+static inline void yasl_private_mutex_destroy(yasl_private_mutex *mutex)
{
pthread_mutex_destroy(mutex);
}
-static inline void yasl_mutex_lock(yasl_mutex *mutex)
+static inline void yasl_private_mutex_lock(yasl_private_mutex *mutex)
{
pthread_mutex_lock(mutex);
}
-static inline void yasl_mutex_unlock(yasl_mutex *mutex)
+static inline void yasl_private_mutex_unlock(yasl_private_mutex *mutex)
{
pthread_mutex_unlock(mutex);
}
diff --git a/src/platform/sync.h b/src/platform/sync.h
index 43b4a34..444ff03 100644
--- a/src/platform/sync.h
+++ b/src/platform/sync.h
@@ -1,8 +1,8 @@
-#ifndef YASL_PLATFORM_SYNC_H
-#define YASL_PLATFORM_SYNC_H
+#ifndef YASL_PRIVATE_PLATFORM_SYNC_H
+#define YASL_PRIVATE_PLATFORM_SYNC_H
-/* The OS support directory selected by Meson provides yasl_mutex and the
- * yasl_mutex_init/destroy/lock/unlock operations. Init returns zero on success
+/* The OS support directory selected by Meson provides yasl_private_mutex and the
+ * yasl_private_mutex_init/destroy/lock/unlock operations. Init returns zero on success
* or an errno value on failure. The other operations require an initialized
* mutex. Native types and calls stay in the selected implementation header. */
#include "yasl_platform_sync.h" // IWYU pragma: export
diff --git a/src/platform/wayland/api.h b/src/platform/wayland/api.h
index 0f935b2..9670af1 100644
--- a/src/platform/wayland/api.h
+++ b/src/platform/wayland/api.h
@@ -1,48 +1,48 @@
-#ifndef YASL_WAYLAND_API_H
-#define YASL_WAYLAND_API_H
+#ifndef YASL_PRIVATE_WAYLAND_API_H
+#define YASL_PRIVATE_WAYLAND_API_H
-#include "yasl/yasl.h"
+#include "common/types.h"
struct wayland_display;
struct wayland_surface;
-struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec);
+struct wayland_display *wayland_display_open(struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec);
void wayland_display_close(struct wayland_display *d);
uint32_t wayland_display_caps(const struct wayland_display *d);
int wayland_display_dispatch_timeout(struct wayland_display *d, int timeout_ms);
void wayland_display_flush(struct wayland_display *d);
int wayland_display_modifiers(const struct wayland_display *d);
-void wayland_display_copy(struct wayland_display *d, enum yasl_selection sel, const char *text);
-void wayland_display_paste(struct wayland_display *d, enum yasl_selection sel, struct wayland_surface *target);
-bool wayland_display_copy_text(struct wayland_display *d, enum yasl_selection sel, const char *text);
-bool wayland_display_request_paste(struct wayland_display *d, enum yasl_selection sel,
+void wayland_display_copy(struct wayland_display *d, enum yasl_private_selection sel, const char *text);
+void wayland_display_paste(struct wayland_display *d, enum yasl_private_selection sel, struct wayland_surface *target);
+bool wayland_display_copy_text(struct wayland_display *d, enum yasl_private_selection sel, const char *text);
+bool wayland_display_request_paste(struct wayland_display *d, enum yasl_private_selection sel,
struct wayland_surface *target, uint64_t request_id);
-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);
+bool wayland_display_drag_start(struct wayland_display *d, struct wayland_surface *origin, const struct yasl_private_drag_image *image, const struct yasl_private_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_window_spec *spec, void *graphics_device);
-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);
+ const struct yasl_private_window_spec *spec, void *graphics_device);
+struct wayland_surface *wayland_surface_create_popup(struct wayland_surface *parent, const struct yasl_private_popup_spec *spec);
+bool wayland_surface_popup_reposition(struct wayland_surface *popup, const struct yasl_private_popup_spec *spec);
void wayland_surface_destroy(struct wayland_surface *s);
-void wayland_surface_set_callbacks(struct wayland_surface *s, const struct yasl_window_cbs *cbs, void *ud);
+void wayland_surface_set_callbacks(struct wayland_surface *s, const struct yasl_private_window_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_poll_event(struct wayland_surface *s, struct yasl_event *ev);
-struct yasl_geometry wayland_surface_geometry(const struct wayland_surface *s);
-struct yasl_timing wayland_surface_timing(const struct wayland_surface *s);
-void wayland_surface_set_cursor(struct wayland_surface *s, enum yasl_cursor cursor);
-bool wayland_surface_set_pointer_mode(struct wayland_surface *s, enum yasl_pointer_mode mode);
+bool wayland_surface_present(struct wayland_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage);
+bool wayland_surface_poll_event(struct wayland_surface *s, struct yasl_private_event *ev);
+struct yasl_private_geometry wayland_surface_geometry(const struct wayland_surface *s);
+struct yasl_private_timing wayland_surface_timing(const struct wayland_surface *s);
+void wayland_surface_set_cursor(struct wayland_surface *s, enum yasl_private_cursor cursor);
+bool wayland_surface_set_pointer_mode(struct wayland_surface *s, enum yasl_private_pointer_mode mode);
void wayland_surface_set_background_color(struct wayland_surface *s, uint32_t bg);
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);
+void wayland_surface_set_ime_caret(struct wayland_surface *s, struct yasl_private_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);
+bool wayland_surface_start_interactive_resize(struct wayland_surface *s, enum yasl_private_resize_edge edge);
+bool wayland_surface_show_window_menu(struct wayland_surface *s, struct yasl_private_vector2i pos);
+bool wayland_surface_drag_carry(struct wayland_surface *s, struct yasl_private_vector2i offset);
+bool wayland_surface_place_under_pointer(struct wayland_surface *s, struct yasl_private_vector2i at);
bool wayland_surface_bell(struct wayland_surface *s);
bool wayland_surface_read_pixels(struct wayland_surface *s, uint32_t *out, size_t cap,
- struct yasl_vector2i *size);
+ struct yasl_private_vector2i *size);
#endif
diff --git a/src/platform/wayland/backend.c b/src/platform/wayland/backend.c
index 7026a8c..02aae59 100644
--- a/src/platform/wayland/backend.c
+++ b/src/platform/wayland/backend.c
@@ -2,50 +2,50 @@
#include "platform/freedesktop/dbus.h"
#include <errno.h>
-static struct yasl_display *impl_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec)
+static struct yasl_private_display *impl_display_open(struct yasl_private_loop *loop, const struct yasl_private_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)
+static void impl_display_close(struct yasl_private_display *d)
{
wayland_display_close((struct wayland_display *)d);
}
-static uint32_t impl_display_caps(const struct yasl_display *d)
+static uint32_t impl_display_caps(const struct yasl_private_display *d)
{
return wayland_display_caps((const struct wayland_display *)d);
}
-static int impl_display_dispatch_timeout(struct yasl_display *d, int timeout_ms)
+static int impl_display_dispatch_timeout(struct yasl_private_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)
+static void impl_display_flush(struct yasl_private_display *d)
{
wayland_display_flush((struct wayland_display *)d);
}
-static int impl_display_modifiers(const struct yasl_display *d)
+static int impl_display_modifiers(const struct yasl_private_display *d)
{
return wayland_display_modifiers((const struct wayland_display *)d);
}
-static int impl_display_double_click_ms(const struct yasl_display *d)
+static int impl_display_double_click_ms(const struct yasl_private_display *d)
{
/* Wayland has no protocol for it; desktops publish it as a setting. */
(void)d;
return portal_double_click_ms();
}
-static void impl_display_copy(struct yasl_display *d, enum yasl_selection sel, const char *text)
+static void impl_display_copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text)
{
wayland_display_copy((struct wayland_display *)d, sel, text);
}
-static bool impl_display_copy_text(struct yasl_display *d, enum yasl_selection sel,
+static bool impl_display_copy_text(struct yasl_private_display *d, enum yasl_private_selection sel,
const char *text)
{
bool ok = wayland_display_copy_text((struct wayland_display *)d, sel, text);
@@ -53,12 +53,12 @@ static bool impl_display_copy_text(struct yasl_display *d, enum yasl_selection s
return ok;
}
-static void impl_display_paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *target)
+static void impl_display_paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *target)
{
wayland_display_paste((struct wayland_display *)d, sel, (struct wayland_surface *)target);
}
-static bool impl_display_request_paste(struct yasl_display *d, enum yasl_selection sel,
+static bool impl_display_request_paste(struct yasl_private_display *d, enum yasl_private_selection sel,
struct yasl_surface *target, uint64_t request_id)
{
bool ok = wayland_display_request_paste((struct wayland_display *)d, sel,
@@ -67,30 +67,171 @@ static bool impl_display_request_paste(struct yasl_display *d, enum yasl_selecti
return ok;
}
-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)
+static bool impl_display_drag_start(struct yasl_private_display *d, struct yasl_surface *origin, const struct yasl_private_drag_image *image, const struct yasl_private_drag_listener *l, void *ud)
{
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)
+static void impl_display_drag_cancel(struct yasl_private_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_window_spec *spec, void *graphics_device)
+static struct yasl_surface *impl_surface_create_toplevel(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *graphics_device)
{
struct wayland_surface *result = wayland_surface_create_toplevel((struct wayland_display *)d, spec, graphics_device);
return result ? &result->base : NULL;
}
-static struct yasl_surface *impl_surface_create_popup(struct yasl_surface *parent, const struct yasl_popup_spec *spec)
+static bool impl_surface_update_text_input(struct yasl_surface *s)
+{
+ return wayland_surface_update_text_input((struct wayland_surface *)s);
+}
+
+#if YASL_HAS_EGL
+static bool egl_configure(struct wayland_display *d, const struct yasl_private_gl_options *o, EGLConfig *config)
+{
+ if (!d->egl_initialized) return false;
+ EGLint renderable = o->api == YASL_PRIVATE_GL_OPENGL_ES
+ ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT;
+#ifdef EGL_OPENGL_ES_BIT
+ if (o->api == YASL_PRIVATE_GL_OPENGL_ES && o->major < 2)
+ renderable = EGL_OPENGL_ES_BIT;
+#endif
+#ifdef EGL_OPENGL_ES3_BIT_KHR
+ if (o->api == YASL_PRIVATE_GL_OPENGL_ES && o->major >= 3)
+ renderable = EGL_OPENGL_ES3_BIT_KHR;
+#endif
+ EGLint attrs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, renderable,
+ EGL_RED_SIZE, o->red_bits,
+ EGL_GREEN_SIZE, o->green_bits,
+ EGL_BLUE_SIZE, o->blue_bits,
+ EGL_ALPHA_SIZE, o->alpha_bits,
+ EGL_DEPTH_SIZE, o->depth_bits,
+ EGL_STENCIL_SIZE, o->stencil_bits,
+ EGL_SAMPLE_BUFFERS, o->samples > 0 ? 1 : 0,
+ EGL_SAMPLES, o->samples,
+ EGL_NONE,
+ };
+ EGLint api = o->api == YASL_PRIVATE_GL_OPENGL_ES ? EGL_OPENGL_ES_API : EGL_OPENGL_API;
+ if (d->egl_api && d->egl_api != api) return false;
+ EGLint count = 0;
+ if (!eglChooseConfig(d->egl_display, attrs, config, 1, &count) || count < 1)
+ return false;
+ d->egl_api = api;
+ return eglBindAPI(api) == EGL_TRUE;
+}
+
+static struct yasl_surface *impl_surface_create_gl(struct yasl_private_display *display,
+ const struct yasl_private_window_spec *spec, const int *attributes, struct yasl_surface *share)
+{
+ struct wayland_display *d = (struct wayland_display *)display;
+ struct yasl_private_gl_options o;
+ if (!spec || !yasl_private_gl_parse_options(attributes, &o) || !d || !d->egl_initialized)
+ return NULL;
+ struct yasl_private_window_spec gl_spec = *spec;
+ gl_spec.external_gpu = true;
+ gl_spec.flags &= ~YASL_PRIVATE_SURFACE_CPU;
+ struct wayland_surface *w = wayland_surface_create_toplevel(d, &gl_spec, NULL);
+ if (!w) return NULL;
+ if (!egl_configure(d, &o, &w->egl_config)) goto fail;
+ EGLContext share_context = share && share->display == display
+ ? ((struct wayland_surface *)share)->egl_context : EGL_NO_CONTEXT;
+ if (share && share_context == EGL_NO_CONTEXT) goto fail;
+ w->egl_window = wl_egl_window_create(w->surface, w->width, w->height);
+ if (!w->egl_window) goto fail;
+ EGLint context_attrs[12];
+ int n = 0;
+ if (o.api == YASL_PRIVATE_GL_OPENGL_ES) {
+ context_attrs[n++] = EGL_CONTEXT_CLIENT_VERSION;
+ context_attrs[n++] = o.major;
+ } else {
+ context_attrs[n++] = EGL_CONTEXT_MAJOR_VERSION_KHR;
+ context_attrs[n++] = o.major;
+ context_attrs[n++] = EGL_CONTEXT_MINOR_VERSION_KHR;
+ context_attrs[n++] = o.minor;
+ if (o.profile != YASL_PRIVATE_GL_ANY) {
+ context_attrs[n++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
+ context_attrs[n++] = o.profile == YASL_PRIVATE_GL_CORE
+ ? EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR
+ : EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR;
+ }
+ EGLint flags = (o.debug ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0) |
+ (o.forward_compat ? EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR : 0);
+ if (flags) {
+ context_attrs[n++] = EGL_CONTEXT_FLAGS_KHR;
+ context_attrs[n++] = flags;
+ }
+ }
+ context_attrs[n] = EGL_NONE;
+ w->egl_context = eglCreateContext(d->egl_display, w->egl_config, share_context, context_attrs);
+ if (w->egl_context == EGL_NO_CONTEXT) goto fail;
+ EGLint surface_attrs[] = {
+#ifdef EGL_GL_COLORSPACE_KHR
+ EGL_GL_COLORSPACE_KHR, o.srgb ? EGL_GL_COLORSPACE_SRGB_KHR : EGL_GL_COLORSPACE_LINEAR_KHR,
+#endif
+ EGL_NONE,
+ };
+ w->egl_surface = eglCreateWindowSurface(d->egl_display, w->egl_config,
+ (EGLNativeWindowType)w->egl_window, surface_attrs);
+ if (w->egl_surface == EGL_NO_SURFACE) { w->egl_surface = 0; goto fail; }
+ w->base.presenter = YASL_PRIVATE_PRESENTER_GL;
+ return &w->base;
+fail:
+ wayland_surface_destroy(w);
+ return NULL;
+}
+
+static bool impl_gl_make_current(struct yasl_private_display *display, struct yasl_surface *surface)
+{
+ struct wayland_display *d = (struct wayland_display *)display;
+ struct wayland_surface *w = (struct wayland_surface *)surface;
+ if (!d) return false;
+ if (!w)
+ return eglMakeCurrent(d->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_TRUE;
+ return w->conn == d && w->base.presenter == YASL_PRIVATE_PRESENTER_GL &&
+ eglMakeCurrent(d->egl_display, w->egl_surface, w->egl_surface, w->egl_context) == EGL_TRUE;
+}
+
+static yasl_private_gl_proc impl_gl_get_proc_address(struct yasl_private_display *display, const char *name)
+{
+ (void)display;
+ return name ? (yasl_private_gl_proc)eglGetProcAddress(name) : NULL;
+}
+
+static bool impl_gl_swap_interval(struct yasl_private_display *display, struct yasl_surface *surface, int interval)
+{
+ struct wayland_display *d = (struct wayland_display *)display;
+ struct wayland_surface *w = (struct wayland_surface *)surface;
+ return d && w && w->conn == d && w->base.presenter == YASL_PRIVATE_PRESENTER_GL &&
+ impl_gl_make_current(display, surface) && eglSwapInterval(d->egl_display, interval) == EGL_TRUE;
+}
+
+static bool impl_gl_swap_buffers(struct yasl_private_display *display, struct yasl_surface *surface)
+{
+ struct wayland_display *d = (struct wayland_display *)display;
+ struct wayland_surface *w = (struct wayland_surface *)surface;
+ if (!d || !w || w->conn != d || w->closed || !w->configured ||
+ w->base.presenter != YASL_PRIVATE_PRESENTER_GL)
+ return false;
+ if (!impl_gl_make_current(display, surface)) return false;
+ wayland_surface_arm_frame(w);
+ surface_request_presentation_feedback(w);
+ if (!eglSwapBuffers(d->egl_display, w->egl_surface)) return false;
+ return true;
+}
+#endif
+
+static struct yasl_surface *impl_surface_create_popup(struct yasl_surface *parent, const struct yasl_private_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)
+static bool impl_surface_popup_reposition(struct yasl_surface *popup, const struct yasl_private_popup_spec *spec)
{
return wayland_surface_popup_reposition((struct wayland_surface *)popup, spec);
}
@@ -106,7 +247,7 @@ static struct input_queue *impl_surface_input_queue(struct yasl_surface *s)
return w ? &w->input : NULL;
}
-static bool impl_surface_set_size(struct yasl_surface *s, struct yasl_vector2i size)
+static bool impl_surface_set_size(struct yasl_surface *s, struct yasl_private_vector2i size)
{
struct wayland_surface *w = (struct wayland_surface *)s;
if (!w || w->closed || w->destroyed || !w->toplevel ||
@@ -114,8 +255,19 @@ static bool impl_surface_set_size(struct yasl_surface *s, struct yasl_vector2i s
errno = EINVAL;
return false;
}
- errno = ENOTSUP;
- return false;
+ /* Floating windows choose their own size; constrained states keep it for later. */
+ w->floating_width = size.x;
+ w->floating_height = size.y;
+ if (w->base.state & (YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_TILED))
+ return true;
+ w->req_width = w->req_height = 0;
+ w->size_pending = true;
+ if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU && w->configured && apply_pending_geometry(w) < 0) {
+ errno = EIO;
+ return false;
+ }
+ wayland_surface_schedule_redraw(w);
+ return true;
}
static bool impl_surface_set_fullscreen(struct yasl_surface *s, bool enabled)
@@ -153,14 +305,14 @@ static bool impl_surface_minimize(struct yasl_surface *s)
return true;
}
-static struct yasl_display *impl_surface_display(const struct yasl_surface *s)
+static struct yasl_private_display *impl_surface_display(const struct yasl_surface *s)
{
const struct wayland_surface *w = (const struct wayland_surface *)s;
struct wayland_display *result = w ? w->conn : NULL;
return result ? &result->base : NULL;
}
-static void impl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_window_cbs *cbs, void *ud)
+static void impl_surface_set_callbacks(struct yasl_surface *s, const struct yasl_private_window_cbs *cbs, void *ud)
{
wayland_surface_set_callbacks((struct wayland_surface *)s, cbs, ud);
}
@@ -175,37 +327,37 @@ 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)
+static bool impl_surface_present(struct yasl_surface *s, const struct yasl_private_framebuffer *fb, const struct yasl_private_damage_list *damage)
{
return wayland_surface_present((struct wayland_surface *)s, fb, damage);
}
-static bool impl_surface_poll_event(struct yasl_surface *s, struct yasl_event *ev)
+static bool impl_surface_poll_event(struct yasl_surface *s, struct yasl_private_event *ev)
{
return wayland_surface_poll_event((struct wayland_surface *)s, ev);
}
-static struct yasl_geometry impl_surface_geometry(const struct yasl_surface *s)
+static struct yasl_private_geometry impl_surface_geometry(const struct yasl_surface *s)
{
return wayland_surface_geometry((const struct wayland_surface *)s);
}
-static struct yasl_timing impl_surface_timing(const struct yasl_surface *s)
+static struct yasl_private_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 yasl_clock_frame_timestamp_ns(&((struct wayland_surface *)s)->clock, now);
+ return yasl_private_clock_frame_timestamp_ns(&((struct wayland_surface *)s)->clock, now);
}
-static void impl_surface_set_cursor(struct yasl_surface *s, enum yasl_cursor cursor)
+static void impl_surface_set_cursor(struct yasl_surface *s, enum yasl_private_cursor cursor)
{
wayland_surface_set_cursor((struct wayland_surface *)s, cursor);
}
-static bool impl_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_pointer_mode mode)
+static bool impl_surface_set_pointer_mode(struct yasl_surface *s, enum yasl_private_pointer_mode mode)
{
return wayland_surface_set_pointer_mode((struct wayland_surface *)s, mode);
}
@@ -226,7 +378,7 @@ static bool impl_surface_ime_preedit_cursor(const struct yasl_surface *s, int *b
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)
+static void impl_surface_set_ime_caret(struct yasl_surface *s, struct yasl_private_rect2i caret)
{
wayland_surface_set_ime_caret((struct wayland_surface *)s, caret);
}
@@ -236,28 +388,28 @@ 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)
+static bool impl_surface_start_interactive_resize(struct yasl_surface *s, enum yasl_private_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)
+static bool impl_surface_show_window_menu(struct yasl_surface *s, struct yasl_private_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)
+static bool impl_surface_drag_carry(struct yasl_surface *s, struct yasl_private_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)
+static bool impl_surface_place_under_pointer(struct yasl_surface *s, struct yasl_private_vector2i at)
{
return wayland_surface_place_under_pointer((struct wayland_surface *)s, at);
}
static bool impl_surface_read_pixels(struct yasl_surface *s, uint32_t *out, size_t cap,
- struct yasl_vector2i *size)
+ struct yasl_private_vector2i *size)
{
return wayland_surface_read_pixels((struct wayland_surface *)s, out, cap, size);
}
@@ -267,7 +419,7 @@ static bool impl_surface_bell(struct yasl_surface *s)
return wayland_surface_bell((struct wayland_surface *)s);
}
-static void *impl_display_get_native_handle(struct yasl_display *d)
+static void *impl_display_get_native_handle(struct yasl_private_display *d)
{
struct wayland_display *w = (struct wayland_display *)d;
return w ? w->wl : NULL;
@@ -279,8 +431,16 @@ static void *impl_surface_get_native_handle(struct yasl_surface *s)
return w ? w->surface : NULL;
}
-const struct yasl_backend_ops yasl_wayland_backend = {
- .type = YASL_BACKEND_WAYLAND,
+const struct yasl_private_backend_ops yasl_private_wayland_backend = {
+#if YASL_HAS_EGL
+ .surface_create_gl = impl_surface_create_gl,
+ .gl_make_current = impl_gl_make_current,
+ .gl_get_proc_address = impl_gl_get_proc_address,
+ .gl_swap_interval = impl_gl_swap_interval,
+ .gl_swap_buffers = impl_gl_swap_buffers,
+#endif
+ .surface_update_text_input = impl_surface_update_text_input,
+ .type = YASL_PRIVATE_BACKEND_WAYLAND,
.display_open = impl_display_open,
.display_close = impl_display_close,
.display_caps = impl_display_caps,
diff --git a/src/platform/wayland/buffer.c b/src/platform/wayland/buffer.c
index 874cc76..bbc055d 100644
--- a/src/platform/wayland/buffer.c
+++ b/src/platform/wayland/buffer.c
@@ -24,7 +24,7 @@ static const struct wl_buffer_listener buffer_listener;
int device_px(int logical, uint32_t scale_120)
{
- int64_t px = ((int64_t)logical * scale_120 + YASL_SCALE_UNIT / 2) / YASL_SCALE_UNIT;
+ int64_t px = ((int64_t)logical * scale_120 + YASL_PRIVATE_SCALE_UNIT / 2) / YASL_PRIVATE_SCALE_UNIT;
return px > 0 && px <= INT_MAX ? (int)px : -1;
}
@@ -58,7 +58,7 @@ int buffer_reserve(struct wayland_surface *w, struct buffer *b, size_t needed)
if (b->busy || needed > INT32_MAX) return -1;
size_t capacity = b->capacity <= INT32_MAX / 2 ? b->capacity * 2 : INT32_MAX;
if (capacity < needed) capacity = needed;
- int fd = memfd_create("yasl_surface_buffer", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ int fd = memfd_create("yasl_private_surface_buffer", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (fd < 0) return -1;
if (!fd_truncate(fd, capacity) ||
fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL) < 0) {
@@ -89,7 +89,7 @@ int buffer_prepare(struct wayland_surface *w, struct buffer *b, int width, int h
{
if (b->proxy && !b->busy && b->width == width && b->height == height) return 0;
long faults_before = minor_faults();
- uint64_t t0 = yasl_loop_now_ns();
+ uint64_t t0 = yasl_private_loop_now_ns();
size_t bytes;
if (!buffer_size(width, height, &bytes) || buffer_reserve(w, b, bytes) < 0)
return -1;
@@ -104,7 +104,7 @@ int buffer_prepare(struct wayland_surface *w, struct buffer *b, int width, int h
b->busy = false;
b->presented = false;
wl_buffer_add_listener(proxy, listener, b);
- yasl_clock_note_resize(&w->clock, yasl_loop_now_ns() - t0);
+ yasl_private_clock_note_resize(&w->clock, yasl_private_loop_now_ns() - t0);
long faults = minor_faults() - faults_before;
if (faults > 0) w->clock.resize_minflt += faults;
return 0;
@@ -114,10 +114,10 @@ static void publish_surface_scale(struct wayland_surface *w)
{
if (w->viewport) {
wp_viewport_set_destination(w->viewport, w->logical_width, w->logical_height);
- } else if (w->scale_120 % YASL_SCALE_UNIT == 0 &&
+ } else if (w->scale_120 % YASL_PRIVATE_SCALE_UNIT == 0 &&
wl_proxy_get_version((struct wl_proxy *)w->surface) >=
WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) {
- wl_surface_set_buffer_scale(w->surface, (int32_t)(w->scale_120 / YASL_SCALE_UNIT));
+ wl_surface_set_buffer_scale(w->surface, (int32_t)(w->scale_120 / YASL_PRIVATE_SCALE_UNIT));
}
}
@@ -137,16 +137,16 @@ int apply_pending_geometry(struct wayland_surface *w)
return 0;
}
bool floating = !w->parent && !(w->base.state &
- (YASL_STATE_MAXIMIZED | YASL_STATE_FULLSCREEN | YASL_STATE_TILED));
+ (YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_TILED));
int free_w = floating && w->floating_width > 0 ? w->floating_width : w->logical_width;
int free_h = floating && w->floating_height > 0 ? w->floating_height : w->logical_height;
int logical_w = !w->parent && w->req_width > 0 ? w->req_width : free_w;
int logical_h = !w->parent && w->req_height > 0 ? w->req_height : free_h;
if (!w->parent) {
- if (logical_w < YASL_MIN_LOGICAL_PX) logical_w = YASL_MIN_LOGICAL_PX;
- if (logical_h < YASL_MIN_LOGICAL_PX) logical_h = YASL_MIN_LOGICAL_PX;
+ if (logical_w < YASL_PRIVATE_MIN_LOGICAL_PX) logical_w = YASL_PRIVATE_MIN_LOGICAL_PX;
+ if (logical_h < YASL_PRIVATE_MIN_LOGICAL_PX) logical_h = YASL_PRIVATE_MIN_LOGICAL_PX;
}
- if (!(w->base.state & YASL_STATE_FULLSCREEN)) {
+ if (!(w->base.state & YASL_PRIVATE_STATE_FULLSCREEN)) {
if (w->bound_width > 0 && logical_w > w->bound_width)
logical_w = w->bound_width;
if (w->bound_height > 0 && logical_h > w->bound_height)
@@ -155,7 +155,9 @@ int apply_pending_geometry(struct wayland_surface *w)
int width = device_px(logical_w, w->pending_scale_120);
int height = device_px(logical_h, w->pending_scale_120);
size_t bytes;
- if (!buffer_size(width, height, &bytes)) {
+ if (width <= 0 || height <= 0 ||
+ (w->base.presenter == YASL_PRIVATE_PRESENTER_CPU &&
+ !buffer_size(width, height, &bytes))) {
return -1;
}
bool rescaled = w->scale_120 != w->pending_scale_120;
@@ -169,13 +171,17 @@ int apply_pending_geometry(struct wayland_surface *w)
return 0;
w->scale_120 = w->pending_scale_120;
- w->scale_factor = (double)w->scale_120 / YASL_SCALE_UNIT;
+ w->scale_factor = (double)w->scale_120 / YASL_PRIVATE_SCALE_UNIT;
w->width = width;
w->height = height;
- w->base.size = (struct yasl_vector2i){width, height};
+ w->base.size = (struct yasl_private_vector2i){width, height};
w->base.scale_120 = w->scale_120;
w->logical_width = logical_w;
w->logical_height = logical_h;
+#if YASL_HAS_EGL
+ if (w->egl_window && (resized || rescaled))
+ wl_egl_window_resize(w->egl_window, width, height, 0, 0);
+#endif
surface_update_opaque_region(w);
if (resized || rescaled) {
for (int i = 0; i < NBUF; i++)
@@ -183,7 +189,7 @@ int apply_pending_geometry(struct wayland_surface *w)
}
publish_surface_scale(w);
if (resized || rescaled) {
- yasl_surface_notify_configure(&w->base, (struct yasl_geometry){
+ yasl_private_surface_notify_configure(&w->base, (struct yasl_private_geometry){
.size = { .x = width, .y = height }, .scale_120 = w->scale_120,
});
if (w->closed || w->destroyed)
@@ -225,7 +231,7 @@ void surface_follow_outputs(struct wayland_surface *w)
w->clock.refresh_ns = refresh;
if (need_scale)
- surface_request_scale(w, (uint32_t)max_scale * YASL_SCALE_UNIT);
+ surface_request_scale(w, (uint32_t)max_scale * YASL_PRIVATE_SCALE_UNIT);
}
static void surface_enter(void *data, struct wl_surface *surface, struct wl_output *output)
@@ -250,7 +256,7 @@ static void surface_preferred_scale(void *data, struct wl_surface *surface, int3
struct wayland_surface *w = data;
if (w->frac_scale || w->parent || factor <= 0)
return;
- surface_request_scale(w, (uint32_t)factor * YASL_SCALE_UNIT);
+ surface_request_scale(w, (uint32_t)factor * YASL_PRIVATE_SCALE_UNIT);
}
static void surface_preferred_transform(void *data, struct wl_surface *surface,
@@ -285,13 +291,15 @@ static void frame_done(void *data, struct wl_callback *cb, uint32_t ms)
wl_callback_destroy(cb);
w->frame_cb = NULL;
- uint64_t now = yasl_loop_now_ns();
+ uint64_t now = yasl_private_loop_now_ns();
if (!w->conn->pres_clock_known) {
- yasl_clock_presented(&w->clock, yasl_clock_extend_ms(ms, now), w->sample_refresh);
- w->clock.source = YASL_TIMING_FRAME_CALLBACK;
+ yasl_private_clock_presented(&w->clock, yasl_private_clock_extend_ms(ms, now), w->sample_refresh);
+ w->clock.source = YASL_PRIVATE_TIMING_FRAME_CALLBACK;
}
- if (w->want_draw)
+ if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU)
+ yasl_private_surface_notify_refresh(&w->base);
+ else if (w->want_draw)
wayland_surface_schedule_redraw(w);
}
@@ -299,14 +307,14 @@ static const struct wl_callback_listener frame_listener = {
.done = frame_done,
};
-static void arm_frame(struct wayland_surface *w)
+void wayland_surface_arm_frame(struct wayland_surface *w)
{
if (!w->configured || w->closed || w->frame_cb)
return;
w->frame_cb = wl_surface_frame(w->surface);
if (!w->frame_cb) { surface_fail(w); return; }
w->sample_refresh = w->clock.last_present_ns &&
- yasl_loop_now_ns() - w->clock.last_present_ns <
+ yasl_private_loop_now_ns() - w->clock.last_present_ns <
(uint64_t)(w->clock.refresh_ns / 2);
if (wl_callback_add_listener(w->frame_cb, &frame_listener, w) < 0) {
wl_callback_destroy(w->frame_cb);
@@ -365,7 +373,7 @@ static void presented(void *data, struct wp_presentation_feedback *fb,
(((uint64_t)tv_sec_hi << 32) | tv_sec_lo) * 1000000000ull + tv_nsec);
if (refresh) w->clock.refresh_ns = refresh;
w->clock.last_present_ns = ns;
- w->clock.source = YASL_TIMING_PRESENTATION;
+ w->clock.source = YASL_PRIVATE_TIMING_PRESENTATION;
}
(void)fb;
feedback_destroy(feedback);
@@ -419,7 +427,7 @@ static void commit_buffer(struct wayland_surface *w, int idx)
{
wl_surface_attach(w->surface, w->buffers[idx].proxy, 0, 0);
- const struct yasl_damage_list *d = &w->damage;
+ const struct yasl_private_damage_list *d = &w->damage;
if (d->n < 0 || !w->buffers[idx].presented)
wl_surface_damage_buffer(w->surface, 0, 0, w->width, w->height);
else
@@ -438,8 +446,8 @@ static void commit_buffer(struct wayland_surface *w, int idx)
wp_fifo_v1_set_barrier(w->fifo);
}
if (w->commit_timer && w->conn->pres_clock_known) {
- uint64_t now = yasl_loop_now_ns();
- uint64_t next = yasl_clock_next_present_ns(&w->clock);
+ uint64_t now = yasl_private_loop_now_ns();
+ uint64_t next = yasl_private_clock_next_present_ns(&w->clock);
bool soon = next > now && next - now < (uint64_t)(w->clock.refresh_ns * 1.5);
next = conn_mono_to_pres(w->conn, next);
if (soon)
@@ -458,10 +466,11 @@ static void commit_buffer(struct wayland_surface *w, int idx)
w->buffers[idx].presented = true;
}
-bool wayland_surface_present(struct wayland_surface *w, const struct yasl_framebuffer *fb,
- const struct yasl_damage_list *damage)
+bool wayland_surface_present(struct wayland_surface *w, const struct yasl_private_framebuffer *fb,
+ const struct yasl_private_damage_list *damage)
{
- if (!w || w->closed || !w->frame_active || w->frame_presented || !fb ||
+ if (!w || w->base.presenter != YASL_PRIVATE_PRESENTER_CPU ||
+ w->closed || !w->frame_active || w->frame_presented || !fb ||
!damage || fb->pixels != w->frame.target.pixels ||
fb->size.x != w->width || fb->size.y != w->height)
return false;
@@ -470,7 +479,7 @@ bool wayland_surface_present(struct wayland_surface *w, const struct yasl_frameb
w->damage.bounds = damage->bounds;
if (damage->n > 0)
memcpy(w->damage.rects, damage->rects, (size_t)damage->n * sizeof(damage->rects[0]));
- arm_frame(w);
+ wayland_surface_arm_frame(w);
if (w->closed)
return false;
commit_buffer(w, w->cur);
@@ -481,17 +490,17 @@ bool wayland_surface_present(struct wayland_surface *w, const struct yasl_frameb
}
bool wayland_surface_read_pixels(struct wayland_surface *w, uint32_t *out, size_t cap,
- struct yasl_vector2i *size)
+ struct yasl_private_vector2i *size)
{
if (!w || w->closed || w->frame_active || !w->has_shown) return false;
const struct buffer *b = &w->buffers[w->shown];
if (!b->pixels) return false;
- return yasl_copy_frame(b->pixels, (struct yasl_vector2i){b->width, b->height}, out, cap, size);
+ return yasl_private_copy_frame(b->pixels, (struct yasl_private_vector2i){b->width, b->height}, out, cap, size);
}
void surface_draw_frame(struct wayland_surface *w)
{
- if (w->closed || !w->configured)
+ if (w->closed || !w->configured || w->base.presenter != YASL_PRIVATE_PRESENTER_CPU)
return;
if (apply_pending_geometry(w) < 0) {
surface_fail(w);
@@ -517,18 +526,18 @@ void surface_draw_frame(struct wayland_surface *w)
if (w->parent)
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){
+ w->damage = (struct yasl_private_damage_list){0};
+ w->frame = (struct yasl_private_frame){
.surface = &w->base,
.target = {
- .generation = yasl_frame_begin(&w->base),
+ .generation = yasl_private_frame_begin(&w->base),
.pixels = w->buffers[idx].pixels,
.size = { .x = w->width, .y = w->height },
},
};
w->frame_active = true;
w->frame_presented = false;
- yasl_surface_draw(&w->base, &w->frame);
+ yasl_private_surface_draw(&w->base, &w->frame);
if (w->closed || w->destroyed)
return;
w->frame_active = false;
@@ -555,21 +564,21 @@ int surface_init_scaling(struct wayland_surface *w)
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);
+ char *copy = yasl_private_utf8_sanitize_dup(title);
if (!copy) return;
xdg_toplevel_set_title(w->toplevel, copy);
free(copy);
}
-struct yasl_timing wayland_surface_timing(const struct wayland_surface *w)
+struct yasl_private_timing wayland_surface_timing(const struct wayland_surface *w)
{
- struct yasl_timing t = {
+ struct yasl_private_timing t = {
.refresh_ns = w->clock.refresh_ns,
.last_present_ns = w->clock.last_present_ns,
- .next_present_ns = yasl_clock_next_present_ns(&w->clock),
+ .next_present_ns = yasl_private_clock_next_present_ns(&w->clock),
.frames_skipped = w->frames_skipped,
.source = w->clock.source,
};
- yasl_clock_resize_stats(&w->clock, &t.resize);
+ yasl_private_clock_resize_stats(&w->clock, &t.resize);
return t;
}
diff --git a/src/platform/wayland/clipboard.c b/src/platform/wayland/clipboard.c
index 8ca9b93..cf39b6c 100644
--- a/src/platform/wayland/clipboard.c
+++ b/src/platform/wayland/clipboard.c
@@ -25,7 +25,7 @@ struct transfer {
struct offer *drop;
struct wl_surface *drop_surface;
int fd, id;
- yasl_timer_id timeout;
+ yasl_private_timer_id timeout;
char *text;
size_t len, offset, capacity;
uint64_t request_id;
@@ -48,7 +48,7 @@ struct clipboard {
struct offer *drag_offer;
struct primary_offer *primary_selection;
struct primary_offer *primary_pending_offers;
- struct yasl_loop *loop;
+ struct yasl_private_loop *loop;
struct transfer *transfers;
char *text;
char *primary_text;
@@ -70,7 +70,7 @@ const char *clipboard_drag_mime(void)
return mime;
}
-static void drag_event(struct clipboard *c, enum yasl_drag_event ev,
+static void drag_event(struct clipboard *c, enum yasl_private_drag_event ev,
wl_fixed_t x, wl_fixed_t y)
{
if (c->drag)
@@ -78,7 +78,7 @@ static void drag_event(struct clipboard *c, enum yasl_drag_event ev,
c->drag_ud);
}
-static void drop_event(struct clipboard *c, enum yasl_drop_event ev)
+static void drop_event(struct clipboard *c, enum yasl_private_drop_event ev)
{
if (c->drop && c->drag_surface)
c->drop(ev, c->drag_surface, wl_fixed_to_double(c->drag_x),
@@ -89,7 +89,7 @@ static void free_offer(struct offer *o);
static void transfer_end(struct transfer *t, bool complete);
-static uint64_t transfer_timeout(void *ud, yasl_timer_id id, uint64_t interval)
+static uint64_t transfer_timeout(void *ud, yasl_private_timer_id id, uint64_t interval)
{
(void)interval;
struct transfer *t = ud;
@@ -106,9 +106,9 @@ static void transfer_end(struct transfer *t, bool complete)
struct transfer **p = &owner->transfers;
while (*p && *p != t) p = &(*p)->next;
if (*p) *p = t->next;
- yasl_loop_del_fd(owner->loop, t->id);
+ yasl_private_loop_del_fd(owner->loop, t->id);
if (t->timeout)
- yasl_loop_del_timer(owner->loop, t->timeout);
+ yasl_private_loop_del_timer(owner->loop, t->timeout);
close(t->fd);
if (t->drop) {
if (complete && wl_data_offer_get_version(t->drop->proxy) >=
@@ -124,7 +124,7 @@ static void transfer_end(struct transfer *t, bool complete)
struct wl_surface *surface = t->drop_surface;
uint64_t request_id = t->request_id;
bool uri_list = t->uri_list;
- enum yasl_transfer_status status = complete ? YASL_TRANSFER_OK : YASL_TRANSFER_FAILED;
+ enum yasl_private_transfer_status status = complete ? YASL_PRIVATE_TRANSFER_OK : YASL_PRIVATE_TRANSFER_FAILED;
const char *text = complete ? (t->text ? t->text : "") : NULL;
size_t len = complete ? t->len : 0;
if (notify)
@@ -156,7 +156,7 @@ static int transfer_io(int fd, short events, void *ud)
if (n > 0) {
t->len += (size_t)n;
} else if (n == 0) {
- bool valid = yasl_utf8_validate(t->text, t->len);
+ bool valid = yasl_private_utf8_validate(t->text, t->len);
transfer_end(t, valid);
} else if (errno != EAGAIN && errno != EINTR) transfer_end(t, false);
}
@@ -176,12 +176,12 @@ static struct transfer *start_transfer_text(struct clipboard *c, int fd, bool se
if (!t->text) { close(fd); free(t); return NULL; }
t->len = strlen(t->text);
}
- t->id = yasl_loop_add_fd(c->loop, fd, sending ? POLLOUT : POLLIN, transfer_io, t);
+ t->id = yasl_private_loop_add_fd(c->loop, fd, sending ? POLLOUT : POLLIN, transfer_io, t);
if (t->id < 0) { close(fd); free(t->text); free(t); return NULL; }
- t->timeout = yasl_loop_add_timer(c->loop, CLIPBOARD_TRANSFER_TIMEOUT_NS,
+ t->timeout = yasl_private_loop_add_timer(c->loop, CLIPBOARD_TRANSFER_TIMEOUT_NS,
transfer_timeout, t);
if (!t->timeout) {
- yasl_loop_del_fd(c->loop, t->id);
+ yasl_private_loop_del_fd(c->loop, t->id);
close(fd);
free(t->text);
free(t);
@@ -311,9 +311,9 @@ static void drag_offer_enter(void *ud, struct wl_data_device *device, uint32_t s
c->drag_y = y;
c->drag_surface = surface;
if (o && o->tab)
- drag_event(c, YASL_DRAG_OVER, x, y);
+ drag_event(c, YASL_PRIVATE_DRAG_OVER, x, y);
else if (o && !o->tab && (o->uri_list || o->mime))
- drop_event(c, YASL_DROP_OVER);
+ drop_event(c, YASL_PRIVATE_DROP_OVER);
}
}
static void drag_offer_clear(void *ud, struct wl_data_device *device)
@@ -321,10 +321,10 @@ static void drag_offer_clear(void *ud, struct wl_data_device *device)
(void)device;
struct clipboard *c = ud;
if (c->drag_offer && c->drag_offer->tab)
- drag_event(c, YASL_DRAG_LEAVE, c->drag_x, c->drag_y);
+ drag_event(c, YASL_PRIVATE_DRAG_LEAVE, c->drag_x, c->drag_y);
else if (c->drag_offer && !c->drag_offer->tab &&
(c->drag_offer->uri_list || c->drag_offer->mime))
- drop_event(c, YASL_DROP_LEAVE);
+ drop_event(c, YASL_PRIVATE_DROP_LEAVE);
free_offer(c->drag_offer);
c->drag_offer = NULL;
c->drag_surface = NULL;
@@ -336,10 +336,10 @@ static void drag_motion(void *ud, struct wl_data_device *device, uint32_t time,
c->drag_x = x;
c->drag_y = y;
if (c->drag_offer && c->drag_offer->tab)
- drag_event(c, YASL_DRAG_OVER, x, y);
+ drag_event(c, YASL_PRIVATE_DRAG_OVER, x, y);
else if (c->drag_offer && !c->drag_offer->tab &&
(c->drag_offer->uri_list || c->drag_offer->mime))
- drop_event(c, YASL_DROP_OVER);
+ drop_event(c, YASL_PRIVATE_DROP_OVER);
}
static void drag_drop(void *ud, struct wl_data_device *device)
{
@@ -350,11 +350,11 @@ static void drag_drop(void *ud, struct wl_data_device *device)
c->drag_offer = NULL;
bool v3 = wl_data_offer_get_version(o->proxy) >= 3;
if (o->tab) {
- drag_event(c, YASL_DRAG_DROP, c->drag_x, c->drag_y);
+ drag_event(c, YASL_PRIVATE_DRAG_DROP, c->drag_x, c->drag_y);
if (v3)
wl_data_offer_finish(o->proxy);
} else if (o && !o->tab && (o->uri_list || o->mime)) {
- drop_event(c, YASL_DROP_LEAVE);
+ drop_event(c, YASL_PRIVATE_DROP_LEAVE);
int fds[2];
if (pipe2(fds, O_CLOEXEC) == 0) {
wl_data_offer_receive(o->proxy, o->uri_list ? "text/uri-list" : o->mime, fds[1]);
@@ -522,7 +522,7 @@ struct wl_data_device *clipboard_data_device(const struct clipboard *c)
}
struct clipboard *clipboard_create(struct wl_display *display, struct wl_data_device_manager *manager,
- struct wl_seat *seat, struct yasl_loop *loop,
+ struct wl_seat *seat, struct yasl_private_loop *loop,
clipboard_receive_fn receive, void *ud)
{
if (!manager || !seat) return NULL;
@@ -603,7 +603,7 @@ bool clipboard_copy(struct clipboard *c, const char *text, uint32_t serial)
void clipboard_paste(struct clipboard *c, struct wl_surface *target)
{
if (c && target)
- (void)clipboard_request_paste(c, YASL_CLIPBOARD, target, 0);
+ (void)clipboard_request_paste(c, YASL_PRIVATE_CLIPBOARD, target, 0);
}
bool clipboard_copy_primary(struct clipboard *c, const char *text, uint32_t serial)
@@ -630,24 +630,24 @@ bool clipboard_copy_primary(struct clipboard *c, const char *text, uint32_t seri
void clipboard_paste_primary(struct clipboard *c, struct wl_surface *target)
{
if (c && target)
- (void)clipboard_request_paste(c, YASL_PRIMARY, target, 0);
+ (void)clipboard_request_paste(c, YASL_PRIVATE_PRIMARY, target, 0);
}
-bool clipboard_request_paste(struct clipboard *c, enum yasl_selection selection,
+bool clipboard_request_paste(struct clipboard *c, enum yasl_private_selection selection,
struct wl_surface *target, uint64_t request_id)
{
if (!c || !target || !c->receive)
return false;
int fds[2];
- if (selection == YASL_PRIMARY) {
+ if (selection == YASL_PRIVATE_PRIMARY) {
if (!c->primary_device || !c->primary_selection || !c->primary_selection->mime) {
c->receive(NULL, 0, false, target, request_id,
- YASL_TRANSFER_UNAVAILABLE, c->ud);
+ YASL_PRIVATE_TRANSFER_UNAVAILABLE, c->ud);
return true;
}
if (pipe2(fds, O_CLOEXEC) < 0) {
c->receive(NULL, 0, false, target, request_id,
- YASL_TRANSFER_FAILED, c->ud);
+ YASL_PRIVATE_TRANSFER_FAILED, c->ud);
return true;
}
zwp_primary_selection_offer_v1_receive(c->primary_selection->proxy,
@@ -656,12 +656,12 @@ bool clipboard_request_paste(struct clipboard *c, enum yasl_selection selection,
} else {
if (!c->device || !c->selection || !c->selection->mime) {
c->receive(NULL, 0, false, target, request_id,
- YASL_TRANSFER_UNAVAILABLE, c->ud);
+ YASL_PRIVATE_TRANSFER_UNAVAILABLE, c->ud);
return true;
}
if (pipe2(fds, O_CLOEXEC) < 0) {
c->receive(NULL, 0, false, target, request_id,
- YASL_TRANSFER_FAILED, c->ud);
+ YASL_PRIVATE_TRANSFER_FAILED, c->ud);
return true;
}
wl_data_offer_receive(c->selection->proxy, c->selection->mime, fds[1]);
@@ -670,7 +670,7 @@ bool clipboard_request_paste(struct clipboard *c, enum yasl_selection selection,
struct transfer *transfer = start_transfer_paste(c, fds[0], target, request_id);
if (!transfer) {
c->receive(NULL, 0, false, target, request_id,
- YASL_TRANSFER_FAILED, c->ud);
+ YASL_PRIVATE_TRANSFER_FAILED, c->ud);
return true;
}
if (wl_display_flush(c->display) < 0 && errno != EAGAIN)
diff --git a/src/platform/wayland/clipboard.h b/src/platform/wayland/clipboard.h
index 133cc14..d03df17 100644
--- a/src/platform/wayland/clipboard.h
+++ b/src/platform/wayland/clipboard.h
@@ -1,10 +1,10 @@
-#ifndef YASL_WAYLAND_CLIPBOARD_H
-#define YASL_WAYLAND_CLIPBOARD_H
+#ifndef YASL_PRIVATE_WAYLAND_CLIPBOARD_H
+#define YASL_PRIVATE_WAYLAND_CLIPBOARD_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
-#include "yasl/yasl.h"
+#include "common/types.h"
struct wl_display;
struct wl_data_device_manager;
@@ -16,27 +16,27 @@ struct zwp_primary_selection_device_manager_v1;
typedef void (*clipboard_receive_fn)(const char *, size_t, bool uri_list,
struct wl_surface *on, uint64_t request_id,
- enum yasl_transfer_status, void *);
+ enum yasl_private_transfer_status, void *);
struct clipboard *clipboard_create(struct wl_display *, struct wl_data_device_manager *,
- struct wl_seat *, struct yasl_loop *,
+ struct wl_seat *, struct yasl_private_loop *,
clipboard_receive_fn, void *);
void clipboard_destroy(struct clipboard *);
bool clipboard_set_seat(struct clipboard *, struct wl_seat *);
void clipboard_set_primary_manager(struct clipboard *, struct zwp_primary_selection_device_manager_v1 *);
bool clipboard_copy(struct clipboard *, const char *, uint32_t);
void clipboard_paste(struct clipboard *, struct wl_surface *target);
-bool clipboard_request_paste(struct clipboard *, enum yasl_selection,
+bool clipboard_request_paste(struct clipboard *, enum yasl_private_selection,
struct wl_surface *target, uint64_t request_id);
void clipboard_forget_surface(struct clipboard *, struct wl_surface *);
bool clipboard_copy_primary(struct clipboard *, const char *, uint32_t);
void clipboard_paste_primary(struct clipboard *, struct wl_surface *target);
-typedef void (*clipboard_drag_fn)(enum yasl_drag_event, struct wl_surface *on,
+typedef void (*clipboard_drag_fn)(enum yasl_private_drag_event, struct wl_surface *on,
double x, double y, void *);
void clipboard_set_drag_handler(struct clipboard *, clipboard_drag_fn, void *);
-typedef void (*clipboard_drop_fn)(enum yasl_drop_event, struct wl_surface *on,
+typedef void (*clipboard_drop_fn)(enum yasl_private_drop_event, struct wl_surface *on,
double x, double y, void *);
void clipboard_set_drop_handler(struct clipboard *, clipboard_drop_fn, void *);
const char *clipboard_drag_mime(void);
diff --git a/src/platform/wayland/drag.c b/src/platform/wayland/drag.c
index 1f4a78b..07e31b5 100644
--- a/src/platform/wayland/drag.c
+++ b/src/platform/wayland/drag.c
@@ -11,13 +11,13 @@ static void icon_clear(struct drag_icon *icon)
*icon = (struct drag_icon){0};
}
-static bool icon_create(struct wayland_display *c, const struct yasl_drag_image *image)
+static bool icon_create(struct wayland_display *c, const struct yasl_private_drag_image *image)
{
struct drag_icon *icon = &c->drag.icon;
size_t bytes;
if (!image->pixels || !buffer_size(image->size.x, image->size.y, &bytes))
return false;
- int fd = memfd_create("yasl_drag_icon", MFD_CLOEXEC | MFD_ALLOW_SEALING);
+ int fd = memfd_create("yasl_private_drag_icon", MFD_CLOEXEC | MFD_ALLOW_SEALING);
if (fd < 0)
return false;
void *mapping = MAP_FAILED;
@@ -48,7 +48,7 @@ static bool icon_create(struct wayland_display *c, const struct yasl_drag_image
return true;
}
-static void icon_show(struct wayland_display *c, const struct yasl_drag_image *image)
+static void icon_show(struct wayland_display *c, const struct yasl_private_drag_image *image)
{
struct drag_icon *icon = &c->drag.icon;
uint32_t scale = c->drag.scale_120;
@@ -56,18 +56,18 @@ static void icon_show(struct wayland_display *c, const struct yasl_drag_image *i
icon->viewport = wp_viewporter_get_viewport(c->viewporter, icon->surface);
if (icon->viewport)
wp_viewport_set_destination(icon->viewport,
- yasl_scale_to_logical(image->size.x, scale),
- yasl_scale_to_logical(image->size.y, scale));
- } else if (scale % YASL_SCALE_UNIT == 0 &&
+ yasl_private_scale_to_logical(image->size.x, scale),
+ yasl_private_scale_to_logical(image->size.y, scale));
+ } else if (scale % YASL_PRIVATE_SCALE_UNIT == 0 &&
wl_proxy_get_version((struct wl_proxy *)icon->surface) >=
WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION) {
- wl_surface_set_buffer_scale(icon->surface, (int32_t)(scale / YASL_SCALE_UNIT));
+ wl_surface_set_buffer_scale(icon->surface, (int32_t)(scale / YASL_PRIVATE_SCALE_UNIT));
}
if (wl_proxy_get_version((struct wl_proxy *)icon->surface) >=
WL_SURFACE_OFFSET_SINCE_VERSION)
wl_surface_offset(icon->surface,
- -yasl_scale_to_logical(image->hotspot.x, scale),
- -yasl_scale_to_logical(image->hotspot.y, scale));
+ -yasl_private_scale_to_logical(image->hotspot.x, scale),
+ -yasl_private_scale_to_logical(image->hotspot.y, scale));
wl_surface_attach(icon->surface, icon->buffer, 0, 0);
wl_surface_damage_buffer(icon->surface, 0, 0, image->size.x, image->size.y);
wl_surface_commit(icon->surface);
@@ -88,9 +88,9 @@ static void drag_clear(struct wayland_display *c, bool ended)
*d = (struct conn_drag){0};
}
-static void drag_finish(struct wayland_display *c, enum yasl_drag_end how, bool ended)
+static void drag_finish(struct wayland_display *c, enum yasl_private_drag_end how, bool ended)
{
- struct yasl_drag_listener l = c->drag.listener;
+ struct yasl_private_drag_listener l = c->drag.listener;
void *ud = c->drag.ud;
drag_clear(c, ended);
if (l.end)
@@ -100,7 +100,7 @@ static void drag_finish(struct wayland_display *c, enum yasl_drag_end how, bool
void conn_drag_end(struct wayland_display *c)
{
if (c && c->drag.source)
- drag_finish(c, YASL_DRAG_CANCELLED, false);
+ drag_finish(c, YASL_PRIVATE_DRAG_CANCELLED, false);
}
static void source_target(void *ud, struct wl_data_source *s, const char *mime) { (void)ud; (void)s; (void)mime; }
@@ -112,7 +112,7 @@ static void source_cancelled(void *ud, struct wl_data_source *s)
(void)s;
struct wayland_display *c = ud;
bool released = c->drag.dropped;
- drag_finish(c, released ? YASL_DRAG_DROPPED : YASL_DRAG_CANCELLED, true);
+ drag_finish(c, released ? YASL_PRIVATE_DRAG_DROPPED : YASL_PRIVATE_DRAG_CANCELLED, true);
}
static void source_dropped(void *ud, struct wl_data_source *s)
@@ -124,7 +124,7 @@ static void source_dropped(void *ud, struct wl_data_source *s)
static void source_finished(void *ud, struct wl_data_source *s)
{
(void)s;
- drag_finish(ud, YASL_DRAG_DROPPED, true);
+ drag_finish(ud, YASL_PRIVATE_DRAG_DROPPED, true);
}
static const struct wl_data_source_listener source_listener = {
@@ -150,8 +150,8 @@ static struct seat *drag_seat(struct wayland_display *c, struct wayland_surface
}
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)
+ const struct yasl_private_drag_image *image,
+ const struct yasl_private_drag_listener *l, void *ud)
{
if (!c || !origin || origin->conn != c || !l || c->drag.source)
return false;
@@ -190,39 +190,39 @@ bool wayland_display_drag_start(struct wayland_display *c, struct wayland_surfac
void wayland_display_drag_cancel(struct wayland_display *c)
{
if (c && c->drag.source)
- drag_finish(c, YASL_DRAG_CANCELLED, false);
+ drag_finish(c, YASL_PRIVATE_DRAG_CANCELLED, false);
}
-bool wayland_surface_drag_carry(struct wayland_surface *w, struct yasl_vector2i offset)
+bool wayland_surface_drag_carry(struct wayland_surface *w, struct yasl_private_vector2i offset)
{
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;
xdg_toplevel_drag_v1_attach(c->drag.toplevel, w->toplevel,
- yasl_scale_to_logical(offset.x, scale),
- yasl_scale_to_logical(offset.y, scale));
+ yasl_private_scale_to_logical(offset.x, scale),
+ yasl_private_scale_to_logical(offset.y, scale));
c->drag.carrying = true;
return true;
}
-bool wayland_surface_place_under_pointer(struct wayland_surface *w, struct yasl_vector2i at)
+bool wayland_surface_place_under_pointer(struct wayland_surface *w, struct yasl_private_vector2i at)
{
(void)w;
(void)at;
return false;
}
-static struct yasl_vector2i drag_device_pos(const struct wayland_surface *w, double x, double y)
+static struct yasl_private_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){
+ return (struct yasl_private_vector2i){
.x = (int)(dx < 0 ? dx - 0.5 : dx + 0.5),
.y = (int)(dy < 0 ? dy - 0.5 : dy + 0.5),
};
}
-void conn_drop_target(struct wayland_display *c, enum yasl_drop_event ev,
+void conn_drop_target(struct wayland_display *c, enum yasl_private_drop_event ev,
struct wl_surface *on, double x, double y)
{
(void)c;
@@ -230,10 +230,10 @@ void conn_drop_target(struct wayland_display *c, enum yasl_drop_event ev,
(struct wayland_surface *)wl_surface_get_user_data(on) : NULL;
if (!w || w->parent || w->closed)
return;
- yasl_surface_notify_drop(&w->base, ev, drag_device_pos(w, x, y));
+ yasl_private_surface_notify_drop(&w->base, ev, drag_device_pos(w, x, y));
}
-void conn_drag_target(struct wayland_display *c, enum yasl_drag_event ev,
+void conn_drag_target(struct wayland_display *c, enum yasl_private_drag_event ev,
struct wl_surface *on, double x, double y)
{
(void)c;
@@ -241,5 +241,5 @@ void conn_drag_target(struct wayland_display *c, enum yasl_drag_event ev,
(struct wayland_surface *)wl_surface_get_user_data(on) : NULL;
if (!w || w->parent || w->closed)
return;
- yasl_surface_notify_drag(&w->base, ev, drag_device_pos(w, x, y));
+ yasl_private_surface_notify_drag(&w->base, ev, drag_device_pos(w, x, y));
}
diff --git a/src/platform/wayland/ime.c b/src/platform/wayland/ime.c
index 4a091d8..62e5936 100644
--- a/src/platform/wayland/ime.c
+++ b/src/platform/wayland/ime.c
@@ -1,5 +1,7 @@
#include "internal.h"
+#include <errno.h>
+
#define IME_TEXT_MAX (256u * 1024)
static void show_preedit(struct wayland_surface *w, char *text, int begin, int end)
@@ -12,7 +14,7 @@ static void show_preedit(struct wayland_surface *w, char *text, int begin, int e
w->ime_shown_begin = begin;
w->ime_shown_end = end;
if (changed) {
- yasl_surface_notify_composition(&w->base, w->ime_shown,
+ yasl_private_surface_notify_composition(&w->base, w->ime_shown,
w->ime_shown_begin, w->ime_shown_end);
wayland_surface_schedule_redraw(w);
}
@@ -24,21 +26,63 @@ void ime_reset(struct wayland_surface *w)
w->ime_preedit = NULL;
free(w->ime_commit);
w->ime_commit = NULL;
+ w->ime_delete_pending = false;
show_preedit(w, NULL, -1, -1);
}
void ime_send_caret(struct wayland_surface *w)
{
+ wayland_surface_update_text_input(w);
+}
+
+bool wayland_surface_update_text_input(struct wayland_surface *w)
+{
+ if (!w || !w->conn) {
+ errno = EINVAL;
+ return false;
+ }
+ if (!w->conn->text_input_mgr) {
+ errno = ENOTSUP;
+ return false;
+ }
struct seat *seat = w->ime_seat;
- if (!seat || !seat->text_input)
- return;
- uint32_t scale = w->scale_120 ? w->scale_120 : YASL_SCALE_UNIT;
+ if (!seat || !seat->text_input || seat->ime_focus != w)
+ return true;
+ if (!w->base.text_input_active) {
+ if (seat->ime_enabled) {
+ zwp_text_input_v3_disable(seat->text_input);
+ zwp_text_input_v3_commit(seat->text_input);
+ seat->ime_enabled = false;
+ }
+ ime_reset(w);
+ return true;
+ }
+ if (!seat->ime_enabled) {
+ zwp_text_input_v3_enable(seat->text_input);
+ seat->ime_enabled = true;
+ }
+ unsigned purpose = w->base.text_purpose;
+ if (purpose > ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL)
+ purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL;
+ zwp_text_input_v3_set_content_type(seat->text_input,
+ ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, purpose);
+ const char *surrounding = w->base.text_surrounding ? w->base.text_surrounding : "";
+ size_t length = w->base.text_surrounding ? w->base.text_length : 0;
+ size_t cursor = w->base.text_surrounding ? w->base.text_end : 0;
+ size_t anchor = w->base.text_surrounding ? w->base.text_begin : 0;
+ if (cursor > length) cursor = length;
+ if (anchor > length) anchor = length;
+ if (length <= INT32_MAX && cursor <= INT32_MAX && anchor <= INT32_MAX)
+ zwp_text_input_v3_set_surrounding_text(seat->text_input, surrounding,
+ (int32_t)cursor, (int32_t)anchor);
+ uint32_t scale = w->scale_120 ? w->scale_120 : YASL_PRIVATE_SCALE_UNIT;
zwp_text_input_v3_set_cursor_rectangle(seat->text_input,
- yasl_scale_to_logical(w->ime_caret.x, scale),
- yasl_scale_to_logical(w->ime_caret.y, scale),
- yasl_scale_to_logical(w->ime_caret.width, scale),
- yasl_scale_to_logical(w->ime_caret.height, scale));
+ yasl_private_scale_to_logical(w->ime_caret.x, scale),
+ yasl_private_scale_to_logical(w->ime_caret.y, scale),
+ yasl_private_scale_to_logical(w->ime_caret.width, scale),
+ yasl_private_scale_to_logical(w->ime_caret.height, scale));
zwp_text_input_v3_commit(seat->text_input);
+ return true;
}
bool wayland_surface_ime_preedit_cursor(const struct wayland_surface *w, int *begin, int *end)
@@ -50,7 +94,7 @@ bool wayland_surface_ime_preedit_cursor(const struct wayland_surface *w, int *be
return true;
}
-void wayland_surface_set_ime_caret(struct wayland_surface *w, struct yasl_rect2i caret)
+void wayland_surface_set_ime_caret(struct wayland_surface *w, struct yasl_private_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))
@@ -62,17 +106,14 @@ void wayland_surface_set_ime_caret(struct wayland_surface *w, struct yasl_rect2i
static void text_input_enter(void *data, struct zwp_text_input_v3 *text_input,
struct wl_surface *surface)
{
+ (void)text_input;
struct seat *s = data;
struct wayland_surface *w = surface_root(surface ?
(struct wayland_surface *)wl_surface_get_user_data(surface) : NULL);
s->ime_focus = w;
if (!w) return;
- zwp_text_input_v3_enable(text_input);
- zwp_text_input_v3_set_content_type(text_input,
- ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE,
- ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL);
w->ime_seat = s;
- ime_send_caret(w);
+ wayland_surface_update_text_input(w);
}
static void text_input_leave(void *data, struct zwp_text_input_v3 *text_input,
@@ -83,8 +124,11 @@ static void text_input_leave(void *data, struct zwp_text_input_v3 *text_input,
struct wayland_surface *w = s->ime_focus;
s->ime_focus = NULL;
if (!w) return;
- zwp_text_input_v3_disable(text_input);
- zwp_text_input_v3_commit(text_input);
+ if (s->ime_enabled) {
+ zwp_text_input_v3_disable(text_input);
+ zwp_text_input_v3_commit(text_input);
+ s->ime_enabled = false;
+ }
if (w->ime_seat == s)
w->ime_seat = NULL;
ime_reset(w);
@@ -110,7 +154,7 @@ static void text_input_preedit_string(void *data,
{
(void)text_input;
struct wayland_surface *w = ((struct seat *)data)->ime_focus;
- if (!w) return;
+ if (!w || !w->base.text_input_active) return;
keyboard_text_reset((struct seat *)data);
free(w->ime_preedit);
w->ime_preedit = text ? copy_text(text) : NULL;
@@ -126,7 +170,7 @@ static void text_input_commit_string(void *data,
{
(void)text_input;
struct wayland_surface *w = ((struct seat *)data)->ime_focus;
- if (!w) return;
+ if (!w || !w->base.text_input_active) return;
keyboard_text_reset((struct seat *)data);
free(w->ime_commit);
w->ime_commit = (text && text[0]) ? copy_text(text) : NULL;
@@ -136,7 +180,31 @@ static void text_input_delete_surrounding_text(void *data,
struct zwp_text_input_v3 *text_input, uint32_t before_length,
uint32_t after_length)
{
- (void)data; (void)text_input; (void)before_length; (void)after_length;
+ (void)text_input;
+ struct seat *seat = data;
+ struct wayland_surface *w = seat->ime_focus;
+ if (!w || !w->base.text_input_active || !w->base.text_surrounding)
+ return;
+ size_t length = w->base.text_length;
+ size_t cursor = w->base.text_end <= length ? w->base.text_end : length;
+ while (cursor && cursor < length &&
+ (((unsigned char)w->base.text_surrounding[cursor] & 0xc0) == 0x80))
+ cursor--;
+ size_t begin = before_length > cursor ? 0 : cursor - before_length;
+ size_t end = after_length > length - cursor ? length : cursor + after_length;
+ while (begin && begin < length &&
+ (((unsigned char)w->base.text_surrounding[begin] & 0xc0) == 0x80))
+ begin--;
+ while (end < length && (((unsigned char)w->base.text_surrounding[end] & 0xc0) == 0x80))
+ end++;
+ /* text_input_v3 batches deletion and commit into one `done` group. Keep
+ * the replacement range paired with that group's committed text and
+ * enqueue both together below, preserving snapshot metadata. */
+ w->ime_delete_pending = true;
+ w->ime_delete_begin = begin;
+ w->ime_delete_end = end;
+ w->ime_delete_revision = w->base.text_revision;
+ w->ime_delete_generation = w->base.text_generation;
}
static void text_input_done(void *data, struct zwp_text_input_v3 *text_input,
@@ -144,17 +212,46 @@ static void text_input_done(void *data, struct zwp_text_input_v3 *text_input,
{
(void)text_input; (void)serial;
struct wayland_surface *w = ((struct seat *)data)->ime_focus;
- if (!w) return;
- if (w->ime_commit) {
- struct yasl_event ev = {
- .type = YASL_EVENT_TEXT,
+ if (!w || !w->base.text_input_active) return;
+ bool has_snapshot = w->base.text_surrounding != NULL &&
+ w->base.text_length <= INT32_MAX;
+ bool has_commit = w->ime_commit && w->ime_commit[0];
+ bool edit = w->ime_delete_pending || (has_commit && has_snapshot);
+ if (edit) {
+ size_t begin = w->ime_delete_pending ? w->ime_delete_begin : w->base.text_begin;
+ size_t end = w->ime_delete_pending ? w->ime_delete_end : w->base.text_end;
+ if (begin > w->base.text_length) begin = w->base.text_length;
+ if (end > w->base.text_length) end = w->base.text_length;
+ if (end < begin) end = begin;
+ struct yasl_private_event ev = {
+ .type = YASL_PRIVATE_EVENT_TEXT_EDIT,
+ .surface = &w->base,
+ .text_edit = {
+ .data = has_commit ? w->ime_commit : "",
+ .len = has_commit ? strlen(w->ime_commit) : 0,
+ .revision = w->ime_delete_revision,
+ .generation = w->ime_delete_generation,
+ .begin = begin,
+ .end = end,
+ },
+ };
+ if (!w->ime_delete_pending) {
+ ev.text_edit.revision = w->base.text_revision;
+ ev.text_edit.generation = w->base.text_generation;
+ }
+ input_queue_push(&w->input, &ev);
+ w->ime_delete_pending = false;
+ }
+ if (w->ime_commit && !edit) {
+ struct yasl_private_event ev = {
+ .type = YASL_PRIVATE_EVENT_TEXT,
.surface = &w->base,
.text = { .data = w->ime_commit, .len = strlen(w->ime_commit) },
};
input_queue_push(&w->input, &ev);
- free(w->ime_commit);
- w->ime_commit = NULL;
}
+ free(w->ime_commit);
+ w->ime_commit = NULL;
char *preedit = w->ime_preedit && w->ime_preedit[0] ? w->ime_preedit : NULL;
if (!preedit)
free(w->ime_preedit);
diff --git a/src/platform/wayland/internal.h b/src/platform/wayland/internal.h
index 34d4133..a479e2b 100644
--- a/src/platform/wayland/internal.h
+++ b/src/platform/wayland/internal.h
@@ -1,5 +1,5 @@
-#ifndef YASL_WAYLAND_INTERNAL_H
-#define YASL_WAYLAND_INTERNAL_H
+#ifndef YASL_PRIVATE_WAYLAND_INTERNAL_H
+#define YASL_PRIVATE_WAYLAND_INTERNAL_H
#define _GNU_SOURCE
@@ -10,6 +10,11 @@
#include <wayland-client.h>
#include <wayland-cursor.h>
+#if YASL_HAS_EGL
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <wayland-egl.h>
+#endif
#include "protocols/xdg-shell-client-protocol.h"
#include "protocols/xdg-decoration-client-protocol.h"
#include "protocols/cursor-shape-v1-client-protocol.h"
@@ -73,7 +78,7 @@ struct wl_conn_output {
struct pending_press {
struct wayland_surface *surface;
- struct yasl_vector2i pos;
+ struct yasl_private_vector2i pos;
int button;
bool release;
};
@@ -105,7 +110,7 @@ struct conn_drag {
struct wl_data_source *source;
struct xdg_toplevel_drag_v1 *toplevel;
struct drag_icon icon;
- struct yasl_drag_listener listener;
+ struct yasl_private_drag_listener listener;
void *ud;
struct seat *seat;
uint32_t scale_120;
@@ -160,15 +165,20 @@ struct seat {
uint64_t pointer_buttons;
uint64_t tool_buttons;
double axis[2], axis_value120[2];
+ double axis_pixels[2];
int64_t axis_discrete[2];
+ uint32_t axis_source;
+ bool scroll_gesture, axis_stop_pending;
+ unsigned axis_phase;
+ bool ime_enabled;
bool axis_has_value120[2], axis_has_discrete[2];
double touch_scroll_y;
/* Embedded structs */
xkb_mod_mask_t input_mod_masks[8];
- struct yasl_vector2i pointer_pos;
- struct yasl_vector2i ptr_left_pos;
- struct yasl_vector2i tool_pos;
+ struct yasl_private_vector2i pointer_pos;
+ struct yasl_private_vector2i ptr_left_pos;
+ struct yasl_private_vector2i tool_pos;
struct touch_point touch_points[SEAT_TOUCH_POINTS];
struct press_queue ptr_presses;
struct press_queue tool_presses;
@@ -183,7 +193,7 @@ struct seat {
uint32_t press_serial;
uint32_t pointer_serial;
int cursor_shape;
- struct yasl_pointer_pen pen;
+ struct yasl_private_pointer_pen pen;
int tool_wheel;
/* 1-byte booleans grouped tightly */
@@ -199,9 +209,14 @@ struct seat {
#include "common/input.h"
struct wayland_display {
- struct yasl_display base;
+ struct yasl_private_display base;
/* 8-byte pointers & 64-bit masks */
struct wl_display *wl;
+#if YASL_HAS_EGL
+ EGLDisplay egl_display;
+ EGLint egl_api;
+ bool egl_initialized;
+#endif
struct wl_registry *registry;
struct wl_compositor *compositor;
struct wl_shm *shm;
@@ -228,8 +243,8 @@ struct wayland_display {
struct wp_fifo_manager_v1 *fifo_mgr;
struct wp_commit_timing_manager_v1 *commit_timing_mgr;
struct xkb_context *xkb_ctx;
- struct yasl_loop *loop;
- const struct yasl_display_cbs *cbs;
+ struct yasl_private_loop *loop;
+ const struct yasl_private_display_cbs *cbs;
void *ud;
char *app_id;
struct xdg_toplevel_drag_manager_v1 *toplevel_drag_mgr;
@@ -243,6 +258,7 @@ struct wayland_display {
clockid_t pres_clock;
int watch;
int depth;
+ uint64_t next_tool_id;
/* 1-byte booleans */
bool owns_loop;
@@ -266,6 +282,12 @@ struct wayland_surface {
struct wayland_surface *dead_next;
struct wayland_surface *parent;
struct wl_surface *surface;
+#if YASL_HAS_EGL
+ struct wl_egl_window *egl_window;
+ EGLSurface egl_surface;
+ EGLContext egl_context;
+ EGLConfig egl_config;
+#endif
struct xdg_surface *xdg_surface;
struct xdg_toplevel *toplevel;
struct xdg_popup *popup;
@@ -276,6 +298,8 @@ struct wayland_surface {
char *ime_commit;
char *ime_shown;
struct seat *ime_seat;
+ size_t ime_delete_begin, ime_delete_end;
+ uint64_t ime_delete_revision, ime_delete_generation;
struct wp_tearing_control_v1 *tearing_control;
struct latency_feedback *latency_feedbacks;
struct latency_feedback latency_pool[8];
@@ -285,24 +309,24 @@ struct wayland_surface {
/* Persistent lock held while relative; lock_seat owns the locked wl_pointer. */
struct zwp_locked_pointer_v1 *pointer_lock;
struct seat *lock_seat;
- const struct yasl_window_cbs *cbs;
+ const struct yasl_private_window_cbs *cbs;
void *ud;
uint64_t frames_skipped;
uint64_t outputs;
double scale_factor;
/* Sub-structures */
- struct yasl_rect2i ime_caret;
- struct yasl_rect2i content;
- struct yasl_frame_clock clock;
+ struct yasl_private_rect2i ime_caret;
+ struct yasl_private_rect2i content;
+ struct yasl_private_frame_clock clock;
struct buffer buffers[NBUF];
- struct yasl_frame frame;
- struct yasl_damage_list damage;
+ struct yasl_private_frame frame;
+ struct yasl_private_damage_list damage;
int ime_cursor_begin;
struct input_queue input;
/* 4-byte integers */
- enum yasl_cursor cursor;
+ enum yasl_private_cursor cursor;
int ime_cursor_end;
int ime_shown_begin, ime_shown_end;
int floating_width, floating_height;
@@ -331,6 +355,7 @@ struct wayland_surface {
bool want_draw;
bool relative;
bool pointer_locked;
+ bool ime_delete_pending;
};
@@ -343,7 +368,7 @@ int buffer_prepare(struct wayland_surface *w, struct buffer *b, int width, int
const struct wl_buffer_listener *listener);
int apply_pending_geometry(struct wayland_surface *w);
-static inline void wl_damage_list(struct wl_surface *s, const struct yasl_damage_list *d)
+static inline void wl_damage_list(struct wl_surface *s, const struct yasl_private_damage_list *d)
{
if (d->n < 0) {
wl_surface_damage_buffer(s, d->bounds.x, d->bounds.y,
@@ -365,6 +390,7 @@ 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 wayland_surface_arm_frame(struct wayland_surface *w);
void surface_fail(struct wayland_surface *w);
void display_leave(struct wayland_display *c);
@@ -376,9 +402,9 @@ static inline struct wayland_surface *surface_root(struct wayland_surface *w)
}
void conn_drag_end(struct wayland_display *c);
-void conn_drop_target(struct wayland_display *c, enum yasl_drop_event ev,
+void conn_drop_target(struct wayland_display *c, enum yasl_private_drop_event ev,
struct wl_surface *on, double x, double y);
-void conn_drag_target(struct wayland_display *c, enum yasl_drag_event ev,
+void conn_drag_target(struct wayland_display *c, enum yasl_private_drag_event ev,
struct wl_surface *on, double x, double y);
void seat_bind(struct wayland_display *c, struct wl_registry *registry,
@@ -386,6 +412,7 @@ void seat_bind(struct wayland_display *c, struct wl_registry *registry,
bool seat_remove(struct wayland_display *c, uint32_t name);
void seat_destroy(struct seat *s);
void seat_note_input(struct seat *s, uint32_t serial);
+void seat_ensure_text_input(struct seat *s);
void seat_forget_surface(struct seat *s, struct wayland_surface *w);
void seat_clipboard_ensure(struct seat *s);
int display_input_init(struct wayland_display *c);
@@ -409,6 +436,7 @@ extern const struct zwp_text_input_v3_listener text_input_listener;
void ime_reset(struct wayland_surface *w);
void keyboard_text_reset(struct seat *s);
void ime_send_caret(struct wayland_surface *w);
+bool wayland_surface_update_text_input(struct wayland_surface *w);
int surface_init_shell(struct wayland_surface *w, const char *title);
extern const struct xdg_surface_listener shell_surface_listener;
diff --git a/src/platform/wayland/keyboard.c b/src/platform/wayland/keyboard.c
index 7f8274d..6f9dec9 100644
--- a/src/platform/wayland/keyboard.c
+++ b/src/platform/wayland/keyboard.c
@@ -133,8 +133,8 @@ bool conn_keyboard_on(const struct wayland_display *c, const struct wayland_surf
static void push_focus(struct wayland_surface *w, bool focused)
{
- struct yasl_event ev = {
- .type = YASL_EVENT_FOCUS,
+ struct yasl_private_event ev = {
+ .type = YASL_PRIVATE_EVENT_FOCUS,
.surface = &w->base,
.focus = { .focused = focused },
};
@@ -218,18 +218,18 @@ static uint32_t key_codepoint_at_level(struct seat *s, uint32_t key, xkb_layout_
return n == 1 ? xkb_keysym_to_utf32(syms[0]) : 0;
}
-static struct yasl_key_event xkb_key_event(struct seat *s, uint32_t key,
+static struct yasl_private_key_event xkb_key_event(struct seat *s, uint32_t key,
enum yasl_key_action action)
{
uint32_t code = key + XKB_EVDEV_OFFSET;
xkb_layout_index_t layout = xkb_state_key_get_layout(s->xkb_state, code);
- struct yasl_key_event k = {
- .key = yasl_key_from_evdev(key),
+ struct yasl_private_key_event k = {
+ .key = yasl_private_key_from_evdev(key),
.action = action,
.mods = input_modifiers(s),
- .codepoint = yasl_key_label(key_codepoint_at_level(s, key, layout, 0)),
- .shifted = yasl_key_label(key_codepoint_at_level(s, key, layout, 1)),
+ .codepoint = yasl_private_key_label(key_codepoint_at_level(s, key, layout, 0)),
+ .shifted = yasl_private_key_label(key_codepoint_at_level(s, key, layout, 1)),
.native_code = key,
};
return k;
@@ -239,8 +239,8 @@ static void keyboard_text(struct seat *s, struct wayland_surface *w, uint32_t ke
{
uint32_t code = key + XKB_EVDEV_OFFSET;
unsigned mods = input_modifiers(s);
- if ((mods & (YASL_MOD_CTRL | YASL_MOD_SUPER)) ||
- ((mods & YASL_MOD_ALT) &&
+ if ((mods & (YASL_PRIVATE_MOD_CTRL | YASL_PRIVATE_MOD_SUPER)) ||
+ ((mods & YASL_PRIVATE_MOD_ALT) &&
!xkb_state_mod_index_is_consumed(s->xkb_state, code, s->mod_alt))) {
keyboard_text_reset(s);
return;
@@ -278,13 +278,13 @@ static void keyboard_text(struct seat *s, struct wayland_surface *w, uint32_t ke
else xkb_state_key_get_utf8(s->xkb_state, code, text, (size_t)len + 1);
/* Editing commands belong to key events, not text insertion. */
if ((unsigned char)text[0] >= 0x20 && text[0] != 0x7f) {
- struct yasl_event ev = {.type = YASL_EVENT_TEXT,
+ struct yasl_private_event ev = {.type = YASL_PRIVATE_EVENT_TEXT,
.surface = &w->base, .text = {.data = text, .len = (size_t)len}};
input_queue_push(&w->input, &ev);
}
if (text != local) free(text);
} else {
- struct yasl_event ev = {.type = YASL_EVENT_INPUT_RESET, .surface = &w->base};
+ struct yasl_private_event ev = {.type = YASL_PRIVATE_EVENT_INPUT_RESET, .surface = &w->base};
input_queue_push(&w->input, &ev);
}
}
@@ -296,9 +296,9 @@ static bool keyboard_dispatch_key(struct seat *s, struct wayland_surface *w, uin
{
if (!s->xkb_state || !w->cbs) return false;
- struct yasl_key_event k = xkb_key_event(s, key, action);
- struct yasl_event ev = {
- .type = YASL_EVENT_KEY,
+ struct yasl_private_key_event k = xkb_key_event(s, key, action);
+ struct yasl_private_event ev = {
+ .type = YASL_PRIVATE_EVENT_KEY,
.surface = &w->base,
.key = k,
};
@@ -329,7 +329,7 @@ static void keyboard_key(void *data, struct wl_keyboard *keyboard,
if (keyboard_dispatch_key(s, w, key, YASL_KEY_PRESS) && repeatable) {
s->repeat_key = key;
s->repeat_held = true;
- s->repeat_started_ns = yasl_loop_now_ns();
+ s->repeat_started_ns = yasl_private_loop_now_ns();
keyboard_repeat_sync(s);
}
}
@@ -389,7 +389,7 @@ int keyboard_repeat_init(struct seat *s)
if (s->repeat_fd >= 0) return 0;
s->repeat_fd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);
if (s->repeat_fd < 0) return -1;
- s->repeat_watch = yasl_loop_add_fd(s->conn->loop, s->repeat_fd, POLLIN,
+ s->repeat_watch = yasl_private_loop_add_fd(s->conn->loop, s->repeat_fd, POLLIN,
keyboard_repeat_ready, s);
if (s->repeat_watch <= 0) {
s->repeat_watch = 0;
@@ -406,18 +406,18 @@ int seat_modifiers(const struct seat *s)
if (unlikely(!s || !s->xkb_state)) return 0;
return ((s->mod_shift != XKB_MOD_INVALID &&
xkb_state_mod_index_is_active(s->xkb_state, s->mod_shift,
- XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_MOD_SHIFT) |
+ XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_PRIVATE_MOD_SHIFT) |
((s->mod_alt != XKB_MOD_INVALID &&
xkb_state_mod_index_is_active(s->xkb_state, s->mod_alt,
- XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_MOD_ALT) |
+ XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_PRIVATE_MOD_ALT) |
((s->mod_ctrl != XKB_MOD_INVALID &&
xkb_state_mod_index_is_active(s->xkb_state, s->mod_ctrl,
- XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_MOD_CTRL) |
+ XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_PRIVATE_MOD_CTRL) |
((s->mod_super != XKB_MOD_INVALID &&
xkb_state_mod_index_is_active(s->xkb_state, s->mod_super,
- XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_MOD_SUPER) |
+ XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_PRIVATE_MOD_SUPER) |
((xkb_state_mod_name_is_active(s->xkb_state, XKB_MOD_NAME_CAPS,
- XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_MOD_CAPS);
+ XKB_STATE_MODS_EFFECTIVE) > 0) * YASL_PRIVATE_MOD_CAPS);
}
int wayland_display_modifiers(const struct wayland_display *c)
@@ -425,12 +425,12 @@ int wayland_display_modifiers(const struct wayland_display *c)
return c ? seat_modifiers(c->active_seat ? c->active_seat : c->seats) : 0;
}
-void wayland_display_copy(struct wayland_display *c, enum yasl_selection sel, const char *text)
+void wayland_display_copy(struct wayland_display *c, enum yasl_private_selection sel, const char *text)
{
(void)wayland_display_copy_text(c, sel, text);
}
-bool wayland_display_copy_text(struct wayland_display *c, enum yasl_selection sel,
+bool wayland_display_copy_text(struct wayland_display *c, enum yasl_private_selection sel,
const char *text)
{
struct seat *s = c ? (c->active_seat ? c->active_seat : c->seats) : NULL;
@@ -447,28 +447,28 @@ bool wayland_display_copy_text(struct wayland_display *c, enum yasl_selection se
return false;
}
errno = 0;
- bool ok = sel == YASL_PRIMARY ?
+ bool ok = sel == YASL_PRIVATE_PRIMARY ?
clipboard_copy_primary(s->clipboard, text, s->input_serial) :
clipboard_copy(s->clipboard, text, s->input_serial);
return ok;
}
-void wayland_display_paste(struct wayland_display *c, enum yasl_selection sel, struct wayland_surface *target)
+void wayland_display_paste(struct wayland_display *c, enum yasl_private_selection sel, struct wayland_surface *target)
{
struct seat *s = c ? (c->active_seat ? c->active_seat : c->seats) : NULL;
if (!target || target->closed || target->destroyed) return;
if (!s || !s->clipboard) {
- yasl_surface_complete_paste(&target->base, 0,
- YASL_TRANSFER_UNAVAILABLE, NULL, 0);
+ yasl_private_surface_complete_paste(&target->base, 0,
+ YASL_PRIVATE_TRANSFER_UNAVAILABLE, NULL, 0);
return;
}
- if (sel == YASL_PRIMARY)
+ if (sel == YASL_PRIVATE_PRIMARY)
clipboard_paste_primary(s->clipboard, target->surface);
else
clipboard_paste(s->clipboard, target->surface);
}
-bool wayland_display_request_paste(struct wayland_display *c, enum yasl_selection sel,
+bool wayland_display_request_paste(struct wayland_display *c, enum yasl_private_selection sel,
struct wayland_surface *target, uint64_t request_id)
{
struct seat *s = c ? (c->active_seat ? c->active_seat : c->seats) : NULL;
@@ -486,7 +486,7 @@ bool wayland_display_request_paste(struct wayland_display *c, enum yasl_selectio
return ok;
}
-bool wayland_surface_poll_event(struct wayland_surface *w, struct yasl_event *ev)
+bool wayland_surface_poll_event(struct wayland_surface *w, struct yasl_private_event *ev)
{
return w && ev && input_queue_pop(&w->input, ev);
}
diff --git a/src/platform/wayland/keymap.c b/src/platform/wayland/keymap.c
index d626f30..6c3b4dd 100644
--- a/src/platform/wayland/keymap.c
+++ b/src/platform/wayland/keymap.c
@@ -1,7 +1,7 @@
#include "keymap.h"
/* Numeric Linux evdev codes; no platform headers are needed by this table. */
-enum yasl_key yasl_key_from_evdev(uint32_t code)
+enum yasl_key yasl_private_key_from_evdev(uint32_t code)
{
switch (code) {
case 1: return YASL_KEY_ESCAPE; /* KEY_ESC */
diff --git a/src/platform/wayland/keymap.h b/src/platform/wayland/keymap.h
index c0ff81e..26b6ed4 100644
--- a/src/platform/wayland/keymap.h
+++ b/src/platform/wayland/keymap.h
@@ -1,8 +1,8 @@
-#ifndef YASL_WAYLAND_KEYMAP_H
-#define YASL_WAYLAND_KEYMAP_H
+#ifndef YASL_PRIVATE_WAYLAND_KEYMAP_H
+#define YASL_PRIVATE_WAYLAND_KEYMAP_H
#include "yasl/keycodes.h"
-enum yasl_key yasl_key_from_evdev(uint32_t code);
+enum yasl_key yasl_private_key_from_evdev(uint32_t code);
#endif
diff --git a/src/platform/wayland/native.c b/src/platform/wayland/native.c
index 55ce445..54ca184 100644
--- a/src/platform/wayland/native.c
+++ b/src/platform/wayland/native.c
@@ -1,14 +1,14 @@
#include "common/backend.h"
-#include "yasl/native/wayland.h"
+#include "common/private_native_wayland.h"
-struct wl_display *yasl_wayland_display_get_wl_display(struct yasl_display *d)
+struct wl_display *yasl_private_wayland_display_get_wl_display(struct yasl_private_display *d)
{
- return (d && yasl_display_backend(d) == YASL_BACKEND_WAYLAND) ?
- (struct wl_display *)yasl_display_get_native_handle(d) : NULL;
+ return (d && yasl_private_display_backend(d) == YASL_PRIVATE_BACKEND_WAYLAND) ?
+ (struct wl_display *)yasl_private_display_get_native_handle(d) : NULL;
}
-struct wl_surface *yasl_wayland_surface_get_wl_surface(struct yasl_surface *s)
+struct wl_surface *yasl_private_wayland_surface_get_wl_surface(struct yasl_surface *s)
{
- return (s && s->display && yasl_display_backend(s->display) == YASL_BACKEND_WAYLAND) ?
- (struct wl_surface *)yasl_surface_get_native_handle(s) : NULL;
+ return (s && s->display && yasl_private_display_backend(s->display) == YASL_PRIVATE_BACKEND_WAYLAND) ?
+ (struct wl_surface *)yasl_private_surface_get_native_handle(s) : NULL;
}
diff --git a/src/platform/wayland/pointer.c b/src/platform/wayland/pointer.c
index da666da..5f486ae 100644
--- a/src/platform/wayland/pointer.c
+++ b/src/platform/wayland/pointer.c
@@ -12,12 +12,22 @@ struct tablet_tool {
struct zwp_tablet_tool_v2 *proxy;
struct wp_cursor_shape_device_v1 *shape_dev;
uint32_t proximity_serial;
+ uint64_t device_id, tool_id;
+ uint32_t axes_valid, axes_changed, capability_mask;
+ double x, y;
+ double pressure, tilt_x, tilt_y, rotation, distance, tangential, wheel_degrees;
+ unsigned changed_button;
+ unsigned pending_actions[8], pending_buttons[8], pending_action_count;
+ bool pending_contacts[8];
+ unsigned kind;
+ bool in_contact, pen_in_proximity;
bool eraser;
};
struct tablet_device {
struct tablet_device *next;
struct zwp_tablet_v2 *proxy;
+ uint64_t id;
};
struct tablet_pad {
@@ -25,10 +35,10 @@ struct tablet_pad {
struct zwp_tablet_pad_v2 *proxy;
};
-static struct yasl_vector2i device_from_fixed(const struct wayland_surface *w,
+static struct yasl_private_vector2i device_from_fixed(const struct wayland_surface *w,
wl_fixed_t x, wl_fixed_t y)
{
- return (struct yasl_vector2i){
+ return (struct yasl_private_vector2i){
.x = (int)lrint(wl_fixed_to_double(x) * w->scale_factor),
.y = (int)lrint(wl_fixed_to_double(y) * w->scale_factor),
};
@@ -41,10 +51,10 @@ void seat_note_input(struct seat *s, uint32_t serial)
}
static void push_pointer(struct seat *s, struct wayland_surface *w,
- enum yasl_event_type type, struct yasl_pointer_event ev)
+ enum yasl_private_event_type type, struct yasl_private_pointer_event ev)
{
ev.mods = (uint32_t)seat_modifiers(s);
- struct yasl_event input_ev = {
+ struct yasl_private_event input_ev = {
.type = type,
.surface = &w->base,
.pointer = ev,
@@ -52,13 +62,15 @@ static void push_pointer(struct seat *s, struct wayland_surface *w,
input_queue_push(&surface_root(w)->input, &input_ev);
}
-static void report_wheel(struct seat *s, struct wayland_surface *w,
- struct yasl_vector2i pos, double x, double y)
+static unsigned scroll_source_of(uint32_t source)
{
- if ((x == 0 && y == 0) || !isfinite(x) || !isfinite(y)) return;
- push_pointer(s, w, YASL_EVENT_POINTER_SCROLL, (struct yasl_pointer_event){
- .pos = pos, .scroll = {x, y},
- });
+ switch (source) {
+ case WL_POINTER_AXIS_SOURCE_WHEEL: return 1;
+ case WL_POINTER_AXIS_SOURCE_WHEEL_TILT: return 1;
+ case WL_POINTER_AXIS_SOURCE_FINGER: return 2;
+ case WL_POINTER_AXIS_SOURCE_CONTINUOUS: return 3;
+ default: return 0;
+ }
}
static bool cursor_theme_for(struct wayland_display *c, int scale)
@@ -90,10 +102,10 @@ static bool seat_cursor_surface(struct seat *s)
}
static void set_cursor_theme_fallback(struct seat *s, struct wayland_surface *w,
- enum yasl_cursor shape)
+ enum yasl_private_cursor shape)
{
struct wayland_display *c = s->conn;
- int scale = (int)((w->scale_120 + YASL_SCALE_UNIT - 1) / YASL_SCALE_UNIT);
+ int scale = (int)((w->scale_120 + YASL_PRIVATE_SCALE_UNIT - 1) / YASL_PRIVATE_SCALE_UNIT);
if (scale < 1) scale = 1;
if (!seat_cursor_surface(s) || !cursor_theme_for(c, scale)) return;
static const char *names[][2] = {
@@ -107,7 +119,7 @@ static void set_cursor_theme_fallback(struct seat *s, struct wayland_surface *w,
{"ew-resize", "ew-resize"},
{"ns-resize", "ns-resize"},
};
- int idx = (unsigned)shape < YASL_CURSOR_HIDDEN ? (int)shape : 0;
+ int idx = (unsigned)shape < YASL_PRIVATE_CURSOR_HIDDEN ? (int)shape : 0;
struct wl_cursor *cursor = wl_cursor_theme_get_cursor(c->cursor_theme, names[idx][0]);
if (!cursor) cursor = wl_cursor_theme_get_cursor(c->cursor_theme, names[idx][1]);
if (!cursor || !cursor->image_count) return;
@@ -117,11 +129,11 @@ static void set_cursor_theme_fallback(struct seat *s, struct wayland_surface *w,
int theme_scale = c->cursor_theme_scale;
if (s->cursor_viewport) {
- uint32_t scale_120 = (uint32_t)theme_scale * YASL_SCALE_UNIT;
+ uint32_t scale_120 = (uint32_t)theme_scale * YASL_PRIVATE_SCALE_UNIT;
wp_viewport_set_destination(s->cursor_viewport,
- (int)(((int64_t)(int)img->width * YASL_SCALE_UNIT + scale_120 - 1) /
+ (int)(((int64_t)(int)img->width * YASL_PRIVATE_SCALE_UNIT + scale_120 - 1) /
scale_120),
- (int)(((int64_t)(int)img->height * YASL_SCALE_UNIT + scale_120 - 1) /
+ (int)(((int64_t)(int)img->height * YASL_PRIVATE_SCALE_UNIT + scale_120 - 1) /
scale_120));
} else {
if (img->width % (uint32_t)theme_scale || img->height % (uint32_t)theme_scale)
@@ -137,17 +149,17 @@ static void set_cursor_theme_fallback(struct seat *s, struct wayland_surface *w,
wl_surface_commit(s->cursor_surface);
}
-static uint32_t cursor_shape_of(enum yasl_cursor shape)
+static uint32_t cursor_shape_of(enum yasl_private_cursor shape)
{
switch (shape) {
- case YASL_CURSOR_TEXT: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT;
- case YASL_CURSOR_LINK: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER;
- case YASL_CURSOR_CROSSHAIR: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR;
- case YASL_CURSOR_MOVE: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE;
- case YASL_CURSOR_RESIZE_EW: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE;
- case YASL_CURSOR_RESIZE_NS: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE;
- case YASL_CURSOR_RESIZE_NWSE: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE;
- case YASL_CURSOR_RESIZE_NESW: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE;
+ case YASL_PRIVATE_CURSOR_TEXT: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT;
+ case YASL_PRIVATE_CURSOR_LINK: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER;
+ case YASL_PRIVATE_CURSOR_CROSSHAIR: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR;
+ case YASL_PRIVATE_CURSOR_MOVE: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE;
+ case YASL_PRIVATE_CURSOR_RESIZE_EW: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE;
+ case YASL_PRIVATE_CURSOR_RESIZE_NS: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE;
+ case YASL_PRIVATE_CURSOR_RESIZE_NWSE: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE;
+ case YASL_PRIVATE_CURSOR_RESIZE_NESW: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE;
default: return WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT;
}
}
@@ -155,11 +167,11 @@ static uint32_t cursor_shape_of(enum yasl_cursor shape)
/* Relative mode hides the pointer but keeps w->cursor for the return to NORMAL. */
static void seat_set_cursor(struct seat *s, struct wayland_surface *w)
{
- enum yasl_cursor shape = w->relative ? YASL_CURSOR_HIDDEN : w->cursor;
+ enum yasl_private_cursor shape = w->relative ? YASL_PRIVATE_CURSOR_HIDDEN : w->cursor;
if (!s->pointer || !s->pointer_serial || s->pointer_focus != w ||
s->cursor_shape == (int)shape)
return;
- if (shape == YASL_CURSOR_HIDDEN)
+ if (shape == YASL_PRIVATE_CURSOR_HIDDEN)
wl_pointer_set_cursor(s->pointer, s->pointer_serial, NULL, 0, 0);
else if (s->cursor_shape_dev)
wp_cursor_shape_device_v1_set_shape(s->cursor_shape_dev,
@@ -169,20 +181,20 @@ static void seat_set_cursor(struct seat *s, struct wayland_surface *w)
s->cursor_shape = shape;
}
-static void tool_set_cursor(struct tablet_tool *t, enum yasl_cursor shape)
+static void tool_set_cursor(struct tablet_tool *t, enum yasl_private_cursor shape)
{
if (!t->proximity_serial)
return;
- if (shape == YASL_CURSOR_HIDDEN)
+ if (shape == YASL_PRIVATE_CURSOR_HIDDEN)
zwp_tablet_tool_v2_set_cursor(t->proxy, t->proximity_serial, NULL, 0, 0);
else if (t->shape_dev)
wp_cursor_shape_device_v1_set_shape(t->shape_dev, t->proximity_serial,
cursor_shape_of(shape));
}
-void wayland_surface_set_cursor(struct wayland_surface *w, enum yasl_cursor shape)
+void wayland_surface_set_cursor(struct wayland_surface *w, enum yasl_private_cursor shape)
{
- if (!w || !w->conn || w->destroyed || (unsigned)shape > YASL_CURSOR_HIDDEN) return;
+ if (!w || !w->conn || w->destroyed || (unsigned)shape > YASL_PRIVATE_CURSOR_HIDDEN) return;
w->cursor = shape;
for (struct seat *s = w->conn->seats; s; s = s->next) {
seat_set_cursor(s, w);
@@ -193,7 +205,7 @@ void wayland_surface_set_cursor(struct wayland_surface *w, enum yasl_cursor shap
static void press_queue_flush(struct seat *s, struct press_queue *q,
struct wayland_surface *only, uint64_t *buttons,
- struct yasl_pointer_event like)
+ struct yasl_private_pointer_event like)
{
int kept = 0;
int count = q->n;
@@ -206,10 +218,10 @@ static void press_queue_flush(struct seat *s, struct press_queue *q,
if (!p.surface) continue;
uint64_t mask = 1ull << p.button;
*buttons = (*buttons & ~mask) | ((uint64_t)(!p.release) << p.button);
- struct yasl_pointer_event ev = like;
+ struct yasl_private_pointer_event ev = like;
ev.pos = p.pos;
- ev.button = (enum yasl_pointer_button)p.button;
- enum yasl_event_type type = p.release ? YASL_EVENT_POINTER_UP : YASL_EVENT_POINTER_DOWN;
+ ev.button = (enum yasl_private_pointer_button)p.button;
+ enum yasl_private_event_type type = p.release ? YASL_PRIVATE_EVENT_POINTER_UP : YASL_PRIVATE_EVENT_POINTER_DOWN;
push_pointer(s, p.surface, type, ev);
}
q->head = 0;
@@ -217,7 +229,7 @@ static void press_queue_flush(struct seat *s, struct press_queue *q,
}
static void press_queue_push(struct press_queue *q, struct wayland_surface *w,
- struct yasl_vector2i pos, int button, bool release)
+ struct yasl_private_vector2i pos, int button, bool release)
{
if (q->n == 8) {
q->head = (q->head + 1) & 7;
@@ -244,10 +256,15 @@ void seat_pointer_reset(struct seat *s)
s->pointer_serial = 0;
s->motion_pending = false;
memset(s->axis, 0, sizeof(s->axis));
+ memset(s->axis_pixels, 0, sizeof(s->axis_pixels));
memset(s->axis_value120, 0, sizeof(s->axis_value120));
memset(s->axis_discrete, 0, sizeof(s->axis_discrete));
memset(s->axis_has_value120, 0, sizeof(s->axis_has_value120));
memset(s->axis_has_discrete, 0, sizeof(s->axis_has_discrete));
+ s->axis_stop_pending = false;
+ s->scroll_gesture = false;
+ s->axis_phase = 0;
+ s->axis_source = WL_POINTER_AXIS_SOURCE_WHEEL;
s->cursor_shape = -1;
}
@@ -263,7 +280,7 @@ static void flush_pending_motion(struct seat *s, struct wayland_surface *w)
{
if (!s->motion_pending) return;
s->motion_pending = false;
- push_pointer(s, w, YASL_EVENT_POINTER_MOTION, (struct yasl_pointer_event){
+ push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_MOTION, (struct yasl_private_pointer_event){
.pos = s->pointer_pos,
});
}
@@ -356,6 +373,13 @@ static void pointer_axis(void *data, struct wl_pointer *pointer,
double v = wl_fixed_to_double(value);
if (likely(axis < 2)) {
s->axis[axis] += v;
+ s->axis_pixels[axis] += v;
+ if (s->axis_source == WL_POINTER_AXIS_SOURCE_FINGER ||
+ s->axis_source == WL_POINTER_AXIS_SOURCE_CONTINUOUS) {
+ if (!s->scroll_gesture) s->axis_phase = 1u;
+ else if (s->axis_phase != 1u) s->axis_phase = 2u;
+ s->scroll_gesture = true;
+ }
}
pointer_event_done(s);
}
@@ -363,13 +387,14 @@ static void pointer_axis(void *data, struct wl_pointer *pointer,
static void pointer_axis_source(void *data, struct wl_pointer *pointer, uint32_t axis_source)
{
(void)pointer;
- (void)data; (void)axis_source;
+ ((struct seat *)data)->axis_source = axis_source;
}
static void pointer_axis_stop(void *data, struct wl_pointer *pointer, uint32_t time, uint32_t axis)
{
/* A stop ends a gesture, not the offsets already received in this frame. */
- (void)data; (void)pointer; (void)time; (void)axis;
+ (void)pointer; (void)time; (void)axis;
+ ((struct seat *)data)->axis_stop_pending = true;
}
static void pointer_axis_discrete(void *data, struct wl_pointer *pointer, uint32_t axis, int32_t discrete)
@@ -405,7 +430,28 @@ static void pointer_frame_scroll(struct seat *s, struct wayland_surface *w)
s->axis_discrete[axis] = 0;
s->axis_has_value120[axis] = s->axis_has_discrete[axis] = false;
}
- report_wheel(s, w, s->pointer_pos, lines[1], lines[0]);
+ bool stopped = s->axis_stop_pending;
+ unsigned source = scroll_source_of(s->axis_source);
+ unsigned phase = source == 2 || source == 3
+ ? (stopped ? 3u : s->axis_phase) : 0u;
+ bool has_pixels = (source == 2 || source == 3) &&
+ (s->axis_pixels[0] != 0 || s->axis_pixels[1] != 0);
+ if (lines[0] != 0 || lines[1] != 0 || stopped) {
+ push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_SCROLL,
+ (struct yasl_private_pointer_event){
+ .pos = s->pointer_pos,
+ .scroll = {lines[1], lines[0]},
+ .pixel_scroll = {s->axis_pixels[1] * w->scale_factor,
+ s->axis_pixels[0] * w->scale_factor},
+ .scroll_valid = 1u | (has_pixels ? 2u : 0u),
+ .scroll_source = source,
+ .scroll_phase = phase,
+ });
+ }
+ memset(s->axis_pixels, 0, sizeof(s->axis_pixels));
+ s->axis_stop_pending = false;
+ if (stopped) s->scroll_gesture = false;
+ s->axis_phase = 0;
}
static void pointer_frame(void *data, struct wl_pointer *pointer)
@@ -416,14 +462,14 @@ static void pointer_frame(void *data, struct wl_pointer *pointer)
if (left) {
s->ptr_left = NULL;
if (s->ptr_left_motion)
- push_pointer(s, left, YASL_EVENT_POINTER_MOTION, (struct yasl_pointer_event){
+ push_pointer(s, left, YASL_PRIVATE_EVENT_POINTER_MOTION, (struct yasl_private_pointer_event){
.pos = s->ptr_left_pos,
});
- press_queue_flush(s, &s->ptr_presses, left, &s->pointer_buttons, (struct yasl_pointer_event){0});
- push_pointer(s, left, YASL_EVENT_POINTER_LEAVE, (struct yasl_pointer_event){
+ press_queue_flush(s, &s->ptr_presses, left, &s->pointer_buttons, (struct yasl_private_pointer_event){0});
+ push_pointer(s, left, YASL_PRIVATE_EVENT_POINTER_LEAVE, (struct yasl_private_pointer_event){
.pos = s->ptr_left_pos,
});
- struct yasl_vector2i pos = s->pointer_pos;
+ struct yasl_private_vector2i pos = s->pointer_pos;
bool motion = s->motion_pending;
uint32_t serial = s->pointer_focus ? s->pointer_serial : 0;
int shape = s->pointer_focus ? s->cursor_shape : -1;
@@ -440,7 +486,7 @@ static void pointer_frame(void *data, struct wl_pointer *pointer)
}
flush_pending_motion(s, w);
- press_queue_flush(s, &s->ptr_presses, NULL, &s->pointer_buttons, (struct yasl_pointer_event){0});
+ press_queue_flush(s, &s->ptr_presses, NULL, &s->pointer_buttons, (struct yasl_private_pointer_event){0});
pointer_frame_scroll(s, w);
}
@@ -460,7 +506,7 @@ static void relative_motion(void *data, struct zwp_relative_pointer_v1 *pointer,
/* Unlocked movement arrives as wl_pointer.motion; the queue derives its delta. */
if (!w || !w->pointer_locked || w->lock_seat != s) return;
flush_pending_motion(s, w);
- push_pointer(s, w, YASL_EVENT_POINTER_MOTION, (struct yasl_pointer_event){
+ push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_MOTION, (struct yasl_private_pointer_event){
.pos = s->pointer_pos,
.delta = {wl_fixed_to_double(dx) * w->scale_factor,
wl_fixed_to_double(dy) * w->scale_factor},
@@ -525,12 +571,12 @@ static void seat_lock_surfaces(struct seat *s)
surface_lock_pointer(w, s);
}
-bool wayland_surface_set_pointer_mode(struct wayland_surface *w, enum yasl_pointer_mode mode)
+bool wayland_surface_set_pointer_mode(struct wayland_surface *w, enum yasl_private_pointer_mode mode)
{
if (!w || !w->conn || w->destroyed)
return false;
struct wayland_display *c = w->conn;
- bool relative = mode == YASL_POINTER_RELATIVE;
+ bool relative = mode == YASL_PRIVATE_POINTER_RELATIVE;
if (relative && (!c->relative_pointer_mgr || !c->pointer_constraints))
return false;
w->relative = relative;
@@ -572,10 +618,10 @@ static struct touch_point *touch_find(struct seat *s, int32_t id)
return NULL;
}
-static struct yasl_vector2i touch_pos(const struct touch_point *p)
+static struct yasl_private_vector2i touch_pos(const struct touch_point *p)
{
double scale = p->surface->scale_factor;
- return (struct yasl_vector2i){
+ return (struct yasl_private_vector2i){
.x = (int)lrint(p->x * scale), .y = (int)lrint(p->y * scale),
};
}
@@ -583,11 +629,11 @@ static struct yasl_vector2i touch_pos(const struct touch_point *p)
static void touch_report(struct seat *s, struct touch_point *p, int button,
- enum yasl_event_type type)
+ enum yasl_private_event_type type)
{
- push_pointer(s, p->surface, type, (struct yasl_pointer_event){
- .pos = touch_pos(p), .button = (enum yasl_pointer_button)button,
- .device = YASL_PTR_DEV_TOUCH,
+ push_pointer(s, p->surface, type, (struct yasl_private_pointer_event){
+ .pos = touch_pos(p), .button = (enum yasl_private_pointer_button)button,
+ .device = YASL_PRIVATE_PTR_DEV_TOUCH,
});
}
@@ -644,7 +690,7 @@ static void touch_frame(void *data, struct wl_touch *touch)
if (p->down && !s->touch_primary && !s->touch_scrolling) {
s->touch_primary = p;
s->touch_pressed = true;
- touch_report(s, p, 0, YASL_EVENT_POINTER_DOWN);
+ touch_report(s, p, 0, YASL_PRIVATE_EVENT_POINTER_DOWN);
}
if (!p->up) {
active_n++;
@@ -657,7 +703,7 @@ static void touch_frame(void *data, struct wl_touch *touch)
struct touch_point *primary = s->touch_primary;
if (primary && !s->touch_scrolling && active_n >= 2) {
if (s->touch_pressed)
- touch_report(s, primary, 0, YASL_EVENT_POINTER_UP);
+ touch_report(s, primary, 0, YASL_PRIVATE_EVENT_POINTER_UP);
s->touch_pressed = false;
s->touch_scrolling = true;
s->touch_scroll_y = centroid_n ? centroid_sum / centroid_n : 0;
@@ -667,14 +713,14 @@ static void touch_frame(void *data, struct wl_touch *touch)
s->touch_scroll_y = y;
if (dy != 0.0) {
double lines = dy / SCROLL_PX_PER_LINE;
- push_pointer(s, primary->surface, YASL_EVENT_POINTER_SCROLL, (struct yasl_pointer_event){
+ push_pointer(s, primary->surface, YASL_PRIVATE_EVENT_POINTER_SCROLL, (struct yasl_private_pointer_event){
.pos = touch_pos(primary),
.scroll = {0, -lines},
- .device = YASL_PTR_DEV_TOUCH,
+ .device = YASL_PRIVATE_PTR_DEV_TOUCH,
});
}
} else if (primary && primary->moved && s->touch_pressed) {
- touch_report(s, primary, 0, YASL_EVENT_POINTER_MOTION);
+ touch_report(s, primary, 0, YASL_PRIVATE_EVENT_POINTER_MOTION);
}
for (int i = 0; i < SEAT_TOUCH_POINTS; i++) {
struct touch_point *p = &s->touch_points[i];
@@ -683,7 +729,7 @@ static void touch_frame(void *data, struct wl_touch *touch)
active_n--;
if (p == s->touch_primary) {
if (s->touch_pressed)
- touch_report(s, p, 0, YASL_EVENT_POINTER_UP);
+ touch_report(s, p, 0, YASL_PRIVATE_EVENT_POINTER_UP);
s->touch_pressed = false;
s->touch_primary = NULL;
}
@@ -701,7 +747,7 @@ static void touch_cancel(void *data, struct wl_touch *touch)
(void)touch;
struct seat *s = data;
if (s->touch_primary && s->touch_pressed)
- touch_report(s, s->touch_primary, 0, YASL_EVENT_POINTER_UP);
+ touch_report(s, s->touch_primary, 0, YASL_PRIVATE_EVENT_POINTER_UP);
memset(s->touch_points, 0, sizeof(s->touch_points));
s->touch_primary = NULL;
s->touch_pressed = false;
@@ -719,29 +765,100 @@ static const struct wl_touch_listener touch_listener = {
.frame = touch_frame, .cancel = touch_cancel,
};
-static struct yasl_pointer_event tool_event(const struct seat *s)
+static struct yasl_private_pointer_event tool_event(const struct seat *s)
{
- return (struct yasl_pointer_event){
- .device = s->tool && s->tool->eraser ? YASL_PTR_DEV_ERASER : YASL_PTR_DEV_PEN,
+ return (struct yasl_private_pointer_event){
+ .device = s->tool && s->tool->eraser ? YASL_PRIVATE_PTR_DEV_ERASER : YASL_PRIVATE_PTR_DEV_PEN,
.pen = s->pen,
};
}
+static void push_pen_sample(struct tablet_tool *tool, struct wayland_surface *w,
+ unsigned action, uint32_t changed)
+{
+ struct seat *s = tool->seat;
+ if (!w) return;
+ uint64_t buttons = s->tool_buttons;
+ for (int i = 0; i < s->tool_presses.n; i++) {
+ const struct pending_press *press = &s->tool_presses.ev[(s->tool_presses.head + i) & 7];
+ if (press->surface && press->button >= 0 && press->button < 32) {
+ uint64_t bit = 1ull << press->button;
+ buttons = (buttons & ~bit) | ((uint64_t)!press->release << press->button);
+ }
+ }
+ if (action == YASL_PRIVATE_PEN_LEAVE || action == YASL_PRIVATE_PEN_CANCEL)
+ buttons = 0;
+ struct yasl_private_event ev = {
+ .type = YASL_PRIVATE_EVENT_PEN,
+ .surface = &w->base,
+ .pen = {
+ .device_id = tool->device_id,
+ .tool_id = tool->tool_id,
+ .x = tool->x,
+ .y = tool->y,
+ .pressure = tool->pressure,
+ .tilt_x = tool->tilt_x,
+ .tilt_y = tool->tilt_y,
+ .rotation = tool->rotation,
+ .distance = tool->distance,
+ .tangential = tool->tangential,
+ .wheel_degrees = tool->wheel_degrees,
+ .axes_valid = tool->axes_valid,
+ .axes_changed = changed,
+ .buttons = (uint32_t)buttons,
+ .changed_button = tool->changed_button,
+ .in_contact = tool->in_contact,
+ .tool = tool->kind,
+ .action = action,
+ },
+ };
+ input_queue_push(&surface_root(w)->input, &ev);
+}
+
+static void pen_action_push(struct tablet_tool *tool, unsigned action, unsigned button)
+{
+ if (tool->pending_action_count == 8) {
+ memmove(tool->pending_actions, tool->pending_actions + 1,
+ 7 * sizeof(tool->pending_actions[0]));
+ memmove(tool->pending_buttons, tool->pending_buttons + 1,
+ 7 * sizeof(tool->pending_buttons[0]));
+ memmove(tool->pending_contacts, tool->pending_contacts + 1,
+ 7 * sizeof(tool->pending_contacts[0]));
+ tool->pending_action_count--;
+ }
+ unsigned i = tool->pending_action_count++;
+ tool->pending_actions[i] = action;
+ tool->pending_buttons[i] = button;
+ tool->pending_contacts[i] = tool->in_contact;
+}
+
static void tablet_tool_proximity_in(void *data,
struct zwp_tablet_tool_v2 *tool, uint32_t serial,
struct zwp_tablet_v2 *tablet, struct wl_surface *surface)
{
- (void)tool; (void)tablet;
+ (void)tool;
struct tablet_tool *t = data;
struct seat *s = t->seat;
struct wayland_surface *w = surface ?
(struct wayland_surface *)wl_surface_get_user_data(surface) : NULL;
s->tablet_focus = w;
s->tool = t;
+ t->device_id = 0;
+ for (struct tablet_device *device = s->tablet_devices; device; device = device->next)
+ if (device->proxy == tablet) { t->device_id = device->id; break; }
+ t->pen_in_proximity = true;
+ t->in_contact = false;
+ t->pending_action_count = 0;
+ pen_action_push(t, YASL_PRIVATE_PEN_ENTER, 0);
+ t->axes_changed = 0;
+ t->pressure = t->tilt_x = t->tilt_y = t->rotation = t->distance = 0;
+ t->tangential = t->wheel_degrees = 0;
s->tool_buttons = 0;
- s->pen = (struct yasl_pointer_pen){0};
+ s->pen = (struct yasl_private_pointer_pen){0};
t->proximity_serial = serial;
if (!w) return;
+ t->x = s->tool_pos.x;
+ t->y = s->tool_pos.y;
if (s->conn->cursor_shape_mgr && !t->shape_dev)
t->shape_dev = wp_cursor_shape_manager_v1_get_tablet_tool_v2(
s->conn->cursor_shape_mgr, t->proxy);
@@ -756,11 +873,14 @@ static void tablet_tool_proximity_out(void *data, struct zwp_tablet_tool_v2 *too
struct seat *s = t->seat;
struct wayland_surface *w = s->tablet_focus;
if (w) {
+ t->in_contact = false;
+ t->pressure = 0;
+ push_pen_sample(t, w, YASL_PRIVATE_PEN_LEAVE, 0);
press_queue_flush(s, &s->tool_presses, NULL, &s->tool_buttons, tool_event(s));
s->pen.pressure = 0;
- struct yasl_pointer_event ev = tool_event(s);
+ struct yasl_private_pointer_event ev = tool_event(s);
ev.pos = s->tool_pos;
- push_pointer(s, w, YASL_EVENT_POINTER_LEAVE, ev);
+ push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_LEAVE, ev);
}
s->tool_presses.n = 0;
s->tablet_focus = NULL;
@@ -769,28 +889,38 @@ static void tablet_tool_proximity_out(void *data, struct zwp_tablet_tool_v2 *too
s->tool_wheel = 0;
s->tool_buttons = 0;
t->proximity_serial = 0;
+ t->pen_in_proximity = false;
+ t->in_contact = false;
}
static void tablet_tool_down(void *data, struct zwp_tablet_tool_v2 *tool, uint32_t serial)
{
(void)tool;
- struct seat *s = ((struct tablet_tool *)data)->seat;
+ struct tablet_tool *t = data;
+ struct seat *s = t->seat;
struct wayland_surface *w = s->tablet_focus;
if (!w) return;
seat_note_input(s, serial);
s->press_serial = serial;
s->press_surface = w;
press_queue_push(&s->tool_presses, w, s->tool_pos, 0, false);
+ t->in_contact = true;
+ pen_action_push(t, YASL_PRIVATE_PEN_DOWN, 0);
}
static void tablet_tool_up(void *data, struct zwp_tablet_tool_v2 *tool)
{
(void)tool;
struct seat *s = ((struct tablet_tool *)data)->seat;
+ struct tablet_tool *t = data;
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);
+ t->pressure = 0;
+ t->in_contact = false;
+ pen_action_push(t, YASL_PRIVATE_PEN_UP, 0);
+ t->axes_changed |= YASL_PRIVATE_PEN_PRESSURE;
}
static void tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *tool, wl_fixed_t x, wl_fixed_t y)
@@ -800,6 +930,9 @@ static void tablet_tool_motion(void *data, struct zwp_tablet_tool_v2 *tool, wl_f
struct wayland_surface *w = s->tablet_focus;
if (!w) return;
s->tool_pos = device_from_fixed(w, x, y);
+ struct tablet_tool *owned = data;
+ owned->x = wl_fixed_to_double(x) * w->scale_factor;
+ owned->y = wl_fixed_to_double(y) * w->scale_factor;
s->tool_motion = true;
}
@@ -807,7 +940,8 @@ static void tablet_tool_button(void *data, struct zwp_tablet_tool_v2 *tool,
uint32_t serial, uint32_t button, uint32_t state)
{
(void)tool;
- struct seat *s = ((struct tablet_tool *)data)->seat;
+ struct tablet_tool *t = data;
+ struct seat *s = t->seat;
struct wayland_surface *w = s->tablet_focus;
if (!w) return;
seat_note_input(s, serial);
@@ -820,13 +954,18 @@ static void tablet_tool_button(void *data, struct zwp_tablet_tool_v2 *tool,
}
bool release = state == ZWP_TABLET_TOOL_V2_BUTTON_STATE_RELEASED;
press_queue_push(&s->tool_presses, w, s->tool_pos, b, release);
+ pen_action_push(t, YASL_PRIVATE_PEN_BUTTON, (unsigned)b);
}
static void tool_pressure(void *d, struct zwp_tablet_tool_v2 *t, uint32_t v)
{
(void)t;
struct seat *s = ((struct tablet_tool *)d)->seat;
+ struct tablet_tool *owned = d;
s->pen.pressure = v > 65535 ? 65535 : (uint16_t)v;
+ owned->pressure = (double)(v > 65535 ? 65535 : v) / 65535.0;
+ owned->axes_valid |= YASL_PRIVATE_PEN_PRESSURE;
+ owned->axes_changed |= YASL_PRIVATE_PEN_PRESSURE;
s->tool_motion = true;
}
@@ -840,40 +979,68 @@ static void tool_tilt(void *d, struct zwp_tablet_tool_v2 *t, wl_fixed_t x, wl_fi
{
(void)t;
struct seat *s = ((struct tablet_tool *)d)->seat;
+ struct tablet_tool *owned = d;
s->pen.tilt_x = tilt_degrees(x);
s->pen.tilt_y = tilt_degrees(y);
+ owned->tilt_x = wl_fixed_to_double(x);
+ owned->tilt_y = wl_fixed_to_double(y);
+ owned->axes_valid |= YASL_PRIVATE_PEN_TILT;
+ owned->axes_changed |= YASL_PRIVATE_PEN_TILT;
s->tool_motion = true;
}
static void tool_wheel(void *d, struct zwp_tablet_tool_v2 *t, wl_fixed_t deg, int32_t clicks)
{
- (void)t; (void)deg;
- ((struct tablet_tool *)d)->seat->tool_wheel += clicks;
+ (void)t;
+ struct tablet_tool *owned = d;
+ owned->seat->tool_wheel += clicks;
+ owned->wheel_degrees += wl_fixed_to_double(deg);
+ owned->axes_valid |= YASL_PRIVATE_PEN_WHEEL;
+ owned->axes_changed |= YASL_PRIVATE_PEN_WHEEL;
}
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 tablet_tool *owned = d;
struct wayland_surface *w = s->tablet_focus;
if (!w) {
s->tool_presses.n = 0;
s->tool_wheel = 0;
return;
}
- struct yasl_pointer_event like = tool_event(s);
+ struct yasl_private_pointer_event like = tool_event(s);
+ if (owned->pending_action_count) {
+ unsigned actions = owned->pending_action_count;
+ bool contact = owned->in_contact;
+ for (unsigned i = 0; i < actions; i++) {
+ owned->changed_button = owned->pending_buttons[i];
+ owned->in_contact = owned->pending_contacts[i];
+ push_pen_sample(owned, w, owned->pending_actions[i],
+ i + 1 == actions ? owned->axes_changed : 0);
+ }
+ owned->in_contact = contact;
+ owned->pending_action_count = 0;
+ owned->axes_changed = 0;
+ owned->changed_button = 0;
+ } else if (s->tool_motion || owned->axes_changed) {
+ push_pen_sample(owned, w, YASL_PRIVATE_PEN_MOVE, owned->axes_changed);
+ owned->axes_changed = 0;
+ owned->changed_button = 0;
+ }
if (s->tool_motion) {
- struct yasl_pointer_event ev = like;
+ struct yasl_private_pointer_event ev = like;
ev.pos = s->tool_pos;
- push_pointer(s, w, YASL_EVENT_POINTER_MOTION, ev);
+ push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_MOTION, ev);
s->tool_motion = false;
}
press_queue_flush(s, &s->tool_presses, NULL, &s->tool_buttons, like);
if (s->tool_wheel) {
- struct yasl_pointer_event ev = like;
+ struct yasl_private_pointer_event ev = like;
ev.pos = s->tool_pos;
ev.scroll.y = s->tool_wheel;
- push_pointer(s, w, YASL_EVENT_POINTER_SCROLL, ev);
+ push_pointer(s, w, YASL_PRIVATE_EVENT_POINTER_SCROLL, ev);
s->tool_wheel = 0;
}
}
@@ -899,16 +1066,43 @@ static void tablet_tool_removed(void *data, struct zwp_tablet_tool_v2 *tool)
static void tool_type(void *d, struct zwp_tablet_tool_v2 *t, uint32_t type)
{
(void)t;
- ((struct tablet_tool *)d)->eraser = type == ZWP_TABLET_TOOL_V2_TYPE_ERASER;
+ struct tablet_tool *tool = d;
+ tool->eraser = type == ZWP_TABLET_TOOL_V2_TYPE_ERASER;
+ switch (type) {
+ case ZWP_TABLET_TOOL_V2_TYPE_PEN: tool->kind = 1; break;
+ case ZWP_TABLET_TOOL_V2_TYPE_ERASER: tool->kind = 2; break;
+ case ZWP_TABLET_TOOL_V2_TYPE_BRUSH: tool->kind = 3; break;
+ case ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH: tool->kind = 4; break;
+ case ZWP_TABLET_TOOL_V2_TYPE_PENCIL: tool->kind = 5; break;
+ default: tool->kind = 0; break;
+ }
}
-static void tool_hardware_serial(void *d, struct zwp_tablet_tool_v2 *t, uint32_t hi, uint32_t lo) { (void)d; (void)t; (void)hi; (void)lo; }
+static void tool_hardware_serial(void *d, struct zwp_tablet_tool_v2 *t, uint32_t hi, uint32_t lo)
+{ (void)t; struct tablet_tool *tool = d; uint64_t id = ((uint64_t)hi << 32) | lo; if (id) tool->tool_id = id; }
static void tool_hardware_id_wacom(void *d, struct zwp_tablet_tool_v2 *t, uint32_t hi, uint32_t lo) { (void)d; (void)t; (void)hi; (void)lo; }
-static void tool_capability(void *d, struct zwp_tablet_tool_v2 *t, uint32_t cap) { (void)d; (void)t; (void)cap; }
+static void tool_capability(void *d, struct zwp_tablet_tool_v2 *t, uint32_t cap)
+{
+ (void)t;
+ struct tablet_tool *tool = d;
+ tool->capability_mask |= 1u << cap;
+ switch (cap) {
+ case ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE: tool->axes_valid |= YASL_PRIVATE_PEN_PRESSURE; break;
+ case ZWP_TABLET_TOOL_V2_CAPABILITY_TILT: tool->axes_valid |= YASL_PRIVATE_PEN_TILT; break;
+ case ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION: tool->axes_valid |= YASL_PRIVATE_PEN_ROTATION; break;
+ case ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE: tool->axes_valid |= YASL_PRIVATE_PEN_DISTANCE; break;
+ case ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER: tool->axes_valid |= YASL_PRIVATE_PEN_TANGENTIAL; break;
+ case ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL: tool->axes_valid |= YASL_PRIVATE_PEN_WHEEL; break;
+ default: break;
+ }
+}
static void tool_done(void *d, struct zwp_tablet_tool_v2 *t) { (void)d; (void)t; }
-static void tool_distance(void *d, struct zwp_tablet_tool_v2 *t, uint32_t v) { (void)d; (void)t; (void)v; }
-static void tool_rotation(void *d, struct zwp_tablet_tool_v2 *t, wl_fixed_t deg) { (void)d; (void)t; (void)deg; }
-static void tool_slider(void *d, struct zwp_tablet_tool_v2 *t, int32_t pos) { (void)d; (void)t; (void)pos; }
+static void tool_distance(void *d, struct zwp_tablet_tool_v2 *t, uint32_t v)
+{ (void)t; struct tablet_tool *tool = d; tool->distance = (double)(v > 65535 ? 65535 : v) / 65535.0; tool->axes_valid |= YASL_PRIVATE_PEN_DISTANCE; tool->axes_changed |= YASL_PRIVATE_PEN_DISTANCE; }
+static void tool_rotation(void *d, struct zwp_tablet_tool_v2 *t, wl_fixed_t deg)
+{ (void)t; struct tablet_tool *tool = d; tool->rotation = wl_fixed_to_double(deg); tool->axes_valid |= YASL_PRIVATE_PEN_ROTATION; tool->axes_changed |= YASL_PRIVATE_PEN_ROTATION; }
+static void tool_slider(void *d, struct zwp_tablet_tool_v2 *t, int32_t pos)
+{ (void)t; struct tablet_tool *tool = d; tool->tangential = pos / 65535.0; if (tool->tangential < -1) tool->tangential = -1; if (tool->tangential > 1) tool->tangential = 1; tool->axes_valid |= YASL_PRIVATE_PEN_TANGENTIAL; tool->axes_changed |= YASL_PRIVATE_PEN_TANGENTIAL; }
static const struct zwp_tablet_tool_v2_listener tablet_tool_listener = {
.type = tool_type, .hardware_serial = tool_hardware_serial,
@@ -997,6 +1191,7 @@ static void tablet_seat_tablet_added(void *data, struct zwp_tablet_seat_v2 *tabl
struct tablet_device *owned = calloc(1, sizeof(*owned));
if (!owned) { zwp_tablet_v2_destroy(tablet); return; }
owned->proxy = tablet;
+ owned->id = ++s->conn->next_tool_id;
owned->next = s->tablet_devices;
s->tablet_devices = owned;
zwp_tablet_v2_add_listener(tablet, &tablet_listener, s);
@@ -1010,6 +1205,7 @@ static void tablet_seat_tool_added(void *data, struct zwp_tablet_seat_v2 *tablet
if (!owned) { zwp_tablet_tool_v2_destroy(tool); return; }
owned->proxy = tool;
owned->seat = s;
+ owned->tool_id = ++s->conn->next_tool_id;
owned->next = s->tablet_tools;
s->tablet_tools = owned;
zwp_tablet_tool_v2_add_listener(tool, &tablet_tool_listener, owned);
@@ -1035,7 +1231,7 @@ static const struct zwp_tablet_seat_v2_listener tablet_seat_listener = {
static void clipboard_received(const char *text, size_t len, bool uri_list,
struct wl_surface *on, uint64_t request_id,
- enum yasl_transfer_status status, void *ud)
+ enum yasl_private_transfer_status status, void *ud)
{
(void)ud;
struct wayland_surface *w = on ?
@@ -1043,13 +1239,13 @@ static void clipboard_received(const char *text, size_t len, bool uri_list,
if (uri_list) w = surface_root(w);
if (!w || w->closed || w->destroyed) return;
if (!uri_list) {
- yasl_surface_complete_paste(&w->base, request_id, status, text, len);
+ yasl_private_surface_complete_paste(&w->base, request_id, status, text, len);
return;
}
- if (status != YASL_TRANSFER_OK) return;
- struct yasl_event ev = {
+ if (status != YASL_PRIVATE_TRANSFER_OK) return;
+ struct yasl_private_event ev = {
.surface = &w->base,
- .type = uri_list ? YASL_EVENT_DROP : YASL_EVENT_PASTE,
+ .type = uri_list ? YASL_PRIVATE_EVENT_DROP : YASL_PRIVATE_EVENT_PASTE,
.text = {
.data = text,
.len = len,
@@ -1058,14 +1254,14 @@ static void clipboard_received(const char *text, size_t len, bool uri_list,
input_queue_push(&w->input, &ev);
}
-static void clipboard_drag(enum yasl_drag_event ev, struct wl_surface *on,
+static void clipboard_drag(enum yasl_private_drag_event ev, struct wl_surface *on,
double x, double y, void *ud)
{
struct seat *s = ud;
conn_drag_target(s->conn, ev, on, x, y);
}
-static void clipboard_drop_hover(enum yasl_drop_event ev, struct wl_surface *on,
+static void clipboard_drop_hover(enum yasl_private_drop_event ev, struct wl_surface *on,
double x, double y, void *ud)
{
struct seat *s = ud;
@@ -1152,6 +1348,16 @@ static void seat_drop_touch(struct seat *s)
}
}
+void seat_ensure_text_input(struct seat *s)
+{
+ if (!s || !s->conn->text_input_mgr || s->text_input || !s->proxy)
+ return;
+ s->text_input = zwp_text_input_manager_v3_get_text_input(
+ s->conn->text_input_mgr, s->proxy);
+ if (s->text_input)
+ zwp_text_input_v3_add_listener(s->text_input, &text_input_listener, s);
+}
+
static void seat_capabilities(void *data, struct wl_seat *proxy, uint32_t caps)
{
struct seat *s = data;
@@ -1209,11 +1415,7 @@ static void seat_capabilities(void *data, struct wl_seat *proxy, uint32_t caps)
zwp_tablet_seat_v2_add_listener(s->tablet_seat, &tablet_seat_listener, s);
}
- if (c->text_input_mgr && !s->text_input) {
- s->text_input = zwp_text_input_manager_v3_get_text_input(c->text_input_mgr, proxy);
- if (s->text_input)
- zwp_text_input_v3_add_listener(s->text_input, &text_input_listener, s);
- }
+ seat_ensure_text_input(s);
seat_clipboard_ensure(s);
}
@@ -1297,8 +1499,8 @@ void seat_destroy(struct seat *s)
seat_drop_keyboard(s);
struct wayland_surface *left = s->focus_leaving;
if (left && !conn_keyboard_on(c, left, NULL)) {
- struct yasl_event ev = {
- .type = YASL_EVENT_FOCUS,
+ struct yasl_private_event ev = {
+ .type = YASL_PRIVATE_EVENT_FOCUS,
.surface = &left->base,
.focus = { .focused = false },
};
@@ -1306,7 +1508,7 @@ void seat_destroy(struct seat *s)
}
seat_drop_touch(s);
clipboard_destroy(s->clipboard);
- if (s->repeat_watch > 0) yasl_loop_del_fd(c->loop, s->repeat_watch);
+ if (s->repeat_watch > 0) yasl_private_loop_del_fd(c->loop, s->repeat_watch);
if (s->repeat_fd >= 0) close(s->repeat_fd);
if (s->cursor_viewport) wp_viewport_destroy(s->cursor_viewport);
if (s->cursor_surface) wl_surface_destroy(s->cursor_surface);
diff --git a/src/platform/wayland/popup.c b/src/platform/wayland/popup.c
index 44c7206..c563eb4 100644
--- a/src/platform/wayland/popup.c
+++ b/src/platform/wayland/popup.c
@@ -24,9 +24,19 @@ static const struct xdg_popup_listener popup_listener = {
};
static bool popup_spec_valid(const struct wayland_surface *parent,
- const struct yasl_popup_spec *spec)
+ const struct yasl_private_popup_spec *spec)
{
if (!parent || !spec) return false;
+ if (spec->positioner_explicit &&
+ (spec->anchor_edges > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT ||
+ spec->gravity > XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT ||
+ (spec->constraints & ~(unsigned)(XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X |
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y |
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X |
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y |
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X |
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y))))
+ return false;
int64_t x = (int64_t)spec->anchor.x - parent->content.x;
int64_t y = (int64_t)spec->anchor.y - parent->content.y;
return x >= INT_MIN && x <= INT_MAX && y >= INT_MIN && y <= INT_MAX &&
@@ -37,29 +47,29 @@ static bool popup_spec_valid(const struct wayland_surface *parent,
(int64_t)spec->content_offset.y + spec->content_size.y <= spec->size.y;
}
-static bool popup_apply_spec(struct wayland_surface *w, const struct yasl_popup_spec *spec)
+static bool popup_apply_spec(struct wayland_surface *w, const struct yasl_private_popup_spec *spec)
{
uint32_t scale = w->parent->scale_120;
- struct yasl_vector2i logical = {
- .x = (int)(((int64_t)spec->size.x * YASL_SCALE_UNIT + scale - 1) / scale),
- .y = (int)(((int64_t)spec->size.y * YASL_SCALE_UNIT + scale - 1) / scale),
+ struct yasl_private_vector2i logical = {
+ .x = (int)(((int64_t)spec->size.x * YASL_PRIVATE_SCALE_UNIT + scale - 1) / scale),
+ .y = (int)(((int64_t)spec->size.y * YASL_PRIVATE_SCALE_UNIT + scale - 1) / scale),
};
int width = device_px(logical.x, scale);
int height = device_px(logical.y, scale);
size_t bytes;
if (logical.x <= 0 || logical.y <= 0 || !buffer_size(width, height, &bytes))
return false;
- struct yasl_vector2i content = spec->content_size;
+ struct yasl_private_vector2i content = spec->content_size;
if (!content.x) content.x = spec->size.x - spec->content_offset.x;
if (!content.y) content.y = spec->size.y - spec->content_offset.y;
bool rescaled = scale != w->scale_120;
w->scale_120 = w->pending_scale_120 = scale;
- w->scale_factor = (double)scale / YASL_SCALE_UNIT;
+ w->scale_factor = (double)scale / YASL_PRIVATE_SCALE_UNIT;
w->width = width;
w->height = height;
w->logical_width = logical.x;
w->logical_height = logical.y;
- w->content = (struct yasl_rect2i){
+ w->content = (struct yasl_private_rect2i){
.x = spec->content_offset.x, .y = spec->content_offset.y,
.width = content.x, .height = content.y
};
@@ -67,20 +77,20 @@ static bool popup_apply_spec(struct wayland_surface *w, const struct yasl_popup_
w->buffers[i].presented = false;
if (w->viewport)
wp_viewport_set_destination(w->viewport, logical.x, logical.y);
- else if ((rescaled || !w->configured) && scale % YASL_SCALE_UNIT == 0 &&
+ else if ((rescaled || !w->configured) && scale % YASL_PRIVATE_SCALE_UNIT == 0 &&
wl_proxy_get_version((struct wl_proxy *)w->surface) >=
WL_SURFACE_SET_BUFFER_SCALE_SINCE_VERSION)
- wl_surface_set_buffer_scale(w->surface, (int32_t)(scale / YASL_SCALE_UNIT));
+ wl_surface_set_buffer_scale(w->surface, (int32_t)(scale / YASL_PRIVATE_SCALE_UNIT));
return true;
}
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);
- int gy = yasl_scale_to_logical(w->content.y, scale);
- int gw = yasl_scale_to_logical(w->content.width, scale);
- int gh = yasl_scale_to_logical(w->content.height, scale);
+ int gx = yasl_private_scale_to_logical(w->content.x, scale);
+ int gy = yasl_private_scale_to_logical(w->content.y, scale);
+ int gw = yasl_private_scale_to_logical(w->content.width, scale);
+ int gh = yasl_private_scale_to_logical(w->content.height, scale);
if (gw < 1) gw = 1;
if (gh < 1) gh = 1;
if (gx + gw > w->logical_width)
@@ -91,41 +101,45 @@ static void popup_set_geometry(struct wayland_surface *w)
}
static struct xdg_positioner *popup_positioner(struct wayland_surface *w,
- const struct yasl_popup_spec *spec)
+ const struct yasl_private_popup_spec *spec)
{
struct wayland_surface *parent = w->parent;
struct xdg_positioner *pos = xdg_wm_base_create_positioner(w->conn->wm_base);
if (!pos)
return NULL;
uint32_t scale = parent->scale_120;
- int gw = yasl_scale_to_logical(w->content.width, scale);
- int gh = yasl_scale_to_logical(w->content.height, scale);
+ int gw = yasl_private_scale_to_logical(w->content.width, scale);
+ int gh = yasl_private_scale_to_logical(w->content.height, scale);
xdg_positioner_set_size(pos, gw > 0 ? gw : 1, gh > 0 ? gh : 1);
- struct yasl_vector2i at = {
+ struct yasl_private_vector2i at = {
.x = spec->anchor.x - parent->content.x,
.y = spec->anchor.y - parent->content.y,
};
- int aw = yasl_scale_to_logical(spec->anchor.width, scale);
- int ah = yasl_scale_to_logical(spec->anchor.height, scale);
+ int aw = yasl_private_scale_to_logical(spec->anchor.width, scale);
+ int ah = yasl_private_scale_to_logical(spec->anchor.height, scale);
xdg_positioner_set_anchor_rect(pos,
- yasl_scale_to_logical(at.x, scale),
- yasl_scale_to_logical(at.y, scale),
+ yasl_private_scale_to_logical(at.x, scale),
+ yasl_private_scale_to_logical(at.y, scale),
aw > 0 ? aw : 1, ah > 0 ? ah : 1);
- xdg_positioner_set_anchor(pos, spec->beside ? XDG_POSITIONER_ANCHOR_TOP_RIGHT
- : XDG_POSITIONER_ANCHOR_TOP_LEFT);
- xdg_positioner_set_gravity(pos, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
- xdg_positioner_set_constraint_adjustment(pos,
+ uint32_t anchor = spec->positioner_explicit ? spec->anchor_edges :
+ (spec->beside ? XDG_POSITIONER_ANCHOR_TOP_RIGHT : XDG_POSITIONER_ANCHOR_TOP_LEFT);
+ uint32_t gravity = spec->positioner_explicit ? spec->gravity :
+ XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT;
+ uint32_t constraints = spec->positioner_explicit ? spec->constraints :
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X |
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y |
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X |
- XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y);
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y;
+ xdg_positioner_set_anchor(pos, anchor);
+ xdg_positioner_set_gravity(pos, gravity);
+ xdg_positioner_set_constraint_adjustment(pos, constraints);
if (xdg_positioner_get_version(pos) >= XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION)
xdg_positioner_set_reactive(pos);
return pos;
}
struct wayland_surface *wayland_surface_create_popup(struct wayland_surface *parent,
- const struct yasl_popup_spec *spec)
+ const struct yasl_private_popup_spec *spec)
{
struct wayland_display *c = parent ? parent->conn : NULL;
struct seat *seat = c ? (c->active_seat ? c->active_seat : c->seats) : NULL;
@@ -137,7 +151,8 @@ struct wayland_surface *wayland_surface_create_popup(struct wayland_surface *par
if (!w)
return NULL;
w->base.display = &c->base;
- w->base.state = YASL_STATE_CSD;
+ w->base.state = YASL_PRIVATE_STATE_CSD;
+ w->base.presenter = YASL_PRIVATE_PRESENTER_CPU;
w->input.leave_ends_press = true;
w->conn = c;
w->conn_next = c->surfaces;
@@ -150,7 +165,7 @@ struct wayland_surface *wayland_surface_create_popup(struct wayland_surface *par
if (!w->surface)
goto err;
w->base.native_handle = w->surface;
- w->base.size = (struct yasl_vector2i){spec->size.x, spec->size.y};
+ w->base.size = (struct yasl_private_vector2i){spec->size.x, spec->size.y};
w->base.scale_120 = parent->scale_120;
wl_surface_set_user_data(w->surface, w);
if (surface_init_scaling(w) < 0)
@@ -180,13 +195,13 @@ err:
return NULL;
}
-bool wayland_surface_popup_reposition(struct wayland_surface *w, const struct yasl_popup_spec *spec)
+bool wayland_surface_popup_reposition(struct wayland_surface *w, const struct yasl_private_popup_spec *spec)
{
if (!w || !w->popup || w->closed || !w->conn || w->conn->lost ||
!popup_spec_valid(w->parent, spec) ||
xdg_popup_get_version(w->popup) < XDG_POPUP_REPOSITION_SINCE_VERSION)
return false;
- struct yasl_popup_spec old = {
+ struct yasl_private_popup_spec old = {
.size = { .x = w->width, .y = w->height },
.content_offset = { .x = w->content.x, .y = w->content.y },
.content_size = { .x = w->content.width, .y = w->content.height },
diff --git a/src/platform/wayland/shell.c b/src/platform/wayland/shell.c
index fc2ce2e..c2578e7 100644
--- a/src/platform/wayland/shell.c
+++ b/src/platform/wayland/shell.c
@@ -173,7 +173,7 @@ uint32_t conn_output_scale_120(const struct wayland_display *c, uint64_t outputs
for (const struct wl_conn_output *o = c->outputs; o; o = o->next)
if ((o->mask & outputs) && o->scale > scale)
scale = o->scale;
- return (uint32_t)scale * YASL_SCALE_UNIT;
+ return (uint32_t)scale * YASL_PRIVATE_SCALE_UNIT;
}
static void registry_global(void *data, struct wl_registry *registry,
@@ -227,6 +227,8 @@ static void registry_global(void *data, struct wl_registry *registry,
} else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
c->text_input_mgr = wl_registry_bind(registry, name,
&zwp_text_input_manager_v3_interface, 1);
+ for (struct seat *seat = c->seats; seat; seat = seat->next)
+ seat_ensure_text_input(seat);
} else if (strcmp(interface, wp_tearing_control_manager_v1_interface.name) == 0) {
c->tearing_mgr = wl_registry_bind(registry, name,
&wp_tearing_control_manager_v1_interface, 1);
@@ -277,7 +279,7 @@ static void set_state(struct wayland_surface *w, uint32_t state)
{
if (state == w->base.state) return;
w->base.state = state;
- yasl_surface_notify_state(&w->base, state);
+ yasl_private_surface_notify_state(&w->base, state);
}
static void toplevel_configure(void *data, struct xdg_toplevel *toplevel,
@@ -286,21 +288,21 @@ static void toplevel_configure(void *data, struct xdg_toplevel *toplevel,
(void)toplevel;
struct wayland_surface *w = data;
- uint32_t state = w->base.state & ~(YASL_STATE_FULLSCREEN |
- YASL_STATE_MAXIMIZED | YASL_STATE_ACTIVATED |
- YASL_STATE_SUSPENDED | YASL_STATE_TILED);
+ uint32_t state = w->base.state & ~(YASL_PRIVATE_STATE_FULLSCREEN |
+ YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_ACTIVATED |
+ YASL_PRIVATE_STATE_SUSPENDED | YASL_PRIVATE_STATE_TILED);
uint32_t *s;
wl_array_for_each(s, states) {
switch (*s) {
- case XDG_TOPLEVEL_STATE_FULLSCREEN: state |= YASL_STATE_FULLSCREEN; break;
- case XDG_TOPLEVEL_STATE_MAXIMIZED: state |= YASL_STATE_MAXIMIZED; break;
- case XDG_TOPLEVEL_STATE_ACTIVATED: state |= YASL_STATE_ACTIVATED; break;
- case XDG_TOPLEVEL_STATE_SUSPENDED: state |= YASL_STATE_SUSPENDED; break;
+ case XDG_TOPLEVEL_STATE_FULLSCREEN: state |= YASL_PRIVATE_STATE_FULLSCREEN; break;
+ case XDG_TOPLEVEL_STATE_MAXIMIZED: state |= YASL_PRIVATE_STATE_MAXIMIZED; break;
+ case XDG_TOPLEVEL_STATE_ACTIVATED: state |= YASL_PRIVATE_STATE_ACTIVATED; break;
+ case XDG_TOPLEVEL_STATE_SUSPENDED: state |= YASL_PRIVATE_STATE_SUSPENDED; break;
case XDG_TOPLEVEL_STATE_TILED_LEFT:
case XDG_TOPLEVEL_STATE_TILED_RIGHT:
case XDG_TOPLEVEL_STATE_TILED_TOP:
case XDG_TOPLEVEL_STATE_TILED_BOTTOM:
- state |= YASL_STATE_TILED;
+ state |= YASL_PRIVATE_STATE_TILED;
break;
default: break;
}
@@ -316,7 +318,7 @@ static void toplevel_close(void *data, struct xdg_toplevel *toplevel)
(void)toplevel;
struct wayland_surface *w = data;
if (w && !w->closed)
- yasl_surface_notify_close(&w->base);
+ yasl_private_surface_notify_close(&w->base);
}
static void toplevel_configure_bounds(void *data, struct xdg_toplevel *toplevel,
@@ -349,11 +351,21 @@ static const struct xdg_toplevel_listener toplevel_listener = {
static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, uint32_t serial)
{
struct wayland_surface *w = data;
- (void)xdg_surface;
w->pending_serial = serial;
w->has_pending_serial = true;
w->size_pending = true;
w->configured = true;
+ /* CPU surfaces acknowledge with their SHM commit. External presenters
+ * commit through EGL/Vulkan, so apply the configure and acknowledge it
+ * before renderer code can submit that first native buffer. */
+ if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU) {
+ xdg_surface_ack_configure(xdg_surface, serial);
+ w->has_pending_serial = false;
+ if (apply_pending_geometry(w) < 0) {
+ surface_fail(w);
+ return;
+ }
+ }
wayland_surface_schedule_redraw(w);
}
@@ -368,9 +380,9 @@ static void toplevel_decoration_configure(void *data,
struct wayland_surface *w = data;
uint32_t state = w->base.state;
if (mode == ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE)
- state &= ~YASL_STATE_CSD;
+ state &= ~YASL_PRIVATE_STATE_CSD;
else
- state |= YASL_STATE_CSD;
+ state |= YASL_PRIVATE_STATE_CSD;
set_state(w, state);
}
@@ -387,11 +399,11 @@ int surface_init_shell(struct wayland_surface *w, const char *title)
if (!w->toplevel) return -1;
xdg_toplevel_add_listener(w->toplevel, &toplevel_listener, w);
- wayland_surface_set_title(w, title ? title : "yasl_app");
+ wayland_surface_set_title(w, title ? title : "yasl_private_app");
- const char *app_id = w->conn->app_id ? w->conn->app_id : "yasl_app";
+ const char *app_id = w->conn->app_id ? w->conn->app_id : "yasl_private_app";
xdg_toplevel_set_app_id(w->toplevel, app_id);
- xdg_toplevel_set_min_size(w->toplevel, YASL_MIN_LOGICAL_PX, YASL_MIN_LOGICAL_PX);
+ xdg_toplevel_set_min_size(w->toplevel, YASL_PRIVATE_MIN_LOGICAL_PX, YASL_PRIVATE_MIN_LOGICAL_PX);
if (w->conn->decor_mgr) {
w->toplevel_decor = zxdg_decoration_manager_v1_get_toplevel_decoration(
@@ -400,7 +412,9 @@ int surface_init_shell(struct wayland_surface *w, const char *title)
zxdg_toplevel_decoration_v1_add_listener(w->toplevel_decor,
&toplevel_decoration_listener, w);
zxdg_toplevel_decoration_v1_set_mode(w->toplevel_decor,
- ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
+ (w->base.creation_flags & YASL_PRIVATE_SURFACE_UNDECORATED)
+ ? ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE
+ : ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
}
return 0;
}
diff --git a/src/platform/wayland/surface.c b/src/platform/wayland/surface.c
index 2e85e2b..31096f4 100644
--- a/src/platform/wayland/surface.c
+++ b/src/platform/wayland/surface.c
@@ -52,7 +52,7 @@ static void display_lose(struct wayland_display *c)
surface_fail(w);
}
display_leave(c);
- yasl_display_notify_event(&c->base, YASL_EVENT_DISPLAY_LOST);
+ yasl_private_display_notify_event(&c->base, YASL_PRIVATE_EVENT_DISPLAY_LOST);
}
void surface_fail(struct wayland_surface *w)
@@ -60,7 +60,7 @@ void surface_fail(struct wayland_surface *w)
if (w->closed)
return;
w->closed = true;
- yasl_surface_notify_close(&w->base);
+ yasl_private_surface_notify_close(&w->base);
}
static int display_ready(int fd, short revents, void *ud)
@@ -80,7 +80,7 @@ static int display_ready(int fd, short revents, void *ud)
if (revents & POLLOUT) {
if (wl_display_flush(c->wl) >= 0) {
if (c->loop && c->watch > 0)
- yasl_loop_set_fd_events(c->loop, c->watch, POLLIN);
+ yasl_private_loop_set_fd_events(c->loop, c->watch, POLLIN);
} else if (errno != EAGAIN) {
display_lose(c);
}
@@ -113,7 +113,7 @@ static bool display_arm(struct wayland_display *c)
events |= POLLOUT;
}
if (c->loop && c->watch > 0)
- yasl_loop_set_fd_events(c->loop, c->watch, events);
+ yasl_private_loop_set_fd_events(c->loop, c->watch, events);
return had_pending;
}
@@ -154,7 +154,7 @@ int wayland_display_dispatch_timeout(struct wayland_display *c, int timeout_ms)
int n = 0;
if (c->loop) {
- n = yasl_loop_step(c->loop, timeout_ms);
+ n = yasl_private_loop_step(c->loop, timeout_ms);
}
if (c->reading) {
@@ -190,7 +190,7 @@ void wayland_display_flush(struct wayland_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);
+ if (c->loop && c->watch > 0) yasl_private_loop_del_fd(c->loop, c->watch);
if (c->wl) display_cancel_read(c);
while (c->seats)
seat_destroy(c->seats);
@@ -217,9 +217,12 @@ static void display_free(struct wayland_display *c)
if (c->shm) wl_shm_destroy(c->shm);
if (c->compositor) wl_compositor_destroy(c->compositor);
if (c->registry) wl_registry_destroy(c->registry);
+#if YASL_HAS_EGL
+ if (c->egl_initialized) eglTerminate(c->egl_display);
+#endif
if (c->wl) wl_display_disconnect(c->wl);
if (c->owns_loop && c->loop) {
- yasl_loop_destroy(c->loop);
+ yasl_private_loop_destroy(c->loop);
c->loop = NULL;
c->base.loop = NULL;
}
@@ -227,13 +230,13 @@ static void display_free(struct wayland_display *c)
free(c);
}
-struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struct yasl_display_spec *spec)
+struct wayland_display *wayland_display_open(struct yasl_private_loop *loop, const struct yasl_private_display_spec *spec)
{
struct wayland_display *c = calloc(1, sizeof(*c));
if (!c) return NULL;
- c->base.ops = &yasl_wayland_backend;
+ c->base.ops = &yasl_private_wayland_backend;
if (!loop) {
- c->loop = yasl_loop_create();
+ c->loop = yasl_private_loop_create();
c->owns_loop = true;
} else {
c->loop = loop;
@@ -241,7 +244,7 @@ struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struc
c->base.loop = c->loop;
if (!c->loop) goto err;
if (spec) {
- c->base.callbacks = spec->cbs ? *spec->cbs : (struct yasl_display_cbs){0};
+ c->base.callbacks = spec->cbs ? *spec->cbs : (struct yasl_private_display_cbs){0};
c->cbs = &c->base.callbacks;
c->ud = spec->ud;
if (spec->app_id) c->app_id = strdup(spec->app_id);
@@ -251,6 +254,13 @@ struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struc
c->wl = c->xkb_ctx ? wl_display_connect(NULL) : NULL;
if (!c->wl) goto err;
c->base.native_handle = c->wl;
+#if YASL_HAS_EGL
+ c->egl_display = eglGetDisplay((EGLNativeDisplayType)c->wl);
+ if (c->egl_display != EGL_NO_DISPLAY) {
+ EGLint major = 0, minor = 0;
+ c->egl_initialized = eglInitialize(c->egl_display, &major, &minor) == EGL_TRUE;
+ }
+#endif
c->registry = wl_display_get_registry(c->wl);
if (!c->registry) goto err;
@@ -260,7 +270,7 @@ struct wayland_display *wayland_display_open(struct yasl_loop *loop, const struc
surface_init_cursor(c);
if (c->loop) {
- c->watch = yasl_loop_add_fd(c->loop, wl_display_get_fd(c->wl), POLLIN, display_ready, c);
+ c->watch = yasl_private_loop_add_fd(c->loop, wl_display_get_fd(c->wl), POLLIN, display_ready, c);
if (c->watch <= 0) { c->watch = 0; goto err; }
}
if (display_input_init(c) < 0)
@@ -282,16 +292,18 @@ void wayland_display_close(struct wayland_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 | YASL_CAP_FRAME_PACING;
- if (c->tablet_mgr) caps |= YASL_CAP_TABLET;
+ uint32_t caps = YASL_PRIVATE_CAP_POPUPS | YASL_PRIVATE_CAP_CSD |
+ YASL_PRIVATE_CAP_FRAME_PACING | YASL_PRIVATE_CAP_NATIVE_GRAPHICS;
+ if (c->tablet_mgr) caps |= YASL_PRIVATE_CAP_TABLET;
+ if (c->text_input_mgr) caps |= YASL_PRIVATE_CAP_TEXT_INPUT;
struct seat *s = c->active_seat ? c->active_seat : c->seats;
const struct wl_data_device *device = s ? clipboard_data_device(s->clipboard) : NULL;
if (device && wl_data_device_get_version((struct wl_data_device *)device) >= 3)
- caps |= YASL_CAP_DRAG;
+ caps |= YASL_PRIVATE_CAP_DRAG;
if (c->toplevel_drag_mgr)
- caps |= YASL_CAP_DRAG_CARRY;
+ caps |= YASL_PRIVATE_CAP_DRAG_CARRY;
if (c->primary_selection_mgr)
- caps |= YASL_CAP_PRIMARY_SELECTION;
+ caps |= YASL_PRIVATE_CAP_PRIMARY_SELECTION;
return caps;
}
@@ -321,15 +333,18 @@ static void conn_unlink(struct wayland_surface *w)
}
-static bool spec_valid(const struct yasl_window_spec *spec)
+static bool spec_valid(const struct yasl_private_window_spec *spec)
{
size_t bytes;
- return spec && spec->size.x > 0 && spec->size.y > 0 &&
- buffer_size(spec->size.x, spec->size.y, &bytes);
+ if (!spec || spec->size.x <= 0 || spec->size.y <= 0)
+ return false;
+ return spec->external_gpu && !(spec->flags & YASL_PRIVATE_SURFACE_CPU)
+ ? spec->size.x <= INT32_MAX && spec->size.y <= INT32_MAX
+ : buffer_size(spec->size.x, spec->size.y, &bytes);
}
struct wayland_surface *wayland_surface_create_toplevel(struct wayland_display *c,
- const struct yasl_window_spec *spec, void *graphics_device)
+ const struct yasl_private_window_spec *spec, void *graphics_device)
{
(void)graphics_device;
if (!c || c->lost || !spec_valid(spec))
@@ -340,28 +355,35 @@ struct wayland_surface *wayland_surface_create_toplevel(struct wayland_display *
if (!w) return NULL;
c->depth++;
conn_link(c, w);
- w->base.state = YASL_STATE_CSD;
+ w->base.state = YASL_PRIVATE_STATE_CSD;
+ w->base.creation_flags = spec->flags;
+ w->base.presenter = (spec->external_gpu && !(spec->flags & YASL_PRIVATE_SURFACE_CPU))
+ ? YASL_PRIVATE_PRESENTER_NONE : YASL_PRIVATE_PRESENTER_CPU;
wayland_surface_set_callbacks(w, NULL, NULL);
- w->cursor = YASL_CURSOR_TEXT;
+ w->cursor = YASL_PRIVATE_CURSOR_TEXT;
w->req_width = width;
w->req_height = height;
w->logical_width = width;
w->logical_height = height;
- w->scale_120 = YASL_SCALE_UNIT;
- w->pending_scale_120 = YASL_SCALE_UNIT;
+ w->scale_120 = YASL_PRIVATE_SCALE_UNIT;
+ w->pending_scale_120 = YASL_PRIVATE_SCALE_UNIT;
w->scale_factor = 1.0;
w->size_pending = true;
w->clock.refresh_ns = c->outputs && c->outputs->refresh_ns
- ? c->outputs->refresh_ns : YASL_REFRESH_DEFAULT_NS;
+ ? c->outputs->refresh_ns : YASL_PRIVATE_REFRESH_DEFAULT_NS;
w->surface = wl_compositor_create_surface(c->compositor);
if (!w->surface) goto err;
w->base.native_handle = w->surface;
- w->base.size = (struct yasl_vector2i){width, height};
- w->base.scale_120 = YASL_SCALE_UNIT;
+ w->base.size = (struct yasl_private_vector2i){width, height};
+ w->base.scale_120 = YASL_PRIVATE_SCALE_UNIT;
wl_surface_set_user_data(w->surface, w);
if (surface_init_scaling(w) < 0) goto err;
if (surface_init_shell(w, spec->title) < 0) goto err;
+ if (spec->flags & YASL_PRIVATE_SURFACE_FIXED_SIZE) {
+ xdg_toplevel_set_min_size(w->toplevel, width, height);
+ xdg_toplevel_set_max_size(w->toplevel, width, height);
+ }
w->opaque = spec->fullscreen;
if (spec->fullscreen)
xdg_toplevel_set_fullscreen(w->toplevel, NULL);
@@ -389,10 +411,10 @@ err:
return NULL;
}
-void wayland_surface_set_callbacks(struct wayland_surface *w, const struct yasl_window_cbs *cbs, void *ud)
+void wayland_surface_set_callbacks(struct wayland_surface *w, const struct yasl_private_window_cbs *cbs, void *ud)
{
if (w && !w->closed) {
- w->base.callbacks = cbs ? *cbs : (struct yasl_window_cbs){0};
+ w->base.callbacks = cbs ? *cbs : (struct yasl_private_window_cbs){0};
w->base.ud = ud;
w->cbs = &w->base.callbacks;
w->ud = ud;
@@ -410,6 +432,15 @@ void wayland_surface_destroy(struct wayland_surface *w)
free(w->ime_commit);
free(w->ime_shown);
if (w->tearing_control) wp_tearing_control_v1_destroy(w->tearing_control);
+#if YASL_HAS_EGL
+ if (w->egl_context) {
+ if (eglGetCurrentContext() == w->egl_context)
+ eglMakeCurrent(w->conn->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroyContext(w->conn->egl_display, w->egl_context);
+ }
+ if (w->egl_surface) eglDestroySurface(w->conn->egl_display, w->egl_surface);
+ if (w->egl_window) wl_egl_window_destroy(w->egl_window);
+#endif
if (w->fifo) wp_fifo_v1_destroy(w->fifo);
if (w->commit_timer) wp_commit_timer_v1_destroy(w->commit_timer);
surface_feedback_destroy(w);
@@ -427,7 +458,7 @@ void wayland_surface_destroy(struct wayland_surface *w)
}
struct wayland_display *c = w->conn;
conn_unlink(w);
- static const struct yasl_window_cbs no_callbacks = {0};
+ static const struct yasl_private_window_cbs no_callbacks = {0};
*w = (struct wayland_surface){
.base = w->base,
.conn_next = w->conn_next,
@@ -446,16 +477,20 @@ void wayland_surface_destroy(struct wayland_surface *w)
void wayland_surface_schedule_redraw(struct wayland_surface *w)
{
if (!w || w->closed) return;
+ if (w->base.presenter != YASL_PRIVATE_PRESENTER_CPU) {
+ yasl_private_surface_notify_refresh(&w->base);
+ return;
+ }
w->want_draw = true;
if (w->conn && w->conn->loop)
- yasl_loop_kick(w->conn->loop);
+ yasl_private_loop_kick(w->conn->loop);
}
-struct yasl_geometry wayland_surface_geometry(const struct wayland_surface *w)
+struct yasl_private_geometry wayland_surface_geometry(const struct wayland_surface *w)
{
- return (struct yasl_geometry){
- .size = w ? (struct yasl_vector2i){w->width, w->height} : (struct yasl_vector2i){0},
- .scale_120 = w ? w->scale_120 : YASL_SCALE_UNIT,
+ return (struct yasl_private_geometry){
+ .size = w ? (struct yasl_private_vector2i){w->width, w->height} : (struct yasl_private_vector2i){0},
+ .scale_120 = w ? w->scale_120 : YASL_PRIVATE_SCALE_UNIT,
};
}
@@ -473,23 +508,23 @@ bool wayland_surface_start_interactive_move(struct wayland_surface *s)
return true;
}
-bool wayland_surface_start_interactive_resize(struct wayland_surface *s, enum yasl_resize_edge edge)
+bool wayland_surface_start_interactive_resize(struct wayland_surface *s, enum yasl_private_resize_edge edge)
{
- if (!s || !s->toplevel || !s->conn || edge == YASL_EDGE_NONE) return false;
+ if (!s || !s->toplevel || !s->conn || edge == YASL_PRIVATE_EDGE_NONE) return false;
struct seat *seat = s->conn->active_seat ? s->conn->active_seat : s->conn->seats;
if (!seat || !seat->proxy || !seat->press_serial) return false;
xdg_toplevel_resize(s->toplevel, seat->proxy, seat->press_serial, (uint32_t)edge);
return true;
}
-bool wayland_surface_show_window_menu(struct wayland_surface *s, struct yasl_vector2i pos)
+bool wayland_surface_show_window_menu(struct wayland_surface *s, struct yasl_private_vector2i pos)
{
if (!s || !s->toplevel || !s->conn) return false;
struct seat *seat = s->conn->active_seat ? s->conn->active_seat : s->conn->seats;
if (!seat || !seat->proxy || !seat->press_serial) return false;
- uint32_t scale = s->scale_120 ? s->scale_120 : YASL_SCALE_UNIT;
+ uint32_t scale = s->scale_120 ? s->scale_120 : YASL_PRIVATE_SCALE_UNIT;
xdg_toplevel_show_window_menu(s->toplevel, seat->proxy, seat->press_serial,
- yasl_scale_to_logical(pos.x, scale),
- yasl_scale_to_logical(pos.y, scale));
+ yasl_private_scale_to_logical(pos.x, scale),
+ yasl_private_scale_to_logical(pos.y, scale));
return true;
}
diff --git a/src/platform/x11/keymap.c b/src/platform/x11/keymap.c
index 3c566b3..35178dd 100644
--- a/src/platform/x11/keymap.c
+++ b/src/platform/x11/keymap.c
@@ -2,7 +2,7 @@
#include <string.h>
/* XKB standard physical key names, independent of server numeric keycodes. */
-enum yasl_key yasl_key_from_xkb_name(const char name[4])
+enum yasl_key yasl_private_key_from_xkb_name(const char name[4])
{
static const struct { char name[4]; enum yasl_key key; } keys[] = {
{{'T', 'L', 'D', 'E'}, YASL_KEY_GRAVE},
diff --git a/src/platform/x11/keymap.h b/src/platform/x11/keymap.h
index 8114fb5..9bb009d 100644
--- a/src/platform/x11/keymap.h
+++ b/src/platform/x11/keymap.h
@@ -1,8 +1,8 @@
-#ifndef YASL_X11_KEYMAP_H
-#define YASL_X11_KEYMAP_H
+#ifndef YASL_PRIVATE_X11_KEYMAP_H
+#define YASL_PRIVATE_X11_KEYMAP_H
#include "yasl/keycodes.h"
-enum yasl_key yasl_key_from_xkb_name(const char name[4]);
+enum yasl_key yasl_private_key_from_xkb_name(const char name[4]);
#endif
diff --git a/src/platform/x11/native.c b/src/platform/x11/native.c
index 249adec..fffe171 100644
--- a/src/platform/x11/native.c
+++ b/src/platform/x11/native.c
@@ -1,14 +1,14 @@
#include "common/backend.h"
-#include "yasl/native/x11.h"
+#include "common/private_native_x11.h"
-Display *yasl_x11_display_get_display(struct yasl_display *d)
+Display *yasl_private_x11_display_get_display(struct yasl_private_display *d)
{
- return (d && yasl_display_backend(d) == YASL_BACKEND_X11) ?
- (Display *)yasl_display_get_native_handle(d) : NULL;
+ return (d && yasl_private_display_backend(d) == YASL_PRIVATE_BACKEND_X11) ?
+ (Display *)yasl_private_display_get_native_handle(d) : NULL;
}
-uintptr_t yasl_x11_surface_get_window(struct yasl_surface *s)
+uintptr_t yasl_private_x11_surface_get_window(struct yasl_surface *s)
{
- return (s && s->display && yasl_display_backend(s->display) == YASL_BACKEND_X11) ?
- (uintptr_t)yasl_surface_get_native_handle(s) : 0;
+ return (s && s->display && yasl_private_display_backend(s->display) == YASL_PRIVATE_BACKEND_X11) ?
+ (uintptr_t)yasl_private_surface_get_native_handle(s) : 0;
}
diff --git a/src/platform/x11/window.c b/src/platform/x11/window.c
index 004d56c..8129dd5 100644
--- a/src/platform/x11/window.c
+++ b/src/platform/x11/window.c
@@ -16,6 +16,10 @@
#include <X11/keysym.h>
#include <X11/extensions/XInput2.h>
#include <X11/extensions/XShm.h>
+#if YASL_HAS_EGL
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#endif
#include <sys/ipc.h>
#include <sys/shm.h>
#if YASL_HAS_XRANDR
@@ -35,7 +39,7 @@
#include <wchar.h>
#define TEXT_LIMIT (16 * 1024 * 1024)
-#define PASTE_TIMEOUT_NS YASL_MS_TO_NS(30000)
+#define PASTE_TIMEOUT_NS YASL_PRIVATE_MS_TO_NS(30000)
#ifndef likely
#define likely(x) __builtin_expect(!!(x), 1)
@@ -50,26 +54,33 @@ struct x11_surface {
struct x11_surface *dead_next;
Window window;
GC gc;
+ Colormap colormap;
XIC ic;
+#if YASL_HAS_EGL
+ EGLContext gl_context;
+ EGLSurface gl_surface;
+ EGLConfig gl_config;
+#endif
XImage *image;
uint32_t *pixels;
bool shm_active;
XShmSegmentInfo shm_info;
- struct yasl_vector2i size;
- const struct yasl_window_cbs *cbs;
+ struct yasl_private_vector2i size;
+ const struct yasl_private_window_cbs *cbs;
void *ud;
struct input_queue input;
- struct yasl_frame frame;
- struct yasl_damage_list damage;
- struct yasl_frame_clock clock;
+ struct yasl_private_frame frame;
+ struct yasl_private_damage_list damage;
+ struct yasl_private_frame_clock clock;
bool want_draw, drawing, presented, destroyed;
+ bool focused;
struct x11_surface *parent;
bool is_popup;
- struct yasl_vector2i content_offset;
+ struct yasl_private_vector2i content_offset;
char *ime_preedit;
int ime_cursor_begin;
int ime_cursor_end;
- struct yasl_rect2i ime_caret;
+ struct yasl_private_rect2i ime_caret;
};
struct paste_request {
@@ -79,33 +90,45 @@ struct paste_request {
Window window;
Atom selection;
uint64_t id;
- yasl_timer_id timeout;
+ yasl_private_timer_id timeout;
};
struct tablet_axis {
int number;
double min, max, value;
+ bool valid;
+};
+
+struct x11_scroll_axis {
+ int number;
+ double increment, value;
+ bool valid, initialized;
+ int type;
};
struct x11_pointer {
struct x11_pointer *next;
int id;
- enum yasl_pointer_device device;
- struct tablet_axis pressure, tilt_x, tilt_y;
+ enum yasl_private_pointer_device device;
+ unsigned tool;
+ struct tablet_axis pressure, tilt_x, tilt_y, rotation, distance, tangential, wheel;
+ struct x11_scroll_axis scroll_x, scroll_y;
+ uint32_t buttons;
+ bool finger_scroll;
};
struct x11_display {
- struct yasl_display base;
+ struct yasl_private_display base;
Display *native;
int screen;
- struct yasl_loop *loop;
+ struct yasl_private_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, incr;
- Atom net_name, net_state, fullscreen, maximized_h, maximized_v;
+ Atom net_name, net_state, fullscreen, maximized_h, maximized_v, motif_hints;
Atom xdnd_aware, xdnd_enter, xdnd_position, xdnd_status, xdnd_leave, xdnd_drop, xdnd_finished;
Atom xdnd_selection, xdnd_action_copy, xdnd_type_list, uri_list;
char *selection[2];
@@ -121,20 +144,26 @@ struct x11_display {
int xkb_event;
enum yasl_key keys[256];
Cursor cursors[16];
- Atom abs_pressure, abs_tilt_x, abs_tilt_y, tool_type, eraser;
+ Atom abs_pressure, abs_tilt_x, abs_tilt_y, abs_rotation, abs_distance, abs_tangential, abs_wheel;
+ Atom tool_type, eraser;
+ Atom brush, airbrush, pencil;
struct x11_pointer *pointers;
+#if YASL_HAS_EGL
+ EGLDisplay egl;
+ bool egl_ready;
+#endif
struct {
bool active;
struct x11_surface *origin;
struct x11_surface *carried;
- struct yasl_vector2i carry_offset;
- struct yasl_drag_listener listener;
+ struct yasl_private_vector2i carry_offset;
+ struct yasl_private_drag_listener listener;
void *ud;
struct x11_surface *hover;
- struct yasl_vector2i hover_pos;
+ struct yasl_private_vector2i hover_pos;
Window icon_window;
Pixmap icon_pixmap;
- struct yasl_vector2i hotspot;
+ struct yasl_private_vector2i hotspot;
} drag;
struct {
Window source;
@@ -142,7 +171,7 @@ struct x11_display {
bool accepts;
bool has_uri_list;
struct x11_surface *surface;
- struct yasl_vector2i pos;
+ struct yasl_private_vector2i pos;
Time time;
} xdnd;
struct {
@@ -153,11 +182,11 @@ struct x11_display {
} pending_drop;
};
-static bool place(struct yasl_surface *base, struct yasl_vector2i at);
-static void drag_end(struct x11_display *d, enum yasl_drag_end how);
-static void drag_cancel(struct yasl_display *base);
+static bool place(struct yasl_surface *base, struct yasl_private_vector2i at);
+static void drag_end(struct x11_display *d, enum yasl_private_drag_end how);
+static void drag_cancel(struct yasl_private_display *base);
static void schedule_redraw(struct yasl_surface *base);
-static uint64_t paste_timeout(void *ud, yasl_timer_id id, uint64_t interval_ns);
+static uint64_t paste_timeout(void *ud, yasl_private_timer_id id, uint64_t interval_ns);
static void leave(struct x11_display *d)
{
@@ -169,7 +198,7 @@ static void leave(struct x11_display *d)
}
}
-static void push(struct x11_surface *s, struct yasl_event event,
+static void push(struct x11_surface *s, struct yasl_private_event event,
const char *text, size_t len)
{
if (s->destroyed) return;
@@ -189,39 +218,39 @@ static unsigned long component(uint32_t color, unsigned long mask)
return (((unsigned long)color * m + 127) / 255) << shift;
}
-static void drag_set_hover(struct x11_display *d, struct x11_surface *s, struct yasl_vector2i pos)
+static void drag_set_hover(struct x11_display *d, struct x11_surface *s, struct yasl_private_vector2i pos)
{
if (!d->drag.active) return;
if (d->drag.hover && d->drag.hover != s) {
struct x11_surface *old = d->drag.hover;
- struct yasl_vector2i old_pos = d->drag.hover_pos;
+ struct yasl_private_vector2i old_pos = d->drag.hover_pos;
d->drag.hover = NULL;
if (!old->destroyed)
- yasl_surface_notify_drag(&old->base, YASL_DRAG_LEAVE, old_pos);
+ yasl_private_surface_notify_drag(&old->base, YASL_PRIVATE_DRAG_LEAVE, old_pos);
if (!d->drag.active || (s && s->destroyed)) return;
}
if (s && s->destroyed) return;
d->drag.hover = s;
d->drag.hover_pos = pos;
if (s && !s->destroyed)
- yasl_surface_notify_drag(&s->base, YASL_DRAG_OVER, pos);
+ yasl_private_surface_notify_drag(&s->base, YASL_PRIVATE_DRAG_OVER, pos);
}
-static void drag_leave_hover(struct x11_display *d, struct x11_surface *s, struct yasl_vector2i pos)
+static void drag_leave_hover(struct x11_display *d, struct x11_surface *s, struct yasl_private_vector2i pos)
{
if (!d->drag.active || d->drag.hover != s) return;
d->drag.hover = NULL;
if (s && !s->destroyed)
- yasl_surface_notify_drag(&s->base, YASL_DRAG_LEAVE, pos);
+ yasl_private_surface_notify_drag(&s->base, YASL_PRIVATE_DRAG_LEAVE, pos);
}
static void drag_drop_hover(struct x11_display *d)
{
if (!d->drag.active) return;
- drag_end(d, YASL_DRAG_DROPPED);
+ drag_end(d, YASL_PRIVATE_DRAG_DROPPED);
}
-static int yasl_x11_error_handler(Display *native, XErrorEvent *event)
+static int yasl_private_x11_error_handler(Display *native, XErrorEvent *event)
{
(void)native;
(void)event;
@@ -230,7 +259,7 @@ static int yasl_x11_error_handler(Display *native, XErrorEvent *event)
return 0;
}
-static struct x11_surface *surface_at_root_pos(struct x11_display *d, int rx, int ry, struct yasl_vector2i *out_pos)
+static struct x11_surface *surface_at_root_pos(struct x11_display *d, int rx, int ry, struct yasl_private_vector2i *out_pos)
{
if (!d->native) return NULL;
Window ignore_icon = d->drag.icon_window;
@@ -243,7 +272,7 @@ static struct x11_surface *surface_at_root_pos(struct x11_display *d, int rx, in
if (XTranslateCoordinates(d->native, RootWindow(d->native, d->screen),
s->window, rx, ry, &tx, &ty, &sub)) {
if (tx >= 0 && ty >= 0 && tx < s->size.x && ty < s->size.y) {
- if (out_pos) *out_pos = (struct yasl_vector2i){tx, ty};
+ if (out_pos) *out_pos = (struct yasl_private_vector2i){tx, ty};
return s;
}
}
@@ -253,19 +282,31 @@ static struct x11_surface *surface_at_root_pos(struct x11_display *d, int rx, in
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);
+ return ((state & ShiftMask) ? YASL_PRIVATE_MOD_SHIFT : 0) |
+ ((state & ControlMask) ? YASL_PRIVATE_MOD_CTRL : 0) |
+ ((state & Mod1Mask) ? YASL_PRIVATE_MOD_ALT : 0) |
+ ((state & Mod4Mask) ? YASL_PRIVATE_MOD_SUPER : 0) |
+ ((state & LockMask) ? YASL_PRIVATE_MOD_CAPS : 0);
}
static void pointer_info(struct x11_display *d, struct x11_pointer *p,
const XIDeviceInfo *info)
{
p->id = info->deviceid;
- p->device = YASL_PTR_DEV_MOUSE;
+ p->device = YASL_PRIVATE_PTR_DEV_MOUSE;
+ p->tool = 0;
+ p->buttons = 0;
p->pressure.number = p->tilt_x.number = p->tilt_y.number = -1;
+ p->rotation.number = p->distance.number = p->tangential.number = p->wheel.number = -1;
+ p->scroll_x.number = p->scroll_y.number = -1;
+ if (info->name) {
+ for (const char *s = info->name; *s; s++) {
+ if (!strncasecmp(s, "touchpad", 8) || !strncasecmp(s, "trackpad", 8)) {
+ p->finger_scroll = true;
+ break;
+ }
+ }
+ }
bool touch = false;
for (int i = 0; i < info->num_classes; i++) {
XIAnyClassInfo *c = info->classes[i];
@@ -275,17 +316,42 @@ static void pointer_info(struct x11_display *d, struct x11_pointer *p,
if (v->mode != XIModeAbsolute) continue;
struct tablet_axis *axis = v->label == d->abs_pressure ? &p->pressure :
v->label == d->abs_tilt_x ? &p->tilt_x :
- v->label == d->abs_tilt_y ? &p->tilt_y : NULL;
- if (axis) *axis = (struct tablet_axis){v->number, v->min, v->max, v->value};
+ v->label == d->abs_tilt_y ? &p->tilt_y :
+ v->label == d->abs_rotation ? &p->rotation :
+ v->label == d->abs_distance ? &p->distance :
+ v->label == d->abs_tangential ? &p->tangential :
+ v->label == d->abs_wheel ? &p->wheel : NULL;
+ if (axis) *axis = (struct tablet_axis){v->number, v->min, v->max, v->value, true};
+ }
+ for (int i = 0; i < info->num_classes; i++) {
+ XIAnyClassInfo *c = info->classes[i];
+ if (c->type != XIScrollClass) continue;
+ XIScrollClassInfo *scroll = (XIScrollClassInfo *)c;
+ struct x11_scroll_axis *axis = scroll->scroll_type == XIScrollTypeVertical ?
+ &p->scroll_y : scroll->scroll_type == XIScrollTypeHorizontal ? &p->scroll_x : NULL;
+ if (!axis) continue;
+ *axis = (struct x11_scroll_axis){.number = scroll->number,
+ .increment = scroll->increment, .type = scroll->scroll_type};
+ for (int j = 0; j < info->num_classes; j++) {
+ XIAnyClassInfo *vclass = info->classes[j];
+ if (vclass->type != XIValuatorClass) continue;
+ XIValuatorClassInfo *v = (XIValuatorClassInfo *)vclass;
+ if (v->number == scroll->number) {
+ axis->value = v->value;
+ axis->initialized = axis->valid = true;
+ break;
+ }
+ }
}
if (!touch && p->pressure.number >= 0) {
- p->device = YASL_PTR_DEV_PEN;
+ p->device = YASL_PRIVATE_PTR_DEV_PEN;
+ p->tool = 1;
/* Xorg's Wacom and libinput drivers expose separate eraser devices.
* Use the tool property when available, with the device name as a
* fallback for servers (including Xwayland) without that property. */
if (info->name) {
for (const char *s = info->name; *s; s++)
- if (!strncasecmp(s, "eraser", 6)) { p->device = YASL_PTR_DEV_ERASER; break; }
+ if (!strncasecmp(s, "eraser", 6)) { p->device = YASL_PRIVATE_PTR_DEV_ERASER; break; }
}
}
}
@@ -301,17 +367,21 @@ static struct x11_pointer *pointer_for(struct x11_display *d, int id)
struct x11_pointer *p = count ? calloc(1, sizeof(*p)) : NULL;
if (p) {
pointer_info(d, p, info);
- if (p->device == YASL_PTR_DEV_PEN || p->device == YASL_PTR_DEV_ERASER) {
+ if (p->device == YASL_PRIVATE_PTR_DEV_PEN || p->device == YASL_PRIVATE_PTR_DEV_ERASER) {
Atom type;
int format;
unsigned long n, after;
unsigned char *value = NULL;
if (XIGetProperty(d->native, id, d->tool_type, 0, 1, False,
XA_ATOM, &type, &format, &n, &after, &value) == Success &&
- type == XA_ATOM && format == 32 && n == 1 && value)
+ type == XA_ATOM && format == 32 && n == 1 && value) {
/* XIGetProperty packs format-32 values as uint32_t, unlike
* XGetWindowProperty's array of native-sized longs. */
- p->device = *(uint32_t *)value == d->eraser ? YASL_PTR_DEV_ERASER : p->device;
+ if (*(uint32_t *)value == d->eraser) p->device = YASL_PRIVATE_PTR_DEV_ERASER;
+ else if (d->brush && *(uint32_t *)value == d->brush) p->tool = 3;
+ else if (d->airbrush && *(uint32_t *)value == d->airbrush) p->tool = 4;
+ else if (d->pencil && *(uint32_t *)value == d->pencil) p->tool = 5;
+ }
if (value) XFree(value);
}
p->next = d->pointers;
@@ -330,21 +400,61 @@ static void clear_pointers(struct x11_display *d)
}
}
-static void update_axes(struct x11_pointer *p, const XIValuatorState *state)
+static uint32_t update_axes(struct x11_pointer *p, const XIValuatorState *state)
{
+ uint32_t changed = 0;
const double *value = state->values;
for (int i = 0; i < state->mask_len * 8; i++) {
if (!XIMaskIsSet(state->mask, i)) continue;
- struct tablet_axis *axes[] = {&p->pressure, &p->tilt_x, &p->tilt_y};
+ struct tablet_axis *axes[] = {&p->pressure, &p->tilt_x, &p->tilt_y, &p->rotation,
+ &p->distance, &p->tangential, &p->wheel};
+ const uint32_t bits[] = {YASL_PRIVATE_PEN_PRESSURE, YASL_PRIVATE_PEN_TILT,
+ YASL_PRIVATE_PEN_TILT, YASL_PRIVATE_PEN_ROTATION, YASL_PRIVATE_PEN_DISTANCE,
+ YASL_PRIVATE_PEN_TANGENTIAL, YASL_PRIVATE_PEN_WHEEL};
for (unsigned a = 0; a < sizeof(axes) / sizeof(axes[0]); a++)
- if (axes[a]->number == i && isfinite(*value)) axes[a]->value = *value;
+ if (axes[a]->number == i && isfinite(*value)) {
+ axes[a]->value = *value;
+ axes[a]->valid = true;
+ changed |= bits[a];
+ }
+ value++;
+ }
+ return changed;
+}
+
+static void update_scroll(struct x11_pointer *p, const XIValuatorState *state,
+ struct yasl_private_pointer_event *out)
+{
+ const double *value = state->values;
+ for (int i = 0; i < state->mask_len * 8; i++) {
+ if (!XIMaskIsSet(state->mask, i)) continue;
+ struct x11_scroll_axis *axes[] = {&p->scroll_x, &p->scroll_y};
+ for (unsigned a = 0; a < 2; a++) {
+ struct x11_scroll_axis *axis = axes[a];
+ if (axis->number != i || !isfinite(*value)) continue;
+ if (axis->initialized) {
+ double delta = *value - axis->value;
+ if (axis->increment != 0 && isfinite(delta)) {
+ double lines = delta / axis->increment;
+ if (a == 0) out->scroll.x += lines;
+ else out->scroll.y += lines;
+ if (lines != 0) {
+ out->scroll_valid |= 1u;
+ out->scroll_source = p->finger_scroll ? 2u : 1u;
+ out->scroll_phase = 2u;
+ }
+ }
+ }
+ axis->value = *value;
+ axis->initialized = true;
+ }
value++;
}
}
static uint16_t pressure_value(const struct tablet_axis *a)
{
- if (a->number < 0 || !(a->max > a->min) || !isfinite(a->value)) return 0;
+ if (a->number < 0 || !(a->max > a->min) || !a->valid || !isfinite(a->value)) return 0;
double v = (a->value - a->min) / (a->max - a->min);
return (uint16_t)lround(fmax(0, fmin(1, v)) * 65535);
}
@@ -352,50 +462,121 @@ static uint16_t pressure_value(const struct tablet_axis *a)
static int8_t tilt_value(const struct tablet_axis *a)
{
/* Xorg tablet tilt valuators are already in degrees. */
- return a->number < 0 || !isfinite(a->value) ? 0 :
+ return a->number < 0 || !a->valid || !isfinite(a->value) ? 0 :
(int8_t)lround(fmax(-90, fmin(90, a->value)));
}
-static enum yasl_pointer_button pointer_button(unsigned button)
+static enum yasl_private_pointer_button pointer_button(unsigned button)
+{
+ return button == 1 ? YASL_PRIVATE_BUTTON_PRIMARY :
+ button == 2 ? YASL_PRIVATE_BUTTON_MIDDLE :
+ button == 3 ? YASL_PRIVATE_BUTTON_SECONDARY :
+ button >= 8 && button <= INT16_MAX ? (enum yasl_private_pointer_button)(button - 5) :
+ YASL_PRIVATE_BUTTON_BACK;
+}
+
+static uint32_t pen_axes_valid(const struct x11_pointer *p)
{
- return button == 1 ? YASL_BUTTON_PRIMARY :
- button == 2 ? YASL_BUTTON_MIDDLE :
- button == 3 ? YASL_BUTTON_SECONDARY :
- button >= 8 && button <= INT16_MAX ? (enum yasl_pointer_button)(button - 5) :
- YASL_BUTTON_BACK;
+ return (p->pressure.valid ? YASL_PRIVATE_PEN_PRESSURE : 0) |
+ (p->tilt_x.valid || p->tilt_y.valid ? YASL_PRIVATE_PEN_TILT : 0) |
+ (p->rotation.valid ? YASL_PRIVATE_PEN_ROTATION : 0) |
+ (p->distance.valid ? YASL_PRIVATE_PEN_DISTANCE : 0) |
+ (p->tangential.valid ? YASL_PRIVATE_PEN_TANGENTIAL : 0) |
+ (p->wheel.valid ? YASL_PRIVATE_PEN_WHEEL : 0);
+}
+
+static double axis_normalized(const struct tablet_axis *axis)
+{
+ return axis->valid && axis->max > axis->min && isfinite(axis->value) ?
+ fmax(0, fmin(1, (axis->value - axis->min) / (axis->max - axis->min))) : 0;
+}
+
+static void push_pen_sample(struct x11_surface *s, struct x11_pointer *p,
+ unsigned action, double x, double y, uint32_t changed, unsigned changed_button)
+{
+ if (!p || (p->device != YASL_PRIVATE_PTR_DEV_PEN && p->device != YASL_PRIVATE_PTR_DEV_ERASER)) return;
+ push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_PEN,
+ .pen = {
+ .device_id = (uint64_t)(unsigned)p->id,
+ .tool_id = 0, /* XI2 exposes source IDs, but not a stable per-tool serial. */
+ .x = x, .y = y,
+ .pressure = p->pressure.valid && p->pressure.max > p->pressure.min ?
+ fmax(0, fmin(1, (p->pressure.value - p->pressure.min) /
+ (p->pressure.max - p->pressure.min))) : 0,
+ .tilt_x = p->tilt_x.valid ? fmax(-90, fmin(90, p->tilt_x.value)) : 0,
+ .tilt_y = p->tilt_y.valid ? fmax(-90, fmin(90, p->tilt_y.value)) : 0,
+ .rotation = p->rotation.valid ? p->rotation.value : 0,
+ .distance = axis_normalized(&p->distance),
+ .tangential = p->tangential.valid && p->tangential.max > p->tangential.min ?
+ fmax(-1, fmin(1, 2 * axis_normalized(&p->tangential) - 1)) : 0,
+ .wheel_degrees = p->wheel.valid ? p->wheel.value : 0,
+ .axes_valid = pen_axes_valid(p), .axes_changed = changed,
+ .buttons = p->buttons, .changed_button = changed_button,
+ .in_contact = (p->buttons & YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY)) != 0,
+ .tool = p->device == YASL_PRIVATE_PTR_DEV_ERASER ? 2u : p->tool,
+ .action = action,
+ }}, NULL, 0);
}
static void xi_pointer_event(struct x11_surface *s, const XIDeviceEvent *ev)
{
struct x11_display *d = (struct x11_display *)s->base.display;
struct x11_pointer *p = pointer_for(d, ev->sourceid);
- struct yasl_event event = {
+ struct yasl_private_event event = {
.pointer = {
- .pos = {yasl_scale_to_device(ev->event_x, YASL_SCALE_UNIT),
- yasl_scale_to_device(ev->event_y, YASL_SCALE_UNIT)},
- .device = p ? p->device : YASL_PTR_DEV_MOUSE,
+ .pos = {yasl_private_scale_to_device(ev->event_x, YASL_PRIVATE_SCALE_UNIT),
+ yasl_private_scale_to_device(ev->event_y, YASL_PRIVATE_SCALE_UNIT)},
+ .device = p ? p->device : YASL_PRIVATE_PTR_DEV_MOUSE,
.mods = modifiers(ev->mods.effective),
},
};
- if (p && (p->device == YASL_PTR_DEV_PEN || p->device == YASL_PTR_DEV_ERASER)) {
- update_axes(p, &ev->valuators);
- if (ev->evtype == XI_ButtonRelease && ev->detail == 1)
+ uint32_t pen_changed = 0;
+ if (p && (p->device == YASL_PRIVATE_PTR_DEV_PEN || p->device == YASL_PRIVATE_PTR_DEV_ERASER)) {
+ pen_changed = update_axes(p, &ev->valuators);
+ if (ev->evtype == XI_ButtonRelease && ev->detail == 1) {
p->pressure.value = p->pressure.min;
- event.pointer.pen = (struct yasl_pointer_pen){
+ pen_changed |= p->pressure.valid ? YASL_PRIVATE_PEN_PRESSURE : 0;
+ }
+ if (ev->evtype == XI_ButtonPress || ev->evtype == XI_ButtonRelease) {
+ enum yasl_private_pointer_button button = pointer_button(ev->detail);
+ uint32_t mask = YASL_PRIVATE_BUTTON_BIT(button);
+ if (ev->evtype == XI_ButtonPress) p->buttons |= mask;
+ else p->buttons &= ~mask;
+ }
+ event.pointer.pen = (struct yasl_private_pointer_pen){
pressure_value(&p->pressure), tilt_value(&p->tilt_x), tilt_value(&p->tilt_y)};
}
- if (ev->evtype == XI_Motion) event.type = YASL_EVENT_POINTER_MOTION;
+ if (p) update_scroll(p, &ev->valuators, &event.pointer);
+ if (ev->evtype == XI_Motion) event.type = YASL_PRIVATE_EVENT_POINTER_MOTION;
else if (ev->detail >= 4 && ev->detail <= 7) {
if (ev->evtype == XI_ButtonRelease) return;
- event.type = YASL_EVENT_POINTER_SCROLL;
+ event.type = YASL_PRIVATE_EVENT_POINTER_SCROLL;
if (ev->detail <= 5) event.pointer.scroll.y = ev->detail == 4 ? -1 : 1;
else event.pointer.scroll.x = ev->detail == 6 ? -1 : 1;
+ event.pointer.scroll_valid = 1u;
+ event.pointer.scroll_source = p && p->finger_scroll ? 2u : 1u;
+ event.pointer.scroll_phase = 2u;
} else {
- event.type = ev->evtype == XI_ButtonPress ? YASL_EVENT_POINTER_DOWN : YASL_EVENT_POINTER_UP;
+ event.type = ev->evtype == XI_ButtonPress ? YASL_PRIVATE_EVENT_POINTER_DOWN : YASL_PRIVATE_EVENT_POINTER_UP;
event.pointer.button = pointer_button(ev->detail);
}
d->mods = event.pointer.mods;
+ if (p && (p->device == YASL_PRIVATE_PTR_DEV_PEN || p->device == YASL_PRIVATE_PTR_DEV_ERASER) &&
+ ev->detail != 4 && ev->detail != 5 && ev->detail != 6 && ev->detail != 7) {
+ unsigned action = ev->evtype == XI_Motion ? YASL_PRIVATE_PEN_MOVE :
+ ev->evtype == XI_ButtonPress ? (ev->detail == 1 ? YASL_PRIVATE_PEN_DOWN : YASL_PRIVATE_PEN_BUTTON) :
+ (ev->detail == 1 ? YASL_PRIVATE_PEN_UP : YASL_PRIVATE_PEN_BUTTON);
+ push_pen_sample(s, p, action, ev->event_x, ev->event_y, pen_changed,
+ ev->evtype == XI_Motion ? 0u : (unsigned)ev->detail);
+ }
push(s, event, NULL, 0);
+ if (event.type == YASL_PRIVATE_EVENT_POINTER_MOTION && event.pointer.scroll_valid) {
+ event.type = YASL_PRIVATE_EVENT_POINTER_SCROLL;
+ event.pointer.pos = (struct yasl_private_vector2i){
+ yasl_private_scale_to_device(ev->event_x, YASL_PRIVATE_SCALE_UNIT),
+ yasl_private_scale_to_device(ev->event_y, YASL_PRIVATE_SCALE_UNIT)};
+ push(s, event, NULL, 0);
+ }
}
static void xi_event(struct x11_display *d, XGenericEventCookie *cookie)
@@ -403,15 +584,20 @@ static void xi_event(struct x11_display *d, XGenericEventCookie *cookie)
if (cookie->evtype == XI_HierarchyChanged || cookie->evtype == XI_DeviceChanged) {
if (cookie->evtype == XI_DeviceChanged &&
((XIDeviceChangedEvent *)cookie->data)->reason == XISlaveSwitch) return;
+ for (struct x11_pointer *p = d->pointers; p; p = p->next) {
+ if (!p->buttons) continue;
+ for (struct x11_surface *s = d->surfaces; s; s = s->next)
+ push_pen_sample(s, p, YASL_PRIVATE_PEN_CANCEL, 0, 0, 0, 0);
+ }
clear_pointers(d);
if (cookie->evtype == XI_HierarchyChanged &&
(((XIHierarchyEvent *)cookie->data)->flags &
(XISlaveRemoved | XIDeviceDisabled | XISlaveDetached | XIMasterRemoved))) {
/* Removal/disable may interrupt a drag before a release arrives. */
if (d->drag.active)
- drag_end(d, YASL_DRAG_CANCELLED);
+ drag_end(d, YASL_PRIVATE_DRAG_CANCELLED);
for (struct x11_surface *s = d->surfaces; s; s = s->next)
- push(s, (struct yasl_event){.type = YASL_EVENT_INPUT_RESET}, NULL, 0);
+ push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_INPUT_RESET}, NULL, 0);
}
return;
}
@@ -430,22 +616,22 @@ static void xi_event(struct x11_display *d, XGenericEventCookie *cookie)
XMoveWindow(d->native, d->drag.icon_window,
(int)round(mev->root_x) - d->drag.hotspot.x,
(int)round(mev->root_y) - d->drag.hotspot.y);
- struct yasl_vector2i hpos = {yasl_scale_to_device(mev->event_x, YASL_SCALE_UNIT),
- yasl_scale_to_device(mev->event_y, YASL_SCALE_UNIT)};
+ struct yasl_private_vector2i hpos = {yasl_private_scale_to_device(mev->event_x, YASL_PRIVATE_SCALE_UNIT),
+ yasl_private_scale_to_device(mev->event_y, YASL_PRIVATE_SCALE_UNIT)};
struct x11_surface *hsurf = d->native ?
surface_at_root_pos(d, (int)round(mev->root_x), (int)round(mev->root_y), &hpos) : s;
drag_set_hover(d, hsurf, hpos);
} else if (cookie->evtype == XI_Enter && d->drag.active) {
const XIEnterEvent *eev = cookie->data;
- struct yasl_vector2i hpos = {yasl_scale_to_device(eev->event_x, YASL_SCALE_UNIT),
- yasl_scale_to_device(eev->event_y, YASL_SCALE_UNIT)};
+ struct yasl_private_vector2i hpos = {yasl_private_scale_to_device(eev->event_x, YASL_PRIVATE_SCALE_UNIT),
+ yasl_private_scale_to_device(eev->event_y, YASL_PRIVATE_SCALE_UNIT)};
struct x11_surface *hsurf = d->native ?
surface_at_root_pos(d, (int)round(eev->root_x), (int)round(eev->root_y), &hpos) : s;
drag_set_hover(d, hsurf, hpos);
} else if (cookie->evtype == XI_Leave && d->drag.active) {
const XIEnterEvent *lev = cookie->data;
- struct yasl_vector2i lpos = {yasl_scale_to_device(lev->event_x, YASL_SCALE_UNIT),
- yasl_scale_to_device(lev->event_y, YASL_SCALE_UNIT)};
+ struct yasl_private_vector2i lpos = {yasl_private_scale_to_device(lev->event_x, YASL_PRIVATE_SCALE_UNIT),
+ yasl_private_scale_to_device(lev->event_y, YASL_PRIVATE_SCALE_UNIT)};
drag_leave_hover(d, s, lpos);
} else if (cookie->evtype == XI_ButtonRelease && d->drag.active) {
drag_drop_hover(d);
@@ -455,24 +641,35 @@ static void xi_event(struct x11_display *d, XGenericEventCookie *cookie)
else {
const XIEnterEvent *ev = cookie->data;
struct x11_pointer *p = pointer_for(d, ev->sourceid);
- if (p) p->pressure.value = p->pressure.min;
- push(s, (struct yasl_event){
- .type = cookie->evtype == XI_Leave ? YASL_EVENT_POINTER_LEAVE : YASL_EVENT_POINTER_MOTION,
+ if (p && cookie->evtype == XI_Leave) {
+ p->pressure.value = p->pressure.min;
+ if (p->pressure.valid) push_pen_sample(s, p, YASL_PRIVATE_PEN_LEAVE,
+ ev->event_x, ev->event_y, YASL_PRIVATE_PEN_PRESSURE, 0);
+ } else if (p && (p->device == YASL_PRIVATE_PTR_DEV_PEN || p->device == YASL_PRIVATE_PTR_DEV_ERASER)) {
+ push_pen_sample(s, p, YASL_PRIVATE_PEN_ENTER, ev->event_x, ev->event_y, 0, 0);
+ }
+ push(s, (struct yasl_private_event){
+ .type = cookie->evtype == XI_Leave ? YASL_PRIVATE_EVENT_POINTER_LEAVE : YASL_PRIVATE_EVENT_POINTER_MOTION,
.pointer = {
- .pos = {yasl_scale_to_device(ev->event_x, YASL_SCALE_UNIT),
- yasl_scale_to_device(ev->event_y, YASL_SCALE_UNIT)},
- .device = p ? p->device : YASL_PTR_DEV_MOUSE,
+ .pos = {yasl_private_scale_to_device(ev->event_x, YASL_PRIVATE_SCALE_UNIT),
+ yasl_private_scale_to_device(ev->event_y, YASL_PRIVATE_SCALE_UNIT)},
+ .device = p ? p->device : YASL_PRIVATE_PTR_DEV_MOUSE,
.mods = modifiers(ev->mods.effective),
},
}, NULL, 0);
}
}
-static bool resize(struct x11_surface *s, struct yasl_vector2i size)
+static bool resize(struct x11_surface *s, struct yasl_private_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->base.presenter != YASL_PRIVATE_PRESENTER_CPU) {
+ s->size = size;
+ s->base.size = size;
+ return true;
+ }
if (s->image && size.x == s->size.x && size.y == s->size.y) return true;
struct x11_display *d = (struct x11_display *)s->base.display;
if (!d->native) {
@@ -480,7 +677,7 @@ static bool resize(struct x11_surface *s, struct yasl_vector2i size)
s->want_draw = true;
return true;
}
- uint64_t start = yasl_loop_now_ns();
+ uint64_t start = yasl_private_loop_now_ns();
/* The presented frame goes with the old pixels. */
s->presented = false;
@@ -557,9 +754,9 @@ static bool resize(struct x11_surface *s, struct yasl_vector2i size)
s->pixels = pixels;
s->size = size;
s->base.size = size;
- s->base.scale_120 = YASL_SCALE_UNIT;
+ s->base.scale_120 = YASL_PRIVATE_SCALE_UNIT;
s->want_draw = true;
- yasl_clock_note_resize(&s->clock, yasl_loop_now_ns() - start);
+ yasl_private_clock_note_resize(&s->clock, yasl_private_loop_now_ns() - start);
return true;
}
@@ -575,7 +772,7 @@ static void cancel_pastes(struct x11_display *d, struct x11_surface *target)
p = &r->next;
} else if (!target) {
*p = r->next;
- if (r->timeout) yasl_loop_del_timer(d->loop, r->timeout);
+ if (r->timeout) yasl_private_loop_del_timer(d->loop, r->timeout);
XDestroyWindow(d->native, r->window);
free(r);
} else {
@@ -584,7 +781,7 @@ static void cancel_pastes(struct x11_display *d, struct x11_surface *target)
}
}
-static uint64_t paste_timeout(void *ud, yasl_timer_id id, uint64_t interval_ns)
+static uint64_t paste_timeout(void *ud, yasl_private_timer_id id, uint64_t interval_ns)
{
(void)id;
(void)interval_ns;
@@ -597,7 +794,7 @@ static uint64_t paste_timeout(void *ud, yasl_timer_id id, uint64_t interval_ns)
r->timeout = 0; /* The loop retires this one-shot timer after the callback. */
XDestroyWindow(d->native, r->window);
if (s && !s->destroyed)
- yasl_surface_complete_paste(&s->base, r->id, YASL_TRANSFER_FAILED, NULL, 0);
+ yasl_private_surface_complete_paste(&s->base, r->id, YASL_PRIVATE_TRANSFER_FAILED, NULL, 0);
free(r);
return 0;
}
@@ -609,7 +806,7 @@ static void surface_destroy(struct yasl_surface *base)
struct x11_display *d = (struct x11_display *)base->display;
if (d->drag.active) {
if (d->drag.origin == s || d->drag.carried == s)
- drag_end(d, YASL_DRAG_CANCELLED);
+ drag_end(d, YASL_PRIVATE_DRAG_CANCELLED);
else if (d->drag.hover == s)
d->drag.hover = NULL;
}
@@ -627,6 +824,10 @@ static void surface_destroy(struct yasl_surface *base)
free(s->ime_preedit);
s->ime_preedit = NULL;
if (s->ic) XDestroyIC(s->ic);
+#if YASL_HAS_EGL
+ if (s->gl_context && d->egl_ready) eglDestroyContext(d->egl, s->gl_context);
+ if (s->gl_surface && d->egl_ready) eglDestroySurface(d->egl, s->gl_surface);
+#endif
if (s->shm_active) {
XShmDetach(d->native, &s->shm_info);
if (s->image) XDestroyImage(s->image);
@@ -643,6 +844,7 @@ static void surface_destroy(struct yasl_surface *base)
if (d->native) {
if (s->gc) XFreeGC(d->native, s->gc);
if (s->window) XDestroyWindow(d->native, s->window);
+ if (s->colormap) XFreeColormap(d->native, s->colormap);
}
input_queue_clear(&s->input);
if (d->depth) {
@@ -652,11 +854,11 @@ static void surface_destroy(struct yasl_surface *base)
}
static void set_callbacks(struct yasl_surface *base,
- const struct yasl_window_cbs *cbs, void *ud)
+ const struct yasl_private_window_cbs *cbs, void *ud)
{
struct x11_surface *s = (struct x11_surface *)base;
if (s->destroyed) return;
- s->base.callbacks = cbs ? *cbs : (struct yasl_window_cbs){0};
+ s->base.callbacks = cbs ? *cbs : (struct yasl_private_window_cbs){0};
s->cbs = &s->base.callbacks;
s->base.ud = ud;
s->ud = ud;
@@ -685,7 +887,7 @@ static bool net_message(struct x11_surface *s, Atom type,
SubstructureRedirectMask | SubstructureNotifyMask, &event) != 0;
}
-static bool surface_set_size(struct yasl_surface *base, struct yasl_vector2i size)
+static bool surface_set_size(struct yasl_surface *base, struct yasl_private_vector2i size)
{
struct x11_surface *s = (struct x11_surface *)base;
struct x11_display *d = (struct x11_display *)base->display;
@@ -817,7 +1019,7 @@ static char *xim_text_dup(const XIMText *text, size_t *len)
if (converted != text->length) { free(out); return NULL; }
}
out[used] = '\0';
- if (!yasl_utf8_validate(out, used)) { free(out); return NULL; }
+ if (!yasl_private_utf8_validate(out, used)) { free(out); return NULL; }
*len = used;
return out;
}
@@ -831,7 +1033,7 @@ static void xim_preedit_done(XIM im, XPointer client_data, XPointer call_data)
free(s->ime_preedit);
s->ime_preedit = NULL;
s->ime_cursor_begin = s->ime_cursor_end = -1;
- yasl_surface_notify_composition(&s->base, NULL, -1, -1);
+ yasl_private_surface_notify_composition(&s->base, NULL, -1, -1);
}
static int xim_preedit_start(XIM im, XPointer client_data, XPointer call_data)
@@ -880,7 +1082,7 @@ static void xim_preedit_draw(XIM im, XPointer client_data, XPointer call_data)
int caret = draw->caret < 0 ? -1 : (int)utf8_offset_for_chars(
s->ime_preedit ? s->ime_preedit : "", new_len, draw->caret);
s->ime_cursor_begin = s->ime_cursor_end = caret;
- yasl_surface_notify_composition(&s->base, s->ime_preedit, caret, caret);
+ yasl_private_surface_notify_composition(&s->base, s->ime_preedit, caret, caret);
}
static void xim_preedit_caret(XIM im, XPointer client_data, XPointer call_data)
@@ -892,7 +1094,7 @@ static void xim_preedit_caret(XIM im, XPointer client_data, XPointer call_data)
int offset = caret->position < 0 ? -1 : (int)utf8_offset_for_chars(
s->ime_preedit, strlen(s->ime_preedit), caret->position);
s->ime_cursor_begin = s->ime_cursor_end = offset;
- yasl_surface_notify_composition(&s->base, s->ime_preedit, offset, offset);
+ yasl_private_surface_notify_composition(&s->base, s->ime_preedit, offset, offset);
}
static XIC create_ic(struct x11_display *d, struct x11_surface *s)
@@ -923,22 +1125,35 @@ static XIC create_ic(struct x11_display *d, struct x11_surface *s)
return ic;
}
-static struct yasl_surface *create_toplevel(struct yasl_display *base,
- const struct yasl_window_spec *spec, void *graphics_device)
+static struct yasl_surface *create_toplevel_visual(struct yasl_private_display *base,
+ const struct yasl_private_window_spec *spec, Visual *visual, int depth,
+ enum yasl_private_presenter presenter)
{
- (void)graphics_device;
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;
+ s->base.creation_flags = spec->flags | (spec->external_gpu ? 0u : YASL_PRIVATE_SURFACE_CPU);
+ s->base.presenter = presenter;
+ s->clock.refresh_ns = YASL_PRIVATE_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);
+ if (!visual) visual = DefaultVisual(d->native, d->screen);
+ if (!depth) depth = DefaultDepth(d->native, d->screen);
+ s->colormap = XCreateColormap(d->native, RootWindow(d->native, d->screen), visual, AllocNone);
+ XSetWindowAttributes attrs = {.colormap = s->colormap, .border_pixel = 0, .background_pixel = 0};
+ s->window = XCreateWindow(d->native, RootWindow(d->native, d->screen), 0, 0,
+ (unsigned)spec->size.x, (unsigned)spec->size.y, 0, depth, InputOutput,
+ visual, CWBorderPixel | CWBackPixel | CWColormap, &attrs);
+ if (!s->window) {
+ XFreeColormap(d->native, s->colormap);
+ free(s);
+ errno = ENODEV;
+ return NULL;
+ }
s->base.native_handle = (void *)(uintptr_t)s->window;
s->base.size = s->size;
- s->base.scale_120 = YASL_SCALE_UNIT;
+ s->base.scale_120 = YASL_PRIVATE_SCALE_UNIT;
s->gc = XCreateGC(d->native, s->window, 0, NULL);
XSelectInput(d->native, s->window, ExposureMask | StructureNotifyMask |
FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask |
@@ -955,6 +1170,19 @@ static struct yasl_surface *create_toplevel(struct yasl_display *base,
XISelectEvents(d->native, s->window, &mask, 1);
}
XSetWMProtocols(d->native, s->window, &d->delete_window, 1);
+ if (spec->flags & YASL_PRIVATE_SURFACE_UNDECORATED) {
+ struct { unsigned long flags, functions, decorations; long input_mode; unsigned long status; } hints = {
+ .flags = 1, .decorations = 0,
+ };
+ XChangeProperty(d->native, s->window, d->motif_hints, d->motif_hints, 32,
+ PropModeReplace, (unsigned char *)&hints, 5);
+ }
+ if (spec->flags & YASL_PRIVATE_SURFACE_FIXED_SIZE) {
+ XSizeHints size_hints = {.flags = PMinSize | PMaxSize,
+ .min_width = spec->size.x, .max_width = spec->size.x,
+ .min_height = spec->size.y, .max_height = spec->size.y};
+ XSetWMNormalHints(d->native, s->window, &size_hints);
+ }
XClassHint hint = { .res_name = d->app_id, .res_class = d->app_id };
XSetClassHint(d->native, s->window, &hint);
Atom xdnd_version = 5;
@@ -967,17 +1195,197 @@ static struct yasl_surface *create_toplevel(struct yasl_display *base,
if (spec->fullscreen) {
XChangeProperty(d->native, s->window, d->net_state, XA_ATOM, 32,
PropModeReplace, (unsigned char *)&d->fullscreen, 1);
- s->base.state |= YASL_STATE_FULLSCREEN;
+ s->base.state |= YASL_PRIVATE_STATE_FULLSCREEN;
}
- XMapWindow(d->native, s->window);
+ if (!(spec->flags & YASL_PRIVATE_SURFACE_HIDDEN)) XMapWindow(d->native, s->window);
XFlush(d->native);
return &s->base;
}
+static struct yasl_surface *create_toplevel(struct yasl_private_display *base,
+ const struct yasl_private_window_spec *spec, void *graphics_device)
+{
+ if (!spec) return NULL;
+ if (graphics_device) { errno = ENOTSUP; return NULL; }
+ return create_toplevel_visual(base, spec, NULL, 0,
+ spec->external_gpu ? YASL_PRIVATE_PRESENTER_NONE : YASL_PRIVATE_PRESENTER_CPU);
+}
+
+#if YASL_HAS_EGL
+static bool egl_open(struct x11_display *d)
+{
+ if (d->egl_ready) return true;
+ d->egl = eglGetDisplay((EGLNativeDisplayType)d->native);
+ EGLint major = 0, minor = 0;
+ if (d->egl == EGL_NO_DISPLAY || !eglInitialize(d->egl, &major, &minor)) {
+ errno = ENOTSUP;
+ return false;
+ }
+ d->egl_ready = true;
+ return true;
+}
+
+static struct yasl_surface *create_gl_surface(struct yasl_private_display *base,
+ const struct yasl_private_window_spec *spec, const int *attrs,
+ struct yasl_surface *share)
+{
+ if (!spec) { errno = EINVAL; return NULL; }
+ struct x11_display *d = (struct x11_display *)base;
+ struct yasl_private_gl_options options;
+ if (!yasl_private_gl_parse_options(attrs, &options)) { errno = EINVAL; return NULL; }
+ if (!egl_open(d)) return NULL;
+ if (options.srgb) {
+ const char *extensions = eglQueryString(d->egl, EGL_EXTENSIONS);
+ if (!extensions || !strstr(extensions, "EGL_KHR_gl_colorspace")) {
+ errno = ENOTSUP;
+ return NULL;
+ }
+ }
+
+ EGLint renderable = options.api == YASL_PRIVATE_GL_OPENGL_ES ? EGL_OPENGL_ES2_BIT : EGL_OPENGL_BIT;
+ EGLint config_attrs[] = {
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+ EGL_RENDERABLE_TYPE, renderable,
+ EGL_RED_SIZE, options.red_bits,
+ EGL_GREEN_SIZE, options.green_bits,
+ EGL_BLUE_SIZE, options.blue_bits,
+ EGL_ALPHA_SIZE, options.alpha_bits,
+ EGL_DEPTH_SIZE, options.depth_bits,
+ EGL_STENCIL_SIZE, options.stencil_bits,
+ EGL_SAMPLE_BUFFERS, options.samples > 0 ? 1 : 0,
+ EGL_SAMPLES, options.samples,
+ EGL_NONE,
+ };
+ EGLConfig config = NULL;
+ EGLint count = 0;
+ if (!eglChooseConfig(d->egl, config_attrs, &config, 1, &count) || !count) {
+ errno = ENOTSUP;
+ return NULL;
+ }
+ EGLint visual_id = 0, depth = 0;
+ if (!eglGetConfigAttrib(d->egl, config, EGL_NATIVE_VISUAL_ID, &visual_id) ||
+ !eglGetConfigAttrib(d->egl, config, EGL_BUFFER_SIZE, &depth)) {
+ errno = ENODEV;
+ return NULL;
+ }
+ XVisualInfo visual_query = {.visualid = (VisualID)visual_id};
+ int visual_count = 0;
+ XVisualInfo *visual_info = XGetVisualInfo(d->native, VisualIDMask, &visual_query, &visual_count);
+ if (!visual_info || !visual_count) {
+ if (visual_info) XFree(visual_info);
+ errno = ENODEV;
+ return NULL;
+ }
+ Visual *visual = visual_info[0].visual;
+ depth = visual_info[0].depth;
+ struct yasl_private_window_spec gl_spec = *spec;
+ gl_spec.external_gpu = true;
+ gl_spec.flags &= ~YASL_PRIVATE_SURFACE_CPU;
+ struct yasl_surface *base_surface = create_toplevel_visual(base, &gl_spec, visual, depth,
+ YASL_PRIVATE_PRESENTER_GL);
+ XFree(visual_info);
+ if (!base_surface) return NULL;
+ struct x11_surface *s = (struct x11_surface *)base_surface;
+ s->gl_config = config;
+ EGLint surface_attrs[] = {EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR, EGL_NONE};
+ s->gl_surface = eglCreateWindowSurface(d->egl, config,
+ (EGLNativeWindowType)s->window, options.srgb ? surface_attrs : NULL);
+ if (s->gl_surface == EGL_NO_SURFACE) {
+ surface_destroy(base_surface);
+ errno = ENODEV;
+ return NULL;
+ }
+ if (options.api == YASL_PRIVATE_GL_OPENGL) {
+ if (!eglBindAPI(EGL_OPENGL_API)) { surface_destroy(base_surface); errno = ENOTSUP; return NULL; }
+ } else if (!eglBindAPI(EGL_OPENGL_ES_API)) {
+ surface_destroy(base_surface); errno = ENOTSUP; return NULL;
+ }
+ EGLContext share_context = EGL_NO_CONTEXT;
+ if (share) {
+ struct x11_surface *shared = (struct x11_surface *)share;
+ if (share->display != base || shared->base.presenter != YASL_PRIVATE_PRESENTER_GL ||
+ shared->gl_context == EGL_NO_CONTEXT) {
+ surface_destroy(base_surface); errno = EINVAL; return NULL;
+ }
+ share_context = shared->gl_context;
+ }
+ EGLint context_attrs[12];
+ EGLint n = 0;
+ context_attrs[n++] = EGL_CONTEXT_MAJOR_VERSION;
+ context_attrs[n++] = options.major;
+ context_attrs[n++] = EGL_CONTEXT_MINOR_VERSION;
+ context_attrs[n++] = options.minor;
+ if (options.api == YASL_PRIVATE_GL_OPENGL && options.profile != YASL_PRIVATE_GL_ANY) {
+ context_attrs[n++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR;
+ context_attrs[n++] = options.profile == YASL_PRIVATE_GL_CORE ?
+ EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR : EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR;
+ }
+ EGLint context_flags = (options.debug ? EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR : 0) |
+ (options.forward_compat ? EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR : 0);
+ if (context_flags) {
+ context_attrs[n++] = EGL_CONTEXT_FLAGS_KHR;
+ context_attrs[n++] = context_flags;
+ }
+ context_attrs[n] = EGL_NONE;
+ s->gl_context = eglCreateContext(d->egl, config, share_context, context_attrs);
+ if (s->gl_context == EGL_NO_CONTEXT) {
+ surface_destroy(base_surface);
+ errno = ENOTSUP;
+ return NULL;
+ }
+ return base_surface;
+}
+
+static bool gl_make_current(struct yasl_private_display *base, struct yasl_surface *surface)
+{
+ struct x11_display *d = (struct x11_display *)base;
+ if (!d->egl_ready) { errno = ENOTSUP; return false; }
+ if (!surface) return eglMakeCurrent(d->egl, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ struct x11_surface *s = (struct x11_surface *)surface;
+ if (surface->display != base || s->base.presenter != YASL_PRIVATE_PRESENTER_GL || !s->gl_context) {
+ errno = EINVAL;
+ return false;
+ }
+ return eglMakeCurrent(d->egl, s->gl_surface, s->gl_surface, s->gl_context) == EGL_TRUE;
+}
+
+static yasl_private_gl_proc gl_get_proc_address(struct yasl_private_display *base, const char *name)
+{
+ (void)base;
+ return name ? (yasl_private_gl_proc)eglGetProcAddress(name) : NULL;
+}
+
+static bool gl_swap_interval(struct yasl_private_display *base, struct yasl_surface *surface, int interval)
+{
+ struct x11_display *d = (struct x11_display *)base;
+ if (!surface || surface->display != base || ((struct x11_surface *)surface)->base.presenter != YASL_PRIVATE_PRESENTER_GL) {
+ errno = EINVAL;
+ return false;
+ }
+ return d->egl_ready && eglSwapInterval(d->egl, interval) == EGL_TRUE;
+}
+
+static bool gl_swap_buffers(struct yasl_private_display *base, struct yasl_surface *surface)
+{
+ struct x11_display *d = (struct x11_display *)base;
+ struct x11_surface *s = (struct x11_surface *)surface;
+ if (!surface || surface->display != base || s->base.presenter != YASL_PRIVATE_PRESENTER_GL || !d->egl_ready) {
+ errno = EINVAL;
+ return false;
+ }
+ if (!eglSwapBuffers(d->egl, s->gl_surface)) return false;
+ s->presented = true;
+ yasl_private_clock_presented(&s->clock, yasl_private_loop_now_ns(), false);
+ return true;
+}
+#endif
+
static bool popup_spec_valid(const struct x11_surface *parent,
- const struct yasl_popup_spec *spec)
+ const struct yasl_private_popup_spec *spec)
{
if (!parent || !spec) return false;
+ if (spec->positioner_explicit && (spec->anchor_edges > 8 || spec->gravity > 8 ||
+ (spec->constraints & ~63u))) return false;
int parent_cx = parent->is_popup ? parent->content_offset.x : 0;
int parent_cy = parent->is_popup ? parent->content_offset.y : 0;
int64_t x = (int64_t)spec->anchor.x - parent_cx;
@@ -992,10 +1400,87 @@ static bool popup_spec_valid(const struct x11_surface *parent,
(int64_t)spec->content_offset.y + spec->content_size.y <= spec->size.y;
}
+enum { X11_ANCHOR_NONE, X11_ANCHOR_TOP, X11_ANCHOR_BOTTOM, X11_ANCHOR_LEFT,
+ X11_ANCHOR_RIGHT, X11_ANCHOR_TOP_LEFT, X11_ANCHOR_BOTTOM_LEFT,
+ X11_ANCHOR_TOP_RIGHT, X11_ANCHOR_BOTTOM_RIGHT };
+enum { X11_CONSTRAINT_SLIDE_X = 1u, X11_CONSTRAINT_SLIDE_Y = 2u,
+ X11_CONSTRAINT_FLIP_X = 4u, X11_CONSTRAINT_FLIP_Y = 8u,
+ X11_CONSTRAINT_RESIZE_X = 16u, X11_CONSTRAINT_RESIZE_Y = 32u };
+
+static void popup_anchor_point(const struct yasl_private_popup_spec *spec, int *x, int *y)
+{
+ switch (spec->anchor_edges) {
+ case X11_ANCHOR_TOP: *x = spec->anchor.x + spec->anchor.width / 2; *y = spec->anchor.y; break;
+ case X11_ANCHOR_BOTTOM: *x = spec->anchor.x + spec->anchor.width / 2; *y = spec->anchor.y + spec->anchor.height; break;
+ case X11_ANCHOR_LEFT: *x = spec->anchor.x; *y = spec->anchor.y + spec->anchor.height / 2; break;
+ case X11_ANCHOR_RIGHT: *x = spec->anchor.x + spec->anchor.width; *y = spec->anchor.y + spec->anchor.height / 2; break;
+ case X11_ANCHOR_TOP_LEFT: *x = spec->anchor.x; *y = spec->anchor.y; break;
+ case X11_ANCHOR_BOTTOM_LEFT: *x = spec->anchor.x; *y = spec->anchor.y + spec->anchor.height; break;
+ case X11_ANCHOR_TOP_RIGHT: *x = spec->anchor.x + spec->anchor.width; *y = spec->anchor.y; break;
+ case X11_ANCHOR_BOTTOM_RIGHT: *x = spec->anchor.x + spec->anchor.width; *y = spec->anchor.y + spec->anchor.height; break;
+ default: *x = spec->anchor.x + spec->anchor.width / 2; *y = spec->anchor.y + spec->anchor.height / 2; break;
+ }
+}
+
+static unsigned popup_flip_x(unsigned gravity)
+{
+ switch (gravity) {
+ case X11_ANCHOR_LEFT: return X11_ANCHOR_RIGHT;
+ case X11_ANCHOR_RIGHT: return X11_ANCHOR_LEFT;
+ case X11_ANCHOR_TOP_LEFT: return X11_ANCHOR_TOP_RIGHT;
+ case X11_ANCHOR_BOTTOM_LEFT: return X11_ANCHOR_BOTTOM_RIGHT;
+ case X11_ANCHOR_TOP_RIGHT: return X11_ANCHOR_TOP_LEFT;
+ case X11_ANCHOR_BOTTOM_RIGHT: return X11_ANCHOR_BOTTOM_LEFT;
+ default: return gravity;
+ }
+}
+
+static unsigned popup_flip_y(unsigned gravity)
+{
+ switch (gravity) {
+ case X11_ANCHOR_TOP: return X11_ANCHOR_BOTTOM;
+ case X11_ANCHOR_BOTTOM: return X11_ANCHOR_TOP;
+ case X11_ANCHOR_TOP_LEFT: return X11_ANCHOR_BOTTOM_LEFT;
+ case X11_ANCHOR_TOP_RIGHT: return X11_ANCHOR_BOTTOM_RIGHT;
+ case X11_ANCHOR_BOTTOM_LEFT: return X11_ANCHOR_TOP_LEFT;
+ case X11_ANCHOR_BOTTOM_RIGHT: return X11_ANCHOR_TOP_RIGHT;
+ default: return gravity;
+ }
+}
+
+static void popup_position_at(const struct yasl_private_popup_spec *spec, int ax, int ay,
+ unsigned gravity, struct yasl_private_vector2i size, int *x, int *y)
+{
+ int content_w = spec->content_size.x ? spec->content_size.x : size.x - spec->content_offset.x;
+ int content_h = spec->content_size.y ? spec->content_size.y : size.y - spec->content_offset.y;
+ switch (gravity) {
+ case X11_ANCHOR_LEFT: case X11_ANCHOR_TOP_LEFT: case X11_ANCHOR_BOTTOM_LEFT:
+ *x = ax - content_w - spec->content_offset.x; break;
+ case X11_ANCHOR_RIGHT: case X11_ANCHOR_TOP_RIGHT: case X11_ANCHOR_BOTTOM_RIGHT:
+ *x = ax - spec->content_offset.x; break;
+ default: *x = ax - content_w / 2 - spec->content_offset.x; break;
+ }
+ switch (gravity) {
+ case X11_ANCHOR_TOP: case X11_ANCHOR_TOP_LEFT: case X11_ANCHOR_TOP_RIGHT:
+ *y = ay - content_h - spec->content_offset.y; break;
+ case X11_ANCHOR_BOTTOM: case X11_ANCHOR_BOTTOM_LEFT: case X11_ANCHOR_BOTTOM_RIGHT:
+ *y = ay - spec->content_offset.y; break;
+ default: *y = ay - content_h / 2 - spec->content_offset.y; break;
+ }
+}
+
+static int popup_overflow(int pos, int extent, int low, int high)
+{
+ int64_t end = (int64_t)pos + extent;
+ int64_t overflow = (pos < low ? (int64_t)low - pos : 0) + (end > high ? end - high : 0);
+ return overflow > INT_MAX ? INT_MAX : (int)overflow;
+}
+
static void popup_calculate_position(struct x11_display *d,
struct x11_surface *parent,
- const struct yasl_popup_spec *spec,
- int *out_x, int *out_y)
+ const struct yasl_private_popup_spec *spec,
+ int *out_x, int *out_y,
+ struct yasl_private_vector2i *out_size)
{
int parent_rx = 0, parent_ry = 0;
if (d->native) {
@@ -1007,16 +1492,23 @@ static void popup_calculate_position(struct x11_display *d,
int parent_cx = parent->is_popup ? parent->content_offset.x : 0;
int parent_cy = parent->is_popup ? parent->content_offset.y : 0;
- int anchor_x = parent_rx + (spec->anchor.x - parent_cx);
- int anchor_y = parent_ry + (spec->anchor.y - parent_cy);
-
- int root_x, root_y;
- if (spec->beside) {
- root_x = anchor_x + spec->anchor.width - spec->content_offset.x;
- root_y = anchor_y - spec->content_offset.y;
+ struct yasl_private_vector2i size = spec->size;
+ int anchor_x, anchor_y, root_x, root_y;
+ if (spec->positioner_explicit) {
+ popup_anchor_point(spec, &anchor_x, &anchor_y);
+ anchor_x += parent_rx - parent_cx;
+ anchor_y += parent_ry - parent_cy;
+ popup_position_at(spec, anchor_x, anchor_y, spec->gravity, size, &root_x, &root_y);
} else {
- root_x = anchor_x - spec->content_offset.x;
- root_y = anchor_y + spec->anchor.height - spec->content_offset.y;
+ anchor_x = parent_rx + (spec->anchor.x - parent_cx);
+ anchor_y = parent_ry + (spec->anchor.y - parent_cy);
+ if (spec->beside) {
+ root_x = anchor_x + spec->anchor.width - spec->content_offset.x;
+ root_y = anchor_y - spec->content_offset.y;
+ } else {
+ root_x = anchor_x - spec->content_offset.x;
+ root_y = anchor_y + spec->anchor.height - spec->content_offset.y;
+ }
}
int mon_x = 0, mon_y = 0;
@@ -1043,29 +1535,69 @@ static void popup_calculate_position(struct x11_display *d,
}
#endif
- if (spec->beside) {
- if (root_x + spec->size.x > mon_x + mon_w) {
- int flipped_x = anchor_x - spec->size.x + spec->content_offset.x;
- if (flipped_x >= mon_x) root_x = flipped_x;
+ if (spec->positioner_explicit) {
+ if (spec->constraints & X11_CONSTRAINT_FLIP_X) {
+ int flip_x, flip_y;
+ /* Reflect the selected anchor edge across the anchor rectangle center. */
+ int anchor_origin_x = parent_rx - parent_cx + spec->anchor.x;
+ int flipped_anchor_x = 2 * anchor_origin_x + spec->anchor.width - anchor_x;
+ popup_position_at(spec, flipped_anchor_x, anchor_y, popup_flip_x(spec->gravity), size, &flip_x, &flip_y);
+ if (popup_overflow(flip_x, size.x, mon_x, mon_x + mon_w) <
+ popup_overflow(root_x, size.x, mon_x, mon_x + mon_w)) root_x = flip_x;
+ }
+ if (spec->constraints & X11_CONSTRAINT_FLIP_Y) {
+ int flip_x, flip_y;
+ int anchor_origin_y = parent_ry - parent_cy + spec->anchor.y;
+ int flipped_anchor_y = 2 * anchor_origin_y + spec->anchor.height - anchor_y;
+ popup_position_at(spec, anchor_x, flipped_anchor_y, popup_flip_y(spec->gravity), size, &flip_x, &flip_y);
+ if (popup_overflow(flip_y, size.y, mon_y, mon_y + mon_h) <
+ popup_overflow(root_y, size.y, mon_y, mon_y + mon_h)) root_y = flip_y;
+ }
+ if (spec->constraints & X11_CONSTRAINT_SLIDE_X) {
+ if (root_x < mon_x) root_x = mon_x;
+ if (root_x + size.x > mon_x + mon_w) root_x = mon_x + mon_w - size.x;
+ }
+ if (spec->constraints & X11_CONSTRAINT_SLIDE_Y) {
+ if (root_y < mon_y) root_y = mon_y;
+ if (root_y + size.y > mon_y + mon_h) root_y = mon_y + mon_h - size.y;
+ }
+ if ((spec->constraints & X11_CONSTRAINT_RESIZE_X) &&
+ popup_overflow(root_x, size.x, mon_x, mon_x + mon_w)) {
+ if (root_x >= mon_x + mon_w) root_x = mon_x + mon_w - 1;
+ if (root_x < mon_x) { size.x -= mon_x - root_x; root_x = mon_x; }
+ if (root_x + size.x > mon_x + mon_w) size.x = mon_x + mon_w - root_x;
+ if (size.x < 1) size.x = 1;
+ }
+ if ((spec->constraints & X11_CONSTRAINT_RESIZE_Y) &&
+ popup_overflow(root_y, size.y, mon_y, mon_y + mon_h)) {
+ if (root_y >= mon_y + mon_h) root_y = mon_y + mon_h - 1;
+ if (root_y < mon_y) { size.y -= mon_y - root_y; root_y = mon_y; }
+ if (root_y + size.y > mon_y + mon_h) size.y = mon_y + mon_h - root_y;
+ if (size.y < 1) size.y = 1;
}
} else {
- if (root_y + spec->size.y > mon_y + mon_h) {
- int flipped_y = anchor_y - spec->size.y + spec->content_offset.y;
+ if (spec->beside) {
+ if (root_x + size.x > mon_x + mon_w) {
+ int flipped_x = anchor_x - size.x + spec->content_offset.x;
+ if (flipped_x >= mon_x) root_x = flipped_x;
+ }
+ } else if (root_y + size.y > mon_y + mon_h) {
+ int flipped_y = anchor_y - size.y + spec->content_offset.y;
if (flipped_y >= mon_y) root_y = flipped_y;
}
+ if (root_x + size.x > mon_x + mon_w) root_x = mon_x + mon_w - size.x;
+ if (root_x < mon_x) root_x = mon_x;
+ if (root_y + size.y > mon_y + mon_h) root_y = mon_y + mon_h - size.y;
+ if (root_y < mon_y) root_y = mon_y;
}
- if (root_x + spec->size.x > mon_x + mon_w) root_x = mon_x + mon_w - spec->size.x;
- if (root_x < mon_x) root_x = mon_x;
- if (root_y + spec->size.y > mon_y + mon_h) root_y = mon_y + mon_h - spec->size.y;
- if (root_y < mon_y) root_y = mon_y;
-
*out_x = root_x;
*out_y = root_y;
+ if (out_size) *out_size = size;
}
static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_base,
- const struct yasl_popup_spec *spec)
+ const struct yasl_private_popup_spec *spec)
{
struct x11_surface *parent = (struct x11_surface *)parent_base;
if (!parent || parent->destroyed || !popup_spec_valid(parent, spec))
@@ -1074,14 +1606,17 @@ static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_bas
struct x11_surface *s = calloc(1, sizeof(*s));
if (!s) return NULL;
s->base.display = parent_base->display;
+ s->base.creation_flags = parent_base->creation_flags;
+ s->base.presenter = (parent_base->creation_flags & YASL_PRIVATE_SURFACE_CPU) ?
+ YASL_PRIVATE_PRESENTER_CPU : YASL_PRIVATE_PRESENTER_NONE;
s->parent = parent;
s->is_popup = true;
s->content_offset = spec->content_offset;
s->clock.refresh_ns = parent->clock.refresh_ns;
- if (!resize(s, spec->size)) { free(s); return NULL; }
-
int root_x = 0, root_y = 0;
- popup_calculate_position(d, parent, spec, &root_x, &root_y);
+ struct yasl_private_vector2i actual_size;
+ popup_calculate_position(d, parent, spec, &root_x, &root_y, &actual_size);
+ if (!resize(s, actual_size)) { free(s); return NULL; }
if (d->native) {
XSetWindowAttributes attrs = {
@@ -1089,7 +1624,7 @@ static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_bas
.save_under = True,
};
s->window = XCreateWindow(d->native, RootWindow(d->native, d->screen),
- root_x, root_y, (unsigned)spec->size.x, (unsigned)spec->size.y,
+ root_x, root_y, (unsigned)actual_size.x, (unsigned)actual_size.y,
0, CopyFromParent, InputOutput, CopyFromParent,
CWOverrideRedirect | CWSaveUnder, &attrs);
if (!s->window) {
@@ -1098,7 +1633,7 @@ static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_bas
}
s->base.native_handle = (void *)(uintptr_t)s->window;
s->base.size = s->size;
- s->base.scale_120 = YASL_SCALE_UNIT;
+ s->base.scale_120 = YASL_PRIVATE_SCALE_UNIT;
s->gc = XCreateGC(d->native, s->window, 0, NULL);
XSelectInput(d->native, s->window, ExposureMask | StructureNotifyMask |
FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask |
@@ -1127,25 +1662,27 @@ static struct yasl_surface *surface_create_popup(struct yasl_surface *parent_bas
}
static bool surface_popup_reposition(struct yasl_surface *popup_base,
- const struct yasl_popup_spec *spec)
+ const struct yasl_private_popup_spec *spec)
{
struct x11_surface *s = (struct x11_surface *)popup_base;
if (!s || s->destroyed || !s->is_popup || !s->parent || s->parent->destroyed ||
!popup_spec_valid(s->parent, spec))
return false;
struct x11_display *d = (struct x11_display *)popup_base->display;
- if (spec->size.x != s->size.x || spec->size.y != s->size.y) {
- if (!resize(s, spec->size))
- return false;
- }
s->content_offset = spec->content_offset;
int root_x = 0, root_y = 0;
- popup_calculate_position(d, s->parent, spec, &root_x, &root_y);
+ struct yasl_private_vector2i actual_size;
+ popup_calculate_position(d, s->parent, spec, &root_x, &root_y, &actual_size);
+ bool changed_size = actual_size.x != s->size.x || actual_size.y != s->size.y;
+ if (changed_size && !resize(s, actual_size)) return false;
if (d->native && s->window) {
XMoveResizeWindow(d->native, s->window, root_x, root_y,
- (unsigned)spec->size.x, (unsigned)spec->size.y);
+ (unsigned)actual_size.x, (unsigned)actual_size.y);
XFlush(d->native);
}
+ if (changed_size)
+ yasl_private_surface_notify_configure(&s->base,
+ (struct yasl_private_geometry){actual_size, YASL_PRIVATE_SCALE_UNIT});
schedule_redraw(&s->base);
return true;
}
@@ -1157,22 +1694,22 @@ static void update_state(struct x11_surface *s)
int format;
unsigned long count, left;
unsigned char *data = NULL;
- uint32_t state = s->base.state & ~(YASL_STATE_FULLSCREEN | YASL_STATE_MAXIMIZED);
+ uint32_t state = s->base.state & ~(YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_MAXIMIZED);
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->fullscreen) state |= YASL_PRIVATE_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 (h && v) state |= YASL_PRIVATE_STATE_MAXIMIZED;
}
if (data) XFree(data);
if (state == s->base.state) return;
s->base.state = state;
- yasl_surface_notify_state(&s->base, state);
+ yasl_private_surface_notify_state(&s->base, state);
}
static void selection_request(struct x11_display *d, XSelectionRequestEvent *request)
@@ -1184,10 +1721,10 @@ static void selection_request(struct x11_display *d, XSelectionRequestEvent *req
.target = request->target, .time = request->time,
};
Atom property = request->property ? request->property : request->target;
- int slot = request->selection == XA_PRIMARY ? YASL_PRIMARY : YASL_CLIPBOARD;
+ int slot = request->selection == XA_PRIMARY ? YASL_PRIVATE_PRIMARY : YASL_PRIVATE_CLIPBOARD;
const char *text = d->selection[slot];
size_t latin1_len = 0;
- char *latin1 = text ? yasl_utf8_to_latin1(text, &latin1_len) : NULL;
+ char *latin1 = text ? yasl_private_utf8_to_latin1(text, &latin1_len) : NULL;
if (text && request->target == d->targets) {
Atom targets[] = {d->targets, d->utf8, XA_STRING};
XChangeProperty(d->native, request->requestor, property, XA_ATOM, 32,
@@ -1267,7 +1804,7 @@ static void xdnd_selection_notify(struct x11_display *d, XSelectionEvent *event)
size_t out_len = 0;
char *parsed = parse_file_uris((const char *)data, count, &out_len);
if (parsed && out_len > 0) {
- push(s, (struct yasl_event){.type = YASL_EVENT_DROP}, parsed, out_len);
+ push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_DROP}, parsed, out_len);
free(parsed);
success = true;
}
@@ -1304,20 +1841,20 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event)
/* Surface destruction detached this accepted request. The reply means
* the owner has finished writing the initial property; retire safely. */
*p = r->next;
- if (r->timeout) yasl_loop_del_timer(d->loop, r->timeout);
+ if (r->timeout) yasl_private_loop_del_timer(d->loop, r->timeout);
XDestroyWindow(d->native, r->window);
free(r);
return;
}
if (event->property == None) {
*p = r->next;
- if (r->timeout) yasl_loop_del_timer(d->loop, r->timeout);
- enum yasl_transfer_status status =
+ if (r->timeout) yasl_private_loop_del_timer(d->loop, r->timeout);
+ enum yasl_private_transfer_status status =
XGetSelectionOwner(d->native, r->selection) == None
- ? YASL_TRANSFER_UNAVAILABLE : YASL_TRANSFER_FAILED;
+ ? YASL_PRIVATE_TRANSFER_UNAVAILABLE : YASL_PRIVATE_TRANSFER_FAILED;
XDestroyWindow(d->native, r->window);
if (s && !s->destroyed)
- yasl_surface_complete_paste(&s->base, r->id, status, NULL, 0);
+ yasl_private_surface_complete_paste(&s->base, r->id, status, NULL, 0);
free(r);
return;
}
@@ -1333,14 +1870,14 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event)
&probe_count, &probe_left, &probe_data);
if (probe_data) XFree(probe_data);
if (probe_result == Success && probe_type == d->incr) {
- yasl_surface_complete_paste(&s->base, r->id,
- YASL_TRANSFER_FAILED, NULL, 0);
+ yasl_private_surface_complete_paste(&s->base, r->id,
+ YASL_PRIVATE_TRANSFER_FAILED, NULL, 0);
r->target = NULL;
r->id = 0;
return;
}
*p = r->next;
- if (r->timeout) yasl_loop_del_timer(d->loop, r->timeout);
+ if (r->timeout) yasl_private_loop_del_timer(d->loop, r->timeout);
Atom actual = None;
int format = 0;
unsigned long count = 0, left = 0;
@@ -1389,17 +1926,17 @@ static void selection_notify(struct x11_display *d, XSelectionEvent *event)
}
if (complete && actual == XA_STRING) {
size_t utf8_len;
- char *utf8 = yasl_utf8_from_latin1(buf, total, &utf8_len);
+ char *utf8 = yasl_private_utf8_from_latin1(buf, total, &utf8_len);
free(buf);
buf = utf8;
if (buf) total = utf8_len;
}
- enum yasl_transfer_status status = YASL_TRANSFER_FAILED;
- if (complete && buf && yasl_utf8_validate(buf, total))
- status = YASL_TRANSFER_OK;
- yasl_surface_complete_paste(&s->base, r->id, status,
- status == YASL_TRANSFER_OK ? buf : NULL,
- status == YASL_TRANSFER_OK ? total : 0);
+ enum yasl_private_transfer_status status = YASL_PRIVATE_TRANSFER_FAILED;
+ if (complete && buf && yasl_private_utf8_validate(buf, total))
+ status = YASL_PRIVATE_TRANSFER_OK;
+ yasl_private_surface_complete_paste(&s->base, r->id, status,
+ status == YASL_PRIVATE_TRANSFER_OK ? buf : NULL,
+ status == YASL_PRIVATE_TRANSFER_OK ? total : 0);
free(buf);
XDestroyWindow(d->native, r->window);
free(r);
@@ -1413,7 +1950,7 @@ static void refresh_keyboard(struct x11_display *d)
if (!keyboard) return;
if (keyboard->names && keyboard->names->keys) {
for (unsigned code = keyboard->min_key_code; code <= keyboard->max_key_code; code++)
- d->keys[code] = yasl_key_from_xkb_name(keyboard->names->keys[code].name);
+ d->keys[code] = yasl_private_key_from_xkb_name(keyboard->names->keys[code].name);
}
XkbFreeKeyboard(keyboard, XkbAllComponentsMask, True);
}
@@ -1460,18 +1997,18 @@ static void key_event(struct x11_surface *s, XKeyEvent *key, bool repeat)
}
}
uint32_t cp = xkb_keysym_to_utf32((xkb_keysym_t)base);
- push(s, (struct yasl_event){
- .type = YASL_EVENT_KEY,
+ push(s, (struct yasl_private_event){
+ .type = YASL_PRIVATE_EVENT_KEY,
.key = {.key = key->keycode < 256 ? d->keys[key->keycode] : YASL_KEY_UNKNOWN,
- .codepoint = yasl_key_label(cp), .native_code = key->keycode,
- .shifted = yasl_key_label(xkb_keysym_to_utf32((xkb_keysym_t)shifted)),
+ .codepoint = yasl_private_key_label(cp), .native_code = key->keycode,
+ .shifted = yasl_private_key_label(xkb_keysym_to_utf32((xkb_keysym_t)shifted)),
.mods = d->mods,
.action = key->type == KeyRelease ? YASL_KEY_RELEASE :
repeat ? YASL_KEY_REPEAT : YASL_KEY_PRESS},
}, NULL, 0);
if (len > 0 && text && (unsigned char)text[0] >= 0x20 && text[0] != 0x7f &&
- !(d->mods & (YASL_MOD_CTRL | YASL_MOD_SUPER | YASL_MOD_ALT)))
- push(s, (struct yasl_event){.type = YASL_EVENT_TEXT}, text, (size_t)len);
+ !(d->mods & (YASL_PRIVATE_MOD_CTRL | YASL_PRIVATE_MOD_SUPER | YASL_PRIVATE_MOD_ALT)))
+ push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_TEXT}, text, (size_t)len);
if (text != local) free(text);
}
@@ -1480,7 +2017,7 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s,
{
if (ev->message_type == d->protocols &&
(Atom)ev->data.l[0] == d->delete_window) {
- yasl_surface_notify_close(&s->base);
+ yasl_private_surface_notify_close(&s->base);
} else if (ev->message_type == d->xdnd_enter) {
Window source = (Window)ev->data.l[0];
int version = (int)((unsigned long)ev->data.l[1] >> 24);
@@ -1521,19 +2058,19 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s,
XTranslateCoordinates(d->native, RootWindow(d->native, d->screen), target,
rx, ry, &wx, &wy, &child);
}
- struct yasl_vector2i pos = {wx, wy};
+ struct yasl_private_vector2i pos = {wx, wy};
if (d->xdnd.surface && d->xdnd.surface != s) {
struct x11_surface *old = d->xdnd.surface;
- struct yasl_vector2i old_pos = d->xdnd.pos;
+ struct yasl_private_vector2i old_pos = d->xdnd.pos;
d->xdnd.surface = NULL;
if (d->xdnd.accepts && !old->destroyed)
- yasl_surface_notify_drop(&old->base, YASL_DROP_LEAVE, old_pos);
+ yasl_private_surface_notify_drop(&old->base, YASL_PRIVATE_DROP_LEAVE, old_pos);
}
d->xdnd.surface = s;
d->xdnd.pos = pos;
d->xdnd.time = t;
if (d->xdnd.accepts && !s->destroyed)
- yasl_surface_notify_drop(&s->base, YASL_DROP_OVER, pos);
+ yasl_private_surface_notify_drop(&s->base, YASL_PRIVATE_DROP_OVER, pos);
if (d->native) {
XEvent reply = {0};
@@ -1556,7 +2093,7 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s,
} else if (ev->message_type == d->xdnd_leave) {
if (d->xdnd.surface == s) {
if (d->xdnd.accepts && !s->destroyed)
- yasl_surface_notify_drop(&s->base, YASL_DROP_LEAVE, d->xdnd.pos);
+ yasl_private_surface_notify_drop(&s->base, YASL_PRIVATE_DROP_LEAVE, d->xdnd.pos);
}
d->xdnd = (typeof(d->xdnd)){0};
} else if (ev->message_type == d->xdnd_drop) {
@@ -1566,7 +2103,7 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s,
bool was_accepting = d->xdnd.accepts && d->xdnd.has_uri_list && (d->xdnd.source == source);
if (d->xdnd.surface == s) {
if (was_accepting && !s->destroyed)
- yasl_surface_notify_drop(&s->base, YASL_DROP_LEAVE, d->xdnd.pos);
+ yasl_private_surface_notify_drop(&s->base, YASL_PRIVATE_DROP_LEAVE, d->xdnd.pos);
}
if (was_accepting && !s->destroyed) {
d->pending_drop.active = true;
@@ -1593,7 +2130,7 @@ static void handle_client_message(struct x11_display *d, struct x11_surface *s,
}
}
-static void dispatch(struct yasl_display *base)
+static void dispatch(struct yasl_private_display *base)
{
struct x11_display *d = (struct x11_display *)base;
d->depth++;
@@ -1636,11 +2173,11 @@ static void dispatch(struct yasl_display *base)
switch (event.type) {
case Expose: s->want_draw = true; break;
case ConfigureNotify: {
- struct yasl_vector2i size = {event.xconfigure.width, event.xconfigure.height};
+ struct yasl_private_vector2i size = {event.xconfigure.width, event.xconfigure.height};
if (size.x == s->size.x && size.y == s->size.y) break;
if (resize(s, size))
- yasl_surface_notify_configure(&s->base,
- (struct yasl_geometry){size, YASL_SCALE_UNIT});
+ yasl_private_surface_notify_configure(&s->base,
+ (struct yasl_private_geometry){size, YASL_PRIVATE_SCALE_UNIT});
break;
}
case ClientMessage:
@@ -1648,12 +2185,16 @@ static void dispatch(struct yasl_display *base)
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->base.state |= YASL_STATE_ACTIVATED;
- else s->base.state &= ~YASL_STATE_ACTIVATED;
- push(s, (struct yasl_event){
- .type = YASL_EVENT_FOCUS, .focus.focused = focused}, NULL, 0);
- yasl_surface_notify_state(&s->base, s->base.state);
+ s->focused = focused;
+ if (s->ic) {
+ if (focused && s->base.text_input_active) XSetICFocus(s->ic);
+ else XUnsetICFocus(s->ic);
+ }
+ if (focused) s->base.state |= YASL_PRIVATE_STATE_ACTIVATED;
+ else s->base.state &= ~YASL_PRIVATE_STATE_ACTIVATED;
+ push(s, (struct yasl_private_event){
+ .type = YASL_PRIVATE_EVENT_FOCUS, .focus.focused = focused}, NULL, 0);
+ yasl_private_surface_notify_state(&s->base, s->base.state);
break;
}
case PropertyNotify:
@@ -1674,13 +2215,13 @@ static void dispatch(struct yasl_display *base)
break;
}
case MotionNotify: case LeaveNotify: case EnterNotify: case ButtonPress: case ButtonRelease: {
- struct yasl_event ev = {
- .pointer = {.device = YASL_PTR_DEV_MOUSE},
+ struct yasl_private_event ev = {
+ .pointer = {.device = YASL_PRIVATE_PTR_DEV_MOUSE},
};
unsigned state;
if (event.type == MotionNotify) {
- ev.pointer.pos = (struct yasl_vector2i){event.xmotion.x, event.xmotion.y};
- ev.type = YASL_EVENT_POINTER_MOTION;
+ ev.pointer.pos = (struct yasl_private_vector2i){event.xmotion.x, event.xmotion.y};
+ ev.type = YASL_PRIVATE_EVENT_POINTER_MOTION;
state = event.xmotion.state;
if (d->drag.active) {
if (d->drag.carried)
@@ -1689,38 +2230,41 @@ static void dispatch(struct yasl_display *base)
XMoveWindow(d->native, d->drag.icon_window,
event.xmotion.x_root - d->drag.hotspot.x,
event.xmotion.y_root - d->drag.hotspot.y);
- struct yasl_vector2i hpos = ev.pointer.pos;
+ struct yasl_private_vector2i hpos = ev.pointer.pos;
struct x11_surface *hsurf = d->native ?
surface_at_root_pos(d, event.xmotion.x_root, event.xmotion.y_root, &hpos) : s;
drag_set_hover(d, hsurf, hpos);
}
} else if (event.type == EnterNotify) {
- ev.pointer.pos = (struct yasl_vector2i){event.xcrossing.x, event.xcrossing.y};
- ev.type = YASL_EVENT_POINTER_MOTION;
+ ev.pointer.pos = (struct yasl_private_vector2i){event.xcrossing.x, event.xcrossing.y};
+ ev.type = YASL_PRIVATE_EVENT_POINTER_MOTION;
state = event.xcrossing.state;
if (d->drag.active) {
- struct yasl_vector2i hpos = ev.pointer.pos;
+ struct yasl_private_vector2i hpos = ev.pointer.pos;
struct x11_surface *hsurf = d->native ?
surface_at_root_pos(d, event.xcrossing.x_root, event.xcrossing.y_root, &hpos) : s;
drag_set_hover(d, hsurf, hpos);
}
} else if (event.type == LeaveNotify) {
- ev.pointer.pos = (struct yasl_vector2i){event.xcrossing.x, event.xcrossing.y};
- ev.type = YASL_EVENT_POINTER_LEAVE;
+ ev.pointer.pos = (struct yasl_private_vector2i){event.xcrossing.x, event.xcrossing.y};
+ ev.type = YASL_PRIVATE_EVENT_POINTER_LEAVE;
state = event.xcrossing.state;
if (d->drag.active)
drag_leave_hover(d, s, ev.pointer.pos);
} else {
- ev.pointer.pos = (struct yasl_vector2i){event.xbutton.x, event.xbutton.y};
+ ev.pointer.pos = (struct yasl_private_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;
- ev.type = YASL_EVENT_POINTER_SCROLL;
+ ev.type = YASL_PRIVATE_EVENT_POINTER_SCROLL;
if (button <= 5) ev.pointer.scroll.y = button == 4 ? -1 : 1;
else ev.pointer.scroll.x = button == 6 ? -1 : 1;
+ ev.pointer.scroll_valid = 1u;
+ ev.pointer.scroll_source = 1u;
+ ev.pointer.scroll_phase = 2u;
} else {
- ev.type = event.type == ButtonPress ? YASL_EVENT_POINTER_DOWN : YASL_EVENT_POINTER_UP;
+ ev.type = event.type == ButtonPress ? YASL_PRIVATE_EVENT_POINTER_DOWN : YASL_PRIVATE_EVENT_POINTER_UP;
ev.pointer.button = pointer_button(button);
if (event.type == ButtonRelease && d->drag.active)
drag_drop_hover(d);
@@ -1735,13 +2279,13 @@ static void dispatch(struct yasl_display *base)
leave(d);
}
-static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb,
- const struct yasl_damage_list *damage)
+static bool present(struct yasl_surface *base, const struct yasl_private_framebuffer *fb,
+ const struct yasl_private_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;
+ damage->n < -1 || damage->n > YASL_PRIVATE_DAMAGE_MAX) return false;
struct x11_display *d = (struct x11_display *)base->display;
XImage *im = s->image;
if (s->shm_active) {
@@ -1749,7 +2293,7 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb
XShmPutImage(d->native, s->window, s->gc, im, 0, 0, 0, 0,
(unsigned)s->size.x, (unsigned)s->size.y, False);
else for (int i = 0; i < damage->n; i++) {
- struct yasl_rect2i r = damage->rects[i];
+ struct yasl_private_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;
@@ -1758,7 +2302,7 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb
XShmPutImage(d->native, s->window, s->gc, im, x, y, x, y, (unsigned)w, (unsigned)h, False);
}
s->presented = true;
- yasl_clock_presented(&s->clock, yasl_loop_now_ns(), false);
+ yasl_private_clock_presented(&s->clock, yasl_private_loop_now_ns(), false);
return true;
}
bool direct = im->bits_per_pixel == 32 && im->red_mask == 0xff0000 &&
@@ -1769,7 +2313,7 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb
y_start = s->size.y; y_end = 0;
x_start = s->size.x; x_end = 0;
for (int i = 0; i < damage->n; i++) {
- struct yasl_rect2i r = damage->rects[i];
+ struct yasl_private_rect2i r = damage->rects[i];
int y0 = r.y > 0 ? r.y : 0;
int y1 = (r.y + r.height < s->size.y) ? r.y + r.height : s->size.y;
int x0 = r.x > 0 ? r.x : 0;
@@ -1798,7 +2342,7 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb
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];
+ struct yasl_private_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;
@@ -1807,27 +2351,33 @@ static bool present(struct yasl_surface *base, const struct yasl_framebuffer *fb
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);
+ yasl_private_clock_presented(&s->clock, yasl_private_loop_now_ns(), false);
return true;
}
-static void flush(struct yasl_display *base)
+static void flush(struct yasl_private_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;
+ if (s->destroyed || !s->want_draw || s->drawing) continue;
+ if (s->base.presenter != YASL_PRIVATE_PRESENTER_CPU) {
+ s->want_draw = false;
+ yasl_private_surface_notify_refresh(&s->base);
+ continue;
+ }
+ if (!s->cbs || !s->cbs->draw) continue;
s->want_draw = false;
s->drawing = true;
s->presented = false;
- s->damage = (struct yasl_damage_list){0};
- s->frame = (struct yasl_frame){
+ s->damage = (struct yasl_private_damage_list){0};
+ s->frame = (struct yasl_private_frame){
.surface = &s->base,
- .target = {.generation = yasl_frame_begin(&s->base),
+ .target = {.generation = yasl_private_frame_begin(&s->base),
.pixels = s->pixels, .size = s->size},
};
- yasl_surface_draw(&s->base, &s->frame);
+ yasl_private_surface_draw(&s->base, &s->frame);
if (!s->destroyed) s->drawing = false;
}
XFlush(d->native);
@@ -1837,11 +2387,11 @@ static void flush(struct yasl_display *base)
static int ready(int fd, short events, void *ud)
{
(void)fd;
- struct yasl_display *base = ud;
+ struct yasl_private_display *base = ud;
struct x11_display *d = (struct x11_display *)base;
if ((events & (POLLERR | POLLHUP)) && !d->lost) {
d->lost = true;
- yasl_display_notify_event(base, YASL_EVENT_DISPLAY_LOST);
+ yasl_private_display_notify_event(base, YASL_PRIVATE_EVENT_DISPLAY_LOST);
return 0;
}
if ((events & POLLIN) && !d->lost) {
@@ -1851,7 +2401,7 @@ static int ready(int fd, short events, void *ud)
return 0;
}
-static int dispatch_timeout(struct yasl_display *base, int timeout_ms)
+static int dispatch_timeout(struct yasl_private_display *base, int timeout_ms)
{
struct x11_display *d = (struct x11_display *)base;
if (unlikely(!d || !d->native || d->lost)) return -1;
@@ -1867,26 +2417,26 @@ static int dispatch_timeout(struct yasl_display *base, int timeout_ms)
if (XEventsQueued(d->native, QueuedAlready) > 0)
timeout_ms = 0;
if (!XPending(d->native) && timeout_ms != 0) {
- yasl_loop_step(d->loop, timeout_ms);
+ yasl_private_loop_step(d->loop, timeout_ms);
dispatch(base);
} else {
- yasl_loop_step(d->loop, 0);
+ yasl_private_loop_step(d->loop, 0);
dispatch(base);
}
}
return 0;
}
-static struct yasl_display *display_open(struct yasl_loop *loop,
- const struct yasl_display_spec *spec)
+static struct yasl_private_display *display_open(struct yasl_private_loop *loop,
+ const struct yasl_private_display_spec *spec)
{
struct x11_display *d = calloc(1, sizeof(*d));
if (!d) return NULL;
- d->base.ops = &yasl_x11_backend;
- d->base.callbacks = spec && spec->cbs ? *spec->cbs : (struct yasl_display_cbs){0};
+ d->base.ops = &yasl_private_x11_backend;
+ d->base.callbacks = spec && spec->cbs ? *spec->cbs : (struct yasl_private_display_cbs){0};
d->native = XOpenDisplay(NULL);
if (!d->native) { free(d); errno = ENODEV; return NULL; }
- XSetErrorHandler(yasl_x11_error_handler);
+ XSetErrorHandler(yasl_private_x11_error_handler);
d->base.native_handle = d->native;
d->screen = DefaultScreen(d->native);
d->has_shm = XShmQueryExtension(d->native);
@@ -1908,8 +2458,15 @@ static struct yasl_display *display_open(struct yasl_loop *loop,
d->abs_pressure = XInternAtom(d->native, "Abs Pressure", False);
d->abs_tilt_x = XInternAtom(d->native, "Abs Tilt X", False);
d->abs_tilt_y = XInternAtom(d->native, "Abs Tilt Y", False);
+ d->abs_rotation = XInternAtom(d->native, "Abs Rotation", False);
+ d->abs_distance = XInternAtom(d->native, "Abs Distance", False);
+ d->abs_tangential = XInternAtom(d->native, "Abs TPressure", False);
+ d->abs_wheel = XInternAtom(d->native, "Abs Wheel", False);
d->tool_type = XInternAtom(d->native, "Wacom Tool Type", False);
d->eraser = XInternAtom(d->native, "ERASER", False);
+ d->brush = XInternAtom(d->native, "BRUSH", False);
+ d->airbrush = XInternAtom(d->native, "AIRBRUSH", False);
+ d->pencil = XInternAtom(d->native, "PENCIL", False);
unsigned char bits[XIMaskLen(XI_LASTEVENT)] = {0};
XISetMask(bits, XI_HierarchyChanged);
XISetMask(bits, XI_DeviceChanged);
@@ -1917,7 +2474,7 @@ static struct yasl_display *display_open(struct yasl_loop *loop,
XISelectEvents(d->native, RootWindow(d->native, d->screen), &mask, 1);
}
if (!loop) {
- d->loop = yasl_loop_create();
+ d->loop = yasl_private_loop_create();
d->owns_loop = true;
} else {
d->loop = loop;
@@ -1926,7 +2483,7 @@ static struct yasl_display *display_open(struct yasl_loop *loop,
d->app_id = strdup(spec && spec->app_id ? spec->app_id : "YASL");
d->base.app_id = d->app_id;
if (!d->loop || !d->app_id) {
- if (d->owns_loop) yasl_loop_destroy(d->loop);
+ if (d->owns_loop) yasl_private_loop_destroy(d->loop);
free(d->app_id);
XCloseDisplay(d->native);
free(d);
@@ -1941,10 +2498,11 @@ static struct yasl_display *display_open(struct yasl_loop *loop,
ATOM(utf8, "UTF8_STRING");
ATOM(clipboard, "CLIPBOARD");
ATOM(targets, "TARGETS");
- ATOM(paste_property, "YASL_SELECTION");
+ ATOM(paste_property, "YASL_PRIVATE_SELECTION");
ATOM(incr, "INCR");
ATOM(net_name, "_NET_WM_NAME");
ATOM(net_state, "_NET_WM_STATE");
+ ATOM(motif_hints, "_MOTIF_WM_HINTS");
ATOM(fullscreen, "_NET_WM_STATE_FULLSCREEN");
ATOM(maximized_h, "_NET_WM_STATE_MAXIMIZED_HORZ");
ATOM(maximized_v, "_NET_WM_STATE_MAXIMIZED_VERT");
@@ -1961,13 +2519,13 @@ static struct yasl_display *display_open(struct yasl_loop *loop,
ATOM(uri_list, "text/uri-list");
#undef ATOM
if (d->loop) {
- d->watch = yasl_loop_add_fd(d->loop, ConnectionNumber(d->native), POLLIN, ready, &d->base);
+ d->watch = yasl_private_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);
+ if (d->owns_loop) yasl_private_loop_destroy(d->loop);
free(d);
return NULL;
}
@@ -1975,21 +2533,24 @@ static struct yasl_display *display_open(struct yasl_loop *loop,
return &d->base;
}
-static void display_close(struct yasl_display *base)
+static void display_close(struct yasl_private_display *base)
{
struct x11_display *d = (struct x11_display *)base;
assert(!d->surfaces && "destroy every surface before its display");
drag_cancel(base);
cancel_pastes(d, NULL);
d->pending_drop.active = false;
- if (d->loop && d->watch > 0) yasl_loop_del_fd(d->loop, d->watch);
+ if (d->loop && d->watch > 0) yasl_private_loop_del_fd(d->loop, d->watch);
if (d->owns_loop && d->loop) {
- yasl_loop_destroy(d->loop);
+ yasl_private_loop_destroy(d->loop);
d->loop = NULL;
d->base.loop = NULL;
}
if (d->im) XCloseIM(d->im);
clear_pointers(d);
+#if YASL_HAS_EGL
+ if (d->egl_ready) eglTerminate(d->egl);
+#endif
for (size_t i = 0; i < sizeof(d->cursors) / sizeof(d->cursors[0]); i++) {
if (d->cursors[i]) {
XFreeCursor(d->native, d->cursors[i]);
@@ -2004,16 +2565,16 @@ static void display_close(struct yasl_display *base)
free(d);
}
-static uint32_t caps(const struct yasl_display *d)
+static uint32_t caps(const struct yasl_private_display *d)
{
- return YASL_CAP_POPUPS | YASL_CAP_DRAG | YASL_CAP_DRAG_CARRY | YASL_CAP_PLACE_WINDOW |
- YASL_CAP_PRIMARY_SELECTION | YASL_CAP_CSD |
- (((const struct x11_display *)d)->xi2 ? YASL_CAP_TABLET : 0);
+ return YASL_PRIVATE_CAP_POPUPS | YASL_PRIVATE_CAP_DRAG | YASL_PRIVATE_CAP_DRAG_CARRY | YASL_PRIVATE_CAP_PLACE_WINDOW |
+ YASL_PRIVATE_CAP_PRIMARY_SELECTION | YASL_PRIVATE_CAP_CSD | YASL_PRIVATE_CAP_NATIVE_GRAPHICS |
+ (((const struct x11_display *)d)->xi2 ? YASL_PRIVATE_CAP_TABLET : 0);
}
-static bool copy_text(struct yasl_display *base, enum yasl_selection sel, const char *text)
+static bool copy_text(struct yasl_private_display *base, enum yasl_private_selection sel, const char *text)
{
- if (!text || (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY)) {
+ if (!text || (sel != YASL_PRIVATE_CLIPBOARD && sel != YASL_PRIVATE_PRIMARY)) {
errno = EINVAL;
return false;
}
@@ -2022,39 +2583,39 @@ static bool copy_text(struct yasl_display *base, enum yasl_selection sel, const
if (!value) { errno = ENOMEM; return false; }
free(d->selection[sel]);
d->selection[sel] = value;
- XSetSelectionOwner(d->native, sel == YASL_PRIMARY ? XA_PRIMARY : d->clipboard,
+ XSetSelectionOwner(d->native, sel == YASL_PRIVATE_PRIMARY ? XA_PRIMARY : d->clipboard,
d->selection_window, CurrentTime);
XFlush(d->native);
return true;
}
-static void copy(struct yasl_display *base, enum yasl_selection sel, const char *text)
+static void copy(struct yasl_private_display *base, enum yasl_private_selection sel, const char *text)
{
(void)copy_text(base, sel, text);
}
-static bool request_paste(struct yasl_display *base, enum yasl_selection sel,
+static bool request_paste(struct yasl_private_display *base, enum yasl_private_selection sel,
struct yasl_surface *target, uint64_t id)
{
- if (!target || (sel != YASL_CLIPBOARD && sel != YASL_PRIMARY)) {
+ if (!target || (sel != YASL_PRIVATE_CLIPBOARD && sel != YASL_PRIVATE_PRIMARY)) {
errno = EINVAL;
return false;
}
struct x11_display *d = (struct x11_display *)base;
struct x11_surface *s = (struct x11_surface *)target;
if (s->destroyed || target->display != base) { errno = EINVAL; return false; }
- Atom selection = sel == YASL_PRIMARY ? XA_PRIMARY : d->clipboard;
+ Atom selection = sel == YASL_PRIVATE_PRIMARY ? XA_PRIMARY : d->clipboard;
Window owner = XGetSelectionOwner(d->native, selection);
if (owner == None) {
- yasl_surface_complete_paste(target, id, YASL_TRANSFER_UNAVAILABLE, NULL, 0);
+ yasl_private_surface_complete_paste(target, id, YASL_PRIVATE_TRANSFER_UNAVAILABLE, NULL, 0);
return true;
}
if (owner == d->selection_window) {
const char *text = d->selection[sel];
if (text)
- yasl_surface_complete_paste(target, id, YASL_TRANSFER_OK, text, strlen(text));
+ yasl_private_surface_complete_paste(target, id, YASL_PRIVATE_TRANSFER_OK, text, strlen(text));
else
- yasl_surface_complete_paste(target, id, YASL_TRANSFER_UNAVAILABLE, NULL, 0);
+ yasl_private_surface_complete_paste(target, id, YASL_PRIVATE_TRANSFER_UNAVAILABLE, NULL, 0);
return true;
} else {
struct paste_request *r = calloc(1, sizeof(*r));
@@ -2067,7 +2628,7 @@ static bool request_paste(struct yasl_display *base, enum yasl_selection sel,
r->selection = selection;
r->id = id;
errno = 0;
- r->timeout = yasl_loop_add_timer(d->loop, PASTE_TIMEOUT_NS,
+ r->timeout = yasl_private_loop_add_timer(d->loop, PASTE_TIMEOUT_NS,
paste_timeout, r);
if (!r->timeout) {
XDestroyWindow(d->native, r->window);
@@ -2084,17 +2645,17 @@ static bool request_paste(struct yasl_display *base, enum yasl_selection sel,
}
}
-static void paste(struct yasl_display *base, enum yasl_selection sel,
+static void paste(struct yasl_private_display *base, enum yasl_private_selection sel,
struct yasl_surface *target)
{
(void)request_paste(base, sel, target, 0);
}
-static bool input_poll(struct yasl_surface *base, struct yasl_event *event)
+static bool input_poll(struct yasl_surface *base, struct yasl_private_event *event)
{
struct x11_surface *s = (struct x11_surface *)base;
if (s->destroyed) return false;
- if (base->display->events) return yasl_events_poll_surface(base, event);
+ if (base->display->events) return yasl_private_events_poll_surface(base, event);
return input_queue_pop(&s->input, event);
}
@@ -2110,47 +2671,47 @@ static void schedule_redraw(struct yasl_surface *base)
s->want_draw = true;
struct x11_display *d = (struct x11_display *)base->display;
if (d && d->loop)
- yasl_loop_kick(d->loop);
+ yasl_private_loop_kick(d->loop);
}
}
-static struct yasl_display *surface_display(const struct yasl_surface *s) { return s->display; }
-static struct yasl_geometry geometry(const struct yasl_surface *s)
+static struct yasl_private_display *surface_display(const struct yasl_surface *s) { return s->display; }
+static struct yasl_private_geometry geometry(const struct yasl_surface *s)
{
- return (struct yasl_geometry){((const struct x11_surface *)s)->size, YASL_SCALE_UNIT};
+ return (struct yasl_private_geometry){((const struct x11_surface *)s)->size, YASL_PRIVATE_SCALE_UNIT};
}
-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 int mods(const struct yasl_private_display *d) { return (int)((const struct x11_display *)d)->mods; }
+static void *native_display(struct yasl_private_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)
+static struct yasl_private_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,
+ struct yasl_private_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),
+ .next_present_ns = yasl_private_clock_next_present_ns(&s->clock),
.source = s->clock.source};
- yasl_clock_resize_stats(&s->clock, &t.resize);
+ yasl_private_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);
+ return yasl_private_clock_frame_timestamp_ns(&((struct x11_surface *)s)->clock, now);
}
-static void cursor(struct yasl_surface *base, enum yasl_cursor cursor)
+static void cursor(struct yasl_surface *base, enum yasl_private_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_HIDDEN) return;
+ if (s->destroyed || (unsigned)cursor > YASL_PRIVATE_CURSOR_HIDDEN) return;
struct x11_display *d = (struct x11_display *)base->display;
if (!d->native) return;
if (!d->cursors[cursor]) {
- if (cursor == YASL_CURSOR_HIDDEN) {
+ if (cursor == YASL_PRIVATE_CURSOR_HIDDEN) {
char bits[] = {0};
Pixmap pixmap = XCreateBitmapFromData(d->native, s->window, bits, 1, 1);
XColor color = {0};
@@ -2164,7 +2725,7 @@ static void cursor(struct yasl_surface *base, enum yasl_cursor cursor)
XDefineCursor(d->native, s->window, d->cursors[cursor]);
}
-static bool place(struct yasl_surface *base, struct yasl_vector2i at)
+static bool place(struct yasl_surface *base, struct yasl_private_vector2i at)
{
struct x11_surface *s = (struct x11_surface *)base;
struct x11_display *d = (struct x11_display *)base->display;
@@ -2227,7 +2788,7 @@ static bool xsettings_int(const unsigned char *p, size_t len, const char *name,
return false;
}
-static int double_click_ms(const struct yasl_display *base)
+static int double_click_ms(const struct yasl_private_display *base)
{
const struct x11_display *d = (const struct x11_display *)base;
if (d->native) {
@@ -2255,12 +2816,12 @@ static int double_click_ms(const struct yasl_display *base)
}
static bool read_pixels(struct yasl_surface *base, uint32_t *out, size_t cap,
- struct yasl_vector2i *size)
+ struct yasl_private_vector2i *size)
{
struct x11_surface *s = (struct x11_surface *)base;
/* presented stays set from the last draw until the next one starts. */
if (s->destroyed || s->drawing || !s->presented || !s->pixels) return false;
- return yasl_copy_frame(s->pixels, s->size, out, cap, size);
+ return yasl_private_copy_frame(s->pixels, s->size, out, cap, size);
}
static bool bell(struct yasl_surface *base)
@@ -2275,16 +2836,16 @@ static bool bell(struct yasl_surface *base)
return true;
}
-static void drag_end(struct x11_display *d, enum yasl_drag_end how)
+static void drag_end(struct x11_display *d, enum yasl_private_drag_end how)
{
if (!d || !d->drag.active) return;
- struct yasl_drag_listener l = d->drag.listener;
+ struct yasl_private_drag_listener l = d->drag.listener;
void *ud = d->drag.ud;
struct x11_surface *hover = d->drag.hover;
- struct yasl_vector2i hover_pos = d->drag.hover_pos;
+ struct yasl_private_vector2i hover_pos = d->drag.hover_pos;
d->drag.active = false;
- d->drag.listener = (struct yasl_drag_listener){0};
+ d->drag.listener = (struct yasl_private_drag_listener){0};
d->drag.hover = NULL;
d->drag.origin = NULL;
d->drag.carried = NULL;
@@ -2299,19 +2860,19 @@ static void drag_end(struct x11_display *d, enum yasl_drag_end how)
}
if (hover && !hover->destroyed) {
- if (how == YASL_DRAG_DROPPED)
- yasl_surface_notify_drag(&hover->base, YASL_DRAG_DROP, hover_pos);
+ if (how == YASL_PRIVATE_DRAG_DROPPED)
+ yasl_private_surface_notify_drag(&hover->base, YASL_PRIVATE_DRAG_DROP, hover_pos);
else
- yasl_surface_notify_drag(&hover->base, YASL_DRAG_LEAVE, hover_pos);
+ yasl_private_surface_notify_drag(&hover->base, YASL_PRIVATE_DRAG_LEAVE, hover_pos);
}
if (l.end)
l.end(ud, how);
}
-static bool drag_start(struct yasl_display *base, struct yasl_surface *origin,
- const struct yasl_drag_image *image,
- const struct yasl_drag_listener *l, void *ud)
+static bool drag_start(struct yasl_private_display *base, struct yasl_surface *origin,
+ const struct yasl_private_drag_image *image,
+ const struct yasl_private_drag_listener *l, void *ud)
{
struct x11_display *d = (struct x11_display *)base;
if (!d || !origin || d->drag.active || !l)
@@ -2383,12 +2944,12 @@ static bool drag_start(struct yasl_display *base, struct yasl_surface *origin,
return true;
}
-static void drag_cancel(struct yasl_display *base)
+static void drag_cancel(struct yasl_private_display *base)
{
- drag_end((struct x11_display *)base, YASL_DRAG_CANCELLED);
+ drag_end((struct x11_display *)base, YASL_PRIVATE_DRAG_CANCELLED);
}
-static bool drag_carry(struct yasl_surface *base, struct yasl_vector2i offset)
+static bool drag_carry(struct yasl_surface *base, struct yasl_private_vector2i offset)
{
struct x11_display *d = (struct x11_display *)base->display;
if (!d || !d->drag.active)
@@ -2420,7 +2981,7 @@ static bool interactive_move(struct yasl_surface *base)
return true;
}
-static bool interactive_resize(struct yasl_surface *base, enum yasl_resize_edge edge)
+static bool interactive_resize(struct yasl_surface *base, enum yasl_private_resize_edge edge)
{
struct x11_surface *s = (struct x11_surface *)base;
struct x11_display *d = (struct x11_display *)base->display;
@@ -2432,14 +2993,14 @@ static bool interactive_resize(struct yasl_surface *base, enum yasl_resize_edge
int dir;
switch (edge) {
- case YASL_EDGE_TOP_LEFT: dir = 0; break;
- case YASL_EDGE_TOP: dir = 1; break;
- case YASL_EDGE_TOP_RIGHT: dir = 2; break;
- case YASL_EDGE_RIGHT: dir = 3; break;
- case YASL_EDGE_BOTTOM_RIGHT: dir = 4; break;
- case YASL_EDGE_BOTTOM: dir = 5; break;
- case YASL_EDGE_BOTTOM_LEFT: dir = 6; break;
- case YASL_EDGE_LEFT: dir = 7; break;
+ case YASL_PRIVATE_EDGE_TOP_LEFT: dir = 0; break;
+ case YASL_PRIVATE_EDGE_TOP: dir = 1; break;
+ case YASL_PRIVATE_EDGE_TOP_RIGHT: dir = 2; break;
+ case YASL_PRIVATE_EDGE_RIGHT: dir = 3; break;
+ case YASL_PRIVATE_EDGE_BOTTOM_RIGHT: dir = 4; break;
+ case YASL_PRIVATE_EDGE_BOTTOM: dir = 5; break;
+ case YASL_PRIVATE_EDGE_BOTTOM_LEFT: dir = 6; break;
+ case YASL_PRIVATE_EDGE_LEFT: dir = 7; break;
default: return false;
}
@@ -2448,7 +3009,7 @@ static bool interactive_resize(struct yasl_surface *base, enum yasl_resize_edge
return true;
}
-static bool show_window_menu(struct yasl_surface *base, struct yasl_vector2i pos)
+static bool show_window_menu(struct yasl_surface *base, struct yasl_private_vector2i pos)
{
struct x11_surface *s = (struct x11_surface *)base;
struct x11_display *d = (struct x11_display *)base->display;
@@ -2458,7 +3019,18 @@ static bool show_window_menu(struct yasl_surface *base, struct yasl_vector2i pos
return true;
}
-static void set_ime_caret(struct yasl_surface *base, struct yasl_rect2i caret)
+static bool surface_update_text_input(struct yasl_surface *base)
+{
+ struct x11_surface *s = (struct x11_surface *)base;
+ if (!s || s->destroyed || !base->display) return false;
+ if (s->ic) {
+ if (s->focused && base->text_input_active) XSetICFocus(s->ic);
+ else XUnsetICFocus(s->ic);
+ }
+ return true;
+}
+
+static void set_ime_caret(struct yasl_surface *base, struct yasl_private_rect2i caret)
{
struct x11_surface *s = (struct x11_surface *)base;
if (!s) return;
@@ -2489,8 +3061,15 @@ static bool ime_preedit_cursor(const struct yasl_surface *base, int *begin, int
return true;
}
-const struct yasl_backend_ops yasl_x11_backend = {
- .type = YASL_BACKEND_X11,
+const struct yasl_private_backend_ops yasl_private_x11_backend = {
+#if YASL_HAS_EGL
+ .surface_create_gl = create_gl_surface,
+ .gl_make_current = gl_make_current,
+ .gl_get_proc_address = gl_get_proc_address,
+ .gl_swap_interval = gl_swap_interval,
+ .gl_swap_buffers = gl_swap_buffers,
+#endif
+ .type = YASL_PRIVATE_BACKEND_X11,
.display_open = display_open,
.display_close = display_close,
.display_caps = caps,
@@ -2530,6 +3109,7 @@ const struct yasl_backend_ops yasl_x11_backend = {
.surface_start_interactive_move = interactive_move,
.surface_start_interactive_resize = interactive_resize,
.surface_show_window_menu = show_window_menu,
+ .surface_update_text_input = surface_update_text_input,
.surface_set_ime_caret = set_ime_caret,
.surface_ime_preedit = ime_preedit,
.surface_ime_preedit_cursor = ime_preedit_cursor,
diff --git a/tests/benchmark.c b/tests/benchmark.c
index 1cea614..8fa8066 100644
--- a/tests/benchmark.c
+++ b/tests/benchmark.c
@@ -7,12 +7,12 @@
#include <time.h>
#include <limits.h>
-#include "yasl/yasl.h"
-#include "yasl/loop.h"
-#include "yasl/timing.h"
+#include "common/types.h"
+#include "common/private_loop.h"
+#include "common/private_timing.h"
#include "common/input.h"
#if YASL_HAS_WAYLAND
-#include "yasl/native/wayland.h"
+#include "common/private_native_wayland.h"
#endif
static inline uint64_t get_time_ns(void) {
@@ -48,28 +48,28 @@ static void print_bench_skipped(const char *name, const char *reason) {
static void bench_surface_lookup(void) {
const int num_surfaces = 16;
const int lookups = 10000000;
- struct yasl_display_spec spec = {
+ struct yasl_private_display_spec spec = {
.app_id = "org.yasl.benchmark",
- .backend = YASL_BACKEND_WAYLAND,
+ .backend = YASL_PRIVATE_BACKEND_WAYLAND,
};
- struct yasl_display *d = yasl_display_open(NULL, &spec);
+ struct yasl_private_display *d = yasl_private_display_open(NULL, &spec);
struct yasl_surface *surfaces[num_surfaces];
memset(surfaces, 0, sizeof(surfaces));
if (d) {
for (int i = 0; i < num_surfaces; i++) {
- struct yasl_window_spec sspec = {
+ struct yasl_private_window_spec sspec = {
.title = "Benchmark",
.size = { .x = 320, .y = 240 },
};
- surfaces[i] = yasl_surface_create_toplevel(d, &sspec);
+ surfaces[i] = yasl_private_surface_create_toplevel(d, &sspec);
}
}
uint64_t t0 = get_time_ns();
uintptr_t sum = 0;
for (int i = 0; i < lookups; i++) {
- struct wl_surface *found = yasl_wayland_surface_get_wl_surface(surfaces[i % num_surfaces]);
+ struct wl_surface *found = yasl_private_wayland_surface_get_wl_surface(surfaces[i % num_surfaces]);
sum += (uintptr_t)found;
}
uint64_t t1 = get_time_ns();
@@ -82,35 +82,35 @@ static void bench_surface_lookup(void) {
if (d) {
for (int i = 0; i < num_surfaces; i++) {
- if (surfaces[i]) yasl_surface_destroy(surfaces[i]);
+ if (surfaces[i]) yasl_private_surface_destroy(surfaces[i]);
}
- yasl_display_close(d);
+ yasl_private_display_close(d);
}
}
/* Benchmark 2: Public Surface Geometry & Refresh Timing */
static void bench_output_follow(void) {
const int iters = 10000000;
- struct yasl_display_spec spec = {
+ struct yasl_private_display_spec spec = {
.app_id = "org.yasl.benchmark",
- .backend = YASL_BACKEND_WAYLAND,
+ .backend = YASL_PRIVATE_BACKEND_WAYLAND,
};
- struct yasl_display *d = yasl_display_open(NULL, &spec);
+ struct yasl_private_display *d = yasl_private_display_open(NULL, &spec);
struct yasl_surface *s = NULL;
if (d) {
- struct yasl_window_spec sspec = {
+ struct yasl_private_window_spec sspec = {
.title = "Benchmark",
.size = { .x = 320, .y = 240 },
};
- s = yasl_surface_create_toplevel(d, &sspec);
+ s = yasl_private_surface_create_toplevel(d, &sspec);
}
uint64_t t0 = get_time_ns();
uint64_t sum = 0;
for (int i = 0; i < iters; i++) {
- struct yasl_geometry geom = yasl_surface_geometry(s);
- struct yasl_timing timing = yasl_surface_timing(s);
- int dev = yasl_scale_to_device(320.0, geom.scale_120);
+ struct yasl_private_geometry geom = yasl_private_surface_geometry(s);
+ struct yasl_private_timing timing = yasl_private_surface_timing(s);
+ int dev = yasl_private_scale_to_device(320.0, geom.scale_120);
sum += (uint64_t)dev + timing.refresh_ns;
}
uint64_t t1 = get_time_ns();
@@ -121,8 +121,8 @@ static void bench_output_follow(void) {
iters, (unsigned long long)sum);
print_bench_row("[2] surface_geometry_timing", per_cycle, ms, details);
- if (s) yasl_surface_destroy(s);
- if (d) yasl_display_close(d);
+ if (s) yasl_private_surface_destroy(s);
+ if (d) yasl_private_display_close(d);
}
#endif
@@ -135,8 +135,8 @@ static void bench_input_queue_resize(void) {
for (int b = 0; b < batches; b++) {
struct input_queue q = {0};
for (int i = 0; i < size; i++) {
- struct yasl_event ev = {
- .type = YASL_EVENT_POINTER_MOTION,
+ struct yasl_private_event ev = {
+ .type = YASL_PRIVATE_EVENT_POINTER_MOTION,
.pointer = { .pos = { .x = i, .y = b } },
};
input_queue_push(&q, &ev);
@@ -160,13 +160,13 @@ static void bench_input_queue_push_pop(void) {
uint64_t t0 = get_time_ns();
for (int r = 0; r < rounds; r++) {
for (int i = 0; i < events_per_round; i++) {
- struct yasl_event ev = {
- .type = YASL_EVENT_POINTER_DOWN,
- .pointer = { .pos = { .x = i, .y = r }, .button = YASL_BUTTON_MIDDLE },
+ struct yasl_private_event ev = {
+ .type = YASL_PRIVATE_EVENT_POINTER_DOWN,
+ .pointer = { .pos = { .x = i, .y = r }, .button = YASL_PRIVATE_BUTTON_MIDDLE },
};
input_queue_push(&q, &ev);
}
- struct yasl_event out;
+ struct yasl_private_event out;
while (input_queue_pop(&q, &out)) {
/* consume */
}
@@ -181,22 +181,22 @@ static void bench_input_queue_push_pop(void) {
}
/* Benchmark 5: Event Loop Timers Add & Delete */
-static uint64_t timer_dummy_cb(void *ud, yasl_timer_id id, uint64_t interval_ns) {
+static uint64_t timer_dummy_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns) {
(void)ud; (void)id; (void)interval_ns;
return 0;
}
static void bench_timers(void) {
const int iters = 2000000;
- struct yasl_loop *loop = yasl_loop_create();
+ struct yasl_private_loop *loop = yasl_private_loop_create();
uint64_t t0 = get_time_ns();
for (int i = 0; i < iters; i++) {
- yasl_timer_id id = yasl_loop_add_timer(loop, 1000000, timer_dummy_cb, NULL);
- yasl_loop_del_timer(loop, id);
+ yasl_private_timer_id id = yasl_private_loop_add_timer(loop, 1000000, timer_dummy_cb, NULL);
+ yasl_private_loop_del_timer(loop, id);
}
uint64_t t1 = get_time_ns();
- yasl_loop_destroy(loop);
+ yasl_private_loop_destroy(loop);
double ms = (double)(t1 - t0) / 1e6;
double per_cycle = (double)(t1 - t0) / iters;
char details[128];
@@ -207,30 +207,30 @@ static void bench_timers(void) {
/* Benchmark 6: Window Cold Create & Teardown */
static void bench_window_cold_create(void) {
const int iters = 200;
- struct yasl_display_spec dspec = { .app_id = "org.yasl.bench.cold" };
- struct yasl_window_spec sspec = {
+ struct yasl_private_display_spec dspec = { .app_id = "org.yasl.bench.cold" };
+ struct yasl_private_window_spec sspec = {
.title = "Benchmark Cold",
.size = { .x = 320, .y = 240 },
};
- struct yasl_display *probe = yasl_display_open(NULL, &dspec);
+ struct yasl_private_display *probe = yasl_private_display_open(NULL, &dspec);
if (!probe) {
print_bench_skipped("[6] window_cold_create", "no display server");
return;
}
- yasl_display_close(probe);
+ yasl_private_display_close(probe);
uint64_t t0 = get_time_ns();
int count = 0;
for (int i = 0; i < iters; i++) {
- struct yasl_display *d = yasl_display_open(NULL, &dspec);
+ struct yasl_private_display *d = yasl_private_display_open(NULL, &dspec);
if (d) {
- struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
+ struct yasl_surface *s = yasl_private_surface_create_toplevel(d, &sspec);
if (s) {
- yasl_surface_destroy(s);
+ yasl_private_surface_destroy(s);
count++;
}
- yasl_display_close(d);
+ yasl_private_display_close(d);
}
}
uint64_t t1 = get_time_ns();
@@ -244,14 +244,14 @@ static void bench_window_cold_create(void) {
/* Benchmark 7: Window Warm Create */
static void bench_window_warm_create(void) {
const int iters = 2000;
- struct yasl_display_spec dspec = { .app_id = "org.yasl.bench.warm" };
- struct yasl_display *d = yasl_display_open(NULL, &dspec);
+ struct yasl_private_display_spec dspec = { .app_id = "org.yasl.bench.warm" };
+ struct yasl_private_display *d = yasl_private_display_open(NULL, &dspec);
if (!d) {
print_bench_skipped("[7] window_warm_create", "no display server");
return;
}
- struct yasl_window_spec sspec = {
+ struct yasl_private_window_spec sspec = {
.title = "Benchmark Warm",
.size = { .x = 320, .y = 240 },
};
@@ -259,14 +259,14 @@ static void bench_window_warm_create(void) {
uint64_t t0 = get_time_ns();
int count = 0;
for (int i = 0; i < iters; i++) {
- struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
+ struct yasl_surface *s = yasl_private_surface_create_toplevel(d, &sspec);
if (s) {
- yasl_surface_destroy(s);
+ yasl_private_surface_destroy(s);
count++;
}
}
uint64_t t1 = get_time_ns();
- yasl_display_close(d);
+ yasl_private_display_close(d);
double ms = (double)(t1 - t0) / 1e6;
double per_cycle = count ? (double)(t1 - t0) / count : 0.0;
char details[128];
@@ -277,14 +277,14 @@ static void bench_window_warm_create(void) {
/* Benchmark 8: Window Shutdown (Single) */
static void bench_window_shutdown_single(void) {
const int iters = 2000;
- struct yasl_display_spec dspec = { .app_id = "org.yasl.bench.shutdown_single" };
- struct yasl_display *d = yasl_display_open(NULL, &dspec);
+ struct yasl_private_display_spec dspec = { .app_id = "org.yasl.bench.shutdown_single" };
+ struct yasl_private_display *d = yasl_private_display_open(NULL, &dspec);
if (!d) {
print_bench_skipped("[8] window_shutdown_single", "no display server");
return;
}
- struct yasl_window_spec sspec = {
+ struct yasl_private_window_spec sspec = {
.title = "Benchmark Shutdown Single",
.size = { .x = 320, .y = 240 },
};
@@ -292,16 +292,16 @@ static void bench_window_shutdown_single(void) {
uint64_t destroy_time_ns = 0;
int count = 0;
for (int i = 0; i < iters; i++) {
- struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
+ struct yasl_surface *s = yasl_private_surface_create_toplevel(d, &sspec);
if (s) {
uint64_t t0 = get_time_ns();
- yasl_surface_destroy(s);
+ yasl_private_surface_destroy(s);
uint64_t t1 = get_time_ns();
destroy_time_ns += (t1 - t0);
count++;
}
}
- yasl_display_close(d);
+ yasl_private_display_close(d);
double ms = (double)destroy_time_ns / 1e6;
double per_cycle = count ? (double)destroy_time_ns / count : 0.0;
char details[128];
@@ -313,14 +313,14 @@ static void bench_window_shutdown_single(void) {
static void bench_window_shutdown_all(void) {
const int batches = 50;
const int batch_size = 64;
- struct yasl_display_spec dspec = { .app_id = "org.yasl.bench.shutdown_all" };
- struct yasl_display *d = yasl_display_open(NULL, &dspec);
+ struct yasl_private_display_spec dspec = { .app_id = "org.yasl.bench.shutdown_all" };
+ struct yasl_private_display *d = yasl_private_display_open(NULL, &dspec);
if (!d) {
print_bench_skipped("[9] window_shutdown_all", "no display server");
return;
}
- struct yasl_window_spec sspec = {
+ struct yasl_private_window_spec sspec = {
.title = "Benchmark Bulk Window",
.size = { .x = 320, .y = 240 },
};
@@ -331,12 +331,12 @@ static void bench_window_shutdown_all(void) {
for (int b = 0; b < batches; b++) {
for (int i = 0; i < batch_size; i++) {
- surfaces[i] = yasl_surface_create_toplevel(d, &sspec);
+ surfaces[i] = yasl_private_surface_create_toplevel(d, &sspec);
}
uint64_t t0 = get_time_ns();
for (int i = 0; i < batch_size; i++) {
if (surfaces[i]) {
- yasl_surface_destroy(surfaces[i]);
+ yasl_private_surface_destroy(surfaces[i]);
surfaces[i] = NULL;
total_destroyed++;
}
@@ -344,7 +344,7 @@ static void bench_window_shutdown_all(void) {
uint64_t t1 = get_time_ns();
destroy_time_ns += (t1 - t0);
}
- yasl_display_close(d);
+ yasl_private_display_close(d);
double ms = (double)destroy_time_ns / 1e6;
double per_cycle = total_destroyed ? (double)destroy_time_ns / total_destroyed : 0.0;
double per_batch_ns = batches ? (double)destroy_time_ns / batches : 0.0;
@@ -359,20 +359,20 @@ static void bench_window_shutdown_all(void) {
/* Benchmark 10: Window Titlebar Update */
static void bench_window_title_update(void) {
const int iters = 200000;
- struct yasl_display_spec dspec = { .app_id = "org.yasl.bench.title" };
- struct yasl_display *d = yasl_display_open(NULL, &dspec);
+ struct yasl_private_display_spec dspec = { .app_id = "org.yasl.bench.title" };
+ struct yasl_private_display *d = yasl_private_display_open(NULL, &dspec);
if (!d) {
print_bench_skipped("[10] window_titlebar_update", "no display server");
return;
}
- struct yasl_window_spec sspec = {
+ struct yasl_private_window_spec sspec = {
.title = "Initial Title",
.size = { .x = 320, .y = 240 },
};
- struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
+ struct yasl_surface *s = yasl_private_surface_create_toplevel(d, &sspec);
if (!s) {
- yasl_display_close(d);
+ yasl_private_display_close(d);
print_bench_skipped("[10] window_titlebar_update", "failed to create surface");
return;
}
@@ -386,11 +386,11 @@ static void bench_window_title_update(void) {
uint64_t t0 = get_time_ns();
for (int i = 0; i < iters; i++) {
- yasl_surface_set_title(s, titles[i & 3]);
+ yasl_private_surface_set_title(s, titles[i & 3]);
}
uint64_t t1 = get_time_ns();
- yasl_surface_destroy(s);
- yasl_display_close(d);
+ yasl_private_surface_destroy(s);
+ yasl_private_display_close(d);
double ms = (double)(t1 - t0) / 1e6;
double per_cycle = (double)(t1 - t0) / iters;
char details[128];
@@ -401,14 +401,14 @@ static void bench_window_title_update(void) {
/* Benchmark 11: Window Resize / Dimension Reallocation */
static void bench_window_resize(void) {
const int iters = 2000;
- struct yasl_display_spec dspec = { .app_id = "org.yasl.bench.resize" };
- struct yasl_display *d = yasl_display_open(NULL, &dspec);
+ struct yasl_private_display_spec dspec = { .app_id = "org.yasl.bench.resize" };
+ struct yasl_private_display *d = yasl_private_display_open(NULL, &dspec);
if (!d) {
print_bench_skipped("[11] window_resize_cycle", "no display server");
return;
}
- static const struct yasl_vector2i sizes[] = {
+ static const struct yasl_private_vector2i sizes[] = {
{ 320, 240 },
{ 640, 480 },
{ 800, 600 },
@@ -421,20 +421,20 @@ static void bench_window_resize(void) {
uint64_t t0 = get_time_ns();
int count = 0;
for (int i = 0; i < iters; i++) {
- struct yasl_window_spec sspec = {
+ struct yasl_private_window_spec sspec = {
.title = "Resize Bench",
.size = sizes[i % num_sizes],
};
- struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
+ struct yasl_surface *s = yasl_private_surface_create_toplevel(d, &sspec);
if (s) {
- struct yasl_geometry geom = yasl_surface_geometry(s);
+ struct yasl_private_geometry geom = yasl_private_surface_geometry(s);
(void)geom;
- yasl_surface_destroy(s);
+ yasl_private_surface_destroy(s);
count++;
}
}
uint64_t t1 = get_time_ns();
- yasl_display_close(d);
+ yasl_private_display_close(d);
double ms = (double)(t1 - t0) / 1e6;
double per_cycle = count ? (double)(t1 - t0) / count : 0.0;
char details[128];
diff --git a/tests/cocoa/test_cursor.m b/tests/cocoa/test_cursor.m
index 4054d59..4c961df 100644
--- a/tests/cocoa/test_cursor.m
+++ b/tests/cocoa/test_cursor.m
@@ -1,5 +1,5 @@
#import <AppKit/AppKit.h>
-#include <yasl/native.h>
+#include <common/private_native.h>
#include <assert.h>
static void assert_transparent(NSCursor *cursor)
@@ -15,18 +15,18 @@ static void assert_transparent(NSCursor *cursor)
int main(void)
{
@autoreleasepool {
- struct yasl_display *display = yasl_display_open(NULL, NULL);
+ struct yasl_private_display *display = yasl_private_display_open(NULL, NULL);
assert(display);
- struct yasl_window_spec spec = {.title = "Cursor test", .size = {320, 240}};
- struct yasl_surface *surface = yasl_surface_create_toplevel(display, &spec);
+ struct yasl_private_window_spec spec = {.title = "Cursor test", .size = {320, 240}};
+ struct yasl_surface *surface = yasl_private_surface_create_toplevel(display, &spec);
assert(surface);
- NSWindow *window = (NSWindow *)yasl_surface_get_native_handle(surface);
+ NSWindow *window = (NSWindow *)yasl_private_surface_get_native_handle(surface);
NSView *view = window.contentView;
NSEvent *cursor = [NSEvent enterExitEventWithType:NSEventTypeCursorUpdate
location:NSMakePoint(100, 120) modifierFlags:0 timestamp:0
windowNumber:window.windowNumber context:nil eventNumber:0 trackingNumber:0 userData:NULL];
assert(cursor);
- yasl_surface_set_cursor(surface, YASL_CURSOR_HIDDEN);
+ yasl_private_surface_set_cursor(surface, YASL_PRIVATE_CURSOR_HIDDEN);
[view cursorUpdate:cursor];
NSCursor *hidden = [NSCursor currentCursor];
assert_transparent(hidden);
@@ -37,21 +37,21 @@ int main(void)
[view mouseMoved:motion];
[view cursorUpdate:cursor];
assert([NSCursor currentCursor] == hidden);
- struct yasl_event event;
+ struct yasl_private_event event;
bool moved = false;
- while (yasl_surface_poll_event(surface, &event))
- if (event.type == YASL_EVENT_POINTER_MOTION)
+ while (yasl_private_surface_poll_event(surface, &event))
+ if (event.type == YASL_PRIVATE_EVENT_POINTER_MOTION)
moved = true;
assert(moved);
[[NSCursor arrowCursor] set]; /* Simulate leaving and re-entering. */
[view cursorUpdate:cursor];
assert([NSCursor currentCursor] == hidden);
- yasl_surface_set_cursor(surface, YASL_CURSOR_ARROW);
+ yasl_private_surface_set_cursor(surface, YASL_PRIVATE_CURSOR_ARROW);
[view cursorUpdate:cursor];
assert([NSCursor currentCursor] == [NSCursor arrowCursor]);
- yasl_surface_destroy(surface);
- yasl_display_close(display);
+ yasl_private_surface_destroy(surface);
+ yasl_private_display_close(display);
}
return 0;
}
diff --git a/tests/cocoa/test_keyboard.m b/tests/cocoa/test_keyboard.m
index f93a745..a41011c 100644
--- a/tests/cocoa/test_keyboard.m
+++ b/tests/cocoa/test_keyboard.m
@@ -1,5 +1,6 @@
#import <AppKit/AppKit.h>
-#include <yasl/native.h>
+#include <common/private_native.h>
+#include <common/backend.h>
#include <assert.h>
#include <math.h>
#include <string.h>
@@ -31,32 +32,40 @@
}
@end
-static struct yasl_event next(struct yasl_surface *surface, enum yasl_event_type type)
+static struct yasl_private_event next(struct yasl_surface *surface, enum yasl_private_event_type type)
{
- struct yasl_event event;
- assert(yasl_surface_poll_event(surface, &event));
+ struct yasl_private_event event;
+ assert(yasl_private_surface_poll_event(surface, &event));
assert(event.type == type && event.surface == surface);
return event;
}
static void empty(struct yasl_surface *surface)
{
- struct yasl_event event;
- assert(!yasl_surface_poll_event(surface, &event));
+ struct yasl_private_event event;
+ assert(!yasl_private_surface_poll_event(surface, &event));
}
int main(void)
{
@autoreleasepool {
- struct yasl_display *display = yasl_display_open(NULL, NULL);
+ struct yasl_private_display *display = yasl_private_display_open(NULL, NULL);
assert(display);
- struct yasl_window_spec spec = {.title = "Keyboard test", .size = {320, 240}};
- struct yasl_surface *surface = yasl_surface_create_toplevel(display, &spec);
+ struct yasl_private_window_spec spec = {.title = "Keyboard test", .size = {320, 240}};
+ struct yasl_surface *surface = yasl_private_surface_create_toplevel(display, &spec);
assert(surface);
- NSWindow *window = (NSWindow *)yasl_surface_get_native_handle(surface);
+ surface->text_input_active = true;
+ surface->text_surrounding = strdup("A😀B");
+ surface->text_length = strlen(surface->text_surrounding);
+ surface->text_begin = 1;
+ surface->text_end = 5;
+ surface->text_revision = 7;
+ surface->text_generation = 3;
+ assert(display->ops->surface_update_text_input(surface));
+ NSWindow *window = (NSWindow *)yasl_private_surface_get_native_handle(surface);
NSView<NSTextInputClient> *view = (id)window.contentView;
- struct yasl_event event;
- while (yasl_surface_poll_event(surface, &event)) {}
+ struct yasl_private_event event;
+ while (yasl_private_surface_poll_event(surface, &event)) {}
InputSample *sample = [InputSample new];
NSEvent *native = (NSEvent *)sample;
@@ -65,7 +74,7 @@ int main(void)
sample.label = @"q";
sample.shiftedLabel = @"Q";
[view keyUp:native];
- event = next(surface, YASL_EVENT_KEY);
+ event = next(surface, YASL_PRIVATE_EVENT_KEY);
assert(event.key.key == YASL_KEY_A && event.key.native_code == 0);
assert(event.key.action == YASL_KEY_RELEASE);
assert(event.key.codepoint == 'q' && event.key.shifted == 'Q');
@@ -73,29 +82,31 @@ int main(void)
sample.label = @"😀"; /* One scalar, two UTF-16 units. */
sample.shiftedLabel = @"ab";
[view keyUp:native];
- event = next(surface, YASL_EVENT_KEY);
+ event = next(surface, YASL_PRIVATE_EVENT_KEY);
assert(event.key.codepoint == 0x1f600 && event.key.shifted == 0);
sample.type = NSEventTypeFlagsChanged;
sample.keyCode = 56; /* Left Shift. */
sample.modifierFlags = NSEventModifierFlagShift | 0x0002;
[view flagsChanged:native];
- event = next(surface, YASL_EVENT_KEY);
+ event = next(surface, YASL_PRIVATE_EVENT_KEY);
assert(event.key.key == YASL_KEY_LEFT_SHIFT && event.key.action == YASL_KEY_PRESS);
- assert(event.key.mods == YASL_MOD_SHIFT && !event.key.codepoint && !event.key.shifted);
+ assert(event.key.mods == YASL_PRIVATE_MOD_SHIFT && !event.key.codepoint && !event.key.shifted);
sample.modifierFlags = 0;
[view flagsChanged:native];
- assert(next(surface, YASL_EVENT_KEY).key.action == YASL_KEY_RELEASE);
+ assert(next(surface, YASL_PRIVATE_EVENT_KEY).key.action == YASL_KEY_RELEASE);
/* Native text services own preedit and commits. Marked text is not an
* insertion, and commits retain all UTF-8 bytes, including non-BMP. */
[view setMarkedText:@"é" selectedRange:NSMakeRange(1, 0)
- replacementRange:NSMakeRange(NSNotFound, 0)];
- assert(strcmp(yasl_surface_ime_preedit(surface), "é") == 0);
+ replacementRange:NSMakeRange(1, 2)];
+ assert(strcmp(yasl_private_surface_ime_preedit(surface), "é") == 0);
empty(surface);
[view insertText:@"😀é" replacementRange:NSMakeRange(NSNotFound, 0)];
- event = next(surface, YASL_EVENT_TEXT);
- assert(event.text.len == strlen("😀é") && strcmp(event.text.data, "😀é") == 0);
- assert(!yasl_surface_ime_preedit(surface));
+ event = next(surface, YASL_PRIVATE_EVENT_TEXT_EDIT);
+ assert(event.text_edit.len == strlen("😀é") && strcmp(event.text_edit.data, "😀é") == 0);
+ assert(event.text_edit.begin == 1 && event.text_edit.end == 5);
+ assert(event.text_edit.revision == 7 && event.text_edit.generation == 3);
+ assert(!yasl_private_surface_ime_preedit(surface));
empty(surface);
/* Exercise the keyDown -> interpretKeyEvents -> insertText route with
@@ -104,9 +115,16 @@ int main(void)
modifierFlags:0 timestamp:0 windowNumber:window.windowNumber context:nil
characters:@"a" charactersIgnoringModifiers:@"a" isARepeat:NO keyCode:0];
[view keyDown:press];
- assert(next(surface, YASL_EVENT_KEY).key.key == YASL_KEY_A);
- event = next(surface, YASL_EVENT_TEXT);
- assert(event.text.len == 1 && event.text.data[0] == 'a');
+ assert(next(surface, YASL_PRIVATE_EVENT_KEY).key.key == YASL_KEY_A);
+ event = next(surface, YASL_PRIVATE_EVENT_TEXT_EDIT);
+ assert(event.text_edit.len == 1 && event.text_edit.data[0] == 'a');
+ assert(event.text_edit.begin == 1 && event.text_edit.end == 5);
+ empty(surface);
+
+ surface->text_end = 1;
+ [view doCommandBySelector:@selector(deleteBackward)];
+ event = next(surface, YASL_PRIVATE_EVENT_TEXT_EDIT);
+ assert(event.text_edit.len == 0 && event.text_edit.begin == 0 && event.text_edit.end == 1);
empty(surface);
sample.type = NSEventTypeScrollWheel;
@@ -114,20 +132,20 @@ int main(void)
sample.scrollingDeltaX = -2.5;
sample.scrollingDeltaY = 1.25;
[view scrollWheel:native];
- event = next(surface, YASL_EVENT_POINTER_SCROLL);
+ event = next(surface, YASL_PRIVATE_EVENT_POINTER_SCROLL);
assert(event.pointer.scroll.x == 0.25 && event.pointer.scroll.y == -0.125);
sample.hasPreciseScrollingDeltas = NO;
sample.scrollingDeltaX = 1;
sample.scrollingDeltaY = -1;
[view scrollWheel:native];
- event = next(surface, YASL_EVENT_POINTER_SCROLL);
+ event = next(surface, YASL_PRIVATE_EVENT_POINTER_SCROLL);
assert(event.pointer.scroll.x == -1 && event.pointer.scroll.y == 1);
sample.scrollingDeltaX = NAN;
[view scrollWheel:native];
empty(surface);
[sample release];
- yasl_surface_destroy(surface);
- yasl_display_close(display);
+ yasl_private_surface_destroy(surface);
+ yasl_private_display_close(display);
}
return 0;
}
diff --git a/tests/cocoa/test_native.c b/tests/cocoa/test_native.c
index 674513d..9b8f8fa 100644
--- a/tests/cocoa/test_native.c
+++ b/tests/cocoa/test_native.c
@@ -1,77 +1,77 @@
#define _POSIX_C_SOURCE 200809L
-#include "yasl/native.h"
-#include "yasl/loop.h"
-#include "yasl/yasl.h"
+#include "common/private_native.h"
+#include "common/private_loop.h"
+#include "common/types.h"
#include <assert.h>
#include <time.h>
struct state {
- struct yasl_display *display;
+ struct yasl_private_display *display;
struct yasl_surface *surface;
unsigned draws;
};
-static void draw(struct yasl_surface *surface, struct yasl_frame *frame, void *ud)
+static void draw(struct yasl_surface *surface, struct yasl_private_frame *frame, void *ud)
{
struct state *state = ud;
assert(surface == state->surface && frame->surface == surface);
- assert(yasl_surface_display(surface) == state->display);
+ assert(yasl_private_surface_display(surface) == state->display);
assert(frame->target.pixels);
- struct yasl_native_frame native;
- assert(yasl_surface_get_native_frame(surface, &frame->target, &native));
+ struct yasl_private_native_frame native;
+ assert(yasl_private_surface_get_native_frame(surface, &frame->target, &native));
assert(native.drawable);
- struct yasl_vector2i size = yasl_surface_geometry(surface).size;
+ struct yasl_private_vector2i size = yasl_private_surface_geometry(surface).size;
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 = YASL_DAMAGE_MAX + 1};
- assert(!yasl_surface_present(surface, &frame->target, &damage));
+ struct yasl_private_damage_list damage = {.n = YASL_PRIVATE_DAMAGE_MAX + 1};
+ assert(!yasl_private_surface_present(surface, &frame->target, &damage));
damage.n = -1;
- struct yasl_framebuffer invalid = frame->target;
+ struct yasl_private_framebuffer invalid = frame->target;
invalid.size.x++;
- assert(!yasl_surface_present(surface, &invalid, &damage));
+ assert(!yasl_private_surface_present(surface, &invalid, &damage));
invalid = frame->target;
invalid.generation--;
- assert(!yasl_surface_present(surface, &invalid, &damage));
- assert(!yasl_surface_present(surface, &frame->target, NULL));
- assert(yasl_surface_present(surface, &frame->target, &damage));
- assert(!yasl_surface_get_native_frame(surface, &frame->target, &native));
- assert(!yasl_surface_present(surface, &frame->target, &damage));
+ assert(!yasl_private_surface_present(surface, &invalid, &damage));
+ assert(!yasl_private_surface_present(surface, &frame->target, NULL));
+ assert(yasl_private_surface_present(surface, &frame->target, &damage));
+ assert(!yasl_private_surface_get_native_frame(surface, &frame->target, &native));
+ assert(!yasl_private_surface_present(surface, &frame->target, &damage));
state->draws++;
- yasl_surface_destroy(surface);
+ yasl_private_surface_destroy(surface);
state->surface = NULL;
}
-static void test_window(enum yasl_backend request, bool null_spec)
+static void test_window(enum yasl_private_backend request, bool null_spec)
{
- struct yasl_display_spec spec = {.backend = request};
- struct state state = {.display = yasl_display_open(NULL, null_spec ? NULL : &spec)};
+ struct yasl_private_display_spec spec = {.backend = request};
+ struct state state = {.display = yasl_private_display_open(NULL, null_spec ? NULL : &spec)};
assert(state.display);
- assert(yasl_display_backend(state.display) == YASL_BACKEND_COCOA);
- struct yasl_window_cbs callbacks = {.draw = draw};
- struct yasl_window_spec surface = {
+ assert(yasl_private_display_backend(state.display) == YASL_PRIVATE_BACKEND_COCOA);
+ struct yasl_private_window_cbs callbacks = {.draw = draw};
+ struct yasl_private_window_spec surface = {
.title = "Native backend test", .size = {160, 100},
.cbs = &callbacks, .ud = &state,
};
- state.surface = yasl_surface_create_toplevel(state.display, &surface);
+ state.surface = yasl_private_surface_create_toplevel(state.display, &surface);
assert(state.surface);
assert(!state.draws);
callbacks.draw = NULL;
- 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, 0);
- yasl_display_flush(state.display);
+ yasl_private_surface_schedule_redraw(state.surface);
+ uint64_t deadline = yasl_private_loop_now_ns() + 3000000000ull;
+ while (!state.draws && yasl_private_loop_now_ns() < deadline) {
+ yasl_private_display_dispatch(state.display, 0);
+ yasl_private_display_flush(state.display);
nanosleep(&(struct timespec){.tv_nsec = 1000000}, NULL);
}
assert(state.draws == 1 && !state.surface);
- yasl_display_close(state.display);
+ yasl_private_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);
+ test_window(YASL_PRIVATE_BACKEND_AUTO, true);
+ test_window(YASL_PRIVATE_BACKEND_AUTO, false);
+ test_window(YASL_PRIVATE_BACKEND_COCOA, false);
return 0;
}
diff --git a/tests/cocoa/test_ownership.m b/tests/cocoa/test_ownership.m
index 6d69907..4efd164 100644
--- a/tests/cocoa/test_ownership.m
+++ b/tests/cocoa/test_ownership.m
@@ -1,7 +1,9 @@
-#include "yasl/native.h"
-#include "yasl/native/cocoa.h"
+#include "common/private_native.h"
+#include "common/private_native_cocoa.h"
+#include "common/backend.h"
#import <AppKit/AppKit.h>
-#include "yasl/yasl.h"
+#import <Metal/Metal.h>
+#include "common/types.h"
#include <assert.h>
#include <errno.h>
#include <stdio.h>
@@ -9,60 +11,98 @@
int main(void)
{
- struct yasl_display *d = yasl_display_open(NULL, NULL);
+ struct yasl_private_display *d = yasl_private_display_open(NULL, NULL);
assert(d);
- struct yasl_window_spec spec = {.title = "Ownership test", .size = {160, 100}};
- struct yasl_surface *s = yasl_surface_create_toplevel(d, &spec);
+ assert(yasl_private_display_caps(d) & YASL_PRIVATE_CAP_TEXT_INPUT);
+ struct yasl_private_window_spec spec = {.title = "Ownership test", .size = {160, 100}};
+ struct yasl_surface *s = yasl_private_surface_create_toplevel(d, &spec);
assert(s);
- assert(yasl_display_get_native_handle(d) == (void *)NSApp);
- assert(yasl_cocoa_display_get_application(d) == NSApp);
- NSWindow *window = (NSWindow *)yasl_surface_get_native_handle(s);
- assert(yasl_cocoa_surface_get_window(s) == window);
- assert(yasl_cocoa_surface_get_view(s) == window.contentView);
+ assert(yasl_private_display_get_native_handle(d) == (void *)NSApp);
+ assert(yasl_private_cocoa_display_get_application(d) == NSApp);
+ NSWindow *window = (NSWindow *)yasl_private_surface_get_native_handle(s);
+ assert(yasl_private_cocoa_surface_get_window(s) == window);
+ assert(yasl_private_cocoa_surface_get_view(s) == window.contentView);
assert([window isKindOfClass:[NSWindow class]] && window.contentView);
- struct yasl_event event;
- while (yasl_display_poll_event(d, &event)) {}
+ struct yasl_private_event event;
+ while (yasl_private_display_poll_event(d, &event)) {}
- assert(yasl_surface_set_size(s, (struct yasl_vector2i){180, 110}));
- assert(yasl_surface_set_fullscreen(s, false));
- assert(yasl_surface_set_maximized(s, false));
- assert(!yasl_surface_set_size(s, (struct yasl_vector2i){0, 100}));
+ struct yasl_private_window_spec gpu_spec = {
+ .flags = YASL_PRIVATE_SURFACE_HIDDEN | YASL_PRIVATE_SURFACE_UNDECORATED |
+ YASL_PRIVATE_SURFACE_FIXED_SIZE,
+ .external_gpu = true,
+ .title = "External GPU test",
+ .size = {160, 100},
+ };
+ struct yasl_surface *gpu = yasl_private_surface_create_toplevel(d, &gpu_spec);
+ assert(gpu && gpu->presenter == YASL_PRIVATE_PRESENTER_NONE);
+ CAMetalLayer *metal = (CAMetalLayer *)d->ops->surface_get_metal_layer(gpu);
+ assert(metal && [metal isKindOfClass:[CAMetalLayer class]] && !metal.device);
+ NSWindow *hidden = (NSWindow *)yasl_private_surface_get_native_handle(gpu);
+ assert(!hidden.isVisible && hidden.styleMask == NSWindowStyleMaskBorderless);
+ yasl_private_surface_destroy(gpu);
+
+ struct yasl_surface *gl = d->ops->surface_create_gl(d, &gpu_spec, NULL, NULL);
+ if (gl) {
+ assert(gl->presenter == YASL_PRIVATE_PRESENTER_GL);
+ assert(d->ops->gl_make_current(d, gl));
+ assert(d->ops->gl_get_proc_address(d, "glClear") != NULL);
+ assert(d->ops->gl_swap_interval(d, gl, 1));
+ assert(d->ops->gl_swap_buffers(d, gl));
+ assert(d->ops->gl_make_current(d, NULL));
+ d->ops->surface_destroy(gl);
+ }
+
+ assert(yasl_private_surface_set_size(s, (struct yasl_private_vector2i){180, 110}));
+ assert(yasl_private_surface_set_fullscreen(s, false));
+ assert(yasl_private_surface_set_maximized(s, false));
+ assert(!yasl_private_surface_set_size(s, (struct yasl_private_vector2i){0, 100}));
assert(errno == EINVAL);
- struct yasl_popup_spec popup_spec = {
+ struct yasl_private_popup_spec popup_spec = {
+ .positioner_explicit = true,
+ .anchor_edges = 4, .gravity = 3, .constraints = 63,
.size = {80, 40}, .anchor = {10, 10, 2, 2},
};
- struct yasl_surface *popup = yasl_surface_create_popup(s, &popup_spec);
+ struct yasl_surface *popup = yasl_private_surface_create_popup(s, &popup_spec);
assert(popup);
- assert(!yasl_surface_set_size(popup, (struct yasl_vector2i){80, 40}));
+ popup_spec.anchor_edges = 8;
+ popup_spec.gravity = 5;
+ assert(yasl_private_surface_popup_reposition(popup, &popup_spec));
+ assert(!yasl_private_surface_set_size(popup, (struct yasl_private_vector2i){80, 40}));
assert(errno == EINVAL);
- assert(!yasl_surface_set_fullscreen(popup, true) && errno == EINVAL);
- assert(!yasl_surface_set_maximized(popup, true) && errno == EINVAL);
- assert(!yasl_surface_minimize(popup) && errno == EINVAL);
- yasl_surface_destroy(popup);
- while (yasl_display_poll_event(d, &event)) {}
+ assert(!yasl_private_surface_set_fullscreen(popup, true) && errno == EINVAL);
+ assert(!yasl_private_surface_set_maximized(popup, true) && errno == EINVAL);
+ assert(!yasl_private_surface_minimize(popup) && errno == EINVAL);
+ yasl_private_surface_destroy(popup);
+ while (yasl_private_display_poll_event(d, &event)) {}
- id<NSTextInputClient> text_input = (id<NSTextInputClient>)yasl_cocoa_surface_get_view(s);
+ s->text_input_active = true;
+ s->text_surrounding = strdup("é漢");
+ s->text_length = strlen(s->text_surrounding);
+ s->text_begin = 0;
+ s->text_end = s->text_length;
+ assert(d->ops->surface_update_text_input(s));
+ id<NSTextInputClient> text_input = (id<NSTextInputClient>)yasl_private_cocoa_surface_get_view(s);
[text_input setMarkedText:@"é漢" selectedRange:NSMakeRange(1, 1)
replacementRange:NSMakeRange(NSNotFound, 0)];
- assert(strcmp(yasl_surface_ime_preedit(s), "é漢") == 0);
+ assert(strcmp(yasl_private_surface_ime_preedit(s), "é漢") == 0);
[text_input unmarkText];
- assert(yasl_surface_ime_preedit(s) == NULL);
- assert(yasl_display_poll_event(d, &event));
- assert(event.type == YASL_EVENT_COMPOSITION && event.composition.active);
+ assert(yasl_private_surface_ime_preedit(s) == NULL);
+ assert(yasl_private_display_poll_event(d, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_COMPOSITION && event.composition.active);
assert(strcmp(event.composition.data, "é漢") == 0);
assert(event.composition.begin == 2 && event.composition.end == 5);
- assert(yasl_display_poll_event(d, &event));
- assert(event.type == YASL_EVENT_COMPOSITION && !event.composition.active);
+ assert(yasl_private_display_poll_event(d, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_COMPOSITION && !event.composition.active);
assert(event.composition.begin == -1 && event.composition.end == -1);
- while (yasl_display_poll_event(d, &event)) {}
+ while (yasl_private_display_poll_event(d, &event)) {}
uint64_t request_ids[600];
@autoreleasepool {
for (int i = 0; i < 600; i++) {
char text[32];
snprintf(text, sizeof(text), "paste-%d", i);
- assert(yasl_clipboard_set(d, YASL_CLIPBOARD, text));
- request_ids[i] = yasl_clipboard_request(s, YASL_CLIPBOARD);
+ assert(yasl_private_clipboard_set(d, YASL_PRIVATE_CLIPBOARD, text));
+ request_ids[i] = yasl_private_clipboard_request(s, YASL_PRIVATE_CLIPBOARD);
assert(request_ids[i] != 0);
memset(text, 'x', sizeof(text));
}
@@ -71,32 +111,32 @@ int main(void)
for (int i = 0; i < 600; i++) {
char text[32];
snprintf(text, sizeof(text), "paste-%d", i);
- assert(yasl_display_poll_event(d, &event));
- assert(event.type == YASL_EVENT_PASTE && event.paste.request_id == request_ids[i]);
- assert(event.paste.status == YASL_TRANSFER_OK && strcmp(event.paste.data, text) == 0);
+ assert(yasl_private_display_poll_event(d, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_PASTE && event.paste.request_id == request_ids[i]);
+ assert(event.paste.status == YASL_PRIVATE_TRANSFER_OK && strcmp(event.paste.data, text) == 0);
}
const char *last = event.paste.data;
- assert(!yasl_display_poll_event(d, &event));
- @autoreleasepool { yasl_display_copy(d, YASL_CLIPBOARD, "new selection"); }
+ assert(!yasl_private_display_poll_event(d, &event));
+ @autoreleasepool { yasl_private_display_copy(d, YASL_PRIVATE_CLIPBOARD, "new selection"); }
assert(strcmp(last, "paste-599") == 0);
- assert(!yasl_clipboard_set(d, YASL_CLIPBOARD, "\xc3"));
+ assert(!yasl_private_clipboard_set(d, YASL_PRIVATE_CLIPBOARD, "\xc3"));
assert(errno == EILSEQ);
- assert(yasl_clipboard_set(d, YASL_CLIPBOARD, ""));
- uint64_t empty_id = yasl_clipboard_request(s, YASL_CLIPBOARD);
- assert(empty_id && yasl_display_poll_event(d, &event));
- assert(event.type == YASL_EVENT_PASTE && event.paste.request_id == empty_id);
- assert(event.paste.status == YASL_TRANSFER_OK && !event.paste.len && !event.paste.data[0]);
+ assert(yasl_private_clipboard_set(d, YASL_PRIVATE_CLIPBOARD, ""));
+ uint64_t empty_id = yasl_private_clipboard_request(s, YASL_PRIVATE_CLIPBOARD);
+ assert(empty_id && yasl_private_display_poll_event(d, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_PASTE && event.paste.request_id == empty_id);
+ assert(event.paste.status == YASL_PRIVATE_TRANSFER_OK && !event.paste.len && !event.paste.data[0]);
[NSPasteboard.generalPasteboard clearContents];
- uint64_t unavailable_id = yasl_clipboard_request(s, YASL_CLIPBOARD);
- assert(unavailable_id && yasl_display_poll_event(d, &event));
- assert(event.type == YASL_EVENT_PASTE && event.paste.request_id == unavailable_id);
- assert(event.paste.status == YASL_TRANSFER_UNAVAILABLE && !event.paste.len);
- yasl_display_copy(d, YASL_CLIPBOARD, "legacy paste");
- yasl_display_paste(d, YASL_CLIPBOARD, s);
- assert(yasl_display_poll_event(d, &event));
- assert(event.type == YASL_EVENT_PASTE && event.paste.request_id == 0);
- assert(event.paste.status == YASL_TRANSFER_OK && strcmp(event.text.data, "legacy paste") == 0);
- yasl_surface_destroy(s);
- yasl_display_close(d);
+ uint64_t unavailable_id = yasl_private_clipboard_request(s, YASL_PRIVATE_CLIPBOARD);
+ assert(unavailable_id && yasl_private_display_poll_event(d, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_PASTE && event.paste.request_id == unavailable_id);
+ assert(event.paste.status == YASL_PRIVATE_TRANSFER_UNAVAILABLE && !event.paste.len);
+ yasl_private_display_copy(d, YASL_PRIVATE_CLIPBOARD, "legacy paste");
+ yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, s);
+ assert(yasl_private_display_poll_event(d, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_PASTE && event.paste.request_id == 0);
+ assert(event.paste.status == YASL_PRIVATE_TRANSFER_OK && strcmp(event.text.data, "legacy paste") == 0);
+ yasl_private_surface_destroy(s);
+ yasl_private_display_close(d);
return 0;
}
diff --git a/tests/cocoa/test_tablet.m b/tests/cocoa/test_tablet.m
index 715a66e..8eb63d5 100644
--- a/tests/cocoa/test_tablet.m
+++ b/tests/cocoa/test_tablet.m
@@ -1,5 +1,5 @@
#import <AppKit/AppKit.h>
-#include <yasl/native.h>
+#include <common/private_native.h>
#include <assert.h>
/* A strict event double catches querying tablet-only properties on ordinary
@@ -15,6 +15,10 @@
@property NSPointingDeviceType tool;
@property float samplePressure;
@property NSPoint sampleTilt;
+@property double sampleRotation;
+@property double sampleTangential;
+@property NSInteger sampleDeviceID;
+@property NSInteger sampleToolID;
@end
@implementation TabletSample
@@ -33,70 +37,106 @@
assert(self.type == NSEventTypeTabletProximity || self.subtype == NSEventSubtypeTabletProximity);
return self.tool;
}
+- (double)rotation { return self.sampleRotation; }
+- (double)tangentialPressure { return self.sampleTangential; }
+- (NSInteger)pointingDeviceID { return self.sampleDeviceID; }
+- (NSInteger)pointingDeviceSerialNumber { return self.sampleToolID; }
@end
-static struct yasl_event next(struct yasl_surface *surface)
+static struct yasl_private_event next(struct yasl_surface *surface, enum yasl_private_event_type type)
{
- struct yasl_event event;
- while (yasl_surface_poll_event(surface, &event))
- return event;
- assert(!"Expected a pointer event");
- return (struct yasl_event){0};
+ struct yasl_private_event event;
+ assert(yasl_private_surface_poll_event(surface, &event));
+ assert(event.type == type);
+ return event;
}
int main(void)
{
@autoreleasepool {
- struct yasl_display *display = yasl_display_open(NULL, NULL);
- assert(display && (yasl_display_caps(display) & YASL_CAP_TABLET));
- struct yasl_window_spec spec = {.title = "Tablet test", .size = {320, 240}};
- struct yasl_surface *surface = yasl_surface_create_toplevel(display, &spec);
+ struct yasl_private_display *display = yasl_private_display_open(NULL, NULL);
+ assert(display && (yasl_private_display_caps(display) & YASL_PRIVATE_CAP_TABLET));
+ struct yasl_private_window_spec spec = {.title = "Tablet test", .size = {320, 240}};
+ struct yasl_surface *surface = yasl_private_surface_create_toplevel(display, &spec);
assert(surface);
- NSWindow *window = (NSWindow *)yasl_surface_get_native_handle(surface);
+ NSWindow *window = (NSWindow *)yasl_private_surface_get_native_handle(surface);
NSView *view = window.contentView;
- struct yasl_event event;
- while (yasl_surface_poll_event(surface, &event)) {}
+ struct yasl_private_event event;
+ while (yasl_private_surface_poll_event(surface, &event)) {}
TabletSample *sample = [TabletSample new];
NSEvent *native = (NSEvent *)sample;
sample.type = NSEventTypeTabletProximity;
sample.enteringProximity = YES;
sample.tool = NSPointingDeviceTypePen;
+ sample.sampleDeviceID = 17;
+ sample.sampleToolID = 29;
[view tabletProximity:native];
sample.type = NSEventTypeMouseMoved;
sample.subtype = NSEventSubtypeTabletPoint;
sample.locationInWindow = NSMakePoint(100, 120);
sample.sampleTilt = NSMakePoint(-0.5, 0.5);
+ sample.sampleRotation = 37.5;
+ sample.sampleTangential = -0.25;
[view mouseMoved:native];
- struct yasl_event ev = next(surface);
- assert(ev.pointer.device == YASL_PTR_DEV_PEN && ev.type == YASL_EVENT_POINTER_MOTION && !ev.pointer.pen.pressure);
+ struct yasl_private_event pen = next(surface, YASL_PRIVATE_EVENT_PEN);
+ assert(pen.pen.action == YASL_PRIVATE_PEN_ENTER && pen.pen.device_id == 17 && pen.pen.tool_id == 29);
+ struct yasl_private_event ev = next(surface, YASL_PRIVATE_EVENT_POINTER_MOTION);
+ assert(ev.pointer.device == YASL_PRIVATE_PTR_DEV_PEN && ev.type == YASL_PRIVATE_EVENT_POINTER_MOTION && !ev.pointer.pen.pressure);
assert(ev.pointer.pen.tilt_x == -45 && ev.pointer.pen.tilt_y == -45);
+ pen = next(surface, YASL_PRIVATE_EVENT_PEN);
+ assert(pen.pen.action == YASL_PRIVATE_PEN_MOVE && pen.pen.device_id == 17 && pen.pen.tool_id == 29);
+ assert((pen.pen.axes_valid & (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT)) ==
+ (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT));
+ assert(pen.pen.rotation == 37.5 && pen.pen.tangential == -0.25);
+ assert((pen.pen.axes_valid & (YASL_PRIVATE_PEN_ROTATION | YASL_PRIVATE_PEN_TANGENTIAL)) ==
+ (YASL_PRIVATE_PEN_ROTATION | YASL_PRIVATE_PEN_TANGENTIAL));
sample.type = NSEventTypeLeftMouseDown;
sample.samplePressure = 0.5;
+ sample.buttonMask = NSEventButtonMaskPenTip;
[view mouseDown:native];
- ev = next(surface);
- assert(ev.pointer.device == YASL_PTR_DEV_PEN && ev.type == YASL_EVENT_POINTER_DOWN && ev.pointer.button == YASL_BUTTON_PRIMARY);
+ ev = next(surface, YASL_PRIVATE_EVENT_POINTER_DOWN);
+ assert(ev.pointer.device == YASL_PRIVATE_PTR_DEV_PEN && ev.type == YASL_PRIVATE_EVENT_POINTER_DOWN && ev.pointer.button == YASL_PRIVATE_BUTTON_PRIMARY);
assert(ev.pointer.pen.pressure == 32767);
- struct yasl_vector2i position = ev.pointer.pos;
+ pen = next(surface, YASL_PRIVATE_EVENT_PEN);
+ assert(pen.pen.action == YASL_PRIVATE_PEN_DOWN && pen.pen.in_contact && pen.pen.pressure == 0.5);
+ assert(pen.pen.buttons == YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY));
+ assert(pen.pen.changed_button == YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY));
+ struct yasl_private_vector2i position = ev.pointer.pos;
/* Native tablet events cover stationary pressure changes. */
sample.type = NSEventTypeTabletPoint;
sample.subtype = NSEventSubtypeMouseEvent;
sample.buttonMask = NSEventButtonMaskPenTip;
sample.samplePressure = 1.5;
[view tabletPoint:native];
- ev = next(surface);
- assert(ev.type == YASL_EVENT_POINTER_MOTION && ev.pointer.pen.pressure == 65535);
+ ev = next(surface, YASL_PRIVATE_EVENT_POINTER_MOTION);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_MOTION && ev.pointer.pen.pressure == 65535);
assert(ev.pointer.pos.x == position.x && ev.pointer.pos.y == position.y);
+ pen = next(surface, YASL_PRIVATE_EVENT_PEN);
+ assert(pen.pen.action == YASL_PRIVATE_PEN_MOVE && pen.pen.pressure == 1.0);
+ sample.buttonMask |= NSEventButtonMaskPenLowerSide;
+ [view tabletPoint:native];
+ next(surface, YASL_PRIVATE_EVENT_POINTER_MOTION);
+ pen = next(surface, YASL_PRIVATE_EVENT_PEN);
+ assert(pen.pen.action == YASL_PRIVATE_PEN_BUTTON);
+ assert(pen.pen.buttons == (YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY) |
+ YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_SECONDARY)));
+ assert(pen.pen.changed_button == YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_SECONDARY));
+ sample.buttonMask = NSEventButtonMaskPenTip;
sample.samplePressure = 0; /* Zero does not release a held tip button. */
[view tabletPoint:native];
- assert(next(surface).type == YASL_EVENT_POINTER_MOTION);
+ assert(next(surface, YASL_PRIVATE_EVENT_POINTER_MOTION).type == YASL_PRIVATE_EVENT_POINTER_MOTION);
+ next(surface, YASL_PRIVATE_EVENT_PEN);
sample.buttonMask = 0;
[view tabletPoint:native];
- ev = next(surface);
- assert(ev.type == YASL_EVENT_POINTER_UP && !ev.pointer.pen.pressure && ev.pointer.device == YASL_PTR_DEV_PEN);
+ ev = next(surface, YASL_PRIVATE_EVENT_POINTER_UP);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_UP && !ev.pointer.pen.pressure && ev.pointer.device == YASL_PRIVATE_PTR_DEV_PEN);
+ pen = next(surface, YASL_PRIVATE_EVENT_PEN);
+ assert(pen.pen.action == YASL_PRIVATE_PEN_UP && !pen.pen.in_contact && pen.pen.pressure == 0.0);
sample.type = NSEventTypeTabletProximity;
sample.enteringProximity = NO;
[view tabletProximity:native];
- assert(next(surface).type == YASL_EVENT_POINTER_LEAVE);
+ assert(next(surface, YASL_PRIVATE_EVENT_POINTER_LEAVE).type == YASL_PRIVATE_EVENT_POINTER_LEAVE);
+ assert(next(surface, YASL_PRIVATE_EVENT_PEN).pen.action == YASL_PRIVATE_PEN_LEAVE);
sample.enteringProximity = YES;
sample.tool = NSPointingDeviceTypeEraser;
[view tabletProximity:native];
@@ -104,20 +144,25 @@ int main(void)
sample.samplePressure = 0.75;
sample.buttonMask = NSEventButtonMaskPenTip;
[view tabletPoint:native];
- ev = next(surface);
- assert(ev.pointer.device == YASL_PTR_DEV_ERASER && ev.type == YASL_EVENT_POINTER_DOWN && ev.pointer.pen.pressure > 0);
+ pen = next(surface, YASL_PRIVATE_EVENT_PEN);
+ assert(pen.pen.action == YASL_PRIVATE_PEN_ENTER && pen.pen.tool == 2);
+ ev = next(surface, YASL_PRIVATE_EVENT_POINTER_DOWN);
+ assert(ev.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER && ev.type == YASL_PRIVATE_EVENT_POINTER_DOWN && ev.pointer.pen.pressure > 0);
+ pen = next(surface, YASL_PRIVATE_EVENT_PEN);
+ assert(pen.pen.action == YASL_PRIVATE_PEN_DOWN && pen.pen.tool == 2 && pen.pen.in_contact);
sample.type = NSEventTypeTabletProximity;
sample.enteringProximity = NO;
[view tabletProximity:native];
- ev = next(surface);
- assert(ev.pointer.device == YASL_PTR_DEV_ERASER && ev.type == YASL_EVENT_POINTER_LEAVE && !ev.pointer.pen.pressure);
+ ev = next(surface, YASL_PRIVATE_EVENT_POINTER_LEAVE);
+ assert(ev.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER && ev.type == YASL_PRIVATE_EVENT_POINTER_LEAVE && !ev.pointer.pen.pressure);
+ assert(next(surface, YASL_PRIVATE_EVENT_PEN).pen.action == YASL_PRIVATE_PEN_LEAVE);
sample.type = NSEventTypeMouseMoved;
[view mouseMoved:native];
- ev = next(surface);
- assert(ev.pointer.device == YASL_PTR_DEV_MOUSE && !ev.pointer.pen.pressure);
+ ev = next(surface, YASL_PRIVATE_EVENT_POINTER_MOTION);
+ assert(ev.pointer.device == YASL_PRIVATE_PTR_DEV_MOUSE && !ev.pointer.pen.pressure);
[sample release];
- yasl_surface_destroy(surface);
- yasl_display_close(display);
+ yasl_private_surface_destroy(surface);
+ yasl_private_display_close(display);
}
return 0;
}
diff --git a/tests/common/test_app.c b/tests/common/test_app.c
deleted file mode 100644
index 214d8f2..0000000
--- a/tests/common/test_app.c
+++ /dev/null
@@ -1,204 +0,0 @@
-#define _DEFAULT_SOURCE
-#include "yasl/app.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <unistd.h>
-
-struct test_state {
- struct yasl_display *display;
- struct yasl_surface *surface;
- bool init_called;
- bool frame_called;
- bool user_event_called;
- bool timer_called;
- bool quit_called;
- int frame_count;
- yasl_timer_id timer_id;
- pthread_t worker;
- uint64_t target_id;
-};
-
-static void *worker_thread(void *arg)
-{
- struct test_state *state = arg;
- usleep(50000); /* 50ms */
- bool ok = yasl_display_post(state->display, &(struct yasl_message){
- .target_id = state->target_id, .code = 42, .payload = (void *)0x1234});
- assert(ok);
- return NULL;
-}
-
-static uint64_t on_timer(void *ud, yasl_timer_id id, uint64_t interval_ns)
-{
- (void)id;
- (void)interval_ns;
- struct test_state *state = ud;
- state->timer_called = true;
- yasl_surface_request_frame(state->surface);
- return 0; /* one-shot */
-}
-
-static enum yasl_app_result on_init(void *ud, struct yasl_display *d)
-{
- struct test_state *state = ud;
- state->display = d;
- state->init_called = true;
-
- struct yasl_window_spec spec = {
- .title = "App Runner Test",
- .size = {320, 240},
- };
- state->surface = yasl_surface_create_toplevel(d, &spec);
- assert(state->surface != NULL);
-
- yasl_surface_set_pacing(state->surface, YASL_PACING_ON_DEMAND);
- yasl_surface_set_pointer_mode(state->surface, YASL_POINTER_RELATIVE);
- yasl_surface_set_pointer_mode(state->surface, YASL_POINTER_NORMAL);
-
- state->timer_id = yasl_loop_add_timer(yasl_display_loop(d), YASL_MS_TO_NS(20), on_timer, state);
- assert(state->timer_id > 0);
-
- state->target_id = yasl_surface_id(state->surface);
- int err = pthread_create(&state->worker, NULL, worker_thread, state);
- assert(err == 0);
-
-
- yasl_surface_request_frame(state->surface);
- return YASL_APP_CONTINUE;
-}
-
-static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev)
-{
- struct test_state *state = ud;
- if (ev->type == YASL_EVENT_USER) {
- assert(ev->user.code == 42);
- assert(ev->user.payload == (void *)0x1234);
- state->user_event_called = true;
- yasl_surface_request_frame(state->surface);
- } else if (ev->type == YASL_EVENT_WINDOW_CLOSE) {
- return YASL_APP_QUIT;
- }
- return YASL_APP_CONTINUE;
-}
-
-static void on_frame(void *ud, struct yasl_surface *s, struct yasl_frame *ctx)
-{
- struct test_state *state = ud;
- assert(s == state->surface);
- assert(ctx->surface == s);
- state->frame_called = true;
- state->frame_count++;
-
- if (ctx->target.pixels) {
- size_t count = (size_t)ctx->target.size.x * ctx->target.size.y;
- for (size_t i = 0; i < count; i++) {
- ctx->target.pixels[i] = 0xff204060;
- }
- bool ok = yasl_frame_present(ctx, NULL, 0);
- assert(ok);
- }
-
- if (state->user_event_called) {
- yasl_surface_destroy(state->surface);
- }
-}
-
-static void on_quit(void *ud, struct yasl_display *d)
-{
- (void)d;
- struct test_state *state = ud;
- assert(!pthread_join(state->worker, NULL));
- state->quit_called = true;
-}
-
-static void present_once(void *ud, struct yasl_surface *s, struct yasl_frame *ctx)
-{
- (*(int *)ud)++;
- if (ctx->target.pixels)
- assert(yasl_frame_present(ctx, NULL, 0));
- yasl_surface_destroy(s);
-}
-
-/* desc.window replaces init: the runner creates the toplevel and draws it. */
-static void test_window_without_init(void)
-{
- int frames = 0;
- struct yasl_window_spec window = {.title = "App Runner Window", .size = {160, 120}};
- int res = yasl_run(&(struct yasl_app_desc){
- .app_id = "org.yasl.test.app.window",
- .userdata = &frames,
- .window = &window,
- .frame = present_once,
- });
- assert(res == 0);
- assert(frames == 1);
-}
-
-
-static enum yasl_app_result fail_init(void *ud, struct yasl_display *d)
-{
- (void)ud; (void)d;
- return YASL_APP_ERROR;
-}
-static void count_quit(void *ud, struct yasl_display *d)
-{
- (void)d;
- (*(int *)ud)++;
-}
-static enum yasl_app_result inspect_initial_window(void *ud, struct yasl_display *d)
-{
- struct yasl_surface *s = yasl_app_window(d);
- assert(s && yasl_surface_userdata(s) == ud);
- errno = 0;
- assert(!yasl_surface_set_callbacks(s, NULL, NULL) && errno == EBUSY);
- return YASL_APP_CONTINUE;
-}
-static void fail_frame(void *ud, struct yasl_surface *s, struct yasl_frame *frame)
-{
- (void)ud;
- assert(frame->surface == s && frame->target.stride_bytes == frame->target.size.x * 4);
- yasl_app_exit(yasl_surface_display(s), YASL_APP_ERROR);
-}
-static void test_error_lifecycle(void)
-{
- int quits = 0;
- assert(yasl_run(&(struct yasl_app_desc){.userdata = &quits,
- .init = fail_init, .quit = count_quit}) < 0);
- assert(quits == 1 && errno == EIO);
- struct yasl_window_spec window = {.size_logical = {160, 120}, .userdata = &quits};
- assert(yasl_run(&(struct yasl_app_desc){.userdata = &quits, .window = &window,
- .init = inspect_initial_window, .frame = fail_frame, .quit = count_quit}) < 0);
- assert(quits == 2 && errno == EIO);
-}
-
-int main(void)
-{
- struct test_state state = {0};
- struct yasl_app_desc desc = {
- .app_id = "org.yasl.test.app",
- .backend = YASL_BACKEND_AUTO,
- .userdata = &state,
- .init = on_init,
- .event = on_event,
- .frame = on_frame,
- .quit = on_quit,
- };
-
- int res = yasl_run(&desc);
- if (res < 0 && !state.init_called &&
- (errno == EPERM || errno == EACCES || errno == ENOENT ||
- errno == ECONNREFUSED || errno == ENODEV)) return 77;
- assert(res == 0);
- assert(state.init_called);
- assert(state.frame_called);
- assert(state.user_event_called);
- assert(state.timer_called);
- assert(state.quit_called);
- test_window_without_init();
- test_error_lifecycle();
- printf("App runner tests passed!\n");
- return 0;
-}
diff --git a/tests/common/test_backends.c b/tests/common/test_backends.c
index 30f9b67..7eba57f 100644
--- a/tests/common/test_backends.c
+++ b/tests/common/test_backends.c
@@ -1,13 +1,13 @@
#define _POSIX_C_SOURCE 200809L
-#include "yasl/native.h"
+#include "common/private_native.h"
#if YASL_HAS_WAYLAND
-#include "yasl/native/wayland.h"
+#include "common/private_native_wayland.h"
#endif
#if YASL_HAS_X11
-#include "yasl/native/x11.h"
+#include "common/private_native_x11.h"
#endif
-#include "yasl/loop.h"
-#include "yasl/yasl.h"
+#include "common/private_loop.h"
+#include "common/types.h"
#include <X11/Xatom.h>
#include <X11/Xlib.h>
@@ -24,14 +24,14 @@
struct window {
struct yasl_surface *surface;
- struct yasl_display *display;
+ struct yasl_private_display *display;
int draws, configures, closes;
bool destroy_on_draw, destroy_on_configure;
};
struct pump {
- struct yasl_loop *loop;
- struct yasl_display *displays[2];
+ struct yasl_private_loop *loop;
+ struct yasl_private_display *displays[2];
int ticks;
};
@@ -43,76 +43,76 @@ static int tick(int fd, short events, void *ud)
struct pump *p = ud;
for (int i = 0; i < 2; i++) {
if (p->displays[i]) {
- yasl_display_dispatch(p->displays[i], 0);
- yasl_display_flush(p->displays[i]);
+ yasl_private_display_dispatch(p->displays[i], 0);
+ yasl_private_display_flush(p->displays[i]);
}
}
- if (--p->ticks == 0) yasl_loop_quit(p->loop);
+ if (--p->ticks == 0) yasl_private_loop_quit(p->loop);
return 0;
}
-static void pump(struct yasl_loop *loop, struct yasl_display *a, struct yasl_display *b)
+static void pump(struct yasl_private_loop *loop, struct yasl_private_display *a, struct yasl_private_display *b)
{
int timer = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
assert(timer >= 0);
struct pump p = {.loop = loop, .displays = {a, b}, .ticks = 20};
struct itimerspec spec = {.it_value.tv_nsec = 10000000, .it_interval.tv_nsec = 10000000};
assert(timerfd_settime(timer, 0, &spec, NULL) == 0);
- int watch = yasl_loop_add_fd(loop, timer, POLLIN, tick, &p);
+ int watch = yasl_private_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);
+ assert(yasl_private_loop_run(loop) == 0);
+ assert(yasl_private_loop_del_fd(loop, watch) == 0);
close(timer);
}
-static void draw(struct yasl_surface *s, struct yasl_frame *frame, void *ud)
+static void draw(struct yasl_surface *s, struct yasl_private_frame *frame, void *ud)
{
struct window *w = ud;
assert(s == w->surface);
assert(frame->surface == s);
- assert(yasl_surface_display(s) == w->display);
- struct yasl_vector2i size = yasl_surface_geometry(s).size;
+ assert(yasl_private_surface_display(s) == w->display);
+ struct yasl_private_vector2i size = yasl_private_surface_geometry(s).size;
assert(size.x == frame->target.size.x && size.y == frame->target.size.y);
assert(frame->target.pixels);
- struct yasl_native_frame native = {0};
- assert(!yasl_surface_get_native_frame(s, &frame->target, &native));
+ struct yasl_private_native_frame native = {0};
+ assert(!yasl_private_surface_get_native_frame(s, &frame->target, &native));
for (int i = 0; i < size.x * size.y; i++) frame->target.pixels[i] = 0xff4080c0;
- struct yasl_damage_list damage = {.n = YASL_DAMAGE_MAX + 1};
- assert(!yasl_surface_present(s, &frame->target, &damage));
+ struct yasl_private_damage_list damage = {.n = YASL_PRIVATE_DAMAGE_MAX + 1};
+ assert(!yasl_private_surface_present(s, &frame->target, &damage));
damage.n = -2;
- assert(!yasl_surface_present(s, &frame->target, &damage));
+ assert(!yasl_private_surface_present(s, &frame->target, &damage));
damage.n = 1;
damage.rects[0].width = -1;
- assert(!yasl_surface_present(s, &frame->target, &damage));
+ assert(!yasl_private_surface_present(s, &frame->target, &damage));
damage.n = -1;
- struct yasl_framebuffer invalid = frame->target;
+ struct yasl_private_framebuffer invalid = frame->target;
invalid.size.x++;
- assert(!yasl_surface_present(s, &invalid, &damage));
+ assert(!yasl_private_surface_present(s, &invalid, &damage));
invalid = frame->target;
invalid.generation--;
- assert(!yasl_surface_present(s, &invalid, &damage));
+ assert(!yasl_private_surface_present(s, &invalid, &damage));
invalid = frame->target;
invalid.pixels = (uint32_t *)(uintptr_t)1;
- assert(!yasl_surface_present(s, &invalid, &damage));
- assert(!yasl_surface_present(s, &frame->target, NULL));
- assert(yasl_surface_present(s, &frame->target, &damage));
- assert(!yasl_surface_present(s, &frame->target, &damage));
+ assert(!yasl_private_surface_present(s, &invalid, &damage));
+ assert(!yasl_private_surface_present(s, &frame->target, NULL));
+ assert(yasl_private_surface_present(s, &frame->target, &damage));
+ assert(!yasl_private_surface_present(s, &frame->target, &damage));
w->draws++;
- if (w->destroy_on_draw) { yasl_surface_destroy(s); w->surface = NULL; }
+ if (w->destroy_on_draw) { yasl_private_surface_destroy(s); w->surface = NULL; }
}
-static void configure(struct yasl_surface *s, struct yasl_geometry geometry, void *ud)
+static void configure(struct yasl_surface *s, struct yasl_private_geometry geometry, void *ud)
{
struct window *w = ud;
assert(w->surface == s);
- struct yasl_vector2i size = yasl_surface_geometry(s).size;
+ struct yasl_private_vector2i size = yasl_private_surface_geometry(s).size;
assert(geometry.size.x == size.x && geometry.size.y == size.y);
- assert(geometry.scale_120 == yasl_surface_geometry(s).scale_120);
+ assert(geometry.scale_120 == yasl_private_surface_geometry(s).scale_120);
w->configures++;
- if (w->destroy_on_configure) { yasl_surface_destroy(s); w->surface = NULL; }
+ if (w->destroy_on_configure) { yasl_private_surface_destroy(s); w->surface = NULL; }
}
-static void draw_without_present(struct yasl_surface *s, struct yasl_frame *frame, void *ud)
+static void draw_without_present(struct yasl_surface *s, struct yasl_private_frame *frame, void *ud)
{
(void)s;
struct window *w = ud;
@@ -126,62 +126,62 @@ static void close_window(struct yasl_surface *s, void *ud)
struct window *w = ud;
assert(s == w->surface);
w->closes++;
- yasl_surface_destroy(s);
+ yasl_private_surface_destroy(s);
w->surface = NULL;
}
-static const struct yasl_window_cbs callbacks = {
+static const struct yasl_private_window_cbs callbacks = {
.draw = draw, .configure = configure, .close = close_window,
};
static void attach(struct window *w)
{
- struct yasl_window_cbs temporary = callbacks;
- yasl_surface_set_callbacks(w->surface, &temporary, w);
+ struct yasl_private_window_cbs temporary = callbacks;
+ yasl_private_surface_set_callbacks(w->surface, &temporary, w);
temporary.draw = NULL;
temporary.configure = NULL;
temporary.close = NULL;
}
-static struct window create(struct yasl_display *d)
+static struct window create(struct yasl_private_display *d)
{
- struct yasl_window_spec spec = {.title = "Backend test", .size = {160, 100}};
- struct yasl_surface *s = yasl_surface_create_toplevel(d, &spec);
+ struct yasl_private_window_spec spec = {.title = "Backend test", .size = {160, 100}};
+ struct yasl_surface *s = yasl_private_surface_create_toplevel(d, &spec);
assert(s);
- assert(yasl_display_get_native_handle(d));
- assert(yasl_surface_get_native_handle(s));
- assert(yasl_surface_display(s) == d);
- assert(yasl_surface_timing(s).source == YASL_TIMING_UNKNOWN);
+ assert(yasl_private_display_get_native_handle(d));
+ assert(yasl_private_surface_get_native_handle(s));
+ assert(yasl_private_surface_display(s) == d);
+ assert(yasl_private_surface_timing(s).source == YASL_PRIVATE_TIMING_UNKNOWN);
return (struct window){.surface = s, .display = d};
}
/* The last presented frame reads back until a later draw replaces it. */
-static void test_read_pixels(struct yasl_loop *loop, struct yasl_display *d, struct window *w)
+static void test_read_pixels(struct yasl_private_loop *loop, struct yasl_private_display *d, struct window *w)
{
- struct yasl_vector2i size = {-1, -1};
- assert(!yasl_surface_read_pixels(w->surface, NULL, 0, NULL));
- assert(!yasl_surface_read_pixels(NULL, NULL, 0, &size) && !size.x && !size.y);
- assert(!yasl_surface_read_pixels(w->surface, NULL, 0, &size));
- struct yasl_vector2i extent = yasl_surface_geometry(w->surface).size;
+ struct yasl_private_vector2i size = {-1, -1};
+ assert(!yasl_private_surface_read_pixels(w->surface, NULL, 0, NULL));
+ assert(!yasl_private_surface_read_pixels(NULL, NULL, 0, &size) && !size.x && !size.y);
+ assert(!yasl_private_surface_read_pixels(w->surface, NULL, 0, &size));
+ struct yasl_private_vector2i extent = yasl_private_surface_geometry(w->surface).size;
assert(size.x == extent.x && size.y == extent.y);
size_t count = (size_t)size.x * (size_t)size.y;
uint32_t *pixels = calloc(count, sizeof(*pixels));
assert(pixels);
- assert(!yasl_surface_read_pixels(w->surface, pixels, count - 1, &size));
+ assert(!yasl_private_surface_read_pixels(w->surface, pixels, count - 1, &size));
assert(!pixels[0]);
- assert(yasl_surface_read_pixels(w->surface, pixels, count, &size));
+ assert(yasl_private_surface_read_pixels(w->surface, pixels, count, &size));
for (size_t i = 0; i < count; i++) assert(pixels[i] == 0xff4080c0);
/* Draws that never present must not show through. X11 reuses its one
* target; Wayland keeps the presented buffer while the compositor holds it. */
- struct yasl_window_cbs skip = callbacks;
+ struct yasl_private_window_cbs skip = callbacks;
skip.draw = draw_without_present;
- yasl_surface_set_callbacks(w->surface, &skip, w);
+ yasl_private_surface_set_callbacks(w->surface, &skip, w);
for (int i = 0; i < 4; i++) {
- yasl_surface_schedule_redraw(w->surface);
+ yasl_private_surface_schedule_redraw(w->surface);
pump(loop, d, NULL);
- if (yasl_surface_read_pixels(w->surface, pixels, count, &size)) {
- assert(yasl_display_backend(d) != YASL_BACKEND_X11);
+ if (yasl_private_surface_read_pixels(w->surface, pixels, count, &size)) {
+ assert(yasl_private_display_backend(d) != YASL_PRIVATE_BACKEND_X11);
for (size_t j = 0; j < count; j++) assert(pixels[j] == 0xff4080c0);
} else {
assert(!size.x && !size.y);
@@ -189,47 +189,47 @@ static void test_read_pixels(struct yasl_loop *loop, struct yasl_display *d, str
}
attach(w);
- yasl_surface_schedule_redraw(w->surface);
+ yasl_private_surface_schedule_redraw(w->surface);
pump(loop, d, NULL);
memset(pixels, 0, count * sizeof(*pixels));
- assert(yasl_surface_read_pixels(w->surface, pixels, count, &size));
+ assert(yasl_private_surface_read_pixels(w->surface, pixels, count, &size));
assert(pixels[0] == 0xff4080c0 && pixels[count - 1] == 0xff4080c0);
free(pixels);
}
-static void test_window_spec(struct yasl_loop *loop, struct yasl_display *d)
+static void test_window_spec(struct yasl_private_loop *loop, struct yasl_private_display *d)
{
struct window w = {.display = d, .destroy_on_draw = true};
{
- struct yasl_window_cbs temporary = callbacks;
- struct yasl_window_spec spec = {
+ struct yasl_private_window_cbs temporary = callbacks;
+ struct yasl_private_window_spec spec = {
.title = "Callbacks at creation", .size = {160, 100},
.cbs = &temporary, .ud = &w,
};
- w.surface = yasl_surface_create_toplevel(d, &spec);
+ w.surface = yasl_private_surface_create_toplevel(d, &spec);
assert(w.surface);
assert(!w.draws && !w.configures && !w.closes);
/* Neither the spec nor its callback table needs to outlive creation. */
memset(&temporary, 0, sizeof(temporary));
memset(&spec, 0, sizeof(spec));
}
- yasl_surface_schedule_redraw(w.surface);
+ yasl_private_surface_schedule_redraw(w.surface);
pump(loop, d, NULL);
assert(w.draws == 1 && !w.surface);
}
-static void x11_behavior(struct yasl_loop *loop, struct yasl_display *d, struct window *w)
+static void x11_behavior(struct yasl_private_loop *loop, struct yasl_private_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);
+ Display *native = yasl_private_display_get_native_handle(d);
+ Window window = (Window)(uintptr_t)yasl_private_surface_get_native_handle(w->surface);
assert(native && window);
- assert(yasl_x11_display_get_display(d) == native);
- assert(yasl_x11_surface_get_window(w->surface) == (uintptr_t)window);
+ assert(yasl_private_x11_display_get_display(d) == native);
+ assert(yasl_private_x11_surface_get_window(w->surface) == (uintptr_t)window);
XSync(native, False);
XImage *image = XGetImage(native, window, 0, 0, 1, 1, AllPlanes, ZPixmap);
assert(image && (XGetPixel(image, 0, 0) & 0xffffff) == 0x4080c0);
XDestroyImage(image);
- yasl_surface_set_title(w->surface, "Updated title");
+ yasl_private_surface_set_title(w->surface, "Updated title");
char *title = NULL;
assert(XFetchName(native, window, &title));
assert(strcmp(title, "Updated title") == 0);
@@ -238,19 +238,19 @@ static void x11_behavior(struct yasl_loop *loop, struct yasl_display *d, struct
XFlush(native);
pump(loop, d, NULL);
assert(w->configures > 0);
- struct yasl_vector2i size = yasl_surface_geometry(w->surface).size;
+ struct yasl_private_vector2i size = yasl_private_surface_geometry(w->surface).size;
assert(size.x == 200 && size.y == 120);
/* A server-generated motion verifies the XI2 subscription and cookie
* dispatch path, including source-device lookup, rather than SendEvent. */
- struct yasl_event pointer_event;
- while (yasl_surface_poll_event(w->surface, &pointer_event)) {}
+ struct yasl_private_event pointer_event;
+ while (yasl_private_surface_poll_event(w->surface, &pointer_event)) {}
XWarpPointer(native, None, window, 0, 0, 0, 0, 80, 50);
XFlush(native);
pump(loop, d, NULL);
bool saw_motion = false;
- while (yasl_surface_poll_event(w->surface, &pointer_event)) {
- if (pointer_event.type == YASL_EVENT_POINTER_MOTION) {
- assert(pointer_event.pointer.device == YASL_PTR_DEV_MOUSE);
+ while (yasl_private_surface_poll_event(w->surface, &pointer_event)) {
+ if (pointer_event.type == YASL_PRIVATE_EVENT_POINTER_MOTION) {
+ assert(pointer_event.pointer.device == YASL_PRIVATE_PTR_DEV_MOUSE);
assert(!pointer_event.pointer.pen.pressure);
if (pointer_event.pointer.pos.x == 80 && pointer_event.pointer.pos.y == 50)
saw_motion = true;
@@ -263,25 +263,25 @@ static void x11_behavior(struct yasl_loop *loop, struct yasl_display *d, struct
.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);
+ yasl_private_display_copy(d, YASL_PRIVATE_CLIPBOARD, "clipboard text");
+ yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, w->surface);
pump(loop, d, NULL);
bool saw_key = false, saw_paste = false, saw_text = false;
- struct yasl_event event;
- while (yasl_surface_poll_event(w->surface, &event)) {
+ struct yasl_private_event event;
+ while (yasl_private_surface_poll_event(w->surface, &event)) {
assert(event.surface == w->surface);
- if (event.type == YASL_EVENT_KEY) {
+ if (event.type == YASL_PRIVATE_EVENT_KEY) {
assert(event.key.codepoint == 'a');
assert(event.key.shifted == 'A');
assert(event.key.native_code == key.xkey.keycode);
saw_key = event.key.key == YASL_KEY_A;
}
- if (event.type == YASL_EVENT_PASTE) {
+ if (event.type == YASL_PRIVATE_EVENT_PASTE) {
assert(event.text.len == strlen("clipboard text"));
assert(memcmp(event.text.data, "clipboard text", event.text.len) == 0);
saw_paste = true;
}
- if (event.type == YASL_EVENT_TEXT) {
+ if (event.type == YASL_PRIVATE_EVENT_TEXT) {
assert(event.text.len == 1 && event.text.data[0] == 'a');
saw_text = true;
}
@@ -297,12 +297,12 @@ static void x11_behavior(struct yasl_loop *loop, struct yasl_display *d, struct
XFlush(native);
pump(loop, d, NULL);
int key_count = 0, text_count = 0;
- while (yasl_surface_poll_event(w->surface, &event)) {
- if (event.type == YASL_EVENT_KEY) {
+ while (yasl_private_surface_poll_event(w->surface, &event)) {
+ if (event.type == YASL_PRIVATE_EVENT_KEY) {
assert(event.key.key == YASL_KEY_A);
assert(event.key.action == (key_count ? YASL_KEY_RELEASE : YASL_KEY_PRESS));
key_count++;
- } else if (event.type == YASL_EVENT_TEXT) {
+ } else if (event.type == YASL_PRIVATE_EVENT_TEXT) {
assert(event.text.len == 1 && event.text.data[0] == 'A');
text_count++;
}
@@ -313,10 +313,10 @@ static void x11_behavior(struct yasl_loop *loop, struct yasl_display *d, struct
XSendEvent(native, window, False, KeyPressMask, &key);
XFlush(native);
pump(loop, d, NULL);
- assert(yasl_surface_poll_event(w->surface, &event));
- assert(event.type == YASL_EVENT_KEY && event.key.key == YASL_KEY_A);
- assert(event.key.mods & YASL_MOD_CTRL);
- assert(!yasl_surface_poll_event(w->surface, &event));
+ assert(yasl_private_surface_poll_event(w->surface, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_KEY && event.key.key == YASL_KEY_A);
+ assert(event.key.mods & YASL_PRIVATE_MOD_CTRL);
+ assert(!yasl_private_surface_poll_event(w->surface, &event));
int symbols_per_key = 0;
KeySym *saved_symbols = XGetKeyboardMapping(native, key.xkey.keycode, 1, &symbols_per_key);
@@ -333,11 +333,11 @@ static void x11_behavior(struct yasl_loop *loop, struct yasl_display *d, struct
XFlush(native);
pump(loop, d, NULL);
saw_key = saw_text = false;
- while (yasl_surface_poll_event(w->surface, &event)) {
- if (event.type == YASL_EVENT_KEY) {
+ while (yasl_private_surface_poll_event(w->surface, &event)) {
+ if (event.type == YASL_PRIVATE_EVENT_KEY) {
assert(event.key.key == YASL_KEY_A && event.key.codepoint == 'q');
saw_key = true;
- } else if (event.type == YASL_EVENT_TEXT) {
+ } else if (event.type == YASL_PRIVATE_EVENT_TEXT) {
assert(event.text.len == 1 && event.text.data[0] == 'q');
saw_text = true;
}
@@ -359,67 +359,67 @@ static void x11_behavior(struct yasl_loop *loop, struct yasl_display *d, struct
XSendEvent(native, window, False, NoEventMask, &wheel);
XFlush(native);
pump(loop, d, NULL);
- assert(yasl_surface_poll_event(w->surface, &event));
- assert(event.type == YASL_EVENT_POINTER_SCROLL);
+ assert(yasl_private_surface_poll_event(w->surface, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_POINTER_SCROLL);
assert(event.pointer.scroll.x == (button == 6 ? -1 : button == 7 ? 1 : 0));
assert(event.pointer.scroll.y == (button == 4 ? -1 : button == 5 ? 1 : 0));
- assert(!yasl_surface_poll_event(w->surface, &event));
+ assert(!yasl_private_surface_poll_event(w->surface, &event));
}
- assert(yasl_surface_timing(w->surface).source == YASL_TIMING_SUBMISSION);
- assert(!(yasl_display_caps(d) & YASL_CAP_FRAME_PACING));
+ assert(yasl_private_surface_timing(w->surface).source == YASL_PRIVATE_TIMING_SUBMISSION);
+ assert(!(yasl_private_display_caps(d) & YASL_PRIVATE_CAP_FRAME_PACING));
/* Reuse caller storage, replace the selection, and exceed the old queue
* cap before polling: each paste must retain its own original contents. */
for (int i = 0; i < 300; i++) {
char text[32];
snprintf(text, sizeof(text), "owned-%d", i);
- yasl_display_copy(d, YASL_CLIPBOARD, text);
- yasl_display_paste(d, YASL_CLIPBOARD, w->surface);
+ yasl_private_display_copy(d, YASL_PRIVATE_CLIPBOARD, text);
+ yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, w->surface);
memset(text, 'x', sizeof(text));
}
for (int i = 0; i < 300; i++) {
char text[32];
snprintf(text, sizeof(text), "owned-%d", i);
- assert(yasl_surface_poll_event(w->surface, &event));
- assert(event.type == YASL_EVENT_PASTE && strcmp(event.text.data, text) == 0);
+ assert(yasl_private_surface_poll_event(w->surface, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_PASTE && strcmp(event.text.data, text) == 0);
}
const char *last = event.text.data;
- assert(!yasl_surface_poll_event(w->surface, &event));
- yasl_display_copy(d, YASL_CLIPBOARD, "changed");
+ assert(!yasl_private_surface_poll_event(w->surface, &event));
+ yasl_private_display_copy(d, YASL_PRIVATE_CLIPBOARD, "changed");
pump(loop, d, NULL);
assert(strcmp(last, "owned-299") == 0);
- yasl_display_copy(d, YASL_CLIPBOARD, "");
- yasl_display_paste(d, YASL_CLIPBOARD, w->surface);
- assert(yasl_surface_poll_event(w->surface, &event));
- assert(event.type == YASL_EVENT_PASTE && !event.text.len && !event.text.data[0]);
+ yasl_private_display_copy(d, YASL_PRIVATE_CLIPBOARD, "");
+ yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, w->surface);
+ assert(yasl_private_surface_poll_event(w->surface, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_PASTE && !event.text.len && !event.text.data[0]);
/* Concurrent external conversions must not share a mutable target slot. */
- struct yasl_display_spec ds = {.backend = YASL_BACKEND_X11};
- struct yasl_display *owner = yasl_display_open(loop, &ds);
+ struct yasl_private_display_spec ds = {.backend = YASL_PRIVATE_BACKEND_X11};
+ struct yasl_private_display *owner = yasl_private_display_open(loop, &ds);
assert(owner);
- yasl_display_copy(owner, YASL_CLIPBOARD, "external");
+ yasl_private_display_copy(owner, YASL_PRIVATE_CLIPBOARD, "external");
struct window other = create(d), cancelled = create(d);
- yasl_display_paste(d, YASL_CLIPBOARD, w->surface);
- yasl_display_paste(d, YASL_CLIPBOARD, other.surface);
- yasl_display_paste(d, YASL_CLIPBOARD, cancelled.surface);
- yasl_surface_destroy(cancelled.surface);
+ yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, w->surface);
+ yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, other.surface);
+ yasl_private_display_paste(d, YASL_PRIVATE_CLIPBOARD, cancelled.surface);
+ yasl_private_surface_destroy(cancelled.surface);
pump(loop, d, owner);
int first_pastes = 0, second_pastes = 0;
- while (yasl_surface_poll_event(w->surface, &event)) {
- if (event.type == YASL_EVENT_PASTE) {
+ while (yasl_private_surface_poll_event(w->surface, &event)) {
+ if (event.type == YASL_PRIVATE_EVENT_PASTE) {
assert(strcmp(event.text.data, "external") == 0);
first_pastes++;
}
}
- while (yasl_surface_poll_event(other.surface, &event)) {
- if (event.type == YASL_EVENT_PASTE) {
+ while (yasl_private_surface_poll_event(other.surface, &event)) {
+ if (event.type == YASL_PRIVATE_EVENT_PASTE) {
assert(strcmp(event.text.data, "external") == 0);
second_pastes++;
}
}
assert(first_pastes == 1 && second_pastes == 1);
- yasl_surface_destroy(other.surface);
- yasl_display_close(owner);
+ yasl_private_surface_destroy(other.surface);
+ yasl_private_display_close(owner);
key.xclient = (XClientMessageEvent){.type = ClientMessage, .display = native,
.window = window, .message_type = XInternAtom(native, "WM_PROTOCOLS", False),
.format = 32, .data.l = {(long)XInternAtom(native, "WM_DELETE_WINDOW", False)}};
@@ -432,64 +432,64 @@ static void x11_behavior(struct yasl_loop *loop, struct yasl_display *d, struct
int main(int argc, char **argv)
{
assert(argc >= 2);
- struct yasl_loop *loop = yasl_loop_create();
+ struct yasl_private_loop *loop = yasl_private_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));
+ struct yasl_private_display_spec spec = {.backend = (enum yasl_private_backend)atoi(argv[2])};
+ assert(!yasl_private_display_open(loop, &spec));
} else if (strcmp(argv[1], "mixed") == 0) {
- struct yasl_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);
+ struct yasl_private_display_spec spec = {.backend = YASL_PRIVATE_BACKEND_WAYLAND};
+ struct yasl_private_display *a = yasl_private_display_open(loop, &spec);
+ spec.backend = YASL_PRIVATE_BACKEND_X11;
+ struct yasl_private_display *b = yasl_private_display_open(loop, &spec);
assert(a && b);
test_window_spec(loop, a);
test_window_spec(loop, b);
struct window wa = create(a), wb = create(b);
#if YASL_HAS_WAYLAND
- assert(yasl_wayland_display_get_wl_display(a) != NULL);
- assert(yasl_wayland_surface_get_wl_surface(wa.surface) != NULL);
- assert(!yasl_x11_display_get_display(a));
+ assert(yasl_private_wayland_display_get_wl_display(a) != NULL);
+ assert(yasl_private_wayland_surface_get_wl_surface(wa.surface) != NULL);
+ assert(!yasl_private_x11_display_get_display(a));
#endif
#if YASL_HAS_X11
- assert(yasl_x11_display_get_display(b) != NULL);
- assert(yasl_x11_surface_get_window(wb.surface) != 0);
- assert(!yasl_wayland_display_get_wl_display(b));
+ assert(yasl_private_x11_display_get_display(b) != NULL);
+ assert(yasl_private_x11_surface_get_window(wb.surface) != 0);
+ assert(!yasl_private_wayland_display_get_wl_display(b));
#endif
attach(&wa);
attach(&wb);
pump(loop, a, b);
assert(wa.draws && wb.draws);
- yasl_surface_destroy(wa.surface);
- yasl_surface_destroy(wb.surface);
- yasl_display_close(a);
- yasl_display_close(b);
+ yasl_private_surface_destroy(wa.surface);
+ yasl_private_surface_destroy(wb.surface);
+ yasl_private_display_close(a);
+ yasl_private_display_close(b);
} else {
assert(argc == 4);
- struct yasl_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);
+ struct yasl_private_display_spec spec = {.backend = (enum yasl_private_backend)atoi(argv[2])};
+ struct yasl_private_display *d = strcmp(argv[1], "null") == 0 ?
+ yasl_private_display_open(loop, NULL) : yasl_private_display_open(loop, &spec);
assert(d);
- assert(yasl_display_backend(d) == (enum yasl_backend)atoi(argv[3]));
+ assert(yasl_private_display_backend(d) == (enum yasl_private_backend)atoi(argv[3]));
struct window w = create(d);
pump(loop, d, NULL);
assert(w.draws == 0);
attach(&w);
- yasl_surface_schedule_redraw(w.surface);
+ yasl_private_surface_schedule_redraw(w.surface);
pump(loop, d, NULL);
assert(w.draws > 0);
test_read_pixels(loop, d, &w);
- if (yasl_display_backend(d) == YASL_BACKEND_X11)
+ if (yasl_private_display_backend(d) == YASL_PRIVATE_BACKEND_X11)
x11_behavior(loop, d, &w);
- if (w.surface) yasl_surface_destroy(w.surface);
+ if (w.surface) yasl_private_surface_destroy(w.surface);
w = create(d);
w.destroy_on_draw = true;
attach(&w);
pump(loop, d, NULL);
assert(w.draws && !w.surface);
- yasl_display_close(d);
+ yasl_private_display_close(d);
}
- yasl_loop_destroy(loop);
+ yasl_private_loop_destroy(loop);
puts("Backend test passed.");
return 0;
}
diff --git a/tests/common/test_contracts.c b/tests/common/test_contracts.c
index d1964ff..be84586 100644
--- a/tests/common/test_contracts.c
+++ b/tests/common/test_contracts.c
@@ -13,17 +13,17 @@
static void test_timing_clock(void)
{
- struct yasl_frame_clock clock = {.refresh_ns = YASL_REFRESH_DEFAULT_NS};
- yasl_clock_presented(&clock, 1000000000, true);
+ struct yasl_private_frame_clock clock = {.refresh_ns = YASL_PRIVATE_REFRESH_DEFAULT_NS};
+ yasl_private_clock_presented(&clock, 1000000000, true);
assert(clock.last_present_ns == 1000000000);
- uint64_t next = yasl_clock_next_present_ns(&clock);
+ uint64_t next = yasl_private_clock_next_present_ns(&clock);
assert(next > 1000000000);
- assert(yasl_clock_extend_ms(1000, 1000000000) == 1000000000);
- assert(yasl_clock_frame_timestamp_ns(&clock, next) == next);
+ assert(yasl_private_clock_extend_ms(1000, 1000000000) == 1000000000);
+ assert(yasl_private_clock_frame_timestamp_ns(&clock, next) == next);
- yasl_clock_note_resize(&clock, 500000);
- struct yasl_resize_stats stats;
- yasl_clock_resize_stats(&clock, &stats);
+ yasl_private_clock_note_resize(&clock, 500000);
+ struct yasl_private_resize_stats stats;
+ yasl_private_clock_resize_stats(&clock, &stats);
assert(stats.count == 1);
assert(stats.max_us == 500.0);
}
@@ -31,27 +31,27 @@ static void test_timing_clock(void)
static void test_surface_state(void)
{
/* Reading reported flags does not require a backend query or display. */
- struct yasl_surface surface = {.state = YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED};
- assert(!yasl_surface_state(NULL));
- assert(yasl_surface_state(&surface) == (YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED));
+ struct yasl_surface surface = {.state = YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_ACTIVATED};
+ assert(!yasl_private_surface_state(NULL));
+ assert(yasl_private_surface_state(&surface) == (YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_ACTIVATED));
}
static void test_queue(void)
{
struct input_queue q = {0};
struct yasl_surface surface = {0};
- const enum yasl_event_type kinds[] = {
- YASL_EVENT_TEXT, YASL_EVENT_PASTE, YASL_EVENT_DROP,
+ const enum yasl_private_event_type kinds[] = {
+ YASL_PRIVATE_EVENT_TEXT, YASL_PRIVATE_EVENT_PASTE, YASL_PRIVATE_EVENT_DROP,
};
for (size_t k = 0; k < sizeof(kinds) / sizeof(kinds[0]); k++) {
char *source = malloc(4);
assert(source);
memcpy(source, "a\0b", 4);
- struct yasl_event in = {.surface = &surface, .type = kinds[k], .text = {.data = source, .len = 3}};
+ struct yasl_private_event in = {.surface = &surface, .type = kinds[k], .text = {.data = source, .len = 3}};
assert(input_queue_push(&q, &in));
memset(source, 'x', 4);
free(source);
- struct yasl_event out;
+ struct yasl_private_event out;
assert(input_queue_pop(&q, &out));
const char *payload = out.text.data;
assert(out.surface == &surface && out.type == kinds[k]);
@@ -60,7 +60,7 @@ static void test_queue(void)
assert(memcmp(payload, "a\0b", 4) == 0);
/* Growth/wraparound must not invalidate a previously polled payload. */
for (unsigned i = 0; i < 4096; i++) {
- in = (struct yasl_event){.type = YASL_EVENT_KEY, .key.key = i};
+ in = (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_KEY, .key.key = i};
assert(input_queue_push(&q, &in));
}
assert(memcmp(payload, "a\0b", 4) == 0);
@@ -78,12 +78,12 @@ static void test_queue(void)
}
assert(!input_queue_pop(&q, &out));
}
- struct yasl_event empty = {.surface = &surface, .type = YASL_EVENT_PASTE};
- struct yasl_event out;
+ struct yasl_private_event empty = {.surface = &surface, .type = YASL_PRIVATE_EVENT_PASTE};
+ struct yasl_private_event out;
assert(input_queue_push(&q, &empty));
assert(input_queue_pop(&q, &out));
assert(out.text.data && !out.text.len && out.text.data[0] == '\0');
- struct yasl_event in = {.surface = &surface, .type = YASL_EVENT_TEXT,
+ struct yasl_private_event in = {.surface = &surface, .type = YASL_PRIVATE_EVENT_TEXT,
.text = {"owned", 5}};
assert(input_queue_push(&q, &in));
assert(input_queue_pop(&q, &out));
@@ -93,13 +93,13 @@ static void test_queue(void)
in.text.len = SIZE_MAX;
assert(!input_queue_push(&q, &in));
assert(strcmp(last, "owned") == 0);
- in = (struct yasl_event){.surface = &surface, .type = YASL_EVENT_KEY};
+ in = (struct yasl_private_event){.surface = &surface, .type = YASL_PRIVATE_EVENT_KEY};
assert(input_queue_push(&q, &in));
- assert(input_queue_pop(&q, &out) && out.type == YASL_EVENT_INPUT_RESET);
+ assert(input_queue_pop(&q, &out) && out.type == YASL_PRIVATE_EVENT_INPUT_RESET);
assert(out.surface == &surface);
- assert(input_queue_pop(&q, &out) && out.type == YASL_EVENT_KEY);
+ assert(input_queue_pop(&q, &out) && out.type == YASL_PRIVATE_EVENT_KEY);
assert(!input_queue_pop(&q, &out));
- in = (struct yasl_event){.type = YASL_EVENT_PASTE, .text = {"pending", 7}};
+ in = (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_PASTE, .text = {"pending", 7}};
assert(input_queue_push(&q, &in));
input_queue_clear(&q);
input_queue_clear(&q);
@@ -107,49 +107,49 @@ static void test_queue(void)
}
static int presents, copies, pastes, creates;
-static struct yasl_damage_list received;
-static bool present(struct yasl_surface *s, const struct yasl_framebuffer *fb,
- const struct yasl_damage_list *damage)
+static struct yasl_private_damage_list received;
+static bool present(struct yasl_surface *s, const struct yasl_private_framebuffer *fb,
+ const struct yasl_private_damage_list *damage)
{
(void)s; (void)fb;
presents++;
received = *damage;
return true;
}
-static uint32_t caps(const struct yasl_display *d) { (void)d; return 0; }
-static void copy(struct yasl_display *d, enum yasl_selection sel, const char *text)
+static uint32_t caps(const struct yasl_private_display *d) { (void)d; return 0; }
+static void copy(struct yasl_private_display *d, enum yasl_private_selection sel, const char *text)
{ (void)d; (void)sel; (void)text; copies++; }
-static void paste(struct yasl_display *d, enum yasl_selection sel, struct yasl_surface *s)
+static void paste(struct yasl_private_display *d, enum yasl_private_selection sel, struct yasl_surface *s)
{ (void)d; (void)sel; (void)s; pastes++; }
-static struct yasl_surface *create(struct yasl_display *d, const struct yasl_window_spec *spec, void *graphics_device)
+static struct yasl_surface *create(struct yasl_private_display *d, const struct yasl_private_window_spec *spec, void *graphics_device)
{ (void)d; (void)spec; (void)graphics_device; creates++; return NULL; }
static void *native_device;
static int native_queries;
static struct yasl_surface native_surface;
-static uint32_t native_caps(const struct yasl_display *d)
-{ (void)d; return YASL_CAP_NATIVE_GRAPHICS; }
-static struct yasl_surface *native_create(struct yasl_display *d,
- const struct yasl_window_spec *spec, void *device)
+static uint32_t native_caps(const struct yasl_private_display *d)
+{ (void)d; return YASL_PRIVATE_CAP_NATIVE_GRAPHICS; }
+static struct yasl_surface *native_create(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *device)
{
(void)spec;
native_device = device;
native_surface = (struct yasl_surface){.display = d, .frame_generation = 1};
return &native_surface;
}
-static bool native_frame(struct yasl_surface *s, const struct yasl_framebuffer *fb,
- struct yasl_native_frame *out)
+static bool native_frame(struct yasl_surface *s, const struct yasl_private_framebuffer *fb,
+ struct yasl_private_native_frame *out)
{
(void)s; (void)fb;
native_queries++;
- *out = (struct yasl_native_frame){.drawable = native_device};
+ *out = (struct yasl_private_native_frame){.drawable = native_device};
return true;
}
-static uint32_t push_buttons(struct input_queue *q, enum yasl_event_type type,
- enum yasl_pointer_button button)
+static uint32_t push_buttons(struct input_queue *q, enum yasl_private_event_type type,
+ enum yasl_private_pointer_button button)
{
- struct yasl_event ev = {.type = type, .pointer.button = button};
+ struct yasl_private_event ev = {.type = type, .pointer.button = button};
assert(input_queue_push(q, &ev));
assert(input_queue_pop(q, &ev));
return ev.pointer.buttons;
@@ -157,31 +157,31 @@ static uint32_t push_buttons(struct input_queue *q, enum yasl_event_type type,
static void test_held_buttons(void)
{
- const uint32_t primary = YASL_BUTTON_BIT(YASL_BUTTON_PRIMARY);
- const uint32_t secondary = YASL_BUTTON_BIT(YASL_BUTTON_SECONDARY);
+ const uint32_t primary = YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY);
+ const uint32_t secondary = YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_SECONDARY);
struct input_queue q = {0};
- assert(push_buttons(&q, YASL_EVENT_POINTER_MOTION, 0) == 0);
- assert(push_buttons(&q, YASL_EVENT_POINTER_DOWN, YASL_BUTTON_PRIMARY) == primary);
- assert(push_buttons(&q, YASL_EVENT_POINTER_MOTION, 0) == primary);
- assert(push_buttons(&q, YASL_EVENT_POINTER_DOWN, YASL_BUTTON_SECONDARY) == (primary | secondary));
- assert(push_buttons(&q, YASL_EVENT_POINTER_UP, YASL_BUTTON_PRIMARY) == secondary);
- assert(push_buttons(&q, YASL_EVENT_POINTER_SCROLL, 0) == secondary);
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_MOTION, 0) == 0);
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_DOWN, YASL_PRIVATE_BUTTON_PRIMARY) == primary);
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_MOTION, 0) == primary);
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_DOWN, YASL_PRIVATE_BUTTON_SECONDARY) == (primary | secondary));
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_UP, YASL_PRIVATE_BUTTON_PRIMARY) == secondary);
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_SCROLL, 0) == secondary);
/* X11 and Cocoa keep the press across leave; the release still follows. */
- assert(push_buttons(&q, YASL_EVENT_POINTER_LEAVE, 0) == secondary);
- assert(push_buttons(&q, YASL_EVENT_POINTER_UP, YASL_BUTTON_SECONDARY) == 0);
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_LEAVE, 0) == secondary);
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_UP, YASL_PRIVATE_BUTTON_SECONDARY) == 0);
/* A release without a press leaves nothing held. */
- assert(push_buttons(&q, YASL_EVENT_POINTER_UP, YASL_BUTTON_MIDDLE) == 0);
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_UP, YASL_PRIVATE_BUTTON_MIDDLE) == 0);
- assert(push_buttons(&q, YASL_EVENT_POINTER_DOWN, YASL_BUTTON_PRIMARY) == primary);
- struct yasl_event reset = {.type = YASL_EVENT_INPUT_RESET};
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_DOWN, YASL_PRIVATE_BUTTON_PRIMARY) == primary);
+ struct yasl_private_event reset = {.type = YASL_PRIVATE_EVENT_INPUT_RESET};
assert(input_queue_push(&q, &reset));
assert(input_queue_pop(&q, &reset));
- assert(push_buttons(&q, YASL_EVENT_POINTER_MOTION, 0) == 0);
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_MOTION, 0) == 0);
q.leave_ends_press = true;
- assert(push_buttons(&q, YASL_EVENT_POINTER_DOWN, YASL_BUTTON_PRIMARY) == primary);
- assert(push_buttons(&q, YASL_EVENT_POINTER_LEAVE, 0) == 0);
- assert(push_buttons(&q, YASL_EVENT_POINTER_MOTION, 0) == 0);
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_DOWN, YASL_PRIVATE_BUTTON_PRIMARY) == primary);
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_LEAVE, 0) == 0);
+ assert(push_buttons(&q, YASL_PRIVATE_EVENT_POINTER_MOTION, 0) == 0);
input_queue_clear(&q);
}
@@ -216,117 +216,117 @@ static void test_drop_paths(void)
/* A short buffer reports the length and leaves both outputs alone. */
const char *before = cursor;
memcpy(path, "keep", 5);
- assert(yasl_drop_next_path(&cursor, path, len) == len);
+ assert(yasl_private_drop_next_path(&cursor, path, len) == len);
assert(cursor == before && strcmp(path, "keep") == 0);
- assert(yasl_drop_next_path(&cursor, NULL, 0) == len && cursor == before);
- assert(yasl_drop_next_path(&cursor, path, sizeof(path)) == len);
+ assert(yasl_private_drop_next_path(&cursor, NULL, 0) == len && cursor == before);
+ assert(yasl_private_drop_next_path(&cursor, path, sizeof(path)) == len);
assert(strcmp(path, want[i]) == 0);
}
- assert(yasl_drop_next_path(&cursor, path, sizeof(path)) == 0 && *cursor == '\0');
- assert(yasl_drop_next_path(&cursor, path, sizeof(path)) == 0);
- assert(yasl_drop_next_path(NULL, path, sizeof(path)) == 0);
+ assert(yasl_private_drop_next_path(&cursor, path, sizeof(path)) == 0 && *cursor == '\0');
+ assert(yasl_private_drop_next_path(&cursor, path, sizeof(path)) == 0);
+ assert(yasl_private_drop_next_path(NULL, path, sizeof(path)) == 0);
const char *none = NULL;
- assert(yasl_drop_next_path(&none, path, sizeof(path)) == 0);
+ assert(yasl_private_drop_next_path(&none, path, sizeof(path)) == 0);
cursor = list;
- assert(yasl_drop_next_path(&cursor, NULL, 8) == 0 && cursor == list);
+ assert(yasl_private_drop_next_path(&cursor, NULL, 8) == 0 && cursor == list);
}
static void test_native(void)
{
- const struct yasl_backend_ops ops = {.display_caps = native_caps,
+ const struct yasl_private_backend_ops ops = {.display_caps = native_caps,
.surface_create_toplevel = native_create, .surface_get_native_frame = native_frame};
- struct yasl_display d = {.ops = &ops};
- struct yasl_window_spec spec = {.size = {100, 80}};
- assert(yasl_surface_create_toplevel(&d, &spec) == &native_surface);
+ struct yasl_private_display d = {.ops = &ops};
+ struct yasl_private_window_spec spec = {.size = {100, 80}};
+ assert(yasl_private_surface_create_toplevel(&d, &spec) == &native_surface);
assert(!native_device);
- assert(yasl_surface_create_native(&d, &spec, &d) == &native_surface);
+ assert(yasl_private_surface_create_native(&d, &spec, &d) == &native_surface);
assert(native_device == &d);
- struct yasl_framebuffer fb = {.generation = 1, .size = {100, 80}};
- struct yasl_native_frame out = {0};
- assert(!yasl_surface_get_native_frame(NULL, &fb, &out));
- assert(!yasl_surface_get_native_frame(&native_surface, NULL, &out));
- assert(!yasl_surface_get_native_frame(&native_surface, &fb, NULL));
+ struct yasl_private_framebuffer fb = {.generation = 1, .size = {100, 80}};
+ struct yasl_private_native_frame out = {0};
+ assert(!yasl_private_surface_get_native_frame(NULL, &fb, &out));
+ assert(!yasl_private_surface_get_native_frame(&native_surface, NULL, &out));
+ assert(!yasl_private_surface_get_native_frame(&native_surface, &fb, NULL));
fb.generation = 0;
- assert(!yasl_surface_get_native_frame(&native_surface, &fb, &out));
+ assert(!yasl_private_surface_get_native_frame(&native_surface, &fb, &out));
fb.generation = 2;
- assert(!yasl_surface_get_native_frame(&native_surface, &fb, &out));
+ assert(!yasl_private_surface_get_native_frame(&native_surface, &fb, &out));
assert(!native_queries && !out.drawable);
fb.generation = 1;
- assert(yasl_surface_get_native_frame(&native_surface, &fb, &out));
+ assert(yasl_private_surface_get_native_frame(&native_surface, &fb, &out));
assert(native_queries == 1 && out.drawable == &d);
}
static void test_validation(void)
{
- const struct yasl_backend_ops ops = {.surface_present = present,
+ const struct yasl_private_backend_ops ops = {.surface_present = present,
.display_caps = caps, .display_copy = copy, .display_paste = paste,
.surface_create_toplevel = create};
- struct yasl_display d = {.ops = &ops};
+ struct yasl_private_display d = {.ops = &ops};
struct yasl_surface s = {.display = &d, .frame_generation = 1};
- struct yasl_framebuffer fb = {.generation = 1, .size = {100, 80}};
- struct yasl_damage_list damage = {.n = -2};
- assert(!yasl_surface_present(&s, &fb, &damage));
- damage.n = YASL_DAMAGE_MAX + 1;
- assert(!yasl_surface_present(&s, &fb, &damage));
+ struct yasl_private_framebuffer fb = {.generation = 1, .size = {100, 80}};
+ struct yasl_private_damage_list damage = {.n = -2};
+ assert(!yasl_private_surface_present(&s, &fb, &damage));
+ damage.n = YASL_PRIVATE_DAMAGE_MAX + 1;
+ assert(!yasl_private_surface_present(&s, &fb, &damage));
damage.n = 1;
- damage.rects[0] = (struct yasl_rect2i){0, 0, -1, 1};
- assert(!yasl_surface_present(&s, &fb, &damage));
- assert(!yasl_surface_present(&s, &fb, NULL));
+ damage.rects[0] = (struct yasl_private_rect2i){0, 0, -1, 1};
+ assert(!yasl_private_surface_present(&s, &fb, &damage));
+ assert(!yasl_private_surface_present(&s, &fb, NULL));
assert(!presents);
damage.n = -1;
fb.generation = 0;
- assert(!yasl_surface_present(&s, &fb, &damage));
+ assert(!yasl_private_surface_present(&s, &fb, &damage));
fb.generation = 2;
- assert(!yasl_surface_present(&s, &fb, &damage));
+ assert(!yasl_private_surface_present(&s, &fb, &damage));
fb.generation = 1;
damage.n = 4;
- damage.rects[0] = (struct yasl_rect2i){-10, -10, 30, 30};
- damage.rects[1] = (struct yasl_rect2i){90, 70, INT_MAX, INT_MAX};
- damage.rects[2] = (struct yasl_rect2i){INT_MAX, INT_MAX, INT_MAX, INT_MAX};
- damage.rects[3] = (struct yasl_rect2i){INT_MIN, INT_MIN, INT_MAX, INT_MAX};
- assert(yasl_surface_present(&s, &fb, &damage));
+ damage.rects[0] = (struct yasl_private_rect2i){-10, -10, 30, 30};
+ damage.rects[1] = (struct yasl_private_rect2i){90, 70, INT_MAX, INT_MAX};
+ damage.rects[2] = (struct yasl_private_rect2i){INT_MAX, INT_MAX, INT_MAX, INT_MAX};
+ damage.rects[3] = (struct yasl_private_rect2i){INT_MIN, INT_MIN, INT_MAX, INT_MAX};
+ assert(yasl_private_surface_present(&s, &fb, &damage));
assert(received.n == 2 && received.rects[0].x == 0 && received.rects[0].width == 20);
assert(received.rects[1].width == 10 && received.rects[1].height == 10);
assert(damage.n == 4 && damage.rects[0].x == -10);
damage.n = -1;
- assert(yasl_surface_present(&s, &fb, &damage) && received.n == -1);
+ assert(yasl_private_surface_present(&s, &fb, &damage) && received.n == -1);
damage.n = 0;
- assert(yasl_surface_present(&s, &fb, &damage) && received.n == 0);
- yasl_display_copy(&d, YASL_PRIMARY, "no alias");
- yasl_display_copy(&d, (enum yasl_selection)255, "invalid");
- yasl_display_paste(&d, YASL_PRIMARY, &s);
- yasl_display_paste(&d, (enum yasl_selection)255, &s);
+ assert(yasl_private_surface_present(&s, &fb, &damage) && received.n == 0);
+ yasl_private_display_copy(&d, YASL_PRIVATE_PRIMARY, "no alias");
+ yasl_private_display_copy(&d, (enum yasl_private_selection)255, "invalid");
+ yasl_private_display_paste(&d, YASL_PRIVATE_PRIMARY, &s);
+ yasl_private_display_paste(&d, (enum yasl_private_selection)255, &s);
assert(!copies && !pastes);
- yasl_display_copy(&d, YASL_CLIPBOARD, "yes");
- yasl_display_paste(&d, YASL_CLIPBOARD, &s);
+ yasl_private_display_copy(&d, YASL_PRIVATE_CLIPBOARD, "yes");
+ yasl_private_display_paste(&d, YASL_PRIVATE_CLIPBOARD, &s);
assert(copies == 1 && pastes == 1);
- struct yasl_window_spec spec = {.size = {100, 80}};
- assert(!yasl_surface_create_native(&d, &spec, &d) && errno == ENOTSUP);
- assert(!yasl_surface_create_native(&d, &spec, NULL) && errno == EINVAL);
+ struct yasl_private_window_spec spec = {.size = {100, 80}};
+ assert(!yasl_private_surface_create_native(&d, &spec, &d) && errno == ENOTSUP);
+ assert(!yasl_private_surface_create_native(&d, &spec, NULL) && errno == EINVAL);
spec.size.x = 0;
- assert(!yasl_surface_create_toplevel(&d, &spec) && errno == EINVAL);
+ assert(!yasl_private_surface_create_toplevel(&d, &spec) && errno == EINVAL);
assert(!creates);
}
static void test_scale(void)
{
- assert(yasl_scale_to_device(-10, 120) == -10);
- assert(yasl_scale_to_device(-0.5, 120) == -1);
- assert(yasl_scale_to_device(0.5, 120) == 1);
- assert(yasl_scale_to_device(NAN, 120) == 0);
- assert(yasl_scale_to_device(INFINITY, 120) == 0);
- assert(yasl_scale_to_device(DBL_MAX, 240) == INT_MAX);
- assert(yasl_scale_to_logical(-1, 240) == -1);
- assert(yasl_scale_to_logical(1, 240) == 1);
- assert(yasl_scale_to_logical(INT_MAX, 1) == INT_MAX);
- assert(yasl_scale_to_logical(INT_MIN, 1) == INT_MIN);
- assert(yasl_scale_to_logical(1, 0) == 0);
+ assert(yasl_private_scale_to_device(-10, 120) == -10);
+ assert(yasl_private_scale_to_device(-0.5, 120) == -1);
+ assert(yasl_private_scale_to_device(0.5, 120) == 1);
+ assert(yasl_private_scale_to_device(NAN, 120) == 0);
+ assert(yasl_private_scale_to_device(INFINITY, 120) == 0);
+ assert(yasl_private_scale_to_device(DBL_MAX, 240) == INT_MAX);
+ assert(yasl_private_scale_to_logical(-1, 240) == -1);
+ assert(yasl_private_scale_to_logical(1, 240) == 1);
+ assert(yasl_private_scale_to_logical(INT_MAX, 1) == INT_MAX);
+ assert(yasl_private_scale_to_logical(INT_MIN, 1) == INT_MIN);
+ assert(yasl_private_scale_to_logical(1, 0) == 0);
}
static int popup_calls;
static struct yasl_surface *popup_create(struct yasl_surface *parent,
- const struct yasl_popup_spec *spec)
+ const struct yasl_private_popup_spec *spec)
{
(void)parent; (void)spec;
popup_calls++;
@@ -334,7 +334,7 @@ static struct yasl_surface *popup_create(struct yasl_surface *parent,
}
static bool popup_reposition(struct yasl_surface *popup,
- const struct yasl_popup_spec *spec)
+ const struct yasl_private_popup_spec *spec)
{
(void)popup; (void)spec;
popup_calls++;
@@ -342,19 +342,19 @@ static bool popup_reposition(struct yasl_surface *popup,
return false;
}
-static uint32_t popup_caps(const struct yasl_display *d)
-{ (void)d; return YASL_CAP_POPUPS; }
+static uint32_t popup_caps(const struct yasl_private_display *d)
+{ (void)d; return YASL_PRIVATE_CAP_POPUPS; }
-static int dispatch_failure(struct yasl_display *d, int timeout)
+static int dispatch_failure(struct yasl_private_display *d, int timeout)
{ (void)d; (void)timeout; return -1; }
-static struct yasl_surface *create_failure(struct yasl_display *d,
- const struct yasl_window_spec *spec, void *device)
+static struct yasl_surface *create_failure(struct yasl_private_display *d,
+ const struct yasl_private_window_spec *spec, void *device)
{ (void)d; (void)spec; (void)device; return NULL; }
static int drag_starts;
-static bool drag_without_listener(struct yasl_display *d, struct yasl_surface *s,
- const struct yasl_drag_image *image, const struct yasl_drag_listener *listener,
+static bool drag_without_listener(struct yasl_private_display *d, struct yasl_surface *s,
+ const struct yasl_private_drag_image *image, const struct yasl_private_drag_listener *listener,
void *ud)
{
(void)d; (void)s; (void)image; (void)ud;
@@ -366,52 +366,52 @@ static bool drag_without_listener(struct yasl_display *d, struct yasl_surface *s
static void test_errors(void)
{
- const struct yasl_backend_ops ops = {.display_caps = popup_caps,
+ const struct yasl_private_backend_ops ops = {.display_caps = popup_caps,
.surface_create_toplevel = create_failure,
.surface_create_popup = popup_create,
.surface_popup_reposition = popup_reposition,
.display_drag_start = drag_without_listener,
.display_dispatch_timeout = dispatch_failure};
- struct yasl_display d = {.ops = &ops};
+ struct yasl_private_display d = {.ops = &ops};
struct yasl_surface s = {.display = &d};
- struct yasl_popup_spec spec = {.size = {120, 80}, .anchor = {0, 0, 1, 1}};
- assert(!yasl_surface_create_popup(NULL, &spec) && errno == EINVAL);
- assert(!yasl_surface_create_popup(&s, NULL) && errno == EINVAL);
+ struct yasl_private_popup_spec spec = {.size = {120, 80}, .anchor = {0, 0, 1, 1}};
+ assert(!yasl_private_surface_create_popup(NULL, &spec) && errno == EINVAL);
+ assert(!yasl_private_surface_create_popup(&s, NULL) && errno == EINVAL);
spec.anchor.width = -1; /* Must not reach Wayland's protocol marshaler. */
- assert(!yasl_surface_create_popup(&s, &spec) && errno == EINVAL);
- assert(!yasl_surface_popup_reposition(&s, &spec) && errno == EINVAL);
+ assert(!yasl_private_surface_create_popup(&s, &spec) && errno == EINVAL);
+ assert(!yasl_private_surface_popup_reposition(&s, &spec) && errno == EINVAL);
spec.anchor.width = 1;
spec.content_offset.x = INT_MAX;
spec.content_size.x = INT_MAX;
- assert(!yasl_surface_create_popup(&s, &spec) && errno == EINVAL);
+ assert(!yasl_private_surface_create_popup(&s, &spec) && errno == EINVAL);
spec.content_offset.x = spec.content_size.x = 0;
spec.size.x = 32768;
- assert(!yasl_surface_create_popup(&s, &spec) && errno == EINVAL);
+ assert(!yasl_private_surface_create_popup(&s, &spec) && errno == EINVAL);
assert(!popup_calls);
spec.size.x = 120;
errno = ERANGE;
- assert(!yasl_surface_create_popup(&s, &spec) && errno == ENODEV);
- assert(!yasl_surface_popup_reposition(&s, &spec) && errno == EBUSY);
+ assert(!yasl_private_surface_create_popup(&s, &spec) && errno == ENODEV);
+ assert(!yasl_private_surface_popup_reposition(&s, &spec) && errno == EBUSY);
assert(popup_calls == 2);
- const struct yasl_backend_ops unsupported = {0};
+ const struct yasl_private_backend_ops unsupported = {0};
d.ops = &unsupported;
- assert(!yasl_surface_create_popup(&s, &spec) && errno == ENOTSUP);
- assert(!yasl_surface_popup_reposition(&s, &spec) && errno == ENOTSUP);
- assert(yasl_display_dispatch(&d, 0) == -1 && errno == ENOTSUP);
- assert(yasl_display_dispatch(NULL, 0) == -1 && errno == EINVAL);
+ assert(!yasl_private_surface_create_popup(&s, &spec) && errno == ENOTSUP);
+ assert(!yasl_private_surface_popup_reposition(&s, &spec) && errno == ENOTSUP);
+ assert(yasl_private_display_dispatch(&d, 0) == -1 && errno == ENOTSUP);
+ assert(yasl_private_display_dispatch(NULL, 0) == -1 && errno == EINVAL);
d.ops = &ops;
errno = ERANGE;
- assert(yasl_display_dispatch(&d, 0) == -1 && errno == EIO);
- struct yasl_window_spec toplevel = {.size = {120, 80}};
+ assert(yasl_private_display_dispatch(&d, 0) == -1 && errno == EIO);
+ struct yasl_private_window_spec toplevel = {.size = {120, 80}};
errno = ERANGE;
- assert(!yasl_surface_create_toplevel(&d, &toplevel) && errno == ENODEV);
- assert(yasl_display_drag_start(&d, &s, NULL, NULL, NULL));
- struct yasl_drag_image image = {.size = {1, 1}};
- assert(!yasl_display_drag_start(&d, &s, &image, NULL, NULL));
+ assert(!yasl_private_surface_create_toplevel(&d, &toplevel) && errno == ENODEV);
+ assert(yasl_private_display_drag_start(&d, &s, NULL, NULL, NULL));
+ struct yasl_private_drag_image image = {.size = {1, 1}};
+ assert(!yasl_private_display_drag_start(&d, &s, &image, NULL, NULL));
uint32_t pixel = 0;
image.pixels = &pixel;
image.size.x = INT_MAX;
- assert(!yasl_display_drag_start(&d, &s, &image, NULL, NULL));
+ assert(!yasl_private_display_drag_start(&d, &s, &image, NULL, NULL));
assert(drag_starts == 1);
}
diff --git a/tests/common/test_desktop.c b/tests/common/test_desktop.c
index 89760fe..a665da5 100644
--- a/tests/common/test_desktop.c
+++ b/tests/common/test_desktop.c
@@ -1,7 +1,7 @@
#define _GNU_SOURCE
#include "common/backend.h"
#include "platform/freedesktop/dbus.h"
-#include "yasl/desktop.h"
+#include "common/private_desktop.h"
#include <assert.h>
#include <fcntl.h>
@@ -39,31 +39,31 @@ static char *read_file(const char *path)
static void test_buffers(void)
{
char exe[PATH_MAX], real[PATH_MAX];
- size_t len = yasl_executable_path(NULL, 0);
+ size_t len = yasl_private_executable_path(NULL, 0);
assert(len > 0);
- assert(yasl_executable_path(exe, len) == len);
- assert(yasl_executable_path(exe, sizeof(exe)) == len && exe[len] == '\0');
+ assert(yasl_private_executable_path(exe, len) == len);
+ assert(yasl_private_executable_path(exe, sizeof(exe)) == len && exe[len] == '\0');
assert(realpath("/proc/self/exe", real) && strcmp(exe, real) == 0);
/* A short buffer stays untouched. */
char small[4] = "abc";
- assert(yasl_executable_path(small, sizeof(small)) == len);
+ assert(yasl_private_executable_path(small, sizeof(small)) == len);
assert(strcmp(small, "abc") == 0);
- assert(yasl_executable_path(NULL, 8) == 0);
+ assert(yasl_private_executable_path(NULL, 8) == 0);
- assert(yasl_resource_dir(exe, sizeof(exe)) == 0);
+ assert(yasl_private_resource_dir(exe, sizeof(exe)) == 0);
char locale[64];
setenv("LC_ALL", "", 1);
setenv("LC_CTYPE", "de_DE.UTF-8", 1);
setenv("LANG", "fr_FR.UTF-8", 1);
- assert(yasl_default_locale(locale, sizeof(locale)) == 11);
+ assert(yasl_private_default_locale(locale, sizeof(locale)) == 11);
assert(strcmp(locale, "de_DE.UTF-8") == 0);
unsetenv("LC_CTYPE");
- assert(yasl_default_locale(locale, sizeof(locale)) == 11);
+ assert(yasl_private_default_locale(locale, sizeof(locale)) == 11);
assert(strcmp(locale, "fr_FR.UTF-8") == 0);
unsetenv("LANG");
unsetenv("LC_ALL");
- assert(yasl_default_locale(locale, sizeof(locale)) == 7);
+ assert(yasl_private_default_locale(locale, sizeof(locale)) == 7);
assert(strcmp(locale, "C.UTF-8") == 0);
}
@@ -86,14 +86,14 @@ static void test_open_uri(const char *dir)
snprintf(path, sizeof(path), "%s:%s", dir, old_path ? old_path : "/usr/bin:/bin");
setenv("PATH", path, 1);
- struct yasl_display display = {0};
- assert(!yasl_display_open_uri(NULL, "https://example.com"));
- assert(!yasl_display_open_uri(&display, NULL));
- assert(!yasl_display_open_uri(&display, ""));
- assert(!yasl_display_open_uri(&display, "example.com"));
- assert(!yasl_display_open_uri(&display, "-x:option"));
- assert(!yasl_display_open_uri(&display, "1http://example.com"));
- assert(!yasl_display_open_uri(&display, "http://example.com/\n"));
+ struct yasl_private_display display = {0};
+ assert(!yasl_private_display_open_uri(NULL, "https://example.com"));
+ assert(!yasl_private_display_open_uri(&display, NULL));
+ assert(!yasl_private_display_open_uri(&display, ""));
+ assert(!yasl_private_display_open_uri(&display, "example.com"));
+ assert(!yasl_private_display_open_uri(&display, "-x:option"));
+ assert(!yasl_private_display_open_uri(&display, "1http://example.com"));
+ assert(!yasl_private_display_open_uri(&display, "http://example.com/\n"));
assert(access(out, F_OK) != 0);
/* The opener must not inherit blocked or ignored signals or descriptors. */
@@ -110,7 +110,7 @@ static void test_open_uri(const char *dir)
close(null);
const char *uri = "https://example.com/a b?c='d'&e=\"f\"";
- assert(yasl_display_open_uri(&display, uri));
+ assert(yasl_private_display_open_uri(&display, uri));
char *text = NULL;
for (int i = 0; i < 500 && !(text = read_file(out)); i++) sleep_ms(10);
assert(text);
@@ -137,29 +137,29 @@ static void test_open_uri(const char *dir)
/* Without xdg-open in PATH, nothing launches. */
setenv("PATH", dir, 1);
- assert(!yasl_display_open_uri(&display, "https://example.com"));
+ assert(!yasl_private_display_open_uri(&display, "https://example.com"));
if (old_path) setenv("PATH", old_path, 1);
}
static void test_appearance_fallback(void)
{
- assert(yasl_display_double_click_ms(NULL) == YASL_DOUBLE_CLICK_MS_DEFAULT);
+ assert(yasl_private_display_double_click_ms(NULL) == YASL_PRIVATE_DOUBLE_CLICK_MS_DEFAULT);
assert(portal_double_click_ms() == 0);
- struct yasl_display display = {0};
- assert(yasl_display_appearance(NULL) == 0);
+ struct yasl_private_display display = {0};
+ assert(yasl_private_display_appearance(NULL) == 0);
unsetenv("GTK_THEME");
- assert(yasl_display_appearance(&display) == 0);
+ assert(yasl_private_display_appearance(&display) == 0);
setenv("GTK_THEME", "Adwaita:dark", 1);
- assert(yasl_display_appearance(&display) == YASL_APPEARANCE_DARK);
+ assert(yasl_private_display_appearance(&display) == YASL_PRIVATE_APPEARANCE_DARK);
setenv("GTK_THEME", "HighContrast", 1);
- assert(yasl_display_appearance(&display) == YASL_APPEARANCE_HIGH_CONTRAST);
+ assert(yasl_private_display_appearance(&display) == YASL_PRIVATE_APPEARANCE_HIGH_CONTRAST);
setenv("GTK_THEME", "HighContrastInverse", 1);
- assert(yasl_display_appearance(&display) ==
- (YASL_APPEARANCE_HIGH_CONTRAST | YASL_APPEARANCE_DARK));
+ assert(yasl_private_display_appearance(&display) ==
+ (YASL_PRIVATE_APPEARANCE_HIGH_CONTRAST | YASL_PRIVATE_APPEARANCE_DARK));
unsetenv("GTK_THEME");
- assert(!yasl_display_notify(NULL, "title", "body"));
- assert(!yasl_display_notify(&display, NULL, "body"));
- assert(!yasl_display_notify(&display, "title", "body"));
+ assert(!yasl_private_display_notify(NULL, "title", "body"));
+ assert(!yasl_private_display_notify(&display, NULL, "body"));
+ assert(!yasl_private_display_notify(&display, "title", "body"));
}
#if YASL_HAS_DBUS
@@ -310,8 +310,8 @@ static void test_bus(const char *address)
pthread_t thread;
assert(pthread_create(&thread, NULL, serve, &fb) == 0);
- struct yasl_display display = {.app_id = "org.example.Test"};
- assert(yasl_display_notify(&display, "Build done", NULL));
+ struct yasl_private_display display = {.app_id = "org.example.Test"};
+ assert(yasl_private_display_notify(&display, "Build done", NULL));
for (int i = 0; i < 500; i++) {
pthread_mutex_lock(&fb.lock);
int n = fb.notifications;
@@ -328,10 +328,10 @@ static void test_bus(const char *address)
/* Portal settings win over GTK_THEME, through ReadOne or the older Read. */
setenv("GTK_THEME", "Adwaita", 1);
- uint32_t want = YASL_APPEARANCE_DARK | YASL_APPEARANCE_HIGH_CONTRAST;
- assert(yasl_display_appearance(&display) == want);
+ uint32_t want = YASL_PRIVATE_APPEARANCE_DARK | YASL_PRIVATE_APPEARANCE_HIGH_CONTRAST;
+ assert(yasl_private_display_appearance(&display) == want);
fb.read_one = false;
- assert(yasl_display_appearance(&display) == want);
+ assert(yasl_private_display_appearance(&display) == want);
unsetenv("GTK_THEME");
/* A KDE value, sent as a string, wins over the GNOME key. */
diff --git a/tests/common/test_events.c b/tests/common/test_events.c
index c8660cb..fcfc118 100644
--- a/tests/common/test_events.c
+++ b/tests/common/test_events.c
@@ -1,5 +1,4 @@
#include "common/backend.h"
-#include "yasl/app.h"
#include <assert.h>
#include <errno.h>
@@ -8,7 +7,7 @@
#include <string.h>
static struct input_queue *queue(struct yasl_surface *s) { return &s->input; }
-static const struct yasl_backend_ops ops = {.surface_input_queue = queue};
+static const struct yasl_private_backend_ops ops = {.surface_input_queue = queue};
static int released;
static pthread_t owner;
static void release(void *payload)
@@ -18,90 +17,90 @@ static void release(void *payload)
released++;
}
-static void setup(struct yasl_display *d, struct yasl_surface *a, struct yasl_surface *b)
+static void setup(struct yasl_private_display *d, struct yasl_surface *a, struct yasl_surface *b)
{
- *d = (struct yasl_display){.ops = &ops};
+ *d = (struct yasl_private_display){.ops = &ops};
*a = (struct yasl_surface){.display = d, .scale_120 = 120};
*b = (struct yasl_surface){.display = d, .scale_120 = 120};
- assert(yasl_events_open(d));
- assert(yasl_events_register(a));
- assert(yasl_events_register(b));
+ assert(yasl_private_events_open(d));
+ assert(yasl_private_events_register(a));
+ assert(yasl_private_events_register(b));
}
-static void teardown(struct yasl_display *d, struct yasl_surface *a, struct yasl_surface *b)
+static void teardown(struct yasl_private_display *d, struct yasl_surface *a, struct yasl_surface *b)
{
- yasl_events_unregister(a);
- yasl_events_unregister(b);
+ yasl_private_events_unregister(a);
+ yasl_private_events_unregister(b);
input_queue_clear(&a->input);
input_queue_clear(&b->input);
- yasl_events_close(d);
+ yasl_private_events_close(d);
}
static void test_order_and_lifetimes(void)
{
- struct yasl_display d;
+ struct yasl_private_display d;
struct yasl_surface a, b;
setup(&d, &a, &b);
- uint64_t aid = yasl_surface_id(&a), bid = yasl_surface_id(&b);
+ uint64_t aid = yasl_private_surface_id(&a), bid = yasl_private_surface_id(&b);
assert(aid && bid && aid != bid);
- yasl_surface_set_userdata(&a, &b);
- assert(yasl_surface_userdata(&a) == &b);
+ yasl_private_surface_set_userdata(&a, &b);
+ assert(yasl_private_surface_userdata(&a) == &b);
char text[] = "text";
- assert(input_queue_push(&a.input, &(struct yasl_event){
- .type = YASL_EVENT_TEXT, .surface = &a, .text = {text, 4}}));
+ assert(input_queue_push(&a.input, &(struct yasl_private_event){
+ .type = YASL_PRIVATE_EVENT_TEXT, .surface = &a, .text = {text, 4}}));
memset(text, 'x', 4);
- yasl_surface_notify_state(&b, YASL_STATE_MAXIMIZED);
- yasl_surface_notify_configure(&a, (struct yasl_geometry){{640, 480}, 180});
- yasl_surface_notify_drag(&b, YASL_DRAG_OVER, (struct yasl_vector2i){3, 4});
- yasl_surface_notify_composition(&a, "hello", 1, 3);
- yasl_surface_notify_composition(&a, NULL, -1, -1);
- yasl_surface_complete_paste(&b, 42, YASL_TRANSFER_UNAVAILABLE, NULL, 0);
- const enum yasl_event_type expected[] = {YASL_EVENT_TEXT, YASL_EVENT_WINDOW_STATE,
- YASL_EVENT_WINDOW_RESIZE, YASL_EVENT_DRAG, YASL_EVENT_COMPOSITION,
- YASL_EVENT_COMPOSITION, YASL_EVENT_PASTE};
- struct yasl_event ev;
+ yasl_private_surface_notify_state(&b, YASL_PRIVATE_STATE_MAXIMIZED);
+ yasl_private_surface_notify_configure(&a, (struct yasl_private_geometry){{640, 480}, 180});
+ yasl_private_surface_notify_drag(&b, YASL_PRIVATE_DRAG_OVER, (struct yasl_private_vector2i){3, 4});
+ yasl_private_surface_notify_composition(&a, "hello", 1, 3);
+ yasl_private_surface_notify_composition(&a, NULL, -1, -1);
+ yasl_private_surface_complete_paste(&b, 42, YASL_PRIVATE_TRANSFER_UNAVAILABLE, NULL, 0);
+ const enum yasl_private_event_type expected[] = {YASL_PRIVATE_EVENT_TEXT, YASL_PRIVATE_EVENT_WINDOW_STATE,
+ YASL_PRIVATE_EVENT_WINDOW_RESIZE, YASL_PRIVATE_EVENT_DRAG, YASL_PRIVATE_EVENT_COMPOSITION,
+ YASL_PRIVATE_EVENT_COMPOSITION, YASL_PRIVATE_EVENT_PASTE};
+ struct yasl_private_event ev;
uint64_t sequence = 0;
for (size_t i = 0; i < sizeof(expected) / sizeof(*expected); i++) {
- assert(yasl_display_poll_event(&d, &ev));
+ assert(yasl_private_display_poll_event(&d, &ev));
assert(ev.type == expected[i] && ev.sequence > sequence && ev.time_ns);
sequence = ev.sequence;
if (i == 0) assert(!strcmp(ev.text.data, "text") && ev.surface_id == aid);
if (i == 2) assert(ev.resize.scale_changed && ev.resize.geometry.size.x == 640);
if (i == 4) assert(ev.composition.active && ev.composition.begin == 1 && !strcmp(ev.composition.data, "hello"));
if (i == 5) assert(!ev.composition.active && !ev.composition.len);
- if (i == 6) assert(ev.paste.request_id == 42 && ev.paste.status == YASL_TRANSFER_UNAVAILABLE);
+ if (i == 6) assert(ev.paste.request_id == 42 && ev.paste.status == YASL_PRIVATE_TRANSFER_UNAVAILABLE);
}
- assert(!yasl_display_poll_event(&d, &ev));
- assert(input_queue_push(&a.input, &(struct yasl_event){
- .type = YASL_EVENT_TEXT, .surface = &a, .text = {"kept", 4}}));
- assert(yasl_display_poll_event(&d, &ev));
+ assert(!yasl_private_display_poll_event(&d, &ev));
+ assert(input_queue_push(&a.input, &(struct yasl_private_event){
+ .type = YASL_PRIVATE_EVENT_TEXT, .surface = &a, .text = {"kept", 4}}));
+ assert(yasl_private_display_poll_event(&d, &ev));
const char *borrowed = ev.text.data;
- assert(input_queue_push(&a.input, &(struct yasl_event){.type = YASL_EVENT_KEY, .surface = &a}));
- yasl_events_unregister(&a);
+ assert(input_queue_push(&a.input, &(struct yasl_private_event){.type = YASL_PRIVATE_EVENT_KEY, .surface = &a}));
+ yasl_private_events_unregister(&a);
assert(!strcmp(borrowed, "kept"));
- assert(!yasl_display_poll_event(&d, &ev));
+ assert(!yasl_private_display_poll_event(&d, &ev));
/* Reusing object memory must never reuse the identity. */
- assert(yasl_events_register(&a));
+ assert(yasl_private_events_register(&a));
assert(a.id != aid);
errno = 0;
- assert(!yasl_display_post(&d, &(struct yasl_message){.target_id = aid}));
+ assert(!yasl_private_display_post(&d, &(struct yasl_private_message){.target_id = aid}));
assert(errno == ENOENT);
teardown(&d, &a, &b);
}
-struct producer { struct yasl_display *display; uint64_t target; };
+struct producer { struct yasl_private_display *display; uint64_t target; };
static void *post_worker(void *ud)
{
struct producer *producer = ud;
for (uint32_t i = 0; i < 32; i++)
- assert(yasl_display_post(producer->display, &(struct yasl_message){
+ assert(yasl_private_display_post(producer->display, &(struct yasl_private_message){
.target_id = producer->target, .code = i, .payload = &released, .release = release}));
return NULL;
}
static void test_post_cleanup(void)
{
- struct yasl_display d;
+ struct yasl_private_display d;
struct yasl_surface a, b;
setup(&d, &a, &b);
owner = pthread_self();
@@ -110,26 +109,26 @@ static void test_post_cleanup(void)
struct producer producer = {&d, a.id};
assert(!pthread_create(&worker, NULL, post_worker, &producer));
assert(!pthread_join(worker, NULL));
- struct yasl_event ev;
+ struct yasl_private_event ev;
for (uint32_t i = 0; i < 8; i++) {
- assert(yasl_display_poll_event(&d, &ev));
+ assert(yasl_private_display_poll_event(&d, &ev));
assert(ev.user.code == i && ev.surface == &a);
}
assert(released == 7); /* Last event remains borrowed. */
- yasl_events_unregister(&a);
+ yasl_private_events_unregister(&a);
assert(released == 31); /* Undelivered target messages canceled. */
- assert(!yasl_display_poll_event(&d, &ev));
- assert(yasl_display_post(&d, &(struct yasl_message){.payload = &released, .release = release}));
- yasl_events_close(&d);
+ assert(!yasl_private_display_poll_event(&d, &ev));
+ assert(yasl_private_display_post(&d, &(struct yasl_private_message){.payload = &released, .release = release}));
+ yasl_private_events_close(&d);
assert(released == 33); /* Pending message and last consumed message. */
errno = 0;
- assert(!yasl_display_post(&d, &(struct yasl_message){.payload = &released, .release = release}));
+ assert(!yasl_private_display_post(&d, &(struct yasl_private_message){.payload = &released, .release = release}));
assert(errno == EINVAL && released == 33);
}
static int damage_count;
-static bool present(struct yasl_surface *s, const struct yasl_framebuffer *target,
- const struct yasl_damage_list *damage)
+static bool present(struct yasl_surface *s, const struct yasl_private_framebuffer *target,
+ const struct yasl_private_damage_list *damage)
{
(void)s; (void)target;
damage_count = damage->n;
@@ -137,64 +136,64 @@ static bool present(struct yasl_surface *s, const struct yasl_framebuffer *targe
}
static void test_damage(void)
{
- struct yasl_backend_ops present_ops = {.surface_present = present};
- struct yasl_display d = {.ops = &present_ops};
+ struct yasl_private_backend_ops present_ops = {.surface_present = present};
+ struct yasl_private_display d = {.ops = &present_ops};
struct yasl_surface s = {.display = &d, .frame_generation = 1, .drawing = true};
- struct yasl_frame frame = {.surface = &s, .target = {.generation = 1, .size = {8, 8}}};
+ struct yasl_private_frame frame = {.surface = &s, .target = {.generation = 1, .size = {8, 8}}};
s.active_frame = &frame;
- struct yasl_rect2i rects[YASL_DAMAGE_MAX + 1] = {{0}};
- assert(yasl_frame_present(&frame, NULL, 0) && damage_count == -1);
- assert(!yasl_frame_present(&frame, NULL, 0) && errno == EALREADY);
+ struct yasl_private_rect2i rects[YASL_PRIVATE_DAMAGE_MAX + 1] = {{0}};
+ assert(yasl_private_frame_present(&frame, NULL, 0) && damage_count == -1);
+ assert(!yasl_private_frame_present(&frame, NULL, 0) && errno == EALREADY);
s.frame_presented = false;
- assert(yasl_frame_present(&frame, rects, 0) && damage_count == 0);
+ assert(yasl_private_frame_present(&frame, rects, 0) && damage_count == 0);
s.frame_presented = false;
- assert(yasl_frame_present(&frame, rects, YASL_DAMAGE_MAX + 1) && damage_count == -1);
+ assert(yasl_private_frame_present(&frame, rects, YASL_PRIVATE_DAMAGE_MAX + 1) && damage_count == -1);
s.frame_presented = false;
- rects[YASL_DAMAGE_MAX].width = -1;
+ rects[YASL_PRIVATE_DAMAGE_MAX].width = -1;
errno = 0;
- assert(!yasl_frame_present(&frame, rects, YASL_DAMAGE_MAX + 1) && errno == EINVAL);
- assert(!yasl_frame_present(&frame, NULL, 1));
+ assert(!yasl_private_frame_present(&frame, rects, YASL_PRIVATE_DAMAGE_MAX + 1) && errno == EINVAL);
+ assert(!yasl_private_frame_present(&frame, NULL, 1));
s.drawing = false;
- assert(!yasl_frame_present(&frame, NULL, 0));
+ assert(!yasl_private_frame_present(&frame, NULL, 0));
}
static void test_failures(void)
{
- struct yasl_display d;
+ struct yasl_private_display d;
struct yasl_surface a, b;
setup(&d, &a, &b);
- assert(!yasl_surface_set_size(&a, (struct yasl_vector2i){0, 10}) && errno == EINVAL);
- assert(!yasl_surface_set_size(&a, (struct yasl_vector2i){10, 10}) && errno == ENOTSUP);
- assert(!yasl_surface_set_fullscreen(&a, true) && errno == ENOTSUP);
- assert(!yasl_surface_set_maximized(&a, true) && errno == ENOTSUP);
- assert(!yasl_surface_minimize(&a) && errno == ENOTSUP);
+ assert(!yasl_private_surface_set_size(&a, (struct yasl_private_vector2i){0, 10}) && errno == EINVAL);
+ assert(!yasl_private_surface_set_size(&a, (struct yasl_private_vector2i){10, 10}) && errno == ENOTSUP);
+ assert(!yasl_private_surface_set_fullscreen(&a, true) && errno == ENOTSUP);
+ assert(!yasl_private_surface_set_maximized(&a, true) && errno == ENOTSUP);
+ assert(!yasl_private_surface_minimize(&a) && errno == ENOTSUP);
a.popup = true;
- assert(!yasl_surface_minimize(&a) && errno == EINVAL);
- assert(!yasl_clipboard_set(&d, YASL_CLIPBOARD, "text") && errno == ENOTSUP);
- assert(!yasl_clipboard_request(&a, YASL_CLIPBOARD) && errno == ENOTSUP);
+ assert(!yasl_private_surface_minimize(&a) && errno == EINVAL);
+ assert(!yasl_private_clipboard_set(&d, YASL_PRIVATE_CLIPBOARD, "text") && errno == ENOTSUP);
+ assert(!yasl_private_clipboard_request(&a, YASL_PRIVATE_CLIPBOARD) && errno == ENOTSUP);
/* Queued native event allocation/validation failure must be observable. */
- assert(!yasl_events_push(&d, &a, &(struct yasl_event){
- .type = YASL_EVENT_TEXT, .surface = &a, .text = {"x", SIZE_MAX}}));
- assert(yasl_events_error(&d) == ENOMEM);
+ assert(!yasl_private_events_push(&d, &a, &(struct yasl_private_event){
+ .type = YASL_PRIVATE_EVENT_TEXT, .surface = &a, .text = {"x", SIZE_MAX}}));
+ assert(yasl_private_events_error(&d) == ENOMEM);
teardown(&d, &a, &b);
}
static void test_legacy_poll_does_not_spin(void)
{
- struct yasl_display d;
+ struct yasl_private_display d;
struct yasl_surface a, b;
setup(&d, &a, &b);
- yasl_surface_notify_state(&a, YASL_STATE_ACTIVATED);
- yasl_surface_notify_close(&b);
- assert(yasl_events_pending(&d));
- struct yasl_event ev;
- assert(!yasl_surface_poll_event(&a, &ev));
- assert(!yasl_events_pending(&d));
- assert(input_queue_push(&b.input, &(struct yasl_event){.type = YASL_EVENT_KEY, .surface = &b}));
- assert(!yasl_surface_poll_event(&a, &ev));
- assert(yasl_surface_poll_event(&b, &ev) && ev.surface == &b);
- assert(!yasl_events_pending(&d));
+ yasl_private_surface_notify_state(&a, YASL_PRIVATE_STATE_ACTIVATED);
+ yasl_private_surface_notify_close(&b);
+ assert(yasl_private_events_pending(&d));
+ struct yasl_private_event ev;
+ assert(!yasl_private_surface_poll_event(&a, &ev));
+ assert(!yasl_private_events_pending(&d));
+ assert(input_queue_push(&b.input, &(struct yasl_private_event){.type = YASL_PRIVATE_EVENT_KEY, .surface = &b}));
+ assert(!yasl_private_surface_poll_event(&a, &ev));
+ assert(yasl_private_surface_poll_event(&b, &ev) && ev.surface == &b);
+ assert(!yasl_private_events_pending(&d));
teardown(&d, &a, &b);
}
diff --git a/tests/common/test_gl.c b/tests/common/test_gl.c
new file mode 100644
index 0000000..cd51575
--- /dev/null
+++ b/tests/common/test_gl.c
@@ -0,0 +1,158 @@
+#define _POSIX_C_SOURCE 200809L
+#include <yasl/gl.h>
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+typedef const unsigned char *(*get_string_fn)(unsigned);
+typedef void (*clear_color_fn)(float, float, float, float);
+typedef void (*clear_fn)(unsigned);
+typedef void (*finish_fn)(void);
+typedef void (*read_pixels_fn)(int, int, int, int, unsigned, unsigned, void *);
+typedef void (*gen_textures_fn)(int, unsigned *);
+typedef void (*bind_texture_fn)(unsigned, unsigned);
+typedef unsigned char (*is_texture_fn)(unsigned);
+typedef void (*delete_textures_fn)(int, const unsigned *);
+
+enum { GL_VERSION_ = 0x1f02, GL_COLOR_BUFFER_BIT_ = 0x00004000,
+ GL_RGBA_ = 0x1908, GL_UNSIGNED_BYTE_ = 0x1401, GL_TEXTURE_2D_ = 0x0de1 };
+
+static yasl_surface *primary;
+static yasl_surface *shared;
+static yasl_surface *cpu;
+
+static void fail(const char *message, int line)
+{
+ const char *description = NULL;
+ enum yasl_error error = yasl_get_error(&description);
+ fprintf(stderr, "GL integration failure at line %d: %s (YASL error %d: %s, native error %d)\n",
+ line, message, error, description ? description : "none", yasl_get_native_error());
+ (void)yasl_gl_make_current(NULL);
+ if (cpu) yasl_surface_destroy(cpu);
+ if (shared) yasl_surface_destroy(shared);
+ if (primary) yasl_surface_destroy(primary);
+ yasl_shutdown();
+ exit(1);
+}
+
+#define REQUIRE(condition, message) do { if (!(condition)) fail((message), __LINE__); } while (0)
+
+#define LOAD_GL(function, type, symbol) do { \
+ union { yasl_gl_proc generic; type typed; } conversion; \
+ conversion.generic = yasl_gl_get_proc_address(symbol); \
+ REQUIRE(conversion.generic != NULL, "missing OpenGL function " symbol); \
+ function = conversion.typed; \
+} while (0)
+
+static double seconds_now(void)
+{
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) return 0;
+ return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
+}
+
+static void wait_for_size(yasl_surface *surface, int width, int height)
+{
+ double deadline = seconds_now() + 8.0;
+ int actual_width = 0, actual_height = 0;
+ while (seconds_now() < deadline) {
+ yasl_surface_get_size(surface, &actual_width, &actual_height);
+ if (actual_width == width && actual_height == height) return;
+ yasl_event event = {0};
+ int result = yasl_wait_event(&event, 50);
+ REQUIRE(result >= 0, "waiting for configure event");
+ }
+ char message[128];
+ snprintf(message, sizeof(message), "resize did not settle at %dx%d (got %dx%d)",
+ width, height, actual_width, actual_height);
+ fail(message, __LINE__);
+}
+
+int main(int argc, char **argv)
+{
+ REQUIRE(argc == 2, "expected backend argument: wayland or x11");
+ if (!strcmp(argv[1], "wayland")) REQUIRE(yasl_set_backend(YASL_BACKEND_WAYLAND), "select Wayland backend");
+ else if (!strcmp(argv[1], "x11")) REQUIRE(yasl_set_backend(YASL_BACKEND_X11), "select X11 backend");
+ else REQUIRE(false, "backend must be wayland or x11");
+
+ primary = yasl_gl_surface_create("YASL GL test", 320, 240, 0, NULL, NULL);
+ REQUIRE(primary != NULL, "create default OpenGL 3.2 surface");
+ REQUIRE(yasl_gl_make_current(primary), "make first GL context current");
+
+ get_string_fn get_string = NULL;
+ clear_color_fn clear_color = NULL;
+ clear_fn clear = NULL;
+ finish_fn finish = NULL;
+ read_pixels_fn read_pixels = NULL;
+ gen_textures_fn gen_textures = NULL;
+ bind_texture_fn bind_texture = NULL;
+ is_texture_fn is_texture = NULL;
+ delete_textures_fn delete_textures = NULL;
+ LOAD_GL(get_string, get_string_fn, "glGetString");
+ LOAD_GL(clear_color, clear_color_fn, "glClearColor");
+ LOAD_GL(clear, clear_fn, "glClear");
+ LOAD_GL(finish, finish_fn, "glFinish");
+ LOAD_GL(read_pixels, read_pixels_fn, "glReadPixels");
+ LOAD_GL(gen_textures, gen_textures_fn, "glGenTextures");
+ LOAD_GL(bind_texture, bind_texture_fn, "glBindTexture");
+ LOAD_GL(is_texture, is_texture_fn, "glIsTexture");
+ LOAD_GL(delete_textures, delete_textures_fn, "glDeleteTextures");
+ const unsigned char *version = get_string(GL_VERSION_);
+ REQUIRE(version && version[0], "query GL version string");
+
+ clear_color(0.2f, 0.4f, 0.8f, 1.0f);
+ clear(GL_COLOR_BUFFER_BIT_);
+ finish();
+ unsigned char pixel[4] = {0};
+ int pixel_width = 0, pixel_height = 0;
+ yasl_surface_get_framebuffer_size(primary, &pixel_width, &pixel_height);
+ REQUIRE(pixel_width > 0 && pixel_height > 0, "query initial framebuffer size");
+ read_pixels(pixel_width / 2, pixel_height / 2, 1, 1, GL_RGBA_, GL_UNSIGNED_BYTE_, pixel);
+ REQUIRE(abs((int)pixel[0] - 51) <= 3 && abs((int)pixel[1] - 102) <= 3 &&
+ abs((int)pixel[2] - 204) <= 3 && pixel[3] >= 250,
+ "read rendered pixel from the native GL framebuffer");
+ REQUIRE(yasl_gl_swap_interval(primary, 0), "disable swap interval");
+ REQUIRE(yasl_gl_swap_buffers(primary), "swap first GL surface");
+
+ unsigned texture = 0;
+ gen_textures(1, &texture);
+ bind_texture(GL_TEXTURE_2D_, texture);
+ REQUIRE(is_texture(texture), "create a shareable GL texture");
+
+ shared = yasl_gl_surface_create("YASL GL shared context", 160, 120, 0, NULL, primary);
+ REQUIRE(shared != NULL, "create shared GL context");
+ REQUIRE(yasl_gl_make_current(shared), "make shared context current");
+ REQUIRE(is_texture(texture), "verify context shares GL objects");
+ delete_textures(1, &texture);
+ REQUIRE(yasl_gl_make_current(primary), "restore primary GL context");
+
+ REQUIRE(yasl_surface_request_size(primary, 400, 300), "request surface resize");
+ wait_for_size(primary, 400, 300);
+ yasl_surface_get_framebuffer_size(primary, &pixel_width, &pixel_height);
+ double scale = yasl_surface_get_scale(primary);
+ REQUIRE(abs(pixel_width - (int)lround(400 * scale)) <= 1 &&
+ abs(pixel_height - (int)lround(300 * scale)) <= 1,
+ "query resized framebuffer dimensions");
+ clear_color(0.7f, 0.1f, 0.3f, 1.0f);
+ clear(GL_COLOR_BUFFER_BIT_);
+ REQUIRE(yasl_gl_swap_buffers(primary), "swap resized GL surface");
+
+ const int invalid_attributes[] = {0x7fff, 1, YASL_GL_END};
+ REQUIRE(yasl_gl_surface_create("invalid attrs", 100, 100, 0, invalid_attributes, NULL) == NULL,
+ "reject unknown GL context attributes");
+ REQUIRE(yasl_gl_surface_create("invalid CPU GL", 100, 100, YASL_SURFACE_CPU, NULL, NULL) == NULL,
+ "reject CPU flag on GL surface creation");
+ cpu = yasl_surface_create("CPU surface", 100, 100, YASL_SURFACE_CPU);
+ REQUIRE(cpu != NULL, "create CPU surface for incompatibility check");
+ REQUIRE(!yasl_gl_make_current(cpu), "reject CPU surface as a GL context");
+
+ REQUIRE(yasl_gl_make_current(NULL), "detach GL context before destruction");
+ yasl_surface_destroy(cpu); cpu = NULL;
+ yasl_surface_destroy(shared); shared = NULL;
+ yasl_surface_destroy(primary); primary = NULL;
+ yasl_shutdown();
+ return 0;
+}
diff --git a/tests/common/test_gl_options.c b/tests/common/test_gl_options.c
new file mode 100644
index 0000000..4bcd521
--- /dev/null
+++ b/tests/common/test_gl_options.c
@@ -0,0 +1,16 @@
+#include "common/gl_options.h"
+#include <assert.h>
+#include <stddef.h>
+#include <errno.h>
+int main(void)
+{
+ struct yasl_private_gl_options o;
+ assert(yasl_private_gl_parse_options(NULL,&o));assert(o.major==3&&o.minor==2&&o.red_bits==8);
+ const int es[]={YASL_PRIVATE_GL_API,YASL_PRIVATE_GL_OPENGL_ES,YASL_PRIVATE_GL_MAJOR,3,YASL_PRIVATE_GL_MINOR,0,YASL_PRIVATE_GL_END};
+ assert(yasl_private_gl_parse_options(es,&o)&&o.api==YASL_PRIVATE_GL_OPENGL_ES&&o.minor==0);
+ const int invalid[]={99,0,YASL_PRIVATE_GL_END};
+ assert(!yasl_private_gl_parse_options(invalid,&o)&&errno==EINVAL);
+ const int negative[]={YASL_PRIVATE_GL_DEPTH_BITS,-1,YASL_PRIVATE_GL_END};
+ assert(!yasl_private_gl_parse_options(negative,&o));
+ return 0;
+}
diff --git a/tests/common/test_hello_example.c b/tests/common/test_hello_example.c
deleted file mode 100644
index 350b3bb..0000000
--- a/tests/common/test_hello_example.c
+++ /dev/null
@@ -1,20 +0,0 @@
-/* Exercise hello's real frame callback; automated exit belongs in the test. */
-#define main hello_example_main
-#include "../../examples/hello.c"
-#undef main
-
-static void draw_once(void *ud, struct yasl_surface *surface, struct yasl_frame *frame)
-{
- draw(ud, surface, frame);
- if (!failed) yasl_surface_destroy(surface);
-}
-
-int main(void)
-{
- struct yasl_window_spec window = {.title = "Hello YASL", .size = {640, 480}};
- struct yasl_app_desc desc = {
- .app_id = "org.yasl.test.hello", .window = &window,
- .frame = draw_once,
- };
- return yasl_run(&desc) || failed ? 1 : 0;
-}
diff --git a/tests/common/test_input_alloc.c b/tests/common/test_input_alloc.c
index 7aba4b2..12e7656 100644
--- a/tests/common/test_input_alloc.c
+++ b/tests/common/test_input_alloc.c
@@ -5,7 +5,7 @@
/* This executable compiles input.c with malloc redirected here. */
static int allocations = -1;
-void *yasl_test_malloc(size_t bytes)
+void *yasl_private_test_malloc(size_t bytes)
{
if (allocations == 0) return NULL;
if (allocations > 0) allocations--;
@@ -13,19 +13,19 @@ void *yasl_test_malloc(size_t bytes)
}
/* input.c timestamps events, but this executable links no event loop. */
-uint64_t yasl_loop_now_ns(void) { return 1; }
+uint64_t yasl_private_loop_now_ns(void) { return 1; }
int main(void)
{
struct yasl_surface *surface = (struct yasl_surface *)(uintptr_t)1;
for (int failure = 0; failure < 2; failure++) {
struct input_queue q = {0};
- struct yasl_event in = {.surface = surface, .type = YASL_EVENT_PASTE,
+ struct yasl_private_event in = {.surface = surface, .type = YASL_PRIVATE_EVENT_PASTE,
.text = {"payload", 7}}, out;
/* Fail either payload allocation or the initial event-array allocation. */
allocations = failure;
assert(!input_queue_push(&q, &in));
- assert(input_queue_pop(&q, &out) && out.type == YASL_EVENT_INPUT_RESET);
+ assert(input_queue_pop(&q, &out) && out.type == YASL_PRIVATE_EVENT_INPUT_RESET);
assert(out.surface == surface);
assert(!input_queue_pop(&q, &out));
input_queue_clear(&q);
@@ -40,15 +40,15 @@ int main(void)
assert(strcmp(last, "payload") == 0);
assert(!q.count && q.reset_pending);
allocations = -1;
- in = (struct yasl_event){.surface = surface, .type = YASL_EVENT_KEY,
+ in = (struct yasl_private_event){.surface = surface, .type = YASL_PRIVATE_EVENT_KEY,
.key.action = YASL_KEY_RELEASE};
assert(input_queue_push(&q, &in));
- assert(input_queue_pop(&q, &out) && out.type == YASL_EVENT_INPUT_RESET);
+ assert(input_queue_pop(&q, &out) && out.type == YASL_PRIVATE_EVENT_INPUT_RESET);
assert(input_queue_pop(&q, &out) && out.key.action == YASL_KEY_RELEASE);
input_queue_clear(&q);
/* A lost event clears held buttons along with the stream. */
- in = (struct yasl_event){.surface = surface, .type = YASL_EVENT_POINTER_DOWN};
+ in = (struct yasl_private_event){.surface = surface, .type = YASL_PRIVATE_EVENT_POINTER_DOWN};
assert(input_queue_push(&q, &in) && q.buttons);
for (size_t i = 1; i < q.capacity; i++) assert(input_queue_push(&q, &in));
allocations = 0;
diff --git a/tests/common/test_keyboard.c b/tests/common/test_keyboard.c
index f2fc8f2..7406c1d 100644
--- a/tests/common/test_keyboard.c
+++ b/tests/common/test_keyboard.c
@@ -9,27 +9,27 @@ int main(void)
{
/* Same physical positions across native encodings, including keypad and
* left/right modifiers. macOS A is code zero, which is not UNKNOWN. */
- assert(yasl_key_from_evdev(30) == YASL_KEY_A);
- assert(yasl_key_from_cocoa(kVK_ANSI_A) == YASL_KEY_A);
- assert(yasl_key_from_xkb_name("AC01") == YASL_KEY_A);
- assert(yasl_key_from_evdev(1) == YASL_KEY_ESCAPE);
- assert(yasl_key_from_cocoa(kVK_Escape) == YASL_KEY_ESCAPE);
- assert(yasl_key_from_xkb_name("ESC\0") == YASL_KEY_ESCAPE);
- assert(yasl_key_from_evdev(96) == YASL_KEY_KP_ENTER);
- assert(yasl_key_from_cocoa(kVK_ANSI_KeypadEnter) == YASL_KEY_KP_ENTER);
- assert(yasl_key_from_cocoa(kVK_Return) == YASL_KEY_ENTER);
- assert(yasl_key_from_evdev(100) == YASL_KEY_RIGHT_ALT);
- assert(yasl_key_from_cocoa(kVK_RightOption) == YASL_KEY_RIGHT_ALT);
- assert(yasl_key_from_evdev(42) == YASL_KEY_LEFT_SHIFT);
- assert(yasl_key_from_cocoa(kVK_Shift) == YASL_KEY_LEFT_SHIFT);
- assert(yasl_key_from_evdev(86) == YASL_KEY_ISO_BACKSLASH);
- assert(yasl_key_from_cocoa(kVK_ISO_Section) == YASL_KEY_ISO_BACKSLASH);
- assert(yasl_key_from_evdev(0) == YASL_KEY_UNKNOWN);
- assert(yasl_key_from_evdev(UINT32_MAX) == YASL_KEY_UNKNOWN);
- assert(yasl_key_from_cocoa(UINT32_MAX) == YASL_KEY_UNKNOWN);
- assert(yasl_key_from_xkb_name("ZZZZ") == YASL_KEY_UNKNOWN);
- assert(yasl_key_label(0x1f600) == 0x1f600);
- assert(yasl_key_label(0xd800) == 0 && yasl_key_label(0x110000) == 0);
- assert(yasl_key_label(0xf700) == 0 && yasl_key_label('\t') == 0);
+ assert(yasl_private_key_from_evdev(30) == YASL_KEY_A);
+ assert(yasl_private_key_from_cocoa(kVK_ANSI_A) == YASL_KEY_A);
+ assert(yasl_private_key_from_xkb_name("AC01") == YASL_KEY_A);
+ assert(yasl_private_key_from_evdev(1) == YASL_KEY_ESCAPE);
+ assert(yasl_private_key_from_cocoa(kVK_Escape) == YASL_KEY_ESCAPE);
+ assert(yasl_private_key_from_xkb_name("ESC\0") == YASL_KEY_ESCAPE);
+ assert(yasl_private_key_from_evdev(96) == YASL_KEY_KP_ENTER);
+ assert(yasl_private_key_from_cocoa(kVK_ANSI_KeypadEnter) == YASL_KEY_KP_ENTER);
+ assert(yasl_private_key_from_cocoa(kVK_Return) == YASL_KEY_ENTER);
+ assert(yasl_private_key_from_evdev(100) == YASL_KEY_RIGHT_ALT);
+ assert(yasl_private_key_from_cocoa(kVK_RightOption) == YASL_KEY_RIGHT_ALT);
+ assert(yasl_private_key_from_evdev(42) == YASL_KEY_LEFT_SHIFT);
+ assert(yasl_private_key_from_cocoa(kVK_Shift) == YASL_KEY_LEFT_SHIFT);
+ assert(yasl_private_key_from_evdev(86) == YASL_KEY_ISO_BACKSLASH);
+ assert(yasl_private_key_from_cocoa(kVK_ISO_Section) == YASL_KEY_ISO_BACKSLASH);
+ assert(yasl_private_key_from_evdev(0) == YASL_KEY_UNKNOWN);
+ assert(yasl_private_key_from_evdev(UINT32_MAX) == YASL_KEY_UNKNOWN);
+ assert(yasl_private_key_from_cocoa(UINT32_MAX) == YASL_KEY_UNKNOWN);
+ assert(yasl_private_key_from_xkb_name("ZZZZ") == YASL_KEY_UNKNOWN);
+ assert(yasl_private_key_label(0x1f600) == 0x1f600);
+ assert(yasl_private_key_label(0xd800) == 0 && yasl_private_key_label(0x110000) == 0);
+ assert(yasl_private_key_label(0xf700) == 0 && yasl_private_key_label('\t') == 0);
return 0;
}
diff --git a/tests/common/test_loop.c b/tests/common/test_loop.c
index f982e5e..5c49691 100644
--- a/tests/common/test_loop.c
+++ b/tests/common/test_loop.c
@@ -1,6 +1,6 @@
#define _GNU_SOURCE
#define _DARWIN_C_SOURCE
-#include "yasl/loop.h"
+#include "common/private_loop.h"
#include <assert.h>
#include <errno.h>
@@ -29,7 +29,7 @@ static void on_alarm(int signo)
}
struct post_log {
- struct yasl_loop *loop;
+ struct yasl_private_loop *loop;
char order[8];
int count;
};
@@ -46,7 +46,7 @@ static void post_a(void *ud)
{
struct post_log *log = ud;
log->order[log->count++] = 'a';
- assert(yasl_loop_post(log->loop, post_c, log));
+ assert(yasl_private_loop_post(log->loop, post_c, log));
}
static void post_b(void *ud)
@@ -57,28 +57,28 @@ static void post_b(void *ud)
static void test_post_order(void)
{
- struct yasl_loop *l = yasl_loop_create();
+ struct yasl_private_loop *l = yasl_private_loop_create();
assert(l);
- assert(!yasl_loop_post(NULL, post_b, NULL));
- assert(!yasl_loop_post(l, NULL, NULL));
+ assert(!yasl_private_loop_post(NULL, post_b, NULL));
+ assert(!yasl_private_loop_post(l, NULL, NULL));
struct post_log log = {.loop = l};
- assert(yasl_loop_post(l, post_a, &log));
- assert(yasl_loop_post(l, post_b, &log));
- assert(yasl_loop_step(l, 0) >= 1);
+ assert(yasl_private_loop_post(l, post_a, &log));
+ assert(yasl_private_loop_post(l, post_b, &log));
+ assert(yasl_private_loop_step(l, 0) >= 1);
/* A post made while posts run waits for a later step. */
assert(log.count == 2 && memcmp(log.order, "ab", 2) == 0);
- assert(yasl_loop_step(l, 0) >= 1);
+ assert(yasl_private_loop_step(l, 0) >= 1);
assert(log.count == 3 && log.order[2] == 'c');
/* Destroy drops posts that never ran. */
- assert(yasl_loop_post(l, post_b, &log));
- yasl_loop_destroy(l);
+ assert(yasl_private_loop_post(l, post_b, &log));
+ yasl_private_loop_destroy(l);
assert(log.count == 3);
}
struct thread_ctx {
- struct yasl_loop *loop;
+ struct yasl_private_loop *loop;
bool kick;
int signo;
_Atomic bool ran;
@@ -97,35 +97,35 @@ static void *wake_thread(void *ud)
if (ctx->signo)
kill(getpid(), ctx->signo);
else if (ctx->kick)
- yasl_loop_kick(ctx->loop);
+ yasl_private_loop_kick(ctx->loop);
else
- assert(yasl_loop_post(ctx->loop, mark_ran, ctx));
+ assert(yasl_private_loop_post(ctx->loop, mark_ran, ctx));
return NULL;
}
static void test_cross_thread(void)
{
- struct yasl_loop *l = yasl_loop_create();
+ struct yasl_private_loop *l = yasl_private_loop_create();
assert(l);
struct thread_ctx post = {.loop = l};
pthread_t t;
assert(pthread_create(&t, NULL, wake_thread, &post) == 0);
while (!post.ran)
- assert(yasl_loop_step(l, -1) >= 0);
+ assert(yasl_private_loop_step(l, -1) >= 0);
pthread_join(t, NULL);
/* A kick ends an indefinite wait without running anything. */
struct thread_ctx kick = {.loop = l, .kick = true};
assert(pthread_create(&t, NULL, wake_thread, &kick) == 0);
- assert(yasl_loop_step(l, -1) >= 1);
+ assert(yasl_private_loop_step(l, -1) >= 1);
pthread_join(t, NULL);
- yasl_loop_destroy(l);
+ yasl_private_loop_destroy(l);
}
struct signal_log {
- struct yasl_loop *loop;
+ struct yasl_private_loop *loop;
int seen[NSIG];
int total;
bool unwatch;
@@ -138,7 +138,7 @@ static void on_signal(void *ud, int signo)
log->seen[signo]++;
log->total++;
if (log->unwatch)
- yasl_loop_unwatch_signals(log->loop);
+ yasl_private_loop_unwatch_signals(log->loop);
}
static volatile sig_atomic_t previous_handler_hits;
@@ -149,10 +149,10 @@ static void previous_handler(int signo)
previous_handler_hits++;
}
-static void step_until(struct yasl_loop *l, const int *counter, int want)
+static void step_until(struct yasl_private_loop *l, const int *counter, int want)
{
while (*counter < want)
- assert(yasl_loop_step(l, -1) >= 0);
+ assert(yasl_private_loop_step(l, -1) >= 0);
}
static void test_signals(void)
@@ -161,30 +161,30 @@ static void test_signals(void)
sigemptyset(&prev.sa_mask);
assert(sigaction(SIGUSR2, &prev, NULL) == 0);
- struct yasl_loop *l = yasl_loop_create();
- struct yasl_loop *other = yasl_loop_create();
+ struct yasl_private_loop *l = yasl_private_loop_create();
+ struct yasl_private_loop *other = yasl_private_loop_create();
assert(l && other);
struct signal_log log = {.loop = l};
const int bad[] = {SIGKILL};
errno = 0;
- assert(!yasl_loop_watch_signals(l, bad, 1, on_signal, &log) && errno == EINVAL);
+ assert(!yasl_private_loop_watch_signals(l, bad, 1, on_signal, &log) && errno == EINVAL);
const int sigs[] = {SIGUSR1, SIGUSR2, SIGCHLD};
errno = 0;
- assert(!yasl_loop_watch_signals(l, sigs, 0, on_signal, &log) && errno == EINVAL);
+ assert(!yasl_private_loop_watch_signals(l, sigs, 0, on_signal, &log) && errno == EINVAL);
errno = 0;
- assert(!yasl_loop_watch_signals(l, sigs, 3, NULL, &log) && errno == EINVAL);
- assert(yasl_loop_watch_signals(l, sigs, 3, on_signal, &log));
+ assert(!yasl_private_loop_watch_signals(l, sigs, 3, NULL, &log) && errno == EINVAL);
+ assert(yasl_private_loop_watch_signals(l, sigs, 3, on_signal, &log));
/* One signal belongs to one loop at a time. */
errno = 0;
- assert(!yasl_loop_watch_signals(other, sigs, 1, on_signal, &log) && errno == EBUSY);
+ assert(!yasl_private_loop_watch_signals(other, sigs, 1, on_signal, &log) && errno == EBUSY);
/* Deliveries before the callback runs coalesce per signal. */
raise(SIGUSR1);
raise(SIGUSR1);
raise(SIGUSR2);
- assert(yasl_loop_step(l, 0) >= 1);
+ assert(yasl_private_loop_step(l, 0) >= 1);
assert(log.seen[SIGUSR1] == 1 && log.seen[SIGUSR2] == 1);
assert(previous_handler_hits == 0);
@@ -205,11 +205,11 @@ static void test_signals(void)
/* Replacing the watch restores the signals it drops. */
const int only_usr1[] = {SIGUSR1};
- assert(yasl_loop_watch_signals(l, only_usr1, 1, on_signal, &log));
+ assert(yasl_private_loop_watch_signals(l, only_usr1, 1, on_signal, &log));
raise(SIGUSR2);
assert(previous_handler_hits == 1);
- assert(yasl_loop_watch_signals(other, (const int[]){SIGUSR2}, 1, on_signal, &log));
- yasl_loop_unwatch_signals(other);
+ assert(yasl_private_loop_watch_signals(other, (const int[]){SIGUSR2}, 1, on_signal, &log));
+ yasl_private_loop_unwatch_signals(other);
assert(previous_handler_hits == 1);
raise(SIGUSR2);
assert(previous_handler_hits == 2);
@@ -221,15 +221,15 @@ static void test_signals(void)
struct sigaction now;
assert(sigaction(SIGUSR1, NULL, &now) == 0);
assert(now.sa_handler == SIG_DFL);
- yasl_loop_unwatch_signals(l);
+ yasl_private_loop_unwatch_signals(l);
/* Destroy unwatches. */
log.unwatch = false;
- assert(yasl_loop_watch_signals(l, only_usr1, 1, on_signal, &log));
- yasl_loop_destroy(l);
+ assert(yasl_private_loop_watch_signals(l, only_usr1, 1, on_signal, &log));
+ yasl_private_loop_destroy(l);
assert(sigaction(SIGUSR1, NULL, &now) == 0);
assert(now.sa_handler == SIG_DFL);
- yasl_loop_destroy(other);
+ yasl_private_loop_destroy(other);
struct sigaction dfl = {.sa_handler = SIG_DFL};
sigemptyset(&dfl.sa_mask);
diff --git a/tests/common/test_selection.c b/tests/common/test_selection.c
index d1b3eb8..9772923 100644
--- a/tests/common/test_selection.c
+++ b/tests/common/test_selection.c
@@ -1,5 +1,5 @@
#define _POSIX_C_SOURCE 200809L
-#include "yasl/yasl.h"
+#include "common/types.h"
#include <assert.h>
#include <errno.h>
@@ -7,27 +7,27 @@
int main(void)
{
- struct yasl_display_spec spec = {.backend = (enum yasl_backend)999};
+ struct yasl_private_display_spec spec = {.backend = (enum yasl_private_backend)999};
errno = 0;
- assert(!yasl_display_open(NULL, &spec));
+ assert(!yasl_private_display_open(NULL, &spec));
assert(errno == EINVAL);
- assert(yasl_display_backend(NULL) == YASL_BACKEND_AUTO);
+ assert(yasl_private_display_backend(NULL) == YASL_PRIVATE_BACKEND_AUTO);
#if !YASL_HAS_COCOA
- spec.backend = YASL_BACKEND_COCOA;
+ spec.backend = YASL_PRIVATE_BACKEND_COCOA;
errno = 0;
- assert(!yasl_display_open(NULL, &spec));
+ assert(!yasl_private_display_open(NULL, &spec));
assert(errno == ENOSYS);
#endif
#if !YASL_HAS_WAYLAND
- spec.backend = YASL_BACKEND_WAYLAND;
+ spec.backend = YASL_PRIVATE_BACKEND_WAYLAND;
errno = 0;
- assert(!yasl_display_open(NULL, &spec));
+ assert(!yasl_private_display_open(NULL, &spec));
assert(errno == ENOSYS);
#endif
#if !YASL_HAS_X11
- spec.backend = YASL_BACKEND_X11;
+ spec.backend = YASL_PRIVATE_BACKEND_X11;
errno = 0;
- assert(!yasl_display_open(NULL, &spec));
+ assert(!yasl_private_display_open(NULL, &spec));
assert(errno == ENOSYS);
#endif
return 0;
diff --git a/tests/common/test_surface_integration.c b/tests/common/test_surface_integration.c
new file mode 100644
index 0000000..2752208
--- /dev/null
+++ b/tests/common/test_surface_integration.c
@@ -0,0 +1,90 @@
+#define _POSIX_C_SOURCE 200809L
+#include <yasl/yasl.h>
+#include <yasl/cpu.h>
+#include "common/backend.h"
+#include <assert.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <stdio.h>
+static void drain(void)
+{
+ yasl_event e;int n=0;
+ while(yasl_wait_event(&e,0)>0)assert(++n<1000);
+}
+static void *wake_thread(void *unused)
+{ (void)unused;struct timespec delay={0,20000000};nanosleep(&delay,NULL);yasl_wake();return NULL; }
+static void *wrong_thread(void *data)
+{ assert(!yasl_surface_set_title(data,"wrong"));assert(yasl_get_error(NULL)==YASL_ERROR_WRONG_THREAD);return NULL; }
+static int refreshes;
+static void refresh(yasl_surface *s,void *ud)
+{
+ (void)ud;refreshes++;
+ yasl_event e;assert(yasl_wait_event(&e,0)==-1);assert(yasl_get_error(NULL)==YASL_ERROR_BUSY);
+ yasl_surface_destroy(s);assert(yasl_get_error(NULL)==YASL_ERROR_BUSY);
+}
+int main(int argc,char **argv)
+{
+ assert(argc==2);
+ enum yasl_backend backend=!strcmp(argv[1],"wayland")?YASL_BACKEND_WAYLAND:!strcmp(argv[1],"x11")?YASL_BACKEND_X11:YASL_BACKEND_COCOA;
+ assert(yasl_set_backend(backend));assert(yasl_set_app_id("org.yasl.surface-test"));
+ yasl_surface *s=yasl_surface_create("CPU contract",96,80,YASL_SURFACE_CPU);
+ if(!s){const char *why;yasl_get_error(&why);fprintf(stderr,"surface: %s native=%d\n",why,yasl_get_native_error());return 1;}
+ assert(yasl_surface_backend(s)==backend);
+ /* The first events report the window size, then ask for a frame. */
+ yasl_event e;int fw=0,fh=0;bool window=false;
+ for(int i=0;i<1000;i++) {
+ assert(yasl_wait_event(&e,i?20:0)>=0);
+ if(e.type==YASL_EVENT_WINDOW){window=true;fw=e.window.pixel_width;fh=e.window.pixel_height;}
+ if(e.type==YASL_EVENT_REFRESH){assert(window&&fw>0&&fh>0&&e.surface==s);break;}
+ }
+ assert(e.type==YASL_EVENT_REFRESH);
+ int w,h;yasl_surface_get_framebuffer_size(s,&w,&h);assert(w==fw&&h==fh);
+ assert(!yasl_set_backend(backend));assert(yasl_get_error(NULL)==YASL_ERROR_BUSY);
+ int pw,ph;uint32_t *pixels=yasl_cpu_pixels(s,&pw,&ph);assert(pixels&&pw==w&&ph==h);
+ assert(pixels[0]==0&&pixels[(size_t)w*h-1]==0);
+ for(size_t i=0;i<(size_t)w*h;i++)pixels[i]=0xff102030u;
+ pixels[(size_t)3*w+2]=0xffaabbccu;
+ assert(yasl_cpu_present(s));
+ assert(yasl_cpu_pixels(s,&pw,&ph)==pixels&&pixels[0]==0xff102030u&&pixels[(size_t)3*w+2]==0xffaabbccu);
+ drain();
+ pthread_t thread;assert(!pthread_create(&thread,NULL,wrong_thread,s));pthread_join(thread,NULL);
+ assert(!pthread_create(&thread,NULL,wake_thread,NULL));
+ int n=yasl_wait_event(&e,500);assert(n>=0);pthread_join(thread,NULL);
+ bool ime=yasl_surface_set_text_input(s,true,YASL_TEXT_NORMAL);
+ if(!ime)assert(yasl_get_error(NULL)==YASL_ERROR_UNSUPPORTED);
+ bool snapshot=yasl_surface_set_surrounding_text(s,"a\xc3\xa9z",4,1,3,7);
+ if(ime)assert(snapshot);
+ else assert(!snapshot&&yasl_get_error(NULL)==YASL_ERROR_UNSUPPORTED);
+ assert(!yasl_surface_set_surrounding_text(s,"a\xc3\xa9z",4,2,3,8));
+ assert(!yasl_surface_set_surrounding_text(s,"\xc0\xaf",2,0,2,8));
+ drain();
+ struct yasl_private_event pen={.type=YASL_PRIVATE_EVENT_PEN,.surface=s,
+ .pen={.device_id=10,.tool_id=20,.x=12.5,.y=9.25,.pressure=0.3,
+ .axes_valid=YASL_PRIVATE_PEN_PRESSURE,.action=YASL_PRIVATE_PEN_DOWN,.in_contact=true}};
+ assert(yasl_private_events_push(s->display,s,&pen));
+ assert(yasl_wait_event(&e,0)==1&&e.type==YASL_EVENT_PEN&&e.pen.device_id==10&&e.pen.pressure==0.3&&e.pen.in_contact);
+ struct yasl_private_event text={.type=YASL_PRIVATE_EVENT_TEXT,.surface=s,.text={.data="owned",.len=5}};
+ assert(yasl_private_events_push(s->display,s,&text));assert(yasl_wait_event(&e,0)==1&&e.type==YASL_EVENT_TEXT);
+ assert(e.text.length==5&&!strcmp(e.text.data,"owned"));
+ /* Unchanged focus folds into no event; a change reports the activated state. */
+ unsigned active=yasl_surface_state(s)&YASL_STATE_ACTIVATED;
+ struct yasl_private_event focus={.type=YASL_PRIVATE_EVENT_FOCUS,.surface=s,.focus={.focused=!active}};
+ assert(yasl_private_events_push(s->display,s,&focus));assert(yasl_wait_event(&e,0)==1&&e.type==YASL_EVENT_WINDOW);
+ assert((e.window.state&YASL_STATE_ACTIVATED)==(active?0u:YASL_STATE_ACTIVATED)&&e.window.pixel_width==w);
+ assert(yasl_private_events_push(s->display,s,&focus));assert(yasl_wait_event(&e,0)==0&&e.type==YASL_EVENT_NONE);
+ struct yasl_private_event hover={.type=YASL_PRIVATE_EVENT_DROP_HOVER,.surface=s,.drop={YASL_PRIVATE_DROP_LEAVE,{0,0}}};
+ assert(yasl_private_events_push(s->display,s,&hover));
+ assert(yasl_wait_event(&e,0)==1&&e.type==YASL_EVENT_DROP&&e.drop.action==YASL_DROP_LEAVE);
+ yasl_surface_set_refresh_callback(s,refresh,NULL);
+ assert(yasl_cpu_present(s));
+ drain();assert(refreshes>0);
+ yasl_surface_set_refresh_callback(s,NULL,NULL);
+ yasl_surface_destroy(s);
+ assert(!yasl_set_backend(backend));
+ s=yasl_surface_create("shared backend remains",64,64,YASL_SURFACE_CPU);assert(s);
+ yasl_shutdown();assert(yasl_set_backend(backend));
+ s=yasl_surface_create("reinitialized",64,64,YASL_SURFACE_CPU);assert(s);yasl_shutdown();
+ puts("public surface lifecycle, pixels, ownership, input, wake and refresh passed");return 0;
+}
diff --git a/tests/common/test_tablet_example.c b/tests/common/test_tablet_example.c
deleted file mode 100644
index e291b2a..0000000
--- a/tests/common/test_tablet_example.c
+++ /dev/null
@@ -1,65 +0,0 @@
-#define main tablet_example_main
-#include "../../examples/tablet.c"
-#undef main
-#include <assert.h>
-
-static void pointer(struct app *app, enum yasl_event_type type,
- enum yasl_pointer_device device, enum yasl_pointer_button button, int x, int y, uint16_t pressure)
-{
- struct yasl_event event = {
- .type = type,
- .pointer = {
- .pos = (struct yasl_vector2i){x, y},
- .device = device,
- .button = button,
- .pen = { .pressure = pressure },
- },
- };
- input(app, &event);
-}
-
-int main(void)
-{
- struct app app = {0};
- assert(resize_canvas(&app, (struct yasl_geometry){{400, 300}, 120}));
- pointer(&app, YASL_EVENT_POINTER_MOTION, YASL_PTR_DEV_PEN, YASL_BUTTON_BACK, 80, 160, 65535);
- /* Hover never paints. */
- assert(app.canvas[(size_t)160 * app.geometry.size.x + 80] == PAPER);
- pointer(&app, YASL_EVENT_POINTER_DOWN, YASL_PTR_DEV_PEN, YASL_BUTTON_SECONDARY, 80, 160, 65535);
- /* Side button isn't the tip. */
- assert(!app.down &&
- app.canvas[(size_t)160 * app.geometry.size.x + 80] == PAPER);
- pointer(&app, YASL_EVENT_POINTER_DOWN, YASL_PTR_DEV_PEN, YASL_BUTTON_PRIMARY, 80, 160, 0);
- assert(app.down &&
- app.canvas[(size_t)160 * app.geometry.size.x + 80] == INK &&
- app.canvas[(size_t)160 * app.geometry.size.x + 90] == PAPER);
- pointer(&app, YASL_EVENT_POINTER_MOTION, YASL_PTR_DEV_PEN, YASL_BUTTON_PRIMARY, 80, 160, 65535);
- /* Stationary pressure widens brush. */
- assert(app.canvas[(size_t)160 * app.geometry.size.x + 100] == INK);
- pointer(&app, YASL_EVENT_POINTER_MOTION, YASL_PTR_DEV_PEN, YASL_BUTTON_PRIMARY, 180, 160, 20000);
- /* Segments are continuous. */
- assert(app.canvas[(size_t)160 * app.geometry.size.x + 130] == INK);
- pointer(&app, YASL_EVENT_POINTER_UP, YASL_PTR_DEV_PEN, YASL_BUTTON_PRIMARY, 180, 160, 0);
- pointer(&app, YASL_EVENT_POINTER_MOTION, YASL_PTR_DEV_PEN, YASL_BUTTON_BACK, 250, 160, 65535);
- assert(!app.down && app.canvas[(size_t)160 * app.geometry.size.x + 250] == PAPER);
- pointer(&app, YASL_EVENT_POINTER_DOWN, YASL_PTR_DEV_ERASER, YASL_BUTTON_PRIMARY, 80, 160, 65535);
- assert(app.canvas[(size_t)160 * app.geometry.size.x + 80] == PAPER &&
- app.canvas[(size_t)160 * app.geometry.size.x + 130] == INK);
- struct yasl_event reset = {.type = YASL_EVENT_INPUT_RESET};
- input(&app, &reset);
- pointer(&app, YASL_EVENT_POINTER_MOTION, YASL_PTR_DEV_ERASER, YASL_BUTTON_PRIMARY, 130, 160, 65535);
- assert(!app.down && app.canvas[(size_t)160 * app.geometry.size.x + 130] == INK);
- pointer(&app, YASL_EVENT_POINTER_DOWN, YASL_PTR_DEV_MOUSE, YASL_BUTTON_PRIMARY, 300, 200, 0);
- assert(app.canvas[(size_t)200 * app.geometry.size.x + 300] == INK);
- struct yasl_event blur = {.type = YASL_EVENT_FOCUS, .focus.focused = false};
- input(&app, &blur);
- assert(!app.down);
- assert(resize_canvas(&app, (struct yasl_geometry){{800, 600}, 240}));
- /* DPI preserves logical drawing. */
- assert(app.canvas[(size_t)400 * app.geometry.size.x + 600] == INK &&
- !app.previous);
- clear_canvas(&app);
- assert(app.canvas[(size_t)400 * app.geometry.size.x + 600] == PAPER);
- free(app.canvas);
- return 0;
-}
diff --git a/tests/common/test_timers.c b/tests/common/test_timers.c
index 8c92158..2c3542f 100644
--- a/tests/common/test_timers.c
+++ b/tests/common/test_timers.c
@@ -1,6 +1,6 @@
#define _POSIX_C_SOURCE 200809L
#include "common/loop.h"
-#include "yasl/yasl.h"
+#include "common/types.h"
#include <assert.h>
#include <stdio.h>
@@ -9,15 +9,15 @@
struct test_ctx {
int count;
uint64_t last_time;
- yasl_timer_id id;
- yasl_timer_id other_id;
- struct yasl_loop *loop;
+ yasl_private_timer_id id;
+ yasl_private_timer_id other_id;
+ struct yasl_private_loop *loop;
struct yasl_surface *surface;
bool done;
};
/* 1. Single-shot timer: returns 0, fires exactly once. */
-static uint64_t oneshot_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
+static uint64_t oneshot_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns)
{
struct test_ctx *ctx = ud;
assert(id == ctx->id);
@@ -27,7 +27,7 @@ static uint64_t oneshot_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
}
/* 2. Repeating timer: ticks N times, then returns 0. */
-static uint64_t repeat_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
+static uint64_t repeat_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns)
{
struct test_ctx *ctx = ud;
assert(id == ctx->id);
@@ -41,7 +41,7 @@ static uint64_t repeat_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
}
/* 3. Rescheduling timer: changes interval dynamically. */
-static uint64_t reschedule_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
+static uint64_t reschedule_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns)
{
struct test_ctx *ctx = ud;
assert(id == ctx->id);
@@ -58,7 +58,7 @@ static uint64_t reschedule_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
}
/* 4. Timer adding another timer from inside callback. */
-static uint64_t child_timer_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
+static uint64_t child_timer_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns)
{
(void)interval_ns;
struct test_ctx *ctx = ud;
@@ -67,40 +67,40 @@ static uint64_t child_timer_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
return 0;
}
-static uint64_t parent_timer_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
+static uint64_t parent_timer_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns)
{
(void)interval_ns;
struct test_ctx *ctx = ud;
assert(id == ctx->id);
ctx->count += 1;
- ctx->other_id = yasl_loop_add_timer(ctx->loop, 1000000ull, child_timer_cb, ctx);
+ ctx->other_id = yasl_private_loop_add_timer(ctx->loop, 1000000ull, child_timer_cb, ctx);
assert(ctx->other_id > 0);
return 0;
}
/* 5. Timer deleting another timer from inside callback. */
-static uint64_t victim_timer_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
+static uint64_t victim_timer_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns)
{
(void)ud; (void)id; (void)interval_ns;
assert(false && "Victim timer should have been cancelled before firing!");
return 0;
}
-static uint64_t killer_timer_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
+static uint64_t killer_timer_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns)
{
(void)id; (void)interval_ns;
struct test_ctx *ctx = ud;
ctx->count++;
- assert(yasl_loop_del_timer(ctx->loop, ctx->other_id));
+ assert(yasl_private_loop_del_timer(ctx->loop, ctx->other_id));
return 0;
}
/* 6. Millisecond convenience conversion uses the same timer callback. */
-static uint64_t ms_timer_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
+static uint64_t ms_timer_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns)
{
struct test_ctx *ctx = ud;
assert(id == ctx->id);
- assert(interval_ns == YASL_MS_TO_NS(5));
+ assert(interval_ns == YASL_PRIVATE_MS_TO_NS(5));
ctx->count++;
if (ctx->count >= 3) {
ctx->done = true;
@@ -111,94 +111,94 @@ static uint64_t ms_timer_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
static void test_loop_timers(void)
{
- struct yasl_loop *l = yasl_loop_create();
+ struct yasl_private_loop *l = yasl_private_loop_create();
assert(l);
/* Invalid arguments */
- assert(yasl_loop_add_timer(NULL, 1000, oneshot_cb, NULL) == 0);
- assert(yasl_loop_add_timer(l, 0, oneshot_cb, NULL) == 0);
- assert(yasl_loop_add_timer(l, 1000, NULL, NULL) == 0);
- assert(!yasl_loop_del_timer(NULL, 1));
- assert(!yasl_loop_del_timer(l, 0));
- assert(!yasl_loop_del_timer(l, 99999));
- assert(yasl_loop_next_timeout_ms(l) == -1);
+ assert(yasl_private_loop_add_timer(NULL, 1000, oneshot_cb, NULL) == 0);
+ assert(yasl_private_loop_add_timer(l, 0, oneshot_cb, NULL) == 0);
+ assert(yasl_private_loop_add_timer(l, 1000, NULL, NULL) == 0);
+ assert(!yasl_private_loop_del_timer(NULL, 1));
+ assert(!yasl_private_loop_del_timer(l, 0));
+ assert(!yasl_private_loop_del_timer(l, 99999));
+ assert(yasl_private_loop_next_timeout_ms(l) == -1);
/* Test 1: Single-shot */
struct test_ctx ctx1 = { .loop = l };
- ctx1.id = yasl_loop_add_timer(l, 5000000ull, oneshot_cb, &ctx1);
+ ctx1.id = yasl_private_loop_add_timer(l, 5000000ull, oneshot_cb, &ctx1);
assert(ctx1.id > 0);
- assert(yasl_loop_next_timeout_ms(l) >= 0);
+ assert(yasl_private_loop_next_timeout_ms(l) >= 0);
while (ctx1.count == 0) {
- int handled = yasl_loop_step(l, 50);
+ int handled = yasl_private_loop_step(l, 50);
assert(handled >= 0);
}
assert(ctx1.count == 1);
/* Should be disarmed and removed */
- assert(!yasl_loop_del_timer(l, ctx1.id));
- assert(yasl_loop_next_timeout_ms(l) == -1);
+ assert(!yasl_private_loop_del_timer(l, ctx1.id));
+ assert(yasl_private_loop_next_timeout_ms(l) == -1);
/* Test 2: Repeating */
struct test_ctx ctx2 = { .loop = l };
- ctx2.id = yasl_loop_add_timer(l, 2000000ull, repeat_cb, &ctx2);
+ ctx2.id = yasl_private_loop_add_timer(l, 2000000ull, repeat_cb, &ctx2);
assert(ctx2.id > 0);
while (!ctx2.done) {
- yasl_loop_step(l, 50);
+ yasl_private_loop_step(l, 50);
}
assert(ctx2.count == 5);
- assert(!yasl_loop_del_timer(l, ctx2.id));
+ assert(!yasl_private_loop_del_timer(l, ctx2.id));
/* Test 3: Reschedule dynamically */
struct test_ctx ctx3 = { .loop = l };
- ctx3.id = yasl_loop_add_timer(l, 2000000ull, reschedule_cb, &ctx3);
+ ctx3.id = yasl_private_loop_add_timer(l, 2000000ull, reschedule_cb, &ctx3);
assert(ctx3.id > 0);
while (!ctx3.done) {
- yasl_loop_step(l, 50);
+ yasl_private_loop_step(l, 50);
}
assert(ctx3.count == 2);
/* Test 4: Timer cancellation before firing */
struct test_ctx ctx4 = { .loop = l };
- yasl_timer_id cancel_id = yasl_loop_add_timer(l, 5000000ull, oneshot_cb, &ctx4);
+ yasl_private_timer_id cancel_id = yasl_private_loop_add_timer(l, 5000000ull, oneshot_cb, &ctx4);
assert(cancel_id > 0);
- assert(yasl_loop_del_timer(l, cancel_id));
- assert(!yasl_loop_del_timer(l, cancel_id));
+ assert(yasl_private_loop_del_timer(l, cancel_id));
+ assert(!yasl_private_loop_del_timer(l, cancel_id));
/* Step for 10ms; timer should not fire */
- yasl_loop_step(l, 10);
+ yasl_private_loop_step(l, 10);
assert(ctx4.count == 0);
/* Test 5: Child timer added inside callback */
struct test_ctx ctx5 = { .loop = l };
- ctx5.id = yasl_loop_add_timer(l, 2000000ull, parent_timer_cb, &ctx5);
+ ctx5.id = yasl_private_loop_add_timer(l, 2000000ull, parent_timer_cb, &ctx5);
while (ctx5.count < 11) {
- yasl_loop_step(l, 50);
+ yasl_private_loop_step(l, 50);
}
assert(ctx5.count == 11);
/* Test 6: Killer timer deletes victim timer before victim fires */
struct test_ctx ctx6 = { .loop = l };
- ctx6.id = yasl_loop_add_timer(l, 1000000ull, killer_timer_cb, &ctx6);
- ctx6.other_id = yasl_loop_add_timer(l, 10000000ull, victim_timer_cb, &ctx6);
+ ctx6.id = yasl_private_loop_add_timer(l, 1000000ull, killer_timer_cb, &ctx6);
+ ctx6.other_id = yasl_private_loop_add_timer(l, 10000000ull, victim_timer_cb, &ctx6);
while (ctx6.count == 0) {
- yasl_loop_step(l, 50);
+ yasl_private_loop_step(l, 50);
}
assert(ctx6.count == 1);
- yasl_loop_step(l, 20);
+ yasl_private_loop_step(l, 20);
/* Test 7: Millisecond conversion */
struct test_ctx ctx7 = { .loop = l };
- ctx7.id = yasl_loop_add_timer(l, YASL_MS_TO_NS(5), ms_timer_cb, &ctx7);
+ ctx7.id = yasl_private_loop_add_timer(l, YASL_PRIVATE_MS_TO_NS(5), ms_timer_cb, &ctx7);
assert(ctx7.id > 0);
while (!ctx7.done) {
- yasl_loop_step(l, 50);
+ yasl_private_loop_step(l, 50);
}
assert(ctx7.count == 3);
- yasl_loop_destroy(l);
+ yasl_private_loop_destroy(l);
}
/* Surface timer tests */
-static uint64_t surface_timer_repeat_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
+static uint64_t surface_timer_repeat_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns)
{
(void)interval_ns;
struct test_ctx *ctx = ud;
@@ -207,96 +207,96 @@ static uint64_t surface_timer_repeat_cb(void *ud, yasl_timer_id id, uint64_t int
return interval_ns;
}
-static uint64_t surface_suicide_cb(void *ud, yasl_timer_id id, uint64_t interval_ns)
+static uint64_t surface_suicide_cb(void *ud, yasl_private_timer_id id, uint64_t interval_ns)
{
(void)id; (void)interval_ns;
struct test_ctx *ctx = ud;
ctx->count++;
/* Destroy surface from within the timer callback */
- yasl_surface_destroy(ctx->surface);
+ yasl_private_surface_destroy(ctx->surface);
ctx->surface = NULL;
return 1000000ull; /* Even if it returns > 0, surface is destroyed */
}
static void test_surface_timers(void)
{
- struct yasl_loop *loop = yasl_loop_create();
+ struct yasl_private_loop *loop = yasl_private_loop_create();
assert(loop);
- struct yasl_display_spec spec = { .app_id = "org.yasl.timer_test" };
- struct yasl_display *d = yasl_display_open(loop, &spec);
+ struct yasl_private_display_spec spec = { .app_id = "org.yasl.timer_test" };
+ struct yasl_private_display *d = yasl_private_display_open(loop, &spec);
if (!d) {
/* Headless without display server */
- yasl_loop_destroy(loop);
+ yasl_private_loop_destroy(loop);
return;
}
- assert(yasl_display_loop(d) == loop);
+ assert(yasl_private_display_loop(d) == loop);
- struct yasl_window_spec sspec = {
+ struct yasl_private_window_spec sspec = {
.title = "Timer Surface",
.size = { .x = 160, .y = 100 },
};
- struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
+ struct yasl_surface *s = yasl_private_surface_create_toplevel(d, &sspec);
assert(s);
- assert(yasl_display_loop(yasl_surface_display(s)) == loop);
+ assert(yasl_private_display_loop(yasl_private_surface_display(s)) == loop);
/* Application timers use the display's loop. */
struct test_ctx dctx = {0};
- dctx.id = yasl_loop_add_timer(yasl_display_loop(d), 2000000ull, oneshot_cb, &dctx);
+ dctx.id = yasl_private_loop_add_timer(yasl_private_display_loop(d), 2000000ull, oneshot_cb, &dctx);
assert(dctx.id > 0);
while (dctx.count == 0) {
- yasl_display_dispatch(d, 50);
+ yasl_private_display_dispatch(d, 50);
}
assert(dctx.count == 1);
/* Surface timer basic repeat & manual delete */
struct test_ctx sctx = {0};
- sctx.id = yasl_surface_add_timer(s, 2000000ull, surface_timer_repeat_cb, &sctx);
+ sctx.id = yasl_private_surface_add_timer(s, 2000000ull, surface_timer_repeat_cb, &sctx);
assert(sctx.id > 0);
while (sctx.count < 3) {
- yasl_display_dispatch(d, 50);
+ yasl_private_display_dispatch(d, 50);
}
assert(sctx.count >= 3);
- assert(yasl_surface_del_timer(s, sctx.id));
+ assert(yasl_private_surface_del_timer(s, sctx.id));
int count_before = sctx.count;
- yasl_display_dispatch(d, 10);
+ yasl_private_display_dispatch(d, 10);
assert(sctx.count == count_before);
/* A fired one-shot surface timer is retired, as a loop timer is. Many
* of them stay retired after the surface's id list compacts. */
for (int i = 0; i < 64; i++) {
struct test_ctx octx = {0};
- octx.id = yasl_surface_add_timer(s, 1000000ull, oneshot_cb, &octx);
+ octx.id = yasl_private_surface_add_timer(s, 1000000ull, oneshot_cb, &octx);
assert(octx.id > 0);
while (octx.count == 0)
- yasl_display_dispatch(d, 50);
- assert(!yasl_surface_del_timer(s, octx.id));
+ yasl_private_display_dispatch(d, 50);
+ assert(!yasl_private_surface_del_timer(s, octx.id));
}
/* Surface timer auto-cleanup on surface destruction */
struct test_ctx sctx2 = {0};
- sctx2.id = yasl_surface_add_timer(s, 2000000ull, surface_timer_repeat_cb, &sctx2);
+ sctx2.id = yasl_private_surface_add_timer(s, 2000000ull, surface_timer_repeat_cb, &sctx2);
assert(sctx2.id > 0);
- yasl_surface_destroy(s);
+ yasl_private_surface_destroy(s);
/* Step display/loop; timer must not fire on dead surface */
- yasl_display_dispatch(d, 10);
+ yasl_private_display_dispatch(d, 10);
assert(sctx2.count == 0);
/* Surface suicide from timer callback */
- s = yasl_surface_create_toplevel(d, &sspec);
+ s = yasl_private_surface_create_toplevel(d, &sspec);
assert(s);
struct test_ctx sctx3 = { .surface = s };
- sctx3.id = yasl_surface_add_timer(s, 2000000ull, surface_suicide_cb, &sctx3);
+ sctx3.id = yasl_private_surface_add_timer(s, 2000000ull, surface_suicide_cb, &sctx3);
assert(sctx3.id > 0);
while (sctx3.count == 0) {
- yasl_display_dispatch(d, 50);
+ yasl_private_display_dispatch(d, 50);
}
assert(sctx3.count == 1);
assert(sctx3.surface == NULL);
- yasl_display_close(d);
- yasl_loop_destroy(loop);
+ yasl_private_display_close(d);
+ yasl_private_loop_destroy(loop);
}
int main(void)
diff --git a/tests/common/test_utf8.c b/tests/common/test_utf8.c
index 72f8904..58bfe9d 100644
--- a/tests/common/test_utf8.c
+++ b/tests/common/test_utf8.c
@@ -9,19 +9,19 @@ int main(void)
for (int i = 1; i <= 255; i++) latin1[i - 1] = (char)i;
latin1[255] = '\0';
size_t len = 0, back_len = 0;
- char *utf8 = yasl_utf8_from_latin1(latin1, 255, &len);
- assert(utf8 && yasl_utf8_validate(utf8, len));
- char *back = yasl_utf8_to_latin1(utf8, &back_len);
+ char *utf8 = yasl_private_utf8_from_latin1(latin1, 255, &len);
+ assert(utf8 && yasl_private_utf8_validate(utf8, len));
+ char *back = yasl_private_utf8_to_latin1(utf8, &back_len);
assert(back && back_len == 255 && memcmp(back, latin1, 256) == 0);
free(back);
free(utf8);
- assert(!yasl_utf8_to_latin1("😀", &len));
- assert(!yasl_utf8_to_latin1("\xc3", &len));
- assert(!yasl_utf8_validate("\xc3", 1));
- assert(!yasl_utf8_validate("\xe2\x82", 2));
- assert(!yasl_utf8_validate("\xf0\x9f\x98", 3));
- assert(!yasl_utf8_validate("\xed\xa0\x80", 3)); /* Surrogate. */
- assert(yasl_utf8_validate("😀", sizeof("😀") - 1));
- assert(yasl_utf8_validate(NULL, 0));
+ assert(!yasl_private_utf8_to_latin1("😀", &len));
+ assert(!yasl_private_utf8_to_latin1("\xc3", &len));
+ assert(!yasl_private_utf8_validate("\xc3", 1));
+ assert(!yasl_private_utf8_validate("\xe2\x82", 2));
+ assert(!yasl_private_utf8_validate("\xf0\x9f\x98", 3));
+ assert(!yasl_private_utf8_validate("\xed\xa0\x80", 3)); /* Surrogate. */
+ assert(yasl_private_utf8_validate("😀", sizeof("😀") - 1));
+ assert(yasl_private_utf8_validate(NULL, 0));
return 0;
}
diff --git a/tests/common/test_vulkan.c b/tests/common/test_vulkan.c
new file mode 100644
index 0000000..12daa22
--- /dev/null
+++ b/tests/common/test_vulkan.c
@@ -0,0 +1,389 @@
+/* Public Vulkan WSI integration test. The launcher supplies a real Wayland
+ * or X11 session (normally Weston/Xwayland); no internal YASL headers are
+ * used here. */
+#include "yasl/yasl.h"
+#include "yasl/vulkan.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int fail_vk(const char *what, VkResult result)
+{
+ fprintf(stderr, "vulkan integration: %s failed (VkResult %d)\n", what, result);
+ return 1;
+}
+
+static bool has_device_extension(VkPhysicalDevice device, const char *name)
+{
+ uint32_t count = 0;
+ if (vkEnumerateDeviceExtensionProperties(device, NULL, &count, NULL) != VK_SUCCESS)
+ return false;
+ VkExtensionProperties *extensions = count ? calloc(count, sizeof(*extensions)) : NULL;
+ if (count && !extensions) return false;
+ bool found = false;
+ if (vkEnumerateDeviceExtensionProperties(device, NULL, &count, extensions) == VK_SUCCESS)
+ for (uint32_t i = 0; i < count; i++)
+ if (strcmp(extensions[i].extensionName, name) == 0) found = true;
+ free(extensions);
+ return found;
+}
+
+static int run_test(enum yasl_backend backend)
+{
+ int status = 1;
+ yasl_surface *surface = NULL;
+ VkInstance instance = VK_NULL_HANDLE;
+ VkSurfaceKHR vk_surface = VK_NULL_HANDLE;
+ VkDevice device = VK_NULL_HANDLE;
+ VkSwapchainKHR swapchain = VK_NULL_HANDLE;
+ VkCommandPool command_pool = VK_NULL_HANDLE;
+ VkSemaphore acquired = VK_NULL_HANDLE, rendered = VK_NULL_HANDLE;
+ VkCommandBuffer command = VK_NULL_HANDLE;
+
+ if (!yasl_set_backend(backend)) {
+ fprintf(stderr, "vulkan integration: cannot select requested backend %d (YASL error %d)\n",
+ backend, yasl_get_error(NULL));
+ goto cleanup;
+ }
+ if (!yasl_vulkan_init(vkGetInstanceProcAddr)) {
+ fprintf(stderr, "vulkan integration: loader initialization failed (YASL error %d)\n",
+ yasl_get_error(NULL));
+ goto cleanup;
+ }
+ surface = yasl_surface_create("yasl Vulkan WSI integration", 256, 192, 0);
+ if (!surface) {
+ fprintf(stderr, "vulkan integration: surface creation failed (YASL error %d)\n",
+ yasl_get_error(NULL));
+ goto cleanup;
+ }
+ if (!(yasl_surface_features(surface) & YASL_CAP_VULKAN)) {
+ fprintf(stderr, "vulkan integration: requested backend does not advertise Vulkan WSI\n");
+ goto cleanup;
+ }
+
+ uint32_t extension_count = 0;
+ const char *const *required = yasl_vulkan_instance_extensions(surface, &extension_count);
+ if (!required || !extension_count) {
+ fprintf(stderr, "vulkan integration: surface returned no required instance extensions\n");
+ goto cleanup;
+ }
+ uint32_t available_count = 0;
+ VkResult vr = vkEnumerateInstanceExtensionProperties(NULL, &available_count, NULL);
+ if (vr != VK_SUCCESS) { status = fail_vk("vkEnumerateInstanceExtensionProperties", vr); goto cleanup; }
+ VkExtensionProperties *available = available_count ? calloc(available_count, sizeof(*available)) : NULL;
+ if (available_count && !available) { fprintf(stderr, "vulkan integration: out of memory\n"); goto cleanup; }
+ vr = vkEnumerateInstanceExtensionProperties(NULL, &available_count, available);
+ if (vr != VK_SUCCESS) {
+ free(available); status = fail_vk("vkEnumerateInstanceExtensionProperties", vr); goto cleanup;
+ }
+ for (uint32_t i = 0; i < extension_count; i++) {
+ bool found = false;
+ for (uint32_t j = 0; j < available_count; j++)
+ if (strcmp(required[i], available[j].extensionName) == 0) found = true;
+ if (!found) {
+ fprintf(stderr, "vulkan integration: required instance extension unavailable: %s\n", required[i]);
+ free(available); goto cleanup;
+ }
+ }
+ free(available);
+
+ VkApplicationInfo app = {
+ .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
+ .pApplicationName = "yasl Vulkan WSI integration",
+ .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
+ .pEngineName = "yasl",
+ .engineVersion = VK_MAKE_VERSION(1, 0, 0),
+ .apiVersion = VK_API_VERSION_1_0,
+ };
+ VkInstanceCreateInfo instance_info = {
+ .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
+ .pApplicationInfo = &app,
+ .flags = yasl_vulkan_instance_flags(surface),
+ .enabledExtensionCount = extension_count,
+ .ppEnabledExtensionNames = required,
+ };
+ vr = vkCreateInstance(&instance_info, NULL, &instance);
+ if (vr != VK_SUCCESS) { status = fail_vk("vkCreateInstance", vr); goto cleanup; }
+ vr = yasl_vulkan_create_surface(surface, instance, NULL, &vk_surface);
+ if (vr != VK_SUCCESS) { status = fail_vk("yasl_vulkan_create_surface", vr); goto cleanup; }
+
+ uint32_t physical_count = 0;
+ vr = vkEnumeratePhysicalDevices(instance, &physical_count, NULL);
+ if (vr != VK_SUCCESS || !physical_count) {
+ status = vr == VK_SUCCESS ? 1 : fail_vk("vkEnumeratePhysicalDevices", vr);
+ if (vr == VK_SUCCESS) fprintf(stderr, "vulkan integration: no physical devices found\n");
+ goto cleanup;
+ }
+ VkPhysicalDevice *physical = calloc(physical_count, sizeof(*physical));
+ if (!physical) { fprintf(stderr, "vulkan integration: out of memory\n"); goto cleanup; }
+ vr = vkEnumeratePhysicalDevices(instance, &physical_count, physical);
+ if (vr != VK_SUCCESS) { free(physical); status = fail_vk("vkEnumeratePhysicalDevices", vr); goto cleanup; }
+
+ VkPhysicalDevice selected = VK_NULL_HANDLE;
+ uint32_t queue_family = UINT32_MAX;
+ for (uint32_t p = 0; p < physical_count && !selected; p++) {
+ if (!has_device_extension(physical[p], VK_KHR_SWAPCHAIN_EXTENSION_NAME)) continue;
+ uint32_t family_count = 0;
+ vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &family_count, NULL);
+ VkQueueFamilyProperties *families = family_count ? calloc(family_count, sizeof(*families)) : NULL;
+ if (family_count && !families) continue;
+ vkGetPhysicalDeviceQueueFamilyProperties(physical[p], &family_count, families);
+ for (uint32_t q = 0; q < family_count; q++) {
+ VkBool32 present = VK_FALSE;
+ if ((families[q].queueFlags & VK_QUEUE_GRAPHICS_BIT) &&
+ vkGetPhysicalDeviceSurfaceSupportKHR(physical[p], q, vk_surface, &present) == VK_SUCCESS && present) {
+ selected = physical[p];
+ queue_family = q;
+ break;
+ }
+ }
+ free(families);
+ }
+ free(physical);
+ if (!selected) {
+ fprintf(stderr, "vulkan integration: no graphics queue with VK_KHR_swapchain presentation support\n");
+ goto cleanup;
+ }
+
+ VkPhysicalDeviceProperties properties;
+ vkGetPhysicalDeviceProperties(selected, &properties);
+ fprintf(stderr, "vulkan integration: using %s\n", properties.deviceName);
+ VkSurfaceCapabilitiesKHR capabilities;
+ vr = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(selected, vk_surface, &capabilities);
+ if (vr != VK_SUCCESS) { status = fail_vk("vkGetPhysicalDeviceSurfaceCapabilitiesKHR", vr); goto cleanup; }
+ if (!(capabilities.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) {
+ fprintf(stderr, "vulkan integration: surface images cannot be transfer destinations\n");
+ goto cleanup;
+ }
+ uint32_t format_count = 0;
+ vr = vkGetPhysicalDeviceSurfaceFormatsKHR(selected, vk_surface, &format_count, NULL);
+ if (vr != VK_SUCCESS || !format_count) {
+ status = vr == VK_SUCCESS ? 1 : fail_vk("vkGetPhysicalDeviceSurfaceFormatsKHR", vr);
+ if (vr == VK_SUCCESS) fprintf(stderr, "vulkan integration: surface has no formats\n");
+ goto cleanup;
+ }
+ VkSurfaceFormatKHR *formats = calloc(format_count, sizeof(*formats));
+ if (!formats) { fprintf(stderr, "vulkan integration: out of memory\n"); goto cleanup; }
+ vr = vkGetPhysicalDeviceSurfaceFormatsKHR(selected, vk_surface, &format_count, formats);
+ if (vr != VK_SUCCESS) { free(formats); status = fail_vk("vkGetPhysicalDeviceSurfaceFormatsKHR", vr); goto cleanup; }
+ VkSurfaceFormatKHR format = formats[0];
+ for (uint32_t i = 0; i < format_count; i++)
+ if (formats[i].format == VK_FORMAT_B8G8R8A8_SRGB &&
+ formats[i].colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { format = formats[i]; break; }
+ if (format.format == VK_FORMAT_UNDEFINED) format.format = VK_FORMAT_B8G8R8A8_UNORM;
+ free(formats);
+
+ VkExtent2D extent = capabilities.currentExtent;
+ if (extent.width == UINT32_MAX) {
+ int width = 0, height = 0;
+ yasl_surface_get_framebuffer_size(surface, &width, &height);
+ if (width <= 0 || height <= 0) {
+ fprintf(stderr, "vulkan integration: surface has no drawable framebuffer extent\n");
+ goto cleanup;
+ }
+ extent.width = (uint32_t)width;
+ extent.height = (uint32_t)height;
+ if (extent.width < capabilities.minImageExtent.width) extent.width = capabilities.minImageExtent.width;
+ if (extent.width > capabilities.maxImageExtent.width) extent.width = capabilities.maxImageExtent.width;
+ if (extent.height < capabilities.minImageExtent.height) extent.height = capabilities.minImageExtent.height;
+ if (extent.height > capabilities.maxImageExtent.height) extent.height = capabilities.maxImageExtent.height;
+ }
+ uint32_t image_count = capabilities.minImageCount + 1;
+ if (capabilities.maxImageCount && image_count > capabilities.maxImageCount)
+ image_count = capabilities.maxImageCount;
+ VkCompositeAlphaFlagBitsKHR alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+ if (!(capabilities.supportedCompositeAlpha & alpha)) {
+ const VkCompositeAlphaFlagBitsKHR alphas[] = {
+ VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
+ VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
+ VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
+ };
+ bool found = false;
+ for (size_t i = 0; i < sizeof(alphas) / sizeof(alphas[0]); i++)
+ if (capabilities.supportedCompositeAlpha & alphas[i]) { alpha = alphas[i]; found = true; break; }
+ if (!found) { fprintf(stderr, "vulkan integration: no supported composite alpha mode\n"); goto cleanup; }
+ }
+ uint32_t present_mode_count = 0;
+ vr = vkGetPhysicalDeviceSurfacePresentModesKHR(selected, vk_surface, &present_mode_count, NULL);
+ if (vr != VK_SUCCESS || !present_mode_count) {
+ status = vr == VK_SUCCESS ? 1 : fail_vk("vkGetPhysicalDeviceSurfacePresentModesKHR", vr);
+ if (vr == VK_SUCCESS) fprintf(stderr, "vulkan integration: surface has no present modes\n");
+ goto cleanup;
+ }
+ VkPresentModeKHR *present_modes = calloc(present_mode_count, sizeof(*present_modes));
+ if (!present_modes) { fprintf(stderr, "vulkan integration: out of memory\n"); goto cleanup; }
+ vr = vkGetPhysicalDeviceSurfacePresentModesKHR(selected, vk_surface, &present_mode_count, present_modes);
+ free(present_modes);
+ if (vr != VK_SUCCESS) { status = fail_vk("vkGetPhysicalDeviceSurfacePresentModesKHR", vr); goto cleanup; }
+
+ float priority = 1.0f;
+ VkDeviceQueueCreateInfo queue_info = {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
+ .queueFamilyIndex = queue_family,
+ .queueCount = 1,
+ .pQueuePriorities = &priority,
+ };
+ const char *device_extensions[] = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
+ VkDeviceCreateInfo device_info = {
+ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
+ .queueCreateInfoCount = 1,
+ .pQueueCreateInfos = &queue_info,
+ .enabledExtensionCount = 1,
+ .ppEnabledExtensionNames = device_extensions,
+ };
+ vr = vkCreateDevice(selected, &device_info, NULL, &device);
+ if (vr != VK_SUCCESS) { status = fail_vk("vkCreateDevice", vr); goto cleanup; }
+ VkQueue queue;
+ vkGetDeviceQueue(device, queue_family, 0, &queue);
+
+ VkSwapchainCreateInfoKHR swap_info = {
+ .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
+ .surface = vk_surface,
+ .minImageCount = image_count,
+ .imageFormat = format.format,
+ .imageColorSpace = format.colorSpace,
+ .imageExtent = extent,
+ .imageArrayLayers = 1,
+ .imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
+ .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
+ .preTransform = capabilities.currentTransform,
+ .compositeAlpha = alpha,
+ .presentMode = VK_PRESENT_MODE_FIFO_KHR,
+ .clipped = VK_TRUE,
+ };
+ vr = vkCreateSwapchainKHR(device, &swap_info, NULL, &swapchain);
+ if (vr != VK_SUCCESS) { status = fail_vk("vkCreateSwapchainKHR", vr); goto cleanup; }
+ uint32_t swap_image_count = 0;
+ vr = vkGetSwapchainImagesKHR(device, swapchain, &swap_image_count, NULL);
+ if (vr != VK_SUCCESS || !swap_image_count) {
+ status = vr == VK_SUCCESS ? 1 : fail_vk("vkGetSwapchainImagesKHR", vr);
+ if (vr == VK_SUCCESS) fprintf(stderr, "vulkan integration: swapchain returned no images\n");
+ goto cleanup;
+ }
+ VkImage *images = calloc(swap_image_count, sizeof(*images));
+ if (!images) { fprintf(stderr, "vulkan integration: out of memory\n"); goto cleanup; }
+ vr = vkGetSwapchainImagesKHR(device, swapchain, &swap_image_count, images);
+ if (vr != VK_SUCCESS) { free(images); status = fail_vk("vkGetSwapchainImagesKHR", vr); goto cleanup; }
+
+ VkCommandPoolCreateInfo pool_info = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+ .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
+ .queueFamilyIndex = queue_family,
+ };
+ vr = vkCreateCommandPool(device, &pool_info, NULL, &command_pool);
+ if (vr != VK_SUCCESS) { free(images); status = fail_vk("vkCreateCommandPool", vr); goto cleanup; }
+ VkCommandBufferAllocateInfo command_info = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+ .commandPool = command_pool,
+ .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+ .commandBufferCount = 1,
+ };
+ vr = vkAllocateCommandBuffers(device, &command_info, &command);
+ if (vr != VK_SUCCESS) { free(images); status = fail_vk("vkAllocateCommandBuffers", vr); goto cleanup; }
+ VkSemaphoreCreateInfo semaphore_info = {.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
+ vr = vkCreateSemaphore(device, &semaphore_info, NULL, &acquired);
+ if (vr != VK_SUCCESS) { free(images); status = fail_vk("vkCreateSemaphore(acquired)", vr); goto cleanup; }
+ vr = vkCreateSemaphore(device, &semaphore_info, NULL, &rendered);
+ if (vr != VK_SUCCESS) { free(images); status = fail_vk("vkCreateSemaphore(rendered)", vr); goto cleanup; }
+
+ uint32_t image_index = 0;
+ vr = vkAcquireNextImageKHR(device, swapchain, UINT64_MAX, acquired, VK_NULL_HANDLE, &image_index);
+ if (vr != VK_SUCCESS && vr != VK_SUBOPTIMAL_KHR) {
+ free(images); status = fail_vk("vkAcquireNextImageKHR", vr); goto cleanup;
+ }
+ VkCommandBufferBeginInfo begin = {.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO};
+ vr = vkBeginCommandBuffer(command, &begin);
+ if (vr != VK_SUCCESS) { free(images); status = fail_vk("vkBeginCommandBuffer", vr); goto cleanup; }
+ VkImageMemoryBarrier to_clear = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .srcAccessMask = 0,
+ .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+ .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
+ .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = images[image_index],
+ .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
+ };
+ vkCmdPipelineBarrier(command, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
+ VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &to_clear);
+ VkClearColorValue color = {.float32 = {0.08f, 0.18f, 0.32f, 1.0f}};
+ VkImageSubresourceRange range = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
+ vkCmdClearColorImage(command, images[image_index], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &color, 1, &range);
+ VkImageMemoryBarrier to_present = {
+ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
+ .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
+ .dstAccessMask = 0,
+ .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .image = images[image_index],
+ .subresourceRange = range,
+ };
+ vkCmdPipelineBarrier(command, VK_PIPELINE_STAGE_TRANSFER_BIT,
+ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, NULL, 1, &to_present);
+ vr = vkEndCommandBuffer(command);
+ free(images);
+ if (vr != VK_SUCCESS) { status = fail_vk("vkEndCommandBuffer", vr); goto cleanup; }
+ VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_TRANSFER_BIT;
+ VkSubmitInfo submit = {
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .waitSemaphoreCount = 1,
+ .pWaitSemaphores = &acquired,
+ .pWaitDstStageMask = &wait_stage,
+ .commandBufferCount = 1,
+ .pCommandBuffers = &command,
+ .signalSemaphoreCount = 1,
+ .pSignalSemaphores = &rendered,
+ };
+ vr = vkQueueSubmit(queue, 1, &submit, VK_NULL_HANDLE);
+ if (vr != VK_SUCCESS) { status = fail_vk("vkQueueSubmit", vr); goto cleanup; }
+ VkPresentInfoKHR present = {
+ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
+ .waitSemaphoreCount = 1,
+ .pWaitSemaphores = &rendered,
+ .swapchainCount = 1,
+ .pSwapchains = &swapchain,
+ .pImageIndices = &image_index,
+ };
+ vr = vkQueuePresentKHR(queue, &present);
+ if (vr != VK_SUCCESS && vr != VK_SUBOPTIMAL_KHR) {
+ status = fail_vk("vkQueuePresentKHR", vr); goto cleanup;
+ }
+ vr = vkDeviceWaitIdle(device);
+ if (vr != VK_SUCCESS) { status = fail_vk("vkDeviceWaitIdle", vr); goto cleanup; }
+ fprintf(stderr, "vulkan integration: created surface, cleared and presented %ux%u swapchain image\n",
+ extent.width, extent.height);
+ status = 0;
+
+cleanup:
+ if (device) vkDeviceWaitIdle(device);
+ if (rendered) vkDestroySemaphore(device, rendered, NULL);
+ if (acquired) vkDestroySemaphore(device, acquired, NULL);
+ if (command_pool) vkDestroyCommandPool(device, command_pool, NULL);
+ if (swapchain) vkDestroySwapchainKHR(device, swapchain, NULL);
+ if (device) vkDestroyDevice(device, NULL);
+ if (vk_surface) vkDestroySurfaceKHR(instance, vk_surface, NULL);
+ if (instance) vkDestroyInstance(instance, NULL);
+ if (surface) yasl_surface_destroy(surface);
+ yasl_shutdown();
+ return status;
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s wayland|x11\n", argv[0]);
+ return 2;
+ }
+ enum yasl_backend backend;
+ if (strcmp(argv[1], "wayland") == 0) backend = YASL_BACKEND_WAYLAND;
+ else if (strcmp(argv[1], "x11") == 0) backend = YASL_BACKEND_X11;
+ else {
+ fprintf(stderr, "vulkan integration: unsupported backend '%s'\n", argv[1]);
+ return 2;
+ }
+ return run_test(backend);
+}
diff --git a/tests/helpers/run_surfaces.py b/tests/helpers/run_surfaces.py
new file mode 100644
index 0000000..f120e35
--- /dev/null
+++ b/tests/helpers/run_surfaces.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python3
+"""Run the public surface/GPU contract tests on private Weston and Xwayland."""
+import os
+from pathlib import Path
+import subprocess
+import sys
+import tempfile
+import time
+
+def wait_for(process, ready):
+ deadline = time.monotonic() + 12
+ while time.monotonic() < deadline:
+ if process.poll() is not None:
+ raise RuntimeError('Display server stopped before readiness')
+ if ready():
+ return
+ time.sleep(0.02)
+ raise TimeoutError('Display server readiness')
+
+def main():
+ weston, xwayland, contract, hello, tablet, gl, vulkan, notepad = sys.argv[1:]
+ servers = []
+ with tempfile.TemporaryDirectory(prefix='yasl-surfaces-') 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', LIBGL_ALWAYS_SOFTWARE='1')
+ lavapipe = next(Path('/usr/share/vulkan/icd.d').glob('*lvp*.json'), None)
+ if lavapipe:
+ env['VK_DRIVER_FILES'] = str(lavapipe)
+ 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=gl',
+ '--socket=yasl-test', '--idle-time=0', '--no-config',
+ '--width=1024', '--height=768'], 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 fd:
+ x = subprocess.Popen([xwayland, '-displayfd', str(fd.fileno()),
+ '-shm', '-noreset', '-nolisten', 'tcp'], env=env,
+ pass_fds=(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()
+ for backend in ('wayland', 'x11'):
+ case = env.copy()
+ if backend == 'x11':
+ case.pop('WAYLAND_DISPLAY', None)
+ case['XDG_SESSION_TYPE'] = 'x11'
+ for binary, args in ((contract,[backend]), (gl,[backend]),
+ (vulkan,[backend]), (tablet,['--backend',backend,'--smoke-test']),
+ (notepad,['--smoke-test'])):
+ if binary == '-':
+ continue
+ print('Testing', Path(binary).name, backend, flush=True)
+ subprocess.run([binary, *args], env=case, check=True, timeout=15)
+ # hello runs until closed: it must survive its first frames.
+ if hello == '-':
+ continue
+ print('Testing', Path(hello).name, backend, flush=True)
+ process = subprocess.Popen([hello], env=case)
+ try:
+ process.wait(timeout=1)
+ raise RuntimeError(f'{Path(hello).name} exited with {process.returncode}')
+ except subprocess.TimeoutExpired:
+ process.terminate()
+ process.wait()
+ except BaseException:
+ for log in (wl_log,x_log):
+ log.flush();log.seek(0);print(log.read(), file=sys.stderr)
+ raise
+ finally:
+ for server in reversed(servers):
+ server.terminate()
+ try:
+ server.wait(timeout=5)
+ except subprocess.TimeoutExpired:
+ server.kill();server.wait()
+if __name__ == '__main__':
+ main()
diff --git a/tests/meson.build b/tests/meson.build
index cac62db..4cbf082 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -1,291 +1,105 @@
-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)
-
-shared_dep = declare_dependency(
- link_with: yasl_lib.get_shared_lib(),
- include_directories: include_directories('../include'),
-)
-
-shared_api_test = executable(
- 'yasl-shared-api-test',
- 'test_lifecycle.c',
- dependencies: shared_dep,
-)
-test('shared-api', shared_api_test)
-
+python = find_program('python3', native: true)
+shared_dep = declare_dependency(link_with: yasl_lib.get_shared_lib(),
+ include_directories: include_directories('../include'))
+foreach kind : ['static', 'shared']
+ dep = kind == 'static' ? yasl_dep : shared_dep
+ public_test = executable('yasl-' + kind + '-api-test', 'test_lifecycle.c', dependencies: dep)
+ test(kind + '-api', public_test)
+endforeach
if add_languages('cpp', native: false, required: false)
- cpp = meson.get_compiler('cpp')
- cpp_api_test = executable(
- 'yasl-cpp-api-test',
- 'test_api.cpp',
- dependencies: shared_dep,
- cpp_args: cpp.get_supported_arguments('-pedantic-errors') + [
- '-DYASL_HAS_WAYLAND=' + (has_wayland ? '1' : '0'),
- '-DYASL_HAS_X11=' + (has_x11 ? '1' : '0'),
- '-DYASL_HAS_COCOA=' + (has_cocoa ? '1' : '0'),
- ],
- override_options: ['cpp_std=c++11'],
- )
- test('cpp-api', cpp_api_test)
+ cpp_api = executable('yasl-cpp-api-test', 'test_api.cpp', dependencies: shared_dep,
+ cpp_args: ['-pedantic-errors'], override_options: ['cpp_std=c++11'])
+ test('cpp-api', cpp_api)
endif
-
-python = find_program('python3', native: true)
-if system == 'linux' or system == 'darwin'
- nm = find_program('nm', native: false)
- export_args = ['--platform=' + system]
- if get_option('b_sanitize').contains('address')
- # Clang's ASan instrumentation exports these linker-generated markers.
- export_args += ['--allow-export=__start_asan_globals',
- '--allow-export=__stop_asan_globals']
- endif
- exported_headers = files(
- '../include/yasl/yasl.h',
- '../include/yasl/app.h',
- '../include/yasl/desktop.h',
- '../include/yasl/timing.h',
- '../include/yasl/loop.h',
- '../include/yasl/native.h',
- )
- if has_wayland
- exported_headers += files('../include/yasl/native/wayland.h')
- endif
- if has_x11
- exported_headers += files('../include/yasl/native/x11.h')
- endif
- if has_cocoa
- exported_headers += files('../include/yasl/native/cocoa.h')
+headers = files('../include/yasl/yasl.h', '../include/yasl/cpu.h', '../include/yasl/gl.h',
+ '../include/yasl/metal.h', '../include/yasl/desktop.h')
+if has_vulkan
+ headers += files('../include/yasl/vulkan.h')
+endif
+foreach backend : ['wayland', 'x11', 'cocoa']
+ enabled = {'wayland': has_wayland, 'x11': has_x11, 'cocoa': has_cocoa}[backend]
+ if enabled
+ headers += files('../include/yasl/native/' + backend + '.h')
endif
- test('public-exports', python,
- args: [files('helpers/check_exports.py')] + export_args +
- [nm, yasl_lib.get_shared_lib(), exported_headers],
- )
+endforeach
+nm = find_program('nm')
+export_args = ['--platform=' + system]
+if get_option('b_sanitize').contains('address')
+ export_args += ['--allow-export=__start_asan_globals', '--allow-export=__stop_asan_globals']
endif
-
-contracts_test = executable(
- 'yasl-contracts-test',
- 'common/test_contracts.c',
- include_directories: inc,
- dependencies: yasl_dep,
-)
-test('contracts', contracts_test)
-
-# Exercise the SDK-independent mappings even when their backend is disabled.
-# Enabled backends already provide their mapping through yasl_dep.
-keyboard_test_sources = files('common/test_keyboard.c')
+test('public-exports', python, args: [files('helpers/check_exports.py')] + export_args +
+ [nm, yasl_lib.get_shared_lib(), headers])
+
+foreach unit : ['contracts', 'utf8', 'timers', 'loop', 'selection', 'events', 'gl_options']
+ exe = executable('yasl-' + unit + '-test', 'common/test_' + unit + '.c',
+ include_directories: inc, dependencies: yasl_dep)
+ test(unit, exe)
+endforeach
+keyboard_sources = files('common/test_keyboard.c')
if not has_wayland
- keyboard_test_sources += files('../src/platform/wayland/keymap.c')
+ keyboard_sources += files('../src/platform/wayland/keymap.c')
endif
if not has_x11
- keyboard_test_sources += files('../src/platform/x11/keymap.c')
+ keyboard_sources += files('../src/platform/x11/keymap.c')
endif
if not has_cocoa
- keyboard_test_sources += files('../src/platform/cocoa/keymap.c')
+ keyboard_sources += files('../src/platform/cocoa/keymap.c')
endif
-keyboard_test = executable('yasl-keyboard-test', keyboard_test_sources,
- include_directories: inc, dependencies: yasl_dep,
-)
-test('keyboard', keyboard_test)
-
-utf8_test = executable('yasl-utf8-test', 'common/test_utf8.c',
- include_directories: inc, dependencies: yasl_dep,
-)
-test('utf8', utf8_test)
-
-timer_test = executable(
- 'yasl-timer-test',
- 'common/test_timers.c',
- include_directories: inc,
- dependencies: yasl_dep,
-)
-test('timers', timer_test)
-
-loop_test = executable(
- 'yasl-loop-test',
- 'common/test_loop.c',
- include_directories: inc,
- dependencies: [yasl_dep, dependency('threads')],
-)
-test('loop', loop_test)
-
+keyboard = executable('yasl-keyboard-test', keyboard_sources, include_directories: inc, dependencies: yasl_dep)
+test('keyboard', keyboard)
+alloc = executable('yasl-input-alloc-test', ['common/test_input_alloc.c', '../src/common/input.c'],
+ include_directories: inc, c_args: ['-Dmalloc=yasl_private_test_malloc'])
+test('input-allocation-failure', alloc)
if system == 'linux'
- desktop_test = executable(
- 'yasl-desktop-test',
- 'common/test_desktop.c',
- include_directories: inc,
- dependencies: [yasl_dep, dependency('threads')],
- )
- # A private session bus keeps the notification and portal checks away
- # from the user's desktop.
- dbus_run_session = find_program('dbus-run-session', required: false)
- if has_dbus and dbus_run_session.found()
- test('desktop', dbus_run_session, args: ['--', desktop_test])
+ desktop = executable('yasl-desktop-test', 'common/test_desktop.c', include_directories: inc, dependencies: yasl_dep)
+ dbus_run = find_program('dbus-run-session', required: false)
+ if has_dbus and dbus_run.found()
+ test('desktop', dbus_run, args: ['--', desktop])
else
- test('desktop', desktop_test)
+ test('desktop', desktop)
endif
endif
-
-app_test = executable(
- 'yasl-app-test',
- 'common/test_app.c',
- include_directories: inc,
- dependencies: yasl_dep,
-)
-test('app-runner', app_test)
-
-input_alloc_test = executable(
- 'yasl-input-alloc-test',
- ['common/test_input_alloc.c', '../src/common/input.c'],
- include_directories: inc,
- c_args: ['-Dmalloc=yasl_test_malloc'],
-)
-test('input-allocation-failure', input_alloc_test)
-
-selection_test = executable(
- 'yasl-selection-test',
- 'common/test_selection.c',
- dependencies: yasl_dep,
-)
-test('selection', selection_test)
-
-tablet_example_test = executable('yasl-tablet-example-test', 'common/test_tablet_example.c',
- dependencies: yasl_dep,
-)
-test('tablet-example', tablet_example_test)
-
+if has_wayland
+ foreach unit : ['state', 'clipboard', 'keyboard', 'input', 'tablet', 'cursor', 'relative_pointer', 'app_input', 'popup_positioner']
+ exe = executable('yasl-' + unit + '-wayland-test', 'wayland/test_' + unit + '.c',
+ include_directories: inc, dependencies: yasl_dep)
+ test(unit + '-wayland', exe)
+ endforeach
+endif
if has_x11
- state_x11_test = executable('yasl-state-x11-test', 'x11/test_state.c',
- include_directories: inc, dependencies: yasl_dep,
- )
- test('state-x11', state_x11_test)
- tablet_x11_test = executable('yasl-tablet-x11-test', 'x11/test_tablet.c',
- include_directories: inc, dependencies: yasl_dep,
- )
- test('tablet-x11', tablet_x11_test)
+ foreach unit : ['state', 'tablet']
+ exe = executable('yasl-' + unit + '-x11-test', 'x11/test_' + unit + '.c',
+ include_directories: inc, dependencies: yasl_dep)
+ test(unit + '-x11', exe)
+ endforeach
if get_option('x11_tablet_tests')
xorg = find_program('/usr/lib/Xorg', '/usr/lib/xorg/Xorg', 'Xorg')
- native_tablet_test = executable('yasl-tablet-x11-native-test', 'x11/test_tablet_native.c',
- dependencies: yasl_dep,
- )
- test('tablet-x11-native', python,
- args: [files('helpers/run_x11_tablet.py'), xorg, native_tablet_test], timeout: 30,
- )
+ native_tablet = executable('yasl-tablet-x11-native-test', 'x11/test_tablet_native.c',
+ include_directories: inc, dependencies: yasl_dep)
+ test('tablet-x11-native', python, args: [files('helpers/run_x11_tablet.py'), xorg, native_tablet], timeout: 30)
endif
-elif get_option('x11_tablet_tests')
- error('Native X11 tablet tests require the X11 backend.')
endif
-
-if has_wayland
- state_wayland_test = executable('yasl-state-wayland-test', 'wayland/test_state.c',
- include_directories: inc, dependencies: yasl_dep,
- )
- test('state-wayland', state_wayland_test)
- clipboard_wayland_test = executable('yasl-clipboard-wayland-test', 'wayland/test_clipboard.c',
- include_directories: inc, dependencies: yasl_dep,
- )
- test('clipboard-wayland', clipboard_wayland_test)
- keyboard_wayland_test = executable('yasl-keyboard-wayland-test', 'wayland/test_keyboard.c',
- include_directories: inc, dependencies: yasl_dep,
- )
- test('keyboard-wayland', keyboard_wayland_test)
- ime_test = executable('yasl-ime-test', 'wayland/test_input.c',
- include_directories: inc,
- dependencies: yasl_dep,
- )
- test('ime-ownership', ime_test)
- app_input_test = executable('yasl-app-input-test', 'wayland/test_app_input.c',
- include_directories: inc, dependencies: yasl_dep,
- )
- test('app-input-destroy', app_input_test)
- tablet_wayland_test = executable('yasl-tablet-wayland-test', 'wayland/test_tablet.c',
- include_directories: inc, dependencies: yasl_dep,
- )
- test('tablet-wayland', tablet_wayland_test)
- cursor_wayland_test = executable('yasl-cursor-wayland-test', 'wayland/test_cursor.c',
- include_directories: inc, dependencies: yasl_dep,
- )
- test('cursor-wayland', cursor_wayland_test)
- relative_pointer_test = executable('yasl-relative-pointer-wayland-test',
- 'wayland/test_relative_pointer.c',
- include_directories: inc, dependencies: yasl_dep,
- )
- test('relative-pointer-wayland', relative_pointer_test)
-endif
-
-benchmark_exe = executable(
- 'yasl-benchmark',
- 'benchmark.c',
- include_directories: inc,
- dependencies: yasl_dep,
-)
-benchmark('performance', benchmark_exe)
-
if get_option('integration_tests')
- hello_example_test = executable('yasl-hello-example-test', 'common/test_hello_example.c',
- dependencies: shared_dep,
- )
- if has_cocoa
- test('hello-cocoa-window', hello_example_test, timeout: 20)
- test('interactive-cocoa-window', interactive_example,
- args: ['--smoke-test'], timeout: 20,
- )
- native_test = executable('yasl-native-test', 'cocoa/test_native.c',
- dependencies: shared_dep,
- )
- test('native', native_test, timeout: 20)
- ownership_test = executable('yasl-ownership-test', 'cocoa/test_ownership.m',
- dependencies: [shared_dep, deps],
- )
- test('native-ownership', ownership_test, timeout: 20)
- keyboard_cocoa_test = executable('yasl-keyboard-cocoa-test', 'cocoa/test_keyboard.m',
- dependencies: [shared_dep, deps],
- )
- test('keyboard-cocoa', keyboard_cocoa_test, timeout: 20)
- tablet_cocoa_test = executable('yasl-tablet-cocoa-test', 'cocoa/test_tablet.m',
- dependencies: [shared_dep, deps],
- )
- test('tablet-cocoa', tablet_cocoa_test, timeout: 20)
- cursor_cocoa_test = executable('yasl-cursor-cocoa-test', 'cocoa/test_cursor.m',
- dependencies: [shared_dep, deps],
- )
- test('cursor-cocoa', cursor_cocoa_test, timeout: 20)
- test('tablet-cocoa-window', tablet_example,
- args: ['--backend', 'cocoa', '--smoke-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: [shared_dep, deps],
- )
- input_x11_test = executable('yasl-input-x11-test', 'x11/test_input.c',
- include_directories: inc, dependencies: yasl_dep,
- )
+ integration = executable('yasl-surface-integration', 'common/test_surface_integration.c',
+ include_directories: inc, dependencies: yasl_dep)
+ gl_integration = executable('yasl-gl-integration', 'common/test_gl.c',
+ dependencies: yasl_dep)
+ vulkan_integration = '-'
+ if has_vulkan
+ vulkan_integration = executable('yasl-vulkan-integration', 'common/test_vulkan.c', dependencies: yasl_dep)
+ endif
+ if system == 'linux'
weston = find_program('weston')
xwayland = find_program('Xwayland')
- test('backends', python,
- args: [files('helpers/run_backends.py'), weston, xwayland,
- backend_test, timer_test, tablet_example, hello_example_test, input_x11_test,
- interactive_example, app_test, app_input_test],
- timeout: 60,
- )
+ test('surface-backends', python, args: [files('helpers/run_surfaces.py'), weston, xwayland,
+ integration, hello_example, tablet_example, gl_integration, vulkan_integration, notepad_example], timeout: 120)
+ elif has_cocoa
+ test('surface-cocoa', integration, args: ['cocoa'], timeout: 30)
+ foreach unit : ['native', 'ownership', 'keyboard', 'tablet', 'cursor']
+ extension = unit == 'native' ? '.c' : '.m'
+ exe = executable('yasl-' + unit + '-cocoa-test', 'cocoa/test_' + unit + extension,
+ include_directories: inc, dependencies: yasl_dep)
+ test(unit + '-cocoa', exe, timeout: 30)
+ endforeach
endif
endif
-
-events_test = executable('yasl-events-test', 'common/test_events.c',
- include_directories: inc, dependencies: [yasl_dep, dependency('threads')],
-)
-test('event-contracts', events_test)
diff --git a/tests/test_api.cpp b/tests/test_api.cpp
index c5859cd..c0693eb 100644
--- a/tests/test_api.cpp
+++ b/tests/test_api.cpp
@@ -1,89 +1,26 @@
-#include "yasl/native.h"
-#include "yasl/app.h"
+#include <yasl/yasl.h>
+#include <yasl/cpu.h>
+#include <yasl/gl.h>
+#include <yasl/metal.h>
+#include <yasl/desktop.h>
+#if YASL_HAS_VULKAN
+#include <yasl/vulkan.h>
+#endif
#if YASL_HAS_WAYLAND
-#include "yasl/native/wayland.h"
+#include <yasl/native/wayland.h>
#endif
#if YASL_HAS_X11
-#include "yasl/native/x11.h"
+#include <yasl/native/x11.h>
#endif
#if YASL_HAS_COCOA
-#include "yasl/native/cocoa.h"
+#include <yasl/native/cocoa.h>
#endif
-#include "yasl/desktop.h"
-#include "yasl/loop.h"
-#include <yasl/timing.h>
-#include <yasl/yasl.h>
#include <cassert>
-
int main()
{
- yasl_window_cbs callbacks{};
- yasl_window_spec window{};
- window.size = {640, 480};
- window.cbs = &callbacks;
- window.ud = &window;
- assert(!yasl_surface_create_toplevel(nullptr, &window));
- assert(!yasl_surface_create_native(nullptr, &window, &window));
-
- yasl_timing timing = yasl_surface_timing(nullptr);
- assert(timing.source == YASL_TIMING_UNKNOWN);
- assert(yasl_scale_to_device(10.0, 240) == 20);
- yasl_geometry geometry = yasl_surface_geometry(nullptr);
- assert(geometry.scale_120 == YASL_SCALE_UNIT);
- assert(!yasl_display_get_native_handle(nullptr));
- assert(!yasl_surface_get_native_handle(nullptr));
- yasl_native_frame native{};
- assert(!yasl_surface_get_native_frame(nullptr, nullptr, &native));
-
- yasl_loop *loop = yasl_loop_create();
- assert(loop);
- assert(yasl_loop_step(loop, 0) >= 0);
- yasl_loop_destroy(loop);
-
- yasl_message message{};
- assert(!yasl_display_post(nullptr, &message));
- assert(!yasl_frame_present(nullptr, nullptr, 0));
- assert(!yasl_display_poll_event(nullptr, nullptr));
- assert(!yasl_surface_set_size(nullptr, {640, 480}));
- assert(!yasl_surface_set_fullscreen(nullptr, true));
- assert(!yasl_surface_set_maximized(nullptr, true));
- assert(!yasl_surface_minimize(nullptr));
- assert(!yasl_surface_set_pacing(nullptr, YASL_PACING_CONTINUOUS));
- assert(!yasl_surface_set_callbacks(nullptr, nullptr, nullptr));
- assert(!yasl_clipboard_set(nullptr, YASL_CLIPBOARD, ""));
- assert(!yasl_clipboard_request(nullptr, YASL_CLIPBOARD));
- assert(!yasl_app_window(nullptr));
- yasl_app_exit(nullptr, YASL_APP_ERROR);
-
- yasl_event event{};
- event.key.key = YASL_KEY_A;
- assert(event.key.key == YASL_KEY_A);
- event.pointer.scroll = {0.25, -0.5};
- assert(event.pointer.scroll.x == 0.25 && event.pointer.scroll.y == -0.5);
- assert(!yasl_surface_poll_event(nullptr, &event));
- assert(!yasl_display_drag_start(nullptr, nullptr, nullptr, nullptr, nullptr));
- assert(!yasl_surface_drag_carry(nullptr, {0, 0}));
-
-#if YASL_HAS_WAYLAND
- assert(!yasl_wayland_display_get_wl_display(nullptr));
- assert(!yasl_wayland_surface_get_wl_surface(nullptr));
-#endif
-
-#if YASL_HAS_X11
- assert(!yasl_x11_display_get_display(nullptr));
- assert(!yasl_x11_surface_get_window(nullptr));
-#endif
-
-#if YASL_HAS_COCOA
- assert(!yasl_cocoa_display_get_application(nullptr));
- assert(!yasl_cocoa_surface_get_window(nullptr));
- assert(!yasl_cocoa_surface_get_view(nullptr));
-#endif
-
- assert(!yasl_display_open_uri(nullptr, "https://example.com"));
- assert(yasl_display_appearance(nullptr) == 0);
- assert(!yasl_surface_bell(nullptr));
- assert(yasl_executable_path(nullptr, 0) > 0);
-
- return 0;
+ yasl_event e{};
+ e.pen.x=1.5;e.pen.pressure=0.25;
+ assert(e.pen.pressure==0.25);
+ assert(!yasl_surface_create("invalid",-1,10,0));
+ yasl_shutdown();return 0;
}
diff --git a/tests/test_lifecycle.c b/tests/test_lifecycle.c
index 00d1264..a334fed 100644
--- a/tests/test_lifecycle.c
+++ b/tests/test_lifecycle.c
@@ -1,75 +1,24 @@
-#include "yasl/loop.h"
-#include "yasl/yasl.h"
+#include <yasl/yasl.h>
+#include <yasl/cpu.h>
+#include <yasl/gl.h>
+#include <yasl/desktop.h>
#include <assert.h>
-#include <poll.h>
-#include <stdio.h>
-#include <unistd.h>
-
-static void test_scaling(void)
-{
- assert(yasl_scale_to_device(10.0, 120) == 10);
- assert(yasl_scale_to_device(10.0, 240) == 20);
- assert(yasl_scale_to_logical(20, 240) == 10);
-}
-
-static int dummy_cb(int fd, short revents, void *ud)
-{
- (void)fd; (void)revents; (void)ud;
- return 0;
-}
-
-static void test_loop(void)
-{
- struct yasl_loop *loop = yasl_loop_create();
- assert(loop != NULL);
-
- int p[2];
- assert(pipe(p) == 0);
- int watch = yasl_loop_add_fd(loop, p[0], POLLIN, dummy_cb, NULL);
- assert(watch > 0);
- assert(yasl_loop_set_fd_events(loop, watch, POLLIN | POLLOUT) == 0);
-
- yasl_loop_kick(loop);
- int n = yasl_loop_step(loop, 10);
- assert(n >= 1);
-
- assert(yasl_loop_del_fd(loop, watch) == 0);
- close(p[0]);
- close(p[1]);
-
- yasl_loop_destroy(loop);
-}
-
-static void test_display_lifecycle_standalone(void)
-{
- struct yasl_display_spec spec = { .app_id = "org.yasl.test" };
- struct yasl_display *d = yasl_display_open(NULL, &spec);
- if (!d) return; /* Headless CI without Wayland */
-
- struct yasl_window_spec sspec = {
- .title = "Test Window",
- .size = { .x = 320, .y = 240 },
- };
- struct yasl_surface *s = yasl_surface_create_toplevel(d, &sspec);
- assert(s != NULL);
- assert(yasl_surface_display(s) == d);
-
- struct yasl_vector2i sz = yasl_surface_geometry(s).size;
- assert(sz.x > 0 && sz.y > 0);
-
- yasl_surface_schedule_redraw(s);
- yasl_display_flush(d);
- yasl_display_dispatch(d, 0);
-
- yasl_surface_destroy(s);
- yasl_display_close(d);
-}
-
int main(void)
{
- test_scaling();
- test_loop();
- test_display_lifecycle_standalone();
- printf("All YASL basic tests passed.\n");
- return 0;
+ yasl_shutdown(); yasl_wake();
+ yasl_event e={.type=YASL_EVENT_QUIT};
+ assert(yasl_wait_event(&e,0)==-1 && e.type==YASL_EVENT_NONE);
+ assert(yasl_get_error(NULL)==YASL_ERROR_NOT_INITIALIZED);
+ assert(!yasl_set_backend((enum yasl_backend)999));
+ assert(yasl_get_error(NULL)==YASL_ERROR_INVALID);
+ assert(!yasl_surface_create("bad",0,64,0));
+ assert(!yasl_surface_create("bad",64,64,16));
+ assert(yasl_set_app_id("org.yasl.api"));
+ assert(yasl_set_backend(YASL_BACKEND_AUTO));
+ yasl_surface_destroy(NULL);
+ assert(yasl_time_ns()>0);
+ const char *description=NULL;yasl_get_error(&description);assert(description);
+ int w=9,h=9;yasl_surface_get_size(NULL,&w,&h);assert(w==0&&h==0);
+ assert(yasl_executable_path(NULL,0)>0);
+ yasl_shutdown();return 0;
}
diff --git a/tests/wayland/test_app_input.c b/tests/wayland/test_app_input.c
index 7e0e9a0..c8e4d2b 100644
--- a/tests/wayland/test_app_input.c
+++ b/tests/wayland/test_app_input.c
@@ -1,132 +1,78 @@
-/* An input event callback that destroys its own surface. The app runner
- * must not touch the surface's entry after the callback, and must not
- * deliver the surface's remaining input. Needs a Wayland compositor. */
-#include "platform/wayland/internal.h"
-#include "yasl/app.h"
+/* The public event queue must retain FIFO order and discard events targeted
+ * at a surface when that surface is destroyed. Native input is injected
+ * through the backend queue, without requiring a compositor. */
+#include "yasl/yasl.h"
+#include "common/backend.h"
#include <assert.h>
-#include <errno.h>
-#include <stdlib.h>
+#include <string.h>
-struct test_state {
- bool initialized;
- int keys;
- int buttons, motions, resets;
- int end;
+static struct input_queue *surface_queue(struct yasl_surface *s)
+{
+ return &s->input;
+}
+
+static const struct yasl_private_backend_ops test_ops = {
+ .surface_input_queue = surface_queue,
};
-static void push_key(struct yasl_surface *s)
+static void setup(struct yasl_private_display *display, struct yasl_surface *surface)
{
- struct yasl_event ev = {
- .surface = s,
- .type = YASL_EVENT_KEY,
- .key = { .key = YASL_KEY_A, .action = YASL_KEY_PRESS },
- };
- assert(input_queue_push(&((struct wayland_surface *)s)->input, &ev));
+ *display = (struct yasl_private_display){.ops = &test_ops};
+ *surface = (struct yasl_surface){.display = display, .scale_120 = 120};
+ assert(yasl_private_events_open(display));
+ assert(yasl_private_events_register(surface));
}
-static enum yasl_app_result on_init(void *ud, struct yasl_display *d)
+static void destroy_surface(struct yasl_surface *surface)
{
- struct test_state *state = ud;
- state->initialized = true;
- if (state->end == 3) {
- /* With no event callback, a close request alone stops the runner. */
- struct yasl_surface *s = yasl_surface_create_toplevel(d,
- &(struct yasl_window_spec){ .title = "App Close Test", .size = {64, 64} });
- assert(s);
- yasl_surface_notify_close(s);
- return YASL_APP_CONTINUE;
- }
- if (state->end) {
- if (state->end == 1)
- yasl_display_notify_event(d, YASL_EVENT_DISPLAY_QUIT);
- else
- yasl_display_notify_event(d, YASL_EVENT_DISPLAY_LOST);
- return YASL_APP_CONTINUE;
- }
- struct yasl_surface *s = yasl_surface_create_toplevel(d,
- &(struct yasl_window_spec){ .title = "App Input Test", .size = {64, 64} });
- assert(s);
- struct input_queue *queue = &((struct wayland_surface *)s)->input;
- input_queue_clear(queue);
- struct yasl_event event = {.surface = s, .type = YASL_EVENT_POINTER_DOWN,
- .pointer = {.device = YASL_PTR_DEV_ERASER, .button = YASL_BUTTON_PRIMARY,
- .pos = {12, 34}, .pen = {.pressure = 50000, .tilt_x = -20, .tilt_y = 30}}};
- assert(input_queue_push(queue, &event));
- event.type = YASL_EVENT_POINTER_MOTION;
- event.pointer.pos = (struct yasl_vector2i){15, 39};
- assert(input_queue_push(queue, &event));
- struct yasl_event reset = {.surface = s, .type = YASL_EVENT_INPUT_RESET};
- assert(input_queue_push(queue, &reset));
- event.pointer.device = YASL_PTR_DEV_PEN;
- event.pointer.pos = (struct yasl_vector2i){100, 200};
- assert(input_queue_push(queue, &event));
- push_key(s);
- push_key(s);
- return YASL_APP_CONTINUE;
+ yasl_private_events_unregister(surface);
+ input_queue_clear(&surface->input);
}
-static enum yasl_app_result on_event(void *ud, const struct yasl_event *ev)
+static void test_fifo_and_destroy(void)
{
- struct test_state *state = ud;
- if (ev->type == YASL_EVENT_POINTER_DOWN) {
- assert(ev->pointer.device == YASL_PTR_DEV_ERASER);
- assert(ev->pointer.button == YASL_BUTTON_PRIMARY);
- assert(ev->pointer.pos.x == 12 && ev->pointer.pos.y == 34);
- assert(ev->pointer.pen.pressure == 50000);
- assert(ev->pointer.pen.tilt_x == -20 && ev->pointer.pen.tilt_y == 30);
- state->buttons++;
- } else if (ev->type == YASL_EVENT_POINTER_MOTION) {
- if (!state->motions) {
- assert(ev->pointer.device == YASL_PTR_DEV_ERASER);
- assert(ev->pointer.delta.x == 3 && ev->pointer.delta.y == 5);
- } else {
- assert(ev->pointer.device == YASL_PTR_DEV_PEN);
- assert(!ev->pointer.delta.x && !ev->pointer.delta.y);
- }
- assert(ev->pointer.pen.pressure == 50000);
- state->motions++;
- } else if (ev->type == YASL_EVENT_INPUT_RESET) {
- state->resets++;
- } else if (ev->type == YASL_EVENT_KEY) {
- state->keys++;
- yasl_surface_destroy(ev->surface);
- }
- return YASL_APP_CONTINUE;
+ struct yasl_private_display display;
+ struct yasl_surface surface;
+ setup(&display, &surface);
+
+ assert(input_queue_push(&surface.input, &(struct yasl_private_event){
+ .type = YASL_PRIVATE_EVENT_KEY,
+ .surface = &surface,
+ .key = {.key = YASL_KEY_A, .action = YASL_KEY_PRESS},
+ }));
+ assert(input_queue_push(&surface.input, &(struct yasl_private_event){
+ .type = YASL_PRIVATE_EVENT_TEXT,
+ .surface = &surface,
+ .text = {.data = "hello", .len = 5},
+ }));
+
+ struct yasl_private_event event;
+ assert(yasl_private_display_poll_event(&display, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_KEY && event.key.key == YASL_KEY_A);
+ assert(yasl_private_display_poll_event(&display, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_TEXT && event.text.len == 5);
+ assert(!strcmp(event.text.data, "hello"));
+
+ /* Queue multiple events, then destroy the target before consumption. */
+ assert(input_queue_push(&surface.input, &(struct yasl_private_event){
+ .type = YASL_PRIVATE_EVENT_KEY, .surface = &surface,
+ .key = {.key = YASL_KEY_B, .action = YASL_KEY_PRESS},
+ }));
+ assert(input_queue_push(&surface.input, &(struct yasl_private_event){
+ .type = YASL_PRIVATE_EVENT_TEXT, .surface = &surface,
+ .text = {.data = "discard", .len = 7},
+ }));
+ destroy_surface(&surface);
+ assert(!yasl_private_display_poll_event(&display, &event));
+ yasl_private_events_close(&display);
+
+ /* Public event names remain the event stream consumed by applications. */
+ assert(YASL_EVENT_KEY != YASL_EVENT_TEXT);
}
int main(void)
{
- if (!getenv("WAYLAND_DISPLAY"))
- return 77;
- struct test_state state = {0};
- int res = yasl_run(&(struct yasl_app_desc){
- .app_id = "org.yasl.test.app-input",
- .backend = YASL_BACKEND_WAYLAND,
- .userdata = &state,
- .init = on_init,
- .event = on_event,
- });
- /* A display variable may exist even when the test sandbox cannot connect
- * to that compositor. Keep this as an integration test in that case. */
- if (res < 0 && !state.initialized &&
- (errno == EACCES || errno == EPERM || errno == ENOENT ||
- errno == ECONNREFUSED || errno == ENODEV))
- return 77;
- assert(res == 0);
- assert(state.keys == 1);
- assert(state.buttons == 1 && state.motions == 2 && state.resets == 1);
- for (int end = 1; end <= 2; end++) {
- state = (struct test_state){.end = end};
- res = yasl_run(&(struct yasl_app_desc){
- .backend = YASL_BACKEND_WAYLAND, .userdata = &state, .init = on_init,
- });
- assert(end == 1 ? res == 0 : res < 0);
- }
- state = (struct test_state){.end = 3};
- res = yasl_run(&(struct yasl_app_desc){
- .backend = YASL_BACKEND_WAYLAND, .userdata = &state, .init = on_init,
- });
- assert(res == 0);
+ test_fifo_and_destroy();
return 0;
}
diff --git a/tests/wayland/test_clipboard.c b/tests/wayland/test_clipboard.c
index 1778ad2..0ee74b6 100644
--- a/tests/wayland/test_clipboard.c
+++ b/tests/wayland/test_clipboard.c
@@ -6,7 +6,7 @@ struct received {
int count;
size_t len;
uint64_t request_id;
- enum yasl_transfer_status status;
+ enum yasl_private_transfer_status status;
struct clipboard *clipboard;
bool forget_on_receive;
char text[32];
@@ -14,12 +14,12 @@ struct received {
static void receive(const char *text, size_t len, bool uri_list,
struct wl_surface *surface, uint64_t request_id,
- enum yasl_transfer_status status, void *ud)
+ enum yasl_private_transfer_status status, void *ud)
{
(void)surface;
struct received *r = ud;
assert(!uri_list && len < sizeof(r->text));
- if (status == YASL_TRANSFER_OK) {
+ if (status == YASL_PRIVATE_TRANSFER_OK) {
assert(text);
memcpy(r->text, text, len);
r->text[len] = '\0';
@@ -39,7 +39,7 @@ static void receive(const char *text, size_t len, bool uri_list,
int main(void)
{
struct received received = {0};
- struct clipboard c = {.loop = yasl_loop_create(), .receive = receive, .ud = &received};
+ struct clipboard c = {.loop = yasl_private_loop_create(), .receive = receive, .ud = &received};
received.clipboard = &c;
assert(c.loop);
const struct { const char *bytes; size_t len; bool valid; } cases[] = {
@@ -55,19 +55,19 @@ int main(void)
assert(pipe(fds) == 0);
struct transfer *transfer = start_transfer_text(&c, fds[0], false, false, c.text);
assert(transfer);
- yasl_timer_id timeout_id = transfer->timeout;
- assert(timeout_id && yasl_loop_timer_armed(c.loop, timeout_id));
+ yasl_private_timer_id timeout_id = transfer->timeout;
+ assert(timeout_id && yasl_private_loop_timer_armed(c.loop, timeout_id));
transfer->paste = true;
transfer->request_id = 42;
assert(write(fds[1], cases[i].bytes, cases[i].len) == (ssize_t)cases[i].len);
close(fds[1]);
for (int steps = 0; c.transfers && steps < 10; steps++)
- assert(yasl_loop_step(c.loop, 0) >= 0);
+ assert(yasl_private_loop_step(c.loop, 0) >= 0);
assert(!c.transfers);
- assert(!yasl_loop_timer_armed(c.loop, timeout_id));
+ assert(!yasl_private_loop_timer_armed(c.loop, timeout_id));
assert(received.count == 1);
assert(received.request_id == 42);
- assert(received.status == (cases[i].valid ? YASL_TRANSFER_OK : YASL_TRANSFER_FAILED));
+ assert(received.status == (cases[i].valid ? YASL_PRIVATE_TRANSFER_OK : YASL_PRIVATE_TRANSFER_FAILED));
if (cases[i].valid) {
assert(received.len == cases[i].len);
assert(memcmp(received.text, cases[i].bytes, received.len) == 0);
@@ -79,8 +79,8 @@ int main(void)
assert(pipe(cancel_fds) == 0);
struct transfer *pending = start_transfer_text(&c, cancel_fds[0], false, false, NULL);
assert(pending);
- yasl_timer_id cancel_timeout_id = pending->timeout;
- assert(cancel_timeout_id && yasl_loop_timer_armed(c.loop, cancel_timeout_id));
+ yasl_private_timer_id cancel_timeout_id = pending->timeout;
+ assert(cancel_timeout_id && yasl_private_loop_timer_armed(c.loop, cancel_timeout_id));
pending->paste = true;
pending->request_id = 99;
struct wl_surface *target = (struct wl_surface *)(uintptr_t)1;
@@ -88,7 +88,7 @@ int main(void)
clipboard_forget_surface(&c, target);
close(cancel_fds[1]);
assert(!c.transfers && received.count == 0);
- assert(!yasl_loop_timer_armed(c.loop, cancel_timeout_id));
+ assert(!yasl_private_loop_timer_armed(c.loop, cancel_timeout_id));
received.forget_on_receive = true;
int timeout_fds[2];
@@ -99,12 +99,12 @@ int main(void)
timed->request_id = 100;
struct wl_surface *timeout_target = (struct wl_surface *)(uintptr_t)2;
timed->drop_surface = timeout_target;
- yasl_timer_id timeout_id = timed->timeout;
- assert(yasl_loop_del_timer(c.loop, timeout_id));
+ yasl_private_timer_id timeout_id = timed->timeout;
+ assert(yasl_private_loop_del_timer(c.loop, timeout_id));
(void)transfer_timeout(timed, timeout_id, CLIPBOARD_TRANSFER_TIMEOUT_NS);
close(timeout_fds[1]);
assert(!c.transfers && received.count == 1);
- assert(received.request_id == 100 && received.status == YASL_TRANSFER_FAILED);
- yasl_loop_destroy(c.loop);
+ assert(received.request_id == 100 && received.status == YASL_PRIVATE_TRANSFER_FAILED);
+ yasl_private_loop_destroy(c.loop);
return 0;
}
diff --git a/tests/wayland/test_cursor.c b/tests/wayland/test_cursor.c
index eb729a3..3425821 100644
--- a/tests/wayland/test_cursor.c
+++ b/tests/wayland/test_cursor.c
@@ -51,9 +51,9 @@ static uint32_t pointer_version(struct wl_pointer *pointer)
int main(void)
{
- struct wayland_display display = {.base.ops = &yasl_wayland_backend};
+ struct wayland_display display = {.base.ops = &yasl_private_wayland_backend};
struct wayland_surface surface = {.base.display = &display.base, .conn = &display,
- .scale_factor = 1, .cursor = YASL_CURSOR_TEXT};
+ .scale_factor = 1, .cursor = YASL_PRIVATE_CURSOR_TEXT};
surface.surface = (struct wl_surface *)&surface;
wl_surface_set_user_data(surface.surface, &surface);
display.surfaces = &surface;
@@ -64,7 +64,7 @@ int main(void)
tool.proxy = (struct zwp_tablet_tool_v2 *)&tool;
/* The example selects hidden before either input device has entered. */
- yasl_surface_set_cursor(&surface.base, YASL_CURSOR_HIDDEN);
+ yasl_private_surface_set_cursor(&surface.base, YASL_PRIVATE_CURSOR_HIDDEN);
assert(!pointer_hides && !tool_hides);
pointer_enter(&seat, seat.pointer, 10, surface.surface, 0, 0);
assert(pointer_hides == 1 && pointer_serial == 10);
@@ -76,14 +76,16 @@ int main(void)
pointer_frame(&seat, seat.pointer);
tablet_tool_motion(&tool, tool.proxy, wl_fixed_from_int(56), wl_fixed_from_int(78));
tool_frame(&tool, tool.proxy, 0);
- struct yasl_event event;
- assert(yasl_surface_poll_event(&surface.base, &event));
- assert(event.type == YASL_EVENT_POINTER_MOTION && event.pointer.device == YASL_PTR_DEV_MOUSE);
+ struct yasl_private_event event;
+ assert(yasl_private_surface_poll_event(&surface.base, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_POINTER_MOTION && event.pointer.device == YASL_PRIVATE_PTR_DEV_MOUSE);
assert(event.pointer.pos.x == 12 && event.pointer.pos.y == 34);
- assert(yasl_surface_poll_event(&surface.base, &event));
- assert(event.type == YASL_EVENT_POINTER_MOTION && event.pointer.device == YASL_PTR_DEV_PEN);
+ assert(yasl_private_surface_poll_event(&surface.base, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_PEN && event.pen.action == YASL_PRIVATE_PEN_ENTER);
+ assert(yasl_private_surface_poll_event(&surface.base, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_POINTER_MOTION && event.pointer.device == YASL_PRIVATE_PTR_DEV_PEN);
assert(event.pointer.pos.x == 56 && event.pointer.pos.y == 78);
- assert(!yasl_surface_poll_event(&surface.base, &event));
+ assert(!yasl_private_surface_poll_event(&surface.base, &event));
/* Each new entry needs a request with its own serial. */
pointer_leave(&seat, seat.pointer, 12, surface.surface);
@@ -98,20 +100,20 @@ int main(void)
/* Restore a visible shape on both currently focused devices. */
pointer_device = seat.cursor_shape_dev = (struct wp_cursor_shape_device_v1 *)&seat;
tool.shape_dev = (struct wp_cursor_shape_device_v1 *)&tool;
- yasl_surface_set_cursor(&surface.base, YASL_CURSOR_ARROW);
+ yasl_private_surface_set_cursor(&surface.base, YASL_PRIVATE_CURSOR_ARROW);
assert(pointer_shapes == 1 && tool_shapes == 1);
assert(pointer_shape == WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
assert(tool_shape == WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT);
- yasl_surface_set_cursor(&surface.base, YASL_CURSOR_HIDDEN);
+ yasl_private_surface_set_cursor(&surface.base, YASL_PRIVATE_CURSOR_HIDDEN);
assert(pointer_hides == 3 && tool_hides == 3);
assert(pointer_shapes == 1 && tool_shapes == 1);
/* Updating an unfocused surface must not alter the active cursor. */
struct wayland_surface other = {.base.display = &display.base, .conn = &display};
- yasl_surface_set_cursor(&other.base, YASL_CURSOR_CROSSHAIR);
+ yasl_private_surface_set_cursor(&other.base, YASL_PRIVATE_CURSOR_CROSSHAIR);
assert(pointer_shapes == 1 && tool_shapes == 1);
- yasl_surface_set_cursor(&surface.base, (enum yasl_cursor)-1);
- yasl_surface_set_cursor(&surface.base, (enum yasl_cursor)(YASL_CURSOR_HIDDEN + 1));
+ yasl_private_surface_set_cursor(&surface.base, (enum yasl_private_cursor)-1);
+ yasl_private_surface_set_cursor(&surface.base, (enum yasl_private_cursor)(YASL_PRIVATE_CURSOR_HIDDEN + 1));
assert(pointer_hides == 3 && tool_hides == 3);
assert(pointer_shapes == 1 && tool_shapes == 1);
input_queue_clear(&surface.input);
diff --git a/tests/wayland/test_input.c b/tests/wayland/test_input.c
index f6112b6..2b5592a 100644
--- a/tests/wayland/test_input.c
+++ b/tests/wayland/test_input.c
@@ -1,17 +1,35 @@
#include "platform/wayland/internal.h"
#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+static void test_ime_capability(void)
+{
+ struct wayland_display display = {0};
+ struct wayland_surface w = {.conn = &display};
+ assert(!(wayland_display_caps(&display) & YASL_PRIVATE_CAP_TEXT_INPUT));
+ errno = 0;
+ assert(!wayland_surface_update_text_input(&w) && errno == ENOTSUP);
+ display.text_input_mgr = (struct zwp_text_input_manager_v3 *)&display;
+ assert(wayland_display_caps(&display) & YASL_PRIVATE_CAP_TEXT_INPUT);
+ w.base.text_input_active = true;
+ w.base.text_purpose = 1; /* Public YASL_TEXT_PASSWORD. */
+ /* A supported compositor can accept the requested state before a seat
+ * or keyboard focus has arrived. The enter callback applies it later. */
+ assert(wayland_surface_update_text_input(&w));
+}
static void test_popup_bounds(void)
{
struct seat seat = {.input_serial = 1};
- struct wayland_display d = {.base.ops = &yasl_wayland_backend, .seats = &seat};
+ struct wayland_display d = {.base.ops = &yasl_private_wayland_backend, .seats = &seat};
struct wayland_surface w = {.base.display = &d.base, .conn = &d};
w.parent = &w;
w.content.x = w.content.y = 1;
/* Invalid bounds must be rejected before accessing any protocol object. */
w.xdg_surface = (struct xdg_surface *)&w;
w.popup = (struct xdg_popup *)&w;
- struct yasl_popup_spec specs[] = {
+ struct yasl_private_popup_spec specs[] = {
{.size = {100, 100}, .content_offset = {INT_MAX, 0}, .content_size = {1, 0}},
{.size = {100, 100}, .content_offset = {0, INT_MAX}, .content_size = {0, 1}},
{.size = {100, 100}, .content_offset = {1, 0}, .content_size = {INT_MAX, 0}},
@@ -20,16 +38,18 @@ static void test_popup_bounds(void)
{.size = {100, 100}, .anchor = {0, INT_MIN, 1, 1}},
};
for (size_t i = 0; i < sizeof(specs) / sizeof(specs[0]); i++) {
- assert(!yasl_surface_create_popup(&w.base, &specs[i]));
- assert(!yasl_surface_popup_reposition(&w.base, &specs[i]));
+ assert(!yasl_private_surface_create_popup(&w.base, &specs[i]));
+ assert(!yasl_private_surface_popup_reposition(&w.base, &specs[i]));
}
}
int main(void)
{
+ test_ime_capability();
test_popup_bounds();
struct wayland_surface w = {0};
struct seat seat = {.ime_focus = &w};
+ w.base.text_input_active = true;
wayland_surface_set_callbacks(&w, NULL, NULL);
for (int i = 0; i < 600; i++) {
char text[32];
@@ -41,11 +61,38 @@ int main(void)
for (int i = 0; i < 600; i++) {
char text[32];
snprintf(text, sizeof(text), "commit-%d", i);
- struct yasl_event event;
+ struct yasl_private_event event;
assert(wayland_surface_poll_event(&w, &event));
- assert(event.type == YASL_EVENT_TEXT && event.surface == &w.base);
+ assert(event.type == YASL_PRIVATE_EVENT_TEXT && event.surface == &w.base);
assert(strcmp(event.text.data, text) == 0);
}
+ w.base.text_input_active = true;
+ w.base.text_surrounding = strdup("ab\xf0\x9f\x98\x80" "cd");
+ w.base.text_length = 8;
+ w.base.text_begin = 2;
+ w.base.text_end = 6;
+ w.base.text_revision = 41;
+ w.base.text_generation = 9;
+ text_input_listener.delete_surrounding_text(&seat, NULL, 2, 2);
+ text_input_listener.commit_string(&seat, NULL, "X");
+ text_input_listener.done(&seat, NULL, 601);
+ struct yasl_private_event edit;
+ assert(wayland_surface_poll_event(&w, &edit));
+ assert(edit.type == YASL_PRIVATE_EVENT_TEXT_EDIT && edit.surface == &w.base);
+ assert(strcmp(edit.text_edit.data, "X") == 0 && edit.text_edit.len == 1);
+ assert(edit.text_edit.begin == 2 && edit.text_edit.end == 8);
+ assert(edit.text_edit.revision == 41 && edit.text_edit.generation == 9);
+ struct yasl_private_event committed;
+ assert(!wayland_surface_poll_event(&w, &committed));
+
+ /* Deletion without a commit replaces the range with the empty string. */
+ text_input_listener.delete_surrounding_text(&seat, NULL, 2, 2);
+ text_input_listener.done(&seat, NULL, 602);
+ assert(wayland_surface_poll_event(&w, &edit));
+ assert(edit.type == YASL_PRIVATE_EVENT_TEXT_EDIT && edit.text_edit.len == 0);
+ assert(edit.text_edit.data[0] == '\0' && edit.text_edit.begin == 2 && edit.text_edit.end == 8);
+ assert(!wayland_surface_poll_event(&w, &committed));
input_queue_clear(&w.input);
+ free(w.base.text_surrounding);
return 0;
}
diff --git a/tests/wayland/test_keyboard.c b/tests/wayland/test_keyboard.c
index bce0a0c..ae4647f 100644
--- a/tests/wayland/test_keyboard.c
+++ b/tests/wayland/test_keyboard.c
@@ -1,18 +1,18 @@
#include "../../src/platform/wayland/keyboard.c"
#include <assert.h>
-static struct yasl_event next(struct wayland_surface *w, enum yasl_event_type type)
+static struct yasl_private_event next(struct wayland_surface *w, enum yasl_private_event_type type)
{
- struct yasl_event ev;
- assert(yasl_surface_poll_event(&w->base, &ev));
+ struct yasl_private_event ev;
+ assert(yasl_private_surface_poll_event(&w->base, &ev));
assert(ev.type == type && ev.surface == &w->base);
return ev;
}
static void empty(struct wayland_surface *w)
{
- struct yasl_event ev;
- assert(!yasl_surface_poll_event(&w->base, &ev));
+ struct yasl_private_event ev;
+ assert(!yasl_private_surface_poll_event(&w->base, &ev));
}
static void layout(struct seat *s, const char *name, const char *variant)
@@ -43,7 +43,7 @@ static void key(struct seat *s, uint32_t code, bool down)
int main(void)
{
- struct wayland_display d = {.base.ops = &yasl_wayland_backend};
+ struct wayland_display d = {.base.ops = &yasl_private_wayland_backend};
d.xkb_ctx = xkb_context_new(0);
assert(d.xkb_ctx);
struct wayland_surface w = {.base.display = &d.base, .conn = &d};
@@ -59,65 +59,66 @@ int main(void)
xkb_compose_table_unref(table);
layout(&s, "us", NULL);
key(&s, KEY_A, true);
- struct yasl_event ev = next(&w, YASL_EVENT_KEY);
+ struct yasl_private_event ev = next(&w, YASL_PRIVATE_EVENT_KEY);
assert(ev.key.key == YASL_KEY_A && ev.key.native_code == KEY_A);
assert(ev.key.codepoint == 'a' && ev.key.shifted == 'A' && !ev.key.mods);
- assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "a") == 0);
+ assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "a") == 0);
modifiers(&s, XKB_MOD_NAME_SHIFT);
key(&s, KEY_A, false); /* Modifier changes cannot change physical identity. */
- ev = next(&w, YASL_EVENT_KEY);
+ ev = next(&w, YASL_PRIVATE_EVENT_KEY);
assert(ev.key.key == YASL_KEY_A && ev.key.action == YASL_KEY_RELEASE);
- assert(ev.key.mods == YASL_MOD_SHIFT);
+ assert(ev.key.mods == YASL_PRIVATE_MOD_SHIFT);
empty(&w);
key(&s, KEY_A, true);
- next(&w, YASL_EVENT_KEY);
- assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "A") == 0);
+ next(&w, YASL_PRIVATE_EVENT_KEY);
+ assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "A") == 0);
modifiers(&s, XKB_MOD_NAME_CAPS);
key(&s, KEY_A, true);
- assert(next(&w, YASL_EVENT_KEY).key.mods == YASL_MOD_CAPS);
- assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "A") == 0);
+ assert(next(&w, YASL_PRIVATE_EVENT_KEY).key.mods == YASL_PRIVATE_MOD_CAPS);
+ assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "A") == 0);
const char *shortcuts[] = {XKB_MOD_NAME_CTRL, XKB_MOD_NAME_ALT, XKB_MOD_NAME_LOGO};
for (size_t i = 0; i < sizeof(shortcuts)/sizeof(shortcuts[0]); i++) {
modifiers(&s, shortcuts[i]);
key(&s, KEY_A, true);
- next(&w, YASL_EVENT_KEY);
+ next(&w, YASL_PRIVATE_EVENT_KEY);
empty(&w); /* Shortcut characters never leak into committed text. */
}
layout(&s, "fr", NULL);
key(&s, KEY_A, true);
- ev = next(&w, YASL_EVENT_KEY);
+ ev = next(&w, YASL_PRIVATE_EVENT_KEY);
assert(ev.key.key == YASL_KEY_A && ev.key.codepoint == 'q');
- assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "q") == 0);
+ assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "q") == 0);
layout(&s, "de", NULL);
modifiers(&s, "Mod5"); /* AltGr is text input, not an Alt shortcut. */
key(&s, KEY_E, true);
- assert(next(&w, YASL_EVENT_KEY).key.key == YASL_KEY_E);
- assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "€") == 0);
+ assert(next(&w, YASL_PRIVATE_EVENT_KEY).key.key == YASL_KEY_E);
+ assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "€") == 0);
layout(&s, "us", "intl");
key(&s, KEY_APOSTROPHE, true);
- next(&w, YASL_EVENT_KEY);
+ next(&w, YASL_PRIVATE_EVENT_KEY);
empty(&w);
key(&s, KEY_E, true);
- next(&w, YASL_EVENT_KEY);
- assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "é") == 0);
+ next(&w, YASL_PRIVATE_EVENT_KEY);
+ assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "é") == 0);
assert(keyboard_dispatch_key(&s, &w, KEY_E, YASL_KEY_REPEAT));
- assert(next(&w, YASL_EVENT_KEY).key.action == YASL_KEY_REPEAT);
- assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "e") == 0);
+ assert(next(&w, YASL_PRIVATE_EVENT_KEY).key.action == YASL_KEY_REPEAT);
+ assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "e") == 0);
key(&s, KEY_APOSTROPHE, true);
- next(&w, YASL_EVENT_KEY);
+ next(&w, YASL_PRIVATE_EVENT_KEY);
keyboard_reset_keys(&s, &w);
key(&s, KEY_E, true);
- next(&w, YASL_EVENT_KEY);
- assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "e") == 0);
+ next(&w, YASL_PRIVATE_EVENT_KEY);
+ assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "e") == 0);
key(&s, KEY_APOSTROPHE, true);
- next(&w, YASL_EVENT_KEY);
+ next(&w, YASL_PRIVATE_EVENT_KEY);
s.ime_focus = &w;
+ w.base.text_input_active = true;
text_input_listener.commit_string(&s, NULL, "字");
text_input_listener.done(&s, NULL, 0);
- assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "字") == 0);
+ assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "字") == 0);
key(&s, KEY_E, true);
- next(&w, YASL_EVENT_KEY);
- assert(strcmp(next(&w, YASL_EVENT_TEXT).text.data, "e") == 0);
+ next(&w, YASL_PRIVATE_EVENT_KEY);
+ assert(strcmp(next(&w, YASL_PRIVATE_EVENT_TEXT).text.data, "e") == 0);
empty(&w);
xkb_compose_state_unref(s.compose);
xkb_state_unref(s.xkb_state);
diff --git a/tests/wayland/test_popup_positioner.c b/tests/wayland/test_popup_positioner.c
new file mode 100644
index 0000000..86778f6
--- /dev/null
+++ b/tests/wayland/test_popup_positioner.c
@@ -0,0 +1,93 @@
+/* Capture xdg_positioner requests to verify explicit placement and the legacy
+ * beside mapping without requiring a compositor. */
+#include "platform/wayland/internal.h"
+#include <assert.h>
+
+struct recorded_positioner {
+ int width, height;
+ int rect_x, rect_y, rect_width, rect_height;
+ uint32_t anchor, gravity, constraints;
+ bool reactive;
+};
+
+static struct recorded_positioner record;
+static uint64_t fake_positioner_storage[8];
+#define FAKE_POSITIONER ((struct xdg_positioner *)fake_positioner_storage)
+static struct xdg_positioner *fake_create(struct xdg_wm_base *base)
+{ (void)base; record = (struct recorded_positioner){0}; return FAKE_POSITIONER; }
+static void fake_size(struct xdg_positioner *pos, int32_t width, int32_t height)
+{ assert(pos == FAKE_POSITIONER); record.width = width; record.height = height; }
+static void fake_rect(struct xdg_positioner *pos, int32_t x, int32_t y, int32_t width, int32_t height)
+{ assert(pos == FAKE_POSITIONER); record.rect_x=x; record.rect_y=y; record.rect_width=width; record.rect_height=height; }
+static void fake_anchor(struct xdg_positioner *pos, uint32_t value)
+{ assert(pos == FAKE_POSITIONER); record.anchor = value; }
+static void fake_gravity(struct xdg_positioner *pos, uint32_t value)
+{ assert(pos == FAKE_POSITIONER); record.gravity = value; }
+static void fake_constraints(struct xdg_positioner *pos, uint32_t value)
+{ assert(pos == FAKE_POSITIONER); record.constraints = value; }
+static uint32_t fake_version(struct xdg_positioner *pos)
+{ assert(pos == FAKE_POSITIONER); return XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION; }
+static void fake_reactive(struct xdg_positioner *pos)
+{ assert(pos == FAKE_POSITIONER); record.reactive = true; }
+
+#define xdg_wm_base_create_positioner fake_create
+#define xdg_positioner_set_size fake_size
+#define xdg_positioner_set_anchor_rect fake_rect
+#define xdg_positioner_set_anchor fake_anchor
+#define xdg_positioner_set_gravity fake_gravity
+#define xdg_positioner_set_constraint_adjustment fake_constraints
+#define xdg_positioner_get_version fake_version
+#define xdg_positioner_set_reactive fake_reactive
+#include "../../src/platform/wayland/popup.c"
+
+int main(void)
+{
+ struct wayland_display display = {.wm_base = (struct xdg_wm_base *)&record};
+ struct wayland_surface parent = {
+ .scale_120 = 120,
+ .content = {.x = 3, .y = 4},
+ };
+ struct wayland_surface popup = {
+ .conn = &display,
+ .parent = &parent,
+ .scale_120 = 120,
+ .content = {.width = 48, .height = 36},
+ };
+ struct yasl_private_popup_spec spec = {
+ .size = {64, 48},
+ .anchor = {13, 24, 8, 6},
+ .beside = true,
+ };
+
+ struct xdg_positioner *positioner = popup_positioner(&popup, &spec);
+ assert(positioner == FAKE_POSITIONER);
+ assert(record.width == 48 && record.height == 36);
+ assert(record.rect_x == 10 && record.rect_y == 20 &&
+ record.rect_width == 8 && record.rect_height == 6);
+ assert(record.anchor == XDG_POSITIONER_ANCHOR_TOP_RIGHT);
+ assert(record.gravity == XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
+ assert(record.constraints == (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X |
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y |
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X |
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y));
+ assert(record.reactive);
+
+ spec.positioner_explicit = true;
+ spec.anchor_edges = XDG_POSITIONER_ANCHOR_NONE;
+ spec.gravity = XDG_POSITIONER_GRAVITY_RIGHT;
+ spec.constraints = XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X |
+ XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y;
+ assert(popup_spec_valid(&parent, &spec));
+ positioner = popup_positioner(&popup, &spec);
+ assert(positioner == FAKE_POSITIONER);
+ assert(record.anchor == XDG_POSITIONER_ANCHOR_NONE);
+ assert(record.gravity == XDG_POSITIONER_GRAVITY_RIGHT);
+ assert(record.constraints == spec.constraints);
+
+ spec.anchor_edges = XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT + 1;
+ assert(!popup_spec_valid(&parent, &spec));
+ spec.anchor_edges = XDG_POSITIONER_ANCHOR_NONE;
+ spec.constraints = 1u << 9;
+ assert(!popup_spec_valid(&parent, &spec));
+ return 0;
+}
diff --git a/tests/wayland/test_relative_pointer.c b/tests/wayland/test_relative_pointer.c
index bcc41e3..d44238d 100644
--- a/tests/wayland/test_relative_pointer.c
+++ b/tests/wayland/test_relative_pointer.c
@@ -65,10 +65,10 @@ static void shape_device_destroy(struct wp_cursor_shape_device_v1 *device) { ass
#define wl_pointer_release pointer_release
#include "../../src/platform/wayland/pointer.c"
-static struct yasl_event next_event(struct wayland_surface *w)
+static struct yasl_private_event next_event(struct wayland_surface *w)
{
- struct yasl_event event;
- assert(yasl_surface_poll_event(&w->base, &event));
+ struct yasl_private_event event;
+ assert(yasl_private_surface_poll_event(&w->base, &event));
return event;
}
@@ -85,9 +85,9 @@ static void relative_move(struct seat *s, double dx, double dy)
int main(void)
{
- struct wayland_display display = {.base.ops = &yasl_wayland_backend};
+ struct wayland_display display = {.base.ops = &yasl_private_wayland_backend};
struct wayland_surface surface = {.base.display = &display.base, .conn = &display,
- .scale_factor = 2, .cursor = YASL_CURSOR_TEXT};
+ .scale_factor = 2, .cursor = YASL_PRIVATE_CURSOR_TEXT};
surface.surface = (struct wl_surface *)&surface;
wl_surface_set_user_data(surface.surface, &surface);
display.surfaces = &surface;
@@ -95,10 +95,10 @@ int main(void)
seat.pointer = (struct wl_pointer *)&seat;
seat.cursor_shape_dev = (struct wp_cursor_shape_device_v1 *)&seat;
display.seats = &seat;
- struct yasl_event event;
+ struct yasl_private_event event;
/* Without both protocols the surface stays in absolute mode. */
- assert(!yasl_surface_set_pointer_mode(&surface.base, YASL_POINTER_RELATIVE));
+ assert(!yasl_private_surface_set_pointer_mode(&surface.base, YASL_PRIVATE_POINTER_RELATIVE));
assert(!surface.relative && !locks);
display.relative_pointer_mgr = (struct zwp_relative_pointer_manager_v1 *)&display;
display.pointer_constraints = (struct zwp_pointer_constraints_v1 *)&display;
@@ -107,10 +107,10 @@ int main(void)
pointer_frame(&seat, seat.pointer);
assert(pointer_shapes == 1);
event = next_event(&surface);
- assert(event.type == YASL_EVENT_POINTER_MOTION && event.pointer.pos.x == 10);
+ assert(event.type == YASL_PRIVATE_EVENT_POINTER_MOTION && event.pointer.pos.x == 10);
/* Relative mode locks persistently on the pointer's seat and hides the cursor. */
- assert(yasl_surface_set_pointer_mode(&surface.base, YASL_POINTER_RELATIVE));
+ assert(yasl_private_surface_set_pointer_mode(&surface.base, YASL_PRIVATE_POINTER_RELATIVE));
assert(locks == 1 && locked_surface == surface.surface && locked_wl_pointer == seat.pointer);
assert(lock_lifetime == ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
assert(pointer_hides == 1 && surface.lock_seat == &seat);
@@ -121,14 +121,14 @@ int main(void)
event = next_event(&surface);
assert(event.pointer.pos.x == 14 && event.pointer.pos.y == 10);
assert(event.pointer.delta.x == 4 && event.pointer.delta.y == 0);
- assert(!yasl_surface_poll_event(&surface.base, &event));
+ assert(!yasl_private_surface_poll_event(&surface.base, &event));
/* Locked: relative samples in device pixels, position frozen at the lock. */
pointer_locked(&surface, surface.pointer_lock);
relative_move(&seat, 3.5, -2);
relative_move(&seat, 1000, 0);
event = next_event(&surface);
- assert(event.type == YASL_EVENT_POINTER_MOTION);
+ assert(event.type == YASL_PRIVATE_EVENT_POINTER_MOTION);
assert(event.pointer.pos.x == 14 && event.pointer.pos.y == 10);
assert(event.pointer.delta.x == 7 && event.pointer.delta.y == -4);
event = next_event(&surface);
@@ -137,7 +137,7 @@ int main(void)
/* Deactivation (e.g. focus loss) returns to absolute motion without unlocking. */
pointer_unlocked(&surface, surface.pointer_lock);
relative_move(&seat, 5, 5);
- assert(!yasl_surface_poll_event(&surface.base, &event));
+ assert(!yasl_private_surface_poll_event(&surface.base, &event));
move_to(&seat, 8, 5);
event = next_event(&surface);
assert(event.pointer.delta.x == 2 && event.pointer.delta.y == 0);
@@ -145,12 +145,12 @@ int main(void)
pointer_locked(&surface, surface.pointer_lock);
/* Returning to normal destroys the lock and restores the chosen cursor. */
- assert(yasl_surface_set_pointer_mode(&surface.base, YASL_POINTER_NORMAL));
+ assert(yasl_private_surface_set_pointer_mode(&surface.base, YASL_PRIVATE_POINTER_NORMAL));
assert(unlocks == 1 && !surface.pointer_lock && !surface.input.relative);
assert(pointer_shapes == 2);
- assert(yasl_surface_set_pointer_mode(&surface.base, YASL_POINTER_NORMAL));
+ assert(yasl_private_surface_set_pointer_mode(&surface.base, YASL_PRIVATE_POINTER_NORMAL));
assert(unlocks == 1);
- assert(yasl_surface_set_pointer_mode(&surface.base, YASL_POINTER_RELATIVE));
+ assert(yasl_private_surface_set_pointer_mode(&surface.base, YASL_PRIVATE_POINTER_RELATIVE));
assert(locks == 2 && pointer_hides == 2);
pointer_locked(&surface, surface.pointer_lock);
diff --git a/tests/wayland/test_state.c b/tests/wayland/test_state.c
index 2da26e5..1ce9189 100644
--- a/tests/wayland/test_state.c
+++ b/tests/wayland/test_state.c
@@ -1,6 +1,20 @@
-#include "../../src/platform/wayland/shell.c"
+#include "platform/wayland/internal.h"
#include <assert.h>
+static uint32_t acknowledged_serial;
+static unsigned acknowledge_count;
+static void test_ack_configure(struct xdg_surface *surface, uint32_t serial);
+#define xdg_surface_ack_configure test_ack_configure
+#include "../../src/platform/wayland/shell.c"
+#undef xdg_surface_ack_configure
+
+static void test_ack_configure(struct xdg_surface *surface, uint32_t serial)
+{
+ (void)surface;
+ acknowledged_serial = serial;
+ acknowledge_count++;
+}
+
struct notifications {
int count;
uint32_t state;
@@ -9,7 +23,7 @@ struct notifications {
static void state_changed(struct yasl_surface *surface, uint32_t state, void *ud)
{
struct notifications *notifications = ud;
- assert(yasl_surface_state(surface) == state);
+ assert(yasl_private_surface_state(surface) == state);
notifications->count++;
notifications->state = state;
}
@@ -24,33 +38,33 @@ static void configure(struct wayland_surface *surface, uint32_t *states, size_t
int main(void)
{
struct notifications notifications = {0};
- struct wayland_surface surface = {.base.state = YASL_STATE_CSD};
- wayland_surface_set_callbacks(&surface, &(struct yasl_window_cbs){
+ struct wayland_surface surface = {.base.state = YASL_PRIVATE_STATE_CSD};
+ wayland_surface_set_callbacks(&surface, &(struct yasl_private_window_cbs){
.state = state_changed}, &notifications);
- assert(yasl_surface_state(&surface.base) == YASL_STATE_CSD);
+ assert(yasl_private_surface_state(&surface.base) == YASL_PRIVATE_STATE_CSD);
uint32_t fullscreen[] = {XDG_TOPLEVEL_STATE_FULLSCREEN,
XDG_TOPLEVEL_STATE_ACTIVATED, XDG_TOPLEVEL_STATE_FULLSCREEN, 31, UINT32_MAX};
configure(&surface, fullscreen, sizeof(fullscreen) / sizeof(*fullscreen));
assert(notifications.count == 1);
- assert(notifications.state == (YASL_STATE_CSD | YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED));
+ assert(notifications.state == (YASL_PRIVATE_STATE_CSD | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_ACTIVATED));
configure(&surface, fullscreen, sizeof(fullscreen) / sizeof(*fullscreen));
assert(notifications.count == 1);
/* Decoration changes preserve the other flags and do not notify twice. */
toplevel_decoration_configure(&surface, NULL, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
assert(notifications.count == 2);
- assert(notifications.state == (YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED));
+ assert(notifications.state == (YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_ACTIVATED));
toplevel_decoration_configure(&surface, NULL, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
assert(notifications.count == 2);
uint32_t maximized[] = {XDG_TOPLEVEL_STATE_MAXIMIZED, XDG_TOPLEVEL_STATE_SUSPENDED};
configure(&surface, maximized, sizeof(maximized) / sizeof(*maximized));
assert(notifications.count == 3);
- assert(notifications.state == (YASL_STATE_MAXIMIZED | YASL_STATE_SUSPENDED));
+ assert(notifications.state == (YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_SUSPENDED));
toplevel_decoration_configure(&surface, NULL, ZXDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE);
assert(notifications.count == 4);
- assert(notifications.state == (YASL_STATE_CSD | YASL_STATE_MAXIMIZED | YASL_STATE_SUSPENDED));
+ assert(notifications.state == (YASL_PRIVATE_STATE_CSD | YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_SUSPENDED));
/* Each tiled edge maps to the same portable flag; changing edges alone
* leaves the reported state unchanged. */
@@ -59,17 +73,38 @@ int main(void)
for (size_t i = 0; i < sizeof(edges) / sizeof(*edges); i++) {
configure(&surface, &edges[i], 1);
assert(notifications.count == 5);
- assert(notifications.state == (YASL_STATE_CSD | YASL_STATE_TILED));
+ assert(notifications.state == (YASL_PRIVATE_STATE_CSD | YASL_PRIVATE_STATE_TILED));
}
configure(&surface, edges, 0);
assert(notifications.count == 6);
- assert(notifications.state == YASL_STATE_CSD);
+ assert(notifications.state == YASL_PRIVATE_STATE_CSD);
/* State still advances when applications have not installed callbacks. */
wayland_surface_set_callbacks(&surface, NULL, NULL);
configure(&surface, fullscreen, sizeof(fullscreen) / sizeof(*fullscreen));
- assert(yasl_surface_state(&surface.base) ==
- (YASL_STATE_CSD | YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED));
+ assert(yasl_private_surface_state(&surface.base) ==
+ (YASL_PRIVATE_STATE_CSD | YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_ACTIVATED));
assert(notifications.count == 6);
+
+ /* External presentation has no SHM commit callback to acknowledge xdg
+ * configures. Apply geometry and acknowledge before renderer submission. */
+ struct wayland_surface gpu = {
+ .base.presenter = YASL_PRIVATE_PRESENTER_VULKAN,
+ .width = 0, .height = 0,
+ .logical_width = 64, .logical_height = 64,
+ .req_width = 64, .req_height = 64,
+ .scale_120 = YASL_PRIVATE_SCALE_UNIT,
+ .pending_scale_120 = 180,
+ };
+ acknowledge_count = 0;
+ xdg_surface_configure(&gpu, (struct xdg_surface *)&gpu, 1234);
+ assert(acknowledge_count == 1 && acknowledged_serial == 1234);
+ assert(!gpu.has_pending_serial && !gpu.size_pending);
+ assert(gpu.configured && gpu.width == 96 && gpu.height == 96);
+
+ /* CPU presentation keeps its existing deferred ack with the SHM commit. */
+ struct wayland_surface cpu = {.base.presenter = YASL_PRIVATE_PRESENTER_CPU};
+ xdg_surface_configure(&cpu, (struct xdg_surface *)&cpu, 5678);
+ assert(acknowledge_count == 1 && cpu.has_pending_serial && cpu.want_draw);
return 0;
}
diff --git a/tests/wayland/test_tablet.c b/tests/wayland/test_tablet.c
index c76ddf8..f47fbfa 100644
--- a/tests/wayland/test_tablet.c
+++ b/tests/wayland/test_tablet.c
@@ -1,63 +1,101 @@
#include "../../src/platform/wayland/pointer.c"
#include <assert.h>
-static struct yasl_event next(struct wayland_surface *surface)
+static struct yasl_private_event next_raw(struct wayland_surface *surface)
{
- struct yasl_event event;
- assert(yasl_surface_poll_event(&surface->base, &event));
+ struct yasl_private_event event;
+ assert(yasl_private_surface_poll_event(&surface->base, &event));
assert(event.surface == &surface->base);
return event;
}
+static struct yasl_private_event next_pointer(struct wayland_surface *surface)
+{
+ struct yasl_private_event event;
+ do { event = next_raw(surface); } while (event.type == YASL_PRIVATE_EVENT_PEN);
+ return event;
+}
+
int main(void)
{
- struct wayland_display display = {.base.ops = &yasl_wayland_backend};
+ struct wayland_display display = {.base.ops = &yasl_private_wayland_backend};
struct wayland_surface surface = {.base.display = &display.base, .conn = &display,
.scale_factor = 1.5};
struct seat seat = {.conn = &display, .tablet_focus = &surface};
- struct tablet_tool tool = {.seat = &seat};
+ struct tablet_tool tool = {.seat = &seat, .device_id = 77, .tool_id = 88, .kind = 1};
seat.tool = &tool;
tablet_tool_motion(&tool, NULL, wl_fixed_from_int(100), wl_fixed_from_int(120));
tool_pressure(&tool, NULL, 32768);
tool_tilt(&tool, NULL, wl_fixed_from_int(-35), wl_fixed_from_int(42));
+ tool_distance(&tool, NULL, 16384);
+ tool_rotation(&tool, NULL, wl_fixed_from_int(17));
+ tool_slider(&tool, NULL, -32768);
+ pen_action_push(&tool, YASL_PRIVATE_PEN_ENTER, 0);
tablet_tool_down(&tool, NULL, 1);
tool_frame(&tool, NULL, 0);
- struct yasl_event ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_MOTION && ev.pointer.device == YASL_PTR_DEV_PEN);
+ struct yasl_private_event pen = next_raw(&surface);
+ assert(pen.type == YASL_PRIVATE_EVENT_PEN);
+ assert(pen.pen.action == YASL_PRIVATE_PEN_ENTER && !pen.pen.in_contact);
+ assert(pen.pen.device_id == 77 && pen.pen.tool_id == 88);
+ pen = next_raw(&surface);
+ assert(pen.type == YASL_PRIVATE_EVENT_PEN);
+ assert(pen.pen.action == YASL_PRIVATE_PEN_DOWN && pen.pen.in_contact);
+ assert(pen.pen.buttons & YASL_PRIVATE_BUTTON_BIT(YASL_PRIVATE_BUTTON_PRIMARY));
+ assert(pen.pen.x == 150 && pen.pen.y == 180);
+ assert(fabs(pen.pen.pressure - (32768.0 / 65535.0)) < 0.00001);
+ assert(pen.pen.axes_valid == (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT |
+ YASL_PRIVATE_PEN_DISTANCE | YASL_PRIVATE_PEN_ROTATION | YASL_PRIVATE_PEN_TANGENTIAL));
+ assert(pen.pen.axes_changed == pen.pen.axes_valid);
+ assert(fabs(pen.pen.distance - 16384.0 / 65535.0) < 0.00001);
+ assert(pen.pen.rotation == 17 && pen.pen.tangential == -32768.0 / 65535.0);
+ struct yasl_private_event ev = next_pointer(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_MOTION && ev.pointer.device == YASL_PRIVATE_PTR_DEV_PEN);
assert(ev.pointer.pos.x == 150 && ev.pointer.pos.y == 180 && ev.pointer.pen.pressure == 32768);
assert(ev.pointer.pen.tilt_x == -35 && ev.pointer.pen.tilt_y == 42);
- ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_DOWN && ev.pointer.button == YASL_BUTTON_PRIMARY && ev.pointer.pen.pressure == 32768);
+ ev = next_pointer(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_DOWN && ev.pointer.button == YASL_PRIVATE_BUTTON_PRIMARY && ev.pointer.pen.pressure == 32768);
/* Stationary pressure and tilt changes are still motion samples. */
tool_pressure(&tool, NULL, 100000);
tool_tilt(&tool, NULL, wl_fixed_from_int(-120), wl_fixed_from_int(120));
tool_frame(&tool, NULL, 0);
- ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_MOTION && ev.pointer.pen.pressure == 65535);
+ pen = next_raw(&surface);
+ assert(pen.type == YASL_PRIVATE_EVENT_PEN && pen.pen.action == YASL_PRIVATE_PEN_MOVE);
+ assert(pen.pen.axes_changed == (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT));
+ ev = next_pointer(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_MOTION && ev.pointer.pen.pressure == 65535);
assert(ev.pointer.pos.x == 150 && ev.pointer.pos.y == 180);
assert(ev.pointer.pen.tilt_x == -90 && ev.pointer.pen.tilt_y == 90);
tablet_tool_up(&tool, NULL);
tool_frame(&tool, NULL, 0);
- ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_UP && ev.pointer.button == YASL_BUTTON_PRIMARY && !ev.pointer.pen.pressure);
+ pen = next_raw(&surface);
+ assert(pen.type == YASL_PRIVATE_EVENT_PEN && pen.pen.action == YASL_PRIVATE_PEN_UP);
+ assert(!pen.pen.in_contact && pen.pen.pressure == 0);
+ ev = next_pointer(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_UP && ev.pointer.button == YASL_PRIVATE_BUTTON_PRIMARY && !ev.pointer.pen.pressure);
tool_type(&tool, NULL, ZWP_TABLET_TOOL_V2_TYPE_ERASER);
tool_pressure(&tool, NULL, 22000);
tool_frame(&tool, NULL, 0);
- assert(next(&surface).pointer.device == YASL_PTR_DEV_ERASER);
+ pen = next_raw(&surface);
+ assert(pen.type == YASL_PRIVATE_EVENT_PEN && pen.pen.tool == 2);
+ assert(next_pointer(&surface).pointer.device == YASL_PRIVATE_PTR_DEV_ERASER);
tool_wheel(&tool, NULL, 0, 1);
tool_frame(&tool, NULL, 0);
- ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_SCROLL && ev.pointer.device == YASL_PTR_DEV_ERASER);
+ pen = next_raw(&surface);
+ assert(pen.type == YASL_PRIVATE_EVENT_PEN && pen.pen.axes_changed == YASL_PRIVATE_PEN_WHEEL);
+ ev = next_pointer(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_SCROLL && ev.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER);
assert(ev.pointer.scroll.x == 0 && ev.pointer.scroll.y == 1);
tablet_tool_proximity_out(&tool, NULL);
- ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_LEAVE && ev.pointer.device == YASL_PTR_DEV_ERASER && !ev.pointer.pen.pressure);
+ pen = next_raw(&surface);
+ assert(pen.type == YASL_PRIVATE_EVENT_PEN && pen.pen.action == YASL_PRIVATE_PEN_LEAVE);
+ ev = next_pointer(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_LEAVE && ev.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER && !ev.pointer.pen.pressure);
assert(!seat.tool && !seat.tablet_focus && !seat.tool_buttons);
- assert(!(yasl_display_caps(&display.base) & YASL_CAP_TABLET));
+ assert(!(yasl_private_display_caps(&display.base) & YASL_PRIVATE_CAP_TABLET));
display.tablet_mgr = (void *)&display; /* Capability depends on protocol availability. */
- assert(yasl_display_caps(&display.base) & YASL_CAP_TABLET);
+ assert(yasl_private_display_caps(&display.base) & YASL_PRIVATE_CAP_TABLET);
seat.pointer_focus = &surface;
/* A compositor may describe the same detent in three ways. value120 wins,
@@ -67,13 +105,32 @@ int main(void)
pointer_axis_value120(&seat, NULL, WL_POINTER_AXIS_VERTICAL_SCROLL, 30);
pointer_axis_value120(&seat, NULL, WL_POINTER_AXIS_HORIZONTAL_SCROLL, -60);
pointer_frame(&seat, NULL);
- ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_SCROLL && ev.pointer.device == YASL_PTR_DEV_MOUSE);
+ ev = next_pointer(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_SCROLL && ev.pointer.device == YASL_PRIVATE_PTR_DEV_MOUSE);
assert(ev.pointer.scroll.x == -0.5 && ev.pointer.scroll.y == 0.25);
- struct yasl_event unused;
- assert(!yasl_surface_poll_event(&surface.base, &unused));
+ assert(ev.pointer.scroll_valid == 1);
+ assert(ev.pointer.scroll_source == 1 && ev.pointer.scroll_phase == 0);
+ struct yasl_private_event unused;
+ assert(!yasl_private_surface_poll_event(&surface.base, &unused));
+ pointer_frame(&seat, NULL);
+ assert(!yasl_private_surface_poll_event(&surface.base, &unused));
+
+ /* Finger scrolling carries pixel offsets and coherent gesture phases. */
+ pointer_axis_source(&seat, NULL, WL_POINTER_AXIS_SOURCE_FINGER);
+ pointer_axis(&seat, NULL, 0, WL_POINTER_AXIS_VERTICAL_SCROLL, wl_fixed_from_double(1.25));
+ pointer_axis(&seat, NULL, 0, WL_POINTER_AXIS_HORIZONTAL_SCROLL, wl_fixed_from_double(-2.5));
+ pointer_frame(&seat, NULL);
+ ev = next_pointer(&surface);
+ assert(ev.pointer.scroll_phase == 1 && ev.pointer.scroll_source == 2);
+ assert(ev.pointer.pixel_scroll.x == -3.75 && ev.pointer.pixel_scroll.y == 1.875);
+ pointer_axis(&seat, NULL, 0, WL_POINTER_AXIS_VERTICAL_SCROLL, wl_fixed_from_double(1.25));
+ pointer_frame(&seat, NULL);
+ ev = next_pointer(&surface);
+ assert(ev.pointer.scroll_phase == 2 && ev.pointer.scroll.y == 0.125);
+ pointer_axis_stop(&seat, NULL, 0, WL_POINTER_AXIS_VERTICAL_SCROLL);
pointer_frame(&seat, NULL);
- assert(!yasl_surface_poll_event(&surface.base, &unused));
+ ev = next_pointer(&surface);
+ assert(ev.pointer.scroll_phase == 3 && ev.pointer.scroll.x == 0 && ev.pointer.scroll.y == 0);
/* Stops preserve offsets from the same frame. Repeated tiny movements
* must be visible immediately rather than held until a whole detent. */
@@ -83,17 +140,19 @@ int main(void)
pointer_axis(&seat, NULL, 0, WL_POINTER_AXIS_HORIZONTAL_SCROLL, wl_fixed_from_double(-2.5));
pointer_axis_stop(&seat, NULL, 0, WL_POINTER_AXIS_VERTICAL_SCROLL);
pointer_frame(&seat, NULL);
- ev = next(&surface);
+ ev = next_pointer(&surface);
assert(ev.pointer.scroll.x == -0.25 && ev.pointer.scroll.y == 0.125);
+ assert(ev.pointer.scroll_source == 2 && ev.pointer.scroll_phase == 3);
+ assert(ev.pointer.pixel_scroll.x == -3.75 && ev.pointer.pixel_scroll.y == 1.875);
}
pointer_axis_discrete(&seat, NULL, WL_POINTER_AXIS_HORIZONTAL_SCROLL, 2);
pointer_frame(&seat, NULL);
- ev = next(&surface);
+ ev = next_pointer(&surface);
assert(ev.pointer.scroll.x == 2 && ev.pointer.scroll.y == 0);
pointer_axis_value120(&seat, NULL, WL_POINTER_AXIS_VERTICAL_SCROLL, 60);
seat_pointer_reset(&seat);
pointer_frame(&seat, NULL);
- assert(!yasl_surface_poll_event(&surface.base, &unused));
+ assert(!yasl_private_surface_poll_event(&surface.base, &unused));
input_queue_clear(&surface.input);
return 0;
}
diff --git a/tests/x11/inputtest-protocol.h b/tests/x11/inputtest-protocol.h
index 18f62ba..2a94a55 100644
--- a/tests/x11/inputtest-protocol.h
+++ b/tests/x11/inputtest-protocol.h
@@ -22,8 +22,8 @@
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef YASL_TEST_INPUTTEST_PROTOCOL_H
-#define YASL_TEST_INPUTTEST_PROTOCOL_H
+#ifndef YASL_PRIVATE_TEST_INPUTTEST_PROTOCOL_H
+#define YASL_PRIVATE_TEST_INPUTTEST_PROTOCOL_H
#include <stdint.h>
diff --git a/tests/x11/test_input.c b/tests/x11/test_input.c
index c2eca37..ccad859 100644
--- a/tests/x11/test_input.c
+++ b/tests/x11/test_input.c
@@ -8,10 +8,10 @@ static void check_selection_encoding(struct x11_display *d, struct x11_surface *
{
Window requestor = XCreateSimpleWindow(d->native, DefaultRootWindow(d->native),
0, 0, 1, 1, 0, 0, 0);
- Atom property = XInternAtom(d->native, "YASL_TEST_STRING", False);
+ Atom property = XInternAtom(d->native, "YASL_PRIVATE_TEST_STRING", False);
XSelectionRequestEvent request = {.display = d->native, .requestor = requestor,
.selection = d->clipboard, .target = XA_STRING, .property = property};
- yasl_display_copy(&d->base, YASL_CLIPBOARD, "café £");
+ yasl_private_display_copy(&d->base, YASL_PRIVATE_CLIPBOARD, "café £");
selection_request(d, &request);
Atom actual;
int format;
@@ -22,7 +22,7 @@ static void check_selection_encoding(struct x11_display *d, struct x11_surface *
assert(actual == XA_STRING && format == 8 && count == 6);
assert(memcmp(data, "caf\xe9 \xa3", count) == 0);
XFree(data);
- yasl_display_copy(&d->base, YASL_CLIPBOARD, "字");
+ yasl_private_display_copy(&d->base, YASL_PRIVATE_CLIPBOARD, "字");
selection_request(d, &request);
assert(XGetWindowProperty(d->native, requestor, property, 0, 100, True,
AnyPropertyType, &actual, &format, &count, &left, &data) == Success);
@@ -44,12 +44,12 @@ static void check_selection_encoding(struct x11_display *d, struct x11_surface *
size_t len;
int format;
const char *expected;
- enum yasl_transfer_status status;
+ enum yasl_private_transfer_status status;
} cases[] = {
- {XA_STRING, "caf\xe9 \xa3", 6, 8, "café £", YASL_TRANSFER_OK},
- {d->utf8, "", 0, 8, "", YASL_TRANSFER_OK},
- {d->utf8, "\xff", 1, 8, NULL, YASL_TRANSFER_FAILED},
- {d->incr, NULL, 1, 32, NULL, YASL_TRANSFER_FAILED},
+ {XA_STRING, "caf\xe9 \xa3", 6, 8, "café £", YASL_PRIVATE_TRANSFER_OK},
+ {d->utf8, "", 0, 8, "", YASL_PRIVATE_TRANSFER_OK},
+ {d->utf8, "\xff", 1, 8, NULL, YASL_PRIVATE_TRANSFER_FAILED},
+ {d->incr, NULL, 1, 32, NULL, YASL_PRIVATE_TRANSFER_FAILED},
};
for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
struct paste_request *pending = calloc(1, sizeof(*pending));
@@ -59,7 +59,7 @@ static void check_selection_encoding(struct x11_display *d, struct x11_surface *
0, 0, 1, 1, 0, 0, 0);
pending->target = s;
pending->id = (uint64_t)i + 1;
- pending->timeout = yasl_loop_add_timer(d->loop, PASTE_TIMEOUT_NS,
+ pending->timeout = yasl_private_loop_add_timer(d->loop, PASTE_TIMEOUT_NS,
paste_timeout, pending);
assert(pending->timeout);
pending->next = d->pastes;
@@ -71,21 +71,21 @@ static void check_selection_encoding(struct x11_display *d, struct x11_surface *
cases[i].format, PropModeReplace, bytes, (int)cases[i].len);
XSelectionEvent reply = {.requestor = pending->window, .property = property};
selection_notify(d, &reply);
- struct yasl_event event;
- assert(yasl_surface_poll_event(&s->base, &event));
- assert(event.type == YASL_EVENT_PASTE && event.paste.request_id == i + 1);
+ struct yasl_private_event event;
+ assert(yasl_private_surface_poll_event(&s->base, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_PASTE && event.paste.request_id == i + 1);
assert(event.paste.status == cases[i].status);
- if (cases[i].status == YASL_TRANSFER_OK) {
+ if (cases[i].status == YASL_PRIVATE_TRANSFER_OK) {
assert(event.text.len == strlen(cases[i].expected));
assert(strcmp(event.text.data, cases[i].expected) == 0);
}
if (cases[i].type == d->incr) {
assert(d->pastes == pending && !pending->target);
- assert(yasl_loop_del_timer(d->loop, pending->timeout));
+ assert(yasl_private_loop_del_timer(d->loop, pending->timeout));
pending->timeout = 0;
paste_timeout(pending, 0, PASTE_TIMEOUT_NS);
}
- assert(!yasl_surface_poll_event(&s->base, &event));
+ assert(!yasl_private_surface_poll_event(&s->base, &event));
}
struct paste_request *rejected = calloc(1, sizeof(*rejected));
@@ -99,10 +99,10 @@ static void check_selection_encoding(struct x11_display *d, struct x11_surface *
d->pastes = rejected;
XSelectionEvent no_property = {.requestor = rejected->window, .property = None};
selection_notify(d, &no_property);
- struct yasl_event event;
- assert(yasl_surface_poll_event(&s->base, &event));
- assert(event.type == YASL_EVENT_PASTE && event.paste.request_id == 99);
- assert(event.paste.status == YASL_TRANSFER_FAILED);
+ struct yasl_private_event event;
+ assert(yasl_private_surface_poll_event(&s->base, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_PASTE && event.paste.request_id == 99);
+ assert(event.paste.status == YASL_PRIVATE_TRANSFER_FAILED);
}
/* Own the XSETTINGS selection with one Net/DoubleClickTime value. */
@@ -111,7 +111,7 @@ static void check_double_click(struct x11_display *d)
/* No settings owner and no portal: the default applies. */
unsetenv("DBUS_SESSION_BUS_ADDRESS");
unsetenv("XDG_RUNTIME_DIR");
- assert(yasl_display_double_click_ms(&d->base) == YASL_DOUBLE_CLICK_MS_DEFAULT);
+ assert(yasl_private_display_double_click_ms(&d->base) == YASL_PRIVATE_DOUBLE_CLICK_MS_DEFAULT);
char name[32];
snprintf(name, sizeof(name), "_XSETTINGS_S%d", d->screen);
@@ -129,64 +129,64 @@ static void check_double_click(struct x11_display *d)
XChangeProperty(d->native, owner, property, property, 8, PropModeReplace,
blob, sizeof(blob));
XSync(d->native, False);
- assert(yasl_display_double_click_ms(&d->base) == 250);
+ assert(yasl_private_display_double_click_ms(&d->base) == 250);
XDestroyWindow(d->native, owner);
XSync(d->native, False);
- assert(yasl_display_double_click_ms(&d->base) == YASL_DOUBLE_CLICK_MS_DEFAULT);
+ assert(yasl_private_display_double_click_ms(&d->base) == YASL_PRIVATE_DOUBLE_CLICK_MS_DEFAULT);
}
-static struct yasl_event wait_paste(struct yasl_display *display, uint64_t id)
+static struct yasl_private_event wait_paste(struct yasl_private_display *display, uint64_t id)
{
- struct yasl_event event;
- for (int i = 0; i < 256 && yasl_display_poll_event(display, &event); i++)
- if (event.type == YASL_EVENT_PASTE && event.paste.request_id == id)
+ struct yasl_private_event event;
+ for (int i = 0; i < 256 && yasl_private_display_poll_event(display, &event); i++)
+ if (event.type == YASL_PRIVATE_EVENT_PASTE && event.paste.request_id == id)
return event;
assert(!"expected paste completion was not queued");
- return (struct yasl_event){0};
+ return (struct yasl_private_event){0};
}
static void check_request_completion(struct x11_display *d, struct x11_surface *s)
{
assert(surface_input_queue(&s->base) == &s->input);
- assert(copy_text(&d->base, YASL_CLIPBOARD, ""));
+ assert(copy_text(&d->base, YASL_PRIVATE_CLIPBOARD, ""));
XSync(d->native, False);
assert(XGetSelectionOwner(d->native, d->clipboard) == d->selection_window);
- assert(request_paste(&d->base, YASL_CLIPBOARD, &s->base, 101));
- struct yasl_event event = wait_paste(&d->base, 101);
- assert(event.paste.status == YASL_TRANSFER_OK && event.paste.len == 0);
+ assert(request_paste(&d->base, YASL_PRIVATE_CLIPBOARD, &s->base, 101));
+ struct yasl_private_event event = wait_paste(&d->base, 101);
+ assert(event.paste.status == YASL_PRIVATE_TRANSFER_OK && event.paste.len == 0);
assert(event.paste.data && !event.paste.data[0]);
XSetSelectionOwner(d->native, d->clipboard, None, CurrentTime);
XSync(d->native, False);
- assert(request_paste(&d->base, YASL_CLIPBOARD, &s->base, 102));
+ assert(request_paste(&d->base, YASL_PRIVATE_CLIPBOARD, &s->base, 102));
XSync(d->native, False);
dispatch(&d->base);
event = wait_paste(&d->base, 102);
- assert(event.paste.status == YASL_TRANSFER_UNAVAILABLE);
+ assert(event.paste.status == YASL_PRIVATE_TRANSFER_UNAVAILABLE);
Window silent_owner = XCreateSimpleWindow(d->native, DefaultRootWindow(d->native),
0, 0, 1, 1, 0, 0, 0);
XSetSelectionOwner(d->native, d->clipboard, silent_owner, CurrentTime);
XSync(d->native, False);
- assert(request_paste(&d->base, YASL_CLIPBOARD, &s->base, 103));
+ assert(request_paste(&d->base, YASL_PRIVATE_CLIPBOARD, &s->base, 103));
struct paste_request *timed = d->pastes;
assert(timed && timed->id == 103 && timed->timeout);
- assert(yasl_loop_del_timer(d->loop, timed->timeout));
+ assert(yasl_private_loop_del_timer(d->loop, timed->timeout));
timed->timeout = 0;
paste_timeout(timed, 0, PASTE_TIMEOUT_NS);
event = wait_paste(&d->base, 103);
- assert(event.paste.status == YASL_TRANSFER_FAILED);
+ assert(event.paste.status == YASL_PRIVATE_TRANSFER_FAILED);
- struct yasl_window_spec spec = {.title = "Cancelled paste", .size = {80, 60}};
- struct yasl_surface *cancel_target = yasl_surface_create_toplevel(&d->base, &spec);
+ struct yasl_private_window_spec spec = {.title = "Cancelled paste", .size = {80, 60}};
+ struct yasl_surface *cancel_target = yasl_private_surface_create_toplevel(&d->base, &spec);
assert(cancel_target);
- assert(request_paste(&d->base, YASL_CLIPBOARD, cancel_target, 104));
+ assert(request_paste(&d->base, YASL_PRIVATE_CLIPBOARD, cancel_target, 104));
assert(d->pastes && d->pastes->target == (struct x11_surface *)cancel_target);
- yasl_timer_id timeout = d->pastes->timeout;
- yasl_surface_destroy(cancel_target);
+ yasl_private_timer_id timeout = d->pastes->timeout;
+ yasl_private_surface_destroy(cancel_target);
assert(d->pastes && !d->pastes->target && d->pastes->id == 0);
- assert(yasl_loop_del_timer(d->loop, timeout));
+ assert(yasl_private_loop_del_timer(d->loop, timeout));
d->pastes->timeout = 0;
XSelectionEvent late_reply = {
.requestor = d->pastes->window,
@@ -200,23 +200,23 @@ static void check_request_completion(struct x11_display *d, struct x11_surface *
XSync(d->native, False);
}
-static struct yasl_event next_composition(struct yasl_display *display)
+static struct yasl_private_event next_composition(struct yasl_private_display *display)
{
- struct yasl_event event;
- for (int i = 0; i < 256 && yasl_display_poll_event(display, &event); i++)
- if (event.type == YASL_EVENT_COMPOSITION)
+ struct yasl_private_event event;
+ for (int i = 0; i < 256 && yasl_private_display_poll_event(display, &event); i++)
+ if (event.type == YASL_PRIVATE_EVENT_COMPOSITION)
return event;
assert(!"expected composition snapshot was not queued");
- return (struct yasl_event){0};
+ return (struct yasl_private_event){0};
}
-static void drain_display_events(struct yasl_display *display)
+static void drain_display_events(struct yasl_private_display *display)
{
- struct yasl_event event;
- while (yasl_display_poll_event(display, &event)) {}
+ struct yasl_private_event event;
+ while (yasl_private_display_poll_event(display, &event)) {}
}
-static void check_xim_callbacks(struct yasl_display *display, struct x11_surface *s)
+static void check_xim_callbacks(struct yasl_private_display *display, struct x11_surface *s)
{
const char *old_locale = setlocale(LC_CTYPE, NULL);
char *saved_locale = old_locale ? strdup(old_locale) : NULL;
@@ -235,7 +235,7 @@ static void check_xim_callbacks(struct yasl_display *display, struct x11_surface
};
xim_preedit_draw(NULL, (XPointer)s, (XPointer)&draw);
assert(s->ime_preedit && strcmp(s->ime_preedit, "A😀B") == 0);
- struct yasl_event event = next_composition(display);
+ struct yasl_private_event event = next_composition(display);
assert(event.composition.active && event.composition.len == 6);
assert(event.composition.begin == 5 && event.composition.end == 5);
@@ -271,8 +271,8 @@ static void check_xim_callbacks(struct yasl_display *display, struct x11_surface
draw.chg_length = 0;
xim_preedit_draw(NULL, (XPointer)s, (XPointer)&draw);
assert(strcmp(s->ime_preedit, "AéZB") == 0);
- struct yasl_event unused;
- assert(!yasl_display_poll_event(display, &unused));
+ struct yasl_private_event unused;
+ assert(!yasl_private_display_poll_event(display, &unused));
wchar_t empty[] = {0};
XIMText deletion = {.length = 0, .encoding_is_wchar = True,
@@ -305,12 +305,12 @@ static void check_xim_callbacks(struct yasl_display *display, struct x11_surface
int main(void)
{
- struct yasl_display_spec ds = {.backend = YASL_BACKEND_X11};
- struct yasl_display *base = yasl_display_open(NULL, &ds);
+ struct yasl_private_display_spec ds = {.backend = YASL_PRIVATE_BACKEND_X11};
+ struct yasl_private_display *base = yasl_private_display_open(NULL, &ds);
assert(base);
struct x11_display *d = (struct x11_display *)base;
- struct yasl_window_spec spec = {.title = "Keyboard fallback", .size = {100, 100}};
- struct yasl_surface *surface = yasl_surface_create_toplevel(base, &spec);
+ struct yasl_private_window_spec spec = {.title = "Keyboard fallback", .size = {100, 100}};
+ struct yasl_surface *surface = yasl_private_surface_create_toplevel(base, &spec);
assert(surface);
struct x11_surface *s = (struct x11_surface *)surface;
XIC saved_ic = s->ic;
@@ -335,22 +335,22 @@ int main(void)
input_queue_clear(&s->input);
/* Native creation and keymap updates may already have queued focus or
* reset input on the shared display stream. Isolate this synthetic key. */
- struct yasl_event event;
- while (yasl_surface_poll_event(surface, &event)) {}
+ struct yasl_private_event event;
+ while (yasl_private_surface_poll_event(surface, &event)) {}
XKeyEvent native = {.type = KeyPress, .display = d->native,
.window = s->window, .keycode = code};
key_event(s, &native, false);
- assert(yasl_surface_poll_event(surface, &event));
- assert(event.type == YASL_EVENT_KEY && event.key.key == YASL_KEY_A);
+ assert(yasl_private_surface_poll_event(surface, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_KEY && event.key.key == YASL_KEY_A);
assert(event.key.codepoint == cases[i].label);
- assert(yasl_surface_poll_event(surface, &event));
- assert(event.type == YASL_EVENT_TEXT && event.text.len == strlen(cases[i].text));
+ assert(yasl_private_surface_poll_event(surface, &event));
+ assert(event.type == YASL_PRIVATE_EVENT_TEXT && event.text.len == strlen(cases[i].text));
assert(strcmp(event.text.data, cases[i].text) == 0);
- assert(!yasl_surface_poll_event(surface, &event));
+ assert(!yasl_private_surface_poll_event(surface, &event));
native.type = KeyRelease;
key_event(s, &native, false);
- assert(yasl_surface_poll_event(surface, &event) && event.type == YASL_EVENT_KEY);
- assert(!yasl_surface_poll_event(surface, &event));
+ assert(yasl_private_surface_poll_event(surface, &event) && event.type == YASL_PRIVATE_EVENT_KEY);
+ assert(!yasl_private_surface_poll_event(surface, &event));
}
XChangeKeyboardMapping(d->native, code, width, saved, 1);
XSync(d->native, False);
@@ -363,14 +363,14 @@ int main(void)
check_xim_callbacks(base, s);
/* Test CSD interactive actions, window menu, and IME caret */
- assert(yasl_display_caps(base) & YASL_CAP_CSD);
- yasl_surface_set_ime_caret(surface, (struct yasl_rect2i){10, 20, 2, 16});
- assert(yasl_surface_ime_preedit(surface) == NULL);
+ assert(yasl_private_display_caps(base) & YASL_PRIVATE_CAP_CSD);
+ yasl_private_surface_set_ime_caret(surface, (struct yasl_private_rect2i){10, 20, 2, 16});
+ assert(yasl_private_surface_ime_preedit(surface) == NULL);
int begin = -1, end = -1;
- assert(!yasl_surface_ime_preedit_cursor(surface, &begin, &end));
- assert(yasl_surface_show_window_menu(surface, (struct yasl_vector2i){50, 50}));
+ assert(!yasl_private_surface_ime_preedit_cursor(surface, &begin, &end));
+ assert(yasl_private_surface_show_window_menu(surface, (struct yasl_private_vector2i){50, 50}));
- yasl_surface_destroy(surface);
- yasl_display_close(base);
+ yasl_private_surface_destroy(surface);
+ yasl_private_display_close(base);
return 0;
}
diff --git a/tests/x11/test_state.c b/tests/x11/test_state.c
index abc0851..4696e60 100644
--- a/tests/x11/test_state.c
+++ b/tests/x11/test_state.c
@@ -31,7 +31,7 @@ int state_property(Display *display, Window window, Atom property,
static void state_changed(struct yasl_surface *surface, uint32_t state, void *ud)
{
(void)ud;
- assert(yasl_surface_state(surface) == state);
+ assert(yasl_private_surface_state(surface) == state);
reported_state = state;
notifications++;
}
@@ -93,39 +93,39 @@ static void test_xsettings(void)
int main(void)
{
test_xsettings();
- struct x11_display display = {.base.ops = &yasl_x11_backend,
+ struct x11_display display = {.base.ops = &yasl_private_x11_backend,
.net_state = 10, .fullscreen = 20, .maximized_h = 21, .maximized_v = 22};
display.native = (Display *)&display;
struct x11_surface surface = {.base = {.display = &display.base,
- .state = YASL_STATE_ACTIVATED}, .window = 42};
- set_callbacks(&surface.base, &(struct yasl_window_cbs){.state = state_changed}, NULL);
+ .state = YASL_PRIVATE_STATE_ACTIVATED}, .window = 42};
+ set_callbacks(&surface.base, &(struct yasl_private_window_cbs){.state = state_changed}, NULL);
properties[0] = display.fullscreen;
property_count = 1;
update_state(&surface);
assert(notifications == 1);
- assert(reported_state == (YASL_STATE_FULLSCREEN | YASL_STATE_ACTIVATED));
+ assert(reported_state == (YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_ACTIVATED));
update_state(&surface);
assert(notifications == 1);
/* Both maximized axes are required; activation survives property changes. */
properties[0] = display.maximized_h;
update_state(&surface);
- assert(notifications == 2 && reported_state == YASL_STATE_ACTIVATED);
+ assert(notifications == 2 && reported_state == YASL_PRIVATE_STATE_ACTIVATED);
properties[1] = display.maximized_v;
property_count = 2;
update_state(&surface);
assert(notifications == 3);
- assert(reported_state == (YASL_STATE_MAXIMIZED | YASL_STATE_ACTIVATED));
+ assert(reported_state == (YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_ACTIVATED));
properties[2] = display.fullscreen;
property_count = 3;
update_state(&surface);
assert(notifications == 4);
- assert(reported_state == (YASL_STATE_FULLSCREEN | YASL_STATE_MAXIMIZED | YASL_STATE_ACTIVATED));
+ assert(reported_state == (YASL_PRIVATE_STATE_FULLSCREEN | YASL_PRIVATE_STATE_MAXIMIZED | YASL_PRIVATE_STATE_ACTIVATED));
property_count = 0;
update_state(&surface);
- assert(notifications == 5 && reported_state == YASL_STATE_ACTIVATED);
+ assert(notifications == 5 && reported_state == YASL_PRIVATE_STATE_ACTIVATED);
update_state(&surface);
assert(notifications == 5);
return 0;
diff --git a/tests/x11/test_tablet.c b/tests/x11/test_tablet.c
index 243ae00..be5da4c 100644
--- a/tests/x11/test_tablet.c
+++ b/tests/x11/test_tablet.c
@@ -1,10 +1,18 @@
/* Exercise native XI2 samples through the backend and public input queue. */
#include "../../src/platform/x11/window.c"
-static struct yasl_event next(struct x11_surface *surface)
+static struct yasl_private_event next(struct x11_surface *surface)
{
- struct yasl_event event;
- assert(yasl_surface_poll_event(&surface->base, &event));
+ struct yasl_private_event event;
+ /* This backend unit harness owns a standalone input queue, without a
+ * registered display event FIFO. Read rich private events directly. */
+ static unsigned count;
+ count++;
+ if (!input_queue_pop(&surface->input, &event)) {
+ fprintf(stderr, "missing private event at expectation %u (queued=%zu reset=%d)\n",
+ count, surface->input.count, surface->input.reset_pending);
+ abort();
+ }
assert(event.surface == &surface->base);
return event;
}
@@ -13,51 +21,51 @@ struct drag_test_recorder {
int over_count;
int leave_count;
int drop_count;
- struct yasl_vector2i last_pos;
+ struct yasl_private_vector2i last_pos;
};
-static void on_test_drag(struct yasl_surface *s, enum yasl_drag_event ev,
- struct yasl_vector2i pos, void *ud)
+static void on_test_drag(struct yasl_surface *s, enum yasl_private_drag_event ev,
+ struct yasl_private_vector2i pos, void *ud)
{
(void)s;
struct drag_test_recorder *rec = ud;
rec->last_pos = pos;
- if (ev == YASL_DRAG_OVER) rec->over_count++;
- else if (ev == YASL_DRAG_LEAVE) rec->leave_count++;
- else if (ev == YASL_DRAG_DROP) rec->drop_count++;
+ if (ev == YASL_PRIVATE_DRAG_OVER) rec->over_count++;
+ else if (ev == YASL_PRIVATE_DRAG_LEAVE) rec->leave_count++;
+ else if (ev == YASL_PRIVATE_DRAG_DROP) rec->drop_count++;
}
struct drop_test_recorder {
int over_count;
int leave_count;
- struct yasl_vector2i last_pos;
+ struct yasl_private_vector2i last_pos;
};
-static void on_test_drop(struct yasl_surface *s, enum yasl_drop_event ev,
- struct yasl_vector2i pos, void *ud)
+static void on_test_drop(struct yasl_surface *s, enum yasl_private_drop_event ev,
+ struct yasl_private_vector2i pos, void *ud)
{
(void)s;
struct drop_test_recorder *rec = ud;
rec->last_pos = pos;
- if (ev == YASL_DROP_OVER) rec->over_count++;
- else if (ev == YASL_DROP_LEAVE) rec->leave_count++;
+ if (ev == YASL_PRIVATE_DROP_OVER) rec->over_count++;
+ else if (ev == YASL_PRIVATE_DROP_LEAVE) rec->leave_count++;
}
-static void on_test_drag_end(void *ud, enum yasl_drag_end how)
+static void on_test_drag_end(void *ud, enum yasl_private_drag_end how)
{
- enum yasl_drag_end *res = ud;
+ enum yasl_private_drag_end *res = ud;
*res = how;
}
static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
{
/* 1. Caps */
- assert(caps(&d->base) & YASL_CAP_POPUPS);
- assert(caps(&d->base) & YASL_CAP_DRAG);
- assert(caps(&d->base) & YASL_CAP_DRAG_CARRY);
+ assert(caps(&d->base) & YASL_PRIVATE_CAP_POPUPS);
+ assert(caps(&d->base) & YASL_PRIVATE_CAP_DRAG);
+ assert(caps(&d->base) & YASL_PRIVATE_CAP_DRAG_CARRY);
/* 2. Popups: spec validation, calculation, creation, reposition, destruction */
- struct yasl_popup_spec invalid_specs[] = {
+ struct yasl_private_popup_spec invalid_specs[] = {
{.size = {0, 100}},
{.size = {100, 0}},
{.size = {35000, 100}},
@@ -70,7 +78,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
assert(!surface_create_popup(&s->base, &invalid_specs[i]));
}
- struct yasl_popup_spec valid_spec = {
+ struct yasl_private_popup_spec valid_spec = {
.size = {100, 120},
.anchor = {30, 40, 50, 60},
.beside = false,
@@ -78,12 +86,40 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
};
assert(popup_spec_valid(s, &valid_spec));
int x = 0, y = 0;
- popup_calculate_position(d, s, &valid_spec, &x, &y);
+ struct yasl_private_vector2i actual_size;
+ popup_calculate_position(d, s, &valid_spec, &x, &y, &actual_size);
assert(x == 30 - 5);
assert(y == 40 + 60 - 10);
+ /* Explicit XDG-style anchor/gravity stays exact unless a constraint allows a fit. */
+ struct yasl_private_popup_spec positioned = {
+ .positioner_explicit = true,
+ .anchor_edges = 2, /* bottom */
+ .gravity = 2, /* below */
+ .size = {100, 120}, .anchor = {100, 100, 40, 20},
+ .content_offset = {5, 10},
+ };
+ popup_calculate_position(d, s, &positioned, &x, &y, &actual_size);
+ assert(x == 68 && y == 110 && actual_size.x == 100 && actual_size.y == 120);
+ positioned.anchor_edges = 4; /* right edge */
+ positioned.gravity = 4; /* to the right */
+ positioned.anchor = (struct yasl_private_rect2i){1900, 100, 20, 20};
+ positioned.constraints = 4; /* flip X */
+ popup_calculate_position(d, s, &positioned, &x, &y, &actual_size);
+ assert(x == 1800 && actual_size.x == 100);
+ positioned.gravity = 0;
+ positioned.constraints = 1; /* slide X */
+ popup_calculate_position(d, s, &positioned, &x, &y, &actual_size);
+ assert(x + actual_size.x == 1920);
+ positioned.content_offset = (struct yasl_private_vector2i){0, 0};
+ positioned.gravity = 4;
+ positioned.constraints = 16; /* resize X */
+ positioned.anchor = (struct yasl_private_rect2i){1880, 100, 20, 20};
+ popup_calculate_position(d, s, &positioned, &x, &y, &actual_size);
+ assert(x == 1920 - actual_size.x && actual_size.x == 20);
+
valid_spec.beside = true;
- popup_calculate_position(d, s, &valid_spec, &x, &y);
+ popup_calculate_position(d, s, &valid_spec, &x, &y, &actual_size);
assert(x == 30 + 50 - 5);
assert(y == 40 - 10);
@@ -94,7 +130,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
assert(popup->parent == s);
assert(popup->size.x == 100 && popup->size.y == 120);
- struct yasl_popup_spec repo_spec = {
+ struct yasl_private_popup_spec repo_spec = {
.size = {150, 180},
.anchor = {10, 20, 30, 40},
.beside = false,
@@ -113,24 +149,24 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
struct drag_test_recorder rec1 = {0};
struct drag_test_recorder rec2 = {0};
- struct yasl_window_cbs cbs1 = {.drag = on_test_drag};
- struct yasl_window_cbs cbs2 = {.drag = on_test_drag};
+ struct yasl_private_window_cbs cbs1 = {.drag = on_test_drag};
+ struct yasl_private_window_cbs cbs2 = {.drag = on_test_drag};
set_callbacks(&s->base, &cbs1, &rec1);
set_callbacks(&s2.base, &cbs2, &rec2);
- enum yasl_drag_end end_reason = (enum yasl_drag_end)-1;
- struct yasl_drag_listener l = {.end = on_test_drag_end};
+ enum yasl_private_drag_end end_reason = (enum yasl_private_drag_end)-1;
+ struct yasl_private_drag_listener l = {.end = on_test_drag_end};
assert(drag_start(&d->base, &s->base, NULL, &l, &end_reason));
assert(d->drag.active);
- /* Motion on s (window 42) -> YASL_DRAG_OVER on s */
+ /* Motion on s (window 42) -> YASL_PRIVATE_DRAG_OVER on s */
XIDeviceEvent mev = {.evtype = XI_Motion, .sourceid = 8, .event = 42, .event_x = 12.0, .event_y = 34.0};
XGenericEventCookie cookie = {.evtype = XI_Motion, .data = &mev};
xi_event(d, &cookie);
assert(rec1.over_count == 1);
assert(rec1.last_pos.x == 12 && rec1.last_pos.y == 34);
- /* Motion on s2 (window 43) -> YASL_DRAG_LEAVE on s, YASL_DRAG_OVER on s2 */
+ /* Motion on s2 (window 43) -> YASL_PRIVATE_DRAG_LEAVE on s, YASL_PRIVATE_DRAG_OVER on s2 */
mev.event = 43;
mev.event_x = 56.0;
mev.event_y = 78.0;
@@ -139,12 +175,12 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
assert(rec2.over_count == 1);
assert(rec2.last_pos.x == 56 && rec2.last_pos.y == 78);
- /* ButtonRelease -> YASL_DRAG_DROP on s2 and drag end */
+ /* ButtonRelease -> YASL_PRIVATE_DRAG_DROP on s2 and drag end */
XIDeviceEvent rel = {.evtype = XI_ButtonRelease, .sourceid = 8, .event = 43, .detail = 1};
cookie = (XGenericEventCookie){.evtype = XI_ButtonRelease, .data = &rel};
xi_event(d, &cookie);
assert(rec2.drop_count == 1);
- assert(end_reason == YASL_DRAG_DROPPED);
+ assert(end_reason == YASL_PRIVATE_DRAG_DROPPED);
assert(!d->drag.active);
/* Drag cancel */
@@ -155,7 +191,7 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
assert(rec1.over_count == 2);
drag_cancel(&d->base);
assert(rec1.leave_count == 2);
- assert(end_reason == YASL_DRAG_CANCELLED);
+ assert(end_reason == YASL_PRIVATE_DRAG_CANCELLED);
assert(!d->drag.active);
/* Hierarchy change cancels drag and pushes reset */
@@ -164,18 +200,18 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
XIHierarchyEvent hiev = {.evtype = XI_HierarchyChanged, .flags = XISlaveRemoved};
cookie = (XGenericEventCookie){.evtype = XI_HierarchyChanged, .data = &hiev};
xi_event(d, &cookie);
- assert(end_reason == YASL_DRAG_CANCELLED);
+ assert(end_reason == YASL_PRIVATE_DRAG_CANCELLED);
assert(!d->drag.active);
- struct yasl_event reset_ev;
- assert(yasl_surface_poll_event(&s->base, &reset_ev));
- assert(reset_ev.type == YASL_EVENT_INPUT_RESET);
+ struct yasl_private_event reset_ev;
+ assert(yasl_private_surface_poll_event(&s->base, &reset_ev));
+ assert(reset_ev.type == YASL_PRIVATE_EVENT_INPUT_RESET);
/* 4. Drag image parameter */
uint32_t pixels[16] = {0xFF00FF00};
- struct yasl_drag_image img = {.pixels = pixels, .size = {4, 4}, .hotspot = {2, 2}};
+ struct yasl_private_drag_image img = {.pixels = pixels, .size = {4, 4}, .hotspot = {2, 2}};
assert(drag_start(&d->base, &s->base, &img, &l, &end_reason));
assert(d->drag.active);
- assert(drag_carry(&s->base, (struct yasl_vector2i){10, 10}));
+ assert(drag_carry(&s->base, (struct yasl_private_vector2i){10, 10}));
drag_cancel(&d->base);
/* 5. XDND drop-target events and URI parsing */
@@ -246,11 +282,11 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
assert(drec.leave_count == 2);
assert(d->pending_drop.active);
- /* Push YASL_EVENT_DROP */
- push(s, (struct yasl_event){.type = YASL_EVENT_DROP}, "file:///sample.png\r\n", 20);
- struct yasl_event drop_ev;
- assert(yasl_surface_poll_event(&s->base, &drop_ev));
- assert(drop_ev.type == YASL_EVENT_DROP);
+ /* Push YASL_PRIVATE_EVENT_DROP */
+ push(s, (struct yasl_private_event){.type = YASL_PRIVATE_EVENT_DROP}, "file:///sample.png\r\n", 20);
+ struct yasl_private_event drop_ev;
+ assert(yasl_private_surface_poll_event(&s->base, &drop_ev));
+ assert(drop_ev.type == YASL_PRIVATE_EVENT_DROP);
assert(drop_ev.text.len == 20);
assert(!strcmp(drop_ev.text.data, "file:///sample.png\r\n"));
@@ -262,10 +298,18 @@ static void test_x11_gaps(struct x11_display *d, struct x11_surface *s)
int main(void)
{
+ /* External GPU surfaces retain geometry without allocating CPU targets. */
+ struct x11_surface external = {0};
+ external.base.presenter = YASL_PRIVATE_PRESENTER_NONE;
+ assert(resize(&external, (struct yasl_private_vector2i){640, 480}));
+ assert(external.size.x == 640 && external.size.y == 480 && !external.image && !external.pixels);
+
struct x11_display display = {
- .base.ops = &yasl_x11_backend, .abs_pressure = 10, .abs_tilt_x = 11, .abs_tilt_y = 12,
+ .base.ops = &yasl_private_x11_backend, .abs_pressure = 10, .abs_tilt_x = 11, .abs_tilt_y = 12,
};
- struct x11_surface surface = {.base.display = &display.base, .window = 42};
+ struct x11_surface surface = {.base = {.display = &display.base,
+ .creation_flags = YASL_PRIVATE_SURFACE_CPU,
+ .presenter = YASL_PRIVATE_PRESENTER_CPU}, .window = 42};
display.surfaces = &surface;
XIValuatorClassInfo pressure = {.type = XIValuatorClass, .number = 2,
.label = 10, .min = 100, .max = 1100, .mode = XIModeAbsolute};
@@ -277,7 +321,7 @@ int main(void)
XIDeviceInfo info = {.deviceid = 8, .name = "Tablet pen", .num_classes = 3, .classes = classes};
struct x11_pointer pointer = {0};
pointer_info(&display, &pointer, &info);
- assert(pointer.device == YASL_PTR_DEV_PEN);
+ assert(pointer.device == YASL_PRIVATE_PTR_DEV_PEN);
display.pointers = &pointer; /* Cache avoids requiring a real X server. */
unsigned char mask[1] = {0};
@@ -290,9 +334,15 @@ int main(void)
.mods.effective = ShiftMask, .valuators = {sizeof(mask), mask, values}};
XGenericEventCookie cookie = {.evtype = XI_ButtonPress, .data = &sample};
xi_event(&display, &cookie);
- struct yasl_event ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_DOWN && ev.pointer.button == YASL_BUTTON_PRIMARY && ev.pointer.device == YASL_PTR_DEV_PEN);
- assert(ev.pointer.pos.x == 201 && ev.pointer.pos.y == 175 && ev.pointer.mods == YASL_MOD_SHIFT);
+ struct yasl_private_event ev = next(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_PEN && ev.pen.device_id == 8 && !ev.pen.tool_id);
+ assert(ev.pen.tool == 1 && ev.pen.action == YASL_PRIVATE_PEN_DOWN && ev.pen.in_contact);
+ assert((ev.pen.axes_valid & (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT)) ==
+ (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT));
+ assert(ev.pen.axes_changed == (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT));
+ ev = next(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_DOWN && ev.pointer.button == YASL_PRIVATE_BUTTON_PRIMARY && ev.pointer.device == YASL_PRIVATE_PTR_DEV_PEN);
+ assert(ev.pointer.pos.x == 201 && ev.pointer.pos.y == 175 && ev.pointer.mods == YASL_PRIVATE_MOD_SHIFT);
assert(ev.pointer.pen.pressure == 32768 && ev.pointer.pen.tilt_x == -35 && ev.pointer.pen.tilt_y == 42);
/* XI2 sends only changed axes, with values packed in mask order. */
@@ -302,36 +352,48 @@ int main(void)
sample.evtype = cookie.evtype = XI_Motion;
xi_event(&display, &cookie);
ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_MOTION && ev.pointer.pen.pressure == 65535);
+ assert(ev.type == YASL_PRIVATE_EVENT_PEN && ev.pen.action == YASL_PRIVATE_PEN_MOVE &&
+ ev.pen.axes_valid == (YASL_PRIVATE_PEN_PRESSURE | YASL_PRIVATE_PEN_TILT) &&
+ ev.pen.axes_changed == YASL_PRIVATE_PEN_PRESSURE && ev.pen.pressure == 1.0);
+ ev = next(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_MOTION && ev.pointer.pen.pressure == 65535);
assert(ev.pointer.pen.tilt_x == -35 && ev.pointer.pen.tilt_y == 42);
values[0] = 5000;
xi_event(&display, &cookie);
+ assert(next(&surface).type == YASL_PRIVATE_EVENT_PEN);
assert(next(&surface).pointer.pen.pressure == 65535);
values[0] = -50;
xi_event(&display, &cookie);
+ assert(next(&surface).type == YASL_PRIVATE_EVENT_PEN);
assert(next(&surface).pointer.pen.pressure == 0);
info.name = "Tablet ERASER";
pointer_info(&display, &pointer, &info);
- assert(pointer.device == YASL_PTR_DEV_ERASER);
+ assert(pointer.device == YASL_PRIVATE_PTR_DEV_ERASER);
values[0] = 900;
sample.evtype = cookie.evtype = XI_ButtonPress;
xi_event(&display, &cookie);
ev = next(&surface);
- assert(ev.pointer.device == YASL_PTR_DEV_ERASER && ev.pointer.pen.pressure > 0);
+ assert(ev.type == YASL_PRIVATE_EVENT_PEN && ev.pen.tool == 2 && ev.pen.action == YASL_PRIVATE_PEN_DOWN);
+ ev = next(&surface);
+ assert(ev.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER && ev.pointer.pen.pressure > 0);
/* Release must clear pressure even when the driver omits that valuator. */
sample.valuators.mask_len = 0;
sample.evtype = cookie.evtype = XI_ButtonRelease;
xi_event(&display, &cookie);
ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_UP && ev.pointer.button == YASL_BUTTON_PRIMARY && ev.pointer.pen.pressure == 0);
- assert(ev.pointer.device == YASL_PTR_DEV_ERASER);
+ assert(ev.type == YASL_PRIVATE_EVENT_PEN && ev.pen.action == YASL_PRIVATE_PEN_UP && !ev.pen.in_contact);
+ ev = next(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_UP && ev.pointer.button == YASL_PRIVATE_BUTTON_PRIMARY && ev.pointer.pen.pressure == 0);
+ assert(ev.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER);
XIEnterEvent crossing = {.sourceid = 8, .event = 42, .event_x = 200, .event_y = 175};
cookie = (XGenericEventCookie){.evtype = XI_Leave, .data = &crossing};
xi_event(&display, &cookie);
ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_LEAVE && ev.pointer.device == YASL_PTR_DEV_ERASER && !ev.pointer.pen.pressure);
+ assert(ev.type == YASL_PRIVATE_EVENT_PEN && ev.pen.action == YASL_PRIVATE_PEN_LEAVE);
+ ev = next(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_LEAVE && ev.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER && !ev.pointer.pen.pressure);
/* No tablet axes must produce an ordinary, pressure-free mouse. */
info.num_classes = 0;
@@ -340,24 +402,48 @@ int main(void)
sample.detail = 3;
xi_pointer_event(&surface, &sample);
ev = next(&surface);
- assert(ev.pointer.device == YASL_PTR_DEV_MOUSE && ev.pointer.button == YASL_BUTTON_SECONDARY && ev.pointer.pen.pressure == 0);
+ assert(ev.pointer.device == YASL_PRIVATE_PTR_DEV_MOUSE && ev.pointer.button == YASL_PRIVATE_BUTTON_SECONDARY && ev.pointer.pen.pressure == 0);
for (int button = 4; button <= 7; button++) {
sample.detail = button;
sample.evtype = XI_ButtonPress;
xi_pointer_event(&surface, &sample);
ev = next(&surface);
- assert(ev.type == YASL_EVENT_POINTER_SCROLL);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_SCROLL);
assert(ev.pointer.scroll.x == (button == 6 ? -1 : button == 7 ? 1 : 0));
assert(ev.pointer.scroll.y == (button == 4 ? -1 : button == 5 ? 1 : 0));
+ assert(ev.pointer.scroll_valid == 1u && ev.pointer.scroll_source == 1u && ev.pointer.scroll_phase == 2u);
sample.evtype = XI_ButtonRelease;
xi_pointer_event(&surface, &sample);
- struct yasl_event unused;
- assert(!yasl_surface_poll_event(&surface.base, &unused));
+ struct yasl_private_event unused;
+ assert(!input_queue_pop(&surface.input, &unused));
}
+ /* XI2 smooth-scroll valuators preserve fractional line data and source. */
+ XIValuatorClassInfo scroll_valuator = {.type = XIValuatorClass, .number = 9,
+ .value = 0, .mode = XIModeRelative};
+ XIScrollClassInfo scroll_class = {.type = XIScrollClass, .number = 9,
+ .scroll_type = XIScrollTypeVertical, .increment = 2.0};
+ XIAnyClassInfo *scroll_classes[] = {(void *)&scroll_valuator, (void *)&scroll_class};
+ info.name = "Test touchpad";
+ info.num_classes = 2;
+ info.classes = scroll_classes;
+ struct x11_pointer touchpad = {0};
+ pointer_info(&display, &touchpad, &info);
+ display.pointers = &touchpad;
+ unsigned char scroll_mask[2] = {0};
+ XISetMask(scroll_mask, 9);
+ double scroll_value[] = {-1.0};
+ sample.evtype = XI_Motion;
+ sample.valuators = (XIValuatorState){sizeof(scroll_mask), scroll_mask, scroll_value};
+ xi_pointer_event(&surface, &sample);
+ assert(next(&surface).type == YASL_PRIVATE_EVENT_POINTER_MOTION);
+ ev = next(&surface);
+ assert(ev.type == YASL_PRIVATE_EVENT_POINTER_SCROLL && ev.pointer.scroll_valid == 1u &&
+ ev.pointer.scroll_source == 2u && ev.pointer.scroll.y == -0.5);
+ input_queue_clear(&surface.input);
assert(pointer_button(2) == 1 && pointer_button(8) == 3);
- pointer.pressure = (struct tablet_axis){2, 1, 1, 50};
+ pointer.pressure = (struct tablet_axis){2, 1, 1, 50, true};
assert(pressure_value(&pointer.pressure) == 0);
- pointer.tilt_x = (struct tablet_axis){4, -90, 90, -200};
+ pointer.tilt_x = (struct tablet_axis){4, -90, 90, -200, true};
assert(tilt_value(&pointer.tilt_x) == -90);
pointer.tilt_x.value = 200;
assert(tilt_value(&pointer.tilt_x) == 90);
diff --git a/tests/x11/test_tablet_native.c b/tests/x11/test_tablet_native.c
index 706a5cc..828082c 100644
--- a/tests/x11/test_tablet_native.c
+++ b/tests/x11/test_tablet_native.c
@@ -2,7 +2,7 @@
#define main tablet_example_main
#include "../../examples/tablet.c"
#undef main
-#include <yasl/native.h>
+#include <common/private_native.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -55,24 +55,24 @@ static int connect_tool(const char *path)
return fd;
}
-static void native_draw(struct yasl_surface *surface, struct yasl_frame *frame, void *ud)
+static void native_draw(struct yasl_surface *surface, struct yasl_private_frame *frame, void *ud)
{
(void)surface;
draw(ud, surface, frame);
}
-static void native_configure(struct yasl_surface *surface, struct yasl_geometry geometry, void *ud)
+static void native_configure(struct yasl_surface *surface, struct yasl_private_geometry geometry, void *ud)
{
- input(ud, &(struct yasl_event){.surface = surface, .type = YASL_EVENT_WINDOW_RESIZE,
+ input(ud, &(struct yasl_private_event){.surface = surface, .type = YASL_PRIVATE_EVENT_WINDOW_RESIZE,
.resize = {.geometry = geometry}});
}
-static void native_input(struct app *app, const struct yasl_event *event)
+static void native_input(struct app *app, const struct yasl_private_event *event)
{
input(app, event);
}
-static void pump(int fd, struct yasl_display *display, struct yasl_surface *surface, struct app *app)
+static void pump(int fd, struct yasl_private_display *display, struct yasl_surface *surface, struct app *app)
{
xf86ITEventWaitForSync sync = {{sizeof(sync), XF86IT_EVENT_WAIT_FOR_SYNC}};
send_all(fd, &sync, sizeof(sync));
@@ -81,14 +81,14 @@ static void pump(int fd, struct yasl_display *display, struct yasl_surface *surf
assert(response.type == XF86IT_RESPONSE_SYNC_FINISHED && response.length == sizeof(response));
/* The driver acknowledgment guarantees input processing has finished.
* Round-trip on YASL's X connection before draining its event queue. */
- XSync(yasl_display_get_native_handle(display), False);
- assert(yasl_display_dispatch(display, 0) == 0);
- struct yasl_event event;
- while (yasl_surface_poll_event(surface, &event)) {
+ XSync(yasl_private_display_get_native_handle(display), False);
+ assert(yasl_private_display_dispatch(display, 0) == 0);
+ struct yasl_private_event event;
+ while (yasl_private_surface_poll_event(surface, &event)) {
native_input(app, &event);
- yasl_surface_schedule_redraw(surface);
+ yasl_private_surface_schedule_redraw(surface);
}
- yasl_display_flush(display);
+ yasl_private_display_flush(display);
assert(!app->failed);
}
@@ -116,23 +116,23 @@ static void set_eraser_property(Display *display)
int main(int argc, char **argv)
{
assert(argc == 3); /* pen socket, eraser socket */
- struct app app = {.tablet_supported = true, .backend = YASL_BACKEND_X11};
- struct yasl_display_spec ds = {.backend = YASL_BACKEND_X11};
- struct yasl_display *display = yasl_display_open(NULL, &ds);
- assert(display && (yasl_display_caps(display) & YASL_CAP_TABLET));
- struct yasl_window_spec ss = {.title = "YASL native pressure test", .size = {500, 400}};
- struct yasl_surface *surface = yasl_surface_create_toplevel(display, &ss);
+ struct app app = {.tablet_supported = true, .backend = YASL_PRIVATE_BACKEND_X11};
+ struct yasl_private_display_spec ds = {.backend = YASL_PRIVATE_BACKEND_X11};
+ struct yasl_private_display *display = yasl_private_display_open(NULL, &ds);
+ assert(display && (yasl_private_display_caps(display) & YASL_PRIVATE_CAP_TABLET));
+ struct yasl_private_window_spec ss = {.title = "YASL native pressure test", .size = {500, 400}};
+ struct yasl_surface *surface = yasl_private_surface_create_toplevel(display, &ss);
assert(surface);
- struct yasl_window_cbs cbs = {.draw = native_draw, .configure = native_configure};
- yasl_surface_set_callbacks(surface, &cbs, &app);
- yasl_surface_schedule_redraw(surface);
- Display *x = yasl_display_get_native_handle(display);
- Window window = (Window)(uintptr_t)yasl_surface_get_native_handle(surface);
+ struct yasl_private_window_cbs cbs = {.draw = native_draw, .configure = native_configure};
+ yasl_private_surface_set_callbacks(surface, &cbs, &app);
+ yasl_private_surface_schedule_redraw(surface);
+ Display *x = yasl_private_display_get_native_handle(display);
+ Window window = (Window)(uintptr_t)yasl_private_surface_get_native_handle(surface);
XMoveWindow(x, window, 0, 0);
XSetInputFocus(x, window, RevertToParent, CurrentTime);
XSync(x, False);
- assert(yasl_display_dispatch(display, 0) == 0);
- yasl_display_flush(display);
+ assert(yasl_private_display_dispatch(display, 0) == 0);
+ yasl_private_display_flush(display);
set_eraser_property(x);
int pen_fd = connect_tool(argv[1]), eraser_fd = connect_tool(argv[2]);
xf86ITEventMotion motion = {.header = {sizeof(motion), XF86IT_EVENT_MOTION}, .is_absolute = 1};
@@ -144,7 +144,7 @@ int main(int argc, char **argv)
send_all(pen_fd, &proximity, sizeof(proximity));
send_all(pen_fd, &motion, sizeof(motion));
pump(pen_fd, display, surface, &app);
- assert(app.visible && !app.down && app.pointer.device == YASL_PTR_DEV_PEN);
+ assert(app.visible && !app.down && app.pointer.device == YASL_PRIVATE_PTR_DEV_PEN);
assert(app.pointer.pos.x == 150 && app.pointer.pos.y == 200);
assert(app.canvas[(size_t)200 * app.geometry.size.x + 150] == PAPER);
xf86ITEventButton button = {.header = {sizeof(button), XF86IT_EVENT_BUTTON},
@@ -177,7 +177,7 @@ int main(int argc, char **argv)
button.valuators.valuators[4] = 1000;
send_all(eraser_fd, &button, sizeof(button));
pump(eraser_fd, display, surface, &app);
- assert(app.down && app.pointer.device == YASL_PTR_DEV_ERASER && app.pointer.pen.pressure == 65535);
+ assert(app.down && app.pointer.device == YASL_PRIVATE_PTR_DEV_ERASER && app.pointer.pen.pressure == 65535);
assert(app.canvas[(size_t)200 * app.geometry.size.x + 150] == PAPER &&
app.canvas[(size_t)200 * app.geometry.size.x + 170] == PAPER);
button.is_press = 0;
@@ -187,8 +187,8 @@ int main(int argc, char **argv)
close(pen_fd);
close(eraser_fd);
free(app.canvas);
- yasl_surface_destroy(surface);
- yasl_display_close(display);
+ yasl_private_surface_destroy(surface);
+ yasl_private_display_close(display);
puts("Native Xorg pen pressure, stationary pressure, release, and eraser drawing passed.");
return 0;
}
diff --git a/yasl-gaps-report.md b/yasl-gaps-report.md
deleted file mode 100644
index 6078536..0000000
--- a/yasl-gaps-report.md
+++ /dev/null
@@ -1,179 +0,0 @@
-# yasl gaps for wsap_term
-
-Date: 2026-10-13
-yasl head checked: `83c2180`
-wsap_term state: `3a1439b`. The project builds, all 111 meson tests pass, and
-the include-graph check reports no violations.
-
-## Summary
-
-All the earlier yasl gaps are closed in code. Drag targets, drag images, and
-file drops now exist on every backend. They were checked by reading code, not at
-runtime on X11 or macOS.
-
-The wsap_term audit of `83c2180` asked for six pieces of yasl API. All six
-are implemented; see "Requests from the wsap_term audit" below. One gap
-remains on the project side.
-
-## Resolved in yasl
-
-### 1. Drop-target events on X11 and Cocoa
-
-Status: **Resolved**. Both backends now send the target events.
-
-- X11: `drag_set_hover`, `drag_leave_hover`, and `drag_end` in
- `src/platform/x11/window.c` send `YASL_DRAG_OVER`, `YASL_DRAG_LEAVE`, and
- `YASL_DRAG_DROP`. The XDND target code sends `YASL_DROP_OVER` and
- `YASL_DROP_LEAVE`.
-- Cocoa: `src/platform/cocoa/window.m` sends all five target events.
-
-### 2. Drag images on X11 and Cocoa
-
-Status: **Resolved**. X11 shows the image in an override-redirect window that
-follows the pointer (`drag_start` in `src/platform/x11/window.c`). Cocoa shows it
-in a borderless image window. No backend ignores the image now.
-
-### 3. X11 file drops
-
-Status: **Resolved**. X11 accepts XDND drops of `text/uri-list` and sends
-`YASL_EVENT_DROP` (`src/platform/x11/window.c`).
-
-### 4. IME preedit on X11
-
-Status: **Resolved**. `XNSpotLocation` places the candidate window through
-`yasl_surface_set_ime_caret`. Preedit text and its cursor are available through
-`yasl_surface_ime_preedit` and `yasl_surface_ime_preedit_cursor`.
-
-### 5. Cocoa popups
-
-Status: **Resolved** in commit `b20263c`. Child-window popups are implemented,
-and `YASL_CAP_POPUPS` is advertised.
-
-### 6. X11 popups
-
-Status: **Resolved** in commit `b20263c`. Override-redirect popups are
-implemented, and `YASL_CAP_POPUPS` is advertised.
-
-### 7. Cocoa primary selection
-
-Status: **Resolved** in commit `b20263c`. A dedicated pasteboard emulates the
-primary selection, and `YASL_CAP_PRIMARY_SELECTION` is advertised.
-
-### 8. X11 and Cocoa CSD and frame-pacing flags
-
-Status: **Resolved**.
-
-- Cocoa advertises `YASL_CAP_CSD` and `YASL_CAP_FRAME_PACING`.
-- X11 advertises `YASL_CAP_CSD`, with `_NET_WM_MOVERESIZE` move and resize and
- `_GTK_SHOW_WINDOW_MENU`.
-- `yasl_run` paces continuous X11 surfaces at about 16 ms to avoid a spin loop.
-
-## Project-side gaps (wsap_term)
-
-### 9. Reset does not clear the UI pointer press
-
-yasl's `YASL_EVENT_INPUT_RESET` says the app must clear held keys, buttons,
-strokes, and composition state.
-
-The adapter (`src/ui/yasl/native_input.c`) now clears keyboard and held-button
-state. The UI still keeps the press active (`ui_route.c:116-119`,
-`selection.c:79`). A press can stay stuck after an X11 device removal, a queue
-allocation failure, or a Wayland text allocation failure.
-
-Suggested fix: send a pointer release to the UI on reset, or add a UI-level
-reset.
-
-## Requests from the wsap_term audit
-
-All six requests are implemented. Linux paths are built and tested on Wayland
-and X11. No Cocoa code was built or run.
-
-### 1.1 Signal watching
-
-Status: **Done** in `efca84e`.
-
-`yasl_loop_watch_signals()` and `yasl_loop_unwatch_signals()` deliver signals
-to a callback on the loop thread. The contract the audit asked for:
-
-- Thread masks: YASL changes no thread's mask. Each signal gets a process-wide
- handler with `SA_RESTART` and an empty handler mask. The handler may run on
- any thread; it only records the signal and wakes the loop. A signal blocked
- in every thread is never delivered.
-- Platforms: the same handler-based design runs on Linux and macOS. There is
- no `signalfd` path.
-- `SIGCHLD`: callbacks run on the loop thread. Stopped and continued children
- also raise it.
-- One loop can watch a given signal at a time; another loop gets `EBUSY`.
- Unwatch restores the saved actions.
-
-### 1.2 Cross-thread wake or post
-
-Status: **Done** in `efca84e`.
-
-- `yasl_loop_kick()` is documented as safe from any thread and from a signal
- handler.
-- `yasl_loop_post()` runs a function on the loop thread from any thread, in
- posting order, with a plain `yasl_loop`.
-- Cocoa dispatch did not wake for kicks, posts, or fd watches while waiting
- for AppKit events. It now mirrors the loop's descriptors in a kqueue while
- it waits. This also fixes `yasl_post_event()` wakeups under `yasl_run()` on
- Cocoa.
-
-### 1.3 Native double-click interval
-
-Status: **Done** in `ac6f9f5`.
-
-`yasl_display_double_click_ms()` reads XSETTINGS `Net/DoubleClickTime` on X11,
-the KDE `DoubleClickInterval` or GNOME `double-click` setting through the
-Settings portal on Wayland (and on X11 without XSETTINGS), and
-`NSEvent.doubleClickInterval` on Cocoa. It falls back to 400 ms.
-
-### 1.4 Held pointer buttons
-
-Status: **Done** in `cbd1fc7`.
-
-Every pointer event now has `buttons`, a `YASL_BUTTON_BIT()` mask of the
-buttons held after the event. `INPUT_RESET` clears it. Wayland also clears it
-on leave, because no release follows. X11 and Cocoa keep it across leave and
-still deliver the release.
-
-### 1.5 Desktop helpers
-
-Status: **Done** in `d4d13e6`, with protocol code in `305a341`.
-
-`<yasl/desktop.h>` has `yasl_display_open_uri()`, `yasl_display_notify()`,
-`yasl_display_appearance()`, `yasl_surface_bell()`, `yasl_executable_path()`,
-`yasl_resource_dir()`, and `yasl_default_locale()`.
-
-- Linux opens URIs with `xdg-open` in a new session, with default signal
- dispositions and no inherited descriptors.
-- Notifications and appearance use libdbus-1, an optional dependency behind
- `-Ddbus`. With it, the app no longer needs GIO for notifications.
-- The bell uses xdg-system-bell on Wayland, the XKB bell on X11, and `NSBeep`.
-- `yasl_display_open_uri()` checks only the URI syntax. Scheme policy, such as
- `str_url_scheme_allowed`, stays in the app.
-- `yasl_default_locale()` on Linux reads `LC_ALL`, `LC_CTYPE`, then `LANG`.
- The app's Linux code always returns `en_US.UTF-8` today.
-
-### 1.6 Smaller items
-
-Status: **Done**.
-
-- Drop URIs: `yasl_drop_next_path()` decodes local paths from a drop payload
- (`0437815`).
-- Framebuffer readback: `yasl_surface_read_pixels()` copies the last presented
- CPU frame (`b7944e4`).
-- `strndup` warning: IME text is copied with `strlen` and a cap. An LTO release
- build is warning-free (`6603122`).
-- `yasl_loop_kick` threading: documented with 1.2 (`efca84e`).
-
-The extra-button encoding in the term subproject belongs to that project, not
-to yasl.
-
-## Not verified
-
-- Runtime behavior of items 1–3 on X11 and Cocoa.
-- Any Cocoa path, including the dispatch wake, desktop services, double-click
- interval, readback, and bell. Cocoa needs a macOS build and test.
-- Notifications and the bell were not shown on a real desktop. The desktop
- test checks the D-Bus messages against a fake service on a private bus.