aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJoe Wilm <joe@jwilm.com>2016-06-30 09:04:06 -0700
committerJoe Wilm <joe@jwilm.com>2016-06-30 09:04:06 -0700
commitd304ea9b774662ca05384b6633507362e389f7a9 (patch)
tree7bb6780b7296bbed87d174409e9777a54e41cfd1 /src
parent58146acea5ea021884bd58a739b24e98bcb81edf (diff)
downloadalacritty-d304ea9b774662ca05384b6633507362e389f7a9.tar.gz
alacritty-d304ea9b774662ca05384b6633507362e389f7a9.zip
Add config file
Configuration may now be specified in either `$HOME/.alacritty.yml` or `$HOME/.config/alacritty.yml`. See `alacritty.yml` in the repository root for an example. When a configuration file cannot be located, a default configuration is used.
Diffstat (limited to 'src')
-rw-r--r--src/ansi.rs1
-rw-r--r--src/config.rs300
-rw-r--r--src/macros.rs15
-rw-r--r--src/main.rs66
4 files changed, 343 insertions, 39 deletions
diff --git a/src/ansi.rs b/src/ansi.rs
index 2fc3e4f3..5d4d9e4c 100644
--- a/src/ansi.rs
+++ b/src/ansi.rs
@@ -31,7 +31,6 @@
//! should be, feel free to add it. Please try not to become overzealous and adding support for
//! sequences only used by folks trapped in 1988.
-use std::io::Write;
use ::Rgb;
/// A CSI Escape sequence
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 00000000..426a3381
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,300 @@
+//! Configuration definitions and file loading
+//!
+//! 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.
+use std::env;
+use std::fs;
+use std::io::{self, Read};
+use std::path::{Path, PathBuf};
+
+use serde_yaml;
+
+/// Top-level config type
+#[derive(Debug, Deserialize, Default)]
+pub struct Config {
+ /// Pixels per inch
+ #[serde(default)]
+ dpi: Dpi,
+
+ /// Font configuration
+ #[serde(default)]
+ font: Font,
+
+ /// Should show render timer
+ #[serde(default)]
+ render_timer: bool,
+}
+
+/// Errors occurring during config loading
+#[derive(Debug)]
+pub enum Error {
+ /// Config file not found
+ NotFound,
+
+ /// Couldn't read $HOME environment variable
+ ReadingEnvHome(env::VarError),
+
+ /// io error reading file
+ Io(io::Error),
+
+ /// Not valid yaml or missing parameters
+ Yaml(serde_yaml::Error),
+}
+
+impl ::std::error::Error for Error {
+ fn cause(&self) -> Option<&::std::error::Error> {
+ match *self {
+ Error::NotFound => None,
+ Error::ReadingEnvHome(ref err) => Some(err),
+ Error::Io(ref err) => Some(err),
+ Error::Yaml(ref err) => Some(err),
+ }
+ }
+
+ fn description(&self) -> &str {
+ match *self {
+ Error::NotFound => "could not locate config file",
+ Error::ReadingEnvHome(ref err) => err.description(),
+ Error::Io(ref err) => err.description(),
+ Error::Yaml(ref err) => err.description(),
+ }
+ }
+}
+
+impl ::std::fmt::Display for Error {
+ fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+ match *self {
+ Error::NotFound => write!(f, "{}", ::std::error::Error::description(self)),
+ Error::ReadingEnvHome(ref err) => {
+ write!(f, "could not read $HOME environment variable: {}", err)
+ },
+ Error::Io(ref err) => write!(f, "error reading config file: {}", err),
+ Error::Yaml(ref err) => write!(f, "problem with config: {}", err),
+ }
+ }
+}
+
+impl From<env::VarError> for Error {
+ fn from(val: env::VarError) -> Error {
+ Error::ReadingEnvHome(val)
+ }
+}
+
+impl From<io::Error> for Error {
+ fn from(val: io::Error) -> Error {
+ if val.kind() == io::ErrorKind::NotFound {
+ Error::NotFound
+ } else {
+ Error::Io(val)
+ }
+ }
+}
+
+impl From<serde_yaml::Error> for Error {
+ fn from(val: serde_yaml::Error) -> Error {
+ Error::Yaml(val)
+ }
+}
+
+/// Result from config loading
+pub type Result<T> = ::std::result::Result<T, Error>;
+
+impl Config {
+ /// Attempt to load the config file
+ ///
+ /// The config file is loaded from the first file it finds in this list of paths
+ ///
+ /// 1. `$HOME/.config/alacritty.yml`
+ /// 2. `$HOME/.alacritty.yml`
+ pub fn load() -> Result<Config> {
+ let home = env::var("HOME")?;
+
+ // First path
+ let mut path = PathBuf::from(&home);
+ path.push(".config");
+ path.push("alacritty.yml");
+
+ // Fallback path
+ let mut alt_path = PathBuf::from(&home);
+ alt_path.push(".alacritty.yml");
+
+ match Config::load_from(&path) {
+ Ok(c) => Ok(c),
+ Err(e) => {
+ match e {
+ Error::NotFound => Config::load_from(&alt_path),
+ _ => Err(e),
+ }
+ }
+ }
+ }
+
+ /// Get font config
+ #[inline]
+ pub fn font(&self) -> &Font {
+ &self.font
+ }
+
+ /// Get dpi config
+ #[inline]
+ pub fn dpi(&self) -> &Dpi {
+ &self.dpi
+ }
+
+ /// Should show render timer
+ #[inline]
+ pub fn render_timer(&self) -> bool {
+ self.render_timer
+ }
+
+ fn load_from<P: AsRef<Path>>(path: P) -> Result<Config> {
+ let raw = Config::read_file(path)?;
+ Ok(serde_yaml::from_str(&raw[..])?)
+ }
+
+ fn read_file<P: AsRef<Path>>(path: P) -> Result<String> {
+ let mut f = fs::File::open(path)?;
+ let mut contents = String::new();
+ f.read_to_string(&mut contents)?;
+
+ Ok(contents)
+ }
+}
+
+/// Pixels per inch
+///
+/// This is only used on FreeType systems
+#[derive(Debug, Deserialize)]
+pub struct Dpi {
+ /// Horizontal dpi
+ x: f32,
+
+ /// Vertical dpi
+ y: f32,
+}
+
+impl Default for Dpi {
+ fn default() -> Dpi {
+ Dpi { x: 96.0, y: 96.0 }
+ }
+}
+
+impl Dpi {
+ /// Get horizontal dpi
+ #[inline]
+ pub fn x(&self) -> f32 {
+ self.x
+ }
+
+ /// Get vertical dpi
+ #[inline]
+ pub fn y(&self) -> f32 {
+ self.y
+ }
+}
+
+/// Modifications to font spacing
+///
+/// The way Alacritty calculates vertical and horizontal cell sizes may not be
+/// ideal for all fonts. This gives the user a way to tweak those values.
+#[derive(Debug, Deserialize)]
+pub struct FontOffset {
+ /// Extra horizontal spacing between letters
+ x: f32,
+ /// Extra vertical spacing between lines
+ y: f32,
+}
+
+impl FontOffset {
+ /// Get letter spacing
+ #[inline]
+ pub fn x(&self) -> f32 {
+ self.x
+ }
+
+ /// Get line spacing
+ #[inline]
+ pub fn y(&self) -> f32 {
+ self.y
+ }
+}
+
+/// 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)]
+pub struct Font {
+ /// Font family
+ family: String,
+
+ /// Font style
+ style: String,
+
+ /// Font size in points
+ size: f32,
+
+ /// Extra spacing per character
+ offset: FontOffset,
+}
+
+impl Font {
+ /// Get the font family
+ #[inline]
+ pub fn family(&self) -> &str {
+ &self.family[..]
+ }
+
+ /// Get the font style
+ #[inline]
+ pub fn style(&self) -> &str {
+ &self.style[..]
+ }
+
+ /// Get the font size in points
+ #[inline]
+ pub fn size(&self) -> f32 {
+ self.size
+ }
+
+ /// Get offsets to font metrics
+ #[inline]
+ pub fn offset(&self) -> &FontOffset {
+ &self.offset
+ }
+}
+
+#[cfg(target_os = "macos")]
+impl Default for Font {
+ fn default() -> Font {
+ Font {
+ family: String::from("Menlo"),
+ style: String::from("Regular"),
+ size: 11.0,
+ offset: FontOffset {
+ x: 0.0,
+ y: 0.0
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "linux")]
+impl Default for Font {
+ fn default() -> Font {
+ Font {
+ family: String::from("DejaVu Sans Mono"),
+ style: String::from("Book"),
+ size: 11.0,
+ offset: FontOffset {
+ // TODO should improve freetype metrics... shouldn't need such
+ // drastic offsets for the default!
+ x: 2.0,
+ y: -7.0
+ }
+ }
+ }
+}
diff --git a/src/macros.rs b/src/macros.rs
index e35eeb90..8abad78d 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -14,17 +14,16 @@
#[macro_export]
macro_rules! die {
- ($($arg:tt)*) => {
- println!($($arg)*);
+ ($($arg:tt)*) => {{
+ err_println!($($arg)*);
::std::process::exit(1);
- }
+ }}
}
#[macro_export]
macro_rules! err_println {
- ($($arg:tt)*) => {
- if let Err(_) = writeln!(&mut ::std::io::stderr(), $($arg)*) {
- die!("Cannot reach stderr");
- }
- }
+ ($($arg:tt)*) => {{
+ use std::io::Write;
+ (writeln!(&mut ::std::io::stderr(), $($arg)*)).expect("stderr");
+ }}
}
diff --git a/src/main.rs b/src/main.rs
index ec68ebf6..b699fe44 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -19,14 +19,18 @@
#![feature(io)]
#![feature(drop_types_in_const)]
#![feature(unicode)]
+#![feature(custom_derive, plugin)]
+#![plugin(serde_macros)]
+extern crate cgmath;
+extern crate errno;
extern crate font;
-extern crate libc;
extern crate glutin;
-extern crate cgmath;
+extern crate libc;
extern crate notify;
-extern crate errno;
extern crate parking_lot;
+extern crate serde;
+extern crate serde_yaml;
#[macro_use]
extern crate bitflags;
@@ -37,6 +41,7 @@ mod macros;
mod renderer;
pub mod grid;
mod meter;
+pub mod config;
mod input;
mod tty;
pub mod ansi;
@@ -49,6 +54,7 @@ use std::sync::{mpsc, Arc};
use parking_lot::Mutex;
+use config::Config;
use font::FontDesc;
use meter::Meter;
use renderer::{QuadRenderer, GlyphCache};
@@ -131,18 +137,21 @@ mod gl {
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}
-#[cfg(target_os = "linux")]
-static FONT: &'static str = "DejaVu Sans Mono";
-#[cfg(target_os = "linux")]
-static FONT_STYLE: &'static str = "Book";
-
-#[cfg(target_os = "macos")]
-static FONT: &'static str = "Menlo";
-#[cfg(target_os = "macos")]
-static FONT_STYLE: &'static str = "Regular";
-
-
fn main() {
+ // Load configuration
+ let config = match Config::load() {
+ Err(err) => match err {
+ // Use default config when not found
+ config::Error::NotFound => Config::default(),
+ // Exit when there's a problem with it
+ _ => die!("{}", err),
+ },
+ Ok(config) => config,
+ };
+
+ let font = config.font();
+ let dpi = config.dpi();
+ let render_timer = config.render_timer();
let mut window = glutin::WindowBuilder::new()
.with_vsync()
@@ -156,17 +165,12 @@ fn main() {
println!("device_pixel_ratio: {}", dpr);
- let font_size = 11.;
-
- let sep_x = 0.0;
- let sep_y = 0.0;
+ let desc = FontDesc::new(font.family(), font.style());
+ let mut rasterizer = font::Rasterizer::new(dpi.x(), dpi.y(), dpr);
- let desc = FontDesc::new(FONT, FONT_STYLE);
- let mut rasterizer = font::Rasterizer::new(96., 96., dpr);
-
- let metrics = rasterizer.metrics(&desc, font_size);
- let cell_width = (metrics.average_advance + sep_x) as u32;
- let cell_height = (metrics.line_height + sep_y) as u32;
+ let metrics = rasterizer.metrics(&desc, font.size());
+ let cell_width = (metrics.average_advance + font.offset().x() as f64) as u32;
+ let cell_height = (metrics.line_height + font.offset().y() as f64) as u32;
println!("Cell Size: ({} x {})", cell_width, cell_height);
@@ -175,7 +179,7 @@ fn main() {
let reader = terminal.tty().reader();
let writer = terminal.tty().writer();
- let mut glyph_cache = GlyphCache::new(rasterizer, desc, font_size);
+ let mut glyph_cache = GlyphCache::new(rasterizer, desc, font.size());
let needs_render = Arc::new(AtomicBool::new(true));
let needs_render2 = needs_render.clone();
@@ -322,11 +326,13 @@ fn main() {
}
// Draw render timer
- let timing = format!("{:.3} usec", meter.average());
- let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
- renderer.with_api(terminal.size_info(), |mut api| {
- api.render_string(&timing[..], &mut glyph_cache, &color);
- });
+ if render_timer {
+ let timing = format!("{:.3} usec", meter.average());
+ let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
+ renderer.with_api(terminal.size_info(), |mut api| {
+ api.render_string(&timing[..], &mut glyph_cache, &color);
+ });
+ }
}
window.swap_buffers().unwrap();