use crate::config::OutputConfig; use serde_json::{from_str, Value}; use std::collections::HashMap; use std::process::Command; use std::str::from_utf8; /// An output, as returned by `swaymsg -t get_outputs`. pub struct Output { /// output name, according to sway pub name: String, pub make: String, pub model: String, pub serial: String, pub output_config: OutputConfig, } /// Get all outputs currently available, according to `swaymsg -t get_outputs`. pub fn get_outputs() -> Vec { let output = Command::new("swaymsg") .arg("-t") .arg("get_outputs") .output() .expect("swaymsg -t get_outputs failed"); let json_string = from_utf8(&output.stdout).unwrap(); let json = from_str(json_string).unwrap(); if let Value::Array(outputs) = json { outputs .iter() .map(|output_json| Output { name: String::from(output_json["name"].as_str().unwrap()), make: String::from(output_json["make"].as_str().unwrap()), model: String::from(output_json["model"].as_str().unwrap()), serial: String::from(output_json["serial"].as_str().unwrap()), output_config: OutputConfig { mode: get_mode(output_json), scale: String::from("1.0"), x: 0, y: 0, transform: String::from("normal"), }, }) .collect() } else { panic!("outputs json was not an array"); } } /// Parse the mode from the output JSON into a string suitable for the mode param for sway-output. /// This will go into `OutputConfig.mode`. fn get_mode(output: &Value) -> String { if let Value::Array(modes) = &output["modes"] { if let Some(mode) = modes.first() { let width = mode["width"].as_i64().unwrap().to_string(); let height = mode["height"].as_i64().unwrap().to_string(); let mut out: String = String::new(); out.push_str(&width); out.push_str("x"); out.push_str(&height); out } else { panic!("no modes") } } else { panic!("modes not an array") } } /// Apply the specified outputs. Enable all outputs in [outputs], disable others. pub fn apply_outputs(all_outputs: &Vec, outputs: &HashMap<&String, &OutputConfig>) { // set enabled outputs first, then set disabled outputs. // That way if some work before an error, you have at least one output enabled. // for outputs to be enabled: map of output name to config let mut enabled: HashMap<&String, &OutputConfig> = HashMap::new(); // for outputs to be disabled: output names let mut disabled: Vec<&String> = Vec::new(); all_outputs.iter().for_each(|output| { if let Some(output_config) = outputs.get(&output.name) { enabled.insert(&output.name, &output_config); } else { disabled.push(&output.name); } }); let mut cmd = Command::new("swaymsg"); enabled.iter().for_each(|(output_name, output_config)| { cmd.arg("output"); cmd.arg(&output_name); cmd.arg("enable"); cmd.arg("mode"); cmd.arg(&output_config.mode); cmd.arg("scale"); cmd.arg(&output_config.scale); cmd.arg("pos"); cmd.arg(&output_config.x.to_string()); cmd.arg(&output_config.y.to_string()); cmd.arg("transform"); cmd.arg(&output_config.transform); cmd.arg(","); }); disabled.iter().for_each(|output_name| { cmd.arg("output"); cmd.arg(&output_name); cmd.arg("disable"); cmd.arg(","); }); // print what we are about to do. cmd.get_args() .for_each(|arg| print!("{} ", arg.to_str().unwrap())); cmd.output().expect("swaymsg output failed"); }