diff --git a/gravel-core/src/config.rs b/gravel-core/src/config.rs index 487134f..e8193b4 100644 --- a/gravel-core/src/config.rs +++ b/gravel-core/src/config.rs @@ -80,13 +80,13 @@ pub enum HotkeyAction { ShowWith(String), } -impl From for FrontendMessage { - fn from(value: HotkeyAction) -> Self { +impl From<&HotkeyAction> for FrontendMessage { + fn from(value: &HotkeyAction) -> Self { match value { HotkeyAction::ShowHide => Self::ShowOrHide, HotkeyAction::Show => Self::Show, HotkeyAction::Hide => Self::Hide, - HotkeyAction::ShowWith(query) => Self::ShowWithQuery(query.into_c()), + HotkeyAction::ShowWith(query) => Self::ShowWithQuery(query.clone().into_c()), } } } diff --git a/gravel-core/src/hotkeys/mod.rs b/gravel-core/src/hotkeys/mod.rs index 5cf5c5f..8ee2a4c 100644 --- a/gravel-core/src/hotkeys/mod.rs +++ b/gravel-core/src/hotkeys/mod.rs @@ -1,4 +1,3 @@ -use abi_stable::external_types::crossbeam_channel::RSender; use enumflags2::BitFlags; use std::fmt::Debug; @@ -7,25 +6,39 @@ pub use self::{parsing::ParseError, structs::*}; mod parsing; mod structs; -#[derive(Copy, Clone, Debug)] -struct Hotkey { +struct Hotkey { pub modifiers: BitFlags, pub key: Key, - pub value: T, + pub callback: Box, +} + +impl Debug for Hotkey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Hotkey") + .field("modifiers", &self.modifiers) + .field("key", &self.key) + .field("value", &"Box") + .finish() + } } /// Listens for system-wide hotkeys and sends an arbitrary signal through /// the given [`RSender`]. /// /// The listener runs in a separate thread to avoid blocking. -pub struct Listener { - hotkeys: Vec>, +#[derive(Default)] +pub struct Listener { + hotkeys: Vec, } -impl Listener { +impl Listener { /// Registers a hotkey given the modifiers and key. - pub fn register(&mut self, modifiers: BitFlags, key: Key, value: T) -> &mut Self { - let hotkey = Hotkey { modifiers, key, value }; + pub fn register(&mut self, modifiers: BitFlags, key: Key, f: impl Fn() + 'static + Send) -> &mut Self { + let hotkey = Hotkey { + modifiers, + key, + callback: Box::from(f), + }; self.hotkeys.push(hotkey); @@ -40,53 +53,36 @@ impl Listener { /// - `a` => A /// /// For a complete list of keys and modifiers, see [`Key`], [`Modifier`] - pub fn register_emacs(&mut self, binding: &str, value: T) -> Result<&mut Self, ParseError> { + pub fn register_emacs(&mut self, binding: &str, f: impl Fn() + 'static + Send) -> Result<&mut Self, ParseError> { let result = parsing::parse_binding(binding)?; - self.register(result.modifiers, result.key, value); + self.register(result.modifiers, result.key, f); Ok(self) } /// Spawns the listener with the registered hotkeys. - pub fn spawn_listener(&mut self, sender: RSender) -> &mut Self { - let hotkeys = self.hotkeys.clone(); - + pub fn spawn_listener(self) { // run the listener on another thread to avoid blocking the current one std::thread::spawn(move || { log::trace!("starting hotkey listener thread"); - init_hotkeys(&sender, hotkeys).listen(); + init_hotkeys(self.hotkeys).listen(); }); - - self - } -} - -impl Default for Listener { - fn default() -> Self { - Self { hotkeys: vec![] } } } /// Registers the given hotkeys with a new [`hotkey::Listener`] and returns it. /// /// If a hotkey cannot be registered, a warning is logged and the hotkey is skipped. -fn init_hotkeys(sender: &RSender, hotkeys: Vec>) -> hotkey::Listener { +fn init_hotkeys(hotkeys: Vec) -> hotkey::Listener { let mut hk = hotkey::Listener::new(); for hotkey in hotkeys { - let sender_clone = sender.clone(); - let value_clone = hotkey.value.clone(); let modifiers = hotkey.modifiers.iter().fold(0, |r, v| r | convert_modifier(v)); let key = convert_key(hotkey.key); - hk.register_hotkey(modifiers, key, move || { - sender_clone - .send(value_clone.clone()) - .inspect_err(|e| log::error!("unable to send hotkey action message: {e}")) - .ok(); - }) - .inspect_err(|e| log::warn!("failed to register hotkey {hotkey:?}: {e}")) - .ok(); + hk.register_hotkey(modifiers, key, move || (hotkey.callback)()) + .inspect_err(|e| log::warn!("failed to register hotkey {:?}: {e}", (hotkey.key, hotkey.modifiers))) + .ok(); } hk diff --git a/gravel-core/src/hotkeys/parsing.rs b/gravel-core/src/hotkeys/parsing.rs index 8b12c96..3b4f074 100644 --- a/gravel-core/src/hotkeys/parsing.rs +++ b/gravel-core/src/hotkeys/parsing.rs @@ -1,14 +1,9 @@ +use super::ParsedBinding; use crate::hotkeys::{Key, Modifier}; use enumflags2::BitFlags; use itertools::Itertools; use thiserror::Error; -#[derive(Debug, PartialEq, Eq)] -pub struct ParsedBinding { - pub modifiers: BitFlags, - pub key: Key, -} - #[derive(Error, Debug, PartialEq, Eq)] pub enum ParseError { #[error("'{0}' is not a valid modifier")] diff --git a/gravel-core/src/hotkeys/structs.rs b/gravel-core/src/hotkeys/structs.rs index 1eca7e0..09273e3 100644 --- a/gravel-core/src/hotkeys/structs.rs +++ b/gravel-core/src/hotkeys/structs.rs @@ -1,4 +1,10 @@ -use enumflags2::bitflags; +use enumflags2::{bitflags, BitFlags}; + +#[derive(Debug, PartialEq, Eq)] +pub struct ParsedBinding { + pub modifiers: BitFlags, + pub key: Key, +} #[bitflags] #[repr(u8)] diff --git a/gravel/src/init/hotkeys.rs b/gravel/src/init/hotkeys.rs index c900a29..bc8ada1 100644 --- a/gravel/src/init/hotkeys.rs +++ b/gravel/src/init/hotkeys.rs @@ -13,18 +13,25 @@ pub fn hotkeys(hotkeys: &[Hotkey], sender: RSender) { log::trace!("initializing hotkeys"); - let mut listener = Listener::::default(); + let mut listener = Listener::default(); for hotkey in hotkeys { let binding = &hotkey.binding; - let action = &hotkey.action; - let message = FrontendMessageNe::new(action.clone().into()); + let action = hotkey.action.clone(); + let sender_clone = sender.clone(); - match listener.register_emacs(binding, message) { - Ok(_) => log::debug!("registered hotkey '{binding}' with action '{action:?}'"), + let result = listener.register_emacs(binding, move || { + sender_clone + .send(FrontendMessageNe::new((&action).into())) + .inspect_err(|e| log::error!("unable to send hotkey action message: {e}")) + .ok(); + }); + + match result { + Ok(_) => log::debug!("registered hotkey '{binding}' with action '{:?}'", hotkey.action), Err(e) => log::warn!("invalid binding '{}', {e}. skipping", binding), }; } - listener.spawn_listener(sender); + listener.spawn_listener(); }