add password + enter for worf warden

This commit is contained in:
Alexander Mohr 2025-05-16 22:31:58 +02:00
parent 6632f2f123
commit ab56048574
3 changed files with 79 additions and 61 deletions

View file

@ -44,7 +44,7 @@ impl PasswordProvider {
key.clone(), key.clone(),
None, None,
None, None,
vec![], vec![].into_iter().collect(),
None, None,
0.0, 0.0,
Some(MenuItemMetaData { ids: value.clone() }), Some(MenuItemMetaData { ids: value.clone() }),
@ -59,7 +59,7 @@ impl PasswordProvider {
format!("Error from rbw: {error}"), format!("Error from rbw: {error}"),
None, None,
None, None,
vec![], vec![].into_iter().collect(),
None, None,
0.0, 0.0,
None, None,
@ -79,11 +79,11 @@ impl PasswordProvider {
rbw_get_user(id, false)?, rbw_get_user(id, false)?,
None, None,
None, None,
vec![], vec![].into_iter().collect(),
None, None,
0.0, 0.0,
Some(MenuItemMetaData { Some(MenuItemMetaData {
ids: vec![id.clone()], ids: vec![id.clone()].into_iter().collect(),
}), }),
)) ))
}) })
@ -125,7 +125,16 @@ fn keyboard_type(text: &str) {
fn keyboard_tab() { fn keyboard_tab() {
Command::new("ydotool") Command::new("ydotool")
.arg("TAB") .arg("type")
.arg("\t")
.output()
.expect("Failed to execute ydotool");
}
fn keyboard_return() {
Command::new("ydotool")
.arg("type")
.arg("\n")
.output() .output()
.expect("Failed to execute ydotool"); .expect("Failed to execute ydotool");
} }
@ -178,7 +187,7 @@ fn rbw_get_totp(id: &str, copy: bool) -> Result<String, String> {
fn key_type_all() -> KeyBinding { fn key_type_all() -> KeyBinding {
KeyBinding { KeyBinding {
key: Key::Num1, key: Key::Num1,
modifiers: Modifier::Alt, modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+1</b> Type All".to_string(), label: "<b>Alt+1</b> Type All".to_string(),
} }
} }
@ -186,7 +195,7 @@ fn key_type_all() -> KeyBinding {
fn key_type_user() -> KeyBinding { fn key_type_user() -> KeyBinding {
KeyBinding { KeyBinding {
key: Key::Num2, key: Key::Num2,
modifiers: Modifier::Alt, modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+2</b> Type User".to_string(), label: "<b>Alt+2</b> Type User".to_string(),
} }
} }
@ -194,15 +203,23 @@ fn key_type_user() -> KeyBinding {
fn key_type_password() -> KeyBinding { fn key_type_password() -> KeyBinding {
KeyBinding { KeyBinding {
key: Key::Num3, key: Key::Num3,
modifiers: Modifier::Alt, modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+3</b> Type Password".to_string(), label: "<b>Alt+3</b> Type Password".to_string(),
} }
} }
fn key_type_password_and_enter() -> KeyBinding {
KeyBinding {
key: Key::Hash,
modifiers: vec![Modifier::Alt, Modifier::Shift].into_iter().collect(),
label: "<b>Alt+Shift+3</b> Type Password + Enter".to_string(),
}
}
fn key_type_totp() -> KeyBinding { fn key_type_totp() -> KeyBinding {
KeyBinding { KeyBinding {
key: Key::Num4, key: Key::Num4,
modifiers: Modifier::Alt, modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+4</b> Type Totp".to_string(), label: "<b>Alt+4</b> Type Totp".to_string(),
} }
} }
@ -210,7 +227,7 @@ fn key_type_totp() -> KeyBinding {
fn key_sync() -> KeyBinding { fn key_sync() -> KeyBinding {
KeyBinding { KeyBinding {
key: Key::R, key: Key::R,
modifiers: Modifier::Alt, modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+r</b> Sync".to_string(), label: "<b>Alt+r</b> Sync".to_string(),
} }
} }
@ -219,7 +236,7 @@ fn key_sync() -> KeyBinding {
fn key_totp() -> KeyBinding { fn key_totp() -> KeyBinding {
KeyBinding { KeyBinding {
key: Key::T, key: Key::T,
modifiers: Modifier::Alt, modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+t</b> Totp".to_string(), label: "<b>Alt+t</b> Totp".to_string(),
} }
} }
@ -227,7 +244,7 @@ fn key_totp() -> KeyBinding {
fn key_lock() -> KeyBinding { fn key_lock() -> KeyBinding {
KeyBinding { KeyBinding {
key: Key::L, key: Key::L,
modifiers: Modifier::Alt, modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+l</b> Lock".to_string(), label: "<b>Alt+l</b> Lock".to_string(),
} }
} }
@ -242,6 +259,7 @@ fn show(config: Config, provider: PasswordProvider) -> Result<(), String> {
key_type_all(), key_type_all(),
key_type_user(), key_type_user(),
key_type_password(), key_type_password(),
key_type_password_and_enter(),
key_type_totp(), key_type_totp(),
key_sync(), key_sync(),
key_totp(), key_totp(),
@ -256,7 +274,7 @@ fn show(config: Config, provider: PasswordProvider) -> Result<(), String> {
let id = meta.ids.first().unwrap_or(&selection.menu.label); let id = meta.ids.first().unwrap_or(&selection.menu.label);
sleep(Duration::from_millis(250)); sleep(Duration::from_millis(500));
if let Some(key) = selection.custom_key { if let Some(key) = selection.custom_key {
if key == key_type_all() { if key == key_type_all() {
keyboard_type(&rbw_get_user(id, false)?); keyboard_type(&rbw_get_user(id, false)?);
@ -266,6 +284,9 @@ fn show(config: Config, provider: PasswordProvider) -> Result<(), String> {
keyboard_type(&rbw_get_user(id, false)?); keyboard_type(&rbw_get_user(id, false)?);
} else if key == key_type_password() { } else if key == key_type_password() {
keyboard_type(&rbw_get_password(id, false)?); keyboard_type(&rbw_get_password(id, false)?);
} else if key == key_type_password_and_enter() {
keyboard_type(&rbw_get_password(id, false)?);
keyboard_return();
} else if key == key_type_totp() { } else if key == key_type_totp() {
keyboard_type(&rbw_get_totp(id, false)?); keyboard_type(&rbw_get_totp(id, false)?);
} else if key == key_lock() { } else if key == key_lock() {

View file

@ -1,12 +1,11 @@
use std::collections::HashMap; use std::collections::{HashMap, HashSet};
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use std::time::{Duration, Instant}; use std::time::Instant;
use crossbeam::channel; use crossbeam::channel;
use crossbeam::channel::Sender; use crossbeam::channel::Sender;
use crossbeam::channel::internal::SelectHandle;
use gdk4::Display; use gdk4::Display;
use gdk4::gio::File; use gdk4::gio::File;
use gdk4::glib::{MainContext, Propagation}; use gdk4::glib::{MainContext, Propagation};
@ -339,24 +338,39 @@ pub enum Modifier {
None, None,
} }
impl From<gdk4::ModifierType> for Modifier { fn modifiers_from_mask(mask: gdk4::ModifierType) -> HashSet<Modifier> {
fn from(value: gdk4::ModifierType) -> Self { let mut modifiers = HashSet::new();
match value {
gdk4::ModifierType::SHIFT_MASK => Modifier::Shift, if mask.contains(gdk4::ModifierType::SHIFT_MASK) {
gdk4::ModifierType::CONTROL_MASK => Modifier::Control, modifiers.insert(Modifier::Shift);
gdk4::ModifierType::ALT_MASK => Modifier::Alt,
gdk4::ModifierType::SUPER_MASK => Modifier::Super,
gdk4::ModifierType::META_MASK => Modifier::Meta,
gdk4::ModifierType::LOCK_MASK => Modifier::CapsLock,
_ => Modifier::None,
}
} }
if mask.contains(gdk4::ModifierType::CONTROL_MASK) {
modifiers.insert(Modifier::Control);
}
if mask.contains(gdk4::ModifierType::ALT_MASK) {
modifiers.insert(Modifier::Alt);
}
if mask.contains(gdk4::ModifierType::SUPER_MASK) {
modifiers.insert(Modifier::Super);
}
if mask.contains(gdk4::ModifierType::META_MASK) {
modifiers.insert(Modifier::Meta);
}
if mask.contains(gdk4::ModifierType::LOCK_MASK) {
modifiers.insert(Modifier::CapsLock);
}
if modifiers.is_empty() {
modifiers.insert(Modifier::None);
}
modifiers
} }
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq, Debug)]
pub struct KeyBinding { pub struct KeyBinding {
pub key: Key, pub key: Key,
pub modifiers: Modifier, // todo support masks pub modifiers: HashSet<Modifier>,
pub label: String, pub label: String,
} }
@ -566,22 +580,19 @@ fn build_ui<T, P>(
let (_changed, provider_elements) = get_provider_elements.join().unwrap(); let (_changed, provider_elements) = get_provider_elements.join().unwrap();
log::debug!("got items after {:?}", wait_for_items.elapsed()); log::debug!("got items after {:?}", wait_for_items.elapsed());
let animate_cfg = config.clone(); let active_cfg = config.clone();
let map_cfg = config.clone();
let animate_window = ui_elements.window.clone(); let animate_window = ui_elements.window.clone();
let (sender, receiver) = channel::bounded(1);
animate_window.connect_is_active_notify(move |w| { animate_window.connect_is_active_notify(move |w| {
w.set_opacity(1.0); window_show_resize(&active_cfg.clone(), w);
window_show_resize(&animate_cfg.clone(), w); });
if let Err(e) = sender.send(()) { animate_window.connect_map(move |w| {
log::debug!("cannot unblock menu builder {e:?}"); window_show_resize(&map_cfg.clone(), w);
}
}); });
build_ui_from_menu_items(&ui_elements, &meta, provider_elements, Some(receiver)); build_ui_from_menu_items(&ui_elements, &meta, provider_elements);
// hide the fact that we are starting with a small window
ui_elements.window.set_opacity(0.01);
let window_start = Instant::now(); let window_start = Instant::now();
ui_elements.window.present(); ui_elements.window.present();
log::debug!("window show took {:?}", window_start.elapsed()); log::debug!("window show took {:?}", window_start.elapsed());
@ -685,7 +696,6 @@ fn build_ui_from_menu_items<T: Clone + 'static + Send>(
ui: &Rc<UiElements<T>>, ui: &Rc<UiElements<T>>,
meta: &Rc<MetaData<T>>, meta: &Rc<MetaData<T>>,
mut items: Vec<MenuItem<T>>, mut items: Vec<MenuItem<T>>,
wait_for_signal: Option<channel::Receiver<()>>,
) { ) {
let start = Instant::now(); let start = Instant::now();
{ {
@ -699,12 +709,6 @@ fn build_ui_from_menu_items<T: Clone + 'static + Send>(
let ui_clone = Rc::<UiElements<T>>::clone(ui); let ui_clone = Rc::<UiElements<T>>::clone(ui);
glib::idle_add_local(move || { glib::idle_add_local(move || {
if let Some(wait) = &wait_for_signal {
if !wait.is_ready() {
return ControlFlow::Continue;
}
}
ui_clone.main_box.unset_sort_func(); ui_clone.main_box.unset_sort_func();
let mut done = false; let mut done = false;
{ {
@ -797,15 +801,18 @@ fn handle_key_press<T: Clone + 'static + Send>(
let update_view_from_provider = |query: &String| { let update_view_from_provider = |query: &String| {
let (changed, filtered_list) = meta.item_provider.lock().unwrap().get_elements(Some(query)); let (changed, filtered_list) = meta.item_provider.lock().unwrap().get_elements(Some(query));
if changed { if changed {
build_ui_from_menu_items(ui, meta, filtered_list, None); build_ui_from_menu_items(ui, meta, filtered_list);
} }
update_view(query); update_view(query);
}; };
if let Some(custom_keys) = custom_keys { if let Some(custom_keys) = custom_keys {
let mods = modifiers_from_mask(modifier_type);
for custom_key in custom_keys { for custom_key in custom_keys {
if custom_key.key == keyboard_key.into() && custom_key.modifiers == modifier_type.into() log::debug!(
{ "comparing custom key {custom_key:?} to mask {mods:?} and key {keyboard_key}"
);
if custom_key.key == keyboard_key.into() && mods.is_subset(&custom_key.modifiers) {
let search_lock = ui.search_text.lock().unwrap(); let search_lock = ui.search_text.lock().unwrap();
if let Err(e) = handle_selected_item( if let Err(e) = handle_selected_item(
ui, ui,
@ -874,7 +881,7 @@ fn handle_key_press<T: Clone + 'static + Send>(
if let Some(changed) = opt_changed { if let Some(changed) = opt_changed {
let items = changed.0.1.unwrap_or_default(); let items = changed.0.1.unwrap_or_default();
if changed.0.0 { if changed.0.0 {
build_ui_from_menu_items(ui, meta, items, None); build_ui_from_menu_items(ui, meta, items);
} }
let query = changed.1; let query = changed.1;

View file

@ -1,8 +1,7 @@
use crate::config::Config; use crate::config::Config;
use crate::desktop::{copy_to_clipboard, spawn_fork}; use crate::desktop::spawn_fork;
use crate::gui::{ItemProvider, MenuItem}; use crate::gui::{ItemProvider, MenuItem};
use crate::modes::drun::{DRunProvider, update_drun_cache_and_run}; use crate::modes::drun::{DRunProvider, update_drun_cache_and_run};
use crate::modes::emoji::EmojiProvider;
use crate::modes::file::FileItemProvider; use crate::modes::file::FileItemProvider;
use crate::modes::math::MathProvider; use crate::modes::math::MathProvider;
use crate::modes::ssh; use crate::modes::ssh;
@ -16,7 +15,6 @@ enum AutoRunType {
DRun, DRun,
File, File,
Ssh, Ssh,
Emoji,
// WebSearch, // WebSearch,
} }
@ -152,14 +150,6 @@ pub fn show(config: &Config) -> Result<(), Error> {
ssh::launch(&selection_result, config)?; ssh::launch(&selection_result, config)?;
break; break;
} }
AutoRunType::Emoji => {
if let Some(action) = selection_result.action {
copy_to_clipboard(action, None)?;
} else {
return Err(Error::MissingAction);
}
break;
}
} }
} else if selection_result.label.starts_with("ssh") { } else if selection_result.label.starts_with("ssh") {
selection_result.label = selection_result.label.chars().skip(4).collect(); selection_result.label = selection_result.label.chars().skip(4).collect();