diff --git a/Cargo.lock b/Cargo.lock index 32faaf5..55381d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -280,6 +280,19 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log 0.4.11", + "regex", + "termcolor", +] + [[package]] name = "eww" version = "0.1.0" @@ -297,10 +310,12 @@ dependencies = [ "hotwatch", "ipc-channel", "itertools", + "log 0.4.11", "maplit", "notify", "num", "pretty_assertions", + "pretty_env_logger", "regex", "roxmltree", "scheduled-executor", @@ -762,6 +777,15 @@ dependencies = [ "notify", ] +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + [[package]] name = "indexmap" version = "1.6.0" @@ -1259,6 +1283,16 @@ dependencies = [ "output_vt100", ] +[[package]] +name = "pretty_env_logger" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" +dependencies = [ + "env_logger", + "log 0.4.11", +] + [[package]] name = "proc-macro-crate" version = "0.1.5" @@ -1313,6 +1347,12 @@ dependencies = [ "unicode-xid 0.2.1", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quote" version = "0.3.15" @@ -1639,6 +1679,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "termcolor" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +dependencies = [ + "winapi-util", +] + [[package]] name = "textwrap" version = "0.11.0" diff --git a/Cargo.toml b/Cargo.toml index 3c9da6b..004d22f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,8 @@ roxmltree = "0.13" itertools = "0.9" scheduled-executor = "0.4" debug_stub_derive = "0.3" +log = "0.4" +pretty_env_logger = "0.4" #thiserror = "1.0" diff --git a/src/app.rs b/src/app.rs index 9fb06fc..384d999 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,5 +1,6 @@ use crate::*; use debug_stub_derive::*; +use script_var_handler::*; use std::collections::HashMap; #[derive(Debug)] @@ -16,12 +17,9 @@ pub struct App { pub eww_config: config::EwwConfig, pub windows: HashMap, pub css_provider: gtk::CssProvider, - #[debug_stub = "script-var poll script handles"] - pub script_var_poll_handles: Vec, - #[debug_stub = "script-var poll executor"] - pub script_var_poll_executor: scheduled_executor::CoreExecutor, - pub app_evt_send: glib::Sender, + #[debug_stub = "ScriptVarHandler(...)"] + pub script_var_handler: ScriptVarHandler, } impl App { @@ -35,6 +33,7 @@ impl App { } pub fn handle_event(&mut self, event: EwwEvent) { + log::debug!("Handling event: {:?}", &event); let result: Result<_> = try { match event { EwwEvent::UserCommand(command) => self.handle_user_command(command)?, @@ -109,9 +108,7 @@ impl App { pub fn reload_all_windows(&mut self, config: config::EwwConfig) -> Result<()> { // refresh script-var poll stuff - self.script_var_poll_handles.iter().for_each(|handle| handle.stop()); - self.script_var_poll_handles.clear(); - if let Err(e) = self.init_command_poll_tasks() { + if let Err(e) = self.script_var_handler.setup_command_poll_tasks(&config) { eprintln!("Error while setting up script-var commands: {:?}", e); } @@ -130,33 +127,6 @@ impl App { self.css_provider.load_from_data(css.as_bytes())?; Ok(()) } - - pub fn init_command_poll_tasks(&mut self) -> Result<()> { - let evt_send = self.app_evt_send.clone(); - self.script_var_poll_handles = self - .eww_config - .get_script_vars() - .iter() - .map(|var| { - self.script_var_poll_executor.schedule_fixed_interval( - std::time::Duration::from_secs(0), - var.interval, - glib::clone!(@strong var, @strong evt_send => move |_| { - let result = eww_state::run_command(&var.command); - match result { - Ok(value) => { - let _ = evt_send.send(app::EwwEvent::UpdateVar(var.name.clone(), value)); - } - Err(e) => { - eprintln!("Error while running script-var command: {:?}", e); - } - } - }), - ) - }) - .collect_vec(); - Ok(()) - } } fn on_screen_changed(window: >k::Window, _old_screen: Option<&gdk::Screen>) { diff --git a/src/main.rs b/src/main.rs index 29c80b1..9bb2c1c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,8 +10,8 @@ use gdk::*; use gtk::prelude::*; use hotwatch; use ipc_channel::ipc; -use itertools::Itertools; -use scheduled_executor; +use log; +use pretty_env_logger; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::path::{Path, PathBuf}; @@ -21,6 +21,7 @@ use value::PrimitiveValue; pub mod app; pub mod config; pub mod eww_state; +pub mod script_var_handler; pub mod util; pub mod value; pub mod widgets; @@ -44,6 +45,7 @@ macro_rules! try_logging_errors { } fn main() { + pretty_env_logger::init(); if let Err(e) = try_main() { eprintln!("{:?}", e); } @@ -71,9 +73,12 @@ pub enum OptAction { fn try_main() -> Result<()> { let opts: Opt = StructOpt::from_args(); + log::info!("Trying to find server process"); if let Ok(sender) = find_server_process() { + log::info!("Forwarding options to server"); sender.send(opts)?; } else { + log::info!("No instance found... Initializing server."); initialize_server(opts)?; } Ok(()) @@ -102,18 +107,21 @@ fn initialize_server(opts: Opt) -> Result<()> { .to_path_buf(); let scss_file_path = config_dir.join("eww.scss"); + log::info!("reading configuration from {:?}", &config_file_path); let eww_config = config::EwwConfig::read_from_file(&config_file_path)?; gtk::init()?; let (evt_send, evt_recv) = glib::MainContext::channel(glib::PRIORITY_DEFAULT); + let mut script_var_handler = script_var_handler::ScriptVarHandler::new(evt_send.clone())?; + script_var_handler.setup_command_poll_tasks(&eww_config)?; + let mut app = app::App { eww_state: EwwState::from_default_vars(eww_config.generate_initial_state()?.clone()), eww_config, windows: HashMap::new(), css_provider: gtk::CssProvider::new(), - script_var_poll_handles: Vec::new(), - script_var_poll_executor: scheduled_executor::CoreExecutor::new()?, + script_var_handler, app_evt_send: evt_send.clone(), }; @@ -131,8 +139,6 @@ fn initialize_server(opts: Opt) -> Result<()> { run_server_thread(evt_send.clone()); let _hotwatch = run_filewatch_thread(&config_file_path, &scss_file_path, evt_send.clone())?; - app.init_command_poll_tasks()?; - evt_recv.attach(None, move |msg| { app.handle_event(msg); glib::Continue(true) @@ -145,11 +151,13 @@ fn initialize_server(opts: Opt) -> Result<()> { fn run_server_thread(evt_send: glib::Sender) { std::thread::spawn(move || { + log::info!("Starting up eww server"); let result: Result<_> = try { loop { let (ipc_server, instance_path): (ipc::IpcOneShotServer, _) = ipc::IpcOneShotServer::new()?; std::fs::write("/tmp/eww-instance-path", instance_path)?; let (_, initial) = ipc_server.accept()?; + log::info!("received command from IPC: {:?}", &initial); evt_send.send(app::EwwEvent::UserCommand(initial))?; } }; @@ -165,11 +173,13 @@ fn run_filewatch_thread>( scss_file_path: P, evt_send: glib::Sender, ) -> Result { + log::info!("Initializing config file watcher"); let mut hotwatch = hotwatch::Hotwatch::new()?; let config_file_change_send = evt_send.clone(); hotwatch.watch_file_changes(config_file_path, move |path| { try_logging_errors!("handling change of config file" => { + log::info!("Reloading eww configuration"); let new_eww_config = config::EwwConfig::read_from_file(path)?; config_file_change_send.send(app::EwwEvent::ReloadConfig(new_eww_config))?; }); @@ -177,6 +187,7 @@ fn run_filewatch_thread>( let result = hotwatch.watch_file_changes(scss_file_path, move |path| { try_logging_errors!("handling change of scss file" => { + log::info!("reloading eww css file"); let eww_css = util::parse_scss_from_file(path)?; evt_send.send(app::EwwEvent::ReloadCss(eww_css))?; }) diff --git a/src/script_var_handler.rs b/src/script_var_handler.rs new file mode 100644 index 0000000..2569f1b --- /dev/null +++ b/src/script_var_handler.rs @@ -0,0 +1,48 @@ +use crate::{app, config, eww_state}; +use anyhow::*; +use glib; +use itertools::Itertools; +use scheduled_executor; +pub struct ScriptVarHandler { + evt_send: glib::Sender, + pub poll_handles: Vec, + pub poll_executor: scheduled_executor::CoreExecutor, +} + +impl ScriptVarHandler { + pub fn new(evt_send: glib::Sender) -> Result { + log::info!("initializing handler for poll script vars"); + Ok(ScriptVarHandler { + evt_send, + poll_handles: Vec::new(), + poll_executor: scheduled_executor::CoreExecutor::new()?, + }) + } + + /// clears and stops the currently running poll handles, then opens the new ones as configured + pub fn setup_command_poll_tasks(&mut self, config: &config::EwwConfig) -> Result<()> { + log::info!("reloading handler for poll script vars"); + self.poll_handles.iter().for_each(|handle| handle.stop()); + self.poll_handles.clear(); + + let evt_send = self.evt_send.clone(); + self.poll_handles = config + .get_script_vars() + .iter() + .map(|var| { + self.poll_executor.schedule_fixed_interval( + std::time::Duration::from_secs(0), + var.interval, + glib::clone!(@strong var, @strong evt_send => move |_| { + let result = eww_state::run_command(&var.command) + .and_then(|output| Ok(evt_send.send(app::EwwEvent::UpdateVar(var.name.clone(), output))?)); + if let Err(e) = result { + eprintln!("Error while running script-var command: {:?}", e); + } + }), + ) + }) + .collect_vec(); + Ok(()) + } +} diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 3b590a3..e423484 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -21,6 +21,7 @@ struct BuilderArgs<'a, 'b, 'c> { eww_state: &'a mut EwwState, local_env: &'b HashMap, widget: &'c element::WidgetUse, + unhandled_attrs: Vec<&'c str>, } pub fn element_to_gtk_thing( @@ -56,6 +57,7 @@ pub fn build_gtk_widget( eww_state, local_env, widget, + unhandled_attrs: widget.attrs.keys().map(|x| x.as_str()).collect(), }; let gtk_widget = match widget_to_gtk_widget(&mut bargs) { Ok(Some(gtk_widget)) => gtk_widget, @@ -82,6 +84,14 @@ pub fn build_gtk_widget( .map(|w| resolve_orientable_attrs(&mut bargs, &w)); resolve_widget_attrs(&mut bargs, >k_widget); + if !bargs.unhandled_attrs.is_empty() { + eprintln!( + "WARN: Unknown attribute used in {}: {}", + widget.name, + bargs.unhandled_attrs.join(", ") + ) + } + Ok(Some(gtk_widget)) } @@ -91,24 +101,25 @@ macro_rules! resolve { $( $func:ident => $attr:literal $( = $default:literal)? $( = req $(@$required:tt)?)? => |$arg:ident| $body:expr ),+ $(,)? - }) => { + }) => {{ $( resolve!($args, $gtk_widget, $func => $attr $( [ $default ] )* $($($required)*)* => |$arg| $body); )+ - }; + }}; ($args:ident, $gtk_widget:ident, { $($func:ident => { $($attr:literal $(= $default:literal)? $(= req $(@$required:tt)?)? => |$arg:ident| $body:expr),+ $(,)? }),+ $(,)? - }) => { + }) => {{ $($( resolve!($args, $gtk_widget, $func => $attr $( [ $default ] )* $($($required)*)* => |$arg| $body); )+)+ - }; + }}; // optional ($args:ident, $gtk_widget:ident, $func:ident => $attr:literal => |$arg:ident| $body:expr) => { + $args.unhandled_attrs.retain(|a| a != &$attr); if let Some(attr_value) = $args.widget.attrs.get($attr) { $args.eww_state.$func($args.local_env, attr_value, { let $gtk_widget = $gtk_widget.clone(); @@ -119,6 +130,7 @@ macro_rules! resolve { // required ($args:ident, $gtk_widget:ident, $func:ident => $attr:literal req => |$arg:ident| $body:expr) => { + $args.unhandled_attrs.retain(|a| a != &$attr); $args.eww_state.$func($args.local_env, $args.widget.get_attr($attr)?, { let $gtk_widget = $gtk_widget.clone(); move |$arg| { $body; } @@ -127,6 +139,7 @@ macro_rules! resolve { // with default ($args:ident, $gtk_widget:ident, $func:ident => $attr:literal [$default:expr] => |$arg:ident| $body:expr) => { + $args.unhandled_attrs.retain(|a| a != &$attr); $args.eww_state.$func($args.local_env, $args.widget.attrs.get($attr).unwrap_or(&AttrValue::Concrete(PrimitiveValue::from($default))), { let $gtk_widget = $gtk_widget.clone(); move |$arg| { $body; } diff --git a/src/widgets/widget_definitions.rs b/src/widgets/widget_definitions.rs index c56284c..26bf0ac 100644 --- a/src/widgets/widget_definitions.rs +++ b/src/widgets/widget_definitions.rs @@ -16,8 +16,10 @@ pub(super) fn resolve_widget_attrs(bargs: &mut BuilderArgs, gtk_widget: >k::Wi resolve!(bargs, gtk_widget, { resolve_str => "class" => |v| gtk_widget.get_style_context().add_class(&v), resolve_bool => "active" = true => |v| gtk_widget.set_sensitive(v), - resolve_str => "valign" => |v| gtk_widget.set_valign(parse_align(&v)), - resolve_str => "halign" => |v| gtk_widget.set_halign(parse_align(&v)), + resolve_str => "valign" => |v| gtk_widget.set_valign(parse_align(&v)), + resolve_str => "halign" => |v| gtk_widget.set_halign(parse_align(&v)), + resolve_f64 => "width" => |v| gtk_widget.set_size_request(v as i32, gtk_widget.get_allocated_height()), + resolve_f64 => "height" => |v| gtk_widget.set_size_request(gtk_widget.get_allocated_width(), v as i32), }); } @@ -59,6 +61,7 @@ pub(super) fn widget_to_gtk_widget(bargs: &mut BuilderArgs) -> Result build_gtk_button(bargs)?.upcast(), "label" => build_gtk_label(bargs)?.upcast(), "text" => build_gtk_text(bargs)?.upcast(), + "aspect" => build_gtk_aspect_frame(bargs)?.upcast(), _ => return Ok(None), }; Ok(Some(gtk_widget)) @@ -129,6 +132,13 @@ fn build_gtk_text(bargs: &mut BuilderArgs) -> Result { ); Ok(gtk_widget) } + +fn build_gtk_aspect_frame(bargs: &mut BuilderArgs) -> Result { + let gtk_widget = gtk::AspectFrame::new(None, 0.5, 0.5, 1.0, true); + //resolve!(bargs, gtk_widget, {}); + Ok(gtk_widget) +} + fn parse_orientation(o: &str) -> gtk::Orientation { match o { "vertical" | "v" => gtk::Orientation::Vertical,