eww/src/app.rs
2020-10-18 23:25:00 +02:00

170 lines
5.9 KiB
Rust

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<String, gtk::Window>,
pub css_provider: gtk::CssProvider,
#[debug_stub = "script-var poll script handles"]
pub script_var_poll_handles: Vec<scheduled_executor::executor::TaskHandle>,
#[debug_stub = "script-var poll executor"]
pub script_var_poll_executor: scheduled_executor::CoreExecutor,
pub app_evt_send: glib::Sender<EwwEvent>,
}
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: &gtk::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());
}