leftwm_core/
event_loop.rs1use 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#[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 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 () = 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 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 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 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}