1use super::{
3 on_error_from_xlib, on_error_from_xlib_dummy, Window, WindowHandle, ICONIC_STATE, NORMAL_STATE,
4 ROOT_EVENT_MASK, WITHDRAWN_STATE,
5};
6use crate::{XWrap, XlibWindowHandle};
7use leftwm_core::config::WindowHidingStrategy;
8use leftwm_core::models::{WindowChange, WindowType, Xyhw, XyhwChange};
9use leftwm_core::DisplayEvent;
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() && sizing_hint.is_none() {
90 if let Ok(geo) = self.get_window_geometry(window) {
91 geo.update_window_floating(&mut w);
92 }
93 }
94
95 let cursor = self.get_cursor_point().unwrap_or_default();
96 Some(DisplayEvent::WindowCreate(w, cursor.0, cursor.1))
97 }
98
99 pub fn setup_managed_window(
102 &mut self,
103 h: WindowHandle<XlibWindowHandle>,
104 floating: bool,
105 follow_mouse: bool,
106 ) -> Option<DisplayEvent<XlibWindowHandle>> {
107 let WindowHandle(XlibWindowHandle(handle)) = h;
108 self.subscribe_to_window_events(handle);
109 self.managed_windows.push(handle);
110 unsafe { (self.xlib.XMapWindow)(self.display, handle) };
112 let list = vec![handle as c_long];
114 self.append_property_long(self.root, self.atoms.NetClientList, xlib::XA_WINDOW, &list);
115
116 let states = self.get_window_states_atoms(handle);
118 self.set_window_states_atoms(handle, &states);
119 self.set_wm_states(handle, &[NORMAL_STATE]);
121
122 let r#type = self.get_window_type(handle);
123 if r#type == WindowType::Dock || r#type == WindowType::Desktop {
124 if let Some(dock_area) = self.get_window_strut_array(handle) {
125 let dems = self.get_screens_area_dimensions();
126 let screen = self
127 .get_screens()
128 .iter()
129 .find(|s| s.contains_dock_area(dock_area, dems))?
130 .clone();
131
132 if let Some(xyhw) = dock_area.as_xyhw(dems.0, dems.1, &screen) {
133 let mut change = WindowChange::new(h);
134 change.strut = Some(xyhw.into());
135 change.r#type = Some(r#type);
136 return Some(DisplayEvent::WindowChange(change));
137 }
138 } else if let Ok(geo) = self.get_window_geometry(handle) {
139 let mut xyhw = Xyhw::default();
140 geo.update(&mut xyhw);
141 let mut change = WindowChange::new(h);
142 change.strut = Some(xyhw.into());
143 change.r#type = Some(r#type);
144 return Some(DisplayEvent::WindowChange(change));
145 }
146 } else {
147 let color = if floating {
148 self.colors.floating
149 } else {
150 self.colors.normal
151 };
152 self.set_window_border_color(handle, color);
153
154 if follow_mouse {
155 _ = self.move_cursor_to_window(handle);
156 }
157 if self.focus_behaviour.is_clickto() {
158 self.grab_mouse_clicks(handle, false);
159 }
160 }
161 None
162 }
163
164 pub fn teardown_managed_window(&mut self, h: &WindowHandle<XlibWindowHandle>, destroyed: bool) {
168 let WindowHandle(XlibWindowHandle(handle)) = h;
169 self.managed_windows.retain(|x| *x != *handle);
170 if !destroyed {
171 unsafe {
172 (self.xlib.XGrabServer)(self.display);
173 (self.xlib.XSetErrorHandler)(Some(on_error_from_xlib_dummy));
174 self.ungrab_buttons(*handle);
175 self.set_wm_states(*handle, &[WITHDRAWN_STATE]);
176 self.sync();
177 (self.xlib.XSetErrorHandler)(Some(on_error_from_xlib));
178 (self.xlib.XUngrabServer)(self.display);
179 }
180 }
181 self.set_client_list();
182 }
183
184 pub fn update_window(&self, window: &Window<XlibWindowHandle>) {
186 let WindowHandle(XlibWindowHandle(handle)) = window.handle;
187 if window.visible() {
188 let changes = xlib::XWindowChanges {
189 x: window.x(),
190 y: window.y(),
191 width: window.width(),
192 height: window.height(),
193 border_width: window.border(),
194 sibling: 0, stack_mode: 0, };
197 let unlock =
198 xlib::CWX | xlib::CWY | xlib::CWWidth | xlib::CWHeight | xlib::CWBorderWidth;
199 self.set_window_config(handle, changes, u32::from(unlock));
200 self.configure_window(window);
201 }
202 let Some(state) = self.get_wm_state(handle) else {
203 return;
204 };
205 if window.visible() && state != NORMAL_STATE {
207 self.toggle_window_visibility(handle, true, window.hiding_strategy);
208 } else if !window.visible() && state != ICONIC_STATE {
209 self.toggle_window_visibility(handle, false, window.hiding_strategy);
210 }
211 }
212
213 pub fn toggle_window_visibility(
219 &self,
220 window: xlib::Window,
221 visible: bool,
222 preferred_stategy: Option<WindowHidingStrategy>,
223 ) {
224 let hiding_strategy = preferred_stategy.unwrap_or(self.window_hiding_strategy);
225 let maybe_change_mask = |mask| {
226 if let WindowHidingStrategy::Unmap = hiding_strategy {
227 let mut attrs: xlib::XSetWindowAttributes = unsafe { std::mem::zeroed() };
228 attrs.event_mask = mask;
229 self.change_window_attributes(self.root, xlib::CWEventMask, attrs);
230 }
231 };
232 maybe_change_mask(ROOT_EVENT_MASK & !(xlib::SubstructureNotifyMask));
234
235 if visible {
236 if hiding_strategy == WindowHidingStrategy::Unmap {
239 unsafe { (self.xlib.XMapWindow)(self.display, window) };
240 }
241
242 self.set_wm_states(window, &[NORMAL_STATE]);
244 if self.focus_behaviour.is_clickto() && self.get_window_type(window) != WindowType::Dock
246 {
247 self.grab_mouse_clicks(window, false);
248 }
249 } else {
250 self.ungrab_buttons(window);
252
253 match hiding_strategy {
254 WindowHidingStrategy::Unmap => {
255 unsafe { (self.xlib.XUnmapWindow)(self.display, window) };
256 }
257 WindowHidingStrategy::MoveMinimize | WindowHidingStrategy::MoveOnly => {
258 let Ok(window_geometry) = self.get_window_geometry(window) else {
260 tracing::error!("Error querying window geometry for window {}", window);
261 return;
262 };
263
264 let (x, y) = (
265 window_geometry
266 .w
267 .unwrap_or_else(|| self.get_screens_area_dimensions().0)
268 * -2,
269 window_geometry
270 .h
271 .unwrap_or_else(|| self.get_screens_area_dimensions().1)
272 * -2,
273 );
274
275 let mut window_changes: xlib::XWindowChanges = unsafe { std::mem::zeroed() };
276 window_changes.x = x;
277 window_changes.y = y;
278 self.set_window_config(
279 window,
280 window_changes,
281 u32::from(xlib::CWX | xlib::CWY),
282 );
283 self.move_resize_window(window, x, y, 0, 0);
284 }
285 }
286
287 if hiding_strategy == WindowHidingStrategy::Unmap
289 || hiding_strategy == WindowHidingStrategy::MoveMinimize
290 {
291 self.set_wm_states(window, &[ICONIC_STATE]);
292 }
293 }
294
295 maybe_change_mask(ROOT_EVENT_MASK);
296 }
297
298 pub fn window_take_focus(
300 &mut self,
301 window: &Window<XlibWindowHandle>,
302 previous: Option<&Window<XlibWindowHandle>>,
303 ) {
304 let WindowHandle(XlibWindowHandle(handle)) = window.handle;
305 if let Some(previous) = previous {
307 let WindowHandle(XlibWindowHandle(previous_handle)) = previous.handle;
308 let color = if previous.floating() {
309 self.colors.floating
310 } else {
311 self.colors.normal
312 };
313 self.set_window_border_color(previous_handle, color);
314 if self.focus_behaviour.is_clickto() {
316 self.grab_mouse_clicks(previous_handle, false);
317 }
318 }
319 self.focused_window = handle;
320 self.grab_mouse_clicks(handle, true);
321 self.set_window_urgency(handle, false);
322 self.set_window_border_color(handle, self.colors.active);
323 self.focus(handle, window.never_focus);
324 self.sync();
325 }
326
327 pub fn focus(&mut self, window: xlib::Window, never_focus: bool) {
330 if !never_focus {
331 unsafe {
332 (self.xlib.XSetInputFocus)(
333 self.display,
334 window,
335 xlib::RevertToPointerRoot,
336 xlib::CurrentTime,
337 );
338 let list = vec![window as c_long];
339 self.replace_property_long(
341 self.root,
342 self.atoms.NetActiveWindow,
343 xlib::XA_WINDOW,
344 &list,
345 );
346 std::mem::forget(list);
347 }
348 }
349 self.send_xevent_atom(window, self.atoms.WMTakeFocus);
351 }
352
353 pub fn unfocus(&self, handle: Option<WindowHandle<XlibWindowHandle>>, floating: bool) {
356 if let Some(WindowHandle(XlibWindowHandle(handle))) = handle {
357 let color = if floating {
358 self.colors.floating
359 } else {
360 self.colors.normal
361 };
362 self.set_window_border_color(handle, color);
363
364 self.grab_mouse_clicks(handle, false);
365 }
366 unsafe {
367 (self.xlib.XSetInputFocus)(
368 self.display,
369 self.root,
370 xlib::RevertToPointerRoot,
371 xlib::CurrentTime,
372 );
373 self.replace_property_long(
374 self.root,
375 self.atoms.NetActiveWindow,
376 xlib::XA_WINDOW,
377 &[c_long::MAX],
378 );
379 }
380 }
381
382 pub fn configure_window(&self, window: &Window<XlibWindowHandle>) {
384 let WindowHandle(XlibWindowHandle(handle)) = window.handle;
385 let mut configure_event: xlib::XConfigureEvent = unsafe { std::mem::zeroed() };
386 configure_event.type_ = xlib::ConfigureNotify;
387 configure_event.display = self.display;
388 configure_event.event = handle;
389 configure_event.window = handle;
390 configure_event.x = window.x();
391 configure_event.y = window.y();
392 configure_event.width = window.width();
393 configure_event.height = window.height();
394 configure_event.border_width = window.border;
395 configure_event.above = 0;
396 configure_event.override_redirect = 0;
397 self.send_xevent(
398 handle,
399 0,
400 xlib::StructureNotifyMask,
401 &mut configure_event.into(),
402 );
403 }
404
405 pub fn change_window_attributes(
408 &self,
409 window: xlib::Window,
410 mask: c_ulong,
411 mut attrs: xlib::XSetWindowAttributes,
412 ) {
413 unsafe {
414 (self.xlib.XChangeWindowAttributes)(self.display, window, mask, &mut attrs);
415 }
416 }
417
418 pub fn restack(&self, handles: Vec<WindowHandle<XlibWindowHandle>>) {
421 let mut windows = vec![];
422 for handle in handles {
423 let WindowHandle(XlibWindowHandle(window)) = handle;
424 windows.push(window);
425 }
426 let size = windows.len();
427 let ptr = windows.as_mut_ptr();
428 unsafe {
429 (self.xlib.XRestackWindows)(self.display, ptr, size as i32);
430 }
431 }
432
433 pub fn move_resize_window(&self, window: xlib::Window, x: i32, y: i32, w: u32, h: u32) {
434 unsafe {
435 (self.xlib.XMoveResizeWindow)(self.display, window, x, y, w, h);
436 }
437 }
438
439 pub fn move_to_top(&self, handle: &WindowHandle<XlibWindowHandle>) {
442 let WindowHandle(XlibWindowHandle(window)) = handle;
443 unsafe {
444 (self.xlib.XRaiseWindow)(self.display, *window);
445 }
446 }
447
448 pub fn kill_window(&self, h: &WindowHandle<XlibWindowHandle>) {
454 let WindowHandle(XlibWindowHandle(handle)) = h;
455 if !self.send_xevent_atom(*handle, self.atoms.WMDelete) {
457 unsafe {
459 (self.xlib.XGrabServer)(self.display);
460 (self.xlib.XSetErrorHandler)(Some(on_error_from_xlib_dummy));
461 (self.xlib.XSetCloseDownMode)(self.display, xlib::DestroyAll);
462 (self.xlib.XKillClient)(self.display, *handle);
463 self.sync();
464 (self.xlib.XSetErrorHandler)(Some(on_error_from_xlib));
465 (self.xlib.XUngrabServer)(self.display);
466 }
467 }
468 }
469
470 pub fn force_unmapped(&mut self, window: xlib::Window) {
472 let managed = self.managed_windows.contains(&window);
473 if managed {
474 self.managed_windows.retain(|x| *x != window);
475 self.set_client_list();
476 }
477 }
478
479 pub fn subscribe_to_event(&self, window: xlib::Window, mask: c_long) {
482 unsafe { (self.xlib.XSelectInput)(self.display, window, mask) };
483 }
484
485 pub fn subscribe_to_window_events(&self, window: xlib::Window) {
487 let mask = xlib::EnterWindowMask | xlib::FocusChangeMask | xlib::PropertyChangeMask;
488 self.subscribe_to_event(window, mask);
489 }
490}