From d6ccf4cf9a37c6ccdb47f0f485089b9fc1a12d5d Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Sun, 8 Sep 2024 20:25:05 -0700 Subject: [PATCH] feat(bar): add logging and config hotwatch --- Cargo.lock | 4 ++ komorebi-bar/Cargo.toml | 6 ++- komorebi-bar/src/bar.rs | 90 +++++++++++++++++++++++++++--------- komorebi-bar/src/komorebi.rs | 4 ++ komorebi-bar/src/main.rs | 89 ++++++++++++++++++++++++++++------- 5 files changed, 154 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25255ea2..049d1fff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2686,6 +2686,7 @@ dependencies = [ "eframe", "egui-phosphor", "font-loader", + "hotwatch", "image", "komorebi-client", "netdev", @@ -2695,6 +2696,9 @@ dependencies = [ "serde_yaml 0.8.26", "starship-battery", "sysinfo 0.31.3", + "tracing", + "tracing-appender", + "tracing-subscriber", "windows 0.54.0", "windows-icons", ] diff --git a/komorebi-bar/Cargo.toml b/komorebi-bar/Cargo.toml index 90ede13f..5de2766f 100644 --- a/komorebi-bar/Cargo.toml +++ b/komorebi-bar/Cargo.toml @@ -10,12 +10,14 @@ komorebi-client = { path = "../komorebi-client" } catppuccin-egui = { version = "5.1", default-features = false, features = ["egui28"] } chrono = "0.4" +clap = { version = "4", features = ["derive", "wrap_help"] } color-eyre = "0.6" crossbeam-channel = "0.5" dirs = { workspace = true } eframe = "0.28" egui-phosphor = "0.6.0" font-loader = "0.11" +hotwatch = "0.5" image = "0.25" netdev = "0.30" schemars = { workspace = true } @@ -24,6 +26,8 @@ serde_json = { workspace = true } serde_yaml = "0.8" starship-battery = "0.10" sysinfo = "0.31" +tracing = "0.1" +tracing-appender = "0.2" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } windows = { workspace = true } windows-icons = "0.1" -clap = { version = "4", features = ["derive", "wrap_help"] } diff --git a/komorebi-bar/src/bar.rs b/komorebi-bar/src/bar.rs index 9b00e864..f37bb099 100644 --- a/komorebi-bar/src/bar.rs +++ b/komorebi-bar/src/bar.rs @@ -14,47 +14,56 @@ use eframe::egui::FontFamily; use eframe::egui::Frame; use eframe::egui::Layout; use eframe::egui::Margin; +use eframe::egui::Style; use font_loader::system_fonts; use font_loader::system_fonts::FontPropertyBuilder; use std::cell::RefCell; -use std::ops::Deref; use std::rc::Rc; use std::sync::Arc; pub struct Komobar { - pub config: KomobarConfig, + pub config: Arc, pub komorebi_notification_state: Option>>, pub left_widgets: Vec>, pub right_widgets: Vec>, pub rx_gui: Receiver, + pub rx_config: Receiver, } impl Komobar { - pub fn new( - cc: &eframe::CreationContext<'_>, - rx_gui: Receiver, - config: Arc, - ) -> Self { + pub fn apply_config( + &mut self, + ctx: &Context, + config: &KomobarConfig, + previous_notification_state: Option>>, + ) { if let Some(font_family) = &config.font_family { - Self::add_custom_font(&cc.egui_ctx, font_family); + tracing::info!("attempting to add custom font family: {font_family}"); + Self::add_custom_font(ctx, font_family); } match config.theme { Some(Theme::CatppuccinFrappe) => { - catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::FRAPPE); + catppuccin_egui::set_theme(ctx, catppuccin_egui::FRAPPE); + tracing::info!("theme updated: Catppuccin Frappe"); } Some(Theme::CatppuccinMacchiato) => { - catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::MACCHIATO); + catppuccin_egui::set_theme(ctx, catppuccin_egui::MACCHIATO); + tracing::info!("theme updated: Catppuccin Macchiato"); } Some(Theme::CatppuccinMocha) => { - catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::MOCHA); + catppuccin_egui::set_theme(ctx, catppuccin_egui::MOCHA); + tracing::info!("theme updated: Catppuccin Mocha"); + } + Some(Theme::Default) | None => { + ctx.set_style(Style::default()); + tracing::info!("theme updated: Egui Default"); } - Some(Theme::Default) | None => {} } let mut komorebi_widget = None; let mut komorebi_widget_idx = None; - let mut komorebi_notification_state = None; + let mut komorebi_notification_state = previous_notification_state; let mut side = None; for (idx, widget_config) in config.left_widgets.iter().enumerate() { @@ -85,9 +94,22 @@ impl Komobar { .map(|config| config.as_boxed_bar_widget()) .collect::>>(); - if let (Some(idx), Some(widget), Some(side)) = (komorebi_widget_idx, komorebi_widget, side) + if let (Some(idx), Some(mut widget), Some(side)) = + (komorebi_widget_idx, komorebi_widget, side) { - komorebi_notification_state = Some(widget.komorebi_notification_state.clone()); + match komorebi_notification_state { + None => { + komorebi_notification_state = Some(widget.komorebi_notification_state.clone()); + } + Some(ref previous) => { + previous + .borrow_mut() + .update_from_config(&widget.komorebi_notification_state.borrow()); + + widget.komorebi_notification_state = previous.clone(); + } + } + let boxed: Box = Box::new(widget); match side { Side::Left => left_widgets[idx] = boxed, @@ -97,13 +119,31 @@ impl Komobar { right_widgets.reverse(); - Self { - config: config.deref().clone(), - komorebi_notification_state, - left_widgets, - right_widgets, + self.left_widgets = left_widgets; + self.right_widgets = right_widgets; + + tracing::info!("widget configuration options applied"); + + self.komorebi_notification_state = komorebi_notification_state; + } + pub fn new( + cc: &eframe::CreationContext<'_>, + rx_gui: Receiver, + rx_config: Receiver, + config: Arc, + ) -> Self { + let mut komobar = Self { + config: config.clone(), + komorebi_notification_state: None, + left_widgets: vec![], + right_widgets: vec![], rx_gui, - } + rx_config, + }; + + komobar.apply_config(&cc.egui_ctx, &config, None); + + komobar } fn add_custom_font(ctx: &Context, name: &str) { @@ -144,6 +184,14 @@ impl eframe::App for Komobar { // } fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) { + if let Ok(updated_config) = self.rx_config.try_recv() { + self.apply_config( + ctx, + &updated_config, + self.komorebi_notification_state.clone(), + ); + } + if let Some(komorebi_notification_state) = &self.komorebi_notification_state { komorebi_notification_state .borrow_mut() diff --git a/komorebi-bar/src/komorebi.rs b/komorebi-bar/src/komorebi.rs index 8bb0badf..60fee5d2 100644 --- a/komorebi-bar/src/komorebi.rs +++ b/komorebi-bar/src/komorebi.rs @@ -163,6 +163,10 @@ pub struct KomorebiNotificationState { } impl KomorebiNotificationState { + pub fn update_from_config(&mut self, config: &Self) { + self.hide_empty_workspaces = config.hide_empty_workspaces; + } + pub fn handle_notification( &mut self, monitor_index: usize, diff --git a/komorebi-bar/src/main.rs b/komorebi-bar/src/main.rs index 5311fad2..47eb3656 100644 --- a/komorebi-bar/src/main.rs +++ b/komorebi-bar/src/main.rs @@ -15,6 +15,8 @@ use crate::config::KomobarConfig; use crate::config::Position; use clap::Parser; use eframe::egui::ViewportBuilder; +use hotwatch::EventKind; +use hotwatch::Hotwatch; use komorebi_client::SocketMessage; use schemars::gen::SchemaSettings; use std::io::BufReader; @@ -22,6 +24,7 @@ use std::io::Read; use std::path::PathBuf; use std::sync::Arc; use std::time::Duration; +use tracing_subscriber::EnvFilter; pub static WIDGET_SPACING: f32 = 10.0; @@ -36,7 +39,7 @@ struct Opts { config: Option, } -fn main() -> eframe::Result<()> { +fn main() -> color_eyre::Result<()> { let opts: Opts = Opts::parse(); if opts.schema { @@ -48,12 +51,28 @@ fn main() -> eframe::Result<()> { let gen = settings.into_generator(); let socket_message = gen.into_root_schema_for::(); - let schema = serde_json::to_string_pretty(&socket_message).unwrap(); + let schema = serde_json::to_string_pretty(&socket_message)?; println!("{schema}"); std::process::exit(0); } + if std::env::var("RUST_LIB_BACKTRACE").is_err() { + std::env::set_var("RUST_LIB_BACKTRACE", "1"); + } + + color_eyre::install()?; + + if std::env::var("RUST_LOG").is_err() { + std::env::set_var("RUST_LOG", "info"); + } + + tracing::subscriber::set_global_default( + tracing_subscriber::fmt::Subscriber::builder() + .with_env_filter(EnvFilter::from_default_env()) + .finish(), + )?; + let home_dir: PathBuf = std::env::var("KOMOREBI_CONFIG_HOME").map_or_else( |_| dirs::home_dir().expect("there is no home directory"), |home_path| { @@ -89,10 +108,19 @@ fn main() -> eframe::Result<()> { "no komorebi.bar.json or komorebi.bar.yaml found in {}", home_dir.as_path().to_string_lossy() ), - Some(config) => KomobarConfig::read(&config).unwrap(), + Some(ref config) => { + tracing::info!( + "found configuration file: {}", + config.as_path().to_string_lossy() + ); + + KomobarConfig::read(config)? + } }; - let mut builder = ViewportBuilder::default() + let config_path = config_path.unwrap(); + + let mut viewport_builder = ViewportBuilder::default() .with_decorations(false) // .with_transparent(config.transparent) .with_taskbar(false) @@ -100,8 +128,7 @@ fn main() -> eframe::Result<()> { .with_inner_size({ let state = serde_json::from_str::( &komorebi_client::send_query(&SocketMessage::State).unwrap(), - ) - .unwrap(); + )?; Position { x: state.monitors.elements()[config.monitor.index].size().right as f32, @@ -111,18 +138,18 @@ fn main() -> eframe::Result<()> { if let Some(viewport) = &config.viewport { if let Some(position) = &viewport.position { - let b = builder.clone(); - builder = b.with_position(*position); + let b = viewport_builder.clone(); + viewport_builder = b.with_position(*position); } if let Some(inner_size) = &viewport.inner_size { - let b = builder.clone(); - builder = b.with_inner_size(*inner_size); + let b = viewport_builder.clone(); + viewport_builder = b.with_inner_size(*inner_size); } } let native_options = eframe::NativeOptions { - viewport: builder, + viewport: viewport_builder, ..Default::default() }; @@ -130,13 +157,35 @@ fn main() -> eframe::Result<()> { komorebi_client::send_message(&SocketMessage::MonitorWorkAreaOffset( config.monitor.index, *rect, - )) - .unwrap(); + ))?; + tracing::info!( + "work area offset applied to monitor: {}", + config.monitor.index + ); } let (tx_gui, rx_gui) = crossbeam_channel::unbounded(); - let config_arc = Arc::new(config); + let (tx_config, rx_config) = crossbeam_channel::unbounded(); + + let mut hotwatch = Hotwatch::new()?; + let config_path_cl = config_path.clone(); + + hotwatch.watch(config_path, move |event| match event.kind { + EventKind::Modify(_) | EventKind::Remove(_) => { + let updated = KomobarConfig::read(&config_path_cl).unwrap(); + tx_config.send(updated).unwrap(); + + tracing::info!( + "configuration file updated: {}", + config_path_cl.as_path().to_string_lossy() + ); + } + _ => {} + })?; + tracing::info!("watching configuration file for changes"); + + let config_arc = Arc::new(config); eframe::run_native( "komorebi-bar", native_options, @@ -152,6 +201,7 @@ fn main() -> eframe::Result<()> { let ctx_komorebi = cc.egui_ctx.clone(); std::thread::spawn(move || { let listener = komorebi_client::subscribe("komorebi-bar").unwrap(); + tracing::info!("subscribed to komorebi notifications: \"komorebi-bar\""); for client in listener.incoming() { match client { @@ -161,6 +211,8 @@ fn main() -> eframe::Result<()> { // this is when we know a shutdown has been sent if matches!(reader.read_to_end(&mut buffer), Ok(0)) { + tracing::info!("disconnected from komorebi"); + // keep trying to reconnect to komorebi while komorebi_client::send_message( &SocketMessage::AddSubscriberSocket(String::from( @@ -172,7 +224,8 @@ fn main() -> eframe::Result<()> { std::thread::sleep(Duration::from_secs(1)); } - // here we have reconnected + tracing::info!("reconnected to komorebi"); + if let Some(rect) = &config_cl.monitor.work_area_offset { while komorebi_client::send_message( &SocketMessage::MonitorWorkAreaOffset( @@ -192,18 +245,20 @@ fn main() -> eframe::Result<()> { &String::from_utf8(buffer).unwrap(), ) { + tracing::debug!("received notification from komorebi"); tx_gui.send(notification).unwrap(); ctx_komorebi.request_repaint(); } } Err(error) => { - dbg!(error); + tracing::error!("{error}"); } } } }); - Ok(Box::new(Komobar::new(cc, rx_gui, config_arc))) + Ok(Box::new(Komobar::new(cc, rx_gui, rx_config, config_arc))) }), ) + .map_err(|error| color_eyre::eyre::Error::msg(error.to_string())) }