Merge "[alacritty] Remove `alacritty/` since we're not using it." into main
diff --git a/alacritty/Cargo.toml b/alacritty/Cargo.toml
deleted file mode 100644
index 759ad8c..0000000
--- a/alacritty/Cargo.toml
+++ /dev/null
@@ -1,75 +0,0 @@
-[package]
-name = "alacritty"
-version = "0.4.2-dev"
-authors = ["Christian Duerr <contact@christianduerr.com", "Joe Wilm <joe@jwilm.com>"]
-license = "Apache-2.0"
-description = "GPU-accelerated terminal emulator"
-readme = "../README.md"
-homepage = "https://github.com/alacritty/alacritty"
-edition = "2018"
-
-[dependencies]
-alacritty_terminal = { path = "../alacritty_terminal" }
-clap = "2"
-log = "0.4"
-time = "0.1.40"
-env_logger = "0.7.1"
-fnv = "1"
-serde = { version = "1", features = ["derive"] }
-serde_yaml = "0.8"
-serde_json = "1"
-glutin = { version = "0.22.0", features = ["serde"] }
-notify = "4"
-libc = "0.2"
-unicode-width = "0.1"
-parking_lot = "0.9"
-font = { path = "../font" }
-urlocator = "0.1.0"
-
-[build-dependencies]
-gl_generator = "0.14.0"
-rustc_tools_util = "0.2.0"
-
-[target.'cfg(not(windows))'.dependencies]
-xdg = "2"
-
-[target.'cfg(not(target_os = "macos"))'.dependencies]
-image = { version = "0.22.3", default-features = false, features = ["ico"] }
-
-[target.'cfg(any(target_os = "macos", windows))'.dependencies]
-dirs = "2.0.2"
-
-[target.'cfg(not(any(target_os="windows", target_os="macos")))'.dependencies]
-x11-dl = "2"
-
-[target.'cfg(windows)'.dependencies]
-winapi = { version = "0.3.7", features = ["impl-default", "wincon"]}
-
-[target.'cfg(windows)'.build-dependencies]
-embed-resource = "1.3"
-
-[features]
-default = []
-# Enabling this feature makes shaders automatically reload when changed
-live-shader-reload = []
-nightly = []
-bench = []
-
-[package.metadata.deb]
-maintainer = "Christian Duerr <contact@christianduerr.com>"
-license-file = ["../LICENSE-APACHE", "3"]
-extended-description = """\
-Alacritty is the fastest terminal emulator in existence. Using the GPU for \
-rendering enables optimizations that simply aren't possible without it.  """
-depends = "$auto"
-section = "rust"
-priority = "optional"
-assets = [
-    ["../target/release/alacritty", "usr/bin/", "755"],
-    ["../extra/linux/alacritty.desktop", "usr/share/applications/", "644"],
-    ["../extra/logo/alacritty-term.svg", "usr/share/pixmaps/Alacritty.svg", "644"],
-    ["../extra/completions/alacritty.bash", "usr/share/bash-completion/completions/alacritty", "644"],
-    ["../extra/completions/alacritty.fish", "usr/share/fish/completions/alacritty.fish", "644"],
-    ["../extra/completions/_alacritty", "usr/share/zsh/vendor-completions/_alacritty", "644"],
-]
-maintainer-scripts = "../extra/linux/debian"
diff --git a/alacritty/build.rs b/alacritty/build.rs
deleted file mode 100644
index 16f3a2b..0000000
--- a/alacritty/build.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2019 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry};
-
-use std::env;
-use std::fs::File;
-use std::path::Path;
-
-#[cfg(windows)]
-use embed_resource;
-
-fn main() {
-    let hash = rustc_tools_util::get_commit_hash().unwrap_or_default();
-    println!("cargo:rustc-env=GIT_HASH={}", hash);
-
-    let dest = env::var("OUT_DIR").unwrap();
-    let mut file = File::create(&Path::new(&dest).join("gl_bindings.rs")).unwrap();
-
-    Registry::new(Api::Gl, (4, 5), Profile::Core, Fallbacks::All, ["GL_ARB_blend_func_extended"])
-        .write_bindings(GlobalGenerator, &mut file)
-        .unwrap();
-
-    #[cfg(windows)]
-    embed_resource::compile("../extra/windows/windows.rc");
-}
diff --git a/alacritty/src/cli.rs b/alacritty/src/cli.rs
deleted file mode 100644
index 7e45e24..0000000
--- a/alacritty/src/cli.rs
+++ /dev/null
@@ -1,339 +0,0 @@
-// Copyright 2019 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::cmp::max;
-use std::path::PathBuf;
-
-use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
-use log::{self, error, LevelFilter};
-
-use alacritty_terminal::config::{Delta, Dimensions, Shell, DEFAULT_NAME};
-use alacritty_terminal::index::{Column, Line};
-
-use crate::config::Config;
-
-#[cfg(not(any(target_os = "macos", windows)))]
-const CONFIG_PATH: &str = "$XDG_CONFIG_HOME/alacritty/alacritty.yml";
-#[cfg(windows)]
-const CONFIG_PATH: &str = "%APPDATA%\\alacritty\\alacritty.yml";
-#[cfg(target_os = "macos")]
-const CONFIG_PATH: &str = "$HOME/.config/alacritty/alacritty.yml";
-
-/// Options specified on the command line
-pub struct Options {
-    pub live_config_reload: Option<bool>,
-    pub print_events: bool,
-    pub ref_test: bool,
-    pub dimensions: Option<Dimensions>,
-    pub position: Option<Delta<i32>>,
-    pub title: Option<String>,
-    pub class: Option<String>,
-    pub embed: Option<String>,
-    pub log_level: LevelFilter,
-    pub command: Option<Shell<'static>>,
-    pub hold: bool,
-    pub working_dir: Option<PathBuf>,
-    pub config: Option<PathBuf>,
-    pub persistent_logging: bool,
-}
-
-impl Default for Options {
-    fn default() -> Options {
-        Options {
-            live_config_reload: None,
-            print_events: false,
-            ref_test: false,
-            dimensions: None,
-            position: None,
-            title: None,
-            class: None,
-            embed: None,
-            log_level: LevelFilter::Warn,
-            command: None,
-            hold: false,
-            working_dir: None,
-            config: None,
-            persistent_logging: false,
-        }
-    }
-}
-
-impl Options {
-    /// Build `Options` from command line arguments.
-    pub fn new() -> Self {
-        let mut version = crate_version!().to_owned();
-        let commit_hash = env!("GIT_HASH");
-        if !commit_hash.is_empty() {
-            version = format!("{} ({})", version, commit_hash);
-        }
-
-        let mut options = Options::default();
-
-        let matches = App::new(crate_name!())
-            .version(version.as_str())
-            .author(crate_authors!("\n"))
-            .about(crate_description!())
-            .arg(Arg::with_name("ref-test").long("ref-test").help("Generates ref test"))
-            .arg(
-                Arg::with_name("live-config-reload")
-                    .long("live-config-reload")
-                    .help("Enable automatic config reloading"),
-            )
-            .arg(
-                Arg::with_name("no-live-config-reload")
-                    .long("no-live-config-reload")
-                    .help("Disable automatic config reloading")
-                    .conflicts_with("live-config-reload"),
-            )
-            .arg(
-                Arg::with_name("print-events")
-                    .long("print-events")
-                    .help("Print all events to stdout"),
-            )
-            .arg(
-                Arg::with_name("persistent-logging")
-                    .long("persistent-logging")
-                    .help("Keep the log file after quitting Alacritty"),
-            )
-            .arg(
-                Arg::with_name("dimensions")
-                    .long("dimensions")
-                    .short("d")
-                    .value_names(&["columns", "lines"])
-                    .help(
-                        "Defines the window dimensions. Falls back to size specified by window \
-                         manager if set to 0x0 [default: 0x0]",
-                    ),
-            )
-            .arg(
-                Arg::with_name("position")
-                    .long("position")
-                    .allow_hyphen_values(true)
-                    .value_names(&["x-pos", "y-pos"])
-                    .help(
-                        "Defines the window position. Falls back to position specified by window \
-                         manager if unset [default: unset]",
-                    ),
-            )
-            .arg(
-                Arg::with_name("title")
-                    .long("title")
-                    .short("t")
-                    .takes_value(true)
-                    .help(&format!("Defines the window title [default: {}]", DEFAULT_NAME)),
-            )
-            .arg(
-                Arg::with_name("class")
-                    .long("class")
-                    .takes_value(true)
-                    .help(&format!("Defines window class on Linux [default: {}]", DEFAULT_NAME)),
-            )
-            .arg(
-                Arg::with_name("embed").long("embed").takes_value(true).help(
-                    "Defines the X11 window ID (as a decimal integer) to embed Alacritty within",
-                ),
-            )
-            .arg(
-                Arg::with_name("q")
-                    .short("q")
-                    .multiple(true)
-                    .conflicts_with("v")
-                    .help("Reduces the level of verbosity (the min level is -qq)"),
-            )
-            .arg(
-                Arg::with_name("v")
-                    .short("v")
-                    .multiple(true)
-                    .conflicts_with("q")
-                    .help("Increases the level of verbosity (the max level is -vvv)"),
-            )
-            .arg(
-                Arg::with_name("working-directory")
-                    .long("working-directory")
-                    .takes_value(true)
-                    .help("Start the shell in the specified working directory"),
-            )
-            .arg(Arg::with_name("config-file").long("config-file").takes_value(true).help(
-                &format!("Specify alternative configuration file [default: {}]", CONFIG_PATH),
-            ))
-            .arg(
-                Arg::with_name("command")
-                    .long("command")
-                    .short("e")
-                    .multiple(true)
-                    .takes_value(true)
-                    .min_values(1)
-                    .allow_hyphen_values(true)
-                    .help("Command and args to execute (must be last argument)"),
-            )
-            .arg(Arg::with_name("hold").long("hold").help("Remain open after child process exits"))
-            .get_matches();
-
-        if matches.is_present("ref-test") {
-            options.ref_test = true;
-        }
-
-        if matches.is_present("print-events") {
-            options.print_events = true;
-        }
-
-        if matches.is_present("live-config-reload") {
-            options.live_config_reload = Some(true);
-        } else if matches.is_present("no-live-config-reload") {
-            options.live_config_reload = Some(false);
-        }
-
-        if matches.is_present("persistent-logging") {
-            options.persistent_logging = true;
-        }
-
-        if let Some(mut dimensions) = matches.values_of("dimensions") {
-            let width = dimensions.next().map(|w| w.parse().map(Column));
-            let height = dimensions.next().map(|h| h.parse().map(Line));
-            if let (Some(Ok(width)), Some(Ok(height))) = (width, height) {
-                options.dimensions = Some(Dimensions::new(width, height));
-            }
-        }
-
-        if let Some(mut position) = matches.values_of("position") {
-            let x = position.next().map(str::parse);
-            let y = position.next().map(str::parse);
-            if let (Some(Ok(x)), Some(Ok(y))) = (x, y) {
-                options.position = Some(Delta { x, y });
-            }
-        }
-
-        options.class = matches.value_of("class").map(ToOwned::to_owned);
-        options.title = matches.value_of("title").map(ToOwned::to_owned);
-        options.embed = matches.value_of("embed").map(ToOwned::to_owned);
-
-        match matches.occurrences_of("q") {
-            0 => {},
-            1 => options.log_level = LevelFilter::Error,
-            2 | _ => options.log_level = LevelFilter::Off,
-        }
-
-        match matches.occurrences_of("v") {
-            0 if !options.print_events => options.log_level = LevelFilter::Warn,
-            0 | 1 => options.log_level = LevelFilter::Info,
-            2 => options.log_level = LevelFilter::Debug,
-            3 | _ => options.log_level = LevelFilter::Trace,
-        }
-
-        if let Some(dir) = matches.value_of("working-directory") {
-            options.working_dir = Some(PathBuf::from(dir.to_string()));
-        }
-
-        if let Some(path) = matches.value_of("config-file") {
-            options.config = Some(PathBuf::from(path.to_string()));
-        }
-
-        if let Some(mut args) = matches.values_of("command") {
-            // The following unwrap is guaranteed to succeed.
-            // If 'command' exists it must also have a first item since
-            // Arg::min_values(1) is set.
-            let command = String::from(args.next().unwrap());
-            let args = args.map(String::from).collect();
-            options.command = Some(Shell::new_with_args(command, args));
-        }
-
-        if matches.is_present("hold") {
-            options.hold = true;
-        }
-
-        options
-    }
-
-    pub fn config_path(&self) -> Option<PathBuf> {
-        self.config.clone()
-    }
-
-    pub fn into_config(self, mut config: Config) -> Config {
-        match self.working_dir.or_else(|| config.working_directory.take()) {
-            Some(ref wd) if !wd.is_dir() => error!("Unable to set working directory to {:?}", wd),
-            wd => config.working_directory = wd,
-        }
-
-        if let Some(lcr) = self.live_config_reload {
-            config.set_live_config_reload(lcr);
-        }
-        config.shell = self.command.or(config.shell);
-
-        config.hold = self.hold;
-
-        config.window.dimensions = self.dimensions.unwrap_or(config.window.dimensions);
-        config.window.title = self.title.unwrap_or(config.window.title);
-        config.window.position = self.position.or(config.window.position);
-        config.window.embed = self.embed.and_then(|embed| embed.parse().ok());
-
-        if let Some(class) = self.class {
-            let parts: Vec<_> = class.split(',').collect();
-            config.window.class.instance = parts[0].into();
-            if let Some(&general) = parts.get(1) {
-                config.window.class.general = general.into();
-            }
-        }
-
-        config.set_dynamic_title(config.dynamic_title() && config.window.title == DEFAULT_NAME);
-
-        config.debug.print_events = self.print_events || config.debug.print_events;
-        config.debug.log_level = max(config.debug.log_level, self.log_level);
-        config.debug.ref_test = self.ref_test || config.debug.ref_test;
-        config.debug.persistent_logging =
-            self.persistent_logging || config.debug.persistent_logging;
-
-        if config.debug.print_events {
-            config.debug.log_level = max(config.debug.log_level, LevelFilter::Info);
-        }
-
-        config
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use crate::cli::Options;
-    use crate::config::Config;
-
-    #[test]
-    fn dynamic_title_ignoring_options_by_default() {
-        let config = Config::default();
-        let old_dynamic_title = config.dynamic_title();
-
-        let config = Options::default().into_config(config);
-
-        assert_eq!(old_dynamic_title, config.dynamic_title());
-    }
-
-    #[test]
-    fn dynamic_title_overridden_by_options() {
-        let config = Config::default();
-
-        let mut options = Options::default();
-        options.title = Some("foo".to_owned());
-        let config = options.into_config(config);
-
-        assert!(!config.dynamic_title());
-    }
-
-    #[test]
-    fn dynamic_title_overridden_by_config() {
-        let mut config = Config::default();
-
-        config.window.title = "foo".to_owned();
-        let config = Options::default().into_config(config);
-
-        assert!(!config.dynamic_title());
-    }
-}
diff --git a/alacritty/src/config/bindings.rs b/alacritty/src/config/bindings.rs
deleted file mode 100644
index 1a9f95a..0000000
--- a/alacritty/src/config/bindings.rs
+++ /dev/null
@@ -1,1072 +0,0 @@
-// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-#![allow(clippy::enum_glob_use)]
-
-use std::fmt;
-use std::str::FromStr;
-
-use glutin::event::VirtualKeyCode::*;
-use glutin::event::{ModifiersState, MouseButton, VirtualKeyCode};
-use log::error;
-use serde::de::Error as SerdeError;
-use serde::de::{self, MapAccess, Unexpected, Visitor};
-use serde::{Deserialize, Deserializer};
-
-use alacritty_terminal::config::LOG_TARGET_CONFIG;
-use alacritty_terminal::term::TermMode;
-
-/// Describes a state and action to take in that state
-///
-/// This is the shared component of `MouseBinding` and `KeyBinding`
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Binding<T> {
-    /// Modifier keys required to activate binding
-    pub mods: ModifiersState,
-
-    /// String to send to pty if mods and mode match
-    pub action: Action,
-
-    /// Terminal mode required to activate binding
-    pub mode: TermMode,
-
-    /// excluded terminal modes where the binding won't be activated
-    pub notmode: TermMode,
-
-    /// This property is used as part of the trigger detection code.
-    ///
-    /// For example, this might be a key like "G", or a mouse button.
-    pub trigger: T,
-}
-
-/// Bindings that are triggered by a keyboard key
-pub type KeyBinding = Binding<Key>;
-
-/// Bindings that are triggered by a mouse button
-pub type MouseBinding = Binding<MouseButton>;
-
-impl Default for KeyBinding {
-    fn default() -> KeyBinding {
-        KeyBinding {
-            mods: Default::default(),
-            action: Action::Esc(String::new()),
-            mode: TermMode::NONE,
-            notmode: TermMode::NONE,
-            trigger: Key::Keycode(A),
-        }
-    }
-}
-
-impl Default for MouseBinding {
-    fn default() -> MouseBinding {
-        MouseBinding {
-            mods: Default::default(),
-            action: Action::Esc(String::new()),
-            mode: TermMode::NONE,
-            notmode: TermMode::NONE,
-            trigger: MouseButton::Left,
-        }
-    }
-}
-
-impl<T: Eq> Binding<T> {
-    #[inline]
-    pub fn is_triggered_by(&self, mode: TermMode, mods: ModifiersState, input: &T) -> bool {
-        // Check input first since bindings are stored in one big list. This is
-        // the most likely item to fail so prioritizing it here allows more
-        // checks to be short circuited.
-        self.trigger == *input
-            && mode.contains(self.mode)
-            && !mode.intersects(self.notmode)
-            && (self.mods == mods)
-    }
-
-    #[inline]
-    pub fn triggers_match(&self, binding: &Binding<T>) -> bool {
-        // Check the binding's key and modifiers
-        if self.trigger != binding.trigger || self.mods != binding.mods {
-            return false;
-        }
-
-        // Completely empty modes match all modes
-        if (self.mode.is_empty() && self.notmode.is_empty())
-            || (binding.mode.is_empty() && binding.notmode.is_empty())
-        {
-            return true;
-        }
-
-        // Check for intersection (equality is required since empty does not intersect itself)
-        (self.mode == binding.mode || self.mode.intersects(binding.mode))
-            && (self.notmode == binding.notmode || self.notmode.intersects(binding.notmode))
-    }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
-pub enum Action {
-    /// Write an escape sequence.
-    #[serde(skip)]
-    Esc(String),
-
-    /// Paste contents of system clipboard.
-    Paste,
-
-    /// Store current selection into clipboard.
-    Copy,
-
-    /// Paste contents of selection buffer.
-    PasteSelection,
-
-    /// Increase font size.
-    IncreaseFontSize,
-
-    /// Decrease font size.
-    DecreaseFontSize,
-
-    /// Reset font size to the config value.
-    ResetFontSize,
-
-    /// Scroll exactly one page up.
-    ScrollPageUp,
-
-    /// Scroll exactly one page down.
-    ScrollPageDown,
-
-    /// Scroll one line up.
-    ScrollLineUp,
-
-    /// Scroll one line down.
-    ScrollLineDown,
-
-    /// Scroll all the way to the top.
-    ScrollToTop,
-
-    /// Scroll all the way to the bottom.
-    ScrollToBottom,
-
-    /// Clear the display buffer(s) to remove history.
-    ClearHistory,
-
-    /// Run given command.
-    #[serde(skip)]
-    Command(String, Vec<String>),
-
-    /// Hide the Alacritty window.
-    Hide,
-
-    /// Minimize the Alacritty window.
-    Minimize,
-
-    /// Quit Alacritty.
-    Quit,
-
-    /// Clear warning and error notices.
-    ClearLogNotice,
-
-    /// Spawn a new instance of Alacritty.
-    SpawnNewInstance,
-
-    /// Toggle fullscreen.
-    ToggleFullscreen,
-
-    /// Toggle simple fullscreen on macos.
-    #[cfg(target_os = "macos")]
-    ToggleSimpleFullscreen,
-
-    /// Allow receiving char input.
-    ReceiveChar,
-
-    /// No action.
-    None,
-}
-
-impl Default for Action {
-    fn default() -> Action {
-        Action::None
-    }
-}
-
-impl From<&'static str> for Action {
-    fn from(s: &'static str) -> Action {
-        Action::Esc(s.into())
-    }
-}
-
-macro_rules! bindings {
-    (
-        KeyBinding;
-        $(
-            $key:ident
-            $(,$mods:expr)*
-            $(,+$mode:expr)*
-            $(,~$notmode:expr)*
-            ;$action:expr
-        );*
-        $(;)*
-    ) => {{
-        bindings!(
-            KeyBinding;
-            $(
-                Key::Keycode($key)
-                $(,$mods)*
-                $(,+$mode)*
-                $(,~$notmode)*
-                ;$action
-            );*
-        )
-    }};
-    (
-        $ty:ident;
-        $(
-            $key:expr
-            $(,$mods:expr)*
-            $(,+$mode:expr)*
-            $(,~$notmode:expr)*
-            ;$action:expr
-        );*
-        $(;)*
-    ) => {{
-        let mut v = Vec::new();
-
-        $(
-            let mut _mods = ModifiersState::empty();
-            $(_mods = $mods;)*
-            let mut _mode = TermMode::empty();
-            $(_mode = $mode;)*
-            let mut _notmode = TermMode::empty();
-            $(_notmode = $notmode;)*
-
-            v.push($ty {
-                trigger: $key,
-                mods: _mods,
-                mode: _mode,
-                notmode: _notmode,
-                action: $action,
-            });
-        )*
-
-        v
-    }};
-}
-
-pub fn default_mouse_bindings() -> Vec<MouseBinding> {
-    bindings!(
-        MouseBinding;
-        MouseButton::Middle; Action::PasteSelection;
-    )
-}
-
-pub fn default_key_bindings() -> Vec<KeyBinding> {
-    let mut bindings = bindings!(
-        KeyBinding;
-        Paste; Action::Paste;
-        Copy;  Action::Copy;
-        L, ModifiersState::CTRL; Action::ClearLogNotice;
-        L, ModifiersState::CTRL; Action::Esc("\x0c".into());
-        PageUp,   ModifiersState::SHIFT, ~TermMode::ALT_SCREEN; Action::ScrollPageUp;
-        PageDown, ModifiersState::SHIFT, ~TermMode::ALT_SCREEN; Action::ScrollPageDown;
-        Home,     ModifiersState::SHIFT, ~TermMode::ALT_SCREEN; Action::ScrollToTop;
-        End,      ModifiersState::SHIFT, ~TermMode::ALT_SCREEN; Action::ScrollToBottom;
-        Home, +TermMode::APP_CURSOR; Action::Esc("\x1bOH".into());
-        Home, ~TermMode::APP_CURSOR; Action::Esc("\x1b[H".into());
-        Home, ModifiersState::SHIFT, +TermMode::ALT_SCREEN; Action::Esc("\x1b[1;2H".into());
-        End,  +TermMode::APP_CURSOR; Action::Esc("\x1bOF".into());
-        End,  ~TermMode::APP_CURSOR; Action::Esc("\x1b[F".into());
-        End,  ModifiersState::SHIFT, +TermMode::ALT_SCREEN; Action::Esc("\x1b[1;2F".into());
-        PageUp;   Action::Esc("\x1b[5~".into());
-        PageUp,   ModifiersState::SHIFT, +TermMode::ALT_SCREEN; Action::Esc("\x1b[5;2~".into());
-        PageDown; Action::Esc("\x1b[6~".into());
-        PageDown, ModifiersState::SHIFT, +TermMode::ALT_SCREEN; Action::Esc("\x1b[6;2~".into());
-        Tab,  ModifiersState::SHIFT; Action::Esc("\x1b[Z".into());
-        Back; Action::Esc("\x7f".into());
-        Back, ModifiersState::ALT; Action::Esc("\x1b\x7f".into());
-        Insert; Action::Esc("\x1b[2~".into());
-        Delete; Action::Esc("\x1b[3~".into());
-        Up,    +TermMode::APP_CURSOR; Action::Esc("\x1bOA".into());
-        Up,    ~TermMode::APP_CURSOR; Action::Esc("\x1b[A".into());
-        Down,  +TermMode::APP_CURSOR; Action::Esc("\x1bOB".into());
-        Down,  ~TermMode::APP_CURSOR; Action::Esc("\x1b[B".into());
-        Right, +TermMode::APP_CURSOR; Action::Esc("\x1bOC".into());
-        Right, ~TermMode::APP_CURSOR; Action::Esc("\x1b[C".into());
-        Left,  +TermMode::APP_CURSOR; Action::Esc("\x1bOD".into());
-        Left,  ~TermMode::APP_CURSOR; Action::Esc("\x1b[D".into());
-        F1;  Action::Esc("\x1bOP".into());
-        F2;  Action::Esc("\x1bOQ".into());
-        F3;  Action::Esc("\x1bOR".into());
-        F4;  Action::Esc("\x1bOS".into());
-        F5;  Action::Esc("\x1b[15~".into());
-        F6;  Action::Esc("\x1b[17~".into());
-        F7;  Action::Esc("\x1b[18~".into());
-        F8;  Action::Esc("\x1b[19~".into());
-        F9;  Action::Esc("\x1b[20~".into());
-        F10; Action::Esc("\x1b[21~".into());
-        F11; Action::Esc("\x1b[23~".into());
-        F12; Action::Esc("\x1b[24~".into());
-        F13; Action::Esc("\x1b[25~".into());
-        F14; Action::Esc("\x1b[26~".into());
-        F15; Action::Esc("\x1b[28~".into());
-        F16; Action::Esc("\x1b[29~".into());
-        F17; Action::Esc("\x1b[31~".into());
-        F18; Action::Esc("\x1b[32~".into());
-        F19; Action::Esc("\x1b[33~".into());
-        F20; Action::Esc("\x1b[34~".into());
-        NumpadEnter; Action::Esc("\n".into());
-    );
-
-    //   Code     Modifiers
-    // ---------+---------------------------
-    //    2     | Shift
-    //    3     | Alt
-    //    4     | Shift + Alt
-    //    5     | Control
-    //    6     | Shift + Control
-    //    7     | Alt + Control
-    //    8     | Shift + Alt + Control
-    // ---------+---------------------------
-    //
-    // from: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-PC-Style-Function-Keys
-    let mut modifiers = vec![
-        ModifiersState::SHIFT,
-        ModifiersState::ALT,
-        ModifiersState::SHIFT | ModifiersState::ALT,
-        ModifiersState::CTRL,
-        ModifiersState::SHIFT | ModifiersState::CTRL,
-        ModifiersState::ALT | ModifiersState::CTRL,
-        ModifiersState::SHIFT | ModifiersState::ALT | ModifiersState::CTRL,
-    ];
-
-    for (index, mods) in modifiers.drain(..).enumerate() {
-        let modifiers_code = index + 2;
-        bindings.extend(bindings!(
-            KeyBinding;
-            Delete, mods; Action::Esc(format!("\x1b[3;{}~", modifiers_code));
-            Up,     mods; Action::Esc(format!("\x1b[1;{}A", modifiers_code));
-            Down,   mods; Action::Esc(format!("\x1b[1;{}B", modifiers_code));
-            Right,  mods; Action::Esc(format!("\x1b[1;{}C", modifiers_code));
-            Left,   mods; Action::Esc(format!("\x1b[1;{}D", modifiers_code));
-            F1,     mods; Action::Esc(format!("\x1b[1;{}P", modifiers_code));
-            F2,     mods; Action::Esc(format!("\x1b[1;{}Q", modifiers_code));
-            F3,     mods; Action::Esc(format!("\x1b[1;{}R", modifiers_code));
-            F4,     mods; Action::Esc(format!("\x1b[1;{}S", modifiers_code));
-            F5,     mods; Action::Esc(format!("\x1b[15;{}~", modifiers_code));
-            F6,     mods; Action::Esc(format!("\x1b[17;{}~", modifiers_code));
-            F7,     mods; Action::Esc(format!("\x1b[18;{}~", modifiers_code));
-            F8,     mods; Action::Esc(format!("\x1b[19;{}~", modifiers_code));
-            F9,     mods; Action::Esc(format!("\x1b[20;{}~", modifiers_code));
-            F10,    mods; Action::Esc(format!("\x1b[21;{}~", modifiers_code));
-            F11,    mods; Action::Esc(format!("\x1b[23;{}~", modifiers_code));
-            F12,    mods; Action::Esc(format!("\x1b[24;{}~", modifiers_code));
-            F13,    mods; Action::Esc(format!("\x1b[25;{}~", modifiers_code));
-            F14,    mods; Action::Esc(format!("\x1b[26;{}~", modifiers_code));
-            F15,    mods; Action::Esc(format!("\x1b[28;{}~", modifiers_code));
-            F16,    mods; Action::Esc(format!("\x1b[29;{}~", modifiers_code));
-            F17,    mods; Action::Esc(format!("\x1b[31;{}~", modifiers_code));
-            F18,    mods; Action::Esc(format!("\x1b[32;{}~", modifiers_code));
-            F19,    mods; Action::Esc(format!("\x1b[33;{}~", modifiers_code));
-            F20,    mods; Action::Esc(format!("\x1b[34;{}~", modifiers_code));
-        ));
-
-        // We're adding the following bindings with `Shift` manually above, so skipping them here
-        // modifiers_code != Shift
-        if modifiers_code != 2 {
-            bindings.extend(bindings!(
-                KeyBinding;
-                Insert,   mods; Action::Esc(format!("\x1b[2;{}~", modifiers_code));
-                PageUp,   mods; Action::Esc(format!("\x1b[5;{}~", modifiers_code));
-                PageDown, mods; Action::Esc(format!("\x1b[6;{}~", modifiers_code));
-                End,      mods; Action::Esc(format!("\x1b[1;{}F", modifiers_code));
-                Home,     mods; Action::Esc(format!("\x1b[1;{}H", modifiers_code));
-            ));
-        }
-    }
-
-    bindings.extend(platform_key_bindings());
-
-    bindings
-}
-
-#[cfg(not(any(target_os = "macos", test)))]
-fn common_keybindings() -> Vec<KeyBinding> {
-    bindings!(
-        KeyBinding;
-        V,        ModifiersState::CTRL | ModifiersState::SHIFT; Action::Paste;
-        C,        ModifiersState::CTRL | ModifiersState::SHIFT; Action::Copy;
-        Insert,   ModifiersState::SHIFT; Action::PasteSelection;
-        Key0,     ModifiersState::CTRL;  Action::ResetFontSize;
-        Equals,   ModifiersState::CTRL;  Action::IncreaseFontSize;
-        Add,      ModifiersState::CTRL;  Action::IncreaseFontSize;
-        Subtract, ModifiersState::CTRL;  Action::DecreaseFontSize;
-        Minus,    ModifiersState::CTRL;  Action::DecreaseFontSize;
-    )
-}
-
-#[cfg(not(any(target_os = "macos", target_os = "windows", test)))]
-pub fn platform_key_bindings() -> Vec<KeyBinding> {
-    common_keybindings()
-}
-
-#[cfg(all(target_os = "windows", not(test)))]
-pub fn platform_key_bindings() -> Vec<KeyBinding> {
-    let mut bindings = bindings!(
-        KeyBinding;
-        Return, ModifiersState::ALT; Action::ToggleFullscreen;
-    );
-    bindings.extend(common_keybindings());
-    bindings
-}
-
-#[cfg(all(target_os = "macos", not(test)))]
-pub fn platform_key_bindings() -> Vec<KeyBinding> {
-    bindings!(
-        KeyBinding;
-        Key0,   ModifiersState::LOGO;  Action::ResetFontSize;
-        Equals, ModifiersState::LOGO;  Action::IncreaseFontSize;
-        Add,    ModifiersState::LOGO;  Action::IncreaseFontSize;
-        Minus,  ModifiersState::LOGO;  Action::DecreaseFontSize;
-        Insert, ModifiersState::SHIFT; Action::Esc("\x1b[2;2~".into());
-        F, ModifiersState::CTRL | ModifiersState::LOGO; Action::ToggleFullscreen;
-        K, ModifiersState::LOGO; Action::ClearHistory;
-        K, ModifiersState::LOGO; Action::Esc("\x0c".into());
-        V, ModifiersState::LOGO; Action::Paste;
-        C, ModifiersState::LOGO; Action::Copy;
-        H, ModifiersState::LOGO; Action::Hide;
-        M, ModifiersState::LOGO; Action::Minimize;
-        Q, ModifiersState::LOGO; Action::Quit;
-        W, ModifiersState::LOGO; Action::Quit;
-    )
-}
-
-// Don't return any bindings for tests since they are commented-out by default
-#[cfg(test)]
-pub fn platform_key_bindings() -> Vec<KeyBinding> {
-    vec![]
-}
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
-pub enum Key {
-    Scancode(u32),
-    Keycode(VirtualKeyCode),
-}
-
-impl<'a> Deserialize<'a> for Key {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'a>,
-    {
-        let value = serde_yaml::Value::deserialize(deserializer)?;
-        match u32::deserialize(value.clone()) {
-            Ok(scancode) => Ok(Key::Scancode(scancode)),
-            Err(_) => {
-                let keycode = VirtualKeyCode::deserialize(value).map_err(D::Error::custom)?;
-                Ok(Key::Keycode(keycode))
-            },
-        }
-    }
-}
-
-struct ModeWrapper {
-    pub mode: TermMode,
-    pub not_mode: TermMode,
-}
-
-impl<'a> Deserialize<'a> for ModeWrapper {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: 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 | Alt, possibly with negation (~)",
-                )
-            }
-
-            fn visit_str<E>(self, value: &str) -> 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().to_lowercase().as_str() {
-                        "appcursor" => res.mode |= TermMode::APP_CURSOR,
-                        "~appcursor" => res.not_mode |= TermMode::APP_CURSOR,
-                        "appkeypad" => res.mode |= TermMode::APP_KEYPAD,
-                        "~appkeypad" => res.not_mode |= TermMode::APP_KEYPAD,
-                        "alt" => res.mode |= TermMode::ALT_SCREEN,
-                        "~alt" => res.not_mode |= TermMode::ALT_SCREEN,
-                        _ => error!(target: LOG_TARGET_CONFIG, "Unknown mode {:?}", modifier),
-                    }
-                }
-
-                Ok(res)
-            }
-        }
-        deserializer.deserialize_str(ModeVisitor)
-    }
-}
-
-struct MouseButtonWrapper(MouseButton);
-
-impl MouseButtonWrapper {
-    fn into_inner(self) -> MouseButton {
-        self.0
-    }
-}
-
-impl<'a> Deserialize<'a> for MouseButtonWrapper {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'a>,
-    {
-        struct MouseButtonVisitor;
-
-        impl<'a> Visitor<'a> for MouseButtonVisitor {
-            type Value = MouseButtonWrapper;
-
-            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) -> Result<MouseButtonWrapper, E>
-            where
-                E: de::Error,
-            {
-                match value {
-                    "Left" => Ok(MouseButtonWrapper(MouseButton::Left)),
-                    "Right" => Ok(MouseButtonWrapper(MouseButton::Right)),
-                    "Middle" => Ok(MouseButtonWrapper(MouseButton::Middle)),
-                    _ => {
-                        if let Ok(index) = u8::from_str(value) {
-                            Ok(MouseButtonWrapper(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`.
-#[derive(PartialEq, Eq)]
-struct RawBinding {
-    key: Option<Key>,
-    mouse: Option<MouseButton>,
-    mods: ModifiersState,
-    mode: TermMode,
-    notmode: TermMode,
-    action: Action,
-}
-
-impl RawBinding {
-    fn into_mouse_binding(self) -> 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) -> 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)
-        }
-    }
-}
-
-impl<'a> Deserialize<'a> for RawBinding {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'a>,
-    {
-        enum Field {
-            Key,
-            Mods,
-            Mode,
-            Action,
-            Chars,
-            Mouse,
-            Command,
-        }
-
-        impl<'a> Deserialize<'a> for Field {
-            fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
-            where
-                D: Deserializer<'a>,
-            {
-                struct FieldVisitor;
-
-                static FIELDS: &[&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) -> 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_str(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) -> Result<RawBinding, V::Error>
-            where
-                V: MapAccess<'a>,
-            {
-                let mut mods: Option<ModifiersState> = None;
-                let mut key: Option<Key> = None;
-                let mut chars: Option<String> = None;
-                let mut action: Option<Action> = None;
-                let mut mode: Option<TermMode> = None;
-                let mut not_mode: Option<TermMode> = None;
-                let mut mouse: Option<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 val = map.next_value::<serde_yaml::Value>()?;
-                            if val.is_u64() {
-                                let scancode = val.as_u64().unwrap();
-                                if scancode > u64::from(::std::u32::MAX) {
-                                    return Err(<V::Error as Error>::custom(format!(
-                                        "Invalid key binding, scancode too big: {}",
-                                        scancode
-                                    )));
-                                }
-                                key = Some(Key::Scancode(scancode as u32));
-                            } else {
-                                let k = Key::deserialize(val).map_err(V::Error::custom)?;
-                                key = Some(k);
-                            }
-                        },
-                        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::<Action>()?);
-                        },
-                        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::<MouseButtonWrapper>()?.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, notmode: not_mode, action, key, mouse, mods })
-            }
-        }
-
-        const FIELDS: &[&str] = &["key", "mods", "mode", "action", "chars", "mouse", "command"];
-
-        deserializer.deserialize_struct("RawBinding", FIELDS, RawBindingVisitor)
-    }
-}
-
-impl<'a> Deserialize<'a> for MouseBinding {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'a>,
-    {
-        let raw = RawBinding::deserialize(deserializer)?;
-        raw.into_mouse_binding().map_err(|_| D::Error::custom("expected mouse binding"))
-    }
-}
-
-impl<'a> Deserialize<'a> for KeyBinding {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: Deserializer<'a>,
-    {
-        let raw = RawBinding::deserialize(deserializer)?;
-        raw.into_key_binding().map_err(|_| D::Error::custom("expected key binding"))
-    }
-}
-
-#[serde(untagged)]
-#[derive(Debug, Deserialize, Clone, PartialEq, Eq)]
-pub enum CommandWrapper {
-    Just(String),
-    WithArgs {
-        program: String,
-        #[serde(default)]
-        args: Vec<String>,
-    },
-}
-
-impl CommandWrapper {
-    pub fn program(&self) -> &str {
-        match self {
-            CommandWrapper::Just(program) => program,
-            CommandWrapper::WithArgs { program, .. } => program,
-        }
-    }
-
-    pub fn args(&self) -> &[String] {
-        match self {
-            CommandWrapper::Just(_) => &[],
-            CommandWrapper::WithArgs { args, .. } => args,
-        }
-    }
-}
-
-/// Newtype for implementing deserialize on glutin Mods
-///
-/// Our deserialize impl wouldn't be covered by a derive(Deserialize); see the
-/// impl below.
-#[derive(Debug, Copy, Clone, Hash, Default, Eq, PartialEq)]
-pub struct ModsWrapper(ModifiersState);
-
-impl ModsWrapper {
-    pub fn into_inner(self) -> ModifiersState {
-        self.0
-    }
-}
-
-impl<'a> de::Deserialize<'a> for ModsWrapper {
-    fn deserialize<D>(deserializer: D) -> 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) -> Result<ModsWrapper, E>
-            where
-                E: de::Error,
-            {
-                let mut res = ModifiersState::empty();
-                for modifier in value.split('|') {
-                    match modifier.trim().to_lowercase().as_str() {
-                        "command" | "super" => res.insert(ModifiersState::LOGO),
-                        "shift" => res.insert(ModifiersState::SHIFT),
-                        "alt" | "option" => res.insert(ModifiersState::ALT),
-                        "control" => res.insert(ModifiersState::CTRL),
-                        "none" => (),
-                        _ => error!(target: LOG_TARGET_CONFIG, "Unknown modifier {:?}", modifier),
-                    }
-                }
-
-                Ok(ModsWrapper(res))
-            }
-        }
-
-        deserializer.deserialize_str(ModsVisitor)
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use glutin::event::ModifiersState;
-
-    use alacritty_terminal::term::TermMode;
-
-    use crate::config::{Action, Binding};
-
-    type MockBinding = Binding<usize>;
-
-    impl Default for MockBinding {
-        fn default() -> Self {
-            Self {
-                mods: Default::default(),
-                action: Default::default(),
-                mode: TermMode::empty(),
-                notmode: TermMode::empty(),
-                trigger: Default::default(),
-            }
-        }
-    }
-
-    #[test]
-    fn binding_matches_itself() {
-        let binding = MockBinding::default();
-        let identical_binding = MockBinding::default();
-
-        assert!(binding.triggers_match(&identical_binding));
-        assert!(identical_binding.triggers_match(&binding));
-    }
-
-    #[test]
-    fn binding_matches_different_action() {
-        let binding = MockBinding::default();
-        let mut different_action = MockBinding::default();
-        different_action.action = Action::ClearHistory;
-
-        assert!(binding.triggers_match(&different_action));
-        assert!(different_action.triggers_match(&binding));
-    }
-
-    #[test]
-    fn mods_binding_requires_strict_match() {
-        let mut superset_mods = MockBinding::default();
-        superset_mods.mods = ModifiersState::all();
-        let mut subset_mods = MockBinding::default();
-        subset_mods.mods = ModifiersState::ALT;
-
-        assert!(!superset_mods.triggers_match(&subset_mods));
-        assert!(!subset_mods.triggers_match(&superset_mods));
-    }
-
-    #[test]
-    fn binding_matches_identical_mode() {
-        let mut b1 = MockBinding::default();
-        b1.mode = TermMode::ALT_SCREEN;
-        let mut b2 = MockBinding::default();
-        b2.mode = TermMode::ALT_SCREEN;
-
-        assert!(b1.triggers_match(&b2));
-    }
-
-    #[test]
-    fn binding_without_mode_matches_any_mode() {
-        let b1 = MockBinding::default();
-        let mut b2 = MockBinding::default();
-        b2.mode = TermMode::APP_KEYPAD;
-        b2.notmode = TermMode::ALT_SCREEN;
-
-        assert!(b1.triggers_match(&b2));
-    }
-
-    #[test]
-    fn binding_with_mode_matches_empty_mode() {
-        let mut b1 = MockBinding::default();
-        b1.mode = TermMode::APP_KEYPAD;
-        b1.notmode = TermMode::ALT_SCREEN;
-        let b2 = MockBinding::default();
-
-        assert!(b1.triggers_match(&b2));
-    }
-
-    #[test]
-    fn binding_matches_superset_mode() {
-        let mut b1 = MockBinding::default();
-        b1.mode = TermMode::APP_KEYPAD;
-        let mut b2 = MockBinding::default();
-        b2.mode = TermMode::ALT_SCREEN | TermMode::APP_KEYPAD;
-
-        assert!(b1.triggers_match(&b2));
-    }
-
-    #[test]
-    fn binding_matches_subset_mode() {
-        let mut b1 = MockBinding::default();
-        b1.mode = TermMode::ALT_SCREEN | TermMode::APP_KEYPAD;
-        let mut b2 = MockBinding::default();
-        b2.mode = TermMode::APP_KEYPAD;
-
-        assert!(b1.triggers_match(&b2));
-    }
-
-    #[test]
-    fn binding_matches_partial_intersection() {
-        let mut b1 = MockBinding::default();
-        b1.mode = TermMode::ALT_SCREEN | TermMode::APP_KEYPAD;
-        let mut b2 = MockBinding::default();
-        b2.mode = TermMode::APP_KEYPAD | TermMode::APP_CURSOR;
-
-        assert!(b1.triggers_match(&b2));
-    }
-
-    #[test]
-    fn binding_mismatches_notmode() {
-        let mut b1 = MockBinding::default();
-        b1.mode = TermMode::ALT_SCREEN;
-        let mut b2 = MockBinding::default();
-        b2.notmode = TermMode::ALT_SCREEN;
-
-        assert!(!b1.triggers_match(&b2));
-    }
-
-    #[test]
-    fn binding_mismatches_unrelated() {
-        let mut b1 = MockBinding::default();
-        b1.mode = TermMode::ALT_SCREEN;
-        let mut b2 = MockBinding::default();
-        b2.mode = TermMode::APP_KEYPAD;
-
-        assert!(!b1.triggers_match(&b2));
-    }
-
-    #[test]
-    fn binding_trigger_input() {
-        let mut binding = MockBinding::default();
-        binding.trigger = 13;
-
-        let mods = binding.mods;
-        let mode = binding.mode;
-
-        assert!(binding.is_triggered_by(mode, mods, &13));
-        assert!(!binding.is_triggered_by(mode, mods, &32));
-    }
-
-    #[test]
-    fn binding_trigger_mods() {
-        let mut binding = MockBinding::default();
-        binding.mods = ModifiersState::ALT | ModifiersState::LOGO;
-
-        let superset_mods = ModifiersState::all();
-        let subset_mods = ModifiersState::empty();
-
-        let t = binding.trigger;
-        let mode = binding.mode;
-
-        assert!(binding.is_triggered_by(mode, binding.mods, &t));
-        assert!(!binding.is_triggered_by(mode, superset_mods, &t));
-        assert!(!binding.is_triggered_by(mode, subset_mods, &t));
-    }
-
-    #[test]
-    fn binding_trigger_modes() {
-        let mut binding = MockBinding::default();
-        binding.mode = TermMode::ALT_SCREEN;
-
-        let t = binding.trigger;
-        let mods = binding.mods;
-
-        assert!(!binding.is_triggered_by(TermMode::INSERT, mods, &t));
-        assert!(binding.is_triggered_by(TermMode::ALT_SCREEN, mods, &t));
-        assert!(binding.is_triggered_by(TermMode::ALT_SCREEN | TermMode::INSERT, mods, &t));
-    }
-
-    #[test]
-    fn binding_trigger_notmodes() {
-        let mut binding = MockBinding::default();
-        binding.notmode = TermMode::ALT_SCREEN;
-
-        let t = binding.trigger;
-        let mods = binding.mods;
-
-        assert!(binding.is_triggered_by(TermMode::INSERT, mods, &t));
-        assert!(!binding.is_triggered_by(TermMode::ALT_SCREEN, mods, &t));
-        assert!(!binding.is_triggered_by(TermMode::ALT_SCREEN | TermMode::INSERT, mods, &t));
-    }
-}
diff --git a/alacritty/src/config/mod.rs b/alacritty/src/config/mod.rs
deleted file mode 100644
index 56e09be..0000000
--- a/alacritty/src/config/mod.rs
+++ /dev/null
@@ -1,228 +0,0 @@
-use std::env;
-use std::fmt::{self, Display, Formatter};
-use std::fs;
-use std::io;
-use std::path::PathBuf;
-
-#[cfg(windows)]
-use dirs;
-use log::{error, warn};
-use serde_yaml;
-#[cfg(not(windows))]
-use xdg;
-
-use alacritty_terminal::config::{Config as TermConfig, LOG_TARGET_CONFIG};
-
-mod bindings;
-pub mod monitor;
-mod mouse;
-mod ui_config;
-
-pub use crate::config::bindings::{Action, Binding, Key};
-#[cfg(test)]
-pub use crate::config::mouse::{ClickHandler, Mouse};
-use crate::config::ui_config::UIConfig;
-
-pub type Config = TermConfig<UIConfig>;
-
-/// Result from config loading
-pub type Result<T> = std::result::Result<T, Error>;
-
-/// Errors occurring during config loading
-#[derive(Debug)]
-pub enum Error {
-    /// Config file not found
-    NotFound,
-
-    /// Couldn't read $HOME environment variable
-    ReadingEnvHome(env::VarError),
-
-    /// io error reading file
-    Io(io::Error),
-
-    /// Not valid yaml or missing parameters
-    Yaml(serde_yaml::Error),
-}
-
-impl std::error::Error for Error {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        match self {
-            Error::NotFound => None,
-            Error::ReadingEnvHome(err) => err.source(),
-            Error::Io(err) => err.source(),
-            Error::Yaml(err) => err.source(),
-        }
-    }
-}
-
-impl Display for Error {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        match self {
-            Error::NotFound => write!(f, "Couldn't locate config file"),
-            Error::ReadingEnvHome(err) => {
-                write!(f, "Couldn't read $HOME environment variable: {}", err)
-            },
-            Error::Io(err) => write!(f, "Error reading config file: {}", err),
-            Error::Yaml(err) => write!(f, "Problem with config: {}", err),
-        }
-    }
-}
-
-impl From<env::VarError> for Error {
-    fn from(val: env::VarError) -> Self {
-        Error::ReadingEnvHome(val)
-    }
-}
-
-impl From<io::Error> for Error {
-    fn from(val: io::Error) -> Self {
-        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) -> Self {
-        Error::Yaml(val)
-    }
-}
-
-/// 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
-#[cfg(not(windows))]
-pub fn installed_config() -> Option<PathBuf> {
-    // 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
-        })
-}
-
-#[cfg(windows)]
-pub fn installed_config() -> Option<PathBuf> {
-    dirs::config_dir().map(|path| path.join("alacritty\\alacritty.yml")).filter(|new| new.exists())
-}
-
-pub fn load_from(path: PathBuf) -> Config {
-    let mut config = reload_from(&path).unwrap_or_else(|_| Config::default());
-    config.config_path = Some(path);
-    config
-}
-
-pub fn reload_from(path: &PathBuf) -> Result<Config> {
-    match read_config(path) {
-        Ok(config) => Ok(config),
-        Err(err) => {
-            error!(target: LOG_TARGET_CONFIG, "Unable to load config {:?}: {}", path, err);
-            Err(err)
-        },
-    }
-}
-
-fn read_config(path: &PathBuf) -> Result<Config> {
-    let mut contents = fs::read_to_string(path)?;
-
-    // Remove UTF-8 BOM
-    if contents.starts_with('\u{FEFF}') {
-        contents = contents.split_off(3);
-    }
-
-    parse_config(&contents)
-}
-
-fn parse_config(contents: &str) -> Result<Config> {
-    match serde_yaml::from_str(contents) {
-        Err(error) => {
-            // Prevent parsing error with an empty string and commented out file.
-            if error.to_string() == "EOF while parsing a value" {
-                Ok(Config::default())
-            } else {
-                Err(Error::Yaml(error))
-            }
-        },
-        Ok(config) => {
-            print_deprecation_warnings(&config);
-            Ok(config)
-        },
-    }
-}
-
-fn print_deprecation_warnings(config: &Config) {
-    if config.window.start_maximized.is_some() {
-        warn!(
-            target: LOG_TARGET_CONFIG,
-            "Config window.start_maximized is deprecated; please use window.startup_mode instead"
-        );
-    }
-
-    if config.render_timer.is_some() {
-        warn!(
-            target: LOG_TARGET_CONFIG,
-            "Config render_timer is deprecated; please use debug.render_timer instead"
-        );
-    }
-
-    if config.persistent_logging.is_some() {
-        warn!(
-            target: LOG_TARGET_CONFIG,
-            "Config persistent_logging is deprecated; please use debug.persistent_logging instead"
-        );
-    }
-
-    if config.scrolling.faux_multiplier().is_some() {
-        warn!(
-            target: LOG_TARGET_CONFIG,
-            "Config scrolling.faux_multiplier is deprecated; the alternate scroll escape can now \
-             be used to disable it and `scrolling.multiplier` controls the number of scrolled \
-             lines"
-        );
-    }
-
-    if config.scrolling.auto_scroll.is_some() {
-        warn!(
-            target: LOG_TARGET_CONFIG,
-            "Config scrolling.auto_scroll has been removed and is now always disabled, it can be \
-             safely removed from the config"
-        );
-    }
-}
-
-#[cfg(test)]
-mod test {
-    static DEFAULT_ALACRITTY_CONFIG: &str =
-        include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../alacritty.yml"));
-
-    use super::Config;
-
-    #[test]
-    fn config_read_eof() {
-        assert_eq!(super::parse_config(DEFAULT_ALACRITTY_CONFIG).unwrap(), Config::default());
-    }
-}
diff --git a/alacritty/src/config/monitor.rs b/alacritty/src/config/monitor.rs
deleted file mode 100644
index 8dc5379..0000000
--- a/alacritty/src/config/monitor.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-use std::path::PathBuf;
-use std::sync::mpsc;
-use std::time::Duration;
-
-use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
-
-use alacritty_terminal::event::{Event, EventListener};
-use alacritty_terminal::util;
-
-use crate::event::EventProxy;
-
-pub struct Monitor {
-    _thread: ::std::thread::JoinHandle<()>,
-}
-
-impl Monitor {
-    pub fn new<P>(path: P, event_proxy: EventProxy) -> Monitor
-    where
-        P: Into<PathBuf>,
-    {
-        let path = path.into();
-
-        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") {
-                        DebouncedEvent::Rename(..) => continue,
-                        DebouncedEvent::Write(path)
-                        | DebouncedEvent::Create(path)
-                        | DebouncedEvent::Chmod(path) => {
-                            if path != config_path {
-                                continue;
-                            }
-
-                            event_proxy.send_event(Event::ConfigReload(path));
-                        },
-                        _ => {},
-                    }
-                }
-            }),
-        }
-    }
-}
diff --git a/alacritty/src/config/mouse.rs b/alacritty/src/config/mouse.rs
deleted file mode 100644
index b7832b4..0000000
--- a/alacritty/src/config/mouse.rs
+++ /dev/null
@@ -1,115 +0,0 @@
-use std::time::Duration;
-
-use glutin::event::ModifiersState;
-use log::error;
-use serde::{Deserialize, Deserializer};
-
-use alacritty_terminal::config::{failure_default, LOG_TARGET_CONFIG};
-
-use crate::config::bindings::{CommandWrapper, ModsWrapper};
-
-#[serde(default)]
-#[derive(Default, Clone, Debug, Deserialize, PartialEq, Eq)]
-pub struct Mouse {
-    #[serde(deserialize_with = "failure_default")]
-    pub double_click: ClickHandler,
-    #[serde(deserialize_with = "failure_default")]
-    pub triple_click: ClickHandler,
-    #[serde(deserialize_with = "failure_default")]
-    pub hide_when_typing: bool,
-    #[serde(deserialize_with = "failure_default")]
-    pub url: Url,
-}
-
-#[serde(default)]
-#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
-pub struct Url {
-    // Program for opening links
-    #[serde(deserialize_with = "deserialize_launcher")]
-    pub launcher: Option<CommandWrapper>,
-
-    // Modifier used to open links
-    #[serde(deserialize_with = "failure_default")]
-    modifiers: ModsWrapper,
-}
-
-impl Url {
-    pub fn mods(&self) -> ModifiersState {
-        self.modifiers.into_inner()
-    }
-}
-
-fn deserialize_launcher<'a, D>(
-    deserializer: D,
-) -> ::std::result::Result<Option<CommandWrapper>, D::Error>
-where
-    D: Deserializer<'a>,
-{
-    let default = Url::default().launcher;
-
-    // Deserialize to generic value
-    let val = serde_yaml::Value::deserialize(deserializer)?;
-
-    // Accept `None` to disable the launcher
-    if val.as_str().filter(|v| v.to_lowercase() == "none").is_some() {
-        return Ok(None);
-    }
-
-    match <Option<CommandWrapper>>::deserialize(val) {
-        Ok(launcher) => Ok(launcher),
-        Err(err) => {
-            error!(
-                target: LOG_TARGET_CONFIG,
-                "Problem with config: {}; using {}",
-                err,
-                default.clone().unwrap().program()
-            );
-            Ok(default)
-        },
-    }
-}
-
-impl Default for Url {
-    fn default() -> Url {
-        Url {
-            #[cfg(not(any(target_os = "macos", windows)))]
-            launcher: Some(CommandWrapper::Just(String::from("xdg-open"))),
-            #[cfg(target_os = "macos")]
-            launcher: Some(CommandWrapper::Just(String::from("open"))),
-            #[cfg(windows)]
-            launcher: Some(CommandWrapper::Just(String::from("explorer"))),
-            modifiers: Default::default(),
-        }
-    }
-}
-
-#[serde(default)]
-#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
-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: Deserializer<'a>,
-{
-    let value = serde_yaml::Value::deserialize(deserializer)?;
-    match u64::deserialize(value) {
-        Ok(threshold_ms) => Ok(Duration::from_millis(threshold_ms)),
-        Err(err) => {
-            error!(target: LOG_TARGET_CONFIG, "Problem with config: {}; using default value", err);
-            Ok(default_threshold_ms())
-        },
-    }
-}
diff --git a/alacritty/src/config/ui_config.rs b/alacritty/src/config/ui_config.rs
deleted file mode 100644
index d7a477a..0000000
--- a/alacritty/src/config/ui_config.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-use serde::{Deserialize, Deserializer};
-
-use alacritty_terminal::config::failure_default;
-
-use crate::config::bindings::{self, Binding, KeyBinding, MouseBinding};
-use crate::config::mouse::Mouse;
-
-#[derive(Debug, PartialEq, Deserialize)]
-pub struct UIConfig {
-    #[serde(default, deserialize_with = "failure_default")]
-    pub mouse: Mouse,
-
-    /// Keybindings
-    #[serde(default = "default_key_bindings", deserialize_with = "deserialize_key_bindings")]
-    pub key_bindings: Vec<KeyBinding>,
-
-    /// Bindings for the mouse
-    #[serde(default = "default_mouse_bindings", deserialize_with = "deserialize_mouse_bindings")]
-    pub mouse_bindings: Vec<MouseBinding>,
-}
-
-impl Default for UIConfig {
-    fn default() -> Self {
-        UIConfig {
-            mouse: Mouse::default(),
-            key_bindings: default_key_bindings(),
-            mouse_bindings: default_mouse_bindings(),
-        }
-    }
-}
-
-fn default_key_bindings() -> Vec<KeyBinding> {
-    bindings::default_key_bindings()
-}
-
-fn default_mouse_bindings() -> Vec<MouseBinding> {
-    bindings::default_mouse_bindings()
-}
-
-fn deserialize_key_bindings<'a, D>(deserializer: D) -> Result<Vec<KeyBinding>, D::Error>
-where
-    D: Deserializer<'a>,
-{
-    deserialize_bindings(deserializer, bindings::default_key_bindings())
-}
-
-fn deserialize_mouse_bindings<'a, D>(deserializer: D) -> Result<Vec<MouseBinding>, D::Error>
-where
-    D: Deserializer<'a>,
-{
-    deserialize_bindings(deserializer, bindings::default_mouse_bindings())
-}
-
-fn deserialize_bindings<'a, D, T>(
-    deserializer: D,
-    mut default: Vec<Binding<T>>,
-) -> Result<Vec<Binding<T>>, D::Error>
-where
-    D: Deserializer<'a>,
-    T: Copy + Eq,
-    Binding<T>: Deserialize<'a>,
-{
-    let mut bindings: Vec<Binding<T>> = failure_default(deserializer)?;
-
-    // Remove matching default bindings
-    for binding in bindings.iter() {
-        default.retain(|b| !b.triggers_match(binding));
-    }
-
-    bindings.extend(default);
-
-    Ok(bindings)
-}
diff --git a/alacritty/src/cursor.rs b/alacritty/src/cursor.rs
deleted file mode 100644
index a3e6a2c..0000000
--- a/alacritty/src/cursor.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Helpers for creating different cursor glyphs from font metrics
-
-use std::cmp;
-
-use alacritty_terminal::ansi::CursorStyle;
-
-use font::{BitmapBuffer, Metrics, RasterizedGlyph};
-
-/// Width/Height of the cursor relative to the font width
-pub const CURSOR_WIDTH_PERCENTAGE: i32 = 15;
-
-pub fn get_cursor_glyph(
-    cursor: CursorStyle,
-    metrics: Metrics,
-    offset_x: i8,
-    offset_y: i8,
-    is_wide: bool,
-) -> RasterizedGlyph {
-    // Calculate the cell metrics
-    let height = metrics.line_height as i32 + i32::from(offset_y);
-    let mut width = metrics.average_advance as i32 + i32::from(offset_x);
-    let line_width = cmp::max(width * CURSOR_WIDTH_PERCENTAGE / 100, 1);
-
-    // Double the cursor width if it's above a double-width glyph
-    if is_wide {
-        width *= 2;
-    }
-
-    match cursor {
-        CursorStyle::HollowBlock => get_box_cursor_glyph(height, width, line_width),
-        CursorStyle::Underline => get_underline_cursor_glyph(width, line_width),
-        CursorStyle::Beam => get_beam_cursor_glyph(height, line_width),
-        CursorStyle::Block => get_block_cursor_glyph(height, width),
-        CursorStyle::Hidden => RasterizedGlyph::default(),
-    }
-}
-
-// Returns a custom underline cursor character
-pub fn get_underline_cursor_glyph(width: i32, line_width: i32) -> RasterizedGlyph {
-    // Create a new rectangle, the height is relative to the font width
-    let buf = vec![255u8; (width * line_width * 3) as usize];
-
-    // Create a custom glyph with the rectangle data attached to it
-    RasterizedGlyph {
-        c: ' ',
-        top: line_width,
-        left: 0,
-        height: line_width,
-        width,
-        buf: BitmapBuffer::RGB(buf),
-    }
-}
-
-// Returns a custom beam cursor character
-pub fn get_beam_cursor_glyph(height: i32, line_width: i32) -> RasterizedGlyph {
-    // Create a new rectangle that is at least one pixel wide
-    let buf = vec![255u8; (line_width * height * 3) as usize];
-
-    // Create a custom glyph with the rectangle data attached to it
-    RasterizedGlyph {
-        c: ' ',
-        top: height,
-        left: 0,
-        height,
-        width: line_width,
-        buf: BitmapBuffer::RGB(buf),
-    }
-}
-
-// Returns a custom box cursor character
-pub fn get_box_cursor_glyph(height: i32, width: i32, line_width: i32) -> RasterizedGlyph {
-    // Create a new box outline rectangle
-    let mut buf = Vec::with_capacity((width * height * 3) as usize);
-    for y in 0..height {
-        for x in 0..width {
-            if y < line_width
-                || y >= height - line_width
-                || x < line_width
-                || x >= width - line_width
-            {
-                buf.append(&mut vec![255u8; 3]);
-            } else {
-                buf.append(&mut vec![0u8; 3]);
-            }
-        }
-    }
-
-    // Create a custom glyph with the rectangle data attached to it
-    RasterizedGlyph { c: ' ', top: height, left: 0, height, width, buf: BitmapBuffer::RGB(buf) }
-}
-
-// Returns a custom block cursor character
-pub fn get_block_cursor_glyph(height: i32, width: i32) -> RasterizedGlyph {
-    // Create a completely filled glyph
-    let buf = vec![255u8; (width * height * 3) as usize];
-
-    // Create a custom glyph with the rectangle data attached to it
-    RasterizedGlyph { c: ' ', top: height, left: 0, height, width, buf: BitmapBuffer::RGB(buf) }
-}
diff --git a/alacritty/src/display.rs b/alacritty/src/display.rs
deleted file mode 100644
index 317c875..0000000
--- a/alacritty/src/display.rs
+++ /dev/null
@@ -1,496 +0,0 @@
-// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! The display subsystem including window management, font rasterization, and
-//! GPU drawing.
-use std::f64;
-use std::fmt::{self, Formatter};
-use std::time::Instant;
-
-use glutin::dpi::{PhysicalPosition, PhysicalSize};
-use glutin::event::ModifiersState;
-use glutin::event_loop::EventLoop;
-#[cfg(not(any(target_os = "macos", windows)))]
-use glutin::platform::unix::EventLoopWindowTargetExtUnix;
-use glutin::window::CursorIcon;
-use log::{debug, info};
-use parking_lot::MutexGuard;
-
-use font::{self, Rasterize};
-
-use alacritty_terminal::config::{Font, StartupMode};
-use alacritty_terminal::event::{Event, OnResize};
-use alacritty_terminal::index::Line;
-use alacritty_terminal::message_bar::MessageBuffer;
-use alacritty_terminal::meter::Meter;
-use alacritty_terminal::selection::Selection;
-use alacritty_terminal::term::color::Rgb;
-use alacritty_terminal::term::{RenderableCell, SizeInfo, Term, TermMode};
-
-use crate::config::Config;
-use crate::event::{DisplayUpdate, Mouse};
-use crate::renderer::rects::{RenderLines, RenderRect};
-use crate::renderer::{self, GlyphCache, QuadRenderer};
-use crate::url::{Url, Urls};
-use crate::window::{self, Window};
-
-#[derive(Debug)]
-pub enum Error {
-    /// Error with window management
-    Window(window::Error),
-
-    /// Error dealing with fonts
-    Font(font::Error),
-
-    /// Error in renderer
-    Render(renderer::Error),
-
-    /// Error during buffer swap
-    ContextError(glutin::ContextError),
-}
-
-impl std::error::Error for Error {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        match self {
-            Error::Window(err) => err.source(),
-            Error::Font(err) => err.source(),
-            Error::Render(err) => err.source(),
-            Error::ContextError(err) => err.source(),
-        }
-    }
-}
-
-impl fmt::Display for Error {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        match self {
-            Error::Window(err) => err.fmt(f),
-            Error::Font(err) => err.fmt(f),
-            Error::Render(err) => err.fmt(f),
-            Error::ContextError(err) => err.fmt(f),
-        }
-    }
-}
-
-impl From<window::Error> for Error {
-    fn from(val: window::Error) -> Self {
-        Error::Window(val)
-    }
-}
-
-impl From<font::Error> for Error {
-    fn from(val: font::Error) -> Self {
-        Error::Font(val)
-    }
-}
-
-impl From<renderer::Error> for Error {
-    fn from(val: renderer::Error) -> Self {
-        Error::Render(val)
-    }
-}
-
-impl From<glutin::ContextError> for Error {
-    fn from(val: glutin::ContextError) -> Self {
-        Error::ContextError(val)
-    }
-}
-
-/// The display wraps a window, font rasterizer, and GPU renderer
-pub struct Display {
-    pub size_info: SizeInfo,
-    pub window: Window,
-    pub urls: Urls,
-
-    /// Currently highlighted URL.
-    pub highlighted_url: Option<Url>,
-
-    renderer: QuadRenderer,
-    glyph_cache: GlyphCache,
-    meter: Meter,
-}
-
-impl Display {
-    pub fn new(config: &Config, event_loop: &EventLoop<Event>) -> Result<Display, Error> {
-        // Guess DPR based on first monitor
-        let estimated_dpr =
-            event_loop.available_monitors().next().map(|m| m.scale_factor()).unwrap_or(1.);
-
-        // Guess the target window dimensions
-        let metrics = GlyphCache::static_metrics(config.font.clone(), estimated_dpr)?;
-        let (cell_width, cell_height) = compute_cell_size(config, &metrics);
-        let dimensions =
-            GlyphCache::calculate_dimensions(config, estimated_dpr, cell_width, cell_height);
-
-        debug!("Estimated DPR: {}", estimated_dpr);
-        debug!("Estimated Cell Size: {} x {}", cell_width, cell_height);
-        debug!("Estimated Dimensions: {:?}", dimensions);
-
-        // Create the window where Alacritty will be displayed
-        let size = dimensions.map(|(width, height)| PhysicalSize::new(width, height));
-
-        // Spawn window
-        let mut window = Window::new(event_loop, &config, size)?;
-
-        let dpr = window.scale_factor();
-        info!("Device pixel ratio: {}", dpr);
-
-        // get window properties for initializing the other subsystems
-        let viewport_size = window.inner_size();
-
-        // Create renderer
-        let mut renderer = QuadRenderer::new()?;
-
-        let (glyph_cache, cell_width, cell_height) =
-            Self::new_glyph_cache(dpr, &mut renderer, config)?;
-
-        let mut padding_x = f32::from(config.window.padding.x) * dpr as f32;
-        let mut padding_y = f32::from(config.window.padding.y) * dpr as f32;
-
-        if let Some((width, height)) =
-            GlyphCache::calculate_dimensions(config, dpr, cell_width, cell_height)
-        {
-            let PhysicalSize { width: w, height: h } = window.inner_size();
-            if w == width && h == height {
-                info!("Estimated DPR correctly, skipping resize");
-            } else {
-                window.set_inner_size(PhysicalSize::new(width, height));
-            }
-        } else if config.window.dynamic_padding {
-            // Make sure additional padding is spread evenly
-            padding_x = dynamic_padding(padding_x, viewport_size.width as f32, cell_width);
-            padding_y = dynamic_padding(padding_y, viewport_size.height as f32, cell_height);
-        }
-
-        padding_x = padding_x.floor();
-        padding_y = padding_y.floor();
-
-        info!("Cell Size: {} x {}", cell_width, cell_height);
-        info!("Padding: {} x {}", padding_x, padding_y);
-
-        // Create new size with at least one column and row
-        let size_info = SizeInfo {
-            dpr,
-            width: (viewport_size.width as f32).max(cell_width + 2. * padding_x),
-            height: (viewport_size.height as f32).max(cell_height + 2. * padding_y),
-            cell_width,
-            cell_height,
-            padding_x,
-            padding_y,
-        };
-
-        // Update OpenGL projection
-        renderer.resize(&size_info);
-
-        // Clear screen
-        let background_color = config.colors.primary.background;
-        renderer.with_api(&config, &size_info, |api| {
-            api.clear(background_color);
-        });
-
-        // We should call `clear` when window is offscreen, so when `window.show()` happens it
-        // would be with background color instead of uninitialized surface.
-        #[cfg(not(any(target_os = "macos", windows)))]
-        {
-            // On Wayland we can safely ignore this call, since the window isn't visible until you
-            // actually draw something into it.
-            if event_loop.is_x11() {
-                window.swap_buffers()
-            }
-        }
-
-        window.set_visible(true);
-
-        // Set window position
-        //
-        // TODO: replace `set_position` with `with_position` once available
-        // Upstream issue: https://github.com/rust-windowing/winit/issues/806
-        if let Some(position) = config.window.position {
-            window.set_outer_position(PhysicalPosition::from((position.x, position.y)));
-        }
-
-        #[allow(clippy::single_match)]
-        match config.window.startup_mode() {
-            StartupMode::Fullscreen => window.set_fullscreen(true),
-            #[cfg(target_os = "macos")]
-            StartupMode::SimpleFullscreen => window.set_simple_fullscreen(true),
-            #[cfg(not(any(target_os = "macos", windows)))]
-            StartupMode::Maximized => window.set_maximized(true),
-            _ => (),
-        }
-
-        Ok(Self {
-            window,
-            renderer,
-            glyph_cache,
-            meter: Meter::new(),
-            size_info,
-            urls: Urls::new(),
-            highlighted_url: None,
-        })
-    }
-
-    fn new_glyph_cache(
-        dpr: f64,
-        renderer: &mut QuadRenderer,
-        config: &Config,
-    ) -> Result<(GlyphCache, f32, f32), Error> {
-        let font = config.font.clone();
-        let rasterizer = font::Rasterizer::new(dpr as f32, config.font.use_thin_strokes())?;
-
-        // Initialize glyph cache
-        let glyph_cache = {
-            info!("Initializing glyph cache...");
-            let init_start = Instant::now();
-
-            let cache =
-                renderer.with_loader(|mut api| GlyphCache::new(rasterizer, &font, &mut api))?;
-
-            let stop = init_start.elapsed();
-            let stop_f = stop.as_secs() as f64 + f64::from(stop.subsec_nanos()) / 1_000_000_000f64;
-            info!("... finished initializing glyph cache in {}s", stop_f);
-
-            cache
-        };
-
-        // Need font metrics to resize the window properly. This suggests to me the
-        // font metrics should be computed before creating the window in the first
-        // place so that a resize is not needed.
-        let (cw, ch) = compute_cell_size(config, &glyph_cache.font_metrics());
-
-        Ok((glyph_cache, cw, ch))
-    }
-
-    /// Update font size and cell dimensions
-    fn update_glyph_cache(&mut self, config: &Config, font: Font) {
-        let size_info = &mut self.size_info;
-        let cache = &mut self.glyph_cache;
-
-        self.renderer.with_loader(|mut api| {
-            let _ = cache.update_font_size(font, size_info.dpr, &mut api);
-        });
-
-        // Update cell size
-        let (cell_width, cell_height) = compute_cell_size(config, &self.glyph_cache.font_metrics());
-        size_info.cell_width = cell_width;
-        size_info.cell_height = cell_height;
-    }
-
-    /// Process update events
-    pub fn handle_update<T>(
-        &mut self,
-        terminal: &mut Term<T>,
-        pty_resize_handle: &mut dyn OnResize,
-        message_buffer: &MessageBuffer,
-        config: &Config,
-        update_pending: DisplayUpdate,
-    ) {
-        // Update font size and cell dimensions
-        if let Some(font) = update_pending.font {
-            self.update_glyph_cache(config, font);
-        }
-
-        let cell_width = self.size_info.cell_width;
-        let cell_height = self.size_info.cell_height;
-
-        // Recalculate padding
-        let mut padding_x = f32::from(config.window.padding.x) * self.size_info.dpr as f32;
-        let mut padding_y = f32::from(config.window.padding.y) * self.size_info.dpr as f32;
-
-        // Update the window dimensions
-        if let Some(size) = update_pending.dimensions {
-            // Ensure we have at least one column and row
-            self.size_info.width = (size.width as f32).max(cell_width + 2. * padding_x);
-            self.size_info.height = (size.height as f32).max(cell_height + 2. * padding_y);
-        }
-
-        // Distribute excess padding equally on all sides
-        if config.window.dynamic_padding {
-            padding_x = dynamic_padding(padding_x, self.size_info.width, cell_width);
-            padding_y = dynamic_padding(padding_y, self.size_info.height, cell_height);
-        }
-
-        self.size_info.padding_x = padding_x.floor() as f32;
-        self.size_info.padding_y = padding_y.floor() as f32;
-
-        let mut pty_size = self.size_info;
-
-        // Subtract message bar lines from pty size
-        if let Some(message) = message_buffer.message() {
-            let lines = message.text(&self.size_info).len();
-            pty_size.height -= pty_size.cell_height * lines as f32;
-        }
-
-        // Resize PTY
-        pty_resize_handle.on_resize(&pty_size);
-
-        // Resize terminal
-        terminal.resize(&pty_size);
-
-        // Resize renderer
-        let physical = PhysicalSize::new(self.size_info.width as u32, self.size_info.height as u32);
-        self.window.resize(physical);
-        self.renderer.resize(&self.size_info);
-    }
-
-    /// Draw the screen
-    ///
-    /// A reference to Term whose state is being drawn must be provided.
-    ///
-    /// This call may block if vsync is enabled
-    pub fn draw<T>(
-        &mut self,
-        terminal: MutexGuard<'_, Term<T>>,
-        message_buffer: &MessageBuffer,
-        config: &Config,
-        mouse: &Mouse,
-        mods: ModifiersState,
-    ) {
-        let grid_cells: Vec<RenderableCell> = terminal.renderable_cells(config).collect();
-        let visual_bell_intensity = terminal.visual_bell.intensity();
-        let background_color = terminal.background_color();
-        let metrics = self.glyph_cache.font_metrics();
-        let glyph_cache = &mut self.glyph_cache;
-        let size_info = self.size_info;
-
-        let selection = !terminal.selection().as_ref().map(Selection::is_empty).unwrap_or(true);
-        let mouse_mode = terminal.mode().intersects(TermMode::MOUSE_MODE);
-
-        // Update IME position
-        #[cfg(not(windows))]
-        self.window.update_ime_position(&terminal, &self.size_info);
-
-        // Drop terminal as early as possible to free lock
-        drop(terminal);
-
-        self.renderer.with_api(&config, &size_info, |api| {
-            api.clear(background_color);
-        });
-
-        let mut lines = RenderLines::new();
-        let mut urls = Urls::new();
-
-        // Draw grid
-        {
-            let _sampler = self.meter.sampler();
-
-            self.renderer.with_api(&config, &size_info, |mut api| {
-                // Iterate over all non-empty cells in the grid
-                for cell in grid_cells {
-                    // Update URL underlines
-                    urls.update(size_info.cols().0, cell);
-
-                    // Update underline/strikeout
-                    lines.update(cell);
-
-                    // Draw the cell
-                    api.render_cell(cell, glyph_cache);
-                }
-            });
-        }
-
-        let mut rects = lines.rects(&metrics, &size_info);
-
-        // Update visible URLs
-        self.urls = urls;
-        if let Some(url) = self.urls.highlighted(config, mouse, mods, mouse_mode, selection) {
-            rects.append(&mut url.rects(&metrics, &size_info));
-
-            self.window.set_mouse_cursor(CursorIcon::Hand);
-
-            self.highlighted_url = Some(url);
-        } else if self.highlighted_url.is_some() {
-            self.highlighted_url = None;
-
-            if mouse_mode {
-                self.window.set_mouse_cursor(CursorIcon::Default);
-            } else {
-                self.window.set_mouse_cursor(CursorIcon::Text);
-            }
-        }
-
-        // Push visual bell after url/underline/strikeout rects
-        if visual_bell_intensity != 0. {
-            let visual_bell_rect = RenderRect::new(
-                0.,
-                0.,
-                size_info.width,
-                size_info.height,
-                config.visual_bell.color,
-                visual_bell_intensity as f32,
-            );
-            rects.push(visual_bell_rect);
-        }
-
-        if let Some(message) = message_buffer.message() {
-            let text = message.text(&size_info);
-
-            // Create a new rectangle for the background
-            let start_line = size_info.lines().0 - text.len();
-            let y = size_info.cell_height.mul_add(start_line as f32, size_info.padding_y);
-            let message_bar_rect =
-                RenderRect::new(0., y, size_info.width, size_info.height - y, message.color(), 1.);
-
-            // Push message_bar in the end, so it'll be above all other content
-            rects.push(message_bar_rect);
-
-            // Draw rectangles
-            self.renderer.draw_rects(&size_info, rects);
-
-            // Relay messages to the user
-            let mut offset = 1;
-            for message_text in text.iter().rev() {
-                self.renderer.with_api(&config, &size_info, |mut api| {
-                    api.render_string(
-                        &message_text,
-                        Line(size_info.lines().saturating_sub(offset)),
-                        glyph_cache,
-                        None,
-                    );
-                });
-                offset += 1;
-            }
-        } else {
-            // Draw rectangles
-            self.renderer.draw_rects(&size_info, rects);
-        }
-
-        // Draw render timer
-        if config.render_timer() {
-            let timing = format!("{:.3} usec", self.meter.average());
-            let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
-            self.renderer.with_api(&config, &size_info, |mut api| {
-                api.render_string(&timing[..], size_info.lines() - 2, glyph_cache, Some(color));
-            });
-        }
-
-        self.window.swap_buffers();
-    }
-}
-
-/// Calculate padding to spread it evenly around the terminal content
-#[inline]
-fn dynamic_padding(padding: f32, dimension: f32, cell_dimension: f32) -> f32 {
-    padding + ((dimension - 2. * padding) % cell_dimension) / 2.
-}
-
-/// Calculate the cell dimensions based on font metrics.
-#[inline]
-fn compute_cell_size(config: &Config, metrics: &font::Metrics) -> (f32, f32) {
-    let offset_x = f64::from(config.font.offset.x);
-    let offset_y = f64::from(config.font.offset.y);
-    (
-        ((metrics.average_advance + offset_x) as f32).floor().max(1.),
-        ((metrics.line_height + offset_y) as f32).floor().max(1.),
-    )
-}
diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs
deleted file mode 100644
index bfadbae..0000000
--- a/alacritty/src/event.rs
+++ /dev/null
@@ -1,686 +0,0 @@
-//! Process window events
-use std::borrow::Cow;
-use std::cmp::max;
-use std::env;
-#[cfg(unix)]
-use std::fs;
-use std::fs::File;
-use std::io::Write;
-use std::mem;
-use std::sync::Arc;
-use std::time::Instant;
-
-use glutin::dpi::PhysicalSize;
-use glutin::event::{ElementState, Event as GlutinEvent, ModifiersState, MouseButton, WindowEvent};
-use glutin::event_loop::{ControlFlow, EventLoop, EventLoopProxy};
-use glutin::platform::desktop::EventLoopExtDesktop;
-use log::{debug, info, warn};
-use serde_json as json;
-
-use font::Size;
-
-use alacritty_terminal::clipboard::ClipboardType;
-use alacritty_terminal::config::Font;
-use alacritty_terminal::config::LOG_TARGET_CONFIG;
-use alacritty_terminal::event::OnResize;
-use alacritty_terminal::event::{Event, EventListener, Notify};
-use alacritty_terminal::grid::Scroll;
-use alacritty_terminal::index::{Column, Line, Point, Side};
-use alacritty_terminal::message_bar::{Message, MessageBuffer};
-use alacritty_terminal::selection::Selection;
-use alacritty_terminal::sync::FairMutex;
-use alacritty_terminal::term::cell::Cell;
-use alacritty_terminal::term::{SizeInfo, Term};
-#[cfg(not(windows))]
-use alacritty_terminal::tty;
-use alacritty_terminal::util::{limit, start_daemon};
-
-use crate::cli::Options;
-use crate::config;
-use crate::config::Config;
-use crate::display::Display;
-use crate::input::{self, ActionContext as _, FONT_SIZE_STEP};
-use crate::window::Window;
-
-#[derive(Default, Clone, Debug, PartialEq)]
-pub struct DisplayUpdate {
-    pub dimensions: Option<PhysicalSize<u32>>,
-    pub message_buffer: Option<()>,
-    pub font: Option<Font>,
-}
-
-impl DisplayUpdate {
-    fn is_empty(&self) -> bool {
-        self.dimensions.is_none() && self.font.is_none() && self.message_buffer.is_none()
-    }
-}
-
-pub struct ActionContext<'a, N, T> {
-    pub notifier: &'a mut N,
-    pub terminal: &'a mut Term<T>,
-    pub size_info: &'a mut SizeInfo,
-    pub mouse: &'a mut Mouse,
-    pub received_count: &'a mut usize,
-    pub suppress_chars: &'a mut bool,
-    pub modifiers: &'a mut ModifiersState,
-    pub window: &'a mut Window,
-    pub message_buffer: &'a mut MessageBuffer,
-    pub display_update_pending: &'a mut DisplayUpdate,
-    pub config: &'a mut Config,
-    font_size: &'a mut Size,
-}
-
-impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext<T> for ActionContext<'a, N, T> {
-    fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&mut self, val: B) {
-        self.notifier.notify(val);
-    }
-
-    fn size_info(&self) -> SizeInfo {
-        *self.size_info
-    }
-
-    fn scroll(&mut self, scroll: Scroll) {
-        self.terminal.scroll_display(scroll);
-
-        if let ElementState::Pressed = self.mouse().left_button_state {
-            let (x, y) = (self.mouse().x, self.mouse().y);
-            let size_info = self.size_info();
-            let point = size_info.pixels_to_coords(x, y);
-            let cell_side = self.mouse().cell_side;
-            self.update_selection(Point { line: point.line, col: point.col }, cell_side);
-        }
-    }
-
-    fn copy_selection(&mut self, ty: ClipboardType) {
-        if let Some(selected) = self.terminal.selection_to_string() {
-            if !selected.is_empty() {
-                self.terminal.clipboard().store(ty, selected);
-            }
-        }
-    }
-
-    fn selection_is_empty(&self) -> bool {
-        self.terminal.selection().as_ref().map(Selection::is_empty).unwrap_or(true)
-    }
-
-    fn clear_selection(&mut self) {
-        *self.terminal.selection_mut() = None;
-        self.terminal.dirty = true;
-    }
-
-    fn update_selection(&mut self, point: Point, side: Side) {
-        let point = self.terminal.visible_to_buffer(point);
-
-        // Update selection if one exists
-        if let Some(ref mut selection) = self.terminal.selection_mut() {
-            selection.update(point, side);
-        }
-
-        self.terminal.dirty = true;
-    }
-
-    fn simple_selection(&mut self, point: Point, side: Side) {
-        let point = self.terminal.visible_to_buffer(point);
-        *self.terminal.selection_mut() = Some(Selection::simple(point, side));
-        self.terminal.dirty = true;
-    }
-
-    fn block_selection(&mut self, point: Point, side: Side) {
-        let point = self.terminal.visible_to_buffer(point);
-        *self.terminal.selection_mut() = Some(Selection::block(point, side));
-        self.terminal.dirty = true;
-    }
-
-    fn semantic_selection(&mut self, point: Point) {
-        let point = self.terminal.visible_to_buffer(point);
-        *self.terminal.selection_mut() = Some(Selection::semantic(point));
-        self.terminal.dirty = true;
-    }
-
-    fn line_selection(&mut self, point: Point) {
-        let point = self.terminal.visible_to_buffer(point);
-        *self.terminal.selection_mut() = Some(Selection::lines(point));
-        self.terminal.dirty = true;
-    }
-
-    fn mouse_coords(&self) -> Option<Point> {
-        let x = self.mouse.x as usize;
-        let y = self.mouse.y as usize;
-
-        if self.size_info.contains_point(x, y) {
-            Some(self.size_info.pixels_to_coords(x, y))
-        } else {
-            None
-        }
-    }
-
-    #[inline]
-    fn mouse_mut(&mut self) -> &mut Mouse {
-        self.mouse
-    }
-
-    #[inline]
-    fn mouse(&self) -> &Mouse {
-        self.mouse
-    }
-
-    #[inline]
-    fn received_count(&mut self) -> &mut usize {
-        &mut self.received_count
-    }
-
-    #[inline]
-    fn suppress_chars(&mut self) -> &mut bool {
-        &mut self.suppress_chars
-    }
-
-    #[inline]
-    fn modifiers(&mut self) -> &mut ModifiersState {
-        &mut self.modifiers
-    }
-
-    #[inline]
-    fn window(&self) -> &Window {
-        self.window
-    }
-
-    #[inline]
-    fn window_mut(&mut self) -> &mut Window {
-        self.window
-    }
-
-    #[inline]
-    fn terminal(&self) -> &Term<T> {
-        self.terminal
-    }
-
-    #[inline]
-    fn terminal_mut(&mut self) -> &mut Term<T> {
-        self.terminal
-    }
-
-    fn spawn_new_instance(&mut self) {
-        let alacritty = env::args().next().unwrap();
-
-        #[cfg(unix)]
-        let args = {
-            #[cfg(not(target_os = "freebsd"))]
-            let proc_prefix = "";
-            #[cfg(target_os = "freebsd")]
-            let proc_prefix = "/compat/linux";
-            let link_path = format!("{}/proc/{}/cwd", proc_prefix, tty::child_pid());
-            if let Ok(path) = fs::read_link(link_path) {
-                vec!["--working-directory".into(), path]
-            } else {
-                Vec::new()
-            }
-        };
-        #[cfg(not(unix))]
-        let args: Vec<String> = Vec::new();
-
-        match start_daemon(&alacritty, &args) {
-            Ok(_) => debug!("Started new Alacritty process: {} {:?}", alacritty, args),
-            Err(_) => warn!("Unable to start new Alacritty process: {} {:?}", alacritty, args),
-        }
-    }
-
-    fn change_font_size(&mut self, delta: f32) {
-        *self.font_size = max(*self.font_size + delta, Size::new(FONT_SIZE_STEP));
-        let font = self.config.font.clone().with_size(*self.font_size);
-        self.display_update_pending.font = Some(font);
-        self.terminal.dirty = true;
-    }
-
-    fn reset_font_size(&mut self) {
-        *self.font_size = self.config.font.size;
-        self.display_update_pending.font = Some(self.config.font.clone());
-        self.terminal.dirty = true;
-    }
-
-    fn pop_message(&mut self) {
-        self.display_update_pending.message_buffer = Some(());
-        self.message_buffer.pop();
-    }
-
-    fn message(&self) -> Option<&Message> {
-        self.message_buffer.message()
-    }
-
-    fn config(&self) -> &Config {
-        self.config
-    }
-}
-
-pub enum ClickState {
-    None,
-    Click,
-    DoubleClick,
-    TripleClick,
-}
-
-/// State of the mouse
-pub struct Mouse {
-    pub x: usize,
-    pub y: usize,
-    pub left_button_state: ElementState,
-    pub middle_button_state: ElementState,
-    pub right_button_state: ElementState,
-    pub last_click_timestamp: Instant,
-    pub click_state: ClickState,
-    pub scroll_px: i32,
-    pub line: Line,
-    pub column: Column,
-    pub cell_side: Side,
-    pub lines_scrolled: f32,
-    pub block_url_launcher: bool,
-    pub last_button: MouseButton,
-    pub inside_grid: bool,
-}
-
-impl Default for Mouse {
-    fn default() -> Mouse {
-        Mouse {
-            x: 0,
-            y: 0,
-            last_click_timestamp: Instant::now(),
-            left_button_state: ElementState::Released,
-            middle_button_state: ElementState::Released,
-            right_button_state: ElementState::Released,
-            click_state: ClickState::None,
-            scroll_px: 0,
-            line: Line(0),
-            column: Column(0),
-            cell_side: Side::Left,
-            lines_scrolled: 0.0,
-            block_url_launcher: false,
-            last_button: MouseButton::Other(0),
-            inside_grid: false,
-        }
-    }
-}
-
-/// The event processor
-///
-/// Stores some state from received events and dispatches actions when they are
-/// triggered.
-pub struct Processor<N> {
-    notifier: N,
-    mouse: Mouse,
-    received_count: usize,
-    suppress_chars: bool,
-    modifiers: ModifiersState,
-    config: Config,
-    message_buffer: MessageBuffer,
-    display: Display,
-    font_size: Size,
-}
-
-impl<N: Notify + OnResize> Processor<N> {
-    /// Create a new event processor
-    ///
-    /// Takes a writer which is expected to be hooked up to the write end of a
-    /// pty.
-    pub fn new(
-        notifier: N,
-        message_buffer: MessageBuffer,
-        config: Config,
-        display: Display,
-    ) -> Processor<N> {
-        Processor {
-            notifier,
-            mouse: Default::default(),
-            received_count: 0,
-            suppress_chars: false,
-            modifiers: Default::default(),
-            font_size: config.font.size,
-            config,
-            message_buffer,
-            display,
-        }
-    }
-
-    /// Run the event loop.
-    pub fn run<T>(&mut self, terminal: Arc<FairMutex<Term<T>>>, mut event_loop: EventLoop<Event>)
-    where
-        T: EventListener,
-    {
-        let mut event_queue = Vec::new();
-
-        event_loop.run_return(|event, _event_loop, control_flow| {
-            if self.config.debug.print_events {
-                info!("glutin event: {:?}", event);
-            }
-
-            // Ignore all events we do not care about
-            if Self::skip_event(&event) {
-                return;
-            }
-
-            match event {
-                // Check for shutdown
-                GlutinEvent::UserEvent(Event::Exit) => {
-                    *control_flow = ControlFlow::Exit;
-                    return;
-                },
-                // Process events
-                GlutinEvent::RedrawEventsCleared => {
-                    *control_flow = ControlFlow::Wait;
-
-                    if event_queue.is_empty() {
-                        return;
-                    }
-                },
-                // Remap DPR change event to remove lifetime
-                GlutinEvent::WindowEvent {
-                    event: WindowEvent::ScaleFactorChanged { scale_factor, new_inner_size },
-                    ..
-                } => {
-                    *control_flow = ControlFlow::Poll;
-                    let size = (new_inner_size.width, new_inner_size.height);
-                    let event = GlutinEvent::UserEvent(Event::DPRChanged(scale_factor, size));
-                    event_queue.push(event);
-                    return;
-                },
-                // Transmute to extend lifetime, which exists only for `ScaleFactorChanged` event.
-                // Since we remap that event to remove the lifetime, this is safe.
-                event => unsafe {
-                    *control_flow = ControlFlow::Poll;
-                    event_queue.push(mem::transmute(event));
-                    return;
-                },
-            }
-
-            let mut terminal = terminal.lock();
-
-            let mut display_update_pending = DisplayUpdate::default();
-
-            let context = ActionContext {
-                terminal: &mut terminal,
-                notifier: &mut self.notifier,
-                mouse: &mut self.mouse,
-                size_info: &mut self.display.size_info,
-                received_count: &mut self.received_count,
-                suppress_chars: &mut self.suppress_chars,
-                modifiers: &mut self.modifiers,
-                message_buffer: &mut self.message_buffer,
-                display_update_pending: &mut display_update_pending,
-                window: &mut self.display.window,
-                font_size: &mut self.font_size,
-                config: &mut self.config,
-            };
-            let mut processor =
-                input::Processor::new(context, &self.display.urls, &self.display.highlighted_url);
-
-            for event in event_queue.drain(..) {
-                Processor::handle_event(event, &mut processor);
-            }
-
-            // Process DisplayUpdate events
-            if !display_update_pending.is_empty() {
-                self.display.handle_update(
-                    &mut terminal,
-                    &mut self.notifier,
-                    &self.message_buffer,
-                    &self.config,
-                    display_update_pending,
-                );
-            }
-
-            if terminal.dirty {
-                terminal.dirty = false;
-
-                // Request immediate re-draw if visual bell animation is not finished yet
-                if !terminal.visual_bell.completed() {
-                    event_queue.push(GlutinEvent::UserEvent(Event::Wakeup));
-                }
-
-                // Redraw screen
-                self.display.draw(
-                    terminal,
-                    &self.message_buffer,
-                    &self.config,
-                    &self.mouse,
-                    self.modifiers,
-                );
-            }
-        });
-
-        // Write ref tests to disk
-        self.write_ref_test_results(&terminal.lock());
-    }
-
-    /// Handle events from glutin
-    ///
-    /// Doesn't take self mutably due to borrow checking.
-    fn handle_event<T>(
-        event: GlutinEvent<Event>,
-        processor: &mut input::Processor<T, ActionContext<N, T>>,
-    ) where
-        T: EventListener,
-    {
-        match event {
-            GlutinEvent::UserEvent(event) => match event {
-                Event::DPRChanged(scale_factor, (width, height)) => {
-                    let display_update_pending = &mut processor.ctx.display_update_pending;
-
-                    // Push current font to update its DPR
-                    display_update_pending.font =
-                        Some(processor.ctx.config.font.clone().with_size(*processor.ctx.font_size));
-
-                    // Resize to event's dimensions, since no resize event is emitted on Wayland
-                    display_update_pending.dimensions = Some(PhysicalSize::new(width, height));
-
-                    processor.ctx.size_info.dpr = scale_factor;
-                    processor.ctx.terminal.dirty = true;
-                },
-                Event::Title(title) => processor.ctx.window.set_title(&title),
-                Event::Wakeup => processor.ctx.terminal.dirty = true,
-                Event::Urgent => {
-                    processor.ctx.window.set_urgent(!processor.ctx.terminal.is_focused)
-                },
-                Event::ConfigReload(path) => {
-                    processor.ctx.message_buffer.remove_target(LOG_TARGET_CONFIG);
-                    processor.ctx.display_update_pending.message_buffer = Some(());
-
-                    if let Ok(config) = config::reload_from(&path) {
-                        let options = Options::new();
-                        let config = options.into_config(config);
-
-                        processor.ctx.terminal.update_config(&config);
-
-                        if processor.ctx.config.font != config.font {
-                            // Do not update font size if it has been changed at runtime
-                            if *processor.ctx.font_size == processor.ctx.config.font.size {
-                                *processor.ctx.font_size = config.font.size;
-                            }
-
-                            let font = config.font.clone().with_size(*processor.ctx.font_size);
-                            processor.ctx.display_update_pending.font = Some(font);
-                        }
-
-                        *processor.ctx.config = config;
-
-                        processor.ctx.terminal.dirty = true;
-                    }
-                },
-                Event::Message(message) => {
-                    processor.ctx.message_buffer.push(message);
-                    processor.ctx.display_update_pending.message_buffer = Some(());
-                    processor.ctx.terminal.dirty = true;
-                },
-                Event::MouseCursorDirty => processor.reset_mouse_cursor(),
-                Event::Exit => (),
-            },
-            GlutinEvent::RedrawRequested(_) => processor.ctx.terminal.dirty = true,
-            GlutinEvent::WindowEvent { event, window_id, .. } => {
-                use glutin::event::WindowEvent::*;
-                match event {
-                    CloseRequested => processor.ctx.terminal.exit(),
-                    Resized(size) => {
-                        #[cfg(windows)]
-                        {
-                            // Minimizing the window sends a Resize event with zero width and
-                            // height. But there's no need to ever actually resize to this.
-                            // Both WinPTY & ConPTY have issues when resizing down to zero size
-                            // and back.
-                            if size.width == 0 && size.height == 0 {
-                                return;
-                            }
-                        }
-
-                        processor.ctx.display_update_pending.dimensions = Some(size);
-                        processor.ctx.terminal.dirty = true;
-                    },
-                    KeyboardInput { input, is_synthetic: false, .. } => {
-                        processor.key_input(input);
-                        if input.state == ElementState::Pressed {
-                            // Hide cursor while typing
-                            if processor.ctx.config.ui_config.mouse.hide_when_typing {
-                                processor.ctx.window.set_mouse_visible(false);
-                            }
-                        }
-                    },
-                    ReceivedCharacter(c) => processor.received_char(c),
-                    MouseInput { state, button, .. } => {
-                        if !cfg!(target_os = "macos") || processor.ctx.terminal.is_focused {
-                            processor.ctx.window.set_mouse_visible(true);
-                            processor.mouse_input(state, button);
-                            processor.ctx.terminal.dirty = true;
-                        }
-                    },
-                    CursorMoved { position, .. } => {
-                        let (x, y) = position.into();
-                        let x = limit(x, 0, processor.ctx.size_info.width as i32);
-                        let y = limit(y, 0, processor.ctx.size_info.height as i32);
-
-                        processor.ctx.window.set_mouse_visible(true);
-                        processor.mouse_moved(x as usize, y as usize);
-                    },
-                    MouseWheel { delta, phase, .. } => {
-                        processor.ctx.window.set_mouse_visible(true);
-                        processor.mouse_wheel_input(delta, phase);
-                    },
-                    Focused(is_focused) => {
-                        if window_id == processor.ctx.window.window_id() {
-                            processor.ctx.terminal.is_focused = is_focused;
-                            processor.ctx.terminal.dirty = true;
-
-                            if is_focused {
-                                processor.ctx.window.set_urgent(false);
-                            } else {
-                                processor.ctx.window.set_mouse_visible(true);
-                            }
-
-                            processor.on_focus_change(is_focused);
-                        }
-                    },
-                    DroppedFile(path) => {
-                        let path: String = path.to_string_lossy().into();
-                        processor.ctx.write_to_pty(path.into_bytes());
-                    },
-                    CursorLeft { .. } => {
-                        processor.ctx.mouse.inside_grid = false;
-
-                        if processor.highlighted_url.is_some() {
-                            processor.ctx.terminal.dirty = true;
-                        }
-                    },
-                    KeyboardInput { is_synthetic: true, .. }
-                    | TouchpadPressure { .. }
-                    | ScaleFactorChanged { .. }
-                    | CursorEntered { .. }
-                    | AxisMotion { .. }
-                    | HoveredFileCancelled
-                    | Destroyed
-                    | ThemeChanged(_)
-                    | HoveredFile(_)
-                    | Touch(_)
-                    | Moved(_) => (),
-                }
-            },
-            GlutinEvent::DeviceEvent { event, .. } => {
-                use glutin::event::DeviceEvent::*;
-                if let ModifiersChanged(modifiers) = event {
-                    processor.modifiers_input(modifiers);
-                }
-            },
-            GlutinEvent::Suspended { .. }
-            | GlutinEvent::NewEvents { .. }
-            | GlutinEvent::MainEventsCleared
-            | GlutinEvent::RedrawEventsCleared
-            | GlutinEvent::Resumed
-            | GlutinEvent::LoopDestroyed => (),
-        }
-    }
-
-    /// Check if an event is irrelevant and can be skipped
-    fn skip_event(event: &GlutinEvent<Event>) -> bool {
-        match event {
-            GlutinEvent::WindowEvent { event, .. } => {
-                use glutin::event::WindowEvent::*;
-                match event {
-                    KeyboardInput { is_synthetic: true, .. }
-                    | TouchpadPressure { .. }
-                    | CursorEntered { .. }
-                    | AxisMotion { .. }
-                    | HoveredFileCancelled
-                    | Destroyed
-                    | HoveredFile(_)
-                    | Touch(_)
-                    | Moved(_) => true,
-                    _ => false,
-                }
-            },
-            GlutinEvent::Suspended { .. }
-            | GlutinEvent::NewEvents { .. }
-            | GlutinEvent::MainEventsCleared
-            | GlutinEvent::LoopDestroyed => true,
-            _ => false,
-        }
-    }
-
-    // Write the ref test results to the disk
-    pub fn write_ref_test_results<T>(&self, terminal: &Term<T>) {
-        if !self.config.debug.ref_test {
-            return;
-        }
-
-        // dump grid state
-        let mut grid = terminal.grid().clone();
-        grid.initialize_all(&Cell::default());
-        grid.truncate();
-
-        let serialized_grid = json::to_string(&grid).expect("serialize grid");
-
-        let serialized_size = json::to_string(&self.display.size_info).expect("serialize size");
-
-        let serialized_config = format!("{{\"history_size\":{}}}", grid.history_size());
-
-        File::create("./grid.json")
-            .and_then(|mut f| f.write_all(serialized_grid.as_bytes()))
-            .expect("write grid.json");
-
-        File::create("./size.json")
-            .and_then(|mut f| f.write_all(serialized_size.as_bytes()))
-            .expect("write size.json");
-
-        File::create("./config.json")
-            .and_then(|mut f| f.write_all(serialized_config.as_bytes()))
-            .expect("write config.json");
-    }
-}
-
-#[derive(Debug, Clone)]
-pub struct EventProxy(EventLoopProxy<Event>);
-
-impl EventProxy {
-    pub fn new(proxy: EventLoopProxy<Event>) -> Self {
-        EventProxy(proxy)
-    }
-}
-
-impl EventListener for EventProxy {
-    fn send_event(&self, event: Event) {
-        let _ = self.0.send_event(event);
-    }
-}
diff --git a/alacritty/src/input.rs b/alacritty/src/input.rs
deleted file mode 100644
index 018140a..0000000
--- a/alacritty/src/input.rs
+++ /dev/null
@@ -1,1146 +0,0 @@
-// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//! Handle input from glutin
-//!
-//! Certain key combinations should send some escape sequence back to the pty.
-//! In order to figure that out, state about which modifier keys are pressed
-//! needs to be tracked. Additionally, we need a bit of a state machine to
-//! determine what to do when a non-modifier key is pressed.
-use std::borrow::Cow;
-use std::cmp::min;
-use std::cmp::Ordering;
-use std::marker::PhantomData;
-use std::time::Instant;
-
-use glutin::event::{
-    ElementState, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase,
-};
-use glutin::window::CursorIcon;
-use log::{debug, trace, warn};
-
-use alacritty_terminal::ansi::{ClearMode, Handler};
-use alacritty_terminal::clipboard::ClipboardType;
-use alacritty_terminal::event::EventListener;
-use alacritty_terminal::grid::Scroll;
-use alacritty_terminal::index::{Column, Line, Point, Side};
-use alacritty_terminal::message_bar::{self, Message};
-use alacritty_terminal::selection::Selection;
-use alacritty_terminal::term::mode::TermMode;
-use alacritty_terminal::term::{SizeInfo, Term};
-use alacritty_terminal::util::start_daemon;
-
-use crate::config::{Action, Binding, Config, Key};
-use crate::event::{ClickState, Mouse};
-use crate::url::{Url, Urls};
-use crate::window::Window;
-
-/// Font size change interval
-pub const FONT_SIZE_STEP: f32 = 0.5;
-
-/// Processes input from glutin.
-///
-/// An escape sequence may be emitted in case specific keys or key combinations
-/// are activated.
-pub struct Processor<'a, T: EventListener, A: ActionContext<T>> {
-    pub ctx: A,
-    pub urls: &'a Urls,
-    pub highlighted_url: &'a Option<Url>,
-    _phantom: PhantomData<T>,
-}
-
-pub trait ActionContext<T: EventListener> {
-    fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&mut self, _: B);
-    fn size_info(&self) -> SizeInfo;
-    fn copy_selection(&mut self, _: ClipboardType);
-    fn clear_selection(&mut self);
-    fn update_selection(&mut self, point: Point, side: Side);
-    fn simple_selection(&mut self, point: Point, side: Side);
-    fn block_selection(&mut self, point: Point, side: Side);
-    fn semantic_selection(&mut self, point: Point);
-    fn line_selection(&mut self, point: Point);
-    fn selection_is_empty(&self) -> bool;
-    fn mouse_mut(&mut self) -> &mut Mouse;
-    fn mouse(&self) -> &Mouse;
-    fn mouse_coords(&self) -> Option<Point>;
-    fn received_count(&mut self) -> &mut usize;
-    fn suppress_chars(&mut self) -> &mut bool;
-    fn modifiers(&mut self) -> &mut ModifiersState;
-    fn scroll(&mut self, scroll: Scroll);
-    fn window(&self) -> &Window;
-    fn window_mut(&mut self) -> &mut Window;
-    fn terminal(&self) -> &Term<T>;
-    fn terminal_mut(&mut self) -> &mut Term<T>;
-    fn spawn_new_instance(&mut self);
-    fn change_font_size(&mut self, delta: f32);
-    fn reset_font_size(&mut self);
-    fn pop_message(&mut self);
-    fn message(&self) -> Option<&Message>;
-    fn config(&self) -> &Config;
-}
-
-trait Execute<T: EventListener> {
-    fn execute<A: ActionContext<T>>(&self, ctx: &mut A);
-}
-
-impl<T, U: EventListener> Execute<U> for Binding<T> {
-    /// Execute the action associate with this binding
-    #[inline]
-    fn execute<A: ActionContext<U>>(&self, ctx: &mut A) {
-        self.action.execute(ctx)
-    }
-}
-
-impl<T: EventListener> Execute<T> for Action {
-    #[inline]
-    fn execute<A: ActionContext<T>>(&self, ctx: &mut A) {
-        match *self {
-            Action::Esc(ref s) => {
-                ctx.clear_selection();
-                ctx.scroll(Scroll::Bottom);
-                ctx.write_to_pty(s.clone().into_bytes())
-            },
-            Action::Copy => {
-                ctx.copy_selection(ClipboardType::Clipboard);
-            },
-            Action::Paste => {
-                let text = ctx.terminal_mut().clipboard().load(ClipboardType::Clipboard);
-                paste(ctx, &text);
-            },
-            Action::PasteSelection => {
-                let text = ctx.terminal_mut().clipboard().load(ClipboardType::Selection);
-                paste(ctx, &text);
-            },
-            Action::Command(ref program, ref args) => {
-                trace!("Running command {} with args {:?}", program, args);
-
-                match start_daemon(program, args) {
-                    Ok(_) => debug!("Spawned new proc"),
-                    Err(err) => warn!("Couldn't run command {}", err),
-                }
-            },
-            Action::ToggleFullscreen => ctx.window_mut().toggle_fullscreen(),
-            #[cfg(target_os = "macos")]
-            Action::ToggleSimpleFullscreen => ctx.window_mut().toggle_simple_fullscreen(),
-            Action::Hide => ctx.window().set_visible(false),
-            Action::Minimize => ctx.window().set_minimized(true),
-            Action::Quit => ctx.terminal_mut().exit(),
-            Action::IncreaseFontSize => ctx.change_font_size(FONT_SIZE_STEP),
-            Action::DecreaseFontSize => ctx.change_font_size(FONT_SIZE_STEP * -1.),
-            Action::ResetFontSize => ctx.reset_font_size(),
-            Action::ScrollPageUp => ctx.scroll(Scroll::PageUp),
-            Action::ScrollPageDown => ctx.scroll(Scroll::PageDown),
-            Action::ScrollLineUp => ctx.scroll(Scroll::Lines(1)),
-            Action::ScrollLineDown => ctx.scroll(Scroll::Lines(-1)),
-            Action::ScrollToTop => ctx.scroll(Scroll::Top),
-            Action::ScrollToBottom => ctx.scroll(Scroll::Bottom),
-            Action::ClearHistory => ctx.terminal_mut().clear_screen(ClearMode::Saved),
-            Action::ClearLogNotice => ctx.pop_message(),
-            Action::SpawnNewInstance => ctx.spawn_new_instance(),
-            Action::ReceiveChar | Action::None => (),
-        }
-    }
-}
-
-fn paste<T: EventListener, A: ActionContext<T>>(ctx: &mut A, contents: &str) {
-    if ctx.terminal().mode().contains(TermMode::BRACKETED_PASTE) {
-        ctx.write_to_pty(&b"\x1b[200~"[..]);
-        ctx.write_to_pty(contents.replace("\x1b", "").into_bytes());
-        ctx.write_to_pty(&b"\x1b[201~"[..]);
-    } else {
-        // In non-bracketed (ie: normal) mode, terminal applications cannot distinguish
-        // pasted data from keystrokes.
-        // In theory, we should construct the keystrokes needed to produce the data we are
-        // pasting... since that's neither practical nor sensible (and probably an impossible
-        // task to solve in a general way), we'll just replace line breaks (windows and unix
-        // style) with a single carriage return (\r, which is what the Enter key produces).
-        ctx.write_to_pty(contents.replace("\r\n", "\r").replace("\n", "\r").into_bytes());
-    }
-}
-
-#[derive(Debug, Clone, PartialEq)]
-pub enum MouseState {
-    Url(Url),
-    MessageBar,
-    MessageBarButton,
-    Mouse,
-    Text,
-}
-
-impl From<MouseState> for CursorIcon {
-    fn from(mouse_state: MouseState) -> CursorIcon {
-        match mouse_state {
-            MouseState::Url(_) | MouseState::MessageBarButton => CursorIcon::Hand,
-            MouseState::Text => CursorIcon::Text,
-            _ => CursorIcon::Default,
-        }
-    }
-}
-
-impl<'a, T: EventListener, A: ActionContext<T>> Processor<'a, T, A> {
-    pub fn new(ctx: A, urls: &'a Urls, highlighted_url: &'a Option<Url>) -> Self {
-        Self { ctx, urls, highlighted_url, _phantom: Default::default() }
-    }
-
-    #[inline]
-    pub fn mouse_moved(&mut self, x: usize, y: usize) {
-        let size_info = self.ctx.size_info();
-
-        self.ctx.mouse_mut().x = x;
-        self.ctx.mouse_mut().y = y;
-
-        let inside_grid = size_info.contains_point(x, y);
-        let point = size_info.pixels_to_coords(x, y);
-        let cell_side = self.get_mouse_side();
-
-        let cell_changed =
-            point.line != self.ctx.mouse().line || point.col != self.ctx.mouse().column;
-
-        // If the mouse hasn't changed cells, do nothing
-        if !cell_changed
-            && self.ctx.mouse().cell_side == cell_side
-            && self.ctx.mouse().inside_grid == inside_grid
-        {
-            return;
-        }
-
-        self.ctx.mouse_mut().inside_grid = inside_grid;
-        self.ctx.mouse_mut().cell_side = cell_side;
-        self.ctx.mouse_mut().line = point.line;
-        self.ctx.mouse_mut().column = point.col;
-
-        // Don't launch URLs if mouse has moved
-        self.ctx.mouse_mut().block_url_launcher = true;
-
-        // Update mouse state and check for URL change
-        let mouse_state = self.mouse_state();
-        self.update_url_state(&mouse_state);
-        self.ctx.window_mut().set_mouse_cursor(mouse_state.into());
-
-        let last_term_line = self.ctx.terminal().grid().num_lines() - 1;
-        if self.ctx.mouse().left_button_state == ElementState::Pressed
-            && (self.ctx.modifiers().shift()
-                || !self.ctx.terminal().mode().intersects(TermMode::MOUSE_MODE))
-        {
-            // Treat motion over message bar like motion over the last line
-            let line = min(point.line, last_term_line);
-
-            self.ctx.update_selection(Point { line, col: point.col }, cell_side);
-        } else if inside_grid
-            && cell_changed
-            && point.line <= last_term_line
-            && self.ctx.terminal().mode().intersects(TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG)
-        {
-            if self.ctx.mouse().left_button_state == ElementState::Pressed {
-                self.mouse_report(32, ElementState::Pressed);
-            } else if self.ctx.mouse().middle_button_state == ElementState::Pressed {
-                self.mouse_report(33, ElementState::Pressed);
-            } else if self.ctx.mouse().right_button_state == ElementState::Pressed {
-                self.mouse_report(34, ElementState::Pressed);
-            } else if self.ctx.terminal().mode().contains(TermMode::MOUSE_MOTION) {
-                self.mouse_report(35, ElementState::Pressed);
-            }
-        }
-    }
-
-    fn get_mouse_side(&self) -> Side {
-        let size_info = self.ctx.size_info();
-        let x = self.ctx.mouse().x;
-
-        let cell_x = x.saturating_sub(size_info.padding_x as usize) % size_info.cell_width as usize;
-        let half_cell_width = (size_info.cell_width / 2.0) as usize;
-
-        let additional_padding =
-            (size_info.width - size_info.padding_x * 2.) % size_info.cell_width;
-        let end_of_grid = size_info.width - size_info.padding_x - additional_padding;
-
-        if cell_x > half_cell_width
-            // Edge case when mouse leaves the window
-            || x as f32 >= end_of_grid
-        {
-            Side::Right
-        } else {
-            Side::Left
-        }
-    }
-
-    fn normal_mouse_report(&mut self, button: u8) {
-        let (line, column) = (self.ctx.mouse().line, self.ctx.mouse().column);
-        let utf8 = self.ctx.terminal().mode().contains(TermMode::UTF8_MOUSE);
-
-        let max_point = if utf8 { 2015 } else { 223 };
-
-        if line >= Line(max_point) || column >= Column(max_point) {
-            return;
-        }
-
-        let mut msg = vec![b'\x1b', b'[', b'M', 32 + button];
-
-        let mouse_pos_encode = |pos: usize| -> Vec<u8> {
-            let pos = 32 + 1 + pos;
-            let first = 0xC0 + pos / 64;
-            let second = 0x80 + (pos & 63);
-            vec![first as u8, second as u8]
-        };
-
-        if utf8 && column >= Column(95) {
-            msg.append(&mut mouse_pos_encode(column.0));
-        } else {
-            msg.push(32 + 1 + column.0 as u8);
-        }
-
-        if utf8 && line >= Line(95) {
-            msg.append(&mut mouse_pos_encode(line.0));
-        } else {
-            msg.push(32 + 1 + line.0 as u8);
-        }
-
-        self.ctx.write_to_pty(msg);
-    }
-
-    fn sgr_mouse_report(&mut self, button: u8, state: ElementState) {
-        let (line, column) = (self.ctx.mouse().line, self.ctx.mouse().column);
-        let c = match state {
-            ElementState::Pressed => 'M',
-            ElementState::Released => 'm',
-        };
-
-        let msg = format!("\x1b[<{};{};{}{}", button, column + 1, line + 1, c);
-        self.ctx.write_to_pty(msg.into_bytes());
-    }
-
-    fn mouse_report(&mut self, button: u8, state: ElementState) {
-        // Calculate modifiers value
-        let mut mods = 0;
-        let modifiers = self.ctx.modifiers();
-        if modifiers.shift() {
-            mods += 4;
-        }
-        if modifiers.alt() {
-            mods += 8;
-        }
-        if modifiers.ctrl() {
-            mods += 16;
-        }
-
-        // Report mouse events
-        if self.ctx.terminal().mode().contains(TermMode::SGR_MOUSE) {
-            self.sgr_mouse_report(button + mods, state);
-        } else if let ElementState::Released = state {
-            self.normal_mouse_report(3 + mods);
-        } else {
-            self.normal_mouse_report(button + mods);
-        }
-    }
-
-    fn on_mouse_double_click(&mut self, button: MouseButton, point: Point) {
-        if button == MouseButton::Left {
-            self.ctx.semantic_selection(point);
-        }
-    }
-
-    fn on_mouse_triple_click(&mut self, button: MouseButton, point: Point) {
-        if button == MouseButton::Left {
-            self.ctx.line_selection(point);
-        }
-    }
-
-    fn on_mouse_press(&mut self, button: MouseButton) {
-        let now = Instant::now();
-        let elapsed = self.ctx.mouse().last_click_timestamp.elapsed();
-        self.ctx.mouse_mut().last_click_timestamp = now;
-
-        let button_changed = self.ctx.mouse().last_button != button;
-
-        // Load mouse point, treating message bar and padding as closest cell
-        let mouse = self.ctx.mouse();
-        let mut point = self.ctx.size_info().pixels_to_coords(mouse.x, mouse.y);
-        point.line = min(point.line, self.ctx.terminal().grid().num_lines() - 1);
-
-        self.ctx.mouse_mut().click_state = match self.ctx.mouse().click_state {
-            ClickState::Click
-                if !button_changed
-                    && elapsed < self.ctx.config().ui_config.mouse.double_click.threshold =>
-            {
-                self.ctx.mouse_mut().block_url_launcher = true;
-                self.on_mouse_double_click(button, point);
-                ClickState::DoubleClick
-            }
-            ClickState::DoubleClick
-                if !button_changed
-                    && elapsed < self.ctx.config().ui_config.mouse.triple_click.threshold =>
-            {
-                self.ctx.mouse_mut().block_url_launcher = true;
-                self.on_mouse_triple_click(button, point);
-                ClickState::TripleClick
-            }
-            _ => {
-                // Don't launch URLs if this click cleared the selection
-                self.ctx.mouse_mut().block_url_launcher = !self.ctx.selection_is_empty();
-
-                self.ctx.clear_selection();
-
-                // Start new empty selection
-                let side = self.ctx.mouse().cell_side;
-                if self.ctx.modifiers().ctrl() {
-                    self.ctx.block_selection(point, side);
-                } else {
-                    self.ctx.simple_selection(point, side);
-                }
-
-                if !self.ctx.modifiers().shift()
-                    && self.ctx.terminal().mode().intersects(TermMode::MOUSE_MODE)
-                {
-                    let code = match button {
-                        MouseButton::Left => 0,
-                        MouseButton::Middle => 1,
-                        MouseButton::Right => 2,
-                        // Can't properly report more than three buttons.
-                        MouseButton::Other(_) => return,
-                    };
-                    self.mouse_report(code, ElementState::Pressed);
-                    return;
-                }
-
-                ClickState::Click
-            },
-        };
-    }
-
-    fn on_mouse_release(&mut self, button: MouseButton) {
-        if !self.ctx.modifiers().shift()
-            && self.ctx.terminal().mode().intersects(TermMode::MOUSE_MODE)
-        {
-            let code = match button {
-                MouseButton::Left => 0,
-                MouseButton::Middle => 1,
-                MouseButton::Right => 2,
-                // Can't properly report more than three buttons.
-                MouseButton::Other(_) => return,
-            };
-            self.mouse_report(code, ElementState::Released);
-            return;
-        } else if let (MouseButton::Left, MouseState::Url(url)) = (button, self.mouse_state()) {
-            self.launch_url(url);
-        }
-
-        self.copy_selection();
-    }
-
-    /// Spawn URL launcher when clicking on URLs.
-    fn launch_url(&self, url: Url) {
-        if self.ctx.mouse().block_url_launcher {
-            return;
-        }
-
-        if let Some(ref launcher) = self.ctx.config().ui_config.mouse.url.launcher {
-            let mut args = launcher.args().to_vec();
-            let start = self.ctx.terminal().visible_to_buffer(url.start());
-            let end = self.ctx.terminal().visible_to_buffer(url.end());
-            args.push(self.ctx.terminal().bounds_to_string(start, end));
-
-            match start_daemon(launcher.program(), &args) {
-                Ok(_) => debug!("Launched {} with args {:?}", launcher.program(), args),
-                Err(_) => warn!("Unable to launch {} with args {:?}", launcher.program(), args),
-            }
-        }
-    }
-
-    pub fn mouse_wheel_input(&mut self, delta: MouseScrollDelta, phase: TouchPhase) {
-        match delta {
-            MouseScrollDelta::LineDelta(_columns, lines) => {
-                let new_scroll_px = lines * self.ctx.size_info().cell_height;
-                self.scroll_terminal(new_scroll_px as i32);
-            },
-            MouseScrollDelta::PixelDelta(lpos) => {
-                match phase {
-                    TouchPhase::Started => {
-                        // Reset offset to zero
-                        self.ctx.mouse_mut().scroll_px = 0;
-                    },
-                    TouchPhase::Moved => {
-                        self.scroll_terminal(lpos.y as i32);
-                    },
-                    _ => (),
-                }
-            },
-        }
-    }
-
-    fn scroll_terminal(&mut self, new_scroll_px: i32) {
-        let height = self.ctx.size_info().cell_height as i32;
-
-        if self.ctx.terminal().mode().intersects(TermMode::MOUSE_MODE) {
-            self.ctx.mouse_mut().scroll_px += new_scroll_px;
-
-            let code = if new_scroll_px > 0 { 64 } else { 65 };
-            let lines = (self.ctx.mouse().scroll_px / height).abs();
-
-            for _ in 0..lines {
-                self.mouse_report(code, ElementState::Pressed);
-            }
-        } else if self
-            .ctx
-            .terminal()
-            .mode()
-            .contains(TermMode::ALT_SCREEN | TermMode::ALTERNATE_SCROLL)
-            && !self.ctx.modifiers().shift()
-        {
-            let multiplier = i32::from(
-                self.ctx
-                    .config()
-                    .scrolling
-                    .faux_multiplier()
-                    .unwrap_or_else(|| self.ctx.config().scrolling.multiplier()),
-            );
-            self.ctx.mouse_mut().scroll_px += new_scroll_px * multiplier;
-
-            let cmd = if new_scroll_px > 0 { b'A' } else { b'B' };
-            let lines = (self.ctx.mouse().scroll_px / height).abs();
-
-            let mut content = Vec::with_capacity(lines as usize * 3);
-            for _ in 0..lines {
-                content.push(0x1b);
-                content.push(b'O');
-                content.push(cmd);
-            }
-            self.ctx.write_to_pty(content);
-        } else {
-            let multiplier = i32::from(self.ctx.config().scrolling.multiplier());
-            self.ctx.mouse_mut().scroll_px += new_scroll_px * multiplier;
-
-            let lines = self.ctx.mouse().scroll_px / height;
-
-            self.ctx.scroll(Scroll::Lines(lines as isize));
-        }
-
-        self.ctx.mouse_mut().scroll_px %= height;
-    }
-
-    pub fn on_focus_change(&mut self, is_focused: bool) {
-        if self.ctx.terminal().mode().contains(TermMode::FOCUS_IN_OUT) {
-            let chr = if is_focused { "I" } else { "O" };
-
-            let msg = format!("\x1b[{}", chr);
-            self.ctx.write_to_pty(msg.into_bytes());
-        }
-    }
-
-    pub fn mouse_input(&mut self, state: ElementState, button: MouseButton) {
-        match button {
-            MouseButton::Left => self.ctx.mouse_mut().left_button_state = state,
-            MouseButton::Middle => self.ctx.mouse_mut().middle_button_state = state,
-            MouseButton::Right => self.ctx.mouse_mut().right_button_state = state,
-            _ => (),
-        }
-
-        // Skip normal mouse events if the message bar has been clicked
-        if self.message_close_at_cursor() && state == ElementState::Pressed {
-            self.ctx.clear_selection();
-            self.ctx.pop_message();
-
-            // Reset cursor when message bar height changed or all messages are gone
-            let size = self.ctx.size_info();
-            let mouse_mode = self.ctx.terminal().mode().intersects(TermMode::MOUSE_MODE);
-            let current_lines = (size.lines() - self.ctx.terminal().grid().num_lines()).0;
-            let new_lines = self.ctx.message().map(|m| m.text(&size).len()).unwrap_or(0);
-
-            let new_icon = match current_lines.cmp(&new_lines) {
-                Ordering::Less => CursorIcon::Default,
-                Ordering::Equal => CursorIcon::Hand,
-                Ordering::Greater => {
-                    if mouse_mode {
-                        CursorIcon::Default
-                    } else {
-                        CursorIcon::Text
-                    }
-                },
-            };
-
-            self.ctx.window_mut().set_mouse_cursor(new_icon);
-        } else {
-            match state {
-                ElementState::Pressed => {
-                    self.process_mouse_bindings(button);
-                    self.on_mouse_press(button);
-                },
-                ElementState::Released => self.on_mouse_release(button),
-            }
-        }
-
-        self.ctx.mouse_mut().last_button = button;
-    }
-
-    /// Process key input.
-    pub fn key_input(&mut self, input: KeyboardInput) {
-        match input.state {
-            ElementState::Pressed => {
-                *self.ctx.received_count() = 0;
-                self.process_key_bindings(input);
-            },
-            ElementState::Released => *self.ctx.suppress_chars() = false,
-        }
-    }
-
-    /// Modifier state change.
-    pub fn modifiers_input(&mut self, modifiers: ModifiersState) {
-        *self.ctx.modifiers() = modifiers;
-
-        // Update mouse state and check for URL change
-        let mouse_state = self.mouse_state();
-        self.update_url_state(&mouse_state);
-        self.ctx.window_mut().set_mouse_cursor(mouse_state.into());
-    }
-
-    /// Process a received character.
-    pub fn received_char(&mut self, c: char) {
-        if *self.ctx.suppress_chars() {
-            return;
-        }
-
-        self.ctx.scroll(Scroll::Bottom);
-        self.ctx.clear_selection();
-
-        let utf8_len = c.len_utf8();
-        let mut bytes = Vec::with_capacity(utf8_len);
-        unsafe {
-            bytes.set_len(utf8_len);
-            c.encode_utf8(&mut bytes[..]);
-        }
-
-        if self.ctx.config().alt_send_esc()
-            && *self.ctx.received_count() == 0
-            && self.ctx.modifiers().alt()
-            && utf8_len == 1
-        {
-            bytes.insert(0, b'\x1b');
-        }
-
-        self.ctx.write_to_pty(bytes);
-
-        *self.ctx.received_count() += 1;
-        self.ctx.terminal_mut().dirty = false;
-    }
-
-    /// Reset mouse cursor based on modifier and terminal state.
-    #[inline]
-    pub fn reset_mouse_cursor(&mut self) {
-        let mouse_state = self.mouse_state();
-        self.ctx.window_mut().set_mouse_cursor(mouse_state.into());
-    }
-
-    /// Attempt to find a binding and execute its action.
-    ///
-    /// The provided mode, mods, and key must match what is allowed by a binding
-    /// for its action to be executed.
-    fn process_key_bindings(&mut self, input: KeyboardInput) {
-        let mods = *self.ctx.modifiers();
-        let mut suppress_chars = None;
-
-        for i in 0..self.ctx.config().ui_config.key_bindings.len() {
-            let binding = &self.ctx.config().ui_config.key_bindings[i];
-
-            let key = match (binding.trigger, input.virtual_keycode) {
-                (Key::Scancode(_), _) => Key::Scancode(input.scancode),
-                (_, Some(key)) => Key::Keycode(key),
-                _ => continue,
-            };
-
-            if binding.is_triggered_by(*self.ctx.terminal().mode(), mods, &key) {
-                // Binding was triggered; run the action
-                let binding = binding.clone();
-                binding.execute(&mut self.ctx);
-
-                // Don't suppress when there has been a `ReceiveChar` action
-                *suppress_chars.get_or_insert(true) &= binding.action != Action::ReceiveChar;
-            }
-        }
-
-        // Don't suppress char if no bindings were triggered
-        *self.ctx.suppress_chars() = suppress_chars.unwrap_or(false);
-    }
-
-    /// Attempt to find a binding and execute its action.
-    ///
-    /// The provided mode, mods, and key must match what is allowed by a binding
-    /// for its action to be executed.
-    fn process_mouse_bindings(&mut self, button: MouseButton) {
-        let mods = *self.ctx.modifiers();
-        let mode = *self.ctx.terminal().mode();
-        let mouse_mode = mode.intersects(TermMode::MOUSE_MODE);
-
-        for i in 0..self.ctx.config().ui_config.mouse_bindings.len() {
-            let mut binding = self.ctx.config().ui_config.mouse_bindings[i].clone();
-
-            // Require shift for all modifiers when mouse mode is active
-            if mouse_mode {
-                binding.mods |= ModifiersState::SHIFT;
-            }
-
-            if binding.is_triggered_by(mode, mods, &button) {
-                binding.execute(&mut self.ctx);
-            }
-        }
-    }
-
-    /// Check if the cursor is hovering above the message bar.
-    fn message_at_cursor(&mut self) -> bool {
-        self.ctx.mouse().line >= self.ctx.terminal().grid().num_lines()
-    }
-
-    /// Whether the point is over the message bar's close button
-    fn message_close_at_cursor(&self) -> bool {
-        let mouse = self.ctx.mouse();
-        mouse.inside_grid
-            && mouse.column + message_bar::CLOSE_BUTTON_TEXT.len() >= self.ctx.size_info().cols()
-            && mouse.line == self.ctx.terminal().grid().num_lines()
-    }
-
-    /// Copy text selection.
-    fn copy_selection(&mut self) {
-        if self.ctx.config().selection.save_to_clipboard {
-            self.ctx.copy_selection(ClipboardType::Clipboard);
-        }
-        self.ctx.copy_selection(ClipboardType::Selection);
-    }
-
-    /// Trigger redraw when URL highlight changed.
-    #[inline]
-    fn update_url_state(&mut self, mouse_state: &MouseState) {
-        if let MouseState::Url(url) = mouse_state {
-            if Some(url) != self.highlighted_url.as_ref() {
-                self.ctx.terminal_mut().dirty = true;
-            }
-        } else if self.highlighted_url.is_some() {
-            self.ctx.terminal_mut().dirty = true;
-        }
-    }
-
-    /// Location of the mouse cursor.
-    fn mouse_state(&mut self) -> MouseState {
-        // Check message bar before URL to ignore URLs in the message bar
-        if self.message_close_at_cursor() {
-            return MouseState::MessageBarButton;
-        } else if self.message_at_cursor() {
-            return MouseState::MessageBar;
-        }
-
-        // Check for URL at mouse cursor
-        let mods = *self.ctx.modifiers();
-        let selection =
-            !self.ctx.terminal().selection().as_ref().map(Selection::is_empty).unwrap_or(true);
-        let mouse_mode = self.ctx.terminal().mode().intersects(TermMode::MOUSE_MODE);
-        let highlighted_url =
-            self.urls.highlighted(self.ctx.config(), self.ctx.mouse(), mods, mouse_mode, selection);
-
-        if let Some(url) = highlighted_url {
-            return MouseState::Url(url);
-        }
-
-        // Check mouse mode if location is not special
-        if self.ctx.terminal().mode().intersects(TermMode::MOUSE_MODE)
-            && !self.ctx.modifiers().shift()
-        {
-            MouseState::Mouse
-        } else {
-            MouseState::Text
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use std::borrow::Cow;
-    use std::time::Duration;
-
-    use glutin::event::{
-        ElementState, Event, ModifiersState, MouseButton, VirtualKeyCode, WindowEvent,
-    };
-
-    use alacritty_terminal::clipboard::{Clipboard, ClipboardType};
-    use alacritty_terminal::event::{Event as TerminalEvent, EventListener};
-    use alacritty_terminal::grid::Scroll;
-    use alacritty_terminal::index::{Point, Side};
-    use alacritty_terminal::message_bar::{Message, MessageBuffer};
-    use alacritty_terminal::selection::Selection;
-    use alacritty_terminal::term::{SizeInfo, Term, TermMode};
-
-    use crate::config::{ClickHandler, Config};
-    use crate::event::{ClickState, Mouse};
-    use crate::url::Urls;
-    use crate::window::Window;
-
-    use super::{Action, Binding, Processor};
-
-    const KEY: VirtualKeyCode = VirtualKeyCode::Key0;
-
-    struct MockEventProxy;
-
-    impl EventListener for MockEventProxy {
-        fn send_event(&self, _event: TerminalEvent) {}
-    }
-
-    #[derive(PartialEq)]
-    enum MultiClick {
-        DoubleClick,
-        TripleClick,
-        None,
-    }
-
-    struct ActionContext<'a, T> {
-        pub terminal: &'a mut Term<T>,
-        pub selection: &'a mut Option<Selection>,
-        pub size_info: &'a SizeInfo,
-        pub mouse: &'a mut Mouse,
-        pub message_buffer: &'a mut MessageBuffer,
-        pub last_action: MultiClick,
-        pub received_count: usize,
-        pub suppress_chars: bool,
-        pub modifiers: ModifiersState,
-        config: &'a Config,
-    }
-
-    impl<'a, T: EventListener> super::ActionContext<T> for ActionContext<'a, T> {
-        fn write_to_pty<B: Into<Cow<'static, [u8]>>>(&mut self, _val: B) {}
-
-        fn update_selection(&mut self, _point: Point, _side: Side) {}
-
-        fn simple_selection(&mut self, _point: Point, _side: Side) {}
-
-        fn block_selection(&mut self, _point: Point, _side: Side) {}
-
-        fn copy_selection(&mut self, _: ClipboardType) {}
-
-        fn clear_selection(&mut self) {}
-
-        fn spawn_new_instance(&mut self) {}
-
-        fn change_font_size(&mut self, _delta: f32) {}
-
-        fn reset_font_size(&mut self) {}
-
-        fn terminal(&self) -> &Term<T> {
-            &self.terminal
-        }
-
-        fn terminal_mut(&mut self) -> &mut Term<T> {
-            &mut self.terminal
-        }
-
-        fn size_info(&self) -> SizeInfo {
-            *self.size_info
-        }
-
-        fn semantic_selection(&mut self, _point: Point) {
-            // set something that we can check for here
-            self.last_action = MultiClick::DoubleClick;
-        }
-
-        fn line_selection(&mut self, _point: Point) {
-            self.last_action = MultiClick::TripleClick;
-        }
-
-        fn selection_is_empty(&self) -> bool {
-            true
-        }
-
-        fn scroll(&mut self, scroll: Scroll) {
-            self.terminal.scroll_display(scroll);
-        }
-
-        fn mouse_coords(&self) -> Option<Point> {
-            let x = self.mouse.x as usize;
-            let y = self.mouse.y as usize;
-
-            if self.size_info.contains_point(x, y) {
-                Some(self.size_info.pixels_to_coords(x, y))
-            } else {
-                None
-            }
-        }
-
-        #[inline]
-        fn mouse_mut(&mut self) -> &mut Mouse {
-            self.mouse
-        }
-
-        #[inline]
-        fn mouse(&self) -> &Mouse {
-            self.mouse
-        }
-
-        fn received_count(&mut self) -> &mut usize {
-            &mut self.received_count
-        }
-
-        fn suppress_chars(&mut self) -> &mut bool {
-            &mut self.suppress_chars
-        }
-
-        fn modifiers(&mut self) -> &mut ModifiersState {
-            &mut self.modifiers
-        }
-
-        fn window(&self) -> &Window {
-            unimplemented!();
-        }
-
-        fn window_mut(&mut self) -> &mut Window {
-            unimplemented!();
-        }
-
-        fn pop_message(&mut self) {
-            self.message_buffer.pop();
-        }
-
-        fn message(&self) -> Option<&Message> {
-            self.message_buffer.message()
-        }
-
-        fn config(&self) -> &Config {
-            self.config
-        }
-    }
-
-    macro_rules! test_clickstate {
-        {
-            name: $name:ident,
-            initial_state: $initial_state:expr,
-            initial_button: $initial_button:expr,
-            input: $input:expr,
-            end_state: $end_state:pat,
-            last_action: $last_action:expr
-        } => {
-            #[test]
-            fn $name() {
-                let mut cfg = Config::default();
-                cfg.ui_config.mouse = crate::config::Mouse {
-                    double_click: ClickHandler {
-                        threshold: Duration::from_millis(1000),
-                    },
-                    triple_click: ClickHandler {
-                        threshold: Duration::from_millis(1000),
-                    },
-                    hide_when_typing: false,
-                    url: Default::default(),
-                };
-
-                let size = SizeInfo {
-                    width: 21.0,
-                    height: 51.0,
-                    cell_width: 3.0,
-                    cell_height: 3.0,
-                    padding_x: 0.0,
-                    padding_y: 0.0,
-                    dpr: 1.0,
-                };
-
-                let mut terminal = Term::new(&cfg, &size, Clipboard::new_nop(), MockEventProxy);
-
-                let mut mouse = Mouse::default();
-                mouse.click_state = $initial_state;
-                mouse.last_button = $initial_button;
-
-                let mut selection = None;
-
-                let mut message_buffer = MessageBuffer::new();
-
-                let context = ActionContext {
-                    terminal: &mut terminal,
-                    selection: &mut selection,
-                    mouse: &mut mouse,
-                    size_info: &size,
-                    last_action: MultiClick::None,
-                    received_count: 0,
-                    suppress_chars: false,
-                    modifiers: Default::default(),
-                    message_buffer: &mut message_buffer,
-                    config: &cfg,
-                };
-
-                let urls = Urls::new();
-                let mut processor = Processor::new(context, &urls, &None);
-
-                let event: Event::<'_, TerminalEvent> = $input;
-                if let Event::WindowEvent {
-                    event: WindowEvent::MouseInput {
-                        state,
-                        button,
-                        ..
-                    },
-                    ..
-                } = event
-                {
-                    processor.mouse_input(state, button);
-                };
-
-                assert!(match processor.ctx.mouse.click_state {
-                    $end_state => processor.ctx.last_action == $last_action,
-                    _ => false
-                });
-            }
-        }
-    }
-
-    macro_rules! test_process_binding {
-        {
-            name: $name:ident,
-            binding: $binding:expr,
-            triggers: $triggers:expr,
-            mode: $mode:expr,
-            mods: $mods:expr,
-        } => {
-            #[test]
-            fn $name() {
-                if $triggers {
-                    assert!($binding.is_triggered_by($mode, $mods, &KEY));
-                } else {
-                    assert!(!$binding.is_triggered_by($mode, $mods, &KEY));
-                }
-            }
-        }
-    }
-
-    test_clickstate! {
-        name: single_click,
-        initial_state: ClickState::None,
-        initial_button: MouseButton::Other(0),
-        input: Event::WindowEvent {
-            event: WindowEvent::MouseInput {
-                state: ElementState::Pressed,
-                button: MouseButton::Left,
-                device_id: unsafe { ::std::mem::transmute_copy(&0) },
-                modifiers: ModifiersState::default(),
-            },
-            window_id: unsafe { ::std::mem::transmute_copy(&0) },
-        },
-        end_state: ClickState::Click,
-        last_action: MultiClick::None
-    }
-
-    test_clickstate! {
-        name: double_click,
-        initial_state: ClickState::Click,
-        initial_button: MouseButton::Left,
-        input: Event::WindowEvent {
-            event: WindowEvent::MouseInput {
-                state: ElementState::Pressed,
-                button: MouseButton::Left,
-                device_id: unsafe { ::std::mem::transmute_copy(&0) },
-                modifiers: ModifiersState::default(),
-            },
-            window_id: unsafe { ::std::mem::transmute_copy(&0) },
-        },
-        end_state: ClickState::DoubleClick,
-        last_action: MultiClick::DoubleClick
-    }
-
-    test_clickstate! {
-        name: triple_click,
-        initial_state: ClickState::DoubleClick,
-        initial_button: MouseButton::Left,
-        input: Event::WindowEvent {
-            event: WindowEvent::MouseInput {
-                state: ElementState::Pressed,
-                button: MouseButton::Left,
-                device_id: unsafe { ::std::mem::transmute_copy(&0) },
-                modifiers: ModifiersState::default(),
-            },
-            window_id: unsafe { ::std::mem::transmute_copy(&0) },
-        },
-        end_state: ClickState::TripleClick,
-        last_action: MultiClick::TripleClick
-    }
-
-    test_clickstate! {
-        name: multi_click_separate_buttons,
-        initial_state: ClickState::DoubleClick,
-        initial_button: MouseButton::Left,
-        input: Event::WindowEvent {
-            event: WindowEvent::MouseInput {
-                state: ElementState::Pressed,
-                button: MouseButton::Right,
-                device_id: unsafe { ::std::mem::transmute_copy(&0) },
-                modifiers: ModifiersState::default(),
-            },
-            window_id: unsafe { ::std::mem::transmute_copy(&0) },
-        },
-        end_state: ClickState::Click,
-        last_action: MultiClick::None
-    }
-
-    test_process_binding! {
-        name: process_binding_nomode_shiftmod_require_shift,
-        binding: Binding { trigger: KEY, mods: ModifiersState::SHIFT, action: Action::from("\x1b[1;2D"), mode: TermMode::NONE, notmode: TermMode::NONE },
-        triggers: true,
-        mode: TermMode::NONE,
-        mods: ModifiersState::SHIFT,
-    }
-
-    test_process_binding! {
-        name: process_binding_nomode_nomod_require_shift,
-        binding: Binding { trigger: KEY, mods: ModifiersState::SHIFT, action: Action::from("\x1b[1;2D"), mode: TermMode::NONE, notmode: TermMode::NONE },
-        triggers: false,
-        mode: TermMode::NONE,
-        mods: ModifiersState::empty(),
-    }
-
-    test_process_binding! {
-        name: process_binding_nomode_controlmod,
-        binding: Binding { trigger: KEY, mods: ModifiersState::CTRL, action: Action::from("\x1b[1;5D"), mode: TermMode::NONE, notmode: TermMode::NONE },
-        triggers: true,
-        mode: TermMode::NONE,
-        mods: ModifiersState::CTRL,
-    }
-
-    test_process_binding! {
-        name: process_binding_nomode_nomod_require_not_appcursor,
-        binding: Binding { trigger: KEY, mods: ModifiersState::empty(), action: Action::from("\x1b[D"), mode: TermMode::NONE, notmode: TermMode::APP_CURSOR },
-        triggers: true,
-        mode: TermMode::NONE,
-        mods: ModifiersState::empty(),
-    }
-
-    test_process_binding! {
-        name: process_binding_appcursormode_nomod_require_appcursor,
-        binding: Binding { trigger: KEY, mods: ModifiersState::empty(), action: Action::from("\x1bOD"), mode: TermMode::APP_CURSOR, notmode: TermMode::NONE },
-        triggers: true,
-        mode: TermMode::APP_CURSOR,
-        mods: ModifiersState::empty(),
-    }
-
-    test_process_binding! {
-        name: process_binding_nomode_nomod_require_appcursor,
-        binding: Binding { trigger: KEY, mods: ModifiersState::empty(), action: Action::from("\x1bOD"), mode: TermMode::APP_CURSOR, notmode: TermMode::NONE },
-        triggers: false,
-        mode: TermMode::NONE,
-        mods: ModifiersState::empty(),
-    }
-
-    test_process_binding! {
-        name: process_binding_appcursormode_appkeypadmode_nomod_require_appcursor,
-        binding: Binding { trigger: KEY, mods: ModifiersState::empty(), action: Action::from("\x1bOD"), mode: TermMode::APP_CURSOR, notmode: TermMode::NONE },
-        triggers: true,
-        mode: TermMode::APP_CURSOR | TermMode::APP_KEYPAD,
-        mods: ModifiersState::empty(),
-    }
-
-    test_process_binding! {
-        name: process_binding_fail_with_extra_mods,
-        binding: Binding { trigger: KEY, mods: ModifiersState::LOGO, action: Action::from("arst"), mode: TermMode::NONE, notmode: TermMode::NONE },
-        triggers: false,
-        mode: TermMode::NONE,
-        mods: ModifiersState::ALT | ModifiersState::LOGO,
-    }
-}
diff --git a/alacritty/src/logging.rs b/alacritty/src/logging.rs
deleted file mode 100644
index 9d837a7..0000000
--- a/alacritty/src/logging.rs
+++ /dev/null
@@ -1,201 +0,0 @@
-// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//! Logging for alacritty.
-//!
-//! The main executable is supposed to call `initialize()` exactly once during
-//! startup. All logging messages are written to stdout, given that their
-//! log-level is sufficient for the level configured in `cli::Options`.
-use std::env;
-use std::fs::{File, OpenOptions};
-use std::io::{self, LineWriter, Stdout, Write};
-use std::path::PathBuf;
-use std::process;
-use std::sync::atomic::{AtomicBool, Ordering};
-use std::sync::{Arc, Mutex};
-
-use glutin::event_loop::EventLoopProxy;
-use log::{self, Level};
-use time;
-
-use alacritty_terminal::event::Event;
-use alacritty_terminal::message_bar::Message;
-use alacritty_terminal::term::color;
-
-use crate::cli::Options;
-
-const ALACRITTY_LOG_ENV: &str = "ALACRITTY_LOG";
-
-pub fn initialize(
-    options: &Options,
-    event_proxy: EventLoopProxy<Event>,
-) -> Result<Option<PathBuf>, log::SetLoggerError> {
-    log::set_max_level(options.log_level);
-
-    // Use env_logger if RUST_LOG environment variable is defined. Otherwise,
-    // use the alacritty-only logger.
-    if std::env::var("RUST_LOG").is_ok() {
-        env_logger::try_init()?;
-        Ok(None)
-    } else {
-        let logger = Logger::new(event_proxy);
-        let path = logger.file_path();
-        log::set_boxed_logger(Box::new(logger))?;
-        Ok(path)
-    }
-}
-
-pub struct Logger {
-    logfile: Mutex<OnDemandLogFile>,
-    stdout: Mutex<LineWriter<Stdout>>,
-    event_proxy: Mutex<EventLoopProxy<Event>>,
-}
-
-impl Logger {
-    fn new(event_proxy: EventLoopProxy<Event>) -> Self {
-        let logfile = Mutex::new(OnDemandLogFile::new());
-        let stdout = Mutex::new(LineWriter::new(io::stdout()));
-
-        Logger { logfile, stdout, event_proxy: Mutex::new(event_proxy) }
-    }
-
-    fn file_path(&self) -> Option<PathBuf> {
-        if let Ok(logfile) = self.logfile.lock() {
-            Some(logfile.path().clone())
-        } else {
-            None
-        }
-    }
-}
-
-impl log::Log for Logger {
-    fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
-        metadata.level() <= log::max_level()
-    }
-
-    fn log(&self, record: &log::Record<'_>) {
-        if self.enabled(record.metadata()) && record.target().starts_with("alacritty") {
-            let now = time::strftime("%F %R", &time::now()).unwrap();
-
-            let msg = if record.level() >= Level::Trace {
-                format!(
-                    "[{}] [{}] [{}:{}] {}\n",
-                    now,
-                    record.level(),
-                    record.file().unwrap_or("?"),
-                    record.line().map(|l| l.to_string()).unwrap_or_else(|| "?".into()),
-                    record.args()
-                )
-            } else {
-                format!("[{}] [{}] {}\n", now, record.level(), record.args())
-            };
-
-            if let Ok(ref mut logfile) = self.logfile.lock() {
-                let _ = logfile.write_all(msg.as_ref());
-
-                if record.level() <= Level::Warn {
-                    #[cfg(not(windows))]
-                    let env_var = format!("${}", ALACRITTY_LOG_ENV);
-                    #[cfg(windows)]
-                    let env_var = format!("%{}%", ALACRITTY_LOG_ENV);
-
-                    let msg = format!(
-                        "[{}] See log at {} ({}):\n{}",
-                        record.level(),
-                        logfile.path.to_string_lossy(),
-                        env_var,
-                        record.args(),
-                    );
-                    let color = match record.level() {
-                        Level::Error => color::RED,
-                        Level::Warn => color::YELLOW,
-                        _ => unreachable!(),
-                    };
-
-                    if let Ok(event_proxy) = self.event_proxy.lock() {
-                        let mut message = Message::new(msg, color);
-                        message.set_target(record.target().to_owned());
-
-                        let _ = event_proxy.send_event(Event::Message(message));
-                    }
-                }
-            }
-
-            if let Ok(ref mut stdout) = self.stdout.lock() {
-                let _ = stdout.write_all(msg.as_ref());
-            }
-        }
-    }
-
-    fn flush(&self) {}
-}
-
-struct OnDemandLogFile {
-    file: Option<LineWriter<File>>,
-    created: Arc<AtomicBool>,
-    path: PathBuf,
-}
-
-impl OnDemandLogFile {
-    fn new() -> Self {
-        let mut path = env::temp_dir();
-        path.push(format!("Alacritty-{}.log", process::id()));
-
-        // Set log path as an environment variable
-        env::set_var(ALACRITTY_LOG_ENV, path.as_os_str());
-
-        OnDemandLogFile { path, file: None, created: Arc::new(AtomicBool::new(false)) }
-    }
-
-    fn file(&mut self) -> Result<&mut LineWriter<File>, io::Error> {
-        // Allow to recreate the file if it has been deleted at runtime
-        if self.file.is_some() && !self.path.as_path().exists() {
-            self.file = None;
-        }
-
-        // Create the file if it doesn't exist yet
-        if self.file.is_none() {
-            let file = OpenOptions::new().append(true).create(true).open(&self.path);
-
-            match file {
-                Ok(file) => {
-                    self.file = Some(io::LineWriter::new(file));
-                    self.created.store(true, Ordering::Relaxed);
-                    let _ =
-                        writeln!(io::stdout(), "Created log file at \"{}\"", self.path.display());
-                },
-                Err(e) => {
-                    let _ = writeln!(io::stdout(), "Unable to create log file: {}", e);
-                    return Err(e);
-                },
-            }
-        }
-
-        Ok(self.file.as_mut().unwrap())
-    }
-
-    fn path(&self) -> &PathBuf {
-        &self.path
-    }
-}
-
-impl Write for OnDemandLogFile {
-    fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
-        self.file()?.write(buf)
-    }
-
-    fn flush(&mut self) -> Result<(), io::Error> {
-        self.file()?.flush()
-    }
-}
diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs
deleted file mode 100644
index db08245..0000000
--- a/alacritty/src/main.rs
+++ /dev/null
@@ -1,237 +0,0 @@
-// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//! Alacritty - The GPU Enhanced Terminal
-#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)]
-#![cfg_attr(feature = "nightly", feature(core_intrinsics))]
-#![cfg_attr(all(test, feature = "bench"), feature(test))]
-// With the default subsystem, 'console', windows creates an additional console
-// window for the program.
-// This is silently ignored on non-windows systems.
-// See https://msdn.microsoft.com/en-us/library/4cc7ya5b.aspx for more details.
-#![windows_subsystem = "windows"]
-
-#[cfg(target_os = "macos")]
-use std::env;
-use std::error::Error;
-use std::fs;
-use std::io::{self, Write};
-use std::sync::Arc;
-
-#[cfg(target_os = "macos")]
-use dirs;
-use glutin::event_loop::EventLoop as GlutinEventLoop;
-use log::info;
-#[cfg(windows)]
-use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS};
-
-use alacritty_terminal::clipboard::Clipboard;
-use alacritty_terminal::event::Event;
-use alacritty_terminal::event_loop::{self, EventLoop, Msg};
-#[cfg(target_os = "macos")]
-use alacritty_terminal::locale;
-use alacritty_terminal::message_bar::MessageBuffer;
-use alacritty_terminal::panic;
-use alacritty_terminal::sync::FairMutex;
-use alacritty_terminal::term::Term;
-use alacritty_terminal::tty;
-
-mod cli;
-mod config;
-mod cursor;
-mod display;
-mod event;
-mod input;
-mod logging;
-mod renderer;
-mod url;
-mod window;
-
-mod gl {
-    #![allow(clippy::all)]
-    include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
-}
-
-use crate::cli::Options;
-use crate::config::monitor::Monitor;
-use crate::config::Config;
-use crate::display::Display;
-use crate::event::{EventProxy, Processor};
-
-fn main() {
-    panic::attach_handler();
-
-    // When linked with the windows subsystem windows won't automatically attach
-    // to the console of the parent process, so we do it explicitly. This fails
-    // silently if the parent has no console.
-    #[cfg(windows)]
-    unsafe {
-        AttachConsole(ATTACH_PARENT_PROCESS);
-    }
-
-    // Load command line options
-    let options = Options::new();
-
-    // Setup glutin event loop
-    let window_event_loop = GlutinEventLoop::<Event>::with_user_event();
-
-    // Initialize the logger as soon as possible as to capture output from other subsystems
-    let log_file = logging::initialize(&options, window_event_loop.create_proxy())
-        .expect("Unable to initialize logger");
-
-    // Load configuration file
-    let config_path = options.config_path().or_else(config::installed_config);
-    let config = config_path.map(config::load_from).unwrap_or_else(Config::default);
-    let config = options.into_config(config);
-
-    // Update the log level from config
-    log::set_max_level(config.debug.log_level);
-
-    // Switch to home directory
-    #[cfg(target_os = "macos")]
-    env::set_current_dir(dirs::home_dir().unwrap()).unwrap();
-    // Set locale
-    #[cfg(target_os = "macos")]
-    locale::set_locale_environment();
-
-    // Store if log file should be deleted before moving config
-    let persistent_logging = config.persistent_logging();
-
-    // Run alacritty
-    if let Err(err) = run(window_event_loop, config) {
-        println!("Alacritty encountered an unrecoverable error:\n\n\t{}\n", err);
-        std::process::exit(1);
-    }
-
-    // Clean up logfile
-    if let Some(log_file) = log_file {
-        if !persistent_logging && fs::remove_file(&log_file).is_ok() {
-            let _ = writeln!(io::stdout(), "Deleted log file at \"{}\"", log_file.display());
-        }
-    }
-}
-
-/// Run Alacritty
-///
-/// Creates a window, the terminal state, pty, I/O event loop, input processor,
-/// config change monitor, and runs the main display loop.
-fn run(window_event_loop: GlutinEventLoop<Event>, config: Config) -> Result<(), Box<dyn Error>> {
-    info!("Welcome to Alacritty");
-
-    match &config.config_path {
-        Some(config_path) => info!("Configuration loaded from \"{}\"", config_path.display()),
-        None => info!("No configuration file found"),
-    }
-
-    // Set environment variables
-    tty::setup_env(&config);
-
-    let event_proxy = EventProxy::new(window_event_loop.create_proxy());
-
-    // Create a display
-    //
-    // The display manages a window and can draw the terminal.
-    let display = Display::new(&config, &window_event_loop)?;
-
-    info!("PTY Dimensions: {:?} x {:?}", display.size_info.lines(), display.size_info.cols());
-
-    // Create new native clipboard
-    #[cfg(not(any(target_os = "macos", windows)))]
-    let clipboard = Clipboard::new(display.window.wayland_display());
-    #[cfg(any(target_os = "macos", windows))]
-    let clipboard = Clipboard::new();
-
-    // Create the terminal
-    //
-    // This object contains all of the state about what's being displayed. It's
-    // wrapped in a clonable mutex since both the I/O loop and display need to
-    // access it.
-    let terminal = Term::new(&config, &display.size_info, clipboard, event_proxy.clone());
-    let terminal = Arc::new(FairMutex::new(terminal));
-
-    // Create the pty
-    //
-    // The pty forks a process to run the shell on the slave side of the
-    // pseudoterminal. A file descriptor for the master side is retained for
-    // reading/writing to the shell.
-    #[cfg(not(any(target_os = "macos", windows)))]
-    let pty = tty::new(&config, &display.size_info, display.window.x11_window_id());
-    #[cfg(any(target_os = "macos", windows))]
-    let pty = tty::new(&config, &display.size_info, None);
-
-    // Create the pseudoterminal I/O loop
-    //
-    // pty I/O is ran on another thread as to not occupy cycles used by the
-    // renderer and input processing. Note that access to the terminal state is
-    // synchronized since the I/O loop updates the state, and the display
-    // consumes it periodically.
-    let event_loop = EventLoop::new(Arc::clone(&terminal), event_proxy.clone(), pty, &config);
-
-    // The event loop channel allows write requests from the event processor
-    // to be sent to the pty loop and ultimately written to the pty.
-    let loop_tx = event_loop.channel();
-
-    // Create a config monitor when config was loaded from path
-    //
-    // The monitor watches the config file for changes and reloads it. Pending
-    // config changes are processed in the main loop.
-    if config.live_config_reload() {
-        config.config_path.as_ref().map(|path| Monitor::new(path, event_proxy.clone()));
-    }
-
-    // Setup storage for message UI
-    let message_buffer = MessageBuffer::new();
-
-    // Event processor
-    let mut processor =
-        Processor::new(event_loop::Notifier(loop_tx.clone()), message_buffer, config, display);
-
-    // Kick off the I/O thread
-    let io_thread = event_loop.spawn();
-
-    info!("Initialisation complete");
-
-    // Start event loop and block until shutdown
-    processor.run(terminal, window_event_loop);
-
-    // This explicit drop is needed for Windows, ConPTY backend. Otherwise a deadlock can occur.
-    // The cause:
-    //   - Drop for Conpty will deadlock if the conout pipe has already been dropped.
-    //   - The conout pipe is dropped when the io_thread is joined below (io_thread owns pty).
-    //   - Conpty is dropped when the last of processor and io_thread are dropped, because both of
-    //     them own an Arc<Conpty>.
-    //
-    // The fix is to ensure that processor is dropped first. That way, when io_thread (i.e. pty)
-    // is dropped, it can ensure Conpty is dropped before the conout pipe in the pty drop order.
-    //
-    // FIXME: Change PTY API to enforce the correct drop order with the typesystem.
-    drop(processor);
-
-    // Shutdown PTY parser event loop
-    loop_tx.send(Msg::Shutdown).expect("Error sending shutdown to pty event loop");
-    io_thread.join().expect("join io thread");
-
-    // FIXME patch notify library to have a shutdown method
-    // config_reloader.join().ok();
-
-    // Without explicitly detaching the console cmd won't redraw it's prompt
-    #[cfg(windows)]
-    unsafe {
-        FreeConsole();
-    }
-
-    info!("Goodbye");
-
-    Ok(())
-}
diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs
deleted file mode 100644
index a099b0d..0000000
--- a/alacritty/src/renderer/mod.rs
+++ /dev/null
@@ -1,1654 +0,0 @@
-// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-use std::collections::HashMap;
-use std::fs::File;
-use std::hash::BuildHasherDefault;
-use std::io::{self, Read};
-use std::mem::size_of;
-use std::path::PathBuf;
-use std::ptr;
-use std::sync::mpsc;
-use std::time::Duration;
-
-use fnv::FnvHasher;
-use font::{
-    self, BitmapBuffer, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer,
-};
-use log::{error, info};
-use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
-
-use crate::cursor;
-use crate::gl;
-use crate::gl::types::*;
-use crate::renderer::rects::RenderRect;
-use alacritty_terminal::config::{self, Config, Delta, Font, StartupMode};
-use alacritty_terminal::index::{Column, Line};
-use alacritty_terminal::term::cell::{self, Flags};
-use alacritty_terminal::term::color::Rgb;
-use alacritty_terminal::term::{self, CursorKey, RenderableCell, RenderableCellContent, SizeInfo};
-use alacritty_terminal::util;
-use std::fmt::{self, Display, Formatter};
-
-pub mod rects;
-
-// Shader paths for live reload
-static TEXT_SHADER_F_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../res/text.f.glsl");
-static TEXT_SHADER_V_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../res/text.v.glsl");
-static RECT_SHADER_F_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../res/rect.f.glsl");
-static RECT_SHADER_V_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../res/rect.v.glsl");
-
-// Shader source which is used when live-shader-reload feature is disable
-static TEXT_SHADER_F: &str =
-    include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../res/text.f.glsl"));
-static TEXT_SHADER_V: &str =
-    include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../res/text.v.glsl"));
-static RECT_SHADER_F: &str =
-    include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../res/rect.f.glsl"));
-static RECT_SHADER_V: &str =
-    include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../res/rect.v.glsl"));
-
-/// `LoadGlyph` allows for copying a rasterized glyph into graphics memory
-pub trait LoadGlyph {
-    /// Load the rasterized glyph into GPU memory
-    fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph;
-
-    /// Clear any state accumulated from previous loaded glyphs
-    ///
-    /// This can, for instance, be used to reset the texture Atlas.
-    fn clear(&mut self);
-}
-
-enum Msg {
-    ShaderReload,
-}
-
-#[derive(Debug)]
-pub enum Error {
-    ShaderCreation(ShaderCreationError),
-}
-
-impl std::error::Error for Error {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        match self {
-            Error::ShaderCreation(err) => err.source(),
-        }
-    }
-}
-
-impl Display for Error {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        match self {
-            Error::ShaderCreation(err) => {
-                write!(f, "There was an error initializing the shaders: {}", err)
-            },
-        }
-    }
-}
-
-impl From<ShaderCreationError> for Error {
-    fn from(val: ShaderCreationError) -> Self {
-        Error::ShaderCreation(val)
-    }
-}
-
-/// Text drawing program
-///
-/// Uniforms are prefixed with "u", and vertex attributes are prefixed with "a".
-#[derive(Debug)]
-pub struct TextShaderProgram {
-    // Program id
-    id: GLuint,
-
-    /// projection scale and offset uniform
-    u_projection: GLint,
-
-    /// Cell dimensions (pixels)
-    u_cell_dim: GLint,
-
-    /// Background pass flag
-    ///
-    /// Rendering is split into two passes; 1 for backgrounds, and one for text
-    u_background: GLint,
-}
-
-/// Rectangle drawing program
-///
-/// Uniforms are prefixed with "u"
-#[derive(Debug)]
-pub struct RectShaderProgram {
-    // Program id
-    id: GLuint,
-    /// Rectangle color
-    u_color: GLint,
-}
-
-#[derive(Copy, Debug, Clone)]
-pub struct Glyph {
-    tex_id: GLuint,
-    colored: bool,
-    top: f32,
-    left: f32,
-    width: f32,
-    height: f32,
-    uv_bot: f32,
-    uv_left: f32,
-    uv_width: f32,
-    uv_height: f32,
-}
-
-/// Naïve glyph cache
-///
-/// Currently only keyed by `char`, and thus not possible to hold different
-/// representations of the same code point.
-pub struct GlyphCache {
-    /// Cache of buffered glyphs
-    cache: HashMap<GlyphKey, Glyph, BuildHasherDefault<FnvHasher>>,
-
-    /// Cache of buffered cursor glyphs
-    cursor_cache: HashMap<CursorKey, Glyph, BuildHasherDefault<FnvHasher>>,
-
-    /// Rasterizer for loading new glyphs
-    rasterizer: Rasterizer,
-
-    /// regular font
-    font_key: FontKey,
-
-    /// bold font
-    bold_key: FontKey,
-
-    /// italic font
-    italic_key: FontKey,
-
-    /// bold italic font
-    bold_italic_key: FontKey,
-
-    /// font size
-    font_size: font::Size,
-
-    /// glyph offset
-    glyph_offset: Delta<i8>,
-
-    metrics: font::Metrics,
-}
-
-impl GlyphCache {
-    pub fn new<L>(
-        mut rasterizer: Rasterizer,
-        font: &config::Font,
-        loader: &mut L,
-    ) -> Result<GlyphCache, font::Error>
-    where
-        L: LoadGlyph,
-    {
-        let (regular, bold, italic, bold_italic) = Self::compute_font_keys(font, &mut rasterizer)?;
-
-        // Need to load at least one glyph for the face before calling metrics.
-        // The glyph requested here ('m' at the time of writing) has no special
-        // meaning.
-        rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?;
-
-        let metrics = rasterizer.metrics(regular, font.size)?;
-
-        let mut cache = Self {
-            cache: HashMap::default(),
-            cursor_cache: HashMap::default(),
-            rasterizer,
-            font_size: font.size,
-            font_key: regular,
-            bold_key: bold,
-            italic_key: italic,
-            bold_italic_key: bold_italic,
-            glyph_offset: font.glyph_offset,
-            metrics,
-        };
-
-        cache.load_glyphs_for_font(regular, loader);
-        cache.load_glyphs_for_font(bold, loader);
-        cache.load_glyphs_for_font(italic, loader);
-        cache.load_glyphs_for_font(bold_italic, loader);
-
-        Ok(cache)
-    }
-
-    fn load_glyphs_for_font<L: LoadGlyph>(&mut self, font: FontKey, loader: &mut L) {
-        let size = self.font_size;
-        for i in 32u8..=126u8 {
-            self.get(GlyphKey { font_key: font, c: i as char, size }, loader);
-        }
-    }
-
-    /// Computes font keys for (Regular, Bold, Italic, Bold Italic)
-    fn compute_font_keys(
-        font: &config::Font,
-        rasterizer: &mut Rasterizer,
-    ) -> Result<(FontKey, FontKey, FontKey, FontKey), font::Error> {
-        let size = font.size;
-
-        // Load regular font
-        let regular_desc =
-            Self::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal);
-
-        let regular = rasterizer.load_font(&regular_desc, size)?;
-
-        // helper to load a description if it is not the regular_desc
-        let mut load_or_regular = |desc: FontDesc| {
-            if desc == regular_desc {
-                regular
-            } else {
-                rasterizer.load_font(&desc, size).unwrap_or_else(|_| regular)
-            }
-        };
-
-        // Load bold font
-        let bold_desc = Self::make_desc(&font.bold(), font::Slant::Normal, font::Weight::Bold);
-
-        let bold = load_or_regular(bold_desc);
-
-        // Load italic font
-        let italic_desc =
-            Self::make_desc(&font.italic(), font::Slant::Italic, font::Weight::Normal);
-
-        let italic = load_or_regular(italic_desc);
-
-        // Load bold italic font
-        let bold_italic_desc =
-            Self::make_desc(&font.bold_italic(), font::Slant::Italic, font::Weight::Bold);
-
-        let bold_italic = load_or_regular(bold_italic_desc);
-
-        Ok((regular, bold, italic, bold_italic))
-    }
-
-    fn make_desc(
-        desc: &config::FontDescription,
-        slant: font::Slant,
-        weight: font::Weight,
-    ) -> FontDesc {
-        let style = if let Some(ref spec) = desc.style {
-            font::Style::Specific(spec.to_owned())
-        } else {
-            font::Style::Description { slant, weight }
-        };
-        FontDesc::new(desc.family.clone(), style)
-    }
-
-    pub fn get<L>(&mut self, glyph_key: GlyphKey, loader: &mut L) -> &Glyph
-    where
-        L: LoadGlyph,
-    {
-        let glyph_offset = self.glyph_offset;
-        let rasterizer = &mut self.rasterizer;
-        let metrics = &self.metrics;
-        self.cache.entry(glyph_key).or_insert_with(|| {
-            let mut rasterized =
-                rasterizer.get_glyph(glyph_key).unwrap_or_else(|_| Default::default());
-
-            rasterized.left += i32::from(glyph_offset.x);
-            rasterized.top += i32::from(glyph_offset.y);
-            rasterized.top -= metrics.descent as i32;
-
-            loader.load_glyph(&rasterized)
-        })
-    }
-
-    pub fn update_font_size<L: LoadGlyph>(
-        &mut self,
-        font: config::Font,
-        dpr: f64,
-        loader: &mut L,
-    ) -> Result<(), font::Error> {
-        // Clear currently cached data in both GL and the registry
-        loader.clear();
-        self.cache = HashMap::default();
-        self.cursor_cache = HashMap::default();
-
-        // Update dpi scaling
-        self.rasterizer.update_dpr(dpr as f32);
-
-        // Recompute font keys
-        let (regular, bold, italic, bold_italic) =
-            Self::compute_font_keys(&font, &mut self.rasterizer)?;
-
-        self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?;
-        let metrics = self.rasterizer.metrics(regular, font.size)?;
-
-        info!("Font size changed to {:?} with DPR of {}", font.size, dpr);
-
-        self.font_size = font.size;
-        self.font_key = regular;
-        self.bold_key = bold;
-        self.italic_key = italic;
-        self.bold_italic_key = bold_italic;
-        self.metrics = metrics;
-
-        self.load_glyphs_for_font(regular, loader);
-        self.load_glyphs_for_font(bold, loader);
-        self.load_glyphs_for_font(italic, loader);
-        self.load_glyphs_for_font(bold_italic, loader);
-
-        Ok(())
-    }
-
-    pub fn font_metrics(&self) -> font::Metrics {
-        self.metrics
-    }
-
-    // Calculate font metrics without access to a glyph cache
-    pub fn static_metrics(font: Font, dpr: f64) -> Result<font::Metrics, font::Error> {
-        let mut rasterizer = font::Rasterizer::new(dpr as f32, font.use_thin_strokes())?;
-        let regular_desc =
-            GlyphCache::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal);
-        let regular = rasterizer.load_font(&regular_desc, font.size)?;
-        rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?;
-
-        rasterizer.metrics(regular, font.size)
-    }
-
-    pub fn calculate_dimensions<C>(
-        config: &Config<C>,
-        dpr: f64,
-        cell_width: f32,
-        cell_height: f32,
-    ) -> Option<(u32, u32)> {
-        let dimensions = config.window.dimensions;
-
-        if dimensions.columns_u32() == 0
-            || dimensions.lines_u32() == 0
-            || config.window.startup_mode() != StartupMode::Windowed
-        {
-            return None;
-        }
-
-        let padding_x = f64::from(config.window.padding.x) * dpr;
-        let padding_y = f64::from(config.window.padding.y) * dpr;
-
-        // Calculate new size based on cols/lines specified in config
-        let grid_width = cell_width as u32 * dimensions.columns_u32();
-        let grid_height = cell_height as u32 * dimensions.lines_u32();
-
-        let width = padding_x.mul_add(2., f64::from(grid_width)).floor();
-        let height = padding_y.mul_add(2., f64::from(grid_height)).floor();
-
-        Some((width as u32, height as u32))
-    }
-}
-
-#[derive(Debug)]
-#[repr(C)]
-struct InstanceData {
-    // coords
-    col: f32,
-    row: f32,
-    // glyph offset
-    left: f32,
-    top: f32,
-    // glyph scale
-    width: f32,
-    height: f32,
-    // uv offset
-    uv_left: f32,
-    uv_bot: f32,
-    // uv scale
-    uv_width: f32,
-    uv_height: f32,
-    // color
-    r: f32,
-    g: f32,
-    b: f32,
-    // background color
-    bg_r: f32,
-    bg_g: f32,
-    bg_b: f32,
-    bg_a: f32,
-}
-
-#[derive(Debug)]
-pub struct QuadRenderer {
-    program: TextShaderProgram,
-    rect_program: RectShaderProgram,
-    vao: GLuint,
-    ebo: GLuint,
-    vbo_instance: GLuint,
-    rect_vao: GLuint,
-    rect_vbo: GLuint,
-    atlas: Vec<Atlas>,
-    current_atlas: usize,
-    active_tex: GLuint,
-    batch: Batch,
-    rx: mpsc::Receiver<Msg>,
-}
-
-#[derive(Debug)]
-pub struct RenderApi<'a, C> {
-    active_tex: &'a mut GLuint,
-    batch: &'a mut Batch,
-    atlas: &'a mut Vec<Atlas>,
-    current_atlas: &'a mut usize,
-    program: &'a mut TextShaderProgram,
-    config: &'a Config<C>,
-}
-
-#[derive(Debug)]
-pub struct LoaderApi<'a> {
-    active_tex: &'a mut GLuint,
-    atlas: &'a mut Vec<Atlas>,
-    current_atlas: &'a mut usize,
-}
-
-#[derive(Debug, Default)]
-pub struct Batch {
-    tex: GLuint,
-    instances: Vec<InstanceData>,
-}
-
-impl Batch {
-    #[inline]
-    pub fn new() -> Self {
-        Self { tex: 0, instances: Vec::with_capacity(BATCH_MAX) }
-    }
-
-    pub fn add_item(&mut self, mut cell: RenderableCell, glyph: &Glyph) {
-        if self.is_empty() {
-            self.tex = glyph.tex_id;
-        }
-
-        if glyph.colored {
-            // XXX Temporary workaround to prevent emojis being rendered with a wrong colors on, at
-            // least, dark backgrounds. For more info see #1864.
-            cell.fg.r = 255;
-            cell.fg.g = 255;
-            cell.fg.b = 255;
-        }
-
-        self.instances.push(InstanceData {
-            col: cell.column.0 as f32,
-            row: cell.line.0 as f32,
-
-            top: glyph.top,
-            left: glyph.left,
-            width: glyph.width,
-            height: glyph.height,
-
-            uv_bot: glyph.uv_bot,
-            uv_left: glyph.uv_left,
-            uv_width: glyph.uv_width,
-            uv_height: glyph.uv_height,
-
-            r: f32::from(cell.fg.r),
-            g: f32::from(cell.fg.g),
-            b: f32::from(cell.fg.b),
-
-            bg_r: f32::from(cell.bg.r),
-            bg_g: f32::from(cell.bg.g),
-            bg_b: f32::from(cell.bg.b),
-            bg_a: cell.bg_alpha,
-        });
-    }
-
-    #[inline]
-    pub fn full(&self) -> bool {
-        self.capacity() == self.len()
-    }
-
-    #[inline]
-    pub fn len(&self) -> usize {
-        self.instances.len()
-    }
-
-    #[inline]
-    pub fn capacity(&self) -> usize {
-        BATCH_MAX
-    }
-
-    #[inline]
-    pub fn is_empty(&self) -> bool {
-        self.len() == 0
-    }
-
-    #[inline]
-    pub fn size(&self) -> usize {
-        self.len() * size_of::<InstanceData>()
-    }
-
-    pub fn clear(&mut self) {
-        self.tex = 0;
-        self.instances.clear();
-    }
-}
-
-/// Maximum items to be drawn in a batch.
-const BATCH_MAX: usize = 0x1_0000;
-const ATLAS_SIZE: i32 = 1024;
-
-impl QuadRenderer {
-    pub fn new() -> Result<QuadRenderer, Error> {
-        let program = TextShaderProgram::new()?;
-        let rect_program = RectShaderProgram::new()?;
-
-        let mut vao: GLuint = 0;
-        let mut ebo: GLuint = 0;
-
-        let mut vbo_instance: GLuint = 0;
-
-        let mut rect_vao: GLuint = 0;
-        let mut rect_vbo: GLuint = 0;
-        let mut rect_ebo: GLuint = 0;
-
-        unsafe {
-            gl::Enable(gl::BLEND);
-            gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
-            gl::Enable(gl::MULTISAMPLE);
-
-            // Disable depth mask, as the renderer never uses depth tests
-            gl::DepthMask(gl::FALSE);
-
-            gl::GenVertexArrays(1, &mut vao);
-            gl::GenBuffers(1, &mut ebo);
-            gl::GenBuffers(1, &mut vbo_instance);
-            gl::BindVertexArray(vao);
-
-            // ---------------------
-            // Set up element buffer
-            // ---------------------
-            let indices: [u32; 6] = [0, 1, 3, 1, 2, 3];
-
-            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, ebo);
-            gl::BufferData(
-                gl::ELEMENT_ARRAY_BUFFER,
-                (6 * size_of::<u32>()) as isize,
-                indices.as_ptr() as *const _,
-                gl::STATIC_DRAW,
-            );
-
-            // ----------------------------
-            // Setup vertex instance buffer
-            // ----------------------------
-            gl::BindBuffer(gl::ARRAY_BUFFER, vbo_instance);
-            gl::BufferData(
-                gl::ARRAY_BUFFER,
-                (BATCH_MAX * size_of::<InstanceData>()) as isize,
-                ptr::null(),
-                gl::STREAM_DRAW,
-            );
-            // coords
-            gl::VertexAttribPointer(
-                0,
-                2,
-                gl::FLOAT,
-                gl::FALSE,
-                size_of::<InstanceData>() as i32,
-                ptr::null(),
-            );
-            gl::EnableVertexAttribArray(0);
-            gl::VertexAttribDivisor(0, 1);
-            // glyphoffset
-            gl::VertexAttribPointer(
-                1,
-                4,
-                gl::FLOAT,
-                gl::FALSE,
-                size_of::<InstanceData>() as i32,
-                (2 * size_of::<f32>()) as *const _,
-            );
-            gl::EnableVertexAttribArray(1);
-            gl::VertexAttribDivisor(1, 1);
-            // uv
-            gl::VertexAttribPointer(
-                2,
-                4,
-                gl::FLOAT,
-                gl::FALSE,
-                size_of::<InstanceData>() as i32,
-                (6 * size_of::<f32>()) as *const _,
-            );
-            gl::EnableVertexAttribArray(2);
-            gl::VertexAttribDivisor(2, 1);
-            // color
-            gl::VertexAttribPointer(
-                3,
-                3,
-                gl::FLOAT,
-                gl::FALSE,
-                size_of::<InstanceData>() as i32,
-                (10 * size_of::<f32>()) as *const _,
-            );
-            gl::EnableVertexAttribArray(3);
-            gl::VertexAttribDivisor(3, 1);
-            // color
-            gl::VertexAttribPointer(
-                4,
-                4,
-                gl::FLOAT,
-                gl::FALSE,
-                size_of::<InstanceData>() as i32,
-                (13 * size_of::<f32>()) as *const _,
-            );
-            gl::EnableVertexAttribArray(4);
-            gl::VertexAttribDivisor(4, 1);
-
-            // Rectangle setup
-            gl::GenVertexArrays(1, &mut rect_vao);
-            gl::GenBuffers(1, &mut rect_vbo);
-            gl::GenBuffers(1, &mut rect_ebo);
-            gl::BindVertexArray(rect_vao);
-            let indices: [i32; 6] = [0, 1, 3, 1, 2, 3];
-            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, rect_ebo);
-            gl::BufferData(
-                gl::ELEMENT_ARRAY_BUFFER,
-                (size_of::<i32>() * indices.len()) as _,
-                indices.as_ptr() as *const _,
-                gl::STATIC_DRAW,
-            );
-
-            // Cleanup
-            gl::BindVertexArray(0);
-            gl::BindBuffer(gl::ARRAY_BUFFER, 0);
-            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
-        }
-
-        let (msg_tx, msg_rx) = mpsc::channel();
-
-        if cfg!(feature = "live-shader-reload") {
-            util::thread::spawn_named("live shader reload", move || {
-                let (tx, rx) = std::sync::mpsc::channel();
-                // The Duration argument is a debouncing period.
-                let mut watcher =
-                    watcher(tx, Duration::from_millis(10)).expect("create file watcher");
-                watcher
-                    .watch(TEXT_SHADER_F_PATH, RecursiveMode::NonRecursive)
-                    .expect("watch fragment shader");
-                watcher
-                    .watch(TEXT_SHADER_V_PATH, RecursiveMode::NonRecursive)
-                    .expect("watch vertex shader");
-
-                loop {
-                    let event = rx.recv().expect("watcher event");
-
-                    match event {
-                        DebouncedEvent::Rename(..) => continue,
-                        DebouncedEvent::Create(_)
-                        | DebouncedEvent::Write(_)
-                        | DebouncedEvent::Chmod(_) => {
-                            msg_tx.send(Msg::ShaderReload).expect("msg send ok");
-                        },
-                        _ => {},
-                    }
-                }
-            });
-        }
-
-        let mut renderer = Self {
-            program,
-            rect_program,
-            vao,
-            ebo,
-            vbo_instance,
-            rect_vao,
-            rect_vbo,
-            atlas: Vec::new(),
-            current_atlas: 0,
-            active_tex: 0,
-            batch: Batch::new(),
-            rx: msg_rx,
-        };
-
-        let atlas = Atlas::new(ATLAS_SIZE);
-        renderer.atlas.push(atlas);
-
-        Ok(renderer)
-    }
-
-    // Draw all rectangles simultaneously to prevent excessive program swaps
-    pub fn draw_rects(&mut self, props: &term::SizeInfo, rects: Vec<RenderRect>) {
-        // Swap to rectangle rendering program
-        unsafe {
-            // Swap program
-            gl::UseProgram(self.rect_program.id);
-
-            // Remove padding from viewport
-            gl::Viewport(0, 0, props.width as i32, props.height as i32);
-
-            // Change blending strategy
-            gl::BlendFuncSeparate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA, gl::SRC_ALPHA, gl::ONE);
-
-            // Setup data and buffers
-            gl::BindVertexArray(self.rect_vao);
-            gl::BindBuffer(gl::ARRAY_BUFFER, self.rect_vbo);
-
-            // Position
-            gl::VertexAttribPointer(
-                0,
-                2,
-                gl::FLOAT,
-                gl::FALSE,
-                (size_of::<f32>() * 2) as _,
-                ptr::null(),
-            );
-            gl::EnableVertexAttribArray(0);
-        }
-
-        // Draw all the rects
-        for rect in rects {
-            self.render_rect(&rect, props);
-        }
-
-        // Deactivate rectangle program again
-        unsafe {
-            // Reset blending strategy
-            gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
-
-            // Reset data and buffers
-            gl::BindBuffer(gl::ARRAY_BUFFER, 0);
-            gl::BindVertexArray(0);
-
-            let padding_x = props.padding_x as i32;
-            let padding_y = props.padding_y as i32;
-            let width = props.width as i32;
-            let height = props.height as i32;
-            gl::Viewport(padding_x, padding_y, width - 2 * padding_x, height - 2 * padding_y);
-
-            // Disable program
-            gl::UseProgram(0);
-        }
-    }
-
-    pub fn with_api<F, T, C>(&mut self, config: &Config<C>, props: &term::SizeInfo, func: F) -> T
-    where
-        F: FnOnce(RenderApi<'_, C>) -> T,
-    {
-        // Flush message queue
-        if let Ok(Msg::ShaderReload) = self.rx.try_recv() {
-            self.reload_shaders(props);
-        }
-        while let Ok(_) = self.rx.try_recv() {}
-
-        unsafe {
-            gl::UseProgram(self.program.id);
-            self.program.set_term_uniforms(props);
-
-            gl::BindVertexArray(self.vao);
-            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
-            gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo_instance);
-            gl::ActiveTexture(gl::TEXTURE0);
-        }
-
-        let res = func(RenderApi {
-            active_tex: &mut self.active_tex,
-            batch: &mut self.batch,
-            atlas: &mut self.atlas,
-            current_atlas: &mut self.current_atlas,
-            program: &mut self.program,
-            config,
-        });
-
-        unsafe {
-            gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
-            gl::BindBuffer(gl::ARRAY_BUFFER, 0);
-            gl::BindVertexArray(0);
-
-            gl::UseProgram(0);
-        }
-
-        res
-    }
-
-    pub fn with_loader<F, T>(&mut self, func: F) -> T
-    where
-        F: FnOnce(LoaderApi<'_>) -> T,
-    {
-        unsafe {
-            gl::ActiveTexture(gl::TEXTURE0);
-        }
-
-        func(LoaderApi {
-            active_tex: &mut self.active_tex,
-            atlas: &mut self.atlas,
-            current_atlas: &mut self.current_atlas,
-        })
-    }
-
-    pub fn reload_shaders(&mut self, props: &term::SizeInfo) {
-        info!("Reloading shaders...");
-        let result = (TextShaderProgram::new(), RectShaderProgram::new());
-        let (program, rect_program) = match result {
-            (Ok(program), Ok(rect_program)) => {
-                unsafe {
-                    gl::UseProgram(program.id);
-                    program.update_projection(
-                        props.width,
-                        props.height,
-                        props.padding_x,
-                        props.padding_y,
-                    );
-                    gl::UseProgram(0);
-                }
-
-                info!("... successfully reloaded shaders");
-                (program, rect_program)
-            },
-            (Err(err), _) | (_, Err(err)) => {
-                error!("{}", err);
-                return;
-            },
-        };
-
-        self.active_tex = 0;
-        self.program = program;
-        self.rect_program = rect_program;
-    }
-
-    pub fn resize(&mut self, size: &SizeInfo) {
-        // viewport
-        unsafe {
-            gl::Viewport(
-                size.padding_x as i32,
-                size.padding_y as i32,
-                size.width as i32 - 2 * size.padding_x as i32,
-                size.height as i32 - 2 * size.padding_y as i32,
-            );
-
-            // update projection
-            gl::UseProgram(self.program.id);
-            self.program.update_projection(size.width, size.height, size.padding_x, size.padding_y);
-            gl::UseProgram(0);
-        }
-    }
-
-    // Render a rectangle
-    //
-    // This requires the rectangle program to be activated
-    fn render_rect(&mut self, rect: &RenderRect, size: &term::SizeInfo) {
-        // Do nothing when alpha is fully transparent
-        if rect.alpha == 0. {
-            return;
-        }
-
-        // Calculate rectangle position
-        let center_x = size.width / 2.;
-        let center_y = size.height / 2.;
-        let x = (rect.x - center_x) / center_x;
-        let y = -(rect.y - center_y) / center_y;
-        let width = rect.width / center_x;
-        let height = rect.height / center_y;
-
-        unsafe {
-            // Setup vertices
-            let vertices: [f32; 8] = [x + width, y, x + width, y - height, x, y - height, x, y];
-
-            // Load vertex data into array buffer
-            gl::BufferData(
-                gl::ARRAY_BUFFER,
-                (size_of::<f32>() * vertices.len()) as _,
-                vertices.as_ptr() as *const _,
-                gl::STATIC_DRAW,
-            );
-
-            // Color
-            self.rect_program.set_color(rect.color, rect.alpha);
-
-            // Draw the rectangle
-            gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null());
-        }
-    }
-}
-
-impl<'a, C> RenderApi<'a, C> {
-    pub fn clear(&self, color: Rgb) {
-        unsafe {
-            let alpha = self.config.background_opacity();
-            gl::ClearColor(
-                (f32::from(color.r) / 255.0).min(1.0) * alpha,
-                (f32::from(color.g) / 255.0).min(1.0) * alpha,
-                (f32::from(color.b) / 255.0).min(1.0) * alpha,
-                alpha,
-            );
-            gl::Clear(gl::COLOR_BUFFER_BIT);
-        }
-    }
-
-    fn render_batch(&mut self) {
-        unsafe {
-            gl::BufferSubData(
-                gl::ARRAY_BUFFER,
-                0,
-                self.batch.size() as isize,
-                self.batch.instances.as_ptr() as *const _,
-            );
-        }
-
-        // Bind texture if necessary
-        if *self.active_tex != self.batch.tex {
-            unsafe {
-                gl::BindTexture(gl::TEXTURE_2D, self.batch.tex);
-            }
-            *self.active_tex = self.batch.tex;
-        }
-
-        unsafe {
-            self.program.set_background_pass(true);
-            gl::DrawElementsInstanced(
-                gl::TRIANGLES,
-                6,
-                gl::UNSIGNED_INT,
-                ptr::null(),
-                self.batch.len() as GLsizei,
-            );
-            self.program.set_background_pass(false);
-            gl::DrawElementsInstanced(
-                gl::TRIANGLES,
-                6,
-                gl::UNSIGNED_INT,
-                ptr::null(),
-                self.batch.len() as GLsizei,
-            );
-        }
-
-        self.batch.clear();
-    }
-
-    /// Render a string in a variable location. Used for printing the render timer, warnings and
-    /// errors.
-    pub fn render_string(
-        &mut self,
-        string: &str,
-        line: Line,
-        glyph_cache: &mut GlyphCache,
-        color: Option<Rgb>,
-    ) {
-        let bg_alpha = color.map(|_| 1.0).unwrap_or(0.0);
-        let col = Column(0);
-
-        let cells = string
-            .chars()
-            .enumerate()
-            .map(|(i, c)| RenderableCell {
-                line,
-                column: col + i,
-                inner: RenderableCellContent::Chars({
-                    let mut chars = [' '; cell::MAX_ZEROWIDTH_CHARS + 1];
-                    chars[0] = c;
-                    chars
-                }),
-                bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0 }),
-                fg: Rgb { r: 0, g: 0, b: 0 },
-                flags: Flags::empty(),
-                bg_alpha,
-            })
-            .collect::<Vec<_>>();
-
-        for cell in cells {
-            self.render_cell(cell, glyph_cache);
-        }
-    }
-
-    #[inline]
-    fn add_render_item(&mut self, cell: RenderableCell, glyph: &Glyph) {
-        // Flush batch if tex changing
-        if !self.batch.is_empty() && self.batch.tex != glyph.tex_id {
-            self.render_batch();
-        }
-
-        self.batch.add_item(cell, glyph);
-
-        // Render batch and clear if it's full
-        if self.batch.full() {
-            self.render_batch();
-        }
-    }
-
-    pub fn render_cell(&mut self, cell: RenderableCell, glyph_cache: &mut GlyphCache) {
-        let chars = match cell.inner {
-            RenderableCellContent::Cursor(cursor_key) => {
-                // Raw cell pixel buffers like cursors don't need to go through font lookup
-                let metrics = glyph_cache.metrics;
-                let glyph = glyph_cache.cursor_cache.entry(cursor_key).or_insert_with(|| {
-                    self.load_glyph(&cursor::get_cursor_glyph(
-                        cursor_key.style,
-                        metrics,
-                        self.config.font.offset.x,
-                        self.config.font.offset.y,
-                        cursor_key.is_wide,
-                    ))
-                });
-                self.add_render_item(cell, glyph);
-                return;
-            },
-            RenderableCellContent::Chars(chars) => chars,
-        };
-
-        // Get font key for cell
-        let font_key = match cell.flags & Flags::BOLD_ITALIC {
-            Flags::BOLD_ITALIC => glyph_cache.bold_italic_key,
-            Flags::ITALIC => glyph_cache.italic_key,
-            Flags::BOLD => glyph_cache.bold_key,
-            _ => glyph_cache.font_key,
-        };
-
-        // Don't render text of HIDDEN cells
-        let mut chars = if cell.flags.contains(Flags::HIDDEN) {
-            [' '; cell::MAX_ZEROWIDTH_CHARS + 1]
-        } else {
-            chars
-        };
-
-        // Render tabs as spaces in case the font doesn't support it
-        if chars[0] == '\t' {
-            chars[0] = ' ';
-        }
-
-        let mut glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: chars[0] };
-
-        // Add cell to batch
-        let glyph = glyph_cache.get(glyph_key, self);
-        self.add_render_item(cell, glyph);
-
-        // Render zero-width characters
-        for c in (&chars[1..]).iter().filter(|c| **c != ' ') {
-            glyph_key.c = *c;
-            let mut glyph = *glyph_cache.get(glyph_key, self);
-
-            // The metrics of zero-width characters are based on rendering
-            // the character after the current cell, with the anchor at the
-            // right side of the preceding character. Since we render the
-            // zero-width characters inside the preceding character, the
-            // anchor has been moved to the right by one cell.
-            glyph.left += glyph_cache.metrics.average_advance as f32;
-
-            self.add_render_item(cell, &glyph);
-        }
-    }
-}
-
-/// Load a glyph into a texture atlas
-///
-/// If the current atlas is full, a new one will be created.
-#[inline]
-fn load_glyph(
-    active_tex: &mut GLuint,
-    atlas: &mut Vec<Atlas>,
-    current_atlas: &mut usize,
-    rasterized: &RasterizedGlyph,
-) -> Glyph {
-    // At least one atlas is guaranteed to be in the `self.atlas` list; thus
-    // the unwrap.
-    match atlas[*current_atlas].insert(rasterized, active_tex) {
-        Ok(glyph) => glyph,
-        Err(AtlasInsertError::Full) => {
-            *current_atlas += 1;
-            if *current_atlas == atlas.len() {
-                let new = Atlas::new(ATLAS_SIZE);
-                *active_tex = 0; // Atlas::new binds a texture. Ugh this is sloppy.
-                atlas.push(new);
-            }
-            load_glyph(active_tex, atlas, current_atlas, rasterized)
-        },
-        Err(AtlasInsertError::GlyphTooLarge) => Glyph {
-            tex_id: atlas[*current_atlas].id,
-            colored: false,
-            top: 0.0,
-            left: 0.0,
-            width: 0.0,
-            height: 0.0,
-            uv_bot: 0.0,
-            uv_left: 0.0,
-            uv_width: 0.0,
-            uv_height: 0.0,
-        },
-    }
-}
-
-#[inline]
-fn clear_atlas(atlas: &mut Vec<Atlas>, current_atlas: &mut usize) {
-    for atlas in atlas.iter_mut() {
-        atlas.clear();
-    }
-    *current_atlas = 0;
-}
-
-impl<'a> LoadGlyph for LoaderApi<'a> {
-    fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph {
-        load_glyph(self.active_tex, self.atlas, self.current_atlas, rasterized)
-    }
-
-    fn clear(&mut self) {
-        clear_atlas(self.atlas, self.current_atlas)
-    }
-}
-
-impl<'a, C> LoadGlyph for RenderApi<'a, C> {
-    fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph {
-        load_glyph(self.active_tex, self.atlas, self.current_atlas, rasterized)
-    }
-
-    fn clear(&mut self) {
-        clear_atlas(self.atlas, self.current_atlas)
-    }
-}
-
-impl<'a, C> Drop for RenderApi<'a, C> {
-    fn drop(&mut self) {
-        if !self.batch.is_empty() {
-            self.render_batch();
-        }
-    }
-}
-
-impl TextShaderProgram {
-    pub fn new() -> Result<TextShaderProgram, ShaderCreationError> {
-        let (vertex_src, fragment_src) = if cfg!(feature = "live-shader-reload") {
-            (None, None)
-        } else {
-            (Some(TEXT_SHADER_V), Some(TEXT_SHADER_F))
-        };
-        let vertex_shader = create_shader(TEXT_SHADER_V_PATH, gl::VERTEX_SHADER, vertex_src)?;
-        let fragment_shader = create_shader(TEXT_SHADER_F_PATH, gl::FRAGMENT_SHADER, fragment_src)?;
-        let program = create_program(vertex_shader, fragment_shader)?;
-
-        unsafe {
-            gl::DeleteShader(fragment_shader);
-            gl::DeleteShader(vertex_shader);
-            gl::UseProgram(program);
-        }
-
-        macro_rules! cptr {
-            ($thing:expr) => {
-                $thing.as_ptr() as *const _
-            };
-        }
-
-        macro_rules! assert_uniform_valid {
-            ($uniform:expr) => {
-                assert!($uniform != gl::INVALID_VALUE as i32);
-                assert!($uniform != gl::INVALID_OPERATION as i32);
-            };
-            ( $( $uniform:expr ),* ) => {
-                $( assert_uniform_valid!($uniform); )*
-            };
-        }
-
-        // get uniform locations
-        let (projection, cell_dim, background) = unsafe {
-            (
-                gl::GetUniformLocation(program, cptr!(b"projection\0")),
-                gl::GetUniformLocation(program, cptr!(b"cellDim\0")),
-                gl::GetUniformLocation(program, cptr!(b"backgroundPass\0")),
-            )
-        };
-
-        assert_uniform_valid!(projection, cell_dim, background);
-
-        let shader = Self {
-            id: program,
-            u_projection: projection,
-            u_cell_dim: cell_dim,
-            u_background: background,
-        };
-
-        unsafe {
-            gl::UseProgram(0);
-        }
-
-        Ok(shader)
-    }
-
-    fn update_projection(&self, width: f32, height: f32, padding_x: f32, padding_y: f32) {
-        // Bounds check
-        if (width as u32) < (2 * padding_x as u32) || (height as u32) < (2 * padding_y as u32) {
-            return;
-        }
-
-        // Compute scale and offset factors, from pixel to ndc space. Y is inverted
-        //   [0, width - 2 * padding_x] to [-1, 1]
-        //   [height - 2 * padding_y, 0] to [-1, 1]
-        let scale_x = 2. / (width - 2. * padding_x);
-        let scale_y = -2. / (height - 2. * padding_y);
-        let offset_x = -1.;
-        let offset_y = 1.;
-
-        info!("Width: {}, Height: {}", width, height);
-
-        unsafe {
-            gl::Uniform4f(self.u_projection, offset_x, offset_y, scale_x, scale_y);
-        }
-    }
-
-    fn set_term_uniforms(&self, props: &term::SizeInfo) {
-        unsafe {
-            gl::Uniform2f(self.u_cell_dim, props.cell_width, props.cell_height);
-        }
-    }
-
-    fn set_background_pass(&self, background_pass: bool) {
-        let value = if background_pass { 1 } else { 0 };
-
-        unsafe {
-            gl::Uniform1i(self.u_background, value);
-        }
-    }
-}
-
-impl Drop for TextShaderProgram {
-    fn drop(&mut self) {
-        unsafe {
-            gl::DeleteProgram(self.id);
-        }
-    }
-}
-
-impl RectShaderProgram {
-    pub fn new() -> Result<Self, ShaderCreationError> {
-        let (vertex_src, fragment_src) = if cfg!(feature = "live-shader-reload") {
-            (None, None)
-        } else {
-            (Some(RECT_SHADER_V), Some(RECT_SHADER_F))
-        };
-        let vertex_shader = create_shader(RECT_SHADER_V_PATH, gl::VERTEX_SHADER, vertex_src)?;
-        let fragment_shader = create_shader(RECT_SHADER_F_PATH, gl::FRAGMENT_SHADER, fragment_src)?;
-        let program = create_program(vertex_shader, fragment_shader)?;
-
-        unsafe {
-            gl::DeleteShader(fragment_shader);
-            gl::DeleteShader(vertex_shader);
-            gl::UseProgram(program);
-        }
-
-        // get uniform locations
-        let u_color = unsafe { gl::GetUniformLocation(program, b"color\0".as_ptr() as *const _) };
-
-        let shader = Self { id: program, u_color };
-
-        unsafe { gl::UseProgram(0) }
-
-        Ok(shader)
-    }
-
-    fn set_color(&self, color: Rgb, alpha: f32) {
-        unsafe {
-            gl::Uniform4f(
-                self.u_color,
-                f32::from(color.r) / 255.,
-                f32::from(color.g) / 255.,
-                f32::from(color.b) / 255.,
-                alpha,
-            );
-        }
-    }
-}
-
-impl Drop for RectShaderProgram {
-    fn drop(&mut self) {
-        unsafe {
-            gl::DeleteProgram(self.id);
-        }
-    }
-}
-
-fn create_program(vertex: GLuint, fragment: GLuint) -> Result<GLuint, ShaderCreationError> {
-    unsafe {
-        let program = gl::CreateProgram();
-        gl::AttachShader(program, vertex);
-        gl::AttachShader(program, fragment);
-        gl::LinkProgram(program);
-
-        let mut success: GLint = 0;
-        gl::GetProgramiv(program, gl::LINK_STATUS, &mut success);
-
-        if success == i32::from(gl::TRUE) {
-            Ok(program)
-        } else {
-            Err(ShaderCreationError::Link(get_program_info_log(program)))
-        }
-    }
-}
-
-fn create_shader(
-    path: &str,
-    kind: GLenum,
-    source: Option<&'static str>,
-) -> Result<GLuint, ShaderCreationError> {
-    let from_disk;
-    let source = if let Some(src) = source {
-        src
-    } else {
-        from_disk = read_file(path)?;
-        &from_disk[..]
-    };
-
-    let len: [GLint; 1] = [source.len() as GLint];
-
-    let shader = unsafe {
-        let shader = gl::CreateShader(kind);
-        gl::ShaderSource(shader, 1, &(source.as_ptr() as *const _), len.as_ptr());
-        gl::CompileShader(shader);
-        shader
-    };
-
-    let mut success: GLint = 0;
-    unsafe {
-        gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success);
-    }
-
-    if success == GLint::from(gl::TRUE) {
-        Ok(shader)
-    } else {
-        // Read log
-        let log = get_shader_info_log(shader);
-
-        // Cleanup
-        unsafe {
-            gl::DeleteShader(shader);
-        }
-
-        Err(ShaderCreationError::Compile(PathBuf::from(path), log))
-    }
-}
-
-fn get_program_info_log(program: GLuint) -> String {
-    // Get expected log length
-    let mut max_length: GLint = 0;
-    unsafe {
-        gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut max_length);
-    }
-
-    // Read the info log
-    let mut actual_length: GLint = 0;
-    let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
-    unsafe {
-        gl::GetProgramInfoLog(program, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
-    }
-
-    // Build a string
-    unsafe {
-        buf.set_len(actual_length as usize);
-    }
-
-    // XXX should we expect opengl to return garbage?
-    String::from_utf8(buf).unwrap()
-}
-
-fn get_shader_info_log(shader: GLuint) -> String {
-    // Get expected log length
-    let mut max_length: GLint = 0;
-    unsafe {
-        gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut max_length);
-    }
-
-    // Read the info log
-    let mut actual_length: GLint = 0;
-    let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
-    unsafe {
-        gl::GetShaderInfoLog(shader, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
-    }
-
-    // Build a string
-    unsafe {
-        buf.set_len(actual_length as usize);
-    }
-
-    // XXX should we expect opengl to return garbage?
-    String::from_utf8(buf).unwrap()
-}
-
-fn read_file(path: &str) -> Result<String, io::Error> {
-    let mut f = File::open(path)?;
-    let mut buf = String::new();
-    f.read_to_string(&mut buf)?;
-
-    Ok(buf)
-}
-
-#[derive(Debug)]
-pub enum ShaderCreationError {
-    /// Error reading file
-    Io(io::Error),
-
-    /// Error compiling shader
-    Compile(PathBuf, String),
-
-    /// Problem linking
-    Link(String),
-}
-
-impl std::error::Error for ShaderCreationError {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        match self {
-            ShaderCreationError::Io(err) => err.source(),
-            _ => None,
-        }
-    }
-}
-
-impl Display for ShaderCreationError {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        match self {
-            ShaderCreationError::Io(err) => write!(f, "Couldn't read shader: {}", err),
-            ShaderCreationError::Compile(path, log) => {
-                write!(f, "Failed compiling shader at {}: {}", path.display(), log)
-            },
-            ShaderCreationError::Link(log) => write!(f, "Failed linking shader: {}", log),
-        }
-    }
-}
-
-impl From<io::Error> for ShaderCreationError {
-    fn from(val: io::Error) -> Self {
-        ShaderCreationError::Io(val)
-    }
-}
-
-/// Manages a single texture atlas
-///
-/// The strategy for filling an atlas looks roughly like this:
-///
-/// ```text
-///                           (width, height)
-///   ┌─────┬─────┬─────┬─────┬─────┐
-///   │ 10  │     │     │     │     │ <- Empty spaces; can be filled while
-///   │     │     │     │     │     │    glyph_height < height - row_baseline
-///   ├─────┼─────┼─────┼─────┼─────┤
-///   │ 5   │ 6   │ 7   │ 8   │ 9   │
-///   │     │     │     │     │     │
-///   ├─────┼─────┼─────┼─────┴─────┤ <- Row height is tallest glyph in row; this is
-///   │ 1   │ 2   │ 3   │ 4         │    used as the baseline for the following row.
-///   │     │     │     │           │ <- Row considered full when next glyph doesn't
-///   └─────┴─────┴─────┴───────────┘    fit in the row.
-/// (0, 0)  x->
-/// ```
-#[derive(Debug)]
-struct Atlas {
-    /// Texture id for this atlas
-    id: GLuint,
-
-    /// Width of atlas
-    width: i32,
-
-    /// Height of atlas
-    height: i32,
-
-    /// Left-most free pixel in a row.
-    ///
-    /// This is called the extent because it is the upper bound of used pixels
-    /// in a row.
-    row_extent: i32,
-
-    /// Baseline for glyphs in the current row
-    row_baseline: i32,
-
-    /// Tallest glyph in current row
-    ///
-    /// This is used as the advance when end of row is reached
-    row_tallest: i32,
-}
-
-/// Error that can happen when inserting a texture to the Atlas
-enum AtlasInsertError {
-    /// Texture atlas is full
-    Full,
-
-    /// The glyph cannot fit within a single texture
-    GlyphTooLarge,
-}
-
-impl Atlas {
-    fn new(size: i32) -> Self {
-        let mut id: GLuint = 0;
-        unsafe {
-            gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1);
-            gl::GenTextures(1, &mut id);
-            gl::BindTexture(gl::TEXTURE_2D, id);
-            gl::TexImage2D(
-                gl::TEXTURE_2D,
-                0,
-                gl::RGB as i32,
-                size,
-                size,
-                0,
-                gl::RGB,
-                gl::UNSIGNED_BYTE,
-                ptr::null(),
-            );
-
-            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
-            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
-            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
-            gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
-
-            gl::BindTexture(gl::TEXTURE_2D, 0);
-        }
-
-        Self { id, width: size, height: size, row_extent: 0, row_baseline: 0, row_tallest: 0 }
-    }
-
-    pub fn clear(&mut self) {
-        self.row_extent = 0;
-        self.row_baseline = 0;
-        self.row_tallest = 0;
-    }
-
-    /// Insert a RasterizedGlyph into the texture atlas
-    pub fn insert(
-        &mut self,
-        glyph: &RasterizedGlyph,
-        active_tex: &mut u32,
-    ) -> Result<Glyph, AtlasInsertError> {
-        if glyph.width > self.width || glyph.height > self.height {
-            return Err(AtlasInsertError::GlyphTooLarge);
-        }
-
-        // If there's not enough room in current row, go onto next one
-        if !self.room_in_row(glyph) {
-            self.advance_row()?;
-        }
-
-        // If there's still not room, there's nothing that can be done here.
-        if !self.room_in_row(glyph) {
-            return Err(AtlasInsertError::Full);
-        }
-
-        // There appears to be room; load the glyph.
-        Ok(self.insert_inner(glyph, active_tex))
-    }
-
-    /// Insert the glyph without checking for room
-    ///
-    /// Internal function for use once atlas has been checked for space. GL
-    /// errors could still occur at this point if we were checking for them;
-    /// hence, the Result.
-    fn insert_inner(&mut self, glyph: &RasterizedGlyph, active_tex: &mut u32) -> Glyph {
-        let offset_y = self.row_baseline;
-        let offset_x = self.row_extent;
-        let height = glyph.height as i32;
-        let width = glyph.width as i32;
-        let colored;
-
-        unsafe {
-            gl::BindTexture(gl::TEXTURE_2D, self.id);
-
-            // Load data into OpenGL
-            let (format, buf) = match &glyph.buf {
-                BitmapBuffer::RGB(buf) => {
-                    colored = false;
-                    (gl::RGB, buf)
-                },
-                BitmapBuffer::RGBA(buf) => {
-                    colored = true;
-                    (gl::RGBA, buf)
-                },
-            };
-
-            gl::TexSubImage2D(
-                gl::TEXTURE_2D,
-                0,
-                offset_x,
-                offset_y,
-                width,
-                height,
-                format,
-                gl::UNSIGNED_BYTE,
-                buf.as_ptr() as *const _,
-            );
-
-            gl::BindTexture(gl::TEXTURE_2D, 0);
-            *active_tex = 0;
-        }
-
-        // Update Atlas state
-        self.row_extent = offset_x + width;
-        if height > self.row_tallest {
-            self.row_tallest = height;
-        }
-
-        // Generate UV coordinates
-        let uv_bot = offset_y as f32 / self.height as f32;
-        let uv_left = offset_x as f32 / self.width as f32;
-        let uv_height = height as f32 / self.height as f32;
-        let uv_width = width as f32 / self.width as f32;
-
-        Glyph {
-            tex_id: self.id,
-            colored,
-            top: glyph.top as f32,
-            width: width as f32,
-            height: height as f32,
-            left: glyph.left as f32,
-            uv_bot,
-            uv_left,
-            uv_width,
-            uv_height,
-        }
-    }
-
-    /// Check if there's room in the current row for given glyph
-    fn room_in_row(&self, raw: &RasterizedGlyph) -> bool {
-        let next_extent = self.row_extent + raw.width as i32;
-        let enough_width = next_extent <= self.width;
-        let enough_height = (raw.height as i32) < (self.height - self.row_baseline);
-
-        enough_width && enough_height
-    }
-
-    /// Mark current row as finished and prepare to insert into the next row
-    fn advance_row(&mut self) -> Result<(), AtlasInsertError> {
-        let advance_to = self.row_baseline + self.row_tallest;
-        if self.height - advance_to <= 0 {
-            return Err(AtlasInsertError::Full);
-        }
-
-        self.row_baseline = advance_to;
-        self.row_extent = 0;
-        self.row_tallest = 0;
-
-        Ok(())
-    }
-}
diff --git a/alacritty/src/renderer/rects.rs b/alacritty/src/renderer/rects.rs
deleted file mode 100644
index 796abe6..0000000
--- a/alacritty/src/renderer/rects.rs
+++ /dev/null
@@ -1,149 +0,0 @@
-// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-use std::collections::HashMap;
-
-use font::Metrics;
-
-use alacritty_terminal::index::{Column, Point};
-use alacritty_terminal::term::cell::Flags;
-use alacritty_terminal::term::color::Rgb;
-use alacritty_terminal::term::{RenderableCell, SizeInfo};
-
-#[derive(Debug, Copy, Clone)]
-pub struct RenderRect {
-    pub x: f32,
-    pub y: f32,
-    pub width: f32,
-    pub height: f32,
-    pub color: Rgb,
-    pub alpha: f32,
-}
-
-impl RenderRect {
-    pub fn new(x: f32, y: f32, width: f32, height: f32, color: Rgb, alpha: f32) -> Self {
-        RenderRect { x, y, width, height, color, alpha }
-    }
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub struct RenderLine {
-    pub start: Point,
-    pub end: Point,
-    pub color: Rgb,
-}
-
-impl RenderLine {
-    pub fn rects(&self, flag: Flags, metrics: &Metrics, size: &SizeInfo) -> Vec<RenderRect> {
-        let mut rects = Vec::new();
-
-        let mut start = self.start;
-        while start.line < self.end.line {
-            let mut end = start;
-            end.col = size.cols() - 1;
-            rects.push(Self::create_rect(metrics, size, flag, start, end, self.color));
-
-            start.col = Column(0);
-            start.line += 1;
-        }
-
-        rects.push(Self::create_rect(metrics, size, flag, start, self.end, self.color));
-
-        rects
-    }
-
-    fn create_rect(
-        metrics: &Metrics,
-        size: &SizeInfo,
-        flag: Flags,
-        start: Point,
-        end: Point,
-        color: Rgb,
-    ) -> RenderRect {
-        let start_x = start.col.0 as f32 * size.cell_width;
-        let end_x = (end.col.0 + 1) as f32 * size.cell_width;
-        let width = end_x - start_x;
-
-        let (position, mut height) = match flag {
-            Flags::UNDERLINE => (metrics.underline_position, metrics.underline_thickness),
-            Flags::STRIKEOUT => (metrics.strikeout_position, metrics.strikeout_thickness),
-            _ => unimplemented!("Invalid flag for cell line drawing specified"),
-        };
-
-        // Make sure lines are always visible
-        height = height.max(1.);
-
-        let line_bottom = (start.line.0 as f32 + 1.) * size.cell_height;
-        let baseline = line_bottom + metrics.descent;
-
-        let mut y = (baseline - position - height / 2.).ceil();
-        let max_y = line_bottom - height;
-        if y > max_y {
-            y = max_y;
-        }
-
-        RenderRect::new(start_x + size.padding_x, y + size.padding_y, width, height, color, 1.)
-    }
-}
-
-/// Lines for underline and strikeout.
-#[derive(Default)]
-pub struct RenderLines {
-    inner: HashMap<Flags, Vec<RenderLine>>,
-}
-
-impl RenderLines {
-    pub fn new() -> Self {
-        Self::default()
-    }
-
-    pub fn rects(&self, metrics: &Metrics, size: &SizeInfo) -> Vec<RenderRect> {
-        self.inner
-            .iter()
-            .map(|(flag, lines)| -> Vec<RenderRect> {
-                lines.iter().map(|line| line.rects(*flag, metrics, size)).flatten().collect()
-            })
-            .flatten()
-            .collect()
-    }
-
-    /// Update the stored lines with the next cell info.
-    pub fn update(&mut self, cell: RenderableCell) {
-        for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] {
-            if !cell.flags.contains(*flag) {
-                continue;
-            }
-
-            // Check if there's an active line
-            if let Some(line) = self.inner.get_mut(flag).and_then(|lines| lines.last_mut()) {
-                if cell.fg == line.color
-                    && cell.column == line.end.col + 1
-                    && cell.line == line.end.line
-                {
-                    // Update the length of the line
-                    line.end = cell.into();
-                    continue;
-                }
-            }
-
-            // Start new line if there currently is none
-            let line = RenderLine { start: cell.into(), end: cell.into(), color: cell.fg };
-            match self.inner.get_mut(flag) {
-                Some(lines) => lines.push(line),
-                None => {
-                    self.inner.insert(*flag, vec![line]);
-                },
-            }
-        }
-    }
-}
diff --git a/alacritty/src/url.rs b/alacritty/src/url.rs
deleted file mode 100644
index 4c5aed2..0000000
--- a/alacritty/src/url.rs
+++ /dev/null
@@ -1,252 +0,0 @@
-use std::cmp::min;
-use std::mem;
-
-use glutin::event::{ElementState, ModifiersState};
-use urlocator::{UrlLocation, UrlLocator};
-
-use font::Metrics;
-
-use alacritty_terminal::index::Point;
-use alacritty_terminal::term::cell::Flags;
-use alacritty_terminal::term::color::Rgb;
-use alacritty_terminal::term::{RenderableCell, RenderableCellContent, SizeInfo};
-
-use crate::config::Config;
-use crate::event::Mouse;
-use crate::renderer::rects::{RenderLine, RenderRect};
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct Url {
-    lines: Vec<RenderLine>,
-    end_offset: u16,
-    num_cols: usize,
-}
-
-impl Url {
-    pub fn rects(&self, metrics: &Metrics, size: &SizeInfo) -> Vec<RenderRect> {
-        let end = self.end();
-        self.lines
-            .iter()
-            .filter(|line| line.start <= end)
-            .map(|line| {
-                let mut rect_line = *line;
-                rect_line.end = min(line.end, end);
-                rect_line.rects(Flags::UNDERLINE, metrics, size)
-            })
-            .flatten()
-            .collect()
-    }
-
-    pub fn start(&self) -> Point {
-        self.lines[0].start
-    }
-
-    pub fn end(&self) -> Point {
-        self.lines[self.lines.len() - 1].end.sub(self.num_cols, self.end_offset as usize, false)
-    }
-}
-
-pub struct Urls {
-    locator: UrlLocator,
-    urls: Vec<Url>,
-    scheme_buffer: Vec<RenderableCell>,
-    last_point: Option<Point>,
-    state: UrlLocation,
-}
-
-impl Default for Urls {
-    fn default() -> Self {
-        Self {
-            locator: UrlLocator::new(),
-            scheme_buffer: Vec::new(),
-            urls: Vec::new(),
-            state: UrlLocation::Reset,
-            last_point: None,
-        }
-    }
-}
-
-impl Urls {
-    pub fn new() -> Self {
-        Self::default()
-    }
-
-    // Update tracked URLs
-    pub fn update(&mut self, num_cols: usize, cell: RenderableCell) {
-        // Convert cell to character
-        let c = match cell.inner {
-            RenderableCellContent::Chars(chars) => chars[0],
-            RenderableCellContent::Cursor(_) => return,
-        };
-
-        let point: Point = cell.into();
-        let end = point;
-
-        // Reset URL when empty cells have been skipped
-        if point != Point::default() && Some(point.sub(num_cols, 1, false)) != self.last_point {
-            self.reset();
-        }
-
-        self.last_point = Some(end);
-
-        // Extend current state if a wide char spacer is encountered
-        if cell.flags.contains(Flags::WIDE_CHAR_SPACER) {
-            if let UrlLocation::Url(_, mut end_offset) = self.state {
-                if end_offset != 0 {
-                    end_offset += 1;
-                }
-
-                self.extend_url(point, end, cell.fg, end_offset);
-            }
-
-            return;
-        }
-
-        // Advance parser
-        let last_state = mem::replace(&mut self.state, self.locator.advance(c));
-        match (self.state, last_state) {
-            (UrlLocation::Url(_length, end_offset), UrlLocation::Scheme) => {
-                // Create empty URL
-                self.urls.push(Url { lines: Vec::new(), end_offset, num_cols });
-
-                // Push schemes into URL
-                for scheme_cell in self.scheme_buffer.split_off(0) {
-                    let point = scheme_cell.into();
-                    self.extend_url(point, point, scheme_cell.fg, end_offset);
-                }
-
-                // Push the new cell into URL
-                self.extend_url(point, end, cell.fg, end_offset);
-            },
-            (UrlLocation::Url(_length, end_offset), UrlLocation::Url(..)) => {
-                self.extend_url(point, end, cell.fg, end_offset);
-            },
-            (UrlLocation::Scheme, _) => self.scheme_buffer.push(cell),
-            (UrlLocation::Reset, _) => self.reset(),
-            _ => (),
-        }
-
-        // Reset at un-wrapped linebreak
-        if cell.column.0 + 1 == num_cols && !cell.flags.contains(Flags::WRAPLINE) {
-            self.reset();
-        }
-    }
-
-    // Extend the last URL
-    fn extend_url(&mut self, start: Point, end: Point, color: Rgb, end_offset: u16) {
-        let url = self.urls.last_mut().unwrap();
-
-        // If color changed, we need to insert a new line
-        if url.lines.last().map(|last| last.color) == Some(color) {
-            url.lines.last_mut().unwrap().end = end;
-        } else {
-            url.lines.push(RenderLine { color, start, end });
-        }
-
-        // Update excluded cells at the end of the URL
-        url.end_offset = end_offset;
-    }
-
-    pub fn highlighted(
-        &self,
-        config: &Config,
-        mouse: &Mouse,
-        mods: ModifiersState,
-        mouse_mode: bool,
-        selection: bool,
-    ) -> Option<Url> {
-        // Require additional shift in mouse mode
-        let mut required_mods = config.ui_config.mouse.url.mods();
-        if mouse_mode {
-            required_mods |= ModifiersState::SHIFT;
-        }
-
-        // Make sure all prerequisites for highlighting are met
-        if selection
-            || !mouse.inside_grid
-            || config.ui_config.mouse.url.launcher.is_none()
-            || required_mods != mods
-            || mouse.left_button_state == ElementState::Pressed
-        {
-            return None;
-        }
-
-        for url in &self.urls {
-            if (url.start()..=url.end()).contains(&Point::new(mouse.line, mouse.column)) {
-                return Some(url.clone());
-            }
-        }
-
-        None
-    }
-
-    fn reset(&mut self) {
-        self.locator = UrlLocator::new();
-        self.state = UrlLocation::Reset;
-        self.scheme_buffer.clear();
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-
-    use alacritty_terminal::index::{Column, Line};
-    use alacritty_terminal::term::cell::MAX_ZEROWIDTH_CHARS;
-
-    fn text_to_cells(text: &str) -> Vec<RenderableCell> {
-        text.chars()
-            .enumerate()
-            .map(|(i, c)| RenderableCell {
-                inner: RenderableCellContent::Chars([c; MAX_ZEROWIDTH_CHARS + 1]),
-                line: Line(0),
-                column: Column(i),
-                fg: Default::default(),
-                bg: Default::default(),
-                bg_alpha: 0.,
-                flags: Flags::empty(),
-            })
-            .collect()
-    }
-
-    #[test]
-    fn multi_color_url() {
-        let mut input = text_to_cells("test https://example.org ing");
-        let num_cols = input.len();
-
-        input[10].fg = Rgb { r: 0xff, g: 0x00, b: 0xff };
-
-        let mut urls = Urls::new();
-
-        for cell in input {
-            urls.update(num_cols, cell);
-        }
-
-        let url = urls.urls.first().unwrap();
-        assert_eq!(url.start().col, Column(5));
-        assert_eq!(url.end().col, Column(23));
-    }
-
-    #[test]
-    fn multiple_urls() {
-        let input = text_to_cells("test git:a git:b git:c ing");
-        let num_cols = input.len();
-
-        let mut urls = Urls::new();
-
-        for cell in input {
-            urls.update(num_cols, cell);
-        }
-
-        assert_eq!(urls.urls.len(), 3);
-
-        assert_eq!(urls.urls[0].start().col, Column(5));
-        assert_eq!(urls.urls[0].end().col, Column(9));
-
-        assert_eq!(urls.urls[1].start().col, Column(11));
-        assert_eq!(urls.urls[1].end().col, Column(15));
-
-        assert_eq!(urls.urls[2].start().col, Column(17));
-        assert_eq!(urls.urls[2].end().col, Column(21));
-    }
-}
diff --git a/alacritty/src/window.rs b/alacritty/src/window.rs
deleted file mode 100644
index 4d1a8ea..0000000
--- a/alacritty/src/window.rs
+++ /dev/null
@@ -1,427 +0,0 @@
-// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-use std::convert::From;
-#[cfg(not(any(target_os = "macos", windows)))]
-use std::ffi::c_void;
-use std::fmt::{self, Display, Formatter};
-#[cfg(not(any(target_os = "macos", windows)))]
-use std::os::raw::c_ulong;
-
-use glutin::dpi::{PhysicalPosition, PhysicalSize};
-use glutin::event_loop::EventLoop;
-#[cfg(target_os = "macos")]
-use glutin::platform::macos::{RequestUserAttentionType, WindowBuilderExtMacOS, WindowExtMacOS};
-#[cfg(not(any(target_os = "macos", windows)))]
-use glutin::platform::unix::{EventLoopWindowTargetExtUnix, WindowBuilderExtUnix, WindowExtUnix};
-#[cfg(not(target_os = "macos"))]
-use glutin::window::Icon;
-use glutin::window::{CursorIcon, Fullscreen, Window as GlutinWindow, WindowBuilder, WindowId};
-use glutin::{self, ContextBuilder, PossiblyCurrent, WindowedContext};
-#[cfg(not(target_os = "macos"))]
-use image::ImageFormat;
-#[cfg(not(any(target_os = "macos", windows)))]
-use x11_dl::xlib::{Display as XDisplay, PropModeReplace, XErrorEvent, Xlib};
-
-use alacritty_terminal::config::{Decorations, StartupMode, WindowConfig};
-use alacritty_terminal::event::Event;
-#[cfg(not(windows))]
-use alacritty_terminal::term::{SizeInfo, Term};
-
-use crate::config::Config;
-use crate::gl;
-
-// It's required to be in this directory due to the `windows.rc` file
-#[cfg(not(target_os = "macos"))]
-static WINDOW_ICON: &[u8] = include_bytes!("../../extra/windows/alacritty.ico");
-
-/// Window errors
-#[derive(Debug)]
-pub enum Error {
-    /// Error creating the window
-    ContextCreation(glutin::CreationError),
-
-    /// Error dealing with fonts
-    Font(font::Error),
-
-    /// Error manipulating the rendering context
-    Context(glutin::ContextError),
-}
-
-/// Result of fallible operations concerning a Window.
-type Result<T> = std::result::Result<T, Error>;
-
-impl std::error::Error for Error {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        match self {
-            Error::ContextCreation(err) => err.source(),
-            Error::Context(err) => err.source(),
-            Error::Font(err) => err.source(),
-        }
-    }
-}
-
-impl Display for Error {
-    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
-        match self {
-            Error::ContextCreation(err) => write!(f, "Error creating GL context; {}", err),
-            Error::Context(err) => write!(f, "Error operating on render context; {}", err),
-            Error::Font(err) => err.fmt(f),
-        }
-    }
-}
-
-impl From<glutin::CreationError> for Error {
-    fn from(val: glutin::CreationError) -> Self {
-        Error::ContextCreation(val)
-    }
-}
-
-impl From<glutin::ContextError> for Error {
-    fn from(val: glutin::ContextError) -> Self {
-        Error::Context(val)
-    }
-}
-
-impl From<font::Error> for Error {
-    fn from(val: font::Error) -> Self {
-        Error::Font(val)
-    }
-}
-
-fn create_gl_window(
-    mut window: WindowBuilder,
-    event_loop: &EventLoop<Event>,
-    srgb: bool,
-    dimensions: Option<PhysicalSize<u32>>,
-) -> Result<WindowedContext<PossiblyCurrent>> {
-    if let Some(dimensions) = dimensions {
-        window = window.with_inner_size(dimensions);
-    }
-
-    let windowed_context = ContextBuilder::new()
-        .with_srgb(srgb)
-        .with_vsync(true)
-        .with_hardware_acceleration(None)
-        .build_windowed(window, event_loop)?;
-
-    // Make the context current so OpenGL operations can run
-    let windowed_context = unsafe { windowed_context.make_current().map_err(|(_, err)| err)? };
-
-    Ok(windowed_context)
-}
-
-/// A window which can be used for displaying the terminal
-///
-/// Wraps the underlying windowing library to provide a stable API in Alacritty
-pub struct Window {
-    windowed_context: WindowedContext<PossiblyCurrent>,
-    current_mouse_cursor: CursorIcon,
-    mouse_visible: bool,
-}
-
-impl Window {
-    /// Create a new window
-    ///
-    /// This creates a window and fully initializes a window.
-    pub fn new(
-        event_loop: &EventLoop<Event>,
-        config: &Config,
-        size: Option<PhysicalSize<u32>>,
-    ) -> Result<Window> {
-        let window_builder = Window::get_platform_window(&config.window.title, &config.window);
-        let windowed_context =
-            create_gl_window(window_builder.clone(), &event_loop, false, size)
-                .or_else(|_| create_gl_window(window_builder, &event_loop, true, size))?;
-
-        // Text cursor
-        let current_mouse_cursor = CursorIcon::Text;
-        windowed_context.window().set_cursor_icon(current_mouse_cursor);
-
-        // Set OpenGL symbol loader. This call MUST be after window.make_current on windows.
-        gl::load_with(|symbol| windowed_context.get_proc_address(symbol) as *const _);
-
-        // On X11, embed the window inside another if the parent ID has been set
-        #[cfg(not(any(target_os = "macos", windows)))]
-        {
-            if event_loop.is_x11() {
-                if let Some(parent_window_id) = config.window.embed {
-                    x_embed_window(windowed_context.window(), parent_window_id);
-                }
-            }
-        }
-
-        Ok(Self { current_mouse_cursor, mouse_visible: true, windowed_context })
-    }
-
-    pub fn set_inner_size(&mut self, size: PhysicalSize<u32>) {
-        self.window().set_inner_size(size);
-    }
-
-    pub fn inner_size(&self) -> PhysicalSize<u32> {
-        self.window().inner_size()
-    }
-
-    pub fn scale_factor(&self) -> f64 {
-        self.window().scale_factor()
-    }
-
-    #[inline]
-    pub fn set_visible(&self, visibility: bool) {
-        self.window().set_visible(visibility);
-    }
-
-    /// Set the window title
-    #[inline]
-    pub fn set_title(&self, title: &str) {
-        self.window().set_title(title);
-    }
-
-    #[inline]
-    pub fn set_mouse_cursor(&mut self, cursor: CursorIcon) {
-        if cursor != self.current_mouse_cursor {
-            self.current_mouse_cursor = cursor;
-            self.window().set_cursor_icon(cursor);
-        }
-    }
-
-    /// Set mouse cursor visible
-    pub fn set_mouse_visible(&mut self, visible: bool) {
-        if visible != self.mouse_visible {
-            self.mouse_visible = visible;
-            self.window().set_cursor_visible(visible);
-        }
-    }
-
-    #[cfg(not(any(target_os = "macos", windows)))]
-    pub fn get_platform_window(title: &str, window_config: &WindowConfig) -> WindowBuilder {
-        let decorations = match window_config.decorations {
-            Decorations::None => false,
-            _ => true,
-        };
-
-        let image = image::load_from_memory_with_format(WINDOW_ICON, ImageFormat::ICO)
-            .expect("loading icon")
-            .to_rgba();
-        let (width, height) = image.dimensions();
-        let icon = Icon::from_rgba(image.into_raw(), width, height);
-
-        let class = &window_config.class;
-
-        let mut builder = WindowBuilder::new()
-            .with_title(title)
-            .with_visible(false)
-            .with_transparent(true)
-            .with_decorations(decorations)
-            .with_maximized(window_config.startup_mode() == StartupMode::Maximized)
-            .with_window_icon(icon.ok())
-            // X11
-            .with_class(class.instance.clone(), class.general.clone())
-            // Wayland
-            .with_app_id(class.instance.clone());
-
-        if let Some(ref val) = window_config.gtk_theme_variant {
-            builder = builder.with_gtk_theme_variant(val.clone())
-        }
-
-        builder
-    }
-
-    #[cfg(windows)]
-    pub fn get_platform_window(title: &str, window_config: &WindowConfig) -> WindowBuilder {
-        let decorations = match window_config.decorations {
-            Decorations::None => false,
-            _ => true,
-        };
-
-        let image = image::load_from_memory_with_format(WINDOW_ICON, ImageFormat::ICO)
-            .expect("loading icon")
-            .to_rgba();
-        let (width, height) = image.dimensions();
-        let icon = Icon::from_rgba(image.into_raw(), width, height);
-
-        WindowBuilder::new()
-            .with_title(title)
-            .with_visible(true)
-            .with_decorations(decorations)
-            .with_transparent(true)
-            .with_maximized(window_config.startup_mode() == StartupMode::Maximized)
-            .with_window_icon(icon.ok())
-    }
-
-    #[cfg(target_os = "macos")]
-    pub fn get_platform_window(title: &str, window_config: &WindowConfig) -> WindowBuilder {
-        let window = WindowBuilder::new()
-            .with_title(title)
-            .with_visible(false)
-            .with_transparent(true)
-            .with_maximized(window_config.startup_mode() == StartupMode::Maximized);
-
-        match window_config.decorations {
-            Decorations::Full => window,
-            Decorations::Transparent => window
-                .with_title_hidden(true)
-                .with_titlebar_transparent(true)
-                .with_fullsize_content_view(true),
-            Decorations::Buttonless => window
-                .with_title_hidden(true)
-                .with_titlebar_buttons_hidden(true)
-                .with_titlebar_transparent(true)
-                .with_fullsize_content_view(true),
-            Decorations::None => window.with_titlebar_hidden(true),
-        }
-    }
-
-    #[cfg(not(any(target_os = "macos", windows)))]
-    pub fn set_urgent(&self, is_urgent: bool) {
-        self.window().set_urgent(is_urgent);
-    }
-
-    #[cfg(target_os = "macos")]
-    pub fn set_urgent(&self, is_urgent: bool) {
-        if !is_urgent {
-            return;
-        }
-
-        self.window().request_user_attention(RequestUserAttentionType::Critical);
-    }
-
-    #[cfg(windows)]
-    pub fn set_urgent(&self, _is_urgent: bool) {}
-
-    pub fn set_outer_position(&self, pos: PhysicalPosition<u32>) {
-        self.window().set_outer_position(pos);
-    }
-
-    #[cfg(not(any(target_os = "macos", windows)))]
-    pub fn x11_window_id(&self) -> Option<usize> {
-        self.window().xlib_window().map(|xlib_window| xlib_window as usize)
-    }
-
-    pub fn window_id(&self) -> WindowId {
-        self.window().id()
-    }
-
-    #[cfg(not(any(target_os = "macos", windows)))]
-    pub fn set_maximized(&self, maximized: bool) {
-        self.window().set_maximized(maximized);
-    }
-
-    pub fn set_minimized(&self, minimized: bool) {
-        self.window().set_minimized(minimized);
-    }
-
-    /// Toggle the window's fullscreen state
-    pub fn toggle_fullscreen(&mut self) {
-        self.set_fullscreen(self.window().fullscreen().is_none());
-    }
-
-    #[cfg(target_os = "macos")]
-    pub fn toggle_simple_fullscreen(&mut self) {
-        self.set_simple_fullscreen(!self.window().simple_fullscreen());
-    }
-
-    pub fn set_fullscreen(&mut self, fullscreen: bool) {
-        #[cfg(macos)]
-        {
-            if self.window().simple_fullscreen() {
-                return;
-            }
-        }
-
-        if fullscreen {
-            let current_monitor = self.window().current_monitor();
-            self.window().set_fullscreen(Some(Fullscreen::Borderless(current_monitor)));
-        } else {
-            self.window().set_fullscreen(None);
-        }
-    }
-
-    #[cfg(target_os = "macos")]
-    pub fn set_simple_fullscreen(&mut self, simple_fullscreen: bool) {
-        if self.window().fullscreen().is_some() {
-            return;
-        }
-
-        self.window().set_simple_fullscreen(simple_fullscreen);
-    }
-
-    #[cfg(not(any(target_os = "macos", target_os = "windows")))]
-    pub fn wayland_display(&self) -> Option<*mut c_void> {
-        self.window().wayland_display()
-    }
-
-    /// Adjust the IME editor position according to the new location of the cursor
-    #[cfg(not(windows))]
-    pub fn update_ime_position<T>(&mut self, terminal: &Term<T>, size_info: &SizeInfo) {
-        let point = terminal.cursor().point;
-        let SizeInfo { cell_width, cell_height, padding_x, padding_y, .. } = size_info;
-
-        let nspot_x = f64::from(padding_x + point.col.0 as f32 * cell_width);
-        let nspot_y = f64::from(padding_y + (point.line.0 + 1) as f32 * cell_height);
-
-        self.window().set_ime_position(PhysicalPosition::new(nspot_x, nspot_y));
-    }
-
-    pub fn swap_buffers(&self) {
-        self.windowed_context.swap_buffers().expect("swap buffers");
-    }
-
-    pub fn resize(&self, size: PhysicalSize<u32>) {
-        self.windowed_context.resize(size);
-    }
-
-    fn window(&self) -> &GlutinWindow {
-        self.windowed_context.window()
-    }
-}
-
-#[cfg(not(any(target_os = "macos", windows)))]
-fn x_embed_window(window: &GlutinWindow, parent_id: c_ulong) {
-    let (xlib_display, xlib_window) = match (window.xlib_display(), window.xlib_window()) {
-        (Some(display), Some(window)) => (display, window),
-        _ => return,
-    };
-
-    let xlib = Xlib::open().expect("get xlib");
-
-    unsafe {
-        let atom = (xlib.XInternAtom)(xlib_display as *mut _, "_XEMBED".as_ptr() as *const _, 0);
-        (xlib.XChangeProperty)(
-            xlib_display as _,
-            xlib_window as _,
-            atom,
-            atom,
-            32,
-            PropModeReplace,
-            [0, 1].as_ptr(),
-            2,
-        );
-
-        // Register new error handler
-        let old_handler = (xlib.XSetErrorHandler)(Some(xembed_error_handler));
-
-        // Check for the existence of the target before attempting reparenting
-        (xlib.XReparentWindow)(xlib_display as _, xlib_window as _, parent_id, 0, 0);
-
-        // Drain errors and restore original error handler
-        (xlib.XSync)(xlib_display as _, 0);
-        (xlib.XSetErrorHandler)(old_handler);
-    }
-}
-
-#[cfg(not(any(target_os = "macos", windows)))]
-unsafe extern "C" fn xembed_error_handler(_: *mut XDisplay, _: *mut XErrorEvent) -> i32 {
-    println!("Could not embed into specified window.");
-    std::process::exit(1);
-}