hyprshell_exec_lib/
collect.rs

1use crate::to_client_id;
2use core_lib::{
3    ClientData, ClientId, FindByFirst, MonitorData, MonitorId, WorkspaceData, WorkspaceId,
4};
5use hyprland::data::{Client, Clients, Monitor, Monitors, Workspace, Workspaces};
6use hyprland::prelude::*;
7use tracing::{debug_span, 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()?.to_vec();
12    // sort and filter all workspaces sorted by ID
13    let workspaces = {
14        let mut workspaces = Workspaces::get()?
15            .into_iter()
16            .filter(|w| w.id != -1) // filter invalid workspaces
17            .filter(|w| !w.id < 0) // TODO someday add special_workspace support
18            .collect::<Vec<_>>();
19
20        workspaces.sort_by(|a, b| a.id.cmp(&b.id));
21        workspaces
22    };
23    let clients = Clients::get()?
24        .into_iter()
25        .filter(|c| c.workspace.id != -1) // ignore clients on invalid workspaces
26        .filter(|w| !w.workspace.id < 0) // TODO someday add special_workspace support
27        .collect::<Vec<_>>();
28
29    Ok((monitors, workspaces, clients))
30}
31
32#[allow(clippy::type_complexity)]
33pub fn collect_hypr_data() -> anyhow::Result<(
34    Vec<(ClientId, ClientData)>,
35    Vec<(WorkspaceId, WorkspaceData)>,
36    Vec<(MonitorId, MonitorData)>,
37    Option<(String, ClientId)>,
38    WorkspaceId,
39    MonitorId,
40)> {
41    let _span = debug_span!("convert_hypr_data").entered();
42
43    let (monitors, workspaces, clients) = get_hypr_data()?;
44
45    // all monitors with their data, x and y are the offset of the monitor, width and height are the size of the monitor.
46    // combined_width and combined_height are the combined size of all workspaces on the monitor and workspaces_on_monitor is the number of workspaces on the monitor
47    let mut monitor_data = {
48        let mut md: Vec<(MonitorId, MonitorData)> = Vec::with_capacity(monitors.iter().len());
49
50        for monitor in &monitors {
51            #[allow(clippy::cast_sign_loss)]
52            md.push((
53                monitor.id,
54                MonitorData {
55                    x: monitor.x,
56                    y: monitor.y,
57                    width: (f32::from(monitor.width) / monitor.scale) as u16,
58                    height: (f32::from(monitor.height) / monitor.scale) as u16,
59                    connector: monitor.name.clone(),
60                },
61            ));
62        }
63        md
64    };
65
66    // all workspaces with their data, x and y are the offset of the workspace
67    let mut workspace_data = {
68        let mut wd: Vec<(WorkspaceId, WorkspaceData)> = Vec::with_capacity(workspaces.len());
69
70        for (monitor_id, monitor_data) in &monitor_data {
71            let mut x_offset: i32 = 0;
72            workspaces
73                .iter()
74                .filter(|ws| ws.monitor_id == Some(*monitor_id))
75                .for_each(|workspace| {
76                    wd.push((
77                        workspace.id,
78                        WorkspaceData {
79                            x: x_offset,
80                            y: monitor_data.y,
81                            name: workspace.name.clone(),
82                            monitor: *monitor_id,
83                            height: monitor_data.height,
84                            width: monitor_data.width,
85                            any_client_enabled: false, // gets updated later
86                        },
87                    ));
88                    x_offset += i32::from(monitor_data.width);
89                });
90        }
91        wd
92    };
93
94    let 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: false, // gets updated later
117                    },
118                ));
119            } else {
120                warn!(
121                    "workspace {:?} not found for client {client:?}",
122                    client.workspace
123                );
124            }
125        }
126        cd
127    };
128
129    workspace_data.sort_by(|a, b| a.0.cmp(&b.0));
130    monitor_data.sort_by(|a, b| a.0.cmp(&b.0));
131
132    let active_ws = Workspace::get_active()?.id;
133    let active_monitor = Monitor::get_active()?.id;
134    let active_client = Client::get_active()?.map(|a| (a.class.clone(), to_client_id(&a.address)));
135
136    Ok((
137        client_data,
138        workspace_data,
139        monitor_data,
140        active_client,
141        active_ws,
142        active_monitor,
143    ))
144}