Production-grade Wayland desktop environment for LegendaryOS, built on Smithay (compositor) and Tauri + Svelte (desktop shell).
- Wayland compositor (
compositor/) — xdg-shell, layer-shell, XWayland, session-lock, idle/idle-inhibit, cursor-shape, fractional-scale, data-device/primary-selection, pointer-constraints and relative-pointer (pointer lock for games), tablet input, text-input/input-method (IME),wlr-foreign-toplevel-management(native window list for the panel/switcher — nowmctrl/xdotoolneeded when running under HackerOS-Comp),wlr-output-management(multi-monitor configuration as a protocol),wlr-screencopy(native screenshot support). Both a nested/dev backend (winit) and a bare-metal DRM/KMS/libseat backend for TTY sessions. - Desktop shell (
src/+src-tauri/) — panel, launcher, window switcher, workspaces, notification center, control center, and a suite of first-party apps: Mail (IMAP/SMTP), Web, Docs (with PDF/DOCX import/export), Code editor, Terminal, File explorer, Camera, Archive manager, System Monitor, Partition Manager, Settings (including Parental Controls: PIN-protected app blocking, daily time limits, allowed-hours windows). - Packaging for Debian/Ubuntu, Fedora, LegendaryOS, Arch, Alpine,
openSUSE, Gentoo, Void, Nix, Snap, and Flatpak (the latter two/Gentoo/
Void as submission-ready templates — see
packaging/).
See ROADMAP.md for exactly what's implemented, what's
best-effort/needs on-hardware verification, and what's still planned.
# System packages (Debian/Ubuntu/HackerOS)
sudo apt install \
build-essential curl git \
libssl-dev libgbm-dev libseat-dev \
libinput-dev libxkbcommon-dev \
libudev-dev libdrm-dev \
libgtk-3-dev libwebkit2gtk-4.0-dev \
libayatana-appindicator3-dev \
librsvg2-dev pkg-config \
seatd
# wmctrl/xdotool are OPTIONAL — only used as a fallback when the shell
# isn't actually running under HackerOS-Comp (e.g. a nested dev session
# under a different desktop environment). Under a real HackerOS-Comp
# session, window listing/focus/close/minimize all go through the
# compositor's own IPC and the wlr-foreign-toplevel-management protocol,
# so these packages aren't required for normal use.
# sudo apt install wmctrl xdotool
# Node.js 18+
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt install nodejs
# Rust stable
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Tauri CLI v1
cargo install tauri-cli --version "^1"
# Enable seatd (needed for DRM/bare-metal mode)
sudo systemctl enable --now seatd
sudo usermod -aG seat $USER
# (re-login after this)npm install
npm run build:tauri
# This runs: npm run build → vite build → tauri buildNote: the Wayland compositor (Smithay) described under Features above is planned architecture —
compositor/doesn't exist in this tree yet, so there is currently nonpm run build:compositorornpm run build:allcommand, and the CI workflows (test.yml,build.yml) don't assume it exists either (they probe forcompositor/Cargo.tomland skip compositor-specific steps when it's absent, rather than failing). The shell above already runs standalone today under any existing Wayland/X11 compositor (GNOME, KDE, sway, ...) — it doesn't require HackerOS-Comp specifically.
npm run dev # Start Vite dev server on :1420
cargo tauri dev # Or: npm run tauri -- devnpm run build:tauri
└─ tauri build
├─ beforeBuildCommand: "npm run build"
│ ├─ tsc --noEmit (type-check)
│ └─ vite build → dist/
└─ cargo build (src-tauri/) → blue-environment binary
The key insight: tauri build calls npm run build automatically via
beforeBuildCommand in tauri.conf.json. You should NOT call
npm run build manually before npm run build:tauri.
blue-environment/
├── index.html ← entry HTML (project root)
├── src/ ← TypeScript/React frontend
│ ├── App.tsx ← Desktop shell
│ ├── constants.tsx ← App registry
│ ├── types.ts ← All TypeScript types
│ ├── vite.config.ts ← Vite config (root = ..)
│ ├── tsconfig.json
│ ├── index.tsx ← React entry point
│ ├── components/
│ │ ├── Window.tsx
│ │ ├── TopBar.tsx
│ │ ├── StartMenu.tsx
│ │ ├── ControlCenter.tsx
│ │ ├── NotificationCenter.tsx
│ │ ├── WindowSwitcher.tsx
│ │ ├── WorkspaceSwitcher.tsx
│ │ ├── ClipboardPanel.tsx
│ │ ├── ToastContainer.tsx
│ │ └── apps/
│ │ ├── BlueAI.tsx
│ │ ├── BlueCodeApp.tsx ← Monaco + xterm
│ │ ├── BlueSoftwareApp.tsx
│ │ ├── BlueWebApp.tsx
│ │ ├── ExplorerApp.tsx
│ │ ├── MailApp.tsx ← Full mail client
│ │ ├── SettingsApp.tsx ← Full settings
│ │ ├── TerminalApp.tsx
│ │ ├── SystemMonitorApp.tsx
│ │ ├── NotepadApp.tsx
│ │ ├── CalculatorApp.tsx
│ │ ├── AboutApp.tsx
│ │ └── MailApp.tsx
│ ├── hooks/
│ │ ├── useWindowManager.ts
│ │ └── useKeyboardShortcuts.ts
│ ├── utils/
│ │ ├── systemBridge.ts ← Tauri IPC bridge
│ │ ├── configStore.ts ← Reactive config (wallpaper etc.)
│ │ └── notificationManager.ts
│ └── contexts/
│ └── LanguageContext.tsx
├── src-tauri/ ← Rust/Tauri backend
│ ├── Cargo.toml
│ ├── tauri.conf.json
│ ├── build.rs
│ ├── icons/icon.png
│ └── src/
│ ├── main.rs ← Tauri commands
│ ├── ai.rs ← AI API proxy
│ ├── weather.rs ← Weather widget backend (IP geolocation + Open-Meteo)
│ ├── parental_controls.rs ← PIN-protected app blocking, time limits
│ ├── apps.rs ← .desktop scanner
│ ├── cache.rs ← Config/cache
│ ├── session.rs ← Session detection
│ └── window_tracker.rs ← External windows (compositor IPC first, wmctrl/xdotool fallback)
└── compositor/ ← Smithay compositor (separate crate)
├── Cargo.toml
└── src/
├── main.rs
├── state/ ← BlueState + protocol handler impls
├── input/ ← libinput dispatch, move/resize grabs
├── render/ ← winit (nested) + DRM/KMS (bare-metal) backends
├── xwayland/ ← XWayland integration
├── ipc/ ← Unix socket protocol to the shell
└── protocols/ ← idle, session-lock, decoration, cursor-shape,
foreign-toplevel-management, output-management,
screencopy
Blue Environment can run on two compositors. The choice is made in the
[backend] section of config.hk (HackerOS Configuration Format):
[backend]
-> compositor => labwc ! or: hackeros-comp (the default)
-> labwc_binary => labwc ! optional: name or full path
-> labwc_args => ! optional: extra labwc arguments
-> labwc_config_dir => ! optional: default ~/.config/labwc
-> generate_labwc_config => true ! create a default labwc config if none exists
Which file? An existing config.hk is used wherever it already is
($BLUE_CONFIG_HK, $XDG_CONFIG_HOME/Blue-Environment/,
~/.config/Blue-Environment/, then /etc/xdg/Blue-Environment/). If there
is none, a default one is created at ~/.config/Blue-Environment/config.hk.
Start-up — the classic blue-environment invocation reads that file:
compositor |
What happens |
|---|---|
hackeros-comp |
Nothing changes: the binary runs as the shell, exactly as before. |
labwc |
Missing labwc config is generated (never overwriting anything), then the process becomes labwc -s "blue-environment --labwc-child"; labwc launches the shell. HackerOS-Comp is not required. |
labwc, but labwc isn't installed |
Warning, then the classic behaviour. |
If a display session already exists, labwc is not nested (force with
--start-backend); --no-backend always just runs the shell;
--backend-info prints what was detected.
What works natively on labwc (src-tauri/src/backend/):
- window list / focus / minimize / maximize / close for every native and
XWayland window —
wlr-foreign-toplevel-management, pushed to the UI as the samecompositor:window-list/compositor:window-focusedevents HackerOS-Comp emits; - system-wide clipboard history, including copies made in external apps
(
wl-paste --watch, needswl-clipboard); - global shortcuts while a native app has focus: labwc keybinds call
blue-environment --ctl <command>(toggle-start-menu,fullscreen-menu,toggle-control-center,toggle-clipboard,open-terminal,screenshot,lock,show-desktop, …) which talks to the running shell over$XDG_RUNTIME_DIR/blue-environment.sock; - the
CompositorBridgecommand set (focus/close/… , screenshots viagrim, lock, reload, workspace count) translated to labwc equivalents; - native windows look like Blue windows: the generated
rc.xmlselects theBlue-Environmentlabwc theme (same palette as the shell's own window chrome) and reserves the top bar area with<margin>.
The ready-made configuration lives in src-tauri/resources/labwc/ (HackerOS
ships it in /etc/skel/.config/labwc and /usr/share/themes/Blue-Environment).
Known limits: Alt+Tab is labwc's own switcher (native windows only); Blue's
in-shell windows live in the shell layer, i.e. beneath native windows; live
workspace switching / DPMS timeout have no labwc IPC and are keybind/idle-daemon
matters.
| Shortcut | Action |
|---|---|
Super |
Toggle Start Menu |
Super+Tab |
Full-screen App Picker |
Super+1–4 |
Switch Workspace |
Super+←/→ |
Switch Workspace |
Super+↑ |
Maximize Window |
Super+↓ |
Minimize Window |
Super+D |
Show Desktop |
Super+L |
Lock Screen |
Alt+Tab |
Window Switcher |
Alt+Shift+Tab |
Window Switcher (backwards) |
Alt+F4 |
Close Window |
Ctrl+Alt+T |
Open Terminal |
Ctrl+Alt+C |
Control Center |
Ctrl+Shift+V |
Clipboard History |
PrintScreen |
Screenshot |
Escape |
Close Panels / Cancel |
When running inside VirtualBox or any VM:
- Compositor auto-detects
WAYLAND_DISPLAY/DISPLAY→ uses winit (nested) backend - Full 3D rendering via host GPU
- XWayland started automatically for X11 app support
On bare metal (TTY, no display server):
- Uses DRM/KMS backend via libseat
- Requires seatd running and user in
seatgroup
This means npm run build was not run before tauri build.
Solution: Always use npm run build:tauri (not npm run tauri).
The beforeBuildCommand in tauri.conf.json handles this automatically.
Ensure Cargo.toml has chrono = "0.4" (no features).
The local-offset feature does not exist in chrono 0.4.x.
sudo systemctl enable --now seatd
sudo usermod -aG seat $USER
# Then re-login© 2026 HackerOS Team
