1
0
Fork 0
mirror of https://github.com/alacritty/alacritty.git synced 2024-11-18 13:55:23 -05:00
This commit is contained in:
Christian Duerr 2018-02-20 18:23:06 +01:00
parent 2466f81d5e
commit 038899c5b3
No known key found for this signature in database
GPG key ID: 85CDAE3C164BA7B4
4 changed files with 283 additions and 280 deletions

212
src/config/font.rs Normal file
View file

@ -0,0 +1,212 @@
// 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 serde_yaml;
use serde::{de, Deserialize};
use config::{Delta, failure_default};
use font::{Size, FontKey, Slant};
// Global and local font configuration
#[derive(Deserialize, Debug)]
pub struct FontConfiguration {
#[serde(default, deserialize_with="failure_default")]
options: GlobalOptions,
#[serde(default, deserialize_with="deserialize_font_collection")]
fonts: Vec<Font>,
}
impl FontConfiguration {
pub fn font_by_char(&self, c: char, weight: Slant) -> FontKey {
for font in fonts {
let options = font.options();
// Skip font if font slant does not match requested slant
if options.unwrap_or(self.options as Options).style.unwrap_or(self.options.style) != weight {
continue;
}
let is_match = match options {
Some(options) => {
for range in options.ranges {
if range.start < c && range.end > c {
return true;
}
}
false
},
None => {
true
},
};
if is_match {
// TODO: Check if this font contains the char
}
}
}
}
impl Default for FontConfiguration {
fn default() -> Self {
Self {
fonts: vec!(Font::default()),
..Default::default()
}
}
}
// Information about a font
#[derive(Deserialize, Debug)]
pub struct Font {
family: String,
#[serde(deserialize_with="failure_default")]
options: Option<Options>,
}
// Default font config in case of failure or missing config
impl Default for Font {
#[cfg(target_os = "macos")]
fn default() -> Self {
Font {
family: "Menlo".into(),
options: None,
}
}
#[cfg(not(target_os = "macos"))]
fn default() -> Self {
Font {
family: "monospace".into(),
options: None,
}
}
}
// Options for a font
#[derive(Deserialize, Debug)]
pub struct Options {
#[serde(deserialize_with="deserialize_size")]
size: Option<Size>,
#[serde(deserialize_with="failure_default")]
thin_strokes: Option<bool>,
#[serde(deserialize_with="failure_default")]
antialias: Option<AntiAlias>,
#[serde(deserialize_with="failure_default")]
hinting: Option<bool>,
#[serde(deserialize_with="failure_default")]
style: Option<String>,
#[serde(deserialize_with="failure_default")]
offset: Option<Delta>,
#[serde(deserialize_with="failure_default")]
ranges: Vec<FontRange>,
}
impl Default for Options {
fn default() -> Self {
Options {
size: None,
thin_strokes: None,
antialias: None,
hinting: None,
style: None,
offset: None,
ranges: Vec::new(),
}
}
}
#[derive(Deserialize, Debug)]
pub struct GlobalOptions(Options);
impl Default for GlobalOptions {
fn default() -> Self {
GlobalOptions(Options {
size: Some(Size::new(12.0)),
thin_strokes: Some(true),
antialias: Some(AntiAlias::LCD),
hinting: Some(true),
style: Some("normal".into()),
offset: Some(Delta::default()),
ranges: Vec::new(),
})
}
}
// AntiAliasing settings for fonts
#[derive(Deserialize, Debug)]
pub enum AntiAlias {
LCD,
LCDV,
GRAY,
DISABLED,
}
// Range for which a specific font should be used
#[derive(Deserialize, Debug)]
pub struct FontRange {
#[serde(deserialize_with="failure_default")]
start: char,
#[serde(deserialize_with="failure_default")]
end: char,
}
// Deserialize the font vector
fn deserialize_font_collection<'a, D>(deserializer: D)
-> ::std::result::Result<Vec<Font>, D::Error>
where D: de::Deserializer<'a>,
{
// Deserialize vector as generic yaml value
let mut value = match serde_yaml::Value::deserialize(deserializer) {
Ok(value) => value,
Err(err) => {
eprintln!("problem with config: {}; Using default fonts", err);
return Ok(vec!(Font::default()));
},
};
// Get value as sequence
let sequence = match value.as_sequence_mut() {
Some(sequence) => sequence,
None => return Ok(vec!(Font::default())),
};
// Deserialize each element in the sequence
let mut font_collection = Vec::new();
for i in 0..sequence.len() {
match Font::deserialize(sequence.remove(i)) {
Ok(font) => font_collection.push(font),
// TODO: Print line or something like that?
Err(err) => eprintln!("problem with config: Malformed font; Skipping"),
}
}
// Return defaults if collection contains no font
if font_collection.is_empty() {
Ok(vec!(Font::default()))
} else {
Ok(font_collection)
}
}
// Deserialize font size
fn deserialize_size<'a, D>(deserializer: D)
-> ::std::result::Result<Option<Size>, D::Error>
where D: de::Deserializer<'a>,
{
match f32::deserialize(deserializer) {
Ok(value) => Ok(Some(Size::new(value))),
_ => {
Ok(None)
},
}
}

View file

@ -3,6 +3,8 @@
//! Alacritty reads from a config file at startup to determine various runtime
//! parameters including font family and style, font size, etc. In the future,
//! the config file will also hold user and platform specific keybindings.
pub mod font;
use std::borrow::Cow;
use std::{env, fmt};
use std::fs::{self, File};
@ -14,15 +16,15 @@ use std::time::Duration;
use std::collections::HashMap;
use ::Rgb;
use font::Size;
use serde_yaml;
use serde::{self, de, Deserialize};
use serde::{de, Deserialize};
use serde::de::Error as SerdeError;
use serde::de::{Visitor, MapAccess, Unexpected};
use notify::{Watcher, watcher, DebouncedEvent, RecursiveMode};
use glutin::ModifiersState;
use self::font::FontConfiguration;
use input::{Action, Binding, MouseBinding, KeyBinding};
use index::{Line, Column};
use ansi::CursorStyle;
@ -326,7 +328,7 @@ pub struct Config {
/// Font configuration
#[serde(default, deserialize_with = "failure_default")]
font: Font,
font: FontConfiguration,
/// Should show render timer
#[serde(default, deserialize_with = "failure_default")]
@ -453,7 +455,7 @@ fn default_true_bool<'a, D>(deserializer: D) -> ::std::result::Result<bool, D::E
}
}
fn failure_default<'a, D, T>(deserializer: D)
pub fn failure_default<'a, D, T>(deserializer: D)
-> ::std::result::Result<T, D::Error>
where D: de::Deserializer<'a>,
T: Deserialize<'a> + Default
@ -467,10 +469,8 @@ fn failure_default<'a, D, T>(deserializer: D)
}
}
#[cfg(not(target_os="macos"))]
static DEFAULT_ALACRITTY_CONFIG: &'static str = include_str!("../alacritty.yml");
#[cfg(target_os="macos")]
static DEFAULT_ALACRITTY_CONFIG: &'static str = include_str!("../alacritty_macos.yml");
static DEFAULT_ALACRITTY_CONFIG: &'static str =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty.yml"));
impl Default for Config {
fn default() -> Self {
@ -1295,12 +1295,6 @@ impl Config {
self.draw_bold_text_with_bright_colors
}
/// Get font config
#[inline]
pub fn font(&self) -> &Font {
&self.font
}
/// Get window dimensions
#[inline]
pub fn dimensions(&self) -> Dimensions {
@ -1325,11 +1319,6 @@ impl Config {
self.render_timer
}
#[inline]
pub fn use_thin_strokes(&self) -> bool {
self.font.use_thin_strokes
}
/// show cursor as inverted
#[inline]
pub fn custom_cursor_colors(&self) -> bool {
@ -1464,173 +1453,6 @@ impl Default for Delta {
}
}
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 size = deserializer
.deserialize_any(NumVisitor::<D>{ _marker: PhantomData })
.map(|v| Size::new(v as _));
// Use font size 12 as fallback
match size {
Ok(size) => Ok(size),
Err(err) => {
eprintln!("problem with config: {}; Using size 12", err);
Ok(Size::new(12.))
},
}
}
}
/// 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.
#[derive(Debug, Deserialize, Clone)]
pub struct Font {
/// Font family
pub normal: FontDescription,
#[serde(default="default_italic_desc")]
pub italic: FontDescription,
#[serde(default="default_bold_desc")]
pub bold: FontDescription,
// Font size in points
#[serde(deserialize_with="DeserializeSize::deserialize")]
pub size: Size,
/// Extra spacing per character
#[serde(default, deserialize_with = "failure_default")]
offset: Delta,
/// Glyph offset within character cell
#[serde(default, deserialize_with = "failure_default")]
glyph_offset: Delta,
#[serde(default="true_bool", deserialize_with = "default_true_bool")]
use_thin_strokes: bool
}
fn default_bold_desc() -> FontDescription {
Font::default().bold
}
fn default_italic_desc() -> FontDescription {
Font::default().italic
}
/// Description of a single font
#[derive(Debug, Deserialize, Clone)]
pub struct FontDescription {
pub family: String,
pub style: Option<String>,
}
impl FontDescription {
fn new_with_family<S: Into<String>>(family: S) -> FontDescription {
FontDescription {
family: family.into(),
style: None,
}
}
}
impl Font {
/// Get the font size in points
#[inline]
pub fn size(&self) -> Size {
self.size
}
/// Get offsets to font metrics
#[inline]
pub fn offset(&self) -> &Delta {
&self.offset
}
/// Get cell offsets for glyphs
#[inline]
pub fn glyph_offset(&self) -> &Delta {
&self.glyph_offset
}
/// Get a font clone with a size modification
pub fn with_size(self, size: Size) -> Font {
Font {
size,
.. self
}
}
}
#[cfg(target_os = "macos")]
impl Default for Font {
fn default() -> Font {
Font {
normal: FontDescription::new_with_family("Menlo"),
bold: FontDescription::new_with_family("Menlo"),
italic: FontDescription::new_with_family("Menlo"),
size: Size::new(11.0),
use_thin_strokes: true,
offset: Default::default(),
glyph_offset: Default::default()
}
}
}
#[cfg(any(target_os = "linux",target_os = "freebsd"))]
impl Default for Font {
fn default() -> Font {
Font {
normal: FontDescription::new_with_family("monospace"),
bold: FontDescription::new_with_family("monospace"),
italic: FontDescription::new_with_family("monospace"),
size: Size::new(11.0),
use_thin_strokes: false,
offset: Default::default(),
glyph_offset: Default::default()
}
}
}
pub struct Monitor {
_thread: ::std::thread::JoinHandle<()>,
rx: mpsc::Receiver<Config>,

43
src/font.rs Normal file
View file

@ -0,0 +1,43 @@
// 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 config::Delta;
use font::Size;
struct Font {
family: String,
options: Option<Options>,
}
enum AntiAlias {
LCD,
LCDV,
GRAY,
// TODO: Maybe change the name so it's not confused with Rust's None?
NONE,
}
struct Options {
size: Option<Size>,
thin_strokes: Option<bool>,
antialias: Option<AntiAlias>,
hinting: Option<bool>,
style: Option<String>,
offset: Option<Delta>,
range: Option<FontRange>,
}
struct FontRange {
start: char,
end: char,
}

View file

@ -30,6 +30,7 @@ use index::{Line, Column, RangeInclusive};
use notify::{Watcher, watcher, RecursiveMode, DebouncedEvent};
use config::{self, Config, Delta};
use config::font::FontConfiguration;
use term::{self, cell, RenderableCell};
use window::{Size, Pixels};
@ -152,124 +153,47 @@ pub struct GlyphCache {
/// Rasterizer for loading new glyphs
rasterizer: Rasterizer,
/// regular font
font_key: FontKey,
/// italic font
italic_key: FontKey,
/// bold font
bold_key: FontKey,
/// font size
font_size: font::Size,
/// glyph offset
glyph_offset: Delta,
/// Font configuration with all fonts
font_config: FontConfiguration,
/// Font metrics like glyph width/height
metrics: ::font::Metrics,
}
impl GlyphCache {
pub fn new<L>(
mut rasterizer: Rasterizer,
font: &config::Font,
font_config: FontConfiguration,
loader: &mut L
) -> Result<GlyphCache, font::Error>
where L: LoadGlyph
{
let (regular, bold, italic) = Self::compute_font_keys(font, &mut rasterizer)?;
// Need to load at least one glyph for the face before calling metrics.
// The glyph requested here ('m' at the time of writing) has no special
// The glyph requested here ('0' at the time of writing) has no special
// meaning.
rasterizer.get_glyph(&GlyphKey { font_key: regular, c: 'm', size: font.size() })?;
let primary_font = font_config.font_by_char('0');
rasterizer.get_glyph(&GlyphKey { font_key: primary_font, c: '0', size: primary_font.size() })?;
let metrics = rasterizer.metrics(regular)?;
let mut cache = GlyphCache {
cache: HashMap::default(),
rasterizer: rasterizer,
font_size: font.size(),
font_key: regular,
bold_key: bold,
italic_key: italic,
glyph_offset: *font.glyph_offset(),
metrics: metrics
metrics,
rasterizer,
font_config,
};
cache.load_glyphs_for_font(regular, loader);
cache.load_glyphs_for_font(bold, loader);
cache.load_glyphs_for_font(italic, loader);
// TODO: Load set of standard glyphs
// cache.load_glyphs_for_font(regular, loader);
// cache.load_glyphs_for_font(bold, loader);
// cache.load_glyphs_for_font(italic, loader);
Ok(cache)
}
fn load_glyphs_for_font<L: LoadGlyph>(
&mut self,
font: FontKey,
loader: &mut L,
) {
let size = self.font_size;
for i in RangeInclusive::new(32u8, 128u8) {
self.get(&GlyphKey {
font_key: font,
c: i as char,
size: size
}, loader);
}
}
/// Computes font keys for (Regular, Bold, Italic)
fn compute_font_keys(
font: &config::Font,
rasterizer: &mut Rasterizer
) -> Result<(FontKey, FontKey, FontKey), font::Error> {
let size = font.size();
// Load regular font
let regular_desc = Self::make_desc(&font.normal, font::Slant::Normal, font::Weight::Normal);
let regular = rasterizer
.load_font(&regular_desc, size)?;
// helper to load a description if it is not the regular_desc
let mut load_or_regular = |desc:FontDesc| {
if desc == regular_desc {
regular
} else {
rasterizer.load_font(&desc, size).unwrap_or_else(|_| regular)
}
};
// Load bold font
let bold_desc = Self::make_desc(&font.bold, font::Slant::Normal, font::Weight::Bold);
let bold = load_or_regular(bold_desc);
// Load italic font
let italic_desc = Self::make_desc(&font.italic, font::Slant::Italic, font::Weight::Normal);
let italic = load_or_regular(italic_desc);
Ok((regular, bold, italic))
}
fn make_desc(
desc: &config::FontDescription,
slant: font::Slant,
weight: font::Weight,
) -> FontDesc {
let style = if let Some(ref spec) = desc.style {
font::Style::Specific(spec.to_owned())
} else {
font::Style::Description {slant:slant, weight:weight}
};
FontDesc::new(&desc.family[..], style)
}
pub fn font_metrics(&self) -> font::Metrics {
let primary_font = self.font_config.font_by_char('0');
self.rasterizer
.metrics(self.font_key)
.metrics(primary_font)
.expect("metrics load since font is loaded at glyph cache creation")
}
@ -292,9 +216,11 @@ impl GlyphCache {
loader.load_glyph(&rasterized)
})
}
// TODO
pub fn update_font_size<L: LoadGlyph>(
&mut self,
font: &config::Font,
font: &FontConfiguration,
size: font::Size,
loader: &mut L
) -> Result<(), font::Error> {