blob: d74390d889eb1f1908cfa85ab1c7fa25e23a255c [file] [log] [blame]
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;
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<Delta<i32>>,
/// Draw the window with title bar / borders.
pub decorations: Decorations,
/// Startup mode.
pub startup_mode: StartupMode,
/// XEmbed parent.
#[config(skip)]
pub embed: Option<c_ulong>,
/// GTK theme variant.
pub gtk_theme_variant: Option<String>,
/// 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<u8>,
/// 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<Dimensions> {
if self.dimensions.columns.0 != 0
&& self.dimensions.lines != 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<Fullscreen> {
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: usize,
}
/// 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<D>(deserializer: D) -> Result<Self, D::Error>
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<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Self::Value { instance: value.into(), ..Self::Value::default() })
}
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'a>,
{
let mut class = Self::Value::default();
while let Some((key, value)) = map.next_entry::<String, serde_yaml::Value>()? {
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)
}
}