"Your apps, your terminal."
Terminal GUI renderer. Capture Wayland applications and encode them as ASCII art with luma-based character selection, edge detection, and AT-SPI text overlay. Runs natively on Niri.
Veil renders graphical Wayland applications inside your terminal by:
niri msg --json windows)grim (Wayland screenshot utility)Result: Your desktop GUI apps render as live ASCII art in the terminal. Works with any GTK4/Qt/native Wayland application.
Launch a TUI app:
veil run my-terminal-app
Launch a GUI app (renders to terminal):
veil run-gui nautilus # File manager veil run-gui zen-browser # Web browser
Exit: Ctrl+C
Check terminal capabilities:
veil probe
git clone https://github.com/viewerofall/veil cd veil cargo build --release ./target/release/veil run-gui nautilus
cargo install --path . # or with git directly: cargo install --git https://github.com/viewerofall/veil
| Command | Description |
|---|---|
veil run <app> |
Launch a TUI/terminal application |
veil run-gui <app> |
Launch a GUI application (renders to terminal) |
veil probe |
Show terminal capabilities and active config |
Control rendering parameters on the fly:
veil run-gui --override fps=15,quality=ascii zen-browser
| Parameter | Description | Default |
|---|---|---|
fps |
Target frames per second (render loop) | 30 |
quality |
Rendering quality (ascii_luma, ascii_edge) | ascii_luma |
veil/ ├── veil-cli/ CLI interface, render loops ├── veil-compositor/ Window detection, capture pipeline ├── veil-render/ Character encoding (luma, edge detection) ├── veil-config/ Lua configuration └── veil-capture/ Zig library (future GPU capture)
For GUI apps (run-gui):
Niri window detection (IPC)
↓ Extract window geometry & PID
↓ grim fullscreen capture (PNG)
↓ Decode to RGBA
↓ Compute luma (luminance) per cell
↓ Apply hysteresis (noise rejection)
↓ Edge detection (neighboring cell luma deltas)
↓ Map luma to ASCII characters (sparse to dense)
↓ AT-SPI text overlay (actual widget text)
↓ Dirty-row optimization (only redraw changed rows)
↓ Terminal output
Command-line interface and main render loops. Two paths:
Window detection and frame capture:
gui.rs — Niri window detection, grim capture, luma computationcapture_shm.rs — Read from /dev/shm (future LD_PRELOAD path)wayland_screencopy.rs — Wayland screencopy framework (May 1st GPU work)Terminal encoding:
Zig library for future GPU rendering:
shm_out.zig — /dev/shm output moduleintercept.zig — Wayland wl_shm pool interceptionegl_intercept.zig — EGL GPU capture skeleton (future)Niri coordinates: Niri is a tiling compositor. Windows don't have fixed screen coordinates — they exist in a layout tree. Full Wayland coordinate calculation requires knowing tile grid dimensions, output scaling, and workspace offsets. Fullscreen capture + downscaling is pragmatic for now; proper solution requires Wayland screencopy protocol (in progress for May 1st).
GPU rendering: Rendering 1920×1080 to 80×24 luma computation on every frame is expensive on weak hardware. GPU path will push capture and initial downscaling to VRAM, freeing CPU for encoding.
--override fps=15Default 30 FPS target assumes ~33ms budget per frame. Adjust --override fps=N for your hardware.
Minimal footprint suitable for embedded systems and remote SSH sessions.
GitHub: viewerofall/veil
Areas for contribution:
MIT
Created by viewerofall. GitHub: viewerofall/veil