1#![allow(clippy::cast_precision_loss)]
3#![allow(clippy::cast_possible_truncation)]
4#![allow(clippy::cast_possible_wrap)]
5#![allow(clippy::cast_sign_loss)]
6
7mod event_translate;
8mod event_translate_client_message;
9mod event_translate_property_notify;
10mod xatom;
11mod xcursor;
12mod xwrap;
13
14use serde::{Deserialize, Serialize};
15pub use xwrap::XWrap;
16
17use self::xwrap::ICONIC_STATE;
18use event_translate::XEvent;
19use futures::prelude::*;
20use leftwm_core::config::Config;
21use leftwm_core::models::{
22 Handle, Mode, Screen, TagId, Window, WindowHandle, WindowState, Workspace,
23};
24use leftwm_core::utils;
25use leftwm_core::{DisplayAction, DisplayEvent, DisplayServer};
26use std::pin::Pin;
27
28use x11_dl::xlib;
29
30#[derive(Serialize, Deserialize, Debug, Default, Clone, Copy, PartialEq, Eq)]
31pub struct XlibWindowHandle(xlib::Window);
32impl Handle for XlibWindowHandle {}
33
34pub struct XlibDisplayServer {
35 xw: XWrap,
36 root: xlib::Window,
37 initial_events: Vec<DisplayEvent<XlibWindowHandle>>,
38}
39
40impl DisplayServer<XlibWindowHandle> for XlibDisplayServer {
41 fn new(config: &impl Config) -> Self {
42 let mut wrap = XWrap::new();
43
44 wrap.load_config(config);
45 wrap.init(); let root = wrap.get_default_root();
48 let instance = Self {
49 xw: wrap,
50 root,
51 initial_events: Vec::new(),
52 };
53 let initial_events = instance.initial_events(config);
54
55 Self {
56 initial_events,
57 ..instance
58 }
59 }
60
61 fn reload_config(
62 &mut self,
63 config: &impl Config,
64 focused: Option<WindowHandle<XlibWindowHandle>>,
65 windows: &[Window<XlibWindowHandle>],
66 ) {
67 self.xw.load_config(config);
68 self.xw.update_colors(focused, windows);
69 }
70
71 fn update_windows(&self, windows: Vec<&Window<XlibWindowHandle>>) {
72 for window in &windows {
73 self.xw.update_window(window);
74 }
75 }
76
77 fn update_workspaces(&self, focused: Option<&Workspace>) {
78 if let Some(focused) = focused {
79 self.xw.set_current_desktop(focused.tag);
80 }
81 }
82
83 fn get_next_events(&mut self) -> Vec<DisplayEvent<XlibWindowHandle>> {
84 let mut events = std::mem::take(&mut self.initial_events);
85
86 let events_in_queue = self.xw.queue_len();
87 for _ in 0..events_in_queue {
88 let xlib_event = self.xw.get_next_event();
89 let event = XEvent(&mut self.xw, xlib_event).into();
90 if let Some(e) = event {
91 tracing::trace!("DisplayEvent: {:?}", e);
92 events.push(e);
93 }
94 }
95
96 for event in &events {
97 if let DisplayEvent::WindowDestroy(WindowHandle(XlibWindowHandle(w))) = event {
98 self.xw.force_unmapped(*w);
99 }
100 }
101
102 events
103 }
104
105 fn execute_action(
106 &mut self,
107 act: DisplayAction<XlibWindowHandle>,
108 ) -> Option<DisplayEvent<XlibWindowHandle>> {
109 tracing::trace!("DisplayAction: {:?}", act);
110 let xw = &mut self.xw;
111 let event: Option<DisplayEvent<XlibWindowHandle>> = match act {
112 DisplayAction::KillWindow(h) => from_kill_window(xw, h),
113 DisplayAction::AddedWindow(h, f, fm) => from_added_window(xw, h, f, fm),
114 DisplayAction::MoveMouseOver(h, f) => from_move_mouse_over(xw, h, f),
115 DisplayAction::MoveMouseOverPoint(p) => from_move_mouse_over_point(xw, p),
116 DisplayAction::DestroyedWindow(h) => from_destroyed_window(xw, h),
117 DisplayAction::Unfocus(h, f) => from_unfocus(xw, h, f),
118 DisplayAction::ReplayClick(h, b) => from_replay_click(xw, h, b.into()),
119 DisplayAction::SetState(h, t, s) => from_set_state(xw, h, t, s),
120 DisplayAction::SetWindowOrder(ws) => from_set_window_order(xw, ws),
121 DisplayAction::MoveToTop(h) => from_move_to_top(xw, h),
122 DisplayAction::ReadyToMoveWindow(h) => from_ready_to_move_window(xw, h),
123 DisplayAction::ReadyToResizeWindow(h) => from_ready_to_resize_window(xw, h),
124 DisplayAction::SetCurrentTags(t) => from_set_current_tags(xw, t),
125 DisplayAction::SetWindowTag(h, t) => from_set_window_tag(xw, h, t),
126 DisplayAction::ConfigureXlibWindow(w) => from_configure_xlib_window(xw, &w),
127
128 DisplayAction::WindowTakeFocus {
129 window,
130 previous_window,
131 } => from_window_take_focus(xw, &window, previous_window.as_ref()),
132
133 DisplayAction::FocusWindowUnderCursor => from_focus_window_under_cursor(xw),
134 DisplayAction::NormalMode => from_normal_mode(xw),
135 };
136 if event.is_some() {
137 tracing::trace!("DisplayEvent: {:?}", event);
138 }
139 event
140 }
141
142 fn wait_readable(&self) -> Pin<Box<dyn Future<Output = ()>>> {
143 let task_notify = self.xw.task_notify.clone();
144 Box::pin(async move {
145 task_notify.notified().await;
146 })
147 }
148
149 fn flush(&self) {
150 self.xw.flush();
151 }
152
153 fn generate_verify_focus_event(&self) -> Option<DisplayEvent<XlibWindowHandle>> {
155 let handle = self.xw.get_cursor_window().ok()?;
156 Some(DisplayEvent::VerifyFocusedAt(handle))
157 }
158}
159
160impl XlibDisplayServer {
161 fn initial_events(&self, config: &impl Config) -> Vec<DisplayEvent<XlibWindowHandle>> {
163 let mut events = vec![];
164 if let Some(workspaces) = config.workspaces() {
165 let screens = self.xw.get_screens();
166 for (i, wsc) in workspaces.iter().enumerate() {
167 let mut screen = Screen::from(wsc);
168 screen.root = WindowHandle(XlibWindowHandle(self.root));
169 match screens.iter().find(|i| i.output == wsc.output) {
171 Some(output_match) => {
172 if wsc.relative.unwrap_or(false) {
173 screen.bbox.add(output_match.bbox);
174 }
175 screen.id = Some(i + 1);
176 }
177 None => continue,
178 }
179 let e = DisplayEvent::ScreenCreate(screen);
180 events.push(e);
181 }
182
183 let auto_derive_workspaces: bool = if config.auto_derive_workspaces() {
184 true
185 } else if events.is_empty() {
186 tracing::warn!("No Workspace in Workspace config matches connected screen. Falling back to \"auto_derive_workspaces: true\".");
187 true
188 } else {
189 false
190 };
191
192 let mut next_id = workspaces.len() + 1;
193
194 if auto_derive_workspaces {
196 screens
197 .iter()
198 .filter(|screen| !workspaces.iter().any(|wsc| wsc.output == screen.output))
199 .for_each(|screen| {
200 let mut s = screen.clone();
201 s.id = Some(next_id);
202 next_id += 1;
203 events.push(DisplayEvent::ScreenCreate(s));
204 });
205 }
206 }
207
208 events.append(&mut self.find_all_windows());
210
211 events
212 }
213
214 fn find_all_windows(&self) -> Vec<DisplayEvent<XlibWindowHandle>> {
215 let mut all: Vec<DisplayEvent<XlibWindowHandle>> = Vec::new();
216 match self.xw.get_all_windows() {
217 Ok(handles) => handles.into_iter().for_each(|handle| {
218 let Ok(attrs) = self.xw.get_window_attrs(handle) else {
219 return;
220 };
221 let Some(state) = self.xw.get_wm_state(handle) else {
222 return;
223 };
224 if attrs.map_state == xlib::IsViewable || state == ICONIC_STATE {
225 if let Some(event) = self.xw.setup_window(handle) {
226 all.push(event);
227 }
228 }
229 }),
230 Err(err) => {
231 println!("ERROR: {err}");
232 }
233 }
234 all
235 }
236}
237
238fn from_kill_window(
240 xw: &mut XWrap,
241 handle: WindowHandle<XlibWindowHandle>,
242) -> Option<DisplayEvent<XlibWindowHandle>> {
243 xw.kill_window(&handle);
244 None
245}
246
247fn from_added_window(
248 xw: &mut XWrap,
249 handle: WindowHandle<XlibWindowHandle>,
250 floating: bool,
251 follow_mouse: bool,
252) -> Option<DisplayEvent<XlibWindowHandle>> {
253 xw.setup_managed_window(handle, floating, follow_mouse)
254}
255
256fn from_move_mouse_over(
257 xw: &mut XWrap,
258 handle: WindowHandle<XlibWindowHandle>,
259 force: bool,
260) -> Option<DisplayEvent<XlibWindowHandle>> {
261 let WindowHandle(XlibWindowHandle(window)) = handle;
262 match xw.get_cursor_window() {
263 Ok(WindowHandle(XlibWindowHandle(cursor_window))) if force || cursor_window != window => {
264 _ = xw.move_cursor_to_window(window);
265 }
266 _ => {}
267 }
268 None
269}
270
271fn from_move_mouse_over_point(
272 xw: &mut XWrap,
273 point: (i32, i32),
274) -> Option<DisplayEvent<XlibWindowHandle>> {
275 _ = xw.move_cursor_to_point(point);
276 None
277}
278
279fn from_destroyed_window(
280 xw: &mut XWrap,
281 handle: WindowHandle<XlibWindowHandle>,
282) -> Option<DisplayEvent<XlibWindowHandle>> {
283 xw.teardown_managed_window(&handle, true);
284 None
285}
286
287fn from_unfocus(
288 xw: &mut XWrap,
289 handle: Option<WindowHandle<XlibWindowHandle>>,
290 floating: bool,
291) -> Option<DisplayEvent<XlibWindowHandle>> {
292 xw.unfocus(handle, floating);
293 None
294}
295
296fn from_replay_click(
297 xw: &mut XWrap,
298 handle: WindowHandle<XlibWindowHandle>,
299 button: u8,
300) -> Option<DisplayEvent<XlibWindowHandle>> {
301 let WindowHandle(XlibWindowHandle(handle)) = handle;
302 xw.replay_click(handle, button.into());
303 None
304}
305
306fn from_set_state(
307 xw: &mut XWrap,
308 handle: WindowHandle<XlibWindowHandle>,
309 toggle_to: bool,
310 window_state: WindowState,
311) -> Option<DisplayEvent<XlibWindowHandle>> {
312 let state = match window_state {
314 WindowState::Modal => xw.atoms.NetWMStateModal,
315 WindowState::Sticky => xw.atoms.NetWMStateSticky,
316 WindowState::MaximizedVert => xw.atoms.NetWMStateMaximizedVert,
317 WindowState::MaximizedHorz => xw.atoms.NetWMStateMaximizedHorz,
318 WindowState::Shaded => xw.atoms.NetWMStateShaded,
319 WindowState::SkipTaskbar => xw.atoms.NetWMStateSkipTaskbar,
320 WindowState::SkipPager => xw.atoms.NetWMStateSkipPager,
321 WindowState::Hidden => xw.atoms.NetWMStateHidden,
322 WindowState::Fullscreen => xw.atoms.NetWMStateFullscreen,
323 WindowState::Above => xw.atoms.NetWMStateAbove,
324 WindowState::Below => xw.atoms.NetWMStateBelow,
325 WindowState::Maximized => {
326 xw.set_state(handle, toggle_to, xw.atoms.NetWMStateMaximizedVert);
327 xw.set_state(handle, toggle_to, xw.atoms.NetWMStateMaximizedHorz);
328 return None;
329 }
330 };
331 xw.set_state(handle, toggle_to, state);
332 None
333}
334
335fn from_set_window_order(
336 xw: &mut XWrap,
337 windows: Vec<WindowHandle<XlibWindowHandle>>,
338) -> Option<DisplayEvent<XlibWindowHandle>> {
339 let unmanaged: Vec<WindowHandle<XlibWindowHandle>> = xw
341 .get_all_windows()
342 .unwrap_or_default()
343 .iter()
344 .filter(|&w| *w != xw.get_default_root())
345 .map(|&w| WindowHandle(XlibWindowHandle(w)))
346 .filter(|h| !windows.iter().any(|w| w == h))
347 .collect();
348 xw.restack([unmanaged, windows].concat());
350 None
351}
352
353fn from_move_to_top(
354 xw: &mut XWrap,
355 handle: WindowHandle<XlibWindowHandle>,
356) -> Option<DisplayEvent<XlibWindowHandle>> {
357 xw.move_to_top(&handle);
358 None
359}
360
361fn from_ready_to_move_window(
362 xw: &mut XWrap,
363 handle: WindowHandle<XlibWindowHandle>,
364) -> Option<DisplayEvent<XlibWindowHandle>> {
365 xw.set_mode(Mode::ReadyToMove(handle));
366 None
367}
368
369fn from_ready_to_resize_window(
370 xw: &mut XWrap,
371 handle: WindowHandle<XlibWindowHandle>,
372) -> Option<DisplayEvent<XlibWindowHandle>> {
373 xw.set_mode(Mode::ReadyToResize(handle));
374 None
375}
376
377fn from_set_current_tags(
378 xw: &mut XWrap,
379 tag: Option<TagId>,
380) -> Option<DisplayEvent<XlibWindowHandle>> {
381 xw.set_current_desktop(tag);
382 None
383}
384
385fn from_set_window_tag(
386 xw: &mut XWrap,
387 handle: WindowHandle<XlibWindowHandle>,
388 tag: Option<TagId>,
389) -> Option<DisplayEvent<XlibWindowHandle>> {
390 let WindowHandle(XlibWindowHandle(window)) = handle;
391 let tag = tag?;
392 xw.set_window_desktop(window, &tag);
393 None
394}
395
396fn from_configure_xlib_window(
397 xw: &mut XWrap,
398 window: &Window<XlibWindowHandle>,
399) -> Option<DisplayEvent<XlibWindowHandle>> {
400 xw.configure_window(window);
401 None
402}
403
404fn from_window_take_focus(
405 xw: &mut XWrap,
406 window: &Window<XlibWindowHandle>,
407 previous_window: Option<&Window<XlibWindowHandle>>,
408) -> Option<DisplayEvent<XlibWindowHandle>> {
409 xw.window_take_focus(window, previous_window);
410 None
411}
412
413fn from_focus_window_under_cursor(xw: &mut XWrap) -> Option<DisplayEvent<XlibWindowHandle>> {
414 if let Ok(mut window) = xw.get_cursor_window() {
415 if window == WindowHandle(XlibWindowHandle(0)) {
416 window = xw.get_default_root_handle();
417 }
418 return Some(DisplayEvent::WindowTakeFocus(window));
419 }
420 let point = xw.get_cursor_point().ok()?;
421 let evt = DisplayEvent::MoveFocusTo(point.0, point.1);
422 Some(evt)
423}
424
425fn from_normal_mode(xw: &mut XWrap) -> Option<DisplayEvent<XlibWindowHandle>> {
426 xw.set_mode(Mode::Normal);
427 None
428}