mirror of
https://github.com/Zedfrigg/ironbar.git
synced 2025-07-01 10:41:03 +02:00
feat: support for multiple bars per monitor, loading config from env var
Restructures `monitor` config key into object of monitor names -> configs.
This commit is contained in:
parent
001dd5473a
commit
2d755d37e5
2 changed files with 63 additions and 34 deletions
|
@ -8,7 +8,9 @@ use crate::modules::tray::TrayModule;
|
||||||
use crate::modules::workspaces::WorkspacesModule;
|
use crate::modules::workspaces::WorkspacesModule;
|
||||||
use dirs::config_dir;
|
use dirs::config_dir;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::fs;
|
use std::collections::HashMap;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::{env, fs};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
#[serde(tag = "type", rename_all = "kebab-case")]
|
#[serde(tag = "type", rename_all = "kebab-case")]
|
||||||
|
@ -23,6 +25,13 @@ pub enum ModuleConfig {
|
||||||
Focused(FocusedModule),
|
Focused(FocusedModule),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum MonitorConfig {
|
||||||
|
Single(Config),
|
||||||
|
Multiple(Vec<Config>),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
|
#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
#[serde(rename_all = "kebab-case")]
|
||||||
pub enum BarPosition {
|
pub enum BarPosition {
|
||||||
|
@ -47,7 +56,7 @@ pub struct Config {
|
||||||
pub center: Option<Vec<ModuleConfig>>,
|
pub center: Option<Vec<ModuleConfig>>,
|
||||||
pub right: Option<Vec<ModuleConfig>>,
|
pub right: Option<Vec<ModuleConfig>>,
|
||||||
|
|
||||||
pub monitors: Option<Vec<Config>>,
|
pub monitors: Option<HashMap<String, MonitorConfig>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn default_bar_position() -> BarPosition {
|
const fn default_bar_position() -> BarPosition {
|
||||||
|
@ -60,35 +69,43 @@ const fn default_bar_height() -> i32 {
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
pub fn load() -> Option<Self> {
|
pub fn load() -> Option<Self> {
|
||||||
let config_dir = config_dir().expect("Failed to locate user config dir");
|
if let Ok(config_path) = env::var("IRONBAR_CONFIG") {
|
||||||
|
let path = PathBuf::from(config_path);
|
||||||
|
Config::load_file(&path, path.extension().unwrap_or_default().to_str().unwrap_or_default())
|
||||||
|
} else {
|
||||||
|
let config_dir = config_dir().expect("Failed to locate user config dir");
|
||||||
|
|
||||||
let extensions = vec!["json", "toml", "yaml", "yml", "corn"];
|
let extensions = vec!["json", "toml", "yaml", "yml", "corn"];
|
||||||
|
|
||||||
extensions.into_iter().find_map(|extension| {
|
extensions.into_iter().find_map(|extension| {
|
||||||
let full_path = config_dir
|
let full_path = config_dir
|
||||||
.join("ironbar")
|
.join("ironbar")
|
||||||
.join(format!("config.{extension}"));
|
.join(format!("config.{extension}"));
|
||||||
|
|
||||||
if full_path.exists() {
|
Config::load_file(&full_path, extension)
|
||||||
let file = fs::read(full_path).expect("Failed to read config file");
|
})
|
||||||
Some(match extension {
|
}
|
||||||
"json" => serde_json::from_slice(&file).expect("Invalid JSON config"),
|
}
|
||||||
"toml" => toml::from_slice(&file).expect("Invalid TOML config"),
|
|
||||||
"yaml" | "yml" => serde_yaml::from_slice(&file).expect("Invalid YAML config"),
|
fn load_file(path: &Path, extension: &str) -> Option<Self> {
|
||||||
"corn" => {
|
if path.exists() {
|
||||||
// corn doesn't support deserialization yet
|
let file = fs::read(path).expect("Failed to read config file");
|
||||||
// so serialize the interpreted result then deserialize that
|
Some(match extension {
|
||||||
let file =
|
"json" => serde_json::from_slice(&file).expect("Invalid JSON config"),
|
||||||
String::from_utf8(file).expect("Config file contains invalid UTF-8");
|
"toml" => toml::from_slice(&file).expect("Invalid TOML config"),
|
||||||
let config = cornfig::parse(&file).expect("Invalid corn config").value;
|
"yaml" | "yml" => serde_yaml::from_slice(&file).expect("Invalid YAML config"),
|
||||||
serde_json::from_str(&serde_json::to_string(&config).unwrap()).unwrap()
|
"corn" => {
|
||||||
}
|
// corn doesn't support deserialization yet
|
||||||
_ => unreachable!(),
|
// so serialize the interpreted result then deserialize that
|
||||||
})
|
let file = String::from_utf8(file).expect("Config file contains invalid UTF-8");
|
||||||
} else {
|
let config = cornfig::parse(&file).expect("Invalid corn config").value;
|
||||||
None
|
serde_json::from_str(&serde_json::to_string(&config).unwrap()).unwrap()
|
||||||
}
|
}
|
||||||
})
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
24
src/main.rs
24
src/main.rs
|
@ -8,7 +8,7 @@ mod style;
|
||||||
mod sway;
|
mod sway;
|
||||||
|
|
||||||
use crate::bar::create_bar;
|
use crate::bar::create_bar;
|
||||||
use crate::config::Config;
|
use crate::config::{Config, MonitorConfig};
|
||||||
use crate::style::load_css;
|
use crate::style::load_css;
|
||||||
use crate::sway::SwayOutput;
|
use crate::sway::SwayOutput;
|
||||||
use dirs::config_dir;
|
use dirs::config_dir;
|
||||||
|
@ -40,6 +40,7 @@ async fn main() {
|
||||||
|
|
||||||
let display = gdk::Display::default().expect("Failed to get default GDK display");
|
let display = gdk::Display::default().expect("Failed to get default GDK display");
|
||||||
let num_monitors = display.n_monitors();
|
let num_monitors = display.n_monitors();
|
||||||
|
|
||||||
for i in 0..num_monitors {
|
for i in 0..num_monitors {
|
||||||
let monitor = display.monitor(i).unwrap();
|
let monitor = display.monitor(i).unwrap();
|
||||||
let monitor_name = &outputs
|
let monitor_name = &outputs
|
||||||
|
@ -47,11 +48,22 @@ async fn main() {
|
||||||
.expect("GTK monitor output differs from Sway's")
|
.expect("GTK monitor output differs from Sway's")
|
||||||
.name;
|
.name;
|
||||||
|
|
||||||
let config = config.monitors.as_ref().map_or(&config, |monitor_config| {
|
if let Some(ref config) = config.monitors {
|
||||||
monitor_config.get(i as usize).unwrap_or(&config)
|
let config = config.get(monitor_name);
|
||||||
});
|
match &config {
|
||||||
|
Some(MonitorConfig::Single(config)) => {
|
||||||
create_bar(app, &monitor, monitor_name, config.clone());
|
create_bar(app, &monitor, monitor_name, config.clone());
|
||||||
|
}
|
||||||
|
Some(MonitorConfig::Multiple(configs)) => {
|
||||||
|
for config in configs {
|
||||||
|
create_bar(app, &monitor, monitor_name, config.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
create_bar(app, &monitor, monitor_name, config.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let style_path = config_dir()
|
let style_path = config_dir()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue