This commit is contained in:
elkowar 2021-01-02 23:10:20 +01:00 committed by ElKowar
parent b9b8a77cf4
commit 05d9320598
5 changed files with 126 additions and 91 deletions

7
Cargo.lock generated
View file

@ -412,6 +412,7 @@ dependencies = [
"tokio 1.0.1",
"tokio-stream",
"tokio-util",
"unescape",
]
[[package]]
@ -2229,6 +2230,12 @@ dependencies = [
"serde",
]
[[package]]
name = "unescape"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e"
[[package]]
name = "unicode-segmentation"
version = "1.7.1"

View file

@ -1,5 +1,5 @@
use crate::{
application_lifecycle, config,
config,
config::{window_definition::WindowName, AnchorPoint, WindowStacking},
eww_state,
script_var_handler::*,
@ -80,7 +80,7 @@ impl App {
EwwCommand::KillServer => {
log::info!("Received kill command, stopping server!");
self.stop_application();
crate::application_lifecycle::send_exit()?;
let _ = crate::application_lifecycle::send_exit();
}
EwwCommand::CloseAll => {
log::info!("Received close command, closing all windows");

View file

@ -15,11 +15,11 @@ use tokio_util::sync::CancellationToken;
/// Initialize the script var handler, and return a handle to that handler, which can be used to control
/// the script var execution.
pub fn init(evt_send: UnboundedSender<EwwCommand>) -> Result<ScriptVarHandlerHandle> {
pub fn init(evt_send: UnboundedSender<EwwCommand>) -> ScriptVarHandlerHandle {
let (msg_send, mut msg_recv) = tokio::sync::mpsc::unbounded_channel();
let handle = ScriptVarHandlerHandle { msg_send };
std::thread::spawn(move || {
let rt = tokio::runtime::Runtime::new().unwrap();
let rt = tokio::runtime::Runtime::new().expect("Failed to initialize tokio runtime for script var handlers");
rt.block_on(async {
let _: Result<_> = try {
let mut handler = ScriptVarHandler {
@ -43,7 +43,7 @@ pub fn init(evt_send: UnboundedSender<EwwCommand>) -> Result<ScriptVarHandlerHan
};
})
});
Ok(handle)
handle
}
/// Handle to the script-var handling system.
@ -54,17 +54,26 @@ pub struct ScriptVarHandlerHandle {
impl ScriptVarHandlerHandle {
/// Add a new script-var that should be executed.
pub fn add(&self, script_var: config::ScriptVar) {
self.msg_send.send(ScriptVarHandlerMsg::AddVar(script_var)).unwrap();
crate::print_result_err!(
"while forwarding instruction to script-var handler",
self.msg_send.send(ScriptVarHandlerMsg::AddVar(script_var))
);
}
/// Stop the execution of a specific script-var.
pub fn stop_for_variable(&self, name: VarName) {
self.msg_send.send(ScriptVarHandlerMsg::Stop(name)).unwrap();
crate::print_result_err!(
"while forwarding instruction to script-var handler",
self.msg_send.send(ScriptVarHandlerMsg::Stop(name)),
);
}
/// Stop the execution of all script-vars.
pub fn stop_all(&self) {
self.msg_send.send(ScriptVarHandlerMsg::StopAll).unwrap();
crate::print_result_err!(
"while forwarding instruction to script-var handler",
self.msg_send.send(ScriptVarHandlerMsg::StopAll)
);
}
}
@ -177,24 +186,25 @@ impl TailVarHandler {
let evt_send = self.evt_send.clone();
tokio::spawn(async move {
let mut handle = tokio::process::Command::new("sh")
.args(&["-c", &var.command])
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::inherit())
.stdin(std::process::Stdio::null())
.spawn()
.unwrap();
let mut stdout_lines = BufReader::new(handle.stdout.take().unwrap()).lines();
crate::loop_select_exiting! {
_ = handle.wait() => break,
_ = cancellation_token.cancelled() => break,
Ok(Some(line)) = stdout_lines.next_line() => {
let new_value = PrimitiveValue::from_string(line.to_owned());
evt_send.send(EwwCommand::UpdateVars(vec![(var.name.to_owned(), new_value)])).unwrap();
crate::try_logging_errors!(format!("Executing tail var command {}", &var.command) => {
let mut handle = tokio::process::Command::new("sh")
.args(&["-c", &var.command])
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::inherit())
.stdin(std::process::Stdio::null())
.spawn()?;
let mut stdout_lines = BufReader::new(handle.stdout.take().unwrap()).lines();
crate::loop_select_exiting! {
_ = handle.wait() => break,
_ = cancellation_token.cancelled() => break,
Ok(Some(line)) = stdout_lines.next_line() => {
let new_value = PrimitiveValue::from_string(line.to_owned());
evt_send.send(EwwCommand::UpdateVars(vec![(var.name.to_owned(), new_value)]))?;
}
else => break,
}
else => break,
}
handle.kill().await.unwrap();
let _ = handle.kill().await;
});
});
}

View file

@ -1,7 +1,12 @@
use crate::{app, config, eww_state::*, opts, script_var_handler, try_logging_errors, util};
use anyhow::*;
use futures_util::StreamExt;
use std::{collections::HashMap, os::unix::io::AsRawFd, path::Path, time::Duration};
use std::{
collections::HashMap,
os::unix::io::AsRawFd,
path::{Path, PathBuf},
time::Duration,
};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
sync::mpsc::*,
@ -16,7 +21,10 @@ pub fn initialize_server(should_detach: bool, action: opts::ActionWithServer) ->
simple_signal::set_handler(&[simple_signal::Signal::Int, simple_signal::Signal::Term], move |_| {
println!("Shutting down eww daemon...");
crate::application_lifecycle::send_exit().unwrap();
if let Err(e) = crate::application_lifecycle::send_exit() {
eprintln!("Failed to send application shutdown event to workers: {:?}", e);
std::process::exit(1);
}
});
let config_file_path = crate::CONFIG_DIR.join("eww.xml");
@ -31,10 +39,9 @@ pub fn initialize_server(should_detach: bool, action: opts::ActionWithServer) ->
gtk::init()?;
let (ui_send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel();
let glib_context = glib::MainContext::default();
log::info!("Initializing script var handler");
let script_var_handler = script_var_handler::init(ui_send.clone())?;
let script_var_handler = script_var_handler::init(ui_send.clone());
let mut app = app::App {
eww_state: EwwState::from_default_vars(eww_config.generate_initial_state()?),
@ -58,49 +65,10 @@ pub fn initialize_server(should_detach: bool, action: opts::ActionWithServer) ->
let (command, maybe_response_recv) = action.into_eww_command();
app.handle_command(command);
std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("Failed to initialize tokio runtime");
rt.block_on(async {
// print out the response of this initial command, if there is any
tokio::spawn(async {
if let Some(mut response_recv) = maybe_response_recv {
if let Ok(Some(response)) = tokio::time::timeout(Duration::from_millis(100), response_recv.recv()).await {
println!("{}", response);
}
}
});
// initialize all the handlers and tasks running asyncronously
init_async_part(config_file_path, scss_file_path, maybe_response_recv, ui_send);
let filewatch_join_handle = {
let ui_send = ui_send.clone();
tokio::spawn(async move { run_filewatch_thingy(config_file_path, scss_file_path, ui_send).await })
};
let ipc_server_join_handle = {
let ui_send = ui_send.clone();
tokio::spawn(async move { async_server(ui_send).await })
};
let forward_lifecycle_to_app_handle = {
let ui_send = ui_send.clone();
tokio::spawn(async move {
while let Ok(()) = crate::application_lifecycle::recv_exit().await {
let _ = ui_send.send(app::EwwCommand::KillServer);
}
})
};
let result = tokio::try_join!(filewatch_join_handle, ipc_server_join_handle, forward_lifecycle_to_app_handle);
if let Err(e) = result {
eprintln!("Eww exiting with error: {:?}", e);
}
})
});
glib_context.spawn_local(async move {
glib::MainContext::default().spawn_local(async move {
while let Some(event) = ui_recv.recv().await {
app.handle_command(event);
}
@ -112,7 +80,58 @@ pub fn initialize_server(should_detach: bool, action: opts::ActionWithServer) ->
Ok(())
}
async fn async_server(evt_send: UnboundedSender<app::EwwCommand>) -> Result<()> {
fn init_async_part(
config_file_path: PathBuf,
scss_file_path: PathBuf,
maybe_response_recv: Option<UnboundedReceiver<String>>,
ui_send: UnboundedSender<app::EwwCommand>,
) {
std::thread::spawn(move || {
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("Failed to initialize tokio runtime");
rt.block_on(async {
// TODO This really does not belong here at all :<
// print out the response of this initial command, if there is any
tokio::spawn(async {
if let Some(mut response_recv) = maybe_response_recv {
if let Ok(Some(response)) = tokio::time::timeout(Duration::from_millis(100), response_recv.recv()).await {
println!("{}", response);
}
}
});
let filewatch_join_handle = {
let ui_send = ui_send.clone();
tokio::spawn(async move { run_filewatch(config_file_path, scss_file_path, ui_send).await })
};
let ipc_server_join_handle = {
let ui_send = ui_send.clone();
tokio::spawn(async move { run_ipc_server(ui_send).await })
};
let forward_exit_to_app_handle = {
let ui_send = ui_send.clone();
tokio::spawn(async move {
// Wait for application exit event
let _ = crate::application_lifecycle::recv_exit().await;
// Then forward that to the application
let _ = ui_send.send(app::EwwCommand::KillServer);
})
};
let result = tokio::try_join!(filewatch_join_handle, ipc_server_join_handle, forward_exit_to_app_handle);
if let Err(e) = result {
eprintln!("Eww exiting with error: {:?}", e);
}
})
});
}
async fn run_ipc_server(evt_send: UnboundedSender<app::EwwCommand>) -> Result<()> {
let listener = tokio::net::UnixListener::bind(&*crate::IPC_SOCKET_PATH)?;
crate::loop_select_exiting! {
connection = listener.accept() => match connection {
@ -123,6 +142,7 @@ async fn async_server(evt_send: UnboundedSender<app::EwwCommand>) -> Result<()>
Ok(())
}
/// Handle a single IPC connection from start to end.
async fn handle_connection(mut stream: tokio::net::UnixStream, evt_send: UnboundedSender<app::EwwCommand>) -> Result<()> {
let (mut stream_read, mut stream_write) = stream.split();
@ -149,7 +169,8 @@ async fn handle_connection(mut stream: tokio::net::UnixStream, evt_send: Unbound
Ok(())
}
async fn run_filewatch_thingy<P: AsRef<Path>>(
/// Watch configuration files for changes, sending reload events to the eww app when the files change.
async fn run_filewatch<P: AsRef<Path>>(
config_file_path: P,
scss_file_path: P,
evt_send: UnboundedSender<app::EwwCommand>,
@ -167,29 +188,26 @@ async fn run_filewatch_thingy<P: AsRef<Path>>(
crate::loop_select_exiting! {
Some(Ok(event)) = event_stream.next() => {
if event.wd == config_file_descriptor {
try_logging_errors!("handling change of config file" => {
log::info!("Reloading eww configuration");
let new_eww_config = config::EwwConfig::read_from_file(config_file_path.as_ref())?;
evt_send.send(app::EwwCommand::ReloadConfig(new_eww_config))?;
});
} else if event.wd == scss_file_descriptor {
try_logging_errors!("handling change of scss file" => {
log::info!("reloading eww css file");
let eww_css = crate::util::parse_scss_from_file(scss_file_path.as_ref())?;
evt_send.send(app::EwwCommand::ReloadCss(eww_css))?;
});
} else {
eprintln!("Got inotify event for unknown thing: {:?}", event);
}
try_logging_errors!("handling change of config file" => {
if event.wd == config_file_descriptor {
log::info!("Reloading eww configuration");
let new_eww_config = config::EwwConfig::read_from_file(config_file_path.as_ref())?;
evt_send.send(app::EwwCommand::ReloadConfig(new_eww_config))?;
} else if event.wd == scss_file_descriptor {
log::info!("reloading eww css file");
let eww_css = crate::util::parse_scss_from_file(scss_file_path.as_ref())?;
evt_send.send(app::EwwCommand::ReloadCss(eww_css))?;
} else {
eprintln!("Got inotify event for unknown thing: {:?}", event);
}
});
}
else => break,
}
Ok(())
}
/// detach the process from the terminal, also redirecting stdout and stderr to
/// LOG_FILE
/// detach the process from the terminal, also redirecting stdout and stderr to LOG_FILE
fn do_detach() -> Result<()> {
// detach from terminal
match unsafe { nix::unistd::fork()? } {

View file

@ -22,7 +22,7 @@ macro_rules! impl_try_from {
#[macro_export]
macro_rules! try_logging_errors {
($context:literal => $code:block) => {{
($context:expr => $code:block) => {{
let result: Result<_> = try { $code };
if let Err(err) = result {
eprintln!("[{}:{}] Error while {}: {:?}", ::std::file!(), ::std::line!(), $context, err);