From 3d07a84d1babac2c28f3ee9cbaa9ed3f1eb70b03 Mon Sep 17 00:00:00 2001 From: ElKowar <5300871+elkowar@users.noreply.github.com> Date: Fri, 16 Oct 2020 22:28:18 +0200 Subject: [PATCH] Ipc cleanup (#18) * Clean up IPC, remove ipc-channel dependency * Remove dead socket file on server startup * Cleanup CLI handling --- Cargo.lock | 52 +----------------------- Cargo.toml | 4 +- src/app.rs | 68 ++++++++++++++++++-------------- src/main.rs | 83 ++++++++++++++++++++++----------------- src/script_var_handler.rs | 9 +++-- src/widgets/mod.rs | 7 ++-- 6 files changed, 99 insertions(+), 124 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 491a809..4cf11b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -298,6 +298,7 @@ name = "eww" version = "0.1.0" dependencies = [ "anyhow", + "bincode", "crossbeam-channel", "debug_stub_derive", "derive_more", @@ -309,7 +310,6 @@ dependencies = [ "grass", "gtk", "hotwatch", - "ipc-channel", "itertools", "lazy_static", "libc", @@ -830,24 +830,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ipc-channel" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3698b8affd5656032a074a7d40b3c2a29b71971f3e1ff6042b9d40724e20d97c" -dependencies = [ - "bincode", - "crossbeam-channel", - "fnv", - "lazy_static", - "libc", - "mio", - "rand", - "serde", - "tempfile", - "uuid", -] - [[package]] name = "itertools" version = "0.9.0" @@ -1477,15 +1459,6 @@ version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "roxmltree" version = "0.13.0" @@ -1699,20 +1672,6 @@ dependencies = [ "version-compare", ] -[[package]] -name = "tempfile" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" -dependencies = [ - "cfg-if", - "libc", - "rand", - "redox_syscall", - "remove_dir_all", - "winapi 0.3.9", -] - [[package]] name = "termcolor" version = "1.1.0" @@ -2017,15 +1976,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" -[[package]] -name = "uuid" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" -dependencies = [ - "rand", -] - [[package]] name = "vec_map" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index 5c4deeb..1c225d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,12 +12,13 @@ glib = { version = "", features = ["v2_44"] } gdk-pixbuf = "0.9" regex = "1" +bincode = "1.3" try_match = "0.2.2" anyhow = "1.0" derive_more = "0.99" maplit = "1" structopt = "0.3" -ipc-channel="0.14.1" +#ipc-channel="0.14.1" serde = {version = "1.0", features = ["derive"]} extend = "0.3.0" grass = "0.10" @@ -37,5 +38,6 @@ libc = "0.2" ref-cast = "1.0" popol = "0.3" + [dev-dependencies] pretty_assertions = "0.6.1" diff --git a/src/app.rs b/src/app.rs index df039d8..eccd892 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,56 +1,64 @@ -use crate::*; -use config::WindowStacking; +use crate::config::WindowStacking; +use crate::{config, script_var_handler::*, util, widgets}; +use crate::{config::WindowName, util::Coords, value::PrimitiveValue}; +use crate::{eww_state, value::VarName}; +use anyhow::*; +use crossbeam_channel::Sender; use debug_stub_derive::*; -use script_var_handler::*; +use gdk::WindowExt; +use gtk::{ContainerExt, CssProviderExt, GtkWindowExt, StyleContextExt, WidgetExt}; use std::collections::HashMap; -use value::VarName; #[derive(Debug)] -pub enum EwwEvent { - UserCommand(Opt), +pub enum EwwCommand { UpdateVar(VarName, PrimitiveValue), ReloadConfig(config::EwwConfig), ReloadCss(String), + OpenWindow { + window_name: WindowName, + pos: Option, + size: Option, + }, + CloseWindow { + window_name: WindowName, + }, + KillServer, + PrintState, } #[derive(DebugStub)] pub struct App { - pub eww_state: EwwState, + pub eww_state: eww_state::EwwState, pub eww_config: config::EwwConfig, pub windows: HashMap, pub css_provider: gtk::CssProvider, - pub app_evt_send: glib::Sender, + pub app_evt_send: glib::Sender, #[debug_stub = "ScriptVarHandler(...)"] pub script_var_handler: ScriptVarHandler, } impl App { - pub fn handle_user_command(&mut self, opts: &Opt) -> Result<()> { - match &opts.action { - OptAction::Update { fieldname, value } => self.update_state(fieldname.clone(), value.clone())?, - OptAction::OpenWindow { window_name, pos, size } => self.open_window(&window_name, *pos, *size)?, - OptAction::CloseWindow { window_name } => self.close_window(&window_name)?, - OptAction::KillServer => { - log::info!("Received kill command, stopping server!"); - std::process::exit(0) - } - } - - Ok(()) - } - - pub fn handle_event(&mut self, event: EwwEvent) { + pub fn handle_command(&mut self, event: EwwCommand, response_sender: Option>) { log::debug!("Handling event: {:?}", &event); - 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)?, + let result: Result<_> = match event { + EwwCommand::UpdateVar(key, value) => self.update_state(key, value), + EwwCommand::ReloadConfig(config) => self.reload_all_windows(config), + EwwCommand::ReloadCss(css) => self.load_css(&css), + EwwCommand::KillServer => { + log::info!("Received kill command, stopping server!"); + std::process::exit(0); } + EwwCommand::OpenWindow { window_name, pos, size } => self.open_window(&window_name, pos, size), + EwwCommand::CloseWindow { window_name } => self.close_window(&window_name), + EwwCommand::PrintState => Ok(()), }; + if let Err(err) = result { - eprintln!("Error while handling event: {:?}", err); + if let Some(response_sender) = response_sender { + let _ = response_sender.send(format!("Error while handling event: {:?}", err)); + } else { + eprintln!("Error while handling event: {:?}", err); + } } } diff --git a/src/main.rs b/src/main.rs index 63a0f74..3dfb473 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,12 +12,13 @@ use eww_state::*; use gdk::*; use gtk::prelude::*; use hotwatch; -use ipc_channel::ipc; use log; use pretty_env_logger; use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, + io::{Read, Write}, + os::unix::net, path::{Path, PathBuf}, }; use structopt::StructOpt; @@ -49,6 +50,18 @@ macro_rules! try_logging_errors { }}; } +lazy_static::lazy_static! { + static ref IPC_SOCKET_PATH: std::path::PathBuf = std::env::var("XDG_RUNTIME_DIR") + .map(std::path::PathBuf::from) + .unwrap_or_else(|_| std::path::PathBuf::from("/tmp")) + .join("eww-server"); + + static ref CONFIG_DIR: std::path::PathBuf = std::env::var("XDG_CONFIG_HOME") + .map(|v| PathBuf::from(v)) + .unwrap_or_else(|_| PathBuf::from(std::env::var("HOME").unwrap()).join(".config")) + .join("eww"); +} + fn main() { pretty_env_logger::init(); if let Err(e) = try_main() { @@ -58,9 +71,6 @@ fn main() { #[derive(StructOpt, Debug, Serialize, Deserialize, PartialEq)] pub struct Opt { - #[structopt(short = "-c", parse(from_os_str))] - config_file: Option, - #[structopt(subcommand)] action: OptAction, @@ -88,17 +98,34 @@ pub enum OptAction { #[structopt(name = "kill")] KillServer, + + #[structopt(name = "state")] + ShowState, +} + +impl Into for OptAction { + fn into(self) -> app::EwwCommand { + match self { + OptAction::Update { fieldname, value } => app::EwwCommand::UpdateVar(fieldname, value), + OptAction::OpenWindow { window_name, pos, size } => app::EwwCommand::OpenWindow { window_name, pos, size }, + OptAction::CloseWindow { window_name } => app::EwwCommand::CloseWindow { window_name }, + OptAction::KillServer => app::EwwCommand::KillServer, + OptAction::ShowState => unimplemented!(), + } + } } fn try_main() -> Result<()> { let opts: Opt = StructOpt::from_args(); log::info!("Trying to find server process"); - if let Ok(sender) = find_server_process() { + if let Ok(mut stream) = net::UnixStream::connect(&*IPC_SOCKET_PATH) { log::info!("Forwarding options to server"); - sender.send(opts)?; + stream.write_all(&bincode::serialize(&opts)?)?; } else { log::info!("No instance found... Initializing server."); + let _ = std::fs::remove_file(&*IPC_SOCKET_PATH); + if opts.should_detach { do_detach(); } @@ -108,27 +135,13 @@ fn try_main() -> Result<()> { Ok(()) } -fn find_server_process() -> Result> { - let instance_path = std::fs::read_to_string("/tmp/eww-instance-path")?; - Ok(ipc::IpcSender::connect(instance_path)?) -} - -fn get_config_file_path() -> PathBuf { - std::env::var("XDG_CONFIG_HOME") - .map(|v| PathBuf::from(v)) - .unwrap_or_else(|_| PathBuf::from(std::env::var("HOME").unwrap()).join(".config")) - .join("eww") - .join("eww.xml") -} - fn initialize_server(opts: Opt) -> Result<()> { if opts.action == OptAction::KillServer { return Ok(()); } - let config_file_path = opts.config_file.clone().unwrap_or_else(get_config_file_path); + let config_file_path = CONFIG_DIR.join("eww.xml"); let config_dir = config_file_path - .clone() .parent() .context("config file did not have a parent?!")? .to_owned() @@ -162,13 +175,13 @@ fn initialize_server(opts: Opt) -> Result<()> { } // run the command that eww was started with - app.handle_user_command(&opts)?; + app.handle_command(opts.action.into(), None); - run_server_thread(evt_send.clone()); + run_server_thread(evt_send.clone())?; let _hotwatch = run_filewatch_thread(&config_file_path, &scss_file_path, evt_send.clone())?; evt_recv.attach(None, move |msg| { - app.handle_event(msg); + app.handle_command(msg, None); glib::Continue(true) }); @@ -177,16 +190,15 @@ fn initialize_server(opts: Opt) -> Result<()> { Ok(()) } -fn run_server_thread(evt_send: glib::Sender) { +fn run_server_thread(evt_send: glib::Sender) -> Result<()> { std::thread::spawn(move || { - log::info!("Starting up eww server"); let result: Result<_> = try { - loop { - let (ipc_server, instance_path): (ipc::IpcOneShotServer, _) = ipc::IpcOneShotServer::new()?; - std::fs::write("/tmp/eww-instance-path", instance_path)?; - let (_, initial) = ipc_server.accept()?; - log::info!("received command from IPC: {:?}", &initial); - evt_send.send(app::EwwEvent::UserCommand(initial))?; + log::info!("Starting up eww server"); + let listener = net::UnixListener::bind(&*IPC_SOCKET_PATH)?; + for stream in listener.incoming() { + let command: Opt = bincode::deserialize_from(stream?)?; + log::info!("received command from IPC: {:?}", &command); + evt_send.send(command.action.into())?; } }; if let Err(err) = result { @@ -194,12 +206,13 @@ fn run_server_thread(evt_send: glib::Sender) { std::process::exit(1); } }); + Ok(()) } fn run_filewatch_thread>( config_file_path: P, scss_file_path: P, - evt_send: glib::Sender, + evt_send: glib::Sender, ) -> Result { log::info!("Initializing config file watcher"); let mut hotwatch = hotwatch::Hotwatch::new()?; @@ -209,7 +222,7 @@ fn run_filewatch_thread>( try_logging_errors!("handling change of config file" => { log::info!("Reloading eww configuration"); let new_eww_config = config::EwwConfig::read_from_file(path)?; - config_file_change_send.send(app::EwwEvent::ReloadConfig(new_eww_config))?; + config_file_change_send.send(app::EwwCommand::ReloadConfig(new_eww_config))?; }); })?; @@ -217,7 +230,7 @@ fn run_filewatch_thread>( try_logging_errors!("handling change of scss file" => { log::info!("reloading eww css file"); let eww_css = util::parse_scss_from_file(path)?; - evt_send.send(app::EwwEvent::ReloadCss(eww_css))?; + evt_send.send(app::EwwCommand::ReloadCss(eww_css))?; }) }); if let Err(e) = result { diff --git a/src/script_var_handler.rs b/src/script_var_handler.rs index 024ae7a..8ea238e 100644 --- a/src/script_var_handler.rs +++ b/src/script_var_handler.rs @@ -17,14 +17,15 @@ use std::io::BufRead; /// Handler that manages running and updating [ScriptVar]s pub struct ScriptVarHandler { - evt_send: glib::Sender, + evt_send: glib::Sender, pub poll_handles: Vec, pub poll_executor: scheduled_executor::CoreExecutor, pub tail_handler_thread: Option>, } impl ScriptVarHandler { - pub fn new(evt_send: glib::Sender) -> Result { + pub fn new(evt_send: glib::Sender) -> Result { + log::info!("initializing handler for poll script vars"); Ok(ScriptVarHandler { evt_send, poll_handles: Vec::new(), @@ -73,7 +74,7 @@ impl ScriptVarHandler { var.interval, glib::clone!(@strong var, @strong evt_send => move |_| { let result = eww_state::run_command(&var.command) - .and_then(|output| Ok(evt_send.send(app::EwwEvent::UpdateVar(var.name.clone(), output))?)); + .and_then(|output| Ok(evt_send.send(app::EwwCommand::UpdateVar(var.name.clone(), output))?)); if let Err(e) = result { eprintln!("Error while running script-var command: {:?}", e); } @@ -112,7 +113,7 @@ impl ScriptVarHandler { .with_context(|| format!("No command output handle found for variable '{}'", var_name))?; let mut buffer = String::new(); handle.read_line(&mut buffer)?; - evt_send.send(app::EwwEvent::UpdateVar( + evt_send.send(app::EwwCommand::UpdateVar( var_name.clone(), PrimitiveValue::parse_string(&buffer), ))?; diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index e103147..72646bf 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -14,7 +14,7 @@ pub mod widget_definitions; const CMD_STRING_PLACEHODLER: &str = "{}"; /// Run a command that was provided as an attribute. This command may use a -/// placeholder ('{}') which will be replaced by the value provided as [arg] +/// placeholder ('{}') which will be replaced by the value provided as [`arg`] pub fn run_command(cmd: &str, arg: T) { let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg)); if let Err(e) = Command::new("/bin/sh").arg("-c").arg(cmd).output() { @@ -99,8 +99,9 @@ pub fn widget_use_to_gtk_widget( Ok(gtk_widget) } -/// build a [gtk::Widget] out of a [element::WidgetUse] that uses a **builtin -/// widget**. User defined widgets are handled by [widget_use_to_gtk_widget]. +/// build a [`gtk::Widget`] out of a [`element::WidgetUse`] that uses a +/// **builtin widget**. User defined widgets are handled by +/// [widget_use_to_gtk_widget]. /// /// Also registers all the necessary handlers in the `eww_state`. ///