Implement trigger for defpoll variable (#273)
* Implement trigger for defpoll variable * Rename functions and configuration properties, edit docs to show sample usage, improve nearby fields in docs.
This commit is contained in:
parent
3a486f181e
commit
80b89dd391
8 changed files with 105 additions and 9 deletions
|
@ -12,7 +12,10 @@ use simplexpr::dynval::DynVal;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use tokio::sync::mpsc::UnboundedSender;
|
use tokio::sync::mpsc::UnboundedSender;
|
||||||
use yuck::{
|
use yuck::{
|
||||||
config::window_geometry::{AnchorPoint, WindowGeometry},
|
config::{
|
||||||
|
script_var_definition::ScriptVarDefinition,
|
||||||
|
window_geometry::{AnchorPoint, WindowGeometry},
|
||||||
|
},
|
||||||
value::Coords,
|
value::Coords,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -200,7 +203,20 @@ impl App {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_state(&mut self, fieldname: VarName, value: DynVal) {
|
fn update_state(&mut self, fieldname: VarName, value: DynVal) {
|
||||||
self.eww_state.update_variable(fieldname, value)
|
self.eww_state.update_variable(fieldname.clone(), value);
|
||||||
|
|
||||||
|
if let Ok(linked_poll_vars) = self.eww_config.get_poll_var_link(&fieldname) {
|
||||||
|
linked_poll_vars.iter().filter_map(|name| self.eww_config.get_script_var(name).ok()).for_each(|var| {
|
||||||
|
if let ScriptVarDefinition::Poll(poll_var) = var {
|
||||||
|
match poll_var.run_while_expr.eval(self.eww_state.get_variables()).map(|v| v.as_bool()) {
|
||||||
|
Ok(Ok(true)) => self.script_var_handler.add(var.clone()),
|
||||||
|
Ok(Ok(false)) => self.script_var_handler.stop_for_variable(poll_var.name.clone()),
|
||||||
|
Ok(Err(err)) => error_handling_ctx::print_error(anyhow!(err)),
|
||||||
|
Err(err) => error_handling_ctx::print_error(anyhow!(err)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn close_window(&mut self, window_name: &String) -> Result<()> {
|
fn close_window(&mut self, window_name: &String) -> Result<()> {
|
||||||
|
|
|
@ -24,11 +24,20 @@ pub struct EwwConfig {
|
||||||
windows: HashMap<String, EwwWindowDefinition>,
|
windows: HashMap<String, EwwWindowDefinition>,
|
||||||
initial_variables: HashMap<VarName, DynVal>,
|
initial_variables: HashMap<VarName, DynVal>,
|
||||||
script_vars: HashMap<VarName, ScriptVarDefinition>,
|
script_vars: HashMap<VarName, ScriptVarDefinition>,
|
||||||
|
|
||||||
|
// Links variable which affect state (active/inactive) of poll var to those poll variables
|
||||||
|
poll_var_links: HashMap<VarName, Vec<VarName>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EwwConfig {
|
impl Default for EwwConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { widgets: HashMap::new(), windows: HashMap::new(), initial_variables: HashMap::new(), script_vars: HashMap::new() }
|
Self {
|
||||||
|
widgets: HashMap::new(),
|
||||||
|
windows: HashMap::new(),
|
||||||
|
initial_variables: HashMap::new(),
|
||||||
|
script_vars: HashMap::new(),
|
||||||
|
poll_var_links: HashMap::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +53,17 @@ impl EwwConfig {
|
||||||
|
|
||||||
let Config { widget_definitions, window_definitions, var_definitions, mut script_vars } = config;
|
let Config { widget_definitions, window_definitions, var_definitions, mut script_vars } = config;
|
||||||
script_vars.extend(crate::config::inbuilt::get_inbuilt_vars());
|
script_vars.extend(crate::config::inbuilt::get_inbuilt_vars());
|
||||||
|
|
||||||
|
let mut poll_var_links = HashMap::<VarName, Vec<VarName>>::new();
|
||||||
|
script_vars
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(_, var)| if let ScriptVarDefinition::Poll(poll_var) = var { Some(poll_var) } else { None })
|
||||||
|
.for_each(|var| {
|
||||||
|
var.run_while_var_refs
|
||||||
|
.iter()
|
||||||
|
.for_each(|name| poll_var_links.entry(name.clone()).or_default().push(var.name.clone()))
|
||||||
|
});
|
||||||
|
|
||||||
Ok(EwwConfig {
|
Ok(EwwConfig {
|
||||||
windows: window_definitions
|
windows: window_definitions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -52,6 +72,7 @@ impl EwwConfig {
|
||||||
widgets: widget_definitions,
|
widgets: widget_definitions,
|
||||||
initial_variables: var_definitions.into_iter().map(|(k, v)| (k, v.initial_value)).collect(),
|
initial_variables: var_definitions.into_iter().map(|(k, v)| (k, v.initial_value)).collect(),
|
||||||
script_vars,
|
script_vars,
|
||||||
|
poll_var_links,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,4 +108,8 @@ impl EwwConfig {
|
||||||
pub fn get_widget_definitions(&self) -> &HashMap<String, WidgetDefinition> {
|
pub fn get_widget_definitions(&self) -> &HashMap<String, WidgetDefinition> {
|
||||||
&self.widgets
|
&self.widgets
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_poll_var_link(&self, name: &VarName) -> Result<&Vec<VarName>> {
|
||||||
|
self.poll_var_links.get(name).with_context(|| format!("{} does not links to any poll variable", name.0))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::HashMap, time::Duration};
|
use std::{collections::HashMap, time::Duration};
|
||||||
|
|
||||||
use simplexpr::dynval::DynVal;
|
use simplexpr::{dynval::DynVal, SimplExpr};
|
||||||
use yuck::config::script_var_definition::{PollScriptVar, ScriptVarDefinition, VarSource};
|
use yuck::config::script_var_definition::{PollScriptVar, ScriptVarDefinition, VarSource};
|
||||||
|
|
||||||
use crate::config::system_stats::*;
|
use crate::config::system_stats::*;
|
||||||
|
@ -12,6 +12,8 @@ macro_rules! builtin_vars {
|
||||||
$(
|
$(
|
||||||
VarName::from($name) => ScriptVarDefinition::Poll(PollScriptVar {
|
VarName::from($name) => ScriptVarDefinition::Poll(PollScriptVar {
|
||||||
name: VarName::from($name),
|
name: VarName::from($name),
|
||||||
|
run_while_expr: SimplExpr::Literal(DynVal::from(true)),
|
||||||
|
run_while_var_refs: Vec::new(),
|
||||||
command: VarSource::Function($fun),
|
command: VarSource::Function($fun),
|
||||||
initial_value: None,
|
initial_value: None,
|
||||||
interval: $interval,
|
interval: $interval,
|
||||||
|
|
|
@ -134,6 +134,10 @@ impl PollVarHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn start(&mut self, var: PollScriptVar) {
|
async fn start(&mut self, var: PollScriptVar) {
|
||||||
|
if self.poll_handles.contains_key(&var.name) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
log::debug!("starting poll var {}", &var.name);
|
log::debug!("starting poll var {}", &var.name);
|
||||||
let cancellation_token = CancellationToken::new();
|
let cancellation_token = CancellationToken::new();
|
||||||
self.poll_handles.insert(var.name.clone(), cancellation_token.clone());
|
self.poll_handles.insert(var.name.clone(), cancellation_token.clone());
|
||||||
|
|
|
@ -116,7 +116,6 @@ pub fn parse_scss_from_file(path: &Path) -> Result<String> {
|
||||||
grass::from_string(file_content, &grass_config).map_err(|err| anyhow!("Encountered SCSS parsing error: {:?}", err))
|
grass::from_string(file_content, &grass_config).map_err(|err| anyhow!("Encountered SCSS parsing error: {:?}", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[ext(pub, name = StringExt)]
|
#[ext(pub, name = StringExt)]
|
||||||
impl<T: AsRef<str>> T {
|
impl<T: AsRef<str>> T {
|
||||||
/// check if the string is empty after removing all linebreaks and trimming
|
/// check if the string is empty after removing all linebreaks and trimming
|
||||||
|
|
|
@ -86,6 +86,39 @@ impl SimplExpr {
|
||||||
pub fn synth_literal<T: Into<DynVal>>(s: T) -> Self {
|
pub fn synth_literal<T: Into<DynVal>>(s: T) -> Self {
|
||||||
Self::Literal(s.into())
|
Self::Literal(s.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn collect_var_refs_into(&self, dest: &mut Vec<VarName>) {
|
||||||
|
match self {
|
||||||
|
SimplExpr::VarRef(_, x) => dest.push(x.clone()),
|
||||||
|
SimplExpr::UnaryOp(_, _, x) => x.as_ref().collect_var_refs_into(dest),
|
||||||
|
SimplExpr::BinOp(_, l, _, r) => {
|
||||||
|
l.as_ref().collect_var_refs_into(dest);
|
||||||
|
r.as_ref().collect_var_refs_into(dest);
|
||||||
|
}
|
||||||
|
SimplExpr::IfElse(_, a, b, c) => {
|
||||||
|
a.as_ref().collect_var_refs_into(dest);
|
||||||
|
b.as_ref().collect_var_refs_into(dest);
|
||||||
|
c.as_ref().collect_var_refs_into(dest);
|
||||||
|
}
|
||||||
|
SimplExpr::JsonAccess(_, value, index) => {
|
||||||
|
value.as_ref().collect_var_refs_into(dest);
|
||||||
|
index.as_ref().collect_var_refs_into(dest);
|
||||||
|
}
|
||||||
|
SimplExpr::FunctionCall(_, _, args) => args.iter().for_each(|x: &SimplExpr| x.collect_var_refs_into(dest)),
|
||||||
|
SimplExpr::JsonArray(_, values) => values.iter().for_each(|v| v.collect_var_refs_into(dest)),
|
||||||
|
SimplExpr::JsonObject(_, entries) => entries.iter().for_each(|(k, v)| {
|
||||||
|
k.collect_var_refs_into(dest);
|
||||||
|
v.collect_var_refs_into(dest);
|
||||||
|
}),
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_var_refs(&self) -> Vec<VarName> {
|
||||||
|
let mut dest = Vec::new();
|
||||||
|
self.collect_var_refs_into(&mut dest);
|
||||||
|
dest
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Spanned for SimplExpr {
|
impl Spanned for SimplExpr {
|
||||||
|
|
|
@ -55,6 +55,8 @@ pub enum VarSource {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
|
||||||
pub struct PollScriptVar {
|
pub struct PollScriptVar {
|
||||||
pub name: VarName,
|
pub name: VarName,
|
||||||
|
pub run_while_expr: SimplExpr,
|
||||||
|
pub run_while_var_refs: Vec<VarName>,
|
||||||
pub command: VarSource,
|
pub command: VarSource,
|
||||||
pub initial_value: Option<DynVal>,
|
pub initial_value: Option<DynVal>,
|
||||||
pub interval: std::time::Duration,
|
pub interval: std::time::Duration,
|
||||||
|
@ -71,10 +73,17 @@ impl FromAstElementContent for PollScriptVar {
|
||||||
let initial_value = Some(attrs.primitive_optional("initial")?.unwrap_or_else(|| DynVal::from_string(String::new())));
|
let initial_value = Some(attrs.primitive_optional("initial")?.unwrap_or_else(|| DynVal::from_string(String::new())));
|
||||||
let interval = attrs.primitive_required::<DynVal, _>("interval")?.as_duration()?;
|
let interval = attrs.primitive_required::<DynVal, _>("interval")?.as_duration()?;
|
||||||
let (script_span, script) = iter.expect_literal()?;
|
let (script_span, script) = iter.expect_literal()?;
|
||||||
|
|
||||||
|
let run_while_expr =
|
||||||
|
attrs.ast_optional::<SimplExpr>("run-while")?.unwrap_or_else(|| SimplExpr::Literal(DynVal::from(true)));
|
||||||
|
let run_while_var_refs = run_while_expr.collect_var_refs();
|
||||||
|
|
||||||
iter.expect_done()?;
|
iter.expect_done()?;
|
||||||
Self {
|
Self {
|
||||||
name_span,
|
name_span,
|
||||||
name: VarName(name),
|
name: VarName(name),
|
||||||
|
run_while_expr,
|
||||||
|
run_while_var_refs,
|
||||||
command: VarSource::Shell(script_span, script.to_string()),
|
command: VarSource::Shell(script_span, script.to_string()),
|
||||||
initial_value,
|
initial_value,
|
||||||
interval,
|
interval,
|
||||||
|
|
|
@ -154,8 +154,13 @@ They may also be useful to have buttons within eww change what is shown within y
|
||||||
**Polling variables (`defpoll`)**
|
**Polling variables (`defpoll`)**
|
||||||
|
|
||||||
```lisp
|
```lisp
|
||||||
|
(defvar time-visible false) ; for :run-while property of below variable
|
||||||
|
; when this turns true, the polling starts and
|
||||||
|
; var gets updated with given interval
|
||||||
|
|
||||||
(defpoll time :interval "1s"
|
(defpoll time :interval "1s"
|
||||||
:initial "initial-value" ; setting initial is optional
|
:initial "initial-value" ; optional, defaults to poll at startup
|
||||||
|
:run-while time-visible ; optional, defaults to 'true'
|
||||||
`date +%H:%M:%S`)
|
`date +%H:%M:%S`)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -163,7 +168,7 @@ A polling variable is a variable which runs a provided shell-script repeatedly,
|
||||||
|
|
||||||
This may be the most commonly used type of variable.
|
This may be the most commonly used type of variable.
|
||||||
They are useful to access any quickly retrieved value repeatedly,
|
They are useful to access any quickly retrieved value repeatedly,
|
||||||
and thus are the perfect choice for showing your time, date, as well as other bits of information such as your volume.
|
and thus are the perfect choice for showing your time, date, as well as other bits of information such as pending package updates, weather and battery-level.
|
||||||
|
|
||||||
You can also specify an initial-value, this should prevent eww from waiting for the result of a give command during startup, thus,
|
You can also specify an initial-value, this should prevent eww from waiting for the result of a give command during startup, thus,
|
||||||
making the startup time faster.
|
making the startup time faster.
|
||||||
|
@ -180,9 +185,12 @@ A listening variable runs a script once, and reads its output continously.
|
||||||
Whenever the script outputs a new line, the value will be updated to that new line.
|
Whenever the script outputs a new line, the value will be updated to that new line.
|
||||||
In the example given above, the value of `foo` will start out as `"whatever"`, and will change whenever a new line is appended to `/tmp/some_file`.
|
In the example given above, the value of `foo` will start out as `"whatever"`, and will change whenever a new line is appended to `/tmp/some_file`.
|
||||||
|
|
||||||
These are particularly useful if you have a script that can monitor some value on its own.
|
These are particularly useful when you want to apply changes instantaneously when an operation happens if you have a script
|
||||||
|
that can monitor some value on its own. Volume, brighness, workspaces that get added/removed at runtime,
|
||||||
|
monitoring currently focused desktop/tag, etc. are the most common usecases of this type of variable.
|
||||||
|
These are particularly efficient and should be preffered if possible.
|
||||||
|
|
||||||
For example, the command `xprop -spy -root _NET_CURRENT_DESKTOP` writes the currently focused desktop whenever it changes.
|
For example, the command `xprop -spy -root _NET_CURRENT_DESKTOP` writes the currently focused desktop whenever it changes.
|
||||||
This can be used to implement a workspace widget for a bar, for example.
|
|
||||||
Another example usecase is monitoring the currently playing song with playerctl: `playerctl --follow metadata --format {{title}}`.
|
Another example usecase is monitoring the currently playing song with playerctl: `playerctl --follow metadata --format {{title}}`.
|
||||||
|
|
||||||
**Built-in "magic" variables**
|
**Built-in "magic" variables**
|
||||||
|
|
Loading…
Add table
Reference in a new issue