|
Some checks failed
CI / check (pull_request) Failing after 3s
breadgreet's stylesheet was a crude subset of what `design/sketch.html` specifies for `.greetoverlay`. Ported it faithfully so the greeter and the lock screen read as one visual identity: - Card: proper elevation (`box-shadow: 0 6px 28px`), a hairline `alpha(@overlay, 0.09)` border, 10px radius, 300px min-width. - Entry: recessed `shade(@surface, 1.5)` fill, and an accent focus ring (`border-color: @accent` + `box-shadow: 0 0 0 2px alpha(@accent, .28)`) matching the sketch's `.gentry:focus`. - Clock/date over the wallpaper get a soft text-shadow for legibility on any background; sizes match the sketch (46 / 14). - Session picker styled as a surface pill (button + popover), not a bare GtkDropDown. - A CSS-animated ring shown while a greetd request is in flight (`sync_busy`, driven from the single `update` exit point). - Shake on a failed attempt (`flash_error`), matching the lock screen's wrong-password motion — fired from both `AppInput::Error` and a PAM `AuthPrompt::Error`. Uses the shared `@define-color` palette (`@surface`, `@overlay`, `@accent`, `@red`) so it recolours with the theme, same as breadbar. `cargo test --workspace --all-targets` (157) / `clippy --all-targets -D warnings` / `fmt --check` all clean. Visual pass still wanted — the CSS is a direct translation of the mockup, not screenshot-verified. |
||
|---|---|---|
| .forgejo/workflows | ||
| breadgreet | ||
| breadlock | ||
| breadlock-ui | ||
| design | ||
| packaging | ||
| .gitignore | ||
| AGENTS.md | ||
| breadgreet.example.toml | ||
| breadlock.example.toml | ||
| Cargo.lock | ||
| Cargo.toml | ||
| CONTRIBUTING.md | ||
| EVENTS.md | ||
| LICENSE | ||
| README.md | ||
breadlock
Session locker and graphical greetd greeter for Hyprland on Wayland — the bread-ecosystem replacement for hyprlock and tuigreet. BOS already ships both binaries: breadgreet under cage via greetd, and breadlock via hypridle (SUPER+L is loginctl lock-session).
Two binaries, one workspace:
breadlock— locks the already running Hyprland session viaext-session-lock-v1. Drop-in forhyprlock.breadgreet— a graphical greeter that speaksgreetd's own IPC protocol (the same architecture asgtkgreet/regreet).greetdkeeps owning PAM auth, VT switching, and session launching;breadgreetonly draws the login UI and relays the conversation. This is a deliberate choice over reimplementing a display manager from scratch —greetdis already installed and battle-tested.
Both use bread-theme for palette loading, matching the rest of the bread* ecosystem (breadbar, breadbox, bos-settings).
bread event integration
breadlock works the same with or without breadd. When breadd is
running, it publishes bread.lock.locked / bread.lock.unlocked and
honors bread.command.lock.lock / bread.command.lock.unlock (emits
bread.lock.lock.done / .failed and bread.lock.unlock.done /
.failed). Run breadlock listen so both commands work while
unlocked; the locker also subscribes while the session is locked.
Unlock is fail-secure: already-unlocked acks bread.lock.unlock.done;
a running locker refuses with bread.lock.unlock.failed (only PAM at
the lock screen unlocks). The bus never calls compositor unlock() or
loginctl unlock-session. Super+L remains loginctl lock-session
(hypridle then runs breadlock). See EVENTS.md.
breadgreet is not on the bus. There is no bakery.toml (PAM /
pacman exception).
Architecture
breadlock/
├── breadlock-ui/ shared: bread-theme wrapper, TOML config, .desktop parsing,
│ software-rendering primitives (tiny-skia + cosmic-text,
│ behind the "paint" feature — only breadlock needs them)
├── breadlock/ the locker (SCTK + PAM; EGL wallpaper + software chrome)
└── breadgreet/ the greeter (GTK4 + relm4 + greetd_ipc)
breadlock
- Protocol:
ext-session-lock-v1viasmithay-client-toolkit— GTK has no session-lock support, so this is a raw Wayland client, not a layer-shell surface like breadbar. - Rendering: hybrid — wallpaper via EGL/GLES2 (
wl_egl_windowwrapping the lock surface); chrome (password pill, clock, status line) is still software (tiny-skia+cosmic-text, "Varela Round" by family name) and blitted over the GPU frame. If EGL init fails, the locker falls back to a fully-softwarewl_shmpath. - Background: a solid palette color or a PNG (cover-fit). Ken Burns (
background.ken_burns) is opt-in: a slow pan+zoom on image backgrounds — cheap on the GPU path, a continuous software redraw if EGL is unavailable.background.bluris not implemented — the key is accepted and logs a warning; the surface is drawn unblurred. Live blur-of-desktop (hyprlock-style) would need awlr-screencopycapture. - Auth:
pam-client2against thebreadlockPAM service (packaging/pam.d/breadlock, installed to/etc/pam.d/breadlockby the package). Runs on its own OS thread — libpam's conversation callback is blocking FFI — and reports back through acalloop::channelregistered on the render loop.
breadgreet
- Protocol:
greetd_ipc(greetd's own crate) over the Unix socket at$GREETD_SOCK:CreateSession→ answer eachAuthMessageviaPostAuthMessageResponse→StartSessionhands the resolved session command togreetd, which execs it and owns the VT switch away. - UI: GTK4 + relm4, matching breadbar's stack — without
gtk4-layer-shell.greetdhosts the greeter under a single-client kiosk compositor (cage -s), which already fullscreens its one client, so layer-shell's multi-surface/anchor semantics don't apply. Confirmed against ReGreet's real dependency list, which has no layer-shell dependency either. - Sessions: scans
/usr/share/wayland-sessionsand/usr/share/xsessionsfor.desktopentries and shows a keyboard-accessible picker. The configured default (compiled-in:bos) is pre-selected when that stem exists; otherwise the first discovered session.StartSessionis the chosen entry'sExec=argv.
Config
Copy breadlock.example.toml to ~/.config/breadlock/breadlock.toml and breadgreet.example.toml to /etc/greetd/breadgreet.toml (or ~/.config/breadgreet/breadgreet.toml for local testing under a normal session — breadgreet checks the system path first since it typically runs as the dedicated greeter user). Every field is optional; both binaries run with sensible defaults and no config at all.
breadlock.toml's [status] table (both flags default on) shows now-playing (MPRIS) and battery (upower) as a small line under the clock. Polled on a background thread; degrades silently if D-Bus or the service is missing.
Building
cargo build --release --bin breadlock --bin breadgreet
cargo test --workspace
Requires GTK4 (≥ 4.12), libxkbcommon, PAM development headers, git (workspace crates bread-theme / bread-utils are git deps), and pkg-config (gtk4-rs; also provided by base-devel). On Arch:
sudo pacman -S gtk4 wayland libxkbcommon pam rust cargo git pkg-config
breadlock-auth-check and breadlock-preview are extra, dev-only binaries in the breadlock package (see Verification below) — not installed by the package. Build them explicitly with cargo build --bin breadlock-auth-check or --bin breadlock-preview if you need them.
Packaging
packaging/arch/PKGBUILD builds and installs both binaries plus /etc/pam.d/breadlock, published to the [breadway] pacman repo by .forgejo/workflows/package.yml. breadlock is a deliberate pacman-only exception — there is no bakery.toml on purpose. A PAM service and greetd greeter need a root-owned install (/etc/pam.d/breadlock), which bakery has no privileged path for.
BOS already wires the packaged binaries (this repo still does not ship those system files):
# /etc/greetd/config.toml — BOS default
[default_session]
command = "cage -s -- breadgreet"
# hypridle lock_cmd (BOS). SUPER+L is loginctl lock-session, which hypridle picks up.
lock_cmd = breadlock
breadlock listen is the unlocked-path subscriber for
bread.command.lock.lock and bread.command.lock.unlock. It is not
started by hypridle; add it to session startup
(exec-once = breadlock listen) if a Lua workflow should be able to
lock the session while it is unlocked, or to ack already-unlocked.
bread.command.lock.unlock does not replace PAM and does not run
loginctl unlock-session. Super+L / hypridle remain
loginctl lock-session.
Verification (why this is safe to test without a lockout risk)
- PAM logic in isolation first:
cargo run --bin breadlock-auth-checkexercises the exact PAM flowbreadlockuses, against a typed password, with no Wayland surface at all. A bad/etc/pam.d/breadlockjust prints an error here — it can never lock a session. - Locker rendering/lock lifecycle nested, never against the live session: run
breadlockinside a nested Hyprland instance or undercage -- breadlock.ext-session-lock-v1only ever affects the compositor instance the client is connected to (scoped to$WAYLAND_DISPLAY), so a nested lock can never lock the real outer session. Verify the full type-password → PAM check → unlock cycle there, including the wrong-password path, before ever binding a real keybind. - If testing against a live session: keep a second TTY or SSH session open the whole time. Killing the
breadlockprocess is not a safe unlock path — per the protocol, an abnormally-terminated lock client is expected to leave the compositor still locked. The real recovery path is "kill it, then use the second session to restart Hyprland or switch VT." - breadgreet:
cargo test -p breadgreetruns thegreetd_ipcframing/state-machine tests against a mock Unix-socket server — no realgreetdor PAM involved. Manual testing against a realgreetdshould happen on a disposable VT, not by replacing the live BOScage -s -- breadgreetsession on VT1.
License
MIT