#!/usr/bin/env rust-script //! //! ```cargo //! [dependencies] //! serde = { version = "1.0", features = ["derive"] } //! serde_json = "1.0" //! anyhow = "1.0" //! handlebars = "3" //! ``` use serde::Deserialize; use anyhow::Result; use std::process::Command; use std::{fs, io}; use handlebars::Handlebars; use std::collections::HashMap; #[derive(Deserialize, Debug)] struct Monitor { id: u32, name: String, width: u32, height: u32, } impl Monitor { fn list() -> Result> { let output = Command::new("hyprctl") .arg("monitors") .arg("-j") .output()?; assert!(output.status.success()); let monitors_json = std::str::from_utf8(&output.stdout)?; Ok(serde_json::from_str(monitors_json)?) } fn launch_bar(&self, wl_display: &str, home: &str) -> Result<()> { let bar_name = format!("bar-{wl_display}-{}", self.id); let dir = format!("/tmp/vbar/{wl_display}/{}", self.id); match fs::create_dir_all(&dir) { Ok(()) => {}, Err(e) if e.kind() == io::ErrorKind::AlreadyExists => { let output = Command::new("rm") .arg("-rf") .arg(&dir) .output()?; assert!(output.status.success()); fs::create_dir_all(&dir)?; }, Err(e) => return Err(e.into()), } self.generate_bar_config( &bar_name, &home, &dir, &format!("{home}/.config/eww/templates/bar"), &dir, )?; let output = Command::new("eww") .arg("-c") .arg(&dir) .arg("reload") .output()?; assert!(output.status.success()); let output = Command::new("eww") .arg("-c") .arg(&dir) .arg("open") .arg(bar_name) .output()?; assert!(output.status.success()); Ok(()) } fn generate_bar_config(&self, bar_name: &str, home: &str, base_dir: &str, source_dir: &str, dest_dir: &str) -> Result<()> { for entry in fs::read_dir(source_dir)? { let entry = entry?; let path = entry.path(); let relpath = path.strip_prefix(&source_dir)? .to_str().unwrap(); if path.is_dir() { fs::create_dir_all(&format!("{dest_dir}/{relpath}"))?; self.generate_bar_config( &bar_name, &home, &base_dir, &path.to_str().unwrap(), &format!("{dest_dir}/{relpath}"), )?; } else if path.is_file() { let data = fs::read_to_string(&path)?; let output = self.template(&data, &bar_name, &home, &base_dir)?; fs::write(format!("{dest_dir}/{relpath}"), output)?; } } Ok(()) } fn template(&self, source: &str, bar_name: &str, home: &str, base_dir: &str) -> Result { let height: u32 = (self.height as f32 * 0.02) as u32; let id_str = self.id.to_string(); let height_str = height.to_string(); let values: HashMap<&str, &str> = HashMap::from([ ("monitor", id_str.as_str()), ("monitor_name", self.name.as_str()), ("bar_name", bar_name), ("home", home), ("base_dir", base_dir), ("height", height_str.as_str()), ]); let mut handlebars = Handlebars::new(); handlebars.register_template_string("tpl", source)?; Ok(handlebars.render("tpl", &values)?) } } fn main() -> Result<()> { let wl_display = std::env::var_os("WAYLAND_DISPLAY").unwrap() .into_string().unwrap(); let home = std::env::var_os("HOME").unwrap() .into_string().unwrap(); let monitors = Monitor::list()?; for monitor in monitors { monitor.launch_bar(&wl_display, &home)?; } Ok(()) }