diff --git a/src/main.rs b/src/main.rs index 7793f0f..8c9a944 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,30 +5,42 @@ use std::env; use std::process::Command; use std::{fs, str}; -// parse the config to this +/// Configuration for swayout. #[derive(Serialize, Deserialize, Debug)] -struct Rules { +struct Config { + /// Monitor name is independent of output name. It can be anything. monitors: HashMap, - // Map of layout name to : map of monitor name to output config + /// Definition of layouts: a map of the layout name to the outputs. + /// The outputs is a map of the monitor name (in `monitors`) to the configuration + /// of that monitor for this layout. + /// Available outputs that do not match a monitor in the map are disabled. layouts: HashMap>, } +/// Defines a monitor by make, model, and serial. #[derive(Serialize, Deserialize, Debug)] struct Monitor { make: String, model: String, serial: String, } +/// Configuration for an enabled output. #[derive(Serialize, Deserialize, Debug)] struct OutputConfig { + /// mode. See man 5 sway-output mode: String, + /// scale. See man 5 sway-output scale: String, + /// x value for position. See man 5 sway-output x: u16, + /// y value for position. See man 5 sway-output y: u16, + /// transform. See man 5 sway-output transform: String, } -// parse output of swaymsg -t get_outputs to this +/// An output, as returned by `swaymsg -t get_outputs`. struct Output { + /// output name, according to sway name: String, make: String, model: String, @@ -48,9 +60,8 @@ fn main() { } } -/// Get all outputs currently available +/// Get all outputs currently available, according to `swaymsg -t get_outputs`. fn get_outputs() -> Vec { - // call `swaymsg -t get_outputs` to get outputs let output = Command::new("swaymsg") .arg("-t") .arg("get_outputs") @@ -82,7 +93,7 @@ fn get_outputs() -> Vec { } /// Parse the mode from the output JSON into a string suitable for the mode param for sway-output. -/// This will go into OutputConfig.mode +/// 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() { @@ -101,15 +112,16 @@ fn get_mode(output: &Value) -> String { } } -/// Get the rules from ~/.config/swayout/config.toml -fn get_rules() -> Rules { +/// Get a `Config` from ~/.config/swayout/config.toml. +/// (It really uses XDG config dirs.) +fn get_config() -> Config { let xdg_dirs = xdg::BaseDirectories::with_prefix("swayout").unwrap(); if let Some(path) = xdg_dirs.find_config_file("config.toml") { let toml = fs::read_to_string(path).expect("error reading file"); toml::from_str(&toml).expect("toml parse error") } else { // no config files, use an empty rules - Rules { + Config { monitors: HashMap::new(), layouts: HashMap::new(), } @@ -118,17 +130,16 @@ fn get_rules() -> Rules { /// Determine the available layout names. /// Return one layout for each layout defined in the configuration file -/// (for which all outputs are available), -/// one for each monitor defined in the configuration file -/// (for using just that monitor), +/// for which all outputs are available, +/// one for each monitor defined in the configuration file (for using just that monitor), /// and one for each available output that is not a configured monitor (for using just that output). fn get_layouts() -> Vec { let available_outputs = get_outputs(); - let rules = get_rules(); + let rules = get_config(); let mut layout_names: Vec = Vec::new(); - // Get the names of monitors that are available (that match a current output) + // Get the names of monitors that are available (that match an available output) let available_monitor_names:Vec<&String> = rules.monitors.iter() .filter(|(_monitor_name,monitor)| available_outputs.iter().any(|output| @@ -175,9 +186,10 @@ fn print_layout_names() { } /// Apply the specified layout. -/// layout_name may be the name of a layout rule, the name of a monitor, or the name of an output. +/// layout_name may be the name of a layout in the config, the name of a monitor, +/// or the name of an output. fn apply_layout(layout_name: &String) { - let rules = get_rules(); + let rules = get_config(); let outputs = get_outputs(); // Map of output name to config for all outputs to enable. @@ -192,7 +204,6 @@ fn apply_layout(layout_name: &String) { let monitor = &rules.monitors[monitor_name]; // find the output for the monitor to get the output name. - // (You can allegedly use " " instead of output name.) let output_opt = outputs.iter().find(|output| { output.make == monitor.make && output.model == monitor.model @@ -210,8 +221,7 @@ fn apply_layout(layout_name: &String) { if let Some(monitor) = rules.monitors.get(layout_name) { // It is a monitor name. Find the matching output... if let Some(output) = outputs.iter().find(|output| { - output.make == monitor.make - && output.model == monitor.model + output.make == monitor.make && output.model == monitor.model && output.serial == monitor.serial }) { output_config_map.insert(&output.name, &output.output_config); @@ -234,12 +244,13 @@ fn apply_layout(layout_name: &String) { /// Apply the specified outputs. Enable all outputs in [outputs], disable others. fn apply_outputs(all_outputs: &Vec, outputs: &HashMap<&String, &OutputConfig>) { - let mut cmd = Command::new("swaymsg"); - // enabled first, then disabled. - // That way if some work before an error, you at least have one output enabled. + // 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| { @@ -250,6 +261,7 @@ fn apply_outputs(all_outputs: &Vec, outputs: &HashMap<&String, &OutputCo } }); + let mut cmd = Command::new("swaymsg"); enabled.iter().for_each(|(output_name,output_config)| { cmd.arg("output"); cmd.arg(&output_name); @@ -273,6 +285,7 @@ fn apply_outputs(all_outputs: &Vec, outputs: &HashMap<&String, &OutputCo cmd.arg(","); }); + // print what we are about to do. cmd.get_args() .for_each(|arg| print!("{} ", arg.to_str().unwrap()));