Refactor app.rs, cleaning up run-while handling
This commit is contained in:
parent
683936c23d
commit
28af00becd
6 changed files with 68 additions and 187 deletions
|
@ -29,6 +29,9 @@ use yuck::{
|
|||
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)]
|
||||
pub enum DaemonCommand {
|
||||
NoOp,
|
||||
|
@ -115,7 +118,7 @@ impl std::fmt::Debug for App {
|
|||
}
|
||||
|
||||
impl App {
|
||||
/// Handle a DaemonCommand event.
|
||||
/// Handle a [DaemonCommand] event.
|
||||
pub fn handle_command(&mut self, event: DaemonCommand) {
|
||||
log::debug!("Handling event: {:?}", &event);
|
||||
let result: Result<_> = try {
|
||||
|
@ -126,7 +129,7 @@ impl App {
|
|||
}
|
||||
DaemonCommand::UpdateVars(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) => {
|
||||
|
@ -146,7 +149,6 @@ impl App {
|
|||
DaemonCommand::KillServer => {
|
||||
log::info!("Received kill command, stopping server!");
|
||||
self.stop_application();
|
||||
let _ = crate::application_lifecycle::send_exit();
|
||||
}
|
||||
DaemonCommand::CloseAll => {
|
||||
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 } => {
|
||||
let is_open = self.open_windows.contains_key(&window_name);
|
||||
let result = if is_open {
|
||||
if should_toggle {
|
||||
let result = if !is_open {
|
||||
self.open_window(&window_name, pos, size, monitor, anchor)
|
||||
} else if should_toggle {
|
||||
self.close_window(&window_name)
|
||||
} else {
|
||||
// user should use `eww reload` to reload windows (https://github.com/elkowar/eww/issues/260)
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
self.open_window(&window_name, pos, size, monitor, anchor)
|
||||
};
|
||||
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) {
|
||||
self.script_var_handler.stop_all();
|
||||
for (_, window) in self.open_windows.drain() {
|
||||
window.close();
|
||||
}
|
||||
gtk::main_quit();
|
||||
let _ = crate::application_lifecycle::send_exit();
|
||||
}
|
||||
|
||||
fn update_global_state(&mut self, fieldname: VarName, value: DynVal) {
|
||||
let result = self.scope_graph.borrow_mut().update_global_value(&fieldname, value);
|
||||
fn update_global_variable(&mut self, name: VarName, value: DynVal) {
|
||||
let result = self.scope_graph.borrow_mut().update_global_value(&name, value);
|
||||
if let Err(err) = result {
|
||||
error_handling_ctx::print_error(err);
|
||||
}
|
||||
|
||||
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| {
|
||||
self.apply_run_while_expressions_mentioning(&name);
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
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)
|
||||
.map(|v| v.as_bool());
|
||||
match run_while {
|
||||
match run_while_result {
|
||||
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)),
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,7 +288,7 @@ impl App {
|
|||
|
||||
let unused_variables = self.scope_graph.borrow().currently_unused_globals();
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ use yuck::{
|
|||
|
||||
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;
|
||||
|
||||
|
@ -30,8 +30,8 @@ pub struct EwwConfig {
|
|||
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>>,
|
||||
// map of variables to all pollvars which refer to them in their run-while-expression
|
||||
run_while_mentions: HashMap<VarName, Vec<VarName>>,
|
||||
}
|
||||
|
||||
impl Default for EwwConfig {
|
||||
|
@ -41,7 +41,7 @@ impl Default for EwwConfig {
|
|||
windows: HashMap::new(),
|
||||
initial_variables: 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());
|
||||
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()))
|
||||
});
|
||||
let mut run_while_mentions = HashMap::<VarName, Vec<VarName>>::new();
|
||||
for var in script_vars.values() {
|
||||
if let ScriptVarDefinition::Poll(var) = var {
|
||||
for name in var.run_while_expr.collect_var_refs() {
|
||||
run_while_mentions.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,
|
||||
run_while_mentions,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -128,7 +127,8 @@ impl EwwConfig {
|
|||
&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))
|
||||
/// Given a variable name, get the names of all variables that reference that variable in their run-while (active/inactive) state
|
||||
pub fn get_run_while_mentions_of(&self, name: &VarName) -> Option<&Vec<VarName>> {
|
||||
self.run_while_mentions.get(name)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ macro_rules! define_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,
|
||||
|
|
|
@ -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 itertools::Itertools;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::error_handling_ctx;
|
||||
|
||||
|
@ -12,10 +19,10 @@ pub enum DaemonResponse {
|
|||
}
|
||||
|
||||
#[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>) {
|
||||
let (sender, recv) = tokio::sync::mpsc::unbounded_channel();
|
||||
pub fn create_pair() -> (DaemonResponseSender, mpsc::UnboundedReceiver<DaemonResponse>) {
|
||||
let (sender, recv) = mpsc::unbounded_channel();
|
||||
(DaemonResponseSender(sender), recv)
|
||||
}
|
||||
|
||||
|
@ -56,4 +63,4 @@ impl DaemonResponseSender {
|
|||
}
|
||||
}
|
||||
|
||||
pub type DaemonResponseReceiver = tokio::sync::mpsc::UnboundedReceiver<DaemonResponse>;
|
||||
pub type DaemonResponseReceiver = mpsc::UnboundedReceiver<DaemonResponse>;
|
||||
|
|
|
@ -56,7 +56,6 @@ pub enum VarSource {
|
|||
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,
|
||||
|
@ -76,14 +75,12 @@ impl FromAstElementContent for PollScriptVar {
|
|||
|
||||
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,
|
||||
|
|
134
mf:w
134
mf:w
|
@ -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))
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue