improve vaultwarden

This commit is contained in:
Alexander Mohr 2025-05-05 21:18:48 +02:00
parent c040a28539
commit 344433e697
4 changed files with 107 additions and 340 deletions

285
Cargo.lock generated
View file

@ -96,15 +96,6 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "block2"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
dependencies = [
"objc2",
]
[[package]] [[package]]
name = "cairo-rs" name = "cairo-rs"
version = "0.20.7" version = "0.20.7"
@ -230,46 +221,6 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "core-foundation"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63"
dependencies = [
"core-foundation-sys",
"libc",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "core-graphics"
version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
dependencies = [
"bitflags 2.9.0",
"core-foundation",
"core-graphics-types",
"foreign-types",
"libc",
]
[[package]]
name = "core-graphics-types"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
dependencies = [
"bitflags 2.9.0",
"core-foundation",
"libc",
]
[[package]] [[package]]
name = "crossbeam" name = "crossbeam"
version = "0.8.4" version = "0.8.4"
@ -371,25 +322,6 @@ dependencies = [
"phf", "phf",
] ]
[[package]]
name = "enigo"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cf6f550bbbdd5fe66f39d429cb2604bcdacbf00dca0f5bbe2e9306a0009b7c6"
dependencies = [
"core-foundation",
"core-graphics",
"foreign-types-shared",
"libc",
"log",
"objc2",
"objc2-app-kit",
"objc2-foundation",
"windows",
"xkbcommon",
"xkeysym",
]
[[package]] [[package]]
name = "env_filter" name = "env_filter"
version = "0.1.3" version = "0.1.3"
@ -463,33 +395,6 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foreign-types"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
dependencies = [
"foreign-types-macros",
"foreign-types-shared",
]
[[package]]
name = "foreign-types-macros"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]]
name = "foreign-types-shared"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
[[package]] [[package]]
name = "freedesktop-file-parser" name = "freedesktop-file-parser"
version = "0.1.3" version = "0.1.3"
@ -1053,15 +958,6 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "memmap2"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "memoffset" name = "memoffset"
version = "0.9.1" version = "0.9.1"
@ -1115,105 +1011,6 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "objc-sys"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
[[package]]
name = "objc2"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
dependencies = [
"objc-sys",
"objc2-encode",
]
[[package]]
name = "objc2-app-kit"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
dependencies = [
"bitflags 2.9.0",
"block2",
"libc",
"objc2",
"objc2-core-data",
"objc2-core-image",
"objc2-foundation",
"objc2-quartz-core",
]
[[package]]
name = "objc2-core-data"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
dependencies = [
"bitflags 2.9.0",
"block2",
"objc2",
"objc2-foundation",
]
[[package]]
name = "objc2-core-image"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
dependencies = [
"block2",
"objc2",
"objc2-foundation",
"objc2-metal",
]
[[package]]
name = "objc2-encode"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
[[package]]
name = "objc2-foundation"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
dependencies = [
"bitflags 2.9.0",
"block2",
"libc",
"objc2",
]
[[package]]
name = "objc2-metal"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
dependencies = [
"bitflags 2.9.0",
"block2",
"objc2",
"objc2-foundation",
]
[[package]]
name = "objc2-quartz-core"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
dependencies = [
"bitflags 2.9.0",
"block2",
"objc2",
"objc2-foundation",
"objc2-metal",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.3" version = "1.21.3"
@ -1969,70 +1766,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6"
dependencies = [
"windows-core",
"windows-targets",
]
[[package]]
name = "windows-core"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99"
dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-strings",
"windows-targets",
]
[[package]]
name = "windows-implement"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]]
name = "windows-interface"
version = "0.58.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]]
name = "windows-result"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-strings"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10"
dependencies = [
"windows-result",
"windows-targets",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.59.0" version = "0.59.0"
@ -2184,7 +1917,6 @@ name = "worf-warden"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"enigo",
"worf", "worf",
] ]
@ -2200,23 +1932,6 @@ dependencies = [
"tini", "tini",
] ]
[[package]]
name = "xkbcommon"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d66ca9352cbd4eecbbc40871d8a11b4ac8107cfc528a6e14d7c19c69d0e1ac9"
dependencies = [
"libc",
"memmap2",
"xkeysym",
]
[[package]]
name = "xkeysym"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
[[package]] [[package]]
name = "xml-rs" name = "xml-rs"
version = "0.8.26" version = "0.8.26"

View file

@ -5,7 +5,6 @@ edition = "2024"
[dependencies] [dependencies]
worf = {path = "../../worf"} worf = {path = "../../worf"}
enigo = "0.3.0"
anyhow = "1.0.98" anyhow = "1.0.98"
# todo re-add this # todo re-add this

View file

@ -1,11 +1,11 @@
use enigo::{Enigo, Keyboard};
use std::process::Command; use std::process::Command;
use std::thread::sleep; use std::thread::sleep;
use std::time::Duration; use std::time::Duration;
use worf_lib::config::Config; use worf_lib::config::Config;
use worf_lib::desktop::spawn_fork;
use worf_lib::gui::{ItemProvider, Key, KeyBinding, MenuItem, Modifier}; use worf_lib::gui::{ItemProvider, Key, KeyBinding, MenuItem, Modifier};
use worf_lib::{Error, config, gui}; use worf_lib::{config, gui};
#[derive(Clone)] #[derive(Clone)]
struct PasswordProvider { struct PasswordProvider {
@ -52,6 +52,34 @@ impl ItemProvider<String> for PasswordProvider {
} }
} }
fn groups() -> String {
let output = Command::new("groups")
.output()
.expect("Failed to get groups");
String::from_utf8_lossy(&output.stdout)
.trim_end()
.to_string()
}
fn keyboard_type(text: &str) {
Command::new("ydotool")
.arg("type")
.arg(text)
.output()
.expect("Failed to execute ydotool");
}
fn keyboard_tab() {
Command::new("ydotool")
.arg("key")
.arg("-d")
.arg("10")
.arg("15:1")
.arg("15:0")
.output()
.expect("Failed to execute ydotool");
}
fn rbw_get(name: &str, field: &str) -> String { fn rbw_get(name: &str, field: &str) -> String {
let output = Command::new("rbw") let output = Command::new("rbw")
.arg("get") .arg("get")
@ -74,9 +102,22 @@ fn rbw_get_password(name: &str) -> String {
rbw_get(name, "password") rbw_get(name, "password")
} }
fn rbw_get_totp(name: &str) -> String {
rbw_get(name, "totp")
}
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
let args = config::parse_args(); let args = config::parse_args();
let config = config::load_config(Some(&args)).unwrap_or(args); let config = config::load_config(Some(&args)).unwrap_or(args);
if !groups().contains("input") {
eprintln!("User must be in input group. 'sudo usermod -aG input $USER', then login again");
std::process::exit(1)
}
// will exit if there is a daemon running already, so it's fine to call this everytime.
spawn_fork("ydotoold", None).expect("failed to spawn ydotoold");
// todo eventually use a propper rust client for this, for now rbw is good enough // todo eventually use a propper rust client for this, for now rbw is good enough
let provider = PasswordProvider::new(&config); let provider = PasswordProvider::new(&config);
@ -90,13 +131,19 @@ fn main() -> anyhow::Result<()> {
let type_user = KeyBinding { let type_user = KeyBinding {
key: Key::Num2, key: Key::Num2,
modifiers: Modifier::Alt, modifiers: Modifier::Alt,
label: "<b>Alt+2</b> Type All".to_string(), label: "<b>Alt+2</b> Type User".to_string(),
};
let type_password = KeyBinding {
key: Key::Num3,
modifiers: Modifier::Alt,
label: "<b>Alt+3</b> Type Password".to_string(),
}; };
let type_totp = KeyBinding { let type_totp = KeyBinding {
key: Key::Num3, key: Key::Num4,
modifiers: Modifier::Alt, modifiers: Modifier::Alt,
label: "<b>Alt+3</b> Sync".to_string(), label: "<b>Alt+3</b> Type Totp".to_string(),
}; };
let reload = KeyBinding { let reload = KeyBinding {
@ -142,26 +189,31 @@ fn main() -> anyhow::Result<()> {
None, None,
Some(vec![ Some(vec![
type_all.clone(), type_all.clone(),
type_user, type_user.clone(),
type_totp, type_password.clone(),
reload, type_totp.clone(),
urls, reload.clone(),
names, urls.clone(),
folders, names.clone(),
totp, folders.clone(),
lock, totp.clone(),
lock.clone(),
]), ]),
) { ) {
Ok(selection) => { Ok(selection) => {
let mut enigo = Enigo::new(&enigo::Settings::default())?;
let id = selection.menu.label.replace("\n", ""); let id = selection.menu.label.replace("\n", "");
sleep(Duration::from_millis(250)); sleep(Duration::from_millis(250));
if let Some(key) = selection.custom_key { if let Some(key) = selection.custom_key {
if key.label == type_all.label { if key == type_all {
enigo.text(&rbw_get_user(&id))?; keyboard_type(&rbw_get_user(&id));
enigo.key(enigo::Key::Tab, enigo::Direction::Press)?; keyboard_tab();
enigo.key(enigo::Key::Tab, enigo::Direction::Release)?; keyboard_type(&rbw_get_password(&id));
enigo.text(&rbw_get_password(&id))?; } else if key == type_user {
keyboard_type(&rbw_get_user(&id));
} else if key == type_password {
keyboard_type(&rbw_get_password(&id));
} else if key == type_totp {
keyboard_type(&rbw_get_totp(&id));
} }
} }
} }

View file

@ -6,10 +6,10 @@ use std::time::{Duration, Instant};
use crossbeam::channel; use crossbeam::channel;
use crossbeam::channel::Sender; use crossbeam::channel::Sender;
use gdk4::Display;
use gdk4::gio::File; use gdk4::gio::File;
use gdk4::glib::{Propagation, timeout_add_local}; use gdk4::glib::{Propagation, timeout_add_local};
use gdk4::prelude::{Cast, DisplayExt, MonitorExt, SurfaceExt}; use gdk4::prelude::{Cast, DisplayExt, MonitorExt, SurfaceExt};
use gdk4::{Display, ModifierType};
use gtk4::glib::ControlFlow; use gtk4::glib::ControlFlow;
use gtk4::prelude::{ use gtk4::prelude::{
ApplicationExt, ApplicationExtManual, BoxExt, EditableExt, FlowBoxChildExt, GestureSingleExt, ApplicationExt, ApplicationExtManual, BoxExt, EditableExt, FlowBoxChildExt, GestureSingleExt,
@ -217,9 +217,9 @@ pub enum Key {
Tilde, // ~ Tilde, // ~
} }
impl Into<Key> for gdk::Key { impl From<gdk::Key> for Key {
fn into(self) -> Key { fn from(value: gdk4::Key) -> Self {
match self { match value {
// Letters // Letters
gdk4::Key::A => Key::A, gdk4::Key::A => Key::A,
gdk4::Key::B => Key::B, gdk4::Key::B => Key::B,
@ -338,15 +338,15 @@ pub enum Modifier {
None, None,
} }
impl Into<Modifier> for gdk::ModifierType { impl From<gdk4::ModifierType> for Modifier {
fn into(self) -> Modifier { fn from(value: gdk4::ModifierType) -> Self {
match self { match value {
ModifierType::SHIFT_MASK => Modifier::Shift, gdk4::ModifierType::SHIFT_MASK => Modifier::Shift,
ModifierType::CONTROL_MASK => Modifier::Control, gdk4::ModifierType::CONTROL_MASK => Modifier::Control,
ModifierType::ALT_MASK => Modifier::Alt, gdk4::ModifierType::ALT_MASK => Modifier::Alt,
ModifierType::SUPER_MASK => Modifier::Super, gdk4::ModifierType::SUPER_MASK => Modifier::Super,
ModifierType::META_MASK => Modifier::Meta, gdk4::ModifierType::META_MASK => Modifier::Meta,
ModifierType::LOCK_MASK => Modifier::CapsLock, gdk4::ModifierType::LOCK_MASK => Modifier::CapsLock,
_ => Modifier::None, _ => Modifier::None,
} }
} }
@ -448,7 +448,7 @@ where
app.clone(), app.clone(),
new_on_empty, new_on_empty,
search_ignored_words.clone(), search_ignored_words.clone(),
custom_keys.clone(), custom_keys.as_ref(),
); );
}); });
@ -464,7 +464,7 @@ fn build_ui<T, P>(
app: Application, app: Application,
new_on_empty: bool, new_on_empty: bool,
search_ignored_words: Option<Vec<Regex>>, search_ignored_words: Option<Vec<Regex>>,
custom_keys: Option<Vec<KeyBinding>>, custom_keys: Option<&Vec<KeyBinding>>,
) where ) where
T: Clone + 'static + Send, T: Clone + 'static + Send,
P: ItemProvider<T> + 'static + Send, P: ItemProvider<T> + 'static + Send,
@ -503,7 +503,7 @@ fn build_ui<T, P>(
}); });
// handle keys as soon as possible // handle keys as soon as possible
setup_key_event_handler(&ui_elements, &meta, custom_keys.as_ref()); setup_key_event_handler(&ui_elements, &meta, custom_keys);
log::debug!("keyboard ready after {:?}", start.elapsed()); log::debug!("keyboard ready after {:?}", start.elapsed());
@ -530,7 +530,7 @@ fn build_ui<T, P>(
let outer_box = gtk4::Box::new(config.orientation().into(), 0); let outer_box = gtk4::Box::new(config.orientation().into(), 0);
outer_box.set_widget_name("outer-box"); outer_box.set_widget_name("outer-box");
outer_box.append(&ui_elements.search); outer_box.append(&ui_elements.search);
build_custom_key_view(config, &ui_elements, &custom_keys, &outer_box); build_custom_key_view(custom_keys, &outer_box);
ui_elements.window.set_child(Some(&outer_box)); ui_elements.window.set_child(Some(&outer_box));
@ -626,29 +626,29 @@ fn build_search_entry<T: Clone + Send>(
} }
} }
fn build_custom_key_view<T>( fn build_custom_key_view(custom_keys: Option<&Vec<KeyBinding>>, outer_box: &gtk4::Box) {
config: &Config, let inner_box = FlowBox::new();
ui: &Rc<UiElements<T>>, inner_box.set_halign(Align::Fill);
custom_keys: &Option<Vec<KeyBinding>>,
outer_box: &gtk4::Box,
) where
T: 'static + Clone + Send,
{
let inner_box = gtk4::Box::new(Orientation::Horizontal, 0);
inner_box.set_halign(Align::Start);
inner_box.set_widget_name("custom-key-box"); inner_box.set_widget_name("custom-key-box");
if let Some(custom_keys) = custom_keys { if let Some(custom_keys) = custom_keys {
for key in custom_keys { for key in custom_keys {
let label_box = gtk4::Box::new(Orientation::Horizontal, 0); let label_box = FlowBoxChild::new();
label_box.set_halign(Align::Start); label_box.set_halign(Align::Fill);
inner_box.set_valign(Align::Start);
label_box.set_widget_name("custom-key-label-box"); label_box.set_widget_name("custom-key-label-box");
inner_box.append(&label_box); inner_box.append(&label_box);
inner_box.set_vexpand(false);
inner_box.set_hexpand(false);
let label = Label::new(Some(&key.label)); let label = Label::new(Some(&key.label));
label.set_halign(Align::Fill);
label.set_valign(Align::Start);
label.set_use_markup(true); label.set_use_markup(true);
label.set_hexpand(true); label.set_hexpand(true);
label.set_vexpand(false);
label.set_widget_name("custom-key-label-text"); label.set_widget_name("custom-key-label-text");
label.set_wrap(true); label.set_wrap(false);
label_box.append(&label); label.set_xalign(0.0);
label_box.set_child(Some(&label));
} }
} }
outer_box.append(&inner_box); outer_box.append(&inner_box);
@ -740,7 +740,7 @@ fn setup_key_event_handler<T: Clone + 'static + Send>(
let ui_clone = Rc::clone(ui); let ui_clone = Rc::clone(ui);
let meta_clone = Rc::clone(meta); let meta_clone = Rc::clone(meta);
let keys_clone = custom_keys.map(|s| s.clone()); let keys_clone = custom_keys.cloned();
key_controller.connect_key_pressed(move |_, key_value, _, modifier| { key_controller.connect_key_pressed(move |_, key_value, _, modifier| {
handle_key_press( handle_key_press(
&ui_clone, &ui_clone,
@ -759,7 +759,7 @@ fn handle_key_press<T: Clone + 'static + Send>(
ui: &Rc<UiElements<T>>, ui: &Rc<UiElements<T>>,
meta: &Rc<MetaData<T>>, meta: &Rc<MetaData<T>>,
keyboard_key: gdk4::Key, keyboard_key: gdk4::Key,
modifier_type: ModifierType, modifier_type: gdk4::ModifierType,
custom_keys: Option<&Vec<KeyBinding>>, custom_keys: Option<&Vec<KeyBinding>>,
) -> Propagation { ) -> Propagation {
let update_view = |query: &String| { let update_view = |query: &String| {
@ -979,7 +979,7 @@ where
visible: true, visible: true,
}; };
send_selected_item(&ui, meta, custom_key, &item); send_selected_item(ui, meta, custom_key, &item);
Ok(()) Ok(())
} else { } else {
Err("selected item cannot be resolved".to_owned()) Err("selected item cannot be resolved".to_owned())
@ -994,13 +994,14 @@ fn send_selected_item<T>(
) where ) where
T: Clone + Send, T: Clone + Send,
{ {
close_gui(&ui.app); ui.window.close();
if let Err(e) = meta.selected_sender.send(Ok(Selection { if let Err(e) = meta.selected_sender.send(Ok(Selection {
menu: selected_item.clone(), menu: selected_item.clone(),
custom_key: custom_key.cloned(), custom_key: custom_key.cloned(),
})) { })) {
log::error!("failed to send message {e}"); log::error!("failed to send message {e}");
} }
close_gui(&ui.app);
} }
fn add_menu_item<T: Clone + 'static + Send>( fn add_menu_item<T: Clone + 'static + Send>(