config: Add config support

This commit is contained in:
Kenny Levinsen
2023-02-11 18:24:34 +01:00
parent 353ed1f189
commit db085168dc
4 changed files with 399 additions and 73 deletions
Generated
+69
View File
@@ -239,6 +239,12 @@ dependencies = [
"version_check",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "iana-time-zone"
version = "0.1.53"
@@ -263,6 +269,16 @@ dependencies = [
"cxx-build",
]
[[package]]
name = "indexmap"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "io-lifetimes"
version = "1.0.3"
@@ -273,6 +289,12 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "itoa"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "js-sys"
version = "0.3.60"
@@ -615,6 +637,12 @@ dependencies = [
"owned_ttf_parser",
]
[[package]]
name = "ryu"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "same-file"
version = "1.0.6"
@@ -636,6 +664,39 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8132065adcfd6e02db789d9285a0deb2f3fcb04002865ab67d5fb103533898"
[[package]]
name = "serde"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_yaml"
version = "0.9.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fb06d4b6cdaef0e0c51fa881acb721bed3c924cfaa71d9c94a3b771dfdf6567"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "sha1"
version = "0.10.5"
@@ -770,6 +831,12 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "unsafe-libyaml"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc7ed8ba44ca06be78ea1ad2c3682a43349126c8818054231ee6f4748012aed2"
[[package]]
name = "vec_map"
version = "0.8.2"
@@ -1032,6 +1099,8 @@ dependencies = [
"nix 0.26.1",
"rcalc_lib",
"rusttype",
"serde",
"serde_yaml",
"shlex",
"unicode-segmentation",
"walkdir",
+3
View File
@@ -37,3 +37,6 @@ rcalc_lib = "1.0"
#alsa = { version = "0.7", optional = true }
dbus = "0.9"
libpulse-binding = "2.26"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9"
+299
View File
@@ -0,0 +1,299 @@
use serde::{self, Deserialize, Serialize};
use std::collections::HashMap;
use std::default::Default;
use std::sync::{Arc, Mutex};
use crate::{
event::Events,
fonts::FontMap,
widgets::{
Backlight, Battery, Calendar, Clock, Date, HorizontalLayout, IndexedLayout, Interface,
InvertedHorizontalLayout, Layout, Line, Margin, PulseAudio, VerticalLayout, Widget as RealWidget,
},
};
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub enum Widget {
Margin {
margins: (u32, u32, u32, u32),
widget: Box<Widget>,
},
HorizontalLayout(Vec<Widget>),
InvertedHorizontalLayout(Vec<Widget>),
VerticalLayout(Vec<Widget>),
Line(u32),
Clock {
font: Option<String>,
font_size: f32,
},
Date {
font: Option<String>,
font_size: f32,
},
Calendar {
font_primary: Option<String>,
font_secondary: Option<String>,
font_size: f32,
sections_x: i32,
sections_y: i32,
},
Launcher {
font: Option<String>,
font_size: f32,
},
Battery {
font: Option<String>,
font_size: f32,
},
Backlight {
device: Option<String>,
font: Option<String>,
font_size: f32,
},
PulseAudio {
font: Option<String>,
font_size: f32,
},
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Config {
pub font_paths: Option<HashMap<String, String>>,
pub widget: Option<Widget>,
}
impl Default for Config {
fn default() -> Config {
Config{
font_paths: None,
widget: None,
}
}
}
fn leak_or_default(v: Option<String>, d: &'static str) -> &'static str {
match v {
Some(v) => Box::leak(v.into_boxed_str()),
None => d,
}
}
impl Default for Widget {
fn default() -> Widget {
Self::v2()
}
}
impl Widget {
pub fn v1() -> Widget {
Widget::Margin {
margins: (20, 20, 20, 20),
widget: Box::new(Widget::VerticalLayout(vec![
Widget::HorizontalLayout(vec![
Widget::VerticalLayout(vec![
Widget::Date {
font: None,
font_size: 48.,
},
Widget::Clock {
font: None,
font_size: 256.,
},
]),
Widget::Margin {
margins: (88, 0, 0, 0),
widget: Box::new(Widget::VerticalLayout(vec![
Widget::Margin {
margins: (0, 0, 0, 8),
widget: Box::new(Widget::Battery {
font: None,
font_size: 24.,
}),
},
Widget::Margin {
margins: (0, 0, 0, 8),
widget: Box::new(Widget::Backlight {
device: None,
font: None,
font_size: 24.,
}),
},
Widget::Margin {
margins: (0, 0, 0, 8),
widget: Box::new(Widget::PulseAudio {
font: None,
font_size: 24.,
}),
},
])),
},
]),
Widget::Calendar {
font_primary: None,
font_secondary: None,
font_size: 36.,
sections_x: 3,
sections_y: 1,
},
Widget::Launcher {
font: None,
font_size: 32.,
},
])),
}
}
pub fn v2() -> Widget {
Widget::VerticalLayout(vec![
Widget::HorizontalLayout(vec![
Widget::Clock {
font: None,
font_size: 128.,
},
Widget::Margin {
margins: (16, 16, 0, 0),
widget: Box::new(Widget::Date {
font: None,
font_size: 48.,
}),
},
Widget::VerticalLayout(vec![
Widget::Margin {
margins: (16, 8, 8, 0),
widget: Box::new(Widget::Battery {
font: None,
font_size: 24.,
}),
},
Widget::Margin {
margins: (16, 8, 8, 0),
widget: Box::new(Widget::Backlight {
device: None,
font: None,
font_size: 24.,
}),
},
Widget::Margin {
margins: (16, 8, 8, 0),
widget: Box::new(Widget::PulseAudio {
font: None,
font_size: 24.,
}),
},
]),
]),
Widget::Line(1),
Widget::InvertedHorizontalLayout(vec![
Widget::Calendar {
font_primary: None,
font_secondary: None,
font_size: 24.,
sections_x: 1,
sections_y: -1,
},
Widget::Launcher {
font: None,
font_size: 32.,
},
]),
])
}
pub fn construct_widgets(
self,
v: &mut Vec<Box<dyn RealWidget>>,
mut fm: &mut FontMap,
events: &Arc<Mutex<Events>>,
) {
match self {
Widget::Margin { widget, .. } => widget.construct_widgets(v, fm, events),
Widget::HorizontalLayout(widgets) => widgets
.into_iter()
.for_each(|w| w.construct_widgets(v, fm, events)),
Widget::InvertedHorizontalLayout(widgets) => widgets
.into_iter()
.for_each(|w| w.construct_widgets(v, fm, events)),
Widget::VerticalLayout(widgets) => widgets
.into_iter()
.for_each(|w| w.construct_widgets(v, fm, events)),
Widget::Line(width) => v.push(Box::new(Line::new(width))),
Widget::Clock { font, font_size } => v.push(Box::new(Clock::new(
&mut fm,
leak_or_default(font, "sans"),
font_size,
))),
Widget::Date { font, font_size } => v.push(Box::new(Date::new(
&mut fm,
leak_or_default(font, "sans"),
font_size,
))),
Widget::Calendar {
font_primary,
font_secondary,
font_size,
sections_x,
sections_y,
} => v.push(Box::new(Calendar::new(
&mut fm,
leak_or_default(font_primary, "monospace"),
leak_or_default(font_secondary, "sans"),
font_size,
sections_x,
sections_y,
))),
Widget::Launcher { font, font_size } => v.push(Box::new(Interface::new(
events.clone(),
&mut fm,
leak_or_default(font, "sans"),
font_size,
))),
Widget::Battery { font, font_size } => v.push(Box::new(Battery::new(
events.clone(),
&mut fm,
leak_or_default(font, "sans"),
font_size,
))),
Widget::Backlight {
device,
font,
font_size,
} => v.push(Box::new(Backlight::new(
leak_or_default(device, "intel_backlight"),
&mut fm,
leak_or_default(font, "sans"),
font_size,
))),
Widget::PulseAudio { font, font_size } => v.push(Box::new(PulseAudio::new(
events.clone(),
&mut fm,
Box::leak(font.unwrap_or_else(|| "sans".to_string()).into_boxed_str()),
font_size,
))),
}
}
pub fn construct_layout(&self, idx: &mut usize) -> Box<dyn Layout> {
match self {
Widget::Margin { margins, widget } => {
Margin::new(widget.construct_layout(idx), *margins)
}
Widget::HorizontalLayout(widgets) => {
HorizontalLayout::new(widgets.iter().map(|w| w.construct_layout(idx)).collect())
}
Widget::InvertedHorizontalLayout(widgets) => InvertedHorizontalLayout::new(
widgets.iter().map(|w| w.construct_layout(idx)).collect(),
),
Widget::VerticalLayout(widgets) => {
VerticalLayout::new(widgets.iter().map(|w| w.construct_layout(idx)).collect())
}
_ => IndexedLayout::new({
let i = *idx;
*idx += 1;
i
}),
}
}
}
+28 -73
View File
@@ -1,5 +1,6 @@
mod buffer;
mod color;
mod config;
mod draw;
mod event;
mod fonts;
@@ -8,6 +9,8 @@ mod state;
mod utils;
mod widgets;
use std::fs::File;
use wayland_client::{Connection, QueueHandle, WaylandSource};
use calloop::{
@@ -17,13 +20,11 @@ use calloop::{
use chrono::{Duration, Local, Timelike};
use buffer::BufferView;
use config::{Config, Widget as ConfigWidget};
use event::{Event, Events};
use fonts::{FontMap, MaybeFontMap};
use state::{OperationMode, State};
use widgets::{
Backlight, Battery, Calendar, Clock, Date, Geometry, HorizontalLayout, IndexedLayout,
Interface, InvertedHorizontalLayout, Layout, Line, Margin, PulseAudio, VerticalLayout, Widget,
};
use widgets::{Geometry, Widget};
use std::{env, rc::Rc, thread};
@@ -115,77 +116,31 @@ fn main() {
let mut fm = FontMap::new();
// TODO: Cache in a file
fm.add_font_path(
"sans",
"/usr/share/fonts/noto/NotoSans-Regular.ttf".to_string(),
);
fm.add_font_path(
"monospace",
"/usr/share/fonts/noto/NotoSansMono-Regular.ttf".to_string(),
);
let (widgets, layout): (Vec<Box<dyn Widget>>, Rc<Box<dyn Layout>>) = match version {
UIVersion::V1 => {
let clock = Box::new(Clock::new(&mut fm, "sans", 256.));
let calendar = Box::new(Calendar::new(&mut fm, "monospace", "sans", 36.0, 3, 1));
let date = Box::new(Date::new(&mut fm, "sans", 48.));
let launcher = Box::new(Interface::new(events.clone(), &mut fm, "monospace", 32.));
let battery = Box::new(Battery::new(events.clone(), &mut fm, "sans", 24.));
let backlight = Box::new(Backlight::new("intel_backlight", &mut fm, "sans", 24.));
let audio = Box::new(PulseAudio::new(events.clone(), &mut fm, "sans", 24.));
let v: Vec<Box<dyn Widget>> =
vec![clock, date, battery, backlight, audio, calendar, launcher];
let l = Rc::new(Margin::new(
VerticalLayout::new(vec![
HorizontalLayout::new(vec![
VerticalLayout::new(vec![IndexedLayout::new(1), IndexedLayout::new(0)]),
Margin::new(
VerticalLayout::new(vec![
Margin::new(IndexedLayout::new(2), (0, 0, 0, 8)),
Margin::new(IndexedLayout::new(3), (0, 0, 0, 8)),
Margin::new(IndexedLayout::new(4), (0, 0, 0, 8)),
]),
(88, 0, 0, 0),
),
]),
IndexedLayout::new(5),
IndexedLayout::new(6),
]),
(20, 20, 20, 20),
));
(v, l)
}
UIVersion::V2 => {
let clock = Box::new(Clock::new(&mut fm, "sans", 128.));
let calendar = Box::new(Calendar::new(&mut fm, "monospace", "sans", 24.0, 1, -1));
let date = Box::new(Date::new(&mut fm, "sans", 48.));
let line = Box::new(Line::new(1));
let launcher = Box::new(Interface::new(events.clone(), &mut fm, "monospace", 32.));
let battery = Box::new(Battery::new(events.clone(), &mut fm, "sans", 24.));
let backlight = Box::new(Backlight::new("intel_backlight", &mut fm, "sans", 24.));
let audio = Box::new(PulseAudio::new(events.clone(), &mut fm, "sans", 24.));
let v: Vec<Box<dyn Widget>> = vec![
clock, date, battery, backlight, audio, line, calendar, launcher,
];
let l = Rc::new(VerticalLayout::new(vec![
HorizontalLayout::new(vec![
IndexedLayout::new(0),
Margin::new(IndexedLayout::new(1), (16, 16, 0, 0)),
VerticalLayout::new(vec![
Margin::new(IndexedLayout::new(2), (16, 8, 8, 0)),
Margin::new(IndexedLayout::new(3), (16, 8, 8, 0)),
Margin::new(IndexedLayout::new(4), (16, 8, 8, 0)),
]),
]),
IndexedLayout::new(5),
InvertedHorizontalLayout::new(vec![IndexedLayout::new(6), IndexedLayout::new(7)]),
]));
(v, l)
}
let home = env::var_os("HOME").unwrap().into_string().unwrap();
let config: Config = match File::open(format!("{}/.config/wldash/config.yml", home)) {
Ok(f) => serde_yaml::from_reader(f).unwrap(),
Err(_) => Default::default(),
};
let widget = match config.widget {
Some(w) => w,
None => match version {
UIVersion::V1 => ConfigWidget::v1(),
UIVersion::V2 => ConfigWidget::v2(),
},
};
if let Some(font_paths) = config.font_paths {
for (key, value) in font_paths.into_iter() {
fm.add_font_path(Box::leak(key.into_boxed_str()), value);
}
}
let layout = Rc::new(widget.construct_layout(&mut 0));
let mut widgets: Vec<Box<dyn Widget>> = Vec::new();
widget.construct_widgets(&mut widgets, &mut fm, &events);
let font_thread = thread::Builder::new()
.name("fontloader".to_string())
.spawn(move || {