Skip to main content

leftwm_core/
event_loop.rs

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