Skip to content

Commit

Permalink
Move to system with WindowInitiator and WindowArguments
Browse files Browse the repository at this point in the history
  • Loading branch information
WilfSilver committed Jul 1, 2023
1 parent 6394aa2 commit 4374f8c
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 171 deletions.
187 changes: 44 additions & 143 deletions crates/eww/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,27 +7,26 @@ use crate::{
paths::EwwPaths,
script_var_handler::ScriptVarHandlerHandle,
state::scope_graph::{ScopeGraph, ScopeIndex},
*,
window_arguments::WindowArguments,
*, window_initiator::WindowInitiator,
};
use anyhow::anyhow;
use codespan_reporting::files::Files;
use eww_shared_util::{AttrName, Span, VarName};
use eww_shared_util::{Span, VarName};
use glib::ObjectExt;
use itertools::Itertools;
use once_cell::sync::Lazy;
use simplexpr::{dynval::DynVal, SimplExpr};
use simplexpr::dynval::DynVal;
use std::{
cell::RefCell,
collections::{HashMap, HashSet},
rc::Rc,
str::FromStr,
};
use tokio::sync::mpsc::UnboundedSender;
use yuck::{
config::{
monitor::MonitorIdentifier,
script_var_definition::ScriptVarDefinition,
window_definition::WindowDefinition,
window_geometry::{AnchorPoint, WindowGeometry},
},
error::DiagError,
Expand Down Expand Up @@ -80,104 +79,6 @@ pub enum DaemonCommand {
PrintWindows(DaemonResponseSender),
}

#[derive(Debug, Clone)]
pub struct WindowInitiator {
pub config_name: String,
pub pos: Option<Coords>,
pub size: Option<Coords>,
pub monitor: Option<MonitorIdentifier>,
pub anchor: Option<AnchorPoint>,
pub args: Vec<(VarName, DynVal)>,
}

impl WindowInitiator {
pub fn new(
config_name: String,
pos: Option<Coords>,
size: Option<Coords>,
monitor: Option<MonitorIdentifier>,
anchor: Option<AnchorPoint>,
args: Vec<(VarName, DynVal)>,
) -> Self {
WindowInitiator { config_name, pos, size, monitor, anchor, args }
}

pub fn new_from_args(config_name: String, mut args: Vec<(VarName, DynVal)>) -> Result<Self> {
let initiator = WindowInitiator {
config_name,
pos: WindowInitiator::extract_value_from_args::<Coords>("pos", &mut args)?,
size: WindowInitiator::extract_value_from_args::<Coords>("size", &mut args)?,
monitor: WindowInitiator::extract_value_from_args::<MonitorIdentifier>("screen", &mut args)?,
anchor: WindowInitiator::extract_value_from_args::<AnchorPoint>("anchor", &mut args)?,
args,
};

Ok(initiator)
}

pub fn extract_value_from_args<T: FromStr>(name: &str, args: &mut Vec<(VarName, DynVal)>) -> Result<Option<T>, T::Err> {
let var_name = name.to_string();
let pos = args.iter().position(|(n, _)| n.0 == var_name);

if pos.is_some() {
let (_, val) = args.remove(pos.unwrap());
let converted_val = T::from_str(&val.0)?;
Ok(Some(converted_val))
} else {
Ok(None)
}
}

pub fn get_local_window_variables(&self, id: &str, window_def: &WindowDefinition) -> Result<HashMap<VarName, DynVal>> {
let expected_args: HashSet<&String> = window_def.expected_args.iter().map(|x| &x.name.0).collect();
let mut local_variables: HashMap<VarName, DynVal> = HashMap::new();

// Inserts these first so they can be overridden
if expected_args.contains(&"id".to_string()) {
local_variables.insert(VarName::from("id"), DynVal::from(id));
}
if self.monitor.is_some() && expected_args.contains(&"screen".to_string()) {
let mon_dyn = match self.monitor.clone().unwrap() {
MonitorIdentifier::Numeric(x) => DynVal::from(x),
MonitorIdentifier::Name(x) => DynVal::from(x),
};
local_variables.insert(VarName::from("screen"), mon_dyn);
}

local_variables.extend(self.args.clone().into_iter());

for attr in &window_def.expected_args {
let name = VarName::from(attr.name.clone());
if !local_variables.contains_key(&name) {
if attr.optional {
local_variables.insert(name, DynVal::from(String::new()));
} else {
return Err(anyhow!(
"Error, {} was required when creating {} but was not given",
attr.name,
self.config_name
));
}
}
}

if local_variables.len() != window_def.expected_args.len() {
let unexpected_vars: Vec<VarName> = local_variables
.iter()
.filter_map(|(n, _)| if !expected_args.contains(&n.0) { Some(n.clone()) } else { None })
.collect();
return Err(anyhow!(
"'{}' {} unexpectedly defined when creating window {}",
unexpected_vars.join(","),
if unexpected_vars.len() == 1 { "was" } else { "were" },
self.config_name
));
}

Ok(local_variables)
}
}

/// An opened window.
#[derive(Debug)]
pub struct EwwWindow {
Expand Down Expand Up @@ -208,7 +109,7 @@ pub struct App<B> {
pub eww_config: config::EwwConfig,
/// Map of all currently open windows
pub open_windows: HashMap<String, EwwWindow>,
pub window_initiators: HashMap<String, WindowInitiator>,
pub window_argumentss: HashMap<String, WindowArguments>,
/// Window names that are supposed to be open, but failed.
/// When reloading the config, these should be opened again.
pub failed_windows: HashSet<String>,
Expand All @@ -228,7 +129,7 @@ impl<B> std::fmt::Debug for App<B> {
.field("eww_config", &self.eww_config)
.field("open_windows", &self.open_windows)
.field("failed_windows", &self.failed_windows)
.field("window_initiators", &self.window_initiators)
.field("window_argumentss", &self.window_argumentss)
.field("paths", &self.paths)
.finish()
}
Expand Down Expand Up @@ -291,14 +192,14 @@ impl<B: DisplayBackend> App<B> {
let window_args: Vec<(VarName, DynVal)> =
args.iter()
.filter_map(|(win_id, n, v)| {
if win_id == "" || win_id == id {
if win_id.is_empty() || win_id == id {
Some((n.clone(), v.clone()))
} else {
None
}
})
.collect();
self.open_window(id, &WindowInitiator::new_from_args(config_name.clone(), window_args)?)
self.open_window(&WindowArguments::new_from_args(id.to_string(), config_name.clone(), window_args)?)
}
})
.filter_map(Result::err);
Expand All @@ -315,14 +216,13 @@ impl<B: DisplayBackend> App<B> {
sender,
args,
} => {
let id = instance_id.unwrap_or(window_name.clone());
let id = instance_id.unwrap_or_else(|| window_name.clone());

let is_open = self.open_windows.contains_key(&id);

let result = if !is_open {
self.open_window(
&id,
&WindowInitiator::new(window_name, pos, size, monitor, anchor, args.unwrap_or(Vec::new())),
&WindowArguments::new(id, window_name, pos, size, monitor, anchor, args.unwrap_or_default()),
)
} else if should_toggle {
self.close_window(&id)
Expand Down Expand Up @@ -443,56 +343,53 @@ impl<B: DisplayBackend> App<B> {
self.script_var_handler.stop_for_variable(unused_var.clone());
}

self.window_initiators.remove(instance_id);
self.window_argumentss.remove(instance_id);

Ok(())
}

fn open_window(&mut self, instance_id: &str, initiator: &WindowInitiator) -> Result<()> {
fn open_window(&mut self, window_args: &WindowArguments) -> Result<()> {
let instance_id = &window_args.id;
self.failed_windows.remove(instance_id);
log::info!("Opening window {} as '{}'", initiator.config_name, instance_id);
log::info!("Opening window {} as '{}'", window_args.config_name, instance_id);

// if an instance of this is already running, close it
if self.open_windows.contains_key(instance_id) {
self.close_window(instance_id)?;
}

self.window_initiators.insert(instance_id.to_string(), initiator.clone());
self.window_argumentss.insert(instance_id.to_string(), window_args.clone());

let open_result: Result<_> = try {
let window_name: &str = &initiator.config_name;
let window_name: &str = &window_args.config_name;

let mut window_def = self.eww_config.get_window(window_name)?.clone();
let window_def = self.eww_config.get_window(window_name)?.clone();
assert_eq!(window_def.name, window_name, "window definition name did not equal the called window");
window_def.geometry =
window_def.geometry.map(|x| x.override_if_given(initiator.anchor, initiator.pos, initiator.size));

let local_variables = initiator.get_local_window_variables(instance_id, &window_def)?;
let initiator = WindowInitiator::new(&window_def, window_args)?;

let root_index = self.scope_graph.borrow().root_index;

let window_scope = self.scope_graph.borrow_mut().register_new_scope(
window_name.to_string(),
Some(root_index),
root_index,
local_variables.iter().map(|(k, v)| (AttrName::from(k.clone()), SimplExpr::Literal(v.clone()))).collect(),
initiator.get_scoped_vars(),
)?;

let root_widget = crate::widgets::build_widget::build_gtk_widget(
&mut self.scope_graph.borrow_mut(),
Rc::new(self.eww_config.get_widget_definitions().clone()),
window_scope,
window_def.widget.clone(),
window_def.widget,
None,
)?;

root_widget.style_context().add_class(&window_name.to_string());
root_widget.style_context().add_class(window_name);

let monitor = initiator.monitor.clone().or(window_def.get_monitor(&local_variables)?);

let monitor_geometry = get_monitor_geometry(monitor.clone())?;
let mut eww_window = initialize_window::<B>(instance_id, monitor_geometry, root_widget, window_def, window_scope)?;
eww_window.gtk_window.style_context().add_class(&window_name.to_string());
let monitor_geometry = get_monitor_geometry(initiator.monitor.clone())?;
let mut eww_window = initialize_window::<B>(&initiator, monitor_geometry, root_widget, window_scope)?;
eww_window.gtk_window.style_context().add_class(window_name);

// initialize script var handlers for variables. As starting a scriptvar with the script_var_handler is idempodent,
// we can just start script vars that are already running without causing issues
Expand Down Expand Up @@ -544,17 +441,17 @@ impl<B: DisplayBackend> App<B> {

let instances: Vec<String> =
self.open_windows.keys().cloned().chain(self.failed_windows.iter().cloned()).dedup().collect();
let initiators = self.window_initiators.clone();
let initiators = self.window_argumentss.clone();
for instance_id in &instances {
let window_initiator;
let window_arguments;
match initiators.get(instance_id) {
Some(x) => window_initiator = x,
Some(x) => window_arguments = x,
None => {
return Err(anyhow!("Cannot reopen window, initial parameters were not saved correctly for {}", instance_id))
}
};

self.open_window(instance_id, window_initiator)?;
self.open_window(window_arguments)?;
}
Ok(())
}
Expand Down Expand Up @@ -583,20 +480,24 @@ impl<B: DisplayBackend> App<B> {
}

fn initialize_window<B: DisplayBackend>(
instance_id: &str,
window_init: &WindowInitiator,
monitor_geometry: gdk::Rectangle,
root_widget: gtk::Widget,
window_def: WindowDefinition,
window_scope: ScopeIndex,
) -> Result<EwwWindow> {
let window = B::initialize_window(&window_def, monitor_geometry)
.with_context(|| format!("monitor {} is unavailable", window_def.monitor.clone().unwrap()))?;

window.set_title(&format!("Eww - {}", window_def.name));
let window = B::initialize_window(window_init, monitor_geometry)
.with_context(
|| format!(
"monitor {} is unavailable",
window_init.monitor.clone().unwrap()
)
)?;

window.set_title(&format!("Eww - {}", window_init.name));
window.set_position(gtk::WindowPosition::None);
window.set_gravity(gdk::Gravity::Center);

if let Some(geometry) = window_def.geometry {
if let Some(geometry) = window_init.geometry {
let actual_window_rect = get_window_rectangle(geometry, monitor_geometry);
window.set_size_request(actual_window_rect.width(), actual_window_rect.height());
window.set_default_size(actual_window_rect.width(), actual_window_rect.height());
Expand All @@ -615,23 +516,23 @@ fn initialize_window<B: DisplayBackend>(

#[cfg(feature = "x11")]
{
if let Some(geometry) = window_def.geometry {
if let Some(geometry) = window_init.geometry {
let _ = apply_window_position(geometry, monitor_geometry, &window);
if window_def.backend_options.x11.window_type != yuck::config::backend_window_options::X11WindowType::Normal {
if window_init.backend_options.x11.window_type != yuck::config::backend_window_options::X11WindowType::Normal {
window.connect_configure_event(move |window, _| {
let _ = apply_window_position(geometry, monitor_geometry, window);
false
});
}
}
display_backend::set_xprops(&window, monitor_geometry, &window_def)?;
display_backend::set_xprops(&window, monitor_geometry, window_init)?;
}

window.show_all();

Ok(EwwWindow {
instance_id: instance_id.to_string(),
name: window_def.name.clone(),
instance_id: window_init.id.clone(),
name: window_init.name.clone(),
gtk_window: window,
scope_index: window_scope,
destroy_event_handler_id: None,
Expand Down
Loading

0 comments on commit 4374f8c

Please sign in to comment.