alacritty/src/config.rs

2192 lines
62 KiB
Rust
Raw Normal View History

//! 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::borrow::Cow;
use std::{env, fmt};
use std::fs::{self, File};
2017-01-06 04:56:05 +00:00
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::sync::mpsc;
use std::time::Duration;
2017-02-14 02:22:59 +00:00
use std::collections::HashMap;
use ::Rgb;
use font::Size;
use serde_yaml;
use serde::{self, de, Deserialize};
use serde::de::Error as SerdeError;
2017-08-30 18:17:27 +00:00
use serde::de::{Visitor, MapAccess, Unexpected};
2017-08-30 19:34:23 +00:00
use notify::{Watcher, watcher, DebouncedEvent, RecursiveMode};
use glutin::ModifiersState;
use cli::Options;
use input::{Action, Binding, MouseBinding, KeyBinding};
use index::{Line, Column};
use ansi::CursorStyle;
use util::fmt::Yellow;
/// Function that returns true for serde default
fn true_bool() -> bool {
true
}
#[derive(Clone, Debug, Deserialize)]
pub struct Selection {
pub semantic_escape_chars: String,
}
impl Default for Selection {
fn default() -> Selection {
Selection {
semantic_escape_chars: String::new()
}
}
}
#[derive(Clone, Debug, Deserialize)]
pub struct ClickHandler {
#[serde(deserialize_with="deserialize_duration_ms")]
pub threshold: Duration,
}
impl Default for ClickHandler {
fn default() -> Self {
ClickHandler { threshold: default_threshold_ms() }
}
}
fn default_threshold_ms() -> Duration {
Duration::from_millis(300)
}
2017-08-30 18:17:27 +00:00
fn deserialize_duration_ms<'a, D>(deserializer: D) -> ::std::result::Result<Duration, D::Error>
where D: de::Deserializer<'a>
{
match u64::deserialize(deserializer) {
Ok(threshold_ms) => Ok(Duration::from_millis(threshold_ms)),
Err(err) => {
eprintln!("problem with config: {}; Using default value", err);
Ok(default_threshold_ms())
},
}
}
#[derive(Clone, Debug, Deserialize)]
pub struct Mouse {
#[serde(default, deserialize_with = "failure_default")]
pub double_click: ClickHandler,
#[serde(default, deserialize_with = "failure_default")]
pub triple_click: ClickHandler,
// TODO: DEPRECATED
#[serde(default)]
pub faux_scrollback_lines: Option<usize>,
}
impl Default for Mouse {
fn default() -> Mouse {
Mouse {
double_click: ClickHandler {
threshold: Duration::from_millis(300),
},
triple_click: ClickHandler {
threshold: Duration::from_millis(300),
},
faux_scrollback_lines: None,
}
}
}
/// `VisualBellAnimations` are modeled after a subset of CSS transitions and Robert
/// Penner's Easing Functions.
#[derive(Clone, Copy, Debug, Deserialize)]
pub enum VisualBellAnimation {
Ease, // CSS
EaseOut, // CSS
EaseOutSine, // Penner
EaseOutQuad, // Penner
EaseOutCubic, // Penner
EaseOutQuart, // Penner
EaseOutQuint, // Penner
EaseOutExpo, // Penner
EaseOutCirc, // Penner
Linear,
}
impl Default for VisualBellAnimation {
fn default() -> Self {
VisualBellAnimation::EaseOutExpo
}
}
#[derive(Debug, Deserialize)]
pub struct VisualBellConfig {
/// Visual bell animation function
#[serde(default, deserialize_with = "failure_default")]
animation: VisualBellAnimation,
/// Visual bell duration in milliseconds
#[serde(deserialize_with = "deserialize_visual_bell_duration")]
#[serde(default="default_visual_bell_duration")]
duration: u16,
}
fn default_visual_bell_duration() -> u16 {
150
}
fn deserialize_visual_bell_duration<'a, D>(deserializer: D) -> ::std::result::Result<u16, D::Error>
where D: de::Deserializer<'a>
{
match u16::deserialize(deserializer) {
Ok(duration) => Ok(duration),
Err(err) => {
eprintln!("problem with config: {}; Using default value", err);
Ok(default_visual_bell_duration())
},
}
}
impl VisualBellConfig {
/// Visual bell animation
#[inline]
pub fn animation(&self) -> VisualBellAnimation {
self.animation
}
/// Visual bell duration in milliseconds
#[inline]
pub fn duration(&self) -> Duration {
Duration::from_millis(u64::from(self.duration))
}
}
impl Default for VisualBellConfig {
fn default() -> VisualBellConfig {
VisualBellConfig {
animation: VisualBellAnimation::default(),
duration: default_visual_bell_duration(),
}
}
}
#[derive(Debug, Deserialize)]
pub struct Shell<'a> {
program: Cow<'a, str>,
#[serde(default, deserialize_with = "failure_default")]
args: Vec<String>,
}
impl<'a> Shell<'a> {
2017-01-24 23:19:45 +00:00
pub fn new<S>(program: S) -> Shell<'a>
where S: Into<Cow<'a, str>>
{
Shell {
2017-01-24 23:19:45 +00:00
program: program.into(),
args: Vec::new(),
}
}
2017-01-24 23:19:45 +00:00
pub fn new_with_args<S>(program: S, args: Vec<String>) -> Shell<'a>
where S: Into<Cow<'a, str>>
{
Shell {
program: program.into(),
args,
2017-01-24 23:19:45 +00:00
}
}
pub fn program(&self) -> &str {
&*self.program
}
pub fn args(&self) -> &[String] {
self.args.as_slice()
}
}
/// Wrapper around f32 that represents an alpha value between 0.0 and 1.0
#[derive(Clone, Copy, Debug)]
pub struct Alpha(f32);
impl Alpha {
pub fn new(value: f32) -> Self {
Alpha(Self::clamp_to_valid_range(value))
}
pub fn set(&mut self, value: f32) {
self.0 = Self::clamp_to_valid_range(value);
}
#[inline]
pub fn get(self) -> f32 {
self.0
}
fn clamp_to_valid_range(value: f32) -> f32 {
if value < 0.0 {
0.0
} else if value > 1.0 {
1.0
} else {
value
}
}
}
impl Default for Alpha {
fn default() -> Self {
Alpha(1.0)
}
}
#[derive(Debug, Copy, Clone, Deserialize)]
pub struct WindowConfig {
/// Initial dimensions
#[serde(default, deserialize_with = "failure_default")]
dimensions: Dimensions,
/// Pixel padding
#[serde(default="default_padding", deserialize_with = "deserialize_padding")]
padding: Delta<u8>,
/// Draw the window with title bar / borders
#[serde(default, deserialize_with = "failure_default")]
decorations: bool,
}
fn default_padding() -> Delta<u8> {
Delta { x: 2, y: 2 }
}
fn deserialize_padding<'a, D>(deserializer: D) -> ::std::result::Result<Delta<u8>, D::Error>
where D: de::Deserializer<'a>
{
match Delta::deserialize(deserializer) {
Ok(delta) => Ok(delta),
Err(err) => {
eprintln!("problem with config: {}; Using default value", err);
Ok(default_padding())
},
}
}
impl WindowConfig {
pub fn decorations(&self) -> bool {
self.decorations
}
}
impl Default for WindowConfig {
fn default() -> Self {
WindowConfig{
dimensions: Default::default(),
padding: default_padding(),
decorations: true,
}
}
}
/// Top-level config type
#[derive(Debug, Deserialize)]
pub struct Config {
/// Initial dimensions
#[serde(default, deserialize_with = "failure_default")]
dimensions: Option<Dimensions>,
/// Pixel padding
#[serde(default, deserialize_with = "failure_default")]
padding: Option<Delta<u8>>,
2017-02-14 02:22:59 +00:00
/// TERM env variable
#[serde(default, deserialize_with = "failure_default")]
2017-02-14 02:22:59 +00:00
env: HashMap<String, String>,
/// Font configuration
#[serde(default, deserialize_with = "failure_default")]
font: Font,
/// Should show render timer
#[serde(default, deserialize_with = "failure_default")]
render_timer: bool,
/// Should use custom cursor colors
#[serde(default, deserialize_with = "failure_default")]
custom_cursor_colors: bool,
2017-10-30 15:03:58 +00:00
/// Should draw bold text with brighter colors instead of bold font
#[serde(default="true_bool", deserialize_with = "default_true_bool")]
draw_bold_text_with_bright_colors: bool,
#[serde(default, deserialize_with = "failure_default")]
colors: Colors,
/// Background opacity from 0.0 to 1.0
#[serde(default, deserialize_with = "failure_default")]
background_opacity: Alpha,
/// Window configuration
#[serde(default, deserialize_with = "failure_default")]
window: WindowConfig,
/// Keybindings
#[serde(default, deserialize_with = "failure_default_vec")]
key_bindings: Vec<KeyBinding>,
/// Bindings for the mouse
#[serde(default, deserialize_with = "failure_default_vec")]
mouse_bindings: Vec<MouseBinding>,
#[serde(default, deserialize_with = "failure_default")]
selection: Selection,
#[serde(default, deserialize_with = "failure_default")]
mouse: Mouse,
2017-01-07 17:07:06 +00:00
/// Path to a shell program to run on startup
#[serde(default, deserialize_with = "failure_default")]
shell: Option<Shell<'static>>,
/// Path where config was loaded from
#[serde(default, deserialize_with = "failure_default")]
config_path: Option<PathBuf>,
/// Visual bell configuration
#[serde(default, deserialize_with = "failure_default")]
visual_bell: VisualBellConfig,
2017-02-22 19:52:37 +00:00
/// Use dynamic title
#[serde(default="true_bool", deserialize_with = "default_true_bool")]
dynamic_title: bool,
2017-02-22 19:52:37 +00:00
/// Hide cursor when typing
#[serde(default, deserialize_with = "failure_default")]
2017-02-22 19:52:37 +00:00
hide_cursor_when_typing: bool,
/// Style of the cursor
#[serde(default, deserialize_with = "failure_default")]
cursor_style: CursorStyle,
Merge master into scrollback * Allow disabling DPI scaling This makes it possible to disable DPI scaling completely, instead the the display pixel ration will always be fixed to 1.0. By default nothing has changed and DPI is still enabled, this just seems like a better way than running `WINIT_HIDPI_FACTOR=1.0 alacritty` every time the user wants to start alacritty. It would be possible to allow specifying any DPR, however I've decided against this since I'd assume it's a very rare usecase. It's also still possible to make use of `WINIT_HIDPI_FACTOR` to do this on X11. Currently this is not updated at runtime using the live config update, there is not really much of a technical limitation why this woudn't be possible, however a solution for that issue should be first added in jwilm/alacritty#1346, once a system is established for changing DPI at runtime, porting that functionality to this PR should be simple. * Add working --class and --title CLI parameters * Reduce Increase-/DecreaseFontSize step to 0.5 Until now the Increase-/DecreaseFontSize keybinds hand a step size of 1.0. Since the font size however is multiplied by two to allow more granular font size control, this lead to the bindings skipping one font size (incrementing/decrementing by +-2). To fix this the step size of the Increase-/DecreaseFontSize bindings has been reduced to the minimum step size that exists with the current font configuration (0.5). This should allow users to increment and decrement the font size by a single point instead of two. This also adds a few tests to make sure the methods for increasing/decreasing/resetting font size work properly. * Add Copy/Cut/Paste keys This just adds support for the Copy/Cut/Paste keys and sets up Copy/Paste as alternative defaults for Ctrl+Shift+C/V. * Move to cargo clippy Using clippy as a library has been deprecated, instead the `cargo clippy` command should be used instead. To comply with this change clippy has been removed from the `Cargo.toml` and is now installed with cargo when building in CI. This has also lead to a few new clippy issues to show up, this includes everything in the `font` subdirectory. This has been fixed and `font` should now be covered by clippy CI too. This also upgrades all dependencies, as a result this fixes #1341 and this fixes #1344. * Override dynamic_title when --title is specified * Change green implementation to use the macro * Ignore mouse input if window is unfocused * Make compilation of binary a phony target * Add opensuse zypper install method to readme * Fix clippy issues * Update manpage to document all CLI options The introduction of `--class` has added a flag to the CLI without adding it to the manpage. This has been fixed by updating the manpage. This also adds the default values of `--class` and `--title` to the CLI options. * Remove unnecessary clippy lint annotations We moved to "cargo clippy" in 5ba34d4f9766a55a06ed5e3e44cc384af1b09f65 and removing the clippy lint annotations in `src/lib.rs` does not cause any additional warnings. This also changes `cargo clippy` to use the flags required for checking integration tests. * Enable clippy in font/copypasta crates Enabled clippy in the sub-crates font and copypasta. All issues that were discovered by this change have also been fixed. * Remove outdated comment about NixOS * Replace debug asserts with static_assertions To check that transmutes will work correctly without having to rely on error-prone runtime checking, the `static_assertions` crate has been introduced. This allows comparing the size of types at compile time, preventing potentially silent breakage. This fixes #1417. * Add `cargo deb` build instructions Updated the `Cargo.toml` file and added a `package.metadata.deb` subsection to define how to build a debian "deb" install file using `cargo deb`. This will allow debian/ubuntu users to install `alacritty` using their system's package manager. It also will make it easier to provide pre-built binaries for those systems. Also fixed a stray debug line in the bash autocomplete script that was writting to a tempfile. * Add config for unfocused window cursor change * Add support for cursor shape escape sequence * Add bright foreground color option It was requested in jwilm/alacritty#825 that it should be possible to add an optional bright foreground color. This is now added to the primary colors structure and allows the user to set a foreground color for bold normal text. This has no effect unless the draw_bold_text_with_bright_colors option is also enabled. If the color is not specified, the bright foreground color will fall back to the normal foreground color. This fixes #825. * Fix clone URL in deb install instructions * Fix 'cargo-deb' desktop file name * Remove redundant dependency from deb build * Switch from deprecated `std::env::home_dir` to `dirs::home_dir` * Allow specifying modifiers for mouse bindings * Send newline with NumpadEnter * Add support for LCD-V pixel mode * Add binding action for hiding the window * Switch to rustup clippy component * Add optional dim foreground color Add optional color for the dim foreground (`\e[2m;`) Defaults to 2/3 of the foreground color. (same as other colors). If a bright color is dimmed, it's displayed as the normal color. The exception for this is when the bright foreground is dimmed when no bright foreground color is set. In that case it's treated as a normal foreground color and dimmed to DimForeground. To minimize the surprise for the user, the bright and dim colors have been completely removed from the default configuration file. Some documentation has also been added to make it clear to users what these options can be used for. This fixes #1448. * Fix clippy lints and run font tests on travis This fixes some existing clippy issues and runs the `font` tests through travis. Testing of copypasta crate was omitted due to problens when running on headless travis-ci environment (x11 clipboard would fail). * Ignore errors when logger can't write to output The (e)print macro will panic when there is no output available to write to, however in our scenario where we only log user errors to stderr, the better choice would be to ignore when writing to stdout or stderr is not possible. This changes the (e)print macro to make use of `write` and ignore any potential errors. Since (e)println rely on (e)print, this also solves potential failuers when calling (e)println. With this change implemented, all of logging, (e)println and (e)print should never fail even if the stdout/stderr is not available.
2018-07-28 23:10:13 +00:00
/// Use hollow block cursor when unfocused
#[serde(default="true_bool", deserialize_with = "default_true_bool")]
unfocused_hollow_cursor: bool,
/// Live config reload
#[serde(default="true_bool", deserialize_with = "default_true_bool")]
live_config_reload: bool,
/// Number of spaces in one tab
#[serde(default="default_tabspaces", deserialize_with = "deserialize_tabspaces")]
tabspaces: usize,
/// How much scrolling history to keep
#[serde(default, deserialize_with="failure_default")]
scrolling: Scrolling,
}
fn failure_default_vec<'a, D, T>(deserializer: D) -> ::std::result::Result<Vec<T>, D::Error>
where D: de::Deserializer<'a>,
T: Deserialize<'a>
{
// Deserialize as generic vector
let vec = match Vec::<serde_yaml::Value>::deserialize(deserializer) {
Ok(vec) => vec,
Err(err) => {
eprintln!("problem with config: {}; Using empty vector", err);
return Ok(Vec::new());
},
};
// Move to lossy vector
let mut bindings: Vec<T> = Vec::new();
for value in vec {
match T::deserialize(value) {
Ok(binding) => bindings.push(binding),
Err(err) => {
eprintln!("problem with config: {}; Skipping value", err);
},
}
}
Ok(bindings)
}
fn default_tabspaces() -> usize {
8
}
fn deserialize_tabspaces<'a, D>(deserializer: D) -> ::std::result::Result<usize, D::Error>
where D: de::Deserializer<'a>
{
match usize::deserialize(deserializer) {
Ok(value) => Ok(value),
Err(err) => {
eprintln!("problem with config: {}; Using `8`", err);
Ok(default_tabspaces())
},
}
}
fn default_true_bool<'a, D>(deserializer: D) -> ::std::result::Result<bool, D::Error>
where D: de::Deserializer<'a>
{
match bool::deserialize(deserializer) {
Ok(value) => Ok(value),
Err(err) => {
eprintln!("problem with config: {}; Using `true`", err);
Ok(true)
},
}
}
fn failure_default<'a, D, T>(deserializer: D)
-> ::std::result::Result<T, D::Error>
where D: de::Deserializer<'a>,
T: Deserialize<'a> + Default
{
match T::deserialize(deserializer) {
Ok(value) => Ok(value),
Err(err) => {
eprintln!("problem with config: {}; Using default value", err);
Ok(T::default())
},
}
}
#[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");
impl Default for Config {
fn default() -> Self {
serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG)
.expect("default config is invalid")
}
}
/// Struct for scrolling related settings
#[derive(Copy, Clone, Debug, Deserialize)]
pub struct Scrolling {
#[serde(deserialize_with="deserialize_scrolling_history")]
#[serde(default="default_scrolling_history")]
pub history: u32,
#[serde(deserialize_with="deserialize_scrolling_multiplier")]
#[serde(default="default_scrolling_multiplier")]
pub multiplier: u8,
#[serde(deserialize_with="deserialize_scrolling_multiplier")]
#[serde(default="default_scrolling_multiplier")]
pub faux_multiplier: u8,
#[serde(default, deserialize_with="failure_default")]
pub auto_scroll: bool,
}
fn default_scrolling_history() -> u32 {
10_000
}
// Default for normal and faux scrolling
fn default_scrolling_multiplier() -> u8 {
3
}
impl Default for Scrolling {
fn default() -> Self {
Self {
history: default_scrolling_history(),
multiplier: default_scrolling_multiplier(),
faux_multiplier: default_scrolling_multiplier(),
auto_scroll: false,
}
}
}
fn deserialize_scrolling_history<'a, D>(deserializer: D) -> ::std::result::Result<u32, D::Error>
where D: de::Deserializer<'a>
{
match u32::deserialize(deserializer) {
Ok(lines) => Ok(lines),
Err(err) => {
eprintln!("problem with config: {}; Using default value", err);
Ok(default_scrolling_history())
},
}
}
fn deserialize_scrolling_multiplier<'a, D>(deserializer: D) -> ::std::result::Result<u8, D::Error>
where D: de::Deserializer<'a>
{
match u8::deserialize(deserializer) {
Ok(lines) => Ok(lines),
Err(err) => {
eprintln!("problem with config: {}; Using default value", err);
Ok(default_scrolling_multiplier())
},
}
}
/// Newtype for implementing deserialize on glutin Mods
///
/// Our deserialize impl wouldn't be covered by a derive(Deserialize); see the
/// impl below.
struct ModsWrapper(ModifiersState);
impl ModsWrapper {
fn into_inner(self) -> ModifiersState {
self.0
}
}
2017-08-30 18:17:27 +00:00
impl<'a> de::Deserialize<'a> for ModsWrapper {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
2017-08-30 18:17:27 +00:00
where D: de::Deserializer<'a>
{
struct ModsVisitor;
2017-08-30 18:17:27 +00:00
impl<'a> Visitor<'a> for ModsVisitor {
type Value = ModsWrapper;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Some subset of Command|Shift|Super|Alt|Option|Control")
}
fn visit_str<E>(self, value: &str) -> ::std::result::Result<ModsWrapper, E>
where E: de::Error,
{
let mut res = ModifiersState::default();
for modifier in value.split('|') {
2017-01-05 13:33:45 +00:00
match modifier.trim() {
"Command" | "Super" => res.logo = true,
"Shift" => res.shift = true,
"Alt" | "Option" => res.alt = true,
"Control" => res.ctrl = true,
_ => eprintln!("unknown modifier {:?}", modifier),
}
}
Ok(ModsWrapper(res))
}
}
deserializer.deserialize_str(ModsVisitor)
}
}
struct ActionWrapper(::input::Action);
impl ActionWrapper {
fn into_inner(self) -> ::input::Action {
self.0
}
}
2017-08-30 18:17:27 +00:00
impl<'a> de::Deserialize<'a> for ActionWrapper {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
2017-08-30 18:17:27 +00:00
where D: de::Deserializer<'a>
{
struct ActionVisitor;
2017-08-30 18:17:27 +00:00
impl<'a> Visitor<'a> for ActionVisitor {
type Value = ActionWrapper;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Paste, Copy, PasteSelection, IncreaseFontSize, DecreaseFontSize, \
Merge master into scrollback * Allow disabling DPI scaling This makes it possible to disable DPI scaling completely, instead the the display pixel ration will always be fixed to 1.0. By default nothing has changed and DPI is still enabled, this just seems like a better way than running `WINIT_HIDPI_FACTOR=1.0 alacritty` every time the user wants to start alacritty. It would be possible to allow specifying any DPR, however I've decided against this since I'd assume it's a very rare usecase. It's also still possible to make use of `WINIT_HIDPI_FACTOR` to do this on X11. Currently this is not updated at runtime using the live config update, there is not really much of a technical limitation why this woudn't be possible, however a solution for that issue should be first added in jwilm/alacritty#1346, once a system is established for changing DPI at runtime, porting that functionality to this PR should be simple. * Add working --class and --title CLI parameters * Reduce Increase-/DecreaseFontSize step to 0.5 Until now the Increase-/DecreaseFontSize keybinds hand a step size of 1.0. Since the font size however is multiplied by two to allow more granular font size control, this lead to the bindings skipping one font size (incrementing/decrementing by +-2). To fix this the step size of the Increase-/DecreaseFontSize bindings has been reduced to the minimum step size that exists with the current font configuration (0.5). This should allow users to increment and decrement the font size by a single point instead of two. This also adds a few tests to make sure the methods for increasing/decreasing/resetting font size work properly. * Add Copy/Cut/Paste keys This just adds support for the Copy/Cut/Paste keys and sets up Copy/Paste as alternative defaults for Ctrl+Shift+C/V. * Move to cargo clippy Using clippy as a library has been deprecated, instead the `cargo clippy` command should be used instead. To comply with this change clippy has been removed from the `Cargo.toml` and is now installed with cargo when building in CI. This has also lead to a few new clippy issues to show up, this includes everything in the `font` subdirectory. This has been fixed and `font` should now be covered by clippy CI too. This also upgrades all dependencies, as a result this fixes #1341 and this fixes #1344. * Override dynamic_title when --title is specified * Change green implementation to use the macro * Ignore mouse input if window is unfocused * Make compilation of binary a phony target * Add opensuse zypper install method to readme * Fix clippy issues * Update manpage to document all CLI options The introduction of `--class` has added a flag to the CLI without adding it to the manpage. This has been fixed by updating the manpage. This also adds the default values of `--class` and `--title` to the CLI options. * Remove unnecessary clippy lint annotations We moved to "cargo clippy" in 5ba34d4f9766a55a06ed5e3e44cc384af1b09f65 and removing the clippy lint annotations in `src/lib.rs` does not cause any additional warnings. This also changes `cargo clippy` to use the flags required for checking integration tests. * Enable clippy in font/copypasta crates Enabled clippy in the sub-crates font and copypasta. All issues that were discovered by this change have also been fixed. * Remove outdated comment about NixOS * Replace debug asserts with static_assertions To check that transmutes will work correctly without having to rely on error-prone runtime checking, the `static_assertions` crate has been introduced. This allows comparing the size of types at compile time, preventing potentially silent breakage. This fixes #1417. * Add `cargo deb` build instructions Updated the `Cargo.toml` file and added a `package.metadata.deb` subsection to define how to build a debian "deb" install file using `cargo deb`. This will allow debian/ubuntu users to install `alacritty` using their system's package manager. It also will make it easier to provide pre-built binaries for those systems. Also fixed a stray debug line in the bash autocomplete script that was writting to a tempfile. * Add config for unfocused window cursor change * Add support for cursor shape escape sequence * Add bright foreground color option It was requested in jwilm/alacritty#825 that it should be possible to add an optional bright foreground color. This is now added to the primary colors structure and allows the user to set a foreground color for bold normal text. This has no effect unless the draw_bold_text_with_bright_colors option is also enabled. If the color is not specified, the bright foreground color will fall back to the normal foreground color. This fixes #825. * Fix clone URL in deb install instructions * Fix 'cargo-deb' desktop file name * Remove redundant dependency from deb build * Switch from deprecated `std::env::home_dir` to `dirs::home_dir` * Allow specifying modifiers for mouse bindings * Send newline with NumpadEnter * Add support for LCD-V pixel mode * Add binding action for hiding the window * Switch to rustup clippy component * Add optional dim foreground color Add optional color for the dim foreground (`\e[2m;`) Defaults to 2/3 of the foreground color. (same as other colors). If a bright color is dimmed, it's displayed as the normal color. The exception for this is when the bright foreground is dimmed when no bright foreground color is set. In that case it's treated as a normal foreground color and dimmed to DimForeground. To minimize the surprise for the user, the bright and dim colors have been completely removed from the default configuration file. Some documentation has also been added to make it clear to users what these options can be used for. This fixes #1448. * Fix clippy lints and run font tests on travis This fixes some existing clippy issues and runs the `font` tests through travis. Testing of copypasta crate was omitted due to problens when running on headless travis-ci environment (x11 clipboard would fail). * Ignore errors when logger can't write to output The (e)print macro will panic when there is no output available to write to, however in our scenario where we only log user errors to stderr, the better choice would be to ignore when writing to stdout or stderr is not possible. This changes the (e)print macro to make use of `write` and ignore any potential errors. Since (e)println rely on (e)print, this also solves potential failuers when calling (e)println. With this change implemented, all of logging, (e)println and (e)print should never fail even if the stdout/stderr is not available.
2018-07-28 23:10:13 +00:00
ResetFontSize, ScrollPageUp, ScrollPageDown, ScrollToTop, \
ScrollToBottom, ClearHistory, Hide, or Quit")
}
fn visit_str<E>(self, value: &str) -> ::std::result::Result<ActionWrapper, E>
where E: de::Error,
{
Ok(ActionWrapper(match value {
"Paste" => Action::Paste,
"Copy" => Action::Copy,
"PasteSelection" => Action::PasteSelection,
"IncreaseFontSize" => Action::IncreaseFontSize,
"DecreaseFontSize" => Action::DecreaseFontSize,
"ResetFontSize" => Action::ResetFontSize,
"ScrollPageUp" => Action::ScrollPageUp,
"ScrollPageDown" => Action::ScrollPageDown,
"ScrollToTop" => Action::ScrollToTop,
"ScrollToBottom" => Action::ScrollToBottom,
"ClearHistory" => Action::ClearHistory,
Merge master into scrollback * Allow disabling DPI scaling This makes it possible to disable DPI scaling completely, instead the the display pixel ration will always be fixed to 1.0. By default nothing has changed and DPI is still enabled, this just seems like a better way than running `WINIT_HIDPI_FACTOR=1.0 alacritty` every time the user wants to start alacritty. It would be possible to allow specifying any DPR, however I've decided against this since I'd assume it's a very rare usecase. It's also still possible to make use of `WINIT_HIDPI_FACTOR` to do this on X11. Currently this is not updated at runtime using the live config update, there is not really much of a technical limitation why this woudn't be possible, however a solution for that issue should be first added in jwilm/alacritty#1346, once a system is established for changing DPI at runtime, porting that functionality to this PR should be simple. * Add working --class and --title CLI parameters * Reduce Increase-/DecreaseFontSize step to 0.5 Until now the Increase-/DecreaseFontSize keybinds hand a step size of 1.0. Since the font size however is multiplied by two to allow more granular font size control, this lead to the bindings skipping one font size (incrementing/decrementing by +-2). To fix this the step size of the Increase-/DecreaseFontSize bindings has been reduced to the minimum step size that exists with the current font configuration (0.5). This should allow users to increment and decrement the font size by a single point instead of two. This also adds a few tests to make sure the methods for increasing/decreasing/resetting font size work properly. * Add Copy/Cut/Paste keys This just adds support for the Copy/Cut/Paste keys and sets up Copy/Paste as alternative defaults for Ctrl+Shift+C/V. * Move to cargo clippy Using clippy as a library has been deprecated, instead the `cargo clippy` command should be used instead. To comply with this change clippy has been removed from the `Cargo.toml` and is now installed with cargo when building in CI. This has also lead to a few new clippy issues to show up, this includes everything in the `font` subdirectory. This has been fixed and `font` should now be covered by clippy CI too. This also upgrades all dependencies, as a result this fixes #1341 and this fixes #1344. * Override dynamic_title when --title is specified * Change green implementation to use the macro * Ignore mouse input if window is unfocused * Make compilation of binary a phony target * Add opensuse zypper install method to readme * Fix clippy issues * Update manpage to document all CLI options The introduction of `--class` has added a flag to the CLI without adding it to the manpage. This has been fixed by updating the manpage. This also adds the default values of `--class` and `--title` to the CLI options. * Remove unnecessary clippy lint annotations We moved to "cargo clippy" in 5ba34d4f9766a55a06ed5e3e44cc384af1b09f65 and removing the clippy lint annotations in `src/lib.rs` does not cause any additional warnings. This also changes `cargo clippy` to use the flags required for checking integration tests. * Enable clippy in font/copypasta crates Enabled clippy in the sub-crates font and copypasta. All issues that were discovered by this change have also been fixed. * Remove outdated comment about NixOS * Replace debug asserts with static_assertions To check that transmutes will work correctly without having to rely on error-prone runtime checking, the `static_assertions` crate has been introduced. This allows comparing the size of types at compile time, preventing potentially silent breakage. This fixes #1417. * Add `cargo deb` build instructions Updated the `Cargo.toml` file and added a `package.metadata.deb` subsection to define how to build a debian "deb" install file using `cargo deb`. This will allow debian/ubuntu users to install `alacritty` using their system's package manager. It also will make it easier to provide pre-built binaries for those systems. Also fixed a stray debug line in the bash autocomplete script that was writting to a tempfile. * Add config for unfocused window cursor change * Add support for cursor shape escape sequence * Add bright foreground color option It was requested in jwilm/alacritty#825 that it should be possible to add an optional bright foreground color. This is now added to the primary colors structure and allows the user to set a foreground color for bold normal text. This has no effect unless the draw_bold_text_with_bright_colors option is also enabled. If the color is not specified, the bright foreground color will fall back to the normal foreground color. This fixes #825. * Fix clone URL in deb install instructions * Fix 'cargo-deb' desktop file name * Remove redundant dependency from deb build * Switch from deprecated `std::env::home_dir` to `dirs::home_dir` * Allow specifying modifiers for mouse bindings * Send newline with NumpadEnter * Add support for LCD-V pixel mode * Add binding action for hiding the window * Switch to rustup clippy component * Add optional dim foreground color Add optional color for the dim foreground (`\e[2m;`) Defaults to 2/3 of the foreground color. (same as other colors). If a bright color is dimmed, it's displayed as the normal color. The exception for this is when the bright foreground is dimmed when no bright foreground color is set. In that case it's treated as a normal foreground color and dimmed to DimForeground. To minimize the surprise for the user, the bright and dim colors have been completely removed from the default configuration file. Some documentation has also been added to make it clear to users what these options can be used for. This fixes #1448. * Fix clippy lints and run font tests on travis This fixes some existing clippy issues and runs the `font` tests through travis. Testing of copypasta crate was omitted due to problens when running on headless travis-ci environment (x11 clipboard would fail). * Ignore errors when logger can't write to output The (e)print macro will panic when there is no output available to write to, however in our scenario where we only log user errors to stderr, the better choice would be to ignore when writing to stdout or stderr is not possible. This changes the (e)print macro to make use of `write` and ignore any potential errors. Since (e)println rely on (e)print, this also solves potential failuers when calling (e)println. With this change implemented, all of logging, (e)println and (e)print should never fail even if the stdout/stderr is not available.
2018-07-28 23:10:13 +00:00
"Hide" => Action::Hide,
"Quit" => Action::Quit,
_ => return Err(E::invalid_value(Unexpected::Str(value), &self)),
}))
}
}
deserializer.deserialize_str(ActionVisitor)
}
}
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum CommandWrapper {
Just(String),
WithArgs {
program: String,
#[serde(default)]
args: Vec<String>,
},
}
use ::term::{mode, TermMode};
struct ModeWrapper {
pub mode: TermMode,
pub not_mode: TermMode,
}
2017-08-30 18:17:27 +00:00
impl<'a> de::Deserialize<'a> for ModeWrapper {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
2017-08-30 18:17:27 +00:00
where D: de::Deserializer<'a>
{
struct ModeVisitor;
2017-08-30 18:17:27 +00:00
impl<'a> Visitor<'a> for ModeVisitor {
type Value = ModeWrapper;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Combination of AppCursor | AppKeypad, possibly with negation (~)")
}
fn visit_str<E>(self, value: &str) -> ::std::result::Result<ModeWrapper, E>
where E: de::Error,
{
let mut res = ModeWrapper {
mode: TermMode::empty(),
not_mode: TermMode::empty()
};
for modifier in value.split('|') {
2017-01-05 13:33:45 +00:00
match modifier.trim() {
2017-10-12 01:52:23 +00:00
"AppCursor" => res.mode |= mode::TermMode::APP_CURSOR,
"~AppCursor" => res.not_mode |= mode::TermMode::APP_CURSOR,
"AppKeypad" => res.mode |= mode::TermMode::APP_KEYPAD,
"~AppKeypad" => res.not_mode |= mode::TermMode::APP_KEYPAD,
2017-10-22 15:41:11 +00:00
_ => eprintln!("unknown mode {:?}", modifier),
}
}
Ok(res)
}
}
deserializer.deserialize_str(ModeVisitor)
}
}
struct MouseButton(::glutin::MouseButton);
impl MouseButton {
fn into_inner(self) -> ::glutin::MouseButton {
self.0
}
}
2017-08-30 18:17:27 +00:00
impl<'a> de::Deserialize<'a> for MouseButton {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
2017-08-30 18:17:27 +00:00
where D: de::Deserializer<'a>
{
struct MouseButtonVisitor;
2017-08-30 18:17:27 +00:00
impl<'a> Visitor<'a> for MouseButtonVisitor {
type Value = MouseButton;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Left, Right, Middle, or a number")
}
fn visit_str<E>(self, value: &str) -> ::std::result::Result<MouseButton, E>
where E: de::Error,
{
match value {
"Left" => Ok(MouseButton(::glutin::MouseButton::Left)),
"Right" => Ok(MouseButton(::glutin::MouseButton::Right)),
"Middle" => Ok(MouseButton(::glutin::MouseButton::Middle)),
_ => {
if let Ok(index) = u8::from_str(value) {
Ok(MouseButton(::glutin::MouseButton::Other(index)))
} else {
Err(E::invalid_value(Unexpected::Str(value), &self))
}
}
}
}
}
deserializer.deserialize_str(MouseButtonVisitor)
}
}
/// Bindings are deserialized into a `RawBinding` before being parsed as a
/// `KeyBinding` or `MouseBinding`.
struct RawBinding {
key: Option<::glutin::VirtualKeyCode>,
mouse: Option<::glutin::MouseButton>,
mods: ModifiersState,
mode: TermMode,
notmode: TermMode,
action: Action,
}
impl RawBinding {
fn into_mouse_binding(self) -> ::std::result::Result<MouseBinding, Self> {
if let Some(mouse) = self.mouse {
Ok(Binding {
trigger: mouse,
mods: self.mods,
action: self.action,
mode: self.mode,
notmode: self.notmode,
})
} else {
Err(self)
}
}
fn into_key_binding(self) -> ::std::result::Result<KeyBinding, Self> {
if let Some(key) = self.key {
Ok(KeyBinding {
trigger: key,
mods: self.mods,
action: self.action,
mode: self.mode,
notmode: self.notmode,
})
} else {
Err(self)
}
}
}
2017-08-30 18:17:27 +00:00
impl<'a> de::Deserialize<'a> for RawBinding {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
2017-08-30 18:17:27 +00:00
where D: de::Deserializer<'a>
{
enum Field {
Key,
Mods,
Mode,
Action,
Chars,
Mouse,
Command,
}
2017-08-30 18:17:27 +00:00
impl<'a> de::Deserialize<'a> for Field {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Field, D::Error>
2017-08-30 18:17:27 +00:00
where D: de::Deserializer<'a>
{
struct FieldVisitor;
static FIELDS: &'static [&'static str] = &[
"key", "mods", "mode", "action", "chars", "mouse", "command",
];
2017-08-30 18:17:27 +00:00
impl<'a> Visitor<'a> for FieldVisitor {
type Value = Field;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("binding fields")
}
fn visit_str<E>(self, value: &str) -> ::std::result::Result<Field, E>
where E: de::Error,
{
match value {
"key" => Ok(Field::Key),
"mods" => Ok(Field::Mods),
"mode" => Ok(Field::Mode),
"action" => Ok(Field::Action),
"chars" => Ok(Field::Chars),
"mouse" => Ok(Field::Mouse),
"command" => Ok(Field::Command),
_ => Err(E::unknown_field(value, FIELDS)),
}
}
}
2017-08-30 18:17:27 +00:00
deserializer.deserialize_struct("Field", FIELDS, FieldVisitor)
}
}
struct RawBindingVisitor;
2017-08-30 18:17:27 +00:00
impl<'a> Visitor<'a> for RawBindingVisitor {
type Value = RawBinding;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("binding specification")
}
2016-12-17 06:48:04 +00:00
fn visit_map<V>(
self,
2017-08-30 18:17:27 +00:00
mut map: V
2016-12-17 06:48:04 +00:00
) -> ::std::result::Result<RawBinding, V::Error>
2017-08-30 18:17:27 +00:00
where V: MapAccess<'a>,
{
let mut mods: Option<ModifiersState> = None;
let mut key: Option<::glutin::VirtualKeyCode> = None;
let mut chars: Option<String> = None;
let mut action: Option<::input::Action> = None;
let mut mode: Option<TermMode> = None;
let mut not_mode: Option<TermMode> = None;
let mut mouse: Option<::glutin::MouseButton> = None;
let mut command: Option<CommandWrapper> = None;
use ::serde::de::Error;
2017-08-30 18:17:27 +00:00
while let Some(struct_key) = map.next_key::<Field>()? {
match struct_key {
Field::Key => {
if key.is_some() {
return Err(<V::Error as Error>::duplicate_field("key"));
}
2017-08-30 18:17:27 +00:00
let coherent_key = map.next_value::<Key>()?;
key = Some(coherent_key.to_glutin_key());
},
Field::Mods => {
if mods.is_some() {
return Err(<V::Error as Error>::duplicate_field("mods"));
}
2017-08-30 18:17:27 +00:00
mods = Some(map.next_value::<ModsWrapper>()?.into_inner());
},
Field::Mode => {
if mode.is_some() {
return Err(<V::Error as Error>::duplicate_field("mode"));
}
2017-08-30 18:17:27 +00:00
let mode_deserializer = map.next_value::<ModeWrapper>()?;
mode = Some(mode_deserializer.mode);
not_mode = Some(mode_deserializer.not_mode);
},
Field::Action => {
if action.is_some() {
return Err(<V::Error as Error>::duplicate_field("action"));
}
2017-08-30 18:17:27 +00:00
action = Some(map.next_value::<ActionWrapper>()?.into_inner());
},
Field::Chars => {
if chars.is_some() {
return Err(<V::Error as Error>::duplicate_field("chars"));
}
2017-08-30 18:17:27 +00:00
chars = Some(map.next_value()?);
},
Field::Mouse => {
if chars.is_some() {
return Err(<V::Error as Error>::duplicate_field("mouse"));
}
2017-08-30 18:17:27 +00:00
mouse = Some(map.next_value::<MouseButton>()?.into_inner());
},
Field::Command => {
if command.is_some() {
return Err(<V::Error as Error>::duplicate_field("command"));
}
2017-08-30 18:17:27 +00:00
command = Some(map.next_value::<CommandWrapper>()?);
},
}
}
let action = match (action, chars, command) {
(Some(action), None, None) => action,
(None, Some(chars), None) => Action::Esc(chars),
(None, None, Some(cmd)) => {
match cmd {
CommandWrapper::Just(program) => {
Action::Command(program, vec![])
},
CommandWrapper::WithArgs { program, args } => {
Action::Command(program, args)
},
}
},
(None, None, None) => return Err(V::Error::custom("must specify chars, action or command")),
_ => return Err(V::Error::custom("must specify only chars, action or command")),
};
let mode = mode.unwrap_or_else(TermMode::empty);
let not_mode = not_mode.unwrap_or_else(TermMode::empty);
let mods = mods.unwrap_or_else(ModifiersState::default);
if mouse.is_none() && key.is_none() {
return Err(V::Error::custom("bindings require mouse button or key"));
}
Ok(RawBinding {
mode,
notmode: not_mode,
action,
key,
mouse,
mods,
})
}
}
const FIELDS: &[&str] = &[
"key", "mods", "mode", "action", "chars", "mouse", "command",
];
deserializer.deserialize_struct("RawBinding", FIELDS, RawBindingVisitor)
}
}
2017-08-30 18:17:27 +00:00
impl<'a> de::Deserialize<'a> for Alpha {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
2017-08-30 18:17:27 +00:00
where D: de::Deserializer<'a>
{
let value = f32::deserialize(deserializer)?;
Ok(Alpha::new(value))
}
}
2017-08-30 18:17:27 +00:00
impl<'a> de::Deserialize<'a> for MouseBinding {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
2017-08-30 18:17:27 +00:00
where D: de::Deserializer<'a>
{
let raw = RawBinding::deserialize(deserializer)?;
raw.into_mouse_binding()
.map_err(|_| D::Error::custom("expected mouse binding"))
}
}
2017-08-30 18:17:27 +00:00
impl<'a> de::Deserialize<'a> for KeyBinding {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
2017-08-30 18:17:27 +00:00
where D: de::Deserializer<'a>
{
let raw = RawBinding::deserialize(deserializer)?;
raw.into_key_binding()
.map_err(|_| D::Error::custom("expected key binding"))
}
}
/// Errors occurring during config loading
#[derive(Debug)]
pub enum Error {
/// Config file not found
NotFound,
/// Config file empty
Empty,
/// 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),
}
#[derive(Debug, Deserialize)]
pub struct Colors {
#[serde(default, deserialize_with = "failure_default")]
pub primary: PrimaryColors,
#[serde(default, deserialize_with = "deserialize_cursor_colors")]
pub cursor: CursorColors,
pub normal: AnsiColors,
pub bright: AnsiColors,
#[serde(default, deserialize_with = "failure_default")]
pub dim: Option<AnsiColors>,
}
2017-08-30 18:17:27 +00:00
fn deserialize_cursor_colors<'a, D>(deserializer: D) -> ::std::result::Result<CursorColors, D::Error>
where D: de::Deserializer<'a>
{
match CursorOrPrimaryColors::deserialize(deserializer) {
Ok(either) => Ok(either.into_cursor_colors()),
Err(err) => {
eprintln!("problem with config: {}; Using default value", err);
Ok(CursorColors::default())
},
}
}
#[derive(Deserialize)]
#[serde(untagged)]
pub enum CursorOrPrimaryColors {
Cursor {
#[serde(deserialize_with = "rgb_from_hex")]
text: Rgb,
#[serde(deserialize_with = "rgb_from_hex")]
cursor: Rgb,
},
Primary {
#[serde(deserialize_with = "rgb_from_hex")]
foreground: Rgb,
#[serde(deserialize_with = "rgb_from_hex")]
background: Rgb,
}
}
impl CursorOrPrimaryColors {
fn into_cursor_colors(self) -> CursorColors {
match self {
CursorOrPrimaryColors::Cursor { text, cursor } => CursorColors {
text,
cursor,
},
CursorOrPrimaryColors::Primary { foreground, background } => {
// Must print in config since logger isn't setup yet.
eprintln!("{}",
Yellow("Config `colors.cursor.foreground` and `colors.cursor.background` \
are deprecated. Please use `colors.cursor.text` and \
`colors.cursor.cursor` instead.")
);
CursorColors {
text: foreground,
cursor: background
}
}
}
}
}
#[derive(Debug)]
pub struct CursorColors {
pub text: Rgb,
pub cursor: Rgb,
}
impl Default for CursorColors {
fn default() -> Self {
CursorColors {
text: Rgb { r: 0, g: 0, b: 0 },
cursor: Rgb { r: 0xff, g: 0xff, b: 0xff },
}
}
}
#[derive(Debug, Deserialize)]
pub struct PrimaryColors {
#[serde(deserialize_with = "rgb_from_hex")]
pub background: Rgb,
#[serde(deserialize_with = "rgb_from_hex")]
pub foreground: Rgb,
Merge master into scrollback * Allow disabling DPI scaling This makes it possible to disable DPI scaling completely, instead the the display pixel ration will always be fixed to 1.0. By default nothing has changed and DPI is still enabled, this just seems like a better way than running `WINIT_HIDPI_FACTOR=1.0 alacritty` every time the user wants to start alacritty. It would be possible to allow specifying any DPR, however I've decided against this since I'd assume it's a very rare usecase. It's also still possible to make use of `WINIT_HIDPI_FACTOR` to do this on X11. Currently this is not updated at runtime using the live config update, there is not really much of a technical limitation why this woudn't be possible, however a solution for that issue should be first added in jwilm/alacritty#1346, once a system is established for changing DPI at runtime, porting that functionality to this PR should be simple. * Add working --class and --title CLI parameters * Reduce Increase-/DecreaseFontSize step to 0.5 Until now the Increase-/DecreaseFontSize keybinds hand a step size of 1.0. Since the font size however is multiplied by two to allow more granular font size control, this lead to the bindings skipping one font size (incrementing/decrementing by +-2). To fix this the step size of the Increase-/DecreaseFontSize bindings has been reduced to the minimum step size that exists with the current font configuration (0.5). This should allow users to increment and decrement the font size by a single point instead of two. This also adds a few tests to make sure the methods for increasing/decreasing/resetting font size work properly. * Add Copy/Cut/Paste keys This just adds support for the Copy/Cut/Paste keys and sets up Copy/Paste as alternative defaults for Ctrl+Shift+C/V. * Move to cargo clippy Using clippy as a library has been deprecated, instead the `cargo clippy` command should be used instead. To comply with this change clippy has been removed from the `Cargo.toml` and is now installed with cargo when building in CI. This has also lead to a few new clippy issues to show up, this includes everything in the `font` subdirectory. This has been fixed and `font` should now be covered by clippy CI too. This also upgrades all dependencies, as a result this fixes #1341 and this fixes #1344. * Override dynamic_title when --title is specified * Change green implementation to use the macro * Ignore mouse input if window is unfocused * Make compilation of binary a phony target * Add opensuse zypper install method to readme * Fix clippy issues * Update manpage to document all CLI options The introduction of `--class` has added a flag to the CLI without adding it to the manpage. This has been fixed by updating the manpage. This also adds the default values of `--class` and `--title` to the CLI options. * Remove unnecessary clippy lint annotations We moved to "cargo clippy" in 5ba34d4f9766a55a06ed5e3e44cc384af1b09f65 and removing the clippy lint annotations in `src/lib.rs` does not cause any additional warnings. This also changes `cargo clippy` to use the flags required for checking integration tests. * Enable clippy in font/copypasta crates Enabled clippy in the sub-crates font and copypasta. All issues that were discovered by this change have also been fixed. * Remove outdated comment about NixOS * Replace debug asserts with static_assertions To check that transmutes will work correctly without having to rely on error-prone runtime checking, the `static_assertions` crate has been introduced. This allows comparing the size of types at compile time, preventing potentially silent breakage. This fixes #1417. * Add `cargo deb` build instructions Updated the `Cargo.toml` file and added a `package.metadata.deb` subsection to define how to build a debian "deb" install file using `cargo deb`. This will allow debian/ubuntu users to install `alacritty` using their system's package manager. It also will make it easier to provide pre-built binaries for those systems. Also fixed a stray debug line in the bash autocomplete script that was writting to a tempfile. * Add config for unfocused window cursor change * Add support for cursor shape escape sequence * Add bright foreground color option It was requested in jwilm/alacritty#825 that it should be possible to add an optional bright foreground color. This is now added to the primary colors structure and allows the user to set a foreground color for bold normal text. This has no effect unless the draw_bold_text_with_bright_colors option is also enabled. If the color is not specified, the bright foreground color will fall back to the normal foreground color. This fixes #825. * Fix clone URL in deb install instructions * Fix 'cargo-deb' desktop file name * Remove redundant dependency from deb build * Switch from deprecated `std::env::home_dir` to `dirs::home_dir` * Allow specifying modifiers for mouse bindings * Send newline with NumpadEnter * Add support for LCD-V pixel mode * Add binding action for hiding the window * Switch to rustup clippy component * Add optional dim foreground color Add optional color for the dim foreground (`\e[2m;`) Defaults to 2/3 of the foreground color. (same as other colors). If a bright color is dimmed, it's displayed as the normal color. The exception for this is when the bright foreground is dimmed when no bright foreground color is set. In that case it's treated as a normal foreground color and dimmed to DimForeground. To minimize the surprise for the user, the bright and dim colors have been completely removed from the default configuration file. Some documentation has also been added to make it clear to users what these options can be used for. This fixes #1448. * Fix clippy lints and run font tests on travis This fixes some existing clippy issues and runs the `font` tests through travis. Testing of copypasta crate was omitted due to problens when running on headless travis-ci environment (x11 clipboard would fail). * Ignore errors when logger can't write to output The (e)print macro will panic when there is no output available to write to, however in our scenario where we only log user errors to stderr, the better choice would be to ignore when writing to stdout or stderr is not possible. This changes the (e)print macro to make use of `write` and ignore any potential errors. Since (e)println rely on (e)print, this also solves potential failuers when calling (e)println. With this change implemented, all of logging, (e)println and (e)print should never fail even if the stdout/stderr is not available.
2018-07-28 23:10:13 +00:00
#[serde(default, deserialize_with = "deserialize_optional_color")]
pub bright_foreground: Option<Rgb>,
#[serde(default, deserialize_with = "deserialize_optional_color")]
pub dim_foreground: Option<Rgb>,
}
fn deserialize_optional_color<'a, D>(deserializer: D) -> ::std::result::Result<Option<Rgb>, D::Error>
where D: de::Deserializer<'a>
{
match Option::deserialize(deserializer) {
Ok(Some(color)) => {
let color: serde_yaml::Value = color;
Ok(Some(rgb_from_hex(color).unwrap()))
},
Ok(None) => Ok(None),
Err(err) => {
eprintln!("problem with config: {}; Using standard foreground color", err);
Ok(None)
},
}
}
impl Default for PrimaryColors {
fn default() -> Self {
PrimaryColors {
background: Rgb { r: 0, g: 0, b: 0 },
foreground: Rgb { r: 0xea, g: 0xea, b: 0xea },
Merge master into scrollback * Allow disabling DPI scaling This makes it possible to disable DPI scaling completely, instead the the display pixel ration will always be fixed to 1.0. By default nothing has changed and DPI is still enabled, this just seems like a better way than running `WINIT_HIDPI_FACTOR=1.0 alacritty` every time the user wants to start alacritty. It would be possible to allow specifying any DPR, however I've decided against this since I'd assume it's a very rare usecase. It's also still possible to make use of `WINIT_HIDPI_FACTOR` to do this on X11. Currently this is not updated at runtime using the live config update, there is not really much of a technical limitation why this woudn't be possible, however a solution for that issue should be first added in jwilm/alacritty#1346, once a system is established for changing DPI at runtime, porting that functionality to this PR should be simple. * Add working --class and --title CLI parameters * Reduce Increase-/DecreaseFontSize step to 0.5 Until now the Increase-/DecreaseFontSize keybinds hand a step size of 1.0. Since the font size however is multiplied by two to allow more granular font size control, this lead to the bindings skipping one font size (incrementing/decrementing by +-2). To fix this the step size of the Increase-/DecreaseFontSize bindings has been reduced to the minimum step size that exists with the current font configuration (0.5). This should allow users to increment and decrement the font size by a single point instead of two. This also adds a few tests to make sure the methods for increasing/decreasing/resetting font size work properly. * Add Copy/Cut/Paste keys This just adds support for the Copy/Cut/Paste keys and sets up Copy/Paste as alternative defaults for Ctrl+Shift+C/V. * Move to cargo clippy Using clippy as a library has been deprecated, instead the `cargo clippy` command should be used instead. To comply with this change clippy has been removed from the `Cargo.toml` and is now installed with cargo when building in CI. This has also lead to a few new clippy issues to show up, this includes everything in the `font` subdirectory. This has been fixed and `font` should now be covered by clippy CI too. This also upgrades all dependencies, as a result this fixes #1341 and this fixes #1344. * Override dynamic_title when --title is specified * Change green implementation to use the macro * Ignore mouse input if window is unfocused * Make compilation of binary a phony target * Add opensuse zypper install method to readme * Fix clippy issues * Update manpage to document all CLI options The introduction of `--class` has added a flag to the CLI without adding it to the manpage. This has been fixed by updating the manpage. This also adds the default values of `--class` and `--title` to the CLI options. * Remove unnecessary clippy lint annotations We moved to "cargo clippy" in 5ba34d4f9766a55a06ed5e3e44cc384af1b09f65 and removing the clippy lint annotations in `src/lib.rs` does not cause any additional warnings. This also changes `cargo clippy` to use the flags required for checking integration tests. * Enable clippy in font/copypasta crates Enabled clippy in the sub-crates font and copypasta. All issues that were discovered by this change have also been fixed. * Remove outdated comment about NixOS * Replace debug asserts with static_assertions To check that transmutes will work correctly without having to rely on error-prone runtime checking, the `static_assertions` crate has been introduced. This allows comparing the size of types at compile time, preventing potentially silent breakage. This fixes #1417. * Add `cargo deb` build instructions Updated the `Cargo.toml` file and added a `package.metadata.deb` subsection to define how to build a debian "deb" install file using `cargo deb`. This will allow debian/ubuntu users to install `alacritty` using their system's package manager. It also will make it easier to provide pre-built binaries for those systems. Also fixed a stray debug line in the bash autocomplete script that was writting to a tempfile. * Add config for unfocused window cursor change * Add support for cursor shape escape sequence * Add bright foreground color option It was requested in jwilm/alacritty#825 that it should be possible to add an optional bright foreground color. This is now added to the primary colors structure and allows the user to set a foreground color for bold normal text. This has no effect unless the draw_bold_text_with_bright_colors option is also enabled. If the color is not specified, the bright foreground color will fall back to the normal foreground color. This fixes #825. * Fix clone URL in deb install instructions * Fix 'cargo-deb' desktop file name * Remove redundant dependency from deb build * Switch from deprecated `std::env::home_dir` to `dirs::home_dir` * Allow specifying modifiers for mouse bindings * Send newline with NumpadEnter * Add support for LCD-V pixel mode * Add binding action for hiding the window * Switch to rustup clippy component * Add optional dim foreground color Add optional color for the dim foreground (`\e[2m;`) Defaults to 2/3 of the foreground color. (same as other colors). If a bright color is dimmed, it's displayed as the normal color. The exception for this is when the bright foreground is dimmed when no bright foreground color is set. In that case it's treated as a normal foreground color and dimmed to DimForeground. To minimize the surprise for the user, the bright and dim colors have been completely removed from the default configuration file. Some documentation has also been added to make it clear to users what these options can be used for. This fixes #1448. * Fix clippy lints and run font tests on travis This fixes some existing clippy issues and runs the `font` tests through travis. Testing of copypasta crate was omitted due to problens when running on headless travis-ci environment (x11 clipboard would fail). * Ignore errors when logger can't write to output The (e)print macro will panic when there is no output available to write to, however in our scenario where we only log user errors to stderr, the better choice would be to ignore when writing to stdout or stderr is not possible. This changes the (e)print macro to make use of `write` and ignore any potential errors. Since (e)println rely on (e)print, this also solves potential failuers when calling (e)println. With this change implemented, all of logging, (e)println and (e)print should never fail even if the stdout/stderr is not available.
2018-07-28 23:10:13 +00:00
bright_foreground: None,
dim_foreground: None,
}
}
}
impl Default for Colors {
fn default() -> Colors {
Colors {
primary: PrimaryColors::default(),
cursor: CursorColors::default(),
normal: AnsiColors {
black: Rgb {r: 0x00, g: 0x00, b: 0x00},
red: Rgb {r: 0xd5, g: 0x4e, b: 0x53},
green: Rgb {r: 0xb9, g: 0xca, b: 0x4a},
yellow: Rgb {r: 0xe6, g: 0xc5, b: 0x47},
blue: Rgb {r: 0x7a, g: 0xa6, b: 0xda},
magenta: Rgb {r: 0xc3, g: 0x97, b: 0xd8},
cyan: Rgb {r: 0x70, g: 0xc0, b: 0xba},
white: Rgb {r: 0xea, g: 0xea, b: 0xea},
},
bright: AnsiColors {
black: Rgb {r: 0x66, g: 0x66, b: 0x66},
red: Rgb {r: 0xff, g: 0x33, b: 0x34},
green: Rgb {r: 0x9e, g: 0xc4, b: 0x00},
yellow: Rgb {r: 0xe7, g: 0xc5, b: 0x47},
blue: Rgb {r: 0x7a, g: 0xa6, b: 0xda},
magenta: Rgb {r: 0xb7, g: 0x7e, b: 0xe0},
cyan: Rgb {r: 0x54, g: 0xce, b: 0xd6},
white: Rgb {r: 0xff, g: 0xff, b: 0xff},
},
dim: None,
}
}
}
/// The 8-colors sections of config
#[derive(Debug, Deserialize)]
pub struct AnsiColors {
#[serde(deserialize_with = "rgb_from_hex")]
pub black: Rgb,
#[serde(deserialize_with = "rgb_from_hex")]
pub red: Rgb,
#[serde(deserialize_with = "rgb_from_hex")]
pub green: Rgb,
#[serde(deserialize_with = "rgb_from_hex")]
pub yellow: Rgb,
#[serde(deserialize_with = "rgb_from_hex")]
pub blue: Rgb,
#[serde(deserialize_with = "rgb_from_hex")]
pub magenta: Rgb,
#[serde(deserialize_with = "rgb_from_hex")]
pub cyan: Rgb,
#[serde(deserialize_with = "rgb_from_hex")]
pub white: Rgb,
}
/// Deserialize an Rgb from a hex string
///
/// This is *not* the deserialize impl for Rgb since we want a symmetric
/// serialize/deserialize impl for ref tests.
2017-08-30 18:17:27 +00:00
fn rgb_from_hex<'a, D>(deserializer: D) -> ::std::result::Result<Rgb, D::Error>
where D: de::Deserializer<'a>
{
struct RgbVisitor;
2017-08-30 18:17:27 +00:00
impl<'a> Visitor<'a> for RgbVisitor {
type Value = Rgb;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Hex colors spec like 'ffaabb'")
}
fn visit_str<E>(self, value: &str) -> ::std::result::Result<Rgb, E>
where E: ::serde::de::Error
{
Rgb::from_str(&value[..])
.map_err(|_| E::custom("failed to parse rgb; expect 0xrrggbb"))
}
}
let rgb = deserializer.deserialize_str(RgbVisitor);
// Use #ff00ff as fallback color
match rgb {
Ok(rgb) => Ok(rgb),
Err(err) => {
eprintln!("problem with config: {}; Using color #ff00ff", err);
Ok(Rgb { r: 255, g: 0, b: 255 })
},
}
}
2017-01-07 04:44:51 +00:00
impl FromStr for Rgb {
type Err = ();
fn from_str(s: &str) -> ::std::result::Result<Rgb, ()> {
let mut chars = s.chars();
let mut rgb = Rgb::default();
macro_rules! component {
($($c:ident),*) => {
$(
match chars.next().and_then(|c| c.to_digit(16)) {
Some(val) => rgb.$c = (val as u8) << 4,
None => return Err(())
}
match chars.next().and_then(|c| c.to_digit(16)) {
Some(val) => rgb.$c |= val as u8,
None => return Err(())
}
)*
}
}
match chars.next() {
Some('0') => if chars.next() != Some('x') { return Err(()); },
Some('#') => (),
2017-06-12 10:16:57 +00:00
_ => return Err(()),
}
component!(r, g, b);
Ok(rgb)
}
}
impl ::std::error::Error for Error {
fn cause(&self) -> Option<&::std::error::Error> {
match *self {
Error::NotFound | Error::Empty => 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::Empty => "empty 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 | Error::Empty => 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 {
/// Get the location of the first found default config file paths
/// according to the following order:
///
/// 1. $XDG_CONFIG_HOME/alacritty/alacritty.yml
/// 2. $XDG_CONFIG_HOME/alacritty.yml
/// 3. $HOME/.config/alacritty/alacritty.yml
/// 4. $HOME/.alacritty.yml
pub fn installed_config<'a>() -> Option<Cow<'a, Path>> {
// Try using XDG location by default
::xdg::BaseDirectories::with_prefix("alacritty")
.ok()
.and_then(|xdg| xdg.find_config_file("alacritty.yml"))
.or_else(|| {
::xdg::BaseDirectories::new().ok().and_then(|fallback| {
fallback.find_config_file("alacritty.yml")
})
})
.or_else(|| {
if let Ok(home) = env::var("HOME") {
// Fallback path: $HOME/.config/alacritty/alacritty.yml
let fallback = PathBuf::from(&home).join(".config/alacritty/alacritty.yml");
if fallback.exists() {
return Some(fallback);
}
// Fallback path: $HOME/.alacritty.yml
let fallback = PathBuf::from(&home).join(".alacritty.yml");
if fallback.exists() {
return Some(fallback);
}
}
None
})
.map(|path| path.into())
}
pub fn write_defaults() -> io::Result<Cow<'static, Path>> {
let path = ::xdg::BaseDirectories::with_prefix("alacritty")
2017-01-06 04:56:05 +00:00
.map_err(|err| io::Error::new(io::ErrorKind::NotFound, ::std::error::Error::description(&err)))
.and_then(|p| p.place_config_file("alacritty.yml"))?;
File::create(&path)?.write_all(DEFAULT_ALACRITTY_CONFIG.as_bytes())?;
Ok(path.into())
2017-01-06 04:56:05 +00:00
}
/// Get list of colors
///
/// The ordering returned here is expected by the terminal. Colors are simply indexed in this
/// array for performance.
pub fn colors(&self) -> &Colors {
&self.colors
}
#[inline]
pub fn background_opacity(&self) -> Alpha {
self.background_opacity
}
pub fn key_bindings(&self) -> &[KeyBinding] {
&self.key_bindings[..]
}
pub fn mouse_bindings(&self) -> &[MouseBinding] {
&self.mouse_bindings[..]
}
pub fn mouse(&self) -> &Mouse {
&self.mouse
}
pub fn selection(&self) -> &Selection {
&self.selection
}
pub fn tabspaces(&self) -> usize {
self.tabspaces
}
pub fn padding(&self) -> &Delta<u8> {
self.padding.as_ref()
.unwrap_or(&self.window.padding)
}
#[inline]
pub fn draw_bold_text_with_bright_colors(&self) -> bool {
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 {
self.dimensions.unwrap_or(self.window.dimensions)
}
/// Get window config
#[inline]
pub fn window(&self) -> &WindowConfig {
&self.window
}
/// Get visual bell config
#[inline]
pub fn visual_bell(&self) -> &VisualBellConfig {
&self.visual_bell
}
/// Should show render timer
#[inline]
pub fn render_timer(&self) -> bool {
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 {
self.custom_cursor_colors
}
pub fn path(&self) -> Option<&Path> {
self.config_path
.as_ref()
.map(|p| p.as_path())
}
pub fn shell(&self) -> Option<&Shell> {
self.shell.as_ref()
}
2017-02-14 02:22:59 +00:00
pub fn env(&self) -> &HashMap<String, String> {
&self.env
}
2017-02-22 19:52:37 +00:00
/// Should hide cursor when typing
#[inline]
pub fn hide_cursor_when_typing(&self) -> bool {
self.hide_cursor_when_typing
}
/// Style of the cursor
#[inline]
pub fn cursor_style(&self) -> CursorStyle {
self.cursor_style
}
Merge master into scrollback * Allow disabling DPI scaling This makes it possible to disable DPI scaling completely, instead the the display pixel ration will always be fixed to 1.0. By default nothing has changed and DPI is still enabled, this just seems like a better way than running `WINIT_HIDPI_FACTOR=1.0 alacritty` every time the user wants to start alacritty. It would be possible to allow specifying any DPR, however I've decided against this since I'd assume it's a very rare usecase. It's also still possible to make use of `WINIT_HIDPI_FACTOR` to do this on X11. Currently this is not updated at runtime using the live config update, there is not really much of a technical limitation why this woudn't be possible, however a solution for that issue should be first added in jwilm/alacritty#1346, once a system is established for changing DPI at runtime, porting that functionality to this PR should be simple. * Add working --class and --title CLI parameters * Reduce Increase-/DecreaseFontSize step to 0.5 Until now the Increase-/DecreaseFontSize keybinds hand a step size of 1.0. Since the font size however is multiplied by two to allow more granular font size control, this lead to the bindings skipping one font size (incrementing/decrementing by +-2). To fix this the step size of the Increase-/DecreaseFontSize bindings has been reduced to the minimum step size that exists with the current font configuration (0.5). This should allow users to increment and decrement the font size by a single point instead of two. This also adds a few tests to make sure the methods for increasing/decreasing/resetting font size work properly. * Add Copy/Cut/Paste keys This just adds support for the Copy/Cut/Paste keys and sets up Copy/Paste as alternative defaults for Ctrl+Shift+C/V. * Move to cargo clippy Using clippy as a library has been deprecated, instead the `cargo clippy` command should be used instead. To comply with this change clippy has been removed from the `Cargo.toml` and is now installed with cargo when building in CI. This has also lead to a few new clippy issues to show up, this includes everything in the `font` subdirectory. This has been fixed and `font` should now be covered by clippy CI too. This also upgrades all dependencies, as a result this fixes #1341 and this fixes #1344. * Override dynamic_title when --title is specified * Change green implementation to use the macro * Ignore mouse input if window is unfocused * Make compilation of binary a phony target * Add opensuse zypper install method to readme * Fix clippy issues * Update manpage to document all CLI options The introduction of `--class` has added a flag to the CLI without adding it to the manpage. This has been fixed by updating the manpage. This also adds the default values of `--class` and `--title` to the CLI options. * Remove unnecessary clippy lint annotations We moved to "cargo clippy" in 5ba34d4f9766a55a06ed5e3e44cc384af1b09f65 and removing the clippy lint annotations in `src/lib.rs` does not cause any additional warnings. This also changes `cargo clippy` to use the flags required for checking integration tests. * Enable clippy in font/copypasta crates Enabled clippy in the sub-crates font and copypasta. All issues that were discovered by this change have also been fixed. * Remove outdated comment about NixOS * Replace debug asserts with static_assertions To check that transmutes will work correctly without having to rely on error-prone runtime checking, the `static_assertions` crate has been introduced. This allows comparing the size of types at compile time, preventing potentially silent breakage. This fixes #1417. * Add `cargo deb` build instructions Updated the `Cargo.toml` file and added a `package.metadata.deb` subsection to define how to build a debian "deb" install file using `cargo deb`. This will allow debian/ubuntu users to install `alacritty` using their system's package manager. It also will make it easier to provide pre-built binaries for those systems. Also fixed a stray debug line in the bash autocomplete script that was writting to a tempfile. * Add config for unfocused window cursor change * Add support for cursor shape escape sequence * Add bright foreground color option It was requested in jwilm/alacritty#825 that it should be possible to add an optional bright foreground color. This is now added to the primary colors structure and allows the user to set a foreground color for bold normal text. This has no effect unless the draw_bold_text_with_bright_colors option is also enabled. If the color is not specified, the bright foreground color will fall back to the normal foreground color. This fixes #825. * Fix clone URL in deb install instructions * Fix 'cargo-deb' desktop file name * Remove redundant dependency from deb build * Switch from deprecated `std::env::home_dir` to `dirs::home_dir` * Allow specifying modifiers for mouse bindings * Send newline with NumpadEnter * Add support for LCD-V pixel mode * Add binding action for hiding the window * Switch to rustup clippy component * Add optional dim foreground color Add optional color for the dim foreground (`\e[2m;`) Defaults to 2/3 of the foreground color. (same as other colors). If a bright color is dimmed, it's displayed as the normal color. The exception for this is when the bright foreground is dimmed when no bright foreground color is set. In that case it's treated as a normal foreground color and dimmed to DimForeground. To minimize the surprise for the user, the bright and dim colors have been completely removed from the default configuration file. Some documentation has also been added to make it clear to users what these options can be used for. This fixes #1448. * Fix clippy lints and run font tests on travis This fixes some existing clippy issues and runs the `font` tests through travis. Testing of copypasta crate was omitted due to problens when running on headless travis-ci environment (x11 clipboard would fail). * Ignore errors when logger can't write to output The (e)print macro will panic when there is no output available to write to, however in our scenario where we only log user errors to stderr, the better choice would be to ignore when writing to stdout or stderr is not possible. This changes the (e)print macro to make use of `write` and ignore any potential errors. Since (e)println rely on (e)print, this also solves potential failuers when calling (e)println. With this change implemented, all of logging, (e)println and (e)print should never fail even if the stdout/stderr is not available.
2018-07-28 23:10:13 +00:00
/// Use hollow block cursor when unfocused
#[inline]
pub fn unfocused_hollow_cursor(&self) -> bool {
self.unfocused_hollow_cursor
}
/// Live config reload
#[inline]
pub fn live_config_reload(&self) -> bool {
self.live_config_reload
}
#[inline]
pub fn dynamic_title(&self) -> bool {
self.dynamic_title
}
/// Scrolling settings
#[inline]
pub fn scrolling(&self) -> Scrolling {
self.scrolling
}
// Update the history size, used in ref tests
pub fn set_history(&mut self, history: u32) {
self.scrolling.history = history;
}
pub fn load_from<P: Into<PathBuf>>(path: P) -> Result<Config> {
let path = path.into();
let raw = Config::read_file(path.as_path())?;
let mut config: Config = serde_yaml::from_str(&raw)?;
config.config_path = Some(path);
config.print_deprecation_warnings();
Ok(config)
}
/// Overrides the `dynamic_title` configuration based on `--title`.
pub fn update_dynamic_title(mut self, options: &Options) -> Self {
if options.title.is_some() {
self.dynamic_title = false;
}
self
}
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)?;
if contents.is_empty() {
return Err(Error::Empty);
}
Ok(contents)
}
fn print_deprecation_warnings(&self) {
use ::util::fmt;
if self.dimensions.is_some() {
eprintln!("{}", fmt::Yellow("Config `dimensions` is deprecated. \
Please use `window.dimensions` instead."));
}
if self.padding.is_some() {
eprintln!("{}", fmt::Yellow("Config `padding` is deprecated. \
Please use `window.padding` instead."));
}
if self.mouse.faux_scrollback_lines.is_some() {
println!("{}", fmt::Yellow("Config `mouse.faux_scrollback_lines` is deprecated. \
Please use `mouse.faux_scrolling_lines` instead."));
}
}
}
/// Window Dimensions
///
/// Newtype to avoid passing values incorrectly
#[derive(Debug, Copy, Clone, Deserialize)]
pub struct Dimensions {
/// Window width in character columns
columns: Column,
/// Window Height in character lines
lines: Line,
}
impl Default for Dimensions {
fn default() -> Dimensions {
Dimensions::new(Column(80), Line(24))
}
}
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
}
}
/// A delta for a point in a 2 dimensional plane
#[derive(Clone, Copy, Debug, Default, Deserialize)]
#[serde(bound(deserialize = "T: Deserialize<'de> + Default"))]
pub struct Delta<T: Default> {
/// Horizontal change
#[serde(default, deserialize_with = "failure_default")]
pub x: T,
/// Vertical change
#[serde(default, deserialize_with = "failure_default")]
pub y: T,
}
trait DeserializeSize : Sized {
fn deserialize<'a, D>(D) -> ::std::result::Result<Self, D::Error>
2017-08-30 18:17:27 +00:00
where D: serde::de::Deserializer<'a>;
}
impl DeserializeSize for Size {
fn deserialize<'a, D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
2017-08-30 18:17:27 +00:00
where D: serde::de::Deserializer<'a>
{
use std::marker::PhantomData;
struct NumVisitor<__D> {
_marker: PhantomData<__D>,
}
impl<'a, __D> Visitor<'a> for NumVisitor<__D>
2017-08-30 18:17:27 +00:00
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<i8>,
/// Glyph offset within character cell
#[serde(default, deserialize_with = "failure_default")]
glyph_offset: Delta<i8>,
#[serde(default="true_bool", deserialize_with = "default_true_bool")]
use_thin_strokes: bool,
#[serde(default="true_bool", deserialize_with = "default_true_bool")]
scale_with_dpi: 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<i8> {
&self.offset
}
/// Get cell offsets for glyphs
#[inline]
pub fn glyph_offset(&self) -> &Delta<i8> {
&self.glyph_offset
}
/// Get a font clone with a size modification
pub fn with_size(self, size: Size) -> Font {
Font {
size,
.. self
}
}
/// Check whether dpi should be applied
pub fn scale_with_dpi(&self) -> bool {
self.scale_with_dpi
}
}
#[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,
scale_with_dpi: true,
glyph_offset: Default::default(),
offset: Default::default(),
}
}
}
#[cfg(any(target_os = "linux",target_os = "freebsd",target_os = "openbsd"))]
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,
scale_with_dpi: true,
glyph_offset: Default::default(),
offset: Default::default(),
}
}
}
pub struct Monitor {
_thread: ::std::thread::JoinHandle<()>,
rx: mpsc::Receiver<Config>,
}
pub trait OnConfigReload {
fn on_config_reload(&mut self);
}
impl OnConfigReload for ::display::Notifier {
fn on_config_reload(&mut self) {
self.notify();
}
}
impl Monitor {
/// Get pending config changes
pub fn pending_config(&self) -> Option<Config> {
let mut config = None;
while let Ok(new) = self.rx.try_recv() {
config = Some(new);
}
config
}
pub fn new<H, P>(path: P, mut handler: H) -> Monitor
where H: OnConfigReload + Send + 'static,
P: Into<PathBuf>
{
let path = path.into();
let (config_tx, config_rx) = mpsc::channel();
Monitor {
_thread: ::util::thread::spawn_named("config watcher", move || {
let (tx, rx) = mpsc::channel();
// The Duration argument is a debouncing period.
let mut watcher = watcher(tx, Duration::from_millis(10))
.expect("Unable to spawn file watcher");
let config_path = ::std::fs::canonicalize(path)
.expect("canonicalize config path");
// Get directory of config
let mut parent = config_path.clone();
parent.pop();
// Watch directory
watcher.watch(&parent, RecursiveMode::NonRecursive)
.expect("watch alacritty.yml dir");
loop {
match rx.recv().expect("watcher event") {
2017-08-30 19:34:23 +00:00
DebouncedEvent::Rename(_, _) => continue,
DebouncedEvent::Write(path) | DebouncedEvent::Create(path)
| DebouncedEvent::Chmod(path) => {
// Reload file
if path == config_path {
match Config::load_from(path) {
Ok(config) => {
let _ = config_tx.send(config);
handler.on_config_reload();
},
Err(err) => eprintln!("Ignoring invalid config: {}", err),
}
2017-08-30 19:34:23 +00:00
}
}
_ => {}
}
}
}),
rx: config_rx,
}
}
}
#[cfg(test)]
mod tests {
use cli::Options;
use super::Config;
#[cfg(target_os="macos")]
static ALACRITTY_YML: &'static str =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty_macos.yml"));
#[cfg(not(target_os="macos"))]
static ALACRITTY_YML: &'static str =
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty.yml"));
#[test]
fn parse_config() {
let config: Config = ::serde_yaml::from_str(ALACRITTY_YML)
.expect("deserialize config");
// Sanity check that mouse bindings are being parsed
2018-05-11 18:22:36 +00:00
assert!(!config.mouse_bindings.is_empty());
// Sanity check that key bindings are being parsed
2018-05-11 18:22:36 +00:00
assert!(!config.key_bindings.is_empty());
}
#[test]
fn dynamic_title_ignoring_options_by_default() {
let config: Config = ::serde_yaml::from_str(ALACRITTY_YML)
.expect("deserialize config");
let old_dynamic_title = config.dynamic_title;
let options = Options::default();
let config = config.update_dynamic_title(&options);
assert_eq!(old_dynamic_title, config.dynamic_title);
}
#[test]
fn dynamic_title_overridden_by_options() {
let config: Config = ::serde_yaml::from_str(ALACRITTY_YML)
.expect("deserialize config");
let mut options = Options::default();
options.title = Some("foo".to_owned());
let config = config.update_dynamic_title(&options);
assert!(!config.dynamic_title);
}
}
#[cfg_attr(feature = "cargo-clippy", allow(enum_variant_names))]
#[derive(Deserialize, Copy, Clone)]
enum Key {
Key1,
Key2,
Key3,
Key4,
Key5,
Key6,
Key7,
Key8,
Key9,
Key0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
Escape,
F1,
F2,
F3,
F4,
F5,
F6,
F7,
F8,
F9,
F10,
F11,
F12,
F13,
F14,
F15,
Snapshot,
Scroll,
Pause,
Insert,
Home,
Delete,
End,
PageDown,
PageUp,
Left,
Up,
Right,
Down,
Back,
Return,
Space,
Compose,
Numlock,
Numpad0,
Numpad1,
Numpad2,
Numpad3,
Numpad4,
Numpad5,
Numpad6,
Numpad7,
Numpad8,
Numpad9,
AbntC1,
AbntC2,
Add,
Apostrophe,
Apps,
At,
Ax,
Backslash,
Calculator,
Capital,
Colon,
Comma,
Convert,
Decimal,
Divide,
Equals,
Grave,
Kana,
Kanji,
LAlt,
LBracket,
LControl,
LMenu,
LShift,
LWin,
Mail,
MediaSelect,
MediaStop,
Minus,
Multiply,
Mute,
MyComputer,
NavigateForward,
NavigateBackward,
NextTrack,
NoConvert,
NumpadComma,
NumpadEnter,
NumpadEquals,
OEM102,
Period,
PlayPause,
Power,
PrevTrack,
RAlt,
RBracket,
RControl,
RMenu,
RShift,
RWin,
Semicolon,
Slash,
Sleep,
Stop,
Subtract,
Sysrq,
Tab,
Underline,
Unlabeled,
VolumeDown,
VolumeUp,
Wake,
WebBack,
WebFavorites,
WebForward,
WebHome,
WebRefresh,
WebSearch,
WebStop,
Yen,
2018-01-04 06:58:19 +00:00
Caret,
Copy,
Paste,
Cut,
}
impl Key {
fn to_glutin_key(self) -> ::glutin::VirtualKeyCode {
use ::glutin::VirtualKeyCode::*;
// Thank you, vim macros!
match self {
Key::Key1 => Key1,
Key::Key2 => Key2,
Key::Key3 => Key3,
Key::Key4 => Key4,
Key::Key5 => Key5,
Key::Key6 => Key6,
Key::Key7 => Key7,
Key::Key8 => Key8,
Key::Key9 => Key9,
Key::Key0 => Key0,
Key::A => A,
Key::B => B,
Key::C => C,
Key::D => D,
Key::E => E,
Key::F => F,
Key::G => G,
Key::H => H,
Key::I => I,
Key::J => J,
Key::K => K,
Key::L => L,
Key::M => M,
Key::N => N,
Key::O => O,
Key::P => P,
Key::Q => Q,
Key::R => R,
Key::S => S,
Key::T => T,
Key::U => U,
Key::V => V,
Key::W => W,
Key::X => X,
Key::Y => Y,
Key::Z => Z,
Key::Escape => Escape,
Key::F1 => F1,
Key::F2 => F2,
Key::F3 => F3,
Key::F4 => F4,
Key::F5 => F5,
Key::F6 => F6,
Key::F7 => F7,
Key::F8 => F8,
Key::F9 => F9,
Key::F10 => F10,
Key::F11 => F11,
Key::F12 => F12,
Key::F13 => F13,
Key::F14 => F14,
Key::F15 => F15,
Key::Snapshot => Snapshot,
Key::Scroll => Scroll,
Key::Pause => Pause,
Key::Insert => Insert,
Key::Home => Home,
Key::Delete => Delete,
Key::End => End,
Key::PageDown => PageDown,
Key::PageUp => PageUp,
Key::Left => Left,
Key::Up => Up,
Key::Right => Right,
Key::Down => Down,
Key::Back => Back,
Key::Return => Return,
Key::Space => Space,
Key::Compose => Compose,
Key::Numlock => Numlock,
Key::Numpad0 => Numpad0,
Key::Numpad1 => Numpad1,
Key::Numpad2 => Numpad2,
Key::Numpad3 => Numpad3,
Key::Numpad4 => Numpad4,
Key::Numpad5 => Numpad5,
Key::Numpad6 => Numpad6,
Key::Numpad7 => Numpad7,
Key::Numpad8 => Numpad8,
Key::Numpad9 => Numpad9,
Key::AbntC1 => AbntC1,
Key::AbntC2 => AbntC2,
Key::Add => Add,
Key::Apostrophe => Apostrophe,
Key::Apps => Apps,
Key::At => At,
Key::Ax => Ax,
Key::Backslash => Backslash,
Key::Calculator => Calculator,
Key::Capital => Capital,
Key::Colon => Colon,
Key::Comma => Comma,
Key::Convert => Convert,
Key::Decimal => Decimal,
Key::Divide => Divide,
Key::Equals => Equals,
Key::Grave => Grave,
Key::Kana => Kana,
Key::Kanji => Kanji,
Key::LAlt => LAlt,
Key::LBracket => LBracket,
Key::LControl => LControl,
Key::LMenu => LMenu,
Key::LShift => LShift,
Key::LWin => LWin,
Key::Mail => Mail,
Key::MediaSelect => MediaSelect,
Key::MediaStop => MediaStop,
Key::Minus => Minus,
Key::Multiply => Multiply,
Key::Mute => Mute,
Key::MyComputer => MyComputer,
Key::NavigateForward => NavigateForward,
Key::NavigateBackward => NavigateBackward,
Key::NextTrack => NextTrack,
Key::NoConvert => NoConvert,
Key::NumpadComma => NumpadComma,
Key::NumpadEnter => NumpadEnter,
Key::NumpadEquals => NumpadEquals,
Key::OEM102 => OEM102,
Key::Period => Period,
Key::PlayPause => PlayPause,
Key::Power => Power,
Key::PrevTrack => PrevTrack,
Key::RAlt => RAlt,
Key::RBracket => RBracket,
Key::RControl => RControl,
Key::RMenu => RMenu,
Key::RShift => RShift,
Key::RWin => RWin,
Key::Semicolon => Semicolon,
Key::Slash => Slash,
Key::Sleep => Sleep,
Key::Stop => Stop,
Key::Subtract => Subtract,
Key::Sysrq => Sysrq,
Key::Tab => Tab,
Key::Underline => Underline,
Key::Unlabeled => Unlabeled,
Key::VolumeDown => VolumeDown,
Key::VolumeUp => VolumeUp,
Key::Wake => Wake,
Key::WebBack => WebBack,
Key::WebFavorites => WebFavorites,
Key::WebForward => WebForward,
Key::WebHome => WebHome,
Key::WebRefresh => WebRefresh,
Key::WebSearch => WebSearch,
Key::WebStop => WebStop,
Key::Yen => Yen,
2018-01-04 06:58:19 +00:00
Key::Caret => Caret,
Key::Copy => Copy,
Key::Paste => Paste,
Key::Cut => Cut,
}
}
}