diff --git a/docs/Controlling Ironbar.md b/docs/Controlling Ironbar.md index f320cb2..8f0ae9c 100644 --- a/docs/Controlling Ironbar.md +++ b/docs/Controlling Ironbar.md @@ -57,6 +57,20 @@ Responds with `ok`. } ``` +### `reload` + +Restarts the bars, reloading the config in the process. + +The IPC server and main GTK application are untouched. + +Responds with `ok`. + +```json +{ + "type": "reload" +} +``` + ### `get` Gets an [ironvar](ironvars) value. diff --git a/src/ipc/commands.rs b/src/ipc/commands.rs index e5712fb..698d991 100644 --- a/src/ipc/commands.rs +++ b/src/ipc/commands.rs @@ -11,12 +11,15 @@ pub enum Command { /// Open the GTK inspector Inspect, + /// Reload the config + Reload, + /// Set an `ironvar` value. /// This creates it if it does not already exist, and updates it if it does. /// Any references to this variable are automatically and immediately updated. /// Keys and values can be any valid UTF-8 string. Set { - /// Variable key. Can be any valid UTF-8 string. + /// Variable key. Can be any alphanumeric ASCII string. key: Box, /// Variable value. Can be any valid UTF-8 string. value: String, diff --git a/src/ipc/server.rs b/src/ipc/server.rs index 7aebde3..21e6105 100644 --- a/src/ipc/server.rs +++ b/src/ipc/server.rs @@ -6,19 +6,20 @@ use crate::style::load_css; use crate::{read_lock, send_async, try_send, write_lock}; use color_eyre::{Report, Result}; use glib::Continue; +use gtk::prelude::*; +use gtk::Application; use std::fs; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::{UnixListener, UnixStream}; use tokio::spawn; -use tokio::sync::mpsc; -use tokio::sync::mpsc::{Receiver, Sender}; +use tokio::sync::mpsc::{self, Receiver, Sender}; use tracing::{debug, error, info, warn}; impl Ipc { /// Starts the IPC server on its socket. /// /// Once started, the server will begin accepting connections. - pub fn start(&self) { + pub fn start(&self, application: &Application) { let bridge = BridgeChannel::::new(); let cmd_tx = bridge.create_sender(); let (res_tx, mut res_rx) = mpsc::channel(32); @@ -61,8 +62,9 @@ impl Ipc { } }); + let application = application.clone(); bridge.recv(move |command| { - let res = Self::handle_command(command); + let res = Self::handle_command(command, &application); try_send!(res_tx, res); Continue(true) }); @@ -102,12 +104,23 @@ impl Ipc { /// Takes an input command, runs it and returns with the appropriate response. /// /// This runs on the main thread, allowing commands to interact with GTK. - fn handle_command(command: Command) -> Response { + fn handle_command(command: Command, application: &Application) -> Response { match command { Command::Inspect => { gtk::Window::set_interactive_debugging(true); Response::Ok } + Command::Reload => { + info!("Closing existing bars"); + let windows = application.windows(); + for window in windows { + window.close(); + } + + crate::load_interface(application); + + Response::Ok + } Command::Set { key, value } => { let variable_manager = get_variable_manager(); let mut variable_manager = write_lock!(variable_manager); diff --git a/src/main.rs b/src/main.rs index f50b0b3..eca31ea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -102,50 +102,11 @@ fn start_ironbar() { cfg_if! { if #[cfg(feature = "ipc")] { let ipc = ipc::Ipc::new(); - ipc.start(); + ipc.start(app); } } - let display = Display::default().map_or_else( - || { - let report = Report::msg("Failed to get default GTK display"); - error!("{:?}", report); - exit(ExitCode::GtkDisplay as i32) - }, - |display| display, - ); - - let config_res = env::var("IRONBAR_CONFIG").map_or_else( - |_| ConfigLoader::new("ironbar").find_and_load(), - ConfigLoader::load, - ); - - let mut config: Config = match config_res { - Ok(config) => config, - Err(err) => { - error!("{:?}", err); - exit(ExitCode::Config as i32) - } - }; - - debug!("Loaded config file"); - - #[cfg(feature = "ipc")] - if let Some(ironvars) = config.ironvar_defaults.take() { - let variable_manager = ironvar::get_variable_manager(); - for (k, v) in ironvars { - if write_lock!(variable_manager).set(k.clone(), v).is_err() { - tracing::warn!("Ignoring invalid ironvar: '{k}'"); - } - } - } - - if let Err(err) = create_bars(app, &display, &config) { - error!("{:?}", err); - exit(ExitCode::CreateBars as i32); - } - - debug!("Created bars"); + load_interface(app); let style_path = env::var("IRONBAR_CSS").ok().map_or_else( || { @@ -187,6 +148,50 @@ fn start_ironbar() { app.run_with_args(&Vec::<&str>::new()); } +/// Loads the Ironbar config and interface. +pub fn load_interface(app: &Application) { + let display = Display::default().map_or_else( + || { + let report = Report::msg("Failed to get default GTK display"); + error!("{:?}", report); + exit(ExitCode::GtkDisplay as i32) + }, + |display| display, + ); + + let config_res = env::var("IRONBAR_CONFIG").map_or_else( + |_| ConfigLoader::new("ironbar").find_and_load(), + ConfigLoader::load, + ); + + let mut config: Config = match config_res { + Ok(config) => config, + Err(err) => { + error!("{:?}", err); + exit(ExitCode::Config as i32) + } + }; + + debug!("Loaded config file"); + + #[cfg(feature = "ipc")] + if let Some(ironvars) = config.ironvar_defaults.take() { + let variable_manager = ironvar::get_variable_manager(); + for (k, v) in ironvars { + if write_lock!(variable_manager).set(k.clone(), v).is_err() { + tracing::warn!("Ignoring invalid ironvar: '{k}'"); + } + } + } + + if let Err(err) = create_bars(app, &display, &config) { + error!("{:?}", err); + exit(ExitCode::CreateBars as i32); + } + + debug!("Created bars"); +} + /// Creates each of the bars across each of the (configured) outputs. fn create_bars(app: &Application, display: &Display, config: &Config) -> Result<()> { let wl = wayland::get_client();