Skip to main content

lefthk_core/worker/
mod.rs

1pub mod context;
2
3use crate::child::Children;
4use crate::config::{Keybind, command};
5use crate::errors::{self, Error, LeftError};
6use crate::ipc::Pipe;
7use crate::xkeysym_lookup;
8use crate::xwrap::XWrap;
9use x11_dl::xlib;
10use xdg::BaseDirectories;
11
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub enum Status {
14    Reload,
15    Kill,
16    Continue,
17}
18
19pub struct Worker {
20    keybinds: Vec<Keybind>,
21    base_directory: BaseDirectories,
22
23    pub xwrap: XWrap,
24    pub children: Children,
25    pub status: Status,
26
27    /// "Chord Context": Holds the relevant data for chording
28    pub chord_ctx: context::Chord,
29}
30
31impl Worker {
32    #[must_use]
33    pub fn new(keybinds: Vec<Keybind>, base_directory: BaseDirectories) -> Self {
34        Self {
35            status: Status::Continue,
36            keybinds,
37            base_directory,
38            xwrap: XWrap::new(),
39            children: Children::default(),
40            chord_ctx: context::Chord::new(),
41        }
42    }
43
44    pub async fn event_loop(mut self) -> Status {
45        self.xwrap.grab_keys(&self.keybinds);
46        let mut pipe = self.get_pipe().await;
47
48        while self.status == Status::Continue {
49            self.xwrap.flush();
50
51            self.evaluate_chord();
52
53            tokio::select! {
54                () = self.children.wait_readable() => {
55                    self.children.reap();
56                }
57                () = self.xwrap.wait_readable() => {
58                    let event_in_queue = self.xwrap.queue_len();
59                    for _ in 0..event_in_queue {
60                        let xlib_event = self.xwrap.get_next_event();
61                        self.handle_event(&xlib_event);
62                    }
63                }
64                Some(command) = pipe.get_next_command() => {
65                    errors::log_on_error!(command.execute(&mut self));
66                }
67            };
68        }
69
70        self.status
71    }
72
73    async fn get_pipe(&self) -> Pipe {
74        let pipe_name = Pipe::pipe_name();
75        let pipe_file = errors::exit_on_error!(self.base_directory.place_runtime_file(pipe_name));
76        errors::exit_on_error!(Pipe::new(pipe_file).await)
77    }
78
79    fn handle_event(&mut self, xlib_event: &xlib::XEvent) {
80        let error = match xlib_event.get_type() {
81            xlib::KeyPress => self.handle_key_press(&xlib::XKeyEvent::from(xlib_event)),
82            xlib::MappingNotify => {
83                self.handle_mapping_notify(&mut xlib::XMappingEvent::from(xlib_event))
84            }
85            _ => return,
86        };
87        errors::log_on_error!(error);
88    }
89
90    fn handle_key_press(&mut self, event: &xlib::XKeyEvent) -> Error {
91        let key = self.xwrap.keycode_to_keysym(event.keycode)?;
92        let mask = xkeysym_lookup::clean_mask(event.state);
93        if let Some(keybind) = self.get_keybind((mask, key)) {
94            if let Ok(command) = command::denormalize(&keybind.command) {
95                return command.execute(self);
96            }
97        } else {
98            return Err(LeftError::CommandNotFound);
99        }
100        Ok(())
101    }
102
103    fn get_keybind(&self, mask_key_pair: (u32, u32)) -> Option<Keybind> {
104        let keybinds = if let Some(keybinds) = &self.chord_ctx.keybinds {
105            keybinds
106        } else {
107            &self.keybinds
108        };
109        keybinds
110            .iter()
111            .find(|keybind| {
112                if let Some(key) = xkeysym_lookup::into_keysym(&keybind.key) {
113                    let mask = xkeysym_lookup::into_modmask(&keybind.modifier);
114                    return mask_key_pair == (mask, key);
115                }
116                false
117            })
118            .cloned()
119    }
120
121    fn handle_mapping_notify(&self, event: &mut xlib::XMappingEvent) -> Error {
122        if event.request == xlib::MappingModifier || event.request == xlib::MappingKeyboard {
123            return self.xwrap.refresh_keyboard(event);
124        }
125        Ok(())
126    }
127}