Files
rustlock/src/input.rs
T

221 lines
7.2 KiB
Rust

use zeroize::Zeroizing;
/// Handles keyboard input for password entry
pub struct InputHandler {
password_buffer: Zeroizing<String>,
cursor_position: usize,
config: crate::config::Config,
wrong_password_timer: Option<std::time::Instant>,
key_highlight_timer: Option<std::time::Instant>,
temp_screenshot_timer: Option<std::time::Instant>,
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<char> {
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<String>),
Cancel,
TempScreenshot,
}