gravel_core/hotkeys/
mod.rs1use enumflags2::BitFlags;
2use std::fmt::Debug;
3
4pub use self::{parsing::ParseError, structs::*};
5
6mod parsing;
7mod structs;
8
9struct Hotkey {
10 pub modifiers: BitFlags<Modifier>,
11 pub key: Key,
12 pub callback: Box<dyn Fn() + 'static + Send>,
13}
14
15impl Debug for Hotkey {
16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17 f.debug_struct("Hotkey")
18 .field("modifiers", &self.modifiers)
19 .field("key", &self.key)
20 .field("value", &"Box<dyn FnMut>")
21 .finish()
22 }
23}
24
25#[derive(Default)]
29pub struct Listener {
30 hotkeys: Vec<Hotkey>,
31}
32
33impl Listener {
34 pub fn register(&mut self, modifiers: BitFlags<Modifier>, key: Key, f: impl Fn() + 'static + Send) -> &mut Self {
36 let hotkey = Hotkey {
37 modifiers,
38 key,
39 callback: Box::from(f),
40 };
41
42 self.hotkeys.push(hotkey);
43
44 self
45 }
46
47 pub fn register_emacs(&mut self, binding: &str, f: impl Fn() + 'static + Send) -> Result<&mut Self, ParseError> {
56 let result = parsing::parse_binding(binding)?;
57 self.register(result.modifiers, result.key, f);
58
59 Ok(self)
60 }
61
62 pub fn spawn_listener(self) {
64 std::thread::spawn(move || {
66 log::trace!("starting hotkey listener thread");
67 init_hotkeys(self.hotkeys).listen();
68 });
69 }
70}
71
72fn init_hotkeys(hotkeys: Vec<Hotkey>) -> hotkey::Listener {
76 let mut hk = hotkey::Listener::new();
77
78 for hotkey in hotkeys {
79 let modifiers = hotkey.modifiers.iter().fold(0, |r, v| r | convert_modifier(v));
80 let key = convert_key(hotkey.key);
81
82 hk.register_hotkey(modifiers, key, move || (hotkey.callback)())
83 .inspect_err(|e| log::warn!("failed to register hotkey {:?}: {e}", (hotkey.key, hotkey.modifiers)))
84 .ok();
85 }
86
87 hk
88}
89
90fn convert_modifier(value: Modifier) -> u32 {
91 match value {
92 Modifier::Alt => hotkey::modifiers::ALT,
93 Modifier::Control => hotkey::modifiers::CONTROL,
94 Modifier::Shift => hotkey::modifiers::SHIFT,
95 Modifier::Super => hotkey::modifiers::SUPER,
96 }
97}
98
99fn convert_key(value: Key) -> u32 {
100 match value {
101 Key::A => 'A' as u32,
102 Key::B => 'B' as u32,
103 Key::C => 'C' as u32,
104 Key::D => 'D' as u32,
105 Key::E => 'E' as u32,
106 Key::F => 'F' as u32,
107 Key::G => 'G' as u32,
108 Key::H => 'H' as u32,
109 Key::I => 'I' as u32,
110 Key::J => 'J' as u32,
111 Key::K => 'K' as u32,
112 Key::L => 'L' as u32,
113 Key::M => 'M' as u32,
114 Key::N => 'N' as u32,
115 Key::O => 'O' as u32,
116 Key::P => 'P' as u32,
117 Key::Q => 'Q' as u32,
118 Key::R => 'R' as u32,
119 Key::S => 'S' as u32,
120 Key::T => 'T' as u32,
121 Key::U => 'U' as u32,
122 Key::V => 'V' as u32,
123 Key::W => 'W' as u32,
124 Key::X => 'X' as u32,
125 Key::Y => 'Y' as u32,
126 Key::Z => 'Z' as u32,
127 Key::Backspace => hotkey::keys::BACKSPACE,
128 Key::Tab => hotkey::keys::TAB,
129 Key::Enter => hotkey::keys::ENTER,
130 Key::CapsLock => hotkey::keys::CAPS_LOCK,
131 Key::Escape => hotkey::keys::ESCAPE,
132 Key::Space => hotkey::keys::SPACEBAR,
133 Key::PageUp => hotkey::keys::PAGE_UP,
134 Key::PageDown => hotkey::keys::PAGE_DOWN,
135 Key::End => hotkey::keys::END,
136 Key::Home => hotkey::keys::HOME,
137 Key::Left => hotkey::keys::ARROW_LEFT,
138 Key::Right => hotkey::keys::ARROW_RIGHT,
139 Key::Up => hotkey::keys::ARROW_UP,
140 Key::Down => hotkey::keys::ARROW_DOWN,
141 Key::PrintScreen => hotkey::keys::PRINT_SCREEN,
142 Key::Insert => hotkey::keys::INSERT,
143 Key::Delete => hotkey::keys::DELETE,
144 }
145}