xlib_display_server/
lib.rs

1// allow casting types
2#![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(); // setup events masks
46
47        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    /// Creates a verify focus event for the cursors current window.
154    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    /// Return a vec of events for setting up state of WM.
162    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                // If there is a screen corresponding to the given output, create the workspace
170                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 there is no hardcoded workspace layout, add every screen not mentioned in the config.
195            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        // Tell manager about existing windows.
209        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
238// Display actions.
239fn 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    // TODO: impl from for windowstate and xlib::Atom
313    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    // Unmanaged windows.
340    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    // Unmanaged windows on top.
349    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}