Ipc cleanup (#18)

* Clean up IPC, remove ipc-channel dependency

* Remove dead socket file on server startup

* Cleanup CLI handling
This commit is contained in:
ElKowar 2020-10-16 22:28:18 +02:00 committed by elkowar
parent 8f02f1fe6b
commit 3d07a84d1b
6 changed files with 99 additions and 124 deletions

52
Cargo.lock generated
View file

@ -298,6 +298,7 @@ name = "eww"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bincode",
"crossbeam-channel", "crossbeam-channel",
"debug_stub_derive", "debug_stub_derive",
"derive_more", "derive_more",
@ -309,7 +310,6 @@ dependencies = [
"grass", "grass",
"gtk", "gtk",
"hotwatch", "hotwatch",
"ipc-channel",
"itertools", "itertools",
"lazy_static", "lazy_static",
"libc", "libc",
@ -830,24 +830,6 @@ dependencies = [
"libc", "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]] [[package]]
name = "itertools" name = "itertools"
version = "0.9.0" version = "0.9.0"
@ -1477,15 +1459,6 @@ version = "0.6.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" 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]] [[package]]
name = "roxmltree" name = "roxmltree"
version = "0.13.0" version = "0.13.0"
@ -1699,20 +1672,6 @@ dependencies = [
"version-compare", "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]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.1.0" version = "1.1.0"
@ -2017,15 +1976,6 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
[[package]]
name = "uuid"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
dependencies = [
"rand",
]
[[package]] [[package]]
name = "vec_map" name = "vec_map"
version = "0.8.2" version = "0.8.2"

View file

@ -12,12 +12,13 @@ glib = { version = "", features = ["v2_44"] }
gdk-pixbuf = "0.9" gdk-pixbuf = "0.9"
regex = "1" regex = "1"
bincode = "1.3"
try_match = "0.2.2" try_match = "0.2.2"
anyhow = "1.0" anyhow = "1.0"
derive_more = "0.99" derive_more = "0.99"
maplit = "1" maplit = "1"
structopt = "0.3" structopt = "0.3"
ipc-channel="0.14.1" #ipc-channel="0.14.1"
serde = {version = "1.0", features = ["derive"]} serde = {version = "1.0", features = ["derive"]}
extend = "0.3.0" extend = "0.3.0"
grass = "0.10" grass = "0.10"
@ -37,5 +38,6 @@ libc = "0.2"
ref-cast = "1.0" ref-cast = "1.0"
popol = "0.3" popol = "0.3"
[dev-dependencies] [dev-dependencies]
pretty_assertions = "0.6.1" pretty_assertions = "0.6.1"

View file

@ -1,56 +1,64 @@
use crate::*; use crate::config::WindowStacking;
use 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 debug_stub_derive::*;
use script_var_handler::*; use gdk::WindowExt;
use gtk::{ContainerExt, CssProviderExt, GtkWindowExt, StyleContextExt, WidgetExt};
use std::collections::HashMap; use std::collections::HashMap;
use value::VarName;
#[derive(Debug)] #[derive(Debug)]
pub enum EwwEvent { pub enum EwwCommand {
UserCommand(Opt),
UpdateVar(VarName, PrimitiveValue), UpdateVar(VarName, PrimitiveValue),
ReloadConfig(config::EwwConfig), ReloadConfig(config::EwwConfig),
ReloadCss(String), ReloadCss(String),
OpenWindow {
window_name: WindowName,
pos: Option<Coords>,
size: Option<Coords>,
},
CloseWindow {
window_name: WindowName,
},
KillServer,
PrintState,
} }
#[derive(DebugStub)] #[derive(DebugStub)]
pub struct App { pub struct App {
pub eww_state: EwwState, pub eww_state: eww_state::EwwState,
pub eww_config: config::EwwConfig, pub eww_config: config::EwwConfig,
pub windows: HashMap<config::WindowName, gtk::Window>, pub windows: HashMap<config::WindowName, gtk::Window>,
pub css_provider: gtk::CssProvider, pub css_provider: gtk::CssProvider,
pub app_evt_send: glib::Sender<EwwEvent>, pub app_evt_send: glib::Sender<EwwCommand>,
#[debug_stub = "ScriptVarHandler(...)"] #[debug_stub = "ScriptVarHandler(...)"]
pub script_var_handler: ScriptVarHandler, pub script_var_handler: ScriptVarHandler,
} }
impl App { impl App {
pub fn handle_user_command(&mut self, opts: &Opt) -> Result<()> { pub fn handle_command(&mut self, event: EwwCommand, response_sender: Option<Sender<String>>) {
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) {
log::debug!("Handling event: {:?}", &event); log::debug!("Handling event: {:?}", &event);
let result: Result<_> = try { let result: Result<_> = match event {
match event { EwwCommand::UpdateVar(key, value) => self.update_state(key, value),
EwwEvent::UserCommand(command) => self.handle_user_command(&command)?, EwwCommand::ReloadConfig(config) => self.reload_all_windows(config),
EwwEvent::UpdateVar(key, value) => self.update_state(key, value)?, EwwCommand::ReloadCss(css) => self.load_css(&css),
EwwEvent::ReloadConfig(config) => self.reload_all_windows(config)?, EwwCommand::KillServer => {
EwwEvent::ReloadCss(css) => self.load_css(&css)?, 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 { 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);
}
} }
} }

View file

@ -12,12 +12,13 @@ use eww_state::*;
use gdk::*; use gdk::*;
use gtk::prelude::*; use gtk::prelude::*;
use hotwatch; use hotwatch;
use ipc_channel::ipc;
use log; use log;
use pretty_env_logger; use pretty_env_logger;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{ use std::{
collections::HashMap, collections::HashMap,
io::{Read, Write},
os::unix::net,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use structopt::StructOpt; 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() { fn main() {
pretty_env_logger::init(); pretty_env_logger::init();
if let Err(e) = try_main() { if let Err(e) = try_main() {
@ -58,9 +71,6 @@ fn main() {
#[derive(StructOpt, Debug, Serialize, Deserialize, PartialEq)] #[derive(StructOpt, Debug, Serialize, Deserialize, PartialEq)]
pub struct Opt { pub struct Opt {
#[structopt(short = "-c", parse(from_os_str))]
config_file: Option<PathBuf>,
#[structopt(subcommand)] #[structopt(subcommand)]
action: OptAction, action: OptAction,
@ -88,17 +98,34 @@ pub enum OptAction {
#[structopt(name = "kill")] #[structopt(name = "kill")]
KillServer, KillServer,
#[structopt(name = "state")]
ShowState,
}
impl Into<app::EwwCommand> 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<()> { fn try_main() -> Result<()> {
let opts: Opt = StructOpt::from_args(); let opts: Opt = StructOpt::from_args();
log::info!("Trying to find server process"); 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"); log::info!("Forwarding options to server");
sender.send(opts)?; stream.write_all(&bincode::serialize(&opts)?)?;
} else { } else {
log::info!("No instance found... Initializing server."); log::info!("No instance found... Initializing server.");
let _ = std::fs::remove_file(&*IPC_SOCKET_PATH);
if opts.should_detach { if opts.should_detach {
do_detach(); do_detach();
} }
@ -108,27 +135,13 @@ fn try_main() -> Result<()> {
Ok(()) Ok(())
} }
fn find_server_process() -> Result<ipc::IpcSender<Opt>> {
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<()> { fn initialize_server(opts: Opt) -> Result<()> {
if opts.action == OptAction::KillServer { if opts.action == OptAction::KillServer {
return Ok(()); 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 let config_dir = config_file_path
.clone()
.parent() .parent()
.context("config file did not have a parent?!")? .context("config file did not have a parent?!")?
.to_owned() .to_owned()
@ -162,13 +175,13 @@ fn initialize_server(opts: Opt) -> Result<()> {
} }
// run the command that eww was started with // 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())?; let _hotwatch = run_filewatch_thread(&config_file_path, &scss_file_path, evt_send.clone())?;
evt_recv.attach(None, move |msg| { evt_recv.attach(None, move |msg| {
app.handle_event(msg); app.handle_command(msg, None);
glib::Continue(true) glib::Continue(true)
}); });
@ -177,16 +190,15 @@ fn initialize_server(opts: Opt) -> Result<()> {
Ok(()) Ok(())
} }
fn run_server_thread(evt_send: glib::Sender<app::EwwEvent>) { fn run_server_thread(evt_send: glib::Sender<app::EwwCommand>) -> Result<()> {
std::thread::spawn(move || { std::thread::spawn(move || {
log::info!("Starting up eww server");
let result: Result<_> = try { let result: Result<_> = try {
loop { log::info!("Starting up eww server");
let (ipc_server, instance_path): (ipc::IpcOneShotServer<Opt>, _) = ipc::IpcOneShotServer::new()?; let listener = net::UnixListener::bind(&*IPC_SOCKET_PATH)?;
std::fs::write("/tmp/eww-instance-path", instance_path)?; for stream in listener.incoming() {
let (_, initial) = ipc_server.accept()?; let command: Opt = bincode::deserialize_from(stream?)?;
log::info!("received command from IPC: {:?}", &initial); log::info!("received command from IPC: {:?}", &command);
evt_send.send(app::EwwEvent::UserCommand(initial))?; evt_send.send(command.action.into())?;
} }
}; };
if let Err(err) = result { if let Err(err) = result {
@ -194,12 +206,13 @@ fn run_server_thread(evt_send: glib::Sender<app::EwwEvent>) {
std::process::exit(1); std::process::exit(1);
} }
}); });
Ok(())
} }
fn run_filewatch_thread<P: AsRef<Path>>( fn run_filewatch_thread<P: AsRef<Path>>(
config_file_path: P, config_file_path: P,
scss_file_path: P, scss_file_path: P,
evt_send: glib::Sender<app::EwwEvent>, evt_send: glib::Sender<app::EwwCommand>,
) -> Result<hotwatch::Hotwatch> { ) -> Result<hotwatch::Hotwatch> {
log::info!("Initializing config file watcher"); log::info!("Initializing config file watcher");
let mut hotwatch = hotwatch::Hotwatch::new()?; let mut hotwatch = hotwatch::Hotwatch::new()?;
@ -209,7 +222,7 @@ fn run_filewatch_thread<P: AsRef<Path>>(
try_logging_errors!("handling change of config file" => { try_logging_errors!("handling change of config file" => {
log::info!("Reloading eww configuration"); log::info!("Reloading eww configuration");
let new_eww_config = config::EwwConfig::read_from_file(path)?; 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<P: AsRef<Path>>(
try_logging_errors!("handling change of scss file" => { try_logging_errors!("handling change of scss file" => {
log::info!("reloading eww css file"); log::info!("reloading eww css file");
let eww_css = util::parse_scss_from_file(path)?; 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 { if let Err(e) = result {

View file

@ -17,14 +17,15 @@ use std::io::BufRead;
/// Handler that manages running and updating [ScriptVar]s /// Handler that manages running and updating [ScriptVar]s
pub struct ScriptVarHandler { pub struct ScriptVarHandler {
evt_send: glib::Sender<app::EwwEvent>, evt_send: glib::Sender<app::EwwCommand>,
pub poll_handles: Vec<scheduled_executor::executor::TaskHandle>, pub poll_handles: Vec<scheduled_executor::executor::TaskHandle>,
pub poll_executor: scheduled_executor::CoreExecutor, pub poll_executor: scheduled_executor::CoreExecutor,
pub tail_handler_thread: Option<stoppable_thread::StoppableHandle<()>>, pub tail_handler_thread: Option<stoppable_thread::StoppableHandle<()>>,
} }
impl ScriptVarHandler { impl ScriptVarHandler {
pub fn new(evt_send: glib::Sender<app::EwwEvent>) -> Result<Self> { pub fn new(evt_send: glib::Sender<app::EwwCommand>) -> Result<Self> {
log::info!("initializing handler for poll script vars");
Ok(ScriptVarHandler { Ok(ScriptVarHandler {
evt_send, evt_send,
poll_handles: Vec::new(), poll_handles: Vec::new(),
@ -73,7 +74,7 @@ impl ScriptVarHandler {
var.interval, var.interval,
glib::clone!(@strong var, @strong evt_send => move |_| { glib::clone!(@strong var, @strong evt_send => move |_| {
let result = eww_state::run_command(&var.command) 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 { if let Err(e) = result {
eprintln!("Error while running script-var command: {:?}", e); 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))?; .with_context(|| format!("No command output handle found for variable '{}'", var_name))?;
let mut buffer = String::new(); let mut buffer = String::new();
handle.read_line(&mut buffer)?; handle.read_line(&mut buffer)?;
evt_send.send(app::EwwEvent::UpdateVar( evt_send.send(app::EwwCommand::UpdateVar(
var_name.clone(), var_name.clone(),
PrimitiveValue::parse_string(&buffer), PrimitiveValue::parse_string(&buffer),
))?; ))?;

View file

@ -14,7 +14,7 @@ pub mod widget_definitions;
const CMD_STRING_PLACEHODLER: &str = "{}"; const CMD_STRING_PLACEHODLER: &str = "{}";
/// Run a command that was provided as an attribute. This command may use a /// 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<T: std::fmt::Display>(cmd: &str, arg: T) { pub fn run_command<T: std::fmt::Display>(cmd: &str, arg: T) {
let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg)); let cmd = cmd.replace(CMD_STRING_PLACEHODLER, &format!("{}", arg));
if let Err(e) = Command::new("/bin/sh").arg("-c").arg(cmd).output() { 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) Ok(gtk_widget)
} }
/// build a [gtk::Widget] out of a [element::WidgetUse] that uses a **builtin /// build a [`gtk::Widget`] out of a [`element::WidgetUse`] that uses a
/// widget**. User defined widgets are handled by [widget_use_to_gtk_widget]. /// **builtin widget**. User defined widgets are handled by
/// [widget_use_to_gtk_widget].
/// ///
/// Also registers all the necessary handlers in the `eww_state`. /// Also registers all the necessary handlers in the `eww_state`.
/// ///