From 18cf806a27f06185b1ceb2d63f3b9bc2dd3dc80e Mon Sep 17 00:00:00 2001 From: Kirill Chibisov Date: Sat, 11 Jul 2020 20:03:09 +0300 Subject: Remove gui dependencies from alacritty_terminal This commit removes font dependency from alacritty_terminal, so it'll simplify the usage of alacritty_terminal as a library, since you won't link to system's libraries anymore. It also moves many alacritty related config options from it. Fixes #3393. --- alacritty_terminal/Cargo.toml | 5 - alacritty_terminal/src/config/debug.rs | 64 ---- alacritty_terminal/src/config/font.rs | 216 ------------- alacritty_terminal/src/config/mod.rs | 106 +------ alacritty_terminal/src/config/window.rs | 215 ------------- alacritty_terminal/src/event.rs | 2 + alacritty_terminal/src/event_loop.rs | 10 +- alacritty_terminal/src/lib.rs | 10 - alacritty_terminal/src/locale.rs | 99 ------ alacritty_terminal/src/message_bar.rs | 433 --------------------------- alacritty_terminal/src/meter.rs | 97 ------ alacritty_terminal/src/panic.rs | 30 -- alacritty_terminal/src/term/mod.rs | 32 +- alacritty_terminal/src/tty/windows/conpty.rs | 3 +- 14 files changed, 24 insertions(+), 1298 deletions(-) delete mode 100644 alacritty_terminal/src/config/debug.rs delete mode 100644 alacritty_terminal/src/config/font.rs delete mode 100644 alacritty_terminal/src/config/window.rs delete mode 100644 alacritty_terminal/src/locale.rs delete mode 100644 alacritty_terminal/src/message_bar.rs delete mode 100644 alacritty_terminal/src/meter.rs delete mode 100644 alacritty_terminal/src/panic.rs (limited to 'alacritty_terminal') diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index 3e796ba6..15edec3f 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -11,7 +11,6 @@ edition = "2018" [dependencies] libc = "0.2" bitflags = "1" -font = { path = "../font", features = ["force_system_fontconfig"] } parking_lot = "0.10.2" serde = { version = "1", features = ["derive"] } serde_yaml = "0.8" @@ -38,12 +37,8 @@ winapi = { version = "0.3.7", features = [ ]} mio-anonymous-pipes = "0.1" -[target.'cfg(target_os = "macos")'.dependencies] -objc = "0.2.2" - [features] default = ["winpty"] -nightly = [] bench = [] [dev-dependencies] diff --git a/alacritty_terminal/src/config/debug.rs b/alacritty_terminal/src/config/debug.rs deleted file mode 100644 index 9c9d4fde..00000000 --- a/alacritty_terminal/src/config/debug.rs +++ /dev/null @@ -1,64 +0,0 @@ -use log::{error, LevelFilter}; -use serde::{Deserialize, Deserializer}; - -use crate::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 -where - D: Deserializer<'a>, -{ - Ok(match failure_default::(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_terminal/src/config/font.rs b/alacritty_terminal/src/config/font.rs deleted file mode 100644 index 6a9120c9..00000000 --- a/alacritty_terminal/src/config/font.rs +++ /dev/null @@ -1,216 +0,0 @@ -use std::fmt; - -use font::Size; -use log::error; -use serde::de::Visitor; -use serde::{Deserialize, Deserializer}; - -#[cfg(target_os = "macos")] -use crate::config::DefaultTrueBool; -use crate::config::{failure_default, Delta, LOG_TARGET_CONFIG}; - -/// 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, - - /// Glyph offset within character cell. - #[serde(deserialize_with = "failure_default")] - pub glyph_offset: Delta, - - #[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, -} - -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, - #[serde(deserialize_with = "failure_default")] - style: Option, -} - -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 - where - D: serde::de::Deserializer<'a>; -} - -impl DeserializeSize for Size { - fn deserialize<'a, D>(deserializer: D) -> ::std::result::Result - 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(self, value: f64) -> ::std::result::Result - where - E: ::serde::de::Error, - { - Ok(value) - } - - fn visit_u64(self, value: u64) -> ::std::result::Result - where - E: ::serde::de::Error, - { - Ok(value as f64) - } - } - - let value = serde_yaml::Value::deserialize(deserializer)?; - let size = value - .deserialize_any(NumVisitor:: { _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_terminal/src/config/mod.rs b/alacritty_terminal/src/config/mod.rs index 83dcd7b8..98579a69 100644 --- a/alacritty_terminal/src/config/mod.rs +++ b/alacritty_terminal/src/config/mod.rs @@ -8,19 +8,13 @@ use serde_yaml::Value; mod bell; mod colors; -mod debug; -mod font; mod scrolling; -mod window; use crate::ansi::CursorStyle; pub use crate::config::bell::{BellAnimation, BellConfig}; 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::window::{Decorations, Dimensions, StartupMode, WindowConfig, DEFAULT_NAME}; pub const LOG_TARGET_CONFIG: &str = "alacritty_config"; const MAX_SCROLLBACK_LINES: u32 = 100_000; @@ -31,18 +25,10 @@ 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, @@ -50,14 +36,6 @@ pub struct Config { #[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, @@ -73,14 +51,6 @@ pub struct Config { #[serde(default, deserialize_with = "failure_default")] bell: BellConfig, - /// 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, @@ -94,18 +64,10 @@ pub struct Config { #[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, @@ -121,14 +83,6 @@ pub struct Config { // 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 { @@ -137,50 +91,6 @@ impl Config { 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 - } - - #[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 - } - #[inline] pub fn bell(&self) -> &BellConfig { self.visual_bell.as_ref().unwrap_or(&self.bell) @@ -294,18 +204,6 @@ impl Program { } } -/// 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); @@ -320,6 +218,10 @@ impl Percentage { value }) } + + pub fn as_f32(self) -> f32 { + self.0 + } } impl Default for Percentage { diff --git a/alacritty_terminal/src/config/window.rs b/alacritty_terminal/src/config/window.rs deleted file mode 100644 index b410f0a2..00000000 --- a/alacritty_terminal/src/config/window.rs +++ /dev/null @@ -1,215 +0,0 @@ -use std::os::raw::c_ulong; - -use log::error; -use serde::{Deserialize, Deserializer}; -use serde_yaml::Value; - -use crate::config::{failure_default, option_explicit_none, Delta, LOG_TARGET_CONFIG}; -use crate::index::{Column, Line}; - -/// 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>, - - /// Pixel padding. - #[serde(deserialize_with = "failure_default")] - pub padding: Delta, - - /// 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")] - 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, - - /// GTK theme variant. - #[serde(deserialize_with = "option_explicit_none")] - pub gtk_theme_variant: Option, - - // TODO: DEPRECATED - #[serde(deserialize_with = "failure_default")] - pub start_maximized: Option, -} - -pub fn default_title() -> String { - DEFAULT_NAME.to_string() -} - -impl WindowConfig { - pub fn startup_mode(&self) -> StartupMode { - match self.start_maximized { - Some(true) => StartupMode::Maximized, - _ => self.startup_mode, - } - } -} - -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(), - start_maximized: Default::default(), - title: default_title(), - } - } -} - -#[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 -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 -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()) - }, - } -} diff --git a/alacritty_terminal/src/event.rs b/alacritty_terminal/src/event.rs index 863a2fbf..c7129b24 100644 --- a/alacritty_terminal/src/event.rs +++ b/alacritty_terminal/src/event.rs @@ -8,6 +8,7 @@ use crate::term::{ClipboardType, SizeInfo}; pub enum Event { MouseCursorDirty, Title(String), + ResetTitle, ClipboardStore(ClipboardType, String), ClipboardLoad(ClipboardType, Arc String + Sync + Send + 'static>), Wakeup, @@ -20,6 +21,7 @@ impl Debug for Event { match self { Event::MouseCursorDirty => write!(f, "MouseCursorDirty"), Event::Title(title) => write!(f, "Title({})", title), + Event::ResetTitle => write!(f, "ResetTitle"), Event::ClipboardStore(ty, text) => write!(f, "ClipboardStore({:?}, {})", ty, text), Event::ClipboardLoad(ty, _) => write!(f, "ClipboardLoad({:?})", ty), Event::Wakeup => write!(f, "Wakeup"), diff --git a/alacritty_terminal/src/event_loop.rs b/alacritty_terminal/src/event_loop.rs index 156510b0..43e8a302 100644 --- a/alacritty_terminal/src/event_loop.rs +++ b/alacritty_terminal/src/event_loop.rs @@ -15,7 +15,6 @@ use mio::{self, Events, PollOpt, Ready}; use mio_extras::channel::{self, Receiver, Sender}; use crate::ansi; -use crate::config::Config; use crate::event::{self, Event, EventListener}; use crate::sync::FairMutex; use crate::term::{SizeInfo, Term}; @@ -155,11 +154,12 @@ where U: EventListener + Send + 'static, { /// Create a new event loop. - pub fn new( + pub fn new( terminal: Arc>>, event_proxy: U, pty: T, - config: &Config, + hold: bool, + ref_test: bool, ) -> EventLoop { let (tx, rx) = channel::channel(); EventLoop { @@ -169,8 +169,8 @@ where rx, terminal, event_proxy, - hold: config.hold, - ref_test: config.debug.ref_test, + hold, + ref_test, } } diff --git a/alacritty_terminal/src/lib.rs b/alacritty_terminal/src/lib.rs index 7130218b..2f3beeb0 100644 --- a/alacritty_terminal/src/lib.rs +++ b/alacritty_terminal/src/lib.rs @@ -1,24 +1,14 @@ //! Alacritty - The GPU Enhanced Terminal. #![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)] -#![cfg_attr(feature = "nightly", feature(core_intrinsics))] #![cfg_attr(all(test, feature = "bench"), feature(test))] -#[cfg(target_os = "macos")] -#[macro_use] -extern crate objc; - pub mod ansi; pub mod config; pub mod event; pub mod event_loop; pub mod grid; pub mod index; -#[cfg(target_os = "macos")] -pub mod locale; -pub mod message_bar; -pub mod meter; -pub mod panic; pub mod selection; pub mod sync; pub mod term; diff --git a/alacritty_terminal/src/locale.rs b/alacritty_terminal/src/locale.rs deleted file mode 100644 index 0a72e775..00000000 --- a/alacritty_terminal/src/locale.rs +++ /dev/null @@ -1,99 +0,0 @@ -#![allow(clippy::let_unit_value)] - -use std::env; -use std::ffi::{CStr, CString}; -use std::os::raw::c_char; -use std::slice; -use std::str; - -use libc::{setlocale, LC_ALL, LC_CTYPE}; -use log::debug; -use objc::runtime::{Class, Object}; - -const FALLBACK_LOCALE: &str = "UTF-8"; - -pub fn set_locale_environment() { - let env_locale_c = CString::new("").unwrap(); - let env_locale_ptr = unsafe { setlocale(LC_ALL, env_locale_c.as_ptr()) }; - if !env_locale_ptr.is_null() { - let env_locale = unsafe { CStr::from_ptr(env_locale_ptr).to_string_lossy() }; - - // Assume `C` locale means unchanged, since it is the default anyways. - if env_locale != "C" { - debug!("Using environment locale: {}", env_locale); - return; - } - } - - let system_locale = system_locale(); - - // Set locale to system locale. - let system_locale_c = CString::new(system_locale.clone()).expect("nul byte in system locale"); - let lc_all = unsafe { setlocale(LC_ALL, system_locale_c.as_ptr()) }; - - // Check if system locale was valid or not. - if lc_all.is_null() { - // Use fallback locale. - debug!("Using fallback locale: {}", FALLBACK_LOCALE); - - let fallback_locale_c = CString::new(FALLBACK_LOCALE).unwrap(); - unsafe { setlocale(LC_CTYPE, fallback_locale_c.as_ptr()) }; - - env::set_var("LC_CTYPE", FALLBACK_LOCALE); - } else { - // Use system locale. - debug!("Using system locale: {}", system_locale); - - env::set_var("LC_ALL", system_locale); - } -} - -/// Determine system locale based on language and country code. -fn system_locale() -> String { - unsafe { - let locale_class = Class::get("NSLocale").unwrap(); - let locale: *const Object = msg_send![locale_class, currentLocale]; - let _: () = msg_send![locale_class, release]; - - // `localeIdentifier` returns extra metadata with the locale (including currency and - // collator) on newer versions of macOS. This is not a valid locale, so we use - // `languageCode` and `countryCode`, if they're available (macOS 10.12+): - // - // https://developer.apple.com/documentation/foundation/nslocale/1416263-localeidentifier?language=objc - // https://developer.apple.com/documentation/foundation/nslocale/1643060-countrycode?language=objc - // https://developer.apple.com/documentation/foundation/nslocale/1643026-languagecode?language=objc - let is_language_code_supported: bool = - msg_send![locale, respondsToSelector: sel!(languageCode)]; - let is_country_code_supported: bool = - msg_send![locale, respondsToSelector: sel!(countryCode)]; - let locale_id = if is_language_code_supported && is_country_code_supported { - let language_code: *const Object = msg_send![locale, languageCode]; - let language_code_str = nsstring_as_str(language_code).to_owned(); - let _: () = msg_send![language_code, release]; - - let country_code: *const Object = msg_send![locale, countryCode]; - let country_code_str = nsstring_as_str(country_code).to_owned(); - let _: () = msg_send![country_code, release]; - - format!("{}_{}.UTF-8", &language_code_str, &country_code_str) - } else { - let identifier: *const Object = msg_send![locale, localeIdentifier]; - let identifier_str = nsstring_as_str(identifier).to_owned(); - let _: () = msg_send![identifier, release]; - - identifier_str + ".UTF-8" - }; - - let _: () = msg_send![locale, release]; - - locale_id - } -} - -const UTF8_ENCODING: usize = 4; - -unsafe fn nsstring_as_str<'a>(nsstring: *const Object) -> &'a str { - let cstr: *const c_char = msg_send![nsstring, UTF8String]; - let len: usize = msg_send![nsstring, lengthOfBytesUsingEncoding: UTF8_ENCODING]; - str::from_utf8(slice::from_raw_parts(cstr as *const u8, len)).unwrap() -} diff --git a/alacritty_terminal/src/message_bar.rs b/alacritty_terminal/src/message_bar.rs deleted file mode 100644 index 240581ff..00000000 --- a/alacritty_terminal/src/message_bar.rs +++ /dev/null @@ -1,433 +0,0 @@ -use std::collections::VecDeque; - -use crate::term::color::Rgb; -use crate::term::SizeInfo; - -pub const CLOSE_BUTTON_TEXT: &str = "[X]"; -const CLOSE_BUTTON_PADDING: usize = 1; -const MIN_FREE_LINES: usize = 3; -const TRUNCATED_MESSAGE: &str = "[MESSAGE TRUNCATED]"; - -/// Message for display in the MessageBuffer. -#[derive(Debug, Eq, PartialEq, Clone)] -pub struct Message { - text: String, - color: Rgb, - target: Option, -} - -impl Message { - /// Create a new message. - pub fn new(text: String, color: Rgb) -> Message { - Message { text, color, target: None } - } - - /// Formatted message text lines. - pub fn text(&self, size_info: &SizeInfo) -> Vec { - let num_cols = size_info.cols().0; - let max_lines = size_info.lines().saturating_sub(MIN_FREE_LINES); - let button_len = CLOSE_BUTTON_TEXT.len(); - - // Split line to fit the screen. - let mut lines = Vec::new(); - let mut line = String::new(); - for c in self.text.trim().chars() { - if c == '\n' - || line.len() == num_cols - // Keep space in first line for button. - || (lines.is_empty() - && num_cols >= button_len - && line.len() == num_cols.saturating_sub(button_len + CLOSE_BUTTON_PADDING)) - { - // Attempt to wrap on word boundaries. - if let (Some(index), true) = (line.rfind(char::is_whitespace), c != '\n') { - let split = line.split_off(index + 1); - line.pop(); - lines.push(Self::pad_text(line, num_cols)); - line = split - } else { - lines.push(Self::pad_text(line, num_cols)); - line = String::new(); - } - } - - if c != '\n' { - line.push(c); - } - } - lines.push(Self::pad_text(line, num_cols)); - - // Truncate output if it's too long. - if lines.len() > max_lines { - lines.truncate(max_lines); - if TRUNCATED_MESSAGE.len() <= num_cols { - if let Some(line) = lines.iter_mut().last() { - *line = Self::pad_text(TRUNCATED_MESSAGE.into(), num_cols); - } - } - } - - // Append close button to first line. - if button_len <= num_cols { - if let Some(line) = lines.get_mut(0) { - line.truncate(num_cols - button_len); - line.push_str(CLOSE_BUTTON_TEXT); - } - } - - lines - } - - /// Message color. - #[inline] - pub fn color(&self) -> Rgb { - self.color - } - - /// Message target. - #[inline] - pub fn target(&self) -> Option<&String> { - self.target.as_ref() - } - - /// Update the message target. - #[inline] - pub fn set_target(&mut self, target: String) { - self.target = Some(target); - } - - /// Right-pad text to fit a specific number of columns. - #[inline] - fn pad_text(mut text: String, num_cols: usize) -> String { - let padding_len = num_cols.saturating_sub(text.len()); - text.extend(vec![' '; padding_len]); - text - } -} - -/// Storage for message bar. -#[derive(Debug, Default)] -pub struct MessageBuffer { - messages: VecDeque, -} - -impl MessageBuffer { - /// Create new message buffer. - pub fn new() -> MessageBuffer { - MessageBuffer { messages: VecDeque::new() } - } - - /// Check if there are any messages queued. - #[inline] - pub fn is_empty(&self) -> bool { - self.messages.is_empty() - } - - /// Current message. - #[inline] - pub fn message(&self) -> Option<&Message> { - self.messages.front() - } - - /// Remove the currently visible message. - #[inline] - pub fn pop(&mut self) { - // Remove the message itself. - let msg = self.messages.pop_front(); - - // Remove all duplicates. - if let Some(msg) = msg { - self.messages = self.messages.drain(..).filter(|m| m != &msg).collect(); - } - } - - /// Remove all messages with a specific target. - #[inline] - pub fn remove_target(&mut self, target: &str) { - self.messages = self - .messages - .drain(..) - .filter(|m| m.target().map(String::as_str) != Some(target)) - .collect(); - } - - /// Add a new message to the queue. - #[inline] - pub fn push(&mut self, message: Message) { - self.messages.push_back(message); - } -} - -#[cfg(test)] -mod tests { - use super::{Message, MessageBuffer, MIN_FREE_LINES}; - use crate::term::{color, SizeInfo}; - - #[test] - fn appends_close_button() { - let input = "a"; - let mut message_buffer = MessageBuffer::new(); - message_buffer.push(Message::new(input.into(), color::RED)); - let size = SizeInfo { - width: 7., - height: 10., - cell_width: 1., - cell_height: 1., - padding_x: 0., - padding_y: 0., - dpr: 0., - }; - - let lines = message_buffer.message().unwrap().text(&size); - - assert_eq!(lines, vec![String::from("a [X]")]); - } - - #[test] - fn multiline_close_button_first_line() { - let input = "fo\nbar"; - let mut message_buffer = MessageBuffer::new(); - message_buffer.push(Message::new(input.into(), color::RED)); - let size = SizeInfo { - width: 6., - height: 10., - cell_width: 1., - cell_height: 1., - padding_x: 0., - padding_y: 0., - dpr: 0., - }; - - let lines = message_buffer.message().unwrap().text(&size); - - assert_eq!(lines, vec![String::from("fo [X]"), String::from("bar ")]); - } - - #[test] - fn splits_on_newline() { - let input = "a\nb"; - let mut message_buffer = MessageBuffer::new(); - message_buffer.push(Message::new(input.into(), color::RED)); - let size = SizeInfo { - width: 6., - height: 10., - cell_width: 1., - cell_height: 1., - padding_x: 0., - padding_y: 0., - dpr: 0., - }; - - let lines = message_buffer.message().unwrap().text(&size); - - assert_eq!(lines.len(), 2); - } - - #[test] - fn splits_on_length() { - let input = "foobar1"; - let mut message_buffer = MessageBuffer::new(); - message_buffer.push(Message::new(input.into(), color::RED)); - let size = SizeInfo { - width: 6., - height: 10., - cell_width: 1., - cell_height: 1., - padding_x: 0., - padding_y: 0., - dpr: 0., - }; - - let lines = message_buffer.message().unwrap().text(&size); - - assert_eq!(lines.len(), 2); - } - - #[test] - fn empty_with_shortterm() { - let input = "foobar"; - let mut message_buffer = MessageBuffer::new(); - message_buffer.push(Message::new(input.into(), color::RED)); - let size = SizeInfo { - width: 6., - height: 0., - cell_width: 1., - cell_height: 1., - padding_x: 0., - padding_y: 0., - dpr: 0., - }; - - let lines = message_buffer.message().unwrap().text(&size); - - assert_eq!(lines.len(), 0); - } - - #[test] - fn truncates_long_messages() { - let input = "hahahahahahahahahahaha truncate this because it's too long for the term"; - let mut message_buffer = MessageBuffer::new(); - message_buffer.push(Message::new(input.into(), color::RED)); - let size = SizeInfo { - width: 22., - height: (MIN_FREE_LINES + 2) as f32, - cell_width: 1., - cell_height: 1., - padding_x: 0., - padding_y: 0., - dpr: 0., - }; - - let lines = message_buffer.message().unwrap().text(&size); - - assert_eq!(lines, vec![ - String::from("hahahahahahahahaha [X]"), - String::from("[MESSAGE TRUNCATED] ") - ]); - } - - #[test] - fn hide_button_when_too_narrow() { - let input = "ha"; - let mut message_buffer = MessageBuffer::new(); - message_buffer.push(Message::new(input.into(), color::RED)); - let size = SizeInfo { - width: 2., - height: 10., - cell_width: 1., - cell_height: 1., - padding_x: 0., - padding_y: 0., - dpr: 0., - }; - - let lines = message_buffer.message().unwrap().text(&size); - - assert_eq!(lines, vec![String::from("ha")]); - } - - #[test] - fn hide_truncated_when_too_narrow() { - let input = "hahahahahahahahaha"; - let mut message_buffer = MessageBuffer::new(); - message_buffer.push(Message::new(input.into(), color::RED)); - let size = SizeInfo { - width: 2., - height: (MIN_FREE_LINES + 2) as f32, - cell_width: 1., - cell_height: 1., - padding_x: 0., - padding_y: 0., - dpr: 0., - }; - - let lines = message_buffer.message().unwrap().text(&size); - - assert_eq!(lines, vec![String::from("ha"), String::from("ha")]); - } - - #[test] - fn add_newline_for_button() { - let input = "test"; - let mut message_buffer = MessageBuffer::new(); - message_buffer.push(Message::new(input.into(), color::RED)); - let size = SizeInfo { - width: 5., - height: 10., - cell_width: 1., - cell_height: 1., - padding_x: 0., - padding_y: 0., - dpr: 0., - }; - - let lines = message_buffer.message().unwrap().text(&size); - - assert_eq!(lines, vec![String::from("t [X]"), String::from("est ")]); - } - - #[test] - fn remove_target() { - let mut message_buffer = MessageBuffer::new(); - for i in 0..10 { - let mut msg = Message::new(i.to_string(), color::RED); - if i % 2 == 0 && i < 5 { - msg.set_target("target".into()); - } - message_buffer.push(msg); - } - - message_buffer.remove_target("target"); - - // Count number of messages. - let mut num_messages = 0; - while message_buffer.message().is_some() { - num_messages += 1; - message_buffer.pop(); - } - - assert_eq!(num_messages, 7); - } - - #[test] - fn pop() { - let mut message_buffer = MessageBuffer::new(); - let one = Message::new(String::from("one"), color::RED); - message_buffer.push(one.clone()); - let two = Message::new(String::from("two"), color::YELLOW); - message_buffer.push(two.clone()); - - assert_eq!(message_buffer.message(), Some(&one)); - - message_buffer.pop(); - - assert_eq!(message_buffer.message(), Some(&two)); - } - - #[test] - fn wrap_on_words() { - let input = "a\nbc defg"; - let mut message_buffer = MessageBuffer::new(); - message_buffer.push(Message::new(input.into(), color::RED)); - let size = SizeInfo { - width: 5., - height: 10., - cell_width: 1., - cell_height: 1., - padding_x: 0., - padding_y: 0., - dpr: 0., - }; - - let lines = message_buffer.message().unwrap().text(&size); - - assert_eq!(lines, vec![ - String::from("a [X]"), - String::from("bc "), - String::from("defg ") - ]); - } - - #[test] - fn remove_duplicates() { - let mut message_buffer = MessageBuffer::new(); - for _ in 0..10 { - let msg = Message::new(String::from("test"), color::RED); - message_buffer.push(msg); - } - message_buffer.push(Message::new(String::from("other"), color::RED)); - message_buffer.push(Message::new(String::from("test"), color::YELLOW)); - let _ = message_buffer.message(); - - message_buffer.pop(); - - // Count number of messages. - let mut num_messages = 0; - while message_buffer.message().is_some() { - num_messages += 1; - message_buffer.pop(); - } - - assert_eq!(num_messages, 2); - } -} diff --git a/alacritty_terminal/src/meter.rs b/alacritty_terminal/src/meter.rs deleted file mode 100644 index c07d901f..00000000 --- a/alacritty_terminal/src/meter.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! Rendering time meter. -//! -//! Used to track rendering times and provide moving averages. -//! -//! # Examples -//! -//! ```rust -//! // create a meter -//! let mut meter = alacritty_terminal::meter::Meter::new(); -//! -//! // Sample something. -//! { -//! let _sampler = meter.sampler(); -//! } -//! -//! // Get the moving average. The meter tracks a fixed number of samples, and -//! // the average won't mean much until it's filled up at least once. -//! println!("Average time: {}", meter.average()); -//! ``` - -use std::time::{Duration, Instant}; - -const NUM_SAMPLES: usize = 10; - -/// The meter. -#[derive(Default)] -pub struct Meter { - /// Track last 60 timestamps. - times: [f64; NUM_SAMPLES], - - /// Average sample time in microseconds. - avg: f64, - - /// Index of next time to update.. - index: usize, -} - -/// Sampler. -/// -/// Samplers record how long they are "alive" for and update the meter on drop.. -pub struct Sampler<'a> { - /// Reference to meter that created the sampler. - meter: &'a mut Meter, - - /// When the sampler was created. - created_at: Instant, -} - -impl<'a> Sampler<'a> { - fn new(meter: &'a mut Meter) -> Sampler<'a> { - Sampler { meter, created_at: Instant::now() } - } - - #[inline] - fn alive_duration(&self) -> Duration { - self.created_at.elapsed() - } -} - -impl<'a> Drop for Sampler<'a> { - fn drop(&mut self) { - self.meter.add_sample(self.alive_duration()); - } -} - -impl Meter { - /// Create a meter. - pub fn new() -> Meter { - Default::default() - } - - /// Get a sampler. - pub fn sampler(&mut self) -> Sampler<'_> { - Sampler::new(self) - } - - /// Get the current average sample duration in microseconds. - pub fn average(&self) -> f64 { - self.avg - } - - /// Add a sample. - /// - /// Used by Sampler::drop. - fn add_sample(&mut self, sample: Duration) { - let mut usec = 0f64; - - usec += f64::from(sample.subsec_nanos()) / 1e3; - usec += (sample.as_secs() as f64) * 1e6; - - let prev = self.times[self.index]; - self.times[self.index] = usec; - self.avg -= prev / NUM_SAMPLES as f64; - self.avg += usec / NUM_SAMPLES as f64; - self.index = (self.index + 1) % NUM_SAMPLES; - } -} diff --git a/alacritty_terminal/src/panic.rs b/alacritty_terminal/src/panic.rs deleted file mode 100644 index 4ec409ff..00000000 --- a/alacritty_terminal/src/panic.rs +++ /dev/null @@ -1,30 +0,0 @@ -#[cfg(windows)] -use crate::tty::windows::win32_string; - -// Use the default behavior of the other platforms. -#[cfg(not(windows))] -pub fn attach_handler() {} - -// Install a panic handler that renders the panic in a classical Windows error -// dialog box as well as writes the panic to STDERR. -#[cfg(windows)] -pub fn attach_handler() { - use std::{io, io::Write, panic, ptr}; - use winapi::um::winuser; - - panic::set_hook(Box::new(|panic_info| { - let _ = writeln!(io::stderr(), "{}", panic_info); - let msg = format!("{}\n\nPress Ctrl-C to Copy", panic_info); - unsafe { - winuser::MessageBoxW( - ptr::null_mut(), - win32_string(&msg).as_ptr(), - win32_string("Alacritty: Runtime Error").as_ptr(), - winuser::MB_ICONERROR - | winuser::MB_OK - | winuser::MB_SETFOREGROUND - | winuser::MB_TASKMODAL, - ); - } - })); -} diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index f3f22d47..5a59b55b 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -721,14 +721,8 @@ pub struct Term { /// Current title of the window. title: Option, - /// Default title for resetting it. - default_title: String, - - /// Whether to permit updating the terminal title. - dynamic_title: bool, - /// Stack of saved window titles. When a title is popped from this stack, the `title` for the - /// term is set, and the Glutin window's title attribute is changed through the event listener. + /// term is set. title_stack: Vec>, /// Current forwards and backwards buffer search regexes. @@ -777,11 +771,9 @@ impl Term { cursor_style: None, default_cursor_style: config.cursor.style, vi_mode_cursor_style: config.cursor.vi_mode_style, - dynamic_title: config.dynamic_title(), event_proxy, is_focused: true, title: None, - default_title: config.window.title.clone(), title_stack: Vec::new(), selection: None, regex_search: None, @@ -808,14 +800,12 @@ impl Term { self.default_cursor_style = config.cursor.style; self.vi_mode_cursor_style = config.cursor.vi_mode_style; - self.default_title = config.window.title.clone(); - self.dynamic_title = config.dynamic_title(); + let title_event = match &self.title { + Some(title) => Event::Title(title.clone()), + None => Event::ResetTitle, + }; - if self.dynamic_title { - self.set_title(self.title.clone()); - } else { - self.event_proxy.send_event(Event::Title(self.default_title.clone())); - } + self.event_proxy.send_event(title_event); if self.mode.contains(TermMode::ALT_SCREEN) { self.inactive_grid.update_history(config.scrolling.history() as usize); @@ -2167,10 +2157,12 @@ impl Handler for Term { self.title = title.clone(); - if self.dynamic_title { - let title = title.unwrap_or_else(|| self.default_title.clone()); - self.event_proxy.send_event(Event::Title(title)); - } + let title_event = match title { + Some(title) => Event::Title(title), + None => Event::ResetTitle, + }; + + self.event_proxy.send_event(title_event); } #[inline] diff --git a/alacritty_terminal/src/tty/windows/conpty.rs b/alacritty_terminal/src/tty/windows/conpty.rs index be147b49..fa9f8b5a 100644 --- a/alacritty_terminal/src/tty/windows/conpty.rs +++ b/alacritty_terminal/src/tty/windows/conpty.rs @@ -125,8 +125,7 @@ pub fn new(config: &Config, size: &SizeInfo, _window_id: Option) -> let mut startup_info_ex: STARTUPINFOEXW = Default::default(); - let title = win32_string(&config.window.title); - startup_info_ex.StartupInfo.lpTitle = title.as_ptr() as LPWSTR; + startup_info_ex.StartupInfo.lpTitle = std::ptr::null_mut() as LPWSTR; startup_info_ex.StartupInfo.cb = mem::size_of::() as u32; -- cgit v1.2.3-54-g00ecf