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!(
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 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 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
240fn 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 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 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 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}