Skip to content

Commit

Permalink
Add IPC config subcommand
Browse files Browse the repository at this point in the history
This patch adds a new mechanism for changing configuration options
without editing the configuration file, by sending options to running
instances through `alacritty msg`.

Each window will load Alacritty's configuration file by default and then
accept IPC messages for config updates using the `alacritty msg config`
subcommand. By default all windows will be updated, individual windows
can be addressed using `alacritty msg config --window-id
"$ALACRITTY_WINDOW_ID"`.

Each option will replace the config's current value and cannot be reset
until Alacritty is restarted or the option is overwritten with a new
value.

Configuration options are passed in the format `field.subfield=value`,
where `value` is interpreted as yaml.

Closes #472.
  • Loading branch information
chrisduerr committed Aug 31, 2022
1 parent 18f9c27 commit 4ddb608
Show file tree
Hide file tree
Showing 32 changed files with 630 additions and 165 deletions.
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
members = [
"alacritty",
"alacritty_terminal",
"alacritty_config",
"alacritty_config_derive",
]

Expand Down
6 changes: 5 additions & 1 deletion alacritty/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,12 @@ default-features = false
path = "../alacritty_config_derive"
version = "0.1.0"

[dependencies.alacritty_config]
path = "../alacritty_config"
version = "0.1.0"

[dependencies]
clap = { version = "3.0.0", features = ["derive"] }
clap = { version = "3.0.0", features = ["derive", "env"] }
log = { version = "0.4", features = ["std", "serde"] }
fnv = "1"
serde = { version = "1", features = ["derive"] }
Expand Down
48 changes: 37 additions & 11 deletions alacritty/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,14 +81,7 @@ impl Options {
let mut options = Self::parse();

// Convert `--option` flags into serde `Value`.
for option in &options.option {
match option_as_value(option) {
Ok(value) => {
options.config_options = serde_utils::merge(options.config_options, value);
},
Err(_) => eprintln!("Invalid CLI config option: {:?}", option),
}
}
options.config_options = options_as_value(&options.option);

options
}
Expand Down Expand Up @@ -132,7 +125,18 @@ impl Options {
}
}

/// Format an option in the format of `parent.field=value` to a serde Value.
/// Combine multiple options into a [`serde_yaml::Value`].
pub fn options_as_value(options: &[String]) -> Value {
options.iter().fold(Value::default(), |value, option| match option_as_value(option) {
Ok(new_value) => serde_utils::merge(value, new_value),
Err(_) => {
eprintln!("Ignoring invalid option: {:?}", option);
value
},
})
}

/// Parse an option in the format of `parent.field=value` as a serde Value.
fn option_as_value(option: &str) -> Result<Value, serde_yaml::Error> {
let mut yaml_text = String::with_capacity(option.len());
let mut closing_brackets = String::new();
Expand Down Expand Up @@ -266,7 +270,7 @@ pub enum Subcommands {
#[derive(Args, Debug)]
pub struct MessageOptions {
/// IPC socket connection path override.
#[clap(long, short, value_hint = ValueHint::FilePath)]
#[clap(short, long, value_hint = ValueHint::FilePath)]
pub socket: Option<PathBuf>,

/// Message which should be sent.
Expand All @@ -280,9 +284,12 @@ pub struct MessageOptions {
pub enum SocketMessage {
/// Create a new window in the same Alacritty process.
CreateWindow(WindowOptions),

/// Update the Alacritty configuration.
Config(IpcConfig),
}

/// Subset of options that we pass to a 'create-window' subcommand.
/// Subset of options that we pass to 'create-window' IPC subcommand.
#[derive(Serialize, Deserialize, Args, Default, Clone, Debug, PartialEq, Eq)]
pub struct WindowOptions {
/// Terminal options which can be passed via IPC.
Expand All @@ -294,6 +301,25 @@ pub struct WindowOptions {
pub window_identity: WindowIdentity,
}

/// Parameters to the `config` IPC subcommand.
#[cfg(unix)]
#[derive(Args, Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
pub struct IpcConfig {
/// Configuration file options [example: cursor.style=Beam].
#[clap(required = true, value_name = "CONFIG_OPTIONS")]
pub options: Vec<String>,

/// Window ID for the new config.
///
/// Use `-1` to apply this change to all windows.
#[clap(short, long, allow_hyphen_values = true, env = "ALACRITTY_WINDOW_ID")]
pub window_id: Option<i128>,

/// Clear all runtime configuration changes.
#[clap(short, long, conflicts_with = "options")]
pub reset: bool,
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
4 changes: 2 additions & 2 deletions alacritty/src/config/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use serde::de::{self, Error as SerdeError, MapAccess, Unexpected, Visitor};
use serde::{Deserialize, Deserializer};
use serde_yaml::Value as SerdeValue;

use alacritty_config_derive::ConfigDeserialize;
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};

use alacritty_terminal::config::Program;
use alacritty_terminal::term::TermMode;
Expand Down Expand Up @@ -1191,7 +1191,7 @@ impl<'a> Deserialize<'a> for KeyBinding {
///
/// Our deserialize impl wouldn't be covered by a derive(Deserialize); see the
/// impl below.
#[derive(Debug, Copy, Clone, Hash, Default, Eq, PartialEq)]
#[derive(SerdeReplace, Debug, Copy, Clone, Hash, Default, Eq, PartialEq)]
pub struct ModsWrapper(pub ModifiersState);

impl ModsWrapper {
Expand Down
4 changes: 2 additions & 2 deletions alacritty/src/config/font.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crossfont::Size as FontSize;
use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer};

use alacritty_config_derive::ConfigDeserialize;
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};

use crate::config::ui_config::Delta;

Expand Down Expand Up @@ -129,7 +129,7 @@ impl SecondaryFontDescription {
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(SerdeReplace, Debug, Clone, PartialEq, Eq)]
struct Size(FontSize);

impl Default for Size {
Expand Down
12 changes: 6 additions & 6 deletions alacritty/src/config/ui_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use serde::de::{Error as SerdeError, MapAccess, Visitor};
use serde::{self, Deserialize, Deserializer};
use unicode_width::UnicodeWidthChar;

use alacritty_config_derive::ConfigDeserialize;
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
use alacritty_terminal::config::{
Config as TerminalConfig, Percentage, Program, LOG_TARGET_CONFIG,
};
Expand All @@ -30,7 +30,7 @@ use crate::config::window::WindowConfig;
const URL_REGEX: &str = "(ipfs:|ipns:|magnet:|mailto:|gemini:|gopher:|https:|http:|news:|file:|git:|ssh:|ftp:)\
[^\u{0000}-\u{001F}\u{007F}-\u{009F}<>\"\\s{-}\\^⟨⟩`]+";

#[derive(ConfigDeserialize, Debug, PartialEq)]
#[derive(ConfigDeserialize, Clone, Debug, PartialEq)]
pub struct UiConfig {
/// Font configuration.
pub font: Font,
Expand Down Expand Up @@ -145,7 +145,7 @@ impl UiConfig {
}
}

#[derive(Debug, PartialEq, Eq)]
#[derive(SerdeReplace, Clone, Debug, PartialEq, Eq)]
struct KeyBindings(Vec<KeyBinding>);

impl Default for KeyBindings {
Expand All @@ -163,7 +163,7 @@ impl<'de> Deserialize<'de> for KeyBindings {
}
}

#[derive(Debug, PartialEq, Eq)]
#[derive(SerdeReplace, Clone, Debug, PartialEq, Eq)]
struct MouseBindings(Vec<MouseBinding>);

impl Default for MouseBindings {
Expand Down Expand Up @@ -223,7 +223,7 @@ pub struct Delta<T: Default> {
}

/// Regex terminal hints.
#[derive(ConfigDeserialize, Debug, PartialEq, Eq)]
#[derive(ConfigDeserialize, Clone, Debug, PartialEq, Eq)]
pub struct Hints {
/// Characters for the hint labels.
alphabet: HintsAlphabet,
Expand Down Expand Up @@ -273,7 +273,7 @@ impl Hints {
}
}

#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(SerdeReplace, Clone, Debug, PartialEq, Eq)]
struct HintsAlphabet(String);

impl Default for HintsAlphabet {
Expand Down
4 changes: 2 additions & 2 deletions alacritty/src/config/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use log::{error, warn};
use serde::de::{self, MapAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize};

use alacritty_config_derive::ConfigDeserialize;
use alacritty_config_derive::{ConfigDeserialize, SerdeReplace};
use alacritty_terminal::config::{Percentage, LOG_TARGET_CONFIG};
use alacritty_terminal::index::Column;

Expand Down Expand Up @@ -201,7 +201,7 @@ pub struct Dimensions {
}

/// Window class hint.
#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
#[derive(SerdeReplace, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct Class {
pub general: String,
pub instance: String,
Expand Down
10 changes: 0 additions & 10 deletions alacritty/src/display/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,16 +405,6 @@ impl Window {
self.window().request_user_attention(attention);
}

#[cfg(all(feature = "x11", not(any(target_os = "macos", windows))))]
pub fn x11_window_id(&self) -> Option<usize> {
self.window().xlib_window().map(|xlib_window| xlib_window as usize)
}

#[cfg(any(not(feature = "x11"), target_os = "macos", windows))]
pub fn x11_window_id(&self) -> Option<usize> {
None
}

pub fn id(&self) -> WindowId {
self.window().id()
}
Expand Down
36 changes: 27 additions & 9 deletions alacritty/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use std::fmt::Debug;
#[cfg(not(windows))]
use std::os::unix::io::RawFd;
use std::path::PathBuf;
use std::rc::Rc;
use std::time::{Duration, Instant};
use std::{env, f32, mem};

Expand Down Expand Up @@ -38,6 +39,8 @@ use alacritty_terminal::selection::{Selection, SelectionType};
use alacritty_terminal::term::search::{Match, RegexSearch};
use alacritty_terminal::term::{self, ClipboardType, Term, TermMode};

#[cfg(unix)]
use crate::cli::IpcConfig;
use crate::cli::{Options as CliOptions, WindowOptions};
use crate::clipboard::Clipboard;
use crate::config::ui_config::{HintAction, HintInternalAction};
Expand Down Expand Up @@ -93,6 +96,8 @@ pub enum EventType {
Message(Message),
Scroll(Scroll),
CreateWindow(WindowOptions),
#[cfg(unix)]
IpcConfig(IpcConfig),
BlinkCursor,
BlinkCursorTimeout,
SearchNext,
Expand Down Expand Up @@ -184,7 +189,7 @@ pub struct ActionContext<'a, N, T> {
pub modifiers: &'a mut ModifiersState,
pub display: &'a mut Display,
pub message_buffer: &'a mut MessageBuffer,
pub config: &'a mut UiConfig,
pub config: &'a UiConfig,
pub cursor_blink_timed_out: &'a mut bool,
pub event_loop: &'a EventLoopWindowTarget<Event>,
pub event_proxy: &'a EventLoopProxy<Event>,
Expand Down Expand Up @@ -1162,6 +1167,8 @@ impl input::Processor<EventProxy, ActionContext<'_, Notifier, EventProxy>> {
TerminalEvent::Exit => (),
TerminalEvent::CursorBlinkingChange => self.ctx.update_cursor_blinking(),
},
#[cfg(unix)]
EventType::IpcConfig(_) => (),
EventType::ConfigReload(_) | EventType::CreateWindow(_) => (),
},
GlutinEvent::RedrawRequested(_) => *self.ctx.dirty = true,
Expand Down Expand Up @@ -1292,7 +1299,7 @@ pub struct Processor {
wayland_event_queue: Option<EventQueue>,
windows: HashMap<WindowId, WindowContext>,
cli_options: CliOptions,
config: UiConfig,
config: Rc<UiConfig>,
}

impl Processor {
Expand All @@ -1313,8 +1320,8 @@ impl Processor {

Processor {
windows: HashMap::new(),
config: Rc::new(config),
cli_options,
config,
#[cfg(all(feature = "wayland", not(any(target_os = "macos", windows))))]
wayland_event_queue,
}
Expand All @@ -1328,7 +1335,7 @@ impl Processor {
options: WindowOptions,
) -> Result<(), Box<dyn Error>> {
let window_context = WindowContext::new(
&self.config,
self.config.clone(),
&options,
event_loop,
proxy,
Expand Down Expand Up @@ -1436,7 +1443,6 @@ impl Processor {
window_context.handle_event(
event_loop,
&proxy,
&mut self.config,
&mut clipboard,
&mut scheduler,
GlutinEvent::RedrawEventsCleared,
Expand All @@ -1457,13 +1463,27 @@ impl Processor {

// Load config and update each terminal.
if let Ok(config) = config::reload(&path, &self.cli_options) {
let old_config = mem::replace(&mut self.config, config);
self.config = Rc::new(config);

for window_context in self.windows.values_mut() {
window_context.update_config(&old_config, &self.config);
window_context.update_config(self.config.clone());
}
}
},
// Process IPC config update.
#[cfg(unix)]
GlutinEvent::UserEvent(Event {
payload: EventType::IpcConfig(ipc_config),
window_id,
}) => {
for (_, window_context) in self
.windows
.iter_mut()
.filter(|(id, _)| window_id.is_none() || window_id == Some(**id))
{
window_context.update_ipc_config(self.config.clone(), ipc_config.clone());
}
},
// Create a new terminal window.
GlutinEvent::UserEvent(Event {
payload: EventType::CreateWindow(options), ..
Expand All @@ -1485,7 +1505,6 @@ impl Processor {
window_context.handle_event(
event_loop,
&proxy,
&mut self.config,
&mut clipboard,
&mut scheduler,
event.clone().into(),
Expand All @@ -1500,7 +1519,6 @@ impl Processor {
window_context.handle_event(
event_loop,
&proxy,
&mut self.config,
&mut clipboard,
&mut scheduler,
event,
Expand Down
Loading

0 comments on commit 4ddb608

Please sign in to comment.