1use crate::config;
19use crate::shared::ipc;
20use crate::shared::ipc::NodeMethods;
21use std::collections::HashMap;
22use swayipc as s;
23
24pub fn auto_tile(res_to_min_width: &HashMap<i32, i32>) {
25 if let Ok(mut con) = s::Connection::new() {
26 if let Ok(tree) = con.get_tree() {
27 for output in &tree.nodes {
28 log::debug!("output: {:?}", output.name);
29
30 if output.node_type != s::NodeType::Output {
33 panic!(
34 "Child of Root is no Output but a {:?}",
35 output.node_type
36 );
37 }
38
39 let output_width = output.rect.width;
40 let min_window_width = &res_to_min_width.get(&output_width);
41
42 if let Some(min_window_width) = min_window_width {
43 for container in output.iter().filter(|n| {
44 let t = n.get_type();
45 t == ipc::Type::Workspace || t == ipc::Type::Container
46 }) {
47 if container.is_scratchpad() {
48 log::debug!(" Skipping scratchpad");
49 continue;
50 }
51 log::debug!(
52 " container: {:?}, layout {:?}, {} nodes",
53 container.node_type,
54 container.layout,
55 container.nodes.len(),
56 );
57 for child_win in container
58 .nodes
59 .iter()
60 .filter(|n| n.get_type() == ipc::Type::Window)
61 {
62 let estimated_width =
64 child_win.rect.width as f32 / 2.0;
65 log::debug!(
66 " child_win: {:?}, estimated width after splith {} px",
67 child_win.app_id, estimated_width
68 );
69 let split = if container.layout
70 == s::NodeLayout::SplitH
71 && estimated_width <= **min_window_width as f32
72 {
73 Some("splitv")
74 } else if container.layout == s::NodeLayout::SplitV
75 && estimated_width > **min_window_width as f32
76 {
77 Some("splith")
78 } else {
79 None
80 };
81
82 if let Some(split) = split {
83 log::debug!(
84 "Auto-tiling performing {} on window {} \
85 because estimated width after another \
86 split is {} and the minimum window width \
87 is {} on this output.",
88 split,
89 child_win.id,
90 estimated_width,
91 min_window_width
92 );
93 match con.run_command(format!(
94 "[con_id={}] {}",
95 child_win.id, split
96 )) {
97 Ok(_) => (),
98 Err(e) => log::error!(
99 "Couldn't set {} on con {}: {:?}",
100 split,
101 child_win.id,
102 e
103 ),
104 }
105 }
106 }
107 }
108 } else {
109 log::error!("No layout.auto_tile_min_window_width_per_output_width \
110 setting for output_width {}", output_width);
111 }
112 }
113 } else {
114 log::error!("Couldn't call get_tree during auto_tile.");
115 }
116 } else {
117 log::error!("Couldn't get connection for auto_tile");
118 }
119}
120
121pub fn maybe_auto_tile() {
122 config::with_config(|cfg| {
123 if cfg.is_layout_auto_tile() {
124 log::debug!("auto_tile: start");
125 auto_tile(
126 &cfg.get_layout_auto_tile_min_window_width_per_output_width_as_map(),
127 );
128 log::debug!("auto_tile: end");
129 }
130 })
131}
132
133const SWAYR_TMP_WORKSPACE: &str = "✨";
134
135pub fn relayout_current_workspace<F>(
136 include_floating: bool,
137 insert_win_fn: F,
138) -> Result<String, String>
139where
140 F: Fn(&mut [&s::Node], &mut s::Connection) -> s::Fallible<()>,
141{
142 let root = ipc::get_root_node(false);
143 let workspaces: Vec<&s::Node> = root
144 .iter()
145 .filter(|n| n.get_type() == ipc::Type::Workspace)
146 .collect();
147 match workspaces.iter().find(|ws| ws.is_current()) {
148 Some(cur_ws) => match s::Connection::new() {
149 Ok(mut con) => {
150 let mut moved_wins: Vec<&s::Node> = vec![];
151 let mut focused_win = None;
152 for win in
153 cur_ws.iter().filter(|n| n.get_type() == ipc::Type::Window)
154 {
155 if win.focused {
156 focused_win = Some(win);
157 }
158 if !include_floating && win.is_floating() {
159 continue;
160 }
161 moved_wins.push(win);
162 con.run_command(format!(
163 "[con_id={}] move to workspace {}",
164 win.id, SWAYR_TMP_WORKSPACE
165 ))
166 .map_err(|err| err.to_string())?;
167 }
168
169 insert_win_fn(moved_wins.as_mut_slice(), &mut con)
170 .map_err(|err| err.to_string())?;
171 std::thread::sleep(std::time::Duration::from_millis(25));
172
173 if let Some(win) = focused_win {
174 con.run_command(format!("[con_id={}] focus", win.id))
175 .map_err(|err| err.to_string())?;
176 }
177 Ok(format!(
178 "Re-layouted current workspace {}.",
179 cur_ws.get_name()
180 ))
181 }
182 Err(err) => Err(err.to_string()),
183 },
184 None => Err("No workspace is focused.".to_string()),
185 }
186}