271 lines
8.2 KiB
Rust
271 lines
8.2 KiB
Rust
use cairo::ImageSurface;
|
|
use std::error::Error;
|
|
use std::time::Instant;
|
|
use wayland_client::protocol::{wl_output, wl_shm, wl_surface};
|
|
|
|
use crate::config::Config;
|
|
use crate::input::{InputAction, InputHandler};
|
|
use crate::render::Renderer;
|
|
use crate::system::SystemStatus;
|
|
use smithay_client_toolkit::seat::keyboard::KeyEvent;
|
|
use smithay_client_toolkit::shm::slot::SlotPool;
|
|
|
|
/// Manages a locked surface for a single output
|
|
pub struct LockedSurface {
|
|
config: Config,
|
|
pub renderer: Renderer,
|
|
input_handler: InputHandler,
|
|
background: Option<ImageSurface>,
|
|
background_applied: bool,
|
|
fade_alpha: f64,
|
|
wrong_password_shown: bool,
|
|
key_highlight_shown: bool,
|
|
start_time: Instant,
|
|
wayland_surface: Option<wl_surface::WlSurface>,
|
|
output: wl_output::WlOutput,
|
|
configured: bool,
|
|
}
|
|
|
|
impl LockedSurface {
|
|
/// Create a new locked surface for an output
|
|
pub fn new(
|
|
width: i32,
|
|
height: i32,
|
|
config: &Config,
|
|
output: wl_output::WlOutput,
|
|
) -> Option<Self> {
|
|
if width <= 0 || height <= 0 {
|
|
return None;
|
|
}
|
|
|
|
let renderer = Renderer::new(width, height, config.clone());
|
|
let input_handler = InputHandler::new(config.clone());
|
|
|
|
Some(Self {
|
|
config: config.clone(),
|
|
renderer,
|
|
input_handler,
|
|
background: None,
|
|
background_applied: false,
|
|
fade_alpha: 0.0,
|
|
wrong_password_shown: false,
|
|
key_highlight_shown: false,
|
|
start_time: Instant::now(),
|
|
wayland_surface: None,
|
|
output,
|
|
configured: false,
|
|
})
|
|
}
|
|
|
|
/// Set the configured state
|
|
pub fn set_configured(&mut self) {
|
|
log::debug!("LockedSurface: Configured, starting animation");
|
|
self.configured = true;
|
|
self.start_time = Instant::now();
|
|
}
|
|
|
|
/// Check if this surface matches the given Wayland surface
|
|
pub fn matches_surface(&self, surface: &wl_surface::WlSurface) -> bool {
|
|
use wayland_client::Proxy;
|
|
self.wayland_surface
|
|
.as_ref()
|
|
.map_or(false, |ws| ws.id() == surface.id())
|
|
}
|
|
|
|
/// Update the surface state (called on each frame)
|
|
pub fn update(&mut self) {
|
|
self.input_handler.update();
|
|
|
|
if !self.configured {
|
|
return;
|
|
}
|
|
|
|
// Update fade animation
|
|
if self.fade_alpha < 1.0 {
|
|
let elapsed = self.start_time.elapsed();
|
|
let fade_duration = std::time::Duration::from_secs_f32(self.config.fade_in);
|
|
let new_alpha = (elapsed.as_secs_f64() / fade_duration.as_secs_f64()).min(1.0);
|
|
if (new_alpha - self.fade_alpha).abs() > 0.001 {
|
|
self.fade_alpha = new_alpha;
|
|
self.renderer.set_fade_alpha(self.fade_alpha);
|
|
}
|
|
}
|
|
|
|
// Check if we should show/hide wrong password feedback
|
|
if self.input_handler.should_show_wrong_password() && !self.wrong_password_shown {
|
|
self.renderer.show_wrong_password();
|
|
self.wrong_password_shown = true;
|
|
} else if !self.input_handler.should_show_wrong_password() && self.wrong_password_shown {
|
|
self.wrong_password_shown = false;
|
|
}
|
|
|
|
// Check if we should show/hide key highlight feedback
|
|
if self.input_handler.should_show_key_highlight() && !self.key_highlight_shown {
|
|
self.renderer.show_key_highlight();
|
|
self.key_highlight_shown = true;
|
|
} else if !self.input_handler.should_show_key_highlight() && self.key_highlight_shown {
|
|
self.key_highlight_shown = false;
|
|
}
|
|
|
|
// Update caps lock state in renderer
|
|
self.renderer.caps_lock = self.input_handler.caps_lock();
|
|
|
|
// Set background if available and not already applied
|
|
if !self.background_applied {
|
|
if let Some(ref background) = self.background {
|
|
log::info!("Applying background image to renderer");
|
|
self.renderer.set_background(background.clone());
|
|
self.background_applied = true;
|
|
}
|
|
}
|
|
|
|
self.renderer
|
|
.set_password_display(self.input_handler.get_display_password());
|
|
self.renderer.render();
|
|
}
|
|
|
|
/// Commit the rendered frame to the Wayland surface
|
|
pub fn commit(&self, pool: &mut SlotPool) -> Result<(), Box<dyn Error>> {
|
|
if !self.configured {
|
|
return Ok(());
|
|
}
|
|
|
|
let pixel_data = self.renderer.get_pixel_data()?;
|
|
let (width, height, stride) = self.renderer.surface_info();
|
|
|
|
let (buffer, canvas) =
|
|
pool.create_buffer(width, height, stride, wl_shm::Format::Argb8888)?;
|
|
|
|
let copy_len = pixel_data.len().min(canvas.len());
|
|
canvas[..copy_len].copy_from_slice(&pixel_data[..copy_len]);
|
|
|
|
if let Some(wl_surface) = &self.wayland_surface {
|
|
buffer.attach_to(wl_surface)?;
|
|
wl_surface.damage_buffer(0, 0, width, height);
|
|
wl_surface.commit();
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn resize(&mut self, width: i32, height: i32) {
|
|
if width <= 0 || height <= 0 {
|
|
return;
|
|
}
|
|
self.renderer.resize(width, height);
|
|
self.background_applied = false;
|
|
}
|
|
|
|
pub fn show_wrong_password(&mut self) {
|
|
self.input_handler.set_wrong_password_feedback();
|
|
}
|
|
|
|
pub fn handle_key_event(
|
|
&mut self,
|
|
event: smithay_client_toolkit::seat::keyboard::KeyEvent,
|
|
modifiers: smithay_client_toolkit::seat::keyboard::Modifiers,
|
|
) -> Option<InputAction> {
|
|
let action = self
|
|
.input_handler
|
|
.handle_key_event(event.keysym, event.utf8, modifiers);
|
|
|
|
match action {
|
|
InputAction::PasswordChanged => {
|
|
self.input_handler.set_key_highlight();
|
|
self.key_highlight_shown = false;
|
|
}
|
|
InputAction::SubmitPassword(_) => {
|
|
self.input_handler.set_key_highlight();
|
|
self.key_highlight_shown = false;
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
Some(action)
|
|
}
|
|
|
|
pub fn set_wayland_surface(&mut self, surface: wl_surface::WlSurface) {
|
|
self.wayland_surface = Some(surface);
|
|
}
|
|
|
|
pub fn output(&self) -> &wl_output::WlOutput {
|
|
&self.output
|
|
}
|
|
|
|
pub fn set_background(&mut self, surface: ImageSurface) {
|
|
self.background = Some(surface);
|
|
self.background_applied = false;
|
|
}
|
|
|
|
pub fn set_system_status(&mut self, status: SystemStatus) {
|
|
self.renderer.system_status = status;
|
|
}
|
|
}
|
|
|
|
pub struct LockManager {
|
|
pub surfaces: Vec<LockedSurface>,
|
|
config: Config,
|
|
}
|
|
|
|
impl LockManager {
|
|
pub fn new(config: Config) -> Self {
|
|
Self {
|
|
surfaces: Vec::new(),
|
|
config,
|
|
}
|
|
}
|
|
|
|
pub fn add_surface(&mut self, width: i32, height: i32, output: wl_output::WlOutput) -> bool {
|
|
match LockedSurface::new(width, height, &self.config, output) {
|
|
Some(surface) => {
|
|
self.surfaces.push(surface);
|
|
true
|
|
}
|
|
None => false,
|
|
}
|
|
}
|
|
|
|
pub fn update(&mut self) {
|
|
for surface in &mut self.surfaces {
|
|
surface.update();
|
|
}
|
|
}
|
|
|
|
pub fn surface_count(&self) -> usize {
|
|
self.surfaces.len()
|
|
}
|
|
|
|
pub fn get_surface_mut(&mut self, index: usize) -> Option<&mut LockedSurface> {
|
|
self.surfaces.get_mut(index)
|
|
}
|
|
|
|
pub fn find_surface_by_wayland_surface(
|
|
&mut self,
|
|
wayland_surface: &wl_surface::WlSurface,
|
|
) -> Option<&mut LockedSurface> {
|
|
self.surfaces
|
|
.iter_mut()
|
|
.find(|surface| surface.matches_surface(wayland_surface))
|
|
}
|
|
|
|
pub fn handle_key_event(
|
|
&mut self,
|
|
event: KeyEvent,
|
|
modifiers: smithay_client_toolkit::seat::keyboard::Modifiers,
|
|
) -> Option<InputAction> {
|
|
let mut action = None;
|
|
for surface in &mut self.surfaces {
|
|
if let Some(a) = surface.handle_key_event(event.clone(), modifiers) {
|
|
action = Some(a);
|
|
}
|
|
}
|
|
action
|
|
}
|
|
|
|
pub fn set_system_status(&mut self, status: SystemStatus) {
|
|
for surface in &mut self.surfaces {
|
|
surface.set_system_status(status.clone());
|
|
}
|
|
}
|
|
}
|