1use super::{
3 ICONIC_STATE, NORMAL_STATE, ROOT_EVENT_MASK, WITHDRAWN_STATE, Window, WindowHandle,
4 on_error_from_xlib, on_error_from_xlib_dummy,
5};
6use crate::{XWrap, XlibWindowHandle};
7use leftwm_core::DisplayEvent;
8use leftwm_core::config::WindowHidingStrategy;
9use leftwm_core::models::{WindowChange, WindowType, Xyhw, XyhwChange};
10use std::os::raw::{c_long, c_ulong};
11use x11_dl::xlib;
12
13impl XWrap {
14 #[must_use]
16 pub fn setup_window(&self, window: xlib::Window) -> Option<DisplayEvent<XlibWindowHandle>> {
17 let attrs = match self.get_window_attrs(window) {
19 Ok(attr) if attr.override_redirect == 0 && !self.managed_windows.contains(&window) => {
20 attr
21 }
22 _ => return None,
23 };
24 let handle = WindowHandle(XlibWindowHandle(window));
25 let name = self.get_window_name(window);
27 let legacy_name = self.get_window_legacy_name(window);
28 let class = self.get_window_class(window);
29 let pid = self.get_window_pid(window);
30 let r#type = self.get_window_type(window);
31 let states = self.get_window_states(window);
32 let actions = self.get_window_actions_atoms(window);
33 let mut can_resize = actions.contains(&self.atoms.NetWMActionResize);
34 let trans = self.get_transient_for(window);
35 let sizing_hint = self.get_hint_sizing_as_xyhw(window);
36 let wm_hint = self.get_wmhints(window);
37
38 let mut w = Window::new(handle, name, pid);
40 if let Some((res_name, res_class)) = class {
41 w.res_name = Some(res_name);
42 w.res_class = Some(res_class);
43 }
44 w.legacy_name = legacy_name;
45 w.r#type = r#type.clone();
46 w.states = states;
47 if let Some(trans) = trans {
48 w.transient = Some(WindowHandle(XlibWindowHandle(trans)));
49 }
50 let xyhw = XyhwChange {
52 x: Some(attrs.x),
53 y: Some(attrs.y),
54 w: Some(attrs.width),
55 h: Some(attrs.height),
56 ..XyhwChange::default()
57 };
58 xyhw.update_window_floating(&mut w);
59 let mut requested = Xyhw::default();
60 xyhw.update(&mut requested);
61 if let Some(mut hint) = sizing_hint {
62 can_resize = match (r#type, hint.minw, hint.minh, hint.maxw, hint.maxh) {
65 (
66 WindowType::Splash,
67 Some(min_width),
68 Some(min_height),
69 Some(max_width),
70 Some(max_height),
71 ) => can_resize || min_width != max_width || min_height != max_height,
72 _ => true,
73 };
74 hint.w = std::cmp::max(xyhw.w, hint.w);
76 hint.h = std::cmp::max(xyhw.h, hint.h);
77 hint.update_window_floating(&mut w);
78 hint.update(&mut requested);
79 }
80 w.requested = Some(requested);
81 w.can_resize = can_resize;
82 if let Some(hint) = wm_hint {
83 w.never_focus = hint.flags & xlib::InputHint != 0 && hint.input == 0;
84 }
85 if let Some(hint) = wm_hint {
86 w.urgent = hint.flags & xlib::XUrgencyHint != 0;
87 }
88 if w.floating()
90 && sizing_hint.is_none()
91 && let Ok(geo) = self.get_window_geometry(window)
92 {
93 geo.update_window_floating(&mut w);
94 }
95
96 let cursor = self.get_cursor_point().unwrap_or_default();
97 Some(DisplayEvent::WindowCreate(w, cursor.0, cursor.1))
98 }
99
100 pub fn setup_managed_window(
103 &mut self,
104 h: WindowHandle<XlibWindowHandle>,
105 floating: bool,
106 follow_mouse: bool,
107 ) -> Option<DisplayEvent<XlibWindowHandle>> {
108 let WindowHandle(XlibWindowHandle(handle)) = h;
109 self.subscribe_to_window_events(handle);
110 self.managed_windows.push(handle);
111 unsafe { (self.xlib.XMapWindow)(self.display, handle) };
113 let list = vec![handle as c_long];
115 self.append_property_long(self.root, self.atoms.NetClientList, xlib::XA_WINDOW, &list);
116
117 let states = self.get_window_states_atoms(handle);
119 self.set_window_states_atoms(handle, &states);
120 self.set_wm_states(handle, &[NORMAL_STATE]);
122
123 let r#type = self.get_window_type(handle);
124 if r#type == WindowType::Dock || r#type == WindowType::Desktop {
125 if let Some(dock_area) = self.get_window_strut_array(handle) {
126 let dems = self.get_screens_area_dimensions();
127 let screen = self
128 .get_screens()
129 .iter()
130 .find(|s| s.contains_dock_area(dock_area, dems))?
131 .clone();
132
133 if let Some(xyhw) = dock_area.as_xyhw(dems.0, dems.1, &screen) {
134 let mut change = WindowChange::new(h);
135 change.strut = Some(xyhw.into());
136 change.r#type = Some(r#type);
137 return Some(DisplayEvent::WindowChange(change));
138 }
139 } else if let Ok(geo) = self.get_window_geometry(handle) {
140 let mut xyhw = Xyhw::default();
141 geo.update(&mut xyhw);
142 let mut change = WindowChange::new(h);
143 change.strut = Some(xyhw.into());
144 change.r#type = Some(r#type);
145 return Some(DisplayEvent::WindowChange(change));
146 }
147 } else {
148 let color = if floating {
149 self.colors.floating
150 } else {
151 self.colors.normal
152 };
153 self.set_window_border_color(handle, color);
154
155 if follow_mouse {
156 _ = self.move_cursor_to_window(handle);
157 }
158 if self.focus_behaviour.is_clickto() {
159 self.grab_mouse_clicks(handle, false);
160 }
161 }
162 None
163 }
164
165 pub fn teardown_managed_window(&mut self, h: &WindowHandle<XlibWindowHandle>, destroyed: bool) {
169 let WindowHandle(XlibWindowHandle(handle)) = h;
170 self.managed_windows.retain(|x| *x != *handle);
171 if !destroyed {
172 unsafe {
173 (self.xlib.XGrabServer)(self.display);
174 (self.xlib.XSetErrorHandler)(Some(on_error_from_xlib_dummy));
175 self.ungrab_buttons(*handle);
176 self.set_wm_states(*handle, &[WITHDRAWN_STATE]);
177 self.sync();
178 (self.xlib.XSetErrorHandler)(Some(on_error_from_xlib));
179 (self.xlib.XUngrabServer)(self.display);
180 }
181 }
182 self.set_client_list();
183 }
184
185 pub fn update_window(&self, window: &Window<XlibWindowHandle>) {
187 let WindowHandle(XlibWindowHandle(handle)) = window.handle;
188 if window.visible() {
189 let changes = xlib::XWindowChanges {
190 x: window.x(),
191 y: window.y(),
192 width: window.width(),
193 height: window.height(),
194 border_width: window.border(),
195 sibling: 0, stack_mode: 0, };
198 let unlock =
199 xlib::CWX | xlib::CWY | xlib::CWWidth | xlib::CWHeight | xlib::CWBorderWidth;
200 self.set_window_config(handle, changes, u32::from(unlock));
201 self.configure_window(window);
202 }
203 let Some(state) = self.get_wm_state(handle) else {
204 return;
205 };
206 if window.visible() && state != NORMAL_STATE {
208 self.toggle_window_visibility(handle, true, window.hiding_strategy);
209 } else if !window.visible() && state != ICONIC_STATE {
210 self.toggle_window_visibility(handle, false, window.hiding_strategy);
211 }
212 }
213
214 pub fn toggle_window_visibility(
220 &self,
221 window: xlib::Window,
222 visible: bool,
223 preferred_stategy: Option<WindowHidingStrategy>,
224 ) {
225 let hiding_strategy = preferred_stategy.unwrap_or(self.window_hiding_strategy);
226 let maybe_change_mask = |mask| {
227 if let WindowHidingStrategy::Unmap = hiding_strategy {
228 let mut attrs: xlib::XSetWindowAttributes = unsafe { std::mem::zeroed() };
229 attrs.event_mask = mask;
230 self.change_window_attributes(self.root, xlib::CWEventMask, attrs);
231 }
232 };
233 maybe_change_mask(ROOT_EVENT_MASK & !(xlib::SubstructureNotifyMask));
235
236 if visible {
237 if hiding_strategy == WindowHidingStrategy::Unmap {
240 unsafe { (self.xlib.XMapWindow)(self.display, window) };
241 }
242
243 self.set_wm_states(window, &[NORMAL_STATE]);
245 if self.focus_behaviour.is_clickto() && self.get_window_type(window) != WindowType::Dock
247 {
248 self.grab_mouse_clicks(window, false);
249 }
250 } else {
251 self.ungrab_buttons(window);
253
254 match hiding_strategy {
255 WindowHidingStrategy::Unmap => {
256 unsafe { (self.xlib.XUnmapWindow)(self.display, window) };
257 }
258 WindowHidingStrategy::MoveMinimize | WindowHidingStrategy::MoveOnly => {
259 let Ok(window_geometry) = self.get_window_geometry(window) else {
261 tracing::error!("Error querying window geometry for window {}", window);
262 return;
263 };
264
265 let (x, y) = (
266 window_geometry
267 .w
268 .unwrap_or_else(|| self.get_screens_area_dimensions().0)
269 * -2,
270 window_geometry
271 .h
272 .unwrap_or_else(|| self.get_screens_area_dimensions().1)
273 * -2,
274 );
275
276 let mut window_changes: xlib::XWindowChanges = unsafe { std::mem::zeroed() };
277 window_changes.x = x;
278 window_changes.y = y;
279 self.set_window_config(
280 window,
281 window_changes,
282 u32::from(xlib::CWX | xlib::CWY),
283 );
284 self.move_resize_window(window, x, y, 0, 0);
285 }
286 }
287
288 let new_state = if hiding_strategy == WindowHidingStrategy::Unmap
291 || hiding_strategy == WindowHidingStrategy::MoveMinimize
292 {
293 ICONIC_STATE
294 } else {
295 WITHDRAWN_STATE
296 };
297 self.set_wm_states(window, &[new_state]);
298 }
299
300 maybe_change_mask(ROOT_EVENT_MASK);
301 }
302
303 pub fn window_take_focus(
305 &mut self,
306 window: &Window<XlibWindowHandle>,
307 previous: Option<&Window<XlibWindowHandle>>,
308 ) {
309 let WindowHandle(XlibWindowHandle(handle)) = window.handle;
310 if let Some(previous) = previous {
312 let WindowHandle(XlibWindowHandle(previous_handle)) = previous.handle;
313 let color = if previous.floating() {
314 self.colors.floating
315 } else {
316 self.colors.normal
317 };
318 self.set_window_border_color(previous_handle, color);
319 if self.focus_behaviour.is_clickto() {
321 self.grab_mouse_clicks(previous_handle, false);
322 }
323 }
324 self.focused_window = handle;
325 self.grab_mouse_clicks(handle, true);
326 self.set_window_urgency(handle, false);
327 self.set_window_border_color(handle, self.colors.active);
328 self.focus(handle, window.never_focus);
329 self.sync();
330 }
331
332 pub fn focus(&mut self, window: xlib::Window, never_focus: bool) {
335 if !never_focus {
336 unsafe {
337 (self.xlib.XSetInputFocus)(
338 self.display,
339 window,
340 xlib::RevertToPointerRoot,
341 xlib::CurrentTime,
342 );
343 let list = vec![window as c_long];
344 self.replace_property_long(
346 self.root,
347 self.atoms.NetActiveWindow,
348 xlib::XA_WINDOW,
349 &list,
350 );
351 std::mem::forget(list);
352 }
353 }
354 self.send_xevent_atom(window, self.atoms.WMTakeFocus);
356 }
357
358 pub fn unfocus(&self, handle: Option<WindowHandle<XlibWindowHandle>>, floating: bool) {
361 if let Some(WindowHandle(XlibWindowHandle(handle))) = handle {
362 let color = if floating {
363 self.colors.floating
364 } else {
365 self.colors.normal
366 };
367 self.set_window_border_color(handle, color);
368
369 self.grab_mouse_clicks(handle, false);
370 }
371 unsafe {
372 (self.xlib.XSetInputFocus)(
373 self.display,
374 self.root,
375 xlib::RevertToPointerRoot,
376 xlib::CurrentTime,
377 );
378 self.replace_property_long(
379 self.root,
380 self.atoms.NetActiveWindow,
381 xlib::XA_WINDOW,
382 &[c_long::MAX],
383 );
384 }
385 }
386
387 pub fn configure_window(&self, window: &Window<XlibWindowHandle>) {
389 let WindowHandle(XlibWindowHandle(handle)) = window.handle;
390 let mut configure_event: xlib::XConfigureEvent = unsafe { std::mem::zeroed() };
391 configure_event.type_ = xlib::ConfigureNotify;
392 configure_event.display = self.display;
393 configure_event.event = handle;
394 configure_event.window = handle;
395 configure_event.x = window.x();
396 configure_event.y = window.y();
397 configure_event.width = window.width();
398 configure_event.height = window.height();
399 configure_event.border_width = window.border;
400 configure_event.above = 0;
401 configure_event.override_redirect = 0;
402 self.send_xevent(
403 handle,
404 0,
405 xlib::StructureNotifyMask,
406 &mut configure_event.into(),
407 );
408 }
409
410 pub fn change_window_attributes(
413 &self,
414 window: xlib::Window,
415 mask: c_ulong,
416 mut attrs: xlib::XSetWindowAttributes,
417 ) {
418 unsafe {
419 (self.xlib.XChangeWindowAttributes)(self.display, window, mask, &raw mut attrs);
420 }
421 }
422
423 pub fn restack(&self, handles: Vec<WindowHandle<XlibWindowHandle>>) {
426 let mut windows = vec![];
427 for handle in handles {
428 let WindowHandle(XlibWindowHandle(window)) = handle;
429 windows.push(window);
430 }
431 let size = windows.len();
432 let ptr = windows.as_mut_ptr();
433 unsafe {
434 (self.xlib.XRestackWindows)(self.display, ptr, size as i32);
435 }
436 }
437
438 pub fn move_resize_window(&self, window: xlib::Window, x: i32, y: i32, w: u32, h: u32) {
439 unsafe {
440 (self.xlib.XMoveResizeWindow)(self.display, window, x, y, w, h);
441 }
442 }
443
444 pub fn move_to_top(&self, handle: &WindowHandle<XlibWindowHandle>) {
447 let WindowHandle(XlibWindowHandle(window)) = handle;
448 unsafe {
449 (self.xlib.XRaiseWindow)(self.display, *window);
450 }
451 }
452
453 pub fn kill_window(&self, h: &WindowHandle<XlibWindowHandle>) {
459 let WindowHandle(XlibWindowHandle(handle)) = h;
460 if !self.send_xevent_atom(*handle, self.atoms.WMDelete) {
462 unsafe {
464 (self.xlib.XGrabServer)(self.display);
465 (self.xlib.XSetErrorHandler)(Some(on_error_from_xlib_dummy));
466 (self.xlib.XSetCloseDownMode)(self.display, xlib::DestroyAll);
467 (self.xlib.XKillClient)(self.display, *handle);
468 self.sync();
469 (self.xlib.XSetErrorHandler)(Some(on_error_from_xlib));
470 (self.xlib.XUngrabServer)(self.display);
471 }
472 }
473 }
474
475 pub fn force_unmapped(&mut self, window: xlib::Window) {
477 let managed = self.managed_windows.contains(&window);
478 if managed {
479 self.managed_windows.retain(|x| *x != window);
480 self.set_client_list();
481 }
482 }
483
484 pub fn subscribe_to_event(&self, window: xlib::Window, mask: c_long) {
487 unsafe { (self.xlib.XSelectInput)(self.display, window, mask) };
488 }
489
490 pub fn subscribe_to_window_events(&self, window: xlib::Window) {
492 let mask = xlib::EnterWindowMask | xlib::FocusChangeMask | xlib::PropertyChangeMask;
493 self.subscribe_to_event(window, mask);
494 }
495}