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 tokio::sync::mpsc::UnboundedSender;
|
||||
use yuck::{
|
||||
config::window_geometry::{AnchorPoint, WindowGeometry},
|
||||
config::{
|
||||
script_var_definition::ScriptVarDefinition,
|
||||
window_geometry::{AnchorPoint, WindowGeometry},
|
||||
},
|
||||
value::Coords,
|
||||
};
|
||||
|
||||
|
@ -200,7 +203,20 @@ impl App {
|
|||
}
|
||||
|
||||
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<()> {
|
||||
|
|
|
@ -24,11 +24,20 @@ pub struct EwwConfig {
|
|||
windows: HashMap<String, EwwWindowDefinition>,
|
||||
initial_variables: HashMap<VarName, DynVal>,
|
||||
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 {
|
||||
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;
|
||||
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 {
|
||||
windows: window_definitions
|
||||
.into_iter()
|
||||
|
@ -52,6 +72,7 @@ impl EwwConfig {
|
|||
widgets: widget_definitions,
|
||||
initial_variables: var_definitions.into_iter().map(|(k, v)| (k, v.initial_value)).collect(),
|
||||
script_vars,
|
||||
poll_var_links,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -87,4 +108,8 @@ impl EwwConfig {
|
|||
pub fn get_widget_definitions(&self) -> &HashMap<String, WidgetDefinition> {
|
||||
&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 simplexpr::dynval::DynVal;
|
||||
use simplexpr::{dynval::DynVal, SimplExpr};
|
||||
use yuck::config::script_var_definition::{PollScriptVar, ScriptVarDefinition, VarSource};
|
||||
|
||||
use crate::config::system_stats::*;
|
||||
|
@ -12,6 +12,8 @@ macro_rules! builtin_vars {
|
|||
$(
|
||||
VarName::from($name) => ScriptVarDefinition::Poll(PollScriptVar {
|
||||
name: VarName::from($name),
|
||||
run_while_expr: SimplExpr::Literal(DynVal::from(true)),
|
||||
run_while_var_refs: Vec::new(),
|
||||
command: VarSource::Function($fun),
|
||||
initial_value: None,
|
||||
interval: $interval,
|
||||
|
|
|
@ -134,6 +134,10 @@ impl PollVarHandler {
|
|||
}
|
||||
|
||||
async fn start(&mut self, var: PollScriptVar) {
|
||||
if self.poll_handles.contains_key(&var.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
log::debug!("starting poll var {}", &var.name);
|
||||
let cancellation_token = CancellationToken::new();
|
||||
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))
|
||||
}
|
||||
|
||||
|
||||
#[ext(pub, name = StringExt)]
|
||||
impl<T: AsRef<str>> T {
|
||||
/// 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 {
|
||||
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 {
|
||||
|
|
|
@ -55,6 +55,8 @@ pub enum VarSource {
|
|||
#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
|
||||
pub struct PollScriptVar {
|
||||
pub name: VarName,
|
||||
pub run_while_expr: SimplExpr,
|
||||
pub run_while_var_refs: Vec<VarName>,
|
||||
pub command: VarSource,
|
||||
pub initial_value: Option<DynVal>,
|
||||
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 interval = attrs.primitive_required::<DynVal, _>("interval")?.as_duration()?;
|
||||
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()?;
|
||||
Self {
|
||||
name_span,
|
||||
name: VarName(name),
|
||||
run_while_expr,
|
||||
run_while_var_refs,
|
||||
command: VarSource::Shell(script_span, script.to_string()),
|
||||
initial_value,
|
||||
interval,
|
||||
|
|
|
@ -154,8 +154,13 @@ They may also be useful to have buttons within eww change what is shown within y
|
|||
**Polling variables (`defpoll`)**
|
||||
|
||||
```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"
|
||||
: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`)
|
||||
```
|
||||
|
||||
|
@ -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.
|
||||
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,
|
||||
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.
|
||||
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.
|
||||
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}}`.
|
||||
|
||||
**Built-in "magic" variables**
|
||||
|
|
Loading…
Add table
Reference in a new issue