use core::panic; use std::{collections::HashMap, io::Write}; use niri_ipc::{ socket::Socket, state::{EventStreamState, EventStreamStatePart}, Request, Response, Workspace, }; use serde::Serialize; // gruvbox colors const COLORS: [[&str; 2]; 7] = [ ["#458588", "#83a598"], // blue ["#b16286", "#d3869b"], // purple ["#689d6a", "#8ec07c"], // aqua ["#97971a", "#b8bb26"], // green ["#d79921", "#fabd2f"], // yellow ["#d65d0e", "#fe8019"], // orange ["#cc241d", "#fb4934"], // red ]; fn main() -> Result<(), std::io::Error> { let mut state = EventStreamState::default(); let sock = Socket::connect()?; let mut func = sock.send(Request::EventStream).and_then(|it| match it { (Ok(Response::Handled), func) => Ok(func), _ => unreachable!(), })?; while let Ok(ev) = (func)() { // check only relevant later, only done here to avoid a clone let ev_is_ws_related = matches!( &ev, niri_ipc::Event::WorkspacesChanged { .. } | niri_ipc::Event::WorkspaceActivated { .. } | niri_ipc::Event::WorkspaceActiveWindowChanged { .. } ); // apply event to state state.apply(ev); if ev_is_ws_related { let mut workspaces = state .workspaces .workspaces .values() .map(|it| it.clone()) .collect::>(); workspaces.sort_by(|a, b| a.idx.cmp(&b.idx)); workspaces.sort_by(|a, b| { a.output .clone() .expect("unreachable") .to_lowercase() .cmp(&b.output.clone().expect("unreachable").to_lowercase()) }); let output_colors_lut = workspaces .iter() .map(|it| it.output.clone().expect("unreachable")) .fold(Vec::new(), |mut acc, it| { if !acc.contains(&it) { acc.push(it); } acc }) .into_iter() .enumerate() .map(|(idx, val)| (val, &COLORS[idx])) .collect::>(); let results = workspaces .iter() .map(|ws| WsData { color: output_colors_lut[&ws.output.clone().expect("unreachable")] [if ws.is_active { 1 } else { 0 }], idx: ws.idx, focused: ws.is_focused, active: ws.is_active, }) .collect::>(); println!("{}", serde_json::to_string(&results).unwrap()); } } Ok(()) } #[derive(Debug, Serialize)] struct WsData { color: &'static str, idx: u8, focused: bool, active: bool, }