// 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::fmt::Display; use std::path::PathBuf; use log::error; use serde::{Deserialize, Deserializer}; use serde_yaml::Value; mod colors; mod debug; mod font; mod scrolling; mod visual_bell; mod window; use crate::ansi::{CursorStyle, NamedColor}; pub use crate::config::colors::Colors; pub use crate::config::debug::Debug; pub use crate::config::font::{Font, FontDescription}; pub use crate::config::scrolling::Scrolling; pub use crate::config::visual_bell::{VisualBellAnimation, VisualBellConfig}; pub use crate::config::window::{Decorations, Dimensions, StartupMode, WindowConfig, DEFAULT_NAME}; use crate::term::color::Rgb; pub const LOG_TARGET_CONFIG: &str = "alacritty_config"; const MAX_SCROLLBACK_LINES: u32 = 100_000; const DEFAULT_CURSOR_THICKNESS: f32 = 0.15; pub type MockConfig = Config>; /// Top-level config type #[derive(Debug, PartialEq, Default, 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: bool, #[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: Percentage, /// Window configuration #[serde(default, deserialize_with = "failure_default")] pub window: WindowConfig, #[serde(default, deserialize_with = "failure_default")] pub selection: Selection, /// Path to a shell program to run on startup #[serde(default, deserialize_with = "from_string_or_deserialize")] 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, /// 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, /// Use WinPTY backend even if ConPTY is available #[cfg(windows)] #[serde(default, deserialize_with = "failure_default")] pub winpty_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 = "option_explicit_none")] pub working_directory: Option, /// Debug options #[serde(default, deserialize_with = "failure_default")] pub debug: Debug, /// Additional configuration options not directly required by the terminal #[serde(flatten)] pub ui_config: T, /// Remain open after child process exits #[serde(skip)] pub hold: bool, // TODO: REMOVED #[serde(default, deserialize_with = "failure_default")] pub tabspaces: Option, // 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 Config { #[inline] pub fn draw_bold_text_with_bright_colors(&self) -> bool { self.draw_bold_text_with_bright_colors } /// 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 } /// Cursor foreground color. #[inline] pub fn cursor_text_color(&self) -> Option { self.colors.cursor.text } /// Cursor background color. #[inline] pub fn cursor_cursor_color(&self) -> Option { self.colors.cursor.cursor.map(|_| NamedColor::Cursor) } /// Vi mode cursor foreground color. #[inline] pub fn vi_mode_cursor_text_color(&self) -> Option { self.colors.vi_mode_cursor.text } /// Vi mode cursor background color. #[inline] pub fn vi_mode_cursor_cursor_color(&self) -> Option { self.colors.vi_mode_cursor.cursor } #[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 as f32 } } #[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(",│`|:\"' ()[]{}<>\t")) } } #[serde(default)] #[derive(Deserialize, Copy, Clone, Debug, PartialEq)] pub struct Cursor { #[serde(deserialize_with = "failure_default")] pub style: CursorStyle, #[serde(deserialize_with = "option_explicit_none")] pub vi_mode_style: Option, #[serde(deserialize_with = "deserialize_cursor_thickness")] thickness: Percentage, #[serde(deserialize_with = "failure_default")] unfocused_hollow: DefaultTrueBool, } impl Cursor { #[inline] pub fn unfocused_hollow(self) -> bool { self.unfocused_hollow.0 } #[inline] pub fn thickness(self) -> f64 { self.thickness.0 as f64 } } impl Default for Cursor { fn default() -> Self { Self { style: Default::default(), vi_mode_style: Default::default(), thickness: Percentage::new(DEFAULT_CURSOR_THICKNESS), unfocused_hollow: Default::default(), } } } pub fn deserialize_cursor_thickness<'a, D>(deserializer: D) -> Result where D: Deserializer<'a>, { Ok(Percentage::deserialize(Value::deserialize(deserializer)?) .unwrap_or_else(|_| Percentage::new(DEFAULT_CURSOR_THICKNESS))) } #[derive(Clone, 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 } } } impl FromString for Option> { fn from(input: String) -> Self { Some(Shell::new(input)) } } /// 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 a percentage value between 0.0 and 1.0. #[derive(Clone, Copy, Debug, PartialEq)] pub struct Percentage(f32); impl Percentage { pub fn new(value: f32) -> Self { Percentage(if value < 0.0 { 0.0 } else if value > 1.0 { 1.0 } else { value }) } } impl Default for Percentage { fn default() -> Self { Percentage(1.0) } } impl<'a> Deserialize<'a> for Percentage { fn deserialize(deserializer: D) -> Result where D: Deserializer<'a>, { Ok(Percentage::new(f32::deserialize(deserializer)?)) } } #[derive(Deserialize, Copy, Clone, Debug, PartialEq, Eq)] struct DefaultTrueBool(bool); impl Default for DefaultTrueBool { fn default() -> Self { DefaultTrueBool(true) } } fn fallback_default(err: E) -> T where T: Default, E: Display, { error!(target: LOG_TARGET_CONFIG, "Problem with config: {}; using default value", err); T::default() } pub fn failure_default<'a, D, T>(deserializer: D) -> Result where D: Deserializer<'a>, T: Deserialize<'a> + Default, { Ok(T::deserialize(Value::deserialize(deserializer)?).unwrap_or_else(fallback_default)) } pub fn option_explicit_none<'de, T, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, T: Deserialize<'de> + Default, { Ok(match Value::deserialize(deserializer)? { Value::String(ref value) if value.to_lowercase() == "none" => None, value => Some(T::deserialize(value).unwrap_or_else(fallback_default)), }) } pub fn from_string_or_deserialize<'de, T, D>(deserializer: D) -> Result where D: Deserializer<'de>, T: Deserialize<'de> + FromString + Default, { Ok(match Value::deserialize(deserializer)? { Value::String(value) => T::from(value), value => T::deserialize(value).unwrap_or_else(fallback_default), }) } // Used over From, to allow implementation for foreign types pub trait FromString { fn from(input: String) -> Self; }