diff --git a/CHANGELOG.md b/CHANGELOG.md index c2c5b27..b472f60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Discard scrolling region escape with bottom above top - Opacity always applying to cells with their background color matching the teriminal background - Allow semicolons when setting titles using an OSC +- Background always opaque on X11 +- Skipping redraws on PTY update +- Not redrawing while resizing on Windows/macOS +- Decorations `none` launching an invisible window on Windows +- Alacritty turning transparent when opening another window on macOS with chunkwm +- Startup mode `Maximized` having no effect on Windows +- Inserting Emojis using `Super+.` or compose sequences on Windows +- Change mouse cursor depending on mode with Wayland +- Hide mouse cursor when typing if the `mouse.hide_when_typing` option is set on Wayland +- Glitches when DPI changes on Windows +- Crash when resuming after suspension +- Crash when trying to start on X11 with a Wayland compositor running +- Crash with a virtual display connected on X11 ### Removed diff --git a/Cargo.lock b/Cargo.lock index 6e25863..0aba11d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,7 +2,7 @@ # It is not intended for manual editing. [[package]] name = "adler32" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -27,15 +27,23 @@ 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)", "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)", + "font 0.1.0", + "glutin 0.22.0-alpha3 (git+https://github.com/chrisduerr/glutin)", + "image 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "notify 4.0.13 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (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 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "x11-dl 2.18.4 (registry+https://github.com/rust-lang/crates.io-index)", "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -44,15 +52,12 @@ name = "alacritty_terminal" version = "0.3.3" 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)", + "bitflags 1.2.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)", "dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "font 0.1.0", "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", - "glutin 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)", - "image 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mio 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", @@ -65,9 +70,8 @@ dependencies = [ "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "rfind_url 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (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)", "static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -78,7 +82,6 @@ dependencies = [ "widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "winpty 0.1.0", - "x11-dl 2.18.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -86,7 +89,7 @@ name = "andrew" version = "0.2.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)", + "bitflags 1.2.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.9 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -117,7 +120,7 @@ dependencies = [ [[package]] name = "arc-swap" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -133,6 +136,11 @@ dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "arrayvec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "atty" version = "0.2.13" @@ -149,11 +157,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.37" +version = "0.3.38" 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)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -181,7 +189,7 @@ version = "0.33.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cexpr 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "clang-sys 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", @@ -195,7 +203,7 @@ dependencies = [ [[package]] name = "bitflags" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -245,6 +253,16 @@ dependencies = [ "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "calloop" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "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)", + "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cc" version = "1.0.45" @@ -264,15 +282,14 @@ dependencies = [ [[package]] name = "cfg-if" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cgl" -version = "0.2.3" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "gleam 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -293,7 +310,7 @@ 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)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.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)", "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -313,7 +330,7 @@ name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -329,7 +346,7 @@ name = "cocoa" version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "block 0.1.6 (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)", @@ -382,7 +399,7 @@ name = "core-graphics" version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.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.62 (registry+https://github.com/rust-lang/crates.io-index)", @@ -399,20 +416,24 @@ dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "core-video-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.10 (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)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crc32fast" version = "1.2.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)", -] - -[[package]] -name = "crossbeam-channel" -version = "0.3.9" -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)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -430,7 +451,7 @@ 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)", + "cfg-if 0.1.10 (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.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -450,7 +471,7 @@ name = "crossbeam-utils" 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)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -459,20 +480,10 @@ name = "deflate" version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "derivative" -version = "1.0.3" -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.44 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "dirs" version = "1.0.5" @@ -483,6 +494,11 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dispatch" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "dlib" version = "0.4.1" @@ -513,23 +529,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "either" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "embed-resource" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "vswhom 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -538,7 +554,7 @@ 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)", - "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -550,7 +566,7 @@ 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)", - "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -578,7 +594,7 @@ name = "failure" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -598,7 +614,7 @@ name = "filetime" version = "0.2.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)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (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.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -606,9 +622,10 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "miniz_oxide 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -658,7 +675,7 @@ name = "foreign-types-macros" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -678,7 +695,7 @@ name = "freetype-rs" version = "0.19.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)", + "bitflags 1.2.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.62 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -698,7 +715,7 @@ name = "fsevent" version = "0.4.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)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -720,7 +737,7 @@ name = "fuchsia-zircon" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -734,30 +751,20 @@ name = "getrandom" version = "0.1.12" 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)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gif" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "color_quant 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "gl_generator" -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.8 (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 = "gl_generator" version = "0.13.1" @@ -768,14 +775,6 @@ dependencies = [ "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "gleam" -version = "0.6.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "glob" version = "0.2.11" @@ -783,68 +782,68 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "glutin" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.22.0-alpha3" +source = "git+https://github.com/chrisduerr/glutin#89aed1cbfe38532c15d073e3b0822362cab3e044" dependencies = [ "android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "cgl 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "cgl 0.3.2 (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)", - "derivative 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "glutin_egl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "glutin_emscripten_sys 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "glutin_gles2_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "glutin_glx_sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "glutin_wgl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "glutin_egl_sys 0.1.3 (git+https://github.com/chrisduerr/glutin)", + "glutin_emscripten_sys 0.1.0 (git+https://github.com/chrisduerr/glutin)", + "glutin_gles2_sys 0.1.3 (git+https://github.com/chrisduerr/glutin)", + "glutin_glx_sys 0.1.5 (git+https://github.com/chrisduerr/glutin)", + "glutin_wgl_sys 0.1.3 (git+https://github.com/chrisduerr/glutin)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libloading 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "winit 0.19.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winit 0.20.0-alpha3 (git+https://github.com/rust-windowing/winit)", ] [[package]] name = "glutin_egl_sys" version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/chrisduerr/glutin#89aed1cbfe38532c15d073e3b0822362cab3e044" dependencies = [ - "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glutin_emscripten_sys" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/chrisduerr/glutin#89aed1cbfe38532c15d073e3b0822362cab3e044" [[package]] name = "glutin_gles2_sys" version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/chrisduerr/glutin#89aed1cbfe38532c15d073e3b0822362cab3e044" dependencies = [ - "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", "objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glutin_glx_sys" version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/chrisduerr/glutin#89aed1cbfe38532c15d073e3b0822362cab3e044" dependencies = [ - "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", "x11-dl 2.18.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glutin_wgl_sys" version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/chrisduerr/glutin#89aed1cbfe38532c15d073e3b0822362cab3e044" dependencies = [ - "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -858,7 +857,7 @@ dependencies = [ [[package]] name = "humantime" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -880,7 +879,7 @@ version = "0.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "gif 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "gif 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)", "jpeg-decoder 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", @@ -896,7 +895,7 @@ name = "inflate" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -904,7 +903,7 @@ name = "inotify" version = "0.6.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)", + "bitflags 1.2.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.62 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1025,7 +1024,7 @@ name = "log" version = "0.4.8" 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)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1086,7 +1085,7 @@ name = "miniz_oxide" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1176,10 +1175,10 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.10.24 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.25 (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.49 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.16 (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)", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1190,7 +1189,7 @@ name = "net2" 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)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1200,9 +1199,9 @@ name = "nix" 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)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1234,7 +1233,7 @@ name = "notify" version = "4.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "filetime 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "fsevent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "fsevent-sys 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1330,15 +1329,15 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.24" +version = "0.10.25" 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)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (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.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1348,7 +1347,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.49" +version = "0.9.50" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1389,7 +1388,7 @@ name = "parking_lot_core" version = "0.6.2" 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)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1452,7 +1451,7 @@ name = "png" 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)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", "inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1478,7 +1477,7 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1507,7 +1506,7 @@ name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1530,7 +1529,7 @@ dependencies = [ [[package]] name = "rand" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1643,13 +1642,21 @@ dependencies = [ "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "raw-window-handle" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rayon" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1771,16 +1778,16 @@ name = "rusttype" version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rusttype 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rusttype 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rusttype" -version = "0.8.0" +version = "0.8.1" 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.5.0 (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.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1800,7 +1807,7 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1851,27 +1858,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "serde_derive" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.40" +version = "1.0.41" 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.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1881,7 +1891,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.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1935,7 +1945,7 @@ name = "signal-hook-registry" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "arc-swap 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "arc-swap 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1954,29 +1964,13 @@ name = "smallvec" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "smithay-client-toolkit" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "andrew 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-protocols 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "smithay-client-toolkit" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "andrew 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1999,7 +1993,7 @@ name = "socket2" version = "0.3.11" 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)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (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.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2043,7 +2037,7 @@ name = "syn" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2064,9 +2058,9 @@ name = "tempfile" 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)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (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)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2253,43 +2247,22 @@ name = "wasi" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "wayland-client" -version = "0.21.13" -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.62 (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)", - "wayland-sys 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "wayland-client" version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "calloop 0.4.4 (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.62 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.19 (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.6 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-scanner 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-sys 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "wayland-commons" -version = "0.21.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-sys 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "wayland-commons" version = "0.23.6" @@ -2299,39 +2272,17 @@ dependencies = [ "wayland-sys 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "wayland-protocols" -version = "0.21.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-client 0.21.13 (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)", - "wayland-sys 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "wayland-protocols" version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-client 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-commons 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)", "wayland-scanner 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "wayland-scanner" -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)", - "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "wayland-scanner" version = "0.23.6" @@ -2342,15 +2293,6 @@ dependencies = [ "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "wayland-sys" -version = "0.21.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "wayland-sys" version = "0.23.6" @@ -2421,24 +2363,26 @@ dependencies = [ [[package]] name = "winit" -version = "0.19.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "0.20.0-alpha3" +source = "git+https://github.com/rust-windowing/winit#4f6ca8792cac0b006205c117fdf9c205a8240657" dependencies = [ "android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "calloop 0.4.4 (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.3 (registry+https://github.com/rust-lang/crates.io-index)", + "core-video-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "dispatch 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (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.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "smithay-client-toolkit 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-window-handle 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smithay-client-toolkit 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", + "wayland-client 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "x11-dl 2.18.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2447,8 +2391,8 @@ dependencies = [ name = "winpty" version = "0.1.0" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "embed-resource 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "embed-resource 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "http_req 0.5.3 (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)", @@ -2469,7 +2413,7 @@ dependencies = [ [[package]] name = "winreg" -version = "0.5.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2537,39 +2481,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", "podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [metadata] -"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "81ce3d38065e618af2d7b77e10c5ad9a069859b4be3c2250f674af3840d9c8a5" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum andrew 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9b7f09f89872c2b6b29e319377b1fbe91c6f5947df19a25596e121cf19a7b35e" "checksum android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" -"checksum arc-swap 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "854ede29f7a0ce90519fb2439d030320c6201119b87dab0ee96044603e1130b9" +"checksum arc-swap 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f1a1eca3195b729bbd64e292ef2f5fff6b1c28504fed762ce2b1013dde4d8e92" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum arrayvec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ead801bcb8843bc91ea0a028f95b786f39ce519b1738de4e74a2a393332c2a16" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" -"checksum backtrace 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "5180c5a20655b14a819b652fd2378fa5f1697b6c9ddad3e695c2f9cedf6df4e2" +"checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5" "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" +"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2" "checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" "checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "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 calloop 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7aa2097be53a00de9e8fc349fea6d76221f398f5c4fa550d420669906962d160" "checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" "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" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +"checksum cgl 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" "checksum clang-sys 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)" = "939a1a34310b120d26eba35c29475933128b0ec58e24b43327f8dbe6036fc538" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum clipboard-win 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3a093d6fed558e5fe24c3dfc85a68bb68f1c824f440d3ba5aca189e2998786b" @@ -2582,22 +2528,22 @@ dependencies = [ "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-video-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8dc065219542086f72d1e9f7aadbbab0989e980263695d129d502082d063a9d0" "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-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" "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.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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "942ca430eef7a3806595a6737bc388bf51adb888d3fc0dd1b50f1c170167ee3a" "checksum dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +"checksum dispatch 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e93ca78226c51902d7aa8c12c988338aadd9e85ed9c6be8aaac39192ff3605" "checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" "checksum downcast-rs 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b92dfd5c2f75260cbf750572f95d387e7ca0ba5e3fbe9e1a33f23025be020f" "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" "checksum dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0ad6bf6a88548d1126045c413548df1453d9be094a8ab9fd59bf1fdd338da4f" "checksum dwrote 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0bd1369e02db5e9b842a9b67bce8a2fcc043beafb2ae8a799dd482d46ea1ff0d" -"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" -"checksum embed-resource 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e1419cfb011b3f11cbe865738cc2a36cc574437de4e3f2a1a57f118b230aa4f3" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +"checksum embed-resource 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bbaba4684ab0af1cbb3ef0b1f540ddc4b57b31940c920ea594efe09ab86e2a6c" "checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" "checksum euclid 0.20.1 (registry+https://github.com/rust-lang/crates.io-index)" = "89c879a4e57d6a2785d517b0771ea6857916173debef0102bf81142d36ca9254" @@ -2605,7 +2551,7 @@ dependencies = [ "checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" "checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" "checksum filetime 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6bd7380b54ced79dda72ecc35cc4fbbd1da6bba54afaa37e96fd1c2a308cd469" -"checksum flate2 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "2adaffba6388640136149e18ed080b77a78611c1e1d6de75aedcdf78df5d4682" +"checksum flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ad3c5233c9a940c8719031b423d7e6c16af66e031cb0420b0896f5245bf181d3" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" "checksum foreign-types 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3684708dacd3b83f4bbe6506d4ccb08bed3c16f521d34366f131b9ecd1884431" @@ -2620,19 +2566,17 @@ dependencies = [ "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" -"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 gif 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "471d90201b3b223f3451cd4ad53e34295f16a1df17b1edf3736d47761c3981af" "checksum gl_generator 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ca98bbde17256e02d17336a6bdb5a50f7d0ccacee502e191d3e3d0ec2f96f84a" -"checksum gleam 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "cae10d7c99d0e77b4766e850a60898a17c1abaf01075531f1066f03dc7dc5fc5" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum glutin 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)" = "938ce7a2b1bbfe1535166133bea3f9c1b46350d2794b49561303575e9e1b9949" -"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" -"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 glutin 0.22.0-alpha3 (git+https://github.com/chrisduerr/glutin)" = "" +"checksum glutin_egl_sys 0.1.3 (git+https://github.com/chrisduerr/glutin)" = "" +"checksum glutin_emscripten_sys 0.1.0 (git+https://github.com/chrisduerr/glutin)" = "" +"checksum glutin_gles2_sys 0.1.3 (git+https://github.com/chrisduerr/glutin)" = "" +"checksum glutin_glx_sys 0.1.5 (git+https://github.com/chrisduerr/glutin)" = "" +"checksum glutin_wgl_sys 0.1.3 (git+https://github.com/chrisduerr/glutin)" = "" "checksum http_req 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3235907ba93aeeb84419957956ab7055f1cc4aacfabd4cd1f32f49addab3ec" -"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" +"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" "checksum image 0.21.3 (registry+https://github.com/rust-lang/crates.io-index)" = "35371e467cd7b0b3d1d6013d619203658467df12d61b0ca43cd67b743b1965eb" "checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" @@ -2685,9 +2629,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.24 (registry+https://github.com/rust-lang/crates.io-index)" = "8152bb5a9b5b721538462336e3bef9a539f892715e5037fda0f984577311af15" +"checksum openssl 0.10.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2f372b2b53ce10fb823a337aaa674e3a7d072b957c6264d0f4ff0bd86e657449" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)" = "f4fad9e54bd23bd4cbbe48fdc08a1b8091707ac869ef8508edea2fec77dcc884" +"checksum openssl-sys 0.9.50 (registry+https://github.com/rust-lang/crates.io-index)" = "2c42dcccb832556b5926bc9ae61e8775f2a61e725ab07ab3d1e7fcf8ae62c3b6" "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 parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" @@ -2703,13 +2647,13 @@ dependencies = [ "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 proc-macro2 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e98a83a9f9b331f54b924e68a66acb1bb35cb01fb0a23645139967abefb697e8" +"checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc" "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 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "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 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" "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_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" @@ -2722,6 +2666,7 @@ dependencies = [ "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" "checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum raw-window-handle 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a292ac09586acd898a72f802519c2997b1eb2caec9bc4c5a06d7259d986a296" "checksum rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "83a27732a533a1be0a0035a111fe76db89ad312f6f0347004c220c57f209a123" "checksum rayon-core 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "98dcf634205083b17d0861252431eb2acbfb698ab7478a2d20de07954f47ec7b" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" @@ -2738,19 +2683,19 @@ dependencies = [ "checksum rustc_tools_util 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b725dadae9fabc488df69a287f5a99c5eaf5d10853842a8a3dfac52476f544ee" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rusttype 0.7.9 (registry+https://github.com/rust-lang/crates.io-index)" = "310942406a39981bed7e12b09182a221a29e0990f3e7e0c971f131922ed135d5" -"checksum rusttype 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "07c67d4df90dcc0296c39e293fa9cfbbd29def131a5a3ffb9bc671f8dc906942" +"checksum rusttype 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6fa38506b5cbf2fb67f915e2725cb5012f1b9a785b0ab55c4733acda5f6554ef" "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 schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" +"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" "checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" "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.100 (registry+https://github.com/rust-lang/crates.io-index)" = "f4473e8506b213730ff2061073b48fa51dcc66349219e2e7c5608f0296a1d95a" -"checksum serde_derive 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "11e410fde43e157d789fc290d26bc940778ad0fdd47836426fbac36573710dbb" -"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" +"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" +"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" +"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" "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" @@ -2760,7 +2705,6 @@ dependencies = [ "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" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" -"checksum smithay-client-toolkit 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2ccb8c57049b2a34d2cc2b203fa785020ba0129d31920ef0d317430adaf748fa" "checksum smithay-client-toolkit 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "93960e8975909fcb14cc755de93af2149d8b8f4eb368315537d40cfd0f324054" "checksum smithay-clipboard 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "315c3f4417d365483dcbaed9ba6488d9f176e43d83f3e1d581050f1c93869e3d" "checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" @@ -2797,15 +2741,10 @@ dependencies = [ "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 wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" -"checksum wayland-client 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "49963e5f9eeaf637bfcd1b9f0701c99fd5cd05225eb51035550d4272806f2713" "checksum wayland-client 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "af1080ebe0efabcf12aef2132152f616038f2d7dcbbccf7b2d8c5270fe14bcda" -"checksum wayland-commons 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "40c08896768b667e1df195d88a62a53a2d1351a1ed96188be79c196b35bb32ec" "checksum wayland-commons 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb" -"checksum wayland-protocols 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "4afde2ea2a428eee6d7d2c8584fdbe8b82eee8b6c353e129a434cd6e07f42145" "checksum wayland-protocols 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9" -"checksum wayland-scanner 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3828c568714507315ee425a9529edc4a4aa9901409e373e9e0027e7622b79e" "checksum wayland-scanner 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93b02247366f395b9258054f964fe293ddd019c3237afba9be2ccbe9e1651c3d" -"checksum wayland-sys 0.21.13 (registry+https://github.com/rust-lang/crates.io-index)" = "520ab0fd578017a0ee2206623ba9ef4afe5e8f23ca7b42f6acfba2f4e66b1628" "checksum wayland-sys 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d94e89a86e6d6d7c7c9b19ebf48a03afaac4af6bc22ae570e9a24124b75358f4" "checksum which 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e84a603e7e0b1ce1aa1ee2b109c7be00155ce52df5081590d1ffb93f4f515cb2" "checksum widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6" @@ -2816,9 +2755,9 @@ dependencies = [ "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" "checksum wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9" -"checksum winit 0.19.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7d0da905e61ae52d55c5ca6f8bea1e09daf5e325b6c77b0947c65a5179b49f5f" +"checksum winit 0.20.0-alpha3 (git+https://github.com/rust-windowing/winit)" = "" "checksum winpty-sys 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dade7ecea144b3578a02925f93900f32370abfb8768630883971f4ef716b568" -"checksum winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" +"checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum x11-clipboard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "89bd49c06c9eb5d98e6ba6536cf64ac9f7ee3a009b2f53996d405b3944f6bcea" "checksum x11-dl 2.18.4 (registry+https://github.com/rust-lang/crates.io-index)" = "be65e1342a3baae65439cd03306778831a3d133b0d20243a7fb83fd5cf403c58" diff --git a/alacritty/Cargo.toml b/alacritty/Cargo.toml index c93f7c5..c47aae6 100644 --- a/alacritty/Cargo.toml +++ b/alacritty/Cargo.toml @@ -14,9 +14,15 @@ clap = "2" log = "0.4" time = "0.1.40" env_logger = "0.6.0" -crossbeam-channel = "0.3.8" +serde = { version = "1", features = ["derive"] } serde_yaml = "0.8" serde_json = "1" +glutin = { git = "https://github.com/chrisduerr/glutin" } +notify = "4" +libc = "0.2" +unicode-width = "0.1" +parking_lot = "0.9" +font = { path = "../font" } [build-dependencies] rustc_tools_util = "0.2.0" @@ -24,9 +30,15 @@ rustc_tools_util = "0.2.0" [target.'cfg(not(windows))'.dependencies] xdg = "2" +[target.'cfg(not(target_os = "macos"))'.dependencies] +image = "0.21.0" + [target.'cfg(any(target_os = "macos", windows))'.dependencies] dirs = "1.0.2" +[target.'cfg(not(any(target_os="windows", target_os="macos")))'.dependencies] +x11-dl = "2" + [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.7", features = ["impl-default", "winuser", "synchapi", "roerrorapi", "winerror", "wincon", "wincontypes"]} diff --git a/alacritty/src/cli.rs b/alacritty/src/cli.rs index d48ab4d..d5eb12d 100644 --- a/alacritty/src/cli.rs +++ b/alacritty/src/cli.rs @@ -19,9 +19,10 @@ use std::path::{Path, PathBuf}; use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg}; use log::{self, LevelFilter}; -use alacritty_terminal::config::{Config, Delta, Dimensions, Shell}; +use alacritty_terminal::config::{Delta, Dimensions, Shell, DEFAULT_NAME}; use alacritty_terminal::index::{Column, Line}; -use alacritty_terminal::window::DEFAULT_NAME; + +use crate::config::Config; /// Options specified on the command line pub struct Options { @@ -283,9 +284,10 @@ impl Options { #[cfg(test)] mod test { - use alacritty_terminal::config::{Config, DEFAULT_ALACRITTY_CONFIG}; + use alacritty_terminal::config::DEFAULT_ALACRITTY_CONFIG; use crate::cli::Options; + use crate::config::Config; #[test] fn dynamic_title_ignoring_options_by_default() { diff --git a/alacritty_terminal/src/config/bindings.rs b/alacritty/src/config/bindings.rs similarity index 74% rename from alacritty_terminal/src/config/bindings.rs rename to alacritty/src/config/bindings.rs index 00090bb..17a6d0b 100644 --- a/alacritty_terminal/src/config/bindings.rs +++ b/alacritty/src/config/bindings.rs @@ -15,13 +15,207 @@ use std::fmt; use std::str::FromStr; -use glutin::{ModifiersState, MouseButton}; +use glutin::event::{ModifiersState, MouseButton}; +use log::error; use serde::de::Error as SerdeError; use serde::de::{self, MapAccess, Unexpected, Visitor}; use serde::{Deserialize, Deserializer}; -use crate::input::{Action, Binding, KeyBinding, MouseBinding}; -use crate::term::TermMode; +use alacritty_terminal::config::LOG_TARGET_CONFIG; +use alacritty_terminal::term::TermMode; + +/// Describes a state and action to take in that state +/// +/// This is the shared component of `MouseBinding` and `KeyBinding` +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Binding { + /// Modifier keys required to activate binding + pub mods: ModifiersState, + + /// String to send to pty if mods and mode match + pub action: Action, + + /// Terminal mode required to activate binding + pub mode: TermMode, + + /// excluded terminal modes where the binding won't be activated + pub notmode: TermMode, + + /// This property is used as part of the trigger detection code. + /// + /// For example, this might be a key like "G", or a mouse button. + pub trigger: T, +} + +/// Bindings that are triggered by a keyboard key +pub type KeyBinding = Binding; + +/// Bindings that are triggered by a mouse button +pub type MouseBinding = Binding; + +impl Default for KeyBinding { + fn default() -> KeyBinding { + KeyBinding { + mods: Default::default(), + action: Action::Esc(String::new()), + mode: TermMode::NONE, + notmode: TermMode::NONE, + trigger: Key::A, + } + } +} + +impl Default for MouseBinding { + fn default() -> MouseBinding { + MouseBinding { + mods: Default::default(), + action: Action::Esc(String::new()), + mode: TermMode::NONE, + notmode: TermMode::NONE, + trigger: MouseButton::Left, + } + } +} + +impl Binding { + #[inline] + pub fn is_triggered_by( + &self, + mode: TermMode, + mods: ModifiersState, + input: &T, + relaxed: bool, + ) -> bool { + // Check input first since bindings are stored in one big list. This is + // the most likely item to fail so prioritizing it here allows more + // checks to be short circuited. + self.trigger == *input + && mode.contains(self.mode) + && !mode.intersects(self.notmode) + && (self.mods == mods || (relaxed && self.mods.relaxed_eq(mods))) + } + + #[inline] + pub fn triggers_match(&self, binding: &Binding) -> bool { + // Check the binding's key and modifiers + if self.trigger != binding.trigger || self.mods != binding.mods { + return false; + } + + // Completely empty modes match all modes + if (self.mode.is_empty() && self.notmode.is_empty()) + || (binding.mode.is_empty() && binding.notmode.is_empty()) + { + return true; + } + + // Check for intersection (equality is required since empty does not intersect itself) + (self.mode == binding.mode || self.mode.intersects(binding.mode)) + && (self.notmode == binding.notmode || self.notmode.intersects(binding.notmode)) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +pub enum Action { + /// Write an escape sequence. + #[serde(skip)] + Esc(String), + + /// Paste contents of system clipboard. + Paste, + + /// Store current selection into clipboard. + Copy, + + /// Paste contents of selection buffer. + PasteSelection, + + /// Increase font size. + IncreaseFontSize, + + /// Decrease font size. + DecreaseFontSize, + + /// Reset font size to the config value. + ResetFontSize, + + /// Scroll exactly one page up. + ScrollPageUp, + + /// Scroll exactly one page down. + ScrollPageDown, + + /// Scroll one line up. + ScrollLineUp, + + /// Scroll one line down. + ScrollLineDown, + + /// Scroll all the way to the top. + ScrollToTop, + + /// Scroll all the way to the bottom. + ScrollToBottom, + + /// Clear the display buffer(s) to remove history. + ClearHistory, + + /// Run given command. + #[serde(skip)] + Command(String, Vec), + + /// Hide the Alacritty window. + Hide, + + /// Quit Alacritty. + Quit, + + /// Clear warning and error notices. + ClearLogNotice, + + /// Spawn a new instance of Alacritty. + SpawnNewInstance, + + /// Toggle fullscreen. + ToggleFullscreen, + + /// Toggle simple fullscreen on macos. + #[cfg(target_os = "macos")] + ToggleSimpleFullscreen, + + /// Allow receiving char input. + ReceiveChar, + + /// No action. + None, +} + +impl Default for Action { + fn default() -> Action { + Action::None + } +} + +impl From<&'static str> for Action { + fn from(s: &'static str) -> Action { + Action::Esc(s.into()) + } +} + +pub trait RelaxedEq { + fn relaxed_eq(&self, other: T) -> bool; +} + +impl RelaxedEq for ModifiersState { + // Make sure that modifiers in the config are always present, + // but ignore surplus modifiers. + fn relaxed_eq(&self, other: Self) -> bool { + (!self.logo || other.logo) + && (!self.alt || other.alt) + && (!self.ctrl || other.ctrl) + && (!self.shift || other.shift) + } +} macro_rules! bindings { ( @@ -444,8 +638,8 @@ pub enum Key { } impl Key { - pub fn from_glutin_input(key: ::glutin::VirtualKeyCode) -> Self { - use glutin::VirtualKeyCode::*; + pub fn from_glutin_input(key: glutin::event::VirtualKeyCode) -> Self { + use glutin::event::VirtualKeyCode::*; // Thank you, vim macros and regex! match key { Key1 => Key::Key1, @@ -646,7 +840,7 @@ impl<'a> Deserialize<'a> for ModeWrapper { "~appkeypad" => res.not_mode |= TermMode::APP_KEYPAD, "~alt" => res.not_mode |= TermMode::ALT_SCREEN, "alt" => res.mode |= TermMode::ALT_SCREEN, - _ => error!("Unknown mode {:?}", modifier), + _ => error!(target: LOG_TARGET_CONFIG, "Unknown mode {:?}", modifier), } } @@ -812,7 +1006,7 @@ impl<'a> Deserialize<'a> for RawBinding { let mut mods: Option = None; let mut key: Option = None; let mut chars: Option = None; - let mut action: Option = None; + let mut action: Option = None; let mut mode: Option = None; let mut not_mode: Option = None; let mut mouse: Option = None; @@ -1010,7 +1204,7 @@ impl<'a> de::Deserialize<'a> for ModsWrapper { "alt" | "option" => res.alt = true, "control" => res.ctrl = true, "none" => (), - _ => error!("Unknown modifier {:?}", modifier), + _ => error!(target: LOG_TARGET_CONFIG, "Unknown modifier {:?}", modifier), } } @@ -1021,3 +1215,195 @@ impl<'a> de::Deserialize<'a> for ModsWrapper { deserializer.deserialize_str(ModsVisitor) } } + +#[cfg(test)] +mod test { + use glutin::event::ModifiersState; + + use alacritty_terminal::term::TermMode; + + use crate::config::{Action, Binding}; + + type MockBinding = Binding; + + impl Default for MockBinding { + fn default() -> Self { + Self { + mods: Default::default(), + action: Default::default(), + mode: TermMode::empty(), + notmode: TermMode::empty(), + trigger: Default::default(), + } + } + } + + #[test] + fn binding_matches_itself() { + let binding = MockBinding::default(); + let identical_binding = MockBinding::default(); + + assert!(binding.triggers_match(&identical_binding)); + assert!(identical_binding.triggers_match(&binding)); + } + + #[test] + fn binding_matches_different_action() { + let binding = MockBinding::default(); + let mut different_action = MockBinding::default(); + different_action.action = Action::ClearHistory; + + assert!(binding.triggers_match(&different_action)); + assert!(different_action.triggers_match(&binding)); + } + + #[test] + fn mods_binding_requires_strict_match() { + let mut superset_mods = MockBinding::default(); + superset_mods.mods = ModifiersState { alt: true, logo: true, ctrl: true, shift: true }; + let mut subset_mods = MockBinding::default(); + subset_mods.mods = ModifiersState { alt: true, logo: false, ctrl: false, shift: false }; + + assert!(!superset_mods.triggers_match(&subset_mods)); + assert!(!subset_mods.triggers_match(&superset_mods)); + } + + #[test] + fn binding_matches_identical_mode() { + let mut b1 = MockBinding::default(); + b1.mode = TermMode::ALT_SCREEN; + let mut b2 = MockBinding::default(); + b2.mode = TermMode::ALT_SCREEN; + + assert!(b1.triggers_match(&b2)); + } + + #[test] + fn binding_without_mode_matches_any_mode() { + let b1 = MockBinding::default(); + let mut b2 = MockBinding::default(); + b2.mode = TermMode::APP_KEYPAD; + b2.notmode = TermMode::ALT_SCREEN; + + assert!(b1.triggers_match(&b2)); + } + + #[test] + fn binding_with_mode_matches_empty_mode() { + let mut b1 = MockBinding::default(); + b1.mode = TermMode::APP_KEYPAD; + b1.notmode = TermMode::ALT_SCREEN; + let b2 = MockBinding::default(); + + assert!(b1.triggers_match(&b2)); + } + + #[test] + fn binding_matches_superset_mode() { + let mut b1 = MockBinding::default(); + b1.mode = TermMode::APP_KEYPAD; + let mut b2 = MockBinding::default(); + b2.mode = TermMode::ALT_SCREEN | TermMode::APP_KEYPAD; + + assert!(b1.triggers_match(&b2)); + } + + #[test] + fn binding_matches_subset_mode() { + let mut b1 = MockBinding::default(); + b1.mode = TermMode::ALT_SCREEN | TermMode::APP_KEYPAD; + let mut b2 = MockBinding::default(); + b2.mode = TermMode::APP_KEYPAD; + + assert!(b1.triggers_match(&b2)); + } + + #[test] + fn binding_matches_partial_intersection() { + let mut b1 = MockBinding::default(); + b1.mode = TermMode::ALT_SCREEN | TermMode::APP_KEYPAD; + let mut b2 = MockBinding::default(); + b2.mode = TermMode::APP_KEYPAD | TermMode::APP_CURSOR; + + assert!(b1.triggers_match(&b2)); + } + + #[test] + fn binding_mismatches_notmode() { + let mut b1 = MockBinding::default(); + b1.mode = TermMode::ALT_SCREEN; + let mut b2 = MockBinding::default(); + b2.notmode = TermMode::ALT_SCREEN; + + assert!(!b1.triggers_match(&b2)); + } + + #[test] + fn binding_mismatches_unrelated() { + let mut b1 = MockBinding::default(); + b1.mode = TermMode::ALT_SCREEN; + let mut b2 = MockBinding::default(); + b2.mode = TermMode::APP_KEYPAD; + + assert!(!b1.triggers_match(&b2)); + } + + #[test] + fn binding_trigger_input() { + let mut binding = MockBinding::default(); + binding.trigger = 13; + + let mods = binding.mods; + let mode = binding.mode; + + assert!(binding.is_triggered_by(mode, mods, &13, true)); + assert!(!binding.is_triggered_by(mode, mods, &32, true)); + } + + #[test] + fn binding_trigger_mods() { + let mut binding = MockBinding::default(); + binding.mods = ModifiersState { alt: true, logo: true, ctrl: false, shift: false }; + + let superset_mods = ModifiersState { alt: true, logo: true, ctrl: true, shift: true }; + let subset_mods = ModifiersState { alt: false, logo: false, ctrl: false, shift: false }; + + let t = binding.trigger; + let mode = binding.mode; + + assert!(binding.is_triggered_by(mode, binding.mods, &t, true)); + assert!(binding.is_triggered_by(mode, binding.mods, &t, false)); + + assert!(binding.is_triggered_by(mode, superset_mods, &t, true)); + assert!(!binding.is_triggered_by(mode, superset_mods, &t, false)); + + assert!(!binding.is_triggered_by(mode, subset_mods, &t, true)); + assert!(!binding.is_triggered_by(mode, subset_mods, &t, false)); + } + + #[test] + fn binding_trigger_modes() { + let mut binding = MockBinding::default(); + binding.mode = TermMode::ALT_SCREEN; + + let t = binding.trigger; + let mods = binding.mods; + + assert!(!binding.is_triggered_by(TermMode::INSERT, mods, &t, true)); + assert!(binding.is_triggered_by(TermMode::ALT_SCREEN, mods, &t, true)); + assert!(binding.is_triggered_by(TermMode::ALT_SCREEN | TermMode::INSERT, mods, &t, true)); + } + + #[test] + fn binding_trigger_notmodes() { + let mut binding = MockBinding::default(); + binding.notmode = TermMode::ALT_SCREEN; + + let t = binding.trigger; + let mods = binding.mods; + + assert!(binding.is_triggered_by(TermMode::INSERT, mods, &t, true)); + assert!(!binding.is_triggered_by(TermMode::ALT_SCREEN, mods, &t, true)); + assert!(!binding.is_triggered_by(TermMode::ALT_SCREEN | TermMode::INSERT, mods, &t, true)); + } +} diff --git a/alacritty/src/config.rs b/alacritty/src/config/mod.rs similarity index 89% rename from alacritty/src/config.rs rename to alacritty/src/config/mod.rs index 6d185fe..fe0ee7a 100644 --- a/alacritty/src/config.rs +++ b/alacritty/src/config/mod.rs @@ -11,9 +11,23 @@ use serde_yaml; #[cfg(not(windows))] use xdg; -use alacritty_terminal::config::{Config, DEFAULT_ALACRITTY_CONFIG}; +use alacritty_terminal::config::{ + Config as TermConfig, DEFAULT_ALACRITTY_CONFIG, LOG_TARGET_CONFIG, +}; -pub const SOURCE_FILE_PATH: &str = file!(); +mod bindings; +pub mod monitor; +mod mouse; +#[cfg(test)] +mod test; +mod ui_config; + +pub use crate::config::bindings::{Action, Binding, Key, RelaxedEq}; +#[cfg(test)] +pub use crate::config::mouse::{ClickHandler, Mouse}; +use crate::config::ui_config::UIConfig; + +pub type Config = TermConfig; /// Result from config loading pub type Result = ::std::result::Result; @@ -169,7 +183,7 @@ pub fn reload_from(path: &PathBuf) -> Result { match read_config(path) { Ok(config) => Ok(config), Err(err) => { - error!("Unable to load config {:?}: {}", path, err); + error!(target: LOG_TARGET_CONFIG, "Unable to load config {:?}: {}", path, err); Err(err) }, } @@ -199,16 +213,21 @@ fn read_config(path: &PathBuf) -> Result { fn print_deprecation_warnings(config: &Config) { if config.window.start_maximized.is_some() { warn!( + target: LOG_TARGET_CONFIG, "Config window.start_maximized is deprecated; please use window.startup_mode instead" ); } if config.render_timer.is_some() { - warn!("Config render_timer is deprecated; please use debug.render_timer instead"); + warn!( + target: LOG_TARGET_CONFIG, + "Config render_timer is deprecated; please use debug.render_timer instead" + ); } if config.persistent_logging.is_some() { warn!( + target: LOG_TARGET_CONFIG, "Config persistent_logging is deprecated; please use debug.persistent_logging instead" ); } diff --git a/alacritty_terminal/src/config/monitor.rs b/alacritty/src/config/monitor.rs similarity index 65% rename from alacritty_terminal/src/config/monitor.rs rename to alacritty/src/config/monitor.rs index 6d2ab41..8dc5379 100644 --- a/alacritty_terminal/src/config/monitor.rs +++ b/alacritty/src/config/monitor.rs @@ -4,43 +4,24 @@ use std::time::Duration; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; +use alacritty_terminal::event::{Event, EventListener}; +use alacritty_terminal::util; + +use crate::event::EventProxy; + pub struct Monitor { _thread: ::std::thread::JoinHandle<()>, - 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 + pub fn new

(path: P, event_proxy: EventProxy) -> 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 || { + _thread: util::thread::spawn_named("config watcher", move || { let (tx, rx) = mpsc::channel(); // The Duration argument is a debouncing period. let mut watcher = @@ -66,14 +47,12 @@ impl Monitor { continue; } - let _ = config_tx.send(path); - handler.on_config_reload(); + event_proxy.send_event(Event::ConfigReload(path)); }, _ => {}, } } }), - rx: config_rx, } } } diff --git a/alacritty_terminal/src/config/mouse.rs b/alacritty/src/config/mouse.rs similarity index 86% rename from alacritty_terminal/src/config/mouse.rs rename to alacritty/src/config/mouse.rs index 7a04cbe..b7832b4 100644 --- a/alacritty_terminal/src/config/mouse.rs +++ b/alacritty/src/config/mouse.rs @@ -1,10 +1,12 @@ use std::time::Duration; -use glutin::ModifiersState; +use glutin::event::ModifiersState; +use log::error; use serde::{Deserialize, Deserializer}; +use alacritty_terminal::config::{failure_default, LOG_TARGET_CONFIG}; + use crate::config::bindings::{CommandWrapper, ModsWrapper}; -use crate::config::failure_default; #[serde(default)] #[derive(Default, Clone, Debug, Deserialize, PartialEq, Eq)] @@ -56,7 +58,12 @@ where match >::deserialize(val) { Ok(launcher) => Ok(launcher), Err(err) => { - error!("Problem with config: {}; using {}", err, default.clone().unwrap().program()); + error!( + target: LOG_TARGET_CONFIG, + "Problem with config: {}; using {}", + err, + default.clone().unwrap().program() + ); Ok(default) }, } @@ -101,7 +108,7 @@ where match u64::deserialize(value) { Ok(threshold_ms) => Ok(Duration::from_millis(threshold_ms)), Err(err) => { - error!("Problem with config: {}; using default value", err); + error!(target: LOG_TARGET_CONFIG, "Problem with config: {}; using default value", err); Ok(default_threshold_ms()) }, } diff --git a/alacritty_terminal/src/config/test.rs b/alacritty/src/config/test.rs similarity index 68% rename from alacritty_terminal/src/config/test.rs rename to alacritty/src/config/test.rs index e789092..8da6cef 100644 --- a/alacritty_terminal/src/config/test.rs +++ b/alacritty/src/config/test.rs @@ -1,4 +1,6 @@ -use crate::config::{Config, DEFAULT_ALACRITTY_CONFIG}; +use alacritty_terminal::config::DEFAULT_ALACRITTY_CONFIG; + +use crate::config::Config; #[test] fn parse_config() { @@ -6,10 +8,10 @@ fn parse_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()); + assert!(!config.ui_config.mouse_bindings.is_empty()); // Sanity check that key bindings are being parsed - assert!(!config.key_bindings.is_empty()); + assert!(!config.ui_config.key_bindings.is_empty()); } #[test] diff --git a/alacritty/src/config/ui_config.rs b/alacritty/src/config/ui_config.rs new file mode 100644 index 0000000..6230c5b --- /dev/null +++ b/alacritty/src/config/ui_config.rs @@ -0,0 +1,63 @@ +use serde::{Deserialize, Deserializer}; + +use alacritty_terminal::config::failure_default; + +use crate::config::bindings::{self, Binding, KeyBinding, MouseBinding}; +use crate::config::mouse::Mouse; + +#[derive(Debug, PartialEq, Deserialize)] +pub struct UIConfig { + #[serde(default, deserialize_with = "failure_default")] + pub mouse: Mouse, + + /// Keybindings + #[serde(default = "default_key_bindings", deserialize_with = "deserialize_key_bindings")] + pub key_bindings: Vec, + + /// Bindings for the mouse + #[serde(default = "default_mouse_bindings", deserialize_with = "deserialize_mouse_bindings")] + pub mouse_bindings: Vec, +} + +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) -> Result, D::Error> +where + D: Deserializer<'a>, +{ + deserialize_bindings(deserializer, bindings::default_key_bindings()) +} + +fn deserialize_mouse_bindings<'a, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'a>, +{ + deserialize_bindings(deserializer, bindings::default_mouse_bindings()) +} + +fn deserialize_bindings<'a, D, T>( + deserializer: D, + mut default: Vec>, +) -> Result>, D::Error> +where + D: Deserializer<'a>, + T: Copy + Eq, + Binding: Deserialize<'a>, +{ + let mut bindings: Vec> = failure_default(deserializer)?; + + // Remove matching default bindings + for binding in bindings.iter() { + default.retain(|b| !b.triggers_match(binding)); + } + + bindings.extend(default); + + Ok(bindings) +} diff --git a/alacritty/src/display.rs b/alacritty/src/display.rs new file mode 100644 index 0000000..a8f72b3 --- /dev/null +++ b/alacritty/src/display.rs @@ -0,0 +1,476 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! The display subsystem including window management, font rasterization, and +//! GPU drawing. +use std::cmp::max; +use std::f64; +use std::fmt; +use std::time::Instant; + +use glutin::dpi::{PhysicalPosition, PhysicalSize}; +use glutin::event_loop::EventLoop; +use log::{debug, info}; +use parking_lot::MutexGuard; + +use font::{self, Rasterize, Size}; + +use alacritty_terminal::config::StartupMode; +use alacritty_terminal::event::{Event, OnResize}; +use alacritty_terminal::index::Line; +use alacritty_terminal::message_bar::MessageBuffer; +use alacritty_terminal::meter::Meter; +use alacritty_terminal::renderer::rects::{RenderLines, RenderRect}; +use alacritty_terminal::renderer::{self, GlyphCache, QuadRenderer}; +use alacritty_terminal::term::color::Rgb; +use alacritty_terminal::term::{RenderableCell, SizeInfo, Term}; + +use crate::config::Config; +use crate::event::{FontResize, Resize}; +use crate::window::{self, Window}; + +/// Font size change interval +pub const FONT_SIZE_STEP: f32 = 0.5; + +#[derive(Debug)] +pub enum Error { + /// Error with window management + Window(window::Error), + + /// Error dealing with fonts + Font(font::Error), + + /// Error in renderer + Render(renderer::Error), + + /// Error during buffer swap + ContextError(glutin::ContextError), +} + +impl std::error::Error for Error { + fn cause(&self) -> Option<&dyn (std::error::Error)> { + match *self { + Error::Window(ref err) => Some(err), + Error::Font(ref err) => Some(err), + Error::Render(ref err) => Some(err), + Error::ContextError(ref err) => Some(err), + } + } + + fn description(&self) -> &str { + match *self { + Error::Window(ref err) => err.description(), + Error::Font(ref err) => err.description(), + Error::Render(ref err) => err.description(), + Error::ContextError(ref err) => err.description(), + } + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + Error::Window(ref err) => err.fmt(f), + Error::Font(ref err) => err.fmt(f), + Error::Render(ref err) => err.fmt(f), + Error::ContextError(ref err) => err.fmt(f), + } + } +} + +impl From for Error { + fn from(val: window::Error) -> Error { + Error::Window(val) + } +} + +impl From for Error { + fn from(val: font::Error) -> Error { + Error::Font(val) + } +} + +impl From for Error { + fn from(val: renderer::Error) -> Error { + Error::Render(val) + } +} + +impl From for Error { + fn from(val: glutin::ContextError) -> Error { + Error::ContextError(val) + } +} + +/// The display wraps a window, font rasterizer, and GPU renderer +pub struct Display { + pub size_info: SizeInfo, + pub font_size: Size, + pub window: Window, + + renderer: QuadRenderer, + glyph_cache: GlyphCache, + meter: Meter, +} + +impl Display { + pub fn new(config: &Config, event_loop: &EventLoop) -> Result { + // Guess DPR based on first monitor + let estimated_dpr = + event_loop.available_monitors().next().map(|m| m.hidpi_factor()).unwrap_or(1.); + + // Guess the target window dimensions + let metrics = GlyphCache::static_metrics(config.font.clone(), estimated_dpr)?; + let (cell_width, cell_height) = compute_cell_size(config, &metrics); + let dimensions = + GlyphCache::calculate_dimensions(config, estimated_dpr, cell_width, cell_height); + + debug!("Estimated DPR: {}", estimated_dpr); + debug!("Estimated Cell Size: {} x {}", cell_width, cell_height); + debug!("Estimated Dimensions: {:?}", dimensions); + + // Create the window where Alacritty will be displayed + let logical = dimensions.map(|d| PhysicalSize::new(d.0, d.1).to_logical(estimated_dpr)); + + // Spawn window + let mut window = Window::new(event_loop, &config, logical)?; + + let dpr = window.hidpi_factor(); + info!("Device pixel ratio: {}", dpr); + + // get window properties for initializing the other subsystems + let mut viewport_size = window.inner_size().to_physical(dpr); + + // Create renderer + let mut renderer = QuadRenderer::new()?; + + let (glyph_cache, cell_width, cell_height) = + Self::new_glyph_cache(dpr, &mut renderer, config)?; + + let mut padding_x = f32::from(config.window.padding.x) * dpr as f32; + let mut padding_y = f32::from(config.window.padding.y) * dpr as f32; + + if let Some((width, height)) = + GlyphCache::calculate_dimensions(config, dpr, cell_width, cell_height) + { + let PhysicalSize { width: w, height: h } = window.inner_size().to_physical(dpr); + if (w - width).abs() < f64::EPSILON && (h - height).abs() < f64::EPSILON { + info!("Estimated DPR correctly, skipping resize"); + } else { + viewport_size = PhysicalSize::new(width, height); + window.set_inner_size(viewport_size.to_logical(dpr)); + } + } else if config.window.dynamic_padding { + // Make sure additional padding is spread evenly + padding_x = dynamic_padding(padding_x, viewport_size.width as f32, cell_width); + padding_y = dynamic_padding(padding_y, viewport_size.height as f32, cell_height); + } + + padding_x = padding_x.floor(); + padding_y = padding_y.floor(); + + info!("Cell Size: {} x {}", cell_width, cell_height); + info!("Padding: {} x {}", padding_x, padding_y); + + let size_info = SizeInfo { + dpr, + width: viewport_size.width as f32, + height: viewport_size.height as f32, + cell_width: cell_width as f32, + cell_height: cell_height as f32, + padding_x: padding_x as f32, + padding_y: padding_y as f32, + }; + + // Update OpenGL projection + renderer.resize(&size_info); + + // Clear screen + let background_color = config.colors.primary.background; + renderer.with_api(&config, &size_info, |api| { + api.clear(background_color); + }); + + // We should call `clear` when window is offscreen, so when `window.show()` happens it + // would be with background color instead of uninitialized surface. + window.swap_buffers(); + + window.set_visible(true); + + // Set window position + // + // TODO: replace `set_position` with `with_position` once available + // Upstream issue: https://github.com/tomaka/winit/issues/806 + if let Some(position) = config.window.position { + let physical = PhysicalPosition::from((position.x, position.y)); + let logical = physical.to_logical(dpr); + window.set_outer_position(logical); + } + + #[allow(clippy::single_match)] + match config.window.startup_mode() { + StartupMode::Fullscreen => window.set_fullscreen(true), + #[cfg(target_os = "macos")] + StartupMode::SimpleFullscreen => window.set_simple_fullscreen(true), + #[cfg(not(any(target_os = "macos", windows)))] + StartupMode::Maximized => window.set_maximized(true), + _ => (), + } + + Ok(Display { + window, + renderer, + glyph_cache, + meter: Meter::new(), + size_info, + font_size: config.font.size, + }) + } + + fn new_glyph_cache( + dpr: f64, + renderer: &mut QuadRenderer, + config: &Config, + ) -> Result<(GlyphCache, f32, f32), Error> { + let font = config.font.clone(); + let rasterizer = font::Rasterizer::new(dpr as f32, config.font.use_thin_strokes())?; + + // Initialize glyph cache + let glyph_cache = { + info!("Initializing glyph cache..."); + let init_start = Instant::now(); + + let cache = + renderer.with_loader(|mut api| GlyphCache::new(rasterizer, &font, &mut api))?; + + let stop = init_start.elapsed(); + let stop_f = stop.as_secs() as f64 + f64::from(stop.subsec_nanos()) / 1_000_000_000f64; + info!("... finished initializing glyph cache in {}s", stop_f); + + cache + }; + + // Need font metrics to resize the window properly. This suggests to me the + // font metrics should be computed before creating the window in the first + // place so that a resize is not needed. + let (cw, ch) = compute_cell_size(config, &glyph_cache.font_metrics()); + + Ok((glyph_cache, cw, ch)) + } + + /// Update font size and cell dimensions + fn update_glyph_cache(&mut self, config: &Config, size: Size) { + let size_info = &mut self.size_info; + let cache = &mut self.glyph_cache; + + let font = config.font.clone().with_size(size); + + self.renderer.with_loader(|mut api| { + let _ = cache.update_font_size(font, size_info.dpr, &mut api); + }); + + // Update cell size + let (cell_width, cell_height) = compute_cell_size(config, &self.glyph_cache.font_metrics()); + size_info.cell_width = cell_width; + size_info.cell_height = cell_height; + } + + /// Process resize events + pub fn handle_resize( + &mut self, + terminal: &mut Term, + pty_resize_handle: &mut dyn OnResize, + message_buffer: &MessageBuffer, + config: &Config, + resize_pending: Resize, + ) { + // Update font size and cell dimensions + if let Some(resize) = resize_pending.font_size { + self.font_size = match resize { + FontResize::Delta(delta) => max(self.font_size + delta, FONT_SIZE_STEP.into()), + FontResize::Reset => config.font.size, + }; + + self.update_glyph_cache(config, self.font_size); + } + + // Update the window dimensions + if let Some(size) = resize_pending.dimensions { + self.size_info.width = size.width as f32; + self.size_info.height = size.height as f32; + } + + let dpr = self.size_info.dpr; + let width = self.size_info.width; + let height = self.size_info.height; + let cell_width = self.size_info.cell_width; + let cell_height = self.size_info.cell_height; + + // Recalculate padding + let mut padding_x = f32::from(config.window.padding.x) * dpr as f32; + let mut padding_y = f32::from(config.window.padding.y) * dpr as f32; + + if config.window.dynamic_padding { + padding_x = dynamic_padding(padding_x, width, cell_width); + padding_y = dynamic_padding(padding_y, height, cell_height); + } + + self.size_info.padding_x = padding_x.floor() as f32; + self.size_info.padding_y = padding_y.floor() as f32; + + let mut pty_size = self.size_info; + + // Subtract message bar lines from pty size + if resize_pending.message_buffer.is_some() { + let lines = + message_buffer.message().map(|m| m.text(&self.size_info).len()).unwrap_or(0); + pty_size.height -= pty_size.cell_height * lines as f32; + } + + // Resize PTY + pty_resize_handle.on_resize(&pty_size); + + // Resize terminal + terminal.resize(&pty_size); + + // Resize renderer + let physical = + PhysicalSize::new(f64::from(self.size_info.width), f64::from(self.size_info.height)); + self.renderer.resize(&self.size_info); + self.window.resize(physical); + } + + /// Draw the screen + /// + /// A reference to Term whose state is being drawn must be provided. + /// + /// This call may block if vsync is enabled + pub fn draw( + &mut self, + terminal: MutexGuard<'_, Term>, + message_buffer: &MessageBuffer, + config: &Config, + ) { + let grid_cells: Vec = terminal.renderable_cells(config).collect(); + let visual_bell_intensity = terminal.visual_bell.intensity(); + let background_color = terminal.background_color(); + let metrics = self.glyph_cache.font_metrics(); + let glyph_cache = &mut self.glyph_cache; + let size_info = self.size_info; + + // Update IME position + #[cfg(not(windows))] + self.window.update_ime_position(&terminal, &self.size_info); + + // Drop terminal as early as possible to free lock + drop(terminal); + + self.renderer.with_api(&config, &size_info, |api| { + api.clear(background_color); + }); + + let mut lines = RenderLines::new(); + + // Draw grid + { + let _sampler = self.meter.sampler(); + + self.renderer.with_api(&config, &size_info, |mut api| { + // Iterate over all non-empty cells in the grid + for cell in grid_cells { + // Update underline/strikeout + lines.update(cell); + + // Draw the cell + api.render_cell(cell, glyph_cache); + } + }); + } + + let mut rects = lines.into_rects(&metrics, &size_info); + + if let Some(message) = message_buffer.message() { + let text = message.text(&size_info); + + // Create a new rectangle for the background + let start_line = size_info.lines().0 - text.len(); + let y = size_info.padding_y + size_info.cell_height * start_line as f32; + rects.push(RenderRect::new( + 0., + y, + size_info.width, + size_info.height - y, + message.color(), + )); + + // Draw rectangles including the new background + self.renderer.draw_rects( + &size_info, + config.visual_bell.color, + visual_bell_intensity, + rects, + ); + + // Relay messages to the user + let mut offset = 1; + for message_text in text.iter().rev() { + self.renderer.with_api(&config, &size_info, |mut api| { + api.render_string( + &message_text, + Line(size_info.lines().saturating_sub(offset)), + glyph_cache, + None, + ); + }); + offset += 1; + } + } else { + // Draw rectangles + self.renderer.draw_rects( + &size_info, + config.visual_bell.color, + visual_bell_intensity, + rects, + ); + } + + // Draw render timer + if config.render_timer() { + let timing = format!("{:.3} usec", self.meter.average()); + let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 }; + self.renderer.with_api(&config, &size_info, |mut api| { + api.render_string(&timing[..], size_info.lines() - 2, glyph_cache, Some(color)); + }); + } + + self.window.swap_buffers(); + } +} + +/// Calculate padding to spread it evenly around the terminal content +#[inline] +fn dynamic_padding(padding: f32, dimension: f32, cell_dimension: f32) -> f32 { + padding + ((dimension - 2. * padding) % cell_dimension) / 2. +} + +/// Calculate the cell dimensions based on font metrics. +#[inline] +fn compute_cell_size(config: &Config, metrics: &font::Metrics) -> (f32, f32) { + let offset_x = f64::from(config.font.offset.x); + let offset_y = f64::from(config.font.offset.y); + ( + f32::max(1., ((metrics.average_advance + offset_x) as f32).floor()), + f32::max(1., ((metrics.line_height + offset_y) as f32).floor()), + ) +} diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs new file mode 100644 index 0000000..984352d --- /dev/null +++ b/alacritty/src/event.rs @@ -0,0 +1,651 @@ +//! Process window events +use std::borrow::Cow; +use std::env; +#[cfg(unix)] +use std::fs; +use std::fs::File; +use std::io::Write; +use std::sync::Arc; +use std::time::Instant; + +use glutin::dpi::PhysicalSize; +use glutin::event::{ElementState, Event as GlutinEvent, MouseButton}; +use glutin::event_loop::{ControlFlow, EventLoop, EventLoopProxy}; +use glutin::platform::desktop::EventLoopExtDesktop; +#[cfg(not(any(target_os = "macos", windows)))] +use glutin::platform::unix::EventLoopWindowTargetExtUnix; +use log::{debug, info, warn}; +use serde_json as json; + +use font::Size; + +use alacritty_terminal::clipboard::ClipboardType; +use alacritty_terminal::config::LOG_TARGET_CONFIG; +use alacritty_terminal::event::OnResize; +use alacritty_terminal::event::{Event, EventListener, Notify}; +use alacritty_terminal::grid::Scroll; +use alacritty_terminal::index::{Column, Line, Point, Side}; +use alacritty_terminal::message_bar::{Message, MessageBuffer}; +use alacritty_terminal::selection::Selection; +use alacritty_terminal::sync::FairMutex; +use alacritty_terminal::term::cell::Cell; +use alacritty_terminal::term::{SizeInfo, Term}; +use alacritty_terminal::tty; +use alacritty_terminal::util::{limit, start_daemon}; + +use crate::config; +use crate::config::Config; +use crate::display::Display; +use crate::input::{self, ActionContext as _, Modifiers}; +use crate::window::Window; + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum FontResize { + Delta(f32), + Reset, +} + +#[derive(Default, Copy, Clone, Debug, PartialEq)] +pub struct Resize { + pub dimensions: Option, + pub message_buffer: Option<()>, + pub font_size: Option, +} + +impl Resize { + fn is_empty(&self) -> bool { + self.dimensions.is_none() && self.font_size.is_none() && self.message_buffer.is_none() + } +} + +pub struct ActionContext<'a, N, T> { + pub notifier: &'a mut N, + pub terminal: &'a mut Term, + pub size_info: &'a mut SizeInfo, + pub mouse: &'a mut Mouse, + pub received_count: &'a mut usize, + pub suppress_chars: &'a mut bool, + pub modifiers: &'a mut Modifiers, + pub window: &'a mut Window, + pub message_buffer: &'a mut MessageBuffer, + pub resize_pending: &'a mut Resize, + pub font_size: &'a Size, +} + +impl<'a, N: Notify + 'a, T: EventListener> input::ActionContext for ActionContext<'a, N, T> { + fn write_to_pty>>(&mut self, val: B) { + self.notifier.notify(val); + } + + fn size_info(&self) -> SizeInfo { + *self.size_info + } + + fn scroll(&mut self, scroll: Scroll) { + self.terminal.scroll_display(scroll); + + if let ElementState::Pressed = self.mouse().left_button_state { + let (x, y) = (self.mouse().x, self.mouse().y); + let size_info = self.size_info(); + let point = size_info.pixels_to_coords(x, y); + let cell_side = self.mouse().cell_side; + self.update_selection(Point { line: point.line, col: point.col }, cell_side); + } + } + + fn copy_selection(&mut self, ty: ClipboardType) { + if let Some(selected) = self.terminal.selection_to_string() { + if !selected.is_empty() { + self.terminal.clipboard().store(ty, selected); + } + } + } + + fn selection_is_empty(&self) -> bool { + self.terminal.selection().as_ref().map(Selection::is_empty).unwrap_or(true) + } + + fn clear_selection(&mut self) { + *self.terminal.selection_mut() = None; + self.terminal.dirty = true; + } + + fn update_selection(&mut self, point: Point, side: Side) { + let point = self.terminal.visible_to_buffer(point); + + // Update selection if one exists + if let Some(ref mut selection) = self.terminal.selection_mut() { + selection.update(point, side); + } + + self.terminal.dirty = true; + } + + fn simple_selection(&mut self, point: Point, side: Side) { + let point = self.terminal.visible_to_buffer(point); + *self.terminal.selection_mut() = Some(Selection::simple(point, side)); + self.terminal.dirty = true; + } + + fn block_selection(&mut self, point: Point, side: Side) { + let point = self.terminal.visible_to_buffer(point); + *self.terminal.selection_mut() = Some(Selection::block(point, side)); + self.terminal.dirty = true; + } + + fn semantic_selection(&mut self, point: Point) { + let point = self.terminal.visible_to_buffer(point); + *self.terminal.selection_mut() = Some(Selection::semantic(point)); + self.terminal.dirty = true; + } + + fn line_selection(&mut self, point: Point) { + let point = self.terminal.visible_to_buffer(point); + *self.terminal.selection_mut() = Some(Selection::lines(point)); + self.terminal.dirty = true; + } + + fn mouse_coords(&self) -> Option { + let x = self.mouse.x as usize; + let y = self.mouse.y as usize; + + if self.size_info.contains_point(x, y, true) { + Some(self.size_info.pixels_to_coords(x, y)) + } else { + None + } + } + + #[inline] + fn mouse_mut(&mut self) -> &mut Mouse { + self.mouse + } + + #[inline] + fn mouse(&self) -> &Mouse { + self.mouse + } + + #[inline] + fn received_count(&mut self) -> &mut usize { + &mut self.received_count + } + + #[inline] + fn suppress_chars(&mut self) -> &mut bool { + &mut self.suppress_chars + } + + #[inline] + fn modifiers(&mut self) -> &mut Modifiers { + &mut self.modifiers + } + + #[inline] + fn window(&self) -> &Window { + self.window + } + + #[inline] + fn window_mut(&mut self) -> &mut Window { + self.window + } + + #[inline] + fn terminal(&self) -> &Term { + self.terminal + } + + #[inline] + fn terminal_mut(&mut self) -> &mut Term { + self.terminal + } + + fn spawn_new_instance(&mut self) { + let alacritty = env::args().next().unwrap(); + + #[cfg(unix)] + let args = { + #[cfg(not(target_os = "freebsd"))] + let proc_prefix = ""; + #[cfg(target_os = "freebsd")] + let proc_prefix = "/compat/linux"; + let link_path = format!("{}/proc/{}/cwd", proc_prefix, tty::child_pid()); + if let Ok(path) = fs::read_link(link_path) { + vec!["--working-directory".into(), path] + } else { + Vec::new() + } + }; + #[cfg(not(unix))] + let args: Vec = Vec::new(); + + match start_daemon(&alacritty, &args) { + Ok(_) => debug!("Started new Alacritty process: {} {:?}", alacritty, args), + Err(_) => warn!("Unable to start new Alacritty process: {} {:?}", alacritty, args), + } + } + + fn change_font_size(&mut self, delta: f32) { + self.resize_pending.font_size = Some(FontResize::Delta(delta)); + self.terminal.dirty = true; + } + + fn reset_font_size(&mut self) { + self.resize_pending.font_size = Some(FontResize::Reset); + self.terminal.dirty = true; + } + + fn pop_message(&mut self) { + self.resize_pending.message_buffer = Some(()); + self.message_buffer.pop(); + } + + fn message(&self) -> Option<&Message> { + self.message_buffer.message() + } +} + +pub enum ClickState { + None, + Click, + DoubleClick, + TripleClick, +} + +/// State of the mouse +pub struct Mouse { + pub x: usize, + pub y: usize, + pub left_button_state: ElementState, + pub middle_button_state: ElementState, + pub right_button_state: ElementState, + pub last_click_timestamp: Instant, + pub click_state: ClickState, + pub scroll_px: i32, + pub line: Line, + pub column: Column, + pub cell_side: Side, + pub lines_scrolled: f32, + pub block_url_launcher: bool, + pub last_button: MouseButton, +} + +impl Default for Mouse { + fn default() -> Mouse { + Mouse { + x: 0, + y: 0, + last_click_timestamp: Instant::now(), + left_button_state: ElementState::Released, + middle_button_state: ElementState::Released, + right_button_state: ElementState::Released, + click_state: ClickState::None, + scroll_px: 0, + line: Line(0), + column: Column(0), + cell_side: Side::Left, + lines_scrolled: 0.0, + block_url_launcher: false, + last_button: MouseButton::Other(0), + } + } +} + +/// The event processor +/// +/// Stores some state from received events and dispatches actions when they are +/// triggered. +pub struct Processor { + notifier: N, + mouse: Mouse, + received_count: usize, + suppress_chars: bool, + modifiers: Modifiers, + config: Config, + pty_resize_handle: Box, + message_buffer: MessageBuffer, + display: Display, +} + +impl Processor { + /// Create a new event processor + /// + /// Takes a writer which is expected to be hooked up to the write end of a + /// pty. + pub fn new( + notifier: N, + pty_resize_handle: Box, + message_buffer: MessageBuffer, + config: Config, + display: Display, + ) -> Processor { + Processor { + notifier, + mouse: Default::default(), + received_count: 0, + suppress_chars: false, + modifiers: Default::default(), + config, + pty_resize_handle, + message_buffer, + display, + } + } + + /// Run the event loop. + pub fn run(&mut self, terminal: Arc>>, mut event_loop: EventLoop) + where + T: EventListener, + { + #[cfg(not(any(target_os = "macos", windows)))] + let mut dpr_initialized = false; + + let mut event_queue = Vec::new(); + + event_loop.run_return(|event, _event_loop, control_flow| { + if self.config.debug.print_events { + info!("glutin event: {:?}", event); + } + + match (&event, tty::process_should_exit()) { + // Check for shutdown + (GlutinEvent::UserEvent(Event::Exit), _) | (_, true) => { + *control_flow = ControlFlow::Exit; + return; + }, + // Process events + (GlutinEvent::EventsCleared, _) => { + *control_flow = ControlFlow::Wait; + + if event_queue.is_empty() { + return; + } + }, + // Buffer events + _ => { + *control_flow = ControlFlow::Poll; + if !Self::skip_event(&event) { + event_queue.push(event); + } + return; + }, + } + + let mut terminal = terminal.lock(); + + let mut resize_pending = Resize::default(); + + let context = ActionContext { + terminal: &mut terminal, + notifier: &mut self.notifier, + mouse: &mut self.mouse, + size_info: &mut self.display.size_info, + received_count: &mut self.received_count, + suppress_chars: &mut self.suppress_chars, + modifiers: &mut self.modifiers, + message_buffer: &mut self.message_buffer, + resize_pending: &mut resize_pending, + window: &mut self.display.window, + font_size: &self.display.font_size, + }; + let mut processor = input::Processor::new(context, &mut self.config); + + for event in event_queue.drain(..) { + Processor::handle_event(event, &mut processor); + } + + // TODO: Workaround for incorrect startup DPI on X11 + // https://github.com/rust-windowing/winit/issues/998 + #[cfg(not(any(target_os = "macos", windows)))] + { + if !dpr_initialized && _event_loop.is_x11() { + dpr_initialized = true; + + let dpr = self.display.window.hidpi_factor(); + self.display.size_info.dpr = dpr; + + let size = self.display.window.inner_size().to_physical(dpr); + + resize_pending.font_size = Some(FontResize::Delta(0.)); + resize_pending.dimensions = Some(size); + + terminal.dirty = true; + } + } + + // Process resize events + if !resize_pending.is_empty() { + self.display.handle_resize( + &mut terminal, + self.pty_resize_handle.as_mut(), + &self.message_buffer, + &self.config, + resize_pending, + ); + } + + if terminal.dirty { + // Clear dirty flag + terminal.dirty = !terminal.visual_bell.completed(); + + // Redraw screen + self.display.draw(terminal, &self.message_buffer, &self.config); + } + }); + + // Write ref tests to disk + self.write_ref_test_results(&terminal.lock()); + } + + /// Handle events from glutin + /// + /// Doesn't take self mutably due to borrow checking. Kinda uggo but w/e. + fn handle_event( + event: GlutinEvent, + processor: &mut input::Processor>, + ) where + T: EventListener, + { + match event { + GlutinEvent::UserEvent(event) => match event { + Event::Title(title) => processor.ctx.window.set_title(&title), + Event::Wakeup => processor.ctx.terminal.dirty = true, + Event::Urgent => { + processor.ctx.window.set_urgent(!processor.ctx.terminal.is_focused) + }, + Event::ConfigReload(path) => { + processor.ctx.message_buffer.remove_target(LOG_TARGET_CONFIG); + processor.ctx.resize_pending.message_buffer = Some(()); + + if let Ok(config) = config::reload_from(&path) { + processor.ctx.terminal.update_config(&config); + + if *processor.ctx.font_size == processor.config.font.size { + processor.ctx.resize_pending.font_size = Some(FontResize::Reset); + } + + *processor.config = config; + + processor.ctx.terminal.dirty = true; + } + }, + Event::Message(message) => { + processor.ctx.message_buffer.push(message); + processor.ctx.resize_pending.message_buffer = Some(()); + processor.ctx.terminal.dirty = true; + }, + Event::MouseCursorDirty => processor.reset_mouse_cursor(), + Event::Exit => (), + }, + GlutinEvent::WindowEvent { event, window_id, .. } => { + use glutin::event::WindowEvent::*; + match event { + CloseRequested => processor.ctx.terminal.exit(), + Resized(lsize) => { + let psize = lsize.to_physical(processor.ctx.size_info.dpr); + processor.ctx.resize_pending.dimensions = Some(psize); + processor.ctx.terminal.dirty = true; + }, + KeyboardInput { input, .. } => { + processor.process_key(input); + if input.state == ElementState::Pressed { + // Hide cursor while typing + if processor.config.ui_config.mouse.hide_when_typing { + processor.ctx.window.set_mouse_visible(false); + } + } + }, + ReceivedCharacter(c) => processor.received_char(c), + MouseInput { state, button, modifiers, .. } => { + if !cfg!(target_os = "macos") || processor.ctx.terminal.is_focused { + processor.ctx.window.set_mouse_visible(true); + processor.mouse_input(state, button, modifiers); + processor.ctx.terminal.dirty = true; + } + }, + 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); + + processor.ctx.window.set_mouse_visible(true); + processor.mouse_moved(x as usize, y as usize, modifiers); + }, + MouseWheel { delta, phase, modifiers, .. } => { + processor.ctx.window.set_mouse_visible(true); + processor.on_mouse_wheel(delta, phase, modifiers); + }, + Focused(is_focused) => { + if window_id == processor.ctx.window.window_id() { + processor.ctx.terminal.is_focused = is_focused; + processor.ctx.terminal.dirty = true; + + if is_focused { + processor.ctx.window.set_urgent(false); + } else { + processor.ctx.window.set_mouse_visible(true); + } + + processor.on_focus_change(is_focused); + } + }, + DroppedFile(path) => { + let path: String = path.to_string_lossy().into(); + processor.ctx.write_to_pty(path.into_bytes()); + }, + HiDpiFactorChanged(dpr) => { + let dpr_change = (dpr / processor.ctx.size_info.dpr) as f32; + let resize_pending = &mut processor.ctx.resize_pending; + + // Push current font to update its DPR + resize_pending.font_size = Some(FontResize::Delta(0.)); + + // Scale window dimensions with new DPR + let old_width = processor.ctx.size_info.width; + let old_height = processor.ctx.size_info.height; + let dimensions = resize_pending.dimensions.get_or_insert_with(|| { + PhysicalSize::new(f64::from(old_width), f64::from(old_height)) + }); + dimensions.width *= f64::from(dpr_change); + dimensions.height *= f64::from(dpr_change); + + processor.ctx.terminal.dirty = true; + processor.ctx.size_info.dpr = dpr; + }, + RedrawRequested => processor.ctx.terminal.dirty = true, + TouchpadPressure { .. } + | CursorEntered { .. } + | CursorLeft { .. } + | AxisMotion { .. } + | HoveredFileCancelled + | Destroyed + | HoveredFile(_) + | Touch(_) + | Moved(_) => (), + // TODO: Add support for proper modifier handling + ModifiersChanged { .. } => (), + } + }, + GlutinEvent::DeviceEvent { .. } + | GlutinEvent::Suspended { .. } + | GlutinEvent::NewEvents { .. } + | GlutinEvent::EventsCleared + | GlutinEvent::Resumed + | GlutinEvent::LoopDestroyed => (), + } + } + + /// Check if an event is irrelevant and can be skipped + fn skip_event(event: &GlutinEvent) -> bool { + match event { + GlutinEvent::UserEvent(Event::Exit) => true, + GlutinEvent::WindowEvent { event, .. } => { + use glutin::event::WindowEvent::*; + match event { + TouchpadPressure { .. } + | CursorEntered { .. } + | CursorLeft { .. } + | AxisMotion { .. } + | HoveredFileCancelled + | Destroyed + | HoveredFile(_) + | Touch(_) + | Moved(_) => true, + _ => false, + } + }, + GlutinEvent::DeviceEvent { .. } + | GlutinEvent::Suspended { .. } + | GlutinEvent::NewEvents { .. } + | GlutinEvent::EventsCleared + | GlutinEvent::LoopDestroyed => true, + _ => false, + } + } + + // Write the ref test results to the disk + pub fn write_ref_test_results(&self, terminal: &Term) { + if !self.config.debug.ref_test { + return; + } + + // dump grid state + let mut grid = terminal.grid().clone(); + grid.initialize_all(&Cell::default()); + grid.truncate(); + + let serialized_grid = json::to_string(&grid).expect("serialize grid"); + + let serialized_size = json::to_string(&self.display.size_info).expect("serialize size"); + + let serialized_config = format!("{{\"history_size\":{}}}", grid.history_size()); + + File::create("./grid.json") + .and_then(|mut f| f.write_all(serialized_grid.as_bytes())) + .expect("write grid.json"); + + File::create("./size.json") + .and_then(|mut f| f.write_all(serialized_size.as_bytes())) + .expect("write size.json"); + + File::create("./config.json") + .and_then(|mut f| f.write_all(serialized_config.as_bytes())) + .expect("write config.json"); + } +} + +#[derive(Debug, Clone)] +pub struct EventProxy(EventLoopProxy); + +impl EventProxy { + pub fn new(proxy: EventLoopProxy) -> Self { + EventProxy(proxy) + } +} + +impl EventListener for EventProxy { + fn send_event(&self, event: Event) { + let _ = self.0.send_event(event); + } +} diff --git a/alacritty_terminal/src/input.rs b/alacritty/src/input.rs similarity index 64% rename from alacritty_terminal/src/input.rs rename to alacritty/src/input.rs index e268bf6..be6a030 100644 --- a/alacritty_terminal/src/input.rs +++ b/alacritty/src/input.rs @@ -18,44 +18,45 @@ //! In order to figure that out, state about which modifier keys are pressed //! needs to be tracked. Additionally, we need a bit of a state machine to //! determine what to do when a non-modifier key is pressed. -use crate::url::Url; use std::borrow::Cow; +use std::marker::PhantomData; use std::mem; use std::time::Instant; -use glutin::{ - ElementState, KeyboardInput, ModifiersState, MouseButton, MouseCursor, MouseScrollDelta, - TouchPhase, VirtualKeyCode, +use glutin::event::{ + ElementState, KeyboardInput, ModifiersState, MouseButton, MouseScrollDelta, TouchPhase, + VirtualKeyCode, }; +use glutin::window::CursorIcon; +use log::{debug, trace, warn}; -use crate::ansi::{ClearMode, Handler}; -use crate::clipboard::ClipboardType; -use crate::config::{self, Key}; +use alacritty_terminal::ansi::{ClearMode, Handler}; +use alacritty_terminal::clipboard::ClipboardType; +use alacritty_terminal::event::EventListener; +use alacritty_terminal::grid::Scroll; +use alacritty_terminal::index::{Column, Line, Point, Side}; +use alacritty_terminal::message_bar::{self, Message}; +use alacritty_terminal::term::mode::TermMode; +use alacritty_terminal::term::{SizeInfo, Term}; +use alacritty_terminal::url::Url; +use alacritty_terminal::util::start_daemon; + +use crate::config::{Action, Binding, Config, Key, RelaxedEq}; +use crate::display::FONT_SIZE_STEP; use crate::event::{ClickState, Mouse}; -use crate::grid::Scroll; -use crate::index::{Column, Line, Point, Side}; -use crate::message_bar::{self, Message}; -use crate::term::mode::TermMode; -use crate::term::{SizeInfo, Term}; -use crate::util::start_daemon; - -pub const FONT_SIZE_STEP: f32 = 0.5; +use crate::window::Window; /// Processes input from glutin. /// /// An escape sequence may be emitted in case specific keys or key combinations /// are activated. -pub struct Processor<'a, A: 'a> { - pub key_bindings: &'a [KeyBinding], - pub mouse_bindings: &'a [MouseBinding], - pub mouse_config: &'a config::Mouse, - pub scrolling_config: &'a config::Scrolling, +pub struct Processor<'a, T: EventListener, A: ActionContext + 'a> { pub ctx: A, - pub save_to_clipboard: bool, - pub alt_send_esc: bool, + pub config: &'a mut Config, + _phantom: PhantomData, } -pub trait ActionContext { +pub trait ActionContext { fn write_to_pty>>(&mut self, _: B); fn size_info(&self) -> SizeInfo; fn copy_selection(&mut self, _: ClipboardType); @@ -73,13 +74,15 @@ pub trait ActionContext { fn suppress_chars(&mut self) -> &mut bool; fn modifiers(&mut self) -> &mut Modifiers; fn scroll(&mut self, scroll: Scroll); - fn hide_window(&mut self); - fn terminal(&self) -> &Term; - fn terminal_mut(&mut self) -> &mut Term; + fn window(&self) -> &Window; + fn window_mut(&mut self) -> &mut Window; + fn terminal(&self) -> &Term; + fn terminal_mut(&mut self) -> &mut Term; fn spawn_new_instance(&mut self); - fn toggle_fullscreen(&mut self); - #[cfg(target_os = "macos")] - fn toggle_simple_fullscreen(&mut self); + fn change_font_size(&mut self, delta: f32); + fn reset_font_size(&mut self); + fn pop_message(&mut self); + fn message(&self) -> Option<&Message>; } #[derive(Debug, Default, Copy, Clone)] @@ -123,199 +126,21 @@ impl From<&mut Modifiers> for ModifiersState { } } -/// Describes a state and action to take in that state -/// -/// This is the shared component of `MouseBinding` and `KeyBinding` -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Binding { - /// Modifier keys required to activate binding - pub mods: ModifiersState, - - /// String to send to pty if mods and mode match - pub action: Action, - - /// Terminal mode required to activate binding - pub mode: TermMode, - - /// excluded terminal modes where the binding won't be activated - pub notmode: TermMode, - - /// This property is used as part of the trigger detection code. - /// - /// For example, this might be a key like "G", or a mouse button. - pub trigger: T, +trait Execute { + fn execute>(&self, ctx: &mut A, mouse_mode: bool); } -/// Bindings that are triggered by a keyboard key -pub type KeyBinding = Binding; - -/// Bindings that are triggered by a mouse button -pub type MouseBinding = Binding; - -impl Default for KeyBinding { - fn default() -> KeyBinding { - KeyBinding { - mods: Default::default(), - action: Action::Esc(String::new()), - mode: TermMode::NONE, - notmode: TermMode::NONE, - trigger: Key::A, - } - } -} - -impl Default for MouseBinding { - fn default() -> MouseBinding { - MouseBinding { - mods: Default::default(), - action: Action::Esc(String::new()), - mode: TermMode::NONE, - notmode: TermMode::NONE, - trigger: MouseButton::Left, - } - } -} - -impl Binding { - #[inline] - fn is_triggered_by( - &self, - mode: TermMode, - mods: ModifiersState, - input: &T, - relaxed: bool, - ) -> bool { - // Check input first since bindings are stored in one big list. This is - // the most likely item to fail so prioritizing it here allows more - // checks to be short circuited. - self.trigger == *input - && mode.contains(self.mode) - && !mode.intersects(self.notmode) - && self.mods_match(mods, relaxed) - } - - #[inline] - pub fn triggers_match(&self, binding: &Binding) -> bool { - // Check the binding's key and modifiers - if self.trigger != binding.trigger || self.mods != binding.mods { - return false; - } - - // Completely empty modes match all modes - if (self.mode.is_empty() && self.notmode.is_empty()) - || (binding.mode.is_empty() && binding.notmode.is_empty()) - { - return true; - } - - // Check for intersection (equality is required since empty does not intersect itself) - (self.mode == binding.mode || self.mode.intersects(binding.mode)) - && (self.notmode == binding.notmode || self.notmode.intersects(binding.notmode)) - } -} - -impl Binding { +impl Execute for Binding { /// Execute the action associate with this binding #[inline] - fn execute(&self, ctx: &mut A, mouse_mode: bool) { + fn execute>(&self, ctx: &mut A, mouse_mode: bool) { self.action.execute(ctx, mouse_mode) } +} - /// Check that two mods descriptions for equivalence +impl Execute for Action { #[inline] - fn mods_match(&self, mods: ModifiersState, relaxed: bool) -> bool { - if relaxed { - self.mods.relaxed_eq(mods) - } else { - self.mods == mods - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] -pub enum Action { - /// Write an escape sequence. - #[serde(skip)] - Esc(String), - - /// Paste contents of system clipboard. - Paste, - - /// Store current selection into clipboard. - Copy, - - /// Paste contents of selection buffer. - PasteSelection, - - /// Increase font size. - IncreaseFontSize, - - /// Decrease font size. - DecreaseFontSize, - - /// Reset font size to the config value. - ResetFontSize, - - /// Scroll exactly one page up. - ScrollPageUp, - - /// Scroll exactly one page down. - ScrollPageDown, - - /// Scroll one line up. - ScrollLineUp, - - /// Scroll one line down. - ScrollLineDown, - - /// Scroll all the way to the top. - ScrollToTop, - - /// Scroll all the way to the bottom. - ScrollToBottom, - - /// Clear the display buffer(s) to remove history. - ClearHistory, - - /// Run given command. - #[serde(skip)] - Command(String, Vec), - - /// Hide the Alacritty window. - Hide, - - /// Quit Alacritty. - Quit, - - /// Clear warning and error notices. - ClearLogNotice, - - /// Spawn a new instance of Alacritty. - SpawnNewInstance, - - /// Toggle fullscreen. - ToggleFullscreen, - - /// Toggle simple fullscreen on macos. - #[cfg(target_os = "macos")] - ToggleSimpleFullscreen, - - /// Allow receiving char input. - ReceiveChar, - - /// No action. - None, -} - -impl Default for Action { - fn default() -> Action { - Action::None - } -} - -impl Action { - #[inline] - fn execute(&self, ctx: &mut A, mouse_mode: bool) { + fn execute>(&self, ctx: &mut A, mouse_mode: bool) { match *self { Action::Esc(ref s) => { ctx.scroll(Scroll::Bottom); @@ -326,13 +151,13 @@ impl Action { }, Action::Paste => { let text = ctx.terminal_mut().clipboard().load(ClipboardType::Clipboard); - self.paste(ctx, &text); + paste(ctx, &text); }, Action::PasteSelection => { // Only paste if mouse events are not captured by an application if !mouse_mode { let text = ctx.terminal_mut().clipboard().load(ClipboardType::Selection); - self.paste(ctx, &text); + paste(ctx, &text); } }, Action::Command(ref program, ref args) => { @@ -343,94 +168,41 @@ impl Action { Err(err) => warn!("Couldn't run command {}", err), } }, - Action::ToggleFullscreen => { - ctx.toggle_fullscreen(); - }, + Action::ToggleFullscreen => ctx.window_mut().toggle_fullscreen(), #[cfg(target_os = "macos")] - Action::ToggleSimpleFullscreen => { - ctx.toggle_simple_fullscreen(); - }, - Action::Hide => { - ctx.hide_window(); - }, - Action::Quit => { - ctx.terminal_mut().exit(); - }, - Action::IncreaseFontSize => { - ctx.terminal_mut().change_font_size(FONT_SIZE_STEP); - }, - Action::DecreaseFontSize => { - ctx.terminal_mut().change_font_size(-FONT_SIZE_STEP); - }, - Action::ResetFontSize => { - ctx.terminal_mut().reset_font_size(); - }, - Action::ScrollPageUp => { - ctx.scroll(Scroll::PageUp); - }, - Action::ScrollPageDown => { - ctx.scroll(Scroll::PageDown); - }, - Action::ScrollLineUp => { - ctx.scroll(Scroll::Lines(1)); - }, - Action::ScrollLineDown => { - ctx.scroll(Scroll::Lines(-1)); - }, - Action::ScrollToTop => { - ctx.scroll(Scroll::Top); - }, - Action::ScrollToBottom => { - ctx.scroll(Scroll::Bottom); - }, - Action::ClearHistory => { - ctx.terminal_mut().clear_screen(ClearMode::Saved); - }, - Action::ClearLogNotice => { - ctx.terminal_mut().message_buffer_mut().pop(); - }, - Action::SpawnNewInstance => { - ctx.spawn_new_instance(); - }, + Action::ToggleSimpleFullscreen => ctx.window_mut().toggle_simple_fullscreen(), + Action::Hide => ctx.window().set_visible(false), + Action::Quit => ctx.terminal_mut().exit(), + Action::IncreaseFontSize => ctx.change_font_size(FONT_SIZE_STEP), + Action::DecreaseFontSize => ctx.change_font_size(FONT_SIZE_STEP * -1.), + Action::ResetFontSize => ctx.reset_font_size(), + Action::ScrollPageUp => ctx.scroll(Scroll::PageUp), + Action::ScrollPageDown => ctx.scroll(Scroll::PageDown), + Action::ScrollLineUp => ctx.scroll(Scroll::Lines(1)), + Action::ScrollLineDown => ctx.scroll(Scroll::Lines(-1)), + Action::ScrollToTop => ctx.scroll(Scroll::Top), + Action::ScrollToBottom => ctx.scroll(Scroll::Bottom), + Action::ClearHistory => ctx.terminal_mut().clear_screen(ClearMode::Saved), + Action::ClearLogNotice => ctx.pop_message(), + Action::SpawnNewInstance => ctx.spawn_new_instance(), Action::ReceiveChar | Action::None => (), } } - - fn paste(&self, ctx: &mut A, contents: &str) { - if ctx.terminal().mode().contains(TermMode::BRACKETED_PASTE) { - ctx.write_to_pty(&b"\x1b[200~"[..]); - ctx.write_to_pty(contents.replace("\x1b", "").into_bytes()); - ctx.write_to_pty(&b"\x1b[201~"[..]); - } else { - // In non-bracketed (ie: normal) mode, terminal applications cannot distinguish - // pasted data from keystrokes. - // In theory, we should construct the keystrokes needed to produce the data we are - // pasting... since that's neither practical nor sensible (and probably an impossible - // task to solve in a general way), we'll just replace line breaks (windows and unix - // style) with a single carriage return (\r, which is what the Enter key produces). - ctx.write_to_pty(contents.replace("\r\n", "\r").replace("\n", "\r").into_bytes()); - } - } } -trait RelaxedEq { - fn relaxed_eq(&self, other: T) -> bool; -} - -impl RelaxedEq for ModifiersState { - // Make sure that modifiers in the config are always present, - // but ignore surplus modifiers. - fn relaxed_eq(&self, other: Self) -> bool { - (!self.logo || other.logo) - && (!self.alt || other.alt) - && (!self.ctrl || other.ctrl) - && (!self.shift || other.shift) - } -} - -impl From<&'static str> for Action { - fn from(s: &'static str) -> Action { - Action::Esc(s.into()) +fn paste>(ctx: &mut A, contents: &str) { + if ctx.terminal().mode().contains(TermMode::BRACKETED_PASTE) { + ctx.write_to_pty(&b"\x1b[200~"[..]); + ctx.write_to_pty(contents.replace("\x1b", "").into_bytes()); + ctx.write_to_pty(&b"\x1b[201~"[..]); + } else { + // In non-bracketed (ie: normal) mode, terminal applications cannot distinguish + // pasted data from keystrokes. + // In theory, we should construct the keystrokes needed to produce the data we are + // pasting... since that's neither practical nor sensible (and probably an impossible + // task to solve in a general way), we'll just replace line breaks (windows and unix + // style) with a single carriage return (\r, which is what the Enter key produces). + ctx.write_to_pty(contents.replace("\r\n", "\r").replace("\n", "\r").into_bytes()); } } @@ -443,7 +215,21 @@ pub enum MouseState { Text, } -impl<'a, A: ActionContext + 'a> Processor<'a, A> { +impl From for CursorIcon { + fn from(mouse_state: MouseState) -> CursorIcon { + match mouse_state { + MouseState::Url(_) | MouseState::MessageBarButton => CursorIcon::Hand, + MouseState::Text => CursorIcon::Text, + _ => CursorIcon::Default, + } + } +} + +impl<'a, T: EventListener, A: ActionContext + 'a> Processor<'a, T, A> { + pub fn new(ctx: A, config: &'a mut Config) -> Self { + Self { ctx, config, _phantom: Default::default() } + } + fn mouse_state(&mut self, point: Point, mods: ModifiersState) -> MouseState { let mouse_mode = TermMode::MOUSE_MOTION | TermMode::MOUSE_DRAG | TermMode::MOUSE_REPORT_CLICK; @@ -458,9 +244,9 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { } // Check for URL at point with required modifiers held - if self.mouse_config.url.mods().relaxed_eq(mods) + if self.config.ui_config.mouse.url.mods().relaxed_eq(mods) && (!self.ctx.terminal().mode().intersects(mouse_mode) || mods.shift) - && self.mouse_config.url.launcher.is_some() + && self.config.ui_config.mouse.url.launcher.is_some() && self.ctx.selection_is_empty() && self.ctx.mouse().left_button_state != ElementState::Pressed { @@ -638,14 +424,16 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { self.ctx.mouse_mut().click_state = match self.ctx.mouse().click_state { ClickState::Click - if !button_changed && elapsed < self.mouse_config.double_click.threshold => + if !button_changed + && elapsed < self.config.ui_config.mouse.double_click.threshold => { self.ctx.mouse_mut().block_url_launcher = true; self.on_mouse_double_click(button, point); ClickState::DoubleClick } ClickState::DoubleClick - if !button_changed && elapsed < self.mouse_config.triple_click.threshold => + if !button_changed + && elapsed < self.config.ui_config.mouse.triple_click.threshold => { self.ctx.mouse_mut().block_url_launcher = true; self.on_mouse_triple_click(button, point); @@ -723,7 +511,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { return; } - if let Some(ref launcher) = self.mouse_config.url.launcher { + if let Some(ref launcher) = self.config.ui_config.mouse.url.launcher { let mut args = launcher.args().to_vec(); args.push(self.ctx.terminal().url_to_string(url)); @@ -766,7 +554,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { let height = self.ctx.size_info().cell_height as i32; // Make sure the new and deprecated setting are both allowed - let faux_multiplier = self.scrolling_config.faux_multiplier() as usize; + let faux_multiplier = self.config.scrolling.faux_multiplier() as usize; if self.ctx.terminal().mode().intersects(mouse_modes) { self.ctx.mouse_mut().scroll_px += new_scroll_px; @@ -794,7 +582,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { } self.ctx.write_to_pty(content); } else { - let multiplier = i32::from(self.scrolling_config.multiplier()); + let multiplier = i32::from(self.config.scrolling.multiplier()); self.ctx.mouse_mut().scroll_px += new_scroll_px * multiplier; let lines = self.ctx.mouse().scroll_px / height; @@ -887,7 +675,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { c.encode_utf8(&mut bytes[..]); } - if self.alt_send_esc + if self.config.alt_send_esc() && *self.ctx.received_count() == 0 && self.ctx.modifiers().alt() && utf8_len == 1 @@ -898,6 +686,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { self.ctx.write_to_pty(bytes); *self.ctx.received_count() += 1; + self.ctx.terminal_mut().dirty = false; } /// Attempt to find a binding and execute its action. @@ -905,29 +694,26 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { /// The provided mode, mods, and key must match what is allowed by a binding /// for its action to be executed. fn process_key_bindings(&mut self, input: KeyboardInput) { - let mode = *self.ctx.terminal().mode(); + let mut suppress_chars = None; - *self.ctx.suppress_chars() = self - .key_bindings - .iter() - .filter(|binding| { - let key = match (binding.trigger, input.virtual_keycode) { - (Key::Scancode(_), _) => Key::Scancode(input.scancode), - (_, Some(key)) => Key::from_glutin_input(key), - _ => return false, - }; + for binding in &self.config.ui_config.key_bindings { + let key = match (binding.trigger, input.virtual_keycode) { + (Key::Scancode(_), _) => Key::Scancode(input.scancode), + (_, Some(key)) => Key::from_glutin_input(key), + _ => continue, + }; - binding.is_triggered_by(mode, input.modifiers, &key, false) - }) - .fold(None, |suppress_chars, binding| { + if binding.is_triggered_by(*self.ctx.terminal().mode(), input.modifiers, &key, false) { // Binding was triggered; run the action binding.execute(&mut self.ctx, false); // Don't suppress when there has been a `ReceiveChar` action - Some(suppress_chars.unwrap_or(true) && binding.action != Action::ReceiveChar) - }) - // Don't suppress char if no bindings were triggered - .unwrap_or(false); + *suppress_chars.get_or_insert(true) &= binding.action != Action::ReceiveChar; + } + } + + // Don't suppress char if no bindings were triggered + *self.ctx.suppress_chars() = suppress_chars.unwrap_or(false); } /// Attempt to find a binding and execute its action. @@ -935,7 +721,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { /// The provided mode, mods, and key must match what is allowed by a binding /// for its action to be executed. fn process_mouse_bindings(&mut self, mods: ModifiersState, button: MouseButton) { - for binding in self.mouse_bindings { + for binding in &self.config.ui_config.mouse_bindings { if binding.is_triggered_by(*self.ctx.terminal().mode(), mods, &button, true) { // binding was triggered; run the action let mouse_mode = !mods.shift @@ -951,12 +737,10 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { /// Return the message bar's message if there is some at the specified point fn message_at_point(&mut self, point: Option) -> Option { - if let (Some(point), Some(message)) = - (point, self.ctx.terminal_mut().message_buffer_mut().message()) - { - let size = self.ctx.size_info(); - if point.line.0 >= size.lines().saturating_sub(message.text(&size).len()) { - return Some(message); + let size = &self.ctx.size_info(); + if let (Some(point), Some(message)) = (point, self.ctx.message()) { + if point.line.0 >= size.lines().saturating_sub(message.text(size).len()) { + return Some(message.to_owned()); } } @@ -984,7 +768,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { if self.message_close_at_point(point, message) { let mouse_state = self.mouse_state(point, mods); self.update_mouse_cursor(mouse_state); - self.ctx.terminal_mut().message_buffer_mut().pop(); + self.ctx.pop_message(); } self.ctx.clear_selection(); @@ -994,7 +778,7 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { /// Copy text selection. fn copy_selection(&mut self) { - if self.save_to_clipboard { + if self.config.selection.save_to_clipboard { self.ctx.copy_selection(ClipboardType::Clipboard); } self.ctx.copy_selection(ClipboardType::Selection); @@ -1002,13 +786,16 @@ impl<'a, A: ActionContext + 'a> Processor<'a, A> { #[inline] fn update_mouse_cursor(&mut self, mouse_state: MouseState) { - let mouse_cursor = match mouse_state { - MouseState::Url(_) | MouseState::MessageBarButton => MouseCursor::Hand, - MouseState::Text => MouseCursor::Text, - _ => MouseCursor::Default, - }; + self.ctx.window_mut().set_mouse_cursor(mouse_state.into()); + } - self.ctx.terminal_mut().set_mouse_cursor(mouse_cursor); + #[inline] + pub fn reset_mouse_cursor(&mut self) { + if let Some(point) = self.ctx.mouse_coords() { + let mods = self.ctx.modifiers().into(); + let mouse_state = self.mouse_state(point, mods); + self.update_mouse_cursor(mouse_state); + } } } @@ -1017,202 +804,30 @@ mod tests { use std::borrow::Cow; use std::time::Duration; - use glutin::{ElementState, Event, ModifiersState, MouseButton, VirtualKeyCode, WindowEvent}; + use glutin::event::{ + ElementState, Event, ModifiersState, MouseButton, VirtualKeyCode, WindowEvent, + }; - use crate::clipboard::{Clipboard, ClipboardType}; - use crate::config::{self, ClickHandler, Config}; - use crate::event::{ClickState, Mouse, WindowChanges}; - use crate::grid::Scroll; - use crate::index::{Point, Side}; - use crate::message_bar::MessageBuffer; - use crate::selection::Selection; - use crate::term::{SizeInfo, Term, TermMode}; + use alacritty_terminal::clipboard::{Clipboard, ClipboardType}; + use alacritty_terminal::event::{Event as TerminalEvent, EventListener}; + use alacritty_terminal::grid::Scroll; + use alacritty_terminal::index::{Point, Side}; + use alacritty_terminal::message_bar::{Message, MessageBuffer}; + use alacritty_terminal::selection::Selection; + use alacritty_terminal::term::{SizeInfo, Term, TermMode}; + + use crate::config::{ClickHandler, Config}; + use crate::event::{ClickState, Mouse}; + use crate::window::Window; use super::{Action, Binding, Modifiers, Processor}; const KEY: VirtualKeyCode = VirtualKeyCode::Key0; - type MockBinding = Binding; + struct MockEventProxy; - impl Default for MockBinding { - fn default() -> Self { - Self { - mods: Default::default(), - action: Default::default(), - mode: TermMode::empty(), - notmode: TermMode::empty(), - trigger: Default::default(), - } - } - } - - #[test] - fn binding_matches_itself() { - let binding = MockBinding::default(); - let identical_binding = MockBinding::default(); - - assert!(binding.triggers_match(&identical_binding)); - assert!(identical_binding.triggers_match(&binding)); - } - - #[test] - fn binding_matches_different_action() { - let binding = MockBinding::default(); - let mut different_action = MockBinding::default(); - different_action.action = Action::ClearHistory; - - assert!(binding.triggers_match(&different_action)); - assert!(different_action.triggers_match(&binding)); - } - - #[test] - fn mods_binding_requires_strict_match() { - let mut superset_mods = MockBinding::default(); - superset_mods.mods = ModifiersState { alt: true, logo: true, ctrl: true, shift: true }; - let mut subset_mods = MockBinding::default(); - subset_mods.mods = ModifiersState { alt: true, logo: false, ctrl: false, shift: false }; - - assert!(!superset_mods.triggers_match(&subset_mods)); - assert!(!subset_mods.triggers_match(&superset_mods)); - } - - #[test] - fn binding_matches_identical_mode() { - let mut b1 = MockBinding::default(); - b1.mode = TermMode::ALT_SCREEN; - let mut b2 = MockBinding::default(); - b2.mode = TermMode::ALT_SCREEN; - - assert!(b1.triggers_match(&b2)); - } - - #[test] - fn binding_without_mode_matches_any_mode() { - let b1 = MockBinding::default(); - let mut b2 = MockBinding::default(); - b2.mode = TermMode::APP_KEYPAD; - b2.notmode = TermMode::ALT_SCREEN; - - assert!(b1.triggers_match(&b2)); - } - - #[test] - fn binding_with_mode_matches_empty_mode() { - let mut b1 = MockBinding::default(); - b1.mode = TermMode::APP_KEYPAD; - b1.notmode = TermMode::ALT_SCREEN; - let b2 = MockBinding::default(); - - assert!(b1.triggers_match(&b2)); - } - - #[test] - fn binding_matches_superset_mode() { - let mut b1 = MockBinding::default(); - b1.mode = TermMode::APP_KEYPAD; - let mut b2 = MockBinding::default(); - b2.mode = TermMode::ALT_SCREEN | TermMode::APP_KEYPAD; - - assert!(b1.triggers_match(&b2)); - } - - #[test] - fn binding_matches_subset_mode() { - let mut b1 = MockBinding::default(); - b1.mode = TermMode::ALT_SCREEN | TermMode::APP_KEYPAD; - let mut b2 = MockBinding::default(); - b2.mode = TermMode::APP_KEYPAD; - - assert!(b1.triggers_match(&b2)); - } - - #[test] - fn binding_matches_partial_intersection() { - let mut b1 = MockBinding::default(); - b1.mode = TermMode::ALT_SCREEN | TermMode::APP_KEYPAD; - let mut b2 = MockBinding::default(); - b2.mode = TermMode::APP_KEYPAD | TermMode::APP_CURSOR; - - assert!(b1.triggers_match(&b2)); - } - - #[test] - fn binding_mismatches_notmode() { - let mut b1 = MockBinding::default(); - b1.mode = TermMode::ALT_SCREEN; - let mut b2 = MockBinding::default(); - b2.notmode = TermMode::ALT_SCREEN; - - assert!(!b1.triggers_match(&b2)); - } - - #[test] - fn binding_mismatches_unrelated() { - let mut b1 = MockBinding::default(); - b1.mode = TermMode::ALT_SCREEN; - let mut b2 = MockBinding::default(); - b2.mode = TermMode::APP_KEYPAD; - - assert!(!b1.triggers_match(&b2)); - } - - #[test] - fn binding_trigger_input() { - let mut binding = MockBinding::default(); - binding.trigger = 13; - - let mods = binding.mods; - let mode = binding.mode; - - assert!(binding.is_triggered_by(mode, mods, &13, true)); - assert!(!binding.is_triggered_by(mode, mods, &32, true)); - } - - #[test] - fn binding_trigger_mods() { - let mut binding = MockBinding::default(); - binding.mods = ModifiersState { alt: true, logo: true, ctrl: false, shift: false }; - - let superset_mods = ModifiersState { alt: true, logo: true, ctrl: true, shift: true }; - let subset_mods = ModifiersState { alt: false, logo: false, ctrl: false, shift: false }; - - let t = binding.trigger; - let mode = binding.mode; - - assert!(binding.is_triggered_by(mode, binding.mods, &t, true)); - assert!(binding.is_triggered_by(mode, binding.mods, &t, false)); - - assert!(binding.is_triggered_by(mode, superset_mods, &t, true)); - assert!(!binding.is_triggered_by(mode, superset_mods, &t, false)); - - assert!(!binding.is_triggered_by(mode, subset_mods, &t, true)); - assert!(!binding.is_triggered_by(mode, subset_mods, &t, false)); - } - - #[test] - fn binding_trigger_modes() { - let mut binding = MockBinding::default(); - binding.mode = TermMode::ALT_SCREEN; - - let t = binding.trigger; - let mods = binding.mods; - - assert!(!binding.is_triggered_by(TermMode::INSERT, mods, &t, true)); - assert!(binding.is_triggered_by(TermMode::ALT_SCREEN, mods, &t, true)); - assert!(binding.is_triggered_by(TermMode::ALT_SCREEN | TermMode::INSERT, mods, &t, true)); - } - - #[test] - fn binding_trigger_notmodes() { - let mut binding = MockBinding::default(); - binding.notmode = TermMode::ALT_SCREEN; - - let t = binding.trigger; - let mods = binding.mods; - - assert!(binding.is_triggered_by(TermMode::INSERT, mods, &t, true)); - assert!(!binding.is_triggered_by(TermMode::ALT_SCREEN, mods, &t, true)); - assert!(!binding.is_triggered_by(TermMode::ALT_SCREEN | TermMode::INSERT, mods, &t, true)); + impl EventListener for MockEventProxy { + fn send_event(&self, _event: TerminalEvent) {} } #[derive(PartialEq)] @@ -1222,19 +837,19 @@ mod tests { None, } - struct ActionContext<'a> { - pub terminal: &'a mut Term, + struct ActionContext<'a, T> { + pub terminal: &'a mut Term, pub selection: &'a mut Option, pub size_info: &'a SizeInfo, pub mouse: &'a mut Mouse, + pub message_buffer: &'a mut MessageBuffer, pub last_action: MultiClick, pub received_count: usize, pub suppress_chars: bool, pub modifiers: Modifiers, - pub window_changes: &'a mut WindowChanges, } - impl<'a> super::ActionContext for ActionContext<'a> { + impl<'a, T: EventListener> super::ActionContext for ActionContext<'a, T> { fn write_to_pty>>(&mut self, _val: B) {} fn update_selection(&mut self, _point: Point, _side: Side) {} @@ -1247,20 +862,17 @@ mod tests { fn clear_selection(&mut self) {} - fn hide_window(&mut self) {} - fn spawn_new_instance(&mut self) {} - fn toggle_fullscreen(&mut self) {} + fn change_font_size(&mut self, _delta: f32) {} - #[cfg(target_os = "macos")] - fn toggle_simple_fullscreen(&mut self) {} + fn reset_font_size(&mut self) {} - fn terminal(&self) -> &Term { + fn terminal(&self) -> &Term { &self.terminal } - fn terminal_mut(&mut self) -> &mut Term { + fn terminal_mut(&mut self) -> &mut Term { &mut self.terminal } @@ -1286,7 +898,14 @@ mod tests { } fn mouse_coords(&self) -> Option { - self.terminal.pixels_to_coords(self.mouse.x as usize, self.mouse.y as usize) + let x = self.mouse.x as usize; + let y = self.mouse.y as usize; + + if self.size_info.contains_point(x, y, true) { + Some(self.size_info.pixels_to_coords(x, y)) + } else { + None + } } #[inline] @@ -1310,6 +929,22 @@ mod tests { fn modifiers(&mut self) -> &mut Modifiers { &mut self.modifiers } + + fn window(&self) -> &Window { + unimplemented!(); + } + + fn window_mut(&mut self) -> &mut Window { + unimplemented!(); + } + + fn pop_message(&mut self) { + self.message_buffer.pop(); + } + + fn message(&self) -> Option<&Message> { + self.message_buffer.message() + } } macro_rules! test_clickstate { @@ -1323,7 +958,18 @@ mod tests { } => { #[test] fn $name() { - let config = Config::default(); + let mut cfg = Config::default(); + cfg.ui_config.mouse = crate::config::Mouse { + double_click: ClickHandler { + threshold: Duration::from_millis(1000), + }, + triple_click: ClickHandler { + threshold: Duration::from_millis(1000), + }, + hide_when_typing: false, + url: Default::default(), + }; + let size = SizeInfo { width: 21.0, height: 51.0, @@ -1334,7 +980,7 @@ mod tests { dpr: 1.0, }; - let mut terminal = Term::new(&config, size, MessageBuffer::new(), Clipboard::new_nop()); + let mut terminal = Term::new(&cfg, &size, Clipboard::new_nop(), MockEventProxy); let mut mouse = Mouse::default(); mouse.click_state = $initial_state; @@ -1342,6 +988,8 @@ mod tests { let mut selection = None; + let mut message_buffer = MessageBuffer::new(); + let context = ActionContext { terminal: &mut terminal, selection: &mut selection, @@ -1350,28 +998,11 @@ mod tests { last_action: MultiClick::None, received_count: 0, suppress_chars: false, - modifiers: Default::default(), - window_changes: &mut WindowChanges::default(), + modifiers: Modifiers::default(), + message_buffer: &mut message_buffer, }; - let mut processor = Processor { - ctx: context, - mouse_config: &config::Mouse { - double_click: ClickHandler { - threshold: Duration::from_millis(1000), - }, - triple_click: ClickHandler { - threshold: Duration::from_millis(1000), - }, - hide_when_typing: false, - url: Default::default(), - }, - scrolling_config: &config::Scrolling::default(), - key_bindings: &config.key_bindings[..], - mouse_bindings: &config.mouse_bindings[..], - save_to_clipboard: config.selection.save_to_clipboard, - alt_send_esc: config.alt_send_esc(), - }; + let mut processor = Processor::new(context, &mut cfg); if let Event::WindowEvent { event: WindowEvent::MouseInput { state, button, modifiers, .. }, .. } = $input { processor.mouse_input(state, button, modifiers); @@ -1408,7 +1039,7 @@ mod tests { name: single_click, initial_state: ClickState::None, initial_button: MouseButton::Other(0), - input: Event::WindowEvent { + input: Event::::WindowEvent { event: WindowEvent::MouseInput { state: ElementState::Pressed, button: MouseButton::Left, @@ -1425,7 +1056,7 @@ mod tests { name: double_click, initial_state: ClickState::Click, initial_button: MouseButton::Left, - input: Event::WindowEvent { + input: Event::::WindowEvent { event: WindowEvent::MouseInput { state: ElementState::Pressed, button: MouseButton::Left, @@ -1442,7 +1073,7 @@ mod tests { name: triple_click, initial_state: ClickState::DoubleClick, initial_button: MouseButton::Left, - input: Event::WindowEvent { + input: Event::::WindowEvent { event: WindowEvent::MouseInput { state: ElementState::Pressed, button: MouseButton::Left, @@ -1459,7 +1090,7 @@ mod tests { name: multi_click_separate_buttons, initial_state: ClickState::DoubleClick, initial_button: MouseButton::Left, - input: Event::WindowEvent { + input: Event::::WindowEvent { event: WindowEvent::MouseInput { state: ElementState::Pressed, button: MouseButton::Right, diff --git a/alacritty/src/logging.rs b/alacritty/src/logging.rs index d4cb70c..d1c95e4 100644 --- a/alacritty/src/logging.rs +++ b/alacritty/src/logging.rs @@ -25,10 +25,11 @@ use std::process; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; -use crossbeam_channel::Sender; +use glutin::event_loop::EventLoopProxy; use log::{self, Level}; use time; +use alacritty_terminal::event::Event; use alacritty_terminal::message_bar::Message; use alacritty_terminal::term::color; @@ -38,7 +39,7 @@ const ALACRITTY_LOG_ENV: &str = "ALACRITTY_LOG"; pub fn initialize( options: &Options, - message_tx: Sender, + event_proxy: EventLoopProxy, ) -> Result, log::SetLoggerError> { log::set_max_level(options.log_level); @@ -48,7 +49,7 @@ pub fn initialize( ::env_logger::try_init()?; Ok(None) } else { - let logger = Logger::new(message_tx); + let logger = Logger::new(event_proxy); let path = logger.file_path(); log::set_boxed_logger(Box::new(logger))?; Ok(path) @@ -58,15 +59,15 @@ pub fn initialize( pub struct Logger { logfile: Mutex, stdout: Mutex>, - message_tx: Sender, + event_proxy: Mutex>, } impl Logger { - fn new(message_tx: Sender) -> Self { + fn new(event_proxy: EventLoopProxy) -> Self { let logfile = Mutex::new(OnDemandLogFile::new()); let stdout = Mutex::new(LineWriter::new(io::stdout())); - Logger { logfile, stdout, message_tx } + Logger { logfile, stdout, event_proxy: Mutex::new(event_proxy) } } fn file_path(&self) -> Option { @@ -122,9 +123,12 @@ impl log::Log for Logger { _ => unreachable!(), }; - let mut message = Message::new(msg, color); - message.set_topic(record.file().unwrap_or("?").into()); - let _ = self.message_tx.send(message); + if let Ok(event_proxy) = self.event_proxy.lock() { + let mut message = Message::new(msg, color); + message.set_target(record.target().to_owned()); + + let _ = event_proxy.send_event(Event::Message(message)); + } } } diff --git a/alacritty/src/main.rs b/alacritty/src/main.rs index 65313cb..146709f 100644 --- a/alacritty/src/main.rs +++ b/alacritty/src/main.rs @@ -25,7 +25,7 @@ #[cfg(target_os = "macos")] use std::env; use std::error::Error; -use std::fs::{self, File}; +use std::fs; use std::io::{self, Write}; #[cfg(not(windows))] use std::os::unix::io::AsRawFd; @@ -33,29 +33,35 @@ use std::sync::Arc; #[cfg(target_os = "macos")] use dirs; +use glutin::event_loop::EventLoop as GlutinEventLoop; use log::{error, info}; -use serde_json as json; #[cfg(windows)] use winapi::um::wincon::{AttachConsole, FreeConsole, ATTACH_PARENT_PROCESS}; use alacritty_terminal::clipboard::Clipboard; -use alacritty_terminal::config::{Config, Monitor}; -use alacritty_terminal::display::Display; +use alacritty_terminal::event::Event; use alacritty_terminal::event_loop::{self, EventLoop, Msg}; #[cfg(target_os = "macos")] use alacritty_terminal::locale; use alacritty_terminal::message_bar::MessageBuffer; use alacritty_terminal::panic; use alacritty_terminal::sync::FairMutex; -use alacritty_terminal::term::{cell::Cell, Term}; +use alacritty_terminal::term::Term; use alacritty_terminal::tty; -use alacritty_terminal::{die, event}; mod cli; mod config; +mod display; +mod event; +mod input; mod logging; +mod window; use crate::cli::Options; +use crate::config::monitor::Monitor; +use crate::config::Config; +use crate::display::Display; +use crate::event::{EventProxy, Processor}; fn main() { panic::attach_handler(); @@ -71,12 +77,12 @@ fn main() { // Load command line options let options = Options::new(); - // Setup storage for message UI - let message_buffer = MessageBuffer::new(); + // Setup glutin event loop + let window_event_loop = GlutinEventLoop::::with_user_event(); // Initialize the logger as soon as possible as to capture output from other subsystems - let log_file = - logging::initialize(&options, message_buffer.tx()).expect("Unable to initialize logger"); + let log_file = logging::initialize(&options, window_event_loop.create_proxy()) + .expect("Unable to initialize logger"); // Load configuration file // If the file is a command line argument, we won't write a generated default file @@ -107,8 +113,9 @@ fn main() { let persistent_logging = config.persistent_logging(); // Run alacritty - if let Err(err) = run(config, message_buffer) { - die!("Alacritty encountered an unrecoverable error:\n\n\t{}\n", err); + if let Err(err) = run(window_event_loop, config) { + println!("Alacritty encountered an unrecoverable error:\n\n\t{}\n", err); + std::process::exit(1); } // Clean up logfile @@ -123,7 +130,7 @@ fn main() { /// /// Creates a window, the terminal state, pty, I/O event loop, input processor, /// config change monitor, and runs the main display loop. -fn run(config: Config, message_buffer: MessageBuffer) -> Result<(), Box> { +fn run(window_event_loop: GlutinEventLoop, config: Config) -> Result<(), Box> { info!("Welcome to Alacritty"); if let Some(config_path) = &config.config_path { info!("Configuration loaded from {:?}", config_path.display()); @@ -132,17 +139,19 @@ fn run(config: Config, message_buffer: MessageBuffer) -> Result<(), Box Result<(), Box Result<(), Box> here since a ref is shared in the resize callback - let mut processor = event::Processor::new( - event_loop::Notifier(event_loop.channel()), - display.resize_channel(), - &config, - display.size().to_owned(), - ); - // Create a config monitor when config was loaded from path // // The monitor watches the config file for changes and reloads it. Pending // config changes are processed in the main loop. - let config_monitor = if config.live_config_reload() { - config.config_path.as_ref().map(|path| Monitor::new(path, display.notifier())) - } else { - None - }; + if config.live_config_reload() { + config.config_path.as_ref().map(|path| Monitor::new(path, event_proxy.clone())); + } + + // Setup storage for message UI + let message_buffer = MessageBuffer::new(); + + // Event processor + // + // Need the Rc> here since a ref is shared in the resize callback + let mut processor = Processor::new( + event_loop::Notifier(loop_tx.clone()), + Box::new(resize_handle), + message_buffer, + config, + display, + ); // Kick off the I/O thread - let _io_thread = event_loop.spawn(None); + let io_thread = event_loop.spawn(); info!("Initialisation complete"); - // Main display loop - loop { - // Process input and window events - let mut terminal_lock = processor.process_events(&terminal, display.window()); + // Start event loop and block until shutdown + processor.run(terminal, window_event_loop); - // Handle config reloads - if let Some(ref path) = config_monitor.as_ref().and_then(Monitor::pending) { - // Clear old config messages from bar - terminal_lock.message_buffer_mut().remove_topic(config::SOURCE_FILE_PATH); - - if let Ok(config) = config::reload_from(path) { - display.update_config(&config); - processor.update_config(&config); - terminal_lock.update_config(&config); - } - - terminal_lock.dirty = true; - } - - // Begin shutdown if the flag was raised - if terminal_lock.should_exit() || tty::process_should_exit() { - break; - } - - // Maybe draw the terminal - if terminal_lock.needs_draw() { - // Try to update the position of the input method editor - #[cfg(not(windows))] - display.update_ime_position(&terminal_lock); - - // Handle pending resize events - // - // The second argument is a list of types that want to be notified - // of display size changes. - display.handle_resize(&mut terminal_lock, &config, &mut resize_handle, &mut processor); - - drop(terminal_lock); - - // Draw the current state of the terminal - display.draw(&terminal, &config); - } - } - - // Write ref tests to disk - if config.debug.ref_test { - write_ref_test_results(&terminal.lock()); - } - - loop_tx.send(Msg::Shutdown).expect("Error sending shutdown to event loop"); + // Shutdown PTY parser event loop + loop_tx.send(Msg::Shutdown).expect("Error sending shutdown to pty event loop"); + io_thread.join().expect("join io thread"); // FIXME patch notify library to have a shutdown method // config_reloader.join().ok(); @@ -274,29 +242,3 @@ fn run(config: Config, message_buffer: MessageBuffer) -> Result<(), Box = ::std::result::Result; -/// A window which can be used for displaying the terminal -/// -/// Wraps the underlying windowing library to provide a stable API in Alacritty -pub struct Window { - event_loop: EventsLoop, - windowed_context: glutin::WindowedContext, - mouse_visible: bool, - - /// Keep track of the current mouse cursor to avoid unnecessarily changing it - current_mouse_cursor: MouseCursor, - - /// Whether or not the window is the focused window. - pub is_focused: bool, -} - -/// Threadsafe APIs for the window -pub struct Proxy { - inner: glutin::EventsLoopProxy, -} - -/// Information about where the window is being displayed -/// -/// Useful for subsystems like the font rasterized which depend on DPI and scale -/// factor. -pub struct DeviceProperties { - /// Scale factor for pixels <-> points. - /// - /// This will be 1. on standard displays and may have a different value on - /// hidpi displays. - pub scale_factor: f64, -} - impl ::std::error::Error for Error { fn cause(&self) -> Option<&dyn (::std::error::Error)> { match *self { Error::ContextCreation(ref err) => Some(err), Error::Context(ref err) => Some(err), + Error::Font(ref err) => Some(err), } } @@ -101,15 +71,17 @@ impl ::std::error::Error for Error { match *self { Error::ContextCreation(ref _err) => "Error creating gl context", Error::Context(ref _err) => "Error operating on render context", + Error::Font(ref err) => err.description(), } } } -impl Display for Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { Error::ContextCreation(ref err) => write!(f, "Error creating GL context; {}", err), Error::Context(ref err) => write!(f, "Error operating on render context; {}", err), + Error::Font(ref err) => err.fmt(f), } } } @@ -126,14 +98,20 @@ impl From for Error { } } +impl From for Error { + fn from(val: font::Error) -> Error { + Error::Font(val) + } +} + fn create_gl_window( mut window: WindowBuilder, - event_loop: &EventsLoop, + event_loop: &EventLoop, srgb: bool, dimensions: Option, -) -> Result> { +) -> Result> { if let Some(dimensions) = dimensions { - window = window.with_dimensions(dimensions); + window = window.with_inner_size(dimensions); } let windowed_context = ContextBuilder::new() @@ -148,25 +126,33 @@ fn create_gl_window( Ok(windowed_context) } +/// A window which can be used for displaying the terminal +/// +/// Wraps the underlying windowing library to provide a stable API in Alacritty +pub struct Window { + windowed_context: WindowedContext, + current_mouse_cursor: CursorIcon, + mouse_visible: bool, +} + impl Window { /// Create a new window /// /// This creates a window and fully initializes a window. pub fn new( - event_loop: EventsLoop, + event_loop: &EventLoop, config: &Config, - dimensions: Option, + logical: Option, ) -> Result { let title = config.window.title.as_ref().map_or(DEFAULT_NAME, |t| t); let window_builder = Window::get_platform_window(title, &config.window); let windowed_context = - create_gl_window(window_builder.clone(), &event_loop, false, dimensions) - .or_else(|_| create_gl_window(window_builder, &event_loop, true, dimensions))?; - let window = windowed_context.window(); + create_gl_window(window_builder.clone(), &event_loop, false, logical) + .or_else(|_| create_gl_window(window_builder, &event_loop, true, logical))?; // Text cursor - window.set_cursor(MouseCursor::Text); + windowed_context.window().set_cursor_icon(CursorIcon::Text); // Set OpenGL symbol loader. This call MUST be after window.make_current on windows. gl::load_with(|symbol| windowed_context.get_proc_address(symbol) as *const _); @@ -176,80 +162,33 @@ impl Window { { if event_loop.is_x11() { if let Some(parent_window_id) = config.window.embed { - x_embed_window(window, parent_window_id); + x_embed_window(windowed_context.window(), parent_window_id); } } } - let window = Window { - event_loop, - current_mouse_cursor: MouseCursor::Default, - windowed_context, + Ok(Window { + current_mouse_cursor: CursorIcon::Default, mouse_visible: true, - is_focused: false, - }; - - Ok(window) - } - - /// Get some properties about the device - /// - /// Some window properties are provided since subsystems like font - /// rasterization depend on DPI and scale factor. - pub fn device_properties(&self) -> DeviceProperties { - DeviceProperties { scale_factor: self.window().get_hidpi_factor() } - } - - pub fn inner_size_pixels(&self) -> Option { - self.window().get_inner_size() + windowed_context, + }) } pub fn set_inner_size(&mut self, size: LogicalSize) { self.window().set_inner_size(size); } - #[inline] + pub fn inner_size(&self) -> LogicalSize { + self.window().inner_size() + } + pub fn hidpi_factor(&self) -> f64 { - self.window().get_hidpi_factor() + self.window().hidpi_factor() } #[inline] - pub fn create_window_proxy(&self) -> Proxy { - Proxy { inner: self.event_loop.create_proxy() } - } - - #[inline] - pub fn swap_buffers(&self) -> Result<()> { - self.windowed_context.swap_buffers().map_err(From::from) - } - - /// Poll for any available events - #[inline] - pub fn poll_events(&mut self, func: F) - where - F: FnMut(Event), - { - self.event_loop.poll_events(func); - } - - #[inline] - pub fn resize(&self, size: PhysicalSize) { - self.windowed_context.resize(size); - } - - /// Show window - #[inline] - pub fn show(&self) { - self.window().show(); - } - - /// Block waiting for events - #[inline] - pub fn wait_events(&mut self, func: F) - where - F: FnMut(Event) -> ControlFlow, - { - self.event_loop.run_forever(func); + pub fn set_visible(&self, visibility: bool) { + self.window().set_visible(visibility); } /// Set the window title @@ -259,10 +198,10 @@ impl Window { } #[inline] - pub fn set_mouse_cursor(&mut self, cursor: MouseCursor) { + pub fn set_mouse_cursor(&mut self, cursor: CursorIcon) { if cursor != self.current_mouse_cursor { self.current_mouse_cursor = cursor; - self.window().set_cursor(cursor); + self.window().set_cursor_icon(cursor); } } @@ -270,27 +209,29 @@ impl Window { pub fn set_mouse_visible(&mut self, visible: bool) { if visible != self.mouse_visible { self.mouse_visible = visible; - self.window().hide_cursor(!visible); + self.window().set_cursor_visible(visible); } } #[cfg(not(any(target_os = "macos", windows)))] pub fn get_platform_window(title: &str, window_config: &WindowConfig) -> WindowBuilder { - use glutin::os::unix::WindowBuilderExt; - let decorations = match window_config.decorations { Decorations::None => false, _ => true, }; - let icon = Icon::from_bytes_with_format(WINDOW_ICON, ImageFormat::ICO); + let image = image::load_from_memory_with_format(WINDOW_ICON, ImageFormat::ICO) + .expect("loading icon") + .to_rgba(); + let (width, height) = image.dimensions(); + let icon = Icon::from_rgba(image.into_raw(), width, height); let class = &window_config.class; let mut builder = WindowBuilder::new() .with_title(title) - .with_visibility(false) - .with_transparency(true) + .with_visible(false) + .with_transparent(true) .with_decorations(decorations) .with_maximized(window_config.startup_mode() == StartupMode::Maximized) .with_window_icon(icon.ok()) @@ -313,25 +254,27 @@ impl Window { _ => true, }; - let icon = Icon::from_bytes_with_format(WINDOW_ICON, ImageFormat::ICO); + let image = image::load_from_memory_with_format(WINDOW_ICON, ImageFormat::ICO) + .expect("loading icon") + .to_rgba(); + let (width, height) = image.dimensions(); + let icon = Icon::from_rgba(image.into_raw(), width, height); WindowBuilder::new() .with_title(title) - .with_visibility(cfg!(windows)) + .with_visible(true) .with_decorations(decorations) - .with_transparency(true) + .with_transparent(true) .with_maximized(window_config.startup_mode() == StartupMode::Maximized) .with_window_icon(icon.ok()) } #[cfg(target_os = "macos")] pub fn get_platform_window(title: &str, window_config: &WindowConfig) -> WindowBuilder { - use glutin::os::macos::WindowBuilderExt; - let window = WindowBuilder::new() .with_title(title) - .with_visibility(false) - .with_transparency(true) + .with_visible(false) + .with_transparent(true) .with_maximized(window_config.startup_mode() == StartupMode::Maximized); match window_config.decorations { @@ -356,76 +299,103 @@ impl Window { #[cfg(target_os = "macos")] pub fn set_urgent(&self, is_urgent: bool) { - self.window().request_user_attention(is_urgent); + if !is_urgent { + return; + } + + self.window().request_user_attention(RequestUserAttentionType::Critical); } #[cfg(windows)] pub fn set_urgent(&self, _is_urgent: bool) {} - pub fn set_ime_spot(&self, pos: LogicalPosition) { - self.window().set_ime_spot(pos); + pub fn set_outer_position(&self, pos: LogicalPosition) { + self.window().set_outer_position(pos); } - pub fn set_position(&self, pos: LogicalPosition) { - self.window().set_position(pos); + #[cfg(not(any(target_os = "macos", windows)))] + pub fn x11_window_id(&self) -> Option { + self.window().xlib_window().map(|xlib_window| xlib_window as usize) } - #[cfg(not(any(target_os = "macos", target_os = "windows")))] - pub fn get_window_id(&self) -> Option { - match self.window().get_xlib_window() { - Some(xlib_window) => Some(xlib_window as usize), - None => None, - } - } - - #[cfg(not(any(target_os = "macos", target_os = "windows")))] - pub fn is_x11(&self) -> bool { - self.event_loop.is_x11() - } - - #[cfg(any(target_os = "macos", target_os = "windows"))] - pub fn get_window_id(&self) -> Option { - None - } - - /// Hide the window - pub fn hide(&self) { - self.window().hide(); - } - - /// Fullscreens the window on the current monitor. - pub fn set_fullscreen(&self, fullscreen: bool) { - let glutin_window = self.window(); - if fullscreen { - let current_monitor = glutin_window.get_current_monitor(); - glutin_window.set_fullscreen(Some(current_monitor)); - } else { - glutin_window.set_fullscreen(None); - } + pub fn window_id(&self) -> WindowId { + self.window().id() } + #[cfg(not(any(target_os = "macos", windows)))] pub fn set_maximized(&self, maximized: bool) { self.window().set_maximized(maximized); } + /// Toggle the window's fullscreen state + pub fn toggle_fullscreen(&mut self) { + self.set_fullscreen(self.window().fullscreen().is_none()); + } + #[cfg(target_os = "macos")] - pub fn set_simple_fullscreen(&self, fullscreen: bool) { - self.window().set_simple_fullscreen(fullscreen); + pub fn toggle_simple_fullscreen(&mut self) { + self.set_simple_fullscreen(!self.window().simple_fullscreen()); + } + + pub fn set_fullscreen(&mut self, fullscreen: bool) { + #[cfg(macos)] + { + if self.window().simple_fullscreen() { + return; + } + } + + if fullscreen { + let current_monitor = self.window().current_monitor(); + self.window().set_fullscreen(Some(Fullscreen::Borderless(current_monitor))); + } else { + self.window().set_fullscreen(None); + } + } + + #[cfg(target_os = "macos")] + pub fn set_simple_fullscreen(&mut self, simple_fullscreen: bool) { + if self.window().fullscreen().is_some() { + return; + } + + self.window().set_simple_fullscreen(simple_fullscreen); } #[cfg(not(any(target_os = "macos", target_os = "windows")))] - pub fn get_wayland_display(&self) -> Option<*mut c_void> { - self.window().get_wayland_display() + pub fn wayland_display(&self) -> Option<*mut c_void> { + self.window().wayland_display() } - fn window(&self) -> &glutin::Window { + /// Adjust the IME editor position according to the new location of the cursor + #[cfg(not(windows))] + pub fn update_ime_position(&mut self, terminal: &Term, size_info: &SizeInfo) { + let point = terminal.cursor().point; + let SizeInfo { cell_width: cw, cell_height: ch, padding_x: px, padding_y: py, dpr, .. } = + size_info; + + 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_position(PhysicalPosition::from((nspot_x, nspot_y)).to_logical(*dpr)); + } + + pub fn swap_buffers(&self) { + self.windowed_context.swap_buffers().expect("swap buffers"); + } + + pub fn resize(&self, size: PhysicalSize) { + self.windowed_context.resize(size); + } + + fn window(&self) -> &GlutinWindow { self.windowed_context.window() } } #[cfg(not(any(target_os = "macos", windows)))] fn x_embed_window(window: &GlutinWindow, parent_id: u64) { - let (xlib_display, xlib_window) = match (window.get_xlib_display(), window.get_xlib_window()) { + let (xlib_display, xlib_window) = match (window.xlib_display(), window.xlib_window()) { (Some(display), Some(window)) => (display, window), _ => return, }; @@ -459,15 +429,6 @@ fn x_embed_window(window: &GlutinWindow, parent_id: u64) { #[cfg(not(any(target_os = "macos", windows)))] unsafe extern "C" fn xembed_error_handler(_: *mut XDisplay, _: *mut XErrorEvent) -> i32 { - die!("Could not embed into specified window."); -} - -impl Proxy { - /// Wakes up the event loop of the window - /// - /// This is useful for triggering a draw when the renderer would otherwise - /// be waiting on user input. - pub fn wakeup_event_loop(&self) { - self.inner.wakeup().unwrap(); - } + println!("Could not embed into specified window."); + std::process::exit(1); } diff --git a/alacritty_terminal/Cargo.toml b/alacritty_terminal/Cargo.toml index 4b3b98e..949cdad 100644 --- a/alacritty_terminal/Cargo.toml +++ b/alacritty_terminal/Cargo.toml @@ -15,8 +15,7 @@ notify = "4" bitflags = "1" font = { path = "../font" } parking_lot = "0.9" -serde = "1" -serde_derive = "1" +serde = { version = "1", features = ["derive"] } serde_yaml = "0.8" vte = "0.3" mio = "0.6" @@ -24,12 +23,10 @@ mio-extras = "2" log = "0.4" fnv = "1" unicode-width = "0.1" -glutin = { version = "0.21.0", features = ["icon_loading"] } base64 = "0.10.0" static_assertions = "0.3.0" terminfo = "0.6.1" url = "2" -crossbeam-channel = "0.3.8" copypasta = { path = "../copypasta" } rfind_url = "0.4.0" @@ -37,9 +34,6 @@ rfind_url = "0.4.0" nix = "0.14.1" signal-hook = { version = "0.1", features = ["mio-support"] } -[target.'cfg(not(any(target_os = "macos", windows)))'.dependencies] -x11-dl = "2" - [target.'cfg(windows)'.dependencies] winpty = { path = "../winpty" } mio-named-pipes = "0.1" @@ -49,9 +43,6 @@ winapi = { version = "0.3.7", features = ["impl-default", "winuser", "synchapi", widestring = "0.4" mio-anonymous-pipes = "0.1" -[target.'cfg(not(target_os = "macos"))'.dependencies] -image = "0.21.0" - [target.'cfg(target_os = "macos")'.dependencies] objc = "0.2.2" diff --git a/alacritty_terminal/src/ansi.rs b/alacritty_terminal/src/ansi.rs index 3bdef32..b34c1cd 100644 --- a/alacritty_terminal/src/ansi.rs +++ b/alacritty_terminal/src/ansi.rs @@ -16,11 +16,13 @@ use std::io; use std::str; -use crate::index::{Column, Contains, Line}; use base64; -use glutin::MouseCursor; +use log::{debug, trace}; +use serde::{Deserialize, Serialize}; + use vte; +use crate::index::{Column, Contains, Line}; use crate::term::color::Rgb; // Parse colors in XParseColor format @@ -104,7 +106,7 @@ struct ProcessorState { /// Processor creates a Performer when running advance and passes the Performer /// to `vte::Parser`. struct Performer<'a, H: Handler + TermInfo, W: io::Write> { - _state: &'a mut ProcessorState, + state: &'a mut ProcessorState, handler: &'a mut H, writer: &'a mut W, } @@ -117,7 +119,7 @@ impl<'a, H: Handler + TermInfo + 'a, W: io::Write> Performer<'a, H, W> { handler: &'b mut H, writer: &'b mut W, ) -> Performer<'b, H, W> { - Performer { _state: state, handler, writer } + Performer { state, handler, writer } } } @@ -157,9 +159,6 @@ pub trait Handler { /// OSC to set window title fn set_title(&mut self, _: &str) {} - /// Set the window's mouse cursor - fn set_mouse_cursor(&mut self, _: MouseCursor) {} - /// Set the cursor style fn set_cursor_style(&mut self, _: Option) {} @@ -686,7 +685,7 @@ where #[inline] fn print(&mut self, c: char) { self.handler.input(c); - self._state.preceding_char = Some(c); + self.state.preceding_char = Some(c); } #[inline] @@ -919,7 +918,7 @@ where handler.move_up(Line(arg_or_default!(idx: 0, default: 1) as usize)); }, ('b', None) => { - if let Some(c) = self._state.preceding_char { + if let Some(c) = self.state.preceding_char { for _ in 0..arg_or_default!(idx: 0, default: 1) { handler.input(c); } diff --git a/alacritty_terminal/src/clipboard.rs b/alacritty_terminal/src/clipboard.rs index dc82648..6f6a41b 100644 --- a/alacritty_terminal/src/clipboard.rs +++ b/alacritty_terminal/src/clipboard.rs @@ -15,6 +15,8 @@ #[cfg(not(any(target_os = "macos", target_os = "windows")))] use std::ffi::c_void; +use log::{debug, warn}; + use copypasta::nop_clipboard::NopClipboardContext; #[cfg(not(any(target_os = "macos", target_os = "windows")))] use copypasta::wayland_clipboard; diff --git a/alacritty_terminal/src/config/colors.rs b/alacritty_terminal/src/config/colors.rs index a9e7a6d..35c0368 100644 --- a/alacritty_terminal/src/config/colors.rs +++ b/alacritty_terminal/src/config/colors.rs @@ -1,10 +1,11 @@ +use log::error; use serde::{Deserialize, Deserializer}; -use crate::config::failure_default; +use crate::config::{failure_default, LOG_TARGET_CONFIG}; use crate::term::color::Rgb; #[serde(default)] -#[derive(Deserialize, Debug, Default, PartialEq, Eq)] +#[derive(Deserialize, Clone, Debug, Default, PartialEq, Eq)] pub struct Colors { #[serde(deserialize_with = "failure_default")] pub primary: PrimaryColors, @@ -33,7 +34,7 @@ impl Colors { } #[serde(default)] -#[derive(Deserialize, Default, Debug, PartialEq, Eq)] +#[derive(Deserialize, Clone, Default, Debug, PartialEq, Eq)] pub struct IndexedColor { #[serde(deserialize_with = "deserialize_color_index")] pub index: u8, @@ -50,6 +51,7 @@ where Ok(index) => { if index < 16 { error!( + target: LOG_TARGET_CONFIG, "Problem with config: indexed_color's index is {}, but a value bigger than 15 \ was expected; ignoring setting", index @@ -62,7 +64,7 @@ where } }, Err(err) => { - error!("Problem with config: {}; ignoring setting", err); + error!(target: LOG_TARGET_CONFIG, "Problem with config: {}; ignoring setting", err); // Return value out of range to ignore this color Ok(0) @@ -89,7 +91,7 @@ pub struct SelectionColors { } #[serde(default)] -#[derive(Deserialize, Debug, PartialEq, Eq)] +#[derive(Deserialize, Clone, Debug, PartialEq, Eq)] pub struct PrimaryColors { #[serde(default = "default_background", deserialize_with = "failure_default")] pub background: Rgb, @@ -121,7 +123,7 @@ fn default_foreground() -> Rgb { } /// The 8-colors sections of config -#[derive(Deserialize, Debug, PartialEq, Eq)] +#[derive(Deserialize, Clone, Debug, PartialEq, Eq)] pub struct AnsiColors { #[serde(deserialize_with = "failure_default")] pub black: Rgb, @@ -141,7 +143,7 @@ pub struct AnsiColors { pub white: Rgb, } -#[derive(Deserialize, Debug, PartialEq, Eq)] +#[derive(Deserialize, Clone, Debug, PartialEq, Eq)] struct NormalColors(AnsiColors); impl Default for NormalColors { @@ -159,7 +161,7 @@ impl Default for NormalColors { } } -#[derive(Deserialize, Debug, PartialEq, Eq)] +#[derive(Deserialize, Clone, Debug, PartialEq, Eq)] struct BrightColors(AnsiColors); impl Default for BrightColors { diff --git a/alacritty_terminal/src/config/debug.rs b/alacritty_terminal/src/config/debug.rs index b7d1144..f348969 100644 --- a/alacritty_terminal/src/config/debug.rs +++ b/alacritty_terminal/src/config/debug.rs @@ -1,7 +1,7 @@ -use log::LevelFilter; -use serde::Deserializer; +use log::{error, LevelFilter}; +use serde::{Deserialize, Deserializer}; -use crate::config::failure_default; +use crate::config::{failure_default, LOG_TARGET_CONFIG}; /// Debugging options #[serde(default)] @@ -54,7 +54,10 @@ where "debug" => LevelFilter::Debug, "trace" => LevelFilter::Trace, level => { - error!("Problem with config: invalid log level {}; using level Warn", level); + error!( + target: LOG_TARGET_CONFIG, + "Problem with config: invalid log level {}; using level Warn", level + ); default_log_level() }, }) diff --git a/alacritty_terminal/src/config/font.rs b/alacritty_terminal/src/config/font.rs index 6148c98..a8a76c1 100644 --- a/alacritty_terminal/src/config/font.rs +++ b/alacritty_terminal/src/config/font.rs @@ -1,12 +1,13 @@ use std::fmt; use font::Size; +use log::error; use serde::de::Visitor; use serde::{Deserialize, Deserializer}; #[cfg(target_os = "macos")] use crate::config::DefaultTrueBool; -use crate::config::{failure_default, Delta}; +use crate::config::{failure_default, Delta, LOG_TARGET_CONFIG}; /// Font config /// @@ -202,7 +203,12 @@ impl DeserializeSize for Size { Ok(size) => Ok(size), Err(err) => { let size = default_font_size(); - error!("Problem with config: {}; using size {}", err, size.as_f32_pts()); + error!( + target: LOG_TARGET_CONFIG, + "Problem with config: {}; using size {}", + err, + size.as_f32_pts() + ); Ok(size) }, } diff --git a/alacritty_terminal/src/config/mod.rs b/alacritty_terminal/src/config/mod.rs index ac945e9..cd90037 100644 --- a/alacritty_terminal/src/config/mod.rs +++ b/alacritty_terminal/src/config/mod.rs @@ -17,42 +17,38 @@ use std::collections::HashMap; use std::fmt::Display; use std::path::PathBuf; +use log::error; +use serde::de::DeserializeOwned; use serde::{Deserialize, Deserializer}; use serde_yaml::Value; -mod bindings; mod colors; mod debug; mod font; -mod monitor; -mod mouse; mod scrolling; -#[cfg(test)] -mod test; mod visual_bell; mod window; use crate::ansi::{Color, CursorStyle, NamedColor}; -use crate::input::{Binding, KeyBinding, MouseBinding}; -pub use crate::config::bindings::Key; pub use crate::config::colors::Colors; pub use crate::config::debug::Debug; pub use crate::config::font::{Font, FontDescription}; -pub use crate::config::monitor::Monitor; -pub use crate::config::mouse::{ClickHandler, Mouse}; pub use crate::config::scrolling::Scrolling; pub use crate::config::visual_bell::{VisualBellAnimation, VisualBellConfig}; -pub use crate::config::window::{Decorations, Dimensions, StartupMode, WindowConfig}; +pub use crate::config::window::{Decorations, Dimensions, StartupMode, WindowConfig, DEFAULT_NAME}; use crate::term::color::Rgb; pub static DEFAULT_ALACRITTY_CONFIG: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../alacritty.yml")); +pub const LOG_TARGET_CONFIG: &str = "alacritty_config"; const MAX_SCROLLBACK_LINES: u32 = 100_000; +pub type MockConfig = Config>; + /// Top-level config type #[derive(Debug, PartialEq, Deserialize)] -pub struct Config { +pub struct Config { /// Pixel padding #[serde(default, deserialize_with = "failure_default")] pub padding: Option>, @@ -80,20 +76,9 @@ pub struct Config { #[serde(default, deserialize_with = "failure_default")] pub window: WindowConfig, - /// Keybindings - #[serde(default = "default_key_bindings", deserialize_with = "deserialize_key_bindings")] - pub key_bindings: Vec, - - /// Bindings for the mouse - #[serde(default = "default_mouse_bindings", deserialize_with = "deserialize_mouse_bindings")] - pub mouse_bindings: Vec, - #[serde(default, deserialize_with = "failure_default")] pub selection: Selection, - #[serde(default, deserialize_with = "failure_default")] - pub mouse: Mouse, - /// Path to a shell program to run on startup #[serde(default, deserialize_with = "from_string_or_deserialize")] pub shell: Option>, @@ -144,6 +129,10 @@ pub struct Config { #[serde(default, deserialize_with = "failure_default")] pub debug: Debug, + /// Additional configuration options not directly required by the terminal + #[serde(flatten)] + pub ui_config: T, + // TODO: DEPRECATED #[serde(default, deserialize_with = "failure_default")] pub render_timer: Option, @@ -153,13 +142,13 @@ pub struct Config { pub persistent_logging: Option, } -impl Default for Config { +impl Default for Config { fn default() -> Self { serde_yaml::from_str(DEFAULT_ALACRITTY_CONFIG).expect("default config is invalid") } } -impl Config { +impl Config { pub fn tabspaces(&self) -> usize { self.tabspaces.0 } @@ -236,49 +225,6 @@ impl Config { } } -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) -> Result, D::Error> -where - D: Deserializer<'a>, -{ - deserialize_bindings(deserializer, bindings::default_key_bindings()) -} - -fn deserialize_mouse_bindings<'a, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'a>, -{ - deserialize_bindings(deserializer, bindings::default_mouse_bindings()) -} - -fn deserialize_bindings<'a, D, T>( - deserializer: D, - mut default: Vec>, -) -> Result>, D::Error> -where - D: Deserializer<'a>, - T: Copy + Eq + std::hash::Hash + std::fmt::Debug, - Binding: Deserialize<'a>, -{ - let mut bindings: Vec> = failure_default(deserializer)?; - - // Remove matching default bindings - for binding in bindings.iter() { - default.retain(|b| !b.triggers_match(binding)); - } - - bindings.extend(default); - - Ok(bindings) -} - #[serde(default)] #[derive(Deserialize, Default, Clone, Debug, PartialEq, Eq)] pub struct Selection { @@ -324,7 +270,7 @@ impl Cursor { } } -#[derive(Debug, Deserialize, PartialEq, Eq)] +#[derive(Clone, Debug, Deserialize, PartialEq, Eq)] pub struct Shell<'a> { pub program: Cow<'a, str>, @@ -397,7 +343,7 @@ impl<'a> Deserialize<'a> for Alpha { } } -#[derive(Deserialize, Debug, PartialEq, Eq)] +#[derive(Deserialize, Copy, Clone, Debug, PartialEq, Eq)] struct Tabspaces(usize); impl Default for Tabspaces { @@ -420,7 +366,7 @@ where T: Default, E: Display, { - error!("Problem with config: {}; using default value", err); + error!(target: LOG_TARGET_CONFIG, "Problem with config: {}; using default value", err); T::default() } diff --git a/alacritty_terminal/src/config/scrolling.rs b/alacritty_terminal/src/config/scrolling.rs index d62b102..8471fcd 100644 --- a/alacritty_terminal/src/config/scrolling.rs +++ b/alacritty_terminal/src/config/scrolling.rs @@ -1,6 +1,7 @@ +use log::error; use serde::{Deserialize, Deserializer}; -use crate::config::{failure_default, MAX_SCROLLBACK_LINES}; +use crate::config::{failure_default, LOG_TARGET_CONFIG, MAX_SCROLLBACK_LINES}; /// Struct for scrolling related settings #[serde(default)] @@ -63,9 +64,11 @@ impl<'de> Deserialize<'de> for ScrollingHistory { Ok(lines) => { if lines > MAX_SCROLLBACK_LINES { error!( + target: LOG_TARGET_CONFIG, "Problem with config: scrollback size is {}, but expected a maximum of \ {}; using {1} instead", - lines, MAX_SCROLLBACK_LINES, + lines, + MAX_SCROLLBACK_LINES, ); Ok(ScrollingHistory(MAX_SCROLLBACK_LINES)) } else { @@ -73,7 +76,10 @@ impl<'de> Deserialize<'de> for ScrollingHistory { } }, Err(err) => { - error!("Problem with config: {}; using default value", err); + error!( + target: LOG_TARGET_CONFIG, + "Problem with config: {}; using default value", err + ); Ok(Default::default()) }, } diff --git a/alacritty_terminal/src/config/visual_bell.rs b/alacritty_terminal/src/config/visual_bell.rs index 3a31b24..8981c92 100644 --- a/alacritty_terminal/src/config/visual_bell.rs +++ b/alacritty_terminal/src/config/visual_bell.rs @@ -1,10 +1,12 @@ use std::time::Duration; +use serde::Deserialize; + use crate::config::failure_default; use crate::term::color::Rgb; #[serde(default)] -#[derive(Debug, Deserialize, PartialEq, Eq)] +#[derive(Deserialize, Clone, Debug, PartialEq, Eq)] pub struct VisualBellConfig { /// Visual bell animation function #[serde(deserialize_with = "failure_default")] diff --git a/alacritty_terminal/src/config/window.rs b/alacritty_terminal/src/config/window.rs index 7ca90a5..f470f93 100644 --- a/alacritty_terminal/src/config/window.rs +++ b/alacritty_terminal/src/config/window.rs @@ -1,8 +1,12 @@ +use serde::Deserialize; + use crate::config::{ failure_default, from_string_or_deserialize, option_explicit_none, Delta, FromString, }; use crate::index::{Column, Line}; -use crate::window::DEFAULT_NAME; + +/// Default Alacritty name, used for window title and class. +pub const DEFAULT_NAME: &str = "Alacritty"; #[serde(default)] #[derive(Deserialize, Debug, Clone, Default, PartialEq, Eq)] diff --git a/alacritty_terminal/src/cursor.rs b/alacritty_terminal/src/cursor.rs index 93f2bd3..d1df14e 100644 --- a/alacritty_terminal/src/cursor.rs +++ b/alacritty_terminal/src/cursor.rs @@ -16,6 +16,8 @@ use std::cmp; +use serde::Deserialize; + use font::{Metrics, RasterizedGlyph}; use crate::ansi::CursorStyle; diff --git a/alacritty_terminal/src/display.rs b/alacritty_terminal/src/display.rs deleted file mode 100644 index bdd3446..0000000 --- a/alacritty_terminal/src/display.rs +++ /dev/null @@ -1,603 +0,0 @@ -// Copyright 2016 Joe Wilm, The Alacritty Project Contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! The display subsystem including window management, font rasterization, and -//! GPU drawing. -use std::f64; -#[cfg(not(any(target_os = "macos", target_os = "windows")))] -use std::ffi::c_void; -use std::sync::mpsc; - -use glutin::dpi::{PhysicalPosition, PhysicalSize}; -use glutin::EventsLoop; -use parking_lot::MutexGuard; - -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::{self, GlyphCache, QuadRenderer}; -use crate::sync::FairMutex; -use crate::term::color::Rgb; -use crate::term::{RenderableCell, SizeInfo, Term}; -use crate::window::{self, Window}; -use font::{self, Rasterize}; - -#[derive(Debug)] -pub enum Error { - /// Error with window management - Window(window::Error), - - /// Error dealing with fonts - Font(font::Error), - - /// Error in renderer - Render(renderer::Error), -} - -impl ::std::error::Error for Error { - fn cause(&self) -> Option<&dyn (::std::error::Error)> { - match *self { - Error::Window(ref err) => Some(err), - Error::Font(ref err) => Some(err), - Error::Render(ref err) => Some(err), - } - } - - fn description(&self) -> &str { - match *self { - Error::Window(ref err) => err.description(), - Error::Font(ref err) => err.description(), - Error::Render(ref err) => err.description(), - } - } -} - -impl ::std::fmt::Display for Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { - match *self { - Error::Window(ref err) => err.fmt(f), - Error::Font(ref err) => err.fmt(f), - Error::Render(ref err) => err.fmt(f), - } - } -} - -impl From for Error { - fn from(val: window::Error) -> Error { - Error::Window(val) - } -} - -impl From for Error { - fn from(val: font::Error) -> Error { - Error::Font(val) - } -} - -impl From for Error { - fn from(val: renderer::Error) -> Error { - Error::Render(val) - } -} - -/// The display wraps a window, font rasterizer, and GPU renderer -pub struct Display { - window: Window, - renderer: QuadRenderer, - glyph_cache: GlyphCache, - render_timer: bool, - rx: mpsc::Receiver, - tx: mpsc::Sender, - meter: Meter, - font_size: font::Size, - size_info: SizeInfo, - last_message: Option, -} - -/// Can wakeup the render loop from other threads -pub struct Notifier(window::Proxy); - -/// Types that are interested in when the display is resized -pub trait OnResize { - fn on_resize(&mut self, size: &SizeInfo); -} - -impl Notifier { - pub fn notify(&self) { - self.0.wakeup_event_loop(); - } -} - -impl Display { - pub fn notifier(&self) -> Notifier { - Notifier(self.window.create_window_proxy()) - } - - pub fn update_config(&mut self, config: &Config) { - self.render_timer = config.render_timer(); - } - - /// Get size info about the display - pub fn size(&self) -> &SizeInfo { - &self.size_info - } - - pub fn new(config: &Config) -> Result { - // Extract some properties from config - let render_timer = config.render_timer(); - - // Guess DPR based on first monitor - let event_loop = EventsLoop::new(); - let estimated_dpr = - event_loop.get_available_monitors().next().map(|m| m.get_hidpi_factor()).unwrap_or(1.); - - // Guess the target window dimensions - let metrics = GlyphCache::static_metrics(config, estimated_dpr as f32)?; - let (cell_width, cell_height) = Self::compute_cell_size(config, &metrics); - let dimensions = Self::calculate_dimensions(config, estimated_dpr, cell_width, cell_height); - - debug!("Estimated DPR: {}", estimated_dpr); - debug!("Estimated Cell Size: {} x {}", cell_width, cell_height); - debug!("Estimated Dimensions: {:?}", dimensions); - - // Create the window where Alacritty will be displayed - let logical = dimensions.map(|d| PhysicalSize::new(d.0, d.1).to_logical(estimated_dpr)); - let mut window = Window::new(event_loop, &config, logical)?; - - let dpr = window.hidpi_factor(); - info!("Device pixel ratio: {}", dpr); - - // get window properties for initializing the other subsystems - let mut viewport_size = - window.inner_size_pixels().expect("glutin returns window size").to_physical(dpr); - - // Create renderer - let mut renderer = QuadRenderer::new()?; - - let (glyph_cache, cell_width, cell_height) = - Self::new_glyph_cache(dpr, &mut renderer, config)?; - - let mut padding_x = f64::from(config.window.padding.x) * dpr; - let mut padding_y = f64::from(config.window.padding.y) * dpr; - - if let Some((width, height)) = - Self::calculate_dimensions(config, dpr, cell_width, cell_height) - { - if dimensions == Some((width, height)) { - info!("Estimated DPR correctly, skipping resize"); - } else { - viewport_size = PhysicalSize::new(width, height); - window.set_inner_size(viewport_size.to_logical(dpr)); - } - } else if config.window.dynamic_padding { - // Make sure additional padding is spread evenly - let cw = f64::from(cell_width); - let ch = f64::from(cell_height); - padding_x = padding_x + (viewport_size.width - 2. * padding_x) % cw / 2.; - padding_y = padding_y + (viewport_size.height - 2. * padding_y) % ch / 2.; - } - - padding_x = padding_x.floor(); - padding_y = padding_y.floor(); - - // Update OpenGL projection - renderer.resize(viewport_size, padding_x as f32, padding_y as f32); - - info!("Cell Size: {} x {}", cell_width, cell_height); - info!("Padding: {} x {}", padding_x, padding_y); - - let size_info = SizeInfo { - dpr, - width: viewport_size.width as f32, - height: viewport_size.height as f32, - cell_width: cell_width as f32, - cell_height: cell_height as f32, - padding_x: padding_x as f32, - padding_y: padding_y as f32, - }; - - // Channel for resize events - // - // macOS has a callback for getting resize events, the channel is used - // to queue resize events until the next draw call. Unfortunately, it - // seems that the event loop is blocked until the window is done - // resizing. If any drawing were to happen during a resize, it would - // need to be in the callback. - let (tx, rx) = mpsc::channel(); - - // Clear screen - let background_color = config.colors.primary.background; - renderer.with_api(config, &size_info, |api| { - api.clear(background_color); - }); - - // We should call `clear` when window is offscreen, so when `window.show()` happens it - // would be with background color instead of uninitialized surface. - window.swap_buffers()?; - - window.show(); - - // Set window position - // - // TODO: replace `set_position` with `with_position` once available - // Upstream issue: https://github.com/tomaka/winit/issues/806 - if let Some(position) = config.window.position { - let physical = PhysicalPosition::from((position.x, position.y)); - let logical = physical.to_logical(window.hidpi_factor()); - window.set_position(logical); - } - - #[allow(clippy::single_match)] - match config.window.startup_mode() { - StartupMode::Fullscreen => window.set_fullscreen(true), - #[cfg(target_os = "macos")] - StartupMode::SimpleFullscreen => window.set_simple_fullscreen(true), - #[cfg(not(any(target_os = "macos", windows)))] - StartupMode::Maximized if window.is_x11() => window.set_maximized(true), - _ => (), - } - - Ok(Display { - window, - renderer, - glyph_cache, - render_timer, - tx, - rx, - meter: Meter::new(), - font_size: config.font.size, - size_info, - last_message: None, - }) - } - - fn calculate_dimensions( - config: &Config, - dpr: f64, - cell_width: f32, - cell_height: f32, - ) -> Option<(f64, f64)> { - let dimensions = config.window.dimensions; - - if dimensions.columns_u32() == 0 - || dimensions.lines_u32() == 0 - || config.window.startup_mode() != StartupMode::Windowed - { - return None; - } - - let padding_x = f64::from(config.window.padding.x) * dpr; - let padding_y = f64::from(config.window.padding.y) * dpr; - - // Calculate new size based on cols/lines specified in config - let grid_width = cell_width as u32 * dimensions.columns_u32(); - let grid_height = cell_height as u32 * dimensions.lines_u32(); - - let width = (f64::from(grid_width) + 2. * padding_x).floor(); - let height = (f64::from(grid_height) + 2. * padding_y).floor(); - - Some((width, height)) - } - - fn new_glyph_cache( - dpr: f64, - renderer: &mut QuadRenderer, - config: &Config, - ) -> Result<(GlyphCache, f32, f32), Error> { - let font = config.font.clone(); - let rasterizer = font::Rasterizer::new(dpr as f32, config.font.use_thin_strokes())?; - - // Initialize glyph cache - let glyph_cache = { - info!("Initializing glyph cache..."); - let init_start = ::std::time::Instant::now(); - - let cache = - renderer.with_loader(|mut api| GlyphCache::new(rasterizer, &font, &mut api))?; - - let stop = init_start.elapsed(); - let stop_f = stop.as_secs() as f64 + f64::from(stop.subsec_nanos()) / 1_000_000_000f64; - info!("... finished initializing glyph cache in {}s", stop_f); - - cache - }; - - // Need font metrics to resize the window properly. This suggests to me the - // font metrics should be computed before creating the window in the first - // place so that a resize is not needed. - let (cw, ch) = Self::compute_cell_size(config, &glyph_cache.font_metrics()); - - Ok((glyph_cache, cw, ch)) - } - - pub fn update_glyph_cache(&mut self, config: &Config) { - let cache = &mut self.glyph_cache; - let dpr = self.size_info.dpr; - let size = self.font_size; - - self.renderer.with_loader(|mut api| { - let _ = cache.update_font_size(&config.font, size, dpr, &mut api); - }); - - let (cw, ch) = Self::compute_cell_size(config, &cache.font_metrics()); - self.size_info.cell_width = cw; - self.size_info.cell_height = ch; - } - - fn compute_cell_size(config: &Config, metrics: &font::Metrics) -> (f32, f32) { - let offset_x = f64::from(config.font.offset.x); - let offset_y = f64::from(config.font.offset.y); - ( - f32::max(1., ((metrics.average_advance + offset_x) as f32).floor()), - f32::max(1., ((metrics.line_height + offset_y) as f32).floor()), - ) - } - - #[inline] - pub fn resize_channel(&self) -> mpsc::Sender { - self.tx.clone() - } - - pub fn window(&mut self) -> &mut Window { - &mut self.window - } - - /// Process pending resize events - pub fn handle_resize( - &mut self, - terminal: &mut MutexGuard<'_, Term>, - config: &Config, - pty_resize_handle: &mut dyn OnResize, - processor_resize_handle: &mut dyn OnResize, - ) { - let previous_cols = self.size_info.cols(); - let previous_lines = self.size_info.lines(); - - // Resize events new_size and are handled outside the poll_events - // iterator. This has the effect of coalescing multiple resize - // events into one. - let mut new_size = None; - - // Take most recent resize event, if any - while let Ok(size) = self.rx.try_recv() { - new_size = Some(size); - } - - // Update the DPR - let dpr = self.window.hidpi_factor(); - - // Font size/DPI factor modification detected - let font_changed = - terminal.font_size != self.font_size || (dpr - self.size_info.dpr).abs() > f64::EPSILON; - - // Skip resize if nothing changed - if let Some(new_size) = new_size { - if !font_changed - && (new_size.width - f64::from(self.size_info.width)).abs() < f64::EPSILON - && (new_size.height - f64::from(self.size_info.height)).abs() < f64::EPSILON - { - return; - } - } - - // Message bar update detected - let message_bar_changed = self.last_message != terminal.message_buffer_mut().message(); - - if font_changed || message_bar_changed { - if new_size == None { - // Force a resize to refresh things - new_size = Some(PhysicalSize::new( - f64::from(self.size_info.width) / self.size_info.dpr * dpr, - f64::from(self.size_info.height) / self.size_info.dpr * dpr, - )); - } - - self.font_size = terminal.font_size; - self.last_message = terminal.message_buffer_mut().message(); - self.size_info.dpr = dpr; - } - - if font_changed { - self.update_glyph_cache(config); - } - - if let Some(psize) = new_size.take() { - let width = psize.width as f32; - let height = psize.height as f32; - let cell_width = self.size_info.cell_width; - let cell_height = self.size_info.cell_height; - - self.size_info.width = width; - self.size_info.height = height; - - let mut padding_x = f32::from(config.window.padding.x) * dpr as f32; - let mut padding_y = f32::from(config.window.padding.y) * dpr as f32; - - if config.window.dynamic_padding { - padding_x = padding_x + ((width - 2. * padding_x) % cell_width) / 2.; - padding_y = padding_y + ((height - 2. * padding_y) % cell_height) / 2.; - } - - self.size_info.padding_x = padding_x.floor(); - self.size_info.padding_y = padding_y.floor(); - - let size = &self.size_info; - terminal.resize(size); - processor_resize_handle.on_resize(size); - - // Subtract message bar lines for pty size - let mut pty_size = *size; - if let Some(message) = terminal.message_buffer_mut().message() { - pty_size.height -= pty_size.cell_height * message.text(&size).len() as f32; - } - - if message_bar_changed - || previous_cols != pty_size.cols() - || previous_lines != pty_size.lines() - { - pty_resize_handle.on_resize(&pty_size); - } - - self.window.resize(psize); - self.renderer.resize(psize, self.size_info.padding_x, self.size_info.padding_y); - } - } - - /// Draw the screen - /// - /// A reference to Term whose state is being drawn must be provided. - /// - /// This call may block if vsync is enabled - pub fn draw(&mut self, terminal: &FairMutex, config: &Config) { - let mut terminal = terminal.lock(); - let size_info = *terminal.size_info(); - let visual_bell_intensity = terminal.visual_bell.intensity(); - let background_color = terminal.background_color(); - 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(); - - // Get message from terminal to ignore modifications after lock is dropped - let message_buffer = terminal.message_buffer_mut().message(); - - // Clear dirty flag - terminal.dirty = !terminal.visual_bell.completed(); - - if let Some(title) = terminal.get_next_title() { - self.window.set_title(&title); - } - - if let Some(mouse_cursor) = terminal.get_next_mouse_cursor() { - self.window.set_mouse_cursor(mouse_cursor); - } - - if let Some(is_urgent) = terminal.next_is_urgent.take() { - // We don't need to set the urgent flag if we already have the - // user's attention. - if !is_urgent || !self.window.is_focused { - self.window.set_urgent(is_urgent); - } - } - - // 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() - // which blocks for a while. - // - // By keeping this outside of the critical region, the Mesa bug is - // worked around to some extent. Since this doesn't actually address the - // issue of glClear being slow, less time is available for input - // handling and rendering. - drop(terminal); - - self.renderer.with_api(config, &size_info, |api| { - api.clear(background_color); - }); - - { - let glyph_cache = &mut self.glyph_cache; - let mut lines = RenderLines::new(); - - // Draw grid - { - let _sampler = self.meter.sampler(); - - self.renderer.with_api(config, &size_info, |mut api| { - // Iterate over all non-empty cells in the grid - for cell in grid_cells { - // Update underline/strikeout - lines.update(&cell); - - // Draw the cell - api.render_cell(cell, glyph_cache); - } - }); - } - - let mut rects = lines.into_rects(&metrics, &size_info); - - if let Some(message) = message_buffer { - let text = message.text(&size_info); - - // Create a new rectangle for the background - let start_line = size_info.lines().0 - text.len(); - let y = size_info.padding_y + size_info.cell_height * start_line as f32; - rects.push(RenderRect::new( - 0., - y, - size_info.width, - size_info.height - y, - message.color(), - )); - - // Draw rectangles including the new background - self.renderer.draw_rects(config, &size_info, visual_bell_intensity, rects); - - // Relay messages to the user - let mut offset = 1; - for message_text in text.iter().rev() { - self.renderer.with_api(config, &size_info, |mut api| { - api.render_string( - &message_text, - Line(size_info.lines().saturating_sub(offset)), - glyph_cache, - None, - ); - }); - offset += 1; - } - } else { - // Draw rectangles - self.renderer.draw_rects(config, &size_info, visual_bell_intensity, rects); - } - - // Draw render timer - if self.render_timer { - let timing = format!("{:.3} usec", self.meter.average()); - let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 }; - self.renderer.with_api(config, &size_info, |mut api| { - api.render_string(&timing[..], size_info.lines() - 2, glyph_cache, Some(color)); - }); - } - } - - self.window.swap_buffers().expect("swap buffers"); - } - - pub fn get_window_id(&self) -> Option { - self.window.get_window_id() - } - - /// Adjust the IME editor position according to the new location of the cursor - pub fn update_ime_position(&mut self, terminal: &Term) { - let point = terminal.cursor().point; - let SizeInfo { cell_width: cw, cell_height: ch, padding_x: px, padding_y: py, .. } = - *terminal.size_info(); - - let dpr = self.window().hidpi_factor(); - 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)); - } - - #[cfg(not(any(target_os = "macos", target_os = "windows")))] - pub fn get_wayland_display(&self) -> Option<*mut c_void> { - self.window.get_wayland_display() - } -} diff --git a/alacritty_terminal/src/event.rs b/alacritty_terminal/src/event.rs index 9d2aa78..2d43e9d 100644 --- a/alacritty_terminal/src/event.rs +++ b/alacritty_terminal/src/event.rs @@ -1,28 +1,19 @@ -//! Process window events use std::borrow::Cow; -use std::env; -#[cfg(unix)] -use std::fs; -use std::sync::mpsc; -use std::time::Instant; +use std::path::PathBuf; -use glutin::dpi::PhysicalSize; -use glutin::{self, ElementState, Event, MouseButton}; -use parking_lot::MutexGuard; +use crate::message_bar::Message; +use crate::term::SizeInfo; -use crate::clipboard::ClipboardType; -use crate::config::{self, Config, StartupMode}; -use crate::display::OnResize; -use crate::grid::Scroll; -use crate::index::{Column, Line, Point, Side}; -use crate::input::{self, KeyBinding, Modifiers, MouseBinding}; -use crate::selection::Selection; -use crate::sync::FairMutex; -use crate::term::{SizeInfo, Term}; -#[cfg(unix)] -use crate::tty; -use crate::util::{limit, start_daemon}; -use crate::window::Window; +#[derive(Clone, Debug, PartialEq)] +pub enum Event { + ConfigReload(PathBuf), + MouseCursorDirty, + Message(Message), + Title(String), + Wakeup, + Urgent, + Exit, +} /// Byte sequences are sent to a `Notify` in response to some events pub trait Notify { @@ -32,526 +23,12 @@ pub trait Notify { fn notify>>(&mut self, _: B); } -pub struct ActionContext<'a, N> { - pub notifier: &'a mut N, - pub terminal: &'a mut Term, - pub size_info: &'a mut SizeInfo, - pub mouse: &'a mut Mouse, - pub received_count: &'a mut usize, - pub suppress_chars: &'a mut bool, - pub modifiers: &'a mut Modifiers, - pub window_changes: &'a mut WindowChanges, +/// Types that are interested in when the display is resized +pub trait OnResize { + fn on_resize(&mut self, size: &SizeInfo); } -impl<'a, N: Notify + 'a> input::ActionContext for ActionContext<'a, N> { - fn write_to_pty>>(&mut self, val: B) { - self.notifier.notify(val); - } - - fn size_info(&self) -> SizeInfo { - *self.size_info - } - - fn scroll(&mut self, scroll: Scroll) { - self.terminal.scroll_display(scroll); - - if let ElementState::Pressed = self.mouse().left_button_state { - let (x, y) = (self.mouse().x, self.mouse().y); - let size_info = self.size_info(); - let point = size_info.pixels_to_coords(x, y); - let cell_side = self.mouse().cell_side; - self.update_selection(Point { line: point.line, col: point.col }, cell_side); - } - } - - fn copy_selection(&mut self, ty: ClipboardType) { - if let Some(selected) = self.terminal.selection_to_string() { - if !selected.is_empty() { - self.terminal.clipboard().store(ty, selected); - } - } - } - - fn selection_is_empty(&self) -> bool { - self.terminal.selection().as_ref().map(Selection::is_empty).unwrap_or(true) - } - - fn clear_selection(&mut self) { - *self.terminal.selection_mut() = None; - self.terminal.dirty = true; - } - - fn update_selection(&mut self, point: Point, side: Side) { - let point = self.terminal.visible_to_buffer(point); - - // Update selection if one exists - if let Some(ref mut selection) = self.terminal.selection_mut() { - selection.update(point, side); - } - - self.terminal.dirty = true; - } - - fn simple_selection(&mut self, point: Point, side: Side) { - let point = self.terminal.visible_to_buffer(point); - *self.terminal.selection_mut() = Some(Selection::simple(point, side)); - self.terminal.dirty = true; - } - - fn block_selection(&mut self, point: Point, side: Side) { - let point = self.terminal.visible_to_buffer(point); - *self.terminal.selection_mut() = Some(Selection::block(point, side)); - self.terminal.dirty = true; - } - - fn semantic_selection(&mut self, point: Point) { - let point = self.terminal.visible_to_buffer(point); - *self.terminal.selection_mut() = Some(Selection::semantic(point)); - self.terminal.dirty = true; - } - - fn line_selection(&mut self, point: Point) { - let point = self.terminal.visible_to_buffer(point); - *self.terminal.selection_mut() = Some(Selection::lines(point)); - self.terminal.dirty = true; - } - - fn mouse_coords(&self) -> Option { - self.terminal.pixels_to_coords(self.mouse.x as usize, self.mouse.y as usize) - } - - #[inline] - fn mouse_mut(&mut self) -> &mut Mouse { - self.mouse - } - - #[inline] - fn mouse(&self) -> &Mouse { - self.mouse - } - - #[inline] - fn received_count(&mut self) -> &mut usize { - &mut self.received_count - } - - #[inline] - fn suppress_chars(&mut self) -> &mut bool { - &mut self.suppress_chars - } - - #[inline] - fn modifiers(&mut self) -> &mut Modifiers { - &mut self.modifiers - } - - #[inline] - fn hide_window(&mut self) { - self.window_changes.hide = true; - } - - #[inline] - fn terminal(&self) -> &Term { - self.terminal - } - - #[inline] - fn terminal_mut(&mut self) -> &mut Term { - self.terminal - } - - fn spawn_new_instance(&mut self) { - let alacritty = env::args().next().unwrap(); - - #[cfg(unix)] - let args = { - #[cfg(not(target_os = "freebsd"))] - let proc_prefix = ""; - #[cfg(target_os = "freebsd")] - let proc_prefix = "/compat/linux"; - let link_path = format!("{}/proc/{}/cwd", proc_prefix, tty::child_pid()); - if let Ok(path) = fs::read_link(link_path) { - vec!["--working-directory".into(), path] - } else { - Vec::new() - } - }; - #[cfg(not(unix))] - let args: Vec = Vec::new(); - - match start_daemon(&alacritty, &args) { - Ok(_) => debug!("Started new Alacritty process: {} {:?}", alacritty, args), - Err(_) => warn!("Unable to start new Alacritty process: {} {:?}", alacritty, args), - } - } - - fn toggle_fullscreen(&mut self) { - self.window_changes.toggle_fullscreen(); - } - - #[cfg(target_os = "macos")] - fn toggle_simple_fullscreen(&mut self) { - self.window_changes.toggle_simple_fullscreen() - } -} - -/// The ActionContext can't really have direct access to the Window -/// with the current design. Event handlers that want to change the -/// window must set these flags instead. The processor will trigger -/// the actual changes. -#[derive(Default)] -pub struct WindowChanges { - pub hide: bool, - pub toggle_fullscreen: bool, - #[cfg(target_os = "macos")] - pub toggle_simple_fullscreen: bool, -} - -impl WindowChanges { - fn clear(&mut self) { - *self = WindowChanges::default(); - } - - fn toggle_fullscreen(&mut self) { - self.toggle_fullscreen = !self.toggle_fullscreen; - } - - #[cfg(target_os = "macos")] - fn toggle_simple_fullscreen(&mut self) { - self.toggle_simple_fullscreen = !self.toggle_simple_fullscreen; - } -} - -pub enum ClickState { - None, - Click, - DoubleClick, - TripleClick, -} - -/// State of the mouse -pub struct Mouse { - pub x: usize, - pub y: usize, - pub left_button_state: ElementState, - pub middle_button_state: ElementState, - pub right_button_state: ElementState, - pub last_click_timestamp: Instant, - pub click_state: ClickState, - pub scroll_px: i32, - pub line: Line, - pub column: Column, - pub cell_side: Side, - pub lines_scrolled: f32, - pub block_url_launcher: bool, - pub last_button: MouseButton, -} - -impl Default for Mouse { - fn default() -> Mouse { - Mouse { - x: 0, - y: 0, - last_click_timestamp: Instant::now(), - left_button_state: ElementState::Released, - middle_button_state: ElementState::Released, - right_button_state: ElementState::Released, - click_state: ClickState::None, - scroll_px: 0, - line: Line(0), - column: Column(0), - cell_side: Side::Left, - lines_scrolled: 0.0, - block_url_launcher: false, - last_button: MouseButton::Other(0), - } - } -} - -/// The event processor -/// -/// Stores some state from received events and dispatches actions when they are -/// triggered. -pub struct Processor { - key_bindings: Vec, - mouse_bindings: Vec, - mouse_config: config::Mouse, - scrolling_config: config::Scrolling, - print_events: bool, - wait_for_event: bool, - notifier: N, - mouse: Mouse, - resize_tx: mpsc::Sender, - size_info: SizeInfo, - hide_mouse_when_typing: bool, - hide_mouse: bool, - received_count: usize, - suppress_chars: bool, - modifiers: Modifiers, - pending_events: Vec, - window_changes: WindowChanges, - save_to_clipboard: bool, - alt_send_esc: bool, - is_fullscreen: bool, - is_simple_fullscreen: bool, -} - -/// Notify that the terminal was resized -/// -/// Currently this just forwards the notice to the input processor. -impl OnResize for Processor { - fn on_resize(&mut self, size: &SizeInfo) { - self.size_info = size.to_owned(); - } -} - -impl Processor { - /// Create a new event processor - /// - /// Takes a writer which is expected to be hooked up to the write end of a - /// pty. - pub fn new( - notifier: N, - resize_tx: mpsc::Sender, - config: &Config, - size_info: SizeInfo, - ) -> Processor { - Processor { - key_bindings: config.key_bindings.to_vec(), - mouse_bindings: config.mouse_bindings.to_vec(), - mouse_config: config.mouse.to_owned(), - scrolling_config: config.scrolling, - print_events: config.debug.print_events, - wait_for_event: true, - notifier, - resize_tx, - mouse: Default::default(), - size_info, - hide_mouse_when_typing: config.mouse.hide_when_typing, - hide_mouse: false, - received_count: 0, - suppress_chars: false, - modifiers: Default::default(), - pending_events: Vec::with_capacity(4), - window_changes: Default::default(), - save_to_clipboard: config.selection.save_to_clipboard, - alt_send_esc: config.alt_send_esc(), - is_fullscreen: config.window.startup_mode() == StartupMode::Fullscreen, - #[cfg(target_os = "macos")] - is_simple_fullscreen: config.window.startup_mode() == StartupMode::SimpleFullscreen, - #[cfg(not(target_os = "macos"))] - is_simple_fullscreen: false, - } - } - - /// Handle events from glutin - /// - /// Doesn't take self mutably due to borrow checking. Kinda uggo but w/e. - fn handle_event<'a>( - processor: &mut input::Processor<'a, ActionContext<'a, N>>, - event: Event, - resize_tx: &mpsc::Sender, - hide_mouse: &mut bool, - window_is_focused: &mut bool, - ) { - match event { - // Pass on device events - Event::DeviceEvent { .. } | Event::Suspended { .. } => (), - Event::WindowEvent { event, .. } => { - use glutin::WindowEvent::*; - match event { - CloseRequested => processor.ctx.terminal.exit(), - Resized(lsize) => { - // Resize events are emitted via glutin/winit with logical sizes - // However the terminal, window and renderer use physical sizes - // so a conversion must be done here - resize_tx - .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, .. } => { - 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, .. } => { - 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, .. } => { - *hide_mouse = false; - processor.on_mouse_wheel(delta, phase, modifiers); - }, - Refresh => { - processor.ctx.terminal.dirty = true; - }, - Focused(is_focused) => { - *window_is_focused = is_focused; - - if is_focused { - processor.ctx.terminal.next_is_urgent = Some(false); - processor.ctx.terminal.dirty = true; - } else { - processor.ctx.terminal.dirty = true; - *hide_mouse = false; - } - - 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; - }, - CursorLeft { .. } => processor.ctx.terminal.reset_url_highlight(), - _ => (), - } - }, - Event::Awakened => { - processor.ctx.terminal.dirty = true; - }, - } - } - - /// Process events. When `wait_for_event` is set, this method is guaranteed - /// to process at least one event. - pub fn process_events<'a>( - &mut self, - term: &'a FairMutex, - window: &mut Window, - ) -> MutexGuard<'a, Term> { - // Terminal is lazily initialized the first time an event is returned - // from the blocking WaitEventsIterator. Otherwise, the pty reader would - // be blocked the entire time we wait for input! - let mut terminal; - - self.pending_events.clear(); - - { - // Ditto on lazy initialization for context and processor. - let context; - let mut processor: input::Processor<'_, ActionContext<'_, N>>; - - let print_events = self.print_events; - - let resize_tx = &self.resize_tx; - - if self.wait_for_event { - // A Vec is used here since wait_events can potentially yield - // multiple events before the interrupt is handled. For example, - // Resize and Moved events. - let pending_events = &mut self.pending_events; - window.wait_events(|e| { - pending_events.push(e); - glutin::ControlFlow::Break - }); - } - - terminal = term.lock(); - - context = ActionContext { - terminal: &mut terminal, - notifier: &mut self.notifier, - mouse: &mut self.mouse, - size_info: &mut self.size_info, - received_count: &mut self.received_count, - suppress_chars: &mut self.suppress_chars, - modifiers: &mut self.modifiers, - window_changes: &mut self.window_changes, - }; - - processor = input::Processor { - ctx: context, - scrolling_config: &self.scrolling_config, - mouse_config: &self.mouse_config, - key_bindings: &self.key_bindings[..], - mouse_bindings: &self.mouse_bindings[..], - save_to_clipboard: self.save_to_clipboard, - alt_send_esc: self.alt_send_esc, - }; - - let mut window_is_focused = window.is_focused; - - // Scope needed to that hide_mouse isn't borrowed after the scope - // ends. - { - let hide_mouse = &mut self.hide_mouse; - let mut process = |event| { - if print_events { - info!("glutin event: {:?}", event); - } - Processor::handle_event( - &mut processor, - event, - resize_tx, - hide_mouse, - &mut window_is_focused, - ); - }; - - for event in self.pending_events.drain(..) { - process(event); - } - - window.poll_events(process); - } - - if self.hide_mouse_when_typing { - window.set_mouse_visible(!self.hide_mouse); - } - - window.is_focused = window_is_focused; - } - - if self.window_changes.hide { - window.hide(); - } - - #[cfg(target_os = "macos")] - { - if self.window_changes.toggle_simple_fullscreen && !self.is_fullscreen { - window.set_simple_fullscreen(!self.is_simple_fullscreen); - self.is_simple_fullscreen = !self.is_simple_fullscreen; - } - } - - if self.window_changes.toggle_fullscreen && !self.is_simple_fullscreen { - window.set_fullscreen(!self.is_fullscreen); - self.is_fullscreen = !self.is_fullscreen; - } - - self.window_changes.clear(); - self.wait_for_event = !terminal.dirty; - - terminal - } - - pub fn update_config(&mut self, config: &Config) { - self.key_bindings = config.key_bindings.to_vec(); - self.mouse_bindings = config.mouse_bindings.to_vec(); - self.mouse_config = config.mouse.to_owned(); - self.save_to_clipboard = config.selection.save_to_clipboard; - self.alt_send_esc = config.alt_send_esc(); - } +/// Event Loop for notifying the renderer about terminal events +pub trait EventListener { + fn send_event(&self, event: Event); } diff --git a/alacritty_terminal/src/event_loop.rs b/alacritty_terminal/src/event_loop.rs index 275d752..ed78d79 100644 --- a/alacritty_terminal/src/event_loop.rs +++ b/alacritty_terminal/src/event_loop.rs @@ -6,20 +6,22 @@ use std::io::{self, ErrorKind, Read, Write}; use std::marker::Send; use std::sync::Arc; +use log::error; +#[cfg(not(windows))] +use mio::unix::UnixReady; use mio::{self, Events, PollOpt, Ready}; use mio_extras::channel::{self, Receiver, Sender}; -#[cfg(not(windows))] -use mio::unix::UnixReady; - use crate::ansi; -use crate::display; -use crate::event; +use crate::event::{self, Event, EventListener}; use crate::sync::FairMutex; use crate::term::Term; use crate::tty; use crate::util::thread; +/// Max bytes to read from the PTY +const MAX_READ: usize = 0x10_000; + /// Messages that may be sent to the `EventLoop` #[derive(Debug)] pub enum Msg { @@ -34,13 +36,13 @@ pub enum Msg { /// /// Handles all the pty I/O and runs the pty parser which updates terminal /// state. -pub struct EventLoop { +pub struct EventLoop { poll: mio::Poll, pty: T, rx: Receiver, tx: Sender, - terminal: Arc>, - display: display::Notifier, + terminal: Arc>>, + event_proxy: U, ref_test: bool, } @@ -50,26 +52,6 @@ struct Writing { written: usize, } -/// Indicates the result of draining the mio channel -#[derive(Debug)] -enum DrainResult { - /// At least one new item was received - ReceivedItem, - /// Nothing was available to receive - Empty, - /// A shutdown message was received - Shutdown, -} - -impl DrainResult { - pub fn is_shutdown(&self) -> bool { - match *self { - DrainResult::Shutdown => true, - _ => false, - } - } -} - /// All of the mutable state needed to run the event loop /// /// Contains list of items to write, current write state, etc. Anything that @@ -155,17 +137,18 @@ impl Writing { } } -impl EventLoop +impl EventLoop where T: tty::EventedPty + Send + 'static, + U: EventListener + Send + 'static, { /// Create a new event loop pub fn new( - terminal: Arc>, - display: display::Notifier, + terminal: Arc>>, + event_proxy: U, pty: T, ref_test: bool, - ) -> EventLoop { + ) -> EventLoop { let (tx, rx) = channel::channel(); EventLoop { poll: mio::Poll::new().expect("create mio Poll"), @@ -173,7 +156,7 @@ where tx, rx, terminal, - display, + event_proxy, ref_test, } } @@ -184,33 +167,22 @@ where // Drain the channel // - // Returns a `DrainResult` indicating the result of receiving from the channel - // - fn drain_recv_channel(&self, state: &mut State) -> DrainResult { - let mut received_item = false; + // Returns `false` when a shutdown message was received. + fn drain_recv_channel(&self, state: &mut State) -> bool { while let Ok(msg) = self.rx.try_recv() { - received_item = true; match msg { - Msg::Input(input) => { - state.write_list.push_back(input); - }, - Msg::Shutdown => { - return DrainResult::Shutdown; - }, + Msg::Input(input) => state.write_list.push_back(input), + Msg::Shutdown => return false, } } - if received_item { - DrainResult::ReceivedItem - } else { - DrainResult::Empty - } + true } // Returns a `bool` indicating whether or not the event loop should continue running #[inline] fn channel_event(&mut self, token: mio::Token, state: &mut State) -> bool { - if self.drain_recv_channel(state).is_shutdown() { + if !self.drain_recv_channel(state) { return false; } @@ -231,13 +203,9 @@ where where X: Write, { - const MAX_READ: usize = 0x1_0000; let mut processed = 0; let mut terminal = None; - // Flag to keep track if wakeup has already been sent - let mut send_wakeup = false; - loop { match self.pty.reader().read(&mut buf[..]) { Ok(0) => break, @@ -255,14 +223,10 @@ where // Get reference to terminal. Lock is acquired on initial // iteration and held until there's no bytes left to parse // or we've reached MAX_READ. - let terminal = if terminal.is_none() { + if terminal.is_none() { terminal = Some(self.terminal.lock()); - let terminal = terminal.as_mut().unwrap(); - send_wakeup = !terminal.dirty; - terminal - } else { - terminal.as_mut().unwrap() - }; + } + let terminal = terminal.as_mut().unwrap(); // Run the parser for byte in &buf[..got] { @@ -283,13 +247,8 @@ where } } - // Only request a draw if one hasn't already been requested. - if let Some(mut terminal) = terminal { - if send_wakeup { - self.display.notify(); - terminal.dirty = true; - } - } + // Queue terminal redraw + self.event_proxy.send_event(Event::Wakeup); Ok(()) } @@ -326,10 +285,10 @@ where Ok(()) } - pub fn spawn(mut self, state: Option) -> thread::JoinHandle<(Self, State)> { + pub fn spawn(mut self) -> thread::JoinHandle<(Self, State)> { thread::spawn_named("pty reader", move || { - let mut state = state.unwrap_or_else(Default::default); - let mut buf = [0u8; 0x1000]; + let mut state = State::default(); + let mut buf = [0u8; MAX_READ]; let mut tokens = (0..).map(Into::into); @@ -369,7 +328,7 @@ where token if token == self.pty.child_event_token() => { if let Some(tty::ChildEvent::Exited) = self.pty.next_child_event() { self.terminal.lock().exit(); - self.display.notify(); + self.event_proxy.send_event(Event::Wakeup); break 'event_loop; } }, diff --git a/alacritty_terminal/src/grid/mod.rs b/alacritty_terminal/src/grid/mod.rs index 01c1715..5f5b84f 100644 --- a/alacritty_terminal/src/grid/mod.rs +++ b/alacritty_terminal/src/grid/mod.rs @@ -17,6 +17,8 @@ use std::cmp::{max, min, Ordering}; use std::ops::{Deref, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo}; +use serde::{Deserialize, Serialize}; + use crate::index::{self, Column, IndexRange, Line, Point}; use crate::selection::Selection; diff --git a/alacritty_terminal/src/grid/row.rs b/alacritty_terminal/src/grid/row.rs index f82e7ea..058583f 100644 --- a/alacritty_terminal/src/grid/row.rs +++ b/alacritty_terminal/src/grid/row.rs @@ -19,6 +19,8 @@ use std::ops::{Index, IndexMut}; use std::ops::{Range, RangeFrom, RangeFull, RangeTo, RangeToInclusive}; use std::slice; +use serde::{Deserialize, Serialize}; + use crate::grid::GridCell; use crate::index::Column; diff --git a/alacritty_terminal/src/grid/storage.rs b/alacritty_terminal/src/grid/storage.rs index e04d01c..2004cec 100644 --- a/alacritty_terminal/src/grid/storage.rs +++ b/alacritty_terminal/src/grid/storage.rs @@ -14,6 +14,7 @@ use std::ops::{Index, IndexMut}; use std::vec::Drain; +use serde::{Deserialize, Serialize}; use static_assertions::assert_eq_size; use super::Row; diff --git a/alacritty_terminal/src/index.rs b/alacritty_terminal/src/index.rs index 7d1a8e9..0a100e1 100644 --- a/alacritty_terminal/src/index.rs +++ b/alacritty_terminal/src/index.rs @@ -19,6 +19,8 @@ use std::cmp::{Ord, Ordering}; use std::fmt; use std::ops::{self, Add, AddAssign, Deref, Range, RangeInclusive, Sub, SubAssign}; +use serde::{Deserialize, Serialize}; + use crate::term::RenderableCell; /// The side of a cell @@ -77,8 +79,8 @@ impl From for Point { } } -impl From<&RenderableCell> for Point { - fn from(cell: &RenderableCell) -> Self { +impl From for Point { + fn from(cell: RenderableCell) -> Self { Point::new(cell.line, cell.column) } } diff --git a/alacritty_terminal/src/lib.rs b/alacritty_terminal/src/lib.rs index 0bada53..dc26046 100644 --- a/alacritty_terminal/src/lib.rs +++ b/alacritty_terminal/src/lib.rs @@ -17,27 +17,18 @@ #![cfg_attr(feature = "nightly", feature(core_intrinsics))] #![cfg_attr(all(test, feature = "bench"), feature(test))] -#[macro_use] -extern crate log; -#[macro_use] -extern crate serde_derive; - #[cfg(target_os = "macos")] #[macro_use] extern crate objc; -#[macro_use] -pub mod macros; pub mod ansi; pub mod clipboard; pub mod config; mod cursor; -pub mod display; pub mod event; pub mod event_loop; pub mod grid; pub mod index; -pub mod input; pub mod locale; pub mod message_bar; pub mod meter; @@ -47,9 +38,8 @@ pub mod selection; pub mod sync; pub mod term; pub mod tty; -mod url; +pub mod url; pub mod util; -pub mod window; pub use crate::grid::Grid; pub use crate::term::Term; diff --git a/alacritty_terminal/src/macros.rs b/alacritty_terminal/src/macros.rs deleted file mode 100644 index 519f8b6..0000000 --- a/alacritty_terminal/src/macros.rs +++ /dev/null @@ -1,21 +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. - -#[macro_export] -macro_rules! die { - ($($arg:tt)*) => {{ - error!($($arg)*); - ::std::process::exit(1); - }} -} diff --git a/alacritty_terminal/src/message_bar.rs b/alacritty_terminal/src/message_bar.rs index 8883dcb..1382684 100644 --- a/alacritty_terminal/src/message_bar.rs +++ b/alacritty_terminal/src/message_bar.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crossbeam_channel::{Receiver, Sender}; +use std::collections::VecDeque; use crate::term::color::Rgb; use crate::term::SizeInfo; @@ -22,21 +22,21 @@ const CLOSE_BUTTON_PADDING: usize = 1; const MIN_FREE_LINES: usize = 3; const TRUNCATED_MESSAGE: &str = "[MESSAGE TRUNCATED]"; -/// Message for display in the MessageBuffer +/// Message for display in the MessageBuffer. #[derive(Debug, Eq, PartialEq, Clone)] pub struct Message { text: String, color: Rgb, - topic: Option, + target: Option, } impl Message { - /// Create a new message + /// Create a new message. pub fn new(text: String, color: Rgb) -> Message { - Message { text, color, topic: None } + Message { text, color, target: None } } - /// Formatted message text lines + /// Formatted message text lines. pub fn text(&self, size_info: &SizeInfo) -> Vec { let num_cols = size_info.cols().0; let max_lines = size_info.lines().saturating_sub(MIN_FREE_LINES); @@ -92,25 +92,25 @@ impl Message { lines } - /// Message color + /// Message color. #[inline] pub fn color(&self) -> Rgb { self.color } - /// Message topic + /// Message target. #[inline] - pub fn topic(&self) -> Option<&String> { - self.topic.as_ref() + pub fn target(&self) -> Option<&String> { + self.target.as_ref() } - /// Update the message topic + /// Update the message target. #[inline] - pub fn set_topic(&mut self, topic: String) { - self.topic = Some(topic); + pub fn set_target(&mut self, target: String) { + self.target = Some(target); } - /// Right-pad text to fit a specific number of columns + /// Right-pad text to fit a specific number of columns. #[inline] fn pad_text(mut text: String, num_cols: usize) -> String { let padding_len = num_cols.saturating_sub(text.len()); @@ -119,82 +119,56 @@ impl Message { } } -/// Storage for message bar -#[derive(Debug)] +/// Storage for message bar. +#[derive(Debug, Default)] pub struct MessageBuffer { - current: Option, - messages: Receiver, - tx: Sender, + messages: VecDeque, } impl MessageBuffer { - /// Create new message buffer + /// Create new message buffer. pub fn new() -> MessageBuffer { - let (tx, messages) = crossbeam_channel::unbounded(); - MessageBuffer { current: None, messages, tx } + MessageBuffer { messages: VecDeque::new() } } - /// Check if there are any messages queued + /// Check if there are any messages queued. #[inline] pub fn is_empty(&self) -> bool { - self.current.is_none() + self.messages.is_empty() } - /// Current message + /// Current message. #[inline] - pub fn message(&mut self) -> Option { - if let Some(current) = &self.current { - Some(current.clone()) - } else { - self.current = self.messages.try_recv().ok(); - self.current.clone() - } + pub fn message(&self) -> Option<&Message> { + self.messages.front() } - /// Channel for adding new messages - #[inline] - pub fn tx(&self) -> Sender { - self.tx.clone() - } - - /// Remove the currently visible message + /// Remove the currently visible message. #[inline] pub fn pop(&mut self) { - // Remove all duplicates - for msg in self - .messages - .try_iter() - .take(self.messages.len()) - .filter(|m| Some(m) != self.current.as_ref()) - { - let _ = self.tx.send(msg); - } - // Remove the message itself - self.current = self.messages.try_recv().ok(); - } + let msg = self.messages.pop_front(); - /// Remove all messages with a specific topic - #[inline] - pub fn remove_topic(&mut self, topic: &str) { - // Filter messages currently pending - for msg in self - .messages - .try_iter() - .take(self.messages.len()) - .filter(|m| m.topic().map(String::as_str) != Some(topic)) - { - let _ = self.tx.send(msg); + // Remove all duplicates + if let Some(msg) = msg { + self.messages = self.messages.drain(..).filter(|m| m != &msg).collect(); } - - // Remove the currently active message - self.current = self.messages.try_recv().ok(); } -} -impl Default for MessageBuffer { - fn default() -> MessageBuffer { - MessageBuffer::new() + /// Remove all messages with a specific target. + #[inline] + pub fn remove_target(&mut self, target: &str) { + self.messages = self + .messages + .drain(..) + .filter(|m| m.target().map(String::as_str) != Some(target)) + .collect(); + } + + /// Add a new message to the queue. + #[inline] + pub fn push(&mut self, message: Message) { + self.messages.push_back(message); } } @@ -207,7 +181,7 @@ mod test { fn appends_close_button() { let input = "a"; let mut message_buffer = MessageBuffer::new(); - message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap(); + message_buffer.push(Message::new(input.into(), color::RED)); let size = SizeInfo { width: 7., height: 10., @@ -227,7 +201,7 @@ mod test { fn multiline_close_button_first_line() { let input = "fo\nbar"; let mut message_buffer = MessageBuffer::new(); - message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap(); + message_buffer.push(Message::new(input.into(), color::RED)); let size = SizeInfo { width: 6., height: 10., @@ -247,7 +221,7 @@ mod test { fn splits_on_newline() { let input = "a\nb"; let mut message_buffer = MessageBuffer::new(); - message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap(); + message_buffer.push(Message::new(input.into(), color::RED)); let size = SizeInfo { width: 6., height: 10., @@ -267,7 +241,7 @@ mod test { fn splits_on_length() { let input = "foobar1"; let mut message_buffer = MessageBuffer::new(); - message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap(); + message_buffer.push(Message::new(input.into(), color::RED)); let size = SizeInfo { width: 6., height: 10., @@ -287,7 +261,7 @@ mod test { fn empty_with_shortterm() { let input = "foobar"; let mut message_buffer = MessageBuffer::new(); - message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap(); + message_buffer.push(Message::new(input.into(), color::RED)); let size = SizeInfo { width: 6., height: 0., @@ -307,7 +281,7 @@ mod test { fn truncates_long_messages() { let input = "hahahahahahahahahahaha truncate this because it's too long for the term"; let mut message_buffer = MessageBuffer::new(); - message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap(); + message_buffer.push(Message::new(input.into(), color::RED)); let size = SizeInfo { width: 22., height: (MIN_FREE_LINES + 2) as f32, @@ -330,7 +304,7 @@ mod test { fn hide_button_when_too_narrow() { let input = "ha"; let mut message_buffer = MessageBuffer::new(); - message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap(); + message_buffer.push(Message::new(input.into(), color::RED)); let size = SizeInfo { width: 2., height: 10., @@ -350,7 +324,7 @@ mod test { fn hide_truncated_when_too_narrow() { let input = "hahahahahahahahaha"; let mut message_buffer = MessageBuffer::new(); - message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap(); + message_buffer.push(Message::new(input.into(), color::RED)); let size = SizeInfo { width: 2., height: (MIN_FREE_LINES + 2) as f32, @@ -370,7 +344,7 @@ mod test { fn add_newline_for_button() { let input = "test"; let mut message_buffer = MessageBuffer::new(); - message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap(); + message_buffer.push(Message::new(input.into(), color::RED)); let size = SizeInfo { width: 5., height: 10., @@ -387,17 +361,17 @@ mod test { } #[test] - fn remove_topic() { + fn remove_target() { let mut message_buffer = MessageBuffer::new(); for i in 0..10 { let mut msg = Message::new(i.to_string(), color::RED); if i % 2 == 0 && i < 5 { - msg.set_topic("topic".into()); + msg.set_target("target".into()); } - message_buffer.tx().send(msg).unwrap(); + message_buffer.push(msg); } - message_buffer.remove_topic("topic"); + message_buffer.remove_target("target"); // Count number of messages let mut num_messages = 0; @@ -413,22 +387,22 @@ mod test { fn pop() { let mut message_buffer = MessageBuffer::new(); let one = Message::new(String::from("one"), color::RED); - message_buffer.tx().send(one.clone()).unwrap(); + message_buffer.push(one.clone()); let two = Message::new(String::from("two"), color::YELLOW); - message_buffer.tx().send(two.clone()).unwrap(); + message_buffer.push(two.clone()); - assert_eq!(message_buffer.message(), Some(one)); + assert_eq!(message_buffer.message(), Some(&one)); message_buffer.pop(); - assert_eq!(message_buffer.message(), Some(two)); + assert_eq!(message_buffer.message(), Some(&two)); } #[test] fn wrap_on_words() { let input = "a\nbc defg"; let mut message_buffer = MessageBuffer::new(); - message_buffer.tx().send(Message::new(input.into(), color::RED)).unwrap(); + message_buffer.push(Message::new(input.into(), color::RED)); let size = SizeInfo { width: 5., height: 10., @@ -453,10 +427,10 @@ mod test { let mut message_buffer = MessageBuffer::new(); for _ in 0..10 { let msg = Message::new(String::from("test"), color::RED); - message_buffer.tx().send(msg).unwrap(); + message_buffer.push(msg); } - 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.push(Message::new(String::from("other"), color::RED)); + message_buffer.push(Message::new(String::from("test"), color::YELLOW)); let _ = message_buffer.message(); message_buffer.pop(); diff --git a/alacritty_terminal/src/meter.rs b/alacritty_terminal/src/meter.rs index 19d7fe7..686dd85 100644 --- a/alacritty_terminal/src/meter.rs +++ b/alacritty_terminal/src/meter.rs @@ -30,6 +30,7 @@ //! // Get the moving average. The meter tracks a fixed number of samples, and //! // the average won't mean much until it's filled up at least once. //! println!("Average time: {}", meter.average()); +//! ``` use std::time::{Duration, Instant}; diff --git a/alacritty_terminal/src/renderer/mod.rs b/alacritty_terminal/src/renderer/mod.rs index 4aae853..51c0d0f 100644 --- a/alacritty_terminal/src/renderer/mod.rs +++ b/alacritty_terminal/src/renderer/mod.rs @@ -23,10 +23,10 @@ use std::time::Duration; use fnv::FnvHasher; use font::{self, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer}; -use glutin::dpi::PhysicalSize; +use log::{error, info}; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; -use crate::config::{self, Config, Delta}; +use crate::config::{self, Config, Delta, Font, StartupMode}; use crate::cursor::{get_cursor_glyph, CursorKey}; use crate::gl; use crate::gl::types::*; @@ -34,7 +34,9 @@ use crate::index::{Column, Line}; use crate::renderer::rects::RenderRect; use crate::term::cell::{self, Flags}; use crate::term::color::Rgb; +use crate::term::SizeInfo; use crate::term::{self, RenderableCell, RenderableCellContent}; +use crate::util; pub mod rects; @@ -284,12 +286,6 @@ impl GlyphCache { FontDesc::new(desc.family.clone(), style) } - pub fn font_metrics(&self) -> font::Metrics { - self.rasterizer - .metrics(self.font_key, self.font_size) - .expect("metrics load since font is loaded at glyph cache creation") - } - pub fn get<'a, L>(&'a mut self, glyph_key: GlyphKey, loader: &mut L) -> &'a Glyph where L: LoadGlyph, @@ -311,8 +307,7 @@ impl GlyphCache { pub fn update_font_size( &mut self, - font: &config::Font, - size: font::Size, + font: config::Font, dpr: f64, loader: &mut L, ) -> Result<(), font::Error> { @@ -325,12 +320,11 @@ impl GlyphCache { self.rasterizer.update_dpr(dpr as f32); // Recompute font keys - let font = font.to_owned().with_size(size); let (regular, bold, italic, bold_italic) = Self::compute_font_keys(&font, &mut self.rasterizer)?; self.rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?; - let metrics = self.rasterizer.metrics(regular, size)?; + let metrics = self.rasterizer.metrics(regular, font.size)?; info!("Font size changed to {:?} with DPR of {}", font.size, dpr); @@ -349,13 +343,15 @@ impl GlyphCache { Ok(()) } - // Calculate font metrics without access to a glyph cache - // - // This should only be used *before* OpenGL is initialized and the glyph cache can be filled. - pub fn static_metrics(config: &Config, dpr: f32) -> Result { - let font = config.font.clone(); + pub fn font_metrics(&self) -> font::Metrics { + self.rasterizer + .metrics(self.font_key, self.font_size) + .expect("metrics load since font is loaded at glyph cache creation") + } - let mut rasterizer = font::Rasterizer::new(dpr, config.font.use_thin_strokes())?; + // Calculate font metrics without access to a glyph cache + pub fn static_metrics(font: Font, dpr: f64) -> Result { + let mut rasterizer = font::Rasterizer::new(dpr as f32, font.use_thin_strokes())?; let regular_desc = GlyphCache::make_desc(&font.normal(), font::Slant::Normal, font::Weight::Normal); let regular = rasterizer.load_font(®ular_desc, font.size)?; @@ -363,6 +359,34 @@ impl GlyphCache { rasterizer.metrics(regular, font.size) } + + pub fn calculate_dimensions( + config: &Config, + dpr: f64, + cell_width: f32, + cell_height: f32, + ) -> Option<(f64, f64)> { + let dimensions = config.window.dimensions; + + if dimensions.columns_u32() == 0 + || dimensions.lines_u32() == 0 + || config.window.startup_mode() != StartupMode::Windowed + { + return None; + } + + let padding_x = f64::from(config.window.padding.x) * dpr; + let padding_y = f64::from(config.window.padding.y) * dpr; + + // Calculate new size based on cols/lines specified in config + let grid_width = cell_width as u32 * dimensions.columns_u32(); + let grid_height = cell_height as u32 * dimensions.lines_u32(); + + let width = (f64::from(grid_width) + 2. * padding_x).floor(); + let height = (f64::from(grid_height) + 2. * padding_y).floor(); + + Some((width, height)) + } } #[derive(Debug)] @@ -411,13 +435,13 @@ pub struct QuadRenderer { } #[derive(Debug)] -pub struct RenderApi<'a> { +pub struct RenderApi<'a, C> { active_tex: &'a mut GLuint, batch: &'a mut Batch, atlas: &'a mut Vec, current_atlas: &'a mut usize, program: &'a mut TextShaderProgram, - config: &'a Config, + config: &'a Config, } #[derive(Debug)] @@ -445,7 +469,7 @@ impl Batch { Batch { tex: 0, instances: Vec::with_capacity(BATCH_MAX) } } - pub fn add_item(&mut self, cell: &RenderableCell, glyph: &Glyph) { + pub fn add_item(&mut self, cell: RenderableCell, glyph: &Glyph) { if self.is_empty() { self.tex = glyph.tex_id; } @@ -639,7 +663,7 @@ impl QuadRenderer { let (msg_tx, msg_rx) = mpsc::channel(); if cfg!(feature = "live-shader-reload") { - ::std::thread::spawn(move || { + util::thread::spawn_named("live shader reload", move || { let (tx, rx) = ::std::sync::mpsc::channel(); // The Duration argument is a debouncing period. let mut watcher = @@ -691,8 +715,8 @@ impl QuadRenderer { // Draw all rectangles simultaneously to prevent excessive program swaps pub fn draw_rects( &mut self, - config: &Config, props: &term::SizeInfo, + visual_bell_color: Rgb, visual_bell_intensity: f64, cell_line_rects: Vec, ) { @@ -724,8 +748,7 @@ impl QuadRenderer { } // Draw visual bell - let color = config.visual_bell.color; - let rect = RenderRect::new(0., 0., props.width, props.height, color); + let rect = RenderRect::new(0., 0., props.width, props.height, visual_bell_color); self.render_rect(&rect, visual_bell_intensity as f32, props); // Draw underlines and strikeouts @@ -753,9 +776,9 @@ impl QuadRenderer { } } - pub fn with_api(&mut self, config: &Config, props: &term::SizeInfo, func: F) -> T + pub fn with_api(&mut self, config: &Config, props: &term::SizeInfo, func: F) -> T where - F: FnOnce(RenderApi<'_>) -> T, + F: FnOnce(RenderApi<'_, C>) -> T, { // Flush message queue if let Ok(Msg::ShaderReload) = self.rx.try_recv() { @@ -838,25 +861,19 @@ impl QuadRenderer { self.rect_program = rect_program; } - pub fn resize(&mut self, size: PhysicalSize, padding_x: f32, padding_y: f32) { - let (width, height): (u32, u32) = size.into(); - + pub fn resize(&mut self, size: &SizeInfo) { // viewport unsafe { - let width = width as i32; - 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( + size.padding_x as i32, + size.padding_y as i32, + size.width as i32 - 2 * size.padding_x as i32, + size.height as i32 - 2 * size.padding_y as i32, + ); // update projection gl::UseProgram(self.program.id); - self.program.update_projection( - width as f32, - height as f32, - padding_x as f32, - padding_y as f32, - ); + self.program.update_projection(size.width, size.height, size.padding_x, size.padding_y); gl::UseProgram(0); } } @@ -899,10 +916,10 @@ impl QuadRenderer { } } -impl<'a> RenderApi<'a> { +impl<'a, C> RenderApi<'a, C> { pub fn clear(&self, color: Rgb) { - let alpha = self.config.background_opacity(); unsafe { + let alpha = self.config.background_opacity(); gl::ClearColor( (f32::from(color.r) / 255.0).min(1.0) * alpha, (f32::from(color.g) / 255.0).min(1.0) * alpha, @@ -989,7 +1006,7 @@ impl<'a> RenderApi<'a> { } #[inline] - 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(); @@ -1009,18 +1026,15 @@ impl<'a> RenderApi<'a> { // 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, + self.config.font.offset.x, + self.config.font.offset.y, cursor_key.is_wide, )) }); - self.add_render_item(&cell, &glyph); + self.add_render_item(cell, &glyph); return; }, RenderableCellContent::Chars(chars) => chars, @@ -1050,7 +1064,7 @@ impl<'a> RenderApi<'a> { // Add cell to batch let glyph = glyph_cache.get(glyph_key, self); - self.add_render_item(&cell, glyph); + self.add_render_item(cell, glyph); // Render zero-width characters for c in (&chars[1..]).iter().filter(|c| **c != ' ') { @@ -1064,7 +1078,7 @@ impl<'a> RenderApi<'a> { // 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); + self.add_render_item(cell, &glyph); } } } @@ -1124,7 +1138,7 @@ impl<'a> LoadGlyph for LoaderApi<'a> { } } -impl<'a> LoadGlyph for RenderApi<'a> { +impl<'a, C> LoadGlyph for RenderApi<'a, C> { fn load_glyph(&mut self, rasterized: &RasterizedGlyph) -> Glyph { load_glyph(self.active_tex, self.atlas, self.current_atlas, rasterized) } @@ -1134,7 +1148,7 @@ impl<'a> LoadGlyph for RenderApi<'a> { } } -impl<'a> Drop for RenderApi<'a> { +impl<'a, C> Drop for RenderApi<'a, C> { fn drop(&mut self) { if !self.batch.is_empty() { self.render_batch(); diff --git a/alacritty_terminal/src/renderer/rects.rs b/alacritty_terminal/src/renderer/rects.rs index 72139e3..dd72f67 100644 --- a/alacritty_terminal/src/renderer/rects.rs +++ b/alacritty_terminal/src/renderer/rects.rs @@ -91,7 +91,7 @@ impl RenderLines { } /// Update the stored lines with the next cell info. - pub fn update(&mut self, cell: &RenderableCell) { + pub fn update(&mut self, cell: RenderableCell) { for flag in &[Flags::UNDERLINE, Flags::STRIKEOUT] { if !cell.flags.contains(*flag) { continue; diff --git a/alacritty_terminal/src/selection.rs b/alacritty_terminal/src/selection.rs index ffe35e0..29934e5 100644 --- a/alacritty_terminal/src/selection.rs +++ b/alacritty_terminal/src/selection.rs @@ -39,6 +39,7 @@ use crate::term::{Search, Term}; /// [`simple`]: enum.Selection.html#method.simple /// [`semantic`]: enum.Selection.html#method.semantic /// [`lines`]: enum.Selection.html#method.lines +/// [`update`]: enum.Selection.html#method.update #[derive(Debug, Clone, PartialEq)] pub enum Selection { Simple { @@ -164,7 +165,7 @@ impl Selection { } } - pub fn to_span(&self, term: &Term) -> Option { + pub fn to_span(&self, term: &Term) -> Option { // Get both sides of the selection let (mut start, mut end) = match *self { Selection::Simple { ref region } | Selection::Block { ref region } => { @@ -405,13 +406,19 @@ mod test { use super::{Selection, Span}; use crate::clipboard::Clipboard; + use crate::config::MockConfig; + use crate::event::{Event, EventListener}; use crate::grid::Grid; use crate::index::{Column, Line, Point, Side}; - use crate::message_bar::MessageBuffer; use crate::term::cell::{Cell, Flags}; use crate::term::{SizeInfo, Term}; - fn term(width: usize, height: usize) -> Term { + struct Mock; + impl EventListener for Mock { + fn send_event(&self, _event: Event) {} + } + + fn term(width: usize, height: usize) -> Term { let size = SizeInfo { width: width as f32, height: height as f32, @@ -421,7 +428,7 @@ mod test { padding_y: 0.0, dpr: 1.0, }; - Term::new(&Default::default(), size, MessageBuffer::new(), Clipboard::new_nop()) + Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock) } /// Test case of single cell selection diff --git a/alacritty_terminal/src/term/cell.rs b/alacritty_terminal/src/term/cell.rs index 7a75977..234805f 100644 --- a/alacritty_terminal/src/term/cell.rs +++ b/alacritty_terminal/src/term/cell.rs @@ -13,6 +13,8 @@ // limitations under the License. use bitflags::bitflags; +use serde::{Deserialize, Serialize}; + use crate::ansi::{Color, NamedColor}; use crate::grid::{self, GridCell}; use crate::index::Column; diff --git a/alacritty_terminal/src/term/color.rs b/alacritty_terminal/src/term/color.rs index f2db335..e9f0a26 100644 --- a/alacritty_terminal/src/term/color.rs +++ b/alacritty_terminal/src/term/color.rs @@ -2,8 +2,9 @@ use std::fmt; use std::ops::{Index, IndexMut, Mul}; use std::str::FromStr; +use log::{error, trace}; use serde::de::Visitor; -use serde::{Deserialize, Deserializer}; +use serde::{Deserialize, Deserializer, Serialize}; use crate::ansi; use crate::config::Colors; diff --git a/alacritty_terminal/src/term/mod.rs b/alacritty_terminal/src/term/mod.rs index 58d0631..4e51d73 100644 --- a/alacritty_terminal/src/term/mod.rs +++ b/alacritty_terminal/src/term/mod.rs @@ -18,9 +18,9 @@ use std::ops::{Index, IndexMut, Range, RangeInclusive}; use std::time::{Duration, Instant}; use std::{io, mem, ptr}; -use font::{self, Size}; -use glutin::MouseCursor; +use log::{debug, trace}; use rfind_url::{Parser, ParserState}; +use serde::{Deserialize, Serialize}; use unicode_width::UnicodeWidthChar; use crate::ansi::{ @@ -29,19 +29,17 @@ use crate::ansi::{ use crate::clipboard::{Clipboard, ClipboardType}; use crate::config::{Config, VisualBellAnimation}; use crate::cursor::CursorKey; +use crate::event::{Event, EventListener}; use crate::grid::{ BidirectionalIterator, DisplayIter, Grid, GridCell, IndexRegion, Indexed, Scroll, }; use crate::index::{self, Column, Contains, IndexRange, Line, Linear, Point}; -use crate::input::FONT_SIZE_STEP; -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::url::Url; - #[cfg(windows)] use crate::tty; +use crate::url::Url; pub mod cell; pub mod color; @@ -62,7 +60,7 @@ pub trait Search { fn bracket_search(&self, _: Point) -> Option>; } -impl Search for Term { +impl Search for Term { fn semantic_search_left(&self, mut point: Point) -> Point { // Limit the starting point to the last line in the history point.line = min(point.line, self.grid.len() - 1); @@ -151,7 +149,7 @@ impl Search for Term { } } -impl selection::Dimensions for Term { +impl selection::Dimensions for Term { fn dimensions(&self) -> Point { let line = if self.mode.contains(TermMode::ALT_SCREEN) { self.grid.num_lines() @@ -170,30 +168,30 @@ impl selection::Dimensions for Term { /// /// This manages the cursor during a render. The cursor location is inverted to /// draw it, and reverted after drawing to maintain state. -pub struct RenderableCellsIter<'a> { +pub struct RenderableCellsIter<'a, C> { inner: DisplayIter<'a, Cell>, grid: &'a Grid, cursor: &'a Point, cursor_offset: usize, cursor_key: Option, cursor_style: CursorStyle, - config: &'a Config, + config: &'a Config, colors: &'a color::List, selection: Option, url_highlight: &'a Option>, } -impl<'a> RenderableCellsIter<'a> { +impl<'a, C> RenderableCellsIter<'a, C> { /// Create the renderable cells iterator /// /// The cursor and terminal mode are required for properly displaying the /// cursor. - fn new<'b>( - term: &'b Term, - config: &'b Config, + fn new<'b, T>( + term: &'b Term, + config: &'b Config, selection: Option, mut cursor_style: CursorStyle, - ) -> RenderableCellsIter<'b> { + ) -> RenderableCellsIter<'b, C> { let grid = &term.grid; let cursor_offset = grid.line_to_offset(term.cursor.point.line); @@ -250,13 +248,13 @@ impl<'a> RenderableCellsIter<'a> { } } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub enum RenderableCellContent { Chars([char; cell::MAX_ZEROWIDTH_CHARS + 1]), Cursor(CursorKey), } -#[derive(Clone, Debug)] +#[derive(Copy, Clone, Debug)] pub struct RenderableCell { /// A _Display_ line (not necessarily an _Active_ line) pub line: Line, @@ -269,7 +267,12 @@ pub struct RenderableCell { } impl RenderableCell { - fn new(config: &Config, colors: &color::List, cell: Indexed, selected: bool) -> Self { + fn new( + config: &Config, + colors: &color::List, + cell: Indexed, + selected: bool, + ) -> Self { // Lookup RGB values let mut fg_rgb = Self::compute_fg_rgb(config, colors, cell.fg, cell.flags); let mut bg_rgb = Self::compute_bg_rgb(colors, cell.bg); @@ -309,7 +312,12 @@ impl RenderableCell { } } - fn compute_fg_rgb(config: &Config, colors: &color::List, fg: Color, flags: cell::Flags) -> Rgb { + fn compute_fg_rgb( + config: &Config, + colors: &color::List, + fg: Color, + flags: cell::Flags, + ) -> Rgb { match fg { Color::Spec(rgb) => rgb, Color::Named(ansi) => { @@ -365,7 +373,7 @@ impl RenderableCell { } } -impl<'a> Iterator for RenderableCellsIter<'a> { +impl<'a, C> Iterator for RenderableCellsIter<'a, C> { type Item = RenderableCell; /// Gets the next renderable cell @@ -573,7 +581,7 @@ fn cubic_bezier(p0: f64, p1: f64, p2: f64, p3: f64, x: f64) -> f64 { } impl VisualBell { - pub fn new(config: &Config) -> VisualBell { + pub fn new(config: &Config) -> VisualBell { let visual_bell_config = &config.visual_bell; VisualBell { animation: visual_bell_config.animation, @@ -668,14 +676,14 @@ impl VisualBell { } } - pub fn update_config(&mut self, config: &Config) { + pub fn update_config(&mut self, config: &Config) { let visual_bell_config = &config.visual_bell; self.animation = visual_bell_config.animation; self.duration = visual_bell_config.duration(); } } -pub struct Term { +pub struct Term { /// The grid grid: Grid, @@ -686,14 +694,6 @@ pub struct Term { /// arrays. Without it we would have to sanitize cursor.col every time we used it. input_needs_wrap: bool, - /// Got a request to set title; it's buffered here until next draw. - /// - /// Would be nice to avoid the allocation... - next_title: Option, - - /// Got a request to set the mouse cursor; it's buffered here until the next draw - next_mouse_cursor: Option, - /// Alternate grid alt_grid: Grid, @@ -716,17 +716,9 @@ pub struct Term { /// Scroll region scroll_region: Range, - /// Font size - pub font_size: Size, - original_font_size: Size, - - /// Size - size_info: SizeInfo, - pub dirty: bool, pub visual_bell: VisualBell, - pub next_is_urgent: Option, /// Saved cursor from main grid cursor_save: Cursor, @@ -760,18 +752,18 @@ pub struct Term { /// Automatically scroll to bottom when new lines are added auto_scroll: bool, - /// Buffer to store messages for the message bar - message_buffer: MessageBuffer, - - /// Hint that Alacritty should be closed - should_exit: bool, - /// Clipboard access coupled to the active window clipboard: Clipboard, + + /// Proxy for sending events to the event loop + event_proxy: T, + + /// Terminal focus + pub is_focused: bool, } /// Terminal size info -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)] pub struct SizeInfo { /// Terminal window width pub width: f32, @@ -829,7 +821,7 @@ impl SizeInfo { } } -impl Term { +impl Term { pub fn selection(&self) -> &Option { &self.grid.selection } @@ -839,29 +831,22 @@ impl Term { } #[inline] - pub fn get_next_title(&mut self) -> Option { - self.next_title.take() - } - - #[inline] - pub fn scroll_display(&mut self, scroll: Scroll) { - self.set_mouse_cursor(MouseCursor::Text); + pub fn scroll_display(&mut self, scroll: Scroll) + where + T: EventListener, + { + self.event_proxy.send_event(Event::MouseCursorDirty); self.grid.scroll_display(scroll); self.reset_url_highlight(); self.dirty = true; } - #[inline] - pub fn get_next_mouse_cursor(&mut self) -> Option { - self.next_mouse_cursor.take() - } - - pub fn new( - config: &Config, - size: SizeInfo, - message_buffer: MessageBuffer, + pub fn new( + config: &Config, + size: &SizeInfo, clipboard: Clipboard, - ) -> Term { + event_proxy: T, + ) -> Term { let num_cols = size.cols(); let num_lines = size.lines(); @@ -877,17 +862,12 @@ impl Term { let colors = color::List::from(&config.colors); Term { - next_title: None, - next_mouse_cursor: None, dirty: false, visual_bell: VisualBell::new(config), - next_is_urgent: None, input_needs_wrap: false, grid, alt_grid: alt, alt: false, - font_size: config.font.size, - original_font_size: config.font.size, active_charset: Default::default(), cursor: Default::default(), cursor_save: Default::default(), @@ -895,7 +875,6 @@ impl Term { tabs, mode: Default::default(), scroll_region, - size_info: size, colors, color_modified: [false; color::COUNT], original_colors: colors, @@ -905,25 +884,13 @@ impl Term { dynamic_title: config.dynamic_title(), tabspaces, auto_scroll: config.scrolling.auto_scroll, - message_buffer, - should_exit: false, clipboard, + event_proxy, + is_focused: true, } } - pub fn change_font_size(&mut self, delta: f32) { - // Saturating addition with minimum font size FONT_SIZE_STEP - let new_size = self.font_size + Size::new(delta); - self.font_size = max(new_size, Size::new(FONT_SIZE_STEP)); - self.dirty = true; - } - - pub fn reset_font_size(&mut self) { - self.font_size = self.original_font_size; - self.dirty = true; - } - - pub fn update_config(&mut self, config: &Config) { + pub fn update_config(&mut self, config: &Config) { self.semantic_escape_chars = config.selection.semantic_escape_chars().to_owned(); self.original_colors.fill_named(&config.colors); self.original_colors.fill_cube(&config.colors); @@ -938,16 +905,6 @@ impl Term { self.dynamic_title = config.dynamic_title(); self.auto_scroll = config.scrolling.auto_scroll; self.grid.update_history(config.scrolling.history() as usize, &self.cursor.template); - - if self.original_font_size == self.font_size { - self.font_size = config.font.size; - } - self.original_font_size = config.font.size; - } - - #[inline] - pub fn needs_draw(&self) -> bool { - self.dirty } pub fn selection_to_string(&self) -> Option { @@ -1072,21 +1029,6 @@ impl Term { self.grid.buffer_to_visible(point) } - /// Convert the given pixel values to a grid coordinate - /// - /// The mouse coordinates are expected to be relative to the top left. The - /// line and column returned are also relative to the top left. - /// - /// Returns None if the coordinates are outside the window, - /// padding pixels are considered inside the window - pub fn pixels_to_coords(&self, x: usize, y: usize) -> Option { - if self.size_info.contains_point(x, y, true) { - Some(self.size_info.pixels_to_coords(x, y)) - } else { - None - } - } - /// Access to the raw grid data structure /// /// This is a bit of a hack; when the window is closed, the event processor @@ -1106,14 +1048,10 @@ 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>( - &'b self, - config: &'b Config, - window_focused: bool, - ) -> RenderableCellsIter<'_> { + pub fn renderable_cells<'b, C>(&'b self, config: &'b Config) -> RenderableCellsIter<'_, C> { let selection = self.grid.selection.as_ref().and_then(|s| s.to_span(self)); - let cursor = if window_focused || !config.cursor.unfocused_hollow() { + let cursor = if self.is_focused || !config.cursor.unfocused_hollow() { self.cursor_style.unwrap_or(self.default_cursor_style) } else { CursorStyle::HollowBlock @@ -1124,11 +1062,9 @@ impl Term { /// Resize terminal to new dimensions pub fn resize(&mut self, size: &SizeInfo) { - debug!("Resizing terminal"); - // Bounds check; lots of math assumes width and height are > 0 - if size.width as usize <= 2 * self.size_info.padding_x as usize - || size.height as usize <= 2 * self.size_info.padding_y as usize + if size.width as usize <= 2 * size.padding_x as usize + || size.height as usize <= 2 * size.padding_y as usize { return; } @@ -1138,12 +1074,6 @@ impl Term { let mut num_cols = size.cols(); let mut num_lines = size.lines(); - if let Some(message) = self.message_buffer.message() { - num_lines -= message.text(size).len(); - } - - self.size_info = *size; - if old_cols == num_cols && old_lines == num_lines { debug!("Term::resize dimensions unchanged"); return; @@ -1210,11 +1140,6 @@ impl Term { self.tabs = TabStops::new(self.grid.num_cols(), self.tabspaces); } - #[inline] - pub fn size_info(&self) -> &SizeInfo { - &self.size_info - } - #[inline] pub fn mode(&self) -> &TermMode { &self.mode @@ -1266,7 +1191,10 @@ impl Term { self.grid.scroll_up(&(origin..self.scroll_region.end), lines, &template); } - fn deccolm(&mut self) { + fn deccolm(&mut self) + where + T: EventListener, + { // Setting 132 column font makes no sense, but run the other side effects // Clear scrolling region self.set_scrolling_region(1, self.grid.num_lines().0); @@ -1282,23 +1210,11 @@ impl Term { } #[inline] - pub fn message_buffer_mut(&mut self) -> &mut MessageBuffer { - &mut self.message_buffer - } - - #[inline] - pub fn message_buffer(&self) -> &MessageBuffer { - &self.message_buffer - } - - #[inline] - pub fn exit(&mut self) { - self.should_exit = true; - } - - #[inline] - pub fn should_exit(&self) -> bool { - self.should_exit + pub fn exit(&mut self) + where + T: EventListener, + { + self.event_proxy.send_event(Event::Exit); } #[inline] @@ -1389,7 +1305,7 @@ impl Term { } } -impl TermInfo for Term { +impl TermInfo for Term { #[inline] fn lines(&self) -> Line { self.grid.num_lines() @@ -1401,33 +1317,33 @@ impl TermInfo for Term { } } -impl ansi::Handler for Term { - /// Set the window title +impl ansi::Handler for Term { #[inline] + #[cfg(not(windows))] fn set_title(&mut self, title: &str) { if self.dynamic_title { - self.next_title = Some(title.to_owned()); - - #[cfg(windows)] - { - // cmd.exe in winpty: winpty incorrectly sets the title to ' ' instead of - // 'Alacritty' - thus we have to substitute this back to get equivalent - // behaviour as conpty. - // - // The starts_with check is necessary because other shells e.g. bash set a - // different title and don't need Alacritty prepended. - if !tty::is_conpty() && title.starts_with(' ') { - self.next_title = Some(format!("Alacritty {}", title.trim())); - } - } + self.event_proxy.send_event(Event::Title(title.to_owned())); } } - /// Set the mouse cursor #[inline] - fn set_mouse_cursor(&mut self, cursor: MouseCursor) { - self.next_mouse_cursor = Some(cursor); - self.dirty = true; + #[cfg(windows)] + fn set_title(&mut self, title: &str) { + if self.dynamic_title { + // cmd.exe in winpty: winpty incorrectly sets the title to ' ' instead of + // 'Alacritty' - thus we have to substitute this back to get equivalent + // behaviour as conpty. + // + // The starts_with check is necessary because other shells e.g. bash set a + // different title and don't need Alacritty prepended. + let title = if !tty::is_conpty() && title.starts_with(' ') { + format!("Alacritty {}", title.trim()) + } else { + title.to_owned() + }; + + self.event_proxy.send_event(Event::Title(title)); + } } /// A character to be displayed @@ -1554,11 +1470,11 @@ impl ansi::Handler for Term { fn insert_blank(&mut self, count: Column) { // Ensure inserting within terminal bounds - let count = min(count, self.size_info.cols() - self.cursor.point.col); + let count = min(count, self.grid.num_cols() - self.cursor.point.col); let source = self.cursor.point.col; let destination = self.cursor.point.col + count; - let num_cells = (self.size_info.cols() - destination).0; + let num_cells = (self.grid.num_cols() - destination).0; let line = &mut self.grid[self.cursor.point.line]; @@ -1703,7 +1619,7 @@ impl ansi::Handler for Term { fn bell(&mut self) { trace!("Bell"); self.visual_bell.ring(); - self.next_is_urgent = Some(true); + self.event_proxy.send_event(Event::Urgent); } #[inline] @@ -1794,12 +1710,14 @@ impl ansi::Handler for Term { #[inline] fn delete_chars(&mut self, count: Column) { + let cols = self.grid.num_cols(); + // Ensure deleting within terminal bounds - let count = min(count, self.size_info.cols()); + let count = min(count, cols); let start = self.cursor.point.col; - let end = min(start + count, self.grid.num_cols() - 1); - let n = (self.size_info.cols() - end).0; + let end = min(start + count, cols - 1); + let n = (cols - end).0; let line = &mut self.grid[self.cursor.point.line]; @@ -1813,7 +1731,7 @@ impl ansi::Handler for Term { // Clear last `count` cells in line. If deleting 1 char, need to delete // 1 cell. let template = self.cursor.template; - let end = self.size_info.cols() - count; + let end = cols - count; for c in &mut line[end..] { c.reset(&template); } @@ -1983,13 +1901,9 @@ impl ansi::Handler for Term { self.swap_alt(); } self.input_needs_wrap = false; - self.next_title = None; - self.next_mouse_cursor = None; self.cursor = Default::default(); self.active_charset = Default::default(); self.mode = Default::default(); - self.font_size = self.original_font_size; - self.next_is_urgent = None; self.cursor_save = Default::default(); self.cursor_save_alt = Default::default(); self.colors = self.original_colors; @@ -2061,15 +1975,15 @@ impl ansi::Handler for Term { 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); + self.event_proxy.send_event(Event::MouseCursorDirty); }, ansi::Mode::ReportCellMouseMotion => { self.mode.insert(TermMode::MOUSE_DRAG); - self.set_mouse_cursor(MouseCursor::Default); + self.event_proxy.send_event(Event::MouseCursorDirty); }, ansi::Mode::ReportAllMouseMotion => { self.mode.insert(TermMode::MOUSE_MOTION); - self.set_mouse_cursor(MouseCursor::Default); + self.event_proxy.send_event(Event::MouseCursorDirty); }, ansi::Mode::ReportFocusInOut => self.mode.insert(TermMode::FOCUS_IN_OUT), ansi::Mode::BracketedPaste => self.mode.insert(TermMode::BRACKETED_PASTE), @@ -2101,15 +2015,15 @@ impl ansi::Handler for Term { 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); + self.event_proxy.send_event(Event::MouseCursorDirty); }, ansi::Mode::ReportCellMouseMotion => { self.mode.remove(TermMode::MOUSE_DRAG); - self.set_mouse_cursor(MouseCursor::Text); + self.event_proxy.send_event(Event::MouseCursorDirty); }, ansi::Mode::ReportAllMouseMotion => { self.mode.remove(TermMode::MOUSE_MOTION); - self.set_mouse_cursor(MouseCursor::Text); + self.event_proxy.send_event(Event::MouseCursorDirty); }, ansi::Mode::ReportFocusInOut => self.mode.remove(TermMode::FOCUS_IN_OUT), ansi::Mode::BracketedPaste => self.mode.remove(TermMode::BRACKETED_PASTE), @@ -2215,19 +2129,22 @@ impl IndexMut for TabStops { mod tests { use std::mem; - use font::Size; use serde_json; use crate::ansi::{self, CharsetIndex, Handler, StandardCharset}; use crate::clipboard::Clipboard; - use crate::config::Config; + use crate::config::MockConfig; + use crate::event::{Event, EventListener}; use crate::grid::{Grid, Scroll}; use crate::index::{Column, Line, Point, Side}; - use crate::input::FONT_SIZE_STEP; - use crate::message_bar::MessageBuffer; use crate::selection::Selection; use crate::term::{cell, Cell, SizeInfo, Term}; + struct Mock; + impl EventListener for Mock { + fn send_event(&self, _event: Event) {} + } + #[test] fn semantic_selection_works() { let size = SizeInfo { @@ -2239,8 +2156,7 @@ mod tests { padding_y: 0.0, dpr: 1.0, }; - let mut term = - Term::new(&Default::default(), size, MessageBuffer::new(), Clipboard::new_nop()); + let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock); let mut grid: Grid = Grid::new(Line(3), Column(5), 0, Cell::default()); for i in 0..5 { for j in 0..2 { @@ -2284,8 +2200,7 @@ mod tests { padding_y: 0.0, dpr: 1.0, }; - let mut term = - Term::new(&Default::default(), size, MessageBuffer::new(), Clipboard::new_nop()); + let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock); let mut grid: Grid = Grid::new(Line(1), Column(5), 0, Cell::default()); for i in 0..5 { grid[Line(0)][Column(i)].c = 'a'; @@ -2310,8 +2225,7 @@ mod tests { padding_y: 0.0, dpr: 1.0, }; - let mut term = - Term::new(&Default::default(), size, MessageBuffer::new(), Clipboard::new_nop()); + let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock); let mut grid: Grid = Grid::new(Line(3), Column(3), 0, Cell::default()); for l in 0..3 { if l != 1 { @@ -2355,8 +2269,7 @@ mod tests { padding_y: 0.0, dpr: 1.0, }; - let mut term = - Term::new(&Default::default(), size, MessageBuffer::new(), Clipboard::new_nop()); + let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock); let cursor = Point::new(Line(0), Column(0)); term.configure_charset(CharsetIndex::G0, StandardCharset::SpecialCharacterAndLineDrawing); term.input('a'); @@ -2364,75 +2277,6 @@ mod tests { assert_eq!(term.grid()[&cursor].c, '▒'); } - fn change_font_size_works(font_size: f32) { - 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 config: Config = Default::default(); - let mut term: Term = Term::new(&config, size, MessageBuffer::new(), Clipboard::new_nop()); - term.change_font_size(font_size); - - let expected_font_size: Size = config.font.size + Size::new(font_size); - assert_eq!(term.font_size, expected_font_size); - } - - #[test] - fn increase_font_size_works() { - change_font_size_works(10.0); - } - - #[test] - fn decrease_font_size_works() { - change_font_size_works(-10.0); - } - - #[test] - fn prevent_font_below_threshold_works() { - 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 config: Config = Default::default(); - let mut term: Term = Term::new(&config, size, MessageBuffer::new(), Clipboard::new_nop()); - - term.change_font_size(-100.0); - - let expected_font_size: Size = Size::new(FONT_SIZE_STEP); - assert_eq!(term.font_size, expected_font_size); - } - - #[test] - fn reset_font_size_works() { - 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 config: Config = Default::default(); - let mut term: Term = Term::new(&config, size, MessageBuffer::new(), Clipboard::new_nop()); - - term.change_font_size(10.0); - term.reset_font_size(); - - let expected_font_size: Size = config.font.size; - assert_eq!(term.font_size, expected_font_size); - } - #[test] fn clear_saved_lines() { let size = SizeInfo { @@ -2444,8 +2288,7 @@ mod tests { padding_y: 0.0, dpr: 1.0, }; - let config: Config = Default::default(); - let mut term: Term = Term::new(&config, size, MessageBuffer::new(), Clipboard::new_nop()); + let mut term = Term::new(&MockConfig::default(), &size, Clipboard::new_nop(), Mock); // Add one line of scrollback term.grid.scroll_up(&(Line(0)..Line(1)), Line(1), &Cell::default()); @@ -2471,13 +2314,18 @@ mod benches { use std::path::Path; use crate::clipboard::Clipboard; - use crate::config::Config; + use crate::config::MockConfig; + use crate::event::{Event, EventListener}; use crate::grid::Grid; - use crate::message_bar::MessageBuffer; use super::cell::Cell; use super::{SizeInfo, Term}; + struct Mock; + impl EventListener for Mock { + fn send_event(&self, _event: Event) {} + } + fn read_string

(path: P) -> String where P: AsRef, @@ -2512,13 +2360,13 @@ mod benches { let mut grid: Grid = json::from_str(&serialized_grid).unwrap(); let size: SizeInfo = json::from_str(&serialized_size).unwrap(); - let config = Config::default(); + let config = MockConfig::default(); - let mut terminal = Term::new(&config, size, MessageBuffer::new(), Clipboard::new_nop()); + let mut terminal = Term::new(&config, &size, Clipboard::new_nop(), Mock); mem::swap(&mut terminal.grid, &mut grid); b.iter(|| { - let iter = terminal.renderable_cells(&config, false); + let iter = terminal.renderable_cells(&config); for cell in iter { test::black_box(cell); } diff --git a/alacritty_terminal/src/tty/mod.rs b/alacritty_terminal/src/tty/mod.rs index 16ae96c..7150a42 100644 --- a/alacritty_terminal/src/tty/mod.rs +++ b/alacritty_terminal/src/tty/mod.rs @@ -77,7 +77,7 @@ pub trait EventedPty: EventedReadWrite { } // Setup environment variables -pub fn setup_env(config: &Config) { +pub fn setup_env(config: &Config) { // Default to 'alacritty' terminfo if it is available, otherwise // default to 'xterm-256color'. May be overridden by user's config // below. diff --git a/alacritty_terminal/src/tty/unix.rs b/alacritty_terminal/src/tty/unix.rs index 77b5db4..a2a277a 100644 --- a/alacritty_terminal/src/tty/unix.rs +++ b/alacritty_terminal/src/tty/unix.rs @@ -15,12 +15,13 @@ //! tty related functionality use crate::config::{Config, Shell}; -use crate::display::OnResize; +use crate::event::OnResize; use crate::term::SizeInfo; use crate::tty::{ChildEvent, EventedPty, EventedReadWrite}; use mio; use libc::{self, c_int, pid_t, winsize, TIOCSCTTY}; +use log::error; use nix::pty::openpty; use signal_hook::{self as sighook, iterator::Signals}; @@ -42,6 +43,13 @@ use std::sync::atomic::{AtomicUsize, Ordering}; /// Necessary to put this in static storage for `sigchld` to have access static PID: AtomicUsize = AtomicUsize::new(0); +macro_rules! die { + ($($arg:tt)*) => {{ + error!($($arg)*); + ::std::process::exit(1); + }} +} + pub fn child_pid() -> pid_t { PID.load(Ordering::Relaxed) as pid_t } @@ -133,24 +141,8 @@ pub struct Pty { signals_token: mio::Token, } -impl Pty { - /// Resize the pty - /// - /// Tells the kernel that the window size changed with the new pixel - /// dimensions and line/column counts. - pub fn resize(&self, size: &T) { - let win = size.to_winsize(); - - let res = unsafe { libc::ioctl(self.fd.as_raw_fd(), libc::TIOCSWINSZ, &win as *const _) }; - - if res < 0 { - die!("ioctl TIOCSWINSZ failed: {}", io::Error::last_os_error()); - } - } -} - /// Create a new tty and return a handle to interact with it. -pub fn new(config: &Config, size: &T, window_id: Option) -> Pty { +pub fn new(config: &Config, size: &SizeInfo, window_id: Option) -> Pty { let win_size = size.to_winsize(); let mut buf = [0; 1024]; let pw = get_pw_entry(&mut buf); @@ -241,12 +233,10 @@ pub fn new(config: &Config, size: &T, window_id: Option) -> signals, signals_token: mio::Token::from(0), }; - pty.resize(size); + pty.fd.as_raw_fd().on_resize(size); pty }, - Err(err) => { - die!("Failed to spawn command: {}", err); - }, + Err(err) => die!("Failed to spawn command: {}", err), } } @@ -365,6 +355,10 @@ impl<'a> ToWinsize for &'a SizeInfo { } impl OnResize for i32 { + /// Resize the pty + /// + /// Tells the kernel that the window size changed with the new pixel + /// dimensions and line/column counts. fn on_resize(&mut self, size: &SizeInfo) { let win = size.to_winsize(); diff --git a/alacritty_terminal/src/tty/windows/conpty.rs b/alacritty_terminal/src/tty/windows/conpty.rs index bd602c3..fe49b4d 100644 --- a/alacritty_terminal/src/tty/windows/conpty.rs +++ b/alacritty_terminal/src/tty/windows/conpty.rs @@ -22,6 +22,7 @@ use std::ptr; use std::sync::Arc; use dunce::canonicalize; +use log::info; use mio_anonymous_pipes::{EventedAnonRead, EventedAnonWrite}; use miow; use widestring::U16CString; @@ -38,7 +39,7 @@ use winapi::um::winbase::{EXTENDED_STARTUPINFO_PRESENT, STARTF_USESTDHANDLES, ST use winapi::um::wincontypes::{COORD, HPCON}; use crate::config::{Config, Shell}; -use crate::display::OnResize; +use crate::event::OnResize; use crate::term::SizeInfo; /// Dynamically-loaded Pseudoconsole API from kernel32.dll @@ -98,7 +99,11 @@ impl Drop for Conpty { unsafe impl Send for Conpty {} unsafe impl Sync for Conpty {} -pub fn new<'a>(config: &Config, size: &SizeInfo, _window_id: Option) -> Option> { +pub fn new<'a, C>( + config: &Config, + size: &SizeInfo, + _window_id: Option, +) -> Option> { if !config.enable_experimental_conpty_backend { return None; } diff --git a/alacritty_terminal/src/tty/windows/mod.rs b/alacritty_terminal/src/tty/windows/mod.rs index 7537d33..922dde2 100644 --- a/alacritty_terminal/src/tty/windows/mod.rs +++ b/alacritty_terminal/src/tty/windows/mod.rs @@ -20,12 +20,13 @@ use mio::{self, Evented, Poll, PollOpt, Ready, Token}; use mio_anonymous_pipes::{EventedAnonRead, EventedAnonWrite}; use mio_named_pipes::NamedPipe; +use log::info; use winapi::shared::winerror::WAIT_TIMEOUT; use winapi::um::synchapi::WaitForSingleObject; use winapi::um::winbase::WAIT_OBJECT_0; use crate::config::Config; -use crate::display::OnResize; +use crate::event::OnResize; use crate::term::SizeInfo; use crate::tty::{EventedPty, EventedReadWrite}; @@ -83,7 +84,7 @@ impl<'a> Pty<'a> { } } -pub fn new<'a>(config: &Config, size: &SizeInfo, window_id: Option) -> Pty<'a> { +pub fn new<'a, C>(config: &Config, size: &SizeInfo, window_id: Option) -> Pty<'a> { if let Some(pty) = conpty::new(config, size, window_id) { info!("Using Conpty agent"); IS_CONPTY.store(true, Ordering::Relaxed); diff --git a/alacritty_terminal/src/tty/windows/winpty.rs b/alacritty_terminal/src/tty/windows/winpty.rs index 8795ca6..db397ad 100644 --- a/alacritty_terminal/src/tty/windows/winpty.rs +++ b/alacritty_terminal/src/tty/windows/winpty.rs @@ -22,13 +22,13 @@ use std::sync::Arc; use std::u16; use dunce::canonicalize; +use log::info; use mio_named_pipes::NamedPipe; use winapi::um::winbase::FILE_FLAG_OVERLAPPED; -use winpty::Config as WinptyConfig; -use winpty::{ConfigFlags, MouseMode, SpawnConfig, SpawnFlags, Winpty}; +use winpty::{Config as WinptyConfig, ConfigFlags, MouseMode, SpawnConfig, SpawnFlags, Winpty}; use crate::config::{Config, Shell}; -use crate::display::OnResize; +use crate::event::OnResize; use crate::term::SizeInfo; // We store a raw pointer because we need mutable access to call @@ -75,7 +75,7 @@ impl<'a> Drop for Agent<'a> { /// This is a placeholder value until we see how often long responses happen const AGENT_TIMEOUT: u32 = 10000; -pub fn new<'a>(config: &Config, size: &SizeInfo, _window_id: Option) -> Pty<'a> { +pub fn new<'a, C>(config: &Config, size: &SizeInfo, _window_id: Option) -> Pty<'a> { // Create config let mut wconfig = WinptyConfig::new(ConfigFlags::empty()).unwrap(); diff --git a/alacritty_terminal/src/url.rs b/alacritty_terminal/src/url.rs index 292f835..9e8ecd4 100644 --- a/alacritty_terminal/src/url.rs +++ b/alacritty_terminal/src/url.rs @@ -29,7 +29,7 @@ impl Url { } /// Convert URLs bounding points to linear indices - pub fn linear_bounds(&self, terminal: &Term) -> RangeInclusive { + pub fn linear_bounds(&self, terminal: &Term) -> RangeInclusive { let mut start = self.start; let mut end = self.end; diff --git a/alacritty_terminal/tests/ref.rs b/alacritty_terminal/tests/ref.rs index b6efb6a..1d59e70 100644 --- a/alacritty_terminal/tests/ref.rs +++ b/alacritty_terminal/tests/ref.rs @@ -1,5 +1,4 @@ -#[macro_use] -extern crate serde_derive; +use serde::Deserialize; use serde_json as json; use std::fs::File; @@ -8,9 +7,9 @@ use std::path::Path; use alacritty_terminal::ansi; use alacritty_terminal::clipboard::Clipboard; -use alacritty_terminal::config::Config; +use alacritty_terminal::config::MockConfig; +use alacritty_terminal::event::{Event, EventListener}; use alacritty_terminal::index::Column; -use alacritty_terminal::message_bar::MessageBuffer; use alacritty_terminal::term::cell::Cell; use alacritty_terminal::term::SizeInfo; use alacritty_terminal::Grid; @@ -81,6 +80,11 @@ struct RefConfig { history_size: u32, } +struct Mock; +impl EventListener for Mock { + fn send_event(&self, _event: Event) {} +} + fn ref_test(dir: &Path) { let recording = read_u8(dir.join("alacritty.recording")); let serialized_size = read_string(dir.join("size.json")).unwrap(); @@ -91,10 +95,10 @@ fn ref_test(dir: &Path) { let grid: Grid = json::from_str(&serialized_grid).unwrap(); let ref_config: RefConfig = json::from_str(&serialized_cfg).unwrap_or_default(); - let mut config: Config = Default::default(); + let mut config = MockConfig::default(); config.scrolling.set_history(ref_config.history_size); - let mut terminal = Term::new(&config, size, MessageBuffer::new(), Clipboard::new_nop()); + let mut terminal = Term::new(&config, &size, Clipboard::new_nop(), Mock); let mut parser = ansi::Processor::new(); for byte in recording { diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 4292765..dae7ee0 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -591,12 +591,11 @@ mod tests { let index = ((glyph.width * 3 * row) + (col * 3)) as usize; let value = glyph.buf[index]; let c = match value { - 0...50 => ' ', - 51...100 => '.', - 101...150 => '~', - 151...200 => '*', - 201...255 => '#', - _ => unreachable!(), + 0..=50 => ' ', + 51..=100 => '.', + 101..=150 => '~', + 151..=200 => '*', + 201..=255 => '#', }; print!("{}", c); } diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 5388aeb..0886f17 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -546,7 +546,7 @@ pub enum Error { } impl ::std::error::Error for Error { - fn cause(&self) -> Option<&dyn (::std::error::Error)> { + fn cause(&self) -> Option<&dyn std::error::Error> { match *self { Error::FreeType(ref err) => Some(err), _ => None, diff --git a/font/src/lib.rs b/font/src/lib.rs index d3bddd5..262cf91 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -47,6 +47,7 @@ extern crate log; use std::fmt; use std::hash::{Hash, Hasher}; +use std::ops::{Add, Mul}; use std::sync::atomic::{AtomicUsize, Ordering}; // If target isn't macos or windows, reexport everything from ft @@ -173,28 +174,42 @@ impl PartialEq for GlyphKey { pub struct Size(i16); impl Size { + /// Create a new `Size` from a f32 size in points + pub fn new(size: f32) -> Size { + Size((size * Size::factor()) as i16) + } + /// Scale factor between font "Size" type and point size #[inline] pub fn factor() -> f32 { 2.0 } - /// Create a new `Size` from a f32 size in points - pub fn new(size: f32) -> Size { - Size((size * Size::factor()) as i16) - } - /// Get the f32 size in points pub fn as_f32_pts(self) -> f32 { f32::from(self.0) / Size::factor() } } -impl ::std::ops::Add for Size { +impl> Add for Size { type Output = Size; - fn add(self, other: Size) -> Size { - Size(self.0.saturating_add(other.0)) + fn add(self, other: T) -> Size { + Size(self.0.saturating_add(other.into().0)) + } +} + +impl> Mul for Size { + type Output = Size; + + fn mul(self, other: T) -> Size { + Size(self.0 * other.into().0) + } +} + +impl From for Size { + fn from(float: f32) -> Size { + Size::new(float) } }