feat: add cli option and ctrl+u clears password
Code Quality / quality-checks (push) Has been cancelled
Security Scan / security-audit (push) Has been cancelled

- Add `--hide-password` CLI flag
- Show temporary "CLEARED" feedback on password clear
This commit is contained in:
2026-04-10 12:59:39 +02:00
parent caa448680e
commit fbd7aee9d2
5 changed files with 108 additions and 15 deletions
+5 -5
View File
@@ -43,8 +43,8 @@ default = ["networking"]
networking = ["dep:reqwest", "tokio/net"] networking = ["dep:reqwest", "tokio/net"]
[profile.release] [profile.release]
opt-level = "z" # Optimize for size opt-level = "z"
lto = true # Enable Link Time Optimization lto = true
codegen-units = 1 # Reduce parallel code gen to allow better size optimization codegen-units = 1
panic = "abort" # Remove heavy stack unwinding code panic = "abort"
strip = true # Automatically strip symbols and debug info strip = true
+14 -10
View File
@@ -6,13 +6,13 @@ use std::path::PathBuf;
#[derive(Parser, Debug, Clone, Serialize, Deserialize)] #[derive(Parser, Debug, Clone, Serialize, Deserialize)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
pub struct Config { pub struct Config {
#[arg(long, action = clap::ArgAction::Set, num_args = 0..=1, default_value = "false", default_missing_value = "true")] #[arg(long, action = clap::ArgAction::SetTrue)]
pub screenshots: bool, pub screenshots: bool,
#[arg(long, action = clap::ArgAction::Set, num_args = 0..=1, default_value = "false", default_missing_value = "true")] #[arg(long, action = clap::ArgAction::SetTrue)]
pub clock: bool, pub clock: bool,
#[arg(long, action = clap::ArgAction::Set, num_args = 0..=1, default_value = "true", default_missing_value = "true")] #[arg(long, action = clap::ArgAction::SetTrue, default_value_t = true)]
pub indicator: bool, pub indicator: bool,
#[arg(long, default_value = "100")] #[arg(long, default_value = "100")]
@@ -54,7 +54,7 @@ pub struct Config {
#[arg(long, default_value = "E5A445", value_parser = util::parse_hex_color)] #[arg(long, default_value = "E5A445", value_parser = util::parse_hex_color)]
pub caps_lock_text_color: (f64, f64, f64, f64), pub caps_lock_text_color: (f64, f64, f64, f64),
#[arg(long, action = clap::ArgAction::Set, num_args = 0..=1, default_value = "true", default_missing_value = "true")] #[arg(long, action = clap::ArgAction::SetTrue, default_value_t = true)]
pub show_caps_lock_text: bool, pub show_caps_lock_text: bool,
#[arg(long, default_value = "00000000", value_parser = util::parse_hex_color)] #[arg(long, default_value = "00000000", value_parser = util::parse_hex_color)]
@@ -85,22 +85,25 @@ pub struct Config {
#[arg(long)] #[arg(long)]
pub log_file: bool, pub log_file: bool,
#[arg(long, action = clap::ArgAction::Set, num_args = 0..=1, default_value = "true", default_missing_value = "true")] #[arg(long, action = clap::ArgAction::SetTrue, default_value_t = true)]
pub show_media: bool, pub show_media: bool,
#[arg(long, action = clap::ArgAction::Set, num_args = 0..=1, default_value = "true", default_missing_value = "true")] #[arg(long, action = clap::ArgAction::SetTrue, default_value_t = true)]
pub show_battery: bool, pub show_battery: bool,
#[arg(long, action = clap::ArgAction::Set, num_args = 0..=1, default_value = "true", default_missing_value = "true")] #[arg(long, action = clap::ArgAction::SetTrue, default_value_t = true)]
pub show_network: bool, pub show_network: bool,
#[arg(long, action = clap::ArgAction::Set, num_args = 0..=1, default_value = "true", default_missing_value = "true")] #[arg(long, action = clap::ArgAction::SetTrue, default_value_t = true)]
pub show_bluetooth: bool, pub show_bluetooth: bool,
#[arg(long, action = clap::ArgAction::Set, num_args = 0..=1, default_value = "true", default_missing_value = "true")] #[arg(long, action = clap::ArgAction::SetTrue, default_value_t = true)]
pub show_album_art: bool, pub show_album_art: bool,
#[arg(long, action = clap::ArgAction::Set, num_args = 0..=1, default_value = "false", default_missing_value = "true")] #[arg(long, action = clap::ArgAction::SetTrue)]
pub hide_password: bool,
#[arg(long, action = clap::ArgAction::SetTrue)]
pub show_keyboard_layout: bool, pub show_keyboard_layout: bool,
#[arg(long)] #[arg(long)]
@@ -203,6 +206,7 @@ impl Config {
merge_bool(&mut config.show_network, "show_network"); merge_bool(&mut config.show_network, "show_network");
merge_bool(&mut config.show_bluetooth, "show_bluetooth"); merge_bool(&mut config.show_bluetooth, "show_bluetooth");
merge_bool(&mut config.show_album_art, "show_album_art"); merge_bool(&mut config.show_album_art, "show_album_art");
merge_bool(&mut config.hide_password, "hide_password");
merge_bool(&mut config.show_keyboard_layout, "show_keyboard_layout"); merge_bool(&mut config.show_keyboard_layout, "show_keyboard_layout");
if !is_cli("image") { if !is_cli("image") {
+13
View File
@@ -30,6 +30,15 @@ impl InputHandler {
// Update Caps Lock state // Update Caps Lock state
self.caps_lock = modifiers.caps_lock; self.caps_lock = modifiers.caps_lock;
if modifiers.ctrl && keysym == Keysym::u {
if !self.password_buffer.is_empty() {
self.password_buffer.clear();
self.cursor_position = 0;
return InputAction::PasswordCleared;
}
return InputAction::None;
}
// Handle special keys first using keysym // Handle special keys first using keysym
use smithay_client_toolkit::seat::keyboard::Keysym; use smithay_client_toolkit::seat::keyboard::Keysym;
match keysym { match keysym {
@@ -37,6 +46,9 @@ impl InputHandler {
if !self.password_buffer.is_empty() && self.cursor_position > 0 { if !self.password_buffer.is_empty() && self.cursor_position > 0 {
self.cursor_position -= 1; self.cursor_position -= 1;
self.password_buffer.remove(self.cursor_position); self.password_buffer.remove(self.cursor_position);
if self.password_buffer.is_empty() {
return InputAction::PasswordCleared;
}
} }
return InputAction::PasswordChanged; return InputAction::PasswordChanged;
} }
@@ -113,6 +125,7 @@ impl InputHandler {
pub enum InputAction { pub enum InputAction {
None, None,
PasswordChanged, PasswordChanged,
PasswordCleared,
SubmitPassword(Zeroizing<String>), SubmitPassword(Zeroizing<String>),
Cancel, Cancel,
} }
+3
View File
@@ -174,6 +174,9 @@ impl LockedSurface {
self.input_handler.set_key_highlight(); self.input_handler.set_key_highlight();
self.key_highlight_shown = false; self.key_highlight_shown = false;
} }
InputAction::PasswordCleared => {
self.renderer.show_cleared_feedback();
}
InputAction::SubmitPassword(_) => { InputAction::SubmitPassword(_) => {
self.input_handler.set_key_highlight(); self.input_handler.set_key_highlight();
self.key_highlight_shown = false; self.key_highlight_shown = false;
+73
View File
@@ -14,8 +14,10 @@ pub struct Renderer {
fade_alpha: f64, fade_alpha: f64,
wrong_password_shown: bool, wrong_password_shown: bool,
key_highlight_shown: bool, key_highlight_shown: bool,
cleared_feedback_shown: bool,
wrong_password_start: Option<Instant>, wrong_password_start: Option<Instant>,
key_highlight_start: Option<Instant>, key_highlight_start: Option<Instant>,
cleared_feedback_start: Option<Instant>,
key_highlight_angle: f64, key_highlight_angle: f64,
background: Option<ImageSurface>, background: Option<ImageSurface>,
password_display: String, password_display: String,
@@ -51,8 +53,10 @@ impl Renderer {
fade_alpha: 0.0, fade_alpha: 0.0,
wrong_password_shown: false, wrong_password_shown: false,
key_highlight_shown: false, key_highlight_shown: false,
cleared_feedback_shown: false,
wrong_password_start: None, wrong_password_start: None,
key_highlight_start: None, key_highlight_start: None,
cleared_feedback_start: None,
key_highlight_angle: 0.0, key_highlight_angle: 0.0,
background: None, background: None,
password_display: String::new(), password_display: String::new(),
@@ -183,6 +187,12 @@ impl Renderer {
self.key_highlight_angle = ((random_val % 360) as f64).to_radians(); self.key_highlight_angle = ((random_val % 360) as f64).to_radians();
} }
/// Show cleared feedback
pub fn show_cleared_feedback(&mut self) {
self.cleared_feedback_shown = true;
self.cleared_feedback_start = Some(Instant::now());
}
/// Set the password display string (masked) /// Set the password display string (masked)
pub fn set_password_display(&mut self, password: String) { pub fn set_password_display(&mut self, password: String) {
self.password_display = password; self.password_display = password;
@@ -250,6 +260,10 @@ impl Renderer {
self.draw_key_highlight_feedback(); self.draw_key_highlight_feedback();
} }
if self.cleared_feedback_shown {
self.draw_cleared_feedback();
}
self.update_feedback_timers(); self.update_feedback_timers();
} }
@@ -376,6 +390,9 @@ impl Renderer {
} }
fn draw_password_display(&self) { fn draw_password_display(&self) {
if self.config.hide_password {
return;
}
let center_x = self.width as f64 / 2.0; let center_x = self.width as f64 / 2.0;
let center_y = self.height as f64 / 2.0; let center_y = self.height as f64 / 2.0;
let radius = self.config.indicator_radius as f64; let radius = self.config.indicator_radius as f64;
@@ -476,6 +493,56 @@ impl Renderer {
} }
} }
fn draw_cleared_feedback(&self) {
let center_x = self.width as f64 / 2.0;
let center_y = self.height as f64 / 2.0;
let radius = self.config.indicator_radius as f64;
let thickness = self.config.indicator_thickness as f64;
let intensity = if let Some(start) = self.cleared_feedback_start {
let elapsed = start.elapsed();
let duration = std::time::Duration::from_millis(500);
if elapsed < duration {
1.0 - (elapsed.as_secs_f64() / duration.as_secs_f64())
} else {
0.0
}
} else {
0.0
};
if intensity > 0.0 {
self.context.new_path();
self.context
.set_source_rgba(1.0, 0.0, 0.0, intensity * self.fade_alpha * 0.5);
self.context.arc(
center_x,
center_y,
radius - thickness / 2.0,
0.0,
2.0 * std::f64::consts::PI,
);
self.context.fill().unwrap();
self.context.new_path();
self.context
.set_source_rgba(1.0, 0.0, 0.0, intensity * self.fade_alpha);
self.context.set_line_width(thickness + 4.0);
self.context
.arc(center_x, center_y, radius, 0.0, 2.0 * std::f64::consts::PI);
self.context.stroke().unwrap();
self.context.new_path();
self.context.set_font_size(24.0);
self.context
.set_source_rgba(1.0, 1.0, 1.0, intensity * self.fade_alpha);
let text = "CLEARED";
let te = self.context.text_extents(text).unwrap();
self.context
.move_to(center_x - te.width() / 2.0, center_y + te.height() / 2.0);
self.context.show_text(text).unwrap();
}
}
fn update_feedback_timers(&mut self) { fn update_feedback_timers(&mut self) {
self.update_uptime(); self.update_uptime();
if let Some(start) = self.wrong_password_start { if let Some(start) = self.wrong_password_start {
@@ -490,6 +557,12 @@ impl Renderer {
self.key_highlight_start = None; self.key_highlight_start = None;
} }
} }
if let Some(start) = self.cleared_feedback_start {
if start.elapsed() > std::time::Duration::from_millis(500) {
self.cleared_feedback_shown = false;
self.cleared_feedback_start = None;
}
}
} }
fn draw_media(&mut self) { fn draw_media(&mut self) {