1use super::{Manager, Window, WindowChange, WindowType, Workspace};
2use crate::child_process::exec_shell;
3use crate::config::{Config, InsertBehavior};
4use crate::display_action::DisplayAction;
5use crate::display_servers::DisplayServer;
6use crate::layouts::{self, MAIN_AND_VERT_STACK};
7use crate::models::{Handle, WindowHandle, WindowState, Xyhw};
8use crate::state::State;
9use crate::utils::helpers;
10use std::env;
11use std::str::FromStr;
12
13impl<H: Handle, C: Config, SERVER: DisplayServer<H>> Manager<H, C, SERVER> {
14 pub fn window_created_handler(&mut self, mut window: Window<H>, x: i32, y: i32) -> bool {
19 if self.state.windows.iter().any(|w| w.handle == window.handle) {
21 return false;
22 }
23
24 self.config
26 .setup_predefined_window(&mut self.state, &mut window);
27
28 let mut is_first = false;
30 let mut on_same_tag = true;
31 let mut layout = MAIN_AND_VERT_STACK.to_string();
33 setup_window(
34 &mut self.state,
35 &mut window,
36 (x, y),
37 &mut layout,
38 &mut is_first,
39 &mut on_same_tag,
40 );
41 self.config.load_window(&mut window);
42 insert_window(&mut self.state, &mut window, &layout);
43
44 let follow_mouse = self.state.focus_manager.focus_new_windows
45 && self.state.focus_manager.behaviour.is_sloppy()
46 && self.state.focus_manager.sloppy_mouse_follows_focus
47 && on_same_tag;
48
49 let act = DisplayAction::AddedWindow(window.handle, window.floating(), follow_mouse);
51 self.state.actions.push_back(act);
52
53 if window.tag.is_some() {
55 let act = DisplayAction::SetWindowTag(window.handle, window.tag);
56 self.state.actions.push_back(act);
57 }
58
59 self.state.sort_windows();
61
62 self.state.handle_single_border(self.config.border_width());
64
65 if (self.state.focus_manager.focus_new_windows || is_first) && on_same_tag {
69 self.state.focus_window(&window.handle);
70 }
71
72 if let Some(cmd) = &self.config.on_new_window_cmd() {
74 exec_shell(cmd, &mut self.children);
75 }
76
77 true
78 }
79
80 pub fn window_destroyed_handler(&mut self, handle: &WindowHandle<H>) -> bool {
85 let new_handle = if let Some(Some(last_focused_window)) = self
87 .state
88 .focus_manager
89 .window_history
90 .iter()
91 .find(|w| w != &&Some(*handle))
93 {
94 Some(*last_focused_window)
95 } else {
96 self.get_next_or_previous_handle(handle)
97 };
98 let (transient, floating, visible) =
100 match self.state.windows.iter().find(|w| &w.handle == handle) {
101 Some(window) => (window.transient, window.floating(), window.visible()),
102 None => return false,
103 };
104 self.state
105 .focus_manager
106 .tags_last_window
107 .retain(|_, h| h != handle);
108 self.state.windows.retain(|w| &w.handle != handle);
109
110 self.state.handle_single_border(self.config.border_width());
111
112 update_workspace_avoid_list(&mut self.state);
114
115 let focused = self.state.focus_manager.window_history.front();
116 if focused == Some(&Some(*handle)) {
118 if self.state.focus_manager.behaviour.is_sloppy()
119 && self.state.focus_manager.sloppy_mouse_follows_focus
120 {
121 let act = DisplayAction::FocusWindowUnderCursor;
122 self.state.actions.push_back(act);
123 self.state.focus_manager.window_history.push_front(None);
126 } else if let Some(parent) =
127 find_transient_parent(&self.state.windows, transient).map(|p| p.handle)
128 {
129 self.state.focus_window(&parent);
130 } else if let Some(handle) = new_handle {
131 self.state.focus_window(&handle);
132 } else {
133 let act = DisplayAction::Unfocus(Some(*handle), floating);
134 self.state.actions.push_back(act);
135 self.state.focus_manager.window_history.push_front(None);
136 }
137 }
138
139 self.state
141 .focus_manager
142 .window_history
143 .retain(|w| w != &Some(*handle));
144
145 visible
147 }
148
149 pub fn window_changed_handler(&mut self, change: WindowChange<H>) -> bool {
154 let mut changed = false;
155 let mut fullscreen_changed = false;
156 let mut above_changed = false;
157 let strut_changed = change.strut.is_some();
158 let windows = self.state.windows.clone();
159 if let Some(window) = self
160 .state
161 .windows
162 .iter_mut()
163 .find(|w| w.handle == change.handle)
164 {
165 if let Some(states) = &change.states {
166 fullscreen_changed =
167 states.contains(&WindowState::Fullscreen) != window.is_fullscreen();
168 above_changed = states.contains(&WindowState::Above)
169 != window.states.contains(&WindowState::Above);
170 }
171 let container = match find_transient_parent(&windows, window.transient) {
172 Some(parent) => Some(parent.exact_xyhw()),
173 None if window.r#type == WindowType::Dialog => self
174 .state
175 .workspaces
176 .iter()
177 .find(|ws| ws.tag == window.tag)
178 .map(|ws| ws.xyhw),
179 _ => None,
180 };
181
182 changed = change.update(window, container);
183 if window.r#type == WindowType::Dock {
184 update_workspace_avoid_list(&mut self.state);
185 }
188 }
189 if fullscreen_changed {
190 if let Some(windows) = self
193 .state
194 .windows
195 .iter()
196 .find(|w| w.r#type == WindowType::Dock)
197 {
198 self.display_server.update_windows(vec![windows]);
199 }
200 }
201
202 if fullscreen_changed || above_changed {
203 self.state.sort_windows();
205 }
206 if strut_changed {
207 self.state.update_static();
208 }
209 changed
210 }
211
212 pub fn get_next_or_previous_handle(
217 &mut self,
218 handle: &WindowHandle<H>,
219 ) -> Option<WindowHandle<H>> {
220 let focused_workspace = self.state.focus_manager.workspace(&self.state.workspaces)?;
221 let on_focused_workspace = |x: &Window<H>| -> bool { focused_workspace.is_managed(x) };
222 let mut windows_on_workspace =
223 helpers::vec_extract(&mut self.state.windows, on_focused_workspace);
224 let is_handle = |x: &Window<H>| -> bool { &x.handle == handle };
225 let new_handle = helpers::relative_find(&windows_on_workspace, is_handle, 1, false)
226 .or_else(|| helpers::relative_find(&windows_on_workspace, is_handle, -1, false))
227 .map(|w| w.handle);
228 self.state.windows.append(&mut windows_on_workspace);
229 new_handle
230 }
231}
232
233fn find_terminal<H: Handle>(state: &State<H>, pid: Option<u32>) -> Option<&Window<H>> {
236 let shell_path = env::var("SHELL").ok()?;
238 let shell = shell_path.split('/').last()?;
240 let is_terminal = |pid: u32| -> Option<bool> {
242 let parent = std::fs::read(format!("/proc/{pid}/comm")).ok()?;
243 let parent_bytes = parent.split(|&c| c == b' ').next()?;
244 let parent_str = std::str::from_utf8(parent_bytes).ok()?.strip_suffix('\n')?;
245 Some(parent_str == shell)
246 };
247
248 let get_parent = |pid: u32| -> Option<u32> {
249 let stat = std::fs::read(format!("/proc/{pid}/stat")).ok()?;
250 let ppid_bytes = stat.split(|&c| c == b' ').nth(3)?;
251 let ppid_str = std::str::from_utf8(ppid_bytes).ok()?;
252 let ppid_u32 = u32::from_str(ppid_str).ok()?;
253 Some(ppid_u32)
254 };
255
256 let pid = pid?;
257 let shell_id = get_parent(pid)?;
258 if is_terminal(shell_id)? {
259 let terminal = get_parent(shell_id)?;
260 return state.windows.iter().find(|w| w.pid == Some(terminal));
261 }
262
263 None
264}
265
266fn find_transient_parent<H: Handle>(
267 windows: &[Window<H>],
268 transient: Option<WindowHandle<H>>,
269) -> Option<&Window<H>> {
270 let mut transient = transient?;
271 loop {
272 transient = if let Some(found) = windows
273 .iter()
274 .find(|x| x.handle == transient)
275 .and_then(|x| x.transient)
276 {
277 found
278 } else {
279 return windows.iter().find(|x| x.handle == transient);
280 };
281 }
282}
283
284fn insert_window<H: Handle>(state: &mut State<H>, window: &mut Window<H>, layout: &str) {
285 let mut was_fullscreen = false;
286 if window.r#type == WindowType::Normal {
287 let for_active_workspace =
288 |x: &Window<H>| -> bool { window.tag == x.tag && x.is_managed() };
289 if let Some(fsw) = state
291 .windows
292 .iter_mut()
293 .find(|w| for_active_workspace(w) && w.is_fullscreen())
294 {
295 let act =
296 DisplayAction::SetState(fsw.handle, !fsw.is_fullscreen(), WindowState::Fullscreen);
297 state.actions.push_back(act);
298 was_fullscreen = true;
299 } else if let Some(fsw) = state
300 .windows
301 .iter_mut()
302 .find(|w| for_active_workspace(w) && w.is_maximized())
303 {
304 if !window.floating() {
305 let act = DisplayAction::SetState(
306 fsw.handle,
307 !fsw.is_maximized(),
308 WindowState::Maximized,
309 );
310 state.actions.push_back(act);
311 }
312 }
313
314 let monocle = layouts::MONOCLE;
316 let main_and_deck = layouts::MAIN_AND_DECK;
317 if layout == monocle || layout == main_and_deck {
318 let mut to_reorder = helpers::vec_extract(&mut state.windows, for_active_workspace);
320 if layout == monocle || to_reorder.is_empty() {
321 if was_fullscreen {
324 let act = DisplayAction::SetState(
325 window.handle,
326 !window.is_fullscreen(),
327 WindowState::Fullscreen,
328 );
329 state.actions.push_back(act);
330 }
331 to_reorder.insert(0, window.clone());
333 } else {
334 to_reorder.insert(1, window.clone());
336 }
337 state.windows.append(&mut to_reorder);
338 return;
339 }
340 }
341
342 if window.r#type == WindowType::Dialog
344 || window.r#type == WindowType::Splash
345 || window.r#type == WindowType::Utility
346 || window.floating()
347 || is_scratchpad(state, window)
348 {
349 state.windows.insert(0, window.clone());
350 return;
351 }
352
353 let current_index = state
354 .focus_manager
355 .window(&state.windows)
356 .and_then(|current| {
357 state
358 .windows
359 .iter()
360 .position(|w| w.handle == current.handle)
361 })
362 .unwrap_or(0);
363
364 match state.insert_behavior {
366 InsertBehavior::Top => state.windows.insert(0, window.clone()),
367 InsertBehavior::Bottom => state.windows.push(window.clone()),
368 InsertBehavior::AfterCurrent if current_index < state.windows.len() => {
369 state.windows.insert(current_index + 1, window.clone());
370 }
371 InsertBehavior::AfterCurrent | InsertBehavior::BeforeCurrent => {
372 state.windows.insert(current_index, window.clone());
373 }
374 }
375}
376
377fn is_scratchpad<H: Handle>(state: &State<H>, window: &Window<H>) -> bool {
378 state
379 .active_scratchpads
380 .iter()
381 .any(|(_, id)| id.iter().any(|id| window.pid == Some(*id)))
382}
383
384fn set_relative_floating<H: Handle>(window: &mut Window<H>, ws: &Workspace, outer: Xyhw) {
387 window.set_floating(true);
388 window.normal = ws.xyhw;
389 let xyhw = window.requested.map_or_else(
390 || ws.center_halfed(),
391 |mut requested| {
392 requested.center_relative(outer, window.border);
393 if !ws.xyhw_avoided.contains_xyhw(&requested) {
394 requested.center_relative(ws.xyhw_avoided, window.border);
395 }
396 requested
397 },
398 );
399 window.set_floating_exact(xyhw);
400}
401
402fn setup_window<H: Handle>(
403 state: &mut State<H>,
404 window: &mut Window<H>,
405 xy: (i32, i32),
406 layout: &mut String,
407 is_first: &mut bool,
408 on_same_tag: &mut bool,
409) {
410 let ws = state
412 .focus_manager
413 .create_follows_cursor() .then_some(
415 state
417 .workspaces
418 .iter()
419 .find(|ws| ws.xyhw.contains_point(xy.0, xy.1)),
420 )
421 .flatten()
422 .or(state.focus_manager.workspace(&state.workspaces)); let Some(ws) = ws else {
426 tracing::warn!("setup_window failed to identify workspace. Falling back to tag 1");
427 window.tag = Some(1);
428 if is_scratchpad(state, window) {
429 if let Some(scratchpad_tag) = state.tags.get_hidden_by_label("NSP") {
430 window.tag(&scratchpad_tag.id);
431 window.set_floating(true);
432 }
433 }
434 return;
435 };
436
437 let for_active_workspace = |x: &Window<H>| -> bool { ws.tag == x.tag && x.is_managed() };
439 *is_first = !state.windows.iter().any(for_active_workspace);
440 if window.tag.is_none() {
442 window.tag =
443 find_terminal(state, window.pid).map_or_else(|| ws.tag, |terminal| terminal.tag);
444 }
445 *on_same_tag = ws.tag == window.tag;
446 layout.clone_from(&state.layout_manager.layout(ws.id, window.tag.unwrap()).name);
447
448 if let Some((scratchpad_name, _)) = state
450 .active_scratchpads
451 .iter()
452 .find(|(_, id)| id.iter().any(|id| Some(*id) == window.pid))
453 {
454 window.set_floating(true);
455 if let Some(s) = state
456 .scratchpads
457 .iter()
458 .find(|s| *scratchpad_name == s.name)
459 {
460 let new_float_exact = s.xyhw(&ws.xyhw);
461 window.normal = ws.xyhw;
462 window.set_floating_exact(new_float_exact);
463 return;
464 }
465 }
466
467 if let Some(parent) = find_transient_parent(&state.windows, window.transient) {
469 if window.r#type != WindowType::Utility {
472 set_relative_floating(window, ws, parent.exact_xyhw());
473 return;
474 }
475 }
476
477 match window.r#type {
479 WindowType::Normal => {
480 window.apply_margin_multiplier(ws.margin_multiplier);
481 if window.floating() {
482 set_relative_floating(window, ws, ws.xyhw_avoided);
483 }
484 }
485 WindowType::Dialog | WindowType::Splash => {
486 set_relative_floating(window, ws, ws.xyhw_avoided);
487 }
488 _ => {}
489 }
490}
491
492fn update_workspace_avoid_list<H: Handle>(state: &mut State<H>) {
493 let mut avoid = vec![];
494 state
495 .windows
496 .iter()
497 .filter(|w| w.r#type == WindowType::Dock)
498 .filter_map(|w| w.strut.map(|strut| (w.handle, strut)))
499 .for_each(|(handle, to_avoid)| {
500 tracing::trace!("AVOID STRUT:[{:?}] {:?}", handle, to_avoid);
501 avoid.push(to_avoid);
502 });
503 for ws in &mut state.workspaces {
504 let struts = avoid
505 .clone()
506 .into_iter()
507 .filter(|s| {
508 let (x, y) = s.center();
509 ws.contains_point(x, y)
510 })
511 .collect();
512 ws.avoid = struts;
513 ws.update_avoided_areas();
514 }
515}
516
517#[cfg(test)]
518mod tests {
519 use super::*;
520 use crate::layouts::MONOCLE;
521 use crate::models::{MockHandle, Screen};
522 use crate::Manager;
523
524 #[test]
525 fn insert_behavior_bottom_add_window_at_the_end_of_the_stack() {
526 let mut manager = Manager::new_test(vec![]);
527 manager.state.insert_behavior = InsertBehavior::Bottom;
528
529 manager.screen_create_handler(Screen::default());
530 manager.window_created_handler(
531 Window::new(WindowHandle::<MockHandle>(1), None, None),
532 -1,
533 -1,
534 );
535 manager.window_created_handler(
536 Window::new(WindowHandle::<MockHandle>(2), None, None),
537 -1,
538 -1,
539 );
540
541 let expected = vec![WindowHandle::<MockHandle>(1), WindowHandle::<MockHandle>(2)];
542
543 let actual: Vec<WindowHandle<MockHandle>> =
544 manager.state.windows.iter().map(|w| w.handle).collect();
545
546 assert_eq!(actual, expected);
547 }
548
549 #[test]
550 fn insert_behavior_top_add_window_at_the_top_of_the_stack() {
551 let mut manager = Manager::new_test(vec![]);
552 manager.state.insert_behavior = InsertBehavior::Top;
553
554 manager.screen_create_handler(Screen::default());
555 manager.window_created_handler(
556 Window::new(WindowHandle::<MockHandle>(1), None, None),
557 -1,
558 -1,
559 );
560 manager.window_created_handler(
561 Window::new(WindowHandle::<MockHandle>(2), None, None),
562 -1,
563 -1,
564 );
565
566 let expected = vec![WindowHandle::<MockHandle>(2), WindowHandle::<MockHandle>(1)];
567 let actual: Vec<WindowHandle<MockHandle>> =
568 manager.state.windows.iter().map(|w| w.handle).collect();
569
570 assert_eq!(actual, expected);
571 }
572
573 #[test]
574 fn insert_behavior_after_current_add_window_after_the_current_window() {
575 let mut manager = Manager::new_test(vec![]);
576 manager.state.insert_behavior = InsertBehavior::AfterCurrent;
577
578 manager.screen_create_handler(Screen::default());
579 manager.window_created_handler(
580 Window::new(WindowHandle::<MockHandle>(1), None, None),
581 -1,
582 -1,
583 );
584 manager.window_created_handler(
585 Window::new(WindowHandle::<MockHandle>(2), None, None),
586 -1,
587 -1,
588 );
589 manager.window_created_handler(
590 Window::new(WindowHandle::<MockHandle>(3), None, None),
591 -1,
592 -1,
593 );
594
595 let expected = vec![
596 WindowHandle::<MockHandle>(1),
597 WindowHandle::<MockHandle>(3),
598 WindowHandle::<MockHandle>(2),
599 ];
600 let actual: Vec<WindowHandle<MockHandle>> =
601 manager.state.windows.iter().map(|w| w.handle).collect();
602
603 assert_eq!(actual, expected);
604 }
605
606 #[test]
607 fn insert_behavior_before_current_add_window_before_the_current_window() {
608 let mut manager = Manager::new_test(vec![]);
609 manager.state.insert_behavior = InsertBehavior::BeforeCurrent;
610
611 manager.screen_create_handler(Screen::default());
612 manager.window_created_handler(
613 Window::new(WindowHandle::<MockHandle>(1), None, None),
614 -1,
615 -1,
616 );
617 manager.window_created_handler(
618 Window::new(WindowHandle::<MockHandle>(2), None, None),
619 -1,
620 -1,
621 );
622
623 manager.window_created_handler(
624 Window::new(WindowHandle::<MockHandle>(3), None, None),
625 -1,
626 -1,
627 );
628
629 let expected = vec![
630 WindowHandle::<MockHandle>(2),
631 WindowHandle::<MockHandle>(3),
632 WindowHandle::<MockHandle>(1),
633 ];
634 let actual: Vec<WindowHandle<MockHandle>> =
635 manager.state.windows.iter().map(|w| w.handle).collect();
636
637 assert_eq!(actual, expected);
638 }
639
640 #[test]
641 fn single_window_has_no_border() {
642 let mut manager = Manager::new_test_with_border(vec![], 1);
643 manager.screen_create_handler(Screen::default());
644
645 manager.window_created_handler(
646 Window::new(WindowHandle::<MockHandle>(1), None, None),
647 -1,
648 -1,
649 );
650
651 assert_eq!((manager.state.windows[0]).border(), 0);
652 }
653
654 #[test]
655 fn multiple_windows_have_borders() {
656 let mut manager = Manager::new_test_with_border(vec![], 1);
657 manager.screen_create_handler(Screen::default());
658
659 manager.window_created_handler(
660 Window::new(WindowHandle::<MockHandle>(1), None, None),
661 -1,
662 -1,
663 );
664 manager.window_created_handler(
665 Window::new(WindowHandle::<MockHandle>(2), None, None),
666 -1,
667 -1,
668 );
669
670 assert_eq!((manager.state.windows[0]).border(), 1);
671 assert_eq!((manager.state.windows[1]).border(), 1);
672 }
673
674 #[test]
675 fn remaining_single_window_has_no_border() {
676 let mut manager = Manager::new_test_with_border(vec![], 1);
677 manager.screen_create_handler(Screen::default());
678
679 manager.window_created_handler(
680 Window::new(WindowHandle::<MockHandle>(1), None, None),
681 -1,
682 -1,
683 );
684 manager.window_created_handler(
685 Window::new(WindowHandle::<MockHandle>(2), None, None),
686 -1,
687 -1,
688 );
689
690 manager.window_destroyed_handler(&manager.state.windows[1].handle.clone());
691
692 assert_eq!((manager.state.windows[0]).border(), 0);
693 }
694
695 #[test]
696 fn single_windows_on_different_tags_have_no_border() {
697 let mut manager = Manager::new_test_with_border(vec!["1".to_string(), "2".to_string()], 1);
698 manager.screen_create_handler(Screen::default());
699
700 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
701 first_window.tag(&1);
702 manager.window_created_handler(first_window, -1, -1);
703
704 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
705 second_window.tag(&2);
706 manager.window_created_handler(second_window, -1, -1);
707
708 assert_eq!((manager.state.windows[0]).border(), 0);
709 assert_eq!((manager.state.windows[1]).border(), 0);
710 }
711
712 #[test]
713 fn single_window_has_no_border_and_windows_on_another_tag_have_borders() {
714 let mut manager = Manager::new_test_with_border(vec!["1".to_string(), "2".to_string()], 1);
715 manager.screen_create_handler(Screen::default());
716
717 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
718 first_window.tag(&1);
719 manager.window_created_handler(first_window, -1, -1);
720
721 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
722 second_window.tag(&2);
723 manager.window_created_handler(second_window, -1, -1);
724
725 let mut third_window = Window::new(WindowHandle::<MockHandle>(3), None, None);
726 third_window.tag(&2);
727 manager.window_created_handler(third_window, -1, -1);
728
729 assert_eq!((manager.state.windows[0]).border(), 0);
730 assert_eq!((manager.state.windows[1]).border(), 1);
731 assert_eq!((manager.state.windows[2]).border(), 1);
732 }
733
734 #[test]
735 fn remaining_single_window_on_another_tag_has_no_border() {
736 let mut manager = Manager::new_test_with_border(vec!["1".to_string(), "2".to_string()], 1);
737 manager.screen_create_handler(Screen::default());
738
739 let mut first_window = Window::new(WindowHandle::<MockHandle>(1), None, None);
740 first_window.tag(&1);
741 manager.window_created_handler(first_window, -1, -1);
742
743 let mut second_window = Window::new(WindowHandle::<MockHandle>(2), None, None);
744 second_window.tag(&2);
745 manager.window_created_handler(second_window, -1, -1);
746
747 let mut third_window = Window::new(WindowHandle::<MockHandle>(3), None, None);
748 third_window.tag(&2);
749 manager.window_created_handler(third_window, -1, -1);
750
751 manager.window_destroyed_handler(&manager.state.windows[2].handle.clone());
752
753 assert_eq!((manager.state.windows[0]).border(), 0);
754 assert_eq!((manager.state.windows[1]).border(), 0);
755 }
756
757 #[test]
758 fn monocle_layout_only_has_single_windows() {
759 let mut manager = Manager::new_test_with_border(vec!["1".to_string()], 1);
760 manager.screen_create_handler(Screen::default());
761 manager.state.layout_manager.set_layout(1, 1, MONOCLE);
762 manager.window_created_handler(
764 Window::new(WindowHandle::<MockHandle>(1), None, None),
765 -1,
766 -1,
767 );
768 manager.window_created_handler(
769 Window::new(WindowHandle::<MockHandle>(2), None, None),
770 -1,
771 -1,
772 );
773 assert_eq!((manager.state.windows[0]).border(), 0);
774 assert_eq!((manager.state.windows[1]).border(), 0);
775 }
776}