diff options
| author | Adam Malczewski <[email protected]> | 2026-06-12 19:11:59 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-12 19:11:59 +0900 |
| commit | a21f705692595ea711a736e2ae9c256c1dde7b1e (patch) | |
| tree | 2cb3e6380e65f9b584a1dea1cd43e1a127a4f56d | |
| parent | 6f45dc177540d6c6ae7596427209091d4c7adc20 (diff) | |
| download | unbox-a21f705692595ea711a736e2ae9c256c1dde7b1e.tar.gz unbox-a21f705692595ea711a736e2ae9c256c1dde7b1e.zip | |
Slice 1: Meson skeleton — kernel links wlroots 0.20 from C++, RMLUi 6.2 vendored
Root meson.build (C++23, WLR_USE_UNSTABLE, ccache-detected) with RMLUi 6.2
as a wrap-file tarball built through the cmake module (no git submodules —
settled decision) and doctest 2.5.2 from wrapdb. kernel unit: extern-"C"
wlr.hpp wrapper (with the C99 [static N] array-param workaround documented
in kernel.md), slice-1 probe contract, doctest suite (1/1 green). host-bin:
composition root printing versions, exit 0. tasks.md slice 1 done.
| -rw-r--r-- | .clang-format | 8 | ||||
| -rw-r--r-- | meson.build | 35 | ||||
| -rw-r--r-- | notes/plan.md | 3 | ||||
| -rw-r--r-- | packages/host-bin/meson.build | 8 | ||||
| -rw-r--r-- | packages/host-bin/src/main.cpp | 14 | ||||
| -rw-r--r-- | packages/kernel/include/unbox/kernel/kernel.hpp | 26 | ||||
| -rw-r--r-- | packages/kernel/include/unbox/kernel/wlr.hpp | 28 | ||||
| -rw-r--r-- | packages/kernel/kernel.md | 19 | ||||
| -rw-r--r-- | packages/kernel/meson.build | 26 | ||||
| -rw-r--r-- | packages/kernel/src/kernel.cpp | 26 | ||||
| -rw-r--r-- | packages/kernel/tests/test_kernel.cpp | 13 | ||||
| -rw-r--r-- | subprojects/doctest.wrap | 10 | ||||
| -rw-r--r-- | subprojects/rmlui.wrap | 6 | ||||
| -rw-r--r-- | tasks.md | 6 |
14 files changed, 224 insertions, 4 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..76d4ae5 --- /dev/null +++ b/.clang-format @@ -0,0 +1,8 @@ +# Minimal seed (plan.md §7: refine on first real dispute, not before). +BasedOnStyle: LLVM +IndentWidth: 4 +ColumnLimit: 100 +PointerAlignment: Left +DerivePointerAlignment: false +AccessModifierOffset: -4 +AllowShortFunctionsOnASingleLine: Inline diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..3c297fe --- /dev/null +++ b/meson.build @@ -0,0 +1,35 @@ +project( + 'unbox', + 'cpp', + version: '0.0.1', + meson_version: '>=1.4.0', + default_options: [ + 'cpp_std=c++23', + 'warning_level=2', + 'default_library=static', + ], +) + +# wlroots is pre-1.0: every release breaks API. The pin lives HERE and in +# the kernel's wlr.hpp wrapper — nowhere else (.unbox/rules/wlroots-include.md). +add_project_arguments('-DWLR_USE_UNSTABLE', language: 'cpp') + +wlroots_dep = dependency('wlroots-0.20') +wayland_server_dep = dependency('wayland-server') + +# RMLUi: CMake-only upstream, vendored as a wrap-file tarball (NO git +# submodules — settled decision, notes/plan.md §2) built via the cmake module. +cmake = import('cmake') +rmlui_opts = cmake.subproject_options() +rmlui_opts.add_cmake_defines({ + 'BUILD_SHARED_LIBS': false, + 'RMLUI_SAMPLES': false, +}) +rmlui_proj = cmake.subproject('rmlui', options: rmlui_opts) +rmlui_dep = rmlui_proj.dependency('rmlui_core') + +doctest_dep = dependency('doctest') + +# Units. Adding one? ALL FOUR steps of .unbox/rules/unit-registration.md. +subdir('packages/kernel') +subdir('packages/host-bin') diff --git a/notes/plan.md b/notes/plan.md index 061eb6a..da0da54 100644 --- a/notes/plan.md +++ b/notes/plan.md @@ -50,7 +50,8 @@ solves), and the trigger that would reopen it. | **wlroots 0.20**, system package, pinned, via ONE extern-"C" wrapper header | Matches running labwc (proven on this hardware); wlroots API churns between minors — the pin + single wrapper contains upgrades to one file | 0.21+ needed for a feature; bump = one dedicated slice | | **wlr GLES2 renderer** for compositing; **sibling GLES 3.2 EGL context** (shared EGLDisplay) for RMLUi | wlroots' renderer is its own battle-tested choice; RMLUi's maintained backend needs GL3/GLES3; GLES 3.2 verified native. Older GL API ≠ faster: same crocus driver either way, and ES3 features (UBOs, VAOs, instancing) REDUCE CPU-side driver overhead — the actual bottleneck on this CPU | — | | **NO ANGLE / translation layers** | ANGLE's Linux backend is Vulkan (no driver here; hasvk deprecated); ANGLE-on-GL just stacks on the Mesa we'd bypass; Chromium-scale build; tens of MB RSS; wlroots speaks system EGL | — | -| **Meson + Ninja**, RMLUi via Meson's cmake subproject module; ccache; `build/` + `build-asan/` separate | Wayland-ecosystem native (wlroots/labwc/sway), first-class wayland-scanner codegen; RMLUi is CMake-only, the cmake module bridges it, built once | cmake-module friction exceeds ~a day of fighting → fall back to prebuilding RMLUi as a system-installed lib | +| **Meson + Ninja**, RMLUi via Meson's cmake subproject module; ccache; `build/` + `build-asan/` separate | Wayland-ecosystem native (wlroots/labwc/sway), first-class wayland-scanner codegen; RMLUi is CMake-only, the cmake module bridges it, built once. PROVEN at slice 1: RMLUi 6.2 configures + builds via the cmake module first try | cmake-module friction on a future RMLUi bump → fall back to prebuilding RMLUi as a system-installed lib | +| **Vendoring = Meson wrap-file tarballs, NEVER git submodules** (user decision) | Wraps pin by sha256, sources land in gitignored `subprojects/`, `packagecache/` allows pre-seeding; submodule UX rejected | — | | **C++23, gcc 16** | Already installed; designated initializers ease wlroots struct setup; Hyprland proved years of C++-on-wlroots | — | | **toml++** for `unbox.toml` config | Mirrors dispatch.toml convention; header-only | — | | **doctest** for pure cores | Lightest compile cost of the mainstream frameworks — compile time IS the scarce resource here | — | diff --git a/packages/host-bin/meson.build b/packages/host-bin/meson.build new file mode 100644 index 0000000..3ff936a --- /dev/null +++ b/packages/host-bin/meson.build @@ -0,0 +1,8 @@ +# host-bin — the composition root (orchestrator-owned). The ONLY place that +# names every extension; activates them in dependency order (from slice 4). + +executable( + 'unbox', + 'src/main.cpp', + dependencies: [kernel_dep], +) diff --git a/packages/host-bin/src/main.cpp b/packages/host-bin/src/main.cpp new file mode 100644 index 0000000..713186d --- /dev/null +++ b/packages/host-bin/src/main.cpp @@ -0,0 +1,14 @@ +#include <unbox/kernel/kernel.hpp> + +#include <cstdio> + +auto main() -> int { + const bool probe_ok = unbox::kernel::link_probe(); + + std::printf("unbox 0.0.1 — kernel skeleton (slice 1)\n"); + std::printf(" wlroots %s\n", unbox::kernel::wlroots_version().c_str()); + std::printf(" RmlUi %s\n", unbox::kernel::rmlui_version().c_str()); + std::printf(" link probe %s\n", probe_ok ? "ok" : "FAILED"); + + return probe_ok ? 0 : 1; +} diff --git a/packages/kernel/include/unbox/kernel/kernel.hpp b/packages/kernel/include/unbox/kernel/kernel.hpp new file mode 100644 index 0000000..9836a14 --- /dev/null +++ b/packages/kernel/include/unbox/kernel/kernel.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include <string> + +// Slice-1 probe surface: proves the kernel compiles against wlroots from +// C++ and links wlroots, libwayland-server, and the vendored RMLUi. The +// real contracts (extension host, bus, scene/seat glue, ui substrate) +// replace this from slice 2 on. +// +// Calling context: everything in unbox runs on the single wl_event_loop +// thread unless a contract explicitly states otherwise. + +namespace unbox::kernel { + +/// The wlroots version this kernel was compiled against (the 0.20 pin). +[[nodiscard]] auto wlroots_version() -> std::string; + +/// The RMLUi version linked from the vendored subproject. +[[nodiscard]] auto rmlui_version() -> std::string; + +/// Creates and immediately destroys a wl_display: proves we can call into +/// libwayland-server/wlroots at runtime, not just link. Returns true on +/// success. No side effects beyond wlroots log initialization. +[[nodiscard]] auto link_probe() -> bool; + +} // namespace unbox::kernel diff --git a/packages/kernel/include/unbox/kernel/wlr.hpp b/packages/kernel/include/unbox/kernel/wlr.hpp new file mode 100644 index 0000000..6374a52 --- /dev/null +++ b/packages/kernel/include/unbox/kernel/wlr.hpp @@ -0,0 +1,28 @@ +#pragma once + +// The ONLY file in the project allowed to include wlroots / libwayland +// headers (.unbox/rules/wlroots-include.md). wlroots is C; this wrapper +// provides the extern-"C" guards C++ needs. WLR_USE_UNSTABLE is defined +// project-wide in the root meson.build; the version pin is wlroots 0.20. +// +// Grow this include list as kernel glue needs more types — never include +// <wlr/...> anywhere else, in any unit. + +extern "C" { +#include <wayland-server-core.h> + +// wlroots headers use C99 array-parameter syntax (`float color[static 4]`), +// which is invalid C++. Blanking `static` around the wlr includes is the +// proven workaround (Hyprland shipped years on it): `static inline` header +// helpers become plain `inline`, which C++ ODR-merges safely. Keep the +// #define scoped to EXACTLY these includes. +#define static +#include <wlr/backend.h> +#include <wlr/render/allocator.h> +#include <wlr/render/wlr_renderer.h> +#include <wlr/types/wlr_output.h> +#include <wlr/types/wlr_scene.h> +#include <wlr/util/log.h> +#include <wlr/version.h> +#undef static +} diff --git a/packages/kernel/kernel.md b/packages/kernel/kernel.md new file mode 100644 index 0000000..e2ee684 --- /dev/null +++ b/packages/kernel/kernel.md @@ -0,0 +1,19 @@ +# kernel — package notes + +Slice-1 state: a probe surface (`kernel.hpp`) proving C++ ↔ wlroots ↔ RMLUi +compile/link. Real contracts (extension host, bus, scene/seat glue, ui +substrate) land from slice 2. + +Gotchas the headers can't express: + +- **`wlr.hpp` blanks `static` around the wlr includes.** wlroots headers + use C99 array-parameter syntax (`float color[static 4]`), invalid in + C++. With `static` blanked, `static inline` helpers become `inline` + (ODR-merged, safe). Cost: a function-local `static` inside a header + inline would silently lose persistence — none exist in our include set; + re-audit when ADDING includes to the wrapper. +- **RMLUi is kernel-private.** `rmlui_dep` is deliberately absent from + `kernel_dep` propagation (see meson.build): extensions contribute RML + documents + data bindings via the ui substrate, never RMLUi API calls. + Do not "fix" a missing-RMLUi-header error downstream by propagating it. +- Everything runs on the single `wl_event_loop` thread. diff --git a/packages/kernel/meson.build b/packages/kernel/meson.build new file mode 100644 index 0000000..4a67ced --- /dev/null +++ b/packages/kernel/meson.build @@ -0,0 +1,26 @@ +# kernel — the minimal runtime core. Public headers (the ABI): include/unbox/kernel/ + +kernel_inc = include_directories('include') + +kernel_lib = static_library( + 'unbox-kernel', + 'src/kernel.cpp', + include_directories: kernel_inc, + dependencies: [wlroots_dep, wayland_server_dep, rmlui_dep], +) + +# What consumers get. wlroots/wayland propagate because wlr.hpp is a public +# header; RMLUi does NOT — it is kernel-private (the ui substrate owns it, +# extensions contribute RML documents + data bindings, never RMLUi calls). +kernel_dep = declare_dependency( + link_with: kernel_lib, + include_directories: kernel_inc, + dependencies: [wlroots_dep, wayland_server_dep], +) + +kernel_test = executable( + 'kernel-tests', + 'tests/test_kernel.cpp', + dependencies: [kernel_dep, doctest_dep], +) +test('kernel', kernel_test, suite: 'kernel') diff --git a/packages/kernel/src/kernel.cpp b/packages/kernel/src/kernel.cpp new file mode 100644 index 0000000..3de39c0 --- /dev/null +++ b/packages/kernel/src/kernel.cpp @@ -0,0 +1,26 @@ +#include <unbox/kernel/kernel.hpp> +#include <unbox/kernel/wlr.hpp> + +#include <RmlUi/Core/Core.h> + +namespace unbox::kernel { + +auto wlroots_version() -> std::string { + return WLR_VERSION_STR; +} + +auto rmlui_version() -> std::string { + return Rml::GetVersion(); +} + +auto link_probe() -> bool { + wlr_log_init(WLR_ERROR, nullptr); + wl_display* display = wl_display_create(); + if (display == nullptr) { + return false; + } + wl_display_destroy(display); + return true; +} + +} // namespace unbox::kernel diff --git a/packages/kernel/tests/test_kernel.cpp b/packages/kernel/tests/test_kernel.cpp new file mode 100644 index 0000000..ebcfbc3 --- /dev/null +++ b/packages/kernel/tests/test_kernel.cpp @@ -0,0 +1,13 @@ +#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN +#include <doctest/doctest.h> + +#include <unbox/kernel/kernel.hpp> + +TEST_CASE("kernel compiles against and links wlroots + libwayland-server") { + CHECK(unbox::kernel::link_probe()); + CHECK(unbox::kernel::wlroots_version().substr(0, 4) == "0.20"); +} + +TEST_CASE("vendored RMLUi subproject compiled and linked") { + CHECK(!unbox::kernel::rmlui_version().empty()); +} diff --git a/subprojects/doctest.wrap b/subprojects/doctest.wrap new file mode 100644 index 0000000..872898c --- /dev/null +++ b/subprojects/doctest.wrap @@ -0,0 +1,10 @@ +[wrap-file] +directory = doctest-2.5.2 +source_url = https://github.com/doctest/doctest/archive/refs/tags/v2.5.2.tar.gz +source_filename = doctest-2.5.2.tar.gz +source_hash = 9189960c2bbbc4f3382ce0773b2bb5f13e3afd8fed47f55f193e11e85a4f9854 +source_fallback_url = https://wrapdb.mesonbuild.com/v2/doctest_2.5.2-1/get_source/doctest-2.5.2.tar.gz +wrapdb_version = 2.5.2-1 + +[provide] +dependency_names = doctest diff --git a/subprojects/rmlui.wrap b/subprojects/rmlui.wrap new file mode 100644 index 0000000..2d56578 --- /dev/null +++ b/subprojects/rmlui.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = RmlUi-6.2 +source_url = https://github.com/mikke89/RmlUi/archive/refs/tags/6.2.tar.gz +source_filename = RmlUi-6.2.tar.gz +source_hash = 814c3ff7b9666280338d8f0dda85979f5daf028d01c85fc8975431d1e2fd8e8b +method = cmake @@ -5,15 +5,15 @@ ## Now -**Next action:** Slice 1 — install toolchain (`sudo pacman -S meson ninja -cmake ccache`), then bootstrap the Meson skeleton per `notes/plan.md` §5. +**Next action:** Slice 2 — port tinywl into the kernel: backend/output/ +seat/scene glue, run nested under labwc, manage a foot toplevel. ## Slices | # | Slice | Status | Acceptance | |---|---|---|---| | 0 | Harness skeleton | **DONE** 2026-06-12 | All harness md files in place | -| 1 | Bootstrap: toolchain, Meson skeleton, RMLUi subproject compiles, empty kernel links wlroots-0.20 from C++ via the extern-"C" wrapper | pending | `ninja -C build` green; no-op binary runs + exits clean | +| 1 | Bootstrap: toolchain, Meson skeleton, RMLUi subproject compiles, empty kernel links wlroots-0.20 from C++ via the extern-"C" wrapper | **DONE** 2026-06-12 | met: build green; tests 1/1; binary prints wlroots 0.20.1 + RmlUi 6.2, exits 0 | | 2 | tinywl port: kernel skeleton runs nested under labwc | pending | opens as a window; manages a foot toplevel; keyboard/pointer/touch events flow | | 3 | **THE SPIKE:** RMLUi→scene bridge | pending | a hello-world RML document composited as a scene node with damage tracking; go/no-go gate | | 4 | Extension host + contracts: bus, manifests, static registration; xdg-shell/layer-shell refactored OUT of kernel into core extensions | pending | kernel names no feature; ext-xdg-shell + ext-layer-shell pass suite | |
