2023-06-22 23:06:45 +01:00
|
|
|
use super::Ipc;
|
|
|
|
use crate::bridge_channel::BridgeChannel;
|
|
|
|
use crate::ipc::{Command, Response};
|
|
|
|
use crate::ironvar::get_variable_manager;
|
2023-06-24 11:19:57 +01:00
|
|
|
use crate::style::load_css;
|
2023-06-22 23:06:45 +01:00
|
|
|
use crate::{read_lock, send_async, try_send, write_lock};
|
|
|
|
use color_eyre::{Report, Result};
|
|
|
|
use glib::Continue;
|
2023-07-01 00:05:12 +01:00
|
|
|
use gtk::prelude::*;
|
|
|
|
use gtk::Application;
|
2023-06-22 23:06:45 +01:00
|
|
|
use std::fs;
|
|
|
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
|
|
use tokio::net::{UnixListener, UnixStream};
|
|
|
|
use tokio::spawn;
|
2023-07-01 00:05:12 +01:00
|
|
|
use tokio::sync::mpsc::{self, Receiver, Sender};
|
2023-06-22 23:06:45 +01:00
|
|
|
use tracing::{debug, error, info, warn};
|
|
|
|
|
|
|
|
impl Ipc {
|
|
|
|
/// Starts the IPC server on its socket.
|
|
|
|
///
|
|
|
|
/// Once started, the server will begin accepting connections.
|
2023-07-01 00:05:12 +01:00
|
|
|
pub fn start(&self, application: &Application) {
|
2023-06-22 23:06:45 +01:00
|
|
|
let bridge = BridgeChannel::<Command>::new();
|
|
|
|
let cmd_tx = bridge.create_sender();
|
|
|
|
let (res_tx, mut res_rx) = mpsc::channel(32);
|
|
|
|
|
|
|
|
let path = self.path.clone();
|
|
|
|
|
|
|
|
if path.exists() {
|
|
|
|
warn!("Socket already exists. Did Ironbar exit abruptly?");
|
|
|
|
warn!("Attempting IPC shutdown to allow binding to address");
|
|
|
|
self.shutdown();
|
|
|
|
}
|
|
|
|
|
|
|
|
spawn(async move {
|
|
|
|
info!("Starting IPC on {}", path.display());
|
|
|
|
|
|
|
|
let listener = match UnixListener::bind(&path) {
|
|
|
|
Ok(listener) => listener,
|
|
|
|
Err(err) => {
|
|
|
|
error!(
|
|
|
|
"{:?}",
|
|
|
|
Report::new(err).wrap_err("Unable to start IPC server")
|
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
loop {
|
|
|
|
match listener.accept().await {
|
|
|
|
Ok((stream, _addr)) => {
|
|
|
|
if let Err(err) =
|
|
|
|
Self::handle_connection(stream, &cmd_tx, &mut res_rx).await
|
|
|
|
{
|
|
|
|
error!("{err:?}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
error!("{err:?}");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-07-01 00:05:12 +01:00
|
|
|
let application = application.clone();
|
2023-06-22 23:06:45 +01:00
|
|
|
bridge.recv(move |command| {
|
2023-07-01 00:05:12 +01:00
|
|
|
let res = Self::handle_command(command, &application);
|
2023-06-22 23:06:45 +01:00
|
|
|
try_send!(res_tx, res);
|
|
|
|
Continue(true)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Takes an incoming connections,
|
|
|
|
/// reads the command message, and sends the response.
|
|
|
|
///
|
|
|
|
/// The connection is closed once the response has been written.
|
|
|
|
async fn handle_connection(
|
|
|
|
mut stream: UnixStream,
|
|
|
|
cmd_tx: &Sender<Command>,
|
|
|
|
res_rx: &mut Receiver<Response>,
|
|
|
|
) -> Result<()> {
|
|
|
|
let (mut stream_read, mut stream_write) = stream.split();
|
|
|
|
|
|
|
|
let mut read_buffer = vec![0; 1024];
|
|
|
|
let bytes = stream_read.read(&mut read_buffer).await?;
|
|
|
|
|
|
|
|
let command = serde_json::from_slice::<Command>(&read_buffer[..bytes])?;
|
|
|
|
|
|
|
|
debug!("Received command: {command:?}");
|
|
|
|
|
|
|
|
send_async!(cmd_tx, command);
|
|
|
|
let res = res_rx
|
|
|
|
.recv()
|
|
|
|
.await
|
|
|
|
.unwrap_or(Response::Err { message: None });
|
|
|
|
let res = serde_json::to_vec(&res)?;
|
|
|
|
|
|
|
|
stream_write.write_all(&res).await?;
|
|
|
|
stream_write.shutdown().await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Takes an input command, runs it and returns with the appropriate response.
|
|
|
|
///
|
|
|
|
/// This runs on the main thread, allowing commands to interact with GTK.
|
2023-07-01 00:05:12 +01:00
|
|
|
fn handle_command(command: Command, application: &Application) -> Response {
|
2023-06-22 23:06:45 +01:00
|
|
|
match command {
|
|
|
|
Command::Inspect => {
|
|
|
|
gtk::Window::set_interactive_debugging(true);
|
|
|
|
Response::Ok
|
2023-06-24 11:19:57 +01:00
|
|
|
}
|
2023-07-01 00:05:12 +01:00
|
|
|
Command::Reload => {
|
|
|
|
info!("Closing existing bars");
|
|
|
|
let windows = application.windows();
|
|
|
|
for window in windows {
|
|
|
|
window.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
crate::load_interface(application);
|
|
|
|
|
|
|
|
Response::Ok
|
|
|
|
}
|
2023-06-22 23:07:40 +01:00
|
|
|
Command::Set { key, value } => {
|
|
|
|
let variable_manager = get_variable_manager();
|
|
|
|
let mut variable_manager = write_lock!(variable_manager);
|
|
|
|
match variable_manager.set(key, value) {
|
|
|
|
Ok(_) => Response::Ok,
|
|
|
|
Err(err) => Response::error(&format!("{err}")),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Command::Get { key } => {
|
|
|
|
let variable_manager = get_variable_manager();
|
|
|
|
let value = read_lock!(variable_manager).get(&key);
|
|
|
|
match value {
|
|
|
|
Some(value) => Response::OkValue { value },
|
|
|
|
None => Response::error("Variable not found"),
|
|
|
|
}
|
2023-06-22 23:06:45 +01:00
|
|
|
}
|
2023-06-24 11:19:57 +01:00
|
|
|
Command::LoadCss { path } => {
|
|
|
|
if path.exists() {
|
|
|
|
load_css(path);
|
|
|
|
Response::Ok
|
|
|
|
} else {
|
|
|
|
Response::error("File not found")
|
|
|
|
}
|
|
|
|
}
|
2023-06-22 23:06:45 +01:00
|
|
|
Command::Ping => Response::Ok,
|
2023-07-12 18:17:04 -04:00
|
|
|
Command::SetVisible { bar_name, visible } => {
|
|
|
|
let windows = application.windows();
|
|
|
|
let found = windows
|
|
|
|
.iter()
|
|
|
|
.find(|window| window.widget_name() == bar_name);
|
|
|
|
|
|
|
|
if let Some(window) = found {
|
|
|
|
window.set_visible(visible);
|
|
|
|
Response::Ok
|
|
|
|
} else {
|
|
|
|
Response::error("Bar not found")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Command::GetVisible { bar_name } => {
|
|
|
|
let windows = application.windows();
|
|
|
|
let found = windows
|
|
|
|
.iter()
|
|
|
|
.find(|window| window.widget_name() == bar_name);
|
|
|
|
|
|
|
|
if let Some(window) = found {
|
|
|
|
Response::OkValue {
|
|
|
|
value: window.is_visible().to_string(),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Response::error("Bar not found")
|
|
|
|
}
|
|
|
|
}
|
2023-06-22 23:06:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Shuts down the IPC server,
|
|
|
|
/// removing the socket file in the process.
|
|
|
|
pub fn shutdown(&self) {
|
|
|
|
fs::remove_file(&self.path).ok();
|
|
|
|
}
|
|
|
|
}
|