Omarchy on Kandelo — the desktop shell: layer-shell bar, launcher, themes - #1241
Open
mho22 wants to merge 14 commits into
Open
Omarchy on Kandelo — the desktop shell: layer-shell bar, launcher, themes#1241mho22 wants to merge 14 commits into
mho22 wants to merge 14 commits into
Conversation
Contributor
Phase B-1 matrix build status —
|
| Package | Arch | Status | Sha |
|---|---|---|---|
| alsa-lib | wasm32 | failed | — |
| atk | wasm32 | failed | — |
| atkmm | wasm32 | failed | — |
| basu | wasm32 | failed | — |
| cairo | wasm32 | failed | — |
| cairomm | wasm32 | failed | — |
| expat | wasm32 | failed | — |
| fcft | wasm32 | failed | — |
| fmt | wasm32 | failed | — |
| fontconfig | wasm32 | failed | — |
| freetype | wasm32 | failed | — |
| fribidi | wasm32 | failed | — |
| gdk-pixbuf | wasm32 | failed | — |
| glib | wasm32 | failed | — |
| glibmm | wasm32 | failed | — |
| gtk-layer-shell | wasm32 | failed | — |
| gtk3 | wasm32 | failed | — |
| gtkmm3 | wasm32 | failed | — |
| harfbuzz | wasm32 | failed | — |
| jsoncpp | wasm32 | failed | — |
| libcurl | wasm32 | failed | — |
| libcxx | wasm32 | failed | — |
| libcxx | wasm64 | failed | — |
| libdrm | wasm32 | failed | — |
| libepoxy | wasm32 | failed | — |
| libevdev | wasm32 | failed | — |
| libffi | wasm32 | failed | — |
| libinput-lite | wasm32 | failed | — |
| libinput | wasm32 | failed | — |
| libpng | wasm32 | failed | — |
| libsigcxx | wasm32 | failed | — |
| libudev | wasm32 | failed | — |
| libwayland | wasm32 | failed | — |
| libxkbcommon | wasm32 | failed | — |
| libxkbregistry | wasm32 | failed | — |
| libxml2 | wasm32 | failed | — |
| libxml2 | wasm64 | failed | — |
| mtdev | wasm32 | failed | — |
| openssl | wasm32 | failed | — |
| openssl | wasm64 | failed | — |
| pango | wasm32 | failed | — |
| pangomm | wasm32 | failed | — |
| pcre2 | wasm32 | failed | — |
| pixman | wasm32 | failed | — |
| sdl2 | wasm32 | failed | — |
| spdlog | wasm32 | failed | — |
| sqlite | wasm32 | failed | — |
| sqlite | wasm64 | failed | — |
| tllist | wasm32 | failed | — |
| utf8proc | wasm32 | failed | — |
| zlib | wasm32 | failed | — |
| zlib | wasm64 | failed | — |
| bc | wasm32 | failed | — |
| bzip2 | wasm32 | failed | — |
| coreutils | wasm32 | failed | — |
| curl | wasm32 | failed | — |
| dash | wasm32 | failed | — |
| dbus | wasm32 | failed | — |
| diffutils | wasm32 | failed | — |
| dinit | wasm32 | failed | — |
| fbdoom | wasm32 | failed | — |
| file | wasm32 | failed | — |
| findutils | wasm32 | failed | — |
| foot | wasm32 | failed | — |
| gawk | wasm32 | failed | — |
| git | wasm32 | failed | — |
| grep | wasm32 | failed | — |
| gzip | wasm32 | failed | — |
| kandelo-sdk | wasm32 | failed | — |
| kernel | wasm32 | failed | — |
| less | wasm32 | failed | — |
| lsof | wasm32 | failed | — |
| m4 | wasm32 | failed | — |
| make | wasm32 | failed | — |
| mako | wasm32 | failed | — |
| mariadb | wasm32 | failed | — |
| mariadb | wasm64 | failed | — |
| msmtpd | wasm32 | failed | — |
| nano | wasm32 | failed | — |
| ncurses | wasm32 | failed | — |
| netcat | wasm32 | failed | — |
| nginx | wasm32 | failed | — |
| php | wasm32 | failed | — |
| posix-utils-lite | wasm32 | failed | — |
| sed | wasm32 | failed | — |
| spidermonkey | wasm32 | failed | — |
| tar | wasm32 | failed | — |
| tcl | wasm32 | failed | — |
| unzip | wasm32 | failed | — |
| userspace | wasm32 | failed | — |
| vim | wasm32 | failed | — |
| waybar | wasm32 | failed | — |
| wget | wasm32 | failed | — |
| xz | wasm32 | failed | — |
| zip | wasm32 | failed | — |
| zstd | wasm32 | failed | — |
| bash | wasm32 | failed | — |
| mariadb-test | wasm32 | failed | — |
| mariadb-vfs | wasm32 | failed | — |
| mariadb-vfs | wasm64 | failed | — |
| nethack | wasm32 | failed | — |
| node | wasm32 | failed | — |
| spidermonkey-node | wasm32 | failed | — |
| vim-browser-bundle | wasm32 | failed | — |
| nethack-browser-bundle | wasm32 | failed | — |
| rootfs | wasm32 | failed | — |
| shell | wasm32 | failed | — |
| lamp | wasm32 | failed | — |
| node-vfs | wasm32 | failed | — |
| wordpress | wasm32 | failed | — |
Auto-generated; replaced on each push. Raw data in the publish-status workflow artifact.
mho22
force-pushed
the
explore-dri-hyprland
branch
from
August 21, 2026 10:03
3104fbc to
66c47da
Compare
mho22
force-pushed
the
explore-dri-omarchy
branch
from
August 21, 2026 10:03
29d653f to
272be07
Compare
mho22
marked this pull request as ready for review
August 21, 2026 14:17
mho22
force-pushed
the
explore-dri-hyprland
branch
from
August 24, 2026 19:08
66c47da to
050f429
Compare
mho22
force-pushed
the
explore-dri-omarchy
branch
from
August 24, 2026 19:09
7ee2af2 to
43031c2
Compare
mho22
force-pushed
the
explore-dri-hyprland
branch
from
August 26, 2026 14:37
050f429 to
df918dd
Compare
mho22
force-pushed
the
explore-dri-omarchy
branch
from
August 26, 2026 14:37
43031c2 to
ab7ab2b
Compare
mho22
changed the base branch from
explore-dri-hyprland
to
explore-dri-toolkit
August 26, 2026 14:38
This was referenced Aug 26, 2026
mho22
force-pushed
the
explore-dri-toolkit
branch
from
August 28, 2026 20:15
267319d to
55eea05
Compare
mho22
force-pushed
the
explore-dri-omarchy
branch
from
August 28, 2026 20:15
ab7ab2b to
ccc7c62
Compare
Three things a desktop has that a window manager does not. wlr-layer-shell is the protocol a bar or a launcher uses to own a strip of the output instead of a window: it anchors to an edge, reserves an exclusive zone the tiler then works around, and picks its own keyboard interactivity. kbar and klauncher are the first two clients of it, and the theme engine is what lets the compositor and its own clients share one palette.
`/?demo=omarchy` is the imitation the stack is aimed at: six themes with their own wallpapers, an Omarchy-style menu, real applications in the launcher, and knotify toasts with a bar uptime module.
A client that paces itself needs to know when its frame actually reached the screen, which is what wp_presentation reports. The keymap also grows the F-keys and the navigation cluster. Browser input maps a typed letter through `KeyboardEvent.key` rather than physical position: the guest applies its own keymap, so sending position would apply a layout twice and a non-QWERTY user would type the wrong letter.
Unmodified clients bind more of wl_compositor than in-tree ones did: subsurfaces to compose their own decorations, a data-device to satisfy clipboard setup, seat version 5 for axis events, and `wl_surface.enter` so a client learns which output it is on.
The point the whole toolkit port was for: the bar is Waybar, the notification daemon is mako, the terminal is foot, and none of them were changed to run here. Getting there needed the protocols they assume — xdg-output for output naming, viewporter and fractional-scale for a scaled surface — and a Hyprland-compatible IPC socket, because Waybar's workspace module is written against hyprctl's event stream. foot's fonts are staged into the image while it is composed.
… GL loss A desktop that boots into windows it opened for itself is a demo; this one boots to a wallpaper and a bar, and the user opens what they want. The KMS presenter also rebuilds after a GL context loss instead of staying dead, restoring the pixel-store state it inherited rather than assuming defaults.
The compositor and its clients become one package with six outputs rather than loose build products, so the browser demo resolves them the way it resolves everything else. A surface with no xdg_toplevel role is not a window and must not be tiled; the klauncher exec column truncates instead of overwriting the name beside it.
On a HiDPI output every surface has a logical size and a device size, and drawing at the logical one gives a soft desktop. The compositor now renders at device resolution and tells each surface the scale it is being composited at, so libkwl clients, the gradient wallpaper and the staged image wallpapers all rasterise at full resolution. A layer surface renegotiates its size when the output changes rather than keeping the box it was first given, and a surface learns its output when it takes a role, which is what a client needs before it can pick a buffer scale.
The desktop's packages have to be placed in the source-build roster, and the SpiderMonkey darwin host link has to find libiconv — a host build that fails takes the whole graph with it even though the package is unrelated to the desktop.
`g.zorder` is the window stack. `retile()` walks it and overwrites each member's x/y/w/h with a tile, and on a workspace holding no windows that tile is the whole usable area. A layer surface reached it through click-to-focus: `surface_at()` returns layer surfaces on purpose, so a click can land on the bar, and click-to-focus raised whatever it returned. Pressing one of Waybar's workspace buttons therefore pushed the bar into the window stack, where it took a tile and drew its clock across the canvas. No xdg_toplevel configure is sent for a layer surface, so Waybar never learned its new box and kept painting bar content into it. The guard sits in `zorder_raise()`, the only door into `g.zorder`. `retile()` still selects on `surface_visible()`, which is true for every mapped layer surface by design; with the door shut that path is unreachable, so a second check there would guard a state that can no longer occur.
Both repaint paths treated every client buffer as opaque. An ARGB8888 surface therefore punched its own background through whatever sat behind it: a translucent Omarchy popup drew its corners as black instead of revealing the wallpaper. wl_shm ARGB8888 is premultiplied by the Wayland spec, so the source contributes unscaled and the destination is attenuated by 1 - alpha. `blend_premultiplied()` applies that to the software blit, on both the memcpy row path and the nearest-sampling scale path. The GL path enables GL_ONE / GL_ONE_MINUS_SRC_ALPHA and gives the fragment shader a third `u_use_tex` state, so a sampled texture keeps its alpha only when its format carries one; XRGB8888 still forces alpha to 1 because its fourth channel holds no coverage. `buffer_has_alpha()` checks both the wl_shm and the DRM format enums. The two share one field and cannot collide: WL_SHM_FORMAT_ARGB8888 is 0 and the DRM constants are fourcc codes.
Click-to-focus called `kbd_set_focus()` on whatever was under the cursor, ignoring any exclusive layer-shell grab. Clicking the bar while klauncher held the keyboard handed focus back to the window underneath, so the launcher stopped receiving keystrokes. wlr-layer-shell-v1 does not allow that. Its `exclusive` entry states that for the top and overlay layers "the seat will always give exclusive keyboard focus to the top-most layer which has keyboard interactivity set to exclusive", and names the case it protects: "a lock screen or a password prompt". A click that moves focus away is how such a surface gets bypassed. klauncher is an overlay-layer surface asking for exactly that. The other three `kbd_set_focus()` sites already consult `layer_kb_grab()` — the map path and both destroy paths — so this makes the outlier agree with them. `zorder_raise()` stays outside the guard: raising is stacking, and the protocol governs keyboard focus only.
CTRL+SHIFT+Space repainted the compositor's half of the desktop but never the bar's. The hook wrote Waybar's stylesheet, then walked /proc to send the bar SIGUSR2 — and never matched it. Two reasons, both in how it read /proc/<pid>/cmdline. `read -r cmd` stops at a newline, but cmdline separates its arguments with NUL and ends with one, so cmd came back as the whole command line with its separators dropped. And the bar reaches its own exec through the socket gate's `exec /usr/local/bin/waybar`, which leaves argv[0] absolute, so `case $cmd in waybar*)` could not have matched even on argv[0] alone. Read to the first NUL with `-d ''`, which yields argv[0] by itself, and match on its basename. The switch now runs end to end: THEME_HOOK reports the bar's pid, Waybar reloads on the signal, and kandelo-omarchy.spec.ts clears gate 6 through to the notification.
`scripts/ci-vitest-evidence-classes.tsv` must exactly cover the live Vitest file inventory, so `ci-run-test-suite.sh vitest exact-abi-source` refuses to enumerate while the two drift. This branch raises ABI_VERSION, which is exactly when that group runs. Classify the test files this branch adds, and drop the rows for files it no longer carries. A file is `source-only` only when its whole import closure stays clear of the binary resolver; the runner enforces that separately from the cover check. The smokes exec staged wasm, so they are `prepared-product`; the remaining unit tests import only `host/src/`.
mho22
force-pushed
the
explore-dri-omarchy
branch
from
August 29, 2026 08:14
ccc7c62 to
9072b53
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
explore-dri-toolkit(#1318) puts a real C/C++ toolkit on the wasm32 sysroot, and #976 gives Kandelo a tiling compositor. This PR is the desktop shell that turns them into an Omarchy-shaped desktop: a layer-shell bar, a launcher, a theme engine, notifications, and the identity work that makes it look and feel like Omarchy rather than a compositor test.Top of the six-PR DRI stack (#698 → #709 → #948 → #976 → #1318 → #1241). It stacks on
explore-dri-toolkit.This PR was split. It previously carried both the toolkit ports and the desktop built on them, at 62 commits. The ports moved to #1318; what remains here is the desktop, at 13 commits.
What changed
The shell
wlr-layer-shell, a bar, a launcher and theming —zwlr_layer_shell_v1with anchors, margins, exclusive zones and keyboard-interactivity, plus the arrangement engine that reserves strips and retiles beneath them. The file-based theme system (/usr/share/kandelo/themes/<name>/theme.conf,kwlctl dispatch theme,theme>>broadcast),kbar,klauncher, andkwl_layer_create()in libkwl. Includes a SIGPIPE fix: a control client exiting without reading its reply killed the desktop.wallpaper = <file>, KWLP raw pixels bilinear-scaled by the compositor; raw because nothing in C decodes PNG or JPEG, so the page renders per-theme aurora wallpapers at staging time) and six themes: Tokyo Night, Catppuccin Mocha, Gruvbox, Nord, Everforest, Rosé Pine. The launcher registry offers Vim, NetHack, Nano and Bash running unmodified inwlterm, lazy-fetched from the shell image's archives.klauncher --menuis the Omarchy menu (Apps/Theme, ESC back to root).notify = <path>spawns a themed overlay toast on every theme switch, andkbarshows the kernel's monotonic uptime.wp_presentation, and typed letters map by key — foot asks for presentation feedback, and the keymap resolves letters by key rather than by position.foot, Waybar and mako actually require.host_gl_presentreturns-EIOon a lost WebGL2 context; a lost context silently no-ops every GL call, so the pump had been presenting frozen pixels while the flip counters advanced. The loss and restore listeners arm at canvas attach rather than at presenter build.wldesktoppackage lands on the rebased kernel.The fixes driving it exposed
surface_at()returns layer surfaces so a click reaches the bar, and click-to-focus raises whatever it returns — so clicking Waybar pushed the bar into the window stack, whereretile()handed it a tile: the whole usable area whenever the workspace held no windows.wlr-layer-shell-unstable-v1v4 states that for the top and overlay layers "the seat will always give exclusive keyboard focus to the top-most layer which has keyboard interactivity set to exclusive", and names the case it protects: "a lock screen or a password prompt". Click-to-focus ignored that; the other threekbd_set_focus()sites already consultedlayer_kb_grab(). Verified end to end in a browser:kandelo-omarchy.spec.tsclicks the bar at(5,5)and then types, andKLAUNCHER_FILTER q=te n=1now appears, so the assertion that used to fail passes.Both hosts
wlcompositor-{layer-shell,theme,keybind,resize,…}smokes, including coverage for the wallpaper triple (image, gradient fallback, broken file), the grid-preserving-resize recommit regression, CTRL-ALT exact-mods binds, the menu flow, and the notify hook. Plusdri-kms-registry(flipCount counts every latch),dri-kms-stats-sab(the flip gate, and the loss/rebuild pair against a fake GL) andkernel-blocking-retry-snapshot(-ESRCHis the export's contract, not corruption).kandelo-omarchy.spec.tscovers the bar, launcher, themes, the vim launch through the launcher with an ABI-mismatch guard, the menu flow and the toast lifecycle.kandelo-kms-context-loss.spec.tsdrives the loss/rebuild path against a real GL context.Rebased and rebuilt
This branch sits on
explore-dri-toolkitat55eea05d0. The history was rebuilt from 62 commits to 13, and the toolkit ports moved to #1318; one test commit added since brings it to 14.Against the previous tip, 77 files differ outside those
mainitself moved. Of those, 51 are deleted session handoff docs (docs/plans/*handoff*.md, 9,437 lines): nothing in the repo referenced any of them, andmain'sdocs/plans/holds 89 files of which exactly one is a handoff, so shipping 51 more contradicted the project's own convention. Every*-plan.mdwas kept. One more deletion is the scratch crash-repro spec, and one isdocs/plans/2026-06-22-scummvm-port-plan.md, unrelated to this stack. The rest are comment-only: plan labels that no longer resolve, replaced by the fact each stood for.The A/B split had one real bug, now fixed.
packages/registry/wayland-protocols/build.tomldeclaresxml/wlr-layer-shell-unstable-v1.xmlandxml/presentation-time.xmlas build inputs, and the first file assignment put both XMLs in this PR — so #1318 would not have built standalone. Waybar is a layer-shell client and foot asks for presentation feedback, so both moved to #1318. Caught byxtask build-deps program-index-context-check.Verification
cargo test -p kandelo --target aarch64-apple-darwin --lib→ 1665 passed, 0 failedbash scripts/check-abi-version.sh→ exit 0, reporting "snapshot is in sync with sources" and "ABI_VERSION and snapshot are consistent". The 43 → 44 bump now lives in Toolkit on Kandelo — the C/C++ library stack behind foot, Waybar and mako #1318, not here.scripts/run-libc-tests.sh→ 302 passed, 20 XFAIL, 0 build failures, and 1 unexpected failure:regression/raise-race. That test forks 100 children inside a signal handler; 5 waits returnECHILD, reproducibly.scripts/run-libc-tests.sh:163skips it wheneverCI=true, so it does not gate this PR, and whether it regressed or predates the stack is not yet established.scripts/run-posix-tests.sh→ 174 passed, 0 FAIL, 3 XFAIL, 2 SKIPBoth suites need
WASM_POSIX_RESOLUTION_POLICY=source-only-v1andWASM_POSIX_SOURCE_ONLY_BINARY_ROOT=<repo>/local-binaries/source-only-v1.binaries-abi-v44/index.tomldoes not exist until the toolkit tier merges, so thebinaries/provenance tier cannot be materialized locally. The full host vitest suite needs both provenance tiers resolvable at once, which only CI's prepared workspace provides — it is the gate for that suite and for the browser demos.The stack has since been rebased onto
mainat888e628d5. Two gates were re-run at the new stack tipccc7c6214(explore-dri-omarchy):bash scripts/check-abi-version.sh→ exit 0, snapshot in sync and the 43 → 44 bump consistent;scripts/ci-run-test-suite.sh vitest exact-abi-source→ 3049 passed, 0 failed, with two files failing to collect on paths #1321 moved tocrates/runtime-core/. That collection failure is amaindefect and #1337 fixes it.Known gaps, carried not introduced
kandelo-omarchy.spec.tsfails waiting for a sixth tile:footopens, but no tile arrives. This line was unreachable before the keyboard-grab fix, so there is no earlier baseline for it. Not yet investigated.dispatch closelayerexposed a host-layer deadlock when a layer client exits in reaction to a compositor event, which needs its own investigation.host/test/wlcompositor-layer-shell-smoke.tsgained two cases that have never executed: the file needswlcompositor.wasm,kbar.wasm,klauncher.wasm,kwlctl.wasmandwlclient-test.wasmfrom one provenance tier, and no tier holds all five.wldesktopowns the first three;build-programs.shbuilds the other two. Closing this means addingkbar,kwlctlandwlclient-testaswldesktopoutputs and bumping itsbuild.tomlrevision.apps/browser-demos/test/kandelo-kms-presenter.spec.tswas deleted with its CI step, andkandelo-kms-context-loss.spec.tscannot take the vacated slot: it needs the built desktop binaries, and that job runs a bare checkout with no binary fetch. So the pixel-store fix is pinned only against a fake GL, which accepts an upload a real context would reject — and that rejection is the failure mode.docs/browser-support.mdcarries the note and the repair route:createCentralizedKernelWorkerTestDoubleis the sanctioned replacement for the writable-property and cast tricks the old fixture used.Docs:
architecture.md(desktop shell, themes, menu, knotify),browser-support.md(Omarchy demo section), presets.