use std::fmt::{self, Formatter}; use std::os::raw::c_ulong; use glutin::window::Fullscreen; use log::error; use serde::de::{self, MapAccess, Visitor}; use serde::{Deserialize, Deserializer}; use alacritty_config_derive::ConfigDeserialize; use alacritty_terminal::config::LOG_TARGET_CONFIG; use alacritty_terminal::index::{Column, Line}; use crate::config::ui_config::Delta; /// Default Alacritty name, used for window title and class. pub const DEFAULT_NAME: &str = "Alacritty"; #[derive(ConfigDeserialize, Debug, Clone, PartialEq, Eq)] pub struct WindowConfig { /// Initial position. pub position: Option>, /// Draw the window with title bar / borders. pub decorations: Decorations, /// Startup mode. pub startup_mode: StartupMode, /// XEmbed parent. #[config(skip)] pub embed: Option, /// GTK theme variant. pub gtk_theme_variant: Option, /// Spread out additional padding evenly. pub dynamic_padding: bool, /// Use dynamic title. pub dynamic_title: bool, /// Window title. pub title: String, /// Window class. pub class: Class, /// Pixel padding. padding: Delta, /// Initial dimensions. dimensions: Dimensions, } impl Default for WindowConfig { fn default() -> Self { Self { dynamic_title: true, title: DEFAULT_NAME.into(), position: Default::default(), decorations: Default::default(), startup_mode: Default::default(), embed: Default::default(), gtk_theme_variant: Default::default(), dynamic_padding: Default::default(), class: Default::default(), padding: Default::default(), dimensions: Default::default(), } } } impl WindowConfig { #[inline] pub fn dimensions(&self) -> Option { if self.dimensions.columns.0 != 0 && self.dimensions.lines.0 != 0 && self.startup_mode != StartupMode::Maximized { Some(self.dimensions) } else { None } } #[inline] pub fn padding(&self, dpr: f64) -> (f32, f32) { let padding_x = (f32::from(self.padding.x) * dpr as f32).floor(); let padding_y = (f32::from(self.padding.y) * dpr as f32).floor(); (padding_x, padding_y) } #[inline] pub fn fullscreen(&self) -> Option { if self.startup_mode == StartupMode::Fullscreen { Some(Fullscreen::Borderless(None)) } else { None } } #[inline] pub fn maximized(&self) -> bool { self.startup_mode == StartupMode::Maximized } } #[derive(ConfigDeserialize, Debug, 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(ConfigDeserialize, Debug, Copy, Clone, PartialEq, Eq)] pub enum Decorations { Full, #[cfg(target_os = "macos")] Transparent, #[cfg(target_os = "macos")] Buttonless, None, } impl Default for Decorations { fn default() -> Decorations { Decorations::Full } } /// Window Dimensions. /// /// Newtype to avoid passing values incorrectly. #[derive(ConfigDeserialize, Default, Debug, Copy, Clone, PartialEq, Eq)] pub struct Dimensions { /// Window width in character columns. pub columns: Column, /// Window Height in character lines. pub lines: Line, } /// Window class hint. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Class { pub instance: String, pub general: String, } impl Default for Class { fn default() -> Self { Self { instance: DEFAULT_NAME.into(), general: DEFAULT_NAME.into() } } } impl<'de> Deserialize<'de> for Class { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct ClassVisitor; impl<'a> Visitor<'a> for ClassVisitor { type Value = Class; fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_str("a mapping") } fn visit_str(self, value: &str) -> Result where E: de::Error, { Ok(Self::Value { instance: value.into(), ..Self::Value::default() }) } fn visit_map(self, mut map: M) -> Result where M: MapAccess<'a>, { let mut class = Self::Value::default(); while let Some((key, value)) = map.next_entry::()? { match key.as_str() { "instance" => match String::deserialize(value) { Ok(instance) => class.instance = instance, Err(err) => { error!( target: LOG_TARGET_CONFIG, "Config error: class.instance: {}", err ); }, }, "general" => match String::deserialize(value) { Ok(general) => class.general = general, Err(err) => { error!( target: LOG_TARGET_CONFIG, "Config error: class.instance: {}", err ); }, }, _ => (), } } Ok(class) } } deserializer.deserialize_any(ClassVisitor) } }