From 2d59156dff16e32343ef684c3d1735424090d4df Mon Sep 17 00:00:00 2001 From: hack Date: Fri, 15 Mar 2019 18:14:53 -0700 Subject: [PATCH 01/84] Add harfbuzz-freetype support Doesn't render properly yet, though. --- Cargo.lock | 355 ++++++++++++------------- Cargo.toml | 2 +- alacritty_terminal/Cargo.toml | 2 + alacritty_terminal/src/display.rs | 126 ++++++++- alacritty_terminal/src/renderer/mod.rs | 39 ++- alacritty_terminal/src/term/mod.rs | 14 +- font/Cargo.toml | 6 + font/src/ft/mod.rs | 97 ++++++- font/src/lib.rs | 59 ++-- 9 files changed, 489 insertions(+), 211 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index efdce31cd3..115a0866d3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,7 +30,7 @@ dependencies = [ "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_tools_util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -54,8 +54,8 @@ dependencies = [ "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "mio-anonymous-pipes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -65,11 +65,11 @@ dependencies = [ "notify 4.0.12 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", - "signal-hook 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "signal-hook 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "terminfo 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -131,7 +131,7 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -139,38 +139,36 @@ dependencies = [ [[package]] name = "atty" -version = "0.2.11" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "autocfg" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.30" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "backtrace-sys 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace-sys" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -192,7 +190,7 @@ dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -209,7 +207,7 @@ name = "blake2-rfc" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -229,7 +227,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -238,7 +236,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -276,8 +274,8 @@ name = "cgl" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gleam 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -286,7 +284,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -296,7 +294,7 @@ version = "2.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -338,7 +336,7 @@ dependencies = [ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -373,7 +371,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -389,18 +387,18 @@ dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-text" -version = "13.2.0" +version = "13.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -434,7 +432,7 @@ name = "crossbeam-epoch" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -474,7 +472,7 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -483,7 +481,7 @@ name = "dirs" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -517,9 +515,9 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -542,9 +540,9 @@ name = "env_logger" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -554,9 +552,9 @@ name = "env_logger" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -567,7 +565,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -577,7 +575,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -595,7 +593,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -613,7 +611,7 @@ name = "failure" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -623,7 +621,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -634,7 +632,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -650,13 +648,14 @@ dependencies = [ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-text 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-text 13.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.9 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype-rs 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "harfbuzz 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -683,7 +682,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -697,6 +696,15 @@ name = "foreign-types-shared" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "freetype" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "servo-freetype-sys 4.0.3", +] + [[package]] name = "freetype-rs" version = "0.19.1" @@ -704,7 +712,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype-sys 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -712,7 +720,7 @@ name = "freetype-sys" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -731,7 +739,7 @@ name = "fsevent-sys" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -764,7 +772,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -782,13 +790,13 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gleam" -version = "0.6.17" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -865,9 +873,30 @@ dependencies = [ "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "harfbuzz" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "harfbuzz-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "harfbuzz-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cmake 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-text 13.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "http_req" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -924,7 +953,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -932,7 +961,7 @@ name = "inotify-sys" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -940,7 +969,7 @@ name = "iovec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -987,7 +1016,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.59" +version = "0.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1017,7 +1046,7 @@ version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1046,7 +1075,7 @@ dependencies = [ [[package]] name = "log" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1062,7 +1091,7 @@ name = "malloc_buf" version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1075,7 +1104,7 @@ name = "memchr" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1088,7 +1117,7 @@ name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1106,8 +1135,8 @@ dependencies = [ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1131,7 +1160,7 @@ version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1141,22 +1170,12 @@ name = "mio-named-pipes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "mio-uds" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "miow" version = "0.2.1" @@ -1192,8 +1211,8 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1209,7 +1228,7 @@ version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1221,7 +1240,7 @@ dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1258,7 +1277,7 @@ dependencies = [ "fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1271,7 +1290,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1280,7 +1299,7 @@ name = "num-integer" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1289,7 +1308,7 @@ name = "num-iter" version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1299,7 +1318,7 @@ name = "num-rational" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1309,7 +1328,7 @@ name = "num-traits" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1317,14 +1336,9 @@ name = "num_cpus" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "numtoa" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "objc" version = "0.2.6" @@ -1360,7 +1374,7 @@ dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1374,9 +1388,9 @@ name = "openssl-sys" version = "0.9.47" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1419,7 +1433,7 @@ name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1516,7 +1530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1527,8 +1541,8 @@ name = "rand" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1546,7 +1560,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1557,7 +1571,7 @@ name = "rand_chacha" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1566,7 +1580,7 @@ name = "rand_chacha" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1621,7 +1635,7 @@ name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1633,7 +1647,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1644,7 +1658,7 @@ name = "rand_pcg" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1691,14 +1705,6 @@ name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "redox_termios" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "redox_users" version = "0.3.0" @@ -1787,7 +1793,7 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1799,7 +1805,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "same-file" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1831,7 +1837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1858,16 +1864,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.94" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.94" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1878,7 +1884,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1888,7 +1894,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1897,7 +1903,7 @@ name = "servo-fontconfig" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "servo-fontconfig-sys 4.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1924,17 +1930,16 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "signal-hook" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", - "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "signal-hook-registry 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1944,7 +1949,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arc-swap 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2008,7 +2013,7 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2052,7 +2057,7 @@ version = "0.15.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2062,7 +2067,7 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2078,7 +2083,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2104,17 +2109,6 @@ dependencies = [ "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "termion" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -2147,7 +2141,7 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2236,7 +2230,7 @@ name = "vswhom" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "vswhom-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2246,7 +2240,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2262,7 +2256,7 @@ name = "walkdir" version = "2.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2274,7 +2268,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "downcast-rs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-scanner 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2288,7 +2282,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "downcast-rs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-commons 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-scanner 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2342,7 +2336,7 @@ version = "0.21.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2352,7 +2346,7 @@ version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2379,7 +2373,7 @@ name = "which" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2439,15 +2433,15 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2463,7 +2457,7 @@ version = "0.1.0" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "embed-resource 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "http_req 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "http_req 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "named_pipe 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2512,7 +2506,7 @@ version = "2.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2521,8 +2515,8 @@ name = "xcb" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2565,11 +2559,11 @@ dependencies = [ "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" "checksum arc-swap 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "bc4662175ead9cd84451d5c35070517777949a2ed84551764129cedb88384841" "checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" -"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" -"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" -"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" -"checksum backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)" = "ada4c783bb7e7443c14e0480f429ae2cc99da95065aeab7ee1b81ada0419404f" -"checksum backtrace-sys 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "5b3a000b9c543553af61bc01cbfc403b04b5caa9e421033866f2e98061eb3e61" +"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" +"checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b" +"checksum backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)" = "88fb679bc9af8fa639198790a77f52d345fe13656c08b43afa9424c206b731c6" +"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum bindgen 0.33.2 (registry+https://github.com/rust-lang/crates.io-index)" = "603ed8d8392ace9581e834e26bd09799bf1e989a79bd1aedbb893e72962bdc6e" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" @@ -2594,7 +2588,7 @@ dependencies = [ "checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" "checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" -"checksum core-text 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d95a72b5e50e549969dd88eff3047495fe5b8c6f028635442c2b708be707e669" +"checksum core-text 13.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12684243b314c95600a2b49628fb775f91d97bbe18424522f665b77014f2a640" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" "checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" @@ -2627,6 +2621,7 @@ dependencies = [ "checksum foreign-types-macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "66626064d64b429efe601d7aabd7b0a9721b2611548ca684e78c4ecde48bd29b" "checksum foreign-types-shared 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" "checksum foreign-types-shared 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c61f9b85573bf0f203eed3633f5018abce85250886a62ca073e0eee022ed564d" +"checksum freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "11926b2b410b469d0e9399eca4cbbe237a9ef02176c485803b29216307e8e028" "checksum freetype-rs 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "28cc92a7040ee7b631e4279e263f9a83aedc1eb6085c68d8ca4d072b5644e705" "checksum freetype-sys 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9c8666cce7cf6e51a290623647febfbab92480b4c3e0f495cb9d4d312b5d38" "checksum fsevent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5ab7d1bd1bd33cc98b0889831b72da23c0aa4df9cec7e0702f46ecea04b35db6" @@ -2638,7 +2633,7 @@ dependencies = [ "checksum getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e65cce4e5084b14874c4e7097f38cab54f47ee554f9194673456ea379dcc4c55" "checksum gif 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "86c2f2b597d6e05c86ee5947b2223bda468fe8dad3e88e2a6520869322aaf568" "checksum gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39a23d5e872a275135d66895d954269cf5e8661d234eb1c2480f4ce0d586acbd" -"checksum gleam 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7f46fd8874e043ffac0d638ed1567a2584f7814f6d72b4db37ab1689004a26c4" +"checksum gleam 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "c8a455b5a3ccd35daeb89fdb8a89ebb0a1fe23c05c7a7f9017840bc3ae176f71" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum glutin 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb26027a84c3b9e1949ef0df0b6a3db8d0c124243a5c161ea25c7def90cb1474" "checksum glutin_egl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "23f48987ab6cb2b61ad903b59e54a2fd0c380a7baff68cffd6826b69a73dd326" @@ -2646,7 +2641,9 @@ dependencies = [ "checksum glutin_gles2_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "89996c30857ae1b4de4b5189abf1ea822a20a9fe9e1c93e5e7b862ff0bdd5cdf" "checksum glutin_glx_sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1290a5ca5e46fcfa7f66f949cc9d9194b2cb6f2ed61892c8c2b82343631dba57" "checksum glutin_wgl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f801bbc91efc22dd1c4818a47814fc72bf74d024510451b119381579bfa39021" -"checksum http_req 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06dc3c469026e12585cb2b783bbfdd853386907558a5e1b86c4c127d50167806" +"checksum harfbuzz 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46f7426266a5ece3e49eae6f48e602c0f8c39917354a847eac9c06437dcde8da" +"checksum harfbuzz-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e1042ab0b3e7bc1ff64f7f5935778b644ff2194a1cae5ec52167127d3fd23961" +"checksum http_req 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b7053e4a7b90e97e3c207f8293dc4a6e7f1d49156470b94f3685f1baa48f65a" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)" = "99198e595d012efccf12abf4abc08da2d97be0b0355a2b08d101347527476ba4" @@ -2660,14 +2657,14 @@ dependencies = [ "checksum khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "3262021842bf00fe07dbd6cf34ff25c99d7a7ebef8deea84db72be3ea3bb0aff" +"checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" "checksum libflate 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "90c6f86f4b0caa347206f916f8b687b51d77c6ef8ff18d52dd007491fd580529" "checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" "checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" "checksum line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" -"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" +"checksum log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c275b6ad54070ac2d665eef9197db647b32239c9d244bfb6f041a766d00da5b3" "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" "checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" @@ -2679,7 +2676,6 @@ dependencies = [ "checksum mio-anonymous-pipes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8c274c3c52dcd1d78c5d7ed841eca1e9ea2db8353f3b8ec25789cc62c471aaf" "checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" "checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" -"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" "checksum named_pipe 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ed10a5ac4f5f7e5d75552b12c1d5d542debca81e573279dd1e4c19fde6efa6d" @@ -2696,7 +2692,6 @@ dependencies = [ "checksum num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" -"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" "checksum objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "31d20fd2b37e07cf5125be68357b588672e8cefe9a96f8c17a9d46053b3e590d" "checksum objc-foundation 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" "checksum objc_id 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" @@ -2721,7 +2716,7 @@ dependencies = [ "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" +"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" @@ -2740,7 +2735,6 @@ dependencies = [ "checksum rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe0df8435ac0c397d467b6cad6d25543d06e8a019ef3f6af3c384597515bd2" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" -"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828" "checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" "checksum regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d8297cc20bbb6184f8b45ff61c8ee6a9ac56c156cec8e38c3e5084773c44ad" @@ -2753,7 +2747,7 @@ dependencies = [ "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rusttype 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "654103d61a05074b268a107cf6581ce120f0fc0115f2610ed9dfea363bb81139" "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" -"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" +"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" "checksum schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" @@ -2761,14 +2755,14 @@ dependencies = [ "checksum security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "076a696fdea89c19d3baed462576b8f6d663064414b5c793642da8dfeb99475b" -"checksum serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "ef45eb79d6463b22f5f9e16d283798b7c0175ba6050bc25c1a946c122727fe7b" +"checksum serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)" = "d46b3dfedb19360a74316866cef04687cd4d6a70df8e6a506c63512790769b72" +"checksum serde_derive 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)" = "c22a0820adfe2f257b098714323563dd06426502abbbce4f51b72ef544c5027f" "checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" "checksum serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "38b08a9a90e5260fe01c6480ec7c811606df6d3a660415808c3c3fa8ed95b582" "checksum servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a088f8d775a5c5314aae09bd77340bc9c67d72b9a45258be34c83548b4814cd9" "checksum servo-fontconfig-sys 4.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b46d201addcfbd25c1798ad1281d98c40743824e0b0f1e611bd3d5d0d31a7b8d" "checksum shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" -"checksum signal-hook 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "72ab58f1fda436857e6337dcb6a5aaa34f16c5ddc87b3a8b6ef7a212f90b9c5a" +"checksum signal-hook 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4f61c4d59f3aaa9f61bba6450a9b80ba48362fd7d651689e7a10c453b1f6dc68" "checksum signal-hook-registry 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cded4ffa32146722ec54ab1f16320568465aa922aa9ab4708129599740da85d7" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" @@ -2789,7 +2783,6 @@ dependencies = [ "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" "checksum terminfo 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8e51065bafd2abe106b6036483b69d1741f4a1ec56ce8a2378de341637de689e" -"checksum termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8fb22f7cde82c8220e5aeacb3258ed7ce996142c77cba193f203515e26c330" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum tiff 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4834f28a0330cb9f3f2c87d2649dca723cb33802e2bdcf18da32759fbec7ce" diff --git a/Cargo.toml b/Cargo.toml index 2e8fd4dbbf..9995755bd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,4 @@ debug = 1 incremental = false [patch.crates-io] -servo-freetype-sys = { path = "servo-freetype-proxy" } +servo-freetype-sys = { path = "servo-freetype-proxy" } \ No newline at end of file diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index dd3f195983..671eec3f34 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -61,6 +61,8 @@ default = [] live-shader-reload = [] nightly = [] bench = [] +# Use FreeType+FontConfig on all platforms, and enable HarfBuzz text shaping +hb-ft = ["font/hb-ft"] [build-dependencies] gl_generator = "0.11.0" diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 4a2e57c4e1..374069dc12 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -31,9 +31,11 @@ use crate::renderer::rects::{Rect, Rects}; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::sync::FairMutex; use crate::term::color::Rgb; -use crate::term::{RenderableCell, SizeInfo, Term}; +use crate::term::{RenderableCell, RenderableCellContent, SizeInfo, Term}; use crate::window::{self, Window}; use font::{self, Rasterize}; +#[cfg(feature = "hb-ft")] +use font::{HbFtExt, HbGlyph}; #[derive(Debug)] pub enum Error { @@ -494,6 +496,9 @@ impl Display { } } + let g_lines = terminal.grid().num_lines(); + let g_cols = terminal.grid().num_cols(); + // Clear when terminal mutex isn't held. Mesa for // some reason takes a long time to call glClear(). The driver descends // into xcb_connect_to_fd() which ends up calling __poll_nocancel() @@ -513,7 +518,8 @@ impl Display { let glyph_cache = &mut self.glyph_cache; let mut rects = Rects::new(&metrics, &size_info); - // Draw grid + // Draw grid (non-HarfBuzz) + #[cfg(not(feature = "hb-ft"))] { let _sampler = self.meter.sampler(); @@ -528,6 +534,122 @@ impl Display { } }); } + // Draw grid (HarfBuzz) + #[cfg(feature = "hb-ft")] + { + let _sampler = self.meter.sampler(); + // Separate the renderable_cells into rows + let mut renderable_cells_rows = Vec::with_capacity(g_lines.0); + let mut row = Vec::with_capacity(g_cols.0); + let mut last_line = None; + let mut i = grid_cells.into_iter().peekable(); + while let Some(rcell) = i.next() { + if last_line.is_none() { + last_line = Some(rcell.line.0); + // Safe to unwrap because we checked that it is not None + } else if last_line.unwrap() != rcell.line.0 { + last_line = Some(rcell.line.0); + renderable_cells_rows.push(row.clone()); + row.clear(); + } + row.push(rcell); + if let None = i.peek() { + renderable_cells_rows.push(row.clone()); + } + } + + // Convert each row into a set of text runs + // (i.e. cells with the same display properties) + let mut text_run_rows = Vec::new(); + let mut row = Vec::new(); + let mut run = String::new(); + let mut rcell = None; + for r in renderable_cells_rows.into_iter() { + let mut ii = r.into_iter().peekable(); + while let Some(c) = ii.next() { + //println!("Got cell: {:?}", c); + let cmp_cell = RenderableCell { + inner: [' '; crate::term::cell::MAX_ZEROWIDTH_CHARS + 1].into(), + column: crate::index::Column(0), + ..(c.clone()) + }; + if rcell.is_none() { + rcell = Some(cmp_cell.clone()); + // Safe to unwrap because we checked that it is not None + } else if rcell.unwrap() != cmp_cell { + //println!("Pushed run"); + row.push((rcell.unwrap(), run.clone())); + row.clear(); + rcell = Some(cmp_cell.clone()); + } + match c.inner { + RenderableCellContent::Cursor(_) => {}, + RenderableCellContent::Chars(chars) => { + for c in chars.iter().cloned() { + run.push(c); + } + }, + }; + if let None = ii.peek() { + row.push((rcell.unwrap(), run.clone())); + } + } + text_run_rows.push(row.clone()); + row.clear(); + } + for row in &text_run_rows { + for (rc, run) in row { + //println!("RC: {:?}", rc); + //println!("RUN: {}", run); + } + } + // Shape each run of text. + let text_run_rows: Vec>)>> = text_run_rows.into_iter().map(|row| { + row.into_iter().map(|(rc, run)| { + //println!("Calling shape!"); + (rc, glyph_cache.rasterizer.shape(&run, if rc.flags.contains(crate::term::cell::Flags::BOLD) { + glyph_cache.bold_key + } else if rc.flags.contains(crate::term::cell::Flags::ITALIC) { + glyph_cache.italic_key + } else { + glyph_cache.font_key + }, glyph_cache.font_size)) + }).collect() + }).collect(); + for row in &text_run_rows { + for (rc, run) in row { + //println!("RC: {:?}", rc); + //println!("Run: {:?}", run); + } + } + // Helper that rounds first arg to be a multiple of second arg. + fn u_round_to(a: f32, b: f32) -> usize { + let a = a as usize; + let b = b as usize; + a / b + } + self.renderer.with_api(config, &size_info, |mut api| { + for row in text_run_rows.into_iter() { + for (mut rc, glyphs) in row.into_iter() { + // XXX: what does this do? (for text runs) + rects.update_lines(&size_info, &rc); + // Render each glyph, advancing based on the information provided. + if let Some(glyphs) = glyphs { + println!("Got glyph run"); + for g in glyphs.into_iter() { + // Hold reference to glyph from cache + let glyph = glyph_cache.get(g.glyph, &mut api).clone(); + //println!("Glyph = {}", g.glyph.c as u32); + api.render_glyph_at_position(&rc, glyph_cache, g.glyph.c); + println!("Glyph width: {}", glyph.width); + rc.column = crate::index::Column(u_round_to(rc.column.0 as f32 * size_info.cell_width + glyph.width, size_info.cell_width as f32) + 1); + } + } + } + } + }); + println!("Successfully rendered glyphs with HarfBuzz!"); + } if let Some(message) = message_buffer { let text = message.text(&size_info); diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index cd94247f31..89c97e2295 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -139,8 +139,8 @@ pub struct Glyph { tex_id: GLuint, top: f32, left: f32, - width: f32, - height: f32, + pub width: f32, + pub height: f32, uv_bot: f32, uv_left: f32, uv_width: f32, @@ -159,19 +159,19 @@ pub struct GlyphCache { cursor_cache: HashMap>, /// Rasterizer for loading new glyphs - rasterizer: Rasterizer, + pub rasterizer: Rasterizer, /// regular font - font_key: FontKey, + pub font_key: FontKey, /// italic font - italic_key: FontKey, + pub italic_key: FontKey, /// bold font - bold_key: FontKey, + pub bold_key: FontKey, /// font size - font_size: font::Size, + pub font_size: font::Size, /// glyph offset glyph_offset: Delta, @@ -987,6 +987,29 @@ impl<'a> RenderApi<'a> { self.render_batch(); } } + + pub fn render_glyph_at_position(&mut self, cell: &RenderableCell, + glyph_cache: &mut GlyphCache, glyph: char) { + // Get font key for cell + // FIXME this is super inefficient. + let font_key = if cell.flags.contains(cell::Flags::BOLD) { + glyph_cache.bold_key + } else if cell.flags.contains(cell::Flags::ITALIC) { + glyph_cache.italic_key + } else { + glyph_cache.font_key + }; + + let glyph_key = GlyphKey { + font_key, + size: glyph_cache.font_size, + c: glyph, + }; + + // Add cell to batch + let glyph = glyph_cache.get(glyph_key, self); + self.add_render_item(cell, &glyph); + } pub fn render_cell(&mut self, cell: RenderableCell, glyph_cache: &mut GlyphCache) { let chars = match cell.inner { @@ -1041,7 +1064,7 @@ impl<'a> RenderApi<'a> { // Render zero-width characters for c in (&chars[1..]).iter().filter(|c| **c != ' ') { - glyph_key.c = *c; + glyph_key.c = *c as _; let mut glyph = *glyph_cache.get(glyph_key, self); // The metrics of zero-width characters are based on rendering diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 34d06b2173..400dd67b6a 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -303,13 +303,23 @@ impl<'a> RenderableCellsIter<'a> { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum RenderableCellContent { Chars([char; cell::MAX_ZEROWIDTH_CHARS + 1]), Cursor(CursorKey), } +impl Into for [char; cell::MAX_ZEROWIDTH_CHARS + 1] { + fn into(self) -> RenderableCellContent { + RenderableCellContent::Chars(self) + } +} +impl Into for CursorKey { + fn into(self) -> RenderableCellContent { + RenderableCellContent::Cursor(self) + } +} -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct RenderableCell { /// A _Display_ line (not necessarily an _Active_ line) pub line: Line, diff --git a/font/Cargo.toml b/font/Cargo.toml index 2159f7d6c5..7f979d071f 100644 --- a/font/Cargo.toml +++ b/font/Cargo.toml @@ -11,6 +11,8 @@ libc = "0.2" foreign-types = "0.4" log = "0.4" +harfbuzz = { version = "0.3.0", optional = true } + [target.'cfg(not(any(target_os = "macos", windows)))'.dependencies] servo-fontconfig = "0.4.0" freetype-rs = "0.19" @@ -23,3 +25,7 @@ core-foundation-sys = "0.6" [target.'cfg(windows)'.dependencies] dwrote = { version = "0.9.0" } + +[features] +default = [] +hb-ft = ["harfbuzz"] \ No newline at end of file diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 6be625629e..bfb665e5d5 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -21,6 +21,8 @@ use std::path::PathBuf; use freetype::tt_os2::TrueTypeOS2Table; use freetype::{self, Library}; use libc::c_uint; +#[cfg(feature = "hb-ft")] +use super::HbGlyph; pub mod fc; @@ -37,6 +39,19 @@ struct Face { render_mode: freetype::RenderMode, lcd_filter: c_uint, non_scalable: Option, + /// This is an option just in case hb_ft_create_font_referenced fails. + #[cfg(feature = "hb-ft")] + hb_font: Option<*mut harfbuzz::sys::hb_font_t>, +} + +/// Required to drop the hb_font. +#[cfg(feature = "hb-ft")] +impl ::std::ops::Drop for Face { + fn drop(&mut self) { + if let Some(hb_font) = self.hb_font { + unsafe { harfbuzz::sys::hb_font_destroy(hb_font); } + } + } } impl fmt::Debug for Face { @@ -143,6 +158,69 @@ impl ::Rasterize for FreeTypeRasterizer { } } +/// Utility function that converts a string into a tag. +#[cfg(feature = "hb-ft")] +fn hb_tag(f: &str) -> harfbuzz::sys::hb_tag_t { + unsafe { harfbuzz::sys::hb_tag_from_string(f.as_ptr() as *const i8, f.len() as i32) } +} + +#[cfg(feature = "hb-ft")] +impl ::HbFtExt for FreeTypeRasterizer { + fn shape(&mut self, text: &str, font_key: FontKey, size: Size) -> Option> { + println!("shape() called!"); + if let Some(hb_font) = self.faces[&font_key].hb_font { + println!("Got harfbuzz font!"); + let mut buf = harfbuzz::Buffer::with(text); + buf.guess_segment_properties(); + // Shape + unsafe { + // ::std::ptr::null() == NULL (with type *const _) + harfbuzz::sys::hb_shape(hb_font, buf.as_ptr(), ::std::ptr::null(), 0); + }; + // Get glyph information + let ginfo: &mut [harfbuzz::sys::hb_glyph_info_t] = unsafe { + let mut len = 0u32; + let res = harfbuzz::sys::hb_buffer_get_glyph_infos(buf.as_ptr(), &mut len as *mut _); + println!("Got glyph information"); + ::std::slice::from_raw_parts_mut( + res, + len as _ + ) + }; + // Get glyph positions + let gpos: &mut [harfbuzz::sys::hb_glyph_position_t] = unsafe { + let mut len = 0u32; + let res = harfbuzz::sys::hb_buffer_get_glyph_positions(buf.as_ptr(), &mut len as *mut _); + println!("Got glyph positions"); + ::std::slice::from_raw_parts_mut( + res, + len as _ + ) + }; + // Combine into HbGlyph's + let hb_glyphs = ginfo.into_iter().zip(gpos.into_iter()).map(|(gi, gp)| { + HbGlyph { + x_advance: gp.x_advance, + y_advance: gp.y_advance, + x_offset: gp.x_offset, + y_offset: gp.y_offset, + glyph: GlyphKey { + c: ::std::char::from_u32(gi.codepoint).expect("HB u32 is not a char!"), + font_key, + size, + }, + cluster: gi.cluster, + } + }).collect(); + println!("Got HbGlyphs's!"); + Some(hb_glyphs) + } else { + println!("Couldn't get harfbuzz font"); + None + } + } +} + pub trait IntoFontconfigType { type FcType; fn into_fontconfig_type(&self) -> Self::FcType; @@ -253,7 +331,7 @@ impl FreeTypeRasterizer { } trace!("Got font path={:?}", path); - let ft_face = self.library.new_face(&path, index)?; + let mut ft_face = self.library.new_face(&path, index)?; // Get available pixel sizes if font isn't scalable. let non_scalable = if pattern.scalable().next().unwrap_or(true) { @@ -265,6 +343,21 @@ impl FreeTypeRasterizer { Some(FixedSize { pixelsize: pixelsize.next().expect("has 1+ pixelsize") }) }; + // Construct harfbuzz font + #[cfg(feature = "hb-ft")] + let hb_font = { + let _hb_font = unsafe { + harfbuzz::sys::hb_ft_font_create_referenced(ft_face.raw_mut() as *mut freetype::freetype_sys::FT_FaceRec as *mut _) + }; + if _hb_font.is_null() { + None + } else { + println!("Could create harfbuzz font"); + Some(_hb_font) + } + }; + + let face = Face { ft_face, key: FontKey::next(), @@ -272,6 +365,8 @@ impl FreeTypeRasterizer { render_mode: Self::ft_render_mode(pattern), lcd_filter: Self::ft_lcd_filter(pattern), non_scalable, + #[cfg(feature = "hb-ft")] + hb_font, }; debug!("Loaded Face {:?}", face); diff --git a/font/src/lib.rs b/font/src/lib.rs index d3bddd5406..cad8e85581 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -20,27 +20,34 @@ #![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)] -#[cfg(not(any(target_os = "macos", windows)))] +/* Note: all applicable cfg statements have been modified to short-circuit + * to freetype if the feature hb-ft is enabled. + */ + +#[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] extern crate fontconfig; -#[cfg(not(any(target_os = "macos", windows)))] +#[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] extern crate freetype; -#[cfg(target_os = "macos")] +#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] extern crate core_foundation; -#[cfg(target_os = "macos")] +#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] extern crate core_foundation_sys; -#[cfg(target_os = "macos")] +#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] extern crate core_graphics; -#[cfg(target_os = "macos")] +#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] extern crate core_text; -#[cfg(target_os = "macos")] +#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] extern crate euclid; +#[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] +#[macro_use] +extern crate foreign_types; extern crate libc; -#[cfg(not(any(target_os = "macos", windows)))] -#[macro_use] -extern crate foreign_types; +#[cfg(feature = "hb-ft")] +extern crate harfbuzz; + #[cfg_attr(not(windows), macro_use)] extern crate log; @@ -50,20 +57,20 @@ use std::hash::{Hash, Hasher}; use std::sync::atomic::{AtomicUsize, Ordering}; // If target isn't macos or windows, reexport everything from ft -#[cfg(not(any(target_os = "macos", windows)))] +#[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] pub mod ft; -#[cfg(not(any(target_os = "macos", windows)))] +#[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] pub use ft::{Error, FreeTypeRasterizer as Rasterizer}; -#[cfg(windows)] +#[cfg(all(windows, not(feature = "hb-ft")))] pub mod directwrite; -#[cfg(windows)] +#[cfg(all(windows, not(feature = "hb-ft")))] pub use crate::directwrite::{DirectWriteRasterizer as Rasterizer, Error}; // If target is macos, reexport everything from darwin -#[cfg(target_os = "macos")] +#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] mod darwin; -#[cfg(target_os = "macos")] +#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] pub use darwin::*; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -267,3 +274,23 @@ pub trait Rasterize { /// Update the Rasterizer's DPI factor fn update_dpr(&mut self, device_pixel_ratio: f32); } + +#[cfg(feature = "hb-ft")] +pub trait HbFtExt { + /// Shape the provided text into a set of glyphs. + /// TODO: properly report HarfBuzz errors + fn shape(&mut self, text: &str, font_key: FontKey, size: Size) -> Option>; +} + +/// A HarfBuzz-shaped glyph with advance and offset information. +#[cfg(feature = "hb-ft")] +#[derive(Debug)] +pub struct HbGlyph { + pub x_advance: harfbuzz::sys::hb_position_t, + pub y_advance: harfbuzz::sys::hb_position_t, + pub x_offset: harfbuzz::sys::hb_position_t, + pub y_offset: harfbuzz::sys::hb_position_t, + pub glyph: GlyphKey, + // Probably will never be used + pub cluster: u32, +} From ab620c9968fe1a399a2f5642a7bdd0fcd70f1528 Mon Sep 17 00:00:00 2001 From: Anirudh Balaji Date: Fri, 15 Mar 2019 18:32:43 -0700 Subject: [PATCH 02/84] Fix cargo.lock CI and everything should now work. --- Cargo.lock | 62 +++++++++++++++++++++++++++--------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 115a0866d3..f906c9d61d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,7 +89,7 @@ dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusttype 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -473,7 +473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -543,7 +543,7 @@ dependencies = [ "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -555,7 +555,7 @@ dependencies = [ "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -594,7 +594,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -622,7 +622,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -683,7 +683,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1213,9 +1213,9 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.48 (registry+https://github.com/rust-lang/crates.io-index)", "schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1280,7 +1280,7 @@ dependencies = [ "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1291,7 +1291,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1367,7 +1367,7 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.23" +version = "0.10.24" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1375,7 +1375,7 @@ dependencies = [ "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.48 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1385,7 +1385,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.47" +version = "0.9.48" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1730,12 +1730,12 @@ dependencies = [ [[package]] name = "regex" -version = "1.1.9" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1745,15 +1745,15 @@ name = "regex-syntax" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.6.8" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1874,7 +1874,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2053,7 +2053,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.15.39" +version = "0.15.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2068,7 +2068,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2148,7 +2148,7 @@ dependencies = [ [[package]] name = "ucd-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2253,7 +2253,7 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.2.8" +version = "2.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2695,9 +2695,9 @@ dependencies = [ "checksum objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "31d20fd2b37e07cf5125be68357b588672e8cefe9a96f8c17a9d46053b3e590d" "checksum objc-foundation 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" "checksum objc_id 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -"checksum openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)" = "97c140cbb82f3b3468193dd14c1b88def39f341f68257f8a7fe8ed9ed3f628a5" +"checksum openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)" = "8152bb5a9b5b721538462336e3bef9a539f892715e5037fda0f984577311af15" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)" = "75bdd6dbbb4958d38e47a1d2348847ad1eb4dc205dc5d37473ae504391865acc" +"checksum openssl-sys 0.9.48 (registry+https://github.com/rust-lang/crates.io-index)" = "b5ba300217253bcc5dc68bed23d782affa45000193866e025329aa8a7a9f05b8" "checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" "checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" @@ -2737,9 +2737,9 @@ dependencies = [ "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828" "checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" -"checksum regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d8297cc20bbb6184f8b45ff61c8ee6a9ac56c156cec8e38c3e5084773c44ad" +"checksum regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b23da8dfd98a84bd7e08700190a5d9f7d2d38abd4369dd1dae651bc40bfd2cc" "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" -"checksum regex-syntax 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9b01330cce219c1c6b2e209e5ed64ccd587ae5c67bed91c0b49eecf02ae40e21" +"checksum regex-syntax 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5485bf1523a9ed51c4964273f22f63f24e31632adb5dad134f488f86a3875c" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum rle-decode-fast 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" @@ -2777,7 +2777,7 @@ dependencies = [ "checksum static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4f8de36da215253eb5f24020bfaa0646613b48bf7ebe36cdfa37c3b3b33b241" "checksum stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "69b7df505db8e81d54ff8be4693421e5b543e08214bd8d99eb761fcb4d5668ba" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c" +"checksum syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)" = "bc945221ccf4a7e8c31222b9d1fc77aefdd6638eb901a6ce457a3dc29d4c31e8" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" @@ -2787,7 +2787,7 @@ dependencies = [ "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum tiff 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4834f28a0330cb9f3f2c87d2649dca723cb33802e2bdcf18da32759fbec7ce" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" +"checksum ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa9b3b49edd3468c0e6565d85783f51af95212b6fa3986a5500954f00b460874" "checksum unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a84e5511b2a947f3ae965dcb29b13b7b1691b6e7332cf5dbc1744138d5acb7f6" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" @@ -2803,7 +2803,7 @@ dependencies = [ "checksum vswhom 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" "checksum vswhom-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc2f5402d3d0e79a069714f7b48e3ecc60be7775a2c049cb839457457a239532" "checksum vte 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f42f536e22f7fcbb407639765c8fd78707a33109301f834a594758bedd6e8cf" -"checksum walkdir 2.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c7904a7e2bb3cdf0cf5e783f44204a85a37a93151738fa349f06680f59a98b45" +"checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" "checksum wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "49963e5f9eeaf637bfcd1b9f0701c99fd5cd05225eb51035550d4272806f2713" "checksum wayland-client 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)" = "80d909ba6616dd772686f421e111f039402a13acecf395bcb4fc665277a504e0" "checksum wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "40c08896768b667e1df195d88a62a53a2d1351a1ed96188be79c196b35bb32ec" From bc7d5e4ac5fb52951a89eb91ea4657de9c1cbca4 Mon Sep 17 00:00:00 2001 From: Anirudh Balaji Date: Fri, 15 Mar 2019 20:30:27 -0700 Subject: [PATCH 03/84] Refine harfbuzz shaping more, now vaguely resembles the real text. --- Cargo.lock | 24 ++++++++++ alacritty_terminal/src/display.rs | 62 +++++++++++++++++++------- alacritty_terminal/src/renderer/mod.rs | 24 +++++++++- font/src/ft/mod.rs | 42 ++++++++++++++--- 4 files changed, 126 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f906c9d61d..02d6792280 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -653,9 +653,16 @@ dependencies = [ "euclid 0.19.9 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype-rs 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", +<<<<<<< HEAD "harfbuzz 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", +======= + "harfbuzz 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "rusttype 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", +>>>>>>> Refine harfbuzz shaping more, now vaguely resembles the real text. "servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -701,7 +708,11 @@ name = "freetype" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ +<<<<<<< HEAD "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", +======= + "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", +>>>>>>> Refine harfbuzz shaping more, now vaguely resembles the real text. "servo-freetype-sys 4.0.3", ] @@ -2635,6 +2646,7 @@ dependencies = [ "checksum gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39a23d5e872a275135d66895d954269cf5e8661d234eb1c2480f4ce0d586acbd" "checksum gleam 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "c8a455b5a3ccd35daeb89fdb8a89ebb0a1fe23c05c7a7f9017840bc3ae176f71" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +<<<<<<< HEAD "checksum glutin 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb26027a84c3b9e1949ef0df0b6a3db8d0c124243a5c161ea25c7def90cb1474" "checksum glutin_egl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "23f48987ab6cb2b61ad903b59e54a2fd0c380a7baff68cffd6826b69a73dd326" "checksum glutin_emscripten_sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "245b3fdb08df6ffed7585365851f8404af9c7e2dd4b59f15262e968b6a95a0c7" @@ -2644,6 +2656,18 @@ dependencies = [ "checksum harfbuzz 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46f7426266a5ece3e49eae6f48e602c0f8c39917354a847eac9c06437dcde8da" "checksum harfbuzz-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e1042ab0b3e7bc1ff64f7f5935778b644ff2194a1cae5ec52167127d3fd23961" "checksum http_req 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b7053e4a7b90e97e3c207f8293dc4a6e7f1d49156470b94f3685f1baa48f65a" +======= +"checksum glutin 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff663466cd51f6fda5976e8a6f02a9fd65b8dde0b9b11db8344585174d015b2c" +"checksum glutin_egl_sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8d1d0032e46d771a21a9e911a5f4ba8e9e7233b69c2c4a3e23756b9ef361ebb8" +"checksum glutin_gles2_sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bbb85a016a10063833c3fa93ba4aa12aa8dcad98252edbcb07a33d111eeaf9bd" +"checksum glutin_glx_sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c7a6094a90a31a6d13ff6ca325582b4273062a0951aeb18db0fbd1c75a84ced" +"checksum glutin_wgl_sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a43bdcfc241feacf7a78600ba61adc122e31a0f91912a5072ca4d97418432746" +"checksum h2 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "910a5e7be6283a9c91b3982fa5188368c8719cce2a3cf3b86048673bf9d9c36b" +"checksum harfbuzz 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2391774b0f90979b2aaf8017874d30b4c1a6b3221b6179a20537bd22228c1c4d" +"checksum harfbuzz-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2c51ca00736b830d7a534b9dde9973c5bf075505e2233017f8c2d5b68301fc92" +"checksum http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fe67e3678f2827030e89cc4b9e7ecd16d52f132c0b940ab5005f88e821500f6a" +"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" +>>>>>>> Refine harfbuzz shaping more, now vaguely resembles the real text. "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)" = "99198e595d012efccf12abf4abc08da2d97be0b0355a2b08d101347527476ba4" diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 374069dc12..976b0c5ed9 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -552,7 +552,9 @@ impl Display { renderable_cells_rows.push(row.clone()); row.clear(); } - row.push(rcell); + if !rcell.flags.contains(crate::term::cell::Flags::HIDDEN) { + row.push(rcell); + } if let None = i.peek() { renderable_cells_rows.push(row.clone()); } @@ -585,9 +587,7 @@ impl Display { match c.inner { RenderableCellContent::Cursor(_) => {}, RenderableCellContent::Chars(chars) => { - for c in chars.iter().cloned() { - run.push(c); - } + run.push(chars[0]); }, }; if let None = ii.peek() { @@ -606,23 +606,40 @@ impl Display { // Shape each run of text. let text_run_rows: Vec>)>> = text_run_rows.into_iter().map(|row| { row.into_iter().map(|(rc, run)| { - //println!("Calling shape!"); - (rc, glyph_cache.rasterizer.shape(&run, if rc.flags.contains(crate::term::cell::Flags::BOLD) { - glyph_cache.bold_key - } else if rc.flags.contains(crate::term::cell::Flags::ITALIC) { - glyph_cache.italic_key - } else { - glyph_cache.font_key - }, glyph_cache.font_size)) + use font::{UNDERLINE_CURSOR_CHAR, BEAM_CURSOR_CHAR, BOX_CURSOR_CHAR}; + let ends_with_special = run.ends_with(UNDERLINE_CURSOR_CHAR) || run.ends_with(BEAM_CURSOR_CHAR) || run.ends_with(BOX_CURSOR_CHAR); + if ends_with_special { + let last_char = run.chars().last().unwrap(); + let rest = run.chars().take(run.len() - 1).collect::(); + (rc, glyph_cache.rasterizer.shape(&rest, if rc.flags.contains(crate::term::cell::Flags::BOLD) { + glyph_cache.bold_key + } else if rc.flags.contains(crate::term::cell::Flags::ITALIC) { + glyph_cache.italic_key + } else { + glyph_cache.font_key + }, glyph_cache.font_size)) + } else { + //println!("Calling shape!"); + (rc, glyph_cache.rasterizer.shape(&run, if rc.flags.contains(crate::term::cell::Flags::BOLD) { + glyph_cache.bold_key + } else if rc.flags.contains(crate::term::cell::Flags::ITALIC) { + glyph_cache.italic_key + } else { + glyph_cache.font_key + }, glyph_cache.font_size)) + } }).collect() }).collect(); + /* for row in &text_run_rows { for (rc, run) in row { //println!("RC: {:?}", rc); //println!("Run: {:?}", run); } } + */ // Helper that rounds first arg to be a multiple of second arg. + #[inline] fn u_round_to(a: f32, b: f32) -> usize { let a = a as usize; let b = b as usize; @@ -635,14 +652,25 @@ impl Display { rects.update_lines(&size_info, &rc); // Render each glyph, advancing based on the information provided. if let Some(glyphs) = glyphs { - println!("Got glyph run"); + //println!("Got glyph run"); for g in glyphs.into_iter() { // Hold reference to glyph from cache - let glyph = glyph_cache.get(g.glyph, &mut api).clone(); + let glyph = glyph_cache.get_raw(g.glyph, &mut api, g.glyph.c as u32).clone(); + // Determine if the glyph is a special character //println!("Glyph = {}", g.glyph.c as u32); - api.render_glyph_at_position(&rc, glyph_cache, g.glyph.c); - println!("Glyph width: {}", glyph.width); - rc.column = crate::index::Column(u_round_to(rc.column.0 as f32 * size_info.cell_width + glyph.width, size_info.cell_width as f32) + 1); + let w = glyph.width; + match g.glyph.c { + font::UNDERLINE_CURSOR_CHAR | font::BEAM_CURSOR_CHAR + | font::BOX_CURSOR_CHAR => { + api.render_glyph_at_position(&rc, glyph_cache, g.glyph.c); + rc.column.0 += 1; + }, + _ => { + api.add_render_item(&rc, &glyph); + rc.column = crate::index::Column(u_round_to(rc.column.0 as f32 * size_info.cell_width + glyph.width, size_info.cell_width as f32) + 1); + }, + } + //println!("Glyph width: {}", glyph.width); } } } diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 89c97e2295..f561fd0d55 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -297,6 +297,24 @@ impl GlyphCache { }) } + #[cfg(feature = "hb-ft")] + pub fn get_raw<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L, glyph_i: u32) -> Glyph + where L: LoadGlyph + { + let glyph_offset = self.glyph_offset; + let rasterizer = &mut self.rasterizer; + let metrics = &self.metrics; + // Doesn't go through cache, making it suuuper slow. + let mut rasterized = rasterizer.get_glyph_raw(glyph_key, glyph_i) + .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( &mut self, font: &config::Font, @@ -973,8 +991,7 @@ impl<'a> RenderApi<'a> { } } - #[inline] - fn add_render_item(&mut self, cell: &RenderableCell, glyph: &Glyph) { + pub 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(); @@ -990,6 +1007,9 @@ impl<'a> RenderApi<'a> { pub fn render_glyph_at_position(&mut self, cell: &RenderableCell, glyph_cache: &mut GlyphCache, glyph: char) { + if cell.flags.contains(cell::Flags::HIDDEN) { + return; + } // Get font key for cell // FIXME this is super inefficient. let font_key = if cell.flags.contains(cell::Flags::BOLD) { diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index bfb665e5d5..a67ed063f1 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -167,11 +167,11 @@ fn hb_tag(f: &str) -> harfbuzz::sys::hb_tag_t { #[cfg(feature = "hb-ft")] impl ::HbFtExt for FreeTypeRasterizer { fn shape(&mut self, text: &str, font_key: FontKey, size: Size) -> Option> { - println!("shape() called!"); if let Some(hb_font) = self.faces[&font_key].hb_font { - println!("Got harfbuzz font!"); let mut buf = harfbuzz::Buffer::with(text); - buf.guess_segment_properties(); + buf.set_direction(harfbuzz::Direction::LTR); + buf.set_script(harfbuzz::sys::HB_SCRIPT_LATIN); + buf.set_language(harfbuzz::Language::from_string("en")); // Shape unsafe { // ::std::ptr::null() == NULL (with type *const _) @@ -181,7 +181,6 @@ impl ::HbFtExt for FreeTypeRasterizer { let ginfo: &mut [harfbuzz::sys::hb_glyph_info_t] = unsafe { let mut len = 0u32; let res = harfbuzz::sys::hb_buffer_get_glyph_infos(buf.as_ptr(), &mut len as *mut _); - println!("Got glyph information"); ::std::slice::from_raw_parts_mut( res, len as _ @@ -191,7 +190,6 @@ impl ::HbFtExt for FreeTypeRasterizer { let gpos: &mut [harfbuzz::sys::hb_glyph_position_t] = unsafe { let mut len = 0u32; let res = harfbuzz::sys::hb_buffer_get_glyph_positions(buf.as_ptr(), &mut len as *mut _); - println!("Got glyph positions"); ::std::slice::from_raw_parts_mut( res, len as _ @@ -212,10 +210,8 @@ impl ::HbFtExt for FreeTypeRasterizer { cluster: gi.cluster, } }).collect(); - println!("Got HbGlyphs's!"); Some(hb_glyphs) } else { - println!("Couldn't get harfbuzz font"); None } } @@ -438,6 +434,38 @@ impl FreeTypeRasterizer { }) } + pub fn get_glyph_raw(&mut self, glyph_key: GlyphKey, glyph_i: u32) + -> Result { + let font_key = self.face_for_glyph(glyph_key, false)?; + let face = &self.faces[&font_key]; + + let size = face.non_scalable.as_ref() + .map(|v| v.pixelsize as f32) + .unwrap_or_else(|| glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.); + + face.ft_face.set_char_size(to_freetype_26_6(size), 0, 0, 0)?; + + unsafe { + let ft_lib = self.library.raw(); + freetype::ffi::FT_Library_SetLcdFilter(ft_lib, face.lcd_filter); + } + + face.ft_face.load_glyph(glyph_i as u32, face.load_flags)?; + let glyph = face.ft_face.glyph(); + glyph.render_glyph(face.render_mode)?; + + let (pixel_height, pixel_width, buf) = Self::normalize_buffer(&glyph.bitmap())?; + + Ok(RasterizedGlyph { + c: glyph_key.c, + top: glyph.bitmap_top(), + left: glyph.bitmap_left(), + width: pixel_width, + height: pixel_height, + buf, + }) + } + fn ft_load_flags(pat: &fc::Pattern) -> freetype::face::LoadFlag { let antialias = pat.antialias().next().unwrap_or(true); let hinting = pat.hintstyle().next().unwrap_or(fc::HintStyle::Slight); From 1218fab1fa20c4ebd0470e611db9442c6f481c01 Mon Sep 17 00:00:00 2001 From: Anirudh Balaji Date: Sat, 16 Mar 2019 10:37:50 -0700 Subject: [PATCH 04/84] Fix clippy errors --- Cargo.lock | 24 ---------------------- alacritty_terminal/src/display.rs | 34 +++++++------------------------ 2 files changed, 7 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02d6792280..f906c9d61d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -653,16 +653,9 @@ dependencies = [ "euclid 0.19.9 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype-rs 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", -<<<<<<< HEAD "harfbuzz 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", -======= - "harfbuzz 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rusttype 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ->>>>>>> Refine harfbuzz shaping more, now vaguely resembles the real text. "servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -708,11 +701,7 @@ name = "freetype" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ -<<<<<<< HEAD "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", -======= - "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ->>>>>>> Refine harfbuzz shaping more, now vaguely resembles the real text. "servo-freetype-sys 4.0.3", ] @@ -2646,7 +2635,6 @@ dependencies = [ "checksum gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39a23d5e872a275135d66895d954269cf5e8661d234eb1c2480f4ce0d586acbd" "checksum gleam 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "c8a455b5a3ccd35daeb89fdb8a89ebb0a1fe23c05c7a7f9017840bc3ae176f71" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -<<<<<<< HEAD "checksum glutin 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb26027a84c3b9e1949ef0df0b6a3db8d0c124243a5c161ea25c7def90cb1474" "checksum glutin_egl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "23f48987ab6cb2b61ad903b59e54a2fd0c380a7baff68cffd6826b69a73dd326" "checksum glutin_emscripten_sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "245b3fdb08df6ffed7585365851f8404af9c7e2dd4b59f15262e968b6a95a0c7" @@ -2656,18 +2644,6 @@ dependencies = [ "checksum harfbuzz 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46f7426266a5ece3e49eae6f48e602c0f8c39917354a847eac9c06437dcde8da" "checksum harfbuzz-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e1042ab0b3e7bc1ff64f7f5935778b644ff2194a1cae5ec52167127d3fd23961" "checksum http_req 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b7053e4a7b90e97e3c207f8293dc4a6e7f1d49156470b94f3685f1baa48f65a" -======= -"checksum glutin 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff663466cd51f6fda5976e8a6f02a9fd65b8dde0b9b11db8344585174d015b2c" -"checksum glutin_egl_sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8d1d0032e46d771a21a9e911a5f4ba8e9e7233b69c2c4a3e23756b9ef361ebb8" -"checksum glutin_gles2_sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bbb85a016a10063833c3fa93ba4aa12aa8dcad98252edbcb07a33d111eeaf9bd" -"checksum glutin_glx_sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c7a6094a90a31a6d13ff6ca325582b4273062a0951aeb18db0fbd1c75a84ced" -"checksum glutin_wgl_sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a43bdcfc241feacf7a78600ba61adc122e31a0f91912a5072ca4d97418432746" -"checksum h2 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "910a5e7be6283a9c91b3982fa5188368c8719cce2a3cf3b86048673bf9d9c36b" -"checksum harfbuzz 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2391774b0f90979b2aaf8017874d30b4c1a6b3221b6179a20537bd22228c1c4d" -"checksum harfbuzz-sys 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2c51ca00736b830d7a534b9dde9973c5bf075505e2233017f8c2d5b68301fc92" -"checksum http 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "fe67e3678f2827030e89cc4b9e7ecd16d52f132c0b940ab5005f88e821500f6a" -"checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" ->>>>>>> Refine harfbuzz shaping more, now vaguely resembles the real text. "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)" = "99198e595d012efccf12abf4abc08da2d97be0b0355a2b08d101347527476ba4" diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 976b0c5ed9..efeeb850d4 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -606,28 +606,13 @@ impl Display { // Shape each run of text. let text_run_rows: Vec>)>> = text_run_rows.into_iter().map(|row| { row.into_iter().map(|(rc, run)| { - use font::{UNDERLINE_CURSOR_CHAR, BEAM_CURSOR_CHAR, BOX_CURSOR_CHAR}; - let ends_with_special = run.ends_with(UNDERLINE_CURSOR_CHAR) || run.ends_with(BEAM_CURSOR_CHAR) || run.ends_with(BOX_CURSOR_CHAR); - if ends_with_special { - let last_char = run.chars().last().unwrap(); - let rest = run.chars().take(run.len() - 1).collect::(); - (rc, glyph_cache.rasterizer.shape(&rest, if rc.flags.contains(crate::term::cell::Flags::BOLD) { - glyph_cache.bold_key - } else if rc.flags.contains(crate::term::cell::Flags::ITALIC) { - glyph_cache.italic_key - } else { - glyph_cache.font_key - }, glyph_cache.font_size)) - } else { - //println!("Calling shape!"); - (rc, glyph_cache.rasterizer.shape(&run, if rc.flags.contains(crate::term::cell::Flags::BOLD) { - glyph_cache.bold_key - } else if rc.flags.contains(crate::term::cell::Flags::ITALIC) { - glyph_cache.italic_key - } else { - glyph_cache.font_key - }, glyph_cache.font_size)) - } + (rc, glyph_cache.rasterizer.shape(&run, if rc.flags.contains(crate::term::cell::Flags::BOLD) { + glyph_cache.bold_key + } else if rc.flags.contains(crate::term::cell::Flags::ITALIC) { + glyph_cache.italic_key + } else { + glyph_cache.font_key + }, glyph_cache.font_size)) }).collect() }).collect(); /* @@ -660,11 +645,6 @@ impl Display { //println!("Glyph = {}", g.glyph.c as u32); let w = glyph.width; match g.glyph.c { - font::UNDERLINE_CURSOR_CHAR | font::BEAM_CURSOR_CHAR - | font::BOX_CURSOR_CHAR => { - api.render_glyph_at_position(&rc, glyph_cache, g.glyph.c); - rc.column.0 += 1; - }, _ => { api.add_render_item(&rc, &glyph); rc.column = crate::index::Column(u_round_to(rc.column.0 as f32 * size_info.cell_width + glyph.width, size_info.cell_width as f32) + 1); From a2ab0cc8994ba600bf199eb63d06f0b6a834f4ae Mon Sep 17 00:00:00 2001 From: Anirudh Balaji Date: Sat, 30 Mar 2019 18:39:34 -0700 Subject: [PATCH 05/84] Break everything --- alacritty_terminal/src/display.rs | 14 +++++++---- alacritty_terminal/src/renderer/mod.rs | 28 +++++++++++++++------ font/src/ft/mod.rs | 34 ++++++++++++++++++-------- font/src/lib.rs | 8 +++--- 4 files changed, 57 insertions(+), 27 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index efeeb850d4..e7c1042ea3 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -615,14 +615,12 @@ impl Display { }, glyph_cache.font_size)) }).collect() }).collect(); - /* for row in &text_run_rows { for (rc, run) in row { - //println!("RC: {:?}", rc); - //println!("Run: {:?}", run); + info!("RC: {:?}", rc); + info!("Run: {:?}", run); } } - */ // Helper that rounds first arg to be a multiple of second arg. #[inline] fn u_round_to(a: f32, b: f32) -> usize { @@ -632,7 +630,13 @@ impl Display { } self.renderer.with_api(config, &size_info, |mut api| { for row in text_run_rows.into_iter() { + let mut used_rcs = Vec::new(); for (mut rc, glyphs) in row.into_iter() { + // Make sure we are not rerendering the same thing twice. + if used_rcs.contains(&rc) { + continue; + } + used_rcs.push(rc.clone()); // XXX: what does this do? (for text runs) rects.update_lines(&size_info, &rc); // Render each glyph, advancing based on the information provided. @@ -647,7 +651,7 @@ impl Display { match g.glyph.c { _ => { api.add_render_item(&rc, &glyph); - rc.column = crate::index::Column(u_round_to(rc.column.0 as f32 * size_info.cell_width + glyph.width, size_info.cell_width as f32) + 1); + rc.column = crate::index::Column(u_round_to(rc.column.0 as f32 * size_info.cell_width + g.x_advance, size_info.cell_width as f32) + 1); }, } //println!("Glyph width: {}", glyph.width); diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index f561fd0d55..bea27ea472 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -304,15 +304,27 @@ impl GlyphCache { let glyph_offset = self.glyph_offset; let rasterizer = &mut self.rasterizer; let metrics = &self.metrics; + let fixed_glyph_key = GlyphKey { + c: ::std::char::from_u32(glyph_i).unwrap(), + ..(glyph_key.clone()) + }; // Doesn't go through cache, making it suuuper slow. - let mut rasterized = rasterizer.get_glyph_raw(glyph_key, glyph_i) - .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) + /* + self.cache + .entry(fixed_glyph_key) + .or_insert_with(|| { + */ + let mut rasterized = rasterizer.get_glyph_raw(glyph_key, glyph_i) + .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( diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index a67ed063f1..0cd72245c2 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -198,10 +198,11 @@ impl ::HbFtExt for FreeTypeRasterizer { // Combine into HbGlyph's let hb_glyphs = ginfo.into_iter().zip(gpos.into_iter()).map(|(gi, gp)| { HbGlyph { - x_advance: gp.x_advance, - y_advance: gp.y_advance, - x_offset: gp.x_offset, - y_offset: gp.y_offset, + /* ugh -_- you have to divide by 64?? */ + x_advance: (gp.x_advance as f32) / 64., + y_advance: (gp.y_advance as f32) / 64., + x_offset: (gp.x_offset as f32) / 64., + y_offset: (gp.y_offset as f32) / 64., glyph: GlyphKey { c: ::std::char::from_u32(gi.codepoint).expect("HB u32 is not a char!"), font_key, @@ -342,14 +343,27 @@ impl FreeTypeRasterizer { // Construct harfbuzz font #[cfg(feature = "hb-ft")] let hb_font = { - let _hb_font = unsafe { - harfbuzz::sys::hb_ft_font_create_referenced(ft_face.raw_mut() as *mut freetype::freetype_sys::FT_FaceRec as *mut _) + let mut buf = Vec::new(); + use std::io::Read; + let mut f = ::std::fs::File::open(&path).unwrap(); + f.read_to_end(&mut buf).unwrap(); + let blob = harfbuzz::Blob::new_read_only(&buf); + let _hb_face = unsafe { + harfbuzz::sys::hb_face_create(blob.as_raw(), index as u32) }; - if _hb_font.is_null() { - None + if !_hb_face.is_null() { + let _hb_font = unsafe { + harfbuzz::sys::hb_font_create(_hb_face) + }; + if !_hb_font.is_null() { + unsafe { harfbuzz::sys::hb_ot_font_set_funcs(_hb_font); } + println!("Could create harfbuzz font"); + Some(_hb_font) + } else { + None + } } else { - println!("Could create harfbuzz font"); - Some(_hb_font) + None } }; diff --git a/font/src/lib.rs b/font/src/lib.rs index cad8e85581..5961845cb2 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -286,10 +286,10 @@ pub trait HbFtExt { #[cfg(feature = "hb-ft")] #[derive(Debug)] pub struct HbGlyph { - pub x_advance: harfbuzz::sys::hb_position_t, - pub y_advance: harfbuzz::sys::hb_position_t, - pub x_offset: harfbuzz::sys::hb_position_t, - pub y_offset: harfbuzz::sys::hb_position_t, + pub x_advance: f32, + pub y_advance: f32, + pub x_offset: f32, + pub y_offset: f32, pub glyph: GlyphKey, // Probably will never be used pub cluster: u32, From f28f6fe25b869ee1452d62a99659dbcd6465cab3 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 3 Jul 2019 11:23:10 -0700 Subject: [PATCH 06/84] Rendering is normal but font ligatures still don't work --- alacritty_terminal/src/display.rs | 213 ++++++++++++++----------- alacritty_terminal/src/renderer/mod.rs | 4 +- copypasta/src/x11.rs | 173 ++++++++++++++++++++ font/src/ft/mod.rs | 27 ++-- 4 files changed, 309 insertions(+), 108 deletions(-) create mode 100644 copypasta/src/x11.rs diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index e7c1042ea3..bfb0dad62f 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -24,7 +24,7 @@ use glutin::EventsLoop; use parking_lot::MutexGuard; use crate::config::{Config, StartupMode}; -use crate::index::Line; +use crate::index::{Line, Column}; use crate::message_bar::Message; use crate::meter::Meter; use crate::renderer::rects::{Rect, Rects}; @@ -454,7 +454,8 @@ impl Display { } self.window.resize(psize); - self.renderer.resize(psize, self.size_info.padding_x, self.size_info.padding_y); + self.renderer + .resize(psize, self.size_info.padding_x, self.size_info.padding_y); } } @@ -496,8 +497,8 @@ impl Display { } } - let g_lines = terminal.grid().num_lines(); - let g_cols = terminal.grid().num_cols(); + #[cfg(feature = "hb-ft")] + let (g_lines, g_cols) = (terminal.grid().num_lines(), terminal.grid().num_cols()); // Clear when terminal mutex isn't held. Mesa for // some reason takes a long time to call glClear(). The driver descends @@ -537,84 +538,106 @@ impl Display { // Draw grid (HarfBuzz) #[cfg(feature = "hb-ft")] { - let _sampler = self.meter.sampler(); - // Separate the renderable_cells into rows - let mut renderable_cells_rows = Vec::with_capacity(g_lines.0); - let mut row = Vec::with_capacity(g_cols.0); - let mut last_line = None; - let mut i = grid_cells.into_iter().peekable(); - while let Some(rcell) = i.next() { - if last_line.is_none() { - last_line = Some(rcell.line.0); - // Safe to unwrap because we checked that it is not None - } else if last_line.unwrap() != rcell.line.0 { - last_line = Some(rcell.line.0); - renderable_cells_rows.push(row.clone()); - row.clear(); - } - if !rcell.flags.contains(crate::term::cell::Flags::HIDDEN) { - row.push(rcell); + fn create_renderable_cell_rows(grid_cells: &[RenderableCell], g_lines: Line, g_cols: Column) -> Vec<&[RenderableCell]> { + let mut renderable_cells_rows: Vec<&[RenderableCell]> = Vec::with_capacity(g_lines.0); + let (mut row_start, mut row_end) = (0, 0); + let mut last_line = None; + for i in 0..grid_cells.len() { + let rcell = grid_cells[i]; + if last_line.is_none() { + last_line = Some(rcell.line); + } else if last_line.unwrap() != grid_cells[i].line { + last_line = Some(rcell.line); + renderable_cells_rows.push(&grid_cells[row_start..row_end]); + row_start = row_end; + } + if !rcell.flags.contains(crate::term::cell::Flags::HIDDEN) { + row_end += 1; + } } - if let None = i.peek() { - renderable_cells_rows.push(row.clone()); + renderable_cells_rows.push(&grid_cells[row_start..row_end]); + renderable_cells_rows + } + let _sampler = self.meter.sampler(); + let renderable_cells_rows = create_renderable_cell_rows(&grid_cells, g_lines, g_cols); + + /// Wrapper to allow comparing everything but column and chars + struct CmpCell(RenderableCell); + impl PartialEq for CmpCell { + fn eq(&self, other: &Self) -> bool { + let a = self.0; + let b = other.0; + a.line == b.line + && a.fg == b.fg + && a.bg == b.bg + && a.bg_alpha == b.bg_alpha + && a.flags == b.flags } } - // Convert each row into a set of text runs - // (i.e. cells with the same display properties) - let mut text_run_rows = Vec::new(); - let mut row = Vec::new(); - let mut run = String::new(); - let mut rcell = None; - for r in renderable_cells_rows.into_iter() { - let mut ii = r.into_iter().peekable(); - while let Some(c) = ii.next() { - //println!("Got cell: {:?}", c); - let cmp_cell = RenderableCell { - inner: [' '; crate::term::cell::MAX_ZEROWIDTH_CHARS + 1].into(), - column: crate::index::Column(0), - ..(c.clone()) - }; - if rcell.is_none() { - rcell = Some(cmp_cell.clone()); - // Safe to unwrap because we checked that it is not None - } else if rcell.unwrap() != cmp_cell { - //println!("Pushed run"); - row.push((rcell.unwrap(), run.clone())); - row.clear(); - rcell = Some(cmp_cell.clone()); - } - match c.inner { - RenderableCellContent::Cursor(_) => {}, - RenderableCellContent::Chars(chars) => { - run.push(chars[0]); - }, - }; - if let None = ii.peek() { - row.push((rcell.unwrap(), run.clone())); + fn create_text_run_rows(renderable_cells_rows: Vec<&[RenderableCell]>) -> Vec> { + let mut text_run_rows = Vec::new(); + let mut row = Vec::new(); + let mut run = String::new(); + let mut rcell = None; + for r in renderable_cells_rows.into_iter() { + let mut ii = r.iter().peekable(); + while let Some(c) = ii.next() { + if rcell.is_none() { + rcell = Some(*c); + } else if rcell.map(CmpCell).map_or(false, |last_cell| last_cell != CmpCell(*c)) { + println!("Run \"{}\" at {:?}", run, rcell); + row.push((rcell.unwrap(), run.clone())); + run.clear(); + rcell = Some(*c); + } + match c.inner { + RenderableCellContent::Cursor(_) => {}, + RenderableCellContent::Chars(chars) => { + run.push(chars[0]); + } + } + if ii.peek().is_none() { + row.push((*c, run.clone())); + } } + text_run_rows.push(row.clone()); + row.clear(); } - text_run_rows.push(row.clone()); - row.clear(); - } - for row in &text_run_rows { - for (rc, run) in row { - //println!("RC: {:?}", rc); - //println!("RUN: {}", run); - } + text_run_rows } + + // Convert each row into a set of text runs + // (i.e. cells with the same display properties) + let text_run_rows = create_text_run_rows(renderable_cells_rows); // Shape each run of text. - let text_run_rows: Vec>)>> = text_run_rows.into_iter().map(|row| { - row.into_iter().map(|(rc, run)| { - (rc, glyph_cache.rasterizer.shape(&run, if rc.flags.contains(crate::term::cell::Flags::BOLD) { - glyph_cache.bold_key - } else if rc.flags.contains(crate::term::cell::Flags::ITALIC) { - glyph_cache.italic_key - } else { - glyph_cache.font_key - }, glyph_cache.font_size)) - }).collect() - }).collect(); + let text_run_rows: Vec>)>> = text_run_rows + .into_iter() + .map(|row| { + row.into_iter() + .map(|(rc, run)| { + let key = if rc.flags.contains(crate::term::cell::Flags::BOLD) { + glyph_cache.bold_key + } else if rc + .flags + .contains(crate::term::cell::Flags::ITALIC) + { + glyph_cache.italic_key + } else { + glyph_cache.font_key + }; + ( + rc, + glyph_cache.rasterizer.shape( + run.as_str(), + key, + glyph_cache.font_size, + ), + ) + }) + .collect() + }) + .collect(); for row in &text_run_rows { for (rc, run) in row { info!("RC: {:?}", rc); @@ -629,34 +652,39 @@ impl Display { a / b } self.renderer.with_api(config, &size_info, |mut api| { + //let mut used_rcs: Vec = Vec::new(); for row in text_run_rows.into_iter() { - let mut used_rcs = Vec::new(); + //used_rcs.clear(); for (mut rc, glyphs) in row.into_iter() { // Make sure we are not rerendering the same thing twice. - if used_rcs.contains(&rc) { - continue; - } - used_rcs.push(rc.clone()); - // XXX: what does this do? (for text runs) - rects.update_lines(&size_info, &rc); + //if used_rcs.contains(&rc) { + // continue; + //} + //used_rcs.push(rc); // Render each glyph, advancing based on the information provided. if let Some(glyphs) = glyphs { - //println!("Got glyph run"); for g in glyphs.into_iter() { // Hold reference to glyph from cache - let glyph = glyph_cache.get_raw(g.glyph, &mut api, g.glyph.c as u32).clone(); + let glyph = glyph_cache.get(g.glyph, &mut api); // Determine if the glyph is a special character - //println!("Glyph = {}", g.glyph.c as u32); - let w = glyph.width; match g.glyph.c { _ => { + println!("Rendered {:?} at {:?}", g.glyph.c, rc.column); api.add_render_item(&rc, &glyph); - rc.column = crate::index::Column(u_round_to(rc.column.0 as f32 * size_info.cell_width + g.x_advance, size_info.cell_width as f32) + 1); - }, + rc.column += 1;/*crate::index::Column( + u_round_to( + rc.column.0 as f32 * size_info.cell_width + + g.x_advance, + size_info.cell_width as f32, + ) + 1, + )*/; + } } - //println!("Glyph width: {}", glyph.width); } } + + // XXX: what does this do? (for text runs) + rects.update_lines(&size_info, &rc); } } }); @@ -673,7 +701,8 @@ impl Display { rects.push(rect, message.color()); // Draw rectangles including the new background - self.renderer.draw_rects(config, &size_info, visual_bell_intensity, rects); + self.renderer + .draw_rects(config, &size_info, visual_bell_intensity, rects); // Relay messages to the user let mut offset = 1; @@ -690,7 +719,8 @@ impl Display { } } else { // Draw rectangles - self.renderer.draw_rects(config, &size_info, visual_bell_intensity, rects); + self.renderer + .draw_rects(config, &size_info, visual_bell_intensity, rects); } // Draw render timer @@ -720,7 +750,8 @@ impl Display { let nspot_x = f64::from(px + point.col.0 as f32 * cw); let nspot_y = f64::from(py + (point.line.0 + 1) as f32 * ch); - self.window().set_ime_spot(PhysicalPosition::from((nspot_x, nspot_y)).to_logical(dpr)); + self.window() + .set_ime_spot(PhysicalPosition::from((nspot_x, nspot_y)).to_logical(dpr)); } #[cfg(not(any(target_os = "macos", target_os = "windows")))] diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index bea27ea472..9df07f07c8 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -305,8 +305,8 @@ impl GlyphCache { let rasterizer = &mut self.rasterizer; let metrics = &self.metrics; let fixed_glyph_key = GlyphKey { - c: ::std::char::from_u32(glyph_i).unwrap(), - ..(glyph_key.clone()) + c: std::char::from_u32(glyph_i).unwrap(), + ..glyph_key }; // Doesn't go through cache, making it suuuper slow. /* diff --git a/copypasta/src/x11.rs b/copypasta/src/x11.rs new file mode 100644 index 0000000000..b54867e827 --- /dev/null +++ b/copypasta/src/x11.rs @@ -0,0 +1,173 @@ +//! X11 Clipboard implementation +//! +//! Note that the x11 implementation is really crap right now - we just depend +//! on xclip being on the user's path. If x11 pasting doesn't work, it's +//! probably because xclip is unavailable. There's currently no non-GPL x11 +//! clipboard library for Rust. Until then, we have this hack. +//! +//! FIXME: Implement actual X11 clipboard API using the ICCCM reference +//! https://tronche.com/gui/x/icccm/ +use std::io; +use std::process::{Output, Command}; +use std::string::FromUtf8Error; +use std::ffi::OsStr; + +use super::{Load, Store}; + +/// The x11 clipboard +pub struct Clipboard; + +#[derive(Debug)] +pub enum Error { + Io(io::Error), + Xclip(String), + Utf8(FromUtf8Error), +} + +impl ::std::error::Error for Error { + fn cause(&self) -> Option<&::std::error::Error> { + match *self { + Error::Io(ref err) => Some(err), + Error::Utf8(ref err) => Some(err), + _ => None, + } + } + + fn description(&self) -> &str { + match *self { + Error::Io(..) => "Error calling xclip", + Error::Xclip(..) => "Error reported by xclip", + Error::Utf8(..) => "Clipboard contents not utf8", + } + } +} + +impl ::std::fmt::Display for Error { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + Error::Io(ref err) => { + match err.kind() { + io::ErrorKind::NotFound => { + write!(f, "Please install `xclip` to enable clipboard support") + }, + _ => write!(f, "Error calling xclip: {}", err), + } + }, + Error::Xclip(ref s) => write!(f, "Error from xclip: {}", s), + Error::Utf8(ref err) => write!(f, "Error parsing xclip output: {}", err), + } + } +} + +impl From for Error { + fn from(val: io::Error) -> Error { + Error::Io(val) + } +} + +impl From for Error { + fn from(val: FromUtf8Error) -> Error { + Error::Utf8(val) + } +} + +impl Load for Clipboard { + type Err = Error; + + fn new() -> Result { + Ok(Clipboard) + } + + fn load_primary(&self) -> Result { + let output = Command::new("xclip") + .args(&["-o", "-selection", "clipboard"]) + .output()?; + + Clipboard::process_xclip_output(output) + } + + fn load_selection(&self) -> Result { + let output = Command::new("xclip") + .args(&["-o"]) + .output()?; + + Clipboard::process_xclip_output(output) + } +} + +impl Store for Clipboard { + /// Sets the primary clipboard contents + #[inline] + fn store_primary(&mut self, contents: S) -> Result<(), Self::Err> + where S: Into + { + self.store(contents, &["-i", "-selection", "clipboard"]) + } + + /// Sets the secondary clipboard contents + #[inline] + fn store_selection(&mut self, contents: S) -> Result<(), Self::Err> + where S: Into + { + self.store(contents, &["-i"]) + } +} + +impl Clipboard { + fn process_xclip_output(output: Output) -> Result { + if output.status.success() { + String::from_utf8(output.stdout) + .map_err(::std::convert::From::from) + } else { + String::from_utf8(output.stderr) + .map_err(::std::convert::From::from) + } + } + + fn store(&mut self, contents: C, args: &[S]) -> Result<(), Error> + where C: Into, + S: AsRef, + { + use std::io::Write; + use std::process::{Stdio}; + + let contents = contents.into(); + let mut child = Command::new("xclip") + .args(args) + .stdin(Stdio::piped()) + .spawn()?; + + if let Some(stdin) = child.stdin.as_mut() { + stdin.write_all(contents.as_bytes())?; + } + + // Return error if didn't exit cleanly + let exit_status = child.wait()?; + if exit_status.success() { + Ok(()) + } else { + Err(Error::Xclip("xclip returned non-zero exit code".into())) + } + } +} + +#[cfg(test)] +mod tests { + use super::Clipboard; + use ::{Load, Store}; + + #[test] + fn clipboard_works() { + let mut clipboard = Clipboard::new().expect("create clipboard"); + let arst = "arst"; + let oien = "oien"; + clipboard.store_primary(arst).expect("store selection"); + clipboard.store_selection(oien).expect("store selection"); + + let selection = clipboard.load_selection().expect("load selection"); + let primary = clipboard.load_primary().expect("load selection"); + + assert_eq!(arst, primary); + assert_eq!(oien, selection); + } +} diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 0cd72245c2..fbeea9f7bb 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -167,7 +167,7 @@ fn hb_tag(f: &str) -> harfbuzz::sys::hb_tag_t { #[cfg(feature = "hb-ft")] impl ::HbFtExt for FreeTypeRasterizer { fn shape(&mut self, text: &str, font_key: FontKey, size: Size) -> Option> { - if let Some(hb_font) = self.faces[&font_key].hb_font { + self.faces[&font_key].hb_font.map(|hb_font| { let mut buf = harfbuzz::Buffer::with(text); buf.set_direction(harfbuzz::Direction::LTR); buf.set_script(harfbuzz::sys::HB_SCRIPT_LATIN); @@ -175,7 +175,7 @@ impl ::HbFtExt for FreeTypeRasterizer { // Shape unsafe { // ::std::ptr::null() == NULL (with type *const _) - harfbuzz::sys::hb_shape(hb_font, buf.as_ptr(), ::std::ptr::null(), 0); + harfbuzz::sys::hb_shape(hb_font, buf.as_ptr(), std::ptr::null(), 0); }; // Get glyph information let ginfo: &mut [harfbuzz::sys::hb_glyph_info_t] = unsafe { @@ -196,7 +196,7 @@ impl ::HbFtExt for FreeTypeRasterizer { ) }; // Combine into HbGlyph's - let hb_glyphs = ginfo.into_iter().zip(gpos.into_iter()).map(|(gi, gp)| { + ginfo.iter_mut().zip(gpos.iter_mut()).map(|(gi, gp)| { HbGlyph { /* ugh -_- you have to divide by 64?? */ x_advance: (gp.x_advance as f32) / 64., @@ -204,17 +204,14 @@ impl ::HbFtExt for FreeTypeRasterizer { x_offset: (gp.x_offset as f32) / 64., y_offset: (gp.y_offset as f32) / 64., glyph: GlyphKey { - c: ::std::char::from_u32(gi.codepoint).expect("HB u32 is not a char!"), + c: text.chars().nth(gi.cluster as usize).expect("Expected cluster index to point to char in text None was found."),//::std::char::from_u32(gi.codepoint).expect("HB u32 is not a char!"), font_key, size, }, cluster: gi.cluster, } - }).collect(); - Some(hb_glyphs) - } else { - None - } + }).collect() + }) } } @@ -351,19 +348,19 @@ impl FreeTypeRasterizer { let _hb_face = unsafe { harfbuzz::sys::hb_face_create(blob.as_raw(), index as u32) }; - if !_hb_face.is_null() { + if _hb_face.is_null() { + None + } else { let _hb_font = unsafe { harfbuzz::sys::hb_font_create(_hb_face) }; - if !_hb_font.is_null() { + if _hb_font.is_null() { + None + } else { unsafe { harfbuzz::sys::hb_ot_font_set_funcs(_hb_font); } println!("Could create harfbuzz font"); Some(_hb_font) - } else { - None } - } else { - None } }; From 5df92b4c6439a1e127bf26666b672fd80b194635 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 3 Jul 2019 11:30:46 -0700 Subject: [PATCH 07/84] whitespace funky. --- alacritty_terminal/src/display.rs | 19 ++++++------------- font/src/ft/mod.rs | 1 + 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index bfb0dad62f..8a194385ce 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -538,7 +538,7 @@ impl Display { // Draw grid (HarfBuzz) #[cfg(feature = "hb-ft")] { - fn create_renderable_cell_rows(grid_cells: &[RenderableCell], g_lines: Line, g_cols: Column) -> Vec<&[RenderableCell]> { + fn create_renderable_cell_rows(grid_cells: &[RenderableCell], g_lines: Line) -> Vec<&[RenderableCell]> { let mut renderable_cells_rows: Vec<&[RenderableCell]> = Vec::with_capacity(g_lines.0); let (mut row_start, mut row_end) = (0, 0); let mut last_line = None; @@ -559,7 +559,7 @@ impl Display { renderable_cells_rows } let _sampler = self.meter.sampler(); - let renderable_cells_rows = create_renderable_cell_rows(&grid_cells, g_lines, g_cols); + let renderable_cells_rows = create_renderable_cell_rows(&grid_cells, g_lines); /// Wrapper to allow comparing everything but column and chars struct CmpCell(RenderableCell); @@ -652,15 +652,8 @@ impl Display { a / b } self.renderer.with_api(config, &size_info, |mut api| { - //let mut used_rcs: Vec = Vec::new(); for row in text_run_rows.into_iter() { - //used_rcs.clear(); for (mut rc, glyphs) in row.into_iter() { - // Make sure we are not rerendering the same thing twice. - //if used_rcs.contains(&rc) { - // continue; - //} - //used_rcs.push(rc); // Render each glyph, advancing based on the information provided. if let Some(glyphs) = glyphs { for g in glyphs.into_iter() { @@ -669,15 +662,15 @@ impl Display { // Determine if the glyph is a special character match g.glyph.c { _ => { - println!("Rendered {:?} at {:?}", g.glyph.c, rc.column); + //println!("Rendered {:?} at {:?}", g.glyph.c, rc.column); api.add_render_item(&rc, &glyph); - rc.column += 1;/*crate::index::Column( + rc.column = crate::index::Column( u_round_to( rc.column.0 as f32 * size_info.cell_width + g.x_advance, size_info.cell_width as f32, - ) + 1, - )*/; + ), + ); } } } diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index fbeea9f7bb..d7f62df436 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -196,6 +196,7 @@ impl ::HbFtExt for FreeTypeRasterizer { ) }; // Combine into HbGlyph's + println!("Shaped text: {:?}", buf); ginfo.iter_mut().zip(gpos.iter_mut()).map(|(gi, gp)| { HbGlyph { /* ugh -_- you have to divide by 64?? */ From 79facbc68b85ea715295d0c58b54b551e45e7c72 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 3 Jul 2019 18:42:56 -0700 Subject: [PATCH 08/84] Tried setting scale and ppem but it didn't change anything. --- alacritty/src/main.rs | 18 +- alacritty_terminal/src/ansi.rs | 55 +- alacritty_terminal/src/display.rs | 87 +- alacritty_terminal/src/event.rs | 56 +- alacritty_terminal/src/event_loop.rs | 20 +- alacritty_terminal/src/grid/mod.rs | 27 +- alacritty_terminal/src/grid/row.rs | 6 +- alacritty_terminal/src/grid/tests.rs | 60 + alacritty_terminal/src/input.rs | 39 +- alacritty_terminal/src/lib.rs | 7 +- alacritty_terminal/src/message_bar.rs | 10 +- alacritty_terminal/src/renderer/mod.rs | 48 +- alacritty_terminal/src/renderer/rects.rs | 7 +- alacritty_terminal/src/term/mod.rs | 91 +- alacritty_terminal/src/tty/unix.rs | 10 +- alacritty_terminal/src/tty/windows/conpty.rs | 8 + alacritty_terminal/src/tty/windows/winpty.rs | 14 + alacritty_terminal/tests/ref.rs | 19 + build.rs | 94 + copypasta/src/macos.rs | 330 ++ copypasta/src/x11.rs | 39 +- font/src/darwin/mod.rs | 77 +- font/src/ft/mod.rs | 140 +- font/src/lib.rs | 10 +- font/src/rusttype/mod.rs | 206 ++ src/cli.rs | 258 ++ src/config/bindings.rs | 217 ++ src/config/mod.rs | 2883 ++++++++++++++++++ src/grid/storage.rs | 928 ++++++ src/selection.rs | 647 ++++ src/term/color.rs | 244 ++ src/url.rs | 307 ++ winpty/src/lib.rs | 7 +- winpty/src/windows.rs | 6 + 34 files changed, 6731 insertions(+), 244 deletions(-) create mode 100644 build.rs create mode 100644 copypasta/src/macos.rs create mode 100644 font/src/rusttype/mod.rs create mode 100644 src/cli.rs create mode 100644 src/config/bindings.rs create mode 100644 src/config/mod.rs create mode 100644 src/grid/storage.rs create mode 100644 src/selection.rs create mode 100644 src/term/color.rs create mode 100644 src/url.rs diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs index 3fa3ff5319..bdf298c953 100644 --- a/alacritty/src/main.rs +++ b/alacritty/src/main.rs @@ -13,7 +13,12 @@ // 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)] +#![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 @@ -220,7 +225,9 @@ fn run(config: Config, message_buffer: MessageBuffer) -> Result<(), Box Result<(), Box handler.insert_blank(Column(arg_or_default!(idx: 0, default: 1) as usize)), 'A' => { handler.move_up(Line(arg_or_default!(idx: 0, default: 1) as usize)); - }, + } 'b' => { if let Some(c) = self._state.preceding_char { for _ in 0..arg_or_default!(idx: 0, default: 1) { @@ -926,7 +926,7 @@ where } else { debug!("tried to repeat with no preceding char"); } - }, + } 'B' | 'e' => handler.move_down(Line(arg_or_default!(idx: 0, default: 1) as usize)), 'c' => handler.identify_terminal(writer), 'C' | 'a' => handler.move_forward(Column(arg_or_default!(idx: 0, default: 1) as usize)), @@ -941,13 +941,13 @@ where }; handler.clear_tabs(mode); - }, + } 'G' | '`' => handler.goto_col(Column(arg_or_default!(idx: 0, default: 1) as usize - 1)), 'H' | 'f' => { let y = arg_or_default!(idx: 0, default: 1) as usize; let x = arg_or_default!(idx: 1, default: 1) as usize; handler.goto(Line(y - 1), Column(x - 1)); - }, + } 'I' => handler.move_forward_tabs(arg_or_default!(idx: 0, default: 1)), 'J' => { let mode = match arg_or_default!(idx: 0, default: 0) { @@ -959,7 +959,7 @@ where }; handler.clear_screen(mode); - }, + } 'K' => { let mode = match arg_or_default!(idx: 0, default: 0) { 0 => LineClearMode::Right, @@ -969,7 +969,7 @@ where }; handler.clear_line(mode); - }, + } 'S' => handler.scroll_up(Line(arg_or_default!(idx: 0, default: 1) as usize)), 'T' => handler.scroll_down(Line(arg_or_default!(idx: 0, default: 1) as usize)), 'L' => handler.insert_blank_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)), @@ -981,7 +981,7 @@ where None => unhandled!(), } } - }, + } 'M' => handler.delete_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)), 'X' => handler.erase_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)), 'P' => handler.delete_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)), @@ -995,7 +995,7 @@ where None => unhandled!(), } } - }, + } 'm' => { // Sometimes a C-style for loop is just what you need let mut i = 0; // C-for initializer @@ -1044,7 +1044,7 @@ where } else { break; } - }, + } 39 => Attr::Foreground(Color::Named(NamedColor::Foreground)), 40 => Attr::Background(Color::Named(NamedColor::Black)), 41 => Attr::Background(Color::Named(NamedColor::Red)), @@ -1062,7 +1062,7 @@ where } else { break; } - }, + } 49 => Attr::Background(Color::Named(NamedColor::Background)), 90 => Attr::Foreground(Color::Named(NamedColor::BrightBlack)), 91 => Attr::Foreground(Color::Named(NamedColor::BrightRed)), @@ -1103,7 +1103,7 @@ where let bottom = Line(arg1); handler.set_scrolling_region(top..bottom); - }, + } 's' => handler.save_cursor_position(), 'u' => handler.restore_cursor_position(), 'q' => { @@ -1217,7 +1217,7 @@ fn parse_color(attrs: &[i64], i: &mut usize) -> Option { }, } } - }, + } _ => { debug!("Unexpected color attr: {}", attrs[*i + 1]); None @@ -1502,7 +1502,6 @@ mod tests { fn lines(&self) -> Line { Line(200) } - fn cols(&self) -> Column { Column(90) } @@ -1519,7 +1518,10 @@ mod tests { } assert_eq!(handler.index, CharsetIndex::G0); - assert_eq!(handler.charset, StandardCharset::SpecialCharacterAndLineDrawing); + assert_eq!( + handler.charset, + StandardCharset::SpecialCharacterAndLineDrawing + ); } #[test] @@ -1533,7 +1535,10 @@ mod tests { } assert_eq!(handler.index, CharsetIndex::G1); - assert_eq!(handler.charset, StandardCharset::SpecialCharacterAndLineDrawing); + assert_eq!( + handler.charset, + StandardCharset::SpecialCharacterAndLineDrawing + ); let mut handler = CharsetHandler::default(); parser.advance(&mut handler, BYTES[3], &mut Void); @@ -1543,12 +1548,26 @@ mod tests { #[test] fn parse_valid_rgb_color() { - assert_eq!(parse_rgb_color(b"rgb:11/aa/ff"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff })); + assert_eq!( + parse_rgb_color(b"rgb:11/aa/ff"), + Some(Rgb { + r: 0x11, + g: 0xaa, + b: 0xff + }) + ); } #[test] fn parse_valid_rgb_color2() { - assert_eq!(parse_rgb_color(b"#11aaff"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff })); + assert_eq!( + parse_rgb_color(b"#11aaff"), + Some(Rgb { + r: 0x11, + g: 0xaa, + b: 0xff + }) + ); } #[test] diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 8a194385ce..dc4f03746c 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -498,7 +498,7 @@ impl Display { } #[cfg(feature = "hb-ft")] - let (g_lines, g_cols) = (terminal.grid().num_lines(), terminal.grid().num_cols()); + let (g_lines) = terminal.grid().num_lines(); // Clear when terminal mutex isn't held. Mesa for // some reason takes a long time to call glClear(). The driver descends @@ -538,12 +538,16 @@ impl Display { // Draw grid (HarfBuzz) #[cfg(feature = "hb-ft")] { - fn create_renderable_cell_rows(grid_cells: &[RenderableCell], g_lines: Line) -> Vec<&[RenderableCell]> { - let mut renderable_cells_rows: Vec<&[RenderableCell]> = Vec::with_capacity(g_lines.0); + fn create_renderable_cell_rows( + grid_cells: &[RenderableCell], + g_lines: Line, + ) -> Vec<&[RenderableCell]> { + let mut renderable_cells_rows: Vec<&[RenderableCell]> = + Vec::with_capacity(g_lines.0); let (mut row_start, mut row_end) = (0, 0); let mut last_line = None; for i in 0..grid_cells.len() { - let rcell = grid_cells[i]; + let rcell = &grid_cells[i]; if last_line.is_none() { last_line = Some(rcell.line); } else if last_line.unwrap() != grid_cells[i].line { @@ -555,41 +559,45 @@ impl Display { row_end += 1; } } - renderable_cells_rows.push(&grid_cells[row_start..row_end]); + renderable_cells_rows.push(&grid_cells[row_start..]); renderable_cells_rows } let _sampler = self.meter.sampler(); let renderable_cells_rows = create_renderable_cell_rows(&grid_cells, g_lines); /// Wrapper to allow comparing everything but column and chars - struct CmpCell(RenderableCell); - impl PartialEq for CmpCell { + struct CmpCell<'a>(&'a RenderableCell); + impl<'a> PartialEq for CmpCell<'a> { fn eq(&self, other: &Self) -> bool { - let a = self.0; - let b = other.0; + let a = &self.0; + let b = &other.0; a.line == b.line - && a.fg == b.fg - && a.bg == b.bg - && a.bg_alpha == b.bg_alpha - && a.flags == b.flags + && a.fg == b.fg + && a.bg == b.bg + && a.bg_alpha == b.bg_alpha + && a.flags == b.flags } } - fn create_text_run_rows(renderable_cells_rows: Vec<&[RenderableCell]>) -> Vec> { + fn create_text_run_rows( + renderable_cells_rows: Vec<&[RenderableCell]>, + ) -> Vec> { let mut text_run_rows = Vec::new(); - let mut row = Vec::new(); + let mut row: Vec<(RenderableCell, String)> = Vec::new(); let mut run = String::new(); let mut rcell = None; for r in renderable_cells_rows.into_iter() { let mut ii = r.iter().peekable(); while let Some(c) = ii.next() { if rcell.is_none() { - rcell = Some(*c); - } else if rcell.map(CmpCell).map_or(false, |last_cell| last_cell != CmpCell(*c)) { - println!("Run \"{}\" at {:?}", run, rcell); - row.push((rcell.unwrap(), run.clone())); + rcell = Some(c); + } else if rcell + .map(CmpCell) + .map_or(false, |last_cell| last_cell != CmpCell(c)) + { + row.push((RenderableCell::clone(rcell.unwrap()), run.clone())); run.clear(); - rcell = Some(*c); + rcell = Some(c); } match c.inner { RenderableCellContent::Cursor(_) => {}, @@ -598,7 +606,7 @@ impl Display { } } if ii.peek().is_none() { - row.push((*c, run.clone())); + row.push((RenderableCell::clone(c), run.clone())); } } text_run_rows.push(row.clone()); @@ -617,15 +625,12 @@ impl Display { row.into_iter() .map(|(rc, run)| { let key = if rc.flags.contains(crate::term::cell::Flags::BOLD) { - glyph_cache.bold_key - } else if rc - .flags - .contains(crate::term::cell::Flags::ITALIC) - { - glyph_cache.italic_key - } else { - glyph_cache.font_key - }; + glyph_cache.bold_key + } else if rc.flags.contains(crate::term::cell::Flags::ITALIC) { + glyph_cache.italic_key + } else { + glyph_cache.font_key + }; ( rc, glyph_cache.rasterizer.shape( @@ -638,12 +643,7 @@ impl Display { .collect() }) .collect(); - for row in &text_run_rows { - for (rc, run) in row { - info!("RC: {:?}", rc); - info!("Run: {:?}", run); - } - } + // Helper that rounds first arg to be a multiple of second arg. #[inline] fn u_round_to(a: f32, b: f32) -> usize { @@ -654,6 +654,8 @@ impl Display { self.renderer.with_api(config, &size_info, |mut api| { for row in text_run_rows.into_iter() { for (mut rc, glyphs) in row.into_iter() { + // XXX: what does this do? (for text runs) + rects.update_lines(&size_info, &rc); // Render each glyph, advancing based on the information provided. if let Some(glyphs) = glyphs { for g in glyphs.into_iter() { @@ -664,13 +666,11 @@ impl Display { _ => { //println!("Rendered {:?} at {:?}", g.glyph.c, rc.column); api.add_render_item(&rc, &glyph); - rc.column = crate::index::Column( - u_round_to( - rc.column.0 as f32 * size_info.cell_width - + g.x_advance, - size_info.cell_width as f32, - ), - ); + rc.column = crate::index::Column(u_round_to( + rc.column.0 as f32 * size_info.cell_width + + g.x_advance, + size_info.cell_width as f32, + )); } } } @@ -681,7 +681,6 @@ impl Display { } } }); - println!("Successfully rendered glyphs with HarfBuzz!"); } if let Some(message) = message_buffer { diff --git a/alacritty_terminal/src/event.rs b/alacritty_terminal/src/event.rs index bb34d2f29e..6191c3216c 100644 --- a/alacritty_terminal/src/event.rs +++ b/alacritty_terminal/src/event.rs @@ -73,7 +73,11 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> { } fn selection_is_empty(&self) -> bool { - self.terminal.selection().as_ref().map(Selection::is_empty).unwrap_or(true) + self.terminal + .selection() + .as_ref() + .map(Selection::is_empty) + .unwrap_or(true) } fn clear_selection(&mut self) { @@ -117,7 +121,8 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> { } fn mouse_coords(&self) -> Option { - self.terminal.pixels_to_coords(self.mouse.x as usize, self.mouse.y as usize) + self.terminal + .pixels_to_coords(self.mouse.x as usize, self.mouse.y as usize) } #[inline] @@ -181,7 +186,10 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> { match start_daemon(&alacritty, &args) { Ok(_) => debug!("Started new Alacritty process: {} {:?}", alacritty, args), - Err(_) => warn!("Unable to start new Alacritty process: {} {:?}", alacritty, args), + Err(_) => warn!( + "Unable to start new Alacritty process: {} {:?}", + alacritty, args + ), } } @@ -369,39 +377,53 @@ impl Processor { .send(lsize.to_physical(processor.ctx.size_info.dpr)) .expect("send new size"); processor.ctx.terminal.dirty = true; - }, + } KeyboardInput { input, .. } => { processor.process_key(input); if input.state == ElementState::Pressed { // Hide cursor while typing *hide_mouse = true; } - }, + } ReceivedCharacter(c) => { processor.received_char(c); - }, - MouseInput { state, button, modifiers, .. } => { + } + MouseInput { + state, + button, + modifiers, + .. + } => { if !cfg!(target_os = "macos") || *window_is_focused { *hide_mouse = false; processor.mouse_input(state, button, modifiers); processor.ctx.terminal.dirty = true; } - }, - CursorMoved { position: lpos, modifiers, .. } => { + } + CursorMoved { + position: lpos, + modifiers, + .. + } => { let (x, y) = lpos.to_physical(processor.ctx.size_info.dpr).into(); let x: i32 = limit(x, 0, processor.ctx.size_info.width as i32); let y: i32 = limit(y, 0, processor.ctx.size_info.height as i32); *hide_mouse = false; processor.mouse_moved(x as usize, y as usize, modifiers); - }, - MouseWheel { delta, phase, modifiers, .. } => { + } + MouseWheel { + delta, + phase, + modifiers, + .. + } => { *hide_mouse = false; processor.on_mouse_wheel(delta, phase, modifiers); - }, + } Refresh => { processor.ctx.terminal.dirty = true; - }, + } Focused(is_focused) => { *window_is_focused = is_focused; @@ -415,19 +437,19 @@ impl Processor { } processor.on_focus_change(is_focused); - }, + } DroppedFile(path) => { use crate::input::ActionContext; let path: String = path.to_string_lossy().into(); processor.ctx.write_to_pty(path.into_bytes()); - }, + } HiDpiFactorChanged(new_dpr) => { processor.ctx.size_info.dpr = new_dpr; processor.ctx.terminal.dirty = true; - }, + } _ => (), } - }, + } Event::Awakened => { processor.ctx.terminal.dirty = true; }, diff --git a/alacritty_terminal/src/event_loop.rs b/alacritty_terminal/src/event_loop.rs index 4941b47962..6f61f179f5 100644 --- a/alacritty_terminal/src/event_loop.rs +++ b/alacritty_terminal/src/event_loop.rs @@ -136,7 +136,10 @@ impl State { impl Writing { #[inline] fn new(c: Cow<'static, [u8]>) -> Writing { - Writing { source: c, written: 0 } + Writing { + source: c, + written: 0, + } } #[inline] @@ -215,7 +218,12 @@ where } self.poll - .reregister(&self.rx, token, Ready::readable(), PollOpt::edge() | PollOpt::oneshot()) + .reregister( + &self.rx, + token, + Ready::readable(), + PollOpt::edge() | PollOpt::oneshot(), + ) .unwrap(); true @@ -363,7 +371,7 @@ where if !self.channel_event(channel_token, &mut state) { break 'event_loop; } - }, + } #[cfg(unix)] token if token == self.pty.child_event_token() => { @@ -372,7 +380,7 @@ where self.display.notify(); break 'event_loop; } - }, + } token if token == self.pty.read_token() @@ -422,7 +430,9 @@ where interest.insert(Ready::writable()); } // Reregister with new interest - self.pty.reregister(&self.poll, interest, poll_opts).unwrap(); + self.pty + .reregister(&self.poll, interest, poll_opts) + .unwrap(); } // The evented instances are not dropped here so deregister them explicitly diff --git a/alacritty_terminal/src/grid/mod.rs b/alacritty_terminal/src/grid/mod.rs index 1925a6f40d..73be6cd599 100644 --- a/alacritty_terminal/src/grid/mod.rs +++ b/alacritty_terminal/src/grid/mod.rs @@ -175,7 +175,7 @@ impl Grid { max((self.display_offset as isize) + count, 0isize) as usize, self.scroll_limit, ); - }, + } Scroll::PageUp => { self.display_offset = min(self.display_offset + self.lines.0, self.scroll_limit); }, @@ -240,7 +240,8 @@ impl Grid { let lines_added = new_line_count - self.lines; // Need to "resize" before updating buffer - self.raw.grow_visible_lines(new_line_count, Row::new(self.cols, template)); + self.raw + .grow_visible_lines(new_line_count, Row::new(self.cols, template)); self.lines = new_line_count; // Move existing lines up if there is no scrollback to fill new lines @@ -568,7 +569,10 @@ impl Grid { T: Copy + GridCell, { let history_size = self.raw.len().saturating_sub(*self.lines); - self.raw.initialize(self.max_scroll_limit - history_size, Row::new(self.cols, template)); + self.raw.initialize( + self.max_scroll_limit - history_size, + Row::new(self.cols, template), + ); } /// This is used only for truncating before saving ref-tests @@ -610,7 +614,7 @@ impl<'a, T> Iterator for GridIterator<'a, T> { self.cur.line -= 1; self.cur.col = Column(0); Some(&self.grid[self.cur.line][self.cur.col]) - }, + } _ => { self.cur.col += Column(1); Some(&self.grid[self.cur.line][self.cur.col]) @@ -624,12 +628,15 @@ impl<'a, T> BidirectionalIterator for GridIterator<'a, T> { let num_cols = self.grid.num_cols(); match self.cur { - Point { line, col: Column(0) } if line == self.grid.len() - 1 => None, + Point { + line, + col: Column(0), + } if line == self.grid.len() - 1 => None, Point { col: Column(0), .. } => { self.cur.line += 1; self.cur.col = num_cols - Column(1); Some(&self.grid[self.cur.line][self.cur.col]) - }, + } _ => { self.cur.col -= Column(1); Some(&self.grid[self.cur.line][self.cur.col]) @@ -857,7 +864,13 @@ impl<'a, T: 'a> DisplayIter<'a, T> { let col = Column(0); let line = Line(0); - DisplayIter { grid, offset, col, limit, line } + DisplayIter { + grid, + offset, + col, + limit, + line, + } } pub fn offset(&self) -> usize { diff --git a/alacritty_terminal/src/grid/row.rs b/alacritty_terminal/src/grid/row.rs index f82e7eaa7a..3cbf64ee36 100644 --- a/alacritty_terminal/src/grid/row.rs +++ b/alacritty_terminal/src/grid/row.rs @@ -71,7 +71,11 @@ impl Row { // Split off cells for a new row let mut new_row = self.inner.split_off(cols.0); - let index = new_row.iter().rposition(|c| !c.is_empty()).map(|i| i + 1).unwrap_or(0); + let index = new_row + .iter() + .rposition(|c| !c.is_empty()) + .map(|i| i + 1) + .unwrap_or(0); new_row.truncate(index); self.occ = min(self.occ, cols.0); diff --git a/alacritty_terminal/src/grid/tests.rs b/alacritty_terminal/src/grid/tests.rs index a352e74797..9797d466b6 100644 --- a/alacritty_terminal/src/grid/tests.rs +++ b/alacritty_terminal/src/grid/tests.rs @@ -140,7 +140,16 @@ fn shrink_reflow() { grid[Line(0)][Column(3)] = cell('4'); grid[Line(0)][Column(4)] = cell('5'); +<<<<<<< HEAD:alacritty_terminal/src/grid/tests.rs grid.resize(true, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default()); +======= + grid.resize( + Line(1), + Column(2), + &mut Point::new(Line(0), Column(0)), + &Cell::default(), + ); +>>>>>>> Tried setting scale and ppem but it didn't change anything.:src/grid/tests.rs assert_eq!(grid.len(), 3); @@ -166,8 +175,23 @@ fn shrink_reflow_twice() { grid[Line(0)][Column(3)] = cell('4'); grid[Line(0)][Column(4)] = cell('5'); +<<<<<<< HEAD:alacritty_terminal/src/grid/tests.rs grid.resize(true, Line(1), Column(4), &mut Point::new(Line(0), Column(0)), &Cell::default()); grid.resize(true, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default()); +======= + grid.resize( + Line(1), + Column(4), + &mut Point::new(Line(0), Column(0)), + &Cell::default(), + ); + grid.resize( + Line(1), + Column(2), + &mut Point::new(Line(0), Column(0)), + &Cell::default(), + ); +>>>>>>> Tried setting scale and ppem but it didn't change anything.:src/grid/tests.rs assert_eq!(grid.len(), 3); @@ -193,7 +217,16 @@ fn shrink_reflow_empty_cell_inside_line() { grid[Line(0)][Column(3)] = cell('4'); grid[Line(0)][Column(4)] = Cell::default(); +<<<<<<< HEAD:alacritty_terminal/src/grid/tests.rs grid.resize(true, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default()); +======= + grid.resize( + Line(1), + Column(2), + &mut Point::new(Line(0), Column(0)), + &Cell::default(), + ); +>>>>>>> Tried setting scale and ppem but it didn't change anything.:src/grid/tests.rs assert_eq!(grid.len(), 2); @@ -205,7 +238,16 @@ fn shrink_reflow_empty_cell_inside_line() { assert_eq!(grid[0][Column(0)], cell('3')); assert_eq!(grid[0][Column(1)], cell('4')); +<<<<<<< HEAD:alacritty_terminal/src/grid/tests.rs grid.resize(true, Line(1), Column(1), &mut Point::new(Line(0), Column(0)), &Cell::default()); +======= + grid.resize( + Line(1), + Column(1), + &mut Point::new(Line(0), Column(0)), + &Cell::default(), + ); +>>>>>>> Tried setting scale and ppem but it didn't change anything.:src/grid/tests.rs assert_eq!(grid.len(), 4); @@ -230,7 +272,16 @@ fn grow_reflow() { grid[Line(1)][Column(0)] = cell('3'); grid[Line(1)][Column(1)] = Cell::default(); +<<<<<<< HEAD:alacritty_terminal/src/grid/tests.rs grid.resize(true, Line(2), Column(3), &mut Point::new(Line(0), Column(0)), &Cell::default()); +======= + grid.resize( + Line(2), + Column(3), + &mut Point::new(Line(0), Column(0)), + &Cell::default(), + ); +>>>>>>> Tried setting scale and ppem but it didn't change anything.:src/grid/tests.rs assert_eq!(grid.len(), 2); @@ -256,7 +307,16 @@ fn grow_reflow_multiline() { grid[Line(2)][Column(0)] = cell('5'); grid[Line(2)][Column(1)] = cell('6'); +<<<<<<< HEAD:alacritty_terminal/src/grid/tests.rs grid.resize(true, Line(3), Column(6), &mut Point::new(Line(0), Column(0)), &Cell::default()); +======= + grid.resize( + Line(3), + Column(6), + &mut Point::new(Line(0), Column(0)), + &Cell::default(), + ); +>>>>>>> Tried setting scale and ppem but it didn't change anything.:src/grid/tests.rs assert_eq!(grid.len(), 3); diff --git a/alacritty_terminal/src/input.rs b/alacritty_terminal/src/input.rs index 83ab026141..d319cf9246 100644 --- a/alacritty_terminal/src/input.rs +++ b/alacritty_terminal/src/input.rs @@ -269,7 +269,7 @@ impl Action { Action::Esc(ref s) => { ctx.scroll(Scroll::Bottom); ctx.write_to_pty(s.clone().into_bytes()) - }, + } Action::Copy => { ctx.copy_selection(ClipboardType::Clipboard); }, @@ -283,7 +283,7 @@ impl Action { let text = ctx.terminal_mut().clipboard().load(ClipboardType::Selection); self.paste(ctx, &text); } - }, + } Action::Command(ref program, ref args) => { trace!("Running command {} with args {:?}", program, args); @@ -301,10 +301,10 @@ impl Action { }, Action::Hide => { ctx.hide_window(); - }, + } Action::Quit => { ctx.terminal_mut().exit(); - }, + } Action::IncreaseFontSize => { ctx.terminal_mut().change_font_size(FONT_SIZE_STEP); }, @@ -316,7 +316,7 @@ impl Action { }, Action::ScrollPageUp => { ctx.scroll(Scroll::PageUp); - }, + } Action::ScrollPageDown => { ctx.scroll(Scroll::PageDown); }, @@ -328,19 +328,19 @@ impl Action { }, Action::ScrollToTop => { ctx.scroll(Scroll::Top); - }, + } Action::ScrollToBottom => { ctx.scroll(Scroll::Bottom); - }, + } Action::ClearHistory => { ctx.terminal_mut().clear_screen(ClearMode::Saved); - }, + } Action::ClearLogNotice => { ctx.terminal_mut().message_buffer_mut().pop(); - }, + } Action::SpawnNewInstance => { ctx.spawn_new_instance(); - }, + } Action::None => (), } } @@ -678,7 +678,11 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { match start_daemon(launcher.program(), &args) { Ok(_) => debug!("Launched {} with args {:?}", launcher.program(), args), - Err(_) => warn!("Unable to launch {} with args {:?}", launcher.program(), args), + Err(_) => warn!( + "Unable to launch {} with args {:?}", + launcher.program(), + args + ), } Some(()) @@ -694,16 +698,16 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { MouseScrollDelta::LineDelta(_columns, lines) => { let new_scroll_px = lines * self.ctx.size_info().cell_height; self.scroll_terminal(modifiers, 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(modifiers, lpos.y as i32); - }, + } _ => (), } }, @@ -789,7 +793,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { ElementState::Pressed => { self.process_mouse_bindings(modifiers, button); self.on_mouse_press(button, modifiers, point); - }, + } ElementState::Released => self.on_mouse_release(button, modifiers, point), } } @@ -808,7 +812,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { if self.process_key_bindings(input) { *self.ctx.suppress_chars() = true; } - }, + } ElementState::Released => *self.ctx.suppress_chars() = false, } } @@ -1181,7 +1185,8 @@ mod tests { } fn mouse_coords(&self) -> Option { - self.terminal.pixels_to_coords(self.mouse.x as usize, self.mouse.y as usize) + self.terminal + .pixels_to_coords(self.mouse.x as usize, self.mouse.y as usize) } #[inline] diff --git a/alacritty_terminal/src/lib.rs b/alacritty_terminal/src/lib.rs index 0bada53545..c37ccbc055 100644 --- a/alacritty_terminal/src/lib.rs +++ b/alacritty_terminal/src/lib.rs @@ -13,7 +13,12 @@ // 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)] +#![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))] diff --git a/alacritty_terminal/src/message_bar.rs b/alacritty_terminal/src/message_bar.rs index 8883dcb004..ddeb38dd3f 100644 --- a/alacritty_terminal/src/message_bar.rs +++ b/alacritty_terminal/src/message_bar.rs @@ -455,8 +455,14 @@ mod test { let msg = Message::new(String::from("test"), color::RED); message_buffer.tx().send(msg).unwrap(); } - message_buffer.tx().send(Message::new(String::from("other"), color::RED)).unwrap(); - message_buffer.tx().send(Message::new(String::from("test"), color::YELLOW)).unwrap(); + message_buffer + .tx() + .send(Message::new(String::from("other"), color::RED)) + .unwrap(); + message_buffer + .tx() + .send(Message::new(String::from("test"), color::YELLOW)) + .unwrap(); let _ = message_buffer.message(); message_buffer.pop(); diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 9df07f07c8..da94bf715a 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -299,7 +299,8 @@ impl GlyphCache { #[cfg(feature = "hb-ft")] pub fn get_raw<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L, glyph_i: u32) -> Glyph - where L: LoadGlyph + where + L: LoadGlyph, { let glyph_offset = self.glyph_offset; let rasterizer = &mut self.rasterizer; @@ -314,17 +315,18 @@ impl GlyphCache { .entry(fixed_glyph_key) .or_insert_with(|| { */ - let mut rasterized = rasterizer.get_glyph_raw(glyph_key, glyph_i) - .unwrap_or_else(|_| Default::default()); + let mut rasterized = rasterizer + .get_glyph_raw(glyph_key, glyph_i) + .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; + rasterized.left += i32::from(glyph_offset.x); + rasterized.top += i32::from(glyph_offset.y); + rasterized.top -= metrics.descent as i32; - loader.load_glyph(&rasterized) - /* - }) - */ + loader.load_glyph(&rasterized) + /* + }) + */ } pub fn update_font_size( @@ -761,7 +763,12 @@ impl QuadRenderer { 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); + gl::Viewport( + padding_x, + padding_y, + width - 2 * padding_x, + height - 2 * padding_y, + ); // Disable program gl::UseProgram(0); @@ -862,7 +869,12 @@ impl QuadRenderer { let height = height as i32; let padding_x = padding_x as i32; let padding_y = padding_y as i32; - gl::Viewport(padding_x, padding_y, width - 2 * padding_x, height - 2 * padding_y); + gl::Viewport( + padding_x, + padding_y, + width - 2 * padding_x, + height - 2 * padding_y, + ); // update projection gl::UseProgram(self.program.id); @@ -1016,9 +1028,13 @@ impl<'a> RenderApi<'a> { self.render_batch(); } } - - pub fn render_glyph_at_position(&mut self, cell: &RenderableCell, - glyph_cache: &mut GlyphCache, glyph: char) { + + pub fn render_glyph_at_position( + &mut self, + cell: &RenderableCell, + glyph_cache: &mut GlyphCache, + glyph: char, + ) { if cell.flags.contains(cell::Flags::HIDDEN) { return; } @@ -1037,7 +1053,7 @@ impl<'a> RenderApi<'a> { size: glyph_cache.font_size, c: glyph, }; - + // Add cell to batch let glyph = glyph_cache.get(glyph_key, self); self.add_render_item(cell, &glyph); diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index b4f8a012af..c54f467959 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -28,7 +28,12 @@ pub struct Rect { impl Rect { pub fn new(x: T, y: T, width: T, height: T) -> Self { - Rect { x, y, width, height } + Rect { + x, + y, + width, + height, + } } } diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 400dd67b6a..ae6e2b0960 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -387,7 +387,7 @@ impl RenderableCell { // None of the above, keep original color. _ => colors[ansi], } - }, + } Color::Indexed(idx) => { let idx = match ( config.draw_bold_text_with_bright_colors(), @@ -707,7 +707,7 @@ impl VisualBell { let inverse_intensity = match self.animation { VisualBellAnimation::Ease | VisualBellAnimation::EaseOut => { cubic_bezier(0.25, 0.1, 0.25, 1.0, time) - }, + } VisualBellAnimation::EaseOutSine => cubic_bezier(0.39, 0.575, 0.565, 1.0, time), VisualBellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time), VisualBellAnimation::EaseOutCubic => { @@ -925,7 +925,12 @@ impl Term { let history_size = config.scrolling.history() as usize; let grid = Grid::new(num_lines, num_cols, history_size, Cell::default()); - let alt = Grid::new(num_lines, num_cols, 0 /* scroll history */, Cell::default()); + let alt = Grid::new( + num_lines, + num_cols, + 0, /* scroll history */ + Cell::default(), + ); let tabspaces = config.tabspaces(); let tabs = TabStops::new(grid.num_cols(), tabspaces); @@ -1215,13 +1220,15 @@ impl Term { // Scroll up to keep cursor in terminal if self.cursor.point.line >= num_lines { let lines = self.cursor.point.line - num_lines + 1; - self.grid.scroll_up(&(Line(0)..old_lines), lines, &self.cursor.template); + self.grid + .scroll_up(&(Line(0)..old_lines), lines, &self.cursor.template); } // Scroll up alt grid as well if self.cursor_save_alt.point.line >= num_lines { let lines = self.cursor_save_alt.point.line - num_lines + 1; - self.alt_grid.scroll_up(&(Line(0)..old_lines), lines, &self.cursor_save_alt.template); + self.alt_grid + .scroll_up(&(Line(0)..old_lines), lines, &self.cursor_save_alt.template); } // Move prompt down when growing if scrollback lines are available @@ -1235,7 +1242,10 @@ impl Term { } } - debug!("New num_cols is {} and num_lines is {}", num_cols, num_lines); + debug!( + "New num_cols is {} and num_lines is {}", + num_cols, num_lines + ); // Resize grids to new size let is_alt = self.mode.contains(TermMode::ALT_SCREEN); @@ -1290,7 +1300,11 @@ impl Term { /// Expects origin to be in scroll range. #[inline] fn scroll_down_relative(&mut self, origin: Line, mut lines: Line) { - trace!("Scrolling down relative: origin={}, lines={}", origin, lines); + trace!( + "Scrolling down relative: origin={}, lines={}", + origin, + lines + ); lines = min(lines, self.scroll_region.end - self.scroll_region.start); lines = min(lines, self.scroll_region.end - origin); @@ -1608,7 +1622,7 @@ impl ansi::Handler for Term { match arg { 5 => { let _ = writer.write_all(b"\x1b[0n"); - }, + } 6 => { let pos = self.cursor.point; let response = format!("\x1b[{};{}R", pos.line + 1, pos.col + 1); @@ -1773,7 +1787,11 @@ impl ansi::Handler for Term { #[inline] fn erase_chars(&mut self, count: Column) { - trace!("Erasing chars: count={}, col={}", count, self.cursor.point.col); + trace!( + "Erasing chars: count={}, col={}", + count, + self.cursor.point.col + ); let start = self.cursor.point.col; let end = min(start + count, self.grid.num_cols()); @@ -1864,19 +1882,19 @@ impl ansi::Handler for Term { for cell in &mut row[col..] { cell.reset(&template); } - }, + } ansi::LineClearMode::Left => { let row = &mut self.grid[self.cursor.point.line]; for cell in &mut row[..=col] { cell.reset(&template); } - }, + } ansi::LineClearMode::All => { let row = &mut self.grid[self.cursor.point.line]; for cell in &mut row[..] { cell.reset(&template); } - }, + } } } @@ -1934,7 +1952,7 @@ impl ansi::Handler for Term { .region_mut((self.cursor.point.line + 1)..) .each(|cell| cell.reset(&template)); } - }, + } ansi::ClearMode::All => self.grid.region_mut(..).each(|c| c.reset(&template)), ansi::ClearMode::Above => { // If clearing more than one line @@ -1961,7 +1979,7 @@ impl ansi::Handler for Term { ansi::TabulationClearMode::Current => { let column = self.cursor.point.col; self.tabs[column] = false; - }, + } ansi::TabulationClearMode::All => { self.tabs.clear_all(); }, @@ -2014,7 +2032,7 @@ impl ansi::Handler for Term { self.cursor.template.fg = Color::Named(NamedColor::Foreground); self.cursor.template.bg = Color::Named(NamedColor::Background); self.cursor.template.flags = cell::Flags::empty(); - }, + } Attr::Reverse => self.cursor.template.flags.insert(cell::Flags::INVERSE), Attr::CancelReverse => self.cursor.template.flags.remove(cell::Flags::INVERSE), Attr::Bold => self.cursor.template.flags.insert(cell::Flags::BOLD), @@ -2094,11 +2112,11 @@ impl ansi::Handler for Term { ansi::Mode::ReportMouseClicks => { self.mode.remove(TermMode::MOUSE_REPORT_CLICK); self.set_mouse_cursor(MouseCursor::Text); - }, + } ansi::Mode::ReportCellMouseMotion => { self.mode.remove(TermMode::MOUSE_DRAG); self.set_mouse_cursor(MouseCursor::Text); - }, + } ansi::Mode::ReportAllMouseMotion => { self.mode.remove(TermMode::MOUSE_MOTION); self.set_mouse_cursor(MouseCursor::Text); @@ -2237,17 +2255,26 @@ mod tests { mem::swap(&mut term.semantic_escape_chars, &mut escape_chars); { - *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(1) })); + *term.selection_mut() = Some(Selection::semantic(Point { + line: 2, + col: Column(1), + })); assert_eq!(term.selection_to_string(), Some(String::from("aa"))); } { - *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(4) })); + *term.selection_mut() = Some(Selection::semantic(Point { + line: 2, + col: Column(4), + })); assert_eq!(term.selection_to_string(), Some(String::from("aaa"))); } { - *term.selection_mut() = Some(Selection::semantic(Point { line: 1, col: Column(1) })); + *term.selection_mut() = Some(Selection::semantic(Point { + line: 1, + col: Column(1), + })); assert_eq!(term.selection_to_string(), Some(String::from("aaa"))); } } @@ -2274,7 +2301,10 @@ mod tests { mem::swap(&mut term.grid, &mut grid); - *term.selection_mut() = Some(Selection::lines(Point { line: 0, col: Column(3) })); + *term.selection_mut() = Some(Selection::lines(Point { + line: 0, + col: Column(3), + })); assert_eq!(term.selection_to_string(), Some(String::from("\"aa\"a\n"))); } @@ -2302,8 +2332,20 @@ mod tests { mem::swap(&mut term.grid, &mut grid); - let mut selection = Selection::simple(Point { line: 2, col: Column(0) }, Side::Left); - selection.update(Point { line: 0, col: Column(2) }, Side::Right); + let mut selection = Selection::simple( + Point { + line: 2, + col: Column(0), + }, + Side::Left, + ); + selection.update( + Point { + line: 0, + col: Column(2), + }, + Side::Right, + ); *term.selection_mut() = Some(selection); assert_eq!(term.selection_to_string(), Some("aaa\n\naaa\n".into())); } @@ -2427,7 +2469,8 @@ mod tests { let mut term: Term = Term::new(&config, size, MessageBuffer::new(), Clipboard::new_nop()); // Add one line of scrollback - term.grid.scroll_up(&(Line(0)..Line(1)), Line(1), &Cell::default()); + term.grid + .scroll_up(&(Line(0)..Line(1)), Line(1), &Cell::default()); // Clear the history term.clear_screen(ansi::ClearMode::Saved); diff --git a/alacritty_terminal/src/tty/unix.rs b/alacritty_terminal/src/tty/unix.rs index cafa7027bb..18a2a5d11f 100644 --- a/alacritty_terminal/src/tty/unix.rs +++ b/alacritty_terminal/src/tty/unix.rs @@ -103,7 +103,13 @@ fn get_pw_entry(buf: &mut [i8; 1024]) -> Passwd<'_> { // Try and read the pw file. let uid = unsafe { libc::getuid() }; let status = unsafe { - libc::getpwuid_r(uid, &mut entry, buf.as_mut_ptr() as *mut _, buf.len(), &mut res) + libc::getpwuid_r( + uid, + &mut entry, + buf.as_mut_ptr() as *mut _, + buf.len(), + &mut res, + ) }; if status < 0 { @@ -252,7 +258,7 @@ pub fn new(config: &Config, size: &T, window_id: Option) -> }; pty.resize(size); pty - }, + } Err(err) => { die!("Failed to spawn command: {}", err); }, diff --git a/alacritty_terminal/src/tty/windows/conpty.rs b/alacritty_terminal/src/tty/windows/conpty.rs index bd602c35e6..dc02469bf5 100644 --- a/alacritty_terminal/src/tty/windows/conpty.rs +++ b/alacritty_terminal/src/tty/windows/conpty.rs @@ -138,7 +138,15 @@ pub fn new<'a>(config: &Config, size: &SizeInfo, _window_id: Option) -> O let mut startup_info_ex: STARTUPINFOEXW = Default::default(); +<<<<<<< HEAD:alacritty_terminal/src/tty/windows/conpty.rs let title = config.window.title.as_ref().map(String::as_str).unwrap_or("Alacritty"); +======= + let title = options + .title + .as_ref() + .map(|w| w.as_str()) + .unwrap_or("Alacritty"); +>>>>>>> Tried setting scale and ppem but it didn't change anything.:src/tty/windows/conpty.rs let title = U16CString::from_str(title).unwrap(); startup_info_ex.StartupInfo.lpTitle = title.as_ptr() as LPWSTR; diff --git a/alacritty_terminal/src/tty/windows/winpty.rs b/alacritty_terminal/src/tty/windows/winpty.rs index 8795ca6df6..2915e0c8c9 100644 --- a/alacritty_terminal/src/tty/windows/winpty.rs +++ b/alacritty_terminal/src/tty/windows/winpty.rs @@ -27,6 +27,7 @@ use winapi::um::winbase::FILE_FLAG_OVERLAPPED; use winpty::Config as WinptyConfig; use winpty::{ConfigFlags, MouseMode, SpawnConfig, SpawnFlags, Winpty}; +use crate::cli::Options; use crate::config::{Config, Shell}; use crate::display::OnResize; use crate::term::SizeInfo; @@ -47,7 +48,13 @@ unsafe impl<'a> Sync for Agent<'a> {} impl<'a> Agent<'a> { pub fn new(winpty: Winpty<'a>) -> Self { +<<<<<<< HEAD:alacritty_terminal/src/tty/windows/winpty.rs Self { winpty: Box::into_raw(Box::new(winpty)) } +======= + Self { + winpty: Box::into_raw(Box::new(winpty)), + } +>>>>>>> Tried setting scale and ppem but it didn't change anything.:src/tty/windows/winpty.rs } /// Get immutable access to Winpty. @@ -94,7 +101,14 @@ pub fn new<'a>(config: &Config, size: &SizeInfo, _window_id: Option) -> P cmdline.insert(0, shell.program.to_string()); // Warning, here be borrow hell +<<<<<<< HEAD:alacritty_terminal/src/tty/windows/winpty.rs let cwd = config.working_directory().as_ref().map(|dir| canonicalize(dir).unwrap()); +======= + let cwd = options + .working_dir + .as_ref() + .map(|dir| canonicalize(dir).unwrap()); +>>>>>>> Tried setting scale and ppem but it didn't change anything.:src/tty/windows/winpty.rs let cwd = cwd.as_ref().map(|dir| dir.to_str().unwrap()); // Spawn process diff --git a/alacritty_terminal/tests/ref.rs b/alacritty_terminal/tests/ref.rs index 91c0b6eedd..0ebedbd180 100644 --- a/alacritty_terminal/tests/ref.rs +++ b/alacritty_terminal/tests/ref.rs @@ -6,6 +6,7 @@ use std::fs::File; use std::io::{self, Read}; use std::path::Path; +<<<<<<< HEAD:alacritty_terminal/tests/ref.rs use alacritty_terminal::ansi; use alacritty_terminal::clipboard::Clipboard; use alacritty_terminal::config::Config; @@ -16,6 +17,17 @@ use alacritty_terminal::term::SizeInfo; use alacritty_terminal::util::fmt::{Green, Red}; use alacritty_terminal::Grid; use alacritty_terminal::Term; +======= +use alacritty::ansi; +use alacritty::config::Config; +use alacritty::index::Column; +use alacritty::message_bar::MessageBuffer; +use alacritty::term::cell::Cell; +use alacritty::term::SizeInfo; +use alacritty::util::fmt::{Green, Red}; +use alacritty::Grid; +use alacritty::Term; +>>>>>>> Tried setting scale and ppem but it didn't change anything.:tests/ref.rs macro_rules! ref_tests { ($($name:ident)*) => { @@ -61,7 +73,14 @@ where P: AsRef, { let mut res = Vec::new(); +<<<<<<< HEAD:alacritty_terminal/tests/ref.rs File::open(path.as_ref()).unwrap().read_to_end(&mut res).unwrap(); +======= + File::open(path.as_ref()) + .unwrap() + .read_to_end(&mut res) + .unwrap(); +>>>>>>> Tried setting scale and ppem but it didn't change anything.:tests/ref.rs res } diff --git a/build.rs b/build.rs new file mode 100644 index 0000000000..19127a1d93 --- /dev/null +++ b/build.rs @@ -0,0 +1,94 @@ +// 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. +#[cfg(windows)] +use embed_resource; +#[cfg(windows)] +use reqwest; +#[cfg(windows)] +use tempfile; +#[cfg(windows)] +use zip; + +use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry}; + +use std::env; +use std::fs::File; +use std::path::Path; + +#[cfg(windows)] +use std::fs::OpenOptions; +#[cfg(windows)] +use std::io; + +#[cfg(windows)] +const WINPTY_PACKAGE_URL: &str = + "https://github.com/rprichard/winpty/releases/download/0.4.3/winpty-0.4.3-msvc2015.zip"; + +fn main() { + 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("assets/windows/windows.rc"); + + // Path is relative to target/{profile}/build/alacritty-HASH/out + let file = Path::new(&env::var("OUT_DIR").unwrap()).join("../../../winpty-agent.exe"); + if !file.exists() { + aquire_winpty_agent(&file); + } + } +} + +#[cfg(windows)] +fn aquire_winpty_agent(out_path: &Path) { + let tmp_dir = tempfile::Builder::new() + .prefix("alacritty_build") + .tempdir() + .unwrap(); + + let mut response = reqwest::get(WINPTY_PACKAGE_URL).unwrap(); + let mut file = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(tmp_dir.path().join("winpty_package.zip")) + .unwrap(); + + io::copy(&mut response, &mut file).unwrap(); + + let mut archive = zip::ZipArchive::new(file).unwrap(); + + let target = match env::var("TARGET").unwrap().split("-").next().unwrap() { + "x86_64" => "x64", + "i386" => "ia32", + _ => panic!("architecture has no winpty binary"), + }; + + let mut winpty_agent = archive + .by_name(&format!("{}/bin/winpty-agent.exe", target)) + .unwrap(); + + io::copy(&mut winpty_agent, &mut File::create(out_path).unwrap()).unwrap(); +} diff --git a/copypasta/src/macos.rs b/copypasta/src/macos.rs new file mode 100644 index 0000000000..23ffe1c74e --- /dev/null +++ b/copypasta/src/macos.rs @@ -0,0 +1,330 @@ +//! Clipboard access on macOS +//! +//! Implemented according to +//! https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/PasteboardGuide106/Articles/pbReading.html#//apple_ref/doc/uid/TP40008123-SW1 + +mod ns { + extern crate objc_foundation; + extern crate objc_id; + + #[link(name = "AppKit", kind = "framework")] + extern "C" {} + + use std::mem; + + use self::objc_foundation::{INSArray, INSObject, INSString}; + use self::objc_foundation::{NSArray, NSDictionary, NSObject, NSString}; + use self::objc_id::{Id, Owned}; + use objc::runtime::{Class, Object}; + + /// Rust API for NSPasteboard + pub struct Pasteboard(Id); + + /// Errors occurring when creating a Pasteboard + #[derive(Debug)] + pub enum NewPasteboardError { + GetPasteboardClass, + LoadGeneralPasteboard, + } + + /// Errors occurring when reading a string from the pasteboard + #[derive(Debug)] + pub enum ReadStringError { + GetStringClass, + ReadObjectsForClasses, + } + + /// Errors from writing strings to the pasteboard + #[derive(Debug)] + pub struct WriteStringError; + + /// A trait for reading contents from the pasteboard + /// + /// This is intended to reflect the underlying objective C API + /// `readObjectsForClasses:options:`. + pub trait PasteboardReadObject { + type Err; + fn read_object(&self) -> Result; + } + + /// A trait for writing contents to the pasteboard + pub trait PasteboardWriteObject { + type Err; + fn write_object(&mut self, T) -> Result<(), Self::Err>; + } + + impl PasteboardReadObject for Pasteboard { + type Err = ReadStringError; + fn read_object(&self) -> Result { + // Get string class; need this for passing to readObjectsForClasses + let ns_string_class = match Class::get("NSString") { + Some(class) => class, + None => return Err(ReadStringError::GetStringClass), + }; + + let ns_string: Id = unsafe { + let ptr: *mut Object = msg_send![ns_string_class, class]; + + if ptr.is_null() { + return Err(ReadStringError::GetStringClass); + } else { + Id::from_ptr(ptr) + } + }; + + let classes: Id> = unsafe { + // I think this transmute is valid. It's going from an + // Id to an Id. From transmute's perspective, + // the only thing that matters is that they both have the same + // size (they do for now since the generic is phantom data). In + // both cases, the underlying pointer is an id (from `[NSString + // class]`), so again, this should be valid. There's just + // nothing implemented in objc_id or objc_foundation to do this + // "safely". By the way, the only reason this is necessary is + // because INSObject isn't implemented for Id. + // + // And if that argument isn't convincing, my final reasoning is + // that "it seems to work". + NSArray::from_vec(vec![mem::transmute(ns_string)]) + }; + + // No options + // + // Apparently this doesn't compile without a type hint, so it maps + // objects to objects! + let options: Id> = NSDictionary::new(); + + // call [pasteboard readObjectsForClasses:options:] + let copied_items = unsafe { + let copied_items: *mut NSArray = msg_send![ + self.0, + readObjectsForClasses:&*classes + options:&*options + ]; + + if copied_items.is_null() { + return Err(ReadStringError::ReadObjectsForClasses); + } else { + Id::from_ptr(copied_items) as Id> + } + }; + + // Ok, this is great. We have an NSArray, and these have + // decent bindings. Use the first item returned (if an item was + // returned) or just return an empty string + // + // XXX Should this return an error if no items were returned? + let contents = copied_items + .first_object() + .map(|ns_string| ns_string.as_str().to_owned()) + .unwrap_or_else(String::new); + + Ok(contents) + } + } + + impl PasteboardWriteObject for Pasteboard { + type Err = WriteStringError; + + fn write_object(&mut self, object: String) -> Result<(), Self::Err> { + let objects = NSArray::from_vec(vec![NSString::from_str(&object)]); + + self.clear_contents(); + + // The writeObjects method returns true in case of success, and + // false otherwise. + let ok: bool = unsafe { msg_send![self.0, writeObjects: objects] }; + + if ok { + Ok(()) + } else { + Err(WriteStringError) + } + } + } + + impl ::std::error::Error for WriteStringError { + fn description(&self) -> &str { + "Failed writing string to the NSPasteboard (writeContents returned false)" + } + } + + impl ::std::fmt::Display for WriteStringError { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.write_str(::std::error::Error::description(self)) + } + } + + impl ::std::error::Error for ReadStringError { + fn description(&self) -> &str { + match *self { + ReadStringError::GetStringClass => "NSString class not found", + ReadStringError::ReadObjectsForClasses => "readObjectsForClasses:options: failed", + } + } + } + + impl ::std::fmt::Display for ReadStringError { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.write_str(::std::error::Error::description(self)) + } + } + + impl ::std::error::Error for NewPasteboardError { + fn description(&self) -> &str { + match *self { + NewPasteboardError::GetPasteboardClass => "NSPasteboard class not found", + NewPasteboardError::LoadGeneralPasteboard => { + "[NSPasteboard generalPasteboard] failed" + } + } + } + } + + impl ::std::fmt::Display for NewPasteboardError { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + f.write_str(::std::error::Error::description(self)) + } + } + + impl Pasteboard { + pub fn new() -> Result { + // NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + let ns_pasteboard_class = match Class::get("NSPasteboard") { + Some(class) => class, + None => return Err(NewPasteboardError::GetPasteboardClass), + }; + + let ptr = unsafe { + let ptr: *mut Object = msg_send![ns_pasteboard_class, generalPasteboard]; + + if ptr.is_null() { + return Err(NewPasteboardError::LoadGeneralPasteboard); + } else { + ptr + } + }; + + let id = unsafe { Id::from_ptr(ptr) }; + + Ok(Pasteboard(id)) + } + + /// Clears the existing contents of the pasteboard, preparing it for new + /// contents. + /// + /// This is the first step in providing data on the pasteboard. The + /// return value is the change count of the pasteboard + pub fn clear_contents(&mut self) -> usize { + unsafe { msg_send![self.0, clearContents] } + } + } +} + +#[derive(Debug)] +pub enum Error { + CreatePasteboard(ns::NewPasteboardError), + ReadString(ns::ReadStringError), + WriteString(ns::WriteStringError), +} + +impl ::std::error::Error for Error { + fn cause(&self) -> Option<&::std::error::Error> { + match *self { + Error::CreatePasteboard(ref err) => Some(err), + Error::ReadString(ref err) => Some(err), + Error::WriteString(ref err) => Some(err), + } + } + + fn description(&self) -> &str { + match *self { + Error::CreatePasteboard(ref _err) => "Failed to create pasteboard", + Error::ReadString(ref _err) => "Failed to read string from pasteboard", + Error::WriteString(ref _err) => "Failed to write string to pasteboard", + } + } +} + +impl ::std::fmt::Display for Error { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + Error::CreatePasteboard(ref err) => write!(f, "Failed to create pasteboard: {}", err), + Error::ReadString(ref err) => { + write!(f, "Failed to read string from pasteboard: {}", err) + } + Error::WriteString(ref err) => { + write!(f, "Failed to write string to pasteboard: {}", err) + } + } + } +} + +impl From for Error { + fn from(val: ns::NewPasteboardError) -> Error { + Error::CreatePasteboard(val) + } +} + +impl From for Error { + fn from(val: ns::ReadStringError) -> Error { + Error::ReadString(val) + } +} + +impl From for Error { + fn from(val: ns::WriteStringError) -> Error { + Error::WriteString(val) + } +} + +pub struct Clipboard(ns::Pasteboard); + +impl super::Load for Clipboard { + type Err = Error; + + fn new() -> Result { + Ok(Clipboard(ns::Pasteboard::new()?)) + } + + fn load_primary(&self) -> Result { + use self::ns::PasteboardReadObject; + + self.0.read_object().map_err(::std::convert::From::from) + } +} + +impl super::Store for Clipboard { + fn store_primary(&mut self, contents: S) -> Result<(), Self::Err> + where + S: Into, + { + use self::ns::PasteboardWriteObject; + + self.0 + .write_object(contents.into()) + .map_err(::std::convert::From::from) + } + + fn store_selection(&mut self, _contents: S) -> Result<(), Self::Err> + where + S: Into, + { + // No such thing on macOS + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::Clipboard; + use {Load, Store}; + + #[test] + fn create_clipboard_save_load_contents() { + let mut clipboard = Clipboard::new().unwrap(); + clipboard.store_primary("arst").unwrap(); + let loaded = clipboard.load_primary().unwrap(); + assert_eq!("arst", loaded); + } +} diff --git a/copypasta/src/x11.rs b/copypasta/src/x11.rs index b54867e827..af7476c27c 100644 --- a/copypasta/src/x11.rs +++ b/copypasta/src/x11.rs @@ -7,10 +7,10 @@ //! //! FIXME: Implement actual X11 clipboard API using the ICCCM reference //! https://tronche.com/gui/x/icccm/ +use std::ffi::OsStr; use std::io; -use std::process::{Output, Command}; +use std::process::{Command, Output}; use std::string::FromUtf8Error; -use std::ffi::OsStr; use super::{Load, Store}; @@ -45,13 +45,11 @@ impl ::std::error::Error for Error { impl ::std::fmt::Display for Error { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match *self { - Error::Io(ref err) => { - match err.kind() { - io::ErrorKind::NotFound => { - write!(f, "Please install `xclip` to enable clipboard support") - }, - _ => write!(f, "Error calling xclip: {}", err), + Error::Io(ref err) => match err.kind() { + io::ErrorKind::NotFound => { + write!(f, "Please install `xclip` to enable clipboard support") } + _ => write!(f, "Error calling xclip: {}", err), }, Error::Xclip(ref s) => write!(f, "Error from xclip: {}", s), Error::Utf8(ref err) => write!(f, "Error parsing xclip output: {}", err), @@ -87,9 +85,7 @@ impl Load for Clipboard { } fn load_selection(&self) -> Result { - let output = Command::new("xclip") - .args(&["-o"]) - .output()?; + let output = Command::new("xclip").args(&["-o"]).output()?; Clipboard::process_xclip_output(output) } @@ -99,7 +95,8 @@ impl Store for Clipboard { /// Sets the primary clipboard contents #[inline] fn store_primary(&mut self, contents: S) -> Result<(), Self::Err> - where S: Into + where + S: Into, { self.store(contents, &["-i", "-selection", "clipboard"]) } @@ -107,7 +104,8 @@ impl Store for Clipboard { /// Sets the secondary clipboard contents #[inline] fn store_selection(&mut self, contents: S) -> Result<(), Self::Err> - where S: Into + where + S: Into, { self.store(contents, &["-i"]) } @@ -116,20 +114,19 @@ impl Store for Clipboard { impl Clipboard { fn process_xclip_output(output: Output) -> Result { if output.status.success() { - String::from_utf8(output.stdout) - .map_err(::std::convert::From::from) + String::from_utf8(output.stdout).map_err(::std::convert::From::from) } else { - String::from_utf8(output.stderr) - .map_err(::std::convert::From::from) + String::from_utf8(output.stderr).map_err(::std::convert::From::from) } } fn store(&mut self, contents: C, args: &[S]) -> Result<(), Error> - where C: Into, - S: AsRef, + where + C: Into, + S: AsRef, { use std::io::Write; - use std::process::{Stdio}; + use std::process::Stdio; let contents = contents.into(); let mut child = Command::new("xclip") @@ -154,7 +151,7 @@ impl Clipboard { #[cfg(test)] mod tests { use super::Clipboard; - use ::{Load, Store}; + use {Load, Store}; #[test] fn clipboard_works() { diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index cccae0327f..339c7c2b11 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -117,8 +117,13 @@ impl ::std::fmt::Display for Error { Error::MissingGlyph(ref c) => write!(f, "Glyph not found for char {:?}", c), Error::MissingFont(ref desc) => write!( f, +<<<<<<< HEAD "Couldn't find a font with {}\n\tPlease check the font config in your \ alacritty.yml.", +======= + "Couldn't find a font with {}\ + \n\tPlease check the font config in your alacritty.yml.", +>>>>>>> Tried setting scale and ppem but it didn't change anything. desc ), Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"), @@ -161,7 +166,14 @@ impl ::Rasterize for Rasterizer { /// Get rasterized glyph for given glyph key fn get_glyph(&mut self, glyph: GlyphKey) -> Result { // get loaded font +<<<<<<< HEAD let font = self.fonts.get(&glyph.font_key).ok_or(Error::FontNotLoaded)?; +======= + let font = self + .fonts + .get(&glyph.font_key) + .ok_or(Error::FontNotLoaded)?; +>>>>>>> Tried setting scale and ppem but it didn't change anything. // first try the font itself as a direct hit self.maybe_get_glyph(glyph, font).unwrap_or_else(|| { @@ -236,7 +248,7 @@ impl Rasterizer { Style::Specific(ref style) => self.get_specific_face(desc, style, size), Style::Description { slant, weight } => { self.get_matching_face(desc, slant, weight, size) - }, + } } } @@ -297,8 +309,15 @@ pub fn get_family_names() -> Vec { fn cascade_list_for_languages(ct_font: &CTFont, languages: &[String]) -> Vec { // convert language type &Vec -> CFArray let langarr: CFArray = { +<<<<<<< HEAD let tmp: Vec = languages.iter().map(|language| CFString::new(&language)).collect(); +======= + let tmp: Vec = languages + .iter() + .map(|language| CFString::new(&language)) + .collect(); +>>>>>>> Tried setting scale and ppem but it didn't change anything. CFArray::from_CFTypes(&tmp) }; @@ -365,11 +384,22 @@ impl Descriptor { }; // Include Menlo in the fallback list as well +<<<<<<< HEAD fallbacks.insert(0, Font { cg_font: menlo.copy_to_CGFont(), ct_font: menlo, fallbacks: Vec::new(), }); +======= + fallbacks.insert( + 0, + Font { + cg_font: menlo.copy_to_CGFont(), + ct_font: menlo, + fallbacks: Vec::new(), + }, + ); +>>>>>>> Tried setting scale and ppem but it didn't change anything. fallbacks }) @@ -450,8 +480,43 @@ impl Font { _size: f64, use_thin_strokes: bool, ) -> Result { +<<<<<<< HEAD let glyph_index = self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(character))?; +======= + // Render custom symbols for underline and beam cursor + match character { + super::UNDERLINE_CURSOR_CHAR => { + // Get the bottom of the bounding box + let descent = -(self.ct_font.descent() as i32); + // Get the width of the cell + let width = self.glyph_advance('0') as i32; + // Return the new custom glyph + return super::get_underline_cursor_glyph(descent, width); + } + super::BEAM_CURSOR_CHAR | super::BOX_CURSOR_CHAR => { + // Get the top of the bounding box + let metrics = self.metrics(); + let height = metrics.line_height; + let ascent = (height - self.ct_font.descent()).ceil(); + + // Get the width of the cell + let width = self.glyph_advance('0') as i32; + + // Return the new custom glyph + if character == super::BEAM_CURSOR_CHAR { + return super::get_beam_cursor_glyph(ascent as i32, height as i32, width); + } else { + return super::get_box_cursor_glyph(ascent as i32, height as i32, width); + } + } + _ => (), + } + + let glyph_index = self + .glyph_index(character) + .ok_or_else(|| Error::MissingGlyph(character))?; +>>>>>>> Tried setting scale and ppem but it didn't change anything. let bounds = self.bounding_rect_for_glyph(Default::default(), glyph_index); @@ -537,7 +602,10 @@ impl Font { // and use the utf-16 buffer to get the index self.glyph_index_utf16(encoded) } +<<<<<<< HEAD +======= +>>>>>>> Tried setting scale and ppem but it didn't change anything. fn glyph_index_utf16(&self, encoded: &[u16]) -> Option { // output buffer for the glyph. for non-BMP glyphs, like // emojis, this will be filled with two chars the second @@ -576,7 +644,14 @@ mod tests { println!("{:?}", list); // Check to_font +<<<<<<< HEAD let fonts = list.iter().map(|desc| desc.to_font(72., false)).collect::>(); +======= + let fonts = list + .iter() + .map(|desc| desc.to_font(72., false)) + .collect::>(); +>>>>>>> Tried setting scale and ppem but it didn't change anything. for font in fonts { // Get a glyph diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index d7f62df436..d17745842c 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -18,16 +18,16 @@ use std::collections::HashMap; use std::fmt; use std::path::PathBuf; +#[cfg(feature = "hb-ft")] +use super::HbGlyph; use freetype::tt_os2::TrueTypeOS2Table; use freetype::{self, Library}; use libc::c_uint; -#[cfg(feature = "hb-ft")] -use super::HbGlyph; - -pub mod fc; use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; +mod fc; + struct FixedSize { pixelsize: f64, } @@ -49,7 +49,9 @@ struct Face { impl ::std::ops::Drop for Face { fn drop(&mut self) { if let Some(hb_font) = self.hb_font { - unsafe { harfbuzz::sys::hb_font_destroy(hb_font); } + unsafe { + harfbuzz::sys::hb_font_destroy(hb_font); + } } } } @@ -60,14 +62,17 @@ impl fmt::Debug for Face { .field("ft_face", &self.ft_face) .field("key", &self.key) .field("load_flags", &self.load_flags) - .field("render_mode", &match self.render_mode { - freetype::RenderMode::Normal => "Normal", - freetype::RenderMode::Light => "Light", - freetype::RenderMode::Mono => "Mono", - freetype::RenderMode::Lcd => "Lcd", - freetype::RenderMode::LcdV => "LcdV", - freetype::RenderMode::Max => "Max", - }) + .field( + "render_mode", + &match self.render_mode { + freetype::RenderMode::Normal => "Normal", + freetype::RenderMode::Light => "Light", + freetype::RenderMode::Mono => "Mono", + freetype::RenderMode::Lcd => "Lcd", + freetype::RenderMode::LcdV => "LcdV", + freetype::RenderMode::Max => "Max", + }, + ) .field("lcd_filter", &self.lcd_filter) .finish() } @@ -167,51 +172,62 @@ fn hb_tag(f: &str) -> harfbuzz::sys::hb_tag_t { #[cfg(feature = "hb-ft")] impl ::HbFtExt for FreeTypeRasterizer { fn shape(&mut self, text: &str, font_key: FontKey, size: Size) -> Option> { - self.faces[&font_key].hb_font.map(|hb_font| { + let size_metrics = self.faces[&font_key].ft_face.size_metrics(); + println!("{:?}", size_metrics); + self.faces[&font_key].hb_font.and_then(|hb_font| size_metrics.map(|s| (hb_font, s))).map(|(hb_font, size_metrics)| { let mut buf = harfbuzz::Buffer::with(text); buf.set_direction(harfbuzz::Direction::LTR); - buf.set_script(harfbuzz::sys::HB_SCRIPT_LATIN); + buf.set_script(harfbuzz::sys::HB_SCRIPT_COMMON); buf.set_language(harfbuzz::Language::from_string("en")); // Shape unsafe { // ::std::ptr::null() == NULL (with type *const _) - harfbuzz::sys::hb_shape(hb_font, buf.as_ptr(), std::ptr::null(), 0); + harfbuzz::sys::hb_shape_full( + hb_font, + buf.as_ptr(), + std::ptr::null(), + 0, + harfbuzz::sys::hb_shape_list_shapers(), + ); }; // Get glyph information let ginfo: &mut [harfbuzz::sys::hb_glyph_info_t] = unsafe { let mut len = 0u32; - let res = harfbuzz::sys::hb_buffer_get_glyph_infos(buf.as_ptr(), &mut len as *mut _); - ::std::slice::from_raw_parts_mut( - res, - len as _ - ) + let res = + harfbuzz::sys::hb_buffer_get_glyph_infos(buf.as_ptr(), &mut len as *mut _); + std::slice::from_raw_parts_mut(res, len as usize) }; // Get glyph positions let gpos: &mut [harfbuzz::sys::hb_glyph_position_t] = unsafe { let mut len = 0u32; - let res = harfbuzz::sys::hb_buffer_get_glyph_positions(buf.as_ptr(), &mut len as *mut _); - ::std::slice::from_raw_parts_mut( - res, - len as _ - ) + let res = + harfbuzz::sys::hb_buffer_get_glyph_positions(buf.as_ptr(), &mut len as *mut _); + std::slice::from_raw_parts_mut(res, len as usize) }; + // Combine into HbGlyph's - println!("Shaped text: {:?}", buf); - ginfo.iter_mut().zip(gpos.iter_mut()).map(|(gi, gp)| { - HbGlyph { - /* ugh -_- you have to divide by 64?? */ - x_advance: (gp.x_advance as f32) / 64., - y_advance: (gp.y_advance as f32) / 64., - x_offset: (gp.x_offset as f32) / 64., - y_offset: (gp.y_offset as f32) / 64., - glyph: GlyphKey { - c: text.chars().nth(gi.cluster as usize).expect("Expected cluster index to point to char in text None was found."),//::std::char::from_u32(gi.codepoint).expect("HB u32 is not a char!"), - font_key, - size, - }, - cluster: gi.cluster, - } - }).collect() + ginfo + .iter_mut() + .zip(gpos.iter_mut()) + .map(|(gi, gp)| { + println!("{:?}", (&gi, &gp)); + HbGlyph { + /* ugh -_- you have to divide by 64?? */ + x_advance: (gp.x_advance as f32) / 64., + y_advance: (gp.y_advance as f32) / 64., + x_offset: (gp.x_offset as f32) / 64., + y_offset: (gp.y_offset as f32) / 64., + glyph: GlyphKey { + c: text.chars().nth(gi.cluster as usize).expect( + "Expected cluster index to point to char in text None was found.", + ), //::std::char::from_u32(gi.codepoint).expect("HB u32 is not a char!"), + font_key, + size, + }, + cluster: gi.cluster, + } + }) + .collect() }) } } @@ -346,26 +362,23 @@ impl FreeTypeRasterizer { let mut f = ::std::fs::File::open(&path).unwrap(); f.read_to_end(&mut buf).unwrap(); let blob = harfbuzz::Blob::new_read_only(&buf); - let _hb_face = unsafe { - harfbuzz::sys::hb_face_create(blob.as_raw(), index as u32) - }; + let _hb_face = + unsafe { harfbuzz::sys::hb_face_create(blob.as_raw(), index as u32) }; if _hb_face.is_null() { None } else { - let _hb_font = unsafe { - harfbuzz::sys::hb_font_create(_hb_face) - }; + let _hb_font = unsafe { harfbuzz::sys::hb_font_create(_hb_face) }; if _hb_font.is_null() { None } else { - unsafe { harfbuzz::sys::hb_ot_font_set_funcs(_hb_font); } - println!("Could create harfbuzz font"); + unsafe { + harfbuzz::sys::hb_ot_font_set_funcs(_hb_font); + } Some(_hb_font) } } }; - let face = Face { ft_face, key: FontKey::next(), @@ -423,7 +436,8 @@ impl FreeTypeRasterizer { glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72. }); - face.ft_face.set_char_size(to_freetype_26_6(size), 0, 0, 0)?; + face.ft_face + .set_char_size(to_freetype_26_6(size), 0, 0, 0)?; unsafe { let ft_lib = self.library.raw(); @@ -446,16 +460,22 @@ impl FreeTypeRasterizer { }) } - pub fn get_glyph_raw(&mut self, glyph_key: GlyphKey, glyph_i: u32) - -> Result { + pub fn get_glyph_raw( + &mut self, + glyph_key: GlyphKey, + glyph_i: u32, + ) -> Result { let font_key = self.face_for_glyph(glyph_key, false)?; let face = &self.faces[&font_key]; - let size = face.non_scalable.as_ref() + let size = face + .non_scalable + .as_ref() .map(|v| v.pixelsize as f32) .unwrap_or_else(|| glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.); - face.ft_face.set_char_size(to_freetype_26_6(size), 0, 0, 0)?; + face.ft_face + .set_char_size(to_freetype_26_6(size), 0, 0, 0)?; unsafe { let ft_lib = self.library.raw(); @@ -556,7 +576,7 @@ impl FreeTypeRasterizer { packed.extend_from_slice(&buf[start..stop]); } Ok((bitmap.rows(), bitmap.width() / 3, packed)) - }, + } PixelMode::LcdV => { for i in 0..bitmap.rows() / 3 { for j in 0..bitmap.width() { @@ -567,7 +587,7 @@ impl FreeTypeRasterizer { } } Ok((bitmap.rows() / 3, bitmap.width(), packed)) - }, + } // Mono data is stored in a packed format using 1 bit per pixel. PixelMode::Mono => { fn unpack_byte(res: &mut Vec, byte: u8, mut count: u8) { @@ -598,7 +618,7 @@ impl FreeTypeRasterizer { } } Ok((bitmap.rows(), bitmap.width(), packed)) - }, + } // Gray data is stored as a value between 0 and 255 using 1 byte per pixel. PixelMode::Gray => { for i in 0..bitmap.rows() { @@ -632,7 +652,7 @@ impl FreeTypeRasterizer { Some(&key) => { debug!("Hit for font {:?}; no need to load", path); Ok(key) - }, + } None => { debug!("Miss for font {:?}; loading now", path); diff --git a/font/src/lib.rs b/font/src/lib.rs index 5961845cb2..2b2042c5e4 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -18,7 +18,12 @@ //! FreeType is used on everything that's not Mac OS. //! Eventually, ClearType support will be available for windows -#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)] +#![deny( + clippy::all, + clippy::if_not_else, + clippy::enum_glob_use, + clippy::wrong_pub_self_convention +)] /* Note: all applicable cfg statements have been modified to short-circuit * to freetype if the feature hb-ft is enabled. @@ -48,13 +53,12 @@ extern crate libc; #[cfg(feature = "hb-ft")] extern crate harfbuzz; - #[cfg_attr(not(windows), macro_use)] extern crate log; -use std::fmt; use std::hash::{Hash, Hasher}; use std::sync::atomic::{AtomicUsize, Ordering}; +use std::{cmp, fmt}; // If target isn't macos or windows, reexport everything from ft #[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] diff --git a/font/src/rusttype/mod.rs b/font/src/rusttype/mod.rs new file mode 100644 index 0000000000..417099fbb9 --- /dev/null +++ b/font/src/rusttype/mod.rs @@ -0,0 +1,206 @@ +extern crate font_loader; +use self::font_loader::system_fonts; + +extern crate rusttype; +use self::rusttype::{point, Codepoint, FontCollection, Scale}; + +use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; + +pub struct RustTypeRasterizer { + fonts: Vec>, + dpi_ratio: f32, +} + +impl crate::Rasterize for RustTypeRasterizer { + type Err = Error; + + fn new(device_pixel_ratio: f32, _: bool) -> Result { + Ok(RustTypeRasterizer { + fonts: Vec::new(), + dpi_ratio: device_pixel_ratio, + }) + } + + fn metrics(&self, key: FontKey, size: Size) -> Result { + let scale = Scale::uniform(size.as_f32_pts() * self.dpi_ratio * 96. / 72.); + let vmetrics = self.fonts[key.token as usize].v_metrics(scale); + let hmetrics = self.fonts[key.token as usize] + .glyph( + // If the font is monospaced all glyphs *should* have the same width + // 33 '!' is the first displaying character + Codepoint(33), + ) + .ok_or(Error::MissingGlyph)? + .scaled(scale) + .h_metrics(); + + let line_height = f64::from(vmetrics.ascent - vmetrics.descent + vmetrics.line_gap); + let average_advance = f64::from(hmetrics.advance_width); + let descent = vmetrics.descent; + + // Strikeout and underline metrics. + // RustType doesn't support these, so we make up our own. + let thickness = (descent / 5.).round(); + let underline_position = descent / 2.; + let strikeout_position = line_height as f32 / 2. - descent; + + Ok(Metrics { + descent, + average_advance, + line_height, + underline_position, + underline_thickness: thickness, + strikeout_position, + strikeout_thickness: thickness, + }) + } + + fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result { + let fp = system_fonts::FontPropertyBuilder::new() + .family(&desc.name) + .monospace(); + + let fp = match desc.style { + Style::Specific(ref style) => match style.to_lowercase().as_str() { + "italic" => fp.italic(), + "bold" => fp.bold(), + _ => fp, + }, + Style::Description { slant, weight } => { + let fp = match slant { + Slant::Normal => fp, + Slant::Italic => fp.italic(), + // This style is not supported by rust-font-loader + Slant::Oblique => return Err(Error::UnsupportedStyle), + }; + match weight { + Weight::Bold => fp.bold(), + Weight::Normal => fp, + } + } + }; + self.fonts.push( + FontCollection::from_bytes( + system_fonts::get(&fp.build()) + .ok_or_else(|| Error::MissingFont(desc.clone()))? + .0, + ) + .into_font() + .ok_or(Error::UnsupportedFont)?, + ); + Ok(FontKey { + token: (self.fonts.len() - 1) as u16, + }) + } + + fn get_glyph(&mut self, glyph_key: GlyphKey) -> Result { + match glyph_key.c { + super::UNDERLINE_CURSOR_CHAR => { + let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?; + return super::get_underline_cursor_glyph( + metrics.descent as i32, + metrics.average_advance as i32, + ); + } + super::BEAM_CURSOR_CHAR => { + let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?; + + return super::get_beam_cursor_glyph( + (metrics.line_height + f64::from(metrics.descent)).round() as i32, + metrics.line_height.round() as i32, + metrics.average_advance.round() as i32, + ); + } + super::BOX_CURSOR_CHAR => { + let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?; + + return super::get_box_cursor_glyph( + (metrics.line_height + f64::from(metrics.descent)).round() as i32, + metrics.line_height.round() as i32, + metrics.average_advance.round() as i32, + ); + } + _ => (), + } + + let scaled_glyph = self.fonts[glyph_key.font_key.token as usize] + .glyph(glyph_key.c) + .ok_or(Error::MissingGlyph)? + .scaled(Scale::uniform( + glyph_key.size.as_f32_pts() * self.dpi_ratio * 96. / 72., + )); + + let glyph = scaled_glyph.positioned(point(0.0, 0.0)); + + // Pixel bounding box + let bb = match glyph.pixel_bounding_box() { + Some(bb) => bb, + // Bounding box calculation fails for spaces so we provide a placeholder bounding box + None => rusttype::Rect { + min: point(0, 0), + max: point(0, 0), + }, + }; + + let mut buf = Vec::with_capacity((bb.width() * bb.height()) as usize); + + glyph.draw(|_x, _y, v| { + buf.push((v * 255.0) as u8); + buf.push((v * 255.0) as u8); + buf.push((v * 255.0) as u8); + }); + Ok(RasterizedGlyph { + c: glyph_key.c, + width: bb.width(), + height: bb.height(), + top: -bb.min.y, + left: bb.min.x, + buf, + }) + } + + fn update_dpr(&mut self, device_pixel_ratio: f32) { + self.dpi_ratio = device_pixel_ratio; + } +} + +#[derive(Debug)] +pub enum Error { + MissingFont(FontDesc), + UnsupportedFont, + UnsupportedStyle, + // NOTE: This error is different from how the FreeType code handles it + MissingGlyph, +} + +impl ::std::error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::MissingFont(ref _desc) => "Couldn't find the requested font", + Error::UnsupportedFont => "Only TrueType fonts are supported", + Error::UnsupportedStyle => "The selected style is not supported by rusttype", + Error::MissingGlyph => "The selected font does not have the requested glyph", + } + } +} + +impl ::std::fmt::Display for Error { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + Error::MissingFont(ref desc) => write!( + f, + "Couldn't find a font with {}\ + \n\tPlease check the font config in your alacritty.yml.", + desc + ), + Error::UnsupportedFont => write!( + f, + "Rusttype only supports TrueType fonts.\n\tPlease select a TrueType font instead." + ), + Error::UnsupportedStyle => { + write!(f, "The selected font style is not supported by rusttype.") + } + Error::MissingGlyph => write!(f, "The selected font did not have the requested glyph."), + } + } +} diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000000..419a54efb3 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,258 @@ +// 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 ::log; +use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; + +use crate::config::{Delta, Dimensions, Shell}; +use crate::index::{Column, Line}; +use crate::window::DEFAULT_NAME; +use std::borrow::Cow; +use std::path::{Path, PathBuf}; + +/// Options specified on the command line +pub struct Options { + pub live_config_reload: Option, + pub print_events: bool, + pub ref_test: bool, + pub dimensions: Option, + pub position: Option>, + pub title: Option, + pub class: Option, + pub log_level: log::LevelFilter, + pub command: Option>, + pub working_dir: Option, + pub config: Option, + 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, + log_level: log::LevelFilter::Warn, + command: None, + working_dir: None, + config: None, + persistent_logging: false, + } + } +} + +impl Options { + /// Build `Options` from command line arguments + pub fn load() -> Options { + let mut options = Options::default(); + + let matches = App::new(crate_name!()) + .version(crate_version!()) + .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 X11 [default: {}]", + DEFAULT_NAME + )), + ) + .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( + "Specify alternative configuration file \ + [default: $XDG_CONFIG_HOME/alacritty/alacritty.yml]", + ), + ) + .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)"), + ) + .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(|x| x.parse::()); + let y = position.next().map(|y| y.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); + + match matches.occurrences_of("q") { + 0 => {} + 1 => options.log_level = log::LevelFilter::Error, + 2 | _ => options.log_level = log::LevelFilter::Off, + } + + match matches.occurrences_of("v") { + 0 if !options.print_events => {} + 0 | 1 => options.log_level = log::LevelFilter::Info, + 2 => options.log_level = log::LevelFilter::Debug, + 3 | _ => options.log_level = log::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)); + } + + options + } + + pub fn dimensions(&self) -> Option { + self.dimensions + } + + pub fn position(&self) -> Option> { + self.position + } + + pub fn command(&self) -> Option<&Shell<'_>> { + self.command.as_ref() + } + + pub fn config_path(&self) -> Option> { + self.config.as_ref().map(|p| Cow::Borrowed(p.as_path())) + } +} diff --git a/src/config/bindings.rs b/src/config/bindings.rs new file mode 100644 index 0000000000..0e00f6fea6 --- /dev/null +++ b/src/config/bindings.rs @@ -0,0 +1,217 @@ +// 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 glutin::{ModifiersState, MouseButton}; + +use super::Key; +use crate::input::{Action, KeyBinding, MouseBinding}; +use crate::term::TermMode; + +macro_rules! bindings { + ( + $ty:ident; + $( + $key:path + $(,[$($mod:ident: $enabled:expr),*])* + $(,+$mode:expr)* + $(,~$notmode:expr)* + ;$action:expr + );* + $(;)* + ) => {{ + let mut v = Vec::new(); + + $( + let mut _mods = ModifiersState { + $($($mod: $enabled),*,)* + ..Default::default() + }; + 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 { + bindings!( + MouseBinding; + MouseButton::Middle; Action::PasteSelection; + ) +} + +pub fn default_key_bindings() -> Vec { + let mut bindings = bindings!( + KeyBinding; + Key::Paste; Action::Paste; + Key::Copy; Action::Copy; + Key::L, [ctrl: true]; Action::ClearLogNotice; + Key::L, [ctrl: true]; Action::Esc("\x0c".into()); + Key::Home, [alt: true]; Action::Esc("\x1b[1;3H".into()); + Key::Home, +TermMode::APP_CURSOR; Action::Esc("\x1bOH".into()); + Key::Home, ~TermMode::APP_CURSOR; Action::Esc("\x1b[H".into()); + Key::End, [alt: true]; Action::Esc("\x1b[1;3F".into()); + Key::End, +TermMode::APP_CURSOR; Action::Esc("\x1bOF".into()); + Key::End, ~TermMode::APP_CURSOR; Action::Esc("\x1b[F".into()); + Key::PageUp, [shift: true], ~TermMode::ALT_SCREEN; Action::ScrollPageUp; + Key::PageUp, [shift: true], +TermMode::ALT_SCREEN; Action::Esc("\x1b[5;2~".into()); + Key::PageUp, [ctrl: true]; Action::Esc("\x1b[5;5~".into()); + Key::PageUp, [alt: true]; Action::Esc("\x1b[5;3~".into()); + Key::PageUp; Action::Esc("\x1b[5~".into()); + Key::PageDown, [shift: true], ~TermMode::ALT_SCREEN; Action::ScrollPageDown; + Key::PageDown, [shift: true], +TermMode::ALT_SCREEN; Action::Esc("\x1b[6;2~".into()); + Key::PageDown, [ctrl: true]; Action::Esc("\x1b[6;5~".into()); + Key::PageDown, [alt: true]; Action::Esc("\x1b[6;3~".into()); + Key::PageDown; Action::Esc("\x1b[6~".into()); + Key::Tab, [shift: true]; Action::Esc("\x1b[Z".into()); + Key::Back; Action::Esc("\x7f".into()); + Key::Back, [alt: true]; Action::Esc("\x1b\x7f".into()); + Key::Insert; Action::Esc("\x1b[2~".into()); + Key::Delete; Action::Esc("\x1b[3~".into()); + Key::Left, [shift: true]; Action::Esc("\x1b[1;2D".into()); + Key::Left, [ctrl: true]; Action::Esc("\x1b[1;5D".into()); + Key::Left, [alt: true]; Action::Esc("\x1b[1;3D".into()); + Key::Left, ~TermMode::APP_CURSOR; Action::Esc("\x1b[D".into()); + Key::Left, +TermMode::APP_CURSOR; Action::Esc("\x1bOD".into()); + Key::Right, [shift: true]; Action::Esc("\x1b[1;2C".into()); + Key::Right, [ctrl: true]; Action::Esc("\x1b[1;5C".into()); + Key::Right, [alt: true]; Action::Esc("\x1b[1;3C".into()); + Key::Right, ~TermMode::APP_CURSOR; Action::Esc("\x1b[C".into()); + Key::Right, +TermMode::APP_CURSOR; Action::Esc("\x1bOC".into()); + Key::Up, [shift: true]; Action::Esc("\x1b[1;2A".into()); + Key::Up, [ctrl: true]; Action::Esc("\x1b[1;5A".into()); + Key::Up, [alt: true]; Action::Esc("\x1b[1;3A".into()); + Key::Up, ~TermMode::APP_CURSOR; Action::Esc("\x1b[A".into()); + Key::Up, +TermMode::APP_CURSOR; Action::Esc("\x1bOA".into()); + Key::Down, [shift: true]; Action::Esc("\x1b[1;2B".into()); + Key::Down, [ctrl: true]; Action::Esc("\x1b[1;5B".into()); + Key::Down, [alt: true]; Action::Esc("\x1b[1;3B".into()); + Key::Down, ~TermMode::APP_CURSOR; Action::Esc("\x1b[B".into()); + Key::Down, +TermMode::APP_CURSOR; Action::Esc("\x1bOB".into()); + Key::F1; Action::Esc("\x1bOP".into()); + Key::F2; Action::Esc("\x1bOQ".into()); + Key::F3; Action::Esc("\x1bOR".into()); + Key::F4; Action::Esc("\x1bOS".into()); + Key::F5; Action::Esc("\x1b[15~".into()); + Key::F6; Action::Esc("\x1b[17~".into()); + Key::F7; Action::Esc("\x1b[18~".into()); + Key::F8; Action::Esc("\x1b[19~".into()); + Key::F9; Action::Esc("\x1b[20~".into()); + Key::F10; Action::Esc("\x1b[21~".into()); + Key::F11; Action::Esc("\x1b[23~".into()); + Key::F12; Action::Esc("\x1b[24~".into()); + Key::F1, [shift: true]; Action::Esc("\x1b[1;2P".into()); + Key::F2, [shift: true]; Action::Esc("\x1b[1;2Q".into()); + Key::F3, [shift: true]; Action::Esc("\x1b[1;2R".into()); + Key::F4, [shift: true]; Action::Esc("\x1b[1;2S".into()); + Key::F5, [shift: true]; Action::Esc("\x1b[15;2~".into()); + Key::F6, [shift: true]; Action::Esc("\x1b[17;2~".into()); + Key::F7, [shift: true]; Action::Esc("\x1b[18;2~".into()); + Key::F8, [shift: true]; Action::Esc("\x1b[19;2~".into()); + Key::F9, [shift: true]; Action::Esc("\x1b[20;2~".into()); + Key::F10, [shift: true]; Action::Esc("\x1b[21;2~".into()); + Key::F11, [shift: true]; Action::Esc("\x1b[23;2~".into()); + Key::F12, [shift: true]; Action::Esc("\x1b[24;2~".into()); + Key::F1, [ctrl: true]; Action::Esc("\x1b[1;5P".into()); + Key::F2, [ctrl: true]; Action::Esc("\x1b[1;5Q".into()); + Key::F3, [ctrl: true]; Action::Esc("\x1b[1;5R".into()); + Key::F4, [ctrl: true]; Action::Esc("\x1b[1;5S".into()); + Key::F5, [ctrl: true]; Action::Esc("\x1b[15;5~".into()); + Key::F6, [ctrl: true]; Action::Esc("\x1b[17;5~".into()); + Key::F7, [ctrl: true]; Action::Esc("\x1b[18;5~".into()); + Key::F8, [ctrl: true]; Action::Esc("\x1b[19;5~".into()); + Key::F9, [ctrl: true]; Action::Esc("\x1b[20;5~".into()); + Key::F10, [ctrl: true]; Action::Esc("\x1b[21;5~".into()); + Key::F11, [ctrl: true]; Action::Esc("\x1b[23;5~".into()); + Key::F12, [ctrl: true]; Action::Esc("\x1b[24;5~".into()); + Key::F1, [alt: true]; Action::Esc("\x1b[1;6P".into()); + Key::F2, [alt: true]; Action::Esc("\x1b[1;6Q".into()); + Key::F3, [alt: true]; Action::Esc("\x1b[1;6R".into()); + Key::F4, [alt: true]; Action::Esc("\x1b[1;6S".into()); + Key::F5, [alt: true]; Action::Esc("\x1b[15;6~".into()); + Key::F6, [alt: true]; Action::Esc("\x1b[17;6~".into()); + Key::F7, [alt: true]; Action::Esc("\x1b[18;6~".into()); + Key::F8, [alt: true]; Action::Esc("\x1b[19;6~".into()); + Key::F9, [alt: true]; Action::Esc("\x1b[20;6~".into()); + Key::F10, [alt: true]; Action::Esc("\x1b[21;6~".into()); + Key::F11, [alt: true]; Action::Esc("\x1b[23;6~".into()); + Key::F12, [alt: true]; Action::Esc("\x1b[24;6~".into()); + Key::F1, [logo: true]; Action::Esc("\x1b[1;3P".into()); + Key::F2, [logo: true]; Action::Esc("\x1b[1;3Q".into()); + Key::F3, [logo: true]; Action::Esc("\x1b[1;3R".into()); + Key::F4, [logo: true]; Action::Esc("\x1b[1;3S".into()); + Key::F5, [logo: true]; Action::Esc("\x1b[15;3~".into()); + Key::F6, [logo: true]; Action::Esc("\x1b[17;3~".into()); + Key::F7, [logo: true]; Action::Esc("\x1b[18;3~".into()); + Key::F8, [logo: true]; Action::Esc("\x1b[19;3~".into()); + Key::F9, [logo: true]; Action::Esc("\x1b[20;3~".into()); + Key::F10, [logo: true]; Action::Esc("\x1b[21;3~".into()); + Key::F11, [logo: true]; Action::Esc("\x1b[23;3~".into()); + Key::F12, [logo: true]; Action::Esc("\x1b[24;3~".into()); + Key::NumpadEnter; Action::Esc("\n".into()); + ); + + bindings.extend(platform_key_bindings()); + + bindings +} + +#[cfg(not(any(target_os = "macos", test)))] +pub fn platform_key_bindings() -> Vec { + bindings!( + KeyBinding; + Key::V, [ctrl: true, shift: true]; Action::Paste; + Key::C, [ctrl: true, shift: true]; Action::Copy; + Key::Insert, [shift: true]; Action::PasteSelection; + Key::Key0, [ctrl: true]; Action::ResetFontSize; + Key::Equals, [ctrl: true]; Action::IncreaseFontSize; + Key::Add, [ctrl: true]; Action::IncreaseFontSize; + Key::Subtract, [ctrl: true]; Action::DecreaseFontSize; + Key::Minus, [ctrl: true]; Action::DecreaseFontSize; + ) +} + +#[cfg(all(target_os = "macos", not(test)))] +pub fn platform_key_bindings() -> Vec { + bindings!( + KeyBinding; + Key::Key0, [logo: true]; Action::ResetFontSize; + Key::Equals, [logo: true]; Action::IncreaseFontSize; + Key::Add, [logo: true]; Action::IncreaseFontSize; + Key::Minus, [logo: true]; Action::DecreaseFontSize; + Key::K, [logo: true]; Action::ClearHistory; + Key::K, [logo: true]; Action::Esc("\x0c".into()); + Key::V, [logo: true]; Action::Paste; + Key::C, [logo: true]; Action::Copy; + Key::H, [logo: true]; Action::Hide; + Key::Q, [logo: true]; Action::Quit; + Key::W, [logo: true]; Action::Quit; + ) +} + +// Don't return any bindings for tests since they are commented-out by default +#[cfg(test)] +pub fn platform_key_bindings() -> Vec { + vec![] +} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000000..21089d884b --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,2883 @@ +//! Configuration definitions and file loading +//! +//! Alacritty reads from a config file at startup to determine various runtime +//! parameters including font family and style, font size, etc. In the future, +//! the config file will also hold user and platform specific keybindings. +use std::borrow::Cow; +use std::collections::HashMap; +use std::fs::File; +use std::io::{self, Read, Write}; +use std::path::{Path, PathBuf}; +use std::str::FromStr; +use std::sync::mpsc; +use std::time::Duration; +use std::{env, fmt}; + +use font::Size; +use glutin::ModifiersState; +use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; +use serde::de::Error as SerdeError; +use serde::de::{MapAccess, Unexpected, Visitor}; +use serde::{self, de, Deserialize}; +use serde_yaml; + +use crate::ansi::{Color, CursorStyle, NamedColor}; +use crate::cli::Options; +use crate::index::{Column, Line}; +use crate::input::{Action, Binding, KeyBinding, MouseBinding}; +use crate::term::color::Rgb; + +mod bindings; + +pub const SOURCE_FILE_PATH: &str = file!(); +const MAX_SCROLLBACK_LINES: u32 = 100_000; +static DEFAULT_ALACRITTY_CONFIG: &'static str = + include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty.yml")); + +#[serde(default)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +pub struct Selection { + #[serde(deserialize_with = "deserialize_escape_chars")] + pub semantic_escape_chars: String, + #[serde(deserialize_with = "failure_default")] + pub save_to_clipboard: bool, +} + +impl Default for Selection { + fn default() -> Selection { + Selection { + semantic_escape_chars: default_escape_chars(), + save_to_clipboard: Default::default(), + } + } +} + +fn deserialize_escape_chars<'a, D>(deserializer: D) -> ::std::result::Result +where + D: de::Deserializer<'a>, +{ + match String::deserialize(deserializer) { + Ok(escape_chars) => Ok(escape_chars), + Err(err) => { + error!("Problem with config: {}; using default value", err); + Ok(default_escape_chars()) + } + } +} + +fn default_escape_chars() -> String { + String::from(",│`|:\"' ()[]{}<>") +} + +#[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 +where + D: de::Deserializer<'a>, +{ + match u64::deserialize(deserializer) { + Ok(threshold_ms) => Ok(Duration::from_millis(threshold_ms)), + Err(err) => { + error!("Problem with config: {}; using default value", err); + Ok(default_threshold_ms()) + } + } +} + +#[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, + + // TODO: DEPRECATED + pub faux_scrollback_lines: Option, +} + +#[serde(default)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] +pub struct Url { + // Program for opening links + #[serde(deserialize_with = "deserialize_launcher")] + pub launcher: Option, + + // Modifier used to open links + #[serde(deserialize_with = "deserialize_modifiers")] + pub modifiers: ModifiersState, +} + +fn deserialize_launcher<'a, D>( + deserializer: D, +) -> ::std::result::Result, D::Error> +where + D: de::Deserializer<'a>, +{ + let default = Url::default().launcher; + + // Deserialize to generic value + let val = match serde_yaml::Value::deserialize(deserializer) { + Ok(val) => val, + Err(err) => { + error!( + "Problem with config: {}; using {}", + err, + default.clone().unwrap().program() + ); + return Ok(default); + } + }; + + // Accept `None` to disable the launcher + if val + .as_str() + .filter(|v| v.to_lowercase() == "none") + .is_some() + { + return Ok(None); + } + + match >::deserialize(val) { + Ok(launcher) => Ok(launcher), + Err(err) => { + error!( + "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(), + } + } +} + +fn deserialize_modifiers<'a, D>(deserializer: D) -> ::std::result::Result +where + D: de::Deserializer<'a>, +{ + ModsWrapper::deserialize(deserializer).map(ModsWrapper::into_inner) +} + +/// `VisualBellAnimations` are modeled after a subset of CSS transitions and Robert +/// Penner's Easing Functions. +#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)] +pub enum VisualBellAnimation { + Ease, // CSS + EaseOut, // CSS + EaseOutSine, // Penner + EaseOutQuad, // Penner + EaseOutCubic, // Penner + EaseOutQuart, // Penner + EaseOutQuint, // Penner + EaseOutExpo, // Penner + EaseOutCirc, // Penner + Linear, +} + +impl Default for VisualBellAnimation { + fn default() -> Self { + VisualBellAnimation::EaseOutExpo + } +} + +#[serde(default)] +#[derive(Debug, Deserialize, PartialEq, Eq)] +pub struct VisualBellConfig { + /// Visual bell animation function + #[serde(deserialize_with = "failure_default")] + animation: VisualBellAnimation, + + /// Visual bell duration in milliseconds + #[serde(deserialize_with = "failure_default")] + duration: u16, + + /// Visual bell flash color + #[serde(deserialize_with = "rgb_from_hex")] + color: Rgb, +} + +impl Default for VisualBellConfig { + fn default() -> VisualBellConfig { + VisualBellConfig { + animation: Default::default(), + duration: Default::default(), + color: default_visual_bell_color(), + } + } +} + +fn default_visual_bell_color() -> Rgb { + Rgb { + r: 255, + g: 255, + b: 255, + } +} + +impl VisualBellConfig { + /// Visual bell animation + #[inline] + pub fn animation(&self) -> VisualBellAnimation { + self.animation + } + + /// Visual bell duration in milliseconds + #[inline] + pub fn duration(&self) -> Duration { + Duration::from_millis(u64::from(self.duration)) + } + + /// Visual bell flash color + #[inline] + pub fn color(&self) -> Rgb { + self.color + } +} + +#[derive(Debug, Deserialize, PartialEq, Eq)] +pub struct Shell<'a> { + program: Cow<'a, str>, + + #[serde(default, deserialize_with = "failure_default")] + args: Vec, +} + +impl<'a> Shell<'a> { + pub fn new(program: S) -> Shell<'a> + where + S: Into>, + { + Shell { + program: program.into(), + args: Vec::new(), + } + } + + pub fn new_with_args(program: S, args: Vec) -> Shell<'a> + where + S: Into>, + { + Shell { + program: program.into(), + args, + } + } + + pub fn program(&self) -> &str { + &*self.program + } + + pub fn args(&self) -> &[String] { + self.args.as_slice() + } +} + +/// Wrapper around f32 that represents an alpha value between 0.0 and 1.0 +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Alpha(f32); + +impl Alpha { + pub fn new(value: f32) -> Self { + Alpha(Self::clamp_to_valid_range(value)) + } + + pub fn set(&mut self, value: f32) { + self.0 = Self::clamp_to_valid_range(value); + } + + #[inline] + pub fn get(self) -> f32 { + self.0 + } + + fn clamp_to_valid_range(value: f32) -> f32 { + if value < 0.0 { + 0.0 + } else if value > 1.0 { + 1.0 + } else { + value + } + } +} + +impl Default for Alpha { + fn default() -> Self { + Alpha(1.0) + } +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Decorations { + Full, + Transparent, + Buttonless, + None, +} + +impl Default for Decorations { + fn default() -> Decorations { + Decorations::Full + } +} + +impl<'de> Deserialize<'de> for Decorations { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: de::Deserializer<'de>, + { + struct DecorationsVisitor; + + impl<'de> Visitor<'de> for DecorationsVisitor { + type Value = Decorations; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Some subset of full|transparent|buttonless|none") + } + + #[cfg(target_os = "macos")] + fn visit_str(self, value: &str) -> ::std::result::Result + where + E: de::Error, + { + match value.to_lowercase().as_str() { + "transparent" => Ok(Decorations::Transparent), + "buttonless" => Ok(Decorations::Buttonless), + "none" => Ok(Decorations::None), + "full" => Ok(Decorations::Full), + "true" => { + error!( + "Deprecated decorations boolean value, \ + use one of transparent|buttonless|none|full instead; \ + falling back to \"full\"" + ); + Ok(Decorations::Full) + } + "false" => { + error!( + "Deprecated decorations boolean value, \ + use one of transparent|buttonless|none|full instead; \ + falling back to \"none\"" + ); + Ok(Decorations::None) + } + _ => { + error!("Invalid decorations value: {}; using default value", value); + Ok(Decorations::Full) + } + } + } + + #[cfg(not(target_os = "macos"))] + fn visit_str(self, value: &str) -> ::std::result::Result + where + E: de::Error, + { + match value.to_lowercase().as_str() { + "none" => Ok(Decorations::None), + "full" => Ok(Decorations::Full), + "true" => { + error!( + "Deprecated decorations boolean value, \ + use one of none|full instead; \ + falling back to \"full\"" + ); + Ok(Decorations::Full) + } + "false" => { + error!( + "Deprecated decorations boolean value, \ + use one of none|full instead; \ + falling back to \"none\"" + ); + Ok(Decorations::None) + } + "transparent" | "buttonless" => { + error!( + "macOS-only decorations value: {}; using default value", + value + ); + Ok(Decorations::Full) + } + _ => { + error!("Invalid decorations value: {}; using default value", value); + Ok(Decorations::Full) + } + } + } + } + + deserializer.deserialize_str(DecorationsVisitor) + } +} + +#[serde(default)] +#[derive(Debug, Copy, Clone, Deserialize, PartialEq, Eq)] +pub struct WindowConfig { + /// Initial dimensions + #[serde(default, deserialize_with = "failure_default")] + dimensions: Dimensions, + + /// Initial position + #[serde(default, deserialize_with = "failure_default")] + position: Option>, + + /// Pixel padding + #[serde(deserialize_with = "deserialize_padding")] + padding: Delta, + + /// Draw the window with title bar / borders + #[serde(deserialize_with = "failure_default")] + decorations: Decorations, + + /// Spread out additional padding evenly + #[serde(deserialize_with = "failure_default")] + dynamic_padding: bool, + + /// Start maximized + #[serde(deserialize_with = "failure_default")] + start_maximized: bool, +} + +impl Default for WindowConfig { + fn default() -> Self { + WindowConfig { + dimensions: Default::default(), + position: Default::default(), + padding: default_padding(), + decorations: Default::default(), + dynamic_padding: Default::default(), + start_maximized: Default::default(), + } + } +} + +fn default_padding() -> Delta { + Delta { x: 2, y: 2 } +} + +fn deserialize_padding<'a, D>(deserializer: D) -> ::std::result::Result, D::Error> +where + D: de::Deserializer<'a>, +{ + match Delta::deserialize(deserializer) { + Ok(delta) => Ok(delta), + Err(err) => { + error!("Problem with config: {}; using default value", err); + Ok(default_padding()) + } + } +} + +impl WindowConfig { + pub fn decorations(&self) -> Decorations { + self.decorations + } + + pub fn dynamic_padding(&self) -> bool { + self.dynamic_padding + } + + pub fn start_maximized(&self) -> bool { + self.start_maximized + } +} + +/// Top-level config type +#[derive(Debug, PartialEq, Deserialize)] +pub struct Config { + /// Pixel padding + #[serde(default, deserialize_with = "failure_default")] + padding: Option>, + + /// TERM env variable + #[serde(default, deserialize_with = "failure_default")] + env: HashMap, + + /// Font configuration + #[serde(default, deserialize_with = "failure_default")] + font: Font, + + /// Should show render timer + #[serde(default, deserialize_with = "failure_default")] + render_timer: bool, + + /// Should draw bold text with brighter colors instead of bold font + #[serde( + default = "default_true_bool", + deserialize_with = "deserialize_true_bool" + )] + draw_bold_text_with_bright_colors: bool, + + #[serde(default, deserialize_with = "failure_default")] + colors: Colors, + + /// Background opacity from 0.0 to 1.0 + #[serde(default, deserialize_with = "failure_default")] + background_opacity: Alpha, + + /// Window configuration + #[serde(default, deserialize_with = "failure_default")] + window: WindowConfig, + + /// Keybindings + #[serde( + default = "default_key_bindings", + deserialize_with = "deserialize_key_bindings" + )] + key_bindings: Vec, + + /// Bindings for the mouse + #[serde( + default = "default_mouse_bindings", + deserialize_with = "deserialize_mouse_bindings" + )] + mouse_bindings: Vec, + + #[serde(default, deserialize_with = "failure_default")] + selection: Selection, + + #[serde(default, deserialize_with = "failure_default")] + mouse: Mouse, + + /// Path to a shell program to run on startup + #[serde(default, deserialize_with = "failure_default")] + shell: Option>, + + /// Path where config was loaded from + #[serde(default, deserialize_with = "failure_default")] + config_path: Option, + + /// Visual bell configuration + #[serde(default, deserialize_with = "failure_default")] + visual_bell: VisualBellConfig, + + /// Use dynamic title + #[serde( + default = "default_true_bool", + deserialize_with = "deserialize_true_bool" + )] + dynamic_title: bool, + + /// Live config reload + #[serde( + default = "default_true_bool", + deserialize_with = "deserialize_true_bool" + )] + live_config_reload: bool, + + /// Number of spaces in one tab + #[serde( + default = "default_tabspaces", + deserialize_with = "deserialize_tabspaces" + )] + tabspaces: usize, + + /// How much scrolling history to keep + #[serde(default, deserialize_with = "failure_default")] + scrolling: Scrolling, + + /// Cursor configuration + #[serde(default, deserialize_with = "failure_default")] + cursor: Cursor, + + /// Keep the log file after quitting + #[serde(default, deserialize_with = "failure_default")] + persistent_logging: bool, + + /// Enable experimental conpty backend instead of using winpty. + /// Will only take effect on Windows 10 Oct 2018 and later. + #[cfg(windows)] + #[serde(default, deserialize_with = "failure_default")] + enable_experimental_conpty_backend: bool, + + /// Send escape sequences using the alt key. + #[serde( + default = "default_true_bool", + deserialize_with = "deserialize_true_bool" + )] + alt_send_esc: bool, + + // TODO: DEPRECATED + custom_cursor_colors: Option, + + // TODO: DEPRECATED + hide_cursor_when_typing: Option, + + // TODO: DEPRECATED + cursor_style: Option, + + // TODO: DEPRECATED + unfocused_hollow_cursor: Option, + + // TODO: DEPRECATED + dimensions: Option, +} + +impl Default for Config { + fn default() -> Self { + serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG).expect("default config is invalid") + } +} + +fn default_key_bindings() -> Vec { + bindings::default_key_bindings() +} + +fn default_mouse_bindings() -> Vec { + bindings::default_mouse_bindings() +} + +fn deserialize_key_bindings<'a, D>( + deserializer: D, +) -> ::std::result::Result, D::Error> +where + D: de::Deserializer<'a>, +{ + deserialize_bindings(deserializer, bindings::default_key_bindings()) +} + +fn deserialize_mouse_bindings<'a, D>( + deserializer: D, +) -> ::std::result::Result, D::Error> +where + D: de::Deserializer<'a>, +{ + deserialize_bindings(deserializer, bindings::default_mouse_bindings()) +} + +fn deserialize_bindings<'a, D, T>( + deserializer: D, + mut default: Vec>, +) -> ::std::result::Result>, D::Error> +where + D: de::Deserializer<'a>, + T: Copy + Eq + std::hash::Hash + std::fmt::Debug, + Binding: de::Deserialize<'a>, +{ + let mut bindings: Vec> = failure_default_vec(deserializer)?; + + for binding in bindings.iter() { + default.retain(|b| !b.triggers_match(binding)); + } + + bindings.extend(default); + + Ok(bindings) +} + +fn failure_default_vec<'a, D, T>(deserializer: D) -> ::std::result::Result, D::Error> +where + D: de::Deserializer<'a>, + T: Deserialize<'a>, +{ + // Deserialize as generic vector + let vec = match Vec::::deserialize(deserializer) { + Ok(vec) => vec, + Err(err) => { + error!("Problem with config: {}; using empty vector", err); + return Ok(Vec::new()); + } + }; + + // Move to lossy vector + let mut bindings: Vec = Vec::new(); + for value in vec { + match T::deserialize(value) { + Ok(binding) => bindings.push(binding), + Err(err) => { + error!("Problem with config: {}; skipping value", err); + } + } + } + + Ok(bindings) +} + +fn default_tabspaces() -> usize { + 8 +} + +fn deserialize_tabspaces<'a, D>(deserializer: D) -> ::std::result::Result +where + D: de::Deserializer<'a>, +{ + match usize::deserialize(deserializer) { + Ok(value) => Ok(value), + Err(err) => { + error!("Problem with config: {}; using 8", err); + Ok(default_tabspaces()) + } + } +} + +fn deserialize_true_bool<'a, D>(deserializer: D) -> ::std::result::Result +where + D: de::Deserializer<'a>, +{ + match bool::deserialize(deserializer) { + Ok(value) => Ok(value), + Err(err) => { + error!("Problem with config: {}; using true", err); + Ok(true) + } + } +} + +fn default_true_bool() -> bool { + true +} + +fn failure_default<'a, D, T>(deserializer: D) -> ::std::result::Result +where + D: de::Deserializer<'a>, + T: Deserialize<'a> + Default, +{ + match T::deserialize(deserializer) { + Ok(value) => Ok(value), + Err(err) => { + error!("Problem with config: {}; using default value", err); + Ok(T::default()) + } + } +} + +/// Struct for scrolling related settings +#[serde(default)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)] +pub struct Scrolling { + #[serde(deserialize_with = "deserialize_scrolling_history")] + pub history: u32, + #[serde(deserialize_with = "deserialize_scrolling_multiplier")] + pub multiplier: u8, + #[serde(deserialize_with = "deserialize_scrolling_multiplier")] + pub faux_multiplier: u8, + #[serde(deserialize_with = "failure_default")] + pub auto_scroll: bool, +} + +impl Default for Scrolling { + fn default() -> Self { + Self { + history: default_scrolling_history(), + multiplier: default_scrolling_multiplier(), + faux_multiplier: default_scrolling_multiplier(), + auto_scroll: Default::default(), + } + } +} + +fn default_scrolling_history() -> u32 { + 10_000 +} + +// Default for normal and faux scrolling +fn default_scrolling_multiplier() -> u8 { + 3 +} + +fn deserialize_scrolling_history<'a, D>(deserializer: D) -> ::std::result::Result +where + D: de::Deserializer<'a>, +{ + match u32::deserialize(deserializer) { + Ok(lines) => { + if lines > MAX_SCROLLBACK_LINES { + error!( + "Problem with config: scrollback size is {}, but expected a maximum of {}; \ + using {1} instead", + lines, MAX_SCROLLBACK_LINES, + ); + Ok(MAX_SCROLLBACK_LINES) + } else { + Ok(lines) + } + } + Err(err) => { + error!("Problem with config: {}; using default value", err); + Ok(default_scrolling_history()) + } + } +} + +fn deserialize_scrolling_multiplier<'a, D>(deserializer: D) -> ::std::result::Result +where + D: de::Deserializer<'a>, +{ + match u8::deserialize(deserializer) { + Ok(lines) => Ok(lines), + Err(err) => { + error!("Problem with config: {}; using default value", err); + Ok(default_scrolling_multiplier()) + } + } +} + +/// Newtype for implementing deserialize on glutin Mods +/// +/// Our deserialize impl wouldn't be covered by a derive(Deserialize); see the +/// impl below. +#[derive(Debug, Copy, Clone, Hash, Default, Eq, PartialEq)] +struct ModsWrapper(ModifiersState); + +impl ModsWrapper { + fn into_inner(self) -> ModifiersState { + self.0 + } +} + +impl<'a> de::Deserialize<'a> for ModsWrapper { + fn deserialize(deserializer: D) -> ::std::result::Result + 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(self, value: &str) -> ::std::result::Result + where + E: de::Error, + { + let mut res = ModifiersState::default(); + for modifier in value.split('|') { + match modifier.trim() { + "Command" | "Super" => res.logo = true, + "Shift" => res.shift = true, + "Alt" | "Option" => res.alt = true, + "Control" => res.ctrl = true, + "None" => (), + _ => error!("Unknown modifier {:?}", modifier), + } + } + + Ok(ModsWrapper(res)) + } + } + + deserializer.deserialize_str(ModsVisitor) + } +} + +struct ActionWrapper(crate::input::Action); + +impl ActionWrapper { + fn into_inner(self) -> crate::input::Action { + self.0 + } +} + +impl<'a> de::Deserialize<'a> for ActionWrapper { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: de::Deserializer<'a>, + { + struct ActionVisitor; + + impl<'a> Visitor<'a> for ActionVisitor { + type Value = ActionWrapper; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str( + "Paste, Copy, PasteSelection, IncreaseFontSize, DecreaseFontSize, \ + ResetFontSize, ScrollPageUp, ScrollPageDown, ScrollToTop, \ + ScrollToBottom, ClearHistory, Hide, ClearLogNotice, SpawnNewInstance, \ + None or Quit", + ) + } + + fn visit_str(self, value: &str) -> ::std::result::Result + where + E: de::Error, + { + Ok(ActionWrapper(match value { + "Paste" => Action::Paste, + "Copy" => Action::Copy, + "PasteSelection" => Action::PasteSelection, + "IncreaseFontSize" => Action::IncreaseFontSize, + "DecreaseFontSize" => Action::DecreaseFontSize, + "ResetFontSize" => Action::ResetFontSize, + "ScrollPageUp" => Action::ScrollPageUp, + "ScrollPageDown" => Action::ScrollPageDown, + "ScrollToTop" => Action::ScrollToTop, + "ScrollToBottom" => Action::ScrollToBottom, + "ClearHistory" => Action::ClearHistory, + "Hide" => Action::Hide, + "Quit" => Action::Quit, + "ClearLogNotice" => Action::ClearLogNotice, + "SpawnNewInstance" => Action::SpawnNewInstance, + "None" => Action::None, + _ => return Err(E::invalid_value(Unexpected::Str(value), &self)), + })) + } + } + deserializer.deserialize_str(ActionVisitor) + } +} + +#[serde(untagged)] +#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +pub enum CommandWrapper { + Just(String), + WithArgs { + program: String, + #[serde(default)] + args: Vec, + }, +} + +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, + } + } +} + +use crate::term::{mode, TermMode}; + +struct ModeWrapper { + pub mode: TermMode, + pub not_mode: TermMode, +} + +impl<'a> de::Deserialize<'a> for ModeWrapper { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: de::Deserializer<'a>, + { + struct ModeVisitor; + + impl<'a> Visitor<'a> for ModeVisitor { + type Value = ModeWrapper; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Combination of AppCursor | AppKeypad, possibly with negation (~)") + } + + fn visit_str(self, value: &str) -> ::std::result::Result + where + E: de::Error, + { + let mut res = ModeWrapper { + mode: TermMode::empty(), + not_mode: TermMode::empty(), + }; + + for modifier in value.split('|') { + match modifier.trim() { + "AppCursor" => res.mode |= mode::TermMode::APP_CURSOR, + "~AppCursor" => res.not_mode |= mode::TermMode::APP_CURSOR, + "AppKeypad" => res.mode |= mode::TermMode::APP_KEYPAD, + "~AppKeypad" => res.not_mode |= mode::TermMode::APP_KEYPAD, + "~Alt" => res.not_mode |= mode::TermMode::ALT_SCREEN, + "Alt" => res.mode |= mode::TermMode::ALT_SCREEN, + _ => error!("Unknown mode {:?}", modifier), + } + } + + Ok(res) + } + } + deserializer.deserialize_str(ModeVisitor) + } +} + +struct MouseButton(::glutin::MouseButton); + +impl MouseButton { + fn into_inner(self) -> ::glutin::MouseButton { + self.0 + } +} + +impl<'a> de::Deserialize<'a> for MouseButton { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: de::Deserializer<'a>, + { + struct MouseButtonVisitor; + + impl<'a> Visitor<'a> for MouseButtonVisitor { + type Value = MouseButton; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("Left, Right, Middle, or a number") + } + + fn visit_str(self, value: &str) -> ::std::result::Result + where + E: de::Error, + { + match value { + "Left" => Ok(MouseButton(::glutin::MouseButton::Left)), + "Right" => Ok(MouseButton(::glutin::MouseButton::Right)), + "Middle" => Ok(MouseButton(::glutin::MouseButton::Middle)), + _ => { + if let Ok(index) = u8::from_str(value) { + Ok(MouseButton(::glutin::MouseButton::Other(index))) + } else { + Err(E::invalid_value(Unexpected::Str(value), &self)) + } + } + } + } + } + + deserializer.deserialize_str(MouseButtonVisitor) + } +} + +/// Bindings are deserialized into a `RawBinding` before being parsed as a +/// `KeyBinding` or `MouseBinding`. +#[derive(PartialEq, Eq)] +struct RawBinding { + key: Option, + mouse: Option<::glutin::MouseButton>, + mods: ModifiersState, + mode: TermMode, + notmode: TermMode, + action: Action, +} + +impl RawBinding { + fn into_mouse_binding(self) -> ::std::result::Result { + if let Some(mouse) = self.mouse { + Ok(Binding { + trigger: mouse, + mods: self.mods, + action: self.action, + mode: self.mode, + notmode: self.notmode, + }) + } else { + Err(self) + } + } + + fn into_key_binding(self) -> ::std::result::Result { + 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> de::Deserialize<'a> for RawBinding { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: de::Deserializer<'a>, + { + enum Field { + Key, + Mods, + Mode, + Action, + Chars, + Mouse, + Command, + } + + impl<'a> de::Deserialize<'a> for Field { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: de::Deserializer<'a>, + { + struct FieldVisitor; + + static FIELDS: &'static [&'static str] = + &["key", "mods", "mode", "action", "chars", "mouse", "command"]; + + impl<'a> Visitor<'a> for FieldVisitor { + type Value = Field; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("binding fields") + } + + fn visit_str(self, value: &str) -> ::std::result::Result + 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(self, mut map: V) -> ::std::result::Result + where + V: MapAccess<'a>, + { + let mut mods: Option = None; + let mut key: Option = None; + let mut chars: Option = None; + let mut action: Option = None; + let mut mode: Option = None; + let mut not_mode: Option = None; + let mut mouse: Option<::glutin::MouseButton> = None; + let mut command: Option = None; + + use ::serde::de::Error; + + while let Some(struct_key) = map.next_key::()? { + match struct_key { + Field::Key => { + if key.is_some() { + return Err(::duplicate_field("key")); + } + + let val = map.next_value::()?; + if val.is_u64() { + let scancode = val.as_u64().unwrap(); + if scancode > u64::from(::std::u32::MAX) { + return Err(::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(::duplicate_field("mods")); + } + + mods = Some(map.next_value::()?.into_inner()); + } + Field::Mode => { + if mode.is_some() { + return Err(::duplicate_field("mode")); + } + + let mode_deserializer = map.next_value::()?; + mode = Some(mode_deserializer.mode); + not_mode = Some(mode_deserializer.not_mode); + } + Field::Action => { + if action.is_some() { + return Err(::duplicate_field("action")); + } + + action = Some(map.next_value::()?.into_inner()); + } + Field::Chars => { + if chars.is_some() { + return Err(::duplicate_field("chars")); + } + + chars = Some(map.next_value()?); + } + Field::Mouse => { + if chars.is_some() { + return Err(::duplicate_field("mouse")); + } + + mouse = Some(map.next_value::()?.into_inner()); + } + Field::Command => { + if command.is_some() { + return Err(::duplicate_field("command")); + } + + command = Some(map.next_value::()?); + } + } + } + + 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> de::Deserialize<'a> for Alpha { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: de::Deserializer<'a>, + { + let value = f32::deserialize(deserializer)?; + Ok(Alpha::new(value)) + } +} + +impl<'a> de::Deserialize<'a> for MouseBinding { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: de::Deserializer<'a>, + { + let raw = RawBinding::deserialize(deserializer)?; + raw.into_mouse_binding() + .map_err(|_| D::Error::custom("expected mouse binding")) + } +} + +impl<'a> de::Deserialize<'a> for KeyBinding { + fn deserialize(deserializer: D) -> ::std::result::Result + where + D: de::Deserializer<'a>, + { + let raw = RawBinding::deserialize(deserializer)?; + raw.into_key_binding() + .map_err(|_| D::Error::custom("expected key binding")) + } +} + +/// Errors occurring during config loading +#[derive(Debug)] +pub enum Error { + /// Config file not found + NotFound, + + /// Config file empty + Empty, + + /// Couldn't read $HOME environment variable + ReadingEnvHome(env::VarError), + + /// io error reading file + Io(io::Error), + + /// Not valid yaml or missing parameters + Yaml(serde_yaml::Error), +} + +#[serde(default)] +#[derive(Debug, Deserialize, PartialEq, Eq)] +pub struct Colors { + #[serde(deserialize_with = "failure_default")] + pub primary: PrimaryColors, + #[serde(deserialize_with = "failure_default")] + pub cursor: CursorColors, + #[serde(deserialize_with = "failure_default")] + pub selection: SelectionColors, + #[serde(deserialize_with = "deserialize_normal_colors")] + pub normal: AnsiColors, + #[serde(deserialize_with = "deserialize_bright_colors")] + pub bright: AnsiColors, + #[serde(deserialize_with = "failure_default")] + pub dim: Option, + #[serde(deserialize_with = "failure_default_vec")] + pub indexed_colors: Vec, +} + +impl Default for Colors { + fn default() -> Colors { + Colors { + primary: Default::default(), + cursor: Default::default(), + selection: Default::default(), + normal: default_normal_colors(), + bright: default_bright_colors(), + dim: Default::default(), + indexed_colors: Default::default(), + } + } +} + +fn default_normal_colors() -> AnsiColors { + AnsiColors { + black: Rgb { + r: 0x00, + g: 0x00, + b: 0x00, + }, + red: Rgb { + r: 0xd5, + g: 0x4e, + b: 0x53, + }, + green: Rgb { + r: 0xb9, + g: 0xca, + b: 0x4a, + }, + yellow: Rgb { + r: 0xe6, + g: 0xc5, + b: 0x47, + }, + blue: Rgb { + r: 0x7a, + g: 0xa6, + b: 0xda, + }, + magenta: Rgb { + r: 0xc3, + g: 0x97, + b: 0xd8, + }, + cyan: Rgb { + r: 0x70, + g: 0xc0, + b: 0xba, + }, + white: Rgb { + r: 0xea, + g: 0xea, + b: 0xea, + }, + } +} + +fn default_bright_colors() -> AnsiColors { + AnsiColors { + black: Rgb { + r: 0x66, + g: 0x66, + b: 0x66, + }, + red: Rgb { + r: 0xff, + g: 0x33, + b: 0x34, + }, + green: Rgb { + r: 0x9e, + g: 0xc4, + b: 0x00, + }, + yellow: Rgb { + r: 0xe7, + g: 0xc5, + b: 0x47, + }, + blue: Rgb { + r: 0x7a, + g: 0xa6, + b: 0xda, + }, + magenta: Rgb { + r: 0xb7, + g: 0x7e, + b: 0xe0, + }, + cyan: Rgb { + r: 0x54, + g: 0xce, + b: 0xd6, + }, + white: Rgb { + r: 0xff, + g: 0xff, + b: 0xff, + }, + } +} + +fn deserialize_normal_colors<'a, D>(deserializer: D) -> ::std::result::Result +where + D: de::Deserializer<'a>, +{ + match AnsiColors::deserialize(deserializer) { + Ok(escape_chars) => Ok(escape_chars), + Err(err) => { + error!("Problem with config: {}; using default value", err); + Ok(default_normal_colors()) + } + } +} + +fn deserialize_bright_colors<'a, D>(deserializer: D) -> ::std::result::Result +where + D: de::Deserializer<'a>, +{ + match AnsiColors::deserialize(deserializer) { + Ok(escape_chars) => Ok(escape_chars), + Err(err) => { + error!("Problem with config: {}; using default value", err); + Ok(default_bright_colors()) + } + } +} + +#[derive(Debug, Deserialize, PartialEq, Eq)] +pub struct IndexedColor { + #[serde(deserialize_with = "deserialize_color_index")] + pub index: u8, + #[serde(deserialize_with = "rgb_from_hex")] + pub color: Rgb, +} + +fn deserialize_color_index<'a, D>(deserializer: D) -> ::std::result::Result +where + D: de::Deserializer<'a>, +{ + match u8::deserialize(deserializer) { + Ok(index) => { + if index < 16 { + error!( + "Problem with config: indexed_color's index is {}, \ + but a value bigger than 15 was expected; \ + ignoring setting", + index + ); + + // Return value out of range to ignore this color + Ok(0) + } else { + Ok(index) + } + } + Err(err) => { + error!("Problem with config: {}; ignoring setting", err); + + // Return value out of range to ignore this color + Ok(0) + } + } +} + +#[serde(default)] +#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq)] +pub struct Cursor { + #[serde(deserialize_with = "failure_default")] + pub style: CursorStyle, + #[serde(deserialize_with = "deserialize_true_bool")] + pub unfocused_hollow: bool, +} + +impl Default for Cursor { + fn default() -> Self { + Self { + style: Default::default(), + unfocused_hollow: true, + } + } +} + +#[serde(default)] +#[derive(Debug, Copy, Clone, Default, Deserialize, PartialEq, Eq)] +pub struct CursorColors { + #[serde(deserialize_with = "deserialize_optional_color")] + pub text: Option, + #[serde(deserialize_with = "deserialize_optional_color")] + pub cursor: Option, +} + +#[serde(default)] +#[derive(Debug, Copy, Clone, Default, Deserialize, PartialEq, Eq)] +pub struct SelectionColors { + #[serde(deserialize_with = "deserialize_optional_color")] + pub text: Option, + #[serde(deserialize_with = "deserialize_optional_color")] + pub background: Option, +} + +#[serde(default)] +#[derive(Debug, Deserialize, PartialEq, Eq)] +pub struct PrimaryColors { + #[serde(deserialize_with = "rgb_from_hex")] + pub background: Rgb, + #[serde(deserialize_with = "rgb_from_hex")] + pub foreground: Rgb, + #[serde(deserialize_with = "deserialize_optional_color")] + pub bright_foreground: Option, + #[serde(deserialize_with = "deserialize_optional_color")] + pub dim_foreground: Option, +} + +impl Default for PrimaryColors { + fn default() -> Self { + PrimaryColors { + background: default_background(), + foreground: default_foreground(), + bright_foreground: Default::default(), + dim_foreground: Default::default(), + } + } +} + +fn deserialize_optional_color<'a, D>( + deserializer: D, +) -> ::std::result::Result, D::Error> +where + D: de::Deserializer<'a>, +{ + match Option::deserialize(deserializer) { + Ok(Some(color)) => { + let color: serde_yaml::Value = color; + Ok(Some(rgb_from_hex(color).unwrap())) + } + Ok(None) => Ok(None), + Err(err) => { + error!( + "Problem with config: {}; using standard foreground color", + err + ); + Ok(None) + } + } +} + +fn default_background() -> Rgb { + Rgb { r: 0, g: 0, b: 0 } +} + +fn default_foreground() -> Rgb { + Rgb { + r: 0xea, + g: 0xea, + b: 0xea, + } +} + +/// The 8-colors sections of config +#[derive(Debug, Deserialize, PartialEq, Eq)] +pub struct AnsiColors { + #[serde(deserialize_with = "rgb_from_hex")] + pub black: Rgb, + #[serde(deserialize_with = "rgb_from_hex")] + pub red: Rgb, + #[serde(deserialize_with = "rgb_from_hex")] + pub green: Rgb, + #[serde(deserialize_with = "rgb_from_hex")] + pub yellow: Rgb, + #[serde(deserialize_with = "rgb_from_hex")] + pub blue: Rgb, + #[serde(deserialize_with = "rgb_from_hex")] + pub magenta: Rgb, + #[serde(deserialize_with = "rgb_from_hex")] + pub cyan: Rgb, + #[serde(deserialize_with = "rgb_from_hex")] + pub white: Rgb, +} + +/// Deserialize an Rgb from a hex string +/// +/// This is *not* the deserialize impl for Rgb since we want a symmetric +/// serialize/deserialize impl for ref tests. +fn rgb_from_hex<'a, D>(deserializer: D) -> ::std::result::Result +where + D: de::Deserializer<'a>, +{ + struct RgbVisitor; + + impl<'a> Visitor<'a> for RgbVisitor { + type Value = Rgb; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("hex color like 0xff00ff") + } + + fn visit_str(self, value: &str) -> ::std::result::Result + where + E: ::serde::de::Error, + { + Rgb::from_str(&value[..]) + .map_err(|_| E::custom("failed to parse rgb; expected hex color like 0xff00ff")) + } + } + + let rgb = deserializer.deserialize_str(RgbVisitor); + + // Use #ff00ff as fallback color + match rgb { + Ok(rgb) => Ok(rgb), + Err(err) => { + error!("Problem with config: {}; using color #ff00ff", err); + Ok(Rgb { + r: 255, + g: 0, + b: 255, + }) + } + } +} + +impl FromStr for Rgb { + type Err = (); + fn from_str(s: &str) -> ::std::result::Result { + let mut chars = s.chars(); + let mut rgb = Rgb::default(); + + macro_rules! component { + ($($c:ident),*) => { + $( + match chars.next().and_then(|c| c.to_digit(16)) { + Some(val) => rgb.$c = (val as u8) << 4, + None => return Err(()) + } + + match chars.next().and_then(|c| c.to_digit(16)) { + Some(val) => rgb.$c |= val as u8, + None => return Err(()) + } + )* + } + } + + match chars.next() { + Some('0') => { + if chars.next() != Some('x') { + return Err(()); + } + } + Some('#') => (), + _ => return Err(()), + } + + component!(r, g, b); + + Ok(rgb) + } +} + +impl ::std::error::Error for Error { + fn cause(&self) -> Option<&dyn (::std::error::Error)> { + match *self { + Error::NotFound | Error::Empty => None, + Error::ReadingEnvHome(ref err) => Some(err), + Error::Io(ref err) => Some(err), + Error::Yaml(ref err) => Some(err), + } + } + + fn description(&self) -> &str { + match *self { + Error::NotFound => "Couldn't locate config file", + Error::Empty => "Empty config file", + Error::ReadingEnvHome(ref err) => err.description(), + Error::Io(ref err) => err.description(), + Error::Yaml(ref err) => err.description(), + } + } +} + +impl ::std::fmt::Display for Error { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + match *self { + Error::NotFound | Error::Empty => { + write!(f, "{}", ::std::error::Error::description(self)) + } + Error::ReadingEnvHome(ref err) => { + write!(f, "Couldn't read $HOME environment variable: {}", err) + } + Error::Io(ref err) => write!(f, "Error reading config file: {}", err), + Error::Yaml(ref err) => write!(f, "Problem with config: {}", err), + } + } +} + +impl From for Error { + fn from(val: env::VarError) -> Error { + Error::ReadingEnvHome(val) + } +} + +impl From for Error { + fn from(val: io::Error) -> Error { + if val.kind() == io::ErrorKind::NotFound { + Error::NotFound + } else { + Error::Io(val) + } + } +} + +impl From for Error { + fn from(val: serde_yaml::Error) -> Error { + Error::Yaml(val) + } +} + +/// Result from config loading +pub type Result = ::std::result::Result; + +impl Config { + /// Get the location of the first found default config file paths + /// according to the following order: + /// + /// 1. $XDG_CONFIG_HOME/alacritty/alacritty.yml + /// 2. $XDG_CONFIG_HOME/alacritty.yml + /// 3. $HOME/.config/alacritty/alacritty.yml + /// 4. $HOME/.alacritty.yml + #[cfg(not(windows))] + pub fn installed_config<'a>() -> Option> { + // Try using XDG location by default + ::xdg::BaseDirectories::with_prefix("alacritty") + .ok() + .and_then(|xdg| xdg.find_config_file("alacritty.yml")) + .or_else(|| { + ::xdg::BaseDirectories::new() + .ok() + .and_then(|fallback| fallback.find_config_file("alacritty.yml")) + }) + .or_else(|| { + if let Ok(home) = env::var("HOME") { + // Fallback path: $HOME/.config/alacritty/alacritty.yml + let fallback = PathBuf::from(&home).join(".config/alacritty/alacritty.yml"); + if fallback.exists() { + return Some(fallback); + } + // Fallback path: $HOME/.alacritty.yml + let fallback = PathBuf::from(&home).join(".alacritty.yml"); + if fallback.exists() { + return Some(fallback); + } + } + None + }) + .map(Into::into) + } + + // TODO: Remove old configuration location warning (Deprecated 03/12/2018) + #[cfg(windows)] + pub fn installed_config<'a>() -> Option> { + let old = dirs::home_dir().map(|path| path.join("alacritty.yml")); + let new = dirs::config_dir().map(|path| path.join("alacritty\\alacritty.yml")); + + if let Some(old_path) = old.as_ref().filter(|old| old.exists()) { + warn!( + "Found configuration at: {}; this file should be moved to the new location: {}", + old_path.to_string_lossy(), + new.as_ref().map(|new| new.to_string_lossy()).unwrap(), + ); + + old.map(Cow::from) + } else { + new.filter(|new| new.exists()).map(Cow::from) + } + } + + #[cfg(not(windows))] + pub fn write_defaults() -> io::Result> { + let path = xdg::BaseDirectories::with_prefix("alacritty") + .map_err(|err| io::Error::new(io::ErrorKind::NotFound, err.to_string().as_str())) + .and_then(|p| p.place_config_file("alacritty.yml"))?; + + File::create(&path)?.write_all(DEFAULT_ALACRITTY_CONFIG.as_bytes())?; + + Ok(path.into()) + } + + #[cfg(windows)] + pub fn write_defaults() -> io::Result> { + let mut path = dirs::config_dir().ok_or_else(|| { + io::Error::new(io::ErrorKind::NotFound, "Couldn't find profile directory") + })?; + + path = path.join("alacritty/alacritty.yml"); + + std::fs::create_dir_all(path.parent().unwrap())?; + + File::create(&path)?.write_all(DEFAULT_ALACRITTY_CONFIG.as_bytes())?; + + Ok(path.into()) + } + + /// Get list of colors + /// + /// The ordering returned here is expected by the terminal. Colors are simply indexed in this + /// array for performance. + pub fn colors(&self) -> &Colors { + &self.colors + } + + #[inline] + pub fn background_opacity(&self) -> Alpha { + self.background_opacity + } + + pub fn key_bindings(&self) -> &[KeyBinding] { + &self.key_bindings[..] + } + + pub fn mouse_bindings(&self) -> &[MouseBinding] { + &self.mouse_bindings[..] + } + + pub fn mouse(&self) -> &Mouse { + &self.mouse + } + + pub fn selection(&self) -> &Selection { + &self.selection + } + + pub fn tabspaces(&self) -> usize { + self.tabspaces + } + + pub fn padding(&self) -> &Delta { + self.padding.as_ref().unwrap_or(&self.window.padding) + } + + #[inline] + pub fn draw_bold_text_with_bright_colors(&self) -> bool { + self.draw_bold_text_with_bright_colors + } + + /// Get font config + #[inline] + pub fn font(&self) -> &Font { + &self.font + } + + /// Get window dimensions + #[inline] + pub fn dimensions(&self) -> Dimensions { + self.dimensions.unwrap_or(self.window.dimensions) + } + + #[inline] + pub fn position(&self) -> Option> { + self.window.position + } + + /// Get window config + #[inline] + pub fn window(&self) -> &WindowConfig { + &self.window + } + + /// Get visual bell config + #[inline] + pub fn visual_bell(&self) -> &VisualBellConfig { + &self.visual_bell + } + + /// Should show render timer + #[inline] + pub fn render_timer(&self) -> bool { + self.render_timer + } + + #[cfg(target_os = "macos")] + #[inline] + pub fn use_thin_strokes(&self) -> bool { + self.font.use_thin_strokes + } + + #[cfg(not(target_os = "macos"))] + #[inline] + pub fn use_thin_strokes(&self) -> bool { + false + } + + pub fn path(&self) -> Option<&Path> { + self.config_path.as_ref().map(PathBuf::as_path) + } + + pub fn shell(&self) -> Option<&Shell<'_>> { + self.shell.as_ref() + } + + pub fn env(&self) -> &HashMap { + &self.env + } + + /// Should hide mouse cursor when typing + #[inline] + pub fn hide_mouse_when_typing(&self) -> bool { + self.hide_cursor_when_typing + .unwrap_or(self.mouse.hide_when_typing) + } + + /// Style of the cursor + #[inline] + pub fn cursor_style(&self) -> CursorStyle { + self.cursor_style.unwrap_or(self.cursor.style) + } + + /// Use hollow block cursor when unfocused + #[inline] + pub fn unfocused_hollow_cursor(&self) -> bool { + self.unfocused_hollow_cursor + .unwrap_or(self.cursor.unfocused_hollow) + } + + /// Live config reload + #[inline] + pub fn live_config_reload(&self) -> bool { + self.live_config_reload + } + + #[inline] + pub fn dynamic_title(&self) -> bool { + self.dynamic_title + } + + /// Scrolling settings + #[inline] + pub fn scrolling(&self) -> Scrolling { + self.scrolling + } + + /// Cursor foreground color + #[inline] + pub fn cursor_text_color(&self) -> Option { + self.colors + .cursor + .text + .map(|_| Color::Named(NamedColor::CursorText)) + } + + /// Cursor background color + #[inline] + pub fn cursor_cursor_color(&self) -> Option { + self.colors + .cursor + .cursor + .map(|_| Color::Named(NamedColor::Cursor)) + } + + /// Enable experimental conpty backend (Windows only) + #[cfg(windows)] + #[inline] + pub fn enable_experimental_conpty_backend(&self) -> bool { + self.enable_experimental_conpty_backend + } + + /// Send escape sequences using the alt key + #[inline] + pub fn alt_send_esc(&self) -> bool { + self.alt_send_esc + } + + // Update the history size, used in ref tests + pub fn set_history(&mut self, history: u32) { + self.scrolling.history = history; + } + + /// Keep the log file after quitting Alacritty + #[inline] + pub fn persistent_logging(&self) -> bool { + self.persistent_logging + } + + /// Overrides the `dynamic_title` configuration based on `--title`. + pub fn update_dynamic_title(mut self, options: &Options) -> Self { + if options.title.is_some() { + self.dynamic_title = false; + } + self + } + + pub fn load_from(path: PathBuf) -> Config { + let mut config = Config::reload_from(&path).unwrap_or_else(|_| Config::default()); + config.config_path = Some(path); + config + } + + pub fn reload_from(path: &PathBuf) -> Result { + match Config::read_config(path) { + Ok(config) => Ok(config), + Err(err) => { + error!("Unable to load config {:?}: {}", path, err); + Err(err) + } + } + } + + fn read_config(path: &PathBuf) -> Result { + let mut contents = String::new(); + File::open(path)?.read_to_string(&mut contents)?; + + // Prevent parsing error with empty string + if contents.is_empty() { + return Ok(Config::default()); + } + + let mut config: Config = serde_yaml::from_str(&contents)?; + config.print_deprecation_warnings(); + + Ok(config) + } + + fn print_deprecation_warnings(&mut self) { + if self.dimensions.is_some() { + warn!( + "Config dimensions is deprecated; \ + please use window.dimensions instead" + ); + } + + if self.padding.is_some() { + warn!( + "Config padding is deprecated; \ + please use window.padding instead" + ); + } + + if self.mouse.faux_scrollback_lines.is_some() { + warn!( + "Config mouse.faux_scrollback_lines is deprecated; \ + please use mouse.faux_scrolling_lines instead" + ); + } + + if let Some(custom_cursor_colors) = self.custom_cursor_colors { + warn!("Config custom_cursor_colors is deprecated"); + + if !custom_cursor_colors { + self.colors.cursor.cursor = None; + self.colors.cursor.text = None; + } + } + + if self.cursor_style.is_some() { + warn!( + "Config cursor_style is deprecated; \ + please use cursor.style instead" + ); + } + + if self.hide_cursor_when_typing.is_some() { + warn!( + "Config hide_cursor_when_typing is deprecated; \ + please use mouse.hide_when_typing instead" + ); + } + + if self.unfocused_hollow_cursor.is_some() { + warn!( + "Config unfocused_hollow_cursor is deprecated; \ + please use cursor.unfocused_hollow instead" + ); + } + } +} + +/// Window Dimensions +/// +/// Newtype to avoid passing values incorrectly +#[serde(default)] +#[derive(Default, Debug, Copy, Clone, Deserialize, PartialEq, Eq)] +pub struct Dimensions { + /// Window width in character columns + #[serde(deserialize_with = "failure_default")] + columns: Column, + + /// Window Height in character lines + #[serde(deserialize_with = "failure_default")] + lines: Line, +} + +impl Dimensions { + pub fn new(columns: Column, lines: Line) -> Self { + Dimensions { columns, lines } + } + + /// Get lines + #[inline] + pub fn lines_u32(&self) -> u32 { + self.lines.0 as u32 + } + + /// Get columns + #[inline] + pub fn columns_u32(&self) -> u32 { + self.columns.0 as u32 + } +} + +/// A delta for a point in a 2 dimensional plane +#[serde(default, bound(deserialize = "T: Deserialize<'de> + Default"))] +#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)] +pub struct Delta { + /// Horizontal change + #[serde(deserialize_with = "failure_default")] + pub x: T, + /// Vertical change + #[serde(deserialize_with = "failure_default")] + pub y: T, +} + +trait DeserializeSize: Sized { + fn deserialize<'a, D>(_: D) -> ::std::result::Result + where + D: serde::de::Deserializer<'a>; +} + +impl DeserializeSize for Size { + fn deserialize<'a, D>(deserializer: D) -> ::std::result::Result + where + D: serde::de::Deserializer<'a>, + { + use std::marker::PhantomData; + + struct NumVisitor<__D> { + _marker: PhantomData<__D>, + } + + impl<'a, __D> Visitor<'a> for NumVisitor<__D> + where + __D: serde::de::Deserializer<'a>, + { + type Value = f64; + + fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("f64 or u64") + } + + fn visit_f64(self, value: f64) -> ::std::result::Result + where + E: ::serde::de::Error, + { + Ok(value) + } + + fn visit_u64(self, value: u64) -> ::std::result::Result + where + E: ::serde::de::Error, + { + Ok(value as f64) + } + } + + let size = deserializer + .deserialize_any(NumVisitor:: { + _marker: PhantomData, + }) + .map(|v| Size::new(v as _)); + + // Use default font size as fallback + match size { + Ok(size) => Ok(size), + Err(err) => { + let size = default_font_size(); + error!( + "Problem with config: {}; using size {}", + err, + size.as_f32_pts() + ); + Ok(size) + } + } + } +} + +/// Font config +/// +/// Defaults are provided at the level of this struct per platform, but not per +/// field in this struct. It might be nice in the future to have defaults for +/// each value independently. Alternatively, maybe erroring when the user +/// doesn't provide complete config is Ok. +#[serde(default)] +#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +pub struct Font { + /// Normal font face + #[serde(deserialize_with = "failure_default")] + normal: FontDescription, + + /// Bold font face + #[serde(deserialize_with = "failure_default")] + italic: SecondaryFontDescription, + + /// Italic font face + #[serde(deserialize_with = "failure_default")] + bold: SecondaryFontDescription, + + /// Font size in points + #[serde(deserialize_with = "DeserializeSize::deserialize")] + pub size: Size, + + /// Extra spacing per character + #[serde(deserialize_with = "failure_default")] + offset: Delta, + + /// Glyph offset within character cell + #[serde(deserialize_with = "failure_default")] + glyph_offset: Delta, + + #[cfg(target_os = "macos")] + #[serde(deserialize_with = "deserialize_true_bool")] + use_thin_strokes: bool, + + // TODO: Deprecated + #[serde(deserialize_with = "deserialize_scale_with_dpi")] + scale_with_dpi: Option<()>, +} + +impl Default for Font { + fn default() -> Font { + Font { + #[cfg(target_os = "macos")] + use_thin_strokes: true, + size: default_font_size(), + normal: Default::default(), + bold: Default::default(), + italic: Default::default(), + scale_with_dpi: Default::default(), + glyph_offset: Default::default(), + offset: Default::default(), + } + } +} + +impl Font { + /// Get the font size in points + #[inline] + pub fn size(&self) -> Size { + self.size + } + + /// Get offsets to font metrics + #[inline] + pub fn offset(&self) -> &Delta { + &self.offset + } + + /// Get cell offsets for glyphs + #[inline] + pub fn glyph_offset(&self) -> &Delta { + &self.glyph_offset + } + + /// Get a font clone with a size modification + pub fn with_size(self, size: Size) -> Font { + Font { size, ..self } + } + + // Get normal font description + pub fn normal(&self) -> &FontDescription { + &self.normal + } + + // Get italic font description + pub fn italic(&self) -> FontDescription { + self.italic.desc(&self.normal) + } + + // Get bold font description + pub fn bold(&self) -> FontDescription { + self.bold.desc(&self.normal) + } +} + +fn default_font_size() -> Size { + Size::new(11.) +} + +fn deserialize_scale_with_dpi<'a, D>(deserializer: D) -> ::std::result::Result, D::Error> +where + D: de::Deserializer<'a>, +{ + // This is necessary in order to get serde to complete deserialization of the configuration + let _ignored = bool::deserialize(deserializer); + error!( + "The scale_with_dpi setting has been removed, \ + on X11 the WINIT_HIDPI_FACTOR environment variable can be used instead." + ); + Ok(None) +} + +/// Description of the normal font +#[serde(default)] +#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] +pub struct FontDescription { + #[serde(deserialize_with = "failure_default")] + pub family: String, + #[serde(deserialize_with = "failure_default")] + pub style: Option, +} + +impl Default for FontDescription { + fn default() -> FontDescription { + FontDescription { + #[cfg(not(any(target_os = "macos", windows)))] + family: "monospace".into(), + #[cfg(target_os = "macos")] + family: "Menlo".into(), + #[cfg(windows)] + family: "Consolas".into(), + style: None, + } + } +} + +/// Description of the italic and bold font +#[serde(default)] +#[derive(Debug, Default, Deserialize, Clone, PartialEq, Eq)] +pub struct SecondaryFontDescription { + #[serde(deserialize_with = "failure_default")] + family: Option, + #[serde(deserialize_with = "failure_default")] + style: Option, +} + +impl SecondaryFontDescription { + pub fn desc(&self, fallback: &FontDescription) -> FontDescription { + FontDescription { + family: self + .family + .clone() + .unwrap_or_else(|| fallback.family.clone()), + style: self.style.clone(), + } + } +} + +pub struct Monitor { + _thread: ::std::thread::JoinHandle<()>, + rx: mpsc::Receiver, +} + +pub trait OnConfigReload { + fn on_config_reload(&mut self); +} + +impl OnConfigReload for crate::display::Notifier { + fn on_config_reload(&mut self) { + self.notify(); + } +} + +impl Monitor { + /// Get pending config changes + pub fn pending(&self) -> Option { + let mut config = None; + while let Ok(new) = self.rx.try_recv() { + config = Some(new); + } + + config + } + + pub fn new(path: P, mut handler: H) -> Monitor + where + H: OnConfigReload + Send + 'static, + P: Into, + { + let path = path.into(); + + let (config_tx, config_rx) = mpsc::channel(); + + Monitor { + _thread: crate::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; + } + + let _ = config_tx.send(path); + handler.on_config_reload(); + } + _ => {} + } + } + }), + rx: config_rx, + } + } +} + +#[derive(Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)] +pub enum Key { + Scancode(u32), + Key1, + Key2, + Key3, + Key4, + Key5, + Key6, + Key7, + Key8, + Key9, + Key0, + A, + B, + C, + D, + E, + F, + G, + H, + I, + J, + K, + L, + M, + N, + O, + P, + Q, + R, + S, + T, + U, + V, + W, + X, + Y, + Z, + Escape, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, + F15, + F16, + F17, + F18, + F19, + F20, + F21, + F22, + F23, + F24, + Snapshot, + Scroll, + Pause, + Insert, + Home, + Delete, + End, + PageDown, + PageUp, + Left, + Up, + Right, + Down, + Back, + Return, + Space, + Compose, + Numlock, + Numpad0, + Numpad1, + Numpad2, + Numpad3, + Numpad4, + Numpad5, + Numpad6, + Numpad7, + Numpad8, + Numpad9, + AbntC1, + AbntC2, + Add, + Apostrophe, + Apps, + At, + Ax, + Backslash, + Calculator, + Capital, + Colon, + Comma, + Convert, + Decimal, + Divide, + Equals, + Grave, + Kana, + Kanji, + LAlt, + LBracket, + LControl, + LShift, + LWin, + Mail, + MediaSelect, + MediaStop, + Minus, + Multiply, + Mute, + MyComputer, + NavigateForward, + NavigateBackward, + NextTrack, + NoConvert, + NumpadComma, + NumpadEnter, + NumpadEquals, + OEM102, + Period, + PlayPause, + Power, + PrevTrack, + RAlt, + RBracket, + RControl, + RShift, + RWin, + Semicolon, + Slash, + Sleep, + Stop, + Subtract, + Sysrq, + Tab, + Underline, + Unlabeled, + VolumeDown, + VolumeUp, + Wake, + WebBack, + WebFavorites, + WebForward, + WebHome, + WebRefresh, + WebSearch, + WebStop, + Yen, + Caret, + Copy, + Paste, + Cut, +} + +impl Key { + pub fn from_glutin_input(key: ::glutin::VirtualKeyCode) -> Self { + use glutin::VirtualKeyCode::*; + // Thank you, vim macros and regex! + match key { + Key1 => Key::Key1, + Key2 => Key::Key2, + Key3 => Key::Key3, + Key4 => Key::Key4, + Key5 => Key::Key5, + Key6 => Key::Key6, + Key7 => Key::Key7, + Key8 => Key::Key8, + Key9 => Key::Key9, + Key0 => Key::Key0, + A => Key::A, + B => Key::B, + C => Key::C, + D => Key::D, + E => Key::E, + F => Key::F, + G => Key::G, + H => Key::H, + I => Key::I, + J => Key::J, + K => Key::K, + L => Key::L, + M => Key::M, + N => Key::N, + O => Key::O, + P => Key::P, + Q => Key::Q, + R => Key::R, + S => Key::S, + T => Key::T, + U => Key::U, + V => Key::V, + W => Key::W, + X => Key::X, + Y => Key::Y, + Z => Key::Z, + Escape => Key::Escape, + F1 => Key::F1, + F2 => Key::F2, + F3 => Key::F3, + F4 => Key::F4, + F5 => Key::F5, + F6 => Key::F6, + F7 => Key::F7, + F8 => Key::F8, + F9 => Key::F9, + F10 => Key::F10, + F11 => Key::F11, + F12 => Key::F12, + F13 => Key::F13, + F14 => Key::F14, + F15 => Key::F15, + F16 => Key::F16, + F17 => Key::F17, + F18 => Key::F18, + F19 => Key::F19, + F20 => Key::F20, + F21 => Key::F21, + F22 => Key::F22, + F23 => Key::F23, + F24 => Key::F24, + Snapshot => Key::Snapshot, + Scroll => Key::Scroll, + Pause => Key::Pause, + Insert => Key::Insert, + Home => Key::Home, + Delete => Key::Delete, + End => Key::End, + PageDown => Key::PageDown, + PageUp => Key::PageUp, + Left => Key::Left, + Up => Key::Up, + Right => Key::Right, + Down => Key::Down, + Back => Key::Back, + Return => Key::Return, + Space => Key::Space, + Compose => Key::Compose, + Numlock => Key::Numlock, + Numpad0 => Key::Numpad0, + Numpad1 => Key::Numpad1, + Numpad2 => Key::Numpad2, + Numpad3 => Key::Numpad3, + Numpad4 => Key::Numpad4, + Numpad5 => Key::Numpad5, + Numpad6 => Key::Numpad6, + Numpad7 => Key::Numpad7, + Numpad8 => Key::Numpad8, + Numpad9 => Key::Numpad9, + AbntC1 => Key::AbntC1, + AbntC2 => Key::AbntC2, + Add => Key::Add, + Apostrophe => Key::Apostrophe, + Apps => Key::Apps, + At => Key::At, + Ax => Key::Ax, + Backslash => Key::Backslash, + Calculator => Key::Calculator, + Capital => Key::Capital, + Colon => Key::Colon, + Comma => Key::Comma, + Convert => Key::Convert, + Decimal => Key::Decimal, + Divide => Key::Divide, + Equals => Key::Equals, + Grave => Key::Grave, + Kana => Key::Kana, + Kanji => Key::Kanji, + LAlt => Key::LAlt, + LBracket => Key::LBracket, + LControl => Key::LControl, + LShift => Key::LShift, + LWin => Key::LWin, + Mail => Key::Mail, + MediaSelect => Key::MediaSelect, + MediaStop => Key::MediaStop, + Minus => Key::Minus, + Multiply => Key::Multiply, + Mute => Key::Mute, + MyComputer => Key::MyComputer, + NavigateForward => Key::NavigateForward, + NavigateBackward => Key::NavigateBackward, + NextTrack => Key::NextTrack, + NoConvert => Key::NoConvert, + NumpadComma => Key::NumpadComma, + NumpadEnter => Key::NumpadEnter, + NumpadEquals => Key::NumpadEquals, + OEM102 => Key::OEM102, + Period => Key::Period, + PlayPause => Key::PlayPause, + Power => Key::Power, + PrevTrack => Key::PrevTrack, + RAlt => Key::RAlt, + RBracket => Key::RBracket, + RControl => Key::RControl, + RShift => Key::RShift, + RWin => Key::RWin, + Semicolon => Key::Semicolon, + Slash => Key::Slash, + Sleep => Key::Sleep, + Stop => Key::Stop, + Subtract => Key::Subtract, + Sysrq => Key::Sysrq, + Tab => Key::Tab, + Underline => Key::Underline, + Unlabeled => Key::Unlabeled, + VolumeDown => Key::VolumeDown, + VolumeUp => Key::VolumeUp, + Wake => Key::Wake, + WebBack => Key::WebBack, + WebFavorites => Key::WebFavorites, + WebForward => Key::WebForward, + WebHome => Key::WebHome, + WebRefresh => Key::WebRefresh, + WebSearch => Key::WebSearch, + WebStop => Key::WebStop, + Yen => Key::Yen, + Caret => Key::Caret, + Copy => Key::Copy, + Paste => Key::Paste, + Cut => Key::Cut, + } + } +} + +#[cfg(test)] +mod tests { + use super::{Config, DEFAULT_ALACRITTY_CONFIG}; + use crate::cli::Options; + + #[test] + fn parse_config() { + let config: Config = + ::serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG).expect("deserialize config"); + + // Sanity check that mouse bindings are being parsed + assert!(!config.mouse_bindings.is_empty()); + + // Sanity check that key bindings are being parsed + assert!(!config.key_bindings.is_empty()); + } + + #[test] + fn dynamic_title_ignoring_options_by_default() { + let config: Config = + ::serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG).expect("deserialize config"); + let old_dynamic_title = config.dynamic_title; + let options = Options::default(); + let config = config.update_dynamic_title(&options); + assert_eq!(old_dynamic_title, config.dynamic_title); + } + + #[test] + fn dynamic_title_overridden_by_options() { + let config: Config = + ::serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG).expect("deserialize config"); + let mut options = Options::default(); + options.title = Some("foo".to_owned()); + let config = config.update_dynamic_title(&options); + assert!(!config.dynamic_title); + } + + #[test] + fn default_match_empty() { + let default = Config::default(); + + let empty = serde_yaml::from_str("key: val\n").unwrap(); + + assert_eq!(default, empty); + } +} diff --git a/src/grid/storage.rs b/src/grid/storage.rs new file mode 100644 index 0000000000..65122b5a39 --- /dev/null +++ b/src/grid/storage.rs @@ -0,0 +1,928 @@ +/// Wrapper around Vec which supports fast indexing and rotation +/// +/// The rotation implemented by grid::Storage is a simple integer addition. +/// Compare with standard library rotation which requires rearranging items in +/// memory. +/// +/// As a consequence, the indexing operators need to be reimplemented for this +/// type to account for the 0th element not always being at the start of the +/// allocation. +/// +/// Because certain Vec operations are no longer valid on this type, no Deref +/// implementation is provided. Anything from Vec that should be exposed must be +/// done so manually. +use std::ops::{Index, IndexMut}; + +use static_assertions::assert_eq_size; + +use super::Row; +use crate::grid::GridCell; +use crate::index::{Column, Line}; + +/// Maximum number of invisible lines before buffer is resized +const TRUNCATE_STEP: usize = 100; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Storage { + inner: Vec>, + zero: usize, + visible_lines: Line, + + /// Total number of lines currently active in the terminal (scrollback + visible) + /// + /// Shrinking this length allows reducing the number of lines in the scrollback buffer without + /// having to truncate the raw `inner` buffer. + /// As long as `len` is bigger than `inner`, it is also possible to grow the scrollback buffer + /// without any additional insertions. + #[serde(default)] + len: usize, +} + +impl ::std::cmp::PartialEq for Storage { + fn eq(&self, other: &Self) -> bool { + // Make sure length is equal + if self.inner.len() != other.inner.len() { + return false; + } + + // Check which vec has the bigger zero + let (ref bigger, ref smaller) = if self.zero >= other.zero { + (self, other) + } else { + (other, self) + }; + + // Calculate the actual zero offset + let len = self.inner.len(); + let bigger_zero = bigger.zero % len; + let smaller_zero = smaller.zero % len; + + // Compare the slices in chunks + // Chunks: + // - Bigger zero to the end + // - Remaining lines in smaller zero vec + // - Beginning of smaller zero vec + // + // Example: + // Bigger Zero (6): + // 4 5 6 | 7 8 9 | 0 1 2 3 + // C2 C2 C2 | C3 C3 C3 | C1 C1 C1 C1 + // Smaller Zero (3): + // 7 8 9 | 0 1 2 3 | 4 5 6 + // C3 C3 C3 | C1 C1 C1 C1 | C2 C2 C2 + bigger.inner[bigger_zero..] + == smaller.inner[smaller_zero..smaller_zero + (len - bigger_zero)] + && bigger.inner[..bigger_zero - smaller_zero] + == smaller.inner[smaller_zero + (len - bigger_zero)..] + && bigger.inner[bigger_zero - smaller_zero..bigger_zero] + == smaller.inner[..smaller_zero] + } +} + +impl Storage { + #[inline] + pub fn with_capacity(lines: Line, template: Row) -> Storage + where + T: Clone, + { + // Initialize visible lines, the scrollback buffer is initialized dynamically + let inner = vec![template; lines.0]; + + Storage { + inner, + zero: 0, + visible_lines: lines - 1, + len: lines.0, + } + } + + /// Update the size of the scrollback history + pub fn update_history(&mut self, history_size: usize, template_row: Row) + where + T: Clone, + { + let current_history = self.len - (self.visible_lines.0 + 1); + if history_size > current_history { + self.grow_lines(history_size - current_history, template_row); + } else if history_size < current_history { + self.shrink_lines(current_history - history_size); + } + } + + /// Increase the number of lines in the buffer + pub fn grow_visible_lines(&mut self, next: Line, template_row: Row) + where + T: Clone, + { + // Number of lines the buffer needs to grow + let growage = (next - (self.visible_lines + 1)).0; + self.grow_lines(growage, template_row); + + // Update visible lines + self.visible_lines = next - 1; + } + + /// Grow the number of lines in the buffer, filling new lines with the template + fn grow_lines(&mut self, growage: usize, template_row: Row) + where + T: Clone, + { + // Only grow if there are not enough lines still hidden + let mut new_growage = 0; + if growage > (self.inner.len() - self.len) { + // Lines to grow additionally to invisible lines + new_growage = growage - (self.inner.len() - self.len); + + // Split off the beginning of the raw inner buffer + let mut start_buffer = self.inner.split_off(self.zero); + + // Insert new template rows at the end of the raw inner buffer + let mut new_lines = vec![template_row; new_growage]; + self.inner.append(&mut new_lines); + + // Add the start to the raw inner buffer again + self.inner.append(&mut start_buffer); + } + + // Update raw buffer length and zero offset + self.zero = (self.zero + new_growage) % self.inner.len(); + self.len += growage; + } + + /// Decrease the number of lines in the buffer + pub fn shrink_visible_lines(&mut self, next: Line) { + // Shrink the size without removing any lines + let shrinkage = (self.visible_lines - (next - 1)).0; + self.shrink_lines(shrinkage); + + // Update visible lines + self.visible_lines = next - 1; + } + + // Shrink the number of lines in the buffer + pub fn shrink_lines(&mut self, shrinkage: usize) { + self.len -= shrinkage; + + // Free memory + if self.inner.len() > self.len() + TRUNCATE_STEP { + self.truncate(); + } + } + + /// Truncate the invisible elements from the raw buffer + pub fn truncate(&mut self) { + self.inner.rotate_left(self.zero); + self.inner.truncate(self.len); + + self.zero = 0; + } + + /// Dynamically grow the storage buffer at runtime + pub fn initialize(&mut self, num_rows: usize, template_row: Row) + where + T: Clone, + { + let mut new = vec![template_row; num_rows]; + + let mut split = self.inner.split_off(self.zero); + self.inner.append(&mut new); + self.inner.append(&mut split); + + self.zero += num_rows; + self.len += num_rows; + } + + #[inline] + pub fn len(&self) -> usize { + self.len + } + + #[inline] + /// Compute actual index in underlying storage given the requested index. + fn compute_index(&self, requested: usize) -> usize { + debug_assert!(requested < self.len); + let zeroed = requested + self.zero; + + // This part is critical for performance, + // so an if/else is used here instead of a moludo operation + if zeroed >= self.inner.len() { + zeroed - self.inner.len() + } else { + zeroed + } + } + + pub fn swap_lines(&mut self, a: Line, b: Line) { + let offset = self.inner.len() + self.zero + *self.visible_lines; + let a = (offset - *a) % self.inner.len(); + let b = (offset - *b) % self.inner.len(); + self.inner.swap(a, b); + } + + /// Swap implementation for Row. + /// + /// Exploits the known size of Row to produce a slightly more efficient + /// swap than going through slice::swap. + /// + /// The default implementation from swap generates 8 movups and 4 movaps + /// instructions. This implementation achieves the swap in only 8 movups + /// instructions. + pub fn swap(&mut self, a: usize, b: usize) { + assert_eq_size!(Row, [usize; 4]); + + let a = self.compute_index(a); + let b = self.compute_index(b); + + unsafe { + // Cast to a qword array to opt out of copy restrictions and avoid + // drop hazards. Byte array is no good here since for whatever + // reason LLVM won't optimized it. + let a_ptr = self.inner.as_mut_ptr().add(a) as *mut usize; + let b_ptr = self.inner.as_mut_ptr().add(b) as *mut usize; + + // Copy 1 qword at a time + // + // The optimizer unrolls this loop and vectorizes it. + let mut tmp: usize; + for i in 0..4 { + tmp = *a_ptr.offset(i); + *a_ptr.offset(i) = *b_ptr.offset(i); + *b_ptr.offset(i) = tmp; + } + } + } + + #[inline] + pub fn rotate(&mut self, count: isize) { + debug_assert!(count.abs() as usize <= self.inner.len()); + + let len = self.inner.len(); + self.zero = (self.zero as isize + count + len as isize) as usize % len; + } + + // Fast path + #[inline] + pub fn rotate_up(&mut self, count: usize) { + self.zero = (self.zero + count) % self.inner.len(); + } + + #[inline] + pub fn insert(&mut self, index: usize, row: Row, max_lines: usize) { + let index = self.compute_index(index); + self.inner.insert(index, row); + + if index < self.zero { + self.zero += 1; + } + + if self.len < max_lines { + self.len += 1; + } + } + + #[inline] + pub fn remove(&mut self, index: usize) -> Row { + let index = self.compute_index(index); + if index < self.zero { + self.zero -= 1; + } + self.len -= 1; + + self.inner.remove(index) + } + + /// Shrink columns of hidden buffered lines. + /// + /// XXX This suggests that Storage is a leaky abstraction. Ultimately, this + /// is needed because of the grow/shrink lines functionality. + #[inline] + pub fn shrink_hidden(&mut self, cols: Column) + where + T: GridCell + Copy, + { + let start = self.zero + self.len; + let end = self.zero + self.inner.len(); + for mut i in start..end { + if i >= self.inner.len() { + i -= self.inner.len(); + } + + self.inner[i].shrink(cols); + } + } + + /// Grow columns of hidden buffered lines. + /// + /// XXX This suggests that Storage is a leaky abstraction. Ultimately, this + /// is needed because of the grow/shrink lines functionality. + #[inline] + pub fn grow_hidden(&mut self, cols: Column, template: &T) + where + T: Copy + Clone, + { + let start = self.zero + self.len; + let end = self.zero + self.inner.len(); + for mut i in start..end { + if i >= self.inner.len() { + i -= self.inner.len(); + } + + self.inner[i].grow(cols, template); + } + } +} + +impl Index for Storage { + type Output = Row; + #[inline] + fn index(&self, index: usize) -> &Self::Output { + &self.inner[self.compute_index(index)] + } +} + +impl IndexMut for Storage { + #[inline] + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + let index = self.compute_index(index); // borrowck + &mut self.inner[index] + } +} + +impl Index for Storage { + type Output = Row; + #[inline] + fn index(&self, index: Line) -> &Self::Output { + let index = self.visible_lines - index; + &self[*index] + } +} + +impl IndexMut for Storage { + #[inline] + fn index_mut(&mut self, index: Line) -> &mut Self::Output { + let index = self.visible_lines - index; + &mut self[*index] + } +} + +/// Grow the buffer one line at the end of the buffer +/// +/// Before: +/// 0: 0 <- Zero +/// 1: 1 +/// 2: - +/// After: +/// 0: - +/// 1: 0 <- Zero +/// 2: 1 +/// 3: - +#[test] +fn grow_after_zero() { + // Setup storage area + let mut storage = Storage { + inner: vec![ + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'-'), + ], + zero: 0, + visible_lines: Line(2), + len: 3, + }; + + // Grow buffer + storage.grow_visible_lines(Line(4), Row::new(Column(1), &'-')); + + // Make sure the result is correct + let expected = Storage { + inner: vec![ + Row::new(Column(1), &'-'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'-'), + ], + zero: 1, + visible_lines: Line(0), + len: 4, + }; + assert_eq!(storage.inner, expected.inner); + assert_eq!(storage.zero, expected.zero); + assert_eq!(storage.len, expected.len); +} + +/// Grow the buffer one line at the start of the buffer +/// +/// Before: +/// 0: - +/// 1: 0 <- Zero +/// 2: 1 +/// After: +/// 0: - +/// 1: - +/// 2: 0 <- Zero +/// 3: 1 +#[test] +fn grow_before_zero() { + // Setup storage area + let mut storage = Storage { + inner: vec![ + Row::new(Column(1), &'-'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + ], + zero: 1, + visible_lines: Line(2), + len: 3, + }; + + // Grow buffer + storage.grow_visible_lines(Line(4), Row::new(Column(1), &'-')); + + // Make sure the result is correct + let expected = Storage { + inner: vec![ + Row::new(Column(1), &'-'), + Row::new(Column(1), &'-'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + ], + zero: 2, + visible_lines: Line(0), + len: 4, + }; + assert_eq!(storage.inner, expected.inner); + assert_eq!(storage.zero, expected.zero); + assert_eq!(storage.len, expected.len); +} + +/// Shrink the buffer one line at the start of the buffer +/// +/// Before: +/// 0: 2 +/// 1: 0 <- Zero +/// 2: 1 +/// After: +/// 0: 2 <- Hidden +/// 0: 0 <- Zero +/// 1: 1 +#[test] +fn shrink_before_zero() { + // Setup storage area + let mut storage = Storage { + inner: vec![ + Row::new(Column(1), &'2'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + ], + zero: 1, + visible_lines: Line(2), + len: 3, + }; + + // Shrink buffer + storage.shrink_visible_lines(Line(2)); + + // Make sure the result is correct + let expected = Storage { + inner: vec![ + Row::new(Column(1), &'2'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + ], + zero: 1, + visible_lines: Line(0), + len: 2, + }; + assert_eq!(storage.inner, expected.inner); + assert_eq!(storage.zero, expected.zero); + assert_eq!(storage.len, expected.len); +} + +/// Shrink the buffer one line at the end of the buffer +/// +/// Before: +/// 0: 0 <- Zero +/// 1: 1 +/// 2: 2 +/// After: +/// 0: 0 <- Zero +/// 1: 1 +/// 2: 2 <- Hidden +#[test] +fn shrink_after_zero() { + // Setup storage area + let mut storage = Storage { + inner: vec![ + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + ], + zero: 0, + visible_lines: Line(2), + len: 3, + }; + + // Shrink buffer + storage.shrink_visible_lines(Line(2)); + + // Make sure the result is correct + let expected = Storage { + inner: vec![ + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + ], + zero: 0, + visible_lines: Line(0), + len: 2, + }; + assert_eq!(storage.inner, expected.inner); + assert_eq!(storage.zero, expected.zero); + assert_eq!(storage.len, expected.len); +} + +/// Shrink the buffer at the start and end of the buffer +/// +/// Before: +/// 0: 4 +/// 1: 5 +/// 2: 0 <- Zero +/// 3: 1 +/// 4: 2 +/// 5: 3 +/// After: +/// 0: 4 <- Hidden +/// 1: 5 <- Hidden +/// 2: 0 <- Zero +/// 3: 1 +/// 4: 2 <- Hidden +/// 5: 3 <- Hidden +#[test] +fn shrink_before_and_after_zero() { + // Setup storage area + let mut storage = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 2, + visible_lines: Line(5), + len: 6, + }; + + // Shrink buffer + storage.shrink_visible_lines(Line(2)); + + // Make sure the result is correct + let expected = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 2, + visible_lines: Line(0), + len: 2, + }; + assert_eq!(storage.inner, expected.inner); + assert_eq!(storage.zero, expected.zero); + assert_eq!(storage.len, expected.len); +} + +/// Check that when truncating all hidden lines are removed from the raw buffer +/// +/// Before: +/// 0: 4 <- Hidden +/// 1: 5 <- Hidden +/// 2: 0 <- Zero +/// 3: 1 +/// 4: 2 <- Hidden +/// 5: 3 <- Hidden +/// After: +/// 0: 0 <- Zero +/// 1: 1 +#[test] +fn truncate_invisible_lines() { + // Setup storage area + let mut storage = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 2, + visible_lines: Line(1), + len: 2, + }; + + // Truncate buffer + storage.truncate(); + + // Make sure the result is correct + let expected = Storage { + inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1')], + zero: 0, + visible_lines: Line(1), + len: 2, + }; + assert_eq!(storage.visible_lines, expected.visible_lines); + assert_eq!(storage.inner, expected.inner); + assert_eq!(storage.zero, expected.zero); + assert_eq!(storage.len, expected.len); +} + +/// Truncate buffer only at the beginning +/// +/// Before: +/// 0: 1 +/// 1: 2 <- Hidden +/// 2: 0 <- Zero +/// After: +/// 0: 1 +/// 0: 0 <- Zero +#[test] +fn truncate_invisible_lines_beginning() { + // Setup storage area + let mut storage = Storage { + inner: vec![ + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'0'), + ], + zero: 2, + visible_lines: Line(1), + len: 2, + }; + + // Truncate buffer + storage.truncate(); + + // Make sure the result is correct + let expected = Storage { + inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1')], + zero: 0, + visible_lines: Line(1), + len: 2, + }; + assert_eq!(storage.visible_lines, expected.visible_lines); + assert_eq!(storage.inner, expected.inner); + assert_eq!(storage.zero, expected.zero); + assert_eq!(storage.len, expected.len); +} + +/// First shrink the buffer and then grow it again +/// +/// Before: +/// 0: 4 +/// 1: 5 +/// 2: 0 <- Zero +/// 3: 1 +/// 4: 2 +/// 5: 3 +/// After Shrinking: +/// 0: 4 <- Hidden +/// 1: 5 <- Hidden +/// 2: 0 <- Zero +/// 3: 1 +/// 4: 2 +/// 5: 3 <- Hidden +/// After Growing: +/// 0: 4 +/// 1: 5 +/// 2: - +/// 3: 0 <- Zero +/// 4: 1 +/// 5: 2 +/// 6: 3 +#[test] +fn shrink_then_grow() { + // Setup storage area + let mut storage = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 2, + visible_lines: Line(0), + len: 6, + }; + + // Shrink buffer + storage.shrink_lines(3); + + // Make sure the result after shrinking is correct + let shrinking_expected = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 2, + visible_lines: Line(0), + len: 3, + }; + assert_eq!(storage.inner, shrinking_expected.inner); + assert_eq!(storage.zero, shrinking_expected.zero); + assert_eq!(storage.len, shrinking_expected.len); + + // Grow buffer + storage.grow_lines(4, Row::new(Column(1), &'-')); + + // Make sure the result after shrinking is correct + let growing_expected = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'-'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 3, + visible_lines: Line(0), + len: 7, + }; + assert_eq!(storage.inner, growing_expected.inner); + assert_eq!(storage.zero, growing_expected.zero); + assert_eq!(storage.len, growing_expected.len); +} + +#[test] +fn initialize() { + // Setup storage area + let mut storage = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 2, + visible_lines: Line(0), + len: 6, + }; + + // Initialize additional lines + storage.initialize(3, Row::new(Column(1), &'-')); + + // Make sure the lines are present and at the right location + let shrinking_expected = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'-'), + Row::new(Column(1), &'-'), + Row::new(Column(1), &'-'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 5, + visible_lines: Line(0), + len: 9, + }; + assert_eq!(storage.inner, shrinking_expected.inner); + assert_eq!(storage.zero, shrinking_expected.zero); + assert_eq!(storage.len, shrinking_expected.len); +} + +#[test] +fn insert() { + // Setup storage area + let mut storage = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 2, + visible_lines: Line(0), + len: 6, + }; + + // Initialize additional lines + storage.insert(2, Row::new(Column(1), &'-'), 100); + + // Make sure the lines are present and at the right location + let shrinking_expected = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'-'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 2, + visible_lines: Line(0), + len: 7, + }; + assert_eq!(storage.inner, shrinking_expected.inner); + assert_eq!(storage.zero, shrinking_expected.zero); + assert_eq!(storage.len, shrinking_expected.len); +} + +#[test] +fn insert_truncate_max() { + // Setup storage area + let mut storage = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 2, + visible_lines: Line(0), + len: 6, + }; + + // Initialize additional lines + storage.insert(2, Row::new(Column(1), &'-'), 6); + + // Make sure the lines are present and at the right location + let shrinking_expected = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'-'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 2, + visible_lines: Line(0), + len: 6, + }; + assert_eq!(storage.inner, shrinking_expected.inner); + assert_eq!(storage.zero, shrinking_expected.zero); + assert_eq!(storage.len, shrinking_expected.len); +} + +#[test] +fn insert_at_zero() { + // Setup storage area + let mut storage = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 2, + visible_lines: Line(0), + len: 6, + }; + + // Initialize additional lines + storage.insert(0, Row::new(Column(1), &'-'), 6); + + // Make sure the lines are present and at the right location + let shrinking_expected = Storage { + inner: vec![ + Row::new(Column(1), &'4'), + Row::new(Column(1), &'5'), + Row::new(Column(1), &'-'), + Row::new(Column(1), &'0'), + Row::new(Column(1), &'1'), + Row::new(Column(1), &'2'), + Row::new(Column(1), &'3'), + ], + zero: 2, + visible_lines: Line(0), + len: 6, + }; + assert_eq!(storage.inner, shrinking_expected.inner); + assert_eq!(storage.zero, shrinking_expected.zero); + assert_eq!(storage.len, shrinking_expected.len); +} diff --git a/src/selection.rs b/src/selection.rs new file mode 100644 index 0000000000..205687b6ae --- /dev/null +++ b/src/selection.rs @@ -0,0 +1,647 @@ +// 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. + +//! State management for a selection in the grid +//! +//! A selection should start when the mouse is clicked, and it should be +//! finalized when the button is released. The selection should be cleared +//! when text is added/removed/scrolled on the screen. The selection should +//! also be cleared if the user clicks off of the selection. +use std::cmp::{max, min}; +use std::ops::Range; + +use crate::index::{Column, Point, Side}; +use crate::term::Search; + +/// Describes a region of a 2-dimensional area +/// +/// Used to track a text selection. There are three supported modes, each with its own constructor: +/// [`simple`], [`semantic`], and [`lines`]. The [`simple`] mode precisely tracks which cells are +/// selected without any expansion. [`semantic`] mode expands the initial selection to the nearest +/// semantic escape char in either direction. [`lines`] will always select entire lines. +/// +/// Calls to [`update`] operate different based on the selection kind. The [`simple`] mode does +/// nothing special, simply tracks points and sides. [`semantic`] will continue to expand out to +/// semantic boundaries as the selection point changes. Similarly, [`lines`] will always expand the +/// new point to encompass entire lines. +/// +/// [`simple`]: enum.Selection.html#method.simple +/// [`semantic`]: enum.Selection.html#method.semantic +/// [`lines`]: enum.Selection.html#method.lines +#[derive(Debug, Clone, PartialEq)] +pub enum Selection { + Simple { + /// The region representing start and end of cursor movement + region: Range, + }, + Semantic { + /// The region representing start and end of cursor movement + region: Range>, + }, + Lines { + /// The region representing start and end of cursor movement + region: Range>, + + /// The line under the initial point. This is always selected regardless + /// of which way the cursor is moved. + initial_line: isize, + }, +} + +/// A Point and side within that point. +#[derive(Debug, Clone, PartialEq)] +pub struct Anchor { + point: Point, + side: Side, +} + +impl Anchor { + fn new(point: Point, side: Side) -> Anchor { + Anchor { point, side } + } +} + +/// A type that has 2-dimensional boundaries +pub trait Dimensions { + /// Get the size of the area + fn dimensions(&self) -> Point; +} + +impl Selection { + pub fn simple(location: Point, side: Side) -> Selection { + Selection::Simple { + region: Range { + start: Anchor::new(location.into(), side), + end: Anchor::new(location.into(), side), + }, + } + } + + pub fn rotate(&mut self, offset: isize) { + match *self { + Selection::Simple { ref mut region } => { + region.start.point.line += offset; + region.end.point.line += offset; + } + Selection::Semantic { ref mut region } => { + region.start.line += offset; + region.end.line += offset; + } + Selection::Lines { + ref mut region, + ref mut initial_line, + } => { + region.start.line += offset; + region.end.line += offset; + *initial_line += offset; + } + } + } + + pub fn semantic(point: Point) -> Selection { + Selection::Semantic { + region: Range { + start: point.into(), + end: point.into(), + }, + } + } + + pub fn lines(point: Point) -> Selection { + Selection::Lines { + region: Range { + start: point.into(), + end: point.into(), + }, + initial_line: point.line as isize, + } + } + + pub fn update(&mut self, location: Point, side: Side) { + // Always update the `end`; can normalize later during span generation. + match *self { + Selection::Simple { ref mut region } => { + region.end = Anchor::new(location.into(), side); + } + Selection::Semantic { ref mut region } | Selection::Lines { ref mut region, .. } => { + region.end = location.into(); + } + } + } + + pub fn to_span(&self, grid: &G, alt_screen: bool) -> Option + where + G: Search + Dimensions, + { + match *self { + Selection::Simple { ref region } => Selection::span_simple(grid, region, alt_screen), + Selection::Semantic { ref region } => { + Selection::span_semantic(grid, region, alt_screen) + } + Selection::Lines { + ref region, + initial_line, + } => Selection::span_lines(grid, region, initial_line, alt_screen), + } + } + + pub fn is_empty(&self) -> bool { + match *self { + Selection::Simple { ref region } => { + region.start == region.end && region.start.side == region.end.side + } + Selection::Semantic { .. } | Selection::Lines { .. } => false, + } + } + + fn span_semantic(grid: &G, region: &Range>, alt_screen: bool) -> Option + where + G: Search + Dimensions, + { + let cols = grid.dimensions().col; + let lines = grid.dimensions().line.0 as isize; + + // Normalize ordering of selected cells + let (mut front, mut tail) = if region.start < region.end { + (region.start, region.end) + } else { + (region.end, region.start) + }; + + if alt_screen { + Selection::alt_screen_clamp(&mut front, &mut tail, lines, cols)?; + } + + let (mut start, mut end) = if front < tail && front.line == tail.line { + ( + grid.semantic_search_left(front.into()), + grid.semantic_search_right(tail.into()), + ) + } else { + ( + grid.semantic_search_right(front.into()), + grid.semantic_search_left(tail.into()), + ) + }; + + if start > end { + ::std::mem::swap(&mut start, &mut end); + } + + Some(Span { + cols, + front: start, + tail: end, + ty: SpanType::Inclusive, + }) + } + + fn span_lines( + grid: &G, + region: &Range>, + initial_line: isize, + alt_screen: bool, + ) -> Option + where + G: Dimensions, + { + let cols = grid.dimensions().col; + let lines = grid.dimensions().line.0 as isize; + + // First, create start and end points based on initial line and the grid + // dimensions. + let mut start = Point { + col: cols - 1, + line: initial_line, + }; + let mut end = Point { + col: Column(0), + line: initial_line, + }; + + // Now, expand lines based on where cursor started and ended. + if region.start.line < region.end.line { + // Start is below end + start.line = min(start.line, region.start.line); + end.line = max(end.line, region.end.line); + } else { + // Start is above end + start.line = min(start.line, region.end.line); + end.line = max(end.line, region.start.line); + } + + if alt_screen { + Selection::alt_screen_clamp(&mut start, &mut end, lines, cols)?; + } + + Some(Span { + cols, + front: start.into(), + tail: end.into(), + ty: SpanType::Inclusive, + }) + } + + fn span_simple(grid: &G, region: &Range, alt_screen: bool) -> Option + where + G: Dimensions, + { + let start = region.start.point; + let start_side = region.start.side; + let end = region.end.point; + let end_side = region.end.side; + let cols = grid.dimensions().col; + let lines = grid.dimensions().line.0 as isize; + + // Make sure front is always the "bottom" and tail is always the "top" + let (mut front, mut tail, front_side, tail_side) = + if start.line > end.line || start.line == end.line && start.col <= end.col { + // Selected upward; start/end are swapped + (end, start, end_side, start_side) + } else { + // Selected downward; no swapping + (start, end, start_side, end_side) + }; + + // No selection for single cell with identical sides or two cell with right+left sides + if (front == tail && front_side == tail_side) + || (tail_side == Side::Right + && front_side == Side::Left + && front.line == tail.line + && front.col == tail.col + 1) + { + return None; + } + + // Remove last cell if selection ends to the left of a cell + if front_side == Side::Left && start != end { + // Special case when selection starts to left of first cell + if front.col == Column(0) { + front.col = cols - 1; + front.line += 1; + } else { + front.col -= 1; + } + } + + // Remove first cell if selection starts at the right of a cell + if tail_side == Side::Right && front != tail { + tail.col += 1; + } + + if alt_screen { + Selection::alt_screen_clamp(&mut front, &mut tail, lines, cols)?; + } + + // Return the selection with all cells inclusive + Some(Span { + cols, + front: front.into(), + tail: tail.into(), + ty: SpanType::Inclusive, + }) + } + + // Clamp selection in the alternate screen to the visible region + fn alt_screen_clamp( + front: &mut Point, + tail: &mut Point, + lines: isize, + cols: Column, + ) -> Option<()> { + if tail.line >= lines { + // Don't show selection above visible region + if front.line >= lines { + return None; + } + + // Clamp selection above viewport to visible region + tail.line = lines - 1; + tail.col = Column(0); + } + + if front.line < 0 { + // Don't show selection below visible region + if tail.line < 0 { + return None; + } + + // Clamp selection below viewport to visible region + front.line = 0; + front.col = cols - 1; + } + + Some(()) + } +} + +/// How to interpret the locations of a Span. +#[derive(Debug, Eq, PartialEq)] +pub enum SpanType { + /// Includes the beginning and end locations + Inclusive, + + /// Exclude both beginning and end + Exclusive, + + /// Excludes last cell of selection + ExcludeTail, + + /// Excludes first cell of selection + ExcludeFront, +} + +/// Represents a span of selected cells +#[derive(Debug, Eq, PartialEq)] +pub struct Span { + front: Point, + tail: Point, + cols: Column, + + /// The type says whether ends are included or not. + ty: SpanType, +} + +#[derive(Debug)] +pub struct Locations { + /// Start point from bottom of buffer + pub start: Point, + /// End point towards top of buffer + pub end: Point, +} + +impl Span { + pub fn to_locations(&self) -> Locations { + let (start, end) = match self.ty { + SpanType::Inclusive => (self.front, self.tail), + SpanType::Exclusive => ( + Span::wrap_start(self.front, self.cols), + Span::wrap_end(self.tail, self.cols), + ), + SpanType::ExcludeFront => (Span::wrap_start(self.front, self.cols), self.tail), + SpanType::ExcludeTail => (self.front, Span::wrap_end(self.tail, self.cols)), + }; + + Locations { start, end } + } + + fn wrap_start(mut start: Point, cols: Column) -> Point { + if start.col == cols - 1 { + Point { + line: start.line + 1, + col: Column(0), + } + } else { + start.col += 1; + start + } + } + + fn wrap_end(end: Point, cols: Column) -> Point { + if end.col == Column(0) && end.line != 0 { + Point { + line: end.line - 1, + col: cols, + } + } else { + Point { + line: end.line, + col: end.col - 1, + } + } + } +} + +/// Tests for selection +/// +/// There are comments on all of the tests describing the selection. Pictograms +/// are used to avoid ambiguity. Grid cells are represented by a [ ]. Only +/// cells that are completely covered are counted in a selection. Ends are +/// represented by `B` and `E` for begin and end, respectively. A selected cell +/// looks like [XX], [BX] (at the start), [XB] (at the end), [XE] (at the end), +/// and [EX] (at the start), or [BE] for a single cell. Partially selected cells +/// look like [ B] and [E ]. +#[cfg(test)] +mod test { + use super::{Selection, Span, SpanType}; + use crate::index::{Column, Line, Point, Side}; + + struct Dimensions(Point); + impl super::Dimensions for Dimensions { + fn dimensions(&self) -> Point { + self.0 + } + } + + impl Dimensions { + pub fn new(line: usize, col: usize) -> Self { + Dimensions(Point { + line: Line(line), + col: Column(col), + }) + } + } + + impl super::Search for Dimensions { + fn semantic_search_left(&self, point: Point) -> Point { + point + } + fn semantic_search_right(&self, point: Point) -> Point { + point + } + fn url_search(&self, _: Point) -> Option { + None + } + } + + /// Test case of single cell selection + /// + /// 1. [ ] + /// 2. [B ] + /// 3. [BE] + #[test] + fn single_cell_left_to_right() { + let location = Point { + line: 0, + col: Column(0), + }; + let mut selection = Selection::simple(location, Side::Left); + selection.update(location, Side::Right); + + assert_eq!( + selection.to_span(&Dimensions::new(1, 1), false).unwrap(), + Span { + cols: Column(1), + ty: SpanType::Inclusive, + front: location, + tail: location + } + ); + } + + /// Test case of single cell selection + /// + /// 1. [ ] + /// 2. [ B] + /// 3. [EB] + #[test] + fn single_cell_right_to_left() { + let location = Point { + line: 0, + col: Column(0), + }; + let mut selection = Selection::simple(location, Side::Right); + selection.update(location, Side::Left); + + assert_eq!( + selection.to_span(&Dimensions::new(1, 1), false).unwrap(), + Span { + cols: Column(1), + ty: SpanType::Inclusive, + front: location, + tail: location + } + ); + } + + /// Test adjacent cell selection from left to right + /// + /// 1. [ ][ ] + /// 2. [ B][ ] + /// 3. [ B][E ] + #[test] + fn between_adjacent_cells_left_to_right() { + let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right); + selection.update(Point::new(0, Column(1)), Side::Left); + + assert_eq!(selection.to_span(&Dimensions::new(1, 2), false), None); + } + + /// Test adjacent cell selection from right to left + /// + /// 1. [ ][ ] + /// 2. [ ][B ] + /// 3. [ E][B ] + #[test] + fn between_adjacent_cells_right_to_left() { + let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Left); + selection.update(Point::new(0, Column(0)), Side::Right); + + assert_eq!(selection.to_span(&Dimensions::new(1, 2), false), None); + } + + /// Test selection across adjacent lines + /// + /// + /// 1. [ ][ ][ ][ ][ ] + /// [ ][ ][ ][ ][ ] + /// 2. [ ][ B][ ][ ][ ] + /// [ ][ ][ ][ ][ ] + /// 3. [ ][ B][XX][XX][XX] + /// [XX][XE][ ][ ][ ] + #[test] + fn across_adjacent_lines_upward_final_cell_exclusive() { + let mut selection = Selection::simple(Point::new(1, Column(1)), Side::Right); + selection.update(Point::new(0, Column(1)), Side::Right); + + assert_eq!( + selection.to_span(&Dimensions::new(2, 5), false).unwrap(), + Span { + cols: Column(5), + front: Point::new(0, Column(1)), + tail: Point::new(1, Column(2)), + ty: SpanType::Inclusive, + } + ); + } + + /// Test selection across adjacent lines + /// + /// + /// 1. [ ][ ][ ][ ][ ] + /// [ ][ ][ ][ ][ ] + /// 2. [ ][ ][ ][ ][ ] + /// [ ][ B][ ][ ][ ] + /// 3. [ ][ E][XX][XX][XX] + /// [XX][XB][ ][ ][ ] + /// 4. [ E][XX][XX][XX][XX] + /// [XX][XB][ ][ ][ ] + #[test] + fn selection_bigger_then_smaller() { + let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Right); + selection.update(Point::new(1, Column(1)), Side::Right); + selection.update(Point::new(1, Column(0)), Side::Right); + + assert_eq!( + selection.to_span(&Dimensions::new(2, 5), false).unwrap(), + Span { + cols: Column(5), + front: Point::new(0, Column(1)), + tail: Point::new(1, Column(1)), + ty: SpanType::Inclusive, + } + ); + } + + #[test] + fn alt_scren_lines() { + let mut selection = Selection::lines(Point::new(0, Column(0))); + selection.update(Point::new(5, Column(3)), Side::Right); + selection.rotate(-3); + + assert_eq!( + selection.to_span(&Dimensions::new(10, 5), true).unwrap(), + Span { + cols: Column(5), + front: Point::new(0, Column(4)), + tail: Point::new(2, Column(0)), + ty: SpanType::Inclusive, + } + ); + } + + #[test] + fn alt_screen_semantic() { + let mut selection = Selection::semantic(Point::new(0, Column(0))); + selection.update(Point::new(5, Column(3)), Side::Right); + selection.rotate(-3); + + assert_eq!( + selection.to_span(&Dimensions::new(10, 5), true).unwrap(), + Span { + cols: Column(5), + front: Point::new(0, Column(4)), + tail: Point::new(2, Column(3)), + ty: SpanType::Inclusive, + } + ); + } + + #[test] + fn alt_screen_simple() { + let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right); + selection.update(Point::new(5, Column(3)), Side::Right); + selection.rotate(-3); + + assert_eq!( + selection.to_span(&Dimensions::new(10, 5), true).unwrap(), + Span { + cols: Column(5), + front: Point::new(0, Column(4)), + tail: Point::new(2, Column(4)), + ty: SpanType::Inclusive, + } + ); + } +} diff --git a/src/term/color.rs b/src/term/color.rs new file mode 100644 index 0000000000..394c6c2d90 --- /dev/null +++ b/src/term/color.rs @@ -0,0 +1,244 @@ +use std::fmt; +use std::ops::{Index, IndexMut, Mul}; + +use crate::ansi; +use crate::config::Colors; + +pub const COUNT: usize = 270; + +pub const RED: Rgb = Rgb { + r: 0xff, + g: 0x0, + b: 0x0, +}; +pub const YELLOW: Rgb = Rgb { + r: 0xff, + g: 0xff, + b: 0x0, +}; + +#[derive(Debug, Eq, PartialEq, Copy, Clone, Default, Serialize, Deserialize)] +pub struct Rgb { + pub r: u8, + pub g: u8, + pub b: u8, +} + +// a multiply function for Rgb, as the default dim is just *2/3 +impl Mul for Rgb { + type Output = Rgb; + + fn mul(self, rhs: f32) -> Rgb { + let result = Rgb { + r: (f32::from(self.r) * rhs).max(0.0).min(255.0) as u8, + g: (f32::from(self.g) * rhs).max(0.0).min(255.0) as u8, + b: (f32::from(self.b) * rhs).max(0.0).min(255.0) as u8, + }; + + trace!("Scaling RGB by {} from {:?} to {:?}", rhs, self, result); + + result + } +} + +/// List of indexed colors +/// +/// The first 16 entries are the standard ansi named colors. Items 16..232 are +/// the color cube. Items 233..256 are the grayscale ramp. Item 256 is +/// the configured foreground color, item 257 is the configured background +/// color, item 258 is the cursor foreground color, item 259 is the cursor +/// background color. Following that are 8 positions for dim colors. +/// Item 268 is the bright foreground color, 269 the dim foreground. +#[derive(Copy, Clone)] +pub struct List([Rgb; COUNT]); + +impl<'a> From<&'a Colors> for List { + fn from(colors: &Colors) -> List { + // Type inference fails without this annotation + let mut list: List = unsafe { ::std::mem::uninitialized() }; + + list.fill_named(colors); + list.fill_cube(colors); + list.fill_gray_ramp(colors); + + list + } +} + +impl List { + pub fn fill_named(&mut self, colors: &Colors) { + // Normals + self[ansi::NamedColor::Black] = colors.normal.black; + self[ansi::NamedColor::Red] = colors.normal.red; + self[ansi::NamedColor::Green] = colors.normal.green; + self[ansi::NamedColor::Yellow] = colors.normal.yellow; + self[ansi::NamedColor::Blue] = colors.normal.blue; + self[ansi::NamedColor::Magenta] = colors.normal.magenta; + self[ansi::NamedColor::Cyan] = colors.normal.cyan; + self[ansi::NamedColor::White] = colors.normal.white; + + // Brights + self[ansi::NamedColor::BrightBlack] = colors.bright.black; + self[ansi::NamedColor::BrightRed] = colors.bright.red; + self[ansi::NamedColor::BrightGreen] = colors.bright.green; + self[ansi::NamedColor::BrightYellow] = colors.bright.yellow; + self[ansi::NamedColor::BrightBlue] = colors.bright.blue; + self[ansi::NamedColor::BrightMagenta] = colors.bright.magenta; + self[ansi::NamedColor::BrightCyan] = colors.bright.cyan; + self[ansi::NamedColor::BrightWhite] = colors.bright.white; + self[ansi::NamedColor::BrightForeground] = colors + .primary + .bright_foreground + .unwrap_or(colors.primary.foreground); + + // Foreground and background + self[ansi::NamedColor::Foreground] = colors.primary.foreground; + self[ansi::NamedColor::Background] = colors.primary.background; + + // Foreground and background for custom cursor colors + self[ansi::NamedColor::CursorText] = colors.cursor.text.unwrap_or_else(Rgb::default); + self[ansi::NamedColor::Cursor] = colors.cursor.cursor.unwrap_or_else(Rgb::default); + + // Dims + self[ansi::NamedColor::DimForeground] = colors + .primary + .dim_foreground + .unwrap_or(colors.primary.foreground * 0.66); + match colors.dim { + Some(ref dim) => { + trace!("Using config-provided dim colors"); + self[ansi::NamedColor::DimBlack] = dim.black; + self[ansi::NamedColor::DimRed] = dim.red; + self[ansi::NamedColor::DimGreen] = dim.green; + self[ansi::NamedColor::DimYellow] = dim.yellow; + self[ansi::NamedColor::DimBlue] = dim.blue; + self[ansi::NamedColor::DimMagenta] = dim.magenta; + self[ansi::NamedColor::DimCyan] = dim.cyan; + self[ansi::NamedColor::DimWhite] = dim.white; + } + None => { + trace!("Deriving dim colors from normal colors"); + self[ansi::NamedColor::DimBlack] = colors.normal.black * 0.66; + self[ansi::NamedColor::DimRed] = colors.normal.red * 0.66; + self[ansi::NamedColor::DimGreen] = colors.normal.green * 0.66; + self[ansi::NamedColor::DimYellow] = colors.normal.yellow * 0.66; + self[ansi::NamedColor::DimBlue] = colors.normal.blue * 0.66; + self[ansi::NamedColor::DimMagenta] = colors.normal.magenta * 0.66; + self[ansi::NamedColor::DimCyan] = colors.normal.cyan * 0.66; + self[ansi::NamedColor::DimWhite] = colors.normal.white * 0.66; + } + } + } + + pub fn fill_cube(&mut self, colors: &Colors) { + let mut index: usize = 16; + // Build colors + for r in 0..6 { + for g in 0..6 { + for b in 0..6 { + // Override colors 16..232 with the config (if present) + if let Some(indexed_color) = colors + .indexed_colors + .iter() + .find(|ic| ic.index == index as u8) + { + self[index] = indexed_color.color; + } else { + self[index] = Rgb { + r: if r == 0 { 0 } else { r * 40 + 55 }, + b: if b == 0 { 0 } else { b * 40 + 55 }, + g: if g == 0 { 0 } else { g * 40 + 55 }, + }; + } + index += 1; + } + } + } + + debug_assert!(index == 232); + } + + pub fn fill_gray_ramp(&mut self, colors: &Colors) { + let mut index: usize = 232; + + for i in 0..24 { + // Index of the color is number of named colors + number of cube colors + i + let color_index = 16 + 216 + i; + + // Override colors 232..256 with the config (if present) + if let Some(indexed_color) = colors + .indexed_colors + .iter() + .find(|ic| ic.index == color_index) + { + self[index] = indexed_color.color; + index += 1; + continue; + } + + let value = i * 10 + 8; + self[index] = Rgb { + r: value, + g: value, + b: value, + }; + index += 1; + } + + debug_assert!(index == 256); + } +} + +impl fmt::Debug for List { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("List[..]") + } +} + +impl Index for List { + type Output = Rgb; + + #[inline] + fn index(&self, idx: ansi::NamedColor) -> &Self::Output { + &self.0[idx as usize] + } +} + +impl IndexMut for List { + #[inline] + fn index_mut(&mut self, idx: ansi::NamedColor) -> &mut Self::Output { + &mut self.0[idx as usize] + } +} + +impl Index for List { + type Output = Rgb; + + #[inline] + fn index(&self, idx: usize) -> &Self::Output { + &self.0[idx] + } +} + +impl IndexMut for List { + #[inline] + fn index_mut(&mut self, idx: usize) -> &mut Self::Output { + &mut self.0[idx] + } +} + +impl Index for List { + type Output = Rgb; + + #[inline] + fn index(&self, idx: u8) -> &Self::Output { + &self.0[idx as usize] + } +} + +impl IndexMut for List { + #[inline] + fn index_mut(&mut self, idx: u8) -> &mut Self::Output { + &mut self.0[idx as usize] + } +} diff --git a/src/url.rs b/src/url.rs new file mode 100644 index 0000000000..f8d25a7499 --- /dev/null +++ b/src/url.rs @@ -0,0 +1,307 @@ +// 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 url::Url; + +// See https://tools.ietf.org/html/rfc3987#page-13 +const URL_SEPARATOR_CHARS: [char; 10] = ['<', '>', '"', ' ', '{', '}', '|', '\\', '^', '`']; +const URL_DENY_END_CHARS: [char; 8] = ['.', ',', ';', ':', '?', '!', '/', '(']; +const URL_SCHEMES: [&str; 8] = [ + "http", "https", "mailto", "news", "file", "git", "ssh", "ftp", +]; + +// Parser for streaming inside-out detection of URLs. +pub struct UrlParser { + state: String, +} + +impl UrlParser { + pub fn new() -> Self { + UrlParser { + state: String::new(), + } + } + + /// Advance the parser one character to the left. + pub fn advance_left(&mut self, c: char) -> bool { + self.advance(c, 0) + } + + /// Advance the parser one character to the right. + pub fn advance_right(&mut self, c: char) -> bool { + self.advance(c, self.state.len()) + } + + /// Returns the URL if the parser has found any. + pub fn url(mut self) -> Option { + // Remove non-alphabetical characters before the scheme + // https://tools.ietf.org/html/rfc3986#section-3.1 + if let Some(index) = self.state.find("://") { + let iter = self + .state + .char_indices() + .rev() + .skip_while(|(byte_index, _)| *byte_index >= index); + for (byte_index, c) in iter { + match c { + 'a'...'z' | 'A'...'Z' => (), + _ => { + self.state = self.state.split_off(byte_index + c.len_utf8()); + break; + } + } + } + } + + // Remove non-matching parenthesis and brackets + let mut open_parens_count: isize = 0; + let mut open_bracks_count: isize = 0; + for (i, c) in self.state.chars().enumerate() { + match c { + '(' => open_parens_count += 1, + ')' if open_parens_count > 0 => open_parens_count -= 1, + '[' => open_bracks_count += 1, + ']' if open_bracks_count > 0 => open_bracks_count -= 1, + ')' | ']' => { + self.state.truncate(i); + break; + } + _ => (), + } + } + + // Track number of quotes + let mut num_quotes = self.state.chars().filter(|&c| c == '\'').count(); + + // Remove all characters which aren't allowed at the end of a URL + while !self.state.is_empty() + && (URL_DENY_END_CHARS.contains(&self.state.chars().last().unwrap()) + || (num_quotes % 2 != 0 && self.state.ends_with('\'')) + || self.state.ends_with("''") + || self.state.ends_with("()")) + { + if self.state.pop().unwrap() == '\'' { + num_quotes -= 1; + } + } + + // Check if string is valid url + match Url::parse(&self.state) { + Ok(url) => { + if URL_SCHEMES.contains(&url.scheme()) { + Some(self.state) + } else { + None + } + } + Err(_) => None, + } + } + + fn advance(&mut self, c: char, pos: usize) -> bool { + if URL_SEPARATOR_CHARS.contains(&c) + || (c >= '\u{00}' && c <= '\u{1F}') + || (c >= '\u{7F}' && c <= '\u{9F}') + { + true + } else { + self.state.insert(pos, c); + false + } + } +} + +#[cfg(test)] +mod tests { + use std::mem; + + use crate::grid::Grid; + use crate::index::{Column, Line, Point}; + use crate::message_bar::MessageBuffer; + use crate::term::cell::Cell; + use crate::term::{Search, SizeInfo, Term}; + + fn url_create_term(input: &str) -> Term { + 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 term = Term::new(&Default::default(), size, MessageBuffer::new()); + let mut grid: Grid = Grid::new(Line(1), Column(input.len()), 0, Cell::default()); + + for (i, c) in input.chars().enumerate() { + grid[Line(0)][Column(i)].c = c; + } + + mem::swap(term.grid_mut(), &mut grid); + + term + } + + fn url_test(input: &str, expected: &str, click_index: usize) { + let term = url_create_term(input); + + let url = term.url_search(Point::new(0, Column(click_index))); + + assert_eq!(url, Some(expected.into())); + } + + #[test] + fn url_skip_invalid() { + let term = url_create_term("no url here"); + let url = term.url_search(Point::new(0, Column(4))); + assert_eq!(url, None); + } + + #[test] + fn url_matching_chars() { + url_test( + "(https://example.org/test(ing))", + "https://example.org/test(ing)", + 5, + ); + url_test( + "https://example.org/test(ing)", + "https://example.org/test(ing)", + 5, + ); + url_test("((https://example.org))", "https://example.org", 5); + url_test(")https://example.org(", "https://example.org", 5); + url_test("https://example.org)", "https://example.org", 5); + url_test("https://example.org(", "https://example.org", 5); + url_test("(https://one.org/)(https://two.org/)", "https://one.org", 5); + + url_test( + "https://[2001:db8:a0b:12f0::1]:80", + "https://[2001:db8:a0b:12f0::1]:80", + 5, + ); + url_test( + "([(https://example.org/test(ing))])", + "https://example.org/test(ing)", + 5, + ); + url_test("https://example.org/]()", "https://example.org", 5); + url_test("[https://example.org]", "https://example.org", 5); + + url_test( + "'https://example.org/test'ing'''", + "https://example.org/test'ing'", + 5, + ); + url_test( + "https://example.org/test'ing'", + "https://example.org/test'ing'", + 5, + ); + url_test("'https://example.org'", "https://example.org", 5); + url_test("'https://example.org", "https://example.org", 5); + url_test("https://example.org'", "https://example.org", 5); + } + + #[test] + fn url_detect_end() { + url_test( + "https://example.org/test\u{00}ing", + "https://example.org/test", + 5, + ); + url_test( + "https://example.org/test\u{1F}ing", + "https://example.org/test", + 5, + ); + url_test( + "https://example.org/test\u{7F}ing", + "https://example.org/test", + 5, + ); + url_test( + "https://example.org/test\u{9F}ing", + "https://example.org/test", + 5, + ); + url_test( + "https://example.org/test\ting", + "https://example.org/test", + 5, + ); + url_test( + "https://example.org/test ing", + "https://example.org/test", + 5, + ); + } + + #[test] + fn url_remove_end_chars() { + url_test( + "https://example.org/test?ing", + "https://example.org/test?ing", + 5, + ); + url_test("https://example.org.,;:)'!/?", "https://example.org", 5); + url_test("https://example.org'.", "https://example.org", 5); + } + + #[test] + fn url_remove_start_chars() { + url_test("complicated:https://example.org", "https://example.org", 15); + url_test("test.https://example.org", "https://example.org", 10); + url_test(",https://example.org", "https://example.org", 5); + url_test("\u{2502}https://example.org", "https://example.org", 5); + } + + #[test] + fn url_unicode() { + url_test( + "https://xn--example-2b07f.org", + "https://xn--example-2b07f.org", + 5, + ); + url_test( + "https://example.org/\u{2008A}", + "https://example.org/\u{2008A}", + 5, + ); + url_test( + "https://example.org/\u{f17c}", + "https://example.org/\u{f17c}", + 5, + ); + url_test( + "https://üñîçøðé.com/ä", + "https://üñîçøðé.com/ä", + 5, + ); + } + + #[test] + fn url_schemes() { + url_test("mailto://example.org", "mailto://example.org", 5); + url_test("https://example.org", "https://example.org", 5); + url_test("http://example.org", "http://example.org", 5); + url_test("news://example.org", "news://example.org", 5); + url_test("file://example.org", "file://example.org", 5); + url_test("git://example.org", "git://example.org", 5); + url_test("ssh://example.org", "ssh://example.org", 5); + url_test("ftp://example.org", "ftp://example.org", 5); + } +} diff --git a/winpty/src/lib.rs b/winpty/src/lib.rs index 117ec16ddc..8604d085ea 100644 --- a/winpty/src/lib.rs +++ b/winpty/src/lib.rs @@ -1,4 +1,9 @@ -#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)] +#![deny( + clippy::all, + clippy::if_not_else, + clippy::enum_glob_use, + clippy::wrong_pub_self_convention +)] #[macro_use] #[cfg(windows)] diff --git a/winpty/src/windows.rs b/winpty/src/windows.rs index ada8bb0d15..4e439e4596 100644 --- a/winpty/src/windows.rs +++ b/winpty/src/windows.rs @@ -431,8 +431,14 @@ mod tests { ) .unwrap(); +<<<<<<< HEAD let processes = winpty.console_process_list(1000).expect("failed to get console process list"); +======= + let processes = winpty + .console_process_list(1000) + .expect("failed to get console process list"); +>>>>>>> Tried setting scale and ppem but it didn't change anything. // Check that each id is valid processes.iter().for_each(|id| { From 662fbe591d3230b8df7e93f32e6aeb56b0549b34 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Mon, 8 Jul 2019 17:40:33 -0700 Subject: [PATCH 09/84] Switch to harfbuzz_rs and change GlyphKey to use a char or glyph_index --- Cargo.lock | 21 +-- alacritty_terminal/src/cursor.rs | 8 +- alacritty_terminal/src/display.rs | 11 +- alacritty_terminal/src/renderer/mod.rs | 48 ++----- font/Cargo.toml | 5 +- font/src/ft/mod.rs | 186 ++++++++----------------- font/src/lib.rs | 84 +++++++---- 7 files changed, 141 insertions(+), 222 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f906c9d61d..72dcbd2997 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -653,7 +653,7 @@ dependencies = [ "euclid 0.19.9 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype-rs 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", - "harfbuzz 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "harfbuzz_rs 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", "servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -873,14 +873,6 @@ dependencies = [ "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "harfbuzz" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "harfbuzz-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "harfbuzz-sys" version = "0.3.2" @@ -894,6 +886,15 @@ dependencies = [ "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "harfbuzz_rs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "harfbuzz-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "http_req" version = "0.5.1" @@ -2641,8 +2642,8 @@ dependencies = [ "checksum glutin_gles2_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "89996c30857ae1b4de4b5189abf1ea822a20a9fe9e1c93e5e7b862ff0bdd5cdf" "checksum glutin_glx_sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1290a5ca5e46fcfa7f66f949cc9d9194b2cb6f2ed61892c8c2b82343631dba57" "checksum glutin_wgl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f801bbc91efc22dd1c4818a47814fc72bf74d024510451b119381579bfa39021" -"checksum harfbuzz 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46f7426266a5ece3e49eae6f48e602c0f8c39917354a847eac9c06437dcde8da" "checksum harfbuzz-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e1042ab0b3e7bc1ff64f7f5935778b644ff2194a1cae5ec52167127d3fd23961" +"checksum harfbuzz_rs 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "534c8e9b15d8db6e69654b07dad955f4132757194e7d2bba620d38cf08996088" "checksum http_req 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b7053e4a7b90e97e3c207f8293dc4a6e7f1d49156470b94f3685f1baa48f65a" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" diff --git a/alacritty_terminal/src/cursor.rs b/alacritty_terminal/src/cursor.rs index 93f2bd30e1..c7b5036972 100644 --- a/alacritty_terminal/src/cursor.rs +++ b/alacritty_terminal/src/cursor.rs @@ -62,7 +62,7 @@ pub fn get_underline_cursor_glyph(width: i32, line_width: i32) -> RasterizedGlyp 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 } + RasterizedGlyph { c: ' '.into(), top: line_width, left: 0, height: line_width, width, buf } } // Returns a custom beam cursor character @@ -71,7 +71,7 @@ pub fn get_beam_cursor_glyph(height: i32, line_width: i32) -> RasterizedGlyph { 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 } + RasterizedGlyph { c: ' '.into(), top: height, left: 0, height, width: line_width, buf } } // Returns a custom box cursor character @@ -93,7 +93,7 @@ pub fn get_box_cursor_glyph(height: i32, width: i32, line_width: i32) -> Rasteri } // Create a custom glyph with the rectangle data attached to it - RasterizedGlyph { c: ' ', top: height, left: 0, height, width, buf } + RasterizedGlyph { c: ' '.into(), top: height, left: 0, height, width, buf } } // Returns a custom block cursor character @@ -102,5 +102,5 @@ pub fn get_block_cursor_glyph(height: i32, width: i32) -> RasterizedGlyph { 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 } + RasterizedGlyph { c: ' '.into(), top: height, left: 0, height, width, buf } } diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index dc4f03746c..8601c58746 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -33,7 +33,7 @@ use crate::sync::FairMutex; use crate::term::color::Rgb; use crate::term::{RenderableCell, RenderableCellContent, SizeInfo, Term}; use crate::window::{self, Window}; -use font::{self, Rasterize}; +use font::{self, Rasterize, KeyType}; #[cfg(feature = "hb-ft")] use font::{HbFtExt, HbGlyph}; @@ -498,7 +498,7 @@ impl Display { } #[cfg(feature = "hb-ft")] - let (g_lines) = terminal.grid().num_lines(); + let g_lines = terminal.grid().num_lines(); // Clear when terminal mutex isn't held. Mesa for // some reason takes a long time to call glClear(). The driver descends @@ -659,18 +659,17 @@ impl Display { // Render each glyph, advancing based on the information provided. if let Some(glyphs) = glyphs { for g in glyphs.into_iter() { - // Hold reference to glyph from cache - let glyph = glyph_cache.get(g.glyph, &mut api); // Determine if the glyph is a special character match g.glyph.c { _ => { - //println!("Rendered {:?} at {:?}", g.glyph.c, rc.column); + // Hold reference to glyph from cache + let glyph = glyph_cache.get(g.glyph, &mut api); api.add_render_item(&rc, &glyph); rc.column = crate::index::Column(u_round_to( rc.column.0 as f32 * size_info.cell_width + g.x_advance, size_info.cell_width as f32, - )); + )) + 1; } } } diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index da94bf715a..02dfbdcfd5 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -22,7 +22,7 @@ use std::sync::mpsc; use std::time::Duration; use fnv::FnvHasher; -use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer}; +use font::{self, FontDesc, FontKey, KeyType, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer}; use glutin::dpi::PhysicalSize; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; @@ -193,7 +193,7 @@ impl GlyphCache { // 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 })?; + rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm'.into(), size: font.size })?; let metrics = rasterizer.metrics(regular, font.size)?; @@ -219,7 +219,7 @@ impl GlyphCache { fn load_glyphs_for_font(&mut self, font: FontKey, loader: &mut L) { let size = self.font_size; for i in 32u8..=128u8 { - self.get(GlyphKey { font_key: font, c: i as char, size }, loader); + self.get(GlyphKey { font_key: font, c: (i as char).into(), size }, loader); } } @@ -297,38 +297,6 @@ impl GlyphCache { }) } - #[cfg(feature = "hb-ft")] - pub fn get_raw<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L, glyph_i: u32) -> Glyph - where - L: LoadGlyph, - { - let glyph_offset = self.glyph_offset; - let rasterizer = &mut self.rasterizer; - let metrics = &self.metrics; - let fixed_glyph_key = GlyphKey { - c: std::char::from_u32(glyph_i).unwrap(), - ..glyph_key - }; - // Doesn't go through cache, making it suuuper slow. - /* - self.cache - .entry(fixed_glyph_key) - .or_insert_with(|| { - */ - let mut rasterized = rasterizer - .get_glyph_raw(glyph_key, glyph_i) - .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( &mut self, font: &config::Font, @@ -348,7 +316,7 @@ impl GlyphCache { let font = font.to_owned().with_size(size); let (regular, bold, italic) = Self::compute_font_keys(&font, &mut self.rasterizer)?; - self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?; + self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm'.into(), size: font.size })?; let metrics = self.rasterizer.metrics(regular, size)?; info!("Font size changed to {:?} with DPR of {}", font.size, dpr); @@ -376,7 +344,7 @@ impl GlyphCache { let regular_desc = GlyphCache::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal); let regular = rasterizer.load_font(®ular_desc, font.size)?; - rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?; + rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm'.into(), size: font.size })?; rasterizer.metrics(regular, font.size) } @@ -1051,7 +1019,7 @@ impl<'a> RenderApi<'a> { let glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, - c: glyph, + c: glyph.into(), }; // Add cell to batch @@ -1104,7 +1072,7 @@ impl<'a> RenderApi<'a> { chars[0] = ' '; } - let mut glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: chars[0] }; + let mut glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: chars[0].into() }; // Add cell to batch let glyph = glyph_cache.get(glyph_key, self); @@ -1112,7 +1080,7 @@ impl<'a> RenderApi<'a> { // Render zero-width characters for c in (&chars[1..]).iter().filter(|c| **c != ' ') { - glyph_key.c = *c as _; + glyph_key.c = KeyType::from(*c); let mut glyph = *glyph_cache.get(glyph_key, self); // The metrics of zero-width characters are based on rendering diff --git a/font/Cargo.toml b/font/Cargo.toml index 7f979d071f..c57181e873 100644 --- a/font/Cargo.toml +++ b/font/Cargo.toml @@ -11,7 +11,8 @@ libc = "0.2" foreign-types = "0.4" log = "0.4" -harfbuzz = { version = "0.3.0", optional = true } +#harfbuzz = { version = "0.3.0", optional = true } +harfbuzz_rs = { version = "1.0.0", optional = true } [target.'cfg(not(any(target_os = "macos", windows)))'.dependencies] servo-fontconfig = "0.4.0" @@ -28,4 +29,4 @@ dwrote = { version = "0.9.0" } [features] default = [] -hb-ft = ["harfbuzz"] \ No newline at end of file +hb-ft = ["harfbuzz_rs"] diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index d17745842c..ada390e2ac 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -24,9 +24,9 @@ use freetype::tt_os2::TrueTypeOS2Table; use freetype::{self, Library}; use libc::c_uint; -use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; +pub mod fc; -mod fc; +use super::{FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; struct FixedSize { pixelsize: f64, @@ -41,19 +41,7 @@ struct Face { non_scalable: Option, /// This is an option just in case hb_ft_create_font_referenced fails. #[cfg(feature = "hb-ft")] - hb_font: Option<*mut harfbuzz::sys::hb_font_t>, -} - -/// Required to drop the hb_font. -#[cfg(feature = "hb-ft")] -impl ::std::ops::Drop for Face { - fn drop(&mut self) { - if let Some(hb_font) = self.hb_font { - unsafe { - harfbuzz::sys::hb_font_destroy(hb_font); - } - } - } + hb_font: Option>>, } impl fmt::Debug for Face { @@ -163,52 +151,20 @@ impl ::Rasterize for FreeTypeRasterizer { } } -/// Utility function that converts a string into a tag. -#[cfg(feature = "hb-ft")] -fn hb_tag(f: &str) -> harfbuzz::sys::hb_tag_t { - unsafe { harfbuzz::sys::hb_tag_from_string(f.as_ptr() as *const i8, f.len() as i32) } -} - #[cfg(feature = "hb-ft")] impl ::HbFtExt for FreeTypeRasterizer { fn shape(&mut self, text: &str, font_key: FontKey, size: Size) -> Option> { - let size_metrics = self.faces[&font_key].ft_face.size_metrics(); - println!("{:?}", size_metrics); - self.faces[&font_key].hb_font.and_then(|hb_font| size_metrics.map(|s| (hb_font, s))).map(|(hb_font, size_metrics)| { - let mut buf = harfbuzz::Buffer::with(text); - buf.set_direction(harfbuzz::Direction::LTR); - buf.set_script(harfbuzz::sys::HB_SCRIPT_COMMON); - buf.set_language(harfbuzz::Language::from_string("en")); + use harfbuzz_rs::{shape, UnicodeBuffer}; + self.faces[&font_key].hb_font.as_ref().map(|hb_font| { + let buf = UnicodeBuffer::default().add_str(text).guess_segment_properties(); + // Shape - unsafe { - // ::std::ptr::null() == NULL (with type *const _) - harfbuzz::sys::hb_shape_full( - hb_font, - buf.as_ptr(), - std::ptr::null(), - 0, - harfbuzz::sys::hb_shape_list_shapers(), - ); - }; - // Get glyph information - let ginfo: &mut [harfbuzz::sys::hb_glyph_info_t] = unsafe { - let mut len = 0u32; - let res = - harfbuzz::sys::hb_buffer_get_glyph_infos(buf.as_ptr(), &mut len as *mut _); - std::slice::from_raw_parts_mut(res, len as usize) - }; - // Get glyph positions - let gpos: &mut [harfbuzz::sys::hb_glyph_position_t] = unsafe { - let mut len = 0u32; - let res = - harfbuzz::sys::hb_buffer_get_glyph_positions(buf.as_ptr(), &mut len as *mut _); - std::slice::from_raw_parts_mut(res, len as usize) - }; + let glyph_buffer = shape(&*hb_font, buf, &[]); // Combine into HbGlyph's - ginfo - .iter_mut() - .zip(gpos.iter_mut()) + glyph_buffer.get_glyph_infos() + .iter() + .zip(glyph_buffer.get_glyph_positions().iter()) .map(|(gi, gp)| { println!("{:?}", (&gi, &gp)); HbGlyph { @@ -218,12 +174,11 @@ impl ::HbFtExt for FreeTypeRasterizer { x_offset: (gp.x_offset as f32) / 64., y_offset: (gp.y_offset as f32) / 64., glyph: GlyphKey { - c: text.chars().nth(gi.cluster as usize).expect( - "Expected cluster index to point to char in text None was found.", - ), //::std::char::from_u32(gi.codepoint).expect("HB u32 is not a char!"), + c: KeyType::GlyphIndex(gi.codepoint), font_key, size, }, + codepoint: gi.codepoint, cluster: gi.cluster, } }) @@ -342,7 +297,7 @@ impl FreeTypeRasterizer { } trace!("Got font path={:?}", path); - let mut ft_face = self.library.new_face(&path, index)?; + let ft_face = self.library.new_face(&path, index)?; // Get available pixel sizes if font isn't scalable. let non_scalable = if pattern.scalable().next().unwrap_or(true) { @@ -357,26 +312,10 @@ impl FreeTypeRasterizer { // Construct harfbuzz font #[cfg(feature = "hb-ft")] let hb_font = { - let mut buf = Vec::new(); - use std::io::Read; - let mut f = ::std::fs::File::open(&path).unwrap(); - f.read_to_end(&mut buf).unwrap(); - let blob = harfbuzz::Blob::new_read_only(&buf); - let _hb_face = - unsafe { harfbuzz::sys::hb_face_create(blob.as_raw(), index as u32) }; - if _hb_face.is_null() { - None - } else { - let _hb_font = unsafe { harfbuzz::sys::hb_font_create(_hb_face) }; - if _hb_font.is_null() { - None - } else { - unsafe { - harfbuzz::sys::hb_ot_font_set_funcs(_hb_font); - } - Some(_hb_font) - } - } + use harfbuzz_rs::*; + let _hb_face = Face::from_file(&path, index as u32)?; + let _hb_font = Font::new(_hb_face); + Some(_hb_font.to_shared()) }; let face = Face { @@ -407,21 +346,27 @@ impl FreeTypeRasterizer { glyph_key: GlyphKey, have_recursed: bool, ) -> Result { - let c = glyph_key.c; - - let use_initial_face = if let Some(face) = self.faces.get(&glyph_key.font_key) { - let index = face.ft_face.get_char_index(c as usize); + match glyph_key.c { + KeyType::GlyphIndex(_) => { + // Already been through shaping so just use font_key + Ok(glyph_key.font_key) + }, + KeyType::Char(c) => { + let use_initial_face = if let Some(face) = self.faces.get(&glyph_key.font_key) { + let index = face.ft_face.get_char_index(c as usize); - index != 0 || have_recursed - } else { - false - }; + index != 0 || have_recursed + } else { + false + }; - if use_initial_face { - Ok(glyph_key.font_key) - } else { - let key = self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key); - Ok(key) + if use_initial_face { + Ok(glyph_key.font_key) + } else { + let key = self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key); + Ok(key) + } + } } } @@ -429,45 +374,15 @@ impl FreeTypeRasterizer { // Render a normal character if it's not a cursor let font_key = self.face_for_glyph(glyph_key, false)?; let face = &self.faces[&font_key]; - let index = face.ft_face.get_char_index(glyph_key.c as usize); - - let size = - face.non_scalable.as_ref().map(|v| v.pixelsize as f32).unwrap_or_else(|| { - glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72. - }); - - face.ft_face - .set_char_size(to_freetype_26_6(size), 0, 0, 0)?; - - unsafe { - let ft_lib = self.library.raw(); - freetype::ffi::FT_Library_SetLcdFilter(ft_lib, face.lcd_filter); - } - - face.ft_face.load_glyph(index as u32, face.load_flags)?; - let glyph = face.ft_face.glyph(); - glyph.render_glyph(face.render_mode)?; - - let (pixel_height, pixel_width, buf) = Self::normalize_buffer(&glyph.bitmap())?; + let index = match glyph_key.c { + KeyType::GlyphIndex(i) => i, + KeyType::Char(c) => face.ft_face.get_char_index(c as usize), + }; - Ok(RasterizedGlyph { - c: glyph_key.c, - top: glyph.bitmap_top(), - left: glyph.bitmap_left(), - width: pixel_width, - height: pixel_height, - buf, - }) + self.get_rendered_glyph_index(face, glyph_key, index) } - pub fn get_glyph_raw( - &mut self, - glyph_key: GlyphKey, - glyph_i: u32, - ) -> Result { - let font_key = self.face_for_glyph(glyph_key, false)?; - let face = &self.faces[&font_key]; - + fn get_rendered_glyph_index(&self, face: &crate::ft::Face, glyph_key: GlyphKey, index: u32) -> Result { let size = face .non_scalable .as_ref() @@ -482,7 +397,7 @@ impl FreeTypeRasterizer { freetype::ffi::FT_Library_SetLcdFilter(ft_lib, face.lcd_filter); } - face.ft_face.load_glyph(glyph_i as u32, face.load_flags)?; + face.ft_face.load_glyph(index as u32, face.load_flags)?; let glyph = face.ft_face.glyph(); glyph.render_glyph(face.render_mode)?; @@ -691,12 +606,16 @@ pub enum Error { /// Requested an operation with a FontKey that isn't known to the rasterizer FontNotLoaded, + + /// Error occurred during IO operation (loading harfbuzz font face, etc.) + IO(std::io::Error), } impl ::std::error::Error for Error { fn cause(&self) -> Option<&dyn (::std::error::Error)> { match *self { Error::FreeType(ref err) => Some(err), + Error::IO(ref err) => Some(err), _ => None, } } @@ -704,6 +623,7 @@ impl ::std::error::Error for Error { fn description(&self) -> &str { match *self { Error::FreeType(ref err) => err.description(), + Error::IO(ref err) => err.description(), Error::MissingFont(ref _desc) => "Couldn't find the requested font", Error::FontNotLoaded => "Tried to operate on font that hasn't been loaded", Error::MissingSizeMetrics => "Tried to get size metrics from a face without a size", @@ -715,6 +635,7 @@ impl ::std::fmt::Display for Error { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match *self { Error::FreeType(ref err) => err.fmt(f), + Error::IO(ref err) => err.fmt(f), Error::MissingFont(ref desc) => write!( f, "Couldn't find a font with {}\n\tPlease check the font config in your \ @@ -734,5 +655,10 @@ impl From for Error { Error::FreeType(val) } } +impl From for Error { + fn from(val: std::io::Error) -> Error { + Error::IO(val) + } +} -unsafe impl Send for FreeTypeRasterizer {} +unsafe impl Send for FreeTypeRasterizer {} \ No newline at end of file diff --git a/font/src/lib.rs b/font/src/lib.rs index 2b2042c5e4..6649f915a7 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -51,7 +51,7 @@ extern crate foreign_types; extern crate libc; #[cfg(feature = "hb-ft")] -extern crate harfbuzz; +extern crate harfbuzz_rs; #[cfg_attr(not(windows), macro_use)] extern crate log; @@ -146,39 +146,62 @@ impl FontKey { } } -#[derive(Debug, Copy, Clone, Eq)] -pub struct GlyphKey { - pub c: char, - pub font_key: FontKey, - pub size: Size, +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub enum KeyType { + Char(char), + GlyphIndex(u32), } - -impl Hash for GlyphKey { - fn hash(&self, state: &mut H) { - unsafe { - // This transmute is fine: - // - // - If GlyphKey ever becomes a different size, this will fail to compile - // - Result is being used for hashing and has no fields (it's a u64) - ::std::mem::transmute::(*self) - } - .hash(state); +impl From for KeyType { + fn from(val: char) -> Self { + KeyType::Char(val) } } - -impl PartialEq for GlyphKey { - fn eq(&self, other: &Self) -> bool { - unsafe { - // This transmute is fine: - // - // - If GlyphKey ever becomes a different size, this will fail to compile - // - Result is being used for equality checking and has no fields (it's a u64) - let other = ::std::mem::transmute::(*other); - ::std::mem::transmute::(*self).eq(&other) - } +impl<'a> From<&'a char> for KeyType { + fn from(val: &'a char) -> Self { + KeyType::Char(*val) + } +} +impl From for KeyType { + fn from(val: u32) -> Self { + KeyType::GlyphIndex(val) } } + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct GlyphKey { + pub c: KeyType, + pub font_key: FontKey, + pub size: Size, +} + +//impl Hash for GlyphKey { +// fn hash(&self, state: &mut H) { +// unsafe { +// // This transmute is fine: +// // +// // - If GlyphKey ever becomes a different size, this will fail to compile +// // - Result is being used for hashing and has no fields (it's a u64) +// ::std::mem::transmute::(*self) +// } +// .hash(state); +// state. +// } +//} +// +//impl PartialEq for GlyphKey { +// fn eq(&self, other: &Self) -> bool { +// unsafe { +// // This transmute is fine: +// // +// // - If GlyphKey ever becomes a different size, this will fail to compile +// // - Result is being used for equality checking and has no fields (it's a u64) +// let other = ::std::mem::transmute::(*other); +// ::std::mem::transmute::(*self).eq(&other) +// } +// } +//} + /// Font size stored as integer #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Size(i16); @@ -211,7 +234,7 @@ impl ::std::ops::Add for Size { #[derive(Clone)] pub struct RasterizedGlyph { - pub c: char, + pub c: KeyType, pub width: i32, pub height: i32, pub top: i32, @@ -221,7 +244,7 @@ pub struct RasterizedGlyph { impl Default for RasterizedGlyph { fn default() -> RasterizedGlyph { - RasterizedGlyph { c: ' ', width: 0, height: 0, top: 0, left: 0, buf: Vec::new() } + RasterizedGlyph { c: ' '.into(), width: 0, height: 0, top: 0, left: 0, buf: Vec::new() } } } @@ -295,6 +318,7 @@ pub struct HbGlyph { pub x_offset: f32, pub y_offset: f32, pub glyph: GlyphKey, + pub codepoint: u32, // Probably will never be used pub cluster: u32, } From e1e33c8dc9c628fc1842dcc2d672448426675045 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 9 Jul 2019 15:48:28 -0700 Subject: [PATCH 10/84] Looking into why whitespace is rendering is incorrectly --- alacritty_terminal/src/display.rs | 13 ++++++------- font/src/ft/mod.rs | 1 - 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 8601c58746..79b612ca07 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -24,16 +24,16 @@ use glutin::EventsLoop; use parking_lot::MutexGuard; use crate::config::{Config, StartupMode}; -use crate::index::{Line, Column}; +use crate::index::{Line}; use crate::message_bar::Message; use crate::meter::Meter; use crate::renderer::rects::{Rect, Rects}; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::sync::FairMutex; use crate::term::color::Rgb; -use crate::term::{RenderableCell, RenderableCellContent, SizeInfo, Term}; +use crate::term::{RenderableCell, SizeInfo, Term, RenderableCellContent}; use crate::window::{self, Window}; -use font::{self, Rasterize, KeyType}; +use font::{self, Rasterize}; #[cfg(feature = "hb-ft")] use font::{HbFtExt, HbGlyph}; @@ -555,16 +555,14 @@ impl Display { renderable_cells_rows.push(&grid_cells[row_start..row_end]); row_start = row_end; } - if !rcell.flags.contains(crate::term::cell::Flags::HIDDEN) { - row_end += 1; - } + row_end += 1; } renderable_cells_rows.push(&grid_cells[row_start..]); renderable_cells_rows } let _sampler = self.meter.sampler(); let renderable_cells_rows = create_renderable_cell_rows(&grid_cells, g_lines); - + /// Wrapper to allow comparing everything but column and chars struct CmpCell<'a>(&'a RenderableCell); impl<'a> PartialEq for CmpCell<'a> { @@ -631,6 +629,7 @@ impl Display { } else { glyph_cache.font_key }; + //println!("Shaped {:?} @ ({:?}, {:?})", text, rc.line, rc.column); ( rc, glyph_cache.rasterizer.shape( diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index ada390e2ac..e5979dc2b0 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -166,7 +166,6 @@ impl ::HbFtExt for FreeTypeRasterizer { .iter() .zip(glyph_buffer.get_glyph_positions().iter()) .map(|(gi, gp)| { - println!("{:?}", (&gi, &gp)); HbGlyph { /* ugh -_- you have to divide by 64?? */ x_advance: (gp.x_advance as f32) / 64., From a4084bc8d1b03da70dc0bdb5832ea94c2561fa40 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 9 Jul 2019 15:57:08 -0700 Subject: [PATCH 11/84] Place holder commit to check something in master. Just more debugging statements. --- alacritty_terminal/src/display.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 79b612ca07..dee9d9e31a 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -499,6 +499,8 @@ impl Display { #[cfg(feature = "hb-ft")] let g_lines = terminal.grid().num_lines(); + #[cfg(feature = "hb-ft")] + let g_cols = terminal.grid().num_cols(); // Clear when terminal mutex isn't held. Mesa for // some reason takes a long time to call glClear(). The driver descends @@ -560,6 +562,12 @@ impl Display { renderable_cells_rows.push(&grid_cells[row_start..]); renderable_cells_rows } + for rc in &grid_cells { + if rc.column.0 == 0 { + println!() + } + print!("{}", rc.chars[0]); + } let _sampler = self.meter.sampler(); let renderable_cells_rows = create_renderable_cell_rows(&grid_cells, g_lines); From 146f8247510300f7093586188a1089cd68337a66 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 10 Jul 2019 16:03:29 -0700 Subject: [PATCH 12/84] Replace sequence of maps with an iterator that produces text runs. --- alacritty_terminal/src/display.rs | 169 +++++++++++++++--------------- output | 0 2 files changed, 86 insertions(+), 83 deletions(-) create mode 100644 output diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index dee9d9e31a..5fed2fd2bb 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -562,18 +562,13 @@ impl Display { renderable_cells_rows.push(&grid_cells[row_start..]); renderable_cells_rows } - for rc in &grid_cells { - if rc.column.0 == 0 { - println!() - } - print!("{}", rc.chars[0]); - } + let _sampler = self.meter.sampler(); let renderable_cells_rows = create_renderable_cell_rows(&grid_cells, g_lines); - /// Wrapper to allow comparing everything but column and chars - struct CmpCell<'a>(&'a RenderableCell); - impl<'a> PartialEq for CmpCell<'a> { + /// Wrapper to compare cells and check they are in the same text run + struct ContiguousCell<'a>(&'a RenderableCell); + impl<'a> PartialEq for ContiguousCell<'a> { fn eq(&self, other: &Self) -> bool { let a = &self.0; let b = &other.0; @@ -582,72 +577,82 @@ impl Display { && a.bg == b.bg && a.bg_alpha == b.bg_alpha && a.flags == b.flags + && ((b.column.0 > 0 && a.column.0 == b.column.0 - 1) || (a.column.0 > 0 && a.column.0 - 1 == b.column.0)) } } - fn create_text_run_rows( - renderable_cells_rows: Vec<&[RenderableCell]>, - ) -> Vec> { - let mut text_run_rows = Vec::new(); - let mut row: Vec<(RenderableCell, String)> = Vec::new(); - let mut run = String::new(); - let mut rcell = None; - for r in renderable_cells_rows.into_iter() { - let mut ii = r.iter().peekable(); - while let Some(c) = ii.next() { - if rcell.is_none() { - rcell = Some(c); - } else if rcell - .map(CmpCell) - .map_or(false, |last_cell| last_cell != CmpCell(c)) - { - row.push((RenderableCell::clone(rcell.unwrap()), run.clone())); - run.clear(); - rcell = Some(c); - } - match c.inner { - RenderableCellContent::Cursor(_) => {}, - RenderableCellContent::Chars(chars) => { - run.push(chars[0]); - } - } - if ii.peek().is_none() { - row.push((RenderableCell::clone(c), run.clone())); + struct TextRunIter { + iter: I, + buffer: Vec, + latest: Option, + }; + impl TextRunIter { + fn new(iter: I) -> Self { + TextRunIter { + iter, + buffer: Vec::new(), + latest: None, + } + } + } + impl Iterator for TextRunIter + where + I: Iterator + { + type Item = Vec; + + fn next(&mut self) -> Option { + let mut output = None; + while let Some(rc) = self.iter.next() { + if self.latest.is_none() { + self.latest = Some(rc); + } else if self.latest.as_ref().map(ContiguousCell).map(|c| c != ContiguousCell(&rc)).unwrap_or(false) { + output = Some(self.buffer.drain(..).collect()); + // Update latest to new rc + self.latest = Some(RenderableCell::clone(&rc)); + // Reset buffer + self.buffer.push(rc); + break; + } else { + self.buffer.push(RenderableCell::clone(&rc)); + self.latest = Some(rc); } } - text_run_rows.push(row.clone()); - row.clear(); + output.or_else(|| { + if self.buffer.is_empty() { + None + } else { + // Save leftover buffer and empty it + Some(self.buffer.drain(..).collect()) + } + }) } - text_run_rows } - // Convert each row into a set of text runs - // (i.e. cells with the same display properties) - let text_run_rows = create_text_run_rows(renderable_cells_rows); + //// Convert each row into a set of text runs + //// (i.e. cells with the same display properties) // Shape each run of text. - let text_run_rows: Vec>)>> = text_run_rows - .into_iter() + let text_runs: Vec<(RenderableCell, Option>)> = + TextRunIter::new(grid_cells.into_iter()) .map(|row| { - row.into_iter() - .map(|(rc, run)| { - let key = if rc.flags.contains(crate::term::cell::Flags::BOLD) { - glyph_cache.bold_key - } else if rc.flags.contains(crate::term::cell::Flags::ITALIC) { - glyph_cache.italic_key - } else { - glyph_cache.font_key - }; - //println!("Shaped {:?} @ ({:?}, {:?})", text, rc.line, rc.column); - ( - rc, - glyph_cache.rasterizer.shape( - run.as_str(), - key, - glyph_cache.font_size, - ), - ) - }) - .collect() + let rc = &row[0]; + let run: String = row.iter().filter_map(|rc| match rc.inner { + RenderableCellContent::Cursor(_) => None, + RenderableCellContent::Chars(chars) => Some(chars[0]), + }).collect(); + let text = &run; + let key = if rc.flags.contains(crate::term::cell::Flags::BOLD) { + glyph_cache.bold_key + } else if rc.flags.contains(crate::term::cell::Flags::ITALIC) { + glyph_cache.italic_key + } else { + glyph_cache.font_key + }; + ( rc.clone(), + glyph_cache + .rasterizer + .shape(text, key, glyph_cache.font_size) + ) }) .collect(); @@ -659,25 +664,23 @@ impl Display { a / b } self.renderer.with_api(config, &size_info, |mut api| { - for row in text_run_rows.into_iter() { - for (mut rc, glyphs) in row.into_iter() { - // XXX: what does this do? (for text runs) - rects.update_lines(&size_info, &rc); - // Render each glyph, advancing based on the information provided. - if let Some(glyphs) = glyphs { - for g in glyphs.into_iter() { + for (mut rc, glyphs) in text_runs.into_iter() { + // Render each glyph, advancing based on the information provided. + if let Some(glyphs) = glyphs { + for g in glyphs.into_iter() { + // XXX: what does this do? (for text runs) + rects.update_lines(&size_info, &rc); + match g.glyph.c { // Determine if the glyph is a special character - match g.glyph.c { - _ => { - // Hold reference to glyph from cache - let glyph = glyph_cache.get(g.glyph, &mut api); - api.add_render_item(&rc, &glyph); - rc.column = crate::index::Column(u_round_to( - rc.column.0 as f32 * size_info.cell_width - + g.x_advance, - size_info.cell_width as f32, - )) + 1; - } + _ => { + // Hold reference to glyph from cache + let glyph = glyph_cache.get(g.glyph, &mut api); + api.add_render_item(&rc, &glyph); + rc.column = crate::index::Column(u_round_to( + rc.column.0 as f32 * size_info.cell_width + + g.x_advance, + size_info.cell_width as f32, + )) + 1; } } } diff --git a/output b/output new file mode 100644 index 0000000000..e69de29bb2 From a3f8d651808e1feb24ce695598c4b10e4b9b316c Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Fri, 12 Jul 2019 18:13:52 -0700 Subject: [PATCH 13/84] Created new TextRun struct to represent the idea of a run of text that all share the same renderablecell except for (column, chars) --- alacritty_terminal/src/display.rs | 199 +++++++++++++++---------- alacritty_terminal/src/renderer/mod.rs | 17 ++- font/src/ft/mod.rs | 24 ++- font/src/lib.rs | 1 - 4 files changed, 154 insertions(+), 87 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 5fed2fd2bb..a56a7531fa 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -24,7 +24,7 @@ use glutin::EventsLoop; use parking_lot::MutexGuard; use crate::config::{Config, StartupMode}; -use crate::index::{Line}; +use crate::index::{Line, Column}; use crate::message_bar::Message; use crate::meter::Meter; use crate::renderer::rects::{Rect, Rects}; @@ -33,7 +33,7 @@ use crate::sync::FairMutex; use crate::term::color::Rgb; use crate::term::{RenderableCell, SizeInfo, Term, RenderableCellContent}; use crate::window::{self, Window}; -use font::{self, Rasterize}; +use font::{self, KeyType, Rasterize}; #[cfg(feature = "hb-ft")] use font::{HbFtExt, HbGlyph}; @@ -540,32 +540,8 @@ impl Display { // Draw grid (HarfBuzz) #[cfg(feature = "hb-ft")] { - fn create_renderable_cell_rows( - grid_cells: &[RenderableCell], - g_lines: Line, - ) -> Vec<&[RenderableCell]> { - let mut renderable_cells_rows: Vec<&[RenderableCell]> = - Vec::with_capacity(g_lines.0); - let (mut row_start, mut row_end) = (0, 0); - let mut last_line = None; - for i in 0..grid_cells.len() { - let rcell = &grid_cells[i]; - if last_line.is_none() { - last_line = Some(rcell.line); - } else if last_line.unwrap() != grid_cells[i].line { - last_line = Some(rcell.line); - renderable_cells_rows.push(&grid_cells[row_start..row_end]); - row_start = row_end; - } - row_end += 1; - } - renderable_cells_rows.push(&grid_cells[row_start..]); - renderable_cells_rows - } - let _sampler = self.meter.sampler(); - let renderable_cells_rows = create_renderable_cell_rows(&grid_cells, g_lines); - + /// Wrapper to compare cells and check they are in the same text run struct ContiguousCell<'a>(&'a RenderableCell); impl<'a> PartialEq for ContiguousCell<'a> { @@ -577,53 +553,121 @@ impl Display { && a.bg == b.bg && a.bg_alpha == b.bg_alpha && a.flags == b.flags - && ((b.column.0 > 0 && a.column.0 == b.column.0 - 1) || (a.column.0 > 0 && a.column.0 - 1 == b.column.0)) } } - struct TextRunIter { + /// Checks two columns are adjacent + struct ContiguousColumn(Column); + impl PartialEq for ContiguousColumn { + fn eq(&self, other: &Self) -> bool { + let (a, b) = (self.0, other.0); + (a.0 == b.0 + 1) || (b.0 == a.0 + 1) + } + } + + // This shouldn't stay here + pub struct TextRun { + // By definition a run is on one line. + pub line: Line, + pub run: (Column, Column), + pub run_chars: Vec, + pub fg: Rgb, + pub bg: Rgb, + pub bg_alpha: f32, + pub flags: crate::term::cell::Flags, + } + impl TextRun { + fn cell_at(&self, col: Column) -> RenderableCell { + RenderableCell { + line: self.line, + column: col, + inner: [' '; crate::term::cell::MAX_ZEROWIDTH_CHARS + 1].into(), + fg: self.fg, + bg: self.bg, + bg_alpha: self.bg_alpha, + flags: self.flags, + } + } + } + struct TextRunIter { iter: I, - buffer: Vec, - latest: Option, + run_start: Option, + latest: Option, + buffer: Vec, }; - impl TextRunIter { + impl TextRunIter { fn new(iter: I) -> Self { TextRunIter { iter, - buffer: Vec::new(), latest: None, + run_start: None, + buffer: Vec::new(), } } } impl Iterator for TextRunIter where - I: Iterator + I: Iterator, { - type Item = Vec; + type Item = TextRun; fn next(&mut self) -> Option { let mut output = None; while let Some(rc) = self.iter.next() { - if self.latest.is_none() { - self.latest = Some(rc); - } else if self.latest.as_ref().map(ContiguousCell).map(|c| c != ContiguousCell(&rc)).unwrap_or(false) { - output = Some(self.buffer.drain(..).collect()); - // Update latest to new rc - self.latest = Some(RenderableCell::clone(&rc)); + if self.latest.is_none() || self.run_start.is_none() { + self.latest = Some(rc.column); + self.run_start = Some(rc); + } else if self + .run_start + .as_ref() + .map(|cell| ContiguousCell(cell) != ContiguousCell(&rc)) + .unwrap_or(false) + || self + .latest + .map(|col| ContiguousColumn(col) != ContiguousColumn(rc.column)) + .unwrap_or(false) + { + let (start, latest) = + (self.run_start.as_ref().unwrap(), self.latest.as_ref().unwrap()); + output = Some(TextRun { + line: start.line, + run: (start.column, *latest), + run_chars: self.buffer.drain(..).collect(), + fg: start.fg, + bg: start.bg, + bg_alpha: start.bg_alpha, + flags: start.flags, + }); // Reset buffer - self.buffer.push(rc); + self.buffer.push(rc.inner.clone()); + // Update latest to new rc + self.latest = Some(rc.column); + self.run_start = Some(rc); break; } else { - self.buffer.push(RenderableCell::clone(&rc)); - self.latest = Some(rc); + self.buffer.push(rc.inner); + self.latest = Some(rc.column); } } output.or_else(|| { if self.buffer.is_empty() { None } else { - // Save leftover buffer and empty it - Some(self.buffer.drain(..).collect()) + if let Some(start) = &self.run_start { + if let Some(latest) = &self.latest { + // Save leftover buffer and empty it + return Some(TextRun { + line: start.line, + run: (start.column, *latest), + run_chars: self.buffer.drain(..).collect(), + fg: start.fg, + bg: start.bg, + bg_alpha: start.bg_alpha, + flags: start.flags, + }); + } + } + return None; } }) } @@ -632,29 +676,36 @@ impl Display { //// Convert each row into a set of text runs //// (i.e. cells with the same display properties) // Shape each run of text. - let text_runs: Vec<(RenderableCell, Option>)> = + type HarfBuzzGlyphs = Vec<(Column, HbGlyph)>; + let text_runs: Vec<(TextRun, Option)> = TextRunIter::new(grid_cells.into_iter()) - .map(|row| { - let rc = &row[0]; - let run: String = row.iter().filter_map(|rc| match rc.inner { - RenderableCellContent::Cursor(_) => None, - RenderableCellContent::Chars(chars) => Some(chars[0]), - }).collect(); - let text = &run; - let key = if rc.flags.contains(crate::term::cell::Flags::BOLD) { - glyph_cache.bold_key - } else if rc.flags.contains(crate::term::cell::Flags::ITALIC) { - glyph_cache.italic_key - } else { - glyph_cache.font_key - }; - ( rc.clone(), - glyph_cache + .map(|text_run| { + let run: String = text_run.run_chars.iter().filter_map(|content| match content { + RenderableCellContent::Cursor(_) => None, + RenderableCellContent::Chars(chars) => Some(chars[0]), + }).collect(); + let text = &run; + let key = if text_run.flags.contains(crate::term::cell::Flags::BOLD) { + glyph_cache.bold_key + } else if text_run.flags.contains(crate::term::cell::Flags::ITALIC) { + glyph_cache.italic_key + } else { + glyph_cache.font_key + }; + let shape = glyph_cache .rasterizer .shape(text, key, glyph_cache.font_size) - ) - }) - .collect(); + .map(|glyphs| { + let (start_col, end_col) = text_run.run; + (start_col.0..=end_col.0) + .map(Column) + .zip(glyphs.into_iter()) + .collect() + }); + + (text_run, shape) + }) + .collect(); // Helper that rounds first arg to be a multiple of second arg. #[inline] @@ -664,29 +715,21 @@ impl Display { a / b } self.renderer.with_api(config, &size_info, |mut api| { - for (mut rc, glyphs) in text_runs.into_iter() { + for (text_run, glyphs) in text_runs.into_iter() { // Render each glyph, advancing based on the information provided. if let Some(glyphs) = glyphs { - for g in glyphs.into_iter() { + for (col, g) in glyphs.into_iter() { // XXX: what does this do? (for text runs) - rects.update_lines(&size_info, &rc); + rects.update_lines(&size_info, &text_run.cell_at(col)); match g.glyph.c { // Determine if the glyph is a special character _ => { // Hold reference to glyph from cache let glyph = glyph_cache.get(g.glyph, &mut api); - api.add_render_item(&rc, &glyph); - rc.column = crate::index::Column(u_round_to( - rc.column.0 as f32 * size_info.cell_width - + g.x_advance, - size_info.cell_width as f32, - )) + 1; + api.add_render_item(&text_run.cell_at(col), &glyph); } } } - - // XXX: what does this do? (for text runs) - rects.update_lines(&size_info, &rc); } } }); diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 02dfbdcfd5..a79a904fc0 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -22,7 +22,7 @@ use std::sync::mpsc; use std::time::Duration; use fnv::FnvHasher; -use font::{self, FontDesc, FontKey, KeyType, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer}; +use font::{self, FontDesc, FontKey, GlyphKey, KeyType, Rasterize, RasterizedGlyph, Rasterizer}; use glutin::dpi::PhysicalSize; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; @@ -1027,6 +1027,21 @@ impl<'a> RenderApi<'a> { self.add_render_item(cell, &glyph); } + pub fn render_text_run( + &mut self, + cell: RenderableCell, + run: &str, + glyph_cache: &mut GlyphCache, + ) { + let font_key = if cell.flags.contains(cell::Flags::BOLD) { + glyph_cache.bold_key + } else if cell.flags.contains(cell::Flags::ITALIC) { + glyph_cache.italic_key + } else { + glyph_cache.font_key + }; + } + pub fn render_cell(&mut self, cell: RenderableCell, glyph_cache: &mut GlyphCache) { let chars = match cell.inner { RenderableCellContent::Cursor(cursor_key) => { diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index e5979dc2b0..78a57d7723 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -26,7 +26,9 @@ use libc::c_uint; pub mod fc; -use super::{FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; +use super::{ + FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, Size, Slant, Style, Weight, +}; struct FixedSize { pixelsize: f64, @@ -156,13 +158,16 @@ impl ::HbFtExt for FreeTypeRasterizer { fn shape(&mut self, text: &str, font_key: FontKey, size: Size) -> Option> { use harfbuzz_rs::{shape, UnicodeBuffer}; self.faces[&font_key].hb_font.as_ref().map(|hb_font| { - let buf = UnicodeBuffer::default().add_str(text).guess_segment_properties(); - + let buf = UnicodeBuffer::default() + .add_str(text) + .guess_segment_properties(); + // Shape let glyph_buffer = shape(&*hb_font, buf, &[]); // Combine into HbGlyph's - glyph_buffer.get_glyph_infos() + glyph_buffer + .get_glyph_infos() .iter() .zip(glyph_buffer.get_glyph_positions().iter()) .map(|(gi, gp)| { @@ -349,7 +354,7 @@ impl FreeTypeRasterizer { KeyType::GlyphIndex(_) => { // Already been through shaping so just use font_key Ok(glyph_key.font_key) - }, + } KeyType::Char(c) => { let use_initial_face = if let Some(face) = self.faces.get(&glyph_key.font_key) { let index = face.ft_face.get_char_index(c as usize); @@ -381,7 +386,12 @@ impl FreeTypeRasterizer { self.get_rendered_glyph_index(face, glyph_key, index) } - fn get_rendered_glyph_index(&self, face: &crate::ft::Face, glyph_key: GlyphKey, index: u32) -> Result { + fn get_rendered_glyph_index( + &self, + face: &crate::ft::Face, + glyph_key: GlyphKey, + index: u32, + ) -> Result { let size = face .non_scalable .as_ref() @@ -660,4 +670,4 @@ impl From for Error { } } -unsafe impl Send for FreeTypeRasterizer {} \ No newline at end of file +unsafe impl Send for FreeTypeRasterizer {} diff --git a/font/src/lib.rs b/font/src/lib.rs index 6649f915a7..77c4be4ac4 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -167,7 +167,6 @@ impl From for KeyType { } } - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct GlyphKey { pub c: KeyType, From 87df12b6d6d998064031924fbddd14f675898b03 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Fri, 12 Jul 2019 19:13:44 -0700 Subject: [PATCH 14/84] Create render_text_run function on Renderer, this pushes call to hb shape one level deeper. Aim to move call into glyph_cache and expose through some API. --- alacritty_terminal/src/display.rs | 252 ++++++-------------- alacritty_terminal/src/renderer/mod.rs | 125 +++++++--- alacritty_terminal/src/term/mod.rs | 309 ++++++++++++++++++------- font/src/ft/mod.rs | 2 +- font/src/lib.rs | 2 +- 5 files changed, 392 insertions(+), 298 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index a56a7531fa..b7853c91c6 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -37,6 +37,9 @@ use font::{self, KeyType, Rasterize}; #[cfg(feature = "hb-ft")] use font::{HbFtExt, HbGlyph}; +#[cfg(feature = "hb-ft")] +use crate::term::text_run::{TextRun, TextRunIter}; + #[derive(Debug)] pub enum Error { /// Error with window management @@ -542,170 +545,47 @@ impl Display { { let _sampler = self.meter.sampler(); - /// Wrapper to compare cells and check they are in the same text run - struct ContiguousCell<'a>(&'a RenderableCell); - impl<'a> PartialEq for ContiguousCell<'a> { - fn eq(&self, other: &Self) -> bool { - let a = &self.0; - let b = &other.0; - a.line == b.line - && a.fg == b.fg - && a.bg == b.bg - && a.bg_alpha == b.bg_alpha - && a.flags == b.flags - } - } - - /// Checks two columns are adjacent - struct ContiguousColumn(Column); - impl PartialEq for ContiguousColumn { - fn eq(&self, other: &Self) -> bool { - let (a, b) = (self.0, other.0); - (a.0 == b.0 + 1) || (b.0 == a.0 + 1) - } - } - - // This shouldn't stay here - pub struct TextRun { - // By definition a run is on one line. - pub line: Line, - pub run: (Column, Column), - pub run_chars: Vec, - pub fg: Rgb, - pub bg: Rgb, - pub bg_alpha: f32, - pub flags: crate::term::cell::Flags, - } - impl TextRun { - fn cell_at(&self, col: Column) -> RenderableCell { - RenderableCell { - line: self.line, - column: col, - inner: [' '; crate::term::cell::MAX_ZEROWIDTH_CHARS + 1].into(), - fg: self.fg, - bg: self.bg, - bg_alpha: self.bg_alpha, - flags: self.flags, - } - } - } - struct TextRunIter { - iter: I, - run_start: Option, - latest: Option, - buffer: Vec, - }; - impl TextRunIter { - fn new(iter: I) -> Self { - TextRunIter { - iter, - latest: None, - run_start: None, - buffer: Vec::new(), - } - } - } - impl Iterator for TextRunIter - where - I: Iterator, - { - type Item = TextRun; - - fn next(&mut self) -> Option { - let mut output = None; - while let Some(rc) = self.iter.next() { - if self.latest.is_none() || self.run_start.is_none() { - self.latest = Some(rc.column); - self.run_start = Some(rc); - } else if self - .run_start - .as_ref() - .map(|cell| ContiguousCell(cell) != ContiguousCell(&rc)) - .unwrap_or(false) - || self - .latest - .map(|col| ContiguousColumn(col) != ContiguousColumn(rc.column)) - .unwrap_or(false) - { - let (start, latest) = - (self.run_start.as_ref().unwrap(), self.latest.as_ref().unwrap()); - output = Some(TextRun { - line: start.line, - run: (start.column, *latest), - run_chars: self.buffer.drain(..).collect(), - fg: start.fg, - bg: start.bg, - bg_alpha: start.bg_alpha, - flags: start.flags, - }); - // Reset buffer - self.buffer.push(rc.inner.clone()); - // Update latest to new rc - self.latest = Some(rc.column); - self.run_start = Some(rc); - break; - } else { - self.buffer.push(rc.inner); - self.latest = Some(rc.column); - } - } - output.or_else(|| { - if self.buffer.is_empty() { - None - } else { - if let Some(start) = &self.run_start { - if let Some(latest) = &self.latest { - // Save leftover buffer and empty it - return Some(TextRun { - line: start.line, - run: (start.column, *latest), - run_chars: self.buffer.drain(..).collect(), - fg: start.fg, - bg: start.bg, - bg_alpha: start.bg_alpha, - flags: start.flags, - }); - } - } - return None; - } - }) - } - } - //// Convert each row into a set of text runs //// (i.e. cells with the same display properties) // Shape each run of text. - type HarfBuzzGlyphs = Vec<(Column, HbGlyph)>; - let text_runs: Vec<(TextRun, Option)> = - TextRunIter::new(grid_cells.into_iter()) - .map(|text_run| { - let run: String = text_run.run_chars.iter().filter_map(|content| match content { - RenderableCellContent::Cursor(_) => None, - RenderableCellContent::Chars(chars) => Some(chars[0]), - }).collect(); - let text = &run; - let key = if text_run.flags.contains(crate::term::cell::Flags::BOLD) { - glyph_cache.bold_key - } else if text_run.flags.contains(crate::term::cell::Flags::ITALIC) { - glyph_cache.italic_key - } else { - glyph_cache.font_key - }; - let shape = glyph_cache - .rasterizer - .shape(text, key, glyph_cache.font_size) - .map(|glyphs| { - let (start_col, end_col) = text_run.run; - (start_col.0..=end_col.0) - .map(Column) - .zip(glyphs.into_iter()) - .collect() - }); - - (text_run, shape) - }) - .collect(); + //type HarfBuzzGlyphs = Vec<(Column, HbGlyph)>; + //let text_runs: Vec<(TextRun, Option)> = + // TextRunIter::new(grid_cells.into_iter()) + // .map(|text_run| { + // use font::{BEAM_CURSOR_CHAR, BOX_CURSOR_CHAR, UNDERLINE_CURSOR_CHAR}; + // let run: String = + // text_run.run_chars.iter().map(|chars| chars[0]).collect(); + // let ends_with_special = run.ends_with(UNDERLINE_CURSOR_CHAR) + // || run.ends_with(BEAM_CURSOR_CHAR) + // || run.ends_with(BOX_CURSOR_CHAR); + // let text = if ends_with_special { + // // Leave off last character + // let i = run.char_indices().last().unwrap().0; + // &run[..i] + // } else { + // &run + // }; + // let key = if text_run.flags.contains(crate::term::cell::Flags::BOLD) { + // glyph_cache.bold_key + // } else if text_run.flags.contains(crate::term::cell::Flags::ITALIC) { + // glyph_cache.italic_key + // } else { + // glyph_cache.font_key + // }; + // let shape = glyph_cache + // .rasterizer + // .shape(text, key, glyph_cache.font_size) + // .map(|glyphs| { + // let (start_col, end_col) = text_run.run; + // (start_col.0..=end_col.0) + // .map(Column) + // .zip(glyphs.into_iter()) + // .collect() + // }); + + // (text_run, shape) + // }) + // .collect(); // Helper that rounds first arg to be a multiple of second arg. #[inline] @@ -715,23 +595,41 @@ impl Display { a / b } self.renderer.with_api(config, &size_info, |mut api| { - for (text_run, glyphs) in text_runs.into_iter() { - // Render each glyph, advancing based on the information provided. - if let Some(glyphs) = glyphs { - for (col, g) in glyphs.into_iter() { - // XXX: what does this do? (for text runs) - rects.update_lines(&size_info, &text_run.cell_at(col)); - match g.glyph.c { - // Determine if the glyph is a special character - _ => { - // Hold reference to glyph from cache - let glyph = glyph_cache.get(g.glyph, &mut api); - api.add_render_item(&text_run.cell_at(col), &glyph); - } - } - } - } + for text_run in TextRunIter::new(grid_cells.into_iter()) { + api.render_text_run(text_run, glyph_cache); } + //for (text_run, glyphs) in text_runs.into_iter() { + // // Render each glyph, advancing based on the information provided. + // if let Some(glyphs) = glyphs { + // for (col, g) in glyphs.into_iter() { + // // XXX: what does this do? (for text runs) + // //rects.update_lines(&rc); + + // match g.glyph_key.c { + // // Determine if the glyph is a special character + // KeyType::Char(c @ font::UNDERLINE_CURSOR_CHAR) + // | KeyType::Char(c @ font::BEAM_CURSOR_CHAR) + // | KeyType::Char(c @ font::BOX_CURSOR_CHAR) => { + // api.render_glyph_at_position( + // &text_run.cell_at(col), + // glyph_cache, + // c, + // ); + // } + // _ => { + // // Hold reference to glyph from cache + // let glyph = glyph_cache.get(g.glyph_key, &mut api); + // api.add_render_item(&text_run.cell_at(col), &glyph); + // //rc.column = crate::index::Column(u_round_to( + // // rc.column.0 as f32 * size_info.cell_width + // // + g.x_advance, + // // size_info.cell_width as f32, + // //)) + 1; + // } + // } + // } + // } + //} }); } diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index a79a904fc0..49fbdfc2cf 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -33,6 +33,8 @@ use crate::gl::types::*; use crate::index::{Column, Line}; use crate::renderer::rects::{Rect, Rects}; use crate::term::color::Rgb; +#[cfg(feature = "hb-ft")] +use crate::term::text_run::{TextRun, TextRunContent}; use crate::term::{self, cell, RenderableCell, RenderableCellContent}; pub mod rects; @@ -92,7 +94,7 @@ impl ::std::fmt::Display for Error { match *self { Error::ShaderCreation(ref err) => { write!(f, "There was an error initializing the shaders: {}", err) - }, + } } } } @@ -316,7 +318,11 @@ impl GlyphCache { let font = font.to_owned().with_size(size); let (regular, bold, italic) = Self::compute_font_keys(&font, &mut self.rasterizer)?; - self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm'.into(), size: font.size })?; + self.rasterizer.get_glyph(GlyphKey { + font_key: regular, + c: 'm'.into(), + size: font.size, + })?; let metrics = self.rasterizer.metrics(regular, size)?; info!("Font size changed to {:?} with DPR of {}", font.size, dpr); @@ -645,8 +651,8 @@ impl QuadRenderer { | DebouncedEvent::Write(_) | DebouncedEvent::Chmod(_) => { msg_tx.send(Msg::ShaderReload).expect("msg send ok"); - }, - _ => {}, + } + _ => {} } } }); @@ -731,12 +737,7 @@ impl QuadRenderer { 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, - ); + gl::Viewport(padding_x, padding_y, width - 2 * padding_x, height - 2 * padding_y); // Disable program gl::UseProgram(0); @@ -816,11 +817,11 @@ impl QuadRenderer { info!("... successfully reloaded shaders"); (program, rect_program) - }, + } (Err(err), _) | (_, Err(err)) => { error!("{}", err); return; - }, + } }; self.active_tex = 0; @@ -837,12 +838,7 @@ impl QuadRenderer { let height = height as i32; let padding_x = padding_x as i32; let padding_y = padding_y as i32; - gl::Viewport( - padding_x, - padding_y, - width - 2 * padding_x, - height - 2 * padding_y, - ); + gl::Viewport(padding_x, padding_y, width - 2 * padding_x, height - 2 * padding_y); // update projection gl::UseProgram(self.program.id); @@ -1016,29 +1012,82 @@ impl<'a> RenderApi<'a> { glyph_cache.font_key }; - let glyph_key = GlyphKey { - font_key, - size: glyph_cache.font_size, - c: glyph.into(), - }; + let glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: glyph.into() }; // Add cell to batch let glyph = glyph_cache.get(glyph_key, self); self.add_render_item(cell, &glyph); } - pub fn render_text_run( - &mut self, - cell: RenderableCell, - run: &str, - glyph_cache: &mut GlyphCache, - ) { - let font_key = if cell.flags.contains(cell::Flags::BOLD) { - glyph_cache.bold_key - } else if cell.flags.contains(cell::Flags::ITALIC) { - glyph_cache.italic_key - } else { - glyph_cache.font_key + #[cfg(feature = "hb-ft")] + pub fn render_text_run(&mut self, text_run: TextRun, glyph_cache: &mut GlyphCache) { + match &text_run.run_chars { + TextRunContent::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(|| { + let offset_x = self.config.font.offset.x; + let offset_y = self.config.font.offset.y; + + self.load_glyph(&get_cursor_glyph( + cursor_key.style, + metrics, + offset_x, + offset_y, + cursor_key.is_wide, + )) + }); + self.add_render_item(&text_run.start_cell(), &glyph); + } + TextRunContent::CharRun(chars) => { + let font_key = if text_run.flags.contains(cell::Flags::BOLD) { + glyph_cache.bold_key + } else if text_run.flags.contains(cell::Flags::ITALIC) { + glyph_cache.italic_key + } else { + glyph_cache.font_key + }; + + let hidden = text_run.flags.contains(cell::Flags::HIDDEN); + let run: String = if hidden { + " ".repeat(chars.len()) + } else { + chars + .iter() + .map(|chars| if chars[0] == '\t' { ' ' } else { chars[0] }) + .collect() + }; + + use font::HbFtExt; + let glyphs = glyph_cache + .rasterizer + .shape(&run, font_key, glyph_cache.font_size) + .expect("harfbuzz font to be present"); + for (col, g) in text_run.col_iter().zip(glyphs.into_iter()) { + let cell = text_run.cell_at(col); + let glyph = glyph_cache.get(g.glyph_key, self); + self.add_render_item(&text_run.cell_at(col), &glyph); + } + + for (cell, chars) in text_run.cell_iter().zip(chars.iter()) { + let slice: &[char] = &chars[1..]; + for c in slice.into_iter().filter(|c| **c != ' ') { + let glyph_key = + GlyphKey { font_key, size: glyph_cache.font_size, c: c.into() }; + let average_advance = glyph_cache.metrics.average_advance as f32; + 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 += average_advance; + + self.add_render_item(&cell, &glyph); + } + } + } }; } @@ -1061,7 +1110,7 @@ impl<'a> RenderApi<'a> { }); self.add_render_item(&cell, &glyph); return; - }, + } RenderableCellContent::Chars(chars) => chars, }; @@ -1132,7 +1181,7 @@ fn load_glyph( atlas.push(new); } load_glyph(active_tex, atlas, current_atlas, rasterized) - }, + } Err(AtlasInsertError::GlyphTooLarge) => Glyph { tex_id: atlas[*current_atlas].id, top: 0.0, @@ -1482,7 +1531,7 @@ impl ::std::fmt::Display for ShaderCreationError { ShaderCreationError::Io(ref err) => write!(f, "Couldn't read shader: {}", err), ShaderCreationError::Compile(ref path, ref log) => { write!(f, "Failed compiling shader at {}: {}", path.display(), log) - }, + } ShaderCreationError::Link(ref log) => write!(f, "Failed linking shader: {}", log), } } diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index ae6e2b0960..3588e952dc 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -248,16 +248,16 @@ impl<'a> RenderableCellsIter<'a> { let locations = match (start_line, end_line) { (ViewportPosition::Visible(start_line), ViewportPosition::Visible(end_line)) => { Some((start_line, span.start.col, end_line, span.end.col)) - }, + } (ViewportPosition::Visible(start_line), ViewportPosition::Above) => { Some((start_line, span.start.col, Line(0), limit_end)) - }, + } (ViewportPosition::Below, ViewportPosition::Visible(end_line)) => { Some((grid.num_lines(), limit_start, end_line, span.end.col)) - }, + } (ViewportPosition::Below, ViewportPosition::Above) => { Some((grid.num_lines(), limit_start, Line(0), limit_end)) - }, + } _ => None, }; @@ -368,6 +368,13 @@ impl RenderableCell { } } + fn is_cursor(&self) -> bool { + match &self.inner { + RenderableCellContent::Cursor(_) => true, + _ => false, + } + } + fn compute_fg_rgb(config: &Config, colors: &color::List, fg: Color, flags: cell::Flags) -> Rgb { match fg { Color::Spec(rgb) => rgb, @@ -379,7 +386,7 @@ impl RenderableCell { && config.colors.primary.bright_foreground.is_none() => { colors[NamedColor::DimForeground] - }, + } // Draw bold text in bright colors *and* contains bold flag. (true, cell::Flags::BOLD) => colors[ansi.to_bright()], // Cell is marked as dim and not bold @@ -401,7 +408,7 @@ impl RenderableCell { }; colors[idx] - }, + } } } @@ -424,6 +431,189 @@ impl RenderableCell { } } +#[cfg(feature = "hb-ft")] +pub mod text_run { + use super::{ + cell::{Flags, MAX_ZEROWIDTH_CHARS}, + color::Rgb, + CursorKey, RenderableCell, RenderableCellContent, + }; + use crate::index::{Column, Line}; + + /// Compare cells and check they are in the same text run + fn is_contiguous_cell(a: &RenderableCell, b: &RenderableCell) -> bool { + a.line == b.line + && a.fg == b.fg + && a.bg == b.bg + && a.bg_alpha == b.bg_alpha + && a.flags == b.flags + } + + /// Checks two columns are adjacent + fn is_contiguous_col(a: &Column, b: &Column) -> bool { + a.0 + 1 == b.0 || b.0 + 1 == a.0 + } + + pub enum TextRunContent { + Cursor(CursorKey), + CharRun(Vec<[char; MAX_ZEROWIDTH_CHARS + 1]>), + } + + pub struct TextRun { + // By definition a run is on one line. + pub line: Line, + pub run: (Column, Column), + pub run_chars: TextRunContent, + pub fg: Rgb, + pub bg: Rgb, + pub bg_alpha: f32, + pub flags: Flags, + } + impl TextRun { + pub(crate) fn from_iter_state( + start: RenderableCell, + latest: Column, + buffer: Vec<[char; MAX_ZEROWIDTH_CHARS + 1]>, + ) -> Self { + TextRun { + line: start.line, + run: (start.column, latest), + run_chars: TextRunContent::CharRun(buffer), + fg: start.fg, + bg: start.bg, + bg_alpha: start.bg_alpha, + flags: start.flags, + } + } + pub(crate) fn from_cursor_rc(start: RenderableCell, cursor: CursorKey) -> Self { + TextRun { + line: start.line, + run: (start.column, start.column), + run_chars: TextRunContent::Cursor(cursor), + fg: start.fg, + bg: start.bg, + bg_alpha: start.bg_alpha, + flags: start.flags, + } + } + /// Holdover method while converting from rendering Cells to TextRuns + pub fn cell_at(&self, col: Column) -> RenderableCell { + RenderableCell { + line: self.line, + column: col, + inner: [' '; crate::term::cell::MAX_ZEROWIDTH_CHARS + 1].into(), + fg: self.fg, + bg: self.bg, + bg_alpha: self.bg_alpha, + flags: self.flags, + } + } + + pub fn start_cell(&self) -> RenderableCell { + self.cell_at(self.run.0) + } + + /// Returns iterator over range of columns [run.0, run.1] + pub fn col_iter(&self) -> impl Iterator { + let (start, end) = self.run; + // unpacking is neccessary while Step trait is nightly + // hopefully this compiles away. + (start.0..=end.0).map(Column) + } + + /// Iterates over each RenderableCell in column range [run.0, run.1] + pub fn cell_iter<'a>(&'a self) -> impl Iterator + 'a { + self.col_iter().map(move |col| self.cell_at(col)) + } + } + pub struct TextRunIter { + iter: I, + run_start: Option, + latest: Option, + buffer: Vec<[char; MAX_ZEROWIDTH_CHARS + 1]>, + } + impl TextRunIter { + pub fn new(iter: I) -> Self { + TextRunIter { iter, latest: None, run_start: None, buffer: Vec::new() } + } + + /// Check if current run ends with incoming RenderableCell + pub fn is_run_break(&self, rc: &RenderableCell) -> bool { + let is_cell_break = + self.run_start.as_ref().map(|cell| !is_contiguous_cell(cell, rc)).unwrap_or(false); + let is_col_break = self + .latest + .as_ref() + .map(|col| !is_contiguous_col(col, &rc.column)) + .unwrap_or(false); + is_cell_break || is_col_break + } + + pub fn buffer_content(&mut self, inner: RenderableCellContent) { + // Add to buffer only if the next rc is a Char (not a cursor) + if let RenderableCellContent::Chars(chars) = inner { + self.buffer.push(chars); + } + } + } + + fn opt_pair(a: Option, b: Option) -> Option<(A, B)> { + a.and_then(move |a_val| b.map(move |b_val| (a_val, b_val))) + } + + impl Iterator for TextRunIter + where + I: Iterator, + { + type Item = TextRun; + + fn next(&mut self) -> Option { + let mut output = None; + while let Some(rc) = self.iter.next() { + if self.latest.is_none() || self.run_start.is_none() { + self.latest.replace(rc.column); + self.run_start.replace(rc); + continue; + } else if self.run_start.as_ref().map(|rc| rc.is_cursor()).unwrap_or(false) { + self.buffer_content(rc.inner.clone()); + self.latest = Some(rc.column); + let start = self.run_start.replace(rc); + output = start.map(|rc| match rc.inner { + RenderableCellContent::Chars(_) => { + panic!("Found chars inner content for Cursor RenderableCell") + } + RenderableCellContent::Cursor(cursor) => { + TextRun::from_cursor_rc(rc, cursor) + } + }); + break; + } else if self.is_run_break(&rc) || rc.is_cursor() { + let prev_buffer = self.buffer.drain(..).collect(); + let latest = self.latest.replace(rc.column); + self.buffer_content(rc.inner.clone()); + let start = self.run_start.replace(rc); + output = opt_pair(start, latest).map(|(start, latest)| { + TextRun::from_iter_state(start, latest, prev_buffer) + }); + break; + } else { + self.latest = Some(rc.column); + self.buffer_content(rc.inner.clone()); + } + } + output.or_else(|| { + if self.buffer.is_empty() { + None + } else { + opt_pair(self.run_start.take(), self.latest.take()).map(|(start, latest)| + // Save leftover buffer and empty it + TextRun::from_iter_state(start, latest, self.buffer.drain(..).collect())) + } + }) + } + } +} + impl<'a> Iterator for RenderableCellsIter<'a> { type Item = RenderableCell; @@ -662,7 +852,7 @@ impl VisualBell { self.start_time = None; } false - }, + } None => true, } } @@ -712,7 +902,7 @@ impl VisualBell { VisualBellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time), VisualBellAnimation::EaseOutCubic => { cubic_bezier(0.215, 0.61, 0.355, 1.0, time) - }, + } VisualBellAnimation::EaseOutQuart => cubic_bezier(0.165, 0.84, 0.44, 1.0, time), VisualBellAnimation::EaseOutQuint => cubic_bezier(0.23, 1.0, 0.32, 1.0, time), VisualBellAnimation::EaseOutExpo => cubic_bezier(0.19, 1.0, 0.22, 1.0, time), @@ -723,7 +913,7 @@ impl VisualBell { // Since we want the `intensity` of the VisualBell to decay over // `time`, we subtract the `inverse_intensity` from 1.0. 1.0 - inverse_intensity - }, + } } } @@ -925,12 +1115,7 @@ impl Term { let history_size = config.scrolling.history() as usize; let grid = Grid::new(num_lines, num_cols, history_size, Cell::default()); - let alt = Grid::new( - num_lines, - num_cols, - 0, /* scroll history */ - Cell::default(), - ); + let alt = Grid::new(num_lines, num_cols, 0 /* scroll history */, Cell::default()); let tabspaces = config.tabspaces(); let tabs = TabStops::new(grid.num_cols(), tabspaces); @@ -1093,7 +1278,7 @@ impl Term { // Selection within single line 0 => { res.append(false, &self.grid, &self.tabs, start.line, start.col..end.col); - }, + } // Selection ends on line following start 1 => { @@ -1102,7 +1287,7 @@ impl Term { // Starting line res.append(false, &self.grid, &self.tabs, start.line, limit_start..start.col); - }, + } // Multi line selection _ => { @@ -1116,7 +1301,7 @@ impl Term { // Starting line res.append(false, &self.grid, &self.tabs, start.line, limit_start..start.col); - }, + } } Some(res) @@ -1220,15 +1405,13 @@ impl Term { // Scroll up to keep cursor in terminal if self.cursor.point.line >= num_lines { let lines = self.cursor.point.line - num_lines + 1; - self.grid - .scroll_up(&(Line(0)..old_lines), lines, &self.cursor.template); + self.grid.scroll_up(&(Line(0)..old_lines), lines, &self.cursor.template); } // Scroll up alt grid as well if self.cursor_save_alt.point.line >= num_lines { let lines = self.cursor_save_alt.point.line - num_lines + 1; - self.alt_grid - .scroll_up(&(Line(0)..old_lines), lines, &self.cursor_save_alt.template); + self.alt_grid.scroll_up(&(Line(0)..old_lines), lines, &self.cursor_save_alt.template); } // Move prompt down when growing if scrollback lines are available @@ -1242,10 +1425,7 @@ impl Term { } } - debug!( - "New num_cols is {} and num_lines is {}", - num_cols, num_lines - ); + debug!("New num_cols is {} and num_lines is {}", num_cols, num_lines); // Resize grids to new size let is_alt = self.mode.contains(TermMode::ALT_SCREEN); @@ -1300,11 +1480,7 @@ impl Term { /// Expects origin to be in scroll range. #[inline] fn scroll_down_relative(&mut self, origin: Line, mut lines: Line) { - trace!( - "Scrolling down relative: origin={}, lines={}", - origin, - lines - ); + trace!("Scrolling down relative: origin={}, lines={}", origin, lines); lines = min(lines, self.scroll_region.end - self.scroll_region.start); lines = min(lines, self.scroll_region.end - origin); @@ -1627,7 +1803,7 @@ impl ansi::Handler for Term { let pos = self.cursor.point; let response = format!("\x1b[{};{}R", pos.line + 1, pos.col + 1); let _ = writer.write_all(response.as_bytes()); - }, + } _ => debug!("unknown device status query: {}", arg), }; } @@ -1787,11 +1963,7 @@ impl ansi::Handler for Term { #[inline] fn erase_chars(&mut self, count: Column) { - trace!( - "Erasing chars: count={}, col={}", - count, - self.cursor.point.col - ); + trace!("Erasing chars: count={}, col={}", count, self.cursor.point.col); let start = self.cursor.point.col; let end = min(start + count, self.grid.num_cols()); @@ -1967,7 +2139,7 @@ impl ansi::Handler for Term { for cell in &mut self.grid[self.cursor.point.line][..end] { cell.reset(&template); } - }, + } ansi::ClearMode::Saved => self.grid.clear_history(), } } @@ -1982,7 +2154,7 @@ impl ansi::Handler for Term { } ansi::TabulationClearMode::All => { self.tabs.clear_all(); - }, + } } } @@ -2040,7 +2212,7 @@ impl ansi::Handler for Term { Attr::Dim => self.cursor.template.flags.insert(cell::Flags::DIM), Attr::CancelBoldDim => { self.cursor.template.flags.remove(cell::Flags::BOLD | cell::Flags::DIM) - }, + } Attr::Italic => self.cursor.template.flags.insert(cell::Flags::ITALIC), Attr::CancelItalic => self.cursor.template.flags.remove(cell::Flags::ITALIC), Attr::Underscore => self.cursor.template.flags.insert(cell::Flags::UNDERLINE), @@ -2051,7 +2223,7 @@ impl ansi::Handler for Term { Attr::CancelStrike => self.cursor.template.flags.remove(cell::Flags::STRIKEOUT), _ => { debug!("Term got unhandled attr: {:?}", attr); - }, + } } } @@ -2066,21 +2238,21 @@ impl ansi::Handler for Term { self.swap_alt(); self.save_cursor_position(); } - }, + } ansi::Mode::ShowCursor => self.mode.insert(TermMode::SHOW_CURSOR), ansi::Mode::CursorKeys => self.mode.insert(TermMode::APP_CURSOR), ansi::Mode::ReportMouseClicks => { self.mode.insert(TermMode::MOUSE_REPORT_CLICK); self.set_mouse_cursor(MouseCursor::Default); - }, + } ansi::Mode::ReportCellMouseMotion => { self.mode.insert(TermMode::MOUSE_DRAG); self.set_mouse_cursor(MouseCursor::Default); - }, + } ansi::Mode::ReportAllMouseMotion => { self.mode.insert(TermMode::MOUSE_MOTION); self.set_mouse_cursor(MouseCursor::Default); - }, + } ansi::Mode::ReportFocusInOut => self.mode.insert(TermMode::FOCUS_IN_OUT), ansi::Mode::BracketedPaste => self.mode.insert(TermMode::BRACKETED_PASTE), ansi::Mode::SgrMouse => self.mode.insert(TermMode::SGR_MOUSE), @@ -2091,7 +2263,7 @@ impl ansi::Handler for Term { ansi::Mode::Insert => self.mode.insert(TermMode::INSERT), // heh ansi::Mode::BlinkingCursor => { trace!("... unimplemented mode"); - }, + } } } @@ -2106,7 +2278,7 @@ impl ansi::Handler for Term { self.swap_alt(); self.restore_cursor_position(); } - }, + } ansi::Mode::ShowCursor => self.mode.remove(TermMode::SHOW_CURSOR), ansi::Mode::CursorKeys => self.mode.remove(TermMode::APP_CURSOR), ansi::Mode::ReportMouseClicks => { @@ -2120,7 +2292,7 @@ impl ansi::Handler for Term { ansi::Mode::ReportAllMouseMotion => { self.mode.remove(TermMode::MOUSE_MOTION); self.set_mouse_cursor(MouseCursor::Text); - }, + } ansi::Mode::ReportFocusInOut => self.mode.remove(TermMode::FOCUS_IN_OUT), ansi::Mode::BracketedPaste => self.mode.remove(TermMode::BRACKETED_PASTE), ansi::Mode::SgrMouse => self.mode.remove(TermMode::SGR_MOUSE), @@ -2131,7 +2303,7 @@ impl ansi::Handler for Term { ansi::Mode::Insert => self.mode.remove(TermMode::INSERT), ansi::Mode::BlinkingCursor => { trace!("... unimplemented mode"); - }, + } } } @@ -2255,26 +2427,17 @@ mod tests { mem::swap(&mut term.semantic_escape_chars, &mut escape_chars); { - *term.selection_mut() = Some(Selection::semantic(Point { - line: 2, - col: Column(1), - })); + *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(1) })); assert_eq!(term.selection_to_string(), Some(String::from("aa"))); } { - *term.selection_mut() = Some(Selection::semantic(Point { - line: 2, - col: Column(4), - })); + *term.selection_mut() = Some(Selection::semantic(Point { line: 2, col: Column(4) })); assert_eq!(term.selection_to_string(), Some(String::from("aaa"))); } { - *term.selection_mut() = Some(Selection::semantic(Point { - line: 1, - col: Column(1), - })); + *term.selection_mut() = Some(Selection::semantic(Point { line: 1, col: Column(1) })); assert_eq!(term.selection_to_string(), Some(String::from("aaa"))); } } @@ -2301,10 +2464,7 @@ mod tests { mem::swap(&mut term.grid, &mut grid); - *term.selection_mut() = Some(Selection::lines(Point { - line: 0, - col: Column(3), - })); + *term.selection_mut() = Some(Selection::lines(Point { line: 0, col: Column(3) })); assert_eq!(term.selection_to_string(), Some(String::from("\"aa\"a\n"))); } @@ -2332,20 +2492,8 @@ mod tests { mem::swap(&mut term.grid, &mut grid); - let mut selection = Selection::simple( - Point { - line: 2, - col: Column(0), - }, - Side::Left, - ); - selection.update( - Point { - line: 0, - col: Column(2), - }, - Side::Right, - ); + let mut selection = Selection::simple(Point { line: 2, col: Column(0) }, Side::Left); + selection.update(Point { line: 0, col: Column(2) }, Side::Right); *term.selection_mut() = Some(selection); assert_eq!(term.selection_to_string(), Some("aaa\n\naaa\n".into())); } @@ -2469,8 +2617,7 @@ mod tests { let mut term: Term = Term::new(&config, size, MessageBuffer::new(), Clipboard::new_nop()); // Add one line of scrollback - term.grid - .scroll_up(&(Line(0)..Line(1)), Line(1), &Cell::default()); + term.grid.scroll_up(&(Line(0)..Line(1)), Line(1), &Cell::default()); // Clear the history term.clear_screen(ansi::ClearMode::Saved); diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 78a57d7723..ec84e204e0 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -177,7 +177,7 @@ impl ::HbFtExt for FreeTypeRasterizer { y_advance: (gp.y_advance as f32) / 64., x_offset: (gp.x_offset as f32) / 64., y_offset: (gp.y_offset as f32) / 64., - glyph: GlyphKey { + glyph_key: GlyphKey { c: KeyType::GlyphIndex(gi.codepoint), font_key, size, diff --git a/font/src/lib.rs b/font/src/lib.rs index 77c4be4ac4..307893b58c 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -316,7 +316,7 @@ pub struct HbGlyph { pub y_advance: f32, pub x_offset: f32, pub y_offset: f32, - pub glyph: GlyphKey, + pub glyph_key: GlyphKey, pub codepoint: u32, // Probably will never be used pub cluster: u32, From a54de7b16d10b5788d4b4c8d5d60bb15ab5aee5c Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 17 Jul 2019 16:54:22 -0700 Subject: [PATCH 15/84] cache now exposes shape method that calls underlying rasterizer shape method. Still not ideal as better use can probably be made of glyph_positions returned by harfbuzz but it will be involved to work those down to the Batch that renders cells at gl float values. --- alacritty_terminal/Cargo.toml | 2 +- alacritty_terminal/src/display.rs | 79 ++---------------------- alacritty_terminal/src/renderer/mod.rs | 33 ++++++++-- alacritty_terminal/src/renderer/rects.rs | 47 ++++++++++++++ alacritty_terminal/src/term/mod.rs | 3 + font/src/ft/mod.rs | 56 +++++++---------- font/src/lib.rs | 9 ++- 7 files changed, 113 insertions(+), 116 deletions(-) diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index 671eec3f34..6caa646c8a 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -56,7 +56,7 @@ image = "0.21.0" objc = "0.2.2" [features] -default = [] +default = ["hb-ft"] # Enabling this feature makes shaders automatically reload when changed live-shader-reload = [] nightly = [] diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index b7853c91c6..c5e8a5e76d 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -545,48 +545,6 @@ impl Display { { let _sampler = self.meter.sampler(); - //// Convert each row into a set of text runs - //// (i.e. cells with the same display properties) - // Shape each run of text. - //type HarfBuzzGlyphs = Vec<(Column, HbGlyph)>; - //let text_runs: Vec<(TextRun, Option)> = - // TextRunIter::new(grid_cells.into_iter()) - // .map(|text_run| { - // use font::{BEAM_CURSOR_CHAR, BOX_CURSOR_CHAR, UNDERLINE_CURSOR_CHAR}; - // let run: String = - // text_run.run_chars.iter().map(|chars| chars[0]).collect(); - // let ends_with_special = run.ends_with(UNDERLINE_CURSOR_CHAR) - // || run.ends_with(BEAM_CURSOR_CHAR) - // || run.ends_with(BOX_CURSOR_CHAR); - // let text = if ends_with_special { - // // Leave off last character - // let i = run.char_indices().last().unwrap().0; - // &run[..i] - // } else { - // &run - // }; - // let key = if text_run.flags.contains(crate::term::cell::Flags::BOLD) { - // glyph_cache.bold_key - // } else if text_run.flags.contains(crate::term::cell::Flags::ITALIC) { - // glyph_cache.italic_key - // } else { - // glyph_cache.font_key - // }; - // let shape = glyph_cache - // .rasterizer - // .shape(text, key, glyph_cache.font_size) - // .map(|glyphs| { - // let (start_col, end_col) = text_run.run; - // (start_col.0..=end_col.0) - // .map(Column) - // .zip(glyphs.into_iter()) - // .collect() - // }); - - // (text_run, shape) - // }) - // .collect(); - // Helper that rounds first arg to be a multiple of second arg. #[inline] fn u_round_to(a: f32, b: f32) -> usize { @@ -595,41 +553,14 @@ impl Display { a / b } self.renderer.with_api(config, &size_info, |mut api| { + // Iterate over each contiguous block of text for text_run in TextRunIter::new(grid_cells.into_iter()) { + // Update underline/strikeout + rects.update_lines_text_run(&size_info, &text_run); + + // Draw text api.render_text_run(text_run, glyph_cache); } - //for (text_run, glyphs) in text_runs.into_iter() { - // // Render each glyph, advancing based on the information provided. - // if let Some(glyphs) = glyphs { - // for (col, g) in glyphs.into_iter() { - // // XXX: what does this do? (for text runs) - // //rects.update_lines(&rc); - - // match g.glyph_key.c { - // // Determine if the glyph is a special character - // KeyType::Char(c @ font::UNDERLINE_CURSOR_CHAR) - // | KeyType::Char(c @ font::BEAM_CURSOR_CHAR) - // | KeyType::Char(c @ font::BOX_CURSOR_CHAR) => { - // api.render_glyph_at_position( - // &text_run.cell_at(col), - // glyph_cache, - // c, - // ); - // } - // _ => { - // // Hold reference to glyph from cache - // let glyph = glyph_cache.get(g.glyph_key, &mut api); - // api.add_render_item(&text_run.cell_at(col), &glyph); - // //rc.column = crate::index::Column(u_round_to( - // // rc.column.0 as f32 * size_info.cell_width - // // + g.x_advance, - // // size_info.cell_width as f32, - // //)) + 1; - // } - // } - // } - // } - //} }); } diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 49fbdfc2cf..a8b7b8e17f 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -36,6 +36,8 @@ use crate::term::color::Rgb; #[cfg(feature = "hb-ft")] use crate::term::text_run::{TextRun, TextRunContent}; use crate::term::{self, cell, RenderableCell, RenderableCellContent}; +#[cfg(feature = "hb-ft")] +use font::HbError; pub mod rects; @@ -280,6 +282,28 @@ impl GlyphCache { .expect("metrics load since font is loaded at glyph cache creation") } + #[cfg(feature = "hb-ft")] + pub fn shape_run<'a, L>( + &'a mut self, + text_run: &str, + font_key: FontKey, + loader: &'a mut L, + ) -> Result, HbError> + where + L: LoadGlyph, + { + use font::{HbFtExt}; + Ok(self.rasterizer.shape(text_run, font_key, self.font_size)? + .get_glyph_infos() + .iter() + .map(move |glyph_info| *self.get(GlyphKey { + c: glyph_info.codepoint.into(), + font_key, + size: self.font_size, + }, loader)) + .collect()) + } + pub fn get<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L) -> &'a Glyph where L: LoadGlyph, @@ -1060,13 +1084,10 @@ impl<'a> RenderApi<'a> { use font::HbFtExt; let glyphs = glyph_cache - .rasterizer - .shape(&run, font_key, glyph_cache.font_size) + .shape_run(&run, font_key, self) .expect("harfbuzz font to be present"); - for (col, g) in text_run.col_iter().zip(glyphs.into_iter()) { - let cell = text_run.cell_at(col); - let glyph = glyph_cache.get(g.glyph_key, self); - self.add_render_item(&text_run.cell_at(col), &glyph); + for (cell, glyph) in text_run.cell_iter().zip(glyphs.into_iter()) { + self.add_render_item(&cell, &glyph); } for (cell, chars) in text_run.cell_iter().zip(chars.iter()) { diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index c54f467959..9b97bc3cd9 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -16,6 +16,8 @@ use font::Metrics; use crate::index::Point; use crate::term::cell::Flags; use crate::term::color::Rgb; +#[cfg(feature = "hb-ft")] +use crate::term::text_run::TextRun; use crate::term::{RenderableCell, SizeInfo}; #[derive(Debug, Copy, Clone)] @@ -68,6 +70,51 @@ impl<'a> Rects<'a> { &self.inner } + #[cfg(feature = "hb-ft")] + pub fn update_lines_text_run(&mut self, size_info: &SizeInfo, text_run: &TextRun) { + for line in self.active_lines.iter_mut() { + match line.range { + Some((ref mut start, ref mut end)) => { + if text_run.line == start.line + && text_run.flags.contains(line.flag) + && text_run.fg == start.fg + && text_run.run.0 == end.col + 1 + { + if size_info.cols() == text_run.run.1 && size_info.lines() == text_run.line { + self.inner.push(create_rect( + &text_run.start_cell(), + (&text_run.last_cell()).into(), + line.flag, + &self.metrics, + &self.size)); + } else { + *end = (&text_run.last_cell()).into(); + } + continue; + } + self.inner.push(create_rect( + &text_run.start_cell(), + (&text_run.last_cell()).into(), + line.flag, + &self.metrics, + &self.size)); + + if text_run.flags.contains(line.flag) { + *start = text_run.start_cell(); + *end = (&text_run.last_cell()).into(); + } else { + line.range = None; + } + }, + None => { + if text_run.flags.contains(line.flag) { + line.range = Some((text_run.start_cell(), (&text_run.last_cell()).into())); + } + } + } + } + } + /// Update the stored lines with the next cell info. pub fn update_lines(&mut self, size_info: &SizeInfo, cell: &RenderableCell) { for line in self.active_lines.iter_mut() { diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 3588e952dc..ec479fce4a 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -512,6 +512,9 @@ pub mod text_run { pub fn start_cell(&self) -> RenderableCell { self.cell_at(self.run.0) } + pub fn last_cell(&self) -> RenderableCell { + self.cell_at(self.run.1) + } /// Returns iterator over range of columns [run.0, run.1] pub fn col_iter(&self) -> impl Iterator { diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index ec84e204e0..f38d13c2d4 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -19,7 +19,9 @@ use std::fmt; use std::path::PathBuf; #[cfg(feature = "hb-ft")] -use super::HbGlyph; +use super::{HbError, HbGlyph}; +#[cfg(feature = "hb-ft")] +use harfbuzz_rs::{Shared, Font, GlyphBuffer}; use freetype::tt_os2::TrueTypeOS2Table; use freetype::{self, Library}; use libc::c_uint; @@ -43,7 +45,7 @@ struct Face { non_scalable: Option, /// This is an option just in case hb_ft_create_font_referenced fails. #[cfg(feature = "hb-ft")] - hb_font: Option>>, + hb_font: Option>>, } impl fmt::Debug for Face { @@ -155,39 +157,25 @@ impl ::Rasterize for FreeTypeRasterizer { #[cfg(feature = "hb-ft")] impl ::HbFtExt for FreeTypeRasterizer { - fn shape(&mut self, text: &str, font_key: FontKey, size: Size) -> Option> { + fn shape( + &mut self, + text: &str, + font_key: FontKey, + size: Size, + ) -> Result { use harfbuzz_rs::{shape, UnicodeBuffer}; - self.faces[&font_key].hb_font.as_ref().map(|hb_font| { - let buf = UnicodeBuffer::default() - .add_str(text) - .guess_segment_properties(); - - // Shape - let glyph_buffer = shape(&*hb_font, buf, &[]); - - // Combine into HbGlyph's - glyph_buffer - .get_glyph_infos() - .iter() - .zip(glyph_buffer.get_glyph_positions().iter()) - .map(|(gi, gp)| { - HbGlyph { - /* ugh -_- you have to divide by 64?? */ - x_advance: (gp.x_advance as f32) / 64., - y_advance: (gp.y_advance as f32) / 64., - x_offset: (gp.x_offset as f32) / 64., - y_offset: (gp.y_offset as f32) / 64., - glyph_key: GlyphKey { - c: KeyType::GlyphIndex(gi.codepoint), - font_key, - size, - }, - codepoint: gi.codepoint, - cluster: gi.cluster, - } - }) - .collect() - }) + self.faces[&font_key] + .hb_font + .as_ref() + .ok_or(HbError::MissingFont(font_key)) + .map(|hb_font| { + let buf = UnicodeBuffer::default() + .add_str(text) + .guess_segment_properties(); + + // Shape with default features + shape(&*hb_font, buf, &[]) + }) } } diff --git a/font/src/lib.rs b/font/src/lib.rs index 307893b58c..b154caa286 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -305,7 +305,8 @@ pub trait Rasterize { pub trait HbFtExt { /// Shape the provided text into a set of glyphs. /// TODO: properly report HarfBuzz errors - fn shape(&mut self, text: &str, font_key: FontKey, size: Size) -> Option>; + fn shape(&mut self, text: &str, font_key: FontKey, size: Size) + -> Result; } /// A HarfBuzz-shaped glyph with advance and offset information. @@ -321,3 +322,9 @@ pub struct HbGlyph { // Probably will never be used pub cluster: u32, } + +#[derive(Debug)] +pub enum HbError { + /// Could not find harfbuzz font for key. + MissingFont(FontKey), +} From 03daff8638aaeadc9136b32246aec50e03600579 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 23 Jul 2019 17:16:37 -0700 Subject: [PATCH 16/84] Cleaned up opt_pair impl --- alacritty_terminal/src/term/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index ec479fce4a..a736e867d4 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -561,7 +561,10 @@ pub mod text_run { } fn opt_pair(a: Option, b: Option) -> Option<(A, B)> { - a.and_then(move |a_val| b.map(move |b_val| (a_val, b_val))) + match (a, b) { + (Some(a_val), Some(b_val)) => Some((a, b)), + _ => None + } } impl Iterator for TextRunIter From dea2a414fc188e539ee6a8977c47353438c753e7 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 23 Jul 2019 18:06:59 -0700 Subject: [PATCH 17/84] Remove cloning of rc.inner by introducing RunStart struct that hold minimal state needed. --- alacritty_terminal/src/term/mod.rs | 114 +++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 32 deletions(-) diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index a736e867d4..7b7e7ff219 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -440,8 +440,30 @@ pub mod text_run { }; use crate::index::{Column, Line}; + pub(crate) struct RunStart { + pub line: Line, + pub column: Column, + pub fg: Rgb, + pub bg: Rgb, + pub bg_alpha: f32, + pub flags: Flags, + } + // Use a macro instead of a function to make use of partial move semantics that don't cross function boundaries + macro_rules! from_rc { + ($rc:ident) => { + RunStart { + line: $rc.line, + column: $rc.column, + fg: $rc.fg, + bg: $rc.bg, + bg_alpha: $rc.bg_alpha, + flags: $rc.flags, + } + }; + } + /// Compare cells and check they are in the same text run - fn is_contiguous_cell(a: &RenderableCell, b: &RenderableCell) -> bool { + fn is_contiguous_context(a: &RunStart, b: &RenderableCell) -> bool { a.line == b.line && a.fg == b.fg && a.bg == b.bg @@ -454,11 +476,34 @@ pub mod text_run { a.0 + 1 == b.0 || b.0 + 1 == a.0 } + use std::fmt; + use std::fmt::Write; pub enum TextRunContent { Cursor(CursorKey), CharRun(Vec<[char; MAX_ZEROWIDTH_CHARS + 1]>), } + impl fmt::Debug for TextRunContent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use TextRunContent::*; + match &self { + Cursor(cursor) => { + f.write_str("Cursor(")?; + cursor.fmt(f)?; + f.write_str(")")?; + }, + CharRun(chars) => { + f.write_str("CharRun(\"")?; + for c in chars.iter() { + f.write_char(c[0])?; + } + f.write_str("\")")?; + } + } + Ok(()) + } + } + #[derive(Debug)] pub struct TextRun { // By definition a run is on one line. pub line: Line, @@ -471,7 +516,7 @@ pub mod text_run { } impl TextRun { pub(crate) fn from_iter_state( - start: RenderableCell, + start: RunStart, latest: Column, buffer: Vec<[char; MAX_ZEROWIDTH_CHARS + 1]>, ) -> Self { @@ -485,7 +530,7 @@ pub mod text_run { flags: start.flags, } } - pub(crate) fn from_cursor_rc(start: RenderableCell, cursor: CursorKey) -> Self { + pub(crate) fn from_cursor_rc(start: RunStart, cursor: CursorKey) -> Self { TextRun { line: start.line, run: (start.column, start.column), @@ -531,19 +576,26 @@ pub mod text_run { } pub struct TextRunIter { iter: I, - run_start: Option, + run_start: Option, latest: Option, buffer: Vec<[char; MAX_ZEROWIDTH_CHARS + 1]>, + cursor: Option, } impl TextRunIter { - pub fn new(iter: I) -> Self { - TextRunIter { iter, latest: None, run_start: None, buffer: Vec::new() } + pub fn new(mut iter: I) -> Self { + TextRunIter { + iter, + latest: None, + run_start: None, + buffer: Vec::new(), + cursor: None + } } /// Check if current run ends with incoming RenderableCell pub fn is_run_break(&self, rc: &RenderableCell) -> bool { let is_cell_break = - self.run_start.as_ref().map(|cell| !is_contiguous_cell(cell, rc)).unwrap_or(false); + self.run_start.as_ref().map(|cell| !is_contiguous_context(cell, &rc)).unwrap_or(false); let is_col_break = self .latest .as_ref() @@ -554,15 +606,20 @@ pub mod text_run { pub fn buffer_content(&mut self, inner: RenderableCellContent) { // Add to buffer only if the next rc is a Char (not a cursor) - if let RenderableCellContent::Chars(chars) = inner { - self.buffer.push(chars); + match inner { + RenderableCellContent::Chars(chars) => { + self.buffer.push(chars); + } + RenderableCellContent::Cursor(cursor) => { + self.cursor = Some(cursor); + } } } } fn opt_pair(a: Option, b: Option) -> Option<(A, B)> { match (a, b) { - (Some(a_val), Some(b_val)) => Some((a, b)), + (Some(a_val), Some(b_val)) => Some((a_val, b_val)), _ => None } } @@ -574,46 +631,39 @@ pub mod text_run { type Item = TextRun; fn next(&mut self) -> Option { + use log::debug; let mut output = None; while let Some(rc) = self.iter.next() { if self.latest.is_none() || self.run_start.is_none() { - self.latest.replace(rc.column); - self.run_start.replace(rc); - continue; - } else if self.run_start.as_ref().map(|rc| rc.is_cursor()).unwrap_or(false) { - self.buffer_content(rc.inner.clone()); + self.run_start.replace(from_rc!(rc)); + } else if self.cursor.is_some() { + self.buffer_content(rc.inner); self.latest = Some(rc.column); - let start = self.run_start.replace(rc); - output = start.map(|rc| match rc.inner { - RenderableCellContent::Chars(_) => { - panic!("Found chars inner content for Cursor RenderableCell") - } - RenderableCellContent::Cursor(cursor) => { - TextRun::from_cursor_rc(rc, cursor) - } - }); + let start = self.run_start.replace(from_rc!(rc)); + output = opt_pair(start, self.cursor.take()).map(|(start, cursor)| TextRun::from_cursor_rc(start, cursor)); break; } else if self.is_run_break(&rc) || rc.is_cursor() { let prev_buffer = self.buffer.drain(..).collect(); let latest = self.latest.replace(rc.column); - self.buffer_content(rc.inner.clone()); - let start = self.run_start.replace(rc); + self.buffer_content(rc.inner); + let start = self.run_start.replace(from_rc!(rc)); output = opt_pair(start, latest).map(|(start, latest)| { TextRun::from_iter_state(start, latest, prev_buffer) }); break; - } else { - self.latest = Some(rc.column); - self.buffer_content(rc.inner.clone()); } + self.latest = Some(rc.column); + self.buffer_content(rc.inner); } output.or_else(|| { - if self.buffer.is_empty() { - None - } else { + if !self.buffer.is_empty() { opt_pair(self.run_start.take(), self.latest.take()).map(|(start, latest)| // Save leftover buffer and empty it TextRun::from_iter_state(start, latest, self.buffer.drain(..).collect())) + } else if let Some(cursor) = self.cursor { + self.run_start.take().map(|start| TextRun::from_cursor_rc(start, cursor)) + } else { + None } }) } From 09a6f9b5cea40fd3bc9a358224ee84487df00688 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 23 Jul 2019 18:32:38 -0700 Subject: [PATCH 18/84] Store string of rendered chars and zero width chars in text run. --- alacritty_terminal/src/renderer/mod.rs | 29 ++++------ alacritty_terminal/src/term/mod.rs | 76 ++++++++++++-------------- 2 files changed, 45 insertions(+), 60 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index a8b7b8e17f..1899ea9fa5 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -460,6 +460,7 @@ impl Batch { Batch { tex: 0, instances: Vec::with_capacity(BATCH_MAX) } } + pub fn add_item(&mut self, cell: &RenderableCell, glyph: &Glyph) { if self.is_empty() { self.tex = glyph.tex_id; @@ -1063,7 +1064,7 @@ impl<'a> RenderApi<'a> { }); self.add_render_item(&text_run.start_cell(), &glyph); } - TextRunContent::CharRun(chars) => { + TextRunContent::CharRun(run, zero_widths) => { let font_key = if text_run.flags.contains(cell::Flags::BOLD) { glyph_cache.bold_key } else if text_run.flags.contains(cell::Flags::ITALIC) { @@ -1073,26 +1074,18 @@ impl<'a> RenderApi<'a> { }; let hidden = text_run.flags.contains(cell::Flags::HIDDEN); - let run: String = if hidden { - " ".repeat(chars.len()) - } else { - chars - .iter() - .map(|chars| if chars[0] == '\t' { ' ' } else { chars[0] }) - .collect() + if !hidden { + let glyphs = glyph_cache + .shape_run(&run, font_key, self) + .expect("harfbuzz font to be present"); + for (cell, glyph) in text_run.cell_iter().zip(glyphs.into_iter()) { + self.add_render_item(&cell, &glyph); + } }; - use font::HbFtExt; - let glyphs = glyph_cache - .shape_run(&run, font_key, self) - .expect("harfbuzz font to be present"); - for (cell, glyph) in text_run.cell_iter().zip(glyphs.into_iter()) { - self.add_render_item(&cell, &glyph); - } - for (cell, chars) in text_run.cell_iter().zip(chars.iter()) { - let slice: &[char] = &chars[1..]; - for c in slice.into_iter().filter(|c| **c != ' ') { + for (cell, chars) in text_run.cell_iter().zip(zero_widths.iter()) { + for c in chars.into_iter().filter(|c| **c != ' ') { let glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: c.into() }; let average_advance = glyph_cache.metrics.average_advance as f32; diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 7b7e7ff219..367b4623fc 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -467,40 +467,19 @@ pub mod text_run { a.line == b.line && a.fg == b.fg && a.bg == b.bg - && a.bg_alpha == b.bg_alpha + && (a.bg_alpha - b.bg_alpha).abs() < 0.01 && a.flags == b.flags } /// Checks two columns are adjacent - fn is_contiguous_col(a: &Column, b: &Column) -> bool { + fn is_contiguous_col(a: Column, b: Column) -> bool { a.0 + 1 == b.0 || b.0 + 1 == a.0 } - use std::fmt; - use std::fmt::Write; + #[derive(Debug)] pub enum TextRunContent { Cursor(CursorKey), - CharRun(Vec<[char; MAX_ZEROWIDTH_CHARS + 1]>), - } - impl fmt::Debug for TextRunContent { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use TextRunContent::*; - match &self { - Cursor(cursor) => { - f.write_str("Cursor(")?; - cursor.fmt(f)?; - f.write_str(")")?; - }, - CharRun(chars) => { - f.write_str("CharRun(\"")?; - for c in chars.iter() { - f.write_char(c[0])?; - } - f.write_str("\")")?; - } - } - Ok(()) - } + CharRun(String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), } #[derive(Debug)] @@ -518,12 +497,12 @@ pub mod text_run { pub(crate) fn from_iter_state( start: RunStart, latest: Column, - buffer: Vec<[char; MAX_ZEROWIDTH_CHARS + 1]>, + buffer: (String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), ) -> Self { TextRun { line: start.line, run: (start.column, latest), - run_chars: TextRunContent::CharRun(buffer), + run_chars: TextRunContent::CharRun(buffer.0, buffer.1), fg: start.fg, bg: start.bg, bg_alpha: start.bg_alpha, @@ -580,41 +559,59 @@ pub mod text_run { latest: Option, buffer: Vec<[char; MAX_ZEROWIDTH_CHARS + 1]>, cursor: Option, + buffer_text: String, + buffer_zero_width: Vec<[char; MAX_ZEROWIDTH_CHARS]>, } impl TextRunIter { - pub fn new(mut iter: I) -> Self { + pub fn new(iter: I) -> Self { TextRunIter { iter, latest: None, run_start: None, buffer: Vec::new(), - cursor: None + cursor: None, + buffer_text: String::new(), + buffer_zero_width: Vec::new(), } } /// Check if current run ends with incoming RenderableCell - pub fn is_run_break(&self, rc: &RenderableCell) -> bool { + fn is_run_break(&self, rc: &RenderableCell) -> bool { let is_cell_break = self.run_start.as_ref().map(|cell| !is_contiguous_context(cell, &rc)).unwrap_or(false); let is_col_break = self .latest .as_ref() - .map(|col| !is_contiguous_col(col, &rc.column)) + .map(|col| !is_contiguous_col(*col, rc.column)) .unwrap_or(false); is_cell_break || is_col_break } - pub fn buffer_content(&mut self, inner: RenderableCellContent) { + fn buffer_content(&mut self, inner: RenderableCellContent) { // Add to buffer only if the next rc is a Char (not a cursor) match inner { RenderableCellContent::Chars(chars) => { - self.buffer.push(chars); + self.buffer_text.push(chars[0]); + let mut arr: [char; MAX_ZEROWIDTH_CHARS] = Default::default(); + arr.copy_from_slice(&chars[1..]); + self.buffer_zero_width.push(arr); } RenderableCellContent::Cursor(cursor) => { self.cursor = Some(cursor); } } } + + fn drain_buffer(&mut self) -> (String, Vec<[char; MAX_ZEROWIDTH_CHARS]>) { + (self.buffer_text.drain(..).collect(), self.buffer_zero_width.drain(..).collect()) + } + + fn start_run(&mut self, rc: RenderableCell) -> (Option, Option) { + self.buffer_content(rc.inner); + let latest = self.latest.replace(rc.column); + let start = self.run_start.replace(from_rc!(rc)); + (start, latest) + } } fn opt_pair(a: Option, b: Option) -> Option<(A, B)> { @@ -631,22 +628,17 @@ pub mod text_run { type Item = TextRun; fn next(&mut self) -> Option { - use log::debug; let mut output = None; while let Some(rc) = self.iter.next() { if self.latest.is_none() || self.run_start.is_none() { self.run_start.replace(from_rc!(rc)); } else if self.cursor.is_some() { - self.buffer_content(rc.inner); - self.latest = Some(rc.column); - let start = self.run_start.replace(from_rc!(rc)); + let (start, _) = self.start_run(rc); output = opt_pair(start, self.cursor.take()).map(|(start, cursor)| TextRun::from_cursor_rc(start, cursor)); break; } else if self.is_run_break(&rc) || rc.is_cursor() { - let prev_buffer = self.buffer.drain(..).collect(); - let latest = self.latest.replace(rc.column); - self.buffer_content(rc.inner); - let start = self.run_start.replace(from_rc!(rc)); + let prev_buffer = self.drain_buffer(); + let (start, latest) = self.start_run(rc); output = opt_pair(start, latest).map(|(start, latest)| { TextRun::from_iter_state(start, latest, prev_buffer) }); @@ -659,7 +651,7 @@ pub mod text_run { if !self.buffer.is_empty() { opt_pair(self.run_start.take(), self.latest.take()).map(|(start, latest)| // Save leftover buffer and empty it - TextRun::from_iter_state(start, latest, self.buffer.drain(..).collect())) + TextRun::from_iter_state(start, latest, self.drain_buffer())) } else if let Some(cursor) = self.cursor { self.run_start.take().map(|start| TextRun::from_cursor_rc(start, cursor)) } else { From 88f334dbed3d149923da09ad57751308fcef1d2e Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 23 Jul 2019 18:43:26 -0700 Subject: [PATCH 19/84] Replace render_string's calls to render_cell with call to render_text_run when hb-ft is on. --- alacritty_terminal/src/renderer/mod.rs | 53 +++++++++++++------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 1899ea9fa5..dd36f43c40 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -971,6 +971,7 @@ impl<'a> RenderApi<'a> { /// Render a string in a variable location. Used for printing the render timer, warnings and /// errors. + #[cfg(not(feature = "hb-ft"))] pub fn render_string( &mut self, string: &str, @@ -1004,6 +1005,30 @@ impl<'a> RenderApi<'a> { } } + #[cfg(feature = "hb-ft")] + pub fn render_string( + &mut self, + string: &str, + line: Line, + glyph_cache: &mut GlyphCache, + color: Option, + ) { + let bg_alpha = color.map(|_| 1.0).unwrap_or(0.0); + let col = Column(0); + + let text_run = TextRun { + line, + run: (Column(0), Column(string.len() - 1)), + run_chars: TextRunContent::CharRun(string.to_owned(), vec![]), + fg: Rgb { r: 0, g: 0, b: 0 }, + bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0 }), + flags: cell::Flags::empty(), + bg_alpha + }; + + self.render_text_run(text_run, glyph_cache); + } + pub 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 { @@ -1018,32 +1043,6 @@ impl<'a> RenderApi<'a> { } } - pub fn render_glyph_at_position( - &mut self, - cell: &RenderableCell, - glyph_cache: &mut GlyphCache, - glyph: char, - ) { - if cell.flags.contains(cell::Flags::HIDDEN) { - return; - } - // Get font key for cell - // FIXME this is super inefficient. - let font_key = if cell.flags.contains(cell::Flags::BOLD) { - glyph_cache.bold_key - } else if cell.flags.contains(cell::Flags::ITALIC) { - glyph_cache.italic_key - } else { - glyph_cache.font_key - }; - - let glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: glyph.into() }; - - // Add cell to batch - let glyph = glyph_cache.get(glyph_key, self); - self.add_render_item(cell, &glyph); - } - #[cfg(feature = "hb-ft")] pub fn render_text_run(&mut self, text_run: TextRun, glyph_cache: &mut GlyphCache) { match &text_run.run_chars { @@ -1085,7 +1084,7 @@ impl<'a> RenderApi<'a> { for (cell, chars) in text_run.cell_iter().zip(zero_widths.iter()) { - for c in chars.into_iter().filter(|c| **c != ' ') { + for c in chars.iter().filter(|c| **c != ' ') { let glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: c.into() }; let average_advance = glyph_cache.metrics.average_advance as f32; From 09755dac071e07e01f0648ac7110fe8f9b397d60 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 24 Jul 2019 10:51:24 -0700 Subject: [PATCH 20/84] Clean up warning and remove some code that is no longer used. --- alacritty_terminal/src/display.rs | 22 ++++------------------ alacritty_terminal/src/renderer/mod.rs | 3 +-- font/src/ft/mod.rs | 3 +-- font/src/lib.rs | 21 +++------------------ 4 files changed, 9 insertions(+), 40 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index c5e8a5e76d..0d2ad06e7d 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -24,21 +24,19 @@ use glutin::EventsLoop; use parking_lot::MutexGuard; use crate::config::{Config, StartupMode}; -use crate::index::{Line, Column}; +use crate::index::{Line}; use crate::message_bar::Message; use crate::meter::Meter; use crate::renderer::rects::{Rect, Rects}; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::sync::FairMutex; use crate::term::color::Rgb; -use crate::term::{RenderableCell, SizeInfo, Term, RenderableCellContent}; +use crate::term::{RenderableCell, SizeInfo, Term}; use crate::window::{self, Window}; -use font::{self, KeyType, Rasterize}; -#[cfg(feature = "hb-ft")] -use font::{HbFtExt, HbGlyph}; +use font::{self, Rasterize}; #[cfg(feature = "hb-ft")] -use crate::term::text_run::{TextRun, TextRunIter}; +use crate::term::text_run::{TextRunIter}; #[derive(Debug)] pub enum Error { @@ -500,11 +498,6 @@ impl Display { } } - #[cfg(feature = "hb-ft")] - let g_lines = terminal.grid().num_lines(); - #[cfg(feature = "hb-ft")] - let g_cols = terminal.grid().num_cols(); - // Clear when terminal mutex isn't held. Mesa for // some reason takes a long time to call glClear(). The driver descends // into xcb_connect_to_fd() which ends up calling __poll_nocancel() @@ -545,13 +538,6 @@ impl Display { { let _sampler = self.meter.sampler(); - // Helper that rounds first arg to be a multiple of second arg. - #[inline] - fn u_round_to(a: f32, b: f32) -> usize { - let a = a as usize; - let b = b as usize; - a / b - } self.renderer.with_api(config, &size_info, |mut api| { // Iterate over each contiguous block of text for text_run in TextRunIter::new(grid_cells.into_iter()) { diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index dd36f43c40..c909c48adf 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -293,7 +293,7 @@ impl GlyphCache { L: LoadGlyph, { use font::{HbFtExt}; - Ok(self.rasterizer.shape(text_run, font_key, self.font_size)? + Ok(self.rasterizer.shape(text_run, font_key)? .get_glyph_infos() .iter() .map(move |glyph_info| *self.get(GlyphKey { @@ -1014,7 +1014,6 @@ impl<'a> RenderApi<'a> { color: Option, ) { let bg_alpha = color.map(|_| 1.0).unwrap_or(0.0); - let col = Column(0); let text_run = TextRun { line, diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index f38d13c2d4..4c899f42ac 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -19,7 +19,7 @@ use std::fmt; use std::path::PathBuf; #[cfg(feature = "hb-ft")] -use super::{HbError, HbGlyph}; +use super::HbError; #[cfg(feature = "hb-ft")] use harfbuzz_rs::{Shared, Font, GlyphBuffer}; use freetype::tt_os2::TrueTypeOS2Table; @@ -161,7 +161,6 @@ impl ::HbFtExt for FreeTypeRasterizer { &mut self, text: &str, font_key: FontKey, - size: Size, ) -> Result { use harfbuzz_rs::{shape, UnicodeBuffer}; self.faces[&font_key] diff --git a/font/src/lib.rs b/font/src/lib.rs index b154caa286..54ac29666d 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -56,9 +56,8 @@ extern crate harfbuzz_rs; #[cfg_attr(not(windows), macro_use)] extern crate log; -use std::hash::{Hash, Hasher}; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::{cmp, fmt}; +use std::fmt; // If target isn't macos or windows, reexport everything from ft #[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] @@ -169,7 +168,7 @@ impl From for KeyType { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct GlyphKey { - pub c: KeyType, + pub c: char, pub font_key: FontKey, pub size: Size, } @@ -305,24 +304,10 @@ pub trait Rasterize { pub trait HbFtExt { /// Shape the provided text into a set of glyphs. /// TODO: properly report HarfBuzz errors - fn shape(&mut self, text: &str, font_key: FontKey, size: Size) + fn shape(&mut self, text: &str, font_key: FontKey) -> Result; } -/// A HarfBuzz-shaped glyph with advance and offset information. -#[cfg(feature = "hb-ft")] -#[derive(Debug)] -pub struct HbGlyph { - pub x_advance: f32, - pub y_advance: f32, - pub x_offset: f32, - pub y_offset: f32, - pub glyph_key: GlyphKey, - pub codepoint: u32, - // Probably will never be used - pub cluster: u32, -} - #[derive(Debug)] pub enum HbError { /// Could not find harfbuzz font for key. From 7c3896b650fa6fd13a42634ab26d9526f4bee877 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 24 Jul 2019 15:47:19 -0700 Subject: [PATCH 21/84] Clean up some of the code that is no longer needed and swap GlyphKey for indice. --- alacritty_terminal/src/grid/tests.rs | 60 ------------------------- alacritty_terminal/src/renderer/mod.rs | 35 ++++++++++++--- alacritty_terminal/tests/ref.rs | 19 -------- font/src/ft/mod.rs | 58 ++++++++++++++---------- font/src/lib.rs | 62 ++++++++++++++------------ 5 files changed, 96 insertions(+), 138 deletions(-) diff --git a/alacritty_terminal/src/grid/tests.rs b/alacritty_terminal/src/grid/tests.rs index 9797d466b6..a352e74797 100644 --- a/alacritty_terminal/src/grid/tests.rs +++ b/alacritty_terminal/src/grid/tests.rs @@ -140,16 +140,7 @@ fn shrink_reflow() { grid[Line(0)][Column(3)] = cell('4'); grid[Line(0)][Column(4)] = cell('5'); -<<<<<<< HEAD:alacritty_terminal/src/grid/tests.rs grid.resize(true, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default()); -======= - grid.resize( - Line(1), - Column(2), - &mut Point::new(Line(0), Column(0)), - &Cell::default(), - ); ->>>>>>> Tried setting scale and ppem but it didn't change anything.:src/grid/tests.rs assert_eq!(grid.len(), 3); @@ -175,23 +166,8 @@ fn shrink_reflow_twice() { grid[Line(0)][Column(3)] = cell('4'); grid[Line(0)][Column(4)] = cell('5'); -<<<<<<< HEAD:alacritty_terminal/src/grid/tests.rs grid.resize(true, Line(1), Column(4), &mut Point::new(Line(0), Column(0)), &Cell::default()); grid.resize(true, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default()); -======= - grid.resize( - Line(1), - Column(4), - &mut Point::new(Line(0), Column(0)), - &Cell::default(), - ); - grid.resize( - Line(1), - Column(2), - &mut Point::new(Line(0), Column(0)), - &Cell::default(), - ); ->>>>>>> Tried setting scale and ppem but it didn't change anything.:src/grid/tests.rs assert_eq!(grid.len(), 3); @@ -217,16 +193,7 @@ fn shrink_reflow_empty_cell_inside_line() { grid[Line(0)][Column(3)] = cell('4'); grid[Line(0)][Column(4)] = Cell::default(); -<<<<<<< HEAD:alacritty_terminal/src/grid/tests.rs grid.resize(true, Line(1), Column(2), &mut Point::new(Line(0), Column(0)), &Cell::default()); -======= - grid.resize( - Line(1), - Column(2), - &mut Point::new(Line(0), Column(0)), - &Cell::default(), - ); ->>>>>>> Tried setting scale and ppem but it didn't change anything.:src/grid/tests.rs assert_eq!(grid.len(), 2); @@ -238,16 +205,7 @@ fn shrink_reflow_empty_cell_inside_line() { assert_eq!(grid[0][Column(0)], cell('3')); assert_eq!(grid[0][Column(1)], cell('4')); -<<<<<<< HEAD:alacritty_terminal/src/grid/tests.rs grid.resize(true, Line(1), Column(1), &mut Point::new(Line(0), Column(0)), &Cell::default()); -======= - grid.resize( - Line(1), - Column(1), - &mut Point::new(Line(0), Column(0)), - &Cell::default(), - ); ->>>>>>> Tried setting scale and ppem but it didn't change anything.:src/grid/tests.rs assert_eq!(grid.len(), 4); @@ -272,16 +230,7 @@ fn grow_reflow() { grid[Line(1)][Column(0)] = cell('3'); grid[Line(1)][Column(1)] = Cell::default(); -<<<<<<< HEAD:alacritty_terminal/src/grid/tests.rs grid.resize(true, Line(2), Column(3), &mut Point::new(Line(0), Column(0)), &Cell::default()); -======= - grid.resize( - Line(2), - Column(3), - &mut Point::new(Line(0), Column(0)), - &Cell::default(), - ); ->>>>>>> Tried setting scale and ppem but it didn't change anything.:src/grid/tests.rs assert_eq!(grid.len(), 2); @@ -307,16 +256,7 @@ fn grow_reflow_multiline() { grid[Line(2)][Column(0)] = cell('5'); grid[Line(2)][Column(1)] = cell('6'); -<<<<<<< HEAD:alacritty_terminal/src/grid/tests.rs grid.resize(true, Line(3), Column(6), &mut Point::new(Line(0), Column(0)), &Cell::default()); -======= - grid.resize( - Line(3), - Column(6), - &mut Point::new(Line(0), Column(0)), - &Cell::default(), - ); ->>>>>>> Tried setting scale and ppem but it didn't change anything.:src/grid/tests.rs assert_eq!(grid.len(), 3); diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index c909c48adf..521163f712 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -22,7 +22,7 @@ use std::sync::mpsc; use std::time::Duration; use fnv::FnvHasher; -use font::{self, FontDesc, FontKey, GlyphKey, KeyType, Rasterize, RasterizedGlyph, Rasterizer}; +use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer}; use glutin::dpi::PhysicalSize; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; @@ -35,7 +35,7 @@ use crate::renderer::rects::{Rect, Rects}; use crate::term::color::Rgb; #[cfg(feature = "hb-ft")] use crate::term::text_run::{TextRun, TextRunContent}; -use crate::term::{self, cell, RenderableCell, RenderableCellContent}; +use crate::term::{self, cell, RenderableCell}; #[cfg(feature = "hb-ft")] use font::HbError; @@ -197,7 +197,13 @@ impl GlyphCache { // 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'.into(), size: font.size })?; + #[cfg(not(feature = "hb-ft"))] + rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?; + // Need to load at least one glyph for the face before calling metrics. + // The glyph requested here (1 at the time of writing) has no special + // meaning. + #[cfg(feature = "hb-ft")] + rasterizer.get_glyph(GlyphKey { font_key: regular, c: 1, size: font.size })?; let metrics = rasterizer.metrics(regular, font.size)?; @@ -222,8 +228,13 @@ impl GlyphCache { fn load_glyphs_for_font(&mut self, font: FontKey, loader: &mut L) { let size = self.font_size; + #[cfg(not(feature = "hb-ft"))] for i in 32u8..=128u8 { - self.get(GlyphKey { font_key: font, c: (i as char).into(), size }, loader); + self.get(GlyphKey { font_key: font, c: i as char, size }, loader); + } + #[cfg(feature = "hb-ft")] + for c in 32u32..=128u32 { + self.get(GlyphKey { font_key: font, c, size }, loader); } } @@ -282,6 +293,11 @@ impl GlyphCache { .expect("metrics load since font is loaded at glyph cache creation") } + #[cfg(feature = "hb-ft")] + pub fn index_for_char(&self, font_key: FontKey, c: char) -> Option { + self.rasterizer.index_for_char(font_key, c) + } + #[cfg(feature = "hb-ft")] pub fn shape_run<'a, L>( &'a mut self, @@ -297,7 +313,7 @@ impl GlyphCache { .get_glyph_infos() .iter() .map(move |glyph_info| *self.get(GlyphKey { - c: glyph_info.codepoint.into(), + c: glyph_info.codepoint, font_key, size: self.font_size, }, loader)) @@ -1005,6 +1021,8 @@ impl<'a> RenderApi<'a> { } } + /// Render a string in a variable location. Used for printing the render timer, warnings and + /// errors. #[cfg(feature = "hb-ft")] pub fn render_string( &mut self, @@ -1084,8 +1102,10 @@ impl<'a> RenderApi<'a> { for (cell, chars) in text_run.cell_iter().zip(zero_widths.iter()) { for c in chars.iter().filter(|c| **c != ' ') { + // Expect this to be a very small number of calls so accept eating the cost of indexing. + let index = glyph_cache.index_for_char(font_key, *c).expect("font_key to be present but there was nothing."); let glyph_key = - GlyphKey { font_key, size: glyph_cache.font_size, c: c.into() }; + GlyphKey { font_key, size: glyph_cache.font_size, c: index}; let average_advance = glyph_cache.metrics.average_advance as f32; let mut glyph = *glyph_cache.get(glyph_key, self); @@ -1103,6 +1123,7 @@ impl<'a> RenderApi<'a> { }; } + #[cfg(not(feature = "hb-ft"))] pub fn render_cell(&mut self, cell: RenderableCell, glyph_cache: &mut GlyphCache) { let chars = match cell.inner { RenderableCellContent::Cursor(cursor_key) => { @@ -1156,7 +1177,7 @@ impl<'a> RenderApi<'a> { // Render zero-width characters for c in (&chars[1..]).iter().filter(|c| **c != ' ') { - glyph_key.c = KeyType::from(*c); + glyph_key.c = *c; let mut glyph = *glyph_cache.get(glyph_key, self); // The metrics of zero-width characters are based on rendering diff --git a/alacritty_terminal/tests/ref.rs b/alacritty_terminal/tests/ref.rs index 0ebedbd180..91c0b6eedd 100644 --- a/alacritty_terminal/tests/ref.rs +++ b/alacritty_terminal/tests/ref.rs @@ -6,7 +6,6 @@ use std::fs::File; use std::io::{self, Read}; use std::path::Path; -<<<<<<< HEAD:alacritty_terminal/tests/ref.rs use alacritty_terminal::ansi; use alacritty_terminal::clipboard::Clipboard; use alacritty_terminal::config::Config; @@ -17,17 +16,6 @@ use alacritty_terminal::term::SizeInfo; use alacritty_terminal::util::fmt::{Green, Red}; use alacritty_terminal::Grid; use alacritty_terminal::Term; -======= -use alacritty::ansi; -use alacritty::config::Config; -use alacritty::index::Column; -use alacritty::message_bar::MessageBuffer; -use alacritty::term::cell::Cell; -use alacritty::term::SizeInfo; -use alacritty::util::fmt::{Green, Red}; -use alacritty::Grid; -use alacritty::Term; ->>>>>>> Tried setting scale and ppem but it didn't change anything.:tests/ref.rs macro_rules! ref_tests { ($($name:ident)*) => { @@ -73,14 +61,7 @@ where P: AsRef, { let mut res = Vec::new(); -<<<<<<< HEAD:alacritty_terminal/tests/ref.rs File::open(path.as_ref()).unwrap().read_to_end(&mut res).unwrap(); -======= - File::open(path.as_ref()) - .unwrap() - .read_to_end(&mut res) - .unwrap(); ->>>>>>> Tried setting scale and ppem but it didn't change anything.:tests/ref.rs res } diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 4c899f42ac..33392c837a 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -29,7 +29,7 @@ use libc::c_uint; pub mod fc; use super::{ - FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, Size, Slant, Style, Weight, + FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight, }; struct FixedSize { @@ -332,32 +332,41 @@ impl FreeTypeRasterizer { } } + #[cfg(feature = "hb-ft")] + pub fn index_for_char(&self, font_key: FontKey, c: char) -> Option { + self.faces.get(&font_key).map(|face| + face.ft_face.get_char_index(c as usize)) + } + + #[cfg(feature = "hb-ft")] + fn face_for_glyph( + &mut self, + glyph_key: GlyphKey, + _have_recursed: bool, + ) -> Result { + Ok(glyph_key.font_key) + } + + #[cfg(not(feature = "hb-ft"))] fn face_for_glyph( &mut self, glyph_key: GlyphKey, have_recursed: bool, ) -> Result { - match glyph_key.c { - KeyType::GlyphIndex(_) => { - // Already been through shaping so just use font_key - Ok(glyph_key.font_key) - } - KeyType::Char(c) => { - let use_initial_face = if let Some(face) = self.faces.get(&glyph_key.font_key) { - let index = face.ft_face.get_char_index(c as usize); + let c = glyph_key.c; + let use_initial_face = if let Some(face) = self.faces.get(&glyph_key.font_key) { + let index = face.ft_face.get_char_index(c as usize); - index != 0 || have_recursed - } else { - false - }; + index != 0 || have_recursed + } else { + false + }; - if use_initial_face { - Ok(glyph_key.font_key) - } else { - let key = self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key); - Ok(key) - } - } + if use_initial_face { + Ok(glyph_key.font_key) + } else { + let key = self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key); + Ok(key) } } @@ -365,10 +374,10 @@ impl FreeTypeRasterizer { // Render a normal character if it's not a cursor let font_key = self.face_for_glyph(glyph_key, false)?; let face = &self.faces[&font_key]; - let index = match glyph_key.c { - KeyType::GlyphIndex(i) => i, - KeyType::Char(c) => face.ft_face.get_char_index(c as usize), - }; + #[cfg(not(feature = "hb-ft"))] + let index = face.ft_face.get_char_index(glyph_key.c as usize); + #[cfg(feature = "hb-ft")] + let index = glyph_key.c; self.get_rendered_glyph_index(face, glyph_key, index) } @@ -547,6 +556,7 @@ impl FreeTypeRasterizer { } } + #[cfg(not(feature = "hb-ft"))] fn load_face_with_glyph(&mut self, glyph: char) -> Result { let mut charset = fc::CharSet::new(); charset.add(glyph); diff --git a/font/src/lib.rs b/font/src/lib.rs index 54ac29666d..3fb8efde25 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -56,6 +56,7 @@ extern crate harfbuzz_rs; #[cfg_attr(not(windows), macro_use)] extern crate log; +use std::hash::{Hash, Hasher}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::fmt; @@ -166,39 +167,41 @@ impl From for KeyType { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, Eq)] pub struct GlyphKey { + #[cfg(not(feature = "hb-ft"))] pub c: char, + #[cfg(feature = "hb-ft")] + pub c: u32, pub font_key: FontKey, pub size: Size, } -//impl Hash for GlyphKey { -// fn hash(&self, state: &mut H) { -// unsafe { -// // This transmute is fine: -// // -// // - If GlyphKey ever becomes a different size, this will fail to compile -// // - Result is being used for hashing and has no fields (it's a u64) -// ::std::mem::transmute::(*self) -// } -// .hash(state); -// state. -// } -//} -// -//impl PartialEq for GlyphKey { -// fn eq(&self, other: &Self) -> bool { -// unsafe { -// // This transmute is fine: -// // -// // - If GlyphKey ever becomes a different size, this will fail to compile -// // - Result is being used for equality checking and has no fields (it's a u64) -// let other = ::std::mem::transmute::(*other); -// ::std::mem::transmute::(*self).eq(&other) -// } -// } -//} +impl Hash for GlyphKey { + fn hash(&self, state: &mut H) { + unsafe { + // This transmute is fine: + // + // - If GlyphKey ever becomes a different size, this will fail to compile + // - Result is being used for hashing and has no fields (it's a u64) + ::std::mem::transmute::(*self) + } + .hash(state); + } +} + +impl PartialEq for GlyphKey { + fn eq(&self, other: &Self) -> bool { + unsafe { + // This transmute is fine: + // + // - If GlyphKey ever becomes a different size, this will fail to compile + // - Result is being used for equality checking and has no fields (it's a u64) + let other = ::std::mem::transmute::(*other); + ::std::mem::transmute::(*self).eq(&other) + } + } +} /// Font size stored as integer #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -232,7 +235,10 @@ impl ::std::ops::Add for Size { #[derive(Clone)] pub struct RasterizedGlyph { - pub c: KeyType, + #[cfg(not(feature = "hb-ft"))] + pub c: char, + #[cfg(feature = "hb-ft")] + pub c: u32, pub width: i32, pub height: i32, pub top: i32, From dd69ce657d731a57f82b80b2fe944f48b0b1d2ec Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 25 Jul 2019 10:22:43 -0700 Subject: [PATCH 22/84] Fix build with hb-ft feature turned off. --- alacritty_terminal/Cargo.toml | 2 +- alacritty_terminal/src/renderer/mod.rs | 2 ++ alacritty_terminal/src/term/mod.rs | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index 6caa646c8a..671eec3f34 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -56,7 +56,7 @@ image = "0.21.0" objc = "0.2.2" [features] -default = ["hb-ft"] +default = [] # Enabling this feature makes shaders automatically reload when changed live-shader-reload = [] nightly = [] diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 521163f712..bd329ca4fc 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -36,6 +36,8 @@ use crate::term::color::Rgb; #[cfg(feature = "hb-ft")] use crate::term::text_run::{TextRun, TextRunContent}; use crate::term::{self, cell, RenderableCell}; +#[cfg(not(feature = "hb-ft"))] +use crate::term::RenderableCellContent; #[cfg(feature = "hb-ft")] use font::HbError; diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 367b4623fc..23b2bd2362 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -368,6 +368,7 @@ impl RenderableCell { } } + #[cfg(feature = "hb-ft")] fn is_cursor(&self) -> bool { match &self.inner { RenderableCellContent::Cursor(_) => true, From cf777a1994b28887704470ec46c80ed58f5b2a74 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 25 Jul 2019 16:43:52 -0700 Subject: [PATCH 23/84] Clean up superfluous changes from rustfmt that clutter PR. --- Cargo.toml | 2 +- alacritty/src/main.rs | 18 +--- alacritty_terminal/src/ansi.rs | 55 ++++--------- alacritty_terminal/src/cursor.rs | 8 +- alacritty_terminal/src/display.rs | 14 ++-- alacritty_terminal/src/event.rs | 56 ++++--------- alacritty_terminal/src/event_loop.rs | 20 ++--- alacritty_terminal/src/grid/mod.rs | 27 ++---- alacritty_terminal/src/grid/row.rs | 6 +- alacritty_terminal/src/input.rs | 33 ++++---- alacritty_terminal/src/lib.rs | 7 +- alacritty_terminal/src/message_bar.rs | 10 +-- alacritty_terminal/src/renderer/mod.rs | 51 ++++++------ alacritty_terminal/src/renderer/rects.rs | 7 +- alacritty_terminal/src/term/mod.rs | 86 +++++++++----------- alacritty_terminal/src/tty/unix.rs | 10 +-- alacritty_terminal/src/tty/windows/conpty.rs | 8 -- alacritty_terminal/src/tty/windows/winpty.rs | 14 ---- 18 files changed, 144 insertions(+), 288 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9995755bd7..2e8fd4dbbf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,4 @@ debug = 1 incremental = false [patch.crates-io] -servo-freetype-sys = { path = "servo-freetype-proxy" } \ No newline at end of file +servo-freetype-sys = { path = "servo-freetype-proxy" } diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs index bdf298c953..3fa3ff5319 100644 --- a/alacritty/src/main.rs +++ b/alacritty/src/main.rs @@ -13,12 +13,7 @@ // 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 -)] +#![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 @@ -225,9 +220,7 @@ fn run(config: Config, message_buffer: MessageBuffer) -> Result<(), Box Result<(), Box handler.insert_blank(Column(arg_or_default!(idx: 0, default: 1) as usize)), 'A' => { handler.move_up(Line(arg_or_default!(idx: 0, default: 1) as usize)); - } + }, 'b' => { if let Some(c) = self._state.preceding_char { for _ in 0..arg_or_default!(idx: 0, default: 1) { @@ -926,7 +926,7 @@ where } else { debug!("tried to repeat with no preceding char"); } - } + }, 'B' | 'e' => handler.move_down(Line(arg_or_default!(idx: 0, default: 1) as usize)), 'c' => handler.identify_terminal(writer), 'C' | 'a' => handler.move_forward(Column(arg_or_default!(idx: 0, default: 1) as usize)), @@ -941,13 +941,13 @@ where }; handler.clear_tabs(mode); - } + }, 'G' | '`' => handler.goto_col(Column(arg_or_default!(idx: 0, default: 1) as usize - 1)), 'H' | 'f' => { let y = arg_or_default!(idx: 0, default: 1) as usize; let x = arg_or_default!(idx: 1, default: 1) as usize; handler.goto(Line(y - 1), Column(x - 1)); - } + }, 'I' => handler.move_forward_tabs(arg_or_default!(idx: 0, default: 1)), 'J' => { let mode = match arg_or_default!(idx: 0, default: 0) { @@ -959,7 +959,7 @@ where }; handler.clear_screen(mode); - } + }, 'K' => { let mode = match arg_or_default!(idx: 0, default: 0) { 0 => LineClearMode::Right, @@ -969,7 +969,7 @@ where }; handler.clear_line(mode); - } + }, 'S' => handler.scroll_up(Line(arg_or_default!(idx: 0, default: 1) as usize)), 'T' => handler.scroll_down(Line(arg_or_default!(idx: 0, default: 1) as usize)), 'L' => handler.insert_blank_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)), @@ -981,7 +981,7 @@ where None => unhandled!(), } } - } + }, 'M' => handler.delete_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)), 'X' => handler.erase_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)), 'P' => handler.delete_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)), @@ -995,7 +995,7 @@ where None => unhandled!(), } } - } + }, 'm' => { // Sometimes a C-style for loop is just what you need let mut i = 0; // C-for initializer @@ -1044,7 +1044,7 @@ where } else { break; } - } + }, 39 => Attr::Foreground(Color::Named(NamedColor::Foreground)), 40 => Attr::Background(Color::Named(NamedColor::Black)), 41 => Attr::Background(Color::Named(NamedColor::Red)), @@ -1062,7 +1062,7 @@ where } else { break; } - } + }, 49 => Attr::Background(Color::Named(NamedColor::Background)), 90 => Attr::Foreground(Color::Named(NamedColor::BrightBlack)), 91 => Attr::Foreground(Color::Named(NamedColor::BrightRed)), @@ -1103,7 +1103,7 @@ where let bottom = Line(arg1); handler.set_scrolling_region(top..bottom); - } + }, 's' => handler.save_cursor_position(), 'u' => handler.restore_cursor_position(), 'q' => { @@ -1217,7 +1217,7 @@ fn parse_color(attrs: &[i64], i: &mut usize) -> Option { }, } } - } + }, _ => { debug!("Unexpected color attr: {}", attrs[*i + 1]); None @@ -1502,6 +1502,7 @@ mod tests { fn lines(&self) -> Line { Line(200) } + fn cols(&self) -> Column { Column(90) } @@ -1518,10 +1519,7 @@ mod tests { } assert_eq!(handler.index, CharsetIndex::G0); - assert_eq!( - handler.charset, - StandardCharset::SpecialCharacterAndLineDrawing - ); + assert_eq!(handler.charset, StandardCharset::SpecialCharacterAndLineDrawing); } #[test] @@ -1535,10 +1533,7 @@ mod tests { } assert_eq!(handler.index, CharsetIndex::G1); - assert_eq!( - handler.charset, - StandardCharset::SpecialCharacterAndLineDrawing - ); + assert_eq!(handler.charset, StandardCharset::SpecialCharacterAndLineDrawing); let mut handler = CharsetHandler::default(); parser.advance(&mut handler, BYTES[3], &mut Void); @@ -1548,26 +1543,12 @@ mod tests { #[test] fn parse_valid_rgb_color() { - assert_eq!( - parse_rgb_color(b"rgb:11/aa/ff"), - Some(Rgb { - r: 0x11, - g: 0xaa, - b: 0xff - }) - ); + assert_eq!(parse_rgb_color(b"rgb:11/aa/ff"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff })); } #[test] fn parse_valid_rgb_color2() { - assert_eq!( - parse_rgb_color(b"#11aaff"), - Some(Rgb { - r: 0x11, - g: 0xaa, - b: 0xff - }) - ); + assert_eq!(parse_rgb_color(b"#11aaff"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff })); } #[test] diff --git a/alacritty_terminal/src/cursor.rs b/alacritty_terminal/src/cursor.rs index c7b5036972..93f2bd30e1 100644 --- a/alacritty_terminal/src/cursor.rs +++ b/alacritty_terminal/src/cursor.rs @@ -62,7 +62,7 @@ pub fn get_underline_cursor_glyph(width: i32, line_width: i32) -> RasterizedGlyp let buf = vec![255u8; (width * line_width * 3) as usize]; // Create a custom glyph with the rectangle data attached to it - RasterizedGlyph { c: ' '.into(), top: line_width, left: 0, height: line_width, width, buf } + RasterizedGlyph { c: ' ', top: line_width, left: 0, height: line_width, width, buf } } // Returns a custom beam cursor character @@ -71,7 +71,7 @@ pub fn get_beam_cursor_glyph(height: i32, line_width: i32) -> RasterizedGlyph { let buf = vec![255u8; (line_width * height * 3) as usize]; // Create a custom glyph with the rectangle data attached to it - RasterizedGlyph { c: ' '.into(), top: height, left: 0, height, width: line_width, buf } + RasterizedGlyph { c: ' ', top: height, left: 0, height, width: line_width, buf } } // Returns a custom box cursor character @@ -93,7 +93,7 @@ pub fn get_box_cursor_glyph(height: i32, width: i32, line_width: i32) -> Rasteri } // Create a custom glyph with the rectangle data attached to it - RasterizedGlyph { c: ' '.into(), top: height, left: 0, height, width, buf } + RasterizedGlyph { c: ' ', top: height, left: 0, height, width, buf } } // Returns a custom block cursor character @@ -102,5 +102,5 @@ pub fn get_block_cursor_glyph(height: i32, width: i32) -> RasterizedGlyph { let buf = vec![255u8; (width * height * 3) as usize]; // Create a custom glyph with the rectangle data attached to it - RasterizedGlyph { c: ' '.into(), top: height, left: 0, height, width, buf } + RasterizedGlyph { c: ' ', top: height, left: 0, height, width, buf } } diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 0d2ad06e7d..d4c3124a4e 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -24,7 +24,7 @@ use glutin::EventsLoop; use parking_lot::MutexGuard; use crate::config::{Config, StartupMode}; -use crate::index::{Line}; +use crate::index::Line; use crate::message_bar::Message; use crate::meter::Meter; use crate::renderer::rects::{Rect, Rects}; @@ -455,8 +455,7 @@ impl Display { } self.window.resize(psize); - self.renderer - .resize(psize, self.size_info.padding_x, self.size_info.padding_y); + self.renderer.resize(psize, self.size_info.padding_x, self.size_info.padding_y); } } @@ -560,8 +559,7 @@ impl Display { rects.push(rect, message.color()); // Draw rectangles including the new background - self.renderer - .draw_rects(config, &size_info, visual_bell_intensity, rects); + self.renderer.draw_rects(config, &size_info, visual_bell_intensity, rects); // Relay messages to the user let mut offset = 1; @@ -578,8 +576,7 @@ impl Display { } } else { // Draw rectangles - self.renderer - .draw_rects(config, &size_info, visual_bell_intensity, rects); + self.renderer.draw_rects(config, &size_info, visual_bell_intensity, rects); } // Draw render timer @@ -609,8 +606,7 @@ impl Display { let nspot_x = f64::from(px + point.col.0 as f32 * cw); let nspot_y = f64::from(py + (point.line.0 + 1) as f32 * ch); - self.window() - .set_ime_spot(PhysicalPosition::from((nspot_x, nspot_y)).to_logical(dpr)); + self.window().set_ime_spot(PhysicalPosition::from((nspot_x, nspot_y)).to_logical(dpr)); } #[cfg(not(any(target_os = "macos", target_os = "windows")))] diff --git a/alacritty_terminal/src/event.rs b/alacritty_terminal/src/event.rs index 6191c3216c..bb34d2f29e 100644 --- a/alacritty_terminal/src/event.rs +++ b/alacritty_terminal/src/event.rs @@ -73,11 +73,7 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> { } fn selection_is_empty(&self) -> bool { - self.terminal - .selection() - .as_ref() - .map(Selection::is_empty) - .unwrap_or(true) + self.terminal.selection().as_ref().map(Selection::is_empty).unwrap_or(true) } fn clear_selection(&mut self) { @@ -121,8 +117,7 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> { } fn mouse_coords(&self) -> Option { - self.terminal - .pixels_to_coords(self.mouse.x as usize, self.mouse.y as usize) + self.terminal.pixels_to_coords(self.mouse.x as usize, self.mouse.y as usize) } #[inline] @@ -186,10 +181,7 @@ impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> { match start_daemon(&alacritty, &args) { Ok(_) => debug!("Started new Alacritty process: {} {:?}", alacritty, args), - Err(_) => warn!( - "Unable to start new Alacritty process: {} {:?}", - alacritty, args - ), + Err(_) => warn!("Unable to start new Alacritty process: {} {:?}", alacritty, args), } } @@ -377,53 +369,39 @@ impl Processor { .send(lsize.to_physical(processor.ctx.size_info.dpr)) .expect("send new size"); processor.ctx.terminal.dirty = true; - } + }, KeyboardInput { input, .. } => { processor.process_key(input); if input.state == ElementState::Pressed { // Hide cursor while typing *hide_mouse = true; } - } + }, ReceivedCharacter(c) => { processor.received_char(c); - } - MouseInput { - state, - button, - modifiers, - .. - } => { + }, + MouseInput { state, button, modifiers, .. } => { if !cfg!(target_os = "macos") || *window_is_focused { *hide_mouse = false; processor.mouse_input(state, button, modifiers); processor.ctx.terminal.dirty = true; } - } - CursorMoved { - position: lpos, - modifiers, - .. - } => { + }, + CursorMoved { position: lpos, modifiers, .. } => { let (x, y) = lpos.to_physical(processor.ctx.size_info.dpr).into(); let x: i32 = limit(x, 0, processor.ctx.size_info.width as i32); let y: i32 = limit(y, 0, processor.ctx.size_info.height as i32); *hide_mouse = false; processor.mouse_moved(x as usize, y as usize, modifiers); - } - MouseWheel { - delta, - phase, - modifiers, - .. - } => { + }, + MouseWheel { delta, phase, modifiers, .. } => { *hide_mouse = false; processor.on_mouse_wheel(delta, phase, modifiers); - } + }, Refresh => { processor.ctx.terminal.dirty = true; - } + }, Focused(is_focused) => { *window_is_focused = is_focused; @@ -437,19 +415,19 @@ impl Processor { } processor.on_focus_change(is_focused); - } + }, DroppedFile(path) => { use crate::input::ActionContext; let path: String = path.to_string_lossy().into(); processor.ctx.write_to_pty(path.into_bytes()); - } + }, HiDpiFactorChanged(new_dpr) => { processor.ctx.size_info.dpr = new_dpr; processor.ctx.terminal.dirty = true; - } + }, _ => (), } - } + }, Event::Awakened => { processor.ctx.terminal.dirty = true; }, diff --git a/alacritty_terminal/src/event_loop.rs b/alacritty_terminal/src/event_loop.rs index 6f61f179f5..4941b47962 100644 --- a/alacritty_terminal/src/event_loop.rs +++ b/alacritty_terminal/src/event_loop.rs @@ -136,10 +136,7 @@ impl State { impl Writing { #[inline] fn new(c: Cow<'static, [u8]>) -> Writing { - Writing { - source: c, - written: 0, - } + Writing { source: c, written: 0 } } #[inline] @@ -218,12 +215,7 @@ where } self.poll - .reregister( - &self.rx, - token, - Ready::readable(), - PollOpt::edge() | PollOpt::oneshot(), - ) + .reregister(&self.rx, token, Ready::readable(), PollOpt::edge() | PollOpt::oneshot()) .unwrap(); true @@ -371,7 +363,7 @@ where if !self.channel_event(channel_token, &mut state) { break 'event_loop; } - } + }, #[cfg(unix)] token if token == self.pty.child_event_token() => { @@ -380,7 +372,7 @@ where self.display.notify(); break 'event_loop; } - } + }, token if token == self.pty.read_token() @@ -430,9 +422,7 @@ where interest.insert(Ready::writable()); } // Reregister with new interest - self.pty - .reregister(&self.poll, interest, poll_opts) - .unwrap(); + self.pty.reregister(&self.poll, interest, poll_opts).unwrap(); } // The evented instances are not dropped here so deregister them explicitly diff --git a/alacritty_terminal/src/grid/mod.rs b/alacritty_terminal/src/grid/mod.rs index 73be6cd599..1925a6f40d 100644 --- a/alacritty_terminal/src/grid/mod.rs +++ b/alacritty_terminal/src/grid/mod.rs @@ -175,7 +175,7 @@ impl Grid { max((self.display_offset as isize) + count, 0isize) as usize, self.scroll_limit, ); - } + }, Scroll::PageUp => { self.display_offset = min(self.display_offset + self.lines.0, self.scroll_limit); }, @@ -240,8 +240,7 @@ impl Grid { let lines_added = new_line_count - self.lines; // Need to "resize" before updating buffer - self.raw - .grow_visible_lines(new_line_count, Row::new(self.cols, template)); + self.raw.grow_visible_lines(new_line_count, Row::new(self.cols, template)); self.lines = new_line_count; // Move existing lines up if there is no scrollback to fill new lines @@ -569,10 +568,7 @@ impl Grid { T: Copy + GridCell, { let history_size = self.raw.len().saturating_sub(*self.lines); - self.raw.initialize( - self.max_scroll_limit - history_size, - Row::new(self.cols, template), - ); + self.raw.initialize(self.max_scroll_limit - history_size, Row::new(self.cols, template)); } /// This is used only for truncating before saving ref-tests @@ -614,7 +610,7 @@ impl<'a, T> Iterator for GridIterator<'a, T> { self.cur.line -= 1; self.cur.col = Column(0); Some(&self.grid[self.cur.line][self.cur.col]) - } + }, _ => { self.cur.col += Column(1); Some(&self.grid[self.cur.line][self.cur.col]) @@ -628,15 +624,12 @@ impl<'a, T> BidirectionalIterator for GridIterator<'a, T> { let num_cols = self.grid.num_cols(); match self.cur { - Point { - line, - col: Column(0), - } if line == self.grid.len() - 1 => None, + Point { line, col: Column(0) } if line == self.grid.len() - 1 => None, Point { col: Column(0), .. } => { self.cur.line += 1; self.cur.col = num_cols - Column(1); Some(&self.grid[self.cur.line][self.cur.col]) - } + }, _ => { self.cur.col -= Column(1); Some(&self.grid[self.cur.line][self.cur.col]) @@ -864,13 +857,7 @@ impl<'a, T: 'a> DisplayIter<'a, T> { let col = Column(0); let line = Line(0); - DisplayIter { - grid, - offset, - col, - limit, - line, - } + DisplayIter { grid, offset, col, limit, line } } pub fn offset(&self) -> usize { diff --git a/alacritty_terminal/src/grid/row.rs b/alacritty_terminal/src/grid/row.rs index 3cbf64ee36..f82e7eaa7a 100644 --- a/alacritty_terminal/src/grid/row.rs +++ b/alacritty_terminal/src/grid/row.rs @@ -71,11 +71,7 @@ impl Row { // Split off cells for a new row let mut new_row = self.inner.split_off(cols.0); - let index = new_row - .iter() - .rposition(|c| !c.is_empty()) - .map(|i| i + 1) - .unwrap_or(0); + let index = new_row.iter().rposition(|c| !c.is_empty()).map(|i| i + 1).unwrap_or(0); new_row.truncate(index); self.occ = min(self.occ, cols.0); diff --git a/alacritty_terminal/src/input.rs b/alacritty_terminal/src/input.rs index d319cf9246..3b6a286c86 100644 --- a/alacritty_terminal/src/input.rs +++ b/alacritty_terminal/src/input.rs @@ -269,7 +269,7 @@ impl Action { Action::Esc(ref s) => { ctx.scroll(Scroll::Bottom); ctx.write_to_pty(s.clone().into_bytes()) - } + }, Action::Copy => { ctx.copy_selection(ClipboardType::Clipboard); }, @@ -283,7 +283,7 @@ impl Action { let text = ctx.terminal_mut().clipboard().load(ClipboardType::Selection); self.paste(ctx, &text); } - } + }, Action::Command(ref program, ref args) => { trace!("Running command {} with args {:?}", program, args); @@ -328,19 +328,19 @@ impl Action { }, Action::ScrollToTop => { ctx.scroll(Scroll::Top); - } + }, Action::ScrollToBottom => { ctx.scroll(Scroll::Bottom); - } + }, Action::ClearHistory => { ctx.terminal_mut().clear_screen(ClearMode::Saved); - } + }, Action::ClearLogNotice => { ctx.terminal_mut().message_buffer_mut().pop(); - } + }, Action::SpawnNewInstance => { ctx.spawn_new_instance(); - } + }, Action::None => (), } } @@ -678,11 +678,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { match start_daemon(launcher.program(), &args) { Ok(_) => debug!("Launched {} with args {:?}", launcher.program(), args), - Err(_) => warn!( - "Unable to launch {} with args {:?}", - launcher.program(), - args - ), + Err(_) => warn!("Unable to launch {} with args {:?}", launcher.program(), args), } Some(()) @@ -698,16 +694,16 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { MouseScrollDelta::LineDelta(_columns, lines) => { let new_scroll_px = lines * self.ctx.size_info().cell_height; self.scroll_terminal(modifiers, 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(modifiers, lpos.y as i32); - } + }, _ => (), } }, @@ -793,7 +789,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { ElementState::Pressed => { self.process_mouse_bindings(modifiers, button); self.on_mouse_press(button, modifiers, point); - } + }, ElementState::Released => self.on_mouse_release(button, modifiers, point), } } @@ -812,7 +808,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { if self.process_key_bindings(input) { *self.ctx.suppress_chars() = true; } - } + }, ElementState::Released => *self.ctx.suppress_chars() = false, } } @@ -1185,8 +1181,7 @@ mod tests { } fn mouse_coords(&self) -> Option { - self.terminal - .pixels_to_coords(self.mouse.x as usize, self.mouse.y as usize) + self.terminal.pixels_to_coords(self.mouse.x as usize, self.mouse.y as usize) } #[inline] diff --git a/alacritty_terminal/src/lib.rs b/alacritty_terminal/src/lib.rs index c37ccbc055..0bada53545 100644 --- a/alacritty_terminal/src/lib.rs +++ b/alacritty_terminal/src/lib.rs @@ -13,12 +13,7 @@ // 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 -)] +#![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))] diff --git a/alacritty_terminal/src/message_bar.rs b/alacritty_terminal/src/message_bar.rs index ddeb38dd3f..8883dcb004 100644 --- a/alacritty_terminal/src/message_bar.rs +++ b/alacritty_terminal/src/message_bar.rs @@ -455,14 +455,8 @@ mod test { let msg = Message::new(String::from("test"), color::RED); message_buffer.tx().send(msg).unwrap(); } - message_buffer - .tx() - .send(Message::new(String::from("other"), color::RED)) - .unwrap(); - message_buffer - .tx() - .send(Message::new(String::from("test"), color::YELLOW)) - .unwrap(); + message_buffer.tx().send(Message::new(String::from("other"), color::RED)).unwrap(); + message_buffer.tx().send(Message::new(String::from("test"), color::YELLOW)).unwrap(); let _ = message_buffer.message(); message_buffer.pop(); diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index bd329ca4fc..0e94910f2e 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -98,7 +98,7 @@ impl ::std::fmt::Display for Error { match *self { Error::ShaderCreation(ref err) => { write!(f, "There was an error initializing the shaders: {}", err) - } + }, } } } @@ -145,8 +145,8 @@ pub struct Glyph { tex_id: GLuint, top: f32, left: f32, - pub width: f32, - pub height: f32, + width: f32, + height: f32, uv_bot: f32, uv_left: f32, uv_width: f32, @@ -165,19 +165,19 @@ pub struct GlyphCache { cursor_cache: HashMap>, /// Rasterizer for loading new glyphs - pub rasterizer: Rasterizer, + rasterizer: Rasterizer, /// regular font - pub font_key: FontKey, + font_key: FontKey, /// italic font - pub italic_key: FontKey, + italic_key: FontKey, /// bold font - pub bold_key: FontKey, + bold_key: FontKey, /// font size - pub font_size: font::Size, + font_size: font::Size, /// glyph offset glyph_offset: Delta, @@ -187,9 +187,7 @@ pub struct GlyphCache { impl GlyphCache { pub fn new( - mut rasterizer: Rasterizer, - font: &config::Font, - loader: &mut L, + mut rasterizer: Rasterizer, font: &config::Font, loader: &mut L, ) -> Result where L: LoadGlyph, @@ -360,11 +358,10 @@ impl GlyphCache { let font = font.to_owned().with_size(size); let (regular, bold, italic) = Self::compute_font_keys(&font, &mut self.rasterizer)?; - self.rasterizer.get_glyph(GlyphKey { - font_key: regular, - c: 'm'.into(), - size: font.size, - })?; + #[cfg(not(feature = "hb-ft"))] + self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?; + #[cfg(feature = "hb-ft")] + self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 1u32, size: font.size })?; let metrics = self.rasterizer.metrics(regular, size)?; info!("Font size changed to {:?} with DPR of {}", font.size, dpr); @@ -392,7 +389,10 @@ impl GlyphCache { let regular_desc = GlyphCache::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal); let regular = rasterizer.load_font(®ular_desc, font.size)?; - rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm'.into(), size: font.size })?; + #[cfg(not(feature = "hb-ft"))] + rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?; + #[cfg(feature = "hb-ft")] + rasterizer.get_glyph(GlyphKey { font_key: regular, c: 1u32, size: font.size })?; rasterizer.metrics(regular, font.size) } @@ -478,7 +478,6 @@ impl Batch { Batch { tex: 0, instances: Vec::with_capacity(BATCH_MAX) } } - pub fn add_item(&mut self, cell: &RenderableCell, glyph: &Glyph) { if self.is_empty() { self.tex = glyph.tex_id; @@ -694,8 +693,8 @@ impl QuadRenderer { | DebouncedEvent::Write(_) | DebouncedEvent::Chmod(_) => { msg_tx.send(Msg::ShaderReload).expect("msg send ok"); - } - _ => {} + }, + _ => {}, } } }); @@ -860,11 +859,11 @@ impl QuadRenderer { info!("... successfully reloaded shaders"); (program, rect_program) - } + }, (Err(err), _) | (_, Err(err)) => { error!("{}", err); return; - } + }, }; self.active_tex = 0; @@ -1145,7 +1144,7 @@ impl<'a> RenderApi<'a> { }); self.add_render_item(&cell, &glyph); return; - } + }, RenderableCellContent::Chars(chars) => chars, }; @@ -1171,7 +1170,7 @@ impl<'a> RenderApi<'a> { chars[0] = ' '; } - let mut glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: chars[0].into() }; + 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); @@ -1216,7 +1215,7 @@ fn load_glyph( atlas.push(new); } load_glyph(active_tex, atlas, current_atlas, rasterized) - } + }, Err(AtlasInsertError::GlyphTooLarge) => Glyph { tex_id: atlas[*current_atlas].id, top: 0.0, @@ -1566,7 +1565,7 @@ impl ::std::fmt::Display for ShaderCreationError { ShaderCreationError::Io(ref err) => write!(f, "Couldn't read shader: {}", err), ShaderCreationError::Compile(ref path, ref log) => { write!(f, "Failed compiling shader at {}: {}", path.display(), log) - } + }, ShaderCreationError::Link(ref log) => write!(f, "Failed linking shader: {}", log), } } diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index 9b97bc3cd9..e5a72961e3 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -30,12 +30,7 @@ pub struct Rect { impl Rect { pub fn new(x: T, y: T, width: T, height: T) -> Self { - Rect { - x, - y, - width, - height, - } + Rect { x, y, width, height } } } diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 23b2bd2362..2e019e6e5d 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -248,16 +248,16 @@ impl<'a> RenderableCellsIter<'a> { let locations = match (start_line, end_line) { (ViewportPosition::Visible(start_line), ViewportPosition::Visible(end_line)) => { Some((start_line, span.start.col, end_line, span.end.col)) - } + }, (ViewportPosition::Visible(start_line), ViewportPosition::Above) => { Some((start_line, span.start.col, Line(0), limit_end)) - } + }, (ViewportPosition::Below, ViewportPosition::Visible(end_line)) => { Some((grid.num_lines(), limit_start, end_line, span.end.col)) - } + }, (ViewportPosition::Below, ViewportPosition::Above) => { Some((grid.num_lines(), limit_start, Line(0), limit_end)) - } + }, _ => None, }; @@ -303,23 +303,13 @@ impl<'a> RenderableCellsIter<'a> { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub enum RenderableCellContent { Chars([char; cell::MAX_ZEROWIDTH_CHARS + 1]), Cursor(CursorKey), } -impl Into for [char; cell::MAX_ZEROWIDTH_CHARS + 1] { - fn into(self) -> RenderableCellContent { - RenderableCellContent::Chars(self) - } -} -impl Into for CursorKey { - fn into(self) -> RenderableCellContent { - RenderableCellContent::Cursor(self) - } -} -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] pub struct RenderableCell { /// A _Display_ line (not necessarily an _Active_ line) pub line: Line, @@ -387,7 +377,7 @@ impl RenderableCell { && config.colors.primary.bright_foreground.is_none() => { colors[NamedColor::DimForeground] - } + }, // Draw bold text in bright colors *and* contains bold flag. (true, cell::Flags::BOLD) => colors[ansi.to_bright()], // Cell is marked as dim and not bold @@ -395,7 +385,7 @@ impl RenderableCell { // None of the above, keep original color. _ => colors[ansi], } - } + }, Color::Indexed(idx) => { let idx = match ( config.draw_bold_text_with_bright_colors(), @@ -409,7 +399,7 @@ impl RenderableCell { }; colors[idx] - } + }, } } @@ -901,7 +891,7 @@ impl VisualBell { self.start_time = None; } false - } + }, None => true, } } @@ -946,12 +936,12 @@ impl VisualBell { let inverse_intensity = match self.animation { VisualBellAnimation::Ease | VisualBellAnimation::EaseOut => { cubic_bezier(0.25, 0.1, 0.25, 1.0, time) - } + }, VisualBellAnimation::EaseOutSine => cubic_bezier(0.39, 0.575, 0.565, 1.0, time), VisualBellAnimation::EaseOutQuad => cubic_bezier(0.25, 0.46, 0.45, 0.94, time), VisualBellAnimation::EaseOutCubic => { cubic_bezier(0.215, 0.61, 0.355, 1.0, time) - } + }, VisualBellAnimation::EaseOutQuart => cubic_bezier(0.165, 0.84, 0.44, 1.0, time), VisualBellAnimation::EaseOutQuint => cubic_bezier(0.23, 1.0, 0.32, 1.0, time), VisualBellAnimation::EaseOutExpo => cubic_bezier(0.19, 1.0, 0.22, 1.0, time), @@ -962,7 +952,7 @@ impl VisualBell { // Since we want the `intensity` of the VisualBell to decay over // `time`, we subtract the `inverse_intensity` from 1.0. 1.0 - inverse_intensity - } + }, } } @@ -1327,7 +1317,7 @@ impl Term { // Selection within single line 0 => { res.append(false, &self.grid, &self.tabs, start.line, start.col..end.col); - } + }, // Selection ends on line following start 1 => { @@ -1336,7 +1326,7 @@ impl Term { // Starting line res.append(false, &self.grid, &self.tabs, start.line, limit_start..start.col); - } + }, // Multi line selection _ => { @@ -1350,7 +1340,7 @@ impl Term { // Starting line res.append(false, &self.grid, &self.tabs, start.line, limit_start..start.col); - } + }, } Some(res) @@ -1847,12 +1837,12 @@ impl ansi::Handler for Term { match arg { 5 => { let _ = writer.write_all(b"\x1b[0n"); - } + }, 6 => { let pos = self.cursor.point; let response = format!("\x1b[{};{}R", pos.line + 1, pos.col + 1); let _ = writer.write_all(response.as_bytes()); - } + }, _ => debug!("unknown device status query: {}", arg), }; } @@ -2103,19 +2093,19 @@ impl ansi::Handler for Term { for cell in &mut row[col..] { cell.reset(&template); } - } + }, ansi::LineClearMode::Left => { let row = &mut self.grid[self.cursor.point.line]; for cell in &mut row[..=col] { cell.reset(&template); } - } + }, ansi::LineClearMode::All => { let row = &mut self.grid[self.cursor.point.line]; for cell in &mut row[..] { cell.reset(&template); } - } + }, } } @@ -2173,7 +2163,7 @@ impl ansi::Handler for Term { .region_mut((self.cursor.point.line + 1)..) .each(|cell| cell.reset(&template)); } - } + }, ansi::ClearMode::All => self.grid.region_mut(..).each(|c| c.reset(&template)), ansi::ClearMode::Above => { // If clearing more than one line @@ -2188,7 +2178,7 @@ impl ansi::Handler for Term { for cell in &mut self.grid[self.cursor.point.line][..end] { cell.reset(&template); } - } + }, ansi::ClearMode::Saved => self.grid.clear_history(), } } @@ -2200,10 +2190,10 @@ impl ansi::Handler for Term { ansi::TabulationClearMode::Current => { let column = self.cursor.point.col; self.tabs[column] = false; - } + }, ansi::TabulationClearMode::All => { self.tabs.clear_all(); - } + }, } } @@ -2253,7 +2243,7 @@ impl ansi::Handler for Term { self.cursor.template.fg = Color::Named(NamedColor::Foreground); self.cursor.template.bg = Color::Named(NamedColor::Background); self.cursor.template.flags = cell::Flags::empty(); - } + }, Attr::Reverse => self.cursor.template.flags.insert(cell::Flags::INVERSE), Attr::CancelReverse => self.cursor.template.flags.remove(cell::Flags::INVERSE), Attr::Bold => self.cursor.template.flags.insert(cell::Flags::BOLD), @@ -2261,7 +2251,7 @@ impl ansi::Handler for Term { Attr::Dim => self.cursor.template.flags.insert(cell::Flags::DIM), Attr::CancelBoldDim => { self.cursor.template.flags.remove(cell::Flags::BOLD | cell::Flags::DIM) - } + }, Attr::Italic => self.cursor.template.flags.insert(cell::Flags::ITALIC), Attr::CancelItalic => self.cursor.template.flags.remove(cell::Flags::ITALIC), Attr::Underscore => self.cursor.template.flags.insert(cell::Flags::UNDERLINE), @@ -2272,7 +2262,7 @@ impl ansi::Handler for Term { Attr::CancelStrike => self.cursor.template.flags.remove(cell::Flags::STRIKEOUT), _ => { debug!("Term got unhandled attr: {:?}", attr); - } + }, } } @@ -2287,21 +2277,21 @@ impl ansi::Handler for Term { self.swap_alt(); self.save_cursor_position(); } - } + }, ansi::Mode::ShowCursor => self.mode.insert(TermMode::SHOW_CURSOR), ansi::Mode::CursorKeys => self.mode.insert(TermMode::APP_CURSOR), ansi::Mode::ReportMouseClicks => { self.mode.insert(TermMode::MOUSE_REPORT_CLICK); self.set_mouse_cursor(MouseCursor::Default); - } + }, ansi::Mode::ReportCellMouseMotion => { self.mode.insert(TermMode::MOUSE_DRAG); self.set_mouse_cursor(MouseCursor::Default); - } + }, ansi::Mode::ReportAllMouseMotion => { self.mode.insert(TermMode::MOUSE_MOTION); self.set_mouse_cursor(MouseCursor::Default); - } + }, ansi::Mode::ReportFocusInOut => self.mode.insert(TermMode::FOCUS_IN_OUT), ansi::Mode::BracketedPaste => self.mode.insert(TermMode::BRACKETED_PASTE), ansi::Mode::SgrMouse => self.mode.insert(TermMode::SGR_MOUSE), @@ -2312,7 +2302,7 @@ impl ansi::Handler for Term { ansi::Mode::Insert => self.mode.insert(TermMode::INSERT), // heh ansi::Mode::BlinkingCursor => { trace!("... unimplemented mode"); - } + }, } } @@ -2327,21 +2317,21 @@ impl ansi::Handler for Term { self.swap_alt(); self.restore_cursor_position(); } - } + }, ansi::Mode::ShowCursor => self.mode.remove(TermMode::SHOW_CURSOR), ansi::Mode::CursorKeys => self.mode.remove(TermMode::APP_CURSOR), ansi::Mode::ReportMouseClicks => { self.mode.remove(TermMode::MOUSE_REPORT_CLICK); self.set_mouse_cursor(MouseCursor::Text); - } + }, ansi::Mode::ReportCellMouseMotion => { self.mode.remove(TermMode::MOUSE_DRAG); self.set_mouse_cursor(MouseCursor::Text); - } + }, ansi::Mode::ReportAllMouseMotion => { self.mode.remove(TermMode::MOUSE_MOTION); self.set_mouse_cursor(MouseCursor::Text); - } + }, ansi::Mode::ReportFocusInOut => self.mode.remove(TermMode::FOCUS_IN_OUT), ansi::Mode::BracketedPaste => self.mode.remove(TermMode::BRACKETED_PASTE), ansi::Mode::SgrMouse => self.mode.remove(TermMode::SGR_MOUSE), @@ -2352,7 +2342,7 @@ impl ansi::Handler for Term { ansi::Mode::Insert => self.mode.remove(TermMode::INSERT), ansi::Mode::BlinkingCursor => { trace!("... unimplemented mode"); - } + }, } } diff --git a/alacritty_terminal/src/tty/unix.rs b/alacritty_terminal/src/tty/unix.rs index 18a2a5d11f..cafa7027bb 100644 --- a/alacritty_terminal/src/tty/unix.rs +++ b/alacritty_terminal/src/tty/unix.rs @@ -103,13 +103,7 @@ fn get_pw_entry(buf: &mut [i8; 1024]) -> Passwd<'_> { // Try and read the pw file. let uid = unsafe { libc::getuid() }; let status = unsafe { - libc::getpwuid_r( - uid, - &mut entry, - buf.as_mut_ptr() as *mut _, - buf.len(), - &mut res, - ) + libc::getpwuid_r(uid, &mut entry, buf.as_mut_ptr() as *mut _, buf.len(), &mut res) }; if status < 0 { @@ -258,7 +252,7 @@ pub fn new(config: &Config, size: &T, window_id: Option) -> }; pty.resize(size); pty - } + }, Err(err) => { die!("Failed to spawn command: {}", err); }, diff --git a/alacritty_terminal/src/tty/windows/conpty.rs b/alacritty_terminal/src/tty/windows/conpty.rs index dc02469bf5..bd602c35e6 100644 --- a/alacritty_terminal/src/tty/windows/conpty.rs +++ b/alacritty_terminal/src/tty/windows/conpty.rs @@ -138,15 +138,7 @@ pub fn new<'a>(config: &Config, size: &SizeInfo, _window_id: Option) -> O let mut startup_info_ex: STARTUPINFOEXW = Default::default(); -<<<<<<< HEAD:alacritty_terminal/src/tty/windows/conpty.rs let title = config.window.title.as_ref().map(String::as_str).unwrap_or("Alacritty"); -======= - let title = options - .title - .as_ref() - .map(|w| w.as_str()) - .unwrap_or("Alacritty"); ->>>>>>> Tried setting scale and ppem but it didn't change anything.:src/tty/windows/conpty.rs let title = U16CString::from_str(title).unwrap(); startup_info_ex.StartupInfo.lpTitle = title.as_ptr() as LPWSTR; diff --git a/alacritty_terminal/src/tty/windows/winpty.rs b/alacritty_terminal/src/tty/windows/winpty.rs index 2915e0c8c9..8795ca6df6 100644 --- a/alacritty_terminal/src/tty/windows/winpty.rs +++ b/alacritty_terminal/src/tty/windows/winpty.rs @@ -27,7 +27,6 @@ use winapi::um::winbase::FILE_FLAG_OVERLAPPED; use winpty::Config as WinptyConfig; use winpty::{ConfigFlags, MouseMode, SpawnConfig, SpawnFlags, Winpty}; -use crate::cli::Options; use crate::config::{Config, Shell}; use crate::display::OnResize; use crate::term::SizeInfo; @@ -48,13 +47,7 @@ unsafe impl<'a> Sync for Agent<'a> {} impl<'a> Agent<'a> { pub fn new(winpty: Winpty<'a>) -> Self { -<<<<<<< HEAD:alacritty_terminal/src/tty/windows/winpty.rs Self { winpty: Box::into_raw(Box::new(winpty)) } -======= - Self { - winpty: Box::into_raw(Box::new(winpty)), - } ->>>>>>> Tried setting scale and ppem but it didn't change anything.:src/tty/windows/winpty.rs } /// Get immutable access to Winpty. @@ -101,14 +94,7 @@ pub fn new<'a>(config: &Config, size: &SizeInfo, _window_id: Option) -> P cmdline.insert(0, shell.program.to_string()); // Warning, here be borrow hell -<<<<<<< HEAD:alacritty_terminal/src/tty/windows/winpty.rs let cwd = config.working_directory().as_ref().map(|dir| canonicalize(dir).unwrap()); -======= - let cwd = options - .working_dir - .as_ref() - .map(|dir| canonicalize(dir).unwrap()); ->>>>>>> Tried setting scale and ppem but it didn't change anything.:src/tty/windows/winpty.rs let cwd = cwd.as_ref().map(|dir| dir.to_str().unwrap()); // Spawn process From d3513f3a5ade15dcab6fb2f69b757f942af6f409 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 25 Jul 2019 17:00:55 -0700 Subject: [PATCH 24/84] Continuing clean up of PR. --- alacritty_terminal/src/input.rs | 6 +- alacritty_terminal/src/renderer/mod.rs | 4 +- copypasta/src/macos.rs | 330 --- copypasta/src/x11.rs | 170 -- font/Cargo.toml | 1 - font/src/darwin/mod.rs | 78 +- font/src/ft/mod.rs | 25 +- font/src/lib.rs | 34 +- font/src/rusttype/mod.rs | 206 -- src/cli.rs | 258 --- src/config/bindings.rs | 217 -- src/config/mod.rs | 2883 ------------------------ src/grid/storage.rs | 928 -------- src/selection.rs | 647 ------ src/term/color.rs | 244 -- src/url.rs | 307 --- winpty/src/lib.rs | 7 +- winpty/src/windows.rs | 6 - 18 files changed, 24 insertions(+), 6327 deletions(-) delete mode 100644 copypasta/src/macos.rs delete mode 100644 copypasta/src/x11.rs delete mode 100644 font/src/rusttype/mod.rs delete mode 100644 src/cli.rs delete mode 100644 src/config/bindings.rs delete mode 100644 src/config/mod.rs delete mode 100644 src/grid/storage.rs delete mode 100644 src/selection.rs delete mode 100644 src/term/color.rs delete mode 100644 src/url.rs diff --git a/alacritty_terminal/src/input.rs b/alacritty_terminal/src/input.rs index 3b6a286c86..83ab026141 100644 --- a/alacritty_terminal/src/input.rs +++ b/alacritty_terminal/src/input.rs @@ -301,10 +301,10 @@ impl Action { }, Action::Hide => { ctx.hide_window(); - } + }, Action::Quit => { ctx.terminal_mut().exit(); - } + }, Action::IncreaseFontSize => { ctx.terminal_mut().change_font_size(FONT_SIZE_STEP); }, @@ -316,7 +316,7 @@ impl Action { }, Action::ScrollPageUp => { ctx.scroll(Scroll::PageUp); - } + }, Action::ScrollPageDown => { ctx.scroll(Scroll::PageDown); }, diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 0e94910f2e..ebfeb519a1 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -187,7 +187,9 @@ pub struct GlyphCache { impl GlyphCache { pub fn new( - mut rasterizer: Rasterizer, font: &config::Font, loader: &mut L, + mut rasterizer: Rasterizer, + font: &config::Font, + loader: &mut L, ) -> Result where L: LoadGlyph, diff --git a/copypasta/src/macos.rs b/copypasta/src/macos.rs deleted file mode 100644 index 23ffe1c74e..0000000000 --- a/copypasta/src/macos.rs +++ /dev/null @@ -1,330 +0,0 @@ -//! Clipboard access on macOS -//! -//! Implemented according to -//! https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/PasteboardGuide106/Articles/pbReading.html#//apple_ref/doc/uid/TP40008123-SW1 - -mod ns { - extern crate objc_foundation; - extern crate objc_id; - - #[link(name = "AppKit", kind = "framework")] - extern "C" {} - - use std::mem; - - use self::objc_foundation::{INSArray, INSObject, INSString}; - use self::objc_foundation::{NSArray, NSDictionary, NSObject, NSString}; - use self::objc_id::{Id, Owned}; - use objc::runtime::{Class, Object}; - - /// Rust API for NSPasteboard - pub struct Pasteboard(Id); - - /// Errors occurring when creating a Pasteboard - #[derive(Debug)] - pub enum NewPasteboardError { - GetPasteboardClass, - LoadGeneralPasteboard, - } - - /// Errors occurring when reading a string from the pasteboard - #[derive(Debug)] - pub enum ReadStringError { - GetStringClass, - ReadObjectsForClasses, - } - - /// Errors from writing strings to the pasteboard - #[derive(Debug)] - pub struct WriteStringError; - - /// A trait for reading contents from the pasteboard - /// - /// This is intended to reflect the underlying objective C API - /// `readObjectsForClasses:options:`. - pub trait PasteboardReadObject { - type Err; - fn read_object(&self) -> Result; - } - - /// A trait for writing contents to the pasteboard - pub trait PasteboardWriteObject { - type Err; - fn write_object(&mut self, T) -> Result<(), Self::Err>; - } - - impl PasteboardReadObject for Pasteboard { - type Err = ReadStringError; - fn read_object(&self) -> Result { - // Get string class; need this for passing to readObjectsForClasses - let ns_string_class = match Class::get("NSString") { - Some(class) => class, - None => return Err(ReadStringError::GetStringClass), - }; - - let ns_string: Id = unsafe { - let ptr: *mut Object = msg_send![ns_string_class, class]; - - if ptr.is_null() { - return Err(ReadStringError::GetStringClass); - } else { - Id::from_ptr(ptr) - } - }; - - let classes: Id> = unsafe { - // I think this transmute is valid. It's going from an - // Id to an Id. From transmute's perspective, - // the only thing that matters is that they both have the same - // size (they do for now since the generic is phantom data). In - // both cases, the underlying pointer is an id (from `[NSString - // class]`), so again, this should be valid. There's just - // nothing implemented in objc_id or objc_foundation to do this - // "safely". By the way, the only reason this is necessary is - // because INSObject isn't implemented for Id. - // - // And if that argument isn't convincing, my final reasoning is - // that "it seems to work". - NSArray::from_vec(vec![mem::transmute(ns_string)]) - }; - - // No options - // - // Apparently this doesn't compile without a type hint, so it maps - // objects to objects! - let options: Id> = NSDictionary::new(); - - // call [pasteboard readObjectsForClasses:options:] - let copied_items = unsafe { - let copied_items: *mut NSArray = msg_send![ - self.0, - readObjectsForClasses:&*classes - options:&*options - ]; - - if copied_items.is_null() { - return Err(ReadStringError::ReadObjectsForClasses); - } else { - Id::from_ptr(copied_items) as Id> - } - }; - - // Ok, this is great. We have an NSArray, and these have - // decent bindings. Use the first item returned (if an item was - // returned) or just return an empty string - // - // XXX Should this return an error if no items were returned? - let contents = copied_items - .first_object() - .map(|ns_string| ns_string.as_str().to_owned()) - .unwrap_or_else(String::new); - - Ok(contents) - } - } - - impl PasteboardWriteObject for Pasteboard { - type Err = WriteStringError; - - fn write_object(&mut self, object: String) -> Result<(), Self::Err> { - let objects = NSArray::from_vec(vec![NSString::from_str(&object)]); - - self.clear_contents(); - - // The writeObjects method returns true in case of success, and - // false otherwise. - let ok: bool = unsafe { msg_send![self.0, writeObjects: objects] }; - - if ok { - Ok(()) - } else { - Err(WriteStringError) - } - } - } - - impl ::std::error::Error for WriteStringError { - fn description(&self) -> &str { - "Failed writing string to the NSPasteboard (writeContents returned false)" - } - } - - impl ::std::fmt::Display for WriteStringError { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - f.write_str(::std::error::Error::description(self)) - } - } - - impl ::std::error::Error for ReadStringError { - fn description(&self) -> &str { - match *self { - ReadStringError::GetStringClass => "NSString class not found", - ReadStringError::ReadObjectsForClasses => "readObjectsForClasses:options: failed", - } - } - } - - impl ::std::fmt::Display for ReadStringError { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - f.write_str(::std::error::Error::description(self)) - } - } - - impl ::std::error::Error for NewPasteboardError { - fn description(&self) -> &str { - match *self { - NewPasteboardError::GetPasteboardClass => "NSPasteboard class not found", - NewPasteboardError::LoadGeneralPasteboard => { - "[NSPasteboard generalPasteboard] failed" - } - } - } - } - - impl ::std::fmt::Display for NewPasteboardError { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - f.write_str(::std::error::Error::description(self)) - } - } - - impl Pasteboard { - pub fn new() -> Result { - // NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; - let ns_pasteboard_class = match Class::get("NSPasteboard") { - Some(class) => class, - None => return Err(NewPasteboardError::GetPasteboardClass), - }; - - let ptr = unsafe { - let ptr: *mut Object = msg_send![ns_pasteboard_class, generalPasteboard]; - - if ptr.is_null() { - return Err(NewPasteboardError::LoadGeneralPasteboard); - } else { - ptr - } - }; - - let id = unsafe { Id::from_ptr(ptr) }; - - Ok(Pasteboard(id)) - } - - /// Clears the existing contents of the pasteboard, preparing it for new - /// contents. - /// - /// This is the first step in providing data on the pasteboard. The - /// return value is the change count of the pasteboard - pub fn clear_contents(&mut self) -> usize { - unsafe { msg_send![self.0, clearContents] } - } - } -} - -#[derive(Debug)] -pub enum Error { - CreatePasteboard(ns::NewPasteboardError), - ReadString(ns::ReadStringError), - WriteString(ns::WriteStringError), -} - -impl ::std::error::Error for Error { - fn cause(&self) -> Option<&::std::error::Error> { - match *self { - Error::CreatePasteboard(ref err) => Some(err), - Error::ReadString(ref err) => Some(err), - Error::WriteString(ref err) => Some(err), - } - } - - fn description(&self) -> &str { - match *self { - Error::CreatePasteboard(ref _err) => "Failed to create pasteboard", - Error::ReadString(ref _err) => "Failed to read string from pasteboard", - Error::WriteString(ref _err) => "Failed to write string to pasteboard", - } - } -} - -impl ::std::fmt::Display for Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match *self { - Error::CreatePasteboard(ref err) => write!(f, "Failed to create pasteboard: {}", err), - Error::ReadString(ref err) => { - write!(f, "Failed to read string from pasteboard: {}", err) - } - Error::WriteString(ref err) => { - write!(f, "Failed to write string to pasteboard: {}", err) - } - } - } -} - -impl From for Error { - fn from(val: ns::NewPasteboardError) -> Error { - Error::CreatePasteboard(val) - } -} - -impl From for Error { - fn from(val: ns::ReadStringError) -> Error { - Error::ReadString(val) - } -} - -impl From for Error { - fn from(val: ns::WriteStringError) -> Error { - Error::WriteString(val) - } -} - -pub struct Clipboard(ns::Pasteboard); - -impl super::Load for Clipboard { - type Err = Error; - - fn new() -> Result { - Ok(Clipboard(ns::Pasteboard::new()?)) - } - - fn load_primary(&self) -> Result { - use self::ns::PasteboardReadObject; - - self.0.read_object().map_err(::std::convert::From::from) - } -} - -impl super::Store for Clipboard { - fn store_primary(&mut self, contents: S) -> Result<(), Self::Err> - where - S: Into, - { - use self::ns::PasteboardWriteObject; - - self.0 - .write_object(contents.into()) - .map_err(::std::convert::From::from) - } - - fn store_selection(&mut self, _contents: S) -> Result<(), Self::Err> - where - S: Into, - { - // No such thing on macOS - Ok(()) - } -} - -#[cfg(test)] -mod tests { - use super::Clipboard; - use {Load, Store}; - - #[test] - fn create_clipboard_save_load_contents() { - let mut clipboard = Clipboard::new().unwrap(); - clipboard.store_primary("arst").unwrap(); - let loaded = clipboard.load_primary().unwrap(); - assert_eq!("arst", loaded); - } -} diff --git a/copypasta/src/x11.rs b/copypasta/src/x11.rs deleted file mode 100644 index af7476c27c..0000000000 --- a/copypasta/src/x11.rs +++ /dev/null @@ -1,170 +0,0 @@ -//! X11 Clipboard implementation -//! -//! Note that the x11 implementation is really crap right now - we just depend -//! on xclip being on the user's path. If x11 pasting doesn't work, it's -//! probably because xclip is unavailable. There's currently no non-GPL x11 -//! clipboard library for Rust. Until then, we have this hack. -//! -//! FIXME: Implement actual X11 clipboard API using the ICCCM reference -//! https://tronche.com/gui/x/icccm/ -use std::ffi::OsStr; -use std::io; -use std::process::{Command, Output}; -use std::string::FromUtf8Error; - -use super::{Load, Store}; - -/// The x11 clipboard -pub struct Clipboard; - -#[derive(Debug)] -pub enum Error { - Io(io::Error), - Xclip(String), - Utf8(FromUtf8Error), -} - -impl ::std::error::Error for Error { - fn cause(&self) -> Option<&::std::error::Error> { - match *self { - Error::Io(ref err) => Some(err), - Error::Utf8(ref err) => Some(err), - _ => None, - } - } - - fn description(&self) -> &str { - match *self { - Error::Io(..) => "Error calling xclip", - Error::Xclip(..) => "Error reported by xclip", - Error::Utf8(..) => "Clipboard contents not utf8", - } - } -} - -impl ::std::fmt::Display for Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match *self { - Error::Io(ref err) => match err.kind() { - io::ErrorKind::NotFound => { - write!(f, "Please install `xclip` to enable clipboard support") - } - _ => write!(f, "Error calling xclip: {}", err), - }, - Error::Xclip(ref s) => write!(f, "Error from xclip: {}", s), - Error::Utf8(ref err) => write!(f, "Error parsing xclip output: {}", err), - } - } -} - -impl From for Error { - fn from(val: io::Error) -> Error { - Error::Io(val) - } -} - -impl From for Error { - fn from(val: FromUtf8Error) -> Error { - Error::Utf8(val) - } -} - -impl Load for Clipboard { - type Err = Error; - - fn new() -> Result { - Ok(Clipboard) - } - - fn load_primary(&self) -> Result { - let output = Command::new("xclip") - .args(&["-o", "-selection", "clipboard"]) - .output()?; - - Clipboard::process_xclip_output(output) - } - - fn load_selection(&self) -> Result { - let output = Command::new("xclip").args(&["-o"]).output()?; - - Clipboard::process_xclip_output(output) - } -} - -impl Store for Clipboard { - /// Sets the primary clipboard contents - #[inline] - fn store_primary(&mut self, contents: S) -> Result<(), Self::Err> - where - S: Into, - { - self.store(contents, &["-i", "-selection", "clipboard"]) - } - - /// Sets the secondary clipboard contents - #[inline] - fn store_selection(&mut self, contents: S) -> Result<(), Self::Err> - where - S: Into, - { - self.store(contents, &["-i"]) - } -} - -impl Clipboard { - fn process_xclip_output(output: Output) -> Result { - if output.status.success() { - String::from_utf8(output.stdout).map_err(::std::convert::From::from) - } else { - String::from_utf8(output.stderr).map_err(::std::convert::From::from) - } - } - - fn store(&mut self, contents: C, args: &[S]) -> Result<(), Error> - where - C: Into, - S: AsRef, - { - use std::io::Write; - use std::process::Stdio; - - let contents = contents.into(); - let mut child = Command::new("xclip") - .args(args) - .stdin(Stdio::piped()) - .spawn()?; - - if let Some(stdin) = child.stdin.as_mut() { - stdin.write_all(contents.as_bytes())?; - } - - // Return error if didn't exit cleanly - let exit_status = child.wait()?; - if exit_status.success() { - Ok(()) - } else { - Err(Error::Xclip("xclip returned non-zero exit code".into())) - } - } -} - -#[cfg(test)] -mod tests { - use super::Clipboard; - use {Load, Store}; - - #[test] - fn clipboard_works() { - let mut clipboard = Clipboard::new().expect("create clipboard"); - let arst = "arst"; - let oien = "oien"; - clipboard.store_primary(arst).expect("store selection"); - clipboard.store_selection(oien).expect("store selection"); - - let selection = clipboard.load_selection().expect("load selection"); - let primary = clipboard.load_primary().expect("load selection"); - - assert_eq!(arst, primary); - assert_eq!(oien, selection); - } -} diff --git a/font/Cargo.toml b/font/Cargo.toml index c57181e873..b57471eac0 100644 --- a/font/Cargo.toml +++ b/font/Cargo.toml @@ -11,7 +11,6 @@ libc = "0.2" foreign-types = "0.4" log = "0.4" -#harfbuzz = { version = "0.3.0", optional = true } harfbuzz_rs = { version = "1.0.0", optional = true } [target.'cfg(not(any(target_os = "macos", windows)))'.dependencies] diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 339c7c2b11..70552d6fc0 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -117,13 +117,8 @@ impl ::std::fmt::Display for Error { Error::MissingGlyph(ref c) => write!(f, "Glyph not found for char {:?}", c), Error::MissingFont(ref desc) => write!( f, -<<<<<<< HEAD "Couldn't find a font with {}\n\tPlease check the font config in your \ alacritty.yml.", -======= - "Couldn't find a font with {}\ - \n\tPlease check the font config in your alacritty.yml.", ->>>>>>> Tried setting scale and ppem but it didn't change anything. desc ), Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"), @@ -166,14 +161,7 @@ impl ::Rasterize for Rasterizer { /// Get rasterized glyph for given glyph key fn get_glyph(&mut self, glyph: GlyphKey) -> Result { // get loaded font -<<<<<<< HEAD let font = self.fonts.get(&glyph.font_key).ok_or(Error::FontNotLoaded)?; -======= - let font = self - .fonts - .get(&glyph.font_key) - .ok_or(Error::FontNotLoaded)?; ->>>>>>> Tried setting scale and ppem but it didn't change anything. // first try the font itself as a direct hit self.maybe_get_glyph(glyph, font).unwrap_or_else(|| { @@ -248,7 +236,7 @@ impl Rasterizer { Style::Specific(ref style) => self.get_specific_face(desc, style, size), Style::Description { slant, weight } => { self.get_matching_face(desc, slant, weight, size) - } + }, } } @@ -309,15 +297,8 @@ pub fn get_family_names() -> Vec { fn cascade_list_for_languages(ct_font: &CTFont, languages: &[String]) -> Vec { // convert language type &Vec -> CFArray let langarr: CFArray = { -<<<<<<< HEAD let tmp: Vec = languages.iter().map(|language| CFString::new(&language)).collect(); -======= - let tmp: Vec = languages - .iter() - .map(|language| CFString::new(&language)) - .collect(); ->>>>>>> Tried setting scale and ppem but it didn't change anything. CFArray::from_CFTypes(&tmp) }; @@ -384,22 +365,11 @@ impl Descriptor { }; // Include Menlo in the fallback list as well -<<<<<<< HEAD fallbacks.insert(0, Font { cg_font: menlo.copy_to_CGFont(), ct_font: menlo, fallbacks: Vec::new(), }); -======= - fallbacks.insert( - 0, - Font { - cg_font: menlo.copy_to_CGFont(), - ct_font: menlo, - fallbacks: Vec::new(), - }, - ); ->>>>>>> Tried setting scale and ppem but it didn't change anything. fallbacks }) @@ -480,43 +450,8 @@ impl Font { _size: f64, use_thin_strokes: bool, ) -> Result { -<<<<<<< HEAD let glyph_index = self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(character))?; -======= - // Render custom symbols for underline and beam cursor - match character { - super::UNDERLINE_CURSOR_CHAR => { - // Get the bottom of the bounding box - let descent = -(self.ct_font.descent() as i32); - // Get the width of the cell - let width = self.glyph_advance('0') as i32; - // Return the new custom glyph - return super::get_underline_cursor_glyph(descent, width); - } - super::BEAM_CURSOR_CHAR | super::BOX_CURSOR_CHAR => { - // Get the top of the bounding box - let metrics = self.metrics(); - let height = metrics.line_height; - let ascent = (height - self.ct_font.descent()).ceil(); - - // Get the width of the cell - let width = self.glyph_advance('0') as i32; - - // Return the new custom glyph - if character == super::BEAM_CURSOR_CHAR { - return super::get_beam_cursor_glyph(ascent as i32, height as i32, width); - } else { - return super::get_box_cursor_glyph(ascent as i32, height as i32, width); - } - } - _ => (), - } - - let glyph_index = self - .glyph_index(character) - .ok_or_else(|| Error::MissingGlyph(character))?; ->>>>>>> Tried setting scale and ppem but it didn't change anything. let bounds = self.bounding_rect_for_glyph(Default::default(), glyph_index); @@ -602,10 +537,6 @@ impl Font { // and use the utf-16 buffer to get the index self.glyph_index_utf16(encoded) } -<<<<<<< HEAD - -======= ->>>>>>> Tried setting scale and ppem but it didn't change anything. fn glyph_index_utf16(&self, encoded: &[u16]) -> Option { // output buffer for the glyph. for non-BMP glyphs, like // emojis, this will be filled with two chars the second @@ -644,14 +575,7 @@ mod tests { println!("{:?}", list); // Check to_font -<<<<<<< HEAD let fonts = list.iter().map(|desc| desc.to_font(72., false)).collect::>(); -======= - let fonts = list - .iter() - .map(|desc| desc.to_font(72., false)) - .collect::>(); ->>>>>>> Tried setting scale and ppem but it didn't change anything. for font in fonts { // Get a glyph diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 33392c837a..79aed749c5 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -21,16 +21,14 @@ use std::path::PathBuf; #[cfg(feature = "hb-ft")] use super::HbError; #[cfg(feature = "hb-ft")] -use harfbuzz_rs::{Shared, Font, GlyphBuffer}; +use harfbuzz_rs::{Shared, Owned, Font, GlyphBuffer}; use freetype::tt_os2::TrueTypeOS2Table; use freetype::{self, Library}; use libc::c_uint; pub mod fc; -use super::{ - FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight, -}; +use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; struct FixedSize { pixelsize: f64, @@ -45,7 +43,7 @@ struct Face { non_scalable: Option, /// This is an option just in case hb_ft_create_font_referenced fails. #[cfg(feature = "hb-ft")] - hb_font: Option>>, + hb_font: Owned>, } impl fmt::Debug for Face { @@ -54,17 +52,14 @@ impl fmt::Debug for Face { .field("ft_face", &self.ft_face) .field("key", &self.key) .field("load_flags", &self.load_flags) - .field( - "render_mode", - &match self.render_mode { + .field("render_mode", &match self.render_mode { freetype::RenderMode::Normal => "Normal", freetype::RenderMode::Light => "Light", freetype::RenderMode::Mono => "Mono", freetype::RenderMode::Lcd => "Lcd", freetype::RenderMode::LcdV => "LcdV", freetype::RenderMode::Max => "Max", - }, - ) + }) .field("lcd_filter", &self.lcd_filter) .finish() } @@ -306,7 +301,7 @@ impl FreeTypeRasterizer { use harfbuzz_rs::*; let _hb_face = Face::from_file(&path, index as u32)?; let _hb_font = Font::new(_hb_face); - Some(_hb_font.to_shared()) + _hb_font }; let face = Face { @@ -496,7 +491,7 @@ impl FreeTypeRasterizer { packed.extend_from_slice(&buf[start..stop]); } Ok((bitmap.rows(), bitmap.width() / 3, packed)) - } + }, PixelMode::LcdV => { for i in 0..bitmap.rows() / 3 { for j in 0..bitmap.width() { @@ -507,7 +502,7 @@ impl FreeTypeRasterizer { } } Ok((bitmap.rows() / 3, bitmap.width(), packed)) - } + }, // Mono data is stored in a packed format using 1 bit per pixel. PixelMode::Mono => { fn unpack_byte(res: &mut Vec, byte: u8, mut count: u8) { @@ -538,7 +533,7 @@ impl FreeTypeRasterizer { } } Ok((bitmap.rows(), bitmap.width(), packed)) - } + }, // Gray data is stored as a value between 0 and 255 using 1 byte per pixel. PixelMode::Gray => { for i in 0..bitmap.rows() { @@ -573,7 +568,7 @@ impl FreeTypeRasterizer { Some(&key) => { debug!("Hit for font {:?}; no need to load", path); Ok(key) - } + }, None => { debug!("Miss for font {:?}; loading now", path); diff --git a/font/src/lib.rs b/font/src/lib.rs index 3fb8efde25..fcb09e8e61 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -18,12 +18,7 @@ //! FreeType is used on everything that's not Mac OS. //! Eventually, ClearType support will be available for windows -#![deny( - clippy::all, - clippy::if_not_else, - clippy::enum_glob_use, - clippy::wrong_pub_self_convention -)] +#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)] /* Note: all applicable cfg statements have been modified to short-circuit * to freetype if the feature hb-ft is enabled. @@ -146,27 +141,6 @@ impl FontKey { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum KeyType { - Char(char), - GlyphIndex(u32), -} -impl From for KeyType { - fn from(val: char) -> Self { - KeyType::Char(val) - } -} -impl<'a> From<&'a char> for KeyType { - fn from(val: &'a char) -> Self { - KeyType::Char(*val) - } -} -impl From for KeyType { - fn from(val: u32) -> Self { - KeyType::GlyphIndex(val) - } -} - #[derive(Debug, Copy, Clone, Eq)] pub struct GlyphKey { #[cfg(not(feature = "hb-ft"))] @@ -248,7 +222,11 @@ pub struct RasterizedGlyph { impl Default for RasterizedGlyph { fn default() -> RasterizedGlyph { - RasterizedGlyph { c: ' '.into(), width: 0, height: 0, top: 0, left: 0, buf: Vec::new() } + #[cfg(feature = "hb-ft")] + let c = 1u32; + #[cfg(not(feature = "hb-ft"))] + let c = ' '; + RasterizedGlyph { c, width: 0, height: 0, top: 0, left: 0, buf: Vec::new() } } } diff --git a/font/src/rusttype/mod.rs b/font/src/rusttype/mod.rs deleted file mode 100644 index 417099fbb9..0000000000 --- a/font/src/rusttype/mod.rs +++ /dev/null @@ -1,206 +0,0 @@ -extern crate font_loader; -use self::font_loader::system_fonts; - -extern crate rusttype; -use self::rusttype::{point, Codepoint, FontCollection, Scale}; - -use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; - -pub struct RustTypeRasterizer { - fonts: Vec>, - dpi_ratio: f32, -} - -impl crate::Rasterize for RustTypeRasterizer { - type Err = Error; - - fn new(device_pixel_ratio: f32, _: bool) -> Result { - Ok(RustTypeRasterizer { - fonts: Vec::new(), - dpi_ratio: device_pixel_ratio, - }) - } - - fn metrics(&self, key: FontKey, size: Size) -> Result { - let scale = Scale::uniform(size.as_f32_pts() * self.dpi_ratio * 96. / 72.); - let vmetrics = self.fonts[key.token as usize].v_metrics(scale); - let hmetrics = self.fonts[key.token as usize] - .glyph( - // If the font is monospaced all glyphs *should* have the same width - // 33 '!' is the first displaying character - Codepoint(33), - ) - .ok_or(Error::MissingGlyph)? - .scaled(scale) - .h_metrics(); - - let line_height = f64::from(vmetrics.ascent - vmetrics.descent + vmetrics.line_gap); - let average_advance = f64::from(hmetrics.advance_width); - let descent = vmetrics.descent; - - // Strikeout and underline metrics. - // RustType doesn't support these, so we make up our own. - let thickness = (descent / 5.).round(); - let underline_position = descent / 2.; - let strikeout_position = line_height as f32 / 2. - descent; - - Ok(Metrics { - descent, - average_advance, - line_height, - underline_position, - underline_thickness: thickness, - strikeout_position, - strikeout_thickness: thickness, - }) - } - - fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result { - let fp = system_fonts::FontPropertyBuilder::new() - .family(&desc.name) - .monospace(); - - let fp = match desc.style { - Style::Specific(ref style) => match style.to_lowercase().as_str() { - "italic" => fp.italic(), - "bold" => fp.bold(), - _ => fp, - }, - Style::Description { slant, weight } => { - let fp = match slant { - Slant::Normal => fp, - Slant::Italic => fp.italic(), - // This style is not supported by rust-font-loader - Slant::Oblique => return Err(Error::UnsupportedStyle), - }; - match weight { - Weight::Bold => fp.bold(), - Weight::Normal => fp, - } - } - }; - self.fonts.push( - FontCollection::from_bytes( - system_fonts::get(&fp.build()) - .ok_or_else(|| Error::MissingFont(desc.clone()))? - .0, - ) - .into_font() - .ok_or(Error::UnsupportedFont)?, - ); - Ok(FontKey { - token: (self.fonts.len() - 1) as u16, - }) - } - - fn get_glyph(&mut self, glyph_key: GlyphKey) -> Result { - match glyph_key.c { - super::UNDERLINE_CURSOR_CHAR => { - let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?; - return super::get_underline_cursor_glyph( - metrics.descent as i32, - metrics.average_advance as i32, - ); - } - super::BEAM_CURSOR_CHAR => { - let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?; - - return super::get_beam_cursor_glyph( - (metrics.line_height + f64::from(metrics.descent)).round() as i32, - metrics.line_height.round() as i32, - metrics.average_advance.round() as i32, - ); - } - super::BOX_CURSOR_CHAR => { - let metrics = self.metrics(glyph_key.font_key, glyph_key.size)?; - - return super::get_box_cursor_glyph( - (metrics.line_height + f64::from(metrics.descent)).round() as i32, - metrics.line_height.round() as i32, - metrics.average_advance.round() as i32, - ); - } - _ => (), - } - - let scaled_glyph = self.fonts[glyph_key.font_key.token as usize] - .glyph(glyph_key.c) - .ok_or(Error::MissingGlyph)? - .scaled(Scale::uniform( - glyph_key.size.as_f32_pts() * self.dpi_ratio * 96. / 72., - )); - - let glyph = scaled_glyph.positioned(point(0.0, 0.0)); - - // Pixel bounding box - let bb = match glyph.pixel_bounding_box() { - Some(bb) => bb, - // Bounding box calculation fails for spaces so we provide a placeholder bounding box - None => rusttype::Rect { - min: point(0, 0), - max: point(0, 0), - }, - }; - - let mut buf = Vec::with_capacity((bb.width() * bb.height()) as usize); - - glyph.draw(|_x, _y, v| { - buf.push((v * 255.0) as u8); - buf.push((v * 255.0) as u8); - buf.push((v * 255.0) as u8); - }); - Ok(RasterizedGlyph { - c: glyph_key.c, - width: bb.width(), - height: bb.height(), - top: -bb.min.y, - left: bb.min.x, - buf, - }) - } - - fn update_dpr(&mut self, device_pixel_ratio: f32) { - self.dpi_ratio = device_pixel_ratio; - } -} - -#[derive(Debug)] -pub enum Error { - MissingFont(FontDesc), - UnsupportedFont, - UnsupportedStyle, - // NOTE: This error is different from how the FreeType code handles it - MissingGlyph, -} - -impl ::std::error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::MissingFont(ref _desc) => "Couldn't find the requested font", - Error::UnsupportedFont => "Only TrueType fonts are supported", - Error::UnsupportedStyle => "The selected style is not supported by rusttype", - Error::MissingGlyph => "The selected font does not have the requested glyph", - } - } -} - -impl ::std::fmt::Display for Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match *self { - Error::MissingFont(ref desc) => write!( - f, - "Couldn't find a font with {}\ - \n\tPlease check the font config in your alacritty.yml.", - desc - ), - Error::UnsupportedFont => write!( - f, - "Rusttype only supports TrueType fonts.\n\tPlease select a TrueType font instead." - ), - Error::UnsupportedStyle => { - write!(f, "The selected font style is not supported by rusttype.") - } - Error::MissingGlyph => write!(f, "The selected font did not have the requested glyph."), - } - } -} diff --git a/src/cli.rs b/src/cli.rs deleted file mode 100644 index 419a54efb3..0000000000 --- a/src/cli.rs +++ /dev/null @@ -1,258 +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 ::log; -use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; - -use crate::config::{Delta, Dimensions, Shell}; -use crate::index::{Column, Line}; -use crate::window::DEFAULT_NAME; -use std::borrow::Cow; -use std::path::{Path, PathBuf}; - -/// Options specified on the command line -pub struct Options { - pub live_config_reload: Option, - pub print_events: bool, - pub ref_test: bool, - pub dimensions: Option, - pub position: Option>, - pub title: Option, - pub class: Option, - pub log_level: log::LevelFilter, - pub command: Option>, - pub working_dir: Option, - pub config: Option, - 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, - log_level: log::LevelFilter::Warn, - command: None, - working_dir: None, - config: None, - persistent_logging: false, - } - } -} - -impl Options { - /// Build `Options` from command line arguments - pub fn load() -> Options { - let mut options = Options::default(); - - let matches = App::new(crate_name!()) - .version(crate_version!()) - .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 X11 [default: {}]", - DEFAULT_NAME - )), - ) - .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( - "Specify alternative configuration file \ - [default: $XDG_CONFIG_HOME/alacritty/alacritty.yml]", - ), - ) - .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)"), - ) - .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(|x| x.parse::()); - let y = position.next().map(|y| y.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); - - match matches.occurrences_of("q") { - 0 => {} - 1 => options.log_level = log::LevelFilter::Error, - 2 | _ => options.log_level = log::LevelFilter::Off, - } - - match matches.occurrences_of("v") { - 0 if !options.print_events => {} - 0 | 1 => options.log_level = log::LevelFilter::Info, - 2 => options.log_level = log::LevelFilter::Debug, - 3 | _ => options.log_level = log::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)); - } - - options - } - - pub fn dimensions(&self) -> Option { - self.dimensions - } - - pub fn position(&self) -> Option> { - self.position - } - - pub fn command(&self) -> Option<&Shell<'_>> { - self.command.as_ref() - } - - pub fn config_path(&self) -> Option> { - self.config.as_ref().map(|p| Cow::Borrowed(p.as_path())) - } -} diff --git a/src/config/bindings.rs b/src/config/bindings.rs deleted file mode 100644 index 0e00f6fea6..0000000000 --- a/src/config/bindings.rs +++ /dev/null @@ -1,217 +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 glutin::{ModifiersState, MouseButton}; - -use super::Key; -use crate::input::{Action, KeyBinding, MouseBinding}; -use crate::term::TermMode; - -macro_rules! bindings { - ( - $ty:ident; - $( - $key:path - $(,[$($mod:ident: $enabled:expr),*])* - $(,+$mode:expr)* - $(,~$notmode:expr)* - ;$action:expr - );* - $(;)* - ) => {{ - let mut v = Vec::new(); - - $( - let mut _mods = ModifiersState { - $($($mod: $enabled),*,)* - ..Default::default() - }; - 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 { - bindings!( - MouseBinding; - MouseButton::Middle; Action::PasteSelection; - ) -} - -pub fn default_key_bindings() -> Vec { - let mut bindings = bindings!( - KeyBinding; - Key::Paste; Action::Paste; - Key::Copy; Action::Copy; - Key::L, [ctrl: true]; Action::ClearLogNotice; - Key::L, [ctrl: true]; Action::Esc("\x0c".into()); - Key::Home, [alt: true]; Action::Esc("\x1b[1;3H".into()); - Key::Home, +TermMode::APP_CURSOR; Action::Esc("\x1bOH".into()); - Key::Home, ~TermMode::APP_CURSOR; Action::Esc("\x1b[H".into()); - Key::End, [alt: true]; Action::Esc("\x1b[1;3F".into()); - Key::End, +TermMode::APP_CURSOR; Action::Esc("\x1bOF".into()); - Key::End, ~TermMode::APP_CURSOR; Action::Esc("\x1b[F".into()); - Key::PageUp, [shift: true], ~TermMode::ALT_SCREEN; Action::ScrollPageUp; - Key::PageUp, [shift: true], +TermMode::ALT_SCREEN; Action::Esc("\x1b[5;2~".into()); - Key::PageUp, [ctrl: true]; Action::Esc("\x1b[5;5~".into()); - Key::PageUp, [alt: true]; Action::Esc("\x1b[5;3~".into()); - Key::PageUp; Action::Esc("\x1b[5~".into()); - Key::PageDown, [shift: true], ~TermMode::ALT_SCREEN; Action::ScrollPageDown; - Key::PageDown, [shift: true], +TermMode::ALT_SCREEN; Action::Esc("\x1b[6;2~".into()); - Key::PageDown, [ctrl: true]; Action::Esc("\x1b[6;5~".into()); - Key::PageDown, [alt: true]; Action::Esc("\x1b[6;3~".into()); - Key::PageDown; Action::Esc("\x1b[6~".into()); - Key::Tab, [shift: true]; Action::Esc("\x1b[Z".into()); - Key::Back; Action::Esc("\x7f".into()); - Key::Back, [alt: true]; Action::Esc("\x1b\x7f".into()); - Key::Insert; Action::Esc("\x1b[2~".into()); - Key::Delete; Action::Esc("\x1b[3~".into()); - Key::Left, [shift: true]; Action::Esc("\x1b[1;2D".into()); - Key::Left, [ctrl: true]; Action::Esc("\x1b[1;5D".into()); - Key::Left, [alt: true]; Action::Esc("\x1b[1;3D".into()); - Key::Left, ~TermMode::APP_CURSOR; Action::Esc("\x1b[D".into()); - Key::Left, +TermMode::APP_CURSOR; Action::Esc("\x1bOD".into()); - Key::Right, [shift: true]; Action::Esc("\x1b[1;2C".into()); - Key::Right, [ctrl: true]; Action::Esc("\x1b[1;5C".into()); - Key::Right, [alt: true]; Action::Esc("\x1b[1;3C".into()); - Key::Right, ~TermMode::APP_CURSOR; Action::Esc("\x1b[C".into()); - Key::Right, +TermMode::APP_CURSOR; Action::Esc("\x1bOC".into()); - Key::Up, [shift: true]; Action::Esc("\x1b[1;2A".into()); - Key::Up, [ctrl: true]; Action::Esc("\x1b[1;5A".into()); - Key::Up, [alt: true]; Action::Esc("\x1b[1;3A".into()); - Key::Up, ~TermMode::APP_CURSOR; Action::Esc("\x1b[A".into()); - Key::Up, +TermMode::APP_CURSOR; Action::Esc("\x1bOA".into()); - Key::Down, [shift: true]; Action::Esc("\x1b[1;2B".into()); - Key::Down, [ctrl: true]; Action::Esc("\x1b[1;5B".into()); - Key::Down, [alt: true]; Action::Esc("\x1b[1;3B".into()); - Key::Down, ~TermMode::APP_CURSOR; Action::Esc("\x1b[B".into()); - Key::Down, +TermMode::APP_CURSOR; Action::Esc("\x1bOB".into()); - Key::F1; Action::Esc("\x1bOP".into()); - Key::F2; Action::Esc("\x1bOQ".into()); - Key::F3; Action::Esc("\x1bOR".into()); - Key::F4; Action::Esc("\x1bOS".into()); - Key::F5; Action::Esc("\x1b[15~".into()); - Key::F6; Action::Esc("\x1b[17~".into()); - Key::F7; Action::Esc("\x1b[18~".into()); - Key::F8; Action::Esc("\x1b[19~".into()); - Key::F9; Action::Esc("\x1b[20~".into()); - Key::F10; Action::Esc("\x1b[21~".into()); - Key::F11; Action::Esc("\x1b[23~".into()); - Key::F12; Action::Esc("\x1b[24~".into()); - Key::F1, [shift: true]; Action::Esc("\x1b[1;2P".into()); - Key::F2, [shift: true]; Action::Esc("\x1b[1;2Q".into()); - Key::F3, [shift: true]; Action::Esc("\x1b[1;2R".into()); - Key::F4, [shift: true]; Action::Esc("\x1b[1;2S".into()); - Key::F5, [shift: true]; Action::Esc("\x1b[15;2~".into()); - Key::F6, [shift: true]; Action::Esc("\x1b[17;2~".into()); - Key::F7, [shift: true]; Action::Esc("\x1b[18;2~".into()); - Key::F8, [shift: true]; Action::Esc("\x1b[19;2~".into()); - Key::F9, [shift: true]; Action::Esc("\x1b[20;2~".into()); - Key::F10, [shift: true]; Action::Esc("\x1b[21;2~".into()); - Key::F11, [shift: true]; Action::Esc("\x1b[23;2~".into()); - Key::F12, [shift: true]; Action::Esc("\x1b[24;2~".into()); - Key::F1, [ctrl: true]; Action::Esc("\x1b[1;5P".into()); - Key::F2, [ctrl: true]; Action::Esc("\x1b[1;5Q".into()); - Key::F3, [ctrl: true]; Action::Esc("\x1b[1;5R".into()); - Key::F4, [ctrl: true]; Action::Esc("\x1b[1;5S".into()); - Key::F5, [ctrl: true]; Action::Esc("\x1b[15;5~".into()); - Key::F6, [ctrl: true]; Action::Esc("\x1b[17;5~".into()); - Key::F7, [ctrl: true]; Action::Esc("\x1b[18;5~".into()); - Key::F8, [ctrl: true]; Action::Esc("\x1b[19;5~".into()); - Key::F9, [ctrl: true]; Action::Esc("\x1b[20;5~".into()); - Key::F10, [ctrl: true]; Action::Esc("\x1b[21;5~".into()); - Key::F11, [ctrl: true]; Action::Esc("\x1b[23;5~".into()); - Key::F12, [ctrl: true]; Action::Esc("\x1b[24;5~".into()); - Key::F1, [alt: true]; Action::Esc("\x1b[1;6P".into()); - Key::F2, [alt: true]; Action::Esc("\x1b[1;6Q".into()); - Key::F3, [alt: true]; Action::Esc("\x1b[1;6R".into()); - Key::F4, [alt: true]; Action::Esc("\x1b[1;6S".into()); - Key::F5, [alt: true]; Action::Esc("\x1b[15;6~".into()); - Key::F6, [alt: true]; Action::Esc("\x1b[17;6~".into()); - Key::F7, [alt: true]; Action::Esc("\x1b[18;6~".into()); - Key::F8, [alt: true]; Action::Esc("\x1b[19;6~".into()); - Key::F9, [alt: true]; Action::Esc("\x1b[20;6~".into()); - Key::F10, [alt: true]; Action::Esc("\x1b[21;6~".into()); - Key::F11, [alt: true]; Action::Esc("\x1b[23;6~".into()); - Key::F12, [alt: true]; Action::Esc("\x1b[24;6~".into()); - Key::F1, [logo: true]; Action::Esc("\x1b[1;3P".into()); - Key::F2, [logo: true]; Action::Esc("\x1b[1;3Q".into()); - Key::F3, [logo: true]; Action::Esc("\x1b[1;3R".into()); - Key::F4, [logo: true]; Action::Esc("\x1b[1;3S".into()); - Key::F5, [logo: true]; Action::Esc("\x1b[15;3~".into()); - Key::F6, [logo: true]; Action::Esc("\x1b[17;3~".into()); - Key::F7, [logo: true]; Action::Esc("\x1b[18;3~".into()); - Key::F8, [logo: true]; Action::Esc("\x1b[19;3~".into()); - Key::F9, [logo: true]; Action::Esc("\x1b[20;3~".into()); - Key::F10, [logo: true]; Action::Esc("\x1b[21;3~".into()); - Key::F11, [logo: true]; Action::Esc("\x1b[23;3~".into()); - Key::F12, [logo: true]; Action::Esc("\x1b[24;3~".into()); - Key::NumpadEnter; Action::Esc("\n".into()); - ); - - bindings.extend(platform_key_bindings()); - - bindings -} - -#[cfg(not(any(target_os = "macos", test)))] -pub fn platform_key_bindings() -> Vec { - bindings!( - KeyBinding; - Key::V, [ctrl: true, shift: true]; Action::Paste; - Key::C, [ctrl: true, shift: true]; Action::Copy; - Key::Insert, [shift: true]; Action::PasteSelection; - Key::Key0, [ctrl: true]; Action::ResetFontSize; - Key::Equals, [ctrl: true]; Action::IncreaseFontSize; - Key::Add, [ctrl: true]; Action::IncreaseFontSize; - Key::Subtract, [ctrl: true]; Action::DecreaseFontSize; - Key::Minus, [ctrl: true]; Action::DecreaseFontSize; - ) -} - -#[cfg(all(target_os = "macos", not(test)))] -pub fn platform_key_bindings() -> Vec { - bindings!( - KeyBinding; - Key::Key0, [logo: true]; Action::ResetFontSize; - Key::Equals, [logo: true]; Action::IncreaseFontSize; - Key::Add, [logo: true]; Action::IncreaseFontSize; - Key::Minus, [logo: true]; Action::DecreaseFontSize; - Key::K, [logo: true]; Action::ClearHistory; - Key::K, [logo: true]; Action::Esc("\x0c".into()); - Key::V, [logo: true]; Action::Paste; - Key::C, [logo: true]; Action::Copy; - Key::H, [logo: true]; Action::Hide; - Key::Q, [logo: true]; Action::Quit; - Key::W, [logo: true]; Action::Quit; - ) -} - -// Don't return any bindings for tests since they are commented-out by default -#[cfg(test)] -pub fn platform_key_bindings() -> Vec { - vec![] -} diff --git a/src/config/mod.rs b/src/config/mod.rs deleted file mode 100644 index 21089d884b..0000000000 --- a/src/config/mod.rs +++ /dev/null @@ -1,2883 +0,0 @@ -//! Configuration definitions and file loading -//! -//! Alacritty reads from a config file at startup to determine various runtime -//! parameters including font family and style, font size, etc. In the future, -//! the config file will also hold user and platform specific keybindings. -use std::borrow::Cow; -use std::collections::HashMap; -use std::fs::File; -use std::io::{self, Read, Write}; -use std::path::{Path, PathBuf}; -use std::str::FromStr; -use std::sync::mpsc; -use std::time::Duration; -use std::{env, fmt}; - -use font::Size; -use glutin::ModifiersState; -use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; -use serde::de::Error as SerdeError; -use serde::de::{MapAccess, Unexpected, Visitor}; -use serde::{self, de, Deserialize}; -use serde_yaml; - -use crate::ansi::{Color, CursorStyle, NamedColor}; -use crate::cli::Options; -use crate::index::{Column, Line}; -use crate::input::{Action, Binding, KeyBinding, MouseBinding}; -use crate::term::color::Rgb; - -mod bindings; - -pub const SOURCE_FILE_PATH: &str = file!(); -const MAX_SCROLLBACK_LINES: u32 = 100_000; -static DEFAULT_ALACRITTY_CONFIG: &'static str = - include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/alacritty.yml")); - -#[serde(default)] -#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] -pub struct Selection { - #[serde(deserialize_with = "deserialize_escape_chars")] - pub semantic_escape_chars: String, - #[serde(deserialize_with = "failure_default")] - pub save_to_clipboard: bool, -} - -impl Default for Selection { - fn default() -> Selection { - Selection { - semantic_escape_chars: default_escape_chars(), - save_to_clipboard: Default::default(), - } - } -} - -fn deserialize_escape_chars<'a, D>(deserializer: D) -> ::std::result::Result -where - D: de::Deserializer<'a>, -{ - match String::deserialize(deserializer) { - Ok(escape_chars) => Ok(escape_chars), - Err(err) => { - error!("Problem with config: {}; using default value", err); - Ok(default_escape_chars()) - } - } -} - -fn default_escape_chars() -> String { - String::from(",│`|:\"' ()[]{}<>") -} - -#[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 -where - D: de::Deserializer<'a>, -{ - match u64::deserialize(deserializer) { - Ok(threshold_ms) => Ok(Duration::from_millis(threshold_ms)), - Err(err) => { - error!("Problem with config: {}; using default value", err); - Ok(default_threshold_ms()) - } - } -} - -#[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, - - // TODO: DEPRECATED - pub faux_scrollback_lines: Option, -} - -#[serde(default)] -#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] -pub struct Url { - // Program for opening links - #[serde(deserialize_with = "deserialize_launcher")] - pub launcher: Option, - - // Modifier used to open links - #[serde(deserialize_with = "deserialize_modifiers")] - pub modifiers: ModifiersState, -} - -fn deserialize_launcher<'a, D>( - deserializer: D, -) -> ::std::result::Result, D::Error> -where - D: de::Deserializer<'a>, -{ - let default = Url::default().launcher; - - // Deserialize to generic value - let val = match serde_yaml::Value::deserialize(deserializer) { - Ok(val) => val, - Err(err) => { - error!( - "Problem with config: {}; using {}", - err, - default.clone().unwrap().program() - ); - return Ok(default); - } - }; - - // Accept `None` to disable the launcher - if val - .as_str() - .filter(|v| v.to_lowercase() == "none") - .is_some() - { - return Ok(None); - } - - match >::deserialize(val) { - Ok(launcher) => Ok(launcher), - Err(err) => { - error!( - "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(), - } - } -} - -fn deserialize_modifiers<'a, D>(deserializer: D) -> ::std::result::Result -where - D: de::Deserializer<'a>, -{ - ModsWrapper::deserialize(deserializer).map(ModsWrapper::into_inner) -} - -/// `VisualBellAnimations` are modeled after a subset of CSS transitions and Robert -/// Penner's Easing Functions. -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq)] -pub enum VisualBellAnimation { - Ease, // CSS - EaseOut, // CSS - EaseOutSine, // Penner - EaseOutQuad, // Penner - EaseOutCubic, // Penner - EaseOutQuart, // Penner - EaseOutQuint, // Penner - EaseOutExpo, // Penner - EaseOutCirc, // Penner - Linear, -} - -impl Default for VisualBellAnimation { - fn default() -> Self { - VisualBellAnimation::EaseOutExpo - } -} - -#[serde(default)] -#[derive(Debug, Deserialize, PartialEq, Eq)] -pub struct VisualBellConfig { - /// Visual bell animation function - #[serde(deserialize_with = "failure_default")] - animation: VisualBellAnimation, - - /// Visual bell duration in milliseconds - #[serde(deserialize_with = "failure_default")] - duration: u16, - - /// Visual bell flash color - #[serde(deserialize_with = "rgb_from_hex")] - color: Rgb, -} - -impl Default for VisualBellConfig { - fn default() -> VisualBellConfig { - VisualBellConfig { - animation: Default::default(), - duration: Default::default(), - color: default_visual_bell_color(), - } - } -} - -fn default_visual_bell_color() -> Rgb { - Rgb { - r: 255, - g: 255, - b: 255, - } -} - -impl VisualBellConfig { - /// Visual bell animation - #[inline] - pub fn animation(&self) -> VisualBellAnimation { - self.animation - } - - /// Visual bell duration in milliseconds - #[inline] - pub fn duration(&self) -> Duration { - Duration::from_millis(u64::from(self.duration)) - } - - /// Visual bell flash color - #[inline] - pub fn color(&self) -> Rgb { - self.color - } -} - -#[derive(Debug, Deserialize, PartialEq, Eq)] -pub struct Shell<'a> { - program: Cow<'a, str>, - - #[serde(default, deserialize_with = "failure_default")] - args: Vec, -} - -impl<'a> Shell<'a> { - pub fn new(program: S) -> Shell<'a> - where - S: Into>, - { - Shell { - program: program.into(), - args: Vec::new(), - } - } - - pub fn new_with_args(program: S, args: Vec) -> Shell<'a> - where - S: Into>, - { - Shell { - program: program.into(), - args, - } - } - - pub fn program(&self) -> &str { - &*self.program - } - - pub fn args(&self) -> &[String] { - self.args.as_slice() - } -} - -/// Wrapper around f32 that represents an alpha value between 0.0 and 1.0 -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct Alpha(f32); - -impl Alpha { - pub fn new(value: f32) -> Self { - Alpha(Self::clamp_to_valid_range(value)) - } - - pub fn set(&mut self, value: f32) { - self.0 = Self::clamp_to_valid_range(value); - } - - #[inline] - pub fn get(self) -> f32 { - self.0 - } - - fn clamp_to_valid_range(value: f32) -> f32 { - if value < 0.0 { - 0.0 - } else if value > 1.0 { - 1.0 - } else { - value - } - } -} - -impl Default for Alpha { - fn default() -> Self { - Alpha(1.0) - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum Decorations { - Full, - Transparent, - Buttonless, - None, -} - -impl Default for Decorations { - fn default() -> Decorations { - Decorations::Full - } -} - -impl<'de> Deserialize<'de> for Decorations { - fn deserialize(deserializer: D) -> ::std::result::Result - where - D: de::Deserializer<'de>, - { - struct DecorationsVisitor; - - impl<'de> Visitor<'de> for DecorationsVisitor { - type Value = Decorations; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Some subset of full|transparent|buttonless|none") - } - - #[cfg(target_os = "macos")] - fn visit_str(self, value: &str) -> ::std::result::Result - where - E: de::Error, - { - match value.to_lowercase().as_str() { - "transparent" => Ok(Decorations::Transparent), - "buttonless" => Ok(Decorations::Buttonless), - "none" => Ok(Decorations::None), - "full" => Ok(Decorations::Full), - "true" => { - error!( - "Deprecated decorations boolean value, \ - use one of transparent|buttonless|none|full instead; \ - falling back to \"full\"" - ); - Ok(Decorations::Full) - } - "false" => { - error!( - "Deprecated decorations boolean value, \ - use one of transparent|buttonless|none|full instead; \ - falling back to \"none\"" - ); - Ok(Decorations::None) - } - _ => { - error!("Invalid decorations value: {}; using default value", value); - Ok(Decorations::Full) - } - } - } - - #[cfg(not(target_os = "macos"))] - fn visit_str(self, value: &str) -> ::std::result::Result - where - E: de::Error, - { - match value.to_lowercase().as_str() { - "none" => Ok(Decorations::None), - "full" => Ok(Decorations::Full), - "true" => { - error!( - "Deprecated decorations boolean value, \ - use one of none|full instead; \ - falling back to \"full\"" - ); - Ok(Decorations::Full) - } - "false" => { - error!( - "Deprecated decorations boolean value, \ - use one of none|full instead; \ - falling back to \"none\"" - ); - Ok(Decorations::None) - } - "transparent" | "buttonless" => { - error!( - "macOS-only decorations value: {}; using default value", - value - ); - Ok(Decorations::Full) - } - _ => { - error!("Invalid decorations value: {}; using default value", value); - Ok(Decorations::Full) - } - } - } - } - - deserializer.deserialize_str(DecorationsVisitor) - } -} - -#[serde(default)] -#[derive(Debug, Copy, Clone, Deserialize, PartialEq, Eq)] -pub struct WindowConfig { - /// Initial dimensions - #[serde(default, deserialize_with = "failure_default")] - dimensions: Dimensions, - - /// Initial position - #[serde(default, deserialize_with = "failure_default")] - position: Option>, - - /// Pixel padding - #[serde(deserialize_with = "deserialize_padding")] - padding: Delta, - - /// Draw the window with title bar / borders - #[serde(deserialize_with = "failure_default")] - decorations: Decorations, - - /// Spread out additional padding evenly - #[serde(deserialize_with = "failure_default")] - dynamic_padding: bool, - - /// Start maximized - #[serde(deserialize_with = "failure_default")] - start_maximized: bool, -} - -impl Default for WindowConfig { - fn default() -> Self { - WindowConfig { - dimensions: Default::default(), - position: Default::default(), - padding: default_padding(), - decorations: Default::default(), - dynamic_padding: Default::default(), - start_maximized: Default::default(), - } - } -} - -fn default_padding() -> Delta { - Delta { x: 2, y: 2 } -} - -fn deserialize_padding<'a, D>(deserializer: D) -> ::std::result::Result, D::Error> -where - D: de::Deserializer<'a>, -{ - match Delta::deserialize(deserializer) { - Ok(delta) => Ok(delta), - Err(err) => { - error!("Problem with config: {}; using default value", err); - Ok(default_padding()) - } - } -} - -impl WindowConfig { - pub fn decorations(&self) -> Decorations { - self.decorations - } - - pub fn dynamic_padding(&self) -> bool { - self.dynamic_padding - } - - pub fn start_maximized(&self) -> bool { - self.start_maximized - } -} - -/// Top-level config type -#[derive(Debug, PartialEq, Deserialize)] -pub struct Config { - /// Pixel padding - #[serde(default, deserialize_with = "failure_default")] - padding: Option>, - - /// TERM env variable - #[serde(default, deserialize_with = "failure_default")] - env: HashMap, - - /// Font configuration - #[serde(default, deserialize_with = "failure_default")] - font: Font, - - /// Should show render timer - #[serde(default, deserialize_with = "failure_default")] - render_timer: bool, - - /// Should draw bold text with brighter colors instead of bold font - #[serde( - default = "default_true_bool", - deserialize_with = "deserialize_true_bool" - )] - draw_bold_text_with_bright_colors: bool, - - #[serde(default, deserialize_with = "failure_default")] - colors: Colors, - - /// Background opacity from 0.0 to 1.0 - #[serde(default, deserialize_with = "failure_default")] - background_opacity: Alpha, - - /// Window configuration - #[serde(default, deserialize_with = "failure_default")] - window: WindowConfig, - - /// Keybindings - #[serde( - default = "default_key_bindings", - deserialize_with = "deserialize_key_bindings" - )] - key_bindings: Vec, - - /// Bindings for the mouse - #[serde( - default = "default_mouse_bindings", - deserialize_with = "deserialize_mouse_bindings" - )] - mouse_bindings: Vec, - - #[serde(default, deserialize_with = "failure_default")] - selection: Selection, - - #[serde(default, deserialize_with = "failure_default")] - mouse: Mouse, - - /// Path to a shell program to run on startup - #[serde(default, deserialize_with = "failure_default")] - shell: Option>, - - /// Path where config was loaded from - #[serde(default, deserialize_with = "failure_default")] - config_path: Option, - - /// Visual bell configuration - #[serde(default, deserialize_with = "failure_default")] - visual_bell: VisualBellConfig, - - /// Use dynamic title - #[serde( - default = "default_true_bool", - deserialize_with = "deserialize_true_bool" - )] - dynamic_title: bool, - - /// Live config reload - #[serde( - default = "default_true_bool", - deserialize_with = "deserialize_true_bool" - )] - live_config_reload: bool, - - /// Number of spaces in one tab - #[serde( - default = "default_tabspaces", - deserialize_with = "deserialize_tabspaces" - )] - tabspaces: usize, - - /// How much scrolling history to keep - #[serde(default, deserialize_with = "failure_default")] - scrolling: Scrolling, - - /// Cursor configuration - #[serde(default, deserialize_with = "failure_default")] - cursor: Cursor, - - /// Keep the log file after quitting - #[serde(default, deserialize_with = "failure_default")] - persistent_logging: bool, - - /// Enable experimental conpty backend instead of using winpty. - /// Will only take effect on Windows 10 Oct 2018 and later. - #[cfg(windows)] - #[serde(default, deserialize_with = "failure_default")] - enable_experimental_conpty_backend: bool, - - /// Send escape sequences using the alt key. - #[serde( - default = "default_true_bool", - deserialize_with = "deserialize_true_bool" - )] - alt_send_esc: bool, - - // TODO: DEPRECATED - custom_cursor_colors: Option, - - // TODO: DEPRECATED - hide_cursor_when_typing: Option, - - // TODO: DEPRECATED - cursor_style: Option, - - // TODO: DEPRECATED - unfocused_hollow_cursor: Option, - - // TODO: DEPRECATED - dimensions: Option, -} - -impl Default for Config { - fn default() -> Self { - serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG).expect("default config is invalid") - } -} - -fn default_key_bindings() -> Vec { - bindings::default_key_bindings() -} - -fn default_mouse_bindings() -> Vec { - bindings::default_mouse_bindings() -} - -fn deserialize_key_bindings<'a, D>( - deserializer: D, -) -> ::std::result::Result, D::Error> -where - D: de::Deserializer<'a>, -{ - deserialize_bindings(deserializer, bindings::default_key_bindings()) -} - -fn deserialize_mouse_bindings<'a, D>( - deserializer: D, -) -> ::std::result::Result, D::Error> -where - D: de::Deserializer<'a>, -{ - deserialize_bindings(deserializer, bindings::default_mouse_bindings()) -} - -fn deserialize_bindings<'a, D, T>( - deserializer: D, - mut default: Vec>, -) -> ::std::result::Result>, D::Error> -where - D: de::Deserializer<'a>, - T: Copy + Eq + std::hash::Hash + std::fmt::Debug, - Binding: de::Deserialize<'a>, -{ - let mut bindings: Vec> = failure_default_vec(deserializer)?; - - for binding in bindings.iter() { - default.retain(|b| !b.triggers_match(binding)); - } - - bindings.extend(default); - - Ok(bindings) -} - -fn failure_default_vec<'a, D, T>(deserializer: D) -> ::std::result::Result, D::Error> -where - D: de::Deserializer<'a>, - T: Deserialize<'a>, -{ - // Deserialize as generic vector - let vec = match Vec::::deserialize(deserializer) { - Ok(vec) => vec, - Err(err) => { - error!("Problem with config: {}; using empty vector", err); - return Ok(Vec::new()); - } - }; - - // Move to lossy vector - let mut bindings: Vec = Vec::new(); - for value in vec { - match T::deserialize(value) { - Ok(binding) => bindings.push(binding), - Err(err) => { - error!("Problem with config: {}; skipping value", err); - } - } - } - - Ok(bindings) -} - -fn default_tabspaces() -> usize { - 8 -} - -fn deserialize_tabspaces<'a, D>(deserializer: D) -> ::std::result::Result -where - D: de::Deserializer<'a>, -{ - match usize::deserialize(deserializer) { - Ok(value) => Ok(value), - Err(err) => { - error!("Problem with config: {}; using 8", err); - Ok(default_tabspaces()) - } - } -} - -fn deserialize_true_bool<'a, D>(deserializer: D) -> ::std::result::Result -where - D: de::Deserializer<'a>, -{ - match bool::deserialize(deserializer) { - Ok(value) => Ok(value), - Err(err) => { - error!("Problem with config: {}; using true", err); - Ok(true) - } - } -} - -fn default_true_bool() -> bool { - true -} - -fn failure_default<'a, D, T>(deserializer: D) -> ::std::result::Result -where - D: de::Deserializer<'a>, - T: Deserialize<'a> + Default, -{ - match T::deserialize(deserializer) { - Ok(value) => Ok(value), - Err(err) => { - error!("Problem with config: {}; using default value", err); - Ok(T::default()) - } - } -} - -/// Struct for scrolling related settings -#[serde(default)] -#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize)] -pub struct Scrolling { - #[serde(deserialize_with = "deserialize_scrolling_history")] - pub history: u32, - #[serde(deserialize_with = "deserialize_scrolling_multiplier")] - pub multiplier: u8, - #[serde(deserialize_with = "deserialize_scrolling_multiplier")] - pub faux_multiplier: u8, - #[serde(deserialize_with = "failure_default")] - pub auto_scroll: bool, -} - -impl Default for Scrolling { - fn default() -> Self { - Self { - history: default_scrolling_history(), - multiplier: default_scrolling_multiplier(), - faux_multiplier: default_scrolling_multiplier(), - auto_scroll: Default::default(), - } - } -} - -fn default_scrolling_history() -> u32 { - 10_000 -} - -// Default for normal and faux scrolling -fn default_scrolling_multiplier() -> u8 { - 3 -} - -fn deserialize_scrolling_history<'a, D>(deserializer: D) -> ::std::result::Result -where - D: de::Deserializer<'a>, -{ - match u32::deserialize(deserializer) { - Ok(lines) => { - if lines > MAX_SCROLLBACK_LINES { - error!( - "Problem with config: scrollback size is {}, but expected a maximum of {}; \ - using {1} instead", - lines, MAX_SCROLLBACK_LINES, - ); - Ok(MAX_SCROLLBACK_LINES) - } else { - Ok(lines) - } - } - Err(err) => { - error!("Problem with config: {}; using default value", err); - Ok(default_scrolling_history()) - } - } -} - -fn deserialize_scrolling_multiplier<'a, D>(deserializer: D) -> ::std::result::Result -where - D: de::Deserializer<'a>, -{ - match u8::deserialize(deserializer) { - Ok(lines) => Ok(lines), - Err(err) => { - error!("Problem with config: {}; using default value", err); - Ok(default_scrolling_multiplier()) - } - } -} - -/// Newtype for implementing deserialize on glutin Mods -/// -/// Our deserialize impl wouldn't be covered by a derive(Deserialize); see the -/// impl below. -#[derive(Debug, Copy, Clone, Hash, Default, Eq, PartialEq)] -struct ModsWrapper(ModifiersState); - -impl ModsWrapper { - fn into_inner(self) -> ModifiersState { - self.0 - } -} - -impl<'a> de::Deserialize<'a> for ModsWrapper { - fn deserialize(deserializer: D) -> ::std::result::Result - 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(self, value: &str) -> ::std::result::Result - where - E: de::Error, - { - let mut res = ModifiersState::default(); - for modifier in value.split('|') { - match modifier.trim() { - "Command" | "Super" => res.logo = true, - "Shift" => res.shift = true, - "Alt" | "Option" => res.alt = true, - "Control" => res.ctrl = true, - "None" => (), - _ => error!("Unknown modifier {:?}", modifier), - } - } - - Ok(ModsWrapper(res)) - } - } - - deserializer.deserialize_str(ModsVisitor) - } -} - -struct ActionWrapper(crate::input::Action); - -impl ActionWrapper { - fn into_inner(self) -> crate::input::Action { - self.0 - } -} - -impl<'a> de::Deserialize<'a> for ActionWrapper { - fn deserialize(deserializer: D) -> ::std::result::Result - where - D: de::Deserializer<'a>, - { - struct ActionVisitor; - - impl<'a> Visitor<'a> for ActionVisitor { - type Value = ActionWrapper; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str( - "Paste, Copy, PasteSelection, IncreaseFontSize, DecreaseFontSize, \ - ResetFontSize, ScrollPageUp, ScrollPageDown, ScrollToTop, \ - ScrollToBottom, ClearHistory, Hide, ClearLogNotice, SpawnNewInstance, \ - None or Quit", - ) - } - - fn visit_str(self, value: &str) -> ::std::result::Result - where - E: de::Error, - { - Ok(ActionWrapper(match value { - "Paste" => Action::Paste, - "Copy" => Action::Copy, - "PasteSelection" => Action::PasteSelection, - "IncreaseFontSize" => Action::IncreaseFontSize, - "DecreaseFontSize" => Action::DecreaseFontSize, - "ResetFontSize" => Action::ResetFontSize, - "ScrollPageUp" => Action::ScrollPageUp, - "ScrollPageDown" => Action::ScrollPageDown, - "ScrollToTop" => Action::ScrollToTop, - "ScrollToBottom" => Action::ScrollToBottom, - "ClearHistory" => Action::ClearHistory, - "Hide" => Action::Hide, - "Quit" => Action::Quit, - "ClearLogNotice" => Action::ClearLogNotice, - "SpawnNewInstance" => Action::SpawnNewInstance, - "None" => Action::None, - _ => return Err(E::invalid_value(Unexpected::Str(value), &self)), - })) - } - } - deserializer.deserialize_str(ActionVisitor) - } -} - -#[serde(untagged)] -#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] -pub enum CommandWrapper { - Just(String), - WithArgs { - program: String, - #[serde(default)] - args: Vec, - }, -} - -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, - } - } -} - -use crate::term::{mode, TermMode}; - -struct ModeWrapper { - pub mode: TermMode, - pub not_mode: TermMode, -} - -impl<'a> de::Deserialize<'a> for ModeWrapper { - fn deserialize(deserializer: D) -> ::std::result::Result - where - D: de::Deserializer<'a>, - { - struct ModeVisitor; - - impl<'a> Visitor<'a> for ModeVisitor { - type Value = ModeWrapper; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Combination of AppCursor | AppKeypad, possibly with negation (~)") - } - - fn visit_str(self, value: &str) -> ::std::result::Result - where - E: de::Error, - { - let mut res = ModeWrapper { - mode: TermMode::empty(), - not_mode: TermMode::empty(), - }; - - for modifier in value.split('|') { - match modifier.trim() { - "AppCursor" => res.mode |= mode::TermMode::APP_CURSOR, - "~AppCursor" => res.not_mode |= mode::TermMode::APP_CURSOR, - "AppKeypad" => res.mode |= mode::TermMode::APP_KEYPAD, - "~AppKeypad" => res.not_mode |= mode::TermMode::APP_KEYPAD, - "~Alt" => res.not_mode |= mode::TermMode::ALT_SCREEN, - "Alt" => res.mode |= mode::TermMode::ALT_SCREEN, - _ => error!("Unknown mode {:?}", modifier), - } - } - - Ok(res) - } - } - deserializer.deserialize_str(ModeVisitor) - } -} - -struct MouseButton(::glutin::MouseButton); - -impl MouseButton { - fn into_inner(self) -> ::glutin::MouseButton { - self.0 - } -} - -impl<'a> de::Deserialize<'a> for MouseButton { - fn deserialize(deserializer: D) -> ::std::result::Result - where - D: de::Deserializer<'a>, - { - struct MouseButtonVisitor; - - impl<'a> Visitor<'a> for MouseButtonVisitor { - type Value = MouseButton; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("Left, Right, Middle, or a number") - } - - fn visit_str(self, value: &str) -> ::std::result::Result - where - E: de::Error, - { - match value { - "Left" => Ok(MouseButton(::glutin::MouseButton::Left)), - "Right" => Ok(MouseButton(::glutin::MouseButton::Right)), - "Middle" => Ok(MouseButton(::glutin::MouseButton::Middle)), - _ => { - if let Ok(index) = u8::from_str(value) { - Ok(MouseButton(::glutin::MouseButton::Other(index))) - } else { - Err(E::invalid_value(Unexpected::Str(value), &self)) - } - } - } - } - } - - deserializer.deserialize_str(MouseButtonVisitor) - } -} - -/// Bindings are deserialized into a `RawBinding` before being parsed as a -/// `KeyBinding` or `MouseBinding`. -#[derive(PartialEq, Eq)] -struct RawBinding { - key: Option, - mouse: Option<::glutin::MouseButton>, - mods: ModifiersState, - mode: TermMode, - notmode: TermMode, - action: Action, -} - -impl RawBinding { - fn into_mouse_binding(self) -> ::std::result::Result { - if let Some(mouse) = self.mouse { - Ok(Binding { - trigger: mouse, - mods: self.mods, - action: self.action, - mode: self.mode, - notmode: self.notmode, - }) - } else { - Err(self) - } - } - - fn into_key_binding(self) -> ::std::result::Result { - 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> de::Deserialize<'a> for RawBinding { - fn deserialize(deserializer: D) -> ::std::result::Result - where - D: de::Deserializer<'a>, - { - enum Field { - Key, - Mods, - Mode, - Action, - Chars, - Mouse, - Command, - } - - impl<'a> de::Deserialize<'a> for Field { - fn deserialize(deserializer: D) -> ::std::result::Result - where - D: de::Deserializer<'a>, - { - struct FieldVisitor; - - static FIELDS: &'static [&'static str] = - &["key", "mods", "mode", "action", "chars", "mouse", "command"]; - - impl<'a> Visitor<'a> for FieldVisitor { - type Value = Field; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("binding fields") - } - - fn visit_str(self, value: &str) -> ::std::result::Result - 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(self, mut map: V) -> ::std::result::Result - where - V: MapAccess<'a>, - { - let mut mods: Option = None; - let mut key: Option = None; - let mut chars: Option = None; - let mut action: Option = None; - let mut mode: Option = None; - let mut not_mode: Option = None; - let mut mouse: Option<::glutin::MouseButton> = None; - let mut command: Option = None; - - use ::serde::de::Error; - - while let Some(struct_key) = map.next_key::()? { - match struct_key { - Field::Key => { - if key.is_some() { - return Err(::duplicate_field("key")); - } - - let val = map.next_value::()?; - if val.is_u64() { - let scancode = val.as_u64().unwrap(); - if scancode > u64::from(::std::u32::MAX) { - return Err(::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(::duplicate_field("mods")); - } - - mods = Some(map.next_value::()?.into_inner()); - } - Field::Mode => { - if mode.is_some() { - return Err(::duplicate_field("mode")); - } - - let mode_deserializer = map.next_value::()?; - mode = Some(mode_deserializer.mode); - not_mode = Some(mode_deserializer.not_mode); - } - Field::Action => { - if action.is_some() { - return Err(::duplicate_field("action")); - } - - action = Some(map.next_value::()?.into_inner()); - } - Field::Chars => { - if chars.is_some() { - return Err(::duplicate_field("chars")); - } - - chars = Some(map.next_value()?); - } - Field::Mouse => { - if chars.is_some() { - return Err(::duplicate_field("mouse")); - } - - mouse = Some(map.next_value::()?.into_inner()); - } - Field::Command => { - if command.is_some() { - return Err(::duplicate_field("command")); - } - - command = Some(map.next_value::()?); - } - } - } - - 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> de::Deserialize<'a> for Alpha { - fn deserialize(deserializer: D) -> ::std::result::Result - where - D: de::Deserializer<'a>, - { - let value = f32::deserialize(deserializer)?; - Ok(Alpha::new(value)) - } -} - -impl<'a> de::Deserialize<'a> for MouseBinding { - fn deserialize(deserializer: D) -> ::std::result::Result - where - D: de::Deserializer<'a>, - { - let raw = RawBinding::deserialize(deserializer)?; - raw.into_mouse_binding() - .map_err(|_| D::Error::custom("expected mouse binding")) - } -} - -impl<'a> de::Deserialize<'a> for KeyBinding { - fn deserialize(deserializer: D) -> ::std::result::Result - where - D: de::Deserializer<'a>, - { - let raw = RawBinding::deserialize(deserializer)?; - raw.into_key_binding() - .map_err(|_| D::Error::custom("expected key binding")) - } -} - -/// Errors occurring during config loading -#[derive(Debug)] -pub enum Error { - /// Config file not found - NotFound, - - /// Config file empty - Empty, - - /// Couldn't read $HOME environment variable - ReadingEnvHome(env::VarError), - - /// io error reading file - Io(io::Error), - - /// Not valid yaml or missing parameters - Yaml(serde_yaml::Error), -} - -#[serde(default)] -#[derive(Debug, Deserialize, PartialEq, Eq)] -pub struct Colors { - #[serde(deserialize_with = "failure_default")] - pub primary: PrimaryColors, - #[serde(deserialize_with = "failure_default")] - pub cursor: CursorColors, - #[serde(deserialize_with = "failure_default")] - pub selection: SelectionColors, - #[serde(deserialize_with = "deserialize_normal_colors")] - pub normal: AnsiColors, - #[serde(deserialize_with = "deserialize_bright_colors")] - pub bright: AnsiColors, - #[serde(deserialize_with = "failure_default")] - pub dim: Option, - #[serde(deserialize_with = "failure_default_vec")] - pub indexed_colors: Vec, -} - -impl Default for Colors { - fn default() -> Colors { - Colors { - primary: Default::default(), - cursor: Default::default(), - selection: Default::default(), - normal: default_normal_colors(), - bright: default_bright_colors(), - dim: Default::default(), - indexed_colors: Default::default(), - } - } -} - -fn default_normal_colors() -> AnsiColors { - AnsiColors { - black: Rgb { - r: 0x00, - g: 0x00, - b: 0x00, - }, - red: Rgb { - r: 0xd5, - g: 0x4e, - b: 0x53, - }, - green: Rgb { - r: 0xb9, - g: 0xca, - b: 0x4a, - }, - yellow: Rgb { - r: 0xe6, - g: 0xc5, - b: 0x47, - }, - blue: Rgb { - r: 0x7a, - g: 0xa6, - b: 0xda, - }, - magenta: Rgb { - r: 0xc3, - g: 0x97, - b: 0xd8, - }, - cyan: Rgb { - r: 0x70, - g: 0xc0, - b: 0xba, - }, - white: Rgb { - r: 0xea, - g: 0xea, - b: 0xea, - }, - } -} - -fn default_bright_colors() -> AnsiColors { - AnsiColors { - black: Rgb { - r: 0x66, - g: 0x66, - b: 0x66, - }, - red: Rgb { - r: 0xff, - g: 0x33, - b: 0x34, - }, - green: Rgb { - r: 0x9e, - g: 0xc4, - b: 0x00, - }, - yellow: Rgb { - r: 0xe7, - g: 0xc5, - b: 0x47, - }, - blue: Rgb { - r: 0x7a, - g: 0xa6, - b: 0xda, - }, - magenta: Rgb { - r: 0xb7, - g: 0x7e, - b: 0xe0, - }, - cyan: Rgb { - r: 0x54, - g: 0xce, - b: 0xd6, - }, - white: Rgb { - r: 0xff, - g: 0xff, - b: 0xff, - }, - } -} - -fn deserialize_normal_colors<'a, D>(deserializer: D) -> ::std::result::Result -where - D: de::Deserializer<'a>, -{ - match AnsiColors::deserialize(deserializer) { - Ok(escape_chars) => Ok(escape_chars), - Err(err) => { - error!("Problem with config: {}; using default value", err); - Ok(default_normal_colors()) - } - } -} - -fn deserialize_bright_colors<'a, D>(deserializer: D) -> ::std::result::Result -where - D: de::Deserializer<'a>, -{ - match AnsiColors::deserialize(deserializer) { - Ok(escape_chars) => Ok(escape_chars), - Err(err) => { - error!("Problem with config: {}; using default value", err); - Ok(default_bright_colors()) - } - } -} - -#[derive(Debug, Deserialize, PartialEq, Eq)] -pub struct IndexedColor { - #[serde(deserialize_with = "deserialize_color_index")] - pub index: u8, - #[serde(deserialize_with = "rgb_from_hex")] - pub color: Rgb, -} - -fn deserialize_color_index<'a, D>(deserializer: D) -> ::std::result::Result -where - D: de::Deserializer<'a>, -{ - match u8::deserialize(deserializer) { - Ok(index) => { - if index < 16 { - error!( - "Problem with config: indexed_color's index is {}, \ - but a value bigger than 15 was expected; \ - ignoring setting", - index - ); - - // Return value out of range to ignore this color - Ok(0) - } else { - Ok(index) - } - } - Err(err) => { - error!("Problem with config: {}; ignoring setting", err); - - // Return value out of range to ignore this color - Ok(0) - } - } -} - -#[serde(default)] -#[derive(Copy, Clone, Debug, Deserialize, PartialEq, Eq)] -pub struct Cursor { - #[serde(deserialize_with = "failure_default")] - pub style: CursorStyle, - #[serde(deserialize_with = "deserialize_true_bool")] - pub unfocused_hollow: bool, -} - -impl Default for Cursor { - fn default() -> Self { - Self { - style: Default::default(), - unfocused_hollow: true, - } - } -} - -#[serde(default)] -#[derive(Debug, Copy, Clone, Default, Deserialize, PartialEq, Eq)] -pub struct CursorColors { - #[serde(deserialize_with = "deserialize_optional_color")] - pub text: Option, - #[serde(deserialize_with = "deserialize_optional_color")] - pub cursor: Option, -} - -#[serde(default)] -#[derive(Debug, Copy, Clone, Default, Deserialize, PartialEq, Eq)] -pub struct SelectionColors { - #[serde(deserialize_with = "deserialize_optional_color")] - pub text: Option, - #[serde(deserialize_with = "deserialize_optional_color")] - pub background: Option, -} - -#[serde(default)] -#[derive(Debug, Deserialize, PartialEq, Eq)] -pub struct PrimaryColors { - #[serde(deserialize_with = "rgb_from_hex")] - pub background: Rgb, - #[serde(deserialize_with = "rgb_from_hex")] - pub foreground: Rgb, - #[serde(deserialize_with = "deserialize_optional_color")] - pub bright_foreground: Option, - #[serde(deserialize_with = "deserialize_optional_color")] - pub dim_foreground: Option, -} - -impl Default for PrimaryColors { - fn default() -> Self { - PrimaryColors { - background: default_background(), - foreground: default_foreground(), - bright_foreground: Default::default(), - dim_foreground: Default::default(), - } - } -} - -fn deserialize_optional_color<'a, D>( - deserializer: D, -) -> ::std::result::Result, D::Error> -where - D: de::Deserializer<'a>, -{ - match Option::deserialize(deserializer) { - Ok(Some(color)) => { - let color: serde_yaml::Value = color; - Ok(Some(rgb_from_hex(color).unwrap())) - } - Ok(None) => Ok(None), - Err(err) => { - error!( - "Problem with config: {}; using standard foreground color", - err - ); - Ok(None) - } - } -} - -fn default_background() -> Rgb { - Rgb { r: 0, g: 0, b: 0 } -} - -fn default_foreground() -> Rgb { - Rgb { - r: 0xea, - g: 0xea, - b: 0xea, - } -} - -/// The 8-colors sections of config -#[derive(Debug, Deserialize, PartialEq, Eq)] -pub struct AnsiColors { - #[serde(deserialize_with = "rgb_from_hex")] - pub black: Rgb, - #[serde(deserialize_with = "rgb_from_hex")] - pub red: Rgb, - #[serde(deserialize_with = "rgb_from_hex")] - pub green: Rgb, - #[serde(deserialize_with = "rgb_from_hex")] - pub yellow: Rgb, - #[serde(deserialize_with = "rgb_from_hex")] - pub blue: Rgb, - #[serde(deserialize_with = "rgb_from_hex")] - pub magenta: Rgb, - #[serde(deserialize_with = "rgb_from_hex")] - pub cyan: Rgb, - #[serde(deserialize_with = "rgb_from_hex")] - pub white: Rgb, -} - -/// Deserialize an Rgb from a hex string -/// -/// This is *not* the deserialize impl for Rgb since we want a symmetric -/// serialize/deserialize impl for ref tests. -fn rgb_from_hex<'a, D>(deserializer: D) -> ::std::result::Result -where - D: de::Deserializer<'a>, -{ - struct RgbVisitor; - - impl<'a> Visitor<'a> for RgbVisitor { - type Value = Rgb; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("hex color like 0xff00ff") - } - - fn visit_str(self, value: &str) -> ::std::result::Result - where - E: ::serde::de::Error, - { - Rgb::from_str(&value[..]) - .map_err(|_| E::custom("failed to parse rgb; expected hex color like 0xff00ff")) - } - } - - let rgb = deserializer.deserialize_str(RgbVisitor); - - // Use #ff00ff as fallback color - match rgb { - Ok(rgb) => Ok(rgb), - Err(err) => { - error!("Problem with config: {}; using color #ff00ff", err); - Ok(Rgb { - r: 255, - g: 0, - b: 255, - }) - } - } -} - -impl FromStr for Rgb { - type Err = (); - fn from_str(s: &str) -> ::std::result::Result { - let mut chars = s.chars(); - let mut rgb = Rgb::default(); - - macro_rules! component { - ($($c:ident),*) => { - $( - match chars.next().and_then(|c| c.to_digit(16)) { - Some(val) => rgb.$c = (val as u8) << 4, - None => return Err(()) - } - - match chars.next().and_then(|c| c.to_digit(16)) { - Some(val) => rgb.$c |= val as u8, - None => return Err(()) - } - )* - } - } - - match chars.next() { - Some('0') => { - if chars.next() != Some('x') { - return Err(()); - } - } - Some('#') => (), - _ => return Err(()), - } - - component!(r, g, b); - - Ok(rgb) - } -} - -impl ::std::error::Error for Error { - fn cause(&self) -> Option<&dyn (::std::error::Error)> { - match *self { - Error::NotFound | Error::Empty => None, - Error::ReadingEnvHome(ref err) => Some(err), - Error::Io(ref err) => Some(err), - Error::Yaml(ref err) => Some(err), - } - } - - fn description(&self) -> &str { - match *self { - Error::NotFound => "Couldn't locate config file", - Error::Empty => "Empty config file", - Error::ReadingEnvHome(ref err) => err.description(), - Error::Io(ref err) => err.description(), - Error::Yaml(ref err) => err.description(), - } - } -} - -impl ::std::fmt::Display for Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - match *self { - Error::NotFound | Error::Empty => { - write!(f, "{}", ::std::error::Error::description(self)) - } - Error::ReadingEnvHome(ref err) => { - write!(f, "Couldn't read $HOME environment variable: {}", err) - } - Error::Io(ref err) => write!(f, "Error reading config file: {}", err), - Error::Yaml(ref err) => write!(f, "Problem with config: {}", err), - } - } -} - -impl From for Error { - fn from(val: env::VarError) -> Error { - Error::ReadingEnvHome(val) - } -} - -impl From for Error { - fn from(val: io::Error) -> Error { - if val.kind() == io::ErrorKind::NotFound { - Error::NotFound - } else { - Error::Io(val) - } - } -} - -impl From for Error { - fn from(val: serde_yaml::Error) -> Error { - Error::Yaml(val) - } -} - -/// Result from config loading -pub type Result = ::std::result::Result; - -impl Config { - /// Get the location of the first found default config file paths - /// according to the following order: - /// - /// 1. $XDG_CONFIG_HOME/alacritty/alacritty.yml - /// 2. $XDG_CONFIG_HOME/alacritty.yml - /// 3. $HOME/.config/alacritty/alacritty.yml - /// 4. $HOME/.alacritty.yml - #[cfg(not(windows))] - pub fn installed_config<'a>() -> Option> { - // Try using XDG location by default - ::xdg::BaseDirectories::with_prefix("alacritty") - .ok() - .and_then(|xdg| xdg.find_config_file("alacritty.yml")) - .or_else(|| { - ::xdg::BaseDirectories::new() - .ok() - .and_then(|fallback| fallback.find_config_file("alacritty.yml")) - }) - .or_else(|| { - if let Ok(home) = env::var("HOME") { - // Fallback path: $HOME/.config/alacritty/alacritty.yml - let fallback = PathBuf::from(&home).join(".config/alacritty/alacritty.yml"); - if fallback.exists() { - return Some(fallback); - } - // Fallback path: $HOME/.alacritty.yml - let fallback = PathBuf::from(&home).join(".alacritty.yml"); - if fallback.exists() { - return Some(fallback); - } - } - None - }) - .map(Into::into) - } - - // TODO: Remove old configuration location warning (Deprecated 03/12/2018) - #[cfg(windows)] - pub fn installed_config<'a>() -> Option> { - let old = dirs::home_dir().map(|path| path.join("alacritty.yml")); - let new = dirs::config_dir().map(|path| path.join("alacritty\\alacritty.yml")); - - if let Some(old_path) = old.as_ref().filter(|old| old.exists()) { - warn!( - "Found configuration at: {}; this file should be moved to the new location: {}", - old_path.to_string_lossy(), - new.as_ref().map(|new| new.to_string_lossy()).unwrap(), - ); - - old.map(Cow::from) - } else { - new.filter(|new| new.exists()).map(Cow::from) - } - } - - #[cfg(not(windows))] - pub fn write_defaults() -> io::Result> { - let path = xdg::BaseDirectories::with_prefix("alacritty") - .map_err(|err| io::Error::new(io::ErrorKind::NotFound, err.to_string().as_str())) - .and_then(|p| p.place_config_file("alacritty.yml"))?; - - File::create(&path)?.write_all(DEFAULT_ALACRITTY_CONFIG.as_bytes())?; - - Ok(path.into()) - } - - #[cfg(windows)] - pub fn write_defaults() -> io::Result> { - let mut path = dirs::config_dir().ok_or_else(|| { - io::Error::new(io::ErrorKind::NotFound, "Couldn't find profile directory") - })?; - - path = path.join("alacritty/alacritty.yml"); - - std::fs::create_dir_all(path.parent().unwrap())?; - - File::create(&path)?.write_all(DEFAULT_ALACRITTY_CONFIG.as_bytes())?; - - Ok(path.into()) - } - - /// Get list of colors - /// - /// The ordering returned here is expected by the terminal. Colors are simply indexed in this - /// array for performance. - pub fn colors(&self) -> &Colors { - &self.colors - } - - #[inline] - pub fn background_opacity(&self) -> Alpha { - self.background_opacity - } - - pub fn key_bindings(&self) -> &[KeyBinding] { - &self.key_bindings[..] - } - - pub fn mouse_bindings(&self) -> &[MouseBinding] { - &self.mouse_bindings[..] - } - - pub fn mouse(&self) -> &Mouse { - &self.mouse - } - - pub fn selection(&self) -> &Selection { - &self.selection - } - - pub fn tabspaces(&self) -> usize { - self.tabspaces - } - - pub fn padding(&self) -> &Delta { - self.padding.as_ref().unwrap_or(&self.window.padding) - } - - #[inline] - pub fn draw_bold_text_with_bright_colors(&self) -> bool { - self.draw_bold_text_with_bright_colors - } - - /// Get font config - #[inline] - pub fn font(&self) -> &Font { - &self.font - } - - /// Get window dimensions - #[inline] - pub fn dimensions(&self) -> Dimensions { - self.dimensions.unwrap_or(self.window.dimensions) - } - - #[inline] - pub fn position(&self) -> Option> { - self.window.position - } - - /// Get window config - #[inline] - pub fn window(&self) -> &WindowConfig { - &self.window - } - - /// Get visual bell config - #[inline] - pub fn visual_bell(&self) -> &VisualBellConfig { - &self.visual_bell - } - - /// Should show render timer - #[inline] - pub fn render_timer(&self) -> bool { - self.render_timer - } - - #[cfg(target_os = "macos")] - #[inline] - pub fn use_thin_strokes(&self) -> bool { - self.font.use_thin_strokes - } - - #[cfg(not(target_os = "macos"))] - #[inline] - pub fn use_thin_strokes(&self) -> bool { - false - } - - pub fn path(&self) -> Option<&Path> { - self.config_path.as_ref().map(PathBuf::as_path) - } - - pub fn shell(&self) -> Option<&Shell<'_>> { - self.shell.as_ref() - } - - pub fn env(&self) -> &HashMap { - &self.env - } - - /// Should hide mouse cursor when typing - #[inline] - pub fn hide_mouse_when_typing(&self) -> bool { - self.hide_cursor_when_typing - .unwrap_or(self.mouse.hide_when_typing) - } - - /// Style of the cursor - #[inline] - pub fn cursor_style(&self) -> CursorStyle { - self.cursor_style.unwrap_or(self.cursor.style) - } - - /// Use hollow block cursor when unfocused - #[inline] - pub fn unfocused_hollow_cursor(&self) -> bool { - self.unfocused_hollow_cursor - .unwrap_or(self.cursor.unfocused_hollow) - } - - /// Live config reload - #[inline] - pub fn live_config_reload(&self) -> bool { - self.live_config_reload - } - - #[inline] - pub fn dynamic_title(&self) -> bool { - self.dynamic_title - } - - /// Scrolling settings - #[inline] - pub fn scrolling(&self) -> Scrolling { - self.scrolling - } - - /// Cursor foreground color - #[inline] - pub fn cursor_text_color(&self) -> Option { - self.colors - .cursor - .text - .map(|_| Color::Named(NamedColor::CursorText)) - } - - /// Cursor background color - #[inline] - pub fn cursor_cursor_color(&self) -> Option { - self.colors - .cursor - .cursor - .map(|_| Color::Named(NamedColor::Cursor)) - } - - /// Enable experimental conpty backend (Windows only) - #[cfg(windows)] - #[inline] - pub fn enable_experimental_conpty_backend(&self) -> bool { - self.enable_experimental_conpty_backend - } - - /// Send escape sequences using the alt key - #[inline] - pub fn alt_send_esc(&self) -> bool { - self.alt_send_esc - } - - // Update the history size, used in ref tests - pub fn set_history(&mut self, history: u32) { - self.scrolling.history = history; - } - - /// Keep the log file after quitting Alacritty - #[inline] - pub fn persistent_logging(&self) -> bool { - self.persistent_logging - } - - /// Overrides the `dynamic_title` configuration based on `--title`. - pub fn update_dynamic_title(mut self, options: &Options) -> Self { - if options.title.is_some() { - self.dynamic_title = false; - } - self - } - - pub fn load_from(path: PathBuf) -> Config { - let mut config = Config::reload_from(&path).unwrap_or_else(|_| Config::default()); - config.config_path = Some(path); - config - } - - pub fn reload_from(path: &PathBuf) -> Result { - match Config::read_config(path) { - Ok(config) => Ok(config), - Err(err) => { - error!("Unable to load config {:?}: {}", path, err); - Err(err) - } - } - } - - fn read_config(path: &PathBuf) -> Result { - let mut contents = String::new(); - File::open(path)?.read_to_string(&mut contents)?; - - // Prevent parsing error with empty string - if contents.is_empty() { - return Ok(Config::default()); - } - - let mut config: Config = serde_yaml::from_str(&contents)?; - config.print_deprecation_warnings(); - - Ok(config) - } - - fn print_deprecation_warnings(&mut self) { - if self.dimensions.is_some() { - warn!( - "Config dimensions is deprecated; \ - please use window.dimensions instead" - ); - } - - if self.padding.is_some() { - warn!( - "Config padding is deprecated; \ - please use window.padding instead" - ); - } - - if self.mouse.faux_scrollback_lines.is_some() { - warn!( - "Config mouse.faux_scrollback_lines is deprecated; \ - please use mouse.faux_scrolling_lines instead" - ); - } - - if let Some(custom_cursor_colors) = self.custom_cursor_colors { - warn!("Config custom_cursor_colors is deprecated"); - - if !custom_cursor_colors { - self.colors.cursor.cursor = None; - self.colors.cursor.text = None; - } - } - - if self.cursor_style.is_some() { - warn!( - "Config cursor_style is deprecated; \ - please use cursor.style instead" - ); - } - - if self.hide_cursor_when_typing.is_some() { - warn!( - "Config hide_cursor_when_typing is deprecated; \ - please use mouse.hide_when_typing instead" - ); - } - - if self.unfocused_hollow_cursor.is_some() { - warn!( - "Config unfocused_hollow_cursor is deprecated; \ - please use cursor.unfocused_hollow instead" - ); - } - } -} - -/// Window Dimensions -/// -/// Newtype to avoid passing values incorrectly -#[serde(default)] -#[derive(Default, Debug, Copy, Clone, Deserialize, PartialEq, Eq)] -pub struct Dimensions { - /// Window width in character columns - #[serde(deserialize_with = "failure_default")] - columns: Column, - - /// Window Height in character lines - #[serde(deserialize_with = "failure_default")] - lines: Line, -} - -impl Dimensions { - pub fn new(columns: Column, lines: Line) -> Self { - Dimensions { columns, lines } - } - - /// Get lines - #[inline] - pub fn lines_u32(&self) -> u32 { - self.lines.0 as u32 - } - - /// Get columns - #[inline] - pub fn columns_u32(&self) -> u32 { - self.columns.0 as u32 - } -} - -/// A delta for a point in a 2 dimensional plane -#[serde(default, bound(deserialize = "T: Deserialize<'de> + Default"))] -#[derive(Clone, Copy, Debug, Default, Deserialize, PartialEq, Eq)] -pub struct Delta { - /// Horizontal change - #[serde(deserialize_with = "failure_default")] - pub x: T, - /// Vertical change - #[serde(deserialize_with = "failure_default")] - pub y: T, -} - -trait DeserializeSize: Sized { - fn deserialize<'a, D>(_: D) -> ::std::result::Result - where - D: serde::de::Deserializer<'a>; -} - -impl DeserializeSize for Size { - fn deserialize<'a, D>(deserializer: D) -> ::std::result::Result - where - D: serde::de::Deserializer<'a>, - { - use std::marker::PhantomData; - - struct NumVisitor<__D> { - _marker: PhantomData<__D>, - } - - impl<'a, __D> Visitor<'a> for NumVisitor<__D> - where - __D: serde::de::Deserializer<'a>, - { - type Value = f64; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("f64 or u64") - } - - fn visit_f64(self, value: f64) -> ::std::result::Result - where - E: ::serde::de::Error, - { - Ok(value) - } - - fn visit_u64(self, value: u64) -> ::std::result::Result - where - E: ::serde::de::Error, - { - Ok(value as f64) - } - } - - let size = deserializer - .deserialize_any(NumVisitor:: { - _marker: PhantomData, - }) - .map(|v| Size::new(v as _)); - - // Use default font size as fallback - match size { - Ok(size) => Ok(size), - Err(err) => { - let size = default_font_size(); - error!( - "Problem with config: {}; using size {}", - err, - size.as_f32_pts() - ); - Ok(size) - } - } - } -} - -/// Font config -/// -/// Defaults are provided at the level of this struct per platform, but not per -/// field in this struct. It might be nice in the future to have defaults for -/// each value independently. Alternatively, maybe erroring when the user -/// doesn't provide complete config is Ok. -#[serde(default)] -#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] -pub struct Font { - /// Normal font face - #[serde(deserialize_with = "failure_default")] - normal: FontDescription, - - /// Bold font face - #[serde(deserialize_with = "failure_default")] - italic: SecondaryFontDescription, - - /// Italic font face - #[serde(deserialize_with = "failure_default")] - bold: SecondaryFontDescription, - - /// Font size in points - #[serde(deserialize_with = "DeserializeSize::deserialize")] - pub size: Size, - - /// Extra spacing per character - #[serde(deserialize_with = "failure_default")] - offset: Delta, - - /// Glyph offset within character cell - #[serde(deserialize_with = "failure_default")] - glyph_offset: Delta, - - #[cfg(target_os = "macos")] - #[serde(deserialize_with = "deserialize_true_bool")] - use_thin_strokes: bool, - - // TODO: Deprecated - #[serde(deserialize_with = "deserialize_scale_with_dpi")] - scale_with_dpi: Option<()>, -} - -impl Default for Font { - fn default() -> Font { - Font { - #[cfg(target_os = "macos")] - use_thin_strokes: true, - size: default_font_size(), - normal: Default::default(), - bold: Default::default(), - italic: Default::default(), - scale_with_dpi: Default::default(), - glyph_offset: Default::default(), - offset: Default::default(), - } - } -} - -impl Font { - /// Get the font size in points - #[inline] - pub fn size(&self) -> Size { - self.size - } - - /// Get offsets to font metrics - #[inline] - pub fn offset(&self) -> &Delta { - &self.offset - } - - /// Get cell offsets for glyphs - #[inline] - pub fn glyph_offset(&self) -> &Delta { - &self.glyph_offset - } - - /// Get a font clone with a size modification - pub fn with_size(self, size: Size) -> Font { - Font { size, ..self } - } - - // Get normal font description - pub fn normal(&self) -> &FontDescription { - &self.normal - } - - // Get italic font description - pub fn italic(&self) -> FontDescription { - self.italic.desc(&self.normal) - } - - // Get bold font description - pub fn bold(&self) -> FontDescription { - self.bold.desc(&self.normal) - } -} - -fn default_font_size() -> Size { - Size::new(11.) -} - -fn deserialize_scale_with_dpi<'a, D>(deserializer: D) -> ::std::result::Result, D::Error> -where - D: de::Deserializer<'a>, -{ - // This is necessary in order to get serde to complete deserialization of the configuration - let _ignored = bool::deserialize(deserializer); - error!( - "The scale_with_dpi setting has been removed, \ - on X11 the WINIT_HIDPI_FACTOR environment variable can be used instead." - ); - Ok(None) -} - -/// Description of the normal font -#[serde(default)] -#[derive(Debug, Deserialize, Clone, PartialEq, Eq)] -pub struct FontDescription { - #[serde(deserialize_with = "failure_default")] - pub family: String, - #[serde(deserialize_with = "failure_default")] - pub style: Option, -} - -impl Default for FontDescription { - fn default() -> FontDescription { - FontDescription { - #[cfg(not(any(target_os = "macos", windows)))] - family: "monospace".into(), - #[cfg(target_os = "macos")] - family: "Menlo".into(), - #[cfg(windows)] - family: "Consolas".into(), - style: None, - } - } -} - -/// Description of the italic and bold font -#[serde(default)] -#[derive(Debug, Default, Deserialize, Clone, PartialEq, Eq)] -pub struct SecondaryFontDescription { - #[serde(deserialize_with = "failure_default")] - family: Option, - #[serde(deserialize_with = "failure_default")] - style: Option, -} - -impl SecondaryFontDescription { - pub fn desc(&self, fallback: &FontDescription) -> FontDescription { - FontDescription { - family: self - .family - .clone() - .unwrap_or_else(|| fallback.family.clone()), - style: self.style.clone(), - } - } -} - -pub struct Monitor { - _thread: ::std::thread::JoinHandle<()>, - rx: mpsc::Receiver, -} - -pub trait OnConfigReload { - fn on_config_reload(&mut self); -} - -impl OnConfigReload for crate::display::Notifier { - fn on_config_reload(&mut self) { - self.notify(); - } -} - -impl Monitor { - /// Get pending config changes - pub fn pending(&self) -> Option { - let mut config = None; - while let Ok(new) = self.rx.try_recv() { - config = Some(new); - } - - config - } - - pub fn new(path: P, mut handler: H) -> Monitor - where - H: OnConfigReload + Send + 'static, - P: Into, - { - let path = path.into(); - - let (config_tx, config_rx) = mpsc::channel(); - - Monitor { - _thread: crate::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; - } - - let _ = config_tx.send(path); - handler.on_config_reload(); - } - _ => {} - } - } - }), - rx: config_rx, - } - } -} - -#[derive(Deserialize, Copy, Clone, Debug, Eq, PartialEq, Hash)] -pub enum Key { - Scancode(u32), - Key1, - Key2, - Key3, - Key4, - Key5, - Key6, - Key7, - Key8, - Key9, - Key0, - A, - B, - C, - D, - E, - F, - G, - H, - I, - J, - K, - L, - M, - N, - O, - P, - Q, - R, - S, - T, - U, - V, - W, - X, - Y, - Z, - Escape, - F1, - F2, - F3, - F4, - F5, - F6, - F7, - F8, - F9, - F10, - F11, - F12, - F13, - F14, - F15, - F16, - F17, - F18, - F19, - F20, - F21, - F22, - F23, - F24, - Snapshot, - Scroll, - Pause, - Insert, - Home, - Delete, - End, - PageDown, - PageUp, - Left, - Up, - Right, - Down, - Back, - Return, - Space, - Compose, - Numlock, - Numpad0, - Numpad1, - Numpad2, - Numpad3, - Numpad4, - Numpad5, - Numpad6, - Numpad7, - Numpad8, - Numpad9, - AbntC1, - AbntC2, - Add, - Apostrophe, - Apps, - At, - Ax, - Backslash, - Calculator, - Capital, - Colon, - Comma, - Convert, - Decimal, - Divide, - Equals, - Grave, - Kana, - Kanji, - LAlt, - LBracket, - LControl, - LShift, - LWin, - Mail, - MediaSelect, - MediaStop, - Minus, - Multiply, - Mute, - MyComputer, - NavigateForward, - NavigateBackward, - NextTrack, - NoConvert, - NumpadComma, - NumpadEnter, - NumpadEquals, - OEM102, - Period, - PlayPause, - Power, - PrevTrack, - RAlt, - RBracket, - RControl, - RShift, - RWin, - Semicolon, - Slash, - Sleep, - Stop, - Subtract, - Sysrq, - Tab, - Underline, - Unlabeled, - VolumeDown, - VolumeUp, - Wake, - WebBack, - WebFavorites, - WebForward, - WebHome, - WebRefresh, - WebSearch, - WebStop, - Yen, - Caret, - Copy, - Paste, - Cut, -} - -impl Key { - pub fn from_glutin_input(key: ::glutin::VirtualKeyCode) -> Self { - use glutin::VirtualKeyCode::*; - // Thank you, vim macros and regex! - match key { - Key1 => Key::Key1, - Key2 => Key::Key2, - Key3 => Key::Key3, - Key4 => Key::Key4, - Key5 => Key::Key5, - Key6 => Key::Key6, - Key7 => Key::Key7, - Key8 => Key::Key8, - Key9 => Key::Key9, - Key0 => Key::Key0, - A => Key::A, - B => Key::B, - C => Key::C, - D => Key::D, - E => Key::E, - F => Key::F, - G => Key::G, - H => Key::H, - I => Key::I, - J => Key::J, - K => Key::K, - L => Key::L, - M => Key::M, - N => Key::N, - O => Key::O, - P => Key::P, - Q => Key::Q, - R => Key::R, - S => Key::S, - T => Key::T, - U => Key::U, - V => Key::V, - W => Key::W, - X => Key::X, - Y => Key::Y, - Z => Key::Z, - Escape => Key::Escape, - F1 => Key::F1, - F2 => Key::F2, - F3 => Key::F3, - F4 => Key::F4, - F5 => Key::F5, - F6 => Key::F6, - F7 => Key::F7, - F8 => Key::F8, - F9 => Key::F9, - F10 => Key::F10, - F11 => Key::F11, - F12 => Key::F12, - F13 => Key::F13, - F14 => Key::F14, - F15 => Key::F15, - F16 => Key::F16, - F17 => Key::F17, - F18 => Key::F18, - F19 => Key::F19, - F20 => Key::F20, - F21 => Key::F21, - F22 => Key::F22, - F23 => Key::F23, - F24 => Key::F24, - Snapshot => Key::Snapshot, - Scroll => Key::Scroll, - Pause => Key::Pause, - Insert => Key::Insert, - Home => Key::Home, - Delete => Key::Delete, - End => Key::End, - PageDown => Key::PageDown, - PageUp => Key::PageUp, - Left => Key::Left, - Up => Key::Up, - Right => Key::Right, - Down => Key::Down, - Back => Key::Back, - Return => Key::Return, - Space => Key::Space, - Compose => Key::Compose, - Numlock => Key::Numlock, - Numpad0 => Key::Numpad0, - Numpad1 => Key::Numpad1, - Numpad2 => Key::Numpad2, - Numpad3 => Key::Numpad3, - Numpad4 => Key::Numpad4, - Numpad5 => Key::Numpad5, - Numpad6 => Key::Numpad6, - Numpad7 => Key::Numpad7, - Numpad8 => Key::Numpad8, - Numpad9 => Key::Numpad9, - AbntC1 => Key::AbntC1, - AbntC2 => Key::AbntC2, - Add => Key::Add, - Apostrophe => Key::Apostrophe, - Apps => Key::Apps, - At => Key::At, - Ax => Key::Ax, - Backslash => Key::Backslash, - Calculator => Key::Calculator, - Capital => Key::Capital, - Colon => Key::Colon, - Comma => Key::Comma, - Convert => Key::Convert, - Decimal => Key::Decimal, - Divide => Key::Divide, - Equals => Key::Equals, - Grave => Key::Grave, - Kana => Key::Kana, - Kanji => Key::Kanji, - LAlt => Key::LAlt, - LBracket => Key::LBracket, - LControl => Key::LControl, - LShift => Key::LShift, - LWin => Key::LWin, - Mail => Key::Mail, - MediaSelect => Key::MediaSelect, - MediaStop => Key::MediaStop, - Minus => Key::Minus, - Multiply => Key::Multiply, - Mute => Key::Mute, - MyComputer => Key::MyComputer, - NavigateForward => Key::NavigateForward, - NavigateBackward => Key::NavigateBackward, - NextTrack => Key::NextTrack, - NoConvert => Key::NoConvert, - NumpadComma => Key::NumpadComma, - NumpadEnter => Key::NumpadEnter, - NumpadEquals => Key::NumpadEquals, - OEM102 => Key::OEM102, - Period => Key::Period, - PlayPause => Key::PlayPause, - Power => Key::Power, - PrevTrack => Key::PrevTrack, - RAlt => Key::RAlt, - RBracket => Key::RBracket, - RControl => Key::RControl, - RShift => Key::RShift, - RWin => Key::RWin, - Semicolon => Key::Semicolon, - Slash => Key::Slash, - Sleep => Key::Sleep, - Stop => Key::Stop, - Subtract => Key::Subtract, - Sysrq => Key::Sysrq, - Tab => Key::Tab, - Underline => Key::Underline, - Unlabeled => Key::Unlabeled, - VolumeDown => Key::VolumeDown, - VolumeUp => Key::VolumeUp, - Wake => Key::Wake, - WebBack => Key::WebBack, - WebFavorites => Key::WebFavorites, - WebForward => Key::WebForward, - WebHome => Key::WebHome, - WebRefresh => Key::WebRefresh, - WebSearch => Key::WebSearch, - WebStop => Key::WebStop, - Yen => Key::Yen, - Caret => Key::Caret, - Copy => Key::Copy, - Paste => Key::Paste, - Cut => Key::Cut, - } - } -} - -#[cfg(test)] -mod tests { - use super::{Config, DEFAULT_ALACRITTY_CONFIG}; - use crate::cli::Options; - - #[test] - fn parse_config() { - let config: Config = - ::serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG).expect("deserialize config"); - - // Sanity check that mouse bindings are being parsed - assert!(!config.mouse_bindings.is_empty()); - - // Sanity check that key bindings are being parsed - assert!(!config.key_bindings.is_empty()); - } - - #[test] - fn dynamic_title_ignoring_options_by_default() { - let config: Config = - ::serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG).expect("deserialize config"); - let old_dynamic_title = config.dynamic_title; - let options = Options::default(); - let config = config.update_dynamic_title(&options); - assert_eq!(old_dynamic_title, config.dynamic_title); - } - - #[test] - fn dynamic_title_overridden_by_options() { - let config: Config = - ::serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG).expect("deserialize config"); - let mut options = Options::default(); - options.title = Some("foo".to_owned()); - let config = config.update_dynamic_title(&options); - assert!(!config.dynamic_title); - } - - #[test] - fn default_match_empty() { - let default = Config::default(); - - let empty = serde_yaml::from_str("key: val\n").unwrap(); - - assert_eq!(default, empty); - } -} diff --git a/src/grid/storage.rs b/src/grid/storage.rs deleted file mode 100644 index 65122b5a39..0000000000 --- a/src/grid/storage.rs +++ /dev/null @@ -1,928 +0,0 @@ -/// Wrapper around Vec which supports fast indexing and rotation -/// -/// The rotation implemented by grid::Storage is a simple integer addition. -/// Compare with standard library rotation which requires rearranging items in -/// memory. -/// -/// As a consequence, the indexing operators need to be reimplemented for this -/// type to account for the 0th element not always being at the start of the -/// allocation. -/// -/// Because certain Vec operations are no longer valid on this type, no Deref -/// implementation is provided. Anything from Vec that should be exposed must be -/// done so manually. -use std::ops::{Index, IndexMut}; - -use static_assertions::assert_eq_size; - -use super::Row; -use crate::grid::GridCell; -use crate::index::{Column, Line}; - -/// Maximum number of invisible lines before buffer is resized -const TRUNCATE_STEP: usize = 100; - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Storage { - inner: Vec>, - zero: usize, - visible_lines: Line, - - /// Total number of lines currently active in the terminal (scrollback + visible) - /// - /// Shrinking this length allows reducing the number of lines in the scrollback buffer without - /// having to truncate the raw `inner` buffer. - /// As long as `len` is bigger than `inner`, it is also possible to grow the scrollback buffer - /// without any additional insertions. - #[serde(default)] - len: usize, -} - -impl ::std::cmp::PartialEq for Storage { - fn eq(&self, other: &Self) -> bool { - // Make sure length is equal - if self.inner.len() != other.inner.len() { - return false; - } - - // Check which vec has the bigger zero - let (ref bigger, ref smaller) = if self.zero >= other.zero { - (self, other) - } else { - (other, self) - }; - - // Calculate the actual zero offset - let len = self.inner.len(); - let bigger_zero = bigger.zero % len; - let smaller_zero = smaller.zero % len; - - // Compare the slices in chunks - // Chunks: - // - Bigger zero to the end - // - Remaining lines in smaller zero vec - // - Beginning of smaller zero vec - // - // Example: - // Bigger Zero (6): - // 4 5 6 | 7 8 9 | 0 1 2 3 - // C2 C2 C2 | C3 C3 C3 | C1 C1 C1 C1 - // Smaller Zero (3): - // 7 8 9 | 0 1 2 3 | 4 5 6 - // C3 C3 C3 | C1 C1 C1 C1 | C2 C2 C2 - bigger.inner[bigger_zero..] - == smaller.inner[smaller_zero..smaller_zero + (len - bigger_zero)] - && bigger.inner[..bigger_zero - smaller_zero] - == smaller.inner[smaller_zero + (len - bigger_zero)..] - && bigger.inner[bigger_zero - smaller_zero..bigger_zero] - == smaller.inner[..smaller_zero] - } -} - -impl Storage { - #[inline] - pub fn with_capacity(lines: Line, template: Row) -> Storage - where - T: Clone, - { - // Initialize visible lines, the scrollback buffer is initialized dynamically - let inner = vec![template; lines.0]; - - Storage { - inner, - zero: 0, - visible_lines: lines - 1, - len: lines.0, - } - } - - /// Update the size of the scrollback history - pub fn update_history(&mut self, history_size: usize, template_row: Row) - where - T: Clone, - { - let current_history = self.len - (self.visible_lines.0 + 1); - if history_size > current_history { - self.grow_lines(history_size - current_history, template_row); - } else if history_size < current_history { - self.shrink_lines(current_history - history_size); - } - } - - /// Increase the number of lines in the buffer - pub fn grow_visible_lines(&mut self, next: Line, template_row: Row) - where - T: Clone, - { - // Number of lines the buffer needs to grow - let growage = (next - (self.visible_lines + 1)).0; - self.grow_lines(growage, template_row); - - // Update visible lines - self.visible_lines = next - 1; - } - - /// Grow the number of lines in the buffer, filling new lines with the template - fn grow_lines(&mut self, growage: usize, template_row: Row) - where - T: Clone, - { - // Only grow if there are not enough lines still hidden - let mut new_growage = 0; - if growage > (self.inner.len() - self.len) { - // Lines to grow additionally to invisible lines - new_growage = growage - (self.inner.len() - self.len); - - // Split off the beginning of the raw inner buffer - let mut start_buffer = self.inner.split_off(self.zero); - - // Insert new template rows at the end of the raw inner buffer - let mut new_lines = vec![template_row; new_growage]; - self.inner.append(&mut new_lines); - - // Add the start to the raw inner buffer again - self.inner.append(&mut start_buffer); - } - - // Update raw buffer length and zero offset - self.zero = (self.zero + new_growage) % self.inner.len(); - self.len += growage; - } - - /// Decrease the number of lines in the buffer - pub fn shrink_visible_lines(&mut self, next: Line) { - // Shrink the size without removing any lines - let shrinkage = (self.visible_lines - (next - 1)).0; - self.shrink_lines(shrinkage); - - // Update visible lines - self.visible_lines = next - 1; - } - - // Shrink the number of lines in the buffer - pub fn shrink_lines(&mut self, shrinkage: usize) { - self.len -= shrinkage; - - // Free memory - if self.inner.len() > self.len() + TRUNCATE_STEP { - self.truncate(); - } - } - - /// Truncate the invisible elements from the raw buffer - pub fn truncate(&mut self) { - self.inner.rotate_left(self.zero); - self.inner.truncate(self.len); - - self.zero = 0; - } - - /// Dynamically grow the storage buffer at runtime - pub fn initialize(&mut self, num_rows: usize, template_row: Row) - where - T: Clone, - { - let mut new = vec![template_row; num_rows]; - - let mut split = self.inner.split_off(self.zero); - self.inner.append(&mut new); - self.inner.append(&mut split); - - self.zero += num_rows; - self.len += num_rows; - } - - #[inline] - pub fn len(&self) -> usize { - self.len - } - - #[inline] - /// Compute actual index in underlying storage given the requested index. - fn compute_index(&self, requested: usize) -> usize { - debug_assert!(requested < self.len); - let zeroed = requested + self.zero; - - // This part is critical for performance, - // so an if/else is used here instead of a moludo operation - if zeroed >= self.inner.len() { - zeroed - self.inner.len() - } else { - zeroed - } - } - - pub fn swap_lines(&mut self, a: Line, b: Line) { - let offset = self.inner.len() + self.zero + *self.visible_lines; - let a = (offset - *a) % self.inner.len(); - let b = (offset - *b) % self.inner.len(); - self.inner.swap(a, b); - } - - /// Swap implementation for Row. - /// - /// Exploits the known size of Row to produce a slightly more efficient - /// swap than going through slice::swap. - /// - /// The default implementation from swap generates 8 movups and 4 movaps - /// instructions. This implementation achieves the swap in only 8 movups - /// instructions. - pub fn swap(&mut self, a: usize, b: usize) { - assert_eq_size!(Row, [usize; 4]); - - let a = self.compute_index(a); - let b = self.compute_index(b); - - unsafe { - // Cast to a qword array to opt out of copy restrictions and avoid - // drop hazards. Byte array is no good here since for whatever - // reason LLVM won't optimized it. - let a_ptr = self.inner.as_mut_ptr().add(a) as *mut usize; - let b_ptr = self.inner.as_mut_ptr().add(b) as *mut usize; - - // Copy 1 qword at a time - // - // The optimizer unrolls this loop and vectorizes it. - let mut tmp: usize; - for i in 0..4 { - tmp = *a_ptr.offset(i); - *a_ptr.offset(i) = *b_ptr.offset(i); - *b_ptr.offset(i) = tmp; - } - } - } - - #[inline] - pub fn rotate(&mut self, count: isize) { - debug_assert!(count.abs() as usize <= self.inner.len()); - - let len = self.inner.len(); - self.zero = (self.zero as isize + count + len as isize) as usize % len; - } - - // Fast path - #[inline] - pub fn rotate_up(&mut self, count: usize) { - self.zero = (self.zero + count) % self.inner.len(); - } - - #[inline] - pub fn insert(&mut self, index: usize, row: Row, max_lines: usize) { - let index = self.compute_index(index); - self.inner.insert(index, row); - - if index < self.zero { - self.zero += 1; - } - - if self.len < max_lines { - self.len += 1; - } - } - - #[inline] - pub fn remove(&mut self, index: usize) -> Row { - let index = self.compute_index(index); - if index < self.zero { - self.zero -= 1; - } - self.len -= 1; - - self.inner.remove(index) - } - - /// Shrink columns of hidden buffered lines. - /// - /// XXX This suggests that Storage is a leaky abstraction. Ultimately, this - /// is needed because of the grow/shrink lines functionality. - #[inline] - pub fn shrink_hidden(&mut self, cols: Column) - where - T: GridCell + Copy, - { - let start = self.zero + self.len; - let end = self.zero + self.inner.len(); - for mut i in start..end { - if i >= self.inner.len() { - i -= self.inner.len(); - } - - self.inner[i].shrink(cols); - } - } - - /// Grow columns of hidden buffered lines. - /// - /// XXX This suggests that Storage is a leaky abstraction. Ultimately, this - /// is needed because of the grow/shrink lines functionality. - #[inline] - pub fn grow_hidden(&mut self, cols: Column, template: &T) - where - T: Copy + Clone, - { - let start = self.zero + self.len; - let end = self.zero + self.inner.len(); - for mut i in start..end { - if i >= self.inner.len() { - i -= self.inner.len(); - } - - self.inner[i].grow(cols, template); - } - } -} - -impl Index for Storage { - type Output = Row; - #[inline] - fn index(&self, index: usize) -> &Self::Output { - &self.inner[self.compute_index(index)] - } -} - -impl IndexMut for Storage { - #[inline] - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - let index = self.compute_index(index); // borrowck - &mut self.inner[index] - } -} - -impl Index for Storage { - type Output = Row; - #[inline] - fn index(&self, index: Line) -> &Self::Output { - let index = self.visible_lines - index; - &self[*index] - } -} - -impl IndexMut for Storage { - #[inline] - fn index_mut(&mut self, index: Line) -> &mut Self::Output { - let index = self.visible_lines - index; - &mut self[*index] - } -} - -/// Grow the buffer one line at the end of the buffer -/// -/// Before: -/// 0: 0 <- Zero -/// 1: 1 -/// 2: - -/// After: -/// 0: - -/// 1: 0 <- Zero -/// 2: 1 -/// 3: - -#[test] -fn grow_after_zero() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'-'), - ], - zero: 0, - visible_lines: Line(2), - len: 3, - }; - - // Grow buffer - storage.grow_visible_lines(Line(4), Row::new(Column(1), &'-')); - - // Make sure the result is correct - let expected = Storage { - inner: vec![ - Row::new(Column(1), &'-'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'-'), - ], - zero: 1, - visible_lines: Line(0), - len: 4, - }; - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// Grow the buffer one line at the start of the buffer -/// -/// Before: -/// 0: - -/// 1: 0 <- Zero -/// 2: 1 -/// After: -/// 0: - -/// 1: - -/// 2: 0 <- Zero -/// 3: 1 -#[test] -fn grow_before_zero() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'-'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - ], - zero: 1, - visible_lines: Line(2), - len: 3, - }; - - // Grow buffer - storage.grow_visible_lines(Line(4), Row::new(Column(1), &'-')); - - // Make sure the result is correct - let expected = Storage { - inner: vec![ - Row::new(Column(1), &'-'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - ], - zero: 2, - visible_lines: Line(0), - len: 4, - }; - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// Shrink the buffer one line at the start of the buffer -/// -/// Before: -/// 0: 2 -/// 1: 0 <- Zero -/// 2: 1 -/// After: -/// 0: 2 <- Hidden -/// 0: 0 <- Zero -/// 1: 1 -#[test] -fn shrink_before_zero() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'2'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - ], - zero: 1, - visible_lines: Line(2), - len: 3, - }; - - // Shrink buffer - storage.shrink_visible_lines(Line(2)); - - // Make sure the result is correct - let expected = Storage { - inner: vec![ - Row::new(Column(1), &'2'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - ], - zero: 1, - visible_lines: Line(0), - len: 2, - }; - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// Shrink the buffer one line at the end of the buffer -/// -/// Before: -/// 0: 0 <- Zero -/// 1: 1 -/// 2: 2 -/// After: -/// 0: 0 <- Zero -/// 1: 1 -/// 2: 2 <- Hidden -#[test] -fn shrink_after_zero() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - ], - zero: 0, - visible_lines: Line(2), - len: 3, - }; - - // Shrink buffer - storage.shrink_visible_lines(Line(2)); - - // Make sure the result is correct - let expected = Storage { - inner: vec![ - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - ], - zero: 0, - visible_lines: Line(0), - len: 2, - }; - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// Shrink the buffer at the start and end of the buffer -/// -/// Before: -/// 0: 4 -/// 1: 5 -/// 2: 0 <- Zero -/// 3: 1 -/// 4: 2 -/// 5: 3 -/// After: -/// 0: 4 <- Hidden -/// 1: 5 <- Hidden -/// 2: 0 <- Zero -/// 3: 1 -/// 4: 2 <- Hidden -/// 5: 3 <- Hidden -#[test] -fn shrink_before_and_after_zero() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(5), - len: 6, - }; - - // Shrink buffer - storage.shrink_visible_lines(Line(2)); - - // Make sure the result is correct - let expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 2, - }; - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// Check that when truncating all hidden lines are removed from the raw buffer -/// -/// Before: -/// 0: 4 <- Hidden -/// 1: 5 <- Hidden -/// 2: 0 <- Zero -/// 3: 1 -/// 4: 2 <- Hidden -/// 5: 3 <- Hidden -/// After: -/// 0: 0 <- Zero -/// 1: 1 -#[test] -fn truncate_invisible_lines() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(1), - len: 2, - }; - - // Truncate buffer - storage.truncate(); - - // Make sure the result is correct - let expected = Storage { - inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1')], - zero: 0, - visible_lines: Line(1), - len: 2, - }; - assert_eq!(storage.visible_lines, expected.visible_lines); - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// Truncate buffer only at the beginning -/// -/// Before: -/// 0: 1 -/// 1: 2 <- Hidden -/// 2: 0 <- Zero -/// After: -/// 0: 1 -/// 0: 0 <- Zero -#[test] -fn truncate_invisible_lines_beginning() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'0'), - ], - zero: 2, - visible_lines: Line(1), - len: 2, - }; - - // Truncate buffer - storage.truncate(); - - // Make sure the result is correct - let expected = Storage { - inner: vec![Row::new(Column(1), &'0'), Row::new(Column(1), &'1')], - zero: 0, - visible_lines: Line(1), - len: 2, - }; - assert_eq!(storage.visible_lines, expected.visible_lines); - assert_eq!(storage.inner, expected.inner); - assert_eq!(storage.zero, expected.zero); - assert_eq!(storage.len, expected.len); -} - -/// First shrink the buffer and then grow it again -/// -/// Before: -/// 0: 4 -/// 1: 5 -/// 2: 0 <- Zero -/// 3: 1 -/// 4: 2 -/// 5: 3 -/// After Shrinking: -/// 0: 4 <- Hidden -/// 1: 5 <- Hidden -/// 2: 0 <- Zero -/// 3: 1 -/// 4: 2 -/// 5: 3 <- Hidden -/// After Growing: -/// 0: 4 -/// 1: 5 -/// 2: - -/// 3: 0 <- Zero -/// 4: 1 -/// 5: 2 -/// 6: 3 -#[test] -fn shrink_then_grow() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - - // Shrink buffer - storage.shrink_lines(3); - - // Make sure the result after shrinking is correct - let shrinking_expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 3, - }; - assert_eq!(storage.inner, shrinking_expected.inner); - assert_eq!(storage.zero, shrinking_expected.zero); - assert_eq!(storage.len, shrinking_expected.len); - - // Grow buffer - storage.grow_lines(4, Row::new(Column(1), &'-')); - - // Make sure the result after shrinking is correct - let growing_expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 3, - visible_lines: Line(0), - len: 7, - }; - assert_eq!(storage.inner, growing_expected.inner); - assert_eq!(storage.zero, growing_expected.zero); - assert_eq!(storage.len, growing_expected.len); -} - -#[test] -fn initialize() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - - // Initialize additional lines - storage.initialize(3, Row::new(Column(1), &'-')); - - // Make sure the lines are present and at the right location - let shrinking_expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 5, - visible_lines: Line(0), - len: 9, - }; - assert_eq!(storage.inner, shrinking_expected.inner); - assert_eq!(storage.zero, shrinking_expected.zero); - assert_eq!(storage.len, shrinking_expected.len); -} - -#[test] -fn insert() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - - // Initialize additional lines - storage.insert(2, Row::new(Column(1), &'-'), 100); - - // Make sure the lines are present and at the right location - let shrinking_expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 7, - }; - assert_eq!(storage.inner, shrinking_expected.inner); - assert_eq!(storage.zero, shrinking_expected.zero); - assert_eq!(storage.len, shrinking_expected.len); -} - -#[test] -fn insert_truncate_max() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - - // Initialize additional lines - storage.insert(2, Row::new(Column(1), &'-'), 6); - - // Make sure the lines are present and at the right location - let shrinking_expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - assert_eq!(storage.inner, shrinking_expected.inner); - assert_eq!(storage.zero, shrinking_expected.zero); - assert_eq!(storage.len, shrinking_expected.len); -} - -#[test] -fn insert_at_zero() { - // Setup storage area - let mut storage = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - - // Initialize additional lines - storage.insert(0, Row::new(Column(1), &'-'), 6); - - // Make sure the lines are present and at the right location - let shrinking_expected = Storage { - inner: vec![ - Row::new(Column(1), &'4'), - Row::new(Column(1), &'5'), - Row::new(Column(1), &'-'), - Row::new(Column(1), &'0'), - Row::new(Column(1), &'1'), - Row::new(Column(1), &'2'), - Row::new(Column(1), &'3'), - ], - zero: 2, - visible_lines: Line(0), - len: 6, - }; - assert_eq!(storage.inner, shrinking_expected.inner); - assert_eq!(storage.zero, shrinking_expected.zero); - assert_eq!(storage.len, shrinking_expected.len); -} diff --git a/src/selection.rs b/src/selection.rs deleted file mode 100644 index 205687b6ae..0000000000 --- a/src/selection.rs +++ /dev/null @@ -1,647 +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. - -//! State management for a selection in the grid -//! -//! A selection should start when the mouse is clicked, and it should be -//! finalized when the button is released. The selection should be cleared -//! when text is added/removed/scrolled on the screen. The selection should -//! also be cleared if the user clicks off of the selection. -use std::cmp::{max, min}; -use std::ops::Range; - -use crate::index::{Column, Point, Side}; -use crate::term::Search; - -/// Describes a region of a 2-dimensional area -/// -/// Used to track a text selection. There are three supported modes, each with its own constructor: -/// [`simple`], [`semantic`], and [`lines`]. The [`simple`] mode precisely tracks which cells are -/// selected without any expansion. [`semantic`] mode expands the initial selection to the nearest -/// semantic escape char in either direction. [`lines`] will always select entire lines. -/// -/// Calls to [`update`] operate different based on the selection kind. The [`simple`] mode does -/// nothing special, simply tracks points and sides. [`semantic`] will continue to expand out to -/// semantic boundaries as the selection point changes. Similarly, [`lines`] will always expand the -/// new point to encompass entire lines. -/// -/// [`simple`]: enum.Selection.html#method.simple -/// [`semantic`]: enum.Selection.html#method.semantic -/// [`lines`]: enum.Selection.html#method.lines -#[derive(Debug, Clone, PartialEq)] -pub enum Selection { - Simple { - /// The region representing start and end of cursor movement - region: Range, - }, - Semantic { - /// The region representing start and end of cursor movement - region: Range>, - }, - Lines { - /// The region representing start and end of cursor movement - region: Range>, - - /// The line under the initial point. This is always selected regardless - /// of which way the cursor is moved. - initial_line: isize, - }, -} - -/// A Point and side within that point. -#[derive(Debug, Clone, PartialEq)] -pub struct Anchor { - point: Point, - side: Side, -} - -impl Anchor { - fn new(point: Point, side: Side) -> Anchor { - Anchor { point, side } - } -} - -/// A type that has 2-dimensional boundaries -pub trait Dimensions { - /// Get the size of the area - fn dimensions(&self) -> Point; -} - -impl Selection { - pub fn simple(location: Point, side: Side) -> Selection { - Selection::Simple { - region: Range { - start: Anchor::new(location.into(), side), - end: Anchor::new(location.into(), side), - }, - } - } - - pub fn rotate(&mut self, offset: isize) { - match *self { - Selection::Simple { ref mut region } => { - region.start.point.line += offset; - region.end.point.line += offset; - } - Selection::Semantic { ref mut region } => { - region.start.line += offset; - region.end.line += offset; - } - Selection::Lines { - ref mut region, - ref mut initial_line, - } => { - region.start.line += offset; - region.end.line += offset; - *initial_line += offset; - } - } - } - - pub fn semantic(point: Point) -> Selection { - Selection::Semantic { - region: Range { - start: point.into(), - end: point.into(), - }, - } - } - - pub fn lines(point: Point) -> Selection { - Selection::Lines { - region: Range { - start: point.into(), - end: point.into(), - }, - initial_line: point.line as isize, - } - } - - pub fn update(&mut self, location: Point, side: Side) { - // Always update the `end`; can normalize later during span generation. - match *self { - Selection::Simple { ref mut region } => { - region.end = Anchor::new(location.into(), side); - } - Selection::Semantic { ref mut region } | Selection::Lines { ref mut region, .. } => { - region.end = location.into(); - } - } - } - - pub fn to_span(&self, grid: &G, alt_screen: bool) -> Option - where - G: Search + Dimensions, - { - match *self { - Selection::Simple { ref region } => Selection::span_simple(grid, region, alt_screen), - Selection::Semantic { ref region } => { - Selection::span_semantic(grid, region, alt_screen) - } - Selection::Lines { - ref region, - initial_line, - } => Selection::span_lines(grid, region, initial_line, alt_screen), - } - } - - pub fn is_empty(&self) -> bool { - match *self { - Selection::Simple { ref region } => { - region.start == region.end && region.start.side == region.end.side - } - Selection::Semantic { .. } | Selection::Lines { .. } => false, - } - } - - fn span_semantic(grid: &G, region: &Range>, alt_screen: bool) -> Option - where - G: Search + Dimensions, - { - let cols = grid.dimensions().col; - let lines = grid.dimensions().line.0 as isize; - - // Normalize ordering of selected cells - let (mut front, mut tail) = if region.start < region.end { - (region.start, region.end) - } else { - (region.end, region.start) - }; - - if alt_screen { - Selection::alt_screen_clamp(&mut front, &mut tail, lines, cols)?; - } - - let (mut start, mut end) = if front < tail && front.line == tail.line { - ( - grid.semantic_search_left(front.into()), - grid.semantic_search_right(tail.into()), - ) - } else { - ( - grid.semantic_search_right(front.into()), - grid.semantic_search_left(tail.into()), - ) - }; - - if start > end { - ::std::mem::swap(&mut start, &mut end); - } - - Some(Span { - cols, - front: start, - tail: end, - ty: SpanType::Inclusive, - }) - } - - fn span_lines( - grid: &G, - region: &Range>, - initial_line: isize, - alt_screen: bool, - ) -> Option - where - G: Dimensions, - { - let cols = grid.dimensions().col; - let lines = grid.dimensions().line.0 as isize; - - // First, create start and end points based on initial line and the grid - // dimensions. - let mut start = Point { - col: cols - 1, - line: initial_line, - }; - let mut end = Point { - col: Column(0), - line: initial_line, - }; - - // Now, expand lines based on where cursor started and ended. - if region.start.line < region.end.line { - // Start is below end - start.line = min(start.line, region.start.line); - end.line = max(end.line, region.end.line); - } else { - // Start is above end - start.line = min(start.line, region.end.line); - end.line = max(end.line, region.start.line); - } - - if alt_screen { - Selection::alt_screen_clamp(&mut start, &mut end, lines, cols)?; - } - - Some(Span { - cols, - front: start.into(), - tail: end.into(), - ty: SpanType::Inclusive, - }) - } - - fn span_simple(grid: &G, region: &Range, alt_screen: bool) -> Option - where - G: Dimensions, - { - let start = region.start.point; - let start_side = region.start.side; - let end = region.end.point; - let end_side = region.end.side; - let cols = grid.dimensions().col; - let lines = grid.dimensions().line.0 as isize; - - // Make sure front is always the "bottom" and tail is always the "top" - let (mut front, mut tail, front_side, tail_side) = - if start.line > end.line || start.line == end.line && start.col <= end.col { - // Selected upward; start/end are swapped - (end, start, end_side, start_side) - } else { - // Selected downward; no swapping - (start, end, start_side, end_side) - }; - - // No selection for single cell with identical sides or two cell with right+left sides - if (front == tail && front_side == tail_side) - || (tail_side == Side::Right - && front_side == Side::Left - && front.line == tail.line - && front.col == tail.col + 1) - { - return None; - } - - // Remove last cell if selection ends to the left of a cell - if front_side == Side::Left && start != end { - // Special case when selection starts to left of first cell - if front.col == Column(0) { - front.col = cols - 1; - front.line += 1; - } else { - front.col -= 1; - } - } - - // Remove first cell if selection starts at the right of a cell - if tail_side == Side::Right && front != tail { - tail.col += 1; - } - - if alt_screen { - Selection::alt_screen_clamp(&mut front, &mut tail, lines, cols)?; - } - - // Return the selection with all cells inclusive - Some(Span { - cols, - front: front.into(), - tail: tail.into(), - ty: SpanType::Inclusive, - }) - } - - // Clamp selection in the alternate screen to the visible region - fn alt_screen_clamp( - front: &mut Point, - tail: &mut Point, - lines: isize, - cols: Column, - ) -> Option<()> { - if tail.line >= lines { - // Don't show selection above visible region - if front.line >= lines { - return None; - } - - // Clamp selection above viewport to visible region - tail.line = lines - 1; - tail.col = Column(0); - } - - if front.line < 0 { - // Don't show selection below visible region - if tail.line < 0 { - return None; - } - - // Clamp selection below viewport to visible region - front.line = 0; - front.col = cols - 1; - } - - Some(()) - } -} - -/// How to interpret the locations of a Span. -#[derive(Debug, Eq, PartialEq)] -pub enum SpanType { - /// Includes the beginning and end locations - Inclusive, - - /// Exclude both beginning and end - Exclusive, - - /// Excludes last cell of selection - ExcludeTail, - - /// Excludes first cell of selection - ExcludeFront, -} - -/// Represents a span of selected cells -#[derive(Debug, Eq, PartialEq)] -pub struct Span { - front: Point, - tail: Point, - cols: Column, - - /// The type says whether ends are included or not. - ty: SpanType, -} - -#[derive(Debug)] -pub struct Locations { - /// Start point from bottom of buffer - pub start: Point, - /// End point towards top of buffer - pub end: Point, -} - -impl Span { - pub fn to_locations(&self) -> Locations { - let (start, end) = match self.ty { - SpanType::Inclusive => (self.front, self.tail), - SpanType::Exclusive => ( - Span::wrap_start(self.front, self.cols), - Span::wrap_end(self.tail, self.cols), - ), - SpanType::ExcludeFront => (Span::wrap_start(self.front, self.cols), self.tail), - SpanType::ExcludeTail => (self.front, Span::wrap_end(self.tail, self.cols)), - }; - - Locations { start, end } - } - - fn wrap_start(mut start: Point, cols: Column) -> Point { - if start.col == cols - 1 { - Point { - line: start.line + 1, - col: Column(0), - } - } else { - start.col += 1; - start - } - } - - fn wrap_end(end: Point, cols: Column) -> Point { - if end.col == Column(0) && end.line != 0 { - Point { - line: end.line - 1, - col: cols, - } - } else { - Point { - line: end.line, - col: end.col - 1, - } - } - } -} - -/// Tests for selection -/// -/// There are comments on all of the tests describing the selection. Pictograms -/// are used to avoid ambiguity. Grid cells are represented by a [ ]. Only -/// cells that are completely covered are counted in a selection. Ends are -/// represented by `B` and `E` for begin and end, respectively. A selected cell -/// looks like [XX], [BX] (at the start), [XB] (at the end), [XE] (at the end), -/// and [EX] (at the start), or [BE] for a single cell. Partially selected cells -/// look like [ B] and [E ]. -#[cfg(test)] -mod test { - use super::{Selection, Span, SpanType}; - use crate::index::{Column, Line, Point, Side}; - - struct Dimensions(Point); - impl super::Dimensions for Dimensions { - fn dimensions(&self) -> Point { - self.0 - } - } - - impl Dimensions { - pub fn new(line: usize, col: usize) -> Self { - Dimensions(Point { - line: Line(line), - col: Column(col), - }) - } - } - - impl super::Search for Dimensions { - fn semantic_search_left(&self, point: Point) -> Point { - point - } - fn semantic_search_right(&self, point: Point) -> Point { - point - } - fn url_search(&self, _: Point) -> Option { - None - } - } - - /// Test case of single cell selection - /// - /// 1. [ ] - /// 2. [B ] - /// 3. [BE] - #[test] - fn single_cell_left_to_right() { - let location = Point { - line: 0, - col: Column(0), - }; - let mut selection = Selection::simple(location, Side::Left); - selection.update(location, Side::Right); - - assert_eq!( - selection.to_span(&Dimensions::new(1, 1), false).unwrap(), - Span { - cols: Column(1), - ty: SpanType::Inclusive, - front: location, - tail: location - } - ); - } - - /// Test case of single cell selection - /// - /// 1. [ ] - /// 2. [ B] - /// 3. [EB] - #[test] - fn single_cell_right_to_left() { - let location = Point { - line: 0, - col: Column(0), - }; - let mut selection = Selection::simple(location, Side::Right); - selection.update(location, Side::Left); - - assert_eq!( - selection.to_span(&Dimensions::new(1, 1), false).unwrap(), - Span { - cols: Column(1), - ty: SpanType::Inclusive, - front: location, - tail: location - } - ); - } - - /// Test adjacent cell selection from left to right - /// - /// 1. [ ][ ] - /// 2. [ B][ ] - /// 3. [ B][E ] - #[test] - fn between_adjacent_cells_left_to_right() { - let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right); - selection.update(Point::new(0, Column(1)), Side::Left); - - assert_eq!(selection.to_span(&Dimensions::new(1, 2), false), None); - } - - /// Test adjacent cell selection from right to left - /// - /// 1. [ ][ ] - /// 2. [ ][B ] - /// 3. [ E][B ] - #[test] - fn between_adjacent_cells_right_to_left() { - let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Left); - selection.update(Point::new(0, Column(0)), Side::Right); - - assert_eq!(selection.to_span(&Dimensions::new(1, 2), false), None); - } - - /// Test selection across adjacent lines - /// - /// - /// 1. [ ][ ][ ][ ][ ] - /// [ ][ ][ ][ ][ ] - /// 2. [ ][ B][ ][ ][ ] - /// [ ][ ][ ][ ][ ] - /// 3. [ ][ B][XX][XX][XX] - /// [XX][XE][ ][ ][ ] - #[test] - fn across_adjacent_lines_upward_final_cell_exclusive() { - let mut selection = Selection::simple(Point::new(1, Column(1)), Side::Right); - selection.update(Point::new(0, Column(1)), Side::Right); - - assert_eq!( - selection.to_span(&Dimensions::new(2, 5), false).unwrap(), - Span { - cols: Column(5), - front: Point::new(0, Column(1)), - tail: Point::new(1, Column(2)), - ty: SpanType::Inclusive, - } - ); - } - - /// Test selection across adjacent lines - /// - /// - /// 1. [ ][ ][ ][ ][ ] - /// [ ][ ][ ][ ][ ] - /// 2. [ ][ ][ ][ ][ ] - /// [ ][ B][ ][ ][ ] - /// 3. [ ][ E][XX][XX][XX] - /// [XX][XB][ ][ ][ ] - /// 4. [ E][XX][XX][XX][XX] - /// [XX][XB][ ][ ][ ] - #[test] - fn selection_bigger_then_smaller() { - let mut selection = Selection::simple(Point::new(0, Column(1)), Side::Right); - selection.update(Point::new(1, Column(1)), Side::Right); - selection.update(Point::new(1, Column(0)), Side::Right); - - assert_eq!( - selection.to_span(&Dimensions::new(2, 5), false).unwrap(), - Span { - cols: Column(5), - front: Point::new(0, Column(1)), - tail: Point::new(1, Column(1)), - ty: SpanType::Inclusive, - } - ); - } - - #[test] - fn alt_scren_lines() { - let mut selection = Selection::lines(Point::new(0, Column(0))); - selection.update(Point::new(5, Column(3)), Side::Right); - selection.rotate(-3); - - assert_eq!( - selection.to_span(&Dimensions::new(10, 5), true).unwrap(), - Span { - cols: Column(5), - front: Point::new(0, Column(4)), - tail: Point::new(2, Column(0)), - ty: SpanType::Inclusive, - } - ); - } - - #[test] - fn alt_screen_semantic() { - let mut selection = Selection::semantic(Point::new(0, Column(0))); - selection.update(Point::new(5, Column(3)), Side::Right); - selection.rotate(-3); - - assert_eq!( - selection.to_span(&Dimensions::new(10, 5), true).unwrap(), - Span { - cols: Column(5), - front: Point::new(0, Column(4)), - tail: Point::new(2, Column(3)), - ty: SpanType::Inclusive, - } - ); - } - - #[test] - fn alt_screen_simple() { - let mut selection = Selection::simple(Point::new(0, Column(0)), Side::Right); - selection.update(Point::new(5, Column(3)), Side::Right); - selection.rotate(-3); - - assert_eq!( - selection.to_span(&Dimensions::new(10, 5), true).unwrap(), - Span { - cols: Column(5), - front: Point::new(0, Column(4)), - tail: Point::new(2, Column(4)), - ty: SpanType::Inclusive, - } - ); - } -} diff --git a/src/term/color.rs b/src/term/color.rs deleted file mode 100644 index 394c6c2d90..0000000000 --- a/src/term/color.rs +++ /dev/null @@ -1,244 +0,0 @@ -use std::fmt; -use std::ops::{Index, IndexMut, Mul}; - -use crate::ansi; -use crate::config::Colors; - -pub const COUNT: usize = 270; - -pub const RED: Rgb = Rgb { - r: 0xff, - g: 0x0, - b: 0x0, -}; -pub const YELLOW: Rgb = Rgb { - r: 0xff, - g: 0xff, - b: 0x0, -}; - -#[derive(Debug, Eq, PartialEq, Copy, Clone, Default, Serialize, Deserialize)] -pub struct Rgb { - pub r: u8, - pub g: u8, - pub b: u8, -} - -// a multiply function for Rgb, as the default dim is just *2/3 -impl Mul for Rgb { - type Output = Rgb; - - fn mul(self, rhs: f32) -> Rgb { - let result = Rgb { - r: (f32::from(self.r) * rhs).max(0.0).min(255.0) as u8, - g: (f32::from(self.g) * rhs).max(0.0).min(255.0) as u8, - b: (f32::from(self.b) * rhs).max(0.0).min(255.0) as u8, - }; - - trace!("Scaling RGB by {} from {:?} to {:?}", rhs, self, result); - - result - } -} - -/// List of indexed colors -/// -/// The first 16 entries are the standard ansi named colors. Items 16..232 are -/// the color cube. Items 233..256 are the grayscale ramp. Item 256 is -/// the configured foreground color, item 257 is the configured background -/// color, item 258 is the cursor foreground color, item 259 is the cursor -/// background color. Following that are 8 positions for dim colors. -/// Item 268 is the bright foreground color, 269 the dim foreground. -#[derive(Copy, Clone)] -pub struct List([Rgb; COUNT]); - -impl<'a> From<&'a Colors> for List { - fn from(colors: &Colors) -> List { - // Type inference fails without this annotation - let mut list: List = unsafe { ::std::mem::uninitialized() }; - - list.fill_named(colors); - list.fill_cube(colors); - list.fill_gray_ramp(colors); - - list - } -} - -impl List { - pub fn fill_named(&mut self, colors: &Colors) { - // Normals - self[ansi::NamedColor::Black] = colors.normal.black; - self[ansi::NamedColor::Red] = colors.normal.red; - self[ansi::NamedColor::Green] = colors.normal.green; - self[ansi::NamedColor::Yellow] = colors.normal.yellow; - self[ansi::NamedColor::Blue] = colors.normal.blue; - self[ansi::NamedColor::Magenta] = colors.normal.magenta; - self[ansi::NamedColor::Cyan] = colors.normal.cyan; - self[ansi::NamedColor::White] = colors.normal.white; - - // Brights - self[ansi::NamedColor::BrightBlack] = colors.bright.black; - self[ansi::NamedColor::BrightRed] = colors.bright.red; - self[ansi::NamedColor::BrightGreen] = colors.bright.green; - self[ansi::NamedColor::BrightYellow] = colors.bright.yellow; - self[ansi::NamedColor::BrightBlue] = colors.bright.blue; - self[ansi::NamedColor::BrightMagenta] = colors.bright.magenta; - self[ansi::NamedColor::BrightCyan] = colors.bright.cyan; - self[ansi::NamedColor::BrightWhite] = colors.bright.white; - self[ansi::NamedColor::BrightForeground] = colors - .primary - .bright_foreground - .unwrap_or(colors.primary.foreground); - - // Foreground and background - self[ansi::NamedColor::Foreground] = colors.primary.foreground; - self[ansi::NamedColor::Background] = colors.primary.background; - - // Foreground and background for custom cursor colors - self[ansi::NamedColor::CursorText] = colors.cursor.text.unwrap_or_else(Rgb::default); - self[ansi::NamedColor::Cursor] = colors.cursor.cursor.unwrap_or_else(Rgb::default); - - // Dims - self[ansi::NamedColor::DimForeground] = colors - .primary - .dim_foreground - .unwrap_or(colors.primary.foreground * 0.66); - match colors.dim { - Some(ref dim) => { - trace!("Using config-provided dim colors"); - self[ansi::NamedColor::DimBlack] = dim.black; - self[ansi::NamedColor::DimRed] = dim.red; - self[ansi::NamedColor::DimGreen] = dim.green; - self[ansi::NamedColor::DimYellow] = dim.yellow; - self[ansi::NamedColor::DimBlue] = dim.blue; - self[ansi::NamedColor::DimMagenta] = dim.magenta; - self[ansi::NamedColor::DimCyan] = dim.cyan; - self[ansi::NamedColor::DimWhite] = dim.white; - } - None => { - trace!("Deriving dim colors from normal colors"); - self[ansi::NamedColor::DimBlack] = colors.normal.black * 0.66; - self[ansi::NamedColor::DimRed] = colors.normal.red * 0.66; - self[ansi::NamedColor::DimGreen] = colors.normal.green * 0.66; - self[ansi::NamedColor::DimYellow] = colors.normal.yellow * 0.66; - self[ansi::NamedColor::DimBlue] = colors.normal.blue * 0.66; - self[ansi::NamedColor::DimMagenta] = colors.normal.magenta * 0.66; - self[ansi::NamedColor::DimCyan] = colors.normal.cyan * 0.66; - self[ansi::NamedColor::DimWhite] = colors.normal.white * 0.66; - } - } - } - - pub fn fill_cube(&mut self, colors: &Colors) { - let mut index: usize = 16; - // Build colors - for r in 0..6 { - for g in 0..6 { - for b in 0..6 { - // Override colors 16..232 with the config (if present) - if let Some(indexed_color) = colors - .indexed_colors - .iter() - .find(|ic| ic.index == index as u8) - { - self[index] = indexed_color.color; - } else { - self[index] = Rgb { - r: if r == 0 { 0 } else { r * 40 + 55 }, - b: if b == 0 { 0 } else { b * 40 + 55 }, - g: if g == 0 { 0 } else { g * 40 + 55 }, - }; - } - index += 1; - } - } - } - - debug_assert!(index == 232); - } - - pub fn fill_gray_ramp(&mut self, colors: &Colors) { - let mut index: usize = 232; - - for i in 0..24 { - // Index of the color is number of named colors + number of cube colors + i - let color_index = 16 + 216 + i; - - // Override colors 232..256 with the config (if present) - if let Some(indexed_color) = colors - .indexed_colors - .iter() - .find(|ic| ic.index == color_index) - { - self[index] = indexed_color.color; - index += 1; - continue; - } - - let value = i * 10 + 8; - self[index] = Rgb { - r: value, - g: value, - b: value, - }; - index += 1; - } - - debug_assert!(index == 256); - } -} - -impl fmt::Debug for List { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("List[..]") - } -} - -impl Index for List { - type Output = Rgb; - - #[inline] - fn index(&self, idx: ansi::NamedColor) -> &Self::Output { - &self.0[idx as usize] - } -} - -impl IndexMut for List { - #[inline] - fn index_mut(&mut self, idx: ansi::NamedColor) -> &mut Self::Output { - &mut self.0[idx as usize] - } -} - -impl Index for List { - type Output = Rgb; - - #[inline] - fn index(&self, idx: usize) -> &Self::Output { - &self.0[idx] - } -} - -impl IndexMut for List { - #[inline] - fn index_mut(&mut self, idx: usize) -> &mut Self::Output { - &mut self.0[idx] - } -} - -impl Index for List { - type Output = Rgb; - - #[inline] - fn index(&self, idx: u8) -> &Self::Output { - &self.0[idx as usize] - } -} - -impl IndexMut for List { - #[inline] - fn index_mut(&mut self, idx: u8) -> &mut Self::Output { - &mut self.0[idx as usize] - } -} diff --git a/src/url.rs b/src/url.rs deleted file mode 100644 index f8d25a7499..0000000000 --- a/src/url.rs +++ /dev/null @@ -1,307 +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 url::Url; - -// See https://tools.ietf.org/html/rfc3987#page-13 -const URL_SEPARATOR_CHARS: [char; 10] = ['<', '>', '"', ' ', '{', '}', '|', '\\', '^', '`']; -const URL_DENY_END_CHARS: [char; 8] = ['.', ',', ';', ':', '?', '!', '/', '(']; -const URL_SCHEMES: [&str; 8] = [ - "http", "https", "mailto", "news", "file", "git", "ssh", "ftp", -]; - -// Parser for streaming inside-out detection of URLs. -pub struct UrlParser { - state: String, -} - -impl UrlParser { - pub fn new() -> Self { - UrlParser { - state: String::new(), - } - } - - /// Advance the parser one character to the left. - pub fn advance_left(&mut self, c: char) -> bool { - self.advance(c, 0) - } - - /// Advance the parser one character to the right. - pub fn advance_right(&mut self, c: char) -> bool { - self.advance(c, self.state.len()) - } - - /// Returns the URL if the parser has found any. - pub fn url(mut self) -> Option { - // Remove non-alphabetical characters before the scheme - // https://tools.ietf.org/html/rfc3986#section-3.1 - if let Some(index) = self.state.find("://") { - let iter = self - .state - .char_indices() - .rev() - .skip_while(|(byte_index, _)| *byte_index >= index); - for (byte_index, c) in iter { - match c { - 'a'...'z' | 'A'...'Z' => (), - _ => { - self.state = self.state.split_off(byte_index + c.len_utf8()); - break; - } - } - } - } - - // Remove non-matching parenthesis and brackets - let mut open_parens_count: isize = 0; - let mut open_bracks_count: isize = 0; - for (i, c) in self.state.chars().enumerate() { - match c { - '(' => open_parens_count += 1, - ')' if open_parens_count > 0 => open_parens_count -= 1, - '[' => open_bracks_count += 1, - ']' if open_bracks_count > 0 => open_bracks_count -= 1, - ')' | ']' => { - self.state.truncate(i); - break; - } - _ => (), - } - } - - // Track number of quotes - let mut num_quotes = self.state.chars().filter(|&c| c == '\'').count(); - - // Remove all characters which aren't allowed at the end of a URL - while !self.state.is_empty() - && (URL_DENY_END_CHARS.contains(&self.state.chars().last().unwrap()) - || (num_quotes % 2 != 0 && self.state.ends_with('\'')) - || self.state.ends_with("''") - || self.state.ends_with("()")) - { - if self.state.pop().unwrap() == '\'' { - num_quotes -= 1; - } - } - - // Check if string is valid url - match Url::parse(&self.state) { - Ok(url) => { - if URL_SCHEMES.contains(&url.scheme()) { - Some(self.state) - } else { - None - } - } - Err(_) => None, - } - } - - fn advance(&mut self, c: char, pos: usize) -> bool { - if URL_SEPARATOR_CHARS.contains(&c) - || (c >= '\u{00}' && c <= '\u{1F}') - || (c >= '\u{7F}' && c <= '\u{9F}') - { - true - } else { - self.state.insert(pos, c); - false - } - } -} - -#[cfg(test)] -mod tests { - use std::mem; - - use crate::grid::Grid; - use crate::index::{Column, Line, Point}; - use crate::message_bar::MessageBuffer; - use crate::term::cell::Cell; - use crate::term::{Search, SizeInfo, Term}; - - fn url_create_term(input: &str) -> Term { - 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 term = Term::new(&Default::default(), size, MessageBuffer::new()); - let mut grid: Grid = Grid::new(Line(1), Column(input.len()), 0, Cell::default()); - - for (i, c) in input.chars().enumerate() { - grid[Line(0)][Column(i)].c = c; - } - - mem::swap(term.grid_mut(), &mut grid); - - term - } - - fn url_test(input: &str, expected: &str, click_index: usize) { - let term = url_create_term(input); - - let url = term.url_search(Point::new(0, Column(click_index))); - - assert_eq!(url, Some(expected.into())); - } - - #[test] - fn url_skip_invalid() { - let term = url_create_term("no url here"); - let url = term.url_search(Point::new(0, Column(4))); - assert_eq!(url, None); - } - - #[test] - fn url_matching_chars() { - url_test( - "(https://example.org/test(ing))", - "https://example.org/test(ing)", - 5, - ); - url_test( - "https://example.org/test(ing)", - "https://example.org/test(ing)", - 5, - ); - url_test("((https://example.org))", "https://example.org", 5); - url_test(")https://example.org(", "https://example.org", 5); - url_test("https://example.org)", "https://example.org", 5); - url_test("https://example.org(", "https://example.org", 5); - url_test("(https://one.org/)(https://two.org/)", "https://one.org", 5); - - url_test( - "https://[2001:db8:a0b:12f0::1]:80", - "https://[2001:db8:a0b:12f0::1]:80", - 5, - ); - url_test( - "([(https://example.org/test(ing))])", - "https://example.org/test(ing)", - 5, - ); - url_test("https://example.org/]()", "https://example.org", 5); - url_test("[https://example.org]", "https://example.org", 5); - - url_test( - "'https://example.org/test'ing'''", - "https://example.org/test'ing'", - 5, - ); - url_test( - "https://example.org/test'ing'", - "https://example.org/test'ing'", - 5, - ); - url_test("'https://example.org'", "https://example.org", 5); - url_test("'https://example.org", "https://example.org", 5); - url_test("https://example.org'", "https://example.org", 5); - } - - #[test] - fn url_detect_end() { - url_test( - "https://example.org/test\u{00}ing", - "https://example.org/test", - 5, - ); - url_test( - "https://example.org/test\u{1F}ing", - "https://example.org/test", - 5, - ); - url_test( - "https://example.org/test\u{7F}ing", - "https://example.org/test", - 5, - ); - url_test( - "https://example.org/test\u{9F}ing", - "https://example.org/test", - 5, - ); - url_test( - "https://example.org/test\ting", - "https://example.org/test", - 5, - ); - url_test( - "https://example.org/test ing", - "https://example.org/test", - 5, - ); - } - - #[test] - fn url_remove_end_chars() { - url_test( - "https://example.org/test?ing", - "https://example.org/test?ing", - 5, - ); - url_test("https://example.org.,;:)'!/?", "https://example.org", 5); - url_test("https://example.org'.", "https://example.org", 5); - } - - #[test] - fn url_remove_start_chars() { - url_test("complicated:https://example.org", "https://example.org", 15); - url_test("test.https://example.org", "https://example.org", 10); - url_test(",https://example.org", "https://example.org", 5); - url_test("\u{2502}https://example.org", "https://example.org", 5); - } - - #[test] - fn url_unicode() { - url_test( - "https://xn--example-2b07f.org", - "https://xn--example-2b07f.org", - 5, - ); - url_test( - "https://example.org/\u{2008A}", - "https://example.org/\u{2008A}", - 5, - ); - url_test( - "https://example.org/\u{f17c}", - "https://example.org/\u{f17c}", - 5, - ); - url_test( - "https://üñîçøðé.com/ä", - "https://üñîçøðé.com/ä", - 5, - ); - } - - #[test] - fn url_schemes() { - url_test("mailto://example.org", "mailto://example.org", 5); - url_test("https://example.org", "https://example.org", 5); - url_test("http://example.org", "http://example.org", 5); - url_test("news://example.org", "news://example.org", 5); - url_test("file://example.org", "file://example.org", 5); - url_test("git://example.org", "git://example.org", 5); - url_test("ssh://example.org", "ssh://example.org", 5); - url_test("ftp://example.org", "ftp://example.org", 5); - } -} diff --git a/winpty/src/lib.rs b/winpty/src/lib.rs index 8604d085ea..117ec16ddc 100644 --- a/winpty/src/lib.rs +++ b/winpty/src/lib.rs @@ -1,9 +1,4 @@ -#![deny( - clippy::all, - clippy::if_not_else, - clippy::enum_glob_use, - clippy::wrong_pub_self_convention -)] +#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)] #[macro_use] #[cfg(windows)] diff --git a/winpty/src/windows.rs b/winpty/src/windows.rs index 4e439e4596..ada8bb0d15 100644 --- a/winpty/src/windows.rs +++ b/winpty/src/windows.rs @@ -431,14 +431,8 @@ mod tests { ) .unwrap(); -<<<<<<< HEAD let processes = winpty.console_process_list(1000).expect("failed to get console process list"); -======= - let processes = winpty - .console_process_list(1000) - .expect("failed to get console process list"); ->>>>>>> Tried setting scale and ppem but it didn't change anything. // Check that each id is valid processes.iter().for_each(|id| { From a32a835bf641024e2e7209fed595b0b43001f2b7 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 25 Jul 2019 17:21:45 -0700 Subject: [PATCH 25/84] Removed HbError and related code. Fixed errors were chars were being used as glyph keys with hb-ft on. Clean up more extraneous changes. --- .gitignore | 1 + Cargo.lock | 117 +++++++++++++------------ alacritty_terminal/Cargo.toml | 2 +- alacritty_terminal/src/cursor.rs | 13 ++- alacritty_terminal/src/renderer/mod.rs | 15 ++-- alacritty_terminal/src/term/mod.rs | 6 +- build.rs | 94 -------------------- font/src/darwin/mod.rs | 1 + font/src/ft/mod.rs | 40 ++++----- font/src/lib.rs | 14 ++- 10 files changed, 103 insertions(+), 200 deletions(-) delete mode 100644 build.rs diff --git a/.gitignore b/.gitignore index c5af9dabcf..ffbb569ab0 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ FlameGraph # Rust subcrate lockfiles */Cargo.lock +Cargo.lock # Temp files .idea diff --git a/Cargo.lock b/Cargo.lock index 72dcbd2997..c7bb8e7f8c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,7 +27,7 @@ version = "0.3.3" dependencies = [ "alacritty_terminal 0.3.3", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -46,7 +46,7 @@ dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "copypasta 0.6.0", - "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -167,7 +167,7 @@ name = "backtrace-sys" version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -235,7 +235,7 @@ name = "bzip2-sys" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -250,7 +250,7 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -323,7 +323,7 @@ name = "cmake" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -411,11 +411,10 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -423,21 +422,21 @@ name = "crossbeam-deque" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-epoch" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -445,12 +444,12 @@ name = "crossbeam-queue" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-utils" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -473,7 +472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -594,7 +593,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -603,7 +602,7 @@ version = "2.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cmake 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -622,7 +621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -683,7 +682,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -722,7 +721,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -883,7 +882,7 @@ dependencies = [ "core-text 13.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1037,7 +1036,7 @@ name = "libloading" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1046,9 +1045,9 @@ name = "libz-sys" version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1124,8 +1123,11 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.2.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "mio" @@ -1193,7 +1195,7 @@ name = "miow" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "socket2 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1239,7 +1241,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1292,7 +1294,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1390,9 +1392,9 @@ version = "0.9.48" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1487,7 +1489,7 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1562,7 +1564,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1578,10 +1580,9 @@ dependencies = [ [[package]] name = "rand_chacha" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1688,7 +1689,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1831,6 +1832,11 @@ name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "security-framework" version = "0.3.1" @@ -1875,7 +1881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1914,7 +1920,7 @@ version = "4.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "expat-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "servo-freetype-sys 4.0.3", ] @@ -2010,7 +2016,7 @@ dependencies = [ [[package]] name = "socket2" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2054,7 +2060,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.15.40" +version = "0.15.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2069,7 +2075,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2240,7 +2246,7 @@ name = "vswhom-sys" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2473,7 +2479,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bindgen 0.33.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2508,7 +2514,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2574,7 +2580,7 @@ dependencies = [ "checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" "checksum bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6584aa36f5ad4c9247f5323b0a42f37802b37a836f0ad87084d7a33961abe25f" "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" -"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" +"checksum cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "ce400c638d48ee0e9ab75aef7997609ec57367ccfe1463f21bf53c3eca67bf46" "checksum cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42aac45e9567d97474a834efdee3081b3c942b2205be932092f53354ce503d6c" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" "checksum cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49" @@ -2591,11 +2597,11 @@ dependencies = [ "checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" "checksum core-text 13.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12684243b314c95600a2b49628fb775f91d97bbe18424522f665b77014f2a640" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -"checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" +"checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" "checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" -"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" +"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" +"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" "checksum deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" "checksum derivative 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6073e9676dbebdddeabaeb63e3b7cefd23c86f5c41d381ee1237cc77b1079898" "checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" @@ -2672,7 +2678,7 @@ dependencies = [ "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" +"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" "checksum mio-anonymous-pipes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8c274c3c52dcd1d78c5d7ed841eca1e9ea2db8353f3b8ec25789cc62c471aaf" "checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" @@ -2710,7 +2716,7 @@ dependencies = [ "checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" "checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" "checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" -"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" +"checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af" "checksum png 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "63daf481fdd0defa2d1d2be15c674fbfa1b0fd71882c303a91f9a79b3252c359" "checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" @@ -2721,7 +2727,7 @@ dependencies = [ "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_chacha 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e193067942ef6f485a349a113329140d0ab9e2168ce92274499bb0e9a4190d9d" +"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" "checksum rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615e683324e75af5d43d8f7a39ffe3ee4a9dc42c5c701167a71dc59c3a493aca" @@ -2752,6 +2758,7 @@ dependencies = [ "checksum schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" +"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eee63d0f4a9ec776eeb30e220f0bc1e092c3ad744b2a379e3993070364d3adc2" "checksum security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" @@ -2771,14 +2778,14 @@ dependencies = [ "checksum smithay-client-toolkit 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2ccb8c57049b2a34d2cc2b203fa785020ba0129d31920ef0d317430adaf748fa" "checksum smithay-client-toolkit 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8c23093ebdaac1ad67558c7aef153522c6b3be7e0257820909e3a25c4519e78d" "checksum smithay-clipboard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d8e61f8a9ef7a15bd38c29b4a39d423776b2d6092723dc7df3f0545ce7a4751e" -"checksum socket2 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "4e626972d3593207547f14bf5fc9efa4d0e7283deb73fef1dff313dae9ab8878" +"checksum socket2 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df028e0e632c2a1823d920ad74895e7f9128e6438cbc4bc6fd1f180e644767b9" "checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" "checksum spsc-buffer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be6c3f39c37a4283ee4b43d1311c828f2e1fb0541e76ea0cb1a2abd9ef2f5b3b" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4f8de36da215253eb5f24020bfaa0646613b48bf7ebe36cdfa37c3b3b33b241" "checksum stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "69b7df505db8e81d54ff8be4693421e5b543e08214bd8d99eb761fcb4d5668ba" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum syn 0.15.40 (registry+https://github.com/rust-lang/crates.io-index)" = "bc945221ccf4a7e8c31222b9d1fc77aefdd6638eb901a6ce457a3dc29d4c31e8" +"checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index 671eec3f34..6caa646c8a 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -56,7 +56,7 @@ image = "0.21.0" objc = "0.2.2" [features] -default = [] +default = ["hb-ft"] # Enabling this feature makes shaders automatically reload when changed live-shader-reload = [] nightly = [] diff --git a/alacritty_terminal/src/cursor.rs b/alacritty_terminal/src/cursor.rs index 93f2bd30e1..b820f757bb 100644 --- a/alacritty_terminal/src/cursor.rs +++ b/alacritty_terminal/src/cursor.rs @@ -56,13 +56,18 @@ pub fn get_cursor_glyph( } } +#[cfg(not(feature = "hb-ft"))] +const CUSOR: char = ' '; +#[cfg(feature = "hb-ft")] +const CURSOR: u32 = 0; + // 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 } + RasterizedGlyph { c: CURSOR, top: line_width, left: 0, height: line_width, width, buf } } // Returns a custom beam cursor character @@ -71,7 +76,7 @@ pub fn get_beam_cursor_glyph(height: i32, line_width: i32) -> RasterizedGlyph { 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 } + RasterizedGlyph { c: CURSOR, top: height, left: 0, height, width: line_width, buf } } // Returns a custom box cursor character @@ -93,7 +98,7 @@ pub fn get_box_cursor_glyph(height: i32, width: i32, line_width: i32) -> Rasteri } // Create a custom glyph with the rectangle data attached to it - RasterizedGlyph { c: ' ', top: height, left: 0, height, width, buf } + RasterizedGlyph { c: CURSOR, top: height, left: 0, height, width, buf } } // Returns a custom block cursor character @@ -102,5 +107,5 @@ pub fn get_block_cursor_glyph(height: i32, width: i32) -> RasterizedGlyph { 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 } + RasterizedGlyph { c: CURSOR, top: height, left: 0, height, width, buf } } diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index ebfeb519a1..4cf61e8954 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -38,8 +38,6 @@ use crate::term::text_run::{TextRun, TextRunContent}; use crate::term::{self, cell, RenderableCell}; #[cfg(not(feature = "hb-ft"))] use crate::term::RenderableCellContent; -#[cfg(feature = "hb-ft")] -use font::HbError; pub mod rects; @@ -187,8 +185,8 @@ pub struct GlyphCache { impl GlyphCache { pub fn new( - mut rasterizer: Rasterizer, - font: &config::Font, + mut rasterizer: Rasterizer, + font: &config::Font, loader: &mut L, ) -> Result where @@ -306,12 +304,12 @@ impl GlyphCache { text_run: &str, font_key: FontKey, loader: &'a mut L, - ) -> Result, HbError> + ) -> Vec where L: LoadGlyph, { use font::{HbFtExt}; - Ok(self.rasterizer.shape(text_run, font_key)? + self.rasterizer.shape(text_run, font_key) .get_glyph_infos() .iter() .map(move |glyph_info| *self.get(GlyphKey { @@ -319,7 +317,7 @@ impl GlyphCache { font_key, size: self.font_size, }, loader)) - .collect()) + .collect() } pub fn get<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L) -> &'a Glyph @@ -1095,8 +1093,7 @@ impl<'a> RenderApi<'a> { let hidden = text_run.flags.contains(cell::Flags::HIDDEN); if !hidden { let glyphs = glyph_cache - .shape_run(&run, font_key, self) - .expect("harfbuzz font to be present"); + .shape_run(&run, font_key, self); for (cell, glyph) in text_run.cell_iter().zip(glyphs.into_iter()) { self.add_render_item(&cell, &glyph); } diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 2e019e6e5d..ef9de635db 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -516,7 +516,7 @@ pub mod text_run { RenderableCell { line: self.line, column: col, - inner: [' '; crate::term::cell::MAX_ZEROWIDTH_CHARS + 1].into(), + inner: RenderableCellContent::Chars([' '; crate::term::cell::MAX_ZEROWIDTH_CHARS + 1]), fg: self.fg, bg: self.bg, bg_alpha: self.bg_alpha, @@ -548,7 +548,6 @@ pub mod text_run { iter: I, run_start: Option, latest: Option, - buffer: Vec<[char; MAX_ZEROWIDTH_CHARS + 1]>, cursor: Option, buffer_text: String, buffer_zero_width: Vec<[char; MAX_ZEROWIDTH_CHARS]>, @@ -559,7 +558,6 @@ pub mod text_run { iter, latest: None, run_start: None, - buffer: Vec::new(), cursor: None, buffer_text: String::new(), buffer_zero_width: Vec::new(), @@ -639,7 +637,7 @@ pub mod text_run { self.buffer_content(rc.inner); } output.or_else(|| { - if !self.buffer.is_empty() { + if !self.buffer_text.is_empty() || !self.buffer_zero_width.is_empty() { opt_pair(self.run_start.take(), self.latest.take()).map(|(start, latest)| // Save leftover buffer and empty it TextRun::from_iter_state(start, latest, self.drain_buffer())) diff --git a/build.rs b/build.rs deleted file mode 100644 index 19127a1d93..0000000000 --- a/build.rs +++ /dev/null @@ -1,94 +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. -#[cfg(windows)] -use embed_resource; -#[cfg(windows)] -use reqwest; -#[cfg(windows)] -use tempfile; -#[cfg(windows)] -use zip; - -use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry}; - -use std::env; -use std::fs::File; -use std::path::Path; - -#[cfg(windows)] -use std::fs::OpenOptions; -#[cfg(windows)] -use std::io; - -#[cfg(windows)] -const WINPTY_PACKAGE_URL: &str = - "https://github.com/rprichard/winpty/releases/download/0.4.3/winpty-0.4.3-msvc2015.zip"; - -fn main() { - 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("assets/windows/windows.rc"); - - // Path is relative to target/{profile}/build/alacritty-HASH/out - let file = Path::new(&env::var("OUT_DIR").unwrap()).join("../../../winpty-agent.exe"); - if !file.exists() { - aquire_winpty_agent(&file); - } - } -} - -#[cfg(windows)] -fn aquire_winpty_agent(out_path: &Path) { - let tmp_dir = tempfile::Builder::new() - .prefix("alacritty_build") - .tempdir() - .unwrap(); - - let mut response = reqwest::get(WINPTY_PACKAGE_URL).unwrap(); - let mut file = OpenOptions::new() - .read(true) - .write(true) - .create(true) - .open(tmp_dir.path().join("winpty_package.zip")) - .unwrap(); - - io::copy(&mut response, &mut file).unwrap(); - - let mut archive = zip::ZipArchive::new(file).unwrap(); - - let target = match env::var("TARGET").unwrap().split("-").next().unwrap() { - "x86_64" => "x64", - "i386" => "ia32", - _ => panic!("architecture has no winpty binary"), - }; - - let mut winpty_agent = archive - .by_name(&format!("{}/bin/winpty-agent.exe", target)) - .unwrap(); - - io::copy(&mut winpty_agent, &mut File::create(out_path).unwrap()).unwrap(); -} diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 70552d6fc0..cccae0327f 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -537,6 +537,7 @@ impl Font { // and use the utf-16 buffer to get the index self.glyph_index_utf16(encoded) } + fn glyph_index_utf16(&self, encoded: &[u16]) -> Option { // output buffer for the glyph. for non-BMP glyphs, like // emojis, this will be filled with two chars the second diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 79aed749c5..b3452d7707 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -19,9 +19,7 @@ use std::fmt; use std::path::PathBuf; #[cfg(feature = "hb-ft")] -use super::HbError; -#[cfg(feature = "hb-ft")] -use harfbuzz_rs::{Shared, Owned, Font, GlyphBuffer}; +use harfbuzz_rs::{Owned, Font, GlyphBuffer}; use freetype::tt_os2::TrueTypeOS2Table; use freetype::{self, Library}; use libc::c_uint; @@ -53,12 +51,12 @@ impl fmt::Debug for Face { .field("key", &self.key) .field("load_flags", &self.load_flags) .field("render_mode", &match self.render_mode { - freetype::RenderMode::Normal => "Normal", - freetype::RenderMode::Light => "Light", - freetype::RenderMode::Mono => "Mono", - freetype::RenderMode::Lcd => "Lcd", - freetype::RenderMode::LcdV => "LcdV", - freetype::RenderMode::Max => "Max", + freetype::RenderMode::Normal => "Normal", + freetype::RenderMode::Light => "Light", + freetype::RenderMode::Mono => "Mono", + freetype::RenderMode::Lcd => "Lcd", + freetype::RenderMode::LcdV => "LcdV", + freetype::RenderMode::Max => "Max", }) .field("lcd_filter", &self.lcd_filter) .finish() @@ -156,20 +154,15 @@ impl ::HbFtExt for FreeTypeRasterizer { &mut self, text: &str, font_key: FontKey, - ) -> Result { + ) -> GlyphBuffer { use harfbuzz_rs::{shape, UnicodeBuffer}; - self.faces[&font_key] - .hb_font - .as_ref() - .ok_or(HbError::MissingFont(font_key)) - .map(|hb_font| { - let buf = UnicodeBuffer::default() - .add_str(text) - .guess_segment_properties(); - - // Shape with default features - shape(&*hb_font, buf, &[]) - }) + let hb_font = &self.faces[&font_key].hb_font; + let buf = UnicodeBuffer::default() + .add_str(text) + .guess_segment_properties(); + + // Shape with default features + shape(&*hb_font, buf, &[]) } } @@ -300,8 +293,7 @@ impl FreeTypeRasterizer { let hb_font = { use harfbuzz_rs::*; let _hb_face = Face::from_file(&path, index as u32)?; - let _hb_font = Font::new(_hb_face); - _hb_font + Font::new(_hb_face) }; let face = Face { diff --git a/font/src/lib.rs b/font/src/lib.rs index fcb09e8e61..9380556e20 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -39,12 +39,14 @@ extern crate core_graphics; extern crate core_text; #[cfg(all(target_os = "macos", not(feature = "hb-ft")))] extern crate euclid; + +extern crate libc; + + #[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] #[macro_use] extern crate foreign_types; -extern crate libc; - #[cfg(feature = "hb-ft")] extern crate harfbuzz_rs; @@ -289,11 +291,5 @@ pub trait HbFtExt { /// Shape the provided text into a set of glyphs. /// TODO: properly report HarfBuzz errors fn shape(&mut self, text: &str, font_key: FontKey) - -> Result; -} - -#[derive(Debug)] -pub enum HbError { - /// Could not find harfbuzz font for key. - MissingFont(FontKey), + -> harfbuzz_rs::GlyphBuffer; } From de83c0bd036216782bfc28759d33af9564e5a9a2 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 25 Jul 2019 17:39:31 -0700 Subject: [PATCH 26/84] Added documentation to the text_run module. --- alacritty_terminal/src/term/mod.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index ef9de635db..1ecabbbe2c 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -474,6 +474,9 @@ pub mod text_run { } #[derive(Debug)] + /// Represents a set of renderable cells that all share the rendering propreties. + /// The assumption is that if two cells are in the same TextRun they can be sent off together to be shaped by harfbuzz. + /// This allows for ligatures to be rendered but not when something breaks up a ligature (e.g. selection hightlight) which is intended behavior. pub struct TextRun { // By definition a run is on one line. pub line: Line, @@ -485,6 +488,7 @@ pub mod text_run { pub flags: Flags, } impl TextRun { + // These two constructors are used by TextRunIter and are not widely applicable pub(crate) fn from_iter_state( start: RunStart, latest: Column, @@ -524,9 +528,12 @@ pub mod text_run { } } + /// First cell in the TextRun pub fn start_cell(&self) -> RenderableCell { self.cell_at(self.run.0) } + + /// Last cell in the TextRun pub fn last_cell(&self) -> RenderableCell { self.cell_at(self.run.1) } @@ -544,6 +551,8 @@ pub mod text_run { self.col_iter().map(move |col| self.cell_at(col)) } } + + /// Wraps an Iterator and produces TextRuns to represent batches of cells pub struct TextRunIter { iter: I, run_start: Option, @@ -576,6 +585,7 @@ pub mod text_run { is_cell_break || is_col_break } + /// Add content of cell to pending TextRun buffer fn buffer_content(&mut self, inner: RenderableCellContent) { // Add to buffer only if the next rc is a Char (not a cursor) match inner { @@ -591,10 +601,12 @@ pub mod text_run { } } + /// Empty out pending buffer producing owned collections that can be moved into a TextRun fn drain_buffer(&mut self) -> (String, Vec<[char; MAX_ZEROWIDTH_CHARS]>) { (self.buffer_text.drain(..).collect(), self.buffer_zero_width.drain(..).collect()) } + /// Handles bookkeeping needed when starting a new run fn start_run(&mut self, rc: RenderableCell) -> (Option, Option) { self.buffer_content(rc.inner); let latest = self.latest.replace(rc.column); @@ -603,6 +615,7 @@ pub mod text_run { } } + /// Utility method to ease use of Options when you need to unwrap both in tandem fn opt_pair(a: Option, b: Option) -> Option<(A, B)> { match (a, b) { (Some(a_val), Some(b_val)) => Some((a_val, b_val)), @@ -620,12 +633,17 @@ pub mod text_run { let mut output = None; while let Some(rc) = self.iter.next() { if self.latest.is_none() || self.run_start.is_none() { + // Initial state, this is should only be hit on the first next() call of iterator self.run_start.replace(from_rc!(rc)); } else if self.cursor.is_some() { + // Last iteration of the loop found a cursor + // Return a run for the cursor and start a new run let (start, _) = self.start_run(rc); output = opt_pair(start, self.cursor.take()).map(|(start, cursor)| TextRun::from_cursor_rc(start, cursor)); break; } else if self.is_run_break(&rc) || rc.is_cursor() { + // If we find a break or a cursor, + // return what we have so far and start a new run. let prev_buffer = self.drain_buffer(); let (start, latest) = self.start_run(rc); output = opt_pair(start, latest).map(|(start, latest)| { @@ -633,9 +651,13 @@ pub mod text_run { }); break; } + // Build up buffer and track the latest column we've seen self.latest = Some(rc.column); self.buffer_content(rc.inner); } + // If we generated output we're done + // Otherwise check for any remaining buffered content and return it as a text run + // This a destructive operation so it will return None after it excutes once output.or_else(|| { if !self.buffer_text.is_empty() || !self.buffer_zero_width.is_empty() { opt_pair(self.run_start.take(), self.latest.take()).map(|(start, latest)| From 4bfedf0b8f31a85b3b21dd3e7e7accfcf552bdc6 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 25 Jul 2019 18:20:03 -0700 Subject: [PATCH 27/84] Further clean up to reduce code in PR. --- .gitignore | 1 - Cargo.lock | 484 ++++++++++++++++++---------------- alacritty_terminal/Cargo.toml | 2 +- 3 files changed, 260 insertions(+), 227 deletions(-) diff --git a/.gitignore b/.gitignore index ffbb569ab0..c5af9dabcf 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,6 @@ FlameGraph # Rust subcrate lockfiles */Cargo.lock -Cargo.lock # Temp files .idea diff --git a/Cargo.lock b/Cargo.lock index c7bb8e7f8c..1d653c4f81 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -27,10 +27,10 @@ version = "0.3.3" dependencies = [ "alacritty_terminal 0.3.3", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_tools_util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -46,7 +46,7 @@ dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "copypasta 0.6.0", - "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "errno 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -54,8 +54,8 @@ dependencies = [ "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "mio-anonymous-pipes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -65,11 +65,11 @@ dependencies = [ "notify 4.0.12 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", - "signal-hook 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "signal-hook 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "terminfo 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -89,7 +89,7 @@ dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusttype 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -131,7 +131,7 @@ dependencies = [ [[package]] name = "arrayvec" -version = "0.4.11" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -139,36 +139,38 @@ dependencies = [ [[package]] name = "atty" -version = "0.2.13" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "autocfg" -version = "0.1.5" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.33" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace-sys 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace-sys" -version = "0.1.31" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -190,7 +192,7 @@ dependencies = [ "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -207,7 +209,7 @@ name = "blake2-rfc" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -227,7 +229,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -235,8 +237,8 @@ name = "bzip2-sys" version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -250,7 +252,7 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.38" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -274,8 +276,8 @@ name = "cgl" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gleam 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "gleam 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -284,7 +286,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -294,7 +296,7 @@ version = "2.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -323,7 +325,7 @@ name = "cmake" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -336,7 +338,7 @@ dependencies = [ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -371,7 +373,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -387,18 +389,18 @@ dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-text" -version = "13.3.0" +version = "13.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -411,10 +413,11 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.3.9" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -422,21 +425,21 @@ name = "crossbeam-deque" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-epoch" -version = "0.7.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -444,12 +447,12 @@ name = "crossbeam-queue" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-utils" -version = "0.6.6" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -471,8 +474,8 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -480,7 +483,7 @@ name = "dirs" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -514,9 +517,9 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -539,10 +542,10 @@ name = "env_logger" version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -551,10 +554,10 @@ name = "env_logger" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -564,7 +567,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "errno-dragonfly 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -574,7 +577,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -592,8 +595,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -602,7 +605,7 @@ version = "2.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cmake 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -610,7 +613,7 @@ name = "failure" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -620,8 +623,8 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -631,7 +634,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -647,14 +650,14 @@ dependencies = [ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-text 13.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-text 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "dwrote 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.9 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype-rs 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)", "harfbuzz_rs 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -681,8 +684,8 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -700,7 +703,7 @@ name = "freetype" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "servo-freetype-sys 4.0.3", ] @@ -711,7 +714,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "freetype-sys 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -719,9 +722,9 @@ name = "freetype-sys" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -738,7 +741,7 @@ name = "fsevent-sys" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -771,7 +774,7 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -789,13 +792,13 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gleam" -version = "0.6.18" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -879,10 +882,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cmake 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-text 13.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-text 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -896,7 +899,7 @@ dependencies = [ [[package]] name = "http_req" -version = "0.5.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "native-tls 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -953,7 +956,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "inotify-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -961,7 +964,7 @@ name = "inotify-sys" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -969,7 +972,7 @@ name = "iovec" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1016,7 +1019,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.60" +version = "0.2.59" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1036,7 +1039,7 @@ name = "libloading" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1045,9 +1048,9 @@ name = "libz-sys" version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1075,7 +1078,7 @@ dependencies = [ [[package]] name = "log" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1091,7 +1094,7 @@ name = "malloc_buf" version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1104,7 +1107,7 @@ name = "memchr" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1117,17 +1120,14 @@ name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memoffset" -version = "0.5.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "mio" @@ -1138,8 +1138,8 @@ dependencies = [ "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1163,7 +1163,7 @@ version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1173,12 +1173,22 @@ name = "mio-named-pipes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mio-uds" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "miow" version = "0.2.1" @@ -1195,7 +1205,7 @@ name = "miow" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "socket2 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1214,11 +1224,11 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.48 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)", "schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1231,7 +1241,7 @@ version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1241,9 +1251,9 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1280,10 +1290,10 @@ dependencies = [ "fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "inotify 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1293,8 +1303,8 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1302,7 +1312,7 @@ name = "num-integer" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1311,7 +1321,7 @@ name = "num-iter" version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1321,7 +1331,7 @@ name = "num-rational" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1331,7 +1341,7 @@ name = "num-traits" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1339,9 +1349,14 @@ name = "num_cpus" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "numtoa" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "objc" version = "0.2.6" @@ -1370,15 +1385,15 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.24" +version = "0.10.23" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.48 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1388,13 +1403,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.48" +version = "0.9.47" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1436,7 +1451,7 @@ name = "parking_lot_core" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1489,7 +1504,7 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.15" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1533,7 +1548,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "quote" -version = "0.6.13" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1544,8 +1559,8 @@ name = "rand" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1563,8 +1578,8 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1574,15 +1589,16 @@ name = "rand_chacha" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand_chacha" -version = "0.2.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1637,7 +1653,7 @@ name = "rand_jitter" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1649,7 +1665,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1660,7 +1676,7 @@ name = "rand_pcg" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1689,7 +1705,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1707,6 +1723,14 @@ name = "redox_syscall" version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "redox_termios" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "redox_users" version = "0.3.0" @@ -1732,12 +1756,12 @@ dependencies = [ [[package]] name = "regex" -version = "1.2.0" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1747,15 +1771,15 @@ name = "regex-syntax" version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "regex-syntax" -version = "0.6.10" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1795,7 +1819,7 @@ version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1807,7 +1831,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "same-file" -version = "1.0.5" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1832,11 +1856,6 @@ name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "scopeguard" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "security-framework" version = "0.3.1" @@ -1844,7 +1863,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1871,17 +1890,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.97" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.97" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1891,7 +1910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1901,7 +1920,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1910,7 +1929,7 @@ name = "servo-fontconfig" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "servo-fontconfig-sys 4.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1920,7 +1939,7 @@ version = "4.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "expat-sys 2.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "servo-freetype-sys 4.0.3", ] @@ -1937,16 +1956,17 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "signal-hook" -version = "0.1.10" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", "signal-hook-registry 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1956,7 +1976,7 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arc-swap 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2016,11 +2036,11 @@ dependencies = [ [[package]] name = "socket2" -version = "0.3.10" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2060,11 +2080,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" -version = "0.15.42" +version = "0.15.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2074,8 +2094,8 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2090,7 +2110,7 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2116,6 +2136,17 @@ dependencies = [ "phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "termion" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -2148,14 +2179,14 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ucd-util" -version = "0.1.5" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2237,7 +2268,7 @@ name = "vswhom" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "vswhom-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2246,8 +2277,8 @@ name = "vswhom-sys" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2260,10 +2291,10 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.2.9" +version = "2.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2275,7 +2306,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "downcast-rs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-scanner 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2289,7 +2320,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "downcast-rs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-commons 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-scanner 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2343,7 +2374,7 @@ version = "0.21.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2353,7 +2384,7 @@ version = "0.23.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2380,7 +2411,7 @@ name = "which" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2440,15 +2471,15 @@ version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cocoa 0.18.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2464,7 +2495,7 @@ version = "0.1.0" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "embed-resource 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "http_req 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "http_req 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "named_pipe 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2479,7 +2510,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bindgen 0.33.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2513,8 +2544,8 @@ version = "2.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2522,8 +2553,8 @@ name = "xcb" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2566,11 +2597,11 @@ dependencies = [ "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" "checksum arc-swap 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "bc4662175ead9cd84451d5c35070517777949a2ed84551764129cedb88384841" "checksum argon2rs 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" -"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" -"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" -"checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b" -"checksum backtrace 0.3.33 (registry+https://github.com/rust-lang/crates.io-index)" = "88fb679bc9af8fa639198790a77f52d345fe13656c08b43afa9424c206b731c6" -"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" +"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" +"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" +"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" +"checksum backtrace 0.3.30 (registry+https://github.com/rust-lang/crates.io-index)" = "ada4c783bb7e7443c14e0480f429ae2cc99da95065aeab7ee1b81ada0419404f" +"checksum backtrace-sys 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "5b3a000b9c543553af61bc01cbfc403b04b5caa9e421033866f2e98061eb3e61" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum bindgen 0.33.2 (registry+https://github.com/rust-lang/crates.io-index)" = "603ed8d8392ace9581e834e26bd09799bf1e989a79bd1aedbb893e72962bdc6e" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" @@ -2580,7 +2611,7 @@ dependencies = [ "checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" "checksum bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6584aa36f5ad4c9247f5323b0a42f37802b37a836f0ad87084d7a33961abe25f" "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" -"checksum cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "ce400c638d48ee0e9ab75aef7997609ec57367ccfe1463f21bf53c3eca67bf46" +"checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" "checksum cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42aac45e9567d97474a834efdee3081b3c942b2205be932092f53354ce503d6c" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" "checksum cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "55e7ec0b74fe5897894cbc207092c577e87c52f8a59e8ca8d97ef37551f60a49" @@ -2595,13 +2626,13 @@ dependencies = [ "checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" "checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" -"checksum core-text 13.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "12684243b314c95600a2b49628fb775f91d97bbe18424522f665b77014f2a640" +"checksum core-text 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d95a72b5e50e549969dd88eff3047495fe5b8c6f028635442c2b708be707e669" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -"checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" +"checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" "checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" -"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" +"checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" -"checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" +"checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" "checksum deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)" = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4" "checksum derivative 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6073e9676dbebdddeabaeb63e3b7cefd23c86f5c41d381ee1237cc77b1079898" "checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" @@ -2640,7 +2671,7 @@ dependencies = [ "checksum getrandom 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e65cce4e5084b14874c4e7097f38cab54f47ee554f9194673456ea379dcc4c55" "checksum gif 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "86c2f2b597d6e05c86ee5947b2223bda468fe8dad3e88e2a6520869322aaf568" "checksum gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39a23d5e872a275135d66895d954269cf5e8661d234eb1c2480f4ce0d586acbd" -"checksum gleam 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "c8a455b5a3ccd35daeb89fdb8a89ebb0a1fe23c05c7a7f9017840bc3ae176f71" +"checksum gleam 0.6.17 (registry+https://github.com/rust-lang/crates.io-index)" = "7f46fd8874e043ffac0d638ed1567a2584f7814f6d72b4db37ab1689004a26c4" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum glutin 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cb26027a84c3b9e1949ef0df0b6a3db8d0c124243a5c161ea25c7def90cb1474" "checksum glutin_egl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "23f48987ab6cb2b61ad903b59e54a2fd0c380a7baff68cffd6826b69a73dd326" @@ -2650,7 +2681,7 @@ dependencies = [ "checksum glutin_wgl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f801bbc91efc22dd1c4818a47814fc72bf74d024510451b119381579bfa39021" "checksum harfbuzz-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e1042ab0b3e7bc1ff64f7f5935778b644ff2194a1cae5ec52167127d3fd23961" "checksum harfbuzz_rs 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "534c8e9b15d8db6e69654b07dad955f4132757194e7d2bba620d38cf08996088" -"checksum http_req 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b7053e4a7b90e97e3c207f8293dc4a6e7f1d49156470b94f3685f1baa48f65a" +"checksum http_req 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "06dc3c469026e12585cb2b783bbfdd853386907558a5e1b86c4c127d50167806" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum image 0.21.2 (registry+https://github.com/rust-lang/crates.io-index)" = "99198e595d012efccf12abf4abc08da2d97be0b0355a2b08d101347527476ba4" @@ -2664,25 +2695,26 @@ dependencies = [ "checksum khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" +"checksum libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "3262021842bf00fe07dbd6cf34ff25c99d7a7ebef8deea84db72be3ea3bb0aff" "checksum libflate 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)" = "90c6f86f4b0caa347206f916f8b687b51d77c6ef8ff18d52dd007491fd580529" "checksum libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" "checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" "checksum line_drawing 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" -"checksum log 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c275b6ad54070ac2d665eef9197db647b32239c9d244bfb6f041a766d00da5b3" +"checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" "checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" +"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "83f51996a3ed004ef184e16818edc51fadffe8e7ca68be67f9dee67d84d0ff23" "checksum mio-anonymous-pipes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8c274c3c52dcd1d78c5d7ed841eca1e9ea2db8353f3b8ec25789cc62c471aaf" "checksum mio-extras 2.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "46e73a04c2fa6250b8d802134d56d554a9ec2922bf977777c805ea5def61ce40" "checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" +"checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" "checksum named_pipe 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ed10a5ac4f5f7e5d75552b12c1d5d542debca81e573279dd1e4c19fde6efa6d" @@ -2699,12 +2731,13 @@ dependencies = [ "checksum num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" +"checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" "checksum objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "31d20fd2b37e07cf5125be68357b588672e8cefe9a96f8c17a9d46053b3e590d" "checksum objc-foundation 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" "checksum objc_id 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" -"checksum openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)" = "8152bb5a9b5b721538462336e3bef9a539f892715e5037fda0f984577311af15" +"checksum openssl 0.10.23 (registry+https://github.com/rust-lang/crates.io-index)" = "97c140cbb82f3b3468193dd14c1b88def39f341f68257f8a7fe8ed9ed3f628a5" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.48 (registry+https://github.com/rust-lang/crates.io-index)" = "b5ba300217253bcc5dc68bed23d782affa45000193866e025329aa8a7a9f05b8" +"checksum openssl-sys 0.9.47 (registry+https://github.com/rust-lang/crates.io-index)" = "75bdd6dbbb4958d38e47a1d2348847ad1eb4dc205dc5d37473ae504391865acc" "checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" "checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" "checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" @@ -2716,18 +2749,18 @@ dependencies = [ "checksum phf_codegen 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b03e85129e324ad4166b06b2c7491ae27fe3ec353af72e72cd1654c7225d517e" "checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662" "checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" -"checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af" +"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum png 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "63daf481fdd0defa2d1d2be15c674fbfa1b0fd71882c303a91f9a79b3252c359" "checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" -"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" -"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" +"checksum rand_chacha 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e193067942ef6f485a349a113329140d0ab9e2168ce92274499bb0e9a4190d9d" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" "checksum rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615e683324e75af5d43d8f7a39ffe3ee4a9dc42c5c701167a71dc59c3a493aca" @@ -2742,11 +2775,12 @@ dependencies = [ "checksum rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe0df8435ac0c397d467b6cad6d25543d06e8a019ef3f6af3c384597515bd2" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum redox_users 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe5204c3a17e97dde73f285d49be585df59ed84b50a872baf416e73b62c3828" "checksum regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9329abc99e39129fcceabd24cf5d85b4671ef7c29c50e972bc5afe32438ec384" -"checksum regex 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b23da8dfd98a84bd7e08700190a5d9f7d2d38abd4369dd1dae651bc40bfd2cc" +"checksum regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d8297cc20bbb6184f8b45ff61c8ee6a9ac56c156cec8e38c3e5084773c44ad" "checksum regex-syntax 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7d707a4fa2637f2dca2ef9fd02225ec7661fe01a53623c1e6515b6916511f7a7" -"checksum regex-syntax 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "cd5485bf1523a9ed51c4964273f22f63f24e31632adb5dad134f488f86a3875c" +"checksum regex-syntax 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9b01330cce219c1c6b2e209e5ed64ccd587ae5c67bed91c0b49eecf02ae40e21" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum rle-decode-fast 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" @@ -2754,23 +2788,22 @@ dependencies = [ "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rusttype 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "654103d61a05074b268a107cf6581ce120f0fc0115f2610ed9dfea363bb81139" "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" -"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" +"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" "checksum schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum security-framework 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eee63d0f4a9ec776eeb30e220f0bc1e092c3ad744b2a379e3993070364d3adc2" "checksum security-framework-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9636f8989cbf61385ae4824b98c1aaa54c994d7d8b41f11c601ed799f0549a56" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)" = "d46b3dfedb19360a74316866cef04687cd4d6a70df8e6a506c63512790769b72" -"checksum serde_derive 1.0.97 (registry+https://github.com/rust-lang/crates.io-index)" = "c22a0820adfe2f257b098714323563dd06426502abbbce4f51b72ef544c5027f" +"checksum serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "076a696fdea89c19d3baed462576b8f6d663064414b5c793642da8dfeb99475b" +"checksum serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "ef45eb79d6463b22f5f9e16d283798b7c0175ba6050bc25c1a946c122727fe7b" "checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" "checksum serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "38b08a9a90e5260fe01c6480ec7c811606df6d3a660415808c3c3fa8ed95b582" "checksum servo-fontconfig 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a088f8d775a5c5314aae09bd77340bc9c67d72b9a45258be34c83548b4814cd9" "checksum servo-fontconfig-sys 4.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b46d201addcfbd25c1798ad1281d98c40743824e0b0f1e611bd3d5d0d31a7b8d" "checksum shared_library 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" -"checksum signal-hook 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4f61c4d59f3aaa9f61bba6450a9b80ba48362fd7d651689e7a10c453b1f6dc68" +"checksum signal-hook 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "72ab58f1fda436857e6337dcb6a5aaa34f16c5ddc87b3a8b6ef7a212f90b9c5a" "checksum signal-hook-registry 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cded4ffa32146722ec54ab1f16320568465aa922aa9ab4708129599740da85d7" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" @@ -2778,24 +2811,25 @@ dependencies = [ "checksum smithay-client-toolkit 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2ccb8c57049b2a34d2cc2b203fa785020ba0129d31920ef0d317430adaf748fa" "checksum smithay-client-toolkit 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8c23093ebdaac1ad67558c7aef153522c6b3be7e0257820909e3a25c4519e78d" "checksum smithay-clipboard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d8e61f8a9ef7a15bd38c29b4a39d423776b2d6092723dc7df3f0545ce7a4751e" -"checksum socket2 0.3.10 (registry+https://github.com/rust-lang/crates.io-index)" = "df028e0e632c2a1823d920ad74895e7f9128e6438cbc4bc6fd1f180e644767b9" +"checksum socket2 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "4e626972d3593207547f14bf5fc9efa4d0e7283deb73fef1dff313dae9ab8878" "checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" "checksum spsc-buffer 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be6c3f39c37a4283ee4b43d1311c828f2e1fb0541e76ea0cb1a2abd9ef2f5b3b" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4f8de36da215253eb5f24020bfaa0646613b48bf7ebe36cdfa37c3b3b33b241" "checksum stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "69b7df505db8e81d54ff8be4693421e5b543e08214bd8d99eb761fcb4d5668ba" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" -"checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e" +"checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c" "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" "checksum terminfo 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8e51065bafd2abe106b6036483b69d1741f4a1ec56ce8a2378de341637de689e" +"checksum termion 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a8fb22f7cde82c8220e5aeacb3258ed7ce996142c77cba193f203515e26c330" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum tiff 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4834f28a0330cb9f3f2c87d2649dca723cb33802e2bdcf18da32759fbec7ce" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" -"checksum ucd-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fa9b3b49edd3468c0e6565d85783f51af95212b6fa3986a5500954f00b460874" +"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a84e5511b2a947f3ae965dcb29b13b7b1691b6e7332cf5dbc1744138d5acb7f6" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" @@ -2811,7 +2845,7 @@ dependencies = [ "checksum vswhom 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" "checksum vswhom-sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc2f5402d3d0e79a069714f7b48e3ecc60be7775a2c049cb839457457a239532" "checksum vte 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f42f536e22f7fcbb407639765c8fd78707a33109301f834a594758bedd6e8cf" -"checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" +"checksum walkdir 2.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c7904a7e2bb3cdf0cf5e783f44204a85a37a93151738fa349f06680f59a98b45" "checksum wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "49963e5f9eeaf637bfcd1b9f0701c99fd5cd05225eb51035550d4272806f2713" "checksum wayland-client 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)" = "80d909ba6616dd772686f421e111f039402a13acecf395bcb4fc665277a504e0" "checksum wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "40c08896768b667e1df195d88a62a53a2d1351a1ed96188be79c196b35bb32ec" diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index 6caa646c8a..671eec3f34 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -56,7 +56,7 @@ image = "0.21.0" objc = "0.2.2" [features] -default = ["hb-ft"] +default = [] # Enabling this feature makes shaders automatically reload when changed live-shader-reload = [] nightly = [] From f1b5388de181f744986abf1405199176510d9134 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 25 Jul 2019 18:57:35 -0700 Subject: [PATCH 28/84] Fix build error in cursor.rs when hb-ft feautre is off --- alacritty_terminal/Cargo.toml | 2 +- alacritty_terminal/src/cursor.rs | 2 +- output | 0 3 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 output diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index 671eec3f34..6caa646c8a 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -56,7 +56,7 @@ image = "0.21.0" objc = "0.2.2" [features] -default = [] +default = ["hb-ft"] # Enabling this feature makes shaders automatically reload when changed live-shader-reload = [] nightly = [] diff --git a/alacritty_terminal/src/cursor.rs b/alacritty_terminal/src/cursor.rs index b820f757bb..ce2c99ca1c 100644 --- a/alacritty_terminal/src/cursor.rs +++ b/alacritty_terminal/src/cursor.rs @@ -57,7 +57,7 @@ pub fn get_cursor_glyph( } #[cfg(not(feature = "hb-ft"))] -const CUSOR: char = ' '; +const CURSOR: char = ' '; #[cfg(feature = "hb-ft")] const CURSOR: u32 = 0; diff --git a/output b/output deleted file mode 100644 index e69de29bb2..0000000000 From b3b8a40cfee5e398b1ea1c734346fb014682c9a9 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 25 Jul 2019 19:01:28 -0700 Subject: [PATCH 29/84] Update Cargo.lock to 0.2.60 inline with main repo lock file --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 145c1af6d5..d273f1ae47 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -702,7 +702,7 @@ name = "freetype" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "servo-freetype-sys 4.0.3", ] From 244a3a691708916d9f953a0a23024b1e3306cb45 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 25 Jul 2019 19:05:42 -0700 Subject: [PATCH 30/84] Remove hb-ft from default feature list in alacritty_terminal --- alacritty_terminal/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index c7ce4f8924..066790e0e1 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -56,7 +56,7 @@ image = "0.21.0" objc = "0.2.2" [features] -default = ["hb-ft"] +default = [] # Enabling this feature makes shaders automatically reload when changed live-shader-reload = [] nightly = [] From 84c62e7a76bad7142eb2c3a3c29afa2a11bd568e Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Fri, 26 Jul 2019 17:00:28 -0700 Subject: [PATCH 31/84] Resolve bug with strikethrough and underlining. Add logic in TextRunIter to account for WIDE_CHAR when building run. --- alacritty_terminal/src/renderer/rects.rs | 16 ++++++------ alacritty_terminal/src/term/mod.rs | 32 ++++++++++++++++-------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index e5a72961e3..7891e2fa66 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -65,6 +65,7 @@ impl<'a> Rects<'a> { &self.inner } + /// Update the stored lines with the next text_run info. #[cfg(feature = "hb-ft")] pub fn update_lines_text_run(&mut self, size_info: &SizeInfo, text_run: &TextRun) { for line in self.active_lines.iter_mut() { @@ -77,33 +78,33 @@ impl<'a> Rects<'a> { { if size_info.cols() == text_run.run.1 && size_info.lines() == text_run.line { self.inner.push(create_rect( - &text_run.start_cell(), - (&text_run.last_cell()).into(), + &start, + text_run.last_point(), line.flag, &self.metrics, &self.size)); } else { - *end = (&text_run.last_cell()).into(); + *end = text_run.last_point() } continue; } self.inner.push(create_rect( - &text_run.start_cell(), - (&text_run.last_cell()).into(), + start, + *end, line.flag, &self.metrics, &self.size)); if text_run.flags.contains(line.flag) { *start = text_run.start_cell(); - *end = (&text_run.last_cell()).into(); + *end = text_run.last_point(); } else { line.range = None; } }, None => { if text_run.flags.contains(line.flag) { - line.range = Some((text_run.start_cell(), (&text_run.last_cell()).into())); + line.range = Some((text_run.start_cell(), text_run.last_point())); } } } @@ -111,6 +112,7 @@ impl<'a> Rects<'a> { } /// Update the stored lines with the next cell info. + #[cfg(not(feature = "hb-ft"))] pub fn update_lines(&mut self, size_info: &SizeInfo, cell: &RenderableCell) { for line in self.active_lines.iter_mut() { match line.range { diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 1ecabbbe2c..1dd7e3f796 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -427,10 +427,11 @@ pub mod text_run { use super::{ cell::{Flags, MAX_ZEROWIDTH_CHARS}, color::Rgb, - CursorKey, RenderableCell, RenderableCellContent, + CursorKey, RenderableCell, RenderableCellContent, Point }; use crate::index::{Column, Line}; + #[derive(Debug)] pub(crate) struct RunStart { pub line: Line, pub column: Column, @@ -462,9 +463,12 @@ pub mod text_run { && a.flags == b.flags } + + type Latest = (Column, bool); /// Checks two columns are adjacent - fn is_contiguous_col(a: Column, b: Column) -> bool { - a.0 + 1 == b.0 || b.0 + 1 == a.0 + fn is_contiguous_col((a, is_wide): Latest, b: Column) -> bool { + let span = if is_wide { 2usize } else { 1usize }; + a + span == b || b + span == a } #[derive(Debug)] @@ -473,10 +477,10 @@ pub mod text_run { CharRun(String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), } - #[derive(Debug)] /// Represents a set of renderable cells that all share the rendering propreties. /// The assumption is that if two cells are in the same TextRun they can be sent off together to be shaped by harfbuzz. /// This allows for ligatures to be rendered but not when something breaks up a ligature (e.g. selection hightlight) which is intended behavior. + #[derive(Debug)] pub struct TextRun { // By definition a run is on one line. pub line: Line, @@ -491,12 +495,12 @@ pub mod text_run { // These two constructors are used by TextRunIter and are not widely applicable pub(crate) fn from_iter_state( start: RunStart, - latest: Column, + latest: Latest, buffer: (String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), ) -> Self { TextRun { line: start.line, - run: (start.column, latest), + run: (start.column, latest.0), run_chars: TextRunContent::CharRun(buffer.0, buffer.1), fg: start.fg, bg: start.bg, @@ -538,6 +542,14 @@ pub mod text_run { self.cell_at(self.run.1) } + /// Last point covered by this TextRun + pub fn last_point(&self) -> Point { + Point { + line: self.line, + col: self.run.1, + } + } + /// Returns iterator over range of columns [run.0, run.1] pub fn col_iter(&self) -> impl Iterator { let (start, end) = self.run; @@ -556,7 +568,7 @@ pub mod text_run { pub struct TextRunIter { iter: I, run_start: Option, - latest: Option, + latest: Option, cursor: Option, buffer_text: String, buffer_zero_width: Vec<[char; MAX_ZEROWIDTH_CHARS]>, @@ -607,9 +619,9 @@ pub mod text_run { } /// Handles bookkeeping needed when starting a new run - fn start_run(&mut self, rc: RenderableCell) -> (Option, Option) { + fn start_run(&mut self, rc: RenderableCell) -> (Option, Option) { self.buffer_content(rc.inner); - let latest = self.latest.replace(rc.column); + let latest = self.latest.replace((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); let start = self.run_start.replace(from_rc!(rc)); (start, latest) } @@ -652,7 +664,7 @@ pub mod text_run { break; } // Build up buffer and track the latest column we've seen - self.latest = Some(rc.column); + self.latest = Some((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); self.buffer_content(rc.inner); } // If we generated output we're done From 6447ef8d1aef894257a3faf8b6d6756ef2c622f3 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Fri, 26 Jul 2019 18:29:50 -0700 Subject: [PATCH 32/84] Able to fallback to loading a font if shaping fails to find a glyph. However cursor rendering is now broken and WIDE_CHAR's are not rendered correctly. --- alacritty_terminal/Cargo.toml | 2 +- alacritty_terminal/src/cursor.rs | 2 +- alacritty_terminal/src/renderer/mod.rs | 43 +++++++++++++++++++------- font/src/ft/mod.rs | 24 +++++++++++--- font/src/lib.rs | 37 ++++++++++++++++++++-- 5 files changed, 87 insertions(+), 21 deletions(-) diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index 066790e0e1..c7ce4f8924 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -56,7 +56,7 @@ image = "0.21.0" objc = "0.2.2" [features] -default = [] +default = ["hb-ft"] # Enabling this feature makes shaders automatically reload when changed live-shader-reload = [] nightly = [] diff --git a/alacritty_terminal/src/cursor.rs b/alacritty_terminal/src/cursor.rs index ce2c99ca1c..5b11fb2d57 100644 --- a/alacritty_terminal/src/cursor.rs +++ b/alacritty_terminal/src/cursor.rs @@ -59,7 +59,7 @@ pub fn get_cursor_glyph( #[cfg(not(feature = "hb-ft"))] const CURSOR: char = ' '; #[cfg(feature = "hb-ft")] -const CURSOR: u32 = 0; +const CURSOR: font::key_type::KeyType = font::key_type::KeyType::Fallback(' '); // Returns a custom underline cursor character pub fn get_underline_cursor_glyph(width: i32, line_width: i32) -> RasterizedGlyph { diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 4cf61e8954..1b09765568 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -203,7 +203,7 @@ impl GlyphCache { // The glyph requested here (1 at the time of writing) has no special // meaning. #[cfg(feature = "hb-ft")] - rasterizer.get_glyph(GlyphKey { font_key: regular, c: 1, size: font.size })?; + rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm'.into(), size: font.size })?; let metrics = rasterizer.metrics(regular, font.size)?; @@ -234,7 +234,7 @@ impl GlyphCache { } #[cfg(feature = "hb-ft")] for c in 32u32..=128u32 { - self.get(GlyphKey { font_key: font, c, size }, loader); + self.get(GlyphKey { font_key: font, c: c.into(), size }, loader); } } @@ -312,11 +312,31 @@ impl GlyphCache { self.rasterizer.shape(text_run, font_key) .get_glyph_infos() .iter() - .map(move |glyph_info| *self.get(GlyphKey { - c: glyph_info.codepoint, - font_key, - size: self.font_size, - }, loader)) + .map(|glyph_info| { + let codepoint = glyph_info.codepoint; + // Codepoint of 0 indicates a missing or undefined glyph + let c = if codepoint == 0 { + println!("text_run: {:?}, cluster: {:?}", text_run.char_indices().collect::>(), glyph_info.cluster); + // TODO: this is a linear scan over text for each missing glyph + // Try to find all missing glyphs first and only scan over text_run once. + text_run.char_indices() + .find_map(|(i, c)| if i == (glyph_info.cluster as usize) { + Some(c) + } else { + None + }) + .expect("Harfbuzz cluster to be index of character within text run") + .into() + } else { + codepoint.into() + }; + let glyph_key = GlyphKey { + c, + font_key, + size: self.font_size, + }; + *self.get(glyph_key, loader) + }) .collect() } @@ -361,7 +381,7 @@ impl GlyphCache { #[cfg(not(feature = "hb-ft"))] self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?; #[cfg(feature = "hb-ft")] - self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 1u32, size: font.size })?; + self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 1u32.into(), size: font.size })?; let metrics = self.rasterizer.metrics(regular, size)?; info!("Font size changed to {:?} with DPR of {}", font.size, dpr); @@ -392,7 +412,7 @@ impl GlyphCache { #[cfg(not(feature = "hb-ft"))] rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?; #[cfg(feature = "hb-ft")] - rasterizer.get_glyph(GlyphKey { font_key: regular, c: 1u32, size: font.size })?; + rasterizer.get_glyph(GlyphKey { font_key: regular, c: 1u32.into(), size: font.size })?; rasterizer.metrics(regular, font.size) } @@ -1065,6 +1085,7 @@ impl<'a> RenderApi<'a> { pub fn render_text_run(&mut self, text_run: TextRun, glyph_cache: &mut GlyphCache) { match &text_run.run_chars { TextRunContent::Cursor(cursor_key) => { + println!("Rendered cursor"); // 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(|| { @@ -1102,10 +1123,8 @@ impl<'a> RenderApi<'a> { for (cell, chars) in text_run.cell_iter().zip(zero_widths.iter()) { for c in chars.iter().filter(|c| **c != ' ') { - // Expect this to be a very small number of calls so accept eating the cost of indexing. - let index = glyph_cache.index_for_char(font_key, *c).expect("font_key to be present but there was nothing."); let glyph_key = - GlyphKey { font_key, size: glyph_cache.font_size, c: index}; + GlyphKey { font_key, size: glyph_cache.font_size, c: (*c).into() }; let average_advance = glyph_cache.metrics.average_advance as f32; let mut glyph = *glyph_cache.get(glyph_key, self); diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 5ab0aaae2b..34b9b61d22 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -27,6 +27,8 @@ use libc::c_uint; pub mod fc; use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; +#[cfg(feature = "hb-ft")] +use super::key_type::KeyType; struct FixedSize { pixelsize: f64, @@ -329,9 +331,21 @@ impl FreeTypeRasterizer { fn face_for_glyph( &mut self, glyph_key: GlyphKey, - _have_recursed: bool, + have_recursed: bool, ) -> Result { - Ok(glyph_key.font_key) + match glyph_key.c { + // We already found a glyph index, use current font + KeyType::GlyphIndex(_) => Ok(glyph_key.font_key), + // Harfbuzz failed to find a glyph index, try to load a font for c + KeyType::Fallback(c) => { + if have_recursed { + Ok(glyph_key.font_key) + } else { + let key = self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key); + Ok(key) + } + } + } } #[cfg(not(feature = "hb-ft"))] @@ -364,7 +378,10 @@ impl FreeTypeRasterizer { #[cfg(not(feature = "hb-ft"))] let index = face.ft_face.get_char_index(glyph_key.c as usize); #[cfg(feature = "hb-ft")] - let index = glyph_key.c; + let index = match glyph_key.c { + KeyType::GlyphIndex(i) => i, + KeyType::Fallback(c) => face.ft_face.get_char_index(c as usize), + }; self.get_rendered_glyph_index(face, glyph_key, index) } @@ -550,7 +567,6 @@ impl FreeTypeRasterizer { } } - #[cfg(not(feature = "hb-ft"))] fn load_face_with_glyph(&mut self, glyph: char) -> Result { let mut charset = fc::CharSet::new(); charset.add(glyph); diff --git a/font/src/lib.rs b/font/src/lib.rs index 9380556e20..813c61c12e 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -53,6 +53,7 @@ extern crate harfbuzz_rs; #[cfg_attr(not(windows), macro_use)] extern crate log; +#[cfg(not(feature = "hb-ft"))] use std::hash::{Hash, Hasher}; use std::sync::atomic::{AtomicUsize, Ordering}; use std::fmt; @@ -143,16 +144,45 @@ impl FontKey { } } +#[cfg(feature = "hb-ft")] +pub mod key_type { + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] + pub enum KeyType { + // Harfbuzz returned a valid index from shaping and we can render that as expected. + GlyphIndex(u32), + // Harfbuzz returned nodef so we provide character instead to make use of font loading. + Fallback(char), + } + // Import enum variants into this module + use KeyType::{Fallback, GlyphIndex}; + + impl From for KeyType { + fn from(val: u32) -> Self { + GlyphIndex(val) + } + } + impl From for KeyType { + fn from(val: char) -> Self { + Fallback(val) + } + } +} +#[cfg(feature = "hb-ft")] +use key_type::KeyType; + +// For now use derived impls, this can be swapped for more efficient implementations once things are working +#[cfg_attr(feature = "hb-ft", derive(Hash, PartialEq))] #[derive(Debug, Copy, Clone, Eq)] pub struct GlyphKey { #[cfg(not(feature = "hb-ft"))] pub c: char, #[cfg(feature = "hb-ft")] - pub c: u32, + pub c: KeyType, pub font_key: FontKey, pub size: Size, } +#[cfg(not(feature = "hb-ft"))] impl Hash for GlyphKey { fn hash(&self, state: &mut H) { unsafe { @@ -166,6 +196,7 @@ impl Hash for GlyphKey { } } +#[cfg(not(feature = "hb-ft"))] impl PartialEq for GlyphKey { fn eq(&self, other: &Self) -> bool { unsafe { @@ -214,7 +245,7 @@ pub struct RasterizedGlyph { #[cfg(not(feature = "hb-ft"))] pub c: char, #[cfg(feature = "hb-ft")] - pub c: u32, + pub c: KeyType, pub width: i32, pub height: i32, pub top: i32, @@ -225,7 +256,7 @@ pub struct RasterizedGlyph { impl Default for RasterizedGlyph { fn default() -> RasterizedGlyph { #[cfg(feature = "hb-ft")] - let c = 1u32; + let c: KeyType = 1u32.into(); #[cfg(not(feature = "hb-ft"))] let c = ' '; RasterizedGlyph { c, width: 0, height: 0, top: 0, left: 0, buf: Vec::new() } From 5f53cc408c83167050b7c19b146d88b933179eff Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Fri, 26 Jul 2019 19:04:16 -0700 Subject: [PATCH 33/84] Resolved issue with cursor not rendering, need to load an explicit glyph index. Loading a char now unconditionally hits fallback logic which won't do what's needed to set metrics for our font. --- alacritty_terminal/src/cursor.rs | 3 +-- alacritty_terminal/src/renderer/mod.rs | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/alacritty_terminal/src/cursor.rs b/alacritty_terminal/src/cursor.rs index 5b11fb2d57..72b727a8fc 100644 --- a/alacritty_terminal/src/cursor.rs +++ b/alacritty_terminal/src/cursor.rs @@ -41,7 +41,6 @@ pub fn get_cursor_glyph( 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; @@ -59,7 +58,7 @@ pub fn get_cursor_glyph( #[cfg(not(feature = "hb-ft"))] const CURSOR: char = ' '; #[cfg(feature = "hb-ft")] -const CURSOR: font::key_type::KeyType = font::key_type::KeyType::Fallback(' '); +const CURSOR: font::key_type::KeyType = font::key_type::KeyType::GlyphIndex(1u32); // Returns a custom underline cursor character pub fn get_underline_cursor_glyph(width: i32, line_width: i32) -> RasterizedGlyph { diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 1b09765568..6f293cae77 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -203,7 +203,7 @@ impl GlyphCache { // The glyph requested here (1 at the time of writing) has no special // meaning. #[cfg(feature = "hb-ft")] - rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm'.into(), size: font.size })?; + rasterizer.get_glyph(GlyphKey { font_key: regular, c: 1u32.into(), size: font.size })?; let metrics = rasterizer.metrics(regular, font.size)?; @@ -233,8 +233,8 @@ impl GlyphCache { self.get(GlyphKey { font_key: font, c: i as char, size }, loader); } #[cfg(feature = "hb-ft")] - for c in 32u32..=128u32 { - self.get(GlyphKey { font_key: font, c: c.into(), size }, loader); + for c in 32u8..=128u8 { + self.get(GlyphKey { font_key: font, c: font::key_type::KeyType::GlyphIndex(c as u32), size }, loader); } } @@ -1085,7 +1085,6 @@ impl<'a> RenderApi<'a> { pub fn render_text_run(&mut self, text_run: TextRun, glyph_cache: &mut GlyphCache) { match &text_run.run_chars { TextRunContent::Cursor(cursor_key) => { - println!("Rendered cursor"); // 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(|| { From 70024dbcf397c20d19d35f8f9522ba60fed83299 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Fri, 26 Jul 2019 19:14:58 -0700 Subject: [PATCH 34/84] Wide characters now render properly. Last item is to look at cleaning up fallback character code post-shaping. --- alacritty_terminal/src/renderer/mod.rs | 1 - alacritty_terminal/src/term/mod.rs | 19 ++++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 6f293cae77..a2868626dd 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -316,7 +316,6 @@ impl GlyphCache { let codepoint = glyph_info.codepoint; // Codepoint of 0 indicates a missing or undefined glyph let c = if codepoint == 0 { - println!("text_run: {:?}, cluster: {:?}", text_run.char_indices().collect::>(), glyph_info.cluster); // TODO: this is a linear scan over text for each missing glyph // Try to find all missing glyphs first and only scan over text_run once. text_run.char_indices() diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 1dd7e3f796..34ebc423e7 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -495,12 +495,17 @@ pub mod text_run { // These two constructors are used by TextRunIter and are not widely applicable pub(crate) fn from_iter_state( start: RunStart, - latest: Latest, + (latest, is_wide): Latest, buffer: (String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), ) -> Self { + let end_column = if is_wide { + latest + 1 + } else { + latest + }; TextRun { line: start.line, - run: (start.column, latest.0), + run: (start.column, end_column), run_chars: TextRunContent::CharRun(buffer.0, buffer.1), fg: start.fg, bg: start.bg, @@ -553,11 +558,19 @@ pub mod text_run { /// Returns iterator over range of columns [run.0, run.1] pub fn col_iter(&self) -> impl Iterator { let (start, end) = self.run; + let step = if self.flags.contains(Flags::WIDE_CHAR) { + // If our run contains wide chars treat each cell like it's 2 cells wide + 2 + } else { + 1 + }; // unpacking is neccessary while Step trait is nightly // hopefully this compiles away. - (start.0..=end.0).map(Column) + (start.0..=end.0).step_by(step).map(Column) } + + /// Iterates over each RenderableCell in column range [run.0, run.1] pub fn cell_iter<'a>(&'a self) -> impl Iterator + 'a { self.col_iter().map(move |col| self.cell_at(col)) From 2935bf4f74e524bb78cafdf5d74b650dc76b9705 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Sun, 28 Jul 2019 14:18:13 -0700 Subject: [PATCH 35/84] Resolve clippy error, remove hb-ft feature from default list to hopefully fix builds on macosx/windwos --- alacritty_terminal/Cargo.toml | 2 +- alacritty_terminal/src/renderer/mod.rs | 66 +++++++++++++------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index c7ce4f8924..066790e0e1 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -56,7 +56,7 @@ image = "0.21.0" objc = "0.2.2" [features] -default = ["hb-ft"] +default = [] # Enabling this feature makes shaders automatically reload when changed live-shader-reload = [] nightly = [] diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index a2868626dd..b15bda96d2 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -35,9 +35,9 @@ use crate::renderer::rects::{Rect, Rects}; use crate::term::color::Rgb; #[cfg(feature = "hb-ft")] use crate::term::text_run::{TextRun, TextRunContent}; -use crate::term::{self, cell, RenderableCell}; #[cfg(not(feature = "hb-ft"))] use crate::term::RenderableCellContent; +use crate::term::{self, cell, RenderableCell}; pub mod rects; @@ -96,7 +96,7 @@ impl ::std::fmt::Display for Error { match *self { Error::ShaderCreation(ref err) => { write!(f, "There was an error initializing the shaders: {}", err) - }, + } } } } @@ -233,8 +233,11 @@ impl GlyphCache { self.get(GlyphKey { font_key: font, c: i as char, size }, loader); } #[cfg(feature = "hb-ft")] - for c in 32u8..=128u8 { - self.get(GlyphKey { font_key: font, c: font::key_type::KeyType::GlyphIndex(c as u32), size }, loader); + for i in 32u32..=128u32 { + self.get( + GlyphKey { font_key: font, c: font::key_type::KeyType::GlyphIndex(i), size }, + loader, + ); } } @@ -308,32 +311,27 @@ impl GlyphCache { where L: LoadGlyph, { - use font::{HbFtExt}; - self.rasterizer.shape(text_run, font_key) + use font::HbFtExt; + self.rasterizer + .shape(text_run, font_key) .get_glyph_infos() .iter() - .map(|glyph_info| { + .map(move |glyph_info| { let codepoint = glyph_info.codepoint; // Codepoint of 0 indicates a missing or undefined glyph - let c = if codepoint == 0 { + let c: font::key_type::KeyType = if codepoint == 0 { // TODO: this is a linear scan over text for each missing glyph // Try to find all missing glyphs first and only scan over text_run once. - text_run.char_indices() - .find_map(|(i, c)| if i == (glyph_info.cluster as usize) { - Some(c) - } else { - None - }) - .expect("Harfbuzz cluster to be index of character within text run") - .into() + text_run.char_indices().find_map(|(i, c)| if i == glyph_info.cluster as usize { + Some (c) + } else { + None + }).unwrap_or_else(|| panic!("Could not find cluster {} in run {}", glyph_info.cluster, text_run)) + .into() } else { codepoint.into() }; - let glyph_key = GlyphKey { - c, - font_key, - size: self.font_size, - }; + let glyph_key = GlyphKey { c, font_key, size: self.font_size }; *self.get(glyph_key, loader) }) .collect() @@ -380,7 +378,11 @@ impl GlyphCache { #[cfg(not(feature = "hb-ft"))] self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?; #[cfg(feature = "hb-ft")] - self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 1u32.into(), size: font.size })?; + self.rasterizer.get_glyph(GlyphKey { + font_key: regular, + c: 1u32.into(), + size: font.size, + })?; let metrics = self.rasterizer.metrics(regular, size)?; info!("Font size changed to {:?} with DPR of {}", font.size, dpr); @@ -712,8 +714,8 @@ impl QuadRenderer { | DebouncedEvent::Write(_) | DebouncedEvent::Chmod(_) => { msg_tx.send(Msg::ShaderReload).expect("msg send ok"); - }, - _ => {}, + } + _ => {} } } }); @@ -878,11 +880,11 @@ impl QuadRenderer { info!("... successfully reloaded shaders"); (program, rect_program) - }, + } (Err(err), _) | (_, Err(err)) => { error!("{}", err); return; - }, + } }; self.active_tex = 0; @@ -1060,7 +1062,7 @@ impl<'a> RenderApi<'a> { fg: Rgb { r: 0, g: 0, b: 0 }, bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0 }), flags: cell::Flags::empty(), - bg_alpha + bg_alpha, }; self.render_text_run(text_run, glyph_cache); @@ -1111,14 +1113,12 @@ impl<'a> RenderApi<'a> { let hidden = text_run.flags.contains(cell::Flags::HIDDEN); if !hidden { - let glyphs = glyph_cache - .shape_run(&run, font_key, self); + let glyphs = glyph_cache.shape_run(&run, font_key, self); for (cell, glyph) in text_run.cell_iter().zip(glyphs.into_iter()) { self.add_render_item(&cell, &glyph); } }; - for (cell, chars) in text_run.cell_iter().zip(zero_widths.iter()) { for c in chars.iter().filter(|c| **c != ' ') { let glyph_key = @@ -1160,7 +1160,7 @@ impl<'a> RenderApi<'a> { }); self.add_render_item(&cell, &glyph); return; - }, + } RenderableCellContent::Chars(chars) => chars, }; @@ -1231,7 +1231,7 @@ fn load_glyph( atlas.push(new); } load_glyph(active_tex, atlas, current_atlas, rasterized) - }, + } Err(AtlasInsertError::GlyphTooLarge) => Glyph { tex_id: atlas[*current_atlas].id, top: 0.0, @@ -1581,7 +1581,7 @@ impl ::std::fmt::Display for ShaderCreationError { ShaderCreationError::Io(ref err) => write!(f, "Couldn't read shader: {}", err), ShaderCreationError::Compile(ref path, ref log) => { write!(f, "Failed compiling shader at {}: {}", path.display(), log) - }, + } ShaderCreationError::Link(ref log) => write!(f, "Failed linking shader: {}", log), } } From d2076aeb2d217fa8de1869a0ac3a520dfcb68eea Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 31 Jul 2019 16:06:25 -0700 Subject: [PATCH 36/84] Add use_font_ligatures conifg option, toggles clig and liga harfbuzz features --- alacritty_terminal/src/config/font.rs | 31 ++++++++++++++++++++++- alacritty_terminal/src/display.rs | 3 +++ alacritty_terminal/src/renderer/mod.rs | 3 +++ font/src/ft/mod.rs | 35 +++++++++++++++++++++----- font/src/lib.rs | 15 +++++++++++ 5 files changed, 80 insertions(+), 7 deletions(-) diff --git a/alacritty_terminal/src/config/font.rs b/alacritty_terminal/src/config/font.rs index 3c78ad29da..408e999758 100644 --- a/alacritty_terminal/src/config/font.rs +++ b/alacritty_terminal/src/config/font.rs @@ -1,10 +1,12 @@ use std::fmt; +#[cfg(feature = "hb-ft")] +use font::RasterizeConfig; use font::Size; use serde::de::Visitor; use serde::{Deserialize, Deserializer}; -#[cfg(target_os = "macos")] +#[cfg(any(target_os = "macos", feature = "hb-ft"))] use crate::config::DefaultTrueBool; use crate::config::{failure_default, Delta}; @@ -44,6 +46,11 @@ pub struct Font { #[cfg(target_os = "macos")] #[serde(deserialize_with = "failure_default")] use_thin_strokes: DefaultTrueBool, + + /// Toggles rendering of font ligatures + #[cfg(feature = "hb-ft")] + #[serde(deserialize_with = "failure_default")] + use_font_ligatures: DefaultTrueBool, } impl Default for Font { @@ -57,6 +64,8 @@ impl Default for Font { offset: Default::default(), #[cfg(target_os = "macos")] use_thin_strokes: Default::default(), + #[cfg(feature = "hb-ft")] + use_font_ligatures: Default::default(), } } } @@ -91,6 +100,26 @@ impl Font { pub fn use_thin_strokes(&self) -> bool { false } + + #[cfg(feature = "hb-ft")] + pub fn use_font_ligatures(&self) -> bool { + self.use_font_ligatures.0 + } + + #[cfg(not(feature = "hb-ft"))] + pub fn use_font_ligatures(&self) -> bool { + false + } +} + +#[cfg(feature = "hb-ft")] +impl<'a> Into for &'a Font { + fn into(self) -> RasterizeConfig { + RasterizeConfig { + use_thin_strokes: self.use_thin_strokes(), + use_font_ligatures: self.use_font_ligatures(), + } + } } fn default_font_size() -> Size { diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index d4c3124a4e..be869999ee 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -301,7 +301,10 @@ impl Display { config: &Config, ) -> Result<(GlyphCache, f32, f32), Error> { let font = config.font.clone(); + #[cfg(not(feature = "hb-ft"))] let rasterizer = font::Rasterizer::new(dpr as f32, config.font.use_thin_strokes())?; + #[cfg(feature = "hb-ft")] + let rasterizer = font::Rasterizer::new(dpr as f32, (&config.font).into())?; // Initialize glyph cache let glyph_cache = { diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index b15bda96d2..eb4e461735 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -406,7 +406,10 @@ impl GlyphCache { pub fn static_metrics(config: &Config, dpr: f32) -> Result { let font = config.font.clone(); + #[cfg(not(feature = "hb-ft"))] let mut rasterizer = font::Rasterizer::new(dpr, config.font.use_thin_strokes())?; + #[cfg(feature = "hb-ft")] + let mut rasterizer = font::Rasterizer::new(dpr, (&config.font).into())?; let regular_desc = GlyphCache::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal); let regular = rasterizer.load_font(®ular_desc, font.size)?; diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 34b9b61d22..5aa09cfc8b 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -28,7 +28,7 @@ pub mod fc; use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; #[cfg(feature = "hb-ft")] -use super::key_type::KeyType; +use super::{key_type::KeyType, RasterizeConfig}; struct FixedSize { pixelsize: f64, @@ -71,6 +71,8 @@ pub struct FreeTypeRasterizer { library: Library, keys: HashMap, device_pixel_ratio: f32, + #[cfg(feature = "hb-ft")] + use_font_ligatures: bool, } #[inline] @@ -81,6 +83,7 @@ fn to_freetype_26_6(f: f32) -> isize { impl ::Rasterize for FreeTypeRasterizer { type Err = Error; + #[cfg(not(feature = "hb-ft"))] fn new(device_pixel_ratio: f32, _: bool) -> Result { let library = Library::init()?; @@ -92,6 +95,19 @@ impl ::Rasterize for FreeTypeRasterizer { }) } + #[cfg(feature = "hb-ft")] + fn new(device_pixel_ratio: f32, config: RasterizeConfig) -> Result { + let library = Library::init()?; + + Ok(FreeTypeRasterizer { + faces: HashMap::new(), + keys: HashMap::new(), + library, + device_pixel_ratio, + use_font_ligatures: config.use_font_ligatures, + }) + } + fn metrics(&self, key: FontKey, _size: Size) -> Result { let face = self.faces.get(&key).ok_or(Error::FontNotLoaded)?; let full = self.full_metrics(key)?; @@ -157,14 +173,21 @@ impl ::HbFtExt for FreeTypeRasterizer { text: &str, font_key: FontKey, ) -> GlyphBuffer { - use harfbuzz_rs::{shape, UnicodeBuffer}; + use harfbuzz_rs::{shape, UnicodeBuffer, Feature, Tag}; let hb_font = &self.faces[&font_key].hb_font; let buf = UnicodeBuffer::default() - .add_str(text) - .guess_segment_properties(); - + .add_str(text); + let value = if self.use_font_ligatures { 1 } else { 0 }; + let features = &[ + Feature::new(Tag::new('c', 'l', 'i', 'g'), value, 0..), + Feature::new(Tag::new('l', 'i', 'g', 'a'), value, 0..), + // This feature controls ligature rendering in Fira Code but this could + // very well be Fira Code specific behavior as it codes all it's "ligatures" as glyph substitutions + // Which are unaffected by features "clig" and "liga" + // Feature::new(Tag::new('c', 'a', 'l', 't'), value, 0..) + ]; // Shape with default features - shape(&*hb_font, buf, &[]) + shape(&*hb_font, buf, features) } } diff --git a/font/src/lib.rs b/font/src/lib.rs index 813c61c12e..c79527474b 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -300,9 +300,15 @@ pub trait Rasterize { type Err: ::std::error::Error + Send + Sync + 'static; /// Create a new Rasterizer + #[cfg(not(feature = "hb-ft"))] fn new(device_pixel_ratio: f32, use_thin_strokes: bool) -> Result where Self: Sized; + + #[cfg(feature = "hb-ft")] + fn new(device_pixel_ratio: f32, rasterize_config: RasterizeConfig) -> Result + where + Self: Sized; /// Get `Metrics` for the given `FontKey` fn metrics(&self, _: FontKey, _: Size) -> Result; @@ -317,6 +323,15 @@ pub trait Rasterize { fn update_dpr(&mut self, device_pixel_ratio: f32); } +#[cfg(feature = "hb-ft")] +pub struct RasterizeConfig { + /// Toggle thin strokes on mac osx + // Technically this is impossible while under "hb-ft" but is included for compatiblity in the api + pub use_thin_strokes: bool, + /// Toggle rendering of font ligatures + pub use_font_ligatures: bool, +} + #[cfg(feature = "hb-ft")] pub trait HbFtExt { /// Shape the provided text into a set of glyphs. From 3ceaf9c0747f5ddc784cbc50f16543c20b0e1f04 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 6 Aug 2019 17:28:39 -0700 Subject: [PATCH 37/84] Moved text_run mod to it's own folder, fixed update_lines_text_run() method, added utility methods to TextRun struct. Added some docs and formatted changed files. --- alacritty_terminal/src/config/font.rs | 2 +- alacritty_terminal/src/cursor.rs | 1 + alacritty_terminal/src/display.rs | 9 +- alacritty_terminal/src/renderer/mod.rs | 38 +-- alacritty_terminal/src/renderer/rects.rs | 88 +++---- alacritty_terminal/src/term/mod.rs | 278 +-------------------- alacritty_terminal/src/term/text_run.rs | 292 +++++++++++++++++++++++ font/src/ft/mod.rs | 44 ++-- font/src/lib.rs | 24 +- 9 files changed, 400 insertions(+), 376 deletions(-) create mode 100644 alacritty_terminal/src/term/text_run.rs diff --git a/alacritty_terminal/src/config/font.rs b/alacritty_terminal/src/config/font.rs index 408e999758..177ee3d1e6 100644 --- a/alacritty_terminal/src/config/font.rs +++ b/alacritty_terminal/src/config/font.rs @@ -117,7 +117,7 @@ impl<'a> Into for &'a Font { fn into(self) -> RasterizeConfig { RasterizeConfig { use_thin_strokes: self.use_thin_strokes(), - use_font_ligatures: self.use_font_ligatures(), + use_font_ligatures: self.use_font_ligatures(), } } } diff --git a/alacritty_terminal/src/cursor.rs b/alacritty_terminal/src/cursor.rs index 72b727a8fc..6ca0e895be 100644 --- a/alacritty_terminal/src/cursor.rs +++ b/alacritty_terminal/src/cursor.rs @@ -55,6 +55,7 @@ pub fn get_cursor_glyph( } } +// This default is done as a constant to avoid duplicating the feature toggle for each cursor type. #[cfg(not(feature = "hb-ft"))] const CURSOR: char = ' '; #[cfg(feature = "hb-ft")] diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 65795b7caa..af095ea296 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -36,7 +36,7 @@ use crate::window::{self, Window}; use font::{self, Rasterize}; #[cfg(feature = "hb-ft")] -use crate::term::text_run::{TextRunIter}; +use crate::term::text_run::TextRunIter; #[derive(Debug)] pub enum Error { @@ -538,15 +538,16 @@ impl Display { // Draw grid (HarfBuzz) #[cfg(feature = "hb-ft")] { + use crate::term::cell::Flags; let _sampler = self.meter.sampler(); self.renderer.with_api(config, &size_info, |mut api| { // Iterate over each contiguous block of text - for text_run in TextRunIter::new(grid_cells.into_iter()) { + for text_run in TextRunIter::new(grid_cells.into_iter().filter(|rc| !rc.flags.contains(Flags::WIDE_CHAR_SPACER))) { // Update underline/strikeout - rects.update_lines_text_run(&size_info, &text_run); + rects.update_lines_text_run(&text_run, &size_info, &metrics); - // Draw text + // Draw text run api.render_text_run(text_run, glyph_cache); } }); diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index eb4e461735..abdce47a4d 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -96,7 +96,7 @@ impl ::std::fmt::Display for Error { match *self { Error::ShaderCreation(ref err) => { write!(f, "There was an error initializing the shaders: {}", err) - } + }, } } } @@ -322,12 +322,18 @@ impl GlyphCache { let c: font::key_type::KeyType = if codepoint == 0 { // TODO: this is a linear scan over text for each missing glyph // Try to find all missing glyphs first and only scan over text_run once. - text_run.char_indices().find_map(|(i, c)| if i == glyph_info.cluster as usize { - Some (c) - } else { - None - }).unwrap_or_else(|| panic!("Could not find cluster {} in run {}", glyph_info.cluster, text_run)) - .into() + text_run + .char_indices() + .find_map( + |(i, c)| if i == glyph_info.cluster as usize { Some(c) } else { None }, + ) + .unwrap_or_else(|| { + panic!( + "Could not find cluster {} in run {}", + glyph_info.cluster, text_run + ) + }) + .into() } else { codepoint.into() }; @@ -717,8 +723,8 @@ impl QuadRenderer { | DebouncedEvent::Write(_) | DebouncedEvent::Chmod(_) => { msg_tx.send(Msg::ShaderReload).expect("msg send ok"); - } - _ => {} + }, + _ => {}, } } }); @@ -883,11 +889,11 @@ impl QuadRenderer { info!("... successfully reloaded shaders"); (program, rect_program) - } + }, (Err(err), _) | (_, Err(err)) => { error!("{}", err); return; - } + }, }; self.active_tex = 0; @@ -1104,7 +1110,7 @@ impl<'a> RenderApi<'a> { )) }); self.add_render_item(&text_run.start_cell(), &glyph); - } + }, TextRunContent::CharRun(run, zero_widths) => { let font_key = if text_run.flags.contains(cell::Flags::BOLD) { glyph_cache.bold_key @@ -1139,7 +1145,7 @@ impl<'a> RenderApi<'a> { self.add_render_item(&cell, &glyph); } } - } + }, }; } @@ -1163,7 +1169,7 @@ impl<'a> RenderApi<'a> { }); self.add_render_item(&cell, &glyph); return; - } + }, RenderableCellContent::Chars(chars) => chars, }; @@ -1234,7 +1240,7 @@ fn load_glyph( atlas.push(new); } load_glyph(active_tex, atlas, current_atlas, rasterized) - } + }, Err(AtlasInsertError::GlyphTooLarge) => Glyph { tex_id: atlas[*current_atlas].id, top: 0.0, @@ -1584,7 +1590,7 @@ impl ::std::fmt::Display for ShaderCreationError { ShaderCreationError::Io(ref err) => write!(f, "Couldn't read shader: {}", err), ShaderCreationError::Compile(ref path, ref log) => { write!(f, "Failed compiling shader at {}: {}", path.display(), log) - } + }, ShaderCreationError::Link(ref log) => write!(f, "Failed linking shader: {}", log), } } diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index ed41922b63..d36bde430e 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -20,7 +20,9 @@ use crate::term::cell::Flags; use crate::term::color::Rgb; #[cfg(feature = "hb-ft")] use crate::term::text_run::TextRun; -use crate::term::{RenderableCell, SizeInfo}; +#[cfg(not(feature = "hb-ft"))] +use crate::term::RenderableCell; +use crate::term::SizeInfo; #[derive(Debug, Copy, Clone)] pub struct Rect { @@ -43,6 +45,7 @@ struct Line { } impl Line { + #[cfg(not(feature = "hb-ft"))] /// Create a line that starts on the left of `cell` and is one cell wide fn from_cell(cell: &RenderableCell, flag: Flags, metrics: &Metrics, size: &SizeInfo) -> Line { let cell_x = cell.column.0 as f32 * size.cell_width; @@ -70,9 +73,40 @@ impl Line { Self { start: cell.into(), color: cell.fg, rect } } + #[cfg(not(feature = "hb-ft"))] fn update_end(&mut self, end: Point, size: &SizeInfo) { self.rect.width = (end.col + 1 - self.start.col).0 as f32 * size.cell_width; } + + #[cfg(feature = "hb-ft")] + /// Create a line that starts on the left of `text_run` and is `text_run.len()` wide + fn from_text_run(text_run: &TextRun, flag: Flags, metrics: &Metrics, size: &SizeInfo) -> Self { + // This is basically a 1:1 copy of from_cell but with semantics of TextRun + let run_x = text_run.start_col().0 as f32 * size.cell_width; + + 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 text run line drawing specified"), + }; + + // Make sure lines are always visible + height = height.max(1.); + + let run_bottom = (text_run.line.0 as f32 + 1.) * size.cell_height; + let baseline = run_bottom + metrics.descent; + + let mut y = baseline - position - height / 2.; + let max_y = run_bottom - height; + if y > max_y { + y = max_y; + } + + let rect = Rect::new(run_x + size.padding_x, y + size.padding_y, (text_run.len() + 1) as f32 * size.cell_width, height); + + Self { start: text_run.start_point(), color: text_run.fg, rect } + } + } /// Rects for underline, strikeout and more. @@ -96,54 +130,22 @@ impl Rects { .collect() } - /// Update the stored lines with the next text_run info. #[cfg(feature = "hb-ft")] - pub fn update_lines_text_run(&mut self, size_info: &SizeInfo, text_run: &TextRun) { - for line in self.active_lines.iter_mut() { - match line.range { - Some((ref mut start, ref mut end)) => { - if text_run.line == start.line - && text_run.flags.contains(line.flag) - && text_run.fg == start.fg - && text_run.run.0 == end.col + 1 - { - if size_info.cols() == text_run.run.1 && size_info.lines() == text_run.line { - self.inner.push(create_rect( - &start, - text_run.last_point(), - line.flag, - &self.metrics, - &self.size)); - } else { - *end = text_run.last_point() - } - continue; - } - self.inner.push(create_rect( - start, - *end, - line.flag, - &self.metrics, - &self.size)); - - if text_run.flags.contains(line.flag) { - *start = text_run.start_cell(); - *end = text_run.last_point(); - } else { - line.range = None; - } - }, - None => { - if text_run.flags.contains(line.flag) { - line.range = Some((text_run.start_cell(), text_run.last_point())); - } - } + /// Update the stored lines with the next text_run info. + pub fn update_lines_text_run(&mut self, text_run: &TextRun, size: &SizeInfo, metrics: &Metrics) { + for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] { + if !text_run.flags.contains(*flag) { + continue; } + + let new_line = Line::from_text_run(text_run, *flag, metrics, size); + self.inner.entry(*flag) + .or_insert_with(|| vec![]) + .push(new_line); } } /// Update the stored lines with the next cell info. - #[cfg(not(feature = "hb-ft"))] pub fn update_lines(&mut self, cell: &RenderableCell, size: &SizeInfo, metrics: &Metrics) { for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] { diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index b20856693b..21517b63e2 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -45,6 +45,8 @@ use crate::tty; pub mod cell; pub mod color; +#[cfg(feature = "hb-ft")] +pub mod text_run; /// Used to match equal brackets, when performing a bracket-pair selection. const BRACKET_PAIRS: [(char, char); 4] = [('(', ')'), ('[', ']'), ('{', '}'), ('<', '>')]; @@ -369,282 +371,6 @@ impl RenderableCell { } } -#[cfg(feature = "hb-ft")] -pub mod text_run { - use super::{ - cell::{Flags, MAX_ZEROWIDTH_CHARS}, - color::Rgb, - CursorKey, RenderableCell, RenderableCellContent, Point - }; - use crate::index::{Column, Line}; - - #[derive(Debug)] - pub(crate) struct RunStart { - pub line: Line, - pub column: Column, - pub fg: Rgb, - pub bg: Rgb, - pub bg_alpha: f32, - pub flags: Flags, - } - // Use a macro instead of a function to make use of partial move semantics that don't cross function boundaries - macro_rules! from_rc { - ($rc:ident) => { - RunStart { - line: $rc.line, - column: $rc.column, - fg: $rc.fg, - bg: $rc.bg, - bg_alpha: $rc.bg_alpha, - flags: $rc.flags, - } - }; - } - - /// Compare cells and check they are in the same text run - fn is_contiguous_context(a: &RunStart, b: &RenderableCell) -> bool { - a.line == b.line - && a.fg == b.fg - && a.bg == b.bg - && (a.bg_alpha - b.bg_alpha).abs() < 0.01 - && a.flags == b.flags - } - - - type Latest = (Column, bool); - /// Checks two columns are adjacent - fn is_contiguous_col((a, is_wide): Latest, b: Column) -> bool { - let span = if is_wide { 2usize } else { 1usize }; - a + span == b || b + span == a - } - - #[derive(Debug)] - pub enum TextRunContent { - Cursor(CursorKey), - CharRun(String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), - } - - /// Represents a set of renderable cells that all share the rendering propreties. - /// The assumption is that if two cells are in the same TextRun they can be sent off together to be shaped by harfbuzz. - /// This allows for ligatures to be rendered but not when something breaks up a ligature (e.g. selection hightlight) which is intended behavior. - #[derive(Debug)] - pub struct TextRun { - // By definition a run is on one line. - pub line: Line, - pub run: (Column, Column), - pub run_chars: TextRunContent, - pub fg: Rgb, - pub bg: Rgb, - pub bg_alpha: f32, - pub flags: Flags, - } - impl TextRun { - // These two constructors are used by TextRunIter and are not widely applicable - pub(crate) fn from_iter_state( - start: RunStart, - (latest, is_wide): Latest, - buffer: (String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), - ) -> Self { - let end_column = if is_wide { - latest + 1 - } else { - latest - }; - TextRun { - line: start.line, - run: (start.column, end_column), - run_chars: TextRunContent::CharRun(buffer.0, buffer.1), - fg: start.fg, - bg: start.bg, - bg_alpha: start.bg_alpha, - flags: start.flags, - } - } - pub(crate) fn from_cursor_rc(start: RunStart, cursor: CursorKey) -> Self { - TextRun { - line: start.line, - run: (start.column, start.column), - run_chars: TextRunContent::Cursor(cursor), - fg: start.fg, - bg: start.bg, - bg_alpha: start.bg_alpha, - flags: start.flags, - } - } - /// Holdover method while converting from rendering Cells to TextRuns - pub fn cell_at(&self, col: Column) -> RenderableCell { - RenderableCell { - line: self.line, - column: col, - inner: RenderableCellContent::Chars([' '; crate::term::cell::MAX_ZEROWIDTH_CHARS + 1]), - fg: self.fg, - bg: self.bg, - bg_alpha: self.bg_alpha, - flags: self.flags, - } - } - - /// First cell in the TextRun - pub fn start_cell(&self) -> RenderableCell { - self.cell_at(self.run.0) - } - - /// Last cell in the TextRun - pub fn last_cell(&self) -> RenderableCell { - self.cell_at(self.run.1) - } - - /// Last point covered by this TextRun - pub fn last_point(&self) -> Point { - Point { - line: self.line, - col: self.run.1, - } - } - - /// Returns iterator over range of columns [run.0, run.1] - pub fn col_iter(&self) -> impl Iterator { - let (start, end) = self.run; - let step = if self.flags.contains(Flags::WIDE_CHAR) { - // If our run contains wide chars treat each cell like it's 2 cells wide - 2 - } else { - 1 - }; - // unpacking is neccessary while Step trait is nightly - // hopefully this compiles away. - (start.0..=end.0).step_by(step).map(Column) - } - - - - /// Iterates over each RenderableCell in column range [run.0, run.1] - pub fn cell_iter<'a>(&'a self) -> impl Iterator + 'a { - self.col_iter().map(move |col| self.cell_at(col)) - } - } - - /// Wraps an Iterator and produces TextRuns to represent batches of cells - pub struct TextRunIter { - iter: I, - run_start: Option, - latest: Option, - cursor: Option, - buffer_text: String, - buffer_zero_width: Vec<[char; MAX_ZEROWIDTH_CHARS]>, - } - impl TextRunIter { - pub fn new(iter: I) -> Self { - TextRunIter { - iter, - latest: None, - run_start: None, - cursor: None, - buffer_text: String::new(), - buffer_zero_width: Vec::new(), - } - } - - /// Check if current run ends with incoming RenderableCell - fn is_run_break(&self, rc: &RenderableCell) -> bool { - let is_cell_break = - self.run_start.as_ref().map(|cell| !is_contiguous_context(cell, &rc)).unwrap_or(false); - let is_col_break = self - .latest - .as_ref() - .map(|col| !is_contiguous_col(*col, rc.column)) - .unwrap_or(false); - is_cell_break || is_col_break - } - - /// Add content of cell to pending TextRun buffer - fn buffer_content(&mut self, inner: RenderableCellContent) { - // Add to buffer only if the next rc is a Char (not a cursor) - match inner { - RenderableCellContent::Chars(chars) => { - self.buffer_text.push(chars[0]); - let mut arr: [char; MAX_ZEROWIDTH_CHARS] = Default::default(); - arr.copy_from_slice(&chars[1..]); - self.buffer_zero_width.push(arr); - } - RenderableCellContent::Cursor(cursor) => { - self.cursor = Some(cursor); - } - } - } - - /// Empty out pending buffer producing owned collections that can be moved into a TextRun - fn drain_buffer(&mut self) -> (String, Vec<[char; MAX_ZEROWIDTH_CHARS]>) { - (self.buffer_text.drain(..).collect(), self.buffer_zero_width.drain(..).collect()) - } - - /// Handles bookkeeping needed when starting a new run - fn start_run(&mut self, rc: RenderableCell) -> (Option, Option) { - self.buffer_content(rc.inner); - let latest = self.latest.replace((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); - let start = self.run_start.replace(from_rc!(rc)); - (start, latest) - } - } - - /// Utility method to ease use of Options when you need to unwrap both in tandem - fn opt_pair(a: Option, b: Option) -> Option<(A, B)> { - match (a, b) { - (Some(a_val), Some(b_val)) => Some((a_val, b_val)), - _ => None - } - } - - impl Iterator for TextRunIter - where - I: Iterator, - { - type Item = TextRun; - - fn next(&mut self) -> Option { - let mut output = None; - while let Some(rc) = self.iter.next() { - if self.latest.is_none() || self.run_start.is_none() { - // Initial state, this is should only be hit on the first next() call of iterator - self.run_start.replace(from_rc!(rc)); - } else if self.cursor.is_some() { - // Last iteration of the loop found a cursor - // Return a run for the cursor and start a new run - let (start, _) = self.start_run(rc); - output = opt_pair(start, self.cursor.take()).map(|(start, cursor)| TextRun::from_cursor_rc(start, cursor)); - break; - } else if self.is_run_break(&rc) || rc.is_cursor() { - // If we find a break or a cursor, - // return what we have so far and start a new run. - let prev_buffer = self.drain_buffer(); - let (start, latest) = self.start_run(rc); - output = opt_pair(start, latest).map(|(start, latest)| { - TextRun::from_iter_state(start, latest, prev_buffer) - }); - break; - } - // Build up buffer and track the latest column we've seen - self.latest = Some((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); - self.buffer_content(rc.inner); - } - // If we generated output we're done - // Otherwise check for any remaining buffered content and return it as a text run - // This a destructive operation so it will return None after it excutes once - output.or_else(|| { - if !self.buffer_text.is_empty() || !self.buffer_zero_width.is_empty() { - opt_pair(self.run_start.take(), self.latest.take()).map(|(start, latest)| - // Save leftover buffer and empty it - TextRun::from_iter_state(start, latest, self.drain_buffer())) - } else if let Some(cursor) = self.cursor { - self.run_start.take().map(|start| TextRun::from_cursor_rc(start, cursor)) - } else { - None - } - }) - } - } -} - impl<'a> Iterator for RenderableCellsIter<'a> { type Item = RenderableCell; diff --git a/alacritty_terminal/src/term/text_run.rs b/alacritty_terminal/src/term/text_run.rs new file mode 100644 index 0000000000..71b9e73c7e --- /dev/null +++ b/alacritty_terminal/src/term/text_run.rs @@ -0,0 +1,292 @@ +use super::{ + cell::{Flags, MAX_ZEROWIDTH_CHARS}, + color::Rgb, + CursorKey, Point, RenderableCell, RenderableCellContent, +}; +use crate::index::{Column, Line}; + +#[derive(Debug)] +pub(crate) struct RunStart { + pub line: Line, + pub column: Column, + pub fg: Rgb, + pub bg: Rgb, + pub bg_alpha: f32, + pub flags: Flags, +} +// Use a macro instead of a function to make use of partial move semantics that don't cross +// function boundaries +// Convert a RenderableCell into a RunStart +macro_rules! from_rc { + ($rc:ident) => { + RunStart { + line: $rc.line, + column: $rc.column, + fg: $rc.fg, + bg: $rc.bg, + bg_alpha: $rc.bg_alpha, + flags: $rc.flags, + } + }; +} + +/// Compare cells and check they are in the same text run +fn is_contiguous_context(a: &RunStart, b: &RenderableCell) -> bool { + a.line == b.line + && a.fg == b.fg + && a.bg == b.bg + && (a.bg_alpha - b.bg_alpha).abs() < 0.01 + && a.flags == b.flags +} + +type Latest = (Column, bool); +/// Checks two columns are adjacent +fn is_contiguous_col((a, is_wide): Latest, b: Column) -> bool { + let span = if is_wide { 2usize } else { 1usize }; + a + span == b || b + span == a +} + +#[derive(Debug)] +pub enum TextRunContent { + Cursor(CursorKey), + CharRun(String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), +} + +/// Represents a set of renderable cells that all share the same rendering propreties. +/// The assumption is that if two cells are in the same TextRun they can be sent off together to +/// be shaped by Harfbuzz. This allows for ligatures to be rendered but not when something +/// breaks up a ligature (e.g. selection hightlight) which is desired behavior. +#[derive(Debug)] +pub struct TextRun { + // By definition a run is on one line. + pub line: Line, + pub run: (Column, Column), + pub run_chars: TextRunContent, + pub fg: Rgb, + pub bg: Rgb, + pub bg_alpha: f32, + pub flags: Flags, +} +impl TextRun { + // These two constructors are used by TextRunIter and are not widely applicable + pub(crate) fn from_iter_state( + start: RunStart, + (latest, is_wide): Latest, + buffer: (String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), + ) -> Self { + let end_column = if is_wide { latest + 1 } else { latest }; + TextRun { + line: start.line, + run: (start.column, end_column), + run_chars: TextRunContent::CharRun(buffer.0, buffer.1), + fg: start.fg, + bg: start.bg, + bg_alpha: start.bg_alpha, + flags: start.flags, + } + } + + pub(crate) fn from_cursor_rc(start: RunStart, cursor: CursorKey) -> Self { + TextRun { + line: start.line, + run: (start.column, start.column), + run_chars: TextRunContent::Cursor(cursor), + fg: start.fg, + bg: start.bg, + bg_alpha: start.bg_alpha, + flags: start.flags, + } + } + + /// Holdover method while converting from rendering Cells to TextRuns + pub fn cell_at(&self, col: Column) -> RenderableCell { + RenderableCell { + line: self.line, + column: col, + inner: RenderableCellContent::Chars( + [' '; crate::term::cell::MAX_ZEROWIDTH_CHARS + 1], + ), + fg: self.fg, + bg: self.bg, + bg_alpha: self.bg_alpha, + flags: self.flags, + } + } + + // Number of columns this TextRun spans + pub fn len(&self) -> usize { + let (start, end) = self.run; + end.0 - start.0 + } + + /// First column of the run + pub fn start_col(&self) -> Column { + self.run.0 + } + + + /// First cell in the TextRun + pub fn start_cell(&self) -> RenderableCell { + self.cell_at(self.run.0) + } + + /// Last cell in the TextRun + pub fn last_cell(&self) -> RenderableCell { + self.cell_at(self.run.1) + } + + /// First point covered by this TextRun + pub fn start_point(&self) -> Point { + Point { line: self.line, col: self.run.0 } + } + + /// Last point covered by this TextRun + pub fn last_point(&self) -> Point { + Point { line: self.line, col: self.run.1 } + } + + /// Returns iterator over range of columns [run.0, run.1] + pub fn col_iter(&self) -> impl Iterator { + let (start, end) = self.run; + let step = if self.flags.contains(Flags::WIDE_CHAR) { + // If our run contains wide chars treat each cell like it's 2 cells wide + 2 + } else { + 1 + }; + // unpacking is neccessary while Step trait is nightly + // hopefully this compiles away. + (start.0..=end.0).step_by(step).map(Column) + } + + /// Iterates over each RenderableCell in column range [run.0, run.1] + pub fn cell_iter<'a>(&'a self) -> impl Iterator + 'a { + self.col_iter().map(move |col| self.cell_at(col)) + } +} + +/// Wraps an Iterator and produces TextRuns to represent batches of cells +pub struct TextRunIter { + iter: I, + run_start: Option, + latest: Option, + cursor: Option, + buffer_text: String, + buffer_zero_width: Vec<[char; MAX_ZEROWIDTH_CHARS]>, +} +impl TextRunIter { + pub fn new(iter: I) -> Self { + TextRunIter { + iter, + latest: None, + run_start: None, + cursor: None, + buffer_text: String::new(), + buffer_zero_width: Vec::new(), + } + } + + /// Check if current run ends with incoming RenderableCell + fn is_run_break(&self, rc: &RenderableCell) -> bool { + let is_cell_break = self + .run_start + .as_ref() + .map(|cell| !is_contiguous_context(cell, &rc)) + .unwrap_or(false); + let is_col_break = self + .latest + .as_ref() + .map(|col| !is_contiguous_col(*col, rc.column)) + .unwrap_or(false); + is_cell_break || is_col_break + } + + /// Add content of cell to pending TextRun buffer + fn buffer_content(&mut self, inner: RenderableCellContent) { + // Add to buffer only if the next rc is a Char (not a cursor) + match inner { + RenderableCellContent::Chars(chars) => { + self.buffer_text.push(chars[0]); + let mut arr: [char; MAX_ZEROWIDTH_CHARS] = Default::default(); + arr.copy_from_slice(&chars[1..]); + self.buffer_zero_width.push(arr); + }, + RenderableCellContent::Cursor(cursor) => { + self.cursor = Some(cursor); + }, + } + } + + /// Empty out pending buffer producing owned collections that can be moved into a TextRun + fn drain_buffer(&mut self) -> (String, Vec<[char; MAX_ZEROWIDTH_CHARS]>) { + (self.buffer_text.drain(..).collect(), self.buffer_zero_width.drain(..).collect()) + } + + /// Handles bookkeeping needed when starting a new run + fn start_run(&mut self, rc: RenderableCell) -> (Option, Option) { + self.buffer_content(rc.inner); + let latest = self.latest.replace((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); + let start = self.run_start.replace(from_rc!(rc)); + (start, latest) + } +} + +/// Utility method to ease use of Options when you need to unwrap both in tandem +fn opt_pair(a: Option, b: Option) -> Option<(A, B)> { + match (a, b) { + (Some(a_val), Some(b_val)) => Some((a_val, b_val)), + _ => None, + } +} + +impl Iterator for TextRunIter +where + I: Iterator, +{ + type Item = TextRun; + + fn next(&mut self) -> Option { + let mut output = None; + while let Some(rc) = self.iter.next() { + // We don't want to add wide_char spacers to the buffer + if self.latest.is_none() || self.run_start.is_none() { + // Initial state, this is should only be hit on the first next() call of + // iterator + self.run_start = Some(from_rc!(rc)); + } else if self.cursor.is_some() { + // Last iteration of the loop found a cursor + // Return a run for the cursor and start a new run + let (start, _) = self.start_run(rc); + output = opt_pair(start, self.cursor.take()) + .map(|(start, cursor)| TextRun::from_cursor_rc(start, cursor)); + break; + } else if self.is_run_break(&rc) || rc.is_cursor() { + // If we find a break or a cursor, + // return what we have so far and start a new run. + let prev_buffer = self.drain_buffer(); + let (start, latest) = self.start_run(rc); + output = opt_pair(start, latest).map(|(start, latest)| { + TextRun::from_iter_state(start, latest, prev_buffer) + }); + break; + } + // Build up buffer and track the latest column we've seen + self.latest = Some((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); + self.buffer_content(rc.inner); + } + // If we generated output we're done + // Otherwise check for any remaining buffered content and return it as a text run + // This a destructive operation so it will return None after it excutes once + output.or_else(|| { + if !self.buffer_text.is_empty() || !self.buffer_zero_width.is_empty() { + opt_pair(self.run_start.take(), self.latest.take()).map(|(start, latest)| + // Save leftover buffer and empty it + TextRun::from_iter_state(start, latest, self.drain_buffer())) + } else if let Some(cursor) = self.cursor { + self.run_start.take().map(|start| TextRun::from_cursor_rc(start, cursor)) + } else { + None + } + }) + } +} \ No newline at end of file diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 5aa09cfc8b..de2b3646eb 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -18,17 +18,17 @@ use std::collections::HashMap; use std::fmt; use std::path::PathBuf; -#[cfg(feature = "hb-ft")] -use harfbuzz_rs::{Owned, Font, GlyphBuffer}; use freetype::tt_os2::TrueTypeOS2Table; use freetype::{self, Library}; +#[cfg(feature = "hb-ft")] +use harfbuzz_rs::{Font, GlyphBuffer, Owned}; use libc::c_uint; pub mod fc; -use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; #[cfg(feature = "hb-ft")] use super::{key_type::KeyType, RasterizeConfig}; +use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; struct FixedSize { pixelsize: f64, @@ -168,23 +168,18 @@ impl ::Rasterize for FreeTypeRasterizer { #[cfg(feature = "hb-ft")] impl ::HbFtExt for FreeTypeRasterizer { - fn shape( - &mut self, - text: &str, - font_key: FontKey, - ) -> GlyphBuffer { - use harfbuzz_rs::{shape, UnicodeBuffer, Feature, Tag}; + fn shape(&mut self, text: &str, font_key: FontKey) -> GlyphBuffer { + use harfbuzz_rs::{shape, Feature, Tag, UnicodeBuffer}; let hb_font = &self.faces[&font_key].hb_font; - let buf = UnicodeBuffer::default() - .add_str(text); + let buf = UnicodeBuffer::default().add_str(text); let value = if self.use_font_ligatures { 1 } else { 0 }; let features = &[ Feature::new(Tag::new('c', 'l', 'i', 'g'), value, 0..), Feature::new(Tag::new('l', 'i', 'g', 'a'), value, 0..), - // This feature controls ligature rendering in Fira Code but this could - // very well be Fira Code specific behavior as it codes all it's "ligatures" as glyph substitutions - // Which are unaffected by features "clig" and "liga" - // Feature::new(Tag::new('c', 'a', 'l', 't'), value, 0..) + /* This feature controls ligature rendering in Fira Code but this could + * very well be Fira Code specific behavior as it codes all it's "ligatures" as + * glyph substitutions Which are unaffected by features "clig" and + * "liga" Feature::new(Tag::new('c', 'a', 'l', 't'), value, 0..) */ ]; // Shape with default features shape(&*hb_font, buf, features) @@ -346,8 +341,7 @@ impl FreeTypeRasterizer { #[cfg(feature = "hb-ft")] pub fn index_for_char(&self, font_key: FontKey, c: char) -> Option { - self.faces.get(&font_key).map(|face| - face.ft_face.get_char_index(c as usize)) + self.faces.get(&font_key).map(|face| face.ft_face.get_char_index(c as usize)) } #[cfg(feature = "hb-ft")] @@ -367,7 +361,7 @@ impl FreeTypeRasterizer { let key = self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key); Ok(key) } - } + }, } } @@ -415,14 +409,12 @@ impl FreeTypeRasterizer { glyph_key: GlyphKey, index: u32, ) -> Result { - let size = face - .non_scalable - .as_ref() - .map(|v| v.pixelsize as f32) - .unwrap_or_else(|| glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.); - - face.ft_face - .set_char_size(to_freetype_26_6(size), 0, 0, 0)?; + let size = + face.non_scalable.as_ref().map(|v| v.pixelsize as f32).unwrap_or_else(|| { + glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72. + }); + + face.ft_face.set_char_size(to_freetype_26_6(size), 0, 0, 0)?; unsafe { let ft_lib = self.library.raw(); diff --git a/font/src/lib.rs b/font/src/lib.rs index c79527474b..1aa8da02a7 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -20,9 +20,8 @@ #![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)] -/* Note: all applicable cfg statements have been modified to short-circuit - * to freetype if the feature hb-ft is enabled. - */ +// Note: all applicable cfg statements have been modified to short-circuit +// to freetype if the feature hb-ft is enabled. #[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] extern crate fontconfig; @@ -42,7 +41,6 @@ extern crate euclid; extern crate libc; - #[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] #[macro_use] extern crate foreign_types; @@ -53,10 +51,10 @@ extern crate harfbuzz_rs; #[cfg_attr(not(windows), macro_use)] extern crate log; +use std::fmt; #[cfg(not(feature = "hb-ft"))] use std::hash::{Hash, Hasher}; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::fmt; // If target isn't macos or windows, reexport everything from ft #[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] @@ -170,7 +168,8 @@ pub mod key_type { #[cfg(feature = "hb-ft")] use key_type::KeyType; -// For now use derived impls, this can be swapped for more efficient implementations once things are working +// For now use derived impls, this can be swapped for more efficient implementations once things are +// working #[cfg_attr(feature = "hb-ft", derive(Hash, PartialEq))] #[derive(Debug, Copy, Clone, Eq)] pub struct GlyphKey { @@ -304,7 +303,7 @@ pub trait Rasterize { fn new(device_pixel_ratio: f32, use_thin_strokes: bool) -> Result where Self: Sized; - + #[cfg(feature = "hb-ft")] fn new(device_pixel_ratio: f32, rasterize_config: RasterizeConfig) -> Result where @@ -323,19 +322,24 @@ pub trait Rasterize { fn update_dpr(&mut self, device_pixel_ratio: f32); } +/// Config option specific to the Rasterizer. +/// Since the Rasterizer lives in the subcrate font we do not want it to depend on the Font config struct from alacritty, as this would introduce a circular dependency between the crates.Clone +/// This struct specifies the subset of Font that the Rasterizer cares about, then traits can be used to convert from Font to this struct when constructing a Rasterizer. #[cfg(feature = "hb-ft")] pub struct RasterizeConfig { /// Toggle thin strokes on mac osx - // Technically this is impossible while under "hb-ft" but is included for compatiblity in the api + // Technically this is impossible while under "hb-ft" but is included for compatiblity in the + // api pub use_thin_strokes: bool, /// Toggle rendering of font ligatures pub use_font_ligatures: bool, } +// Only implemented for the FreeType rasterizer so far. +/// Conceptually this extends the Rasterizer trait with Harfbuzz specific functionality. #[cfg(feature = "hb-ft")] pub trait HbFtExt { /// Shape the provided text into a set of glyphs. /// TODO: properly report HarfBuzz errors - fn shape(&mut self, text: &str, font_key: FontKey) - -> harfbuzz_rs::GlyphBuffer; + fn shape(&mut self, text: &str, font_key: FontKey) -> harfbuzz_rs::GlyphBuffer; } From ef3a5a90f8f761bbccb96055e89fef60e493fcd1 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 6 Aug 2019 17:32:49 -0700 Subject: [PATCH 38/84] Ran formatter over new changes in code. Added line to CHANGELOG.md --- CHANGELOG.md | 1 + alacritty_terminal/src/display.rs | 6 +++++- alacritty_terminal/src/renderer/rects.rs | 12 ++++++----- alacritty_terminal/src/term/mod.rs | 2 +- alacritty_terminal/src/term/text_run.rs | 26 ++++++++---------------- font/src/lib.rs | 8 +++++--- 6 files changed, 27 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06862ac0ed..0e281406d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Config option `window.gtk_theme_variant` to set GTK theme variant - Completions for `--class` and `-t` (short title) - Change the mouse cursor when hovering over the message bar and its close button +- Support for Font Ligatures on Linux. ### Changed diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index af095ea296..f2f9570656 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -543,7 +543,11 @@ impl Display { self.renderer.with_api(config, &size_info, |mut api| { // Iterate over each contiguous block of text - for text_run in TextRunIter::new(grid_cells.into_iter().filter(|rc| !rc.flags.contains(Flags::WIDE_CHAR_SPACER))) { + for text_run in TextRunIter::new( + grid_cells + .into_iter() + .filter(|rc| !rc.flags.contains(Flags::WIDE_CHAR_SPACER)), + ) { // Update underline/strikeout rects.update_lines_text_run(&text_run, &size_info, &metrics); diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index d36bde430e..620b993167 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -106,7 +106,6 @@ impl Line { Self { start: text_run.start_point(), color: text_run.fg, rect } } - } /// Rects for underline, strikeout and more. @@ -132,16 +131,19 @@ impl Rects { #[cfg(feature = "hb-ft")] /// Update the stored lines with the next text_run info. - pub fn update_lines_text_run(&mut self, text_run: &TextRun, size: &SizeInfo, metrics: &Metrics) { + pub fn update_lines_text_run( + &mut self, + text_run: &TextRun, + size: &SizeInfo, + metrics: &Metrics, + ) { for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] { if !text_run.flags.contains(*flag) { continue; } let new_line = Line::from_text_run(text_run, *flag, metrics, size); - self.inner.entry(*flag) - .or_insert_with(|| vec![]) - .push(new_line); + self.inner.entry(*flag).or_insert_with(|| vec![]).push(new_line); } } diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 21517b63e2..e90c926a02 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -46,7 +46,7 @@ use crate::tty; pub mod cell; pub mod color; #[cfg(feature = "hb-ft")] -pub mod text_run; +pub mod text_run; /// Used to match equal brackets, when performing a bracket-pair selection. const BRACKET_PAIRS: [(char, char); 4] = [('(', ')'), ('[', ']'), ('{', '}'), ('<', '>')]; diff --git a/alacritty_terminal/src/term/text_run.rs b/alacritty_terminal/src/term/text_run.rs index 71b9e73c7e..9ef77cf85d 100644 --- a/alacritty_terminal/src/term/text_run.rs +++ b/alacritty_terminal/src/term/text_run.rs @@ -103,9 +103,7 @@ impl TextRun { RenderableCell { line: self.line, column: col, - inner: RenderableCellContent::Chars( - [' '; crate::term::cell::MAX_ZEROWIDTH_CHARS + 1], - ), + inner: RenderableCellContent::Chars([' '; crate::term::cell::MAX_ZEROWIDTH_CHARS + 1]), fg: self.fg, bg: self.bg, bg_alpha: self.bg_alpha, @@ -124,7 +122,6 @@ impl TextRun { self.run.0 } - /// First cell in the TextRun pub fn start_cell(&self) -> RenderableCell { self.cell_at(self.run.0) @@ -188,16 +185,10 @@ impl TextRunIter { /// Check if current run ends with incoming RenderableCell fn is_run_break(&self, rc: &RenderableCell) -> bool { - let is_cell_break = self - .run_start - .as_ref() - .map(|cell| !is_contiguous_context(cell, &rc)) - .unwrap_or(false); - let is_col_break = self - .latest - .as_ref() - .map(|col| !is_contiguous_col(*col, rc.column)) - .unwrap_or(false); + let is_cell_break = + self.run_start.as_ref().map(|cell| !is_contiguous_context(cell, &rc)).unwrap_or(false); + let is_col_break = + self.latest.as_ref().map(|col| !is_contiguous_col(*col, rc.column)).unwrap_or(false); is_cell_break || is_col_break } @@ -265,9 +256,8 @@ where // return what we have so far and start a new run. let prev_buffer = self.drain_buffer(); let (start, latest) = self.start_run(rc); - output = opt_pair(start, latest).map(|(start, latest)| { - TextRun::from_iter_state(start, latest, prev_buffer) - }); + output = opt_pair(start, latest) + .map(|(start, latest)| TextRun::from_iter_state(start, latest, prev_buffer)); break; } // Build up buffer and track the latest column we've seen @@ -289,4 +279,4 @@ where } }) } -} \ No newline at end of file +} diff --git a/font/src/lib.rs b/font/src/lib.rs index 1aa8da02a7..ae3a767bc4 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -323,8 +323,10 @@ pub trait Rasterize { } /// Config option specific to the Rasterizer. -/// Since the Rasterizer lives in the subcrate font we do not want it to depend on the Font config struct from alacritty, as this would introduce a circular dependency between the crates.Clone -/// This struct specifies the subset of Font that the Rasterizer cares about, then traits can be used to convert from Font to this struct when constructing a Rasterizer. +/// Since the Rasterizer lives in the subcrate font we do not want it to depend on the Font config +/// struct from alacritty, as this would introduce a circular dependency between the crates.Clone +/// This struct specifies the subset of Font that the Rasterizer cares about, then traits can be +/// used to convert from Font to this struct when constructing a Rasterizer. #[cfg(feature = "hb-ft")] pub struct RasterizeConfig { /// Toggle thin strokes on mac osx @@ -335,7 +337,7 @@ pub struct RasterizeConfig { pub use_font_ligatures: bool, } -// Only implemented for the FreeType rasterizer so far. +// Only implemented for the FreeType rasterizer so far. /// Conceptually this extends the Rasterizer trait with Harfbuzz specific functionality. #[cfg(feature = "hb-ft")] pub trait HbFtExt { From d04e706c2227139ff6f52faeb1ffa63bf731532a Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 6 Aug 2019 17:45:58 -0700 Subject: [PATCH 39/84] Merge in latest changes to rects (now lines). --- alacritty_terminal/src/display.rs | 6 ++-- alacritty_terminal/src/renderer/rects.rs | 41 ++++-------------------- 2 files changed, 10 insertions(+), 37 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index c7b94f6fee..ca3bc2a8af 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -27,7 +27,7 @@ use crate::config::{Config, StartupMode}; use crate::index::Line; use crate::message_bar::Message; use crate::meter::Meter; -use crate::renderer::rects::{RenderRect, RenderLines}; +use crate::renderer::rects::{RenderLines, RenderRect}; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::sync::FairMutex; use crate::term::color::Rgb; @@ -546,10 +546,12 @@ impl Display { for text_run in TextRunIter::new( grid_cells .into_iter() + // Logic for WIDE_CHAR is handled internally by TextRun + // So we no longer need WIDE_CHAR_SPACER at this point. .filter(|rc| !rc.flags.contains(Flags::WIDE_CHAR_SPACER)), ) { // Update underline/strikeout - rects.update(&text_run, &size_info, &metrics); + lines.update(&text_run, &size_info, &metrics); // Draw text run api.render_text_run(text_run, glyph_cache); diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index af06198723..6ebce1e52a 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -72,34 +72,6 @@ impl RenderLine { RenderRect::new(start_x + size.padding_x, y + size.padding_y, width, height, self.color) } - //#[cfg(feature = "hb-ft")] - ///// Create a line that starts on the left of `text_run` and is `text_run.len()` wide - //fn from_text_run(text_run: &TextRun, flag: Flags, metrics: &Metrics, size: &SizeInfo) -> Self { - // // This is basically a 1:1 copy of from_cell but with semantics of TextRun - // let run_x = text_run.start_col().0 as f32 * size.cell_width; - - // 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 text run line drawing specified"), - // }; - - // // Make sure lines are always visible - // height = height.max(1.); - - // let run_bottom = (text_run.line.0 as f32 + 1.) * size.cell_height; - // let baseline = run_bottom + metrics.descent; - - // let mut y = baseline - position - height / 2.; - // let max_y = run_bottom - height; - // if y > max_y { - // y = max_y; - // } - - // let rect = Rect::new(run_x + size.padding_x, y + size.padding_y, (text_run.len() + 1) as f32 * size.cell_width, height); - - // Self { start: text_run.start_point(), color: text_run.fg, rect } - //} } /// Lines for underline and strikeout. @@ -125,18 +97,17 @@ impl RenderLines { #[cfg(feature = "hb-ft")] /// Update the stored lines with the next text_run info. - pub fn update( - &mut self, - text_run: &TextRun, - size: &SizeInfo, - metrics: &Metrics, - ) { + pub fn update(&mut self, text_run: &TextRun, size: &SizeInfo, metrics: &Metrics) { for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] { if !text_run.flags.contains(*flag) { continue; } - let new_line = RenderLine { start: text_run.start_point(), end: text_run.last_point(), color: text_run.fg }; + let new_line = RenderLine { + start: text_run.start_point(), + end: text_run.last_point(), + color: text_run.fg, + }; self.inner.entry(*flag).or_insert_with(|| vec![]).push(new_line); } } From e392c882fe04ca96267ea92cf8db665099284e0e Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 6 Aug 2019 18:02:56 -0700 Subject: [PATCH 40/84] Fix clippy errors. Remove extraneous parameters from lines.update() for TextRun. --- Cargo.lock | 4 ++-- alacritty_terminal/Cargo.toml | 2 +- alacritty_terminal/src/display.rs | 2 +- alacritty_terminal/src/renderer/rects.rs | 2 +- alacritty_terminal/src/term/text_run.rs | 16 +++++++++++++--- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6613c64266..02700ba3b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -865,10 +865,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cmake 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", "core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)", - "core-text 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-text 13.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "freetype 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index 87408c5044..cabbe47c11 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -56,7 +56,7 @@ image = "0.21.0" objc = "0.2.2" [features] -default = [] +default = ["hb-ft"] # Enabling this feature makes shaders automatically reload when changed live-shader-reload = [] nightly = [] diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index ca3bc2a8af..01489a7d0c 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -551,7 +551,7 @@ impl Display { .filter(|rc| !rc.flags.contains(Flags::WIDE_CHAR_SPACER)), ) { // Update underline/strikeout - lines.update(&text_run, &size_info, &metrics); + lines.update(&text_run); // Draw text run api.render_text_run(text_run, glyph_cache); diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index 6ebce1e52a..e7606e6ec9 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -97,7 +97,7 @@ impl RenderLines { #[cfg(feature = "hb-ft")] /// Update the stored lines with the next text_run info. - pub fn update(&mut self, text_run: &TextRun, size: &SizeInfo, metrics: &Metrics) { + pub fn update(&mut self, text_run: &TextRun) { for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] { if !text_run.flags.contains(*flag) { continue; diff --git a/alacritty_terminal/src/term/text_run.rs b/alacritty_terminal/src/term/text_run.rs index 9ef77cf85d..2fc38331a6 100644 --- a/alacritty_terminal/src/term/text_run.rs +++ b/alacritty_terminal/src/term/text_run.rs @@ -111,12 +111,22 @@ impl TextRun { } } - // Number of columns this TextRun spans + /// Number of columns this TextRun spans pub fn len(&self) -> usize { let (start, end) = self.run; end.0 - start.0 } + /// True if TextRun contains characters, false if it contains no characters. + pub fn is_empty(&self) -> bool { + match &self.run_chars { + TextRunContent::Cursor(_) => true, + TextRunContent::CharRun(ref string, ref zero_widths) => { + !(string.is_empty() && zero_widths.is_empty()) + }, + } + } + /// First column of the run pub fn start_col(&self) -> Column { self.run.0 @@ -201,10 +211,10 @@ impl TextRunIter { let mut arr: [char; MAX_ZEROWIDTH_CHARS] = Default::default(); arr.copy_from_slice(&chars[1..]); self.buffer_zero_width.push(arr); - }, + } RenderableCellContent::Cursor(cursor) => { self.cursor = Some(cursor); - }, + } } } From 3f1f25d3c50f5bebe9cc09d81b7b540e5a5f0594 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 6 Aug 2019 18:04:13 -0700 Subject: [PATCH 41/84] Remove "hb-ft" from default feature list. --- alacritty_terminal/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index cabbe47c11..87408c5044 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -56,7 +56,7 @@ image = "0.21.0" objc = "0.2.2" [features] -default = ["hb-ft"] +default = [] # Enabling this feature makes shaders automatically reload when changed live-shader-reload = [] nightly = [] From 499fe03e85954a581d25826dc8004317c938b701 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Fri, 9 Aug 2019 16:00:30 -0700 Subject: [PATCH 42/84] Cargo fmt code, fix residual clippy errors in ansi.rs --- alacritty_terminal/src/ansi.rs | 60 ++++++++++++++++++------ alacritty_terminal/src/config/font.rs | 5 -- alacritty_terminal/src/cursor.rs | 19 +++++--- alacritty_terminal/src/display.rs | 8 ++-- alacritty_terminal/src/input.rs | 1 - alacritty_terminal/src/renderer/rects.rs | 1 - alacritty_terminal/src/term/text_run.rs | 4 +- 7 files changed, 65 insertions(+), 33 deletions(-) diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs index 1914e952f9..c98df91a10 100644 --- a/alacritty_terminal/src/ansi.rs +++ b/alacritty_terminal/src/ansi.rs @@ -774,7 +774,7 @@ where // Set icon name // This is ignored, since alacritty has no concept of tabs - b"1" => return, + b"1" => {}, // Set color index b"4" => { @@ -890,7 +890,14 @@ where } #[inline] - fn csi_dispatch(&mut self, args: &[i64], intermediates: &[u8], has_ignored_intermediates: bool, action: char) { + #[allow(clippy::needless_return)] // csi_dispatch doesn't type check if return in unhandled is removed. + fn csi_dispatch( + &mut self, + args: &[i64], + intermediates: &[u8], + has_ignored_intermediates: bool, + action: char, + ) { macro_rules! unhandled { () => {{ debug!( @@ -917,7 +924,9 @@ where let writer = &mut self.writer; match (action, intermediates.get(0)) { - ('@', None) => handler.insert_blank(Column(arg_or_default!(idx: 0, default: 1) as usize)), + ('@', None) => { + handler.insert_blank(Column(arg_or_default!(idx: 0, default: 1) as usize)) + }, ('A', None) => { handler.move_up(Line(arg_or_default!(idx: 0, default: 1) as usize)); }, @@ -930,12 +939,22 @@ where debug!("tried to repeat with no preceding char"); } }, - ('B', None) | ('e', None) => handler.move_down(Line(arg_or_default!(idx: 0, default: 1) as usize)), + ('B', None) | ('e', None) => { + handler.move_down(Line(arg_or_default!(idx: 0, default: 1) as usize)) + }, ('c', None) => handler.identify_terminal(writer), - ('C', None) | ('a', None) => handler.move_forward(Column(arg_or_default!(idx: 0, default: 1) as usize)), - ('D', None) => handler.move_backward(Column(arg_or_default!(idx: 0, default: 1) as usize)), - ('E', None) => handler.move_down_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)), - ('F', None) => handler.move_up_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)), + ('C', None) | ('a', None) => { + handler.move_forward(Column(arg_or_default!(idx: 0, default: 1) as usize)) + }, + ('D', None) => { + handler.move_backward(Column(arg_or_default!(idx: 0, default: 1) as usize)) + }, + ('E', None) => { + handler.move_down_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)) + }, + ('F', None) => { + handler.move_up_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)) + }, ('g', None) => { let mode = match arg_or_default!(idx: 0, default: 0) { 0 => TabulationClearMode::Current, @@ -945,7 +964,9 @@ where handler.clear_tabs(mode); }, - ('G', None) | ('`', None) => handler.goto_col(Column(arg_or_default!(idx: 0, default: 1) as usize - 1)), + ('G', None) | ('`', None) => { + handler.goto_col(Column(arg_or_default!(idx: 0, default: 1) as usize - 1)) + }, ('H', None) | ('f', None) => { let y = arg_or_default!(idx: 0, default: 1) as usize; let x = arg_or_default!(idx: 1, default: 1) as usize; @@ -975,7 +996,9 @@ where }, ('S', None) => handler.scroll_up(Line(arg_or_default!(idx: 0, default: 1) as usize)), ('T', None) => handler.scroll_down(Line(arg_or_default!(idx: 0, default: 1) as usize)), - ('L', None) => handler.insert_blank_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)), + ('L', None) => { + handler.insert_blank_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)) + }, ('l', intermediate) => { let is_private_mode = match intermediate { Some(b'?') => true, @@ -991,10 +1014,16 @@ where } }, ('M', None) => handler.delete_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)), - ('X', None) => handler.erase_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)), - ('P', None) => handler.delete_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)), + ('X', None) => { + handler.erase_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)) + }, + ('P', None) => { + handler.delete_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)) + }, ('Z', None) => handler.move_backward_tabs(arg_or_default!(idx: 0, default: 1)), - ('d', None) => handler.goto_line(Line(arg_or_default!(idx: 0, default: 1) as usize - 1)), + ('d', None) => { + handler.goto_line(Line(arg_or_default!(idx: 0, default: 1) as usize - 1)) + }, ('h', intermediate) => { let is_private_mode = match intermediate { Some(b'?') => true, @@ -1021,7 +1050,9 @@ where } } }, - ('n', None) => handler.device_status(writer, arg_or_default!(idx: 0, default: 0) as usize), + ('n', None) => { + handler.device_status(writer, arg_or_default!(idx: 0, default: 0) as usize) + }, ('q', Some(b' ')) => { // DECSCUSR (CSI Ps SP q) -- Set Cursor Style let style = match arg_or_default!(idx: 0, default: 0) { @@ -1053,6 +1084,7 @@ where } #[inline] + #[allow(clippy::needless_return)] // esc_dispatch doesn't type check if return in unhandled is removed. fn esc_dispatch(&mut self, params: &[i64], intermediates: &[u8], _ignore: bool, byte: u8) { macro_rules! unhandled { () => {{ diff --git a/alacritty_terminal/src/config/font.rs b/alacritty_terminal/src/config/font.rs index 177ee3d1e6..96eec7c6ea 100644 --- a/alacritty_terminal/src/config/font.rs +++ b/alacritty_terminal/src/config/font.rs @@ -105,11 +105,6 @@ impl Font { pub fn use_font_ligatures(&self) -> bool { self.use_font_ligatures.0 } - - #[cfg(not(feature = "hb-ft"))] - pub fn use_font_ligatures(&self) -> bool { - false - } } #[cfg(feature = "hb-ft")] diff --git a/alacritty_terminal/src/cursor.rs b/alacritty_terminal/src/cursor.rs index 6ca0e895be..352ed8fb81 100644 --- a/alacritty_terminal/src/cursor.rs +++ b/alacritty_terminal/src/cursor.rs @@ -57,9 +57,9 @@ pub fn get_cursor_glyph( // This default is done as a constant to avoid duplicating the feature toggle for each cursor type. #[cfg(not(feature = "hb-ft"))] -const CURSOR: char = ' '; +pub const PLACEHOLDER_GLYPH: char = ' '; #[cfg(feature = "hb-ft")] -const CURSOR: font::key_type::KeyType = font::key_type::KeyType::GlyphIndex(1u32); +pub const PLACEHOLDER_GLYPH: font::key_type::KeyType = font::key_type::KeyType::GlyphIndex(1u32); // Returns a custom underline cursor character pub fn get_underline_cursor_glyph(width: i32, line_width: i32) -> RasterizedGlyph { @@ -67,7 +67,14 @@ pub fn get_underline_cursor_glyph(width: i32, line_width: i32) -> RasterizedGlyp let buf = vec![255u8; (width * line_width * 3) as usize]; // Create a custom glyph with the rectangle data attached to it - RasterizedGlyph { c: CURSOR, top: line_width, left: 0, height: line_width, width, buf } + RasterizedGlyph { + c: PLACEHOLDER_GLYPH, + top: line_width, + left: 0, + height: line_width, + width, + buf, + } } // Returns a custom beam cursor character @@ -76,7 +83,7 @@ pub fn get_beam_cursor_glyph(height: i32, line_width: i32) -> RasterizedGlyph { let buf = vec![255u8; (line_width * height * 3) as usize]; // Create a custom glyph with the rectangle data attached to it - RasterizedGlyph { c: CURSOR, top: height, left: 0, height, width: line_width, buf } + RasterizedGlyph { c: PLACEHOLDER_GLYPH, top: height, left: 0, height, width: line_width, buf } } // Returns a custom box cursor character @@ -98,7 +105,7 @@ pub fn get_box_cursor_glyph(height: i32, width: i32, line_width: i32) -> Rasteri } // Create a custom glyph with the rectangle data attached to it - RasterizedGlyph { c: CURSOR, top: height, left: 0, height, width, buf } + RasterizedGlyph { c: PLACEHOLDER_GLYPH, top: height, left: 0, height, width, buf } } // Returns a custom block cursor character @@ -107,5 +114,5 @@ pub fn get_block_cursor_glyph(height: i32, width: i32) -> RasterizedGlyph { let buf = vec![255u8; (width * height * 3) as usize]; // Create a custom glyph with the rectangle data attached to it - RasterizedGlyph { c: CURSOR, top: height, left: 0, height, width, buf } + RasterizedGlyph { c: PLACEHOLDER_GLYPH, top: height, left: 0, height, width, buf } } diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 01489a7d0c..209e792f44 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -300,19 +300,19 @@ impl Display { renderer: &mut QuadRenderer, config: &Config, ) -> Result<(GlyphCache, f32, f32), Error> { - let font = config.font.clone(); #[cfg(not(feature = "hb-ft"))] let rasterizer = font::Rasterizer::new(dpr as f32, config.font.use_thin_strokes())?; #[cfg(feature = "hb-ft")] - let rasterizer = font::Rasterizer::new(dpr as f32, (&config.font).into())?; + let rasterizer = + font::Rasterizer::new(dpr as f32, font::RasterizerConfig::from(&config.font))?; // Initialize glyph cache let glyph_cache = { info!("Initializing glyph cache..."); let init_start = ::std::time::Instant::now(); - let cache = - renderer.with_loader(|mut api| GlyphCache::new(rasterizer, &font, &mut api))?; + let cache = renderer + .with_loader(|mut api| GlyphCache::new(rasterizer, &config.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; diff --git a/alacritty_terminal/src/input.rs b/alacritty_terminal/src/input.rs index 7e87054c7d..5784d9c249 100644 --- a/alacritty_terminal/src/input.rs +++ b/alacritty_terminal/src/input.rs @@ -393,7 +393,6 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { let mouse_mode = TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG | TermMode::MOUSE_REPORT_CLICK; - // Check message bar before URL to ignore URLs in the message bar if let Some(message) = self.message_at_point(Some(point)) { if self.message_close_at_point(point, message) { diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index e7606e6ec9..8682f4260c 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -71,7 +71,6 @@ impl RenderLine { RenderRect::new(start_x + size.padding_x, y + size.padding_y, width, height, self.color) } - } /// Lines for underline and strikeout. diff --git a/alacritty_terminal/src/term/text_run.rs b/alacritty_terminal/src/term/text_run.rs index 2fc38331a6..9093b0b66e 100644 --- a/alacritty_terminal/src/term/text_run.rs +++ b/alacritty_terminal/src/term/text_run.rs @@ -211,10 +211,10 @@ impl TextRunIter { let mut arr: [char; MAX_ZEROWIDTH_CHARS] = Default::default(); arr.copy_from_slice(&chars[1..]); self.buffer_zero_width.push(arr); - } + }, RenderableCellContent::Cursor(cursor) => { self.cursor = Some(cursor); - } + }, } } From e423a089d883d2c265c4c344dfbc07152aca30cb Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Sun, 11 Aug 2019 13:19:07 -0700 Subject: [PATCH 43/84] Reduce scope of hb-ft feature, replace with linux toggled cfg --- alacritty_terminal/Cargo.toml | 2 - alacritty_terminal/src/config/font.rs | 23 +-- alacritty_terminal/src/cursor.rs | 5 +- alacritty_terminal/src/display.rs | 69 +++----- alacritty_terminal/src/renderer/mod.rs | 201 ++++++----------------- alacritty_terminal/src/renderer/rects.rs | 35 ---- alacritty_terminal/src/term/mod.rs | 2 - alacritty_terminal/src/util.rs | 3 +- font/Cargo.toml | 3 +- font/src/ft/mod.rs | 71 +++----- font/src/lib.rs | 137 ++++----------- 11 files changed, 143 insertions(+), 408 deletions(-) diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index 87408c5044..464a137085 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -61,8 +61,6 @@ default = [] live-shader-reload = [] nightly = [] bench = [] -# Use FreeType+FontConfig on all platforms, and enable HarfBuzz text shaping -hb-ft = ["font/hb-ft"] [build-dependencies] gl_generator = "0.13.0" diff --git a/alacritty_terminal/src/config/font.rs b/alacritty_terminal/src/config/font.rs index 96eec7c6ea..88a0574a44 100644 --- a/alacritty_terminal/src/config/font.rs +++ b/alacritty_terminal/src/config/font.rs @@ -1,12 +1,10 @@ use std::fmt; -#[cfg(feature = "hb-ft")] -use font::RasterizeConfig; +use font::RasterizerConfig; use font::Size; use serde::de::Visitor; use serde::{Deserialize, Deserializer}; -#[cfg(any(target_os = "macos", feature = "hb-ft"))] use crate::config::DefaultTrueBool; use crate::config::{failure_default, Delta}; @@ -48,7 +46,6 @@ pub struct Font { use_thin_strokes: DefaultTrueBool, /// Toggles rendering of font ligatures - #[cfg(feature = "hb-ft")] #[serde(deserialize_with = "failure_default")] use_font_ligatures: DefaultTrueBool, } @@ -62,10 +59,10 @@ impl Default for Font { italic: Default::default(), glyph_offset: Default::default(), offset: Default::default(), + #[cfg(not(any(target_os = "macos", windows)))] + use_font_ligatures: Default::default(), #[cfg(target_os = "macos")] use_thin_strokes: Default::default(), - #[cfg(feature = "hb-ft")] - use_font_ligatures: Default::default(), } } } @@ -101,16 +98,20 @@ impl Font { false } - #[cfg(feature = "hb-ft")] + #[cfg(not(any(target_os = "macos", windows)))] pub fn use_font_ligatures(&self) -> bool { self.use_font_ligatures.0 } + + #[cfg(any(target_os = "macos", windows))] + pub fn use_font_ligatures(&self) -> bool { + false + } } -#[cfg(feature = "hb-ft")] -impl<'a> Into for &'a Font { - fn into(self) -> RasterizeConfig { - RasterizeConfig { +impl<'a> Into for &'a Font { + fn into(self) -> RasterizerConfig { + RasterizerConfig { use_thin_strokes: self.use_thin_strokes(), use_font_ligatures: self.use_font_ligatures(), } diff --git a/alacritty_terminal/src/cursor.rs b/alacritty_terminal/src/cursor.rs index 352ed8fb81..b180735667 100644 --- a/alacritty_terminal/src/cursor.rs +++ b/alacritty_terminal/src/cursor.rs @@ -56,10 +56,7 @@ pub fn get_cursor_glyph( } // This default is done as a constant to avoid duplicating the feature toggle for each cursor type. -#[cfg(not(feature = "hb-ft"))] -pub const PLACEHOLDER_GLYPH: char = ' '; -#[cfg(feature = "hb-ft")] -pub const PLACEHOLDER_GLYPH: font::key_type::KeyType = font::key_type::KeyType::GlyphIndex(1u32); +pub const PLACEHOLDER_GLYPH: font::KeyType = font::KeyType::GlyphIndex(1u32); // Returns a custom underline cursor character pub fn get_underline_cursor_glyph(width: i32, line_width: i32) -> RasterizedGlyph { diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 209e792f44..e9d4172ccf 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -30,12 +30,14 @@ use crate::meter::Meter; use crate::renderer::rects::{RenderLines, RenderRect}; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::sync::FairMutex; -use crate::term::color::Rgb; -use crate::term::{RenderableCell, SizeInfo, Term}; +use crate::term::{ + RenderableCell, SizeInfo, Term, + color::Rgb, cell::Flags, +}; use crate::window::{self, Window}; use font::{self, Rasterize}; -#[cfg(feature = "hb-ft")] + use crate::term::text_run::TextRunIter; #[derive(Debug)] @@ -300,11 +302,8 @@ impl Display { renderer: &mut QuadRenderer, config: &Config, ) -> Result<(GlyphCache, f32, f32), Error> { - #[cfg(not(feature = "hb-ft"))] - let rasterizer = font::Rasterizer::new(dpr as f32, config.font.use_thin_strokes())?; - #[cfg(feature = "hb-ft")] let rasterizer = - font::Rasterizer::new(dpr as f32, font::RasterizerConfig::from(&config.font))?; + font::Rasterizer::new(dpr as f32, (&config.font).into())?; // Initialize glyph cache let glyph_cache = { @@ -519,45 +518,23 @@ impl Display { let glyph_cache = &mut self.glyph_cache; let mut lines = RenderLines::new(); - // Draw grid (non-HarfBuzz) - #[cfg(not(feature = "hb-ft"))] - { - 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 underline/strikeout - lines.update(&cell); - - // Draw the cell - api.render_cell(cell, glyph_cache); - } - }); - } - // Draw grid (HarfBuzz) - #[cfg(feature = "hb-ft")] - { - use crate::term::cell::Flags; - let _sampler = self.meter.sampler(); - - self.renderer.with_api(config, &size_info, |mut api| { - // Iterate over each contiguous block of text - for text_run in TextRunIter::new( - grid_cells - .into_iter() - // Logic for WIDE_CHAR is handled internally by TextRun - // So we no longer need WIDE_CHAR_SPACER at this point. - .filter(|rc| !rc.flags.contains(Flags::WIDE_CHAR_SPACER)), - ) { - // Update underline/strikeout - lines.update(&text_run); - - // Draw text run - api.render_text_run(text_run, glyph_cache); - } - }); - } + // Draw grid + self.renderer.with_api(config, &size_info, |mut api| { + // Iterate over each contiguous block of text + for text_run in TextRunIter::new( + grid_cells + .into_iter() + // Logic for WIDE_CHAR is handled internally by TextRun + // So we no longer need WIDE_CHAR_SPACER at this point. + .filter(|rc| !rc.flags.contains(Flags::WIDE_CHAR_SPACER)), + ) { + // Update underline/strikeout + lines.update(&text_run); + + // Draw text run + api.render_text_run(text_run, glyph_cache); + } + }); let mut rects = lines.into_rects(&metrics, &size_info); diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index f46ec5dbe4..2cd9c4c6d0 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -23,20 +23,19 @@ use std::time::Duration; use fnv::FnvHasher; use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer}; +#[cfg(not(any(target_os = "macos", windows)))] +use font::HbFtExt; use glutin::dpi::PhysicalSize; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; use crate::config::{self, Config, Delta}; -use crate::cursor::{get_cursor_glyph, CursorKey}; +use crate::cursor::{get_cursor_glyph, CursorKey, PLACEHOLDER_GLYPH}; use crate::gl; use crate::gl::types::*; use crate::index::{Column, Line}; use crate::renderer::rects::RenderRect; use crate::term::color::Rgb; -#[cfg(feature = "hb-ft")] use crate::term::text_run::{TextRun, TextRunContent}; -#[cfg(not(feature = "hb-ft"))] -use crate::term::RenderableCellContent; use crate::term::{self, cell, RenderableCell}; pub mod rects; @@ -194,16 +193,10 @@ impl GlyphCache { { let (regular, bold, italic) = Self::compute_font_keys(font, &mut rasterizer)?; - // Need to load at least one glyph for the face before calling metrics. - // The glyph requested here ('m' at the time of writing) has no special - // meaning. - #[cfg(not(feature = "hb-ft"))] - rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?; // Need to load at least one glyph for the face before calling metrics. // The glyph requested here (1 at the time of writing) has no special // meaning. - #[cfg(feature = "hb-ft")] - rasterizer.get_glyph(GlyphKey { font_key: regular, c: 1u32.into(), size: font.size })?; + rasterizer.get_glyph(GlyphKey { c: PLACEHOLDER_GLYPH, font_key: regular, size: font.size })?; let metrics = rasterizer.metrics(regular, font.size)?; @@ -228,14 +221,9 @@ impl GlyphCache { fn load_glyphs_for_font(&mut self, font: FontKey, loader: &mut L) { let size = self.font_size; - #[cfg(not(feature = "hb-ft"))] - for i in 32u8..=128u8 { - self.get(GlyphKey { font_key: font, c: i as char, size }, loader); - } - #[cfg(feature = "hb-ft")] for i in 32u32..=128u32 { self.get( - GlyphKey { font_key: font, c: font::key_type::KeyType::GlyphIndex(i), size }, + GlyphKey { font_key: font, c: font::KeyType::GlyphIndex(i), size }, loader, ); } @@ -296,12 +284,28 @@ impl GlyphCache { .expect("metrics load since font is loaded at glyph cache creation") } - #[cfg(feature = "hb-ft")] - pub fn index_for_char(&self, font_key: FontKey, c: char) -> Option { - self.rasterizer.index_for_char(font_key, c) + // Shaping is only avaiable on linux for now + // On other OSs grab run glyphs as normal + #[cfg(any(target_os = "macos", windows))] + fn shape_run<'a, L>( + &'a mut self, + text_run: &str, + font_key: FontKey, + loader: &'a mut L, + ) -> Vec + where + L: LoadGlyph, + { + text_run.chars() + .map(|c| { + let glyph_key = GlyphKey { c: c.into(), font_key, size: self.font_size }; + *self.get(glyph_key, loader) + }) + .collect() } - #[cfg(feature = "hb-ft")] + // Shape using harfbuzz + #[cfg(not(any(target_os = "macos", windows)))] pub fn shape_run<'a, L>( &'a mut self, text_run: &str, @@ -311,7 +315,6 @@ impl GlyphCache { where L: LoadGlyph, { - use font::HbFtExt; self.rasterizer .shape(text_run, font_key) .get_glyph_infos() @@ -319,21 +322,8 @@ impl GlyphCache { .map(move |glyph_info| { let codepoint = glyph_info.codepoint; // Codepoint of 0 indicates a missing or undefined glyph - let c: font::key_type::KeyType = if codepoint == 0 { - // TODO: this is a linear scan over text for each missing glyph - // Try to find all missing glyphs first and only scan over text_run once. - text_run - .char_indices() - .find_map( - |(i, c)| if i == glyph_info.cluster as usize { Some(c) } else { None }, - ) - .unwrap_or_else(|| { - panic!( - "Could not find cluster {} in run {}", - glyph_info.cluster, text_run - ) - }) - .into() + let c: font::KeyType = if codepoint == 0 { + Self::find_fallback_char(text_run, glyph_info.cluster as usize) } else { codepoint.into() }; @@ -343,6 +333,24 @@ impl GlyphCache { .collect() } + #[cfg(not(any(target_os = "macos", windows)))] + fn find_fallback_char(text_run: &str, index: usize) -> font::KeyType { + // TODO: this is a linear scan over text_run for each missing glyph. + // Try to find all missing glyphs first and only scan over text_run once. + text_run + .char_indices() + .find_map( + |(i, c)| if i == index { Some(c) } else { None }, + ) + .unwrap_or_else(|| { + panic!( + "Could not find cluster {} in run {}", + index, text_run + ) + }) + .into() + } + pub fn get<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L) -> &'a Glyph where L: LoadGlyph, @@ -381,12 +389,9 @@ impl GlyphCache { let font = font.to_owned().with_size(size); let (regular, bold, italic) = Self::compute_font_keys(&font, &mut self.rasterizer)?; - #[cfg(not(feature = "hb-ft"))] - self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?; - #[cfg(feature = "hb-ft")] self.rasterizer.get_glyph(GlyphKey { + c: PLACEHOLDER_GLYPH, font_key: regular, - c: 1u32.into(), size: font.size, })?; let metrics = self.rasterizer.metrics(regular, size)?; @@ -412,17 +417,11 @@ impl GlyphCache { pub fn static_metrics(config: &Config, dpr: f32) -> Result { let font = config.font.clone(); - #[cfg(not(feature = "hb-ft"))] - let mut rasterizer = font::Rasterizer::new(dpr, config.font.use_thin_strokes())?; - #[cfg(feature = "hb-ft")] let mut rasterizer = font::Rasterizer::new(dpr, (&config.font).into())?; let regular_desc = GlyphCache::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal); let regular = rasterizer.load_font(®ular_desc, font.size)?; - #[cfg(not(feature = "hb-ft"))] - rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?; - #[cfg(feature = "hb-ft")] - rasterizer.get_glyph(GlyphKey { font_key: regular, c: 1u32.into(), size: font.size })?; + rasterizer.get_glyph(GlyphKey { c: PLACEHOLDER_GLYPH, font_key: regular, size: font.size })?; rasterizer.metrics(regular, font.size) } @@ -1018,43 +1017,6 @@ impl<'a> RenderApi<'a> { /// Render a string in a variable location. Used for printing the render timer, warnings and /// errors. - #[cfg(not(feature = "hb-ft"))] - pub fn render_string( - &mut self, - string: &str, - line: Line, - glyph_cache: &mut GlyphCache, - color: Option, - ) { - 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: cell::Flags::empty(), - bg_alpha, - }) - .collect::>(); - - for cell in cells { - self.render_cell(cell, glyph_cache); - } - } - - /// Render a string in a variable location. Used for printing the render timer, warnings and - /// errors. - #[cfg(feature = "hb-ft")] pub fn render_string( &mut self, string: &str, @@ -1091,7 +1053,6 @@ impl<'a> RenderApi<'a> { } } - #[cfg(feature = "hb-ft")] pub fn render_text_run(&mut self, text_run: TextRun, glyph_cache: &mut GlyphCache) { match &text_run.run_chars { TextRunContent::Cursor(cursor_key) => { @@ -1148,74 +1109,6 @@ impl<'a> RenderApi<'a> { }, }; } - - #[cfg(not(feature = "hb-ft"))] - 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(|| { - let offset_x = self.config.font.offset.x; - let offset_y = self.config.font.offset.y; - - self.load_glyph(&get_cursor_glyph( - cursor_key.style, - metrics, - offset_x, - offset_y, - cursor_key.is_wide, - )) - }); - self.add_render_item(&cell, &glyph); - return; - }, - RenderableCellContent::Chars(chars) => chars, - }; - - // Get font key for cell - // FIXME this is super inefficient. - let font_key = if cell.flags.contains(cell::Flags::BOLD) { - glyph_cache.bold_key - } else if cell.flags.contains(cell::Flags::ITALIC) { - glyph_cache.italic_key - } else { - glyph_cache.font_key - }; - - // Don't render text of HIDDEN cells - let mut chars = if cell.flags.contains(cell::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 diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index 8682f4260c..a926a31c2e 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -18,10 +18,7 @@ use font::Metrics; use crate::index::Point; use crate::term::cell::Flags; use crate::term::color::Rgb; -#[cfg(feature = "hb-ft")] use crate::term::text_run::TextRun; -#[cfg(not(feature = "hb-ft"))] -use crate::term::RenderableCell; use crate::term::SizeInfo; #[derive(Debug, Copy, Clone)] @@ -94,7 +91,6 @@ impl RenderLines { .collect() } - #[cfg(feature = "hb-ft")] /// Update the stored lines with the next text_run info. pub fn update(&mut self, text_run: &TextRun) { for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] { @@ -110,35 +106,4 @@ impl RenderLines { self.inner.entry(*flag).or_insert_with(|| vec![]).push(new_line); } } - - #[cfg(not(feature = "hb-ft"))] - /// 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.line == line.start.line - && cell.fg == line.color - && cell.column == line.end.col + 1 - { - // 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_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index e90c926a02..064945aa2f 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -45,7 +45,6 @@ use crate::tty; pub mod cell; pub mod color; -#[cfg(feature = "hb-ft")] pub mod text_run; /// Used to match equal brackets, when performing a bracket-pair selection. @@ -307,7 +306,6 @@ impl RenderableCell { } } - #[cfg(feature = "hb-ft")] fn is_cursor(&self) -> bool { match &self.inner { RenderableCellContent::Cursor(_) => true, diff --git a/alacritty_terminal/src/util.rs b/alacritty_terminal/src/util.rs index 250fa430dc..7758de0f49 100644 --- a/alacritty_terminal/src/util.rs +++ b/alacritty_terminal/src/util.rs @@ -29,8 +29,7 @@ pub mod thread { /// Like `thread::spawn`, but with a `name` argument pub fn spawn_named(name: S, f: F) -> ::std::thread::JoinHandle where - F: FnOnce() -> T, - F: Send + 'static, + F: FnOnce() -> T + Send + 'static, T: Send + 'static, S: Into, { diff --git a/font/Cargo.toml b/font/Cargo.toml index b57471eac0..9f6e268afc 100644 --- a/font/Cargo.toml +++ b/font/Cargo.toml @@ -11,11 +11,11 @@ libc = "0.2" foreign-types = "0.4" log = "0.4" -harfbuzz_rs = { version = "1.0.0", optional = true } [target.'cfg(not(any(target_os = "macos", windows)))'.dependencies] servo-fontconfig = "0.4.0" freetype-rs = "0.19" +harfbuzz_rs = "1.0.0" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.6" @@ -28,4 +28,3 @@ dwrote = { version = "0.9.0" } [features] default = [] -hb-ft = ["harfbuzz_rs"] diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index de2b3646eb..164f11abce 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -20,15 +20,12 @@ use std::path::PathBuf; use freetype::tt_os2::TrueTypeOS2Table; use freetype::{self, Library}; -#[cfg(feature = "hb-ft")] use harfbuzz_rs::{Font, GlyphBuffer, Owned}; use libc::c_uint; pub mod fc; -#[cfg(feature = "hb-ft")] -use super::{key_type::KeyType, RasterizeConfig}; -use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; +use super::{KeyType, RasterizerConfig, FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; struct FixedSize { pixelsize: f64, @@ -42,7 +39,6 @@ struct Face { lcd_filter: c_uint, non_scalable: Option, /// This is an option just in case hb_ft_create_font_referenced fails. - #[cfg(feature = "hb-ft")] hb_font: Owned>, } @@ -71,7 +67,6 @@ pub struct FreeTypeRasterizer { library: Library, keys: HashMap, device_pixel_ratio: f32, - #[cfg(feature = "hb-ft")] use_font_ligatures: bool, } @@ -83,20 +78,7 @@ fn to_freetype_26_6(f: f32) -> isize { impl ::Rasterize for FreeTypeRasterizer { type Err = Error; - #[cfg(not(feature = "hb-ft"))] - fn new(device_pixel_ratio: f32, _: bool) -> Result { - let library = Library::init()?; - - Ok(FreeTypeRasterizer { - faces: HashMap::new(), - keys: HashMap::new(), - library, - device_pixel_ratio, - }) - } - - #[cfg(feature = "hb-ft")] - fn new(device_pixel_ratio: f32, config: RasterizeConfig) -> Result { + fn new(device_pixel_ratio: f32, config: RasterizerConfig) -> Result { let library = Library::init()?; Ok(FreeTypeRasterizer { @@ -166,7 +148,6 @@ impl ::Rasterize for FreeTypeRasterizer { } } -#[cfg(feature = "hb-ft")] impl ::HbFtExt for FreeTypeRasterizer { fn shape(&mut self, text: &str, font_key: FontKey) -> GlyphBuffer { use harfbuzz_rs::{shape, Feature, Tag, UnicodeBuffer}; @@ -309,7 +290,6 @@ impl FreeTypeRasterizer { }; // Construct harfbuzz font - #[cfg(feature = "hb-ft")] let hb_font = { use harfbuzz_rs::*; let _hb_face = Face::from_file(&path, index as u32)?; @@ -323,7 +303,6 @@ impl FreeTypeRasterizer { render_mode: Self::ft_render_mode(pattern), lcd_filter: Self::ft_lcd_filter(pattern), non_scalable, - #[cfg(feature = "hb-ft")] hb_font, }; @@ -339,39 +318,36 @@ impl FreeTypeRasterizer { } } - #[cfg(feature = "hb-ft")] - pub fn index_for_char(&self, font_key: FontKey, c: char) -> Option { - self.faces.get(&font_key).map(|face| face.ft_face.get_char_index(c as usize)) - } - - #[cfg(feature = "hb-ft")] fn face_for_glyph( &mut self, glyph_key: GlyphKey, have_recursed: bool, ) -> Result { - match glyph_key.c { + let font_key = match glyph_key.c { // We already found a glyph index, use current font - KeyType::GlyphIndex(_) => Ok(glyph_key.font_key), + KeyType::GlyphIndex(_) => glyph_key.font_key, // Harfbuzz failed to find a glyph index, try to load a font for c KeyType::Fallback(c) => { - if have_recursed { - Ok(glyph_key.font_key) - } else { - let key = self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key); - Ok(key) - } + self.handle_fallback_char(c, glyph_key.font_key, have_recursed) }, + }; + Ok(font_key) + } + + + // On unix harfbuzz turns characters into glyph indices. So if we hit this method we already know the glyph is missing from the font and we want to fallback to a system font. + #[cfg(unix)] + fn handle_fallback_char(&mut self, c: char, font_key: FontKey, have_recursed: bool) -> FontKey { + if have_recursed { + font_key + } else { + self.load_face_with_glyph(c).unwrap_or(font_key) } } - #[cfg(not(feature = "hb-ft"))] - fn face_for_glyph( - &mut self, - glyph_key: GlyphKey, - have_recursed: bool, - ) -> Result { - let c = glyph_key.c; + // If we aren't on unix this will be called for all characters so we still want to check configured font first. + #[cfg(not(unix))] + fn handle_fallback_char(&mut self, c: char, font_key: FontKey, have_recursed: bool) -> FontKey { let use_initial_face = if let Some(face) = self.faces.get(&glyph_key.font_key) { let index = face.ft_face.get_char_index(c as usize); @@ -381,10 +357,10 @@ impl FreeTypeRasterizer { }; if use_initial_face { - Ok(glyph_key.font_key) + font_key } else { let key = self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key); - Ok(key) + key } } @@ -392,9 +368,6 @@ impl FreeTypeRasterizer { // Render a normal character if it's not a cursor let font_key = self.face_for_glyph(glyph_key, false)?; let face = &self.faces[&font_key]; - #[cfg(not(feature = "hb-ft"))] - let index = face.ft_face.get_char_index(glyph_key.c as usize); - #[cfg(feature = "hb-ft")] let index = match glyph_key.c { KeyType::GlyphIndex(i) => i, KeyType::Fallback(c) => face.ft_face.get_char_index(c as usize), diff --git a/font/src/lib.rs b/font/src/lib.rs index ae3a767bc4..78c5355ed5 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -20,57 +20,52 @@ #![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)] -// Note: all applicable cfg statements have been modified to short-circuit -// to freetype if the feature hb-ft is enabled. - -#[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] +#[cfg(not(any(target_os = "macos", windows)))] extern crate fontconfig; -#[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] +#[cfg(not(any(target_os = "macos", windows)))] extern crate freetype; -#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] +#[cfg(target_os = "macos")] extern crate core_foundation; -#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] +#[cfg(target_os = "macos")] extern crate core_foundation_sys; -#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] +#[cfg(target_os = "macos")] extern crate core_graphics; -#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] +#[cfg(target_os = "macos")] extern crate core_text; -#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] +#[cfg(target_os = "macos")] extern crate euclid; extern crate libc; -#[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] +#[cfg(not(any(target_os = "macos", windows)))] #[macro_use] extern crate foreign_types; -#[cfg(feature = "hb-ft")] +#[cfg(not(any(target_os = "macos", windows)))] extern crate harfbuzz_rs; #[cfg_attr(not(windows), macro_use)] extern crate log; use std::fmt; -#[cfg(not(feature = "hb-ft"))] -use std::hash::{Hash, Hasher}; use std::sync::atomic::{AtomicUsize, Ordering}; // If target isn't macos or windows, reexport everything from ft -#[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] +#[cfg(not(any(target_os = "macos", windows)))] pub mod ft; -#[cfg(any(not(any(target_os = "macos", windows)), feature = "hb-ft"))] +#[cfg(not(any(target_os = "macos", windows)))] pub use ft::{Error, FreeTypeRasterizer as Rasterizer}; -#[cfg(all(windows, not(feature = "hb-ft")))] +#[cfg(windows)] pub mod directwrite; -#[cfg(all(windows, not(feature = "hb-ft")))] +#[cfg(windows)] pub use crate::directwrite::{DirectWriteRasterizer as Rasterizer, Error}; // If target is macos, reexport everything from darwin -#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] +#[cfg(target_os = "macos")] mod darwin; -#[cfg(all(target_os = "macos", not(feature = "hb-ft")))] +#[cfg(target_os = "macos")] pub use darwin::*; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -142,73 +137,34 @@ impl FontKey { } } -#[cfg(feature = "hb-ft")] -pub mod key_type { - #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] - pub enum KeyType { - // Harfbuzz returned a valid index from shaping and we can render that as expected. - GlyphIndex(u32), - // Harfbuzz returned nodef so we provide character instead to make use of font loading. - Fallback(char), - } - // Import enum variants into this module - use KeyType::{Fallback, GlyphIndex}; +/// Captures possible outcomes of shaping, if shaping succeeded it will return a `GlyphIndex`. +/// If shaping failed or did not occur, `Fallback` will be returned. +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum KeyType { + /// Shaping returned a valid index and we can render that as expected. + GlyphIndex(u32), + /// Shaping returned a missing glyph or shaping did not occur. If glyph is missing system will attempt to load character glyph from a fallback font. If shaping did not occur this will first try the configured font then fallback. + Fallback(char), +} - impl From for KeyType { - fn from(val: u32) -> Self { - GlyphIndex(val) - } +impl From for KeyType { + fn from(val: u32) -> Self { + KeyType::GlyphIndex(val) } - impl From for KeyType { - fn from(val: char) -> Self { - Fallback(val) - } +} +impl From for KeyType { + fn from(val: char) -> Self { + KeyType::Fallback(val) } } -#[cfg(feature = "hb-ft")] -use key_type::KeyType; -// For now use derived impls, this can be swapped for more efficient implementations once things are -// working -#[cfg_attr(feature = "hb-ft", derive(Hash, PartialEq))] -#[derive(Debug, Copy, Clone, Eq)] +#[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] pub struct GlyphKey { - #[cfg(not(feature = "hb-ft"))] - pub c: char, - #[cfg(feature = "hb-ft")] pub c: KeyType, pub font_key: FontKey, pub size: Size, } -#[cfg(not(feature = "hb-ft"))] -impl Hash for GlyphKey { - fn hash(&self, state: &mut H) { - unsafe { - // This transmute is fine: - // - // - If GlyphKey ever becomes a different size, this will fail to compile - // - Result is being used for hashing and has no fields (it's a u64) - ::std::mem::transmute::(*self) - } - .hash(state); - } -} - -#[cfg(not(feature = "hb-ft"))] -impl PartialEq for GlyphKey { - fn eq(&self, other: &Self) -> bool { - unsafe { - // This transmute is fine: - // - // - If GlyphKey ever becomes a different size, this will fail to compile - // - Result is being used for equality checking and has no fields (it's a u64) - let other = ::std::mem::transmute::(*other); - ::std::mem::transmute::(*self).eq(&other) - } - } -} - /// Font size stored as integer #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct Size(i16); @@ -241,9 +197,6 @@ impl ::std::ops::Add for Size { #[derive(Clone)] pub struct RasterizedGlyph { - #[cfg(not(feature = "hb-ft"))] - pub c: char, - #[cfg(feature = "hb-ft")] pub c: KeyType, pub width: i32, pub height: i32, @@ -254,10 +207,7 @@ pub struct RasterizedGlyph { impl Default for RasterizedGlyph { fn default() -> RasterizedGlyph { - #[cfg(feature = "hb-ft")] let c: KeyType = 1u32.into(); - #[cfg(not(feature = "hb-ft"))] - let c = ' '; RasterizedGlyph { c, width: 0, height: 0, top: 0, left: 0, buf: Vec::new() } } } @@ -298,14 +248,7 @@ pub trait Rasterize { /// Errors occurring in Rasterize methods type Err: ::std::error::Error + Send + Sync + 'static; - /// Create a new Rasterizer - #[cfg(not(feature = "hb-ft"))] - fn new(device_pixel_ratio: f32, use_thin_strokes: bool) -> Result - where - Self: Sized; - - #[cfg(feature = "hb-ft")] - fn new(device_pixel_ratio: f32, rasterize_config: RasterizeConfig) -> Result + fn new(device_pixel_ratio: f32, rasterize_config: RasterizerConfig) -> Result where Self: Sized; @@ -322,16 +265,9 @@ pub trait Rasterize { fn update_dpr(&mut self, device_pixel_ratio: f32); } -/// Config option specific to the Rasterizer. -/// Since the Rasterizer lives in the subcrate font we do not want it to depend on the Font config -/// struct from alacritty, as this would introduce a circular dependency between the crates.Clone -/// This struct specifies the subset of Font that the Rasterizer cares about, then traits can be -/// used to convert from Font to this struct when constructing a Rasterizer. -#[cfg(feature = "hb-ft")] -pub struct RasterizeConfig { +/// Config options specific to the Rasterizer. +pub struct RasterizerConfig { /// Toggle thin strokes on mac osx - // Technically this is impossible while under "hb-ft" but is included for compatiblity in the - // api pub use_thin_strokes: bool, /// Toggle rendering of font ligatures pub use_font_ligatures: bool, @@ -339,9 +275,8 @@ pub struct RasterizeConfig { // Only implemented for the FreeType rasterizer so far. /// Conceptually this extends the Rasterizer trait with Harfbuzz specific functionality. -#[cfg(feature = "hb-ft")] +#[cfg(not(any(target_os = "macos", windows)))] pub trait HbFtExt { /// Shape the provided text into a set of glyphs. - /// TODO: properly report HarfBuzz errors fn shape(&mut self, text: &str, font_key: FontKey) -> harfbuzz_rs::GlyphBuffer; } From 922cd41b1f811a0f48cd9502b883947cfbbedd0b Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Sun, 11 Aug 2019 14:22:26 -0700 Subject: [PATCH 44/84] Resolving review comments in text_run and ft --- alacritty_terminal/src/renderer/mod.rs | 13 +- alacritty_terminal/src/renderer/rects.rs | 2 +- alacritty_terminal/src/term/text_run.rs | 151 ++++++++++++----------- font/src/ft/mod.rs | 20 ++- 4 files changed, 96 insertions(+), 90 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 2cd9c4c6d0..e3376cdc10 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -1028,8 +1028,8 @@ impl<'a> RenderApi<'a> { let text_run = TextRun { line, - run: (Column(0), Column(string.len() - 1)), - run_chars: TextRunContent::CharRun(string.to_owned(), vec![]), + span: (Column(0), Column(string.len() - 1)), + content: TextRunContent::CharRun(string.to_owned(), vec![]), fg: Rgb { r: 0, g: 0, b: 0 }, bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0 }), flags: cell::Flags::empty(), @@ -1054,7 +1054,7 @@ impl<'a> RenderApi<'a> { } pub fn render_text_run(&mut self, text_run: TextRun, glyph_cache: &mut GlyphCache) { - match &text_run.run_chars { + match &text_run.content { TextRunContent::Cursor(cursor_key) => { // Raw cell pixel buffers like cursors don't need to go through font lookup let metrics = glyph_cache.metrics; @@ -1081,16 +1081,15 @@ impl<'a> RenderApi<'a> { glyph_cache.font_key }; - let hidden = text_run.flags.contains(cell::Flags::HIDDEN); - if !hidden { + if !text_run.flags.contains(cell::Flags::HIDDEN) { let glyphs = glyph_cache.shape_run(&run, font_key, self); for (cell, glyph) in text_run.cell_iter().zip(glyphs.into_iter()) { self.add_render_item(&cell, &glyph); } }; - for (cell, chars) in text_run.cell_iter().zip(zero_widths.iter()) { - for c in chars.iter().filter(|c| **c != ' ') { + for (cell, zero_width_chars) in text_run.cell_iter().zip(zero_widths.iter()) { + for c in zero_width_chars.iter().filter(|c| **c != ' ') { let glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: (*c).into() }; let average_advance = glyph_cache.metrics.average_advance as f32; diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index a926a31c2e..7d8c7fbbba 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -100,7 +100,7 @@ impl RenderLines { let new_line = RenderLine { start: text_run.start_point(), - end: text_run.last_point(), + end: text_run.end_point(), color: text_run.fg, }; self.inner.entry(*flag).or_insert_with(|| vec![]).push(new_line); diff --git a/alacritty_terminal/src/term/text_run.rs b/alacritty_terminal/src/term/text_run.rs index 9093b0b66e..c037271b8a 100644 --- a/alacritty_terminal/src/term/text_run.rs +++ b/alacritty_terminal/src/term/text_run.rs @@ -6,7 +6,7 @@ use super::{ use crate::index::{Column, Line}; #[derive(Debug)] -pub(crate) struct RunStart { +struct RunStart { pub line: Line, pub column: Column, pub fg: Rgb, @@ -14,6 +14,7 @@ pub(crate) struct RunStart { pub bg_alpha: f32, pub flags: Flags, } + // Use a macro instead of a function to make use of partial move semantics that don't cross // function boundaries // Convert a RenderableCell into a RunStart @@ -31,19 +32,19 @@ macro_rules! from_rc { } /// Compare cells and check they are in the same text run -fn is_contiguous_context(a: &RunStart, b: &RenderableCell) -> bool { +fn is_adjacent_context(a: &RunStart, b: &RenderableCell) -> bool { a.line == b.line && a.fg == b.fg && a.bg == b.bg - && (a.bg_alpha - b.bg_alpha).abs() < 0.01 + && (a.bg_alpha - b.bg_alpha).abs() < std::f32::EPSILON && a.flags == b.flags } -type Latest = (Column, bool); +type LatestCol = (Column, bool); /// Checks two columns are adjacent -fn is_contiguous_col((a, is_wide): Latest, b: Column) -> bool { - let span = if is_wide { 2usize } else { 1usize }; - a + span == b || b + span == a +fn is_adjacent_col((a, is_wide): LatestCol, b: Column) -> bool { + let width = if is_wide { 2usize } else { 1usize }; + a + width == b || b + width == a } #[derive(Debug)] @@ -54,31 +55,36 @@ pub enum TextRunContent { /// Represents a set of renderable cells that all share the same rendering propreties. /// The assumption is that if two cells are in the same TextRun they can be sent off together to -/// be shaped by Harfbuzz. This allows for ligatures to be rendered but not when something -/// breaks up a ligature (e.g. selection hightlight) which is desired behavior. +/// be shaped. This allows for ligatures to be rendered but not when something breaks up a ligature (e.g. selection hightlight) which is desired behavior. #[derive(Debug)] pub struct TextRun { - // By definition a run is on one line. + /// By definition a run is on one line. pub line: Line, - pub run: (Column, Column), - pub run_chars: TextRunContent, + /// Span of columns the text run covers. + pub span: (Column, Column), + /// Cursor or sequence of characters. + pub content: TextRunContent, + /// Foreground color of text run content. pub fg: Rgb, + /// Background color of text run content. pub bg: Rgb, + /// Opacity of background color of text run content. pub bg_alpha: f32, + /// Attributes of this text run (e.g. HIDDEN, STRIKETHROUGH, etc.) pub flags: Flags, } impl TextRun { // These two constructors are used by TextRunIter and are not widely applicable - pub(crate) fn from_iter_state( + fn from_iter_state( start: RunStart, - (latest, is_wide): Latest, + (latest, is_wide): LatestCol, buffer: (String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), ) -> Self { let end_column = if is_wide { latest + 1 } else { latest }; TextRun { line: start.line, - run: (start.column, end_column), - run_chars: TextRunContent::CharRun(buffer.0, buffer.1), + span: (start.column, end_column), + content: TextRunContent::CharRun(buffer.0, buffer.1), fg: start.fg, bg: start.bg, bg_alpha: start.bg_alpha, @@ -86,11 +92,11 @@ impl TextRun { } } - pub(crate) fn from_cursor_rc(start: RunStart, cursor: CursorKey) -> Self { + fn from_cursor_rc(start: RunStart, cursor: CursorKey) -> Self { TextRun { line: start.line, - run: (start.column, start.column), - run_chars: TextRunContent::Cursor(cursor), + span: (start.column, start.column), + content: TextRunContent::Cursor(cursor), fg: start.fg, bg: start.bg, bg_alpha: start.bg_alpha, @@ -99,7 +105,7 @@ impl TextRun { } /// Holdover method while converting from rendering Cells to TextRuns - pub fn cell_at(&self, col: Column) -> RenderableCell { + fn cell_at(&self, col: Column) -> RenderableCell { RenderableCell { line: self.line, column: col, @@ -113,50 +119,50 @@ impl TextRun { /// Number of columns this TextRun spans pub fn len(&self) -> usize { - let (start, end) = self.run; + let (start, end) = self.span; end.0 - start.0 } /// True if TextRun contains characters, false if it contains no characters. pub fn is_empty(&self) -> bool { - match &self.run_chars { - TextRunContent::Cursor(_) => true, + match &self.content { + TextRunContent::Cursor(_) => false, TextRunContent::CharRun(ref string, ref zero_widths) => { - !(string.is_empty() && zero_widths.is_empty()) + string.is_empty() && zero_widths.is_empty() }, } } /// First column of the run pub fn start_col(&self) -> Column { - self.run.0 + self.span.0 } /// First cell in the TextRun pub fn start_cell(&self) -> RenderableCell { - self.cell_at(self.run.0) + self.cell_at(self.span.0) } - /// Last cell in the TextRun - pub fn last_cell(&self) -> RenderableCell { - self.cell_at(self.run.1) + /// End cell in the TextRun + pub fn end_cell(&self) -> RenderableCell { + self.cell_at(self.span.1) } /// First point covered by this TextRun pub fn start_point(&self) -> Point { - Point { line: self.line, col: self.run.0 } + Point { line: self.line, col: self.span.0 } } - /// Last point covered by this TextRun - pub fn last_point(&self) -> Point { - Point { line: self.line, col: self.run.1 } + /// End point covered by this TextRun + pub fn end_point(&self) -> Point { + Point { line: self.line, col: self.span.1 } } /// Returns iterator over range of columns [run.0, run.1] - pub fn col_iter(&self) -> impl Iterator { - let (start, end) = self.run; + pub fn cols(&self) -> impl Iterator { + let (start, end) = self.span; let step = if self.flags.contains(Flags::WIDE_CHAR) { - // If our run contains wide chars treat each cell like it's 2 cells wide + // If our run contains ide chars treat each cell like it's 2 cells wide 2 } else { 1 @@ -167,8 +173,8 @@ impl TextRun { } /// Iterates over each RenderableCell in column range [run.0, run.1] - pub fn cell_iter<'a>(&'a self) -> impl Iterator + 'a { - self.col_iter().map(move |col| self.cell_at(col)) + pub fn cells<'a>(&'a self) -> impl Iterator + 'a { + self.cols().map(move |col| self.cell_at(col)) } } @@ -176,7 +182,7 @@ impl TextRun { pub struct TextRunIter { iter: I, run_start: Option, - latest: Option, + latest: Option, cursor: Option, buffer_text: String, buffer_zero_width: Vec<[char; MAX_ZEROWIDTH_CHARS]>, @@ -194,12 +200,12 @@ impl TextRunIter { } /// Check if current run ends with incoming RenderableCell - fn is_run_break(&self, rc: &RenderableCell) -> bool { - let is_cell_break = - self.run_start.as_ref().map(|cell| !is_contiguous_context(cell, &rc)).unwrap_or(false); - let is_col_break = - self.latest.as_ref().map(|col| !is_contiguous_col(*col, rc.column)).unwrap_or(false); - is_cell_break || is_col_break + fn is_end_of_run(&self, rc: &RenderableCell) -> bool { + let is_cell_adjacent = + self.run_start.as_ref().map(|cell| !is_adjacent_context(cell, &rc)).unwrap_or(false); + let is_col_adjacent = + self.latest.as_ref().map(|col| !is_adjacent_col(*col, rc.column)).unwrap_or(false); + is_cell_adjacent || is_col_adjacent } /// Add content of cell to pending TextRun buffer @@ -224,19 +230,30 @@ impl TextRunIter { } /// Handles bookkeeping needed when starting a new run - fn start_run(&mut self, rc: RenderableCell) -> (Option, Option) { + fn start_run(&mut self, rc: RenderableCell) -> (Option, Option) { self.buffer_content(rc.inner); let latest = self.latest.replace((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); let start = self.run_start.replace(from_rc!(rc)); (start, latest) } -} -/// Utility method to ease use of Options when you need to unwrap both in tandem -fn opt_pair(a: Option, b: Option) -> Option<(A, B)> { - match (a, b) { - (Some(a_val), Some(b_val)) => Some((a_val, b_val)), - _ => None, + /// Producer a run containing a single cursor from state of the `TextRunIter`. + /// This is a destructive operation, the iterator will be in a new run state after it's completion. + fn produce_cursor(&mut self, rc: RenderableCell) -> Option { + let (opt_start, _) = self.start_run(rc); + let start = opt_start?; + let cursor = self.cursor.take()?; + Some(TextRun::from_cursor_rc(start, cursor)) + } + + /// Create a run of chars from the current state of the `TextRunIter`. + /// This is a destructive operation, the iterator will be in a new run state after it's completion. + fn produce_char_run(&mut self, rc: RenderableCell) -> Option { + let prev_buffer = self.drain_buffer(); + let (start_opt, latest_opt) = self.start_run(rc); + let start = start_opt?; + let latest = latest_opt?; + Some(TextRun::from_iter_state(start, latest, prev_buffer)) } } @@ -249,7 +266,6 @@ where fn next(&mut self) -> Option { let mut output = None; while let Some(rc) = self.iter.next() { - // We don't want to add wide_char spacers to the buffer if self.latest.is_none() || self.run_start.is_none() { // Initial state, this is should only be hit on the first next() call of // iterator @@ -257,33 +273,30 @@ where } else if self.cursor.is_some() { // Last iteration of the loop found a cursor // Return a run for the cursor and start a new run - let (start, _) = self.start_run(rc); - output = opt_pair(start, self.cursor.take()) - .map(|(start, cursor)| TextRun::from_cursor_rc(start, cursor)); + output = self.produce_cursor(rc); break; - } else if self.is_run_break(&rc) || rc.is_cursor() { - // If we find a break or a cursor, + } else if self.is_end_of_run(&rc) || rc.is_cursor() { + // If we find a run break or a cursor, // return what we have so far and start a new run. - let prev_buffer = self.drain_buffer(); - let (start, latest) = self.start_run(rc); - output = opt_pair(start, latest) - .map(|(start, latest)| TextRun::from_iter_state(start, latest, prev_buffer)); + output = self.produce_char_run(rc); break; } // Build up buffer and track the latest column we've seen self.latest = Some((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); self.buffer_content(rc.inner); } - // If we generated output we're done - // Otherwise check for any remaining buffered content and return it as a text run - // This a destructive operation so it will return None after it excutes once + // If we generated output we're done. + // Otherwise check for any remaining buffered content and return it as a text run. + // This is a destructive operation, it will return None after it excutes once. output.or_else(|| { if !self.buffer_text.is_empty() || !self.buffer_zero_width.is_empty() { - opt_pair(self.run_start.take(), self.latest.take()).map(|(start, latest)| - // Save leftover buffer and empty it - TextRun::from_iter_state(start, latest, self.drain_buffer())) + let start = self.run_start.take()?; + let latest = self.latest.take()?; + // Save leftover buffer and empty it + Some(TextRun::from_iter_state(start, latest, self.drain_buffer())) } else if let Some(cursor) = self.cursor { - self.run_start.take().map(|start| TextRun::from_cursor_rc(start, cursor)) + let start = self.run_start.take()?; + Some(TextRun::from_cursor_rc(start, cursor)) } else { None } diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 164f11abce..9dee81bad7 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -20,7 +20,8 @@ use std::path::PathBuf; use freetype::tt_os2::TrueTypeOS2Table; use freetype::{self, Library}; -use harfbuzz_rs::{Font, GlyphBuffer, Owned}; +use harfbuzz_rs::{shape, Feature, Tag, UnicodeBuffer, Font, GlyphBuffer, Owned}; +use harfbuzz_rs::Face as HbFace; use libc::c_uint; pub mod fc; @@ -148,21 +149,15 @@ impl ::Rasterize for FreeTypeRasterizer { } } -impl ::HbFtExt for FreeTypeRasterizer { +impl crate::HbFtExt for FreeTypeRasterizer { fn shape(&mut self, text: &str, font_key: FontKey) -> GlyphBuffer { - use harfbuzz_rs::{shape, Feature, Tag, UnicodeBuffer}; let hb_font = &self.faces[&font_key].hb_font; let buf = UnicodeBuffer::default().add_str(text); - let value = if self.use_font_ligatures { 1 } else { 0 }; + let use_font_ligature = if self.use_font_ligatures { 1 } else { 0 }; let features = &[ - Feature::new(Tag::new('c', 'l', 'i', 'g'), value, 0..), - Feature::new(Tag::new('l', 'i', 'g', 'a'), value, 0..), - /* This feature controls ligature rendering in Fira Code but this could - * very well be Fira Code specific behavior as it codes all it's "ligatures" as - * glyph substitutions Which are unaffected by features "clig" and - * "liga" Feature::new(Tag::new('c', 'a', 'l', 't'), value, 0..) */ + Feature::new(Tag::new('l', 'i', 'g', 'a'), use_font_ligature, 0..), + Feature::new(Tag::new('c', 'a', 'l', 't'), use_font_ligature, 0..) ]; - // Shape with default features shape(&*hb_font, buf, features) } } @@ -291,8 +286,7 @@ impl FreeTypeRasterizer { // Construct harfbuzz font let hb_font = { - use harfbuzz_rs::*; - let _hb_face = Face::from_file(&path, index as u32)?; + let _hb_face = HbFace::from_file(&path, index as u32)?; Font::new(_hb_face) }; From c7c634178bbafc1d46e27a16390ea1f50e0bbdce Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Sun, 11 Aug 2019 14:23:10 -0700 Subject: [PATCH 45/84] Run fmt over refactored code. --- alacritty_terminal/src/display.rs | 9 ++----- alacritty_terminal/src/renderer/mod.rs | 33 ++++++++++++------------- alacritty_terminal/src/term/text_run.rs | 11 ++++++--- font/src/ft/mod.rs | 20 ++++++++------- font/src/lib.rs | 6 +++-- 5 files changed, 40 insertions(+), 39 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index e9d4172ccf..2ff4baf4b5 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -30,14 +30,10 @@ use crate::meter::Meter; use crate::renderer::rects::{RenderLines, RenderRect}; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::sync::FairMutex; -use crate::term::{ - RenderableCell, SizeInfo, Term, - color::Rgb, cell::Flags, -}; +use crate::term::{cell::Flags, color::Rgb, RenderableCell, SizeInfo, Term}; use crate::window::{self, Window}; use font::{self, Rasterize}; - use crate::term::text_run::TextRunIter; #[derive(Debug)] @@ -302,8 +298,7 @@ impl Display { renderer: &mut QuadRenderer, config: &Config, ) -> Result<(GlyphCache, f32, f32), Error> { - let rasterizer = - font::Rasterizer::new(dpr as f32, (&config.font).into())?; + let rasterizer = font::Rasterizer::new(dpr as f32, (&config.font).into())?; // Initialize glyph cache let glyph_cache = { diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index e3376cdc10..a3d2585884 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -22,9 +22,9 @@ use std::sync::mpsc; use std::time::Duration; use fnv::FnvHasher; -use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer}; #[cfg(not(any(target_os = "macos", windows)))] use font::HbFtExt; +use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer}; use glutin::dpi::PhysicalSize; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; @@ -196,7 +196,11 @@ impl GlyphCache { // Need to load at least one glyph for the face before calling metrics. // The glyph requested here (1 at the time of writing) has no special // meaning. - rasterizer.get_glyph(GlyphKey { c: PLACEHOLDER_GLYPH, font_key: regular, size: font.size })?; + rasterizer.get_glyph(GlyphKey { + c: PLACEHOLDER_GLYPH, + font_key: regular, + size: font.size, + })?; let metrics = rasterizer.metrics(regular, font.size)?; @@ -222,10 +226,7 @@ impl GlyphCache { fn load_glyphs_for_font(&mut self, font: FontKey, loader: &mut L) { let size = self.font_size; for i in 32u32..=128u32 { - self.get( - GlyphKey { font_key: font, c: font::KeyType::GlyphIndex(i), size }, - loader, - ); + self.get(GlyphKey { font_key: font, c: font::KeyType::GlyphIndex(i), size }, loader); } } @@ -296,7 +297,8 @@ impl GlyphCache { where L: LoadGlyph, { - text_run.chars() + text_run + .chars() .map(|c| { let glyph_key = GlyphKey { c: c.into(), font_key, size: self.font_size }; *self.get(glyph_key, loader) @@ -339,15 +341,8 @@ impl GlyphCache { // Try to find all missing glyphs first and only scan over text_run once. text_run .char_indices() - .find_map( - |(i, c)| if i == index { Some(c) } else { None }, - ) - .unwrap_or_else(|| { - panic!( - "Could not find cluster {} in run {}", - index, text_run - ) - }) + .find_map(|(i, c)| if i == index { Some(c) } else { None }) + .unwrap_or_else(|| panic!("Could not find cluster {} in run {}", index, text_run)) .into() } @@ -421,7 +416,11 @@ impl GlyphCache { let regular_desc = GlyphCache::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal); let regular = rasterizer.load_font(®ular_desc, font.size)?; - rasterizer.get_glyph(GlyphKey { c: PLACEHOLDER_GLYPH, font_key: regular, size: font.size })?; + rasterizer.get_glyph(GlyphKey { + c: PLACEHOLDER_GLYPH, + font_key: regular, + size: font.size, + })?; rasterizer.metrics(regular, font.size) } diff --git a/alacritty_terminal/src/term/text_run.rs b/alacritty_terminal/src/term/text_run.rs index c037271b8a..e338eaba9d 100644 --- a/alacritty_terminal/src/term/text_run.rs +++ b/alacritty_terminal/src/term/text_run.rs @@ -55,7 +55,8 @@ pub enum TextRunContent { /// Represents a set of renderable cells that all share the same rendering propreties. /// The assumption is that if two cells are in the same TextRun they can be sent off together to -/// be shaped. This allows for ligatures to be rendered but not when something breaks up a ligature (e.g. selection hightlight) which is desired behavior. +/// be shaped. This allows for ligatures to be rendered but not when something breaks up a ligature +/// (e.g. selection hightlight) which is desired behavior. #[derive(Debug)] pub struct TextRun { /// By definition a run is on one line. @@ -237,8 +238,9 @@ impl TextRunIter { (start, latest) } - /// Producer a run containing a single cursor from state of the `TextRunIter`. - /// This is a destructive operation, the iterator will be in a new run state after it's completion. + /// Producer a run containing a single cursor from state of the `TextRunIter`. + /// This is a destructive operation, the iterator will be in a new run state after it's + /// completion. fn produce_cursor(&mut self, rc: RenderableCell) -> Option { let (opt_start, _) = self.start_run(rc); let start = opt_start?; @@ -247,7 +249,8 @@ impl TextRunIter { } /// Create a run of chars from the current state of the `TextRunIter`. - /// This is a destructive operation, the iterator will be in a new run state after it's completion. + /// This is a destructive operation, the iterator will be in a new run state after it's + /// completion. fn produce_char_run(&mut self, rc: RenderableCell) -> Option { let prev_buffer = self.drain_buffer(); let (start_opt, latest_opt) = self.start_run(rc); diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 9dee81bad7..510ff3eab9 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -20,13 +20,16 @@ use std::path::PathBuf; use freetype::tt_os2::TrueTypeOS2Table; use freetype::{self, Library}; -use harfbuzz_rs::{shape, Feature, Tag, UnicodeBuffer, Font, GlyphBuffer, Owned}; use harfbuzz_rs::Face as HbFace; +use harfbuzz_rs::{shape, Feature, Font, GlyphBuffer, Owned, Tag, UnicodeBuffer}; use libc::c_uint; pub mod fc; -use super::{KeyType, RasterizerConfig, FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; +use super::{ + FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, RasterizerConfig, Size, Slant, + Style, Weight, +}; struct FixedSize { pixelsize: f64, @@ -156,7 +159,7 @@ impl crate::HbFtExt for FreeTypeRasterizer { let use_font_ligature = if self.use_font_ligatures { 1 } else { 0 }; let features = &[ Feature::new(Tag::new('l', 'i', 'g', 'a'), use_font_ligature, 0..), - Feature::new(Tag::new('c', 'a', 'l', 't'), use_font_ligature, 0..) + Feature::new(Tag::new('c', 'a', 'l', 't'), use_font_ligature, 0..), ]; shape(&*hb_font, buf, features) } @@ -321,15 +324,13 @@ impl FreeTypeRasterizer { // We already found a glyph index, use current font KeyType::GlyphIndex(_) => glyph_key.font_key, // Harfbuzz failed to find a glyph index, try to load a font for c - KeyType::Fallback(c) => { - self.handle_fallback_char(c, glyph_key.font_key, have_recursed) - }, + KeyType::Fallback(c) => self.handle_fallback_char(c, glyph_key.font_key, have_recursed), }; Ok(font_key) } - - // On unix harfbuzz turns characters into glyph indices. So if we hit this method we already know the glyph is missing from the font and we want to fallback to a system font. + // On unix harfbuzz turns characters into glyph indices. So if we hit this method we already + // know the glyph is missing from the font and we want to fallback to a system font. #[cfg(unix)] fn handle_fallback_char(&mut self, c: char, font_key: FontKey, have_recursed: bool) -> FontKey { if have_recursed { @@ -339,7 +340,8 @@ impl FreeTypeRasterizer { } } - // If we aren't on unix this will be called for all characters so we still want to check configured font first. + // If we aren't on unix this will be called for all characters so we still want to check + // configured font first. #[cfg(not(unix))] fn handle_fallback_char(&mut self, c: char, font_key: FontKey, have_recursed: bool) -> FontKey { let use_initial_face = if let Some(face) = self.faces.get(&glyph_key.font_key) { diff --git a/font/src/lib.rs b/font/src/lib.rs index 78c5355ed5..8c96ef65ad 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -137,13 +137,15 @@ impl FontKey { } } -/// Captures possible outcomes of shaping, if shaping succeeded it will return a `GlyphIndex`. +/// Captures possible outcomes of shaping, if shaping succeeded it will return a `GlyphIndex`. /// If shaping failed or did not occur, `Fallback` will be returned. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum KeyType { /// Shaping returned a valid index and we can render that as expected. GlyphIndex(u32), - /// Shaping returned a missing glyph or shaping did not occur. If glyph is missing system will attempt to load character glyph from a fallback font. If shaping did not occur this will first try the configured font then fallback. + /// Shaping returned a missing glyph or shaping did not occur. If glyph is missing system will + /// attempt to load character glyph from a fallback font. If shaping did not occur this will + /// first try the configured font then fallback. Fallback(char), } From 549acfc4e45f57c7b99c1952a53d8f03527cb490 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Sun, 11 Aug 2019 14:27:17 -0700 Subject: [PATCH 46/84] Change cell_iter() -> cells() at unchanged callsites. --- alacritty_terminal/src/renderer/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index a3d2585884..8e84d8c011 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -1082,12 +1082,12 @@ impl<'a> RenderApi<'a> { if !text_run.flags.contains(cell::Flags::HIDDEN) { let glyphs = glyph_cache.shape_run(&run, font_key, self); - for (cell, glyph) in text_run.cell_iter().zip(glyphs.into_iter()) { + for (cell, glyph) in text_run.cells().zip(glyphs.into_iter()) { self.add_render_item(&cell, &glyph); } }; - for (cell, zero_width_chars) in text_run.cell_iter().zip(zero_widths.iter()) { + for (cell, zero_width_chars) in text_run.cells().zip(zero_widths.iter()) { for c in zero_width_chars.iter().filter(|c| **c != ' ') { let glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: (*c).into() }; From cd5201e5bcbd5bb92e7631a97a9304f4fbc3e156 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Sun, 11 Aug 2019 15:06:11 -0700 Subject: [PATCH 47/84] Don't update strikethrough and underline for hidden runs. --- alacritty_terminal/src/renderer/rects.rs | 3 ++ alacritty_terminal/src/term/text_run.rs | 35 ++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index 7d8c7fbbba..0232e2039c 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -93,6 +93,9 @@ impl RenderLines { /// Update the stored lines with the next text_run info. pub fn update(&mut self, text_run: &TextRun) { + if text_run.flags.contains(Flags::HIDDEN) { + return; + } for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] { if !text_run.flags.contains(*flag) { continue; diff --git a/alacritty_terminal/src/term/text_run.rs b/alacritty_terminal/src/term/text_run.rs index e338eaba9d..af74613b52 100644 --- a/alacritty_terminal/src/term/text_run.rs +++ b/alacritty_terminal/src/term/text_run.rs @@ -47,17 +47,31 @@ fn is_adjacent_col((a, is_wide): LatestCol, b: Column) -> bool { a + width == b || b + width == a } -#[derive(Debug)] pub enum TextRunContent { Cursor(CursorKey), CharRun(String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), } +impl std::fmt::Debug for TextRunContent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TextRunContent::Cursor(cursor) => { + f.write_str("Cursor(")?; + cursor.fmt(f)?; + f.write_str(")") + } + TextRunContent::CharRun(text, _) => { + f.write_str("CharRun(")?; + text.fmt(f)?; + f.write_str(")") + } + } + } +} /// Represents a set of renderable cells that all share the same rendering propreties. /// The assumption is that if two cells are in the same TextRun they can be sent off together to /// be shaped. This allows for ligatures to be rendered but not when something breaks up a ligature /// (e.g. selection hightlight) which is desired behavior. -#[derive(Debug)] pub struct TextRun { /// By definition a run is on one line. pub line: Line, @@ -74,6 +88,21 @@ pub struct TextRun { /// Attributes of this text run (e.g. HIDDEN, STRIKETHROUGH, etc.) pub flags: Flags, } + +impl std::fmt::Debug for TextRun { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TextRun") + .field("line", &self.line) + .field("span", &(self.span.0..=self.span.1)) + .field("content", &self.content) + .field("fg", &format!("#{:X}{:X}{:X}", self.fg.r, self.fg.b, self.fg.g)) + .field("bg", &format!("#{:X}{:X}{:X}", self.bg.r, self.bg.b, self.bg.g)) + .field("bg_alpha", &self.bg_alpha) + .field("flags", &self.flags) + .finish() + } +} + impl TextRun { // These two constructors are used by TextRunIter and are not widely applicable fn from_iter_state( @@ -238,7 +267,7 @@ impl TextRunIter { (start, latest) } - /// Producer a run containing a single cursor from state of the `TextRunIter`. + /// Produce a run containing a single cursor from state of the `TextRunIter`. /// This is a destructive operation, the iterator will be in a new run state after it's /// completion. fn produce_cursor(&mut self, rc: RenderableCell) -> Option { From 36776691ca72412444ddb2473af477568c7a29a1 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Sun, 11 Aug 2019 15:14:33 -0700 Subject: [PATCH 48/84] Need to rework handling of hidden text. --- alacritty_terminal/src/renderer/rects.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index 0232e2039c..7d8c7fbbba 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -93,9 +93,6 @@ impl RenderLines { /// Update the stored lines with the next text_run info. pub fn update(&mut self, text_run: &TextRun) { - if text_run.flags.contains(Flags::HIDDEN) { - return; - } for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] { if !text_run.flags.contains(*flag) { continue; From 1708927e5eedbab03c73974cecb809729c17c114 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Sat, 17 Aug 2019 10:43:46 -0700 Subject: [PATCH 49/84] Fix rendering of hidden text based on broken.txt --- alacritty_terminal/src/renderer/mod.rs | 45 ++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 8e84d8c011..494f56db41 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -1080,14 +1080,47 @@ impl<'a> RenderApi<'a> { glyph_cache.font_key }; - if !text_run.flags.contains(cell::Flags::HIDDEN) { - let glyphs = glyph_cache.shape_run(&run, font_key, self); - for (cell, glyph) in text_run.cells().zip(glyphs.into_iter()) { - self.add_render_item(&cell, &glyph); + fn hidden_glyph_iter<'a, L>(font_key: FontKey, font_size: font::Size, glyph_cache: &'a mut GlyphCache, loader: &mut L) -> std::iter::Repeat + where + L: LoadGlyph + { + // If text_run is hidden we don't want to shape text. + // But we still want to run each cell through add_render_item so colors get handled appropiately. + // We construct a dummy key that should bee cached a majority of the time so that we have a valid glyph. + let key = GlyphKey { + c: crate::cursor::PLACEHOLDER_GLYPH, + font_key, + size: font_size, + }; + let glyph = *glyph_cache.get(key, loader); + std::iter::repeat(glyph) + } + enum ShapedGlyphIter { + /// Our run was not hidden and our glyphs were shaped + Shaped(std::vec::IntoIter), + /// Our run is hidden and was not shaped + Hidden(std::iter::Repeat), + } + impl<'a> Iterator for ShapedGlyphIter { + type Item = Glyph; + + fn next(&mut self) -> Option { + match self { + ShapedGlyphIter::Hidden(inner) => inner.next(), + ShapedGlyphIter::Shaped(inner) => inner.next(), + } } - }; + } - for (cell, zero_width_chars) in text_run.cells().zip(zero_widths.iter()) { + let shaped_glyphs: ShapedGlyphIter = if text_run.flags.contains(cell::Flags::HIDDEN) { + ShapedGlyphIter::Hidden(hidden_glyph_iter(font_key, glyph_cache.font_size, glyph_cache, self)) + } else { + ShapedGlyphIter::Shaped(glyph_cache.shape_run(&run, font_key, self).into_iter()) + }; + for ((cell, glyph), zero_width_chars) in text_run.cells() + .zip(shaped_glyphs) + .zip(zero_widths.iter()) { + self.add_render_item(&cell, &glyph); for c in zero_width_chars.iter().filter(|c| **c != ' ') { let glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: (*c).into() }; From a181714028e180e269c1248b01b228462355307a Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Sat, 17 Aug 2019 10:50:55 -0700 Subject: [PATCH 50/84] Move code for hidden glyphs outside of render_text_run and format code. --- alacritty_terminal/src/renderer/mod.rs | 82 ++++++++++++++----------- alacritty_terminal/src/term/text_run.rs | 22 +++---- 2 files changed, 58 insertions(+), 46 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 494f56db41..b14cb537c0 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -1080,46 +1080,21 @@ impl<'a> RenderApi<'a> { glyph_cache.font_key }; - fn hidden_glyph_iter<'a, L>(font_key: FontKey, font_size: font::Size, glyph_cache: &'a mut GlyphCache, loader: &mut L) -> std::iter::Repeat - where - L: LoadGlyph + let shaped_glyphs: ShapedGlyphIter = if text_run.flags.contains(cell::Flags::HIDDEN) { - // If text_run is hidden we don't want to shape text. - // But we still want to run each cell through add_render_item so colors get handled appropiately. - // We construct a dummy key that should bee cached a majority of the time so that we have a valid glyph. - let key = GlyphKey { - c: crate::cursor::PLACEHOLDER_GLYPH, + ShapedGlyphIter::Hidden(hidden_glyph_iter( font_key, - size: font_size, - }; - let glyph = *glyph_cache.get(key, loader); - std::iter::repeat(glyph) - } - enum ShapedGlyphIter { - /// Our run was not hidden and our glyphs were shaped - Shaped(std::vec::IntoIter), - /// Our run is hidden and was not shaped - Hidden(std::iter::Repeat), - } - impl<'a> Iterator for ShapedGlyphIter { - type Item = Glyph; - - fn next(&mut self) -> Option { - match self { - ShapedGlyphIter::Hidden(inner) => inner.next(), - ShapedGlyphIter::Shaped(inner) => inner.next(), - } - } - } - - let shaped_glyphs: ShapedGlyphIter = if text_run.flags.contains(cell::Flags::HIDDEN) { - ShapedGlyphIter::Hidden(hidden_glyph_iter(font_key, glyph_cache.font_size, glyph_cache, self)) + glyph_cache.font_size, + glyph_cache, + self, + )) } else { ShapedGlyphIter::Shaped(glyph_cache.shape_run(&run, font_key, self).into_iter()) }; - for ((cell, glyph), zero_width_chars) in text_run.cells() - .zip(shaped_glyphs) - .zip(zero_widths.iter()) { + + for ((cell, glyph), zero_width_chars) in + text_run.cells().zip(shaped_glyphs).zip(zero_widths.iter()) + { self.add_render_item(&cell, &glyph); for c in zero_width_chars.iter().filter(|c| **c != ' ') { let glyph_key = @@ -1142,6 +1117,43 @@ impl<'a> RenderApi<'a> { } } +/// Returns an infinite iterator of hidden glyphs for a given font key and size. +fn hidden_glyph_iter<'a, L>( + font_key: FontKey, + font_size: font::Size, + glyph_cache: &'a mut GlyphCache, + loader: &mut L, +) -> std::iter::Repeat +where + L: LoadGlyph, +{ + // If text_run is hidden we don't want to shape text. + // But we still want to run each cell through add_render_item so colors get handled + // appropiately. We construct a dummy key that should bee cached a majority of the time so + // that we have a valid glyph. + let key = GlyphKey { c: crate::cursor::PLACEHOLDER_GLYPH, font_key, size: font_size }; + let glyph = *glyph_cache.get(key, loader); + std::iter::repeat(glyph) +} + +/// Abstracts iteration over a run of hidden glyphs or shaped glyphs. +enum ShapedGlyphIter { + /// Our run was not hidden and our glyphs were shaped + Shaped(std::vec::IntoIter), + /// Our run is hidden and was not shaped + Hidden(std::iter::Repeat), +} +impl<'a> Iterator for ShapedGlyphIter { + type Item = Glyph; + + fn next(&mut self) -> Option { + match self { + ShapedGlyphIter::Hidden(inner) => inner.next(), + ShapedGlyphIter::Shaped(inner) => inner.next(), + } + } +} + /// Load a glyph into a texture atlas /// /// If the current atlas is full, a new one will be created. diff --git a/alacritty_terminal/src/term/text_run.rs b/alacritty_terminal/src/term/text_run.rs index af74613b52..1a14c8e6a4 100644 --- a/alacritty_terminal/src/term/text_run.rs +++ b/alacritty_terminal/src/term/text_run.rs @@ -58,12 +58,12 @@ impl std::fmt::Debug for TextRunContent { f.write_str("Cursor(")?; cursor.fmt(f)?; f.write_str(")") - } + }, TextRunContent::CharRun(text, _) => { f.write_str("CharRun(")?; text.fmt(f)?; f.write_str(")") - } + }, } } } @@ -91,15 +91,15 @@ pub struct TextRun { impl std::fmt::Debug for TextRun { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("TextRun") - .field("line", &self.line) - .field("span", &(self.span.0..=self.span.1)) - .field("content", &self.content) - .field("fg", &format!("#{:X}{:X}{:X}", self.fg.r, self.fg.b, self.fg.g)) - .field("bg", &format!("#{:X}{:X}{:X}", self.bg.r, self.bg.b, self.bg.g)) - .field("bg_alpha", &self.bg_alpha) - .field("flags", &self.flags) - .finish() + f.debug_struct("TextRun") + .field("line", &self.line) + .field("span", &(self.span.0..=self.span.1)) + .field("content", &self.content) + .field("fg", &format!("#{:X}{:X}{:X}", self.fg.r, self.fg.b, self.fg.g)) + .field("bg", &format!("#{:X}{:X}{:X}", self.bg.r, self.bg.b, self.bg.g)) + .field("bg_alpha", &self.bg_alpha) + .field("flags", &self.flags) + .finish() } } From 9a75eda3edf5e2785765cfa841ac6593156d3e07 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Sat, 17 Aug 2019 10:56:01 -0700 Subject: [PATCH 51/84] Remove changes to ansi.rs after file was updated to comply with clippy. --- alacritty_terminal/src/ansi.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs index 0b4fd7476a..57ad17fdc4 100644 --- a/alacritty_terminal/src/ansi.rs +++ b/alacritty_terminal/src/ansi.rs @@ -773,7 +773,7 @@ where }, // Set icon name - // This is ignored, since alacritty has no concept of tab + // This is ignored, since alacritty has no concept of tabs b"1" => (), // Set color index @@ -1110,7 +1110,6 @@ where } #[inline] - #[allow(clippy::needless_return)] // esc_dispatch doesn't type check if return in unhandled is removed. fn esc_dispatch(&mut self, params: &[i64], intermediates: &[u8], _ignore: bool, byte: u8) { macro_rules! unhandled { () => {{ From 70edaddc2da12b5e227a3e6eca6fa3bb65e5968b Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Sat, 17 Aug 2019 11:16:19 -0700 Subject: [PATCH 52/84] Fix typos in documentation, remove custom Debug impls for TextRun --- alacritty_terminal/src/renderer/mod.rs | 5 ++-- alacritty_terminal/src/term/text_run.rs | 32 ++----------------------- 2 files changed, 4 insertions(+), 33 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index b14cb537c0..521a801446 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -194,8 +194,7 @@ impl GlyphCache { let (regular, bold, italic) = Self::compute_font_keys(font, &mut rasterizer)?; // Need to load at least one glyph for the face before calling metrics. - // The glyph requested here (1 at the time of writing) has no special - // meaning. + // The glyph requested here has no special meaning. rasterizer.get_glyph(GlyphKey { c: PLACEHOLDER_GLYPH, font_key: regular, @@ -1129,7 +1128,7 @@ where { // If text_run is hidden we don't want to shape text. // But we still want to run each cell through add_render_item so colors get handled - // appropiately. We construct a dummy key that should bee cached a majority of the time so + // appropiately. We construct a dummy key that should be cached a majority of the time so // that we have a valid glyph. let key = GlyphKey { c: crate::cursor::PLACEHOLDER_GLYPH, font_key, size: font_size }; let glyph = *glyph_cache.get(key, loader); diff --git a/alacritty_terminal/src/term/text_run.rs b/alacritty_terminal/src/term/text_run.rs index 1a14c8e6a4..b224810046 100644 --- a/alacritty_terminal/src/term/text_run.rs +++ b/alacritty_terminal/src/term/text_run.rs @@ -47,31 +47,17 @@ fn is_adjacent_col((a, is_wide): LatestCol, b: Column) -> bool { a + width == b || b + width == a } +#[derive(Debug)] pub enum TextRunContent { Cursor(CursorKey), CharRun(String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), } -impl std::fmt::Debug for TextRunContent { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - TextRunContent::Cursor(cursor) => { - f.write_str("Cursor(")?; - cursor.fmt(f)?; - f.write_str(")") - }, - TextRunContent::CharRun(text, _) => { - f.write_str("CharRun(")?; - text.fmt(f)?; - f.write_str(")") - }, - } - } -} /// Represents a set of renderable cells that all share the same rendering propreties. /// The assumption is that if two cells are in the same TextRun they can be sent off together to /// be shaped. This allows for ligatures to be rendered but not when something breaks up a ligature /// (e.g. selection hightlight) which is desired behavior. +#[derive(Debug)] pub struct TextRun { /// By definition a run is on one line. pub line: Line, @@ -89,20 +75,6 @@ pub struct TextRun { pub flags: Flags, } -impl std::fmt::Debug for TextRun { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("TextRun") - .field("line", &self.line) - .field("span", &(self.span.0..=self.span.1)) - .field("content", &self.content) - .field("fg", &format!("#{:X}{:X}{:X}", self.fg.r, self.fg.b, self.fg.g)) - .field("bg", &format!("#{:X}{:X}{:X}", self.bg.r, self.bg.b, self.bg.g)) - .field("bg_alpha", &self.bg_alpha) - .field("flags", &self.flags) - .finish() - } -} - impl TextRun { // These two constructors are used by TextRunIter and are not widely applicable fn from_iter_state( From bb57c4a6e7162a1e1daae94ce235499c962be4c8 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Sun, 18 Aug 2019 11:17:29 -0700 Subject: [PATCH 53/84] Fix for compilation on windows and mac osx --- alacritty_terminal/src/renderer/mod.rs | 2 +- font/src/darwin/mod.rs | 4 ++-- font/src/directwrite/mod.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 521a801446..6cab7417c6 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -1128,7 +1128,7 @@ where { // If text_run is hidden we don't want to shape text. // But we still want to run each cell through add_render_item so colors get handled - // appropiately. We construct a dummy key that should be cached a majority of the time so + // appropiately. We construct a dummy key that should bee cached a majority of the time so // that we have a valid glyph. let key = GlyphKey { c: crate::cursor::PLACEHOLDER_GLYPH, font_key, size: font_size }; let glyph = *glyph_cache.get(key, loader); diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index cccae0327f..9e94afdc22 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -129,12 +129,12 @@ impl ::std::fmt::Display for Error { impl ::Rasterize for Rasterizer { type Err = Error; - fn new(device_pixel_ratio: f32, use_thin_strokes: bool) -> Result { + fn new(device_pixel_ratio: f32, config: RasterizerConfig) -> Result { Ok(Rasterizer { fonts: HashMap::new(), keys: HashMap::new(), device_pixel_ratio, - use_thin_strokes, + config.use_thin_strokes(), }) } diff --git a/font/src/directwrite/mod.rs b/font/src/directwrite/mod.rs index 579f7faa6a..1df4b08d83 100644 --- a/font/src/directwrite/mod.rs +++ b/font/src/directwrite/mod.rs @@ -28,7 +28,7 @@ pub struct DirectWriteRasterizer { impl crate::Rasterize for DirectWriteRasterizer { type Err = Error; - fn new(device_pixel_ratio: f32, _: bool) -> Result { + fn new(device_pixel_ratio: f32, _: RasterizerConfig) -> Result { Ok(DirectWriteRasterizer { fonts: Vec::new(), device_pixel_ratio }) } From 601724ec4dbff94918e8c88366ad59004ae96ed2 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Sun, 18 Aug 2019 11:19:17 -0700 Subject: [PATCH 54/84] Fix typo bee -> be --- alacritty_terminal/src/renderer/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 6cab7417c6..521a801446 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -1128,7 +1128,7 @@ where { // If text_run is hidden we don't want to shape text. // But we still want to run each cell through add_render_item so colors get handled - // appropiately. We construct a dummy key that should bee cached a majority of the time so + // appropiately. We construct a dummy key that should be cached a majority of the time so // that we have a valid glyph. let key = GlyphKey { c: crate::cursor::PLACEHOLDER_GLYPH, font_key, size: font_size }; let glyph = *glyph_cache.get(key, loader); From 9b3e7f9ba6a2735b87ec30e7090ca2f676f35cbe Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Mon, 26 Aug 2019 20:26:46 -0700 Subject: [PATCH 55/84] Fix windows build --- font/src/directwrite/mod.rs | 33 ++++++++++++++++++++------------- font/src/lib.rs | 9 +++++++++ 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/font/src/directwrite/mod.rs b/font/src/directwrite/mod.rs index 1df4b08d83..257d445f4e 100644 --- a/font/src/directwrite/mod.rs +++ b/font/src/directwrite/mod.rs @@ -18,7 +18,7 @@ use self::dwrote::{ FontCollection, FontStretch, FontStyle, FontWeight, GlyphOffset, GlyphRunAnalysis, }; -use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; +use super::{FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, Size, Slant, Style, Weight, RasterizerConfig}; pub struct DirectWriteRasterizer { fonts: Vec, @@ -122,15 +122,21 @@ impl crate::Rasterize for DirectWriteRasterizer { let offset = GlyphOffset { advanceOffset: 0.0, ascenderOffset: 0.0 }; - let glyph_index = *font - .get_glyph_indices(&[glyph.c as u32]) - .first() - .ok_or_else(|| Error::MissingGlyph(glyph.c))?; - if glyph_index == 0 { - // The DirectWrite documentation states that we should get 0 returned if the glyph - // does not exist in the font - return Err(Error::MissingGlyph(glyph.c)); - } + let glyph_index: u16 = match glyph.c { + KeyType::GlyphIndex(i) => i as u16, + KeyType::Fallback(c) => { + let index_u16 = *font + .get_glyph_indices(&[c as u32]) + .first() + .ok_or_else(|| Error::MissingGlyph(c))?; + if index_u16 == 0 { + // The DirectWrite documentation states that we should get 0 returned if the glyph + // does not exist in the font + return Err(Error::MissingGlyph(c)); + } + index_u16 + } + }; let glyph_run = dwrote::DWRITE_GLYPH_RUN { fontFace: unsafe { font.as_ptr() }, @@ -158,14 +164,15 @@ impl crate::Rasterize for DirectWriteRasterizer { 0.0, 0.0, ) - .or_else(|_| Err(Error::MissingGlyph(glyph.c)))?; + // Since we don't shape on windows our KeyType will always be a char + .or_else(|_| Err(Error::MissingGlyph(glyph.c.unwrap_char())))?; let bounds = glyph_analysis .get_alpha_texture_bounds(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1) - .or_else(|_| Err(Error::MissingGlyph(glyph.c)))?; + .or_else(|_| Err(Error::MissingGlyph(glyph.c.unwrap_char())))?; let buf = glyph_analysis .create_alpha_texture(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1, bounds) - .or_else(|_| Err(Error::MissingGlyph(glyph.c)))?; + .or_else(|_| Err(Error::MissingGlyph(glyph.c.unwrap_char())))?; Ok(RasterizedGlyph { c: glyph.c, diff --git a/font/src/lib.rs b/font/src/lib.rs index 8c96ef65ad..7238aaf538 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -149,6 +149,15 @@ pub enum KeyType { Fallback(char), } +impl KeyType { + fn unwrap_char(self) -> char { + match self { + KeyType::Fallback(c) => c, + KeyType::GlyphIndex(_) => panic!("Expected KeyType to contain char but was GlyphIndex"), + } + } +} + impl From for KeyType { fn from(val: u32) -> Self { KeyType::GlyphIndex(val) From 6c7b88edac0b0224f17d1e165ff92422fd637f25 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Mon, 26 Aug 2019 20:31:34 -0700 Subject: [PATCH 56/84] Run cargo fmt over the changes. --- font/src/darwin/mod.rs | 2 +- font/src/directwrite/mod.rs | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 9e94afdc22..6615cc3e46 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -134,7 +134,7 @@ impl ::Rasterize for Rasterizer { fonts: HashMap::new(), keys: HashMap::new(), device_pixel_ratio, - config.use_thin_strokes(), + use_thin_strokes: config.use_thin_strokes(), }) } diff --git a/font/src/directwrite/mod.rs b/font/src/directwrite/mod.rs index 257d445f4e..5aa1a4e7f3 100644 --- a/font/src/directwrite/mod.rs +++ b/font/src/directwrite/mod.rs @@ -18,7 +18,10 @@ use self::dwrote::{ FontCollection, FontStretch, FontStyle, FontWeight, GlyphOffset, GlyphRunAnalysis, }; -use super::{FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, Size, Slant, Style, Weight, RasterizerConfig}; +use super::{ + FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, RasterizerConfig, Size, Slant, + Style, Weight, +}; pub struct DirectWriteRasterizer { fonts: Vec, @@ -130,12 +133,12 @@ impl crate::Rasterize for DirectWriteRasterizer { .first() .ok_or_else(|| Error::MissingGlyph(c))?; if index_u16 == 0 { - // The DirectWrite documentation states that we should get 0 returned if the glyph - // does not exist in the font + // The DirectWrite documentation states that we should get 0 returned if the + // glyph does not exist in the font return Err(Error::MissingGlyph(c)); } index_u16 - } + }, }; let glyph_run = dwrote::DWRITE_GLYPH_RUN { From 0ca96c3b3193634377af091d17a3949a97655b90 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Mon, 26 Aug 2019 20:51:59 -0700 Subject: [PATCH 57/84] Resolve build error introduced by bold_italic merge. --- alacritty_terminal/src/renderer/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 26fdda3f54..b442cf972b 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -1088,8 +1088,8 @@ impl<'a> RenderApi<'a> { // Get font key for cell // FIXME this is super inefficient. let font_key = match ( - cell.flags.contains(cell::Flags::BOLD), - cell.flags.contains(cell::Flags::ITALIC), + text_run.flags.contains(cell::Flags::BOLD), + text_run.flags.contains(cell::Flags::ITALIC), ) { (false, false) => glyph_cache.font_key, (true, false) => glyph_cache.bold_key, From aff6bfefe9046832d75f811cb5c9368fb84b5695 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 27 Aug 2019 10:19:05 -0700 Subject: [PATCH 58/84] Resolve build errors on macos --- alacritty_terminal/src/config/font.rs | 1 + font/src/darwin/mod.rs | 20 ++++++++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/alacritty_terminal/src/config/font.rs b/alacritty_terminal/src/config/font.rs index 88a0574a44..1653216500 100644 --- a/alacritty_terminal/src/config/font.rs +++ b/alacritty_terminal/src/config/font.rs @@ -46,6 +46,7 @@ pub struct Font { use_thin_strokes: DefaultTrueBool, /// Toggles rendering of font ligatures + #[cfg(not(any(target_os ="macos", windows)))] #[serde(deserialize_with = "failure_default")] use_font_ligatures: DefaultTrueBool, } diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 9e94afdc22..d1e5788dc5 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -43,7 +43,7 @@ use core_text::font_descriptor::{CTFontDescriptor, CTFontOrientation}; use euclid::{Point2D, Rect, Size2D}; -use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph}; +use super::{FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, RasterizerConfig}; pub mod byte_order; use self::byte_order::extract_rgb; @@ -92,7 +92,7 @@ pub struct Rasterizer { #[derive(Debug)] pub enum Error { /// Tried to rasterize a glyph but it was not available - MissingGlyph(char), + MissingGlyph(KeyType), /// Couldn't find font matching description MissingFont(FontDesc), @@ -134,7 +134,7 @@ impl ::Rasterize for Rasterizer { fonts: HashMap::new(), keys: HashMap::new(), device_pixel_ratio, - config.use_thin_strokes(), + use_thin_strokes: config.use_thin_strokes, }) } @@ -446,12 +446,16 @@ impl Font { pub fn get_glyph( &self, - character: char, + key_type: KeyType, _size: f64, use_thin_strokes: bool, ) -> Result { - let glyph_index = - self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(character))?; + let glyph_index = match key_type { + KeyType::GlyphIndex(i) => Ok(i), + KeyType::Fallback(character) => + self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(key_type)), + }?; + let bounds = self.bounding_rect_for_glyph(Default::default(), glyph_index); @@ -464,7 +468,7 @@ impl Font { if rasterized_width == 0 || rasterized_height == 0 { return Ok(RasterizedGlyph { - c: ' ', + c: ' '.into(), width: 0, height: 0, top: 0, @@ -521,7 +525,7 @@ impl Font { let buf = extract_rgb(&rasterized_pixels); Ok(RasterizedGlyph { - c: character, + c: key_type, left: rasterized_left, top: (bounds.size.height + bounds.origin.y).ceil() as i32, width: rasterized_width as i32, From a8b8f4e27e4a4a5b3be123cd7b9297d68b9acc28 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 27 Aug 2019 10:53:27 -0700 Subject: [PATCH 59/84] Replace use_thin_strokes method call with field access. --- alacritty_terminal/src/config/font.rs | 2 +- font/src/darwin/mod.rs | 8 ++++---- font/src/lib.rs | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/alacritty_terminal/src/config/font.rs b/alacritty_terminal/src/config/font.rs index 4efbe255d1..5403d07f46 100644 --- a/alacritty_terminal/src/config/font.rs +++ b/alacritty_terminal/src/config/font.rs @@ -50,7 +50,7 @@ pub struct Font { use_thin_strokes: DefaultTrueBool, /// Toggles rendering of font ligatures - #[cfg(not(any(target_os ="macos", windows)))] + #[cfg(not(any(target_os = "macos", windows)))] #[serde(deserialize_with = "failure_default")] use_font_ligatures: DefaultTrueBool, } diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 824bd38d07..6a93d1719e 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -134,7 +134,7 @@ impl ::Rasterize for Rasterizer { fonts: HashMap::new(), keys: HashMap::new(), device_pixel_ratio, - use_thin_strokes: config.use_thin_strokes(), + use_thin_strokes: config.use_thin_strokes, }) } @@ -456,10 +456,10 @@ impl Font { ) -> Result { let glyph_index = match key_type { KeyType::GlyphIndex(i) => Ok(i), - KeyType::Fallback(character) => - self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(key_type)), + KeyType::Fallback(character) => { + self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(key_type)) + }, }?; - let bounds = self.bounding_rect_for_glyph(Default::default(), glyph_index); diff --git a/font/src/lib.rs b/font/src/lib.rs index 7238aaf538..0962404693 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -150,6 +150,8 @@ pub enum KeyType { } impl KeyType { + // Only used in directwrite rasterizer + #[cfg(windows)] fn unwrap_char(self) -> char { match self { KeyType::Fallback(c) => c, From 14b1b6cf3a925fba2abf081e7068feb30f73f00d Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 27 Aug 2019 11:52:52 -0700 Subject: [PATCH 60/84] Fix test code for macosx --- font/src/darwin/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 6a93d1719e..6e63dc353a 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -589,7 +589,7 @@ mod tests { for font in fonts { // Get a glyph for c in &['a', 'b', 'c', 'd'] { - let glyph = font.get_glyph(*c, 72., false).unwrap(); + let glyph = font.get_glyph((*c).into(), 72., false).unwrap(); // Debug the glyph.. sigh for row in 0..glyph.height { From 168c0bffbc7a658b78906a9eb10a55962961aa13 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 4 Sep 2019 18:46:05 -0700 Subject: [PATCH 61/84] initial fix for selection issue, but it needs some cleaning up. current solution is hacky. --- alacritty_terminal/src/display.rs | 3 ++- alacritty_terminal/src/renderer/mod.rs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 2ff4baf4b5..e49a064df5 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -521,8 +521,9 @@ impl Display { .into_iter() // Logic for WIDE_CHAR is handled internally by TextRun // So we no longer need WIDE_CHAR_SPACER at this point. - .filter(|rc| !rc.flags.contains(Flags::WIDE_CHAR_SPACER)), + //.filter(|rc| !rc.flags.contains(Flags::WIDE_CHAR_SPACER)), ) { + println!("{:?}", text_run); // Update underline/strikeout lines.update(&text_run); diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index b442cf972b..7d6c496bd5 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -1113,6 +1113,18 @@ impl<'a> RenderApi<'a> { text_run.cells().zip(shaped_glyphs).zip(zero_widths.iter()) { self.add_render_item(&cell, &glyph); + if cell.flags.contains(cell::Flags::WIDE_CHAR) { + // Manually add instance data for WIDE_CHAR_SPACER + let glyph = hidden_glyph_iter( + font_key, + glyph_cache.font_size, + glyph_cache, + self).next().unwrap(); + self.add_render_item(&RenderableCell { + column: cell.column + 1, + ..cell.clone() + }, &glyph); + } for c in zero_width_chars.iter().filter(|c| **c != ' ') { let glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, c: (*c).into() }; From 79ef496545c54493973e6fdb08beccde015dc384 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Mon, 9 Sep 2019 18:34:24 -0700 Subject: [PATCH 62/84] Resolving review comments, mainly minor cleanup and renamings. --- CHANGELOG.md | 2 +- alacritty_terminal/src/config/font.rs | 20 +-- alacritty_terminal/src/cursor.rs | 5 +- alacritty_terminal/src/display.rs | 8 +- alacritty_terminal/src/renderer/mod.rs | 177 +++++++++++++++---------- font/src/darwin/mod.rs | 14 +- font/src/directwrite/mod.rs | 38 +++--- font/src/ft/mod.rs | 13 +- font/src/lib.rs | 42 +++--- 9 files changed, 167 insertions(+), 152 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d2b39c117..f2feadb44f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Completions for `--class` and `-t` (short title) - Change the mouse cursor when hovering over the message bar and its close button - Support combined bold and italic text (with `font.bold_italic` to customize it) -- Support for Font Ligatures on Linux. +- Font Ligatures on Linux ### Changed diff --git a/alacritty_terminal/src/config/font.rs b/alacritty_terminal/src/config/font.rs index 5403d07f46..04a4127411 100644 --- a/alacritty_terminal/src/config/font.rs +++ b/alacritty_terminal/src/config/font.rs @@ -1,6 +1,5 @@ use std::fmt; -use font::RasterizerConfig; use font::Size; use serde::de::Visitor; use serde::{Deserialize, Deserializer}; @@ -52,7 +51,7 @@ pub struct Font { /// Toggles rendering of font ligatures #[cfg(not(any(target_os = "macos", windows)))] #[serde(deserialize_with = "failure_default")] - use_font_ligatures: DefaultTrueBool, + ligatures: DefaultTrueBool, } impl Default for Font { @@ -66,7 +65,7 @@ impl Default for Font { glyph_offset: Default::default(), offset: Default::default(), #[cfg(not(any(target_os = "macos", windows)))] - use_font_ligatures: Default::default(), + ligatures: Default::default(), #[cfg(target_os = "macos")] use_thin_strokes: Default::default(), } @@ -110,25 +109,16 @@ impl Font { } #[cfg(not(any(target_os = "macos", windows)))] - pub fn use_font_ligatures(&self) -> bool { - self.use_font_ligatures.0 + pub fn ligatures(&self) -> bool { + self.ligatures.0 } #[cfg(any(target_os = "macos", windows))] - pub fn use_font_ligatures(&self) -> bool { + pub fn ligatures(&self) -> bool { false } } -impl<'a> Into for &'a Font { - fn into(self) -> RasterizerConfig { - RasterizerConfig { - use_thin_strokes: self.use_thin_strokes(), - use_font_ligatures: self.use_font_ligatures(), - } - } -} - fn default_font_size() -> Size { Size::new(11.) } diff --git a/alacritty_terminal/src/cursor.rs b/alacritty_terminal/src/cursor.rs index b180735667..338a046bf6 100644 --- a/alacritty_terminal/src/cursor.rs +++ b/alacritty_terminal/src/cursor.rs @@ -16,7 +16,7 @@ use std::cmp; -use font::{Metrics, RasterizedGlyph}; +use font::{Metrics, RasterizedGlyph, PLACEHOLDER_GLYPH}; use crate::ansi::CursorStyle; @@ -55,9 +55,6 @@ pub fn get_cursor_glyph( } } -// This default is done as a constant to avoid duplicating the feature toggle for each cursor type. -pub const PLACEHOLDER_GLYPH: font::KeyType = font::KeyType::GlyphIndex(1u32); - // 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 diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index e49a064df5..baf46b5e7b 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -298,7 +298,11 @@ impl Display { renderer: &mut QuadRenderer, config: &Config, ) -> Result<(GlyphCache, f32, f32), Error> { - let rasterizer = font::Rasterizer::new(dpr as f32, (&config.font).into())?; + let rasterizer = font::Rasterizer::new( + dpr as f32, + config.font.use_thin_strokes(), + config.font.ligatures(), + )?; // Initialize glyph cache let glyph_cache = { @@ -521,7 +525,7 @@ impl Display { .into_iter() // Logic for WIDE_CHAR is handled internally by TextRun // So we no longer need WIDE_CHAR_SPACER at this point. - //.filter(|rc| !rc.flags.contains(Flags::WIDE_CHAR_SPACER)), + .filter(|rc| !rc.flags.contains(Flags::WIDE_CHAR_SPACER)), ) { println!("{:?}", text_run); // Update underline/strikeout diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 7d6c496bd5..285ba7a315 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -24,12 +24,14 @@ use std::time::Duration; use fnv::FnvHasher; #[cfg(not(any(target_os = "macos", windows)))] use font::HbFtExt; -use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer}; +use font::{ + self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer, PLACEHOLDER_GLYPH, +}; use glutin::dpi::PhysicalSize; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; use crate::config::{self, Config, Delta}; -use crate::cursor::{get_cursor_glyph, CursorKey, PLACEHOLDER_GLYPH}; +use crate::cursor::{get_cursor_glyph, CursorKey}; use crate::gl; use crate::gl::types::*; use crate::index::{Column, Line}; @@ -199,7 +201,7 @@ impl GlyphCache { // Need to load at least one glyph for the face before calling metrics. // The glyph requested here has no special meaning. rasterizer.get_glyph(GlyphKey { - c: PLACEHOLDER_GLYPH, + id: PLACEHOLDER_GLYPH, font_key: regular, size: font.size, })?; @@ -229,8 +231,8 @@ impl GlyphCache { fn load_glyphs_for_font(&mut self, font: FontKey, loader: &mut L) { let size = self.font_size; - for i in 32u32..=128u32 { - self.get(GlyphKey { font_key: font, c: font::KeyType::GlyphIndex(i), size }, loader); + for i in 32..=128 { + self.get(GlyphKey { font_key: font, id: font::KeyType::GlyphIndex(i), size }, loader); } } @@ -320,7 +322,7 @@ impl GlyphCache { #[cfg(not(any(target_os = "macos", windows)))] pub fn shape_run<'a, L>( &'a mut self, - text_run: &str, + text_run: &'a str, font_key: FontKey, loader: &'a mut L, ) -> Vec @@ -334,12 +336,12 @@ impl GlyphCache { .map(move |glyph_info| { let codepoint = glyph_info.codepoint; // Codepoint of 0 indicates a missing or undefined glyph - let c: font::KeyType = if codepoint == 0 { + let id: font::KeyType = if codepoint == 0 { Self::find_fallback_char(text_run, glyph_info.cluster as usize) } else { codepoint.into() }; - let glyph_key = GlyphKey { c, font_key, size: self.font_size }; + let glyph_key = GlyphKey { id, font_key, size: self.font_size }; *self.get(glyph_key, loader) }) .collect() @@ -356,7 +358,7 @@ impl GlyphCache { .into() } - pub fn get<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L) -> &'a Glyph + pub fn get(&mut self, glyph_key: GlyphKey, loader: &mut L) -> &Glyph where L: LoadGlyph, { @@ -396,7 +398,7 @@ impl GlyphCache { Self::compute_font_keys(&font, &mut self.rasterizer)?; self.rasterizer.get_glyph(GlyphKey { - c: PLACEHOLDER_GLYPH, + id: PLACEHOLDER_GLYPH, font_key: regular, size: font.size, })?; @@ -425,12 +427,13 @@ impl GlyphCache { pub fn static_metrics(config: &Config, dpr: f32) -> Result { let font = config.font.clone(); - let mut rasterizer = font::Rasterizer::new(dpr, (&config.font).into())?; + let mut rasterizer = + font::Rasterizer::new(dpr, config.font.use_thin_strokes(), config.font.ligatures())?; let regular_desc = GlyphCache::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal); let regular = rasterizer.load_font(®ular_desc, font.size)?; rasterizer.get_glyph(GlyphKey { - c: PLACEHOLDER_GLYPH, + id: PLACEHOLDER_GLYPH, font_key: regular, size: font.size, })?; @@ -1051,7 +1054,7 @@ impl<'a> RenderApi<'a> { self.render_text_run(text_run, glyph_cache); } - pub fn add_render_item(&mut self, cell: &RenderableCell, glyph: &Glyph) { + 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(); @@ -1065,81 +1068,108 @@ impl<'a> RenderApi<'a> { } } + fn render_cursor( + &mut self, + start_cell: &RenderableCell, + cursor_key: CursorKey, + glyph_cache: &mut GlyphCache, + ) { + // 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(|| { + let offset_x = self.config.font.offset.x; + let offset_y = self.config.font.offset.y; + + self.load_glyph(&get_cursor_glyph( + cursor_key.style, + metrics, + offset_x, + offset_y, + cursor_key.is_wide, + )) + }); + self.add_render_item(start_cell, &glyph); + } + + fn determine_font_key(flags: cell::Flags, glyph_cache: &GlyphCache) -> FontKey { + // FIXME this is super inefficient. + match (flags.contains(cell::Flags::BOLD), flags.contains(cell::Flags::ITALIC)) { + (false, false) => glyph_cache.font_key, + (true, false) => glyph_cache.bold_key, + (false, true) => glyph_cache.italic_key, + (true, true) => glyph_cache.bold_italic_key, + } + } + + fn render_zero_widths<'r, I>( + &mut self, + zero_width_chars: I, + cell: &RenderableCell, + font_key: FontKey, + glyph_cache: &mut GlyphCache, + ) where + I: Iterator, + { + for c in zero_width_chars { + let glyph_key = GlyphKey { font_key, size: glyph_cache.font_size, id: (*c).into() }; + let average_advance = glyph_cache.metrics.average_advance as f32; + 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 += average_advance; + + self.add_render_item(&cell, &glyph); + } + } + pub fn render_text_run(&mut self, text_run: TextRun, glyph_cache: &mut GlyphCache) { match &text_run.content { TextRunContent::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(|| { - let offset_x = self.config.font.offset.x; - let offset_y = self.config.font.offset.y; - - self.load_glyph(&get_cursor_glyph( - cursor_key.style, - metrics, - offset_x, - offset_y, - cursor_key.is_wide, - )) - }); - self.add_render_item(&text_run.start_cell(), &glyph); + self.render_cursor(&text_run.start_cell(), *cursor_key, glyph_cache) }, TextRunContent::CharRun(run, zero_widths) => { // Get font key for cell - // FIXME this is super inefficient. - let font_key = match ( - text_run.flags.contains(cell::Flags::BOLD), - text_run.flags.contains(cell::Flags::ITALIC), - ) { - (false, false) => glyph_cache.font_key, - (true, false) => glyph_cache.bold_key, - (false, true) => glyph_cache.italic_key, - (true, true) => glyph_cache.bold_italic_key, - }; + let font_key = Self::determine_font_key(text_run.flags, glyph_cache); - let shaped_glyphs: ShapedGlyphIter = if text_run.flags.contains(cell::Flags::HIDDEN) - { - ShapedGlyphIter::Hidden(hidden_glyph_iter( + let shaped_glyphs = if text_run.flags.contains(cell::Flags::HIDDEN) { + GlyphIter::Hidden(hidden_glyph_iter( font_key, glyph_cache.font_size, glyph_cache, self, )) } else { - ShapedGlyphIter::Shaped(glyph_cache.shape_run(&run, font_key, self).into_iter()) + GlyphIter::Shaped(glyph_cache.shape_run(&run, font_key, self).into_iter()) }; for ((cell, glyph), zero_width_chars) in text_run.cells().zip(shaped_glyphs).zip(zero_widths.iter()) { self.add_render_item(&cell, &glyph); - if cell.flags.contains(cell::Flags::WIDE_CHAR) { + if text_run.flags.contains(cell::Flags::WIDE_CHAR) { // Manually add instance data for WIDE_CHAR_SPACER - let glyph = hidden_glyph_iter( + // Renderer only renders single character width rectangles + // If we don't add in this instance data then we only render one cell of + // fg/bg/strikethrough/etc. + let glyph = + hidden_glyph_iter(font_key, glyph_cache.font_size, glyph_cache, self) + .next() + .unwrap(); + self.add_render_item( + &RenderableCell { column: cell.column + 1, ..cell.clone() }, + &glyph, + ); + } + self.render_zero_widths( + zero_width_chars.iter().filter(|c| **c != ' '), + &cell, font_key, - glyph_cache.font_size, glyph_cache, - self).next().unwrap(); - self.add_render_item(&RenderableCell { - column: cell.column + 1, - ..cell.clone() - }, &glyph); - } - for c in zero_width_chars.iter().filter(|c| **c != ' ') { - let glyph_key = - GlyphKey { font_key, size: glyph_cache.font_size, c: (*c).into() }; - let average_advance = glyph_cache.metrics.average_advance as f32; - 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 += average_advance; - - self.add_render_item(&cell, &glyph); - } + ); } }, }; @@ -1160,25 +1190,28 @@ where // But we still want to run each cell through add_render_item so colors get handled // appropiately. We construct a dummy key that should be cached a majority of the time so // that we have a valid glyph. - let key = GlyphKey { c: crate::cursor::PLACEHOLDER_GLYPH, font_key, size: font_size }; + let key = GlyphKey { id: PLACEHOLDER_GLYPH, font_key, size: font_size }; let glyph = *glyph_cache.get(key, loader); std::iter::repeat(glyph) } /// Abstracts iteration over a run of hidden glyphs or shaped glyphs. -enum ShapedGlyphIter { +enum GlyphIter { /// Our run was not hidden and our glyphs were shaped - Shaped(std::vec::IntoIter), + Shaped(I), /// Our run is hidden and was not shaped Hidden(std::iter::Repeat), } -impl<'a> Iterator for ShapedGlyphIter { +impl Iterator for GlyphIter +where + I: Iterator, +{ type Item = Glyph; fn next(&mut self) -> Option { match self { - ShapedGlyphIter::Hidden(inner) => inner.next(), - ShapedGlyphIter::Shaped(inner) => inner.next(), + GlyphIter::Hidden(inner) => inner.next(), + GlyphIter::Shaped(inner) => inner.next(), } } } diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 6e63dc353a..3455c4e416 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -129,12 +129,12 @@ impl ::std::fmt::Display for Error { impl ::Rasterize for Rasterizer { type Err = Error; - fn new(device_pixel_ratio: f32, config: RasterizerConfig) -> Result { + fn new(device_pixel_ratio: f32, use_thin_strokes: bool, _: bool) -> Result { Ok(Rasterizer { fonts: HashMap::new(), keys: HashMap::new(), device_pixel_ratio, - use_thin_strokes: config.use_thin_strokes, + use_thin_strokes, }) } @@ -173,7 +173,7 @@ impl ::Rasterize for Rasterizer { } } // no fallback, give up. - Err(Error::MissingGlyph(glyph.c)) + Err(Error::MissingGlyph(glyph.id)) }) } @@ -247,7 +247,7 @@ impl Rasterizer { font: &Font, ) -> Option> { let scaled_size = self.device_pixel_ratio * glyph.size.as_f32_pts(); - font.get_glyph(glyph.c, f64::from(scaled_size), self.use_thin_strokes) + font.get_glyph(glyph.id, f64::from(scaled_size), self.use_thin_strokes) .map(|r| Some(Ok(r))) .unwrap_or_else(|e| match e { Error::MissingGlyph(_) => None, @@ -455,11 +455,11 @@ impl Font { use_thin_strokes: bool, ) -> Result { let glyph_index = match key_type { - KeyType::GlyphIndex(i) => Ok(i), + KeyType::GlyphIndex(i) => i, KeyType::Fallback(character) => { - self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(key_type)) + self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(key_type))? }, - }?; + }; let bounds = self.bounding_rect_for_glyph(Default::default(), glyph_index); diff --git a/font/src/directwrite/mod.rs b/font/src/directwrite/mod.rs index 5aa1a4e7f3..14725a5e54 100644 --- a/font/src/directwrite/mod.rs +++ b/font/src/directwrite/mod.rs @@ -31,7 +31,7 @@ pub struct DirectWriteRasterizer { impl crate::Rasterize for DirectWriteRasterizer { type Err = Error; - fn new(device_pixel_ratio: f32, _: RasterizerConfig) -> Result { + fn new(device_pixel_ratio: f32, _: bool, _: bool) -> Result { Ok(DirectWriteRasterizer { fonts: Vec::new(), device_pixel_ratio }) } @@ -125,20 +125,13 @@ impl crate::Rasterize for DirectWriteRasterizer { let offset = GlyphOffset { advanceOffset: 0.0, ascenderOffset: 0.0 }; - let glyph_index: u16 = match glyph.c { + let glyph_index: u16 = match glyph.id { KeyType::GlyphIndex(i) => i as u16, - KeyType::Fallback(c) => { - let index_u16 = *font - .get_glyph_indices(&[c as u32]) - .first() - .ok_or_else(|| Error::MissingGlyph(c))?; - if index_u16 == 0 { - // The DirectWrite documentation states that we should get 0 returned if the - // glyph does not exist in the font - return Err(Error::MissingGlyph(c)); - } - index_u16 - }, + KeyType::Fallback(c) => *font + .get_glyph_indices(&[c as u32]) + .first() + .filter(|index| index != 0) + .ok_or_else(|| Error::MissingGlyph(c))?, }; let glyph_run = dwrote::DWRITE_GLYPH_RUN { @@ -168,17 +161,17 @@ impl crate::Rasterize for DirectWriteRasterizer { 0.0, ) // Since we don't shape on windows our KeyType will always be a char - .or_else(|_| Err(Error::MissingGlyph(glyph.c.unwrap_char())))?; + .or_else(|_| Err(Error::MissingGlyph(keytype_unwrap_char(glyph.id))))?; let bounds = glyph_analysis .get_alpha_texture_bounds(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1) - .or_else(|_| Err(Error::MissingGlyph(glyph.c.unwrap_char())))?; + .or_else(|_| Err(Error::MissingGlyph(keytype_unwrap_char(glyph.id))))?; let buf = glyph_analysis .create_alpha_texture(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1, bounds) - .or_else(|_| Err(Error::MissingGlyph(glyph.c.unwrap_char())))?; + .or_else(|_| Err(Error::MissingGlyph(keytype_unwrap_char(glyph.id))))?; Ok(RasterizedGlyph { - c: glyph.c, + c: glyph.id, width: (bounds.right - bounds.left) as i32, height: (bounds.bottom - bounds.top) as i32, top: -bounds.top, @@ -223,3 +216,12 @@ impl ::std::fmt::Display for Error { } } } + +// Used for error reporting only (to return a missing glyph). Windows doesn't shape text right now +// keys will always be chars. +fn keytype_unwrap_char(key_type: KeyType) -> char { + match key_type { + KeyType::GlyphIndex(_) => panic!("Expected KeyType to be a char"), + KeyType::Fallback(c) => c, + } +} diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 510ff3eab9..b839cdcbaf 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -27,8 +27,7 @@ use libc::c_uint; pub mod fc; use super::{ - FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, RasterizerConfig, Size, Slant, - Style, Weight, + FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, Size, Slant, Style, Weight, }; struct FixedSize { @@ -82,7 +81,7 @@ fn to_freetype_26_6(f: f32) -> isize { impl ::Rasterize for FreeTypeRasterizer { type Err = Error; - fn new(device_pixel_ratio: f32, config: RasterizerConfig) -> Result { + fn new(device_pixel_ratio: f32, _: bool, ligatures: bool) -> Result { let library = Library::init()?; Ok(FreeTypeRasterizer { @@ -90,7 +89,7 @@ impl ::Rasterize for FreeTypeRasterizer { keys: HashMap::new(), library, device_pixel_ratio, - use_font_ligatures: config.use_font_ligatures, + use_font_ligatures: ligatures, }) } @@ -320,7 +319,7 @@ impl FreeTypeRasterizer { glyph_key: GlyphKey, have_recursed: bool, ) -> Result { - let font_key = match glyph_key.c { + let font_key = match glyph_key.id { // We already found a glyph index, use current font KeyType::GlyphIndex(_) => glyph_key.font_key, // Harfbuzz failed to find a glyph index, try to load a font for c @@ -364,7 +363,7 @@ impl FreeTypeRasterizer { // Render a normal character if it's not a cursor let font_key = self.face_for_glyph(glyph_key, false)?; let face = &self.faces[&font_key]; - let index = match glyph_key.c { + let index = match glyph_key.id { KeyType::GlyphIndex(i) => i, KeyType::Fallback(c) => face.ft_face.get_char_index(c as usize), }; @@ -397,7 +396,7 @@ impl FreeTypeRasterizer { let (pixel_height, pixel_width, buf) = Self::normalize_buffer(&glyph.bitmap())?; Ok(RasterizedGlyph { - c: glyph_key.c, + c: glyph_key.id, top: glyph.bitmap_top(), left: glyph.bitmap_left(), width: pixel_width, diff --git a/font/src/lib.rs b/font/src/lib.rs index 0962404693..204ab2514a 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -68,6 +68,9 @@ mod darwin; #[cfg(target_os = "macos")] pub use darwin::*; +/// Placehodler glyph used as glyph key for cursor's RasterizedGlyphs +pub const PLACEHOLDER_GLYPH: KeyType = KeyType::GlyphIndex(1u32); + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct FontDesc { name: String, @@ -149,14 +152,11 @@ pub enum KeyType { Fallback(char), } -impl KeyType { - // Only used in directwrite rasterizer - #[cfg(windows)] - fn unwrap_char(self) -> char { - match self { - KeyType::Fallback(c) => c, - KeyType::GlyphIndex(_) => panic!("Expected KeyType to contain char but was GlyphIndex"), - } +impl Default for KeyType { + // Arbitrary non missing (non 0) glyph + // Used as a placeholder when glyph doesn't matter, such as when representing a cursor. + fn default() -> Self { + PLACEHOLDER_GLYPH } } @@ -165,6 +165,7 @@ impl From for KeyType { KeyType::GlyphIndex(val) } } + impl From for KeyType { fn from(val: char) -> Self { KeyType::Fallback(val) @@ -173,7 +174,7 @@ impl From for KeyType { #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)] pub struct GlyphKey { - pub c: KeyType, + pub id: KeyType, pub font_key: FontKey, pub size: Size, } @@ -208,7 +209,7 @@ impl ::std::ops::Add for Size { } } -#[derive(Clone)] +#[derive(Clone, Default)] pub struct RasterizedGlyph { pub c: KeyType, pub width: i32, @@ -218,13 +219,6 @@ pub struct RasterizedGlyph { pub buf: Vec, } -impl Default for RasterizedGlyph { - fn default() -> RasterizedGlyph { - let c: KeyType = 1u32.into(); - RasterizedGlyph { c, width: 0, height: 0, top: 0, left: 0, buf: Vec::new() } - } -} - struct BufDebugger<'a>(&'a [u8]); impl<'a> fmt::Debug for BufDebugger<'a> { @@ -261,7 +255,11 @@ pub trait Rasterize { /// Errors occurring in Rasterize methods type Err: ::std::error::Error + Send + Sync + 'static; - fn new(device_pixel_ratio: f32, rasterize_config: RasterizerConfig) -> Result + fn new( + device_pixel_ratio: f32, + use_thin_strokes: bool, + ligatures: bool, + ) -> Result where Self: Sized; @@ -278,14 +276,6 @@ pub trait Rasterize { fn update_dpr(&mut self, device_pixel_ratio: f32); } -/// Config options specific to the Rasterizer. -pub struct RasterizerConfig { - /// Toggle thin strokes on mac osx - pub use_thin_strokes: bool, - /// Toggle rendering of font ligatures - pub use_font_ligatures: bool, -} - // Only implemented for the FreeType rasterizer so far. /// Conceptually this extends the Rasterizer trait with Harfbuzz specific functionality. #[cfg(not(any(target_os = "macos", windows)))] From dd2f870a45ceb0b0a97d48c57d0b0619ad3ed785 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Mon, 9 Sep 2019 19:14:43 -0700 Subject: [PATCH 63/84] Fix build errors on mac and windows. --- font/src/darwin/mod.rs | 2 +- font/src/directwrite/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 3455c4e416..a7d3554b1f 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -43,7 +43,7 @@ use core_text::font_descriptor::{CTFontDescriptor, CTFontOrientation}; use euclid::{Point2D, Rect, Size2D}; -use super::{FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, RasterizerConfig}; +use super::{FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph}; pub mod byte_order; use self::byte_order::extract_rgb; diff --git a/font/src/directwrite/mod.rs b/font/src/directwrite/mod.rs index 14725a5e54..4ed37be872 100644 --- a/font/src/directwrite/mod.rs +++ b/font/src/directwrite/mod.rs @@ -19,7 +19,7 @@ use self::dwrote::{ }; use super::{ - FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, RasterizerConfig, Size, Slant, + FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, Size, Slant, Style, Weight, }; @@ -130,7 +130,7 @@ impl crate::Rasterize for DirectWriteRasterizer { KeyType::Fallback(c) => *font .get_glyph_indices(&[c as u32]) .first() - .filter(|index| index != 0) + .filter(|index| **index != 0) .ok_or_else(|| Error::MissingGlyph(c))?, }; From 2034c7b47a0a21e1f8bdf703734903fbdc94d3e8 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 10 Sep 2019 11:09:29 -0700 Subject: [PATCH 64/84] Continue fixing up the codebase and making refactorings. Changes PLACEHOLDER_GLYPH to 0 (from 1). --- alacritty_terminal/src/cursor.rs | 1 + alacritty_terminal/src/display.rs | 11 +-- alacritty_terminal/src/lib.rs | 1 + alacritty_terminal/src/renderer/mod.rs | 5 +- alacritty_terminal/src/renderer/rects.rs | 2 +- alacritty_terminal/src/term/mod.rs | 3 +- alacritty_terminal/src/{term => }/text_run.rs | 27 +++++-- font/src/directwrite/mod.rs | 3 +- font/src/ft/mod.rs | 70 +++---------------- font/src/lib.rs | 3 +- 10 files changed, 41 insertions(+), 85 deletions(-) rename alacritty_terminal/src/{term => }/text_run.rs (93%) diff --git a/alacritty_terminal/src/cursor.rs b/alacritty_terminal/src/cursor.rs index 338a046bf6..44a68f7a3d 100644 --- a/alacritty_terminal/src/cursor.rs +++ b/alacritty_terminal/src/cursor.rs @@ -41,6 +41,7 @@ pub fn get_cursor_glyph( 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; diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index baf46b5e7b..b4b3b95752 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -31,11 +31,10 @@ use crate::renderer::rects::{RenderLines, RenderRect}; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::sync::FairMutex; use crate::term::{cell::Flags, color::Rgb, RenderableCell, SizeInfo, Term}; +use crate::text_run::TextRunIter; use crate::window::{self, Window}; use font::{self, Rasterize}; -use crate::term::text_run::TextRunIter; - #[derive(Debug)] pub enum Error { /// Error with window management @@ -520,13 +519,7 @@ impl Display { // Draw grid self.renderer.with_api(config, &size_info, |mut api| { // Iterate over each contiguous block of text - for text_run in TextRunIter::new( - grid_cells - .into_iter() - // Logic for WIDE_CHAR is handled internally by TextRun - // So we no longer need WIDE_CHAR_SPACER at this point. - .filter(|rc| !rc.flags.contains(Flags::WIDE_CHAR_SPACER)), - ) { + for text_run in TextRunIter::new(grid_cells.into_iter()) { println!("{:?}", text_run); // Update underline/strikeout lines.update(&text_run); diff --git a/alacritty_terminal/src/lib.rs b/alacritty_terminal/src/lib.rs index 0bada53545..6d842940aa 100644 --- a/alacritty_terminal/src/lib.rs +++ b/alacritty_terminal/src/lib.rs @@ -46,6 +46,7 @@ pub mod renderer; pub mod selection; pub mod sync; pub mod term; +mod text_run; pub mod tty; mod url; pub mod util; diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 285ba7a315..b23730a583 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -36,9 +36,8 @@ use crate::gl; use crate::gl::types::*; use crate::index::{Column, Line}; use crate::renderer::rects::RenderRect; -use crate::term::color::Rgb; -use crate::term::text_run::{TextRun, TextRunContent}; -use crate::term::{self, cell, RenderableCell}; +use crate::term::{self, cell, color::Rgb, RenderableCell}; +use crate::text_run::{TextRun, TextRunContent}; pub mod rects; diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index 7d8c7fbbba..bf958ccea0 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -18,8 +18,8 @@ use font::Metrics; use crate::index::Point; use crate::term::cell::Flags; use crate::term::color::Rgb; -use crate::term::text_run::TextRun; use crate::term::SizeInfo; +use crate::text_run::TextRun; #[derive(Debug, Copy, Clone)] pub struct RenderRect { diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 5878f511da..91ebdd6437 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -45,7 +45,6 @@ use crate::tty; pub mod cell; pub mod color; -pub mod text_run; /// Used to match equal brackets, when performing a bracket-pair selection. const BRACKET_PAIRS: [(char, char); 4] = [('(', ')'), ('[', ']'), ('{', '}'), ('<', '>')]; @@ -306,7 +305,7 @@ impl RenderableCell { } } - fn is_cursor(&self) -> bool { + pub fn is_cursor(&self) -> bool { match &self.inner { RenderableCellContent::Cursor(_) => true, _ => false, diff --git a/alacritty_terminal/src/term/text_run.rs b/alacritty_terminal/src/text_run.rs similarity index 93% rename from alacritty_terminal/src/term/text_run.rs rename to alacritty_terminal/src/text_run.rs index b224810046..e7567e54df 100644 --- a/alacritty_terminal/src/term/text_run.rs +++ b/alacritty_terminal/src/text_run.rs @@ -1,9 +1,10 @@ -use super::{ +use crate::cursor::CursorKey; +use crate::index::{Column, Line, Point}; +use crate::term::{ cell::{Flags, MAX_ZEROWIDTH_CHARS}, color::Rgb, - CursorKey, Point, RenderableCell, RenderableCellContent, + RenderableCell, RenderableCellContent, }; -use crate::index::{Column, Line}; #[derive(Debug)] struct RunStart { @@ -189,10 +190,21 @@ pub struct TextRunIter { buffer_text: String, buffer_zero_width: Vec<[char; MAX_ZEROWIDTH_CHARS]>, } -impl TextRunIter { - pub fn new(iter: I) -> Self { + +// This is an explicit function (as opposed to a closure) to make it easy to use as a function +// pointer. +fn is_not_wide_char_spacer(rc: &RenderableCell) -> bool { + !rc.flags.contains(Flags::WIDE_CHAR_SPACER) +} +impl TextRunIter bool>> +where + BaseIter: Iterator, +{ + pub fn new(iter: BaseIter) -> Self { TextRunIter { - iter, + // Logic for WIDE_CHAR is handled internally by TextRun + // So we no longer need WIDE_CHAR_SPACER at this point. + iter: iter.filter(is_not_wide_char_spacer), latest: None, run_start: None, cursor: None, @@ -200,7 +212,8 @@ impl TextRunIter { buffer_zero_width: Vec::new(), } } - +} +impl TextRunIter { /// Check if current run ends with incoming RenderableCell fn is_end_of_run(&self, rc: &RenderableCell) -> bool { let is_cell_adjacent = diff --git a/font/src/directwrite/mod.rs b/font/src/directwrite/mod.rs index 4ed37be872..e8b3e55805 100644 --- a/font/src/directwrite/mod.rs +++ b/font/src/directwrite/mod.rs @@ -19,8 +19,7 @@ use self::dwrote::{ }; use super::{ - FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, Size, Slant, - Style, Weight, + FontDesc, FontKey, GlyphKey, KeyType, Metrics, RasterizedGlyph, Size, Slant, Style, Weight, }; pub struct DirectWriteRasterizer { diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index b839cdcbaf..8bde1cb59e 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -20,8 +20,7 @@ use std::path::PathBuf; use freetype::tt_os2::TrueTypeOS2Table; use freetype::{self, Library}; -use harfbuzz_rs::Face as HbFace; -use harfbuzz_rs::{shape, Feature, Font, GlyphBuffer, Owned, Tag, UnicodeBuffer}; +use harfbuzz_rs::{Face as HbFace, Feature, Font, GlyphBuffer, Owned, Tag, UnicodeBuffer}; use libc::c_uint; pub mod fc; @@ -41,7 +40,6 @@ struct Face { render_mode: freetype::RenderMode, lcd_filter: c_uint, non_scalable: Option, - /// This is an option just in case hb_ft_create_font_referenced fails. hb_font: Owned>, } @@ -154,13 +152,13 @@ impl ::Rasterize for FreeTypeRasterizer { impl crate::HbFtExt for FreeTypeRasterizer { fn shape(&mut self, text: &str, font_key: FontKey) -> GlyphBuffer { let hb_font = &self.faces[&font_key].hb_font; - let buf = UnicodeBuffer::default().add_str(text); + let buf = UnicodeBuffer::new().add_str(text); let use_font_ligature = if self.use_font_ligatures { 1 } else { 0 }; let features = &[ - Feature::new(Tag::new('l', 'i', 'g', 'a'), use_font_ligature, 0..), - Feature::new(Tag::new('c', 'a', 'l', 't'), use_font_ligature, 0..), + Feature::new(Tag::new('l', 'i', 'g', 'a'), use_font_ligature, ..), + Feature::new(Tag::new('c', 'a', 'l', 't'), use_font_ligature, ..), ]; - shape(&*hb_font, buf, features) + harfbuzz_rs::shape(&*hb_font, buf, features) } } @@ -287,10 +285,7 @@ impl FreeTypeRasterizer { }; // Construct harfbuzz font - let hb_font = { - let _hb_face = HbFace::from_file(&path, index as u32)?; - Font::new(_hb_face) - }; + let hb_font = Font::new(HbFace::from_file(&path, index as u32)?); let face = Face { ft_face, @@ -314,69 +309,24 @@ impl FreeTypeRasterizer { } } - fn face_for_glyph( - &mut self, - glyph_key: GlyphKey, - have_recursed: bool, - ) -> Result { - let font_key = match glyph_key.id { + fn face_for_glyph(&mut self, glyph_key: GlyphKey) -> FontKey { + match glyph_key.id { // We already found a glyph index, use current font KeyType::GlyphIndex(_) => glyph_key.font_key, // Harfbuzz failed to find a glyph index, try to load a font for c - KeyType::Fallback(c) => self.handle_fallback_char(c, glyph_key.font_key, have_recursed), - }; - Ok(font_key) - } - - // On unix harfbuzz turns characters into glyph indices. So if we hit this method we already - // know the glyph is missing from the font and we want to fallback to a system font. - #[cfg(unix)] - fn handle_fallback_char(&mut self, c: char, font_key: FontKey, have_recursed: bool) -> FontKey { - if have_recursed { - font_key - } else { - self.load_face_with_glyph(c).unwrap_or(font_key) - } - } - - // If we aren't on unix this will be called for all characters so we still want to check - // configured font first. - #[cfg(not(unix))] - fn handle_fallback_char(&mut self, c: char, font_key: FontKey, have_recursed: bool) -> FontKey { - let use_initial_face = if let Some(face) = self.faces.get(&glyph_key.font_key) { - let index = face.ft_face.get_char_index(c as usize); - - index != 0 || have_recursed - } else { - false - }; - - if use_initial_face { - font_key - } else { - let key = self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key); - key + KeyType::Fallback(c) => self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key), } } fn get_rendered_glyph(&mut self, glyph_key: GlyphKey) -> Result { // Render a normal character if it's not a cursor - let font_key = self.face_for_glyph(glyph_key, false)?; + let font_key = self.face_for_glyph(glyph_key); let face = &self.faces[&font_key]; let index = match glyph_key.id { KeyType::GlyphIndex(i) => i, KeyType::Fallback(c) => face.ft_face.get_char_index(c as usize), }; - self.get_rendered_glyph_index(face, glyph_key, index) - } - - fn get_rendered_glyph_index( - &self, - face: &crate::ft::Face, - glyph_key: GlyphKey, - index: u32, - ) -> Result { let size = face.non_scalable.as_ref().map(|v| v.pixelsize as f32).unwrap_or_else(|| { glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72. diff --git a/font/src/lib.rs b/font/src/lib.rs index 204ab2514a..4695392ee7 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -69,7 +69,8 @@ mod darwin; pub use darwin::*; /// Placehodler glyph used as glyph key for cursor's RasterizedGlyphs -pub const PLACEHOLDER_GLYPH: KeyType = KeyType::GlyphIndex(1u32); +/// 0 indicates a missing glyph, this ensures it will not render a random glyph in any font. +pub const PLACEHOLDER_GLYPH: KeyType = KeyType::GlyphIndex(0u32); #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct FontDesc { From 54c33c780ccc0f5274526d36ee00a8871e543d19 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 10 Sep 2019 11:39:07 -0700 Subject: [PATCH 65/84] Fix for windows and mac osx --- alacritty_terminal/src/display.rs | 2 +- alacritty_terminal/src/renderer/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index b4b3b95752..85dd078671 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -30,7 +30,7 @@ use crate::meter::Meter; use crate::renderer::rects::{RenderLines, RenderRect}; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::sync::FairMutex; -use crate::term::{cell::Flags, color::Rgb, RenderableCell, SizeInfo, Term}; +use crate::term::{color::Rgb, RenderableCell, SizeInfo, Term}; use crate::text_run::TextRunIter; use crate::window::{self, Window}; use font::{self, Rasterize}; diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index b23730a583..59969ad9bf 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -311,7 +311,7 @@ impl GlyphCache { text_run .chars() .map(|c| { - let glyph_key = GlyphKey { c: c.into(), font_key, size: self.font_size }; + let glyph_key = GlyphKey { id: c.into(), font_key, size: self.font_size }; *self.get(glyph_key, loader) }) .collect() From 715263ac3a6c298a88a73db418313555a2af2091 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 10 Sep 2019 15:27:30 -0700 Subject: [PATCH 66/84] Refactor text_run for clarity of intent and clean up dead code. --- alacritty_terminal/src/text_run.rs | 173 ++++++++++++----------------- 1 file changed, 70 insertions(+), 103 deletions(-) diff --git a/alacritty_terminal/src/text_run.rs b/alacritty_terminal/src/text_run.rs index e7567e54df..40996471bd 100644 --- a/alacritty_terminal/src/text_run.rs +++ b/alacritty_terminal/src/text_run.rs @@ -8,44 +8,22 @@ use crate::term::{ #[derive(Debug)] struct RunStart { - pub line: Line, - pub column: Column, - pub fg: Rgb, - pub bg: Rgb, - pub bg_alpha: f32, - pub flags: Flags, + line: Line, + column: Column, + fg: Rgb, + bg: Rgb, + bg_alpha: f32, + flags: Flags, } - -// Use a macro instead of a function to make use of partial move semantics that don't cross -// function boundaries -// Convert a RenderableCell into a RunStart -macro_rules! from_rc { - ($rc:ident) => { - RunStart { - line: $rc.line, - column: $rc.column, - fg: $rc.fg, - bg: $rc.bg, - bg_alpha: $rc.bg_alpha, - flags: $rc.flags, - } - }; -} - -/// Compare cells and check they are in the same text run -fn is_adjacent_context(a: &RunStart, b: &RenderableCell) -> bool { - a.line == b.line - && a.fg == b.fg - && a.bg == b.bg - && (a.bg_alpha - b.bg_alpha).abs() < std::f32::EPSILON - && a.flags == b.flags -} - -type LatestCol = (Column, bool); -/// Checks two columns are adjacent -fn is_adjacent_col((a, is_wide): LatestCol, b: Column) -> bool { - let width = if is_wide { 2usize } else { 1usize }; - a + width == b || b + width == a +impl RunStart { + /// Compare cell and check if it belongs to the same run. + fn is_adjacent_to_cell(&self, rc: &RenderableCell) -> bool { + self.line == rc.line + && self.fg == rc.fg + && self.bg == rc.bg + && (self.bg_alpha - rc.bg_alpha).abs() < std::f32::EPSILON + && self.flags == rc.flags + } } #[derive(Debug)] @@ -60,7 +38,7 @@ pub enum TextRunContent { /// (e.g. selection hightlight) which is desired behavior. #[derive(Debug)] pub struct TextRun { - /// By definition a run is on one line. + /// A run never spans multiple lines. pub line: Line, /// Span of columns the text run covers. pub span: (Column, Column), @@ -70,14 +48,13 @@ pub struct TextRun { pub fg: Rgb, /// Background color of text run content. pub bg: Rgb, - /// Opacity of background color of text run content. + /// Background color opacity of the text run pub bg_alpha: f32, - /// Attributes of this text run (e.g. HIDDEN, STRIKETHROUGH, etc.) + /// Attributes of this text run. pub flags: Flags, } impl TextRun { - // These two constructors are used by TextRunIter and are not widely applicable fn from_iter_state( start: RunStart, (latest, is_wide): LatestCol, @@ -120,37 +97,11 @@ impl TextRun { } } - /// Number of columns this TextRun spans - pub fn len(&self) -> usize { - let (start, end) = self.span; - end.0 - start.0 - } - - /// True if TextRun contains characters, false if it contains no characters. - pub fn is_empty(&self) -> bool { - match &self.content { - TextRunContent::Cursor(_) => false, - TextRunContent::CharRun(ref string, ref zero_widths) => { - string.is_empty() && zero_widths.is_empty() - }, - } - } - - /// First column of the run - pub fn start_col(&self) -> Column { - self.span.0 - } - /// First cell in the TextRun pub fn start_cell(&self) -> RenderableCell { self.cell_at(self.span.0) } - /// End cell in the TextRun - pub fn end_cell(&self) -> RenderableCell { - self.cell_at(self.span.1) - } - /// First point covered by this TextRun pub fn start_point(&self) -> Point { Point { line: self.line, col: self.span.0 } @@ -161,31 +112,23 @@ impl TextRun { Point { line: self.line, col: self.span.1 } } - /// Returns iterator over range of columns [run.0, run.1] - pub fn cols(&self) -> impl Iterator { - let (start, end) = self.span; - let step = if self.flags.contains(Flags::WIDE_CHAR) { - // If our run contains ide chars treat each cell like it's 2 cells wide - 2 - } else { - 1 - }; - // unpacking is neccessary while Step trait is nightly - // hopefully this compiles away. - (start.0..=end.0).step_by(step).map(Column) - } - /// Iterates over each RenderableCell in column range [run.0, run.1] pub fn cells<'a>(&'a self) -> impl Iterator + 'a { - self.cols().map(move |col| self.cell_at(col)) + let step = if self.flags.contains(Flags::WIDE_CHAR) { 2 } else { 1 }; + let (Column(start), Column(end)) = self.span; + // TODO: impl Step for Column (once Step is stable) to avoid unwrapping then rewrapping. + (start..=end).step_by(step).map(move |col| self.cell_at(Column(col))) } } +type IsWide = bool; +type LatestCol = (Column, IsWide); + /// Wraps an Iterator and produces TextRuns to represent batches of cells pub struct TextRunIter { iter: I, run_start: Option, - latest: Option, + latest_col: Option, cursor: Option, buffer_text: String, buffer_zero_width: Vec<[char; MAX_ZEROWIDTH_CHARS]>, @@ -205,7 +148,7 @@ where // Logic for WIDE_CHAR is handled internally by TextRun // So we no longer need WIDE_CHAR_SPACER at this point. iter: iter.filter(is_not_wide_char_spacer), - latest: None, + latest_col: None, run_start: None, cursor: None, buffer_text: String::new(), @@ -214,13 +157,23 @@ where } } impl TextRunIter { - /// Check if current run ends with incoming RenderableCell + /// Check if current run ends at incoming RenderableCell + /// Run will not include incoming RenderableCell if it ends fn is_end_of_run(&self, rc: &RenderableCell) -> bool { - let is_cell_adjacent = - self.run_start.as_ref().map(|cell| !is_adjacent_context(cell, &rc)).unwrap_or(false); - let is_col_adjacent = - self.latest.as_ref().map(|col| !is_adjacent_col(*col, rc.column)).unwrap_or(false); - is_cell_adjacent || is_col_adjacent + let is_cell_not_adjacent = self + .run_start + .as_ref() + .map(|run_start| !run_start.is_adjacent_to_cell(rc)) + .unwrap_or(false); + let is_col_not_adjacent = self + .latest_col + .as_ref() + .map(|&(col, is_wide)| { + let width = if is_wide { 2usize } else { 1usize }; + col + width != rc.column && rc.column + width != col + }) + .unwrap_or(false); + is_cell_not_adjacent || is_col_not_adjacent } /// Add content of cell to pending TextRun buffer @@ -244,11 +197,19 @@ impl TextRunIter { (self.buffer_text.drain(..).collect(), self.buffer_zero_width.drain(..).collect()) } - /// Handles bookkeeping needed when starting a new run + /// Start a new run by setting latest_col, run_start, and buffering content of rc + /// Returns the previous runs run_start and latest_col data if available. fn start_run(&mut self, rc: RenderableCell) -> (Option, Option) { self.buffer_content(rc.inner); - let latest = self.latest.replace((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); - let start = self.run_start.replace(from_rc!(rc)); + let latest = self.latest_col.replace((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); + let start = self.run_start.replace(RunStart { + line: rc.line, + column: rc.column, + fg: rc.fg, + bg: rc.bg, + bg_alpha: rc.bg_alpha, + flags: rc.flags, + }); (start, latest) } @@ -267,10 +228,10 @@ impl TextRunIter { /// completion. fn produce_char_run(&mut self, rc: RenderableCell) -> Option { let prev_buffer = self.drain_buffer(); - let (start_opt, latest_opt) = self.start_run(rc); + let (start_opt, latest_col_opt) = self.start_run(rc); let start = start_opt?; - let latest = latest_opt?; - Some(TextRun::from_iter_state(start, latest, prev_buffer)) + let latest_col = latest_col_opt?; + Some(TextRun::from_iter_state(start, latest_col, prev_buffer)) } } @@ -283,10 +244,17 @@ where fn next(&mut self) -> Option { let mut output = None; while let Some(rc) = self.iter.next() { - if self.latest.is_none() || self.run_start.is_none() { + if self.latest_col.is_none() || self.run_start.is_none() { // Initial state, this is should only be hit on the first next() call of // iterator - self.run_start = Some(from_rc!(rc)); + self.run_start = Some(RunStart { + line: rc.line, + column: rc.column, + fg: rc.fg, + bg: rc.bg, + bg_alpha: rc.bg_alpha, + flags: rc.flags, + }) } else if self.cursor.is_some() { // Last iteration of the loop found a cursor // Return a run for the cursor and start a new run @@ -299,18 +267,17 @@ where break; } // Build up buffer and track the latest column we've seen - self.latest = Some((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); + self.latest_col = Some((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); self.buffer_content(rc.inner); } - // If we generated output we're done. - // Otherwise check for any remaining buffered content and return it as a text run. + // Check for any remaining buffered content and return it as a text run. // This is a destructive operation, it will return None after it excutes once. output.or_else(|| { if !self.buffer_text.is_empty() || !self.buffer_zero_width.is_empty() { let start = self.run_start.take()?; - let latest = self.latest.take()?; + let latest_col = self.latest_col.take()?; // Save leftover buffer and empty it - Some(TextRun::from_iter_state(start, latest, self.drain_buffer())) + Some(TextRun::from_iter_state(start, latest_col, self.drain_buffer())) } else if let Some(cursor) = self.cursor { let start = self.run_start.take()?; Some(TextRun::from_cursor_rc(start, cursor)) From ecec6ef678996fd5bd1150a1c025a0aad59ab3cc Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 10 Sep 2019 18:15:11 -0700 Subject: [PATCH 67/84] Introduce placeholder keytype to represent a glyph that should render as nothing. --- alacritty_terminal/src/renderer/mod.rs | 15 +++------------ font/src/darwin/mod.rs | 3 +++ font/src/directwrite/mod.rs | 9 ++++++++- font/src/ft/mod.rs | 10 ++++++++-- font/src/lib.rs | 6 ++++-- 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 59969ad9bf..6839dfac31 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -1135,12 +1135,7 @@ impl<'a> RenderApi<'a> { let font_key = Self::determine_font_key(text_run.flags, glyph_cache); let shaped_glyphs = if text_run.flags.contains(cell::Flags::HIDDEN) { - GlyphIter::Hidden(hidden_glyph_iter( - font_key, - glyph_cache.font_size, - glyph_cache, - self, - )) + GlyphIter::Hidden(hidden_glyph_iter(font_key, glyph_cache, self)) } else { GlyphIter::Shaped(glyph_cache.shape_run(&run, font_key, self).into_iter()) }; @@ -1154,10 +1149,7 @@ impl<'a> RenderApi<'a> { // Renderer only renders single character width rectangles // If we don't add in this instance data then we only render one cell of // fg/bg/strikethrough/etc. - let glyph = - hidden_glyph_iter(font_key, glyph_cache.font_size, glyph_cache, self) - .next() - .unwrap(); + let glyph = hidden_glyph_iter(font_key, glyph_cache, self).next().unwrap(); self.add_render_item( &RenderableCell { column: cell.column + 1, ..cell.clone() }, &glyph, @@ -1178,7 +1170,6 @@ impl<'a> RenderApi<'a> { /// Returns an infinite iterator of hidden glyphs for a given font key and size. fn hidden_glyph_iter<'a, L>( font_key: FontKey, - font_size: font::Size, glyph_cache: &'a mut GlyphCache, loader: &mut L, ) -> std::iter::Repeat @@ -1189,7 +1180,7 @@ where // But we still want to run each cell through add_render_item so colors get handled // appropiately. We construct a dummy key that should be cached a majority of the time so // that we have a valid glyph. - let key = GlyphKey { id: PLACEHOLDER_GLYPH, font_key, size: font_size }; + let key = GlyphKey { id: PLACEHOLDER_GLYPH, font_key, size: glyph_cache.font_size }; let glyph = *glyph_cache.get(key, loader); std::iter::repeat(glyph) } diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index a7d3554b1f..627013d28f 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -456,6 +456,9 @@ impl Font { ) -> Result { let glyph_index = match key_type { KeyType::GlyphIndex(i) => i, + KeyType::Placeholder => { + self.glyph_index(' ').ok_or_else(|| Error::MissingFont(key_type))? + }, KeyType::Fallback(character) => { self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(key_type))? }, diff --git a/font/src/directwrite/mod.rs b/font/src/directwrite/mod.rs index e8b3e55805..b28af4ca13 100644 --- a/font/src/directwrite/mod.rs +++ b/font/src/directwrite/mod.rs @@ -126,6 +126,12 @@ impl crate::Rasterize for DirectWriteRasterizer { let glyph_index: u16 = match glyph.id { KeyType::GlyphIndex(i) => i as u16, + // On placeholder return a missing glyph error with the placeholder unicode character + KeyType::Placeholder => *font + .get_glyph_indices(&[' ' as u32]) + .first() + .filter(|index| **index != 0) + .ok_or_else(|| Error::MissingGlyph(' '))?, KeyType::Fallback(c) => *font .get_glyph_indices(&[c as u32]) .first() @@ -220,7 +226,8 @@ impl ::std::fmt::Display for Error { // keys will always be chars. fn keytype_unwrap_char(key_type: KeyType) -> char { match key_type { - KeyType::GlyphIndex(_) => panic!("Expected KeyType to be a char"), + KeyType::GlyphIndex(_) => panic!("Expected KeyType to be contain a char"), KeyType::Fallback(c) => c, + KeyType::Placeholder => ' ', } } diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 8bde1cb59e..620ad2c26a 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -41,6 +41,7 @@ struct Face { lcd_filter: c_uint, non_scalable: Option, hb_font: Owned>, + placeholder_glyph_index: u32, } impl fmt::Debug for Face { @@ -273,7 +274,9 @@ impl FreeTypeRasterizer { trace!("Got font path={:?}", path); let ft_face = self.library.new_face(&path, index)?; - + // This will different for each font so we can't use a constant but we don't want to + // look it up every time so we cache it on load. + let placeholder_glyph_index = ft_face.get_char_index(' ' as usize); // Get available pixel sizes if font isn't scalable. let non_scalable = if pattern.scalable().next().unwrap_or(true) { None @@ -295,6 +298,7 @@ impl FreeTypeRasterizer { lcd_filter: Self::ft_lcd_filter(pattern), non_scalable, hb_font, + placeholder_glyph_index, }; debug!("Loaded Face {:?}", face); @@ -312,7 +316,7 @@ impl FreeTypeRasterizer { fn face_for_glyph(&mut self, glyph_key: GlyphKey) -> FontKey { match glyph_key.id { // We already found a glyph index, use current font - KeyType::GlyphIndex(_) => glyph_key.font_key, + KeyType::GlyphIndex(_) | KeyType::Placeholder => glyph_key.font_key, // Harfbuzz failed to find a glyph index, try to load a font for c KeyType::Fallback(c) => self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key), } @@ -325,6 +329,8 @@ impl FreeTypeRasterizer { let index = match glyph_key.id { KeyType::GlyphIndex(i) => i, KeyType::Fallback(c) => face.ft_face.get_char_index(c as usize), + // Render + KeyType::Placeholder => face.placeholder_glyph_index, }; let size = diff --git a/font/src/lib.rs b/font/src/lib.rs index 4695392ee7..d2a3a57c15 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -69,8 +69,7 @@ mod darwin; pub use darwin::*; /// Placehodler glyph used as glyph key for cursor's RasterizedGlyphs -/// 0 indicates a missing glyph, this ensures it will not render a random glyph in any font. -pub const PLACEHOLDER_GLYPH: KeyType = KeyType::GlyphIndex(0u32); +pub const PLACEHOLDER_GLYPH: KeyType = KeyType::Placeholder; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct FontDesc { @@ -151,6 +150,9 @@ pub enum KeyType { /// attempt to load character glyph from a fallback font. If shaping did not occur this will /// first try the configured font then fallback. Fallback(char), + /// Placeholder glyph useful when we need a glyph but it shouldn't ever render as anything + /// (cursors, wide_char_spacers, etc.) + Placeholder, } impl Default for KeyType { From 853619f3dd8cb3a9de25beb1ac5e8ab6358d6986 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Tue, 10 Sep 2019 18:33:41 -0700 Subject: [PATCH 68/84] Fix error returned for missing placeholder glyph. Refactor hidden_glyph_iter to be more flexible. --- alacritty_terminal/src/renderer/mod.rs | 13 ++++++------- font/src/darwin/mod.rs | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 6839dfac31..12bf27883c 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -1135,7 +1135,7 @@ impl<'a> RenderApi<'a> { let font_key = Self::determine_font_key(text_run.flags, glyph_cache); let shaped_glyphs = if text_run.flags.contains(cell::Flags::HIDDEN) { - GlyphIter::Hidden(hidden_glyph_iter(font_key, glyph_cache, self)) + GlyphIter::Hidden(std::iter::repeat(hidden_glyph(font_key, glyph_cache, self))) } else { GlyphIter::Shaped(glyph_cache.shape_run(&run, font_key, self).into_iter()) }; @@ -1148,8 +1148,8 @@ impl<'a> RenderApi<'a> { // Manually add instance data for WIDE_CHAR_SPACER // Renderer only renders single character width rectangles // If we don't add in this instance data then we only render one cell of - // fg/bg/strikethrough/etc. - let glyph = hidden_glyph_iter(font_key, glyph_cache, self).next().unwrap(); + // fg/bg/strikethrough/etc. for WIDE_CHARs + let glyph = hidden_glyph(font_key, glyph_cache, self); self.add_render_item( &RenderableCell { column: cell.column + 1, ..cell.clone() }, &glyph, @@ -1168,11 +1168,11 @@ impl<'a> RenderApi<'a> { } /// Returns an infinite iterator of hidden glyphs for a given font key and size. -fn hidden_glyph_iter<'a, L>( +fn hidden_glyph<'a, L>( font_key: FontKey, glyph_cache: &'a mut GlyphCache, loader: &mut L, -) -> std::iter::Repeat +) -> Glyph where L: LoadGlyph, { @@ -1181,8 +1181,7 @@ where // appropiately. We construct a dummy key that should be cached a majority of the time so // that we have a valid glyph. let key = GlyphKey { id: PLACEHOLDER_GLYPH, font_key, size: glyph_cache.font_size }; - let glyph = *glyph_cache.get(key, loader); - std::iter::repeat(glyph) + *glyph_cache.get(key, loader) } /// Abstracts iteration over a run of hidden glyphs or shaped glyphs. diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 627013d28f..3d4c1d857d 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -457,7 +457,7 @@ impl Font { let glyph_index = match key_type { KeyType::GlyphIndex(i) => i, KeyType::Placeholder => { - self.glyph_index(' ').ok_or_else(|| Error::MissingFont(key_type))? + self.glyph_index(' ').ok_or_else(|| Error::MissingGlyph(key_type))? }, KeyType::Fallback(character) => { self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(key_type))? From 1a4d7e01bd5506b166e41806ae41662bc1d16992 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 19 Sep 2019 21:42:14 -0700 Subject: [PATCH 69/84] Responding to review comments. Minor cleanups. --- alacritty_terminal/src/display.rs | 24 ++++++++++++++---------- font/src/darwin/mod.rs | 16 ++++++++++++---- font/src/ft/mod.rs | 11 ++++++----- font/src/lib.rs | 5 +---- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index b8784947a4..9c57ba07d7 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -517,16 +517,20 @@ impl Display { let mut lines = RenderLines::new(); // Draw grid - self.renderer.with_api(config, &size_info, |mut api| { - // Iterate over each contiguous block of text - for text_run in TextRunIter::new(grid_cells.into_iter()) { - // Update underline/strikeout - lines.update(&text_run); - - // Draw text run - api.render_text_run(text_run, glyph_cache); - } - }); + { + let _sampler = self.meter.sampler(); + + self.renderer.with_api(config, &size_info, |mut api| { + // Iterate over each contiguous block of text + for text_run in TextRunIter::new(grid_cells.into_iter()) { + // Update underline/strikeout + lines.update(&text_run); + + // Draw text run + api.render_text_run(text_run, glyph_cache); + } + }); + } let mut rects = lines.into_rects(&metrics, &size_info); diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 3d4c1d857d..cb5e475290 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -456,12 +456,20 @@ impl Font { ) -> Result { let glyph_index = match key_type { KeyType::GlyphIndex(i) => i, - KeyType::Placeholder => { - self.glyph_index(' ').ok_or_else(|| Error::MissingGlyph(key_type))? - }, KeyType::Fallback(character) => { self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(key_type))? }, + KeyType::Placeholder => { + // Early return with an empty buffer for a placeholder glyph + return Ok(RasterizedGlyph { + c: KeyType::Placeholder, + width: 0, + height: 0, + top: 0, + left: 0, + buf: Vec::new(), + }); + }, }; let bounds = self.bounding_rect_for_glyph(Default::default(), glyph_index); @@ -475,7 +483,7 @@ impl Font { if rasterized_width == 0 || rasterized_height == 0 { return Ok(RasterizedGlyph { - c: ' '.into(), + c: key_type, width: 0, height: 0, top: 0, diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 620ad2c26a..3cdde444bf 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -155,11 +155,11 @@ impl crate::HbFtExt for FreeTypeRasterizer { let hb_font = &self.faces[&font_key].hb_font; let buf = UnicodeBuffer::new().add_str(text); let use_font_ligature = if self.use_font_ligatures { 1 } else { 0 }; - let features = &[ + let features = [ Feature::new(Tag::new('l', 'i', 'g', 'a'), use_font_ligature, ..), Feature::new(Tag::new('c', 'a', 'l', 't'), use_font_ligature, ..), ]; - harfbuzz_rs::shape(&*hb_font, buf, features) + harfbuzz_rs::shape(hb_font, buf, &features) } } @@ -274,9 +274,11 @@ impl FreeTypeRasterizer { trace!("Got font path={:?}", path); let ft_face = self.library.new_face(&path, index)?; - // This will different for each font so we can't use a constant but we don't want to - // look it up every time so we cache it on load. + + // This will be different for each font so we can't use a constant but we don't want to + // look it up every time so we cache it on font load. let placeholder_glyph_index = ft_face.get_char_index(' ' as usize); + // Get available pixel sizes if font isn't scalable. let non_scalable = if pattern.scalable().next().unwrap_or(true) { None @@ -329,7 +331,6 @@ impl FreeTypeRasterizer { let index = match glyph_key.id { KeyType::GlyphIndex(i) => i, KeyType::Fallback(c) => face.ft_face.get_char_index(c as usize), - // Render KeyType::Placeholder => face.placeholder_glyph_index, }; diff --git a/font/src/lib.rs b/font/src/lib.rs index d2a3a57c15..ce7a1b632b 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -68,7 +68,7 @@ mod darwin; #[cfg(target_os = "macos")] pub use darwin::*; -/// Placehodler glyph used as glyph key for cursor's RasterizedGlyphs +/// Placeholder glyph key that represents a blank gylph pub const PLACEHOLDER_GLYPH: KeyType = KeyType::Placeholder; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -156,8 +156,6 @@ pub enum KeyType { } impl Default for KeyType { - // Arbitrary non missing (non 0) glyph - // Used as a placeholder when glyph doesn't matter, such as when representing a cursor. fn default() -> Self { PLACEHOLDER_GLYPH } @@ -279,7 +277,6 @@ pub trait Rasterize { fn update_dpr(&mut self, device_pixel_ratio: f32); } -// Only implemented for the FreeType rasterizer so far. /// Conceptually this extends the Rasterizer trait with Harfbuzz specific functionality. #[cfg(not(any(target_os = "macos", windows)))] pub trait HbFtExt { From 37ff931241d3f48cdd9635c9dab5a126e95f4661 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 19 Sep 2019 23:00:16 -0700 Subject: [PATCH 70/84] Remove RenderLines in favor of converting TextRun directly RenderRect's. --- alacritty_terminal/src/display.rs | 11 +-- alacritty_terminal/src/renderer/mod.rs | 6 +- alacritty_terminal/src/renderer/rects.rs | 106 ++++++++--------------- 3 files changed, 44 insertions(+), 79 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 9c57ba07d7..6b55b753c2 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -27,7 +27,7 @@ use crate::config::{Config, StartupMode}; use crate::index::Line; use crate::message_bar::Message; use crate::meter::Meter; -use crate::renderer::rects::{RenderLines, RenderRect}; +use crate::renderer::rects::RenderRect; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::sync::FairMutex; use crate::term::{color::Rgb, RenderableCell, SizeInfo, Term}; @@ -514,17 +514,20 @@ impl Display { { let glyph_cache = &mut self.glyph_cache; - let mut lines = RenderLines::new(); + let mut rects = Vec::new(); // Draw grid { + // Tracks render timings let _sampler = self.meter.sampler(); self.renderer.with_api(config, &size_info, |mut api| { // Iterate over each contiguous block of text for text_run in TextRunIter::new(grid_cells.into_iter()) { // Update underline/strikeout - lines.update(&text_run); + rects.extend(RenderRect::iter_from_text_run( + &text_run, &metrics, &size_info, + )); // Draw text run api.render_text_run(text_run, glyph_cache); @@ -532,8 +535,6 @@ impl Display { }); } - let mut rects = lines.into_rects(&metrics, &size_info); - if let Some(message) = message_buffer { let text = message.text(&size_info); diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index b06a4baf32..fff5187564 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -1168,11 +1168,7 @@ impl<'a> RenderApi<'a> { } /// Returns an infinite iterator of hidden glyphs for a given font key and size. -fn hidden_glyph<'a, L>( - font_key: FontKey, - glyph_cache: &'a mut GlyphCache, - loader: &mut L, -) -> Glyph +fn hidden_glyph<'a, L>(font_key: FontKey, glyph_cache: &'a mut GlyphCache, loader: &mut L) -> Glyph where L: LoadGlyph, { diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index 37d732393c..84bf610bd3 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -11,11 +11,9 @@ // 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 crate::index::Point; use crate::term::cell::Flags; use crate::term::color::Rgb; use crate::term::SizeInfo; @@ -34,76 +32,46 @@ impl RenderRect { pub fn new(x: f32, y: f32, width: f32, height: f32, color: Rgb) -> Self { RenderRect { x, y, width, height, color } } -} - -struct RenderLine { - start: Point, - end: Point, - color: Rgb, -} -impl RenderLine { - fn into_rect(self, flag: Flags, metrics: &Metrics, size: &SizeInfo) -> RenderRect { - let start_x = self.start.col.0 as f32 * size.cell_width; - let end_x = (self.end.col.0 + 1) as f32 * size.cell_width; + /// Construct an iterator from a text run for flags Flags::UNDERLINE and Flags::STRIKETHROUGH, + /// iterator returns a RenderRect for each flag text_run contains. + pub fn iter_from_text_run<'a>( + text_run: &'a TextRun, + metrics: &'a Metrics, + size: &'a SizeInfo, + ) -> impl Iterator + 'a { + let start_point = text_run.start_point(); + let start_x = start_point.col.0 as f32 * size.cell_width; + let end_x = text_run.end_point().col.0 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 = (self.start.line.0 as f32 + 1.) * size.cell_height; + let line_bottom = (start_point.line.0 as f32 + 1.) * size.cell_height; let baseline = line_bottom + metrics.descent; - - let mut y = baseline - position - height / 2.; - 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, self.color) - } -} - -/// Lines for underline and strikeout. -#[derive(Default)] -pub struct RenderLines { - inner: HashMap>, -} - -impl RenderLines { - pub fn new() -> Self { - Self::default() - } - - pub fn into_rects(self, metrics: &Metrics, size: &SizeInfo) -> Vec { - self.inner - .into_iter() - .map(|(flag, lines)| -> Vec { - lines.into_iter().map(|line| line.into_rect(flag, &metrics, &size)).collect() - }) - .flatten() - .collect() - } - - /// Update the stored lines with the next text_run info. - pub fn update(&mut self, text_run: &TextRun) { - for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] { - if !text_run.flags.contains(*flag) { - continue; - } - - let new_line = RenderLine { - start: text_run.start_point(), - end: text_run.end_point(), - color: text_run.fg, - }; - self.inner.entry(*flag).or_default().push(new_line); - } + let flags = text_run.flags; + + [Flags::UNDERLINE, Flags::STRIKEOUT].iter().filter(move |flag| flags.contains(**flag)).map( + move |flag| { + 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 mut y = baseline - position - height / 2.; + let max_y = line_bottom - height; + y = y.min(max_y); + + RenderRect::new( + start_x + size.padding_x, + y + size.padding_y, + width, + height, + text_run.fg, + ) + }, + ) } } From 282adaea23be261578594fe9ef966533851dc935 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 19 Sep 2019 23:31:57 -0700 Subject: [PATCH 71/84] Rename KeyType Fallback to Char --- font/src/ft/mod.rs | 10 +++++----- font/src/lib.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 3cdde444bf..f0a8f36907 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -154,10 +154,10 @@ impl crate::HbFtExt for FreeTypeRasterizer { fn shape(&mut self, text: &str, font_key: FontKey) -> GlyphBuffer { let hb_font = &self.faces[&font_key].hb_font; let buf = UnicodeBuffer::new().add_str(text); - let use_font_ligature = if self.use_font_ligatures { 1 } else { 0 }; + let use_font_ligatures = if self.use_font_ligatures { 1 } else { 0 }; let features = [ - Feature::new(Tag::new('l', 'i', 'g', 'a'), use_font_ligature, ..), - Feature::new(Tag::new('c', 'a', 'l', 't'), use_font_ligature, ..), + Feature::new(Tag::new('l', 'i', 'g', 'a'), use_font_ligatures, ..), + Feature::new(Tag::new('c', 'a', 'l', 't'), use_font_ligatures, ..), ]; harfbuzz_rs::shape(hb_font, buf, &features) } @@ -320,7 +320,7 @@ impl FreeTypeRasterizer { // We already found a glyph index, use current font KeyType::GlyphIndex(_) | KeyType::Placeholder => glyph_key.font_key, // Harfbuzz failed to find a glyph index, try to load a font for c - KeyType::Fallback(c) => self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key), + KeyType::Char(c) => self.load_face_with_glyph(c).unwrap_or(glyph_key.font_key), } } @@ -330,7 +330,7 @@ impl FreeTypeRasterizer { let face = &self.faces[&font_key]; let index = match glyph_key.id { KeyType::GlyphIndex(i) => i, - KeyType::Fallback(c) => face.ft_face.get_char_index(c as usize), + KeyType::Char(c) => face.ft_face.get_char_index(c as usize), KeyType::Placeholder => face.placeholder_glyph_index, }; diff --git a/font/src/lib.rs b/font/src/lib.rs index ce7a1b632b..35a111e941 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -149,7 +149,7 @@ pub enum KeyType { /// Shaping returned a missing glyph or shaping did not occur. If glyph is missing system will /// attempt to load character glyph from a fallback font. If shaping did not occur this will /// first try the configured font then fallback. - Fallback(char), + Char(char), /// Placeholder glyph useful when we need a glyph but it shouldn't ever render as anything /// (cursors, wide_char_spacers, etc.) Placeholder, @@ -169,7 +169,7 @@ impl From for KeyType { impl From for KeyType { fn from(val: char) -> Self { - KeyType::Fallback(val) + KeyType::Char(val) } } From 5411fda3ae860dc0bbc922a28e5f65a5cca85e95 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 19 Sep 2019 23:38:00 -0700 Subject: [PATCH 72/84] Update comment on HbFtExt. --- font/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/font/src/lib.rs b/font/src/lib.rs index 35a111e941..1933ccb009 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -277,7 +277,7 @@ pub trait Rasterize { fn update_dpr(&mut self, device_pixel_ratio: f32); } -/// Conceptually this extends the Rasterizer trait with Harfbuzz specific functionality. +/// Extends the Rasterizer with Harfbuzz specific functionality. #[cfg(not(any(target_os = "macos", windows)))] pub trait HbFtExt { /// Shape the provided text into a set of glyphs. From 38d4eec6efec7a52d8118cb0ba5c9192ff4e907e Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Thu, 19 Sep 2019 23:39:29 -0700 Subject: [PATCH 73/84] Actually resolve merge conflict in CHANGELOG --- CHANGELOG.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1425e67984..5b73d2f853 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,17 +18,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Completions for `--class` and `-t` (short title) - Change the mouse cursor when hovering over the message bar and its close button - Support combined bold and italic text (with `font.bold_italic` to customize it) -<<<<<<< HEAD - Font Ligatures on Linux - Extra bindings for F13-F20 - Terminal escape bindings with combined modifiers - Bindings for ScrollToTop and ScrollToBottom actions -======= -- Extra bindings for F13-F20 -- Terminal escape bindings with combined modifiers -- Bindings for ScrollToTop and ScrollToBottom actions -- Font Ligatures on Linux ->>>>>>> ecfb55dee060286b55b65a9c48460eafab7fcefc ### Changed From fd1bcc36824d334064d2d138b4fef2c8e8da10e6 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Fri, 20 Sep 2019 00:34:56 -0700 Subject: [PATCH 74/84] Update Char for windows and mac osx --- font/src/darwin/mod.rs | 2 +- font/src/directwrite/mod.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index cb5e475290..5620043b26 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -456,7 +456,7 @@ impl Font { ) -> Result { let glyph_index = match key_type { KeyType::GlyphIndex(i) => i, - KeyType::Fallback(character) => { + KeyType::Char(character) => { self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(key_type))? }, KeyType::Placeholder => { diff --git a/font/src/directwrite/mod.rs b/font/src/directwrite/mod.rs index b28af4ca13..2d28e82777 100644 --- a/font/src/directwrite/mod.rs +++ b/font/src/directwrite/mod.rs @@ -132,7 +132,7 @@ impl crate::Rasterize for DirectWriteRasterizer { .first() .filter(|index| **index != 0) .ok_or_else(|| Error::MissingGlyph(' '))?, - KeyType::Fallback(c) => *font + KeyType::Char(c) => *font .get_glyph_indices(&[c as u32]) .first() .filter(|index| **index != 0) @@ -227,7 +227,7 @@ impl ::std::fmt::Display for Error { fn keytype_unwrap_char(key_type: KeyType) -> char { match key_type { KeyType::GlyphIndex(_) => panic!("Expected KeyType to be contain a char"), - KeyType::Fallback(c) => c, + KeyType::Char(c) => c, KeyType::Placeholder => ' ', } } From 226e2d06f28121b4f2aa0063b7fed111b5ff1850 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Fri, 20 Sep 2019 19:41:28 -0700 Subject: [PATCH 75/84] Update comment on KeyType --- font/src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/font/src/lib.rs b/font/src/lib.rs index 1933ccb009..f3af68644e 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -144,11 +144,9 @@ impl FontKey { /// If shaping failed or did not occur, `Fallback` will be returned. #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum KeyType { - /// Shaping returned a valid index and we can render that as expected. + /// A valid glyph index from Font face to be rasterized to a glyph GlyphIndex(u32), - /// Shaping returned a missing glyph or shaping did not occur. If glyph is missing system will - /// attempt to load character glyph from a fallback font. If shaping did not occur this will - /// first try the configured font then fallback. + /// A character that has not been converted to an index before rasterizing Char(char), /// Placeholder glyph useful when we need a glyph but it shouldn't ever render as anything /// (cursors, wide_char_spacers, etc.) From ab7567674569eeb6896f03b888ca1a90374db851 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Fri, 20 Sep 2019 20:03:30 -0700 Subject: [PATCH 76/84] Refactor iter_from_text_run to be from_text_run and move flag checking logic to text run draw loop in display --- alacritty_terminal/src/display.rs | 27 +++++++++++-- alacritty_terminal/src/renderer/rects.rs | 48 +++++++----------------- 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 6b55b753c2..68829dd11a 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -30,7 +30,7 @@ use crate::meter::Meter; use crate::renderer::rects::RenderRect; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::sync::FairMutex; -use crate::term::{color::Rgb, RenderableCell, SizeInfo, Term}; +use crate::term::{cell::Flags, color::Rgb, RenderableCell, SizeInfo, Term}; use crate::text_run::TextRunIter; use crate::window::{self, Window}; use font::{self, Rasterize}; @@ -525,9 +525,28 @@ impl Display { // Iterate over each contiguous block of text for text_run in TextRunIter::new(grid_cells.into_iter()) { // Update underline/strikeout - rects.extend(RenderRect::iter_from_text_run( - &text_run, &metrics, &size_info, - )); + if text_run.flags.contains(Flags::UNDERLINE) { + rects.push(RenderRect::from_text_run( + &text_run, + ( + metrics.descent, + metrics.underline_position, + metrics.underline_thickness, + ), + &size_info, + )); + } + if text_run.flags.contains(Flags::STRIKEOUT) { + rects.push(RenderRect::from_text_run( + &text_run, + ( + metrics.descent, + metrics.strikeout_position, + metrics.strikeout_thickness, + ), + &size_info, + )); + } // Draw text run api.render_text_run(text_run, glyph_cache); diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index 84bf610bd3..5933fd2193 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -12,9 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use font::Metrics; - -use crate::term::cell::Flags; use crate::term::color::Rgb; use crate::term::SizeInfo; use crate::text_run::TextRun; @@ -33,45 +30,26 @@ impl RenderRect { RenderRect { x, y, width, height, color } } - /// Construct an iterator from a text run for flags Flags::UNDERLINE and Flags::STRIKETHROUGH, - /// iterator returns a RenderRect for each flag text_run contains. - pub fn iter_from_text_run<'a>( - text_run: &'a TextRun, - metrics: &'a Metrics, - size: &'a SizeInfo, - ) -> impl Iterator + 'a { + pub fn from_text_run( + text_run: &TextRun, + (descent, position, thickness): (f32, f32, f32), + size: &SizeInfo, + ) -> Self { let start_point = text_run.start_point(); let start_x = start_point.col.0 as f32 * size.cell_width; - let end_x = text_run.end_point().col.0 as f32 * size.cell_width; + let end_x = (text_run.end_point().col.0 + 1) as f32 * size.cell_width; let width = end_x - start_x; let line_bottom = (start_point.line.0 as f32 + 1.) * size.cell_height; - let baseline = line_bottom + metrics.descent; - let flags = text_run.flags; - - [Flags::UNDERLINE, Flags::STRIKEOUT].iter().filter(move |flag| flags.contains(**flag)).map( - move |flag| { - 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"), - }; + let baseline = line_bottom + descent; - // Make sure lines are always visible - height = height.max(1.); + // Make sure lines are always visible + let height = thickness.max(1.); - let mut y = baseline - position - height / 2.; - let max_y = line_bottom - height; - y = y.min(max_y); + let mut y = baseline - position - height / 2.; + let max_y = line_bottom - height; + y = y.min(max_y); - RenderRect::new( - start_x + size.padding_x, - y + size.padding_y, - width, - height, - text_run.fg, - ) - }, - ) + RenderRect::new(start_x + size.padding_x, y + size.padding_y, width, height, text_run.fg) } } From 91e0606225e9b37bdf8056058ee25661f89670e9 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 25 Sep 2019 17:40:37 -0700 Subject: [PATCH 77/84] Fix render_string to render as expected --- alacritty_terminal/src/renderer/mod.rs | 61 +++++-------- alacritty_terminal/src/text_run.rs | 114 ++++++++++++------------- font/Cargo.toml | 2 +- 3 files changed, 74 insertions(+), 103 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index ef6e8cdb3e..0bad5f90f7 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -25,7 +25,7 @@ use fnv::FnvHasher; #[cfg(not(any(target_os = "macos", windows)))] use font::HbFtExt; use font::{ - self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer, PLACEHOLDER_GLYPH, + self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer, PLACEHOLDER_GLYPH, KeyType }; use glutin::dpi::PhysicalSize; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; @@ -36,14 +36,8 @@ use crate::gl; use crate::gl::types::*; use crate::index::{Column, Line}; use crate::renderer::rects::RenderRect; -<<<<<<< HEAD -use crate::term::{self, cell, color::Rgb, RenderableCell}; +use crate::term::{self, cell::{Flags, MAX_ZEROWIDTH_CHARS}, color::Rgb, RenderableCell}; use crate::text_run::{TextRun, TextRunContent}; -======= -use crate::term::cell::{self, Flags}; -use crate::term::color::Rgb; -use crate::term::{self, RenderableCell, RenderableCellContent}; ->>>>>>> 856cddc8739c32d8bbfff72dd3692f49359142a9 pub mod rects; @@ -237,7 +231,7 @@ impl GlyphCache { fn load_glyphs_for_font(&mut self, font: FontKey, loader: &mut L) { let size = self.font_size; for i in 32..=126 { - self.get(GlyphKey { font_key: font, id: font::KeyType::GlyphIndex(i), size }, loader); + self.get(GlyphKey { font_key: font, id: KeyType::GlyphIndex(i), size }, loader); } } @@ -302,8 +296,7 @@ impl GlyphCache { .expect("metrics load since font is loaded at glyph cache creation") } - // Shaping is only avaiable on linux for now - // On other OSs grab run glyphs as normal + // Since shaping is not available on Windows/BSD, text run glyphs are rasterized one by one as characters #[cfg(any(target_os = "macos", windows))] fn shape_run<'a, L>( &'a mut self, @@ -340,12 +333,14 @@ impl GlyphCache { .iter() .map(move |glyph_info| { let codepoint = glyph_info.codepoint; + // Codepoint of 0 indicates a missing or undefined glyph let id: font::KeyType = if codepoint == 0 { Self::find_fallback_char(text_run, glyph_info.cluster as usize) } else { codepoint.into() }; + let glyph_key = GlyphKey { id, font_key, size: self.font_size }; *self.get(glyph_key, loader) }) @@ -1046,39 +1041,21 @@ impl<'a> RenderApi<'a> { ) { let bg_alpha = color.map(|_| 1.0).unwrap_or(0.0); -<<<<<<< HEAD + let str_length: usize = string.chars().map(|_| 1).sum(); let text_run = TextRun { line, - span: (Column(0), Column(string.len() - 1)), - content: TextRunContent::CharRun(string.to_owned(), vec![]), + span: (Column(0), Column(str_length - 1)), + content: TextRunContent::CharRun(string.to_owned(), vec![[' '; MAX_ZEROWIDTH_CHARS]; str_length]), fg: Rgb { r: 0, g: 0, b: 0 }, bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0 }), - flags: cell::Flags::empty(), + flags: Flags::empty(), bg_alpha, }; -======= - 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::>(); ->>>>>>> 856cddc8739c32d8bbfff72dd3692f49359142a9 self.render_text_run(text_run, 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 { @@ -1116,7 +1093,7 @@ impl<'a> RenderApi<'a> { self.add_render_item(start_cell, &glyph); } - fn determine_font_key(flags: cell::Flags, glyph_cache: &GlyphCache) -> FontKey { + fn determine_font_key(flags: Flags, glyph_cache: &GlyphCache) -> FontKey { // FIXME this is super inefficient. match flags & Flags::BOLD_ITALIC { Flags::BOLD_ITALIC => glyph_cache.bold_italic_key, @@ -1160,7 +1137,7 @@ impl<'a> RenderApi<'a> { // Get font key for cell let font_key = Self::determine_font_key(text_run.flags, glyph_cache); - let shaped_glyphs = if text_run.flags.contains(cell::Flags::HIDDEN) { + let shaped_glyphs = if text_run.flags.contains(Flags::HIDDEN) { GlyphIter::Hidden(std::iter::repeat(hidden_glyph(font_key, glyph_cache, self))) } else { GlyphIter::Shaped(glyph_cache.shape_run(&run, font_key, self).into_iter()) @@ -1170,7 +1147,7 @@ impl<'a> RenderApi<'a> { text_run.cells().zip(shaped_glyphs).zip(zero_widths.iter()) { self.add_render_item(&cell, &glyph); - if text_run.flags.contains(cell::Flags::WIDE_CHAR) { + if text_run.flags.contains(Flags::WIDE_CHAR) { // Manually add instance data for WIDE_CHAR_SPACER // Renderer only renders single character width rectangles // If we don't add in this instance data then we only render one cell of @@ -1193,15 +1170,11 @@ impl<'a> RenderApi<'a> { } } -/// Returns an infinite iterator of hidden glyphs for a given font key and size. +/// Returns a hidden glyphs for a given font key and size. fn hidden_glyph<'a, L>(font_key: FontKey, glyph_cache: &'a mut GlyphCache, loader: &mut L) -> Glyph where L: LoadGlyph, { - // If text_run is hidden we don't want to shape text. - // But we still want to run each cell through add_render_item so colors get handled - // appropiately. We construct a dummy key that should be cached a majority of the time so - // that we have a valid glyph. let key = GlyphKey { id: PLACEHOLDER_GLYPH, font_key, size: glyph_cache.font_size }; *glyph_cache.get(key, loader) } @@ -1213,6 +1186,7 @@ enum GlyphIter { /// Our run is hidden and was not shaped Hidden(std::iter::Repeat), } + impl Iterator for GlyphIter where I: Iterator, @@ -1803,3 +1777,6 @@ impl Atlas { Ok(()) } } + + // Shaping is only avaiable on linux for now + // On other OSs grab run glyphs as normal \ No newline at end of file diff --git a/alacritty_terminal/src/text_run.rs b/alacritty_terminal/src/text_run.rs index 40996471bd..c4af2666bc 100644 --- a/alacritty_terminal/src/text_run.rs +++ b/alacritty_terminal/src/text_run.rs @@ -15,14 +15,15 @@ struct RunStart { bg_alpha: f32, flags: Flags, } + impl RunStart { /// Compare cell and check if it belongs to the same run. - fn is_adjacent_to_cell(&self, rc: &RenderableCell) -> bool { - self.line == rc.line - && self.fg == rc.fg - && self.bg == rc.bg - && (self.bg_alpha - rc.bg_alpha).abs() < std::f32::EPSILON - && self.flags == rc.flags + fn belongs_to_this_run(&self, render_cell: &RenderableCell) -> bool { + self.line == render_cell.line + && self.fg == render_cell.fg + && self.bg == render_cell.bg + && (self.bg_alpha - render_cell.bg_alpha).abs() < std::f32::EPSILON + && self.flags == render_cell.flags } } @@ -72,7 +73,7 @@ impl TextRun { } } - fn from_cursor_rc(start: RunStart, cursor: CursorKey) -> Self { + fn from_cursor_key(start: RunStart, cursor: CursorKey) -> Self { TextRun { line: start.line, span: (start.column, start.column), @@ -84,8 +85,8 @@ impl TextRun { } } - /// Holdover method while converting from rendering Cells to TextRuns - fn cell_at(&self, col: Column) -> RenderableCell { + /// Returns dummy RenderableCell with no content with positioning and color information from this TextRun. + fn dummy_cell_at(&self, col: Column) -> RenderableCell { RenderableCell { line: self.line, column: col, @@ -99,7 +100,7 @@ impl TextRun { /// First cell in the TextRun pub fn start_cell(&self) -> RenderableCell { - self.cell_at(self.span.0) + self.dummy_cell_at(self.span.0) } /// First point covered by this TextRun @@ -117,7 +118,7 @@ impl TextRun { let step = if self.flags.contains(Flags::WIDE_CHAR) { 2 } else { 1 }; let (Column(start), Column(end)) = self.span; // TODO: impl Step for Column (once Step is stable) to avoid unwrapping then rewrapping. - (start..=end).step_by(step).map(move |col| self.cell_at(Column(col))) + (start..=end).step_by(step).map(move |col| self.dummy_cell_at(Column(col))) } } @@ -134,20 +135,15 @@ pub struct TextRunIter { buffer_zero_width: Vec<[char; MAX_ZEROWIDTH_CHARS]>, } -// This is an explicit function (as opposed to a closure) to make it easy to use as a function -// pointer. -fn is_not_wide_char_spacer(rc: &RenderableCell) -> bool { - !rc.flags.contains(Flags::WIDE_CHAR_SPACER) -} -impl TextRunIter bool>> +impl TextRunIter bool>> where - BaseIter: Iterator, + I: Iterator, { - pub fn new(iter: BaseIter) -> Self { + pub fn new(iter: I) -> Self { TextRunIter { // Logic for WIDE_CHAR is handled internally by TextRun // So we no longer need WIDE_CHAR_SPACER at this point. - iter: iter.filter(is_not_wide_char_spacer), + iter: iter.filter(|cell| !cell.flags.contains(Flags::WIDE_CHAR_SPACER)), latest_col: None, run_start: None, cursor: None, @@ -159,36 +155,34 @@ where impl TextRunIter { /// Check if current run ends at incoming RenderableCell /// Run will not include incoming RenderableCell if it ends - fn is_end_of_run(&self, rc: &RenderableCell) -> bool { - let is_cell_not_adjacent = self + fn is_end_of_run(&self, render_cell: &RenderableCell) -> bool { + let does_cell_not_belong_to_run = self .run_start .as_ref() - .map(|run_start| !run_start.is_adjacent_to_cell(rc)) + .map(|run_start| !run_start.belongs_to_this_run(render_cell)) .unwrap_or(false); let is_col_not_adjacent = self .latest_col .as_ref() .map(|&(col, is_wide)| { - let width = if is_wide { 2usize } else { 1usize }; - col + width != rc.column && rc.column + width != col + let width = if is_wide { 2 } else { 1 }; + col + width != render_cell.column && render_cell.column + width != col }) .unwrap_or(false); - is_cell_not_adjacent || is_col_not_adjacent + does_cell_not_belong_to_run || is_col_not_adjacent } /// Add content of cell to pending TextRun buffer fn buffer_content(&mut self, inner: RenderableCellContent) { - // Add to buffer only if the next rc is a Char (not a cursor) + // Add to buffer only if the next RenderableCell is a Char (not a cursor) match inner { RenderableCellContent::Chars(chars) => { self.buffer_text.push(chars[0]); - let mut arr: [char; MAX_ZEROWIDTH_CHARS] = Default::default(); - arr.copy_from_slice(&chars[1..]); - self.buffer_zero_width.push(arr); - }, - RenderableCellContent::Cursor(cursor) => { - self.cursor = Some(cursor); + let mut zero_width_chars = <[char; MAX_ZEROWIDTH_CHARS]>::default(); + zero_width_chars.copy_from_slice(&chars[1..]); + self.buffer_zero_width.push(zero_width_chars); }, + RenderableCellContent::Cursor(cursor) => self.cursor = Some(cursor), } } @@ -199,16 +193,16 @@ impl TextRunIter { /// Start a new run by setting latest_col, run_start, and buffering content of rc /// Returns the previous runs run_start and latest_col data if available. - fn start_run(&mut self, rc: RenderableCell) -> (Option, Option) { - self.buffer_content(rc.inner); - let latest = self.latest_col.replace((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); + fn start_run(&mut self, render_cell: RenderableCell) -> (Option, Option) { + self.buffer_content(render_cell.inner); + let latest = self.latest_col.replace((render_cell.column, render_cell.flags.contains(Flags::WIDE_CHAR))); let start = self.run_start.replace(RunStart { - line: rc.line, - column: rc.column, - fg: rc.fg, - bg: rc.bg, - bg_alpha: rc.bg_alpha, - flags: rc.flags, + line: render_cell.line, + column: render_cell.column, + fg: render_cell.fg, + bg: render_cell.bg, + bg_alpha: render_cell.bg_alpha, + flags: render_cell.flags, }); (start, latest) } @@ -216,19 +210,19 @@ impl TextRunIter { /// Produce a run containing a single cursor from state of the `TextRunIter`. /// This is a destructive operation, the iterator will be in a new run state after it's /// completion. - fn produce_cursor(&mut self, rc: RenderableCell) -> Option { - let (opt_start, _) = self.start_run(rc); + fn produce_cursor(&mut self, render_cell: RenderableCell) -> Option { + let (opt_start, _) = self.start_run(render_cell); let start = opt_start?; let cursor = self.cursor.take()?; - Some(TextRun::from_cursor_rc(start, cursor)) + Some(TextRun::from_cursor_key(start, cursor)) } /// Create a run of chars from the current state of the `TextRunIter`. /// This is a destructive operation, the iterator will be in a new run state after it's /// completion. - fn produce_char_run(&mut self, rc: RenderableCell) -> Option { + fn produce_char_run(&mut self, render_cell: RenderableCell) -> Option { let prev_buffer = self.drain_buffer(); - let (start_opt, latest_col_opt) = self.start_run(rc); + let (start_opt, latest_col_opt) = self.start_run(render_cell); let start = start_opt?; let latest_col = latest_col_opt?; Some(TextRun::from_iter_state(start, latest_col, prev_buffer)) @@ -243,32 +237,32 @@ where fn next(&mut self) -> Option { let mut output = None; - while let Some(rc) = self.iter.next() { + while let Some(render_cell) = self.iter.next() { if self.latest_col.is_none() || self.run_start.is_none() { // Initial state, this is should only be hit on the first next() call of // iterator self.run_start = Some(RunStart { - line: rc.line, - column: rc.column, - fg: rc.fg, - bg: rc.bg, - bg_alpha: rc.bg_alpha, - flags: rc.flags, + line: render_cell.line, + column: render_cell.column, + fg: render_cell.fg, + bg: render_cell.bg, + bg_alpha: render_cell.bg_alpha, + flags: render_cell.flags, }) } else if self.cursor.is_some() { // Last iteration of the loop found a cursor // Return a run for the cursor and start a new run - output = self.produce_cursor(rc); + output = self.produce_cursor(render_cell); break; - } else if self.is_end_of_run(&rc) || rc.is_cursor() { + } else if self.is_end_of_run(&render_cell) || render_cell.is_cursor() { // If we find a run break or a cursor, // return what we have so far and start a new run. - output = self.produce_char_run(rc); + output = self.produce_char_run(render_cell); break; } // Build up buffer and track the latest column we've seen - self.latest_col = Some((rc.column, rc.flags.contains(Flags::WIDE_CHAR))); - self.buffer_content(rc.inner); + self.latest_col = Some((render_cell.column, render_cell.flags.contains(Flags::WIDE_CHAR))); + self.buffer_content(render_cell.inner); } // Check for any remaining buffered content and return it as a text run. // This is a destructive operation, it will return None after it excutes once. @@ -280,7 +274,7 @@ where Some(TextRun::from_iter_state(start, latest_col, self.drain_buffer())) } else if let Some(cursor) = self.cursor { let start = self.run_start.take()?; - Some(TextRun::from_cursor_rc(start, cursor)) + Some(TextRun::from_cursor_key(start, cursor)) } else { None } diff --git a/font/Cargo.toml b/font/Cargo.toml index 55307e1801..6c02b2d29b 100644 --- a/font/Cargo.toml +++ b/font/Cargo.toml @@ -23,4 +23,4 @@ core-graphics = "0.17" core-foundation-sys = "0.6" [target.'cfg(windows)'.dependencies] -dwrote = { version = "0.9.0" } \ No newline at end of file +dwrote = { version = "0.9.0" } From 6f0454df0dd912a18b84600554ecdfd63d2ff643 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 25 Sep 2019 17:46:17 -0700 Subject: [PATCH 78/84] Update CHANGELOG and format recent changes. --- CHANGELOG.md | 2 +- alacritty_terminal/src/renderer/mod.rs | 21 ++++++++++++++------- alacritty_terminal/src/text_run.rs | 10 +++++++--- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b73d2f853..0ec897a733 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Completions for `--class` and `-t` (short title) - Change the mouse cursor when hovering over the message bar and its close button - Support combined bold and italic text (with `font.bold_italic` to customize it) -- Font Ligatures on Linux +- Font Ligatures on Linux/BSD - Extra bindings for F13-F20 - Terminal escape bindings with combined modifiers - Bindings for ScrollToTop and ScrollToBottom actions diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 0bad5f90f7..1c53f604b3 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -25,7 +25,8 @@ use fnv::FnvHasher; #[cfg(not(any(target_os = "macos", windows)))] use font::HbFtExt; use font::{ - self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer, PLACEHOLDER_GLYPH, KeyType + self, FontDesc, FontKey, GlyphKey, KeyType, Rasterize, RasterizedGlyph, Rasterizer, + PLACEHOLDER_GLYPH, }; use glutin::dpi::PhysicalSize; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; @@ -36,7 +37,12 @@ use crate::gl; use crate::gl::types::*; use crate::index::{Column, Line}; use crate::renderer::rects::RenderRect; -use crate::term::{self, cell::{Flags, MAX_ZEROWIDTH_CHARS}, color::Rgb, RenderableCell}; +use crate::term::{ + self, + cell::{Flags, MAX_ZEROWIDTH_CHARS}, + color::Rgb, + RenderableCell, +}; use crate::text_run::{TextRun, TextRunContent}; pub mod rects; @@ -296,7 +302,8 @@ impl GlyphCache { .expect("metrics load since font is loaded at glyph cache creation") } - // Since shaping is not available on Windows/BSD, text run glyphs are rasterized one by one as characters + // Since shaping is not available on Windows/Mac OSX, text run glyphs are rasterized one by one + // as characters #[cfg(any(target_os = "macos", windows))] fn shape_run<'a, L>( &'a mut self, @@ -1045,7 +1052,10 @@ impl<'a> RenderApi<'a> { let text_run = TextRun { line, span: (Column(0), Column(str_length - 1)), - content: TextRunContent::CharRun(string.to_owned(), vec![[' '; MAX_ZEROWIDTH_CHARS]; str_length]), + content: TextRunContent::CharRun(string.to_owned(), vec![ + [' '; MAX_ZEROWIDTH_CHARS]; + str_length + ]), fg: Rgb { r: 0, g: 0, b: 0 }, bg: color.unwrap_or(Rgb { r: 0, g: 0, b: 0 }), flags: Flags::empty(), @@ -1777,6 +1787,3 @@ impl Atlas { Ok(()) } } - - // Shaping is only avaiable on linux for now - // On other OSs grab run glyphs as normal \ No newline at end of file diff --git a/alacritty_terminal/src/text_run.rs b/alacritty_terminal/src/text_run.rs index c4af2666bc..88761f6a07 100644 --- a/alacritty_terminal/src/text_run.rs +++ b/alacritty_terminal/src/text_run.rs @@ -85,7 +85,8 @@ impl TextRun { } } - /// Returns dummy RenderableCell with no content with positioning and color information from this TextRun. + /// Returns dummy RenderableCell with no content with positioning and color information from + /// this TextRun. fn dummy_cell_at(&self, col: Column) -> RenderableCell { RenderableCell { line: self.line, @@ -195,7 +196,9 @@ impl TextRunIter { /// Returns the previous runs run_start and latest_col data if available. fn start_run(&mut self, render_cell: RenderableCell) -> (Option, Option) { self.buffer_content(render_cell.inner); - let latest = self.latest_col.replace((render_cell.column, render_cell.flags.contains(Flags::WIDE_CHAR))); + let latest = self + .latest_col + .replace((render_cell.column, render_cell.flags.contains(Flags::WIDE_CHAR))); let start = self.run_start.replace(RunStart { line: render_cell.line, column: render_cell.column, @@ -261,7 +264,8 @@ where break; } // Build up buffer and track the latest column we've seen - self.latest_col = Some((render_cell.column, render_cell.flags.contains(Flags::WIDE_CHAR))); + self.latest_col = + Some((render_cell.column, render_cell.flags.contains(Flags::WIDE_CHAR))); self.buffer_content(render_cell.inner); } // Check for any remaining buffered content and return it as a text run. From f7d0b4a2cea2cfe37b1066dd05cc61219fbc3ab6 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 25 Sep 2019 17:59:16 -0700 Subject: [PATCH 79/84] Resolving review comments, mainly renaming --- alacritty_terminal/src/renderer/mod.rs | 25 ++++---------- alacritty_terminal/src/text_run.rs | 46 ++++++++++++++------------ 2 files changed, 30 insertions(+), 41 deletions(-) diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 1c53f604b3..4bb238c278 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -144,7 +144,7 @@ pub struct RectShaderProgram { u_color: GLint, } -#[derive(Copy, Debug, Clone)] +#[derive(Clone, Copy, Debug, Default)] pub struct Glyph { tex_id: GLuint, top: f32, @@ -1148,7 +1148,7 @@ impl<'a> RenderApi<'a> { let font_key = Self::determine_font_key(text_run.flags, glyph_cache); let shaped_glyphs = if text_run.flags.contains(Flags::HIDDEN) { - GlyphIter::Hidden(std::iter::repeat(hidden_glyph(font_key, glyph_cache, self))) + GlyphIter::Hidden } else { GlyphIter::Shaped(glyph_cache.shape_run(&run, font_key, self).into_iter()) }; @@ -1157,15 +1157,11 @@ impl<'a> RenderApi<'a> { text_run.cells().zip(shaped_glyphs).zip(zero_widths.iter()) { self.add_render_item(&cell, &glyph); + // Add empty spacer for full width characters if text_run.flags.contains(Flags::WIDE_CHAR) { - // Manually add instance data for WIDE_CHAR_SPACER - // Renderer only renders single character width rectangles - // If we don't add in this instance data then we only render one cell of - // fg/bg/strikethrough/etc. for WIDE_CHARs - let glyph = hidden_glyph(font_key, glyph_cache, self); self.add_render_item( &RenderableCell { column: cell.column + 1, ..cell.clone() }, - &glyph, + &Glyph::default(), ); } self.render_zero_widths( @@ -1180,21 +1176,12 @@ impl<'a> RenderApi<'a> { } } -/// Returns a hidden glyphs for a given font key and size. -fn hidden_glyph<'a, L>(font_key: FontKey, glyph_cache: &'a mut GlyphCache, loader: &mut L) -> Glyph -where - L: LoadGlyph, -{ - let key = GlyphKey { id: PLACEHOLDER_GLYPH, font_key, size: glyph_cache.font_size }; - *glyph_cache.get(key, loader) -} - /// Abstracts iteration over a run of hidden glyphs or shaped glyphs. enum GlyphIter { /// Our run was not hidden and our glyphs were shaped Shaped(I), /// Our run is hidden and was not shaped - Hidden(std::iter::Repeat), + Hidden, } impl Iterator for GlyphIter @@ -1205,8 +1192,8 @@ where fn next(&mut self) -> Option { match self { - GlyphIter::Hidden(inner) => inner.next(), GlyphIter::Shaped(inner) => inner.next(), + GlyphIter::Hidden => Some(Glyph::default()), } } } diff --git a/alacritty_terminal/src/text_run.rs b/alacritty_terminal/src/text_run.rs index 88761f6a07..f509653d00 100644 --- a/alacritty_terminal/src/text_run.rs +++ b/alacritty_terminal/src/text_run.rs @@ -18,7 +18,8 @@ struct RunStart { impl RunStart { /// Compare cell and check if it belongs to the same run. - fn belongs_to_this_run(&self, render_cell: &RenderableCell) -> bool { + #[inline] + fn belongs_to_text_run(&self, render_cell: &RenderableCell) -> bool { self.line == render_cell.line && self.fg == render_cell.fg && self.bg == render_cell.bg @@ -56,23 +57,6 @@ pub struct TextRun { } impl TextRun { - fn from_iter_state( - start: RunStart, - (latest, is_wide): LatestCol, - buffer: (String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), - ) -> Self { - let end_column = if is_wide { latest + 1 } else { latest }; - TextRun { - line: start.line, - span: (start.column, end_column), - content: TextRunContent::CharRun(buffer.0, buffer.1), - fg: start.fg, - bg: start.bg, - bg_alpha: start.bg_alpha, - flags: start.flags, - } - } - fn from_cursor_key(start: RunStart, cursor: CursorKey) -> Self { TextRun { line: start.line, @@ -85,7 +69,7 @@ impl TextRun { } } - /// Returns dummy RenderableCell with no content with positioning and color information from + /// Returns dummy RenderableCell containing no content with positioning and color information from /// this TextRun. fn dummy_cell_at(&self, col: Column) -> RenderableCell { RenderableCell { @@ -160,7 +144,7 @@ impl TextRunIter { let does_cell_not_belong_to_run = self .run_start .as_ref() - .map(|run_start| !run_start.belongs_to_this_run(render_cell)) + .map(|run_start| !run_start.belongs_to_text_run(render_cell)) .unwrap_or(false); let is_col_not_adjacent = self .latest_col @@ -175,7 +159,7 @@ impl TextRunIter { /// Add content of cell to pending TextRun buffer fn buffer_content(&mut self, inner: RenderableCellContent) { - // Add to buffer only if the next RenderableCell is a Char (not a cursor) + // Add to buffer only if the next RenderableCell is a Chars (not a cursor) match inner { RenderableCellContent::Chars(chars) => { self.buffer_text.push(chars[0]); @@ -228,7 +212,25 @@ impl TextRunIter { let (start_opt, latest_col_opt) = self.start_run(render_cell); let start = start_opt?; let latest_col = latest_col_opt?; - Some(TextRun::from_iter_state(start, latest_col, prev_buffer)) + Some(Self::build_text_run(start, latest_col, prev_buffer)) + } + + /// Build a TextRun instance from passed state of TextRunIter + fn build_text_run( + start: RunStart, + (latest, is_wide): LatestCol, + buffer: (String, Vec<[char; MAX_ZEROWIDTH_CHARS]>), + ) -> TextRun { + let end_column = if is_wide { latest + 1 } else { latest }; + TextRun { + line: start.line, + span: (start.column, end_column), + content: TextRunContent::CharRun(buffer.0, buffer.1), + fg: start.fg, + bg: start.bg, + bg_alpha: start.bg_alpha, + flags: start.flags, + } } } From 0113097c4a509c57e351c4cdc88f839045374bf3 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 25 Sep 2019 18:39:26 -0700 Subject: [PATCH 80/84] Fix call to from_iter_state that wasn't renamed. Avoid allocating Vec of RenderableCells, allocate a Vec of TextRun instead. --- alacritty_terminal/src/display.rs | 10 +++++----- alacritty_terminal/src/text_run.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 68829dd11a..4d69410738 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -30,8 +30,8 @@ use crate::meter::Meter; use crate::renderer::rects::RenderRect; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::sync::FairMutex; -use crate::term::{cell::Flags, color::Rgb, RenderableCell, SizeInfo, Term}; -use crate::text_run::TextRunIter; +use crate::term::{cell::Flags, color::Rgb, SizeInfo, Term}; +use crate::text_run::{TextRunIter, TextRun}; use crate::window::{self, Window}; use font::{self, Rasterize}; @@ -472,8 +472,8 @@ impl Display { let metrics = self.glyph_cache.font_metrics(); let window_focused = self.window.is_focused; - let grid_cells: Vec = - terminal.renderable_cells(config, window_focused).collect(); + let grid_text_runs: Vec = + TextRunIter::new(terminal.renderable_cells(config, window_focused)).collect(); // Get message from terminal to ignore modifications after lock is dropped let message_buffer = terminal.message_buffer_mut().message(); @@ -523,7 +523,7 @@ impl Display { self.renderer.with_api(config, &size_info, |mut api| { // Iterate over each contiguous block of text - for text_run in TextRunIter::new(grid_cells.into_iter()) { + for text_run in grid_text_runs { // Update underline/strikeout if text_run.flags.contains(Flags::UNDERLINE) { rects.push(RenderRect::from_text_run( diff --git a/alacritty_terminal/src/text_run.rs b/alacritty_terminal/src/text_run.rs index f509653d00..4b23be7abc 100644 --- a/alacritty_terminal/src/text_run.rs +++ b/alacritty_terminal/src/text_run.rs @@ -163,7 +163,7 @@ impl TextRunIter { match inner { RenderableCellContent::Chars(chars) => { self.buffer_text.push(chars[0]); - let mut zero_width_chars = <[char; MAX_ZEROWIDTH_CHARS]>::default(); + let mut zero_width_chars = [char::default(); MAX_ZEROWIDTH_CHARS]; zero_width_chars.copy_from_slice(&chars[1..]); self.buffer_zero_width.push(zero_width_chars); }, @@ -277,7 +277,7 @@ where let start = self.run_start.take()?; let latest_col = self.latest_col.take()?; // Save leftover buffer and empty it - Some(TextRun::from_iter_state(start, latest_col, self.drain_buffer())) + Some(Self::build_text_run(start, latest_col, self.drain_buffer())) } else if let Some(cursor) = self.cursor { let start = self.run_start.take()?; Some(TextRun::from_cursor_key(start, cursor)) From 7da599a5a50b96695c7a5c864a50ebb73cc46d6d Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 25 Sep 2019 18:58:51 -0700 Subject: [PATCH 81/84] Clean up directwrite code and remove keytype_unwrap_char --- alacritty_terminal/src/display.rs | 2 +- alacritty_terminal/src/text_run.rs | 4 +-- font/src/directwrite/mod.rs | 40 +++++++++++------------------- 3 files changed, 17 insertions(+), 29 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 4d69410738..95b8f29c6e 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -31,7 +31,7 @@ use crate::renderer::rects::RenderRect; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::sync::FairMutex; use crate::term::{cell::Flags, color::Rgb, SizeInfo, Term}; -use crate::text_run::{TextRunIter, TextRun}; +use crate::text_run::{TextRun, TextRunIter}; use crate::window::{self, Window}; use font::{self, Rasterize}; diff --git a/alacritty_terminal/src/text_run.rs b/alacritty_terminal/src/text_run.rs index 4b23be7abc..2fcefdd644 100644 --- a/alacritty_terminal/src/text_run.rs +++ b/alacritty_terminal/src/text_run.rs @@ -69,8 +69,8 @@ impl TextRun { } } - /// Returns dummy RenderableCell containing no content with positioning and color information from - /// this TextRun. + /// Returns dummy RenderableCell containing no content with positioning and color information + /// from this TextRun. fn dummy_cell_at(&self, col: Column) -> RenderableCell { RenderableCell { line: self.line, diff --git a/font/src/directwrite/mod.rs b/font/src/directwrite/mod.rs index 2d28e82777..4e719db54c 100644 --- a/font/src/directwrite/mod.rs +++ b/font/src/directwrite/mod.rs @@ -124,21 +124,19 @@ impl crate::Rasterize for DirectWriteRasterizer { let offset = GlyphOffset { advanceOffset: 0.0, ascenderOffset: 0.0 }; - let glyph_index: u16 = match glyph.id { - KeyType::GlyphIndex(i) => i as u16, - // On placeholder return a missing glyph error with the placeholder unicode character - KeyType::Placeholder => *font - .get_glyph_indices(&[' ' as u32]) - .first() - .filter(|index| **index != 0) - .ok_or_else(|| Error::MissingGlyph(' '))?, - KeyType::Char(c) => *font - .get_glyph_indices(&[c as u32]) - .first() - .filter(|index| **index != 0) - .ok_or_else(|| Error::MissingGlyph(c))?, + let glyph_char: char = match glyph.id { + KeyType::Char(c) => c, + KeyType::Placeholder => ' ', + // We never send a glyph index on Windows so this variant can't show up + KeyType::GlyphIndex(i) => unreachable!(), }; + let glyph_index: u16 = *font + .get_glyph_indices(&[glyph_char as u32]) + .first() + .filter(|index| **index != 0) + .ok_or_else(|| Error::MissingGlyph(glyph_char)); + let glyph_run = dwrote::DWRITE_GLYPH_RUN { fontFace: unsafe { font.as_ptr() }, fontEmSize: glyph.size.as_f32_pts(), @@ -166,14 +164,14 @@ impl crate::Rasterize for DirectWriteRasterizer { 0.0, ) // Since we don't shape on windows our KeyType will always be a char - .or_else(|_| Err(Error::MissingGlyph(keytype_unwrap_char(glyph.id))))?; + .or_else(|_| Err(Error::MissingGlyph(glyph_char)))?; let bounds = glyph_analysis .get_alpha_texture_bounds(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1) - .or_else(|_| Err(Error::MissingGlyph(keytype_unwrap_char(glyph.id))))?; + .or_else(|_| Err(Error::MissingGlyph(glyph_char)))?; let buf = glyph_analysis .create_alpha_texture(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1, bounds) - .or_else(|_| Err(Error::MissingGlyph(keytype_unwrap_char(glyph.id))))?; + .or_else(|_| Err(Error::MissingGlyph(glyph_char)))?; Ok(RasterizedGlyph { c: glyph.id, @@ -221,13 +219,3 @@ impl ::std::fmt::Display for Error { } } } - -// Used for error reporting only (to return a missing glyph). Windows doesn't shape text right now -// keys will always be chars. -fn keytype_unwrap_char(key_type: KeyType) -> char { - match key_type { - KeyType::GlyphIndex(_) => panic!("Expected KeyType to be contain a char"), - KeyType::Char(c) => c, - KeyType::Placeholder => ' ', - } -} From 816ec2f28d4bfdfebacd3f1f08874bb5f7ad5172 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Wed, 2 Oct 2019 17:50:43 -0700 Subject: [PATCH 82/84] Create text_runs method on terminal to replace renderable_cells --- alacritty_terminal/src/display.rs | 3 +-- alacritty_terminal/src/term/mod.rs | 22 +++++++++++++++++++++- alacritty_terminal/src/text_run.rs | 6 ++---- font/src/ft/mod.rs | 6 +++++- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index 95b8f29c6e..fb772491ab 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -472,8 +472,7 @@ impl Display { let metrics = self.glyph_cache.font_metrics(); let window_focused = self.window.is_focused; - let grid_text_runs: Vec = - TextRunIter::new(terminal.renderable_cells(config, window_focused)).collect(); + let grid_text_runs: Vec = terminal.text_runs(config, window_focused).collect(); // Get message from terminal to ignore modifications after lock is dropped let message_buffer = terminal.message_buffer_mut().message(); diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 91ebdd6437..ff7a47ebab 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -38,6 +38,7 @@ use crate::message_bar::MessageBuffer; use crate::selection::{self, Selection, SelectionRange, Span}; use crate::term::cell::{Cell, Flags, LineLength}; use crate::term::color::Rgb; +use crate::text_run::{TextRun, TextRunIter}; use crate::url::Url; #[cfg(windows)] @@ -1104,7 +1105,7 @@ impl Term { /// A renderable cell is any cell which has content other than the default /// background color. Cells with an alternate background color are /// considered renderable as are cells with any text content. - pub fn renderable_cells<'b>( + fn renderable_cells<'b>( &'b self, config: &'b Config, window_focused: bool, @@ -1120,6 +1121,25 @@ impl Term { RenderableCellsIter::new(&self, config, selection, cursor) } + /// Iterate over the text runs in the terminal + /// + /// A text run is a continuous line of cells that all share the same rendering properties (background color, foreground color, etc.). + pub fn text_runs<'b>( + &'b self, + config: &'b Config, + window_focused: bool, + ) -> impl Iterator + 'b { + // Logic for WIDE_CHAR is handled internally by TextRun + // So we no longer need WIDE_CHAR_SPACER at this point. + let filtered_cells: std::iter::Filter< + RenderableCellsIter<'b>, + fn(&RenderableCell) -> bool, + > = self + .renderable_cells(config, window_focused) + .filter(|cell| !cell.flags.contains(Flags::WIDE_CHAR_SPACER)); + TextRunIter::new(filtered_cells) + } + /// Resize terminal to new dimensions pub fn resize(&mut self, size: &SizeInfo) { debug!("Resizing terminal"); diff --git a/alacritty_terminal/src/text_run.rs b/alacritty_terminal/src/text_run.rs index 2fcefdd644..a9734e6863 100644 --- a/alacritty_terminal/src/text_run.rs +++ b/alacritty_terminal/src/text_run.rs @@ -120,15 +120,13 @@ pub struct TextRunIter { buffer_zero_width: Vec<[char; MAX_ZEROWIDTH_CHARS]>, } -impl TextRunIter bool>> +impl TextRunIter where I: Iterator, { pub fn new(iter: I) -> Self { TextRunIter { - // Logic for WIDE_CHAR is handled internally by TextRun - // So we no longer need WIDE_CHAR_SPACER at this point. - iter: iter.filter(|cell| !cell.flags.contains(Flags::WIDE_CHAR_SPACER)), + iter, latest_col: None, run_start: None, cursor: None, diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index f0a8f36907..f79020255c 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -273,7 +273,7 @@ impl FreeTypeRasterizer { } trace!("Got font path={:?}", path); - let ft_face = self.library.new_face(&path, index)?; + let mut ft_face = self.library.new_face(&path, index)?; // This will be different for each font so we can't use a constant but we don't want to // look it up every time so we cache it on font load. @@ -291,6 +291,10 @@ impl FreeTypeRasterizer { // Construct harfbuzz font let hb_font = Font::new(HbFace::from_file(&path, index as u32)?); + //let hb_font = unsafe { + // let hb_font_raw = harfbuzz_rs::hb::hb_ft_font_create_referenced(ft_face.raw_mut() as *mut _ as *mut u64 as *mut _); + // harfbuzz_rs::Owned::from_raw(hb_font_raw) + //}; let face = Face { ft_face, From 8e619b7851bf735ca13b83aac26a1c52622fc815 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Mon, 7 Oct 2019 10:55:24 -0700 Subject: [PATCH 83/84] Format TextRun changes --- alacritty_terminal/src/display.rs | 2 +- alacritty_terminal/src/term/mod.rs | 7 ++++--- font/src/ft/mod.rs | 7 ++++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs index fb772491ab..c8276dc6f1 100644 --- a/alacritty_terminal/src/display.rs +++ b/alacritty_terminal/src/display.rs @@ -31,7 +31,7 @@ use crate::renderer::rects::RenderRect; use crate::renderer::{self, GlyphCache, QuadRenderer}; use crate::sync::FairMutex; use crate::term::{cell::Flags, color::Rgb, SizeInfo, Term}; -use crate::text_run::{TextRun, TextRunIter}; +use crate::text_run::TextRun; use crate::window::{self, Window}; use font::{self, Rasterize}; diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 080cd3e548..13e65b7f0b 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -1131,13 +1131,14 @@ impl Term { } /// Iterate over the text runs in the terminal - /// - /// A text run is a continuous line of cells that all share the same rendering properties (background color, foreground color, etc.). + /// + /// A text run is a continuous line of cells that all share the same rendering properties + /// (background color, foreground color, etc.). pub fn text_runs<'b>( &'b self, config: &'b Config, window_focused: bool, - ) -> impl Iterator + 'b { + ) -> impl Iterator + 'b { // Logic for WIDE_CHAR is handled internally by TextRun // So we no longer need WIDE_CHAR_SPACER at this point. let filtered_cells: std::iter::Filter< diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index f79020255c..01233bf63a 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -292,7 +292,12 @@ impl FreeTypeRasterizer { // Construct harfbuzz font let hb_font = Font::new(HbFace::from_file(&path, index as u32)?); //let hb_font = unsafe { - // let hb_font_raw = harfbuzz_rs::hb::hb_ft_font_create_referenced(ft_face.raw_mut() as *mut _ as *mut u64 as *mut _); + // // Cast here is done to convert a C struct from the freetype-rs Rust type to the + // // freetype Rust type that harfbuzz accepts. + // let hb_font_raw = harfbuzz_rs::hb::hb_ft_font_create_referenced(ft_face.raw_mut() + // as *mut _ + // as *mut u64 + // as *mut _); // harfbuzz_rs::Owned::from_raw(hb_font_raw) //}; From b293d08fa9e9b1c40fe9505f0434342a9009be90 Mon Sep 17 00:00:00 2001 From: thunderseethe Date: Mon, 7 Oct 2019 14:45:55 -0700 Subject: [PATCH 84/84] Tidy up merge and ensure build behaves as expected --- alacritty/src/display.rs | 26 ++++++++++++++++---------- alacritty_terminal/src/lib.rs | 2 +- alacritty_terminal/src/renderer/mod.rs | 16 ++++++++-------- alacritty_terminal/src/term/mod.rs | 2 +- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/alacritty/src/display.rs b/alacritty/src/display.rs index a8f72b3ec9..abb4bc610b 100644 --- a/alacritty/src/display.rs +++ b/alacritty/src/display.rs @@ -31,10 +31,11 @@ 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::renderer::rects::{RenderLines, RenderRect}; +use alacritty_terminal::renderer::rects::{RenderRect}; use alacritty_terminal::renderer::{self, GlyphCache, QuadRenderer}; use alacritty_terminal::term::color::Rgb; -use alacritty_terminal::term::{RenderableCell, SizeInfo, Term}; +use alacritty_terminal::term::{cell::Flags, SizeInfo, Term}; +use alacritty_terminal::text_run::TextRun; use crate::config::Config; use crate::event::{FontResize, Resize}; @@ -244,7 +245,7 @@ impl Display { 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())?; + let rasterizer = font::Rasterizer::new(dpr as f32, config.font.use_thin_strokes(), config.font.ligatures())?; // Initialize glyph cache let glyph_cache = { @@ -362,7 +363,7 @@ impl Display { message_buffer: &MessageBuffer, config: &Config, ) { - let grid_cells: Vec = terminal.renderable_cells(config).collect(); + let grid_text_runs: Vec = terminal.text_runs(config).collect(); let visual_bell_intensity = terminal.visual_bell.intensity(); let background_color = terminal.background_color(); let metrics = self.glyph_cache.font_metrics(); @@ -380,7 +381,7 @@ impl Display { api.clear(background_color); }); - let mut lines = RenderLines::new(); + let mut rects: Vec = Vec::new(); // Draw grid { @@ -388,18 +389,23 @@ impl Display { self.renderer.with_api(&config, &size_info, |mut api| { // Iterate over all non-empty cells in the grid - for cell in grid_cells { + for text_run in grid_text_runs { // Update underline/strikeout - lines.update(cell); + if text_run.flags.contains(Flags::UNDERLINE) { + let underline_metrics = (metrics.descent, metrics.underline_position, metrics.underline_thickness); + rects.push(RenderRect::from_text_run(&text_run, underline_metrics, &size_info)); + } + if text_run.flags.contains(Flags::STRIKEOUT) { + let strikeout_metrics = (metrics.descent, metrics.strikeout_position, metrics.strikeout_thickness); + rects.push(RenderRect::from_text_run(&text_run, strikeout_metrics, &size_info)); + } // Draw the cell - api.render_cell(cell, glyph_cache); + api.render_text_run(text_run, glyph_cache); } }); } - let mut rects = lines.into_rects(&metrics, &size_info); - if let Some(message) = message_buffer.message() { let text = message.text(&size_info); diff --git a/alacritty_terminal/src/lib.rs b/alacritty_terminal/src/lib.rs index 966032a14d..f9cd1fa396 100644 --- a/alacritty_terminal/src/lib.rs +++ b/alacritty_terminal/src/lib.rs @@ -37,7 +37,7 @@ pub mod renderer; pub mod selection; pub mod sync; pub mod term; -mod text_run; +pub mod text_run; pub mod tty; pub mod url; pub mod util; diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 7c68012e0b..711ef5241c 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -37,10 +37,10 @@ use crate::gl; use crate::gl::types::*; use crate::index::{Column, Line}; use crate::renderer::rects::RenderRect; -use crate::term::cell::{self, Flags, MAX_ZEROWIDTH_CHARS}; +use crate::term::cell::{Flags, MAX_ZEROWIDTH_CHARS}; use crate::term::color::Rgb; use crate::term::SizeInfo; -use crate::term::{self, RenderableCell, RenderableCellContent}; +use crate::term::{self, RenderableCell}; use crate::text_run::{TextRun, TextRunContent}; use crate::util; @@ -1101,7 +1101,7 @@ impl<'a, C> RenderApi<'a, C> { fn render_cursor( &mut self, - start_cell: &RenderableCell, + start_cell: RenderableCell, cursor_key: CursorKey, glyph_cache: &mut GlyphCache, ) { @@ -1132,7 +1132,7 @@ impl<'a, C> RenderApi<'a, C> { fn render_zero_widths<'r, I>( &mut self, zero_width_chars: I, - cell: &RenderableCell, + cell: RenderableCell, font_key: FontKey, glyph_cache: &mut GlyphCache, ) where @@ -1157,7 +1157,7 @@ impl<'a, C> RenderApi<'a, C> { pub fn render_text_run(&mut self, text_run: TextRun, glyph_cache: &mut GlyphCache) { match &text_run.content { TextRunContent::Cursor(cursor_key) => { - self.render_cursor(&text_run.start_cell(), *cursor_key, glyph_cache) + self.render_cursor(text_run.start_cell(), *cursor_key, glyph_cache) }, TextRunContent::CharRun(run, zero_widths) => { // Get font key for cell @@ -1172,17 +1172,17 @@ impl<'a, C> RenderApi<'a, C> { for ((cell, glyph), zero_width_chars) in text_run.cells().zip(shaped_glyphs).zip(zero_widths.iter()) { - self.add_render_item(&cell, &glyph); + self.add_render_item(cell, &glyph); // Add empty spacer for full width characters if text_run.flags.contains(Flags::WIDE_CHAR) { self.add_render_item( - &RenderableCell { column: cell.column + 1, ..cell.clone() }, + RenderableCell { column: cell.column + 1, ..cell }, &Glyph::default(), ); } self.render_zero_widths( zero_width_chars.iter().filter(|c| **c != ' '), - &cell, + cell, font_key, glyph_cache, ); diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index f3be5dc5db..8279c58be8 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -1080,7 +1080,7 @@ impl Term { // Logic for WIDE_CHAR is handled internally by TextRun // So we no longer need WIDE_CHAR_SPACER at this point. let filtered_cells: std::iter::Filter< - RenderableCellsIter<'b>, + RenderableCellsIter<'b, C>, fn(&RenderableCell) -> bool, > = self .renderable_cells(config)