diff options
Diffstat (limited to 'alacritty/src/config')
-rw-r--r-- | alacritty/src/config/debug.rs | 64 | ||||
-rw-r--r-- | alacritty/src/config/font.rs | 218 | ||||
-rw-r--r-- | alacritty/src/config/mod.rs | 33 | ||||
-rw-r--r-- | alacritty/src/config/ui_config.rs | 97 | ||||
-rw-r--r-- | alacritty/src/config/window.rs | 220 |
5 files changed, 608 insertions, 24 deletions
diff --git a/alacritty/src/config/debug.rs b/alacritty/src/config/debug.rs new file mode 100644 index 00000000..62de0500 --- /dev/null +++ b/alacritty/src/config/debug.rs @@ -0,0 +1,64 @@ +use log::{error, LevelFilter}; +use serde::{Deserialize, Deserializer}; + +use alacritty_terminal::config::{failure_default, LOG_TARGET_CONFIG}; + +/// Debugging options. +#[serde(default)] +#[derive(Deserialize, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Debug { + #[serde(default = "default_log_level", deserialize_with = "deserialize_log_level")] + pub log_level: LevelFilter, + + #[serde(deserialize_with = "failure_default")] + pub print_events: bool, + + /// Keep the log file after quitting. + #[serde(deserialize_with = "failure_default")] + pub persistent_logging: bool, + + /// Should show render timer. + #[serde(deserialize_with = "failure_default")] + pub render_timer: bool, + + /// Record ref test. + #[serde(skip)] + pub ref_test: bool, +} + +impl Default for Debug { + fn default() -> Self { + Self { + log_level: default_log_level(), + print_events: Default::default(), + persistent_logging: Default::default(), + render_timer: Default::default(), + ref_test: Default::default(), + } + } +} + +fn default_log_level() -> LevelFilter { + LevelFilter::Warn +} + +fn deserialize_log_level<'a, D>(deserializer: D) -> Result<LevelFilter, D::Error> +where + D: Deserializer<'a>, +{ + Ok(match failure_default::<D, String>(deserializer)?.to_lowercase().as_str() { + "off" | "none" => LevelFilter::Off, + "error" => LevelFilter::Error, + "warn" => LevelFilter::Warn, + "info" => LevelFilter::Info, + "debug" => LevelFilter::Debug, + "trace" => LevelFilter::Trace, + level => { + error!( + target: LOG_TARGET_CONFIG, + "Problem with config: invalid log level {}; using level Warn", level + ); + default_log_level() + }, + }) +} diff --git a/alacritty/src/config/font.rs b/alacritty/src/config/font.rs new file mode 100644 index 00000000..f718587c --- /dev/null +++ b/alacritty/src/config/font.rs @@ -0,0 +1,218 @@ +use std::fmt; + +use font::Size; +use log::error; +use serde::de::Visitor; +use serde::{Deserialize, Deserializer}; + +use alacritty_terminal::config::{failure_default, LOG_TARGET_CONFIG}; + +#[cfg(target_os = "macos")] +use crate::config::ui_config::DefaultTrueBool; +use crate::config::ui_config::Delta; + +/// Font config. +/// +/// Defaults are provided at the level of this struct per platform, but not per +/// field in this struct. It might be nice in the future to have defaults for +/// each value independently. Alternatively, maybe erroring when the user +/// doesn't provide complete config is Ok. +#[serde(default)] +#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +pub struct Font { + /// Normal font face. + #[serde(deserialize_with = "failure_default")] + normal: FontDescription, + + /// Bold font face. + #[serde(deserialize_with = "failure_default")] + bold: SecondaryFontDescription, + + /// Italic font face. + #[serde(deserialize_with = "failure_default")] + italic: SecondaryFontDescription, + + /// Bold italic font face. + #[serde(deserialize_with = "failure_default")] + bold_italic: SecondaryFontDescription, + + /// Font size in points. + #[serde(deserialize_with = "DeserializeSize::deserialize")] + pub size: Size, + + /// Extra spacing per character. + #[serde(deserialize_with = "failure_default")] + pub offset: Delta<i8>, + + /// Glyph offset within character cell. + #[serde(deserialize_with = "failure_default")] + pub glyph_offset: Delta<i8>, + + #[cfg(target_os = "macos")] + #[serde(deserialize_with = "failure_default")] + use_thin_strokes: DefaultTrueBool, +} + +impl Default for Font { + fn default() -> Font { + Font { + size: default_font_size(), + normal: Default::default(), + bold: Default::default(), + italic: Default::default(), + bold_italic: Default::default(), + glyph_offset: Default::default(), + offset: Default::default(), + #[cfg(target_os = "macos")] + use_thin_strokes: Default::default(), + } + } +} + +impl Font { + /// Get a font clone with a size modification. + pub fn with_size(self, size: Size) -> Font { + Font { size, ..self } + } + + /// Get normal font description. + pub fn normal(&self) -> &FontDescription { + &self.normal + } + + /// Get bold font description. + pub fn bold(&self) -> FontDescription { + self.bold.desc(&self.normal) + } + + /// Get italic font description. + pub fn italic(&self) -> FontDescription { + self.italic.desc(&self.normal) + } + + /// Get bold italic font description. + pub fn bold_italic(&self) -> FontDescription { + self.bold_italic.desc(&self.normal) + } + + #[cfg(target_os = "macos")] + pub fn use_thin_strokes(&self) -> bool { + self.use_thin_strokes.0 + } + + #[cfg(not(target_os = "macos"))] + pub fn use_thin_strokes(&self) -> bool { + false + } +} + +fn default_font_size() -> Size { + Size::new(11.) +} + +/// Description of the normal font. +#[serde(default)] +#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +pub struct FontDescription { + #[serde(deserialize_with = "failure_default")] + pub family: String, + #[serde(deserialize_with = "failure_default")] + pub style: Option<String>, +} + +impl Default for FontDescription { + fn default() -> FontDescription { + FontDescription { + #[cfg(not(any(target_os = "macos", windows)))] + family: "monospace".into(), + #[cfg(target_os = "macos")] + family: "Menlo".into(), + #[cfg(windows)] + family: "Consolas".into(), + style: None, + } + } +} + +/// Description of the italic and bold font. +#[serde(default)] +#[derive(Debug, Default, Deserialize, Clone, PartialEq, Eq)] +pub struct SecondaryFontDescription { + #[serde(deserialize_with = "failure_default")] + family: Option<String>, + #[serde(deserialize_with = "failure_default")] + style: Option<String>, +} + +impl SecondaryFontDescription { + pub fn desc(&self, fallback: &FontDescription) -> FontDescription { + FontDescription { + family: self.family.clone().unwrap_or_else(|| fallback.family.clone()), + style: self.style.clone(), + } + } +} + +trait DeserializeSize: Sized { + fn deserialize<'a, D>(_: D) -> ::std::result::Result<Self, D::Error> + where + D: serde::de::Deserializer<'a>; +} + +impl DeserializeSize for Size { + fn deserialize<'a, D>(deserializer: D) -> ::std::result::Result<Self, D::Error> + where + D: serde::de::Deserializer<'a>, + { + use std::marker::PhantomData; + + struct NumVisitor<__D> { + _marker: PhantomData<__D>, + } + + impl<'a, __D> Visitor<'a> for NumVisitor<__D> + where + __D: serde::de::Deserializer<'a>, + { + type Value = f64; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("f64 or u64") + } + + fn visit_f64<E>(self, value: f64) -> ::std::result::Result<Self::Value, E> + where + E: ::serde::de::Error, + { + Ok(value) + } + + fn visit_u64<E>(self, value: u64) -> ::std::result::Result<Self::Value, E> + where + E: ::serde::de::Error, + { + Ok(value as f64) + } + } + + let value = serde_yaml::Value::deserialize(deserializer)?; + let size = value + .deserialize_any(NumVisitor::<D> { _marker: PhantomData }) + .map(|v| Size::new(v as _)); + + // Use default font size as fallback. + match size { + Ok(size) => Ok(size), + Err(err) => { + let size = default_font_size(); + error!( + target: LOG_TARGET_CONFIG, + "Problem with config: {}; using size {}", + err, + size.as_f32_pts() + ); + Ok(size) + }, + } + } +} diff --git a/alacritty/src/config/mod.rs b/alacritty/src/config/mod.rs index e6437d26..7fffcc39 100644 --- a/alacritty/src/config/mod.rs +++ b/alacritty/src/config/mod.rs @@ -11,9 +11,12 @@ use log::{error, warn}; use alacritty_terminal::config::{Config as TermConfig, LOG_TARGET_CONFIG}; mod bindings; +pub mod debug; +pub mod font; pub mod monitor; mod mouse; -mod ui_config; +pub mod ui_config; +pub mod window; pub use crate::config::bindings::{Action, Binding, Key, ViAction}; #[cfg(test)] @@ -172,27 +175,6 @@ fn parse_config(contents: &str) -> Result<Config> { } fn print_deprecation_warnings(config: &Config) { - if config.window.start_maximized.is_some() { - warn!( - target: LOG_TARGET_CONFIG, - "Config window.start_maximized is deprecated; please use window.startup_mode instead" - ); - } - - if config.render_timer.is_some() { - warn!( - target: LOG_TARGET_CONFIG, - "Config render_timer is deprecated; please use debug.render_timer instead" - ); - } - - if config.persistent_logging.is_some() { - warn!( - target: LOG_TARGET_CONFIG, - "Config persistent_logging is deprecated; please use debug.persistent_logging instead" - ); - } - if config.scrolling.faux_multiplier().is_some() { warn!( target: LOG_TARGET_CONFIG, @@ -224,6 +206,13 @@ fn print_deprecation_warnings(config: &Config) { "Config visual_bell has been deprecated; please use bell instead" ) } + + if config.ui_config.dynamic_title.is_some() { + warn!( + target: LOG_TARGET_CONFIG, + "Config dynamic_title is deprecated; please use window.dynamic_title instead", + ) + } } #[cfg(test)] diff --git a/alacritty/src/config/ui_config.rs b/alacritty/src/config/ui_config.rs index 49e54e05..a8b1749f 100644 --- a/alacritty/src/config/ui_config.rs +++ b/alacritty/src/config/ui_config.rs @@ -1,13 +1,24 @@ use log::error; use serde::{Deserialize, Deserializer}; -use alacritty_terminal::config::{failure_default, LOG_TARGET_CONFIG}; +use alacritty_terminal::config::{failure_default, Percentage, LOG_TARGET_CONFIG}; use crate::config::bindings::{self, Binding, KeyBinding, MouseBinding}; +use crate::config::debug::Debug; +use crate::config::font::Font; use crate::config::mouse::Mouse; +use crate::config::window::WindowConfig; #[derive(Debug, PartialEq, Deserialize)] pub struct UIConfig { + /// Font configuration. + #[serde(default, deserialize_with = "failure_default")] + pub font: Font, + + /// Window configuration. + #[serde(default, deserialize_with = "failure_default")] + pub window: WindowConfig, + #[serde(default, deserialize_with = "failure_default")] pub mouse: Mouse, @@ -18,18 +29,79 @@ pub struct UIConfig { /// Bindings for the mouse. #[serde(default = "default_mouse_bindings", deserialize_with = "deserialize_mouse_bindings")] pub mouse_bindings: Vec<MouseBinding>, + + /// Debug options. + #[serde(default, deserialize_with = "failure_default")] + pub debug: Debug, + + /// Send escape sequences using the alt key. + #[serde(default, deserialize_with = "failure_default")] + alt_send_esc: DefaultTrueBool, + + /// Live config reload. + #[serde(default, deserialize_with = "failure_default")] + live_config_reload: DefaultTrueBool, + + /// Background opacity from 0.0 to 1.0. + #[serde(default, deserialize_with = "failure_default")] + background_opacity: Percentage, + + // TODO: DEPRECATED + #[serde(default, deserialize_with = "failure_default")] + pub dynamic_title: Option<bool>, } impl Default for UIConfig { fn default() -> Self { UIConfig { - mouse: Mouse::default(), + font: Default::default(), + window: Default::default(), + mouse: Default::default(), key_bindings: default_key_bindings(), mouse_bindings: default_mouse_bindings(), + debug: Default::default(), + alt_send_esc: Default::default(), + background_opacity: Default::default(), + live_config_reload: Default::default(), + dynamic_title: Default::default(), } } } +impl UIConfig { + #[inline] + pub fn background_opacity(&self) -> f32 { + self.background_opacity.as_f32() + } + + #[inline] + pub fn dynamic_title(&self) -> bool { + self.dynamic_title.unwrap_or_else(|| self.window.dynamic_title()) + } + + #[inline] + pub fn set_dynamic_title(&mut self, dynamic_title: bool) { + self.window.set_dynamic_title(dynamic_title); + } + + /// 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; + } + + /// Send escape sequences using the alt key. + #[inline] + pub fn alt_send_esc(&self) -> bool { + self.alt_send_esc.0 + } +} + fn default_key_bindings() -> Vec<KeyBinding> { bindings::default_key_bindings() } @@ -83,3 +155,24 @@ where Ok(bindings) } + +#[derive(Deserialize, Copy, Clone, Debug, PartialEq, Eq)] +pub struct DefaultTrueBool(pub bool); + +impl Default for DefaultTrueBool { + fn default() -> Self { + DefaultTrueBool(true) + } +} + +/// 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<T: Default + PartialEq + Eq> { + /// Horizontal change. + #[serde(deserialize_with = "failure_default")] + pub x: T, + /// Vertical change. + #[serde(deserialize_with = "failure_default")] + pub y: T, +} diff --git a/alacritty/src/config/window.rs b/alacritty/src/config/window.rs new file mode 100644 index 00000000..f866e180 --- /dev/null +++ b/alacritty/src/config/window.rs @@ -0,0 +1,220 @@ +use std::os::raw::c_ulong; + +use log::error; +use serde::{Deserialize, Deserializer}; +use serde_yaml::Value; + +use alacritty_terminal::config::{failure_default, option_explicit_none, LOG_TARGET_CONFIG}; +use alacritty_terminal::index::{Column, Line}; + +use crate::config::ui_config::{DefaultTrueBool, Delta}; + +/// Default Alacritty name, used for window title and class. +pub const DEFAULT_NAME: &str = "Alacritty"; + +#[serde(default)] +#[derive(Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct WindowConfig { + /// Initial dimensions. + #[serde(deserialize_with = "failure_default")] + pub dimensions: Dimensions, + + /// Initial position. + #[serde(deserialize_with = "failure_default")] + pub position: Option<Delta<i32>>, + + /// Pixel padding. + #[serde(deserialize_with = "failure_default")] + pub padding: Delta<u8>, + + /// Draw the window with title bar / borders. + #[serde(deserialize_with = "failure_default")] + pub decorations: Decorations, + + /// Spread out additional padding evenly. + #[serde(deserialize_with = "failure_default")] + pub dynamic_padding: bool, + + /// Startup mode. + #[serde(deserialize_with = "failure_default")] + pub startup_mode: StartupMode, + + /// Window title. + #[serde(default = "default_title")] + pub title: String, + + /// Window class. + #[serde(deserialize_with = "deserialize_class")] + pub class: Class, + + /// XEmbed parent. + #[serde(skip)] + pub embed: Option<c_ulong>, + + /// GTK theme variant. + #[serde(deserialize_with = "option_explicit_none")] + pub gtk_theme_variant: Option<String>, + + /// Use dynamic title. + #[serde(default, deserialize_with = "failure_default")] + dynamic_title: DefaultTrueBool, +} + +pub fn default_title() -> String { + DEFAULT_NAME.to_string() +} + +impl WindowConfig { + #[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; + } +} + +impl Default for WindowConfig { + fn default() -> WindowConfig { + WindowConfig { + dimensions: Default::default(), + position: Default::default(), + padding: Default::default(), + decorations: Default::default(), + dynamic_padding: Default::default(), + startup_mode: Default::default(), + class: Default::default(), + embed: Default::default(), + gtk_theme_variant: Default::default(), + title: default_title(), + dynamic_title: Default::default(), + } + } +} + +#[derive(Debug, Deserialize, Copy, Clone, PartialEq, Eq)] +pub enum StartupMode { + Windowed, + Maximized, + Fullscreen, + #[cfg(target_os = "macos")] + SimpleFullscreen, +} + +impl Default for StartupMode { + fn default() -> StartupMode { + StartupMode::Windowed + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +pub enum Decorations { + #[serde(rename = "full")] + Full, + #[cfg(target_os = "macos")] + #[serde(rename = "transparent")] + Transparent, + #[cfg(target_os = "macos")] + #[serde(rename = "buttonless")] + Buttonless, + #[serde(rename = "none")] + None, +} + +impl Default for Decorations { + fn default() -> Decorations { + Decorations::Full + } +} + +/// Window Dimensions. +/// +/// Newtype to avoid passing values incorrectly. +#[serde(default)] +#[derive(Default, Debug, Copy, Clone, Deserialize, PartialEq, Eq)] +pub struct Dimensions { + /// Window width in character columns. + #[serde(deserialize_with = "failure_default")] + columns: Column, + + /// Window Height in character lines. + #[serde(deserialize_with = "failure_default")] + lines: Line, +} + +impl Dimensions { + pub fn new(columns: Column, lines: Line) -> Self { + Dimensions { columns, lines } + } + + /// Get lines. + #[inline] + pub fn lines_u32(&self) -> u32 { + self.lines.0 as u32 + } + + /// Get columns. + #[inline] + pub fn columns_u32(&self) -> u32 { + self.columns.0 as u32 + } +} + +/// Window class hint. +#[serde(default)] +#[derive(Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct Class { + #[serde(deserialize_with = "deserialize_class_resource")] + pub instance: String, + + #[serde(deserialize_with = "deserialize_class_resource")] + pub general: String, +} + +impl Default for Class { + fn default() -> Self { + Class { instance: DEFAULT_NAME.into(), general: DEFAULT_NAME.into() } + } +} + +fn deserialize_class_resource<'a, D>(deserializer: D) -> Result<String, D::Error> +where + D: Deserializer<'a>, +{ + let value = Value::deserialize(deserializer)?; + match String::deserialize(value) { + Ok(value) => Ok(value), + Err(err) => { + error!( + target: LOG_TARGET_CONFIG, + "Problem with config: {}, using default value {}", err, DEFAULT_NAME, + ); + + Ok(DEFAULT_NAME.into()) + }, + } +} + +fn deserialize_class<'a, D>(deserializer: D) -> Result<Class, D::Error> +where + D: Deserializer<'a>, +{ + let value = Value::deserialize(deserializer)?; + + if let Value::String(instance) = value { + return Ok(Class { instance, general: DEFAULT_NAME.into() }); + } + + match Class::deserialize(value) { + Ok(value) => Ok(value), + Err(err) => { + error!( + target: LOG_TARGET_CONFIG, + "Problem with config: {}; using class {}", err, DEFAULT_NAME + ); + Ok(Class::default()) + }, + } +} |