use crate::*; use debug_stub_derive::*; use std::collections::HashMap; #[derive(Debug)] pub enum EwwEvent { UserCommand(Opt), UpdateVar(String, PrimitiveValue), ReloadConfig(config::EwwConfig), ReloadCss(String), } #[derive(DebugStub)] pub struct App { pub eww_state: EwwState, pub eww_config: config::EwwConfig, pub windows: HashMap, pub css_provider: gtk::CssProvider, #[debug_stub = "script-var poll script handles"] pub script_var_poll_handles: Vec, #[debug_stub = "script-var poll executor"] pub script_var_poll_executor: scheduled_executor::CoreExecutor, pub app_evt_send: glib::Sender, } impl App { pub fn handle_user_command(&mut self, opts: Opt) -> Result<()> { match opts.action { OptAction::Update { fieldname, value } => self.update_state(fieldname, value), OptAction::OpenWindow { window_name } => self.open_window(&window_name)?, OptAction::CloseWindow { window_name } => self.close_window(&window_name)?, } Ok(()) } pub fn handle_event(&mut self, event: EwwEvent) { let result: Result<_> = try { match event { EwwEvent::UserCommand(command) => self.handle_user_command(command)?, EwwEvent::UpdateVar(key, value) => self.update_state(key, value), EwwEvent::ReloadConfig(config) => self.reload_all_windows(config)?, EwwEvent::ReloadCss(css) => self.load_css(&css)?, } }; if let Err(err) = result { eprintln!("Error while handling event: {:?}", err); } } fn update_state(&mut self, fieldname: String, value: PrimitiveValue) { self.eww_state.update_value(fieldname, value); } fn close_window(&mut self, window_name: &str) -> Result<()> { let window = self .windows .get(window_name) .context(format!("No window with name '{}' is running.", window_name))?; window.close(); Ok(()) } fn open_window(&mut self, window_name: &str) -> Result<()> { let window_def = self .eww_config .get_windows() .get(window_name) .context(format!("No window named '{}' defined", window_name))? .clone(); let window = gtk::Window::new(gtk::WindowType::Popup); window.set_title("Eww"); window.set_wmclass("noswallow", "noswallow"); window.set_type_hint(gdk::WindowTypeHint::Dock); window.set_position(gtk::WindowPosition::Center); window.set_default_size(window_def.size.0, window_def.size.1); window.set_size_request(window_def.size.0, window_def.size.1); window.set_decorated(false); window.set_resizable(false); // run on_screen_changed to set the visual correctly initially. on_screen_changed(&window, None); window.connect_screen_changed(on_screen_changed); let empty_local_state = HashMap::new(); let root_widget = &widgets::element_to_gtk_thing( &self.eww_config.get_widgets(), &mut self.eww_state, &empty_local_state, &window_def.widget, )?; root_widget.get_style_context().add_class(window_name); window.add(root_widget); window.show_all(); let gdk_window = window.get_window().context("couldn't get gdk window from gtk window")?; gdk_window.set_override_redirect(true); gdk_window.move_(window_def.position.0, window_def.position.1); gdk_window.show(); gdk_window.raise(); window.set_keep_above(true); self.windows.insert(window_name.to_string(), window); Ok(()) } pub fn reload_all_windows(&mut self, config: config::EwwConfig) -> Result<()> { // refresh script-var poll stuff self.script_var_poll_handles.iter().for_each(|handle| handle.stop()); self.script_var_poll_handles.clear(); if let Err(e) = self.init_command_poll_tasks() { eprintln!("Error while setting up script-var commands: {:?}", e); } self.eww_config = config; self.eww_state.clear_callbacks(); let windows = self.windows.clone(); for (window_name, window) in windows { window.close(); self.open_window(&window_name)?; } Ok(()) } pub fn load_css(&mut self, css: &str) -> Result<()> { self.css_provider.load_from_data(css.as_bytes())?; Ok(()) } pub fn init_command_poll_tasks(&mut self) -> Result<()> { let evt_send = self.app_evt_send.clone(); self.script_var_poll_handles = self .eww_config .get_script_vars() .iter() .map(|var| { self.script_var_poll_executor.schedule_fixed_interval( std::time::Duration::from_secs(0), var.interval, glib::clone!(@strong var, @strong evt_send => move |_| { let result = eww_state::run_command(&var.command); match result { Ok(value) => { let _ = evt_send.send(app::EwwEvent::UpdateVar(var.name.clone(), value)); } Err(e) => { eprintln!("Error while running script-var command: {:?}", e); } } }), ) }) .collect_vec(); Ok(()) } } fn on_screen_changed(window: >k::Window, _old_screen: Option<&gdk::Screen>) { let visual = window.get_screen().and_then(|screen| { screen .get_rgba_visual() .filter(|_| screen.is_composited()) .or_else(|| screen.get_system_visual()) }); window.set_visual(visual.as_ref()); }