diff --git a/crates/eww/src/app.rs b/crates/eww/src/app.rs index ea339872..9c8f180d 100644 --- a/crates/eww/src/app.rs +++ b/crates/eww/src/app.rs @@ -41,6 +41,7 @@ pub enum DaemonCommand { }, OpenWindow { window_name: String, + instance_id: Option, pos: Option, size: Option, anchor: Option, @@ -67,9 +68,30 @@ pub enum DaemonCommand { PrintWindows(DaemonResponseSender), } +#[derive(Debug, Clone)] +pub struct WindowInitiator { + pub config_name: String, + pub pos: Option, + pub size: Option, + pub monitor: Option, + pub anchor: Option, +} + +impl WindowInitiator { + pub fn new( + config_name: String, + pos: Option, + size: Option, + monitor: Option, + anchor: Option, + ) -> Self { + WindowInitiator { config_name, pos, size, monitor, anchor } + } +} + #[derive(Debug, Clone)] pub struct EwwWindow { - pub name: String, + pub instance_id: String, pub definition: yuck::config::window_definition::WindowDefinition, pub scope_index: ScopeIndex, pub gtk_window: gtk::Window, @@ -85,6 +107,7 @@ pub struct App { pub scope_graph: Rc>, pub eww_config: config::EwwConfig, pub open_windows: HashMap, + pub window_initiators: HashMap, /// Window names that are supposed to be open, but failed. /// When reloading the config, these should be opened again. pub failed_windows: HashSet, @@ -103,6 +126,7 @@ impl std::fmt::Debug for App { .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("paths", &self.paths) .finish() } @@ -168,23 +192,35 @@ impl App { if should_toggle && self.open_windows.contains_key(w) { self.close_window(w) } else { - self.open_window(w, None, None, None, None) + self.open_window(w, &WindowInitiator::new(w.clone(), None, None, None, None)) } }) .filter_map(Result::err); sender.respond_with_error_list(errors)?; } - DaemonCommand::OpenWindow { window_name, pos, size, anchor, screen: monitor, should_toggle, sender } => { - let is_open = self.open_windows.contains_key(&window_name); + DaemonCommand::OpenWindow { + window_name, + instance_id, + pos, + size, + anchor, + screen: monitor, + should_toggle, + sender, + } => { + let id = instance_id.unwrap_or(window_name.clone()); + + let is_open = self.open_windows.contains_key(&id); + let result = if is_open { if should_toggle { - self.close_window(&window_name) + self.close_window(&id) } 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) + self.open_window(&id, &WindowInitiator::new(window_name, pos, size, monitor, anchor)) }; sender.respond_with_result(result)?; } @@ -279,11 +315,11 @@ impl App { // Due to poll vars not being able to be per window, these do not need to be updated } - fn close_window(&mut self, window_name: &str) -> Result<()> { + fn close_window(&mut self, instance_id: &str) -> Result<()> { let eww_window = self .open_windows - .remove(window_name) - .with_context(|| format!("Tried to close window named '{}', but no such window was open", window_name))?; + .remove(instance_id) + .with_context(|| format!("Tried to close window named '{}', but no such window was open", instance_id))?; self.scope_graph.borrow_mut().remove_scope(eww_window.scope_index); @@ -295,28 +331,26 @@ impl App { self.script_var_handler.stop_for_variable(unused_var.clone()); } + self.window_initiators.remove(instance_id); + Ok(()) } - fn open_window( - &mut self, - window_call: &str, - pos: Option, - size: Option, - monitor: Option, - anchor: Option, - ) -> Result<()> { - self.failed_windows.remove(window_call); - log::info!("Opening window {}", window_call); + fn open_window(&mut self, instance_id: &str, initiator: &WindowInitiator) -> Result<()> { + self.failed_windows.remove(instance_id); + log::info!("Opening window {} as '{}'", initiator.config_name, instance_id); // if an instance of this is already running, close it - let _ = self.close_window(window_call); + let _ = self.close_window(instance_id); + + self.window_initiators.insert(instance_id.to_string(), initiator.clone()); let open_result: Result<_> = try { - let (window_name, params) = extract_window_info(window_call); + let (window_name, params) = extract_window_info(&initiator.config_name); let mut window_def = self.eww_config.get_window(window_name)?.clone(); - window_def.geometry = window_def.geometry.map(|x| x.override_if_given(anchor, pos, size)); + window_def.geometry = + window_def.geometry.map(|x| x.override_if_given(initiator.anchor, initiator.pos, initiator.size)); if params.len() != window_def.expected_args.len() { // Throw error @@ -339,7 +373,7 @@ impl App { )?; let global_window_scope = self.scope_graph.borrow_mut().register_new_scope( - window_call.to_string() + "_global", + instance_id.to_string() + "_global", Some(root_index), root_index, self.eww_config @@ -360,10 +394,11 @@ impl App { root_widget.style_context().add_class(&window_name.to_string()); - let monitor_number = monitor.or(window_def.get_monitor_number(&local_variables)?); + let monitor_number = initiator.monitor.or(window_def.get_monitor_number(&local_variables)?); let monitor_geometry = get_monitor_geometry(monitor_number)?; - let eww_window = initialize_window(monitor_number, monitor_geometry, root_widget, window_def, window_scope)?; + let eww_window = + initialize_window(instance_id, monitor_number, monitor_geometry, root_widget, window_def, window_scope)?; // initialize script var handlers for variables that where not used before opening this window. // TODO maybe this could be handled by having a track_newly_used_variables function in the scope tree? @@ -379,12 +414,12 @@ impl App { let _ = scope_graph_sender.send(ScopeGraphEvent::RemoveScope(eww_window.scope_index)); } }); - self.open_windows.insert(window_call.to_string(), eww_window); + self.open_windows.insert(instance_id.to_string(), eww_window); }; if let Err(err) = open_result { - self.failed_windows.insert(window_call.to_string()); - Err(err).with_context(|| format!("failed to open window `{}`", window_call)) + self.failed_windows.insert(instance_id.to_string()); + Err(err).with_context(|| format!("failed to open window `{}`", instance_id)) } else { Ok(()) } @@ -402,10 +437,19 @@ impl App { self.eww_config = config; self.scope_graph.borrow_mut().clear(self.eww_config.generate_initial_state()?); - let window_names: Vec = + let instances: Vec = self.open_windows.keys().cloned().chain(self.failed_windows.iter().cloned()).dedup().collect(); - for window_name in &window_names { - self.open_window(window_name, None, None, None, None)?; + let initiators = self.window_initiators.clone(); + for instance_id in &instances { + let window_initiator; + match initiators.get(instance_id) { + Some(x) => window_initiator = x, + None => { + return Err(anyhow!("Cannot reopen window, initial parameters were not saved correctly for {}", instance_id)) + } + }; + + self.open_window(instance_id, window_initiator)?; } Ok(()) } @@ -417,6 +461,7 @@ impl App { } fn initialize_window( + instance_id: &str, monitor_number: Option, monitor_geometry: gdk::Rectangle, root_widget: gtk::Widget, @@ -463,7 +508,7 @@ fn initialize_window( window.show_all(); - Ok(EwwWindow { name: window_def.name.clone(), definition: window_def, gtk_window: window, scope_index: window_scope }) + Ok(EwwWindow { instance_id: instance_id.to_string(), definition: window_def, gtk_window: window, scope_index: window_scope }) } /// Apply the provided window-positioning rules to the window. diff --git a/crates/eww/src/opts.rs b/crates/eww/src/opts.rs index 3482cd84..44450a7a 100644 --- a/crates/eww/src/opts.rs +++ b/crates/eww/src/opts.rs @@ -94,6 +94,10 @@ pub enum ActionWithServer { /// Name of the window you want to open. window_name: String, + // The id of the window instance + #[structopt(long)] + id: Option, + /// Monitor-index the window should open on #[structopt(long)] screen: Option, @@ -211,9 +215,10 @@ impl ActionWithServer { ActionWithServer::OpenMany { windows, should_toggle } => { return with_response_channel(|sender| app::DaemonCommand::OpenMany { windows, should_toggle, sender }); } - ActionWithServer::OpenWindow { window_name, pos, size, screen, anchor, should_toggle } => { + ActionWithServer::OpenWindow { window_name, id, pos, size, screen, anchor, should_toggle } => { return with_response_channel(|sender| app::DaemonCommand::OpenWindow { window_name, + instance_id: id, pos, size, anchor, diff --git a/crates/eww/src/server.rs b/crates/eww/src/server.rs index de2006ca..b5aade89 100644 --- a/crates/eww/src/server.rs +++ b/crates/eww/src/server.rs @@ -73,6 +73,7 @@ pub fn initialize_server(paths: EwwPaths, action: Option, should_ eww_config, open_windows: HashMap::new(), failed_windows: HashSet::new(), + window_initiators: HashMap::new(), css_provider: gtk::CssProvider::new(), script_var_handler, app_evt_send: ui_send.clone(),