use zeroize::Zeroizing; /// Handles keyboard input for password entry pub struct InputHandler { password_buffer: Zeroizing, cursor_position: usize, config: crate::config::Config, wrong_password_timer: Option, key_highlight_timer: Option, temp_screenshot_timer: Option, temp_screenshot_active: bool, caps_lock: bool, } impl InputHandler { pub fn new(config: crate::config::Config) -> Self { Self { password_buffer: Zeroizing::new(String::new()), cursor_position: 0, config, wrong_password_timer: None, key_highlight_timer: None, temp_screenshot_timer: None, temp_screenshot_active: false, caps_lock: false, } } /// Handle a key event from Wayland pub fn handle_key_event( &mut self, keysym: smithay_client_toolkit::seat::keyboard::Keysym, state: wayland_client::protocol::wl_keyboard::KeyState, modifiers: smithay_client_toolkit::seat::keyboard::Modifiers, ) -> InputAction { // Update Caps Lock state self.caps_lock = modifiers.caps_lock; // Only process key press events if state != wayland_client::protocol::wl_keyboard::KeyState::Pressed { return InputAction::None; } // Convert keysym to character let ch = self.keysym_to_char(keysym, modifiers); match ch { Some('\x08') | Some('\x7f') => { // Backspace or Delete if !self.password_buffer.is_empty() && self.cursor_position > 0 { self.cursor_position -= 1; self.password_buffer.remove(self.cursor_position); } InputAction::PasswordChanged } Some('\r') | Some('\n') => { // Enter key - submit password let password = self.password_buffer.clone(); self.password_buffer.clear(); self.cursor_position = 0; InputAction::SubmitPassword(password) } Some('\x1b') => { // Escape key - cancel InputAction::Cancel } Some('p') | Some('P') if self.config.temp_screenshot => { // 'p' key for temp screenshot peek self.activate_temp_screenshot(); InputAction::TempScreenshot } Some(c) if c.is_ascii() && !c.is_control() => { // Printable ASCII character self.password_buffer.insert(self.cursor_position, c); self.cursor_position += 1; InputAction::PasswordChanged } _ => { // Other keys (function keys, arrows, etc.) InputAction::None } } } /// Convert a keysym to a character, considering modifiers fn keysym_to_char( &self, keysym: smithay_client_toolkit::seat::keyboard::Keysym, modifiers: smithay_client_toolkit::seat::keyboard::Modifiers, ) -> Option { use smithay_client_toolkit::seat::keyboard::Keysym; // Handle special keys first match keysym { Keysym::BackSpace => return Some('\x08'), Keysym::Delete => return Some('\x7f'), Keysym::Return => return Some('\r'), Keysym::KP_Enter => return Some('\n'), Keysym::Escape => return Some('\x1b'), _ => {} } // Convert keysym to character let keysym_value = keysym.raw(); // Basic ASCII conversion (simplified - real implementation would use xkbcommon) // This is a simplified mapping for demonstration if keysym_value >= 0x20 && keysym_value <= 0x7e { let mut ch = keysym_value as u8 as char; // Apply shift modifier if modifiers.shift { ch = match ch { '`' => '~', '1' => '!', '2' => '@', '3' => '#', '4' => '$', '5' => '%', '6' => '^', '7' => '&', '8' => '*', '9' => '(', '0' => ')', '-' => '_', '=' => '+', '[' => '{', ']' => '}', '\\' => '|', ';' => ':', '\'' => '"', ',' => '<', '.' => '>', '/' => '?', c if c.is_ascii_lowercase() => c.to_ascii_uppercase(), _ => ch, }; } Some(ch) } else { None } } /// Get the current password (for display purposes only - returns masked version) pub fn get_display_password(&self) -> String { self.password_buffer.chars().map(|_| '•').collect() } /// Set wrong password feedback timer pub fn set_wrong_password_feedback(&mut self) { self.wrong_password_timer = Some(std::time::Instant::now()); } /// Check if wrong password feedback should be shown pub fn should_show_wrong_password(&self) -> bool { if let Some(timer) = self.wrong_password_timer { timer.elapsed() < std::time::Duration::from_millis(1000) } else { false } } /// Set key highlight timer (for visual feedback) pub fn set_key_highlight(&mut self) { self.key_highlight_timer = Some(std::time::Instant::now()); } /// Check if key highlight should be shown pub fn should_show_key_highlight(&self) -> bool { if let Some(timer) = self.key_highlight_timer { timer.elapsed() < std::time::Duration::from_millis(200) } else { false } } /// Update timers (should be called periodically) pub fn update(&mut self) { // Update temp screenshot state self.update_temp_screenshot(); } /// Activate temporary screenshot display (peek feature) pub fn activate_temp_screenshot(&mut self) { self.temp_screenshot_timer = Some(std::time::Instant::now()); self.temp_screenshot_active = true; } /// Check if temporary screenshot should be shown pub fn should_show_temp_screenshot(&self) -> bool { if let Some(timer) = self.temp_screenshot_timer { let elapsed = timer.elapsed(); // Show for 2 seconds if elapsed < std::time::Duration::from_secs(2) { return true; } } false } /// Update temp screenshot state (call periodically) pub fn update_temp_screenshot(&mut self) { if self.temp_screenshot_active && !self.should_show_temp_screenshot() { self.temp_screenshot_active = false; self.temp_screenshot_timer = None; } } } /// Actions that can result from keyboard input #[derive(Debug)] pub enum InputAction { None, PasswordChanged, SubmitPassword(Zeroizing), Cancel, TempScreenshot, }