// Copyright 2016 Joe Wilm, The Alacritty Project Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::borrow::Cow; use std::collections::HashMap; use std::path::PathBuf; use serde::{Deserialize, Deserializer}; mod bindings; mod colors; mod debug; mod font; mod monitor; mod mouse; mod scrolling; #[cfg(test)] mod test; mod visual_bell; mod window; use crate::ansi::CursorStyle; use crate::input::{Binding, KeyBinding, MouseBinding}; pub use crate::config::bindings::Key; pub use crate::config::colors::Colors; pub use crate::config::debug::Debug; pub use crate::config::font::{Font, FontDescription}; pub use crate::config::monitor::Monitor; pub use crate::config::mouse::{ClickHandler, Mouse}; pub use crate::config::scrolling::Scrolling; pub use crate::config::visual_bell::{VisualBellAnimation, VisualBellConfig}; pub use crate::config::window::{Decorations, Dimensions, StartupMode, WindowConfig}; pub static DEFAULT_ALACRITTY_CONFIG: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../alacritty.yml")); const MAX_SCROLLBACK_LINES: u32 = 100_000; /// Top-level config type #[derive(Debug, PartialEq, Deserialize)] pub struct Config { /// Pixel padding #[serde(default, deserialize_with = "failure_default")] pub padding: Option>, /// TERM env variable #[serde(default, deserialize_with = "failure_default")] pub env: HashMap, /// Font configuration #[serde(default, deserialize_with = "failure_default")] pub font: Font, /// Should draw bold text with brighter colors instead of bold font #[serde(default, deserialize_with = "failure_default")] draw_bold_text_with_bright_colors: DefaultTrueBool, #[serde(default, deserialize_with = "failure_default")] pub colors: Colors, /// Background opacity from 0.0 to 1.0 #[serde(default, deserialize_with = "failure_default")] background_opacity: Alpha, /// Window configuration #[serde(default, deserialize_with = "failure_default")] pub window: WindowConfig, /// Keybindings #[serde(default = "default_key_bindings", deserialize_with = "deserialize_key_bindings")] pub key_bindings: Vec, /// Bindings for the mouse #[serde(default = "default_mouse_bindings", deserialize_with = "deserialize_mouse_bindings")] pub mouse_bindings: Vec, #[serde(default, deserialize_with = "failure_default")] pub selection: Selection, #[serde(default, deserialize_with = "failure_default")] pub mouse: Mouse, /// Path to a shell program to run on startup #[serde(default, deserialize_with = "failure_default")] pub shell: Option>, /// Path where config was loaded from #[serde(default, deserialize_with = "failure_default")] pub config_path: Option, /// Visual bell configuration #[serde(default, deserialize_with = "failure_default")] pub visual_bell: VisualBellConfig, /// Use dynamic title #[serde(default, deserialize_with = "failure_default")] dynamic_title: DefaultTrueBool, /// Live config reload #[serde(default, deserialize_with = "failure_default")] live_config_reload: DefaultTrueBool, /// Number of spaces in one tab #[serde(default, deserialize_with = "failure_default")] tabspaces: Tabspaces, /// How much scrolling history to keep #[serde(default, deserialize_with = "failure_default")] pub scrolling: Scrolling, /// Cursor configuration #[serde(default, deserialize_with = "failure_default")] pub cursor: Cursor, /// Enable experimental conpty backend instead of using winpty. /// Will only take effect on Windows 10 Oct 2018 and later. #[cfg(windows)] #[serde(default, deserialize_with = "failure_default")] pub enable_experimental_conpty_backend: bool, /// Send escape sequences using the alt key. #[serde(default, deserialize_with = "failure_default")] alt_send_esc: DefaultTrueBool, /// Shell startup directory #[serde(default, deserialize_with = "failure_default")] working_directory: WorkingDirectory, /// Debug options #[serde(default, deserialize_with = "failure_default")] pub debug: Debug, // TODO: DEPRECATED #[serde(default, deserialize_with = "failure_default")] pub render_timer: Option, // TODO: DEPRECATED #[serde(default, deserialize_with = "failure_default")] pub persistent_logging: Option, } impl Default for Config { fn default() -> Self { serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG).expect("default config is invalid") } } impl Config { pub fn tabspaces(&self) -> usize { self.tabspaces.0 } #[inline] pub fn draw_bold_text_with_bright_colors(&self) -> bool { self.draw_bold_text_with_bright_colors.0 } /// Should show render timer #[inline] pub fn render_timer(&self) -> bool { self.render_timer.unwrap_or(self.debug.render_timer) } /// Live config reload #[inline] pub fn live_config_reload(&self) -> bool { self.live_config_reload.0 } #[inline] pub fn set_live_config_reload(&mut self, live_config_reload: bool) { self.live_config_reload.0 = live_config_reload; } #[inline] pub fn dynamic_title(&self) -> bool { self.dynamic_title.0 } #[inline] pub fn set_dynamic_title(&mut self, dynamic_title: bool) { self.dynamic_title.0 = dynamic_title; } /// Send escape sequences using the alt key #[inline] pub fn alt_send_esc(&self) -> bool { self.alt_send_esc.0 } /// Keep the log file after quitting Alacritty #[inline] pub fn persistent_logging(&self) -> bool { self.persistent_logging.unwrap_or(self.debug.persistent_logging) } #[inline] pub fn background_opacity(&self) -> f32 { self.background_opacity.0 } #[inline] pub fn working_directory(&self) -> &Option { &self.working_directory.0 } #[inline] pub fn set_working_directory(&mut self, working_directory: Option) { self.working_directory.0 = working_directory; } } #[derive(Default, Debug, PartialEq, Eq)] struct WorkingDirectory(Option); impl<'de> Deserialize<'de> for WorkingDirectory { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let value = serde_yaml::Value::deserialize(deserializer)?; // Accept `None` to use the default path if value.as_str().filter(|v| v.to_lowercase() == "none").is_some() { return Ok(WorkingDirectory(None)); } Ok(match PathBuf::deserialize(value) { Ok(path) => WorkingDirectory(Some(path)), Err(err) => { error!("Problem with config: {}; using None", err); WorkingDirectory(None) }, }) } } fn default_key_bindings() -> Vec { bindings::default_key_bindings() } fn default_mouse_bindings() -> Vec { bindings::default_mouse_bindings() } fn deserialize_key_bindings<'a, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'a>, { deserialize_bindings(deserializer, bindings::default_key_bindings()) } fn deserialize_mouse_bindings<'a, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'a>, { deserialize_bindings(deserializer, bindings::default_mouse_bindings()) } fn deserialize_bindings<'a, D, T>( deserializer: D, mut default: Vec>, ) -> Result>, D::Error> where D: Deserializer<'a>, T: Copy + Eq + std::hash::Hash + std::fmt::Debug, Binding: Deserialize<'a>, { let mut bindings: Vec> = failure_default(deserializer)?; for binding in bindings.iter() { default.retain(|b| !b.triggers_match(binding)); } bindings.extend(default); Ok(bindings) } #[serde(default)] #[derive(Deserialize, Default, Clone, Debug, PartialEq, Eq)] pub struct Selection { #[serde(deserialize_with = "failure_default")] semantic_escape_chars: EscapeChars, #[serde(deserialize_with = "failure_default")] pub save_to_clipboard: bool, } impl Selection { pub fn semantic_escape_chars(&self) -> &str { &self.semantic_escape_chars.0 } } #[derive(Deserialize, Clone, Debug, PartialEq, Eq)] struct EscapeChars(String); impl Default for EscapeChars { fn default() -> Self { EscapeChars(String::from(",│`|:\"' ()[]{}<>")) } } #[serde(default)] #[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq)] pub struct Cursor { #[serde(deserialize_with = "failure_default")] pub style: CursorStyle, #[serde(deserialize_with = "failure_default")] unfocused_hollow: DefaultTrueBool, } impl Default for Cursor { fn default() -> Self { Self { style: Default::default(), unfocused_hollow: Default::default() } } } impl Cursor { pub fn unfocused_hollow(self) -> bool { self.unfocused_hollow.0 } } #[derive(Debug, Deserialize, PartialEq, Eq)] pub struct Shell<'a> { pub program: Cow<'a, str>, #[serde(default, deserialize_with = "failure_default")] pub args: Vec, } impl<'a> Shell<'a> { pub fn new(program: S) -> Shell<'a> where S: Into>, { Shell { program: program.into(), args: Vec::new() } } pub fn new_with_args(program: S, args: Vec) -> Shell<'a> where S: Into>, { Shell { program: program.into(), args } } } /// A delta for a point in a 2 dimensional plane #[serde(default, bound(deserialize = "T: Deserialize<'de> + Default"))] #[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)] pub struct Delta { /// Horizontal change #[serde(deserialize_with = "failure_default")] pub x: T, /// Vertical change #[serde(deserialize_with = "failure_default")] pub y: T, } /// Wrapper around f32 that represents an alpha value between 0.0 and 1.0 #[derive(Clone, Copy, Debug, PartialEq)] pub struct Alpha(f32); impl Alpha { pub fn new(value: f32) -> Self { Alpha(if value < 0.0 { 0.0 } else if value > 1.0 { 1.0 } else { value }) } } impl Default for Alpha { fn default() -> Self { Alpha(1.0) } } impl<'a> Deserialize<'a> for Alpha { fn deserialize(deserializer: D) -> Result where D: Deserializer<'a>, { Ok(Alpha::new(f32::deserialize(deserializer)?)) } } #[derive(Deserialize, Debug, PartialEq, Eq)] struct Tabspaces(usize); impl Default for Tabspaces { fn default() -> Self { Tabspaces(8) } } #[derive(Deserialize, Copy, Clone, Debug, PartialEq, Eq)] struct DefaultTrueBool(bool); impl Default for DefaultTrueBool { fn default() -> Self { DefaultTrueBool(true) } } pub fn failure_default<'a, D, T>(deserializer: D) -> Result where D: Deserializer<'a>, T: Deserialize<'a> + Default, { let value = serde_yaml::Value::deserialize(deserializer)?; match T::deserialize(value) { Ok(value) => Ok(value), Err(err) => { error!("Problem with config: {}; using default value", err); Ok(T::default()) }, } }