diff --git a/Cargo.lock b/Cargo.lock index 5dc4c48..8199cb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1590,6 +1590,32 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "rustlock" +version = "0.1.0" +dependencies = [ + "anyhow", + "cairo-rs", + "chrono", + "clap", + "env_logger", + "fastblur", + "futures", + "image", + "log", + "pam-client", + "secstr", + "serde", + "smithay-client-toolkit", + "toml 1.0.3+spec-1.1.0", + "users", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", + "xkbcommon", + "zeroize", +] + [[package]] name = "rustversion" version = "1.0.22" @@ -2085,32 +2111,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "wayrustlock" -version = "0.1.0" -dependencies = [ - "anyhow", - "cairo-rs", - "chrono", - "clap", - "env_logger", - "fastblur", - "futures", - "image", - "log", - "pam-client", - "secstr", - "serde", - "smithay-client-toolkit", - "toml 1.0.3+spec-1.1.0", - "users", - "wayland-client", - "wayland-protocols", - "wayland-protocols-wlr", - "xkbcommon", - "zeroize", -] - [[package]] name = "weezl" version = "0.1.12" diff --git a/Cargo.toml b/Cargo.toml index 373d2df..76d46ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "wayrustlock" +name = "rustlock" version = "0.1.0" edition = "2021" diff --git a/README.md b/README.md index ff0ef59..1fe5e71 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,42 @@ -# wayrustlock +# rustlock -A premium, high-performance Wayland screen locker written in Rust, designed to replicate and exceed the capabilities of `swaylock-effects` while ensuring stability on modern compositors like Niri and Sway. +A high-performance Wayland screen locker written in Rust, inspired by `swaylock-effects`. ## Features -- **Blazing Fast Performance:** Written in 100% safe Rust for maximum efficiency and security. -- **Advanced Visual Effects:** - - **Gaussian Blur:** Highly optimized blur effect for your desktop background. - - **Vignette:** Add a professional vignette effect to darken the edges of your screen. - - **Fade-in Animation:** Smooth transition from your desktop to the lock screen. -- **Iconic swaylock-effects UI:** - - **Dynamic Rotating Highlights:** Premium visual feedback as you type, with segments that appear at random angles and rotate dynamically. - - **Internal Information Hub:** A perfectly centered clock, date, and system uptime indicator inside the ring. - - **Custom Colors:** Full control over ring, inside, separator, and highlight colors. -- **Robust Logic:** - - **Niri Compatibility:** Fixed protocol violations that cause red screens on Niri. - - **Pre-lock Screenshot:** Captures your desktop *before* locking to ensure visual effects work perfectly even if the compositor hides surfaces immediately. - - **Grace Period:** Configurable unlock grace period for convenience. - - **Safe Exit:** Always waits for the compositor's confirmation before exiting, preventing session corruption. -- **Permanent Logging:** Verbose debug logs are always captured in `~/.wayrustlock.log` for immediate troubleshooting. +- **Performance**: Written in safe Rust with minimal dependencies +- **Visual Effects**: + - Gaussian blur (configurable radius and passes) + - Vignette effect (configurable base and factor) + - Smooth fade-in animation +- **Password Indicator**: + - Circular ring with configurable radius and thickness + - Dynamic key highlight segments that rotate with each keystroke + - Customizable colors (ring, inside, separator, highlight) +- **Information Display**: + - Centered clock (HH:MM format) + - Full date + - System uptime +- **Screenshot Support**: + - Captures desktop background before locking + - Applies visual effects to background + - **Authentication**: + - PAM-based authentication + - Configurable grace period (any key press within N seconds unlocks without password) +- **Logging**: Verbose debug logs written to `~/.rustlock.log` ## Usage -### Basic Command +### Basic Example + ```bash -wayrustlock --screenshots --effect-blur 7x5 --effect-vignette 0.5:0.5 +rustlock --screenshots --effect-blur 7x5 --effect-vignette 0.5:0.5 ``` -### Full Configuration Example +### Full Configuration + ```bash -wayrustlock \ +rustlock \ --screenshots \ --clock \ --indicator \ @@ -46,36 +53,41 @@ wayrustlock \ --fade-in 0.2 ``` -## Configuration File +## Configuration -You can also provide settings via a configuration file located at `~/.config/wayrustlock/config.toml`. Note that CLI arguments always take precedence over the config file. +Options can be provided via command line or a configuration file at `~/.config/rustlock/config.toml`. CLI arguments take precedence. -## Options +### Options | Option | Description | |--------|-------------| -| `--screenshots` | Enable desktop background capture. | -| `--clock` | Show the centered clock and uptime. | -| `--indicator` | Show the password indicator ring. | -| `--indicator-radius` | Radius of the indicator ring (default: 100). | -| `--indicator-thickness` | Thickness of the indicator ring (default: 7). | -| `--effect-blur` | Gaussian blur settings (e.g., `7x5` for radius 7, 5 passes). | -| `--effect-vignette` | Vignette settings (e.g., `0.5:0.5` for base:factor). | -| `--ring-color` | Color of the outer ring (RRGGBB[AA]). | -| `--inside-color` | Color of the inner circle (RRGGBB[AA]). | -| `--key-hl-color` | Color of the key highlight segments (RRGGBB[AA]). | -| `--grace` | Unlock grace period in seconds (default: 2). | -| `--fade-in` | Fade-in animation duration in seconds (default: 0.2). | +| `--screenshots` | Capture desktop background before locking | +| `--clock` | Display centered clock and date | +| `--indicator` | Show password entry ring (default: true) | +| `--indicator-radius ` | Ring radius in pixels (default: 100) | +| `--indicator-thickness ` | Ring thickness in pixels (default: 7) | +| `--effect-blur x

` | Gaussian blur: radius x passes (e.g., `7x5`) | +| `--effect-vignette :` | Vignette: base:factor (e.g., `0.5:0.5`) | +| `--ring-color ` | Outer ring color (hex, optional alpha) | +| `--key-hl-color ` | Key highlight segment color | +| `--line-color ` | Separator line color | +| `--inside-color ` | Inner circle color | +| `--separator-color ` | Ring separator color | +| `--grace ` | Grace period in seconds (default: 2) | +| `--fade-in ` | Fade-in animation duration (default: 0.2) | +| `--pam-service ` | PAM service name (default: "rustlock") | +| `--config ` | Path to config file | +| `--debug` | Enable debug logging | +| `--log-file` | Write logs to `~/.rustlock.log` | +| `--temp-screenshot` | Enable peek feature (press 'p' to temporarily show background) | ## Installation -Ensure you have a Rust toolchain and the necessary Wayland development libraries installed, then run: - ```bash cargo build --release ``` -The binary will be available at `target/release/wayrustlock`. +The binary will be available at `target/release/rustlock`. ## License diff --git a/default.nix b/default.nix index bb68a93..8ed87cd 100644 --- a/default.nix +++ b/default.nix @@ -1,7 +1,7 @@ { pkgs ? import {} }: pkgs.rustPlatform.buildRustPackage { - pname = "wayrustlock"; + pname = "rustlock"; version = "0.1.0"; src = ./.; cargoLock = { lockFile = ./Cargo.lock; }; diff --git a/pam.d/wayrustlock b/pam.d/wayrustlock index 45532b4..160d134 100644 --- a/pam.d/wayrustlock +++ b/pam.d/wayrustlock @@ -1,5 +1,5 @@ -# PAM configuration for wayrustlock -# Install this file to /etc/pam.d/wayrustlock +# PAM configuration for rustlock +# Install this file to /etc/pam.d/rustlock # Use the standard login service authentication auth include login diff --git a/src/auth.rs b/src/auth.rs index 7047f28..d132ead 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -7,7 +7,7 @@ use smithay_client_toolkit::reexports::{calloop::channel, calloop::EventLoop}; use users::get_current_username; use zeroize::Zeroizing; -const SERVICE_NAME: &str = "wayrustlock"; +const SERVICE_NAME: &str = "rustlock"; pub struct LockConversation { pub password: Option>, @@ -36,12 +36,6 @@ impl pam_client::ConversationHandler for LockConversation { } pub fn create_and_run_auth_loop() -> (channel::Sender>, channel::Channel) { - struct AuthLoopState { - auth_res_send: channel::Sender, - main_closed: bool, - context: pam_client::Context, - } - let username = get_current_username() .expect("Failed to get username") .to_str() @@ -49,7 +43,7 @@ pub fn create_and_run_auth_loop() -> (channel::Sender>, channe .to_string(); let conversation = LockConversation { password: None }; - let context = Context::new(SERVICE_NAME, Some(username.as_str()), conversation) + let _context = Context::new(SERVICE_NAME, Some(username.as_str()), conversation) .expect("Failed to initialize PAM context"); debug!("Prepared to authenticate user '{}'", username); @@ -57,35 +51,33 @@ pub fn create_and_run_auth_loop() -> (channel::Sender>, channe let (auth_res_send, auth_res_recv) = channel::channel::(); thread::spawn(move || { - let mut event_loop: EventLoop = EventLoop::try_new().unwrap(); + let mut event_loop: EventLoop<()> = EventLoop::try_new().unwrap(); event_loop .handle() - .insert_source(auth_req_recv, |evt, _metadata, state| match evt { + .insert_source(auth_req_recv, |evt, _metadata, _state| match evt { channel::Event::Msg(password) => { - state.context.conversation_mut().password = Some(password); - let status = match state.context.authenticate(Flag::NONE) { - Ok(()) => true, + let conversation = LockConversation { + password: Some(password), + }; + let mut context = + Context::new(SERVICE_NAME, Some(username.as_str()), conversation) + .expect("Failed to initialize PAM context"); + match context.authenticate(Flag::NONE) { + Ok(()) => { + auth_res_send.send(true).unwrap(); + } Err(err) => { error!("Pam authenticate failed with {:?}", err); - false + auth_res_send.send(false).unwrap(); } - }; - state.auth_res_send.send(status).unwrap(); + } } - channel::Event::Closed => state.main_closed = true, + channel::Event::Closed => {} }) .unwrap(); - let mut state = AuthLoopState { - auth_res_send, - main_closed: false, - context, - }; - - while !state.main_closed { - event_loop - .dispatch(None, &mut state) - .expect("Failed to run"); + loop { + event_loop.dispatch(None, &mut ()).expect("Failed to run"); } }); diff --git a/src/config.rs b/src/config.rs index d50ae41..0a6d981 100644 --- a/src/config.rs +++ b/src/config.rs @@ -57,7 +57,7 @@ pub struct Config { #[arg(long)] pub debug: bool, - /// Write verbose logs to ~/.wayrustlock.log + /// Write verbose logs to ~/.rustlock.log #[arg(long)] pub log_file: bool, @@ -69,18 +69,18 @@ pub struct Config { impl Config { pub fn load() -> Self { let cli_config = Config::parse(); - + // Use path from CLI if provided, otherwise default let config_path = cli_config.config.clone().unwrap_or_else(|| { let mut path = std::path::PathBuf::from(std::env::var("HOME").unwrap_or_default()); - path.push(".config/wayrustlock/config.toml"); + path.push(".config/rustlock/config.toml"); path }); if config_path.exists() { if let Ok(file_content) = std::fs::read_to_string(&config_path) { if let Ok(_file_config) = toml::from_str::(&file_content) { - return cli_config; + return cli_config; } } } diff --git a/src/main.rs b/src/main.rs index 9203b7f..968f244 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,16 +9,18 @@ mod util; use config::Config; use lock::LockManager; -use screenshot::{CaptureData, ScreenshotManager, Screenshot}; +use screenshot::{CaptureData, Screenshot, ScreenshotManager}; use std::error::Error; use std::fs::OpenOptions; use std::io::Write; use std::sync::{Arc, Mutex}; -use std::time::Duration; +use std::time::{Duration, Instant}; use wayland_client::protocol::wl_output::WlOutput; use wayland_client::protocol::wl_surface::WlSurface; -use wayland_client::{Connection, Dispatch, QueueHandle, Proxy}; -use wayland_protocols_wlr::screencopy::v1::client::zwlr_screencopy_frame_v1::{ZwlrScreencopyFrameV1, Flags}; +use wayland_client::{Connection, Dispatch, Proxy, QueueHandle}; +use wayland_protocols_wlr::screencopy::v1::client::zwlr_screencopy_frame_v1::{ + Flags, ZwlrScreencopyFrameV1, +}; use wayland_protocols_wlr::screencopy::v1::client::zwlr_screencopy_manager_v1::ZwlrScreencopyManagerV1; use zeroize::Zeroizing; @@ -45,9 +47,7 @@ static FILE_LOGGER: std::sync::LazyLock>> std::sync::LazyLock::new(|| std::sync::Mutex::new(None)); fn setup_file_logging(_config: &Config) { - let log_path = std::env::var("HOME") - .unwrap_or_else(|_| "/tmp".to_string()) - + "/.wayrustlock.log"; + let log_path = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string()) + "/.rustlock.log"; match OpenOptions::new() .create(true) @@ -124,10 +124,14 @@ struct WaylandLock { exit: bool, unlocking: bool, screenshot_manager: Option, + grace_until: Option, } impl WaylandLock { fn handle_auth_result(&mut self, success: bool) { + // Clear grace period on any auth result + self.grace_until = None; + if success { log::info!("✅ Authentication successful - unlocking session"); if let Some(session_lock) = &self.session_lock { @@ -159,6 +163,16 @@ impl WaylandLock { } fn handle_key_event(&mut self, event: KeyEvent) { + // Check if we're in the grace period (any key unlocks without password) + if let Some(grace_until) = self.grace_until { + if Instant::now() < grace_until { + self.handle_auth_result(true); + return; + } else { + self.grace_until = None; + } + } + use crate::input::InputAction; use smithay_client_toolkit::seat::keyboard::Keysym; @@ -167,7 +181,9 @@ impl WaylandLock { if let Ok(mut lock_manager) = self.lock_manager.lock() { let mut password = Zeroizing::new(String::new()); for surface in &mut lock_manager.surfaces { - if let Some(InputAction::SubmitPassword(p)) = surface.handle_key_event(event.clone()) { + if let Some(InputAction::SubmitPassword(p)) = + surface.handle_key_event(event.clone()) + { password = p; } } @@ -194,32 +210,75 @@ impl WaylandLock { } impl ProvidesRegistryState for WaylandLock { - fn registry(&mut self) -> &mut RegistryState { &mut self.registry_state } + fn registry(&mut self) -> &mut RegistryState { + &mut self.registry_state + } registry_handlers![OutputState, SeatState]; } impl CompositorHandler for WaylandLock { - fn scale_factor_changed(&mut self, _conn: &Connection, _qh: &QueueHandle, _surface: &WlSurface, _new_factor: i32) {} - fn transform_changed(&mut self, _conn: &Connection, _qh: &QueueHandle, _surface: &WlSurface, _new_transform: wayland_client::protocol::wl_output::Transform) {} - fn frame(&mut self, _conn: &Connection, _qh: &QueueHandle, _surface: &WlSurface, _time: u32) {} - fn surface_enter(&mut self, _conn: &Connection, _qh: &QueueHandle, _surface: &WlSurface, _output: &WlOutput) {} - fn surface_leave(&mut self, _conn: &Connection, _qh: &QueueHandle, _surface: &WlSurface, _output: &WlOutput) {} + fn scale_factor_changed( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _surface: &WlSurface, + _new_factor: i32, + ) { + } + fn transform_changed( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _surface: &WlSurface, + _new_transform: wayland_client::protocol::wl_output::Transform, + ) { + } + fn frame( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _surface: &WlSurface, + _time: u32, + ) { + } + fn surface_enter( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _surface: &WlSurface, + _output: &WlOutput, + ) { + } + fn surface_leave( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _surface: &WlSurface, + _output: &WlOutput, + ) { + } } impl OutputHandler for WaylandLock { - fn output_state(&mut self) -> &mut OutputState { &mut self.output_state } + fn output_state(&mut self) -> &mut OutputState { + &mut self.output_state + } fn new_output(&mut self, _conn: &Connection, _qh: &QueueHandle, _output: WlOutput) {} fn update_output(&mut self, _conn: &Connection, _qh: &QueueHandle, _output: WlOutput) {} - fn output_destroyed(&mut self, _conn: &Connection, _qh: &QueueHandle, _output: WlOutput) {} + fn output_destroyed(&mut self, _conn: &Connection, _qh: &QueueHandle, _output: WlOutput) { + } } impl ShmHandler for WaylandLock { - fn shm_state(&mut self) -> &mut Shm { &mut self.shm_state } + fn shm_state(&mut self) -> &mut Shm { + &mut self.shm_state + } } impl SessionLockHandler for WaylandLock { fn locked(&mut self, _conn: &Connection, _qh: &QueueHandle, _session_lock: SessionLock) { log::info!("Session LOCKED confirmed by compositor"); + self.grace_until = Some(Instant::now() + Duration::from_secs_f32(self.config.grace)); } fn finished(&mut self, _conn: &Connection, _qh: &QueueHandle, _lock: SessionLock) { @@ -239,21 +298,27 @@ impl SessionLockHandler for WaylandLock { log::debug!("CONFIGURE callback: {}x{}", width, height); if let Ok(mut lock_manager) = self.lock_manager.lock() { - if let Some(locked_surface) = lock_manager.find_surface_by_wayland_surface(session_lock_surface.wl_surface()) { + if let Some(locked_surface) = + lock_manager.find_surface_by_wayland_surface(session_lock_surface.wl_surface()) + { locked_surface.resize(width as i32, height as i32); locked_surface.set_configured(); - + let output = locked_surface.output(); let output_id = Proxy::id(output); let output_idx = self.outputs.iter().position(|o| Proxy::id(o) == output_id); - + if let Some(idx) = output_idx { if let Some(bg) = self.captured_backgrounds.get(idx).and_then(|b| b.as_ref()) { - log::info!("Applying background for output {} (ID: {:?})", idx, output_id); + log::info!( + "Applying background for output {} (ID: {:?})", + idx, + output_id + ); locked_surface.set_background(bg.clone()); } } - + locked_surface.update(); let _ = locked_surface.commit(&mut self.pool); } @@ -262,40 +327,143 @@ impl SessionLockHandler for WaylandLock { } impl KeyboardHandler for WaylandLock { - fn enter(&mut self, _conn: &Connection, _qh: &QueueHandle, _keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard, _surface: &WlSurface, _serial: u32, _raw: &[u32], _keysyms: &[smithay_client_toolkit::seat::keyboard::Keysym]) {} - fn leave(&mut self, _conn: &Connection, _qh: &QueueHandle, _keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard, _surface: &WlSurface, _serial: u32) {} - fn press_key(&mut self, _conn: &Connection, _qh: &QueueHandle, _keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard, _serial: u32, event: KeyEvent) { + fn enter( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard, + _surface: &WlSurface, + _serial: u32, + _raw: &[u32], + _keysyms: &[smithay_client_toolkit::seat::keyboard::Keysym], + ) { + } + fn leave( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard, + _surface: &WlSurface, + _serial: u32, + ) { + } + fn press_key( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard, + _serial: u32, + event: KeyEvent, + ) { self.handle_key_event(event); } - fn release_key(&mut self, _conn: &Connection, _qh: &QueueHandle, _keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard, _serial: u32, _event: KeyEvent) {} - fn update_modifiers(&mut self, _conn: &Connection, _qh: &QueueHandle, _keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard, _serial: u32, _modifiers: Modifiers, _layout: u32) {} + fn release_key( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard, + _serial: u32, + _event: KeyEvent, + ) { + } + fn update_modifiers( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _keyboard: &wayland_client::protocol::wl_keyboard::WlKeyboard, + _serial: u32, + _modifiers: Modifiers, + _layout: u32, + ) { + } } impl SeatHandler for WaylandLock { - fn seat_state(&mut self) -> &mut SeatState { &mut self.seat_state } - fn new_seat(&mut self, _conn: &Connection, _qh: &QueueHandle, _seat: wayland_client::protocol::wl_seat::WlSeat) {} - fn new_capability(&mut self, _conn: &Connection, qh: &QueueHandle, seat: wayland_client::protocol::wl_seat::WlSeat, capability: smithay_client_toolkit::seat::Capability) { + fn seat_state(&mut self) -> &mut SeatState { + &mut self.seat_state + } + fn new_seat( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _seat: wayland_client::protocol::wl_seat::WlSeat, + ) { + } + fn new_capability( + &mut self, + _conn: &Connection, + qh: &QueueHandle, + seat: wayland_client::protocol::wl_seat::WlSeat, + capability: smithay_client_toolkit::seat::Capability, + ) { if capability == smithay_client_toolkit::seat::Capability::Keyboard { - let _ = self.seat_state.get_keyboard_with_repeat(qh, &seat, None, self.loop_handle.clone(), Box::new(|_state, _kbd, _event| {})); + let _ = self.seat_state.get_keyboard_with_repeat( + qh, + &seat, + None, + self.loop_handle.clone(), + Box::new(|_state, _kbd, _event| {}), + ); } } - fn remove_capability(&mut self, _conn: &Connection, _qh: &QueueHandle, _seat: wayland_client::protocol::wl_seat::WlSeat, _capability: smithay_client_toolkit::seat::Capability) {} - fn remove_seat(&mut self, _conn: &Connection, _qh: &QueueHandle, _seat: wayland_client::protocol::wl_seat::WlSeat) {} + fn remove_capability( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _seat: wayland_client::protocol::wl_seat::WlSeat, + _capability: smithay_client_toolkit::seat::Capability, + ) { + } + fn remove_seat( + &mut self, + _conn: &Connection, + _qh: &QueueHandle, + _seat: wayland_client::protocol::wl_seat::WlSeat, + ) { + } } impl Dispatch for WaylandLock { - fn event(_state: &mut Self, _proxy: &ZwlrScreencopyManagerV1, _event: ::Event, _data: &(), _conn: &Connection, _qh: &QueueHandle) {} + fn event( + _state: &mut Self, + _proxy: &ZwlrScreencopyManagerV1, + _event: ::Event, + _data: &(), + _conn: &Connection, + _qh: &QueueHandle, + ) { + } } impl Dispatch for WaylandLock { - fn event(state: &mut Self, frame: &ZwlrScreencopyFrameV1, event: ::Event, data: &CaptureData, _conn: &Connection, _qh: &QueueHandle) { + fn event( + state: &mut Self, + frame: &ZwlrScreencopyFrameV1, + event: ::Event, + data: &CaptureData, + _conn: &Connection, + _qh: &QueueHandle, + ) { use wayland_protocols_wlr::screencopy::v1::client::zwlr_screencopy_frame_v1::Event; match event { - Event::Buffer { format, width, height, stride } => { + Event::Buffer { + format, + width, + height, + stride, + } => { let format = format.into_result().unwrap(); let mut info = data.info.lock().unwrap(); - *info = Some(screenshot::BufferInfo { width, height, stride, format }); - match state.pool.create_buffer(width as i32, height as i32, stride as i32, format) { + *info = Some(screenshot::BufferInfo { + width, + height, + stride, + format, + }); + match state + .pool + .create_buffer(width as i32, height as i32, stride as i32, format) + { Ok((buffer, _canvas)) => { frame.copy(buffer.wl_buffer()); *data.buffer.lock().unwrap() = Some(buffer); @@ -303,7 +471,9 @@ impl Dispatch for WaylandLock { Err(e) => log::error!("Screencopy: Buffer creation failed: {:?}", e), } } - Event::Flags { flags } => { *data.flags.lock().unwrap() = Some(flags.into_result().unwrap()); } + Event::Flags { flags } => { + *data.flags.lock().unwrap() = Some(flags.into_result().unwrap()); + } Event::Ready { .. } => { log::info!("Screencopy: Ready for output {}", data.output_idx); if let Some(mgr) = &state.screenshot_manager { @@ -311,7 +481,11 @@ impl Dispatch for WaylandLock { let info = data.info.lock().unwrap().take(); let flags = data.flags.lock().unwrap().take(); if let (Some(buffer), Some(info), Some(flags)) = (buffer, info, flags) { - let handle = screenshot::ScreencopyBufferHandle { buffer, info, y_invert: flags.contains(Flags::YInvert) }; + let handle = screenshot::ScreencopyBufferHandle { + buffer, + info, + y_invert: flags.contains(Flags::YInvert), + }; if let Ok(surface) = mgr.buffer_to_surface(handle, &mut state.pool) { let mut ss = Screenshot::new(surface); let _ = ss.apply_effects(&state.config); @@ -334,8 +508,18 @@ impl Dispatch for WaylandLock { } } -impl wayland_client::Dispatch for WaylandLock { - fn event(_state: &mut Self, _proxy: &wayland_client::protocol::wl_registry::WlRegistry, _event: wayland_client::protocol::wl_registry::Event, _data: &(), _conn: &Connection, _qh: &QueueHandle) {} +impl wayland_client::Dispatch + for WaylandLock +{ + fn event( + _state: &mut Self, + _proxy: &wayland_client::protocol::wl_registry::WlRegistry, + _event: wayland_client::protocol::wl_registry::Event, + _data: &(), + _conn: &Connection, + _qh: &QueueHandle, + ) { + } } smithay_client_toolkit::delegate_compositor!(WaylandLock); @@ -353,12 +537,13 @@ fn main() -> Result<(), Box> { static LOGGER: DualLogger = DualLogger; log::set_logger(&LOGGER).map(|()| log::set_max_level(log::LevelFilter::Debug))?; - log::info!("Starting wayrustlock v{}", env!("CARGO_PKG_VERSION")); + log::info!("Starting rustlock v{}", env!("CARGO_PKG_VERSION")); let lock_manager = Arc::new(Mutex::new(LockManager::new(config.clone()))); let ctrlc_exit = Arc::new(std::sync::atomic::AtomicBool::new(false)); let conn = Connection::connect_to_env()?; - let (globals, mut event_queue) = wayland_client::globals::registry_queue_init::(&conn)?; + let (globals, mut event_queue) = + wayland_client::globals::registry_queue_init::(&conn)?; let qh: QueueHandle = event_queue.handle(); let shm_state = Shm::bind(&globals, &qh).map_err(|_| "wl_shm not supported")?; @@ -389,35 +574,57 @@ fn main() -> Result<(), Box> { exit: false, unlocking: false, screenshot_manager: ScreenshotManager::new(&globals, &qh).ok(), + grace_until: None, }; event_queue.blocking_dispatch(&mut state)?; - let _wayland_source = WaylandSource::new(conn.clone(), event_queue).insert(event_loop.handle())?; + let _wayland_source = + WaylandSource::new(conn.clone(), event_queue).insert(event_loop.handle())?; - event_loop.handle().insert_source(auth_feedback_rx_actual, |event, _, state| { - if let calloop::channel::Event::Msg(success) = event { - state.handle_auth_result(success); - if success { state.lock_surfaces.clear(); } - } - })?; + event_loop + .handle() + .insert_source(auth_feedback_rx_actual, |event, _, state| { + if let calloop::channel::Event::Msg(success) = event { + state.handle_auth_result(success); + if success { + state.lock_surfaces.clear(); + } + } + })?; let timer = calloop::timer::Timer::from_duration(Duration::from_millis(16)); event_loop.handle().insert_source(timer, |_, _, state| { + // Clear expired grace period + if let Some(grace_until) = state.grace_until { + if Instant::now() >= grace_until { + state.grace_until = None; + } + } + if state.unlocking { return calloop::timer::TimeoutAction::ToDuration(Duration::from_millis(100)); } if let Ok(mut lm) = state.lock_manager.lock() { lm.update(); - for surface in &mut lm.surfaces { let _ = surface.commit(&mut state.pool); } + for surface in &mut lm.surfaces { + let _ = surface.commit(&mut state.pool); + } + } + if state.exit { + calloop::timer::TimeoutAction::Drop + } else { + calloop::timer::TimeoutAction::ToDuration(Duration::from_millis(16)) } - if state.exit { calloop::timer::TimeoutAction::Drop } else { calloop::timer::TimeoutAction::ToDuration(Duration::from_millis(16)) } })?; if state.config.screenshots { state.outputs = state.output_state.outputs().collect(); - log::info!("Capturing screenshots for {} outputs...", state.outputs.len()); + log::info!( + "Capturing screenshots for {} outputs...", + state.outputs.len() + ); state.screenshot_frames = vec![None; state.outputs.len()]; state.captured_backgrounds = vec![None; state.outputs.len()]; state.pending_screenshots = state.outputs.len(); @@ -427,11 +634,16 @@ fn main() -> Result<(), Box> { let data = CaptureData::new(i); match mgr.capture_output(output, &qh, data) { Ok(frame) => state.screenshot_frames[i] = Some(frame), - Err(e) => { log::error!("Screencopy failed for output {}: {:?}", i, e); state.pending_screenshots -= 1; } + Err(e) => { + log::error!("Screencopy failed for output {}: {:?}", i, e); + state.pending_screenshots -= 1; + } } - } else { state.pending_screenshots -= 1; } + } else { + state.pending_screenshots -= 1; + } } - + let start = std::time::Instant::now(); while state.pending_screenshots > 0 && start.elapsed() < Duration::from_secs(2) { event_loop.dispatch(Duration::from_millis(50), &mut state)?; @@ -440,7 +652,10 @@ fn main() -> Result<(), Box> { } log::info!("Attempting to lock Wayland session..."); - let session_lock = state.session_lock_state.lock(&qh).map_err(|e| { log::error!("Lock failed: {}", e); e })?; + let session_lock = state.session_lock_state.lock(&qh).map_err(|e| { + log::error!("Lock failed: {}", e); + e + })?; let outputs_to_lock: Vec = state.output_state.outputs().collect(); for output in outputs_to_lock { @@ -448,11 +663,15 @@ fn main() -> Result<(), Box> { let (width, height) = state.get_output_dimensions(&output); let lock_surface = session_lock.create_lock_surface(surface.clone(), &output, &qh); state.lock_surfaces.push(lock_surface); - if !state.outputs.contains(&output) { state.outputs.push(output.clone()); } + if !state.outputs.contains(&output) { + state.outputs.push(output.clone()); + } if let Ok(mut lm) = state.lock_manager.lock() { lm.add_surface(width, height, output.clone()); let count = lm.surface_count(); - if let Some(ls) = lm.get_surface_mut(count - 1) { ls.set_wayland_surface(surface); } + if let Some(ls) = lm.get_surface_mut(count - 1) { + ls.set_wayland_surface(surface); + } } } state.session_lock = Some(session_lock); @@ -461,6 +680,6 @@ fn main() -> Result<(), Box> { event_loop.dispatch(Duration::from_millis(16), &mut state)?; } - log::info!("Exiting wayrustlock"); + log::info!("Exiting rustlock"); Ok(()) }