- Click indicator ring to toggle password peek (persistent click-to-toggle)
- Ctrl-hold transient peek refactored to share peek path
- Cursor always visible (previously hidden at position 0, now shows at top of ring)
- .pre-commit-config.yaml: standard hooks + cargo fmt/clippy
- flake.nix: devShell with cargo, rustc, rustfmt, clippy, cargo-audit, prek
- cargo fmt pass across all source files (trailing whitespace, line wrapping)
- MPRIS error logging (silent failures now logged)
- Dependabot versioning-strategy: "increase" → "auto"
The lock screen re-rendered and re-committed every output's full
screen-sized cairo surface on every 16ms timer tick regardless of
whether anything had changed, burning 60%+ of a core the entire time
the session was locked.
Track a dirty flag per surface and render only when state actually
changes: a keystroke, a system-status change, the clock minute rolling
over, or an in-flight animation. update() now reports whether it
redrew, so the timer commits only the surfaces that changed.
Also fix the fade-in never formally completing. The eased alpha
approaches 1.0 asymptotically while the 0.001 step throttle suppresses
the final sub-threshold increments, leaving fade_alpha stuck just under
1.0. Since "fade_alpha < 1.0" is the "still animating" signal, that kept
it permanently true and forced a full render every frame. Snap alpha to
exactly 1.0 once the fade duration elapses so the animation completes.
Idle CPU while locked drops from 60%+ to ~2%.
Hyprland was treating multi-output rustlock unlocks as client
crashes and showing its "lockscreen died" failsafe instead of
unlocking. Two issues:
1. Order. ext-session-lock-v1 recommends destroying every
ext_session_lock_surface_v1 before issuing unlock_and_destroy
on ext_session_lock_v1. rustlock did the opposite. Move
lock_surfaces.clear() into handle_auth_result so it runs
before session_lock.unlock(), and drop the now-redundant
clear from the auth-feedback callback.
2. Drain. After unlock_and_destroy the compositor sends
keyboard/pointer leave events and delete_id acks; if the
client disconnects before processing them, Hyprland treats
the disconnect as unclean. Add a conn.roundtrip() after the
main loop to drain those events before exit.
Single-output setups tolerate both of these (which is why the
bug does not show up on the upstream author's laptop). The
failsafe only reproduces with multiple outputs.
- Add arrow key support (Left/Right/Home/End) for cursor movement
- Add Delete key to remove character at cursor position
- Render cursor between password dots with visual indicator
- Modularize render.rs into separate modules (indicator, media_bar, status_bar, feedback)
- Add cubic ease-in-out for fade-in animation
- Optimize media_rects to use &'static str instead of String allocations
- Implement Wayland pointer handling for interactive media buttons.
- Add pure-Rust SVG loading via resvg.
- Refactor status indicators for consistent icon and layout handling.
When monitors are powered off (e.g. via niri power-off-monitors or
physical power switches), niri destroys and re-advertises the Wayland
outputs as they come back. Previously all three OutputHandler callbacks
were no-ops, causing two bugs:
- new_output: no lock surface was created for outputs that appeared
after the initial lock, so the slowest monitor to wake up would show
the compositor's red fallback instead of the lock screen.
- output_destroyed: stale LockedSurface, SessionLockSurface, output,
and captured_background entries accumulated for gone outputs.
Fix new_output to create a lock surface (and register it with the lock
manager) whenever a new output appears while the session is locked.
Fix output_destroyed to remove the corresponding entries from
lock_surfaces, lock_manager.surfaces, outputs, and
captured_backgrounds, keeping all parallel vecs in sync. Add
LockManager::remove_surface_by_output to support this, returning the
removal index so lock_surfaces can be updated with the same index.
The all-monitors-off scenario (all outputs destroyed simultaneously)
is handled naturally: the vecs are emptied and repopulated as each
monitor fires new_output on wake.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The ext-session-lock-v1 protocol does not guarantee a `finished` event
after the client calls `unlock_and_destroy` — that event is only sent
when the compositor independently terminates the lock. Waiting for it
caused rustlock to hang forever on niri (and any spec-compliant
compositor).
The previous attempt to fix this by setting exit=true immediately broke
unlocking because the while loop stopped calling event_loop.dispatch,
leaving the unlock_and_destroy bytes unflushed in the client-side
Wayland send buffer and never reaching the compositor.
Store the Connection in WaylandLock and explicitly call conn.flush()
after session_lock.unlock(), ensuring the unlock request is sent before
we exit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The users crate (RUSTSEC-2025-0040) has an unfixable vulnerability.
Replace it with the maintained whoami crate for getting the current
username. Also remove metrics.yml workflow as it provided no value.
- Add caps lock ring color change when caps lock is enabled (matching swaylock-effects)
- Add configurable caps lock colors: ring, text, key highlight, backspace highlight
- Add show_caps_lock_text config option (enabled by default)
- Add keyboard layout display with show_keyboard_layout option (disabled by default)
- Change keyboard_layout from u32 to String for proper display
- Add rust-cache to CI and dbus-1-dev dependency
- Add new system.rs module for system monitoring via D-Bus:
- Battery status (UPower)
- Media player controls and metadata (MPRIS)
- WiFi/Bluetooth status (NetworkManager)
- Keyboard layout tracking
- Add media key support (XF86 Play/Pause/Next/Prev)
- Add function keys F1-F3 for Suspend/Reboot/PowerOff
- Replace deprecated timer.rs with async system management
- Add GitHub Actions CI/CD workflows (CI + Release)
- Update dependencies and add optional networking feature
- Add pending_screenshots counter to WaylandLock
- Set pending_screenshots = output_count in locked() if screenshots enabled
- Timer skips rendering while pending_screenshots > 0
- Decrement pending_screenshots on Ready or Failed events
- When all screenshots done, timer starts rendering
- This ensures screenshots capture the desktop, not the lock UI
Fixes grey screenshot issue caused by capturing lock surface itself.
Matches swaylock-effects behavior: wait for screenshots before initial render.
- Timer callback: log each tick and commit status
- LockedSurface::update(): log background presence, fade alpha, feedback states
- Renderer::render(): log background drawing and fade alpha
- Ready event: log background set confirmation
These logs will help diagnose why screenshots are not displaying.
The conversion functions were producing big-endian ARGB (A,R,G,B) but
Cairo's ARGB32 on little-endian systems expects B,G,R,A byte order.
This caused screenshots to appear completely corrupted (likely black or
solid color).
Fixed both convert_xbgr8888_to_argb32 and convert_xrgb8888_to_argb32:
- Xbgr8888: source [R,G,B,X] -> dest [B,G,R,A=255]
- Xrgb8888: source [B,G,R,X] -> dest [B,G,R,A=255]
Added detailed comments explaining memory layouts and conversion logic
to prevent future regressions.
This should make screenshots display correctly.
- Fixed brace mismatch and duplicate code in screenshot.rs
- Increased SHM pool size to 256MB for high-res displays
- Corrected wlr-screencopy protocol usage: send copy request in Buffer event
- Fixed background handling: set_background now updates LockedSurface.background
- Removed dummy background creation; screenshots set when ready
- Added format conversion for Xbgr8888 and Xrgb8888 to ARGB32
- Proper Y-inversion handling based on flags
The lock screen now displays captured screenshots as background.
Screenshots are taken immediately after lock, before UI is shown.
Matches swaylock-effects behavior: lock appears first, then screenshot
applies when ready (no blocking).
Fixes: black/red screen issues, screenshot not displaying.