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:
parent
8f02f1fe6b
commit
3d07a84d1b
6 changed files with 99 additions and 124 deletions
52
Cargo.lock
generated
52
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
68
src/app.rs
68
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<Coords>,
|
||||
size: Option<Coords>,
|
||||
},
|
||||
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<config::WindowName, gtk::Window>,
|
||||
pub css_provider: gtk::CssProvider,
|
||||
pub app_evt_send: glib::Sender<EwwEvent>,
|
||||
pub app_evt_send: glib::Sender<EwwCommand>,
|
||||
#[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<Sender<String>>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
83
src/main.rs
83
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<PathBuf>,
|
||||
|
||||
#[structopt(subcommand)]
|
||||
action: OptAction,
|
||||
|
||||
|
@ -88,17 +98,34 @@ pub enum OptAction {
|
|||
|
||||
#[structopt(name = "kill")]
|
||||
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<()> {
|
||||
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<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<()> {
|
||||
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<app::EwwEvent>) {
|
||||
fn run_server_thread(evt_send: glib::Sender<app::EwwCommand>) -> Result<()> {
|
||||
std::thread::spawn(move || {
|
||||
log::info!("Starting up eww server");
|
||||
let result: Result<_> = try {
|
||||
loop {
|
||||
let (ipc_server, instance_path): (ipc::IpcOneShotServer<Opt>, _) = 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<app::EwwEvent>) {
|
|||
std::process::exit(1);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_filewatch_thread<P: AsRef<Path>>(
|
||||
config_file_path: P,
|
||||
scss_file_path: P,
|
||||
evt_send: glib::Sender<app::EwwEvent>,
|
||||
evt_send: glib::Sender<app::EwwCommand>,
|
||||
) -> Result<hotwatch::Hotwatch> {
|
||||
log::info!("Initializing config file watcher");
|
||||
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" => {
|
||||
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<P: AsRef<Path>>(
|
|||
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 {
|
||||
|
|
|
@ -17,14 +17,15 @@ use std::io::BufRead;
|
|||
|
||||
/// Handler that manages running and updating [ScriptVar]s
|
||||
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_executor: scheduled_executor::CoreExecutor,
|
||||
pub tail_handler_thread: Option<stoppable_thread::StoppableHandle<()>>,
|
||||
}
|
||||
|
||||
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 {
|
||||
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),
|
||||
))?;
|
||||
|
|
|
@ -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<T: std::fmt::Display>(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`.
|
||||
///
|
||||
|
|
Loading…
Add table
Reference in a new issue