leftwm_core/
event_loop.rs

1use crate::models::Handle;
2use crate::{child_process::Nanny, config::Config};
3use crate::{
4    Command, CommandPipe, DisplayEvent, DisplayServer, Manager, Mode, StateSocket, Window,
5};
6use std::path::{Path, PathBuf};
7use std::sync::{atomic::Ordering, Once};
8
9use tracing::error;
10
11/// Errors which can appear while running the event loop.
12#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq, Hash)]
13pub enum Error {
14    #[error("Couldn't create the file: '{0}'")]
15    CreateFile(PathBuf),
16
17    #[error("Couldn't connect to file: '{0}'")]
18    ConnectToFile(PathBuf),
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Hash)]
22enum EventResponse {
23    None,
24    DisplayRefreshNeeded,
25}
26
27impl<H: Handle, C: Config, SERVER: DisplayServer<H>> Manager<H, C, SERVER> {
28    /// Starts the event loop of leftwm
29    ///
30    /// # Errors
31    /// `EventResponse` if the initialisation of the command pipe or/and the state socket failed.
32    pub async fn start_event_loop(mut self) -> Result<(), Error> {
33        let state_socket = get_state_socket().await?;
34        let command_pipe = get_command_pipe().await?;
35
36        self.call_up_scripts();
37        tracing::info!("LeftWM-core booted!");
38        self.event_loop(state_socket, command_pipe).await
39    }
40
41    async fn event_loop(
42        &mut self,
43        mut state_socket: StateSocket,
44        mut command_pipe: CommandPipe<H>,
45    ) -> Result<(), Error> {
46        let after_first_loop: Once = Once::new();
47        let mut event_buffer: Vec<DisplayEvent<H>> = vec![];
48        while self.should_keep_running(&mut state_socket).await {
49            self.update_manager_state(&mut state_socket).await;
50            self.display_server.flush();
51
52            let response: EventResponse = tokio::select! {
53                () = self.display_server.wait_readable(), if event_buffer.is_empty() => {
54                    self.add_events(&mut event_buffer);
55                    continue;
56                }
57                // When a mouse button is pressed or enter/motion notifies are blocked and only appear
58                // once the button is released. This is to double check that we know which window
59                // is currently focused.
60                () = timeout(100), if (self.state.focus_manager.sloppy_mouse_follows_focus &&
61                       self.state.focus_manager.behaviour.is_sloppy() &&
62                       event_buffer.is_empty()) => {
63                        self.refresh_focus(&mut event_buffer);
64                        continue;
65                    }
66                Some::<Command<H>>(cmd) = command_pipe.read_command(), if event_buffer.is_empty() => self.execute_command(&cmd),
67                else => self.execute_display_events(&mut event_buffer),
68            };
69
70            match response {
71                EventResponse::None => (),
72                EventResponse::DisplayRefreshNeeded => self.refresh_display(),
73            };
74
75            self.execute_actions(&mut event_buffer);
76
77            // We need to run once through all of the loop to properly initialize the state
78            // before we can restore the previous state
79            after_first_loop.call_once(|| {
80                self.config.load_state(&mut self.state);
81            });
82
83            if self.reap_requested.swap(false, Ordering::SeqCst) {
84                self.children.remove_finished_children();
85            }
86        }
87
88        Ok(())
89    }
90
91    async fn update_manager_state(&self, state_socket: &mut StateSocket) {
92        if self.state.mode == Mode::Normal {
93            state_socket.write_manager_state(&self.state).await.ok();
94        }
95    }
96
97    async fn should_keep_running(&self, state_socket: &mut StateSocket) -> bool {
98        if self.reload_requested {
99            state_socket.shutdown().await;
100            false
101        } else {
102            true
103        }
104    }
105
106    fn execute_display_events(&mut self, event_buffer: &mut Vec<DisplayEvent<H>>) -> EventResponse {
107        let mut display_needs_refresh = false;
108
109        event_buffer.drain(..).for_each(|event: DisplayEvent<H>| {
110            display_needs_refresh = self.display_event_handler(event) || display_needs_refresh;
111        });
112
113        if display_needs_refresh {
114            EventResponse::DisplayRefreshNeeded
115        } else {
116            EventResponse::None
117        }
118    }
119
120    fn refresh_display(&mut self) {
121        self.update_windows();
122
123        match self.state.mode {
124            // When (resizing / moving) only deal with the single window.
125            Mode::ResizingWindow(h) | Mode::MovingWindow(h) => {
126                if let Some(window) = self.state.windows.iter().find(|w| w.handle == h) {
127                    self.display_server.update_windows(vec![window]);
128                }
129            }
130            _ => {
131                let windows: Vec<&Window<H>> = self.state.windows.iter().collect();
132                self.display_server.update_windows(windows);
133            }
134        }
135    }
136
137    fn execute_command(&mut self, command: &Command<H>) -> EventResponse {
138        if self.command_handler(command) {
139            EventResponse::DisplayRefreshNeeded
140        } else {
141            EventResponse::None
142        }
143    }
144
145    fn add_events(&mut self, event_buffer: &mut Vec<DisplayEvent<H>>) -> EventResponse {
146        event_buffer.append(&mut self.display_server.get_next_events());
147        EventResponse::None
148    }
149
150    fn refresh_focus(&self, event_buffer: &mut Vec<DisplayEvent<H>>) -> EventResponse {
151        if let Some(verify_event) = self.display_server.generate_verify_focus_event() {
152            event_buffer.push(verify_event);
153        }
154        EventResponse::None
155    }
156
157    // Perform any actions requested by the handler.
158    fn execute_actions(&mut self, event_buffer: &mut Vec<DisplayEvent<H>>) {
159        while !self.state.actions.is_empty() {
160            if let Some(act) = self.state.actions.pop_front() {
161                if let Some(event) = self.display_server.execute_action(act) {
162                    event_buffer.push(event);
163                }
164            }
165        }
166    }
167
168    fn call_up_scripts(&mut self) {
169        match Nanny::run_global_up_script() {
170            Ok(child) => {
171                self.children.insert(child);
172            }
173            Err(err) => tracing::warn!("Global up script failed: {}", err),
174        }
175        match Nanny::boot_current_theme() {
176            Ok(child) => {
177                self.children.insert(child);
178            }
179            Err(err) => tracing::warn!("Theme loading failed: {}", err),
180        }
181    }
182}
183
184async fn get_state_socket() -> Result<StateSocket, Error> {
185    let socket_filename = Path::new("current_state.sock");
186    let socket_file = place_runtime_file(socket_filename)
187        .map_err(|_| Error::CreateFile(socket_filename.into()))?;
188
189    let mut state_socket = StateSocket::default();
190
191    state_socket
192        .listen(socket_file)
193        .await
194        .map_err(|_| Error::ConnectToFile(socket_filename.into()))?;
195
196    Ok(state_socket)
197}
198
199async fn get_command_pipe<H: Handle>() -> Result<CommandPipe<H>, Error> {
200    let file_name = crate::pipe_name();
201
202    let pipe_file =
203        place_runtime_file(&file_name).map_err(|_| Error::CreateFile(file_name.clone()))?;
204
205    CommandPipe::new(pipe_file)
206        .await
207        .map_err(|_| Error::ConnectToFile(file_name))
208}
209
210fn place_runtime_file<P>(path: P) -> std::io::Result<PathBuf>
211where
212    P: AsRef<Path>,
213{
214    xdg::BaseDirectories::with_prefix("leftwm")?.place_runtime_file(path)
215}
216
217async fn timeout(mills: u64) {
218    use tokio::time::{sleep, Duration};
219    sleep(Duration::from_millis(mills)).await;
220}