| Age | Commit message (Collapse) | Author |
|
|
|
The logo already includes the unbox wordmark, so the separate heading
was redundant. Left-align the logo.
|
|
Add the unbox logo to the README header. Replace the stale
planning/harness status with the current state (slices 0-5b done,
stage dock landed). Add an Extensions table, useful CLI flags, and
the asset hot-reload dev workflow.
|
|
Replace the specific builder hostname, username, home path, and the cited
private methodology-source repo with generic wording across ORCHESTRATOR.md,
notes/plan.md, tasks.md, and the PKGBUILD maintainer line. No machine/network
identifiers remain in the tree.
|
|
Transparent distributed compilation: setup-distcc.sh configures ccache
(prefix_command=distcc) + ~/.distcc/hosts locally and a locked-down distccd on a
remote builder, so a plain ninja/meson test offloads compiles automatically and
falls back to local when the remote is down. build-remote.sh (renamed from the
machine-specific build-remote.sh) builds + installs the pacman package on the
fast box. start-unbox now takes its wallpaper from $HOME/.config/unbox/wallpaper
(or $UNBOX_WALLPAPER) instead of a hardcoded filename.
Machine/network specifics (hosts, private IPs) are NOT committed: the helpers
read packaging/remote.local (gitignored); packaging/remote.local.example is the
committed template with setup instructions for a new network.
|
|
RmlUi only starts a transition on a class/definition change, never on the
inline data-style-transform the dock uses for slide, so keyboard/minimize/restore
open-close had stopped animating (snapped). Own the animation in C++ instead: a
pure SlideAnimator that every path flows through -- keyboard/minimize/restore play
it, a touch drag scrubs it (pause + set position from the finger), and release
resumes easing from the current position to the snap target. Duration + easing are
read from the #panel RCSS 'transition' via transition_timing(), so they stay
hot-reloadable and any named RmlUi tween works. Drives slide per frame via
request_frames; the surface now hides when the close animation completes (replaces
the old transitionend path). Body=drag-handle / panel=transform split preserved.
|
|
Two additive primitives for C++-driven, RCSS-tunable animation:
- Host::request_frames(cb) -> FrameRequest: a per-frame callback (RAII handle)
run before tick_all each frame; the kernel schedules frames continuously while
>=1 request is alive and stops at rest. Fills the missing animation timer.
- UiSurface::transition_timing(element_id, property): reads the RCSS-authored
transition duration + easing, returning RmlUi's tween wrapped as a pure
std::function (no RmlUi types cross the contract) so an extension can drive its
own animation with hot-reloadable, designer-tunable timing/easing.
|
|
horizontal-gradient is RmlUi's legacy decorator: it bakes the ramp into
interpolated vertex colours through the plain colour shader, so it bypassed the
gradient dither and showed hard 8-bit alpha banding over light backgrounds.
Switch to linear-gradient(to right, ...), which renders per-pixel in our GL3
gradient shader (where the dither lives), giving a clean fade to transparent.
|
|
The shader-based gradient (linear/radial) fragment shader now adds a
mean-zero triangular-PDF dither (~1 LSB of 8-bit) from a spatial pixel-position
hash before writing finalColor, so smooth ramps (e.g. an alpha fade) no longer
show quantization banding when written to the 8-bit surface buffer. Spatial,
non-temporal — no shimmer on static panels. Applies to every shader gradient.
|
|
Drag from the left edge to open, or drag the open dock back to close;
finger-following with a 50%-or-fling snap on release. New gesture::Controller
pure core converges both input paths onto one RevealRecognizer: OPEN via the
kernel touch bus (dock hidden at down), CLOSE via UiSurface::bind_drag (the
visible dock captures the touch). Slide is value-driven (data-style-transform)
and eased only when not dragging.
Two real-seat fixes found via per-frame logging:
- Flicker: RmlUi projects a drag event's coords into the DRAGGED element's
transformed frame. We had drag:drag on the same <body> we translate by
slide, so the reported x fed back into slide and ping-ponged every frame.
Fix: body is a stationary drag handle; an inner .panel carries the transform.
- Direction: drag_start now seeds the recognizer from the dock's current
fraction (1 + slide/width) instead of a hardcoded value, so a drag opens or
closes correctly from any state.
|
|
Forwards RmlUi Dragstart/Drag/Dragend for a named callback as DragPhase
{start,move,end} with surface-local x/y, so an extension can drive an
interactive drag from a captured ui-surface touch (the touch bus never
sees it). Mirrors bind_event's error-isolation + hot-reload handling.
|
|
- host-bin: install the `unbox` binary (install: true) so `meson install`
(and the package) ship /usr/bin/unbox. Assets already install via the
top-level install_subdir to /usr/share/unbox.
- ext-keybindings: force the toml++ subproject to default_library=static.
Its wrapped meson.build hardcodes default_library=shared, which made the
installed binary NEED libtomlplusplus.so.3 at runtime (only resolvable in
the dev tree via LD_LIBRARY_PATH). Static-linking bakes it in.
- packaging/PKGBUILD: builds the working tree (reuses synced subprojects, no
network), plain meson (no devtools needed), options=(!debug), and ALWAYS
configures a fresh build dir — a reused/stale dir silently drops subproject
option overrides (this is what reintroduced the shared-toml regression).
- packaging/start-unbox: dbus-run-session -- unbox (mirrors start-labwc.sh).
- packaging/build-remote.sh: rsync the tree to the fast box (builder),
makepkg there, ferry the .pkg.tar.zst back, pacman -U here. Self-verifies
the packaged + installed binary has no unresolved/shared-toml dependency
(extracting to a real file — readelf can't read a non-seekable pipe).
Runtime NEEDED is now only system libs (wlroots-0.20, wayland, xkbcommon,
freetype, EGL, GLESv2, libstdc++/m/gcc_s/c) — installs + runs on the CF-AX3.
|
|
|
|
- **ext-stage-dock**: exports a `Service` interface with `toggle_visible()`.
The extension inherits from it and registers via `provide_service` in
`activate()`. The method slides the dock in/out (using the existing RCSS
transition) regardless of slot count; showing an empty dock is valid.
- **ext-keybindings**: new `Action::dock_toggle_visible` action, mapped from
`"dock-toggle-visible"` in `unbox.toml [[keybind]]`, dispatched to the
stage-dock Service. Default binding: `Super+d`.
- **Manifest**: ext-keybindings now `depends_on {"xdg-shell", "stage-dock"}`.
- **Build**: subdir order swapped so ext-stage-dock builds before
ext-keybindings; `ext_stage_dock_dep` is a link-time dep of the
ext-keybindings library and transitively exposed via `ext_keybindings_dep`.
- **Tests**: glue tests install ext-stage-dock alongside ext-xdg-shell;
policy test expects 6 default bindings. All 10/10 green on build +
build-asan.
Configure in unbox.toml:
[[keybind]]
keys = "Super+d"
action = "dock-toggle-visible"
|
|
Turn the card-hugging dock into a full-height left rail.
- C++ (extension.cpp/dock_layout.hpp): the surface is now the full OUTPUT HEIGHT
(hug reverted; content_height helpers/tests dropped) and kDockWidth widened
240 -> 288 (~20%). Cards unchanged at 224dp. set_visible still hides the rail
when empty, so it only appears when there are minimized windows.
- RCSS (dock.rcss): body.dock fills the surface (width:100% height:100%) as a flex
COLUMN scroll container with a `horizontal-gradient(#000000ff #00000000)`
decorator — fully-opaque black at the left screen edge fading to transparent
across the wider rail. Cards centered both axes (align-items:center + a
`div.rail` margin:auto wrapper that vertically centers when they fit and
collapses gracefully — no flex-center+overflow strand — scrolling when they
overflow). Scrollbars hidden (scrollbarvertical/horizontal size 0).
- RML (dock.rml): a `div.rail` wrapper around the data-for slot list enables the
center-or-scroll pattern.
Accepted caveat (quick path): the full-height 288px surface consumes pointer/touch
across the left strip while shown; the deferred input-transparent UiSurfaceSpec
flag is the real fix. Real-seat verified via live RML/RCSS hot-reload.
ext-stage-dock 2/2 green on build + build-asan.
|
|
.rml basename)
The watch_file refactor (35e5d32) moved the substrate's UI-asset hot-reload onto
the shared FileWatcher but armed a BASENAME watch on the document's .rml file only.
The dock's styling lives in a separately-<link>ed dock.rcss, so editing it (the
common case) never matched the watch — asset hot-reload silently stopped working on
the real seat (no "dev hot-reload ON" line, no reload on save), while config
watching kept working. The ui_reload_surface() seam test passed because it bypassed
the real inotify->reload path.
Fix: FileWatcher::add_dir watches the document's whole DIRECTORY (so any .rml/.rcss
in it triggers the surface reload); the substrate uses it and restores the
"dev hot-reload ON (inotify watching asset dir '...')" log. Added an END-TO-END
test mirroring the dock (a doc that <link>s a separate .rcss, real inotify event,
wl_event_loop pumped, assert the document actually reloaded) — fails on the buggy
code, passes now; no more relying on the seam.
Real-seat verified: editing dock.rcss now reloads the live dock (border-radius +
background-color changes apply on save). kernel 59 cases/260 assertions green on
build + build-asan, no new suppressions. Edits confined to packages/kernel/.
|
|
|
|
|
|
Editing the config now re-applies keybindings live, via the kernel's watch_file
service. In activate() we watch the effective config path (the create() arg, else
~/.config/unbox/unbox.toml) — even if it doesn't exist yet, so creating it later is
picked up — holding the FileWatch as a member. On change, reload_config() re-reads
+ re-parses (the existing pure toml core) and SWAPs the live binding table the
key_filter link matches against (matcher_), so new bindings apply with no
re-subscribe. A malformed / unreadable / mid-edit-broken file KEEPS the current
bindings and logs one warning — the session never loses working keys, never throws.
Real-seat verified: editing the command (fuzzel->foot) logged "config reloaded
(5 binding(s))" live; a deliberately broken file logged "reload failed; keeping
current bindings" with the session staying ALIVE; restoring it recovered. Added a
pure reload-semantics doctest (A->B swap; malformed keeps prior). ext-keybindings
2/2 green on build + build-asan. Edits confined to packages/ext-keybindings/.
|
|
The hot-reload watcher was substrate-internal; expose it as a typed RAII primitive
any extension can use (config hot-reload is the first consumer), per "the kernel
owns the event/service bus; extensions never hold raw event-loop glue".
- New public watch.hpp: `class FileWatch` (move-only RAII; ~/reset() stop the
watch) + `Host::watch_file(path, on_change) -> FileWatch`. on_change fires on the
event-loop thread, COALESCED (one save = one call), EDITOR-SAFE (dir-watch the
basename across temp+rename), fires on CREATE of a not-yet-existing file, and is
ERROR-ISOLATED to the calling extension (carries its id; a throw disables only
that extension). UNGATED — works without UNBOX_DEV.
- New src/file_watcher.{hpp,cpp}: ONE session-wide inotify instance on the
wl_event_loop multiplexing all watched paths. The substrate's UI-asset hot-reload
was refactored onto it (no second inotify); only the substrate's *decision* to
watch UI assets stays UNBOX_DEV-gated. Created lazily on first watch; torn down
leak-clean before the loop dies.
host.hpp/kernel.md documented. kernel 58 cases/254 assertions green on build +
build-asan (incl. the inotify path), no new suppressions. Edits confined to
packages/kernel/.
|
|
|
|
(hot-reloadable)
- The dock's inline kDockRml C++ string is gone; the document now lives in
assets/ext-stage-dock/dock.rml (structure) + dock.rcss (styles), loaded via
UiSurfaceSpec::rml_path = "ext-stage-dock/dock.rml". The bind_list*/event setup
in activate() is unchanged (the substrate re-applies it across hot-reloads).
- Build wiring (top-level meson.build): install_subdir the top-level assets/ tree
to <datadir>/unbox/<unit>/, and -DUNBOX_ASSET_DIR_DEFAULT=<prefix>/<datadir>/unbox
so an installed unbox finds its assets with no env. Dev runs set
UNBOX_ASSET_DIR=<repo>/assets + UNBOX_DEV=1 to read the source tree and arm the
hot-reload watcher.
Real-seat verified: editing dock.rcss (border-radius 10dp<->70dp) updates the live
dock with NO recompile and NO restart. ext-stage-dock 2/2 green on build +
build-asan. Design iteration on the dock is now edit-a-file.
|
|
Externalize UI documents so RML/RCSS design changes need no C++ recompile — and,
in dev, no restart.
- UiSurfaceSpec::rml_path now actually loads the document from a file (path wins
over rml_inline, as documented). Resolution: absolute path as-is; relative path
against $UNBOX_ASSET_DIR, else the compile-time UNBOX_ASSET_DIR_DEFAULT (the
install data dir), else cwd. The document URL is set so its <link> RCSS / asset
refs resolve relative to the doc's own dir. Missing/unreadable file -> nullptr
(degrade, never throw).
- Dev hot-reload (gated by $UNBOX_DEV): an inotify watcher integrated into the
wl_event_loop (never blocks) watches the asset DIRS (dir-watch for IN_CLOSE_WRITE
/ IN_MOVED_TO, since editors save via temp+rename), coalesces events, and on a
change to a surface's backing .rml/.rcss reloads the document IN PLACE:
ClearStyleSheetCache + UnloadDocument + reload, preserving the surface's RmlUi
context, data model and the extension's registered bind_*/bind_list* getters
(the extension does NOT re-register), and its geometry/visibility; preview
textures are kept. A malformed file on reload is ERROR-ISOLATED — the previous
good document keeps rendering, one warning is logged, and a later good save
recovers; the session never dies.
- Test seam Server::ui_reload_surface() drives reload deterministically.
ui.hpp documents rml_path + the dev hot-reload behavior. kernel 54 cases/232
assertions green on build + build-asan (incl. the UNBOX_DEV inotify path), no new
suppressions. Edits confined to packages/kernel/.
|
|
sliver)
Real-seat pixel sampling showed a ~2px vertical sliver of the slot placeholder
(#2e2e32) on the card's RIGHT edge only (full height; other edges flush). A magenta
diagnostic background on the thumb proved the gap was NOT a transparent texture edge
(it stayed placeholder-colored, not magenta) — the thumb BOX was ~2px short on the
right (RmlUi box rounding anchored top-left). The preview texture import is correct.
Fix: the full-bleed thumb now overscans the slot by -2dp on all sides; the rounded
overflow:hidden slot clips the overscan, so the image covers the whole rounded card
with no placeholder edge on any side and corners stay rounded. RCSS-only.
Verified real-seat: image reaches all four edges, zero placeholder sliver.
ext-stage-dock 2/2 green on build + build-asan. Also records the transparency/
card-redesign milestone in tasks.md.
|
|
Replaces the panel-with-inset-image card with the window preview as the card
itself, rounded on all four corners.
- div.slot is a rounded overflow:hidden clip container (explicit 224x140dp box,
#2e2e32 rounded placeholder for not-yet-rendered previews).
- A full-bleed child <div class="thumb"> carries the preview via
data-style-decorator="'image( ' + row.preview + ' )'" with `cover center
center` (shorthand order verified against vendored RmlUi DecoratorTiled). As a
child of the rounded overflow:hidden slot, its image clips to the rounded
corners — RmlUi does NOT clip an element's own decorator to its own
border-radius, so the decorator must live on the clipped child.
- Title overlay disabled (display:none) but kept in the markup + the
{{ row.title }} binding/getter stay live, for a later text redesign.
- Card height 140 (kCardHeight); surface-hug content heights 156/304/600 updated.
- Preserved: d1 slot-enter animation, transparent strip, surface hugs the stack.
Real-seat verified: a clean rounded window thumbnail in the dock. ext-stage-dock
2/2 green on build + build-asan.
|
|
The stage dock's rounded image cards were the first thing to exercise the
substrate's RmlUi clipping path (the slice-3 spike doc never used overflow /
border-radius). Investigated on report of square corners: the renderer's
clipping is CORRECT — added 4 headless+gles2 cases proving it (shm + dmabuf,
asymmetric so a flipped scissor Y would fail):
- overflow:hidden parent clips an oversized child (outside bands transparent);
- border-radius circle clip-mask rounds the corners (corners transparent);
- an image() decorator on a CHILD of a rounded overflow:hidden card clips to the
rounded shape (the dock's correct structure);
- a transformed rounded clip survives a set_size grow (the dock's real path).
The square-corner bug was RmlUi-core behavior — an element's own image()
decorator is not clipped to its own border-radius (a background-color rounds by
geometry; a decorator needs the clip mask, which self-render never sets) — fixed
in the dock by moving the decorator to a child. No renderer/substrate source
change. kernel 49 cases/208 assertions green on build + build-asan.
|
|
defer in plan §7
|
|
|
|
empty
Builds on the kernel per-pixel-alpha + set_size-resize capabilities.
- Transparent strip: body.dock background #1c1c1ee6 -> transparent, so the
windows beneath show through everywhere the cards don't cover; cards keep their
solid #2e2e32 panel. data-attr-src preview, Noto Sans, and the d1 slot-enter
animation are intact.
- Surface hugs the card stack: height = surface_height(count) =
max(1, 2*pad + count*card + (count-1)*gap) (0->1px hidden, 1->140, 2->272, …),
never the full output height, so the transparent area doesn't needlessly
capture input. The empty dock is a positive 1px hidden placeholder (the substrate
rejects 0 geometry); grows/shrinks via set_size on minimize/restore.
- Fix: re-minimize after the dock empties was a no-op. do_restore relied on
on_toplevel_focused re-firing, but a restored window was never seat-defocused,
so focus() is a seat no-op and the event never fires — leaving focused_ stale,
so the next Super+M's focused_ guard rejected it (a new toplevel mapping
unstuck it). Fix: set focused_ = tl directly in restore. No kernel change.
Tests: new policy cases (surface_height always positive; hug heights 0/140/272/536)
and a glue minimize->restore->minimize 1->0->1 cycle with has_focused() probe.
ext-stage-dock 2/2 green on build + build-asan (no sanitizer reports). Real-seat
verified: transparent strip, dock visible, cards float, re-minimize works.
Edits confined to packages/ext-stage-dock/.
|
|
Two substrate capabilities the stage dock forced (both verified real-seat nested
and on the gles2 headless path):
1. Per-pixel alpha. A ui surface composited opaque, so any overlay (the dock)
occluded the toplevels beneath it. Root cause: a stray opaque
render_iface->Clear() (glClearColor 0,0,0,1) in render_surface overrode the
transparent BeginFrame clear, and EndFrame's premultiplied composite carried
the opaque base to the buffer. Fix: drop the stray Clear(); clear the OUTPUT
FBO to (0,0,0,0) once before BeginFrame. Blend was already correct
premultiplied; the substrate never sets an opaque region (now guarded by a
probe); ARGB8888 alpha survives end to end. A document whose <body> is
transparent now shows the scene through its un-painted pixels.
2. set_size resizes the render target. Previously logical-only (the slice-5
documented change-request): set_size re-laid-out the RmlUi document but did
NOT realloc the GL target, so a surface created small and grown rendered into
its original buffer (the dock, created as a 1px placeholder and grown on
minimize, was invisible). Fix: set_size now reallocs the FBO + dmabuf
swapchain/shm + EGLImage + texture + scene buffer on an ACTUAL size change
(no-op same-size, cheap; set_position still cheap). Grow and shrink both
render fully; alpha/upright-flip/blend/fence-sync preserved.
ui.hpp documents both. kernel 45 cases/182 assertions green on build + build-asan
(no new suppressions). Edits confined to packages/kernel/.
|
|
|
|
Real-seat (nested) showed the dock compositing but slots blank. Three bugs in the
inline dock document, all caught in the live RmlUi log:
- <img src="{{ row.preview }}"> never bound — RmlUi interpolates {{ }} only in
TEXT, not attributes, so it tried to load a texture literally named
"{{ row.preview }}". Fixed with data-attr-src="row.preview".
- font-family: sans-serif -> "No font face defined" (substrate only loads Noto
Sans). Fixed to the loaded face.
- transform-origin: top left -> RCSS parse error. Fixed to RmlUi-valid syntax.
Verified real-seat: minimizing two foot windows shows two preview cards in the
dock with the actual window snapshots visible; the live log is free of the three
[rmlui] errors. Super+M works repeatedly with >1 window ("works once" was the
single-window no-op: nothing left to focus/minimize). Suites green build + asan.
|
|
|
|
Layers animation on c2's static pipeline with zero data-model / contract change
(c2 glue test passes unchanged). The dock body slides in from the left edge on
empty->non-empty via an RCSS transition on transform:translateX driven by a bound
`open` bool (data-class-open); slide-out defers set_visible(false) until the
slide's transitionend so the close is seen. Each freshly instanced preview card
plays a one-shot @keyframes (scale 0.72->1.0 + fade, transform-origin top-left =
"scales down into a spot"). Dock stays a 240px left-edge strip (animation is a
transform inside the surface, never a resize) so windows stay clickable.
Key finding: RmlUi 6.2 dispatches animationend/transitionend as real events, and
the substrate's data-event-* path binds a listener for ANY registered event — so
data-event-transitionend routes into bind_event with NO kernel change. The ONLY
remaining gap for the literal cross-screen window->dock flight is a single
input-transparent UiSurfaceSpec flag (a kernel boundary decision, NOT built).
Restore stays instant in d1 (animated grow-back is e1's drag-out).
10/10 suites green on build + build-asan (asan clean). Visual feel is real-seat.
|
|
ext-stage-dock activate() now wires the full static pipeline: track toplevels via
the ext-xdg-shell Service; Super+M (stopgap keybinding) minimizes the focused
window -> snapshot a Preview from its scene_tree(), push a slot, hide() the live
node, refocus another window; the dock is one overlay UiSurface rendering the
slots via the b2 list bindings (data-for over {preview src, title}); tapping a
slot fires bind_list_event -> show()+focus() the window and drop its slot/Preview.
Storing the Toplevel* across minimize is safe because hide() keeps it mapped;
slots are dropped on on_toplevel_unmapped. Teardown is reverse-declaration-order
(subscriptions first, surface before slots), asan-clean.
host-bin installs ext-stage-dock (standard, depends_on xdg-shell; hidden until it
holds a minimized window). Headless glue test (real in-process xdg client) proves
the model + scene-node enable bit true->false->true, slots 0->1->0. RML carries
dock/slot classes as d1's RCSS animation hooks. Stopgaps: Super+M (to migrate into
an ext-keybindings action + a stage-dock Service post-d1); favicon deferred
(needs an XDG icon-theme dep — to surface to the user).
10/10 suites green on build + build-asan. Visual/tap path is real-seat (pending).
|
|
New standard extension (id "stage-dock", depends_on xdg-shell). Ships the unit
skeleton (factory-only public header, minimal no-op activate, meson + suite) and
the two PURE DECISION CORES, doctest-hard with no kernel/wlroots:
- src/reveal.hpp: reversible left-edge swipe recognizer (stream -> reveal fraction
[0,1]; release -> open/close commit by threshold + fling velocity).
- src/dock_layout.hpp: reveal-fraction -> on-screen dock box (slides -dock_width
.. 0) + slot capacity / content-height / per-slot rect math.
Real wiring (RML doc, snapshot, minimize/restore) lands in c2/d1/e1. Root
meson.build gains the subdir; host-bin registration deferred to c2.
ext-stage-dock suite green (17 cases / 74 assertions). Edits in packages/ext-stage-dock/ + root meson.build subdir.
|
|
Neutral compositor-side mechanism the stage dock drives to minimize a window
(the "minimized" state/policy stays in ext-stage-dock). Adds to Toplevel:
geometry() -> wlr_box (size the preview + restore position), scene_tree() ->
wlr_scene_tree* (feed UiSubstrate::create_preview + the node hide/show toggles),
and hide()/show() — disable/enable the scene node so the client stops compositing
and frame callbacks WITHOUT unmapping (no on_toplevel_unmapped, no focus change),
idempotent. Verified against wlroots 0.20 that a disabled node quiesces frames.
A real in-process wayland client test (test_minimize.cpp) drives it on the
headless backend.
ext-xdg-shell suite green on build + build-asan. Edits confined to packages/ext-xdg-shell/.
|
|
The stage dock is one RML document rendering a variable list of slots (one per
minimized window). Adds the deferred slice-6 list-binding shape to UiSurface:
bind_list(name, count) + typed per-row fields bind_list_string/int/double/bool
(list, field, getter(row)) read as {{ row.field }} via data-for, and
bind_list_event(list, event, callback(row)) routed from data-event-*(it_index).
dirty(<list>) re-reads count + visible rows. Same error-isolation + bind-before-
first-frame contract as the scalar bindings; nested lists unsupported.
kernel suite green on build + build-asan (asan clean). Edits confined to packages/kernel/.
|
|
The keystone for the stage dock. Proves Fork B on the real target (Mesa crocus,
HD 4400): a toplevel's pixels, rendered by the wlr GLES2 renderer into a LINEAR
ARGB8888 dmabuf, import as an EGLImage -> sampled GL texture in the sibling RMLUi
GLES 3.2 context (the slice-3 bridge run in reverse) and composite into an
<img src="unbox-preview://N"> inside a ui surface — upright, color-correct.
Public surface (ui.hpp): class Preview (source_uri/source_width/source_height/
refresh) + UiSubstrate::create_preview(wlr_scene_tree*) -> unique_ptr<Preview>
(nullptr if no GL path; never throws). Kernel-suite probes: ui_preview_import_is_dmabuf,
ui_pixel(x,y). Clean four-resource teardown (URI reg, GL texture, EGLImage, dmabuf);
refresh-after-source-destruction is UB (consumer drops Preview on unmap).
kernel 42 cases/150 assertions green on build + build-asan (asan clean, no new
suppressions). Edits confined to packages/kernel/.
|
|
Slice 10 (stage dock): Stage-Manager-style left-edge dock of minimized-window
previews revealed by a left-edge swipe. Records the Fork-B decision (previews are
toplevel snapshots imported as textures into the RMLUi context, shown as <img> in
one RML document) and the mechanism/policy split (kernel: snapshot + list bindings
+ gesture-claim; ext-xdg-shell: hide/show/geometry/scene_tree; ext-stage-dock:
minimized set, layout, recognition, easing). Adds canonical terms: stage dock,
stage (reserved), preview, favicon, gesture, swipe, minimize, restore, reveal.
|
|
|
|
Intercept the XF86Switch_VT_1..12 keysyms before the keybinding filter and call
wlr_session_change_vt, so the user can always switch consoles while unbox runs.
Clean no-op without a session (headless/nested). Pure vt_for_keysym helper +
doctest; wlroots reached via the wlr.hpp wrapper.
Real-seat verified on the CF-AX3.
|
|
exports WAYLAND_DISPLAY
ext-keybindings (new core ext) reads unbox.toml: tap-Super spawns fuzzel,
Alt+Tab/Alt+Shift+Tab rotate focus across all toplevels, plus Alt+F1 and
Ctrl+Alt+Backspace (quit). ext-xdg-shell's hardcoded keybinds removed
(migrated to the toml).
Kernel setenv()s WAYLAND_DISPLAY at startup so extension-spawned clients
connect to unbox, not the launching session — fixes fuzzel "no monitors"
on the real seat.
build + build-asan green: third-party Mesa/EGL/DRM + vendored-RmlUi sanitizer
noise suppressed (suppressions/), our code stays leak-checked; a real
libwayland leak in the layer-shell client test fixed.
Harness: spawn-env + sanitizer-noise rules, diagnose-real-seat skill,
GLOSSARY keybinding/action/tap-binding. Real-seat verified on the CF-AX3.
|
|
The ui substrate is now the extension-facing contract (unbox/kernel/ui.hpp):
Host::ui() -> UiSubstrate::create_surface(spec) -> UiSurface with typed
scalar bindings (int/double/bool/string getters), data-event callbacks
(error-isolated per extension), dirty(), geometry/visibility — RMLUi and
GL stay kernel-private. Production sync: glFinish replaced by
EGL_KHR_fence_sync + 2-deep wlr_swapchain. ui_spike retired (orientation
guard + dirty-cycle coverage live on as substrate tests).
Input: ONE kernel routing path feeds pointer AND touch into ui surfaces
with consume-or-pass semantics and implicit-grab ownership (the consumer
of a press owns the matching release; per touch point too) — fixes
drag-release-over-ui sticking. touch-mode: state machine + debounce +
on_touch_mode_changed notification, NO visual scaling (user decision
after hardware hands-on; dp-ratio stays 1.0; see plan §2).
ext-xdg-shell: GrabMachine generalized to pointer-OR-touch interaction
source (touch titlebar drag works; originating-point pinning); fixed the
seat implicit-grab leak (suppressed release after forwarded press
swallowed all later touch-downs — pointer/touch alternation doctested);
factory renamed create(). ext-layer-shell: on_demand keyboard
interactivity via scene hit resolution. host-bin: --ui-demo extension
(temporary acceptance demo on the public contract, dies in slice 6).
User hands-on verified: same surface by mouse and finger, tap counter,
touch-mode neutrality, no click-through, drag alternation, fuzzel
on_demand. 113 doctest cases green, ASan/UBSan clean (our code), idle
RSS ≈78 MiB. Harness: UX-feel hands-on lesson (ORCHESTRATOR §2.6),
nested-run pkill/setsid notes, touch-mode glossary redefinition.
|
|
extensions
The kernel now names NO concrete feature. It owns: the extension host
(install/topological activate, missing-dep/cycle = startup error), the
typed Event/Filter bus (error-isolated: a throwing extension is disabled,
never the session; RAII Subscriptions), the Host API (per-extension
facade: borrows, scene layers, event catalogue, typed services), the
public RAII Listener, and a typed surface→scene-tree registry
(Host::host_surface/scene_tree_for) that replaced the untyped
wlr_surface.data convention both extensions flagged.
- ext-xdg-shell (core): toplevel/popup lifecycle, focus-on-map,
click/tap-to-focus, pointer/touch routing incl. button+axis (the
kernel only moves the cursor and emits — a contract-doc lie caught by
user hands-on), interactive move/resize via pure GrabMachine (fixes
the request-arrives-after-release race: grab requires request ∧
button-down, release always ends it), Alt+F1 cycle,
Ctrl+Alt+Backspace terminate (labwc's default A-Escape=Exit killed
the dev session once — never again; see nested-run skill).
- ext-layer-shell (core): wlr-layer-shell v1 (proto v5) for external
clients; pure doctest-hard arrangement core; fuzzel verified visually
nested (fix: seed outputs from output_layout at activate — events-only
tracking missed pre-activation outputs; plus a scene-node double-free).
- First protocol codegen: vendored wlr-layer-shell XML + wayland-scanner
server-header propagated through kernel_dep; wlr.hpp grew a
namespace→_namespace keyword fix for the generated header.
- Glossary: 'scene layer' (user-approved). New rules earned:
parallel-wave-builds, contract-docs.
- User hands-on verified: typing, click-to-focus, drag-select, scroll,
titlebar drag-move (slow + flick), Alt+F1, fuzzel + arrows, touch tap,
Ctrl+Alt+Backspace. 68 doctest cases green, ASan/UBSan clean (our
code), idle RSS ≈73 MiB.
|
|
|
|
Plan A verified on hardware (HD 4400/crocus): RMLUi renders into a GLES
3.2 sibling-context FBO backed by a dmabuf wlr_buffer (wlr_allocator +
EGLImage import), composited as a wlr_scene_buffer with per-frame damage.
Plan B (glReadPixels→shm) implemented and verified as runtime fallback;
auto-engages when any Plan-A precondition fails. Plan C not needed.
- Hello-world RML doc: text, data-bound frame counter, pointer input
proof (hover/:active) — verified upright on screen via screenshot after
fixing the classic FBO Y-flip (buffer-level V-flip keeps display ==
document coords for input); position-aware orientation guard added.
- Temporary spike surface: Options::ui_spike + frame-count/orientation
probes, host-bin --ui-spike flag; replaced by the real ui substrate
contract in slice 4+.
- kernel suite 6 cases / 416 assertions green; ASan/UBSan clean in our
code (Mesa leak noise + 2 benign UBSan downcast reports inside vendored
RMLUi are known); idle RSS ≈83 MiB.
- Deferred (notes/plan.md §7): glFinish→EGL fence + swapchain; dmabuf
render-format negotiation (private API in wlroots 0.20).
|
|
touch added
Server contract (pimpl, create/run/dispatch/terminate) over a faithful
tinywl 0.20.1 port: outputs via wlr_scene, xdg-shell toplevels+popups,
focus, interactive move/resize, keyboard/pointer through wlr_cursor — plus
touch (down/up/motion/cancel/frame via seat notifies with per-point origin
tracking), which tinywl lacks. RAII Listener replaces manual wl_list_remove
bookkeeping; shutdown ordering documented in kernel.md. xkbcommon added as
a system dep. Verified: nested under labwc (output WL-1, foot mapped and
focused on GLES2) and a headless+pixman boot test in the kernel suite.
|
|
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.
|
|
Constitution (AGENTS.md), orchestrator workflow with the header-contract
read rule and harness-growth duties (ORCHESTRATOR.md), wlroots-seeded
glossary, 4 safety reflexes, owner-agent briefs, 3 skills, and the living
plan (notes/plan.md) with hardware-verified facts, settled decisions, and
the slice 1-9 roadmap (tasks.md). Slice 0 done; next: toolchain bootstrap.
|