From df5793b2048bfb62113c9ac5f4b71dc9c2405329 Mon Sep 17 00:00:00 2001 From: legendofmiracles <30902201+legendofmiracles@users.noreply.github.com> Date: Tue, 11 May 2021 18:45:29 +0200 Subject: [PATCH] Predefined variables/Magic vars (#117) * basic idea * generates docs * hardcoded the gen script * trying to hide magic vars * eww-state is good now * structure for cpu var is now there * renamed cpu to diskstat, bc lib supports it after all * not going to implement disk IO. go back to this commit, to see a rough idea * removed it * formatting * stopped data race * Update src/config/system_stats/ram.rs Co-authored-by: ElKowar <5300871+elkowar@users.noreply.github.com> * Update src/config/system_stats/disk.rs Co-authored-by: ElKowar <5300871+elkowar@users.noreply.github.com> * Explains macos better * Update battery.rs * Function for each OS when getting battery, a bit cleaner * reworked battery a little * all in one big file * facepalm * cleaner gen script and one huge file for the system stat stuff * merge conflicts * github interface for resolving merge conflicts sucks, this fixes it * Apply suggestions from code review Co-authored-by: ElKowar <5300871+elkowar@users.noreply.github.com> * Update src/config/inbuilt.rs Co-authored-by: ElKowar <5300871+elkowar@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: ElKowar <5300871+elkowar@users.noreply.github.com> * code suggestions * component temperature is a json struct * newlines in magic vars descriptions * disks is now json, numbers are not wrapped in strings, and more idiomatic code * Update gen-docs.ts Co-authored-by: mlvzk * removes a unneeded heading * more doc updates and EWW_CPU_USAGE is now json * calculates battery total avg and it's a json struct Co-authored-by: ElKowar <5300871+elkowar@users.noreply.github.com> Co-authored-by: mlvzk --- .github/workflows/gh-pages.yml | 2 +- Cargo.lock | 133 ++++++++++++++++++++++----- Cargo.toml | 1 + docs/content/main/magic-vars.md | 13 +++ docs/content/main/widgets.md | 1 + gen-docs.ts | 22 ++++- src/app.rs | 18 ++-- src/config/eww_config.rs | 14 ++- src/config/inbuilt.rs | 48 ++++++++++ src/config/mod.rs | 2 + src/config/script_var.rs | 23 +++-- src/config/system_stats.rs | 153 ++++++++++++++++++++++++++++++++ src/opts.rs | 12 ++- src/util.rs | 17 ++++ src/value/primitive.rs | 17 ++++ 15 files changed, 434 insertions(+), 42 deletions(-) create mode 100644 docs/content/main/magic-vars.md create mode 100644 src/config/inbuilt.rs create mode 100644 src/config/system_stats.rs diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index a3bea5e..6e31f56 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -21,7 +21,7 @@ jobs: uses: denoland/setup-deno@main with: deno-version: "v1.x" - - run: deno run --allow-read gen-docs.ts ./src/widgets/widget_definitions.rs >> ./docs/content/main/widgets.md + - run: deno run --allow-read --allow-write gen-docs.ts ./src/widgets/widget_definitions.rs ./src/config/inbuilt.rs # Build & deploy - name: shalzz/zola-deploy-action diff --git a/Cargo.lock b/Cargo.lock index 64c62b1..2b142b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,7 +71,7 @@ checksum = "db134ba52475c060f3329a8ef0f8786d6b872ed01515d4b79c162e5798da1340" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", ] [[package]] @@ -227,6 +227,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + [[package]] name = "crossbeam-channel" version = "0.5.1" @@ -237,6 +243,30 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52fb27eab85b17fbb9f6fd667089e07d6a2eb8743d02639ee7f6a7a7729c9c94" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + [[package]] name = "crossbeam-utils" version = "0.8.4" @@ -255,7 +285,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d" dependencies = [ "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", ] [[package]] @@ -277,7 +307,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", ] [[package]] @@ -286,6 +316,12 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "dyn-clone" version = "1.0.4" @@ -353,6 +389,7 @@ dependencies = [ "simple-signal", "smart-default", "structopt", + "sysinfo", "tokio", "tokio-stream", "tokio-util", @@ -370,7 +407,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", ] [[package]] @@ -467,7 +504,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", ] [[package]] @@ -693,7 +730,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", ] [[package]] @@ -981,6 +1018,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +[[package]] +name = "memoffset" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d" +dependencies = [ + "autocfg", +] + [[package]] name = "mio" version = "0.7.11" @@ -1273,7 +1319,7 @@ dependencies = [ "proc-macro-hack", "proc-macro2", "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", ] [[package]] @@ -1349,7 +1395,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", "version_check", ] @@ -1464,19 +1510,44 @@ dependencies = [ ] [[package]] -name = "redox_syscall" -version = "0.2.7" +name = "rayon" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85dd92e586f7355c633911e11f77f3d12f04b1b1bd76a198bd34ae3af8341ef2" +checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.5.2" +version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efb2352a0f4d4b128f734b5c44c79ff80117351138733f12f982fe3e2b13343" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ "aho-corasick", "memchr", @@ -1485,9 +1556,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.24" +version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00efb87459ba4f6fb2169d20f68565555688e1250ee6825cdf6254f8b48fafb2" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "roxmltree" @@ -1536,7 +1607,7 @@ checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", ] [[package]] @@ -1595,7 +1666,7 @@ checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", ] [[package]] @@ -1631,7 +1702,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", ] [[package]] @@ -1649,7 +1720,7 @@ dependencies = [ "heck", "proc-macro2", "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", ] [[package]] @@ -1665,9 +1736,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.71" +version = "1.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad184cc9470f9117b2ac6817bfe297307418819ba40552f9b3846f05c33d5373" +checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" dependencies = [ "proc-macro2", "quote 1.0.9", @@ -1683,6 +1754,22 @@ dependencies = [ "unicode-xid 0.0.4", ] +[[package]] +name = "sysinfo" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567e910ef0207be81a4e1bb0491e9a8d9866cf45b20fe1a52c03d347da9ea51b" +dependencies = [ + "cfg-if", + "core-foundation-sys", + "doc-comment", + "libc", + "ntapi", + "once_cell", + "rayon", + "winapi", +] + [[package]] name = "system-deps" version = "1.3.2" @@ -1739,7 +1826,7 @@ checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", ] [[package]] @@ -1770,7 +1857,7 @@ checksum = "caf7b11a536f46a809a8a9f0bb4237020f70ecbf115b842360afb127ea2fda57" dependencies = [ "proc-macro2", "quote 1.0.9", - "syn 1.0.71", + "syn 1.0.72", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 9bf6173..df60452 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -60,6 +60,7 @@ futures-core = "0.3" futures-util = "0.3" tokio-util = "0.6" +sysinfo = "0.16.1" nom = "6.1" dyn-clone = "1.0" diff --git a/docs/content/main/magic-vars.md b/docs/content/main/magic-vars.md new file mode 100644 index 00000000..3c4ae95 --- /dev/null +++ b/docs/content/main/magic-vars.md @@ -0,0 +1,13 @@ ++++ +title = "Magic Variables" +slug = "magic variables documenation" +weight = 3 ++++ + +These are variables that are always there, without you having to import them. + +The delay between the updating variables is always 2s. + + +## Updating magic variables + diff --git a/docs/content/main/widgets.md b/docs/content/main/widgets.md index 8186826..5359a76 100644 --- a/docs/content/main/widgets.md +++ b/docs/content/main/widgets.md @@ -3,3 +3,4 @@ title = "Widgets" slug = "widget documentation" weight = 3 +++ + diff --git a/gen-docs.ts b/gen-docs.ts index f274c09..4359a58 100644 --- a/gen-docs.ts +++ b/gen-docs.ts @@ -12,6 +12,18 @@ interface Widget { isVisible: boolean; } +function parseMagicVariables(data: string) { + const pattern = /^.*\/\/\s*@desc\s*(\w+)\s*-\s*(.*)$/gm; + let output = []; + for (const [_, name, desc] of data.matchAll(pattern)) { + output.push( +`### \`${name}\` +${desc.replaceAll("\\n", "\n\n")} +`); + } + return output.join("\n"); +} + function parseVars(code: string): Record { const VAR_PATTERN = /^.*\/\/+ *@var +(.*?) +- +(.*)$/; const vars: Record = {}; @@ -121,7 +133,7 @@ function printDocs(vars: Record, docs: Record) { .map((x) => printWidget(x)) .map((x) => x.replace(/\$\w+/g, (x) => vars[x.replace("$", "")])) .join("\n\n"); - console.log(output); + return output; } function printWidget(widget: Widget) { @@ -138,7 +150,13 @@ ${widget.props.map((prop) => `- **\`${prop.name}\`**: *\`${prop.type}\`* ${prop. // deno run --allow-read gen-docs.ts ./src/widgets/widget_definitions.ts 2> /dev/null Deno.readTextFile(Deno.args[0]).then(data => { const vars = parseVars(data); - printDocs(vars, parseDocs(data)); + Deno.writeTextFile("docs/content/main/widgets.md", printDocs(vars, parseDocs(data)), {"append": true}); +}).catch(err => { + return console.error(err); +}) + +let magic = Deno.readTextFile(Deno.args[1]).then(data => { + Deno.writeTextFile("docs/content/main/magic-vars.md", parseMagicVariables(data), {"append": true}); }).catch(err => { return console.error(err); }) diff --git a/src/app.rs b/src/app.rs index 4a6b448..aa865d4 100644 --- a/src/app.rs +++ b/src/app.rs @@ -60,7 +60,10 @@ pub enum DaemonCommand { }, KillServer, CloseAll, - PrintState(DaemonResponseSender), + PrintState { + all: bool, + sender: DaemonResponseSender, + }, PrintDebug(DaemonResponseSender), PrintWindows(DaemonResponseSender), } @@ -157,9 +160,15 @@ impl App { let result = self.close_window(&window_name); respond_with_error(sender, result)?; } - DaemonCommand::PrintState(sender) => { - let output = - self.eww_state.get_variables().iter().map(|(key, value)| format!("{}: {}", key, value)).join("\n"); + DaemonCommand::PrintState { all, sender } => { + let vars = self.eww_state.get_variables().iter(); + let output = if all { + vars.map(|(key, value)| format!("{}: {}", key, value)).join("\n") + } else { + vars.filter(|(x, _)| self.eww_state.referenced_vars().any(|var| x == &var)) + .map(|(key, value)| format!("{}: {}", key, value)) + .join("\n") + }; sender.send(DaemonResponse::Success(output)).context("sending response from main thread")? } DaemonCommand::PrintWindows(sender) => { @@ -219,7 +228,6 @@ impl App { ) -> Result<()> { // remove and close existing window with the same name let _ = self.close_window(window_name); - log::info!("Opening window {}", window_name); let mut window_def = self.eww_config.get_window(window_name)?.clone(); diff --git a/src/config/eww_config.rs b/src/config/eww_config.rs index 8a8bb1f..c7e381c 100644 --- a/src/config/eww_config.rs +++ b/src/config/eww_config.rs @@ -90,7 +90,6 @@ impl RawEwwConfig { included_path.display() ); }; - for included_config in includes { for conflict in util::extend_safe(&mut eww_config.widgets, included_config.widgets) { log_conflict("widget", &conflict, &included_config.filepath) @@ -197,6 +196,17 @@ fn parse_variables_block(xml: XmlElement) -> Result<(HashMap, _ => bail!("Illegal element in variables block: {}", node.as_tag_string()), } } + + // Extends the variables with the predefined variables + let inbuilt = crate::config::inbuilt::get_inbuilt_vars(); + for i in util::extend_safe(&mut script_vars, inbuilt) { + eprintln!( + "script-var '{}' defined twice (defined in your config and in the eww included variables)\nHint: don't define any \ + varible like any of these: https://elkowar.github.io/eww/main/magic-variables-documenation/", + i, + ); + } + Ok((normal_vars, script_vars)) } @@ -274,6 +284,6 @@ mod test { assert_eq!(merged_config.widgets.len(), 2); assert_eq!(merged_config.windows.len(), 2); assert_eq!(merged_config.initial_variables.len(), 2); - assert_eq!(merged_config.script_vars.len(), 0); + assert_eq!(merged_config.script_vars.len(), 7); } } diff --git a/src/config/inbuilt.rs b/src/config/inbuilt.rs new file mode 100644 index 00000000..cd9e117 --- /dev/null +++ b/src/config/inbuilt.rs @@ -0,0 +1,48 @@ +use crate::{ + config::{system_stats::*, PollScriptVar, ScriptVar, VarSource}, + value::{PrimVal as PrimitiveValue, VarName}, +}; +use std::{collections::HashMap, time::Duration}; + +macro_rules! builtin_vars { + ($interval:expr, $($name:literal => $fun:expr),*$(,)?) => {{ + maplit::hashmap! { + $( + VarName::from($name) => ScriptVar::Poll(PollScriptVar { + name: VarName::from($name), + command: VarSource::Function($fun), + interval: $interval, + }) + ),* + } + }}} + +pub fn get_inbuilt_vars() -> HashMap { + builtin_vars! {Duration::new(2, 0), + // @desc EWW_TEMPS - Heat of the components in Celcius\nExample: `{{(CPU_TEMPS.core_1 + CPU_TEMPS.core_2) / 2}}` + "EWW_TEMPS" => || Ok(PrimitiveValue::from(cores())), + + // @desc EWW_RAM - The current RAM + Swap usage + "EWW_RAM" => || Ok(PrimitiveValue::from(format!("{:.2}", ram()))), + + // @desc EWW_DISK - Information on on all mounted partitions (Might report inaccurately on some filesystems, like btrfs)\nExample: `{{EWW_DISK["/"]}}` + "EWW_DISK" => || Ok(PrimitiveValue::from(disk())), + + // @desc EWW_BATTERY - Battery capacity in procent of the main battery + "EWW_BATTERY" => || Ok(PrimitiveValue::from( + match get_battery_capacity() { + Err(e) => { + log::error!("Couldn't get the battery capacity: {:?}", e); + "Error: Check `eww log` for more details".to_string() + } + Ok(o) => o, + } + )), + + // @desc EWW_CPU_USAGE - Average CPU usage (all cores) since the last update (No MacOS support) + "EWW_CPU_USAGE" => || Ok(PrimitiveValue::from(get_avg_cpu_usage())), + + // @desc EWW_NET - Bytes up/down on all interfaces + "EWW_NET" => || Ok(PrimitiveValue::from(net())), + } +} diff --git a/src/config/mod.rs b/src/config/mod.rs index d6d20b1..361c337 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -10,7 +10,9 @@ use xml_ext::*; pub mod element; pub mod eww_config; +pub mod inbuilt; pub mod script_var; +pub mod system_stats; pub mod window_definition; pub mod window_geometry; pub mod xml_ext; diff --git a/src/config/script_var.rs b/src/config/script_var.rs index 9eba0b7..c5c235e 100644 --- a/src/config/script_var.rs +++ b/src/config/script_var.rs @@ -6,16 +6,24 @@ use crate::ensure_xml_tag_is; use super::*; +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum VarSource { + Shell(String), + Function(fn() -> Result), +} #[derive(Clone, Debug, PartialEq, Eq)] pub struct PollScriptVar { pub name: VarName, - pub command: String, + pub command: VarSource, pub interval: std::time::Duration, } impl PollScriptVar { pub fn run_once(&self) -> Result { - run_command(&self.command) + match &self.command { + VarSource::Shell(x) => run_command(x), + VarSource::Function(x) => x(), + } } } @@ -41,9 +49,12 @@ impl ScriptVar { pub fn initial_value(&self) -> Result { match self { - ScriptVar::Poll(x) => { - run_command(&x.command).with_context(|| format!("Failed to compute initial value for {}", &self.name())) - } + ScriptVar::Poll(x) => match &x.command { + VarSource::Function(f) => f().with_context(|| format!("Failed to compute initial value for {}", &self.name())), + VarSource::Shell(f) => { + run_command(&f).with_context(|| format!("Failed to compute initial value for {}", &self.name())) + } + }, ScriptVar::Tail(_) => Ok(PrimVal::from_string(String::new())), } } @@ -55,7 +66,7 @@ impl ScriptVar { let command = xml.only_child()?.as_text()?.text(); if let Ok(interval) = xml.attr("interval") { let interval = util::parse_duration(&interval)?; - Ok(ScriptVar::Poll(PollScriptVar { name, command, interval })) + Ok(ScriptVar::Poll(PollScriptVar { name, command: crate::config::VarSource::Shell(command), interval })) } else { Ok(ScriptVar::Tail(TailScriptVar { name, command })) } diff --git a/src/config/system_stats.rs b/src/config/system_stats.rs new file mode 100644 index 00000000..282c3a1 --- /dev/null +++ b/src/config/system_stats.rs @@ -0,0 +1,153 @@ +use crate::util::IterAverage; +use anyhow::*; +use itertools::Itertools; +use lazy_static::lazy_static; +use std::{fs::read_to_string, sync::Mutex}; +use sysinfo::{ComponentExt, DiskExt, NetworkExt, NetworksExt, ProcessorExt, System, SystemExt}; + +lazy_static! { + static ref SYSTEM: Mutex = Mutex::new(System::new()); +} + +pub fn disk() -> String { + let mut c = SYSTEM.lock().unwrap(); + c.refresh_disks_list(); + + format!( + "{{ {} }}", + c.get_disks() + .iter() + .map(|c| format!( + r#""{}": {{"name": {:?}, "total": {}, "free": {}}}"#, + c.get_mount_point().display(), + c.get_name(), + c.get_total_space(), + c.get_available_space(), + )) + .join(",") + ) +} + +pub fn ram() -> f32 { + let mut c = SYSTEM.lock().unwrap(); + c.refresh_memory(); + (c.get_used_memory() as f32 + c.get_used_swap() as f32) / 1_000_000f32 +} + +pub fn cores() -> String { + let mut c = SYSTEM.lock().unwrap(); + c.refresh_components_list(); + c.refresh_components(); + format!( + "{{ {} }}", + c.get_components() + .iter() + .map(|c| format!(r#""{}": {}"#, c.get_label().to_uppercase().replace(" ", "_"), c.get_temperature())) + .join(",") + ) +} + +pub fn get_avg_cpu_usage() -> String { + let mut c = SYSTEM.lock().unwrap(); + c.refresh_cpu(); + let processors = c.get_processors(); + format!( + r#"{{ "cores": [{}], "avg": {} }}"#, + processors + .iter() + .map(|a| format!(r#"{{"core": "{}", "freq": {}, "usage": {}}}"#, a.get_name(), a.get_frequency(), a.get_cpu_usage())) + .join(","), + processors.iter().map(|a| a.get_cpu_usage()).avg() + ) +} + +#[cfg(target_os = "macos")] +pub fn get_battery_capacity() -> Result { + use regex::Regex; + let capacity = String::from_utf8( + std::process::Command::new("pmset") + .args(&["-g", "batt"]) + .output() + .context("\nError while getting the battery value on macos, with `pmset`: ")? + .stdout, + )?; + + // Example output of that command: + // Now drawing from 'Battery Power' + //-InternalBattery-0 (id=11403363) 100%; discharging; (no estimate) present: true + let regex = Regex::new(r"[0-9]*%")?; + let mut number = regex.captures(&capacity).unwrap().get(0).unwrap().as_str().to_string(); + + // Removes the % at the end + number.pop(); + Ok(format!( + "{{ \"BAT0\": {{ \"capacity\": \"{}\", \"status\": \"{}\" }}}}", + number, + capacity.split(";").collect::>()[1] + )) +} + +#[cfg(target_os = "linux")] +pub fn get_battery_capacity() -> Result { + let mut current = 0_f64; + let mut total = 0_f64; + let mut json = String::from('{'); + for i in + std::path::Path::new("/sys/class/power_supply").read_dir().context("Couldn't read /sys/class/power_supply directory")? + { + let i = i?.path(); + if i.is_dir() { + // some ugly hack because if let Some(a) = a && Some(b) = b doesn't work yet + if let (Ok(o), Ok(s)) = (read_to_string(i.join("capacity")), read_to_string(i.join("status"))) { + json.push_str(&format!( + r#"{:?}: {{ "status": "{}", "capacity": {} }},"#, + i.file_name().context("couldn't convert file name to rust string")?, + s.replace("\n", ""), + o.replace("\n", "") + )); + if let (Ok(t), Ok(c), Ok(v)) = ( + read_to_string(i.join("charge_full")), + read_to_string(i.join("charge_now")), + read_to_string(i.join("voltage_now")), + ) { + // (uAh / 1000000) * U = p and that / one million so that we have microwatt + current += + ((c.replace("\n", "").parse::()? / 1000000_f64) * v.replace("\n", "").parse::()?) / 1000000_f64; + total += + ((t.replace("\n", "").parse::()? / 1000000_f64) * v.replace("\n", "").parse::()?) / 1000000_f64; + } else if let (Ok(t), Ok(c)) = (read_to_string(i.join("energy_full")), read_to_string(i.join("energy_now"))) { + current += c.replace("\n", "").parse::()?; + total += t.replace("\n", "").parse::()?; + } else { + log::warn!( + "Failed to get/calculate uWh: the total_avg value of the battery magic var will probably be a garbage \ + value that can not be trusted." + ); + } + } + } + } + json.pop(); + json.push_str(&format!(r#", "total_avg": {:.1}}}"#, (current / total) * 100_f64)); + + Ok(json) +} + +#[cfg(not(target_os = "macos"))] +#[cfg(not(target_os = "linux"))] +pub fn get_battery_capacity() -> Result { + anyhow!("eww doesn't support your OS for getting the battery capacity") +} + +pub fn net() -> String { + let mut c = SYSTEM.lock().unwrap(); + let interfaces = format!( + "{{ {} }}", + &c.get_networks() + .iter() + .map(|a| format!(r#""{}": {{ "NET_UP": {}, "NET_DOWN": {} }}"#, a.0, a.1.get_transmitted(), a.1.get_received())) + .join(","), + ); + c.refresh_networks_list(); + interfaces +} diff --git a/src/opts.rs b/src/opts.rs index bd6e3be..5e84c99 100644 --- a/src/opts.rs +++ b/src/opts.rs @@ -108,9 +108,13 @@ pub enum ActionWithServer { #[structopt(name = "close-all", alias = "ca")] CloseAll, - /// Print the current eww-state + /// Prints the variables used in all currently open window #[structopt(name = "state")] - ShowState, + ShowState { + /// Shows all variables, including not currently used ones + #[structopt(short, long)] + all: bool, + }, /// Print the names of all configured windows. Windows with a * in front of them are currently opened. #[structopt(name = "windows")] @@ -175,7 +179,9 @@ impl ActionWithServer { } ActionWithServer::Reload => return with_response_channel(app::DaemonCommand::ReloadConfigAndCss), ActionWithServer::ShowWindows => return with_response_channel(app::DaemonCommand::PrintWindows), - ActionWithServer::ShowState => return with_response_channel(app::DaemonCommand::PrintState), + ActionWithServer::ShowState { all } => { + return with_response_channel(|sender| app::DaemonCommand::PrintState { all, sender }) + } ActionWithServer::ShowDebug => return with_response_channel(app::DaemonCommand::PrintDebug), }; (command, None) diff --git a/src/util.rs b/src/util.rs index 2a78896..103006f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -112,6 +112,23 @@ pub fn parse_duration(s: &str) -> Result { } } + +pub trait IterAverage { + fn avg(self) -> f32; +} + +impl> IterAverage for I { + fn avg(self) -> f32 { + let mut total = 0f32; + let mut cnt = 0f32; + for value in self { + total += value; + cnt += 1f32; + } + total / cnt + } +} + /// Replace all env-var references of the format `"something ${foo}"` in a string /// by the actual env-variables. If the env-var isn't found, will replace the /// reference with an empty string. diff --git a/src/value/primitive.rs b/src/value/primitive.rs index 1dbbf4f..98a771c 100644 --- a/src/value/primitive.rs +++ b/src/value/primitive.rs @@ -65,6 +65,23 @@ impl From for PrimVal { } } +impl From for PrimVal { + fn from(s: u32) -> Self { + PrimVal(s.to_string()) + } +} + +impl From for PrimVal { + fn from(s: f32) -> Self { + PrimVal(s.to_string()) + } +} + +impl From for PrimVal { + fn from(s: u8) -> Self { + PrimVal(s.to_string()) + } +} impl From for PrimVal { fn from(s: f64) -> Self { PrimVal(s.to_string())