config: Generate configs

This commit is contained in:
Kenny Levinsen
2023-02-12 17:02:44 +01:00
parent db085168dc
commit 9e48eb053d
7 changed files with 179 additions and 104 deletions
-4
View File
@@ -8,10 +8,6 @@ edition = "2021"
lto = "fat" lto = "fat"
panic = "abort" panic = "abort"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
fontloading = ["fontconfig"]
[dependencies] [dependencies]
wayland-client = { version = "0.30", features = ["calloop", "log"] } wayland-client = { version = "0.30", features = ["calloop", "log"] }
wayland-protocols = { version = "0.30", features = ["client", "unstable", "staging"] } wayland-protocols = { version = "0.30", features = ["client", "unstable", "staging"] }
+116 -43
View File
@@ -6,7 +6,7 @@ use std::sync::{Arc, Mutex};
use crate::{ use crate::{
event::Events, event::Events,
fonts::FontMap, fonts::{FontMap, find_font},
widgets::{ widgets::{
Backlight, Battery, Calendar, Clock, Date, HorizontalLayout, IndexedLayout, Interface, Backlight, Battery, Calendar, Clock, Date, HorizontalLayout, IndexedLayout, Interface,
InvertedHorizontalLayout, Layout, Line, Margin, PulseAudio, VerticalLayout, Widget as RealWidget, InvertedHorizontalLayout, Layout, Line, Margin, PulseAudio, VerticalLayout, Widget as RealWidget,
@@ -14,7 +14,14 @@ use crate::{
}; };
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "snake_case")]
pub enum OperationMode {
LayerSurface((u32, u32)),
XdgToplevel,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum Widget { pub enum Widget {
Margin { Margin {
margins: (u32, u32, u32, u32), margins: (u32, u32, u32, u32),
@@ -58,38 +65,47 @@ pub enum Widget {
}, },
} }
impl Default for OperationMode {
fn default() -> OperationMode {
OperationMode::LayerSurface((1024, 768))
}
}
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "snake_case")]
pub struct Config { pub struct Config {
pub font_paths: Option<HashMap<String, String>>, pub font_paths: Option<HashMap<String, String>>,
pub widget: Option<Widget>, pub widget: Widget,
pub mode: OperationMode,
} }
impl Default for Config { impl Default for Config {
fn default() -> Config { fn default() -> Config {
Config{ Config{
font_paths: None, font_paths: None,
widget: None, widget: Default::default(),
mode: Default::default(),
} }
} }
} }
fn leak_or_default(v: Option<String>, d: &'static str) -> &'static str { impl Config {
match v { pub fn generate_v1() -> Config {
Some(v) => Box::leak(v.into_boxed_str()), let has_battery = Battery::detect();
None => d, let has_backlight = Backlight::detect("intel_backlight");
} let sans = find_font("sans");
} let monospace = find_font("monospace");
impl Default for Widget { let mut bars = Vec::new();
fn default() -> Widget { if has_battery {
Self::v2() bars.push(Widget::Margin{ margins: (0, 0, 0, 8), widget: Box::new(Widget::Battery { font: None, font_size: 24. })});
} }
} if has_backlight {
bars.push(Widget::Margin{ margins: (0, 0, 0, 8), widget: Box::new(Widget::Backlight { device: Some("intel_backlight".to_string()), font: None, font_size: 24. })});
}
bars.push(Widget::Margin{ margins: (0, 0, 0, 8), widget: Box::new(Widget::PulseAudio { font: None, font_size: 24. })});
impl Widget { let widget = Widget::Margin {
pub fn v1() -> Widget {
Widget::Margin {
margins: (20, 20, 20, 20), margins: (20, 20, 20, 20),
widget: Box::new(Widget::VerticalLayout(vec![ widget: Box::new(Widget::VerticalLayout(vec![
Widget::HorizontalLayout(vec![ Widget::HorizontalLayout(vec![
@@ -105,30 +121,7 @@ impl Widget {
]), ]),
Widget::Margin { Widget::Margin {
margins: (88, 0, 0, 0), margins: (88, 0, 0, 0),
widget: Box::new(Widget::VerticalLayout(vec![ widget: Box::new(Widget::VerticalLayout(bars)),
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 { Widget::Calendar {
@@ -143,10 +136,88 @@ impl Widget {
font_size: 32., font_size: 32.,
}, },
])), ])),
};
let map = HashMap::from([
("sans".to_string(), sans),
("monospace".to_string(), monospace),
]);
Config{
font_paths: Some(map),
widget: widget,
mode: OperationMode::LayerSurface((1024, 768)),
} }
} }
pub fn v2() -> Widget { pub fn generate_v2(clay: bool) -> Config {
let has_battery = Battery::detect();
let has_backlight = Backlight::detect("intel_backlight");
let sans = find_font("sans");
let monospace = find_font("monospace");
let mut bars = Vec::new();
if has_battery {
bars.push(Widget::Margin{ margins: (16, 8, 8, 0), widget: Box::new(Widget::Battery { font: None, font_size: 24. })});
}
if has_backlight {
bars.push(Widget::Margin{ margins: (16, 8, 8, 0), widget: Box::new(Widget::Backlight { device: Some("intel_backlight".to_string()), font: None, font_size: 24. })});
}
bars.push(Widget::Margin{ margins: (16, 8, 8, 0), widget: Box::new(Widget::PulseAudio { font: None, font_size: 24. })});
let 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(bars),
]),
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.,
},
]),
]);
let map = HashMap::from([
("sans".to_string(), sans),
("monospace".to_string(), monospace),
]);
Config{
font_paths: Some(map),
widget: widget,
mode: if clay { OperationMode::XdgToplevel } else { OperationMode::LayerSurface((1024, 768)) },
}
}
}
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 {
Widget::VerticalLayout(vec![ Widget::VerticalLayout(vec![
Widget::HorizontalLayout(vec![ Widget::HorizontalLayout(vec![
Widget::Clock { Widget::Clock {
@@ -201,7 +272,9 @@ impl Widget {
]), ]),
]) ])
} }
}
impl Widget {
pub fn construct_widgets( pub fn construct_widgets(
self, self,
v: &mut Vec<Box<dyn RealWidget>>, v: &mut Vec<Box<dyn RealWidget>>,
+6 -12
View File
@@ -9,12 +9,9 @@ use std::{
/// FontRef is used to store Fonts on widgets. /// FontRef is used to store Fonts on widgets.
pub type FontRef<'a> = &'a rusttype::Font<'a>; pub type FontRef<'a> = &'a rusttype::Font<'a>;
#[cfg(feature = "fontloading")] #[cfg(feature = "fontconfig")]
mod fc { pub(crate) fn find_font(name: &str) -> String {
use fontconfig::Fontconfig as FontConfig; use fontconfig::Fontconfig as FontConfig;
/// Acts like fc-match.
/// Given a string, it matches it to a font file and returns its path.
pub(crate) fn from_string(name: &str) -> String {
let fc = FontConfig::new().unwrap(); let fc = FontConfig::new().unwrap();
fc.find(name, None) fc.find(name, None)
.unwrap() .unwrap()
@@ -22,14 +19,11 @@ mod fc {
.to_str() .to_str()
.unwrap() .unwrap()
.to_string() .to_string()
}
} }
#[cfg(not(feature = "fontloading"))] #[cfg(not(feature = "fontconfig"))]
mod fc { pub(crate) fn find_font(_name: &str) -> String {
pub(crate) fn from_string(_name: &str) -> String { panic!("fontconfig not enabled so font search not available");
panic!("fontloading feature not enabled");
}
} }
/// FontLoader is a marker struct that is used to load files /// FontLoader is a marker struct that is used to load files
@@ -110,7 +104,7 @@ impl FontMap {
let path = match self.font_paths.get(font_name) { let path = match self.font_paths.get(font_name) {
Some(res) => res, Some(res) => res,
_ => { _ => {
let s = fc::from_string(font_name); let s = find_font(font_name);
self.font_paths.insert(font_name, s); self.font_paths.insert(font_name, s);
self.font_paths.get(font_name).unwrap() self.font_paths.get(font_name).unwrap()
} }
+19 -31
View File
@@ -20,39 +20,36 @@ use calloop::{
use chrono::{Duration, Local, Timelike}; use chrono::{Duration, Local, Timelike};
use buffer::BufferView; use buffer::BufferView;
use config::{Config, Widget as ConfigWidget}; use config::Config;
use event::{Event, Events}; use event::{Event, Events};
use fonts::{FontMap, MaybeFontMap}; use fonts::{FontMap, MaybeFontMap};
use state::{OperationMode, State}; use state::State;
use widgets::{Geometry, Widget}; use widgets::{Geometry, Widget};
use std::{env, rc::Rc, thread}; use std::{env, rc::Rc, thread};
enum UIVersion {
V1,
V2,
}
fn main() { fn main() {
let mut args = env::args(); let mut args = env::args();
// Skip program name // Skip program name
_ = args.next(); _ = args.next();
let mut version = UIVersion::V2;
let mut mode = OperationMode::XdgToplevel;
loop { loop {
match args.next() { match args.next() {
Some(ref s) if s == "layer_shell" => { Some(ref s) if s == "generate" => {
let width = args.next().map(|x| x.parse::<u32>().ok()).flatten(); let config = match args.next() {
let height = args.next().map(|x| x.parse::<u32>().ok()).flatten(); Some(ref s) if s == "v1" => Config::generate_v1(),
match (width, height) { Some(ref s) if s == "v2" => Config::generate_v2(false),
(Some(w), Some(h)) => mode = OperationMode::LayerSurface((w, h)), Some(ref s) if s == "clay" => Config::generate_v2(true),
_ => panic!("invalid arguments"), None => Config::generate_v2(false),
Some(s) => panic!("unknown argument: {}", s),
};
let home = env::var_os("HOME").unwrap().into_string().unwrap();
match File::create(format!("{}/.config/wldash/config.yml", home)) {
Ok(f) => serde_yaml::to_writer(f, &config).unwrap(),
Err(_) => panic!("uh"),
} }
} std::process::exit(0);
Some(ref s) if s == "xdg_shell" => mode = OperationMode::XdgToplevel, },
Some(ref s) if s == "v1" => version = UIVersion::V1,
Some(ref s) if s == "v2" => version = UIVersion::V2,
Some(_) => panic!("unknown argument"), Some(_) => panic!("unknown argument"),
None => break, None => break,
} }
@@ -122,24 +119,15 @@ fn main() {
Err(_) => Default::default(), 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 { if let Some(font_paths) = config.font_paths {
for (key, value) in font_paths.into_iter() { for (key, value) in font_paths.into_iter() {
fm.add_font_path(Box::leak(key.into_boxed_str()), value); fm.add_font_path(Box::leak(key.into_boxed_str()), value);
} }
} }
let layout = Rc::new(widget.construct_layout(&mut 0)); let layout = Rc::new(config.widget.construct_layout(&mut 0));
let mut widgets: Vec<Box<dyn Widget>> = Vec::new(); let mut widgets: Vec<Box<dyn Widget>> = Vec::new();
widget.construct_widgets(&mut widgets, &mut fm, &events); config.widget.construct_widgets(&mut widgets, &mut fm, &events);
let font_thread = thread::Builder::new() let font_thread = thread::Builder::new()
.name("fontloader".to_string()) .name("fontloader".to_string())
@@ -150,7 +138,7 @@ fn main() {
.unwrap(); .unwrap();
let mut state = State::new( let mut state = State::new(
mode, config.mode,
widgets, widgets,
layout, layout,
MaybeFontMap::Waiting(font_thread), MaybeFontMap::Waiting(font_thread),
+1 -6
View File
@@ -29,6 +29,7 @@ use wayland_protocols::{
use wayland_protocols_wlr::layer_shell::v1::client::{zwlr_layer_shell_v1, zwlr_layer_surface_v1}; use wayland_protocols_wlr::layer_shell::v1::client::{zwlr_layer_shell_v1, zwlr_layer_surface_v1};
use crate::{ use crate::{
config::OperationMode,
buffer::BufferManager, buffer::BufferManager,
event::{Event, Events, PointerButton, PointerEvent}, event::{Event, Events, PointerButton, PointerEvent},
fonts::{FontMap, MaybeFontMap}, fonts::{FontMap, MaybeFontMap},
@@ -36,12 +37,6 @@ use crate::{
widgets::{Geometry, Layout, Widget, WidgetUpdater}, widgets::{Geometry, Layout, Widget, WidgetUpdater},
}; };
#[derive(Debug)]
pub enum OperationMode {
LayerSurface((u32, u32)),
XdgToplevel,
}
#[derive(Default)] #[derive(Default)]
pub struct MainSurface { pub struct MainSurface {
pub wl_surface: Option<wl_surface::WlSurface>, pub wl_surface: Option<wl_surface::WlSurface>,
+8
View File
@@ -39,6 +39,14 @@ impl Backlight {
dev.update(); dev.update();
BarWidget::new(Box::new(dev), fm, font, size) BarWidget::new(Box::new(dev), fm, font, size)
} }
pub fn detect(path: &str) -> bool {
let path = Path::new("/sys/class/backlight").to_path_buf().join(path);
match read_file_as_u64(path.join("brightness").as_path()) {
Ok(_) => true,
Err(_) => false,
}
}
} }
impl BarWidgetImpl for Backlight { impl BarWidgetImpl for Backlight {
+21
View File
@@ -2,6 +2,7 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
thread, thread,
time::Duration, time::Duration,
error::Error,
}; };
use dbus::blocking::{ use dbus::blocking::{
@@ -46,6 +47,19 @@ struct InnerBattery {
dirty: bool, dirty: bool,
} }
fn get_battery_state() -> Result<UpowerBatteryState, Box<dyn Error>> {
let conn = LocalConnection::new_system()?;
let device_path = "/org/freedesktop/UPower/devices/DisplayDevice";
let proxy = conn.with_proxy(
"org.freedesktop.UPower",
device_path,
Duration::from_millis(500),
);
let state: u32 = proxy
.get("org.freedesktop.UPower.Device", "State")?;
Ok(UpowerBatteryState::from_dbus(state as u64))
}
fn start_monitor(inner: Arc<Mutex<InnerBattery>>, events: Arc<Mutex<Events>>) { fn start_monitor(inner: Arc<Mutex<InnerBattery>>, events: Arc<Mutex<Events>>) {
thread::Builder::new() thread::Builder::new()
.name("battmon".to_string()) .name("battmon".to_string())
@@ -131,6 +145,13 @@ impl Battery {
start_monitor(battery.inner.clone(), events); start_monitor(battery.inner.clone(), events);
BarWidget::new(Box::new(battery), fm, font, size) BarWidget::new(Box::new(battery), fm, font, size)
} }
pub fn detect() -> bool {
match get_battery_state() {
Ok(_) => true,
Err(_) => false,
}
}
} }
impl BarWidgetImpl for Battery { impl BarWidgetImpl for Battery {