1use crate::util::to_client_id;
2use anyhow::Context;
3use core_lib::{ByFirst, ClientData, ClientId, MonitorData, MonitorId, WorkspaceData, WorkspaceId};
4use hyprland::data::{Client, Clients, Monitor, Monitors, Workspace, Workspaces};
5use hyprland::prelude::*;
6use regex::Regex;
7use tracing::{debug_span, instrument, trace, warn};
8
9fn get_hypr_data() -> anyhow::Result<(Vec<Monitor>, Vec<Workspace>, Vec<Client>)> {
10 let _span = debug_span!("get_hypr_data").entered();
11 let monitors = Monitors::get().context("monitors failed")?.to_vec();
12 let workspaces = {
14 let mut workspaces = Workspaces::get()
15 .context("workspaces failed")?
16 .into_iter()
17 .filter(|w| w.id != -1) .collect::<Vec<_>>();
19
20 workspaces.sort_by(|a, b| a.id.cmp(&b.id));
21 workspaces
22 };
23 let clients = Clients::get()
24 .context("clients failed")?
25 .into_iter()
26 .filter(|c| c.workspace.id != -1) .collect::<Vec<_>>();
28
29 Ok((monitors, workspaces, clients))
30}
31
32#[allow(clippy::type_complexity)]
33#[instrument(level = "debug")]
34pub fn collect_hypr_data(
35 exclude_workspaces: Option<Regex>,
36) -> anyhow::Result<(
37 Vec<(ClientId, ClientData)>,
38 Vec<(WorkspaceId, WorkspaceData)>,
39 Vec<(MonitorId, MonitorData)>,
40 Option<(String, ClientId)>,
41 WorkspaceId,
42 MonitorId,
43)> {
44 let (monitors, workspaces, clients) =
45 get_hypr_data().context("loading hyprland data failed")?;
46
47 let mut monitor_data = {
50 let mut md: Vec<(MonitorId, MonitorData)> = Vec::with_capacity(monitors.iter().len());
51
52 for monitor in &monitors {
53 #[allow(clippy::cast_sign_loss)]
54 md.push((
55 monitor.id,
56 MonitorData {
57 id: monitor.id,
58 x: monitor.x,
59 y: monitor.y,
60 width: (f32::from(monitor.width) / monitor.scale) as u16,
61 height: (f32::from(monitor.height) / monitor.scale) as u16,
62 connector: monitor.name.clone(),
63 scale: monitor.scale,
64 },
65 ));
66 }
67 md
68 };
69
70 let mut workspace_data = {
72 let mut wd: Vec<(WorkspaceId, WorkspaceData)> = Vec::with_capacity(workspaces.len());
73
74 for (monitor_id, monitor_data) in &monitor_data {
75 workspaces
76 .iter()
77 .filter(|ws| ws.monitor_id == Some(*monitor_id))
78 .for_each(|workspace| {
79 wd.push((
80 workspace.id,
81 WorkspaceData {
82 name: workspace.name.clone(),
83 monitor: *monitor_id,
84 height: monitor_data.height,
85 width: monitor_data.width,
86 any_client_enabled: true, },
88 ));
89 });
90 }
91 wd
92 };
93
94 let mut client_data = {
95 let mut cd: Vec<(ClientId, ClientData)> = Vec::with_capacity(clients.len());
96
97 for client in clients {
98 let Some(monitor) = client.monitor else {
99 continue;
100 };
101 if workspace_data.find_by_first(&client.workspace.id).is_some() {
102 cd.push((
103 to_client_id(&client.address),
104 ClientData {
105 x: client.at.0,
106 y: client.at.1,
107 width: client.size.0,
108 height: client.size.1,
109 class: client.class.clone(),
110 workspace: client.workspace.id,
111 monitor,
112 focus_history_id: client.focus_history_id,
113 title: client.title.clone(),
114 floating: client.floating,
115 pid: client.pid,
116 enabled: true, },
118 ));
119 } else {
120 warn!(
121 "workspace {:?} not found for client {client:?}",
122 client.workspace
123 );
124 }
125 }
126 cd
127 };
128
129 trace!(
132 "workspaces bevore filter by regex: {}",
133 workspace_data.len()
134 );
135 workspace_data = workspace_data
136 .into_iter()
137 .filter(|(_, ws)| {
138 exclude_workspaces
139 .as_ref()
140 .map(|reg| !reg.is_match(&ws.name))
141 .unwrap_or(true)
142 })
143 .collect();
144 trace!("workspaces after filter by regex: {}", workspace_data.len());
145 client_data = client_data
146 .into_iter()
147 .filter(|(_id, cl)| workspace_data.find_by_first(&cl.workspace).is_some())
148 .collect();
149
150 workspace_data.sort_by(|a, b| a.0.cmp(&b.0));
151 monitor_data.sort_by(|a, b| a.0.cmp(&b.0));
152
153 let active_ws = Workspace::get_active()
156 .map(|w| w.id)
157 .context("active workspace failed")?;
158 let active_ws = Client::get_active()
159 .context("active client failed")?
160 .map_or(active_ws, |a| a.workspace.id);
161 let active_monitor = Monitor::get_active().context("active monitor failed")?.id;
162 let active_client = Client::get_active()
163 .context("active client failed")?
164 .map(|a| (a.class.clone(), to_client_id(&a.address)));
165
166 Ok((
167 client_data,
168 workspace_data,
169 monitor_data,
170 active_client,
171 active_ws,
172 active_monitor,
173 ))
174}
175
176pub fn get_monitors() -> Vec<MonitorData> {
177 Monitors::get()
178 .map_or(vec![], HyprDataVec::to_vec)
179 .iter()
180 .map(|m| MonitorData {
181 id: m.id,
182 x: m.x,
183 y: m.y,
184 width: m.width,
185 height: m.height,
186 connector: m.name.clone(),
187 scale: m.scale,
188 })
189 .collect()
190}
191
192#[must_use]
193pub fn get_current_monitor() -> Option<MonitorData> {
194 Monitor::get_active().ok().map(|m| MonitorData {
195 id: m.id,
196 x: m.x,
197 y: m.y,
198 width: m.width,
199 height: m.height,
200 connector: m.name.clone(),
201 scale: m.scale,
202 })
203}
204
205pub fn get_client_classes() -> Vec<String> {
206 Clients::get()
207 .map_or(vec![], HyprDataVec::to_vec)
208 .iter()
209 .map(|client| client.class.clone())
210 .collect()
211}