Refactor app.rs, cleaning up run-while handling

This commit is contained in:
elkowar 2022-08-29 15:33:27 +02:00
parent 683936c23d
commit 28af00becd
No known key found for this signature in database
GPG key ID: E321AD71B1D1F27F
6 changed files with 68 additions and 187 deletions

View file

@ -29,6 +29,9 @@ use yuck::{
value::Coords, value::Coords,
}; };
/// A command for the eww daemon.
/// While these are mostly generated from eww CLI commands (see [`opts::ActionWithServer`]),
/// they may also be generated from other places internally.
#[derive(Debug)] #[derive(Debug)]
pub enum DaemonCommand { pub enum DaemonCommand {
NoOp, NoOp,
@ -115,7 +118,7 @@ impl std::fmt::Debug for App {
} }
impl App { impl App {
/// Handle a DaemonCommand event. /// Handle a [DaemonCommand] event.
pub fn handle_command(&mut self, event: DaemonCommand) { pub fn handle_command(&mut self, event: DaemonCommand) {
log::debug!("Handling event: {:?}", &event); log::debug!("Handling event: {:?}", &event);
let result: Result<_> = try { let result: Result<_> = try {
@ -126,7 +129,7 @@ impl App {
} }
DaemonCommand::UpdateVars(mappings) => { DaemonCommand::UpdateVars(mappings) => {
for (var_name, new_value) in mappings { for (var_name, new_value) in mappings {
self.update_global_state(var_name, new_value); self.update_global_variable(var_name, new_value);
} }
} }
DaemonCommand::ReloadConfigAndCss(sender) => { DaemonCommand::ReloadConfigAndCss(sender) => {
@ -146,7 +149,6 @@ impl App {
DaemonCommand::KillServer => { DaemonCommand::KillServer => {
log::info!("Received kill command, stopping server!"); log::info!("Received kill command, stopping server!");
self.stop_application(); self.stop_application();
let _ = crate::application_lifecycle::send_exit();
} }
DaemonCommand::CloseAll => { DaemonCommand::CloseAll => {
log::info!("Received close command, closing all windows"); log::info!("Received close command, closing all windows");
@ -169,15 +171,12 @@ impl App {
} }
DaemonCommand::OpenWindow { window_name, pos, size, anchor, screen: monitor, should_toggle, sender } => { DaemonCommand::OpenWindow { window_name, pos, size, anchor, screen: monitor, should_toggle, sender } => {
let is_open = self.open_windows.contains_key(&window_name); let is_open = self.open_windows.contains_key(&window_name);
let result = if is_open { let result = if !is_open {
if should_toggle { self.open_window(&window_name, pos, size, monitor, anchor)
} else if should_toggle {
self.close_window(&window_name) self.close_window(&window_name)
} else { } else {
// user should use `eww reload` to reload windows (https://github.com/elkowar/eww/issues/260)
Ok(()) Ok(())
}
} else {
self.open_window(&window_name, pos, size, monitor, anchor)
}; };
sender.respond_with_result(result)?; sender.respond_with_result(result)?;
} }
@ -231,35 +230,48 @@ impl App {
} }
} }
/// Fully stop eww:
/// close all windows, stop the script_var_handler, quit the gtk appliaction and send the exit instruction to the lifecycle manager
fn stop_application(&mut self) { fn stop_application(&mut self) {
self.script_var_handler.stop_all(); self.script_var_handler.stop_all();
for (_, window) in self.open_windows.drain() { for (_, window) in self.open_windows.drain() {
window.close(); window.close();
} }
gtk::main_quit(); gtk::main_quit();
let _ = crate::application_lifecycle::send_exit();
} }
fn update_global_state(&mut self, fieldname: VarName, value: DynVal) { fn update_global_variable(&mut self, name: VarName, value: DynVal) {
let result = self.scope_graph.borrow_mut().update_global_value(&fieldname, value); let result = self.scope_graph.borrow_mut().update_global_value(&name, value);
if let Err(err) = result { if let Err(err) = result {
error_handling_ctx::print_error(err); error_handling_ctx::print_error(err);
} }
if let Ok(linked_poll_vars) = self.eww_config.get_poll_var_link(&fieldname) { self.apply_run_while_expressions_mentioning(&name);
linked_poll_vars.iter().filter_map(|name| self.eww_config.get_script_var(name).ok()).for_each(|var| { }
/// Variables may be referenced in defpoll :run-while expressions.
/// Thus, when a variable changes, the run-while conditions of all variables
/// that mention the changed variable need to be reevaluated and reapplied.
fn apply_run_while_expressions_mentioning(&mut self, name: &VarName) {
let mentioning_vars = match self.eww_config.get_run_while_mentions_of(&name) {
Some(x) => x,
None => return,
};
let mentioning_vars = mentioning_vars.iter().filter_map(|name| self.eww_config.get_script_var(name).ok());
for var in mentioning_vars {
if let ScriptVarDefinition::Poll(poll_var) = var { if let ScriptVarDefinition::Poll(poll_var) = var {
let scope_graph = self.scope_graph.borrow(); let scope_graph = self.scope_graph.borrow();
let run_while = scope_graph let run_while_result = scope_graph
.evaluate_simplexpr_in_scope(scope_graph.root_index, &poll_var.run_while_expr) .evaluate_simplexpr_in_scope(scope_graph.root_index, &poll_var.run_while_expr)
.map(|v| v.as_bool()); .map(|v| v.as_bool());
match run_while { match run_while_result {
Ok(Ok(true)) => self.script_var_handler.add(var.clone()), 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(Ok(false)) => self.script_var_handler.stop_for_variable(poll_var.name.clone()),
Ok(Err(err)) => error_handling_ctx::print_error(anyhow!(err)), Ok(Err(err)) => error_handling_ctx::print_error(anyhow!(err)),
Err(err) => error_handling_ctx::print_error(anyhow!(err)), Err(err) => error_handling_ctx::print_error(anyhow!(err)),
}; };
} }
});
} }
} }
@ -276,7 +288,7 @@ impl App {
let unused_variables = self.scope_graph.borrow().currently_unused_globals(); let unused_variables = self.scope_graph.borrow().currently_unused_globals();
for unused_var in unused_variables { for unused_var in unused_variables {
log::debug!("stopping for {}", &unused_var); log::debug!("stopping script-var {}", &unused_var);
self.script_var_handler.stop_for_variable(unused_var.clone()); self.script_var_handler.stop_for_variable(unused_var.clone());
} }

View file

@ -11,7 +11,7 @@ use yuck::{
use simplexpr::dynval::DynVal; use simplexpr::dynval::DynVal;
use crate::{config::inbuilt, error_handling_ctx, widgets::widget_definitions, paths::EwwPaths}; use crate::{config::inbuilt, error_handling_ctx, paths::EwwPaths, widgets::widget_definitions};
use super::script_var; use super::script_var;
@ -30,8 +30,8 @@ pub struct EwwConfig {
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 // map of variables to all pollvars which refer to them in their run-while-expression
poll_var_links: HashMap<VarName, Vec<VarName>>, run_while_mentions: HashMap<VarName, Vec<VarName>>,
} }
impl Default for EwwConfig { impl Default for EwwConfig {
@ -41,7 +41,7 @@ impl Default for EwwConfig {
windows: HashMap::new(), windows: HashMap::new(),
initial_variables: HashMap::new(), initial_variables: HashMap::new(),
script_vars: HashMap::new(), script_vars: HashMap::new(),
poll_var_links: HashMap::new(), run_while_mentions: HashMap::new(),
} }
} }
} }
@ -76,22 +76,21 @@ impl EwwConfig {
script_vars.extend(inbuilt::get_inbuilt_vars()); script_vars.extend(inbuilt::get_inbuilt_vars());
var_definitions.extend(inbuilt::get_magic_constants(eww_paths)); var_definitions.extend(inbuilt::get_magic_constants(eww_paths));
let mut poll_var_links = HashMap::<VarName, Vec<VarName>>::new(); let mut run_while_mentions = HashMap::<VarName, Vec<VarName>>::new();
script_vars for var in script_vars.values() {
.iter() if let ScriptVarDefinition::Poll(var) = var {
.filter_map(|(_, var)| if let ScriptVarDefinition::Poll(poll_var) = var { Some(poll_var) } else { None }) for name in var.run_while_expr.collect_var_refs() {
.for_each(|var| { run_while_mentions.entry(name.clone()).or_default().push(var.name.clone())
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,
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, run_while_mentions,
}) })
} }
@ -128,7 +127,8 @@ impl EwwConfig {
&self.widgets &self.widgets
} }
pub fn get_poll_var_link(&self, name: &VarName) -> Result<&Vec<VarName>> { /// Given a variable name, get the names of all variables that reference that variable in their run-while (active/inactive) state
self.poll_var_links.get(name).with_context(|| format!("{} does not links to any poll variable", name.0)) pub fn get_run_while_mentions_of(&self, name: &VarName) -> Option<&Vec<VarName>> {
self.run_while_mentions.get(name)
} }
} }

View file

@ -18,7 +18,6 @@ macro_rules! define_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_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,

View file

@ -1,5 +1,12 @@
//! Types to manage messages that notify the eww client over the result of a command
//!
//! Communcation between the daemon and eww client happens via IPC.
//! If the daemon needs to send messages back to the client as a response to a command (mostly for CLI output),
//! this happens via the DaemonResponse types
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use itertools::Itertools; use itertools::Itertools;
use tokio::sync::mpsc;
use crate::error_handling_ctx; use crate::error_handling_ctx;
@ -12,10 +19,10 @@ pub enum DaemonResponse {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct DaemonResponseSender(tokio::sync::mpsc::UnboundedSender<DaemonResponse>); pub struct DaemonResponseSender(mpsc::UnboundedSender<DaemonResponse>);
pub fn create_pair() -> (DaemonResponseSender, tokio::sync::mpsc::UnboundedReceiver<DaemonResponse>) { pub fn create_pair() -> (DaemonResponseSender, mpsc::UnboundedReceiver<DaemonResponse>) {
let (sender, recv) = tokio::sync::mpsc::unbounded_channel(); let (sender, recv) = mpsc::unbounded_channel();
(DaemonResponseSender(sender), recv) (DaemonResponseSender(sender), recv)
} }
@ -56,4 +63,4 @@ impl DaemonResponseSender {
} }
} }
pub type DaemonResponseReceiver = tokio::sync::mpsc::UnboundedReceiver<DaemonResponse>; pub type DaemonResponseReceiver = mpsc::UnboundedReceiver<DaemonResponse>;

View file

@ -56,7 +56,6 @@ pub enum VarSource {
pub struct PollScriptVar { pub struct PollScriptVar {
pub name: VarName, pub name: VarName,
pub run_while_expr: SimplExpr, 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,
@ -76,14 +75,12 @@ impl FromAstElementContent for PollScriptVar {
let run_while_expr = let run_while_expr =
attrs.ast_optional::<SimplExpr>("run-while")?.unwrap_or_else(|| SimplExpr::Literal(DynVal::from(true))); 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_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,

134
mf:w
View file

@ -1,134 +0,0 @@
use anyhow::{bail, Context, Result};
use eww_shared_util::VarName;
use std::collections::HashMap;
use yuck::{
config::{
file_provider::YuckFiles, script_var_definition::ScriptVarDefinition, validate::ValidationError,
widget_definition::WidgetDefinition, window_definition::WindowDefinition, Config,
},
error::AstError,
};
use simplexpr::dynval::DynVal;
use crate::{config::inbuilt, error_handling_ctx, widgets::widget_definitions, paths::EwwPaths};
use super::script_var;
/// Load an [`EwwConfig`] from the config dir of the given [`crate::EwwPaths`],
/// resetting and applying the global YuckFiles object in [`crate::error_handling_ctx`].
pub fn read_from_eww_paths(eww_paths: &EwwPaths) -> Result<EwwConfig> {
error_handling_ctx::clear_files();
EwwConfig::read_from_dir(&mut error_handling_ctx::YUCK_FILES.write().unwrap(), eww_paths)
}
/// Eww configuration structure.
#[derive(Debug, Clone)]
pub struct EwwConfig {
widgets: HashMap<String, WidgetDefinition>,
windows: HashMap<String, WindowDefinition>,
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(),
poll_var_links: HashMap::new(),
}
}
}
impl EwwConfig {
/// Load an [`EwwConfig`] from the config dir of the given [`crate::EwwPaths`], reading the main config file.
pub fn read_from_dir(files: &mut YuckFiles, eww_paths: &EwwPaths) -> Result<Self> {
let yuck_path = eww_paths.get_yuck_path();
if !yuck_path.exists() {
bail!("The configuration file `{}` does not exist", yuck_path.display());
}
let config = Config::generate_from_main_file(files, yuck_path)?;
// run some validations on the configuration
let magic_globals: Vec<_> = inbuilt::INBUILT_VAR_NAMES
.into_iter()
.chain(inbuilt::MAGIC_CONSTANT_NAMES)
.into_iter()
.map(|x| VarName::from(x.clone()))
.collect();
yuck::config::validate::validate(&config, magic_globals)?;
for (name, def) in &config.widget_definitions {
if widget_definitions::BUILTIN_WIDGET_NAMES.contains(&name.as_str()) {
return Err(
AstError::ValidationError(ValidationError::AccidentalBuiltinOverride(def.span, name.to_string())).into()
);
}
}
let Config { widget_definitions, window_definitions, mut var_definitions, mut script_vars } = config;
script_vars.extend(inbuilt::get_inbuilt_vars());
var_definitions.extend(inbuilt::get_magic_constants(eww_paths));
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,
widgets: widget_definitions,
initial_variables: var_definitions.into_iter().map(|(k, v)| (k, v.initial_value)).collect(),
script_vars,
poll_var_links,
})
}
// TODO this is kinda ugly
pub fn generate_initial_state(&self) -> Result<HashMap<VarName, DynVal>> {
let mut vars = self
.script_vars
.iter()
.map(|(name, var)| Ok((name.clone(), script_var::initial_value(var)?)))
.collect::<Result<HashMap<_, _>>>()?;
vars.extend(self.initial_variables.clone());
Ok(vars)
}
pub fn get_windows(&self) -> &HashMap<String, WindowDefinition> {
&self.windows
}
pub fn get_window(&self, name: &str) -> Result<&WindowDefinition> {
self.windows.get(name).with_context(|| {
format!(
"No window named '{}' exists in config.\nThis may also be caused by your config failing to load properly, \
please check for any other errors in that case.",
name
)
})
}
pub fn get_script_var(&self, name: &VarName) -> Result<&ScriptVarDefinition> {
self.script_vars.get(name).with_context(|| format!("No script var named '{}' exists", name))
}
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))
}
}