Skip to main content

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!(
187                    "No Workspace in Workspace config matches connected screen. Falling back to \"auto_derive_workspaces: true\"."
188                );
189                true
190            } else {
191                false
192            };
193
194            let mut next_id = workspaces.len() + 1;
195
196            // If there is no hardcoded workspace layout, add every screen not mentioned in the config.
197            if auto_derive_workspaces {
198                screens
199                    .iter()
200                    .filter(|screen| !workspaces.iter().any(|wsc| wsc.output == screen.output))
201                    .for_each(|screen| {
202                        let mut s = screen.clone();
203                        s.id = Some(next_id);
204                        next_id += 1;
205                        events.push(DisplayEvent::ScreenCreate(s));
206                    });
207            }
208        }
209
210        // Tell manager about existing windows.
211        events.append(&mut self.find_all_windows());
212
213        events
214    }
215
216    fn find_all_windows(&self) -> Vec<DisplayEvent<XlibWindowHandle>> {
217        let mut all: Vec<DisplayEvent<XlibWindowHandle>> = Vec::new();
218        match self.xw.get_all_windows() {
219            Ok(handles) => handles.into_iter().for_each(|handle| {
220                let Ok(attrs) = self.xw.get_window_attrs(handle) else {
221                    return;
222                };
223                let Some(state) = self.xw.get_wm_state(handle) else {
224                    return;
225                };
226                if (attrs.map_state == xlib::IsViewable || state == ICONIC_STATE)
227                    && let Some(event) = self.xw.setup_window(handle)
228                {
229                    all.push(event);
230                }
231            }),
232            Err(err) => {
233                println!("ERROR: {err}");
234            }
235        }
236        all
237    }
238}
239
240// Display actions.
241fn from_kill_window(
242    xw: &mut XWrap,
243    handle: WindowHandle<XlibWindowHandle>,
244) -> Option<DisplayEvent<XlibWindowHandle>> {
245    xw.kill_window(&handle);
246    None
247}
248
249fn from_added_window(
250    xw: &mut XWrap,
251    handle: WindowHandle<XlibWindowHandle>,
252    floating: bool,
253    follow_mouse: bool,
254) -> Option<DisplayEvent<XlibWindowHandle>> {
255    xw.setup_managed_window(handle, floating, follow_mouse)
256}
257
258fn from_move_mouse_over(
259    xw: &mut XWrap,
260    handle: WindowHandle<XlibWindowHandle>,
261    force: bool,
262) -> Option<DisplayEvent<XlibWindowHandle>> {
263    let WindowHandle(XlibWindowHandle(window)) = handle;
264    match xw.get_cursor_window() {
265        Ok(WindowHandle(XlibWindowHandle(cursor_window))) if force || cursor_window != window => {
266            _ = xw.move_cursor_to_window(window);
267        }
268        _ => {}
269    }
270    None
271}
272
273fn from_move_mouse_over_point(
274    xw: &mut XWrap,
275    point: (i32, i32),
276) -> Option<DisplayEvent<XlibWindowHandle>> {
277    _ = xw.move_cursor_to_point(point);
278    None
279}
280
281fn from_destroyed_window(
282    xw: &mut XWrap,
283    handle: WindowHandle<XlibWindowHandle>,
284) -> Option<DisplayEvent<XlibWindowHandle>> {
285    xw.teardown_managed_window(&handle, true);
286    None
287}
288
289fn from_unfocus(
290    xw: &mut XWrap,
291    handle: Option<WindowHandle<XlibWindowHandle>>,
292    floating: bool,
293) -> Option<DisplayEvent<XlibWindowHandle>> {
294    xw.unfocus(handle, floating);
295    None
296}
297
298fn from_replay_click(
299    xw: &mut XWrap,
300    handle: WindowHandle<XlibWindowHandle>,
301    button: u8,
302) -> Option<DisplayEvent<XlibWindowHandle>> {
303    let WindowHandle(XlibWindowHandle(handle)) = handle;
304    xw.replay_click(handle, button.into());
305    None
306}
307
308fn from_set_state(
309    xw: &mut XWrap,
310    handle: WindowHandle<XlibWindowHandle>,
311    toggle_to: bool,
312    window_state: WindowState,
313) -> Option<DisplayEvent<XlibWindowHandle>> {
314    // TODO: impl from for windowstate and xlib::Atom
315    let state = match window_state {
316        WindowState::Modal => xw.atoms.NetWMStateModal,
317        WindowState::Sticky => xw.atoms.NetWMStateSticky,
318        WindowState::MaximizedVert => xw.atoms.NetWMStateMaximizedVert,
319        WindowState::MaximizedHorz => xw.atoms.NetWMStateMaximizedHorz,
320        WindowState::Shaded => xw.atoms.NetWMStateShaded,
321        WindowState::SkipTaskbar => xw.atoms.NetWMStateSkipTaskbar,
322        WindowState::SkipPager => xw.atoms.NetWMStateSkipPager,
323        WindowState::Hidden => xw.atoms.NetWMStateHidden,
324        WindowState::Fullscreen => xw.atoms.NetWMStateFullscreen,
325        WindowState::Above => xw.atoms.NetWMStateAbove,
326        WindowState::Below => xw.atoms.NetWMStateBelow,
327        WindowState::Maximized => {
328            xw.set_state(handle, toggle_to, xw.atoms.NetWMStateMaximizedVert);
329            xw.set_state(handle, toggle_to, xw.atoms.NetWMStateMaximizedHorz);
330            return None;
331        }
332    };
333    xw.set_state(handle, toggle_to, state);
334    None
335}
336
337fn from_set_window_order(
338    xw: &mut XWrap,
339    windows: Vec<WindowHandle<XlibWindowHandle>>,
340) -> Option<DisplayEvent<XlibWindowHandle>> {
341    // Unmanaged windows.
342    let unmanaged: Vec<WindowHandle<XlibWindowHandle>> = xw
343        .get_all_windows()
344        .unwrap_or_default()
345        .iter()
346        .filter(|&w| *w != xw.get_default_root())
347        .map(|&w| WindowHandle(XlibWindowHandle(w)))
348        .filter(|h| !windows.iter().any(|w| w == h))
349        .collect();
350    // Unmanaged windows on top.
351    xw.restack([unmanaged, windows].concat());
352    None
353}
354
355fn from_move_to_top(
356    xw: &mut XWrap,
357    handle: WindowHandle<XlibWindowHandle>,
358) -> Option<DisplayEvent<XlibWindowHandle>> {
359    xw.move_to_top(&handle);
360    None
361}
362
363fn from_ready_to_move_window(
364    xw: &mut XWrap,
365    handle: WindowHandle<XlibWindowHandle>,
366) -> Option<DisplayEvent<XlibWindowHandle>> {
367    xw.set_mode(Mode::ReadyToMove(handle));
368    None
369}
370
371fn from_ready_to_resize_window(
372    xw: &mut XWrap,
373    handle: WindowHandle<XlibWindowHandle>,
374) -> Option<DisplayEvent<XlibWindowHandle>> {
375    xw.set_mode(Mode::ReadyToResize(handle));
376    None
377}
378
379fn from_set_current_tags(
380    xw: &mut XWrap,
381    tag: Option<TagId>,
382) -> Option<DisplayEvent<XlibWindowHandle>> {
383    xw.set_current_desktop(tag);
384    None
385}
386
387fn from_set_window_tag(
388    xw: &mut XWrap,
389    handle: WindowHandle<XlibWindowHandle>,
390    tag: Option<TagId>,
391) -> Option<DisplayEvent<XlibWindowHandle>> {
392    let WindowHandle(XlibWindowHandle(window)) = handle;
393    let tag = tag?;
394    xw.set_window_desktop(window, &tag);
395    None
396}
397
398fn from_configure_xlib_window(
399    xw: &mut XWrap,
400    window: &Window<XlibWindowHandle>,
401) -> Option<DisplayEvent<XlibWindowHandle>> {
402    xw.configure_window(window);
403    None
404}
405
406fn from_window_take_focus(
407    xw: &mut XWrap,
408    window: &Window<XlibWindowHandle>,
409    previous_window: Option<&Window<XlibWindowHandle>>,
410) -> Option<DisplayEvent<XlibWindowHandle>> {
411    xw.window_take_focus(window, previous_window);
412    None
413}
414
415fn from_focus_window_under_cursor(xw: &mut XWrap) -> Option<DisplayEvent<XlibWindowHandle>> {
416    let point = xw.get_cursor_point().ok()?;
417    let evt = DisplayEvent::MoveFocusTo(point.0, point.1);
418    Some(evt)
419}
420
421fn from_normal_mode(xw: &mut XWrap) -> Option<DisplayEvent<XlibWindowHandle>> {
422    xw.set_mode(Mode::Normal);
423    None
424}