use crate::handle::get_recent_clients_map;
use crate::handle::sort::{sort_clients, update_clients};
use crate::{Active, ClientData, HyprlandData, MonitorData, SwitchType, WorkspaceData};
use crate::{FindByFirst, SimpleConfig};
use hyprland::data::{Client, Clients, Monitors, Workspaces};
use hyprland::prelude::{HyprData, HyprDataActiveOptional};
use hyprland::shared::{Address, MonitorId, WorkspaceId};
use tracing::{span, trace, warn, Level};
pub fn collect_data(config: SimpleConfig) -> anyhow::Result<(HyprlandData, Option<Active>)> {
let _span = span!(Level::TRACE, "collect_data").entered();
let clients = Clients::get()?
.into_iter()
.filter(|c| c.workspace.id != -1) .filter(|w| config.include_special_workspaces || !w.workspace.id < 0)
.collect::<Vec<_>>();
let monitors = Monitors::get()?;
let workspaces = {
let mut workspaces = Workspaces::get()?
.into_iter()
.filter(|w| w.id != -1) .filter(|w| config.include_special_workspaces || !w.id < 0)
.collect::<Vec<_>>();
workspaces.sort_by(|a, b| a.id.cmp(&b.id));
workspaces
};
let mut monitor_data = {
let mut md: Vec<(MonitorId, MonitorData)> = Vec::with_capacity(monitors.iter().len());
monitors.iter().for_each(|monitor| {
md.push((
monitor.id,
MonitorData {
x: monitor.x,
y: monitor.y,
width: (monitor.width as f32 / monitor.scale) as u16,
height: (monitor.height as f32 / monitor.scale) as u16,
connector: monitor.name.clone(),
enabled: false, },
));
});
md
};
let mut workspace_data = {
let mut wd: Vec<(WorkspaceId, WorkspaceData)> = Vec::with_capacity(workspaces.len());
for (monitor_id, monitor_data) in monitor_data.iter() {
let mut x_offset: i32 = 0;
workspaces
.iter()
.filter(|ws| ws.monitor_id == *monitor_id)
.for_each(|workspace| {
wd.push((
workspace.id,
WorkspaceData {
x: x_offset,
y: monitor_data.y,
name: workspace.name.clone(),
monitor: *monitor_id,
height: monitor_data.height,
width: monitor_data.width,
enabled: false, },
));
x_offset += monitor_data.width as i32;
});
}
wd
};
let mut client_data = {
let mut cd: Vec<(Address, ClientData)> = Vec::with_capacity(clients.len());
for client in clients {
if workspace_data.find_by_first(&client.workspace.id).is_some() {
cd.push((
client.address.clone(),
ClientData {
x: client.at.0,
y: client.at.1,
width: client.size.0,
height: client.size.1,
class: client.class.clone(),
workspace: client.workspace.id,
monitor: client.monitor,
focus_history_id: client.focus_history_id,
title: client.title.clone(),
floating: client.floating,
pid: client.pid,
enabled: false, },
));
} else {
warn!(
"workspace {:?} not found for client {:?}",
client.workspace, client
);
}
}
cd
};
if config.ignore_monitors {
client_data = update_clients(client_data, Some(&workspace_data), None);
} else {
client_data = update_clients(client_data, Some(&workspace_data), Some(&monitor_data));
}
if config.sort_recent {
let mut focus_map = get_recent_clients_map()
.lock()
.expect("Failed to lock focus_map");
if focus_map.is_empty() {
focus_map.extend(
client_data
.iter()
.map(|(address, client_data)| (address.clone(), client_data.focus_history_id)),
);
};
client_data.sort_by(|(a_addr, a), (b_addr, b)| {
let a_focus_id = focus_map.get(a_addr);
let b_focus_id = focus_map.get(b_addr);
if a_focus_id.is_none() && b_focus_id.is_none() {
a.focus_history_id.cmp(&b.focus_history_id) } else if a_focus_id.is_none() {
std::cmp::Ordering::Greater
} else if b_focus_id.is_none() {
std::cmp::Ordering::Less
} else {
#[allow(clippy::unnecessary_unwrap)]
a_focus_id.unwrap().cmp(b_focus_id.unwrap())
}
});
} else {
client_data = sort_clients(
client_data,
config.ignore_workspaces,
config.ignore_monitors,
);
}
if config.ignore_monitors {
client_data = update_clients(client_data, None, Some(&monitor_data));
}
workspace_data.sort_by(|a, b| a.0.cmp(&b.0));
monitor_data.sort_by(|a, b| a.0.cmp(&b.0));
let active = Client::get_active()?;
let active: Option<(String, WorkspaceId, MonitorId, Address)> = active.as_ref().map_or_else(
|| None,
|a| {
Some((
a.class.clone(),
a.workspace.id,
a.monitor,
a.address.clone(),
))
},
);
trace!("active: {:?}", active);
for (_, client) in client_data.iter_mut() {
client.enabled = (!config.filter_same_class
|| active
.as_ref()
.map_or(true, |active| client.class == *active.0))
&& (!config.filter_current_workspace
|| active
.as_ref()
.map_or(true, |active| client.workspace == active.1))
&& (!config.filter_current_monitor
|| active
.as_ref()
.map_or(true, |active| client.monitor == active.2));
}
for (wid, workspace) in workspace_data.iter_mut() {
workspace.enabled = client_data
.iter()
.any(|(_, c)| c.enabled && c.workspace == *wid);
}
for (id, monitor) in monitor_data.iter_mut() {
monitor.enabled = client_data
.iter()
.any(|(_, c)| c.enabled && c.monitor == *id);
}
trace!("client_data: {:?}", client_data);
trace!("workspace_data: {:?}", workspace_data);
trace!("monitor_data: {:?}", monitor_data);
let active = match config.switch_type {
SwitchType::Client => active.as_ref().map(|a| a.3.clone()).map(Active::Client),
SwitchType::Workspace => active.as_ref().map(|a| a.1).map(Active::Workspace),
SwitchType::Monitor => active.as_ref().map(|a| a.2).map(Active::Monitor),
};
Ok((
HyprlandData {
clients: client_data,
workspaces: workspace_data,
monitors: monitor_data,
},
active,
))
}