Skip to content

Omarchy on Kandelo — the desktop shell: layer-shell bar, launcher, themes - #1241

Draft
mho22 wants to merge 6 commits into
explore-dri-hyprlandfrom
explore-dri-omarchy
Draft

Omarchy on Kandelo — the desktop shell: layer-shell bar, launcher, themes#1241
mho22 wants to merge 6 commits into
explore-dri-hyprlandfrom
explore-dri-omarchy

Conversation

@mho22

@mho22 mho22 commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #976 (the tiling WM). This starts at the O1 milestone of docs/plans/2026-07-14-build-hyprland-class-compositor-plan.md §3 (PR15–PR18) and continues past it: the desktop shell that turns the tiling compositor into an Omarchy-shaped desktop, then the identity work that makes it look and feel like Omarchy rather than a compositor test.

Commits

  1. wlr-layer-shell, kbar + klauncher, theme enginezwlr_layer_shell_v1 (anchors, margins, exclusive zones, keyboard-interactivity) with 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 and klauncher; kwl_layer_create() in libkwl; a SIGPIPE fix (a control client exiting without reading its reply killed the desktop).
  2. /?demo=omarchy — the O1 integration gate: bar + launcher + themes, every SUPER bind mirrored on CTRL (browsers reserve SUPER).
  3. Image wallpapers + six themeswallpaper = <file> (KWLP raw pixels, bilinear-scaled by the compositor; raw because nothing in C decodes PNG/JPEG — the page renders per-theme aurora wallpapers at staging time). Tokyo Night, Catppuccin Mocha, Gruvbox, Nord, Everforest, Rosé Pine. Fixes a wlterm bug this exposed: a resize smaller than one terminal cell left the window buffer-less and invisible.
  4. Omarchy menu + real applications — the launcher registry offers Vim, NetHack, Nano and Bash running unmodified in wlterm (lazy-fetched from the shell image's archives); klauncher --menu is the Omarchy menu (Apps/Theme, ESC back to root) on SUPER/CTRL+ALT+Space; the compositor gains the ALT modifier and the keymap gains Alt_L + arrow keys (arrows never reached clients before); wlterm advertises TERM=vt100 to its child.
  5. knotify toasts + bar uptimenotify = <path> spawns a themed overlay toast on every theme switch; kbar shows the kernel's monotonic uptime (no /proc/meminfo → no memory module; real stats or none).

Both hosts

  • Node: wlcompositor-{layer-shell,theme,keybind,resize,…} smokes — all 10 wlcompositor/wlterm suites pass serially, including new 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.
  • Browser: kandelo-omarchy.spec.ts grew from 7 to 10 gates (vim launch through the launcher incl. an ABI-mismatch guard, the menu flow, the toast lifecycle); omarchy + hyprland + wayland gates pass.

Verification: cargo -p kandelo --lib 0 fail; run-libc-tests.sh / run-posix-tests.sh 0 FAIL; check-abi-version.sh in sync — everything here is additive userspace, no ABI_VERSION bump.

Known issues carried in the tree (not this PR's regressions): the published vim archive is still abi-v15 and needs a matrix re-publish (a locally rebuilt vim.zip is what the demo uses meanwhile); the exclusive-zone release path (bar exits → tiling area grows back) remains untested — a prototype dispatch closelayer exposed a host-layer deadlock when a layer client exits in reaction to a compositor event, which needs its own investigation.

Docs: architecture.md (desktop shell, themes, menu, knotify), browser-support.md (Omarchy demo section), presets.

mho22 and others added 4 commits August 10, 2026 18:57
…me engine

The Tier 1 / O1 desktop shell from the Hyprland-class compositor plan
(docs/plans/2026-07-14-build-hyprland-class-compositor-plan.md §3).

Compositor: zwlr_layer_shell_v1 (anchors, margins, exclusive zones,
keyboard-interactivity). layers_arrange() computes the window work area
from the anchored strips, background→overlay, then retiles. A surface
joins the layer list at role creation, not at map, so a client dying
mid-handshake cannot strand a strip. Compositing and hit-testing put
background/bottom under windows and top/overlay over them on the GPU
and CPU paths; layer surfaces show on every workspace.

Theme engine: theme_scan/theme_load/theme_apply/theme_switch over
/usr/share/kandelo/themes/<name>/theme.conf. `theme =` config key,
`kwlctl theme` query, `dispatch theme <name|next|prev>`, a `theme`
keybind dispatcher, and a `theme>>name` broadcast. Gaps and the focus
border color are theme variables; the wallpaper repaints and re-uploads
through a retained staging bo on switch.

Fixes: SIGPIPE from a control client that exits without reading its
reply no longer kills the compositor; a failed exec reports
KWLCTL_EXEC_FAILED on stdout.

libkwl: kwl_layer_create() + struct kwl_layer_opts; the connect path is
factored into kwl_open()/kwl_open_buffers() shared with
kwl_window_create().

New clients: kbar (top bar, exclusive zone, workspace pills + focused
window + clock off the kwlctl socket) and klauncher (overlay launcher,
keyboard-exclusive, filters /usr/share/kandelo/apps, Enter dispatches
exec).

Tests: layer-shell and theme Node smokes; the resize smoke now waits
for both windows' resize markers instead of racing the second one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The O1 integration gate: the tiling compositor boots with kbar on a
layer-shell strip, klauncher on SUPER/CTRL+Space, and three themes
(Tokyo Night, Catppuccin Mocha, Gruvbox) cycling on CTRL+SHIFT+Space.

omarchy-desktop.ts carries the data half: the compositor config, the
launcher app registry, and the theme palettes. Staging and spawn reuse
the hyprland block in live-setup.ts; only the extra files and the two
extra binaries differ. Every SUPER bind is mirrored on CTRL because the
browser reserves SUPER for the OS.

Both hosts: the compositor, bar, launcher, and theme engine are gated
by Node smokes (previous commit); the browser path is gated by the new
kandelo-omarchy.spec.ts Playwright suite (config + theme loaded, bar
anchored, windows tile under the bar, canvas composited, launcher takes
the keyboard + filters + launches, theme cycles, bar tracks the
workspace).

Docs: architecture.md gains the desktop-shell subsection;
browser-support.md gains the demo-table row and the Omarchy demo
section.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A theme's theme.conf gains `wallpaper = <file>`: KWLP raw pixels
("KWLP", u32le width/height, XRGB) that the compositor bilinear-scales
into g.wallpaper, falling back to the gradient when the key is absent
or the file fails to load. Raw pixels because nothing in the
compositor decodes PNG/JPEG — whoever stages the theme renders the
image.

The demo now ships six themes — Tokyo Night, Catppuccin Mocha,
Gruvbox, Nord, Everforest, Rosé Pine — each with an aurora wallpaper
the page renders at staging time (renderWallpaperKwlp: radial glows in
the palette colours on an OffscreenCanvas, 960x540, ~2 MB staged per
theme).

Fix exposed by per-theme gaps: wlterm only redrew + committed on a
KWL_RESIZE that changed its cell grid, but kwl_apply_resize has
already destroyed the buffer the compositor held — a tile shift
smaller than one cell (a theme's gap change) left the window invisible
until the shell printed again. wlterm now always recommits and gates
only the PTY winsize/SIGWINCH on a grid change. The other libkwl
clients already redrew unconditionally.

Both hosts: theme smoke gains the wallpaper triple (image, gradient
fallback, broken file) and the small-gap recommit regression (three
1px gap steps guarantee a grid-preserving resize); the Playwright gate
asserts WALLPAPER image and the widened theme-cycle set; hyprland +
omarchy browser gates green, all 10 wlcompositor/wlterm Node suites
green serially.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The launcher registry now offers the shell image's real software next
to the demo clients: Vim, NetHack and Nano run unmodified inside a
wlterm (`exec = /usr/local/bin/wlterm /usr/bin/vim`), plus a Bash
terminal. The binaries lazy-fetch from the image's archives on first
launch — the normal runtime path, no demo staging.

wlterm now sets TERM=vt100 in its forkpty child: the inherited TERM
describes the launcher's terminal, and curses apps must see the type
this terminal implements.

`klauncher --menu` opens the Omarchy menu: a root level (Apps, Theme)
that descends into the app list or the installed-theme list (read from
`kwlctl theme`, switched with `dispatch theme`). ESC in a submenu goes
back to the root; ESC at the root dismisses. Bound to
SUPER/CTRL+ALT+Space in the demo config.

Compositor: the keybind engine gains the ALT (Mod1) modifier, and the
self-contained xkb keymap gains Alt_L and the four arrow keys — the
keymap had no arrows, so Up/Down never reached the launcher (or vim)
in the browser.

Both hosts: keybind smoke gains a CTRL ALT exact-mods case; theme
smoke gains the menu flow (root → themes → dispatch, ESC back to
root); the Playwright gate gains the vim launch through the launcher
(lazy fetch + fifth tile + an ABI-mismatch guard) and the menu flow.
All 10 wlcompositor/wlterm Node suites and the omarchy/hyprland/
wayland browser gates pass.

The vim launch initially died on exec: the cached vim archive was
built against ABI 15 (stale June link output reused by an incremental
build). Rebuilt locally against the current sysroot; the published
archive needs a re-publish for abi-v17.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Phase B-1 matrix build status — pr-1241-staging

ABI v17. 0 built, 77 failed, 77 total.

Package Arch Status Sha
alsa-lib wasm32 failed
libcurl wasm32 failed
libcxx wasm32 failed
libcxx wasm64 failed
libdrm wasm32 failed
libevdev wasm32 failed
libffi wasm32 failed
libinput-lite wasm32 failed
libinput wasm32 failed
libpng wasm32 failed
libudev wasm32 failed
libwayland wasm32 failed
libxkbcommon wasm32 failed
libxml2 wasm32 failed
libxml2 wasm64 failed
mtdev wasm32 failed
openssl wasm32 failed
openssl wasm64 failed
sdl2 wasm32 failed
sqlite wasm32 failed
sqlite wasm64 failed
zlib wasm32 failed
zlib wasm64 failed
bc wasm32 failed
bzip2 wasm32 failed
coreutils wasm32 failed
curl wasm32 failed
dash wasm32 failed
diffutils wasm32 failed
dinit wasm32 failed
fbdoom wasm32 failed
file wasm32 failed
findutils 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
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
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 and others added 2 commits August 10, 2026 21:56
knotify — the notify-send slot: one toast per process. A corner-
anchored overlay layer surface (top-right, under the bar's strip)
shows `knotify <title> <body…>` for a moment and exits; the surface
teardown is the dismissal, so there is no daemon and no unmap
protocol. It adopts the compositor's live theme over kwlctl.

The compositor gains a `notify = <path>` config key: a theme switch
spawns the configured notifier with the new name — the hook Omarchy
fills with notify-send in its scripts. Absent key, no spawn.

libkwl: kwl_layer_opts gains margins
(zwlr_layer_surface_v1.set_margin), which corner toasts need.

kbar: the right side gains the kernel's monotonic uptime next to the
clock, in the theme's muted colour. The kernel exposes no
/proc/meminfo, so there is no memory module — real stats or none.

Both hosts: theme smoke asserts the notify hook fires on a switch
(Node); the Playwright gate asserts the toast maps and dismisses
after the theme cycle. All 10 wlcompositor/wlterm Node suites and the
omarchy/hyprland/wayland browser gates pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The browser input bridge translated keys purely by physical position
(KeyboardEvent.code), and the kernel-side xkb keymap is a fixed US
layout — so a non-US physical layout delivered the letter printed at
the US position of the pressed key. On AZERTY the key labeled W sits
at the QWERTY-Z position: Ctrl+W arrived at the compositor as Ctrl+Z
and the killactive bind never fired, and every typed letter in wlterm
or the launcher came out QWERTY-scrambled.

charToKey maps a single typed ASCII letter to its US-layout keycode
and wins over the positional path; everything else (digits,
punctuation, dead keys, modifier-altered characters) keeps
KeyboardEvent.code, because their key value depends on Shift and the
layout's unshifted symbols. The Framebuffer pane's own translation
stays positional on purpose — game movement keys are positional.

Browser-only layer: Node has no keyboard capture (tests inject evdev
codes directly), so there is no Node-side counterpart to change.

Unit tests cover the AZERTY Ctrl+W pair and the non-letter fallback;
verified live against /?demo=omarchy with synthesized code=KeyZ/key=w
events (window closes), and the omarchy/hyprland/wayland/sdl2 browser
gates pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant