aboutsummaryrefslogtreecommitdiff
path: root/src/config/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/config/mod.rs')
-rw-r--r--src/config/mod.rs1872
1 files changed, 1872 insertions, 0 deletions
diff --git a/src/config/mod.rs b/src/config/mod.rs
new file mode 100644
index 00000000..b3400b7d
--- /dev/null
+++ b/src/config/mod.rs
@@ -0,0 +1,1872 @@
+//! 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.
+pub mod font;
+
+use std::borrow::Cow;
+use std::{env, fmt};
+use std::fs::{self, File};
+use std::io::{self, Read, Write};
+use std::path::{Path, PathBuf};
+use std::str::FromStr;
+use std::sync::mpsc;
+use std::time::Duration;
+use std::collections::HashMap;
+
+use ::Rgb;
+use serde_yaml;
+use serde::{de, Deserialize};
+use serde::de::Error as SerdeError;
+use serde::de::{Visitor, MapAccess, Unexpected};
+use notify::{Watcher, watcher, DebouncedEvent, RecursiveMode};
+
+use glutin::ModifiersState;
+
+use self::font::FontConfiguration;
+use input::{Action, Binding, MouseBinding, KeyBinding};
+use index::{Line, Column};
+use ansi::CursorStyle;
+
+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)
+}
+
+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,
+
+ /// up/down arrows sent when scrolling in alt screen buffer
+ #[serde(deserialize_with = "deserialize_faux_scrollback_lines")]
+ #[serde(default="default_faux_scrollback_lines")]
+ pub faux_scrollback_lines: usize,
+}
+
+fn default_faux_scrollback_lines() -> usize {
+ 1
+}
+
+fn deserialize_faux_scrollback_lines<'a, D>(deserializer: D) -> ::std::result::Result<usize, D::Error>
+ where D: de::Deserializer<'a>
+{
+ match usize::deserialize(deserializer) {
+ Ok(lines) => Ok(lines),
+ Err(err) => {
+ eprintln!("problem with config: {}; Using default value", err);
+ Ok(default_faux_scrollback_lines())
+ },
+ }
+}
+
+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: 1,
+ }
+ }
+}
+
+/// `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> {
+ pub fn new<S>(program: S) -> Shell<'a>
+ where S: Into<Cow<'a, str>>
+ {
+ Shell {
+ program: program.into(),
+ args: Vec::new(),
+ }
+ }
+
+ pub fn new_with_args<S>(program: S, args: Vec<String>) -> Shell<'a>
+ where S: Into<Cow<'a, str>>
+ {
+ Shell {
+ program: program.into(),
+ args: args
+ }
+ }
+
+ 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,
+
+ /// Draw the window with title bar / borders
+ #[serde(default, deserialize_with = "failure_default")]
+ decorations: bool,
+}
+
+fn default_padding() -> Delta {
+ Delta { x: 2., y: 2. }
+}
+
+fn deserialize_padding<'a, D>(deserializer: D) -> ::std::result::Result<Delta, 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>,
+
+ /// TERM env variable
+ #[serde(default, deserialize_with = "failure_default")]
+ env: HashMap<String, String>,
+
+ /// Font configuration
+ #[serde(default, deserialize_with = "failure_default")]
+ font: FontConfiguration,
+
+ /// 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,
+
+ /// 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,
+
+ /// 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,
+
+ /// Use dynamic title
+ #[serde(default="true_bool", deserialize_with = "default_true_bool")]
+ dynamic_title: bool,
+
+ /// Hide cursor when typing
+ #[serde(default, deserialize_with = "failure_default")]
+ hide_cursor_when_typing: bool,
+
+ /// Style of the cursor
+ #[serde(default, deserialize_with = "failure_default")]
+ cursor_style: CursorStyle,
+
+ /// 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,
+}
+
+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)
+ },
+ }
+}
+
+pub 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())
+ },
+ }
+}
+
+static DEFAULT_ALACRITTY_CONFIG: &'static str =
+ include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty.yml"));
+
+impl Default for Config {
+ fn default() -> Self {
+ serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG)
+ .expect("default config is invalid")
+ }
+}
+
+/// 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
+ }
+}
+
+impl<'a> de::Deserialize<'a> for ModsWrapper {
+ fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
+ where D: de::Deserializer<'a>
+ {
+ struct ModsVisitor;
+
+ 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('|') {
+ 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
+ }
+}
+
+impl<'a> de::Deserialize<'a> for ActionWrapper {
+ fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
+ where D: de::Deserializer<'a>
+ {
+ struct ActionVisitor;
+
+ 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, ResetFontSize, 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,
+ "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,
+}
+
+impl<'a> de::Deserialize<'a> for ModeWrapper {
+ fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
+ where D: de::Deserializer<'a>
+ {
+ struct ModeVisitor;
+
+ 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('|') {
+ match modifier.trim() {
+ "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,
+ _ => eprintln!("unknown mode {:?}", modifier),
+ }
+ }
+
+ Ok(res)
+ }
+ }
+ deserializer.deserialize_str(ModeVisitor)
+ }
+}
+
+struct MouseButton(::glutin::MouseButton);
+
+impl MouseButton {
+ fn into_inner(self) -> ::glutin::MouseButton {
+ self.0
+ }
+}
+
+impl<'a> de::Deserialize<'a> for MouseButton {
+ fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
+ where D: de::Deserializer<'a>
+ {
+ struct MouseButtonVisitor;
+
+ 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 self.mouse.is_some() {
+ Ok(Binding {
+ trigger: self.mouse.unwrap(),
+ 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 self.key.is_some() {
+ Ok(KeyBinding {
+ trigger: self.key.unwrap(),
+ mods: self.mods,
+ action: self.action,
+ mode: self.mode,
+ notmode: self.notmode,
+ })
+ } else {
+ Err(self)
+ }
+ }
+}
+
+impl<'a> de::Deserialize<'a> for RawBinding {
+ fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
+ where D: de::Deserializer<'a>
+ {
+ enum Field {
+ Key,
+ Mods,
+ Mode,
+ Action,
+ Chars,
+ Mouse,
+ Command,
+ }
+
+ impl<'a> de::Deserialize<'a> for Field {
+ fn deserialize<D>(deserializer: D) -> ::std::result::Result<Field, D::Error>
+ where D: de::Deserializer<'a>
+ {
+ struct FieldVisitor;
+
+ static FIELDS: &'static [&'static str] = &[
+ "key", "mods", "mode", "action", "chars", "mouse", "command",
+ ];
+
+ 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)),
+ }
+ }
+ }
+
+ deserializer.deserialize_struct("Field", FIELDS, FieldVisitor)
+ }
+ }
+
+ struct RawBindingVisitor;
+ impl<'a> Visitor<'a> for RawBindingVisitor {
+ type Value = RawBinding;
+
+ fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str("binding specification")
+ }
+
+ fn visit_map<V>(
+ self,
+ mut map: V
+ ) -> ::std::result::Result<RawBinding, V::Error>
+ 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;
+
+ 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"));
+ }
+
+ 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"));
+ }
+
+ mods = Some(map.next_value::<ModsWrapper>()?.into_inner());
+ },
+ Field::Mode => {
+ if mode.is_some() {
+ return Err(<V::Error as Error>::duplicate_field("mode"));
+ }
+
+ 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"));
+ }
+
+ action = Some(map.next_value::<ActionWrapper>()?.into_inner());
+ },
+ Field::Chars => {
+ if chars.is_some() {
+ return Err(<V::Error as Error>::duplicate_field("chars"));
+ }
+
+ chars = Some(map.next_value()?);
+ },
+ Field::Mouse => {
+ if chars.is_some() {
+ return Err(<V::Error as Error>::duplicate_field("mouse"));
+ }
+
+ mouse = Some(map.next_value::<MouseButton>()?.into_inner());
+ },
+ Field::Command => {
+ if command.is_some() {
+ return Err(<V::Error as Error>::duplicate_field("command"));
+ }
+
+ 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: mode,
+ notmode: not_mode,
+ action: action,
+ key: key,
+ mouse: mouse,
+ mods: mods,
+ })
+ }
+ }
+
+ const FIELDS: &[&str] = &[
+ "key", "mods", "mode", "action", "chars", "mouse", "command",
+ ];
+
+ deserializer.deserialize_struct("RawBinding", FIELDS, RawBindingVisitor)
+ }
+}
+
+
+impl<'a> de::Deserialize<'a> for Alpha {
+ fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
+ where D: de::Deserializer<'a>
+ {
+ let value = f32::deserialize(deserializer)?;
+ Ok(Alpha::new(value))
+ }
+}
+
+impl<'a> de::Deserialize<'a> for MouseBinding {
+ fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
+ where D: de::Deserializer<'a>
+ {
+ let raw = RawBinding::deserialize(deserializer)?;
+ raw.into_mouse_binding()
+ .map_err(|_| D::Error::custom("expected mouse binding"))
+ }
+}
+
+impl<'a> de::Deserialize<'a> for KeyBinding {
+ fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
+ 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>,
+}
+
+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: text,
+ cursor: 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,
+}
+
+impl Default for PrimaryColors {
+ fn default() -> Self {
+ PrimaryColors {
+ background: Rgb { r: 0, g: 0, b: 0 },
+ foreground: Rgb { r: 0xea, g: 0xea, b: 0xea },
+ }
+ }
+}
+
+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.
+fn rgb_from_hex<'a, D>(deserializer: D) -> ::std::result::Result<Rgb, D::Error>
+ where D: de::Deserializer<'a>
+{
+ struct RgbVisitor;
+
+ 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 })
+ },
+ }
+}
+
+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().unwrap().to_digit(16) {
+ Some(val) => rgb.$c = (val as u8) << 4,
+ None => return Err(())
+ }
+
+ match chars.next().unwrap().to_digit(16) {
+ Some(val) => rgb.$c |= val as u8,
+ None => return Err(())
+ }
+ )*
+ }
+ }
+
+ match chars.next().unwrap() {
+ '0' => if chars.next().unwrap() != 'x' { return Err(()); },
+ '#' => (),
+ _ => 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")
+ .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())
+ }
+
+ /// 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 {
+ 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 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
+ }
+
+ /// 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()
+ }
+
+ pub fn env(&self) -> &HashMap<String, String> {
+ &self.env
+ }
+
+ /// 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
+ }
+
+ /// 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
+ }
+
+ 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)
+ }
+
+ 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."));
+ }
+ }
+}
+
+/// 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: columns,
+ lines: 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, Deserialize)]
+pub struct Delta {
+ /// Horizontal change
+ #[serde(default, deserialize_with = "failure_default")]
+ pub x: f32,
+ /// Vertical change
+ #[serde(default, deserialize_with = "failure_default")]
+ pub y: f32,
+}
+
+impl Default for Delta {
+ fn default() -> Delta {
+ Delta { x: 0.0, y: 0.0 }
+ }
+}
+
+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)).unwrap();
+ 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") {
+ 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),
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ }),
+ rx: config_rx,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ 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
+ assert!(config.mouse_bindings.len() >= 1);
+
+ // Sanity check that key bindings are being parsed
+ assert!(config.key_bindings.len() >= 1);
+ }
+}
+
+#[cfg_attr(feature = "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,
+}
+
+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,
+ }
+ }
+}