hyprshell_exec_lib/
collect.rs

1use crate::to_client_id;
2use anyhow::Context;
3use core_lib::{
4    ClientData, ClientId, FindByFirst, MonitorData, MonitorId, WorkspaceData, WorkspaceId,
5};
6use hyprland::data::{Client, Clients, Monitor, Monitors, Workspace, Workspaces};
7use hyprland::prelude::*;
8use tracing::{debug_span, warn};
9
10fn get_hypr_data() -> anyhow::Result<(Vec<Monitor>, Vec<Workspace>, Vec<Client>)> {
11    let _span = debug_span!("get_hypr_data").entered();
12    let monitors = Monitors::get().context("monitors failed")?.to_vec();
13    // sort and filter all workspaces sorted by ID
14    let workspaces = {
15        let mut workspaces = Workspaces::get()
16            .context("workspaces failed")?
17            .into_iter()
18            .filter(|w| w.id != -1) // TODO: check if still needed: ignore clients on invalid workspaces
19            .collect::<Vec<_>>();
20
21        workspaces.sort_by(|a, b| a.id.cmp(&b.id));
22        workspaces
23    };
24    let clients = Clients::get()
25        .context("clients failed")?
26        .into_iter()
27        .filter(|c| c.workspace.id != -1) // TODO: check if still needed: ignore clients on invalid workspaces
28        .collect::<Vec<_>>();
29
30    Ok((monitors, workspaces, clients))
31}
32
33#[allow(clippy::type_complexity)]
34pub fn collect_hypr_data() -> anyhow::Result<(
35    Vec<(ClientId, ClientData)>,
36    Vec<(WorkspaceId, WorkspaceData)>,
37    Vec<(MonitorId, MonitorData)>,
38    Option<(String, ClientId)>,
39    WorkspaceId,
40    MonitorId,
41)> {
42    let _span = debug_span!("convert_hypr_data").entered();
43
44    let (monitors, workspaces, clients) =
45        get_hypr_data().context("loading hyprland data failed")?;
46
47    // all monitors with their data, x and y are the offset of the monitor, width and height are the size of the monitor.
48    // 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
49    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                    x: monitor.x,
58                    y: monitor.y,
59                    width: (f32::from(monitor.width) / monitor.scale) as u16,
60                    height: (f32::from(monitor.height) / monitor.scale) as u16,
61                    connector: monitor.name.clone(),
62                },
63            ));
64        }
65        md
66    };
67
68    // all workspaces with their data, x and y are the offset of the workspace
69    let mut workspace_data = {
70        let mut wd: Vec<(WorkspaceId, WorkspaceData)> = Vec::with_capacity(workspaces.len());
71
72        for (monitor_id, monitor_data) in &monitor_data {
73            workspaces
74                .iter()
75                .filter(|ws| ws.monitor_id == Some(*monitor_id))
76                .for_each(|workspace| {
77                    wd.push((
78                        workspace.id,
79                        WorkspaceData {
80                            name: workspace.name.clone(),
81                            monitor: *monitor_id,
82                            height: monitor_data.height,
83                            width: monitor_data.width,
84                            any_client_enabled: true, // gets updated later
85                        },
86                    ));
87                });
88        }
89        wd
90    };
91
92    let client_data = {
93        let mut cd: Vec<(ClientId, ClientData)> = Vec::with_capacity(clients.len());
94
95        for client in clients {
96            let Some(monitor) = client.monitor else {
97                continue;
98            };
99            if workspace_data.find_by_first(&client.workspace.id).is_some() {
100                cd.push((
101                    to_client_id(&client.address),
102                    ClientData {
103                        x: client.at.0,
104                        y: client.at.1,
105                        width: client.size.0,
106                        height: client.size.1,
107                        class: client.class.clone(),
108                        workspace: client.workspace.id,
109                        monitor,
110                        focus_history_id: client.focus_history_id,
111                        title: client.title.clone(),
112                        floating: client.floating,
113                        pid: client.pid,
114                        enabled: true, // gets updated later
115                    },
116                ));
117            } else {
118                warn!(
119                    "workspace {:?} not found for client {client:?}",
120                    client.workspace
121                );
122            }
123        }
124        cd
125    };
126
127    workspace_data.sort_by(|a, b| a.0.cmp(&b.0));
128    monitor_data.sort_by(|a, b| a.0.cmp(&b.0));
129
130    // is broken, reports the "normal" workspace as active when a client in special workspace is selected
131    // let active_ws = Workspace::get_active()?.id;
132    let active_ws = Workspace::get_active()
133        .map(|w| w.id)
134        .context("active workspace failed")?;
135    let active_ws = Client::get_active()
136        .context("active client failed")?
137        .map_or(active_ws, |a| a.workspace.id);
138    let active_monitor = Monitor::get_active().context("active monitor failed")?.id;
139    let active_client = Client::get_active()
140        .context("active client failed")?
141        .map(|a| (a.class.clone(), to_client_id(&a.address)));
142
143    Ok((
144        client_data,
145        workspace_data,
146        monitor_data,
147        active_client,
148        active_ws,
149        active_monitor,
150    ))
151}